#include "Includes.h" Module SurfaceClass use SMConstants use HexMeshClass use ZoneClass use ElementConnectivityDefinitions, only: NODES_PER_FACE, FACES_PER_ELEMENT Implicit None private public Surface_t, SurfaceEdge, SurfaceFace_t, SurfaceElement_t type SurfaceEdge real(kind=RP), dimension(3,2) :: corners contains procedure :: construct => EdgeConstruct procedure :: isEqual => EdgeIsEqualToEdge procedure :: isConnected => EdgeIsConnectedToEdge procedure :: getBCPostion => EdgeGetBCPosition end type SurfaceEdge type SurfaceFace_t class(SurfaceEdge), dimension(:), allocatable :: edges integer :: eID integer :: globaleID integer :: fID integer :: numberOfConnections contains procedure :: construct => FaceConstruct procedure :: destruct => FaceDestruct procedure :: setNoConnections => FaceSetNoOfConnections procedure :: isConnected => FaceIsConnected procedure :: isFullConnected => FaceIsFullConnected procedure :: isTwiceEdConnected => FaceIsConnectedByEdgeTwice procedure :: getBCPostion => FaceGetBCPosition procedure :: shareCorner => FaceShareCorner procedure :: reconstructPeriod => FaceReconstructPeriodic procedure :: updateEdgesPeriod => FaceUpdateEdgesOnPeriodic end type SurfaceFace_t type SurfaceElement_t class(SurfaceFace_t), dimension(:), allocatable :: faces integer :: eID integer :: globaleID integer :: fID integer, dimension(:), allocatable :: extrafIDs logical :: needSecondFace logical :: isInBCZone contains procedure :: construct => ElementConstruct procedure :: destruct => ElementDestruct procedure :: updateIsInZone => ElementUpdateIsInZone procedure :: setNeedSecond => ElementSetNeedSecond procedure :: setNeedNotSecond => ElementSetNotNeedSecond procedure :: getNotConnectedN => ElementGetNotConnectedN procedure :: reconstructPeriod => ElementReconstructPeriodic end type SurfaceElement_t type Surface_t integer :: totalNumberOfPoints integer :: totalNumberOfFaces integer, dimension(:), allocatable :: globaleIDs integer, dimension(:), allocatable :: fIDs class(Zone_t), allocatable :: surfaceZone character(len=LINE_LENGTH) :: Name character(len=LINE_LENGTH) :: fileName contains procedure :: construct => SurfaceConstruct procedure :: destruct => SurfaceDestruct procedure :: writeToTecplot => SurfaceWriteSingleZoneToTecplot procedure :: saveToFile => SurfaceSaveToFile end type Surface_t integer, parameter :: NO_OF_OUTPUTVARIABLES = 2 character(len=8), parameter :: PRECISION_FORMAT = "(E18.10)" contains ! !//////////////////////////////////////////////////////////////////////// ! surface class methods !//////////////////////////////////////////////////////////////////////// ! Subroutine SurfaceConstruct(self, mesh, fileName, eIDs, gIDs, fIDs, N) use FileReadingUtilities, only: getFileName, RemovePath use MPI_Utilities implicit none class(Surface_t) :: self type(HexMesh), intent(in) :: mesh character(len=LINE_LENGTH), intent(in) :: fileName integer, dimension(N), intent(in) :: eIDs, gIDs, fIDs integer, intent(in) :: N ! local variables integer :: totalN totalN = N self % totalNumberOfFaces = totalN self % totalNumberOfPoints = NODES_PER_FACE * totalN self % fileName = fileName self % name = RemovePath(getFileName(fileName)) allocate(self % surfaceZone) call self % surfaceZone % CreateFictitious(-1, self % name, totalN, fIDs) allocate( self % globaleIDs(totalN), self % fIDs(totalN) ) self % globaleIDs = gIDs self % fIDs = fIDs print *, "surf constructed" End Subroutine SurfaceConstruct ! !//////////////////////////////////////////////////////////////////////// ! Subroutine SurfaceDestruct(self) implicit none class(Surface_t), intent(inout) :: self safedeallocate(self % globaleIDs) safedeallocate(self % fIDs) safedeallocate(self % surfaceZone) End Subroutine SurfaceDestruct ! !//////////////////////////////////////////////////////////////////////// ! Subroutine SurfaceWriteSingleZoneToTecplot(self, mesh, meshName) use FileReadingUtilities, only: getFileName implicit none class(Surface_t) :: self type(HexMesh), intent(in) :: mesh character(len=LINE_LENGTH), intent(in) :: meshName ! local variables integer :: fd, fID, i, row character(len=LINE_LENGTH) :: formatout, title, tecplotFileName integer :: Nx,Ny integer, dimension(NODES_PER_FACE) :: corners logical :: facesHasErrors integer :: totalNumberOfPoints, totalNumberOfFaces, skipped totalNumberOfPoints = self % totalNumberOfPoints totalNumberOfFaces = self % totalNumberOfFaces tecplotFileName = trim(getFileName(self % fileName)) // ".tec" open ( newunit = fD , file = trim(tecplotFileName) , status = "unknown" , action = "write" ) ! Add the title ! ------------- write(title,'(A,A,A)') '"Generated from ',trim(meshName),'"' write(fd,'(A,A)') "TITLE = ", trim(title) ! Add the variables ! ----------------- write(fd,'(A,A)') 'VARIABLES = "x","y","z"', '"eID","fID"' ! Add the zone info ! ----------------- write(fd,'(A,I0,A,I0,A,A,A)') "ZONE N=", totalNumberOfPoints,", E=", totalNumberOfFaces, & ',ET=QUADRILATERAL, F=FEPOINT, T="surface_', trim(self % Name), '"' ! Write the points ! ---------------- write(formatout,'(A,I0,A,A)') "(",3+no_of_outputVariables,PRECISION_FORMAT,")" do fID = 1 , self % totalNumberOfFaces ! if (self % fIDs(fID) .eq. 0) cycle associate ( f => mesh % faces(self % fIDs(fID)) ) Nx = f % Nf(1) Ny = f % Nf(2) associate (x => f % geom % x) write(fd,trim(formatout)) x(:,0,0), real(self % globaleIDs(fID),KIND=RP), real(self % fIDs(fID),KIND=RP) write(fd,trim(formatout)) x(:,Nx,0), real(self % globaleIDs(fID),KIND=RP), real(self % fIDs(fID),KIND=RP) write(fd,trim(formatout)) x(:,Nx,Ny), real(self % globaleIDs(fID),KIND=RP), real(self % fIDs(fID),KIND=RP) write(fd,trim(formatout)) x(:,0,Ny), real(self % globaleIDs(fID),KIND=RP), real(self % fIDs(fID),KIND=RP) end associate end associate end do ! Write the connections ! --------------------- row = 0 do fID = 1 , self % totalNumberOfFaces ! if (self % fIDs(fID) .eq. 0) cycle row = row + 1 corners = [(i,i=1,NODES_PER_FACE)] + NODES_PER_FACE*(row-1) write(fd,*) corners end do close(fd) End Subroutine SurfaceWriteSingleZoneToTecplot ! !//////////////////////////////////////////////////////////////////////// ! Subroutine SurfaceSaveToFile(self, mesh, saveNodes) implicit none class(Surface_t) :: self type(HexMesh), intent(in) :: mesh logical, intent(in) :: saveNodes ! local variables integer :: fd, fID open ( newunit = fD , file = trim(self % fileName) , status = "unknown" , action = "write" ) ! Add total numberOfFaces ! ------------- write(fd,*) self % totalNumberOfFaces write(fd,*) saveNodes ! Write the points ! ---------------- if (saveNodes) then do fID = 1 , self % totalNumberOfFaces write(fd,*) self % globaleIDs(fID), self % fIDs(fID), mesh % faces(self % fIDs(fID)) % nodeIDs end do else do fID = 1 , self % totalNumberOfFaces write(fd,*) self % globaleIDs(fID), self % fIDs(fID) end do end if close(fd) END SUBROUTINE SurfaceSaveToFile ! !//////////////////////////////////////////////////////////////////////// ! edge methods !//////////////////////////////////////////////////////////////////////// ! Subroutine EdgeConstruct(self, f, side) ! ***************************************************************************** ! This subroutine creates and edge based on the desired side ! being counterclock numbered ! ----(3)---- ! | | ! | | ! (4) (2) ! | | ! | | ! ----(1)---- ! ***************************************************************************** use FaceClass implicit none class(SurfaceEdge) :: self type(Face), intent(in) :: f integer, intent(in) :: side !local variables integer :: Nx, Ny Nx = f % Nf(1) Ny = f % Nf(2) associate(x => f % geom % x) select case (side) case (1) self % corners(:,1) = x(:,0,0) self % corners(:,2) = x(:,Nx,0) case (2) self % corners(:,1) = x(:,Nx,0) self % corners(:,2) = x(:,Nx,Ny) case (3) self % corners(:,1) = x(:,Nx,Ny) self % corners(:,2) = x(:,0,Ny) case (4) self % corners(:,1) = x(:,0,Ny) self % corners(:,2) = x(:,0,0) end select end associate End Subroutine EdgeConstruct ! !//////////////////////////////////////////////////////////////////////// ! Function EdgeIsEqualToEdge(self, otherEdge) result(isEqual) use Utilities, only: AlmostEqual implicit none class(SurfaceEdge) :: self class(SurfaceEdge), intent(in) :: otherEdge logical :: isEqual !local variables integer :: i, j, equalCorners, usedCorner isEqual = .false. equalCorners = 0 usedCorner = 0 do i = 1, 2 ext_corner_loop: do j = 1, 2 if (j .eq. usedCorner) cycle ext_corner_loop if (testEqualCorners(self % corners(:,i), otherEdge % corners(:,j))) then equalCorners = equalCorners + 1 usedCorner = j exit ext_corner_loop end if end do ext_corner_loop end do if (equalCorners .eq. 2) isEqual = .true. End Function EdgeIsEqualToEdge ! !//////////////////////////////////////////////////////////////////////// ! Function EdgeIsConnectedToEdge(self, otherEdge) result(isConnected) use Utilities, only: AlmostEqual implicit none class(SurfaceEdge) :: self class(SurfaceEdge), intent(in) :: otherEdge logical :: isConnected !local variables integer :: i, j, equalCorners, usedCorner isConnected = .false. equalCorners = 0 usedCorner = 0 do i = 1, 2 ext_corner_loop: do j = 1, 2 if (j .eq. usedCorner) cycle ext_corner_loop if (testEqualCorners(self % corners(:,i), otherEdge % corners(:,j))) then equalCorners = equalCorners + 1 usedCorner = j exit ext_corner_loop end if end do ext_corner_loop end do if (equalCorners .eq. 1) isConnected = .true. End Function EdgeIsConnectedToEdge ! !//////////////////////////////////////////////////////////////////////// ! Function EdgeGetBCPosition(self, BCDimension) result(pos) class(SurfaceEdge) :: self integer, intent(in) :: BCDimension real(kind=RP), dimension(2) :: pos pos(1) = self % corners(BCDimension,1) pos(2) = self % corners(BCDimension,2) End Function EdgeGetBCPosition ! !//////////////////////////////////////////////////////////////////////// ! surface face methods !//////////////////////////////////////////////////////////////////////// ! Subroutine FaceConstruct(self, mesh, eID, geID, fID) implicit none class(SurfaceFace_t) :: self type(HexMesh), intent(in) :: mesh integer, intent(in) :: eID, geID, fID !local variables integer :: i self % eID = eID self % globaleID = geID self % fID = fID allocate(self % edges(NODES_PER_FACE)) do i = 1, NODES_PER_FACE call self % edges(i) % construct(mesh % faces(fID), i) end do ! This is change only for faces at boundaries, for non closed surfaces self % numberOfConnections = NODES_PER_FACE End Subroutine FaceConstruct ! !//////////////////////////////////////////////////////////////////////// ! Subroutine FaceDestruct(self) implicit none class(SurfaceFace_t), intent(inout) :: self safedeallocate(self % edges) End Subroutine FaceDestruct ! !//////////////////////////////////////////////////////////////////////// ! Subroutine FaceSetNoOfConnections(self, mode) use Utilities, only: AlmostEqual implicit none class(SurfaceFace_t) :: self integer, intent(in) :: mode select case (mode) case (1) self % numberOfConnections = self % numberOfConnections - 1 case (2) self % numberOfConnections = self % numberOfConnections - 2 case default self % numberOfConnections = self % numberOfConnections - 1 end select End Subroutine FaceSetNoOfConnections ! !//////////////////////////////////////////////////////////////////////// ! Function FaceIsConnected(self,otherFace) result(isCon) implicit none class(SurfaceFace_t) :: self class(SurfaceFace_t), intent(in) :: otherFace logical :: isCon !local variables integer :: k, edgeID isCon = .false. do edgeID = 1, NODES_PER_FACE do k = 1, NODES_PER_FACE if (self % edges(edgeID) % isEqual(otherFace % edges(k))) then isCon = .true. return end if end do end do End Function FaceIsConnected ! !//////////////////////////////////////////////////////////////////////// ! Function FaceIsConnectedByEdgeTwice(self, surfaceFaces, N) result(isTwice) implicit none class(SurfaceFace_t) :: self type(SurfaceFace_t), dimension(N), intent(in) :: surfaceFaces integer, intent(in) :: N logical :: isTwice !local variables integer :: i, k, equalEdges, edgeID integer, dimension(NODES_PER_FACE) :: usedEdges equalEdges = 0 usedEdges = 0 face_loop: do i = 1, N if ((self % fID .eq. surfaceFaces(i) % fID) .and. (self % globaleID .eq. surfaceFaces(i) % globaleID)) cycle face_loop if (equalEdges .ge. NODES_PER_FACE) exit face_loop this_edge_loop: do edgeID = 1, NODES_PER_FACE ext_edge_loop: do k = 1, NODES_PER_FACE if (self % edges(edgeID) % isEqual(surfaceFaces(i) % edges(k))) then equalEdges = equalEdges + 1 usedEdges(equalEdges) = edgeID cycle face_loop end if end do ext_edge_loop end do this_edge_loop end do face_loop isTwice = .false. do i = 1, equalEdges do k = i+1, equalEdges if (usedEdges(i) .eq. usedEdges(k)) then isTwice = .true. return end if end do end do End Function FaceIsConnectedByEdgeTwice ! !//////////////////////////////////////////////////////////////////////// ! Function FaceIsFullConnected(self, surfaceFaces, N) result(isCon) implicit none class(SurfaceFace_t) :: self class(SurfaceFace_t), dimension(N), intent(in) :: surfaceFaces integer, intent(in) :: N logical :: isCon !local variables integer :: i, k, equalEdges, edgeID ! integer, dimension(NODES_PER_FACE) :: usedEdges equalEdges = 0 ! usedEdges = 0 face_loop: do i = 1, N if ((self % fID .eq. surfaceFaces(i) % fID) .and. (self % globaleID .eq. surfaceFaces(i) % globaleID)) cycle face_loop this_edge_loop: do edgeID = 1, NODES_PER_FACE ! if (any(usedEdges .eq. edgeID)) cycle this_edge_loop ext_edge_loop: do k = 1, NODES_PER_FACE if (self % edges(edgeID) % isEqual(surfaceFaces(i) % edges(k))) then equalEdges = equalEdges + 1 ! usedEdges(equalEdges) = edgeID cycle face_loop end if end do ext_edge_loop end do this_edge_loop end do face_loop isCon = .false. if (equalEdges .eq. self % numberOfConnections) isCon = .true. End Function FaceIsFullConnected ! !//////////////////////////////////////////////////////////////////////// ! Function FaceGetBCPosition(self, BCDimension, BCSide) result(pos) implicit none class(SurfaceFace_t) :: self integer, intent(in) :: BCDimension, BCSide real(kind=RP) :: pos !local variables real(kind=RP), dimension(NODES_PER_FACE,2) :: cornersPosition integer :: i do i = 1, NODES_PER_FACE cornersPosition(i,:) = self % edges(i) % getBCPostion(BCDimension) end do select case (BCSide) case (1) pos = minval(cornersPosition) case (2) pos = maxval(cornersPosition) end select End Function FaceGetBCPosition ! !//////////////////////////////////////////////////////////////////////// ! Function FaceShareCorner(self, otherFace) result(share) implicit none class(SurfaceFace_t) :: self class(SurfaceFace_t), intent(in) :: otherFace logical :: share !local variables integer :: k, edgeID share = .false. do edgeID = 1, NODES_PER_FACE do k = 1, NODES_PER_FACE if (self % edges(edgeID) % isConnected(otherFace % edges(k))) then share = .true. return end if end do end do End Function FaceShareCorner ! !//////////////////////////////////////////////////////////////////////// ! Subroutine FaceReconstructPeriodic(self, old, new, oldNewMap) class(SurfaceFace_t) :: self real(kind=RP), dimension(NDIM,NODES_PER_FACE), intent(in) :: old, new integer, dimension(NODES_PER_FACE), intent(in) :: oldNewMap !local variables integer :: i, j, k do i = 1, NODES_PER_FACE do j = 1, 2 old_loop: do k = 1, NODES_PER_FACE if (testEqualCorners(self % edges(i) % corners(:,j), old(:,k))) then self % edges(i) % corners(:,j) = new(:,oldNewMap(k)) exit old_loop end if end do old_loop end do end do End Subroutine FaceReconstructPeriodic ! !//////////////////////////////////////////////////////////////////////// ! Subroutine FaceUpdateEdgesOnPeriodic(self, surfaceElements, N) implicit none class(SurfaceFace_t) :: self class(SurfaceElement_t), dimension(N), intent(in) :: surfaceElements integer, intent(in) :: N !local variables integer :: i, j integer :: eIndex, faceIndex ! get the index of the element and the face do i = 1, N if (self % globaleID .eq. surfaceElements(i) % globaleID) then eIndex = i exit end if end do do i = 1, FACES_PER_ELEMENT if (self % fID .eq. surfaceElements(eIndex) % faces(i) % fID) then faceIndex = i exit end if end do !replace corners of edges with element ones that have been modified by reconstructPeriod do i = 1, NODES_PER_FACE self % edges(i) % corners = surfaceElements(eIndex) % faces(faceIndex) % edges(i) % corners end do End Subroutine FaceUpdateEdgesOnPeriodic ! !//////////////////////////////////////////////////////////////////////// ! surface face methods !//////////////////////////////////////////////////////////////////////// ! Subroutine ElementConstruct(self, mesh, eID, geID, fID, zoneMarkers, M, notcheckBC) implicit none class(SurfaceElement_t) :: self type(HexMesh), intent(in) :: mesh integer, intent(in) :: eID, geID, fID, M integer, dimension(M), intent(in) :: zoneMarkers logical, intent(in) :: notcheckBC !local variables integer :: i, localFaceID self % eID = eID self % globaleID = geID self % fID = fID allocate(self % faces(NUM_OF_NEIGHBORS)) do i = 1, NUM_OF_NEIGHBORS localFaceID = mesh % elements(eID) % faceIDs(i) call self % faces(i) % construct(mesh, eID, geID, localFaceID) end do self % isInBCZone = .false. if (.not. notcheckBC) call self % updateIsInZone(mesh, zoneMarkers, M) ! is false by default, will be change if necessary in future self % needSecondFace = .false. End Subroutine ElementConstruct ! !//////////////////////////////////////////////////////////////////////// ! Subroutine ElementDestruct(self) implicit none class(SurfaceElement_t), intent(inout) :: self !local variables integer :: i safedeallocate(self % extrafIDs) do i = 1, NUM_OF_NEIGHBORS call self % faces(i) % destruct() end do safedeallocate(self % faces) End Subroutine ElementDestruct ! !//////////////////////////////////////////////////////////////////////// ! Subroutine ElementUpdateIsInZone(self, mesh, zoneMarkers, M) implicit none class(SurfaceElement_t) :: self type(HexMesh), intent(in) :: mesh integer, dimension(M), intent(in) :: zoneMarkers integer, intent(in) :: M !local variables integer :: zoneID, fID, i, j integer, dimension(2) :: eIDs real(kind=RP) :: limits do j = 1, M zoneID = zoneMarkers(j) do i = 1, mesh % zones(zoneID) % no_of_faces fID = mesh % zones(zoneID) % faces(i) eIDs = mesh % faces(fID) % elementIDs if (any(eIDs .eq. self % eID)) then self % isInBCZone = .true. return end if end do end do End Subroutine ElementUpdateIsInZone ! !//////////////////////////////////////////////////////////////////////// ! Subroutine ElementSetNeedSecond(self, N) implicit none class(SurfaceElement_t) :: self integer, intent(in) :: N if (N .eq. 0) return allocate(self % extrafIDs(N)) self % needSecondFace = .true. self % extrafIDs(1) = 0 End Subroutine ElementSetNeedSecond ! !//////////////////////////////////////////////////////////////////////// ! Subroutine ElementSetNotNeedSecond(self) implicit none class(SurfaceElement_t) :: self if (.not. self % needSecondFace) return if (self % extrafIDs(1) .ne. 0) return safedeallocate(self % extrafIDs) self % needSecondFace = .false. End Subroutine ElementSetNotNeedSecond ! !//////////////////////////////////////////////////////////////////////// ! Subroutine ElementGetNotConnectedN(self, mesh, surfaceElements, N, newFaceID) use ElementConnectivityDefinitions, only: normalAxis implicit none class(SurfaceElement_t), target :: self type(HexMesh), intent(in) :: mesh type(SurfaceElement_t), dimension(N) :: surfaceElements integer, intent(in) :: N integer, intent(out) :: newFaceID !local variables integer :: i, j, k, numE, eID, targetEID, edgeIndex, targertEfID, faceIndex, normalFace integer, dimension(2) :: thisFaceIndexes class(SurfaceFace_t), pointer :: surfaceFace integer, dimension(:), allocatable :: allElements, connectedElements, possibleElements if ( .not. self % needSecondFace ) return newFaceID = 0 targetEID = 0 do i = 1, NUM_OF_NEIGHBORS if (self % faces(i) % fID .eq. self % fID) surfaceFace => self % faces(i) end do ! first try with the opposite element to the actual face ! first get the opposite element do i = 1, NUM_OF_NEIGHBORS if (mesh % elements(self % eID) % faceIDs(i) .ne. self % fID) cycle thisFaceIndexes(1) = i exit end do normalFace = normalAxis(thisFaceIndexes(1)) * (-1) ! thisFaceIndexes(2) = findloc(normalAxis, normalFace, dim=1) thisFaceIndexes(2) = maxloc(merge(1.0, 0.0, normalAxis == normalFace), dim=1) do eID = 1, N if (mesh % elements(self % eID) % Connection(thisFaceIndexes(2)) % globID .eq. surfaceElements(eID) % globaleID) then targetEID = eID exit end if end do if (targetEID .ne. 0) then ! get face of self which is connected to the element face and is connected to the old face do j = 1, NUM_OF_NEIGHBORS if (surfaceElements(targetEID) % faces(j) % fID .eq. surfaceElements(targetEID) % fID) then targertEfID = j exit end if end do do i = 1, NUM_OF_NEIGHBORS if (self % faces(i) % isConnected(surfaceElements(targetEID) % faces(targertEfID))) then if (.not. surfaceFace % isConnected(self % faces(i))) cycle newFaceID = self % faces(i) % fID self % extrafIDs(1) = newFaceID exit end if end do end if if (newFaceID .ne. 0) return ! ------------------ ! if not, get all connected elements allocate(allElements(NUM_OF_NEIGHBORS+3)) numE = 0 elems_loop: do eID = 1, N if (numE .ge. NUM_OF_NEIGHBORS+3) exit elems_loop if ( self % globaleID .eq. surfaceElements(eID) % globaleID ) cycle elems_loop this_elem_loop: do i = 1, NUM_OF_NEIGHBORS if (associated(surfaceFace, target=self % faces(i))) cycle this_elem_loop ext_elem_loop: do j = 1, NUM_OF_NEIGHBORS if (self % faces(i) % isConnected(surfaceElements(eID) % faces(j))) then numE = numE + 1 allElements(numE) = eID cycle elems_loop end if end do ext_elem_loop end do this_elem_loop end do elems_loop allocate(connectedElements(numE)) connectedElements = allElements(1:numE) deallocate(allElements) ! get the element that is not connected to the already found surface ! first get the elements with non shared edges allocate(allElements(numE)) targetEID = 0 i = 0 con_elems_loop: do k = 1, numE eID = connectedElements(k) do j = 1, NUM_OF_NEIGHBORS if (surfaceFace % isConnected(surfaceElements(eID) % faces(j))) cycle con_elems_loop end do i = i + 1 allElements(i) = eID end do con_elems_loop ! now reduce to non connected by corner if necessary numE = i if (numE .eq. 1) then targetEID = allElements(1) deallocate(allElements) else allocate(possibleElements(numE)) possibleElements = allElements(1:numE) deallocate(allElements) i = 0 pos_elems_loop: do k = 1, numE eID = possibleElements(k) do j = 1, NUM_OF_NEIGHBORS if (surfaceFace % shareCorner(surfaceElements(eID) % faces(j))) cycle pos_elems_loop end do targetEID = eID exit i = i + 1 end do pos_elems_loop ! if (i .gt. 1) print *, "Warning: more than one element is possible to be neighbour" end if if (targetEID .eq. 0) then ! print *, "Warning: for element ", self%globaleID, "a neighbour was not found" return end if ! get face of self which is connected to the element face and is connected to the old face do j = 1, NUM_OF_NEIGHBORS if (surfaceElements(targetEID) % faces(j) % fID .eq. surfaceElements(targetEID) % fID) then targertEfID = j exit end if end do do i = 1, NUM_OF_NEIGHBORS if (self % faces(i) % isConnected(surfaceElements(targetEID) % faces(targertEfID))) then if (.not. surfaceFace % isConnected(self % faces(i))) cycle newFaceID = self % faces(i) % fID self % extrafIDs(1) = newFaceID exit end if end do if (newFaceID .ne. 0) return ! if not get face of self which is connected to some other element face and is connected to the old face this_face_loop: do i = 1, NUM_OF_NEIGHBORS do j = 1, NUM_OF_NEIGHBORS if (self % faces(i) % isConnected(surfaceElements(targetEID) % faces(j))) then if (.not. surfaceFace % isConnected(self % faces(i))) cycle this_face_loop newFaceID = self % faces(i) % fID self % extrafIDs(1) = newFaceID exit this_face_loop end if end do end do this_face_loop End Subroutine ElementGetNotConnectedN ! !//////////////////////////////////////////////////////////////////////// ! Subroutine ElementReconstructPeriodic(self) use ElementConnectivityDefinitions, only: NODES_PER_ELEMENT implicit none class(SurfaceElement_t), target :: self !local variables class(SurfaceFace_t), pointer :: faceNew=>null(), faceOpposite=>null() integer :: faceConnections real(kind=RP), dimension(:,:), allocatable :: allCorners real(kind=RP), dimension(NDIM,FACES_PER_ELEMENT) :: newCorners, notChangeCorners, oldCorners integer :: i,j,k integer, dimension(NODES_PER_FACE) :: oldNewCornersMap real(kind=RP), dimension(NODES_PER_FACE) :: residual ! get the new face and the opposite to it: the one without any connections and the one with FACES_PER_ELEMENT-2 connections ! -------------------------------------- faces_loop: do i = 1, FACES_PER_ELEMENT faceConnections = 0 do j = 1, FACES_PER_ELEMENT if (i .eq. j) cycle if (self % faces(i) % isConnected(self % faces(j))) faceConnections = faceConnections + 1 end do if (faceConnections .eq. 0) then faceNew => self % faces(i) cycle faces_loop elseif (faceConnections .eq. FACES_PER_ELEMENT-2) then faceOpposite => self % faces(i) cycle faces_loop end if end do faces_loop if (.not. associated(faceNew)) then return end if ! get corners arrays ! ----------------- ! first get all the cornes, new and original allocate( allCorners(NDIM,NODES_PER_FACE*FACES_PER_ELEMENT) ) do i = 1, FACES_PER_ELEMENT do j = 1, NODES_PER_FACE allCorners(:,(i-1)*NODES_PER_FACE+j) = self % faces(i) % edges(j) % corners(:,1) end do end do ! get new and opposite corners do j = 1, NODES_PER_FACE newCorners(:,j) = faceNew % edges(j) % corners(:,1) notChangeCorners(:,j) = faceOpposite % edges(j) % corners(:,1) end do ! get old corners, the ones that are not new nor opposite corners, remove duplicated k = 0 outer: do i = 1, size(allCorners(1,:)) if (k .ge. NODES_PER_FACE) exit outer do j = 1, NODES_PER_FACE if (testEqualCorners(newCorners(:,j), allCorners(:,i))) cycle outer if (testEqualCorners(notChangeCorners(:,j), allCorners(:,i))) cycle outer end do do j = 1, k if (testEqualCorners(oldCorners(:,j), allCorners(:,i))) cycle outer end do k = k + 1 oldCorners(:,k) = allCorners(:,i) end do outer ! map old vs new ! -------------- do i = 1, NODES_PER_FACE residual = huge(newCorners(1,1)) do j = 1, NODES_PER_FACE residual(j) = POW2(oldCorners(1,i) - newCorners(1,j)) + POW2(oldCorners(2,i) - newCorners(2,j)) end do oldNewCornersMap(i) = minloc(residual,dim=1) end do do i = 1, FACES_PER_ELEMENT if (associated(faceNew, target=self % faces(i))) cycle if (associated(faceOpposite, target=self % faces(i))) cycle call self % faces(i) % reconstructPeriod(oldCorners, newCorners, oldNewCornersMap) end do nullify(faceNew, faceOpposite) End Subroutine ElementReconstructPeriodic ! !//////////////////////////////////////////////////////////////////////// ! helper procedures !//////////////////////////////////////////////////////////////////////// ! Function testEqualCorners(cornerA, cornerB) result(isEqual) use Utilities, only: AlmostEqual implicit none real(kind=RP), dimension(3), intent(in) :: cornerA, cornerB logical :: isEqual isEqual = AlmostEqual(cornerA(1), cornerB(1)) .and. & AlmostEqual(cornerA(2), cornerB(2)) .and. & AlmostEqual(cornerA(3), cornerB(3)) End Function testEqualCorners ! !//////////////////////////////////////////////////////////////////////// ! End Module SurfaceClass