subroutine register_ocean_bt(cfg, schema)
!! `&ocean_bt_nml`: split-explicit barotropic substep controls.
type(config_t), target, intent(in) :: cfg
type(nml_schema_t), intent(inout) :: schema
type(nml_group_t) :: g
integer, pointer :: pi
real(wp), pointer :: pr
logical, pointer :: pl
character(len=:), pointer :: ps
g%name = "ocean_bt"
g%doc = "Split-explicit barotropic substep controls."
pi => cfg%ocean%bt%n_inner
call g%add(nml_int("n_inner", pi, &
"Barotropic substeps per outer step (0 = unsplit)", min=0))
pl => cfg%ocean%bt%auto_n_inner
call g%add(nml_logical("auto_n_inner", pl, &
"Derive n_inner from the gravity-wave CFL at setup"))
pr => cfg%ocean%bt%cfl_bt_safety
call g%add(nml_real("cfl_bt_safety", pr, &
"Safety fraction on the BT CFL when auto_n_inner"))
pr => cfg%ocean%bt%bebt
call g%add(nml_real("bebt", pr, &
"Forward-velocity-projection weight (MOM6 BEBT; default 0.1 = MOM6)"))
pl => cfg%ocean%bt%use_cont_type
call g%add(nml_logical("use_cont_type", pl, &
"Use the BT_cont flux-bounded closure (MOM6 USE_BT_CONT_TYPE)"))
pl => cfg%ocean%bt%cont_corr_bounds
call g%add(nml_logical("cont_corr_bounds", pl, &
"Use BT_cont flux limits for the eta-correction bound"))
pl => cfg%ocean%bt%upstream_h_face
call g%add(nml_logical("upstream_h_face", pl, &
"Use per-face upstream-PPM column-sum thickness in the BT chain"))
pl => cfg%ocean%bt%correction_h_weighted
call g%add(nml_logical("correction_h_weighted", pl, &
"RETIRED h-weighted BT-corrector fold (refused when set)", &
dead_on_ocean_path="RETIRED -- the h-weighted barotropic-"// &
"correction fold was energy-non-conserving (a positive "// &
"0.5*D^2*H*(kappa-1) source plus shear feedback) and MOM6 "// &
"has no such fold; setting it .true. is a fail-loud "// &
"configure error (validate_config). Drag-aware damping "// &
"is visc_rem_chain (it does not re-weight this fold -- "// &
"correction_visc_rem, which used to, is itself retired)."))
pl => cfg%ocean%bt%visc_rem_chain
call g%add(nml_logical("visc_rem_chain", pl, &
"PR-3 (D1): ONE switch for exactly MOM6's visc_rem/av_rem/"// &
"bt_rem set -- equivalent to switching on the visc_rem "// &
"producer plus forcing_visc_rem + renorm_visc_rem + "// &
"bt_rem_from_visc_rem all at once (never a superset); the "// &
"BT-correction fold stays UNIFORM (MOM6 accel_layer_u never "// &
"weights it; correction_visc_rem, which used to, is retired). "// &
"strong_drag/rescale_strong_drag stay separate keys (their "// &
"own MOM6 params). Still requires ocean_vdiff_nml "// &
"implicit_drag or bbl_glue, else visc_rem is inert "// &
"(=1, warned)."))
pl => cfg%ocean%bt%correction_visc_rem
call g%add(nml_logical("correction_visc_rem", pl, &
"RETIRED (refused when set)", &
dead_on_ocean_path="RETIRED -- MOM6's accel_layer_u applies "// &
"the BT-correction acceleration UNIFORMLY across every layer, "// &
"then the SAME implicit "// &
"friction the glue uses distributes it -- never twice. This "// &
"fold re-weighted it a second time by visc_rem/<visc_rem>_h, "// &
"an unbounded ratio that NaNs the 1-degree Southern Ocean z* "// &
"open-step case under bbl_glue at step ~40. Setting it "// &
".true. is a fail-loud configure error (validate_config). "// &
"Use visc_rem_chain instead."))
pl => cfg%ocean%bt%bt_rem_from_visc_rem
call g%add(nml_logical("bt_rem_from_visc_rem", pl, &
"bt_rem_u/v = mask*av_rem**(1/n_inner), av_rem the frhat-"// &
"weighted depth mean of visc_rem (MOM6's barotropic "// &
"solver); self-sufficient (the producer runs whenever "// &
"this is on), mutually exclusive with substep_drag and "// &
"bt_halo > 0. An equivalent subset of visc_rem_chain, kept "// &
"for granular testing -- prefer visc_rem_chain."))
pl => cfg%ocean%bt%strong_drag
call g%add(nml_logical("strong_drag", pl, &
"MOM6 BT_STRONG_DRAG: rational-approximation bt_rem form "// &
"n_inner*av_rem/(1+(n_inner-1)*av_rem) instead of the plain "// &
"power; requires bt_rem_from_visc_rem (or visc_rem_chain)"))
pl => cfg%ocean%bt%rescale_strong_drag
call g%add(nml_logical("rescale_strong_drag", pl, &
"MOM6 RESCALE_STRONG_DRAG: rescale the BT-correction "// &
"increment by min(bt_rem**n_inner/av_rem, 1.0); requires "// &
"strong_drag"))
ps => cfg%ocean%bt%split_scheme
call g%add(nml_enum("split_scheme", ps, &
"Outer split-explicit time scheme: pred_corr (DEFAULT; MOM6 "// &
"predictor-corrector, slow tendencies on the u_av/h_av step "// &
"time-means, forward-backward gravity-wave pairing; lifts the "// &
"internal-wave dt ceiling) or ssp_rk2 (EXPERIMENTAL; two-stage "// &
"SSP average, widest envelope — the only scheme wired through "// &
"eulerian_z, wet/dry and dt_tracer_advect_ratio>1 — but it "// &
"spuriously grows internal gravity waves out of a stratified "// &
"REST state, En 2.992E-05 vs 1.739E-09 at day 25 on "// &
"resting_stratified_channel.nml; a (omega*dt)^4 noise floor, "// &
"so forced viscous runs sit decades above it and quiescent or "// &
"long spin-up runs do not)", &
allowed=[character(len=9) :: "pred_corr", "ssp_rk2"], &
retired=[character(len=9) :: "mom6_pc", "split_rk2"], &
retired_hint="the MOM6 predictor-corrector is now spelled "// &
"'pred_corr' and is the DEFAULT, so this key can also simply "// &
'be deleted; write split_scheme = "pred_corr" to keep it '// &
"pinned"))
pr => cfg%ocean%bt%pc_be
call g%add(nml_real("pc_be", pr, &
"pred_corr predictor fraction BE (MOM6 BE, 0.6 reference)", &
min=0.0_wp, max=1.0_wp))
pl => cfg%ocean%bt%renorm_visc_rem
call g%add(nml_logical("renorm_visc_rem", pl, &
"gamma-weighted continuity transport-matching inversion "// &
"(MOM6 u_cor = u + du*visc_rem); self-sufficient (the "// &
"producer runs whenever this is on). An equivalent subset "// &
"of visc_rem_chain, kept for granular testing -- prefer "// &
"visc_rem_chain."))
pl => cfg%ocean%bt%forcing_visc_rem
call g%add(nml_logical("forcing_visc_rem", pl, &
"MOM6 wt_u parity: h*visc_rem-weight the BT forcing "// &
"depth-mean so friction-damped (glued) layers do not "// &
"force the fast loop; self-sufficient (the producer runs "// &
"whenever this is on). An equivalent subset of "// &
"visc_rem_chain, kept for granular testing -- prefer "// &
"visc_rem_chain."))
ps => cfg%ocean%bt%frhat_scheme
call g%add(nml_enum("frhat_scheme", ps, &
"Per-layer barotropic face-thickness closure for every depth "// &
"mean that reads a face thickness (derive_bt_from_layers, "// &
"face_depth_mean_u/v, face_depth_mean_rem_u/v's wt_u, "// &
"apply_bt_correction's open/visc_rem folds, and av_rem via "// &
"face_depth_mean_u/v): arithmetic (DEFAULT, bit-identical -- "// &
"the plain two-abutting-cell mean) or hybrid (MOM6 btcalc "// &
"HVEL_SCHEME=HYBRID -- arithmetic "// &
"mean above the shallower column's bed, harmonic mean below "// &
"it, suppressing a thin partial-bed layer's weight next to a "// &
"thick abutting one)", &
allowed=[character(len=10) :: "arithmetic", "hybrid"]))
pl => cfg%ocean%bt%correction_bc_pgf
call g%add(nml_logical("correction_bc_pgf", pl, &
"Per-layer baroclinic-PGF retro-correction for the eta change "// &
"(requires &ocean_pgf_nml form='fv_mom6')"))
pl => cfg%ocean%bt%bc_pgf_forcing
call g%add(nml_logical("bc_pgf_forcing", pl, &
"Force the BT substep with the depth mean of the full slow layer "// &
"PGF, shedding only the free-surface term the PGF itself carries "// &
"(MOM6 BT_force/eta_PF); .false. = legacy split that discarded the "// &
"depth-mean baroclinic PGF (no JEBAR)"))
pl => cfg%ocean%bt%substep_drag
call g%add(nml_logical("substep_drag", pl, &
"Apply a per-substep BT velocity damping factor"))
pl => cfg%ocean%bt%substep_zeta_ke
call g%add(nml_logical("substep_zeta_ke", pl, &
"Integrate live zeta_bt + KE-gradient in the BT fast loop "// &
"(.false. = MOM6 parity: planetary Coriolis only, "// &
"zeta/KE frozen in the slow forcing)"))
pl => cfg%ocean%bt%wave_drag
call g%add(nml_logical("wave_drag", pl, &
"Barotropic linear wave drag master switch (MOM6 BT_LINEAR_WAVE_DRAG)"))
ps => cfg%ocean%bt%wave_drag_form
call g%add(nml_enum("wave_drag_form", ps, "Wave-drag r_H filler", &
allowed=[character(len=15) :: "uniform", "roughness_proxy", "file"]))
pr => cfg%ocean%bt%wave_drag_scale
call g%add(nml_real("wave_drag_scale", pr, &
"Global tuning multiplier on r_H (MOM6 BT_WAVE_DRAG_SCALE)"))
pr => cfg%ocean%bt%wave_drag_r_uniform
call g%add(nml_real("wave_drag_r_uniform", pr, &
"Piston velocity r_H for wave_drag_form='uniform'", units="m/s"))
pr => cfg%ocean%bt%wave_drag_kappa
call g%add(nml_real("wave_drag_kappa", pr, &
"Topographic wavenumber for wave_drag_form='roughness_proxy'", &
units="1/m"))
pr => cfg%ocean%bt%wave_drag_n_bot
call g%add(nml_real("wave_drag_n_bot", pr, &
"Reference bottom N for wave_drag_form='roughness_proxy'", units="1/s"))
pr => cfg%ocean%bt%wave_drag_h2_max
call g%add(nml_real("wave_drag_h2_max", pr, &
"Ceiling on <h^2> proxy for wave_drag_form='roughness_proxy'", &
units="m^2"))
ps => cfg%ocean%bt%wave_drag_file
call g%add(nml_string("wave_drag_file", ps, &
"Reserved for PR-14 (MOM6 BT_WAVE_DRAG_FILE); unused today"))
ps => cfg%ocean%bt%wave_drag_var
call g%add(nml_string("wave_drag_var", ps, &
"Reserved for PR-14 (MOM6 BT_WAVE_DRAG_VAR); unused today"))
pi => cfg%ocean%bt%bt_halo
call g%add(nml_int("bt_halo", pi, &
"Wide-halo BT march-in width "// &
"(-1 = auto: resolves to 0, the march-in is opt-in; "// &
"0 = explicit off, bit-identical)", &
min=BT_HALO_AUTO_SENTINEL))
call schema%add_group(g)
end subroutine register_ocean_bt