Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/elmerice/Solvers/CalvingRemeshparMMG.F90
3204 views
1
!/*****************************************************************************/
2
! *
3
! * Elmer/Ice, a glaciological add-on to Elmer
4
! * http://elmerice.elmerfem.org
5
! *
6
! *
7
! * This program is free software; you can redistribute it and/or
8
! * modify it under the terms of the GNU General Public License
9
! * as published by the Free Software Foundation; either version 2
10
! * of the License, or (at your option) any later version.
11
! *
12
! * This program is distributed in the hope that it will be useful,
13
! * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
! * GNU General Public License for more details.
16
! *
17
! * You should have received a copy of the GNU General Public License
18
! * along with this program (in file fem/GPL-2); if not, write to the
19
! * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20
! * Boston, MA 02110-1301, USA.
21
! *
22
! *****************************************************************************/
23
! ******************************************************************************
24
! *
25
! * Authors: Joe Todd
26
! * Email:
27
! * Web: http://elmerice.elmerfem.org
28
! *
29
! *
30
! *****************************************************************************
31
32
!Remesh the calving model using MMG3D - runs in parallel but remeshing is serial!
33
!Takes a level set which defines a calving event (or multiple calving events). Level
34
! set is negative inside a calving event, and positive in the remaining domain. This
35
! hasn't actually been implemented yet, we use a test function.
36
37
! Strategy:
38
!----------------
39
40
! - Use Mesh % Repartition and RedistributeMesh to send relevant (calving)
41
! mesh regions to the nominated remeshing partition
42
43
! - Remesh with MMG - done serially on nominated partition (TODO - improve nomination of partition)
44
45
! - Global node/element number renegotiation
46
47
! - Zoltan to compute new partitioning
48
49
! - RedistributeMesh back to target processors
50
51
! - Interpolate variables etc
52
53
! - Continue simulation
54
55
SUBROUTINE CalvingRemeshParMMG( Model, Solver, dt, Transient )
56
57
USE MeshUtils
58
USE CalvingGeometry
59
USE MeshPartition
60
USE MeshRemeshing
61
62
IMPLICIT NONE
63
64
#include "parmmg/libparmmgtypesf.h"
65
66
TYPE(Model_t) :: Model
67
TYPE(Solver_t), TARGET :: Solver
68
REAL(KIND=dp) :: dt
69
LOGICAL :: Transient
70
!--------------------------------------
71
TYPE(Variable_t), POINTER :: CalvingVar,DistanceVar
72
TYPE(ValueList_t), POINTER :: SolverParams, MeshParams
73
TYPE(Mesh_t),POINTER :: Mesh,GatheredMesh,NewMeshR,NewMeshRR,FinalMesh, DistributedMesh
74
TYPE(Element_t),POINTER :: Element, ParentElem
75
INTEGER :: i,j,k,NNodes,GNBulk, GNBdry, GNNode, NBulk, Nbdry, ierr, &
76
my_cboss,MyPE, PEs,CCount, counter, GlNode_min, GlNode_max,adjList(4),&
77
front_BC_ID, front_body_id, my_calv_front,calv_front, ncalv_parts, &
78
group_calve, comm_calve, group_world,ecode, NElNodes, target_bodyid,gdofs(4), &
79
PairCount,RPairCount, NCalvNodes, croot, nonCalvBoss, RNNodes, RNElems
80
INTEGER, POINTER :: NodeIndexes(:), geom_id
81
INTEGER, ALLOCATABLE :: Prnode_count(:), cgroup_membs(:),disps(:), &
82
PGDOFs_send(:),pcalv_front(:),GtoLNN(:),EdgePairs(:,:),REdgePairs(:,:), ElNodes(:),&
83
Nodes(:), IslandCounts(:), pNCalvNodes(:,:)
84
REAL(KIND=dp) :: test_thresh, test_point(3), remesh_thresh, hmin, hmax, hgrad, hausd, newdist
85
REAL(KIND=dp), ALLOCATABLE :: test_dist(:), test_lset(:), Ptest_lset(:), Gtest_lset(:), &
86
target_length(:,:), Ptest_dist(:), Gtest_dist(:)
87
LOGICAL, ALLOCATABLE :: calved_node(:), remeshed_node(:), fixed_node(:), fixed_elem(:), &
88
elem_send(:), RmElem(:), RmNode(:),new_fixed_node(:), new_fixed_elem(:), FoundNode(:,:), &
89
UsedElem(:), NewNodes(:), RmIslandNode(:), RmIslandElem(:)
90
LOGICAL :: ImBoss, Found, Isolated, Debug,DoAniso,NSFail,CalvingOccurs,&
91
RemeshOccurs,CheckFlowConvergence, HasNeighbour, Distributed
92
CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, CalvingVarName
93
TYPE(Variable_t), POINTER :: TimeVar
94
INTEGER :: Time, remeshtimestep, proc, idx, island, node
95
REAL(KIND=dp) :: TimeReal, PreCalveVolume, PostCalveVolume, CalveVolume
96
97
SolverParams => GetSolverParams()
98
SolverName = "CalvingRemeshParMMG"
99
Debug=.TRUE.
100
Mesh => Model % Mesh
101
NNodes = Mesh % NumberOfNodes
102
NBulk = Mesh % NumberOfBulkElements
103
NBdry = Mesh % NumberOfBoundaryElements
104
105
calv_front = 1
106
MyPe = ParEnv % MyPE
107
PEs = ParEnv % PEs
108
109
TimeVar => VariableGet( Model % Mesh % Variables, 'Timestep' )
110
TimeReal = TimeVar % Values(1)
111
Time = INT(TimeReal)
112
113
!for first time step calculate mesh volume
114
IF(Time == 1) THEN
115
CALL MeshVolume(Mesh, .TRUE., PreCalveVolume)
116
IF(MyPe == 0) PRINT*, 'First timestep precalve volume = ', PreCalveVolume
117
END IF
118
119
!Check all elements are tetras or tris:
120
DO i=1,NBulk+Nbdry
121
Element => Mesh % Elements(i)
122
ecode = Element % TYPE % ElementCode
123
IF(ecode /= 101 .AND. ecode /= 202 .AND. ecode /= 303 .AND. ecode /= 504) THEN
124
PRINT *,MyPE,' has unsupported element type: ',ecode
125
CALL Fatal(SolverName, "Invalid Element Type!")
126
END IF
127
END DO
128
129
!Get main Body ID
130
target_bodyid = Mesh % Elements(1) % BodyID
131
IF(target_bodyid /= 1) CALL Warn(SolverName, "Body ID is not 1, this case might not be well handled")
132
133
!TODO - unhardcode (detect?) this
134
front_BC_id = 1
135
front_body_id = ListGetInteger( &
136
Model % BCs(front_bc_id) % Values, 'Body Id', Found, 1, Model % NumberOfBodies )
137
138
hmin = ListGetConstReal(SolverParams, "Mesh Hmin", DefValue=20.0_dp)
139
hmax = ListGetConstReal(SolverParams, "Mesh Hmax", DefValue=4000.0_dp)
140
hgrad = ListGetConstReal(SolverParams,"Mesh Hgrad", DefValue=0.5_dp)
141
hausd = ListGetConstReal(SolverParams, "Mesh Hausd",DefValue=20.0_dp)
142
remesh_thresh = ListGetConstReal(SolverParams,"Remeshing Distance", DefValue=1000.0_dp)
143
CalvingVarName = ListGetString(SolverParams,"Calving Variable Name", DefValue="Calving Lset")
144
remeshtimestep = ListGetInteger(SolverParams,"Remesh TimeStep", DefValue=2)
145
146
IF(ParEnv % MyPE == 0) THEN
147
PRINT *,ParEnv % MyPE,' hmin: ',hmin
148
PRINT *,ParEnv % MyPE,' hmax: ',hmax
149
PRINT *,ParEnv % MyPE,' hgrad: ',hgrad
150
PRINT *,ParEnv % MyPE,' hausd: ',hausd
151
PRINT *,ParEnv % MyPE,' remeshing distance: ',remesh_thresh
152
PRINT *,ParEnv % MyPE,' remeshing every ', remeshtimestep
153
END IF
154
155
!If FlowSolver failed to converge (usually a result of weird mesh), large unphysical
156
!calving events can be predicted. So, turn off CalvingOccurs, and ensure a remesh
157
!Also undo this iterations mesh update
158
NSFail = ListGetLogical(Model % Simulation, "Flow Solution Failed",CheckFlowConvergence)
159
IF(CheckFlowConvergence) THEN
160
IF(NSFail) THEN
161
CalvingOccurs = .FALSE.
162
RemeshOccurs = .TRUE.
163
CALL Info(SolverName, "Remeshing but not calving because NS failed to converge.")
164
END IF
165
ELSE
166
CalvingOccurs=.TRUE.
167
RemeshOccurs=.TRUE.
168
END IF
169
170
ALLOCATE(remeshed_node(NNodes),&
171
fixed_node(NNodes))
172
remeshed_node = .FALSE.
173
fixed_node = .FALSE.
174
175
! TO DO some other wah to define remeshed nodes
176
DistanceVar => VariableGet(Mesh % Variables, "Distance", .TRUE., UnfoundFatal=.TRUE.)
177
ALLOCATE(test_dist(NNodes))
178
test_dist = DistanceVar % Values(DistanceVar % Perm(:))
179
remeshed_node = test_dist < remesh_thresh
180
181
!Get the calving levelset function (-ve inside calving event, +ve in intact ice)
182
!-------------------
183
IF (CalvingOccurs) THEN
184
CalvingVar => VariableGet(Mesh % Variables, "Calving Lset", .TRUE., UnfoundFatal=.TRUE.)
185
186
ALLOCATE(test_lset(NNodes),&
187
calved_node(NNodes)&
188
)
189
190
calved_node = .FALSE.
191
test_lset = CalvingVar % Values(CalvingVar % Perm(:)) !TODO - quick&dirty, possibly zero perm?
192
calved_node = test_lset < 0.0
193
! calving front boundary nodes may have lset value greater than remesh dist
194
DO i=1, NNodes
195
newdist = MINVAL((/test_lset(i), test_dist(i)/))
196
remeshed_node(i) = newdist < remesh_thresh
197
END DO
198
END IF
199
200
my_calv_front = 0
201
IF(ANY(remeshed_node)) THEN
202
my_calv_front = 1 !this is integer (not logical) so we can later move to multiple calving fronts
203
END IF
204
205
NCalvNodes = COUNT(remeshed_node)
206
207
!TODO - could make this more efficient by cutting out some of the elements from the calved region.
208
!This region will be remeshed too, but we discard it, so the closer we can get to the edge of the
209
!calving event, the better.
210
211
!Identify all elements which need to be sent (including those fixed in place)
212
!Here we also find extra nodes which are just beyond the remeshing threshold
213
!but which are in fixed elements, thus also need to be sent
214
ALLOCATE(elem_send(nbulk+nbdry))
215
elem_send = .FALSE.
216
217
IF(my_calv_front > 0) THEN
218
!Each partition identifies (based on nodes), elements which need to be transferred
219
DO i=1,Nbulk
220
Element => Mesh % Elements(i)
221
CALL Assert(Element % ElementIndex == i, SolverName,"Misnumbered bulk element.")
222
NodeIndexes => Element % NodeIndexes
223
NElNodes = Element % TYPE % NumberOfNodes
224
IF(ANY(remeshed_node(NodeIndexes(1:NElNodes)))) THEN
225
elem_send(i) = .TRUE.
226
IF(.NOT. ALL(remeshed_node(NodeIndexes(1:NElNodes)))) THEN
227
fixed_node(NodeIndexes(1:NElNodes)) = .TRUE.
228
END IF
229
END IF
230
END DO
231
232
remeshed_node = remeshed_node .OR. fixed_node
233
234
!Cycle boundary elements, checking parent elems
235
!BC elements follow parents
236
DO i=NBulk+1, NBulk+NBdry
237
Element => Mesh % Elements(i)
238
ParentElem => Element % BoundaryInfo % Left
239
IF(.NOT. ASSOCIATED(ParentElem)) THEN
240
ParentElem => Element % BoundaryInfo % Right
241
END IF
242
CALL Assert(ASSOCIATED(ParentElem),SolverName,"Boundary element has no parent!")
243
IF(elem_send(ParentElem % ElementIndex)) elem_send(i) = .TRUE.
244
END DO
245
246
DEALLOCATE(fixed_node) !reuse later
247
END IF
248
249
250
!This does nothing yet but it will be important - determine
251
!the discrete calving zones, each of which will be separately remeshed
252
!by a nominated boss partition
253
! CALL CountCalvingEvents(Model, Mesh, ccount)
254
ccount = 1
255
256
!Negotiate local calving boss - just one front for now
257
!-----------------------
258
ALLOCATE(pcalv_front(PEs))
259
pcalv_front = 0
260
CALL MPI_AllGather(my_calv_front, 1, MPI_INTEGER, pcalv_front, &
261
1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
262
263
! loop to allow negotiation for multiple calving fronts
264
! This communication allows the determination of which part of the mesh
265
! has the most calvnodes and will become cboss
266
ALLOCATE(pNCalvNodes(MAXVAL(pcalv_front),PEs))
267
DO i=1, MAXVAL(pcalv_front)
268
CALL MPI_ALLGATHER(NCalvNodes, 1, MPI_INTEGER, pNCalvNodes(i,:), &
269
1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
270
END DO
271
272
IF(Debug) THEN
273
PRINT *,mype,' debug calv_front: ',my_calv_front
274
PRINT *,mype,' debug calv_fronts: ',pcalv_front
275
END IF
276
277
ncalv_parts = COUNT(pcalv_front==calv_front) !only one front for now...
278
ALLOCATE(cgroup_membs(ncalv_parts))
279
counter = 0
280
DO i=1,PEs
281
IF(pcalv_front(i) == calv_front) THEN !one calve front
282
counter = counter + 1
283
cgroup_membs(counter) = i-1
284
END IF
285
END DO
286
IF(Debug) PRINT *,mype,' debug group: ',cgroup_membs
287
288
289
!Create an MPI_COMM for each calving region, allow gathering instead of
290
!explicit send/receive
291
CALL MPI_Comm_Group( ELMER_COMM_WORLD, group_world, ierr)
292
CALL MPI_Group_Incl( group_world, ncalv_parts, cgroup_membs, group_calve, ierr)
293
CALL MPI_Comm_create( ELMER_COMM_WORLD, group_calve, COMM_CALVE, ierr)
294
!--------------------------
295
296
!Work out to whom I send mesh parts for calving
297
!TODO - this is currently the lowest partition number which has some calving nodes,
298
!but it'd be more efficient to take the partition with the *most* calved nodes
299
!Now cboss set to part with most calving nodes
300
!NonCalvBoss will take any non calving nodes from the cboss
301
! this is still *TODO*
302
IF(My_Calv_Front>0) THEN
303
304
! assume currently only one calving front
305
!my_cboss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MAXVAL(pNCalvNodes(1,:)))-1
306
my_cboss = 0
307
nonCalvBoss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MINVAL(pNCalvNodes(1,:)))-1
308
IF(Debug) PRINT *,MyPe,' debug calving boss: ',my_cboss
309
ImBoss = MyPE == my_cboss
310
311
IF(ImBoss) THEN
312
ALLOCATE(Prnode_count(ncalv_parts))
313
Prnode_count = 0
314
END IF
315
ELSE
316
! only used if cboss has non calving nodes
317
nonCalvBoss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MINVAL(pNCalvNodes(1,:)))-1
318
ImBoss = .FALSE.
319
END IF
320
321
!croot location is i when cboss = groupmember(i)
322
DO i=1, ncalv_parts
323
IF(my_cboss /= cgroup_membs(i)) CYCLE
324
croot = i-1
325
END DO
326
327
!Redistribute the mesh (i.e. partitioning) so that cboss partitions
328
!contain entire calving/remeshing regions.
329
IF(.NOT. ASSOCIATED(Mesh % Repartition)) THEN
330
ALLOCATE(Mesh % Repartition(NBulk+NBdry), STAT=ierr)
331
IF(ierr /= 0) PRINT *,ParEnv % MyPE,' could not allocate Mesh % Repartition'
332
END IF
333
334
!TODO send non calving nodes on ImBoss to nocalvboss
335
Mesh % Repartition = ParEnv % MyPE + 1
336
DO i=1,NBulk+NBdry
337
IF(elem_send(i)) Mesh % Repartition(i) = my_cboss+1
338
IF(ImBoss .AND. .NOT. elem_send(i)) THEN
339
WRITE(Message, '(A,i0,A)') "ImBoss sending Element ",i," to NonCalvBoss"
340
CALL WARN(SolverName, Message)
341
Mesh % Repartition(i) = NonCalvBoss+1
342
END IF
343
END DO
344
345
GatheredMesh => RedistributeMesh(Model, Mesh, .TRUE., .FALSE.)
346
!Confirmed that boundary info is for Zoltan at this point
347
348
IF(Debug) THEN
349
PRINT *,ParEnv % MyPE,' gatheredmesh % nonodes: ',GatheredMesh % NumberOfNodes
350
PRINT *,ParEnv % MyPE,' gatheredmesh % neelems: ',GatheredMesh % NumberOfBulkElements, &
351
GatheredMesh % NumberOfBoundaryElements
352
END IF
353
354
!Now we have the gathered mesh, need to send:
355
! - Remeshed_node, test_lset
356
! - we can convert this into an integer code on elements (0 = leave alone, 1 = remeshed, 2 = fixed)
357
! - or we can simply send test_lset and recompute on calving_boss
358
359
IF(My_Calv_Front>0) THEN
360
!root is always zero because cboss is always lowest member of new group (0)
361
CALL MPI_Gather(COUNT(remeshed_node), 1, MPI_INTEGER, Prnode_count, 1, &
362
MPI_INTEGER, croot, COMM_CALVE,ierr)
363
364
IF(ImBoss) THEN
365
366
IF(Debug) PRINT *,'boss debug prnode_count: ', Prnode_count
367
GLNode_max = MAXVAL(GatheredMesh % ParallelInfo % GlobalDOFs)
368
GLNode_min = MINVAL(GatheredMesh % ParallelInfo % GlobalDOFs)
369
GNBulk = GatheredMesh % NumberOfBulkElements
370
GNBdry = GatheredMesh % NumberOfBoundaryElements
371
GNNode = GatheredMesh % NumberOfNodes
372
373
ALLOCATE(PGDOFs_send(SUM(Prnode_count)), &
374
Ptest_dist(SUM(Prnode_count)), &
375
disps(ncalv_parts),GtoLNN(GLNode_min:GLNode_max),&
376
gtest_dist(GNNode),&
377
fixed_node(GNNode),&
378
fixed_elem(GNBulk + GNBdry))
379
380
IF(CalvingOccurs) ALLOCATE(Ptest_lset(SUM(Prnode_count)), &
381
gtest_lset(GNNode))
382
fixed_node = .FALSE.
383
fixed_elem = .FALSE.
384
gtest_lset = remesh_thresh + 500.0 !Ensure any far (unshared) nodes are fixed
385
386
!Compute the global to local map
387
DO i=1,GNNode
388
GtoLNN(GatheredMesh % ParallelInfo % GlobalDOFs(i)) = i
389
END DO
390
391
ELSE
392
ALLOCATE(disps(1), prnode_count(1))
393
END IF
394
395
!Compute the offset in the gathered array from each part
396
IF(ImBoss) THEN
397
disps(1) = 0
398
DO i=2,ncalv_parts
399
disps(i) = disps(i-1) + prnode_count(i-1)
400
END DO
401
END IF
402
403
IF (CalvingOccurs) THEN
404
!Gather the level set function
405
CALL MPI_GatherV(PACK(test_lset,remeshed_node), COUNT(remeshed_node), MPI_DOUBLE_PRECISION, &
406
Ptest_lset, Prnode_count, disps, MPI_DOUBLE_PRECISION, croot, COMM_CALVE,ierr)
407
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
408
END IF
409
!Gather the distance to front, but let it output to Ptest_lset to avoid repetitive code
410
CALL MPI_GatherV(PACK(test_dist,remeshed_node), COUNT(remeshed_node), MPI_DOUBLE_PRECISION, &
411
Ptest_dist, Prnode_count, disps, MPI_DOUBLE_PRECISION, croot, COMM_CALVE,ierr)
412
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
413
!END IF
414
!Gather the GDOFs
415
CALL MPI_GatherV(PACK(Mesh % ParallelInfo % GlobalDOFs,remeshed_node), &
416
COUNT(remeshed_node), MPI_INTEGER, PGDOFs_send, Prnode_count, &
417
disps, MPI_INTEGER, croot, COMM_CALVE,ierr)
418
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
419
420
!Nodes with levelset value greater than remesh_thresh are kept fixed
421
!as are any elements with any fixed_nodes
422
!This implies the levelset is a true distance function, everywhere in the domain.
423
IF(ImBoss) THEN
424
DO i=1,SUM(Prnode_count)
425
k = GtoLNN(PGDOFs_send(i))
426
IF(k==0) CALL Fatal(SolverName, "Programming error in Global to Local NNum map")
427
IF(CalvingOccurs) Gtest_lset(k) = Ptest_lset(i)
428
Gtest_dist(k) = Ptest_dist(i)
429
END DO
430
431
! calving front boundary nodes may have lset value greater than remesh dist
432
! so use dist so front boundary nodes aren't fixed
433
IF(CalvingOccurs) THEN
434
DO i=1,GNNode
435
newdist = MINVAL((/Gtest_lset(i), Gtest_dist(i)/))
436
fixed_node(i) = newdist > remesh_thresh
437
END DO
438
ELSE
439
fixed_node = Gtest_dist > remesh_thresh
440
END IF
441
442
DO i=1, GNBulk + GNBdry
443
Element => GatheredMesh % Elements(i)
444
IF(ANY(fixed_node(Element % NodeIndexes))) THEN
445
fixed_elem(i) = .TRUE.
446
END IF
447
END DO
448
END IF
449
END IF ! My calving front > 1
450
451
!Nominated partition does the remeshing
452
IF(ImBoss) THEN
453
IF (CalvingOccurs) THEN
454
!Initialise MMG datastructures
455
mmgMesh = 0
456
mmgLs = 0
457
mmgMet = 0
458
459
CALL MeshVolume(GatheredMesh, .FALSE., PreCalveVolume)
460
461
CALL MMG3D_Init_mesh(MMG5_ARG_start, &
462
MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppLs,mmgLs, &
463
MMG5_ARG_end)
464
465
CALL GetCalvingEdgeNodes(GatheredMesh, .FALSE., EdgePairs, PairCount)
466
! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)
467
CALL Set_MMG3D_Mesh(GatheredMesh, .TRUE., EdgePairs, PairCount)
468
469
!Request isosurface discretization
470
CALL MMG3D_Set_iparameter(mmgMesh, mmgLs, MMGPARAM_iso, 1,ierr)
471
472
!set angle detection on (1, default) and set threshold angle (85 degrees)
473
!TODO - here & in MeshRemesh, need a better strategy than automatic detection
474
!i.e. feed in edge elements.
475
476
!I think these are defunct as they are called in RemeshMMG
477
! this is unless cutting lset and anisotrophic remeshing take place in one step
478
!print *, 'first call of angle detection $$$ - turned on '
479
CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_angle, &
480
0,ierr)
481
482
!!! angle detection changes calving in next time steps dramatically
483
!! if on automatically turns angle on
484
!CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgSol,MMGPARAM_angleDetection,&
485
! 85.0_dp,ierr)
486
487
!Turn on debug (1)
488
CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_debug, &
489
1,ierr)
490
491
!Turn off automatic aniso remeshing (0)
492
CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_aniso, &
493
0,ierr)
494
495
!Set geometric parameters for remeshing
496
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hmin,&
497
hmin,ierr)
498
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hmax,&
499
hmax,ierr)
500
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hausd,&
501
hausd,ierr)
502
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hgrad,&
503
hgrad,ierr)
504
505
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_rmc,&
506
0.0001_dp,ierr)
507
508
!Feed in the level set distance
509
CALL MMG3D_SET_SOLSIZE(mmgMesh, mmgLs, MMG5_Vertex, GNNode ,MMG5_Scalar, ierr)
510
DO i=1,GNNode
511
CALL MMG3D_Set_scalarSol(mmgLs,&
512
Gtest_lset(i), &
513
i,ierr)
514
END DO
515
516
IF(Debug) THEN
517
PRINT *,' boss fixed node: ',COUNT(fixed_node),SIZE(fixed_node)
518
PRINT *,' boss fixed elem: ',COUNT(fixed_elem),SIZE(fixed_elem)
519
END IF
520
521
!Set required nodes and elements
522
DO i=1,GNNode
523
IF(fixed_node(i)) THEN
524
CALL MMG3D_SET_REQUIREDVERTEX(mmgMesh,i,ierr)
525
END IF
526
END DO
527
528
DO i=1,GNBulk + GNBdry
529
IF(fixed_elem(i)) THEN
530
IF(GatheredMesh % Elements(i) % TYPE % DIMENSION == 3) THEN
531
CALL MMG3D_SET_REQUIREDTETRAHEDRON(mmgMesh,i,ierr)
532
ELSEIF(GatheredMesh % Elements(i) % TYPE % DIMENSION == 2) THEN
533
CALL MMG3D_SET_REQUIREDTRIANGLE(mmgMesh,i-GNBulk,ierr)
534
END IF
535
END IF
536
END DO
537
538
!> 4) (not mandatory): check if the number of given entities match with mesh size
539
CALL MMG3D_Chk_meshData(mmgMesh,mmgLs,ierr)
540
IF ( ierr /= 1 ) CALL EXIT(105)
541
542
CALL MMG3D_SaveMesh(mmgMesh,"test_prels.mesh",LEN(TRIM("test_prels.mesh")),ierr)
543
CALL MMG3D_SaveSol(mmgMesh, mmgLs,"test_prels.sol",LEN(TRIM("test_prels.sol")),ierr)
544
!> ------------------------------ STEP II --------------------------
545
!! remesh function
546
! mmg5.5 not using isosurface discretization. More robust to remesh seperately
547
! addtionally computationally lighter as iceberg are not finely remeshed
548
CALL MMG3D_mmg3dls(mmgMesh,mmgLs,0_dp,ierr)
549
550
IF ( ierr == MMG5_STRONGFAILURE ) THEN
551
PRINT*,"BAD ENDING OF MMG3DLS: UNABLE TO SAVE MESH", Time
552
ELSE IF ( ierr == MMG5_LOWFAILURE ) THEN
553
PRINT*,"BAD ENDING OF MMG3DLS", time
554
ENDIF
555
556
CALL MMG3D_SaveMesh(mmgMesh,"test_ls.mesh",LEN(TRIM("test_ls.mesh")),ierr)
557
CALL MMG3D_SaveSol(mmgMesh, mmgLs,"test_ls.sol",LEN(TRIM("test_ls.sol")),ierr)
558
559
CALL Get_MMG3D_Mesh(NewMeshR, .TRUE., new_fixed_node, new_fixed_elem)
560
561
NewMeshR % Name = Mesh % Name
562
563
NNodes = NewMeshR % NumberOfNodes
564
NBulk = NewMeshR % NumberOfBulkElements
565
NBdry = NewMeshR % NumberOfBoundaryElements
566
IF(DEBUG) PRINT *, 'NewMeshR nonodes, nbulk, nbdry: ',NNodes, NBulk, NBdry
567
568
!Clear out unneeded elements
569
!Body elems with BodyID 3 (2) are the calving event (remaining domain)
570
!Boundary elems seem to have tags 0,1,10...
571
! 0 seems to be the edge triangles, 1 the outer surface (all bcs) and 2 the new level set surf
572
ALLOCATE(RmElem(NBulk+NBdry), RmNode(NNodes))
573
RmElem = .FALSE.
574
RmNode = .TRUE.
575
576
DO i=1,NBulk
577
Element => NewMeshR % Elements(i)
578
NElNodes = Element % TYPE % NumberOfNodes
579
580
IF(Element % TYPE % ElementCode /= 504) &
581
PRINT *,'Programming error, bulk type: ',Element % TYPE % ElementCode
582
IF(NElNodes /= 4) PRINT *,'Programming error, bulk nonodes: ',NElNodes
583
584
!outbody - mark for deletion and move on
585
IF(Element % BodyID == 3) THEN
586
RmElem(i) = .TRUE.
587
!Deal with an MMG3D eccentricity - returns erroneous GlobalDOF = 10
588
!on some split calving elements
589
NewMeshR % ParallelInfo % GlobalDOFs(Element % NodeIndexes) = 0
590
CYCLE
591
ELSE IF(Element % BodyID == 2) THEN
592
Element % BodyID = target_bodyid
593
ELSE
594
PRINT *,'Erroneous body id: ',Element % BodyID
595
CALL Fatal(SolverName, "Bad body id!")
596
END IF
597
598
!Get rid of any isolated elements (I think?)
599
! This may not be necessary, if the calving levelset is well defined
600
CALL MMG3D_Get_AdjaTet(mmgMesh, i, adjList,ierr)
601
IF(ALL(adjList == 0)) THEN
602
603
!check if this is truly isolated or if it's at a partition boundary
604
isolated = .TRUE.
605
gdofs = NewMeshR % ParallelInfo % GlobalDOFs(Element % NodeIndexes(1:NELNodes))
606
DO j=1,GatheredMesh % NumberOfNodes
607
IF(ANY(gdofs == GatheredMesh % ParallelInfo % GlobalDOFs(j)) .AND. &
608
GatheredMesh % ParallelInfo % GInterface(j)) THEN
609
isolated = .FALSE.
610
EXIT
611
END IF
612
END DO
613
614
IF(isolated) THEN
615
RmElem(i) = .TRUE.
616
CALL WARN(SolverName, 'Found an isolated body element.')
617
END IF
618
END IF
619
620
!Mark all nodes in valid elements for keeping
621
RmNode(Element % NodeIndexes(1:NElNodes)) = .FALSE.
622
END DO
623
624
!Same thru the boundary elements
625
!If their parent elem is body 3, delete, *unless* they have constraint 10 (the calving face)
626
!in which case use mmg3d function to get the adjacent tetras to find the valid parent
627
DO i=NBulk+1, NBulk + NBdry
628
Element => NewMeshR % Elements(i)
629
NElNodes = Element % TYPE % NumberOfNodes
630
631
!Get rid of non-triangles
632
IF(Element % TYPE % ElementCode /= 303) THEN
633
RmElem(i) = .TRUE.
634
CYCLE
635
END IF
636
637
!Get rid of boundary elements without BCID (newly created internal)
638
IF(Element % BoundaryInfo % Constraint == 0) THEN
639
RmElem(i) = .TRUE.
640
CYCLE
641
END IF
642
643
ParentElem => Element % BoundaryInfo % Left
644
645
IF(ParentElem % BodyID == 3) THEN
646
647
!Not needed
648
IF(Element % BoundaryInfo % Constraint /= 10) THEN
649
!TODO, test constraint == 10 for other BC numbers on front
650
651
RmElem(i) = .TRUE.
652
653
!Switch parent elem to elem in remaining domain
654
ELSE
655
CALL MMG3D_Get_AdjaTet(mmgMesh, ParentElem % ElementIndex, adjList,ierr)
656
DO j=1,4
657
IF(adjlist(j) == 0) CYCLE
658
IF(NewMeshR % Elements(adjlist(j)) % BodyID == target_bodyid) THEN
659
Element % BoundaryInfo % Left => NewMeshR % Elements(adjList(j))
660
EXIT
661
END IF
662
END DO
663
END IF
664
665
!Edge case - unconnected bulk element
666
ELSE IF(RmElem(ParentElem % ElementIndex)) THEN
667
RmElem(i) = .TRUE.
668
END IF
669
670
END DO
671
672
!! Release mmg mesh
673
CALL MMG3D_Free_all(MMG5_ARG_start, &
674
MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppLs,mmgLs, &
675
MMG5_ARG_end)
676
677
!MMG3DLS returns constraint = 10 on newly formed boundary elements
678
!(i.e. the new calving front). Here it is set to front_BC_id
679
!And set all BC BodyIDs based on constraint
680
DO i=NBulk+1, NBulk + NBdry
681
682
IF(RmElem(i)) CYCLE
683
684
Element => NewMeshR % Elements(i)
685
geom_id => Element % BoundaryInfo % Constraint
686
687
IF(geom_id == 10) THEN
688
geom_id = front_BC_id
689
690
Element % BodyId = front_body_id
691
END IF
692
693
CALL Assert((geom_id > 0) .AND. (geom_id <= Model % NumberOfBCs),&
694
SolverName,"Unexpected BC element body id!")
695
696
END DO
697
698
699
!TODO - issue - MMG3Dls will mark new nodes 'required' if they split a previous
700
!boundary element. but only *front* boundary elements? Have confirmed it ONLY returns
701
!required & 10 for calving front nodes, and it's not to do with manually setting required stuff.
702
!But, the model does complain about required entities if the hole is internal, but no weird
703
!GIDs are returned.
704
IF(Debug) THEN
705
DO i=1,GatheredMesh % NumberOfNodes
706
PRINT *, ParEnv % MyPE,' debug old ',i,&
707
' GDOF: ',GatheredMesh % ParallelInfo % GlobalDOFs(i),&
708
' xyz: ',&
709
GatheredMesh % Nodes % x(i),&
710
GatheredMesh % Nodes % y(i),&
711
GatheredMesh % Nodes % z(i),fixed_node(i)
712
IF(fixed_node(i)) THEN
713
IF(.NOT. ANY(NewMeshR % ParallelInfo % GlobalDOFs == &
714
GatheredMesh % ParallelInfo % GlobalDOFs(i))) CALL Fatal(SolverName, &
715
"Missing required node in output!")
716
END IF
717
END DO
718
END IF
719
720
!Chop out the flagged elems and nodes
721
CALL CutMesh(NewMeshR, RmNode, RmElem)
722
723
NNodes = NewMeshR % NumberOfNodes
724
NBulk = NewMeshR % NumberOfBulkElements
725
NBdry = NewMeshR % NumberOfBoundaryElements
726
727
!sometimes some icebergs are not removed fully from the MMG levelset
728
!find all connected nodes in groups and remove in groups not in the
729
!main icebody
730
!limit here of 10 possible mesh 'islands'
731
ALLOCATE(FoundNode(10, NNodes), ElNodes(4), &
732
UsedElem(NBulk), NewNodes(NBulk))
733
FoundNode = .FALSE.! count of nodes found
734
UsedElem = .FALSE. !count of elems used
735
NewNodes=.FALSE. !count of new elems in last sweep
736
island=0 ! count of different mesh islands
737
HasNeighbour=.FALSE. ! whether node has neighour
738
DO WHILE(COUNT(FoundNode) < NNodes)
739
! if last sweep has found no new neighbour nodes then move onto new island
740
IF(.NOT. HasNeighbour) THEN
741
island = island + 1
742
IF(Island > 10) CALL FATAL(SolverName, 'More than 10 icebergs left after levelset')
743
!find first unfound node
744
Inner: DO i=1, NNodes
745
IF(ANY(FoundNode(:, i))) CYCLE
746
NewNodes(i) = .TRUE.
747
FoundNode(island, i) = .TRUE.
748
EXIT Inner
749
END DO Inner
750
END IF
751
HasNeighbour=.FALSE.
752
nodes=PACK((/ (i,i=1,SIZE(NewNodes)) /), NewNodes .eqv. .TRUE.)
753
NewNodes=.FALSE.
754
DO i=1, Nbulk
755
IF(UsedElem(i)) CYCLE !already used elem
756
DO j=1, SIZE(Nodes)
757
node=Nodes(j)
758
Element => NewMeshR % Elements(i)
759
ElNodes = Element % NodeIndexes
760
IF(.NOT. ANY(ElNodes==node)) CYCLE ! doesn't contain node
761
UsedElem(i) = .TRUE. ! got nodes from elem
762
DO k=1,SIZE(ElNodes)
763
idx = Element % NodeIndexes(k)
764
IF(idx == Node) CYCLE ! this nodes
765
IF(FoundNode(island,idx)) CYCLE ! already got
766
FoundNode(island, idx) = .TRUE.
767
counter = counter + 1
768
HasNeighbour = .TRUE.
769
NewNodes(idx) = .TRUE.
770
END DO
771
END DO
772
END DO
773
END DO
774
775
ALLOCATE(IslandCounts(10))
776
DO i=1,10
777
IslandCounts(i) = COUNT(FoundNode(i, :))
778
END DO
779
Island = COUNT(IslandCounts > 0)
780
DEALLOCATE(IslandCounts) ! reused
781
782
! if iceberg not removed mark nodes and elems
783
IF(Island > 1) THEN
784
CALL WARN(SolverName, 'Mesh island found after levelset- removing...')
785
ALLOCATE(RmIslandNode(NNodes), RmIslandElem(Nbulk+NBdry),&
786
IslandCounts(Island))
787
RmIslandNode = .FALSE.
788
RmIslandElem = .FALSE.
789
counter=0
790
DO i=1, 10
791
IF(COUNT(FoundNode(i, :)) == 0) CYCLE
792
counter=counter+1
793
IslandCounts(counter) = COUNT(FoundNode(i, :))
794
END DO
795
DO i=1, Island
796
IF(IslandCounts(i) < MAXVAL(IslandCounts)) THEN
797
DO j=1, NNodes
798
IF(.NOT. FoundNode(i,j)) CYCLE! if not part of island
799
RmIslandNode(j) = .TRUE.
800
END DO
801
END IF
802
END DO
803
! mark all elems with rm nodes as need removing
804
nodes = PACK((/ (i,i=1,SIZE(RmIslandNode)) /), RmIslandNode .eqv. .TRUE.)
805
DO i=1, Nbulk+Nbdry
806
Element => NewMeshR % Elements(i)
807
ElNodes = Element % NodeIndexes
808
!any([(any(A(i) == B), i = 1, size(A))])
809
IF( .NOT. ANY([(ANY(ElNodes(i)==Nodes), i = 1, SIZE(ElNodes))])) CYCLE
810
RmIslandElem(i) = .TRUE.
811
END DO
812
813
!Chop out missed iceberg if they exist
814
CALL CutMesh(NewMeshR, RmIslandNode, RmIslandElem)
815
816
!modify rmelem and rmnode to include island removal
817
counter=0
818
DO i=1, SIZE(RmElem)
819
IF(RmElem(i)) CYCLE
820
counter=counter+1
821
IF(RmIslandElem(Counter)) RmElem(i) =.TRUE.
822
END DO
823
counter=0
824
DO i=1, SIZE(RmNode)
825
IF(RmNode(i)) CYCLE
826
counter=counter+1
827
IF(RmIslandNode(Counter)) RmNode(i) =.TRUE.
828
END DO
829
CALL INFO(SolverName, 'Mesh island removed', level=10)
830
END IF
831
832
END IF ! CalvingOccurs
833
834
DoAniso = .TRUE.
835
IF(DoAniso .AND. CalvingOccurs) THEN
836
837
new_fixed_elem = PACK(new_fixed_elem, .NOT. RmElem)
838
new_fixed_node = PACK(new_fixed_node, .NOT. RmNode)
839
840
IF(Debug) THEN
841
DO i=1,NewMeshR % NumberOfNodes
842
PRINT *, ParEnv % MyPE,' debug new ',i,&
843
' GDOF: ',NewMeshR % ParallelInfo % GlobalDOFs(i),&
844
' xyz: ',&
845
NewMeshR % Nodes % x(i),&
846
NewMeshR % Nodes % y(i),&
847
NewMeshR % Nodes % z(i)
848
849
IF(NewMeshR % ParallelInfo % GlobalDOFs(i) == 0) CYCLE
850
IF(.NOT. ANY(GatheredMesh % ParallelInfo % GlobalDOFs == &
851
NewMeshR % ParallelInfo % GlobalDOFs(i))) CALL Warn(SolverName, &
852
"Unexpected GID")
853
END DO
854
END IF
855
856
!Anisotropic mesh improvement following calving cut:
857
!TODO - unhardcode! Also this doesn't work very well yet.
858
ALLOCATE(target_length(NewMeshR % NumberOfNodes,3))
859
target_length(:,1) = 300.0
860
target_length(:,2) = 300.0
861
target_length(:,3) = 50.0
862
863
!Parameters for anisotropic remeshing are set in the Materials section, or can &
864
!optionally be passed as a valuelist here. They have prefix RemeshMMG3D
865
!TODO - apparently there is beta-testing capability to do both levelset cut and anisotropic
866
!remeshing in the same step:
867
!https://forum.mmgtools.org/t/level-set-and-anisotropic-mesh-optimization/369/3
868
! GetCalvingEdgeNodes detects all shared boundary edges, to keep them sharp
869
870
! calculate calved volume
871
CALL MeshVolume(NewMeshR, .FALSE., PostCalveVolume)
872
873
CalveVolume = PreCalveVolume - PostCalveVolume
874
875
PRINT*, 'CalveVolume', CalveVolume
876
END IF
877
END IF
878
879
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
880
881
Distributed = .TRUE.
882
IF(DoAniso .AND. .NOT. Distributed) THEN
883
! remeshing but no calving
884
IF(ImBoss .AND. .NOT. CalvingOccurs) NewMeshR => GatheredMesh
885
886
!remeshing for calvign and no calving
887
IF(ImBoss) CALL GetCalvingEdgeNodes(NewMeshR, .FALSE., REdgePairs, RPairCount)
888
! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)
889
MeshParams => GetMaterial(Model % Mesh % Elements(1))
890
CALL SequentialRemeshParMMG(Model, NewMeshR, NewMeshRR, ImBoss, REdgePairs, &
891
RPairCount,new_fixed_node,new_fixed_elem, MeshParams)
892
IF(ImBoss) THEN
893
CALL ReleaseMesh(NewMeshR)
894
NewMeshR => NewMeshRR
895
NewMeshRR => NULL()
896
897
!Update parallel info from old mesh nodes (shared node neighbours)
898
CALL MapNewParallelInfo(GatheredMesh, NewMeshR)
899
CALL ReleaseMesh(GatheredMesh)
900
! CALL ReleaseMesh(NewMeshR)
901
GatheredMesh => NewMeshR
902
NewMeshR => NULL()
903
NewMeshRR => NULL()
904
END IF
905
END IF
906
907
IF(DoAniso .AND. Distributed) THEN
908
! remeshing but no calving
909
IF(ImBoss .AND. .NOT. CalvingOccurs) THEN
910
NewMeshR => GatheredMesh
911
!ELSE IF (ImBoss .AND. CalvingOccurs) THEN
912
!CALL MapNewParallelInfo(GatheredMesh, NewMeshR)
913
!DO i=1, NewMeshR % NumberOfNodes
914
!PRINT*, 'boss', i, NewMeshR % ParallelInfo % GlobalDOFs(i)
915
!NewMeshR % ParallelInfo % GlobalDOFs
916
!END DO
917
END IF
918
919
IF(.NOT. ImBoss) THEN
920
NewMeshR => AllocateMesh( 0, 0, 0, InitParallel = .TRUE.)
921
ALLOCATE( NewMeshR % ParallelInfo % GlobalDofs( 0 ))
922
!NewMeshR % ParallelInfo % GlobalDofs = 0
923
END IF
924
925
!IF(.NOT. ImBoss) PRINT*, ParEnv % MyPE, 'meshcheck', NewMeshR % ParallelInfo % GlobalDOFs
926
!Now each partition has GatheredMesh, we need to renegotiate globalDOFs
927
!CALL RenumberGDOFs(Mesh, NewMeshR)
928
929
!and global element numbers
930
!CALL RenumberGElems(NewMeshR)
931
932
933
! share fixed nodes
934
! assumption here is globaldofs == nodenumber
935
!
936
937
IF (ImBoss) THEN
938
RNNodes = NewMeshR % NumberOfNodes
939
RNElems = NewMeshR % NumberOfBulkElements + NewMeshR % NumberOfBoundaryElements
940
941
DO i=1, RNNodes
942
NewMeshR % ParallelInfo % GlobalDOFs(i) = i
943
END DO
944
DO i=1, RNElems
945
NewMeshR % Elements(i) % GElementIndex = i
946
END DO
947
END IF
948
CALL MPI_BCAST(RNNodes, 1, MPI_INTEGER, my_cboss, ELMER_COMM_WORLD, ierr)
949
CALL MPI_BCAST(RNElems, 1, MPI_INTEGER, my_cboss, ELMER_COMM_WORLD, ierr)
950
IF(.NOT. ImBoss) ALLOCATE(new_fixed_node(RNNodes), new_fixed_elem(RNElems))
951
CALL MPI_BCAST(new_fixed_node,RNNodes,MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)
952
CALL MPI_BCAST(new_fixed_elem,RNElems, MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)
953
954
CALL Zoltan_Interface( Model, NewMeshR, StartImbalanceTol=1.1_dp, TolChange=0.02_dp, MinElems=10 )
955
956
DistributedMesh => RedistributeMesh(Model, NewMeshR, .TRUE., .FALSE.)
957
CALL ReleaseMesh(NewMeshR)
958
959
!remeshing for calvign and no calving
960
!not sure if this works in parallel
961
CALL GetCalvingEdgeNodes(DistributedMesh, .FALSE., REdgePairs, RPairCount)
962
! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)
963
MeshParams => GetMaterial(Model % Mesh % Elements(1))
964
CALL DistributedRemeshParMMG(Model, DistributedMesh, NewMeshRR, REdgePairs, &
965
RPairCount,new_fixed_node,new_fixed_elem, MeshParams)
966
CALL ReleaseMesh(NewMeshR)
967
NewMeshR => NewMeshRR
968
NewMeshRR => NULL()
969
970
!Update parallel info from old mesh nodes (shared node neighbours)
971
CALL MapNewParallelInfo(GatheredMesh, NewMeshR)
972
CALL ReleaseMesh(GatheredMesh)
973
! CALL ReleaseMesh(NewMeshR)
974
GatheredMesh => NewMeshR
975
NewMeshR => NULL()
976
NewMeshRR => NULL()
977
END IF
978
979
!Wait for all partitions to finish
980
IF(My_Calv_Front>0) THEN
981
CALL MPI_BARRIER(COMM_CALVE,ierr)
982
CALL MPI_COMM_FREE(COMM_CALVE,ierr)
983
CALL MPI_GROUP_FREE(group_world,ierr)
984
END IF
985
CALL MPI_BARRIER(ELMER_COMM_WORLD,ierr)
986
987
!Now each partition has GatheredMesh, we need to renegotiate globalDOFs
988
CALL RenumberGDOFs(Mesh, GatheredMesh)
989
990
!and global element numbers
991
CALL RenumberGElems(GatheredMesh)
992
993
!Some checks on the new mesh
994
!----------------------------
995
DO i=1,GatheredMesh % NumberOfNodes
996
IF(GatheredMesh % ParallelInfo % GInterface(i)) THEN
997
IF(.NOT. ASSOCIATED(GatheredMesh % ParallelInfo % Neighbourlist(i) % Neighbours)) &
998
CALL Fatal(SolverName, "Neighbourlist not associated!")
999
IF(SIZE(GatheredMesh % ParallelInfo % Neighbourlist(i) % Neighbours) < 2) &
1000
CALL Fatal(SolverName, "Neighbourlist too small!")
1001
END IF
1002
END DO
1003
1004
DO i=1,GatheredMesh % NumberOfNodes
1005
IF(.NOT. ASSOCIATED(GatheredMesh % ParallelInfo % NeighbourList(i) % Neighbours)) &
1006
CALL Fatal(SolverName, 'Unassociated Neighbourlist % Neighbours')
1007
IF(GatheredMesh % ParallelInfo % GlobalDOFs(i) == 0) &
1008
CALL Fatal(SolverName, 'Bad GID 0')
1009
END DO
1010
1011
ParEnv % IsNeighbour(:) = .FALSE.
1012
DO i=1, Mesh % NumberOfNodes
1013
IF ( ASSOCIATED(Mesh % ParallelInfo % NeighbourList(i) % Neighbours) ) THEN
1014
DO j=1,SIZE(Mesh % ParallelInfo % NeighbourList(i) % Neighbours)
1015
proc = Mesh % ParallelInfo % NeighbourList(i) % Neighbours(j)
1016
IF ( ParEnv % Active(proc+1).AND.proc/=ParEnv % MYpe) &
1017
ParEnv % IsNeighbour(proc+1) = .TRUE.
1018
END DO
1019
END IF
1020
END DO
1021
1022
!Call zoltan to determine redistribution of mesh
1023
! then do the redistribution
1024
!-------------------------------
1025
1026
CALL Zoltan_Interface( Model, GatheredMesh, StartImbalanceTol=1.1_dp, TolChange=0.02_dp, MinElems=10 )
1027
1028
FinalMesh => RedistributeMesh(Model, GatheredMesh, .TRUE., .FALSE.)
1029
1030
CALL CheckMeshQuality(FinalMesh)
1031
1032
FinalMesh % Name = TRIM(Mesh % Name)
1033
FinalMesh % OutputActive = .TRUE.
1034
FinalMesh % Changed = .TRUE.
1035
1036
!Actually switch the model's mesh
1037
CALL SwitchMesh(Model, Solver, Mesh, FinalMesh)
1038
1039
!After SwitchMesh because we need GroundedMask
1040
CALL EnforceGroundedMask(Model, Mesh)
1041
1042
!Calculate mesh volume
1043
CALL MeshVolume(Model % Mesh, .TRUE., PostCalveVolume)
1044
IF(MyPe == 0) PRINT*, 'Post calve volume: ', PostCalveVolume, ' after timestep', Time
1045
1046
!Recompute mesh bubbles etc
1047
CALL MeshStabParams( Model % Mesh )
1048
1049
!Release the old mesh
1050
CALL ReleaseMesh(GatheredMesh)
1051
1052
!remove mesh update
1053
CALL ResetMeshUpdate(Model, Solver)
1054
1055
END SUBROUTINE CalvingRemeshParMMG
1056
1057
! test routine for parallel remeshing of a distributed mesh
1058
SUBROUTINE ParMMGRemeshing ( Model, Solver, dt, TransientSimulation )
1059
1060
USE MeshUtils
1061
USE CalvingGeometry
1062
USE MeshPartition
1063
USE MeshRemeshing
1064
1065
IMPLICIT NONE
1066
1067
!-----------------------------------------------
1068
TYPE(Model_t) :: Model
1069
TYPE(Solver_t), TARGET :: Solver
1070
REAL(KIND=dp) :: dt
1071
LOGICAL :: TransientSimulation
1072
!-----------------------------------------------
1073
TYPE(Mesh_t), POINTER :: Mesh, OutMesh, FinalMesh
1074
TYPE(ValueList_t), POINTER :: SolverParams
1075
REAL(kind=dp), POINTER :: WorkReal(:)
1076
INTEGER, POINTER :: WorkPerm(:)
1077
INTEGER, ALLOCATABLE :: EdgePairs(:,:)
1078
LOGICAL :: Parallel
1079
INTEGER :: i,j,k,n, PairCount
1080
!------------------------------------------------
1081
CHARACTER(LEN=MAX_NAME_LEN) :: SolverName = "ParMMGRemeshing"
1082
1083
#ifdef HAVE_PARMMG
1084
1085
IF(ParEnv % PEs > 1) THEN
1086
Parallel = .TRUE.
1087
ELSE
1088
CALL FATAL(Solvername, 'Needs to be run in parallel')
1089
END IF
1090
1091
Mesh => Model % Mesh
1092
SolverParams => GetSolverParams()
1093
1094
n = Mesh % NumberOfNodes
1095
ALLOCATE(WorkPerm(n), WorkReal(n))
1096
WorkReal = 0.0_dp
1097
WorkPerm = [(i,i=1,n)]
1098
CALL VariableAdd(Mesh % Variables, Mesh, Solver, "dummy", &
1099
1, WorkReal, WorkPerm)
1100
NULLIFY(WorkReal,WorkPerm)
1101
1102
CALL GetCalvingEdgeNodes(Mesh, .FALSE., EdgePairs, PairCount)
1103
1104
CALL DistributedRemeshParMMG(Model, Mesh,OutMesh, EdgePairs, PairCount,Params=SolverParams)
1105
1106
!Now each partition has GatheredMesh, we need to renegotiate globalDOFs
1107
CALL RenumberGDOFs(Mesh, OutMesh)
1108
1109
!and global element numbers
1110
CALL RenumberGElems(OutMesh)
1111
1112
CALL Zoltan_Interface( Model, OutMesh, StartImbalanceTol=1.1_dp, TolChange=0.02_dp, MinElems=10 )
1113
1114
FinalMesh => RedistributeMesh(Model, OutMesh, .TRUE., .FALSE.)
1115
1116
FinalMesh % Name = TRIM(Mesh % Name)
1117
FinalMesh % OutputActive = .TRUE.
1118
FinalMesh % Changed = .TRUE.
1119
1120
!Actually switch the model's mesh
1121
! also releases old mesh
1122
CALL SwitchMesh(Model, Solver, Mesh, FinalMesh)
1123
1124
CALL ReleaseMesh(OutMesh)
1125
!CALL ReleaseMesh(Mesh)
1126
1127
#else
1128
CALL FATAL(SolverName, 'ParMMG needs to be installed to use this solver!')
1129
#endif
1130
1131
END SUBROUTINE ParMMGRemeshing
1132