Skip to content

Commit

Permalink
*Updates in FPMix and Stokes Most (#283)
Browse files Browse the repository at this point in the history
This PR revises the formulation of the legacy K-profile parameterization (KPP) ocean boundary layer scheme. It incorporates:

1.  a non-local momentum flux—the Flux-profile parameterization (`FPMIX`); when the local shear is not aligned with the wind, this scheme adds a non-local momentum flux in the direction of the wind; and 
2.  mixing with and without waves following the Monin-Obukhov Similarity Theory expanded to include Stokes drift (`STOKES_MOST`). This option provides the transition from waveless to ocean surface waves in any stage of growth or decay.

**Summary:**

* Uncomment omega w2x entries;
* Simplify the nonlocal increments in `vertFPMix`;
* In the call to `CVmix_kpp_compute_unresolved_shear`, passes the 2D surface buoyancy flux (`surfBuoyFlux2`) instead of the 1D version (`surfBuoyFlux`), which is preferable. **This is answer changing**;
* Remove `uold` and `vold` diagnostics. These were used in an alternative time-stepping scheme that is now obsolete; 
* Pass boundary layer depths to the RK2 and add consistency check to make sure `FPMix` is always used with `SPLIT`;
* Add the capability to mix down the Eulerian gradient instead of the Lagrangian;
* Make a minimum set of `FPMix` diagnostics available.

This PR relies on CVMix/CVMix-src#94. 

New diagnostics:
```

"StokesXI"  
    ! modules: {ocean_model,ocean_model_d2}
    ! long_name: Stokes Similarity Parameter
    ! units: nondim
    ! cell_methods: xh:mean yh:mean area:mean

"Lam2"  
    ! modules: {ocean_model,ocean_model_d2}
    ! long_name: Ustk0_ustar
    ! units: nondim
    ! cell_methods: xh:mean yh:mean area:mean

"uE_h" 
    ! modules: {ocean_model,ocean_model_z,ocean_model_rho2,ocean_model_d2,ocean_model_z_d2,ocean_model_rho2_d2}
    ! long_name: x-zonal Eulerian
    ! units: m s-1
    ! cell_methods: xh:mean yh:mean zl:mean area:mean
    ! variants: {uE_h,uE_h_xyave}

"vE_h" 
    ! modules: {ocean_model,ocean_model_z,ocean_model_rho2,ocean_model_d2,ocean_model_z_d2,ocean_model_rho2_d2}
    ! long_name: y-merid Eulerian
    ! units: m s-1
    ! cell_methods: xh:mean yh:mean zl:mean area:mean
    ! variants: {vE_h,vE_h_xyave}

"uInc_h"  
    ! modules: {ocean_model,ocean_model_z,ocean_model_rho2,ocean_model_d2,ocean_model_z_d2,ocean_model_rho2_d2}
    ! long_name: x-zonal Eulerian
    ! units: m s-1
    ! cell_methods: xh:mean yh:mean zl:mean area:mean
    ! variants: {uInc_h,uInc_h_xyave}

"vInc_h"  
    ! modules: {ocean_model,ocean_model_z,ocean_model_rho2,ocean_model_d2,ocean_model_z_d2,ocean_model_rho2_d2}
    ! long_name: x-zonal Eulerian
    ! units: m s-1
    ! cell_methods: xh:mean yh:mean zl:mean area:mean
    ! variants: {vInc_h,vInc_h_xyave}

"uStk" 
    ! modules: {ocean_model,ocean_model_z,ocean_model_rho2,ocean_model_d2,ocean_model_z_d2,ocean_model_rho2_d2}
    ! long_name: x-FP du increment
    ! units: m s-1
    ! cell_methods: xh:mean yh:mean zl:mean area:mean
    ! variants: {uStk,uStk_xyave}

"vStk"  
    ! modules: {ocean_model,ocean_model_z,ocean_model_rho2,ocean_model_d2,ocean_model_z_d2,ocean_model_rho2_d2}
    ! long_name: y-FP dv increment
    ! units: m s-1
    ! cell_methods: xh:mean yh:mean zl:mean area:mean
    ! variants: {vStk,vStk_xyave}

"Omega_tau2s" 
    ! modules: {ocean_model,ocean_model_z,ocean_model_rho2,ocean_model_d2,ocean_model_z_d2,ocean_model_rho2_d2}
    ! long_name: Stress direction from shear
    ! units: radians
    ! cell_methods: xh:mean yh:mean zi:point area:mean
    ! variants: {Omega_tau2s,Omega_tau2s_xyave}

"Omega_tau2w"  
    ! modules: {ocean_model,ocean_model_z,ocean_model_rho2,ocean_model_d2,ocean_model_z_d2,ocean_model_rho2_d2}
    ! long_name: Stress direction from wind
    ! units: radians
    ! cell_methods: xh:mean yh:mean zi:point area:mean
    ! variants: {Omega_tau2w,Omega_tau2w_xyave}

"uStk0"  
    ! modules: {ocean_model,ocean_model_d2}
    ! long_name: Zonal Surface Stokes
    ! units: m s-1
    ! cell_methods: xh:mean yh:mean area:mean

"vStk0"  
    ! modules: {ocean_model,ocean_model_d2}
    ! long_name: Merid Surface Stokes
    ! units: m s-1
    ! cell_methods: xh:mean yh:mean area:mean
```
  • Loading branch information
gustavo-marques authored Sep 9, 2024
1 parent a077a61 commit 15deea4
Show file tree
Hide file tree
Showing 8 changed files with 552 additions and 476 deletions.
6 changes: 3 additions & 3 deletions config_src/drivers/nuopc_cap/mom_surface_forcing_nuopc.F90
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,
press=.true., fix_accum_bug=.not.CS%ustar_gustless_bug, &
cfc=CS%use_CFC, marbl=CS%use_marbl_tracers, hevap=CS%enthalpy_cpl, &
tau_mag=.true., ice_ncat=IOB%ice_ncat)
!call safe_alloc_ptr(fluxes%omega_w2x,isd,ied,jsd,jed)
call safe_alloc_ptr(fluxes%omega_w2x,isd,ied,jsd,jed)
call safe_alloc_ptr(fluxes%sw_vis_dir,isd,ied,jsd,jed)
call safe_alloc_ptr(fluxes%sw_vis_dif,isd,ied,jsd,jed)
call safe_alloc_ptr(fluxes%sw_nir_dir,isd,ied,jsd,jed)
Expand Down Expand Up @@ -762,7 +762,7 @@ subroutine convert_IOB_to_forces(IOB, forces, index_bounds, Time, G, US, CS)

