Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PBL utils transfer #193

Open
wants to merge 34 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
adbad1c
Initial PBL utils refactor.
mwaxmonsky Jan 13, 2025
ae2fbb1
Merge branch 'development' into feature/pbl_utils_refactor
mwaxmonsky Jan 14, 2025
06e0328
Updating documentation and clarifying variable names.
mwaxmonsky Jan 14, 2025
aba5ec1
Minor clarification.
mwaxmonsky Jan 14, 2025
9fbafbb
Replacing r8 with kind_phys. Additional whitespace and spelling corr…
mwaxmonsky Jan 15, 2025
1fcc007
Update function name and fix equation page reference.
mwaxmonsky Jan 15, 2025
8e106fb
Elemental refactoring of austausch functions. Refactored variable na…
mwaxmonsky Jan 17, 2025
71f5e04
Renaming file to not conflict with CAM.
mwaxmonsky Jan 17, 2025
483f46b
Whitespace fixes.
mwaxmonsky Jan 17, 2025
d8c00aa
Removing file added back accidentally
mwaxmonsky Jan 22, 2025
2e6a946
Whitespace fix.
mwaxmonsky Jan 22, 2025
9193552
Fixing runtime error.
mwaxmonsky Jan 22, 2025
860abf9
Reorder parameter declarations to match function parameter list and a…
mwaxmonsky Jan 23, 2025
8dc6284
Fixing line spacing.
mwaxmonsky Jan 23, 2025
e71de9a
Whitespace fixing
mwaxmonsky Jan 23, 2025
bcc145a
Fix minimum parameter name.
mwaxmonsky Jan 23, 2025
1bf1633
Adding example unit test.
mwaxmonsky Jan 24, 2025
6ad515c
Removing unused ifdef and fix test name.
mwaxmonsky Jan 24, 2025
31632c6
Removing unused compiler flags.
mwaxmonsky Jan 24, 2025
933affd
Adding missing newline.
mwaxmonsky Jan 24, 2025
937a087
Add phys_utils to code coverage.
mwaxmonsky Jan 24, 2025
8ad300a
Updating variable names to attempt modernization.
mwaxmonsky Jan 27, 2025
0d9e0be
Updating unit tests missed in previous commit.
mwaxmonsky Jan 27, 2025
7538c86
Update test names to reflect function name changes.
mwaxmonsky Jan 27, 2025
a348392
Updating minimum value member name.
mwaxmonsky Jan 27, 2025
a74defe
Updating free function to free_atm.
mwaxmonsky Jan 27, 2025
7a70379
Fixing function name under test.
mwaxmonsky Jan 27, 2025
1d0801a
adding latex form of obukhov length
mwaxmonsky Jan 27, 2025
3302625
Adding comment on unknown variable.
mwaxmonsky Jan 27, 2025
426f759
Merge branch 'development' into feature/pbl_utils_refactor
mwaxmonsky Jan 28, 2025
e76c482
Fixing whitespace
mwaxmonsky Jan 28, 2025
48189a8
Address review comments.
mwaxmonsky Feb 12, 2025
01bfbf4
Adding variable name updates missed in previous commit.
mwaxmonsky Feb 12, 2025
8376133
Merge branch 'development' into feature/pbl_utils_refactor
mwaxmonsky Feb 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
run: |
source venv/bin/activate
cd build
gcovr -r .. --filter '\.\./schemes' --html atmospheric_physics_code_coverage.html --txt
gcovr -r .. --filter '\.\./schemes' --filter '\.\./phys_utils' --html atmospheric_physics_code_coverage.html --txt

- name: Upload code coverage results
uses: actions/upload-artifact@v4
Expand Down
213 changes: 213 additions & 0 deletions phys_utils/atmos_pbl_utils.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
module atmos_phys_pbl_utils
! Planetary boundary layer related functions used for vertical diffusion schemes.

use ccpp_kinds, only: kind_phys

implicit none
private

public :: calc_rrho
public :: calc_friction_velocity
public :: calc_kinematic_heat_flux
public :: calc_kinematic_water_vapor_flux
public :: calc_kinematic_buoyancy_flux
public :: calc_obukhov_length
public :: calc_virtual_temperature
public :: calc_eddy_flux_coefficient
public :: calc_free_atm_eddy_flux_coefficient

real(kind_phys), parameter :: minimum_friction_velocity = 0.01_kind_phys ! Assuming minimum for coarse grids
real(kind_phys), parameter :: minimum_eddy_flux_coefficient = 0.01_kind_phys ! CCM1 2.f.14

contains

pure elemental function calc_rrho(rair, surface_temperature, pmid) result(rrho)
! air density reciprocal
real(kind_phys), intent(in) :: rair ! gas constant for dry air
real(kind_phys), intent(in) :: surface_temperature
real(kind_phys), intent(in) :: pmid ! midpoint pressure (bottom level)

