Multilayer extension of ocean_dyn_step_barotropic. One
SSP-RK2 outer step that orchestrates the full per-layer
dynamical core:
EOS → continuity-PPM → Sadourny Coriolis-adv → Mont pressure-force → Laplacian hvisc → bottom drag → surface stress → horizontal tracer advection → diagnose w → vertical tracer advection (Eulerian z) → applies
Each stage runs every compute kernel in sequence, then the apply step. The two SSP-RK2 stages save the prognostic state into the *_0 buffers (h_layer0, u_face_x_layer0, v_face_y_layer0, plus hTr0 on every registered tracer), run two full FE substeps, and average with the saved state to recover the second-order-accurate result.
The Coriolis parameter lives on the coriolis_adv_t slot
as f_corner(:, :) (C-grid corners). Defaults to a uniform
f_0 at init; call cor%set_beta_plane(grid, f_0, beta, y_ref)
for the beta-plane variant f(y) = f_0 + beta*(y - y_ref).
Caller passes the individual slots rather than the full
ocean_state_t to avoid a circular module dependency
(rdb_ocean_state already uses rdb_ocean_dyn).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| type(hgrid_t), | intent(in) | :: | grid | |||
| type(ocean_metrics_t), | intent(in) | :: | metrics |
Curvilinear horizontal metrics — forwarded to the PGF (and, in slice 2, the other geometry-aware kernels). |
||
| type(ocean_dyn_t), | intent(inout) | :: | dyn | |||
| type(eos_t), | intent(in) | :: | eos | |||
| type(coriolis_adv_t), | intent(inout) | :: | cor | |||
| type(continuity_t), | intent(inout) | :: | ct | |||
| type(ocean_pressure_force_t), | intent(inout) | :: | pgf | |||
| type(ocean_horizontal_viscosity_t), | intent(inout) | :: | hv | |||
| type(ocean_bottom_drag_t), | intent(inout) | :: | bd | |||
| type(ocean_surface_stress_t), | intent(inout) | :: | ss | |||
| type(ocean_vertical_advection_t), | intent(inout) | :: | va | |||
| type(ocean_hdiff_tracer_t), | intent(inout) | :: | hd | |||
| type(ocean_vdiff_t), | intent(inout) | :: | vd | |||
| type(ocean_vmix_t), | intent(inout) | :: | vmix | |||
| type(multilayer_state_t), | intent(inout) | :: | ms | |||
| real(kind=wp), | intent(in) | :: | dt | |||
| type(ocean_surface_flux_t), | intent(in), | optional | :: | sf | ||
| type(ocean_geothermal_t), | intent(in), | optional | :: | geo |
Geothermal bottom-heat-flux slot. Absent or |
|
| type(ocean_lateral_mix_t), | intent(inout), | optional | :: | lateral_mix | ||
| type(ocean_epbl_t), | intent(inout), | optional | :: | epbl |
Energetics-based PBL slot. Absent or |
|
| type(ocean_kappa_shear_t), | intent(inout), | optional | :: | kshear |
Kappa-shear interior closure slot. Absent or
|
|
| type(ocean_slopes_t), | intent(inout), | optional | :: | slopes |
Isopycnal-slope diagnostic slot. Absent or |
|
| type(ocean_tidal_mixing_t), | intent(inout), | optional | :: | vmix_tidal |
Tidal-mixing interior closure slot. Absent or
|
|
| type(ocean_bc_state_t), | intent(in), | optional | :: | bc |
Boundary state — forwarded ONLY to the windowed tracer-advect
drain so its single-rank periodic-x / north-fold seam wraps
fire for periodic unsplit runs at |
|
| real(kind=wp), | intent(in), | optional | :: | t |
Model time (s) since run start, used to evaluate the ideal-age
vintage-mode surface value (PR-7). Mirrors the split driver’s
|
|
| type(ocean_top_drag_t), | intent(inout), | optional | :: | td |
Ice-shelf TOP-drag slot ( |
|
| type(ocean_cavity_flux_t), | intent(inout), | optional | :: | cav |
Ice-shelf basal-melt slot ( |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | private | :: | it | ||||
| real(kind=wp), | private | :: | t_now |
subroutine ocean_dyn_step(grid, metrics, dyn, eos, cor, ct, pgf, hv, bd, ss, va, hd, vd, vmix, ms, dt, sf, geo, lateral_mix, epbl, kshear, slopes, vmix_tidal, bc, t, td, cav) !! Multilayer extension of `ocean_dyn_step_barotropic`. One !! SSP-RK2 outer step that orchestrates the full per-layer !! dynamical core: !! !! EOS → continuity-PPM → Sadourny Coriolis-adv !! → Mont pressure-force → Laplacian hvisc !! → bottom drag → surface stress !! → horizontal tracer advection !! → diagnose w → vertical tracer advection (Eulerian z) !! → applies !! !! Each stage runs every compute kernel in sequence, then the !! apply step. The two SSP-RK2 stages save the prognostic !! state into the *_0 buffers (h_layer0, u_face_x_layer0, !! v_face_y_layer0, plus hTr0 on every registered tracer), !! run two full FE substeps, and average with the saved !! state to recover the second-order-accurate result. !! !! The Coriolis parameter lives on the `coriolis_adv_t` slot !! as `f_corner(:, :)` (C-grid corners). Defaults to a uniform !! `f_0` at init; call `cor%set_beta_plane(grid, f_0, beta, y_ref)` !! for the beta-plane variant `f(y) = f_0 + beta*(y - y_ref)`. !! !! Caller passes the individual slots rather than the full !! `ocean_state_t` to avoid a circular module dependency !! (rdb_ocean_state already `use`s rdb_ocean_dyn). type(hgrid_t), intent(in) :: grid type(ocean_metrics_t), intent(in) :: metrics !! Curvilinear horizontal metrics — forwarded to the PGF (and, !! in slice 2, the other geometry-aware kernels). type(ocean_dyn_t), intent(inout) :: dyn type(eos_t), intent(in) :: eos type(coriolis_adv_t), intent(inout) :: cor type(continuity_t), intent(inout) :: ct type(ocean_pressure_force_t), intent(inout) :: pgf type(ocean_horizontal_viscosity_t), intent(inout) :: hv type(ocean_bottom_drag_t), intent(inout) :: bd type(ocean_top_drag_t), intent(inout), optional :: td !! Ice-shelf TOP-drag slot (`&ocean_tdrag_nml`). OPTIONAL so the !! many direct `ocean_dyn_step*` / `run_stage*` call sites in the !! test suite need no churn; the production driver always passes !! it. Absent, or present and disabled, => no kernel launch and !! a bit-identical step. type(ocean_cavity_flux_t), intent(inout), optional :: cav !! Ice-shelf basal-melt slot (`&ocean_cavity_melt_nml`). !! OPTIONAL for the same reason `td` is: the direct !! `ocean_dyn_step*` / `run_stage*` call sites in the test suite !! need no churn, and the production driver always passes it. !! Absent, disabled, or `freshwater="virtual"` => no kernel !! launch and a bit-identical step. It is threaded down here !! rather than acted on in `engine_step_finalize` because the !! real-freshwater volume must be spent in the SAME stage, at !! the SAME stage weight and from the SAME `melt` value as the !! salt and heat halves the surface-flux apply spends -- see !! `ocean_cavity_mass_step`'s docstring. type(ocean_surface_stress_t), intent(inout) :: ss type(ocean_vertical_advection_t), intent(inout) :: va type(ocean_hdiff_tracer_t), intent(inout) :: hd type(ocean_vdiff_t), intent(inout) :: vd type(ocean_vmix_t), intent(inout) :: vmix type(multilayer_state_t), intent(inout) :: ms real(wp), intent(in) :: dt type(ocean_surface_flux_t), intent(in), optional :: sf type(ocean_geothermal_t), intent(in), optional :: geo !! Geothermal bottom-heat-flux slot. Absent or `enable=.false.` !! preserves the historical no-geothermal path bit-identically. type(ocean_lateral_mix_t), intent(inout), optional :: lateral_mix type(ocean_epbl_t), intent(inout), optional :: epbl !! Energetics-based PBL slot. Absent or `enable=.false.` !! preserves the historical PP81/KPP path bit-identically. type(ocean_kappa_shear_t), intent(inout), optional :: kshear !! Kappa-shear interior closure slot. Absent or !! `enable=.false.` preserves the historical path bit-identically. type(ocean_slopes_t), intent(inout), optional :: slopes !! Isopycnal-slope diagnostic slot. Absent or `enable=.false.` !! preserves the historical path bit-identically (diagnostic !! only — never feeds back into prognostics). type(ocean_tidal_mixing_t), intent(inout), optional :: vmix_tidal !! Tidal-mixing interior closure slot. Absent or !! `enable=.false.` preserves the historical path bit-identically. type(ocean_bc_state_t), intent(in), optional :: bc !! Boundary state — forwarded ONLY to the windowed tracer-advect !! drain so its single-rank periodic-x / north-fold seam wraps !! fire for periodic unsplit runs at `dt_tracer_advect_ratio > 1`. !! Absent ⇒ wall seams (the historical unsplit default); ratio = 1 !! never drains so this is inert there (bit-identical). real(wp), intent(in), optional :: t !! Model time (s) since run start, used to evaluate the ideal-age !! vintage-mode surface value (PR-7). Mirrors the split driver's !! `t` dummy (`ocean_dyn_step_split`). Absent ⇒ `t = 0`. integer :: it real(wp) :: t_now ! ---- Save u^n into the *_0 buffers ---- call save_state(ms) if (allocated(ms%tracers)) then do it = 1, size(ms%tracers) call copy_field_3d(ms%tracers(it)%hTr, ms%tracers(it)%hTr0, & size(ms%tracers(it)%hTr, 1), & size(ms%tracers(it)%hTr, 2), & size(ms%tracers(it)%hTr, 3)) end do end if ! MOM6 `set_viscous_BBL`: the per-face bottom boundary layer the ! vdiff glue reads, once per outer step from the start-of-step ! state. No-op unless the per-face BBL glue is configured. call vdiff_set_viscous_bbl(grid, vd, ms, eos, cor%f_corner) ! ---- Stage 1: tendencies at u^n, FE step -> u^(1) ---- call run_stage(grid, metrics, dyn, eos, cor, ct, pgf, hv, bd, ss, va, hd, & vd, vmix, ms, dt, 1, sf=sf, geo=geo, lateral_mix=lateral_mix, & epbl=epbl, kshear=kshear, slopes=slopes, vmix_tidal=vmix_tidal, td=td, & cav=cav) ! ---- Stage 2: tendencies at u^(1), FE step -> u^(1) + dt*L(u^(1)) ---- call run_stage(grid, metrics, dyn, eos, cor, ct, pgf, hv, bd, ss, va, hd, & vd, vmix, ms, dt, 2, sf=sf, geo=geo, lateral_mix=lateral_mix, & epbl=epbl, kshear=kshear, slopes=slopes, vmix_tidal=vmix_tidal, td=td, & cav=cav) ! ---- RK2 average: u^(n+1) = 0.5 * (u^n + stage2 result) ---- call rk2_average(ms) if (dyn%check_h_positive) then call check_h_positive_or_die(grid, ms, "after rk2_average", 3, & dyn%outer_step_count + 1, check_layers=.true.) end if ! Land-face velocity reset on the averaged state (C4 / R4b.2). call mask_layer_velocities(grid, metrics, ms, bt_work=dyn%bt_work) if (allocated(ms%tracers)) then do it = 1, size(ms%tracers) call rk2_average_field_3d(ms%tracers(it)%hTr0, ms%tracers(it)%hTr, & size(ms%tracers(it)%hTr, 1), & size(ms%tracers(it)%hTr, 2), & size(ms%tracers(it)%hTr, 3)) end do end if ! Velocity housekeeping (E7): advective-CFL truncation then the ! absolute maxvel cap. Both no-op when their knob <= 0. call apply_velocity_truncation(ms, metrics, dt, dyn%cfl_trunc, dyn%maxvel, dyn%ntrunc_step) dyn%ntrunc_total = dyn%ntrunc_total + dyn%ntrunc_step ! Phase 2 (6b) windowed tracer-advect drain (unsplit reference path). ! No-op at ratio = 1 (never accumulated). No vcoord here (the unsplit ! path does not remap), so the drain fires on the window-full predicate ! only. `bc` IS forwarded (when present) so periodic-x / north-fold ! seam wraps fire for periodic unsplit runs — matching the split path. if (dyn%dt_tracer_advect_ratio > 1) then ct%t_dyn_rel_adv = ct%t_dyn_rel_adv + dt if (dyn%is_tracer_advect_step()) then if (present(bc)) then call continuity_tracer_drain(grid, metrics, ct, ms, dyn%dt_tracer_advect_ratio, bc=bc) else call continuity_tracer_drain(grid, metrics, ct, ms, dyn%dt_tracer_advect_ratio) end if end if end if ! Ideal-age surface reset (PR-7): once per outer step, after ! rk2_average and the windowed-drain block above (last operator to ! touch k=nz — there is no ALE remap on the unsplit path, so ! post-average + post-drain is already last). See the ordering ! comment in `ocean_dyn_step_split` for the full rationale. if (dyn%is_thermo_step()) then t_now = 0.0_wp if (present(t)) t_now = t call ocean_ideal_age_reset_surface(grid, ms, & ocean_ideal_age_young_val(dyn%ideal_age_young_val, & dyn%ideal_age_sfc_growth_rate, t_now)) end if dyn%outer_step_count = dyn%outer_step_count + 1 end subroutine ocean_dyn_step