Path: blob/devel/elmerice/Solvers/CalvingRemeshparMMG.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. This34! hasn't actually been implemented yet, we use a test function.3536! Strategy:37!----------------3839! - Use Mesh % Repartition and RedistributeMesh to send relevant (calving)40! mesh regions to the nominated remeshing partition4142! - Remesh with MMG - done serially on nominated partition (TODO - improve nomination of partition)4344! - Global node/element number renegotiation4546! - Zoltan to compute new partitioning4748! - RedistributeMesh back to target processors4950! - Interpolate variables etc5152! - Continue simulation5354SUBROUTINE CalvingRemeshParMMG( Model, Solver, dt, Transient )5556USE MeshUtils57USE CalvingGeometry58USE MeshPartition59USE MeshRemeshing6061IMPLICIT NONE6263#include "parmmg/libparmmgtypesf.h"6465TYPE(Model_t) :: Model66TYPE(Solver_t), TARGET :: Solver67REAL(KIND=dp) :: dt68LOGICAL :: Transient69!--------------------------------------70TYPE(Variable_t), POINTER :: CalvingVar,DistanceVar71TYPE(ValueList_t), POINTER :: SolverParams, MeshParams72TYPE(Mesh_t),POINTER :: Mesh,GatheredMesh,NewMeshR,NewMeshRR,FinalMesh, DistributedMesh73TYPE(Element_t),POINTER :: Element, ParentElem74INTEGER :: i,j,k,NNodes,GNBulk, GNBdry, GNNode, NBulk, Nbdry, ierr, &75my_cboss,MyPE, PEs,CCount, counter, GlNode_min, GlNode_max,adjList(4),&76front_BC_ID, front_body_id, my_calv_front,calv_front, ncalv_parts, &77group_calve, comm_calve, group_world,ecode, NElNodes, target_bodyid,gdofs(4), &78PairCount,RPairCount, NCalvNodes, croot, nonCalvBoss, RNNodes, RNElems79INTEGER, POINTER :: NodeIndexes(:), geom_id80INTEGER, ALLOCATABLE :: Prnode_count(:), cgroup_membs(:),disps(:), &81PGDOFs_send(:),pcalv_front(:),GtoLNN(:),EdgePairs(:,:),REdgePairs(:,:), ElNodes(:),&82Nodes(:), IslandCounts(:), pNCalvNodes(:,:)83REAL(KIND=dp) :: test_thresh, test_point(3), remesh_thresh, hmin, hmax, hgrad, hausd, newdist84REAL(KIND=dp), ALLOCATABLE :: test_dist(:), test_lset(:), Ptest_lset(:), Gtest_lset(:), &85target_length(:,:), Ptest_dist(:), Gtest_dist(:)86LOGICAL, ALLOCATABLE :: calved_node(:), remeshed_node(:), fixed_node(:), fixed_elem(:), &87elem_send(:), RmElem(:), RmNode(:),new_fixed_node(:), new_fixed_elem(:), FoundNode(:,:), &88UsedElem(:), NewNodes(:), RmIslandNode(:), RmIslandElem(:)89LOGICAL :: ImBoss, Found, Isolated, Debug,DoAniso,NSFail,CalvingOccurs,&90RemeshOccurs,CheckFlowConvergence, HasNeighbour, Distributed91CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, CalvingVarName92TYPE(Variable_t), POINTER :: TimeVar93INTEGER :: Time, remeshtimestep, proc, idx, island, node94REAL(KIND=dp) :: TimeReal, PreCalveVolume, PostCalveVolume, CalveVolume9596SolverParams => GetSolverParams()97SolverName = "CalvingRemeshParMMG"98Debug=.TRUE.99Mesh => Model % Mesh100NNodes = Mesh % NumberOfNodes101NBulk = Mesh % NumberOfBulkElements102NBdry = Mesh % NumberOfBoundaryElements103104calv_front = 1105MyPe = ParEnv % MyPE106PEs = ParEnv % PEs107108TimeVar => VariableGet( Model % Mesh % Variables, 'Timestep' )109TimeReal = TimeVar % Values(1)110Time = INT(TimeReal)111112!for first time step calculate mesh volume113IF(Time == 1) THEN114CALL MeshVolume(Mesh, .TRUE., PreCalveVolume)115IF(MyPe == 0) PRINT*, 'First timestep precalve volume = ', PreCalveVolume116END IF117118!Check all elements are tetras or tris:119DO i=1,NBulk+Nbdry120Element => Mesh % Elements(i)121ecode = Element % TYPE % ElementCode122IF(ecode /= 101 .AND. ecode /= 202 .AND. ecode /= 303 .AND. ecode /= 504) THEN123PRINT *,MyPE,' has unsupported element type: ',ecode124CALL Fatal(SolverName, "Invalid Element Type!")125END IF126END DO127128!Get main Body ID129target_bodyid = Mesh % Elements(1) % BodyID130IF(target_bodyid /= 1) CALL Warn(SolverName, "Body ID is not 1, this case might not be well handled")131132!TODO - unhardcode (detect?) this133front_BC_id = 1134front_body_id = ListGetInteger( &135Model % BCs(front_bc_id) % Values, 'Body Id', Found, 1, Model % NumberOfBodies )136137hmin = ListGetConstReal(SolverParams, "Mesh Hmin", DefValue=20.0_dp)138hmax = ListGetConstReal(SolverParams, "Mesh Hmax", DefValue=4000.0_dp)139hgrad = ListGetConstReal(SolverParams,"Mesh Hgrad", DefValue=0.5_dp)140hausd = ListGetConstReal(SolverParams, "Mesh Hausd",DefValue=20.0_dp)141remesh_thresh = ListGetConstReal(SolverParams,"Remeshing Distance", DefValue=1000.0_dp)142CalvingVarName = ListGetString(SolverParams,"Calving Variable Name", DefValue="Calving Lset")143remeshtimestep = ListGetInteger(SolverParams,"Remesh TimeStep", DefValue=2)144145IF(ParEnv % MyPE == 0) THEN146PRINT *,ParEnv % MyPE,' hmin: ',hmin147PRINT *,ParEnv % MyPE,' hmax: ',hmax148PRINT *,ParEnv % MyPE,' hgrad: ',hgrad149PRINT *,ParEnv % MyPE,' hausd: ',hausd150PRINT *,ParEnv % MyPE,' remeshing distance: ',remesh_thresh151PRINT *,ParEnv % MyPE,' remeshing every ', remeshtimestep152END IF153154!If FlowSolver failed to converge (usually a result of weird mesh), large unphysical155!calving events can be predicted. So, turn off CalvingOccurs, and ensure a remesh156!Also undo this iterations mesh update157NSFail = ListGetLogical(Model % Simulation, "Flow Solution Failed",CheckFlowConvergence)158IF(CheckFlowConvergence) THEN159IF(NSFail) THEN160CalvingOccurs = .FALSE.161RemeshOccurs = .TRUE.162CALL Info(SolverName, "Remeshing but not calving because NS failed to converge.")163END IF164ELSE165CalvingOccurs=.TRUE.166RemeshOccurs=.TRUE.167END IF168169ALLOCATE(remeshed_node(NNodes),&170fixed_node(NNodes))171remeshed_node = .FALSE.172fixed_node = .FALSE.173174! TO DO some other wah to define remeshed nodes175DistanceVar => VariableGet(Mesh % Variables, "Distance", .TRUE., UnfoundFatal=.TRUE.)176ALLOCATE(test_dist(NNodes))177test_dist = DistanceVar % Values(DistanceVar % Perm(:))178remeshed_node = test_dist < remesh_thresh179180!Get the calving levelset function (-ve inside calving event, +ve in intact ice)181!-------------------182IF (CalvingOccurs) THEN183CalvingVar => VariableGet(Mesh % Variables, "Calving Lset", .TRUE., UnfoundFatal=.TRUE.)184185ALLOCATE(test_lset(NNodes),&186calved_node(NNodes)&187)188189calved_node = .FALSE.190test_lset = CalvingVar % Values(CalvingVar % Perm(:)) !TODO - quick&dirty, possibly zero perm?191calved_node = test_lset < 0.0192! calving front boundary nodes may have lset value greater than remesh dist193DO i=1, NNodes194newdist = MINVAL((/test_lset(i), test_dist(i)/))195remeshed_node(i) = newdist < remesh_thresh196END DO197END IF198199my_calv_front = 0200IF(ANY(remeshed_node)) THEN201my_calv_front = 1 !this is integer (not logical) so we can later move to multiple calving fronts202END IF203204NCalvNodes = COUNT(remeshed_node)205206!TODO - could make this more efficient by cutting out some of the elements from the calved region.207!This region will be remeshed too, but we discard it, so the closer we can get to the edge of the208!calving event, the better.209210!Identify all elements which need to be sent (including those fixed in place)211!Here we also find extra nodes which are just beyond the remeshing threshold212!but which are in fixed elements, thus also need to be sent213ALLOCATE(elem_send(nbulk+nbdry))214elem_send = .FALSE.215216IF(my_calv_front > 0) THEN217!Each partition identifies (based on nodes), elements which need to be transferred218DO i=1,Nbulk219Element => Mesh % Elements(i)220CALL Assert(Element % ElementIndex == i, SolverName,"Misnumbered bulk element.")221NodeIndexes => Element % NodeIndexes222NElNodes = Element % TYPE % NumberOfNodes223IF(ANY(remeshed_node(NodeIndexes(1:NElNodes)))) THEN224elem_send(i) = .TRUE.225IF(.NOT. ALL(remeshed_node(NodeIndexes(1:NElNodes)))) THEN226fixed_node(NodeIndexes(1:NElNodes)) = .TRUE.227END IF228END IF229END DO230231remeshed_node = remeshed_node .OR. fixed_node232233!Cycle boundary elements, checking parent elems234!BC elements follow parents235DO i=NBulk+1, NBulk+NBdry236Element => Mesh % Elements(i)237ParentElem => Element % BoundaryInfo % Left238IF(.NOT. ASSOCIATED(ParentElem)) THEN239ParentElem => Element % BoundaryInfo % Right240END IF241CALL Assert(ASSOCIATED(ParentElem),SolverName,"Boundary element has no parent!")242IF(elem_send(ParentElem % ElementIndex)) elem_send(i) = .TRUE.243END DO244245DEALLOCATE(fixed_node) !reuse later246END IF247248249!This does nothing yet but it will be important - determine250!the discrete calving zones, each of which will be separately remeshed251!by a nominated boss partition252! CALL CountCalvingEvents(Model, Mesh, ccount)253ccount = 1254255!Negotiate local calving boss - just one front for now256!-----------------------257ALLOCATE(pcalv_front(PEs))258pcalv_front = 0259CALL MPI_AllGather(my_calv_front, 1, MPI_INTEGER, pcalv_front, &2601, MPI_INTEGER, ELMER_COMM_WORLD, ierr)261262! loop to allow negotiation for multiple calving fronts263! This communication allows the determination of which part of the mesh264! has the most calvnodes and will become cboss265ALLOCATE(pNCalvNodes(MAXVAL(pcalv_front),PEs))266DO i=1, MAXVAL(pcalv_front)267CALL MPI_ALLGATHER(NCalvNodes, 1, MPI_INTEGER, pNCalvNodes(i,:), &2681, MPI_INTEGER, ELMER_COMM_WORLD, ierr)269END DO270271IF(Debug) THEN272PRINT *,mype,' debug calv_front: ',my_calv_front273PRINT *,mype,' debug calv_fronts: ',pcalv_front274END IF275276ncalv_parts = COUNT(pcalv_front==calv_front) !only one front for now...277ALLOCATE(cgroup_membs(ncalv_parts))278counter = 0279DO i=1,PEs280IF(pcalv_front(i) == calv_front) THEN !one calve front281counter = counter + 1282cgroup_membs(counter) = i-1283END IF284END DO285IF(Debug) PRINT *,mype,' debug group: ',cgroup_membs286287288!Create an MPI_COMM for each calving region, allow gathering instead of289!explicit send/receive290CALL MPI_Comm_Group( ELMER_COMM_WORLD, group_world, ierr)291CALL MPI_Group_Incl( group_world, ncalv_parts, cgroup_membs, group_calve, ierr)292CALL MPI_Comm_create( ELMER_COMM_WORLD, group_calve, COMM_CALVE, ierr)293!--------------------------294295!Work out to whom I send mesh parts for calving296!TODO - this is currently the lowest partition number which has some calving nodes,297!but it'd be more efficient to take the partition with the *most* calved nodes298!Now cboss set to part with most calving nodes299!NonCalvBoss will take any non calving nodes from the cboss300! this is still *TODO*301IF(My_Calv_Front>0) THEN302303! assume currently only one calving front304!my_cboss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MAXVAL(pNCalvNodes(1,:)))-1305my_cboss = 0306nonCalvBoss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MINVAL(pNCalvNodes(1,:)))-1307IF(Debug) PRINT *,MyPe,' debug calving boss: ',my_cboss308ImBoss = MyPE == my_cboss309310IF(ImBoss) THEN311ALLOCATE(Prnode_count(ncalv_parts))312Prnode_count = 0313END IF314ELSE315! only used if cboss has non calving nodes316nonCalvBoss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MINVAL(pNCalvNodes(1,:)))-1317ImBoss = .FALSE.318END IF319320!croot location is i when cboss = groupmember(i)321DO i=1, ncalv_parts322IF(my_cboss /= cgroup_membs(i)) CYCLE323croot = i-1324END DO325326!Redistribute the mesh (i.e. partitioning) so that cboss partitions327!contain entire calving/remeshing regions.328IF(.NOT. ASSOCIATED(Mesh % Repartition)) THEN329ALLOCATE(Mesh % Repartition(NBulk+NBdry), STAT=ierr)330IF(ierr /= 0) PRINT *,ParEnv % MyPE,' could not allocate Mesh % Repartition'331END IF332333!TODO send non calving nodes on ImBoss to nocalvboss334Mesh % Repartition = ParEnv % MyPE + 1335DO i=1,NBulk+NBdry336IF(elem_send(i)) Mesh % Repartition(i) = my_cboss+1337IF(ImBoss .AND. .NOT. elem_send(i)) THEN338WRITE(Message, '(A,i0,A)') "ImBoss sending Element ",i," to NonCalvBoss"339CALL WARN(SolverName, Message)340Mesh % Repartition(i) = NonCalvBoss+1341END IF342END DO343344GatheredMesh => RedistributeMesh(Model, Mesh, .TRUE., .FALSE.)345!Confirmed that boundary info is for Zoltan at this point346347IF(Debug) THEN348PRINT *,ParEnv % MyPE,' gatheredmesh % nonodes: ',GatheredMesh % NumberOfNodes349PRINT *,ParEnv % MyPE,' gatheredmesh % neelems: ',GatheredMesh % NumberOfBulkElements, &350GatheredMesh % NumberOfBoundaryElements351END IF352353!Now we have the gathered mesh, need to send:354! - Remeshed_node, test_lset355! - we can convert this into an integer code on elements (0 = leave alone, 1 = remeshed, 2 = fixed)356! - or we can simply send test_lset and recompute on calving_boss357358IF(My_Calv_Front>0) THEN359!root is always zero because cboss is always lowest member of new group (0)360CALL MPI_Gather(COUNT(remeshed_node), 1, MPI_INTEGER, Prnode_count, 1, &361MPI_INTEGER, croot, COMM_CALVE,ierr)362363IF(ImBoss) THEN364365IF(Debug) PRINT *,'boss debug prnode_count: ', Prnode_count366GLNode_max = MAXVAL(GatheredMesh % ParallelInfo % GlobalDOFs)367GLNode_min = MINVAL(GatheredMesh % ParallelInfo % GlobalDOFs)368GNBulk = GatheredMesh % NumberOfBulkElements369GNBdry = GatheredMesh % NumberOfBoundaryElements370GNNode = GatheredMesh % NumberOfNodes371372ALLOCATE(PGDOFs_send(SUM(Prnode_count)), &373Ptest_dist(SUM(Prnode_count)), &374disps(ncalv_parts),GtoLNN(GLNode_min:GLNode_max),&375gtest_dist(GNNode),&376fixed_node(GNNode),&377fixed_elem(GNBulk + GNBdry))378379IF(CalvingOccurs) ALLOCATE(Ptest_lset(SUM(Prnode_count)), &380gtest_lset(GNNode))381fixed_node = .FALSE.382fixed_elem = .FALSE.383gtest_lset = remesh_thresh + 500.0 !Ensure any far (unshared) nodes are fixed384385!Compute the global to local map386DO i=1,GNNode387GtoLNN(GatheredMesh % ParallelInfo % GlobalDOFs(i)) = i388END DO389390ELSE391ALLOCATE(disps(1), prnode_count(1))392END IF393394!Compute the offset in the gathered array from each part395IF(ImBoss) THEN396disps(1) = 0397DO i=2,ncalv_parts398disps(i) = disps(i-1) + prnode_count(i-1)399END DO400END IF401402IF (CalvingOccurs) THEN403!Gather the level set function404CALL MPI_GatherV(PACK(test_lset,remeshed_node), COUNT(remeshed_node), MPI_DOUBLE_PRECISION, &405Ptest_lset, Prnode_count, disps, MPI_DOUBLE_PRECISION, croot, COMM_CALVE,ierr)406IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")407END IF408!Gather the distance to front, but let it output to Ptest_lset to avoid repetitive code409CALL MPI_GatherV(PACK(test_dist,remeshed_node), COUNT(remeshed_node), MPI_DOUBLE_PRECISION, &410Ptest_dist, Prnode_count, disps, MPI_DOUBLE_PRECISION, croot, COMM_CALVE,ierr)411IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")412!END IF413!Gather the GDOFs414CALL MPI_GatherV(PACK(Mesh % ParallelInfo % GlobalDOFs,remeshed_node), &415COUNT(remeshed_node), MPI_INTEGER, PGDOFs_send, Prnode_count, &416disps, MPI_INTEGER, croot, COMM_CALVE,ierr)417IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")418419!Nodes with levelset value greater than remesh_thresh are kept fixed420!as are any elements with any fixed_nodes421!This implies the levelset is a true distance function, everywhere in the domain.422IF(ImBoss) THEN423DO i=1,SUM(Prnode_count)424k = GtoLNN(PGDOFs_send(i))425IF(k==0) CALL Fatal(SolverName, "Programming error in Global to Local NNum map")426IF(CalvingOccurs) Gtest_lset(k) = Ptest_lset(i)427Gtest_dist(k) = Ptest_dist(i)428END DO429430! calving front boundary nodes may have lset value greater than remesh dist431! so use dist so front boundary nodes aren't fixed432IF(CalvingOccurs) THEN433DO i=1,GNNode434newdist = MINVAL((/Gtest_lset(i), Gtest_dist(i)/))435fixed_node(i) = newdist > remesh_thresh436END DO437ELSE438fixed_node = Gtest_dist > remesh_thresh439END IF440441DO i=1, GNBulk + GNBdry442Element => GatheredMesh % Elements(i)443IF(ANY(fixed_node(Element % NodeIndexes))) THEN444fixed_elem(i) = .TRUE.445END IF446END DO447END IF448END IF ! My calving front > 1449450!Nominated partition does the remeshing451IF(ImBoss) THEN452IF (CalvingOccurs) THEN453!Initialise MMG datastructures454mmgMesh = 0455mmgLs = 0456mmgMet = 0457458CALL MeshVolume(GatheredMesh, .FALSE., PreCalveVolume)459460CALL MMG3D_Init_mesh(MMG5_ARG_start, &461MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppLs,mmgLs, &462MMG5_ARG_end)463464CALL GetCalvingEdgeNodes(GatheredMesh, .FALSE., EdgePairs, PairCount)465! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)466CALL Set_MMG3D_Mesh(GatheredMesh, .TRUE., EdgePairs, PairCount)467468!Request isosurface discretization469CALL MMG3D_Set_iparameter(mmgMesh, mmgLs, MMGPARAM_iso, 1,ierr)470471!set angle detection on (1, default) and set threshold angle (85 degrees)472!TODO - here & in MeshRemesh, need a better strategy than automatic detection473!i.e. feed in edge elements.474475!I think these are defunct as they are called in RemeshMMG476! this is unless cutting lset and anisotrophic remeshing take place in one step477!print *, 'first call of angle detection $$$ - turned on '478CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_angle, &4790,ierr)480481!!! angle detection changes calving in next time steps dramatically482!! if on automatically turns angle on483!CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgSol,MMGPARAM_angleDetection,&484! 85.0_dp,ierr)485486!Turn on debug (1)487CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_debug, &4881,ierr)489490!Turn off automatic aniso remeshing (0)491CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_aniso, &4920,ierr)493494!Set geometric parameters for remeshing495CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hmin,&496hmin,ierr)497CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hmax,&498hmax,ierr)499CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hausd,&500hausd,ierr)501CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hgrad,&502hgrad,ierr)503504CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_rmc,&5050.0001_dp,ierr)506507!Feed in the level set distance508CALL MMG3D_SET_SOLSIZE(mmgMesh, mmgLs, MMG5_Vertex, GNNode ,MMG5_Scalar, ierr)509DO i=1,GNNode510CALL MMG3D_Set_scalarSol(mmgLs,&511Gtest_lset(i), &512i,ierr)513END DO514515IF(Debug) THEN516PRINT *,' boss fixed node: ',COUNT(fixed_node),SIZE(fixed_node)517PRINT *,' boss fixed elem: ',COUNT(fixed_elem),SIZE(fixed_elem)518END IF519520!Set required nodes and elements521DO i=1,GNNode522IF(fixed_node(i)) THEN523CALL MMG3D_SET_REQUIREDVERTEX(mmgMesh,i,ierr)524END IF525END DO526527DO i=1,GNBulk + GNBdry528IF(fixed_elem(i)) THEN529IF(GatheredMesh % Elements(i) % TYPE % DIMENSION == 3) THEN530CALL MMG3D_SET_REQUIREDTETRAHEDRON(mmgMesh,i,ierr)531ELSEIF(GatheredMesh % Elements(i) % TYPE % DIMENSION == 2) THEN532CALL MMG3D_SET_REQUIREDTRIANGLE(mmgMesh,i-GNBulk,ierr)533END IF534END IF535END DO536537!> 4) (not mandatory): check if the number of given entities match with mesh size538CALL MMG3D_Chk_meshData(mmgMesh,mmgLs,ierr)539IF ( ierr /= 1 ) CALL EXIT(105)540541CALL MMG3D_SaveMesh(mmgMesh,"test_prels.mesh",LEN(TRIM("test_prels.mesh")),ierr)542CALL MMG3D_SaveSol(mmgMesh, mmgLs,"test_prels.sol",LEN(TRIM("test_prels.sol")),ierr)543!> ------------------------------ STEP II --------------------------544!! remesh function545! mmg5.5 not using isosurface discretization. More robust to remesh seperately546! addtionally computationally lighter as iceberg are not finely remeshed547CALL MMG3D_mmg3dls(mmgMesh,mmgLs,0_dp,ierr)548549IF ( ierr == MMG5_STRONGFAILURE ) THEN550PRINT*,"BAD ENDING OF MMG3DLS: UNABLE TO SAVE MESH", Time551ELSE IF ( ierr == MMG5_LOWFAILURE ) THEN552PRINT*,"BAD ENDING OF MMG3DLS", time553ENDIF554555CALL MMG3D_SaveMesh(mmgMesh,"test_ls.mesh",LEN(TRIM("test_ls.mesh")),ierr)556CALL MMG3D_SaveSol(mmgMesh, mmgLs,"test_ls.sol",LEN(TRIM("test_ls.sol")),ierr)557558CALL Get_MMG3D_Mesh(NewMeshR, .TRUE., new_fixed_node, new_fixed_elem)559560NewMeshR % Name = Mesh % Name561562NNodes = NewMeshR % NumberOfNodes563NBulk = NewMeshR % NumberOfBulkElements564NBdry = NewMeshR % NumberOfBoundaryElements565IF(DEBUG) PRINT *, 'NewMeshR nonodes, nbulk, nbdry: ',NNodes, NBulk, NBdry566567!Clear out unneeded elements568!Body elems with BodyID 3 (2) are the calving event (remaining domain)569!Boundary elems seem to have tags 0,1,10...570! 0 seems to be the edge triangles, 1 the outer surface (all bcs) and 2 the new level set surf571ALLOCATE(RmElem(NBulk+NBdry), RmNode(NNodes))572RmElem = .FALSE.573RmNode = .TRUE.574575DO i=1,NBulk576Element => NewMeshR % Elements(i)577NElNodes = Element % TYPE % NumberOfNodes578579IF(Element % TYPE % ElementCode /= 504) &580PRINT *,'Programming error, bulk type: ',Element % TYPE % ElementCode581IF(NElNodes /= 4) PRINT *,'Programming error, bulk nonodes: ',NElNodes582583!outbody - mark for deletion and move on584IF(Element % BodyID == 3) THEN585RmElem(i) = .TRUE.586!Deal with an MMG3D eccentricity - returns erroneous GlobalDOF = 10587!on some split calving elements588NewMeshR % ParallelInfo % GlobalDOFs(Element % NodeIndexes) = 0589CYCLE590ELSE IF(Element % BodyID == 2) THEN591Element % BodyID = target_bodyid592ELSE593PRINT *,'Erroneous body id: ',Element % BodyID594CALL Fatal(SolverName, "Bad body id!")595END IF596597!Get rid of any isolated elements (I think?)598! This may not be necessary, if the calving levelset is well defined599CALL MMG3D_Get_AdjaTet(mmgMesh, i, adjList,ierr)600IF(ALL(adjList == 0)) THEN601602!check if this is truly isolated or if it's at a partition boundary603isolated = .TRUE.604gdofs = NewMeshR % ParallelInfo % GlobalDOFs(Element % NodeIndexes(1:NELNodes))605DO j=1,GatheredMesh % NumberOfNodes606IF(ANY(gdofs == GatheredMesh % ParallelInfo % GlobalDOFs(j)) .AND. &607GatheredMesh % ParallelInfo % GInterface(j)) THEN608isolated = .FALSE.609EXIT610END IF611END DO612613IF(isolated) THEN614RmElem(i) = .TRUE.615CALL WARN(SolverName, 'Found an isolated body element.')616END IF617END IF618619!Mark all nodes in valid elements for keeping620RmNode(Element % NodeIndexes(1:NElNodes)) = .FALSE.621END DO622623!Same thru the boundary elements624!If their parent elem is body 3, delete, *unless* they have constraint 10 (the calving face)625!in which case use mmg3d function to get the adjacent tetras to find the valid parent626DO i=NBulk+1, NBulk + NBdry627Element => NewMeshR % Elements(i)628NElNodes = Element % TYPE % NumberOfNodes629630!Get rid of non-triangles631IF(Element % TYPE % ElementCode /= 303) THEN632RmElem(i) = .TRUE.633CYCLE634END IF635636!Get rid of boundary elements without BCID (newly created internal)637IF(Element % BoundaryInfo % Constraint == 0) THEN638RmElem(i) = .TRUE.639CYCLE640END IF641642ParentElem => Element % BoundaryInfo % Left643644IF(ParentElem % BodyID == 3) THEN645646!Not needed647IF(Element % BoundaryInfo % Constraint /= 10) THEN648!TODO, test constraint == 10 for other BC numbers on front649650RmElem(i) = .TRUE.651652!Switch parent elem to elem in remaining domain653ELSE654CALL MMG3D_Get_AdjaTet(mmgMesh, ParentElem % ElementIndex, adjList,ierr)655DO j=1,4656IF(adjlist(j) == 0) CYCLE657IF(NewMeshR % Elements(adjlist(j)) % BodyID == target_bodyid) THEN658Element % BoundaryInfo % Left => NewMeshR % Elements(adjList(j))659EXIT660END IF661END DO662END IF663664!Edge case - unconnected bulk element665ELSE IF(RmElem(ParentElem % ElementIndex)) THEN666RmElem(i) = .TRUE.667END IF668669END DO670671!! Release mmg mesh672CALL MMG3D_Free_all(MMG5_ARG_start, &673MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppLs,mmgLs, &674MMG5_ARG_end)675676!MMG3DLS returns constraint = 10 on newly formed boundary elements677!(i.e. the new calving front). Here it is set to front_BC_id678!And set all BC BodyIDs based on constraint679DO i=NBulk+1, NBulk + NBdry680681IF(RmElem(i)) CYCLE682683Element => NewMeshR % Elements(i)684geom_id => Element % BoundaryInfo % Constraint685686IF(geom_id == 10) THEN687geom_id = front_BC_id688689Element % BodyId = front_body_id690END IF691692CALL Assert((geom_id > 0) .AND. (geom_id <= Model % NumberOfBCs),&693SolverName,"Unexpected BC element body id!")694695END DO696697698!TODO - issue - MMG3Dls will mark new nodes 'required' if they split a previous699!boundary element. but only *front* boundary elements? Have confirmed it ONLY returns700!required & 10 for calving front nodes, and it's not to do with manually setting required stuff.701!But, the model does complain about required entities if the hole is internal, but no weird702!GIDs are returned.703IF(Debug) THEN704DO i=1,GatheredMesh % NumberOfNodes705PRINT *, ParEnv % MyPE,' debug old ',i,&706' GDOF: ',GatheredMesh % ParallelInfo % GlobalDOFs(i),&707' xyz: ',&708GatheredMesh % Nodes % x(i),&709GatheredMesh % Nodes % y(i),&710GatheredMesh % Nodes % z(i),fixed_node(i)711IF(fixed_node(i)) THEN712IF(.NOT. ANY(NewMeshR % ParallelInfo % GlobalDOFs == &713GatheredMesh % ParallelInfo % GlobalDOFs(i))) CALL Fatal(SolverName, &714"Missing required node in output!")715END IF716END DO717END IF718719!Chop out the flagged elems and nodes720CALL CutMesh(NewMeshR, RmNode, RmElem)721722NNodes = NewMeshR % NumberOfNodes723NBulk = NewMeshR % NumberOfBulkElements724NBdry = NewMeshR % NumberOfBoundaryElements725726!sometimes some icebergs are not removed fully from the MMG levelset727!find all connected nodes in groups and remove in groups not in the728!main icebody729!limit here of 10 possible mesh 'islands'730ALLOCATE(FoundNode(10, NNodes), ElNodes(4), &731UsedElem(NBulk), NewNodes(NBulk))732FoundNode = .FALSE.! count of nodes found733UsedElem = .FALSE. !count of elems used734NewNodes=.FALSE. !count of new elems in last sweep735island=0 ! count of different mesh islands736HasNeighbour=.FALSE. ! whether node has neighour737DO WHILE(COUNT(FoundNode) < NNodes)738! if last sweep has found no new neighbour nodes then move onto new island739IF(.NOT. HasNeighbour) THEN740island = island + 1741IF(Island > 10) CALL FATAL(SolverName, 'More than 10 icebergs left after levelset')742!find first unfound node743Inner: DO i=1, NNodes744IF(ANY(FoundNode(:, i))) CYCLE745NewNodes(i) = .TRUE.746FoundNode(island, i) = .TRUE.747EXIT Inner748END DO Inner749END IF750HasNeighbour=.FALSE.751nodes=PACK((/ (i,i=1,SIZE(NewNodes)) /), NewNodes .eqv. .TRUE.)752NewNodes=.FALSE.753DO i=1, Nbulk754IF(UsedElem(i)) CYCLE !already used elem755DO j=1, SIZE(Nodes)756node=Nodes(j)757Element => NewMeshR % Elements(i)758ElNodes = Element % NodeIndexes759IF(.NOT. ANY(ElNodes==node)) CYCLE ! doesn't contain node760UsedElem(i) = .TRUE. ! got nodes from elem761DO k=1,SIZE(ElNodes)762idx = Element % NodeIndexes(k)763IF(idx == Node) CYCLE ! this nodes764IF(FoundNode(island,idx)) CYCLE ! already got765FoundNode(island, idx) = .TRUE.766counter = counter + 1767HasNeighbour = .TRUE.768NewNodes(idx) = .TRUE.769END DO770END DO771END DO772END DO773774ALLOCATE(IslandCounts(10))775DO i=1,10776IslandCounts(i) = COUNT(FoundNode(i, :))777END DO778Island = COUNT(IslandCounts > 0)779DEALLOCATE(IslandCounts) ! reused780781! if iceberg not removed mark nodes and elems782IF(Island > 1) THEN783CALL WARN(SolverName, 'Mesh island found after levelset- removing...')784ALLOCATE(RmIslandNode(NNodes), RmIslandElem(Nbulk+NBdry),&785IslandCounts(Island))786RmIslandNode = .FALSE.787RmIslandElem = .FALSE.788counter=0789DO i=1, 10790IF(COUNT(FoundNode(i, :)) == 0) CYCLE791counter=counter+1792IslandCounts(counter) = COUNT(FoundNode(i, :))793END DO794DO i=1, Island795IF(IslandCounts(i) < MAXVAL(IslandCounts)) THEN796DO j=1, NNodes797IF(.NOT. FoundNode(i,j)) CYCLE! if not part of island798RmIslandNode(j) = .TRUE.799END DO800END IF801END DO802! mark all elems with rm nodes as need removing803nodes = PACK((/ (i,i=1,SIZE(RmIslandNode)) /), RmIslandNode .eqv. .TRUE.)804DO i=1, Nbulk+Nbdry805Element => NewMeshR % Elements(i)806ElNodes = Element % NodeIndexes807!any([(any(A(i) == B), i = 1, size(A))])808IF( .NOT. ANY([(ANY(ElNodes(i)==Nodes), i = 1, SIZE(ElNodes))])) CYCLE809RmIslandElem(i) = .TRUE.810END DO811812!Chop out missed iceberg if they exist813CALL CutMesh(NewMeshR, RmIslandNode, RmIslandElem)814815!modify rmelem and rmnode to include island removal816counter=0817DO i=1, SIZE(RmElem)818IF(RmElem(i)) CYCLE819counter=counter+1820IF(RmIslandElem(Counter)) RmElem(i) =.TRUE.821END DO822counter=0823DO i=1, SIZE(RmNode)824IF(RmNode(i)) CYCLE825counter=counter+1826IF(RmIslandNode(Counter)) RmNode(i) =.TRUE.827END DO828CALL INFO(SolverName, 'Mesh island removed', level=10)829END IF830831END IF ! CalvingOccurs832833DoAniso = .TRUE.834IF(DoAniso .AND. CalvingOccurs) THEN835836new_fixed_elem = PACK(new_fixed_elem, .NOT. RmElem)837new_fixed_node = PACK(new_fixed_node, .NOT. RmNode)838839IF(Debug) THEN840DO i=1,NewMeshR % NumberOfNodes841PRINT *, ParEnv % MyPE,' debug new ',i,&842' GDOF: ',NewMeshR % ParallelInfo % GlobalDOFs(i),&843' xyz: ',&844NewMeshR % Nodes % x(i),&845NewMeshR % Nodes % y(i),&846NewMeshR % Nodes % z(i)847848IF(NewMeshR % ParallelInfo % GlobalDOFs(i) == 0) CYCLE849IF(.NOT. ANY(GatheredMesh % ParallelInfo % GlobalDOFs == &850NewMeshR % ParallelInfo % GlobalDOFs(i))) CALL Warn(SolverName, &851"Unexpected GID")852END DO853END IF854855!Anisotropic mesh improvement following calving cut:856!TODO - unhardcode! Also this doesn't work very well yet.857ALLOCATE(target_length(NewMeshR % NumberOfNodes,3))858target_length(:,1) = 300.0859target_length(:,2) = 300.0860target_length(:,3) = 50.0861862!Parameters for anisotropic remeshing are set in the Materials section, or can &863!optionally be passed as a valuelist here. They have prefix RemeshMMG3D864!TODO - apparently there is beta-testing capability to do both levelset cut and anisotropic865!remeshing in the same step:866!https://forum.mmgtools.org/t/level-set-and-anisotropic-mesh-optimization/369/3867! GetCalvingEdgeNodes detects all shared boundary edges, to keep them sharp868869! calculate calved volume870CALL MeshVolume(NewMeshR, .FALSE., PostCalveVolume)871872CalveVolume = PreCalveVolume - PostCalveVolume873874PRINT*, 'CalveVolume', CalveVolume875END IF876END IF877878CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)879880Distributed = .TRUE.881IF(DoAniso .AND. .NOT. Distributed) THEN882! remeshing but no calving883IF(ImBoss .AND. .NOT. CalvingOccurs) NewMeshR => GatheredMesh884885!remeshing for calvign and no calving886IF(ImBoss) CALL GetCalvingEdgeNodes(NewMeshR, .FALSE., REdgePairs, RPairCount)887! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)888MeshParams => GetMaterial(Model % Mesh % Elements(1))889CALL SequentialRemeshParMMG(Model, NewMeshR, NewMeshRR, ImBoss, REdgePairs, &890RPairCount,new_fixed_node,new_fixed_elem, MeshParams)891IF(ImBoss) THEN892CALL ReleaseMesh(NewMeshR)893NewMeshR => NewMeshRR894NewMeshRR => NULL()895896!Update parallel info from old mesh nodes (shared node neighbours)897CALL MapNewParallelInfo(GatheredMesh, NewMeshR)898CALL ReleaseMesh(GatheredMesh)899! CALL ReleaseMesh(NewMeshR)900GatheredMesh => NewMeshR901NewMeshR => NULL()902NewMeshRR => NULL()903END IF904END IF905906IF(DoAniso .AND. Distributed) THEN907! remeshing but no calving908IF(ImBoss .AND. .NOT. CalvingOccurs) THEN909NewMeshR => GatheredMesh910!ELSE IF (ImBoss .AND. CalvingOccurs) THEN911!CALL MapNewParallelInfo(GatheredMesh, NewMeshR)912!DO i=1, NewMeshR % NumberOfNodes913!PRINT*, 'boss', i, NewMeshR % ParallelInfo % GlobalDOFs(i)914!NewMeshR % ParallelInfo % GlobalDOFs915!END DO916END IF917918IF(.NOT. ImBoss) THEN919NewMeshR => AllocateMesh( 0, 0, 0, InitParallel = .TRUE.)920ALLOCATE( NewMeshR % ParallelInfo % GlobalDofs( 0 ))921!NewMeshR % ParallelInfo % GlobalDofs = 0922END IF923924!IF(.NOT. ImBoss) PRINT*, ParEnv % MyPE, 'meshcheck', NewMeshR % ParallelInfo % GlobalDOFs925!Now each partition has GatheredMesh, we need to renegotiate globalDOFs926!CALL RenumberGDOFs(Mesh, NewMeshR)927928!and global element numbers929!CALL RenumberGElems(NewMeshR)930931932! share fixed nodes933! assumption here is globaldofs == nodenumber934!935936IF (ImBoss) THEN937RNNodes = NewMeshR % NumberOfNodes938RNElems = NewMeshR % NumberOfBulkElements + NewMeshR % NumberOfBoundaryElements939940DO i=1, RNNodes941NewMeshR % ParallelInfo % GlobalDOFs(i) = i942END DO943DO i=1, RNElems944NewMeshR % Elements(i) % GElementIndex = i945END DO946END IF947CALL MPI_BCAST(RNNodes, 1, MPI_INTEGER, my_cboss, ELMER_COMM_WORLD, ierr)948CALL MPI_BCAST(RNElems, 1, MPI_INTEGER, my_cboss, ELMER_COMM_WORLD, ierr)949IF(.NOT. ImBoss) ALLOCATE(new_fixed_node(RNNodes), new_fixed_elem(RNElems))950CALL MPI_BCAST(new_fixed_node,RNNodes,MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)951CALL MPI_BCAST(new_fixed_elem,RNElems, MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)952953CALL Zoltan_Interface( Model, NewMeshR, StartImbalanceTol=1.1_dp, TolChange=0.02_dp, MinElems=10 )954955DistributedMesh => RedistributeMesh(Model, NewMeshR, .TRUE., .FALSE.)956CALL ReleaseMesh(NewMeshR)957958!remeshing for calvign and no calving959!not sure if this works in parallel960CALL GetCalvingEdgeNodes(DistributedMesh, .FALSE., REdgePairs, RPairCount)961! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)962MeshParams => GetMaterial(Model % Mesh % Elements(1))963CALL DistributedRemeshParMMG(Model, DistributedMesh, NewMeshRR, REdgePairs, &964RPairCount,new_fixed_node,new_fixed_elem, MeshParams)965CALL ReleaseMesh(NewMeshR)966NewMeshR => NewMeshRR967NewMeshRR => NULL()968969!Update parallel info from old mesh nodes (shared node neighbours)970CALL MapNewParallelInfo(GatheredMesh, NewMeshR)971CALL ReleaseMesh(GatheredMesh)972! CALL ReleaseMesh(NewMeshR)973GatheredMesh => NewMeshR974NewMeshR => NULL()975NewMeshRR => NULL()976END IF977978!Wait for all partitions to finish979IF(My_Calv_Front>0) THEN980CALL MPI_BARRIER(COMM_CALVE,ierr)981CALL MPI_COMM_FREE(COMM_CALVE,ierr)982CALL MPI_GROUP_FREE(group_world,ierr)983END IF984CALL MPI_BARRIER(ELMER_COMM_WORLD,ierr)985986!Now each partition has GatheredMesh, we need to renegotiate globalDOFs987CALL RenumberGDOFs(Mesh, GatheredMesh)988989!and global element numbers990CALL RenumberGElems(GatheredMesh)991992!Some checks on the new mesh993!----------------------------994DO i=1,GatheredMesh % NumberOfNodes995IF(GatheredMesh % ParallelInfo % GInterface(i)) THEN996IF(.NOT. ASSOCIATED(GatheredMesh % ParallelInfo % Neighbourlist(i) % Neighbours)) &997CALL Fatal(SolverName, "Neighbourlist not associated!")998IF(SIZE(GatheredMesh % ParallelInfo % Neighbourlist(i) % Neighbours) < 2) &999CALL Fatal(SolverName, "Neighbourlist too small!")1000END IF1001END DO10021003DO i=1,GatheredMesh % NumberOfNodes1004IF(.NOT. ASSOCIATED(GatheredMesh % ParallelInfo % NeighbourList(i) % Neighbours)) &1005CALL Fatal(SolverName, 'Unassociated Neighbourlist % Neighbours')1006IF(GatheredMesh % ParallelInfo % GlobalDOFs(i) == 0) &1007CALL Fatal(SolverName, 'Bad GID 0')1008END DO10091010ParEnv % IsNeighbour(:) = .FALSE.1011DO i=1, Mesh % NumberOfNodes1012IF ( ASSOCIATED(Mesh % ParallelInfo % NeighbourList(i) % Neighbours) ) THEN1013DO j=1,SIZE(Mesh % ParallelInfo % NeighbourList(i) % Neighbours)1014proc = Mesh % ParallelInfo % NeighbourList(i) % Neighbours(j)1015IF ( ParEnv % Active(proc+1).AND.proc/=ParEnv % MYpe) &1016ParEnv % IsNeighbour(proc+1) = .TRUE.1017END DO1018END IF1019END DO10201021!Call zoltan to determine redistribution of mesh1022! then do the redistribution1023!-------------------------------10241025CALL Zoltan_Interface( Model, GatheredMesh, StartImbalanceTol=1.1_dp, TolChange=0.02_dp, MinElems=10 )10261027FinalMesh => RedistributeMesh(Model, GatheredMesh, .TRUE., .FALSE.)10281029CALL CheckMeshQuality(FinalMesh)10301031FinalMesh % Name = TRIM(Mesh % Name)1032FinalMesh % OutputActive = .TRUE.1033FinalMesh % Changed = .TRUE.10341035!Actually switch the model's mesh1036CALL SwitchMesh(Model, Solver, Mesh, FinalMesh)10371038!After SwitchMesh because we need GroundedMask1039CALL EnforceGroundedMask(Model, Mesh)10401041!Calculate mesh volume1042CALL MeshVolume(Model % Mesh, .TRUE., PostCalveVolume)1043IF(MyPe == 0) PRINT*, 'Post calve volume: ', PostCalveVolume, ' after timestep', Time10441045!Recompute mesh bubbles etc1046CALL MeshStabParams( Model % Mesh )10471048!Release the old mesh1049CALL ReleaseMesh(GatheredMesh)10501051!remove mesh update1052CALL ResetMeshUpdate(Model, Solver)10531054END SUBROUTINE CalvingRemeshParMMG10551056! test routine for parallel remeshing of a distributed mesh1057SUBROUTINE ParMMGRemeshing ( Model, Solver, dt, TransientSimulation )10581059USE MeshUtils1060USE CalvingGeometry1061USE MeshPartition1062USE MeshRemeshing10631064IMPLICIT NONE10651066!-----------------------------------------------1067TYPE(Model_t) :: Model1068TYPE(Solver_t), TARGET :: Solver1069REAL(KIND=dp) :: dt1070LOGICAL :: TransientSimulation1071!-----------------------------------------------1072TYPE(Mesh_t), POINTER :: Mesh, OutMesh, FinalMesh1073TYPE(ValueList_t), POINTER :: SolverParams1074REAL(kind=dp), POINTER :: WorkReal(:)1075INTEGER, POINTER :: WorkPerm(:)1076INTEGER, ALLOCATABLE :: EdgePairs(:,:)1077LOGICAL :: Parallel1078INTEGER :: i,j,k,n, PairCount1079!------------------------------------------------1080CHARACTER(LEN=MAX_NAME_LEN) :: SolverName = "ParMMGRemeshing"10811082#ifdef HAVE_PARMMG10831084IF(ParEnv % PEs > 1) THEN1085Parallel = .TRUE.1086ELSE1087CALL FATAL(Solvername, 'Needs to be run in parallel')1088END IF10891090Mesh => Model % Mesh1091SolverParams => GetSolverParams()10921093n = Mesh % NumberOfNodes1094ALLOCATE(WorkPerm(n), WorkReal(n))1095WorkReal = 0.0_dp1096WorkPerm = [(i,i=1,n)]1097CALL VariableAdd(Mesh % Variables, Mesh, Solver, "dummy", &10981, WorkReal, WorkPerm)1099NULLIFY(WorkReal,WorkPerm)11001101CALL GetCalvingEdgeNodes(Mesh, .FALSE., EdgePairs, PairCount)11021103CALL DistributedRemeshParMMG(Model, Mesh,OutMesh, EdgePairs, PairCount,Params=SolverParams)11041105!Now each partition has GatheredMesh, we need to renegotiate globalDOFs1106CALL RenumberGDOFs(Mesh, OutMesh)11071108!and global element numbers1109CALL RenumberGElems(OutMesh)11101111CALL Zoltan_Interface( Model, OutMesh, StartImbalanceTol=1.1_dp, TolChange=0.02_dp, MinElems=10 )11121113FinalMesh => RedistributeMesh(Model, OutMesh, .TRUE., .FALSE.)11141115FinalMesh % Name = TRIM(Mesh % Name)1116FinalMesh % OutputActive = .TRUE.1117FinalMesh % Changed = .TRUE.11181119!Actually switch the model's mesh1120! also releases old mesh1121CALL SwitchMesh(Model, Solver, Mesh, FinalMesh)11221123CALL ReleaseMesh(OutMesh)1124!CALL ReleaseMesh(Mesh)11251126#else1127CALL FATAL(SolverName, 'ParMMG needs to be installed to use this solver!')1128#endif11291130END SUBROUTINE ParMMGRemeshing11311132