source: LMDZ6/trunk/libf/phylmd/ecrad/radiation/radiation_general_cloud_optics_data.F90 @ 4802

Last change on this file since 4802 was 4802, checked in by lguez, 4 months ago

Circumvent a bug of ifort

LMDZ crashed with ifort 19.0.4 and 2021.9.0, when compiled with
-parallel mpi -rad ecrad. See [LMDZ release
notes](https://lmdz.lmd.jussieu.fr/pub/src_archives/testing/release_notes.html)
for svn tag LMDZ-testing-202304 and [Crash in
setup_general_cloud_optics](https://github.com/ecmwf-ifs/ecrad/issues/13).

File size: 16.2 KB
Line 
1! radiation_general_cloud_optics_data.F90 - Type to store generalized cloud optical properties
2!
3! (C) Copyright 2019- 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! License: see the COPYING file for details
15!
16
17module radiation_general_cloud_optics_data
18
19  use parkind1, only : jprb
20
21  implicit none
22
23  public
24
25  !---------------------------------------------------------------------
26  ! This type holds the configuration information to compute optical
27  ! properties for a particular type of cloud or hydrometeor in one of
28  ! the shortwave or longwave
29  type general_cloud_optics_type
30    ! Band-specific (or g-point-specific) values as a look-up table
31    ! versus effective radius dimensioned (nband,n_effective_radius)
32
33    ! Extinction coefficient per unit mass (m2 kg-1)
34    real(jprb), allocatable, dimension(:,:) :: &
35         &  mass_ext
36
37    ! Single-scattering albedo and asymmetry factor (dimensionless)
38    real(jprb), allocatable, dimension(:,:) :: &
39         &  ssa, asymmetry
40
41    ! Number of effective radius coefficients, start value and
42    ! interval in look-up table
43    integer    :: n_effective_radius = 0
44    real(jprb) :: effective_radius_0, d_effective_radius
45
46    ! Name of cloud/precip type and scattering model
47    ! (e.g. "mie_droplet", "fu-muskatel_ice"). These are used to
48    ! generate the name of the data file from which the coefficients
49    ! are read.
50    character(len=511) :: type_name
51
52    ! Do we use bands or g-points?
53    logical :: use_bands = .false.
54
55   contains
56     procedure :: setup => setup_general_cloud_optics
57     procedure :: add_optical_properties
58     procedure :: save => save_general_cloud_optics_data
59
60  end type general_cloud_optics_type
61
62contains
63
64  ! Provides elemental function "delta_eddington"
65#include "radiation_delta_eddington.h"
66
67  !---------------------------------------------------------------------
68  ! Setup cloud optics coefficients by reading them from a file
69  subroutine setup_general_cloud_optics(this, file_name, specdef, &
70       &                                use_bands, use_thick_averaging, &
71       &                                weighting_temperature, &
72       &                                iverbose)
73
74    use yomhook,                       only : lhook, dr_hook, jphook
75    use easy_netcdf,                   only : netcdf_file
76    use radiation_spectral_definition, only : spectral_definition_type
77    use radiation_io,                  only : nulout, nulerr, radiation_abort
78
79    class(general_cloud_optics_type), intent(inout)    :: this
80    character(len=*), intent(in)               :: file_name
81    type(spectral_definition_type), intent(in) :: specdef
82    logical, intent(in), optional              :: use_bands, use_thick_averaging
83    real(jprb), intent(in), optional           :: weighting_temperature ! K
84    integer, intent(in), optional              :: iverbose
85
86    ! Spectral properties read from file, dimensioned (wavenumber,
87    ! n_effective_radius)
88    real(jprb), dimension(:,:), allocatable :: mass_ext, & ! m2 kg-1
89         &                                     ssa, asymmetry
90
91    ! Reflectance of an infinitely thick cloud, needed for thick
92    ! averaging
93    real(jprb), dimension(:,:), allocatable :: ref_inf
94
95    ! Coordinate variables from file
96    real(jprb), dimension(:), allocatable :: wavenumber       ! cm-1
97    real(jprb), dimension(:), allocatable :: effective_radius ! m
98
99    ! Matrix mapping optical properties in the file to values per
100    ! g-point or band, such that in the thin-averaging case,
101    ! this%mass_ext=matmul(mapping,file%mass_ext), so mapping is
102    ! dimensioned (ngpoint,nwav)
103    real(jprb), dimension(:,:), allocatable :: mapping
104
105    ! The NetCDF file containing the coefficients
106    type(netcdf_file)  :: file
107
108    real(jprb) :: diff_spread
109    integer    :: iverb
110    integer    :: nre  ! Number of effective radii
111    integer    :: nwav ! Number of wavenumbers describing cloud
112
113    logical    :: use_bands_local, use_thick_averaging_local
114
115    real(jphook) :: hook_handle
116
117    if (lhook) call dr_hook('radiation_general_cloud_optics_data:setup',0,hook_handle)
118
119    ! Set local values of optional inputs
120    if (present(iverbose)) then
121      iverb = iverbose
122    else
123      iverb = 2
124    end if
125
126    if (present(use_bands)) then
127      use_bands_local = use_bands
128    else
129      use_bands_local = .false.
130    end if
131
132    if (present(use_thick_averaging)) then
133      use_thick_averaging_local = use_thick_averaging
134    else
135      use_thick_averaging_local = .false.
136    end if
137
138    ! Open the scattering file and configure the way it is read
139    call file%open(trim(file_name), iverbose=iverb)
140    !call file%transpose_matrices()
141
142    ! Read coordinate variables
143    call file%get('wavenumber', wavenumber)
144    call file%get('effective_radius', effective_radius)
145
146    ! Read the band-specific coefficients
147    call file%get('mass_extinction_coefficient', mass_ext)
148    call file%get('single_scattering_albedo', ssa)
149    call file%get('asymmetry_factor', asymmetry)
150
151    ! Close scattering file
152    call file%close()
153
154    ! Check effective radius is evenly spaced
155    nre = size(effective_radius)
156    ! Fractional range of differences, should be near zero for evenly
157    ! spaced data
158    diff_spread = (maxval(effective_radius(2:nre)-effective_radius(1:nre-1))  &
159         &        -minval(effective_radius(2:nre)-effective_radius(1:nre-1))) &
160         &      /  minval(abs(effective_radius(2:nre)-effective_radius(1:nre-1)))
161    if (diff_spread > 0.01_jprb) then
162      write(nulerr, '(a,a,a)') '*** Error: effective_radius in ', &
163           &  trim(file_name), ', is not evenly spaced to 1%'
164      call radiation_abort('Radiation configuration error')
165    end if
166
167    ! Set up effective radius coordinate variable
168    this%n_effective_radius = nre
169    this%effective_radius_0 = effective_radius(1)
170    this%d_effective_radius = effective_radius(2) - effective_radius(1)
171
172    nwav = size(wavenumber)
173
174    ! Define the mapping matrix
175    call specdef%calc_mapping(wavenumber, mapping, &
176         weighting_temperature=weighting_temperature, use_bands=use_bands)
177
178    ! Thick averaging should be performed on delta-Eddington scaled
179    ! quantities (it makes no difference to thin averaging)
180    call delta_eddington(mass_ext, ssa, asymmetry)
181
182    ! Thin averaging
183
184    ! Circumvent ifort bug (see https://github.com/ecmwf-ifs/ecrad/issues/13):
185    allocate(this%mass_ext(size(mapping, 1), nre))
186
187    this%mass_ext  = matmul(mapping, mass_ext)
188    this%ssa       = matmul(mapping, mass_ext*ssa) / this%mass_ext
189    this%asymmetry = matmul(mapping, mass_ext*ssa*asymmetry) / (this%mass_ext*this%ssa)
190
191    if (use_thick_averaging_local) then
192      ! Thick averaging as described by Edwards and Slingo (1996),
193      ! modifying only the single-scattering albedo
194      allocate(ref_inf(nwav, nre))
195
196      ! Eqs. 18 and 17 of Edwards & Slingo (1996)
197      ref_inf = sqrt((1.0_jprb - ssa) / (1.0_jprb - ssa*asymmetry))
198      ref_inf = (1.0_jprb - ref_inf) / (1.0_jprb + ref_inf)
199      ! Here the left-hand side is actually the averaged ref_inf
200      this%ssa = matmul(mapping, ref_inf)
201      ! Eq. 19 of Edwards and Slingo (1996)
202      this%ssa = 4.0_jprb * this%ssa / ((1.0_jprb + this%ssa)**2 &
203           &  - this%asymmetry * (1.0_jprb - this%ssa)**2)
204
205      deallocate(ref_inf)
206    end if
207
208    deallocate(mapping)
209
210    ! Revert back to unscaled quantities
211    call revert_delta_eddington(this%mass_ext, this%ssa, this%asymmetry)
212
213    if (iverb >= 2) then
214      write(nulout,'(a,a)') '  File: ', trim(file_name)
215      if (present(weighting_temperature)) then
216        write(nulout,'(a,f7.1,a)') '  Weighting temperature: ', weighting_temperature, ' K'
217      else
218        write(nulout,'(a,f7.1,a)') '  Weighting temperature: ', specdef%reference_temperature, ' K'
219      end if
220      if (use_thick_averaging_local) then
221        write(nulout,'(a)') '  SSA averaging: optically thick limit'
222      else
223        write(nulout,'(a)') '  SSA averaging: optically thin limit'
224      end if
225      if (use_bands_local) then
226        write(nulout,'(a,i0,a)') '  Spectral discretization: ', specdef%nband, ' bands'
227      else
228        write(nulout,'(a,i0,a)') '  Spectral discretization: ', specdef%ng, ' g-points'
229      end if
230      write(nulout,'(a,i0,a,f6.1,a,f6.1,a)') '  Effective radius look-up: ', nre, ' points in range ', &
231           &  effective_radius(1)*1.0e6_jprb, '-', effective_radius(nre)*1.0e6_jprb, ' um'
232      write(nulout,'(a,i0,a,i0,a)') '  Wavenumber range: ', int(specdef%min_wavenumber()), '-', &
233           &  int(specdef%max_wavenumber()), ' cm-1'
234    end if
235
236    if (lhook) call dr_hook('radiation_general_cloud_optics_data:setup',1,hook_handle)
237
238  end subroutine setup_general_cloud_optics
239
240
241  !---------------------------------------------------------------------
242  ! Add the optical properties of a particular cloud type to the
243  ! accumulated optical properties of all cloud types
244  subroutine add_optical_properties(this, ng, nlev, ncol, &
245       &                            cloud_fraction, &
246       &                            water_path, effective_radius, &
247       &                            od, scat_od, scat_asymmetry)
248
249    use yomhook, only : lhook, dr_hook, jphook
250
251    class(general_cloud_optics_type), intent(in) :: this
252
253    ! Number of g points, levels and columns
254    integer, intent(in) :: ng, nlev, ncol
255
256    ! Properties of present cloud type, dimensioned (ncol,nlev)
257    real(jprb), intent(in) :: cloud_fraction(:,:)
258    real(jprb), intent(in) :: water_path(:,:)       ! kg m-2
259    real(jprb), intent(in) :: effective_radius(:,:) ! m
260
261    ! Optical properties which are additive per cloud type,
262    ! dimensioned (ng,nlev,ncol)
263    real(jprb), intent(inout), dimension(ng,nlev,ncol) &
264         &  :: od             ! Optical depth of layer
265    real(jprb), intent(inout), dimension(ng,nlev,ncol), optional &
266         &  :: scat_od, &     ! Scattering optical depth of layer
267         &     scat_asymmetry ! Scattering optical depth x asymmetry factor
268
269    real(jprb) :: od_local(ng)
270
271    real(jprb) :: re_index, weight1, weight2
272    integer :: ire
273
274    integer :: jcol, jlev
275
276    real(jphook) :: hook_handle
277
278    if (lhook) call dr_hook('radiation_general_cloud_optics_data:add_optical_properties',0,hook_handle)
279
280    if (present(scat_od)) then
281      do jcol = 1,ncol
282        do jlev = 1,nlev
283          if (cloud_fraction(jcol, jlev) > 0.0_jprb) then
284            re_index = max(1.0_jprb, min(1.0_jprb + (effective_radius(jcol,jlev)-this%effective_radius_0) &
285                 &              / this%d_effective_radius, this%n_effective_radius-0.0001_jprb))
286            ire = int(re_index)
287            weight2 = re_index - ire
288            weight1 = 1.0_jprb - weight2
289            od_local = water_path(jcol, jlev) * (weight1*this%mass_ext(:,ire) &
290                 &                              +weight2*this%mass_ext(:,ire+1))
291            od(:,jlev,jcol) = od(:,jlev,jcol) + od_local
292            od_local = od_local * (weight1*this%ssa(:,ire) &
293                 &                +weight2*this%ssa(:,ire+1))
294            scat_od(:,jlev,jcol) = scat_od(:,jlev,jcol) + od_local
295            scat_asymmetry(:,jlev,jcol) = scat_asymmetry(:,jlev,jcol) &
296                 & + od_local * (weight1*this%asymmetry(:,ire) &
297                 &              +weight2*this%asymmetry(:,ire+1))
298          end if
299        end do
300      end do
301    else
302      ! No scattering: return the absorption optical depth
303      do jcol = 1,ncol
304        do jlev = 1,nlev
305          if (water_path(jcol, jlev) > 0.0_jprb) then
306            re_index = max(1.0_jprb, min(1.0_jprb + (effective_radius(jcol,jlev)-this%effective_radius_0) &
307                 &              / this%d_effective_radius, this%n_effective_radius-0.0001_jprb))
308            ire = int(re_index)
309            weight2 = re_index - ire
310            weight1 = 1.0_jprb - weight2
311            od(:,jlev,jcol) = od(:,jlev,jcol) &
312                 &  + water_path(jcol, jlev) * (weight1*this%mass_ext(:,ire) &
313                 &                             +weight2*this%mass_ext(:,ire+1)) &
314                 &  * (1.0_jprb - (weight1*this%ssa(:,ire)+weight2*this%ssa(:,ire+1)))
315          end if
316        end do
317      end do
318    end if
319
320    if (lhook) call dr_hook('radiation_general_cloud_optics_data:add_optical_properties',1,hook_handle)
321
322  end subroutine add_optical_properties
323
324
325  !---------------------------------------------------------------------
326  ! Return the Planck function (in W m-2 (cm-1)-1) for a given
327  ! wavenumber (cm-1) and temperature (K), ensuring double precision
328  ! for internal calculation
329  elemental function calc_planck_function_wavenumber(wavenumber, temperature)
330
331    use parkind1,            only : jprb, jprd
332    use radiation_constants, only : SpeedOfLight, BoltzmannConstant, PlanckConstant
333
334    real(jprb), intent(in) :: wavenumber  ! cm-1
335    real(jprb), intent(in) :: temperature ! K
336    real(jprb) :: calc_planck_function_wavenumber
337
338    real(jprd) :: freq ! Hz
339    real(jprd) :: planck_fn_freq ! W m-2 Hz-1
340
341    freq = 100.0_jprd * real(SpeedOfLight,jprd) * real(wavenumber,jprd)
342    planck_fn_freq = 2.0_jprd * real(PlanckConstant,jprd) * freq**3 &
343         &  / (real(SpeedOfLight,jprd)**2 * (exp(real(PlanckConstant,jprd)*freq &
344         &     /(real(BoltzmannConstant,jprd)*real(temperature,jprd))) - 1.0_jprd))
345    calc_planck_function_wavenumber = real(planck_fn_freq * 100.0_jprd * real(SpeedOfLight,jprd), jprb)
346
347  end function calc_planck_function_wavenumber
348
349  !---------------------------------------------------------------------
350  ! Save cloud optical properties in the named file
351  subroutine save_general_cloud_optics_data(this, file_name, iverbose)
352
353    use yomhook,     only : lhook, dr_hook, jphook
354    use easy_netcdf, only : netcdf_file
355
356    class(general_cloud_optics_type), intent(in) :: this
357    character(len=*),                 intent(in) :: file_name
358    integer,                optional, intent(in) :: iverbose
359
360    ! Object for output NetCDF file
361    type(netcdf_file) :: out_file
362
363    real(jprb) :: effective_radius(this%n_effective_radius)
364    integer :: ire
365   
366    real(jphook) :: hook_handle
367
368    if (lhook) call dr_hook('radiation_general_cloud_optics_data:save',0,hook_handle)
369
370    ! Create the file
371    call out_file%create(trim(file_name), iverbose=iverbose)
372
373    ! Define dimensions
374    call out_file%define_dimension("band", size(this%mass_ext,1))
375    call out_file%define_dimension("effective_radius", this%n_effective_radius)
376
377    ! Put global attributes
378    call out_file%put_global_attributes( &
379         &   title_str="Optical properties of "//trim(this%type_name) &
380         &   //" hydrometeors using the spectral intervals of ecRad", &
381         &   source_str="ecRad offline radiation model")
382
383    ! Define variables
384    call out_file%define_variable("effective_radius", units_str="m", &
385         &  long_name="Effective radius", dim1_name="effective_radius")
386    call out_file%define_variable("mass_extinction_coefficient", units_str="m2 kg-1", &
387         &  long_name="Mass-extinction coefficient", &
388         &  dim2_name="effective_radius", dim1_name="band")
389    call out_file%define_variable("single_scattering_albedo", units_str="1", &
390         &  long_name="Single scattering albedo", &
391         &  dim2_name="effective_radius", dim1_name="band")
392    call out_file%define_variable("asymmetry_factor", units_str="1", &
393         &  long_name="Asymmetry factor", &
394         &  dim2_name="effective_radius", dim1_name="band")
395
396    ! Define effective radius
397    do ire = 1,this%n_effective_radius
398      effective_radius(ire) = this%effective_radius_0 + this%d_effective_radius*(ire-1)
399    end do
400   
401    ! Write variables
402    call out_file%put("effective_radius", effective_radius)
403    call out_file%put("mass_extinction_coefficient", this%mass_ext)
404    call out_file%put("single_scattering_albedo", this%ssa)
405    call out_file%put("asymmetry_factor", this%asymmetry)
406   
407    call out_file%close()
408
409    if (lhook) call dr_hook('radiation_general_cloud_optics_data:save',1,hook_handle)
410
411  end subroutine save_general_cloud_optics_data
412 
413end module radiation_general_cloud_optics_data
Note: See TracBrowser for help on using the repository browser.