mp5.f90 Source File


Source Code

!> @file mp5.f90
!> @brief MP5 (Monotonicity-Preserving 5th-order) reconstruction (Suresh & Huynh 1997).
!!
!! Applies a 5th-order unlimited reconstruction and clips it to a
!! monotonicity-preserving bound computed from the local curvature.  Achieves
!! 5th-order accuracy in smooth regions while suppressing oscillations near
!! discontinuities.
!!
!! References:
!!   [1] A. Suresh and H. T. Huynh, "Accurate Monotonicity-Preserving Schemes
!!       with Runge-Kutta Time Stepping," J. Comput. Phys., 136:83-99, 1997.

module mp5
  use precision, only: wp
  use solver_state, only: neq
  implicit none
  private

  !> MP5 monotonicity parameter α.  Used in the upper-bound estimate.
  !! α = 4 following Suresh & Huynh (1997), Eq. (2.27).
  real(wp), parameter :: alpha_mp5 = 4.0_wp

  !> MP5 curvature-limiter coefficient β_L.
  !! β_L = 4/3 following Suresh & Huynh (1997), Eq. (2.29).
  real(wp), parameter :: beta_mp5 = 4.0_wp / 3.0_wp

  public :: mp5_reconstruct

contains

  ! ---------------------------------------------------------------------------
  !> Two-argument minmod function.
  !! Returns the value with the smaller magnitude if a and b have the same sign,
  !! else 0.
  ! ---------------------------------------------------------------------------
  pure elemental function minmod2(a, b) result(m)
    real(wp), intent(in) :: a, b
    real(wp) :: m
    if (a * b <= 0.0_wp) then
      m = 0.0_wp
    else if (abs(a) <= abs(b)) then
      m = a
    else
      m = b
    end if
  end function minmod2

  ! ---------------------------------------------------------------------------
  !> Four-argument minmod function.
  !! Returns 0 if the arguments do not all share the same sign; otherwise
  !! returns the value with the smallest magnitude.
  ! ---------------------------------------------------------------------------
  pure elemental function minmod4(a, b, c, d) result(m)
    real(wp), intent(in) :: a, b, c, d
    real(wp) :: m
    integer :: s

    ! Derive an integer sign token via sign() to preserve IEEE-754 signed-zero
    ! semantics (sign(1.0,-0.0) == -1) without real-equality comparisons.
    s = int(sign(1.0_wp, a))
    if (s /= int(sign(1.0_wp, b)) .or. s /= int(sign(1.0_wp, c)) .or. &
        s /= int(sign(1.0_wp, d))) then
      m = 0.0_wp
    else
      m = real(s, wp) * min(abs(a), abs(b), abs(c), abs(d))
    end if
  end function minmod4

  ! ---------------------------------------------------------------------------
  !> Perform MP5 reconstruction on the supplied stencil.
  !!
  !! Applies MP5 reconstruction component-wise to a 5-point stencil.
  !! The unlimited 5th-order approximation is identical to the optimal (linear)
  !! WENO5 reconstruction.  A two-stage monotonicity check and curvature-based
  !! limiter enforce non-oscillatory behaviour near discontinuities while
  !! preserving 5th-order accuracy in smooth regions.
  !!
  !! Stencil column mapping (half_stencil=3):
  !!   col 1 = v_{i-2},  col 2 = v_{i-1},  col 3 = v_i (centre),
  !!   col 4 = v_{i+1},  col 5 = v_{i+2}.
  !!
  !! See Suresh & Huynh (1997), Sec. 2.3, Eqs. (2.27)-(2.29).
  !!
  !! @param[in]  f_stencil  Stencil at 5 points (neq x 5)
  !! @param[out] f_hat      Reconstructed value at the interface
  ! ---------------------------------------------------------------------------
  pure subroutine mp5_reconstruct(f_stencil, f_hat)
    real(wp), intent(in) :: f_stencil(:, :)
    real(wp), intent(out) :: f_hat(:)

    integer :: k
    real(wp) :: v1, v2, v3, v4, v5
    real(wp) :: u_unlimited, u_mp
    real(wp) :: d0, d1, d2, d_right_lcm, d_left_lcm
    real(wp) :: u_md, u_lc, u_min, u_max

    do k = 1, neq
      v1 = f_stencil(k, 1)
      v2 = f_stencil(k, 2)
      v3 = f_stencil(k, 3)
      v4 = f_stencil(k, 4)
      v5 = f_stencil(k, 5)

      ! --- Step 1: Unlimited 5th-order approximation ---
      ! Eq. (2.26) of Suresh & Huynh (1997).  Identical to optimal WENO5.
      u_unlimited = (2.0_wp * v1 - 13.0_wp * v2 + 47.0_wp * v3 &
                 & + 27.0_wp * v4 - 3.0_wp * v5) / 60.0_wp

      ! --- Step 2: MP upper bound; accept if u_unlimited already monotone ---
      ! u_MP = v3 + minmod2(v4-v3, alpha*(v3-v2)),  alpha=4.
      u_mp = v3 + minmod2(v4 - v3, alpha_mp5 * (v3 - v2))

      if ((u_unlimited - v3) * (u_unlimited - u_mp) <= 0.0_wp) then
        f_hat(k) = u_unlimited
        cycle
      end if

      ! --- Step 3: Curvature-based limiting (Eqs. 2.28-2.29) ---
      d0 = v3 - 2.0_wp * v2 + v1          ! 2nd difference at i-1
      d1 = v4 - 2.0_wp * v3 + v2          ! 2nd difference at i
      d2 = v5 - 2.0_wp * v4 + v3          ! 2nd difference at i+1

      d_right_lcm = minmod4(4.0_wp * d1 - d2, 4.0_wp * d2 - d1, d1, d2)
      d_left_lcm = minmod4(4.0_wp * d0 - d1, 4.0_wp * d1 - d0, d0, d1)

      u_md = 0.5_wp * (v3 + v4) - 0.5_wp * d_right_lcm
      u_lc = v3 + 0.5_wp * (v3 - v2) + beta_mp5 * d_left_lcm

      u_min = max(min(v3, v4, u_md), min(v3, u_unlimited, u_lc))
      u_max = min(max(v3, v4, u_md), max(v3, u_unlimited, u_lc))

      f_hat(k) = u_unlimited + minmod2(u_min - u_unlimited, u_max - u_unlimited)
    end do
  end subroutine mp5_reconstruct

end module mp5