real(kind_phys) :: rrho ! 1./bottom level density
jimmielin marked this conversation as resolved.
Show resolved Hide resolved

rrho = rair * surface_temperature / pmid
end function calc_rrho

pure elemental function calc_friction_velocity(taux, tauy, rrho) result(friction_velocity)
! https://glossary.ametsoc.org/wiki/Friction_velocity
! NOTE: taux,tauy come form the expansion of the Reynolds stress
!
! Also found in:
! Stull, Roland B. An Introduction to Boundary Layer Meteorology. Springer Kluwer Academic Publishers, 1988. Print.
! DOI: https://doi.org/10.1007/978-94-009-3027-8
! Equation 2.10b, page 67

real(kind_phys), intent(in) :: taux ! surface u stress [N/m2]
real(kind_phys), intent(in) :: tauy ! surface v stress [N/m2]
real(kind_phys), intent(in) :: rrho ! 1./bottom level density

real(kind_phys) :: friction_velocity ! surface friction velocity [m/s]
jimmielin marked this conversation as resolved.
Show resolved Hide resolved

friction_velocity = max( sqrt( sqrt(taux**2 + tauy**2)*rrho ), minimum_friction_velocity )
end function calc_friction_velocity

pure elemental function calc_kinematic_heat_flux(shflx, rrho, cpair) result(khfs)
real(kind_phys), intent(in) :: shflx ! surface heat flux (W/m2)
real(kind_phys), intent(in) :: rrho ! 1./bottom level density [ m3/kg ]
real(kind_phys), intent(in) :: cpair ! specific heat of dry air

real(kind_phys) :: khfs ! sfc kinematic heat flux [mK/s]
jimmielin marked this conversation as resolved.
Show resolved Hide resolved

khfs = shflx*rrho/cpair
end function calc_kinematic_heat_flux

pure elemental function calc_kinematic_water_vapor_flux(qflx, rrho) result(kqfs)
real(kind_phys), intent(in) :: qflx ! water vapor flux (kg/m2/s)
real(kind_phys), intent(in) :: rrho ! 1./bottom level density [ m3/kg ]

real(kind_phys) :: kqfs ! sfc kinematic water vapor flux [m/s]

kqfs = qflx*rrho
end function calc_kinematic_water_vapor_flux

pure elemental function calc_kinematic_buoyancy_flux(khfs, zvir, ths, kqfs) result(kbfs)
real(kind_phys), intent(in) :: khfs ! sfc kinematic heat flux [mK/s]
real(kind_phys), intent(in) :: zvir ! rh2o/rair - 1
real(kind_phys), intent(in) :: ths ! potential temperature at surface [K]
real(kind_phys), intent(in) :: kqfs ! sfc kinematic water vapor flux [m/s]

real(kind_phys) :: kbfs ! sfc kinematic buoyancy flux [mK/s] (`kbfs = \overline{(w' \theta'_v)}_s`)

kbfs = khfs + zvir*ths*kqfs
end function calc_kinematic_buoyancy_flux

pure elemental function calc_obukhov_length(thvs, ustar, g, vk, kbfs) result(obukhov_length)
! Stull, Roland B. An Introduction to Boundary Layer Meteorology. Springer Kluwer Academic Publishers, 1988. Print.
! DOI: https://doi.org/10.1007/978-94-009-3027-8
! Equation 5.7c, page 181
! \frac{-\theta*u_*^3}{g*k*\overline{(w' \theta_v')}_s} = frac{-\theta*u_*^3}{g*k*kbfs}

real(kind_phys), intent(in) :: thvs ! virtual potential temperature at surface
real(kind_phys), intent(in) :: ustar ! Surface friction velocity [ m/s ]
real(kind_phys), intent(in) :: g ! acceleration of gravity
real(kind_phys), intent(in) :: vk ! Von Karman's constant
jimmielin marked this conversation as resolved.
Show resolved Hide resolved
real(kind_phys), intent(in) :: kbfs ! sfc kinematic buoyancy flux [m*K/s]

real(kind_phys) :: obukhov_length ! Obukhov length
jimmielin marked this conversation as resolved.
Show resolved Hide resolved

! Added sign(...) term to prevent division by 0 and using the fact that
! `kbfs = \overline{(w' \theta_v')}_s`
obukhov_length = -thvs * ustar**3 / &
(g*vk*(kbfs + sign(1.e-10_kind_phys,kbfs)))
end function calc_obukhov_length

