subroutine ocean_metrics_init(this, grid)
!! Allocate + zero every metric array. Always allocates (configure
!! runs after init, before `enter_data`); off-cost is ~24
!! `(nx,ny)`-class arrays (~2 MB at Tasman size).
class(ocean_metrics_t), intent(inout) :: this
type(hgrid_t), intent(in) :: grid
integer :: nx, ny
nx = grid%nx_total
ny = grid%ny_total
! Lengths
allocate (this%dxT(nx, ny), source=0.0_wp)
allocate (this%dyT(nx, ny), source=0.0_wp)
allocate (this%dxCu(nx + 1, ny), source=0.0_wp)
allocate (this%dyCu(nx + 1, ny), source=0.0_wp)
allocate (this%dxCv(nx, ny + 1), source=0.0_wp)
allocate (this%dyCv(nx, ny + 1), source=0.0_wp)
allocate (this%dxBu(nx + 1, ny + 1), source=0.0_wp)
allocate (this%dyBu(nx + 1, ny + 1), source=0.0_wp)
! Topo face widths
allocate (this%dy_cu(nx + 1, ny), source=0.0_wp)
allocate (this%dx_cv(nx, ny + 1), source=0.0_wp)
! BT transport widths: ALWAYS full size (2D, cheap) so the
! barotropic substep can read them unconditionally — no branch, no
! placeholder. `metrics_finalize` / `metrics_apply_land_mask` keep
! them a byte copy of `dy_cu` / `dx_cv` until porous barriers narrow
! them, which is what makes the knob-off path bit-identical.
allocate (this%dy_cu_bt(nx + 1, ny), source=0.0_wp)
allocate (this%dx_cv_bt(nx, ny + 1), source=0.0_wp)
! Porous barriers start at the (1,1)/(1,1,1) placeholder size:
! `metrics_porous_alloc` grows them at configure time (which runs
! after init and before enter_data) only when the knob is on, so a
! default run pays ~7 words instead of two full 3D fields.
allocate (this%por_bed(1, 1), source=0.0_wp)
allocate (this%por_dmin_u(1, 1), source=0.0_wp)
allocate (this%por_dmax_u(1, 1), source=0.0_wp)
allocate (this%por_davg_u(1, 1), source=0.0_wp)
allocate (this%por_dmin_v(1, 1), source=0.0_wp)
allocate (this%por_dmax_v(1, 1), source=0.0_wp)
allocate (this%por_davg_v(1, 1), source=0.0_wp)
allocate (this%por_face_area_u(1, 1, 1), source=1.0_wp)
allocate (this%por_face_area_v(1, 1, 1), source=1.0_wp)
! z-level closed faces: same placeholder discipline as the porous
! arrays -- `metrics_closed_faces_alloc` grows them at configure
! (after init, before enter_data) only when the knob is on. The
! placeholder is 1 (fully open) so an accidental read is inert, but
! it must NEVER reach an explicit-shape device dummy: every consumer
! names `open_u`/`open_v` only inside a branch guarded by
! `use_closed_faces`.
allocate (this%open_u(1, 1, 1), source=1.0_wp)
allocate (this%open_v(1, 1, 1), source=1.0_wp)
! Ice-shelf cavity statics. Unlike the porous arrays (grown at
! configure), these are sized HERE off the `use_cavity` flag that
! `init_from_config` latches before `init` — the draft has to exist
! before `ocean_state_seed_from_cfg` seeds the wet mask and the
! layer thicknesses from `b - z_draft`, which is well before any
! `configure_ocean_*` runs. Knob off ⇒ three `(1,1)` placeholders.
if (this%use_cavity) then
allocate (this%z_draft(nx, ny), source=0.0_wp)
allocate (this%cover_frac(nx, ny), source=0.0_wp)
allocate (this%p_ice_ref(nx, ny), source=0.0_wp)
else
allocate (this%z_draft(1, 1), source=0.0_wp)
allocate (this%cover_frac(1, 1), source=0.0_wp)
allocate (this%p_ice_ref(1, 1), source=0.0_wp)
end if
! Land masks default ALL-WET (1.0): if metrics_apply_land_mask is
! never called (no land), the masks stay inert (×1) and the 6 face
! metrics are never altered — bit-identical to a no-mask build.
allocate (this%wet_T(nx, ny), source=1.0_wp)
allocate (this%wet_u(nx + 1, ny), source=1.0_wp)
allocate (this%wet_v(nx, ny + 1), source=1.0_wp)
allocate (this%wet_q(nx + 1, ny + 1), source=1.0_wp)
! Areas
allocate (this%areaT(nx, ny), source=0.0_wp)
allocate (this%areaCu(nx + 1, ny), source=0.0_wp)
allocate (this%areaCv(nx, ny + 1), source=0.0_wp)
allocate (this%areaBu(nx + 1, ny + 1), source=0.0_wp)
! Inverses
allocate (this%idxT(nx, ny), source=0.0_wp)
allocate (this%idyT(nx, ny), source=0.0_wp)
allocate (this%idxCu(nx + 1, ny), source=0.0_wp)
allocate (this%idyCu(nx + 1, ny), source=0.0_wp)
allocate (this%idxCv(nx, ny + 1), source=0.0_wp)
allocate (this%idyCv(nx, ny + 1), source=0.0_wp)
allocate (this%iareaT(nx, ny), source=0.0_wp)
allocate (this%iareaBu(nx + 1, ny + 1), source=0.0_wp)
allocate (this%iareaCu(nx + 1, ny), source=0.0_wp)
allocate (this%iareaCv(nx, ny + 1), source=0.0_wp)
! Geography
allocate (this%geolatT(nx, ny), source=0.0_wp)
allocate (this%geolonT(nx, ny), source=0.0_wp)
allocate (this%geolatBu(nx + 1, ny + 1), source=0.0_wp)
allocate (this%geolonBu(nx + 1, ny + 1), source=0.0_wp)
allocate (this%angle_dx(nx, ny), source=0.0_wp)
! hvisc ratio bundle
allocate (this%dy_dxT(nx, ny), source=0.0_wp)
allocate (this%dx_dyT(nx, ny), source=0.0_wp)
allocate (this%dy_dxBu(nx + 1, ny + 1), source=0.0_wp)
allocate (this%dx_dyBu(nx + 1, ny + 1), source=0.0_wp)
allocate (this%dx2h(nx, ny), source=0.0_wp)
allocate (this%dy2h(nx, ny), source=0.0_wp)
allocate (this%dx2q(nx + 1, ny + 1), source=0.0_wp)
allocate (this%dy2q(nx + 1, ny + 1), source=0.0_wp)
this%is_init = .true.
end subroutine ocean_metrics_init