Two kernels:
barotropic_substep_linear — linearized shallow-water with closed
walls, no Coriolis, no advection. Used by the
test_ocean_barotropic_substep analytic checks (rest state, constant
forcing, gravity-wave standing mode) and as the unit-test
anchor for the dynamics. Not called by the production
split driver.
barotropic_substep_nonlinear — production substep with
free-surface continuity ((H_ref + η) face thickness),
Coriolis ((ζ + f)·v_perp Sadourny enstrophy-conserving),
and vector-invariant advection. Called by the split RK2
driver inside each outer baroclinic step.
Both kernels read force_u, force_v as constant slow forcing
for the inner substeps and write the time-mean (sum / n_steps)
back into bt_eta, bt_work%bt_ubt, bt_work%bt_vbt on exit.
Per-substep running sums land in bt_work%eta_sum, bt_work%ubt_sum,
bt_work%vbt_sum. The nonlinear path additionally uses
bt_work%bt_zeta_corner, bt_work%bt_ke_centre, bt_eta_new as
per-substep scratch.
Closed-wall convention on the outer boundary:
* u_bt at i=1 and i=nx+1 (array edges) stay at zero
* v_bt at j=1 and j=ny+1 (array edges) stay at zero
* u_bt at i=nghost+1 and i=nghost+nx_phys+1 (physical walls)
stay at zero — slow continuity closes here, so the
barotropic substep must too or sum_k(h_layer) drifts from H+bt_eta
* v_bt at j=nghost+1 and j=nghost+ny_phys+1 (physical walls)
stay at zero — same reason
* ζ at the outer corner ring is clamped to zero so (ζ+f)·v
reduces to the f-plane Coriolis term there
Stability: Forward-Backward Euler ordering — η update first
(consumes u^n, v^n), then u/v updates (consume the just-updated
η). Stable on the gravity-wave eigenmode for
dt_inner · √(g·H) / dx ≤ 1.
Performance footgun (still relevant): the η-update do-concurrent
uses four distinct local() face-thickness variables
(h_face_E/W/N/S) rather than reusing a single h_face. gfortran
15.1 + -O3 -funroll-loops was observed to corrupt the flux
divergence when h_face was reused across if/else branches.
See feedback_gfortran_local_reassign.md in claude memory.
Public only for the unit-test suite (no production module imports it);
ignore when developing production code in other modules.
Linearized barotropic-substep kernel. Forward-Euler steps
the barotropic state (eta, ubt, vbt) at dt_inner for n_steps
against the constant slow forcing force_u, force_v (each
at the same C-grid location as ubt, vbt). Accumulates the
per-step running sums into ubt_sum, vbt_sum, eta_sum;
at the end divides by n_steps and stores the time-mean
back into bt_eta, bt_ubt, bt_vbt for the caller.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics | |||
| type(barotropic_workstate_t), | intent(inout) | :: | bt_work | |||
| real(kind=wp), | intent(in) | :: | force_u(grid%nx_total+1,grid%ny_total) | |||
| real(kind=wp), | intent(in) | :: | force_v(grid%nx_total,grid%ny_total+1) | |||
| integer, | intent(in) | :: | n_steps | |||
| real(kind=wp), | intent(in) | :: | dt_inner | |||
| real(kind=wp), | intent(in), | optional | :: | eta_forcing(grid%nx_total,grid%ny_total) |
Optional equilibrium-tide elevation (m); when present the PGF drives grad(eta - eta_forcing). Absent => bit-identical. |
Nonlinear barotropic substep. Same time-mean accumulator
pattern as barotropic_substep_linear but the per-substep
dynamics include the three barotropic nonlinearities that
matter for MOM6-grade physics:
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(barotropic_workstate_t), | intent(inout) | :: | bt_work |
Carries scalars (g_bt, bebt, wetdry_enable, use_bt_cont_type,
use_upstream_h_face, bt_substep_drag, wd_) and the BTCL_u/v,
h_face_up_x/y, wd_ arrays that are NOT promoted. The
22 fast-loop 2D arrays below replace the bt_work% |
||
| real(kind=wp), | intent(in) | :: | force_u(grid%nx_total+1,grid%ny_total) | |||
| real(kind=wp), | intent(in) | :: | force_v(grid%nx_total,grid%ny_total+1) | |||
| integer, | intent(in) | :: | n_steps | |||
| real(kind=wp), | intent(in) | :: | dt_inner | |||
| real(kind=wp), | intent(inout) | :: | bt_eta(grid%nx_total,grid%ny_total) |
Barotropic SSH at cell centres; read+written every substep. |
||
| real(kind=wp), | intent(in) | :: | bt_H_ref(grid%nx_total,grid%ny_total) |
Reference column thickness (m); read-only inside the fast loop. |
||
| real(kind=wp), | intent(inout) | :: | bt_eta_new(grid%nx_total,grid%ny_total) |
Per-substep η^{n+1} Jacobi scratch; written in Pass 1, read in η-swap. |
||
| real(kind=wp), | intent(inout) | :: | bt_ke_centre(grid%nx_total,grid%ny_total) |
Barotropic KE at cell centres; written in Pass 1, read in Pass 2b/2c. |
||
| real(kind=wp), | intent(inout) | :: | eta_sum(grid%nx_total,grid%ny_total) |
η time-mean accumulator; zeroed at entry, accumulated each substep. |
||
| real(kind=wp), | intent(inout) | :: | bt_eta_end(grid%nx_total,grid%ny_total) |
End-of-loop η snapshot (before time-mean overwrite). |
||
| real(kind=wp), | intent(inout) | :: | bt_ubt(grid%nx_total+1,grid%ny_total) |
Depth-mean u at east faces; read+written every substep. |
||
| real(kind=wp), | intent(inout) | :: | bt_ubt_prev(grid%nx_total+1,grid%ny_total) |
u^{n-1} BEBT projection snapshot; read+written when bebt > 0. |
||
| real(kind=wp), | intent(in) | :: | bt_rem_u(grid%nx_total+1,grid%ny_total) |
BT-substep multiplicative drag factor at u-faces; read-only. |
||
| real(kind=wp), | intent(inout) | :: | ubt_sum(grid%nx_total+1,grid%ny_total) |
u time-mean accumulator; zeroed at entry, accumulated each substep. |
||
| real(kind=wp), | intent(inout) | :: | uhbt_sum(grid%nx_total+1,grid%ny_total) |
Depth-integrated u transport accumulator; zeroed and accumulated. |
||
| real(kind=wp), | intent(inout) | :: | bt_uhbt(grid%nx_total+1,grid%ny_total) |
Time-mean east-face transport (m²/s); written at loop end. |
||
| real(kind=wp), | intent(inout) | :: | bt_ubt_end(grid%nx_total+1,grid%ny_total) |
End-of-loop u snapshot (before time-mean overwrite). |
||
| real(kind=wp), | intent(inout) | :: | bt_vbt(grid%nx_total,grid%ny_total+1) |
Depth-mean v at north faces; read+written every substep. |
||
| real(kind=wp), | intent(inout) | :: | bt_vbt_prev(grid%nx_total,grid%ny_total+1) |
v^{n-1} BEBT projection snapshot; read+written when bebt > 0. |
||
| real(kind=wp), | intent(in) | :: | bt_rem_v(grid%nx_total,grid%ny_total+1) |
BT-substep multiplicative drag factor at v-faces; read-only. |
||
| real(kind=wp), | intent(inout) | :: | vbt_sum(grid%nx_total,grid%ny_total+1) |
v time-mean accumulator; zeroed at entry, accumulated each substep. |
||
| real(kind=wp), | intent(inout) | :: | vhbt_sum(grid%nx_total,grid%ny_total+1) |
Depth-integrated v transport accumulator; zeroed and accumulated. |
||
| real(kind=wp), | intent(inout) | :: | bt_vhbt(grid%nx_total,grid%ny_total+1) |
Time-mean north-face transport (m²/s); written at loop end. |
||
| real(kind=wp), | intent(inout) | :: | bt_vbt_end(grid%nx_total,grid%ny_total+1) |
End-of-loop v snapshot (before time-mean overwrite). |
||
| real(kind=wp), | intent(inout) | :: | bt_zeta_corner(grid%nx_total+1,grid%ny_total+1) |
Barotropic relative vorticity at corners; written in Pass 1, read in Pass 2b/2c. |
||
| real(kind=wp), | intent(in) | :: | f_corner(grid%nx_total+1,grid%ny_total+1) |
Coriolis parameter at corners (from coriolis_adv_t%f_corner); read-only. Passed explicitly so a wide-halo caller (Phase 3c) can supply a wide f_corner without touching the arithmetic body. |
||
| real(kind=wp), | intent(in) | :: | area_cu(grid%nx_total+1,grid%ny_total) | |||
| real(kind=wp), | intent(in) | :: | area_cv(grid%nx_total,grid%ny_total+1) | |||
| real(kind=wp), | intent(in) | :: | dx_cu(grid%nx_total+1,grid%ny_total) | |||
| real(kind=wp), | intent(in) | :: | dx_cv(grid%nx_total,grid%ny_total+1) | |||
| real(kind=wp), | intent(in) | :: | dy_cu(grid%nx_total+1,grid%ny_total) | |||
| real(kind=wp), | intent(in) | :: | dy_cv(grid%nx_total,grid%ny_total+1) | |||
| real(kind=wp), | intent(in) | :: | iarea_bu(grid%nx_total+1,grid%ny_total+1) | |||
| real(kind=wp), | intent(in) | :: | iarea_t(grid%nx_total,grid%ny_total) | |||
| real(kind=wp), | intent(in) | :: | idx_cu(grid%nx_total+1,grid%ny_total) | |||
| real(kind=wp), | intent(in) | :: | idy_cv(grid%nx_total,grid%ny_total+1) | |||
| type(ocean_bc_state_t), | intent(inout), | optional | :: | bc |
|
|
| real(kind=wp), | intent(in), | optional | :: | t |
Outer-step wall time (s) used to evaluate the per-edge tidal constituent table for OBC_TIDAL. Treated as a constant across the barotropic substeps (tidal periods are orders of magnitude longer than the inner dt). Absent ⇒ t = 0; the OBC_TIDAL formula degenerates to OBC_OPEN. |
|
| real(kind=wp), | intent(in), | optional | :: | eta_forcing(grid%nx_total,grid%ny_total) |
Optional equilibrium-tide elevation (m); when present the PGF drives grad(eta - eta_forcing). Absent => bit-identical. |
|
| integer, | intent(in), | optional | :: | bt_halo |
Wide-halo march-in width (Phase 3c). When > 0 the caller has
supplied wide shadow arrays and |
Interior (normal-width) entry point for the nonlinear barotropic
fast loop. Unpacks the bt_work fast-loop arrays and forwards them
to barotropic_substep_nonlinear (bt_halo=0), so the ~20-array
plumbing lives here once instead of at every call site. The optional
bc/t/eta_forcing propagate by absence (F2018 15.5.2.13), so this
single entry reproduces the former present()-branch call variants
bit-for-bit. See bt_wide_substep for the wide-halo march-in twin.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics | |||
| type(barotropic_workstate_t), | intent(inout) | :: | bt_work | |||
| real(kind=wp), | intent(in) | :: | f_corner(grid%nx_total+1,grid%ny_total+1) |
Coriolis at corners ( |
||
| integer, | intent(in) | :: | n_steps | |||
| real(kind=wp), | intent(in) | :: | dt_inner | |||
| type(ocean_bc_state_t), | intent(inout), | optional | :: | bc | ||
| real(kind=wp), | intent(in), | optional | :: | t | ||
| real(kind=wp), | intent(in), | optional | :: | eta_forcing(grid%nx_total,grid%ny_total) |