boole_dpa_face Subroutine

public pure subroutine boole_dpa_face(eos, rho0, rho_ref, e_top_l, e_top_r, dz_l, dz_r, t_t_l, t_b_l, t_m_l, t_t_r, t_b_r, t_m_r, s_t_l, s_b_l, s_m_l, s_t_r, s_b_r, s_m_r, dpa_l, dpa_r, parabolic, dpa_face)

HORIZONTAL (cross-face) Boole quadrature of the layer pressure increment dpa = g * int rho' dz — the face integral the FV pressure-gradient contour needs (Adcroft, Hallberg & Harrison 2008 §3; Yung, Hallberg, Adcroft & Morrison 2026 §2.4).

WHY A QUADRATURE AND NOT A MEAN. The FV assembly evaluates the top/bottom edges of the control volume as Delta_e * pbar, where pbar is the mean pressure ALONG that edge; pbar is marched down from the surface by adding this routine’s result layer by layer. Delta_e * pbar is the exact int p dz along the edge only when pbar is the true along-face mean. Replacing it with the two-column average 0.5*(dpa_L + dpa_R) is a TRAPEZOID: it is exact only if p is linear in x along the edge. With a tilted interface, z is linear in x but p is QUADRATIC in z under a linear stratification, so the trapezoid leaves a curvature residual g*(-drho/dz)*Delta_e^2/12 at every interface — the terrain-following “pressure gradient error of the second kind” (Haney 1991; Mellor, Ezer & Oey 1994), reported for the sloping ice-shelf surface as the LINEAR pressure reconstruction by Yung et al. (2026) §3.1 and cured there by the same device.

THE FIX. Sample five evenly-spaced sub-columns across the face. At fraction w from the left column, interpolate the interface height, the thickness, and the T/S edge + mean triples linearly, then run the same in-layer vertical Boole quadrature. Both interpolations are in the SAME parameter, so a sub-column’s profile is the true profile at the interpolated depth: for T/S linear in z the sub-column reconstruction is exact and dpa(w) is a quadratic in w, which the 5-point closed Newton-Cotes rule integrates exactly. The whole PGF then vanishes to round-off on a resting linear-EOS/linear-stratification column under ANY layer geometry — the algorithm’s defining property.

THE END POINTS ARE THE COLUMNS’ OWN INTEGRALS. At w = 0 and w = 1 the sub-column IS the left / right column, so its dpa is the one Pass 1 already integrated; the caller passes it in (dpa_l / dpa_r, recovered from the pa stack as MOM6 int_density_dz_generic_plm does) and only the three interior sub-columns are integrated here.

COST: 3 sub-columns x 5 sub-points = 15 EOS evaluations per face per layer (25 before the end points were reused), against 0 for the trapezoid. reconstruct_for_pressure is opt-in and already the expensive branch.

Arguments

Type IntentOptional Attributes Name
type(eos_t), intent(in) :: eos
real(kind=wp), intent(in) :: rho0

Boussinesq reference density used in the pressure estimate.

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

Anomaly reference subtracted from the EOS density.

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

Shallower-interface heights of the layer in the LEFT and RIGHT columns (m, surface-relative, <= 0).

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

Shallower-interface heights of the layer in the LEFT and RIGHT columns (m, surface-relative, <= 0).

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

Layer thicknesses in the left / right columns (m, >= 0).

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

Layer thicknesses in the left / right columns (m, >= 0).

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

Left column temperature: top edge, bottom edge, layer mean.

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

Left column temperature: top edge, bottom edge, layer mean.

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

Left column temperature: top edge, bottom edge, layer mean.

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

Right column temperature triple.

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

Right column temperature triple.

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

Right column temperature triple.

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

Left column salinity triple.

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

Left column salinity triple.

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

Left column salinity triple.

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

Right column salinity triple.

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

Right column salinity triple.

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

Right column salinity triple.

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

The left / right columns’ own g * int rho' dz over the layer (Pa) – the w = 0 / w = 1 end points of the rule.

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

The left / right columns’ own g * int rho' dz over the layer (Pa) – the w = 0 / w = 1 end points of the rule.

logical, intent(in) :: parabolic

.true. -> the sub-column profiles carry the PPM curvature.

