LCOV - code coverage report
Current view: top level - atmos_phys/phys_utils - atmos_phys_pbl_utils.F90 (source / functions) Hit Total Coverage
Test: coverage.info Lines: 34 44 77.3 %
Date: 2025-03-13 19:04:48 Functions: 10 12 83.3 %

          Line data    Source code
       1             : module atmos_phys_pbl_utils
       2             :     ! Planetary boundary layer related functions used for vertical diffusion schemes.
       3             : 
       4             :     use ccpp_kinds, only: kind_phys
       5             : 
       6             :     implicit none
       7             :     private
       8             : 
       9             :     public :: calc_ideal_gas_rrho
      10             :     public :: calc_friction_velocity
      11             :     public :: calc_kinematic_heat_flux
      12             :     public :: calc_kinematic_water_vapor_flux
      13             :     public :: calc_kinematic_buoyancy_flux
      14             :     public :: calc_obukhov_length
      15             :     public :: calc_virtual_temperature
      16             :     public :: calc_eddy_flux_coefficient
      17             :     public :: calc_free_atm_eddy_flux_coefficient
      18             : 
      19             :     real(kind_phys), parameter :: minimum_friction_velocity     = 0.01_kind_phys ! Assuming minimum for coarse grids
      20             :     real(kind_phys), parameter :: minimum_eddy_flux_coefficient = 0.01_kind_phys ! CCM1 2.f.14
      21             : 
      22             : contains
      23             : 
      24     6463800 :     pure elemental function calc_ideal_gas_rrho(rair, surface_temperature, pmid) result(rrho)
      25             :         ! air density reciprocal
      26             :         ! Taken from https://glossary.ametsoc.org/wiki/Equation_of_state
      27             :         ! where \alpha = rrho
      28             :         real(kind_phys), intent(in) :: rair  ! gas constant for dry air         [ J kg-1 K-1 ]
      29             :         real(kind_phys), intent(in) :: surface_temperature                    ! [ K          ]
      30             :         real(kind_phys), intent(in) :: pmid  ! midpoint pressure (bottom level) [ Pa         ]
      31             : 
      32             :         real(kind_phys)             :: rrho  ! 1./bottom level density          [ m3 kg-1    ]
      33             : 
      34     6463800 :         rrho = rair * surface_temperature / pmid
      35     6463800 :     end function calc_ideal_gas_rrho
      36             : 
      37     6463800 :     pure elemental function calc_friction_velocity(taux, tauy, rrho) result(friction_velocity)
      38             :         ! https://glossary.ametsoc.org/wiki/Friction_velocity
      39             :         ! NOTE: taux,tauy come form the expansion of the Reynolds stress
      40             :         !
      41             :         ! Also found in:
      42             :         ! Stull, Roland B. An Introduction to Boundary Layer Meteorology. Springer Kluwer Academic Publishers, 1988. Print.
      43             :         ! DOI: https://doi.org/10.1007/978-94-009-3027-8
      44             :         ! Equation 2.10b, page 67
      45             : 
      46             :         real(kind_phys), intent(in) :: taux              ! surface u stress          [ N m-2   ]
      47             :         real(kind_phys), intent(in) :: tauy              ! surface v stress          [ N m-2   ]
      48             :         real(kind_phys), intent(in) :: rrho              ! 1./bottom level density   [ m3 kg-1 ]
      49             : 
      50             :         real(kind_phys)             :: friction_velocity ! surface friction velocity [ m s-1   ]
      51             : 
      52     6463800 :         friction_velocity = max( sqrt( sqrt(taux**2 + tauy**2)*rrho ), minimum_friction_velocity )
      53     6463800 :     end function calc_friction_velocity
      54             : 
      55     3693600 :     pure elemental function calc_kinematic_heat_flux(shflx, rrho, cpair) result(khfs)
      56             :         real(kind_phys), intent(in)  :: shflx  ! surface heat flux            [ W m-2       ]
      57             :         real(kind_phys), intent(in)  :: rrho   ! 1./bottom level density      [ m3 kg-1     ]
      58             :         real(kind_phys), intent(in)  :: cpair  ! specific heat of dry air     [ J kg-1 K-1  ]
      59             : 
      60             :         real(kind_phys)              :: khfs   ! surface kinematic heat flux  [ m K s-1     ]
      61             : 
      62     3693600 :         khfs = shflx*rrho/cpair
      63     3693600 :     end function calc_kinematic_heat_flux
      64             : 
      65     3693600 :     pure elemental function calc_kinematic_water_vapor_flux(qflx, rrho) result(kqfs)
      66             :         real(kind_phys), intent(in)  :: qflx ! water vapor flux                   [ kg m-2 s-1    ]
      67             :         real(kind_phys), intent(in)  :: rrho ! 1./bottom level density            [ m3 kg-1       ]
      68             : 
      69             :         real(kind_phys)              :: kqfs ! surface kinematic water vapor flux [ kg kg-1 m s-1 ]
      70             : 
      71     3693600 :         kqfs = qflx*rrho
      72     3693600 :     end function calc_kinematic_water_vapor_flux
      73             : 
      74     3693600 :     pure elemental function calc_kinematic_buoyancy_flux(khfs, zvir, ths, kqfs) result(kbfs)
      75             :         real(kind_phys), intent(in) :: khfs  ! surface kinematic heat flux          [ m K s-1       ]
      76             :         real(kind_phys), intent(in) :: zvir  ! rh2o/rair - 1
      77             :         real(kind_phys), intent(in) :: ths   ! potential temperature at surface     [ K             ]
      78             :         real(kind_phys), intent(in) :: kqfs  ! surface kinematic water vapor flux   [ kg kg-1 m s-1 ]
      79             : 
      80             :         ! (`kbfs = \overline{(w' \theta'_v)}_s`)
      81             :         real(kind_phys)             :: kbfs  ! surface kinematic buoyancy flux      [ m K s-1 ]
      82             : 
      83     3693600 :         kbfs = khfs + zvir*ths*kqfs
      84     3693600 :     end function calc_kinematic_buoyancy_flux
      85             : 
      86     3693600 :     pure elemental function calc_obukhov_length(thvs, ustar, g, karman, kbfs) result(obukhov_length)
      87             :         ! Stull, Roland B. An Introduction to Boundary Layer Meteorology. Springer Kluwer Academic Publishers, 1988. Print.
      88             :         ! DOI: https://doi.org/10.1007/978-94-009-3027-8
      89             :         ! Equation 5.7c, page 181
      90             :         ! \frac{-\theta*u_*^3}{g*k*\overline{(w' \theta_v')}_s} = frac{-\theta*u_*^3}{g*k*kbfs}
      91             : 
      92             :         real(kind_phys), intent(in)  :: thvs              ! virtual potential temperature at surface [ K       ]
      93             :         real(kind_phys), intent(in)  :: ustar             ! Surface friction velocity                [ m s-1   ]
      94             :         real(kind_phys), intent(in)  :: g                 ! acceleration of gravity                  [ m s-2   ]
      95             :         real(kind_phys), intent(in)  :: karman            ! Von Karman's constant (unitless)
      96             :         real(kind_phys), intent(in)  :: kbfs              ! surface kinematic buoyancy flux          [ m K s-1 ]
      97             : 
      98             :         real(kind_phys)              :: obukhov_length    ! Obukhov length                           [ m       ]
      99             : 
     100             :         ! Added sign(...) term to prevent division by 0 and using the fact that
     101             :         ! `kbfs = \overline{(w' \theta_v')}_s`
     102             :         obukhov_length = -thvs * ustar**3 /                             &
     103     3693600 :                          (g*karman*(kbfs + sign(1.e-10_kind_phys,kbfs)))
     104     3693600 :     end function calc_obukhov_length
     105             : 
     106    85876200 :     pure elemental function calc_virtual_temperature(temperature, specific_humidity, zvir) result(virtual_temperature)
     107             :         ! Williamson, D., Kiehl, J., Ramanathan, V., Dickinson, R., & Hack, J. (1987).
     108             :         ! Description of the NCAR Community Climate Model (CCM1).
     109             :         ! University Corporation for Atmospheric Research. https://doi.org/10.5065/D6TB14WH (Original work published 1987)
     110             :         ! Equation 2.a.7
     111             : 
     112             :         real(kind_phys), intent(in) :: temperature
     113             :         real(kind_phys), intent(in) :: specific_humidity
     114             :         real(kind_phys), intent(in) :: zvir    ! rh2o/rair - 1
     115             : 
     116             :         real(kind_phys)             :: virtual_temperature
     117             : 
     118    85876200 :         virtual_temperature = temperature * (1.0_kind_phys + zvir*specific_humidity)
     119    85876200 :     end function calc_virtual_temperature
     120             : 
     121           0 :     pure elemental function calc_eddy_flux_coefficient(mixing_length_squared, &
     122             :                                                        richardson_number,     &
     123             :                                                        shear_squared)         &
     124             :                                                        result(kvf)
     125             :         ! Computes exchange coefficients for turbulent flows.
     126             :         !
     127             :         ! The stable case (Richardson number, Ri>0) is taken from Holtslag and
     128             :         ! Beljaars (1989), ECMWF proceedings.
     129             :         ! The unstable case (Richardson number, Ri<0) is taken from  CCM1.
     130             : 
     131             :         real(kind_phys), intent(in)  :: mixing_length_squared     ! [ m2     ]
     132             :         real(kind_phys), intent(in)  :: richardson_number         ! [ 1      ]
     133             :         real(kind_phys), intent(in)  :: shear_squared             ! [ s-2    ]
     134             : 
     135             :         real(kind_phys)              :: kvf    ! Eddy diffusivity ! [ m2 s-1 ]
     136             : 
     137             :         real(kind_phys)              :: fofri  ! f(ri)
     138             :         real(kind_phys)              :: kvn    ! Neutral Kv
     139             : 
     140           0 :         if( richardson_number < 0.0_kind_phys ) then
     141           0 :             fofri = unstable_gradient_richardson_stability_parameter(richardson_number)
     142             :         else
     143           0 :             fofri = stable_gradient_richardson_stability_parameter(richardson_number)
     144             :         end if
     145           0 :         kvn = neutral_exchange_coefficient(mixing_length_squared, shear_squared)
     146           0 :         kvf = max( minimum_eddy_flux_coefficient, kvn * fofri )
     147           0 :     end function calc_eddy_flux_coefficient
     148             : 
     149    84952800 :     pure elemental function calc_free_atm_eddy_flux_coefficient(mixing_length_squared, &
     150             :                                                                 richardson_number,     &
     151             :                                                                 shear_squared)         &
     152             :                                                                 result(kvf)
     153             :         ! same as austausch_atm but only mixing for Ri<0
     154             :         ! i.e. no background mixing and mixing for Ri>0
     155             : 
     156             :         real(kind_phys), intent(in)  :: mixing_length_squared ! [ m2     ]
     157             :         real(kind_phys), intent(in)  :: richardson_number     ! [ 1      ]
     158             :         real(kind_phys), intent(in)  :: shear_squared         ! [ s-2    ]
     159             : 
     160             :         real(kind_phys)              :: kvf                   ! [ m2 s-1 ]
     161             : 
     162             :         real(kind_phys)              :: fofri ! f(ri)
     163             :         real(kind_phys)              :: kvn   ! Neutral Kv
     164             : 
     165    84952800 :         kvf = 0.0_kind_phys
     166    84952800 :         if( richardson_number < 0.0_kind_phys ) then
     167     1947345 :             fofri = unstable_gradient_richardson_stability_parameter(richardson_number)
     168     1947345 :             kvn = neutral_exchange_coefficient(mixing_length_squared, shear_squared)
     169     1947345 :             kvf = kvn * fofri
     170             :         end if
     171    84952800 :     end function calc_free_atm_eddy_flux_coefficient
     172             : 
     173     1947345 :     pure elemental function unstable_gradient_richardson_stability_parameter(richardson_number) result(modifier)
     174             :         ! Williamson, D., Kiehl, J., Ramanathan, V., Dickinson, R., & Hack, J. (1987).
     175             :         ! Description of the NCAR Community Climate Model (CCM1).
     176             :         ! University Corporation for Atmospheric Research. https://doi.org/10.5065/D6TB14WH (Original work published 1987)
     177             :         ! Equation 2.f.13
     178             :         ! \sqrt{ 1-18*Ri }
     179             : 
     180             :         real(kind_phys), intent(in)  :: richardson_number
     181             : 
     182             :         real(kind_phys)              :: modifier
     183             : 
     184     1947345 :         modifier = sqrt( 1._kind_phys - 18._kind_phys * richardson_number )
     185     1947345 :     end function unstable_gradient_richardson_stability_parameter
     186             : 
     187           0 :     pure elemental function stable_gradient_richardson_stability_parameter(richardson_number) result(modifier)
     188             :         ! Holtslag, A. A. M., and Beljaars A. C. M. , 1989: Surface flux parameterization schemes: Developments and experiences at KNMI.
     189             :         ! ECMWF Workshop on Parameterization of Fluxes and Land Surface, Reading, United Kingdom, ECMWF, 121–147.
     190             :         ! equation 20, page 140
     191             :         ! Originally used published equation from CCM1, 2.f.12, page 11
     192             :         ! \frac{1}{1+10*Ri*(1+8*Ri)}
     193             : 
     194             :         real(kind_phys), intent(in)  :: richardson_number
     195             : 
     196             :         real(kind_phys)              :: modifier
     197             : 
     198             :         modifier = 1.0_kind_phys /                                                                                              &
     199           0 :                  ( 1.0_kind_phys + 10.0_kind_phys * richardson_number * ( 1.0_kind_phys + 8.0_kind_phys * richardson_number ) )
     200           0 :     end function stable_gradient_richardson_stability_parameter
     201             : 
     202     1947345 :     pure elemental function neutral_exchange_coefficient(mixing_length_squared, shear_squared) result(neutral_k)
     203             :         ! Williamson, D., Kiehl, J., Ramanathan, V., Dickinson, R., & Hack, J. (1987).
     204             :         ! Description of the NCAR Community Climate Model (CCM1).
     205             :         ! University Corporation for Atmospheric Research. https://doi.org/10.5065/D6TB14WH (Original work published 1987)
     206             :         ! Equation 2.f.15, page 12
     207             :         ! NOTE: shear_squared variable currently (01/2025) computed in hb_diff.F90 (s2 in trbintd(...)) matches referenced equation.
     208             : 
     209             :         real(kind_phys), intent(in) :: mixing_length_squared ! [ m2     ]
     210             :         real(kind_phys), intent(in) :: shear_squared         ! [ s-2    ]
     211             : 
     212             :         real(kind_phys)             :: neutral_k
     213             : 
     214     1947345 :         neutral_k = mixing_length_squared * sqrt(shear_squared)
     215     1947345 :     end function neutral_exchange_coefficient
     216             : end module atmos_phys_pbl_utils

Generated by: LCOV version 1.14