Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/elmerice/Solvers/CalvingRemeshMMG.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.
35
36
! Strategy:
37
!----------------
38
39
! - Use Mesh % Repartition and RedistributeMesh to send relevant (calving)
40
! mesh regions to the nominated remeshing partition
41
42
! - Remesh with MMG - done serially on nominated partition (TODO - improve nomination of partition)
43
44
! - Global node/element number renegotiation
45
46
! - Zoltan to compute new partitioning
47
48
! - RedistributeMesh back to target processors
49
50
! - Interpolate variables etc
51
52
! - Continue simulation
53
54
SUBROUTINE CalvingRemeshMMG( Model, Solver, dt, Transient )
55
56
USE MeshUtils
57
USE CalvingGeometry
58
USE MeshPartition
59
USE MeshRemeshing
60
61
IMPLICIT NONE
62
63
#include "mmg/common/libmmgtypesf.h"
64
#ifndef MMGVERSION_H
65
#define MMG_VERSION_LT(MAJOR,MINOR) 1
66
#endif
67
68
TYPE(Model_t) :: Model
69
TYPE(Solver_t), TARGET :: Solver
70
REAL(KIND=dp) :: dt
71
LOGICAL :: Transient
72
!--------------------------------------
73
TYPE(Variable_t), POINTER :: CalvingVar,DistanceVar,mmgVar
74
TYPE(ValueList_t), POINTER :: SolverParams
75
TYPE(Mesh_t),POINTER :: Mesh,GatheredMesh,NewMeshR,NewMeshRR,FinalMesh,ParMetisMesh
76
TYPE(Element_t),POINTER :: Element, ParentElem
77
INTEGER :: i,j,k,NNodes,GNBulk, GNBdry, GNNode, NBulk, Nbdry, ierr, &
78
my_cboss,MyPE, PEs,CCount, counter, GlNode_min, GlNode_max,adjList(4),&
79
front_BC_ID, front_body_id, my_calv_front,calv_front, ncalv_parts, &
80
group_calve, comm_calve, group_world,ecode, NElNodes, target_bodyid,gdofs(4), &
81
PairCount,RPairCount, NCalvNodes, croot, nonCalvBoss,&
82
NVerts, NTetras, NPrisms, NTris, NQuads, NEdges, dummyint
83
INTEGER, POINTER :: NodeIndexes(:), geom_id, TopPerm(:)=>NULL(), FrontPerm(:)=>NULL()
84
INTEGER, ALLOCATABLE :: Prnode_count(:), cgroup_membs(:),disps(:), &
85
PGDOFs_send(:),pcalv_front(:),GtoLNN(:),EdgePairs(:,:),REdgePairs(:,:), ElNodes(:),&
86
Nodes(:), IslandCounts(:), pNCalvNodes(:,:), TetraQuality(:)
87
REAL(KIND=dp) :: test_thresh, test_point(3), remesh_thresh, hmin, hmax, hgrad, hausd, &
88
newdist, Quality, PauseVolumeThresh, MaxBergVolume, RmcValue
89
REAL(KIND=dp), ALLOCATABLE :: test_dist(:), test_lset(:), Ptest_lset(:), Gtest_lset(:), &
90
target_length(:,:), Ptest_dist(:), Gtest_dist(:), hminarray(:), hausdarray(:)
91
REAL(KIND=dp), POINTER :: WorkArray(:,:) => NULL()
92
LOGICAL, ALLOCATABLE :: calved_node(:), remeshed_node(:), fixed_node(:), fixed_elem(:), &
93
elem_send(:), RmElem(:), RmNode(:),new_fixed_node(:), new_fixed_elem(:), FoundNode(:,:), &
94
UsedElem(:), NewNodes(:), RmIslandNode(:), RmIslandElem(:), PartGotNodes(:), SendNode(:)
95
LOGICAL :: ImBoss, Found, Isolated, Debug,DoAniso,NSFail,CalvingOccurs,&
96
RemeshOccurs,CheckFlowConvergence, NoNewNodes, RSuccess, Success,&
97
SaveMMGMeshes, SaveMMGSols, PauseSolvers, PauseAfterCalving, FixNodesOnRails, &
98
SolversPaused, NewIceberg, GotNodes(4), CalvingFileCreated=.FALSE., SuppressCalv,&
99
DistributedMesh,SaveTerminus,RemeshFront,RemeshIfNoCalving
100
CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, CalvingVarName, MeshName, SolName, &
101
premmgls_meshfile, mmgls_meshfile, premmgls_solfile, mmgls_solfile,&
102
RepartMethod, Filename, DistVarName
103
TYPE(Variable_t), POINTER :: TimeVar
104
INTEGER :: Time, remeshtimestep, proc, idx, island, node, MaxLSetIter, mmgloops
105
REAL(KIND=dp) :: TimeReal, PreCalveVolume, PostCalveVolume, CalveVolume, LsetMinQuality
106
107
SAVE :: WorkArray, CalvingFileCreated
108
109
SolverParams => GetSolverParams()
110
SolverName = "CalvingRemeshMMG"
111
Debug=.FALSE.
112
Mesh => Model % Mesh
113
NNodes = Mesh % NumberOfNodes
114
NBulk = Mesh % NumberOfBulkElements
115
NBdry = Mesh % NumberOfBoundaryElements
116
117
calv_front = 1
118
MyPe = ParEnv % MyPE
119
PEs = ParEnv % PEs
120
121
#if MMG_VERSION_LT(5,6)
122
PRINT*, SolverName, ': Starting MMG'
123
#elif MMG_VERSION_LT(5,8)
124
PRINT*, SolverName, ': Starting MMG'
125
#else
126
CALL FATAL(SolverName, 'Calving code only works with MMG 5.5')
127
#endif
128
129
TimeVar => VariableGet( Model % Mesh % Variables, 'Timestep' )
130
TimeReal = TimeVar % Values(1)
131
Time = INT(TimeReal)
132
133
! create mmg variable as this is added to one proc in remeshing changes following merge Nov/23
134
mmgVar => VariableGet( Model % Mesh % Variables,'MMG Loop', ThisOnly = .TRUE.)
135
IF(.NOT. ASSOCIATED(mmgVar) ) THEN
136
CALL VariableAddVector( Model % Mesh % Variables,Model % Mesh,&
137
Name='MMG Loop',Global=.TRUE.)
138
mmgVar => VariableGet( Model % Mesh % Variables,'MMG Loop' )
139
END IF
140
141
!for first time step calculate mesh volume
142
IF(Time == 1) THEN
143
CALL MeshVolume(Mesh, .TRUE., PreCalveVolume)
144
IF(MyPe == 0) PRINT*, 'First timestep precalve volume = ', PreCalveVolume
145
END IF
146
147
!Check all elements are tetras or tris:
148
DO i=1,NBulk+Nbdry
149
Element => Mesh % Elements(i)
150
ecode = Element % TYPE % ElementCode
151
IF(ecode /= 101 .AND. ecode /= 202 .AND. ecode /= 303 .AND. ecode /= 504) THEN
152
PRINT *,MyPE,' has unsupported element type: ',ecode
153
CALL Fatal(SolverName, "Invalid Element Type!")
154
END IF
155
END DO
156
157
!Get main Body ID
158
target_bodyid = Mesh % Elements(1) % BodyID
159
IF(target_bodyid /= 1) CALL Warn(SolverName, "Body ID is not 1, this case might not be well handled")
160
!For testing - set a calving levelset function to mimic calving events
161
!-------------------
162
163
!TODO - unhardcode (detect?) this
164
front_BC_id = 1
165
front_body_id = ListGetInteger( &
166
Model % BCs(front_bc_id) % Values, 'Body Id', Found, 1, Model % NumberOfBodies )
167
168
WorkArray => ListGetConstRealArray(SolverParams, "Mesh Hmin", Found)
169
IF(.NOT. Found) CALL FATAL(SolverName, 'Provide hmin input array to be iterated through: "Mesh Hmin"')
170
MaxLsetIter= SIZE(WorkArray(:,1))
171
ALLOCATE(hminarray(MaxLsetIter))
172
hminarray = WorkArray(:,1)
173
NULLIFY(WorkArray)
174
hmax = ListGetConstReal(SolverParams, "Mesh Hmax", DefValue=4000.0_dp)
175
hgrad = ListGetConstReal(SolverParams,"Mesh Hgrad", DefValue=0.5_dp)
176
WorkArray => ListGetConstRealArray(SolverParams, "Mesh Hausd", Found)
177
IF(.NOT. Found) CALL FATAL(SolverName, 'Provide hausd input array to be iterated through: "Mesh Hausd"')
178
IF(MaxLsetIter /= SIZE(WorkArray(:,1))) CALL FATAL(SolverName, 'The number of hmin options &
179
must equal the number of hausd options')
180
ALLOCATE(hausdarray(MaxLsetIter))
181
hausdarray = WorkArray(:,1)
182
NULLIFY(WorkArray)
183
remesh_thresh = ListGetConstReal(SolverParams,"Remeshing Distance", DefValue=1000.0_dp)
184
LsetMinQuality = ListGetConstReal(SolverParams,"Mesh Min Quality", DefValue=0.00001_dp)
185
RmcValue = ListGetConstReal(SolverParams,"Mesh Rmc Value", DefValue=1e-15_dp)
186
CalvingVarName = ListGetString(SolverParams, "Calving Variable Name", Found)
187
IF(.NOT. Found) THEN
188
CALL WARN(SolverName, "'Levelset Variable Name' not set so assuming 'Calving Lset'.")
189
CalvingVarName = "Calving LSet"
190
END IF
191
DistVarName = ListGetString(SolverParams, "Distance Variable Name", Found)
192
IF(.NOT. Found) THEN
193
CALL WARN(SolverName, "'Distance Variable Name' not set so assuming 'Distance'.")
194
DistVarName = "Distance"
195
END IF
196
SaveMMGMeshes = ListGetLogical(SolverParams,"Save MMGLS Meshes", DefValue=.FALSE.)
197
SaveMMGSols = ListGetLogical(SolverParams,"Save MMGLS Sols", DefValue=.FALSE.)
198
IF(SaveMMGMeshes) THEN
199
premmgls_meshfile = ListGetString(SolverParams, "Pre MMGLS Mesh Name", UnfoundFatal = .TRUE.)
200
mmgls_meshfile = ListGetString(SolverParams, "MMGLS Output Mesh Name", UnfoundFatal = .TRUE.)
201
END IF
202
IF(SaveMMGSols) THEN
203
premmgls_solfile = ListGetString(SolverParams, "Pre MMGLS Sol Name", UnfoundFatal = .TRUE.)
204
mmgls_solfile = ListGetString(SolverParams, "MMGLS Output Sol Name", UnfoundFatal = .TRUE.)
205
END IF
206
PauseAfterCalving = ListGetLogical(SolverParams, "Pause After Calving Event", Found)
207
IF(.NOT. Found) THEN
208
CALL Info(SolverName, "Can't find 'Pause After Calving Event' logical in Solver section, &
209
& assuming True")
210
PauseAfterCalving = .TRUE.
211
END IF
212
FixNodesOnRails = ListGetLogical(SolverParams,"Fix Nodes On Rails", DefValue=.TRUE.)
213
SuppressCalv = ListGetLogical(SolverParams,"Suppress Calving", DefValue=.FALSE.)
214
SaveTerminus = ListGetLogical(SolverParams,"Save Terminus", DefValue=.TRUE.)
215
RemeshFront = ListGetLogical(SolverParams,"Remesh Full Calving Front", Found)
216
IF(.NOT. Found) THEN
217
CALL Info(SolverName, "Not Found 'Remesh Full Calving Front' asssuming TRUE")
218
RemeshFront = .TRUE.
219
END IF
220
RemeshIfNoCalving = ListGetLogical(SolverParams,"Remesh if no calving", Found)
221
IF(.NOT. Found) THEN
222
CALL Info(SolverName, "Not Found 'Remesh if no calving' asssuming TRUE")
223
RemeshIfNoCalving = .TRUE.
224
END IF
225
226
! calving algo passes through 202 elems to mmg
227
i = ListGetInteger( Model % Bodies(Mesh % Elements(1) % BodyId) % Values, 'Material')
228
CALL ListAddLogical(Model % Materials(i) % Values,'mmg No Angle Detection',.TRUE.)
229
230
IF(ParEnv % MyPE == 0) THEN
231
PRINT *,ParEnv % MyPE,' hmin: ',hminarray
232
PRINT *,ParEnv % MyPE,' hmax: ',hmax
233
PRINT *,ParEnv % MyPE,' hgrad: ',hgrad
234
PRINT *,ParEnv % MyPE,' hausd: ',hausdarray
235
PRINT *,ParEnv % MyPE,' remeshing distance: ',remesh_thresh
236
PRINT *,ParEnv % MyPE,' Max Levelset Iterations ', MaxLsetIter
237
END IF
238
239
!If FlowSolver failed to converge (usually a result of weird mesh), large unphysical
240
!calving events can be predicted. So, turn off CalvingOccurs, and ensure a remesh
241
!Also undo this iterations mesh update
242
NSFail = ListGetLogical(Model % Simulation, "Flow Solution Failed",CheckFlowConvergence)
243
CalvingOccurs = ListGetLogical( Model % Simulation, 'CalvingOccurs', Found )
244
RemeshOccurs=.TRUE.
245
IF(CheckFlowConvergence) THEN
246
IF(NSFail) THEN
247
CalvingOccurs = .FALSE.
248
RemeshOccurs = .TRUE.
249
CALL Info(SolverName, "Remeshing but not calving because NS failed to converge.")
250
END IF
251
END IF
252
IF(SuppressCalv) CalvingOccurs = .FALSE.
253
254
ALLOCATE(remeshed_node(NNodes),&
255
fixed_node(NNodes))
256
remeshed_node = .FALSE.
257
fixed_node = .FALSE.
258
259
! TO DO some other wah to define remeshed nodes
260
DistanceVar => VariableGet(Mesh % Variables, DistVarName, .TRUE., UnfoundFatal=.TRUE.)
261
ALLOCATE(test_dist(NNodes))
262
test_dist = DistanceVar % Values(DistanceVar % Perm(:))
263
remeshed_node = test_dist < remesh_thresh
264
265
!Get the calving levelset function (-ve inside calving event, +ve in intact ice)
266
!-------------------
267
IF (CalvingOccurs) THEN
268
CalvingVar => VariableGet(Mesh % Variables, CalvingVarName, .TRUE., UnfoundFatal=.TRUE.)
269
270
ALLOCATE(test_lset(NNodes),&
271
calved_node(NNodes)&
272
)
273
274
calved_node = .FALSE.
275
test_lset = CalvingVar % Values(CalvingVar % Perm(:)) !TODO - quick&dirty, possibly zero perm?
276
calved_node = test_lset < 0.0
277
! calving front boundary nodes may have lset value greater than remesh dist
278
DO i=1, NNodes
279
IF(RemeshFront) THEN
280
newdist = MINVAL((/test_lset(i), test_dist(i)/))
281
ELSE
282
newdist = test_lset(i)
283
END IF
284
remeshed_node(i) = newdist < remesh_thresh
285
END DO
286
END IF
287
288
my_calv_front = 0
289
IF(ANY(remeshed_node)) THEN
290
my_calv_front = 1 !this is integer (not logical) so we can later move to multiple calving fronts
291
END IF
292
293
NCalvNodes = COUNT(remeshed_node)
294
295
!TODO - could make this more efficient by cutting out some of the elements from the calved region.
296
!This region will be remeshed too, but we discard it, so the closer we can get to the edge of the
297
!calving event, the better.
298
299
!Identify all elements which need to be sent (including those fixed in place)
300
!Here we also find extra nodes which are just beyond the remeshing threshold
301
!but which are in fixed elements, thus also need to be sent
302
ALLOCATE(elem_send(nbulk+nbdry))
303
elem_send = .FALSE.
304
305
IF(my_calv_front > 0) THEN
306
!Each partition identifies (based on nodes), elements which need to be transferred
307
DO i=1,Nbulk
308
Element => Mesh % Elements(i)
309
CALL Assert(Element % ElementIndex == i, SolverName,"Misnumbered bulk element.")
310
NodeIndexes => Element % NodeIndexes
311
NElNodes = Element % TYPE % NumberOfNodes
312
IF(ANY(remeshed_node(NodeIndexes(1:NElNodes)))) THEN
313
elem_send(i) = .TRUE.
314
IF(.NOT. ALL(remeshed_node(NodeIndexes(1:NElNodes)))) THEN
315
fixed_node(NodeIndexes(1:NElNodes)) = .TRUE.
316
END IF
317
END IF
318
END DO
319
320
remeshed_node = remeshed_node .OR. fixed_node
321
322
!Cycle boundary elements, checking parent elems
323
!BC elements follow parents
324
DO i=NBulk+1, NBulk+NBdry
325
Element => Mesh % Elements(i)
326
ParentElem => Element % BoundaryInfo % Left
327
IF(.NOT. ASSOCIATED(ParentElem)) THEN
328
ParentElem => Element % BoundaryInfo % Right
329
END IF
330
CALL Assert(ASSOCIATED(ParentElem),SolverName,"Boundary element has no parent!")
331
IF(elem_send(ParentElem % ElementIndex)) elem_send(i) = .TRUE.
332
END DO
333
334
END IF
335
DEALLOCATE(fixed_node) !reuse later
336
337
!This does nothing yet but it will be important - determine
338
!the discrete calving zones, each of which will be separately remeshed
339
!by a nominated boss partition
340
! CALL CountCalvingEvents(Model, Mesh, ccount)
341
ccount = 1
342
343
!Negotiate local calving boss - just one front for now
344
!-----------------------
345
ALLOCATE(pcalv_front(PEs))
346
pcalv_front = 0
347
CALL MPI_AllGather(my_calv_front, 1, MPI_INTEGER, pcalv_front, &
348
1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
349
350
! loop to allow negotiation for multiple calving fronts
351
! This communication allows the determination of which part of the mesh
352
! has the most calvnodes and will become cboss
353
ALLOCATE(pNCalvNodes(MAXVAL(pcalv_front),PEs))
354
DO i=1, MAXVAL(pcalv_front)
355
CALL MPI_ALLGATHER(NCalvNodes, 1, MPI_INTEGER, pNCalvNodes(i,:), &
356
1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
357
END DO
358
359
IF(Debug) THEN
360
PRINT *,mype,' debug calv_front: ',my_calv_front
361
PRINT *,mype,' debug calv_fronts: ',pcalv_front
362
END IF
363
364
ncalv_parts = COUNT(pcalv_front==calv_front) !only one front for now...
365
ALLOCATE(cgroup_membs(ncalv_parts))
366
counter = 0
367
DO i=1,PEs
368
IF(pcalv_front(i) == calv_front) THEN !one calve front
369
counter = counter + 1
370
cgroup_membs(counter) = i-1
371
END IF
372
END DO
373
IF(Debug) PRINT *,mype,' debug group: ',cgroup_membs
374
375
376
!Create an MPI_COMM for each calving region, allow gathering instead of
377
!explicit send/receive
378
CALL MPI_Comm_Group( ELMER_COMM_WORLD, group_world, ierr)
379
CALL MPI_Group_Incl( group_world, ncalv_parts, cgroup_membs, group_calve, ierr)
380
CALL MPI_Comm_create( ELMER_COMM_WORLD, group_calve, COMM_CALVE, ierr)
381
!--------------------------
382
383
!Work out to whom I send mesh parts for calving
384
!TODO - this is currently the lowest partition number which has some calving nodes,
385
!but it'd be more efficient to take the partition with the *most* calved nodes
386
!Now cboss set to part with most calving nodes
387
!NonCalvBoss will take any non calving nodes from the cboss
388
! this is still *TODO*
389
IF(My_Calv_Front>0) THEN
390
391
! assume currently only one calving front
392
my_cboss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MAXVAL(pNCalvNodes(1,:)))-1
393
nonCalvBoss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MINVAL(pNCalvNodes(1,:)))-1
394
IF(Debug) PRINT *,MyPe,' debug calving boss: ',my_cboss
395
ImBoss = MyPE == my_cboss
396
397
IF(ImBoss) THEN
398
ALLOCATE(Prnode_count(ncalv_parts))
399
Prnode_count = 0
400
END IF
401
ELSE
402
! only used if cboss has non calving nodes
403
nonCalvBoss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MINVAL(pNCalvNodes(1,:)))-1
404
my_cboss = MINLOC(pNCalvNodes(1,:), 1, MASK=pNCalvNodes(1,:)==MAXVAL(pNCalvNodes(1,:)))-1
405
ImBoss = .FALSE.
406
END IF
407
408
!croot location is i when cboss = groupmember(i)
409
DO i=1, ncalv_parts
410
IF(my_cboss /= cgroup_membs(i)) CYCLE
411
croot = i-1
412
END DO
413
414
!Redistribute the mesh (i.e. partitioning) so that cboss partitions
415
!contain entire calving/remeshing regions.
416
IF(.NOT. ASSOCIATED(Mesh % Repartition)) THEN
417
ALLOCATE(Mesh % Repartition(NBulk+NBdry), STAT=ierr)
418
IF(ierr /= 0) PRINT *,ParEnv % MyPE,' could not allocate Mesh % Repartition'
419
END IF
420
421
!TODO send non calving nodes on ImBoss to nocalvboss
422
Mesh % Repartition = ParEnv % MyPE + 1
423
DO i=1,NBulk+NBdry
424
IF(elem_send(i)) Mesh % Repartition(i) = my_cboss+1
425
IF(ImBoss .AND. .NOT. elem_send(i)) THEN
426
WRITE(Message, '(A,i0,A)') "ImBoss sending Element ",i," to NonCalvBoss"
427
CALL WARN(SolverName, Message)
428
Mesh % Repartition(i) = NonCalvBoss+1
429
END IF
430
END DO
431
432
GatheredMesh => RedistributeMesh(Model, Mesh, .TRUE., .FALSE.)
433
!Confirmed that boundary info is for Zoltan at this point
434
435
IF(ASSOCIATED(Mesh % Repartition)) THEN
436
DEALLOCATE(Mesh % Repartition)
437
Mesh % Repartition => NULL()
438
END IF
439
440
IF(Debug) THEN
441
PRINT *,ParEnv % MyPE,' gatheredmesh % nonodes: ',GatheredMesh % NumberOfNodes
442
PRINT *,ParEnv % MyPE,' gatheredmesh % neelems: ',GatheredMesh % NumberOfBulkElements, &
443
GatheredMesh % NumberOfBoundaryElements
444
END IF
445
446
!Now we have the gathered mesh, need to send:
447
! - Remeshed_node, test_lset
448
! - we can convert this into an integer code on elements (0 = leave alone, 1 = remeshed, 2 = fixed)
449
! - or we can simply send test_lset and recompute on calving_boss
450
451
IF(My_Calv_Front>0) THEN
452
!root is always zero because cboss is always lowest member of new group (0)
453
CALL MPI_Gather(COUNT(remeshed_node), 1, MPI_INTEGER, Prnode_count, 1, &
454
MPI_INTEGER, croot, COMM_CALVE,ierr)
455
456
IF(ImBoss) THEN
457
458
IF(Debug) PRINT *,'boss debug prnode_count: ', Prnode_count
459
GLNode_max = MAXVAL(GatheredMesh % ParallelInfo % GlobalDOFs)
460
GLNode_min = MINVAL(GatheredMesh % ParallelInfo % GlobalDOFs)
461
GNBulk = GatheredMesh % NumberOfBulkElements
462
GNBdry = GatheredMesh % NumberOfBoundaryElements
463
GNNode = GatheredMesh % NumberOfNodes
464
465
ALLOCATE(PGDOFs_send(SUM(Prnode_count)), &
466
Ptest_dist(SUM(Prnode_count)), &
467
disps(ncalv_parts),GtoLNN(GLNode_min:GLNode_max),&
468
gtest_dist(GNNode),&
469
fixed_node(GNNode),&
470
fixed_elem(GNBulk + GNBdry))
471
472
IF(CalvingOccurs) ALLOCATE(Ptest_lset(SUM(Prnode_count)), &
473
gtest_lset(GNNode))
474
fixed_node = .FALSE.
475
fixed_elem = .FALSE.
476
IF(CalvingOccurs) gtest_lset = remesh_thresh + 500.0 !Ensure any far (unshared) nodes are fixed
477
478
!Compute the global to local map
479
DO i=1,GNNode
480
GtoLNN(GatheredMesh % ParallelInfo % GlobalDOFs(i)) = i
481
END DO
482
483
ELSE
484
ALLOCATE(disps(1), prnode_count(1))
485
END IF
486
487
!Compute the offset in the gathered array from each part
488
IF(ImBoss) THEN
489
disps(1) = 0
490
DO i=2,ncalv_parts
491
disps(i) = disps(i-1) + prnode_count(i-1)
492
END DO
493
END IF
494
495
IF (CalvingOccurs) THEN
496
!Gather the level set function
497
CALL MPI_GatherV(PACK(test_lset,remeshed_node), COUNT(remeshed_node), MPI_DOUBLE_PRECISION, &
498
Ptest_lset, Prnode_count, disps, MPI_DOUBLE_PRECISION, croot, COMM_CALVE,ierr)
499
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
500
END IF
501
!Gather the distance to front, but let it output to Ptest_lset to avoid repetitive code
502
CALL MPI_GatherV(PACK(test_dist,remeshed_node), COUNT(remeshed_node), MPI_DOUBLE_PRECISION, &
503
Ptest_dist, Prnode_count, disps, MPI_DOUBLE_PRECISION, croot, COMM_CALVE,ierr)
504
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
505
!END IF
506
!Gather the GDOFs
507
CALL MPI_GatherV(PACK(Mesh % ParallelInfo % GlobalDOFs,remeshed_node), &
508
COUNT(remeshed_node), MPI_INTEGER, PGDOFs_send, Prnode_count, &
509
disps, MPI_INTEGER, croot, COMM_CALVE,ierr)
510
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
511
512
!Nodes with levelset value greater than remesh_thresh are kept fixed
513
!as are any elements with any fixed_nodes
514
!This implies the levelset is a true distance function, everywhere in the domain.
515
IF(ImBoss) THEN
516
DO i=1,SUM(Prnode_count)
517
k = GtoLNN(PGDOFs_send(i))
518
IF(k==0) CALL Fatal(SolverName, "Programming error in Global to Local NNum map")
519
IF(CalvingOccurs) Gtest_lset(k) = Ptest_lset(i)
520
Gtest_dist(k) = Ptest_dist(i)
521
END DO
522
523
! calving front boundary nodes may have lset value greater than remesh dist
524
! so use dist so front boundary nodes aren't fixed
525
IF(CalvingOccurs) THEN
526
DO i=1,GNNode
527
IF(RemeshFront) THEN
528
newdist = MINVAL((/Gtest_lset(i), Gtest_dist(i)/))
529
ELSE
530
newdist = Gtest_lset(i)
531
END IF
532
fixed_node(i) = newdist > remesh_thresh
533
END DO
534
ELSE
535
fixed_node = Gtest_dist > remesh_thresh
536
END IF
537
538
DO i=1, GNBulk + GNBdry
539
Element => GatheredMesh % Elements(i)
540
IF(ANY(fixed_node(Element % NodeIndexes))) THEN
541
fixed_elem(i) = .TRUE.
542
END IF
543
END DO
544
END IF
545
END IF ! My calving front > 1
546
547
! if no calving and we don't want to remesh then go to end
548
! need gathered mesh for terminus output
549
! also need to update mesh deformation variables
550
IF(.NOT. RemeshIfNoCalving .AND. .NOT. CalvingOccurs .AND. .NOT. NSFail) THEN
551
RSuccess =.FALSE.
552
GO TO 30
553
END IF
554
555
!Nominated partition does the remeshing
556
IF(ImBoss) THEN
557
IF (CalvingOccurs) THEN
558
559
CALL MeshVolume(GatheredMesh, .FALSE., PreCalveVolume)
560
561
CALL GetCalvingEdgeNodes(GatheredMesh, .FALSE., EdgePairs, PairCount)
562
563
mmgloops = 0
564
565
10 CONTINUE
566
567
mmgloops=mmgloops+1
568
hmin = hminarray(mmgloops)
569
hausd = hausdarray(mmgloops)
570
571
WRITE(Message, '(A,F10.5,A,F10.5)') 'Applying levelset with Hmin ',Hmin, ' and Hausd ', Hausd
572
CALL INFO(SolverName, Message)
573
!Initialise MMG datastructures
574
mmgMesh = 0
575
mmgLs = 0
576
mmgMet = 0
577
578
CALL MMG3D_Init_mesh(MMG5_ARG_start, &
579
MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppLs,mmgLs, &
580
MMG5_ARG_end)
581
582
! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)
583
CALL Set_MMG3D_Mesh(GatheredMesh, .TRUE., EdgePairs, PairCount)
584
585
!Request isosurface discretization
586
CALL MMG3D_Set_iparameter(mmgMesh, mmgLs, MMGPARAM_iso, 1,ierr)
587
588
!set angle detection on (1, default) and set threshold angle (85 degrees)
589
!TODO - here & in MeshRemesh, need a better strategy than automatic detection
590
!i.e. feed in edge elements.
591
592
!I think these are defunct as they are called in RemeshMMG
593
! this is unless cutting lset and anisotrophic remeshing take place in one step
594
!print *, 'first call of angle detection $$$ - turned on '
595
CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_angle, &
596
0,ierr)
597
598
!!! angle detection changes calving in next time steps dramatically
599
!! if on automatically turns angle on
600
!CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgSol,MMGPARAM_angleDetection,&
601
! 85.0_dp,ierr)
602
603
!Turn on debug (1)
604
CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_debug, &
605
1,ierr)
606
607
!Turn off automatic aniso remeshing (0)
608
CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgLs,MMGPARAM_aniso, &
609
0,ierr)
610
611
!Set geometric parameters for remeshing
612
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hmin,&
613
hmin,ierr)
614
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hmax,&
615
hmax,ierr)
616
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hausd,&
617
hausd,ierr)
618
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_hgrad,&
619
hgrad,ierr)
620
621
CALL MMG3D_SET_DPARAMETER(mmgMesh,mmgLs,MMGPARAM_rmc,&
622
RmcValue,ierr)
623
624
CALL MMG3D_SET_IPARAMETER(mmgMesh,mmgSol,MMGPARAM_nosizreq,&
625
1,ierr)
626
627
!Feed in the level set distance
628
CALL MMG3D_SET_SOLSIZE(mmgMesh, mmgLs, MMG5_Vertex, GNNode ,MMG5_Scalar, ierr)
629
DO i=1,GNNode
630
CALL MMG3D_Set_scalarSol(mmgLs,&
631
Gtest_lset(i), &
632
i,ierr)
633
END DO
634
635
IF(Debug) THEN
636
PRINT *,' boss fixed node: ',COUNT(fixed_node),SIZE(fixed_node)
637
PRINT *,' boss fixed elem: ',COUNT(fixed_elem),SIZE(fixed_elem)
638
END IF
639
640
!Set required nodes and elements
641
DO i=1,GNNode
642
IF(fixed_node(i)) THEN
643
CALL MMG3D_SET_REQUIREDVERTEX(mmgMesh,i,ierr)
644
END IF
645
END DO
646
647
DO i=1,GNBulk + GNBdry
648
IF(fixed_elem(i)) THEN
649
IF(GatheredMesh % Elements(i) % TYPE % DIMENSION == 3) THEN
650
CALL MMG3D_SET_REQUIREDTETRAHEDRON(mmgMesh,i,ierr)
651
ELSEIF(GatheredMesh % Elements(i) % TYPE % DIMENSION == 2) THEN
652
CALL MMG3D_SET_REQUIREDTRIANGLE(mmgMesh,i-GNBulk,ierr)
653
END IF
654
END IF
655
END DO
656
657
!> 4) (not mandatory): check if the number of given entities match with mesh size
658
CALL MMG3D_Chk_meshData(mmgMesh,mmgLs,ierr)
659
IF ( ierr /= 1 ) CALL EXIT(105)
660
IF(SaveMMGMeshes) THEN
661
WRITE(MeshName, '(A,i0,A)') TRIM(premmgls_meshfile), time, '.mesh'
662
CALL MMG3D_SaveMesh(mmgMesh,MeshName,LEN(TRIM(MeshName)),ierr)
663
END IF
664
IF(SaveMMGSols) THEN
665
WRITE(SolName, '(A,i0,A)') TRIM(premmgls_solfile), time, '.sol'
666
CALL MMG3D_SaveSol(mmgMesh, mmgLs,SolName,LEN(TRIM(SolName)),ierr)
667
END IF
668
!> ------------------------------ STEP II --------------------------
669
!! remesh function
670
! mmg5.5 not using isosurface discretization. More robust to remesh seperately
671
! addtionally computationally lighter as iceberg are not finely remeshed
672
CALL MMG3D_mmg3dls(mmgMesh,mmgLs,0_dp,ierr)
673
674
IF ( ierr == MMG5_STRONGFAILURE ) THEN
675
PRINT*,"BAD ENDING OF MMG3DLS: UNABLE TO SAVE MESH", Time
676
!! Release mmg mesh
677
CALL MMG3D_Free_all(MMG5_ARG_start, &
678
MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppMet,mmgSol, &
679
MMG5_ARG_end)
680
WRITE(Message, '(A,F10.5,A,F10.5)') 'Levelset failed with Hmin ',Hmin, ' and Hausd ', Hausd
681
CALL WARN(SolverName, Message)
682
IF(mmgloops==MaxLsetIter) THEN
683
Success=.FALSE.
684
CalvingOccurs = .FALSE.
685
GO TO 20
686
ELSE
687
GO TO 10
688
END IF
689
ELSE IF ( ierr == MMG5_LOWFAILURE ) THEN
690
PRINT*,"LOW ENDING OF MMG3DLS", time
691
ENDIF
692
IF(SaveMMGMeshes) THEN
693
WRITE(MeshName, '(A,i0,A)') TRIM(mmgls_meshfile), time, '.mesh'
694
CALL MMG3D_SaveMesh(mmgMesh,MeshName,LEN(TRIM(MeshName)),ierr)
695
END IF
696
IF(SaveMMGSols) THEN
697
WRITE(SolName, '(A,i0,A)') TRIM(mmgls_solfile), time, '.sol'
698
CALL MMG3D_SaveSol(mmgMesh, mmgLs,SolName,LEN(TRIM(SolName)),ierr)
699
END IF
700
701
CALL MMG3D_Get_meshSize(mmgMesh,NVerts,NTetras,NPrisms,NTris,NQuads,NEdges,ierr)
702
703
counter=0
704
Do i=1, NTetras
705
CALL MMG3D_Get_TetrahedronQuality(mmgMesh, mmgSol, i, Quality)
706
IF(Quality == 0) CALL WARN(SolverName, 'Levelset could not determine elem quality')
707
IF(Quality <= LsetMinQuality) counter = counter+1
708
END DO
709
IF ( Counter > 0 ) THEN
710
PRINT*,"Bad elements detected - reruning levelset"
711
!! Release mmg mesh
712
CALL MMG3D_Free_all(MMG5_ARG_start, &
713
MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppMet,mmgSol, &
714
MMG5_ARG_end)
715
WRITE(Message, '(A,F10.5,A,F10.5)') 'Levelset failed with Hmin ',Hmin, ' and Hausd ', Hausd
716
CALL WARN(SolverName, Message)
717
IF(mmgloops==MaxLsetIter) THEN
718
Success=.FALSE.
719
CalvingOccurs = .FALSE.
720
GO TO 20
721
ELSE
722
GO TO 10
723
END IF
724
!STOP MMG5_STRONGFAILURE
725
ENDIF
726
727
CALL Get_MMG3D_Mesh(NewMeshR, .TRUE., new_fixed_node, new_fixed_elem, Calving=.TRUE.)
728
729
NewMeshR % Name = Mesh % Name
730
731
NNodes = NewMeshR % NumberOfNodes
732
NBulk = NewMeshR % NumberOfBulkElements
733
NBdry = NewMeshR % NumberOfBoundaryElements
734
IF(DEBUG) PRINT *, 'NewMeshR nonodes, nbulk, nbdry: ',NNodes, NBulk, NBdry
735
736
!Clear out unneeded elements
737
!Body elems with BodyID 3 (2) are the calving event (remaining domain)
738
!Boundary elems seem to have tags 0,1,10...
739
! 0 seems to be the edge triangles, 1 the outer surface (all bcs) and 2 the new level set surf
740
ALLOCATE(RmElem(NBulk+NBdry), RmNode(NNodes))
741
RmElem = .FALSE.
742
RmNode = .TRUE.
743
744
DO i=1,NBulk
745
Element => NewMeshR % Elements(i)
746
NElNodes = Element % TYPE % NumberOfNodes
747
748
IF(Element % TYPE % ElementCode /= 504) &
749
PRINT *,'Programming error, bulk type: ',Element % TYPE % ElementCode
750
IF(NElNodes /= 4) PRINT *,'Programming error, bulk nonodes: ',NElNodes
751
752
!outbody - mark for deletion and move on
753
IF(Element % BodyID == 3) THEN
754
RmElem(i) = .TRUE.
755
!Deal with an MMG3D eccentricity - returns erroneous GlobalDOF = 10
756
!on some split calving elements
757
NewMeshR % ParallelInfo % GlobalDOFs(Element % NodeIndexes) = 0
758
CYCLE
759
ELSE IF(Element % BodyID == 2) THEN
760
Element % BodyID = target_bodyid
761
ELSE
762
PRINT *,'Erroneous body id: ',Element % BodyID
763
CALL Fatal(SolverName, "Bad body id!")
764
END IF
765
766
!Get rid of any isolated elements (I think?)
767
! This may not be necessary, if the calving levelset is well defined
768
CALL MMG3D_Get_AdjaTet(mmgMesh, i, adjList,ierr)
769
IF(ALL(adjList == 0)) THEN
770
771
!check if this is truly isolated or if it's at a partition boundary
772
isolated = .TRUE.
773
gdofs = NewMeshR % ParallelInfo % GlobalDOFs(Element % NodeIndexes(1:NELNodes))
774
DO j=1,GatheredMesh % NumberOfNodes
775
IF(ANY(gdofs == GatheredMesh % ParallelInfo % GlobalDOFs(j)) .AND. &
776
GatheredMesh % ParallelInfo % GInterface(j)) THEN
777
isolated = .FALSE.
778
EXIT
779
END IF
780
END DO
781
782
IF(isolated) THEN
783
RmElem(i) = .TRUE.
784
CALL WARN(SolverName, 'Found an isolated body element.')
785
END IF
786
END IF
787
788
!Mark all nodes in valid elements for keeping
789
RmNode(Element % NodeIndexes(1:NElNodes)) = .FALSE.
790
END DO
791
792
!Same thru the boundary elements
793
!If their parent elem is body 3, delete, *unless* they have constraint 10 (the calving face)
794
!in which case use mmg3d function to get the adjacent tetras to find the valid parent
795
DO i=NBulk+1, NBulk + NBdry
796
Element => NewMeshR % Elements(i)
797
NElNodes = Element % TYPE % NumberOfNodes
798
799
!Get rid of non-triangles
800
IF(Element % TYPE % ElementCode /= 303) THEN
801
RmElem(i) = .TRUE.
802
CYCLE
803
END IF
804
805
!Get rid of boundary elements without BCID (newly created internal)
806
IF(Element % BoundaryInfo % Constraint == 0) THEN
807
RmElem(i) = .TRUE.
808
CYCLE
809
END IF
810
811
ParentElem => Element % BoundaryInfo % Left
812
813
IF(ParentElem % BodyID == 3) THEN
814
815
!Not needed
816
IF(Element % BoundaryInfo % Constraint /= 10) THEN
817
!TODO, test constraint == 10 for other BC numbers on front
818
819
RmElem(i) = .TRUE.
820
821
!Switch parent elem to elem in remaining domain
822
ELSE
823
CALL MMG3D_Get_AdjaTet(mmgMesh, ParentElem % ElementIndex, adjList,ierr)
824
DO j=1,4
825
IF(adjlist(j) == 0) CYCLE
826
IF(NewMeshR % Elements(adjlist(j)) % BodyID == target_bodyid) THEN
827
Element % BoundaryInfo % Left => NewMeshR % Elements(adjList(j))
828
EXIT
829
END IF
830
END DO
831
END IF
832
833
!Edge case - unconnected bulk element
834
ELSE IF(RmElem(ParentElem % ElementIndex)) THEN
835
RmElem(i) = .TRUE.
836
END IF
837
838
END DO
839
840
!! Release mmg mesh
841
CALL MMG3D_Free_all(MMG5_ARG_start, &
842
MMG5_ARG_ppMesh,mmgMesh,MMG5_ARG_ppLs,mmgLs, &
843
MMG5_ARG_end)
844
845
!MMG3DLS returns constraint = 10 on newly formed boundary elements
846
!(i.e. the new calving front). Here it is set to front_BC_id
847
!And set all BC BodyIDs based on constraint
848
DO i=NBulk+1, NBulk + NBdry
849
850
IF(RmElem(i)) CYCLE
851
852
Element => NewMeshR % Elements(i)
853
geom_id => Element % BoundaryInfo % Constraint
854
855
IF(geom_id == 10) THEN
856
geom_id = front_BC_id
857
858
Element % BodyId = front_body_id
859
END IF
860
861
CALL Assert((geom_id > 0) .AND. (geom_id <= Model % NumberOfBCs),&
862
SolverName,"Unexpected BC element body id!")
863
864
Element % BodyId = ListGetInteger( &
865
Model % BCs(geom_id) % Values, 'Body Id', Found, 1, Model % NumberOfBodies )
866
END DO
867
868
869
!TODO - issue - MMG3Dls will mark new nodes 'required' if they split a previous
870
!boundary element. but only *front* boundary elements? Have confirmed it ONLY returns
871
!required & 10 for calving front nodes, and it's not to do with manually setting required stuff.
872
!But, the model does complain about required entities if the hole is internal, but no weird
873
!GIDs are returned.
874
IF(Debug) THEN
875
DO i=1,GatheredMesh % NumberOfNodes
876
PRINT *, ParEnv % MyPE,' debug old ',i,&
877
' GDOF: ',GatheredMesh % ParallelInfo % GlobalDOFs(i),&
878
' xyz: ',&
879
GatheredMesh % Nodes % x(i),&
880
GatheredMesh % Nodes % y(i),&
881
GatheredMesh % Nodes % z(i),fixed_node(i)
882
IF(fixed_node(i)) THEN
883
IF(.NOT. ANY(NewMeshR % ParallelInfo % GlobalDOFs == &
884
GatheredMesh % ParallelInfo % GlobalDOFs(i))) CALL Fatal(SolverName, &
885
"Missing required node in output!")
886
END IF
887
END DO
888
END IF
889
890
!output calving stats to file and find maxbergvolume
891
CALL CalvingStatsMMG(SolverParams, NewMeshR, RmNode, RmElem, &
892
CalvingFileCreated, MaxBergVolume)
893
894
!Chop out the flagged elems and nodes
895
CALL CutMesh(NewMeshR, RmNode, RmElem)
896
897
NNodes = NewMeshR % NumberOfNodes
898
NBulk = NewMeshR % NumberOfBulkElements
899
NBdry = NewMeshR % NumberOfBoundaryElements
900
901
IF(RemeshFront) THEN ! if just remesh where calving occurs cannot remove
902
!islands as mesh already split
903
!sometimes some icebergs are not removed fully from the MMG levelset
904
!find all connected nodes in groups and remove in groups not in the
905
!main icebody
906
!limit here of 10 possible mesh 'islands'
907
ALLOCATE(FoundNode(10, NNodes), ElNodes(4), &
908
UsedElem(NBulk))
909
FoundNode = .FALSE.! count of nodes found
910
UsedElem = .FALSE. !count of elems used
911
island=0 ! count of different mesh islands
912
NoNewNodes=.TRUE. ! whether node has neighour
913
DO WHILE(COUNT(FoundNode) < NNodes)
914
IF(NoNewNodes) THEN
915
island = island + 1
916
NewIceberg = .TRUE.
917
END IF
918
NoNewNodes = .TRUE.
919
DO i=1, NBulk
920
IF(UsedElem(i)) CYCLE
921
Element => NewMeshR % Elements(i)
922
ElNodes = Element % NodeIndexes
923
! if there are not any matching nodes and its not a new iceberg
924
DO j=1, 4
925
GotNodes(j) = ANY(FoundNode(:, ElNodes(j)))
926
END DO
927
IF(ALL(.NOT. GotNodes) .AND. .NOT. NewIceberg) CYCLE
928
NewIceberg = .FALSE.
929
UsedElem(i) = .TRUE.
930
FoundNode(island, ElNodes) = .TRUE.
931
NoNewNodes = .FALSE.
932
END DO
933
END DO
934
935
ALLOCATE(IslandCounts(10))
936
DO i=1,10
937
IslandCounts(i) = COUNT(FoundNode(i, :))
938
END DO
939
Island = COUNT(IslandCounts > 0)
940
DEALLOCATE(IslandCounts) ! reused
941
942
! if iceberg not removed mark nodes and elems
943
IF(Island > 1) THEN
944
CALL WARN(SolverName, 'Mesh island found after levelset- removing...')
945
ALLOCATE(RmIslandNode(NNodes), RmIslandElem(Nbulk+NBdry),&
946
IslandCounts(Island))
947
RmIslandNode = .FALSE.
948
RmIslandElem = .FALSE.
949
counter=0
950
DO i=1, 10
951
IF(COUNT(FoundNode(i, :)) == 0) CYCLE
952
counter=counter+1
953
IslandCounts(counter) = COUNT(FoundNode(i, :))
954
END DO
955
DO i=1, Island
956
IF(IslandCounts(i) < MAXVAL(IslandCounts)) THEN
957
DO j=1, NNodes
958
IF(.NOT. FoundNode(i,j)) CYCLE! if not part of island
959
RmIslandNode(j) = .TRUE.
960
END DO
961
END IF
962
END DO
963
! mark all elems with rm nodes as need removing
964
nodes = PACK((/ (i,i=1,SIZE(RmIslandNode)) /), RmIslandNode .eqv. .TRUE.)
965
DO i=1, Nbulk+Nbdry
966
Element => NewMeshR % Elements(i)
967
ElNodes = Element % NodeIndexes
968
!any([(any(A(i) == B), i = 1, size(A))])
969
IF( .NOT. ANY([(ANY(ElNodes(i)==Nodes), i = 1, SIZE(ElNodes))])) CYCLE
970
RmIslandElem(i) = .TRUE.
971
END DO
972
973
!Chop out missed iceberg if they exist
974
CALL CutMesh(NewMeshR, RmIslandNode, RmIslandElem)
975
976
!modify rmelem and rmnode to include island removal
977
counter=0
978
DO i=1, SIZE(RmElem)
979
IF(RmElem(i)) CYCLE
980
counter=counter+1
981
IF(RmIslandElem(Counter)) RmElem(i) =.TRUE.
982
END DO
983
counter=0
984
DO i=1, SIZE(RmNode)
985
IF(RmNode(i)) CYCLE
986
counter=counter+1
987
IF(RmIslandNode(Counter)) RmNode(i) =.TRUE.
988
END DO
989
CALL INFO(SolverName, 'Mesh island removed', level=10)
990
END IF
991
END IF !remeshfront
992
993
END IF ! CalvingOccurs
994
995
20 CONTINUE
996
997
DEALLOCATE(hminarray, hausdarray)
998
999
DoAniso = .TRUE.
1000
IF(DoAniso .AND. CalvingOccurs) THEN
1001
1002
new_fixed_elem = PACK(new_fixed_elem, .NOT. RmElem)
1003
new_fixed_node = PACK(new_fixed_node, .NOT. RmNode)
1004
1005
IF(Debug) THEN
1006
DO i=1,NewMeshR % NumberOfNodes
1007
PRINT *, ParEnv % MyPE,' debug new ',i,&
1008
' GDOF: ',NewMeshR % ParallelInfo % GlobalDOFs(i),&
1009
' xyz: ',&
1010
NewMeshR % Nodes % x(i),&
1011
NewMeshR % Nodes % y(i),&
1012
NewMeshR % Nodes % z(i)
1013
1014
IF(NewMeshR % ParallelInfo % GlobalDOFs(i) == 0) CYCLE
1015
IF(.NOT. ANY(GatheredMesh % ParallelInfo % GlobalDOFs == &
1016
NewMeshR % ParallelInfo % GlobalDOFs(i))) CALL Warn(SolverName, &
1017
"Unexpected GID")
1018
END DO
1019
END IF
1020
1021
!Anisotropic mesh improvement following calving cut:
1022
!TODO - unhardcode! Also this doesn't work very well yet.
1023
!ALLOCATE(target_length(NewMeshR % NumberOfNodes,3))
1024
!target_length(:,1) = 300.0
1025
!target_length(:,2) = 300.0
1026
!target_length(:,3) = 50.0
1027
1028
!Parameters for anisotropic remeshing are set in the Materials section, or can &
1029
!optionally be passed as a valuelist here. They have prefix RemeshMMG3D
1030
!TODO - apparently there is beta-testing capability to do both levelset cut and anisotropic
1031
!remeshing in the same step:
1032
!https://forum.mmgtools.org/t/level-set-and-anisotropic-mesh-optimization/369/3
1033
! GetCalvingEdgeNodes detects all shared boundary edges, to keep them sharp
1034
1035
! calculate calved volume
1036
CALL MeshVolume(NewMeshR, .FALSE., PostCalveVolume)
1037
1038
CalveVolume = PreCalveVolume - PostCalveVolume
1039
1040
PRINT*, 'CalveVolume', CalveVolume, 'MaxBergVolume', MaxBergVolume
1041
1042
CALL GetCalvingEdgeNodes(NewMeshR, .FALSE., REdgePairs, RPairCount)
1043
! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)
1044
CALL RemeshMMG3D(Model, NewMeshR, NewMeshRR,REdgePairs, RPairCount,&
1045
NodeFixed=new_fixed_node, ElemFixed=new_fixed_elem, Success=RSuccess)
1046
IF(.NOT. RSuccess) THEN
1047
CALL WARN(SolverName, 'Remeshing failed - no calving at this timestep')
1048
GO TO 30 ! remeshing failed so no calving at this timestep
1049
END IF
1050
CALL ReleaseMesh(NewMeshR)
1051
NewMeshR => NewMeshRR
1052
NewMeshRR => NULL()
1053
1054
!Update parallel info from old mesh nodes (shared node neighbours)
1055
CALL MapNewParallelInfo(GatheredMesh, NewMeshR)
1056
1057
ELSE IF (DoAniso) THEN
1058
! remeshing but no calving
1059
CALL GetCalvingEdgeNodes(GatheredMesh, .FALSE., REdgePairs, RPairCount)
1060
! now Set_MMG3D_Mesh(Mesh, Parallel, EdgePairs, PairCount)
1061
CALL RemeshMMG3D(Model, GatheredMesh, NewMeshR,REdgePairs, RPairCount,&
1062
NodeFixed=fixed_node, ElemFixed=fixed_elem, Success=RSuccess)
1063
!Update parallel info from old mesh nodes (shared node neighbours)
1064
IF(.NOT. RSuccess) THEN
1065
CALL WARN(SolverName, 'Remeshing failed - no calving at this timestep')
1066
GO TO 30 ! remeshing failed so no calving at this timestep
1067
END IF
1068
CALL MapNewParallelInfo(GatheredMesh, NewMeshR)
1069
1070
ELSE ! Not DoAniso
1071
1072
!Update parallel info from old mesh nodes (shared node neighbours)
1073
IF (CalvingOccurs) CALL MapNewParallelInfo(GatheredMesh, NewMeshR)
1074
1075
END IF
1076
1077
CALL ReleaseMesh(GatheredMesh)
1078
! CALL ReleaseMesh(NewMeshR)
1079
GatheredMesh => NewMeshR
1080
NewMeshR => NULL()
1081
NewMeshRR => NULL()
1082
END IF ! ImBoss
1083
1084
30 CONTINUE
1085
1086
!Wait for all partitions to finish
1087
1088
CALL MPI_GROUP_FREE(group_world,ierr)
1089
CALL MPI_GROUP_FREE(group_calve,ierr)
1090
IF(My_Calv_Front > 0) &
1091
CALL MPI_COMM_FREE(COMM_CALVE,ierr)
1092
1093
CALL MPI_BARRIER(ELMER_COMM_WORLD,ierr)
1094
1095
CALL MPI_BCAST(CalvingFileCreated, 1, MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)
1096
CALL MPI_BCAST(RSuccess, 1, MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)
1097
IF(.NOT. RSuccess) THEN
1098
IF(ImBoss .AND. CalvingFileCreated) THEN
1099
Filename = ListGetString(SolverParams,"Calving Stats File Name", Found)
1100
IF(.NOT. Found) THEN
1101
CALL WARN('CalvingStat', 'Output file name not given so using CalvingStats.txt')
1102
Filename = "CalvingStats.txt"
1103
END IF
1104
OPEN( 36, FILE=filename, STATUS='UNKNOWN', POSITION='APPEND')
1105
WRITE(36, '(A,i0)') 'Remeshing failed: ', GetTimestep()
1106
CLOSE(36)
1107
END IF
1108
1109
CALL ReleaseMesh(GatheredMesh)
1110
! Release GatheredMesh % Redistribution
1111
IF(ASSOCIATED(GatheredMesh % Repartition)) THEN
1112
DEALLOCATE(GatheredMesh % Repartition)
1113
GatheredMesh % Repartition => NULL()
1114
END IF
1115
SolversPaused = ListGetLogical(Model % Simulation, 'Calving Pause Solvers', DefValue=.FALSE.)
1116
!remove mesh update
1117
IF(.NOT. SolversPaused) THEN
1118
CALL ResetMeshUpdate(Model, Solver)
1119
ELSE ! solvers paused so mesh must have changed since last free surface
1120
Mesh % MeshTag = Mesh % MeshTag + 1
1121
END IF
1122
1123
! make sure solvers are unpaused so front advances
1124
CALL PauseCalvingSolvers(Model, SolverParams, .FALSE.)
1125
RETURN
1126
END IF
1127
1128
!Now each partition has GatheredMesh, we need to renegotiate globalDOFs
1129
CALL RenumberGDOFs(Mesh, GatheredMesh)
1130
1131
!and global element numbers
1132
CALL RenumberGElems(GatheredMesh)
1133
1134
!Some checks on the new mesh
1135
!----------------------------
1136
DO i=1,GatheredMesh % NumberOfNodes
1137
IF(GatheredMesh % ParallelInfo % GInterface(i)) THEN
1138
IF(.NOT. ASSOCIATED(GatheredMesh % ParallelInfo % Neighbourlist(i) % Neighbours)) &
1139
CALL Fatal(SolverName, "Neighbourlist not associated!")
1140
IF(SIZE(GatheredMesh % ParallelInfo % Neighbourlist(i) % Neighbours) < 2) &
1141
CALL Fatal(SolverName, "Neighbourlist too small!")
1142
END IF
1143
END DO
1144
1145
DO i=1,GatheredMesh % NumberOfNodes
1146
IF(.NOT. ASSOCIATED(GatheredMesh % ParallelInfo % NeighbourList(i) % Neighbours)) &
1147
CALL Fatal(SolverName, 'Unassociated Neighbourlist % Neighbours')
1148
IF(GatheredMesh % ParallelInfo % GlobalDOFs(i) == 0) &
1149
CALL Fatal(SolverName, 'Bad GID 0')
1150
END DO
1151
1152
ParEnv % IsNeighbour(:) = .FALSE.
1153
DO i=1, Mesh % NumberOfNodes
1154
IF ( ASSOCIATED(Mesh % ParallelInfo % NeighbourList(i) % Neighbours) ) THEN
1155
DO j=1,SIZE(Mesh % ParallelInfo % NeighbourList(i) % Neighbours)
1156
proc = Mesh % ParallelInfo % NeighbourList(i) % Neighbours(j)
1157
IF ( ParEnv % Active(proc+1).AND.proc/=ParEnv % MYpe) &
1158
ParEnv % IsNeighbour(proc+1) = .TRUE.
1159
END DO
1160
END IF
1161
END DO
1162
1163
IF(SaveTerminus) THEN
1164
IF(RemeshFront) THEN ! got entire front
1165
CALL SaveTerminusPosition(Model, Solver, GatheredMesh, ImBoss)
1166
ELSE ! only remeshing where bergs calve
1167
CALL MakePermUsingMask( Model, Solver, GatheredMesh, "Calving Front Mask", &
1168
.FALSE., FrontPerm, dummyint)
1169
CALL MakePermUsingMask( Model, Solver, GatheredMesh, "Top Surface Mask", &
1170
.FALSE., TopPerm, dummyint)
1171
1172
IF(.NOT. ASSOCIATED(GatheredMesh % Repartition)) THEN
1173
ALLOCATE(GatheredMesh % Repartition(GatheredMesh % NumberOfBulkElements+&
1174
GatheredMesh % NumberOfBoundaryElements), STAT=ierr)
1175
IF(ierr /= 0) PRINT*, ParEnv % MyPE, 'Unable to allocate mesh % repartition'
1176
END IF
1177
1178
GatheredMesh % Repartition = ParEnv % MyPE + 1
1179
IF(ImBoss) DEALLOCATE(fixed_node)
1180
DEALLOCATE(elem_send)
1181
ALLOCATE(SendNode(GatheredMesh % NumberOfNodes), &
1182
fixed_node(GatheredMesh % NumberOfNodes))
1183
SendNode = .FALSE.; fixed_node = .FALSE.
1184
DO i=1, GatheredMesh % NumberOfNodes
1185
IF( (TopPerm(i)>0) .AND. (FrontPerm(i)>0)) THEN
1186
SendNode(i) = .TRUE.
1187
END IF
1188
END DO
1189
1190
ALLOCATE(elem_send(GatheredMesh % NumberOfBulkElements+&
1191
GatheredMesh % NumberOfBoundaryElements))
1192
elem_send = .FALSE.
1193
DO i=1,GatheredMesh % NumberOfBulkElements
1194
Element => GatheredMesh % Elements(i)
1195
CALL Assert(Element % ElementIndex == i, SolverName,"Misnumbered bulk element.")
1196
NodeIndexes => Element % NodeIndexes
1197
NElNodes = Element % TYPE % NumberOfNodes
1198
IF(ANY(SendNode(NodeIndexes(1:NElNodes)))) THEN
1199
elem_send(i) = .TRUE.
1200
IF(.NOT. ALL(SendNode(NodeIndexes(1:NElNodes)))) THEN
1201
fixed_node(NodeIndexes(1:NElNodes)) = .TRUE.
1202
END IF
1203
END IF
1204
END DO
1205
1206
SendNode = SendNode .OR. fixed_node
1207
1208
!Cycle boundary elements, checking parent elems
1209
!BC elements follow parents
1210
DO i=GatheredMesh % NumberOfBulkElements+1, GatheredMesh % NumberOfBulkElements+&
1211
GatheredMesh % NumberOfBoundaryElements
1212
Element => GatheredMesh % Elements(i)
1213
ParentElem => Element % BoundaryInfo % Left
1214
IF(.NOT. ASSOCIATED(ParentElem)) THEN
1215
ParentElem => Element % BoundaryInfo % Right
1216
END IF
1217
CALL Assert(ASSOCIATED(ParentElem),SolverName,"Boundary element has no parent!")
1218
IF(elem_send(ParentElem % ElementIndex)) elem_send(i) = .TRUE.
1219
END DO
1220
1221
DO i=1, GatheredMesh % NumberOfBulkElements + GatheredMesh % NumberOfBoundaryElements
1222
IF(Elem_send(i)) GatheredMesh % RePartition(i) = my_cboss + 1
1223
END DO
1224
1225
ParMetisMesh => RedistributeMesh(Model, GatheredMesh, .TRUE., .FALSE.)
1226
1227
CALL SaveTerminusPosition(Model, Solver, ParMetisMesh, ImBoss)
1228
1229
CALL ReleaseMesh(ParMetisMesh)
1230
ParMetisMesh => NULL()
1231
DEALLOCATE(FrontPerm,TopPerm)
1232
END IF ! remeshfront
1233
END IF ! saveterminus
1234
1235
!Call zoltan to determine redistribution of mesh
1236
! then do the redistribution
1237
!-------------------------------
1238
1239
RepartMethod = ListGetString(Model % Solver % Values,"Repartition Method", Found)
1240
SELECT CASE( RepartMethod )
1241
CASE( 'parmetis' ) ! bit of a hack here. Parmetis requires fully distributed mesh so ensure all parts have one element
1242
ALLOCATE(PartGotNodes(ParEnv % PEs))
1243
CALL MPI_ALLGATHER(GatheredMesh % NumberOfNodes > 0, 1, MPI_LOGICAL, PartGotNodes, &
1244
1, MPI_LOGICAL, ELMER_COMM_WORLD, ierr)
1245
1246
DistributedMesh = ALL(PartGotNodes)
1247
1248
IF(.NOT. ASSOCIATED(GatheredMesh % Repartition)) THEN
1249
ALLOCATE(GatheredMesh % Repartition(GatheredMesh % NumberOfBulkElements+&
1250
GatheredMesh % NumberOfBoundaryElements), STAT=ierr)
1251
IF(ierr /= 0) PRINT *,ParEnv % MyPE,' could not allocate Mesh % Repartition'
1252
END IF
1253
1254
GatheredMesh % Repartition = ParEnv % MyPE + 1
1255
IF(ImBoss) THEN
1256
counter=0
1257
DO i=1,ParEnv % PEs
1258
IF(PartGotNodes(i)) CYCLE ! got nodes
1259
counter=counter+1
1260
IF(counter > GatheredMesh % NumberOfNodes) &
1261
CALL FATAL(SolverName, 'CalvBoss does not have enough elems to share for ParMetis repartitioning')
1262
GatheredMesh % Repartition(counter) = i
1263
DO j=GatheredMesh % NumberOfBulkElements+1, GatheredMesh % NumberOfBulkElements+&
1264
GatheredMesh % NumberOfBoundaryElements
1265
Element => GatheredMesh % Elements(j)
1266
ParentElem => Element % BoundaryInfo % Left
1267
IF(.NOT. ASSOCIATED(ParentElem)) THEN
1268
ParentElem => Element % BoundaryInfo % Right
1269
END IF
1270
CALL Assert(ASSOCIATED(ParentElem),SolverName,"Boundary element has no parent!")
1271
IF(ParentElem % ElementIndex == counter) THEN
1272
GatheredMesh % Repartition(j) = i
1273
END IF
1274
END DO
1275
END DO
1276
END IF
1277
1278
ParMetisMesh => RedistributeMesh(Model, GatheredMesh, .TRUE., .FALSE.)
1279
1280
IF(ASSOCIATED(ParMetisMesh % Repartition)) THEN
1281
DEALLOCATE(ParMetisMesh % Repartition)
1282
ParMetisMesh % Repartition => NULL()
1283
END IF
1284
1285
CALL ReleaseMesh(GatheredMesh)
1286
GatheredMesh => ParMetisMesh
1287
ParMetisMesh => NULL()
1288
END SELECT
1289
1290
CALL Zoltan_Interface( Model, GatheredMesh, StartImbalanceTol=1.1_dp, TolChange=0.02_dp, MinElems=10 )
1291
1292
FinalMesh => RedistributeMesh(Model, GatheredMesh, .TRUE., .FALSE.)
1293
1294
CALL CheckMeshQuality(FinalMesh)
1295
1296
FinalMesh % Name = TRIM(Mesh % Name)
1297
FinalMesh % OutputActive = .TRUE.
1298
FinalMesh % Changed = .TRUE.
1299
1300
!Actually switch the model's mesh
1301
CALL SwitchMesh(Model, Solver, Mesh, FinalMesh)
1302
1303
!After SwitchMesh because we need GroundedMask
1304
CALL EnforceGroundedMask(Model, Mesh)
1305
1306
CALL CheckBaseFreeSurface(Model, Mesh, 0.01_dp)
1307
1308
IF(FixNodesOnRails) CALL EnforceLateralMargins(Model, Solver % Values)
1309
1310
!Calculate mesh volume
1311
CALL MeshVolume(Model % Mesh, .TRUE., PostCalveVolume)
1312
IF(MyPe == 0) PRINT*, 'Post calve volume: ', PostCalveVolume, ' after timestep', Time
1313
1314
!Recompute mesh bubbles etc
1315
CALL MeshStabParams( Model % Mesh )
1316
1317
!Release the old mesh
1318
CALL ReleaseMesh(GatheredMesh)
1319
1320
! Release GatheredMesh % Redistribution
1321
IF(ASSOCIATED(GatheredMesh % Repartition)) THEN
1322
DEALLOCATE(GatheredMesh % Repartition)
1323
GatheredMesh % Repartition => NULL()
1324
END IF
1325
1326
!remove mesh update
1327
CALL ResetMeshUpdate(Model, Solver)
1328
1329
!pause solvers?
1330
IF(PauseAfterCalving) THEN
1331
IF(ImBoss) THEN
1332
PauseVolumeThresh = ListGetConstReal(SolverParams, "Pause Solvers Minimum Iceberg Volume", Found)
1333
IF(.NOT. Found) THEN
1334
CALL WARN(SolverName, "'Pause Solvers Minimum Iceberg Volume' not provided do assuming is 0")
1335
PauseVolumeThresh = 0.0_dp
1336
END IF
1337
1338
PauseSolvers = MaxBergVolume > PauseVolumeThresh .AND. RSuccess
1339
END IF
1340
1341
CALL MPI_BCAST(PauseSolvers, 1, MPI_LOGICAL, my_cboss, ELMER_COMM_WORLD, ierr)
1342
1343
CALL PauseCalvingSolvers(Model, SolverParams, PauseSolvers)
1344
END IF
1345
1346
END SUBROUTINE CalvingRemeshMMG
1347
1348
SUBROUTINE CheckFlowConvergenceMMG( Model, Solver, dt, Transient )
1349
1350
USE CalvingGeometry
1351
1352
IMPLICIT NONE
1353
1354
TYPE(Model_t) :: Model
1355
TYPE(Solver_t) :: Solver
1356
REAL(KIND=dp) :: dt
1357
LOGICAL :: Transient
1358
!-------------------------------------
1359
TYPE(Mesh_t), POINTER :: Mesh
1360
TYPE(Solver_t) :: RemeshSolver
1361
TYPE(Variable_t), POINTER :: FlowVar, TimeVar
1362
TYPE(ValueList_t), POINTER :: Params, FuncParams
1363
LOGICAL :: Parallel, Found, CheckFlowDiverge=.TRUE., CheckFlowMax, FirstTime=.TRUE.,&
1364
NSDiverge, NSFail, NSTooFast, ChangeMesh, NewMesh, CalvingOccurs, GotSaveMetric,&
1365
CalvingPauseSolvers, PNSFail=.FALSE.
1366
REAL(KIND=dp) :: SaveNewtonTol, MaxNSDiverge, MaxNSValue, FirstMaxNSValue, FlowMax,&
1367
SaveFlowMax, Mag, NSChange, SaveDt, SaveRelax,SaveMeshHmin,SaveMeshHmax,&
1368
SaveMeshHgrad,SaveMeshHausd, SaveMetric, MeshChange, NewMetric, NewMeshDist,&
1369
SaveMeshDist, NewMeshHGrad
1370
#ifdef ELMER_BROKEN_MPI_IN_PLACE
1371
REAL(KIND=dp) :: buffer
1372
#endif
1373
REAL(KIND=dp), ALLOCATABLE :: SaveMeshHausdArray(:,:), SaveMeshHminArray(:,:), &
1374
NewMeshHausdArray(:,:), NewMeshHminArray(:,:)
1375
REAL(KIND=dp), POINTER :: TimestepSizes(:,:), WorkArray(:,:)
1376
INTEGER :: i,j,SaveNewtonIter,Num, ierr, FailCount, TimeIntervals, SaveSSiter,&
1377
MaxRemeshIter,SaveNLIter,CurrentNLIter,NewMeshCount
1378
CHARACTER(MAX_NAME_LEN) :: FlowVarName, SolverName, EqName, RemeshEqName
1379
1380
SAVE ::SaveNewtonTol, SaveNewtonIter, SaveFlowMax, FirstTime, FailCount,&
1381
SaveRelax,SaveMeshHminArray,SaveMeshHmax,SaveMeshHausdArray,SaveMeshHgrad, &
1382
SaveSSiter, MaxRemeshIter, SaveNLIter, NewMesh, NewMeshCount,&
1383
SaveMetric, GotSaveMetric, MeshChange, NewMeshHausdArray, NewMeshHminArray,&
1384
NewMetric, SaveMeshDist, NewMeshDist, NewMeshHGrad, PNSFail
1385
1386
Mesh => Solver % Mesh
1387
SolverName = 'CheckFlowConvergenceMMG'
1388
Params => Solver % Values
1389
Parallel = (ParEnv % PEs > 1)
1390
FuncParams => GetMaterial(Mesh % Elements(1)) !TODO, this is not generalised
1391
FlowVarName = ListGetString(Params,'Flow Solver Name',Found)
1392
IF(.NOT. Found) FlowVarName = "Flow Solution"
1393
FlowVar => VariableGet(Mesh % Variables, FlowVarName, .TRUE., UnfoundFatal=.TRUE.)
1394
1395
RemeshEqName = ListGetString(Params,'Remesh Equation Name',Found)
1396
IF(.NOT. Found) RemeshEqName = "remesh"
1397
1398
!Get a handle to the remesh solver
1399
Found = .FALSE.
1400
DO j=1,Model % NumberOfSolvers
1401
IF(ListGetString(Model % Solvers(j) % Values, "Equation") == RemeshEqName) THEN
1402
RemeshSolver = Model % Solvers(j)
1403
Found = .TRUE.
1404
EXIT
1405
END IF
1406
END DO
1407
IF(.NOT. Found) CALL Fatal(SolverName, "Failed to get handle to Remesh Solver.")
1408
1409
IF(FirstTime) THEN
1410
1411
FailCount = 0
1412
NewMeshCount = 0
1413
1414
SaveNewtonTol = ListGetConstReal(FlowVar % Solver % Values, &
1415
"Nonlinear System Newton After Tolerance", Found)
1416
IF(.NOT. Found) SaveNewtonTol = 1.0E-3
1417
SaveNewtonIter = ListGetInteger(FlowVar % Solver % Values, &
1418
"Nonlinear System Newton After Iterations", Found)
1419
IF(.NOT. Found) SaveNewtonIter = 15
1420
SaveNLIter = ListGetInteger(FlowVar % Solver % Values, &
1421
"Nonlinear System Max Iterations", Found)
1422
IF(.NOT. Found) SaveNewtonIter = 50
1423
1424
SaveRelax = ListGetConstReal(FlowVar % Solver % Values, &
1425
"Nonlinear System Relaxation Factor", Found)
1426
IF(.NOT. Found) SaveRelax = 1.0
1427
PRINT *, 'TO DO: replace with MMG stuff, hausdorff?'
1428
! Use RemeshMMG3D Hmin in Material or Mesh Hmin in RemeshSolver
1429
! should remesh without calving/cutting as well, so take RemeshMMG3D
1430
!SaveMeshHmin = ListGetConstReal(FuncParams, "MMG Hmin", Found, UnfoundFatal=.TRUE.)
1431
SaveMeshHmax = ListGetConstReal(FuncParams, "MMG Hmax", Found, UnfoundFatal=.TRUE.)
1432
!SaveMeshHausd = ListGetConstReal(FuncParams, "MMG Hausd", Found, UnfoundFatal=.TRUE.)
1433
SaveMeshHgrad = ListGetConstReal(FuncParams, "MMG Hgrad", Found, UnfoundFatal=.TRUE.)
1434
SaveMeshDist = ListGetConstReal(RemeshSolver % Values, "Remeshing Distance", Found, UnfoundFatal=.TRUE.)
1435
1436
WorkArray => ListGetConstRealArray(FuncParams, "MMG Hmin", Found)
1437
IF(.NOT. Found) CALL FATAL(SolverName, 'Provide hmin input array to be iterated through: "Mesh Hmin"')
1438
MaxRemeshIter= SIZE(WorkArray(:,1))
1439
1440
PRINT*, 'workarraysize', SIZE(WorkArray(1,:)), SIZE(WorkArray(:,1))
1441
1442
ALLOCATE(SaveMeshHminArray(MaxRemeshIter, 1), NewMeshHminArray(MaxRemeshIter,1))
1443
SaveMeshHminArray = WorkArray
1444
NULLIFY(WorkArray)
1445
WorkArray => ListGetConstRealArray(FuncParams, "MMG Hausd", Found)
1446
IF(.NOT. Found) CALL FATAL(SolverName, 'Provide hmin input array to be iterated through: "Mesh Hausd"')
1447
IF(MaxRemeshIter /= SIZE(WorkArray(:,1))) CALL FATAL(SolverName, 'The number of hmin options &
1448
must equal the number of hausd options')
1449
ALLOCATE(SaveMeshHausdArray(MaxRemeshIter,1), NewMeshHausdArray(MaxRemeshIter,1))
1450
SaveMeshHausdArray = WorkArray
1451
NULLIFY(WorkArray)
1452
1453
SaveMetric = ListGetConstReal( FuncParams, 'GlacierMeshMetric Min LC', GotSaveMetric)
1454
1455
MeshChange = ListGetConstReal( Params, 'Mesh Change Percentage', Found)
1456
IF(.NOT. Found) THEN
1457
MeshChange = 10.0_dp
1458
CALL WARN(SolverName, "Not found Mesh Percentage Change so assuming 10%")
1459
END IF
1460
MeshChange = (MeshChange/100.0_dp)+1
1461
1462
SaveSSiter = ListGetInteger(Model % Simulation, "Steady State Max Iterations", Found)
1463
IF(.NOT. Found) SaveSSiter = 1
1464
1465
TimestepSizes => ListGetConstRealArray( CurrentModel % Simulation, &
1466
'Timestep Sizes', Found, UnfoundFatal=.TRUE.)
1467
IF(SIZE(TimestepSizes,1) > 1) CALL Fatal(SolverName,&
1468
"Calving solver requires a single constant 'Timestep Sizes'")
1469
SaveDt = TimestepSizes(1,1)
1470
1471
NewMeshHminArray = SaveMeshHminArray
1472
NewMeshHausdArray = SaveMeshHausdArray
1473
NewMetric = SaveMetric
1474
NewMeshDist = SaveMeshDist
1475
NewMeshHGrad = SaveMeshHgrad
1476
ELSE
1477
SaveDt = ListGetCReal( CurrentModel % Simulation,'Timestep Size' )
1478
END IF
1479
1480
! since "Calving solver requires a single constant 'Timestep Sizes'"
1481
TimeIntervals = ListGetInteger(Model % Simulation, "Timestep Intervals", UnfoundFatal = .TRUE.)
1482
1483
!Get current simulation time
1484
TimeVar => VariableGet(Model % Variables, 'Time', .TRUE.)
1485
1486
MaxNSDiverge = ListGetConstReal(Params, "Maximum Flow Solution Divergence", CheckFlowDiverge)
1487
MaxNSValue = ListGetConstReal(Params, "Maximum Velocity Magnitude", CheckFlowMax)
1488
FirstMaxNSValue = ListGetConstReal(Params, "First Time Max Expected Velocity", Found)
1489
IF(.NOT. Found .AND. CheckFlowDiverge) THEN
1490
CALL Info(SolverName, "'First Time Max Expected Velocity' not found, setting to 1.0E4")
1491
FirstMaxNSValue = 1.0E4
1492
END IF
1493
1494
!====================================================!
1495
!---------------- DO THINGS! ------------------------!
1496
!====================================================!
1497
1498
NSFail=.FALSE.
1499
NSDiverge=.FALSE.
1500
NSTooFast=.FALSE.
1501
1502
!In addition to checking for absolute failure (% NonlinConverged = 0), we can check
1503
!for suspiciously large shift in the max variable value (this usually indicates a problem)
1504
!and we can also check for unphysically large velocity values
1505
IF(CheckFlowDiverge .OR. CheckFlowMax) THEN
1506
1507
FlowMax = 0.0_dp
1508
DO i=1,Mesh % NumberOfNodes
1509
Mag = 0.0_dp
1510
1511
DO j=1,FlowVar % DOFs-1
1512
Mag = Mag + (FlowVar % Values( (FlowVar % Perm(i)-1)*FlowVar % DOFs + j ) ** 2.0_dp)
1513
END DO
1514
Mag = Mag ** 0.5_dp
1515
FlowMax = MAX(FlowMax, Mag)
1516
END DO
1517
1518
#ifdef ELMER_BROKEN_MPI_IN_PLACE
1519
buffer = FlowMax
1520
CALL MPI_AllReduce(buffer, &
1521
#else
1522
CALL MPI_AllReduce(MPI_IN_PLACE, &
1523
#endif
1524
FlowMax, 1, MPI_DOUBLE_PRECISION, MPI_MAX, ELMER_COMM_WORLD, ierr)
1525
END IF
1526
1527
IF(CheckFlowDiverge) THEN
1528
!First time, there's no previous velocity against which to check divergence.
1529
!This is somewhat messy because of the separate 'Maximum Velocity Magnitude'
1530
IF(FirstTime) SaveFlowMax = MIN(FlowMax,FirstMaxNSValue)
1531
1532
NSChange = FlowMax / SaveFlowMax
1533
IF(ParEnv % MyPE == 0) PRINT *,'Debug, Flow Max (old/new): ',SaveFlowMax, FlowMax,' NSChange: ',NSChange
1534
1535
IF(NSChange > MaxNSDiverge) THEN
1536
NSDiverge = .TRUE.
1537
CALL Info(SolverName,"Large change in maximum velocity suggests dodgy&
1538
&Navier-Stokes solution.")
1539
END IF
1540
IF(.NOT. NSDiverge) SaveFlowMax = FlowMax
1541
END IF
1542
1543
IF(CheckFlowMax) THEN
1544
IF(FlowMax > MaxNSValue) THEN
1545
NSTooFast = .TRUE.
1546
CALL Info(SolverName,"Large maximum velocity suggests dodgy&
1547
&Navier-Stokes solution.")
1548
END IF
1549
END IF
1550
1551
NSFail = FlowVar % NonlinConverged < 1 .OR. NSDiverge .OR. NSTooFast
1552
! Joe note: I commented out Eef's testing here during merge:
1553
! PRINT *, 'temporarily set NSFail=True for testing'
1554
! NSFail=.TRUE.
1555
IF(ParEnv % MyPE == 0) PRINT*, 'Debug', FlowVar % NonlinConverged, NSDiverge, NSTooFast
1556
IF(NSFail) THEN
1557
CALL Info(SolverName, "Skipping solvers except Remesh because NS failed to converge.")
1558
1559
FailCount = FailCount + 1
1560
IF(ParEnv % MyPE == 0) PRINT *, 'FailCount=',FailCount
1561
! Joe note: I commented out Eef's testing here during merge:
1562
! PRINT *, 'Temporarily set failcount to 2, to force remeshing!'
1563
! FailCount=2
1564
IF(NewMeshCount > 3) THEN
1565
CALL Fatal(SolverName, "Don't seem to be able to recover from NS failure, giving up...")
1566
END IF
1567
1568
!Set the clock back one second less than a timestep size.
1569
!This means next time we are effectively redoing the same timestep
1570
!but without any solvers which are dependent on (t > told) to reallocate
1571
TimeVar % Values(1) = TimeVar % Values(1) - SaveDt + (1.0/(365.25 * 24 * 60 * 60.0_dp))
1572
1573
CALL ListAddConstReal(FlowVar % Solver % Values, &
1574
"Nonlinear System Newton After Tolerance", 0.0_dp)
1575
CALL ListAddInteger( FlowVar % Solver % Values, &
1576
"Nonlinear System Newton After Iterations", 10000)
1577
1578
IF(failcount > 1) ChangeMesh = .TRUE.
1579
IF(.NOT. NSDiverge .AND. .NOT. NSTooFast) THEN
1580
!ie fails from nonconvergence but flow looks ok
1581
CurrentNLIter = ListGetInteger(FlowVar % Solver % Values, &
1582
"Nonlinear System Max Iterations", Found)
1583
IF(CurrentNLIter >= SaveNLIter*4) THEN !clearly not going to converge
1584
CALL ListAddInteger( FlowVar % Solver % Values, &
1585
"Nonlinear System Max Iterations", SaveNLIter)
1586
!ChangeMesh = .FALSE.
1587
ELSE
1588
CALL ListAddInteger( FlowVar % Solver % Values, &
1589
"Nonlinear System Max Iterations", CurrentNLIter*2)
1590
!ChangeMesh = .FALSE.
1591
END IF
1592
END IF
1593
1594
!If this is the second failure in a row, fiddle with the mesh
1595
!PRINT *, 'TO DO, optimize MMG parameters, need to change remesh distance as well?'
1596
IF(ChangeMesh) THEN
1597
NewMeshHminArray = NewMeshHminArray/MeshChange
1598
NewMeshHausdArray = NewMeshHausdArray/MeshChange
1599
NewMetric = NewMetric/MeshChange
1600
NewMeshDist = NewMeshDist*MeshChange
1601
NewMeshHGrad = NewMeshHGrad/MeshChange
1602
1603
CALL Info(SolverName,"NS failed twice, fiddling with the mesh... ")
1604
CALL Info(SolverName,"Temporarily slightly change MMG params ")
1605
CALL ListAddConstRealArray(FuncParams, "MMG Hmin", MaxRemeshIter, 1, NewMeshHminArray)
1606
!CALL ListAddConstReal(FuncParams, "MMG Hmax", SaveMeshHmax*1.1_dp)
1607
CALL ListAddConstReal(FuncParams, "MMG Hgrad", NewMeshHGrad) !default 1.3
1608
CALL ListAddConstReal(RemeshSolver % Values, "Remeshing Distance", NewMeshDist)
1609
CALL ListAddConstRealArray(FuncParams, "MMG Hausd", MaxRemeshiter, 1, NewMeshHausdArray)
1610
CALL ListAddInteger( FlowVar % Solver % Values, "Nonlinear System Max Iterations", SaveNLIter)
1611
IF(GotSaveMetric) CALL ListAddConstReal(FuncParams, "GlacierMeshMetric Min LC", NewMetric)
1612
NewMesh = .TRUE.
1613
NewMeshCount = NewMeshCount + 1
1614
ELSE
1615
NewMesh = .FALSE.
1616
END IF
1617
1618
IF( .NOT. (NSTooFast .OR. NSDiverge)) THEN
1619
!---Not quite converging---!
1620
1621
CALL ListAddConstReal(FlowVar % Solver % Values, &
1622
"Nonlinear System Relaxation Factor", 0.9_dp)
1623
1624
ELSE
1625
!---Solution blowing up----!
1626
1627
!Set var values to zero so it doesn't mess up viscosity next time
1628
FlowVar % Values = 0.0_dp
1629
1630
!TODO: What else? different linear method? more relaxation?
1631
END IF
1632
1633
!prevent calving on next time step
1634
CalvingOccurs = .FALSE.
1635
CALL SParIterAllReduceOR(CalvingOccurs)
1636
CALL ListAddLogical( Model % Simulation, 'CalvingOccurs', CalvingOccurs )
1637
ELSE
1638
! set original values back
1639
FailCount = 0
1640
NewMeshCount = 0
1641
1642
NewMeshHminArray = SaveMeshHminArray
1643
NewMeshHausdArray = SaveMeshHausdArray
1644
NewMetric = SaveMetric
1645
NewMeshDist = SaveMeshDist
1646
NewMeshHGrad = SaveMeshHgrad
1647
1648
CALL ListAddConstReal(FlowVar % Solver % Values, &
1649
"Nonlinear System Newton After Tolerance", SaveNewtonTol)
1650
CALL ListAddInteger( FlowVar % Solver % Values, &
1651
"Nonlinear System Newton After Iterations", SaveNewtonIter)
1652
CALL ListAddConstReal(FlowVar % Solver % Values, &
1653
"Nonlinear System Relaxation Factor", SaveRelax)
1654
CALL ListAddInteger( FlowVar % Solver % Values, "Nonlinear System Max Iterations", SaveNLIter)
1655
CALL ListAddConstRealArray(FuncParams, "MMG Hmin", MaxRemeshIter, 1, SaveMeshHminArray)
1656
!CALL ListAddConstReal(FuncParams, "MMG Hmax", SaveMeshHmax)
1657
CALL ListAddConstReal(RemeshSolver % Values, "Remeshing Distance", SaveMeshDist)
1658
CALL ListAddConstReal(FuncParams, "MMG Hgrad", SaveMeshHgrad)
1659
CALL ListAddConstRealArray(FuncParams, "MMG Hausd", MaxRemeshIter, 1, SaveMeshHausdArray)
1660
IF(GotSaveMetric) CALL ListAddConstReal(FuncParams, "GlacierMeshMetric Min LC", SaveMetric)
1661
1662
END IF
1663
1664
!Set a simulation switch to be picked up by Remesher
1665
CALL ListAddLogical( Model % Simulation, 'Flow Solution Failed', NSFail )
1666
1667
CalvingPauseSolvers = ListGetLogical( Model % Simulation, 'Calving Pause Solvers', Found )
1668
IF(.NOT. Found) THEN
1669
CALL INFO(SolverName, 'Cannot find Calving Pause Solvers so assuming calving has not pasued time.')
1670
CalvingPauseSolvers = .TRUE.
1671
END IF
1672
IF(.NOT. CalvingPauseSolvers .OR. FirstTime .OR. NSFail .OR. PNSFail) THEN
1673
!Switch off solvers
1674
DO Num = 1,999
1675
WRITE(Message,'(A,I0)') 'Switch Off Equation ',Num
1676
EqName = ListGetString( Params, Message, Found)
1677
IF( .NOT. Found) EXIT
1678
1679
Found = .FALSE.
1680
DO j=1,Model % NumberOfSolvers
1681
IF(ListGetString(Model % Solvers(j) % Values, "Equation") == EqName) THEN
1682
Found = .TRUE.
1683
!Turn off (or on) the solver
1684
!If NS failed to converge, (switch) off = .true.
1685
CALL SwitchSolverExec(Model % Solvers(j), NSFail)
1686
EXIT
1687
END IF
1688
END DO
1689
1690
IF(.NOT. Found) THEN
1691
WRITE (Message,'(A,A,A)') "Failed to find Equation Name: ",EqName,&
1692
" to switch off after calving."
1693
CALL Fatal(SolverName,Message)
1694
END IF
1695
END DO
1696
END IF
1697
1698
IF(NSFail) THEN
1699
!CALL ListAddConstReal( Model % Simulation, 'Timestep Size', PseudoSSdt)
1700
!CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', 1)
1701
CALL ListAddInteger( Model % Simulation, 'Timestep Intervals', TimeIntervals + 1)
1702
ELSE
1703
!CALL ListAddConstReal( Model % Simulation, 'Timestep Size', SaveDt)
1704
CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', SaveSSiter)
1705
END IF
1706
1707
FirstTime = .FALSE.
1708
PNSFail = NSFail
1709
1710
END SUBROUTINE CheckFlowConvergenceMMG
1711
1712