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

Last change on this file since 4853 was 4853, checked in by idelkadi, 2 months ago

Ecrad update in LMDZ / Implementation of Ecrad double call in LMDZ

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