source: LMDZ6/trunk/libf/phylmd/ecrad/radiation/radiation_aerosol_optics.F90 @ 4773

Last change on this file since 4773 was 4773, checked in by idelkadi, 5 months ago
  • Update of Ecrad in LMDZ The same organization of the Ecrad offline version is retained in order to facilitate the updating of Ecrad in LMDZ and the comparison between online and offline results. version 1.6.1 of Ecrad (https://github.com/lguez/ecrad.git)
  • Implementation of the double call of Ecrad in LMDZ


File size: 48.5 KB
Line 
1! radiation_aerosol_optics.F90 - Computing aerosol optical properties
2!
3! (C) Copyright 2015- ECMWF.
4!
5! This software is licensed under the terms of the Apache Licence Version 2.0
6! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
7!
8! In applying this licence, ECMWF does not waive the privileges and immunities
9! granted to it by virtue of its status as an intergovernmental organisation
10! nor does it submit to any jurisdiction.
11!
12! Author:  Robin Hogan
13! Email:   r.j.hogan@ecmwf.int
14!
15! Modifications
16!   2018-04-15  R. Hogan  Add "direct" option
17!   2020-11-14  R. Hogan  Add setup_general_aerosol_optics for ecCKD compatibility
18!   2022-03-27  R. Hogan  Add setup_general_aerosol_optics_legacy to use RRTM aerosol files with ecCKD
19!   2022-11-22  P. Ukkonen / R. Hogan  Optimizations to enhance vectorization
20
21module radiation_aerosol_optics
22
23  implicit none
24  public
25
26contains
27
28  ! Provides the elemental function "delta_eddington_extensive"
29#include "radiation_delta_eddington.h"
30
31  !---------------------------------------------------------------------
32  ! Load aerosol scattering data; this subroutine delegates to one
33  ! in radiation_aerosol_optics_data.F90
34  subroutine setup_aerosol_optics(config)
35
36    use parkind1,                      only : jprb
37    use yomhook,                       only : lhook, dr_hook, jphook
38    use radiation_config,              only : config_type
39    use radiation_aerosol_optics_data, only : aerosol_optics_type
40    use radiation_io,                  only : nulerr, radiation_abort
41
42    type(config_type), intent(inout) :: config
43
44    real(jphook) :: hook_handle
45
46    if (lhook) call dr_hook('radiation_aerosol_optics:setup_aerosol_optics',0,hook_handle)
47
48    if (config%n_aerosol_types > 0) then
49      ! Load data from file and prepare to map config%n_aerosol_types
50      ! aerosol types
51      if (config%use_general_aerosol_optics) then
52        ! Read file containing high spectral resolution optical
53        ! properties and average to the spectral intervals of the
54        ! current gas-optics scheme
55        call setup_general_aerosol_optics(config)
56      else
57        ! Read file containing optical properties already in the bands
58        ! of the gas-optics scheme
59        call config%aerosol_optics%setup(trim(config%aerosol_optics_file_name), &
60             &                           iverbose=config%iverbosesetup)
61      end if
62
63      call config%aerosol_optics%initialize_types(config%n_aerosol_types)
64
65      ! Check agreement in number of bands
66      if (config%n_bands_lw /= config%aerosol_optics%n_bands_lw) then
67        write(nulerr,'(a,i0,a,i0,a)') '*** Error: number of longwave bands (', &
68             &  config%n_bands_lw, ') does not match aerosol optics look-up table (', &
69             &  config%aerosol_optics%n_bands_lw, ')'
70        call radiation_abort()
71      end if
72      if (config%n_bands_sw /= config%aerosol_optics%n_bands_sw) then
73        write(nulerr,'(a)') '*** Error: number of shortwave bands does not match aerosol optics look-up table'
74        call radiation_abort()
75      end if
76
77      ! Map aerosol types to those loaded from the data file
78      call config%aerosol_optics%set_types(config%i_aerosol_type_map(1:config%n_aerosol_types))
79    end if
80
81    if (config%iverbosesetup >= 1) then
82      call config%aerosol_optics%print_description(config%i_aerosol_type_map(1:config%n_aerosol_types))
83    end if
84
85    if (lhook) call dr_hook('radiation_aerosol_optics:setup_aerosol_optics',1,hook_handle)
86
87  end subroutine setup_aerosol_optics
88
89
90  !---------------------------------------------------------------------
91  ! Read file containing high spectral resolution optical properties
92  ! and average to the spectral intervals of the current gas-optics
93  ! scheme
94  subroutine setup_general_aerosol_optics(config)
95
96    use parkind1,                      only : jprb
97    use yomhook,                       only : lhook, dr_hook, jphook
98    use easy_netcdf,                   only : netcdf_file
99    use radiation_config,              only : config_type
100    use radiation_aerosol_optics_data, only : aerosol_optics_type
101    use radiation_spectral_definition, only : SolarReferenceTemperature, &
102         &                                    TerrestrialReferenceTemperature
103    use radiation_io,                  only : nulout
104
105    type(config_type), intent(inout), target :: config
106
107    ! The NetCDF file containing the aerosol optics data
108    type(netcdf_file)  :: file
109
110    ! Wavenumber points in NetCDF file
111    real(jprb), allocatable :: wavenumber(:) ! cm-1
112
113    ! Hydrophilic aerosol properties
114    real(jprb), allocatable :: mass_ext_philic(:,:,:)    ! Mass-ext coefficient (m2 kg-1)
115    real(jprb), allocatable :: ssa_philic(:,:,:)         ! Single-scattering albedo
116    real(jprb), allocatable :: g_philic(:,:,:)           ! Asymmetry factor
117    real(jprb), allocatable :: lidar_ratio_philic(:,:,:) ! Lidar ratio (sr)
118
119    ! Hydrophobic aerosol properties
120    real(jprb), allocatable :: mass_ext_phobic(:,:)      ! Mass-ext coefficient (m2 kg-1)
121    real(jprb), allocatable :: ssa_phobic(:,:)           ! Single-scattering albedo
122    real(jprb), allocatable :: g_phobic(:,:)             ! Asymmetry factor
123    real(jprb), allocatable :: lidar_ratio_phobic(:,:)   ! Lidar ratio (sr)
124
125    ! Mapping matrix between optical properties at the wavenumbers in
126    ! the file, and spectral intervals used by the gas-optics scheme
127    real(jprb), allocatable :: mapping(:,:)
128
129    ! Target monochromatic wavenumber for interpolation (cm-1)
130    real(jprb) :: wavenumber_target
131
132    ! Number of spectral points describing aerosol properties in the
133    ! shortwave and longwave
134    integer    :: nspecsw, nspeclw
135
136    ! Number of monochromatic wavelengths required
137    integer    :: nmono
138
139    integer    :: n_type_philic, n_type_phobic, nrh, nwn
140    integer    :: jtype, jwl, iwn
141
142    ! Weight of first point in interpolation
143    real(jprb) :: weight1
144
145    real(jphook) :: hook_handle
146
147    if (lhook) call dr_hook('radiation_aerosol_optics:setup_general_aerosol_optics',0,hook_handle)
148
149    associate(ao => config%aerosol_optics)
150
151      call file%open(trim(config%aerosol_optics_file_name), iverbose=config%iverbosesetup)
152
153      if (.not. file%exists('wavenumber')) then
154        ! Assume we have an old-style aerosol optics file with optical
155        ! properties provided per pre-defined band
156        call file%close()
157        if (config%iverbosesetup >= 2) then
158          write(nulout,'(a)') 'Legacy aerosol optics file: mapping between bands'
159        end if
160        call setup_general_aerosol_optics_legacy(config, trim(config%aerosol_optics_file_name))
161        if (lhook) call dr_hook('radiation_aerosol_optics:setup_general_aerosol_optics',1,hook_handle)
162        return
163      end if
164
165      if (file%exists('mass_ext_hydrophilic')) then
166        ao%use_hydrophilic = .true.
167      else
168        ao%use_hydrophilic = .false.
169      end if
170
171      call file%get('wavenumber', wavenumber)
172      nwn = size(wavenumber)
173
174      ! Read the raw scattering data
175      call file%get('mass_ext_hydrophobic',    mass_ext_phobic)
176      call file%get('ssa_hydrophobic',         ssa_phobic)
177      call file%get('asymmetry_hydrophobic',   g_phobic)
178      call file%get('lidar_ratio_hydrophobic', lidar_ratio_phobic)
179
180      call file%get_global_attribute('description_hydrophobic', &
181          &                         ao%description_phobic_str)
182
183      if (ao%use_hydrophilic) then
184        call file%get('mass_ext_hydrophilic',    mass_ext_philic)
185        call file%get('ssa_hydrophilic',         ssa_philic)
186        call file%get('asymmetry_hydrophilic',   g_philic)
187        call file%get('lidar_ratio_hydrophilic', lidar_ratio_philic)
188
189        call file%get('relative_humidity1',      ao%rh_lower)
190
191        call file%get_global_attribute('description_hydrophilic', &
192            &                         ao%description_philic_str)
193      end if
194
195      ! Close aerosol scattering file
196      call file%close()
197
198      n_type_phobic = size(mass_ext_phobic, 2)
199      if (ao%use_hydrophilic) then
200        n_type_philic = size(mass_ext_philic, 3)
201        nrh = size(ao%rh_lower)
202      else
203        n_type_philic = 0
204        nrh = 0
205      end if
206
207      if (config%do_cloud_aerosol_per_sw_g_point) then
208        nspecsw = config%gas_optics_sw%spectral_def%ng
209      else
210        nspecsw = config%gas_optics_sw%spectral_def%nband
211      end if
212
213      if (config%do_cloud_aerosol_per_lw_g_point) then
214        nspeclw = config%gas_optics_lw%spectral_def%ng
215      else
216        nspeclw = config%gas_optics_lw%spectral_def%nband
217      end if
218
219      if (allocated(ao%wavelength_mono)) then
220        ! Monochromatic wavelengths also required
221        nmono = size(ao%wavelength_mono)
222      else
223        nmono = 0
224      end if
225
226      call ao%allocate(n_type_phobic, n_type_philic, nrh, nspeclw, nspecsw, nmono)
227
228      if (config%do_sw) then
229        call config%gas_optics_sw%spectral_def%calc_mapping(wavenumber, mapping, &
230            &          use_bands=(.not. config%do_cloud_aerosol_per_sw_g_point))
231
232        ao%mass_ext_sw_phobic = matmul(mapping, mass_ext_phobic)
233        ao%ssa_sw_phobic = matmul(mapping, mass_ext_phobic*ssa_phobic) &
234            &           / ao%mass_ext_sw_phobic
235        ao%g_sw_phobic = matmul(mapping, mass_ext_phobic*ssa_phobic*g_phobic) &
236            &         / (ao%mass_ext_sw_phobic*ao%ssa_sw_phobic)
237
238        if (ao%use_hydrophilic) then
239          do jtype = 1,n_type_philic
240            ao%mass_ext_sw_philic(:,:,jtype) = matmul(mapping, mass_ext_philic(:,:,jtype))
241            ao%ssa_sw_philic(:,:,jtype) = matmul(mapping, mass_ext_philic(:,:,jtype) &
242                &                                        *ssa_philic(:,:,jtype)) &
243                &           / ao%mass_ext_sw_philic(:,:,jtype)
244            ao%g_sw_philic(:,:,jtype) = matmul(mapping, mass_ext_philic(:,:,jtype) &
245                &                       *ssa_philic(:,:,jtype)*g_philic(:,:,jtype)) &
246                &         / (ao%mass_ext_sw_philic(:,:,jtype)*ao%ssa_sw_philic(:,:,jtype))
247          end do
248        end if
249      end if
250
251      if (config%do_lw) then
252        call config%gas_optics_lw%spectral_def%calc_mapping(wavenumber, mapping, &
253            &          use_bands=(.not. config%do_cloud_aerosol_per_lw_g_point))
254
255        ao%mass_ext_lw_phobic = matmul(mapping, mass_ext_phobic)
256        ao%ssa_lw_phobic = matmul(mapping, mass_ext_phobic*ssa_phobic) &
257            &           / ao%mass_ext_lw_phobic
258        ao%g_lw_phobic = matmul(mapping, mass_ext_phobic*ssa_phobic*g_phobic) &
259            &         / (ao%mass_ext_lw_phobic*ao%ssa_lw_phobic)
260
261        if (ao%use_hydrophilic) then
262          do jtype = 1,n_type_philic
263            ao%mass_ext_lw_philic(:,:,jtype) = matmul(mapping, mass_ext_philic(:,:,jtype))
264            ao%ssa_lw_philic(:,:,jtype) = matmul(mapping, mass_ext_philic(:,:,jtype) &
265                &                                        *ssa_philic(:,:,jtype)) &
266                &           / ao%mass_ext_lw_philic(:,:,jtype)
267            ao%g_lw_philic(:,:,jtype) = matmul(mapping, mass_ext_philic(:,:,jtype) &
268                &                       *ssa_philic(:,:,jtype)*g_philic(:,:,jtype)) &
269                &         / (ao%mass_ext_lw_philic(:,:,jtype)*ao%ssa_lw_philic(:,:,jtype))
270          end do
271        end if
272      end if
273
274      if (allocated(ao%wavelength_mono)) then
275        ! Monochromatic wavelengths also required
276        do jwl = 1,nmono
277          ! Wavelength (m) to wavenumber (cm-1)
278          wavenumber_target = 0.01_jprb / ao%wavelength_mono(jwl)
279          ! Find index to first interpolation point, and its weight
280          if (wavenumber_target <= wavenumber(1)) then
281            weight1 = 1.0_jprb
282            iwn = 1
283          else if (wavenumber_target >= wavenumber(nwn)) then
284            iwn = nwn-1
285            weight1 = 0.0_jprb
286          else
287            iwn = 1
288            do while (wavenumber(iwn+1) < wavenumber_target .and. iwn < nwn-1)
289              iwn = iwn + 1
290            end do
291            weight1 = (wavenumber(iwn+1)-wavenumber_target) &
292                &  / (wavenumber(iwn+1)-wavenumber(iwn))
293          end if
294          ! Linear interpolation
295          ao%mass_ext_mono_phobic(jwl,:) = weight1 * mass_ext_phobic(iwn,:) &
296              &             + (1.0_jprb - weight1)* mass_ext_phobic(iwn+1,:)
297          ao%ssa_mono_phobic(jwl,:)      = weight1 * ssa_phobic(iwn,:) &
298              &             + (1.0_jprb - weight1)* ssa_phobic(iwn+1,:)
299          ao%g_mono_phobic(jwl,:)        = weight1 * g_phobic(iwn,:) &
300              &             + (1.0_jprb - weight1)* g_phobic(iwn+1,:)
301          ao%lidar_ratio_mono_phobic(jwl,:) = weight1 * lidar_ratio_phobic(iwn,:) &
302              &                + (1.0_jprb - weight1)* lidar_ratio_phobic(iwn+1,:)
303          if (ao%use_hydrophilic) then
304            ao%mass_ext_mono_philic(jwl,:,:) = weight1 * mass_ext_philic(iwn,:,:) &
305                &               + (1.0_jprb - weight1)* mass_ext_philic(iwn+1,:,:)
306            ao%ssa_mono_philic(jwl,:,:)      = weight1 * ssa_philic(iwn,:,:) &
307                &               + (1.0_jprb - weight1)* ssa_philic(iwn+1,:,:)
308            ao%g_mono_philic(jwl,:,:)        = weight1 * g_philic(iwn,:,:) &
309                &               + (1.0_jprb - weight1)* g_philic(iwn+1,:,:)
310            ao%lidar_ratio_mono_philic(jwl,:,:) = weight1 * lidar_ratio_philic(iwn,:,:) &
311                &                  + (1.0_jprb - weight1)* lidar_ratio_philic(iwn+1,:,:)
312          end if
313        end do
314      end if
315
316      ! Deallocate memory local to this routine
317      deallocate(mass_ext_phobic)
318      deallocate(ssa_phobic)
319      deallocate(g_phobic)
320      deallocate(lidar_ratio_phobic)
321      if (ao%use_hydrophilic) then
322        deallocate(mass_ext_philic)
323        deallocate(ssa_philic)
324        deallocate(g_philic)
325        deallocate(lidar_ratio_philic)
326      end if
327
328    end associate
329
330    if (lhook) call dr_hook('radiation_aerosol_optics:setup_general_aerosol_optics',1,hook_handle)
331
332  end subroutine setup_general_aerosol_optics
333
334
335  !---------------------------------------------------------------------
336  ! Read file containing legacy-style band-wise aerosol optical
337  ! properties and average to the spectral intervals of the current
338  ! gas-optics scheme
339  subroutine setup_general_aerosol_optics_legacy(config, file_name)
340
341    use parkind1,                      only : jprb
342    use yomhook,                       only : lhook, dr_hook, jphook
343    use easy_netcdf,                   only : netcdf_file
344    use radiation_config,              only : config_type
345    use radiation_aerosol_optics_data, only : aerosol_optics_type
346    use radiation_spectral_definition, only : SolarReferenceTemperature, &
347         &                                    TerrestrialReferenceTemperature
348
349    type(config_type), intent(inout), target :: config
350
351    ! The NetCDF file containing the aerosol optics data
352    character(len=*), intent(in) :: file_name
353
354    ! Mapping matrix between optical properties at the wavenumbers in
355    ! the file, and spectral intervals used by the gas-optics scheme
356    real(jprb), allocatable :: mapping(:,:), mapping_transp(:,:)
357
358    ! Pointer to the aerosol optics coefficients for brevity of access
359    type(aerosol_optics_type), pointer :: ao
360
361    ! Local copy of aerosol optical properties in the spectral
362    ! intervals of the file, which is deallocated when it goes out of
363    ! scope
364    type(aerosol_optics_type) :: ao_legacy
365
366    integer :: jtype
367
368    real(jphook) :: hook_handle
369
370    if (lhook) call dr_hook('radiation_aerosol_optics:setup_general_aerosol_optics_legacy',0,hook_handle)
371    ao => config%aerosol_optics
372
373    ! Load file into a local structure
374    call ao_legacy%setup(file_name, iverbose=config%iverbosesetup)
375
376    ! Copy over scalars and coordinate variables
377    call ao%allocate(ao_legacy%n_type_phobic, ao_legacy%n_type_philic, ao_legacy%nrh, &
378         &           config%n_bands_lw, config%n_bands_sw, ao_legacy%n_mono_wl)
379    ao%description_phobic_str = ao_legacy%description_phobic_str
380    if (ao_legacy%use_hydrophilic) then
381      ao%description_philic_str = ao_legacy%description_philic_str
382      ao%rh_lower = ao_legacy%rh_lower
383    end if
384
385    ! use_hydrophilic = ao_legacy%use_hydrophilic
386    ! ao%iclass = ao_legacy%iclass
387    ! ao%itype = ao_legacy%itype
388    ! ao%ntype = ao_legacy%ntype
389    ! ao%n_type_phobic = ao_legacy%n_type_phobic
390    ! ao%n_type_philic = ao_legacy%n_type_philic
391    ! ao%n_mono_wl = ao_legacy%n_mono_wl
392    ! ao%use_monochromatic = ao_legacy%use_monochromatic
393
394    if (config%do_sw) then
395      call config%gas_optics_sw%spectral_def%calc_mapping_from_wavenumber_bands( &
396           &  ao_legacy%wavenumber1_sw, ao_legacy%wavenumber2_sw, mapping_transp, &
397           &  use_bands=(.not. config%do_cloud_aerosol_per_sw_g_point))
398      if (allocated(mapping)) then
399        deallocate(mapping)
400      end if
401      allocate(mapping(config%n_bands_sw,ao_legacy%n_bands_sw))
402      mapping = transpose(mapping_transp)
403      ao%mass_ext_sw_phobic = matmul(mapping, ao_legacy%mass_ext_sw_phobic)
404      ao%ssa_sw_phobic = matmul(mapping, ao_legacy%mass_ext_sw_phobic*ao_legacy%ssa_sw_phobic) &
405           &           / ao%mass_ext_sw_phobic
406      ao%g_sw_phobic = matmul(mapping, ao_legacy%mass_ext_sw_phobic*ao_legacy%ssa_sw_phobic &
407           &                           *ao_legacy%g_sw_phobic) &
408           &         / (ao%mass_ext_sw_phobic*ao%ssa_sw_phobic)
409
410      if (ao%use_hydrophilic) then
411        do jtype = 1,ao%n_type_philic
412          ao%mass_ext_sw_philic(:,:,jtype) = matmul(mapping, ao_legacy%mass_ext_sw_philic(:,:,jtype))
413          ao%ssa_sw_philic(:,:,jtype) = matmul(mapping, ao_legacy%mass_ext_sw_philic(:,:,jtype) &
414               &                                        *ao_legacy%ssa_sw_philic(:,:,jtype)) &
415               &           / ao%mass_ext_sw_philic(:,:,jtype)
416          ao%g_sw_philic(:,:,jtype) = matmul(mapping, ao_legacy%mass_ext_sw_philic(:,:,jtype) &
417               &               *ao_legacy%ssa_sw_philic(:,:,jtype)*ao_legacy%g_sw_philic(:,:,jtype)) &
418               &         / (ao%mass_ext_sw_philic(:,:,jtype)*ao%ssa_sw_philic(:,:,jtype))
419        end do
420      end if
421    end if
422
423    if (config%do_lw) then
424      if (allocated(mapping_transp)) then
425        deallocate(mapping_transp)
426      end if
427      call config%gas_optics_lw%spectral_def%calc_mapping_from_wavenumber_bands( &
428           &  ao_legacy%wavenumber1_lw, ao_legacy%wavenumber2_lw, mapping_transp, &
429           &  use_bands=(.not. config%do_cloud_aerosol_per_lw_g_point))
430      if (allocated(mapping)) then
431        deallocate(mapping)
432      end if
433      allocate(mapping(config%n_bands_lw,ao_legacy%n_bands_lw))
434      mapping = transpose(mapping_transp)
435      ao%mass_ext_lw_phobic = matmul(mapping, ao_legacy%mass_ext_lw_phobic)
436      ao%ssa_lw_phobic = matmul(mapping, ao_legacy%mass_ext_lw_phobic*ao_legacy%ssa_lw_phobic) &
437           &           / ao%mass_ext_lw_phobic
438      ao%g_lw_phobic = matmul(mapping, ao_legacy%mass_ext_lw_phobic*ao_legacy%ssa_lw_phobic &
439           &                           *ao_legacy%g_lw_phobic) &
440           &         / (ao%mass_ext_lw_phobic*ao%ssa_lw_phobic)
441
442      if (ao%use_hydrophilic) then
443        do jtype = 1,ao%n_type_philic
444          ao%mass_ext_lw_philic(:,:,jtype) = matmul(mapping, ao_legacy%mass_ext_lw_philic(:,:,jtype))
445          ao%ssa_lw_philic(:,:,jtype) = matmul(mapping, ao_legacy%mass_ext_lw_philic(:,:,jtype) &
446               &                                        *ao_legacy%ssa_lw_philic(:,:,jtype)) &
447               &           / ao%mass_ext_lw_philic(:,:,jtype)
448          ao%g_lw_philic(:,:,jtype) = matmul(mapping, ao_legacy%mass_ext_lw_philic(:,:,jtype) &
449               &               *ao_legacy%ssa_lw_philic(:,:,jtype)*ao_legacy%g_lw_philic(:,:,jtype)) &
450               &         / (ao%mass_ext_lw_philic(:,:,jtype)*ao%ssa_lw_philic(:,:,jtype))
451        end do
452      end if
453    end if
454
455    if (allocated(ao_legacy%wavelength_mono)) then
456      ao%wavelength_mono = ao_legacy%wavelength_mono
457      ao%mass_ext_mono_phobic = ao_legacy%mass_ext_mono_phobic
458      ao%ssa_mono_phobic = ao_legacy%ssa_mono_phobic
459      ao%g_mono_phobic = ao_legacy%g_mono_phobic
460      ao%lidar_ratio_mono_phobic = ao_legacy%lidar_ratio_mono_phobic
461      if (ao%use_hydrophilic) then
462        ao%mass_ext_mono_philic = ao_legacy%mass_ext_mono_philic
463        ao%ssa_mono_philic = ao_legacy%ssa_mono_philic
464        ao%g_mono_philic = ao_legacy%g_mono_philic
465        ao%lidar_ratio_mono_philic = ao_legacy%lidar_ratio_mono_philic
466      end if
467    end if
468
469    if (lhook) call dr_hook('radiation_aerosol_optics:setup_general_aerosol_optics_legacy',1,hook_handle)
470
471  end subroutine setup_general_aerosol_optics_legacy
472
473
474  !---------------------------------------------------------------------
475  ! Compute aerosol optical properties and add to existing gas optical
476  ! depth and scattering properties
477  subroutine add_aerosol_optics(nlev,istartcol,iendcol, &
478       &  config, thermodynamics, gas, aerosol, &
479       &  od_lw, ssa_lw, g_lw, od_sw, ssa_sw, g_sw)
480
481    use parkind1,                      only : jprb
482    use radiation_io,                  only : nulout, nulerr, radiation_abort
483    use yomhook,                       only : lhook, dr_hook, jphook
484    use radiation_config,              only : config_type
485    use radiation_thermodynamics,      only : thermodynamics_type
486    use radiation_gas,                 only : gas_type, IH2O, IMassMixingRatio
487    use radiation_aerosol,             only : aerosol_type
488    use radiation_constants,           only : AccelDueToGravity
489    use radiation_aerosol_optics_data, only : aerosol_optics_type, &
490         &  IAerosolClassUndefined,   IAerosolClassIgnored, &
491         &  IAerosolClassHydrophobic, IAerosolClassHydrophilic
492
493    real(jprb), parameter :: OneOverAccelDueToGravity = 1.0_jprb / AccelDueToGravity
494
495    integer, intent(in) :: nlev               ! number of model levels
496    integer, intent(in) :: istartcol, iendcol ! range of columns to process
497    type(config_type), intent(in), target :: config
498    type(thermodynamics_type),intent(in)  :: thermodynamics
499    type(gas_type),           intent(in)  :: gas
500    type(aerosol_type),       intent(in)  :: aerosol
501    ! Optical depth, single scattering albedo and asymmetry factor of
502    ! the atmosphere (gases on input, gases and aerosols on output)
503    ! for each g point. Note that longwave ssa and asymmetry and
504    ! shortwave asymmetry are all zero for gases, so are not yet
505    ! defined on input and are therefore intent(out).
506    real(jprb), dimension(config%n_g_lw,nlev,istartcol:iendcol), &
507         &   intent(inout) :: od_lw
508    real(jprb), dimension(config%n_g_lw_if_scattering,nlev,istartcol:iendcol), &
509         &   intent(out)   :: ssa_lw, g_lw
510    real(jprb), dimension(config%n_g_sw,nlev,istartcol:iendcol), &
511         &   intent(inout) :: od_sw, ssa_sw
512    real(jprb), dimension(config%n_g_sw,nlev,istartcol:iendcol), &
513         &   intent(out)   :: g_sw
514
515    ! Extinction optical depth, scattering optical depth and
516    ! asymmetry-times-scattering-optical-depth for all the aerosols in
517    ! a column for each spectral band of the shortwave and longwave
518    ! spectrum
519    real(jprb), dimension(config%n_bands_sw,nlev) &
520         &  :: od_sw_aerosol, scat_sw_aerosol, scat_g_sw_aerosol
521    real(jprb), dimension(config%n_bands_lw,nlev) &
522         &  :: od_lw_aerosol
523    real(jprb), dimension(config%n_bands_lw_if_scattering,nlev) &
524         &  :: scat_lw_aerosol, scat_g_lw_aerosol
525
526    real(jprb) :: local_od_sw, local_od_lw
527
528    real(jprb) :: h2o_mmr(istartcol:iendcol,nlev)
529
530    real(jprb) :: rh ! Relative humidity with respect to liquid water
531
532    ! Factor (kg m-2) to convert mixing ratio (kg kg-1) to mass in
533    ! path (kg m-2)
534    real(jprb) :: factor(nlev)
535
536    ! Temporary extinction and scattering optical depths of aerosol
537    ! plus gas
538    real(jprb) :: local_od, local_scat
539
540    ! Aerosol mixing ratio as a scalar
541    real(jprb) :: mixing_ratio
542
543    ! Loop indices for column, level, g point, band and aerosol type
544    integer :: jcol, jlev, jg, jtype, jband
545
546    ! Range of levels over which aerosols are present
547    integer :: istartlev, iendlev
548
549    ! Indices to spectral band and relative humidity look-up table
550    integer :: iband, irh, irhs(nlev)
551
552    ! Short cut for ao%itype(jtype)
553    integer :: itype
554
555    ! Pointer to the aerosol optics coefficients for brevity of access
556    type(aerosol_optics_type), pointer :: ao
557
558    real(jphook) :: hook_handle
559
560    if (lhook) call dr_hook('radiation_aerosol_optics:add_aerosol_optics',0,hook_handle)
561
562    if (aerosol%is_direct) then
563      ! Aerosol optical properties have been provided in each band
564      ! directly by the user
565      call add_aerosol_optics_direct(nlev,istartcol,iendcol, &
566           &  config, aerosol, &
567           &  od_lw, ssa_lw, g_lw, od_sw, ssa_sw, g_sw)
568    else
569      ! Aerosol mixing ratios have been provided
570
571      do jtype = 1,config%n_aerosol_types
572        if (config%aerosol_optics%iclass(jtype) == IAerosolClassUndefined) then
573          write(nulerr,'(a)') '*** Error: not all aerosol types are defined'
574          call radiation_abort()
575        end if
576      end do
577
578      if (config%iverbose >= 2) then
579        write(nulout,'(a)') 'Computing aerosol absorption/scattering properties'
580      end if
581
582      ao => config%aerosol_optics
583
584      istartlev = lbound(aerosol%mixing_ratio,2)
585      iendlev   = ubound(aerosol%mixing_ratio,2)
586
587      if (ubound(aerosol%mixing_ratio,3) /= config%n_aerosol_types) then
588        write(nulerr,'(a,i0,a,i0)') '*** Error: aerosol%mixing_ratio contains ', &
589             &  ubound(aerosol%mixing_ratio,3), ' aerosol types, expected ', &
590             &  config%n_aerosol_types
591        call radiation_abort()
592      end if
593
594      ! Set variables to zero that may not have been previously
595      g_sw(:,:,istartcol:iendcol) = 0.0_jprb
596      if (config%do_lw_aerosol_scattering) then
597        ssa_lw(:,:,istartcol:iendcol) = 0.0_jprb
598        g_lw(:,:,istartcol:iendcol)   = 0.0_jprb
599      end if
600
601      call gas%get(IH2O, IMassMixingRatio, h2o_mmr, istartcol=istartcol)
602
603      ! Loop over column
604      do jcol = istartcol,iendcol
605
606        ! Reset temporary arrays
607        od_sw_aerosol     = 0.0_jprb
608        scat_sw_aerosol   = 0.0_jprb
609        scat_g_sw_aerosol = 0.0_jprb
610        od_lw_aerosol     = 0.0_jprb
611        scat_lw_aerosol   = 0.0_jprb
612        scat_g_lw_aerosol = 0.0_jprb
613
614        do jlev = istartlev,iendlev
615          ! Compute relative humidity with respect to liquid
616          ! saturation and the index to the relative-humidity index of
617          ! hydrophilic-aerosol data
618          rh  = h2o_mmr(jcol,jlev) / thermodynamics%h2o_sat_liq(jcol,jlev)
619          irhs(jlev) = ao%calc_rh_index(rh)
620
621          factor(jlev) = ( thermodynamics%pressure_hl(jcol,jlev+1) &
622               &    -thermodynamics%pressure_hl(jcol,jlev  )  ) &
623               &   * OneOverAccelDueToGravity
624        end do
625
626        do jtype = 1,config%n_aerosol_types
627          itype = ao%itype(jtype)
628
629          ! Add the optical depth, scattering optical depth and
630          ! scattering optical depth-weighted asymmetry factor for
631          ! this aerosol type to the total for all aerosols.  Note
632          ! that the following expressions are array-wise, the
633          ! dimension being spectral band.
634          if (ao%iclass(jtype) == IAerosolClassHydrophobic) then
635            do jlev = istartlev,iendlev
636              mixing_ratio = aerosol%mixing_ratio(jcol,jlev,jtype)
637              do jband = 1,config%n_bands_sw
638                local_od_sw = factor(jlev) * mixing_ratio &
639                     &  * ao%mass_ext_sw_phobic(jband,itype)
640                od_sw_aerosol(jband,jlev) = od_sw_aerosol(jband,jlev) + local_od_sw
641                scat_sw_aerosol(jband,jlev) = scat_sw_aerosol(jband,jlev) &
642                     &  + local_od_sw * ao%ssa_sw_phobic(jband,itype)
643                scat_g_sw_aerosol(jband,jlev) = scat_g_sw_aerosol(jband,jlev) &
644                     &  + local_od_sw * ao%ssa_sw_phobic(jband,itype) &
645                     &  * ao%g_sw_phobic(jband,itype)
646              end do
647              if (config%do_lw_aerosol_scattering) then
648                do jband = 1,config%n_bands_lw
649                  local_od_lw = factor(jlev) * mixing_ratio &
650                       &  * ao%mass_ext_lw_phobic(jband,itype)
651                  od_lw_aerosol(jband,jlev) = od_lw_aerosol(jband,jlev) + local_od_lw
652                  scat_lw_aerosol(jband,jlev) = scat_lw_aerosol(jband,jlev) &
653                       &  + local_od_lw * ao%ssa_lw_phobic(jband,itype)
654                  scat_g_lw_aerosol(jband,jlev) = scat_g_lw_aerosol(jband,jlev) &
655                       &  + local_od_lw * ao%ssa_lw_phobic(jband,itype) &
656                       &  * ao%g_lw_phobic(jband,itype)
657                end do
658              else
659                ! If aerosol longwave scattering is not included then we
660                ! weight the optical depth by the single scattering
661                ! co-albedo
662                do jband = 1,config%n_bands_lw
663                  od_lw_aerosol(jband,jlev) = od_lw_aerosol(jband,jlev) &
664                       &  + factor(jlev) * mixing_ratio &
665                       &  * ao%mass_ext_lw_phobic(jband,itype) &
666                       &  * (1.0_jprb - ao%ssa_lw_phobic(jband,itype))
667                end do
668              end if
669            end do
670
671          else if (ao%iclass(jtype) == IAerosolClassHydrophilic) then
672            ! Hydrophilic aerosols require the look-up tables to
673            ! be indexed with irh
674            do jlev = istartlev,iendlev
675              mixing_ratio = aerosol%mixing_ratio(jcol,jlev,jtype)
676              irh = irhs(jlev)
677              do jband = 1,config%n_bands_sw
678                local_od_sw = factor(jlev) * mixing_ratio &
679                     &  * ao%mass_ext_sw_philic(jband,irh,itype)
680                od_sw_aerosol(jband,jlev) = od_sw_aerosol(jband,jlev) + local_od_sw
681                scat_sw_aerosol(jband,jlev) = scat_sw_aerosol(jband,jlev) &
682                     &  + local_od_sw * ao%ssa_sw_philic(jband,irh,itype)
683                scat_g_sw_aerosol(jband,jlev) = scat_g_sw_aerosol(jband,jlev) &
684                     &  + local_od_sw * ao%ssa_sw_philic(jband,irh,itype) &
685                     &  * ao%g_sw_philic(jband,irh,itype)
686              end do
687              if (config%do_lw_aerosol_scattering) then
688                do jband = 1,config%n_bands_lw
689                  local_od_lw = factor(jlev) * mixing_ratio &
690                       &  * ao%mass_ext_lw_philic(jband,irh,itype)
691                  od_lw_aerosol(jband,jlev) = od_lw_aerosol(jband,jlev) + local_od_lw
692                  scat_lw_aerosol(jband,jlev) = scat_lw_aerosol(jband,jlev) &
693                       &  + local_od_lw * ao%ssa_lw_philic(jband,irh,itype)
694                  scat_g_lw_aerosol(jband,jlev) = scat_g_lw_aerosol(jband,jlev) &
695                       &  + local_od_lw * ao%ssa_lw_philic(jband,irh,itype) &
696                       &  * ao%g_lw_philic(jband,irh,itype)
697                end do
698              else
699                ! If aerosol longwave scattering is not included then we
700                ! weight the optical depth by the single scattering
701                ! co-albedo
702                do jband = 1,config%n_bands_lw
703                  od_lw_aerosol(jband,jlev) = od_lw_aerosol(jband,jlev) &
704                       &  + factor(jlev) * mixing_ratio &
705                       &  * ao%mass_ext_lw_philic(jband,irh,itype) &
706                       &  * (1.0_jprb - ao%ssa_lw_philic(jband,irh,itype))
707                end do
708              end if
709            end do
710
711            ! Implicitly, if ao%iclass(jtype) == IAerosolClassNone, then
712            ! no aerosol scattering properties are added
713          end if
714
715        end do ! Loop over aerosol type
716
717        if (.not. config%do_sw_delta_scaling_with_gases) then
718          ! Delta-Eddington scaling on aerosol only.  Note that if
719          ! do_sw_delta_scaling_with_gases==.true. then the delta
720          ! scaling is done to the cloud-aerosol-gas mixture inside
721          ! the solver
722          call delta_eddington_extensive_vec(config%n_bands_sw*nlev, od_sw_aerosol, &
723               &                             scat_sw_aerosol, scat_g_sw_aerosol)
724        end if
725
726        ! Combine aerosol shortwave scattering properties with gas
727        ! properties (noting that any gas scattering will have an
728        ! asymmetry factor of zero)
729        if (config%do_cloud_aerosol_per_sw_g_point) then
730
731          ! We can assume the band and g-point indices are the same
732          do jlev = 1,nlev
733            do jg = 1,config%n_g_sw
734              local_scat = ssa_sw(jg,jlev,jcol)*od_sw(jg,jlev,jcol) + scat_sw_aerosol(jg,jlev)
735              od_sw(jg,jlev,jcol) = od_sw(jg,jlev,jcol) + od_sw_aerosol(jg,jlev)
736              g_sw(jg,jlev,jcol) = scat_g_sw_aerosol(jg,jlev) / max(local_scat, 1.0e-24_jprb)
737              ssa_sw(jg,jlev,jcol) = min(local_scat / max(od_sw(jg,jlev,jcol), 1.0e-24_jprb), 1.0_jprb)
738            end do
739          end do
740
741        else
742
743          do jlev = 1,nlev
744            do jg = 1,config%n_g_sw
745              ! Need to map between bands and g-points
746              iband = config%i_band_from_reordered_g_sw(jg)
747              local_od = od_sw(jg,jlev,jcol) + od_sw_aerosol(iband,jlev)
748              if (local_od > 0.0_jprb .and. od_sw_aerosol(iband,jlev) > 0.0_jprb) then
749                local_scat = ssa_sw(jg,jlev,jcol) * od_sw(jg,jlev,jcol) &
750                     &  + scat_sw_aerosol(iband,jlev)
751                ! Note that asymmetry_sw of gases is zero so the following
752                ! simply weights the aerosol asymmetry by the scattering
753                ! optical depth
754                if (local_scat > 0.0_jprb) then
755                  g_sw(jg,jlev,jcol) = scat_g_sw_aerosol(iband,jlev) / local_scat
756                end if
757                ssa_sw(jg,jlev,jcol) = local_scat / local_od
758                od_sw (jg,jlev,jcol) = local_od
759              end if
760            end do
761          end do
762
763        end if
764
765        ! Combine aerosol longwave scattering properties with gas
766        ! properties, noting that in the longwave, gases do not
767        ! scatter at all
768        if (config%do_lw_aerosol_scattering) then
769
770          call delta_eddington_extensive_vec(config%n_bands_lw*nlev, od_lw_aerosol, &
771               &                             scat_lw_aerosol, scat_g_lw_aerosol)
772
773          do jlev = istartlev,iendlev
774            do jg = 1,config%n_g_lw
775              iband = config%i_band_from_reordered_g_lw(jg)
776              local_od = od_lw(jg,jlev,jcol) + od_lw_aerosol(iband,jlev)
777              if (local_od > 0.0_jprb .and. od_lw_aerosol(iband,jlev) > 0.0_jprb) then
778                ! All scattering is due to aerosols, therefore the
779                ! asymmetry factor is equal to the value for aerosols
780                if (scat_lw_aerosol(iband,jlev) > 0.0_jprb) then
781                  g_lw(jg,jlev,jcol) = scat_g_lw_aerosol(iband,jlev) &
782                       &  / scat_lw_aerosol(iband,jlev)
783                end if
784                ssa_lw(jg,jlev,jcol) = scat_lw_aerosol(iband,jlev) / local_od
785                od_lw (jg,jlev,jcol) = local_od
786              end if
787            end do
788          end do
789
790        else
791
792          if (config%do_cloud_aerosol_per_lw_g_point) then
793            ! We can assume band and g-point indices are the same
794            do jlev = istartlev,iendlev
795              do jg = 1,config%n_g_lw
796                od_lw(jg,jlev,jcol) = od_lw(jg,jlev,jcol) + od_lw_aerosol(jg,jlev)
797              end do
798            end do
799          else
800            do jlev = istartlev,iendlev
801              do jg = 1,config%n_g_lw
802                od_lw(jg,jlev,jcol) = od_lw(jg,jlev,jcol) &
803                     &  + od_lw_aerosol(config%i_band_from_reordered_g_lw(jg),jlev)
804              end do
805            end do
806          end if
807
808        end if
809
810      end do ! Loop over column
811
812    end if
813
814    if (lhook) call dr_hook('radiation_aerosol_optics:add_aerosol_optics',1,hook_handle)
815
816  end subroutine add_aerosol_optics
817
818
819  !---------------------------------------------------------------------
820  ! Add precomputed optical properties to gas optical depth and
821  ! scattering properties
822  subroutine add_aerosol_optics_direct(nlev,istartcol,iendcol, &
823       &  config, aerosol, &
824       &  od_lw, ssa_lw, g_lw, od_sw, ssa_sw, g_sw)
825
826    use parkind1,                      only : jprb
827    use radiation_io,                  only : nulerr, radiation_abort
828    use yomhook,                       only : lhook, dr_hook, jphook
829    use radiation_config,              only : config_type
830    use radiation_aerosol,             only : aerosol_type
831
832    integer, intent(in) :: nlev               ! number of model levels
833    integer, intent(in) :: istartcol, iendcol ! range of columns to process
834    type(config_type), intent(in), target :: config
835    type(aerosol_type),       intent(in)  :: aerosol
836    ! Optical depth, single scattering albedo and asymmetry factor of
837    ! the atmosphere (gases on input, gases and aerosols on output)
838    ! for each g point. Note that longwave ssa and asymmetry and
839    ! shortwave asymmetry are all zero for gases, so are not yet
840    ! defined on input and are therefore intent(out).
841    real(jprb), dimension(config%n_g_lw,nlev,istartcol:iendcol), &
842         &   intent(inout) :: od_lw
843    real(jprb), dimension(config%n_g_lw_if_scattering,nlev,istartcol:iendcol), &
844         &   intent(out)   :: ssa_lw, g_lw
845    real(jprb), dimension(config%n_g_sw,nlev,istartcol:iendcol), &
846         &   intent(inout) :: od_sw, ssa_sw
847    real(jprb), dimension(config%n_g_sw,nlev,istartcol:iendcol), &
848         &   intent(out)   :: g_sw
849
850    ! Temporary extinction and scattering optical depths of aerosol
851    ! plus gas
852    real(jprb) :: local_od, local_scat
853
854    ! Extinction optical depth, scattering optical depth and
855    ! asymmetry-times-scattering-optical-depth for all the aerosols at
856    ! a point in space for each spectral band of the shortwave and
857    ! longwave spectrum
858    real(jprb), dimension(config%n_bands_sw,nlev) &
859         & :: od_sw_aerosol, scat_sw_aerosol, scat_g_sw_aerosol
860    real(jprb), dimension(config%n_bands_lw,nlev) :: od_lw_aerosol
861    real(jprb), dimension(config%n_bands_lw_if_scattering,nlev) &
862         & :: scat_lw_aerosol, scat_g_lw_aerosol
863
864    ! Loop indices for column, level, g point and band
865    integer :: jcol, jlev, jg, jb
866
867    ! Range of levels over which aerosols are present
868    integer :: istartlev, iendlev
869
870    ! Indices to spectral band
871    integer :: iband
872
873    real(jphook) :: hook_handle
874
875    if (lhook) call dr_hook('radiation_aerosol_optics:add_aerosol_optics_direct',0,hook_handle)
876
877    if (config%do_sw) then
878      ! Check array dimensions
879      if (ubound(aerosol%od_sw,1) /= config%n_bands_sw) then
880        write(nulerr,'(a,i0,a,i0)') '*** Error: aerosol%od_sw contains ', &
881           &  ubound(aerosol%od_sw,1), ' band, expected ', &
882           &  config%n_bands_sw
883        call radiation_abort()
884      end if
885
886      istartlev = lbound(aerosol%od_sw,2)
887      iendlev   = ubound(aerosol%od_sw,2)
888
889      ! Set variables to zero that may not have been previously
890      g_sw(:,:,istartcol:iendcol) = 0.0_jprb
891
892      ! Loop over position
893      do jcol = istartcol,iendcol
894! Added for DWD (2020)
895!NEC$ forced_collapse
896        do jlev = istartlev,iendlev
897          do jb = 1,config%n_bands_sw
898            od_sw_aerosol(jb,jlev) = aerosol%od_sw(jb,jlev,jcol)
899            scat_sw_aerosol(jb,jlev) = aerosol%ssa_sw(jb,jlev,jcol) * od_sw_aerosol(jb,jlev)
900            scat_g_sw_aerosol(jb,jlev) = aerosol%g_sw(jb,jlev,jcol) * scat_sw_aerosol(jb,jlev)
901
902            if (.not. config%do_sw_delta_scaling_with_gases) then
903              ! Delta-Eddington scaling on aerosol only.  Note that if
904              ! do_sw_delta_scaling_with_gases==.true. then the delta
905              ! scaling is done to the cloud-aerosol-gas mixture
906              ! inside the solver
907              call delta_eddington_extensive(od_sw_aerosol(jb,jlev), scat_sw_aerosol(jb,jlev), &
908                   &                         scat_g_sw_aerosol(jb,jlev))
909            end if
910          end do
911        end do
912        ! Combine aerosol shortwave scattering properties with gas
913        ! properties (noting that any gas scattering will have an
914        ! asymmetry factor of zero)
915        do jlev = istartlev,iendlev
916          if (od_sw_aerosol(1,jlev) > 0.0_jprb) then
917            do jg = 1,config%n_g_sw
918              iband = config%i_band_from_reordered_g_sw(jg)
919              local_od = od_sw(jg,jlev,jcol) + od_sw_aerosol(iband,jlev)
920              local_scat = ssa_sw(jg,jlev,jcol) * od_sw(jg,jlev,jcol) &
921                   &  + scat_sw_aerosol(iband,jlev)
922              ! Note that asymmetry_sw of gases is zero so the following
923              ! simply weights the aerosol asymmetry by the scattering
924              ! optical depth
925              g_sw(jg,jlev,jcol) = scat_g_sw_aerosol(iband,jlev) / local_scat
926              local_od = od_sw(jg,jlev,jcol) + od_sw_aerosol(iband,jlev)
927              ssa_sw(jg,jlev,jcol) = local_scat / local_od
928              od_sw (jg,jlev,jcol) = local_od
929            end do
930          end if
931        end do
932      end do
933
934    end if
935
936    if (config%do_lw) then
937
938      if (ubound(aerosol%od_lw,1) /= config%n_bands_lw) then
939        write(nulerr,'(a,i0,a,i0)') '*** Error: aerosol%od_lw contains ', &
940           &  ubound(aerosol%od_lw,1), ' band, expected ', &
941           &  config%n_bands_lw
942        call radiation_abort()
943      end if
944
945      istartlev = lbound(aerosol%od_lw,2)
946      iendlev   = ubound(aerosol%od_lw,2)
947
948      if (config%do_lw_aerosol_scattering) then
949        ssa_lw(:,:,istartcol:iendcol) = 0.0_jprb
950        g_lw(:,:,istartcol:iendcol)   = 0.0_jprb
951
952        ! Loop over position
953        do jcol = istartcol,iendcol
954! Added for DWD (2020)
955!NEC$ forced_collapse
956          do jlev = istartlev,iendlev
957            do jb = 1,config%n_bands_lw
958              od_lw_aerosol(jb,jlev) = aerosol%od_lw(jb,jlev,jcol)
959              scat_lw_aerosol(jb,jlev) = aerosol%ssa_lw(jb,jlev,jcol) * od_lw_aerosol(jb,jlev)
960              scat_g_lw_aerosol(jb,jlev) = aerosol%g_lw(jb,jlev,jcol) * scat_lw_aerosol(jb,jlev)
961
962              call delta_eddington_extensive(od_lw_aerosol(jb,jlev), scat_lw_aerosol(jb,jlev), &
963                   &                         scat_g_lw_aerosol(jb,jlev))
964            end do
965          end do
966          do jlev = istartlev,iendlev
967            do jg = 1,config%n_g_lw
968              iband = config%i_band_from_reordered_g_lw(jg)
969              if (od_lw_aerosol(iband,jlev) > 0.0_jprb) then
970                ! All scattering is due to aerosols, therefore the
971                ! asymmetry factor is equal to the value for aerosols
972                if (scat_lw_aerosol(iband,jlev) > 0.0_jprb) then
973                  g_lw(jg,jlev,jcol) = scat_g_lw_aerosol(iband,jlev) &
974                       &  / scat_lw_aerosol(iband,jlev)
975                end if
976                local_od = od_lw(jg,jlev,jcol) + od_lw_aerosol(iband,jlev)
977                ssa_lw(jg,jlev,jcol) = scat_lw_aerosol(iband,jlev) / local_od
978                od_lw (jg,jlev,jcol) = local_od
979              end if
980            end do
981          end do
982        end do
983
984      else ! No longwave scattering
985
986        ! Loop over position
987        do jcol = istartcol,iendcol
988! Added for DWD (2020)
989!NEC$ forced_collapse
990          do jlev = istartlev,iendlev
991            ! If aerosol longwave scattering is not included then we
992            ! weight the optical depth by the single scattering
993            ! co-albedo
994            do jb = 1, config%n_bands_lw
995              od_lw_aerosol(jb,jlev) = aerosol%od_lw(jb,jlev,jcol) &
996                 &  * (1.0_jprb - aerosol%ssa_lw(jb,jlev,jcol))
997            end do
998          end do
999          do jlev = istartlev,iendlev
1000            do jg = 1,config%n_g_lw
1001              od_lw(jg,jlev,jcol) = od_lw(jg,jlev,jcol) &
1002                   &  + od_lw_aerosol(config%i_band_from_reordered_g_lw(jg),jlev)
1003            end do
1004          end do
1005        end do
1006
1007      end if
1008    end if
1009
1010
1011    if (lhook) call dr_hook('radiation_aerosol_optics:add_aerosol_optics_direct',1,hook_handle)
1012
1013  end subroutine add_aerosol_optics_direct
1014
1015
1016  !---------------------------------------------------------------------
1017  ! Sometimes it is useful to specify aerosol in terms of its optical
1018  ! depth at a particular wavelength.  This function returns the dry
1019  ! mass-extinction coefficient, i.e. the extinction cross section per
1020  ! unit mass, for aerosol of type "itype" at the specified wavelength
1021  ! (m). For hydrophilic types, the value at the first relative
1022  ! humidity bin is taken.
1023  function dry_aerosol_mass_extinction(config, itype, wavelength)
1024
1025    use parkind1,                      only : jprb
1026    use radiation_io,                  only : nulerr, radiation_abort
1027    use radiation_config,              only : config_type
1028    use radiation_aerosol_optics_data, only : aerosol_optics_type, &
1029         &  IAerosolClassUndefined,   IAerosolClassIgnored, &
1030         &  IAerosolClassHydrophobic, IAerosolClassHydrophilic
1031
1032    type(config_type), intent(in), target :: config
1033
1034    ! Aerosol type
1035    integer, intent(in) :: itype
1036
1037    ! Wavelength (m)
1038    real(jprb), intent(in) :: wavelength
1039
1040    real(jprb) :: dry_aerosol_mass_extinction
1041
1042    ! Index to the monochromatic wavelength requested
1043    integer :: imono
1044
1045    ! Pointer to the aerosol optics coefficients for brevity of access
1046    type(aerosol_optics_type), pointer :: ao
1047
1048    ao => config%aerosol_optics
1049
1050    imono = minloc(abs(wavelength - ao%wavelength_mono), 1)
1051
1052    if (abs(wavelength - ao%wavelength_mono(imono))/wavelength > 0.02_jprb) then
1053      write(nulerr,'(a,e11.4,a)') '*** Error: requested wavelength ', &
1054           &  wavelength, ' not within 2% of stored wavelengths'
1055      call radiation_abort()
1056     end if
1057
1058    if (ao%iclass(itype) == IAerosolClassHydrophobic) then
1059      dry_aerosol_mass_extinction = ao%mass_ext_mono_phobic(imono,ao%itype(itype))
1060    else if (ao%iclass(itype) == IAerosolClassHydrophilic) then
1061      ! Take the value at the first relative-humidity bin for the
1062      ! "dry" aerosol value
1063      dry_aerosol_mass_extinction = ao%mass_ext_mono_philic(imono,1,ao%itype(itype))
1064    else
1065      dry_aerosol_mass_extinction = 0.0_jprb
1066    end if
1067
1068  end function dry_aerosol_mass_extinction
1069
1070
1071  !---------------------------------------------------------------------
1072  ! Compute aerosol extinction coefficient at a particular wavelength
1073  ! and a single height - this is useful for visibility diagnostics
1074  subroutine aerosol_extinction(ncol,istartcol,iendcol, &
1075       &  config, wavelength, mixing_ratio, relative_humidity, extinction)
1076
1077    use parkind1,                      only : jprb
1078    use yomhook,                       only : lhook, dr_hook, jphook
1079    use radiation_io,                  only : nulerr, radiation_abort
1080    use radiation_config,              only : config_type
1081    use radiation_aerosol_optics_data, only : aerosol_optics_type, &
1082         &  IAerosolClassUndefined,   IAerosolClassIgnored, &
1083         &  IAerosolClassHydrophobic, IAerosolClassHydrophilic
1084
1085    integer, intent(in) :: ncol               ! number of columns
1086    integer, intent(in) :: istartcol, iendcol ! range of columns to process
1087    type(config_type), intent(in), target :: config
1088    real(jprb), intent(in)  :: wavelength ! Requested wavelength (m)
1089    real(jprb), intent(in)  :: mixing_ratio(ncol,config%n_aerosol_types)
1090    real(jprb), intent(in)  :: relative_humidity(ncol)
1091    real(jprb), intent(out) :: extinction(ncol)
1092
1093    ! Local aerosol extinction
1094    real(jprb) :: ext
1095
1096    ! Index to the monochromatic wavelength requested
1097    integer :: imono
1098
1099    ! Pointer to the aerosol optics coefficients for brevity of access
1100    type(aerosol_optics_type), pointer :: ao
1101
1102    ! Loop indices for column and aerosol type
1103    integer :: jcol, jtype
1104
1105    ! Relative humidity index
1106    integer :: irh
1107
1108    real(jphook) :: hook_handle
1109
1110    if (lhook) call dr_hook('radiation_aerosol_optics:aerosol_extinction',0,hook_handle)
1111
1112    do jtype = 1,config%n_aerosol_types
1113      if (config%aerosol_optics%iclass(jtype) == IAerosolClassUndefined) then
1114        write(nulerr,'(a)') '*** Error: not all aerosol types are defined'
1115        call radiation_abort()
1116      end if
1117    end do
1118
1119    ao => config%aerosol_optics
1120
1121    imono = minloc(abs(wavelength - ao%wavelength_mono), 1)
1122
1123    if (abs(wavelength - ao%wavelength_mono(imono))/wavelength > 0.02_jprb) then
1124      write(nulerr,'(a,e11.4,a)') '*** Error: requested wavelength ', &
1125           &  wavelength, ' not within 2% of stored wavelengths'
1126      call radiation_abort()
1127     end if
1128
1129    ! Loop over position
1130    do jcol = istartcol,iendcol
1131      ext = 0.0_jprb
1132      ! Get relative-humidity index
1133      irh = ao%calc_rh_index(relative_humidity(jcol))
1134      ! Add extinction coefficients from each aerosol type
1135      do jtype = 1,config%n_aerosol_types
1136        if (ao%iclass(jtype) == IAerosolClassHydrophobic) then
1137          ext = ext + mixing_ratio(jcol,jtype) &
1138               &    * ao%mass_ext_mono_phobic(imono,ao%itype(jtype))
1139        else if (ao%iclass(jtype) == IAerosolClassHydrophilic) then
1140          ext = ext + mixing_ratio(jcol,jtype) &
1141               &    * ao%mass_ext_mono_philic(imono,irh,ao%itype(jtype))
1142        end if
1143      end do
1144
1145      extinction(jcol) = ext
1146    end do
1147
1148    if (lhook) call dr_hook('radiation_aerosol_optics:aerosol_extinction',1,hook_handle)
1149
1150  end subroutine aerosol_extinction
1151
1152end module radiation_aerosol_optics
Note: See TracBrowser for help on using the repository browser.