MODULE lmdz_lscp_tools IMPLICIT NONE CONTAINS !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SUBROUTINE FALLICE_VELOCITY(klon, iwc, temp, rho, pres, ptconv, velo) !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! routine that calculates the ice crystals fall velocity ! References: ! - Heymsfield 1977, doi: 10.1175/1520-0469(1977)034<0367:PDISIC>2.0.CO;2 ! - Heymsfield and Donner 1990, doi: 10.1175/1520-0469(1990)047<1865:ASFPIC>2.0.CO;2 ! - Stubenrauch et al. 2019, doi: 10.1029/2019MS001642 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ use lmdz_lscp_ini, only: iflag_vice, ffallv_con, ffallv_lsc use lmdz_lscp_ini, only: cice_velo, dice_velo IMPLICIT NONE INTEGER, INTENT(IN) :: klon REAL, INTENT(IN), DIMENSION(klon) :: iwc !-- specific ice water content [kg/m3] REAL, INTENT(IN), DIMENSION(klon) :: temp !-- temperature [K] REAL, INTENT(IN), DIMENSION(klon) :: rho !-- dry air density [kg/m3] REAL, INTENT(IN), DIMENSION(klon) :: pres !-- air pressure [Pa] LOGICAL, INTENT(IN), DIMENSION(klon) :: ptconv !-- convective point [-] REAL, INTENT(OUT), DIMENSION(klon) :: velo !-- fallspeed velocity of crystals [m/s] INTEGER i REAL logvm, iwcg, tempc, phpa, fallv_tun REAL m2ice, m2snow, vmice, vmsnow REAL aice, bice, asnow, bsnow DO i = 1, klon IF (ptconv(i)) THEN fallv_tun = ffallv_con ELSE fallv_tun = ffallv_lsc END IF tempc = temp(i) - 273.15 ! celcius temp iwcg = MAX(iwc(i)*1000., 1E-3) ! iwc in g/m3. We set a minimum value to prevent from division by 0 phpa = pres(i)/100. ! pressure in hPa IF (iflag_vice .EQ. 1) THEN ! so-called 'empirical parameterization' in Stubenrauch et al. 2019 if (tempc .GE. -60.0) then logvm = -0.0000414122*tempc*tempc*log(iwcg) - 0.00538922*tempc*log(iwcg) & - 0.0516344*log(iwcg) + 0.00216078*tempc + 1.9714 velo(i) = exp(logvm) else velo(i) = 65.0*(iwcg**0.2)*(150./phpa)**0.15 end if velo(i) = fallv_tun*velo(i)/100.0 ! from cm/s to m/s ELSE IF (iflag_vice .EQ. 2) THEN ! so called PSDM empirical coherent bulk ice scheme in Stubenrauch et al. 2019 aice = 0.587 bice = 2.45 asnow = 0.0444 bsnow = 2.1 m2ice = ((iwcg*0.001/aice)/(exp(13.6 - bice*7.76 + 0.479*bice**2)* & exp((-0.0361 + bice*0.0151 + 0.00149*bice**2)*tempc))) & **(1./(0.807 + bice*0.00581 + 0.0457*bice**2)) vmice = 100.*1042.4*exp(13.6 - (bice + 1)*7.76 + 0.479*(bice + 1.)**2)*exp((-0.0361 + & (bice + 1.)*0.0151 + 0.00149*(bice + 1.)**2)*tempc) & *(m2ice**(0.807 + (bice + 1.)*0.00581 + 0.0457*(bice + 1.)**2))/(iwcg*0.001/aice) vmice = vmice*((1000./phpa)**0.2) m2snow = ((iwcg*0.001/asnow)/(exp(13.6 - bsnow*7.76 + 0.479*bsnow**2)* & exp((-0.0361 + bsnow*0.0151 + 0.00149*bsnow**2)*tempc))) & **(1./(0.807 + bsnow*0.00581 + 0.0457*bsnow**2)) vmsnow = 100.*14.3*exp(13.6 - (bsnow + .416)*7.76 + 0.479*(bsnow + .416)**2) & *exp((-0.0361 + (bsnow + .416)*0.0151 + 0.00149*(bsnow + .416)**2)*tempc) & *(m2snow**(0.807 + (bsnow + .416)*0.00581 + 0.0457*(bsnow + .416)**2))/(iwcg*0.001/asnow) vmsnow = vmsnow*((1000./phpa)**0.35) velo(i) = fallv_tun*min(vmsnow, vmice)/100. ! to m/s ELSE ! By default, fallspeed velocity of ice crystals according to Heymsfield & Donner 1990 velo(i) = fallv_tun*cice_velo*((iwcg/1000.)**dice_velo) END IF END DO END SUBROUTINE FALLICE_VELOCITY !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SUBROUTINE CALC_QSAT_ECMWF(klon, temp, qtot, pressure, tref, phase, flagth, qs, dqs) !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! Calculate qsat following ECMWF method !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ USE yoethf_mod_h USE yomcst_mod_h IMPLICIT NONE include "FCTTRE.h" INTEGER, INTENT(IN) :: klon !-- number of horizontal grid points REAL, INTENT(IN), DIMENSION(klon) :: temp !-- temperature [K] REAL, INTENT(IN), DIMENSION(klon) :: qtot !-- total specific water [kg/kg] REAL, INTENT(IN), DIMENSION(klon) :: pressure !-- pressure [Pa] REAL, INTENT(IN) :: tref ! reference temperature [K] LOGICAL, INTENT(IN) :: flagth ! flag for qsat calculation for thermals INTEGER, INTENT(IN) :: phase ! phase: 0=depend on temperature sign (temp>tref -> liquid, tempgammasat*qsat !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ use lmdz_lscp_ini, only: iflag_gammasat, temp_nowater, RTT use lmdz_lscp_ini, only: a_homofreez, b_homofreez, delta_hetfreez IMPLICIT NONE INTEGER, INTENT(IN) :: klon !-- number of horizontal grid points REAL, INTENT(IN), DIMENSION(klon) :: temp !-- temperature [K] REAL, INTENT(IN), DIMENSION(klon) :: qtot !-- total specific water [kg/kg] REAL, INTENT(IN), DIMENSION(klon) :: pressure !-- pressure [Pa] REAL, INTENT(OUT), DIMENSION(klon) :: gammasat !-- coefficient to multiply qsat with to calculate saturation REAL, INTENT(OUT), DIMENSION(klon) :: dgammasatdt !-- derivative of gammasat wrt temperature [K-1] REAL, DIMENSION(klon) :: qsi, qsl, dqsl, dqsi REAL f_homofreez, fac INTEGER i CALL CALC_QSAT_ECMWF(klon, temp, qtot, pressure, RTT, 1, .false., qsl, dqsl) CALL CALC_QSAT_ECMWF(klon, temp, qtot, pressure, RTT, 2, .false., qsi, dqsi) DO i = 1, klon IF (temp(i) .GE. RTT) THEN ! warm clouds: condensation at saturation wrt liquid gammasat(i) = 1. dgammasatdt(i) = 0. ELSE ! cold clouds: qsi > qsl ! homogeneous freezing of aerosols, according to ! Koop, 2000 and Ren and MacKenzie, 2005 (QJRMS) ! 'Cirrus regime' ! if f_homofreez > qsl / qsi, liquid nucleation ! if f_homofreez < qsl / qsi, homogeneous freezing of aerosols ! Note: f_homofreez = qsl / qsi for temp ~= -38degC f_homofreez = a_homofreez - temp(i)/b_homofreez IF (iflag_gammasat .GE. 3) THEN ! condensation at homogeneous freezing threshold for temp < -38 degC ! condensation at liquid saturation for temp > -38 degC IF (f_homofreez .LE. qsl(i)/qsi(i)) THEN gammasat(i) = f_homofreez dgammasatdt(i) = -1./b_homofreez ELSE gammasat(i) = qsl(i)/qsi(i) dgammasatdt(i) = (dqsl(i)*qsi(i) - dqsi(i)*qsl(i))/qsi(i)/qsi(i) END IF ELSEIF (iflag_gammasat .EQ. 2) THEN ! condensation at homogeneous freezing threshold for temp < -38 degC ! condensation at a threshold linearly decreasing between homogeneous ! freezing and ice saturation for -38 degC < temp < temp_nowater ! condensation at ice saturation for temp > temp_nowater ! If temp_nowater = 235.15 K, this is equivalent to iflag_gammasat = 1 IF (f_homofreez .LE. qsl(i)/qsi(i)) THEN gammasat(i) = f_homofreez dgammasatdt(i) = -1./b_homofreez ELSEIF (temp(i) .LE. temp_nowater) THEN ! Here, we assume that f_homofreez = qsl / qsi for temp = -38 degC = 235.15 K dgammasatdt(i) = (a_homofreez - 235.15/b_homofreez - 1.) & /(235.15 - temp_nowater) gammasat(i) = dgammasatdt(i)*(temp(i) - temp_nowater) + 1. ELSE gammasat(i) = 1. dgammasatdt(i) = 0. END IF ELSEIF (iflag_gammasat .EQ. 1) THEN ! condensation at homogeneous freezing threshold for temp < -38 degC ! condensation at ice saturation for temp > -38 degC IF (f_homofreez .LE. qsl(i)/qsi(i)) THEN gammasat(i) = f_homofreez dgammasatdt(i) = -1./b_homofreez ELSE gammasat(i) = 1. dgammasatdt(i) = 0. END IF ELSE ! condensation at ice saturation for temp < -38 degC ! condensation at ice saturation for temp > -38 degC gammasat(i) = 1. dgammasatdt(i) = 0. END IF ! Note that the delta_hetfreez parameter allows to linearly decrease the ! condensation threshold between the calculated threshold and the ice saturation ! for delta_hetfreez = 1, the threshold is the calculated condensation threshold ! for delta_hetfreez = 0, the threshold is the ice saturation gammasat(i) = (1.-delta_hetfreez) + delta_hetfreez*gammasat(i) dgammasatdt(i) = delta_hetfreez*dgammasatdt(i) END IF END DO END SUBROUTINE CALC_GAMMASAT !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SUBROUTINE DISTANCE_TO_CLOUD_TOP(klon, klev, k, temp, pplay, paprs, rneb, distcltop1D, temp_cltop) !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! routine that calculates the distance to cloud top for cloud phase ! determination (when cloud phase is assumed to depend on both ! temperature and distance to cloud top, see Lea Raillard's PhD !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ USE lmdz_lscp_ini, ONLY: rd, rg, tresh_cl IMPLICIT NONE INTEGER, INTENT(IN) :: klon, klev !-- number of horizontal and vertical grid points INTEGER, INTENT(IN) :: k !-- vertical index REAL, INTENT(IN), DIMENSION(klon, klev) :: temp !-- temperature [K] REAL, INTENT(IN), DIMENSION(klon, klev) :: pplay !-- pressure middle layer [Pa] REAL, INTENT(IN), DIMENSION(klon, klev + 1) :: paprs !-- pressure interfaces [Pa] REAL, INTENT(IN), DIMENSION(klon, klev) :: rneb !-- cloud fraction [-] REAL, INTENT(OUT), DIMENSION(klon) :: distcltop1D !-- distance from cloud top [m] REAL, INTENT(OUT), DIMENSION(klon) :: temp_cltop !-- temperature of cloud top [K] REAL dzlay(klon, klev) REAL zlay(klon, klev) REAL dzinterf INTEGER i, k_top, kvert LOGICAL bool_cl DO i = 1, klon ! Initialization height middle of first layer dzlay(i, 1) = Rd*temp(i, 1)/rg*log(paprs(i, 1)/paprs(i, 2)) zlay(i, 1) = dzlay(i, 1)/2 DO kvert = 2, klev IF (kvert .EQ. klev) THEN dzlay(i, kvert) = 2*(rd*temp(i, kvert)/rg*log(paprs(i, kvert)/pplay(i, kvert))) ELSE dzlay(i, kvert) = rd*temp(i, kvert)/rg*log(paprs(i, kvert)/paprs(i, kvert + 1)) END IF dzinterf = rd*temp(i, kvert)/rg*log(pplay(i, kvert - 1)/pplay(i, kvert)) zlay(i, kvert) = zlay(i, kvert - 1) + dzinterf END DO END DO DO i = 1, klon k_top = k IF (rneb(i, k) .LE. tresh_cl) THEN bool_cl = .FALSE. ELSE bool_cl = .TRUE. END IF DO WHILE ((bool_cl) .AND. (k_top .LE. klev)) ! find cloud top IF (rneb(i, k_top) .GT. tresh_cl) THEN k_top = k_top + 1 ELSE bool_cl = .FALSE. k_top = k_top - 1 END IF END DO k_top = min(k_top, klev) !dist to top is dist between current layer and layer of cloud top (from middle to middle) + dist middle to !interf for layer of cloud top distcltop1D(i) = zlay(i, k_top) - zlay(i, k) + dzlay(i, k_top)/2 temp_cltop(i) = temp(i, k_top) END DO ! klon END SUBROUTINE DISTANCE_TO_CLOUD_TOP !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ FUNCTION GAMMAINC(p, x) !*****************************************************************************80 ! !! GAMMAINC computes the regularized lower incomplete Gamma Integral ! ! Modified: ! ! 20 January 2008 ! ! Author: ! ! Original FORTRAN77 version by B Shea. ! FORTRAN90 version by John Burkardt. ! ! Reference: ! ! B Shea, ! Algorithm AS 239: ! Chi-squared and Incomplete Gamma Integral, ! Applied Statistics, ! Volume 37, Number 3, 1988, pages 466-473. ! ! Parameters: ! ! Input, real X, P, the parameters of the incomplete ! gamma ratio. 0 <= X, and 0 < P. ! ! Output, real GAMMAINC, the value of the incomplete ! Gamma integral. ! IMPLICIT NONE REAL A REAL AN REAL ARG REAL B REAL C REAL, PARAMETER :: ELIMIT = -88.0E+00 REAL GAMMAINC REAL, PARAMETER :: OFLO = 1.0E+37 REAL P REAL, PARAMETER :: PLIMIT = 1000.0E+00 REAL PN1 REAL PN2 REAL PN3 REAL PN4 REAL PN5 REAL PN6 REAL RN REAL, PARAMETER :: TOL = 1.0E-14 REAL X REAL, PARAMETER :: XBIG = 1.0E+08 GAMMAINC = 0.0E+00 IF (X == 0.0E+00) THEN GAMMAINC = 0.0E+00 RETURN END IF ! ! IF P IS LARGE, USE A NORMAL APPROXIMATION. ! IF (PLIMIT < P) THEN PN1 = 3.0E+00*SQRT(P)*((X/P)**(1.0E+00/3.0E+00) & + 1.0E+00/(9.0E+00*P) - 1.0E+00) GAMMAINC = 0.5E+00*(1.+ERF(PN1)) RETURN END IF ! ! IF X IS LARGE SET GAMMAD = 1. ! IF (XBIG < X) THEN GAMMAINC = 1.0E+00 RETURN END IF ! ! USE PEARSON'S SERIES EXPANSION. ! (NOTE THAT P IS NOT LARGE ENOUGH TO FORCE OVERFLOW IN ALOGAM). ! IF (X <= 1.0E+00 .OR. X < P) THEN ARG = P*LOG(X) - X - LOG_GAMMA(P + 1.0E+00) C = 1.0E+00 GAMMAINC = 1.0E+00 A = P DO A = A + 1.0E+00 C = C*X/A GAMMAINC = GAMMAINC + C IF (C <= TOL) THEN EXIT END IF END DO ARG = ARG + LOG(GAMMAINC) IF (ELIMIT <= ARG) THEN GAMMAINC = EXP(ARG) ELSE GAMMAINC = 0.0E+00 END IF ! ! USE A CONTINUED FRACTION EXPANSION. ! ELSE ARG = P*LOG(X) - X - LOG_GAMMA(P) A = 1.0E+00 - P B = A + X + 1.0E+00 C = 0.0E+00 PN1 = 1.0E+00 PN2 = X PN3 = X + 1.0E+00 PN4 = X*B GAMMAINC = PN3/PN4 DO A = A + 1.0E+00 B = B + 2.0E+00 C = C + 1.0E+00 AN = A*C PN5 = B*PN3 - AN*PN1 PN6 = B*PN4 - AN*PN2 IF (PN6 /= 0.0E+00) THEN RN = PN5/PN6 IF (ABS(GAMMAINC - RN) <= MIN(TOL, TOL*RN)) THEN EXIT END IF GAMMAINC = RN END IF PN1 = PN3 PN2 = PN4 PN3 = PN5 PN4 = PN6 ! ! RE-SCALE TERMS IN CONTINUED FRACTION IF TERMS ARE LARGE. ! IF (OFLO <= ABS(PN5)) THEN PN1 = PN1/OFLO PN2 = PN2/OFLO PN3 = PN3/OFLO PN4 = PN4/OFLO END IF END DO ARG = ARG + LOG(GAMMAINC) IF (ELIMIT <= ARG) THEN GAMMAINC = 1.0E+00 - EXP(ARG) ELSE GAMMAINC = 1.0E+00 END IF END IF RETURN END FUNCTION GAMMAINC !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ END MODULE lmdz_lscp_tools