real(kind=wp), intent(out) :: dpa_face

Along-face mean of g * int rho' dz over the layer (Pa).


Calls

proc~~boole_dpa_face~~CallsGraph proc~boole_dpa_face boole_dpa_face proc~boole_dpa_intz_layer boole_dpa_intz_layer proc~boole_dpa_face->proc~boole_dpa_intz_layer proc~eos_density_point eos_density_point proc~boole_dpa_intz_layer->proc~eos_density_point proc~roquet_spv_value roquet_spv_value proc~eos_density_point->proc~roquet_spv_value rdb_roq_spv_p rdb_roq_spv_p proc~roquet_spv_value->rdb_roq_spv_p rdb_roq_ts_coeffs rdb_roq_ts_coeffs proc~roquet_spv_value->rdb_roq_ts_coeffs

Called by

proc~~boole_dpa_face~~CalledByGraph proc~boole_dpa_face boole_dpa_face proc~compute_fv_mom6_reconstruct_impl compute_fv_mom6_reconstruct_impl proc~compute_fv_mom6_reconstruct_impl->proc~boole_dpa_face proc~ocean_pressure_force_compute ocean_pressure_force_compute proc~ocean_pressure_force_compute->proc~compute_fv_mom6_reconstruct_impl proc~run_stage run_stage proc~run_stage->proc~ocean_pressure_force_compute proc~run_stage_split run_stage_split proc~run_stage_split->proc~ocean_pressure_force_compute proc~ocean_dyn_step ocean_dyn_step proc~ocean_dyn_step->proc~run_stage proc~ocean_dyn_step_split ocean_dyn_step_split proc~ocean_dyn_step_split->proc~run_stage_split proc~engine_step engine_step proc~engine_step->proc~ocean_dyn_step proc~engine_step->proc~ocean_dyn_step_split

Variables

Type Visibility Attributes Name Initial
real(kind=wp), private, parameter :: BOOLE_W(N_BOOLE) = [7.0_wp, 32.0_wp, 12.0_wp, 32.0_wp, 7.0_wp]
real(kind=wp), private :: acc
real(kind=wp), private :: dpa_m
real(kind=wp), private :: intz_m
integer, private :: m
real(kind=wp), private :: wl
real(kind=wp), private :: wr

