On a column that carries a vanished layer (source or target), make
the tracer remap conservative EXACTLY, not just on a matched column:
the content the remap failed to place, Σ q_src − Σ q_new, is
handed to the topmost live target layer (the donor of any fillers
above it, so the write-side pool that follows shares it with them).
Why it is needed. remap_column conserves only when Σ h_old ==
Σ h_new, and the target builders reach that sum by a different
arithmetic route than continuity does, so they differ by a few ulp
every step (OCEAN_REMAP_PRECOND_RTOL). The unmatched sliver is at
the TOP of the column, and it goes with its content. Under I1 that
sliver was an empty filler and the loss was invisible; under I1′ the
filler carries c_live, and on a quasi-steady column the sliver has
the same sign every step: cavity_flat_lid_rest_zfixed.nml AT REST
lost salt at -1.2e-13/day (the whole run’s residual: -3.2e-12 at
day 30, against -1.4e-13 under I1) until this fold.
Bit-identity: a column with no layer at or below H_VANISHED
on either grid returns before touching anything, so every family
without fillers (sigma, z*-lite, eulerian_z) is unchanged. A
column with no live target layer is left to the pool (land: zero).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | nz | |||
| real(kind=wp), | intent(in) | :: | h_old_col(NZ_STACK_MAX) | |||
| real(kind=wp), | intent(in) | :: | h_new_col(NZ_STACK_MAX) | |||
| real(kind=wp), | intent(in) | :: | q_src(NZ_STACK_MAX) |
Source content the remap was handed (after the read-side pool). |
||
| real(kind=wp), | intent(inout) | :: | q_new(NZ_STACK_MAX) |
Target content |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | private | :: | defect | ||||
| logical, | private | :: | has_filler | ||||
| integer, | private | :: | k | ||||
| integer, | private | :: | k_top_live |
pure subroutine remap_fold_filler_defect(nz, h_old_col, h_new_col, q_src, q_new) !$acc routine seq !! On a column that carries a vanished layer (source or target), make !! the tracer remap conservative EXACTLY, not just on a matched column: !! the content the remap failed to place, `Σ q_src − Σ q_new`, is !! handed to the topmost live target layer (the donor of any fillers !! above it, so the write-side pool that follows shares it with them). !! !! Why it is needed. `remap_column` conserves only when `Σ h_old == !! Σ h_new`, and the target builders reach that sum by a different !! arithmetic route than continuity does, so they differ by a few ulp !! every step (`OCEAN_REMAP_PRECOND_RTOL`). The unmatched sliver is at !! the TOP of the column, and it goes with its content. Under I1 that !! sliver was an empty filler and the loss was invisible; under I1′ the !! filler carries `c_live`, and on a quasi-steady column the sliver has !! the same sign every step: `cavity_flat_lid_rest_zfixed.nml` AT REST !! lost salt at `-1.2e-13`/day (the whole run's residual: `-3.2e-12` at !! day 30, against `-1.4e-13` under I1) until this fold. !! !! **Bit-identity:** a column with no layer at or below `H_VANISHED` !! on either grid returns before touching anything, so every family !! without fillers (sigma, z*-lite, `eulerian_z`) is unchanged. A !! column with no live target layer is left to the pool (land: zero). integer, intent(in) :: nz real(wp), intent(in) :: h_old_col(NZ_STACK_MAX) real(wp), intent(in) :: h_new_col(NZ_STACK_MAX) real(wp), intent(in) :: q_src(NZ_STACK_MAX) !! Source content the remap was handed (after the read-side pool). real(wp), intent(inout) :: q_new(NZ_STACK_MAX) !! Target content `c_new·h_new`, before the write-side pool. integer :: k, k_top_live logical :: has_filler real(wp) :: defect has_filler = .false. k_top_live = 0 do k = 1, nz if (.not. rdb_vl_is_live(h_old_col(k)) .or. .not. rdb_vl_is_live(h_new_col(k))) then has_filler = .true. end if if (rdb_vl_is_live(h_new_col(k))) k_top_live = k end do if (.not. has_filler .or. k_top_live == 0) return defect = 0.0_wp do k = 1, nz defect = defect + (q_src(k) - q_new(k)) end do q_new(k_top_live) = q_new(k_top_live) + defect end subroutine remap_fold_filler_defect