call safe_alloc_ptr(forces%p_surf,isd,ied,jsd,jed)
call safe_alloc_ptr(forces%p_surf_full,isd,ied,jsd,jed)
!call safe_alloc_ptr(forces%omega_w2x,isd,ied,jsd,jed)
call safe_alloc_ptr(forces%omega_w2x,isd,ied,jsd,jed)

if (CS%rigid_sea_ice) then
call safe_alloc_ptr(forces%rigidity_ice_u,IsdB,IedB,jsd,jed)
Expand Down Expand Up @@ -923,7 +923,7 @@ subroutine convert_IOB_to_forces(IOB, forces, index_bounds, Time, G, US, CS)
forces%tau_mag(i,j) = gustiness + G%mask2dT(i,j) * sqrt(taux_at_h(i,j)**2 + tauy_at_h(i,j)**2)
forces%ustar(i,j) = sqrt(gustiness*Irho0 + Irho0 * G%mask2dT(i,j) * &
sqrt(taux_at_h(i,j)**2 + tauy_at_h(i,j)**2))
!forces%omega_w2x(i,j) = atan(tauy_at_h(i,j), taux_at_h(i,j))
forces%omega_w2x(i,j) = atan(tauy_at_h(i,j), taux_at_h(i,j))
enddo ; enddo
call pass_vector(forces%taux, forces%tauy, G%Domain, halo=1)
else ! C-grid wind stresses.
Expand Down
19 changes: 18 additions & 1 deletion src/core/MOM.F90
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ module MOM
use MOM_dynamics_split_RK2, only : step_MOM_dyn_split_RK2, register_restarts_dyn_split_RK2
use MOM_dynamics_split_RK2, only : initialize_dyn_split_RK2, end_dyn_split_RK2
use MOM_dynamics_split_RK2, only : MOM_dyn_split_RK2_CS, remap_dyn_split_rk2_aux_vars
use MOM_dynamics_split_RK2, only : init_dyn_split_RK2_diabatic
use MOM_dynamics_split_RK2b, only : step_MOM_dyn_split_RK2b, register_restarts_dyn_split_RK2b
use MOM_dynamics_split_RK2b, only : initialize_dyn_split_RK2b, end_dyn_split_RK2b
use MOM_dynamics_split_RK2b, only : MOM_dyn_split_RK2b_CS, remap_dyn_split_RK2b_aux_vars
Expand Down Expand Up @@ -2102,6 +2103,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, &
logical :: symmetric ! If true, use symmetric memory allocation.
logical :: save_IC ! If true, save the initial conditions.
logical :: do_unit_tests ! If true, call unit tests.
logical :: fpmix ! Needed to decide if BLD should be passed to RK2.
logical :: test_grid_copy = .false.