pure elemental function calc_virtual_temperature(temperature, specific_humidity, zvir) result(virtual_temperature)
! Williamson, D., Kiehl, J., Ramanathan, V., Dickinson, R., & Hack, J. (1987).
! Description of the NCAR Community Climate Model (CCM1).
! University Corporation for Atmospheric Research. https://doi.org/10.5065/D6TB14WH (Original work published 1987)
! Equation 2.a.7

real(kind_phys), intent(in) :: temperature
real(kind_phys), intent(in) :: specific_humidity
real(kind_phys), intent(in) :: zvir ! rh2o/rair - 1

real(kind_phys) :: virtual_temperature

virtual_temperature = temperature * (1.0_kind_phys + zvir*specific_humidity)
end function calc_virtual_temperature

pure elemental function calc_eddy_flux_coefficient(mixing_length_squared, &
richardson_number, &
shear_squared) &
result(kvf)
! Computes exchange coefficients for turbulent flows.
!
! The stable case (Richardson number, Ri>0) is taken from Holtslag and
! Beljaars (1989), ECMWF proceedings.
! The unstable case (Richardson number, Ri<0) is taken from CCM1.

real(kind_phys), intent(in) :: mixing_length_squared
real(kind_phys), intent(in) :: richardson_number
real(kind_phys), intent(in) :: shear_squared

real(kind_phys) :: kvf ! Eddy diffusivity for heat and tracers

real(kind_phys) :: fofri ! f(ri)
real(kind_phys) :: kvn ! Neutral Kv
jimmielin marked this conversation as resolved.
Show resolved Hide resolved

if( richardson_number < 0.0_kind_phys ) then
fofri = unstable_gradient_richardson_stability_parameter(richardson_number)
else
fofri = stable_gradient_richardson_stability_parameter(richardson_number)
end if
kvn = neutral_exchange_coefficient(mixing_length_squared, shear_squared)
kvf = max( minimum_eddy_flux_coefficient, kvn * fofri )
end function calc_eddy_flux_coefficient

pure elemental function calc_free_atm_eddy_flux_coefficient(mixing_length_squared, &
richardson_number, &
shear_squared) &
result(kvf)
! same as austausch_atm but only mixing for Ri<0
! i.e. no background mixing and mixing for Ri>0

real(kind_phys), intent(in) :: mixing_length_squared
real(kind_phys), intent(in) :: richardson_number
real(kind_phys), intent(in) :: shear_squared

real(kind_phys) :: kvf

real(kind_phys) :: fofri ! f(ri)
real(kind_phys) :: kvn ! Neutral Kv

kvf = 0.0_kind_phys
if( richardson_number < 0.0_kind_phys ) then
fofri = unstable_gradient_richardson_stability_parameter(richardson_number)
kvn = neutral_exchange_coefficient(mixing_length_squared, shear_squared)
kvf = kvn * fofri
end if
end function calc_free_atm_eddy_flux_coefficient

pure elemental function unstable_gradient_richardson_stability_parameter(richardson_number) result(modifier)
! Williamson, D., Kiehl, J., Ramanathan, V., Dickinson, R., & Hack, J. (1987).
! Description of the NCAR Community Climate Model (CCM1).
! University Corporation for Atmospheric Research. https://doi.org/10.5065/D6TB14WH (Original work published 1987)
! Equation 2.f.13
! \sqrt{ 1-18*Ri }

real(kind_phys), intent(in) :: richardson_number

real(kind_phys) :: modifier

modifier = sqrt( 1._kind_phys - 18._kind_phys * richardson_number )
end function unstable_gradient_richardson_stability_parameter

pure elemental function stable_gradient_richardson_stability_parameter(richardson_number) result(modifier)
! Holtslag, A. A. M., and Beljaars A. C. M. , 1989: Surface flux parameterization schemes: Developments and experiences at KNMI.
! ECMWF Workshop on Parameterization of Fluxes and Land Surface, Reading, United Kingdom, ECMWF, 121–147.
! equation 20, page 140
! Originally used published equation from CCM1, 2.f.12, page 11
! \frac{1}{1+10*Ri*(1+8*Ri)}

real(kind_phys), intent(in) :: richardson_number

real(kind_phys) :: modifier

modifier = 1.0_kind_phys / &
( 1.0_kind_phys + 10.0_kind_phys * richardson_number * ( 1.0_kind_phys + 8.0_kind_phys * richardson_number ) )
end function stable_gradient_richardson_stability_parameter

pure elemental function neutral_exchange_coefficient(mixing_length_squared, shear_squared) result(neutral_k)
! Williamson, D., Kiehl, J., Ramanathan, V., Dickinson, R., & Hack, J. (1987).
! Description of the NCAR Community Climate Model (CCM1).
! University Corporation for Atmospheric Research. https://doi.org/10.5065/D6TB14WH (Original work published 1987)
! Equation 2.f.15, page 12
! NOTE: shear_squared vriable currently (01/2025) computed in hb_diff.F90 (trbintd) matches referenced equation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vriable -> variable

