MODULE Interpolation
USE Types
USE SParIterGlobals
USE CoordinateSystems
USE ElementDescription, ONLY : GlobalToLocal, ElementInfo, GetElementType, &
SquareFaceDOFsOrdering, EdgeElementStyle, mGetElementDOFs
USE PElementMaps, ONLY : isActivePElement
USE Integration, ONLY : GaussIntegrationPoints_t, GaussPoints
USE GeneralUtils, ONLY : AllocateMatrix
USE ListMatrix, ONLY : List_AddToMatrixElement, List_toCRSMatrix
IMPLICIT NONE
CONTAINS
SUBROUTINE FindLeafElements( Point, dim, RootQuadrant, LeafQuadrant )
REAL(KIND=dp), DIMENSION(3) :: Point
REAL(KIND=dp), DIMENSION(6) :: GeometryBoundingBox
INTEGER :: dim
TYPE(Quadrant_t), POINTER :: RootQuadrant, LeafQuadrant
LeafQuadrant => RootQuadrant
GeometryBoundingBox = RootQuadrant % BoundingBox
CALL FindPointsQuadrant(Point, dim, LeafQuadrant)
CONTAINS
RECURSIVE SUBROUTINE FindPointsQuadrant(Point, dim, MotherQuadrant)
REAL(KIND=dp), DIMENSION(3) :: Point
INTEGER :: dim
TYPE(Quadrant_t), POINTER :: MotherQuadrant
TYPE(Quadrant_t), POINTER :: ChildQuadrant
INTEGER :: i
REAL(KIND=dp) :: BBox(6), eps3
REAL(KIND=dp), PARAMETER :: eps2=0.0_dp
DO i=1, 2**dim
ChildQuadrant => MotherQuadrant % ChildQuadrants(i) % Quadrant
BBox = ChildQuadrant % BoundingBox
eps3 = eps2 * MAXVAL(BBox(4:6) - BBox(1:3))
BBox(1:3) = BBox(1:3) - eps3
BBox(4:6) = BBox(4:6) + eps3
IF ( ( Point(1) >= BBox(1)) .AND. (Point(1) <= BBox(4) ) .AND. &
( Point(2) >= BBox(2)) .AND. (Point(2) <= BBox(5) ) .AND. &
( Point(3) >= BBox(3)) .AND. (Point(3) <= BBox(6) ) ) EXIT
END DO
IF ( i > 2**dim ) THEN
NULLIFY( MotherQuadrant )
RETURN
END IF
MotherQuadrant => ChildQuadrant
IF ( ASSOCIATED ( MotherQuadrant % ChildQuadrants ) )THEN
CALL FindPointsQuadrant( Point, dim, MotherQuadrant )
END IF
END SUBROUTINE FindPointsQuadrant
END SUBROUTINE FindLeafElements
FUNCTION PointInElement( Element, ElementNodes, Point, &
LocalCoordinates, GlobalEps, LocalEps, NumericEps, &
GlobalDistance, LocalDistance, EdgeBasis, &
USolver ) RESULT(IsInElement)
TYPE(Element_t), POINTER :: Element
TYPE(Nodes_t) :: ElementNodes
LOGICAL :: IsInElement
REAL(KIND=dp), DIMENSION(:) :: Point
REAL(KIND=dp), DIMENSION(:) :: LocalCoordinates
REAL(KIND=dp), OPTIONAL :: GlobalEps
REAL(KIND=dp), OPTIONAL :: LocalEps
REAL(KIND=dp), OPTIONAL :: NumericEps
REAL(KIND=dp), OPTIONAL :: GlobalDistance
REAL(KIND=dp), OPTIONAL :: LocalDistance
LOGICAL, OPTIONAL :: EdgeBasis
TYPE(Solver_t), POINTER, OPTIONAL :: USolver
INTEGER :: n
INTEGER :: i
LOGICAL :: ComputeDistance, trans
REAL(KIND=dp) :: ug,vg,wg,xdist,ydist,zdist,sumdist,eps0,eps1,eps2,escale,&
minx,maxx,miny,maxy,minz,maxz
IsInElement = .FALSE.
n = Element % TYPE % NumberOfNodes
IF ( PRESENT(NumericEps) ) THEN
eps0 = NumericEps
ELSE
eps0 = EPSILON( eps0 )
END IF
IF ( PRESENT(GlobalEps) ) THEN
Eps1 = GlobalEps
ELSE
Eps1 = 1.0e-4
END IF
IF ( PRESENT(LocalEps) ) THEN
Eps2 = LocalEps
ELSE
Eps2 = 1.0e-10
END IF
IF( PRESENT( LocalDistance ) ) THEN
LocalDistance = HUGE( LocalDistance )
END IF
IF( Eps1 < 0.0_dp ) THEN
CONTINUE
ELSE IF( PRESENT( GlobalDistance ) ) THEN
minx = MINVAL( ElementNodes % x(1:n) )
maxx = MAXVAL( ElementNodes % x(1:n) )
miny = MINVAL( ElementNodes % y(1:n) )
maxy = MAXVAL( ElementNodes % y(1:n) )
minz = MINVAL( ElementNodes % z(1:n) )
maxz = MAXVAL( ElementNodes % z(1:n) )
xdist = MAX( MAX( Point(1) - maxx, 0.0_dp ), minx - Point(1) )
ydist = MAX( MAX( Point(2) - maxy, 0.0_dp ), miny - Point(2) )
zdist = MAX( MAX( Point(3) - maxz, 0.0_dp ), minz - Point(3) )
GlobalDistance = SQRT( xdist**2 + ydist**2 + zdist**2)
IF( xdist > eps0 + eps1 * (maxx - minx) ) RETURN
IF( ydist > eps0 + eps1 * (maxy - miny) ) RETURN
IF( zdist > eps0 + eps1 * (maxz - minz) ) RETURN
ELSE
minx = MINVAL( ElementNodes % x(1:n) )
maxx = MAXVAL( ElementNodes % x(1:n) )
xdist = MAX( MAX( Point(1) - maxx, 0.0_dp ), minx - Point(1) )
IF( xdist > eps0 + eps1 * (maxx - minx) ) RETURN
miny = MINVAL( ElementNodes % y(1:n) )
maxy = MAXVAL( ElementNodes % y(1:n) )
ydist = MAX( MAX( Point(2) - maxy, 0.0_dp ), miny - Point(2) )
IF( ydist > eps0 + eps1 * (maxy - miny) ) RETURN
minz = MINVAL( ElementNodes % z(1:n) )
maxz = MAXVAL( ElementNodes % z(1:n) )
zdist = MAX( MAX( Point(3) - maxz, 0.0_dp ), minz - Point(3) )
IF( zdist > eps0 + eps1 * (maxz - minz) ) RETURN
END IF
CALL GlobalToLocal( ug, vg, wg, Point(1), Point(2), Point(3), &
Element, ElementNodes )
SELECT CASE ( Element % TYPE % ElementCode / 100 )
CASE(1)
sumdist = ug
CASE(2)
sumdist = MAX( ug - 1.0, MAX( -ug - 1.0, 0.0 ) )
CASE(3)
sumdist = MAX( -ug, 0.0 ) + MAX( -vg, 0.0 )
sumdist = sumdist + MAX( ug + vg - 1.0_dp, 0.0 )
CASE(4)
sumdist = MAX( ug - 1.0, MAX( -ug -1.0, 0.0 ) )
sumdist = sumdist + MAX( vg - 1.0, MAX( -vg - 1.0, 0.0 ) )
CASE(5)
sumdist = MAX( -ug, 0.0 ) + MAX( -vg, 0.0 ) + MAX( -wg, 0.0 )
sumdist = sumdist + MAX( ug + vg + wg - 1.0, 0.0 )
CASE(7)
sumdist = MAX( -ug, 0.0 ) + MAX( -vg, 0.0 )
sumdist = sumdist + MAX( ug + vg - 1.0_dp, 0.0 )
sumdist = sumdist + MAX( wg - 1.0, MAX( -wg - 1.0, 0.0 ) )
CASE(8)
sumdist = MAX( ug - 1.0, MAX( -ug -1.0, 0.0 ) )
sumdist = sumdist + MAX( vg - 1.0, MAX( -vg - 1.0, 0.0 ) )
sumdist = sumdist + MAX( wg - 1.0, MAX( -wg - 1.0, 0.0 ) )
CASE DEFAULT
WRITE( Message,'(A,I4)') 'Not implemented for element code',&
Element % TYPE % ElementCode
CALL Warn('PointInElement',Message)
END SELECT
IF( sumdist < eps0 + eps2 ) THEN
IsInElement = .TRUE.
END IF
IF( PRESENT( LocalDistance ) ) THEN
LocalDistance = sumdist
END IF
trans = PRESENT(EdgeBasis)
IF(trans) trans=EdgeBasis
trans = trans .OR. isActivePElement(Element,USolver)
IF (trans) THEN
SELECT CASE(Element % Type % ElementCode/100)
CASE(3)
ug = 2*ug + vg - 1
vg = SQRT(3._dp)*vg
CASE(5)
ug = 2*ug + vg + wg - 1
vg = SQRT(3._dp)*vg + 1/SQRT(3._dp)*wg
wg = 2*SQRT(2/3._dp)*wg
CASE(6)
wg = SQRT(2._dp)*wg
CASE(7)
ug = 2*ug + vg - 1
vg = SQRT(3._dp)*vg
END SELECT
END IF
LocalCoordinates(1) = ug
LocalCoordinates(2) = vg
LocalCoordinates(3) = wg
END FUNCTION PointInElement
SUBROUTINE BuildQuadrantTree(Mesh, BoundingBox, RootQuadrant)
TYPE(Mesh_t) :: Mesh
REAL(KIND=dp), DIMENSION(6) :: BoundingBox
TYPE(Quadrant_t), POINTER :: RootQuadrant
INTEGER :: dim, Generation, i
REAL(KIND=dp) :: XMin, XMax, YMin, YMax, ZMin, ZMax
TYPE(Quadrant_t), POINTER :: MotherQuadrant
INTEGER :: MaxLeafElems
dim = MAX( Mesh % MeshDim, CoordinateSystemDimension() )
IF ( dim == 3 ) THEN
MaxLeafElems = 16
ELSE
MaxLeafElems = 8
END IF
Generation = 0
XMin = BoundingBox(1)
XMax = BoundingBox(4)
IF ( dim >= 2 ) THEN
YMin = BoundingBox(2)
YMax = BoundingBox(5)
ELSE
YMin = 0.d0
YMax = 0.d0
END IF
IF ( dim == 3) THEN
ZMin = BoundingBox(3)
ZMax = BoundingBox(6)
ELSE
ZMin = 0.d0
ZMax = 0.d0
END IF
ALLOCATE ( RootQuadrant )
RootQuadrant % BoundingBox = [ XMin, YMin, ZMin, XMax, YMax, ZMax ]
RootQuadrant % NElemsInQuadrant = Mesh % NumberOfBulkElements
ALLOCATE ( RootQuadrant % Elements( Mesh % NumberOfBulkElements ) )
RootQuadrant % Elements = [ (i, i=1,Mesh % NumberOfBulkElements) ]
CALL Info( 'BuildQuandrantTree', 'Start', Level=4 )
MotherQuadrant => RootQuadrant
CALL CreateChildQuadrants( MotherQuadrant, dim )
CALL Info( 'BuildQuandrantTree', 'Ready', Level=4 )
CONTAINS
RECURSIVE SUBROUTINE CreateChildQuadrants( MotherQuadrant, dim )
TYPE(Quadrant_t), POINTER :: MotherQuadrant
INTEGER :: i, dim, n
TYPE(QuadrantPointer_t) :: ChildQuadrant(8)
REAL(KIND=dp) :: XMin, XMax, YMin, YMax, ZMin, ZMax
n = 2**dim
ALLOCATE ( MotherQuadrant % ChildQuadrants(n) )
DO i=1, n
ALLOCATE( MotherQuadrant % ChildQuadrants(i) % Quadrant )
ChildQuadrant(i) % Quadrant => &
MotherQuadrant % ChildQuadrants(i) % Quadrant
ChildQuadrant(i) % Quadrant % NElemsInQuadrant = 0
NULLIFY ( ChildQuadrant(i) % Quadrant % Elements )
NULLIFY ( ChildQuadrant(i) % Quadrant % ChildQuadrants )
END DO
XMin = MotherQuadrant % BoundingBox(1)
YMin = MotherQuadrant % BoundingBox(2)
ZMin = MotherQuadrant % BoundingBox(3)
XMax = MotherQuadrant % BoundingBox(4)
YMax = MotherQuadrant % BoundingBox(5)
ZMax = MotherQuadrant % BoundingBox(6)
MotherQuadrant % Size = MAX ( MAX( XMax-XMin, YMax-YMin), ZMax-ZMin )
ChildQuadrant(1) % Quadrant % BoundingBox = [ XMin, YMin, ZMin, &
(XMin + XMax)/2.d0, (YMin + YMax)/2.d0, (ZMin + ZMax)/2.d0 ]
ChildQuadrant(2) % Quadrant % BoundingBox = [ (XMin+XMax)/2.d0, &
YMin, ZMin, XMax, (YMin+YMax)/2.d0, (ZMin+ZMax)/2.d0 ]
IF ( dim >= 2 ) THEN
ChildQuadrant(3) % Quadrant % BoundingBox = [ XMin, (YMin+YMax)/2.d0, &
ZMin, (XMin+XMax)/2.d0, YMax, (ZMin+ZMax)/2.d0 ]
ChildQuadrant(4) % Quadrant % BoundingBox = [ (XMin+XMax)/2.d0, &
(YMin+YMax)/2.d0, ZMin, XMax, YMax, (ZMin+ZMax)/2.d0 ]
END IF
IF ( dim == 3 ) THEN
ChildQuadrant(5) % Quadrant % BoundingBox = [ XMin, YMin, &
(ZMin+ZMax)/2.d0, (XMin+XMax)/2.d0, (YMin+YMax)/2.d0, ZMax ]
ChildQuadrant(6) % Quadrant % BoundingBox = [ (XMin+XMax)/2.d0, YMin, &
(ZMin+ZMax)/2.d0, XMax, (YMin+YMax)/2.d0, ZMax ]
ChildQuadrant(7) % Quadrant % BoundingBox = [ XMin, (YMin+YMax)/2.d0, &
(ZMin+ZMax)/2.d0, (XMin+XMax)/2.d0, YMax, ZMax ]
ChildQuadrant(8) % Quadrant % BoundingBox = [ (XMin+XMax)/2.d0, &
(YMin+YMax)/2.d0, (ZMin+ZMax)/2.d0, XMax, YMax, ZMax ]
END IF
CALL PutElementsInChildQuadrants( ChildQuadrant, MotherQuadrant, dim )
DO i=1,n
ChildQuadrant(i) % Quadrant % Size = MotherQuadrant % Size / 2
IF ( ChildQuadrant(i) % Quadrant % NElemsInQuadrant > MaxLeafElems ) THEN
IF ( ChildQuadrant(i) % Quadrant % Size > &
ChildQuadrant(i) % Quadrant % MinElementSize ) THEN
IF ( Generation <= 8 ) THEN
Generation = Generation + 1
CALL CreateChildQuadrants( ChildQuadrant(i) % Quadrant, dim )
Generation = Generation - 1
END IF
END IF
END IF
END DO
DEALLOCATE ( MotherQuadrant % Elements )
NULLIFY ( MotherQuadrant % Elements )
END SUBROUTINE CreateChildQuadrants
RECURSIVE SUBROUTINE PutElementsInChildQuadrants( ChildQuadrant, &
MotherQuadrant, dim )
TYPE(QuadrantPointer_t) :: ChildQuadrant(8)
TYPE(Quadrant_t), POINTER :: MotherQuadrant
INTEGER :: dim
REAL(KIND=dp) :: eps3
REAL(KIND=dp), PARAMETER :: eps2=2.5d-2
TYPE(Element_t), POINTER :: CurrentElement
INTEGER :: i, j, t, n
INTEGER, POINTER :: NodeIndexes(:)
INTEGER :: ElementList(2**dim, MotherQuadrant % NElemsInQuadrant)
LOGICAL :: ElementInQuadrant
REAL(KIND=dp) :: BBox(6), XMin, XMax, YMin, YMax, ZMin, ZMax, ElementSize
DO i=1,2**dim
ChildQuadrant(i) % Quadrant % NElemsInQuadrant = 0
ChildQuadrant(i) % Quadrant % MinElementSize = 1.0d20
END DO
DO t=1, MotherQuadrant % NElemsInQuadrant
CurrentElement => Mesh % Elements( MotherQuadrant % Elements(t) )
n = CurrentElement % TYPE % NumberOfNodes
NodeIndexes => CurrentElement % NodeIndexes
XMin = MINVAL( Mesh % Nodes % x(NodeIndexes) )
XMax = MAXVAL( Mesh % Nodes % x(NodeIndexes) )
YMin = MINVAL( Mesh % Nodes % y(NodeIndexes) )
YMax = MAXVAL( Mesh % Nodes % y(NodeIndexes) )
ZMin = MINVAL( Mesh % Nodes % z(NodeIndexes) )
ZMax = MAXVAL( Mesh % Nodes % z(NodeIndexes) )
ElementSize = MAX( MAX( XMax-XMin, YMax-YMin ), ZMax-ZMin )
DO i=1, 2**dim
BBox = ChildQuadrant(i) % Quadrant % BoundingBox
eps3 = 0.0d0
eps3 = MAX( eps3, BBox(4) - BBox(1) )
eps3 = MAX( eps3, BBox(5) - BBox(2) )
eps3 = MAX( eps3, BBox(6) - BBox(3) )
eps3 = eps2 * eps3
BBox(1:3) = BBox(1:3) - eps3
BBox(4:6) = BBox(4:6) + eps3
ElementInQuadrant = .TRUE.
IF ( XMax < BBox(1) .OR. XMin > BBox(4) .OR. &
YMax < BBox(2) .OR. YMin > BBox(5) .OR. &
ZMax < BBox(3) .OR. ZMin > BBox(6) ) ElementInQuadrant = .FALSE.
IF ( ElementInQuadrant ) THEN
ChildQuadrant(i) % Quadrant % NElemsInQuadrant = &
ChildQuadrant(i) % Quadrant % NElemsInQuadrant + 1
ChildQuadrant(i) % Quadrant % MinElementSize = &
MIN(ElementSize, ChildQuadrant(i) % Quadrant % MinElementSize)
ElementList(i,ChildQuadrant(i) % Quadrant % NElemsInQuadrant) = &
MotherQuadrant % Elements(t)
END IF
END DO
END DO
DO i=1,2**dim
IF ( ChildQuadrant(i) % Quadrant % NElemsInQuadrant /= 0 ) THEN
ALLOCATE ( ChildQuadrant(i) % Quadrant % Elements ( &
ChildQuadrant(i) % Quadrant % NElemsInQuadrant ) )
ChildQuadrant(i) % Quadrant % Elements (1: &
ChildQuadrant(i) % Quadrant % NElemsInQuadrant) = &
ElementList(i,1:ChildQuadrant(i) % Quadrant % NElemsInQuadrant)
END IF
END DO
END SUBROUTINE PutElementsInChildQuadrants
END SUBROUTINE BuildQuadrantTree
SUBROUTINE CopyElementNodesFromMesh(ElementNodes, Mesh, n, Indexes)
TYPE(Nodes_t) :: ElementNodes
TYPE(Mesh_t), POINTER :: Mesh
INTEGER :: n
INTEGER, POINTER :: Indexes(:)
INTEGER :: m
IF ( .NOT. ASSOCIATED( ElementNodes % x ) ) THEN
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n) )
ELSE
m = SIZE(ElementNodes % x)
IF ( m < n ) THEN
DEALLOCATE(ElementNodes % x, ElementNodes % y, ElementNodes % z)
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n) )
ELSE IF( m > n ) THEN
ElementNodes % x(n+1:m) = 0.0_dp
ElementNodes % y(n+1:m) = 0.0_dp
ElementNodes % z(n+1:m) = 0.0_dp
END IF
END IF
ElementNodes % x(1:n) = Mesh % Nodes % x(Indexes(1:n))
ElementNodes % y(1:n) = Mesh % Nodes % y(Indexes(1:n))
ElementNodes % z(1:n) = Mesh % Nodes % z(Indexes(1:n))
END SUBROUTINE CopyElementNodesFromMesh
SUBROUTINE NodalToNedelecPiMatrix(PiMat, Edge, Mesh, dim, SecondFamily)
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,6)
TYPE(Element_t), POINTER, INTENT(IN) :: Edge
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh
INTEGER, INTENT(IN) :: dim
LOGICAL, OPTIONAL, INTENT(IN) :: SecondFamily
TYPE(Nodes_t), SAVE :: Nodes
TYPE(GaussIntegrationPoints_t) :: IP
LOGICAL :: SecondKindBasis, stat
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
INTEGER :: EDOFs, i, k, p, i1, i2, j1, j2, n
REAL(KIND=dp) :: Basis(2), detJ, s, e(3), t(3), fun(3), u, v
IF ((Edge % Type % ElementCode / 100) /= 2) THEN
CALL Warn('NodalToNedelecPiMatrix', 'A 1-dimensional element expected')
RETURN
END IF
IF (.NOT. ASSOCIATED(Mesh % Edges)) THEN
CALL Fatal('NodalToNedelecPiMatrix', 'Mesh edges are not associated!')
END IF
n = Edge % Type % NumberOfNodes
IF (n /= 2) CALL Fatal('NodalToNedelecPiMatrix', &
'A 2-node element expected ')
IF (PRESENT(SecondFamily)) THEN
SecondKindBasis = SecondFamily
ELSE
SecondKindBasis = .FALSE.
END IF
IF (SecondKindBasis) THEN
EDOFs = 2
ELSE
EDOFs = 1
END IF
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Edge % NodeIndexes)
t(1) = Nodes % x(2) - Nodes % x(1)
t(2) = Nodes % y(2) - Nodes % y(1)
t(3) = Nodes % z(2) - Nodes % z(1)
i1 = Edge % NodeIndexes(1)
i2 = Edge % NodeIndexes(2)
IF (ParEnv % PEs > 1) THEN
j1 = Mesh % ParallelInfo % GlobalDOFs(i1)
j2 = Mesh % ParallelInfo % GlobalDOFs(i2)
ELSE
j1 = i1
j2 = i2
END IF
IF (j2 < j1) t = -t
t = t/SQRT(SUM(t**2))
PiMat = 0.0_dp
IP = GaussPoints(Edge)
DO p=1,IP % n
stat = ElementInfo(Edge, Nodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis)
s = IP % s(p) * DetJ
DO k=1,dim
e(:) = 0.0_dp
e(k) = 1.0_dp
DO i=1,n
fun(:) = Basis(i) * e(:)
IF (SecondKindBasis) THEN
u = IP % u(p)
v = 0.5d0*(1.0d0-sqrt(3.0d0)*u)
PiMat(1,3*(i-1)+k) = PiMat(1,3*(i-1)+k) + s * SUM(fun*t)*v
v = 0.5d0*(1.0d0+sqrt(3.0d0)*u)
PiMat(2,3*(i-1)+k) = PiMat(2,3*(i-1)+k) + s * SUM(fun*t)*v
ELSE
PiMat(1,3*(i-1)+k) = PiMat(1,3*(i-1)+k) + s * SUM(fun*t)
END IF
END DO
END DO
END DO
END SUBROUTINE NodalToNedelecPiMatrix
SUBROUTINE NodalToNedelecPiMatrix_Faces(PiMat, Face, Mesh, dim, BasisDegree)
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,12)
TYPE(Element_t), POINTER, INTENT(IN) :: Face
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh
INTEGER, INTENT(IN) :: dim
INTEGER, OPTIONAL, INTENT(IN) :: BasisDegree
TYPE(Nodes_t), SAVE :: Nodes, EdgeNodes
TYPE(Element_t), POINTER, SAVE :: Edge => NULL()
TYPE(GaussIntegrationPoints_t) :: IP
LOGICAL :: Parallel, SecondOrder, stat
INTEGER :: FDOFs, istat, i, j, k, p, i1, i2, j1, j2, n
INTEGER :: FaceIndices(4), SquareFaceMap(4)
REAL(KIND=dp) :: t(3), WorkPiMat(2,12), D1, D2
REAL(KIND=dp) :: Basis(2), detJ, s, e(3), fun(3), wrkfun(3), u, v
CHARACTER(*), PARAMETER :: Caller = 'NodalToNedelecPiMatrix_Faces'
IF (.NOT. (Face % Type % ElementCode / 100 /= 3 .OR. &
Face % Type % ElementCode / 100 /= 4)) THEN
CALL Fatal(Caller, 'A 2-dimensional element expected')
END IF
IF (Face % Type % ElementCode / 100 == 3) THEN
CALL Fatal(Caller, 'Cannot handle triangular faces yet')
END IF
IF (.NOT. ASSOCIATED(Mesh % Faces)) THEN
CALL Fatal(Caller, 'Mesh faces are not associated!')
END IF
IF ( PRESENT(BasisDegree) ) THEN
SecondOrder = BasisDegree > 1
IF (SecondOrder) CALL Fatal(Caller, 'Cannot handle higher-order basis yet')
ELSE
SecondOrder = .FALSE.
END IF
n = Face % Type % NumberOfNodes
FDOFs = 2
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Face % NodeIndexes)
IF (.NOT. ASSOCIATED(EdgeNodes % x)) THEN
ALLOCATE(EdgeNodes % x(2), EdgeNodes % y(2), EdgeNodes % z(2), stat = istat)
END IF
IF (.NOT. ASSOCIATED(Edge)) THEN
ALLOCATE(Edge, stat=istat)
Edge % Type => GetElementType(202, .FALSE.)
END IF
IP = GaussPoints(Edge, EdgeBasis = .TRUE.)
WorkPiMat(:,:) = 0.0_dp
DO j=1,FDOFs
SELECT CASE(j)
CASE(1)
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(2))
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(2))
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(2))
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(4) + Nodes % x(1))
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(4) + Nodes % y(1))
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(4) + Nodes % z(1))
CASE(2)
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(4))
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(4))
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(4))
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(2) + Nodes % x(1))
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(2) + Nodes % y(1))
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(2) + Nodes % z(1))
END SELECT
t(1) = EdgeNodes % x(2) - EdgeNodes % x(1)
t(2) = EdgeNodes % y(2) - EdgeNodes % y(1)
t(3) = EdgeNodes % z(2) - EdgeNodes % z(1)
t = t/SQRT(SUM(t**2))
DO p=1,IP % n
stat = ElementInfo(Edge, EdgeNodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis)
s = IP % s(p) * DetJ
DO i=1,Face % Type % NumberOfNodes
SELECT CASE(i)
CASE(1,4)
wrkfun = 0.5_dp * Basis(1)
CASE(2,3)
wrkfun = 0.5_dp * Basis(2)
CASE DEFAULT
CALL Fatal(Caller, 'The lowest-order mesh supposed')
END SELECT
DO k=1,dim
e(:) = 0.0_dp
e(k) = 1.0_dp
fun(:) = wrkfun * e(:)
WorkPiMat(j,3*(i-1)+k) = WorkPiMat(j,3*(i-1)+k) + s * SUM(fun*t)
END DO
END DO
END DO
END DO
SquareFaceMap(:) = (/ 1,2,3,4 /)
FaceIndices(1:n) = Face % NodeIndexes(SquareFaceMap(1:n))
Parallel = ParEnv % PEs > 1
IF (Parallel) FaceIndices(1:n) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(1:n))
CALL SquareFaceDofsOrdering(I1,I2,D1,D2,FaceIndices)
PiMat(1,:) = D1 * WorkPiMat(I1,:)
PiMat(2,:) = D2 * WorkPiMat(I2,:)
END SUBROUTINE NodalToNedelecPiMatrix_Faces
SUBROUTINE NodalToNedelecInterpolation_GlobalMatrix(Mesh, NodalVar, &
VectorElementVar, GlobalPiMat, cdim, UseNodalPermArg )
IMPLICIT NONE
TYPE(Mesh_t), POINTER :: Mesh
TYPE(Variable_t), POINTER, INTENT(IN) :: NodalVar
TYPE(Variable_t), POINTER, INTENT(IN) :: VectorElementVar
TYPE(Matrix_t), POINTER :: GlobalPiMat
INTEGER, OPTIONAL :: cdim
LOGICAL, OPTIONAL :: UseNodalPermArg
INTEGER, PARAMETER :: MaxEDOFs = 2
INTEGER, PARAMETER :: MaxFDOFs = 2
TYPE(Nodes_t), SAVE :: Nodes
TYPE(Element_t), POINTER :: Edge, Face
LOGICAL :: PiolaVersion, SecondKindBasis, SecondOrder
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
INTEGER :: dim, istat, EDOFs, i, j, k, i1, i2, k1, k2, nd, dofi, i0, k0, &
vdofs, edgej, facej
REAL(KIND=dp) :: PiMat(MaxEDOFs,6), FacePiMat(MaxFDOFs,12)
CHARACTER(*), PARAMETER :: Caller = 'NodalToNedelecInterpolation_GlobalMatrix'
LOGICAL :: UseNodalPerm, DoFatal
INTEGER, POINTER :: VectorPerm(:), NodalPerm(:)
CALL Info(Caller,'Creating interpolation matrix between H1 and H(curl)!')
DoFatal = .FALSE.
IF (.NOT. ASSOCIATED(NodalVar)) THEN
CALL Warn(Caller, 'H1 variable is not associated!')
DoFatal = .TRUE.
END IF
IF (.NOT. ASSOCIATED(VectorElementVar)) THEN
CALL Warn(Caller, 'H(curl) variable is not associated!')
DoFatal = .TRUE.
END IF
IF(.NOT. ASSOCIATED(Mesh)) THEN
CALL Warn(Caller, 'Mesh structure is not associated!')
DoFatal = .TRUE.
END IF
IF (ASSOCIATED(GlobalPiMat)) THEN
CALL Warn(Caller, 'Matrix structure has already been created')
DoFatal = .TRUE.
END IF
IF(DoFatal) CALL Fatal(Caller,'Cannot continue with these errors!')
IF (.NOT. ASSOCIATED(Mesh % Edges)) CALL Fatal(Caller, 'Mesh edges not associated!')
IF (PRESENT(cdim)) THEN
dim = cdim
ELSE
dim = 3
END IF
vdofs = VectorElementVar % DOFs
IF(vdofs /=1 .AND. vdofs /= 2) THEN
CALL Fatal(Caller,'Vector dofs only makes sense for values 1 (real) and 2 (complex)!')
END IF
NodalPerm => NodalVar % Perm
UseNodalPerm = .TRUE.
IF ( PRESENT(UseNodalPermArg) ) UseNodalPerm = UseNodalPermArg
VectorPerm => VectorElementVar % Perm
IF (NodalVar % DOFs /= dim * vdofs) CALL Fatal(Caller, &
'Coordinate system dimension and DOF counts are not as expected')
CALL EdgeElementStyle(VectorElementVar % Solver % Values, PiolaVersion, SecondKindBasis, &
SecondOrder, Check = .TRUE.)
IF (SecondKindBasis) THEN
EDOFs = 2
ELSE
EDOFs = 1
END IF
GlobalPiMat => AllocateMatrix()
GlobalPiMat % Format = MATRIX_LIST
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, SIZE(VectorElementVar % Values), &
SIZE(NodalVar % Values), 0.0_dp)
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, 1, 1, 0.0_dp)
IF (.NOT. ALLOCATED(Ind)) THEN
ALLOCATE( Ind(Mesh % MaxElementDOFs), stat=istat )
END IF
DO edgej=1, Mesh % NumberOfEdges
Edge => Mesh % Edges(edgej)
CALL NodalToNedelecPiMatrix(PiMat, Edge, Mesh, dim, SecondKindBasis)
nd = mGetElementDOFs(Ind, Edge, VectorElementVar % Solver)
i1 = Edge % NodeIndexes(1)
i2 = Edge % NodeIndexes(2)
IF ( UseNodalPerm ) THEN
k1 = NodalPerm(i1)
k2 = NodalPerm(i2)
ELSE
k1 = i1
k2 = i2
END IF
DO dofi=1, vdofs
DO j=1,EDOFs
k = VectorPerm(Ind(j))
k0 = vdofs*(k-1)+dofi
DO i=1,dim
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k1-1)+vdofs*(i-1)+dofi, PiMat(j,i) )
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k2-1)+vdofs*(i-1)+dofi, PiMat(j,3+i) )
END DO
END DO
END DO
END DO
IF (ASSOCIATED(Mesh % Faces)) THEN
DO facej=1, Mesh % NumberOfFaces
Face => Mesh % Faces(facej)
IF (Face % BDOFs < 1) CYCLE
IF ( Face % Type % ElementCode /100 == 3 ) CYCLE
nd = mGetElementDOFs(Ind, Face, VectorElementVar % Solver)
i0 = 0
DO k=1,Face % Type % NumberOfEdges
Edge => Mesh % Edges(Face % EdgeIndexes(k))
EDOFs = Edge % BDOFs
IF (EDOFs < 1) CYCLE
i0 = i0 + EDOFs
END DO
CALL NodalToNedelecPiMatrix_Faces(FacePiMat, Face, Mesh, dim, BasisDegree = 1)
DO dofi=1, vdofs
DO j=1,Face % BDOFs
k2 = VectorPerm(Ind(j+i0))
k0 = vdofs*(k2-1)+dofi
DO i=1,Face % TYPE % NumberOfNodes
k1 = Face % NodeIndexes(i)
IF(UseNodalPerm) k1 = NodalPerm(k1)
DO k=1,dim
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k1-1)+vdofs*(k-1)+dofi, FacePiMat(j,3*(i-1)+k) )
END DO
END DO
END DO
END DO
END DO
END IF
CALL List_toCRSMatrix(GlobalPiMat)
CALL Info(Caller, 'Created Projection Matrix: H1 -> H(curl)', Level=6)
END SUBROUTINE NodalToNedelecInterpolation_GlobalMatrix
SUBROUTINE NodalGradientToNedelecPiMatrix(PiMat, Edge, Mesh, SecondFamily)
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,2)
TYPE(Element_t), POINTER, INTENT(IN) :: Edge
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh
LOGICAL, OPTIONAL, INTENT(IN) :: SecondFamily
TYPE(Nodes_t), SAVE :: Nodes
TYPE(GaussIntegrationPoints_t) :: IP
LOGICAL :: SecondKindBasis, stat
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
INTEGER :: EDOFs, i, k, p, i1, i2, j1, j2, n
REAL(KIND=dp) :: dBasis(2,3), Basis(2), detJ, s, e(3), t(3), fun(3), u, v
IF ((Edge % Type % ElementCode / 100) /= 2) THEN
CALL Warn('NodalGradientToNedelecPiMatrix', 'A 1-dimensional element expected')
RETURN
END IF
IF (.NOT. ASSOCIATED(Mesh % Edges)) THEN
CALL Fatal('NodalGradientToNedelecPiMatrix', 'Mesh edges are not associated!')
END IF
n = Edge % Type % NumberOfNodes
IF (n /= 2) CALL Fatal('NodalGradientToNedelecPiMatrix', &
'A 2-node element expected ')
IF (PRESENT(SecondFamily)) THEN
SecondKindBasis = SecondFamily
ELSE
SecondKindBasis = .FALSE.
END IF
IF (SecondKindBasis) THEN
EDOFs = 2
ELSE
EDOFs = 1
END IF
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Edge % NodeIndexes)
t(1) = Nodes % x(2) - Nodes % x(1)
t(2) = Nodes % y(2) - Nodes % y(1)
t(3) = Nodes % z(2) - Nodes % z(1)
i1 = Edge % NodeIndexes(1)
i2 = Edge % NodeIndexes(2)
IF (ParEnv % PEs > 1) THEN
j1 = Mesh % ParallelInfo % GlobalDOFs(i1)
j2 = Mesh % ParallelInfo % GlobalDOFs(i2)
ELSE
j1 = i1
j2 = i2
END IF
IF (j2 < j1) t = -t
t = t/SQRT(SUM(t**2))
PiMat = 0.0_dp
IP = GaussPoints(Edge)
DO p=1,IP % n
stat = ElementInfo(Edge, Nodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis, dBasis)
s = IP % s(p) * DetJ
DO i=1,n
fun(:) = dBasis(i,:)
IF (SecondKindBasis) THEN
u = IP % u(p)
v = 0.5d0*(1.0d0-sqrt(3.0d0)*u)
PiMat(1,i) = PiMat(1,i) + s * SUM(fun*t)*v
v = 0.5d0*(1.0d0+sqrt(3.0d0)*u)
PiMat(2,i) = PiMat(2,i) + s * SUM(fun*t)*v
ELSE
PiMat(1,i) = PiMat(1,i) + s * SUM(fun*t)
END IF
END DO
END DO
END SUBROUTINE NodalGradientToNedelecPiMatrix
SUBROUTINE NodalGradientToNedelecPiMatrix_Faces(PiMat, Face, Mesh, BasisDegree)
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,4)
TYPE(Element_t), POINTER, INTENT(IN) :: Face
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh
INTEGER, OPTIONAL, INTENT(IN) :: BasisDegree
TYPE(Nodes_t), SAVE :: Nodes, EdgeNodes
TYPE(Element_t), POINTER, SAVE :: Edge => NULL()
TYPE(GaussIntegrationPoints_t) :: IP
LOGICAL :: Parallel, SecondOrder, stat
INTEGER :: FDOFs, istat, i, j, k, p, i1, i2, j1, j2, n
INTEGER :: FaceIndices(4), SquareFaceMap(4)
REAL(KIND=dp) :: t(3), WorkPiMat(2,4), D1, D2
REAL(KIND=dp) :: Basis(4), detJ, s, fun(3), u, v, grad0(4,3)
CHARACTER(*), PARAMETER :: Caller = 'NodalGradientToNedelecPiMatrix_Faces'
IF (.NOT. (Face % Type % ElementCode / 100 /= 3 .OR. &
Face % Type % ElementCode / 100 /= 4)) THEN
CALL Fatal(Caller, 'A 2-dimensional element expected')
END IF
IF (Face % Type % ElementCode / 100 == 3) THEN
CALL Fatal(Caller, 'Cannot handle triangular faces yet')
END IF
IF (.NOT. ASSOCIATED(Mesh % Faces)) THEN
CALL Fatal(Caller, 'Mesh faces are not associated!')
END IF
IF ( PRESENT(BasisDegree) ) THEN
SecondOrder = BasisDegree > 1
IF (SecondOrder) CALL Fatal(Caller, 'Cannot handle higher-order basis yet')
ELSE
SecondOrder = .FALSE.
END IF
n = Face % Type % NumberOfNodes
FDOFs = 2
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Face % NodeIndexes)
IF (.NOT. ASSOCIATED(EdgeNodes % x)) THEN
ALLOCATE(EdgeNodes % x(2), EdgeNodes % y(2), EdgeNodes % z(2), stat = istat)
END IF
IF (.NOT. ASSOCIATED(Edge)) THEN
ALLOCATE(Edge, stat=istat)
Edge % Type => GetElementType(202, .FALSE.)
END IF
IP = GaussPoints(Edge, EdgeBasis = .TRUE.)
WorkPiMat(:,:) = 0.0_dp
stat = ElementInfo(Face, Nodes, 0.0_dp, 0.0_dp, 0.0_dp, DetJ, Basis, grad0)
DO j=1,FDOFs
SELECT CASE(j)
CASE(1)
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(2))
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(2))
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(2))
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(4) + Nodes % x(1))
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(4) + Nodes % y(1))
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(4) + Nodes % z(1))
CASE(2)
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(4))
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(4))
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(4))
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(2) + Nodes % x(1))
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(2) + Nodes % y(1))
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(2) + Nodes % z(1))
END SELECT
t(1) = EdgeNodes % x(2) - EdgeNodes % x(1)
t(2) = EdgeNodes % y(2) - EdgeNodes % y(1)
t(3) = EdgeNodes % z(2) - EdgeNodes % z(1)
t = t/SQRT(SUM(t**2))
DO p=1,IP % n
stat = ElementInfo(Edge, EdgeNodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis)
s = IP % s(p) * DetJ
DO i=1,Face % Type % NumberOfNodes
fun(:) = grad0(i,:)
WorkPiMat(j,i) = WorkPiMat(j,i) + s * SUM(fun*t)
END DO
END DO
END DO
SquareFaceMap(:) = (/ 1,2,3,4 /)
FaceIndices(1:n) = Face % NodeIndexes(SquareFaceMap(1:n))
Parallel = ParEnv % PEs > 1
IF (Parallel) FaceIndices(1:n) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(1:n))
CALL SquareFaceDofsOrdering(I1,I2,D1,D2,FaceIndices)
PiMat(1,:) = D1 * WorkPiMat(I1,:)
PiMat(2,:) = D2 * WorkPiMat(I2,:)
END SUBROUTINE NodalGradientToNedelecPiMatrix_Faces
SUBROUTINE NodalGradientToNedelecInterpolation_GlobalMatrix(Mesh, NodalVar, &
VectorElementVar, GlobalPiMat, cdim, UseNodalPermArg )
IMPLICIT NONE
TYPE(Mesh_t), POINTER :: Mesh
TYPE(Variable_t), POINTER, INTENT(IN) :: NodalVar
TYPE(Variable_t), POINTER, INTENT(IN) :: VectorElementVar
TYPE(Matrix_t), POINTER :: GlobalPiMat
INTEGER, OPTIONAL :: cdim
LOGICAL, OPTIONAL :: UseNodalPermArg
INTEGER, PARAMETER :: MaxEDOFs = 2
INTEGER, PARAMETER :: MaxFDOFs = 2
TYPE(Element_t), POINTER :: Edge, Face
LOGICAL :: PiolaVersion, SecondKindBasis, SecondOrder
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
INTEGER :: EDOFs, dof, i, istat, i0, j, k, l, m, nd, ndofs, p, q, vdofs, dim
REAL(KIND=dp) :: PiMat(MaxEDOFs,2), FacePiMat(MaxFDOFs,4)
CHARACTER(*), PARAMETER :: Caller = 'NodalGradientToNedelecInterpolation_GlobalMatrix'
LOGICAL :: UseNodalPerm
INTEGER, POINTER :: VectorPerm(:), NodalPerm(:)
IF (.NOT. ASSOCIATED(NodalVar) .OR. .NOT. ASSOCIATED(VectorElementVar)) THEN
CALL Fatal(Caller, 'H1 or H(curl) variable is not associated')
END IF
IF (ASSOCIATED(Mesh)) THEN
IF (.NOT. ASSOCIATED(Mesh % Edges)) CALL Fatal(Caller, 'Mesh edges not associated!')
ELSE
CALL Fatal(Caller, 'Mesh structure is not associated')
END IF
IF (ASSOCIATED(GlobalPiMat)) THEN
CALL Fatal(Caller, 'Matrix structure has already been created')
END IF
IF (PRESENT(cdim)) THEN
dim = cdim
ELSE
dim = 3
END IF
vdofs = VectorElementVar % DOFs
ndofs = NodalVar % DOFs
IF (ndofs /= dim * vdofs .AND. ndofs /= vdofs) CALL Fatal(Caller, &
'Coordinate system dimension and DOF counts are not as expected')
UseNodalPerm = .TRUE.
NodalPerm => NodalVar % Perm
IF(PRESENT(UseNodalPermArg)) UseNodalPerm = UseNodalPermArg
VectorPerm => VectorElementVar % Perm
CALL EdgeElementStyle(VectorElementVar % Solver % Values, PiolaVersion, SecondKindBasis, &
SecondOrder, Check = .TRUE.)
IF (SecondKindBasis) THEN
EDOFs = 2
ELSE
EDOFs = 1
END IF
GlobalPiMat => AllocateMatrix()
GlobalPiMat % Format = MATRIX_LIST
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, SIZE(VectorElementVar % Values), &
SIZE(NodalVar % Values), 0.0_dp)
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, 1, 1, 0.0_dp)
IF (.NOT. ALLOCATED(Ind)) THEN
ALLOCATE( Ind(Mesh % MaxElementDOFs), stat=istat )
END IF
DO j=1, Mesh % NumberOfEdges
Edge => Mesh % Edges(j)
CALL NodalGradientToNedelecPiMatrix(PiMat, Edge, Mesh, SecondKindBasis)
nd = mGetElementDOFs(Ind, Edge, VectorElementVar % Solver)
DO dof=1,vDOFs
DO i=1,Edge % Type % NumberOfNodes
m = Edge % NodeIndexes(i)
IF ( UseNodalPerm ) m = NodalPerm(m)
l = ndofs*(m-1) + dof
DO p=1,EDOFs
q = VectorPerm(Ind(p))
k = vdofs*(q-1)+dof
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k, l, PiMat(p,i))
END DO
END DO
END DO
END DO
IF (ASSOCIATED(Mesh % Faces)) THEN
DO j=1, Mesh % NumberOfFaces
Face => Mesh % Faces(j)
IF (Face % BDOFs < 1) CYCLE
IF ( Face % Type % ElementCode /100 == 3 ) CYCLE
nd = mGetElementDOFs(Ind, Face, VectorElementVar % Solver)
i0 = 0
DO k=1,Face % Type % NumberOfEdges
Edge => Mesh % Edges(Face % EdgeIndexes(k))
EDOFs = Edge % BDOFs
IF (EDOFs < 1) CYCLE
i0 = i0 + EDOFs
END DO
CALL NodalGradientToNedelecPiMatrix_Faces(FacePiMat, Face, Mesh, BasisDegree = 1)
DO dof=1,vdofs
DO p=1,Face % BDOFs
k = VectorPerm(Ind(p+i0))
k = vdofs*(k-1)+dof
DO i=1,Face % TYPE % NumberOfNodes
m = Face % NodeIndexes(i)
IF ( UseNodalPerm ) m = NodalPerm(m)
l = ndofs*(m-1) + dof
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k, l, FacePiMat(p,i))
END DO
END DO
END DO
END DO
END IF
CALL List_toCRSMatrix(GlobalPiMat)
CALL Info(Caller, 'Created Gradient Matrix: grad(H1) -> H(curl)', Level=6)
END SUBROUTINE NodalGradientToNedelecInterpolation_GlobalMatrix
END MODULE Interpolation