logical :: bulkmixedlayer ! If true, a refined bulk mixed layer scheme is used
Expand Down Expand Up @@ -2206,6 +2208,16 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, &
default=.false.)
endif

! FPMIX is needed to decide if boundary layer depth should be passed to RK2
call get_param(param_file, '', "FPMIX", fpmix, &
"If true, add non-local momentum flux increments and diffuse down the Eulerian gradient.", &
default=.false., do_not_log=.true.)

if (fpmix .and. .not. CS%split) then
call MOM_error(FATAL, "initialize_MOM: "//&
"FPMIX=True only works when SPLIT=True.")
endif

call get_param(param_file, "MOM", "BOUSSINESQ", Boussinesq, &
"If true, make the Boussinesq approximation.", default=.true., do_not_log=.true.)
call get_param(param_file, "MOM", "SEMI_BOUSSINESQ", semi_Boussinesq, &
Expand Down Expand Up @@ -3334,6 +3346,11 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, &
CS%sponge_CSp, CS%ALE_sponge_CSp, CS%oda_incupd_CSp, CS%int_tide_CSp)
endif

! GMM, the following is needed to get BLDs into the dynamics module
if (CS%split .and. fpmix) then
call init_dyn_split_RK2_diabatic(CS%diabatic_CSp, CS%dyn_split_RK2_CSp)
endif

if (associated(CS%sponge_CSp)) &
call init_sponge_diags(Time, G, GV, US, diag, CS%sponge_CSp)

Expand Down Expand Up @@ -3609,7 +3626,7 @@ subroutine set_restart_fields(GV, US, param_file, CS, restart_CSp)
! hML is needed when using the ice shelf module
call get_param(param_file, '', "ICE_SHELF", use_ice_shelf, default=.false., &
do_not_log=.true.)
if (use_ice_shelf) then
if (use_ice_shelf .and. associated(CS%Hml)) then
call register_restart_field(CS%Hml, "hML", .false., restart_CSp, &
"Mixed layer thickness", "m", conversion=US%Z_to_m)
endif
Expand Down
88 changes: 58 additions & 30 deletions src/core/MOM_dynamics_split_RK2.F90
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module MOM_dynamics_split_RK2
use MOM_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
use MOM_cpu_clock, only : CLOCK_COMPONENT, CLOCK_SUBCOMPONENT
use MOM_cpu_clock, only : CLOCK_MODULE_DRIVER, CLOCK_MODULE, CLOCK_ROUTINE
use MOM_diabatic_driver, only : diabatic_CS, extract_diabatic_member
use MOM_diag_mediator, only : diag_mediator_init, enable_averages
use MOM_diag_mediator, only : disable_averaging, post_data, safe_alloc_ptr
use MOM_diag_mediator, only : post_product_u, post_product_sum_u
Expand Down Expand Up @@ -45,7 +46,9 @@ module MOM_dynamics_split_RK2
use MOM_continuity, only : continuity_init, continuity_stencil
use MOM_CoriolisAdv, only : CorAdCalc, CoriolisAdv_CS
use MOM_CoriolisAdv, only : CoriolisAdv_init, CoriolisAdv_end
use MOM_CVMix_KPP, only : KPP_get_BLD, KPP_CS
use MOM_debugging, only : check_redundant
use MOM_energetic_PBL, only : energetic_PBL_get_MLD, energetic_PBL_CS
use MOM_grid, only : ocean_grid_type
use MOM_hor_index, only : hor_index_type
use MOM_hor_visc, only : horizontal_viscosity, hor_visc_CS, hor_visc_vel_stencil
Expand Down Expand Up @@ -137,14 +140,16 @@ module MOM_dynamics_split_RK2
real ALLOCABLE_, dimension(NIMEM_,NJMEM_,NKMEM_) :: pbce !< pbce times eta gives the baroclinic pressure
!! anomaly in each layer due to free surface height
!! anomalies [L2 H-1 T-2 ~> m s-2 or m4 kg-1 s-2].
type(KPP_CS), pointer :: KPP_CSp => NULL() !< KPP control structure needed to ge
type(energetic_PBL_CS), pointer :: energetic_PBL_CSp => NULL() !< ePBL control structure

