Whole-array min / max / sum of a diagnostic buffer in ONE pass, over the FINITE cells only.
Replaces three separate host passes (minval/maxval/sum) with
a single do concurrent ... reduce, so on a GPU build this runs
where the buffer already lives and only a few scalars come back.
Explicit-shape dummies (never assumed-shape) so NVHPC does not walk
a descriptor per launch; index order is (k, j, i) with the
contiguous index last.
Missing data. A diagnostic buffer legitimately carries IEEE
NaN as the “no water here” sentinel — fill_tracer_impl writes it
into every land column and every dynamically vanished layer (see
its docstring, and test_fill_vanished_nan). Those cells are not
data and must not enter the statistics, so every cell is
ieee_is_finite-guarded and n_valid counts the cells that did
contribute — the caller divides the sum by THAT, not by the array
size. Relying on “comparisons with NaN are false” to make
min/max skip them is not enough and not portable: it leaves
sum poisoned (the whole field’s mean becomes NaN next to a
perfectly finite min/max) and nvfortran’s relaxed-FP default is
free to lower an unguarded min/max to a NaN-blind select.
For an all-finite buffer the guard is always taken, so the
reduction order — and therefore the result — is bit-identical to
the unguarded form. n_valid == 0 (nothing finite anywhere)
leaves the +huge / -huge / 0 seeds untouched; the caller
substitutes the missing-value sentinel.
The caller MUST only invoke this when the buffer is device-resident
on a GPU build — see ocean_diag_t%on_device.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | buf(n1,n2,n3) | |||
| integer, | intent(in) | :: | n1 | |||
| integer, | intent(in) | :: | n2 | |||
| integer, | intent(in) | :: | n3 | |||
| real(kind=wp), | intent(out) | :: | vmin | |||
| real(kind=wp), | intent(out) | :: | vmax | |||
| real(kind=wp), | intent(out) | :: | vsum | |||
| integer, | intent(out), | optional | :: | n_valid |
Number of finite cells folded in. Optional so the pre-existing three-scalar call sites keep working unchanged. |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| integer, | private | :: | i | ||||
| integer, | private | :: | j | ||||
| integer, | private | :: | k | ||||
| integer, | private | :: | nv |
pure subroutine diag_reduce_stats(buf, n1, n2, n3, vmin, vmax, vsum, n_valid) !! Whole-array min / max / sum of a diagnostic buffer in ONE pass, !! over the FINITE cells only. !! !! Replaces three separate host passes (`minval`/`maxval`/`sum`) with !! a single `do concurrent ... reduce`, so on a GPU build this runs !! where the buffer already lives and only a few scalars come back. !! Explicit-shape dummies (never assumed-shape) so NVHPC does not walk !! a descriptor per launch; index order is `(k, j, i)` with the !! contiguous index last. !! !! **Missing data.** A diagnostic buffer legitimately carries IEEE !! NaN as the "no water here" sentinel — `fill_tracer_impl` writes it !! into every land column and every dynamically vanished layer (see !! its docstring, and `test_fill_vanished_nan`). Those cells are not !! data and must not enter the statistics, so every cell is !! `ieee_is_finite`-guarded and `n_valid` counts the cells that did !! contribute — the caller divides the sum by THAT, not by the array !! size. Relying on "comparisons with NaN are false" to make !! `min`/`max` skip them is not enough and not portable: it leaves !! `sum` poisoned (the whole field's mean becomes NaN next to a !! perfectly finite min/max) and nvfortran's relaxed-FP default is !! free to lower an unguarded `min`/`max` to a NaN-blind select. !! !! For an all-finite buffer the guard is always taken, so the !! reduction order — and therefore the result — is bit-identical to !! the unguarded form. `n_valid == 0` (nothing finite anywhere) !! leaves the `+huge` / `-huge` / `0` seeds untouched; the caller !! substitutes the missing-value sentinel. !! !! The caller MUST only invoke this when the buffer is device-resident !! on a GPU build — see `ocean_diag_t%on_device`. integer, intent(in) :: n1, n2, n3 real(wp), intent(in) :: buf(n1, n2, n3) real(wp), intent(out) :: vmin, vmax, vsum integer, intent(out), optional :: n_valid !! Number of finite cells folded in. Optional so the pre-existing !! three-scalar call sites keep working unchanged. integer :: i, j, k, nv vmin = huge(1.0_wp) vmax = -huge(1.0_wp) vsum = 0.0_wp nv = 0 do concurrent(k=1:n3, j=1:n2, i=1:n1) reduce(min:vmin) reduce(max:vmax) & reduce(+:vsum) reduce(+:nv) if (ieee_is_finite(buf(i, j, k))) then vmin = min(vmin, buf(i, j, k)) vmax = max(vmax, buf(i, j, k)) vsum = vsum + buf(i, j, k) nv = nv + 1 end if end do if (present(n_valid)) n_valid = nv end subroutine diag_reduce_stats