source: trunk/LMDZ.TITAN/libf/muphytitan/mm_clouds.f90 @ 4084

Last change on this file since 4084 was 4084, checked in by cpetetin, 2 months ago

TITAN PCM : ccn moment first layer correction CP

File size: 35.4 KB
Line 
1! Copyright (2013-2015,2017,2022-2023) Université de Reims Champagne-Ardenne
2! Contributors : J. Burgalat (GSMA, URCA), B. de Batz de Trenquelléon (GSMA, URCA)
3! email of the author : jeremie.burgalat@univ-reims.fr
4!
5! This software is a computer program whose purpose is to compute
6! microphysics processes using a two-moments scheme.
7!
8! This library is governed by the CeCILL-B license under French law and
9! abiding by the rules of distribution of free software.  You can  use,
10! modify and/ or redistribute the software under the terms of the CeCILL-B
11! license as circulated by CEA, CNRS and INRIA at the following URL
12! "http://www.cecill.info".
13!
14! As a counterpart to the access to the source code and  rights to copy,
15! modify and redistribute granted by the license, users are provided only
16! with a limited warranty  and the software's author,  the holder of the
17! economic rights,  and the successive licensors  have only  limited
18! liability.
19!
20! In this respect, the user's attention is drawn to the risks associated
21! with loading,  using,  modifying and/or developing or reproducing the
22! software by the user in light of its specific status of free software,
23! that may mean  that it is complicated to manipulate,  and  that  also
24! therefore means  that it is reserved for developers  and  experienced
25! professionals having in-depth computer knowledge. Users are therefore
26! encouraged to load and test the software's suitability as regards their
27! requirements in conditions enabling the security of their systems and/or
28! data to be ensured and,  more generally, to use and operate it in the
29! same conditions as regards security.
30!
31! The fact that you are presently reading this means that you have had
32! knowledge of the CeCILL-B license and that you accept its terms.
33
34!! file: mm_clouds.f90
35!! summary: Clouds microphysics module
36!! author: J. Burgalat
37!! date: 2013-2015,2017,2022-2023
38!! modifications: B. de Batz de Trenquelléon
39
40MODULE MM_CLOUDS
41  !! Clouds microphysics module.
42  !!
43  !! The module contains all definitions of the microphysics processes related to clouds:
44  !!
45  !! - [nucleation](page/clouds.html#nucleation)
46  !! - [condensation](page/clouds.html#condensation)
47  !! - [sedimentation](page/clouds.html#sedimentation)
48  !!
49  !!
50  !! The interface methods always use the global variables defined in [[mm_globals(module)]] when values
51  !! (temperature, pressure, moments...) over the vertical grid are required.
52  !! Consequently, all these functions only deal with output arguments which are most of the time the
53  !! tendencies of relevant variables on the atmospheric column.
54  !!
55  !! @note
56  !! Tendencies returned by public methods are always defined from  __TOP__ of the atmosphere to the
57  !! __GROUND__.
58  USE MM_MPREC
59  USE MM_GLOBALS
60  USE MM_METHODS
61  IMPLICIT NONE
62
63  PRIVATE
64
65  PUBLIC :: mm_cloud_microphysics, mm_cloud_sedimentation, mm_cloud_nucond
66
67CONTAINS
68
69  !============================================================================
70  ! CLOUDS MICROPHYSICS INTERFACE SUBROUTINE
71  !============================================================================
72
73  SUBROUTINE mm_cloud_microphysics(dm0a,dm3a,dm0n,dm3n,dm3i,dgazs)
74    !! Get the evolution of moments tracers through clouds microphysics processes.
75    !!
76    !! The subroutine is a wrapper to the clouds microphysics methods. It computes the tendencies of moments
77    !! tracers for nucleation, condensation and sedimentation processes for the atmospheric column.
78    !!
79    !! @note
80    !! Both __dm3i__ and __dgazs__ are 2D-array with the vertical layers in first dimension and the number
81    !! of ice components in the second.
82    REAL(kind=mm_wp), DIMENSION(:), INTENT(out)   :: dm0a
83    !! Tendency of the 0th order moment of the aerosols (fractal mode) (\(m^{-3}\)).
84    REAL(kind=mm_wp), DIMENSION(:), INTENT(out)   :: dm3a
85    !! Tendency of the 3rd order moment of the aerosols distribution (fractal mode) (\(m^{3}.m^{-3}\)) .
86    REAL(kind=mm_wp), DIMENSION(:), INTENT(out)   :: dm0n
87    !! Tendency of the 0th order moment of the ccn distribution (\(m^{-3}\)).
88    REAL(kind=mm_wp), DIMENSION(:), INTENT(out)   :: dm3n
89    !! Tendency of the 3rd order moment of the ccn distribution (\(m^{3}.m^{-3}\)).
90    REAL(kind=mm_wp), DIMENSION(:,:), INTENT(out) :: dm3i
91    !! Tendencies of the 3rd order moments of each ice components (\(m^{3}.m^{-3}\)).
92    REAL(kind=mm_wp), DIMENSION(:,:), INTENT(out) :: dgazs
93    !! Tendencies of each condensible gaz species (\(mol.mol^{-1}\)).
94    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE   :: zdm0n,zdm3n
95    REAL(kind=mm_wp), DIMENSION(:,:), ALLOCATABLE :: zdm3i
96    INTEGER                                    :: i
97    dm0a = 0._mm_wp ; dm3a = 0._mm_wp
98    dm0n = 0._mm_wp ; dm3n = 0._mm_wp
99    dm3i = 0._mm_wp ; dgazs = 0._mm_wp
100
101    IF (mm_w_cloud_nucond) THEN
102      ! Calls condensation/nucleation (and update saturation ratio diagnostic)
103      ! ADDED : Extraction of nucleation and growth rates
104      call mm_cloud_nucond(dm0a,dm3a,dm0n,dm3n,dm3i,dgazs,mm_gazs_sat,mm_nrate,mm_grate)
105    ENDIF
106
107    IF (mm_w_cloud_sed) THEN
108      ! Calls sedimentation
109      ALLOCATE(zdm0n(mm_nla),zdm3n(mm_nla),zdm3i(mm_nla,mm_nesp))
110      call mm_cloud_sedimentation(zdm0n,zdm3n,zdm3i)
111
112      ! Computes settling velocity [m.s-1] of clouds (ccn and ices)
113      mm_ccn_vsed(:) = wsettle(mm_play,mm_temp,mm_zlay,mm_drho,mm_drad)
114
115      ! Computes flux [kg.m-2.s-1] and precipitation [kg.m-2] of ccn
116      mm_ccn_flux(:) = get_mass_flux(mm_rhoaer,mm_m3ccn(:))
117      mm_ccn_prec = SUM(zdm3n*mm_dzlev*mm_rhoaer)
118
119      ! Computes flux [kg.m-2.s-1] and precipitation [kg.m-2] of ices
120      DO i = 1, mm_nesp
121        mm_ice_fluxes(:,i) = get_mass_flux(mm_xESPS(i)%rho,(3._mm_wp*mm_m3ice(:,i))/(4._mm_wp*mm_pi))
122        mm_ice_prec(i) = SUM(zdm3i(:,i)*mm_dzlev*mm_xESPS(i)%rho)
123      ENDDO
124
125      ! updates tendencies
126      dm0n = dm0n + zdm0n
127      dm3n = dm3n + zdm3n
128      dm3i = dm3i + zdm3i
129    ENDIF
130
131  END SUBROUTINE mm_cloud_microphysics
132
133  !-----------------------------------------------------------------------------
134  ! NUCLEATION/CONDENSATION PROCESS RELATED METHODS
135  !-----------------------------------------------------------------------------
136
137  SUBROUTINE mm_cloud_nucond(dm0a,dm3a,dm0n,dm3n,dm3i,dgazs,gazsat,nrate,grate)
138    !! Get moments tendencies through nucleation/condensation/evaporation.
139    !!
140    !! The method is a wrapper of [[mm_clouds(module):nc_esp(subroutine)]] which computes the
141    !! tendencies of tracers for all the condensible species given in the vector __xESPS__.
142    !!
143    !! Aerosols and CCN distribution evolution depends on the ice components:
144    !!   - For nucleation only creation of CCN can occur.
145    !!   - For condensation only loss of CCN can occur.
146    !!
147    !! We use the simple following rule to compute the variation of CCN and aerosols:
148    !! The global variation of CCN (and thus aerosols) is determined from the most intense activity
149    !! of the ice components.
150    !!
151    !! @warning
152    !! __xESPS__, __m3i__ and __gazes__ must share the same indexing. For example if __xESPS(IDX)__
153    !! corresponds to \(CH_{4}\) properties then __m3i(IDX)__ must be the total volume of solid
154    !! \(CH_{4}\) (ice) and  __gazs(IDX)__ its vapor mole fraction.
155    REAL(kind=mm_wp), DIMENSION(:), INTENT(out)   :: dm0a
156    !! Tendency of the 0th order moment of the aerosols (fractal mode) (\(m^{-3}\)).
157    REAL(kind=mm_wp), DIMENSION(:), INTENT(out)   :: dm3a
158    !! Tendency of the 3rd order moment of the aerosols distribution (fractal mode) (\(m^{3}.m^{-3}\)).
159    REAL(kind=mm_wp), DIMENSION(:), INTENT(out)   :: dm0n
160    !! Tendency of the 0th order moment of the aerosols distribution (fractal mode) (\(m^{-3}\)).
161    REAL(kind=mm_wp), DIMENSION(:), INTENT(out)   :: dm3n
162    !! Tendency of the 3rd order moment of the ccn distribution (\(m^{3}.m^{-3}\)).
163    REAL(kind=mm_wp), DIMENSION(:,:), INTENT(out) :: dm3i
164    !! Tendencies of the 3rd order moments of each ice components (\(m^{3}.m^{-3}\)).
165    REAL(kind=mm_wp), DIMENSION(:,:), INTENT(out) :: dgazs
166    !! Tendencies of each condensible gaz species (\(mol.mol^{-1}\)) .
167    REAL(kind=mm_wp), DIMENSION(:,:), INTENT(out) :: gazsat
168    !! Saturation ratio of each condensible specie.
169
170    ! ADDED : Extraction of the nucleation and growth rate
171    REAL(kind=mm_wp), DIMENSION(:,:),INTENT(out) :: nrate
172    !! Nucleation rate values of each condensible species (\(m^{-2}.s^{-1}\)).
173    REAL(kind=mm_wp), DIMENSION(:,:),INTENT(out) :: grate
174    !! Growth rate values of each condensible species (\(m^{2}.s^{-1}\)).
175
176    INTEGER                                       :: i,idx
177    REAL(kind=mm_wp), DIMENSION(:,:), ALLOCATABLE :: zdm0a,zdm3a,zdm0n,zdm3n
178
179    ALLOCATE(zdm0a(mm_nla,mm_nesp),zdm3a(mm_nla,mm_nesp), &
180             zdm0n(mm_nla,mm_nesp),zdm3n(mm_nla,mm_nesp))
181    zdm0a(:,:) = 0._mm_wp ; zdm3a(:,:) = 0._mm_wp
182    zdm0n(:,:) = 0._mm_wp ; zdm3n(:,:) = 0._mm_wp
183    DO i = 1, mm_nesp
184      call nc_esp(mm_xESPS(i),mm_gazs(:,i),mm_m3ice(:,i),dgazs(:,i),dm3i(:,i), &
185                  zdm0a(:,i),zdm3a(:,i),zdm0n(:,i),zdm3n(:,i),gazsat(:,i),nrate(:,i),grate(:,i))
186      ! ADDED : Extraction of nucleation and growth rates
187    ENDDO
188
189    DO i=1, mm_nla
190      ! retrieve the index of the maximum tendency of CCN where ice variation is not null.
191      idx = MAXLOC(zdm0n(i,:),DIM=1,MASK=(dm3i(i,:) /= 0._mm_wp .OR. mm_m3ice(i,:)+dm3i(i,:) >= 0._mm_wp))
192      IF (idx == 0) THEN
193        dm0n(i) = 0._mm_wp
194        dm3n(i) = 0._mm_wp
195        dm0a(i) = 0._mm_wp
196        dm3a(i) = 0._mm_wp
197      ELSE
198        IF (mm_debug .AND. ABS(zdm0n(i,idx)) > 1e3) THEN
199          WRITE(*,'((a),I2.2,(a),ES10.3,(a))') "Z(",i,") = ",mm_play(i)*1e2, &
200            " mbar: Max aer/ccn exchange variation due to specie: "//TRIM(mm_xESPS(idx)%name)
201        ENDIF
202        dm0n(i) = zdm0n(i,idx)
203        dm3n(i) = zdm3n(i,idx)
204        dm0a(i) = zdm0a(i,idx)
205        dm3a(i) = zdm3a(i,idx)
206      ENDIF
207    ENDDO
208
209  END SUBROUTINE mm_cloud_nucond
210
211  SUBROUTINE nc_esp(xESP,vapX,m3iX,dvapX,dm3iX,dm0aer,dm3aer,dm0ccn,dm3ccn,Xsat,Xnrate,Xgrate) ! ADDED : arguments nrate, grate
212    !! Get moments tendencies through nucleation/condensation/evaporation of a given condensible specie.
213    !!
214    !! The method computes the global tendencies of the aerosols, ccn and "ice" moments through cloud
215    !! microphysics processes (nucleation & condensation).
216    !!
217    !! @warning
218    !! Input quantities __m3iX__,__m3iO__, __m0aer__,__m3aer__, __m0ccn__,__m3ccn__ are assumed to be in
219    !! \(X.m^{-3}\) (where X is the unit of the moment that is, a number for M0 and a volume - \(m^3\)
220    !! for M3) ; __vapX__ must be expressed in term of molar fraction.
221    TYPE(mm_esp), INTENT(in)                   :: xESP
222    !! Condensate specie properties.
223    REAL(kind=mm_wp),INTENT(in), DIMENSION(:)  :: vapX
224    !! Gas specie molar fraction on the vertical grid from __TOP__ to __GROUND__ (\(mol.mol^{-1}\)).
225    REAL(kind=mm_wp),INTENT(in), DIMENSION(:)  :: m3iX
226    !! 3rd order moment of the ice component (\(m^{3}.m^{-3}\)).
227    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: dvapX
228    !! Tendency of gas specie (\(mol.mol^{-1}\)).
229    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: dm3iX
230    !! Tendency of the 3rd order moment of the ice component (\(m^{3}.m^{-3}\)).
231    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: dm0aer
232    !! Tendency of the 0th order moment of the fractal mode distribution (\(m^{-3}\)).
233    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: dm3aer
234    !! Tendency of the 3rd order moment of the fractal mode distribution (\(m^{3}.m^{-3}\)).
235    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: dm0ccn
236    !! Tendency of the 0th order moment of the ccn distribution (\(m^{-3}\)).
237    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: dm3ccn
238    !! Tendency of the 3rd order moment of the ccn distribution (\(m^{3}.m^{-3}\)).
239    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: Xsat
240    !! Saturation ratio values on the vertical grid (--).
241
242    ! ADDED : Extraction of the nucleation and growth rate
243    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: Xnrate
244    !! Nucleation rate values on the vertical grid for the species X (\(m^{-2}.s^{-1}\)).
245    REAL(kind=mm_wp),INTENT(out), DIMENSION(:) :: Xgrate
246    !! Growth rate values on the vertical grid for the species X (\(m^{2}.s^{-1}\)).
247
248
249    INTEGER                                 :: i
250    REAL(kind=mm_wp)                        :: bef,aft
251    REAL(kind=mm_wp), DIMENSION(SIZE(vapX)) :: sm0a,sm3a,sm0n,sm3n,sm3iX
252    REAL(kind=mm_wp), DIMENSION(SIZE(vapX)) :: zm0a,zm3a,zm0n,zm3n,zm3iX,zvapX
253    REAL(kind=mm_wp), DIMENSION(SIZE(vapX)) :: pX,sig,qsat,seq,up,down,ctot,newvap,nucr,grate,cm0,cm3,drad
254
255    ! get a copy of drop radius.
256    drad(:) = mm_drad(:)
257
258    ! Initialization :
259    ! Copy input argument and convert units X.m-3 -> X.kg-1
260    ! sXXX is the initial converted value saved
261    sm3iX = m3iX/mm_rhoair
262    sm0a = mm_m0aer_f/mm_rhoair ; sm3a = mm_m3aer_f/mm_rhoair
263    sm0n = mm_m0ccn/mm_rhoair ; sm3n = mm_m3ccn/mm_rhoair
264    ! zXXX is our working copy
265    zm3iX = sm3iX ; zm0a = sm0a ; zm3a = sm3a ; zm0n = sm0n ; zm3n = sm3n
266
267    ! Molar fraction of X specie is set in mass mixing ratio [kg.kg-1]
268    zvapX  = vapX  * xESP%fmol2fmas
269    ! Surface tension [N.m-1]
270    sig = mm_sigX(mm_temp,xESP)
271    ! X specie mass mixing ratio at saturation [kg.kg-1]
272    qsat = mm_ysatX(mm_temp,mm_play,xESP) * xESP%fmol2fmas
273    ! partial pressure of X specie
274    pX = vapX * mm_play
275    ! Saturation ratio
276    Xsat = zvapX / qsat
277
278   
279    ! Gets nucleation rate (ccn radius is the monomer !)
280    call nuc_rate((/(mm_rm, i=1,mm_nla)/),mm_temp,xESP,pX,Xsat,nucr)
281    ! IMPORTANT: update CCN and aerosols moment from nucleation NOW !
282    ! Doing so should prevent a nasty bug that occurs if we want to generate clouds from scratch (i.e. a "dry" atmosphere without any clouds tracers already present).
283    !
284    ! In such case, we do not produce ice variation on the first call of the method, at most only CCN are produced (i.e. dm3i == 0, dm3n != 0)
285    ! But the rules for computing the global tendencies in mm_cloud_nucond state that the global variation for CCN is due to the most active specie exchange.
286    !
287    ! for nucleation we have the following equations:
288    !   dMa(k)/dt = - dMn(k)/dt (conservation of aerosols+ccn)           (1)
289    !   dMa(k)/dt = - 4*PI*nucr/rm * Ma(k+3)                             (2)
290    !             = - 4*PI*nucr/rm * alpha(k+3)/alpha(k) * rc**3 * Ma(k)
291    ! With:
292    !   - Ma(k): k-th order moment of aerosol
293    !   - Mn(k): k-th order moment of ccn
294    !   - nucr : the nucleation rate.
295    ! We solve (implicit scheme) :
296    !   CST_M(k) = 4*PI*nucr/rm * alpha(k+3)/alpha(k)*rc**3 * dt
297    !   Ma(k)[t+dt] = 1/(1+CST_M(k)) * Ma(k)[t]                          (3)
298    ! Then, from eq. 2:
299    ! Mn(k)[t+dt] = Mn(k)[t] + CST_M(k)/(1+CST_M(k))*Ma(k)[t]            (4)
300   
301    ! Copies the nucleation rate into an output variable
302    Xnrate = nucr
303   
304    cm0 = 4._mm_wp*mm_pi*nucr/mm_rm*mm_alpha_f(3._mm_wp)*mm_rcf**3*mm_dt
305    cm3 = 4._mm_wp*mm_pi*nucr/mm_rm*mm_alpha_f(6._mm_wp)/mm_alpha_f(3._mm_wp)*mm_rcf**3*mm_dt
306    zm0a = 1._mm_wp/(1._mm_wp+cm0) * zm0a
307    zm3a = 1._mm_wp/(1._mm_wp+cm3) * zm3a
308    WHERE (zm0a <= 0._mm_wp .OR. zm3a <= 0._mm_wp)
309      zm0a = 0._mm_wp
310      zm3a = 0._mm_wp
311      zm0n = zm0n + sm0a
312      zm3n = zm3n + sm3a
313    ELSEWHERE
314      zm0n = zm0n + cm0*zm0a
315      zm3n = zm3n + cm3*zm3a
316    ENDWHERE
317
318    ! update the drop radius (we probably should recompute totally the radius to be in better agreement with the other moments)
319    ! We must manage the case where there is no ices and no ccn ==> drop radius is ZERO,
320    ! but conditions are met to spawn nucleation process: creation of ccn.
321    ! Then we set the drop radius to the monomer radius.
322    !
323    ! Doing so will prevent a nasty bug to occur later when ice volume is updated !
324    WHERE (nucr > 0._mm_wp .AND. drad <= mm_drad_min)
325      drad = mm_rm
326    ENDWHERE
327
328    ! Equilibrium saturation near the drop
329    seq = exp(min(30._mm_wp,2._mm_wp*sig*xESP%masmol/(xESP%rho*mm_rgas*mm_temp*drad)))
330    ! Gets growth rate
331    call growth_rate(mm_temp,mm_play,pX/Xsat,xESP,seq,drad,grate)
332    ctot = zvapx + xESP%rho * zm3iX
333    up = zvapx + mm_dt * grate * 4._mm_wp * mm_pi * xESP%rho * drad * seq * zm0n
334    down = 1._mm_wp + mm_dt * grate * 4._mm_wp * mm_pi * xESP%rho * drad / qsat * zm0n
335    ! gets new vapor X specie mass mixing ratio : cannot be greater than the
336    ! total gas + ice and lower than nothing :)
337    newvap = max(min(up/down,ctot),0._mm_wp)
338    ! gets "true" growth rate
339    grate = grate * (newvap/qsat - seq)
340    ! Copies the growth rate into an output variable
341    Xgrate = grate
342
343    ! computes tendencies through condensation
344    DO i=1,mm_nla
345      ! check for the specific case : NO ICE and SUBLIMATION
346      IF (zm3iX(i) <= 0._mm_wp .AND. grate(i) <= 0._mm_wp) THEN
347        zm3iX(i) = 0._mm_wp
348      ELSE
349        ! update ice volume ...
350        zm3iX(i) = zm3iX(i) + mm_dt*grate(i)*4._mm_wp*mm_pi*drad(i)*zm0n(i)
351        ! ... and check if there is ice left in the ccn
352        IF (zm3iX(i) <= 0._mm_wp) THEN
353          zm3iX(i) = 0._mm_wp
354          zm0a(i) = zm0a(i) + zm0n(i) ; zm0n(i) = 0._mm_wp
355          zm3a(i) = zm3a(i) + zm3n(i) ; zm3n(i) = 0._mm_wp
356        ENDIF
357      ENDIF
358    ENDDO
359
360    ! Computes balance
361    IF (mm_debug) THEN
362      DO i=1,mm_nla
363        bef = sm0a(i) + sm0n(i)
364        aft = zm0a(i)  + zm0n(i)
365        IF (ABS(bef-aft)/bef > 1e-10_mm_wp) THEN
366          WRITE(*,'((a),I2.2,(a),ES20.12,(a),ES20.12)') &
367            "[DEBUG] nc_esp("//TRIM(xESP%name)//"): M0 not conserved (z=",i,")",bef," <-> ",aft
368        ENDIF
369        bef = sm3a(i) + sm3n(i)
370        aft = zm3a(i)  + zm3n(i)
371        IF (ABS(bef-aft)/bef > 1e-10_mm_wp) THEN
372          WRITE(*,'((a),I2.2,(a),ES20.12,(a),ES20.12)') &
373            "[DEBUG] nc_esp("//TRIM(xESP%name)//"): M3 not conserved (z=",i,")",bef," <-> ",aft
374        ENDIF
375      ENDDO
376    ENDIF
377
378    ! compute tendencies:
379    ! all of these tendencies are in X.m-3 !
380    dm0aer = (zm0a - sm0a)*mm_rhoair
381    dm3aer = (zm3a - sm3a)*mm_rhoair
382    dm0ccn = (zm0n - sm0n)*mm_rhoair
383    dm3ccn = (zm3n - sm3n)*mm_rhoair
384
385    dm3iX  = (zm3iX - sm3iX)                    ! this one in X.kg-1 (temporary) !
386    dvapX  = -xESP%rho * dm3iX / xESP%fmol2fmas ! in order to compute this one in mol.mol-1
387    dm3iX  = dm3iX*mm_rhoair                    ! update ice tendencies in X.m-3 !
388
389  END SUBROUTINE nc_esp
390
391  SUBROUTINE nuc_rate(rccn,temp,xESP,pvp,sat,rate)
392    !! Get nucleation rate.
393    !!
394    !! The method computes the heterogeneous nucleation rate for the given specie on a fractal particle
395    !! of size __rccn__.
396    !! Except __xESP__, all arguments are vectors of the same size (vertical grid).
397    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: rccn !! Radius of the cloud condensation nuclei (m).
398    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: temp !! Temperature (K).
399    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: pvp  !! Partial vapor pressure of X specie (Pa).
400    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: sat  !! Saturation ratio of given specie (--).
401    TYPE(mm_esp), INTENT(in)                    :: xESP !! X specie properties (--).
402    REAL(kind=mm_wp), INTENT(out), DIMENSION(:) :: rate !! The nucleation rate (\(m^{-2}.s^{-1}\)).
403    INTEGER          :: i
404    REAL(kind=mm_wp) :: r,t,s,sig,nX,rstar,gstar,x,zeldov,deltaf,fsh,fstar
405
406    rate(:) = 0._mm_wp
407    ! Activation condition
408    DO i=1, SIZE(rccn)
409      s = sat(i)
410      IF (s > 1._mm_wp) THEN
411        t = temp(i) ; r = rccn(i)
412        sig = mm_sigX(t,xESP)
413        nX    = pvp(i)/mm_kboltz/t
414        rstar = 2._mm_wp*sig*xESP%vol/(mm_kboltz*t*dlog(s))
415        ! curvature radius
416        x     = r/rstar
417        fsh   = mm_fshape(xESP%mteta,x)
418        fstar = (4._mm_wp*mm_pi/3._mm_wp)*sig*(rstar**2.)*fsh
419        deltaf=MIN(MAX((2.*mm_fdes-mm_fdif-fstar)/(mm_kboltz*t),-100._mm_wp),100._mm_wp)
420        IF (deltaf > -100._mm_wp) THEN
421          gstar  = 4._mm_wp*mm_pi*(rstar**3)/(3._mm_wp*xESP%vol)
422          zeldov = dsqrt(fstar/(3._mm_wp*mm_pi*mm_kboltz*t*(gstar**2)))
423          rate(i)= zeldov*mm_kboltz*t*(nX*rstar)**2._mm_wp*dexp(deltaf)/(fsh*mm_nus*xESP%mas)
424        ENDIF
425      ENDIF
426    ENDDO
427
428    RETURN
429  END SUBROUTINE nuc_rate
430
431  SUBROUTINE growth_rate(temp,pres,pXsat,xESP,seq,drad,rate)
432    !! Get growth rate through condensation/evaporation process.
433    !!
434    !! The method computes the growth rate a drop through condensation/evaporation processes:
435    !!
436    !! $$ r \times \frac{dr}{dt} = g_{rate} \times (S - S_{eq}) $$
437    !!
438    !! Except __xESP__ which is a scalar, all arguments are vectors of the same size (vertical grid).
439    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: temp  !! Temperature (K).
440    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: pres  !! Pressure level (Pa).
441    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: pXsat !! Saturation vapor pressure of specie (Pa).
442    TYPE(mm_esp), INTENT(in)                    :: xESP  !! Specie properties.
443    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: seq   !! Equilibrium saturation near the drop.
444    REAL(kind=mm_wp), INTENT(in), DIMENSION(:)  :: drad  !! Drop radius (m).
445    REAL(kind=mm_wp), INTENT(out), DIMENSION(:) :: rate  !! Growth rate (\(m^{2}.s^{-1}\)).
446    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: k,knu,slf,rkc,rdc,l,dv
447    INTEGER                                     :: n
448    n = SIZE(temp)
449    ALLOCATE(k(n),knu(n),slf(n),rkc(n),rdc(n),l(n),dv(n))
450
451    ! N2 (air) Thermal conductivity (where does it come from ?)
452    k(:) = ( 2.857e-2_mm_wp*temp-0.5428_mm_wp)*4.184e-3_mm_wp
453    ! Gas mean free path
454    l(:) = mm_lambda_g(temp,pres)
455    ! Diffusion coefficient of X gas
456    Dv(:) = 1._mm_wp/3._mm_wp*dsqrt(8._mm_wp*mm_rgas*temp(:)/(mm_pi*xESP%masmol))*mm_kboltz*temp(:) / &
457      (mm_pi*pres(:)*(mm_air_rad+xESP%ray)**2* dsqrt(1._mm_wp+xESP%fmol2fmas))
458    knu(:) = l(:)/drad(:)                                         ! The knudsen number of the drop
459    slf(:) = (1.333_mm_wp+0.71_mm_wp/knu(:))/(1._mm_wp+1._mm_wp/knu(:)) ! Slip flow correction
460    Dv(:)  = Dv(:)/(1._mm_wp+slf(:)*knu(:))
461    ! latent heat resistance coefficient
462    rkc(:) = mm_lheatX(temp(:),xESP)**2 * xESP%rho * xESP%masmol / (k(:)*mm_rgas*temp(:)**2)
463    ! Diffusion resistance coefficient
464    rdc(:) = mm_rgas * temp(:) * xESP%rho / (Dv(:)*pXsat(:)*xESP%masmol)
465    ! Growth rate: rdr/dt = rate * (S-Seq) ; rate is returned
466    rate(:) = 1._mm_wp / (seq(:)*rkc(:)+rdc(:))
467    RETURN
468  END SUBROUTINE growth_rate
469
470
471  !-----------------------------------------------------------------------------
472  ! SEDIMENTATION PROCESS RELATED METHODS
473  !-----------------------------------------------------------------------------
474
475  SUBROUTINE mm_cloud_sedimentation(dm0n,dm3n,dm3i)
476    !! Compute the tendency of _clouds_ related moments through sedimentation process.
477    !!
478    !! The method computes the tendencies of moments related to cloud microphysics through
479    !! sedimentation process. The algorithm used here differs from
480    !! [[mm_haze(module):mm_haze_sedimentation(subroutine)]] as all moments settle with the same
481    !! terminal velocity which is computed with the average drop radius of the size distribution.
482    !! We simply compute an _exchange matrix_ that stores the new positions of each cells through
483    !! sedimentation process and then computes the matrix
484    !! product with input moments values to get final tendencies.
485    REAL(kind=mm_wp), INTENT(out), DIMENSION(:)   :: dm0n
486    !! Tendency of the 0th order moment of the ccn distribution (\(m^{-3}\)).
487    REAL(kind=mm_wp), INTENT(out), DIMENSION(:)   :: dm3n
488    !! Tendency of the 3rd order moment of the ccn distribution (\(m^{3}.m^{-3}\)).
489    REAL(kind=mm_wp), INTENT(out), DIMENSION(:,:) :: dm3i
490    !! Tendencies of the 3rd order moment of each ice component of the cloud (\(m^{3}m^{-3}\)).
491    INTEGER                                   :: im,nm
492    REAL(kind=mm_wp), DIMENSION(:,:), ALLOCATABLE :: moms, momsf,chg_matrix
493
494    nm = 2 + mm_nesp
495    ALLOCATE(moms(mm_nla,nm),momsf(mm_nla,nm),chg_matrix(mm_nla,mm_nla))
496    ! Initializes moms
497    moms(:,1)  = mm_m0ccn * mm_dzlev
498    moms(:,2)  = mm_m3ccn * mm_dzlev
499    DO im=1,mm_nesp ; moms(:,2+im) = mm_m3ice(:,im) * mm_dzlev ; ENDDO
500    ! Computes exchange matrix
501    CALL exchange(mm_drad,mm_drho,mm_dt,chg_matrix)
502    ! Computes final moments values
503    DO im=1,nm ; momsf(:,im) = MATMUL(chg_matrix,moms(:,im)) ; ENDDO
504    ! Computes tendencies (converted in X.m-3)
505    dm0n = (momsf(:,1)-moms(:,1))/mm_dzlev
506    dm3n = (momsf(:,2)-moms(:,2))/mm_dzlev
507    DO im=1,mm_nesp ; dm3i(:,im) = (momsf(:,2+im)-moms(:,2+im))/mm_dzlev ; ENDDO
508    RETURN
509  END SUBROUTINE mm_cloud_sedimentation
510
511  SUBROUTINE exchange(rad,rhog,dt,matrix)
512    !! Compute the exchange matrix.
513    !!
514    !! The subroutine computes the matrix exchange used by
515    !! [[mm_clouds(module):mm_cloud_sedimentation(subroutine)]] to compute moments tendencies
516    !! through sedimentation process. Both __rad__ and __rhog__ must be vector with relevant
517    !! values over the atmospheric vertical structure. __matrix__ is square 2D-array with same
518    !! dimension size than __rad__.
519    REAL(kind=mm_wp), INTENT(in), DIMENSION(:) :: rad
520    !! Cloud drop radius over the atmospheric vertical structure (m).
521    REAL(kind=mm_wp), INTENT(in), DIMENSION(:) :: rhog
522    !! Cloud drop density over the atmospheric vertical structure (\(kg.m^{-3}\)).
523    REAL(kind=mm_wp), INTENT(in)               :: dt
524    !! Timestep (s).
525    REAL(kind=mm_wp), INTENT(out)              :: matrix(:,:)
526    !! The output _exchange matrix_.
527    INTEGER                                     :: nz,i,j,jj,jinf,jsup
528    REAL(kind=mm_wp)                            :: zni,znip1,xf,xft,xcnt
529    REAL(kind=mm_wp), DIMENSION(:), ALLOCATABLE :: puit
530    REAL(kind=mm_wp)                            :: cpte,cpte2
531    REAL(kind=mm_wp)                            :: zsurf
532    INTEGER, PARAMETER                          :: ichx  = 1
533    matrix = 0._mm_wp ; nz = SIZE(rad) ; ALLOCATE(puit(nz))
534    zsurf = mm_zlev(nz+1)
535   
536    ! compute exchange matrix
537    DO i=1,nz
538      puit(i) = 0._mm_wp
539      xcnt = 0._mm_wp
540      ! computes drop move (i.e. its new positions)
541      CALL getnzs(ichx,i,rad,rhog,dt,zni,znip1)
542
543      ! Peculiar case : Ground level precipitation [znip1 < zsurf && (zni < zsurf || zni > zsurf)]
544      ! - complete precipitation [ znip1 <= 0 && zni <= 0 ] :
545      IF(zni <= zsurf .and. znip1 <= zsurf) THEN
546        xft=0._mm_wp
547        xf=1._mm_wp
548        xcnt=xcnt+xf
549        puit(i)=puit(i)+xf
550      ENDIF
551      ! - partial precipitation [ znip1 <= zsurf && zni > zsurf ] :
552      IF (zni > zsurf .and. znip1 <= zsurf) THEN
553        xft=(zni-zsurf)/(zni-znip1)
554        xf=(1.-xft)
555        xcnt=xcnt+xf
556        puit(i)=puit(i)+xf
557      ENDIF
558      ! General case : no ground precipitation [ znip1 > zsurf && zni > zsurf ]
559      IF (zni > zsurf .and. znip1 > zsurf) THEN
560        xft = 1._mm_wp ! on a la totalite de la case
561        xf  = 0._mm_wp
562        xcnt=xcnt+xf
563        puit(i)=puit(i)+xf
564      ENDIF
565      IF (zni < znip1) THEN
566        WRITE(*,'("[EXCHANGES] WARNING, missing this case :",2(2X,ES10.3))') zni, znip1
567      ENDIF
568     
569      ! Fix minimum level to the ground
570      znip1 = MAX(znip1,zsurf)
571      zni   = MAX(zni,zsurf)
572      ! Locate new "drop" position in the verical grid
573      jsup=nz+1
574      jinf=nz+1
575      DO j=1,nz
576        IF (zni<=mm_zlev(j).and.zni>=mm_zlev(j+1)) jsup=j
577        IF (znip1<=mm_zlev(j).and.znip1>=mm_zlev(j+1)) jinf=j
578      ENDDO
579      ! Volume is out of range: (all drops have touched the ground!)
580      ! Note: cannot happen here, it has been treated previously :)
581      IF (jsup>=nz+1.and.jinf==jsup) THEN
582        WRITE(*,'(a)') "[EXCHANGE] speaking: The impossible happened !"
583        call EXIT(666)
584      ENDIF
585      ! Volume inside a single level
586      IF (jsup==jinf.and.jsup<=nz) THEN
587        xf=1._mm_wp
588        xcnt=xcnt+xft*xf
589        matrix(jinf,i)=matrix(jinf,i)+xft*xf
590      ENDIF
591
592      ! Volume over 2 levels
593      IF (jinf==jsup+1) THEN
594        xf=(zni-mm_zlev(jinf))/(zni-znip1)
595        xcnt=xcnt+xf*xft
596        IF(jsup<=nz) THEN
597          matrix(jsup,i)=matrix(jsup,i)+xft*xf
598        ENDIF
599        xf=(mm_zlev(jinf)-znip1)/(zni-znip1)
600        xcnt=xcnt+xf*xft
601        IF (jinf<=nz) THEN
602          matrix(jinf,i)=matrix(jinf,i)+xft*xf
603        ENDIF
604      ENDIF
605
606      ! Volume over 3 or more levels
607      IF (jinf > jsup+1) THEN
608        ! first cell
609        xf=(zni-mm_zlev(jsup+1))/(zni-znip1)
610        xcnt=xcnt+xf*xft
611        matrix(jsup,i)=matrix(jsup,i)+xft*xf
612        ! last cell
613        xf=(mm_zlev(jinf)-znip1)/(zni-znip1)
614        xcnt=xcnt+xf*xft
615        matrix(jinf,i)=matrix(jinf,i)+xft*xf
616        ! other :)
617        DO jj=jsup+1,jinf-1
618          xf=(mm_zlev(jj)-mm_zlev(jj+1))/(zni-znip1)
619          xcnt=xcnt+xf*xft
620          matrix(jj,i)=matrix(jj,i)+xft*xf
621        ENDDO
622      ENDIF
623    ENDDO
624
625    ! checking if everything is alright if debug enabled...
626    IF (mm_debug) THEN
627      cpte=0._mm_wp ; cpte2=0._mm_wp
628      DO j=1,nz
629        DO jj=1,nz
630          cpte=cpte+matrix(jj,j)
631        ENDDO
632      ENDDO
633      cpte2=cpte+sum(puit)
634      IF (abs(cpte2-nz)>1.e-4_mm_wp) THEN
635        WRITE(*,'("[EXCHANGE] speaking: tx expl (/nz):",2(2X,ES10.3))') cpte,cpte2
636      ENDIF
637    ENDIF
638
639    RETURN
640  END SUBROUTINE exchange
641
642  SUBROUTINE getnzs(ichx,idx,rad,rho,dt,zni,zns)
643    !! Compute displacement of a cell under sedimentation process.
644    !!
645    !! The method computes the new position of a _drop cell_ through sedimentation process as
646    !! descibed in the following scheme:
647    !!
648    !! ![Cloud sedimentation scheme](|media|/cloud_sed_scheme.svg)
649    !!
650    !! New positions are returned in __zni__ and __zns__ ouptut arguments.
651    !!
652    !! @note
653    !! The method uses directly [[mm_globals(module):mm_play(variable)]], [[mm_globals(module):mm_plev(variable)]],
654    !! [[mm_globals(module):mm_temp(variable)]],[[mm_globals(module):mm_btemp(variable)]],
655    !! [[mm_globals(module):mm_zlay(variable)]] and [[mm_globals(module):mm_zlev(variable)]] and uses __idx__ to
656    !! get the relevant value to use on the vertical grid.
657    INTEGER, INTENT(in)                        :: ichx
658    !! Velocity extrapolation control flag (0 for linear, 1 for exponential -preferred -).
659    INTEGER, INTENT(in)                        :: idx
660    !! Initial position of the drop (subscript of vertical layers vectors).
661    REAL(kind=mm_wp), INTENT(in), DIMENSION(:) :: rad
662    !! Cloud drop radius over the atmospheric vertical structure (m).
663    REAL(kind=mm_wp), INTENT(in), DIMENSION(:) :: rho
664    !! Cloud drop density over the atmospheric vertical structure (\(kg.m^{-3}\)).
665    REAL(kind=mm_wp), INTENT(in)               :: dt
666    !! Timestep (s).
667    REAL(kind=mm_wp), INTENT(out)              :: zni
668    !! Final layer upper boundary position (m).
669    REAL(kind=mm_wp), INTENT(out)              :: zns
670    !! Final layer lower boundary position (m).
671    REAL(kind=mm_wp)            :: ws,wi,w,zi,zs
672    REAL(kind=mm_wp)            :: alpha,argexp,v0,arg1,arg2
673    INTEGER                 :: i,nz
674    REAL(kind=mm_wp), PARAMETER :: es = 30._mm_wp
675
676    nz = SIZE(rad)
677    ! Linear extrapolation of velocity
678    IF (ichx==0) THEN
679      ! velocity upper interface
680      ws = wsettle(mm_plev(idx),mm_btemp(idx),mm_zlev(idx),rho(idx),rad(idx))
681      IF (idx==nz) THEN
682        ! veloctity center layer
683        wi = wsettle(mm_play(idx),mm_temp(idx),mm_zlay(idx),rho(idx),rad(idx))
684      ELSEIF(idx<nz) THEN
685        ! velocity lower interface
686        wi = wsettle(mm_plev(idx+1),mm_btemp(idx+1),mm_zlev(idx+1), &
687          rho(idx+1),rad(idx+1))
688      ELSE
689        WRITE(*,'(a)') "[getnzs] speaking:"
690        WRITE(*,'(a)') "This is the fatal error..."
691        WRITE(*,'(a)') "index is higher than number of levels"
692        call EXIT(111)
693      ENDIF
694      w   = (ws+wi)/2._mm_wp
695      zni = mm_zlev(idx)-w*dt
696      zns = mm_zlev(idx)-mm_dzlev(idx)-w*dt
697      RETURN
698      ! Exponential extrapolation of velocity
699
700    ELSEIF(ichx==1) THEN
701      zs = mm_zlev(idx)
702      ws = wsettle(mm_plev(idx),mm_btemp(idx),zs,rho(idx),rad(idx))
703      zi=mm_zlay(idx)
704      wi = wsettle(mm_play(idx),mm_temp(idx),zi,rho(idx),rad(idx))
705      ! ws & wi must be different !
706      IF(dabs(wi-ws)/wi <= 1.e-3_mm_wp)  wi=ws/1.001_mm_wp
707      IF (wi /= 0._mm_wp) alpha = dlog(ws/wi)/(zs-zi)  ! alpha < 0 if wi > ws
708      !   -es < argexp < es
709      argexp=MAX(MIN(alpha*zs,es),-es)
710      v0 = ws/dexp(argexp)
711      arg1=1._mm_wp+v0*alpha*dexp(argexp)*dt
712      argexp=MAX(MIN(alpha*(mm_zlev(idx)-mm_dzlev(idx)),es),-es)
713      arg2=1._mm_wp+v0*alpha*dexp(argexp)*dt
714      IF (arg1<=0._mm_wp.OR.arg2<=0._mm_wp) THEN
715        ! correct velocity
716        ! divides the velocity argument in arg1 and arg2 :
717        ! argX=1+alpha*v0*exp(alpha*z)*dt <==> argX-1=alpha*v0*exp(alpha*z)*dt
718        ! ==> argX' = 1 + (argX-1)/2  <==> argX' = (1+argX)/2.
719        DO i=1,25
720          IF (arg1<=0._mm_wp.OR.arg2<=0._mm_wp) THEN
721            IF (mm_debug) &
722              WRITE(*,'((a),I2.2,(a))') "[getnzs] must adjust velocity (iter:",i,"/25)"
723            arg1=(arg1+1._mm_wp)/2._mm_wp ; arg2=(arg2+1._mm_wp)/2._mm_wp
724          ELSE
725            EXIT
726          ENDIF
727        ENDDO
728        ! sh** we have to stop
729        IF (i>25) THEN
730          WRITE(*,'(a)')"[getnzs] speaking:"
731          WRITE(*,'(a)') "Cannot adjust velocities"
732          call EXIT(111)
733        ENDIF
734      ENDIF
735
736      zni = mm_zlev(idx)-dlog(arg1)/alpha
737      zns = mm_zlev(idx)-mm_dzlev(idx)-dlog(arg2)/alpha
738
739      RETURN
740    ENDIF
741  END SUBROUTINE getnzs
742
743  ELEMENTAL FUNCTION wsettle(p,t,z,rho,rad) RESULT(w)
744    !! Compute the settling velocity of a spherical particle.
745    !!
746    !! The method computes the effective settling velocity of spherical particle of
747    !! radius __rad__. It accounts for the slip-flow transition (no approximation).
748    REAL(kind=mm_wp), INTENT(in) :: p   !! The pressure level (Pa).
749    REAL(kind=mm_wp), INTENT(in) :: t   !! The temperature (K).
750    REAL(kind=mm_wp), INTENT(in) :: z   !! The altitude level (m).
751    REAL(kind=mm_wp), INTENT(in) :: rho !! Density of the particle (\(kg.m^{-3}\)).
752    REAL(kind=mm_wp), INTENT(in) :: rad !! Radius of the particle (m).
753    REAL(kind=mm_wp) :: w               !! Settling velocity (\(m.s^{-1}\)).
754    REAL(kind=mm_wp) :: Us, Fc, kn, wtmp, wmax
755    REAL(kind=mm_wp), PARAMETER :: ra = 1.75e-10_mm_wp
756   
757    ! Knudsen number
758    kn = (mm_kboltz * t) / (p * 4._mm_wp * sqrt(2._mm_wp) * mm_pi * ra**2) / rad
759
760    ! Computes slip-flow correction : Fc = 1 + (mm_akn * mm_lambda_g(t,p) / rad)
761    Fc = (1._mm_wp + 1.2517_mm_wp*kn + 0.4_mm_wp*kn*dexp(-1.1_mm_wp/kn))
762   
763    ! Computes Stokes settling velocity
764    Us = (2._mm_wp * rad**2 * rho * mm_effg(z)) / (9._mm_wp * mm_eta_g(t))
765   
766    ! Computes settling velocity (correction factor : x3.0)
767    w = Us * Fc * 3._mm_wp
768
769    ! Imposes a velocity limit
770    wmax = 20._mm_wp ! 20 m/s [Lorenz 1993]
771    wtmp = (1._mm_wp / w) + (1._mm_wp / wmax)
772    w = 1._mm_wp / wtmp
773  END FUNCTION wsettle
774
775  FUNCTION get_mass_flux(rho,m3) RESULT(flx)
776    !> Get the mass flux of (clouds related) moment through sedimention.
777    !!
778    !! @warning
779    !! The method is __only__ valid for cloud moments (i.e. ice or ccn). It calls
780    !! [[mm_clouds(module):wsettle(function)]] that compute the _mean_ settling velocity of a
781    !! cloud drop.
782    !!
783    !! @note
784    !! The computed flux is always positive.
785    REAL(kind=mm_wp), INTENT(in)               :: rho
786    !! Tracer density (\(kg.m^{-3}\)).
787    REAL(kind=mm_wp), INTENT(in), DIMENSION(:) :: m3
788    !! Vertical profile of the total volume of tracer (i.e. M3) from __TOP__ to __GROUND__ (\(m^{3}.m^{-3}\)).
789    REAL(kind=mm_wp), DIMENSION(SIZE(m3))      :: flx
790    !! Mass sedimentation fluxes at each layer from __TOP__ to __GROUND__ (\(kg.m^{-2}.s^{-1}\)).
791    REAL(kind=mm_wp), SAVE :: pifac = (4._mm_wp * mm_pi) / 3._mm_wp
792    flx = rho * pifac * m3 * wsettle(mm_play,mm_temp,mm_zlay,mm_drho,mm_drad)
793    RETURN
794  END FUNCTION get_mass_flux
795
796END MODULE MM_CLOUDS
Note: See TracBrowser for help on using the repository browser.