real, pointer, dimension(:,:) :: taux_bot => NULL() !< frictional x-bottom stress from the ocean
!! to the seafloor [R L Z T-2 ~> Pa]
real, pointer, dimension(:,:) :: tauy_bot => NULL() !< frictional y-bottom stress from the ocean
!! to the seafloor [R L Z T-2 ~> Pa]
type(BT_cont_type), pointer :: BT_cont => NULL() !< A structure with elements that describe the
!! effective summed open face areas as a function
!! of barotropic flow.
real, pointer, dimension(:,:) :: taux_bot => NULL() !< frictional x-bottom stress from the ocean
!! to the seafloor [R L Z T-2 ~> Pa]
real, pointer, dimension(:,:) :: tauy_bot => NULL() !< frictional y-bottom stress from the ocean
!! to the seafloor [R L Z T-2 ~> Pa]
type(BT_cont_type), pointer :: BT_cont => NULL() !< A structure with elements that describe the
!! effective summed open face areas as a function
!! of barotropic flow.

! This is to allow the previous, velocity-based coupling with between the
! baroclinic and barotropic modes.
Expand Down Expand Up @@ -174,13 +179,13 @@ module MOM_dynamics_split_RK2
!! the extent to which the treatment of gravity waves
!! is forward-backward (0) or simulated backward
!! Euler (1) [nondim]. 0 is often used.
logical :: debug !< If true, write verbose checksums for debugging purposes.
real :: Cemp_NL !< Empirical coefficient of non-local momentum mixing [nondim]
logical :: debug !< If true, write verbose checksums for debugging purposes.
logical :: debug_OBC !< If true, do debugging calls for open boundary conditions.
logical :: fpmix = .false. !< If true, applies profiles of momentum flux magnitude and direction.
logical :: fpmix !< If true, add non-local momentum flux increments and diffuse down the Eulerian gradient.
logical :: module_is_initialized = .false. !< Record whether this module has been initialized.

