!moduleMesh1DRad: 1D radial module
!              x == r
!              y == theta (unused)
!              z == unused
MODULE moduleMesh1DRad
  USE moduleMesh
  use moduleMeshCommon
  IMPLICIT NONE

  TYPE, PUBLIC, EXTENDS(meshNode):: meshNode1DRad
    !Element coordinates
    REAL(8):: r = 0.D0
    CONTAINS
      !meshNode DEFERRED PROCEDURES
      PROCEDURE, PASS:: init           => initNode1DRad
      PROCEDURE, PASS:: getCoordinates => getCoord1DRad

  END TYPE meshNode1DRad

  TYPE, PUBLIC, EXTENDS(meshEdge):: meshEdge1DRad
    !Element coordinates
    REAL(8):: r = 0.D0
    !Connectivity to nodes
    CLASS(meshNode), POINTER:: n1 => NULL()
    CONTAINS
      !meshEdge DEFERRED PROCEDURES
      PROCEDURE, PASS::   init         => initEdge1DRad
      PROCEDURE, PASS::   getNodes     => getNodes1DRad
      PROCEDURE, PASS::   intersection => intersection1DRad
      PROCEDURE, PASS::   randPos      => randPos1DRad
      procedure, pass::   center       => centerEdgePoint
      procedure, nopass:: centerXi     => centerXiPoint
      procedure, nopass:: fPsi         => fPsiPoint

  END TYPE meshEdge1DRad

  TYPE, PUBLIC, EXTENDS(meshCell):: meshCell1DRadSegm
    !Element coordinates
    REAL(8):: r(1:2)
    !Connectivity to nodes
    CLASS(meshNode), POINTER:: n1 => NULL(), n2 => NULL()
    !Connectivity to adjacent elements
    CLASS(meshElement), POINTER:: e1 => NULL(), e2 => NULL()
    CONTAINS
      !meshCell DEFERRED PROCEDURES
      PROCEDURE, PASS::   init                => initCell1DRadSegm
      PROCEDURE, PASS::   getNodes            => getNodesSegm
      PROCEDURE, PASS::   randPos             => randPos1DRadSegm
      PROCEDURE, NOPASS:: fPsi                => fPsiSegm
      PROCEDURE, NOPASS:: dPsi                => dPsiSegm
      PROCEDURE, PASS::   partialDer          => partialDerSegm
      PROCEDURE, NOPASS:: detJac              => detJ1DRad
      PROCEDURE, NOPASS:: invJac              => invJ1DRad
      PROCEDURE, PASS::   gatherElectricField => gatherEFSegm
      PROCEDURE, PASS::   gatherMagneticField => gatherMFSegm
      PROCEDURE, PASS::   elemK               => elemKSegm
      PROCEDURE, PASS::   elemF               => elemFSegm
      PROCEDURE, NOPASS:: inside              => insideSegm
      PROCEDURE, PASS::   phy2log             => phy2logSegm
      PROCEDURE, PASS::   neighbourElement    => neighbourElementSegm
      !PARTICLUAR PROCEDURES
      PROCEDURE, PASS, PRIVATE:: calculateVolume => volumeSegm

  END TYPE meshCell1DRadSegm

  CONTAINS
    !NODE FUNCTIONS
    !Init node element
    SUBROUTINE initNode1DRad(self, n, r)
      USE moduleSpecies
      USE moduleRefParam
      USE OMP_LIB
      IMPLICIT NONE

      CLASS(meshNode1DRad), INTENT(out):: self
      INTEGER, INTENT(in):: n
      REAL(8), INTENT(in):: r(1:3)

      self%n = n
      self%r = r(1)/L_ref
      !Node volume, to be determined in mesh
      self%v = 0.D0

      !Allocate output
      ALLOCATE(self%output(1:nSpecies))

      CALL OMP_INIT_LOCK(self%lock)

    END SUBROUTINE initNode1DRad

    PURE FUNCTION getCoord1DRad(self) RESULT(r)
      IMPLICIT NONE

      CLASS(meshNode1DRad), INTENT(in):: self
      REAL(8):: r(1:3)

      r = (/ self%r, 0.D0, 0.D0 /)

    END FUNCTION getCoord1DRad

    !EDGE FUNCTIONS
    !Init edge element
    SUBROUTINE initEdge1DRad(self, n, p, ps)
      USE moduleSpecies, only:nSpecies
      USE moduleErrors
      IMPLICIT NONE

      CLASS(meshEdge1DRad), INTENT(out):: self
      INTEGER, INTENT(in):: n
      INTEGER, INTENT(in):: p(:)
      INTEGER, INTENT(in):: ps
      integer:: ps_index
      REAL(8), DIMENSION(1:3):: r1
      INTEGER:: s

      self%n = n
      self%nNodes = SIZE(p)
      self%n1 => mesh%nodes(p(1))%obj
      !Get element coordinates
      r1 = self%n1%getCoordinates()

      self%r = r1(1)

      self%surface = 1.D0

      self%normal = (/ 1.D0, 0.D0, 0.D0 /)

      ! Get Physical Surface index based on input numering
      ps_index = physicalSurface_to_index(ps)

      ! Add elements to physical surface
      call meshEdgePointer_add(physicalSurfaces(ps_index)%edges, self%n)
      call meshNodePointer_add(physicalSurfaces(ps_index)%nodes, self%n1%n)

      ! Link edge to particle boundaries
      allocate(self%boundariesParticle(1:nSpecies))
      do s = 1, nSpecies
        self%boundariesParticle(s)%obj => physicalSurfaces(ps_index)%particles(s)%obj

      end do

    end subroutine initEdge1DRad

    !Get nodes from edge
    PURE FUNCTION getNodes1DRad(self, nNodes) RESULT(n)
      IMPLICIT NONE

      CLASS(meshEdge1DRad), INTENT(in):: self
      INTEGER, INTENT(in):: nNodes
      INTEGER:: n(1:nNodes)

      n = (/ self%n1%n /)

    END FUNCTION getNodes1DRad

    PURE FUNCTION intersection1DRad(self, r0) RESULT(r)
      IMPLICIT NONE

      CLASS(meshEdge1DRad), INTENT(in):: self
      REAL(8), DIMENSION(1:3), INTENT(in):: r0 ! NOTE: Required by interface but unused
      REAL(8), DIMENSION(1:3):: r

      r = (/ self%r, 0.D0, 0.D0 /)

    END FUNCTION intersection1DRad

    !Calculate a 'random' position in edge
    FUNCTION randPos1DRad(self) RESULT(r)
      CLASS(meshEdge1DRad), INTENT(in):: self
      REAL(8):: r(1:3)

      r = (/ self%r, 0.D0, 0.D0 /)

    END FUNCTION randPos1DRad

    function centerEdgePoint(self) RESULT(r)
      class(meshEdge1DRad), intent(in):: self
      real(8):: r(1:3)

      r = (/ self%r, 0.D0, 0.D0 /)

    end function centerEdgePoint

    !CELL FUNCTIONS
    !SEGMENT FUNCTIONS
    !Init element
    SUBROUTINE initCell1DRadSegm(self, n, p, nodes)
      USE moduleRefParam
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(out):: self
      INTEGER, INTENT(in):: n
      INTEGER, INTENT(in):: p(:)
      TYPE(meshNodeCont), INTENT(in), TARGET:: nodes(:)
      REAL(8), DIMENSION(1:3):: r1, r2

      self%n = n
      self%nNodes = SIZE(p)
      self%n1 => nodes(p(1))%obj
      self%n2 => nodes(p(2))%obj
      !Get element coordinates
      r1 = self%n1%getCoordinates()
      r2 = self%n2%getCoordinates()
      self%r = (/ r1(1), r2(1) /)

      !Assign node volume
      CALL self%calculateVolume()

      CALL OMP_INIT_LOCK(self%lock)

      ALLOCATE(self%listPart_in(1:nSpecies))
      ALLOCATE(self%totalWeight(1:nSpecies))

    END SUBROUTINE initCell1DRadSegm

    !Get nodes from 1D volume
    PURE FUNCTION getNodesSegm(self, nNodes) RESULT(n)
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(in):: self
      INTEGER, INTENT(in):: nNodes
      INTEGER:: n(1:nNodes)

      n = (/ self%n1%n, self%n2%n /)

    END FUNCTION getNodesSegm

    !Random position in 1D volume
    FUNCTION randPos1DRadSegm(self) RESULT(r)
      USE moduleRandom
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(in):: self
      REAL(8):: r(1:3)
      REAL(8):: Xi(1:3)
      REAL(8):: fPsi(1:2)

      Xi    = 0.D0
      Xi(1) = random(-1.D0, 1.D0)

      fPsi = self%fPsi(Xi, 2)

      r    = 0.D0
      r(1) = DOT_PRODUCT(fPsi, self%r)

    END FUNCTION randPos1DRadSegm

    !Partial derivative in global coordinates
    PURE FUNCTION partialDerSegm(self, nNodes, dPsi) RESULT(pDer)
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(in):: self
      INTEGER, INTENT(in):: nNodes
      REAL(8), INTENT(in):: dPsi(1:3,1:nNodes)
      REAL(8):: pDer(1:3, 1:3)

      pDer = 0.D0

      pDer(1,1) = DOT_PRODUCT(dPsi(1,1:2), self%r(1:2))
      pDer(2,2) = 1.D0
      pDer(3,3) = 1.D0

    END FUNCTION partialDerSegm

    PURE FUNCTION gatherEFSegm(self, Xi) RESULT(array)
      IMPLICIT NONE
      CLASS(meshCell1DRadSegm), INTENT(in):: self
      REAL(8), INTENT(in):: Xi(1:3)
      REAL(8):: array(1:3)
      REAL(8):: phi(1:2)

      phi = (/ self%n1%emData%phi, &
               self%n2%emData%phi /)

      array = -self%gatherDF(Xi, 2, phi)

    END FUNCTION gatherEFSegm

    PURE FUNCTION gatherMFSegm(self, Xi) RESULT(array)
      IMPLICIT NONE
      CLASS(meshCell1DRadSegm), INTENT(in):: self
      REAL(8), INTENT(in):: Xi(1:3)
      REAL(8):: array(1:3)
      REAL(8):: B(1:2,1:3)

      B(:,1) = (/ self%n1%emData%B(1), &
                  self%n2%emData%B(1) /)

      B(:,2) = (/ self%n1%emData%B(2), &
                  self%n2%emData%B(2) /)

      B(:,3) = (/ self%n1%emData%B(3), &
                  self%n2%emData%B(3) /)

      array = self%gatherF(Xi, 2, B)

    END FUNCTION gatherMFSegm

    !Compute element local stiffness matrix
    PURE FUNCTION elemKSegm(self, nNodes) RESULT(localK)
      USE moduleConstParam, ONLY: PI2
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(in):: self
      INTEGER, INTENT(in):: nNodes
      REAL(8):: localK(1:nNodes,1:nNodes)
      REAL(8):: Xi(1:3)
      REAL(8):: dPsi(1:3, 1:2)
      REAL(8):: pDer(1:3, 1:3)
      REAL(8):: r
      REAL(8):: invJ(1:3, 1:3), detJ
      INTEGER:: l

      localK = 0.D0

      Xi = 0.D0
      !Start 1D Gauss Quad Integral
      DO l = 1, 3
        Xi(1)  = corSeg(l)
        dPsi   = self%dPsi(Xi, 2)
        pDer   = self%partialDer(2, dPsi)
        detJ   = self%detJac(pDer)
        invJ   = self%invJac(pDer)
        r      = self%gatherF(Xi, 2, self%r)
        localK = localK + MATMUL(TRANSPOSE(MATMUL(invJ,dPsi)), &
                                           MATMUL(invJ,dPsi))* &
                          r*wSeg(l)/detJ

      END DO
      localK = localK*PI2

    END FUNCTION elemKSegm

    !Compute the local source vector for a force f
    PURE FUNCTION elemFSegm(self, nNodes, source) RESULT(localF)
      USE moduleConstParam, ONLY: PI2
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(in):: self
      INTEGER, INTENT(in):: nNodes
      REAL(8), INTENT(in):: source(1:nNodes)
      REAL(8):: localF(1:nNodes)
      REAL(8):: fPsi(1:2)
      REAL(8):: dPsi(1:3, 1:2), pDer(1:3, 1:3)
      REAL(8):: Xi(1:3)
      REAL(8):: detJ, f
      REAL(8):: r
      INTEGER:: l

      localF = 0.D0

      Xi = 0.D0
      !Start 1D Gauss Quad Integral
      DO l = 1, 3
        Xi(1) = corSeg(l)
        dPsi   = self%dPsi(Xi, 2)
        pDer   = self%partialDer(2, dPsi)
        detJ   = self%detJac(pDer)
        fPsi   = self%fPsi(Xi, 2)
        r      = DOT_PRODUCT(fPsi, self%r)
        f      = DOT_PRODUCT(fPsi, source)
        localF = localF + r*f*fPsi*wSeg(l)*detJ

      END DO
      localF = localF*PI2

    END FUNCTION elemFSegm

    PURE FUNCTION insideSegm(Xi) RESULT(ins)
      IMPLICIT NONE

      REAL(8), INTENT(in):: Xi(1:3)
      LOGICAL:: ins

      ins = Xi(1) >=-1.D0 .AND. &
            Xi(1) <= 1.D0

    END FUNCTION insideSegm

    PURE FUNCTION phy2logSegm(self, r) RESULT(Xi)
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(in):: self
      REAL(8), INTENT(in):: r(1:3)
      REAL(8):: Xi(1:3)

      Xi = 0.D0

      Xi(1) = 2.D0*(r(1) - self%r(1))/(self%r(2) - self%r(1)) - 1.D0

    END FUNCTION phy2logSegm

    !Get the next element for a logical position Xi
    SUBROUTINE neighbourElementSegm(self, Xi, neighbourElement)
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(in):: self
      REAL(8), INTENT(in):: Xi(1:3)
      CLASS(meshElement), POINTER, INTENT(out):: neighbourElement
      
      NULLIFY(neighbourElement)
      IF     (Xi(1) < -1.D0) THEN
        neighbourElement => self%e2

      ELSEIF (Xi(1) >  1.D0) THEN
        neighbourElement => self%e1

      END IF

    END SUBROUTINE neighbourElementSegm

    !Compute element volume
    PURE SUBROUTINE volumeSegm(self)
      USE moduleConstParam, ONLY: PI4
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(inout):: self
      REAL(8):: Xi(1:3)
      REAL(8):: dPsi(1:3, 1:2), pDer(1:3, 1:3)
      REAL(8):: detJ
      REAL(8):: fPsi(1:2)
      REAL(8):: r

      self%volume  = 0.D0
      !1D 1 point Gauss Quad Integral
      Xi = 0.D0
      dPsi = self%dPsi(Xi, 2)
      pDer = self%partialDer(2, dPsi)
      detJ = self%detJac(pDer)
      fPsi = self%fPsi(Xi, 2)
      r = DOT_PRODUCT(fPsi, self%r)
      !Compute total volume of the cell
      self%volume  = r*detJ*PI4 !2*2PI
      !Compute volume per node
      Xi = (/-5.D-1,  0.D0, 0.D0/)
      r = self%gatherF(Xi, 2, self%r)
      self%n1%v = self%n1%v + fPsi(1)*r*detJ*PI4
      Xi = (/ 5.D-1,  0.D0, 0.D0/)
      r = self%gatherF(Xi, 2, self%r)
      self%n2%v = self%n2%v + fPsi(2)*r*detJ*PI4

    END SUBROUTINE volumeSegm

    !COMMON FUNCTIONS FOR 1D CELL ELEMENTS
    !Compute element Jacobian determinant
    PURE FUNCTION detJ1DRad(pDer) RESULT(dJ)
      IMPLICIT NONE

      REAL(8), INTENT(in):: pDer(1:3, 1:3)
      REAL(8):: dJ

      dJ = pDer(1, 1)

    END FUNCTION detJ1DRad

    !Compute element Jacobian inverse matrix (without determinant)
    PURE FUNCTION invJ1DRad(pDer) RESULT(invJ)
      IMPLICIT NONE

      REAL(8), INTENT(in):: pDer(1:3, 1:3)
      REAL(8):: invJ(1:3,1:3)

      invJ = 0.D0

      invJ(1, 1) = 1.D0/pDer(1, 1)
      invJ(2, 2) = 1.D0
      invJ(3, 3) = 1.D0

    END FUNCTION invJ1DRad

    SUBROUTINE connectMesh1DRad(self)
      IMPLICIT NONE

      CLASS(meshGeneric), INTENT(inout):: self
      INTEGER:: e, et

      DO e = 1, self%numCells
        !Connect Cell-Cell
        DO et = 1, self%numCells
          IF (e /= et) THEN
            CALL connectCellCell(self%cells(e)%obj, self%cells(et)%obj)

          END IF

        END DO

        SELECT TYPE(self)
        TYPE IS(meshParticles)
          !Connect Cell-Edge
          DO et = 1, self%numEdges
            CALL connectCellEdge(self%cells(e)%obj, self%edges(et)%obj)

          END DO

        END SELECT

      END DO

    END SUBROUTINE connectMesh1DRad

    SUBROUTINE connectCellCell(elemA, elemB)
      IMPLICIT NONE

      CLASS(meshCell), INTENT(inout):: elemA
      CLASS(meshCell), INTENT(inout):: elemB

        SELECT TYPE(elemA)
        TYPE IS(meshCell1DRadSegm)
          SELECT TYPE(elemB)
          TYPE IS(meshCell1DRadSegm)
            CALL connectSegmSegm(elemA, elemB)

          END SELECT

        END SELECT

    END SUBROUTINE connectCellCell

    SUBROUTINE connectSegmSegm(elemA, elemB)
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(inout), TARGET:: elemA
      CLASS(meshCell1DRadSegm), INTENT(inout), TARGET:: elemB

      IF (.NOT. ASSOCIATED(elemA%e1) .AND. &
          elemA%n2%n == elemB%n1%n) THEN
        elemA%e1 => elemB
        elemB%e2 => elemA
        
      END IF

      IF (.NOT. ASSOCIATED(elemA%e2) .AND. &
          elemA%n1%n == elemB%n2%n) THEN

        elemA%e2 => elemB
        elemB%e1 => elemA

      END IF

    END SUBROUTINE connectSegmSegm

    SUBROUTINE connectCellEdge(elemA, elemB)
      IMPLICIT NONE

      CLASS(meshCell),  INTENT(inout):: elemA
      CLASS(meshEdge), INTENT(inout):: elemB

      SELECT TYPE(elemA)
      TYPE IS (meshCell1DRadSegm)
        SELECT TYPE(elemB)
        CLASS IS(meshEdge1DRad)
          CALL connectSegmEdge(elemA, elemB)

        END SELECT

      END SELECT

    END SUBROUTINE connectCellEdge

    SUBROUTINE connectSegmEdge(elemA, elemB)
      IMPLICIT NONE

      CLASS(meshCell1DRadSegm), INTENT(inout), TARGET:: elemA
      CLASS(meshEdge1DRad),    INTENT(inout), TARGET:: elemB

      IF (.NOT. ASSOCIATED(elemA%e1) .AND. &
          elemA%n2%n == elemB%n1%n) THEN

        elemA%e1 => elemB
        elemB%e2 => elemA

        !Rever the normal to point inside the domain
        elemB%normal = - elemB%normal

      END IF

      IF (.NOT. ASSOCIATED(elemA%e2) .AND. &
          elemA%n1%n == elemB%n1%n) THEN

        elemA%e2 => elemB
        elemB%e1 => elemA

      END IF

    END SUBROUTINE connectSegmEdge

END MODULE moduleMesh1DRad

