Path: blob/devel/elmerice/Solvers/CalvingRemeshMMG.F90
3204 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!Remesh the calving model using MMG3D - runs in parallel but remeshing is serial!32!Takes a level set which defines a calving event (or multiple calving events). Level33! set is negative inside a calving event, and positive in the remaining domain.3435! Strategy:36!----------------3738! - Use Mesh % Repartition and RedistributeMesh to send relevant (calving)39! mesh regions to the nominated remeshing partition4041! - Remesh with MMG - done serially on nominated partition (TODO - improve nomination of partition)4243! - Global node/element number renegotiation4445! - Zoltan to compute new partitioning4647! - RedistributeMesh back to target processors4849! - Interpolate variables etc5051! - Continue simulation5253SUBROUTINE CalvingRemeshMMG( Model, Solver, dt, Transient )5455USE MeshUtils56USE CalvingGeometry57USE MeshPartition58USE MeshRemeshing5960IMPLICIT NONE6162#include "mmg/common/libmmgtypesf.h"63#ifndef MMGVERSION_H64#define MMG_VERSION_LT(MAJOR,MINOR) 165#endif6667TYPE(Model_t) :: Model68TYPE(Solver_t), TARGET :: Solver69REAL(KIND=dp) :: dt70LOGICAL :: Transient71!--------------------------------------72TYPE(Variable_t), POINTER :: CalvingVar,DistanceVar,mmgVar73TYPE(ValueList_t), POINTER :: SolverParams74TYPE(Mesh_t),POINTER :: Mesh,GatheredMesh,NewMeshR,NewMeshRR,FinalMesh,ParMetisMesh75TYPE(Element_t),POINTER :: Element, ParentElem76INTEGER :: i,j,k,NNodes,GNBulk, GNBdry, GNNode, NBulk, Nbdry, ierr, &77my_cboss,MyPE, PEs,CCount, counter, GlNode_min, GlNode_max,adjList(4),&78front_BC_ID, front_body_id, my_calv_front,calv_front, ncalv_parts, &79group_calve, comm_calve, group_world,ecode, NElNodes, target_bodyid,gdofs(4), &80PairCount,RPairCount, NCalvNodes, croot, nonCalvBoss,&81NVerts, NTetras, NPrisms, NTris, NQuads, NEdges, dummyint82INTEGER, POINTER :: NodeIndexes(:), geom_id, TopPerm(:)=>NULL(), FrontPerm(:)=>NULL()83INTEGER, ALLOCATABLE :: Prnode_count(:), cgroup_membs(:),disps(:), &84PGDOFs_send(:),pcalv_front(:),GtoLNN(:),EdgePairs(:,:),REdgePairs(:,:), ElNodes(:),&85Nodes(:), IslandCounts(:), pNCalvNodes(:,:), TetraQuality(:)86REAL(KIND=dp) :: test_thresh, test_point(3), remesh_thresh, hmin, hmax, hgrad, hausd, &87newdist, Quality, PauseVolumeThresh, MaxBergVolume, RmcValue88REAL(KIND=dp), ALLOCATABLE :: test_dist(:), test_lset(:), Ptest_lset(:), Gtest_lset(:), &89target_length(:,:), Ptest_dist(:), Gtest_dist(:), hminarray(:), hausdarray(:)90REAL(KIND=dp), POINTER :: WorkArray(:,:) => NULL()91LOGICAL, ALLOCATABLE :: calved_node(:), remeshed_node(:), fixed_node(:), fixed_elem(:), &92elem_send(:), RmElem(:), RmNode(:),new_fixed_node(:), new_fixed_elem(:), FoundNode(:,:), &93UsedElem(:), NewNodes(:), RmIslandNode(:), RmIslandElem(:), PartGotNodes(:), SendNode(:)94LOGICAL :: ImBoss, Found, Isolated, Debug,DoAniso,NSFail,CalvingOccurs,&95RemeshOccurs,CheckFlowConvergence, NoNewNodes, RSuccess, Success,&96SaveMMGMeshes, SaveMMGSols, PauseSolvers, PauseAfterCalving, FixNodesOnRails, &97SolversPaused, NewIceberg, GotNodes(4), CalvingFileCreated=.FALSE., SuppressCalv,&98DistributedMesh,SaveTerminus,RemeshFront,RemeshIfNoCalving99CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, CalvingVarName, MeshName, SolName, &100premmgls_meshfile, mmgls_meshfile, premmgls_solfile, mmgls_solfile,&101RepartMethod, Filename, DistVarName102TYPE(Variable_t), POINTER :: TimeVar103INTEGER :: Time, remeshtimestep, proc, idx, island, node, MaxLSetIter, mmgloops104REAL(KIND=dp) :: TimeReal, PreCalveVolume, PostCalveVolume, CalveVolume, LsetMinQuality105106SAVE :: WorkArray, CalvingFileCreated107108SolverParams => GetSolverParams()109SolverName = "CalvingRemeshMMG"110Debug=.FALSE.111Mesh => Model % Mesh112NNodes = Mesh % NumberOfNodes113NBulk = Mesh % NumberOfBulkElements114NBdry = Mesh % NumberOfBoundaryElements115116calv_front = 1117MyPe = ParEnv % MyPE118PEs = ParEnv % PEs119120#if MMG_VERSION_LT(5,6)121PRINT*, SolverName, ': Starting MMG'122#elif MMG_VERSION_LT(5,8)123PRINT*, SolverName, ': Starting MMG'124#else125CALL FATAL(SolverName, 'Calving code only works with MMG 5.5')126#endif127128TimeVar => VariableGet( Model % Mesh % Variables, 'Timestep' )129TimeReal = TimeVar % Values(1)130Time = INT(TimeReal)131132! create mmg variable as this is added to one proc in remeshing changes following merge Nov/23133mmgVar => VariableGet( Model % Mesh % Variables,'MMG Loop', ThisOnly = .TRUE.)134IF(.NOT. ASSOCIATED(mmgVar) ) THEN135CALL VariableAddVector( Model % Mesh % Variables,Model % Mesh,&136Name='MMG Loop',Global=.TRUE.)137mmgVar => VariableGet( Model % Mesh % Variables,'MMG Loop' )138END IF139140!for first time step calculate mesh volume141IF(Time == 1) THEN142CALL MeshVolume(Mesh, .TRUE., PreCalveVolume)143IF(MyPe == 0) PRINT*, 'First timestep precalve volume = ', PreCalveVolume144END IF145146!Check all elements are tetras or tris:147DO i=1,NBulk+Nbdry148Element => Mesh % Elements(i)149ecode = Element % TYPE % ElementCode150IF(ecode /= 101 .AND. ecode /= 202 .AND. ecode /= 303 .AND. ecode /= 504) THEN151PRINT *,MyPE,' has unsupported element type: ',ecode152CALL Fatal(SolverName, "Invalid Element Type!")153END IF154END DO155156!Get main Body ID157target_bodyid = Mesh % Elements(1) % BodyID158IF(target_bodyid /= 1) CALL Warn(SolverName, "Body ID is not 1, this case might not be well handled")159!For testing - set a calving levelset function to mimic calving events160!-------------------161162!TODO - unhardcode (detect?) this163front_BC_id = 1164front_body_id = ListGetInteger( &165Model % BCs(front_bc_id) % Values, 'Body Id', Found, 1, Model % NumberOfBodies )166167WorkArray => ListGetConstRealArray(SolverParams, "Mesh Hmin", Found)168IF(.NOT. Found) CALL FATAL(SolverName, 'Provide hmin input array to be iterated through: "Mesh Hmin"')169MaxLsetIter= SIZE(WorkArray(:,1))170ALLOCATE(hminarray(MaxLsetIter))171hminarray = WorkArray(:,1)172NULLIFY(WorkArray)173hmax = ListGetConstReal(SolverParams, "Mesh Hmax", DefValue=4000.0_dp)174hgrad = ListGetConstReal(SolverParams,"Mesh Hgrad", DefValue=0.5_dp)175WorkArray => ListGetConstRealArray(SolverParams, "Mesh Hausd", Found)176IF(.NOT. Found) CALL FATAL(SolverName, 'Provide hausd input array to be iterated through: "Mesh Hausd"')177IF(MaxLsetIter /= SIZE(WorkArray(:,1))) CALL FATAL(SolverName, 'The number of hmin options &178must equal the number of hausd options')179ALLOCATE(hausdarray(MaxLsetIter))180hausdarray = WorkArray(:,1)181NULLIFY(WorkArray)182remesh_thresh = ListGetConstReal(SolverParams,"Remeshing Distance", DefValue=1000.0_dp)183LsetMinQuality = ListGetConstReal(SolverParams,"Mesh Min Quality", DefValue=0.00001_dp)184RmcValue = ListGetConstReal(SolverParams,"Mesh Rmc Value", DefValue=1e-15_dp)185CalvingVarName = ListGetString(SolverParams, "Calving Variable Name", Found)186IF(.NOT. Found) THEN187CALL WARN(SolverName, "'Levelset Variable Name' not set so assuming 'Calving Lset'.")188CalvingVarName = "Calving LSet"189END IF190DistVarName = ListGetString(SolverParams, "Distance Variable Name", Found)191IF(.NOT. Found) THEN192CALL WARN(SolverName, "'Distance Variable Name' not set so assuming 'Distance'.")193DistVarName = "Distance"194END IF195SaveMMGMeshes = ListGetLogical(SolverParams,"Save MMGLS Meshes", DefValue=.FALSE.)196SaveMMGSols = ListGetLogical(SolverParams,"Save MMGLS Sols", DefValue=.FALSE.)197IF(SaveMMGMeshes) THEN198premmgls_meshfile = ListGetString(SolverParams, "Pre MMGLS Mesh Name", UnfoundFatal = .TRUE.)199mmgls_meshfile = ListGetString(SolverParams, "MMGLS Output Mesh Name", UnfoundFatal = .TRUE.)200END IF201IF(SaveMMGSols) THEN202premmgls_solfile = ListGetString(SolverParams, "Pre MMGLS Sol Name", UnfoundFatal = .TRUE.)203mmgls_solfile = ListGetString(SolverParams, "MMGLS Output Sol Name", UnfoundFatal = .TRUE.)204END IF205PauseAfterCalving = ListGetLogical(SolverParams, "Pause After Calving Event", Found)206IF(.NOT. Found) THEN207CALL Info(SolverName, "Can't find 'Pause After Calving Event' logical in Solver section, &208& assuming True")209PauseAfterCalving = .TRUE.210END IF211FixNodesOnRails = ListGetLogical(SolverParams,"Fix Nodes On Rails", DefValue=.TRUE.)212SuppressCalv = ListGetLogical(SolverParams,"Suppress Calving", DefValue=.FALSE.)213SaveTerminus = ListGetLogical(SolverParams,"Save Terminus", DefValue=.TRUE.)214RemeshFront = ListGetLogical(SolverParams,"Remesh Full Calving Front", Found)215IF(.NOT. Found) THEN216CALL Info(SolverName, "Not Found 'Remesh Full Calving Front' asssuming TRUE")217RemeshFront = .TRUE.218END IF219RemeshIfNoCalving = ListGetLogical(SolverParams,"Remesh if no calving", Found)220IF(.NOT. Found) THEN221CALL Info(SolverName, "Not Found 'Remesh if no calving' asssuming TRUE")222RemeshIfNoCalving = .TRUE.223END IF224225! calving algo passes through 202 elems to mmg226i = ListGetInteger( Model % Bodies(Mesh % Elements(1) % BodyId) % Values, 'Material')227CALL ListAddLogical(Model % Materials(i) % Values,'mmg No Angle Detection',.TRUE.)228229IF(ParEnv % MyPE == 0) THEN230PRINT *,ParEnv % MyPE,' hmin: ',hminarray231PRINT *,ParEnv % MyPE,' hmax: ',hmax232PRINT *,ParEnv % MyPE,' hgrad: ',hgrad233PRINT *,ParEnv % MyPE,' hausd: ',hausdarray234PRINT *,ParEnv % MyPE,' remeshing distance: ',remesh_thresh235PRINT *,ParEnv % MyPE,' Max Levelset Iterations ', MaxLsetIter236END IF237238!If FlowSolver failed to converge (usually a result of weird mesh), large unphysical239!calving events can be predicted. So, turn off CalvingOccurs, and ensure a remesh240!Also undo this iterations mesh update241NSFail = ListGetLogical(Model % Simulation, "Flow Solution Failed",CheckFlowConvergence)242CalvingOccurs = ListGetLogical( Model % Simulation, 'CalvingOccurs', Found )243RemeshOccurs=.TRUE.244IF(CheckFlowConvergence) THEN245IF(NSFail) THEN246CalvingOccurs = .FALSE.247RemeshOccurs = .TRUE.248CALL Info(SolverName, "Remeshing but not calving because NS failed to converge.")249END IF250END IF251IF(SuppressCalv) CalvingOccurs = .FALSE.252253ALLOCATE(remeshed_node(NNodes),&254fixed_node(NNodes))255remeshed_node = .FALSE.256fixed_node = .FALSE.257258! TO DO some other wah to define remeshed nodes259DistanceVar => VariableGet(Mesh % Variables, DistVarName, .TRUE., UnfoundFatal=.TRUE.)260ALLOCATE(test_dist(NNodes))261test_dist = DistanceVar % Values(DistanceVar % Perm(:))262remeshed_node = test_dist < remesh_thresh263264!Get the calving levelset function (-ve inside calving event, +ve in intact ice)265!-------------------266IF (CalvingOccurs) THEN267CalvingVar => VariableGet(Mesh % Variables, CalvingVarName, .TRUE., UnfoundFatal=.TRUE.)268269ALLOCATE(test_lset(NNodes),&270calved_node(NNodes)&271)272273calved_node = .FALSE.274test_lset = CalvingVar % Values(CalvingVar % Perm(:)) !TODO - quick&dirty, possibly zero perm?275calved_node = test_lset < 0.0276! calving front boundary nodes may have lset value greater than remesh dist277DO i=1, NNodes278IF(RemeshFront) THEN279newdist = MINVAL((/test_lset(i), test_dist(i)/))280ELSE281newdist = test_lset(i)282END IF283remeshed_node(i) = newdist < remesh_thresh284END DO285END IF286287my_calv_front = 0288IF(ANY(remeshed_node)) THEN289my_calv_front = 1 !this is integer (not logical) so we can later move to multiple calving fronts290END IF291292NCalvNodes = COUNT(remeshed_node)293294!TODO - could make this more efficient by cutting out some of the elements from the calved region.295!This region will be remeshed too, but we discard it, so the closer we can get to the edge of the296!calving event, the better.297298!Identify all elements which need to be sent (including those fixed in place)299!Here we also find extra nodes which are just beyond the remeshing threshold300!but which are in fixed elements, thus also need to be sent301ALLOCATE(elem_send(nbulk+nbdry))302elem_send = .FALSE.303304IF(my_calv_front > 0) THEN305!Each partition identifies (based on nodes), elements which need to be transferred306DO i=1,Nbulk307Element => Mesh % Elements(i)308CALL Assert(Element % ElementIndex == i, SolverName,"Misnumbered bulk element.")309NodeIndexes => Element % NodeIndexes310NElNodes = Element % TYPE % NumberOfNodes311IF(ANY(remeshed_node(NodeIndexes(1:NElNodes)))) THEN312elem_send(i) = .TRUE.313IF(.NOT. ALL(remeshed_node(NodeIndexes(1:NElNodes)))) THEN314fixed_node(NodeIndexes(1:NElNodes)) = .TRUE.315END IF316END IF317END DO318319remeshed_node = remeshed_node .OR. fixed_node320321!Cycle boundary elements, checking parent elems322!BC elements follow parents323DO i=NBulk+1, NBulk+NBdry324Element => Mesh % Elements(i)325ParentElem => Element % BoundaryInfo % Left326IF(.NOT. ASSOCIATED(ParentElem)) THEN327ParentElem => Element % BoundaryInfo % Right328END IF329CALL Assert(ASSOCIATED(ParentElem),SolverName,"Boundary element has no parent!")330IF(elem_send(ParentElem % ElementIndex)) elem_send(i) = .TRUE.331END DO332333END IF334DEALLOCATE(fixed_node) !reuse later335336!This does nothing yet but it will be important - determine337!the discrete calving zones, each of which will be separately remeshed338!by a nominated boss partition339! CALL CountCalvingEvents(Model, Mesh, ccount)340ccount = 1341342!Negotiate local calving boss - just one front for now343!-----------------------344ALLOCATE(pcalv_front(PEs))345pcalv_front = 0346CALL MPI_AllGather(my_calv_front, 1, MPI_INTEGER, pcalv_front, &3471, MPI_INTEGER, ELMER_COMM_WORLD, ierr)348349! loop to allow negotiation for multiple calving fronts350! This communication allows the determination of which part of the mesh351! has the most calvnodes and will become cboss352ALLOCATE(pNCalvNodes(MAXVAL(pcalv_front),PEs))353DO i=1, MAXVAL(pcalv_front)354CALL MPI_ALLGATHER(NCalvNodes, 1, MPI_INTEGER, pNCalvNodes(i,:), &3551, MPI_INTEGER, ELMER_COMM_WORLD, ierr)356END DO357358IF(Debug) THEN359PRINT *,mype,' debug calv_front: ',my_calv_front360PRINT *,mype,' debug calv_fronts: ',pcalv_front361END IF362363ncalv_parts = COUNT(pcalv_front==calv_front) !only one front for now...364ALLOCATE(cgroup_membs(ncalv_parts))365counter = 0366DO i=1,PEs367IF(pcalv_front(i) == calv_front) THEN !one calve front368counter = counter + 1369cgroup_membs(counter) = i-1370END IF371END DO372IF(Debug) PRINT *,mype,' debug group: ',cgroup_membs373374375!Create an MPI_COMM for each calving region, allow gathering instead of376!explicit send/receive377CALL MPI_Comm_Group( ELMER_COMM_WORLD, group_world, ierr)378CALL MPI_Group_Incl( group_world, ncalv_parts, cgroup_membs, group_calve, ierr)379CALL MPI_Comm_create( ELMER_COMM_WORLD, group_calve, COMM_CALVE, ierr)380!--------------------------381382!Work out to whom I send mesh parts for calving383!TODO - this is currently the lowest partition number which has some calving nodes,384!but it'd be more efficient to take the partition with the *most* calved nodes385!Now cboss set to part with most calving nodes386!NonCalvBoss will take any non calving nodes from the cboss387! this is still *TODO*388IF(My_Calv_Front>0) THEN389390! assume currently only one calving front391my_cboss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MAXVAL(pNCalvNodes(1,:)))-1392nonCalvBoss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MINVAL(pNCalvNodes(1,:)))-1393IF(Debug) PRINT *,MyPe,' debug calving boss: ',my_cboss394ImBoss = MyPE == my_cboss395396IF(ImBoss) THEN397ALLOCATE(Prnode_count(ncalv_parts))398Prnode_count = 0399END IF400ELSE401! only used if cboss has non calving nodes402nonCalvBoss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MINVAL(pNCalvNodes(1,:)))-1403my_cboss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MAXVAL(pNCalvNodes(1,:)))-1404ImBoss = .FALSE.405END IF406407!croot location is i when cboss = groupmember(i)408DO i=1, ncalv_parts409IF(my_cboss /= cgroup_membs(i)) CYCLE410croot = i-1411END DO412413!Redistribute the mesh (i.e. partitioning) so that cboss partitions414!contain entire calving/remeshing regions.415IF(.NOT. ASSOCIATED(Mesh % Repartition)) THEN416ALLOCATE(Mesh % Repartition(NBulk+NBdry), STAT=ierr)417IF(ierr /= 0) PRINT *,ParEnv % MyPE,' could not allocate Mesh % Repartition'418END IF419420!TODO send non calving nodes on ImBoss to nocalvboss421Mesh % Repartition = ParEnv % MyPE + 1422DO i=1,NBulk+NBdry423IF(elem_send(i)) Mesh % Repartition(i) = my_cboss+1424IF(ImBoss .AND. .NOT. elem_send(i)) THEN425WRITE(Message, '(A,i0,A)') "ImBoss sending Element ",i," to NonCalvBoss"426CALL WARN(SolverName, Message)427Mesh % Repartition(i) = NonCalvBoss+1428END IF429END DO430431GatheredMesh => RedistributeMesh(Model, Mesh, .TRUE., .FALSE.)432!Confirmed that boundary info is for Zoltan at this point433434IF(ASSOCIATED(Mesh % Repartition)) THEN435DEALLOCATE(Mesh % Repartition)436Mesh % Repartition => NULL()437END IF438439IF(Debug) THEN440PRINT *,ParEnv % MyPE,' gatheredmesh % nonodes: ',GatheredMesh % NumberOfNodes441PRINT *,ParEnv % MyPE,' gatheredmesh % neelems: ',GatheredMesh % NumberOfBulkElements, &442GatheredMesh % NumberOfBoundaryElements443END IF444445!Now we have the gathered mesh, need to send:446! - Remeshed_node, test_lset447! - we can convert this into an integer code on elements (0 = leave alone, 1 = remeshed, 2 = fixed)448! - or we can simply send test_lset and recompute on calving_boss449450IF(My_Calv_Front>0) THEN451!root is always zero because cboss is always lowest member of new group (0)452CALL MPI_Gather(COUNT(remeshed_node), 1, MPI_INTEGER, Prnode_count, 1, &453MPI_INTEGER, croot, COMM_CALVE,ierr)454455IF(ImBoss) THEN456457IF(Debug) PRINT *,'boss debug prnode_count: ', Prnode_count458GLNode_max = MAXVAL(GatheredMesh % ParallelInfo % GlobalDOFs)459GLNode_min = MINVAL(GatheredMesh % ParallelInfo % GlobalDOFs)460GNBulk = GatheredMesh % NumberOfBulkElements461GNBdry = GatheredMesh % NumberOfBoundaryElements462GNNode = GatheredMesh % NumberOfNodes463464ALLOCATE(PGDOFs_send(SUM(Prnode_count)), &465Ptest_dist(SUM(Prnode_count)), &466disps(ncalv_parts),GtoLNN(GLNode_min:GLNode_max),&467gtest_dist(GNNode),&468fixed_node(GNNode),&469fixed_elem(GNBulk + GNBdry))470471IF(CalvingOccurs) ALLOCATE(Ptest_lset(SUM(Prnode_count)), &472gtest_lset(GNNode))473fixed_node = .FALSE.474fixed_elem = .FALSE.475IF(CalvingOccurs) gtest_lset = remesh_thresh + 500.0 !Ensure any far (unshared) nodes are fixed476477!Compute the global to local map478DO i=1,GNNode479GtoLNN(GatheredMesh % ParallelInfo % GlobalDOFs(i)) = i480END DO481482ELSE483ALLOCATE(disps(1), prnode_count(1))484END IF485486!Compute the offset in the gathered array from each part487IF(ImBoss) THEN488disps(1) = 0489DO i=2,ncalv_parts490disps(i) = disps(i-1) + prnode_count(i-1)491END DO492END IF493494IF (CalvingOccurs) THEN495!Gather the level set function496CALL MPI_GatherV(PACK(test_lset,remeshed_node), COUNT(remeshed_node), MPI_DOUBLE_PRECISION, &497Ptest_lset, Prnode_count, disps, MPI_DOUBLE_PRECISION, croot, COMM_CALVE,ierr)498IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")499END IF500!Gather the distance to front, but let it output to Ptest_lset to avoid repetitive code501CALL MPI_GatherV(PACK(test_dist,remeshed_node), COUNT(remeshed_node), MPI_DOUBLE_PRECISION, &502Ptest_dist, Prnode_count, disps, MPI_DOUBLE_PRECISION, croot, COMM_CALVE,ierr)503IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")504!END IF505!Gather the GDOFs506CALL MPI_GatherV(PACK(Mesh % ParallelInfo % GlobalDOFs,remeshed_node), &507COUNT(remeshed_node), MPI_INTEGER, PGDOFs_send, Prnode_count, &508disps, MPI_INTEGER, croot, COMM_CALVE,ierr)509IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")510511!Nodes with levelset value greater than remesh_thresh are kept fixed512!as are any elements with any fixed_nodes513!This implies the levelset is a true distance function, everywhere in the domain.514IF(ImBoss) THEN515DO i=1,SUM(Prnode_count)516k = GtoLNN(PGDOFs_send(i))517IF(k==0) CALL Fatal(SolverName, "Programming error in Global to Local NNum map")518IF(CalvingOccurs) Gtest_lset(k) = Ptest_lset(i)519Gtest_dist(k) = Ptest_dist(i)520END DO521522! calving front boundary nodes may have lset value greater than remesh dist523! so use dist so front boundary nodes aren't fixed524IF(CalvingOccurs) THEN525DO i=1,GNNode526IF(RemeshFront) THEN527newdist = MINVAL((/Gtest_lset(i), Gtest_dist(i)/))528ELSE529newdist = Gtest_lset(i)530END IF531fixed_node(i) = newdist > remesh_thresh532END DO533ELSE534fixed_node = Gtest_dist > remesh_thresh535END IF536537DO i=1, GNBulk + GNBdry538Element => GatheredMesh % Elements(i)539IF(ANY(fixed_node(Element % NodeIndexes))) THEN540fixed_elem(i) = .TRUE.541END IF542END DO543END IF544END IF ! My calving front > 1545546! if no calving and we don't want to remesh then go to end547! need gathered mesh for terminus output548! also need to update mesh deformation variables549IF(.NOT. RemeshIfNoCalving .AND. .NOT. CalvingOccurs .AND. .NOT. NSFail) THEN550RSuccess =.FALSE.551GO TO 30552END IF553554!Nominated partition does the remeshing555IF(ImBoss) THEN556IF (CalvingOccurs) THEN557558CALL MeshVolume(GatheredMesh, .FALSE., PreCalveVolume)559560CALL GetCalvingEdgeNodes(GatheredMesh, .FALSE., EdgePairs, PairCount)561562mmgloops = 056356410 CONTINUE565566mmgloops=mmgloops+1567hmin = hminarray(mmgloops)568hausd = hausdarray(mmgloops)569570WRITE(Message, '(A,F10.5,A,F10.5)') 'Applying levelset with Hmin ',Hmin, ' and Hausd ', Hausd571CALL INFO(SolverName, Message)572!Initialise MMG datastructures573mmgMesh = 0574mmgLs = 0575mmgMet = 0576577CALL MMG3D_Init_mesh(MMG5_ARG_start, &578MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppLs,mmgLs, &579MMG5_ARG_end)580581! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)582CALL Set_MMG3D_Mesh(GatheredMesh, .TRUE., EdgePairs, PairCount)583584!Request isosurface discretization585CALL MMG3D_Set_iparameter(mmgMesh, mmgLs, MMGPARAM_iso, 1,ierr)586587!set angle detection on (1, default) and set threshold angle (85 degrees)588!TODO - here & in MeshRemesh, need a better strategy than automatic detection589!i.e. feed in edge elements.590591!I think these are defunct as they are called in RemeshMMG592! this is unless cutting lset and anisotrophic remeshing take place in one step593!print *, 'first call of angle detection $$$ - turned on '594CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_angle, &5950,ierr)596597!!! angle detection changes calving in next time steps dramatically598!! if on automatically turns angle on599!CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgSol,MMGPARAM_angleDetection,&600! 85.0_dp,ierr)601602!Turn on debug (1)603CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_debug, &6041,ierr)605606!Turn off automatic aniso remeshing (0)607CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_aniso, &6080,ierr)609610!Set geometric parameters for remeshing611CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hmin,&612hmin,ierr)613CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hmax,&614hmax,ierr)615CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hausd,&616hausd,ierr)617CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hgrad,&618hgrad,ierr)619620CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_rmc,&621RmcValue,ierr)622623CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgSol,MMGPARAM_nosizreq,&6241,ierr)625626!Feed in the level set distance627CALL MMG3D_SET_SOLSIZE(mmgMesh, mmgLs, MMG5_Vertex, GNNode ,MMG5_Scalar, ierr)628DO i=1,GNNode629CALL MMG3D_Set_scalarSol(mmgLs,&630Gtest_lset(i), &631i,ierr)632END DO633634IF(Debug) THEN635PRINT *,' boss fixed node: ',COUNT(fixed_node),SIZE(fixed_node)636PRINT *,' boss fixed elem: ',COUNT(fixed_elem),SIZE(fixed_elem)637END IF638639!Set required nodes and elements640DO i=1,GNNode641IF(fixed_node(i)) THEN642CALL MMG3D_SET_REQUIREDVERTEX(mmgMesh,i,ierr)643END IF644END DO645646DO i=1,GNBulk + GNBdry647IF(fixed_elem(i)) THEN648IF(GatheredMesh % Elements(i) % TYPE % DIMENSION == 3) THEN649CALL MMG3D_SET_REQUIREDTETRAHEDRON(mmgMesh,i,ierr)650ELSEIF(GatheredMesh % Elements(i) % TYPE % DIMENSION == 2) THEN651CALL MMG3D_SET_REQUIREDTRIANGLE(mmgMesh,i-GNBulk,ierr)652END IF653END IF654END DO655656!> 4) (not mandatory): check if the number of given entities match with mesh size657CALL MMG3D_Chk_meshData(mmgMesh,mmgLs,ierr)658IF ( ierr /= 1 ) CALL EXIT(105)659IF(SaveMMGMeshes) THEN660WRITE(MeshName, '(A,i0,A)') TRIM(premmgls_meshfile), time, '.mesh'661CALL MMG3D_SaveMesh(mmgMesh,MeshName,LEN(TRIM(MeshName)),ierr)662END IF663IF(SaveMMGSols) THEN664WRITE(SolName, '(A,i0,A)') TRIM(premmgls_solfile), time, '.sol'665CALL MMG3D_SaveSol(mmgMesh, mmgLs,SolName,LEN(TRIM(SolName)),ierr)666END IF667!> ------------------------------ STEP II --------------------------668!! remesh function669! mmg5.5 not using isosurface discretization. More robust to remesh seperately670! addtionally computationally lighter as iceberg are not finely remeshed671CALL MMG3D_mmg3dls(mmgMesh,mmgLs,0_dp,ierr)672673IF ( ierr == MMG5_STRONGFAILURE ) THEN674PRINT*,"BAD ENDING OF MMG3DLS: UNABLE TO SAVE MESH", Time675!! Release mmg mesh676CALL MMG3D_Free_all(MMG5_ARG_start, &677MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppMet,mmgSol, &678MMG5_ARG_end)679WRITE(Message, '(A,F10.5,A,F10.5)') 'Levelset failed with Hmin ',Hmin, ' and Hausd ', Hausd680CALL WARN(SolverName, Message)681IF(mmgloops==MaxLsetIter) THEN682Success=.FALSE.683CalvingOccurs = .FALSE.684GO TO 20685ELSE686GO TO 10687END IF688ELSE IF ( ierr == MMG5_LOWFAILURE ) THEN689PRINT*,"LOW ENDING OF MMG3DLS", time690ENDIF691IF(SaveMMGMeshes) THEN692WRITE(MeshName, '(A,i0,A)') TRIM(mmgls_meshfile), time, '.mesh'693CALL MMG3D_SaveMesh(mmgMesh,MeshName,LEN(TRIM(MeshName)),ierr)694END IF695IF(SaveMMGSols) THEN696WRITE(SolName, '(A,i0,A)') TRIM(mmgls_solfile), time, '.sol'697CALL MMG3D_SaveSol(mmgMesh, mmgLs,SolName,LEN(TRIM(SolName)),ierr)698END IF699700CALL MMG3D_Get_meshSize(mmgMesh,NVerts,NTetras,NPrisms,NTris,NQuads,NEdges,ierr)701702counter=0703Do i=1, NTetras704CALL MMG3D_Get_TetrahedronQuality(mmgMesh, mmgSol, i, Quality)705IF(Quality == 0) CALL WARN(SolverName, 'Levelset could not determine elem quality')706IF(Quality <= LsetMinQuality) counter = counter+1707END DO708IF ( Counter > 0 ) THEN709PRINT*,"Bad elements detected - reruning levelset"710!! Release mmg mesh711CALL MMG3D_Free_all(MMG5_ARG_start, &712MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppMet,mmgSol, &713MMG5_ARG_end)714WRITE(Message, '(A,F10.5,A,F10.5)') 'Levelset failed with Hmin ',Hmin, ' and Hausd ', Hausd715CALL WARN(SolverName, Message)716IF(mmgloops==MaxLsetIter) THEN717Success=.FALSE.718CalvingOccurs = .FALSE.719GO TO 20720ELSE721GO TO 10722END IF723!STOP MMG5_STRONGFAILURE724ENDIF725726CALL Get_MMG3D_Mesh(NewMeshR, .TRUE., new_fixed_node, new_fixed_elem, Calving=.TRUE.)727728NewMeshR % Name = Mesh % Name729730NNodes = NewMeshR % NumberOfNodes731NBulk = NewMeshR % NumberOfBulkElements732NBdry = NewMeshR % NumberOfBoundaryElements733IF(DEBUG) PRINT *, 'NewMeshR nonodes, nbulk, nbdry: ',NNodes, NBulk, NBdry734735!Clear out unneeded elements736!Body elems with BodyID 3 (2) are the calving event (remaining domain)737!Boundary elems seem to have tags 0,1,10...738! 0 seems to be the edge triangles, 1 the outer surface (all bcs) and 2 the new level set surf739ALLOCATE(RmElem(NBulk+NBdry), RmNode(NNodes))740RmElem = .FALSE.741RmNode = .TRUE.742743DO i=1,NBulk744Element => NewMeshR % Elements(i)745NElNodes = Element % TYPE % NumberOfNodes746747IF(Element % TYPE % ElementCode /= 504) &748PRINT *,'Programming error, bulk type: ',Element % TYPE % ElementCode749IF(NElNodes /= 4) PRINT *,'Programming error, bulk nonodes: ',NElNodes750751!outbody - mark for deletion and move on752IF(Element % BodyID == 3) THEN753RmElem(i) = .TRUE.754!Deal with an MMG3D eccentricity - returns erroneous GlobalDOF = 10755!on some split calving elements756NewMeshR % ParallelInfo % GlobalDOFs(Element % NodeIndexes) = 0757CYCLE758ELSE IF(Element % BodyID == 2) THEN759Element % BodyID = target_bodyid760ELSE761PRINT *,'Erroneous body id: ',Element % BodyID762CALL Fatal(SolverName, "Bad body id!")763END IF764765!Get rid of any isolated elements (I think?)766! This may not be necessary, if the calving levelset is well defined767CALL MMG3D_Get_AdjaTet(mmgMesh, i, adjList,ierr)768IF(ALL(adjList == 0)) THEN769770!check if this is truly isolated or if it's at a partition boundary771isolated = .TRUE.772gdofs = NewMeshR % ParallelInfo % GlobalDOFs(Element % NodeIndexes(1:NELNodes))773DO j=1,GatheredMesh % NumberOfNodes774IF(ANY(gdofs == GatheredMesh % ParallelInfo % GlobalDOFs(j)) .AND. &775GatheredMesh % ParallelInfo % GInterface(j)) THEN776isolated = .FALSE.777EXIT778END IF779END DO780781IF(isolated) THEN782RmElem(i) = .TRUE.783CALL WARN(SolverName, 'Found an isolated body element.')784END IF785END IF786787!Mark all nodes in valid elements for keeping788RmNode(Element % NodeIndexes(1:NElNodes)) = .FALSE.789END DO790791!Same thru the boundary elements792!If their parent elem is body 3, delete, *unless* they have constraint 10 (the calving face)793!in which case use mmg3d function to get the adjacent tetras to find the valid parent794DO i=NBulk+1, NBulk + NBdry795Element => NewMeshR % Elements(i)796NElNodes = Element % TYPE % NumberOfNodes797798!Get rid of non-triangles799IF(Element % TYPE % ElementCode /= 303) THEN800RmElem(i) = .TRUE.801CYCLE802END IF803804!Get rid of boundary elements without BCID (newly created internal)805IF(Element % BoundaryInfo % Constraint == 0) THEN806RmElem(i) = .TRUE.807CYCLE808END IF809810ParentElem => Element % BoundaryInfo % Left811812IF(ParentElem % BodyID == 3) THEN813814!Not needed815IF(Element % BoundaryInfo % Constraint /= 10) THEN816!TODO, test constraint == 10 for other BC numbers on front817818RmElem(i) = .TRUE.819820!Switch parent elem to elem in remaining domain821ELSE822CALL MMG3D_Get_AdjaTet(mmgMesh, ParentElem % ElementIndex, adjList,ierr)823DO j=1,4824IF(adjlist(j) == 0) CYCLE825IF(NewMeshR % Elements(adjlist(j)) % BodyID == target_bodyid) THEN826Element % BoundaryInfo % Left => NewMeshR % Elements(adjList(j))827EXIT828END IF829END DO830END IF831832!Edge case - unconnected bulk element833ELSE IF(RmElem(ParentElem % ElementIndex)) THEN834RmElem(i) = .TRUE.835END IF836837END DO838839!! Release mmg mesh840CALL MMG3D_Free_all(MMG5_ARG_start, &841MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppLs,mmgLs, &842MMG5_ARG_end)843844!MMG3DLS returns constraint = 10 on newly formed boundary elements845!(i.e. the new calving front). Here it is set to front_BC_id846!And set all BC BodyIDs based on constraint847DO i=NBulk+1, NBulk + NBdry848849IF(RmElem(i)) CYCLE850851Element => NewMeshR % Elements(i)852geom_id => Element % BoundaryInfo % Constraint853854IF(geom_id == 10) THEN855geom_id = front_BC_id856857Element % BodyId = front_body_id858END IF859860CALL Assert((geom_id > 0) .AND. (geom_id <= Model % NumberOfBCs),&861SolverName,"Unexpected BC element body id!")862863Element % BodyId = ListGetInteger( &864Model % BCs(geom_id) % Values, 'Body Id', Found, 1, Model % NumberOfBodies )865END DO866867868!TODO - issue - MMG3Dls will mark new nodes 'required' if they split a previous869!boundary element. but only *front* boundary elements? Have confirmed it ONLY returns870!required & 10 for calving front nodes, and it's not to do with manually setting required stuff.871!But, the model does complain about required entities if the hole is internal, but no weird872!GIDs are returned.873IF(Debug) THEN874DO i=1,GatheredMesh % NumberOfNodes875PRINT *, ParEnv % MyPE,' debug old ',i,&876' GDOF: ',GatheredMesh % ParallelInfo % GlobalDOFs(i),&877' xyz: ',&878GatheredMesh % Nodes % x(i),&879GatheredMesh % Nodes % y(i),&880GatheredMesh % Nodes % z(i),fixed_node(i)881IF(fixed_node(i)) THEN882IF(.NOT. ANY(NewMeshR % ParallelInfo % GlobalDOFs == &883GatheredMesh % ParallelInfo % GlobalDOFs(i))) CALL Fatal(SolverName, &884"Missing required node in output!")885END IF886END DO887END IF888889!output calving stats to file and find maxbergvolume890CALL CalvingStatsMMG(SolverParams, NewMeshR, RmNode, RmElem, &891CalvingFileCreated, MaxBergVolume)892893!Chop out the flagged elems and nodes894CALL CutMesh(NewMeshR, RmNode, RmElem)895896NNodes = NewMeshR % NumberOfNodes897NBulk = NewMeshR % NumberOfBulkElements898NBdry = NewMeshR % NumberOfBoundaryElements899900IF(RemeshFront) THEN ! if just remesh where calving occurs cannot remove901!islands as mesh already split902!sometimes some icebergs are not removed fully from the MMG levelset903!find all connected nodes in groups and remove in groups not in the904!main icebody905!limit here of 10 possible mesh 'islands'906ALLOCATE(FoundNode(10, NNodes), ElNodes(4), &907UsedElem(NBulk))908FoundNode = .FALSE.! count of nodes found909UsedElem = .FALSE. !count of elems used910island=0 ! count of different mesh islands911NoNewNodes=.TRUE. ! whether node has neighour912DO WHILE(COUNT(FoundNode) < NNodes)913IF(NoNewNodes) THEN914island = island + 1915NewIceberg = .TRUE.916END IF917NoNewNodes = .TRUE.918DO i=1, NBulk919IF(UsedElem(i)) CYCLE920Element => NewMeshR % Elements(i)921ElNodes = Element % NodeIndexes922! if there are not any matching nodes and its not a new iceberg923DO j=1, 4924GotNodes(j) = ANY(FoundNode(:, ElNodes(j)))925END DO926IF(ALL(.NOT. GotNodes) .AND. .NOT. NewIceberg) CYCLE927NewIceberg = .FALSE.928UsedElem(i) = .TRUE.929FoundNode(island, ElNodes) = .TRUE.930NoNewNodes = .FALSE.931END DO932END DO933934ALLOCATE(IslandCounts(10))935DO i=1,10936IslandCounts(i) = COUNT(FoundNode(i, :))937END DO938Island = COUNT(IslandCounts > 0)939DEALLOCATE(IslandCounts) ! reused940941! if iceberg not removed mark nodes and elems942IF(Island > 1) THEN943CALL WARN(SolverName, 'Mesh island found after levelset- removing...')944ALLOCATE(RmIslandNode(NNodes), RmIslandElem(Nbulk+NBdry),&945IslandCounts(Island))946RmIslandNode = .FALSE.947RmIslandElem = .FALSE.948counter=0949DO i=1, 10950IF(COUNT(FoundNode(i, :)) == 0) CYCLE951counter=counter+1952IslandCounts(counter) = COUNT(FoundNode(i, :))953END DO954DO i=1, Island955IF(IslandCounts(i) < MAXVAL(IslandCounts)) THEN956DO j=1, NNodes957IF(.NOT. FoundNode(i,j)) CYCLE! if not part of island958RmIslandNode(j) = .TRUE.959END DO960END IF961END DO962! mark all elems with rm nodes as need removing963nodes = PACK((/ (i,i=1,SIZE(RmIslandNode)) /), RmIslandNode .eqv. .TRUE.)964DO i=1, Nbulk+Nbdry965Element => NewMeshR % Elements(i)966ElNodes = Element % NodeIndexes967!any([(any(A(i) == B), i = 1, size(A))])968IF( .NOT. ANY([(ANY(ElNodes(i)==Nodes), i = 1, SIZE(ElNodes))])) CYCLE969RmIslandElem(i) = .TRUE.970END DO971972!Chop out missed iceberg if they exist973CALL CutMesh(NewMeshR, RmIslandNode, RmIslandElem)974975!modify rmelem and rmnode to include island removal976counter=0977DO i=1, SIZE(RmElem)978IF(RmElem(i)) CYCLE979counter=counter+1980IF(RmIslandElem(Counter)) RmElem(i) =.TRUE.981END DO982counter=0983DO i=1, SIZE(RmNode)984IF(RmNode(i)) CYCLE985counter=counter+1986IF(RmIslandNode(Counter)) RmNode(i) =.TRUE.987END DO988CALL INFO(SolverName, 'Mesh island removed', level=10)989END IF990END IF !remeshfront991992END IF ! CalvingOccurs99399420 CONTINUE995996DEALLOCATE(hminarray, hausdarray)997998DoAniso = .TRUE.999IF(DoAniso .AND. CalvingOccurs) THEN10001001new_fixed_elem = PACK(new_fixed_elem, .NOT. RmElem)1002new_fixed_node = PACK(new_fixed_node, .NOT. RmNode)10031004IF(Debug) THEN1005DO i=1,NewMeshR % NumberOfNodes1006PRINT *, ParEnv % MyPE,' debug new ',i,&1007' GDOF: ',NewMeshR % ParallelInfo % GlobalDOFs(i),&1008' xyz: ',&1009NewMeshR % Nodes % x(i),&1010NewMeshR % Nodes % y(i),&1011NewMeshR % Nodes % z(i)10121013IF(NewMeshR % ParallelInfo % GlobalDOFs(i) == 0) CYCLE1014IF(.NOT. ANY(GatheredMesh % ParallelInfo % GlobalDOFs == &1015NewMeshR % ParallelInfo % GlobalDOFs(i))) CALL Warn(SolverName, &1016"Unexpected GID")1017END DO1018END IF10191020!Anisotropic mesh improvement following calving cut:1021!TODO - unhardcode! Also this doesn't work very well yet.1022!ALLOCATE(target_length(NewMeshR % NumberOfNodes,3))1023!target_length(:,1) = 300.01024!target_length(:,2) = 300.01025!target_length(:,3) = 50.010261027!Parameters for anisotropic remeshing are set in the Materials section, or can &1028!optionally be passed as a valuelist here. They have prefix RemeshMMG3D1029!TODO - apparently there is beta-testing capability to do both levelset cut and anisotropic1030!remeshing in the same step:1031!https://forum.mmgtools.org/t/level-set-and-anisotropic-mesh-optimization/369/31032! GetCalvingEdgeNodes detects all shared boundary edges, to keep them sharp10331034! calculate calved volume1035CALL MeshVolume(NewMeshR, .FALSE., PostCalveVolume)10361037CalveVolume = PreCalveVolume - PostCalveVolume10381039PRINT*, 'CalveVolume', CalveVolume, 'MaxBergVolume', MaxBergVolume10401041CALL GetCalvingEdgeNodes(NewMeshR, .FALSE., REdgePairs, RPairCount)1042! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)1043CALL RemeshMMG3D(Model, NewMeshR, NewMeshRR,REdgePairs, RPairCount,&1044NodeFixed=new_fixed_node, ElemFixed=new_fixed_elem, Success=RSuccess)1045IF(.NOT. RSuccess) THEN1046CALL WARN(SolverName, 'Remeshing failed - no calving at this timestep')1047GO TO 30 ! remeshing failed so no calving at this timestep1048END IF1049CALL ReleaseMesh(NewMeshR)1050NewMeshR => NewMeshRR1051NewMeshRR => NULL()10521053!Update parallel info from old mesh nodes (shared node neighbours)1054CALL MapNewParallelInfo(GatheredMesh, NewMeshR)10551056ELSE IF (DoAniso) THEN1057! remeshing but no calving1058CALL GetCalvingEdgeNodes(GatheredMesh, .FALSE., REdgePairs, RPairCount)1059! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)1060CALL RemeshMMG3D(Model, GatheredMesh, NewMeshR,REdgePairs, RPairCount,&1061NodeFixed=fixed_node, ElemFixed=fixed_elem, Success=RSuccess)1062!Update parallel info from old mesh nodes (shared node neighbours)1063IF(.NOT. RSuccess) THEN1064CALL WARN(SolverName, 'Remeshing failed - no calving at this timestep')1065GO TO 30 ! remeshing failed so no calving at this timestep1066END IF1067CALL MapNewParallelInfo(GatheredMesh, NewMeshR)10681069ELSE ! Not DoAniso10701071!Update parallel info from old mesh nodes (shared node neighbours)1072IF (CalvingOccurs) CALL MapNewParallelInfo(GatheredMesh, NewMeshR)10731074END IF10751076CALL ReleaseMesh(GatheredMesh)1077! CALL ReleaseMesh(NewMeshR)1078GatheredMesh => NewMeshR1079NewMeshR => NULL()1080NewMeshRR => NULL()1081END IF ! ImBoss1082108330 CONTINUE10841085!Wait for all partitions to finish10861087CALL MPI_GROUP_FREE(group_world,ierr)1088CALL MPI_GROUP_FREE(group_calve,ierr)1089IF(My_Calv_Front > 0) &1090CALL MPI_COMM_FREE(COMM_CALVE,ierr)10911092CALL MPI_BARRIER(ELMER_COMM_WORLD,ierr)10931094CALL MPI_BCAST(CalvingFileCreated, 1, MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)1095CALL MPI_BCAST(RSuccess, 1, MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)1096IF(.NOT. RSuccess) THEN1097IF(ImBoss .AND. CalvingFileCreated) THEN1098Filename = ListGetString(SolverParams,"Calving Stats File Name", Found)1099IF(.NOT. Found) THEN1100CALL WARN('CalvingStat', 'Output file name not given so using CalvingStats.txt')1101Filename = "CalvingStats.txt"1102END IF1103OPEN( 36, FILE=filename, STATUS='UNKNOWN', POSITION='APPEND')1104WRITE(36, '(A,i0)') 'Remeshing failed: ', GetTimestep()1105CLOSE(36)1106END IF11071108CALL ReleaseMesh(GatheredMesh)1109! Release GatheredMesh % Redistribution1110IF(ASSOCIATED(GatheredMesh % Repartition)) THEN1111DEALLOCATE(GatheredMesh % Repartition)1112GatheredMesh % Repartition => NULL()1113END IF1114SolversPaused = ListGetLogical(Model % Simulation, 'Calving Pause Solvers', DefValue=.FALSE.)1115!remove mesh update1116IF(.NOT. SolversPaused) THEN1117CALL ResetMeshUpdate(Model, Solver)1118ELSE ! solvers paused so mesh must have changed since last free surface1119Mesh % MeshTag = Mesh % MeshTag + 11120END IF11211122! make sure solvers are unpaused so front advances1123CALL PauseCalvingSolvers(Model, SolverParams, .FALSE.)1124RETURN1125END IF11261127!Now each partition has GatheredMesh, we need to renegotiate globalDOFs1128CALL RenumberGDOFs(Mesh, GatheredMesh)11291130!and global element numbers1131CALL RenumberGElems(GatheredMesh)11321133!Some checks on the new mesh1134!----------------------------1135DO i=1,GatheredMesh % NumberOfNodes1136IF(GatheredMesh % ParallelInfo % GInterface(i)) THEN1137IF(.NOT. ASSOCIATED(GatheredMesh % ParallelInfo % Neighbourlist(i) % Neighbours)) &1138CALL Fatal(SolverName, "Neighbourlist not associated!")1139IF(SIZE(GatheredMesh % ParallelInfo % Neighbourlist(i) % Neighbours) < 2) &1140CALL Fatal(SolverName, "Neighbourlist too small!")1141END IF1142END DO11431144DO i=1,GatheredMesh % NumberOfNodes1145IF(.NOT. ASSOCIATED(GatheredMesh % ParallelInfo % NeighbourList(i) % Neighbours)) &1146CALL Fatal(SolverName, 'Unassociated Neighbourlist % Neighbours')1147IF(GatheredMesh % ParallelInfo % GlobalDOFs(i) == 0) &1148CALL Fatal(SolverName, 'Bad GID 0')1149END DO11501151ParEnv % IsNeighbour(:) = .FALSE.1152DO i=1, Mesh % NumberOfNodes1153IF ( ASSOCIATED(Mesh % ParallelInfo % NeighbourList(i) % Neighbours) ) THEN1154DO j=1,SIZE(Mesh % ParallelInfo % NeighbourList(i) % Neighbours)1155proc = Mesh % ParallelInfo % NeighbourList(i) % Neighbours(j)1156IF ( ParEnv % Active(proc+1).AND.proc/=ParEnv % MYpe) &1157ParEnv % IsNeighbour(proc+1) = .TRUE.1158END DO1159END IF1160END DO11611162IF(SaveTerminus) THEN1163IF(RemeshFront) THEN ! got entire front1164CALL SaveTerminusPosition(Model, Solver, GatheredMesh, ImBoss)1165ELSE ! only remeshing where bergs calve1166CALL MakePermUsingMask( Model, Solver, GatheredMesh, "Calving Front Mask", &1167.FALSE., FrontPerm, dummyint)1168CALL MakePermUsingMask( Model, Solver, GatheredMesh, "Top Surface Mask", &1169.FALSE., TopPerm, dummyint)11701171IF(.NOT. ASSOCIATED(GatheredMesh % Repartition)) THEN1172ALLOCATE(GatheredMesh % Repartition(GatheredMesh % NumberOfBulkElements+&1173GatheredMesh % NumberOfBoundaryElements), STAT=ierr)1174IF(ierr /= 0) PRINT*, ParEnv % MyPE, 'Unable to allocate mesh % repartition'1175END IF11761177GatheredMesh % Repartition = ParEnv % MyPE + 11178IF(ImBoss) DEALLOCATE(fixed_node)1179DEALLOCATE(elem_send)1180ALLOCATE(SendNode(GatheredMesh % NumberOfNodes), &1181fixed_node(GatheredMesh % NumberOfNodes))1182SendNode = .FALSE.; fixed_node = .FALSE.1183DO i=1, GatheredMesh % NumberOfNodes1184IF( (TopPerm(i)>0) .AND. (FrontPerm(i)>0)) THEN1185SendNode(i) = .TRUE.1186END IF1187END DO11881189ALLOCATE(elem_send(GatheredMesh % NumberOfBulkElements+&1190GatheredMesh % NumberOfBoundaryElements))1191elem_send = .FALSE.1192DO i=1,GatheredMesh % NumberOfBulkElements1193Element => GatheredMesh % Elements(i)1194CALL Assert(Element % ElementIndex == i, SolverName,"Misnumbered bulk element.")1195NodeIndexes => Element % NodeIndexes1196NElNodes = Element % TYPE % NumberOfNodes1197IF(ANY(SendNode(NodeIndexes(1:NElNodes)))) THEN1198elem_send(i) = .TRUE.1199IF(.NOT. ALL(SendNode(NodeIndexes(1:NElNodes)))) THEN1200fixed_node(NodeIndexes(1:NElNodes)) = .TRUE.1201END IF1202END IF1203END DO12041205SendNode = SendNode .OR. fixed_node12061207!Cycle boundary elements, checking parent elems1208!BC elements follow parents1209DO i=GatheredMesh % NumberOfBulkElements+1, GatheredMesh % NumberOfBulkElements+&1210GatheredMesh % NumberOfBoundaryElements1211Element => GatheredMesh % Elements(i)1212ParentElem => Element % BoundaryInfo % Left1213IF(.NOT. ASSOCIATED(ParentElem)) THEN1214ParentElem => Element % BoundaryInfo % Right1215END IF1216CALL Assert(ASSOCIATED(ParentElem),SolverName,"Boundary element has no parent!")1217IF(elem_send(ParentElem % ElementIndex)) elem_send(i) = .TRUE.1218END DO12191220DO i=1, GatheredMesh % NumberOfBulkElements + GatheredMesh % NumberOfBoundaryElements1221IF(Elem_send(i)) GatheredMesh % RePartition(i) = my_cboss + 11222END DO12231224ParMetisMesh => RedistributeMesh(Model, GatheredMesh, .TRUE., .FALSE.)12251226CALL SaveTerminusPosition(Model, Solver, ParMetisMesh, ImBoss)12271228CALL ReleaseMesh(ParMetisMesh)1229ParMetisMesh => NULL()1230DEALLOCATE(FrontPerm,TopPerm)1231END IF ! remeshfront1232END IF ! saveterminus12331234!Call zoltan to determine redistribution of mesh1235! then do the redistribution1236!-------------------------------12371238RepartMethod = ListGetString(Model % Solver % Values,"Repartition Method", Found)1239SELECT CASE( RepartMethod )1240CASE( 'parmetis' ) ! bit of a hack here. Parmetis requires fully distributed mesh so ensure all parts have one element1241ALLOCATE(PartGotNodes(ParEnv % PEs))1242CALL MPI_ALLGATHER(GatheredMesh % NumberOfNodes > 0, 1, MPI_LOGICAL, PartGotNodes, &12431, MPI_LOGICAL, ELMER_COMM_WORLD, ierr)12441245DistributedMesh = ALL(PartGotNodes)12461247IF(.NOT. ASSOCIATED(GatheredMesh % Repartition)) THEN1248ALLOCATE(GatheredMesh % Repartition(GatheredMesh % NumberOfBulkElements+&1249GatheredMesh % NumberOfBoundaryElements), STAT=ierr)1250IF(ierr /= 0) PRINT *,ParEnv % MyPE,' could not allocate Mesh % Repartition'1251END IF12521253GatheredMesh % Repartition = ParEnv % MyPE + 11254IF(ImBoss) THEN1255counter=01256DO i=1,ParEnv % PEs1257IF(PartGotNodes(i)) CYCLE ! got nodes1258counter=counter+11259IF(counter > GatheredMesh % NumberOfNodes) &1260CALL FATAL(SolverName, 'CalvBoss does not have enough elems to share for ParMetis repartitioning')1261GatheredMesh % Repartition(counter) = i1262DO j=GatheredMesh % NumberOfBulkElements+1, GatheredMesh % NumberOfBulkElements+&1263GatheredMesh % NumberOfBoundaryElements1264Element => GatheredMesh % Elements(j)1265ParentElem => Element % BoundaryInfo % Left1266IF(.NOT. ASSOCIATED(ParentElem)) THEN1267ParentElem => Element % BoundaryInfo % Right1268END IF1269CALL Assert(ASSOCIATED(ParentElem),SolverName,"Boundary element has no parent!")1270IF(ParentElem % ElementIndex == counter) THEN1271GatheredMesh % Repartition(j) = i1272END IF1273END DO1274END DO1275END IF12761277ParMetisMesh => RedistributeMesh(Model, GatheredMesh, .TRUE., .FALSE.)12781279IF(ASSOCIATED(ParMetisMesh % Repartition)) THEN1280DEALLOCATE(ParMetisMesh % Repartition)1281ParMetisMesh % Repartition => NULL()1282END IF12831284CALL ReleaseMesh(GatheredMesh)1285GatheredMesh => ParMetisMesh1286ParMetisMesh => NULL()1287END SELECT12881289CALL Zoltan_Interface( Model, GatheredMesh, StartImbalanceTol=1.1_dp, TolChange=0.02_dp, MinElems=10 )12901291FinalMesh => RedistributeMesh(Model, GatheredMesh, .TRUE., .FALSE.)12921293CALL CheckMeshQuality(FinalMesh)12941295FinalMesh % Name = TRIM(Mesh % Name)1296FinalMesh % OutputActive = .TRUE.1297FinalMesh % Changed = .TRUE.12981299!Actually switch the model's mesh1300CALL SwitchMesh(Model, Solver, Mesh, FinalMesh)13011302!After SwitchMesh because we need GroundedMask1303CALL EnforceGroundedMask(Model, Mesh)13041305CALL CheckBaseFreeSurface(Model, Mesh, 0.01_dp)13061307IF(FixNodesOnRails) CALL EnforceLateralMargins(Model, Solver % Values)13081309!Calculate mesh volume1310CALL MeshVolume(Model % Mesh, .TRUE., PostCalveVolume)1311IF(MyPe == 0) PRINT*, 'Post calve volume: ', PostCalveVolume, ' after timestep', Time13121313!Recompute mesh bubbles etc1314CALL MeshStabParams( Model % Mesh )13151316!Release the old mesh1317CALL ReleaseMesh(GatheredMesh)13181319! Release GatheredMesh % Redistribution1320IF(ASSOCIATED(GatheredMesh % Repartition)) THEN1321DEALLOCATE(GatheredMesh % Repartition)1322GatheredMesh % Repartition => NULL()1323END IF13241325!remove mesh update1326CALL ResetMeshUpdate(Model, Solver)13271328!pause solvers?1329IF(PauseAfterCalving) THEN1330IF(ImBoss) THEN1331PauseVolumeThresh = ListGetConstReal(SolverParams, "Pause Solvers Minimum Iceberg Volume", Found)1332IF(.NOT. Found) THEN1333CALL WARN(SolverName, "'Pause Solvers Minimum Iceberg Volume' not provided do assuming is 0")1334PauseVolumeThresh = 0.0_dp1335END IF13361337PauseSolvers = MaxBergVolume > PauseVolumeThresh .AND. RSuccess1338END IF13391340CALL MPI_BCAST(PauseSolvers, 1, MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)13411342CALL PauseCalvingSolvers(Model, SolverParams, PauseSolvers)1343END IF13441345END SUBROUTINE CalvingRemeshMMG13461347SUBROUTINE CheckFlowConvergenceMMG( Model, Solver, dt, Transient )13481349USE CalvingGeometry13501351IMPLICIT NONE13521353TYPE(Model_t) :: Model1354TYPE(Solver_t) :: Solver1355REAL(KIND=dp) :: dt1356LOGICAL :: Transient1357!-------------------------------------1358TYPE(Mesh_t), POINTER :: Mesh1359TYPE(Solver_t) :: RemeshSolver1360TYPE(Variable_t), POINTER :: FlowVar, TimeVar1361TYPE(ValueList_t), POINTER :: Params, FuncParams1362LOGICAL :: Parallel, Found, CheckFlowDiverge=.TRUE., CheckFlowMax, FirstTime=.TRUE.,&1363NSDiverge, NSFail, NSTooFast, ChangeMesh, NewMesh, CalvingOccurs, GotSaveMetric,&1364CalvingPauseSolvers, PNSFail=.FALSE.1365REAL(KIND=dp) :: SaveNewtonTol, MaxNSDiverge, MaxNSValue, FirstMaxNSValue, FlowMax,&1366SaveFlowMax, Mag, NSChange, SaveDt, SaveRelax,SaveMeshHmin,SaveMeshHmax,&1367SaveMeshHgrad,SaveMeshHausd, SaveMetric, MeshChange, NewMetric, NewMeshDist,&1368SaveMeshDist, NewMeshHGrad1369#ifdef ELMER_BROKEN_MPI_IN_PLACE1370REAL(KIND=dp) :: buffer1371#endif1372REAL(KIND=dp), ALLOCATABLE :: SaveMeshHausdArray(:,:), SaveMeshHminArray(:,:), &1373NewMeshHausdArray(:,:), NewMeshHminArray(:,:)1374REAL(KIND=dp), POINTER :: TimestepSizes(:,:), WorkArray(:,:)1375INTEGER :: i,j,SaveNewtonIter,Num, ierr, FailCount, TimeIntervals, SaveSSiter,&1376MaxRemeshIter,SaveNLIter,CurrentNLIter,NewMeshCount1377CHARACTER(MAX_NAME_LEN) :: FlowVarName, SolverName, EqName, RemeshEqName13781379SAVE ::SaveNewtonTol, SaveNewtonIter, SaveFlowMax, FirstTime, FailCount,&1380SaveRelax,SaveMeshHminArray,SaveMeshHmax,SaveMeshHausdArray,SaveMeshHgrad, &1381SaveSSiter, MaxRemeshIter, SaveNLIter, NewMesh, NewMeshCount,&1382SaveMetric, GotSaveMetric, MeshChange, NewMeshHausdArray, NewMeshHminArray,&1383NewMetric, SaveMeshDist, NewMeshDist, NewMeshHGrad, PNSFail13841385Mesh => Solver % Mesh1386SolverName = 'CheckFlowConvergenceMMG'1387Params => Solver % Values1388Parallel = (ParEnv % PEs > 1)1389FuncParams => GetMaterial(Mesh % Elements(1)) !TODO, this is not generalised1390FlowVarName = ListGetString(Params,'Flow Solver Name',Found)1391IF(.NOT. Found) FlowVarName = "Flow Solution"1392FlowVar => VariableGet(Mesh % Variables, FlowVarName, .TRUE., UnfoundFatal=.TRUE.)13931394RemeshEqName = ListGetString(Params,'Remesh Equation Name',Found)1395IF(.NOT. Found) RemeshEqName = "remesh"13961397!Get a handle to the remesh solver1398Found = .FALSE.1399DO j=1,Model % NumberOfSolvers1400IF(ListGetString(Model % Solvers(j) % Values, "Equation") == RemeshEqName) THEN1401RemeshSolver = Model % Solvers(j)1402Found = .TRUE.1403EXIT1404END IF1405END DO1406IF(.NOT. Found) CALL Fatal(SolverName, "Failed to get handle to Remesh Solver.")14071408IF(FirstTime) THEN14091410FailCount = 01411NewMeshCount = 014121413SaveNewtonTol = ListGetConstReal(FlowVar % Solver % Values, &1414"Nonlinear System Newton After Tolerance", Found)1415IF(.NOT. Found) SaveNewtonTol = 1.0E-31416SaveNewtonIter = ListGetInteger(FlowVar % Solver % Values, &1417"Nonlinear System Newton After Iterations", Found)1418IF(.NOT. Found) SaveNewtonIter = 151419SaveNLIter = ListGetInteger(FlowVar % Solver % Values, &1420"Nonlinear System Max Iterations", Found)1421IF(.NOT. Found) SaveNewtonIter = 5014221423SaveRelax = ListGetConstReal(FlowVar % Solver % Values, &1424"Nonlinear System Relaxation Factor", Found)1425IF(.NOT. Found) SaveRelax = 1.01426PRINT *, 'TO DO: replace with MMG stuff, hausdorff?'1427! Use RemeshMMG3D Hmin in Material or Mesh Hmin in RemeshSolver1428! should remesh without calving/cutting as well, so take RemeshMMG3D1429!SaveMeshHmin = ListGetConstReal(FuncParams, "MMG Hmin", Found, UnfoundFatal=.TRUE.)1430SaveMeshHmax = ListGetConstReal(FuncParams, "MMG Hmax", Found, UnfoundFatal=.TRUE.)1431!SaveMeshHausd = ListGetConstReal(FuncParams, "MMG Hausd", Found, UnfoundFatal=.TRUE.)1432SaveMeshHgrad = ListGetConstReal(FuncParams, "MMG Hgrad", Found, UnfoundFatal=.TRUE.)1433SaveMeshDist = ListGetConstReal(RemeshSolver % Values, "Remeshing Distance", Found, UnfoundFatal=.TRUE.)14341435WorkArray => ListGetConstRealArray(FuncParams, "MMG Hmin", Found)1436IF(.NOT. Found) CALL FATAL(SolverName, 'Provide hmin input array to be iterated through: "Mesh Hmin"')1437MaxRemeshIter= SIZE(WorkArray(:,1))14381439PRINT*, 'workarraysize', SIZE(WorkArray(1,:)), SIZE(WorkArray(:,1))14401441ALLOCATE(SaveMeshHminArray(MaxRemeshIter, 1), NewMeshHminArray(MaxRemeshIter,1))1442SaveMeshHminArray = WorkArray1443NULLIFY(WorkArray)1444WorkArray => ListGetConstRealArray(FuncParams, "MMG Hausd", Found)1445IF(.NOT. Found) CALL FATAL(SolverName, 'Provide hmin input array to be iterated through: "Mesh Hausd"')1446IF(MaxRemeshIter /= SIZE(WorkArray(:,1))) CALL FATAL(SolverName, 'The number of hmin options &1447must equal the number of hausd options')1448ALLOCATE(SaveMeshHausdArray(MaxRemeshIter,1), NewMeshHausdArray(MaxRemeshIter,1))1449SaveMeshHausdArray = WorkArray1450NULLIFY(WorkArray)14511452SaveMetric = ListGetConstReal( FuncParams, 'GlacierMeshMetric Min LC', GotSaveMetric)14531454MeshChange = ListGetConstReal( Params, 'Mesh Change Percentage', Found)1455IF(.NOT. Found) THEN1456MeshChange = 10.0_dp1457CALL WARN(SolverName, "Not found Mesh Percentage Change so assuming 10%")1458END IF1459MeshChange = (MeshChange/100.0_dp)+114601461SaveSSiter = ListGetInteger(Model % Simulation, "Steady State Max Iterations", Found)1462IF(.NOT. Found) SaveSSiter = 114631464TimestepSizes => ListGetConstRealArray( CurrentModel % Simulation, &1465'Timestep Sizes', Found, UnfoundFatal=.TRUE.)1466IF(SIZE(TimestepSizes,1) > 1) CALL Fatal(SolverName,&1467"Calving solver requires a single constant 'Timestep Sizes'")1468SaveDt = TimestepSizes(1,1)14691470NewMeshHminArray = SaveMeshHminArray1471NewMeshHausdArray = SaveMeshHausdArray1472NewMetric = SaveMetric1473NewMeshDist = SaveMeshDist1474NewMeshHGrad = SaveMeshHgrad1475ELSE1476SaveDt = ListGetCReal( CurrentModel % Simulation,'Timestep Size' )1477END IF14781479! since "Calving solver requires a single constant 'Timestep Sizes'"1480TimeIntervals = ListGetInteger(Model % Simulation, "Timestep Intervals", UnfoundFatal = .TRUE.)14811482!Get current simulation time1483TimeVar => VariableGet(Model % Variables, 'Time', .TRUE.)14841485MaxNSDiverge = ListGetConstReal(Params, "Maximum Flow Solution Divergence", CheckFlowDiverge)1486MaxNSValue = ListGetConstReal(Params, "Maximum Velocity Magnitude", CheckFlowMax)1487FirstMaxNSValue = ListGetConstReal(Params, "First Time Max Expected Velocity", Found)1488IF(.NOT. Found .AND. CheckFlowDiverge) THEN1489CALL Info(SolverName, "'First Time Max Expected Velocity' not found, setting to 1.0E4")1490FirstMaxNSValue = 1.0E41491END IF14921493!====================================================!1494!---------------- DO THINGS! ------------------------!1495!====================================================!14961497NSFail=.FALSE.1498NSDiverge=.FALSE.1499NSTooFast=.FALSE.15001501!In addition to checking for absolute failure (% NonlinConverged = 0), we can check1502!for suspiciously large shift in the max variable value (this usually indicates a problem)1503!and we can also check for unphysically large velocity values1504IF(CheckFlowDiverge .OR. CheckFlowMax) THEN15051506FlowMax = 0.0_dp1507DO i=1,Mesh % NumberOfNodes1508Mag = 0.0_dp15091510DO j=1,FlowVar % DOFs-11511Mag = Mag + (FlowVar % Values( (FlowVar % Perm(i)-1)*FlowVar % DOFs + j ) ** 2.0_dp)1512END DO1513Mag = Mag ** 0.5_dp1514FlowMax = MAX(FlowMax, Mag)1515END DO15161517#ifdef ELMER_BROKEN_MPI_IN_PLACE1518buffer = FlowMax1519CALL MPI_AllReduce(buffer, &1520#else1521CALL MPI_AllReduce(MPI_IN_PLACE, &1522#endif1523FlowMax, 1, MPI_DOUBLE_PRECISION, MPI_MAX, ELMER_COMM_WORLD, ierr)1524END IF15251526IF(CheckFlowDiverge) THEN1527!First time, there's no previous velocity against which to check divergence.1528!This is somewhat messy because of the separate 'Maximum Velocity Magnitude'1529IF(FirstTime) SaveFlowMax = MIN(FlowMax,FirstMaxNSValue)15301531NSChange = FlowMax / SaveFlowMax1532IF(ParEnv % MyPE == 0) PRINT *,'Debug, Flow Max (old/new): ',SaveFlowMax, FlowMax,' NSChange: ',NSChange15331534IF(NSChange > MaxNSDiverge) THEN1535NSDiverge = .TRUE.1536CALL Info(SolverName,"Large change in maximum velocity suggests dodgy&1537&Navier-Stokes solution.")1538END IF1539IF(.NOT. NSDiverge) SaveFlowMax = FlowMax1540END IF15411542IF(CheckFlowMax) THEN1543IF(FlowMax > MaxNSValue) THEN1544NSTooFast = .TRUE.1545CALL Info(SolverName,"Large maximum velocity suggests dodgy&1546&Navier-Stokes solution.")1547END IF1548END IF15491550NSFail = FlowVar % NonlinConverged < 1 .OR. NSDiverge .OR. NSTooFast1551! Joe note: I commented out Eef's testing here during merge:1552! PRINT *, 'temporarily set NSFail=True for testing'1553! NSFail=.TRUE.1554IF(ParEnv % MyPE == 0) PRINT*, 'Debug', FlowVar % NonlinConverged, NSDiverge, NSTooFast1555IF(NSFail) THEN1556CALL Info(SolverName, "Skipping solvers except Remesh because NS failed to converge.")15571558FailCount = FailCount + 11559IF(ParEnv % MyPE == 0) PRINT *, 'FailCount=',FailCount1560! Joe note: I commented out Eef's testing here during merge:1561! PRINT *, 'Temporarily set failcount to 2, to force remeshing!'1562! FailCount=21563IF(NewMeshCount > 3) THEN1564CALL Fatal(SolverName, "Don't seem to be able to recover from NS failure, giving up...")1565END IF15661567!Set the clock back one second less than a timestep size.1568!This means next time we are effectively redoing the same timestep1569!but without any solvers which are dependent on (t > told) to reallocate1570TimeVar % Values(1) = TimeVar % Values(1) - SaveDt + (1.0/(365.25 * 24 * 60 * 60.0_dp))15711572CALL ListAddConstReal(FlowVar % Solver % Values, &1573"Nonlinear System Newton After Tolerance", 0.0_dp)1574CALL ListAddInteger( FlowVar % Solver % Values, &1575"Nonlinear System Newton After Iterations", 10000)15761577IF(failcount > 1) ChangeMesh = .TRUE.1578IF(.NOT. NSDiverge .AND. .NOT. NSTooFast) THEN1579!ie fails from nonconvergence but flow looks ok1580CurrentNLIter = ListGetInteger(FlowVar % Solver % Values, &1581"Nonlinear System Max Iterations", Found)1582IF(CurrentNLIter >= SaveNLIter*4) THEN !clearly not going to converge1583CALL ListAddInteger( FlowVar % Solver % Values, &1584"Nonlinear System Max Iterations", SaveNLIter)1585!ChangeMesh = .FALSE.1586ELSE1587CALL ListAddInteger( FlowVar % Solver % Values, &1588"Nonlinear System Max Iterations", CurrentNLIter*2)1589!ChangeMesh = .FALSE.1590END IF1591END IF15921593!If this is the second failure in a row, fiddle with the mesh1594!PRINT *, 'TO DO, optimize MMG parameters, need to change remesh distance as well?'1595IF(ChangeMesh) THEN1596NewMeshHminArray = NewMeshHminArray/MeshChange1597NewMeshHausdArray = NewMeshHausdArray/MeshChange1598NewMetric = NewMetric/MeshChange1599NewMeshDist = NewMeshDist*MeshChange1600NewMeshHGrad = NewMeshHGrad/MeshChange16011602CALL Info(SolverName,"NS failed twice, fiddling with the mesh... ")1603CALL Info(SolverName,"Temporarily slightly change MMG params ")1604CALL ListAddConstRealArray(FuncParams, "MMG Hmin", MaxRemeshIter, 1, NewMeshHminArray)1605!CALL ListAddConstReal(FuncParams, "MMG Hmax", SaveMeshHmax*1.1_dp)1606CALL ListAddConstReal(FuncParams, "MMG Hgrad", NewMeshHGrad) !default 1.31607CALL ListAddConstReal(RemeshSolver % Values, "Remeshing Distance", NewMeshDist)1608CALL ListAddConstRealArray(FuncParams, "MMG Hausd", MaxRemeshiter, 1, NewMeshHausdArray)1609CALL ListAddInteger( FlowVar % Solver % Values, "Nonlinear System Max Iterations", SaveNLIter)1610IF(GotSaveMetric) CALL ListAddConstReal(FuncParams, "GlacierMeshMetric Min LC", NewMetric)1611NewMesh = .TRUE.1612NewMeshCount = NewMeshCount + 11613ELSE1614NewMesh = .FALSE.1615END IF16161617IF( .NOT. (NSTooFast .OR. NSDiverge)) THEN1618!---Not quite converging---!16191620CALL ListAddConstReal(FlowVar % Solver % Values, &1621"Nonlinear System Relaxation Factor", 0.9_dp)16221623ELSE1624!---Solution blowing up----!16251626!Set var values to zero so it doesn't mess up viscosity next time1627FlowVar % Values = 0.0_dp16281629!TODO: What else? different linear method? more relaxation?1630END IF16311632!prevent calving on next time step1633CalvingOccurs = .FALSE.1634CALL SParIterAllReduceOR(CalvingOccurs)1635CALL ListAddLogical( Model % Simulation, 'CalvingOccurs', CalvingOccurs )1636ELSE1637! set original values back1638FailCount = 01639NewMeshCount = 016401641NewMeshHminArray = SaveMeshHminArray1642NewMeshHausdArray = SaveMeshHausdArray1643NewMetric = SaveMetric1644NewMeshDist = SaveMeshDist1645NewMeshHGrad = SaveMeshHgrad16461647CALL ListAddConstReal(FlowVar % Solver % Values, &1648"Nonlinear System Newton After Tolerance", SaveNewtonTol)1649CALL ListAddInteger( FlowVar % Solver % Values, &1650"Nonlinear System Newton After Iterations", SaveNewtonIter)1651CALL ListAddConstReal(FlowVar % Solver % Values, &1652"Nonlinear System Relaxation Factor", SaveRelax)1653CALL ListAddInteger( FlowVar % Solver % Values, "Nonlinear System Max Iterations", SaveNLIter)1654CALL ListAddConstRealArray(FuncParams, "MMG Hmin", MaxRemeshIter, 1, SaveMeshHminArray)1655!CALL ListAddConstReal(FuncParams, "MMG Hmax", SaveMeshHmax)1656CALL ListAddConstReal(RemeshSolver % Values, "Remeshing Distance", SaveMeshDist)1657CALL ListAddConstReal(FuncParams, "MMG Hgrad", SaveMeshHgrad)1658CALL ListAddConstRealArray(FuncParams, "MMG Hausd", MaxRemeshIter, 1, SaveMeshHausdArray)1659IF(GotSaveMetric) CALL ListAddConstReal(FuncParams, "GlacierMeshMetric Min LC", SaveMetric)16601661END IF16621663!Set a simulation switch to be picked up by Remesher1664CALL ListAddLogical( Model % Simulation, 'Flow Solution Failed', NSFail )16651666CalvingPauseSolvers = ListGetLogical( Model % Simulation, 'Calving Pause Solvers', Found )1667IF(.NOT. Found) THEN1668CALL INFO(SolverName, 'Cannot find Calving Pause Solvers so assuming calving has not pasued time.')1669CalvingPauseSolvers = .TRUE.1670END IF1671IF(.NOT. CalvingPauseSolvers .OR. FirstTime .OR. NSFail .OR. PNSFail) THEN1672!Switch off solvers1673DO Num = 1,9991674WRITE(Message,'(A,I0)') 'Switch Off Equation ',Num1675EqName = ListGetString( Params, Message, Found)1676IF( .NOT. Found) EXIT16771678Found = .FALSE.1679DO j=1,Model % NumberOfSolvers1680IF(ListGetString(Model % Solvers(j) % Values, "Equation") == EqName) THEN1681Found = .TRUE.1682!Turn off (or on) the solver1683!If NS failed to converge, (switch) off = .true.1684CALL SwitchSolverExec(Model % Solvers(j), NSFail)1685EXIT1686END IF1687END DO16881689IF(.NOT. Found) THEN1690WRITE (Message,'(A,A,A)') "Failed to find Equation Name: ",EqName,&1691" to switch off after calving."1692CALL Fatal(SolverName,Message)1693END IF1694END DO1695END IF16961697IF(NSFail) THEN1698!CALL ListAddConstReal( Model % Simulation, 'Timestep Size', PseudoSSdt)1699!CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', 1)1700CALL ListAddInteger( Model % Simulation, 'Timestep Intervals', TimeIntervals + 1)1701ELSE1702!CALL ListAddConstReal( Model % Simulation, 'Timestep Size', SaveDt)1703CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', SaveSSiter)1704END IF17051706FirstTime = .FALSE.1707PNSFail = NSFail17081709END SUBROUTINE CheckFlowConvergenceMMG171017111712