source: LMDZ6/trunk/libf/phylmd/ecrad/radiation/radiation_cloud.F90 @ 5821

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

Added a formulation to prescribe effective cloud size as a hyperbolic tangent function of pressure for calculating radiative fluxes related to 3D cloud effects.
Activation is controlled in namelist_ecrad by the logical key ok_separation_tanh

File size: 38.7 KB
Line 
1! radiation_cloud.F90 - Derived type to store cloud/precip properties
2!
3! (C) Copyright 2014- 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!   2019-01-14  R. Hogan  Added inv_inhom_effective_size variable
17!   2019-01-14  R. Hogan  Added out_of_physical_bounds routine
18!   2019-06-14  R. Hogan  Added capability to store any number of cloud/precip types
19
20module radiation_cloud
21
22  use parkind1, only : jprb
23
24  implicit none
25  public
26
27  !---------------------------------------------------------------------
28  ! The intention is that all variables describing clouds and
29  ! radiatively-active precipitation are contained in this derived
30  ! type, and if cloud variables are to be added in future, they can
31  ! be added to this type without requiring extra variables to be
32  ! passed between subroutines elsewhere in the program.
33  type cloud_type
34    ! For maximum flexibility, an arbitrary number "ntype" of
35    ! hydrometeor types can be stored, dimensioned (ncol,nlev,ntype)
36    integer                                   :: ntype = 0
37    real(jprb), allocatable, dimension(:,:,:) :: &
38         &  mixing_ratio, &  ! mass mixing ratio (kg/kg)
39         &  effective_radius ! (m)
40
41    ! For backwards compatibility, we also allow for the two
42    ! traditional cloud types, liquid cloud droplets and ice cloud
43    ! particles, dimensioned (ncol,nlev)
44    real(jprb), pointer, dimension(:,:) :: &
45         &  q_liq,  q_ice,  & ! mass mixing ratio (kg/kg)
46         &  re_liq, re_ice    ! effective radius (m)
47
48    ! For the moment, the different types of hydrometeor are assumed
49    ! to be mixed with each other, so there is just one cloud fraction
50    ! variable varying from 0 to 1
51    real(jprb), allocatable, dimension(:,:) :: fraction
52
53    ! The fractional standard deviation of cloud optical depth in the
54    ! cloudy part of the gridbox.  In the Tripleclouds representation
55    ! of cloud inhomogeneity, this is implemented by splitting the
56    ! cloudy part of the gridbox into two equal-area regions, one
57    ! with the cloud optical depth scaled by 1+fractional_std and the
58    ! other scaled by 1-fractional_std. This variable is dimensioned
59    ! (ncol,nlev)
60    real(jprb), allocatable, dimension(:,:) :: fractional_std
61
62    ! The inverse of the effective horizontal size of the clouds in
63    ! the gridbox, used to compute the cloud edge length per unit
64    ! gridbox area for use in representing 3D effects. This variable
65    ! is dimensioned (ncol,nlev).
66    real(jprb), allocatable, dimension(:,:) :: inv_cloud_effective_size ! m-1
67
68    ! Similarly for the in-cloud heterogeneities, used to compute the
69    ! edge length between the optically thin and thick cloudy regions
70    ! of the gridbox.
71    real(jprb), allocatable, dimension(:,:) :: inv_inhom_effective_size ! m-1
72
73    ! The following variable describes the overlap of cloud boundaries
74    ! in adjacent layers, with dimensions (ncol,nlev-1): 1 corresponds
75    ! to maximum overlap and 0 to random overlap. Depending on the
76    ! ecRad configuration, it may be the "alpha" overlap parameter of
77    ! Hogan and Illingworth (2000) or the "beta" overlap parameter of
78    ! Shonk et al. (2010).
79    real(jprb), allocatable, dimension(:,:) :: overlap_param
80
81  contains
82    procedure :: allocate   => allocate_cloud_arrays
83    procedure :: deallocate => deallocate_cloud_arrays
84    procedure :: set_overlap_param_fix
85    procedure :: set_overlap_param_var
86    generic   :: set_overlap_param => set_overlap_param_fix, set_overlap_param_var
87    procedure :: set_overlap_param_var2D
88    procedure :: set_overlap_param_approx
89    procedure :: create_fractional_std
90    procedure :: create_inv_cloud_effective_size
91    procedure :: create_inv_cloud_effective_size_eta
92    procedure :: param_cloud_effective_separation_eta
93    procedure :: param_cloud_effective_separation_tanh
94    procedure :: crop_cloud_fraction
95    procedure :: out_of_physical_bounds
96
97  end type cloud_type
98
99contains
100
101  !---------------------------------------------------------------------
102  ! Allocate arrays for describing clouds and precipitation, although
103  ! in the offline code these are allocated when they are read from
104  ! the NetCDF file
105  subroutine allocate_cloud_arrays(this, ncol, nlev, ntype, use_inhom_effective_size)
106
107    use yomhook,     only : lhook, dr_hook, jphook
108
109    class(cloud_type), intent(inout), target :: this
110    integer, intent(in)              :: ncol   ! Number of columns
111    integer, intent(in)              :: nlev   ! Number of levels
112    ! Number of cloud/precip particle types.  If not present then the
113    ! older cloud behaviour is assumed: two types are present, (1)
114    ! liquid and (2) ice, and they can be accessed via q_liq, q_ice,
115    ! re_liq and re_ice.
116    integer, intent(in), optional    :: ntype
117    logical, intent(in), optional    :: use_inhom_effective_size
118
119    real(jphook) :: hook_handle
120
121    if (lhook) call dr_hook('radiation_cloud:allocate',0,hook_handle)
122
123    if (present(ntype)) then
124      this%ntype = ntype
125    else
126      this%ntype = 2
127    end if
128    allocate(this%mixing_ratio(ncol,nlev,this%ntype))
129    allocate(this%effective_radius(ncol,nlev,this%ntype))
130    nullify(this%q_liq)
131    nullify(this%q_ice)
132    nullify(this%re_liq)
133    nullify(this%re_ice)
134    if (.not. present(ntype)) then
135      ! Older interface in which only liquid and ice are supported
136      this%q_liq  => this%mixing_ratio(:,:,1)
137      this%q_ice  => this%mixing_ratio(:,:,2)
138      this%re_liq => this%effective_radius(:,:,1)
139      this%re_ice => this%effective_radius(:,:,2)
140    end if
141
142    allocate(this%fraction(ncol,nlev))
143    allocate(this%overlap_param(ncol,nlev-1))
144    allocate(this%fractional_std(ncol,nlev))
145
146    if (present(use_inhom_effective_size)) then
147      if (use_inhom_effective_size) then
148        allocate(this%inv_inhom_effective_size(ncol,nlev))
149      end if
150    end if
151
152    if (lhook) call dr_hook('radiation_cloud:allocate',1,hook_handle)
153
154  end subroutine allocate_cloud_arrays
155
156
157  !---------------------------------------------------------------------
158  ! Deallocate arrays
159  subroutine deallocate_cloud_arrays(this)
160
161    use yomhook,     only : lhook, dr_hook, jphook
162
163    class(cloud_type), intent(inout) :: this
164
165    real(jphook) :: hook_handle
166
167    if (lhook) call dr_hook('radiation_cloud:deallocate',0,hook_handle)
168
169    nullify(this%q_liq)
170    nullify(this%q_ice)
171    nullify(this%re_liq)
172    nullify(this%re_ice)
173
174    if (allocated(this%mixing_ratio))     deallocate(this%mixing_ratio)
175    if (allocated(this%effective_radius)) deallocate(this%effective_radius)
176    if (allocated(this%fraction))         deallocate(this%fraction)
177    if (allocated(this%overlap_param))    deallocate(this%overlap_param)
178    if (allocated(this%fractional_std))   deallocate(this%fractional_std)
179    if (allocated(this%inv_cloud_effective_size)) &
180         &  deallocate(this%inv_cloud_effective_size)
181    if (allocated(this%inv_inhom_effective_size)) &
182         &  deallocate(this%inv_inhom_effective_size)
183
184    if (lhook) call dr_hook('radiation_cloud:deallocate',1,hook_handle)
185
186  end subroutine deallocate_cloud_arrays
187
188
189  !---------------------------------------------------------------------
190  ! Compute and store the overlap parameter from the provided overlap
191  ! decorrelation length (in metres).  If istartcol and/or iendcol are
192  ! provided then only columns in this range are computed.  If the
193  ! overlap_param array has not been allocated then it will be
194  ! allocated to be of the correct size relative to the pressure
195  ! field. This version assumes a fixed decorrelation_length for all
196  ! columns.
197  subroutine set_overlap_param_fix(this, thermodynamics, decorrelation_length, &
198       &  istartcol, iendcol)
199
200    use yomhook,                  only : lhook, dr_hook, jphook
201    use radiation_thermodynamics, only : thermodynamics_type
202    use radiation_constants,      only : GasConstantDryAir, AccelDueToGravity
203
204    class(cloud_type),         intent(inout) :: this
205    type(thermodynamics_type), intent(in)    :: thermodynamics
206    real(jprb),                intent(in)    :: decorrelation_length ! m
207    integer,         optional, intent(in)    :: istartcol, iendcol
208
209    ! Ratio of gas constant for dry air to acceleration due to gravity
210    real(jprb), parameter :: R_over_g = GasConstantDryAir / AccelDueToGravity
211
212    ! Process only columns i1 to i2, which will be istartcol to
213    ! iendcol if they were provided
214    integer :: i1, i2
215
216    integer :: ncol, nlev
217
218    integer :: jcol, jlev
219
220    real(jphook) :: hook_handle
221
222    if (lhook) call dr_hook('radiation_cloud:set_overlap_param_fix',0,hook_handle)
223
224    ! Pressure at half-levels, pressure_hl, is defined at nlev+1
225    ! points
226    ncol = size(thermodynamics%pressure_hl,dim=1)
227    nlev = size(thermodynamics%pressure_hl,dim=2)-1
228
229    if (present(istartcol)) then
230      i1 = istartcol
231    else
232      i1 = 1
233    end if
234
235    if (present(iendcol)) then
236      i2 = iendcol
237    else
238      i2 = ncol
239    end if
240
241    if (.not. allocated(this%overlap_param)) then
242      ! If pressure is of size (ncol,nlev+1) then overlap_param is of
243      ! size (ncol,nlev-1), since overlap parameter is only defined here
244      ! for interfaces between model layers, not for the interface to
245      ! space or the surface
246      allocate(this%overlap_param(ncol, nlev-1))
247    end if
248
249    if (thermodynamics%pressure_hl(i1,2) > thermodynamics%pressure_hl(i1,1)) then
250      ! Pressure is increasing with index (order of layers is
251      ! top-of-atmosphere to surface). In case pressure_hl(:,1)=0, we
252      ! don't take the logarithm of the first pressure in each column.
253      do jcol = i1,i2
254        this%overlap_param(jcol,1) = exp(-(R_over_g/decorrelation_length) &
255             &                            * thermodynamics%temperature_hl(jcol,2) &
256             &                            *log(thermodynamics%pressure_hl(jcol,3) &
257             &                                /thermodynamics%pressure_hl(jcol,2)))
258      end do
259
260      do jlev = 2,nlev-1
261        do jcol = i1,i2
262          this%overlap_param(jcol,jlev) = exp(-(0.5_jprb*R_over_g/decorrelation_length) &
263              &                            * thermodynamics%temperature_hl(jcol,jlev+1) &
264              &                            *log(thermodynamics%pressure_hl(jcol,jlev+2) &
265              &                                /thermodynamics%pressure_hl(jcol,jlev)))
266        end do
267      end do
268
269    else
270       ! Pressure is decreasing with index (order of layers is surface
271       ! to top-of-atmosphere).  In case pressure_hl(:,nlev+1)=0, we
272       ! don't take the logarithm of the last pressure in each column.
273      do jlev = 1,nlev-2
274        do jcol = i1,i2
275          this%overlap_param(jcol,jlev) = exp(-(0.5_jprb*R_over_g/decorrelation_length) &
276              &                            * thermodynamics%temperature_hl(jcol,jlev+1) &
277              &                            *log(thermodynamics%pressure_hl(jcol,jlev) &
278              &                                /thermodynamics%pressure_hl(jcol,jlev+2)))
279        end do
280      end do
281
282      do jcol = i1,i2
283        this%overlap_param(jcol,nlev-1) = exp(-(R_over_g/decorrelation_length) &
284            &                            * thermodynamics%temperature_hl(jcol,nlev) &
285            &                            *log(thermodynamics%pressure_hl(jcol,nlev-1) &
286            &                                /thermodynamics%pressure_hl(jcol,nlev)))
287      end do
288    end if
289
290    if (lhook) call dr_hook('radiation_cloud:set_overlap_param_fix',1,hook_handle)
291
292  end subroutine set_overlap_param_fix
293
294
295  !---------------------------------------------------------------------
296  ! Compute and store the overlap parameter from the provided overlap
297  ! decorrelation length (in metres), which may vary with column. Only
298  ! columns from istartcol to iendcol are computed.  If the
299  ! overlap_param array has not been allocated then it will be
300  ! allocated to be of the correct size relative to the pressure
301  ! field.
302  subroutine set_overlap_param_var(this, thermodynamics, decorrelation_length, &
303       &                           istartcol, iendcol)
304
305    use yomhook,                  only : lhook, dr_hook, jphook
306    use radiation_thermodynamics, only : thermodynamics_type
307    use radiation_constants,      only : GasConstantDryAir, AccelDueToGravity
308
309    class(cloud_type),         intent(inout) :: this
310    type(thermodynamics_type), intent(in)    :: thermodynamics
311    integer,                   intent(in)    :: istartcol, iendcol
312    real(jprb),                intent(in)    :: decorrelation_length(istartcol:iendcol) ! m
313
314    ! Ratio of gas constant for dry air to acceleration due to gravity
315    real(jprb), parameter :: R_over_g = GasConstantDryAir / AccelDueToGravity
316
317    integer :: ncol, nlev
318
319    integer :: jcol, jlev
320
321    real(jphook) :: hook_handle
322
323    if (lhook) call dr_hook('radiation_cloud:set_overlap_param_var',0,hook_handle)
324
325    ! Pressure at half-levels, pressure_hl, is defined at nlev+1
326    ! points
327    ncol = size(thermodynamics%pressure_hl,dim=1)
328    nlev = size(thermodynamics%pressure_hl,dim=2)-1
329
330    if (.not. allocated(this%overlap_param)) then
331      ! If pressure is of size (ncol,nlev+1) then overlap_param is of
332      ! size (ncol,nlev-1), since overlap parameter is only defined here
333      ! for interfaces between model layers, not for the interface to
334      ! space or the surface
335      allocate(this%overlap_param(ncol, nlev-1))
336    end if
337
338    if (thermodynamics%pressure_hl(istartcol,2) > thermodynamics%pressure_hl(istartcol,1)) then
339      ! Pressure is increasing with index (order of layers is
340      ! top-of-atmosphere to surface). In case pressure_hl(:,1)=0, we
341      ! don't take the logarithm of the first pressure in each column.
342      do jcol = istartcol,iendcol
343        this%overlap_param(jcol,1) = exp(-(R_over_g/decorrelation_length(jcol)) &
344             &                            * thermodynamics%temperature_hl(jcol,2) &
345             &                            *log(thermodynamics%pressure_hl(jcol,3) &
346             &                                /thermodynamics%pressure_hl(jcol,2)))
347      end do
348
349      do jlev = 2,nlev-1
350        do jcol = istartcol,iendcol
351          this%overlap_param(jcol,jlev) = exp(-(0.5_jprb*R_over_g/decorrelation_length(jcol)) &
352              &                            * thermodynamics%temperature_hl(jcol,jlev+1) &
353              &                            *log(thermodynamics%pressure_hl(jcol,jlev+2) &
354              &                                /thermodynamics%pressure_hl(jcol,jlev)))
355        end do
356      end do
357
358    else
359       ! Pressure is decreasing with index (order of layers is surface
360       ! to top-of-atmosphere).  In case pressure_hl(:,nlev+1)=0, we
361       ! don't take the logarithm of the last pressure in each column.
362      do jlev = 1,nlev-2
363        do jcol = istartcol,iendcol
364          this%overlap_param(jcol,jlev) = exp(-(0.5_jprb*R_over_g/decorrelation_length(jcol)) &
365              &                            * thermodynamics%temperature_hl(jcol,jlev+1) &
366              &                            *log(thermodynamics%pressure_hl(jcol,jlev) &
367              &                                /thermodynamics%pressure_hl(jcol,jlev+2)))
368        end do
369      end do
370
371      do jcol = istartcol,iendcol
372        this%overlap_param(jcol,nlev-1) = exp(-(R_over_g/decorrelation_length(jcol)) &
373            &                            * thermodynamics%temperature_hl(jcol,nlev) &
374            &                            *log(thermodynamics%pressure_hl(jcol,nlev-1) &
375            &                                /thermodynamics%pressure_hl(jcol,nlev)))
376      end do
377    end if
378
379    if (lhook) call dr_hook('radiation_cloud:set_overlap_param_var',1,hook_handle)
380
381  end subroutine set_overlap_param_var
382
383! AI 04 2024 variation de la longueur Ld en fonction de la verticale 
384  subroutine set_overlap_param_var2D(this, thermodynamics, decorrelation_length, &
385       &                           klev, istartcol, iendcol)
386
387    use yomhook,                  only : lhook, dr_hook, jphook
388    use radiation_thermodynamics, only : thermodynamics_type
389    use radiation_constants,      only : GasConstantDryAir, AccelDueToGravity
390
391    integer,                   intent(in)    :: klev
392    class(cloud_type),         intent(inout) :: this
393    type(thermodynamics_type), intent(in)    :: thermodynamics
394    integer,                   intent(in)    :: istartcol, iendcol
395    real(jprb),                intent(in)    :: decorrelation_length(istartcol:iendcol,klev) ! m
396
397    ! Ratio of gas constant for dry air to acceleration due to gravity
398    real(jprb), parameter :: R_over_g = GasConstantDryAir / AccelDueToGravity
399
400    integer :: ncol, nlev
401
402    integer :: jcol, jlev
403
404    real(jphook) :: hook_handle
405
406    if (lhook) call dr_hook('radiation_cloud:set_overlap_param_var',0,hook_handle)
407
408    ! Pressure at half-levels, pressure_hl, is defined at nlev+1
409    ! points
410    ncol = size(thermodynamics%pressure_hl,dim=1)
411    nlev = size(thermodynamics%pressure_hl,dim=2)-1
412
413    if (.not. allocated(this%overlap_param)) then
414      ! If pressure is of size (ncol,nlev+1) then overlap_param is of
415      ! size (ncol,nlev-1), since overlap parameter is only defined here
416      ! for interfaces between model layers, not for the interface to
417      ! space or the surface
418      allocate(this%overlap_param(ncol, nlev-1))
419    end if
420
421    if (thermodynamics%pressure_hl(istartcol,2) > thermodynamics%pressure_hl(istartcol,1)) then
422      ! Pressure is increasing with index (order of layers is
423      ! top-of-atmosphere to surface). In case pressure_hl(:,1)=0, we
424      ! don't take the logarithm of the first pressure in each column.
425      do jcol = istartcol,iendcol
426        this%overlap_param(jcol,1) = exp(-(R_over_g/decorrelation_length(jcol,1)) &
427             &                            * thermodynamics%temperature_hl(jcol,2) &
428             &                            *log(thermodynamics%pressure_hl(jcol,3) &
429             &                                /thermodynamics%pressure_hl(jcol,2)))
430      end do
431
432      do jlev = 2,nlev-1
433        do jcol = istartcol,iendcol
434          this%overlap_param(jcol,jlev) = exp(-(0.5_jprb*R_over_g/decorrelation_length(jcol,jlev)) &
435              &                            * thermodynamics%temperature_hl(jcol,jlev+1) &
436              &                            *log(thermodynamics%pressure_hl(jcol,jlev+2) &
437              &                                /thermodynamics%pressure_hl(jcol,jlev)))
438        end do
439      end do
440
441    else
442       ! Pressure is decreasing with index (order of layers is surface
443       ! to top-of-atmosphere).  In case pressure_hl(:,nlev+1)=0, we
444       ! don't take the logarithm of the last pressure in each column.
445      do jlev = 1,nlev-2
446        do jcol = istartcol,iendcol
447          this%overlap_param(jcol,jlev) = exp(-(0.5_jprb*R_over_g/decorrelation_length(jcol,jlev)) &
448              &                            * thermodynamics%temperature_hl(jcol,jlev+1) &
449              &                            *log(thermodynamics%pressure_hl(jcol,jlev) &
450              &                                /thermodynamics%pressure_hl(jcol,jlev+2)))
451        end do
452      end do
453
454      do jcol = istartcol,iendcol
455      ! AI ATTENTION a verifier decorrelation_length(jcol,nlev-1) ou nlev
456        this%overlap_param(jcol,nlev-1) = exp(-(R_over_g/decorrelation_length(jcol,nlev-1)) &
457            &                            * thermodynamics%temperature_hl(jcol,nlev) &
458            &                            *log(thermodynamics%pressure_hl(jcol,nlev-1) &
459            &                                /thermodynamics%pressure_hl(jcol,nlev)))
460      end do
461    end if
462
463    if (lhook) call dr_hook('radiation_cloud:set_overlap_param_var',1,hook_handle)
464
465  end subroutine set_overlap_param_var2D
466
467  !---------------------------------------------------------------------
468  ! Compute and store the overlap parameter from the provided overlap
469  ! decorrelation length (in metres).  If istartcol and/or iendcol are
470  ! provided then only columns in this range are computed.  If the
471  ! overlap_param array has not been allocated then it will be
472  ! allocated to be of the correct size relative to the pressure
473  ! field. This is the APPROXIMATE method as it assumes a fixed
474  ! atmospheric scale height, which leads to differences particularly
475  ! in low cloud.
476  subroutine set_overlap_param_approx(this, thermodynamics, decorrelation_length, &
477       &  istartcol, iendcol)
478
479    use yomhook,                  only : lhook, dr_hook, jphook
480    use radiation_thermodynamics, only : thermodynamics_type
481
482    class(cloud_type),         intent(inout) :: this
483    type(thermodynamics_type), intent(in)    :: thermodynamics
484    real(jprb),                intent(in)    :: decorrelation_length ! m
485    integer,         optional, intent(in)    :: istartcol, iendcol
486
487    ! To convert decorrelation length (m) to overlap parameter between
488    ! layers, we need an estimate for the thickness of the layer. This
489    ! is found using the pressure difference between the edges of the
490    ! layer, along with the approximate scale height of the atmosphere
491    ! (m) given here:
492    real(jprb), parameter :: scale_height = 8000.0_jprb
493
494    ! Process only columns i1 to i2, which will be istartcol to
495    ! iendcol if they were provided
496    integer :: i1, i2
497
498    integer :: ncol, nlev
499
500    real(jphook) :: hook_handle
501
502    if (lhook) call dr_hook('radiation_cloud:set_overlap_param_approx',0,hook_handle)
503
504    ! Pressure at half-levels, pressure_hl, is defined at nlev+1
505    ! points
506    ncol = size(thermodynamics%pressure_hl,dim=1)
507    nlev = size(thermodynamics%pressure_hl,dim=2)-1
508
509    if (present(istartcol)) then
510      i1 = istartcol
511    else
512      i1 = 1
513    end if
514
515    if (present(iendcol)) then
516      i2 = iendcol
517    else
518      i2 = ncol
519    end if
520
521    if (.not. allocated(this%overlap_param)) then
522      ! If pressure is of size (ncol,nlev+1) then overlap_param is of
523      ! size (ncol,nlev-1), since overlap parameter is only defined here
524      ! for interfaces between model layers, not for the interface to
525      ! space or the surface
526      allocate(this%overlap_param(ncol, nlev-1))
527    end if
528
529    if (thermodynamics%pressure_hl(i1,2) > thermodynamics%pressure_hl(i1,1)) then
530       ! Pressure is increasing with index (order of layers is
531       ! top-of-atmosphere to surface). In case pressure_hl(:,1)=0, we
532       ! don't take the logarithm of the first pressure in each
533       ! column.
534       this%overlap_param(i1:i2,:) = exp(-(scale_height/decorrelation_length) &
535            &  * ( log(thermodynamics%pressure_hl(i1:i2,3:nlev+1) &
536            &         /thermodynamics%pressure_hl(i1:i2,2:nlev  )) ) )
537    else
538       ! Pressure is decreasing with index (order of layers is surface
539       ! to top-of-atmosphere).  In case pressure_hl(:,nlev+1)=0, we
540       ! don't take the logarithm of the last pressure in each column.
541       this%overlap_param(i1:i2,:) = exp(-(scale_height/decorrelation_length) &
542            &  * ( log(thermodynamics%pressure_hl(i1:i2,1:nlev-1) &
543            &         /thermodynamics%pressure_hl(i1:i2,2:nlev  )) ) )
544    end if
545
546    if (lhook) call dr_hook('radiation_cloud:set_overlap_param_approx',1,hook_handle)
547
548  end subroutine set_overlap_param_approx
549
550
551  !---------------------------------------------------------------------
552  ! Create a matrix of constant fractional standard deviations
553  ! (dimensionless)
554  subroutine create_fractional_std(this, ncol, nlev, frac_std)
555
556    use yomhook,                  only : lhook, dr_hook, jphook
557
558    class(cloud_type), intent(inout) :: this
559    integer,           intent(in)    :: ncol, nlev
560    real(jprb),        intent(in)    :: frac_std
561
562    real(jphook) :: hook_handle
563
564    if (lhook) call dr_hook('radiation_cloud:create_fractional_std',0,hook_handle)
565
566    if (allocated(this%fractional_std)) then
567       deallocate(this%fractional_std)
568    end if
569   
570    allocate(this%fractional_std(ncol, nlev))
571
572    this%fractional_std = frac_std
573
574    if (lhook) call dr_hook('radiation_cloud:create_fractional_std',1,hook_handle)
575
576  end subroutine create_fractional_std
577
578
579  !---------------------------------------------------------------------
580  ! Create a matrix of constant inverse cloud effective size (m-1)
581  subroutine create_inv_cloud_effective_size(this, ncol, nlev, inv_eff_size)
582
583    use yomhook,                  only : lhook, dr_hook, jphook
584
585    class(cloud_type), intent(inout) :: this
586    integer,           intent(in)    :: ncol, nlev
587    real(jprb),        intent(in)    :: inv_eff_size
588
589    real(jphook) :: hook_handle
590
591    if (lhook) call dr_hook('radiation_cloud:create_inv_cloud_effective_size',0,hook_handle)
592
593    if (allocated(this%inv_cloud_effective_size)) then
594       deallocate(this%inv_cloud_effective_size)
595    end if
596   
597    allocate(this%inv_cloud_effective_size(ncol, nlev))
598
599    this%inv_cloud_effective_size = inv_eff_size
600
601    if (lhook) call dr_hook('radiation_cloud:create_inv_cloud_effective_size',1,hook_handle)
602
603  end subroutine create_inv_cloud_effective_size
604
605
606  !---------------------------------------------------------------------
607  ! Create a matrix of inverse cloud effective size (m-1) according to
608  ! the value of eta (=pressure divided by surface pressure)
609  subroutine create_inv_cloud_effective_size_eta(this, ncol, nlev, &
610       &  pressure_hl, inv_eff_size_low, inv_eff_size_mid, inv_eff_size_high, &
611       &  eta_low_mid, eta_mid_high, istartcol, iendcol)
612
613    use yomhook,                  only : lhook, dr_hook, jphook
614
615    class(cloud_type), intent(inout) :: this
616    integer,           intent(in)    :: ncol, nlev
617    ! Pressure on half levels (Pa)
618    real(jprb),        intent(in)    :: pressure_hl(:,:)
619    ! Inverse effective size for low, mid and high cloud (m-1)
620    real(jprb),        intent(in)    :: inv_eff_size_low
621    real(jprb),        intent(in)    :: inv_eff_size_mid
622    real(jprb),        intent(in)    :: inv_eff_size_high
623    ! Eta values at low-mid and mid-high interfaces
624    real(jprb),        intent(in)    :: eta_low_mid, eta_mid_high
625    integer, optional, intent(in)    :: istartcol, iendcol
626
627    ! Ratio of layer midpoint pressure to surface pressure
628    real(jprb) :: eta(nlev)
629
630    ! Indices of column, level and surface half-level
631    integer :: jcol, isurf
632
633    ! Local values of istartcol, iendcol
634    integer :: i1, i2
635
636    real(jphook) :: hook_handle
637
638    if (lhook) call dr_hook('radiation_cloud:create_inv_cloud_effective_size_eta',0,hook_handle)
639
640    if (allocated(this%inv_cloud_effective_size)) then
641      deallocate(this%inv_cloud_effective_size)
642    end if
643   
644    allocate(this%inv_cloud_effective_size(ncol, nlev))
645
646    if (present(istartcol)) then
647      i1 = istartcol
648    else
649      i1 = 1
650    end if
651
652    if (present(iendcol)) then
653      i2 = iendcol
654    else
655      i2 = ncol
656    end if
657
658    ! Locate the surface half-level
659    if (pressure_hl(1,1) > pressure_hl(1,2)) then
660      isurf = 1
661    else
662      isurf = nlev+1
663    end if
664
665    do jcol = i1,i2
666      eta = (pressure_hl(jcol,1:nlev)+pressure_hl(jcol,2:nlev+1)) &
667           &  * (0.5_jprb / pressure_hl(jcol,isurf))
668      where (eta > eta_low_mid)
669        this%inv_cloud_effective_size(jcol,:) = inv_eff_size_low
670      elsewhere (eta > eta_mid_high)
671        this%inv_cloud_effective_size(jcol,:) = inv_eff_size_mid
672      elsewhere
673        this%inv_cloud_effective_size(jcol,:) = inv_eff_size_high
674      end where
675    end do
676
677    if (lhook) call dr_hook('radiation_cloud:create_inv_cloud_effective_size_eta',1,hook_handle)
678
679  end subroutine create_inv_cloud_effective_size_eta
680
681
682  !---------------------------------------------------------------------
683  ! Create a matrix of inverse cloud and inhomogeneity effective size
684  ! (m-1) parameterized according to the value of eta (=pressure
685  ! divided by surface pressure): effective_separation =
686  ! coeff_a + coeff_b*exp(-(eta**power)). 
687  subroutine param_cloud_effective_separation_eta(this, ncol, nlev, &
688       &  pressure_hl, separation_surf, separation_toa, power, &
689       &  inhom_separation_factor, istartcol, iendcol)
690
691    use yomhook,                  only : lhook, dr_hook, jphook
692
693    class(cloud_type), intent(inout) :: this
694    integer,           intent(in)    :: ncol, nlev
695    ! Pressure on half levels (Pa)
696    real(jprb),        intent(in)    :: pressure_hl(:,:)
697    ! Separation distances at surface and top-of-atmosphere, and power
698    ! on eta
699    real(jprb),           intent(in) :: separation_surf ! m
700    real(jprb),           intent(in) :: separation_toa ! m
701    real(jprb),           intent(in) :: power
702    real(jprb), optional, intent(in) :: inhom_separation_factor
703    integer,    optional, intent(in) :: istartcol, iendcol
704
705    ! Ratio of layer midpoint pressure to surface pressure
706    real(jprb) :: eta(nlev)
707
708    ! Effective cloud separation (m)
709    real(jprb) :: eff_separation(nlev)
710
711    ! Coefficients used to compute effective separation distance
712    real(jprb) :: coeff_e, coeff_a, coeff_b, inhom_sep_factor
713
714    ! Indices of column, level and surface half-level
715    integer :: jcol, isurf
716
717    ! Local values of istartcol, iendcol
718    integer :: i1, i2
719
720    real(jphook) :: hook_handle
721
722    if (lhook) call dr_hook('radiation_cloud:param_cloud_effective_separation_eta',0,hook_handle)
723
724    if (present(inhom_separation_factor)) then
725      inhom_sep_factor = inhom_separation_factor
726    else
727      inhom_sep_factor = 1.0_jprb
728    end if
729
730    coeff_e = 1.0_jprb - exp(-1.0_jprb)
731    coeff_b = (separation_toa - separation_surf) / coeff_e
732    coeff_a = separation_toa - coeff_b
733
734    if (allocated(this%inv_cloud_effective_size)) then
735      deallocate(this%inv_cloud_effective_size)
736    end if
737     if (allocated(this%inv_inhom_effective_size)) then
738      deallocate(this%inv_inhom_effective_size)
739    end if
740   
741    allocate(this%inv_cloud_effective_size(ncol, nlev))
742    allocate(this%inv_inhom_effective_size(ncol, nlev))
743
744    if (present(istartcol)) then
745      i1 = istartcol
746    else
747      i1 = 1
748    end if
749
750    if (present(iendcol)) then
751      i2 = iendcol
752    else
753      i2 = ncol
754    end if
755
756    ! Locate the surface half-level
757    if (pressure_hl(1,1) > pressure_hl(1,2)) then
758      isurf = 1
759    else
760      isurf = nlev+1
761    end if
762
763    do jcol = i1,i2
764      eta = (pressure_hl(jcol,1:nlev)+pressure_hl(jcol,2:nlev+1)) &
765           &  * (0.5_jprb / pressure_hl(jcol,isurf))
766      eff_separation = coeff_a + coeff_b * exp(-eta**power)
767      this%inv_cloud_effective_size(jcol,:) = 1.0_jprb / (eff_separation &
768           &  * sqrt(max(1.0e-5_jprb,this%fraction(jcol,:)*(1.0_jprb-this%fraction(jcol,:)))))
769      this%inv_inhom_effective_size(jcol,:) = 1.0_jprb / (eff_separation * inhom_sep_factor &
770           &  * sqrt(max(1.0e-5_jprb,0.5_jprb*this%fraction(jcol,:)*(1.0_jprb-0.5_jprb*this%fraction(jcol,:)))))
771    end do
772
773    if (lhook) call dr_hook('radiation_cloud:param_cloud_effective_separation_eta',1,hook_handle)
774
775  end subroutine param_cloud_effective_separation_eta
776
777  !AI 20 mai 2025
778 !Nouvelle param Lx=f(P) avec Lf(P) tgte hyperb
779 subroutine param_cloud_effective_separation_tanh(this, ncol, nlev, &
780     &  pressure_hl, &
781     &  lx_surf, lx_toa, gama, &
782     &  inhom_separation_factor, istartcol, iendcol)
783
784  use yomhook,                  only : lhook, dr_hook, jphook
785  use write_field_phy
786  implicit none
787
788  class(cloud_type), intent(inout) :: this
789  integer,           intent(in)    :: ncol, nlev
790  ! Pressure on half levels (Pa) : size should be (ncol, nlev+1)
791  real(jprb),        intent(in)    :: pressure_hl(:,:)
792  ! Separation distances at surface and top-of-atmosphere (m)
793  real(jprb),        intent(in)    :: lx_surf, lx_toa
794  ! Facteur de raideur
795  real(jprb),        intent(in)    :: gama
796  ! Optional factor
797  real(jprb), optional, intent(in) :: inhom_separation_factor
798  integer,    optional, intent(in) :: istartcol, iendcol
799
800  ! Parameters (AI attention a revoir)
801  real(kind=jprb), parameter :: eps = 1.0e-5_jprb
802
803  ! Locals
804  real(jprb) :: eff_separation(ncol,nlev)
805  real(jprb) :: Lmin, Lmax, Pmin, Pmax, p_mean, p0, inhom_sep_factor, alpha
806  integer :: jcol, k
807  integer :: i1, i2
808  real(jphook) :: hook_handle
809  real(jprb) :: frac, denom1, denom2
810  real(jprb) :: pdiff
811
812  ! Hook entry
813  if (lhook) call dr_hook('radiation_cloud:param_cloud_effective_separation_eta',0,hook_handle)
814
815  ! inhom separation factor default
816  if (present(inhom_separation_factor)) then
817    inhom_sep_factor = inhom_separation_factor
818  else
819    inhom_sep_factor = 1.0_jprb
820  end if
821
822  ! (re)allocation of output arrays
823  if (allocated(this%inv_cloud_effective_size)) then
824    deallocate(this%inv_cloud_effective_size)
825  end if
826  if (allocated(this%inv_inhom_effective_size)) then
827    deallocate(this%inv_inhom_effective_size)
828  end if
829
830  allocate(this%inv_cloud_effective_size(ncol, nlev))
831  allocate(this%inv_inhom_effective_size(ncol, nlev))
832
833  ! columns range
834  if (present(istartcol)) then
835    i1 = istartcol
836  else
837    i1 = 1
838  end if
839
840  if (present(iendcol)) then
841    i2 = iendcol
842  else
843    i2 = ncol
844  end if
845
846  ! map inputs to local names
847  Lmin = lx_surf
848  Lmax = lx_toa
849
850  ! Determine pressure range (expect pressure_hl(:,1) ~ surface, (:,nlev+1) ~ TOA)
851  ! Use safe guards if pressure_hl dimension is smaller/wrong => runtime will fail earlier
852  Pmin = minval(pressure_hl(:,nlev+1))
853  Pmax = maxval(pressure_hl(:,1))
854
855  ! compute centre and alpha (guard if Pmax==Pmin)
856  p0 = 0.5_jprb*(Pmax + Pmin)
857  pdiff = (Pmax - Pmin)
858  if (abs(pdiff) < eps) then
859    ! improbable but guard against division by zero
860    alpha = gama / eps
861    write(*,*) 'Warning: Pmax - Pmin trop petit -> alpha forcé'
862  else
863    alpha = gama / pdiff
864  end if
865
866  ! Main loops
867  do jcol = i1, i2
868    do k = 1, nlev
869      ! pression moyenne de la couche k (half-levels k and k+1 must exist)
870      p_mean = 0.5_jprb * (pressure_hl(jcol,k) + pressure_hl(jcol,k+1))
871
872      ! loi hyperbolique bornée entre Lmin et Lmax
873      eff_separation(jcol,k) = 0.5_jprb*(Lmax + Lmin) - 0.5_jprb*(Lmax - Lmin) * tanh(alpha*(p_mean - p0))
874
875      ! borne de sécurité (eviter 0 ou négatif)
876      eff_separation(jcol,k) = max(eps, eff_separation(jcol,k))
877
878      ! fraction safe clamp
879      frac = this%fraction(jcol,k)
880      if (frac < 0.0_jprb) then
881        frac = 0.0_jprb
882      else if (frac > 1.0_jprb) then
883        frac = 1.0_jprb
884      end if
885
886      ! denominators and protection
887      denom1 = eff_separation(jcol,k) * sqrt( max(eps, frac*(1.0_jprb - frac)) )
888      if (denom1 < eps) denom1 = eps
889      this%inv_cloud_effective_size(jcol,k) = 1.0_jprb / denom1
890
891      denom2 = eff_separation(jcol,k) * inhom_sep_factor * sqrt( max(eps, 0.5_jprb*frac*(1.0_jprb - 0.5_jprb*frac)) )
892      if (denom2 < eps) denom2 = eps
893      this%inv_inhom_effective_size(jcol,k) = 1.0_jprb / denom2
894
895    end do
896  end do
897
898  ! diagnostics
899  !CALL writefield_phy('pressure_hl',pressure_hl,nlev+1)
900  !CALL writefield_phy('eff_lx',eff_separation,nlev)
901
902  if (lhook) call dr_hook('radiation_cloud:param_cloud_effective_separation_eta',1,hook_handle)
903
904 end subroutine param_cloud_effective_separation_tanh
905 
906  !---------------------------------------------------------------------
907  ! Remove "ghost" clouds: those with a cloud fraction that is too
908  ! small to treat sensibly (e.g. because it implies that the
909  ! "in-cloud" water content is too high), or with a cloud water
910  ! content that is too small.  We do this in one place to ensure that
911  ! all subsequent subroutines can assume that if cloud_fraction > 0.0
912  ! then cloud is really present and should be treated.
913  subroutine crop_cloud_fraction(this, istartcol, iendcol, &
914       &    cloud_fraction_threshold, cloud_mixing_ratio_threshold)
915   
916    use yomhook, only : lhook, dr_hook, jphook
917
918    class(cloud_type), intent(inout) :: this
919    integer,           intent(in)    :: istartcol, iendcol
920
921    integer :: nlev, ntype
922    integer :: jcol, jlev, jh
923
924    real(jprb) :: cloud_fraction_threshold, cloud_mixing_ratio_threshold
925    real(jprb) :: sum_mixing_ratio(istartcol:iendcol)
926
927    real(jphook) :: hook_handle
928
929    if (lhook) call dr_hook('radiation_cloud:crop_cloud_fraction',0,hook_handle)
930
931    nlev  = size(this%fraction,2)
932    ntype = size(this%mixing_ratio,3)
933   
934    do jlev = 1,nlev
935      do jcol = istartcol,iendcol
936        sum_mixing_ratio(jcol) = 0.0_jprb
937      end do
938      do jh = 1, ntype
939        do jcol = istartcol,iendcol
940          sum_mixing_ratio(jcol) = sum_mixing_ratio(jcol) + this%mixing_ratio(jcol,jlev,jh)
941        end do
942      end do
943      do jcol = istartcol,iendcol
944        if (this%fraction(jcol,jlev)        < cloud_fraction_threshold &
945             &  .or. sum_mixing_ratio(jcol) < cloud_mixing_ratio_threshold) then
946          this%fraction(jcol,jlev) = 0.0_jprb
947        end if
948      end do
949    end do
950
951    if (lhook) call dr_hook('radiation_cloud:crop_cloud_fraction',1,hook_handle)
952
953  end subroutine crop_cloud_fraction
954
955
956  !---------------------------------------------------------------------
957  ! Return .true. if variables are out of a physically sensible range,
958  ! optionally only considering columns between istartcol and iendcol
959  function out_of_physical_bounds(this, istartcol, iendcol, do_fix) result(is_bad)
960
961    use yomhook,          only : lhook, dr_hook, jphook
962    use radiation_check, only : out_of_bounds_2d, out_of_bounds_3d
963
964    class(cloud_type), intent(inout) :: this
965    integer,  optional,intent(in) :: istartcol, iendcol
966    logical,  optional,intent(in) :: do_fix
967    logical                       :: is_bad
968
969    logical    :: do_fix_local
970
971    real(jphook) :: hook_handle
972
973    if (lhook) call dr_hook('radiation_cloud:out_of_physical_bounds',0,hook_handle)
974
975    if (present(do_fix)) then
976      do_fix_local = do_fix
977    else
978      do_fix_local = .false.
979    end if
980
981    is_bad =    out_of_bounds_3d(this%mixing_ratio, 'cloud%mixing_ratio', 0.0_jprb, 1.0_jprb, &
982         &                       do_fix_local, i1=istartcol, i2=iendcol) &
983         & .or. out_of_bounds_3d(this%effective_radius, 'cloud%effective_radius', 0.0_jprb, 0.1_jprb, &
984         &                       do_fix_local, i1=istartcol, i2=iendcol) &
985         & .or. out_of_bounds_2d(this%fraction, 'cloud%fraction', 0.0_jprb, 1.0_jprb, &
986         &                       do_fix_local, i1=istartcol, i2=iendcol) &
987         & .or. out_of_bounds_2d(this%fractional_std, 'fractional_std', 0.0_jprb, 10.0_jprb, &
988         &                       do_fix_local, i1=istartcol, i2=iendcol) &
989         & .or. out_of_bounds_2d(this%inv_cloud_effective_size, 'inv_cloud_effective_size', &
990         &                       0.0_jprb, 1.0_jprb, do_fix_local, i1=istartcol, i2=iendcol) &
991         & .or. out_of_bounds_2d(this%inv_inhom_effective_size, 'inv_inhom_effective_size', &
992         &                       0.0_jprb, 1.0_jprb, do_fix_local, i1=istartcol, i2=iendcol) &
993         & .or. out_of_bounds_2d(this%overlap_param, 'overlap_param', -0.5_jprb, 1.0_jprb, &
994         &                       do_fix_local, i1=istartcol, i2=iendcol)
995
996    if (lhook) call dr_hook('radiation_cloud:out_of_physical_bounds',1,hook_handle)
997
998  end function out_of_physical_bounds
999 
1000end module radiation_cloud
Note: See TracBrowser for help on using the repository browser.