!>@{ Diagnostic IDs
integer :: id_uold = -1, id_vold = -1
integer :: id_uh = -1, id_vh = -1
integer :: id_umo = -1, id_vmo = -1
integer :: id_umo_2d = -1, id_vmo_2d = -1
Expand Down Expand Up @@ -267,6 +272,7 @@ module MOM_dynamics_split_RK2
public register_restarts_dyn_split_RK2
public initialize_dyn_split_RK2
public remap_dyn_split_RK2_aux_vars
public init_dyn_split_RK2_diabatic
public end_dyn_split_RK2

!>@{ CPU time clock IDs
Expand Down Expand Up @@ -394,7 +400,8 @@ subroutine step_MOM_dyn_split_RK2(u_inst, v_inst, h, tv, visc, Time_local, dt, f
logical :: Use_Stokes_PGF ! If true, add Stokes PGF to hydrostatic PGF
!---For group halo pass
logical :: showCallTree, sym

logical :: lFPpost ! Used to only post diagnostics in vertFPmix when fpmix=true and
! in the corrector step (not the predict)
integer :: i, j, k, is, ie, js, je, Isq, Ieq, Jsq, Jeq, nz
integer :: cont_stencil, obc_stencil, vel_stencil

Expand Down Expand Up @@ -710,16 +717,22 @@ subroutine step_MOM_dyn_split_RK2(u_inst, v_inst, h, tv, visc, Time_local, dt, f
call thickness_to_dz(h, tv, dz, G, GV, US, halo_size=1)
call vertvisc_coef(up, vp, h, dz, forces, visc, tv, dt_pred, G, GV, US, CS%vertvisc_CSp, &
CS%OBC, VarMix)
call vertvisc(up, vp, h, forces, visc, dt_pred, CS%OBC, CS%AD_pred, CS%CDp, G, &
GV, US, CS%vertvisc_CSp, CS%taux_bot, CS%tauy_bot, waves=waves)

if (CS%fpmix) then
hbl(:,:) = 0.0
if (associated(visc%h_ML)) hbl(:,:) = visc%h_ML(:,:)
call vertFPmix(up, vp, uold, vold, hbl, h, forces, &
dt_pred, G, GV, US, CS%vertvisc_CSp, CS%OBC)
call vertvisc(up, vp, h, forces, visc, dt_pred, CS%OBC, CS%ADp, CS%CDp, G, &
GV, US, CS%vertvisc_CSp, CS%taux_bot, CS%tauy_bot, waves=waves)
if (ASSOCIATED(CS%KPP_CSp)) call KPP_get_BLD(CS%KPP_CSp, hbl, G, US, m_to_BLD_units=GV%m_to_H)
if (ASSOCIATED(CS%energetic_PBL_CSp)) &
call energetic_PBL_get_MLD(CS%energetic_PBL_CSp, hbl, G, US, m_to_MLD_units=GV%m_to_H)

! lFPpost must be false in the predictor step to avoid averaging into the diagnostics
lFPpost = .false.
call vertFPmix(up, vp, uold, vold, hbl, h, forces, dt_pred, lFPpost, CS%Cemp_NL, &
G, GV, US, CS%vertvisc_CSp, CS%OBC, waves=waves)
call vertvisc(up, vp, h, forces, visc, dt_pred, CS%OBC, CS%AD_pred, CS%CDp, G, &
GV, US, CS%vertvisc_CSp, CS%taux_bot, CS%tauy_bot, fpmix=CS%fpmix, waves=waves)
else
call vertvisc(up, vp, h, forces, visc, dt_pred, CS%OBC, CS%AD_pred, CS%CDp, G, &
GV, US, CS%vertvisc_CSp, CS%taux_bot, CS%tauy_bot, waves=waves)
endif

if (showCallTree) call callTree_wayPoint("done with vertvisc (step_MOM_dyn_split_RK2)")
Expand Down Expand Up @@ -960,12 +973,15 @@ subroutine step_MOM_dyn_split_RK2(u_inst, v_inst, h, tv, visc, Time_local, dt, f

call thickness_to_dz(h, tv, dz, G, GV, US, halo_size=1)
call vertvisc_coef(u_inst, v_inst, h, dz, forces, visc, tv, dt, G, GV, US, CS%vertvisc_CSp, CS%OBC, VarMix)
call vertvisc(u_inst, v_inst, h, forces, visc, dt, CS%OBC, CS%ADp, CS%CDp, G, GV, US, &
CS%vertvisc_CSp, CS%taux_bot, CS%tauy_bot,waves=waves)

if (CS%fpmix) then
call vertFPmix(u_inst, v_inst, uold, vold, hbl, h, forces, dt, &
G, GV, US, CS%vertvisc_CSp, CS%OBC)
lFPpost = .true.
call vertFPmix(u_inst, v_inst, uold, vold, hbl, h, forces, dt, lFPpost, CS%Cemp_NL, &
G, GV, US, CS%vertvisc_CSp, CS%OBC, Waves=Waves)
call vertvisc(u_inst, v_inst, h, forces, visc, dt, CS%OBC, CS%ADp, CS%CDp, G, GV, US, &
CS%vertvisc_CSp, CS%taux_bot, CS%tauy_bot, fpmix=CS%fpmix, waves=waves)

else
call vertvisc(u_inst, v_inst, h, forces, visc, dt, CS%OBC, CS%ADp, CS%CDp, G, GV, US, &
CS%vertvisc_CSp, CS%taux_bot, CS%tauy_bot, waves=waves)
endif
Expand Down Expand Up @@ -1052,11 +1068,6 @@ subroutine step_MOM_dyn_split_RK2(u_inst, v_inst, h, tv, visc, Time_local, dt, f
CS%CAu_pred_stored = .false.
endif

if (CS%fpmix) then
if (CS%id_uold > 0) call post_data(CS%id_uold, uold, CS%diag)
if (CS%id_vold > 0) call post_data(CS%id_vold, vold, CS%diag)
endif

! The time-averaged free surface height has already been set by the last call to btstep.

! Deallocate this memory to avoid a memory leak. ### We should revisit how this array is declared. -RWH
Expand Down Expand Up @@ -1290,6 +1301,17 @@ subroutine remap_dyn_split_RK2_aux_vars(G, GV, CS, h_old_u, h_old_v, h_new_u, h_

end subroutine remap_dyn_split_RK2_aux_vars

!> Initializes aspects of the dyn_split_RK2 that depend on diabatic processes.
!! Needed when BLDs are used in the dynamics.
subroutine init_dyn_split_RK2_diabatic(diabatic_CSp, CS)
type(diabatic_CS), intent(in) :: diabatic_CSp !< diabatic structure
type(MOM_dyn_split_RK2_CS), pointer :: CS !< module control structure

call extract_diabatic_member(diabatic_CSp, KPP_CSp=CS%KPP_CSp)
call extract_diabatic_member(diabatic_CSp, energetic_PBL_CSp=CS%energetic_PBL_CSp)

end subroutine init_dyn_split_RK2_diabatic

!> This subroutine initializes all of the variables that are used by this
!! dynamic core, including diagnostics and the cpu clocks.
subroutine initialize_dyn_split_RK2(u, v, h, tv, uh, vh, eta, Time, G, GV, US, param_file, &
Expand Down Expand Up @@ -1402,8 +1424,13 @@ subroutine initialize_dyn_split_RK2(u, v, h, tv, uh, vh, eta, Time, G, GV, US, p
"timestep for use in the predictor step of the next split RK2 timestep.", &
default=.true.)
call get_param(param_file, mdl, "FPMIX", CS%fpmix, &
"If true, apply profiles of momentum flux magnitude and "//&
" direction", default=.false.)
"If true, add non-local momentum flux increments and diffuse down the Eulerian gradient.", &
default=.false.)
if (CS%fpmix) then
call get_param(param_file, "MOM", "CEMP_NL", CS%Cemp_NL, &
"Empirical coefficient of non-local momentum mixing.", &
units="nondim", default=3.6)
endif
call get_param(param_file, mdl, "REMAP_AUXILIARY_VARS", CS%remap_aux, &
"If true, apply ALE remapping to all of the auxiliary 3-dimensional "//&
"variables that are needed to reproduce across restarts, similarly to "//&
Expand Down Expand Up @@ -1480,7 +1507,8 @@ subroutine initialize_dyn_split_RK2(u, v, h, tv, uh, vh, eta, Time, G, GV, US, p
call PressureForce_init(Time, G, GV, US, param_file, diag, CS%PressureForce_CSp, &
CS%SAL_CSp, CS%tides_CSp)
call hor_visc_init(Time, G, GV, US, param_file, diag, CS%hor_visc, ADp=CS%ADp)
call vertvisc_init(MIS, Time, G, GV, US, param_file, diag, CS%ADp, dirs, ntrunc, CS%vertvisc_CSp)
call vertvisc_init(MIS, Time, G, GV, US, param_file, diag, CS%ADp, dirs, &
ntrunc, CS%vertvisc_CSp, CS%fpmix)
CS%set_visc_CSp => set_visc
call updateCFLtruncationValue(Time, CS%vertvisc_CSp, US, activate=is_new_run(restart_CS) )

Expand Down
Loading

0 comments on commit 15deea4

Please sign in to comment.