diff --git a/components/eam/bld/build-namelist b/components/eam/bld/build-namelist
index 4660e6b8d704..95416cb5e4ac 100755
--- a/components/eam/bld/build-namelist
+++ b/components/eam/bld/build-namelist
@@ -4137,6 +4137,9 @@ if ($nl->get_value('use_gw_convect') =~ /$TRUE/io) {
add_default($nl, 'gw_drag_file');
}
+# this correction is disabled in the released version E3SMv3 and prior versions
+add_default($nl, 'use_fgf_pgrad_correction', 'val'=>'.false.');
+add_default($nl, 'use_fgf_zgrad_correction', 'val'=>'.false.');
add_default($nl, 'use_gw_energy_fix' , 'val'=>'.true.');
diff --git a/components/eam/bld/namelist_files/namelist_definition.xml b/components/eam/bld/namelist_files/namelist_definition.xml
index 0106c418ded0..258c3ef00e27 100644
--- a/components/eam/bld/namelist_files/namelist_definition.xml
+++ b/components/eam/bld/namelist_files/namelist_definition.xml
@@ -1072,6 +1072,18 @@ Whether or not to enable gravity waves produced by convection.
Default: set by build-namelist.
+
+Flag to enable frontogenesis pressure gradient correction for frontal GWD
+Default: set by build-namelist.
+
+
+
+Flag to enable frontogenesis geopotential gradient correction for frontal GWD
+Default: set by build-namelist.
+
+
Whether or not to enable GWD brute-force energy fix.
diff --git a/components/eam/src/dynamics/se/dp_coupling.F90 b/components/eam/src/dynamics/se/dp_coupling.F90
index 5655948e2f39..149fff8b1c4c 100644
--- a/components/eam/src/dynamics/se/dp_coupling.F90
+++ b/components/eam/src/dynamics/se/dp_coupling.F90
@@ -38,7 +38,7 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
use cam_abortutils, only: endrun
use gravity_waves_sources, only: gws_src_fnct
use dyn_comp, only: frontgf_idx, frontga_idx, hvcoord
- use phys_control, only: use_gw_front
+ use phys_control, only: use_gw_front, use_fgf_pgrad_correction, use_fgf_zgrad_correction
use dyn_comp, only: dom_mt
use gllfvremap_mod, only: gfr_dyn_to_fv_phys
@@ -107,7 +107,10 @@ subroutine d_p_coupling(phys_state, phys_tend, pbuf2d, dyn_out)
elem => dyn_out%elem
tl_f = TimeLevel%n0 ! time split physics (with forward-in-time RK)
- if (use_gw_front) call gws_src_fnct(elem, tl_f, nphys, frontgf, frontga)
+ if (use_gw_front) call gws_src_fnct(elem, tl_f, nphys, &
+ use_fgf_pgrad_correction, &
+ use_fgf_zgrad_correction, &
+ frontgf, frontga)
if (fv_nphys > 0) then
!-----------------------------------------------------------------------
diff --git a/components/eam/src/dynamics/se/gravity_waves_sources.F90 b/components/eam/src/dynamics/se/gravity_waves_sources.F90
index 16985e8083e0..c6fcc76dc1fe 100644
--- a/components/eam/src/dynamics/se/gravity_waves_sources.F90
+++ b/components/eam/src/dynamics/se/gravity_waves_sources.F90
@@ -1,6 +1,6 @@
module gravity_waves_sources
use derivative_mod, only : derivative_t
- use dimensions_mod, only : np,nlev
+ use dimensions_mod, only : np,nlev,nlevp
use edgetype_mod, only : EdgeBuffer_t
use element_mod, only : element_t
use hybrid_mod, only : hybrid_t
@@ -38,7 +38,7 @@ subroutine gws_init(elem)
end subroutine gws_init
!-------------------------------------------------------------------------------------------------
- subroutine gws_src_fnct(elem, tl, nphys, frontgf, frontga)
+ subroutine gws_src_fnct(elem, tl, nphys, use_fgf_pgrad_correction, use_fgf_zgrad_correction, frontgf, frontga)
use dimensions_mod, only : npsq, nelemd
use dof_mod, only : UniquePoints
use dyn_comp, only : dom_mt
@@ -51,6 +51,8 @@ subroutine gws_src_fnct(elem, tl, nphys, frontgf, frontga)
type (element_t), intent(inout), dimension(:) :: elem
integer, intent(in ) :: tl
integer, intent(in ) :: nphys
+ logical, intent(in ) :: use_fgf_pgrad_correction
+ logical, intent(in ) :: use_fgf_zgrad_correction
real (kind=real_kind), intent(out ) :: frontgf(nphys*nphys,pver,nelemd)
real (kind=real_kind), intent(out ) :: frontga(nphys*nphys,pver,nelemd)
@@ -67,7 +69,10 @@ subroutine gws_src_fnct(elem, tl, nphys, frontgf, frontga)
hybrid = hybrid_create(par,ithr,hthreads)
allocate(frontgf_thr(nphys,nphys,nlev,nets:nete))
allocate(frontga_thr(nphys,nphys,nlev,nets:nete))
- call compute_frontogenesis(frontgf_thr,frontga_thr,tl,elem,hybrid,nets,nete,nphys)
+ call compute_frontogenesis( frontgf_thr, frontga_thr, tl, &
+ use_fgf_pgrad_correction, &
+ use_fgf_zgrad_correction, &
+ elem, hybrid, nets, nete, nphys )
if (fv_nphys>0) then
do ie = nets,nete
do k = 1,nlev
@@ -88,7 +93,10 @@ subroutine gws_src_fnct(elem, tl, nphys, frontgf, frontga)
end subroutine gws_src_fnct
!-------------------------------------------------------------------------------------------------
- subroutine compute_frontogenesis(frontgf,frontga,tl,elem,hybrid,nets,nete,nphys)
+ subroutine compute_frontogenesis( frontgf, frontga, tl, &
+ use_fgf_pgrad_correction, &
+ use_fgf_zgrad_correction, &
+ elem, hybrid, nets, nete, nphys )
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! compute frontogenesis function F
! F = -gradth dot C
@@ -97,32 +105,45 @@ subroutine compute_frontogenesis(frontgf,frontga,tl,elem,hybrid,nets,nete,nphys)
! gradth = grad(theta)
! C = ( gradth dot grad ) U
!
+ ! For more details on the frontal GW scheme and fronotogenesis source calculation:
+ ! Charron, M., and E. Manzini, 2002: Gravity Waves from Fronts:
+ ! Parameterization and Middle Atmosphere Response in a General
+ ! Circulation Model. J. Atmos. Sci., 59, 923–941
+ !
! Original by Mark Taylor, July 2011
! Change by Santos, 10 Aug 2011:
- ! Integrated into gravity_waves_sources module, several arguments made global
- ! to prevent repeated allocation/initialization
+ ! Integrated into gravity_waves_sources module, several arguments made global
+ ! to prevent repeated allocation/initialization
! Change by Aaron Donahue, April 2017:
! Fixed bug where boundary information was called for processors not associated
! with dynamics when dyn_npes cartesian - Summing along the third dimension is a sum over components for each point
+ do component=1,3
+ dum_cart(:,:,component,k)=sum( elem(ie)%vec_sphere2cart(:,:,component,:) * elem(ie)%state%v(:,:,:,k,tl) ,3)
+ end do
+ end do
+
+ do component=1,3
+ if (use_fgf_pgrad_correction) call compute_vertical_derivative(pint,dum_cart(:,:,component,:),dum_cart_dvert(:,:,component,:))
+ if (use_fgf_zgrad_correction) call compute_vertical_derivative(zint,dum_cart(:,:,component,:),dum_cart_dvert(:,:,component,:))
+ end do
+
+ do k = 1,nlev
+ ! Do ugradv on the cartesian components - Dot u with the gradient of each component
+ do component=1,3
+ dum_grad(:,:,:) = gradient_sphere(dum_cart(:,:,component,k),deriv1,elem(ie)%Dinv)
+ do i=1,2
+ dum_grad(:,:,i) = dum_grad(:,:,i) - dum_cart_dvert(:,:,component,k) * grad_vert_gll(:,:,i,k)
+ end do
+ dum_cart(:,:,component,k) = sum( gradth_gll(:,:,:,k,ie) * dum_grad ,3)
+ enddo
+ ! cartesian -> latlon - vec_sphere2cart is its own pseudoinverse.
+ do i=1,2
+ C(:,:,i) = sum(dum_cart(:,:,:,k)*elem(ie)%vec_sphere2cart(:,:,:,i), 3)
+ end do
+ ! gradth_gll dot C
+ frontgf_gll(:,:,k,ie) = -( C(:,:,1)*gradth_gll(:,:,1,k,ie) + C(:,:,2)*gradth_gll(:,:,2,k,ie) )
+ ! apply mass matrix
+ gradth_gll(:,:,1,k,ie) = gradth_gll(:,:,1,k,ie)*elem(ie)%spheremp(:,:)
+ gradth_gll(:,:,2,k,ie) = gradth_gll(:,:,2,k,ie)*elem(ie)%spheremp(:,:)
+ frontgf_gll(:,:,k,ie) = frontgf_gll(:,:,k,ie)*elem(ie)%spheremp(:,:)
+ end do ! k
+
+ else ! .not. use_fgf_pgrad_correction
+
+ do k = 1,nlev
+ ! pressure at mid points - this pressure preserves the old behavior of E3SMv3 and prior
+ pmid(:,:,k) = hvcoord%hyam(k)*hvcoord%ps0 + hvcoord%hybm(k)*elem(ie)%state%ps_v(:,:,tl)
+ ! potential temperature: theta = T (p/p0)^kappa
+ call get_temperature(elem(ie),temperature,hvcoord,tl)
+ theta(:,:,k) = temperature(:,:,k)*(psurf_ref / pmid(:,:,k))**kappa
+ gradth_gll(:,:,:,k,ie) = gradient_sphere(theta(:,:,k),deriv1,elem(ie)%Dinv)
+ ! compute C = (grad(theta) dot grad ) u
+ C(:,:,:) = ugradv_sphere(gradth_gll(:,:,:,k,ie), elem(ie)%state%v(:,:,:,k,tl),deriv1,elem(ie))
+ ! gradth_gll dot C
+ frontgf_gll(:,:,k,ie) = -( C(:,:,1)*gradth_gll(:,:,1,k,ie) + C(:,:,2)*gradth_gll(:,:,2,k,ie) )
+ ! apply mass matrix
+ gradth_gll(:,:,1,k,ie) = gradth_gll(:,:,1,k,ie)*elem(ie)%spheremp(:,:)
+ gradth_gll(:,:,2,k,ie) = gradth_gll(:,:,2,k,ie)*elem(ie)%spheremp(:,:)
+ frontgf_gll(:,:,k,ie) = frontgf_gll(:,:,k,ie)*elem(ie)%spheremp(:,:)
+ end do ! k
+
+ end if ! use_fgf_pgrad_correction
+
! pack
- call edgeVpack_nlyr(edge_g, elem(ie)%desc, frontgf_gll(:,:,:,ie),nlev,0,3*nlev)
- call edgeVpack_nlyr(edge_g, elem(ie)%desc, gradth_gll(:,:,:,:,ie),2*nlev,nlev,3*nlev)
+ call edgeVpack_nlyr(edge_g, elem(ie)%desc, frontgf_gll(:,:,:,ie),nlev,0,3*nlev)
+ call edgeVpack_nlyr(edge_g, elem(ie)%desc, gradth_gll(:,:,:,:,ie),2*nlev,nlev,3*nlev)
end do ! ie
@@ -192,4 +332,37 @@ subroutine compute_frontogenesis(frontgf,frontga,tl,elem,hybrid,nets,nete,nphys)
end subroutine compute_frontogenesis
!-------------------------------------------------------------------------------------------------
+ subroutine compute_vertical_derivative(vert_int, data_mid, ddata_dvert)
+ !---------------------------------------------------------------------------
+ real(kind=real_kind), intent(in ) :: vert_int(np,np,nlev) ! vertical coord on interfaces (i.e. pint or zint)
+ real(kind=real_kind), intent(in ) :: data_mid(np,np,nlev) ! input data in mid-points
+ real(kind=real_kind), intent(out) :: ddata_dvert(np,np,nlev) ! vertical derivative of data
+ !---------------------------------------------------------------------------
+ integer :: k
+ real(kind=real_kind) :: vert_above(np,np) ! pressure interpolated to interface above the current k mid-point
+ real(kind=real_kind) :: vert_below(np,np) ! pressure interpolated to interface below the current k mid-point
+ real(kind=real_kind) :: data_above(np,np) ! data interpolated to interface above the current k mid-point
+ real(kind=real_kind) :: data_below(np,np) ! data interpolated to interface below the current k mid-point
+ !---------------------------------------------------------------------------
+ do k = 1,nlev
+ if (k==1) then
+ data_above = data_mid(:,:,k)
+ data_below = ( data_mid(:,:,k+1) + data_mid(:,:,k) ) / 2.0 ! interpolate to interface k+1
+ vert_above = ( vert_int(:,:,k+1) + vert_int(:,:,k) ) / 2.0 ! interpolate to mid-point k
+ vert_below = vert_int(:,:,k+1)
+ elseif (k==nlev) then
+ data_above = ( data_mid(:,:,k-1) + data_mid(:,:,k) ) / 2.0 ! interpolate to interface
+ data_below = data_mid(:,:,k)
+ vert_above = vert_int(:,:,k)
+ vert_below = ( vert_int(:,:,k+1) + vert_int(:,:,k) ) / 2.0 ! interpolate to mid-point k
+ else
+ data_above = ( data_mid(:,:,k-1) + data_mid(:,:,k) ) / 2.0 ! interpolate to interface k
+ data_below = ( data_mid(:,:,k+1) + data_mid(:,:,k) ) / 2.0 ! interpolate to interface k+1
+ vert_above = vert_int(:,:,k)
+ vert_below = vert_int(:,:,k+1)
+ end if
+ ddata_dvert(:,:,k) = ( data_above - data_below ) / ( vert_above - vert_below )
+ end do
+ end subroutine compute_vertical_derivative
+ !-------------------------------------------------------------------------------------------------
end module gravity_waves_sources
diff --git a/components/eam/src/physics/cam/phys_control.F90 b/components/eam/src/physics/cam/phys_control.F90
index 501daee3855d..31734d9f636c 100644
--- a/components/eam/src/physics/cam/phys_control.F90
+++ b/components/eam/src/physics/cam/phys_control.F90
@@ -174,6 +174,9 @@ module phys_control
! Convective
logical, public, protected :: use_gw_convect = .false.
+! Frontogenesis gradient correction options
+logical, public, protected :: use_fgf_pgrad_correction = .false.
+logical, public, protected :: use_fgf_zgrad_correction = .false.
!GW energy fix
logical, public, protected :: use_gw_energy_fix = .false.
@@ -258,7 +261,7 @@ subroutine phys_ctl_readnl(nlfile)
use_qqflx_fixer, &
print_fixer_message, &
use_hetfrz_classnuc, use_gw_oro, use_gw_front, use_gw_convect, &
- use_gw_energy_fix, &
+ use_fgf_pgrad_correction,use_fgf_zgrad_correction, use_gw_energy_fix, &
use_od_ls,use_od_bl,use_od_ss,use_od_fd,&
cld_macmic_num_steps, micro_do_icesupersat, &
fix_g1_err_ndrop, ssalt_tuning, resus_fix, convproc_do_aer, &
@@ -378,6 +381,8 @@ subroutine phys_ctl_readnl(nlfile)
call mpibcast(use_hetfrz_classnuc, 1 , mpilog, 0, mpicom)
call mpibcast(use_gw_oro, 1 , mpilog, 0, mpicom)
call mpibcast(use_gw_front, 1 , mpilog, 0, mpicom)
+ call mpibcast(use_fgf_pgrad_correction, 1 , mpilog, 0, mpicom)
+ call mpibcast(use_fgf_zgrad_correction, 1 , mpilog, 0, mpicom)
call mpibcast(use_gw_convect, 1 , mpilog, 0, mpicom)
call mpibcast(use_gw_energy_fix, 1 , mpilog, 0, mpicom)
call mpibcast(use_od_ls, 1 , mpilog, 0, mpicom)
@@ -429,6 +434,12 @@ subroutine phys_ctl_readnl(nlfile)
! Defaults for PBL and microphysics are set in build-namelist. Check here that
! values have been set to guard against problems with hand edited namelists.
+ ! check frontal gwd gradient correction options
+ if (use_fgf_pgrad_correction .and. use_fgf_zgrad_correction) then
+ write(iulog,*)'phys_setopts: use_fgf_pgrad_correction and use_fgf_zgrad_correction cannot both be true'
+ call endrun('phys_setopts: use_fgf_pgrad_correction and use_fgf_zgrad_correction cannot both be true')
+ end if
+
! WACCM-X run option set in build-namelist. Check for valid values
if (.not. (waccmx_opt == 'ionosphere' .or. waccmx_opt == 'neutral' .or. waccmx_opt == 'off')) then
write(iulog,*)'waccm: illegal value of waccmx_opt:', waccmx_opt
diff --git a/components/homme/src/preqx/share/element_ops.F90 b/components/homme/src/preqx/share/element_ops.F90
index 01e31e5ea362..85ca6be5bb61 100644
--- a/components/homme/src/preqx/share/element_ops.F90
+++ b/components/homme/src/preqx/share/element_ops.F90
@@ -221,6 +221,24 @@ subroutine get_temperature(elem,temperature,hvcoord,nt)
end subroutine get_temperature
+ !_____________________________________________________________________
+ subroutine get_hydro_pressure_i(p_i,dp,hvcoord)
+ !
+ implicit none
+
+ real (kind=real_kind), intent(out) :: p_i(np,np,nlevp)
+ real (kind=real_kind), intent(in) :: dp(np,np,nlev)
+ type (hvcoord_t), intent(in) :: hvcoord ! hybrid vertical coordinate struct
+
+ integer :: k
+
+ p_i(:,:,1)=hvcoord%hyai(1)*hvcoord%ps0
+ do k=1,nlev ! SCAN
+ p_i(:,:,k+1)=p_i(:,:,k) + dp(:,:,k)
+ enddo
+
+ end subroutine get_hydro_pressure_i
+
!_____________________________________________________________________
subroutine copy_state(elem,nin,nout)
implicit none
diff --git a/components/homme/src/theta-l/share/element_ops.F90 b/components/homme/src/theta-l/share/element_ops.F90
index 853d3be3085c..033c95609e97 100644
--- a/components/homme/src/theta-l/share/element_ops.F90
+++ b/components/homme/src/theta-l/share/element_ops.F90
@@ -43,6 +43,7 @@
! get_pottemp()
! get_dpnh_dp()
! get_hydro_pressure()
+! get_hydro_pressure_i()
! get_nonhydro_pressure()
! get_phi()
! get_cp_star()
@@ -68,7 +69,7 @@ module element_ops
type(elem_state_t), dimension(:), allocatable :: state0 ! storage for save_initial_state routine
public get_field, get_field_i, get_state, get_pottemp
- public get_temperature, get_phi, get_R_star, get_hydro_pressure
+ public get_temperature, get_phi, get_R_star, get_hydro_pressure, get_hydro_pressure_i
public set_thermostate, set_state, set_state_i, set_elem_state
public set_forcing_rayleigh_friction
public initialize_reference_states, set_theta_ref
@@ -307,6 +308,24 @@ subroutine get_hydro_pressure(p,dp,hvcoord)
end subroutine get_hydro_pressure
+ !_____________________________________________________________________
+ subroutine get_hydro_pressure_i(p_i,dp,hvcoord)
+ !
+ implicit none
+
+ real (kind=real_kind), intent(out) :: p_i(np,np,nlevp)
+ real (kind=real_kind), intent(in) :: dp(np,np,nlev)
+ type (hvcoord_t), intent(in) :: hvcoord ! hybrid vertical coordinate struct
+
+ integer :: k
+
+ p_i(:,:,1)=hvcoord%hyai(1)*hvcoord%ps0
+ do k=1,nlev ! SCAN
+ p_i(:,:,k+1)=p_i(:,:,k) + dp(:,:,k)
+ enddo
+
+ end subroutine get_hydro_pressure_i
+
!_____________________________________________________________________
subroutine get_nonhydro_pressure(elem,pnh,exner,hvcoord,nt)