Barotropic-substep correction knobs (MOM6 frhatu h-weighting, bc-PGF retro-correction, bt_rem_u drag damping, visc_rem joint weight), the BT_cont_type / upstream-PPM h_face workspace allocations, and the rank-0 PGF/BT configuration log lines.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(config_t), | intent(in) | :: | cfg | |||
| type(ocean_state_t), | intent(inout) | :: | ocean_state | |||
| type(hgrid_t), | intent(in) | :: | grid | |||
| integer, | intent(in) | :: | compute_rank |
subroutine configure_ocean_bt(cfg, ocean_state, grid, compute_rank) !! Barotropic-substep correction knobs (MOM6 frhatu h-weighting, bc-PGF !! retro-correction, bt_rem_u drag damping, visc_rem joint weight), the !! BT_cont_type / upstream-PPM h_face workspace allocations, and the !! rank-0 PGF/BT configuration log lines. type(config_t), intent(in) :: cfg type(ocean_state_t), intent(inout) :: ocean_state type(hgrid_t), intent(in) :: grid integer, intent(in) :: compute_rank ! MOM6 BT_force / eta_PF split: the depth-mean baroclinic PGF forces ! the barotropic substep (default on). ocean_state%dyn%bt_work%bt_bc_pgf_forcing = cfg%ocean%bt%bc_pgf_forcing if (compute_rank == 0 .and. .not. cfg%ocean%bt%bc_pgf_forcing) then call logger%warning("&ocean_bt_nml bc_pgf_forcing = .false.: LEGACY split — the "// & "barotropic mode does not feel the depth-mean baroclinic "// & "pressure gradient (no JEBAR / bottom-pressure torque)") end if ! MOM6 btstep_layer_accel — per-layer bc-PGF retro-correction (needs FV_MOM6). ocean_state%dyn%bt_work%bt_correction_bc_pgf = cfg%ocean%bt%correction_bc_pgf if (compute_rank == 0 .and. cfg%ocean%bt%correction_bc_pgf) then call logger%info("BT correction: bc-PGF retro-correction ON "// & "(MOM6 btstep_layer_accel; requires fv_mom6 PGF)") end if ! MOM6 bt_rem_u — multiplicative drag damping inside the BT substep. ocean_state%dyn%bt_work%bt_substep_drag = cfg%ocean%bt%substep_drag if (compute_rank == 0 .and. cfg%ocean%bt%substep_drag) then call logger%info("BT substep: drag damping ON "// & "(MOM6 bt_rem_u: r·HBBL/(Htot+r·HBBL·dt_bt))") end if ! PR-2 (bt-rem-from-av-rem): bt_rem_u/v from the SAME viscous ! remnant the layered momentum solve uses (MOM6's barotropic ! solver). Mutually exclusive with substep_drag and bt_halo > 0 ! (validated at configure). ocean_state%dyn%bt_work%bt_rem_from_visc_rem = ocean_bt_rem_from_visc_rem_on(cfg) ocean_state%dyn%bt_work%bt_strong_drag = cfg%ocean%bt%strong_drag ocean_state%dyn%bt_work%bt_rescale_strong_drag = cfg%ocean%bt%rescale_strong_drag ! frhat port (&ocean_bt_nml frhat_scheme): the per-layer face-thickness ! closure every barotropic depth mean reads (MOM6 btcalc HYBRID vs the ! plain two-abutting-cell arithmetic mean). Default "arithmetic" == ! bit-identical to the pre-port tree. ocean_state%dyn%bt_work%frhat_scheme = parse_frhat_scheme(cfg%ocean%bt%frhat_scheme, & FRHAT_ARITHMETIC) if (compute_rank == 0 .and. ocean_state%dyn%bt_work%frhat_scheme == FRHAT_HYBRID) then call logger%info("BT frhat: hybrid ON "// & "(MOM6 btcalc HVEL_SCHEME=HYBRID face-thickness closure)") end if if (compute_rank == 0 .and. ocean_bt_rem_from_visc_rem_on(cfg)) then block character(len=:), allocatable :: bt_rem_msg bt_rem_msg = "BT substep: bt_rem = av_rem**(1/n_inner) ON "// & "(MOM6 av_rem/bt_rem from visc_rem" if (cfg%ocean%bt%strong_drag) bt_rem_msg = bt_rem_msg//", strong_drag" if (cfg%ocean%bt%rescale_strong_drag) bt_rem_msg = bt_rem_msg//", rescale_strong_drag" bt_rem_msg = bt_rem_msg//")" call logger%info(bt_rem_msg) end block end if ! MOM6 planetary-only fast loop — drop live ζ_bt/∇KE from the substeps ! (they stay frozen inside F_bt_*_fast); Cor_ref reduces to f·v̄. ocean_state%dyn%bt_work%substep_zeta_ke = cfg%ocean%bt%substep_zeta_ke if (compute_rank == 0 .and. .not. cfg%ocean%bt%substep_zeta_ke) then call logger%info("BT substep: planetary Coriolis only "// & "(substep_zeta_ke=.false.; MOM6 q=f/D parity — "// & "live ζ_bt/∇KE off, frozen copies stay in F_bt)") end if ! RETIRED visc_rem-weighted BT-corrector fold (wt = visc_rem/<visc_rem>_h): ! `correction_visc_rem` is now fail-loud at configure (D1 follow-up — ! MOM6's accel_layer_u never weights this fold), so this field is ! wired straight from the RAW (deprecated) knob, never from ! `visc_rem_chain` — a test that constructs `cfg` directly and sets ! the raw field (bypassing the nml refusal) still gets the weighted ! dispatch the kernel itself supports; everyone else reads 1. ocean_state%dyn%bt_work%bt_correction_visc_rem = cfg%ocean%bt%correction_visc_rem if (compute_rank == 0 .and. cfg%ocean%bt%correction_visc_rem) then call logger%info("BT correction: visc_rem/<visc_rem>_h weight ON "// & "(visc_rem produced by vdiff)") end if ! D1 follow-up: the visc_rem PRODUCER is decoupled from the retired ! weighted fold above — it runs whenever ANY real consumer needs ! visc_rem_u/v fresh (forcing/renorm/bt_rem_from, or visc_rem_chain, ! which implies all three). ocean_state%dyn%bt_work%bt_visc_rem_producer = ocean_bt_visc_rem_producer_on(cfg) ! MOM6 wt_u parity for the BT FORCING assembly (PGF_BUG.md §9): ! friction-damped layers stop forcing the fast loop. Self-sufficient ! (see `bt_visc_rem_producer` above). ocean_state%dyn%bt_work%bt_forcing_visc_rem = ocean_bt_forcing_visc_rem_on(cfg) if (compute_rank == 0 .and. ocean_bt_forcing_visc_rem_on(cfg)) then call logger%info("BT forcing: h·visc_rem weight ON (MOM6 wt_u)") end if ! MOM6 dt·visc_rem·accel parity: the per-layer slow applies are ! attenuated by the viscous remnant (requires correction_visc_rem, ! validated at configure). ocean_state%dyn%accel_visc_rem = cfg%ocean%vdiff%accel_visc_rem if (compute_rank == 0 .and. cfg%ocean%vdiff%accel_visc_rem) then call logger%info("Velocity applies: visc_rem-attenuated ON "// & "(MOM6 u = u0 + dt*visc_rem*accel)") end if ! Eagerly allocate the accel_visc_rem stage-entry velocity snapshots ! here (ocean-state convention: slots allocated at setup, mapped once ! at enter_data — no mid-run lazy alloc), but ONLY when the knob is on ! — on a large grid these two face arrays are ~GB, so the default-off ! path must not pay for them. Runs BEFORE ocean_state_enter_data, so ! ocean_dyn_enter_data_impl copyin sees them allocated (host source=0). if (ocean_state%dyn%accel_visc_rem) then allocate (ocean_state%dyn%avr_u0(grid%nx_total + 1, grid%ny_total, & ocean_state%multilayer%nz_ml), source=0.0_wp) allocate (ocean_state%dyn%avr_v0(grid%nx_total, grid%ny_total + 1, & ocean_state%multilayer%nz_ml), source=0.0_wp) end if ! SPEC S3: outer split-explicit time-scheme selector. The enum ! registration constrains the string (and rejects both retired ! spellings of this scheme, "mom6_pc" and "split_rk2", by name); ! the default is the MOM6 predictor-corrector, `pred_corr`. if (trim(cfg%ocean%bt%split_scheme) == "pred_corr") then ocean_state%dyn%split_scheme = SPLIT_SCHEME_PRED_CORR else ocean_state%dyn%split_scheme = SPLIT_SCHEME_SSP_RK2 end if ocean_state%dyn%pc_be = cfg%ocean%bt%pc_be ! Print the RESOLVED scheme for BOTH branches, not just the non-default ! one. A banner that only speaks up for one value cannot answer "which ! scheme did this namelist actually run", which is the only reliable way ! to enumerate what a default flip touches -- grepping the namelists ! cannot (a key may be absent, commented out, or set in an included ! group). if (compute_rank == 0) then if (ocean_state%dyn%split_scheme == SPLIT_SCHEME_PRED_CORR) then call logger%info("Split scheme: pred_corr (MOM6 predictor-corrector; "// & "tendencies on u_av/h_av; BE = "// & to_string(cfg%ocean%bt%pc_be)//")") else call logger%info("Split scheme: ssp_rk2 (EXPERIMENTAL; two "// & "identical stages, SSP average -- grows internal "// & "gravity waves from a stratified rest state)") end if end if ! SPEC S2b: γ-weighted continuity transport-matching inversion. ocean_state%dyn%bt_work%bt_renorm_visc_rem = ocean_bt_renorm_visc_rem_on(cfg) if (compute_rank == 0 .and. ocean_bt_renorm_visc_rem_on(cfg)) then call logger%info("BT renormaliser: gamma-weighted du + u_cor (MOM6 "// & "continuity inversion parity)") end if ! PR-8 / D1 follow-up: every `*_visc_rem` consumer couples against ! `visc_rem_u/v`, which are only ever filled away from their ! `source=1.0` default by the implicit bottom-drag diagonal fold ! (`&ocean_vdiff_nml implicit_drag`) or `bbl_glue` breaking vdiff's ! per-row unit-sum normalisation — without either the chain is ! exactly gamma==1 (a x1.0). Warn rather than abort (tidal_mixing ! e_uniform=0 precedent): PR-19 (visc_rem) is the named owner that ! fills the arrays for other configurations. (`validate_config` also ! carries this check at configure; repeated here for the rank-0 log.) if (compute_rank == 0 .and. ocean_bt_visc_rem_producer_on(cfg) .and. & .not. (cfg%ocean%vdiff%implicit_drag .or. ocean_state%vdiff%bbl_glue)) then call logger%warning("&ocean_bt_nml forcing_visc_rem/renorm_visc_rem/"// & "bt_rem_from_visc_rem/visc_rem_chain has no effect without "// & "&ocean_vdiff_nml implicit_drag=.true. or bbl_glue=.true. "// & "(visc_rem_u/v stay at their source=1.0 default; PR-19 owns filling them)") end if ! BT_cont_type flux-bounded continuity: allocate the per-face coefficient ! packs so enter_data attaches them; default off leaves them unallocated. ocean_state%dyn%bt_work%use_bt_cont_type = cfg%ocean%bt%use_cont_type if (cfg%ocean%bt%use_cont_type) then block integer :: nx_w, ny_w nx_w = grid%nx_total ny_w = grid%ny_total allocate (ocean_state%dyn%bt_work%BTCL_u(nx_w + 1, ny_w)) allocate (ocean_state%dyn%bt_work%BTCL_v(nx_w, ny_w + 1)) end block end if ! Upstream-PPM h_face for the BT chain — allocate the per-face slots. ocean_state%dyn%bt_work%use_upstream_h_face = cfg%ocean%bt%upstream_h_face if (cfg%ocean%bt%upstream_h_face) then block integer :: nx_w, ny_w nx_w = grid%nx_total ny_w = grid%ny_total allocate (ocean_state%dyn%bt_work%h_face_up_x(nx_w + 1, ny_w), source=0.0_wp) allocate (ocean_state%dyn%bt_work%h_face_up_y(nx_w, ny_w + 1), source=0.0_wp) end block end if if (compute_rank == 0) then if (ocean_state%pressure_force%variant == OPGF_VARIANT_GPRIME) then call logger%info("PGF form: gprime gfs="// & to_string(cfg%ocean%pgf%gprime_gfs)//" m/s² gint="// & to_string(cfg%ocean%pgf%gprime_gint)//" m/s²") else call logger%info("PGF form: "//trim(cfg%ocean%pgf%form)) end if if (cfg%ocean%bt%bebt > 0.0_wp) then call logger%info("BEBT projection: bebt="// & to_string(cfg%ocean%bt%bebt)// & " (BT η flux uses (1+bebt)·u^n − bebt·u^{n-1})") end if if (cfg%ocean%bt%use_cont_type) then call logger%info("BT_cont_type: ON (flux-bounded BT continuity)") if (cfg%ocean%bt%cont_corr_bounds) then call logger%info(" BT_CONT_CORR_BOUNDS=.true. (η-corr bound from BT_cont)") end if end if if (cfg%ocean%bt%upstream_h_face) then call logger%info("BT upstream-h: ON (BT chain uses upstream-PPM h_face)") end if end if end subroutine configure_ocean_bt