module slopes_m

  ! Author: Lionel GUEZ

  implicit none

  interface slopes
     ! This generic function computes second order slopes with Van
     ! Leer slope-limiting, given cell averages. Reference: Dukowicz,
     ! 1987, SIAM Journal on Scientific and Statistical Computing, 8,
     ! 305.

     ! The only difference between the specific functions is the rank
     ! of the first argument and the equal rank of the result.

     ! real, intent(in), rank >= 1:: f ! (n, ...) cell averages, n must be >= 1
     ! real, intent(in):: x(:) ! (n + 1) cell edges
     ! real slopes, same shape as f ! (n, ...)

     module procedure slopes1, slopes2, slopes3, slopes4
  end interface

  private
  public slopes

contains

  pure function slopes1(f, x)

    real, intent(in):: f(:)
    real, intent(in):: x(:)
    real slopes1(size(f))

    ! Local:
    integer n, i
    real xc(size(f)) ! (n) cell centers
    real h

    !------------------------------------------------------

    n = size(f)
    forall (i = 1:n) xc(i) = (x(i) + x(i + 1)) / 2.
    slopes1(1) = 0.
    slopes1(n) = 0.

    do i = 2, n - 1
       if (f(i) >= max(f(i - 1), f(i + 1)) .or. f(i) &
            <= min(f(i - 1), f(i + 1))) then
          ! Local extremum
          slopes1(i) = 0.
       else
          ! (f(i - 1), f(i), f(i + 1)) strictly monotonous

          ! Second order slope:
          slopes1(i) = (f(i + 1) - f(i - 1)) / (xc(i + 1) - xc(i - 1))

          ! Slope limitation:
          h = abs(x(i + 1) - xc(i))
          slopes1(i) = sign(min(abs(slopes1(i)), abs(f(i + 1) - f(i)) / h, &
               abs(f(i) - f(i - 1)) / h), slopes1(i))
       end if
    end do

  end function slopes1

  !*************************************************************

  pure function slopes2(f, x)

    real, intent(in):: f(:, :)
    real, intent(in):: x(:)
    real slopes2(size(f, 1), size(f, 2))

    ! Local:
    integer n, i, j
    real xc(size(f, 1)) ! (n) cell centers
    real h(2:size(f, 1) - 1), delta_xc(2:size(f, 1) - 1) ! (2:n - 1)

    !------------------------------------------------------

    n = size(f, 1)
    forall (i = 1:n) xc(i) = (x(i) + x(i + 1)) / 2.

    forall (i = 2:n - 1) 
       h(i) = abs(x(i + 1) - xc(i))
       delta_xc(i) = xc(i + 1) - xc(i - 1)
    end forall

    do j = 1, size(f, 2)
       slopes2(1, j) = 0.

       do i = 2, n - 1
          if (f(i, j) >= max(f(i - 1, j), f(i + 1, j)) .or. &
               f(i, j) <= min(f(i - 1, j), f(i + 1, j))) then
             ! Local extremum
             slopes2(i, j) = 0.
          else
             ! (f(i - 1, j), f(i, j), f(i + 1, j))
             ! strictly monotonous

             ! Second order slope:
             slopes2(i, j) = (f(i + 1, j) - f(i - 1, j)) / delta_xc(i)

             ! Slope limitation:
             slopes2(i, j) = sign(min(abs(slopes2(i, j)), &
                  abs(f(i + 1, j) - f(i, j)) / h(i), &
                  abs(f(i, j) - f(i - 1, j)) / h(i)), slopes2(i, j))
          end if
       end do

       slopes2(n, j) = 0.
    end do

  end function slopes2

  !*************************************************************

  pure function slopes3(f, x)

    real, intent(in):: f(:, :, :)
    real, intent(in):: x(:)
    real slopes3(size(f, 1), size(f, 2), size(f, 3))

    ! Local:
    integer n, i, j, k
    real xc(size(f, 1)) ! (n) cell centers
    real h(2:size(f, 1) - 1), delta_xc(2:size(f, 1) - 1) ! (2:n - 1)

    !------------------------------------------------------

    n = size(f, 1)
    forall (i = 1:n) xc(i) = (x(i) + x(i + 1)) / 2.

    forall (i = 2:n - 1) 
       h(i) = abs(x(i + 1) - xc(i))
       delta_xc(i) = xc(i + 1) - xc(i - 1)
    end forall

    do k = 1, size(f, 3)
       do j = 1, size(f, 2)
          slopes3(1, j, k) = 0.

          do i = 2, n - 1
             if (f(i, j, k) >= max(f(i - 1, j, k), f(i + 1, j, k)) .or. &
                  f(i, j, k) <= min(f(i - 1, j, k), f(i + 1, j, k))) then
                ! Local extremum
                slopes3(i, j, k) = 0.
             else
                ! (f(i - 1, j, k), f(i, j, k), f(i + 1, j, k))
                ! strictly monotonous

                ! Second order slope:
                slopes3(i, j, k) = (f(i + 1, j, k) - f(i - 1, j, k)) &
                     / delta_xc(i)

                ! Slope limitation:
                slopes3(i, j, k) = sign(min(abs(slopes3(i, j, k)), &
                     abs(f(i + 1, j, k) - f(i, j, k)) / h(i), &
                     abs(f(i, j, k) - f(i - 1, j, k)) / h(i)), slopes3(i, j, k))
             end if
          end do

          slopes3(n, j, k) = 0.
       end do
    end do

  end function slopes3

  !*************************************************************

  pure function slopes4(f, x)

    real, intent(in):: f(:, :, :, :)
    real, intent(in):: x(:)
    real slopes4(size(f, 1), size(f, 2), size(f, 3), size(f, 4))

    ! Local:
    integer n, i, j, k, l
    real xc(size(f, 1)) ! (n) cell centers
    real h(2:size(f, 1) - 1), delta_xc(2:size(f, 1) - 1) ! (2:n - 1)

    !------------------------------------------------------

    n = size(f, 1)
    forall (i = 1:n) xc(i) = (x(i) + x(i + 1)) / 2.

    forall (i = 2:n - 1) 
       h(i) = abs(x(i + 1) - xc(i))
       delta_xc(i) = xc(i + 1) - xc(i - 1)
    end forall

    do l = 1, size(f, 4)
       do k = 1, size(f, 3)
          do j = 1, size(f, 2)
             slopes4(1, j, k, l) = 0.

             do i = 2, n - 1
                if (f(i, j, k, l) >= max(f(i - 1, j, k, l), f(i + 1, j, k, l)) &
                     .or. f(i, j, k, l) &
                     <= min(f(i - 1, j, k, l), f(i + 1, j, k, l))) then
                   ! Local extremum
                   slopes4(i, j, k, l) = 0.
                else
                   ! (f(i - 1, j, k, l), f(i, j, k, l), f(i + 1, j, k, l))
                   ! strictly monotonous

                   ! Second order slope:
                   slopes4(i, j, k, l) = (f(i + 1, j, k, l) &
                        - f(i - 1, j, k, l)) / delta_xc(i)

                   ! Slope limitation:
                   slopes4(i, j, k, l) = sign(min(abs(slopes4(i, j, k, l)), &
                        abs(f(i + 1, j, k, l) - f(i, j, k, l)) / h(i), &
                        abs(f(i, j, k, l) - f(i - 1, j, k, l)) / h(i)), &
                        slopes4(i, j, k, l))
                end if
             end do

             slopes4(n, j, k, l) = 0.
          end do
       end do
    end do

  end function slopes4

end module slopes_m
