remap_column_preconditions_ok Function

public pure function remap_column_preconditions_ok(nz, dz_old, dz_new, rel_tol) result(ok)

Precondition test for one remap column, as a pure predicate so the caller decides what to do about a violation (audit findings V5, V6).

The overlap sweep every method shares assumes BOTH of

  • dz_old(k) >= 0 and dz_new(k) >= 0 — a negative source thickness makes the cumulative interface stack z_old NON-MONOTONE, and the sweep then integrates the reversed interval twice, CREATING mass with no NaN and no bounds hit;
  • sum(dz_old) == sum(dz_new) — the sweep integrates the reconstruction only over the overlap of the two stacks, so a SHORT target silently deletes the non-overlapping tail and a LONG target integrates it as q = 0, diluting the outermost target cell.

Neither is checked inside the kernels: they are caller obligations, and a kernel-side check would cost a branch per column in the hot loop for a condition that must never hold. This predicate exists so a cadence-bounded caller can assert them and fail loud.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: nz
real(kind=wp), intent(in) :: dz_old(nz)

Source-column thicknesses.

real(kind=wp), intent(in) :: dz_new(nz)

Target-column thicknesses.

real(kind=wp), intent(in) :: rel_tol

Relative tolerance on the column-total match, applied against the larger of the two totals (so a land column of zero total passes trivially).

Return Value logical

.true. when both preconditions hold.


Variables

Type Visibility Attributes Name Initial
integer, private :: k
real(kind=wp), private :: s_new
real(kind=wp), private :: s_old

Source Code

   pure function remap_column_preconditions_ok(nz, dz_old, dz_new, rel_tol) result(ok)
      !$acc routine seq
      !! Precondition test for one remap column, as a `pure` predicate so the
      !! caller decides what to do about a violation (audit findings V5, V6).
      !!
      !! The overlap sweep every method shares assumes BOTH of
      !!
      !!   * `dz_old(k) >= 0` and `dz_new(k) >= 0` — a negative source
      !!     thickness makes the cumulative interface stack `z_old`
      !!     NON-MONOTONE, and the sweep then integrates the reversed
      !!     interval twice, CREATING mass with no NaN and no bounds hit;
      !!   * `sum(dz_old) == sum(dz_new)` — the sweep integrates the
      !!     reconstruction only over the overlap of the two stacks, so a
      !!     SHORT target silently deletes the non-overlapping tail and a
      !!     LONG target integrates it as `q = 0`, diluting the outermost
      !!     target cell.
      !!
      !! Neither is checked inside the kernels: they are caller obligations,
      !! and a kernel-side check would cost a branch per column in the hot
      !! loop for a condition that must never hold.  This predicate exists so
      !! a cadence-bounded caller can assert them and fail loud.
      integer, intent(in) :: nz
      real(wp), intent(in) :: dz_old(nz)
         !! Source-column thicknesses.
      real(wp), intent(in) :: dz_new(nz)
         !! Target-column thicknesses.
      real(wp), intent(in) :: rel_tol
         !! Relative tolerance on the column-total match, applied against
         !! the larger of the two totals (so a land column of zero total
         !! passes trivially).
      logical :: ok
         !! `.true.` when both preconditions hold.

      real(wp) :: s_old, s_new
      integer :: k

      ok = .true.
      s_old = 0.0_wp
      s_new = 0.0_wp
      do k = 1, nz
         if (dz_old(k) < 0.0_wp) ok = .false.
         if (dz_new(k) < 0.0_wp) ok = .false.
         s_old = s_old + dz_old(k)
         s_new = s_new + dz_new(k)
      end do
      if (abs(s_new - s_old) > rel_tol*max(abs(s_old), abs(s_new))) ok = .false.
   end function remap_column_preconditions_ok