Path: blob/devel/elmerice/Solvers/CalvingGeometry.F90
3203 views
!/*****************************************************************************/1! *2! * Elmer/Ice, a glaciological add-on to Elmer3! * http://elmerice.elmerfem.org4! *5! *6! * This program is free software; you can redistribute it and/or7! * modify it under the terms of the GNU General Public License8! * as published by the Free Software Foundation; either version 29! * of the License, or (at your option) any later version.10! *11! * This program is distributed in the hope that it will be useful,12! * but WITHOUT ANY WARRANTY; without even the implied warranty of13! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14! * GNU General Public License for more details.15! *16! * You should have received a copy of the GNU General Public License17! * along with this program (in file fem/GPL-2); if not, write to the18! * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,19! * Boston, MA 02110-1301, USA.20! *21! *****************************************************************************/22! ******************************************************************************23! *24! * Authors: Joe Todd25! * Email:26! * Web: http://elmerice.elmerfem.org27! *28! *29! *****************************************************************************3031!This moduled, loosely named 'CalvingGeometry' is for basically any32!reusable routines for the 3D calving model.3334MODULE CalvingGeometry3536USE Types37USE SParIterComm38USE MainUtils39USE DefUtils4041IMPLICIT NONE4243INTERFACE DoubleIntVectorSize44MODULE PROCEDURE DoubleIntVectorSizeP, DoubleIntVectorSizeA45END INTERFACE4647INTERFACE Double2DLogSize48MODULE PROCEDURE Double2DLogSizeP, Double2DLogSizeA49END INTERFACE5051INTERFACE Double3DArraySize52MODULE PROCEDURE Double3DArraySizeP, Double3DArraySizeA53END INTERFACE5455INTERFACE Double4DArraySize56MODULE PROCEDURE Double4DArraySizeP, Double4DArraySizeA57END INTERFACE5859INTERFACE DoubleDPVectorSize60MODULE PROCEDURE DoubleDPVectorSizeP, DoubleDPVectorSizeA61END INTERFACE6263!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!64! Derived type for 3D crevasse group info65!66! Using the => Next, => Prev format like67! variables_t, because there's no way of68! knowing, a priori, how many we need.69!70! Actually the only use of this is borrowed by BasalMelt3D.F90, so its misnamed...71!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!72TYPE CrevasseGroup3D_t73INTEGER :: NumberOfNodes = 0, ID = 074INTEGER, POINTER :: NodeNumbers(:) => NULL()75INTEGER, POINTER :: BoundaryNodes(:) => NULL(), FrontNodes(:) => NULL() !allocatable too?76REAL(KIND=dp) :: BoundingBox(4) !min_x, max_x, min_y, max_y7778LOGICAL :: FrontConnected !Does the group touch the terminus?79TYPE(CrevasseGroup3D_t), POINTER :: Next => NULL(), Prev => NULL()80END TYPE CrevasseGroup3D_t8182!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!83! Derived type for a calving path defined by84! the IsoSurface/Line solver.85! (doubly linked list)86!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!87TYPE CrevassePath_t88INTEGER :: NumberOfNodes = 0, NumberOfElements = 0, ID = 089INTEGER, POINTER :: NodeNumbers(:) => NULL(), ElementNumbers(:)=>NULL()90! INTEGER :: Ends(2)91REAL(KIND=dp) :: Left, Right, Extent, Orientation(2)92TYPE(CrevassePath_t), POINTER :: Next => NULL(), Prev => NULL()93LOGICAL :: Valid = .TRUE., LeftToRight = .TRUE.94END TYPE CrevassePath_t9596CONTAINS979899!Returns the neighbours of a specified node using the matrix100!provided.101!Note the current definition of neighbours:102!Two nodes are neighbours if they are in the same bulk element103!NOT ONLY if they are joined by a bar...104!User may provide an inverse perm (InvPerm_in), or else this will recomputed105!each time (which would be pretty inefficient)106FUNCTION FindNodeNeighbours(NodeNumber, Matrix, Perm, DOFs, InvPerm_in) RESULT (Neighbours)107INTEGER :: NodeNumber, NoNeighbours, i, count, DOFs !<---!!!108TYPE(Matrix_t), POINTER :: Matrix109INTEGER, POINTER :: Perm(:), Neighbours(:), InvPerm(:)110INTEGER, POINTER, OPTIONAL, INTENT(IN) :: InvPerm_in(:)111LOGICAL :: Debug112Debug = .FALSE.113114IF(PRESENT(InvPerm_in)) THEN115InvPerm => InvPerm_in116ELSE117IF(Debug) PRINT *, 'Debug FindNodeNeighbours, creating InvPerm'118InvPerm => CreateInvPerm(Perm)119END IF120121NoNeighbours = Matrix % Rows((Perm(NodeNumber)*DOFs)+1) &122- Matrix % Rows(Perm(NodeNumber)*DOFs)123124IF(MOD(NoNeighbours, DOFs).NE. 0) &125CALL FATAL("Geometry","This shouldn't have happened...")126127!Each neighbour appears once per DOF, and there's also the current node thus: (x/DOFS) - 1...128NoNeighbours = (NoNeighbours / DOFs) - 1129130ALLOCATE(Neighbours(NoNeighbours))131Neighbours = 0132133count = 0134135DO i=Matrix % Rows(Perm(NodeNumber)*DOFs),&136(Matrix % Rows((Perm(NodeNumber)*DOFs)+1)-1) !move along the row137IF(MOD(i,DOFs) /= 0) CYCLE !Stored DOF1, DOF2, DOF3, only need every DOFth138IF(MOD(Matrix % Cols(i), DOFs) /= 0) CALL Fatal("Geometry:FindNodeNeighbours", &139"This is a programming error, Matrix structure is not what was expected.")140141IF(InvPerm(Matrix % Cols(i)/DOFs) == NodeNumber) CYCLE !Not our own neighbour142count = count + 1143Neighbours(count) = &144InvPerm(Matrix % Cols(i)/DOFs)145END DO146147IF(.NOT. PRESENT(InvPerm_in)) DEALLOCATE(InvPerm)148149END FUNCTION FindNodeNeighbours150151152!-----------------------------------------------------------------------------153!Returns the 2D (x,y) Cartesian distance between two nodes154!NOTE: This isn't well programmed, should probably pass nodes...155FUNCTION NodeDist2D(Nodes, NodeNum1, NodeNum2 ) RESULT (dist)156TYPE(Nodes_t) :: Nodes157INTEGER :: NodeNum1, NodeNum2158REAL(KIND=dp) :: xdist,ydist,dist159!Pythagoras in 2D160xdist = Nodes % x(NodeNum1)&161- Nodes % x(NodeNum2)162ydist = Nodes % y(NodeNum1)&163- Nodes % y(NodeNum2)164!TODO: Can this be simplified? See Interpolation.f90165dist = ((xdist**2) + (ydist**2))**0.5166END FUNCTION NodeDist2D167168!-----------------------------------------------------------------------------169!Returns the 3D Cartesian distance between two nodes170!NOTE: This isn't well programmed, should probably pass nodes...171FUNCTION NodeDist3D( Nodes, Node1, Node2 ) RESULT (dist)172TYPE(Nodes_t) :: Nodes173INTEGER :: Node1, Node2174REAL(KIND=dp) :: xdist,ydist,zdist,xydist,dist175!Pythagoras in 3D176xdist = Nodes % x(Node1)&177- Nodes % x(Node2)178ydist = Nodes % y(Node1)&179- Nodes % y(Node2)180zdist = Nodes % z(Node1)&181- Nodes % z(Node2)182!TODO: Can this be simplified? See Interpolation.f90183xydist = ((xdist**2) + (ydist**2))**0.5184dist = ((xydist**2) + (zdist**2))**0.5185END FUNCTION NodeDist3D186187FUNCTION PointDist2D( Point1, Point2 ) RESULT (dist)188REAL(KIND=dp) :: Point1(2),Point2(2),xdist,ydist,dist189!Pythagoras in 3D190xdist = Point1(1) - Point2(1)191ydist = Point1(2) - Point2(2)192!TODO: Can this be simplified? See Interpolation.f90193dist = ((xdist**2) + (ydist**2))**0.5194END FUNCTION PointDist2D195196FUNCTION PointDist3D( Point1, Point2 ) RESULT (dist)197REAL(KIND=dp) :: Point1(3),Point2(3),xdist,ydist,zdist,xydist,dist198!Pythagoras in 3D199xdist = Point1(1) - Point2(1)200ydist = Point1(2) - Point2(2)201zdist = Point1(3) - Point2(3)202!TODO: Can this be simplified? See Interpolation.f90203xydist = ((xdist**2) + (ydist**2))**0.5204dist = ((xydist**2) + (zdist**2))**0.5205END FUNCTION PointDist3D206207!-----------------------------------------------------------------------------208!Returns the inverse permutation table for a given perm and DOFs209!NOTE, differs from the definition of InvPerm currently used in210!Calving.F90211FUNCTION CreateInvPerm(Perm) RESULT(InvPerm)212INTEGER, POINTER :: Perm(:), InvPerm(:)213INTEGER :: i, j214215ALLOCATE(InvPerm(MAXVAL(Perm)))216217j = 0218DO i=1,SIZE(Perm)219IF(Perm(i) == 0) CYCLE220j = j + 1221InvPerm( Perm(i) ) = j222END DO223224END FUNCTION CreateInvPerm225226!-----------------------------------------------------------------------------227!Returns dx/dy for two given nodes228FUNCTION NodesGradXY(Nodes, Node1, Node2)RESULT(dxdy)229INTEGER :: Node1, Node2230TYPE(Nodes_t) :: Nodes231REAL(KIND=dp) :: dx,dy,dxdy232233dx = Nodes % x(Node1) - Nodes % x(Node2)234dy = Nodes % y(Node1) - Nodes % y(Node2)235dxdy = dx/dy236END FUNCTION NodesGradXY237238!-----------------------------------------------------------------------------239!Returns the number of decimal places of a real number240!which has been read from a text file (.e.g mesh.nodes)241!this differs from intrinsic PRECISION() because these242!numbers often have trailing 000s or 999s243FUNCTION RealAeps(in)RESULT(myaeps)244REAL(KIND=dp) :: in, toler, x, myaeps245INTEGER :: sigs, mag, decs246247!Find how many decimal places248mag = FLOOR(LOG10(ABS(in))) + 1 !Order of magnitude of number249decs = PRECISION(in) - mag !total digits - magnitude = decimal places250251toler = 10.0_dp**(-decs)252sigs = 0253x = in254255DO WHILE (.TRUE.)256IF(ABS(x - NINT(x)) < toler) THEN !found the precision limit257EXIT258ELSE259sigs = sigs + 1260x = x * 10 !move the decimal point along261x = x - FLOOR(x) !convert number to O(1) so FLOOR doesn't reach integer limit262toler = toler * 10.0_dp !1 fewer remaining decimal places263END IF264END DO265myaeps = 10.0**(-sigs)266END FUNCTION RealAeps267268!-----------------------------------------------------------------------------269! Constructs paths of connected isoline (202) elements which intersect the270! front. Each path will begin and end with a node where OnFront=.TRUE.271!-----------------------------------------------------------------------------272SUBROUTINE FindCrevassePaths(IsoMesh, OnFront, CrevassePaths, PathCount)273IMPLICIT NONE274TYPE(Mesh_t), POINTER :: IsoMesh275LOGICAL, ALLOCATABLE :: OnFront(:)276TYPE(CrevassePath_t), POINTER :: CrevassePaths277INTEGER :: PathCount278!----------------------------------------------279TYPE(CrevassePath_t), POINTER :: CurrentPath280LOGICAL :: Found, Debug281INTEGER :: i,j,NodeCount,ElemCount, NextElem282INTEGER, ALLOCATABLE :: WorkElems(:), WorkNodes(:)283284Debug = .FALSE.285PathCount = 1286287!TODO assert all 202 elements288289ALLOCATE(CrevassePaths)290CurrentPath => CrevassePaths291292ALLOCATE(WorkElems(100), WorkNodes(100))293WorkElems = 0; WorkNodes = 0294295DO i=1, IsoMesh % NumberOfBulkElements296297IF(ANY(OnFront(Isomesh % Elements(i) % NodeIndexes))) THEN298!Found an element with one node on calving front299300IF(ElementPathID(CrevassePaths, i) /= 0) CYCLE !already in a path301302!Starting a new group...303CurrentPath % ID = PathCount304IF(Debug) PRINT *, 'Potential calving isomesh element: ',i305306ElemCount = 1307NextElem = i308309!Identify which of the two nodes are on the front...310DO j=1,2311IF(OnFront(IsoMesh % Elements(i) % NodeIndexes(j))) EXIT312END DO313IF(j==3) CALL Fatal("FindCrevassePaths", "Couldn't find node on boundary")314315!... and put it first in the list316WorkNodes(1) = IsoMesh % Elements(i) % NodeIndexes(j)317NodeCount = 2318319!Follow the chain320DO WHILE(.TRUE.)321322WorkElems(ElemCount) = NextElem323ElemCount = ElemCount + 1324!Put the other node into the list325DO j=1,2326IF(ANY(WorkNodes == IsoMesh % Elements(NextElem) % NodeIndexes(j))) CYCLE327WorkNodes(NodeCount) = IsoMesh % Elements(NextElem) % NodeIndexes(j)328NodeCount = NodeCount + 1329EXIT330END DO331332!Look for element which contains previous element's node333Found = .FALSE.334DO j=1,IsoMesh % NumberOfBulkElements335IF(ANY(IsoMesh % Elements(j) % NodeIndexes == WorkNodes(NodeCount-1))) THEN336337!already in another group (is this possible?)338IF(ElementPathID(CrevassePaths, j ) /= 0) CYCLE339!Already in current group340IF(ANY(WorkElems == j)) CYCLE341342NextElem = j343Found = .TRUE.344EXIT345END IF346END DO347348IF(.NOT. Found) EXIT349350IF(ElemCount > SIZE(WorkElems)) THEN351IF(Debug) PRINT *, 'FindCrevassePaths, doubling size of element array.'352CALL DoubleIntVectorSize(WorkElems)353END IF354IF(NodeCount > SIZE(WorkNodes)) THEN355IF(Debug) PRINT *, 'FindCrevassePaths, doubling size of node array.'356CALL DoubleIntVectorSize(WorkNodes)357END IF358END DO359360ElemCount = ElemCount - 1361NodeCount = NodeCount - 1362363CurrentPath % NumberOfNodes = NodeCount364CurrentPath % NumberOfElements = ElemCount365366ALLOCATE(CurrentPath % ElementNumbers(ElemCount), &367CurrentPath % NodeNumbers(NodeCount))368369CurrentPath % NodeNumbers = WorkNodes(1:NodeCount)370CurrentPath % ElementNumbers = WorkElems(1:ElemCount)371372WorkNodes = 0373WorkElems = 0374375ALLOCATE(CurrentPath % Next)376CurrentPath % Next % Prev => CurrentPath377CurrentPath => CurrentPath % Next378PathCount = PathCount + 1379END IF380END DO381382!We always overshoot by one383PathCount = PathCount - 1384385IF(PathCount > 0) THEN386PRINT *,'Number of crevasse paths: ', PathCount387CurrentPath % Prev % Next => NULL()388DEALLOCATE(CurrentPath)389ELSE390PRINT *,'No crevasse paths'391DEALLOCATE(CrevassePaths)392END IF393394DEALLOCATE(WorkNodes, WorkElems)395396END SUBROUTINE FindCrevassePaths397398!Removes a CrevassePath from a linked list of CrevassePaths399SUBROUTINE RemoveCrevassePath(Path)400IMPLICIT NONE401TYPE(CrevassePath_t), POINTER :: Path402!------------------------------------------------403IF(ASSOCIATED(Path % Prev)) Path % Prev % Next => Path % Next404IF(ASSOCIATED(Path % Next)) Path % Next % Prev => Path % Prev405406IF(ASSOCIATED(Path % NodeNumbers)) DEALLOCATE(Path % NodeNumbers)407IF(ASSOCIATED(Path % ElementNumbers)) DEALLOCATE(Path % ElementNumbers)408DEALLOCATE(Path)409410END SUBROUTINE RemoveCrevassePath411412!--------------------------------------------------------------------413! 'Tidies up' isomesh and the CrevassePaths found by FindCrevassePaths414!--------------------------------------------------------------------415! This involves removing duplicate nodes, taking care to replace node416! indexes in affected elements. This then allows easy removal of417! 202 elements with zero length.418!419! Closed loops are removed from crevasse paths420!--------------------------------------------------------------------421422SUBROUTINE CheckCrevasseNodes(Mesh, CrevassePaths, Onleft, OnRight)423IMPLICIT NONE424TYPE(Mesh_t), POINTER :: Mesh425TYPE(CrevassePath_t), POINTER :: CrevassePaths426LOGICAL, OPTIONAL :: OnLeft(:),OnRight(:)427!-------------------------------------------------428TYPE(CrevassePath_t), POINTER :: CurrentPath,WorkPath429INTEGER :: i,j,ElNo,counter, ElementNumbers(2)430INTEGER, ALLOCATABLE :: ReplaceWithNode(:),WorkInt(:)431LOGICAL :: Debug432LOGICAL, ALLOCATABLE :: RemoveElement(:), RemoveNode(:), PathRemoveElement(:)433434Debug = .FALSE.435436ALLOCATE(RemoveNode(Mesh % NumberOfNodes),&437ReplaceWithNode(Mesh % NumberOfNodes),&438RemoveElement(Mesh % NumberOfBulkElements))439RemoveNode = .FALSE.440RemoveElement = .FALSE.441ReplaceWithNode = 0442443!Cycle mesh NODES, looking for duplicates and marking them for deletion444DO i=1,Mesh % NumberOfNodes445IF(RemoveNode(i)) CYCLE446DO j=1,Mesh % NumberOfNodes447IF(i==j .OR. RemoveNode(j)) CYCLE448IF(Mesh % Nodes % x(i) == Mesh % Nodes % x(j) .AND.&449Mesh % Nodes % y(i) == Mesh % Nodes % y(j) .AND.&450Mesh % Nodes % z(i) == Mesh % Nodes % z(j)) THEN451RemoveNode(j) = .TRUE.452ReplaceWithNode(j) = i453END IF454END DO455END DO456457!Replace element nodeindexes where nodes are removed458DO i=1,Mesh % NumberOfBulkElements459DO j=1,SIZE(Mesh % Elements(i) % NodeIndexes)460IF(RemoveNode(Mesh % Elements(i) % NodeIndexes(j))) THEN461IF(PRESENT(OnLeft) .AND. OnLeft(Mesh % Elements(i) % NodeIndexes(j))) THEN462OnLeft(Mesh % Elements(i) % NodeIndexes(j)) = .FALSE.463OnLeft(ReplaceWithNode(Mesh % Elements(i) % NodeIndexes(j))) = .TRUE.464END IF465IF(PRESENT(OnRight) .AND. OnRight(Mesh % Elements(i) % NodeIndexes(j))) THEN466PRINT*, 'replace', Mesh % Elements(i) % NodeIndexes(j),&467ReplaceWithNode(Mesh % Elements(i) % NodeIndexes(j))468OnRight(Mesh % Elements(i) % NodeIndexes(j)) = .FALSE.469OnRight(ReplaceWithNode(Mesh % Elements(i) % NodeIndexes(j))) = .TRUE.470END IF471Mesh % Elements(i) % NodeIndexes(j) = &472ReplaceWithNode(Mesh % Elements(i) % NodeIndexes(j))473END IF474END DO475END DO476477!Mark elements with zero length (duplicate node indexes) for removal478DO i=1,Mesh % NumberOfBulkElements479IF(Mesh % Elements(i) % NodeIndexes(1) == Mesh % Elements(i) % NodeIndexes(2)) THEN480RemoveElement(i) = .TRUE.481IF(Debug) PRINT *,'debug, removing element: ',i,' with identical nodes: ',&482Mesh % Elements(i) % NodeIndexes(1)483END IF484END DO485486IF(Debug) PRINT *,'Debug, removing ',COUNT(RemoveElement),' of ',SIZE(RemoveElement),' elements'487488!Cycle paths, looking for nodes which are identical and removing them, joining up elements etc489CurrentPath => CrevassePaths490DO WHILE(ASSOCIATED(CurrentPath))491492IF(Debug) PRINT *,'Debug, Path: ',CurrentPath % ID,'initial no elems: ',&493CurrentPath % NumberOfElements,&494' no nodes: ', CurrentPath % NumberOfNodes495496ALLOCATE(WorkInt(CurrentPath % NumberOfElements))497WorkInt = 0498counter = 0499500!Mark pairs of duplicate elements in path for removal501ALLOCATE(PathRemoveElement(CurrentPath % NumberOfElements))502PathRemoveElement = .FALSE.503504IF(CurrentPath % NumberOfElements == 1) THEN505!Only has one element, remove506PathRemoveElement = .TRUE.507ELSE508DO i=1,CurrentPath % NumberOfElements-1509510IF(PathRemoveElement(i)) CYCLE511ElementNumbers(1) = CurrentPath % ElementNumbers(i)512IF(RemoveElement(ElementNumbers(1))) CYCLE513514j = i+1515IF(PathRemoveElement(j)) CYCLE516ElementNumbers(2) = CurrentPath % ElementNumbers(j)517IF(RemoveElement(ElementNumbers(2))) CYCLE518519IF( ANY(Mesh % Elements(ElementNumbers(1)) % NodeIndexes == &520Mesh % Elements(ElementNumbers(2)) % NodeIndexes(1)) .AND. &521ANY(Mesh % Elements(ElementNumbers(1)) % NodeIndexes == &522Mesh % Elements(ElementNumbers(2)) % NodeIndexes(2)) ) THEN523PathRemoveElement(j) = .TRUE.524PathRemoveElement(i) = .TRUE.525IF(Debug) PRINT *,'Path: ',CurrentPath % ID,' removing identical elements: ',i,' ',j526END IF527528END DO529530531!Check if entire crevasse group is a closed loop532ElementNumbers(1) = CurrentPath % ElementNumbers(1)533ElementNumbers(2) = CurrentPath % ElementNumbers(CurrentPath % NumberOfElements)534DO i=1,2535IF(.NOT. ANY(Mesh % Elements(CurrentPath % ElementNumbers(2)) % NodeIndexes == &536Mesh % Elements(ElementNumbers(1)) % NodeIndexes(i))) EXIT537END DO538IF(i==3) CALL Fatal("CheckCrevassePaths","Programming error: unable to determine first node")539IF(ANY(Mesh % Elements(ElementNumbers(2)) % NodeIndexes == &540Mesh % Elements(ElementNumbers(1)) % NodeIndexes(i))) THEN541PathRemoveElement = .TRUE.542IF(Debug) PRINT *,'Debug, removing path ',CurrentPath % ID,' because its entirely closed.'543END IF544545!For each element 'i' in turn, cycle backwards through element list looking546!for element(i)'s nodes. If found, this indicates a closed loop which should547!be removed.548DO i=1,CurrentPath % NumberOfElements549IF(PathRemoveElement(i)) CYCLE550IF(RemoveElement(CurrentPath % ElementNumbers(i))) CYCLE551ElementNumbers(1) = CurrentPath % ElementNumbers(i)552553DO j=CurrentPath % NumberOfElements,i+1,-1 !cycle backwards from end to i+1554IF(PathRemoveElement(j)) CYCLE555IF(RemoveElement(CurrentPath % ElementNumbers(j))) CYCLE556ElementNumbers(2) = CurrentPath % ElementNumbers(j)557558IF( ANY(Mesh % Elements(ElementNumbers(1)) % NodeIndexes == &559Mesh % Elements(ElementNumbers(2)) % NodeIndexes(1)) .OR. &560ANY(Mesh % Elements(ElementNumbers(1)) % NodeIndexes == &561Mesh % Elements(ElementNumbers(2)) % NodeIndexes(2)) ) THEN562PathRemoveElement(i+1:j-1) = .TRUE.563IF(Debug) PRINT *,'CheckCrevasseNodes, &564&Removing a closed loop from ',i+1,' to ',j-1565END IF566567END DO568569END DO570END IF571572!Replace CrevassePath % ElementNumbers based on previous removals573DO i=1,CurrentPath % NumberOfElements574IF(.NOT.RemoveElement(CurrentPath % ElementNumbers(i)) .AND. &575.NOT.PathRemoveElement(i)) THEN576counter = counter + 1577WorkInt(counter) = CurrentPath % ElementNumbers(i)578IF(Debug) THEN579PRINT *,'Debug, keeping element: ',i,' from path: ',CurrentPath % ID580PRINT *,'Debug, element global: ',CurrentPath % ElementNumbers(i),' and nodes :',&581Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes582END IF583ELSE584IF(Debug) THEN585PRINT *,'Debug, removing element: ',i,' from path: ',CurrentPath % ID586PRINT *,'Debug, element global: ',CurrentPath % ElementNumbers(i),' and nodes :',&587Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes588END IF589END IF590END DO591IF(counter < CurrentPath % NumberOfElements) THEN592IF(Debug) PRINT *,'debug, path loses ',CurrentPath % NumberOfElements - counter,&593' of ',CurrentPath % NumberOfElements,' elements.'594595CurrentPath % NumberOfElements = counter596DEALLOCATE(CurrentPath % ElementNumbers)597ALLOCATE(CurrentPath % ElementNumbers(counter))598599CurrentPath % ElementNumbers = WorkInt(1:counter)600END IF601DEALLOCATE(WorkInt,PathRemoveElement)602603IF (CurrentPath % NumberOfElements <= 0) THEN604WorkPath => CurrentPath % Next605606IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath607CALL RemoveCrevassePath(CurrentPath)608IF(Debug) CALL Info("CheckCrevasseNodes",&609"Removing a crevasse path with no elements")610CurrentPath => WorkPath611CYCLE612END IF613614!Now reconstruct node list for path:615DEALLOCATE(CurrentPath % NodeNumbers)616CurrentPath % NumberOfNodes = CurrentPath % NumberOfElements + 1617ALLOCATE(CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes))618CurrentPath % NodeNumbers = 0619620!First node621IF(CurrentPath % NumberOfElements >= 2) THEN622DO i=1,2623IF( ANY(Mesh % Elements(CurrentPath % ElementNumbers(2)) % NodeIndexes == &624Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(i))) CYCLE625CurrentPath % NodeNumbers(1) = &626Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(i)627628IF(i==2) THEN !Reorder so that nodeindexes(1) and (2) are in chain order629Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(2) = &630Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(1)631Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(1) = &632CurrentPath % NodeNumbers(1)633END IF634EXIT635END DO636ELSE !Rare, single element path, choice of first node is arbitrary...637CurrentPath % NodeNumbers(1) = &638Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(1)639END IF640641IF(Debug) PRINT *,'Path ',CurrentPath % ID,' has first node: ',CurrentPath % NodeNumbers(1)642643!Follow the chain...644DO i=1,CurrentPath % NumberOfElements645ElNo = CurrentPath % ElementNumbers(i)646DO j=1,2647IF(ANY(CurrentPath % NodeNumbers == Mesh % Elements(ElNo) % NodeIndexes(j))) CYCLE648CurrentPath % NodeNumbers(i+1) = Mesh % Elements(ElNo) % NodeIndexes(j)649650IF(j==1) THEN !Reorder so that nodeindexes(1) and (2) are in chain order651Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(1) = &652Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(2)653Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(2) = &654CurrentPath % NodeNumbers(i+1)655END IF656657EXIT658END DO659END DO660661IF(Debug) PRINT *,'Debug, path ',CurrentPath % ID,' has nodes: ',CurrentPath % NodeNumbers662IF(ANY(CurrentPath % NodeNumbers == 0)) CALL Fatal("CheckCrevasseNodes","Failed to fill node indexes")663CurrentPath => CurrentPath % Next664END DO665666END SUBROUTINE CheckCrevasseNodes667668!----------------------------------------------------669! Checks paths for projectability and overlap670! In case of overlap, smaller enclosed path is deleted671! In case of unprojectability, nodes are moved laterally672! to restore projectability.673!----------------------------------------------------674! NOTE: if this breaks, it could be due to two paths675! sharing a node. Thinking about it, I see no reason676! this should be an issue, but we'll see...677SUBROUTINE ValidateCrevassePaths(Mesh, CrevassePaths, FrontOrientation, PathCount, OnLeft, OnRight, EnsureProjectible)678IMPLICIT NONE679TYPE(Mesh_t), POINTER :: Mesh680TYPE(CrevassePath_t), POINTER :: CrevassePaths681LOGICAL, OPTIONAL :: OnLeft(:),OnRight(:),EnsureProjectible682REAL(KIND=dp) :: FrontOrientation(3)683INTEGER :: PathCount, First, Last, LeftIdx, RightIdx684!---------------------------------------------------685REAL(KIND=dp) :: RotationMatrix(3,3), UnRotationMatrix(3,3), FrontDist, MaxDist, &686ShiftTo, Dir1(2), Dir2(2), CCW_value,a1(2),a2(2),b1(2),b2(2),intersect(2)687REAL(KIND=dp), ALLOCATABLE :: ConstrictDirection(:,:)688TYPE(CrevassePath_t), POINTER :: CurrentPath, OtherPath, WorkPath, LeftPath, RightPath689INTEGER :: i,j,k,n,ElNo,ShiftToMe, NodeNums(2),A,B,FirstIndex, LastIndex,Start690INTEGER, ALLOCATABLE :: WorkInt(:)691LOGICAL :: Debug, Shifted, CCW, ToLeft, Snakey, OtherRight, ShiftRightPath, &692DoProjectible, headland693LOGICAL, ALLOCATABLE :: PathMoveNode(:), DeleteElement(:), BreakElement(:), &694FarNode(:), DeleteNode(:), Constriction(:)695CHARACTER(MAX_NAME_LEN) :: FuncName="ValidateCrevassePaths"696697Debug = .FALSE.698Snakey = .TRUE.699700IF(PRESENT(EnsureProjectible)) THEN701DoProjectible = EnsureProjectible702ELSE703DoProjectible = .TRUE.704END IF705706RotationMatrix = ComputeRotationMatrix(FrontOrientation)707UnRotationMatrix = TRANSPOSE(RotationMatrix)708709! Temporarily rotate the mesh710CALL RotateMesh(Mesh, RotationMatrix)711712! Find path %left, %right, %extent (width)713CALL ComputePathExtent(CrevassePaths, Mesh % Nodes, .TRUE.)714715IF(PRESENT(OnLeft) .OR. PRESENT(OnRight)) THEN716CALL Assert((PRESENT(OnLeft) .AND. PRESENT(OnRight)), FuncName, &717"Provided only one of OnLeft/OnRight!")718719!Check that crevasse path doesn't begin and end on same lateral margin720CurrentPath => CrevassePaths721DO WHILE(ASSOCIATED(CurrentPath))722!Check node OnLeft, OnRight723First = CurrentPath % NodeNumbers(1)724Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)725IF((OnLeft(First) .AND. OnLeft(Last)) .OR. &726(OnRight(First) .AND. OnRight(Last))) THEN727CurrentPath % Valid = .FALSE.728END IF729CurrentPath => CurrentPath % Next730END DO731732!Actually remove previous marked733CurrentPath => CrevassePaths734DO WHILE(ASSOCIATED(CurrentPath))735WorkPath => CurrentPath % Next736737IF(.NOT. CurrentPath % Valid) THEN738IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath739CALL RemoveCrevassePath(CurrentPath)740IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path which &741&starts and ends on same margin")742END IF743CurrentPath => WorkPath744END DO745END IF746747IF(Snakey) THEN748!-----------------------------------------------------749! Paths should not 'snake' inwards in a narrow slit...750!-----------------------------------------------------751752!it's insufficient to require that no nodes be753!further away than the two edge nodes.754!Instead, must ensure that no nodes are further away than any755!surrounding nodes.756757!First need to determine path orientation758!with respect to front....759760CurrentPath => CrevassePaths761DO WHILE(ASSOCIATED(CurrentPath))762763!First and last node on path764First = CurrentPath % NodeNumbers(1)765Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)766767!if ToLeft, the crevasse path goes from right to left, from the768!perspective of someone sitting in the fjord, looking at the front769ToLeft = Mesh % Nodes % y(Last) > Mesh % Nodes % y(First)770771IF(Debug) THEN772FrontDist = NodeDist3D(Mesh % Nodes,First, Last)773PRINT *,'PATH: ', CurrentPath % ID, ' FrontDist: ',FrontDist774PRINT *,'PATH: ', CurrentPath % ID, &775' nonodes: ',CurrentPath % NumberOfNodes,&776' noelems: ',CurrentPath % NumberOfElements777END IF778779!Cycle path nodes, finding those which are too far away780ALLOCATE(FarNode(CurrentPath % NumberOfNodes), &781Constriction(CurrentPath % NumberOfNodes),&782ConstrictDirection(CurrentPath % NumberOfNodes,2))783FarNode = .FALSE.784Constriction = .FALSE.785ConstrictDirection = 0.0_dp786787!Determine which nodes have the potential to be constriction (based on angle)788!and compute constriction direction (i.e. which way the 'pointy bit' points...')789DO i=2,CurrentPath % NumberOfNodes-1790First = CurrentPath % NodeNumbers(i-1)791Last = CurrentPath % NodeNumbers(i+1)792n = CurrentPath % NodeNumbers(i)793794CCW_value = ((Mesh % Nodes % y(n) - Mesh % Nodes % y(First)) * &795(Mesh % Nodes % z(Last) - Mesh % Nodes % z(First))) - &796((Mesh % Nodes % z(n) - Mesh % Nodes % z(First)) * &797(Mesh % Nodes % y(Last) - Mesh % Nodes % y(First)))798799CCW = CCW_value > 0.0_dp800801IF((CCW .NEQV. ToLeft) .AND. (ABS(CCW_value) > 10*AEPS)) THEN802Constriction(i) = .TRUE.803!Calculate constriction direction804805Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(First)806Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(First)807Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)808809Dir2(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)810Dir2(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)811Dir2 = Dir2 / ((Dir2(1)**2.0 + Dir2(2)**2.0) ** 0.5)812813ConstrictDirection(i,1) = Dir1(1) + Dir2(1)814ConstrictDirection(i,2) = Dir1(2) + Dir2(2)815ConstrictDirection(i,:) = ConstrictDirection(i,:) / &816((ConstrictDirection(i,1)**2.0 + ConstrictDirection(i,2)**2.0) ** 0.5)817818IF(Debug) PRINT *, 'Debug, node ',i,' dir1,2: ',Dir1, Dir2819IF(Debug) PRINT *, 'Debug, node ',i,' constriction direction: ',ConstrictDirection(i,:)820IF(Debug) PRINT *, 'Debug, node ',i,' xyz: ',Mesh % Nodes % x(n),Mesh % Nodes % y(n),Mesh % Nodes % z(n)821END IF822END DO823824!First and last can always be constriction825Constriction(1) = .TRUE.826Constriction(SIZE(Constriction)) = .TRUE.827828!Compute constriction direction for first and last829!We don't have info about the third node, so take orthogonal to 2830Last = CurrentPath % NodeNumbers(2)831n = CurrentPath % NodeNumbers(1)832Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)833Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)834Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)835836!Depending on which end of the chain we are,837!we take either the right or left orthogonal vector838IF(ToLeft) THEN839ConstrictDirection(1,1) = Dir1(2)840ConstrictDirection(1,2) = -1.0 * Dir1(1)841ELSE842ConstrictDirection(1,1) = -1.0 * Dir1(2)843ConstrictDirection(1,2) = Dir1(1)844END IF845IF(Debug) PRINT *, 'Debug, node 1 constriction direction: ',ConstrictDirection(1,:)846847Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes - 1)848n = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)849850Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)851Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)852Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)853854IF(.NOT. ToLeft) THEN855ConstrictDirection(CurrentPath % NumberOfNodes,1) = Dir1(2)856ConstrictDirection(CurrentPath % NumberOfNodes,2) = -1.0 * Dir1(1)857ELSE858ConstrictDirection(CurrentPath % NumberOfNodes,1) = -1.0 * Dir1(2)859ConstrictDirection(CurrentPath % NumberOfNodes,2) = Dir1(1)860END IF861IF(Debug) PRINT *, 'Debug, node last constriction direction: ',&862ConstrictDirection(CurrentPath % NumberOfNodes,:)863864!---------------------------------------865! Now that we have constrictions marked and directions computed, cycle nodes866867DO i=1,CurrentPath % NumberOfNodes868IF(.NOT. Constriction(i)) CYCLE869870DO j=CurrentPath % NumberOfNodes,i+1,-1871IF(.NOT. Constriction(j)) CYCLE872873874First = CurrentPath % NodeNumbers(i)875Last = CurrentPath % NodeNumbers(j)876877!Check that these constrictions 'face' each other via dot product878Dir1(1) = Mesh % Nodes % y(Last) - Mesh % Nodes % y(First)879Dir1(2) = Mesh % Nodes % z(Last) - Mesh % Nodes % z(First)880Dir2(1) = -Dir1(1)881Dir2(2) = -Dir1(2)882883!If the two constrictions aren't roughly facing each other:884! < > rather than > <885! then skip this combo886IF(SUM(ConstrictDirection(i,:)*Dir1) < 0) THEN887IF(Debug) PRINT *,'Constrictions ',i,j,' do not face each other 1: ',&888SUM(ConstrictDirection(i,:)*Dir1)889CYCLE890END IF891892IF(SUM(ConstrictDirection(j,:)*Dir2) < 0) THEN893IF(Debug) PRINT *,'Constrictions ',j,i,' do not face each other 2: ',&894SUM(ConstrictDirection(j,:)*Dir2)895CYCLE896END IF897898IF(Debug) PRINT *,'Constrictions ',i,j,' do face each other: ',&899SUM(ConstrictDirection(i,:)*Dir1)900901!test that the line drawn between the constriction doesn't intersect902!any intermediate elements as this indicates903!crossing a headland (difficult to draw - but it's bad news)904!905! - --- ---- -906! \/ \ / \/907! ----908!909910a1(1) = Mesh % Nodes % y(First)911a1(2) = Mesh % Nodes % z(First)912a2(1) = Mesh % Nodes % y(Last)913a2(2) = Mesh % Nodes % z(Last)914headland = .FALSE.915DO k=i+1,j-2916b1(1) = Mesh % Nodes % y(CurrentPath % NodeNumbers(k))917b1(2) = Mesh % Nodes % z(CurrentPath % NodeNumbers(k))918b2(1) = Mesh % Nodes % y(CurrentPath % NodeNumbers(k+1))919b2(2) = Mesh % Nodes % z(CurrentPath % NodeNumbers(k+1))920921CALL LineSegmentsIntersect(a1,a2,b1,b2,intersect,headland)922IF(headland .AND. Debug) PRINT*, 'Headland intersect: ', 'a1', a1, &923'a2', a2, 'b1', b1, 'b2', b2924IF(headland) EXIT925END DO926IF(headland) CYCLE927928MaxDist = NodeDist3D(Mesh % Nodes,First, Last)929930DO k=i+1,j-1931IF(FarNode(k)) CYCLE932933n = CurrentPath % NodeNumbers(k)934935IF((NodeDist3D(Mesh % Nodes, First, n) <= MaxDist) .AND. &936(NodeDist3D(Mesh % Nodes, Last, n) <= MaxDist)) CYCLE !within range937938FarNode(k) = .TRUE.939IF(Debug) PRINT *,'Far node: ',k,' xyz: ',Mesh % Nodes % x(n),&940Mesh % Nodes % y(n),Mesh % Nodes % z(n)941942END DO943END DO944END DO945946!Cycle elements, marking those which need to be adjusted947ALLOCATE(BreakElement(CurrentPath % NumberOfElements),&948DeleteElement(CurrentPath % NumberOfElements))949BreakElement = .FALSE.950DeleteElement = .FALSE.951952DO i=1,CurrentPath % NumberOfElements953IF(ANY(FarNode(i:i+1))) THEN954IF(ALL(FarNode(i:i+1))) THEN955DeleteElement(i) = .TRUE.956IF(Debug) PRINT *,'PATH: ', CurrentPath % ID, ' element: ',i,' is deleted.'957ELSE958BreakElement(i) = .TRUE.959IF(Debug) PRINT *,'PATH: ', CurrentPath % ID, ' element: ',i,' is broken.'960END IF961END IF962END DO963964DO i=1,CurrentPath % NumberOfElements965IF((.NOT. BreakElement(i)) .OR. DeleteElement(i)) CYCLE966!This element needs to be adjusted967DO j=i+1,CurrentPath % NumberOfElements968IF(.NOT. (BreakElement(j) .OR. DeleteElement(j))) &969CALL Fatal("ValidateCrevasseGroups","Programming error in maintaining aspect ratio")970IF(DeleteElement(j)) CYCLE971!This is the next 'break element' after i972!Determine which nodes we keep973974IF((CurrentPath % NodeNumbers(j) /= &975Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(1)) .OR. &976(CurrentPath % NodeNumbers(j+1) /= &977Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(2))) THEN978979CALL Fatal("ValidateCrevassePaths", "Chain building error")980END IF981982Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(2) = &983Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(2)984985!We now want to delete it, because we only keep one from each broken pair986DeleteElement(j) = .TRUE.987EXIT !we paired this one, move on988END DO989END DO990991!Delete the elements and nodes992IF(COUNT(DeleteElement) > 0) THEN993!elements994ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))995WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)996997DEALLOCATE(CurrentPath % ElementNumbers)998ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))9991000CurrentPath % ElementNumbers = WorkInt1001CurrentPath % NumberOfElements = SIZE(WorkInt)1002DEALLOCATE(WorkInt)10031004!nodes1005ALLOCATE(WorkInt(COUNT(.NOT. FarNode)))1006WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.FarNode)10071008DEALLOCATE(CurrentPath % NodeNumbers)1009ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))10101011CurrentPath % NodeNumbers = WorkInt1012CurrentPath % NumberOfNodes = SIZE(WorkInt)1013DEALLOCATE(WorkInt)1014END IF10151016DEALLOCATE(FarNode, Constriction, ConstrictDirection, BreakElement, DeleteElement)1017CurrentPath => CurrentPath % Next1018END DO1019END IF !Snakey10201021!Update Left, Right & Extent1022CALL ComputePathExtent(CrevassePaths, Mesh % Nodes, .TRUE.)10231024!-----------------------------------------------------1025! Move nodes from crevassepaths which aren't projectable1026!-----------------------------------------------------1027! 1) Path elements are ordered as a chain1028! 2) Path % Element(i) has nodes i, i+11029!1030! Go through CrevassePath nodes, marking those1031! which are 'shadowed' by further away elements.1032!-----------------------------------------------------10331034IF(DoProjectible) THEN1035CurrentPath => CrevassePaths1036DO WHILE(ASSOCIATED(CurrentPath))10371038ALLOCATE(PathMoveNode(CurrentPath % NumberOfNodes))1039PathMoveNode = .FALSE.10401041DO i=1,CurrentPath % NumberOfNodes1042n = CurrentPath % NodeNumbers(i)1043DO j=1,CurrentPath % NumberOfElements1044ElNo = CurrentPath % ElementNumbers(j)1045NodeNums = Mesh % Elements(ElNo) % NodeIndexes1046IF(ANY(NodeNums == n)) CYCLE !Node is in element, skip1047!Check if node lies between element nodes1048IF( (Mesh % Nodes % y(NodeNums(1)) > Mesh % Nodes % y(n)) .NEQV. &1049(Mesh % Nodes % y(NodeNums(2)) > Mesh % Nodes % y(n)) ) THEN1050!Check the node is in front of the element10511052A = MINLOC(Mesh % Nodes % z(NodeNums),1)1053B = MAXLOC(Mesh % Nodes % z(NodeNums),1)1054CCW = ((Mesh % Nodes % y(n) - Mesh % Nodes % y(NodeNums(A))) * &1055(Mesh % Nodes % z(NodeNums(B)) - Mesh % Nodes % z(NodeNums(A)))) > &1056((Mesh % Nodes % z(n) - Mesh % Nodes % z(NodeNums(A))) * &1057(Mesh % Nodes % y(NodeNums(B)) - Mesh % Nodes % y(NodeNums(A))))10581059ToLeft = Mesh % Nodes % y(NodeNums(A)) > Mesh % Nodes % y(NodeNums(B))10601061IF(CCW .EQV. ToLeft) THEN1062!Node should be removed1063PathMoveNode(i) = .TRUE.1064EXIT1065END IF10661067END IF1068END DO1069END DO10701071IF(Debug) THEN1072PRINT *,'Path ',CurrentPath % ID,' has ',&1073COUNT(PathMoveNode),' nodes which need to be shifted.'10741075DO i=1,CurrentPath % NumberOfNodes1076IF(.NOT. PathMoveNode(i)) CYCLE1077PRINT *,'Need to move node: ',i,' y: ',&1078Mesh % Nodes % y(CurrentPath % NodeNumbers(i)),&1079' z: ',Mesh % Nodes % z(CurrentPath % NodeNumbers(i))10801081END DO1082END IF10831084!Now that nodes have been marked as shadowed, identify chains1085!and the location of the node to which these groups of nodes should be moved.1086Shifted = .TRUE.1087Start = 11088DO WHILE(Shifted)1089Shifted = .FALSE.10901091DO i=Start,CurrentPath % NumberOfNodes1092IF(PathMoveNode(i)) THEN1093IF(.NOT. Shifted) THEN1094Shifted = .TRUE.1095FirstIndex = i1096END IF1097LastIndex = i1098ELSE1099IF(Shifted) EXIT1100END IF1101END DO1102IF(.NOT. Shifted) EXIT11031104!We have identified a chain from FirstIndex to LastIndex which need to be moved.1105!They should be moved to either FirstIndex-1 or LastIndex+11106!(Whichever is further back)1107!Note special case at start and end of path1108IF(FirstIndex == 1) THEN1109ShiftToMe = CurrentPath % NodeNumbers(LastIndex+1)1110ELSE IF(LastIndex == CurrentPath % NumberOfNodes) THEN1111ShiftToMe = CurrentPath % NodeNumbers(FirstIndex-1)1112ELSE IF(Mesh % Nodes % z(CurrentPath % NodeNumbers(FirstIndex-1)) <&1113Mesh % Nodes % z(CurrentPath % NodeNumbers(LastIndex+1))) THEN1114ShiftToMe = CurrentPath % NodeNumbers(FirstIndex-1)1115ELSE1116ShiftToMe = CurrentPath % NodeNumbers(LastIndex+1)1117END IF11181119Mesh % Nodes % y(CurrentPath % NodeNumbers(FirstIndex:LastIndex)) = &1120Mesh % Nodes % y(ShiftToMe)11211122IF(Debug) PRINT *,'Shifting nodes ',FirstIndex,' to ',LastIndex,&1123' to point: ',Mesh % Nodes % y(ShiftToMe)1124Start = LastIndex + 11125END DO11261127DEALLOCATE(PathMoveNode)1128CurrentPath => CurrentPath % Next1129END DO1130END IF !DoProjectible11311132!NOTE: probably not really necessary here, Shifted nodes don't extend1133!the extent1134!Update Left, Right & Extent1135CALL ComputePathExtent(CrevassePaths, Mesh % Nodes, .TRUE.)11361137!--------------------------------------------------------1138! Remove crevassepaths which are contained within others.1139!--------------------------------------------------------1140! 1) All crevasse paths start and end on the calving front1141! or lateral margin.1142! 2) Crevasse paths can't cross each other.1143!1144! Thus, iff a crevasse path is surrounded laterally by1145! another single crevasse path, we remove it, because1146! it must be contained by the larger one.1147!--------------------------------------------------------11481149CurrentPath => CrevassePaths1150DO WHILE(ASSOCIATED(CurrentPath))11511152OtherPath => CrevassePaths1153DO WHILE(ASSOCIATED(OtherPath))1154IF(ASSOCIATED(OtherPath, CurrentPath)) THEN1155OtherPath => OtherPath % Next1156CYCLE1157END IF11581159IF((CurrentPath % Left >= OtherPath % Left) .AND. &1160(CurrentPath % Right <= OtherPath % Right)) THEN!contained within1161CurrentPath % Valid = .FALSE.1162IF(Debug) PRINT *,'Debug, marked path ',CurrentPath % ID,' for deletion &1163&because its contained within path ',OtherPath % ID1164END IF1165OtherPath => OtherPath % Next1166END DO11671168CurrentPath => CurrentPath % Next1169END DO11701171!Actually remove previous marked1172CurrentPath => CrevassePaths1173DO WHILE(ASSOCIATED(CurrentPath))1174WorkPath => CurrentPath % Next11751176IF(.NOT. CurrentPath % Valid) THEN1177IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath1178CALL RemoveCrevassePath(CurrentPath)1179IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path")1180END IF1181CurrentPath => WorkPath1182END DO11831184!-------------------------------------------------1185! Check for paths partly obscuring each other1186! (fully obscured are dealt with above)1187!-------------------------------------------------1188! If paths partially overlap, the overlapping nodes1189! of whichever path is seaward are moved.1190! i.e. the larger calving event takes precedent1191!-------------------------------------------------11921193IF(DoProjectible) THEN1194CurrentPath => CrevassePaths1195DO WHILE(ASSOCIATED(CurrentPath))11961197OtherPath => CrevassePaths1198DO WHILE(ASSOCIATED(OtherPath))1199IF(ASSOCIATED(OtherPath, CurrentPath)) THEN1200OtherPath => OtherPath % Next1201CYCLE1202END IF12031204IF((CurrentPath % Left < OtherPath % Right) .EQV. &1205(OtherPath % Left < CurrentPath % Right)) THEN !overlap12061207IF(Debug) PRINT *,'Debug, paths: ',CurrentPath % ID, OtherPath % ID,' partially overlap'12081209!Is the other path to the right or left?1210OtherRight = CurrentPath % Right < OtherPath % Right12111212!Check not fully contained - should have been dealt with above1213IF((CurrentPath % Right > OtherPath % Right) .NEQV. &1214(CurrentPath % Left > OtherPath % Left)) THEN1215CALL Warn("ValidateCrevassePaths","Encountered full overlap which &1216&should already have been taken care of! OK if this is rare, &1217&otherwise maybe programming error")1218END IF12191220IF(OtherRight) THEN1221RightPath => OtherPath1222LeftPath => CurrentPath1223ELSE1224RightPath => CurrentPath1225LeftPath => OtherPath1226END IF12271228!Find the left and rightmost nodes of the two paths1229DO i=1,LeftPath % NumberOfNodes1230IF(Debug) PRINT *,'Debug, node ',i,' of leftpath: ',&1231Mesh % Nodes % y(LeftPath % NodeNumbers(i)), LeftPath % Right12321233IF(Mesh % Nodes % y(LeftPath % NodeNumbers(i)) >= LeftPath % Right) LeftIdx = i1234END DO12351236DO i=1,RightPath % NumberOfNodes1237IF(Debug) PRINT *,'Debug, node ',i,' of rightpath: ',&1238Mesh % Nodes % y(RightPath % NodeNumbers(i)), RightPath % Left12391240IF(Mesh % Nodes % y(RightPath % NodeNumbers(i)) <= RightPath % Left) RightIdx = i1241END DO12421243!See which is further forward.1244ShiftRightPath = Mesh % Nodes % z(LeftPath % NodeNumbers(LeftIdx)) < &1245Mesh % Nodes % z(RightPath % NodeNumbers(RightIdx))12461247IF(ShiftRightPath) THEN1248ShiftTo = Mesh % Nodes % y(LeftPath % NodeNumbers(LeftIdx))1249DO i=1,RightPath % NumberOfNodes1250IF(Mesh % Nodes % y(RightPath % NodeNumbers(i)) < ShiftTo) THEN1251IF(Debug) PRINT *,'Debug, overlap shifting right node ',i,' path '&1252,RightPath % ID,' from ', Mesh % Nodes % y(RightPath % NodeNumbers(i)),&1253' to ',ShiftTo1254Mesh % Nodes % y(RightPath % NodeNumbers(i)) = ShiftTo1255END IF1256END DO1257CALL ComputePathExtent(RightPath, Mesh % Nodes, .FALSE.)12581259ELSE1260ShiftTo = Mesh % Nodes % y(RightPath % NodeNumbers(RightIdx))1261DO i=1,LeftPath % NumberOfNodes1262IF(Mesh % Nodes % y(LeftPath % NodeNumbers(i)) > ShiftTo) THEN1263IF(Debug) PRINT *,'Debug, overlap shifting left node ',i,' path ',&1264LeftPath % ID,' from ',Mesh % Nodes % y(LeftPath % NodeNumbers(i)),&1265' to ',ShiftTo1266Mesh % Nodes % y(LeftPath % NodeNumbers(i)) = ShiftTo1267END IF1268END DO1269CALL ComputePathExtent(LeftPath, Mesh % Nodes, .FALSE.)12701271END IF1272END IF12731274OtherPath => OtherPath % Next1275END DO12761277CurrentPath => CurrentPath % Next1278END DO12791280!-----------------------------------------------------------------------1281! Remove elements whose nodes are in a vertical line1282! (to prevent potential issues in interp)1283!-----------------------------------------------------------------------1284! This occurs due to the shifting which occurs above.1285! NOTE: This breaks the assumption that element(i) has nodes (i) & (i+1)1286! It also breaks the chain! Currently OK but don't rely on this below this1287! point, or in Calving3D.F901288!-----------------------------------------------------------------------12891290CurrentPath => CrevassePaths1291DO WHILE(ASSOCIATED(CurrentPath))12921293ALLOCATE(DeleteElement(CurrentPath % NumberOfElements),&1294DeleteNode(CurrentPath % NumberOfNodes))1295DeleteElement = .FALSE.1296DeleteNode = .FALSE.12971298DO i=1,CurrentPath % NumberOfElements1299!Element i is composed of nodes i,i+11300IF(Mesh % Nodes % y(CurrentPath % NodeNumbers(i)) == &1301Mesh % Nodes % y(CurrentPath % NodeNumbers(i+1))) THEN1302DeleteElement(i) = .TRUE.1303IF(Debug) PRINT *,'Debug, deleting element: ',i,' from path: ',&1304CurrentPath % ID,' because its a straight line'1305END IF1306END DO13071308IF(DeleteElement(1)) DeleteNode(1) = .TRUE.1309IF(DeleteElement(SIZE(DeleteElement))) DeleteNode(SIZE(DeleteNode)) = .TRUE.13101311DO i=2,CurrentPath % NumberOfNodes-11312IF(DeleteElement(i-1) .AND. DeleteElement(i)) DeleteNode(i) = .TRUE.1313END DO13141315!Delete them1316IF(COUNT(DeleteElement) > 0) THEN1317!elements1318ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))1319WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)13201321DEALLOCATE(CurrentPath % ElementNumbers)1322ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))13231324CurrentPath % ElementNumbers = WorkInt1325CurrentPath % NumberOfElements = SIZE(WorkInt)1326DEALLOCATE(WorkInt)13271328!nodes1329ALLOCATE(WorkInt(COUNT(.NOT. DeleteNode)))1330WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.DeleteNode)13311332DEALLOCATE(CurrentPath % NodeNumbers)1333ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))13341335CurrentPath % NodeNumbers = WorkInt1336CurrentPath % NumberOfNodes = SIZE(WorkInt)1337DEALLOCATE(WorkInt)1338END IF13391340DEALLOCATE(DeleteElement, DeleteNode)1341CurrentPath => CurrentPath % Next1342END DO13431344END IF !DoProjectible13451346!--------------------------------------------------------1347! Put the mesh back1348!--------------------------------------------------------1349CALL RotateMesh(Mesh, UnRotationMatrix)13501351END SUBROUTINE ValidateCrevassePaths13521353!Calculates the left and rightmost extent, and the difference (width) of1354!Path, given the node locations in Nodes.1355SUBROUTINE ComputePathExtent(CrevassePaths, Nodes, DoAll)1356TYPE(CrevassePath_t), POINTER :: CrevassePaths1357TYPE(Nodes_t), POINTER :: Nodes1358LOGICAL :: DoAll1359!-----------------------------------------------1360TYPE(CrevassePath_t), POINTER :: CurrentPath1361INTEGER :: n13621363CurrentPath => CrevassePaths1364DO WHILE(ASSOCIATED(CurrentPath))1365CurrentPath % Left = HUGE(1.0_dp)1366CurrentPath % Right = -1.0*HUGE(1.0_dp)13671368n = CurrentPath % NumberOfNodes13691370CurrentPath % Left = MINVAL(Nodes % y(CurrentPath % NodeNumbers))1371CurrentPath % Right = MAXVAL(Nodes % y(CurrentPath % NodeNumbers))13721373CurrentPath % Extent = CurrentPath % Right - CurrentPath % Left13741375CurrentPath => CurrentPath % Next13761377IF(.NOT. DoAll) EXIT1378END DO13791380END SUBROUTINE ComputePathExtent13811382!-----------------------------------------------------------------------------1383! Returns the Path ID of the CrevassePath_t which contains the given element1384! 0 if not found1385!-----------------------------------------------------------------------------1386FUNCTION ElementPathID(CrevassePaths, ElementNo) RESULT(ID)1387TYPE(CrevassePath_t), POINTER :: CrevassePaths1388INTEGER :: ElementNo, ID1389!----------------------------------------------1390TYPE(CrevassePath_t), POINTER :: CurrentPath13911392ID = 013931394CurrentPath => CrevassePaths1395DO WHILE(ASSOCIATED(CurrentPath))1396IF(ASSOCIATED(CurrentPath % ElementNumbers)) THEN1397IF(ANY(CurrentPath % ElementNumbers == ElementNo)) THEN1398ID = CurrentPath % ID1399EXIT1400END IF1401END IF1402CurrentPath => CurrentPath % Next1403END DO14041405END FUNCTION ElementPathID14061407!--------------------------------------------------------------------------1408!tests if a point is Left|On|Right of an infinite line.1409! Input: three points a, b, and c1410! Return: >0 for c left of the line AB1411! =0 for c on the line AB1412! <0 for c right of the line AB1413! used for winding number algorithm1414!---------------------------------------------------------------------------1415FUNCTION IsLeft(a, b, c) RESULT(d)1416REAL(kind=dp) :: a(2), b(2), c(2), d14171418d = (b(1)-a(1)) * (c(2)-a(2)) - &1419(c(1)-a(1)) * (b(2)-a(2))14201421END FUNCTION Isleft14221423!----------------------------------------------------------------------------1424! point in polygon - winding number algorithm1425!1426! input a polygon where polygon(1) = polygon(n) where n = SIZE(polygon)1427!----------------------------------------------------------------------------14281429FUNCTION PointInPolygon2D(Polygon, Point, buffer) RESULT(inside)1430REAL(kind=dp) :: polygon(:,:)1431REAL(kind=dp), ALLOCATABLE :: ZPolygon(:,:)1432REAL(kind=dp) :: left, point(2), ZPoint(2), buf1433REAL(kind=dp), OPTIONAL :: buffer1434INTEGER :: n, i, windingnumber1435LOGICAL :: inside14361437IF(SIZE(polygon(:,1)) /= 2) CALL FATAL('PointInPolygon2D', 'Please provide a 2D array with x and y coords')1438IF(PRESENT(buffer)) THEN1439buf = buffer1440ELSE1441buf = 0.0_dp1442END IF14431444n=SIZE(polygon(1,:))14451446ZPoint = Point1447ALLOCATE(ZPolygon(2,n))1448ZPolygon = Polygon1449CALL ZeroPolygon(ZPolygon, ZPoint)14501451windingnumber=1001452DO i=1, n-11453! polygon y i <= point y1454IF(ZPolygon(2,i) <= ZPoint(2) + buf) THEN !start with y<=P.y1455IF(ZPolygon(2, i+1) > ZPoint(2) - buf) THEN !upward crossing1456left=IsLeft(ZPolygon(:, i), ZPolygon(:, i+1), ZPoint(:))1457IF(left > buf) THEN !p is to left of intersect1458windingnumber=windingnumber+1 !valid up intersect1459END IF1460END IF1461ELSE !start at y> point y1462IF(ZPolygon(2, i+1) <= ZPoint(2) + buf) THEN ! downward crossing1463Left = IsLeft(ZPolygon(:, i), ZPolygon(:, i+1), ZPoint(:))1464IF(left < buf) THEN ! p right of edge1465windingnumber=windingnumber-11466END IF1467END IF1468END IF1469END DO14701471IF(windingnumber /= 100) THEN1472inside = .TRUE.1473ELSE1474inside = .FALSE.1475END IF14761477END FUNCTION PointInPolygon2D14781479!----------------------------------------------------------------------------1480! zeros polygon to reduce floating point errors in PointInPolygon2D1481!----------------------------------------------------------------------------14821483SUBROUTINE ZeroPolygon(Polygon, Point)1484REAL(kind=dp) :: Polygon(:,:), Point(2)1485REAL(kind=dp) :: minx, miny14861487minx = MINVAL(Polygon(1,:))1488miny = MINVAL(Polygon(2,:))14891490Polygon(1,:) = Polygon(1,:) - minx1491Polygon(2,:) = Polygon(2,:) - miny14921493Point(1) = Point(1) - minx1494Point(2) = Point(2) - miny14951496END SUBROUTINE ZeroPolygon14971498!-----------------------------------------------------------------------------1499! Constructs groups of nodes which fall below a given threshold for some variable1500! Used with the result of ProjectCalving, it groups nodes which have crevasse1501! penetration beyond the threshold.1502!-----------------------------------------------------------------------------1503SUBROUTINE FindCrevasseGroups(Mesh, Variable, Neighbours, Threshold, Groups)1504IMPLICIT NONE15051506TYPE(Mesh_t), POINTER :: Mesh1507TYPE(Variable_t), POINTER :: Variable1508INTEGER, POINTER :: Neighbours(:,:)1509TYPE(CrevasseGroup3D_t), POINTER :: Groups, CurrentGroup1510REAL(KIND=dp) :: Threshold1511!---------------------------------------1512INTEGER :: i, ID1513REAL(KIND=dp), POINTER :: Values(:)1514INTEGER, POINTER :: VPerm(:)1515INTEGER, ALLOCATABLE :: WorkInt(:)1516LOGICAL, ALLOCATABLE :: Condition(:)1517LOGICAL :: First, Debug15181519Debug = .FALSE.15201521Values => Variable % Values1522VPerm => Variable % Perm15231524ALLOCATE(Condition(Mesh % NumberOfNodes))1525DO i=1, Mesh % NumberOfNodes15261527IF(VPerm(i) <= 0) THEN1528Condition(i) = .FALSE.1529ELSE IF(Values(VPerm(i)) < Threshold) THEN1530Condition(i) = .TRUE.1531ELSE1532Condition(i) = .FALSE.1533END IF15341535END DO15361537First = .TRUE.1538ID = 11539DO i=1,Mesh % NumberOfNodes1540IF(.NOT. Condition(i)) CYCLE15411542IF(Debug) PRINT *,'PE:', ParEnv % MyPE,' debug, new group'15431544IF(First) THEN1545ALLOCATE(CurrentGroup)1546Groups => CurrentGroup1547First = .FALSE.1548ELSE1549ALLOCATE(CurrentGroup % Next)1550CurrentGroup % Next % Prev => CurrentGroup1551CurrentGroup => CurrentGroup % Next1552END IF15531554CurrentGroup % ID = ID1555ID = ID + 115561557ALLOCATE(CurrentGroup % NodeNumbers(500))1558CurrentGroup % NumberOfNodes = 115591560!Add node to group and switch it off1561CurrentGroup % NodeNumbers(CurrentGroup % NumberOfNodes) = i1562Condition(i) = .FALSE.15631564!Search neighbours1565CALL SearchNeighbours(i, Neighbours, CurrentGroup, Condition)15661567ALLOCATE(WorkInt(CurrentGroup % NumberOfNodes))1568WorkInt = CurrentGroup % NodeNumbers(1:CurrentGroup % NumberOfNodes)1569DEALLOCATE(CurrentGroup % NodeNumbers)1570ALLOCATE(CurrentGroup % NodeNumbers(CurrentGroup % NumberOfNodes))1571CurrentGroup % NodeNumbers = WorkInt1572DEALLOCATE(WorkInt)15731574CALL UpdateCGrpBB(CurrentGroup, Mesh)1575END DO15761577IF(Debug) THEN1578CurrentGroup => Groups1579i=11580DO WHILE(ASSOCIATED(CurrentGroup))1581PRINT *,'group: ',i,' has ', CurrentGroup % NumberOfNodes,' nodes.'1582i = i + 11583CurrentGroup => CurrentGroup % Next1584END DO1585END IF1586END SUBROUTINE FindCrevasseGroups15871588SUBROUTINE DeallocateCrevasseGroup(CGrp)1589TYPE(CrevasseGroup3D_t), POINTER :: CGrp15901591IF(ASSOCIATED(CGrp % Next)) CGrp % Next % Prev => CGrp % Prev1592IF(ASSOCIATED(CGrp % Prev)) CGrp % Prev % Next => CGrp % Next15931594IF(ASSOCIATED(CGrp % NodeNumbers)) DEALLOCATE(CGrp % NodeNumbers)1595IF(ASSOCIATED(CGrp % FrontNodes)) DEALLOCATE(CGrp % FrontNodes)1596IF(ASSOCIATED(CGrp % BoundaryNodes)) DEALLOCATE(CGrp % BoundaryNodes)15971598DEALLOCATE(CGrp)15991600END SUBROUTINE DeallocateCrevasseGroup16011602!Update the Bounding Box of a CrevasseGroup1603SUBROUTINE UpdateCGrpBB(CGrp, Mesh)1604TYPE(CrevasseGroup3D_t), POINTER :: CGrp1605TYPE(Mesh_t), POINTER :: Mesh16061607CGrp % BoundingBox(1) = MINVAL(Mesh % Nodes % x(CGrp % NodeNumbers))1608CGrp % BoundingBox(2) = MAXVAL(Mesh % Nodes % x(CGrp % NodeNumbers))1609CGrp % BoundingBox(3) = MINVAL(Mesh % Nodes % y(CGrp % NodeNumbers))1610CGrp % BoundingBox(4) = MAXVAL(Mesh % Nodes % y(CGrp % NodeNumbers))16111612END SUBROUTINE UpdateCGrpBB16131614!Add a list of points to a CrevasseGroup3D object1615!Don't need to pass the mesh because we're just adding1616!point indices1617SUBROUTINE AddNodesToGroup(Group, Points, PointCount)1618TYPE(CrevasseGroup3D_t), POINTER :: Group1619INTEGER :: Points(:)1620INTEGER, POINTER :: NewNodeNumbers(:)1621INTEGER :: PointCount, NewNumberOfNodes16221623NewNumberOfNodes = Group % NumberOfNodes + PointCount1624ALLOCATE(NewNodeNumbers(NewNumberOfNodes))16251626NewNodeNumbers(1:Group % NumberOfNodes) = Group % NodeNumbers1627NewNodeNumbers(Group % NumberOfNodes+1:NewNumberOfNodes) = Points(1:PointCount)16281629!Update count1630Group % NumberOfNodes = NewNumberOfNodes16311632!Point Group to new node list1633DEALLOCATE(Group % NodeNumbers)1634Group % NodeNumbers => NewNodeNumbers1635NULLIFY(NewNodeNumbers)1636END SUBROUTINE AddNodesToGroup16371638!------------------------------------------------------------1639! Routine to recursively search neighbours and put them1640! in the current group1641! Adapted from 2D Calving1642!------------------------------------------------------------1643RECURSIVE SUBROUTINE SearchNeighbours(nodenum, Neighbours, Group, Condition)1644INTEGER :: nodenum1645INTEGER, POINTER :: Neighbours(:,:)1646TYPE(CrevasseGroup3D_t), POINTER :: Group1647LOGICAL, ALLOCATABLE :: Condition(:)1648!------------------------------------------------1649INTEGER :: i, neighbourindex, NoNeighbours16501651NoNeighbours = COUNT(Neighbours(nodenum,:) > 0)1652DO i = 1,NoNeighbours1653neighbourindex = Neighbours(nodenum,i)1654IF(.NOT. Condition(neighbourindex)) CYCLE16551656Group % NumberOfNodes = Group % NumberOfNodes + 116571658!check space1659IF(Group % NumberOfNodes > SIZE(Group % NodeNumbers)) THEN1660PRINT *, 'Debug, need more space, allocating: ', 2*SIZE(Group % NodeNumbers)1661CALL DoubleIntVectorSize(Group % NodeNumbers)1662PRINT *, 'Debug, new size: ', SIZE(Group % NodeNumbers)1663END IF16641665Group % NodeNumbers(Group % NumberOfNodes) = neighbourindex16661667!Switch it off so it doesn't get readded1668Condition(neighbourindex) = .FALSE.16691670CALL SearchNeighbours(neighbourindex, Neighbours, Group, Condition)1671END DO16721673END SUBROUTINE SearchNeighbours16741675!Marks recursive neighbours with same int1676RECURSIVE SUBROUTINE MarkNeighbours(nodenum, Neighbours, Array, Mark)1677INTEGER :: nodenum1678INTEGER, POINTER :: Array(:)1679LOGICAL, POINTER :: Neighbours(:,:)1680!------------------------------------------------1681INTEGER :: i, Mark16821683DO i = 1,SIZE(Neighbours,1)1684IF(.NOT. Neighbours(nodenum,i)) CYCLE1685IF(Array(i)==Mark) CYCLE !already got16861687Array(i) = Mark1688CALL MarkNeighbours(i, Neighbours, Array, Mark)1689END DO16901691END SUBROUTINE MarkNeighbours16921693!-------------------------------------------------------------1694! Given a CrevasseGroup3D object, finds and stores boundary nodes1695! BoundaryMask is a logical array TRUE where node sits on a1696! mesh (not group) boundary1697! Note: Not used1698!-------------------------------------------------------------1699SUBROUTINE GetGroupBoundaryNodes(Group, Neighbours, BoundaryMask)1700TYPE(CrevasseGroup3D_t), POINTER :: Group1701INTEGER, POINTER :: Neighbours(:,:)1702LOGICAL :: BoundaryMask(:)1703!-----------------------------------------1704INTEGER :: i, j, node, BNodes, NoNeighbours, neighbour1705INTEGER, ALLOCATABLE :: WorkInt(:)1706LOGICAL :: IsBoundaryNode17071708IF(ASSOCIATED(Group % BoundaryNodes)) &1709DEALLOCATE(Group % BoundaryNodes)17101711ALLOCATE(Group % BoundaryNodes(100))1712Group % BoundaryNodes = 01713BNodes = 017141715DO i=1, Group % NumberOfNodes1716IsBoundaryNode = .FALSE.1717node = Group % NodeNumbers(i)17181719IF(BoundaryMask(node)) THEN1720IsBoundaryNode = .TRUE.1721ELSE1722NoNeighbours = COUNT(Neighbours(node, :) > 0)1723DO j=1,NoNeighbours1724neighbour = Neighbours(node, j)1725IF(ANY(Group % NodeNumbers == neighbour)) CYCLE17261727!Only get here if there's a node NOT in the group1728IsBoundaryNode = .TRUE.1729EXIT1730END DO1731END IF17321733IF(IsBoundaryNode) THEN1734BNodes = BNodes + 11735IF(BNodes > SIZE(Group % BoundaryNodes)) &1736CALL DoubleIntVectorSize(Group % BoundaryNodes)1737Group % BoundaryNodes(BNodes) = node1738END IF1739END DO17401741ALLOCATE(WorkInt(BNodes))1742WorkInt = Group % BoundaryNodes(1:BNodes)1743DEALLOCATE(Group % BoundaryNodes)1744ALLOCATE(Group % BoundaryNodes(BNodes))1745Group % BoundaryNodes = WorkInt1746DEALLOCATE(WorkInt)17471748!TODO: Order boundary nodes (clockwise?)1749END SUBROUTINE GetGroupBoundaryNodes17501751!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1752! Function to detect if a given node lies within1753! a 3D crevasse group (physically, not 'graph'ically1754! Note: not used...1755!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1756FUNCTION NodeInCrevasseGroup(NodeNumber, Nodes, CrevasseGroup) RESULT(InGroup)1757INTEGER :: NodeNumber1758TYPE(Nodes_t) :: Nodes1759TYPE(CrevasseGroup3D_t) :: CrevasseGroup1760!--------------------------------------------------1761LOGICAL :: InGroup1762REAL(KIND=dp) :: node_x,node_y, BB(4)17631764IF(ANY(CrevasseGroup % NodeNumbers == NodeNumber)) &1765CALL Fatal("NodeInCrevasseGroup", "Scanning for node which&1766&belongs to CrevasseGroup. This is not intended usage!")17671768node_x = Nodes % x(NodeNumber)1769node_y = Nodes % y(NodeNumber)17701771BB = CrevasseGroup % BoundingBox17721773IF(node_x < BB(1) .OR. node_x > BB(2) .OR. &1774node_y < BB(3) .OR. node_y > BB(4)) THEN17751776InGroup = .FALSE.1777RETURN17781779END IF17801781CALL Fatal("NodeInCrevasseGroup", "Haven't finished implementing this yet!")17821783!Recursively look at node neighbours, stopping when we reach a group member,1784!until we reach freedom (a mesh boundary node) or give up (node is contained1785!within crevassegroup, and this tells us about the topology of the group)17861787!RETURN should not just be a logical, this should be repurposed to inform1788!about which boundary it reached.17891790END FUNCTION NodeInCrevasseGroup17911792!Doubles the size of a pointer integer array1793!This version takes a Pointer argument, should1794!be used with care...1795SUBROUTINE DoubleIntVectorSizeP(Vec, fill)1796INTEGER, POINTER :: Vec(:)1797INTEGER, OPTIONAL :: fill1798!----------------------------------------1799INTEGER, ALLOCATABLE :: WorkVec(:)18001801ALLOCATE(WorkVec(SIZE(Vec)))1802WorkVec = Vec18031804DEALLOCATE(Vec)1805ALLOCATE(Vec(2*SIZE(WorkVec)))18061807IF(PRESENT(fill)) THEN1808Vec = fill1809ELSE1810Vec = 01811END IF18121813Vec(1:SIZE(WorkVec)) = WorkVec18141815END SUBROUTINE DoubleIntVectorSizeP18161817!Doubles the size of a pointer integer array1818!Allocatable array version1819SUBROUTINE DoubleIntVectorSizeA(Vec, fill)1820INTEGER, ALLOCATABLE :: Vec(:)1821INTEGER, OPTIONAL :: fill1822!----------------------------------------1823INTEGER, ALLOCATABLE :: WorkVec(:)18241825ALLOCATE(WorkVec(SIZE(Vec)))1826WorkVec = Vec18271828DEALLOCATE(Vec)1829ALLOCATE(Vec(2*SIZE(WorkVec)))18301831IF(PRESENT(fill)) THEN1832Vec = fill1833ELSE1834Vec = 01835END IF18361837Vec(1:SIZE(WorkVec)) = WorkVec18381839END SUBROUTINE DoubleIntVectorSizeA184018411842!-----------------------------------------------------------------------------1843!Given a Nodes_t object, removes the nodes specified by RemoveLogical array1844!Optionally, user may provide a list of node numbers (NodeNums), from which1845!relevant nodes will also be removed1846SUBROUTINE RemoveNodes(InNodes, RemoveLogical, NodeNums)1847TYPE(Nodes_t) :: InNodes, WorkNodes1848LOGICAL, ALLOCATABLE :: RemoveLogical(:)1849INTEGER :: i,counter1850INTEGER, POINTER, OPTIONAL :: NodeNums(:)1851INTEGER, ALLOCATABLE :: WorkNodeNums(:)18521853WorkNodes % NumberOfNodes = SIZE(InNodes % x) - COUNT(RemoveLogical)18541855ALLOCATE(WorkNodes % x(WorkNodes % NumberOfNodes),&1856WorkNodes % y(WorkNodes % NumberOfNodes),&1857WorkNodes % z(WorkNodes % NumberOfNodes))1858IF(PRESENT(NodeNums)) ALLOCATE(WorkNodeNums(WorkNodes % NumberOfNodes))18591860counter = 11861DO i=1,InNodes % NumberOfNodes1862IF(.NOT. RemoveLogical(i)) THEN1863WorkNodes % x(counter) = InNodes % x(i)1864WorkNodes % y(counter) = InNodes % y(i)1865WorkNodes % z(counter) = InNodes % z(i)1866IF(PRESENT(NodeNums)) WorkNodeNums(counter) = NodeNums(i)18671868counter = counter + 11869END IF1870END DO18711872DEALLOCATE(InNodes % x, InNodes % y, InNodes % z )1873ALLOCATE(InNodes % x(WorkNodes % NumberOfNodes), &1874InNodes % y(WorkNodes % NumberOfNodes), &1875InNodes % z(WorkNodes % NumberOfNodes))18761877IF(PRESENT(NodeNums)) THEN1878DEALLOCATE(NodeNums)1879ALLOCATE(NodeNums(WorkNodes % NumberOfNodes))1880END IF18811882InNodes % NumberOfNodes = WorkNodes % NumberOfNodes1883InNodes % x = WorkNodes % x1884InNodes % y = WorkNodes % y1885InNodes % z = WorkNodes % z1886IF(PRESENT(NodeNums)) NodeNums = WorkNodeNums18871888DEALLOCATE(WorkNodes % x, WorkNodes % y, WorkNodes % z)1889IF(PRESENT(NodeNums)) DEALLOCATE(WorkNodeNums)1890END SUBROUTINE RemoveNodes18911892!------------------------------------------------------------------------------1893!> Sort an index array, and change the order of an real array accordingly.1894!> Stolen from GeneralUtils, modified so as to leave the initial index array in order1895!------------------------------------------------------------------------------1896SUBROUTINE MySortF( n,c,b )1897!------------------------------------------------------------------------------1898INTEGER :: n,c(:)1899INTEGER, ALLOCATABLE :: a(:)1900REAL(KIND=dp) :: b(:)1901!------------------------------------------------------------------------------19021903INTEGER :: i,j,l,ir,ra1904REAL(KIND=dp) :: rb1905!------------------------------------------------------------------------------19061907ALLOCATE(a(SIZE(c)))1908a = c19091910IF ( n <= 1 ) RETURN19111912l = n / 2 + 11913ir = n1914DO WHILE( .TRUE. )19151916IF ( l > 1 ) THEN1917l = l - 11918ra = a(l)1919rb = b(l)1920ELSE1921ra = a(ir)1922rb = b(ir)1923a(ir) = a(1)1924b(ir) = b(1)1925ir = ir - 11926IF ( ir == 1 ) THEN1927a(1) = ra1928b(1) = rb1929RETURN1930END IF1931END IF1932i = l1933j = l + l1934DO WHILE( j <= ir )1935IF ( j<ir ) THEN1936IF ( a(j)<a(j+1) ) j = j+11937END IF1938IF ( ra<a(j) ) THEN1939a(i) = a(j)1940b(i) = b(j)1941i = j1942j = j + i1943ELSE1944j = ir + 11945END IF1946a(i) = ra1947b(i) = rb1948END DO1949END DO19501951DEALLOCATE(a)19521953!------------------------------------------------------------------------------1954END SUBROUTINE MySortF1955!------------------------------------------------------------------------------195619571958!If EdgeMaskName is not provided, returns the ring of nodes which define the extent1959!of the upper surface of the mesh, arbitrarily beginning with the nodes from the lowest1960!partition (PE).1961!If EdgeMaskName is provided, this specifies a lateral margin. Then this returns an1962!ordered list of nodenumbers which specify an edge of a domain,1963!where the edge is determined by the overlap between the two provided permutations1964!NOTE: Returned domain edge is valid only on boss partition (PE=0)1965SUBROUTINE GetDomainEdge(Model, Mesh, TopPerm, OrderedNodes, OrderedNodeNums, Parallel, &1966EdgeMaskName, Simplify, MinDist)19671968IMPLICIT NONE19691970TYPE(Model_t) :: Model1971TYPE(Mesh_t), POINTER :: Mesh1972INTEGER, POINTER :: TopPerm(:)1973TYPE(Nodes_t) :: OrderedNodes, UnorderedNodes1974LOGICAL :: Parallel1975CHARACTER(MAX_NAME_LEN), OPTIONAL :: EdgeMaskName1976LOGICAL, OPTIONAL :: Simplify1977REAL(KIND=dp), OPTIONAL :: MinDist1978!----------------------------------------------------------------1979TYPE(Element_t), POINTER :: Element1980TYPE(NeighbourList_T), ALLOCATABLE :: PartNeighbourList(:)1981INTEGER :: i,j,k,m,n,prev,next,part_start,find_start,find_fin,find_stride,put_start,&1982put_fin, counter,NoNodes, NoNodesOnEdge, NoNeighbours, neigh, Segments, TotSegSplits, &1983direction, index, segnum, soff, foff, target_nodenum, next_nodenum, EdgeBCtag, GlobalNN1984INTEGER :: comm, ierr !MPI stuff1985INTEGER, POINTER :: UnorderedNodeNums(:)=>NULL(), OrderedNodeNums(:), &1986UOGlobalNodeNums(:)=>NULL(), OrderedGlobalNodeNums(:)=>NULL()1987INTEGER, ALLOCATABLE :: NeighbourPartsList(:), PartNodesOnEdge(:), &1988disps(:), nodenum_disps(:), PartOrder(:,:), MyCornerNodes(:), MyNeighbourParts(:), &1989NewSegStart(:), PartSegments(:), SegStarts_Gather(:), WorkInt(:), NodeNeighbours(:,:), &1990GlobalCorners(:), CornerParts(:), PCornerCounts(:)1991LOGICAL :: Debug, ActivePart, Boss, Simpl, NotThis, Found, ThisBC, FullBoundary1992LOGICAL, ALLOCATABLE :: OnEdge(:), ActivePartList(:), RemoveNode(:), IsCornerNode(:)1993REAL(KIND=dp) :: prec, CCW_value1994REAL(KIND=dp), ALLOCATABLE :: WorkReal(:,:)1995CHARACTER(MAX_NAME_LEN) :: FuncName19961997TYPE AllocIntList_t1998INTEGER, DIMENSION(:), POINTER :: Indices1999END TYPE AllocIntList_t2000TYPE(AllocIntList_t), ALLOCATABLE :: PartSegStarts(:)20012002!------------------------------------------------2003! Change in strategy:2004!2005! Previously, used stiffness matrix to determine connectivity, but2006! this causes problems when multiple nodes on the boundary reside2007! in the same top surface tri element:2008!2009! *===*===*---*===*===*2010! from this one---^\ /2011! *2012! ^-- we want this one2013!2014! Various versions of this issue can occur...2015!2016! SO, instead of using the stiffness matrix, we should2017! check all the boundary elements on the relevant SIDE2018! boundary (e.g. calving front, right sidewall...),2019! looking for elements containing nodes for which the2020! top mask is true.2021!2022! Because of the extruded structure of the mesh, nodes2023! within the same boundary quad will always be neighbours,2024! and each node shall have no more than 2 neighbours.2025!----------------------------------------------------20262027FuncName = "GetDomainEdge"2028Debug = .FALSE.2029ActivePart = .TRUE.20302031NoNodes = SIZE(TopPerm) !total number of nodes in domain/partition20322033IF(Parallel) THEN2034comm = ELMER_COMM_WORLD2035Boss = (ParEnv % MyPE == 0)2036ELSE2037Boss = .TRUE. !only one part in serial, so it's in charge of computation2038END IF20392040IF(Boss .AND. Debug .AND. PRESENT(EdgeMaskName)) THEN2041PRINT *, '================================================='2042PRINT *, ' Locating domain edge for ',TRIM(EdgeMaskName)2043PRINT *, '================================================='2044END IF20452046IF(PRESENT(Simplify)) THEN2047Simpl = Simplify2048ELSE2049Simpl = .FALSE.2050END IF20512052ALLOCATE(OnEdge(NoNodes), NodeNeighbours(NoNodes,2))2053OnEdge = .FALSE.2054NodeNeighbours = -120552056FullBoundary = .NOT.(PRESENT(EdgeMaskName))2057IF(.NOT. FullBoundary) THEN2058!Find correct BC from logical2059DO i=1,Model % NumberOfBCs2060ThisBC = ListGetLogical(Model % BCs(i) % Values,EdgeMaskName,Found)2061IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE2062EdgeBCtag = Model % BCs(i) % Tag2063EXIT2064END DO2065END IF20662067!Cycle boundary elements, marking nodes on edge and finding neighbours2068DO i=Mesh % NumberOfBulkElements+1, &2069Mesh % NumberOfBulkElements+Mesh % NumberOfBoundaryElements2070Element => Mesh % Elements(i)20712072IF((.NOT. FullBoundary) .AND. Element % BoundaryInfo % Constraint /= EdgeBCtag) &2073CYCLE !elem not on lateral boundary20742075IF(ALL(TopPerm(Element % NodeIndexes) > 0)) CYCLE !not a lateral element2076IF(.NOT. ANY(TopPerm(Element % NodeIndexes) > 0)) CYCLE !elem contains no nodes on top2077!Logic gates above should leave only lateral elements with some nodes on top.20782079IF(GetElementFamily(Element) == 1) &2080CALL Fatal(FuncName, "101 Elements are supposed to be a thing of the past!")20812082!Cycle nodes in element2083DO j=1,Element % TYPE % NumberOfNodes2084IF(.NOT. TopPerm(Element % NodeIndexes(j)) > 0) CYCLE2085OnEdge(Element % NodeIndexes(j)) = .TRUE.20862087!Cycle nodes in element2088DO k=1,Element % TYPE % NumberOfNodes2089IF(j==k) CYCLE2090IF(.NOT. TopPerm(Element % NodeIndexes(k))>0) CYCLE2091DO m=1,2 !fill NodeNeighbours2092IF(NodeNeighbours(Element % NodeIndexes(j),m) /= -1) CYCLE2093NodeNeighbours(Element % NodeIndexes(j),m) = Element % NodeIndexes(k)2094EXIT2095END DO2096IF(.NOT. ANY(NodeNeighbours(Element % NodeIndexes(j),:) == Element % NodeIndexes(k))) &2097CALL Fatal(FuncName,'Identified more than two neighbours')2098END DO2099END DO21002101END DO21022103NoNodesOnEdge = COUNT(OnEdge)2104IF(NoNodesOnEdge == 1) THEN2105CALL Fatal(FuncName, "A single node identified on boundary, should not be possible. &2106&Someone is messing around with 101 elements.")2107END IF21082109ALLOCATE(UnorderedNodeNums(NoNodesOnEdge),&2110OrderedNodeNums(NoNodesOnEdge))2111OrderedNodeNums = -1 !initialize to invalid value21122113j = 02114DO i=1,NoNodes2115IF(.NOT. OnEdge(i)) CYCLE2116j = j + 12117UnorderedNodeNums(j) = i2118END DO21192120!Cycle nodes on edge, looking for one with only one neighbour (a corner)2121!Edge case = serial fullboundary run, no corner exists, choose arbitrarily2122!Rare case (not dealt with!! TODO) = parallel fullboundary, no corners2123! (whole mesh edge in one partition)2124IF(NoNodesOnEdge > 1) THEN21252126ALLOCATE(IsCornerNode(NoNodesOnEdge))2127IsCornerNode = .FALSE.21282129DO i=1,NoNodesOnEdge2130IsCornerNode(i) = COUNT(NodeNeighbours(UnOrderedNodeNums(i),:) == -1) == 12131IF(COUNT(NodeNeighbours(UnOrderedNodeNums(i),:) == -1) == 2) &2132CALL Fatal(FuncName, "Found an isolated node on edge")2133END DO21342135IF(MOD(COUNT(IsCornerNode),2) /= 0) THEN2136WRITE(Message,'(A,i0)') "Found an odd number of&2137& corner nodes in partition: ",ParEnv % MyPE2138CALL Fatal(FuncName, Message)2139END IF21402141IF(FullBoundary .AND. .NOT. Parallel) THEN21422143!If serial FullBoundary request, no corner exists so just choose the first2144!unordered node in the list and loop from there2145Segments = 12146ALLOCATE(MyCornerNodes(2))2147MyCornerNodes(1) = 121482149ELSE21502151Segments = COUNT(IsCornerNode) / 22152IF(Debug .AND. Segments > 1) PRINT *, &2153'Partition ',ParEnv % MyPE, ' has ',Segments,' line segments on boundary.'21542155ALLOCATE(NewSegStart(Segments-1))2156ALLOCATE(MyCornerNodes(COUNT(IsCornerNode)))21572158counter = 12159DO i=1,NoNodesOnEdge2160IF(IsCornerNode(i)) THEN2161MyCornerNodes(counter) = i2162counter = counter + 12163END IF2164END DO21652166END IF21672168counter = 12169DO k=1,Segments21702171IF(k==1) THEN2172OrderedNodeNums(counter) = UnorderedNodeNums(MyCornerNodes(1))2173ELSE2174DO i=2, SIZE(MyCornerNodes)2175IF(ANY(OrderedNodeNums == UnorderedNodeNums(MyCornerNodes(i)))) THEN2176CYCLE2177ELSE2178OrderedNodeNums(counter) = UnorderedNodeNums(MyCornerNodes(i))2179EXIT2180END IF2181END DO2182END IF2183counter = counter + 121842185!----------------------------------------------------2186! Move along from corner, filling in order2187!----------------------------------------------------2188DO i=counter,NoNodesOnEdge2189Found = .FALSE.2190IF(OrderedNodeNums(i-1) == -1) CALL Abort()21912192DO j=1,22193IF(NodeNeighbours(OrderedNodeNums(i-1),j) == -1) CYCLE !First and last nodes, corner2194IF(ANY(OrderedNodeNums(1:i-1) == NodeNeighbours(OrderedNodeNums(i-1),j))) &2195CYCLE !already in list21962197OrderedNodeNums(i) = NodeNeighbours(OrderedNodeNums(i-1),j)2198Found = .TRUE.2199END DO22002201IF(.NOT. Found) EXIT2202END DO22032204counter = i22052206IF(counter >= NoNodesOnEdge) EXIT !this should be redundant...2207NewSegStart(k) = counter2208END DO22092210ELSE !Either 1 or 0 nodes found, not an active boundary partition2211!0 node case, obvious2212!1 node case, if THIS partition only has one node on the boundary,2213!this same node must be caught by two other partitions, so we aren't needed.2214ALLOCATE(NewSegStart(0), MyCornerNodes(0))2215ActivePart = .FALSE.2216Segments = 02217NoNodesOnEdge = 0 !simplifies mpi comms later2218IF(.NOT.Parallel) CALL Fatal(FuncName,&2219"Found either 1 or 0 nodes in a serial run, this isn't a valid boundary edge!")2220END IF222122222223!Remember that, in parallel, we're using local rather than global node numbers2224IF(Parallel) THEN22252226!gather corner count - replaces 101 element detection2227ALLOCATE(PCornerCounts(ParEnv % PEs),disps(ParEnv % PEs))22282229CALL MPI_AllGather(SIZE(MyCornerNodes), 1, MPI_INTEGER, PCornerCounts, &22301, MPI_INTEGER, ELMER_COMM_WORLD, ierr)22312232disps(1) = 02233DO i=2, ParEnv % PEs2234disps(i) = disps(i-1) + PCornerCounts(i-1)2235END DO22362237ALLOCATE(GlobalCorners(SUM(PCornerCounts)),&2238CornerParts(SUM(PCornerCounts)))22392240!gather corner nodenums2241CALL MPI_AllGatherV(Mesh % ParallelInfo % GlobalDOFs(UnorderedNodeNums(MyCornerNodes)), &2242SIZE(MyCornerNodes), MPI_INTEGER, GlobalCorners, PCornerCounts, disps, &2243MPI_INTEGER, ELMER_COMM_WORLD, ierr)22442245!note which partition sent each corner node2246counter = 12247DO i=1,ParEnv % PEs2248IF(PCornerCounts(i) == 0) CYCLE2249CornerParts(counter:counter+PCornerCounts(i)-1) = i-12250counter = counter + PCornerCounts(i)2251END DO22522253!Quick check:2254DO i=1,SIZE(GlobalCorners)2255counter = COUNT(GlobalCorners == GlobalCorners(i))2256IF(counter > 2) CALL Fatal(FuncName,"Programming error in partition &2257&segment detection, node found too many times!")2258END DO2259!Now GlobalCorners and CornerParts tell us which partitions found corner nodes2260!(i.e. nodes which will join other segments)22612262IF(ActivePart) THEN2263ALLOCATE(MyNeighbourParts(Segments*2))22642265DO i=1,Segments*2 !Find neighbour partition numbers22662267IF(i==1) THEN2268n = OrderedNodeNums(1)2269ELSE IF(i==Segments*2) THEN2270n = OrderedNodeNums(NoNodesOnEdge)2271ELSE IF(MOD(i,2)==0) THEN2272n = OrderedNodeNums(NewSegStart(i/2)-1)2273ELSE2274n = OrderedNodeNums(NewSegStart(i/2))2275END IF22762277MyNeighbourParts(i) = -1 !default if not caught in loop below2278GlobalNN = Mesh % ParallelInfo % GlobalDOFs(n)2279DO j=1,SIZE(GlobalCorners)2280IF(GlobalCorners(j) /= GlobalNN) CYCLE2281IF(CornerParts(j) == ParEnv % MyPE) CYCLE2282MyNeighbourParts(i) = CornerParts(j)2283IF( .NOT. (ANY(Mesh % ParallelInfo % NeighbourList(n) % Neighbours &2284== MyNeighbourParts(i)))) CALL Fatal(FuncName, &2285"Failed sanity check on neighbour partition detection.")2286END DO2287END DO2288ELSE2289ALLOCATE(MyNeighbourParts(0))2290END IF22912292IF(Boss) ALLOCATE(PartSegments(ParEnv % PEs))22932294CALL MPI_GATHER(Segments, 1, MPI_INTEGER, PartSegments, &22951, MPI_INTEGER, 0, comm, ierr)22962297IF(Boss) THEN22982299TotSegSplits = 02300DO i=1,SIZE(PartSegments)2301TotSegSplits = TotSegSplits + MAX(PartSegments(i)-1,0)2302END DO23032304ALLOCATE(nodenum_disps(ParEnv % PEs), &2305PartNodesOnEdge(ParEnv % PEs), &2306NeighbourPartsList(SUM(PartSegments)*2), &2307PartNeighbourList(ParEnv % PEs), &2308SegStarts_Gather(TotSegSplits))23092310DO i=1,ParEnv % PEs2311ALLOCATE(PartNeighbourList(i) % Neighbours(PartSegments(i)*2))2312END DO23132314disps(1) = 02315DO i=2, ParEnv % PEs2316disps(i) = disps(i-1) + MAX(PartSegments(i-1)-1,0)2317END DO23182319END IF23202321!Get found count from each part to boss2322CALL MPI_GATHER(NoNodesOnEdge, 1, MPI_INTEGER, PartNodesOnEdge, &23231, MPI_INTEGER, 0, comm ,ierr)2324IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")2325CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)23262327IF(Debug .AND. Boss) THEN2328PRINT *, 'boss size(SegStarts_Gather): ', SIZE(SegStarts_Gather)2329PRINT *, 'boss PartSegments: ', PartSegments2330PRINT *, 'boss disps:', disps2331DO i=1,ParEnv % PEs2332IF(PartNodesOnEdge(i) == 0) CYCLE2333PRINT *, 'partition ',i-1,' NoNodesOnEdge: ',PartNodesOnEdge(i)2334END DO2335END IF23362337IF(Boss) THEN2338ALLOCATE(WorkInt(ParEnv % PEs))2339WorkInt = MAX(PartSegments-1,0)2340END IF23412342CALL MPI_GATHERV(NewSegStart, MAX(Segments-1,0), MPI_INTEGER, SegStarts_Gather, &2343WorkInt, disps, MPI_INTEGER, 0, comm, ierr)2344IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")2345CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)23462347IF(Boss) THEN2348ALLOCATE(PartSegStarts(ParEnv % PEs))2349DO i=1,ParEnv % PEs2350j = PartSegments(i)2351ALLOCATE( PartSegStarts(i) % Indices(MAX((j - 1),0)))2352IF(j > 1) THEN2353IF(Debug) PRINT *, 'debug disps(i),j', disps(i),j2354PartSegStarts(i) % Indices = SegStarts_Gather(1+disps(i) : (1+disps(i) + (j-1)-1) )2355END IF2356IF(Debug) PRINT *, i,' partsegstarts: ', PartSegStarts(i) % Indices2357END DO23582359disps(1) = 02360DO i=2, ParEnv % PEs2361disps(i) = disps(i-1) + PartSegments(i-1)*22362END DO23632364WorkInt = PartSegments*22365END IF23662367!Get neighbour part numbers from each part to boss2368CALL MPI_GATHERV(MyNeighbourParts, Segments*2, MPI_INTEGER, NeighbourPartsList, &2369WorkInt, disps, MPI_INTEGER, 0, comm ,ierr)2370IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")2371CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)23722373IF(Debug .AND. Boss) PRINT *, 'DEBUG, NewSegStart: ', NewSegStart23742375IF(Boss) THEN2376ActivePartList = (PartNodesOnEdge > 0)23772378!Here we account for shared nodes on partition boundaries2379OrderedNodes % NumberOfNodes = SUM(PartNodesOnEdge) - (SIZE(NeighbourPartsList)/2 - 1)2380!but they are still present when gathered...2381UnorderedNodes % NumberOfNodes = SUM(PartNodesOnEdge)23822383ALLOCATE(PartOrder(SIZE(NeighbourPartsList)/2,2),&2384OrderedNodes % x(OrderedNodes % NumberOfNodes),&2385OrderedNodes % y(OrderedNodes % NumberOfNodes),&2386OrderedNodes % z(OrderedNodes % NumberOfNodes),&2387UnorderedNodes % x(UnorderedNodes % NumberOfNodes),&2388UnorderedNodes % y(UnorderedNodes % NumberOfNodes),&2389UnorderedNodes % z(UnorderedNodes % NumberOfNodes),&2390UOGlobalNodeNums(UnorderedNodes % NumberOfNodes),&2391OrderedGlobalNodeNums(OrderedNodes % NumberOfNodes))23922393nodenum_disps(1) = 02394DO i=2, ParEnv % PEs2395nodenum_disps(i) = nodenum_disps(i-1) + PartNodesOnEdge(i-1)2396END DO23972398IF(Debug) THEN2399PRINT *, 'debug disps: ', disps2400PRINT *, 'debug nodenum_disps: ', nodenum_disps2401PRINT *, 'debug neighbourpartslist: ',NeighbourPartsList2402PRINT *, 'Partition Segments: ',PartSegments2403END IF2404END IF24052406!-----------------------------------------------------------2407! Gather node coords from all partitions2408! Note, they're going into 'UnorderedNodes': though they are ordered2409! within their partition, the partitions aren't ordered...2410!-----------------------------------------------------------24112412!Global Node Numbers2413CALL MPI_GATHERV(Mesh % ParallelInfo % GlobalDOFs(OrderedNodeNums),&2414NoNodesOnEdge,MPI_INTEGER,&2415UOGlobalNodeNums,PartNodesOnEdge,&2416nodenum_disps,MPI_INTEGER,0,comm, ierr)2417IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")2418CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)24192420!X coords2421CALL MPI_GATHERV(Mesh % Nodes % x(OrderedNodeNums),&2422NoNodesOnEdge,MPI_DOUBLE_PRECISION,&2423UnorderedNodes % x,PartNodesOnEdge,&2424nodenum_disps,MPI_DOUBLE_PRECISION,0,comm, ierr)2425IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")2426CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)24272428!Y coords2429CALL MPI_GATHERV(Mesh % Nodes % y(OrderedNodeNums),&2430NoNodesOnEdge,MPI_DOUBLE_PRECISION,&2431UnorderedNodes % y,PartNodesOnEdge,&2432nodenum_disps,MPI_DOUBLE_PRECISION,0,comm, ierr)2433IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")2434CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)24352436!Z coords2437CALL MPI_GATHERV(Mesh % Nodes % z(OrderedNodeNums),&2438NoNodesOnEdge,MPI_DOUBLE_PRECISION,&2439UnorderedNodes % z,PartNodesOnEdge,&2440nodenum_disps,MPI_DOUBLE_PRECISION,0,comm, ierr)2441IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")2442CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)24432444!-----------------------------------------------------------2445! Determine order of partitions by linking neighbours and2446! checking globalnodenumbers where appropriate2447!-----------------------------------------------------------24482449IF(Boss) THEN2450!Notes: NeighbourPartsList is zero indexed, like PEs2451!PartOrder is 1 indexed2452!disps is 1 indexed. So disps(NeighbourPartsList+1)24532454PartOrder = 0 !init2455direction = 02456prev = -12457next = 024582459!First fill in PartNeighbourList % Neighbours2460DO i=1,ParEnv % PEs2461IF(PartSegments(i)==0) CYCLE2462PartNeighbourList(i) % Neighbours = &2463NeighbourPartsList( (1+disps(i)) : (1+disps(i) + (PartSegments(i)*2) - 1) )2464!There is the possibility of missing an end (-1) due to partition2465!landing right on corner2466DO j=1,SIZE(PartNeighbourList(i) % Neighbours)2467IF(PartNeighbourList(i) % Neighbours(j) == -1) CYCLE2468IF(PartSegments(PartNeighbourList(i) % Neighbours(j)+1) < 1) THEN2469IF(Debug) PRINT *, 'Neighbour ',PartNeighbourList(i) % Neighbours(j)+1,&2470"isn't really on boundary, so changing to -1"2471PartNeighbourList(i) % Neighbours(j) = -12472END IF2473END DO24742475IF(Debug) PRINT *, i-1, ': Neighbours: ', PartNeighbourList(i) % Neighbours2476!find a corner partition2477IF(ANY(PartNeighbourList(i) % Neighbours == prev)) next = i2478END DO24792480!No partition had corner (-1)2481IF(next==0) THEN2482IF(FullBoundary) THEN !this is expected, a closed loop so no -12483DO i=1,ParEnv % PEs2484IF(PartSegments(i)>0) THEN2485next = i2486prev = PartNeighbourList(i) % Neighbours(1)2487EXIT2488END IF2489END DO2490ELSE2491CALL Fatal(FuncName,"Error finding corner of requested boundary in partitions.")2492END IF2493ELSE IF(FullBoundary) THEN2494CALL Fatal(FuncName,"Error - found corner but requested FullBoundary&2495&- programming mistake.")2496END IF24972498IF(Debug) THEN2499PRINT *, 'Debug GetDomainEdge, globalno, unorderednodes % x: '2500DO i=1,SIZE(UOGlobalNodeNums)2501PRINT *, i, UOGlobalNodeNums(i), UnorderedNodes % x(i)2502END DO25032504PRINT *, 'debug nodenum_disps: '2505DO i=1, SIZE(nodenum_disps)2506PRINT *, i,' ',nodenum_disps(i)2507END DO2508END IF250925102511counter = 125122513DO WHILE(.TRUE.)2514IF(Debug) PRINT *,'Next Partition is: ',next2515IF((COUNT(PartNeighbourList(next) % Neighbours == prev) == 1) .OR. &2516(prev == -1)) THEN2517DO j=1,SIZE(PartNeighbourList(next) % Neighbours)2518IF(PartNeighbourList(next) % Neighbours(j) == prev) THEN2519index = j2520EXIT2521END IF2522END DO2523ELSE !Neighbours on both sides, so need to inspect globalnodenumbers2524IF(Debug) PRINT *, 'debug, two matches'2525DO j=1,SIZE(PartNeighbourList(next) % Neighbours)2526IF(PartNeighbourList(next) % Neighbours(j) == prev) THEN25272528segnum = ((j-1)/2) + 12529direction = (2 * MOD(j, 2)) - 125302531IF(segnum == 1) THEN2532soff = 02533ELSE2534soff = PartSegStarts(next) % Indices(segnum - 1) - 12535END IF2536IF(segnum == PartSegments(next)) THEN2537foff = 02538ELSE2539foff = -1 * (PartNodesOnEdge(next) - PartSegStarts(next) % Indices(segnum) + 1)2540END IF25412542IF(direction > 0) THEN2543next_nodenum = UOGlobalNodeNums(1 + nodenum_disps(next) + soff)2544ELSE2545!one node before (-1) the next partition's (+1) nodes2546IF(next == ParEnv % PEs) THEN2547k = SIZE(UOGlobalNodeNums)2548ELSE2549k = 1 + nodenum_disps(next+1) - 12550END IF2551next_nodenum = UOGlobalNodeNums(k + foff)2552END IF2553IF(Debug) THEN2554PRINT *, 'debug, next_nodenum: ', next_nodenum2555PRINT *, 'debug, target_nodenum: ', target_nodenum2556END IF2557IF(next_nodenum == target_nodenum) THEN2558index = j2559EXIT2560END IF2561END IF2562END DO2563END IF25642565segnum = ((index-1)/2) + 1 !1,2 -> 1, 3,4 -> 22566direction = (2 * MOD(index, 2)) - 12567PartOrder(counter,1) = next - 12568PartOrder(counter,2) = direction * segnum2569counter = counter + 125702571IF(Debug) THEN2572PRINT *, 'index: ', index2573PRINT *, 'segnum: ', segnum2574PRINT *, 'direction: ',direction2575PRINT *, 'next: ', next2576PRINT *, 'prev: ', prev2577END IF25782579prev = next - 12580j = next2581next = PartNeighbourList(next) % Neighbours(index + direction)25822583!In case of two matches, need a target node to find2584IF(segnum == 1) THEN2585soff = 02586ELSE2587soff = PartSegStarts(j) % Indices(segnum - 1) - 12588END IF2589IF(segnum == PartSegments(j)) THEN2590foff = 02591ELSE2592foff = -1 * (PartNodesOnEdge(j) - PartSegStarts(j) % Indices(segnum) + 1)2593END IF25942595IF(direction < 0) THEN2596target_nodenum = UOGlobalNodeNums(1 + nodenum_disps(prev+1) + soff)2597ELSE2598IF(prev + 1 == ParEnv % PEs) THEN2599k = SIZE(UOGlobalNodeNums)2600ELSE2601k = 1 + nodenum_disps(prev+1+1) - 12602END IF2603!one node before (-1) the next partition's (+1) nodes2604target_nodenum = UOGlobalNodeNums(k + foff)2605END IF26062607!wipe them out so we don't accidentally come back this way2608PartNeighbourList(j) % Neighbours(index:index+direction:direction) = -226092610IF(FullBoundary) THEN2611IF(Debug) THEN2612PRINT *, 'new index: ', index2613PRINT *, 'new segnum: ', segnum2614PRINT *, 'new direction: ',direction2615PRINT *, 'new next: ', next2616PRINT *, 'new prev: ', prev2617PRINT *, 'new neighbours: ', PartNeighbourList(next+1) % Neighbours2618END IF26192620IF(ALL(PartNeighbourList(next+1) % Neighbours == -2)) THEN2621IF(Debug) PRINT *,'Finished cycling neighbours in FullBoundary'2622EXIT2623END IF2624ELSE IF(next == -1) THEN2625EXIT2626END IF26272628next = next + 12629END DO26302631IF(Debug) PRINT *, 'Debug GetDomainEdge, part order:', PartOrder26322633END IF26342635!-----------------------------------------------------------2636! Put nodes collected from partitions into order2637!-----------------------------------------------------------26382639IF(Boss) THEN2640put_start = 126412642DO i=1,SIZE(PartOrder,1)2643j = PartOrder(i,1) + 12644segnum = PartOrder(i,2)26452646IF(j==0) CALL Abort()26472648foff = 02649soff = 02650IF(PartSegments(j) > 1) THEN2651IF(Debug) THEN2652PRINT *, 'Debug GetDomainEdge, extracting nodes from segmented partition'2653PRINT *, 'Debug GetDomainEdge, segnum: ', segnum2654PRINT *, 'Debug GetDomainEdge, partnodes: ', PartNodesOnEdge(j)2655PRINT *, 'Debug GetDomainEdge, PartSegStarts(j) % Indices: ',&2656PartSegStarts(j) % Indices2657PRINT *, 'Debug GetDomainEdge, nodenum_disps(j): ',nodenum_disps(j)2658END IF26592660IF(ABS(segnum) == 1) THEN26612662soff = 02663ELSE2664soff = PartSegStarts(j) % Indices(ABS(segnum) - 1) - 12665END IF2666IF(ABS(segnum) == PartSegments(j)) THEN2667foff = 02668ELSE2669foff = -1 * (PartNodesOnEdge(j) - PartSegStarts(j) % Indices(ABS(segnum)) + 1)2670END IF2671END IF26722673part_start = 1 + nodenum_disps(j) !where are this partitions nodes?2674IF(segnum > 0) THEN2675find_start = part_start + soff2676find_fin = part_start + PartNodesOnEdge(j) - 1 + foff2677find_stride = 12678ELSE2679find_fin = part_start + soff2680find_start = part_start + PartNodesOnEdge(j) - 1 + foff2681find_stride = -12682END IF26832684put_fin = put_start + ABS(find_start - find_fin)2685IF(Debug) THEN2686PRINT *, 'Debug, find start, end: ',find_start, find_fin, find_stride2687PRINT *, 'Debug, put start, end: ',put_start, put_fin2688PRINT *, 'Total slots: ',SIZE(OrderedNodes % x)2689END IF26902691OrderedNodes % x(put_start:put_fin) = &2692UnorderedNodes % x(find_start:find_fin:find_stride)2693OrderedNodes % y(put_start:put_fin) = &2694UnorderedNodes % y(find_start:find_fin:find_stride)2695OrderedNodes % z(put_start:put_fin) = &2696UnorderedNodes % z(find_start:find_fin:find_stride)2697OrderedGlobalNodeNums(put_start:put_fin) = &2698UOGlobalNodeNums(find_start:find_fin:find_stride)26992700put_start = put_fin !1 node overlap2701END DO27022703IF(FullBoundary) THEN2704!In the full boundary case, we've inadvertently saved the first node twice2705! (once at the end too) - this sorts that out2706n = OrderedNodes % NumberOfNodes - 12707OrderedNodes % NumberOfNodes = n27082709ALLOCATE(WorkReal(n,3))2710WorkReal(:,1) = OrderedNodes % x(1:n)2711WorkReal(:,2) = OrderedNodes % y(1:n)2712WorkReal(:,3) = OrderedNodes % z(1:n)2713DEALLOCATE(OrderedNodes % x, OrderedNodes % y, OrderedNodes % z)2714ALLOCATE(OrderedNodes % x(n), OrderedNodes % y(n), OrderedNodes % z(n))2715OrderedNodes % x(1:n) = WorkReal(:,1)2716OrderedNodes % y(1:n) = WorkReal(:,2)2717OrderedNodes % z(1:n) = WorkReal(:,3)2718DEALLOCATE(WorkReal)2719END IF27202721DEALLOCATE(OrderedNodeNums)2722ALLOCATE(OrderedNodeNums(OrderedNodes % NumberOfNodes))2723OrderedNodeNums = OrderedGlobalNodeNums(1:OrderedNodes % NumberOfNodes)27242725IF(Debug) THEN2726PRINT *, 'Debug GetDomainEdge, globalno, orderednodes % x: '2727DO i=1,SIZE(OrderedNodes % x)2728PRINT *, OrderedNodeNums(i), OrderedNodes % x(i)2729END DO2730END IF2731END IF27322733ELSE !serial2734OrderedNodes % NumberOfNodes = NoNodesOnEdge2735ALLOCATE(OrderedNodes % x(OrderedNodes % NumberOfNodes),&2736OrderedNodes % y(OrderedNodes % NumberOfNodes),&2737OrderedNodes % z(OrderedNodes % NumberOfNodes))27382739OrderedNodes % x = Mesh % Nodes % x(OrderedNodeNums)2740OrderedNodes % y = Mesh % Nodes % y(OrderedNodeNums)2741OrderedNodes % z = Mesh % Nodes % z(OrderedNodeNums)27422743!No action required on OrderedNodeNums...2744END IF27452746!-------------------------------------------------------------2747! Simplify geometry by removing interior nodes on any straight2748! lines if requested2749!-------------------------------------------------------------2750IF(Simpl .AND. Boss) THEN2751ALLOCATE(RemoveNode(OrderedNodes % NumberOfNodes))2752RemoveNode = .FALSE.27532754DO i=2,OrderedNodes % NumberOfNodes-1 !Test all interior nodes27552756CCW_value = ((OrderedNodes % y(i) - OrderedNodes % y(i+1)) * &2757(OrderedNodes % x(i-1) - OrderedNodes % x(i+1))) - &2758((OrderedNodes % x(i) - OrderedNodes % x(i+1)) * &2759(OrderedNodes % y(i-1) - OrderedNodes % y(i+1)))27602761IF(Debug) PRINT *,'Debug simplify node: ',&2762OrderedNodes % x(i), OrderedNodes % y(i),' ccw: ',ccw_value27632764!Need to determine numerical precision of input datapoints2765!i.e. after how many decimal places are values constant2766!e.g. 0.23000000... or 99999...2767prec = MAX(RealAeps(OrderedNodes % x(i)),RealAeps(OrderedNodes % y(i)))27682769IF(ABS(CCW_value) < 10*AEPS) THEN2770RemoveNode(i) = .TRUE.2771END IF2772END DO27732774IF(COUNT(RemoveNode) > 0) THEN27752776CALL RemoveNodes(OrderedNodes, RemoveNode, OrderedNodeNums)27772778IF(Debug) THEN2779PRINT *, 'Debug GetDomainEdge, Simplify removing: ', COUNT(RemoveNode), ' nodes'2780DO i=1,OrderedNodes % NumberOfNodes2781PRINT *, 'Debug GetDomainEdge, node: ',i2782PRINT *, 'x: ',OrderedNodes % x(i),'y: ',OrderedNodes % y(i)2783END DO2784END IF !debug27852786END IF !removing any nodes2787DEALLOCATE(RemoveNode)2788END IF !simplify27892790!-------------------------------------------------------------2791! Remove any nodes which are closer together than MinDist, if2792! this is specified.2793!-------------------------------------------------------------2794IF(PRESENT(MinDist) .AND. Boss) THEN2795!Cycle all nodes, remove any too close together2796!This won't guarantee that the new domain edge is *within* the old one2797!but could be adapted to do so2798ALLOCATE(RemoveNode(OrderedNodes % NumberOfNodes))2799RemoveNode = .FALSE.2800DO i=2,OrderedNodes % NumberOfNodes-1 !Test all interior nodes2801j = i - 12802DO WHILE(RemoveNode(j))2803j = j-12804END DO28052806IF(NodeDist2D(OrderedNodes, i, j) < MinDist) THEN2807RemoveNode(i) = .TRUE.2808IF(Debug) THEN2809PRINT *, 'Debug GetDomainEdge, MinDist, removing node ',i,' too close to: ', j2810PRINT *, 'Debug GetDomainEdge, MinDist, dist: ',NodeDist2D(OrderedNodes, i, j)2811END IF2812END IF2813END DO28142815IF(COUNT(RemoveNode) > 0) THEN28162817CALL RemoveNodes(OrderedNodes, RemoveNode, OrderedNodeNums)28182819IF(Debug) THEN2820PRINT *, 'Debug GetDomainEdge, MinDist removing: ', COUNT(RemoveNode), ' nodes'2821DO i=1,OrderedNodes % NumberOfNodes2822PRINT *, 'Debug GetDomainEdge, node: ',i2823PRINT *, 'x: ',OrderedNodes % x(i),'y: ',OrderedNodes % y(i)2824END DO2825END IF !debug28262827END IF !removing any nodes2828DEALLOCATE(RemoveNode)2829END IF !MinDist28302831!------------ DEALLOCATIONS ------------------28322833DEALLOCATE(OnEdge, UnorderedNodeNums, GlobalCorners, CornerParts, PCornerCounts, OrderedNodeNums)28342835IF(Boss .AND. Parallel) THEN !Deallocations2836DEALLOCATE(UnorderedNodes % x, &2837UnorderedNodes % y, &2838UnorderedNodes % z, &2839PartNodesOnEdge, &2840disps, nodenum_disps, &2841PartOrder, &2842UOGlobalNodeNums, &2843OrderedGlobalNodeNums)2844END IF28452846IF(.NOT. Boss) DEALLOCATE(UnorderedNodes % x, UnorderedNodes % y, UnorderedNodes % z)28472848END SUBROUTINE GetDomainEdge28492850! Copies over time variables and creates coordinate vars. Basically pinched2851! from AddMeshCoordinatesAndTime() and Multigrid2852SUBROUTINE CopyIntrinsicVars(OldMesh, NewMesh)2853IMPLICIT NONE28542855TYPE(Mesh_t), POINTER :: OldMesh, NewMesh2856TYPE(Solver_t), POINTER :: Solver2857TYPE(Variable_t), POINTER :: WorkVar2858!----------------------------------------------------------2859NULLIFY( Solver )28602861CALL VariableAdd( NewMesh % Variables, NewMesh,Solver, &2862'Coordinate 1',1,NewMesh % Nodes % x )28632864CALL VariableAdd(NewMesh % Variables,NewMesh,Solver, &2865'Coordinate 2',1,NewMesh % Nodes % y )28662867CALL VariableAdd(NewMesh % Variables,NewMesh,Solver, &2868'Coordinate 3',1,NewMesh % Nodes % z )28692870WorkVar => VariableGet( OldMesh % Variables, 'Time', ThisOnly=.TRUE.)2871IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Time', 1, WorkVar % Values )28722873WorkVar => VariableGet( OldMesh % Variables, 'Periodic Time', ThisOnly=.TRUE.)2874IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Periodic Time', 1, WorkVar % Values )28752876WorkVar => VariableGet( OldMesh % Variables, 'Timestep', ThisOnly=.TRUE.)2877IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Timestep', 1, WorkVar % Values )28782879WorkVar => VariableGet( OldMesh % Variables, 'Timestep size', ThisOnly=.TRUE.)2880IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Timestep size', 1, WorkVar % Values )28812882WorkVar => VariableGet( OldMesh % Variables, 'Timestep interval', ThisOnly=.TRUE.)2883IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Timestep interval', 1, WorkVar % Values )28842885WorkVar => VariableGet( OldMesh % Variables, 'Coupled iter', ThisOnly=.TRUE.)2886IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Coupled iter', 1, WorkVar % Values )28872888WorkVar => VariableGet( OldMesh % Variables, 'Nonlin iter', ThisOnly=.TRUE.)2889IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Nonlin iter', 1, WorkVar % Values )28902891END SUBROUTINE CopyIntrinsicVars28922893!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!2894!Function to rotate a mesh by rotationmatrix2895!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!2896SUBROUTINE RotateMesh(Mesh, RotationMatrix)28972898IMPLICIT NONE28992900TYPE(Mesh_t) :: Mesh2901REAL(KIND=dp) :: RotationMatrix(3,3), NodeHolder(3)2902INTEGER :: i29032904DO i=1,Mesh % NumberOfNodes2905NodeHolder(1) = Mesh % Nodes % x(i)2906NodeHolder(2) = Mesh % Nodes % y(i)2907NodeHolder(3) = Mesh % Nodes % z(i)29082909NodeHolder = MATMUL(RotationMatrix,NodeHolder)29102911Mesh % Nodes % x(i) = NodeHolder(1)2912Mesh % Nodes % y(i) = NodeHolder(2)2913Mesh % Nodes % z(i) = NodeHolder(3)2914END DO29152916END SUBROUTINE RotateMesh29172918SUBROUTINE DeallocateElement(Element)29192920IMPLICIT NONE2921TYPE(Element_t) :: Element29222923IF ( ASSOCIATED( Element % NodeIndexes ) ) &2924DEALLOCATE( Element % NodeIndexes )2925Element % NodeIndexes => NULL()29262927IF ( ASSOCIATED( Element % EdgeIndexes ) ) &2928DEALLOCATE( Element % EdgeIndexes )2929Element % EdgeIndexes => NULL()29302931IF ( ASSOCIATED( Element % FaceIndexes ) ) &2932DEALLOCATE( Element % FaceIndexes )2933Element % FaceIndexes => NULL()29342935IF ( ASSOCIATED( Element % DGIndexes ) ) &2936DEALLOCATE( Element % DGIndexes )2937Element % DGIndexes => NULL()29382939IF ( ASSOCIATED( Element % BubbleIndexes ) ) &2940DEALLOCATE( Element % BubbleIndexes )2941Element % BubbleIndexes => NULL()29422943IF ( ASSOCIATED( Element % PDefs ) ) &2944DEALLOCATE( Element % PDefs )2945Element % PDefs => NULL()29462947END SUBROUTINE DeallocateElement29482949!Identify front elements connected to the bed, which are sufficiently horizontal2950!to warrant reclassification as basal elements.2951!Note, only does elements currently connected to the bed. i.e. one row per dt2952!Returns:2953! NewBasalNode(:), LOGICAL true where frontal node becomes basal2954! ExFrontalNode(:), LOGICAL true where a frontal node no longer2955! belongs to its front column (though it may still be on the front...)2956!2957! NOTE, if an error in this subroutine, could be element2958! which sits between 2 NewBasalElems29592960SUBROUTINE ConvertFrontalToBasal(Model, Mesh, FrontMaskName, BotMaskName, &2961ZThresh, NewBasalNode, FoundSome)29622963TYPE(Model_t) :: Model2964TYPE(Mesh_t), POINTER :: Mesh2965REAL(KIND=dp) :: ZThresh2966LOGICAL :: FoundSome2967LOGICAL, POINTER :: NewBasalNode(:), ExFrontalNode(:), NewBasalElem(:)2968CHARACTER(MAX_NAME_LEN) :: FrontMaskName, BotMaskName2969!-------------------------------------------------------2970TYPE(Nodes_t) :: Nodes2971TYPE(Solver_t), POINTER :: NullSolver => NULL()2972TYPE(Element_t), POINTER :: Element, New303Elements(:,:), WorkElements(:)2973INTEGER :: i,j,k,n,dummyint, ierr, FrontBCtag, BasalBCtag, count303, &2974CountSharedExFrontal, CountSharedNewBasal, SharedExGlobal(2), &2975SharedNewGlobal(2), OldElemCount, NewElemCount2976INTEGER, POINTER :: NodeIndexes(:), AllSharedExGlobal(:)=>NULL(), &2977AllSharedNewGlobal(:)=>NULL(), FrontPerm(:), BotPerm(:)2978REAL(KIND=dp) :: Normal(3)2979LOGICAL :: ThisBC, Found, Debug2980CHARACTER(MAX_NAME_LEN) :: FuncName29812982FoundSome = .FALSE.2983FuncName = "ConvertFrontalToBasal"2984Debug = .FALSE.29852986n = Mesh % NumberOfNodes2987ALLOCATE(NewBasalNode(n),&2988ExFrontalNode(n),&2989FrontPerm(n),&2990BotPerm(n),&2991NewBasalElem(Mesh % NumberOfBulkElements+1: &2992Mesh % NumberOfBulkElements+Mesh % NumberOfBoundaryElements))29932994NewBasalNode = .FALSE.2995ExFrontalNode = .FALSE.2996NewBasalElem = .FALSE.29972998CALL MakePermUsingMask( Model, NullSolver, Mesh, BotMaskName, &2999.FALSE., BotPerm, dummyint)3000CALL MakePermUsingMask( Model, NullSolver, Mesh, FrontMaskName, &3001.FALSE., FrontPerm, dummyint)30023003!Find frontal BC from logical3004DO i=1,Model % NumberOfBCs3005ThisBC = ListGetLogical(Model % BCs(i) % Values,FrontMaskName,Found)3006IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE3007FrontBCtag = Model % BCs(i) % Tag3008EXIT3009END DO30103011!Find basal BC from logical3012DO i=1,Model % NumberOfBCs3013ThisBC = ListGetLogical(Model % BCs(i) % Values,BotMaskName,Found)3014IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE3015BasalBCtag = Model % BCs(i) % Tag3016EXIT3017END DO30183019CountSharedExFrontal = 03020CountSharedNewBasal = 03021SharedExGlobal = 03022SharedNewGlobal = 030233024!---------------------------------------------------3025! Find elements for conversion, and set node switches3026!---------------------------------------------------3027DO i=Mesh % NumberOfBulkElements + 1, &3028Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements30293030Element => Mesh % Elements(i)3031IF(Element % BoundaryInfo % Constraint /= FrontBCtag) CYCLE !not on front3032IF(Element % TYPE % ElementCode == 101) CYCLE30333034NodeIndexes => Element % NodeIndexes30353036IF(.NOT. (ANY(BotPerm(NodeIndexes) > 0) )) CYCLE !not connected to bed30373038n = Element % TYPE % NumberOfNodes30393040ALLOCATE(Nodes % x(n), Nodes % y(n), Nodes % z(n))30413042Nodes % x = Mesh % Nodes % x(NodeIndexes)3043Nodes % y = Mesh % Nodes % y(NodeIndexes)3044Nodes % z = Mesh % Nodes % z(NodeIndexes)30453046Normal = NormalVector(Element, Nodes)30473048!compare element normal to threshold3049IF(Normal(3) < ZThresh) THEN3050FoundSome = .TRUE.30513052!Nodes currently on bed become 'ex frontal nodes'3053!Nodes not currently on bed become 'new basal nodes'3054DO j=1,SIZE(NodeIndexes)30553056IF(BotPerm(NodeIndexes(j)) > 0) THEN3057IF(.NOT. ExFrontalNode(NodeIndexes(j))) THEN !maybe already got in another elem30583059ExFrontalNode(NodeIndexes(j)) = .TRUE.30603061!If node is in another partition, need to pass this info3062IF(SIZE(Mesh % ParallelInfo % NeighbourList(NodeIndexes(j)) % Neighbours)>1) THEN3063CountSharedExFrontal = CountSharedExFrontal + 13064IF(CountSharedExFrontal > 2) CALL Fatal(FuncName, &3065"Found more than 2 ExFrontalNodes on partition boundary...")30663067SharedExGlobal(CountSharedExFrontal) = Mesh % ParallelInfo % GlobalDofs(NodeIndexes(j))3068END IF3069END IF3070ELSE3071IF(.NOT. NewBasalNode(NodeIndexes(j))) THEN !maybe already got in another elem30723073NewBasalNode(NodeIndexes(j)) = .TRUE.30743075!If node is in another partition, need to pass this info3076IF(SIZE(Mesh % ParallelInfo % NeighbourList(NodeIndexes(j)) % Neighbours)>1) THEN3077CountSharedNewBasal = CountSharedNewBasal + 13078IF(CountSharedNewBasal > 2) CALL Fatal(FuncName, &3079"Found more than 2 NewBasalNodes on partition boundary...")30803081SharedNewGlobal(CountSharedNewBasal) = &3082Mesh % ParallelInfo % GlobalDofs(NodeIndexes(j))3083END IF3084END IF308530863087END IF30883089END DO30903091NewBasalElem(i) = .TRUE.3092IF(Debug) PRINT *, ParEnv % MyPE, 'Debug, converting element: ',i,&3093' with nodes: ', NodeIndexes3094END IF30953096DEALLOCATE(Nodes % x, Nodes % y, Nodes % z)3097END DO30983099!Distribute information about shared frontal nodes3100!which are no longer on the front.3101!NOTE: we may also need to pass NewBasalNodes...3102IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, shared ex frontal nodes: ',SharedExGlobal3103IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, shared new basal nodes: ',SharedNewGlobal31043105ALLOCATE(AllSharedExGlobal(2*ParEnv % PEs),&3106AllSharedNewGlobal(2*ParEnv % PEs))31073108CALL MPI_ALLGATHER(SharedExGlobal,2,MPI_INTEGER,&3109AllSharedExGlobal,2,MPI_INTEGER, ELMER_COMM_WORLD, ierr)3110CALL MPI_ALLGATHER(SharedNewGlobal,2,MPI_INTEGER,&3111AllSharedNewGlobal,2,MPI_INTEGER, ELMER_COMM_WORLD, ierr)31123113DO i=1,Mesh % NumberOfNodes3114IF(FrontPerm(i) <= 0) CYCLE3115IF(ANY(AllSharedExGlobal == Mesh % ParallelInfo % GlobalDOFs(i))) THEN3116ExFrontalNode(i) = .TRUE.3117FoundSome = .TRUE.3118IF(Debug) PRINT *, ParEnv % MyPE, ' Debug, received shared exfrontalnode: ',i3119END IF3120IF(ANY(AllSharedNewGlobal == Mesh % ParallelInfo % GlobalDOFs(i))) THEN3121NewBasalNode(i) = .TRUE.3122FoundSome = .TRUE.3123IF(Debug) PRINT *, ParEnv % MyPE, ' Debug, received shared newbasalnode: ',i3124END IF3125END DO31263127!------------------------------------------------------------------------------3128! Cycle front elements, looking for those to convert 404 -> 303 for front interp3129! And, also, a rare case where one element is sandwiched between shared ExFrontalNodes3130! In this case, cycle3131!------------------------------------------------------------------------------3132DO j=1,231333134count303 = 03135DO i=Mesh % NumberOfBulkElements + 1, &3136Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements31373138Element => Mesh % Elements(i)3139IF(Element % BoundaryInfo % Constraint /= FrontBCtag) CYCLE !not on front3140IF(Element % TYPE % ElementCode == 101) CYCLE3141IF(NewBasalElem(i)) CYCLE !element disappears from front entirely31423143NodeIndexes => Element % NodeIndexes31443145IF(.NOT. (ANY(BotPerm(NodeIndexes) > 0) )) CYCLE3146IF(.NOT. ANY(ExFrontalNode(NodeIndexes))) CYCLE !Not affected31473148IF(j==2 .AND. Debug) PRINT *, ParEnv % MyPE, ' Debug, switching element: ',&3149i,' with nodeindexes ', NodeIndexes31503151IF(COUNT(ExFrontalNode(NodeIndexes)) /= 1) CYCLE31523153!iff only change one row of elements at at time, we only get here3154!through elements to the side which become 3033155count303 = count303 + 131563157!First time we just count and allocate...3158IF(j==2) THEN3159DO k=1,23160New303Elements(count303,k) % TYPE => GetElementType( 303, .FALSE. )3161New303Elements(count303,k) % NDOFs = 33162New303Elements(count303,k) % ElementIndex = i3163New303Elements(count303,k) % BodyID = Element % BodyID31643165ALLOCATE(New303Elements(count303,k) % NodeIndexes(3))3166END DO31673168!The temporary frontal element3169New303Elements(count303,1) % NodeIndexes = &3170PACK(NodeIndexes, (.NOT. ExFrontalNode(NodeIndexes)))31713172!The temporary basal element3173New303Elements(count303,2) % NodeIndexes = &3174PACK(NodeIndexes, ( (BotPerm(NodeIndexes)>0) .OR. NewBasalNode(NodeIndexes) ) )31753176DO k=1,23177ALLOCATE(New303Elements(count303,k) % BoundaryInfo)3178New303Elements(count303,k) % BoundaryInfo % Left => Element % BoundaryInfo % Left3179New303Elements(count303,k) % BoundaryInfo % Right => Element % BoundaryInfo % Right31803181IF(k==1) THEN3182n = FrontBCtag3183ELSE3184n = BasalBCtag3185END IF31863187New303Elements(count303,k) % BoundaryInfo % Constraint = n3188END DO31893190IF(Debug) PRINT *, ParEnv % MyPE, ' debug, new frontal element ',i,' has nodes: ', &3191New303Elements(count303,1) % NodeIndexes31923193IF(Debug) PRINT *, ParEnv % MyPE, ' debug, new basal element ',i,' has nodes: ', &3194New303Elements(count303,2) % NodeIndexes3195END IF3196END DO31973198IF(j==1) THEN3199ALLOCATE(New303Elements(count303,2))3200END IF32013202END DO32033204!-------------------------------------------------------3205! Now modify mesh % elements accordingly3206!-------------------------------------------------------3207IF(FoundSome) THEN32083209OldElemCount = Mesh % NumberOfBulkElements + &3210Mesh % NumberOfBoundaryElements3211NewElemCount = OldElemCount + count30332123213ALLOCATE(WorkElements(NewElemCount))3214WorkElements(1:OldElemCount) = Mesh % Elements(1:OldElemCount)32153216DO i=1,count3033217n = New303Elements(i,1) % ElementIndex32183219Element => WorkElements(n)32203221CALL FreeElementStuff(Element)32223223Element = New303Elements(i,1)3224Element => WorkElements(OldElemCount + i)32253226Element = New303Elements(i,2)3227Element % ElementIndex = OldElemCount + i3228END DO32293230! Change constraint on NewBasalElem3231DO i=LBOUND(NewBasalElem,1),UBOUND(NewBasalElem,1)3232IF(.NOT. NewBasalElem(i)) CYCLE3233WorkElements(i) % BoundaryInfo % Constraint = BasalBCtag3234END DO32353236DEALLOCATE(Mesh % Elements)3237Mesh % NumberOfBoundaryElements = Mesh % NumberOfBoundaryElements + count3033238Mesh % Elements => WorkElements3239END IF32403241CALL SParIterAllReduceOR(FoundSome)32423243NULLIFY(WorkElements)32443245!TODO: Free New303Elements3246DEALLOCATE(AllSharedExGlobal, AllSharedNewGlobal, &3247NewBasalElem, FrontPerm, BotPerm, ExFrontalNode)32483249END SUBROUTINE ConvertFrontalToBasal32503251SUBROUTINE FreeElementStuff(Element)3252TYPE(Element_t), POINTER :: Element3253IF(ASSOCIATED(Element % NodeIndexes)) DEALLOCATE(Element % NodeIndexes)3254IF(ASSOCIATED(Element % EdgeIndexes)) DEALLOCATE(Element % EdgeIndexes)3255IF(ASSOCIATED(Element % FaceIndexes)) DEALLOCATE(Element % FaceIndexes)3256IF(ASSOCIATED(Element % BubbleIndexes)) DEALLOCATE(Element % BubbleIndexes)3257IF(ASSOCIATED(Element % DGIndexes)) DEALLOCATE(Element % DGIndexes)3258IF(ASSOCIATED(Element % PDefs)) DEALLOCATE(Element % PDefs)3259END SUBROUTINE FreeElementStuff326032613262!Turns off (or back on) a specified solver, and adds a string "Save Exec When"3263! to solver % values to allow it to be switched back on to the correct setting.3264SUBROUTINE SwitchSolverExec(Solver, Off)32653266IMPLICIT NONE32673268TYPE(Solver_t) :: Solver3269LOGICAL :: Off3270!-----------------------------------------3271CHARACTER(MAX_NAME_LEN) :: SaveExecWhen3272LOGICAL :: Found32733274SaveExecWhen = ListGetString(Solver % Values, "Save Exec When", Found)3275IF(.NOT. Found) THEN3276SaveExecWhen = ListGetString(Solver % Values, 'Exec Solver', Found)3277IF(.NOT. Found) SaveExecWhen = 'always'3278CALL ListAddString(Solver % Values, 'Save Exec When', SaveExecWhen)3279END IF32803281IF(Off) THEN32823283!Turning the solver off3284Solver % SolverExecWhen = SOLVER_EXEC_NEVER3285CALL ListAddString(Solver % Values, 'Exec Solver', 'Never')32863287ELSE32883289CALL ListAddString(Solver % Values, 'Exec Solver', SaveExecWhen)32903291SELECT CASE( SaveExecWhen )3292CASE( 'never' )3293Solver % SolverExecWhen = SOLVER_EXEC_NEVER3294CASE( 'always' )3295Solver % SolverExecWhen = SOLVER_EXEC_ALWAYS3296CASE( 'after simulation', 'after all' )3297Solver % SolverExecWhen = SOLVER_EXEC_AFTER_ALL3298CASE( 'before simulation', 'before all' )3299Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_ALL3300CASE( 'before timestep' )3301Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_TIME3302CASE( 'after timestep' )3303Solver % SolverExecWhen = SOLVER_EXEC_AFTER_TIME3304CASE( 'before saving' )3305Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_SAVE3306CASE( 'after saving' )3307Solver % SolverExecWhen = SOLVER_EXEC_AFTER_SAVE3308CASE DEFAULT3309CALL Fatal("SwitchSolverExec","Programming error here...")3310END SELECT33113312END IF33133314END SUBROUTINE SwitchSolverExec33153316SUBROUTINE PlanePointIntersection ( pp, pnorm, p1, p2, p_intersect, found_intersection )3317!Get the intersection point between a line and plane in 3D3318! Plane defined by point "pp" and norm "pnorm", line defined by points "p1" and "p2"3319! Intersection returned in p_intersect3320!found_intersection = .FALSE. if they happen to be parallel33213322REAL(KIND=dp) :: pp(3), pnorm(3), p1(3), p2(3), p_intersect(3)3323LOGICAL :: found_intersection3324!----------------------------3325REAL(KIND=dp) :: pl(3), dist33263327pl = p2 - p133283329IF(ABS(DOT_PRODUCT(pl,pnorm)) < EPSILON(1.0_dp)) THEN3330!Line and plane are parallel...3331found_intersection = .FALSE.3332RETURN3333END IF33343335dist = DOT_PRODUCT((pp - p1), pnorm) / DOT_PRODUCT(pl,pnorm)33363337p_intersect = p1 + dist*pl3338found_intersection = .TRUE.33393340END SUBROUTINE PlanePointIntersection33413342SUBROUTINE LineSegmentsIntersect ( a1, a2, b1, b2, intersect_point, does_intersect )3343! Find if two 2D line segments intersect3344! Line segment 'a' runs from point a1 => a2, same for b33453346IMPLICIT NONE33473348REAL(KIND=dp) :: a1(2), a2(2), b1(2), b2(2), intersect_point(2)3349LOGICAL :: does_intersect3350!-----------------------3351REAL(KIND=dp) :: r(2), s(2), rxs, bma(2), t, u335233533354does_intersect = .FALSE.3355intersect_point = 0.0_dp33563357r = a2 - a13358s = b2 - b133593360rxs = VecCross2D(r,s)33613362IF(rxs == 0.0_dp) RETURN33633364bma = b1 - a133653366t = VecCross2D(bma,s) / rxs3367u = VecCross2D(bma,r) / rxs33683369IF(t < 0.0_dp .OR. t > 1.0_dp .OR. u < 0.0_dp .OR. u > 1.0_dp) RETURN33703371intersect_point = a1 + (t * r)3372does_intersect = .TRUE.33733374END SUBROUTINE LineSegmentsIntersect33753376SUBROUTINE LinesIntersect ( a1, a2, b1, b2, intersect_point, does_intersect )3377! Find where two 2D lines intersect3378! Line 'a' explicitly defined by points a1, a2 which lie on line, same for b3379! based on LineSegmentsIntersect above33803381IMPLICIT NONE33823383REAL(KIND=dp) :: a1(2), a2(2), b1(2), b2(2), intersect_point(2)3384LOGICAL :: does_intersect3385!-----------------------3386REAL(KIND=dp) :: r(2), s(2), rxs, bma(2), t, u338733883389does_intersect = .TRUE.33903391intersect_point = 0.0_dp33923393r = a2 - a13394s = b2 - b133953396rxs = VecCross2D(r,s)33973398IF(rxs == 0.0_dp) THEN3399does_intersect = .FALSE.3400RETURN3401ENDIF34023403bma = b1 - a134043405t = VecCross2D(bma,s) / rxs3406u = VecCross2D(bma,r) / rxs34073408intersect_point = a1 + (t * r)34093410END SUBROUTINE LinesIntersect34113412SUBROUTINE LineSegmLineIntersect ( a1, a2, b1, b2, intersect_point, does_intersect )3413! Find if two 2D line segments intersect3414! Line segment 'a' runs from point a1 => a23415! Line b is defined by vector b1 -> b234163417IMPLICIT NONE34183419REAL(KIND=dp) :: a1(2), a2(2), b1(2), b2(2), intersect_point(2)3420LOGICAL :: does_intersect3421!-----------------------3422REAL(KIND=dp) :: r(2), s(2), rxs, bma(2), t, u342334243425does_intersect = .FALSE.3426intersect_point = 0.0_dp34273428r = a2 - a13429s = b2 - b134303431rxs = VecCross2D(r,s)34323433IF(rxs == 0.0_dp) RETURN34343435bma = b1 - a134363437t = VecCross2D(bma,s) / rxs34383439IF(t < 0.0_dp .OR. t > 1.0_dp) RETURN34403441intersect_point = a1 + (t * r)3442does_intersect = .TRUE.34433444END SUBROUTINE LineSegmLineIntersect34453446FUNCTION VecCross2D(a, b) RESULT (c)3447REAL(KIND=dp) :: a(2), b(2), c34483449c = a(1)*b(2) - a(2)*b(1)34503451END FUNCTION VecCross2D34523453!This subroutine should identify discrete calving events for the3454!purposes of local remeshing. For now it returns 13455SUBROUTINE CountCalvingEvents(Model, Mesh,CCount)3456TYPE(Model_t) :: Model3457TYPE(Mesh_t),POINTER :: Mesh3458INTEGER :: CCount34593460Ccount = 13461END SUBROUTINE CountCalvingEvents34623463! shortest distance of c to segment ab, a b and c are in 2D3464FUNCTION PointLineSegmDist2D(a, b, c) RESULT (pdis)3465REAL(KIND=dp) :: a(2), b(2), c(2), n(2), v(2), dd, t, pdis3466n=b-a ! Vector ab3467dd = (n(1)**2.+n(2)**2.) ! Length of ab squared3468dd = DOT_PRODUCT(n,n) ! alternative writing3469t = DOT_PRODUCT(c-a,b-a)/dd3470dd = MAXVAL( (/0.0_dp, MINVAL( (/1.0_dp,t/) ) /) )3471v = c - a - dd * n3472pdis=sqrt(v(1)**2.+v(2)**2.)3473END FUNCTION PointLineSegmDist2D34743475! Takes two meshes which are assumed to represent the same domain3476! and interpolates variables between them. Uses full dimension3477! interpolation (InterpolateMeshToMesh) for all nodes, then picks3478! up missing boundary nodes using reduced dim3479! (InterpolateVarToVarReduced)3480SUBROUTINE SwitchMesh(Model, Solver, OldMesh, NewMesh)34813482IMPLICIT NONE34833484TYPE(Model_t) :: Model3485TYPE(Solver_t) :: Solver3486TYPE(Mesh_t), POINTER :: OldMesh, NewMesh3487!-------------------------------------------------3488TYPE(Solver_t), POINTER :: WorkSolver3489TYPE(Variable_t), POINTER :: Var=>NULL(), NewVar=>NULL(), WorkVar=>NULL()3490TYPE(Valuelist_t), POINTER :: Params3491TYPE(Matrix_t), POINTER :: WorkMatrix=>NULL()3492LOGICAL :: Found, Global, GlobalBubbles, Debug, DoPrevValues, &3493NoMatrix, DoOptimizeBandwidth, PrimaryVar, HasValuesInPartition, &3494PrimarySolver,CreatedParMatrix3495LOGICAL, POINTER :: UnfoundNodes(:)=>NULL(), BulkUnfoundNodes(:)=>NULL()3496INTEGER :: i,j,k,DOFs, nrows,n, dummyint, ierr3497INTEGER, POINTER :: WorkPerm(:)=>NULL(), SolversToIgnore(:)=>NULL(), &3498SurfaceMaskPerm(:)=>NULL(), BottomMaskPerm(:)=>NULL()3499REAL(KIND=dp), POINTER :: WorkReal(:)=>NULL(), WorkReal2(:)=>NULL(), PArray(:,:) => NULL()3500REAL(KIND=dp) :: FrontOrientation(3), RotationMatrix(3,3), UnRotationMatrix(3,3), &3501globaleps, localeps3502LOGICAL, ALLOCATABLE :: PartActive(:)3503CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, WorkName35043505INTERFACE3506SUBROUTINE InterpolateMeshToMesh( OldMesh, NewMesh, OldVariables, &3507NewVariables, UseQuadrantTree, Projector, MaskName, UnfoundNodes )3508!------------------------------------------------------------------------------3509USE Lists3510USE SParIterComm3511USE Interpolation3512USE CoordinateSystems3513!-------------------------------------------------------------------------------3514TYPE(Mesh_t), TARGET :: OldMesh, NewMesh3515TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables, NewVariables3516LOGICAL, OPTIONAL :: UseQuadrantTree3517LOGICAL, POINTER, OPTIONAL :: UnfoundNodes(:)3518TYPE(Projector_t), POINTER, OPTIONAL :: Projector3519CHARACTER(LEN=*),OPTIONAL :: MaskName3520END SUBROUTINE InterpolateMeshToMesh3521END INTERFACE35223523SolverName = "SwitchMesh"3524Debug = .FALSE.3525Params => Solver % Values3526CALL Info( 'Remesher', ' ',Level=4 )3527CALL Info( 'Remesher', '-------------------------------------',Level=4 )3528CALL Info( 'Remesher', ' Switching from old to new mesh...',Level=4 )3529CALL Info( 'Remesher', '-------------------------------------',Level=4 )3530CALL Info( 'Remesher', ' ',Level=4 )35313532IF(ASSOCIATED(NewMesh % Variables)) CALL Fatal(SolverName,&3533"New mesh already has variables associated!")35343535! need to set Mesh % MaxNDOFs in NewMesh3536CALL SetMeshMaxDOFs(NewMesh)35373538!interpolation epsilons3539globaleps = 1.0E-2_dp3540localeps = 1.0E-2_dp35413542!----------------------------------------------3543! Get the orientation of the calving front3544! & compute rotation matrix3545!----------------------------------------------3546FrontOrientation = GetFrontOrientation(Model)3547RotationMatrix = ComputeRotationMatrix(FrontOrientation)3548UnRotationMatrix = TRANSPOSE(RotationMatrix)35493550!----------------------------------------------3551! Action3552!----------------------------------------------35533554CALL CopyIntrinsicVars(OldMesh, NewMesh)35553556!----------------------------------------------3557! Add Variables to NewMesh3558!----------------------------------------------35593560Var => OldMesh % Variables35613562ALLOCATE(PartActive(ParEnv % PEs))3563CreatedParMatrix = .FALSE.35643565DO WHILE( ASSOCIATED(Var) )35663567DoPrevValues = ASSOCIATED(Var % PrevValues)3568WorkSolver => Var % Solver3569HasValuesInPartition = .TRUE.35703571!Do nothing if it already exists3572!e.g. it's a DOF component added previously3573NewVar => VariableGet( NewMesh % Variables, Var % Name, ThisOnly = .TRUE.)3574IF(ASSOCIATED(NewVar)) THEN3575NULLIFY(NewVar)3576Var => Var % Next3577CYCLE3578END IF35793580DOFs = Var % DOFs3581Global = (SIZE(Var % Values) .EQ. DOFs)35823583!Allocate storage for values and perm3584IF(Global) THEN3585ALLOCATE(WorkReal(DOFs))3586WorkReal = Var % Values35873588CALL VariableAdd( NewMesh % Variables, NewMesh, &3589Var % Solver, TRIM(Var % Name), &3590Var % DOFs, WorkReal)35913592ELSE !Regular field variable3593ALLOCATE(WorkPerm(NewMesh % NumberOfNodes))35943595IF(.NOT. ASSOCIATED(WorkSolver)) THEN3596WRITE(Message, '(a,a,a)') "Variable ",Var % Name," has no solver, unexpected."3597CALL Fatal(SolverName, Message)3598END IF35993600PrimaryVar = ASSOCIATED(WorkSolver % Variable, Var)36013602IF(PrimaryVar) THEN !Take care of the matrix3603NoMatrix = ListGetLogical( WorkSolver % Values, 'No matrix',Found)3604!Issue here, this will recreate matrix for every variable associated w/ solver.36053606IF(.NOT. NoMatrix) THEN3607IF(ParEnv % MyPE == 0) PRINT *, 'Computing matrix for variable: ',TRIM(Var % Name)36083609DoOptimizeBandwidth = ListGetLogical( WorkSolver % Values, &3610'Optimize Bandwidth', Found )3611IF ( .NOT. Found ) DoOptimizeBandwidth = .TRUE.36123613GlobalBubbles = ListGetLogical( WorkSolver % Values, &3614'Bubbles in Global System', Found )3615IF ( .NOT. Found ) GlobalBubbles = .TRUE.36163617WorkMatrix => CreateMatrix(Model, WorkSolver, &3618NewMesh, WorkPerm, DOFs, MATRIX_CRS, DoOptimizeBandwidth, &3619ListGetString( WorkSolver % Values, 'Equation' ), &3620GlobalBubbles = GlobalBubbles )36213622IF(ASSOCIATED(WorkMatrix)) THEN3623WorkMatrix % Comm = ELMER_COMM_WORLD36243625WorkMatrix % Symmetric = ListGetLogical( WorkSolver % Values, &3626'Linear System Symmetric', Found )36273628WorkMatrix % Lumped = ListGetLogical( WorkSolver % Values, &3629'Lumped Mass Matrix', Found )36303631CALL AllocateVector( WorkMatrix % RHS, WorkMatrix % NumberOfRows )3632WorkMatrix % RHS = 0.0_dp3633WorkMatrix % RHS_im => NULL()36343635ALLOCATE(WorkMatrix % Force(WorkMatrix % NumberOfRows, WorkSolver % TimeOrder+1))3636WorkMatrix % Force = 0.0_dp3637ELSE3638!No nodes in this partition now3639NoMatrix = .TRUE.3640END IF3641END IF36423643IF(.NOT. CreatedParMatrix) &3644CALL MPI_AllGather(.NOT. NoMatrix, 1, MPI_LOGICAL, PartActive, 1, MPI_LOGICAL, ELMER_COMM_WORLD, ierr)36453646IF ( ASSOCIATED(Var % EigenValues) ) THEN3647n = SIZE(Var % EigenValues)36483649IF ( n > 0 ) THEN3650WorkSolver % NOFEigenValues = n3651CALL AllocateVector( NewVar % EigenValues,n )3652CALL AllocateArray( NewVar % EigenVectors, n, &3653SIZE(NewVar % Values) )36543655NewVar % EigenValues = 0.0d03656NewVar % EigenVectors = 0.0d03657IF(.NOT.NoMatrix) THEN3658CALL AllocateVector( WorkMatrix % MassValues, SIZE(WorkMatrix % Values) )3659WorkMatrix % MassValues = 0.0d03660END IF3661END IF3662END IF36633664!Check for duplicate solvers with same var3665!Nullify/deallocate and repoint the matrix3666!Note: previously this DO loop was after the FreeMatrix3667!and pointing below, but this caused double free errors3668DO j=1,Model % NumberOfSolvers3669IF(ASSOCIATED(WorkSolver, Model % Solvers(j))) CYCLE3670IF(.NOT. ASSOCIATED(Model % Solvers(j) % Variable)) CYCLE3671IF( TRIM(Model % Solvers(j) % Variable % Name) /= TRIM(Var % Name)) CYCLE36723673!If the other solver's matrix is the same as WorkSolver matrix, we just3674!nullify, otherwise we deallocate. After the first timestep, solvers3675!with the same variable will have the same matrix3676IF(ASSOCIATED(Model % Solvers(j) % Matrix, WorkSolver % Matrix)) THEN3677Model % Solvers(j) % Matrix => NULL()3678ELSE3679CALL FreeMatrix(Model % Solvers(j) % Matrix)3680END IF3681!Point this other solver % matrix to the matrix we just created3682Model % Solvers(j) % Matrix => WorkMatrix3683END DO36843685!Deallocate the old matrix & repoint3686IF(ASSOCIATED(WorkSolver % Matrix)) CALL FreeMatrix(WorkSolver % Matrix)3687WorkSolver % Matrix => WorkMatrix36883689! bit of a hack3690! since ParEnv become a pointer to ParMatrix we need to ensure one ParMatrix is formed3691! it needs to be from a solver present on all parts hence the all gather further up.3692! it seems we only need to this once per timestep/interpolation as ParEnv will have some thing3693! to point to. If we don't do this ParEnv % PEs, % MyPE etc. all become nans mucking eveything up!3694IF ( ASSOCIATED(WorkSolver % Matrix) .and. ALL(PartActive) .and. .NOT. CreatedParMatrix) THEN3695IF (.NOT. ASSOCIATED(WorkSolver % Matrix % ParMatrix) ) THEN3696WorkSolver % Mesh => NewMesh36973698CALL ParallelInitMatrix( WorkSolver, WorkSolver % Matrix, WorkPerm)3699CreatedParMatrix = .TRUE.3700END IF3701END IF37023703NULLIFY(WorkMatrix)37043705!NOTE: We don't switch Solver % Variable here, because3706!Var % Solver % Var doesn't necessarily point to self3707!if solver has more than one variable. We do this below.3708ELSE3709k = InitialPermutation(WorkPerm, Model, WorkSolver, &3710NewMesh, ListGetString(WorkSolver % Values,'Equation'))3711END IF !Primary var37123713HasValuesInPartition = COUNT(WorkPerm>0) > 03714IF(HasValuesInPartition) THEN3715ALLOCATE(WorkReal(COUNT(WorkPerm>0)*DOFs))3716ELSE3717!this is silly but it matches AddEquationBasics3718ALLOCATE(WorkReal(NewMesh % NumberOfNodes * DOFs))3719END IF37203721WorkReal = 0.0_dp3722CALL VariableAdd( NewMesh % Variables, NewMesh, &3723Var % Solver, TRIM(Var % Name), &3724Var % DOFs, WorkReal, WorkPerm, &3725Var % Output, Var % Secondary, Var % TYPE )37263727END IF !Not global37283729NewVar => VariableGet( NewMesh % Variables, Var % Name, ThisOnly = .TRUE. )3730IF(.NOT.ASSOCIATED(NewVar)) CALL Fatal(SolverName,&3731"Problem creating variable on new mesh.")37323733IF(DoPrevValues) THEN3734ALLOCATE(NewVar % PrevValues( SIZE(NewVar % Values), SIZE(Var % PrevValues,2) ))3735END IF37363737!Add the components of variables with more than one DOF3738!NOTE, this implementation assumes the vector variable3739!comes before the scalar components in the list.3740!e.g., we add Mesh Update and so here we add MU 1,2,33741!SO: next time round, new variable (MU 1) already exists3742!and so it's CYCLE'd3743IF((DOFs > 1) .AND. (.NOT.Global)) THEN3744nrows = SIZE(WorkReal)3745DO i=1,DOFs37463747WorkReal2 => WorkReal( i:nrows-DOFs+i:DOFs )3748WorkName = ComponentName(TRIM(Var % Name),i)3749CALL VariableAdd( NewMesh % Variables, NewMesh, &3750Var % Solver, WorkName, &37511, WorkReal2, WorkPerm, &3752Var % Output, Var % Secondary, Var % TYPE )37533754IF(DoPrevValues) THEN3755WorkVar => VariableGet( NewMesh % Variables, WorkName, .TRUE. )3756IF(.NOT. ASSOCIATED(WorkVar)) CALL Fatal(SolverName, &3757"Error allocating Remesh Update PrevValues.")37583759NULLIFY(WorkVar % PrevValues)3760WorkVar % PrevValues => NewVar % PrevValues(i:nrows-DOFs+i:DOFs,:)3761END IF37623763NULLIFY(WorkReal2)3764END DO3765END IF37663767NULLIFY(WorkReal, WorkPerm)3768Var => Var % Next3769END DO37703771!Go back through and set non-primary variables to have same % perm as the primary var.3772!Bit of a hack - would be nice to somehow do this in one loop...3773!Set perms equal if: variable has solver, solver has variable, both variables have perm3774Var => NewMesh % Variables3775DO WHILE (ASSOCIATED(Var))37763777WorkSolver => Var % Solver3778IF(ASSOCIATED(WorkSolver)) THEN3779IF(ASSOCIATED(WorkSolver % Variable % Perm)) THEN3780WorkVar => VariableGet(NewMesh % Variables, &3781WorkSolver % Variable % Name, .TRUE., UnfoundFatal=.TRUE.)3782PrimaryVar = ASSOCIATED(WorkSolver % Variable, Var)3783IF(ASSOCIATED(WorkVar) .AND. .NOT. PrimaryVar) THEN3784IF(ASSOCIATED(WorkVar % Perm) .AND. ASSOCIATED(Var % Perm)) THEN3785Var % Perm = WorkVar % Perm3786END IF3787END IF3788END IF3789END IF37903791Var => Var % Next3792END DO37933794!set partitions to active, so variable can be -global -nooutput3795CALL ParallelActive(.TRUE.)3796!MPI_BSend buffer issue in this call to InterpolateMeshToMesh3797!Free quadrant tree to ensure its rebuilt in InterpolateMeshToMesh (bug fix)3798CALL FreeQuadrantTree(OldMesh % RootQuadrant)3799CALL InterpolateMeshToMesh( OldMesh, NewMesh, OldMesh % Variables, UnfoundNodes=UnfoundNodes)3800! unfound nodes are on or near calving front when terminus advances3801! 3D interp missing nodes doesn't require projectablility or to interp calving front seperately3802! since there are no important variables only present on calving front3803! we only need bulk variables.3804! these nodes tend to group together eg when a section of the terminus advances3805! need to make sure that we don't interp from any other unfound nodes.3806IF(ANY(UnfoundNodes)) THEN3807PRINT *, ParEnv % MyPE, ' missing ', COUNT(UnfoundNodes),' out of ',SIZE(UnfoundNodes),&3808' nodes in SwitchMesh.'3809END IF38103811! only search for 3D advance extrapolation of bulk and non-projected boundaries3812CALL MakePermUsingMask( Model, Solver, NewMesh, "Top Surface Mask", &3813.FALSE., SurfaceMaskPerm, dummyint)3814CALL MakePermUsingMask( Model, Solver, NewMesh, "Bottom Surface Mask", &3815.FALSE., BottomMaskPerm, dummyint)38163817ALLOCATE(BulkUnfoundNodes(NewMesh % NumberOfNodes))3818BulkUnfoundNodes = (SurfaceMaskPerm <= 0) .AND. &3819(BottomMaskPerm <= 0) .AND. &3820UnfoundNodes38213822!---------------------------------------------------------3823! For top, bottom and calving front BC, do reduced dim3824! interpolation to avoid epsilon problems3825!---------------------------------------------------------38263827CALL InterpMaskedBCReduced(Model, Solver, OldMesh, NewMesh, OldMesh % Variables, &3828"Top Surface Mask",globaleps=globaleps,localeps=localeps)3829CALL InterpMaskedBCReduced(Model, Solver, OldMesh, NewMesh, OldMesh % Variables, &3830"Bottom Surface Mask",globaleps=globaleps,localeps=localeps)38313832! could improve by only required procs entering this3833! need this after surface interps otherwise surface nodes with inserts nans into the system3834CALL InterpAdvanceUnfoundNodes(OldMesh, NewMesh, BulkUnfoundNodes)38353836! removed as 2d interp on calving front no longer valid since calving front is3837! not projectable38383839!CALL RotateMesh(OldMesh, RotationMatrix)3840!CALL RotateMesh(NewMesh, RotationMatrix)38413842!CHANGE - need to delete UnfoundNOtes from this statement, or front3843!variables not copied across. If you get some odd interpolation artefact,3844!suspect this3845!CALL InterpMaskedBCReduced(Model, Solver, OldMesh, NewMesh, OldMesh % Variables, &3846! "Calving Front Mask", globaleps=globaleps,localeps=localeps)38473848!NOTE: InterpMaskedBCReduced on the calving front will most likely fail to3849! find a few points, due to vertical adjustment to account for GroundedSolver.3850! Briefly, the 'DoGL' sections of CalvingRemesh adjust the Z coordinate of3851! basal nodes which are grounded, to ensure they match the bed dataset.3852! Thus, it's not impossible for points on the new mesh to sit slightly outside3853! the old.3854! However, these points should sit behind or on the old calving front, so3855! InterpMaskedBC... on the bed should get them. Thus the only thing that may3856! be missed would be variables defined solely on the front. Currently, none3857! of these are important for the next timestep, so this should be fine.38583859!CALL RotateMesh(NewMesh, UnrotationMatrix)3860!CALL RotateMesh(OldMesh, UnrotationMatrix)38613862!-----------------------------------------------3863! Point solvers at the correct mesh and variable3864!-----------------------------------------------38653866!CHANGE3867!Needs to be told to ignore certain solvers if using multiple meshes3868SolversToIgnore => ListGetIntegerArray(Params, 'Solvers To Ignore')38693870DO i=1,Model % NumberOfSolvers3871WorkSolver => Model % Solvers(i)38723873!CHANGE - see above3874IF (ASSOCIATED(SolversToIgnore)) THEN3875IF(ANY(SolversToIgnore(1:SIZE(SolversToIgnore))==i)) CYCLE3876END IF38773878WorkSolver % Mesh => NewMesh !note, assumption here that there's only one active mesh38793880!hack to get SingleSolver to recompute3881!should be taken care of by Mesh % Changed, but3882!this is reset by CoupledSolver for some reason3883WorkSolver % NumberOfActiveElements = -138843885IF(.NOT. ASSOCIATED(WorkSolver % Variable)) CYCLE3886IF(WorkSolver % Variable % NameLen == 0) CYCLE !dummy !invalid read38873888!Check for multiple solvers with same var:3889!If one of the duplicate solvers is only executed before the simulation (or never),3890!then we don't point the variable at this solver. (e.g. initial groundedmask).3891!If both solvers are executed during each timestep, we have a problem.3892!If neither are, it doesn't matter, and so the the later occurring solver will have3893!the variable pointed at it (arbitrary).3894PrimarySolver = .TRUE.3895DO j=1,Model % NumberOfSolvers3896IF(j==i) CYCLE3897IF(.NOT. ASSOCIATED(Model % Solvers(j) % Variable)) CYCLE3898IF(TRIM(Model % Solvers(j) % Variable % Name) == WorkSolver % Variable % Name) THEN38993900IF( (WorkSolver % SolverExecWhen == SOLVER_EXEC_NEVER) .OR. &3901(WorkSolver % SolverExecWhen == SOLVER_EXEC_AHEAD_ALL) ) THEN3902IF((Model % Solvers(j) % SolverExecWhen == SOLVER_EXEC_NEVER) .OR. &3903(Model % Solvers(j) % SolverExecWhen == SOLVER_EXEC_AHEAD_ALL) ) THEN3904PrimarySolver = .TRUE.3905ELSE3906PrimarySolver = .FALSE.3907WorkSolver % Matrix => NULL()3908EXIT3909END IF3910ELSE3911IF( (Model % Solvers(j) % SolverExecWhen == SOLVER_EXEC_NEVER) .OR. &3912(Model % Solvers(j) % SolverExecWhen == SOLVER_EXEC_AHEAD_ALL) ) THEN3913PrimarySolver = .TRUE.3914EXIT3915ELSE3916WRITE(Message, '(A,A)') "Unable to determine main solver for variable: ", &3917TRIM(WorkSolver % Variable % Name)3918CALL Fatal(SolverName, Message)3919END IF3920END IF39213922END IF3923END DO39243925WorkVar => VariableGet(NewMesh % Variables, &3926WorkSolver % Variable % Name, .TRUE.) !invalid read39273928IF(ASSOCIATED(WorkVar)) THEN3929WorkSolver % Variable => WorkVar3930IF(PrimarySolver) WorkVar % Solver => WorkSolver3931ELSE3932WRITE(Message, '(a,a,a)') "Variable ",WorkSolver % Variable % Name," wasn't &3933&correctly switched to the new mesh." !invalid read3934PRINT *, i,' debug, solver equation: ', ListGetString(WorkSolver % Values, "Equation")3935CALL Fatal(SolverName, Message)3936END IF39373938END DO393939403941NewMesh % Next => OldMesh % Next3942Model % Meshes => NewMesh3943Model % Mesh => NewMesh3944Model % Variables => NewMesh % Variables39453946!Free old mesh and associated variables3947CALL ReleaseMesh(OldMesh)3948DEALLOCATE(OldMesh)3949DEALLOCATE(UnfoundNodes, BulkUnfoundNodes, SurfaceMaskPerm, BottomMaskPerm)39503951OldMesh => Model % Meshes39523953END SUBROUTINE SwitchMesh39543955SUBROUTINE InterpMaskedBCReduced(Model, Solver, OldMesh, NewMesh, Variables, MaskName, &3956SeekNodes, globaleps, localeps)39573958USE InterpVarToVar39593960IMPLICIT NONE39613962TYPE(Model_t) :: Model3963TYPE(Solver_t) :: Solver3964TYPE(Mesh_t), POINTER :: OldMesh, NewMesh3965TYPE(Variable_t), POINTER :: Variables3966REAL(KIND=dp), OPTIONAL :: globaleps,localeps3967LOGICAL, POINTER, OPTIONAL :: SeekNodes(:)3968CHARACTER(LEN=*) :: MaskName3969!----------------------------3970TYPE(Variable_t), POINTER :: Var3971INTEGER, POINTER :: OldMaskPerm(:)=>NULL(), NewMaskPerm(:)=>NULL()3972INTEGER, POINTER :: InterpDim(:)3973INTEGER :: i,j,dummyint,BCTag3974REAL(KIND=dp) :: geps,leps3975LOGICAL :: Debug, skip, PartMask, Complete, ThisBC, Found3976#ifdef ELMER_BROKEN_MPI_IN_PLACE3977LOGICAL :: buffer3978#endif3979LOGICAL, POINTER :: OldMaskLogical(:), NewMaskLogical(:), UnfoundNodes(:)=>NULL(), OldElemMask(:)3980LOGICAL, ALLOCATABLE :: PartsMask(:), FoundNode(:)3981CHARACTER(LEN=MAX_NAME_LEN) :: HeightName, Solvername3982INTEGER, ALLOCATABLE :: PartUnfoundCount(:), AllUnfoundDOFS(:), UnfoundDOFS(:), disps(:), Unique(:), &3983FinalDOFs(:), UnfoundIndex(:), UnfoundShared(:), Repeats(:), RemainingDOFs(:)3984LOGICAL, ALLOCATABLE :: PartHasUnfoundNodes(:)3985INTEGER :: ClusterSize, ierr, UnfoundCount, min_val, max_val, CountDOFs, CountRepeats, Previous, NodeCount3986SolverName = 'InterpMaskedBCReduced'39873988CALL MakePermUsingMask( Model, Solver, NewMesh, MaskName, &3989.FALSE., NewMaskPerm, dummyint)39903991CALL MakePermUsingMask( Model, Solver, OldMesh, MaskName, &3992.FALSE., OldMaskPerm, dummyint)39933994ALLOCATE(OldMaskLogical(SIZE(OldMaskPerm)),&3995NewMaskLogical(SIZE(NewMaskPerm)))39963997OldMaskLogical = (OldMaskPerm <= 0)3998NewMaskLogical = (NewMaskPerm <= 0)3999IF(PRESENT(SeekNodes)) NewMaskLogical = &4000NewMaskLogical .OR. .NOT. SeekNodes40014002!create mask of elems as with an unstructred mesh all nodes can be in mask but not elem4003DO i=1,Model % NumberOfBCs4004ThisBC = ListGetLogical(Model % BCs(i) % Values, MaskName, Found)4005IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE4006BCtag = Model % BCs(i) % Tag4007EXIT4008END DO40094010ALLOCATE(OldElemMask(OldMesh % NumberOfBulkElements &4011+ OldMesh % NumberOfBoundaryElements))4012OldElemMask = .TRUE.4013DO i=OldMesh % NumberOfBulkElements+1, &4014OldMesh % NumberOfBulkElements+OldMesh % NumberOfBoundaryElements4015IF(OldMesh % Elements(i) % BoundaryInfo % constraint == BCTag) &4016OldElemMask(i) = .FALSE.4017END DO401840194020IF(PRESENT(globaleps)) THEN4021geps = globaleps4022ELSE4023geps = 1.0E-44024END IF40254026IF(PRESENT(localeps)) THEN4027leps = localeps4028ELSE4029leps = 1.0E-44030END IF40314032!Silly hack - InterpolateVarToVarReduced requires a designated 'height' variable4033!name which it considers the primary target. A quick fix here is to just find a4034!candidate variable and pass its name.4035Var => Variables4036DO WHILE(ASSOCIATED(Var))40374038IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global4039(Var % DOFs > 1) .OR. & !-multi-dof4040Var % Secondary) THEN !-secondary4041Var => Var % Next4042CYCLE4043ELSE IF(LEN(Var % Name) >= 10) THEN4044IF(Var % Name(1:10)=='coordinate') THEN !-coord var4045Var => Var % Next4046CYCLE4047END IF4048ELSE4049HeightName = TRIM(Var % Name)4050EXIT4051END IF4052END DO40534054IF(Debug) PRINT *, ParEnv % MyPE,'Debug, on boundary: ',TRIM(MaskName),' seeking ',&4055COUNT(.NOT. NewMaskLogical),' of ',SIZE(NewMaskLogical),' nodes.'40564057ALLOCATE(InterpDim(1))4058InterpDim(1) = 340594060CALL ParallelActive(.TRUE.)4061CALL InterpolateVarToVarReduced(OldMesh, NewMesh, HeightName, InterpDim, &4062UnfoundNodes, OldMaskLogical, NewMaskLogical, OldElemMask, OldMesh % Variables, &4063geps, leps)406440654066UnfoundCount = COUNT(UnfoundNodes)40674068ClusterSize = ParEnv % PEs40694070! Gather missing counts at this stage4071ALLOCATE(PartUnfoundCount(ClusterSize), &4072PartHasUnfoundNodes(ClusterSize))4073CALL MPI_AllGather(UnfoundCount, 1, MPI_INTEGER, &4074PartUnfoundCount, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)40754076! Process node numbers and global node number important for translation later on4077! gather all DOFs from all processes4078UnfoundDOFS = PACK(NewMesh % ParallelInfo % GlobalDOFs, UnfoundNodes)4079UnfoundIndex = PACK((/ (i,i=1,SIZE(UnfoundNodes)) /),UnfoundNodes .eqv. .TRUE.)40804081ALLOCATE(disps(ClusterSize))4082disps(1) = 04083DO i=2,ClusterSize4084disps(i) = disps(i-1) + PartUnfoundCount(i-1)4085END DO4086ALLOCATE(AllUnfoundDOFS(SUM(PartUnfoundCount)))4087CALL MPI_allGatherV(UnfoundDOFS, UnfoundCount, MPI_INTEGER, &4088AllUnfoundDOFS, PartUnfoundCount, disps, MPI_INTEGER, ELMER_COMM_WORLD, ierr)4089IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")40904091! Loop to remove duplicates and order allDOFs in ascending order4092CountDOFs=04093CountRepeats=04094IF(SUM(PartUnfoundCount) > 0) THEN4095ALLOCATE(unique(SIZE(AllUnfoundDOFS)), repeats(SIZE(AllUnfoundDOFS)))4096min_val = minval(AllUnfoundDOFS)-14097max_val = maxval(AllUnfoundDOFS)40984099DO WHILE (min_val<max_val)4100Previous = COUNT(AllUNfoundDOFS>min_val)4101CountDOFs = CountDOFs+14102min_val = MINVAL(AllUnfoundDOFS, mask=AllUnfoundDOFS>min_val)4103Unique(countDOFs) = min_val4104IF(COUNT(AllUnfoundDOFS>min_val) /= Previous-1) THEN4105CountRepeats = CountRepeats + 14106Repeats(CountRepeats) = min_val4107END IF4108END DO4109END IF4110ALLOCATE(FinalDOFs(CountDOFs), source=Unique(1:countDOFs))4111ALLOCATE(UnfoundShared(CountRepeats), source=Repeats(1:CountRepeats))4112ALLOCATE(FoundNode(UnfoundCount))41134114!What you should do here is, rather than looping over the size of UnfoundNodes is4115! 1. Construct an ordered list of every GlobalDOF which needs to be found (on ANY partition) (AllMissingGlobal)4116! 2. Construct a logical array of the same size which is TRUE where the current partition needs the node (MissingThisGlobal)4117! 3. Loop over AllMissingGlobal (possibly with an MPI_Barrier on each loop).4118! NOTE - this means you will need to make *every* partition enter this loop (as opposed to just the ones which are missing nodes)4119! but this is OK because there's no real performance hit - those partitions would just be waiting anyway41204121!NewMaskLogical changes purpose, now it masks supporting nodes4122NewMaskLogical = (NewMaskPerm <= 0)41234124PRINT*, ParEnv % MyPE, MaskName, ' NumberofUnfoundpoints', Size(FinalDOFs), UnfoundCount4125!Loop through all DOFS with barrier before shared nodes4126NodeCount = 04127FoundNode = .FALSE.41284129Complete = .FALSE.4130DO WHILE(.NOT. Complete)4131DO i=1, SIZE(FinalDOFs)4132IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN4133DO j=1, UnfoundCount4134IF(UnfoundDOFS(j) == FinalDOFs(i)) THEN4135nodecount = j4136EXIT4137END IF4138END DO4139ELSE4140nodecount = 04141END IF4142! no need for a mask since nodes in both arrays in ascending order4143IF(ANY(UnfoundShared == FinalDOFs(i))) THEN4144! ok to barrier since all parts enter loop and4145! have same AllUnfoundDOFs/UnfoundShared4146! barrier for shared nodes to endsure these are found at same time4147CALL MPI_Barrier(ELMER_COMM_WORLD, ierr)4148!nodenumber = UnfoundIndex(nodecount) since different on each process4149!always finds correct translation from DOFs to process nodenumber since4150!all arrays in ascending order4151IF(nodecount == 0) CYCLE4152IF(FoundNode(nodecount)) CYCLE4153IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN4154PRINT *,ParEnv % MyPE,'Didnt find shared point: ', UnfoundIndex(nodecount), &4155' x:', NewMesh % Nodes % x(Unfoundindex(nodecount)),&4156' y:', NewMesh % Nodes % y(Unfoundindex(nodecount)),&4157' z:', NewMesh % Nodes % z(Unfoundindex(nodecount)), &4158'GDOF', FinalDOFs(i), &4159NewMesh % ParallelInfo % GlobalDOFs(UnfoundIndex(nodecount))4160RemainingDOFs = PACK(UnfoundDOFs,.NOT. FoundNode)4161CALL InterpolateUnfoundSharedPoint( UnfoundIndex(nodecount), NewMesh, HeightName, InterpDim, &4162NodeMask=NewMaskLogical, Variables=NewMesh % Variables, UnfoundDOFS=RemainingDOFs, &4163Found=FoundNode(nodecount))4164END IF4165! no need for a mask since nodes in both arrays in ascending order4166ELSE IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN4167IF(FoundNode(nodecount)) CYCLE4168!nodenumber = UnfoundIndex(nodecount) since different on each process4169!always finds correct translation from DOFs to process nodenumber since4170!all arrays in ascending order4171PRINT *,ParEnv % MyPE,'Didnt find point: ', UnfoundIndex(nodecount), &4172' x:', NewMesh % Nodes % x(Unfoundindex(nodecount)),&4173' y:', NewMesh % Nodes % y(Unfoundindex(nodecount)),&4174' z:', NewMesh % Nodes % z(Unfoundindex(nodecount)), &4175'GDOF', FinalDOFs(i), &4176NewMesh % ParallelInfo % GlobalDOFs(UnfoundIndex(nodecount))4177RemainingDOFs = PACK(UnfoundDOFs,.NOT. FoundNode)4178CALL InterpolateUnfoundPoint( UnfoundIndex(nodecount), NewMesh, HeightName, InterpDim, &4179NodeMask=NewMaskLogical, Variables=NewMesh % Variables, UnfoundDOFs=RemainingDOFs, &4180Found=FoundNode(nodecount))4181END IF4182END DO4183IF(COUNT(FoundNode) == UnfoundCount) Complete = .TRUE.4184#ifdef ELMER_BROKEN_MPI_IN_PLACE4185buffer = Complete4186CALL MPI_AllReduce(buffer, &4187#else4188CALL MPI_AllReduce(MPI_IN_PLACE, &4189#endif4190Complete, 1, MPI_LOGICAL, MPI_LAND, ELMER_COMM_WORLD, ierr)4191END DO41924193DEALLOCATE(OldMaskLogical, &4194NewMaskLogical, NewMaskPerm, &4195OldMaskPerm, UnfoundNodes, &4196InterpDim, OldElemMask)41974198END SUBROUTINE InterpMaskedBCReduced41994200!Function to return the orientation of a calving front4201!If specified in SIF, returns this, otherwise computes it4202FUNCTION GetFrontOrientation(Model) RESULT (Orientation)4203TYPE(Model_t) :: Model4204TYPE(Mesh_t),POINTER :: Mesh4205!--------------------------4206TYPE(Solver_t), POINTER :: NullSolver => NULL()4207TYPE(Variable_t), POINTER :: TimeVar4208INTEGER :: i,dummyint,FaceNodeCount, ierr, proc4209REAL(KIND=dp) :: Orientation(3),OrientSaved(3),xLeft,yLeft,xRight,yRight4210REAL(KIND=dp) :: RecvXL,RecvYL,RecvXR,RecvYR,Temp,PrevTime4211REAL(KIND=dp), POINTER :: PArray(:,:) => NULL()4212INTEGER, POINTER :: Perm(:), FrontPerm(:)=>NULL(), TopPerm(:)=>NULL(), &4213FrontNodeNums(:)=>NULL(),LeftPerm(:)=>NULL(), RightPerm(:)=>NULL()4214LOGICAL :: FirstTime=.TRUE.,Constant,Debug=.TRUE.,Parallel,&4215HaveRight,HaveLeft, Boss, FirstThisTime4216CHARACTER(LEN=MAX_NAME_LEN) :: FrontMaskName, TopMaskName, &4217LeftMaskName, RightMaskName4218INTEGER :: status(MPI_STATUS_SIZE), iLeft, iRight4219SAVE :: FirstTime,Constant,PArray,OrientSaved, Parallel, Boss, FirstThisTime4220SAVE :: PrevTime4221IF(FirstTime) THEN4222FirstTime = .FALSE.4223!TODO - this will need to be defined on individual boundary conditions4224!if we want to handle multiple calving fronts in same simulation.4225PArray => ListGetConstRealArray( Model % Constants,'Front Orientation', &4226Constant)4227Parallel = (ParEnv % PEs > 1)4228Boss = (ParEnv % MyPE == 0) .OR. (.NOT. Parallel)4229PrevTime = 0.0_dp4230FirstThisTime = .TRUE.4231IF(Constant) THEN4232CALL Info("GetFrontOrientation","Using predefined Front Orientation from SIF.", Level=6)4233DO i=1,34234OrientSaved(i) = PArray(i,1)4235END DO4236ELSE ! constant not found above4237CALL Info("GetFrontOrientation","No predefined Front Orientation, computing instead.", Level=6)4238END IF ! constant4239END IF ! first time42404241! check whether already did a front orientation computation this timestep4242! Changed Model % Mesh % Variables to avoid segfault as when calling vtusolver after mmg step as4243! Model % Variables lost after vtuoutput4244TimeVar => VariableGet( Model % Mesh % Variables, 'Timestep' )4245IF (Debug) PRINT *, 'Time', TimeVar % Values4246IF (Debug) PRINT *, 'PrevTime', PrevTime4247IF (Debug) PRINT *, 'FirstThisTime', FirstThisTime4248IF (TimeVar % Values(1) > PrevTime ) THEN4249FirstThisTime=.TRUE.4250END IF4251PrevTime = TimeVar % Values(1)4252IF (.NOT. FirstThisTime) PRINT *, 'use orientation calculated earlier in this timestep'4253IF(Constant .OR. (.NOT. FirstThisTime) ) THEN4254Orientation = OrientSaved4255RETURN4256ELSE4257PRINT *, 'computing orientation'4258Orientation(3) = 0.0_dp ! always set z-component to 04259Mesh => Model % Mesh4260!Get the front line4261FrontMaskName = "Calving Front Mask"4262TopMaskName = "Top Surface Mask"4263CALL MakePermUsingMask( Model, NullSolver, Mesh, TopMaskName, &4264.FALSE., TopPerm, dummyint)4265CALL MakePermUsingMask( Model, NullSolver, Mesh, FrontMaskName, &4266.FALSE., FrontPerm, FaceNodeCount)4267LeftMaskName = "Left Sidewall Mask"4268RightMaskName = "Right Sidewall Mask"4269!Generate perms to quickly get nodes on each boundary4270CALL MakePermUsingMask( Model, NullSolver, Mesh, LeftMaskName, &4271.FALSE., LeftPerm, dummyint)4272CALL MakePermUsingMask( Model, NullSolver, Mesh, RightMaskName, &4273.FALSE., RightPerm, dummyint)4274iLeft=04275iRight=04276HaveLeft=.FALSE.4277HaveRight=.FALSE.4278DO i=1,Mesh % NumberOfNodes4279IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN4280IF( LeftPerm(i) >0 ) THEN4281xLeft = Mesh % Nodes % x(i)4282yLeft = Mesh % Nodes % y(i)4283HaveLeft =.TRUE.4284ELSE IF ( RightPerm(i) >0 ) THEN4285xRight = Mesh % Nodes % x(i)4286yRight = Mesh % Nodes % y(i)4287HaveRight =.TRUE.4288END IF4289END IF4290END DO4291IF (Debug) PRINT *, 'GetFrontOrientation: HaveLeft, HaveRight', HaveLeft, HaveRight4292IF (Parallel) THEN4293IF (HaveLeft) PRINT *, 'GetFrontOrientation: xL, yL', xLeft, yLeft4294IF (HaveRight) PRINT *, 'GetFrontOrientation: xR, yR', xRight, yRight4295IF (Debug) PRINT *, 'communicate the corners'4296IF (HaveLeft .AND. (ParEnv % MyPE>0)) THEN ! left not in root4297iLeft=ParEnv % MyPE4298CALL MPI_BSEND(xLeft, 1, MPI_DOUBLE_PRECISION, &42990 ,7001, ELMER_COMM_WORLD, ierr )4300CALL MPI_BSEND(yLeft, 1, MPI_DOUBLE_PRECISION, &43010 ,7002, ELMER_COMM_WORLD, ierr )4302IF (Debug) PRINT *, 'sending left'4303END IF4304IF (HaveRight .AND. (ParEnv % MyPE>0) ) THEN ! right not in root4305iRight=ParEnv % MyPE4306CALL MPI_BSEND(xRight, 1, MPI_DOUBLE_PRECISION, &43070 , 7003, ELMER_COMM_WORLD, ierr )4308CALL MPI_BSEND(yRight, 1, MPI_DOUBLE_PRECISION, &43090 , 7004, ELMER_COMM_WORLD, ierr )4310IF (Debug) PRINT *, 'sending right'4311END IF4312IF (Debug) PRINT *, 'sent the corners'4313IF (Boss) THEN4314IF (Debug) PRINT *, ParEnv % PEs4315IF (.NOT.HaveLeft) THEN4316CALL MPI_RECV(RecvXL,1,MPI_DOUBLE_PRECISION,MPI_ANY_SOURCE,&43177001,ELMER_COMM_WORLD, status, ierr )4318CALL MPI_RECV(RecvYL,1,MPI_DOUBLE_PRECISION,MPI_ANY_SOURCE,&43197002,ELMER_COMM_WORLD, status, ierr )4320xLeft=RecvXL4321yLeft=RecvYL4322END IF4323IF (.NOT. HaveRight) THEN4324CALL MPI_RECV(RecvXR,1,MPI_DOUBLE_PRECISION,MPI_ANY_SOURCE,&43257003,ELMER_COMM_WORLD, status, ierr )4326CALL MPI_RECV(RecvYR,1,MPI_DOUBLE_PRECISION,MPI_ANY_SOURCE,&43277004,ELMER_COMM_WORLD, status, ierr )4328xRight=RecvXR4329yRight=RecvYR4330END IF4331IF (Debug) PRINT *, 'received corners'4332IF (Debug) PRINT *, 'GetFrontOrientation: Boss xL, yL, xR, yR', xLeft, yLeft, xRight, yRight4333END IF4334END IF ! end if parallel4335IF (Boss) THEN ! root or not parallel4336IF( ABS(xLeft-xRight) < AEPS) THEN4337! front orientation is aligned with y-axis4338Orientation(2) = 0.0_dp4339IF(yRight > yLeft) THEN4340Orientation(1)=1.0_dp4341ELSE4342Orientation(1)=-1.0_dp4343END IF4344ELSE IF (ABS(yLeft-yRight)<AEPS) THEN4345! front orientation is aligned with x-axis4346Orientation(1) = 0.0_dp4347IF(xRight > xLeft) THEN4348Orientation(2)=1.0_dp4349ELSE4350Orientation(2)=-1.0_dp4351END IF4352ELSE4353! set dot product equal to 04354! no need to ensure it is unit normal, done in ComputeRotation4355IF(xRight > xLeft) THEN4356Orientation(2)=1.0_dp4357ELSE4358Orientation(2)=-1.0_dp4359END IF4360Orientation(1)=Orientation(2)*(yRight-yLeft)/(xLeft-xRight)4361END IF4362END IF !boss4363IF (Parallel) CALL MPI_BCAST(Orientation,3,MPI_DOUBLE_PRECISION, 0, ELMER_COMM_WORLD, ierr)4364! deallocations4365DEALLOCATE(FrontPerm, TopPerm, LeftPerm, RightPerm)4366END IF4367Temp=(Orientation(1)**2+Orientation(2)**2+Orientation(3)**2)**0.54368Orientation=Orientation/Temp ! normalized the orientation4369IF((.NOT. Constant).AND.Debug) PRINT *, "GetFrontOrientation: ", Orientation,'part',ParEnv % MyPE4370FirstThisTime=.FALSE.4371OrientSaved=Orientation4372END FUNCTION GetFrontOrientation43734374SUBROUTINE Double2DLogSizeA(Vec, fill)4375!only doubles size in one dimension4376LOGICAL, ALLOCATABLE :: Vec(:,:)4377LOGICAL, OPTIONAL :: fill4378!----------------------------------------4379LOGICAL, ALLOCATABLE :: WorkVec(:,:)4380INTEGER, ALLOCATABLE :: D(:)43814382ALLOCATE(D(2))4383d = SHAPE(Vec)43844385ALLOCATE(WorkVec(d(1), d(2)))43864387WorkVec = Vec43884389DEALLOCATE(Vec)4390ALLOCATE(Vec(d(1)*2,d(2)))43914392IF(PRESENT(fill)) THEN4393Vec = fill4394ELSE4395Vec = .FALSE.4396END IF43974398Vec(1:d(1),:) = WorkVec43994400END SUBROUTINE Double2DLogSizeA44014402SUBROUTINE Double2DLogSizeP(Vec, fill)4403!only doubles size in one dimension4404LOGICAL, POINTER :: Vec(:,:)4405LOGICAL, OPTIONAL :: fill4406!----------------------------------------4407LOGICAL, ALLOCATABLE :: WorkVec(:,:)4408INTEGER, ALLOCATABLE :: D(:)44094410ALLOCATE(D(2))4411d = SHAPE(Vec)44124413ALLOCATE(WorkVec(d(1), d(2)))44144415WorkVec = Vec44164417DEALLOCATE(Vec)4418ALLOCATE(Vec(d(1)*2,d(2)))44194420IF(PRESENT(fill)) THEN4421Vec = fill4422ELSE4423Vec = .FALSE.4424END IF44254426Vec(1:d(1),:) = WorkVec44274428END SUBROUTINE Double2DLogSizeP44294430SUBROUTINE Double3DArraySizeA(Vec, fill)4431!only doubles size in one dimension4432INTEGER, ALLOCATABLE :: Vec(:,:,:)4433INTEGER, OPTIONAL :: fill4434!----------------------------------------4435INTEGER, ALLOCATABLE :: WorkVec(:,:,:), D(:)44364437ALLOCATE(D(3))4438d = SHAPE(Vec)44394440ALLOCATE(WorkVec(d(1), d(2),d(3)))44414442WorkVec = Vec44434444DEALLOCATE(Vec)4445ALLOCATE(Vec(d(1),d(2),2*d(3)))44464447IF(PRESENT(fill)) THEN4448Vec = fill4449ELSE4450Vec = 04451END IF44524453Vec(:,:,1:d(3)) = WorkVec44544455END SUBROUTINE Double3DArraySizeA44564457SUBROUTINE Double3DArraySizeP(Vec, fill)4458!only doubles size in one dimension4459INTEGER, POINTER :: Vec(:,:,:)4460INTEGER, OPTIONAL :: fill4461!----------------------------------------4462INTEGER, ALLOCATABLE :: WorkVec(:,:,:), D(:)44634464ALLOCATE(D(3))4465d = SHAPE(Vec)44664467ALLOCATE(WorkVec(d(1), d(2),d(3)))44684469WorkVec = Vec44704471DEALLOCATE(Vec)4472ALLOCATE(Vec(d(1),d(2),2*d(3)))44734474IF(PRESENT(fill)) THEN4475Vec = fill4476ELSE4477Vec = 04478END IF44794480Vec(:,:,1:d(3)) = WorkVec44814482END SUBROUTINE Double3DArraySizeP44834484SUBROUTINE Double4DArraySizeA(Vec, fill)4485!only doubles size in one dimension4486INTEGER, ALLOCATABLE :: Vec(:,:,:,:)4487INTEGER, OPTIONAL :: fill4488!----------------------------------------4489INTEGER, ALLOCATABLE :: WorkVec(:,:,:,:), D(:)44904491ALLOCATE(D(3))4492d = SHAPE(Vec)44934494ALLOCATE(WorkVec(d(1),d(2),d(3),d(4)))44954496WorkVec = Vec44974498DEALLOCATE(Vec)4499ALLOCATE(Vec(d(1),d(2),d(3),2*d(4)))45004501IF(PRESENT(fill)) THEN4502Vec = fill4503ELSE4504Vec = 04505END IF45064507Vec(:,:,:,1:d(4)) = WorkVec45084509END SUBROUTINE Double4DArraySizeA45104511SUBROUTINE Double4DArraySizeP(Vec, fill)4512!only doubles size in one dimension4513INTEGER, POINTER :: Vec(:,:,:,:)4514INTEGER, OPTIONAL :: fill4515!----------------------------------------4516INTEGER, ALLOCATABLE :: WorkVec(:,:,:,:), D(:)45174518ALLOCATE(D(3))4519d = SHAPE(Vec)45204521ALLOCATE(WorkVec(d(1),d(2),d(3),d(4)))45224523WorkVec = Vec45244525DEALLOCATE(Vec)4526ALLOCATE(Vec(d(1),d(2),d(3),2*d(4)))45274528IF(PRESENT(fill)) THEN4529Vec = fill4530ELSE4531Vec = 04532END IF45334534Vec(:,:,:,1:d(4)) = WorkVec45354536END SUBROUTINE Double4DArraySizeP45374538SUBROUTINE GetCalvingEdgeNodes(Mesh, Parallel, Shared, TotalCount)4539! Cycle through all 303 elements of GatheredMesh, creating lists of those4540! on the top surface, bottom surface, calving front, possibly also lateral4541! margins4542! Cycle these lists, identifying elements on different boundaries, which4543! share nodes (therefore share a theoretical 202 element), construct4544! list of these 202 elements4545! Add option to Set_MMG3D_Mesh to feed in 202 elements, or find a way to add4546! elems after Set_MMG3D_mesh is finished doing its thing45474548TYPE(Mesh_t),POINTER :: Mesh4549TYPE(Element_t),POINTER :: Element4550LOGICAL :: Parallel4551!---------------4552INTEGER :: i,j,k, BoundaryNumber, NumNodes, Match, BoundaryID, TotalCount, &4553FirstBdryID, SecondBdryID, CountSoFar4554INTEGER, ALLOCATABLE :: ElementNodes(:), Counts(:), BdryNodes(:,:,:), &4555CountPairs(:,:),SharedPairs(:,:,:,:),Shared(:, :)4556LOGICAL :: Debug, Counted, FirstMatch, SecondMatch, ThirdMatch4557CHARACTER(LEN=MAX_NAME_LEN) :: SolverName4558SolverName = 'GetCalvingEdgeNodes'45594560IF (Parallel) CALL Fatal(SolverName, 'Written to run in serial with MMG')45614562ALLOCATE(Counts(6))4563DO i=1,64564Counts(i) = 04565END DO45664567ALLOCATE(BdryNodes(6,3,100))45684569DO i=Mesh % NumberOfBulkElements + 1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements4570Element => Mesh % Elements(i)4571ElementNodes = Element % NodeIndexes4572BoundaryNumber = Element % BoundaryInfo % constraint45734574NumNodes = Element % TYPE % NumberOfNodes4575IF (NumNodes /= 3) CALL FATAL(Solvername, "BoundaryElements must be 303s")45764577DO BoundaryID=1,64578IF (BoundaryNumber == BoundaryID) THEN4579Counts(BoundaryID) = Counts(BoundaryID) + 14580IF (Counts(BoundaryID) > SIZE(BdryNodes(BoundaryID,1,:))) THEN4581IF(Debug) PRINT *, BoundaryID, 'BdryNodes, doubling array size'4582CALL Double3DArraySize(BdryNodes)4583END IF4584!ELSE4585! print *, ElementNodes(i), BoundaryNumber4586! CALL FATAL(Solvername, "BoundaryElement: has no boundary number")4587BdryNodes(BoundaryID,:,Counts(BoundaryID)) = ElementNodes4588END IF4589END DO4590END DO45914592!set counts for calving and other boundary shared nodes4593ALLOCATE(CountPairs(5,5))4594CountPairs(:,:) = 045954596!set allocatables4597ALLOCATE(SharedPairs(5,5,2,100))45984599! loop for 1-2, 1-3 ... 1-6, 2,3 ... 5,64600!!! assume one is calving front4601DO FirstBdryID=1,54602IF (Counts(FirstBdryID) /= 0) THEN4603DO i=1, Counts(FirstBdryID)4604DO SecondBdryID=FirstBdryID+1,64605IF (Counts(SecondBdryID) /= 0) THEN4606DO j=1, Counts(SecondBdryID)4607Match = 04608FirstMatch=.FALSE.4609SecondMatch=.FALSE.4610ThirdMatch=.FALSE.4611DO k=1,34612IF (BdryNodes(FirstBdryID,1,i) == BdryNodes(SecondBdryID,k,j)) THEN4613FirstMatch=.TRUE.4614Match = Match + 14615END IF4616IF (BdryNodes(FirstBdryID,2,i) == BdryNodes(SecondBdryID,k,j)) THEN4617SecondMatch=.TRUE.4618Match = Match + 14619END IF4620IF (BdryNodes(FirstBdryID,3,i) == BdryNodes(SecondBdryID,k,j)) THEN4621ThirdMatch=.TRUE.4622Match = Match + 14623END IF4624END DO4625IF (Match == 2) THEN4626CountPairs(FirstBdryID,SecondBdryID-FirstBdryID) = CountPairs(FirstBdryId,SecondBdryID-FirstBdryID) + 14627IF (CountPairs(FirstBdryID,SecondBdryID-FirstBdryID) > &4628SIZE(SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,1,:))) THEN4629IF(Debug) PRINT *, 'SharedPairs boundaryIDs-,',FirstBdryID,SecondBdryID,'doubling size of node array.'4630CALL Double4DArraySize(SharedPairs)4631END IF4632IF (FirstMatch .AND. SecondMatch) THEN4633SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,:,CountPairs(FirstBdryID,SecondBdryID-FirstBdryID)) &4634= BdryNodes(FirstBdryID,1:2,i)4635ELSE IF (SecondMatch .AND. ThirdMatch) THEN4636SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,:,CountPairs(FirstBdryID,SecondBdryID-FirstBdryID)) &4637= BdryNodes(FirstBdryID,2:3,i)4638ELSE IF (FirstMatch .AND. ThirdMatch) THEN4639SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,1,CountPairs(FirstBdryID,SecondBdryID-FirstBdryID)) &4640= BdryNodes(FirstBdryID,1,i)4641SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,2,CountPairs(FirstBdryID,SecondBdryID-FirstBdryID)) &4642= BdryNodes(FirstBdryID,3,i)4643END IF4644ELSE IF (Match == 3) THEN4645PRINT*, 'BoundaryElement: Duplicated', FirstBdryID,BdryNodes(FirstBdryID,:,i)4646PRINT*, 'BoundaryElement: Duplicated', SecondBdryID,BdryNodes(SecondBdryID,:,j), j4647CALL FATAL(Solvername, "BoundaryElement: Duplicated")4648END IF4649END DO4650END IF4651END DO4652END DO4653END IF4654END DO46554656TotalCount=04657DO i=1,54658DO j=1,54659TotalCount=TotalCount+CountPairs(i,j)4660END DO4661END DO46624663ALLOCATE(Shared(2, TotalCount))46644665CountSoFar=04666DO i=1,54667DO j=1,54668Shared(:,1+CountSoFar:CountSoFar+CountPairs(i,j)) = SharedPairs(i,j,:,1:CountPairs(i,j))4669CountSoFar = CountSoFar + CountPairs(i,j)4670END DO4671END DO46724673END SUBROUTINE GetCalvingEdgeNodes46744675SUBROUTINE MeshVolume(Mesh, Parallel, Volume, ElemMask, Centroid)46764677TYPE(Mesh_t), POINTER :: Mesh4678LOGICAL :: Parallel4679REAL(kind=dp) :: Volume4680LOGICAL, OPTIONAL :: ElemMask(:)4681REAL(kind=dp), OPTIONAL :: Centroid(3)4682!-----------------------------4683TYPE(Element_t), POINTER :: Element4684INTEGER :: i, j, NBdry, NBulk, n, ierr4685INTEGER, ALLOCATABLE :: ElementNodes(:)4686REAL(kind=dp), ALLOCATABLE :: Vertices(:,:), Vectors(:,:), PartVolume(:)4687REAL(kind=dp) :: det, Centre(3)46884689NBdry = Mesh % NumberOfBoundaryElements4690NBulk = Mesh % NumberOfBulkElements46914692ALLOCATE(Vertices(4,3), Vectors(3,3))46934694! calculate volume of each bulk tetra. Add these together to get mesh volume4695Volume = 0.0_dp4696IF(PRESENT(Centroid)) Centroid = 0.0_dp4697DO, i=1, NBulk4698IF(PRESENT(ElemMask)) THEN4699IF(.NOT. ElemMask(i)) CYCLE4700END IF4701Element => Mesh % Elements(i)4702ElementNodes = Element % NodeIndexes4703n = Element % TYPE % NumberOfNodes47044705IF(n /= 4) CALL FATAL('MeshVolume', 'Only designed for tetra mesh')47064707! get elem nodes4708DO j=1, n4709Vertices(j,1) = Mesh % Nodes % x(ElementNodes(j))4710Vertices(j,2) = Mesh % Nodes % y(ElementNodes(j))4711Vertices(j,3) = Mesh % Nodes % z(ElementNodes(j))4712END DO47134714! calculate vectors AB, AC and AD4715! play these in 3x3 matrix4716DO j=1,34717Vectors(j,:) = Vertices(1,:) - Vertices(j+1,:)4718END DO47194720! calc matrix det4721Det = ABS(Vectors(1,1) * (Vectors(2,2)*Vectors(3,3) - Vectors(2,3)*Vectors(3,2)) &4722- Vectors(1,2) * (Vectors(2,1)*Vectors(3,3) - Vectors(2,3)*Vectors(3,1)) &4723+ Vectors(1,3) * (Vectors(2,1)*Vectors(3,2) - Vectors(2,2)*Vectors(3,1)))47244725Centre(1) = SUM(Vertices(:,1))/44726Centre(2) = SUM(Vertices(:,2))/44727Centre(3) = SUM(Vertices(:,3))/447284729! tetra volume = det/64730Volume = Volume + Det/64731IF(PRESENT(Centroid)) Centroid = Centroid + Det/6 * Centre4732END DO47334734IF(PRESENT(Centroid)) Centroid = Centroid / Volume47354736! if parallel calculate total mesh volume over all parts4737IF(Parallel) THEN4738ALLOCATE(PartVolume(ParEnv % PEs))4739CALL MPI_AllGather(Volume, 1, MPI_DOUBLE_PRECISION, &4740PartVolume, 1, MPI_DOUBLE_PRECISION, ELMER_COMM_WORLD, ierr)4741Volume = SUM(PartVolume)4742END IF47434744END SUBROUTINE MeshVolume47454746! subroutine to interp variables for missing nodes caused from terminus advance. These are generally on the calving front4747! but could be anywhere on the mesh which has advanced beyond the previous timestep4748SUBROUTINE InterpAdvanceUnfoundNodes(OldMesh, NewMesh, Unfoundnodes)47494750IMPLICIT NONE47514752TYPE(Mesh_t), POINTER :: OldMesh, NewMesh4753LOGICAL, POINTER :: UnfoundNodes(:)4754!----------------------------4755INTEGER :: i,j, UnfoundCount, ClusterSize, ierr, CountDOFs, CountRepeats, min_val, max_val, &4756previous, NodeCount4757INTEGER, ALLOCATABLE :: PartUnfoundCount(:), UnfoundDOFS(:), UnfoundIndex(:), disps(:), &4758AllUnfoundDOFS(:), unique(:), repeats(:), FinalDOFs(:), UnfoundShared(:)4759CHARACTER(LEN=MAX_NAME_LEN) :: Solvername4760SolverName = 'InterpAdvanceUnfoundNodes'47614762PRINT*, SolverName47634764UnfoundCount = COUNT(UnfoundNodes)47654766ClusterSize = ParEnv % PEs47674768! Gather missing counts at this stage4769ALLOCATE(PartUnfoundCount(ClusterSize))4770CALL MPI_AllGather(UnfoundCount, 1, MPI_INTEGER, &4771PartUnfoundCount, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)47724773! Process node numbers and global node number important for translation later on4774! gather all DOFs from all processes4775UnfoundDOFS = PACK(NewMesh % ParallelInfo % GlobalDOFs, UnfoundNodes)4776UnfoundIndex = PACK((/ (i,i=1,SIZE(UnfoundNodes)) /),UnfoundNodes .eqv. .TRUE.)47774778ALLOCATE(disps(ClusterSize))4779disps(1) = 04780DO i=2,ClusterSize4781disps(i) = disps(i-1) + PartUnfoundCount(i-1)4782END DO4783ALLOCATE(AllUnfoundDOFS(SUM(PartUnfoundCount)))4784CALL MPI_allGatherV(UnfoundDOFS, UnfoundCount, MPI_INTEGER, &4785AllUnfoundDOFS, PartUnfoundCount, disps, MPI_INTEGER, ELMER_COMM_WORLD, ierr)4786IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")47874788! Loop to remove duplicates and order allDOFs in ascending order4789CountDOFs=04790CountRepeats=04791IF(SUM(PartUnfoundCount) > 0) THEN4792ALLOCATE(unique(SIZE(AllUnfoundDOFS)), repeats(SIZE(AllUnfoundDOFS)))4793min_val = minval(AllUnfoundDOFS)-14794max_val = maxval(AllUnfoundDOFS)47954796DO WHILE (min_val<max_val)4797Previous = COUNT(AllUNfoundDOFS>min_val)4798CountDOFs = CountDOFs+14799min_val = MINVAL(AllUnfoundDOFS, mask=AllUnfoundDOFS>min_val)4800Unique(countDOFs) = min_val4801IF(COUNT(AllUnfoundDOFS>min_val) /= Previous-1) THEN4802CountRepeats = CountRepeats + 14803Repeats(CountRepeats) = min_val4804END IF4805END DO4806END IF4807ALLOCATE(FinalDOFs(CountDOFs), source=Unique(1:countDOFs))4808ALLOCATE(UnfoundShared(CountRepeats), source=Repeats(1:CountRepeats))48094810PRINT*, ParEnv % MyPE, SolverName, Size(FinalDOFs), UnfoundCount4811!Loop through all DOFS with barrier before shared nodes4812NodeCount = 04813DO i=1, SIZE(FinalDOFs)4814IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN4815DO j=1, UnfoundCount4816IF(UnfoundDOFS(j) == FinalDOFs(i)) nodecount = j4817END DO4818END IF4819! no need for a mask since nodes in both arrays in ascending order4820IF(ANY(UnfoundShared == FinalDOFs(i))) THEN4821! ok to barrier since all parts enter loop and4822! have same AllUnfoundDOFs/UnfoundShared4823! barrier for shared nodes to endsure these are found at same time4824CALL MPI_Barrier(ELMER_COMM_WORLD, ierr)4825!nodenumber = UnfoundIndex(nodecount) since different on each process4826!always finds correct translation from DOFs to process nodenumber since4827!all arrays in ascending order4828IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN4829PRINT *,ParEnv % MyPE,'Didnt find shared 3D point: ', UnfoundIndex(nodecount), &4830' x:', NewMesh % Nodes % x(Unfoundindex(nodecount)),&4831' y:', NewMesh % Nodes % y(Unfoundindex(nodecount)),&4832' z:', NewMesh % Nodes % z(Unfoundindex(nodecount)), &4833'GDOF', FinalDOFs(i), &4834NewMesh % ParallelInfo % GlobalDOFs(UnfoundIndex(nodecount))4835CALL InterpolateUnfoundSharedPoint3D( UnfoundIndex(nodecount), NewMesh, &4836NewMesh % Variables, FinalDOFs )4837END IF4838! no need for a mask since nodes in both arrays in ascending order4839ELSE IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN4840!nodenumber = UnfoundIndex(nodecount) since different on each process4841!always finds correct translation from DOFs to process nodenumber since4842!all arrays in ascending order4843PRINT *,ParEnv % MyPE,'Didnt find 3D point: ', UnfoundIndex(nodecount), &4844' x:', NewMesh % Nodes % x(Unfoundindex(nodecount)),&4845' y:', NewMesh % Nodes % y(Unfoundindex(nodecount)),&4846' z:', NewMesh % Nodes % z(Unfoundindex(nodecount)), &4847'GDOF', FinalDOFs(i), &4848NewMesh % ParallelInfo % GlobalDOFs(UnfoundIndex(nodecount))4849CALL InterpolateUnfoundPoint3D( UnfoundIndex(nodecount), NewMesh, &4850NewMesh % Variables, FinalDOFs )4851END IF4852END DO48534854END SUBROUTINE InterpAdvanceUnfoundNodes48554856SUBROUTINE InterpolateUnfoundPoint3D( NodeNumber, Mesh, Variables, UnfoundDOFS )48574858! similar process to InterpolateUnfoundPoint but uses bulk element4859! 3D interpolation4860! also prevents unfound nodes which have yet to be interped from being suppnodes48614862TYPE(Mesh_t), TARGET, INTENT(INOUT) :: Mesh4863TYPE(Variable_t), POINTER, OPTIONAL :: Variables4864INTEGER :: NodeNumber4865INTEGER, ALLOCATABLE :: UnfoundDOFS(:)4866!------------------------------------------------------------------------------4867TYPE(Variable_t), POINTER :: Var4868TYPE(Element_t),POINTER :: Element4869LOGICAL :: Parallel, Debug, HasNeighbours4870LOGICAL, ALLOCATABLE :: ValidNode(:), SuppNodeMask(:,:), SuppNodePMask(:,:)4871REAL(KIND=dp) :: Point(3), SuppPoint(3), weight, Exponent, distance4872REAL(KIND=dp), ALLOCATABLE :: interpedValue(:),SuppNodeWeights(:),SumWeights(:),&4873InterpedPValue(:), PSumWeights(:)4874INTEGER :: i,j,n,idx,NoNeighbours,NoSuppNodes, MaskCount, PMaskCount4875INTEGER, ALLOCATABLE :: WorkInt(:), SuppNodes(:)4876INTEGER, POINTER :: Neighbours(:)4877Debug = .TRUE.4878Parallel = ParEnv % PEs > 148794880!The sought point4881Point(1) = Mesh % Nodes % x(NodeNumber)4882Point(2) = Mesh % Nodes % y(NodeNumber)4883Point(3) = Mesh % Nodes % z(NodeNumber)48844885!IDW exponent4886Exponent = 1.048874888!Is another partition also contributing to this4889NoNeighbours = SIZE(Mesh % ParallelInfo % &4890NeighbourList(NodeNumber) % Neighbours) - 14891HasNeighbours = NoNeighbours > 048924893IF(HasNeighbours) THEN4894! given the complexity of shared point problems put in seperate subroutine4895CALL FATAL('InterpolateUnfoundPoint3D', 'Use InterpolateUnfoundSharedPoint3D for shared nodes!')4896END IF48974898ALLOCATE(WorkInt(100))4899WorkInt = 049004901! cycle trhough bulk elements4902NoSuppNodes = 04903DO i=1,Mesh % NumberOfBulkElements4904Element => Mesh % Elements(i)4905n = Element % TYPE % NumberOfNodes49064907!Doesn't contain our point4908IF(.NOT. ANY(Element % NodeIndexes(1:n)==NodeNumber)) CYCLE4909!Cycle element nodes4910DO j=1,n4911idx = Element % NodeIndexes(j)4912IF(idx == NodeNumber) CYCLE4913IF(ANY(WorkInt == idx)) CYCLE4914! do not include nodes that has yet to be interped4915! nodes are interped in GDOF order so if this unfoundnode has a lower4916! GDOF then the SuppNode has yet to be interped4917IF(ANY(UnfoundDOFS == Mesh % ParallelInfo % GlobalDOFs(idx)) .AND. &4918Mesh % ParallelInfo % GlobalDOFs(NodeNumber) < Mesh % ParallelInfo % GlobalDOFs(idx)) CYCLE49194920NoSuppNodes = NoSuppNodes + 14921WorkInt(NoSuppNodes) = idx4922END DO4923END DO49244925ALLOCATE(SuppNodes(NoSuppNodes))4926SuppNodes = WorkInt(:NoSuppNodes)49274928IF(Debug) PRINT *,ParEnv % MyPE,'Debug, seeking nn: ',NodeNumber,' found ',&4929NoSuppNodes,' supporting nodes.'49304931! calculate maskcount and pmaskcount4932IF(PRESENT(Variables)) THEN4933MaskCount = 0 ! zero since no variables already4934PMaskCount = 04935Var => Variables4936DO WHILE(ASSOCIATED(Var))4937MaskCount = MaskCount + 14938IF(ASSOCIATED(Var % PrevValues)) &4939PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)49404941Var => Var % Next4942END DO4943END IF49444945!create suppnode mask and get node values4946! get node weights too4947ALLOCATE(SuppNodeMask(NoSuppNodes, MaskCount), &4948SuppNodePMask(NoSuppNodes, PMaskCount), &4949InterpedValue(MaskCount), InterpedPValue(PMaskCount), &4950SuppNodeWeights(NoSuppNodes))4951SuppNodeMask = .FALSE.; SuppNodePMask = .FALSE.4952interpedValue = 0.0_dp; InterpedPValue = 0.0_dp4953DO i=1, NoSuppNodes4954! SuppNodes for interp4955SuppPoint(1) = Mesh % Nodes % x(SuppNodes(i))4956SuppPoint(2) = Mesh % Nodes % y(SuppNodes(i))4957SuppPoint(3) = Mesh % Nodes % z(SuppNodes(i))49584959distance = 0.0_dp4960DO j=1,34961distance = distance + (Point(j) - SuppPoint(j))**2.0_dp4962END DO4963distance = distance**0.5_dp49644965weight = distance**(-exponent)4966SuppNodeWeights(i) = weight49674968IF(PRESENT(Variables)) THEN4969MaskCount = 0 ! zero since no variables already4970PMaskCount = 04971Var => Variables4972DO WHILE(ASSOCIATED(Var))4973MaskCount = MaskCount + 14974IF(ASSOCIATED(Var % PrevValues)) &4975PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)4976IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global4977(Var % DOFs > 1) .OR. & !-multi-dof4978Var % Secondary) THEN !-secondary4979Var => Var % Next4980CYCLE4981ELSE IF(LEN(Var % Name) >= 10) THEN4982IF(Var % Name(1:10)=='coordinate') THEN !-coord var4983Var => Var % Next4984CYCLE4985END IF4986END IF4987IF(Var % Perm(SuppNodes(i)) <= 0 .OR. &4988(Var % Perm(NodeNumber) <= 0)) THEN !-not fully defined here4989Var => Var % Next4990CYCLE4991END IF49924993SuppNodeMask(i, MaskCount) = .TRUE.4994InterpedValue(MaskCount) = interpedvalue(MaskCount) + &4995weight * Var % Values(Var % Perm(SuppNodes(i)))49964997!PrevValues4998IF(ASSOCIATED(Var % PrevValues)) THEN4999SuppNodePMask(i, PMaskCount) = .TRUE.5000DO j=1, SIZE(Var % PrevValues, 2)5001n = PMaskCount + j - SIZE(Var % PrevValues, 2)5002InterpedPValue(n) = InterpedPValue(n) +&5003weight * Var % PrevValues(Var % Perm(SuppNodes(i)), j)5004END DO5005END IF50065007Var => Var % Next5008END DO5009END IF5010END DO50115012!Calculate weights5013ALLOCATE(SumWeights(MaskCount), PSumWeights(PMaskCount))5014SumWeights = 0.0_dp; PSumWeights = 0.0_dp5015DO i=1, NoSuppNodes5016DO j=1, MaskCount5017!var exists on that node5018IF(SuppNodeMask(i,j)) &5019SumWeights(j) = SumWeights(j) + SuppNodeWeights(i)5020END DO5021DO j=1, PMaskCount5022IF(SuppNodePMask(i,j)) &5023PSumWeights(j) = PSumWeights(j) + SuppNodeWeights(i)5024END DO5025END DO50265027interpedValue = interpedValue/SumWeights5028InterpedPValue = InterpedPValue/PSumWeights50295030IF(PRESENT(Variables)) THEN5031MaskCount = 05032PMaskCount = 05033Var => Variables5034DO WHILE(ASSOCIATED(Var))5035MaskCount = MaskCount + 15036IF(ASSOCIATED(Var % PrevValues)) &5037PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)5038IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global5039(Var % DOFs > 1) .OR. & !-multi-dof5040Var % Secondary) THEN !-secondary5041Var => Var % Next5042CYCLE5043ELSE IF(LEN(Var % Name) >= 10) THEN5044IF(Var % Name(1:10)=='coordinate') THEN !-coord var5045Var => Var % Next5046CYCLE5047END IF5048END IF5049IF(Var % Perm(NodeNumber) <= 0) THEN !-not fully defined here5050Var => Var % Next5051CYCLE5052END IF50535054!if any suppnode had variable5055IF(ANY(SuppNodeMask(:,MaskCount))) THEN5056Var % Values(Var % Perm(NodeNumber)) = interpedValue(MaskCount)5057END IF50585059IF(ASSOCIATED(Var % PrevValues)) THEN5060DO j=1, SIZE(Var % PrevValues,2)5061n = PMaskCount + j - SIZE(Var % PrevValues, 2)5062IF(ANY(SuppNodePMask(:,n))) THEN ! defined at suppnodes5063Var % PrevValues(Var % Perm(NodeNumber),j) = InterpedPValue(n)5064ELSE5065CALL WARN('InterpolateUnfoundPoint3D', 'PrevValues not found on Supp Nodes but defined on node so setting to zero')5066Var % PrevValues(Var % Perm(NodeNumber),j) = 0.0_dp5067END IF5068END DO5069END IF50705071Var => Var % Next5072END DO5073END IF50745075END SUBROUTINE InterpolateUnfoundPoint3D50765077SUBROUTINE InterpolateUnfoundSharedPoint3D( NodeNumber, Mesh, Variables, UnfoundDOFS )50785079! similar process to InterpolateUnfoundShared Point but uses bulk element5080! 3D interpolation5081! also prevents unfound nodes which have yet to be interped from being suppnodes50825083TYPE(Mesh_t), TARGET, INTENT(INOUT) :: Mesh5084TYPE(Variable_t), POINTER, OPTIONAL :: Variables5085INTEGER :: NodeNumber5086INTEGER, ALLOCATABLE :: UnfoundDOFs(:)5087!------------------------------------------------------------------------------5088TYPE(Variable_t), POINTER :: Var5089TYPE(Element_t),POINTER :: Element5090LOGICAL :: Parallel, Debug, HasNeighbours5091LOGICAL, ALLOCATABLE :: ValidNode(:), SuppNodeMask(:,:), PartSuppNodeMask(:,:,:), &5092UseProc(:), SuppNodePMask(:,:), PartSuppNodePMask(:,:,:)5093REAL(KIND=dp) :: Point(3), SuppPoint(3), weight, Exponent, distance5094REAL(KIND=dp), ALLOCATABLE :: interpedValue(:), PartInterpedValues(:,:), &5095SuppNodeWeights(:), PartSuppNodeWeights(:,:), SumWeights(:),&5096FinalInterpedValues(:), InterpedPValue(:), PartInterpedPValues(:,:), &5097FinalInterpedPValues(:), PSumWeights(:)5098INTEGER :: i,j,k,n,idx,NoNeighbours,NoSuppNodes,NoUsedNeighbours,&5099proc,status(MPI_STATUS_SIZE), counter, ierr, MaskCount, PMaskCount5100INTEGER, ALLOCATABLE :: NeighbourParts(:), WorkInt(:), SuppNodes(:), PartNoSuppNodes(:), WorkInt2(:), &5101GDOFs(:), PartGDOFs(:), GDOFLoc(:)5102INTEGER, POINTER :: Neighbours(:)5103Debug = .TRUE.5104Parallel = ParEnv % PEs > 151055106!The sought point5107Point(1) = Mesh % Nodes % x(NodeNumber)5108Point(2) = Mesh % Nodes % y(NodeNumber)5109Point(3) = Mesh % Nodes % z(NodeNumber)51105111!IDW exponent5112Exponent = 1.051135114!Is another partition also contributing to this5115NoNeighbours = SIZE(Mesh % ParallelInfo % &5116NeighbourList(NodeNumber) % Neighbours) - 15117HasNeighbours = NoNeighbours > 051185119ALLOCATE(WorkInt(100), WorkInt2(100))5120WorkInt = 0; WorkInt2 = 051215122!Cycle elements containing our node, adding other nodes to list5123NoSuppNodes = 05124DO i=1,Mesh % NumberOfBulkElements5125Element => Mesh % Elements(i)5126n = Element % TYPE % NumberOfNodes51275128!Doesn't contain our point5129IF(.NOT. ANY(Element % NodeIndexes(1:n)==NodeNumber)) CYCLE5130!Cycle element nodes5131DO j=1,n5132idx = Element % NodeIndexes(j)5133IF(idx == NodeNumber) CYCLE5134IF(ANY(WorkInt == idx)) CYCLE5135! do not include nodes that has yet to be interped5136! nodes are interped in GDOF order so if this unfoundnode has a lower5137! GDOF then the SuppNode has yet to be interped5138IF(ANY(UnfoundDOFS == Mesh % ParallelInfo % GlobalDOFs(idx)) .AND. &5139Mesh % ParallelInfo % GlobalDOFs(NodeNumber) < Mesh % ParallelInfo % GlobalDOFs(idx)) CYCLE51405141NoSuppNodes = NoSuppNodes + 15142WorkInt(NoSuppNodes) = idx5143WorkInt2(NoSuppNodes) = Mesh % ParallelInfo % GlobalDOFs(idx)5144END DO5145END DO51465147ALLOCATE(SuppNodes(NoSuppNodes), GDOFs(NoSuppNodes))5148SuppNodes = WorkInt(:NoSuppNodes)5149GDOFs = WorkInt2(:NoSuppNodes)51505151!Create list of neighbour partitions5152ALLOCATE(NeighbourParts(NoNeighbours))5153counter = 05154DO i=1,NoNeighbours+15155IF(Mesh % ParallelInfo % NeighbourList(NodeNumber) % &5156Neighbours(i) == ParEnv % MyPE) CYCLE5157counter = counter + 15158NeighbourParts(counter) = Mesh % ParallelInfo &5159% NeighbourList(NodeNumber) % Neighbours(i)5160END DO51615162! share number of supp nodes5163ALLOCATE(PartNoSuppNodes(NoNeighbours+1))5164PartNoSuppNodes(1) = NoSuppNodes5165DO i=1, NoNeighbours5166proc = NeighbourParts(i)5167CALL MPI_BSEND( NoSuppNodes, 1, MPI_INTEGER, proc, &51683998, ELMER_COMM_WORLD,ierr )5169CALL MPI_RECV( PartNoSuppNodes(i+1) , 1, MPI_INTEGER, proc, &51703998, ELMER_COMM_WORLD, status, ierr )5171END DO51725173! is the proc used?5174NoUsedNeighbours=NoNeighbours5175ALLOCATE(UseProc(NoNeighbours+1))5176UseProc = .TRUE. ! default is to use proc5177IF(ANY(PartNoSuppNodes == 0)) THEN5178DO i=1, NoNeighbours+15179IF(PartNoSuppNodes(i) == 0) UseProc(i) = .FALSE.5180END DO5181!reassign noneighbours to neighbours with suppnodes5182NoUsedNeighbours = COUNT(UseProc(2:NoNeighbours+1))5183END IF51845185! change of strategy here. previously supp nodes dropped if a larger5186! neighbour present. However this doesn't work for complex geometries often5187! resulting from repartitioning. Instead gather global indexes and remove supp5188! node if global index present on higher partition5189ALLOCATE(PartGDOFs(SUM(PartNoSuppNodes)))5190counter = 05191IF(NoSuppNodes /= 0) THEN5192PartGDOFs(1:NoSuppNodes) = GDOFs5193counter=NoSuppNodes5194END IF5195DO i=1, NoNeighbours5196proc = NeighbourParts(i)5197IF(UseProc(1)) THEN ! if this proc has supp nodes send5198CALL MPI_BSEND( GDOFs, NoSuppNodes, MPI_INTEGER, proc, &51993999, ELMER_COMM_WORLD,ierr )5200END IF5201IF(UseProc(i+1)) THEN !neighouring proc has supp nodes5202CALL MPI_RECV( PartGDOFs(counter+1:counter+PartNoSuppNodes(i+1)), &5203PartNoSuppNodes(i+1), MPI_INTEGER, proc, &52043999, ELMER_COMM_WORLD, status, ierr )5205counter=counter+PartNoSuppNodes(i+1)5206END IF5207END DO52085209!create list of GDOFS parts5210ALLOCATE(GDOFLoc(SUM(PartNoSuppNodes)))5211counter=05212DO i=1, NoNeighbours+15213IF(PartNoSuppNodes(i) == 0) CYCLE5214IF(i==1) THEN5215GDOFLoc(counter+1:counter+PartNoSuppNodes(i)) = ParEnv % MyPE5216ELSE5217GDOFLoc(counter+1:counter+PartNoSuppNodes(i)) = NeighbourParts(i-1)5218END IF5219counter = counter + PartNoSuppNodes(i)5220END DO52215222! is global index present on higher part?5223DO i=1, NoSuppNodes5224DO j=NoSuppNodes+1, SUM(PartNoSuppNodes)5225IF(GDOFs(i) == PartGDOFs(j)) THEN5226IF(GDOFLoc(j) > ParEnv % MyPE) THEN5227WorkInt(i) = 05228END IF5229END IF5230END DO5231END DO5232NoSuppNodes = COUNT(WorkInt > 0)5233IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, seeking ',NodeNumber,&5234' higher partition has node, so deleting...'52355236DEALLOCATE(SuppNodes)5237ALLOCATE(SuppNodes(NoSuppNodes))5238SuppNodes = PACK(WorkInt, WorkInt > 0)5239DEALLOCATE(WorkInt)52405241IF(NoSuppNodes == 0) THEN5242WRITE(Message, '(i0,A,i0)') ParEnv % MyPE, ' NoSuppNodes = ',NoSuppNodes5243CALL WARN('CalvingGeometry', Message)5244END IF52455246!share NoSuppNodes5247PartNoSuppNodes(1) = NoSuppNodes5248DO i=1, NoNeighbours5249proc = NeighbourParts(i)5250CALL MPI_BSEND( NoSuppNodes, 1, MPI_INTEGER, proc, &52514000, ELMER_COMM_WORLD,ierr )5252CALL MPI_RECV( PartNoSuppNodes(i+1) , 1, MPI_INTEGER, proc, &52534000, ELMER_COMM_WORLD, status, ierr )5254END DO52555256! an mpi_error can occur if one proc has zero supp nodes5257! if proc has zero supp nodes it needs to receive mpi info but cannot send any5258! therefore neighbours need to allocate less space to avoid nans5259NoUsedNeighbours=NoNeighbours5260UseProc = .TRUE. ! default is to use proc5261IF(ANY(PartNoSuppNodes == 0)) THEN5262DO i=1, NoNeighbours+15263IF(PartNoSuppNodes(i) == 0) UseProc(i) = .FALSE.5264END DO5265!reassign noneighbours to neighbours with suppnodes5266NoUsedNeighbours = COUNT(UseProc(2:NoNeighbours+1))5267END IF52685269! calculate maskcount and pmaskcount5270IF(PRESENT(Variables)) THEN5271MaskCount = 0 ! zero since no variables already5272PMaskCount = 05273Var => Variables5274DO WHILE(ASSOCIATED(Var))5275MaskCount = MaskCount + 15276IF(ASSOCIATED(Var % PrevValues)) &5277PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)52785279Var => Var % Next5280END DO5281END IF52825283!create suppnode mask and get node values5284! get node weights too5285ALLOCATE(SuppNodeMask(NoSuppNodes, MaskCount), &5286SuppNodePMask(NoSuppNodes, PMaskCount), &5287InterpedValue(MaskCount), InterpedPValue(PMaskCount), &5288SuppNodeWeights(NoSuppNodes))5289SuppNodeMask = .FALSE.; SuppNodePMask = .FALSE.5290interpedValue = 0.0_dp; InterpedPValue = 0.0_dp5291DO i=1, NoSuppNodes5292! SuppNodes for interp5293SuppPoint(1) = Mesh % Nodes % x(SuppNodes(i))5294SuppPoint(2) = Mesh % Nodes % y(SuppNodes(i))5295SuppPoint(3) = Mesh % Nodes % z(SuppNodes(i))52965297distance = 0.0_dp5298DO j=1,35299distance = distance + (Point(j) - SuppPoint(j))**2.0_dp5300END DO5301distance = distance**0.5_dp53025303weight = distance**(-exponent)5304SuppNodeWeights(i) = weight53055306IF(PRESENT(Variables)) THEN5307MaskCount = 0 ! zero since no variables already5308PMaskCount = 05309Var => Variables5310DO WHILE(ASSOCIATED(Var))5311MaskCount = MaskCount + 15312IF(ASSOCIATED(Var % PrevValues)) &5313PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)5314IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global5315(Var % DOFs > 1) .OR. & !-multi-dof5316Var % Secondary) THEN !-secondary5317Var => Var % Next5318CYCLE5319ELSE IF(LEN(Var % Name) >= 10) THEN5320IF(Var % Name(1:10)=='coordinate') THEN !-coord var5321Var => Var % Next5322CYCLE5323END IF5324END IF5325IF(Var % Perm(SuppNodes(i)) <= 0 .OR. &5326(Var % Perm(NodeNumber) <= 0)) THEN !-not fully defined here5327Var => Var % Next5328CYCLE5329END IF53305331SuppNodeMask(i, MaskCount) = .TRUE.5332InterpedValue(MaskCount) = interpedvalue(MaskCount) + &5333weight * Var % Values(Var % Perm(SuppNodes(i)))53345335!PrevValues5336IF(ASSOCIATED(Var % PrevValues)) THEN5337SuppNodePMask(i, PMaskCount) = .TRUE.5338DO j=1, SIZE(Var % PrevValues, 2)5339n = PMaskCount + j - SIZE(Var % PrevValues, 2)5340InterpedPValue(n) = InterpedPValue(n) +&5341weight * Var % PrevValues(Var % Perm(SuppNodes(i)), j)5342END DO5343END IF53445345Var => Var % Next5346END DO5347END IF5348END DO53495350! all parallel communication changed to use NoUsedNeighbours so neighbouring procs5351! of those with zero suppnodes (no info) do not over allocate (eg allocate nans)5352!share SuppNodeMask5353ALLOCATE(PartSuppNodeMask(NoUsedNeighbours+1, 50, MaskCount))5354PartSuppNodeMask = .FALSE.5355PartSuppNodeMask(1,:NoSuppNodes,:) = SuppNodeMask5356counter=05357DO i=1, NoNeighbours5358proc = NeighbourParts(i)5359IF(UseProc(1)) THEN ! if this proc has supp nodes send5360CALL MPI_BSEND( SuppNodeMask, NoSuppNodes*MaskCount, MPI_LOGICAL, proc, &53614001, ELMER_COMM_WORLD,ierr )5362END IF5363IF(UseProc(i+1)) THEN !neighouring proc has supp nodes5364counter=counter+15365CALL MPI_RECV( PartSuppNodeMask(counter+1,:PartNoSuppNodes(i+1),: ) , &5366PartNoSuppNodes(i+1)*MaskCount, MPI_LOGICAL, proc, &53674001, ELMER_COMM_WORLD, status, ierr )5368END If5369END DO53705371!share SuppNodePMask for prevvalues5372ALLOCATE(PartSuppNodePMask(NoUsedNeighbours+1, 50, PMaskCount))5373PartSuppNodePMask = .FALSE.5374PartSuppNodePMask(1,:NoSuppNodes,:) = SuppNodePMask5375counter=05376DO i=1, NoNeighbours5377proc = NeighbourParts(i)5378IF(UseProc(1)) THEN ! if this proc has supp nodes send5379CALL MPI_BSEND( SuppNodePMask, NoSuppNodes*PMaskCount, MPI_LOGICAL, proc, &53804011, ELMER_COMM_WORLD,ierr )5381END IF5382IF(UseProc(i+1)) THEN !neighouring proc has supp nodes5383counter=counter+15384CALL MPI_RECV( PartSuppNodePMask(counter+1,:PartNoSuppNodes(i+1),: ) , &5385PartNoSuppNodes(i+1)*PMaskCount, MPI_LOGICAL, proc, &53864011, ELMER_COMM_WORLD, status, ierr )5387END If5388END DO53895390!share interped value5391ALLOCATE(PartInterpedValues(NoUsedNeighbours+1, MaskCount))5392PartInterpedValues(1,1:MaskCount) = InterpedValue5393counter=05394DO i=1, NoNeighbours5395proc = NeighbourParts(i)5396IF(UseProc(1)) THEN ! if this proc has supp nodes send5397CALL MPI_BSEND( InterpedValue, MaskCount, MPI_DOUBLE_PRECISION, proc, &53984002, ELMER_COMM_WORLD,ierr )5399END IF5400IF(UseProc(i+1)) THEN !neighouring prco has supp nodes5401counter=counter+15402CALL MPI_RECV( PartInterpedValues(counter+1,:), MaskCount, MPI_DOUBLE_PRECISION, proc, &54034002, ELMER_COMM_WORLD, status, ierr )5404END IF5405END DO54065407!share interped prevvalue5408ALLOCATE(PartInterpedPValues(NoUsedNeighbours+1, PMaskCount))5409PartInterpedPValues(1,1:PMaskCount) = InterpedPValue5410counter=05411DO i=1, NoNeighbours5412proc = NeighbourParts(i)5413IF(UseProc(1)) THEN ! if this proc has supp nodes send5414CALL MPI_BSEND( InterpedPValue, PMaskCount, MPI_DOUBLE_PRECISION, proc, &54154012, ELMER_COMM_WORLD,ierr )5416END IF5417IF(UseProc(i+1)) THEN !neighouring prco has supp nodes5418counter=counter+15419CALL MPI_RECV( PartInterpedPValues(counter+1,:), PMaskCount, MPI_DOUBLE_PRECISION, proc, &54204012, ELMER_COMM_WORLD, status, ierr )5421END IF5422END DO54235424!share suppnode weights5425ALLOCATE(PartSuppNodeWeights(NoUsedNeighbours+1, 25))5426PartSuppNodeWeights=0.0_dp5427PartSuppNodeWeights(1,1:NoSuppNodes) = SuppNodeWeights5428counter=05429DO i=1, NoNeighbours5430proc = NeighbourParts(i)5431IF(UseProc(1)) THEN ! if this proc has supp nodes send5432CALL MPI_BSEND( SuppNodeWeights, NoSuppNodes, MPI_DOUBLE_PRECISION, proc, &54334003, ELMER_COMM_WORLD,ierr )5434END IF5435IF(UseProc(i+1)) THEN !neighouring prco has supp nodes5436counter=counter+15437CALL MPI_RECV( PartSuppNodeWeights(counter+1,1:PartNoSuppNodes(i+1)), &5438PartNoSuppNodes(i+1), MPI_DOUBLE_PRECISION, proc, &54394003, ELMER_COMM_WORLD, status, ierr )5440END IF5441END DO54425443!calculate interped values5444ALLOCATE(FinalInterpedValues(MaskCount), FinalInterpedPValues(PMaskCount))5445FinalInterpedValues = 0.0_dp; FinalInterpedPValues = 0.0_dp5446! add up interpedvalues5447DO i=1, NoUsedNeighbours+15448FinalInterpedValues = FinalInterpedValues + PartInterpedValues(i, :)5449FinalInterpedPValues = FinalInterpedPValues + PartInterpedPValues(i, :)5450END DO54515452! convert PartNoSuppNodes to only used procs5453ALLOCATE(WorkInt(NoNeighbours+1))5454WorkInt=PartNoSuppNodes5455DEALLOCATE(PartNoSuppNodes)5456ALLOCATE(PartNoSuppNodes(NoUsedNeighbours+1))5457counter=05458DO i=1, NoNeighbours+15459IF(i/=1 .AND. .NOT. UseProc(i)) CYCLE5460counter=counter+15461PartNoSuppNodes(counter) = WorkInt(i)5462END DO5463DEALLOCATE(WorkInt)54645465! calculate weight for each var5466ALLOCATE(SumWeights(MaskCount), PSumWeights(PMaskCount))5467SumWeights = 0.0_dp; PSumWeights = 0.0_dp5468DO i=1, NoUsedNeighbours+15469! loop through procs suppnodes5470DO j=1, PartNoSuppNodes(i)5471DO k=1, MaskCount5472!var exists on that node5473IF(PartSuppNodeMask(i,j,k)) THEN5474SumWeights(k) = SumWeights(k) + PartSuppNodeWeights(i,j)5475END IF5476END DO5477DO k=1, PMaskCount5478!var exists on that node5479IF(PartSuppNodePMask(i,j,k)) THEN5480PSumWeights(k) = PSumWeights(k) + PartSuppNodeWeights(i,j)5481END IF5482END DO5483END DO5484END DO54855486!interpedvalue/sumweights5487FinalInterpedValues = FinalInterpedValues/sumweights5488FinalInterpedPValues = FinalInterpedPValues/PSumWeights54895490!return values5491IF(PRESENT(Variables)) THEN5492MaskCount = 0; PMaskCount = 05493Var => Variables5494DO WHILE(ASSOCIATED(Var))5495MaskCount = MaskCount + 15496IF(ASSOCIATED(Var % PrevValues)) &5497PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)54985499IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global5500(Var % DOFs > 1) .OR. & !-multi-dof5501Var % Secondary) THEN !-secondary5502Var => Var % Next5503CYCLE5504ELSE IF(LEN(Var % Name) >= 10) THEN5505IF(Var % Name(1:10)=='coordinate') THEN !-coord var5506Var => Var % Next5507CYCLE5508END IF5509END IF5510IF(Var % Perm(NodeNumber) <= 0) THEN !-not fully defined here5511Var => Var % Next5512CYCLE5513END IF55145515!if any suppnode from any proc has var5516IF(ANY(PartSuppNodeMask(:,:,MaskCount))) THEN5517Var % Values(Var % Perm(NodeNumber)) = FinalInterpedValues(MaskCount)5518END IF55195520IF(ASSOCIATED(Var % PrevValues)) THEN5521DO j=1, SIZE(Var % PrevValues,2)5522n = PMaskCount + j - SIZE(Var % PrevValues, 2)5523IF(ANY(PartSuppNodePMask(:,:,n))) THEN ! defined at suppnodes5524Var % PrevValues(Var % Perm(NodeNumber),j) = FinalInterpedPValues(n)5525ELSE5526CALL WARN('InterpolateUnfoundSharedPoint3D', &5527'PrevValues not found on Supp Nodes but defined on node so setting to zero')5528Var % PrevValues(Var % Perm(NodeNumber),j) = 0.0_dp5529END IF5530END DO5531END IF55325533Var => Var % Next5534END DO5535END IF55365537END SUBROUTINE InterpolateUnfoundSharedPoint3D55385539!Doubles the size of a pointer double precision array5540!This version takes a Pointer argument, should5541!be used with care...5542SUBROUTINE DoubleDPVectorSizeP(Vec, fill)5543REAL(kind=dp), POINTER :: Vec(:)5544REAL(kind=dp), OPTIONAL :: fill5545!----------------------------------------5546REAL(kind=dp), ALLOCATABLE :: WorkVec(:)55475548ALLOCATE(WorkVec(SIZE(Vec)))5549WorkVec = Vec55505551DEALLOCATE(Vec)5552ALLOCATE(Vec(2*SIZE(WorkVec)))55535554IF(PRESENT(fill)) THEN5555Vec = fill5556ELSE5557Vec = 05558END IF55595560Vec(1:SIZE(WorkVec)) = WorkVec55615562END SUBROUTINE DoubleDPVectorSizeP55635564!Doubles the size of a pointer double precision array5565!Allocatable array version5566SUBROUTINE DoubleDPVectorSizeA(Vec, fill)5567REAL(kind=dp), ALLOCATABLE :: Vec(:)5568REAL(kind=dp), OPTIONAL :: fill5569!----------------------------------------5570REAL(kind=dp), ALLOCATABLE :: WorkVec(:)55715572ALLOCATE(WorkVec(SIZE(Vec)))5573WorkVec = Vec55745575DEALLOCATE(Vec)5576ALLOCATE(Vec(2*SIZE(WorkVec)))55775578IF(PRESENT(fill)) THEN5579Vec = fill5580ELSE5581Vec = 05582END IF55835584Vec(1:SIZE(WorkVec)) = WorkVec55855586END SUBROUTINE DoubleDPVectorSizeA55875588! returns calving polygons if given edge and crevasse info.5589! assumes all this is on boss and then broadcast to other procs.5590SUBROUTINE GetCalvingPolygons(Mesh, CrevassePaths, EdgeX, EdgeY, Polygon, PolyStart, PolyEnd, GridSize)5591IMPLICIT NONE5592TYPE(Mesh_t), POINTER :: Mesh5593TYPE(CrevassePath_t), POINTER :: CrevassePaths5594REAL(kind=dp) :: EdgeX(:), EdgeY(:)5595REAL(kind=dp), OPTIONAL :: GridSize5596!-------------------------------------------------------------------------5597TYPE(CrevassePath_t), POINTER :: CurrentPath5598REAL(kind=dp), ALLOCATABLE :: PolyX(:), PolyY(:), Polygon(:,:)5599INTEGER, ALLOCATABLE :: PolyStart(:), PolyEnd(:)5600INTEGER :: path, i, counter, CrevLen, crop(2), EdgeLen, start, end5601REAL(kind=dp) :: StartX, StartY, EndX, EndY, err_buffer56025603path=05604CurrentPath => CrevassePaths5605DO WHILE(ASSOCIATED(CurrentPath))5606path=path+15607CurrentPath => CurrentPath % Next5608END DO56095610ALLOCATE(PolyX(100), PolyY(100), PolyStart(path), PolyEnd(path))5611counter=05612path=05613CurrentPath => CrevassePaths5614DO WHILE(ASSOCIATED(CurrentPath))5615path=path+156165617start=CurrentPath % NodeNumbers(1)5618end=CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)5619StartX = Mesh % Nodes % x(start)5620StartY = Mesh % Nodes % y(start)5621EndX = Mesh % Nodes % x(end)5622EndY = Mesh % Nodes % y(end)5623CrevLen = CurrentPath % NumberOfNodes56245625crop =05626IF(PRESENT(GridSize)) THEN5627err_buffer = GridSize/105628ELSE5629err_buffer = 0.0_dp5630END IF56315632DO i=1, SIZE(EdgeX)5633IF((EdgeX(i) <= StartX+err_buffer .AND. EdgeX(i) >= StartX-err_buffer) .AND. &5634(EdgeY(i) <= StartY+err_buffer .AND. EdgeY(i) >= StartY-err_buffer)) crop(1) = i5635IF((EdgeX(i) <= EndX+err_buffer .AND. EdgeX(i) >= EndX-err_buffer) .AND. &5636(EdgeY(i) <= EndY+err_buffer .AND. EdgeY(i) >= EndY-err_buffer )) crop(2) = i5637END DO5638IF(ANY(crop == 0)) CALL FATAL('GetCalvingPolygons', 'Edge not found')56395640EdgeLen = MAXVAL(crop)-MINVAL(crop)-2+156415642DO WHILE(SIZE(PolyX) < Counter+CrevLen+EdgeLen+1)5643CALL DoubleDPVectorSize(PolyX)5644CALL DoubleDPVectorSize(PolyY)5645END DO56465647PolyStart(path) = Counter+15648DO i=1, CrevLen5649counter=counter+15650PolyX(Counter) = Mesh % Nodes % x(CurrentPath % NodeNumbers(i))5651PolyY(Counter) = Mesh % Nodes % y(CurrentPath % NodeNumbers(i))5652END DO56535654IF(crop(2) < crop(1)) THEN ! end of crev lines up with start of edge no need to flip edge5655PolyX(Counter+1:Counter+EdgeLen) = EdgeX(MINVAL(crop)+1:MAXVAL(crop)-1)5656PolyY(Counter+1:Counter+EdgeLen) = EdgeY(MINVAL(crop)+1:MAXVAL(crop)-1)5657counter=counter+EdgeLen5658ELSE5659! since crevasses are plotted left to right if crevasse on part of front facing upstream5660! need to add the edge in reverse5661DO i=MAXVAL(crop)-1, MINVAL(crop)+1, -1 ! backwards iteration5662counter=counter+15663PolyX(Counter) = EdgeX(i)5664PolyY(Counter) = EdgeY(i)5665END DO5666END IF56675668! add first node in again to complete polygon5669counter=counter+15670PolyX(Counter) = StartX5671PolyY(counter) = StartY5672PolyEnd(path) = Counter56735674CurrentPath => CurrentPath % Next5675END DO56765677ALLOCATE(Polygon(2, Counter))5678Polygon(1,:) = PolyX(1:Counter)5679Polygon(2,:) = PolyY(1:Counter)5680DEALLOCATE(PolyX, PolyY)56815682END SUBROUTINE GetCalvingPolygons56835684SUBROUTINE RemoveInvalidCrevs(Mesh, CrevassePaths, EdgeX, EdgeY, RemoveInsideCrevs, LateralCrevs, &5685OnLeft, OnRight, OnFront, GridSize)5686IMPLICIT NONE5687TYPE(Mesh_t), POINTER :: Mesh5688TYPE(CrevassePath_t), POINTER :: CrevassePaths5689REAL(kind=dp) :: EdgeX(:), EdgeY(:)5690LOGICAL, OPTIONAL :: OnLeft(:),OnRight(:),OnFront(:)5691LOGICAL :: RemoveInsideCrevs, LateralCrevs5692REAL(kind=dp), OPTIONAL :: GridSize5693!-------------------------------------------------5694TYPE(CrevassePath_t), POINTER :: CurrentPath, WorkPath, SecondPath5695INTEGER :: i,j,k, counter, first, last, path, start, end, startidx, endidx, DeleteEndNodes, spath5696REAL(kind=dp), ALLOCATABLE :: Polygons(:,:), PathPoly(:,:)5697INTEGER, ALLOCATABLE :: PolyStart(:), PolyEnd(:), WorkInt(:)5698REAL(kind=dp) :: xx, yy, StartX, StartY, EndX, EndY, err_buffer, area1, area25699LOGICAL :: inside, debug, Found(2), overlap5700LOGICAL, ALLOCATABLE :: DeleteNode(:), DeleteElement(:), OnEdge(:)57015702IF(.NOT. LateralCrevs) THEN5703! assumption here is that invalid crevs with no interior already removed by5704! a previous call. If lateral edges have been added to crevs cannot filter using edges.57055706! if no part of crev is in interior remove5707CurrentPath => CrevassePaths5708DO WHILE(ASSOCIATED(CurrentPath))5709Found = .FALSE.5710! buffer for floating point errors5711IF(PRESENT(GridSize)) THEN5712err_buffer = GridSize/105713ELSE5714err_buffer = 0.0_dp5715END IF57165717ALLOCATE(OnEdge(CurrentPath % NumberOfNodes))5718OnEdge = .FALSE.5719DO i=1, CurrentPath % NumberOfNodes5720xx = Mesh % Nodes % x(CurrentPath % NodeNumbers(i))5721yy = Mesh % Nodes % y(CurrentPath % NodeNumbers(i))5722DO j=1, SIZE(EdgeX)5723IF((EdgeX(j) <= xx+err_buffer .AND. EdgeX(j) >= xx-err_buffer) .AND. &5724(EdgeY(j) <= yy+err_buffer .AND. EdgeY(j) >= yy-err_buffer)) OnEdge(i) = .TRUE.5725END DO5726END DO57275728IF(ALL(OnEdge)) CurrentPath % Valid = .FALSE.57295730DEALLOCATE(OnEdge)5731CurrentPath => CurrentPath % Next5732END DO5733END IF57345735! remove paths that end on both lateral boundaries5736IF(PRESENT(OnLeft) .OR. PRESENT(OnRight)) THEN5737!CALL Assert((PRESENT(OnLeft) .AND. PRESENT(OnRight)), FuncName, &5738! "Provided only one of OnLeft/OnRight!")57395740!Check that crevasse path doesn't begin and end on same lateral margin5741CurrentPath => CrevassePaths5742DO WHILE(ASSOCIATED(CurrentPath))5743!Check node OnLeft, OnRight5744First = CurrentPath % NodeNumbers(1)5745Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)5746IF((OnLeft(First) .AND. OnLeft(Last)) .OR. &5747(OnRight(First) .AND. OnRight(Last))) THEN5748CurrentPath % Valid = .FALSE.5749END IF5750CurrentPath => CurrentPath % Next5751END DO57525753!Actually remove previous marked5754CurrentPath => CrevassePaths5755DO WHILE(ASSOCIATED(CurrentPath))5756WorkPath => CurrentPath % Next57575758IF(.NOT. CurrentPath % Valid) THEN5759IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath5760CALL RemoveCrevassePath(CurrentPath)5761IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path which &5762&starts and ends on same margin")5763END IF5764CurrentPath => WorkPath5765END DO5766END IF57675768! crop crev path so it ends on edge node5769CurrentPath => CrevassePaths5770DO WHILE(ASSOCIATED(CurrentPath))57715772Found = .FALSE.5773! buffer for floating point errors5774IF(PRESENT(GridSize)) THEN5775err_buffer = GridSize/105776ELSE5777err_buffer = 0.0_dp5778END IF57795780DO i=1, CurrentPath % NumberOfNodes-15781IF(.NOT. Found(1)) THEN5782start=CurrentPath % NodeNumbers(i)5783startidx=i5784END IF5785IF(.NOT. Found(2)) THEN5786end=CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes+1-i)5787endidx=CurrentPath % NumberOfNodes+1-i5788END IF5789StartX = Mesh % Nodes % x(start)5790StartY = Mesh % Nodes % y(start)5791EndX = Mesh % Nodes % x(end)5792EndY = Mesh % Nodes % y(end)5793DO j=1, SIZE(EdgeX)5794IF((EdgeX(j) <= StartX+err_buffer .AND. EdgeX(j) >= StartX-err_buffer) .AND. &5795(EdgeY(j) <= StartY+err_buffer .AND. EdgeY(j) >= StartY-err_buffer)) Found(1) = .TRUE.5796IF((EdgeX(j) <= EndX+err_buffer .AND. EdgeX(j) >= EndX-err_buffer) .AND. &5797(EdgeY(j) <= EndY+err_buffer .AND. EdgeY(j) >= EndY-err_buffer )) Found(2) = .TRUE.5798END DO5799IF(ALL(Found)) EXIT5800END DO58015802! If crevasses does not intersect edgeline twice remove it5803IF(ANY(.NOT. Found)) THEN5804CALL WARN('RemoveInvalidCrevs', 'Crev does not intersect edge twice so removing')5805CurrentPath % Valid = .FALSE.5806END IF58075808ALLOCATE(DeleteElement(CurrentPath % NumberOfElements),&5809DeleteNode(CurrentPath % NumberOfNodes))5810DeleteElement = .FALSE.5811DeleteNode = .FALSE.58125813IF(startidx /= 1) THEN5814DeleteNode(1:startidx-1) = .TRUE.5815DeleteElement(1:startidx-1) = .TRUE.5816END IF5817IF(endidx /= CurrentPath % NumberOfNodes) THEN5818DeleteEndNodes = CurrentPath % NumberOfNodes - endidx5819DeleteNode(endidx+1:CurrentPath % NumberOfNodes) = .TRUE.5820DeleteElement(CurrentPath % NumberOfElements - DeleteEndNodes:CurrentPath % NumberOfElements) = .TRUE.5821END IF58225823!Delete them5824IF(COUNT(DeleteElement) > 0) THEN5825!elements5826ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))5827WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)58285829DEALLOCATE(CurrentPath % ElementNumbers)5830ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))58315832CurrentPath % ElementNumbers = WorkInt5833CurrentPath % NumberOfElements = SIZE(WorkInt)5834DEALLOCATE(WorkInt)58355836!nodes5837ALLOCATE(WorkInt(COUNT(.NOT. DeleteNode)))5838WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.DeleteNode)58395840DEALLOCATE(CurrentPath % NodeNumbers)5841ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))58425843CurrentPath % NodeNumbers = WorkInt5844CurrentPath % NumberOfNodes = SIZE(WorkInt)5845DEALLOCATE(WorkInt)5846END IF58475848DEALLOCATE(DeleteElement, DeleteNode)5849CurrentPath => CurrentPath % Next5850END DO58515852! actually remove path5853CurrentPath => CrevassePaths5854DO WHILE(ASSOCIATED(CurrentPath))5855WorkPath => CurrentPath % Next58565857IF(.NOT. CurrentPath % Valid) THEN5858IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath5859CALL RemoveCrevassePath(CurrentPath)5860IF(Debug) CALL Info("RemoveInvalidCrevs","Removing a crevasse path which doesn't end on the edge")5861END IF5862CurrentPath => WorkPath5863END DO58645865IF(RemoveInsideCrevs) THEN ! made optional as after validation the largest crev could already be removed etc...5866CALL GetCalvingPolygons(Mesh, CrevassePaths, EdgeX, EdgeY, Polygons, PolyStart, PolyEnd, GridSize)58675868! remove crevs found within other crevasses5869CurrentPath => CrevassePaths5870path=05871DO WHILE(ASSOCIATED(CurrentPath))5872path=path+15873inside = .FALSE.5874DO i=1, SIZE(PolyStart)5875IF(i==path) CYCLE5876ALLOCATE(PathPoly(2, PolyEnd(i)-PolyStart(i)+1))5877PathPoly = Polygons(:, PolyStart(i):PolyEnd(i))5878DO j=1, CurrentPath % NumberOfNodes5879xx = Mesh % Nodes % x(CurrentPath % NodeNumbers(j))5880yy = Mesh % Nodes % y(CurrentPath % NodeNumbers(j))5881DO k=1, SIZE(PathPoly(1,:))5882IF((xx+err_buffer >= PathPoly(1,k) .AND. xx-err_buffer <= PathPoly(1,k)) .AND. &5883(yy+err_buffer >= PathPoly(2,k) .AND. yy-err_buffer <= PathPoly(2,k))) THEN5884inside=.TRUE.5885EXIT5886END IF5887END DO5888END DO5889IF(inside) THEN5890! area 1 actual gives area*25891area1 = 0.0_dp5892xx = Polygons(1,PolyStart(path))5893yy = Polygons(2,PolyStart(path))5894DO j=PolyStart(path)+1, PolyEnd(path)5895area1 = area1 + (Polygons(1,j) * yy - Polygons(2,j) * xx)5896xx = Polygons(1,j)5897yy = Polygons(2,j)5898END DO5899area2 = 0.0_dp5900xx = Polygons(1,PolyStart(i))5901yy = Polygons(2,PolyStart(i))5902DO j=PolyStart(i)+1, PolyEnd(i)5903area2 = area2 + (Polygons(1,j) * yy - Polygons(2,j) * xx)5904xx = Polygons(1,j)5905yy = Polygons(2,j)5906END DO5907IF(ABS(area1) <= ABS(area2)) THEN ! remove this path if smaller5908CurrentPath % Valid = .FALSE.5909ELSE !remove second path5910SecondPath => CrevassePaths5911spath=05912DO WHILE(ASSOCIATED(SecondPath))5913spath=spath+15914IF(spath==i) SecondPath % Valid = .FALSE.5915SecondPath => SecondPath % Next5916END DO5917END IF5918END IF5919DEALLOCATE(PathPoly)5920IF(inside) EXIT5921END DO59225923CurrentPath => CurrentPath % Next5924END DO59255926!Actually remove previous marked5927CurrentPath => CrevassePaths5928DO WHILE(ASSOCIATED(CurrentPath))5929WorkPath => CurrentPath % Next59305931IF(.NOT. CurrentPath % Valid) THEN5932IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath5933CALL RemoveCrevassePath(CurrentPath)5934IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path")5935END IF5936CurrentPath => WorkPath5937END DO59385939DEALLOCATE(Polygons)5940END IF59415942END SUBROUTINE RemoveInvalidCrevs59435944SUBROUTINE GetFrontCorners(Model, Solver, FrontLeft, FrontRight)59455946TYPE(Model_t) :: Model5947TYPE(Solver_t) :: Solver5948!--------------------------5949TYPE(Mesh_t), POINTER :: Mesh5950TYPE(Solver_t), POINTER :: NullSolver => NULL(), AdvSolver5951TYPE(Valuelist_t), POINTER :: SolverParams, AdvParams5952INTEGER :: i,j,k, dummyint, LeftRoot, RightRoot, ierr, NNodes,RCounter, LCounter,&5953Nl,Nr, Naux, ok, RightTotal, LeftTotal, Nrail, CornersTotal, Counter, side5954REAL(KIND=dp) :: FrontLeft(2), FrontRight(2), buffer, xx, yy, mindist, tempdist5955INTEGER, POINTER :: FrontPerm(:)=>NULL(), TopPerm(:)=>NULL(), &5956LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), SidePerm(:)5957LOGICAL :: FoundRight, FoundLeft, reducecorners(2), Found5958LOGICAL, ALLOCATABLE :: PFoundRight(:), PFoundLeft(:), InFront(:), Duplicate(:)5959INTEGER, ALLOCATABLE :: PRightCount(:), PLeftCount(:), disps(:),&5960PCount(:), jmin(:), Corner(:)5961REAL(kind=dp), ALLOCATABLE :: xL(:),yL(:),xR(:),yR(:), xRail(:), yRail(:),&5962AllCorners(:), PAllCorners(:), MinDists(:)5963CHARACTER(LEN=MAX_NAME_LEN) :: FrontMaskName, TopMaskName, &5964LeftMaskName, RightMaskName, SolverName = "GetFrontCorners",&5965RightRailFName, LeftRailFName, Adv_EqName5966INTEGER, PARAMETER :: io=2059675968NNodes = Model % Mesh % NumberOfNodes5969Mesh => Model % Mesh5970SolverParams => Solver % Values59715972ALLOCATE(FrontPerm(NNodes), TopPerm(NNodes), LeftPerm(NNodes),&5973RightPerm(NNodes))5974FrontMaskName = "Calving Front Mask"5975TopMaskName = "Top Surface Mask"5976CALL MakePermUsingMask( Model, Solver, Mesh, TopMaskName, &5977.FALSE., TopPerm, dummyint)5978CALL MakePermUsingMask( Model, Solver, Mesh, FrontMaskName, &5979.FALSE., FrontPerm, dummyint)5980LeftMaskName = "Left Sidewall Mask"5981RightMaskName = "Right Sidewall Mask"5982!Generate perms to quickly get nodes on each boundary5983CALL MakePermUsingMask( Model, Solver, Mesh, LeftMaskName, &5984.FALSE., LeftPerm, dummyint)5985CALL MakePermUsingMask( Model, Solver, Mesh, RightMaskName, &5986.FALSE., RightPerm, dummyint)59875988FoundLeft=.FALSE.5989FoundRight=.FALSE.5990RCounter= 0; LCounter=05991DO i=1,NNodes5992IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN5993IF( LeftPerm(i) >0 ) THEN5994FrontLeft(1) = Mesh % Nodes % x(i)5995FrontLeft(2) = Mesh % Nodes % y(i)5996LCounter = LCounter + 15997FoundLeft = .TRUE.5998ELSE IF ( RightPerm(i) >0 ) THEN5999FrontRight(1) = Mesh % Nodes % x(i)6000FrontRight(2) = Mesh % Nodes % y(i)6001RCounter = RCounter + 16002FoundRight = .TRUE.6003END IF6004END IF6005END DO60066007ALLOCATE(PFoundRight(ParEnv % PEs), PFoundLeft(ParEnv % PEs))6008CALL MPI_ALLGATHER(FoundRight, 1, MPI_LOGICAL, PFoundRight, 1, &6009MPI_LOGICAL, ELMER_COMM_WORLD, ierr)6010CALL MPI_ALLGATHER(FoundLeft, 1, MPI_LOGICAL, PFoundLeft, 1, &6011MPI_LOGICAL, ELMER_COMM_WORLD, ierr)60126013DO i=1, ParEnv % PEs6014IF(.NOT. PFoundLeft(i) .AND. .NOT. PFoundRight(i)) CYCLE6015IF(PFoundLeft(i)) LeftRoot = i-16016IF(PFoundRight(i)) RightRoot = i-16017END DO60186019IF(ALL(.NOT. PFoundLeft)) CALL FATAL(SolverName, 'Unable to find left corner')6020IF(ALL(.NOT. PFoundRight)) CALL FATAL(SolverName, 'Unable to find right corner')60216022ALLOCATE(PRightCount(ParEnv % PEs), PLeftCount(ParEnv % PEs))6023CALL MPI_ALLGATHER(RCounter, 1, MPI_LOGICAL, PRightCount, 1, &6024MPI_LOGICAL, ELMER_COMM_WORLD, ierr)6025CALL MPI_ALLGATHER(LCounter, 1, MPI_LOGICAL, PLeftCount, 1, &6026MPI_LOGICAL, ELMER_COMM_WORLD, ierr)60276028RightTotal = SUM(PRightCount)6029LeftTotal = SUM(PLeftCount)60306031reducecorners=.TRUE.6032IF(LeftTotal == 1) THEN6033CALL MPI_BCAST(FrontLeft,2,MPI_DOUBLE_PRECISION, LeftRoot, ELMER_COMM_WORLD, ierr)6034reducecorners(1) = .FALSE.6035END IF60366037IF(RightTotal == 1) THEN6038CALL MPI_BCAST(FrontRight,2,MPI_DOUBLE_PRECISION, RightRoot, ELMER_COMM_WORLD, ierr)6039reducecorners(2) = .FALSE.6040END IF60416042IF(ANY(reducecorners)) THEN60436044Adv_EqName = ListGetString(SolverParams,"Front Advance Solver", DefValue="Front Advance")6045! Locate CalvingAdvance Solver6046Found = .FALSE.6047DO i=1,Model % NumberOfSolvers6048IF(GetString(Model % Solvers(i) % Values, 'Equation') == Adv_EqName) THEN6049AdvSolver => Model % Solvers(i)6050Found = .TRUE.6051EXIT6052END IF6053END DO6054IF(.NOT. Found) CALL FATAL(SolverName, "Advance Solver Equation not given")6055AdvParams => AdvSolver % Values60566057buffer = ListGetConstReal(AdvParams, "Rail Buffer", Found, DefValue=0.1_dp)6058IF(.NOT. Found) CALL Info(SolverName, "No Rail Buffer set using default 0.1")60596060LeftRailFName = ListGetString(AdvParams, "Left Rail File Name", Found)6061IF(.NOT. Found) THEN6062CALL Info(SolverName, "Left Rail File Name not found, assuming './LeftRail.xy'")6063LeftRailFName = "LeftRail.xy"6064END IF6065Nl = ListGetInteger(AdvParams, "Left Rail Number Nodes", Found)6066IF(.NOT.Found) THEN6067WRITE(Message,'(A,A)') 'Left Rail Number Nodes not found'6068CALL FATAL(SolverName, Message)6069END IF6070!TO DO only do these things if firsttime=true?6071OPEN(unit = io, file = TRIM(LeftRailFName), status = 'old',iostat = ok)6072IF (ok /= 0) THEN6073WRITE(message,'(A,A)') 'Unable to open file ',TRIM(LeftRailFName)6074CALL FATAL(Trim(SolverName),Trim(message))6075END IF6076ALLOCATE(xL(Nl), yL(Nl))60776078! read data6079DO i = 1, Nl6080READ(io,*,iostat = ok, end=200) xL(i), yL(i)6081END DO6082200 Naux = Nl - i6083IF (Naux > 0) THEN6084WRITE(Message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(LeftRailFName)6085CALL INFO(Trim(SolverName),Trim(message))6086END IF6087CLOSE(io)6088RightRailFName = ListGetString(AdvParams, "Right Rail File Name", Found)6089IF(.NOT. Found) THEN6090CALL Info(SolverName, "Right Rail File Name not found, assuming './RightRail.xy'")6091RightRailFName = "RightRail.xy"6092END IF60936094Nr = ListGetInteger(AdvParams, "Right Rail Number Nodes", Found)6095IF(.NOT.Found) THEN6096WRITE(Message,'(A,A)') 'Right Rail Number Nodes not found'6097CALL FATAL(SolverName, Message)6098END IF6099!TO DO only do these things if firsttime=true?6100OPEN(unit = io, file = TRIM(RightRailFName), status = 'old',iostat = ok)61016102IF (ok /= 0) THEN6103WRITE(Message,'(A,A)') 'Unable to open file ',TRIM(RightRailFName)6104CALL FATAL(Trim(SolverName),Trim(message))6105END IF6106ALLOCATE(xR(Nr), yR(Nr))61076108! read data6109DO i = 1, Nr6110READ(io,*,iostat = ok, end=100) xR(i), yR(i)6111END DO6112100 Naux = Nr - i6113IF (Naux > 0) THEN6114WRITE(message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(RightRailFName)6115CALL INFO(Trim(SolverName),Trim(message))6116END IF6117CLOSE(io)6118END IF61196120DO side=1,2 ! left 1, right 261216122IF(.NOT. reducecorners(side)) CYCLE61236124IF (side==1) THEN6125Nrail= Nl6126ALLOCATE(xRail(Nrail), yRail(Nrail), PCount(ParEnv % PEs))6127xRail = xL6128yRail = yL6129SidePerm => LeftPerm6130Counter = LCounter6131CornersTotal = LeftTotal6132PCount = PLeftCount6133ELSE6134Nrail= Nr6135ALLOCATE(xRail(Nrail), yRail(Nrail), PCount(ParEnv % PEs))6136xRail = xR6137yRail = yR ! TO DO use pointers instead?6138SidePerm => RightPerm6139Counter = RCounter6140CornersTotal = RightTotal6141PCount = PRightCount6142END IF61436144ALLOCATE(AllCorners(Counter*2))6145Counter = 06146DO i=1,NNodes6147IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN6148IF ( SidePerm(i) >0 ) THEN6149Counter = Counter + 16150AllCorners(Counter*2-1) = Mesh % Nodes % x(i)6151AllCorners(Counter*2) = Mesh % Nodes % y(i)6152END IF6153END IF6154END DO61556156ALLOCATE(disps(ParEnv % PEs))6157disps(1) = 06158DO i=2,ParEnv % PEs6159disps(i) = disps(i-1) + PCount(i-1)*26160END DO61616162ALLOCATE(PAllCorners(CornersTotal*2))6163CALL MPI_ALLGATHERV(AllCorners, Counter*2, MPI_DOUBLE_PRECISION, &6164PAllCorners, PCount*2, disps, MPI_DOUBLE_PRECISION, ELMER_COMM_WORLD, ierr)6165IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")61666167ALLOCATE(Duplicate(CornersTotal*2))6168Duplicate = .FALSE.6169DO i=1, CornersTotal6170IF(Duplicate(i*2)) CYCLE6171DO j=1, CornersTotal6172IF(i==j) CYCLE6173IF(PAllCorners(i*2-1) == PAllCorners(j*2-1) .AND. &6174PAllCorners(i*2) == PAllCorners(j*2)) Duplicate(j*2-1:j*2) = .TRUE.6175END DO6176END DO61776178DEALLOCATE(AllCorners)6179AllCorners = PACK(PAllCorners, .NOT. Duplicate)6180CornersTotal = INT(SIZE(AllCorners)/2)61816182ALLOCATE(jmin(CornersTotal),InFront(CornersTotal),MinDists(CornersTotal))6183DO i=1, CornersTotal61846185xx = AllCorners(i*2-1)6186yy = AllCorners(i*2)61876188MinDist=(xRail(1)-xRail(Nrail))**2.+(yRail(1)-yRail(Nrail))**2.6189! MinDist is actually maximum distance, needed for finding closest rail node6190DO j=1,Nrail ! Find closest point on rail6191TempDist=(xRail(j)-xx)**2.+(yRail(j)-yy)**2.6192IF(TempDist < MinDist) THEN6193MinDist=TempDist6194jmin(i)=j6195END IF6196END DO6197MinDists(i) = MinDist6198!check if in front or behind node6199InFront(i) = .TRUE.6200IF(jmin(i) == Nrail) InFront(i) = .FALSE.6201IF(jmin(i) > 1 .AND. jmin(i) /= Nrail) THEN6202MinDist = PointLineSegmDist2D((/xRail(jmin(i)),yRail(jmin(i))/), &6203(/xRail(jmin(i)+1),yRail(jmin(i)+1)/),(/xx,yy/))6204TempDist = PointLineSegmDist2D((/xRail(jmin(i)),yRail(jmin(i))/), &6205(/xRail(jmin(i)-1),yRail(jmin(i)-1)/),(/xx,yy/))6206IF(MinDist > TempDist) InFront(i) = .FALSE.6207END IF6208END DO62096210IF(COUNT(jmin == MAXVAL(jmin)) == 1) THEN6211Corner = MAXLOC(jmin)6212ELSE IF(COUNT(jmin == MAXVAL(jmin) .AND. InFront) == 1) THEN6213Corner = PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin) .AND. InFront)6214ELSE IF(ALL(InFront(PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin))))) THEN6215ALLOCATE(Corner(1))6216MinDist = HUGE(1.0_dp)6217DO i=1, CornersTotal6218IF(jmin(i) /= MAXVAL(jmin)) CYCLE6219IF(.NOT. InFront(i)) CYCLE6220IF(MinDists(i) < mindist) THEN6221mindist = MinDists(i)6222Corner(1) = i6223END IF6224END DO6225ELSE IF(ALL(.NOT. InFront(PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin))))) THEN6226ALLOCATE(Corner(1))6227MinDist = HUGE(1.0_dp)6228DO i=1, CornersTotal6229IF(jmin(i) /= MAXVAL(jmin)) CYCLE6230IF(MinDists(i) < mindist) THEN6231mindist = MinDists(i)6232Corner(1) = i6233END IF6234END DO6235ELSE6236CALL FATAL(SolverName, 'Problem reducing corners')6237END IF62386239IF(side == 1) THEN6240FrontLeft(1) = PAllCorners(Corner(1)*2-1)6241FrontLeft(2) = PAllCorners(Corner(1)*2)6242ELSE6243FrontRight(1) = PAllCorners(Corner(1)*2-1)6244FrontRight(2) = PAllCorners(Corner(1)*2)6245END IF62466247DEALLOCATE(xRail,yRail,AllCorners,disps,PAllCorners,jmin,InFront,Corner,MinDists,PCount,Duplicate)6248NULLIFY(SidePerm)6249END DO62506251DEALLOCATE(FrontPerm, TopPerm, LeftPerm, RightPerm)62526253END SUBROUTINE GetFrontCorners62546255SUBROUTINE ValidateNPCrevassePaths(Mesh, CrevassePaths, OnLeft, OnRight, FrontLeft, FrontRight, &6256EdgeX, EdgeY, LatCalvMargins, GridSize)6257IMPLICIT NONE6258TYPE(Mesh_t), POINTER :: Mesh6259TYPE(CrevassePath_t), POINTER :: CrevassePaths6260LOGICAL, ALLOCATABLE :: OnLeft(:),OnRight(:)6261LOGICAL :: LatCalvMargins6262REAL(KIND=dp) :: FrontRight(2), FrontLeft(2), EdgeX(:), EdgeY(:)6263REAL(KIND=dp), OPTIONAL :: GridSize6264INTEGER :: First, Last, LeftIdx, RightIdx6265!---------------------------------------------------6266REAL(KIND=dp) :: RotationMatrix(3,3), UnRotationMatrix(3,3), FrontDist, MaxDist, &6267ShiftTo, Dir1(2), Dir2(2), CCW_value,a1(2),a2(2),b1(2),b2(2),intersect(2), &6268StartX, StartY, EndX, EndY, Orientation(3), temp, NodeHolder(3), err_buffer,&6269yy, zz, gradient, c, intersect_z, SideCorner(3), MinDist, TempDist, IsBelowMean,&6270PolyMin, PolyMax6271REAL(KIND=dp), ALLOCATABLE :: ConstrictDirection(:,:), REdge(:,:), Polygons(:,:)6272REAL(KIND=dp), POINTER :: WorkReal(:)6273TYPE(CrevassePath_t), POINTER :: CurrentPath, OtherPath, WorkPath, LeftPath, RightPath6274TYPE(Element_t), POINTER :: WorkElements(:)6275TYPE(Nodes_t) :: WorkNodes6276INTEGER :: i,j,k,n,ElNo,ShiftToMe, NodeNums(2),A,B,FirstIndex, LastIndex,Start, path, &6277EdgeLength,crop(2),OnSide,SideCornerNum,addnodes,AddEdgeInt(2), CrevEndNode, Sideloops,&6278Counter, SideCornerOptions(4), LeftRight, ONNodes6279INTEGER, ALLOCATABLE :: WorkInt(:), IsBelow(:), PolyStart(:), PolyEnd(:)6280INTEGER, POINTER :: WorkPerm(:), NodeIndexes(:)6281LOGICAL :: Debug, Shifted, CCW, ToLeft, Snakey, OtherRight, ShiftRightPath, &6282DoProjectible, headland, CrevBelow, LeftToRight, AddLateralMargins, inside6283LOGICAL, ALLOCATABLE :: PathMoveNode(:), DeleteElement(:), BreakElement(:), &6284FarNode(:), DeleteNode(:), Constriction(:), InRange(:)6285CHARACTER(MAX_NAME_LEN) :: FuncName="ValidateNPCrevassePaths"6286REAL(kind=dp) :: rt0, rt62876288rt0 = RealTime()6289Debug = .FALSE.6290Snakey = .TRUE.62916292IF(PRESENT(GridSize)) THEN6293err_buffer = GridSize/1000.06294ELSE6295err_buffer = AEPS6296END IF6297IF( err_buffer < AEPS) err_buffer = AEPS62986299! if on lateral margin need to make sure that glacier corner is within crev.6300! if it lies outside the crev then the crev isn't really on front but on the lateral corner6301! first and last both actually on same lateral margin6302CALL GetCalvingPolygons(Mesh, CrevassePaths, EdgeX, EdgeY, Polygons, PolyStart, PolyEnd, GridSize)6303CurrentPath => CrevassePaths6304path=06305DO WHILE(ASSOCIATED(CurrentPath))6306path = path+16307First = CurrentPath % NodeNumbers(1)6308Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)6309IF(OnLeft(First) .OR. OnLeft(Last)) THEN6310inside = PointInPolygon2D(Polygons(:,PolyStart(path):PolyEnd(path)),FrontLeft)6311IF(.NOT. inside) THEN6312CurrentPath % Valid = .FALSE.6313CALL WARN(FuncName,'Left sidecorner not in crevasse so removing')6314END IF6315END IF6316IF(OnRight(First) .OR. OnRight(Last)) THEN6317inside = PointInPolygon2D(Polygons(:,PolyStart(path):PolyEnd(path)),FrontRight)6318IF(.NOT. inside) THEN6319CurrentPath % Valid = .FALSE.6320CALL WARN(FuncName,'Right sidecorner not in crevasse so removing')6321END IF6322END IF6323CurrentPath => CurrentPath % Next6324END DO63256326!Actually remove previous marked6327CurrentPath => CrevassePaths6328DO WHILE(ASSOCIATED(CurrentPath))6329WorkPath => CurrentPath % Next63306331IF(.NOT. CurrentPath % Valid) THEN6332IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath6333CALL RemoveCrevassePath(CurrentPath)6334IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path")6335END IF6336CurrentPath => WorkPath6337END DO63386339DEALLOCATE(Polygons, PolyStart, PolyEnd)6340CALL GetCalvingPolygons(Mesh, CrevassePaths, EdgeX, EdgeY, Polygons, PolyStart, PolyEnd, GridSize)6341! invalid lateral crevs must first be removed before this subroutine6342CurrentPath => CrevassePaths6343path=06344DO WHILE(ASSOCIATED(CurrentPath))6345path=path+16346First = CurrentPath % NodeNumbers(1)6347Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)6348StartX = Mesh % Nodes % x(First)6349StartY = Mesh % Nodes % y(First)6350EndX = Mesh % Nodes % x(Last)6351EndY = Mesh % Nodes % y(Last)6352! onside = 0, crev not on side6353! onside =1, first node on side leftright=1, on left6354! onside =2, second node on side leftright=2, on right6355! if on both sides corrected in loop6356Sideloops = 0; Onside = 0; LeftRight = 06357IF(OnLeft(First)) THEN6358StartX = FrontLeft(1)6359StartY = FrontLeft(2)6360Onside = 1; LeftRight = 16361Sideloops = Sideloops + 16362ELSE IF(OnRight(First)) THEN6363StartX = FrontRight(1)6364StartY = FrontRight(2)6365Onside = 1; LeftRight = 26366Sideloops = Sideloops + 16367END IF6368IF(OnLeft(Last)) THEN6369EndX = FrontLeft(1)6370EndY = FrontLeft(2)6371OnSide = 2; LeftRight = 16372Sideloops = Sideloops + 16373ELSE IF(OnRight(Last)) THEN6374EndX = FrontRight(1)6375EndY = FrontRight(2)6376Onside = 2; LeftRight = 26377Sideloops = Sideloops + 16378END IF63796380AddLateralMargins = .FALSE.6381IF(Onside /= 0 .AND. LatCalvMargins) AddLateralMargins = .TRUE.63826383orientation(3) = 0.0_dp6384IF( ABS(StartX-EndX) < err_buffer) THEN6385! front orientation is aligned with y-axis6386Orientation(2) = 0.0_dp6387IF(EndY > StartY) THEN6388Orientation(1)=1.0_dp6389ELSE6390Orientation(1)=-1.0_dp6391END IF6392ELSE IF (ABS(StartY-EndY)< err_buffer) THEN6393! front orientation is aligned with x-axis6394Orientation(1) = 0.0_dp6395IF(EndX > StartX) THEN6396Orientation(2)=1.0_dp6397ELSE6398Orientation(2)=-1.0_dp6399END IF6400ELSE6401CALL ComputePathExtent(CrevassePaths, Mesh % Nodes, .TRUE.)6402! endx always greater than startx6403! check if yextent min smaller than starty64046405PolyMin = MINVAL(Polygons(2,PolyStart(path):PolyEnd(path)))6406PolyMax = MAXVAL(Polygons(2,PolyStart(path):PolyEnd(path)))64076408IF(ABS(CurrentPath % Right - PolyMax) > &6409CurrentPath % Left - PolyMin) THEN6410Orientation(2)=-1.0_dp6411ELSE6412Orientation(2)=1.0_dp6413END IF6414Orientation(1)=Orientation(2)*(EndY-StartY)/(StartX-EndX)6415END IF6416Temp=(Orientation(1)**2+Orientation(2)**2+Orientation(3)**2)**0.56417Orientation=Orientation/Temp ! normalized the orientation64186419RotationMatrix = ComputeRotationMatrix(Orientation)6420UnRotationMatrix = TRANSPOSE(RotationMatrix)64216422!save crevasse orientation6423CurrentPath % Orientation = Orientation(1:2)64246425! Temporarily rotate the mesh6426CALL RotateMesh(Mesh, RotationMatrix)64276428! Find path %left, %right, %extent (width)6429CALL ComputePathExtent(CurrentPath, Mesh % Nodes, .TRUE.)64306431! rotate edgex and edgey6432EdgeLength = SIZE(EdgeX)6433ALLOCATE(REdge(3, EdgeLength))6434DO i=1,EdgeLength6435NodeHolder(1) = EdgeX(i)6436NodeHolder(2) = EdgeY(i)6437NodeHolder(3) = 0.0_dp64386439NodeHolder = MATMUL(RotationMatrix,NodeHolder)64406441REdge(1,i) = NodeHolder(1)6442REdge(2,i) = NodeHolder(2)6443REdge(3,i) = NodeHolder(3)6444END DO64456446! crop edge around crev ends6447crop=06448DO i=1, EdgeLength6449IF((REdge(2,i) <= Mesh % Nodes % y(First) + err_buffer .AND. &6450REdge(2,i) >= Mesh % Nodes % y(First) - err_buffer) .AND. &6451(REdge(3,i) <= Mesh % Nodes % z(First) + err_buffer .AND. &6452REdge(3,i) >= Mesh % Nodes % z(First) - err_buffer)) crop(1) = i6453IF((REdge(2,i) <= Mesh % Nodes % y(Last) + err_buffer .AND. &6454REdge(2,i) >= Mesh % Nodes % y(Last) - err_buffer) .AND. &6455(REdge(3,i) <= Mesh % Nodes % z(Last) + err_buffer .AND. &6456REdge(3,i) >= Mesh % Nodes % z(Last) - err_buffer )) crop(2) = i6457END DO6458IF(ANY(crop == 0)) CALL FATAL(FuncName, 'Edge not found')64596460! if onside we need to consider that constriction may occur as result of6461! narrowing on the fjord walls. Easiest way to do this is add the lateral edge nodes6462! to the crevasse permanently6463! GetFrontCorners only provides surface edges - is this a problem on a nonvertical front?6464! loop as crev may be on both lateral margins6465IF(AddLateralMargins) THEN6466ONNodes = Mesh % NumberOfNodes6467DO j=1,Sideloops6468!adjust onside and leftright6469!if on both side must be left(first) then right(last)6470IF(j==1 .AND. Sideloops==2) THEN6471LeftRight = 1; OnSide = 16472ELSE IF(j==2) THEN6473LeftRight = 2; OnSide = 26474END IF64756476! rotate side corner if it exists6477IF(LeftRight == 1) THEN6478NodeHolder(1) = FrontLeft(1)6479NodeHolder(2) = FrontLeft(2)6480NodeHolder(3) = 0.0_dp6481ELSE IF(LeftRight == 2) THEN6482NodeHolder(1) = FrontRight(1)6483NodeHolder(2) = FrontRight(2)6484NodeHolder(3) = 0.0_dp6485END IF64866487NodeHolder = MATMUL(RotationMatrix,NodeHolder)64886489SideCorner = NodeHolder64906491SideCornerNum = 06492SideCornerOptions = 06493MinDist = HUGE(1.0_dp)6494Counter = 06495DO i=1, EdgeLength6496TempDist = PointDist3D(REdge(:,i), SideCorner)6497IF(TempDist < MinDist) THEN6498MinDist = TempDist6499SideCornerNum = i6500END IF6501IF(TempDist < GridSize) THEN6502counter = counter + 16503IF(counter > 4) CALL FATAL(FuncName, 'More than 4 nodes closer than gridsize to side corner')6504SideCornerOptions(counter) = i6505END IF6506END DO65076508! this is for when the closest edgenode to the SideCorner is actually on6509! the front causing a constriction in crevasse. This moves it back onto the6510! lateral margin6511IF(counter == 2) THEN6512IF(LeftRight == 1) SideCornerNum = MINVAL(SideCornerOptions(1:2))6513IF(LeftRight == 2) SideCornerNum = MAXVAL(SideCornerOptions(1:2))6514END IF6515IF(counter == 3) SideCornerNum = SideCornerOptions(2)6516IF(counter == 4) THEN6517IF(LeftRight == 1) SideCornerNum = MINVAL(SideCornerOptions)6518IF(LeftRight == 2) SideCornerNum = MAXVAL(SideCornerOptions)6519END IF65206521IF(SideCornerNum==0) CALL FATAL(FuncName, 'Side Corner not found')6522IF(SideCornerNum > MAXVAL(crop) .OR. SideCornerNum < MINVAL(crop)) THEN6523CALL WARN(FuncName, 'Side Corner not in cropped edge range')6524! node must be in front of sidecorner which is only based off surface nodes6525EXIT6526END IF65276528! see which nodes we want to add6529IF(LeftRight == 1) THEN6530AddEdgeInt(1) = crop(OnSide) + 16531AddEdgeInt(2) = SideCornerNum6532ELSE IF(LeftRight == 2) THEN6533AddEdgeInt(1) = SideCornerNum6534AddEdgeInt(2) = crop(OnSide) - 16535END IF6536crop(Onside) = SideCornerNum6537addnodes = AddEdgeInt(2) - AddEdgeInt(1) + 165386539IF(addnodes < 0) CALL FATAL(FuncName, 'Problem adding lateral margins - addition is negative')65406541IF(Onside == 1) CrevEndNode=First6542IF(OnSide == 2) CrevEndNode=Last65436544! add elements to the mesh6545ALLOCATE(WorkElements(Mesh % NumberOfBulkElements + addnodes))6546WorkElements(1:Mesh % NumberOfBulkElements) = Mesh % Elements6547IF(Onside == 1) THEN6548DO i=1, addnodes6549WorkElements(Mesh % NumberOfBulkElements + i) % ElementIndex = Mesh % NumberOfBulkElements + i6550WorkElements(Mesh % NumberOfBulkElements + i) % TYPE => GetElementType(202)6551WorkElements(Mesh % NumberOfBulkElements + i) % BodyID = 16552CALL AllocateVector(WorkElements(Mesh % NumberOfBulkElements + i) % NodeIndexes, 2)6553NodeIndexes => WorkElements(Mesh % NumberOfBulkElements + i) % NodeIndexes6554IF(LeftRight == 1) THEN6555IF(i==1) THEN6556NodeIndexes(2) = CrevEndNode6557ELSE6558NodeIndexes(2) = Mesh % NumberOfNodes + i - 16559END IF6560NodeIndexes(1) = Mesh % NumberOfNodes + i6561ELSE IF(LeftRight == 2) THEN6562IF(i==1) THEN6563NodeIndexes(2) = CrevEndNode6564ELSE6565NodeIndexes(2) = Mesh % NumberOfNodes + addnodes - i + 26566END IF6567NodeIndexes(1) = Mesh % NumberOfNodes + addnodes - i + 16568END IF6569END DO6570ELSE IF(OnSide == 2) THEN6571DO i=1, addnodes6572WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % ElementIndex = &6573Mesh % NumberOfBulkElements - i + addnodes + 16574WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % TYPE => GetElementType(202)6575WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % BodyID = 16576CALL AllocateVector(WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % NodeIndexes, 2)6577NodeIndexes => WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % NodeIndexes6578IF(LeftRight == 1) THEN6579IF(i==1) THEN6580NodeIndexes(1) = CrevEndNode6581ELSE6582NodeIndexes(1) = Mesh % NumberOfNodes + i - 16583END IF6584NodeIndexes(2) = Mesh % NumberOfNodes + i6585ELSE IF(LeftRight == 2) THEN6586IF(i==1) THEN6587NodeIndexes(1) = CrevEndNode6588ELSE6589NodeIndexes(1) = Mesh % NumberOfNodes - i + addnodes + 26590END IF6591NodeIndexes(2) = Mesh % NumberOfNodes - i + addnodes + 16592END IF6593END DO6594END IF65956596! add nodes to mesh6597WorkNodes % NumberOfNodes = Mesh % NumberOfNodes + addnodes65986599ALLOCATE(WorkNodes % x(WorkNodes % NumberOfNodes),&6600WorkNodes % y(WorkNodes % NumberOfNodes),&6601WorkNodes % z(WorkNodes % NumberOfNodes))6602WorkNodes % x(1:Mesh % NumberOfNodes) = Mesh % Nodes % x6603WorkNodes % y(1:Mesh % NumberOfNodes) = Mesh % Nodes % y6604WorkNodes % z(1:Mesh % NumberOfNodes) = Mesh % Nodes % z6605DO i=1, addnodes6606WorkNodes % x(Mesh % NumberOfNodes + i) = REdge(1,AddEdgeInt(1)+i-1)6607WorkNodes % y(Mesh % NumberOfNodes + i) = REdge(2,AddEdgeInt(1)+i-1)6608WorkNodes % z(Mesh % NumberOfNodes + i) = REdge(3,AddEdgeInt(1)+i-1)6609END DO66106611IF(ASSOCIATED(Mesh % Elements)) DEALLOCATE(Mesh % Elements)6612Mesh % Elements => WorkElements6613DEALLOCATE(Mesh % Nodes % x, Mesh % Nodes % y, Mesh % Nodes % z)6614ALLOCATE(Mesh % Nodes % x(WorkNodes % NumberOfNodes), &6615Mesh % Nodes % y(WorkNodes % NumberOfNodes), &6616Mesh % Nodes % z(WorkNodes % NumberOfNodes))6617Mesh % NumberOfNodes = WorkNodes % NumberOfNodes6618Mesh % Nodes % NumberOfNodes = WorkNodes % NumberOfNodes6619Mesh % Nodes % x = WorkNodes % x6620Mesh % Nodes % y = WorkNodes % y6621Mesh % Nodes % z = WorkNodes % z6622Mesh % NumberOfBulkElements = SIZE(WorkElements)66236624NULLIFY(WorkElements) !nulify as mesh % elements points to this allocation6625DEALLOCATE(WorkNodes % x, WorkNodes % y, WorkNodes % z)66266627!modify crevasse6628ALLOCATE(WorkInt(CurrentPath % NumberOfNodes + addnodes))6629IF(OnSide == 1 .AND. LeftRight == 1) THEN ! add at start6630WorkInt(addnodes+1:CurrentPath % NumberOfNodes+addnodes) = CurrentPath % NodeNumbers6631DO i=1,addnodes6632WorkInt(i) = Mesh % NumberOfNodes - i + 1 !edge nodes added backwards6633END DO6634ELSE IF(OnSide == 1 .AND. LeftRight == 2) THEN ! add at start6635WorkInt(addnodes+1:CurrentPath % NumberOfNodes+addnodes) = CurrentPath % NodeNumbers6636DO i=1,addnodes6637WorkInt(i) = Mesh % NumberOfNodes - addnodes + i !edge nodes added forwards6638END DO6639ELSE IF(OnSide == 2 .AND. LeftRight == 1) THEN6640WorkInt(1:CurrentPath % NumberOfNodes) = CurrentPath % NodeNumbers6641DO i=1,addnodes6642WorkInt(CurrentPath % NumberOfNodes+ i) = Mesh % NumberOfNodes - addnodes + i !edge nodes added forwards6643END DO6644ELSE IF(OnSide == 2 .AND. LeftRight == 2) THEN6645WorkInt(1:CurrentPath % NumberOfNodes) = CurrentPath % NodeNumbers6646DO i=1,addnodes6647WorkInt(CurrentPath % NumberOfNodes+ i) = Mesh % NumberOfNodes-i+1 !edge nodes added backwards6648END DO6649END IF6650CurrentPath % NumberOfNodes = SIZE(WorkInt)6651DEALLOCATE(CurrentPath % NodeNumbers)6652ALLOCATE(Currentpath % NodeNumbers(CurrentPath % NumberOfNodes))6653CurrentPath % NodeNumbers = WorkInt6654DEALLOCATE(WorkInt)66556656! elements6657ALLOCATE(WorkInt(CurrentPath % NumberOfElements + addnodes))6658IF(OnSide == 1) THEN6659WorkInt(addnodes+1:CurrentPath % NumberOfElements+addnodes) = CurrentPath % ElementNumbers6660DO i=1,addnodes6661WorkInt(i) = Mesh % NumberOfBulkElements - i + 1 !new nodes always on end6662END DO6663ELSE IF(OnSide == 2) THEN6664WorkInt(1:CurrentPath % NumberOfElements) = CurrentPath % ElementNumbers6665DO i=1,addnodes6666WorkInt(CurrentPath % NumberOfElements+ i) = Mesh % NumberOfBulkElements - i + 1 !new nodes always on end6667END DO6668END IF6669CurrentPath % NumberOfElements = SIZE(WorkInt)6670DEALLOCATE(CurrentPath % ElementNumbers)6671ALLOCATE(Currentpath % ElementNumbers(CurrentPath % NumberOfElements))6672CurrentPath % ElementNumbers = WorkInt6673DEALLOCATE(WorkInt)6674END DO66756676! update first and last6677First = CurrentPath % NodeNumbers(1)6678Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)66796680! adjust mesh perm6681n = Mesh % NumberOfNodes6682ALLOCATE(WorkPerm(n), WorkReal(n))6683WorkReal = 0.0_dp6684WorkPerm = [(i,i=1,n)]6685CALL VariableRemove(Mesh % Variables, "isoline id", .TRUE.)6686CALL VariableAdd(Mesh % Variables, Mesh, NULL(), "isoline id", 1, WorkReal, WorkPerm)6687NULLIFY(WorkPerm, WorkReal) ! new variables points to these allocations6688END IF ! end onside66896690!-----------------------------------------------------6691! Paths should not 'snake' inwards in a narrow slit...6692!-----------------------------------------------------66936694!it's insufficient to require that no nodes be6695!further away than the two edge nodes.6696!Instead, must ensure that no nodes are further away than any6697!surrounding nodes.66986699!First need to determine path orientation6700!with respect to front....67016702!if ToLeft, the crevasse path goes from right to left, from the6703!perspective of someone sitting in the fjord, looking at the front6704ToLeft = Mesh % Nodes % y(Last) > Mesh % Nodes % y(First)67056706! since front no longer projectible we must now see if the crev is below or6707! above the front (edge of glacier)67086709! see if crev is above or below glacier edge6710ALLOCATE(IsBelow(CurrentPath % NumberOfNodes-2),&6711InRange(CurrentPath % NumberOfNodes-2))6712IsBelow = 06713InRange = .FALSE.6714DO i=2, CurrentPath % NumberOfNodes-16715yy = Mesh % Nodes % y(CurrentPath % NodeNumbers(i))6716zz = Mesh % Nodes % z(CurrentPath % NodeNumbers(i))6717DO j=MINVAL(crop), MAXVAL(crop)-16718IF((yy >= REdge(2,j) - err_buffer .AND. yy <= REdge(2,j+1) + err_buffer) .OR. &6719(yy <= REdge(2,j) + err_buffer .AND. yy >= REdge(2,j+1) - err_buffer)) THEN6720IF(REdge(2,j) - err_buffer <= REdge(2,j+1) .AND. &6721REdge(2,j) + err_buffer >= REdge(2,j+1)) CYCLE ! vertical6722IF(REdge(3,j) - err_buffer <= REdge(3,j+1) .AND. &6723REdge(3,j) + err_buffer >= REdge(3,j+1)) THEN ! horizontal6724intersect_z = REdge(3,j)6725ELSE6726gradient = (REdge(3,j)-REdge(3,j+1)) / (REdge(2,j)-REdge(2,j+1))6727c = REdge(3,j) - (gradient*REdge(2,j))6728intersect_z = gradient * yy + c6729END IF6730InRange(i-1) = .TRUE. ! found6731IF(zz - err_buffer <= intersect_z) THEN6732IF(zz + err_buffer >= intersect_z) THEN6733IsBelow(i-1) = 1 !in same position as edge6734ELSE6735IsBelow(i-1) = 2 ! below edge6736END IF6737EXIT6738END IF6739END IF6740END DO6741END DO67426743! if out of edge range remove6744IsBelow = PACK(IsBelow, InRange)67456746IF(SIZE(IsBelow) == 0) THEN6747! occurs when crev is on lateral margin and only had one node on front6748CurrentPath % Valid = .FALSE.6749CALL WARN(FuncName, 'No crev nodes in range of edge segment')6750END IF67516752IF(.NOT. CurrentPath % Valid) GOTO 10 ! skip constriction67536754IsBelowMean = SUM(IsBelow)/SIZE(IsBelow)6755IF(IsBelowMean >= 1) THEN6756CrevBelow = .TRUE.6757ELSE IF(IsBelowMean <= 1) THEN6758CrevBelow = .FALSE.6759ELSE6760CALL FATAL(FuncName, 'Some of the crevasse is below and some is above the glacier edge')6761END IF67626763! see if crev runs from its left to right6764IF(CrevBelow .AND. ToLeft) THEN6765LeftToRight = .TRUE.6766ELSE IF(.NOT. CrevBelow .AND. .NOT. ToLeft) THEN6767LeftToRight = .TRUE.6768ELSE6769LeftToRight = .FALSE.6770END IF67716772IF(Debug) PRINT*, 'LeftToRight: ', LeftToRight, CrevBelow, ToLeft67736774CurrentPath % LeftToRight = LeftToRight67756776IF(Debug) THEN6777FrontDist = NodeDist3D(Mesh % Nodes,First, Last)6778PRINT *,'PATH: ', CurrentPath % ID, ' FrontDist: ',FrontDist6779PRINT *,'PATH: ', CurrentPath % ID, &6780' nonodes: ',CurrentPath % NumberOfNodes,&6781' noelems: ',CurrentPath % NumberOfElements6782END IF67836784!Cycle path nodes, finding those which are too far away6785ALLOCATE(FarNode(CurrentPath % NumberOfNodes), &6786Constriction(CurrentPath % NumberOfNodes),&6787ConstrictDirection(CurrentPath % NumberOfNodes,2))6788FarNode = .FALSE.6789Constriction = .FALSE.6790ConstrictDirection = 0.0_dp67916792!Determine which nodes have the potential to be constriction (based on angle)6793!and compute constriction direction (i.e. which way the 'pointy bit' points...')6794DO i=2,CurrentPath % NumberOfNodes-16795First = CurrentPath % NodeNumbers(i-1)6796Last = CurrentPath % NodeNumbers(i+1)6797n = CurrentPath % NodeNumbers(i)67986799CCW_value = ((Mesh % Nodes % y(n) - Mesh % Nodes % y(First)) * &6800(Mesh % Nodes % z(Last) - Mesh % Nodes % z(First))) - &6801((Mesh % Nodes % z(n) - Mesh % Nodes % z(First)) * &6802(Mesh % Nodes % y(Last) - Mesh % Nodes % y(First)))68036804CCW = CCW_value > 0.0_dp68056806IF((CCW .NEQV. LeftToRight) .AND. (ABS(CCW_value) > 10*AEPS)) THEN6807Constriction(i) = .TRUE.6808!Calculate constriction direction68096810Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(First)6811Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(First)6812Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)68136814Dir2(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)6815Dir2(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)6816Dir2 = Dir2 / ((Dir2(1)**2.0 + Dir2(2)**2.0) ** 0.5)68176818ConstrictDirection(i,1) = Dir1(1) + Dir2(1)6819ConstrictDirection(i,2) = Dir1(2) + Dir2(2)6820! no point normalising just gives floating point errors?6821!ConstrictDirection(i,:) = ConstrictDirection(i,:) / &6822! ((ConstrictDirection(i,1)**2.0 + ConstrictDirection(i,2)**2.0) ** 0.5)68236824IF(Debug) PRINT *, 'Debug, node ',i,' dir1,2: ',Dir1, Dir26825IF(Debug) PRINT *, 'Debug, node ',i,' constriction direction: ',ConstrictDirection(i,:)6826IF(Debug) PRINT *, 'Debug, node ',i,' xyz: ',Mesh % Nodes % x(n),Mesh % Nodes % y(n),Mesh % Nodes % z(n)6827END IF6828END DO68296830!First and last can always be constriction6831Constriction(1) = .TRUE.6832Constriction(SIZE(Constriction)) = .TRUE.68336834!Compute constriction direction for first and last6835!We don't have info about the third node, so take orthogonal to 26836Last = CurrentPath % NodeNumbers(2)6837n = CurrentPath % NodeNumbers(1)6838Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)6839Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)6840Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)68416842!Depending on which end of the chain we are,6843!we take either the right or left orthogonal vector6844IF(LeftToRight) THEN6845ConstrictDirection(1,1) = Dir1(2)6846ConstrictDirection(1,2) = -1.0 * Dir1(1)6847ELSE6848ConstrictDirection(1,1) = -1.0 * Dir1(2)6849ConstrictDirection(1,2) = Dir1(1)6850END IF6851IF(Debug) PRINT *, 'Debug, node 1 constriction direction: ',ConstrictDirection(1,:)68526853Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes - 1)6854n = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)68556856Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)6857Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)6858Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)68596860IF(.NOT. LeftToRight) THEN6861ConstrictDirection(CurrentPath % NumberOfNodes,1) = Dir1(2)6862ConstrictDirection(CurrentPath % NumberOfNodes,2) = -1.0 * Dir1(1)6863ELSE6864ConstrictDirection(CurrentPath % NumberOfNodes,1) = -1.0 * Dir1(2)6865ConstrictDirection(CurrentPath % NumberOfNodes,2) = Dir1(1)6866END IF6867IF(Debug) PRINT *, 'Debug, node last constriction direction: ',&6868ConstrictDirection(CurrentPath % NumberOfNodes,:)68696870!---------------------------------------6871! Now that we have constrictions marked and directions computed, cycle nodes68726873DO i=1,CurrentPath % NumberOfNodes6874IF(.NOT. Constriction(i)) CYCLE68756876DO j=CurrentPath % NumberOfNodes,i+1,-16877IF(.NOT. Constriction(j)) CYCLE687868796880First = CurrentPath % NodeNumbers(i)6881Last = CurrentPath % NodeNumbers(j)68826883!Check that these constrictions 'face' each other via dot product6884Dir1(1) = Mesh % Nodes % y(Last) - Mesh % Nodes % y(First)6885Dir1(2) = Mesh % Nodes % z(Last) - Mesh % Nodes % z(First)6886Dir2(1) = -Dir1(1)6887Dir2(2) = -Dir1(2)68886889!If the two constrictions aren't roughly facing each other:6890! < > rather than > <6891! then skip this combo6892IF(SUM(ConstrictDirection(i,:)*Dir1) < 0.0000001_dp) THEN6893IF(Debug) PRINT *,'Constrictions ',i,j,' do not face each other 1: ',&6894SUM(ConstrictDirection(i,:)*Dir1)6895CYCLE6896END IF68976898IF(SUM(ConstrictDirection(j,:)*Dir2) < 0.0000001_dp) THEN6899IF(Debug) PRINT *,'Constrictions ',j,i,' do not face each other 2: ',&6900SUM(ConstrictDirection(j,:)*Dir2)6901CYCLE6902END IF69036904IF(Debug) PRINT *,'Constrictions ',i,j,' do face each other: ',&6905SUM(ConstrictDirection(i,:)*Dir1), SUM(ConstrictDirection(j,:)*Dir2)69066907!test that the line drawn between the constriction doesn't intersect6908!any intermediate elements as this indicates6909!crossing a headland (difficult to draw - but it's bad news)6910!6911! - --- ---- -6912! \/ \ / \/6913! ----6914!69156916a1(1) = Mesh % Nodes % y(First)6917a1(2) = Mesh % Nodes % z(First)6918a2(1) = Mesh % Nodes % y(Last)6919a2(2) = Mesh % Nodes % z(Last)6920headland = .FALSE.6921DO k=i+1,j-26922b1(1) = Mesh % Nodes % y(CurrentPath % NodeNumbers(k))6923b1(2) = Mesh % Nodes % z(CurrentPath % NodeNumbers(k))6924b2(1) = Mesh % Nodes % y(CurrentPath % NodeNumbers(k+1))6925b2(2) = Mesh % Nodes % z(CurrentPath % NodeNumbers(k+1))69266927CALL LineSegmentsIntersect(a1,a2,b1,b2,intersect,headland)6928IF(headland .AND. Debug) PRINT*, 'Headland intersect: ', 'a1', a1, &6929'a2', a2, 'b1', b1, 'b2', b26930IF(headland) EXIT6931END DO6932IF(headland) CYCLE69336934MaxDist = NodeDist3D(Mesh % Nodes,First, Last)69356936DO k=i+1,j-16937IF(FarNode(k)) CYCLE69386939n = CurrentPath % NodeNumbers(k)69406941IF((NodeDist3D(Mesh % Nodes, First, n) <= MaxDist) .AND. &6942(NodeDist3D(Mesh % Nodes, Last, n) <= MaxDist)) CYCLE !within range69436944FarNode(k) = .TRUE.6945IF(Debug) PRINT *,'Far node: ',k,' xyz: ',Mesh % Nodes % x(n),&6946Mesh % Nodes % y(n),Mesh % Nodes % z(n)69476948END DO6949END DO6950END DO69516952!Cycle elements, marking those which need to be adjusted6953ALLOCATE(BreakElement(CurrentPath % NumberOfElements),&6954DeleteElement(CurrentPath % NumberOfElements))6955BreakElement = .FALSE.6956DeleteElement = .FALSE.69576958DO i=1,CurrentPath % NumberOfElements6959IF(ANY(FarNode(i:i+1))) THEN6960IF(ALL(FarNode(i:i+1))) THEN6961DeleteElement(i) = .TRUE.6962IF(Debug) PRINT *,'PATH: ', CurrentPath % ID, ' element: ',i,' is deleted.'6963ELSE6964BreakElement(i) = .TRUE.6965IF(Debug) PRINT *,'PATH: ', CurrentPath % ID, ' element: ',i,' is broken.'6966END IF6967END IF6968END DO69696970DO i=1,CurrentPath % NumberOfElements6971IF((.NOT. BreakElement(i)) .OR. DeleteElement(i)) CYCLE6972!This element needs to be adjusted6973DO j=i+1,CurrentPath % NumberOfElements6974IF(.NOT. (BreakElement(j) .OR. DeleteElement(j))) &6975CALL Fatal("ValidateCrevasseGroups","Programming error in maintaining aspect ratio")6976IF(DeleteElement(j)) CYCLE6977!This is the next 'break element' after i6978!Determine which nodes we keep69796980IF((CurrentPath % NodeNumbers(j) /= &6981Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(1)) .OR. &6982(CurrentPath % NodeNumbers(j+1) /= &6983Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(2))) THEN69846985CALL Fatal("ValidateCrevassePaths", "Chain building error")6986END IF69876988Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(2) = &6989Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(2)69906991!We now want to delete it, because we only keep one from each broken pair6992DeleteElement(j) = .TRUE.6993EXIT !we paired this one, move on6994END DO6995END DO69966997!Delete the elements and nodes6998IF(COUNT(DeleteElement) > 0) THEN6999!elements7000ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))7001WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)70027003DEALLOCATE(CurrentPath % ElementNumbers)7004ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))70057006CurrentPath % ElementNumbers = WorkInt7007CurrentPath % NumberOfElements = SIZE(WorkInt)7008DEALLOCATE(WorkInt)70097010!nodes7011ALLOCATE(WorkInt(COUNT(.NOT. FarNode)))7012WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.FarNode)70137014DEALLOCATE(CurrentPath % NodeNumbers)7015ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))70167017CurrentPath % NodeNumbers = WorkInt7018CurrentPath % NumberOfNodes = SIZE(WorkInt)7019DEALLOCATE(WorkInt)7020END IF70217022DEALLOCATE(FarNode, Constriction, ConstrictDirection, BreakElement, DeleteElement)70237024! remove excess lateral nodes as this leads to errors in level set7025IF(AddLateralMargins) THEN7026IF(CurrentPath % NodeNumbers(1) <= ONNodes) THEN7027crop(1) = 17028ELSE7029DO i=1, CurrentPath % NumberOfNodes-17030IF(CurrentPath % NodeNumbers(i) > ONNodes .AND. &7031CurrentPath % NodeNumbers(i+1) <= ONNodes) THEN7032crop(1) = i7033EXIT7034END IF7035END DO7036END IF7037IF(CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes) <= ONNodes) THEN7038crop(2) = CurrentPath % NumberOfNodes7039ELSE7040DO i=CurrentPath % NumberOfNodes, 2, -17041IF(CurrentPath % NodeNumbers(i) > ONNodes .AND. &7042CurrentPath % NodeNumbers(i-1) <= ONNodes) THEN7043crop(2) = i7044EXIT7045END IF7046END DO7047END IF70487049ALLOCATE(DeleteNode(CurrentPath % NumberOfNodes),&7050DeleteElement(CurrentPath % NumberOfElements),&7051BreakElement(CurrentPath % NumberOfElements))7052DeleteNode = .TRUE.; DeleteElement = .FALSE.; BreakElement = .FALSE.7053DeleteNode(crop(1):crop(2)) = .FALSE.7054DO i=1,CurrentPath % NumberOfElements7055IF(ANY(DeleteNode(i:i+1))) THEN7056IF(ALL(DeleteNode(i:i+1))) THEN7057DeleteElement(i) = .TRUE.7058ELSE7059BreakElement(i) = .TRUE.7060END IF7061END IF7062END DO70637064IF(COUNT(BreakElement) > 1) THEN7065IF(Sideloops > 1) THEN7066IF(COUNT(BreakElement) > 3) CALL FATAL('ValidateNPCrevassePath', &7067'Assumption removing lateral margins does not break elements')7068ELSE7069IF(COUNT(BreakElement) > 2) CALL FATAL('ValidateNPCrevassePath', &7070'Assumption removing lateral margins does not break elements')7071IF(DeleteElement(1) .OR. DeleteElement(CurrentPath % NumberOfElements)) &7072CALL FATAL('ValidateNPCrevassePath', &7073'Assumption removing lateral margins does not break elements')7074END IF7075END IF70767077!Delete them7078IF(COUNT(DeleteElement) > 0) THEN7079!elements7080ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))7081WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)70827083DEALLOCATE(CurrentPath % ElementNumbers)7084ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))70857086CurrentPath % ElementNumbers = WorkInt7087CurrentPath % NumberOfElements = SIZE(WorkInt)7088DEALLOCATE(WorkInt)70897090!nodes7091ALLOCATE(WorkInt(COUNT(.NOT. DeleteNode)))7092WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.DeleteNode)70937094DEALLOCATE(CurrentPath % NodeNumbers)7095ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))70967097CurrentPath % NodeNumbers = WorkInt7098CurrentPath % NumberOfNodes = SIZE(WorkInt)7099DEALLOCATE(WorkInt)7100END IF7101DEALLOCATE(DeleteElement, DeleteNode, BreakElement)7102END IF7103710410 CONTINUE ! if crev was invalid need to rotate mesh back71057106! deallocations7107DEALLOCATE(REdge, IsBelow, InRange)7108!--------------------------------------------------------7109! Put the mesh back7110!--------------------------------------------------------7111CALL RotateMesh(Mesh, UnRotationMatrix)7112CurrentPath => CurrentPath % Next7113END DO71147115!Actually remove previous marked7116CurrentPath => CrevassePaths7117DO WHILE(ASSOCIATED(CurrentPath))7118WorkPath => CurrentPath % Next7119path=path+171207121IF(.NOT. CurrentPath % Valid) THEN7122IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath7123CALL RemoveCrevassePath(CurrentPath)7124IF(Debug) CALL Info("ValidateNPCrevassePaths","Removing a crevasse path")7125END IF7126CurrentPath => WorkPath7127END DO71287129rt = RealTime() - rt07130PRINT*, 'time', rt71317132END SUBROUTINE ValidateNPCrevassePaths71337134SUBROUTINE CheckMeshQuality(Mesh)71357136TYPE(Mesh_t), POINTER :: Mesh7137TYPE(Nodes_t) :: ElementNodes7138TYPE(Element_t),POINTER :: Element, Parent7139REAL(KIND=dp) :: U,V,W,detJ,Basis(10), mean7140INTEGER, POINTER :: NodeIndexes(:)7141INTEGER :: i,j,n,l,k, count7142INTEGER, ALLOCATABLE :: counters(:)7143LOGICAL :: stat,Debug7144CHARACTER(LEN=MAX_NAME_LEN) :: FuncName="CheckMeshQuality"71457146Debug = .FALSE.7147n = Mesh % MaxElementNodes7148ALLOCATE(ElementNodes % x(n),&7149ElementNodes % y(n),&7150ElementNodes % z(n))71517152!Some debug stats on the number of elements in each body/boundary7153IF(Debug) THEN7154ALLOCATE(counters(-2:10))71557156!Some stats7157counters = 07158DO i=1,Mesh % NumberOfBulkElements7159n = Mesh % Elements(i) % BodyID7160counters(n) = counters(n) + 17161END DO71627163DO i=-2,107164PRINT *,ParEnv % MyPE,' body body id: ',i,' count: ',counters(i),' of ',&7165Mesh % NumberOfBulkElements7166END DO716771687169counters = 07170DO i=Mesh % NumberOfBulkElements + 1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements7171n = Mesh % Elements(i) % BodyID7172IF(n <= 10 .AND. n > -3) THEN7173counters(n) = counters(n) + 17174ELSE7175PRINT *,ParEnv % MyPE,' unexpected BC body id: ',n,i7176END IF7177END DO71787179DO i=0,47180PRINT *,ParEnv % MyPE,' BC body id: ',i,' count: ',counters(i),' of ',&7181Mesh % NumberOfBoundaryElements, REAL(counters(i))/REAL(Mesh % NumberOfBoundaryElements)7182END DO71837184counters = 07185DO i=Mesh % NumberOfBulkElements+1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements7186n = Mesh % Elements(i) % BoundaryInfo % Constraint7187IF(n <= 10 .AND. n > -3) THEN7188counters(n) = counters(n) + 17189ELSE7190PRINT *,ParEnv % MyPE,' unexpected constraint: ',n,i7191END IF7192END DO71937194DO i=0,67195PRINT *,ParEnv % MyPE,' BC constraint: ',i,' count: ',counters(i),' of ',Mesh % NumberOfBoundaryElements,&7196REAL(counters(i))/REAL(Mesh % NumberOfBoundaryElements)7197END DO71987199counters = 07200DO i=Mesh % NumberOfBulkElements+1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements7201n = Mesh % Elements(i) % BoundaryInfo % OutBody7202IF(n <= 10 .AND. n > -3) THEN7203counters(n) = counters(n) + 17204ELSE7205PRINT *,ParEnv % MyPE,' unexpected outbody: ',n,i7206END IF7207END DO72087209DO i=-2,107210PRINT *,ParEnv % MyPE,' outbody: ',i,' count: ',counters(i),' of ',Mesh % NumberOfBoundaryElements7211END DO7212END IF72137214!Check all BC elements have parents7215DO i=Mesh % NumberOfBulkElements+1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements7216Element => Mesh % Elements(i)7217Parent => Element % BoundaryInfo % Left7218IF( .NOT. ASSOCIATED(Parent) ) THEN7219Parent => Element % BoundaryInfo % Right7220END IF7221IF( .NOT. ASSOCIATED( Parent ) ) THEN7222PRINT *,ParEnv % MyPE,i,' BC element without parent! constraint: ',Element % BoundaryInfo % constraint, &7223' body id: ',Element % BodyID,' nodes: ',Element % NodeIndexes,&7224' global: ',Mesh % ParallelInfo % GlobalDOFs(Element%NodeIndexes)7225CALL Fatal(FuncName, "BC Element without parent!")7226END IF7227END DO72287229!check for duplicate element & node indices (locally only)7230DO i=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements7231IF(Mesh % Elements(i) % GElementIndex <= 0) CALL Fatal(FuncName, 'Element has ID 0')7232DO j=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements7233IF(i==j) CYCLE7234IF(Mesh % Elements(i) % GElementIndex == Mesh % Elements(j) % GElementIndex) THEN7235PRINT *,ParEnv % MyPE,' elements ',i,j,' have same GElementIndex: ',&7236Mesh % Elements(j) % GElementIndex7237CALL Fatal(FuncName, "Duplicate GElementIndexes!")7238END IF7239END DO7240END DO72417242DO i=1,Mesh % NumberOfNodes7243IF(Mesh % ParallelInfo % GlobalDOFs(i) <= 0) THEN7244PRINT*, ParEnv % MyPE, 'Node ', i, 'Has no GlobalID'7245CALL Fatal(FuncName, 'Node has ID 0')7246END IF7247DO j=1,Mesh % NumberOfNodes7248IF(i==j) CYCLE7249IF(Mesh % ParallelInfo % GlobalDOFs(i) == Mesh % ParallelInfo % GlobalDOFs(j)) THEN7250PRINT *,ParEnv % MyPE,' nodes ',i,j,' have same GlobalDOF: ',&7251Mesh % ParallelInfo % GlobalDOFs(j)7252CALL Fatal(FuncName, "Duplicate GlobalDOFs!")7253END IF7254END DO7255END DO72567257!Check element detj etc7258DO j=1,27259IF(j==1) mean = 0.07260DO i=1,Mesh % NumberOfBulkElements7261Element => Mesh % Elements(i)7262n = Element % TYPE % NumberOfNodes7263NodeIndexes => Element % NodeIndexes72647265!Check element for duplicate node indexes7266DO k=1,n7267DO l=1,n7268IF(l==k) CYCLE7269IF(NodeIndexes(k) == NodeIndexes(l)) THEN7270WRITE(Message, '(A,i0,A)') "Mesh Element ",i," has duplicate node indexes!"7271CALL Fatal(FuncName,Message)7272END IF7273END DO7274END DO72757276ElementNodes % x(1:n) = Mesh % Nodes % x(NodeIndexes(1:n))7277ElementNodes % y(1:n) = Mesh % Nodes % y(NodeIndexes(1:n))7278ElementNodes % z(1:n) = Mesh % Nodes % z(NodeIndexes(1:n))72797280stat = ElementInfo( Element,ElementNodes,U,V,W,detJ, &7281Basis )7282!Check detj - warn if deviates from mean, fatal if <= 07283IF(j==2) THEN7284IF(detj <= 0.0) THEN7285WRITE(Message, '(A,i0,A)') "Element ",j," has detj <= 0"7286CALL Fatal(FuncName, Message)7287ELSE IF(detj < mean/10.0 .OR. detj > mean*10.0) THEN7288WRITE(Message, '(i0,A,i0,A,F10.2,A,F10.2,A)') ParEnv % MyPE,' element ',&7289i,' detj (',detj,') deviates from mean (',mean,')'7290IF(Debug) CALL Warn(FuncName, Message)7291END IF7292ELSE7293mean = mean + detj7294END IF7295END DO7296IF(j==1) mean = mean / Mesh % NumberOfBulkElements7297END DO72987299DEALLOCATE(ElementNodes % x,&7300ElementNodes % y,&7301ElementNodes % z)73027303END SUBROUTINE CheckMeshQuality73047305!Takes a mesh with GroundedMask defined on the base, and7306!ensures that grounded nodes remain grounded7307!i.e. sets z = min zs bottom wherever GroundedMask>-0.57308SUBROUTINE EnforceGroundedMask(Model, Mesh)7309TYPE(Model_t) :: Model7310TYPE(Mesh_t), POINTER :: Mesh7311!-------------------------7312TYPE(Solver_t), POINTER :: NullSolver => NULL()7313TYPE(ValueList_t), POINTER :: Material7314TYPE(Variable_t), POINTER :: GMaskVar7315TYPE(Element_t), POINTER :: Element7316REAL(KIND=dp), POINTER :: GMask(:)7317REAL(KIND=dp) :: zb, xydist, zdist7318INTEGER :: i,j,k,n,BaseBCtag,FrontBCtag, dummyint, counter, NoNeighbours, ierr7319INTEGER, POINTER :: GMaskPerm(:), FrontPerm(:)=>NULL()7320INTEGER, ALLOCATABLE :: GDOFs(:), PartNoGDOFs(:), PartGDOFs(:), disps(:)7321LOGICAL :: ConstraintChanged, ThisBC, Found, HasNeighbours7322CHARACTER(MAX_NAME_LEN) :: FuncName="EnforceGroundedMask", GMaskVarName73237324GMaskVarName = "GroundedMask"7325GMaskVar => VariableGet(Mesh % Variables, GMaskVarName, .TRUE.)7326IF(.NOT.ASSOCIATED(GMaskVar)) THEN7327CALL Info(FuncName, "Didn't find GroundedMask, so not enforcing bed height",Level=5)7328RETURN7329END IF73307331CALL MakePermUsingMask( Model, NullSolver, Mesh, "Calving Front Mask", &7332.FALSE., FrontPerm, dummyint)73337334Material => GetMaterial(Mesh % Elements(1)) !TODO, this is not generalised73357336GMask => GMaskVar % Values7337GMaskPerm => GMaskVar % Perm73387339DO i=1,Model % NumberOfBCs7340ThisBC = ListGetLogical(Model % BCs(i) % Values,"Bottom Surface Mask",Found)7341IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE7342BaseBCtag = Model % BCs(i) % Tag7343EXIT7344END DO73457346DO i=1,Model % NumberOfBCs7347ThisBC = ListGetLogical(Model % BCs(i) % Values,"Calving Front Mask",Found)7348IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE7349FrontBCtag = Model % BCs(i) % Tag7350EXIT7351END DO73527353ALLOCATE(GDOFs(Mesh % NumberOfNodes))7354counter=07355DO i=1,Mesh % NumberOfNodes7356IF(GMaskPerm(i) == 0) CYCLE7357zb = ListGetRealAtNode(Material, "Min Zs Bottom",i,UnfoundFatal=.TRUE.)73587359NoNeighbours = SIZE(Mesh % ParallelInfo % &7360NeighbourList(i) % Neighbours) - 17361HasNeighbours = NoNeighbours > 073627363!Floating -> check no penetration7364!Grounded -> set to bedrock height7365IF(GMask(GMaskPerm(i)) < -0.5) THEN7366IF(Mesh % Nodes % z(i) < zb) THEN7367Mesh % Nodes % z(i) = zb7368IF(HasNeighbours) THEN7369counter = counter+17370GDOFs(counter) = Mesh % ParallelInfo % GlobalDOFs(i)7371END IF7372END IF7373ELSE7374IF(HasNeighbours) THEN7375counter = counter+17376GDOFs(counter) = Mesh % ParallelInfo % GlobalDOFs(i)7377END IF7378Mesh % Nodes % z(i) = zb73797380!check element how much this deforms elements near front7381!if the element is above a 45 degree vertical angle from xy plane change to front boundary7382DO j=Mesh % NumberOfBulkElements +1, &7383Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements73847385Element => Mesh % Elements(j)7386IF(Element % BoundaryInfo % Constraint /= BaseBCtag) CYCLE7387n = Element % TYPE % NumberOfNodes73887389!Doesn't contain our point7390IF(.NOT. ANY(Element % NodeIndexes(1:n)==i)) CYCLE73917392ConstraintChanged = .FALSE.73937394DO k=1,n7395IF(ConstraintChanged) CYCLE7396IF(Element % NodeIndexes(k) == i) CYCLE ! this node7397IF(GMask(GMaskPerm(Element % NodeIndexes(k))) >= -0.5) CYCLE ! grounded7398IF(FrontPerm(Element % NodeIndexes(k)) == 0) CYCLE ! new node not on front73997400xydist = NodeDist2D(Mesh % Nodes, i, Element % NodeIndexes(k))7401zdist = ABS(Mesh % Nodes % z(i) - Mesh % Nodes % z(Element % NodeIndexes(k)))74027403IF(zdist > xydist) THEN7404CALL WARN(FuncName, "Transferring boundary element to front as it vertically &7405angled after GroundedMask has been enforced")7406PRINT*, 'For node', i, 'x:', Mesh % Nodes % x(i), 'y:', Mesh % Nodes % y(i),&7407'z:', Mesh % Nodes % z(i)74087409Element % BoundaryInfo % Constraint = FrontBCtag7410ConstraintChanged = .TRUE.7411END IF7412END DO74137414IF(ConstraintChanged) THEN7415FrontPerm(Element % NodeIndexes) = 17416END IF7417END DO7418END IF7419END DO74207421! sometimes if a shared node is on a partition without a bsae boundary element then7422! GMaskPerm will be zero on this partition but be above zero ot other partitions7423! therefore we need to share any gdofs that have been moved to ensure they are movoed7424! on all partitions so coords are consistent74257426ALLOCATE(PartNoGDOFs(ParEnv % PEs))7427CALL MPI_ALLGATHER(counter, 1, MPI_INTEGER, &7428PartNoGDOFs, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)74297430ALLOCATE(disps(ParEnv % PEs))7431disps(1) = 07432DO i=2,ParEnv % PEs7433disps(i) = disps(i-1) + PartNoGDOFs(i-1)7434END DO74357436ALLOCATE(PartGDOFs(SUM(PartNoGDOFs)))7437CALL MPI_AllGatherV(GDOFs(:counter), counter, MPI_INTEGER, &7438PartGDOFs, PartNoGDOFs, disps, MPI_INTEGER, ELMER_COMM_WORLD, ierr)74397440DO i=1, Mesh % NumberOfNodes7441IF(ANY(PartGDOFs == Mesh % ParallelInfo % GlobalDOFs(i))) THEN7442zb = ListGetRealAtNode(Material, "Min Zs Bottom",i,UnfoundFatal=.TRUE.)7443Mesh % Nodes % z(i) = zb7444END IF7445END DO74467447DEALLOCATE(FrontPerm)74487449END SUBROUTINE EnforceGroundedMask74507451SUBROUTINE ResetMeshUpdate(Model, Solver)7452USE MeshUtils74537454TYPE(Model_t) :: Model7455TYPE(Solver_t) :: Solver7456! --------------------7457TYPE(Variable_t), POINTER :: Var, RefVar7458TYPE(ValueList_t), POINTER :: Params7459INTEGER :: i, Num7460LOGICAL :: Found7461CHARACTER(MAX_NAME_LEN) :: SolverName, VarName7462SolverName = 'ResetMeshUpdate'74637464Params => Solver % Values74657466DO Num = 1,9997467WRITE(Message,'(A,I0)') 'Mesh Update Variable ',Num7468VarName = ListGetString( Params, Message, Found)7469IF( .NOT. Found) EXIT74707471Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )7472IF(.NOT. ASSOCIATED(Var)) THEN7473WRITE(Message,'(A,A)') "Listed mesh update variable but can not find: ",VarName7474CALL Fatal(SolverName, Message)7475END IF7476Var % Values = 0.0_dp7477END DO74787479!Turn off free surface solvers for next timestep7480!And set values equal to z (or rotated) coordinate7481DO Num = 1,9997482WRITE(Message,'(A,I0)') 'FreeSurface Variable ',Num7483VarName = ListGetString( Params, Message, Found)7484IF( .NOT. Found) EXIT74857486Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )7487IF(.NOT. ASSOCIATED(Var)) THEN7488WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",VarName7489CALL Fatal(SolverName, Message)7490END IF74917492RefVar => VariableGet( Model % Mesh % Variables, "Reference "//TRIM(VarName), .TRUE. )7493IF(.NOT. ASSOCIATED(RefVar)) THEN7494WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",&7495"Reference "//TRIM(VarName)7496CALL Fatal(SolverName, Message)7497END IF74987499DO i=1,Model % Mesh % NumberOfNodes7500IF(Var % Perm(i) <= 0) CYCLE7501Var % Values(Var % Perm(i)) = Model % Mesh % Nodes % z(i)7502RefVar % Values(RefVar % Perm(i)) = Model % Mesh % Nodes % z(i)7503END DO7504END DO75057506END SUBROUTINE ResetMeshUpdate75077508SUBROUTINE ReleaseCrevassePaths(CrevassePaths)7509TYPE(CrevassePath_t), POINTER :: CrevassePaths,CurrentPath75107511CurrentPath => CrevassePaths7512DO WHILE(ASSOCIATED(CurrentPath))7513IF(ASSOCIATED(CurrentPath % NodeNumbers)) THEN7514DEALLOCATE(CurrentPath % NodeNumbers)7515CurrentPath % NodeNumbers => NULL()7516END IF7517IF(ASSOCIATED(CurrentPath % ElementNumbers)) THEN7518DEALLOCATE(CurrentPath % ElementNumbers)7519CurrentPath % ElementNumbers => NULL()7520END IF75217522CurrentPath => CurrentPath % Next7523END DO75247525DEALLOCATE(CrevassePaths)75267527END SUBROUTINE ReleaseCrevassePaths75287529SUBROUTINE EnforceLateralMargins(Model, SolverParams)7530IMPLICIT NONE7531TYPE(Model_t) :: Model7532TYPE(Valuelist_t), POINTER :: SolverParams7533!-----------------------------------------7534TYPE(Solver_t), POINTER :: AdvSolver7535TYPE(Valuelist_t), POINTER :: AdvParams7536TYPE(Element_t), POINTER :: Element7537TYPE(Mesh_t), POINTER :: Mesh7538CHARACTER(MAX_NAME_LEN) :: FuncName, Adv_EqName, LeftRailFName, RightRailFName, &7539FrontMaskName,LeftMaskName, RightMaskName7540INTEGER, POINTER :: FrontPerm(:)=>NULL(), LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), &7541NodeIndexes(:)7542LOGICAL :: Found, inside, GotNode, ClosestRight7543LOGICAL, ALLOCATABLE :: UsedNode(:)7544INTEGER :: i,j,k,m,Nl,Nr, Naux, ok, DummyInt, ClosestRail, ClosestNode, counter, node(1), &7545closest, DuplicateNode, ierr7546REAL(kind=dp), ALLOCATABLE :: xL(:),yL(:),xR(:),yR(:), RailPoly(:,:)7547REAL(kind=dp) :: xx,yy, mindist, tempdist, minx, maxx, miny, maxy, &7548triangle(4,2,3), area(4), poly(2,4), closestpoint(2), buffer7549INTEGER, PARAMETER :: io=2075507551FuncName = "EnforceLateralMargins"75527553Mesh => Model % Mesh75547555Adv_EqName = ListGetString(SolverParams,"Front Advance Solver", DefValue="Front Advance")7556! Locate CalvingAdvance Solver7557Found = .FALSE.7558DO i=1,Model % NumberOfSolvers7559IF(GetString(Model % Solvers(i) % Values, 'Equation') == Adv_EqName) THEN7560AdvSolver => Model % Solvers(i)7561Found = .TRUE.7562EXIT7563END IF7564END DO7565IF(.NOT. Found) CALL FATAL(FuncName, "'Front Advance Solver' not given")7566AdvParams => AdvSolver % Values75677568buffer = ListGetConstReal(AdvParams, "Rail Buffer", Found, DefValue=0.1_dp)7569IF(.NOT. Found) CALL Info(FuncName, "No Rail Buffer set using default 0.1")75707571LeftRailFName = ListGetString(AdvParams, "Left Rail File Name", Found)7572IF(.NOT. Found) THEN7573CALL Info(FuncName, "Left Rail File Name not found, assuming './LeftRail.xy'")7574LeftRailFName = "LeftRail.xy"7575END IF7576Nl = ListGetInteger(AdvParams, "Left Rail Number Nodes", Found)7577IF(.NOT.Found) THEN7578WRITE(Message,'(A,A)') 'Left Rail Number Nodes not found'7579CALL FATAL(FuncName, Message)7580END IF7581!TO DO only do these things if firsttime=true?7582OPEN(unit = io, file = TRIM(LeftRailFName), status = 'old',iostat = ok)7583IF (ok /= 0) THEN7584WRITE(message,'(A,A)') 'Unable to open file ',TRIM(LeftRailFName)7585CALL FATAL(Trim(FuncName),Trim(message))7586END IF7587ALLOCATE(xL(Nl), yL(Nl))75887589! read data7590DO i = 1, Nl7591READ(io,*,iostat = ok, end=200) xL(i), yL(i)7592END DO7593200 Naux = Nl - i7594IF (Naux > 0) THEN7595WRITE(Message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(LeftRailFName)7596CALL INFO(Trim(FuncName),Trim(message))7597END IF7598CLOSE(io)7599RightRailFName = ListGetString(AdvParams, "Right Rail File Name", Found)7600IF(.NOT. Found) THEN7601CALL Info(FuncName, "Right Rail File Name not found, assuming './RightRail.xy'")7602RightRailFName = "RightRail.xy"7603END IF76047605Nr = ListGetInteger(AdvParams, "Right Rail Number Nodes", Found)7606IF(.NOT.Found) THEN7607WRITE(Message,'(A,A)') 'Right Rail Number Nodes not found'7608CALL FATAL(FuncName, Message)7609END IF7610!TO DO only do these things if firsttime=true?7611OPEN(unit = io, file = TRIM(RightRailFName), status = 'old',iostat = ok)76127613IF (ok /= 0) THEN7614WRITE(Message,'(A,A)') 'Unable to open file ',TRIM(RightRailFName)7615CALL FATAL(Trim(FuncName),Trim(message))7616END IF7617ALLOCATE(xR(Nr), yR(Nr))76187619! read data7620DO i = 1, Nr7621READ(io,*,iostat = ok, end=100) xR(i), yR(i)7622END DO7623100 Naux = Nr - i7624IF (Naux > 0) THEN7625WRITE(message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(RightRailFName)7626CALL INFO(Trim(FuncName),Trim(message))7627END IF7628CLOSE(io)76297630ALLOCATE(RailPoly(2, Nl+Nr+1))7631RailPoly(1,1:Nl) = xL7632RailPoly(2,1:Nl) = yL7633counter=07634DO i=Nr, 1, -17635counter=counter+17636RailPoly(1,Nl+counter) = xR(i)7637RailPoly(2,Nl+counter) = yR(i)7638END DO7639RailPoly(1,Nl+Nr+1) = xL(1)7640RailPoly(2,Nl+Nr+1) = yL(1)76417642LeftMaskName = "Left Sidewall Mask"7643RightMaskName = "Right Sidewall Mask"7644FrontMaskName = "Calving Front Mask"76457646!Generate perms to quickly get nodes on each boundary7647CALL MakePermUsingMask( Model, AdvSolver, Mesh, LeftMaskName, &7648.FALSE., LeftPerm, dummyint)7649CALL MakePermUsingMask( Model, AdvSolver, Mesh, RightMaskName, &7650.FALSE., RightPerm, dummyint)7651CALL MakePermUsingMask( Model, AdvSolver, Mesh, FrontMaskName, &7652.FALSE., FrontPerm, dummyint)76537654DO i=1, Mesh % NumberOfNodes7655IF(.NOT. (LeftPerm(i) > 0 .OR. RightPerm(i) > 0 .OR. FrontPerm(i) > 0)) CYCLE7656xx = Mesh % Nodes % x(i)7657yy = Mesh % Nodes % y(i)76587659IF(LeftPerm(i) > 0) THEN ! check if on left side7660mindist = HUGE(1.0_dp)7661DO j=1, Nl-17662tempdist = PointLineSegmDist2D((/xL(j), yL(j)/),(/xL(j+1), yL(j+1)/), (/xx, yy/))7663IF(tempdist < mindist) THEN7664closest = j7665mindist = tempdist7666END IF7667END DO76687669IF(mindist > buffer) THEN7670closestpoint = ClosestPointOfLineSegment((/xL(closest), yL(closest)/),(/xL(closest+1), yL(closest+1)/), (/xx, yy/))7671Mesh % Nodes % x(i) = closestpoint(1)7672Mesh % Nodes % y(i) = closestpoint(2)7673END IF7674END IF76757676IF(RightPerm(i) > 0) THEN ! check if on left side7677mindist = HUGE(1.0_dp)7678DO j=1, Nr-17679tempdist = PointLineSegmDist2D((/xR(j), yR(j)/),(/xR(j+1), yR(j+1)/), (/xx, yy/))7680IF(tempdist < mindist) THEN7681closest = j7682mindist = tempdist7683END IF7684END DO76857686IF(mindist > buffer) THEN7687closestpoint = ClosestPointOfLineSegment((/xR(closest), yR(closest)/),(/xR(closest+1), yR(closest+1)/), (/xx, yy/))7688Mesh % Nodes % x(i) = closestpoint(1)7689Mesh % Nodes % y(i) = closestpoint(2)7690END IF7691END IF76927693IF(FrontPerm(i) > 0) THEN ! check if front is on rail eg advance on narrowing rails7694inside = PointInPolygon2D(RailPoly, (/xx,yy/))7695IF(inside) CYCLE76967697mindist = HUGE(1.0_dp)7698DO j=1, Nr-17699tempdist = PointLineSegmDist2D((/xR(j), yR(j)/),(/xR(j+1), yR(j+1)/), (/xx, yy/))7700IF(tempdist < mindist) THEN7701closest = j7702mindist = tempdist7703END IF7704END DO7705ClosestRight = .TRUE.7706DO j=1, Nl-17707tempdist = PointLineSegmDist2D((/xL(j), yL(j)/),(/xL(j+1), yL(j+1)/), (/xx, yy/))7708IF(tempdist < mindist) THEN7709closest = j7710mindist = tempdist7711ClosestRight = .FALSE.7712END IF7713END DO7714! check to see if closest point is frontleft to right eg outside the rail polygon7715! from the front not over the sides don't need to enforce margins7716! check both ends of rails as not sure which way glacier flowing7717tempdist = PointLineSegmDist2D((/xL(1), yL(1)/),(/xR(1), yL(1)/), (/xx, yy/))7718IF(tempdist < mindist) CYCLE7719tempdist = PointLineSegmDist2D((/xL(Nl), yL(Nl)/),(/xR(Nr), yR(Nr)/), (/xx, yy/))7720IF(tempdist < mindist) CYCLE77217722IF(mindist > buffer) THEN7723IF(ClosestRight) THEN7724closestpoint = ClosestPointOfLineSegment((/xR(closest), yR(closest)/),(/xR(closest+1), yR(closest+1)/), (/xx, yy/))7725ELSE7726closestpoint = ClosestPointOfLineSegment((/xL(closest), yL(closest)/),(/xL(closest+1), yL(closest+1)/), (/xx, yy/))7727END IF7728Mesh % Nodes % x(i) = closestpoint(1)7729Mesh % Nodes % y(i) = closestpoint(2)7730END IF7731END IF7732END DO77337734DEALLOCATE(FrontPerm, LeftPerm, RightPerm)77357736END SUBROUTINE EnforceLateralMargins77377738! determine the closest point of a line segment to a given point7739FUNCTION ClosestPointOfLineSegment(a1, a2, b) RESULT(c)7740REAL(kind=dp) :: a1(2), a2(2), b(2), a(2), c(2), dist, nx77417742a = a2 - a17743dist = a(1)**2 + a(2)**27744nx = ((b(1) - a1(1))*a(1) + (b(2)-a1(2))*a(2)) / dist77457746c(1) = a(1)*nx + a1(1)7747c(2) = a(2)*nx + a1(2)77487749END FUNCTION ClosestPointOfLineSegment77507751SUBROUTINE PauseCalvingSolvers(Model, Params, PauseSolvers)7752IMPLICIT NONE7753TYPE(Model_t) :: Model7754TYPE(Valuelist_t), POINTER :: Params7755LOGICAL :: PauseSolvers7756!---------------------------------------------7757TYPE(Variable_t), POINTER :: Var, RefVar7758REAL(kind=dp) :: PseudoSSdt, SaveDt, LastRemeshTime7759REAL(KIND=dp), POINTER :: TimestepSizes(:,:)7760LOGICAL :: CalvingOccurs, Found7761INTEGER :: i,j,Num, PauseTimeMax, PauseTimeCount, SaveSSiter, TimeIntervals, &7762NewTInterval7763CHARACTER(MAX_NAME_LEN) :: VarName, EqName, FuncName = "PauseCalvingSolvers"77647765SAVE :: SaveDt, SaveSSiter, PseudoSSdt, PauseTimeCount77667767!Need this for temporarily stopping simulation clock when calving occurs,7768! to recheck for multiple calving events triggered in the same timestep7769TimestepSizes => ListGetConstRealArray( CurrentModel % Simulation, &7770'Timestep Sizes', Found, UnfoundFatal=.TRUE.)7771IF(SIZE(TimestepSizes,1) > 1) CALL Fatal(FuncName,&7772"Calving solver requires a single constant 'Timestep Sizes'")77737774SaveDt = TimestepSizes(1,1)77757776SaveSSiter = ListGetInteger(Model % Simulation, "Steady State Max Iterations", Found)7777IF(.NOT. Found) SaveSSiter = 177787779! since "Calving solver requires a single constant 'Timestep Sizes'"7780TimeIntervals = ListGetInteger(Model % Simulation, "Timestep Intervals", UnfoundFatal = .TRUE.)77817782PseudoSSdt = ListGetConstReal( Params, 'Pseudo SS dt', Found)7783IF(.NOT. Found) THEN7784CALL Warn(FuncName,"No value specified for 'Pseudo SS dt', taking 1.0e-10")7785PseudoSSdt = 1.0e-107786END IF77877788PauseTimeMax = ListGetInteger(Params, "Calving Pause Max Steps", Found)7789IF(.NOT. Found) THEN7790CALL Warn(FuncName,"No value specified for 'Calving Pause Max Steps', using 15")7791PauseTimeMax = 157792END IF77937794IF(PauseSolvers) THEN7795PauseTimeCount = PauseTimeCount + 17796IF(PauseTimeCount > PauseTimeMax) THEN7797PauseSolvers = .FALSE.7798PauseTimeCount = 07799CALL Info(FuncName,"Calving paused steps exceeded given threshold, moving on...")7800END IF7801ELSE7802PauseTimeCount = 07803END IF78047805DO Num = 1,9997806WRITE(Message,'(A,I0)') 'Mesh Update Variable ',Num7807VarName = ListGetString( Params, Message, Found)7808IF( .NOT. Found) EXIT78097810Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )7811IF(.NOT. ASSOCIATED(Var)) THEN7812WRITE(Message,'(A,A)') "Listed mesh update variable but can not find: ",VarName7813CALL Fatal(FuncName, Message)7814END IF78157816CALL SwitchSolverExec(Var % Solver, (PauseSolvers))7817END DO78187819!Turn off free surface solvers for next timestep7820DO Num = 1,9997821WRITE(Message,'(A,I0)') 'FreeSurface Variable ',Num7822VarName = ListGetString( Params, Message, Found)7823IF( .NOT. Found) EXIT78247825Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )7826IF(.NOT. ASSOCIATED(Var)) THEN7827WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",VarName7828CALL Fatal(FuncName, Message)7829END IF78307831RefVar => VariableGet( Model % Mesh % Variables, "Reference "//TRIM(VarName), .TRUE. )7832IF(.NOT. ASSOCIATED(RefVar)) THEN7833WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",&7834"Reference "//TRIM(VarName)7835CALL Fatal(FuncName, Message)7836END IF78377838!Turn off (or on) the solver7839!If CalvingOccurs, (switch) off = .true.7840CALL SwitchSolverExec(Var % Solver, (PauseSolvers))7841END DO78427843IF(PauseSolvers) THEN7844CALL ListAddConstReal( Model % Simulation, 'Timestep Size', PseudoSSdt)7845CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', 1)7846CALL ListAddInteger( Model % Simulation, 'Timestep Intervals', TimeIntervals + 1)7847ELSE7848CALL ListAddConstReal( Model % Simulation, 'Timestep Size', SaveDt)7849CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', SaveSSiter)7850END IF78517852DO Num = 1,9997853WRITE(Message,'(A,I0)') 'Switch Off Equation ',Num7854EqName = ListGetString( Params, Message, Found)7855IF( .NOT. Found) EXIT78567857Found = .FALSE.7858DO j=1,Model % NumberOfSolvers7859IF(ListGetString(Model % Solvers(j) % Values, "Equation") == EqName) THEN7860Found = .TRUE.7861!Turn off (or on) the solver7862!If CalvingOccurs, (switch) off = .true.7863CALL SwitchSolverExec(Model % Solvers(j), (PauseSolvers))7864EXIT7865END IF7866END DO78677868IF(.NOT. Found) THEN7869WRITE (Message,'(A,A,A)') "Failed to find Equation Name: ",EqName,&7870" to switch off after calving."7871CALL Fatal(FuncName,Message)7872END IF7873END DO78747875CALL ListAddLogical( Model % Simulation, 'Calving Pause Solvers', PauseSolvers )78767877IF(PauseSolvers) PRINT*, 'Solvers Paused!'78787879END SUBROUTINE PauseCalvingSolvers78807881SUBROUTINE CalvingStatsMMG(Params, Mesh, Mask, ElemMask, FileCreated, MaxBergVolume)78827883TYPE(Valuelist_t), POINTER :: Params7884TYPE(Mesh_t), POINTER :: Mesh7885LOGICAL :: Mask(:), ElemMask(:), FileCreated7886REAL(kind=dp) :: MaxBergVolume7887!-----------------------------7888TYPE(Element_t), POINTER :: Element7889INTEGER :: i, j, k, idx, NBdry, NBulk, NNodes, index, iceberg, node7890INTEGER, ALLOCATABLE :: ElNodes(:), nodes(:)7891LOGICAL :: HasNeighbour, NoNewNodes, NewIceBerg, Found7892LOGICAL, ALLOCATABLE :: FoundNode(:), UsedElem(:), IcebergElem(:), GotNode(:), &7893NodeCount(:)7894CHARACTER(LEN=MAX_NAME_LEN) :: Filename7895REAL(kind=dp), ALLOCATABLE :: BergVolumes(:), BergExtents(:), BergCentroids(:)7896REAL(kind=dp) :: BergVolume, extent(4), Centroid(3)78977898Filename = ListGetString(Params,"Calving Stats File Name", Found)7899IF(.NOT. Found) THEN7900CALL WARN('CalvingStat', 'Output file name not given so using CalvingStats.txt')7901Filename = "CalvingStats.txt"7902END IF79037904NBdry = Mesh % NumberOfBoundaryElements7905NBulk = Mesh % NumberOfBulkElements7906NNodes = Mesh % NumberOfNodes79077908!limit here of 10 possible mesh 'islands'7909ALLOCATE(FoundNode(NNodes), NodeCount(NNodes), ElNodes(4), &7910UsedElem(NBulk), IceBergElem(NBulk), BergVolumes(100), &7911BergExtents(100 * 4), BergCentroids(100*3))7912FoundNode = .FALSE.7913NodeCount = .NOT. Mask7914UsedElem = .FALSE. !count of elems used7915IcebergElem = .FALSE.7916iceberg=0 ! count of different mesh islands7917HasNeighbour=.FALSE. ! whether node has neighour79187919NoNewNodes = .TRUE.7920DO WHILE(COUNT(NodeCount) < NNodes)7921IF(NoNewNodes) THEN7922NewIceberg = .TRUE.7923IcebergElem=.FALSE.7924END IF7925NoNewNodes = .TRUE.7926DO i=1, NBulk7927IF(.NOT. ElemMask(i)) CYCLE7928IF(UsedElem(i)) CYCLE7929Element => Mesh % Elements(i)7930ElNodes = Element % NodeIndexes7931! if there are not any matching nodes and its not a new iceberg7932IF(ALL(.NOT. FoundNode(ElNodes)) .AND. .NOT. NewIceberg) CYCLE7933NewIceberg = .FALSE.7934UsedElem(i) = .TRUE.7935IcebergElem(i) = .TRUE.7936FoundNode(ElNodes) = .TRUE.7937NodeCount(ElNodes) = .TRUE.7938NoNewNodes = .FALSE.7939END DO7940IF(ALL(.NOT. IcebergElem)) EXIT7941IF(COUNT(NodeCount) == NNodes .OR. NoNewNodes) THEN7942DO i=1, NBulk7943IF(.NOT. ElemMask(i)) CYCLE7944IF(UsedElem(i)) CYCLE7945Element => Mesh % Elements(i)7946ElNodes = Element % NodeIndexes7947IF(ANY(.NOT. FoundNode(Elnodes))) CYCLE7948IcebergElem(i) = .TRUE.7949END DO7950iceberg = iceberg + 17951CALL MeshVolume(Mesh, .FALSE., BergVolume, IcebergElem, Centroid)7952CALL IcebergExtent(Mesh, IcebergElem, Extent)79537954IF(SIZE(BergVolumes) < Iceberg) CALL DoubleDPVectorSize(BergVolumes)7955BergVolumes(iceberg) = BergVolume79567957IF(SIZE(BergExtents) < Iceberg*4) CALL DoubleDPVectorSize(BergExtents)7958BergExtents(iceberg*4-3:iceberg*4) = Extent79597960IF(SIZE(BergCentroids) < Iceberg*3) CALL DoubleDPVectorSize(BergCentroids)7961BergCentroids(iceberg*3-2:iceberg*3) = Centroid79627963IF(Iceberg > 0) THEN ! not first time7964PRINT*, 'Iceberg no.', Iceberg, BergVolume, 'extent', extent, 'centroid', centroid7965END IF7966END IF7967END DO79687969MaxBergVolume = MAXVAL(BergVolumes(1:iceberg))79707971! write to file7972IF(FileCreated) THEN7973OPEN( 36, FILE=filename, STATUS='UNKNOWN', POSITION='APPEND')7974ELSE7975OPEN( 36, FILE=filename, STATUS='UNKNOWN')7976WRITE(36, '(A)') "Calving Stats Output File"7977END IF79787979!Write out the left and rightmost points7980WRITE(36, '(A,i0,ES30.21)') 'Time: ',GetTimestep(),GetTime()79817982!Write the iceberg count7983WRITE(36, '(A,i0)') 'Number of Icebergs: ',Iceberg79847985DO i=1,iceberg79867987WRITE(36, '(A,i0,A,F20.0,A,F20.4,F20.4,F20.4,F20.4,A,F20.4,F20.4,F20.4)') &7988'Iceberg ',i, ' Volume ', BergVolumes(i),&7989' Extent ', BergExtents(i*4-3:i*4), ' Centroid ', BergCentroids(i*3-2:i*3)79907991END DO79927993CLOSE(36)7994FileCreated = .TRUE.79957996END SUBROUTINE CalvingStatsMMG79977998SUBROUTINE IcebergExtent(Mesh, ElemMask, Extent)79998000TYPE(Mesh_t), POINTER :: Mesh8001LOGICAL :: ElemMask(:)8002REAL(kind=dp) :: Extent(4)8003!-----------------------------8004TYPE(Element_t), POINTER :: Element8005INTEGER :: i, j, NBulk, n8006INTEGER, ALLOCATABLE :: ElementNodes(:)8007REAL(kind=dp) :: MinX, MaxX, MinY, MaxY80088009NBulk = Mesh % NumberOfBulkElements80108011! calculate volume of each bulk tetra. Add these together to get mesh volume8012MinX = HUGE(1.0_dp)8013MinY = HUGE(1.0_dp)8014MaxX = -HUGE(1.0_dp)8015MaxY = -HUGE(1.0_dp)8016DO, i=1, NBulk8017IF(.NOT. ElemMask(i)) CYCLE8018Element => Mesh % Elements(i)8019ElementNodes = Element % NodeIndexes8020n = Element % TYPE % NumberOfNodes80218022! get elem nodes8023DO j=1, n8024MinX = MIN(MinX, Mesh % Nodes % x(ElementNodes(j)))8025MinY = MIN(MinY, Mesh % Nodes % y(ElementNodes(j)))8026MaxX = MAX(MaxX, Mesh % Nodes % x(ElementNodes(j)))8027MaxY = MAX(MaxY, Mesh % Nodes % y(ElementNodes(j)))8028END DO80298030END DO80318032Extent(1) = MinX8033Extent(2) = MaxX8034Extent(3) = MinY8035Extent(4) = MaxY80368037END SUBROUTINE IcebergExtent80388039! check the front boundary elements are connected8040! if not (usually due to front advance around a corner) return8041! an array of the disconnected front element groups with the nearest lateral boundary constraint8042! the isomesh assigns a boundary on these to suppress calving here8043SUBROUTINE CheckFrontBoundary(Model, FrontConstraint, RightConstraint, LeftConstraint, ElemConstraint)80448045TYPE(Model_t) :: Model8046INTEGER :: FrontConstraint, RightConstraint, LeftConstraint8047INTEGER, ALLOCATABLE :: ElemConstraint(:)8048!------------------------------------------8049TYPE(Mesh_t), POINTER :: Mesh8050TYPE(Element_t), POINTER :: Element8051TYPE(Solver_t), POINTER :: NullSolver => NULL()8052INTEGER, POINTER :: LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), TopPerm(:)=>NULL(), BottomPerm(:)=>NULL()8053INTEGER :: i,j,k,l, counter, NBulk, NBdry, NNodes, LNNodes, RNNodes, group, &8054FNElm, ierr, status(MPI_STATUS_SIZE), proc, Neighbour, NGroups, &8055NNeighbours, dummyint8056INTEGER, ALLOCATABLE :: GroupCounts(:), ElNodes(:), PartGroups(:), PartGroupCounts(:), &8057GroupToPart(:), NeighbourList(:), TotalGroupCounts(:), GroupConstraint(:), &8058GDOFs(:), PartGDOFs(:), PNNeighbours(:), Order(:), WorkInt(:), PartConstraint(:)8059INTEGER, POINTER :: Neighbours(:)8060LOGICAL :: NoNewNodes, NewGroup, NoNewParts8061LOGICAL, ALLOCATABLE :: UsedElem(:), FoundNode(:), IsNeighbour(:,:), &8062PartNeighbours(:,:), GroupNeighbours(:,:), Grouper(:,:), PartGrouper(:,:), GroupElems(:,:)8063CHARACTER(MAX_NAME_LEN) :: LeftMaskName, RightMaskName, TopMaskName, BottomMaskName80648065Mesh => Model % Mesh8066NBulk = Mesh % NumberOfBulkElements8067NBdry = Mesh % NumberOfBoundaryElements8068NNodes = Mesh % NumberOfNodes80698070LeftMaskName = "Left Sidewall Mask"8071RightMaskName = "Right Sidewall Mask"8072TopMaskName = "Top Surface Mask"8073BottomMaskName = "Bottom Surface Mask"8074!Generate perms to quickly get nodes on each boundary8075CALL MakePermUsingMask( Model, NullSolver, Mesh, LeftMaskName, &8076.FALSE., LeftPerm, LNNodes)8077CALL MakePermUsingMask( Model, NullSolver, Mesh, RightMaskName, &8078.FALSE., RightPerm, RNNodes)8079CALL MakePermUsingMask( Model, NullSolver, Mesh, TopMaskName, &8080.FALSE., TopPerm, dummyint)8081CALL MakePermUsingMask( Model, NullSolver, Mesh, BottomMaskName, &8082.FALSE., BottomPerm, dummyint)80838084! first step is to isolate any unconnected elements8085! two sweep allocate then fill8086FNElm=08087DO i=NBulk+1, NBulk + NBdry8088Element => Mesh % Elements(i)8089IF(Element % BoundaryInfo % Constraint /= FrontConstraint) CYCLE8090FNElm = FNElm + 18091END DO80928093ALLOCATE(UsedElem(NBulk + NBdry), GroupCounts(10), ElNodes(3),&8094FoundNode(NNodes), IsNeighbour(10,ParEnv % PEs), &8095GroupElems(10, NBulk+NBdry), GDOFs(10), Order(10))8096UsedElem = .FALSE.8097FoundNode = .FALSE.8098GroupElems = .FALSE.8099IsNeighbour = .FALSE.8100GroupCounts = 08101group = 18102NNeighbours=081038104NoNewNodes = .TRUE.8105DO WHILE(COUNT(UsedElem) < FNElm)8106IF(NoNewNodes) THEN8107NewGroup = .TRUE.8108Counter=081098110!ensure arrays are large enough8111IF(SIZE(GroupCounts) < group) THEN8112CALL DoubleIntVectorSize(GroupCounts)8113CALL Double2DLogSize(IsNeighbour)8114CALL Double2DLogSize(GroupElems)8115END IF8116END IF8117NoNewNodes = .TRUE.8118DO i=NBulk+1, NBulk + NBdry8119IF(UsedElem(i)) CYCLE8120Element => Mesh % Elements(i)8121ElNodes = Element % NodeIndexes8122IF(Element % BoundaryInfo % Constraint /= FrontConstraint) CYCLE8123IF(ALL(.NOT. FoundNode(ElNodes)) .AND. .NOT. NewGroup) CYCLE8124NewGroup = .FALSE.8125UsedElem(i) = .TRUE.8126GroupElems(group,i) = .TRUE.8127FoundNode(ElNodes) = .TRUE.8128NoNewNodes = .FALSE.8129counter= counter + 18130! do any nodes have neighbours?8131DO j=1, SIZE(ElNodes)8132IF(TopPerm(ElNodes(j)) /= 0) CYCLE8133IF(BottomPerm(ElNodes(j)) /= 0) CYCLE8134IF(LeftPerm(ElNodes(j)) /= 0) CYCLE8135IF(RightPerm(ElNodes(j)) /= 0) CYCLE8136Neighbours => Mesh % ParallelInfo % NeighbourList(ElNodes(j)) % Neighbours8137DO k=1, SIZE(Neighbours)8138IF(Neighbours(k) == ParEnv % MyPE) CYCLE8139IF(.NOT. IsNeighbour(group, Neighbours(k)+1)) THEN8140IsNeighbour(group, Neighbours(k)+1) = .TRUE.8141NNeighbours = NNeighbours + 18142IF(SIZE(GDOFs) < NNeighbours) THEN8143CALL DoubleIntVectorSize(GDOFs)8144CALL DoubleIntVectorSize(Order)8145END IF8146GDOFs(NNeighbours) = Mesh % ParallelInfo % GlobalDOFs(ElNodes(j))8147Order(NNeighbours) = Neighbours(k)+18148END IF8149END DO8150END DO81518152END DO8153IF(COUNT(UsedElem) == FNElm .OR. NoNewNodes) THEN8154DO i=NBulk+1, NBulk + NBdry8155IF(UsedElem(i)) CYCLE8156Element => Mesh % Elements(i)8157IF(Element % BoundaryInfo % Constraint /= FrontConstraint) CYCLE8158ElNodes = Element % NodeIndexes8159IF(ANY(.NOT. FoundNode(Elnodes))) CYCLE8160counter = counter+18161FoundNode(ElNodes) = .TRUE.8162END DO8163GroupCounts(group) = counter8164group = group + 18165END IF8166END DO81678168!overshoot by 18169group = group-181708171!order GDOFs into rank order8172ALLOCATE(WorkInt(NNeighbours))8173counter=08174l=08175DO i=1, group8176DO j=1, ParEnv % PEs8177IF(.NOT. IsNeighbour(i,j)) CYCLE8178counter=counter+18179DO k=l+1, l+COUNT(IsNeighbour(i,:))8180IF(Order(k) == j) THEN8181WorkInt(counter) = GDOFs(k)8182EXIT8183END IF8184END DO8185END DO8186l=l+COUNT(IsNeighbour(i,:))8187END DO81888189DEALLOCATE(GDOFs)8190ALLOCATE(GDOFs(NNeighbours))8191GDOFs = WorkInt8192DEALLOCATE(WorkInt)81938194! gather number of groups on each proc8195ALLOCATE(PartGroups(ParEnv % PEs))8196CALL MPI_ALLGATHER(group, 1, MPI_INTEGER, &8197PartGroups, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)8198ALLOCATE(PNNeighbours(ParEnv % PEs))8199CALL MPI_ALLGATHER(NNeighbours, 1, MPI_INTEGER, &8200PNNeighbours, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)82018202NGroups = SUM(PartGroups)8203ALLOCATE(GroupToPart(NGroups))8204counter=08205DO i=1, ParEnv % PEs8206DO j=1, PartGroups(i)8207counter=counter+18208GroupToPart(counter) = i8209END DO8210END DO82118212! only ranks that have front elements beyond this8213IF(group /= 0) THEN8214ALLOCATE(PartGroupCounts(NGroups))8215counter=08216DO i=1, ParEnv % PEs8217proc = i-18218IF(proc==ParEnv % MyPE) THEN8219PartGroupCounts(counter+1:counter+group) = GroupCounts(:group)8220counter=counter+group8221CYCLE8222END IF8223IF(PartGroups(i) == 0) CYCLE8224CALL MPI_BSEND(GroupCounts(:group), group, MPI_INTEGER, &8225proc,9000, ELMER_COMM_WORLD, ierr )8226CALL MPI_RECV( PartGroupCounts(counter+1:counter+PartGroups(i)), PartGroups(i), MPI_INTEGER, &8227proc, 9000, ELMER_COMM_WORLD, status, ierr )8228counter = counter + PartGroups(i)8229END DO82308231ALLOCATE(PartGDOFs(SUM(PNNeighbours)))8232counter=08233DO i=1, ParEnv % PEs8234proc = i-18235IF(proc==ParEnv % MyPE) THEN8236PartGDOFs(counter+1:counter+NNeighbours) = GDOFs(:NNeighbours)8237counter=counter+NNeighbours8238CYCLE8239END IF8240IF(PartGroups(i) == 0) CYCLE8241CALL MPI_BSEND(GDOFs(:NNeighbours), NNeighbours, MPI_INTEGER, &8242proc,9001, ELMER_COMM_WORLD, ierr )8243CALL MPI_RECV( PartGDOFs(counter+1:counter+PNNeighbours(i)), PNNeighbours(i), MPI_INTEGER,&8244proc, 9001, ELMER_COMM_WORLD, status, ierr )8245counter = counter + PNNeighbours(i)8246END DO82478248ALLOCATE(PartNeighbours(NGroups, ParEnv % PEs))8249PartNeighbours = .FALSE.8250counter=18251DO i=1, ParEnv % PEs8252proc = i-18253IF(proc==ParEnv % MyPE) THEN8254DO j=1,group8255PartNeighbours(counter,:) = IsNeighbour(j,:)8256counter=counter+18257END DO8258CYCLE8259END IF8260IF(PartGroups(i) == 0) CYCLE8261DO j=1, group8262CALL MPI_BSEND(IsNeighbour(j,:), ParEnv % PEs, MPI_LOGICAL, &8263proc,9100+j, ELMER_COMM_WORLD, ierr )8264END DO8265DO j=1, PartGroups(i)8266CALL MPI_RECV( PartNeighbours(counter,:), &8267ParEnv % PEs, MPI_LOGICAL, proc, 9100+j, ELMER_COMM_WORLD, status, ierr )8268counter = counter + 18269END DO8270END DO82718272ALLOCATE(GroupNeighbours(group, NGroups))8273GroupNeighbours = .FALSE.8274DO i=1, group8275DO j=1, NGroups8276IF(j==1) THEN8277counter=08278ELSE8279counter = SUM(PNNeighbours(1:GroupToPart(j)-1))8280IF(PartGroups(GroupToPart(j))>1) THEN8281DO k=1,j-18282IF(GroupToPart(j) /= GroupToPart(k)) CYCLE8283counter = counter + COUNT(PartNeighbours(k,:))8284END DO8285END IF8286END IF8287IF(.NOT. IsNeighbour(i,GroupToPart(j))) CYCLE8288NeighbourList = PACK( (/ (k, k=1, ParEnv % PEs) /), PartNeighbours(j,:))8289DO k=1, SIZE(NeighbourList)8290counter=counter+18291!check gdof present in group8292IF(NeighbourList(k)-1 /= ParEnv % MyPE) CYCLE8293DO l=NBulk+1, NBulk + NBdry8294IF(.NOT. GroupElems(i, l)) CYCLE8295Element => Mesh % Elements(l)8296ElNodes = Element % NodeIndexes8297IF(.NOT. ANY(Mesh % ParallelInfo % GlobalDOFs(ElNodes) == PartGDOFs(Counter))) CYCLE8298GroupNeighbours(i,j) = .TRUE.8299EXIT8300END DO8301END DO8302END DO8303END DO83048305DEALLOCATE(PartNeighbours)8306ALLOCATE(PartNeighbours(NGroups, NGroups))8307PartNeighbours = .FALSE.8308counter=18309DO i=1, ParEnv % PEs8310proc = i-18311IF(proc==ParEnv % MyPE) THEN8312DO j=1,group8313PartNeighbours(counter,:) = GroupNeighbours(j,:)8314counter=counter+18315END DO8316CYCLE8317END IF8318IF(PartGroups(i) == 0) CYCLE8319DO j=1, group8320CALL MPI_BSEND(GroupNeighbours(j,:), NGroups, MPI_LOGICAL, &8321proc,9200+j, ELMER_COMM_WORLD, ierr )8322END DO8323DO j=1, PartGroups(i)8324CALL MPI_RECV( PartNeighbours(counter,:), &8325NGroups, MPI_LOGICAL, proc, 9200+j, ELMER_COMM_WORLD, status, ierr )8326counter = counter + 18327END DO8328END DO83298330ALLOCATE(TotalGroupCounts(NGroups), Grouper(group, NGroups))8331TotalGroupCounts = PartGroupCounts8332counter=08333Grouper = .FALSE.8334DO i=1, NGroups8335IF(GroupToPart(i) == ParEnv % MyPE + 1) THEN8336DO j=1,group8337Grouper(j,i+j-1) = .TRUE.8338END DO8339EXIT8340END IF8341END DO8342DO i=1, group8343NoNewParts = .FALSE.8344DO WHILE(.NOT. NoNewParts)8345NoNewParts = .TRUE.8346DO j=1, NGroups8347IF(.NOT. Grouper(i,j)) CYCLE8348DO k=1, NGroups8349IF(.NOT. PartNeighbours(j,k)) CYCLE8350IF(.NOT. Grouper(i,k)) NoNewParts = .FALSE.8351Grouper(i,k) = .TRUE.8352END DO8353END DO8354END DO8355END DO83568357ALLOCATE(PartGrouper(NGroups, NGroups))8358counter=18359DO i=1, ParEnv % PEs8360proc = i-18361IF(proc==ParEnv % MyPE) THEN8362DO j=1, group8363PartGrouper(counter,:) = Grouper(j,:)8364counter=counter+18365END DO8366CYCLE8367END IF8368IF(PartGroups(i) == 0) CYCLE8369DO j=1, group8370CALL MPI_BSEND(Grouper(j,:), NGroups, MPI_LOGICAL, &8371proc,9300+j, ELMER_COMM_WORLD, ierr )8372END DO8373DO j=1, PartGroups(i)8374CALL MPI_RECV( PartGrouper(counter,:), &8375NGroups, MPI_LOGICAL, proc, 9300+j, ELMER_COMM_WORLD, status, ierr )8376counter=counter+18377END DO8378END DO83798380DO i=1, NGroups8381TotalGroupCounts(i) = SUM(PartGroupCounts, PartGrouper(i,:))8382END DO83838384!find lateral margin tag8385ALLOCATE(GroupConstraint(group))8386GroupConstraint=08387DO i=1, group8388FoundNode = .FALSE.8389DO j=NBulk+1, NBulk+Nbdry8390IF(.NOT. GroupElems(i,j)) CYCLE8391Element => Mesh % Elements(j)8392ElNodes = Element % NodeIndexes8393FoundNode(ElNodes) = .TRUE.8394END DO8395!check if any node indexes are also on lateral boundaries8396DO j=1, NNodes8397IF(.NOT. FoundNode(j)) CYCLE8398IF(RightPerm(j) /= 0) THEN8399GroupConstraint(i) = RightConstraint8400EXIT8401END IF8402IF(LeftPerm(j) /= 0) THEN8403GroupConstraint(i) = LeftConstraint8404EXIT8405END IF8406END DO8407END DO84088409ALLOCATE(PartConstraint(NGroups))8410counter=18411DO i=1, ParEnv % PEs8412proc = i-18413IF(proc==ParEnv % MyPE) THEN8414DO j=1, group8415PartConstraint(counter) = GroupConstraint(j)8416counter=counter+18417END DO8418CYCLE8419END IF8420IF(PartGroups(i) == 0) CYCLE8421DO j=1, group8422CALL MPI_BSEND(GroupConstraint(j), 1, MPI_LOGICAL, &8423proc,9400+j, ELMER_COMM_WORLD, ierr )8424END DO8425DO j=1, PartGroups(i)8426CALL MPI_RECV( PartConstraint(counter), &84271, MPI_LOGICAL, proc, 9400+j, ELMER_COMM_WORLD, status, ierr )8428counter=counter+18429END DO8430END DO84318432ALLOCATE(ElemConstraint(NBdry+NBulk))8433ElemConstraint=08434DO i=1, group8435IF(GroupConstraint(i) == 0) THEN8436DO j=1, NGroups8437IF(.NOT. Grouper(i,j)) CYCLE8438IF(PartConstraint(j) == 0) CYCLE8439IF(MAXVAL(TotalGroupCounts) == TotalGroupCounts(j)) CYCLE8440GroupConstraint(i) = PartConstraint(j)8441END DO8442END IF84438444DO j=1, NGroups8445IF(GroupToPart(j)-1 /= ParEnv % MyPE) CYCLE8446IF(MAXVAL(TotalGroupCounts) == TotalGroupCounts(j)) CYCLE8447DO k=NBulk+1, NBulk + NBdry8448IF(GroupElems(i,k)) ElemConstraint(k) = GroupConstraint(i)8449END DO8450END DO84518452END DO84538454ELSE8455ALLOCATE(ElemConstraint(Nbdry+ NBulk))8456ElemConstraint = 08457END IF84588459DEALLOCATE(LeftPerm, RightPerm, TopPerm, BottomPerm)84608461CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)84628463END SUBROUTINE CheckFrontBoundary84648465! subroutine to ceck for inverted based elements due to lagrangian movement8466SUBROUTINE CheckBaseFreeSurface(Model, Mesh, Buffer)8467TYPE(Model_t) :: Model8468TYPE(Mesh_t), POINTER :: Mesh8469REAL(KIND=dp), OPTIONAL :: Buffer8470!-------------------------8471TYPE(Solver_t), POINTER :: NullSolver=>NULL()8472TYPE(Element_t), POINTER :: Element8473TYPE(Nodes_t) :: Nodes8474INTEGER, POINTER :: NodeIndexes(:),BottomPerm(:)=>NULL(),FrontPerm(:)=>NULL(),&8475LeftPerm(:)=>NULL(),RightPerm(:)=>NULL()8476INTEGER :: i,j,n,k, counter,BaseBCtag,FrontBCtag,LeftBCtag,RightBCtag,dummyint8477REAL(KIND=dp) :: Normal(3), NBuffer8478LOGICAL :: Found, ThisBC8479CHARACTER(MAX_NAME_LEN) :: SolverName, BottomMaskName, FrontMaskName,&8480LeftMaskName, RightMaskName, Message8481SolverName="CheckBaseFreeSurface"84828483IF(.NOT. PRESENT(Buffer)) THEN8484NBuffer = -0.01_dp8485ELSE8486NBuffer = -Buffer8487END IF84888489FrontMaskName = "Calving Front Mask"8490BottomMaskName = "Bottom Surface Mask"8491CALL MakePermUsingMask( Model, NullSolver, Mesh, BottomMaskName, &8492.FALSE., BottomPerm, dummyint)8493CALL MakePermUsingMask( Model, NullSolver, Mesh, FrontMaskName, &8494.FALSE., FrontPerm, dummyint)8495LeftMaskName = "Left Sidewall Mask"8496RightMaskName = "Right Sidewall Mask"8497!Generate perms to quickly get nodes on each boundary8498CALL MakePermUsingMask( Model, NullSolver, Mesh, LeftMaskName, &8499.FALSE., LeftPerm, dummyint)8500CALL MakePermUsingMask( Model, NullSolver, Mesh, RightMaskName, &8501.FALSE., RightPerm, dummyint)85028503DO i=1,Model % NumberOfBCs8504ThisBC = ListGetLogical(Model % BCs(i) % Values,BottomMaskName,Found)8505IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE8506BaseBCtag = Model % BCs(i) % Tag8507EXIT8508END DO85098510DO i=1,Model % NumberOfBCs8511ThisBC = ListGetLogical(Model % BCs(i) % Values,FrontMaskName,Found)8512IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE8513FrontBCtag = Model % BCs(i) % Tag8514EXIT8515END DO85168517DO i=1,Model % NumberOfBCs8518ThisBC = ListGetLogical(Model % BCs(i) % Values,LeftMaskName,Found)8519IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE8520LeftBCtag = Model % BCs(i) % Tag8521EXIT8522END DO85238524DO i=1,Model % NumberOfBCs8525ThisBC = ListGetLogical(Model % BCs(i) % Values,RightMaskName,Found)8526IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE8527RightBCtag = Model % BCs(i) % Tag8528EXIT8529END DO85308531!check element how much this deforms elements near front8532!if the element is above a 45 degree vertical angle from xy plane change to front boundary8533DO i=Mesh % NumberOfBulkElements +1, &8534Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements85358536Element => Mesh % Elements(i)8537IF(Element % BoundaryInfo % Constraint /= BaseBCtag) CYCLE8538n = Element % TYPE % NumberOfNodes85398540NodeIndexes => Element % NodeIndexes85418542ALLOCATE(Nodes % x(n), Nodes % y(n), Nodes % z(n))85438544Nodes % x = Mesh % Nodes % x(NodeIndexes)8545Nodes % y = Mesh % Nodes % y(NodeIndexes)8546Nodes % z = Mesh % Nodes % z(NodeIndexes)85478548Normal = NormalVector(Element, Nodes)85498550IF(Normal(3) > NBuffer) THEN85518552PRINT*, SolverName,' Inverted base element:',i, 'on part:', ParEnv % MyPE, &8553'moving to...'85548555counter=08556DO k=1,n8557IF(LeftPerm(NodeIndexes(k)) > 0) counter = counter+18558END DO8559IF(Counter >= 2) THEN8560PRINT*, SolverName, ' Left boundary', ParEnv % MyPE8561Element % BoundaryInfo % Constraint = LeftBCtag8562CYCLE8563END IF85648565counter=08566DO k=1,n8567IF(RightPerm(NodeIndexes(k)) > 0) counter = counter+18568END DO8569IF(Counter >= 2) THEN8570PRINT*, SolverName, ' Right boundary', ParEnv % MyPE8571Element % BoundaryInfo % Constraint = RightBCtag8572CYCLE8573END IF85748575counter=08576DO k=1,n8577IF(FrontPerm(NodeIndexes(k)) > 0) counter = counter+18578END DO8579IF(Counter >= 2) THEN8580PRINT*, SolverName, ' Front boundary', ParEnv % MyPE8581Element % BoundaryInfo % Constraint = FrontBCtag8582CYCLE8583END IF85848585IF(Element % BoundaryInfo % Constraint == BaseBCtag) &8586CALL WARN(SolverName, 'Inverted base element not on edge so &8587cannot tranfer to other boundary')8588END IF85898590DEALLOCATE(Nodes % x, Nodes % y, Nodes % z)85918592END DO8593DEALLOCATE(FrontPerm,BottomPerm,LeftPerm,RightPerm)85948595END SUBROUTINE CheckBaseFreeSurface85968597! only serial. Will need to write parallel routine but will only be needed will parallel]8598! remeshing properly ingrated into calving routines8599SUBROUTINE SaveTerminusPosition(Model, Solver, Mesh, Boss)86008601IMPLICIT NONE8602!------------------------------------------------------------------------------8603TYPE(Solver_t) :: Solver8604TYPE(Model_t) :: Model8605TYPE(Mesh_t) :: Mesh8606LOGICAL :: Boss8607!------------------------------------------------------------------------------8608TYPE(Solver_t), POINTER :: AdvSolver8609TYPE(Valuelist_t), POINTER :: SolverParams, AdvParams8610INTEGER, POINTER :: TopPerm(:)=>NULL(), FrontPerm(:)=>NULL(), &8611LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), SidePerm(:)=> NULL(),&8612NodeIndexes(:)8613LOGICAL :: FileCreated = .FALSE.,Found,FoundRight,FoundLeft,FirstTime,reducecorners(2),&8614ThisBC8615INTEGER :: i,j,k, NNodes, NBulk, NBdry, RCounter, LCounter,dummyint,&8616Nl,Nr, Naux, ok, Nrail, Counter,FrontBCtag,side,LastNode,CornersTotal8617REAL(KIND=dp) :: buffer, xx, yy, mindist, tempdist8618REAL(kind=dp), ALLOCATABLE :: xL(:),yL(:),xR(:),yR(:), xRail(:), yRail(:),&8619PAllCorners(:), MinDists(:)8620INTEGER, ALLOCATABLE :: FrontRight(:), FrontLeft(:), NodeList(:), jmin(:), Corner(:),&8621AllCorners(:)8622LOGICAL, ALLOCATABLE :: GotNode(:), InFront(:)8623CHARACTER(LEN=MAX_NAME_LEN) :: Filename, SolverName, LeftRailFName, RightRailFName,&8624Adv_EqName8625INTEGER, PARAMETER :: io=2086268627SAVE :: FileCreated86288629SolverName ="SaveTerminusPosition"8630SolverParams => Solver % Values86318632NBulk = Mesh % NumberOfBulkElements8633NBdry = Mesh % NumberOfBoundaryElements8634NNodes = Mesh % NumberOfNodes86358636CALL MakePermUsingMask( Model, Solver, Mesh, "Calving Front Mask", &8637.FALSE., FrontPerm, dummyint)8638CALL MakePermUsingMask( Model, Solver, Mesh, "Top Surface Mask", &8639.FALSE., TopPerm, dummyint)8640CALL MakePermUsingMask( Model, Solver, Mesh, "Left Sidewall Mask", &8641.FALSE., LeftPerm, dummyint)8642CALL MakePermUsingMask( Model, Solver, Mesh, "Right Sidewall Mask", &8643.FALSE., RightPerm, dummyint)86448645IF(Boss) THEN8646FoundLeft=.FALSE.8647FoundRight=.FALSE.8648RCounter= 0; LCounter=08649ALLOCATE(FrontRight(100), FrontLeft(100))8650DO i=1,NNodes8651IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN8652IF( LeftPerm(i) >0 ) THEN8653LCounter = LCounter + 18654FrontLeft(LCounter) = i8655FoundLeft = .TRUE.8656ELSE IF ( RightPerm(i) >0 ) THEN8657RCounter = RCounter + 18658FrontRight(RCounter) = i8659FoundRight = .TRUE.8660END IF8661END IF8662END DO86638664IF(.NOT. FoundLeft .OR. .NOT. FoundRight) CALL FATAL(SolverName, 'Unable to find terminus corners')86658666reducecorners = .FALSE.8667IF(LCounter > 1) reducecorners(1)=.TRUE.8668IF(RCounter > 1) reducecorners(2)=.TRUE.86698670IF(ANY(reducecorners)) THEN86718672Adv_EqName = ListGetString(SolverParams,"Front Advance Solver", DefValue="Front Advance")8673! Locate CalvingAdvance Solver8674Found = .FALSE.8675DO i=1,Model % NumberOfSolvers8676IF(GetString(Model % Solvers(i) % Values, 'Equation') == Adv_EqName) THEN8677AdvSolver => Model % Solvers(i)8678Found = .TRUE.8679EXIT8680END IF8681END DO8682IF(.NOT. Found) CALL FATAL(SolverName, "Advance Solver Equation not given")8683AdvParams => AdvSolver % Values86848685buffer = ListGetConstReal(AdvParams, "Rail Buffer", Found, DefValue=0.1_dp)8686IF(.NOT. Found) CALL Info(SolverName, "No Rail Buffer set using default 0.1")86878688LeftRailFName = ListGetString(AdvParams, "Left Rail File Name", Found)8689IF(.NOT. Found) THEN8690CALL Info(SolverName, "Left Rail File Name not found, assuming './LeftRail.xy'")8691LeftRailFName = "LeftRail.xy"8692END IF8693Nl = ListGetInteger(AdvParams, "Left Rail Number Nodes", Found)8694IF(.NOT.Found) THEN8695WRITE(Message,'(A,A)') 'Left Rail Number Nodes not found'8696CALL FATAL(SolverName, Message)8697END IF8698!TO DO only do these things if firsttime=true?8699OPEN(unit = io, file = TRIM(LeftRailFName), status = 'old',iostat = ok)8700IF (ok /= 0) THEN8701WRITE(message,'(A,A)') 'Unable to open file ',TRIM(LeftRailFName)8702CALL FATAL(Trim(SolverName),Trim(message))8703END IF8704ALLOCATE(xL(Nl), yL(Nl))87058706! read data8707DO i = 1, Nl8708READ(io,*,iostat = ok, end=200) xL(i), yL(i)8709END DO8710200 Naux = Nl - i8711IF (Naux > 0) THEN8712WRITE(Message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(LeftRailFName)8713CALL INFO(Trim(SolverName),Trim(message))8714END IF8715CLOSE(io)8716RightRailFName = ListGetString(AdvParams, "Right Rail File Name", Found)8717IF(.NOT. Found) THEN8718CALL Info(SolverName, "Right Rail File Name not found, assuming './RightRail.xy'")8719RightRailFName = "RightRail.xy"8720END IF87218722Nr = ListGetInteger(AdvParams, "Right Rail Number Nodes", Found)8723IF(.NOT.Found) THEN8724WRITE(Message,'(A,A)') 'Right Rail Number Nodes not found'8725CALL FATAL(SolverName, Message)8726END IF8727!TO DO only do these things if firsttime=true?8728OPEN(unit = io, file = TRIM(RightRailFName), status = 'old',iostat = ok)87298730IF (ok /= 0) THEN8731WRITE(Message,'(A,A)') 'Unable to open file ',TRIM(RightRailFName)8732CALL FATAL(Trim(SolverName),Trim(message))8733END IF8734ALLOCATE(xR(Nr), yR(Nr))87358736! read data8737DO i = 1, Nr8738READ(io,*,iostat = ok, end=100) xR(i), yR(i)8739END DO8740100 Naux = Nr - i8741IF (Naux > 0) THEN8742WRITE(message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(RightRailFName)8743CALL INFO(Trim(SolverName),Trim(message))8744END IF8745CLOSE(io)8746END IF87478748DO side=1,2 ! left 1, right 287498750IF(.NOT. reducecorners(side)) CYCLE87518752IF (side==1) THEN8753Nrail= Nl8754ALLOCATE(xRail(Nrail), yRail(Nrail))8755xRail = xL8756yRail = yL8757SidePerm => LeftPerm8758CornersTotal = LCounter8759ELSE8760Nrail= Nr8761ALLOCATE(xRail(Nrail), yRail(Nrail))8762xRail = xR8763yRail = yR ! TO DO use pointers instead?8764SidePerm => RightPerm8765CornersTotal = RCounter8766END IF87678768ALLOCATE(AllCorners(CornersTotal))8769Counter = 08770DO i=1,NNodes8771IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN8772IF ( SidePerm(i) >0 ) THEN8773Counter = Counter + 18774AllCorners(Counter) = i8775END IF8776END IF8777END DO87788779ALLOCATE(jmin(CornersTotal),InFront(CornersTotal),MinDists(CornersTotal))8780DO i=1, CornersTotal87818782xx = Mesh % Nodes % x(AllCorners(i))8783yy = Mesh % Nodes % y(AllCorners(i))87848785MinDist=(xRail(1)-xRail(Nrail))**2.+(yRail(1)-yRail(Nrail))**2.8786! MinDist is actually maximum distance, needed for finding closest rail node8787DO j=1,Nrail ! Find closest point on rail8788TempDist=(xRail(j)-xx)**2.+(yRail(j)-yy)**2.8789IF(TempDist < MinDist) THEN8790MinDist=TempDist8791jmin(i)=j8792END IF8793END DO8794MinDists(i) = MinDist8795!check if in front or behind node8796InFront(i) = .TRUE.8797IF(jmin(i) == Nrail) InFront(i) = .FALSE.8798IF(jmin(i) > 1 .AND. jmin(i) /= Nrail) THEN8799MinDist = PointLineSegmDist2D((/xRail(jmin(i)),yRail(jmin(i))/), &8800(/xRail(jmin(i)+1),yRail(jmin(i)+1)/),(/xx,yy/))8801TempDist = PointLineSegmDist2D((/xRail(jmin(i)),yRail(jmin(i))/), &8802(/xRail(jmin(i)-1),yRail(jmin(i)-1)/),(/xx,yy/))8803IF(MinDist > TempDist) InFront(i) = .FALSE.8804END IF8805END DO88068807IF(COUNT(jmin == MAXVAL(jmin)) == 1) THEN8808Corner = MAXLOC(jmin)8809ELSE IF(COUNT(jmin == MAXVAL(jmin) .AND. InFront) == 1) THEN8810Corner = PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin) .AND. InFront)8811ELSE IF(ALL(InFront(PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin))))) THEN8812ALLOCATE(Corner(1))8813MinDist = HUGE(1.0_dp)8814DO i=1, CornersTotal8815IF(jmin(i) /= MAXVAL(jmin)) CYCLE8816IF(.NOT. InFront(i)) CYCLE8817IF(MinDists(i) < mindist) THEN8818mindist = MinDists(i)8819Corner(1) = i8820END IF8821END DO8822ELSE IF(ALL(.NOT. InFront(PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin))))) THEN8823ALLOCATE(Corner(1))8824MinDist = HUGE(1.0_dp)8825DO i=1, CornersTotal8826IF(jmin(i) /= MAXVAL(jmin)) CYCLE8827IF(MinDists(i) < mindist) THEN8828mindist = MinDists(i)8829Corner(1) = i8830END IF8831END DO8832ELSE8833CALL FATAL(SolverName, 'Problem reducing corners')8834END IF88358836IF(side == 1) THEN8837FrontLeft(1) = AllCorners(Corner(1))8838ELSE8839FrontRight(1) = AllCorners(Corner(1))8840END IF88418842DEALLOCATE(xRail, yRail, AllCorners, jmin, InFront, MinDists, Corner)8843END DO88448845DO i=1,Model % NumberOfBCs8846ThisBC = ListGetLogical(Model % BCs(i) % Values,"Calving Front Mask",Found)8847IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE8848FrontBCtag = Model % BCs(i) % Tag8849EXIT8850END DO88518852ALLOCATE(GotNode(NNodes), NodeList(NNodes))8853FirstTime=.TRUE.8854GotNode = .FALSE.8855counter = 08856LastNode = 08857DO WHILE(LastNode /= FrontRight(1))8858Found = .FALSE.8859IF(FirstTime) THEN8860LastNode = FrontLeft(1)8861GotNode(FrontLeft(1)) = .TRUE.8862NodeList(1) = LastNode8863counter = counter + 18864END IF8865DO i= NBulk+1, NBulk+NBdry8866IF(Mesh % Elements(i) % BoundaryInfo % constraint /= FrontBCtag) CYCLE8867NodeIndexes => Mesh % Elements(i) % NodeIndexes8868IF(.NOT. ANY(NodeIndexes == LastNode)) CYCLE8869DO j=1,Mesh % Elements(i) % TYPE % NumberOfNodes8870IF(GotNode(NodeIndexes(j))) CYCLE8871IF(TopPerm(NodeIndexes(j)) > 0) THEN8872LastNode = NodeIndexes(j)8873Found = .TRUE.8874GotNode(LastNode) = .TRUE.8875counter = counter + 18876NodeList(counter) = LastNode8877EXIT8878END IF8879END DO8880IF(Found) EXIT8881END DO8882IF(.NOT. Found) THEN8883CALL WARN(SolverName, 'Unable to get terminus loop for this timestep')8884EXIT8885END IF8886IF(ANY(FrontLeft(1:LCounter) == LastNode)) THEN8887! reset to first node as gone wrong way8888! GotNode should prvent us doing this again8889PRINT*, 'gone wrong way...'8890LastNode = NodeList(1)8891counter = 18892END IF8893FirstTime=.FALSE.8894END DO88958896IF(LastNode == FrontRight(1)) THEN ! loop success8897Filename = ListGetString(SolverParams,"Output Terminus File Name", Found)8898IF(.NOT. Found) THEN8899CALL WARN(SolverName, 'Output file name not given so using TerminusPosition.txt')8900Filename = "TerminusPosition.txt"8901END IF89028903! write to file8904IF(FileCreated) THEN8905OPEN( 37, FILE=filename, STATUS='UNKNOWN', POSITION='APPEND')8906ELSE8907OPEN( 37, FILE=filename, STATUS='UNKNOWN')8908WRITE(37, '(A)') "Terminus Position File"8909WRITE(37, '(A)') "TimeStep, Time, NumberOfNodes"8910WRITE(37, '(A)') "xx, yy"8911END IF89128913!Write out the left and rightmost points8914WRITE(37, *) 'NewTime:', GetTimestep(), GetTime(), counter8915DO i=1, counter8916WRITE(37, *) Mesh % Nodes % x(NodeList(i)), Mesh % Nodes % y(NodeList(i))8917END DO89188919CLOSE(37)8920END IF8921END IF89228923FileCreated = .TRUE.8924DEALLOCATE(FrontPerm,TopPerm,LeftPerm,RightPerm)89258926END SUBROUTINE SaveTerminusPosition89278928END MODULE CalvingGeometry8929893089318932