I looked in hb_diff.F90 and the other files in the companion PR in ESCOMP/CAM (https://github.com/ESCOMP/CAM/pull/1235/files) and couldn't find where neutral_exchange_coefficient was being used, except through calc_eddy_flux_coefficient.

Additionally hb_diff.F90 still names the shear squared variable as s2 and not shear_squared.

I was wondering if you intended for there to be any further changes on trbintd in hb_diff.F90 in the future?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the typo!

In terms of uses of neutral_exchange_coefficient, yup, it's only a helper function (though probably named a little too long) to prevent us from having to copy/paste the ml2*sqrt(s2). It's only used twice so it is right on that edge of probably not needing it but I'm happy to discuss whether it's useful enough to pull out or not.

Updated the comment to reference s2.

In an ideal world, yes, I would like to update and pull some of the functions in trbintd into the physics utils as they are completely standalone and also follow CCM1/other documents that might be useful in other schemes. There was already enough in this PR so we decided it best to add it to review and follow up in the next step with possibly refactoring trbintd.

In theory, there may be some improvements to doing so as well since the computed data is done every time step anyway, moving it into atmos_phys would allow for a cleaner API for the host so that the temporary data is computed by the physics layer and used directly instead of the host doing physics calculations.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for explaining @mwaxmonsky! This sounds great. All the updates will greatly help eventually CCPPizing hb_diff.F90 (when I was first scoping it out months ago I kept thinking how to deal with these pbl_utils...) so I really appreciate this work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nusbaume Actually, after closer inspection, would it be worthwhile to remove trbintd in this PR? It computes s2, n2, and ri and we actually don't use n2 from what I can tell. So we could at least remove the n2 part and I could look at cleaning up the API right now and I don't think there's be that much of a lift to pull that into atmos_phys. Would this be reasonable at the moment?


real(kind_phys), intent(in) :: mixing_length_squared
real(kind_phys), intent(in) :: shear_squared

real(kind_phys) :: neutral_k

neutral_k = mixing_length_squared * sqrt(shear_squared)
end function neutral_exchange_coefficient
end module atmos_phys_pbl_utils
9 changes: 9 additions & 0 deletions test/unit-test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ add_library(utilities ${UTILITIES_SRC})
target_compile_options(utilities PRIVATE -ffree-line-length-none)
target_include_directories(utilities PUBLIC ${CMAKE_CURRENT_BINARY_DIR})

set(PHYS_UTILS_SRC
../../phys_utils/atmos_pbl_utils.F90
include/ccpp_kinds.F90
)

add_library(phys_utils ${PHYS_UTILS_SRC})
target_compile_options(phys_utils PRIVATE -ffree-line-length-none)
target_include_directories(phys_utils PUBLIC ${CMAKE_CURRENT_BINARY_DIR})

if(ATMOSPHERIC_PHYSICS_ENABLE_TESTS OR ATMOSPHERIC_PHYSICS_ENABLE_CODE_COVERAGE)
enable_testing()
add_subdirectory(tests)
Expand Down
2 changes: 1 addition & 1 deletion test/unit-test/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
add_subdirectory(utilities)

add_subdirectory(phys_utils)
4 changes: 4 additions & 0 deletions test/unit-test/tests/phys_utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
add_pfunit_ctest(phys_utils_tests
TEST_SOURCES test_atmos_pbl_utils.pf
LINK_LIBRARIES phys_utils
)
25 changes: 25 additions & 0 deletions test/unit-test/tests/phys_utils/test_atmos_pbl_utils.pf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@test
subroutine test_free_eddy_coef_is_zero_when_ri_equals_zero()
use funit
use atmos_phys_pbl_utils, only : calc_free_atm_eddy_flux_coefficient
use ccpp_kinds, only: kind_phys

real(kind_phys) :: kvf

kvf = calc_free_atm_eddy_flux_coefficient(30.0_kind_phys, 0.0_kind_phys, 0.01_kind_phys)

@assertEqual(0.0_kind_phys, kvf)
end subroutine test_free_eddy_coef_is_zero_when_ri_equals_zero

@test
subroutine test_free_eddy_atm_coef_is_zero_when_ri_stable_near_zero()
use funit
use atmos_phys_pbl_utils, only : calc_free_atm_eddy_flux_coefficient
use ccpp_kinds, only: kind_phys

real(kind_phys) :: kvf

kvf = calc_free_atm_eddy_flux_coefficient(30.0_kind_phys, nearest(0.0_kind_phys, 1.0_kind_phys), 0.01_kind_phys)

@assertEqual(0.0_kind_phys, kvf)
end subroutine test_free_eddy_atm_coef_is_zero_when_ri_stable_near_zero