Flat-impl tracer remap. Per (i,j) column: c = hTr/h (vanishing-layer-
guarded) → per-column remap kernel → hTr_new = c_new·h_new. Conservative.
budget (optional): when present, the per-cell hTr_new−hTr_old increment
is accumulated into the slot (heat/salt remap deltas) before overwriting.
Flat-arg so GPU codegen doesn’t chase the array-of-derived-types pointer.
The vanishing-layer guard is two-sided (rdb_vl_merge_content,
invariant I1′): on the READ side a SOURCE filler is pooled with its
donor live layer, and the reconstruction is handed the donor’s
concentration for it (rdb_vl_column_conc) — never one recovered
from a near-zero divisor; on the WRITE side a TARGET filler is pooled
with ITS donor live target layer, so it receives h_new·c_live,
taken from that layer. Without the write half the remap parks
arbitrary content in a filler (the day-16 z_fixed salt/heat break,
when the read side then zeroed it); with a write half that ZEROES the
filler (the previous I1), a uniform tracer stops being uniform — the
thickness the continuity step later moves out of the filler arrives
in the live layers carrying no content.
Post-condition (I1′): h_new <= H_VANISHED ⇒ hTr = h_new·c_live,
column sum conserved to round-off, so budget still telescopes per
column.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | nx | |||
| integer, | intent(in) | :: | ny | |||
| integer, | intent(in) | :: | nz | |||
| real(kind=wp), | intent(in) | :: | h_old(nx,ny,nz) | |||
| real(kind=wp), | intent(in) | :: | h_new(nx,ny,nz) | |||
| real(kind=wp), | intent(inout) | :: | hTr(nx,ny,nz) | |||
| integer, | intent(in) | :: | method | |||
| logical, | intent(in) | :: | bnd_extrap |
Linear-exact boundary-cell reconstruction in the column kernel
( |
||
| logical, | intent(in) | :: | nonunif |
Non-uniform-grid PLM/PPM reconstruction weights in the column
kernel ( |
||
| real(kind=wp), | intent(inout), | optional | :: | budget(nx,ny,nz) |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | private | :: | c_new_col(NZ_STACK_MAX) | ||||
| real(kind=wp), | private | :: | c_old_col(NZ_STACK_MAX) | ||||
| real(kind=wp), | private | :: | hTr_col(NZ_STACK_MAX) | ||||
| real(kind=wp), | private | :: | hTr_new_col(NZ_STACK_MAX) | ||||
| real(kind=wp), | private | :: | h_new_col(NZ_STACK_MAX) | ||||
| real(kind=wp), | private | :: | h_old_col(NZ_STACK_MAX) | ||||
| integer, | private | :: | i | ||||
| integer, | private | :: | j | ||||
| integer, | private | :: | k |
pure subroutine ocean_remap_tracer_field(nx, ny, nz, h_old, h_new, hTr, method, & bnd_extrap, nonunif, budget) !! Flat-impl tracer remap. Per (i,j) column: c = hTr/h (vanishing-layer- !! guarded) → per-column remap kernel → hTr_new = c_new·h_new. Conservative. !! `budget` (optional): when present, the per-cell hTr_new−hTr_old increment !! is accumulated into the slot (heat/salt remap deltas) before overwriting. !! Flat-arg so GPU codegen doesn't chase the array-of-derived-types pointer. !! !! **The vanishing-layer guard is two-sided** (`rdb_vl_merge_content`, !! invariant I1′): on the READ side a SOURCE filler is pooled with its !! donor live layer, and the reconstruction is handed the donor's !! concentration for it (`rdb_vl_column_conc`) — never one recovered !! from a near-zero divisor; on the WRITE side a TARGET filler is pooled !! with ITS donor live target layer, so it receives `h_new·c_live`, !! taken from that layer. Without the write half the remap parks !! arbitrary content in a filler (the day-16 z_fixed salt/heat break, !! when the read side then zeroed it); with a write half that ZEROES the !! filler (the previous I1), a uniform tracer stops being uniform — the !! thickness the continuity step later moves out of the filler arrives !! in the live layers carrying no content. !! Post-condition (I1′): `h_new <= H_VANISHED ⇒ hTr = h_new·c_live`, !! column sum conserved to round-off, so `budget` still telescopes per !! column. integer, intent(in) :: nx, ny, nz, method real(wp), intent(in) :: h_old(nx, ny, nz) real(wp), intent(in) :: h_new(nx, ny, nz) real(wp), intent(inout) :: hTr(nx, ny, nz) logical, intent(in) :: bnd_extrap !! Linear-exact boundary-cell reconstruction in the column kernel !! (`&vcoord_nml remap_boundary_extrap`); `.false.` ⇒ the PCM !! flatten, i.e. bit-identical to the pre-knob behaviour. logical, intent(in) :: nonunif !! Non-uniform-grid PLM/PPM reconstruction weights in the column !! kernel (`&vcoord_nml remap_nonuniform_weights`); `.false.` ⇒ the !! equal-thickness specialisations, i.e. bit-identical. real(wp), intent(inout), optional :: budget(nx, ny, nz) integer :: i, j, k real(wp) :: h_old_col(NZ_STACK_MAX), h_new_col(NZ_STACK_MAX) real(wp) :: c_old_col(NZ_STACK_MAX), c_new_col(NZ_STACK_MAX) real(wp) :: hTr_col(NZ_STACK_MAX), hTr_new_col(NZ_STACK_MAX) ! Gate the budget write INSIDE the one loop (as vdiff does); splitting ! present(budget) into two loops makes NVHPC compile the no-budget branch ! ~25x slower. Bit-identical to the split form. do concurrent(j=1:ny, i=1:nx) & local(k, h_old_col, h_new_col, c_old_col, c_new_col, hTr_col, hTr_new_col) do k = 1, nz h_old_col(k) = h_old(i, j, k) h_new_col(k) = h_new(i, j, k) hTr_col(k) = hTr(i, j, k) end do ! Read side. `hTr_col` is a LOCAL copy — the budget below still ! differences against the original `hTr` array, so the merge is ! invisible to the leak detector. call rdb_vl_merge_content(nz, h_old_col, hTr_col) call rdb_vl_column_conc(nz, h_old_col, hTr_col, c_old_col) call remap_column(method, nz, & h_old_col(1:nz), h_new_col(1:nz), & c_old_col(1:nz), c_new_col(1:nz), bnd_extrap, nonunif) ! Write side. do k = 1, nz hTr_new_col(k) = c_new_col(k)*h_new_col(k) end do call remap_fold_filler_defect(nz, h_old_col, h_new_col, hTr_col, hTr_new_col) call rdb_vl_merge_content(nz, h_new_col, hTr_new_col) if (present(budget)) then do k = 1, nz budget(i, j, k) = budget(i, j, k) + (hTr_new_col(k) - hTr(i, j, k)) hTr(i, j, k) = hTr_new_col(k) end do else do k = 1, nz hTr(i, j, k) = hTr_new_col(k) end do end if end do end subroutine ocean_remap_tracer_field