subroutine configure_ocean_wetdry(cfg, ocean_state, grid, compute_rank)
!! Dynamic wetting/drying (docs/ocean_wetdry_plan.md): copy the
!! `&ocean_wetdry_nml` knobs onto the BT workstate, allocate the
!! wd_* workspaces (lazy — absent when the knob is off, so the
!! default path carries no new arrays and stays byte-identical),
!! and seed the hysteresis wet mask from the seeded bathymetry
!! (`barotropic%b` — the same array `configure_ocean_bt_split`
!! later latches into `bt_H_ref`; `bt_eta` is still 0 here).
!! Must run BEFORE the restart read (so `wd_wet_dyn` is allocated
!! + registered when the registry walk runs and a warm restart
!! overwrites the seed with the saved front state) and BEFORE
!! `ocean_state_enter_data` (host seeding; the enter_data walk
!! attaches whatever is allocated).
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
integer :: nx_w, ny_w, i, j, ng, n_intertidal
real(wp) :: d0
ocean_state%dyn%bt_work%wetdry_enable = cfg%ocean%wetdry%enable
if (.not. cfg%ocean%wetdry%enable) return
ocean_state%dyn%bt_work%wd_dry_depth = cfg%ocean%wetdry%dry_depth
ocean_state%dyn%bt_work%wd_rewet_depth = cfg%ocean%wetdry%rewet_depth
nx_w = grid%nx_total
ny_w = grid%ny_total
associate (bt_work => ocean_state%dyn%bt_work)
allocate (bt_work%wd_wet_dyn(nx_w, ny_w), source=1.0_wp)
allocate (bt_work%wd_theta(nx_w, ny_w), source=1.0_wp)
allocate (bt_work%wd_flux_x(nx_w + 1, ny_w), source=0.0_wp)
allocate (bt_work%wd_flux_y(nx_w, ny_w + 1), source=0.0_wp)
allocate (bt_work%wd_open_u(nx_w + 1, ny_w), source=1.0_wp)
allocate (bt_work%wd_open_v(nx_w, ny_w + 1), source=1.0_wp)
! Seed the hysteresis mask from the seeded bathymetry (host
! loop, pre-enter_data; eta = 0 at configure so D0 = b). Cells
! inside the hysteresis band seed WET — the first substep's own
! update settles them. A restart carrying `wd_wet_dyn`
! overwrites this seed (this configure runs BEFORE the registry
! read — see the driver ordering comment).
do j = 1, ny_w
do i = 1, nx_w
d0 = ocean_state%barotropic%b(i, j)
! `d0 < dry_depth` is the live criterion. The `wet_mask<=0`
! term is belt-and-suspenders: with the knob ON a static-land
! column has `b < -land_margin < 0 < dry_depth`, so the first
! test already fires — it only guards against a caller seeding
! wet_mask with a stricter cutoff than the depth test implies.
if (d0 < cfg%ocean%wetdry%dry_depth .or. &
ocean_state%multilayer%wet_mask(i, j) <= 0.0_wp) then
bt_work%wd_wet_dyn(i, j) = 0.0_wp
end if
end do
end do
end associate
if (compute_rank == 0) then
call logger%info("Wet/dry: ON dry_depth = "// &
to_string(cfg%ocean%wetdry%dry_depth)// &
" m, rewet_depth = "// &
to_string(cfg%ocean%wetdry%rewet_depth)// &
" m (upwind BT faces + positive-definite "// &
"outflow limiter + bed-blocking gate)")
! v2 semantic trap (plan §10.1): with wet/dry ON the static land
! seed cutoff moves to `b < -land_margin`, so any column whose bed
! is in the band `[-land_margin, LAND_DEPTH_THRESHOLD)` is NOT
! static land any more — it becomes a dynamically-dry INTERTIDAL
! flat that floods once eta clears dry_depth. This affects formula
! topos that flag land as b ≈ 0 (island / double_drake), file
! bathymetry with a b = 1 m land-flag convention, AND generated
! topos like neverworld2 that force continents into the band — so
! a name-based check is incomplete. Scan the seeded interior
! bathymetry directly and warn (rank 0) with the exact count when
! ANY intertidal-band cell exists. It's a WARNING, not an abort:
! intertidal flats can be intentional; the message just makes the
! regime change loud and specific.
ng = grid%nghost
n_intertidal = 0
do j = ng + 1, ny_w - ng
do i = ng + 1, nx_w - ng
d0 = ocean_state%barotropic%b(i, j)
if (d0 >= -cfg%ocean%wetdry%land_margin .and. &
d0 < LAND_DEPTH_THRESHOLD) then
n_intertidal = n_intertidal + 1
end if
end do
end do
if (n_intertidal > 0) then
call logger%warning("Wet/dry: "//to_string(n_intertidal)// &
" interior cell(s) have bed elevations in the "// &
"intertidal band [-land_margin, "// &
"LAND_DEPTH_THRESHOLD) — under the wetdry-aware "// &
"static seed (land iff b < -land_margin) these "// &
"are LIVE intertidal flats that flood when eta "// &
"clears dry_depth, NOT static land. If you "// &
"intended static continents, use bed elevations "// &
"b < -land_margin (= "// &
to_string(-cfg%ocean%wetdry%land_margin)//" m).")
end if
end if
end subroutine configure_ocean_wetdry