Thickness-weighted PLM slope — Colella & Woodward (1984) eq (1.7)
with the (1.8) bound, the form MOM6 ships as PLM_slope_cw.
Returns the HALF-jump across the cell (the module’s slope
convention: q_hat(xi) = q + slope*(2*xi - 1)), i.e. half CW84’s
delta a_j. For a profile linear in z the unlimited estimate is
exactly a*h_c at ANY thickness triple, and the bound
2*min(q_c - q_min, q_max - q_c) is then a*min(h_l+h_c, h_c+h_r)
which never bites — so the reconstruction is linear-exact.
This is NOT the shipped formula’s equal-thickness limit, and the
difference is deliberate: on a uniform column (1.7) collapses to the
CENTRED difference 0.5*(dq_l + dq_r) under the (1.8) bound, where
the shipped kernel uses the strictly more diffusive
0.5*minmod(dq_l, dq_r). So switching the knob on changes the PLM
answer even on an unstretched column — it swaps minmod for the CW84
limiter, which is what MOM6 ships as PLM_slope_cw and the only
h-weighted PLM slope that is second-order rather than first-order at
a smooth extremum. (PPM’s path, by contrast, reduces exactly; see
ppm_jump_nonuniform.) Both remain monotone: the (1.8) bound keeps
the reconstructed edges inside the three cell means.
H_DIV_EPS (not H_NEGLECT) armours the denominators: every one of
them is a SUM of thicknesses, already non-negative by the caller’s
precondition, so this is the pure 1/0 role and nothing else.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| real(kind=wp), | intent(in) | :: | h_l |
Thickness of the cell below (toward the bed). |
||
| real(kind=wp), | intent(in) | :: | h_c |
Thickness of the cell being reconstructed. |
||
| real(kind=wp), | intent(in) | :: | h_r |
Thickness of the cell above (toward the surface). |
||
| real(kind=wp), | intent(in) | :: | q_l |
Cell mean below. |
||
| real(kind=wp), | intent(in) | :: | q_c |
Cell mean here. |
||
| real(kind=wp), | intent(in) | :: | q_r |
Cell mean above. |
||
| real(kind=wp), | intent(out) | :: | slope |
Limited half-jump across the cell. |
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| real(kind=wp), | private | :: | q_max | ||||
| real(kind=wp), | private | :: | q_min | ||||
| real(kind=wp), | private | :: | sig_c | ||||
| real(kind=wp), | private | :: | sig_l | ||||
| real(kind=wp), | private | :: | sig_r |
pure subroutine plm_slope_nonuniform(h_l, h_c, h_r, q_l, q_c, q_r, slope) !$acc routine seq !! Thickness-weighted PLM slope — Colella & Woodward (1984) eq (1.7) !! with the (1.8) bound, the form MOM6 ships as `PLM_slope_cw`. !! !! Returns the HALF-jump across the cell (the module's `slope` !! convention: `q_hat(xi) = q + slope*(2*xi - 1)`), i.e. half CW84's !! `delta a_j`. For a profile linear in `z` the unlimited estimate is !! exactly `a*h_c` at ANY thickness triple, and the bound !! `2*min(q_c - q_min, q_max - q_c)` is then `a*min(h_l+h_c, h_c+h_r)` !! which never bites — so the reconstruction is linear-exact. !! !! **This is NOT the shipped formula's equal-thickness limit**, and the !! difference is deliberate: on a uniform column (1.7) collapses to the !! CENTRED difference `0.5*(dq_l + dq_r)` under the (1.8) bound, where !! the shipped kernel uses the strictly more diffusive !! `0.5*minmod(dq_l, dq_r)`. So switching the knob on changes the PLM !! answer even on an unstretched column — it swaps minmod for the CW84 !! limiter, which is what MOM6 ships as `PLM_slope_cw` and the only !! h-weighted PLM slope that is second-order rather than first-order at !! a smooth extremum. (PPM's path, by contrast, reduces exactly; see !! `ppm_jump_nonuniform`.) Both remain monotone: the (1.8) bound keeps !! the reconstructed edges inside the three cell means. !! !! `H_DIV_EPS` (not `H_NEGLECT`) armours the denominators: every one of !! them is a SUM of thicknesses, already non-negative by the caller's !! precondition, so this is the pure 1/0 role and nothing else. real(wp), intent(in) :: h_l !! Thickness of the cell below (toward the bed). real(wp), intent(in) :: h_c !! Thickness of the cell being reconstructed. real(wp), intent(in) :: h_r !! Thickness of the cell above (toward the surface). real(wp), intent(in) :: q_l !! Cell mean below. real(wp), intent(in) :: q_c !! Cell mean here. real(wp), intent(in) :: q_r !! Cell mean above. real(wp), intent(out) :: slope !! Limited half-jump across the cell. real(wp) :: sig_l, sig_r, sig_c, q_min, q_max sig_l = q_c - q_l sig_r = q_r - q_c sig_c = (h_c/(h_l + h_c + h_r + H_DIV_EPS))* & ((2.0_wp*h_l + h_c)/(h_c + h_r + H_DIV_EPS)*sig_r & + (h_c + 2.0_wp*h_r)/(h_l + h_c + H_DIV_EPS)*sig_l) if (sig_l*sig_r > 0.0_wp) then q_min = min(q_l, q_c, q_r) q_max = max(q_l, q_c, q_r) slope = 0.5_wp*sign(min(abs(sig_c), & 2.0_wp*min(q_c - q_min, q_max - q_c)), sig_c) else slope = 0.0_wp end if end subroutine plm_slope_nonuniform