! Common subroutines and parameters for mesh elements
module moduleMeshCommon
  ! values for integration gauss integral
  ! Segment
  real(8), parameter:: corSeg(1:3)  = (/ -dsqrt(3.D0/5.D0),  0.D0,      dsqrt(3.D0/5.D0) /)
  real(8), parameter:: wSeg(1:3)    = (/        5.D0/9.D0,   8.D0/9.D0,       5.D0/9.D0  /)

  ! tria
  real(8), parameter:: Xi1Tria(1:4) = (/        1.D0/3.D0,   1.D0/5.D0,       3.D0/5.D0,   1.D0/5.D0  /)
  real(8), parameter:: Xi2Tria(1:4) = (/        1.D0/3.D0,   1.D0/5.D0,       1.D0/5.D0,   3.D0/5.D0  /)
  real(8), parameter:: wTria(1:4)   = (/      -27.D0/96.D0, 25.D0/96.D0,     25.D0/96.D0, 25.D0/96.D0 /)

  ! Quad
  real(8), parameter:: corQuad(1:3) = (/ -dsqrt(3.D0/5.D0),  0.D0,      dsqrt(3.D0/5.D0) /)
  real(8), parameter:: wQuad(1:3)   = (/        5.D0/9.D0,   8.D0/9.D0,       5.D0/9.D0  /)

  ! Center point in natural coordinates
  ! Point
  real(8), parameter:: cenPoint(1:3) = (/ 0.0d0,       0.0d0,       0.0d0 /)
  ! Segment
  real(8), parameter:: cenSeg(1:3)   = (/ 0.0d0,       0.0d0,       0.0d0 /)
  ! Tria
  real(8), parameter:: cenTria(1:3)  = (/ 1.0d0/3.0d0, 1.0d0/3.0d0, 0.0d0 /)
  ! Quad
  real(8), parameter:: cenQuad(1:3)  = (/ 0.0d0,       0.0d0,       0.0d0 /)
  ! Tetra
  real(8), parameter:: cenTetra(1:3) = (/ 1.0d0/3.0d0, 1.0d0/3.0d0, 1.d0/3.d0 /)

  contains
    ! RETURN CENTER IN NATURAL COORDINATES
    ! Point
    pure function centerXiPoint() result(Xi)
      implicit none

      real(8):: Xi(1:3)

      Xi = cenPoint

    end function centerXiPoint

    ! Segment
    pure function centerXiSegm() result(Xi)
      implicit none

      real(8):: Xi(1:3)

      Xi = cenSeg

    end function centerXiSegm

    ! Tria
    pure function centerXiTria() result(Xi)
      implicit none

      real(8):: Xi(1:3)

      Xi = cenTria

    end function centerXiTria

    ! Quad
    pure function centerXiQuad() result(Xi)
      implicit none

      real(8):: Xi(1:3)

      Xi = cenQuad

    end function centerXiQuad

    ! Quad
    pure function centerXiTetra() result(Xi)
      implicit none

      real(8):: Xi(1:3)

      Xi = cenTetra

    end function centerXiTetra

    ! ELEMENT FUNCTIONS
    ! Point
    pure function fPsiPoint(Xi, nNodes) RESULT(fPsi)
      implicit none

      real(8), intent(in):: Xi(1:3) ! NOTE: Required by interface but unused
      integer, intent(in):: nNodes
      real(8):: fPsi(1:nNodes)

      fPsi = (/ 1.D0 /)

    end function fPsiPoint

    ! Segment
    pure function fPsiSegm(Xi, nNodes) result(fPsi)
      implicit none

      real(8), intent(in):: Xi(1:3)
      integer, intent(in):: nNodes
      real(8):: fPsi(1:nNodes)

      fPsi = (/ 1.D0 - Xi(1), &
                1.D0 + Xi(1) /)

      fPsi    = fPsi * 0.50D0

    end function fPsiSegm

    ! Quad
    pure function fPsiQuad(Xi, nNodes) result(fPsi)
      implicit none

      real(8), intent(in):: Xi(1:3)
      integer, intent(in):: nNodes
      real(8):: fPsi(1:nNodes)

      fPsi = 0.D0

      fPsi = (/ (1.D0 - Xi(1)) * (1.D0 - Xi(2)), &
                (1.D0 + Xi(1)) * (1.D0 - Xi(2)), &
                (1.D0 + Xi(1)) * (1.D0 + Xi(2)), &
                (1.D0 - Xi(1)) * (1.D0 + Xi(2)) /)

      fPsi    = fPsi * 0.25D0

    end function fPsiQuad

    ! Tria
    pure function fPsiTria(Xi, nNodes) result(fPsi)
      IMPLICIT NONE

      real(8), intent(in):: Xi(1:3)
      integer, intent(in):: nNodes
      real(8):: fPsi(1:nNodes)

      fPsi(1) = 1.D0 - Xi(1) - Xi(2)
      fPsi(2) =        Xi(1)
      fPsi(3) =                Xi(2)

    end function fPsiTria

    ! Tetra
    pure function fPsiTetra(Xi, nNodes) result(fPsi)
      implicit none

      real(8), intent(in):: Xi(1:3)
      integer, intent(in):: nNodes
      real(8):: fPsi(1:nNodes)

      fPsi(1) = 1.D0 - Xi(1) - Xi(2) - Xi(3)
      fPsi(2) =        Xi(1)
      fPsi(3) =                Xi(2)
      fPsi(4) =                        Xi(3)

    end function fPsiTetra

    ! DERIVATIVE FOR ELEMENT FUNCTIONS
    ! Segment
    pure function dPsiSegm(Xi, nNodes) result(dPsi)
      implicit none

      real(8), intent(in):: Xi(1:3) ! NOTE: Required by interface but unused
      integer, intent(in):: nNodes
      real(8):: dPsi(1:3,1:nNodes)

      dPsi = 0.D0

      dPsi(1, 1:2) = (/ -5.D-1, 5.D-1 /)

    end function dPsiSegm

    ! Quad
    pure function dPsiQuad(Xi, nNodes) result(dPsi)
      implicit none

      real(8), intent(in):: Xi(1:3)
      integer, intent(in):: nNodes
      real(8):: dPsi(1:3,1:nNodes)

      dPsi = 0.D0

      dPsi(1, 1:4) = (/ -(1.D0 - Xi(2)), &
                         (1.D0 - Xi(2)), &
                         (1.D0 + Xi(2)), &
                        -(1.D0 + Xi(2)) /)

      dPsi(2, 1:4) = (/ -(1.D0 - Xi(1)), &
                        -(1.D0 + Xi(1)), &
                         (1.D0 + Xi(1)), &
                         (1.D0 - Xi(1)) /)

      dPsi = dPsi * 0.25D0

    end function dPsiQuad

    ! Tria
    pure function dPsiTria(Xi, nNodes) result(dPsi)
      implicit none

      real(8), intent(in):: Xi(1:3) ! NOTE: Required by interface but unused
      integer, intent(in):: nNodes
      real(8):: dPsi(1:3,1:nNodes)

      dPsi = 0.D0

      dPsi(1,1:3) = (/ -1.D0, 1.D0, 0.D0 /)
      dPsi(2,1:3) = (/ -1.D0, 0.D0, 1.D0 /)

    end function dPsiTria

    !Compute element derivative functions in point Xi
    pure function dPsiTetra(Xi, nNodes) result(dPsi)
      implicit none

      real(8), intent(in):: Xi(1:3) ! NOTE: Required by interface but unused
      integer, intent(in):: nNodes
      real(8):: dPsi(1:3, 1:nNodes)

      dPsi = 0.D0

      dPsi(1,1:4) = (/ -1.D0, 1.D0, 0.D0, 0.D0 /)
      dPsi(2,1:4) = (/ -1.D0, 0.D0, 1.D0, 0.D0 /)
      dPsi(3,1:4) = (/ -1.D0, 0.D0, 0.D0, 1.D0 /)

    end function dPsiTetra

end module moduleMeshCommon