Source Code

   pure subroutine boole_dpa_face(eos, rho0, rho_ref, &
                                  e_top_l, e_top_r, dz_l, dz_r, &
                                  t_t_l, t_b_l, t_m_l, t_t_r, t_b_r, t_m_r, &
                                  s_t_l, s_b_l, s_m_l, s_t_r, s_b_r, s_m_r, &
                                  dpa_l, dpa_r, parabolic, dpa_face)
      !$acc routine seq
      !! HORIZONTAL (cross-face) Boole quadrature of the layer pressure
      !! increment `dpa = g * int rho' dz` — the face integral the FV
      !! pressure-gradient contour needs (Adcroft, Hallberg & Harrison
      !! 2008 §3; Yung, Hallberg, Adcroft & Morrison 2026 §2.4).
      !!
      !! WHY A QUADRATURE AND NOT A MEAN.  The FV assembly evaluates the
      !! top/bottom edges of the control volume as `Delta_e * pbar`, where
      !! `pbar` is the mean pressure ALONG that edge; `pbar` is marched
      !! down from the surface by adding this routine's result layer by
      !! layer.  `Delta_e * pbar` is the exact `int p dz` along the edge
      !! only when `pbar` is the true along-face mean.  Replacing it with
      !! the two-column average `0.5*(dpa_L + dpa_R)` is a TRAPEZOID: it is
      !! exact only if `p` is linear in x along the edge.  With a tilted
      !! interface, `z` is linear in x but `p` is QUADRATIC in z under a
      !! linear stratification, so the trapezoid leaves a curvature
      !! residual `g*(-drho/dz)*Delta_e^2/12` at every interface — the
      !! terrain-following "pressure gradient error of the second kind"
      !! (Haney 1991; Mellor, Ezer & Oey 1994), reported for the sloping
      !! ice-shelf surface as the LINEAR pressure reconstruction by Yung
      !! et al. (2026) §3.1 and cured there by the same device.
      !!
      !! THE FIX.  Sample five evenly-spaced sub-columns across the face.
      !! At fraction `w` from the left column, interpolate the interface
      !! height, the thickness, and the T/S edge + mean triples linearly,
      !! then run the same in-layer vertical Boole quadrature.  Both
      !! interpolations are in the SAME parameter, so a sub-column's
      !! profile is the true profile at the interpolated depth: for T/S
      !! linear in z the sub-column reconstruction is exact and `dpa(w)`
      !! is a quadratic in `w`, which the 5-point closed Newton-Cotes rule
      !! integrates exactly.  The whole PGF then vanishes to round-off on
      !! a resting linear-EOS/linear-stratification column under ANY
      !! layer geometry — the algorithm's defining property.
      !!
      !! THE END POINTS ARE THE COLUMNS' OWN INTEGRALS.  At `w = 0` and
      !! `w = 1` the sub-column IS the left / right column, so its `dpa` is
      !! the one Pass 1 already integrated; the caller passes it in
      !! (`dpa_l` / `dpa_r`, recovered from the `pa` stack as MOM6
      !! `int_density_dz_generic_plm` does) and only the three interior
      !! sub-columns are integrated here.
      !!
      !! COST: 3 sub-columns x 5 sub-points = 15 EOS evaluations per face
      !! per layer (25 before the end points were reused), against 0 for the
      !! trapezoid.  `reconstruct_for_pressure` is opt-in and already the
      !! expensive branch.
      type(eos_t), intent(in) :: eos
      real(wp), intent(in)  :: rho0
         !! Boussinesq reference density used in the pressure estimate.
      real(wp), intent(in)  :: rho_ref
         !! Anomaly reference subtracted from the EOS density.
      real(wp), intent(in)  :: e_top_l, e_top_r
         !! Shallower-interface heights of the layer in the LEFT and
         !! RIGHT columns (m, surface-relative, <= 0).
      real(wp), intent(in)  :: dz_l, dz_r
         !! Layer thicknesses in the left / right columns (m, >= 0).
      real(wp), intent(in)  :: t_t_l, t_b_l, t_m_l
         !! Left column temperature: top edge, bottom edge, layer mean.
      real(wp), intent(in)  :: t_t_r, t_b_r, t_m_r
         !! Right column temperature triple.
      real(wp), intent(in)  :: s_t_l, s_b_l, s_m_l
         !! Left column salinity triple.
      real(wp), intent(in)  :: s_t_r, s_b_r, s_m_r
         !! Right column salinity triple.
      real(wp), intent(in)  :: dpa_l, dpa_r
         !! The left / right columns' own `g * int rho' dz` over the layer
         !! (Pa) -- the `w = 0` / `w = 1` end points of the rule.
      logical, intent(in)  :: parabolic
         !! .true. -> the sub-column profiles carry the PPM curvature.
      real(wp), intent(out) :: dpa_face
         !! Along-face mean of `g * int rho' dz` over the layer (Pa).

      real(wp) :: wr, wl, dpa_m, intz_m, acc
      integer  :: m
      real(wp), parameter :: BOOLE_W(N_BOOLE) = &
                             [7.0_wp, 32.0_wp, 12.0_wp, 32.0_wp, 7.0_wp]

      acc = BOOLE_W(1)*dpa_l + BOOLE_W(N_BOOLE)*dpa_r
      do m = 2, N_BOOLE - 1
         wr = 0.25_wp*real(m - 1, wp)   ! 0 at the left column .. 1 at the right
         wl = 1.0_wp - wr
         call boole_dpa_intz_layer(eos, rho0, rho_ref, &
                                   wl*e_top_l + wr*e_top_r, &
                                   wl*dz_l + wr*dz_r, &
                                   wl*t_t_l + wr*t_t_r, &
                                   wl*t_b_l + wr*t_b_r, &
                                   wl*t_m_l + wr*t_m_r, &
                                   wl*s_t_l + wr*s_t_r, &
                                   wl*s_b_l + wr*s_b_r, &
                                   wl*s_m_l + wr*s_m_r, &
                                   parabolic, dpa_m, intz_m)
         acc = acc + BOOLE_W(m)*dpa_m
      end do
      dpa_face = acc/90.0_wp
   end subroutine boole_dpa_face