Zonal half of tracer_advect_one_impl. Same three-pass
pattern (PPM reconstruction → upwind pick into tracer mass
flux → forward-Euler update) but only the x-direction half.
Reads the input h for the Tr = hTr/h reconstruction. The
tracer transport mass_flux_x·Tr_face inherits dy_cu, and
the divergence closes with iareaT (= inv_dx on uniform).
Mirror-T at land neighbours (C2): a held land column’s tracer
is reflected to the local cell so the wet-side face value is
unbiased; bit-identical for all-wet (wet_T≡1).
Optional budget_adv: when present, a separate do concurrent
loop accumulates −dt·(Tr_face_left_x(i+1)−Tr_face_left_x(i))·iareaT
into budget_adv (same divergence written to hTr). The
prognostic hTr update loop is UNCHANGED — byte-identical when
budget_adv is absent. Used by the console salt/heat closure.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| integer, | intent(in) | :: | nx | |||
| integer, | intent(in) | :: | ny | |||
| integer, | intent(in) | :: | nz | |||
| real(kind=wp), | intent(in) | :: | dt | |||
| real(kind=wp), | intent(in) | :: | iareaT(nx,ny) | |||
| real(kind=wp), | intent(in) | :: | wet_T(nx,ny) | |||
| real(kind=wp), | intent(in) | :: | h(nx,ny,nz) | |||
| real(kind=wp), | intent(inout) | :: | hTr(nx,ny,nz) | |||
| real(kind=wp), | intent(in) | :: | mass_flux_x(nx+1,ny,nz) | |||
| real(kind=wp), | intent(inout) | :: | Tr_face_left_x(nx+1,ny,nz) | |||
| real(kind=wp), | intent(inout) | :: | Tr_face_right_x(nx+1,ny,nz) | |||
| real(kind=wp), | intent(inout), | optional | :: | budget_adv(nx,ny,nz) |
Per-cell accumulator for the horizontal-advection budget (same sign/units as hTr). When present, the zonal-flux divergence is added (+=) here after the prognostic update. Absent ⇒ inert (byte-identical to the pre-feature build). |
|
| real(kind=wp), | intent(in), | optional | :: | budget_w |
Bookkeeping weight on the |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | private | :: | Tr_0 | ||||
| real(kind=wp), | private | :: | Tr_face | ||||
| real(kind=wp), | private | :: | Tr_left | ||||
| real(kind=wp), | private | :: | Tr_m1 | ||||
| real(kind=wp), | private | :: | Tr_m2 | ||||
| real(kind=wp), | private | :: | Tr_p1 | ||||
| real(kind=wp), | private | :: | Tr_p2 | ||||
| real(kind=wp), | private | :: | Tr_right | ||||
| real(kind=wp), | private | :: | dh_0 | ||||
| real(kind=wp), | private | :: | dh_m1 | ||||
| real(kind=wp), | private | :: | dh_p1 | ||||
| real(kind=wp), | private | :: | dt_b | ||||
| integer, | private | :: | i | ||||
| integer, | private | :: | j | ||||
| integer, | private | :: | k | ||||
| real(kind=wp), | private | :: | mass_x |
pure subroutine tracer_advect_zonal_one_impl(nx, ny, nz, dt, iareaT, wet_T, h, hTr, & mass_flux_x, & Tr_face_left_x, Tr_face_right_x, & budget_adv, budget_w) !! Zonal half of `tracer_advect_one_impl`. Same three-pass !! pattern (PPM reconstruction → upwind pick into tracer mass !! flux → forward-Euler update) but only the x-direction half. !! Reads the input `h` for the Tr = hTr/h reconstruction. The !! tracer transport `mass_flux_x·Tr_face` inherits `dy_cu`, and !! the divergence closes with `iareaT` (= `inv_dx` on uniform). !! Mirror-T at land neighbours (C2): a held land column's tracer !! is reflected to the local cell so the wet-side face value is !! unbiased; bit-identical for all-wet (`wet_T≡1`). !! !! Optional `budget_adv`: when present, a separate `do concurrent` !! loop accumulates `−dt·(Tr_face_left_x(i+1)−Tr_face_left_x(i))·iareaT` !! into `budget_adv` (same divergence written to `hTr`). The !! prognostic hTr update loop is UNCHANGED — byte-identical when !! `budget_adv` is absent. Used by the console salt/heat closure. integer, intent(in) :: nx, ny, nz real(wp), intent(in) :: dt real(wp), intent(in) :: iareaT(nx, ny) real(wp), intent(in) :: wet_T(nx, ny) real(wp), intent(in) :: h(nx, ny, nz) real(wp), intent(inout) :: hTr(nx, ny, nz) real(wp), intent(in) :: mass_flux_x(nx + 1, ny, nz) real(wp), intent(inout) :: Tr_face_left_x(nx + 1, ny, nz) real(wp), intent(inout) :: Tr_face_right_x(nx + 1, ny, nz) real(wp), intent(inout), optional :: budget_adv(nx, ny, nz) !! Per-cell accumulator for the horizontal-advection budget !! (same sign/units as hTr). When present, the zonal-flux !! divergence is added (+=) here after the prognostic update. !! Absent ⇒ inert (byte-identical to the pre-feature build). real(wp), intent(in), optional :: budget_w !! Bookkeeping weight on the `budget_adv` increment (absent ⇒ 1, !! bit-identical). A caller writing OUTSIDE an RK2 stage (the GM !! operator, after the stage average) passes the reciprocal of the !! console's per-step weight, `1/ocean_budget_stage_weight`. integer :: i, j, k real(wp) :: Tr_m2, Tr_m1, Tr_0, Tr_p1, Tr_p2 real(wp) :: dh_m1, dh_0, dh_p1, Tr_left, Tr_right real(wp) :: mass_x, Tr_face, dt_b do concurrent(k=1:nz, j=1:ny, i=3:nx - 2) & local(Tr_m2, Tr_m1, Tr_0, Tr_p1, Tr_p2, & dh_m1, dh_0, dh_p1, Tr_left, Tr_right) Tr_0 = hTr(i, j, k)/h(i, j, k) Tr_m1 = ppm_mirror_h(hTr(i - 1, j, k)/h(i - 1, j, k), Tr_0, wet_T(i - 1, j)) Tr_p1 = ppm_mirror_h(hTr(i + 1, j, k)/h(i + 1, j, k), Tr_0, wet_T(i + 1, j)) Tr_m2 = ppm_mirror_h(hTr(i - 2, j, k)/h(i - 2, j, k), Tr_m1, wet_T(i - 2, j)) Tr_p2 = ppm_mirror_h(hTr(i + 2, j, k)/h(i + 2, j, k), Tr_p1, wet_T(i + 2, j)) call ppm_limited_slope(Tr_m2, Tr_m1, Tr_0, dh_m1) call ppm_limited_slope(Tr_m1, Tr_0, Tr_p1, dh_0) call ppm_limited_slope(Tr_0, Tr_p1, Tr_p2, dh_p1) dh_0 = dh_0*wet_T(i - 1, j)*wet_T(i, j)*wet_T(i + 1, j) Tr_left = 0.5_wp*(Tr_m1 + Tr_0) - (dh_0 - dh_m1)/6.0_wp Tr_right = 0.5_wp*(Tr_0 + Tr_p1) - (dh_p1 - dh_0)/6.0_wp call ppm_cell_limiter(Tr_0, Tr_left, Tr_right) Tr_face_right_x(i, j, k) = Tr_left Tr_face_left_x(i + 1, j, k) = Tr_right end do do concurrent(k=1:nz, j=1:ny) Tr_face_left_x(1, j, k) = hTr(1, j, k)/h(1, j, k) Tr_face_right_x(1, j, k) = hTr(1, j, k)/h(1, j, k) Tr_face_left_x(2, j, k) = hTr(1, j, k)/h(1, j, k) Tr_face_right_x(2, j, k) = hTr(2, j, k)/h(2, j, k) Tr_face_left_x(3, j, k) = hTr(2, j, k)/h(2, j, k) Tr_face_right_x(3, j, k) = hTr(2, j, k)/h(2, j, k) Tr_face_right_x(nx - 1, j, k) = hTr(nx - 1, j, k)/h(nx - 1, j, k) Tr_face_left_x(nx, j, k) = hTr(nx - 1, j, k)/h(nx - 1, j, k) Tr_face_right_x(nx, j, k) = hTr(nx, j, k)/h(nx, j, k) Tr_face_left_x(nx + 1, j, k) = hTr(nx, j, k)/h(nx, j, k) Tr_face_right_x(nx + 1, j, k) = hTr(nx, j, k)/h(nx, j, k) end do do concurrent(k=1:nz, j=1:ny, i=1:nx + 1) local(mass_x, Tr_face) mass_x = mass_flux_x(i, j, k) if (mass_x >= 0.0_wp) then Tr_face = Tr_face_left_x(i, j, k) else Tr_face = Tr_face_right_x(i, j, k) end if Tr_face_left_x(i, j, k) = mass_x*Tr_face end do do concurrent(k=1:nz, j=1:ny, i=1:nx) hTr(i, j, k) = hTr(i, j, k) - dt* & (Tr_face_left_x(i + 1, j, k) - Tr_face_left_x(i, j, k))*iareaT(i, j) end do ! Budget accumulator: separate guarded loop so the prognostic update above ! is UNCHANGED (byte-identical when budget_adv is absent). Mirrors the ! same divergence that was just applied to hTr — the console uses it to ! close the salt/heat conservation residual. if (present(budget_adv)) then dt_b = dt if (present(budget_w)) dt_b = dt*budget_w do concurrent(k=1:nz, j=1:ny, i=1:nx) budget_adv(i, j, k) = budget_adv(i, j, k) - dt_b* & (Tr_face_left_x(i + 1, j, k) - Tr_face_left_x(i, j, k))*iareaT(i, j) end do end if end subroutine tracer_advect_zonal_one_impl