Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/elmerice/Solvers/CalvingRemesh.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
!Routines for dealing with the Remeshing of the 3D calving model
33
34
SUBROUTINE CheckFlowConvergence( Model, Solver, dt, Transient )
35
36
USE CalvingGeometry
37
38
IMPLICIT NONE
39
40
TYPE(Model_t) :: Model
41
TYPE(Solver_t) :: Solver
42
REAL(KIND=dp) :: dt
43
LOGICAL :: Transient
44
!-------------------------------------
45
TYPE(Mesh_t), POINTER :: Mesh
46
TYPE(Solver_t) :: RemeshSolver
47
TYPE(Variable_t), POINTER :: FlowVar, TimeVar
48
TYPE(ValueList_t), POINTER :: Params
49
LOGICAL :: Parallel, Found, CheckFlowDiverge=.TRUE., CheckFlowMax, FirstTime=.TRUE.,&
50
NSDiverge, NSFail, NSTooFast
51
REAL(KIND=dp) :: SaveNewtonTol, MaxNSDiverge, MaxNSValue, FirstMaxNSValue, FlowMax,&
52
SaveFlowMax, Mag, NSChange, SaveDt, SaveRelax,SaveMeshMinLC,SaveMeshRmLC,SaveMeshRmThresh
53
#ifdef ELMER_BROKEN_MPI_IN_PLACE
54
REAL(KIND=dp) :: buffer
55
#endif
56
REAL(KIND=dp), POINTER :: TimestepSizes(:,:)
57
INTEGER :: i,j,SaveNewtonIter,Num, ierr, FailCount, ier
58
CHARACTER(MAX_NAME_LEN) :: FlowVarName, SolverName, EqName, RemeshEqName
59
60
SAVE ::SaveNewtonTol, SaveNewtonIter, SaveFlowMax, SaveDt, FirstTime, FailCount,&
61
SaveRelax,SaveMeshMinLC,SaveMeshRmLC,SaveMeshRmThresh
62
63
Mesh => Solver % Mesh
64
SolverName = 'CheckFlowConvergence'
65
Params => Solver % Values
66
Parallel = (ParEnv % PEs > 1)
67
68
FlowVarName = ListGetString(Params,'Flow Solver Name',Found)
69
IF(.NOT. Found) FlowVarName = "Flow Solution"
70
FlowVar => VariableGet(Mesh % Variables, FlowVarName, .TRUE., UnfoundFatal=.TRUE.)
71
72
RemeshEqName = ListGetString(Params,'Remesh Equation Name',Found)
73
IF(.NOT. Found) RemeshEqName = "remesh"
74
75
!Get a handle to the remesh solver
76
Found = .FALSE.
77
DO j=1,Model % NumberOfSolvers
78
IF(ListGetString(Model % Solvers(j) % Values, "Equation") == RemeshEqName) THEN
79
RemeshSolver = Model % Solvers(j)
80
Found = .TRUE.
81
EXIT
82
END IF
83
END DO
84
IF(.NOT. Found) CALL Fatal(SolverName, "Failed to get handle to Remesh Solver.")
85
86
IF(FirstTime) THEN
87
88
FailCount = 0
89
TimestepSizes => ListGetConstRealArray( Model % Simulation, &
90
'Timestep Sizes', Found, UnfoundFatal=.TRUE.)
91
IF(SIZE(TimestepSizes,1) > 1) CALL Fatal(SolverName,&
92
"Calving solver requires a single constant 'Timestep Sizes'")
93
SaveDt = TimestepSizes(1,1)
94
95
SaveNewtonTol = ListGetConstReal(FlowVar % Solver % Values, &
96
"Nonlinear System Newton After Tolerance", Found)
97
IF(.NOT. Found) SaveNewtonTol = 1.0E-3
98
SaveNewtonIter = ListGetInteger(FlowVar % Solver % Values, &
99
"Nonlinear System Newton After Iterations", Found)
100
IF(.NOT. Found) SaveNewtonIter = 15
101
102
SaveRelax = ListGetConstReal(FlowVar % Solver % Values, &
103
"Nonlinear System Relaxation Factor", Found)
104
IF(.NOT. Found) SaveRelax = 1.0
105
106
SaveMeshMinLC = ListGetConstReal(RemeshSolver % Values, &
107
"Remesh Min Characteristic Length", Found, UnfoundFatal=.TRUE.)
108
109
SaveMeshRmLC = ListGetConstReal(RemeshSolver % Values, &
110
"Remesh Remove Nodes Closer Than", Found, UnfoundFatal=.TRUE.)
111
112
SaveMeshRmThresh = ListGetConstReal(RemeshSolver % Values, &
113
"Remesh Remove Nodes Deviation Threshold", Found, UnfoundFatal=.TRUE.)
114
END IF
115
116
!Get current simulation time
117
TimeVar => VariableGet(Model % Variables, 'Time', .TRUE.)
118
119
MaxNSDiverge = ListGetConstReal(Params, "Maximum Flow Solution Divergence", CheckFlowDiverge)
120
MaxNSValue = ListGetConstReal(Params, "Maximum Velocity Magnitude", CheckFlowMax)
121
FirstMaxNSValue = ListGetConstReal(Params, "First Time Max Expected Velocity", Found)
122
IF(.NOT. Found .AND. CheckFlowDiverge) THEN
123
CALL Info(SolverName, "'First Time Max Expected Velocity' not found, setting to 1.0E4")
124
FirstMaxNSValue = 1.0E4
125
END IF
126
127
!====================================================!
128
!---------------- DO THINGS! ------------------------!
129
!====================================================!
130
131
NSFail=.FALSE.
132
NSDiverge=.FALSE.
133
NSTooFast=.FALSE.
134
135
!In addition to checking for absolute failure (% NonlinConverged = 0), we can check
136
!for suspiciously large shift in the max variable value (this usually indicates a problem)
137
!and we can also check for unphysically large velocity values
138
IF(CheckFlowDiverge .OR. CheckFlowMax) THEN
139
140
FlowMax = 0.0_dp
141
DO i=1,Mesh % NumberOfNodes
142
Mag = 0.0_dp
143
144
DO j=1,FlowVar % DOFs-1
145
Mag = Mag + (FlowVar % Values( (FlowVar % Perm(i)-1)*FlowVar % DOFs + j ) ** 2.0_dp)
146
END DO
147
Mag = Mag ** 0.5_dp
148
FlowMax = MAX(FlowMax, Mag)
149
END DO
150
151
#ifdef ELMER_BROKEN_MPI_IN_PLACE
152
buffer = FlowMax
153
CALL MPI_AllReduce(buffer, &
154
#else
155
CALL MPI_AllReduce(MPI_IN_PLACE, &
156
#endif
157
FlowMax, 1, MPI_DOUBLE_PRECISION, MPI_MAX, ELMER_COMM_WORLD, ierr)
158
END IF
159
160
IF(CheckFlowDiverge) THEN
161
!First time, there's no previous velocity against which to check divergence.
162
!This is somewhat messy because of the separate 'Maximum Velocity Magnitude'
163
IF(FirstTime) SaveFlowMax = MIN(FlowMax,FirstMaxNSValue)
164
165
NSChange = FlowMax / SaveFlowMax
166
PRINT *,'Debug, Flow Max (old/new): ',SaveFlowMax, FlowMax,' NSChange: ',NSChange
167
168
IF(NSChange > MaxNSDiverge) THEN
169
NSDiverge = .TRUE.
170
CALL Info(SolverName,"Large change in maximum velocity suggests dodgy&
171
&Navier-Stokes solution.")
172
END IF
173
IF(.NOT. NSDiverge) SaveFlowMax = FlowMax
174
END IF
175
176
IF(CheckFlowMax) THEN
177
IF(FlowMax > MaxNSValue) THEN
178
NSTooFast = .TRUE.
179
CALL Info(SolverName,"Large maximum velocity suggests dodgy&
180
&Navier-Stokes solution.")
181
END IF
182
END IF
183
184
NSFail = FlowVar % NonlinConverged < 1 .OR. NSDiverge .OR. NSTooFast
185
186
IF(NSFail) THEN
187
CALL Info(SolverName, "Skipping solvers except Remesh because NS failed to converge.")
188
189
FailCount = FailCount + 1
190
IF(FailCount >= 4) THEN
191
CALL Fatal(SolverName, "Don't seem to be able to recover from NS failure, giving up...")
192
END IF
193
194
!Set the clock back one second less than a timestep size.
195
!This means next time we are effectively redoing the same timestep
196
!but without any solvers which are dependent on (t > told) to reallocate
197
TimeVar % Values(1) = TimeVar % Values(1) - SaveDt + (1.0/(365.25 * 24 * 60 * 60.0_dp))
198
199
200
CALL ListAddConstReal(FlowVar % Solver % Values, &
201
"Nonlinear System Newton After Tolerance", 0.0_dp)
202
CALL ListAddInteger( FlowVar % Solver % Values, &
203
"Nonlinear System Newton After Iterations", 10000)
204
205
!If this is the second failure in a row, fiddle with the mesh
206
IF(FailCount >= 2) THEN
207
CALL Info(SolverName,"NS failed twice, fiddling with the mesh...")
208
CALL ListAddConstReal(RemeshSolver % Values, &
209
"Remesh Min Characteristic Length", 150.0_dp)
210
CALL ListAddConstReal(RemeshSolver % Values, &
211
"Remesh Remove Nodes Closer Than", 120.0_dp)
212
CALL ListAddConstReal(RemeshSolver % Values, &
213
"Remesh Remove Nodes Deviation Threshold", 50.0_dp)
214
END IF
215
216
IF( .NOT. (NSTooFast .OR. NSDiverge)) THEN
217
!---Not quite converging---!
218
219
CALL ListAddConstReal(FlowVar % Solver % Values, &
220
"Nonlinear System Relaxation Factor", 0.9_dp)
221
222
ELSE
223
!---Solution blowing up----!
224
225
!Set var values to zero so it doesn't mess up viscosity next time
226
FlowVar % Values = 0.0_dp
227
228
!TODO: What else? different linear method? more relaxation?
229
END IF
230
ELSE
231
232
FailCount = 0
233
CALL ListAddConstReal(FlowVar % Solver % Values, &
234
"Nonlinear System Newton After Tolerance", SaveNewtonTol)
235
CALL ListAddInteger( FlowVar % Solver % Values, &
236
"Nonlinear System Newton After Iterations", SaveNewtonIter)
237
CALL ListAddConstReal(FlowVar % Solver % Values, &
238
"Nonlinear System Relaxation Factor", SaveRelax)
239
240
CALL ListAddConstReal(RemeshSolver % Values, &
241
"Remesh Min Characteristic Length", SaveMeshMinLC)
242
CALL ListAddConstReal(RemeshSolver % Values, &
243
"Remesh Remove Nodes Closer Than", SaveMeshRmLC)
244
CALL ListAddConstReal(RemeshSolver % Values, &
245
"Remesh Remove Nodes Deviation Threshold", SaveMeshRmThresh)
246
247
END IF
248
249
!Set a simulation switch to be picked up by Remesher
250
CALL ListAddLogical( Model % Simulation, 'Flow Solution Failed', NSFail )
251
252
!Switch off solvers
253
DO Num = 1,999
254
WRITE(Message,'(A,I0)') 'Switch Off Equation ',Num
255
EqName = ListGetString( Params, Message, Found)
256
IF( .NOT. Found) EXIT
257
258
Found = .FALSE.
259
DO j=1,Model % NumberOfSolvers
260
IF(ListGetString(Model % Solvers(j) % Values, "Equation") == EqName) THEN
261
Found = .TRUE.
262
!Turn off (or on) the solver
263
!If NS failed to converge, (switch) off = .true.
264
CALL SwitchSolverExec(Model % Solvers(j), NSFail)
265
EXIT
266
END IF
267
END DO
268
269
IF(.NOT. Found) THEN
270
WRITE (Message,'(A,A,A)') "Failed to find Equation Name: ",EqName,&
271
" to switch off after calving."
272
CALL Fatal(SolverName,Message)
273
END IF
274
END DO
275
276
FirstTime = .FALSE.
277
278
END SUBROUTINE CheckFlowConvergence
279
280
SUBROUTINE Remesher( Model, Solver, dt, Transient )
281
282
USE DefUtils
283
USE GeneralUtils
284
USE ElementDescription
285
USE MeshUtils
286
USE SParIterComm
287
USE CalvingGeometry
288
289
IMPLICIT NONE
290
291
TYPE(Model_t) :: Model
292
TYPE(Solver_t) :: Solver
293
TYPE(Mesh_t), POINTER :: Mesh
294
LOGICAL :: Transient
295
!-------------------------------------
296
TYPE(Mesh_t), POINTER :: NewMesh
297
TYPE(Variable_t), POINTER :: Var, RefVar, TimeVar, CalvingVar, TangledVar
298
TYPE(ValueList_t), POINTER :: Params
299
REAL(KIND=dp) ::FrontOrientation(3), RotationMatrix(3,3), UnRotationMatrix(3,3), NodeHolder(3)
300
REAL(KIND=dp), POINTER :: TimestepSizes(:,:)
301
REAL(KIND=dp) :: time, dt, PseudoSSdt, SaveDt, LastRemeshTime, TimeSinceRemesh, ForceRemeshTime,&
302
ZThresh, global_eps, local_eps, CalvingTime
303
LOGICAL :: Debug, Parallel, CalvingOccurs, RemeshOccurs, PauseSolvers, Found, &
304
RotFS, FirstTime = .TRUE.,CalvingLastTime, PauseAfterCalving, FrontalBecomeBasal, &
305
TangleOccurs, CheckTangled, NSFail, CheckFlowConvergence, IgnoreCalving
306
LOGICAL, POINTER :: NewBasalNode(:)=>NULL()
307
CHARACTER(MAX_NAME_LEN) :: SolverName, VarName, EqName, CalvingVarName,&
308
FrontMaskName,InMaskName,TopMaskName,BotMaskName,LeftMaskName,RightMaskName, &
309
TangledVarName
310
INTEGER :: Num, i,j, SaveSSiter, PauseTimeCount=0, PauseTimeMax
311
312
SAVE :: FirstTime, SaveDt, SaveSSiter, PseudoSSdt, LastRemeshTime, ForceRemeshTime, &
313
CalvingLastTime,FrontMaskName,InMaskName,TopMaskName,BotMaskName,LeftMaskName,&
314
RightMaskName, ZThresh, CalvingVarName, PauseAfterCalving, global_eps, local_eps,&
315
PauseTimeCount, IgnoreCalving
316
317
Debug = .FALSE.
318
319
Mesh => Solver % Mesh
320
SolverName = 'Remesher'
321
Params => Solver % Values
322
Parallel = (ParEnv % PEs > 1)
323
324
!-----------------------------------------------------------
325
! Some notes on linking this solver to Calving3D
326
!
327
! Input: -CalvingOccurs logical
328
! -Pseudo mesh update for each
329
! calving front point.
330
!
331
!-----------------------------------------------------------
332
333
!Trying out new functionality:
334
! CALL Assert(1==0, SolverName, "1 does not equal zero!")
335
! CALL NumericalError( SolverName, "Couldn't do pretend convergence")
336
337
IF(FirstTime) THEN
338
339
TopMaskName = "Top Surface Mask"
340
BotMaskName = "Bottom Surface Mask"
341
LeftMaskName = "Left Sidewall Mask"
342
RightMaskName = "Right Sidewall Mask"
343
FrontMaskName = "Calving Front Mask"
344
InMaskName = "Inflow Mask"
345
346
LastRemeshTime = GetTime()
347
348
!Need this for temporarily stopping simulation clock when calving occurs,
349
! to recheck for multiple calving events triggered in the same timestep
350
TimestepSizes => ListGetConstRealArray( CurrentModel % Simulation, &
351
'Timestep Sizes', Found, UnfoundFatal=.TRUE.)
352
IF(SIZE(TimestepSizes,1) > 1) CALL Fatal(SolverName,&
353
"Calving solver requires a single constant 'Timestep Sizes'")
354
355
SaveDt = TimestepSizes(1,1)
356
357
SaveSSiter = ListGetInteger(Model % Simulation, "Steady State Max Iterations", Found)
358
IF(.NOT. Found) SaveSSiter = 1
359
360
PseudoSSdt = ListGetConstReal( Params, 'Pseudo SS dt', Found)
361
IF(.NOT. Found) THEN
362
CALL Warn(SolverName,"No value specified for 'Pseudo SS dt', taking 1.0e-10")
363
PseudoSSdt = 1.0e-10
364
END IF
365
366
ForceRemeshTime = ListGetConstReal(Params, 'Force Remesh After Time', Found)
367
IF(.NOT. Found) THEN
368
CALL Warn(SolverName, 'No "Force Remesh After Time" found, defaulting to 1 month...')
369
ForceRemeshTime = 1.0/12.0
370
END IF
371
372
!Get the calving variable
373
CalvingVarName = ListGetString(Params,"Calving Variable Name", Found)
374
IF(.NOT. Found) THEN
375
CALL Info(SolverName, "Can't find Calving Variable Name in solver section, &
376
& assuming 'Calving'")
377
CalvingVarName = "Calving"
378
END IF
379
380
!The threshold for converting overhanging frontal elements to basal elements.
381
ZThresh = ListGetConstReal(Params, "Front Normal Z Threshold", Found)
382
IF(.NOT. Found) THEN
383
CALL Warn(SolverName, "Couldn't find Front Normal Z Threshold, setting to -0.9.")
384
ZThresh = -0.9
385
ELSE
386
IF(ZThresh > 0) CALL Fatal(SolverName, "Front Normal Z Threshold controls the &
387
&conversion of overhanging frontal elements to basal elements. Sensible values &
388
&lie in the range -0.5 to -0.95")
389
END IF
390
391
PauseAfterCalving = ListGetLogical(Params, "Pause After Calving Event", Found)
392
IF(.NOT. Found) THEN
393
CALL Info(SolverName, "Can't find 'Pause After Calving Event' logical in Solver section, &
394
& assuming True")
395
PauseAfterCalving = .TRUE.
396
END IF
397
398
IgnoreCalving = ListGetLogical(Params, "Ignore Calving", Found)
399
IF(.NOT. Found) THEN
400
CALL Info(SolverName, "Can't find 'Ignore Calving' logical in Solver section, &
401
& assuming False")
402
IgnoreCalving = .FALSE.
403
END IF
404
405
global_eps = 1.0E-2_dp
406
local_eps = 1.0E-2_dp
407
END IF !FirstTime
408
409
!Get the orientation of the front and compute rotation matrices
410
FrontOrientation = GetFrontOrientation(Model)
411
RotationMatrix = ComputeRotationMatrix(FrontOrientation)
412
UnRotationMatrix = TRANSPOSE(RotationMatrix)
413
414
CALL Info( SolverName, ' ---- Front Rotation Matrix ---- ')
415
DO i=1,3
416
WRITE(Message, '(f10.7,2x,f10.7,2x,f10.7)') &
417
RotationMatrix(i,1),&
418
RotationMatrix(i,2),&
419
RotationMatrix(i,3)
420
CALL Info(SolverName, Message)
421
END DO
422
423
!Get current simulation time
424
TimeVar => VariableGet(Model % Variables, 'Time', .TRUE.)
425
time = TimeVar % Values(1)
426
!CHANGE
427
CalvingTime = ListGetConstReal(Model % Simulation, 'CalvingTime', Found)
428
429
!Is there a calving event?
430
IF(.NOT. IgnoreCalving) THEN
431
CalvingOccurs = ListGetLogical(Model % Simulation, 'CalvingOccurs', Found)
432
IF(.NOT.Found) CALL Warn(SolverName, "Unable to find CalvingOccurs logical, &
433
& assuming no calving event.")
434
!CHANGE
435
IF(time == CalvingTime) THEN
436
CalvingOccurs = CalvingOccurs
437
ELSE
438
CalvingOccurs = .FALSE.
439
END IF
440
ELSE
441
CalvingOccurs = .FAlSE.
442
END IF
443
444
!Note - two switches: PauseAfterCalving is the rule in the sif (i.e. do or don't)
445
!PauseSolvers is marked by Calving3D if calving event exceeding certain size occurred
446
PauseSolvers = ListGetLogical(Model % Simulation, 'Calving Pause Solvers', Found)
447
IF(.NOT.Found) THEN
448
CALL Warn(SolverName, "Unable to find 'Calving Pause Solvers' logical, &
449
& assuming true.")
450
PauseSolvers = CalvingOccurs
451
END IF
452
453
PauseTimeMax = ListGetInteger(Params, "Calving Pause Max Steps", Found)
454
IF(.NOT. Found) THEN
455
PauseTimeMax = 15
456
END IF
457
458
IF(PauseSolvers) THEN
459
PauseTimeCount = PauseTimeCount + 1
460
IF(PauseTimeCount > PauseTimeMax) THEN
461
PauseSolvers = .FALSE.
462
PauseTimeCount = 0
463
CALL Info(SolverName,"Calving paused steps exceeded given threshold, moving on...")
464
END IF
465
ELSE
466
PauseTimeCount = 0
467
END IF
468
469
CalvingVar => VariableGet(Mesh % Variables, CalvingVarName, .TRUE.)
470
IF(.NOT. ASSOCIATED(CalvingVar)) &
471
CALL Fatal(SolverName, "Couldn't get Calving variable.")
472
473
!-----------------------------------------
474
! Action!
475
!-----------------------------------------
476
477
!Initialize
478
RemeshOccurs = .FALSE.
479
480
!If FlowSolver failed to converge (usually a result of weird mesh), large unphysical
481
!calving events can be predicted. So, turn off CalvingOccurs, and ensure a remesh
482
!Also undo this iterations mesh update
483
NSFail = ListGetLogical(Model % Simulation, "Flow Solution Failed",CheckFlowConvergence)
484
IF(CheckFlowConvergence) THEN
485
IF(NSFail) THEN
486
CalvingOccurs = .FALSE.
487
RemeshOccurs = .TRUE.
488
CALL Info(SolverName, "Remeshing but not calving because NS failed to converge.")
489
ELSE
490
491
END IF
492
END IF
493
494
!The lagrangian front advance method can sometimes (though rarely) result
495
!in columns getting tangled up. FrontAdvance fixes this tangling by restoring
496
!the tangled region to a very thin pinnacle (or rift) and then marks them via TangledVar > 0.5
497
!When Remeshing, these columns should be removed from the new mesh, and we also ensure that new
498
!nodes in FootprintMesh are not in the region where they may be interpolated from the tangled columns
499
TangledVarName = ListGetString(Params, "Tangled Variable Name", CheckTangled)
500
IF(.NOT. CheckTangled) THEN
501
CALL Info(SolverName, "No 'Tangled Variable Name' found, not checking for tangled nodes")
502
TangleOccurs = .FALSE.
503
ELSE
504
CALL Info(SolverName, "Checking for tangled nodes")
505
506
TangledVar => VariableGet(Mesh % Variables, TangledVarName, .TRUE., UnfoundFatal=.TRUE.)
507
508
TangleOccurs = ANY(TangledVar % Values > 0.5)
509
IF(Parallel) CALL SParIterAllReduceOR(TangleOccurs)
510
IF(TangleOccurs) CALL Info(SolverName, "Some front columns are tangled, remeshing...")
511
END IF
512
513
!-------------------------------------------------------------------------
514
515
IF(CalvingOccurs) THEN
516
!--Move front nodes--
517
CALL DisplaceCalvingFront(Mesh, CalvingVar, 1)
518
ELSE
519
!-- or ensure set to zero
520
CalvingVar % Values = 0.0_dp
521
END IF
522
!----------------------------------------------------
523
! Check front elements normal vectors
524
! convert downward pointing into basal elements, and nodes
525
!----------------------------------------------------
526
CALL ConvertFrontalToBasal(Model, Mesh, FrontMaskName, BotMaskName, ZThresh, &
527
NewBasalNode, FrontalBecomeBasal)
528
529
IF(CalvingOccurs) LastRemeshTime = time
530
TimeSinceRemesh = time - LastRemeshTime
531
532
!Force remesh every so often to maintain mesh quality
533
IF( (TimeSinceRemesh > ForceRemeshTime) .OR. FrontalBecomeBasal) THEN
534
RemeshOccurs = .TRUE.
535
LastRemeshTime = time
536
END IF
537
538
IF(CalvingOccurs .OR. RemeshOccurs .OR. TangleOccurs) THEN
539
!TODO: there are evidently some large buffered sends in this subroutine
540
!Find them and calculate this properly...
541
CALL CheckBuffer(104857600)
542
543
CALL Info( SolverName, ' ',Level=4 )
544
CALL Info( SolverName, '-------------------------------------',Level=4 )
545
IF(CalvingOccurs) THEN
546
WRITE(Message,'(A,f8.4)') " Calving Event at time: ",time
547
ELSE IF(TangleOccurs) THEN
548
WRITE(Message,'(A,f8.4)') " Tangled columns, forcing glacier remesh at time: ",time
549
ELSE
550
WRITE(Message,'(A,f8.4)') " Forcing glacier remesh at time: ",time
551
END IF
552
CALL Info( SolverName, Message,Level=4 )
553
CALL Info( SolverName, ' ',Level=4 )
554
CALL Info( SolverName, ' Remeshing Glacier',Level=4 )
555
CALL Info( SolverName, '-------------------------------------',Level=4 )
556
CALL Info( SolverName, ' ',Level=4 )
557
558
CALL CalvingRemesh(Model, Solver, Mesh, NewMesh, Parallel, Transient)
559
560
NewMesh % Name = TRIM(Mesh % Name)
561
NewMesh % OutputActive = .TRUE.
562
NewMesh % Changed = .TRUE.
563
!CHANGE
564
NewMesh % MeshTag = Mesh % MeshTag + 1
565
566
CALL SwitchMesh(Model, Solver, Mesh, NewMesh)
567
CALL MeshStabParams( Model % Mesh )
568
569
CALL Info( SolverName, ' ',Level=4 )
570
CALL Info( SolverName, '-------------------------------------',Level=4 )
571
CALL Info( SolverName, ' Remeshing Complete',Level=4 )
572
CALL Info( SolverName, '-------------------------------------',Level=4 )
573
CALL Info( SolverName, ' ',Level=4 )
574
575
ELSE
576
577
CALL Info( SolverName, ' ',Level=4 )
578
CALL Info( SolverName, '-------------------------------------',Level=4 )
579
CALL Info( SolverName, ' No calving or remesh, doing nothing...',Level=4 )
580
CALL Info( SolverName, '-------------------------------------',Level=4 )
581
CALL Info( SolverName, ' ',Level=4 )
582
583
!Mesh % Changed forces solvers to reallocate their internal arrays
584
!Needs to be .TRUE. for first timestep after a calving event, as
585
!Mesh Update and FreeSurface solvers haven't been called in the mean time
586
IF(.NOT. CalvingLastTime) Mesh % Changed = .FALSE.
587
END IF
588
589
!Reset listed mesh update variable values to zero
590
!Regardless of whether calving occurs.
591
DO Num = 1,999
592
WRITE(Message,'(A,I0)') 'Mesh Update Variable ',Num
593
VarName = ListGetString( Params, Message, Found)
594
IF( .NOT. Found) EXIT
595
596
Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )
597
IF(.NOT. ASSOCIATED(Var)) THEN
598
WRITE(Message,'(A,A)') "Listed mesh update variable but can not find: ",VarName
599
CALL Fatal(SolverName, Message)
600
END IF
601
Var % Values = 0.0_dp
602
603
!Turn off (or on) the solver
604
!If CalvingOccurs, (switch) off = .true.
605
IF(PauseAfterCalving) CALL SwitchSolverExec(Var % Solver, (CalvingOccurs .AND. PauseSolvers))
606
END DO
607
608
!Turn off free surface solvers for next timestep
609
!And set values equal to z (or rotated) coordinate
610
DO Num = 1,999
611
WRITE(Message,'(A,I0)') 'FreeSurface Variable ',Num
612
VarName = ListGetString( Params, Message, Found)
613
IF( .NOT. Found) EXIT
614
615
Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )
616
IF(.NOT. ASSOCIATED(Var)) THEN
617
WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",VarName
618
CALL Fatal(SolverName, Message)
619
END IF
620
621
RefVar => VariableGet( Model % Mesh % Variables, "Reference "//TRIM(VarName), .TRUE. )
622
IF(.NOT. ASSOCIATED(RefVar)) THEN
623
WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",&
624
"Reference "//TRIM(VarName)
625
CALL Fatal(SolverName, Message)
626
END IF
627
628
WRITE(Message, '(A,A)') TRIM(Message) // " Rotated"
629
RotFS = ListGetLogical(Params, Message, Found)
630
IF(.NOT. Found) RotFS = .FALSE.
631
632
IF(RotFS) THEN !calving "zs" front
633
DO i=1,Model % Mesh % NumberOfNodes
634
IF(Var % Perm(i) <= 0) CYCLE
635
NodeHolder(1) = Model % Mesh % Nodes % x(i)
636
NodeHolder(2) = Model % Mesh % Nodes % y(i)
637
NodeHolder(3) = Model % Mesh % Nodes % z(i)
638
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
639
Var % Values(Var % Perm(i)) = NodeHolder(3)
640
RefVar % Values(RefVar % Perm(i)) = NodeHolder(3)
641
END DO
642
ELSE
643
DO i=1,Model % Mesh % NumberOfNodes
644
IF(Var % Perm(i) <= 0) CYCLE
645
Var % Values(Var % Perm(i)) = Model % Mesh % Nodes % z(i)
646
RefVar % Values(RefVar % Perm(i)) = Model % Mesh % Nodes % z(i)
647
END DO
648
END IF
649
650
!Turn off (or on) the solver
651
!If CalvingOccurs, (switch) off = .true.
652
IF(PauseAfterCalving) CALL SwitchSolverExec(Var % Solver, (CalvingOccurs .AND. PauseSolvers))
653
END DO
654
655
IF(PauseAfterCalving) THEN
656
657
IF(CalvingOccurs .AND. PauseSolvers) THEN
658
CALL ListAddConstReal( Model % Simulation, 'Timestep Size', PseudoSSdt)
659
CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', 1)
660
ELSE
661
CALL ListAddConstReal( Model % Simulation, 'Timestep Size', SaveDt)
662
CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', SaveSSiter)
663
END IF
664
665
DO Num = 1,999
666
WRITE(Message,'(A,I0)') 'Switch Off Equation ',Num
667
EqName = ListGetString( Params, Message, Found)
668
IF( .NOT. Found) EXIT
669
670
Found = .FALSE.
671
DO j=1,Model % NumberOfSolvers
672
IF(ListGetString(Model % Solvers(j) % Values, "Equation") == EqName) THEN
673
Found = .TRUE.
674
!Turn off (or on) the solver
675
!If CalvingOccurs, (switch) off = .true.
676
CALL SwitchSolverExec(Model % Solvers(j), (CalvingOccurs .AND. PauseSolvers))
677
EXIT
678
END IF
679
END DO
680
681
IF(.NOT. Found) THEN
682
WRITE (Message,'(A,A,A)') "Failed to find Equation Name: ",EqName,&
683
" to switch off after calving."
684
CALL Fatal(SolverName,Message)
685
END IF
686
END DO
687
END IF
688
689
!Zero TangledVar on new mesh - this avoids the problem of interpolated values not being
690
!updated next timestep, leading to erroneous tangling.
691
IF(CheckTangled) THEN
692
TangledVar => VariableGet(Model % Mesh % Variables, TangledVarName, .TRUE., UnfoundFatal=.TRUE.)
693
TangledVar % Values = 0.0_dp
694
END IF
695
696
FirstTime = .FALSE.
697
698
!CHANGE
699
IF(time == CalvingTime) CalvingLastTime = CalvingOccurs
700
701
IF(ASSOCIATED(NewBasalNode)) DEALLOCATE(NewBasalNode)
702
703
CONTAINS
704
705
!--------------------------------------------------------------
706
! Takes an existing extruded mesh with a non-vertical face,
707
! remeshes the footprint, extrudes the mesh, mesh-updates the
708
! calving front to match the old one, and returns a handle
709
! to the new mesh : NewMesh
710
!--------------------------------------------------------------
711
SUBROUTINE CalvingRemesh(Model, Solver, Mesh, NewMesh, Parallel, Transient)
712
713
USE CalvingGeometry
714
USE MainUtils
715
USE InterpVarToVar
716
717
IMPLICIT NONE
718
719
TYPE(Model_t) :: Model
720
TYPE(Solver_t), TARGET :: Solver
721
TYPE(Mesh_t), POINTER :: Mesh, NewMesh, FootprintMesh, ExtrudedMesh
722
LOGICAL :: Parallel, Transient
723
!-------------------------------------------
724
TYPE(Mesh_t), POINTER :: OldMesh
725
TYPE(Solver_t), POINTER :: MUSolver=>NULL()
726
TYPE(Matrix_t), POINTER :: StiffMatrix
727
TYPE(Element_t), POINTER :: Element, CurrentElement
728
TYPE(ValueList_t), POINTER :: Params, Material
729
TYPE(Variable_t), POINTER :: TopVar=>NULL(), BottomVar=>NULL(), OldGLVar, NewGLVar, &
730
WorkVar, HeightVar, Var, TimestepVar
731
TYPE(Nodes_t), TARGET :: FaceNodesT, LeftNodes, RightNodes, BackNodes, FrontNodes, WorkNodes, Nodes
732
TYPE(Nodes_t), POINTER :: WriteNodes
733
TYPE(GaussIntegrationPoints_t) :: IP
734
LOGICAL :: Boss, Found, Debug, FirstTime = .TRUE., BadMesh, &
735
TriedMetis(5), ThisBC, DoGL, AllFail, AllFailCatch, RemovedOne, InGroup, First, &
736
FixDegenerate, MoveMesh=.FALSE.,CalvingColumn, AnyDegenerate,does_intersect
737
LOGICAL, ALLOCATABLE :: RemoveNode(:), IsCalvingNode(:), Degenerate(:)
738
LOGICAL, POINTER :: UnfoundNodes(:)=>NULL(), OldElemMask(:)
739
REAL(KIND=dp) :: MeshEdgeLC, MeshMinDist, MeshMaxDist, MeshMinLC, MeshMaxLC, MeshRmLC,&
740
MeshRmThresh, extrude_localeps, extrude_globaleps, Norm, MuStretchZ, NodeHolder(3), &
741
ColMin(3), ColMax(3), p1(2), p2(2), q1(2), q2(2), intersect(2), &
742
BotDisplacement, TopDisplacement, Displacement, prop, x,dx,maxdz,maxdzdx,DzDxThresh,&
743
DzDxMaxDev,ThisDzDxMaxDev,dist,detJ,ShiftBuffer,&
744
rt0, rt
745
746
REAL(KIND=dp), POINTER :: TopVarValues(:), BottomVarValues(:), ZeroOneHeight(:),&
747
ActualHeight(:), WorkReal(:), WorkReal2(:), ForceVector(:),Basis(:)
748
REAL(KIND=dp), ALLOCATABLE :: STIFF(:,:), FORCE(:), BedHeight(:), RemovalDeviation(:),&
749
RemovalDistance(:), ColumnRange(:),TangledZone(:,:),LocalCalvingVar(:),&
750
FrontCalving1Values(:), MyDegenerateCoords(:,:), DegenerateCoords(:,:)
751
CHARACTER(MAX_NAME_LEN) :: SolverName, Str, NonVertBCName, MeshDir, MeshName, filename, &
752
filename_root, MaskName, MuVarName, TopVarName, BottomVarName,&
753
NameSuffix, FrontLineMethod, GLVarName, VarName, MoveMeshDir,MoveMeshFullPath,&
754
WorkName
755
INTEGER :: i,j,k,n, counter, NoNodes, dummyint, FaceNodeCount, &
756
ExtrudedLevels, ExtrudeLevels, NodesPerLevel, start, fin, stride, next, WriteNodeCount, &
757
MeshBC,col, dim, MetisMethod, MetisMethodDefault, active, NextBasalPerm, &
758
FrontBCtag, GroupCount, GroupEnd, GroupStart, TangledGroups
759
760
INTEGER :: comm, ierr, Me, PEs, TotalNodes, DegenCount, ier
761
INTEGER, PARAMETER :: GeoUnit = 10
762
INTEGER, ALLOCATABLE :: MyFaceNodeNums(:), PFaceNodeCount(:), FNColumns(:), disps(:), &
763
WritePoints(:), LocalTangledNode(:), TangledNode(:), WorkInt(:), TangledColumn(:),&
764
PartCountDegenerate(:)
765
INTEGER, POINTER :: TopPerm(:)=>NULL(), BotPerm(:)=>NULL(), FrontPerm(:)=>NULL(), &
766
ExtrudedFrontPerm(:)=>NULL(), WorkPerm(:),OrderPerm(:),BList(:), &
767
InterpDim(:)=>NULL(), ColumnPerm(:), TopVarPerm(:), FootprintFrontPerm(:)=>NULL(),&
768
BottomVarPerm(:), TopVarOldPerm(:), BottomVarOldPerm(:)
769
INTEGER, POINTER :: LeftLineNodeNums(:), RightLineNodeNums(:), &
770
BackLineNodeNums(:), FrontLineNodeNums(:), NodeNums(:),FaceNodeNums(:)=>NULL()
771
772
SAVE FirstTime, MoveMesh
773
774
INTERFACE
775
SUBROUTINE InterpolateMeshToMesh( OldMesh, NewMesh, OldVariables, &
776
NewVariables, UseQuadrantTree, Projector, MaskName, UnfoundNodes )
777
!------------------------------------------------------------------------------
778
USE Lists
779
USE SParIterComm
780
USE Interpolation
781
USE CoordinateSystems
782
!-------------------------------------------------------------------------------
783
TYPE(Mesh_t), TARGET :: OldMesh, NewMesh
784
TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables, NewVariables
785
LOGICAL, OPTIONAL :: UseQuadrantTree
786
LOGICAL, POINTER, OPTIONAL :: UnfoundNodes(:)
787
TYPE(Projector_t), POINTER, OPTIONAL :: Projector
788
CHARACTER(LEN=*),OPTIONAL :: MaskName
789
END SUBROUTINE InterpolateMeshToMesh
790
END INTERFACE
791
792
Debug = .FALSE.
793
794
CALL Info( 'Remesher', '-----------------------------------------------',Level=4 )
795
CALL Info( 'Remesher', ' Using calving glacier remeshing implementation',Level=4 )
796
CALL Info( 'Remesher', '-----------------------------------------------',Level=4 )
797
798
rt0 = RealTime()
799
800
dim = CoordinateSystemDimension()
801
SolverName = "Calving Remesh"
802
Params => Solver % Values
803
Boss = (ParEnv % MyPE == 0) .OR. (.NOT. Parallel)
804
TriedMetis = .FALSE.
805
806
extrude_globaleps = global_eps
807
extrude_localeps = local_eps
808
809
OldMesh => Mesh
810
DegenCount = 0
811
812
NonVertBCName = ListGetString(Params, "Non-Vertical Face Name", Found, UnfoundFatal=.TRUE.)
813
NameSuffix = ListGetString(Params, "Remesh Append Name", Found, UnfoundFatal=.TRUE.)
814
815
!Produce mesh and gmsh .geo filenames
816
WRITE(filename_root,'(A,A)') "Remesh_temp",TRIM(NameSuffix)
817
WRITE(filename,'(A,A)') TRIM(filename_root), ".geo"
818
819
MoveMeshDir = ListGetString(Params, "Remesh Move Mesh Dir", Found)
820
IF(Found) THEN
821
MoveMesh = .TRUE.
822
CALL Info(SolverName, "Moving temporary mesh files after done")
823
END IF
824
825
MetisMethodDefault = ListGetInteger(Params, "Metis Algorithm", Found)
826
IF(.NOT. Found ) MetisMethodDefault = 4
827
MetisMethod = MetisMethodDefault
828
829
!NOTE: don't first time these, they can be altered if meshing fails
830
!TODO: MeshEdgeLC isn't really necessary because we use the Distance field...
831
MeshEdgeLC = ListGetConstReal(Params, "Remesh Default Characteristic Length", Found, UnfoundFatal=.TRUE.)
832
MeshMinLC = ListGetConstReal(Params, "Remesh Min Characteristic Length", Found, UnfoundFatal=.TRUE.)
833
MeshMaxLC = ListGetConstReal(Params, "Remesh Max Characteristic Length", Found, UnfoundFatal=.TRUE.)
834
MeshMinDist = ListGetConstReal(Params, "Remesh Min Distance Threshold", Found, UnfoundFatal=.TRUE.)
835
MeshMaxDist = ListGetConstReal(Params, "Remesh Max Distance Threshold", Found, UnfoundFatal=.TRUE.)
836
MeshRmLC = ListGetConstReal(Params, "Remesh Remove Nodes Closer Than", Found, UnfoundFatal=.TRUE.)
837
MeshRmThresh = ListGetConstReal(Params, "Remesh Remove Nodes Deviation Threshold", Found, UnfoundFatal=.TRUE.)
838
839
DzDxThresh = ListGetConstReal(Params, "Remesh Max Displacement Gradient", FixDegenerate)
840
IF(FixDegenerate) THEN
841
CALL Info(SolverName, &
842
"Attempting to prevent degeneracy by limiting node displacement gradient.")
843
844
DzDxMaxDev = ListGetConstReal(Params, "Remesh Displacement Deviation Limit", Found)
845
IF(.NOT. Found) THEN
846
DzDxMaxDev = 50.0_dp
847
CALL Info(SolverName, &
848
"No deviation limit found for displacement gradient limitation, setting to 50m.")
849
END IF
850
END IF
851
852
!How the footprint mesh's calving front is computed:
853
FrontLineMethod = ListGetString(Params, "Vertical Front Computation", Found)
854
IF(.NOT. Found) FrontLineMethod = "midrange"
855
856
GLVarName = ListGetString(Params, "Grounding Line Variable Name", Found)
857
IF(.NOT. Found) THEN
858
CALL Info(SolverName, "No 'Grounding Line Variable Name' found, assuming GroundedMask")
859
GLVarName = "GroundedMask"
860
END IF
861
862
!Return here if new mesh has degenerate elements
863
8989 CONTINUE
864
865
OldGLVar => VariableGet(OldMesh % Variables, GLVarName, .TRUE.)
866
IF(ASSOCIATED(OldGLVar)) THEN
867
DoGL = .TRUE.
868
ELSE
869
DoGL = .FALSE.
870
IF(Found) THEN
871
CALL Fatal(SolverName, "Specified 'Grounding Line Variable Name' but variable not found!")
872
ELSE
873
CALL Info(SolverName, "Didn't find GroundedMask, not accounting for Grounding Line in remeshing.")
874
END IF
875
END IF
876
877
!------------------------------------------
878
! Notes on new strategy:
879
!
880
! - deformed by calving above to calc new footprint and 0-1
881
!
882
! - when cycling front columns, determine and store 0-1 z height
883
!
884
! - extrude footprint (already implemented)
885
!
886
! - Change front to 0-1 domain, Rotate, and InterpVarToVar
887
!
888
! - Mesh Update NewMesh based on interped values
889
!
890
! - Undeform from 0-1 back to real mesh
891
!
892
! - InterpVarToVarReduced on top and bottom (post deformed new mesh)
893
!
894
! - Poisson eq / Mesh Update?
895
!
896
!------------------------------------------
897
898
!-------------------------------------------
899
! Create nodal BC perms to make lookup simpler
900
!-------------------------------------------
901
NoNodes = OldMesh % NumberOfNodes
902
ALLOCATE( TopPerm(NoNodes), BotPerm(NoNodes), FrontPerm(NoNodes))
903
904
!Generate perms to quickly get nodes on each boundary
905
CALL MakePermUsingMask( Model, Solver, OldMesh, TopMaskName, &
906
.FALSE., TopPerm, dummyint)
907
CALL MakePermUsingMask( Model, Solver, OldMesh, BotMaskName, &
908
.FALSE., BotPerm, dummyint)
909
CALL MakePermUsingMask( Model, Solver, OldMesh, FrontMaskName, &
910
.FALSE., FrontPerm, FaceNodeCount) !<- Number of nodes in this part on calving face
911
912
IF(FrontalBecomeBasal .AND. Debug) &
913
PRINT *,ParEnv % MyPe,'Frontal become basal!'
914
915
916
!---------------------------------------------------
917
! FOOTPRINT GENERATION
918
!
919
! Get the global node
920
! numbers, connectivity etc of the footprint. Needs
921
! special care in parallel
922
!
923
!---------------------------------------------------
924
925
!Get each of the 4 edges of the top surface
926
!The result of these calls is only valid in Boss part
927
CALL GetDomainEdge(Model, OldMesh, TopPerm, LeftNodes, &
928
LeftLineNodeNums, Parallel, LeftMaskName,Simplify=.TRUE.)
929
IF(Debug) CALL Info(SolverName, "Done left domain edge")
930
CALL GetDomainEdge(Model, OldMesh, TopPerm, RightNodes, &
931
RightLineNodeNums, Parallel, RightMaskName, Simplify=.TRUE.)
932
IF(Debug) CALL Info(SolverName, "Done right domain edge")
933
CALL GetDomainEdge(Model, OldMesh, TopPerm, BackNodes, &
934
BackLineNodeNums, Parallel, InMaskName, Simplify=.TRUE.)
935
IF(Debug) CALL Info(SolverName, "Done back domain edge")
936
CALL GetDomainEdge(Model, OldMesh, TopPerm, FrontNodes, &
937
FrontLineNodeNums, Parallel, FrontMaskName, Simplify=.FALSE.)
938
IF(Debug) CALL Info(SolverName, "Done front domain edge")
939
940
!Call this again later to remove too close nodes from result
941
IF(Boss .AND. Debug) PRINT *, 'Debug CalvingRemesh, FrontLineNodeNums: ',FrontLineNodeNums
942
IF(Boss .AND. Debug) PRINT *, 'Debug CalvingRemesh, BackLineNodeNums: ',BackLineNodeNums
943
IF(Boss .AND. Debug) PRINT *, 'Debug CalvingRemesh, LeftLineNodeNums: ',LeftLineNodeNums
944
945
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
946
947
rt = RealTime() - rt0
948
IF(ParEnv % MyPE == 0) &
949
PRINT *, 'Remesh, Time taken for variable loading, making perms, GetDomainEdge: ', rt
950
rt0 = RealTime()
951
952
!---------------------------------------------
953
!
954
! Get average calving line
955
!
956
! TODO: front to basal important here
957
!---------------------------------------------
958
959
!Cycle all boundary elements in current partition, get calving face nodes
960
ALLOCATE(MyFaceNodeNums(FaceNodeCount))
961
j = 0
962
DO i=1, NoNodes
963
IF(FrontPerm(i) <= 0) CYCLE
964
j = j + 1
965
MyFaceNodeNums(j) = i
966
END DO
967
!Now MyFaceNodeNums is a list of all nodes on the calving boundary.
968
969
!Send calving nodes to boss partition
970
!This sacrifices a little performance for potentially unneeded generality
971
!Because the mesh is extruded, we could simply average each column of points
972
!and then send result to boss part. But let's not, in case we want to do
973
!some other kind of smoothing in the future.
974
IF(Parallel) THEN
975
976
Me = ParEnv % MyPe
977
PEs = ParEnv % PEs
978
comm = ParEnv % ActiveComm
979
980
!Send node COUNT
981
IF(Boss) ALLOCATE(PFaceNodeCount(PEs))
982
983
984
CALL MPI_GATHER(FaceNodeCount,1,MPI_INTEGER,PFaceNodeCount,&
985
1,MPI_INTEGER, 0, comm, ierr)
986
987
IF(Boss) THEN
988
FaceNodesT % NumberOfNodes = SUM(PFaceNodeCount)
989
n = FaceNodesT % NumberOfNodes
990
ALLOCATE(FaceNodeNums(n),&
991
FaceNodesT % x(n),&
992
FaceNodesT % y(n),&
993
FaceNodesT % z(n),&
994
disps(PEs)) !variable to hold array offset from each proc
995
!work out where in array to put data from each proc
996
!how to deal with zero size?
997
disps(1) = 0
998
DO i=2,PEs
999
disps(i) = disps(i-1) + PFaceNodeCount(i-1)
1000
END DO
1001
END IF
1002
1003
!Global NodeNumbers
1004
CALL MPI_GATHERV(OldMesh % ParallelInfo % GlobalDOFs(MyFaceNodeNums),&
1005
FaceNodeCount,MPI_INTEGER,&
1006
FaceNodeNums,PFaceNodeCount,&
1007
disps,MPI_INTEGER,0,comm, ierr)
1008
!X coords
1009
CALL MPI_GATHERV(OldMesh % Nodes % x(MyFaceNodeNums),&
1010
FaceNodeCount,MPI_DOUBLE_PRECISION,&
1011
FaceNodesT % x,PFaceNodeCount,&
1012
disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
1013
!Y coords
1014
CALL MPI_GATHERV(OldMesh % Nodes % y(MyFaceNodeNums),&
1015
FaceNodeCount,MPI_DOUBLE_PRECISION,&
1016
FaceNodesT % y,PFaceNodeCount,&
1017
disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
1018
!Z coords
1019
CALL MPI_GATHERV(OldMesh % Nodes % z(MyFaceNodeNums),&
1020
FaceNodeCount,MPI_DOUBLE_PRECISION,&
1021
FaceNodesT % z,PFaceNodeCount,&
1022
disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
1023
1024
!Gather tangled columns info to boss
1025
IF(TangleOccurs .AND. NSFail) &
1026
TangleOccurs=.FALSE.
1027
1028
IF(TangleOccurs) THEN
1029
IF(Boss) ALLOCATE(TangledNode(n), FrontCalving1Values(n))
1030
ALLOCATE(LocalTangledNode(FaceNodeCount),&
1031
LocalCalvingVar(FaceNodeCount))
1032
1033
LocalTangledNode = NINT(TangledVar % Values(TangledVar % Perm(MyFaceNodeNums)))
1034
LocalCalvingVar = &
1035
CalvingVar % Values((CalvingVar % Perm(MyFaceNodeNums)-1)*CalvingVar % DOFs + 1)
1036
1037
CALL MPI_GATHERV(LocalTangledNode,&
1038
FaceNodeCount,MPI_INTEGER,&
1039
TangledNode,PFaceNodeCount,&
1040
disps,MPI_INTEGER,0,comm, ierr)
1041
1042
CALL MPI_GATHERV(LocalCalvingVar,&
1043
FaceNodeCount,MPI_DOUBLE_PRECISION,&
1044
FrontCalving1Values,PFaceNodeCount,&
1045
disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
1046
1047
IF(Boss) THEN
1048
TangledGroups = MAXVAL(TangledNode)
1049
END IF
1050
1051
CALL MPI_BCAST( TangledGroups, 1, MPI_INTEGER, 0, comm, ierr)
1052
ALLOCATE(TangledZone(TangledGroups,2))
1053
END IF
1054
1055
IF(Boss) THEN
1056
IF(Debug) THEN
1057
PRINT *, 'Debug Remesh, pre removal nodes: '
1058
DO i=1,FaceNodesT % NumberOfNodes
1059
PRINT *, FaceNodesT % x(i),FaceNodesT % y(i),FaceNodesT % z(i)
1060
END DO
1061
END IF
1062
1063
!Remove duplicates!!!
1064
ALLOCATE(RemoveNode(FaceNodesT % NumberOfNodes))
1065
RemoveNode = .FALSE.
1066
DO i=2,FaceNodesT % NumberOfNodes
1067
IF(ANY(FaceNodeNums(1:i-1) == FaceNodeNums(i))) THEN
1068
RemoveNode(i) = .TRUE.
1069
END IF
1070
END DO
1071
1072
!Remove duplicate values from TangledNode
1073
IF(TangleOccurs) THEN
1074
ALLOCATE(WorkInt(COUNT(.NOT. RemoveNode)))
1075
WorkInt = PACK(TangledNode, .NOT. RemoveNode)
1076
DEALLOCATE(TangledNode)
1077
ALLOCATE(TangledNode(SIZE(WorkInt)))
1078
TangledNode = WorkInt
1079
DEALLOCATE(WorkInt)
1080
1081
ALLOCATE(WorkReal(COUNT(.NOT. RemoveNode)))
1082
WorkReal = PACK(FrontCalving1Values, .NOT. RemoveNode)
1083
DEALLOCATE(FrontCalving1Values)
1084
ALLOCATE(FrontCalving1Values(SIZE(WorkReal)))
1085
FrontCalving1Values = WorkReal
1086
DEALLOCATE(WorkReal)
1087
END IF
1088
1089
CALL RemoveNodes(FaceNodesT, RemoveNode, FaceNodeNums)
1090
DEALLOCATE(RemoveNode)
1091
1092
IF(Debug) THEN
1093
PRINT *, 'Size of FaceNodeNums: ', SIZE(FaceNodeNums)
1094
PRINT *, 'Debug Remesh, post removal nodes: '
1095
DO i=1,FaceNodesT % NumberOfNodes
1096
PRINT *, FaceNodesT % x(i),FaceNodesT % y(i),FaceNodesT % z(i)
1097
END DO
1098
END IF
1099
END IF
1100
1101
ELSE !Serial
1102
n = FaceNodeCount
1103
ALLOCATE(FaceNodeNums(n),&
1104
FaceNodesT % x(n),&
1105
FaceNodesT % y(n),&
1106
FaceNodesT % z(n))
1107
1108
!This seems a little redundant but it saves
1109
!some lines of code later on...
1110
FaceNodeNums = MyFaceNodeNums
1111
FaceNodesT % x = OldMesh % Nodes % x(MyFaceNodeNums)
1112
FaceNodesT % y = OldMesh % Nodes % y(MyFaceNodeNums)
1113
FaceNodesT % z = OldMesh % Nodes % z(MyFaceNodeNums)
1114
END IF
1115
1116
rt = RealTime() - rt0
1117
IF(ParEnv % MyPE == 0) &
1118
PRINT *, 'Remesh, Time taken for collecting front node, removing duplicates: ', rt
1119
rt0 = RealTime()
1120
1121
!--------------------------------------------------------------------
1122
1123
!Need global mesh structure info
1124
IF(Parallel) THEN
1125
!Rather than summing NoNodes from each part, we simply find
1126
!the maximum global node number
1127
CALL MPI_Reduce(MAXVAL(OldMesh % ParallelInfo % GlobalDOFs), TotalNodes, &
1128
1, MPI_INTEGER, MPI_MAX, 0,comm,ierr)
1129
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
1130
1131
ELSE
1132
TotalNodes = NoNodes
1133
END IF
1134
1135
ExtrudedLevels = GetInteger(CurrentModel % Simulation,'Extruded Mesh Levels',Found)
1136
IF(.NOT. Found) ExtrudedLevels = &
1137
GetInteger(CurrentModel % Simulation,'Remesh Extruded Mesh Levels',Found)
1138
IF(.NOT. Found) CALL Fatal("Remesh",&
1139
"Unable to find 'Extruded Mesh Levels' or 'Remesh Extruded Mesh Levels'")
1140
1141
IF(Boss) THEN !Master/Slave problem (or serial)
1142
1143
IF(Debug) PRINT *, 'Debug remesh, Total nodes: ', TotalNodes
1144
1145
IF(MOD(TotalNodes, ExtrudedLevels) /= 0) &
1146
CALL Fatal("Remesh","Total number of nodes isn't divisible by number&
1147
&of mesh levels. WHY?")
1148
1149
NodesPerLevel = TotalNodes / ExtrudedLevels
1150
1151
PRINT *, 'Debug CalvingRemesh, NodesPerLevel: ', NodesPerLevel
1152
1153
!note, if ExtrudedLevels is messed with in remeshing, it'll need to be
1154
!pushed back to simulation
1155
1156
!Now columns *should* have the same integer FNColumns
1157
!NOTE: Think about how messing with BCs following undercutting affects this
1158
ALLOCATE(FNColumns(FaceNodesT % NumberOfNodes), &
1159
ColumnRange(SIZE(FrontLineNodeNums)))
1160
1161
FNColumns = MOD(FaceNodeNums, NodesPerLevel)
1162
1163
!Check for tangled columns
1164
IF(TangleOccurs) THEN
1165
ALLOCATE(TangledColumn(SIZE(FrontLineNodeNums)))
1166
TangledColumn = 0
1167
IF(Debug) PRINT *,'tangled nodes: ',COUNT(TangledNode > 0)
1168
1169
DO i=1,TangledGroups
1170
TangledZone(i,1) = HUGE(TangledZone(i,1))
1171
TangledZone(i,2) = -HUGE(TangledZone(i,2))
1172
END DO
1173
1174
DO i=1,SIZE(FrontLineNodeNums) !cycle columns
1175
j = MOD(FrontLineNodeNums(i), NodesPerLevel)
1176
CalvingColumn = .FALSE.
1177
!If any node in column is tangled, mark column
1178
! (in fact all nodes in column will be)
1179
DO k=1,SIZE(FNColumns)
1180
IF(FNColumns(k)==j) THEN
1181
IF(TangledNode(k) > 0) THEN
1182
TangledColumn(i) = TangledNode(k)
1183
IF(FrontCalving1Values(k) /= 0.0_dp) CalvingColumn = .TRUE.
1184
END IF
1185
END IF
1186
END DO
1187
1188
!Mark the extent of tangling
1189
IF((TangledColumn(i) > 0) .AND. .NOT. CalvingColumn) THEN
1190
NodeHolder(1) = FrontNodes % x(i)
1191
NodeHolder(2) = FrontNodes % y(i)
1192
NodeHolder(3) = FrontNodes % z(i)
1193
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1194
TangledZone(TangledColumn(i),1) = MIN(TangledZone(TangledColumn(i),1), NodeHolder(2))
1195
TangledZone(TangledColumn(i),2) = MAX(TangledZone(TangledColumn(i),2), NodeHolder(2))
1196
END IF
1197
END DO
1198
PRINT *,'Remesh, identified ',COUNT(TangledColumn > 0), ' tangled columns.'
1199
IF(COUNT(TangledColumn > 0) == 0) CALL Fatal(SolverName, &
1200
"TangleOccurs is true, but found no tangled columns...")
1201
END IF
1202
1203
!---------------------------------------------------------------------
1204
! Cycle FrontLineNodeNums, finding column averages and updating locations
1205
!---------------------------------------------------------------------
1206
FrontNodes % x = 0
1207
FrontNodes % y = 0
1208
FrontNodes % z = 0
1209
1210
DO i=1,SIZE(FrontLineNodeNums) !cycle columns
1211
j = MOD(FrontLineNodeNums(i), NodesPerLevel)
1212
WorkNodes % NumberOfNodes = COUNT(FNColumns == j)
1213
n = WorkNodes % NumberOfNodes
1214
1215
IF(n < 2) CALL Fatal("CalvingRemesh",&
1216
"Found fewer than 2 nodes for a column of calving face nodes.")
1217
IF(Debug) THEN
1218
PRINT *, 'Debug CalvingRemesh, number of worknodes: ',n
1219
END IF
1220
1221
ALLOCATE(WorkNodes % x(n),&
1222
WorkNodes % y(n),&
1223
WorkNodes % z(n))
1224
1225
!----------------------------------------------
1226
! Compute and store column horizontal displacement range
1227
ColMin(3) = HUGE(ColMin(3))
1228
ColMax(3) = -HUGE(ColMax(3))
1229
DO k=1,SIZE(FNColumns)
1230
IF(FNColumns(k)==j) THEN
1231
NodeHolder(1) = FaceNodesT % x(k)
1232
NodeHolder(2) = FaceNodesT % y(k)
1233
NodeHolder(3) = FaceNodesT % z(k)
1234
1235
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1236
1237
IF(NodeHolder(3) < ColMin(3)) ColMin = NodeHolder
1238
1239
IF(NodeHolder(3) > ColMax(3)) ColMax = NodeHolder
1240
END IF
1241
END DO
1242
ColumnRange(i) = ColMax(3) - ColMin(3)
1243
1244
!----------------------------------------------
1245
! Compute front points for plane mesh
1246
1247
SELECT CASE(FrontLineMethod)
1248
CASE("mean")
1249
1250
counter=1
1251
DO k=1,SIZE(FNColumns)
1252
IF(FNColumns(k)==j) THEN
1253
WorkNodes % x(counter) = FaceNodesT % x(k)
1254
WorkNodes % y(counter) = FaceNodesT % y(k)
1255
WorkNodes % z(counter) = FaceNodesT % z(k)
1256
counter = counter + 1
1257
END IF
1258
END DO
1259
1260
!Order by ascending WorkNodes % z
1261
ALLOCATE(OrderPerm(n))
1262
DO k=1,n; OrderPerm(k) = k;
1263
END DO
1264
1265
CALL SortD( n, WorkNodes % z, OrderPerm )
1266
CALL MySortF( n, OrderPerm, WorkNodes % x )
1267
CALL MySortF( n, OrderPerm, WorkNodes % y )
1268
1269
!Zs aren't necessarily equidistant, better: mean = integral/total_z
1270
!**Trapezoid rule over nodes in column**
1271
DO k=2,n
1272
FrontNodes % x(i) = FrontNodes % x(i) + &
1273
((WorkNodes % x(k) + WorkNodes % x(k-1))/2.0_dp) * &
1274
(WorkNodes % z(k) - WorkNodes % z(k-1))
1275
1276
FrontNodes % y(i) = FrontNodes % y(i) + &
1277
((WorkNodes % y(k) + WorkNodes % y(k-1))/2.0_dp) * &
1278
(WorkNodes % z(k) - WorkNodes % z(k-1))
1279
END DO
1280
1281
FrontNodes % x(i) = FrontNodes % x(i) / (WorkNodes % z(n) - WorkNodes % z(1))
1282
FrontNodes % y(i) = FrontNodes % y(i) / (WorkNodes % z(n) - WorkNodes % z(1))
1283
1284
IF(Debug) PRINT *,'Debug, calving column ',i,' has points :',&
1285
FrontNodes % x(i), FrontNodes % y(i)
1286
1287
DEALLOCATE(OrderPerm)
1288
1289
CASE("minimum")
1290
1291
ColMin(3) = HUGE(ColMin(3))
1292
DO k=1,SIZE(FNColumns)
1293
IF(FNColumns(k)==j) THEN
1294
NodeHolder(1) = FaceNodesT % x(k)
1295
NodeHolder(2) = FaceNodesT % y(k)
1296
NodeHolder(3) = FaceNodesT % z(k)
1297
1298
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1299
1300
IF(NodeHolder(3) < ColMin(3)) ColMin = NodeHolder
1301
END IF
1302
END DO
1303
1304
NodeHolder = MATMUL(UnRotationMatrix, ColMin)
1305
1306
FrontNodes % x(i) = NodeHolder(1)
1307
FrontNodes % y(i) = NodeHolder(2)
1308
1309
IF(Debug) PRINT *,'Debug, calving column ',i,' has points :',&
1310
FrontNodes % x(i), FrontNodes % y(i)
1311
1312
CASE("midrange")
1313
1314
ColMin(3) = HUGE(ColMin(3))
1315
ColMax(3) = -HUGE(ColMax(3))
1316
DO k=1,SIZE(FNColumns)
1317
IF(FNColumns(k)==j) THEN
1318
NodeHolder(1) = FaceNodesT % x(k)
1319
NodeHolder(2) = FaceNodesT % y(k)
1320
NodeHolder(3) = FaceNodesT % z(k)
1321
1322
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1323
1324
IF(NodeHolder(3) < ColMin(3)) ColMin = NodeHolder
1325
1326
IF(NodeHolder(3) > ColMax(3)) ColMax = NodeHolder
1327
END IF
1328
END DO
1329
1330
! NodeHolder(1) = MinColX + 0.5*(MaxColX - MinColX)
1331
! NodeHolder(2) = MinColY + 0.5*(MaxColY - MinColY)
1332
! NodeHolder(3) = MinColZ + 0.5*(MaxColZ - MinColZ)
1333
NodeHolder = ColMin + 0.5*(ColMax - ColMin)
1334
NodeHolder = MATMUL(UnRotationMatrix, NodeHolder)
1335
1336
FrontNodes % x(i) = NodeHolder(1)
1337
FrontNodes % y(i) = NodeHolder(2)
1338
1339
IF(Debug) PRINT *,'Debug, calving column ',i,' has points :',&
1340
FrontNodes % x(i), FrontNodes % y(i)
1341
CASE DEFAULT
1342
CALL Fatal(SolverName, "Invalid choice given for 'Vertical Front Computation'")
1343
END SELECT
1344
1345
DEALLOCATE(WorkNodes % x, WorkNodes % y, WorkNodes % z)
1346
END DO
1347
1348
DEALLOCATE(FNColumns)
1349
IF(Debug) THEN
1350
PRINT *, 'Debug CalvingRemesh, FrontNodes: '
1351
DO i=1,FrontNodes % NumberOfNodes
1352
PRINT *, 'node: ',i,' x: ',FrontNodes % x(i),' y: ', FrontNodes % y(i)
1353
END DO
1354
END IF
1355
1356
END IF !Boss
1357
1358
!------------------------------------------------------------
1359
! Calculate ZeroOneHeight for all calving front nodes
1360
!------------------------------------------------------------
1361
1362
!Send NodesPerLevel (to get column mod)
1363
CALL MPI_BCAST( NodesPerLevel, 1, MPI_INTEGER, 0, comm, ierr)
1364
1365
ALLOCATE(FNColumns(OldMesh % NumberOfNodes),&
1366
ZeroOneHeight(FaceNodeCount))
1367
1368
FNColumns = MOD(OldMesh % ParallelInfo % GlobalDOFs, NodesPerLevel)
1369
ZeroOneHeight = -1.0_dp
1370
1371
DO WHILE(.TRUE.) !cycle columns
1372
1373
!Find a new column
1374
col = -1
1375
DO j=1, OldMesh % NumberOfNodes
1376
IF(FrontPerm(j) <= 0) CYCLE
1377
IF(ZeroOneHeight(FrontPerm(j)) == -1.0_dp) THEN
1378
col = FNColumns(j)
1379
EXIT
1380
END IF
1381
END DO
1382
IF(col == -1) EXIT !All done
1383
1384
!Gather front nodes in specified column
1385
WorkNodes % NumberOfNodes = COUNT((FrontPerm > 0) .AND. (FNColumns == col))
1386
n = WorkNodes % NumberOfNodes
1387
ALLOCATE(WorkNodes % z(n), ColumnPerm(n))
1388
1389
counter = 1
1390
DO j=1, OldMesh % NumberOfNodes
1391
IF(FrontPerm(j) <= 0) CYCLE
1392
IF(FNColumns(j) == col) THEN
1393
WorkNodes % z(counter) = OldMesh % Nodes % z(j)
1394
ColumnPerm(counter) = j
1395
counter = counter + 1
1396
END IF
1397
END DO
1398
1399
!Calc ZeroOneHeight
1400
!Take advantage of the fact that nodes are numbered up from the bottom level, so
1401
!worknodes(1) is the lowest, and worknodes(n) is the top.
1402
DO k=1,n !Node elevation in 0-1
1403
ZeroOneHeight(FrontPerm(ColumnPerm(k))) = (WorkNodes % z(k) - WorkNodes % z(1)) / &
1404
(WorkNodes % z(n) - WorkNodes % z(1))
1405
END DO
1406
1407
DEALLOCATE(WorkNodes % z, ColumnPerm)
1408
END DO
1409
1410
rt = RealTime() - rt0
1411
IF(ParEnv % MyPE == 0) &
1412
PRINT *, 'Remesh, Time taken for calculating zero one height: ', rt
1413
rt0 = RealTime()
1414
1415
!Remove very close nodes & those which will lead to mesh degeneracy
1416
!Also remove those which were marked by FrontAdvance3D.F90 as being tangled
1417
IF(Boss) THEN
1418
!------------------------------------------------------------
1419
! Check no calving nodes too close (too far will be dealt with by gmsh)
1420
!
1421
! Strategy:
1422
! For each node, calculate:
1423
! - Mean dist from 2 neighbours
1424
! - Distance from point to line which would result from its removal
1425
! Effectively, this is the resulting 'deviation' of the front
1426
!
1427
! Each cycle, remove the best candidate:
1428
! lowest distance & below deviation threshold
1429
!
1430
! This isn't terribly efficient, everything is recomputed each time a
1431
! node is removed, but it's not very computationally expensive...
1432
!------------------------------------------------------------
1433
ALLOCATE(RemovalDeviation(FrontNodes % NumberOfNodes), &
1434
RemovalDistance(FrontNodes % NumberOfNodes),&
1435
RemoveNode(FrontNodes % NumberOfNodes))
1436
1437
RemoveNode = .FALSE.
1438
1439
!Remove tangled columns
1440
IF(TangleOccurs) THEN
1441
DO i=2,FrontNodes % NumberOfNodes-1
1442
IF(TangledColumn(i) > 0) RemoveNode(i) = .TRUE.
1443
END DO
1444
END IF
1445
1446
RemovedOne = .TRUE.
1447
DO WHILE(RemovedOne)
1448
RemovedOne = .FALSE.
1449
1450
RemovalDeviation = HUGE(0.0_dp)
1451
RemovalDistance = HUGE(0.0_dp)
1452
1453
!find deviation of point in front direction if removed...
1454
!and average distance between each node and its neighbours
1455
!NOTE: code duplication here from above
1456
DO i=2,FrontNodes % NumberOfNodes-1
1457
1458
IF(RemoveNode(i)) CYCLE !already got
1459
1460
j = i - 1
1461
k = i + 1
1462
1463
!If neighbours are marked for removal, look for next available
1464
!neighbour in each direction
1465
DO WHILE(RemoveNode(j))
1466
j = j-1
1467
END DO
1468
DO WHILE(RemoveNode(k))
1469
k = k+1
1470
END DO
1471
1472
p1(1) = FrontNodes % x(j) !prev point
1473
p1(2) = FrontNodes % y(j)
1474
p2(1) = FrontNodes % x(k) !next point
1475
p2(2) = FrontNodes % y(k)
1476
q1(1) = FrontNodes % x(i) !this point
1477
q1(2) = FrontNodes % y(i)
1478
q2(1) = FrontNodes % x(i) + FrontOrientation(1)
1479
q2(2) = FrontNodes % y(i) + FrontOrientation(2)
1480
1481
!Find where this node would theoretically sit between its
1482
!neighbours, were it removed. Then find the distance
1483
!p1,p2 are the surrounding points on front
1484
!q1 is this point, q2 is another point on the line from
1485
!this point, normal to the front direction
1486
CALL LinesIntersect ( p1, p2, q1, q2, intersect, does_intersect )
1487
1488
IF(does_intersect) THEN
1489
RemovalDeviation(i) = &
1490
( ((q1(1) - intersect(1))**2) + ((q1(2) - intersect(2))**2) ) ** 0.5
1491
ELSE
1492
!Rare (impossible?) case where line between the two neighbours
1493
!is parallel to front orientation.
1494
RemovalDeviation(i) = 0.0_dp
1495
END IF
1496
1497
RemovalDistance(i) = (NodeDist2D(FrontNodes,i,j) + NodeDist2D(FrontNodes,i,k)) / 2.0_dp
1498
1499
!Scale RemovalDeviation by removal distance.
1500
!For a given front deviation, the larger the RemovalDistance, the larger
1501
!the volume artificially missing from the front.
1502
!So we scale dev = dev * (maxdist / dist)
1503
!Thus, note that given value for MeshRmThresh is for dist = MeshRmLC
1504
RemovalDistance(i) = MAX(RemovalDistance(i),0.0001_dp) !avoid /0
1505
RemovalDeviation(i) = RemovalDeviation(i) / (MeshRmLC / RemovalDistance(i))
1506
END DO
1507
1508
IF(Debug) THEN
1509
PRINT *,'Debug minval, minloc, removaldistance', MINVAL(RemovalDistance), &
1510
MINLOC(RemovalDistance,1)
1511
PRINT *,'Debug minval, minloc, removaldeviation', MINVAL(RemovalDeviation), &
1512
MINLOC(RemovalDeviation,1)
1513
END IF
1514
1515
!If the closest node on the front has a sufficiently low removal deviation, remove it
1516
!else, look for the second closest, etc.
1517
DO WHILE(.TRUE.)
1518
1519
IF(MINVAL(RemovalDistance) > MeshRmLC) EXIT
1520
IF(MINVAL(RemovalDeviation) > MeshRmThresh) EXIT
1521
1522
IF(RemovalDeviation(MINLOC(RemovalDistance,1)) < MeshRmThresh) THEN
1523
IF(Debug) THEN
1524
PRINT *, 'Debug CalvingRemesh, MinDist, removing node ',MINLOC(RemovalDistance)&
1525
,' dist: ', MINVAL(RemovalDistance),' deviation: ',&
1526
RemovalDeviation(MINLOC(RemovalDistance,1))
1527
END IF
1528
1529
RemoveNode(MINLOC(RemovalDistance)) = .TRUE.
1530
RemovedOne = .TRUE.
1531
EXIT
1532
END IF
1533
RemovalDeviation(MINLOC(RemovalDistance)) = HUGE(0.0_dp)
1534
RemovalDistance(MINLOC(RemovalDistance)) = HUGE(0.0_dp)
1535
END DO
1536
END DO
1537
1538
!Look for likely degenerates
1539
IF(FixDegenerate) THEN
1540
1541
RemovedOne = .TRUE.
1542
1543
DO WHILE(RemovedOne)
1544
RemovedOne = .FALSE.
1545
1546
!find deviation of point in front direction if removed...
1547
DO i=2,FrontNodes % NumberOfNodes-1
1548
1549
IF(RemoveNode(i)) CYCLE !already got
1550
1551
j = i - 1
1552
k = i + 1
1553
1554
!If neighbours are marked for removal, look for next available
1555
!neighbour in each direction
1556
DO WHILE(RemoveNode(j))
1557
j = j-1
1558
END DO
1559
1560
DO WHILE(RemoveNode(k))
1561
k = k+1
1562
END DO
1563
1564
p1(1) = FrontNodes % x(j) !prev point
1565
p1(2) = FrontNodes % y(j)
1566
p2(1) = FrontNodes % x(k) !next point
1567
p2(2) = FrontNodes % y(k)
1568
q1(1) = FrontNodes % x(i) !this point
1569
q1(2) = FrontNodes % y(i)
1570
q2(1) = FrontNodes % x(i) + FrontOrientation(1)
1571
q2(2) = FrontNodes % y(i) + FrontOrientation(2)
1572
1573
!Find where this node would theoretically sit between its
1574
!neighbours, were it removed. Then find the distance
1575
!p1,p2 are the surrounding points on front
1576
!q1 is this point, q2 is another point on the line from
1577
!this point, normal to the front direction
1578
CALL LinesIntersect ( p1, p2, q1, q2, intersect, does_intersect )
1579
1580
IF(does_intersect) THEN
1581
RemovalDeviation(i) = &
1582
( ((q1(1) - intersect(1))**2) + ((q1(2) - intersect(2))**2) ) ** 0.5
1583
ELSE
1584
!Rare (impossible?) case where line between the two neighbours
1585
!is parallel to front orientation.
1586
RemovalDeviation(i) = 0.0_dp
1587
END IF
1588
END DO
1589
1590
!Look for columns where there is a large amount of horizontal
1591
!deviation (i.e. melt undercutting), and remove close nodes
1592
DO i=1,FrontNodes % NumberOfNodes-1
1593
IF(RemoveNode(i)) CYCLE
1594
1595
j = i + 1
1596
DO WHILE(RemoveNode(j))
1597
j = j+1
1598
END DO
1599
1600
!Find front-horizontal distance between nodes (dx)
1601
NodeHolder(1) = FrontNodes % x(i)
1602
NodeHolder(2) = FrontNodes % y(i)
1603
NodeHolder(3) = 0.0_dp
1604
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1605
1606
x = NodeHolder(2)
1607
1608
NodeHolder(1) = FrontNodes % x(j)
1609
NodeHolder(2) = FrontNodes % y(j)
1610
NodeHolder(3) = 0.0_dp
1611
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1612
1613
dx = ABS(x - NodeHolder(2))
1614
1615
!Compute maximum possible 'deviation' of plane nodes
1616
maxdz = MAX(ColumnRange(i), ColumnRange(j))
1617
maxdzdx = maxdz / dx
1618
1619
dist = NodeDist2D(FrontNodes,i,j)
1620
1621
ThisDzDxMaxDev = DzDxMaxDev / (DzDxThresh / maxdzdx)
1622
1623
IF(Debug) PRINT *,'Debug, Remesh, i, dx, maxdz, maxdzdx: ', i, dx, maxdz, maxdzdx
1624
1625
!Two conditions: 1) high gradient, 2) columns actually close in xy distance
1626
IF(maxdzdx > DzDxThresh .AND. dist < maxdz) THEN
1627
1628
!Check removal deviation here
1629
!If lower columnrange node has low deviation, remove it
1630
!Else if higher columnrange node has low deviation, remove it
1631
!Else nothing
1632
IF(j == FrontNodes % NumberOfNodes) THEN
1633
!Special case at end of line
1634
IF(RemovalDeviation(i) < ThisDzDxMaxDev) THEN
1635
RemoveNode(i) = .TRUE.
1636
RemovedOne = .TRUE.
1637
EXIT !go back and recalc
1638
END IF
1639
ELSE IF(i == 1) THEN
1640
!Special case at start of line
1641
IF(RemovalDeviation(j) < ThisDzDxMaxDev) THEN
1642
RemoveNode(j) = .TRUE.
1643
RemovedOne = .TRUE.
1644
EXIT !go back and recalc
1645
END IF
1646
ELSE
1647
!Remove whichever node has smallest displacement range
1648
IF((ColumnRange(i) < ColumnRange(j)) ) THEN
1649
IF(RemovalDeviation(i) < ThisDzDxMaxDev) THEN
1650
RemoveNode(i) = .TRUE.
1651
RemovedOne = .TRUE.
1652
EXIT !go back and recalc
1653
ELSE IF(RemovalDeviation(j) < ThisDzDxMaxDev) THEN
1654
RemoveNode(j) = .TRUE.
1655
RemovedOne = .TRUE.
1656
EXIT !go back and recalc
1657
END IF
1658
ELSE
1659
IF(RemovalDeviation(j) < ThisDzDxMaxDev) THEN
1660
RemoveNode(j) = .TRUE.
1661
RemovedOne = .TRUE.
1662
EXIT !go back and recalc
1663
ELSE IF(RemovalDeviation(i) < ThisDzDxMaxDev) THEN
1664
RemoveNode(i) = .TRUE.
1665
RemovedOne = .TRUE.
1666
EXIT !go back and recalc
1667
END IF
1668
END IF
1669
END IF
1670
END IF
1671
END DO
1672
1673
IF(RemovedOne) PRINT *,'Remesh, removing a column to prevent degeneracy'
1674
END DO
1675
END IF !FixDegenerate
1676
1677
IF(COUNT(RemoveNode) > 0) CALL RemoveNodes(FrontNodes, RemoveNode, FrontLineNodeNums)
1678
DEALLOCATE(RemovalDeviation, RemovalDistance, RemoveNode)
1679
1680
END IF
1681
1682
1280 CONTINUE
1683
!------------------------------------------------------------
1684
! Write and generate footprint mesh
1685
!------------------------------------------------------------
1686
! TODO: This only works on a 4-BC mesh...
1687
1688
IF(Boss) THEN
1689
AllFail = .FALSE.
1690
1691
OPEN( UNIT=GeoUnit, FILE=filename, STATUS='UNKNOWN')
1692
1693
WriteNodeCount = SIZE(FrontLineNodeNums) + &
1694
SIZE(LeftLineNodeNums) + &
1695
SIZE(BackLineNodeNums) + &
1696
SIZE(RightLineNodeNums) - 4
1697
1698
!-------------Write Points---------------
1699
ALLOCATE(WritePoints(WriteNodeCount))
1700
counter = 1
1701
DO i=1,4 !cycle boundaries
1702
SELECT CASE(i)
1703
CASE(1) !left
1704
WriteNodes => LeftNodes
1705
NodeNums => LeftLineNodeNums
1706
CASE(2) !back
1707
WriteNodes => BackNodes
1708
NodeNums => BackLineNodeNums
1709
CASE(3) !right
1710
WriteNodes => RightNodes
1711
NodeNums => RightLineNodeNums
1712
CASE(4) !front
1713
WriteNodes => FrontNodes
1714
NodeNums => FrontLineNodeNums
1715
END SELECT
1716
1717
n = WriteNodes % NumberOfNodes
1718
IF(n /= SIZE(NodeNums)) CALL Fatal("CalvingRemesh","Size mismatch in perm size")
1719
1720
!Determine order
1721
!TODO - MMigrate issue here with providing all but last node from each boundary
1722
! however, this isn't the root cause...
1723
IF(i==1) THEN !left edge, find which end neighbours calving front
1724
IF(ANY(FrontLineNodeNums == LeftLineNodeNums(1))) THEN
1725
start=1;fin=n-1;stride=1
1726
next = NodeNums(n)
1727
ELSE IF(ANY(FrontLineNodeNums == LeftLineNodeNums(SIZE(LeftLineNodeNums)))) THEN
1728
start=n;fin=2;stride=-1
1729
next = NodeNums(1)
1730
ELSE
1731
CALL Fatal("CalvingRemesh","Problem joining up a closed loop for footprint mesh.")
1732
END IF
1733
ELSE
1734
IF(NodeNums(1)==next) THEN
1735
start=1;fin=n-1;stride=1
1736
next = NodeNums(n)
1737
ELSE IF(NodeNums(n)==next) THEN
1738
start=n;fin=2;stride=-1
1739
next = NodeNums(1)
1740
ELSE
1741
PRINT *, 'i, NodeNums(1), (n), next: ',i,NodeNums(1), NodeNums(n), next
1742
CALL Fatal("CalvingRemesh","Problem joining up a closed loop for footprint mesh.")
1743
END IF
1744
END IF
1745
1746
DO j=start,fin,stride !cycle nodes except last, these are overlap
1747
WRITE( GeoUnit,'(A,I0,A,ES20.11,A,ES20.11,A,ES20.11,A,ES20.11,A)')&
1748
'Point(',NodeNums(j),') = {',&
1749
WriteNodes % x(j),',',&
1750
WriteNodes % y(j),',',&
1751
0.0,',',& !don't need z coord for footprint
1752
MeshEdgeLC,'};'
1753
WritePoints(counter) = NodeNums(j)
1754
counter = counter + 1
1755
END DO
1756
WRITE(GeoUnit,'(A)') ''
1757
END DO
1758
1759
!---------------Write lines------------------
1760
DO i=1,WriteNodeCount-1
1761
WRITE( GeoUnit,'(A,i0,A,i0,A,i0,A)') 'Line(',WritePoints(i),') = {'&
1762
,WritePoints(i),',',WritePoints(i+1),'};'
1763
END DO
1764
WRITE( GeoUnit,'(A,i0,A,i0,A,i0,A)') 'Line(',WritePoints(WriteNodeCount),') = {',&
1765
WritePoints(WriteNodeCount),',',WritePoints(1),'};'
1766
1767
!------------Write physical lines-------------
1768
!TODO - Adapt this from 4-BC setup. DO WHILE, watch for
1769
! change in BC ID (?) and cycle to next loop
1770
! I think this is the ONLY part of this loop which needs fixing
1771
counter = 1
1772
DO i=1,4 !cycle boundaries
1773
SELECT CASE(i)
1774
CASE(1) !left
1775
NodeNums => LeftLineNodeNums
1776
MaskName = LeftMaskName
1777
CASE(2) !back
1778
NodeNums => BackLineNodeNums
1779
MaskName = InMaskName
1780
CASE(3) !right
1781
NodeNums => RightLineNodeNums
1782
MaskName = RightMaskName
1783
CASE(4) !front
1784
NodeNums => FrontLineNodeNums
1785
MaskName = FrontMaskName
1786
END SELECT
1787
1788
!Find BC number for physical line
1789
DO j=1,Model % NumberOfBCs
1790
Found = ListCheckPresent(Model % BCs(j) % Values,MaskName)
1791
IF(Found) THEN
1792
BList => ListGetIntegerArray( Model % BCs(j) % Values, &
1793
'Target Boundaries', Found )
1794
IF(SIZE(BList)>1) CALL Fatal(SolverName,&
1795
"Could not uniquely determine target BC")
1796
!TODO - data is lost (which target BC?)
1797
!However - can be detected/guessed by inspecting noncontiguity of given BC
1798
MeshBC = BList(1)
1799
EXIT
1800
END IF
1801
END DO
1802
IF(Debug) THEN
1803
PRINT *, 'Debug CalvingRemesh, BC number for ',MaskName,' is: ',MeshBC
1804
END IF
1805
1806
WRITE(GeoUnit,'(A,i0,A)') 'Physical Line(',MeshBC,') = {'
1807
DO j=1,SIZE(NodeNums)-2
1808
WRITE(GeoUnit,'(i0,A)') WritePoints(counter),','
1809
counter = counter + 1
1810
END DO
1811
!Last line
1812
WRITE(GeoUnit,'(i0,A)') WritePoints(counter),'};'
1813
counter = counter + 1
1814
END DO
1815
1816
!--------------Write Line Loop-----------------
1817
WRITE(GeoUnit, '(A)') 'Line Loop(1) = {'
1818
DO i=1,WriteNodeCount-1
1819
WRITE(GeoUnit,'(i0,A)') WritePoints(i),','
1820
END DO
1821
WRITE(GeoUnit,'(i0,A)') WritePoints(WriteNodeCount),'};'
1822
1823
WRITE(GeoUnit,'(A)') 'Plane Surface(1)={1};'
1824
WRITE(GeoUnit,'(A)') 'Physical Surface(1)={1};'
1825
1826
!-------------Write attractor etc--------------
1827
WRITE(GeoUnit,'(A)') 'Field[1] = Distance;'
1828
WRITE(GeoUnit,'(A)') 'Field[1].NNodesByEdge = 100.0;'
1829
WRITE(GeoUnit,'(A)') 'Field[1].NodesList = {'
1830
DO i=1,SIZE(FrontLineNodeNums)-1
1831
WRITE(GeoUnit,'(I0,A)') FrontLineNodeNums(i),','
1832
END DO
1833
WRITE(GeoUnit,'(I0,A)') FrontLineNodeNums(SIZE(FrontLineNodeNums)),'};'
1834
1835
WRITE(GeoUnit, '(A)') 'Field[2] = Threshold;'
1836
WRITE(GeoUnit, '(A)') 'Field[2].IField = 1;'
1837
WRITE(GeoUnit, '(A,F9.1,A)') 'Field[2].LcMin = ',MeshMinLC,';'
1838
WRITE(GeoUnit, '(A,F9.1,A)') 'Field[2].LcMax = ',MeshMaxLC,';'
1839
WRITE(GeoUnit, '(A,F9.1,A)') 'Field[2].DistMin = ',MeshMinDist,';'
1840
WRITE(GeoUnit, '(A,F9.1,A)') 'Field[2].DistMax = ',MeshMaxDist,';'
1841
1842
WRITE(GeoUnit, '(A)') 'Background Field = 2;'
1843
WRITE(GeoUnit, '(A)') 'Mesh.CharacteristicLengthExtendFromBoundary = 0;'
1844
1845
rt = RealTime() - rt0
1846
IF(ParEnv % MyPE == 0) &
1847
PRINT *, 'Remesh, Time taken to write footprint mesh: ', rt
1848
rt0 = RealTime()
1849
1850
!-----------system call gmsh------------------
1851
!'env -i' obscures all environment variables, so gmsh doesn't see any
1852
!MPI stuff and break down.
1853
CALL EXECUTE_COMMAND_LINE( "env -i PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH &
1854
gmsh -2 "// filename//achar(0), .TRUE., ierr, ier)
1855
IF(ierr > 1) THEN
1856
IF(ierr == 127) THEN
1857
CALL Fatal(SolverName, "The 3D Calving implementation depends on GMSH, but this has not been found.")
1858
END IF
1859
WRITE(Message, '(A,i0)') "Error executing gmsh, error code: ",ierr
1860
CALL Fatal(SolverName,Message)
1861
END IF
1862
1863
rt = RealTime() - rt0
1864
IF(ParEnv % MyPE == 0) &
1865
PRINT *, 'Remesh, Time taken to execute gmsh: ', rt
1866
rt0 = RealTime()
1867
1868
END IF
1869
1870
999 CONTINUE
1871
1872
IF(Boss) THEN
1873
!-----------system call ElmerGrid------------------
1874
IF(Parallel) THEN
1875
WRITE(Message,'(A,A,A,i0,A,i0)') "ElmerGrid 14 2 ",TRIM(filename_root),".msh -metis ",&
1876
ParEnv % PEs,' ',MetisMethod
1877
ELSE
1878
WRITE(Message, '(A,A)') "ElmerGrid 14 2 ",TRIM(filename_root)
1879
END IF
1880
1881
CALL EXECUTE_COMMAND_LINE( Message, .TRUE., ierr, ier)
1882
IF(ierr /= 0) THEN
1883
WRITE(Message, '(A,i0)') "Error executing ElmerGrid, error code: ",ierr
1884
CALL Fatal(SolverName,Message)
1885
END IF
1886
1887
rt = RealTime() - rt0
1888
IF(ParEnv % MyPE == 0) &
1889
PRINT *, 'Remesh, Time taken to execute ElmerGrid: ', rt
1890
rt0 = RealTime()
1891
1892
END IF !Boss only
1893
1894
!Ensure all partitions wait until boss has remeshed
1895
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
1896
1897
!------------------------------------------------------------
1898
! Read in our section of footprint mesh
1899
! This is copied from above, and should be modified probably...
1900
!------------------------------------------------------------
1901
1902
MeshName = TRIM(filename_root) !will eventually be set internally, so not important for testing
1903
MeshDir = ""
1904
1905
!Read our section of the mesh
1906
!Note: MeshDir isn't used by LoadMesh2, so keep meshes in WD
1907
FootprintMesh => LoadMesh2( Model, MeshDir, MeshName, .FALSE., &
1908
ParEnv % PEs, ParEnv % MyPE)
1909
FootprintMesh % Name = TRIM(OldMesh % Name)//'_footprint'
1910
FootprintMesh % OutputActive = .TRUE.
1911
FootprintMesh % Changed = .TRUE.
1912
1913
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
1914
1915
rt = RealTime() - rt0
1916
IF(ParEnv % MyPE == 0) &
1917
PRINT *, 'Remesh, Time taken to read new mesh: ', rt
1918
rt0 = RealTime()
1919
1920
!-------------------------------------------------
1921
! Check for 101 elements and remesh if found
1922
!-------------------------------------------------
1923
BadMesh = .FALSE.
1924
DO i=FootprintMesh % NumberOfBulkElements+1, &
1925
FootprintMesh % NumberOfBulkElements + FootprintMesh % NumberOfBoundaryElements
1926
1927
Element => FootprintMesh % Elements(i)
1928
IF(GetElementFamily(Element) == 1) THEN
1929
BadMesh = .TRUE.
1930
PRINT *, 'PE: ', ParEnv % MyPE,' BAD MESH!'
1931
EXIT
1932
END IF
1933
END DO
1934
1935
CALL SParIterAllReduceOR(BadMesh)
1936
1937
1938
rt = RealTime() - rt0
1939
IF(ParEnv % MyPE == 0) &
1940
PRINT *, 'Remesh, Time taken to check bad mesh: ', rt
1941
rt0 = RealTime()
1942
1943
IF(BadMesh .AND. .FALSE.) THEN
1944
CALL ReleaseMesh(FootprintMesh)
1945
IF(Boss) THEN
1946
TriedMetis(MetisMethod+1) = .TRUE.
1947
IF(.NOT. ALL(TriedMetis)) THEN !try another metis algo
1948
DO i=5,1,-1
1949
IF(.NOT. TriedMetis(i)) THEN
1950
MetisMethod = i-1
1951
EXIT
1952
END IF
1953
END DO
1954
WRITE(Message, '(A,i0)' ) "Resulting mesh contained 101 elements, &
1955
&trying again with metis method: ",MetisMethod
1956
CALL Info(SolverName, Message)
1957
1958
WRITE(Message,'(A,A)') "rm -r ",TRIM(filename_root)
1959
1960
CALL EXECUTE_COMMAND_LINE( Message, .FALSE., ierr )
1961
ELSE
1962
TriedMetis = .FALSE.
1963
AllFail = .TRUE.
1964
DEALLOCATE(WritePoints)
1965
MetisMethod = MetisMethodDefault
1966
1967
WRITE(Message, '(A,i0)' ) "All metis algorithms produced orphan nodes, &
1968
&perturbing gmsh parameters."
1969
CALL Info(SolverName, Message)
1970
1971
WRITE(Message,'(A,A,A,A,A,A)') "rm -r ",TRIM(filename)," ",&
1972
TRIM(filename_root),".msh ",TRIM(filename_root)
1973
CALL EXECUTE_COMMAND_LINE( Message, .TRUE., ierr )
1974
1975
MeshMinLC = MeshMinLC * 1.05
1976
MeshMaxLC = MeshMaxLC * 1.05
1977
MeshMinDist = MeshMinDist * 0.95
1978
MeshMaxDist = MeshMaxDist * 1.05
1979
END IF
1980
END IF
1981
1982
CALL MPI_Scatter(AllFail, 1, MPI_LOGICAL, &
1983
AllFailCatch, 1, MPI_LOGICAL,0,ELMER_COMM_WORLD, ierr)
1984
1985
IF(AllFailCatch) THEN
1986
GO TO 1280
1987
ELSE
1988
GO TO 999
1989
END IF
1990
END IF !bad mesh
1991
1992
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
1993
1994
!Check to ensure no nodes are within the region of tangled
1995
!columns - if they are, shift them!
1996
IF(TangleOccurs) THEN
1997
1998
ALLOCATE(FootprintFrontPerm(FootprintMesh % NumberOfNodes))
1999
2000
CALL MakePermUsingMask( Model, Solver, FootprintMesh, FrontMaskName, &
2001
.FALSE., FootprintFrontPerm, dummyint) !<- Number of nodes in this part on calving face
2002
2003
!Broadcast tangled zones to all partitions
2004
DO i=1,TangledGroups
2005
CALL MPI_BCAST( TangledZone(i,:), 2, MPI_DOUBLE_PRECISION, 0, comm, ierr)
2006
IF(Debug) PRINT *,ParEnv % MyPE, ' tangled zone: ',i,TangledZone(i,:)
2007
END DO
2008
2009
DO i=1,FootprintMesh % NumberOfNodes
2010
IF(FootprintFrontPerm(i) <= 0) CYCLE
2011
2012
NodeHolder(1) = FootprintMesh % Nodes % x(i)
2013
NodeHolder(2) = FootprintMesh % Nodes % y(i)
2014
NodeHolder(3) = FootprintMesh % Nodes % z(i)
2015
2016
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
2017
DO j=1,TangledGroups
2018
IF((NodeHolder(2) <= TangledZone(j,2)) .AND. (NodeHolder(2) >= TangledZone(j,1))) THEN
2019
PRINT *,ParEnv % MyPE, 'Shifted node ',i,&
2020
' in footprint mesh because its in the tangled zone: ',NodeHolder(2)
2021
2022
NodeHolder(2) = TangledZone(j,1) - 0.1_dp !TODO: eps param
2023
NodeHolder = MATMUL(UnRotationMatrix, NodeHolder)
2024
FootprintMesh % Nodes % x(i) = NodeHolder(1)
2025
FootprintMesh % Nodes % y(i) = NodeHolder(2)
2026
FootprintMesh % Nodes % z(i) = NodeHolder(3)
2027
END IF
2028
END DO
2029
END DO
2030
2031
DEALLOCATE(FootprintFrontPerm)
2032
END IF
2033
2034
!----------------------------------------------
2035
! Extrude new mesh
2036
!----------------------------------------------
2037
2038
ExtrudeLevels = ListGetInteger(Model % Simulation, "Remesh Extruded Mesh Levels", Found, UnfoundFatal=.TRUE.)
2039
2040
! The dirty ListAdd/ListRemove stuff is due to changed API of the ExtrudedMesh routine.
2041
i = ListGetInteger( Model % Simulation,'Extruded Mesh Layers',Found)
2042
CALL ListAddInteger( Model % Simulation,'Extruded Mesh Layers',ExtrudeLevels-1)
2043
ExtrudedMesh => MeshExtrude(FootprintMesh, Model % Simulation)
2044
IF(i>0) THEN
2045
CALL ListAddInteger( Model % Simulation,'Extruded Mesh Layers',i)
2046
ELSE
2047
CALL ListRemove( Model % Simulation,'Extruded Mesh Layers')
2048
END IF
2049
!----------------------------------------------------
2050
! Interp front position from squished front nodes
2051
! onto freshly extruded footprint mesh
2052
!----------------------------------------------------
2053
2054
!Save Z and set to ZeroOne for all front nodes
2055
ALLOCATE(ActualHeight(FaceNodeCount))
2056
DO i=1, OldMesh % NumberOfNodes
2057
IF(FrontPerm(i) <= 0) CYCLE
2058
ActualHeight(FrontPerm(i)) = OldMesh % Nodes % z(i)
2059
OldMesh % Nodes % z(i) = ZeroOneHeight(FrontPerm(i))
2060
END DO
2061
ALLOCATE(WorkPerm(SIZE(FrontPerm)))
2062
WorkPerm = FrontPerm
2063
2064
CALL VariableRemove(OldMesh % Variables, "ActualHeight", .FALSE.)
2065
CALL VariableAdd(OldMesh % Variables, OldMesh, Solver, "ActualHeight", 1,&
2066
ActualHeight, WorkPerm)
2067
2068
CALL RotateMesh(OldMesh, RotationMatrix)
2069
2070
NULLIFY(WorkReal, WorkPerm)
2071
ALLOCATE(WorkReal(FaceNodeCount), WorkPerm(OldMesh % NumberOfNodes))
2072
WorkPerm = FrontPerm
2073
2074
DO i=1, OldMesh % NumberOfNodes
2075
IF(WorkPerm(i) <= 0) CYCLE
2076
WorkReal(WorkPerm(i)) = OldMesh % Nodes % z(i)
2077
END DO
2078
2079
CALL VariableRemove(OldMesh % Variables, "FrontExtent", .FALSE.)
2080
CALL VariableAdd(OldMesh % Variables, OldMesh, Solver, "FrontExtent", 1,&
2081
WorkReal, WorkPerm)
2082
2083
ALLOCATE(ExtrudedFrontPerm(ExtrudedMesh % NumberOfNodes))
2084
CALL MakePermUsingMask( Model, Solver, ExtrudedMesh, FrontMaskName, &
2085
.FALSE., ExtrudedFrontPerm, dummyint) !<- Number of nodes in this part on calving face
2086
2087
CALL RotateMesh(ExtrudedMesh, RotationMatrix)
2088
2089
n = ExtrudedMesh % NumberOfNodes
2090
NULLIFY(WorkReal, WorkPerm)
2091
ALLOCATE(WorkReal(n), WorkPerm(n))
2092
2093
WorkPerm = ExtrudedFrontPerm
2094
WorkReal = 0.0_dp
2095
2096
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, Solver, "ActualHeight", 1, &
2097
WorkReal, WorkPerm, .FALSE.)
2098
2099
NULLIFY(WorkReal, WorkPerm)
2100
2101
! --- Add front extent (rotated height) var ---
2102
ALLOCATE(WorkReal(n), WorkPerm(n))
2103
2104
WorkPerm = ExtrudedFrontPerm
2105
WorkReal = 0.0_dp
2106
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, Solver, "FrontExtent", 1, &
2107
WorkReal, WorkPerm, .FALSE.)
2108
NULLIFY(WorkReal, WorkPerm)
2109
2110
2111
! --- Add Calving variable so we can determine which nodes on --- !
2112
! --- the new front can be shifted vertically --- !
2113
ALLOCATE(WorkPerm(n))
2114
WorkPerm = ExtrudedFrontPerm
2115
2116
IF(COUNT(WorkPerm > 0) > 0) THEN
2117
ALLOCATE(WorkReal(COUNT(WorkPerm>0)*3))
2118
ELSE
2119
ALLOCATE(WorkReal(SIZE(WorkPerm)*3))
2120
END IF
2121
WorkReal = 0.0_dp
2122
2123
VarName = TRIM(CalvingVarName)
2124
2125
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, Solver, VarName, &
2126
CalvingVar % DOFs, WorkReal, WorkPerm, .FALSE.)
2127
2128
Var => VariableGet(ExtrudedMesh % Variables,VarName,.TRUE.)
2129
ALLOCATE(Var % PrevValues(SIZE(WorkReal),SIZE(CalvingVar % PrevValues, 2)))
2130
Var % PrevValues = 0.0_dp
2131
2132
DO i=1,3 !add each calving DOF
2133
WorkReal2 => WorkReal( i::3 )
2134
WorkName = ComponentName(TRIM(Var % Name),i)
2135
2136
NULLIFY(WorkPerm); ALLOCATE(WorkPerm(SIZE(ExtrudedFrontPerm)))
2137
WorkPerm = ExtrudedFrontPerm
2138
2139
CALL VariableAdd( ExtrudedMesh % Variables, ExtrudedMesh, &
2140
Solver, WorkName, &
2141
1, WorkReal2, WorkPerm, .FALSE.)
2142
2143
WorkVar => VariableGet( ExtrudedMesh % Variables, WorkName, .TRUE. )
2144
IF(.NOT. ASSOCIATED(WorkVar)) CALL Fatal(SolverName, &
2145
"Error allocating calving PrevValues.")
2146
2147
NULLIFY(WorkVar % PrevValues)
2148
WorkVar % PrevValues => Var % PrevValues( i::3, : )
2149
END DO
2150
2151
NULLIFY(WorkReal, WorkPerm)
2152
2153
!Add rotated front height as var to both
2154
!InterpVarToVarReduced
2155
ALLOCATE(InterpDim(1)); InterpDim = (/3/);
2156
CALL ParallelActive(.TRUE.)
2157
2158
!Create an element mask to mask out elements not on the front.
2159
! Find the right BC
2160
DO i=1,Model % NumberOfBCs
2161
ThisBC = ListGetLogical(Model % BCs(i) % Values,FrontMaskName,Found)
2162
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
2163
FrontBCtag = Model % BCs(i) % Tag
2164
EXIT
2165
END DO
2166
2167
! Create the mask
2168
ALLOCATE(OldElemMask(OldMesh % NumberOfBulkElements+&
2169
OldMesh % NumberOfBoundaryElements))
2170
2171
OldElemMask = .TRUE.
2172
DO i=OldMesh % NumberOfBulkElements+1,&
2173
OldMesh % NumberOfBulkElements+OldMesh % NumberOfBoundaryElements
2174
IF(OldMesh % Elements(i) % BoundaryInfo % Constraint /= FrontBCtag) CYCLE !not on front
2175
OldElemMask(i) = .FALSE.
2176
END DO
2177
2178
CALL InterpolateVarToVarReduced(OldMesh, ExtrudedMesh, "ActualHeight", &
2179
InterpDim, UnfoundNodes, OldElemMask=OldElemMask, Variables=OldMesh % Variables, &
2180
GlobalEps=extrude_globaleps, LocalEps=extrude_localeps)
2181
2182
IF(ANY(UnfoundNodes)) THEN
2183
DO i=1, SIZE(UnfoundNodes)
2184
IF(UnfoundNodes(i)) THEN
2185
PRINT *,ParEnv % MyPE,' Did not find point: ', i, ' x:', ExtrudedMesh % Nodes % x(i),&
2186
' y:', ExtrudedMesh % Nodes % y(i),&
2187
' z:', ExtrudedMesh % Nodes % z(i)
2188
CALL InterpolateUnfoundPoint( i, ExtrudedMesh, "ActualHeight", InterpDim,&
2189
Variables=ExtrudedMesh % Variables )
2190
END IF
2191
END DO
2192
CALL Warn(SolverName,"Failed to find all nodes in calving front interp")
2193
END IF
2194
2195
DEALLOCATE(InterpDim, OldElemMask)
2196
2197
Var => VariableGet(ExtrudedMesh % Variables, VarName, .TRUE.)
2198
IF(.NOT. ASSOCIATED(Var)) CALL Fatal(SolverName, &
2199
"Couldn't get calving var on new mesh to determining calving nodes")
2200
2201
ALLOCATE(IsCalvingNode(ExtrudedMesh % NumberOfNodes))
2202
IsCalvingNode = .FALSE.
2203
DO i=1,ExtrudedMesh % NumberOfNodes
2204
IF(Var % Perm(i) <= 0) CYCLE
2205
IF(ANY(Var % Values((Var % Perm(i)*3)-2:(Var % Perm(i)*3)) /= 0.0_dp)) THEN
2206
IsCalvingNode(i) = .TRUE.
2207
END IF
2208
END DO
2209
2210
! ---------------------------------------------------
2211
! Internal Mesh Update New Mesh
2212
! (to ensure proper height interp)
2213
! ---------------------------------------------------
2214
!
2215
! Mesh Update 3 = dist between squished non-vertical
2216
! front and vertical extruded front
2217
! Mesh Update 1,2 = 0
2218
! ---------------------------------------------------
2219
2220
! Find mesh update solver
2221
MuVarName = ListGetString(Params,"Mesh Update Helper Variable",Found, UnfoundFatal=.TRUE.)
2222
2223
DO i=1,Model % NumberOfSolvers
2224
IF(.NOT. ASSOCIATED(Model % Solvers(i) % Variable)) CYCLE
2225
IF(Model % Solvers(i) % Variable % Name == MuVarName) THEN
2226
MUSolver => Model % Solvers(i)
2227
EXIT
2228
END IF
2229
END DO
2230
IF(.NOT. ASSOCIATED(MUSolver)) &
2231
CALL Fatal("Calving Remesh","Couldn't find Remesh Update variable")
2232
2233
!Point mesh update to new mesh
2234
MUSolver % Mesh => ExtrudedMesh
2235
CALL CopyIntrinsicVars(OldMesh, ExtrudedMesh)
2236
2237
!Add mesh velocity variable to extruded mesh (this shouldn't be necessary really...)
2238
n = ExtrudedMesh % NumberOfNodes
2239
NULLIFY(WorkPerm, WorkReal)
2240
ALLOCATE(WorkPerm(n), WorkReal(n*dim))
2241
WorkPerm = [(i,i=1,n)]
2242
WorkReal = 0.0_dp
2243
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, MUSolver, "Mesh Velocity",&
2244
dim, WorkReal, WorkPerm, .FALSE.)
2245
2246
!--------------------------------------------
2247
! Reallocate variable and reconstruct matrix
2248
! based on new mesh (ExtrudedMesh)
2249
!--------------------------------------------
2250
2251
NULLIFY(WorkPerm, WorkReal)
2252
ALLOCATE(WorkPerm(n), WorkReal(n*dim))
2253
WorkPerm = [(i,i=1,n)]
2254
WorkReal = 0.0_dp
2255
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, MUSolver, MuVarName,&
2256
DIM, WorkReal, WorkPerm)
2257
NULLIFY(WorkReal, WorkPerm)
2258
MuSolver % Variable => VariableGet(ExtrudedMesh % Variables, MuVarName, .TRUE.)
2259
2260
!Reconstruct matrix
2261
IF(ASSOCIATED(MUSolver % Matrix)) CALL FreeMatrix(MUSolver % Matrix)
2262
MUSolver % Matrix => CreateMatrix(Model, MUSolver, ExtrudedMesh, &
2263
MUSolver % Variable % Perm, dim, MATRIX_CRS, .TRUE., &
2264
ListGetString(MUSolver % Values, "Equation"))
2265
2266
MUSolver % Matrix % Perm => MUSolver % Variable % Perm
2267
CALL AllocateVector( MUSolver % Matrix % RHS, MUSolver % Matrix % NumberOfRows )
2268
2269
Model % Solver => MUSolver
2270
CALL SetCurrentMesh( Model, ExtrudedMesh)
2271
2272
!------------------------------------------------
2273
! Issue here: 'thin plate' style glacier meshes
2274
! aren't great for mesh update in their long axes.
2275
!
2276
! We want to make it behave better (deformations
2277
! transmitted further upstream). Ideally, specify
2278
! anisotropy in mesh update, but easier to
2279
! temporarily stretch the mesh vertically to improve
2280
! its aspect ratio...
2281
!------------------------------------------------
2282
2283
!Stretch new mesh vertically to induce pseudo-anisotropy
2284
MuStretchZ = ListGetConstReal(Params, "Remesh Vertical Stretch", Found, UnfoundFatal=.TRUE.)
2285
2286
NULLIFY(WorkReal)
2287
ALLOCATE(WorkReal(ExtrudedMesh % NumberOfNodes))
2288
2289
CALL RotateMesh(ExtrudedMesh, UnRotationMatrix)
2290
WorkReal = ExtrudedMesh % Nodes % z !save to avoid FP error
2291
DO i=1, ExtrudedMesh % NumberOfNodes
2292
ExtrudedMesh % Nodes % z(i) = ExtrudedMesh % Nodes % z(i) * MuStretchZ
2293
END DO
2294
CALL RotateMesh(ExtrudedMesh, RotationMatrix)
2295
2296
!---------- Call mesh update solver ------------
2297
CALL SingleSolver( Model, MUSolver, MUSolver % dt, Transient)
2298
2299
!Unstretch the mesh
2300
CALL RotateMesh(ExtrudedMesh, UnRotationMatrix)
2301
ExtrudedMesh % Nodes % Z = WorkReal
2302
CALL RotateMesh(ExtrudedMesh, RotationMatrix)
2303
DEALLOCATE(WorkReal)
2304
2305
!Put things back
2306
CALL SetCurrentMesh( Model, OldMesh)
2307
Model % Solver => Solver
2308
MuSolver % Variable => VariableGet(OldMesh % Variables, MuVarName, .TRUE.)
2309
2310
!Unrotate meshes
2311
CALL RotateMesh(ExtrudedMesh, UnRotationMatrix)
2312
CALL RotateMesh(OldMesh, UnRotationMatrix)
2313
2314
!Put nodes back
2315
DO i=1, OldMesh % NumberOfNodes
2316
IF(FrontPerm(i) <= 0) CYCLE
2317
OldMesh % Nodes % z(i) = ActualHeight(FrontPerm(i))
2318
END DO
2319
2320
!Now that we've set FrontExtent for Mesh Update dirichlet,
2321
! put the nodes back to pre-calving geometry
2322
CALL DisplaceCalvingFront(OldMesh, CalvingVar, -1)
2323
2324
rt = RealTime() - rt0
2325
IF(ParEnv % MyPE == 0) &
2326
PRINT *, 'Remesh, Time taken to Extrude Mesh and do front interp: ', rt
2327
rt0 = RealTime()
2328
2329
!----------------------------------------------
2330
! Interp Top and Bottom Height
2331
!----------------------------------------------
2332
2333
!Get pointer to top and bottom vars in old mesh
2334
TopVarName = "RemeshTopSurf"
2335
BottomVarName = "RemeshBottomSurf"
2336
2337
TopVar => VariableGet(OldMesh % Variables, TopVarName, .TRUE.)
2338
IF(.NOT.ASSOCIATED(TopVar)) CALL Fatal(SolverName, "Couldn't get variable:&
2339
&RemeshTopSurf")
2340
BottomVar => VariableGet(OldMesh % Variables, BottomVarName, .TRUE.)
2341
IF(.NOT.ASSOCIATED(BottomVar)) CALL Fatal(SolverName, "Couldn't get variable:&
2342
&RemeshBottomSurf")
2343
2344
!When the model initialises, these two exported variables
2345
!share a perm with other vars, so we don't want to deallocate
2346
!However, once variables are copied to a new mesh, they have
2347
!their own perm, and so we deallocate it here to avoid memory leak
2348
IF(FirstTime) THEN
2349
NULLIFY(BottomVar % Perm, TopVar % Perm)
2350
ELSE
2351
DEALLOCATE(BottomVar % Perm, TopVar % Perm)
2352
END IF
2353
2354
n = OldMesh % NumberOfNodes
2355
ALLOCATE(BottomVarOldPerm(n), TopVarOldPerm(n))
2356
2357
BottomVar % Perm => BottomVarOldPerm
2358
TopVar % Perm => TopVarOldPerm
2359
2360
!mess with botperm before this point
2361
!and bottomvar % values
2362
BottomVar % Perm = BotPerm
2363
TopVar % Perm = TopPerm
2364
2365
IF(FrontalBecomeBasal) THEN
2366
NextBasalPerm = MAXVAL(BottomVar % Perm)
2367
DO i=1,OldMesh % NumberOfNodes
2368
IF(BottomVar % Perm(i) > 0) CYCLE
2369
IF(.NOT. NewBasalNode(i)) CYCLE
2370
2371
NextBasalPerm = NextBasalPerm + 1
2372
BottomVar % Perm(i) = NextBasalPerm
2373
IF(Debug) PRINT *,ParEnv % MyPE,' debug, adding basalperm to node: ',i
2374
END DO
2375
2376
DEALLOCATE(BottomVar % Values)
2377
ALLOCATE(BottomVar % Values(MAXVAL(BottomVar % Perm)))
2378
END IF
2379
2380
DO i=1,OldMesh % NumberOfNodes
2381
IF(TopVar % Perm(i) > 0) THEN
2382
TopVar % Values(TopVar % Perm(i)) = OldMesh % Nodes % z(i)
2383
END IF
2384
IF(BottomVar % Perm(i) > 0) THEN
2385
BottomVar % Values(BottomVar % Perm(i)) = OldMesh % Nodes % z(i)
2386
END IF
2387
END DO
2388
2389
!Add to ExtrudedMesh
2390
n = ExtrudedMesh % NumberOfNodes
2391
NodesPerLevel = n / ExtrudeLevels
2392
ALLOCATE(TopVarValues(NodesPerLevel),BottomVarValues(NodesPerLevel),TopVarPerm(n),BottomVarPerm(n))
2393
TopVarPerm = 0; BottomVarPerm = 0;
2394
2395
DO i=1,NodesPerLevel
2396
BottomVarPerm(i) = i
2397
TopVarPerm(n - NodesPerLevel + i) = i
2398
END DO
2399
2400
!These variables will be added to ExtrudedMesh (with wrong Perm) by Exported Variable
2401
!in Remesh Mesh Update. So, get rid of them and rewrite
2402
CALL VariableRemove(ExtrudedMesh % Variables, TopVarName, .FALSE.)
2403
CALL VariableRemove(ExtrudedMesh % Variables, BottomVarName, .FALSE.)
2404
2405
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, Solver, TopVarName, 1, &
2406
TopVarValues, TopVarPerm, .TRUE.)
2407
2408
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, Solver, BottomVarName, 1, &
2409
BottomVarValues, BottomVarPerm, .TRUE.)
2410
2411
!If required, add Grounding Line variable to new mesh to be interpolated.
2412
!We do this here, instead of in SwitchMesh, because nodes which will be
2413
!grounded read their z coordinate from the specified bed, rather than the
2414
!old mesh, to avoid problems with GL on the next timestep
2415
IF(DoGL) THEN
2416
ALLOCATE(WorkPerm(ExtrudedMesh % NumberOfNodes), &
2417
WorkReal(COUNT(BottomVarPerm > 0)))
2418
WorkPerm = BottomVarPerm
2419
WorkReal = 0.0_dp
2420
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, Solver, GLVarName, 1, &
2421
WorkReal, WorkPerm, .TRUE.)
2422
NULLIFY(WorkReal, WorkPerm)
2423
NewGLVar => VariableGet(ExtrudedMesh % Variables, GLVarName, .TRUE.)
2424
IF(ASSOCIATED(OldGLVar % PrevValues)) THEN
2425
ALLOCATE(NewGLVar % PrevValues(SIZE(NewGLVar % Values), SIZE(OldGLVar % PrevValues,2)))
2426
END IF
2427
END IF
2428
2429
IF(ASSOCIATED(InterpDim)) DEALLOCATE(InterpDim)
2430
ALLOCATE(InterpDim(1)); InterpDim(1) = 3
2431
2432
CALL InterpolateVarToVarReduced(OldMesh, ExtrudedMesh, TopVarName, InterpDim, UnfoundNodes,&
2433
GlobalEps=extrude_globaleps, LocalEps=extrude_localeps)
2434
2435
IF(ANY(UnfoundNodes)) THEN
2436
DO i=1, SIZE(UnfoundNodes)
2437
IF(UnfoundNodes(i)) THEN
2438
PRINT *,ParEnv % MyPE,' Missing interped point: ', i, &
2439
' x:', ExtrudedMesh % Nodes % x(i),&
2440
' y:', ExtrudedMesh % Nodes % y(i),&
2441
' z:', ExtrudedMesh % Nodes % z(i)
2442
CALL InterpolateUnfoundPoint( i, ExtrudedMesh, TopVarName, InterpDim )
2443
END IF
2444
END DO
2445
WRITE(Message,'(a,i0,a,i0,a)') "Failed to find ",COUNT(UnfoundNodes),' of ',&
2446
SIZE(UnfoundNodes),' nodes on top surface for mesh extrusion.'
2447
CALL Warn(SolverName, Message)
2448
END IF
2449
2450
!Avoid accidentally interpolating any other variables (specifically, ActualHeight in
2451
!cases where a basal element has all 3 nodes on the front)
2452
!So, we temporarily chop OldGLVar out the variable linked list
2453
IF(DoGL) THEN
2454
WorkVar => OldMesh % Variables
2455
IF(ASSOCIATED(WorkVar, OldGLVar)) THEN
2456
OldMesh % Variables => OldMesh % Variables % Next
2457
First = .TRUE.
2458
ELSE
2459
2460
DO WHILE(ASSOCIATED(WorkVar))
2461
IF(ASSOCIATED(WorkVar % Next, OldGLVar)) EXIT
2462
WorkVar => WorkVar % Next
2463
END DO
2464
2465
WorkVar % Next => OldGLVar % Next
2466
First = .FALSE.
2467
END IF
2468
NULLIFY(OldGLVar % Next)
2469
END IF
2470
2471
CALL InterpolateVarToVarReduced(OldMesh, ExtrudedMesh, BottomVarName, InterpDim, UnfoundNodes,&
2472
Variables=OldGLVar, GlobalEps=extrude_globaleps, LocalEps=extrude_localeps)
2473
2474
!Put GL var back in the linked list
2475
IF(DoGL) THEN
2476
IF(First) THEN
2477
OldGLVar % Next => OldMesh % Variables
2478
OldMesh % Variables => OldGLVar
2479
ELSE
2480
OldGLVar % Next => WorkVar % Next
2481
WorkVar % Next => OldGLVar
2482
END IF
2483
END IF
2484
2485
IF(ANY(UnfoundNodes)) THEN
2486
DO i=1, SIZE(UnfoundNodes)
2487
IF(UnfoundNodes(i)) THEN
2488
PRINT *,ParEnv % MyPE,'Did not find point: ', i,&
2489
' frontperm: ',ExtrudedFrontPerm(i),&
2490
' x:', ExtrudedMesh % Nodes % x(i),&
2491
' y:', ExtrudedMesh % Nodes % y(i),&
2492
' z:', ExtrudedMesh % Nodes % z(i)
2493
CALL InterpolateUnfoundPoint( i, ExtrudedMesh, BottomVarName, &
2494
InterpDim, Variables=NewGLVar )
2495
END IF
2496
END DO
2497
WRITE(Message,'(a,i0,a,i0,a)') "Failed to find ",COUNT(UnfoundNodes),' of ',&
2498
SIZE(UnfoundNodes),' nodes on bottom surface for mesh extrusion.'
2499
CALL Warn(SolverName, Message)
2500
END IF
2501
2502
TopVar => NULL(); BottomVar => NULL()
2503
TopVar => VariableGet(ExtrudedMesh % Variables, TopVarName, .TRUE.)
2504
IF(.NOT. ASSOCIATED(TopVar)) CALL Fatal(SolverName, &
2505
"Couldn't find top surface variable on extruded mesh.")
2506
BottomVar => VariableGet(ExtrudedMesh % Variables,BottomVarName, .TRUE.)
2507
IF(.NOT. ASSOCIATED(BottomVar)) CALL Fatal(SolverName, &
2508
"Couldn't find bottom surface variable on extruded mesh.")
2509
2510
!Grounded nodes get BottomVar (i.e. nodes % z) from the bed function
2511
IF(DoGL) THEN
2512
NewGLVar => VariableGet(ExtrudedMesh % Variables, GLVarName, .TRUE.)
2513
IF(.NOT. ASSOCIATED(NewGLVar)) CALL Fatal(SolverName,&
2514
"Trying to account for the grounding line, but can't find GL var on new mesh.")
2515
2516
ALLOCATE(BedHeight(ExtrudedMesh % NumberOfNodes))
2517
BedHeight = 0.0_dp
2518
Material => GetMaterial(ExtrudedMesh % Elements(1)) !TODO, this is not generalised
2519
2520
CALL SetCurrentMesh( Model, ExtrudedMesh)
2521
2522
DO i=ExtrudedMesh % NumberOfBulkElements+1, &
2523
ExtrudedMesh % NumberOfBulkElements+ExtrudedMesh % NumberOfBoundaryElements
2524
2525
Element => ExtrudedMesh % Elements(i)
2526
n = Element % TYPE % NumberOfNodes
2527
2528
IF(ANY(BottomVarPerm(Element % NodeIndexes) <= 0)) CYCLE
2529
2530
BedHeight(Element % Nodeindexes(1:N)) = &
2531
ListGetReal(Material,'Min Zs Bottom',n,Element % NodeIndexes, Found, UnfoundFatal=.TRUE.)
2532
2533
END DO
2534
2535
CALL SetCurrentMesh( Model, OldMesh)
2536
2537
PRINT *, ParEnv % MyPE, ' Remesh, max/min bedheight: ', MAXVAL(BedHeight), MINVAL(BedHeight)
2538
2539
DO i=1,ExtrudedMesh % NumberOfNodes
2540
IF(NewGLVar % Perm(i) <= 0) CYCLE
2541
IF(NewGLVar % Values(NewGLVar % Perm(i)) < -0.5) CYCLE !floating, so interped bed is fine
2542
BottomVar % Values(BottomVar % Perm(i)) = BedHeight(i)
2543
END DO
2544
END IF
2545
2546
rt = RealTime() - rt0
2547
IF(ParEnv % MyPE == 0) &
2548
PRINT *, 'Remesh, Time taken to interp top and bottom: ', rt
2549
rt0 = RealTime()
2550
2551
!--------------------------------------------------------------------------
2552
! Adjust 'ActualHeight' where the removal of nodes on the front has resulted
2553
! in a discrepancy between 'ActualHeight' and 'Top (or Bottom) Var'
2554
! Three possible cases:
2555
! 1) Full depth calving - vertical shift constrained by top and bottom node
2556
! 2) Partial calving reaching top or bottom -
2557
! vertical shift constrained by 1 of top and bottom, and
2558
! the node below/above the last calving node
2559
! 3) Partial calving reaches neither top nor bottom - ignore, doesn't matter
2560
!--------------------------------------------------------------------------
2561
2562
HeightVar => VariableGet(ExtrudedMesh % Variables, "ActualHeight", .TRUE.)
2563
IF(.NOT.ASSOCIATED(HeightVar)) &
2564
CALL Fatal(SolverName, "Unable to get ActualHeight var from new mesh")
2565
2566
DEALLOCATE(FNColumns)
2567
ALLOCATE(FNColumns(ExtrudedMesh % NumberOfNodes))
2568
FNColumns = [(i,i=1,ExtrudedMesh % NumberOfNodes)]
2569
!NodesPerLevel is currently for ExtrudedMesh, see above
2570
2571
FNColumns = MOD(FNColumns, NodesPerLevel)
2572
2573
DO WHILE(.TRUE.) !cycle columns
2574
2575
!Find a new column
2576
col = -1
2577
DO j=1, ExtrudedMesh % NumberOfNodes
2578
IF(ExtrudedFrontPerm(j) <= 0) CYCLE
2579
IF(FNColumns(j) /= -1) THEN !not done
2580
col = FNColumns(j)
2581
EXIT
2582
END IF
2583
END DO
2584
IF(col == -1) EXIT !All done
2585
2586
!Gather front nodes in specified column
2587
WorkNodes % NumberOfNodes = COUNT((ExtrudedFrontPerm > 0) .AND. (FNColumns == col))
2588
2589
IF(WorkNodes % NumberOfNodes /= ExtrudedLevels ) THEN
2590
WRITE(Message,'(A,i0,A)') "Error in FNColumns, only found ",&
2591
WorkNodes % NumberOfNodes," nodes in column."
2592
CALL Fatal(SolverName,Message)
2593
END IF
2594
2595
n = WorkNodes % NumberOfNodes
2596
ALLOCATE(WorkNodes % z(n), ColumnPerm(n))
2597
2598
counter = 1
2599
DO j=1, ExtrudedMesh % NumberOfNodes
2600
IF(ExtrudedFrontPerm(j) <= 0) CYCLE
2601
IF(FNColumns(j) == col) THEN
2602
WorkNodes % z(counter) = ExtrudedMesh % Nodes % z(j)
2603
ColumnPerm(counter) = j
2604
counter = counter + 1
2605
END IF
2606
END DO
2607
2608
!Order by ascending WorkNodes % z
2609
ALLOCATE(OrderPerm(n))
2610
OrderPerm = [(i,i=1,n)]
2611
2612
CALL SortD( n, WorkNodes % z, OrderPerm )
2613
2614
start = 1
2615
DO WHILE(.TRUE.)
2616
2617
!The difference between front interpolated and top interpolated height
2618
! at the top and bottom of the column - set these inside this loop because
2619
! they'll be modified in some scenarios
2620
BotDisplacement = BottomVar % Values(BottomVar % Perm(ColumnPerm(OrderPerm(1)))) - &
2621
HeightVar % Values(HeightVar % Perm(ColumnPerm(OrderPerm(1))))
2622
2623
TopDisplacement = TopVar % Values(TopVar % Perm(ColumnPerm(OrderPerm(n)))) - &
2624
HeightVar % Values(HeightVar % Perm(ColumnPerm(OrderPerm(n))))
2625
2626
InGroup = .FALSE.
2627
GroupCount = 0
2628
GroupEnd = 0
2629
2630
!Cycle nodes upwards
2631
DO k=start,n
2632
2633
!Add node to group
2634
IF(IsCalvingNode(ColumnPerm(OrderPerm(k)))) THEN
2635
2636
IF(.NOT. InGroup) GroupStart = k
2637
InGroup = .TRUE.
2638
2639
GroupEnd = k
2640
GroupCount = GroupCount + 1
2641
2642
!top node, forces empty DO loop next time, InGroup = .FALSE.
2643
IF(k == n) start = k + 1
2644
2645
!Not in group
2646
ELSE
2647
IF(InGroup) THEN
2648
start = k + 1
2649
EXIT
2650
END IF
2651
END IF
2652
2653
END DO
2654
2655
IF(.NOT. InGroup) EXIT !didn't find any more calving nodes this round, done
2656
2657
!Which case?:
2658
IF((GroupStart==1) .AND. (GroupEnd==n)) THEN
2659
!Top and BotDisplacement are already set above, good to go...
2660
CONTINUE
2661
ELSE IF(GroupStart==1) THEN
2662
TopDisplacement = 0.0_dp
2663
ELSE IF(GroupEnd==n) THEN
2664
BotDisplacement = 0.0_dp
2665
ELSE !do nothing
2666
CYCLE
2667
END IF
2668
2669
DO k=GroupStart, GroupEnd
2670
IF(GroupCount > 1) THEN
2671
!How far through the column are we? Drowning in permutations...
2672
prop = (HeightVar % Values(HeightVar % Perm(ColumnPerm(OrderPerm(k)))) - &
2673
HeightVar % Values(HeightVar % Perm(ColumnPerm(OrderPerm(GroupStart))))) / &
2674
(HeightVar % Values(HeightVar % Perm(ColumnPerm(OrderPerm(GroupEnd)))) - &
2675
HeightVar % Values(HeightVar % Perm(ColumnPerm(OrderPerm(GroupStart)))))
2676
ELSE
2677
!Special case - only top or bottom node moves
2678
IF(GroupStart == 1) THEN
2679
prop = 0.0_dp
2680
ELSE
2681
prop = 1.0_dp
2682
END IF
2683
END IF
2684
2685
Displacement = (prop * TopDisplacement) + ((1 - prop) * BotDisplacement)
2686
2687
!Displace this node's heightvar
2688
HeightVar % Values(HeightVar % Perm(ColumnPerm(OrderPerm(k)))) = &
2689
HeightVar % Values(HeightVar % Perm(ColumnPerm(OrderPerm(k)))) + &
2690
Displacement
2691
END DO
2692
END DO
2693
2694
FNColumns(ColumnPerm) = -1 !mark column nodes as done already
2695
2696
DEALLOCATE(WorkNodes % z, ColumnPerm, OrderPerm)
2697
END DO
2698
DEALLOCATE(FNColumns)
2699
2700
!------------------------------------------------
2701
!
2702
! Height Solver
2703
!
2704
!------------------------------------------------
2705
2706
n = ExtrudedMesh % NumberOfNodes
2707
NULLIFY(WorkPerm, WorkReal)
2708
ALLOCATE(WorkPerm(n), WorkReal(n))
2709
WorkPerm = [(i,i=1,n)]
2710
WorkReal = 0.0_dp
2711
CALL VariableAdd(ExtrudedMesh % Variables, ExtrudedMesh, Solver, &
2712
Solver % Variable % Name, 1, WorkReal, WorkPerm)
2713
2714
Solver % Mesh => ExtrudedMesh
2715
Solver % Variable => VariableGet(ExtrudedMesh % Variables, Solver % Variable % Name, .TRUE.)
2716
2717
!Reconstruct matrix
2718
IF(ASSOCIATED(Solver % Matrix)) CALL FreeMatrix(Solver % Matrix)
2719
Solver % Matrix => CreateMatrix(Model, Solver, ExtrudedMesh, &
2720
Solver % Variable % Perm, 1, MATRIX_CRS, .TRUE., &
2721
ListGetString(Solver % Values, "Equation"))
2722
2723
Solver % Matrix % Perm => Solver % Variable % Perm
2724
CALL AllocateVector( Solver % Matrix % RHS, Solver % Matrix % NumberOfRows )
2725
2726
Solver % Matrix % RHS = 0.0_dp
2727
Solver % Matrix % RHS_im => NULL()
2728
2729
ALLOCATE(Solver % Matrix % Force(Solver % Matrix % NumberOfRows, Solver % TimeOrder+1))
2730
Solver % Matrix % Force = 0.0_dp
2731
2732
ParEnv % ActiveComm = Solver % Matrix % Comm
2733
2734
n = ExtrudedMesh % MaxElementNodes
2735
ALLOCATE( FORCE(n), STIFF(n,n))
2736
2737
Solver % NumberOfActiveElements = 0
2738
DEALLOCATE(Solver % ActiveElements)
2739
ALLOCATE(Solver % ActiveElements(Solver % Mesh % NumberOfBulkElements + &
2740
Solver % Mesh % NumberOfBoundaryElements))
2741
2742
DO i=1,Solver % Mesh % NumberOfBulkElements+Solver % Mesh % NumberOFBoundaryElements
2743
CurrentElement => Solver % Mesh % Elements(i)
2744
IF( CurrentElement % PartIndex /= ParEnv % myPE ) CYCLE
2745
2746
IF ( CheckElementEquation( Model, CurrentElement, &
2747
ListGetString(Solver % Values, "Equation")) ) THEN
2748
Solver % NumberOfActiveElements = Solver % NumberOFActiveElements + 1
2749
Solver % ActiveElements( Solver % NumberOFActiveElements ) = i
2750
END IF
2751
2752
END DO
2753
2754
CALL DefaultInitialize()
2755
2756
active = GetNOFActive()
2757
DO i=1,active
2758
Element => GetActiveElement(i)
2759
n = GetElementNOFNodes(Element)
2760
CALL LocalMatrix( STIFF, FORCE, Element, n )
2761
CALL DefaultUpdateEquations( STIFF, FORCE, Element )
2762
END DO
2763
2764
CALL DefaultFinishBulkAssembly()
2765
CALL DefaultFinishAssembly()
2766
2767
StiffMatrix => Solver % Matrix
2768
ForceVector => StiffMatrix % RHS
2769
2770
!-----------------------------------------
2771
! Set dirichlet points for height solution
2772
! 1) Top and Bottom from respective vars
2773
! 2) Calving front from interpolated "ActualHeight"
2774
!-----------------------------------------
2775
!TODO: Possibility to remove other dirichlets from SIF and implement them like this:
2776
2777
DO i=1, ExtrudedMesh % NumberOfNodes
2778
IF(HeightVar % Perm(i)>0) THEN
2779
CALL SetDirichtletPoint( StiffMatrix, ForceVector,1,1, &
2780
Solver % Variable % Perm, i, HeightVar % Values(HeightVar % Perm(i)))
2781
ELSE IF(TopVar % Perm(i) > 0) THEN
2782
CALL SetDirichtletPoint( StiffMatrix, ForceVector,1,1, &
2783
Solver % Variable % Perm, i, TopVar % Values(TopVar % Perm(i)))
2784
ELSE IF(BottomVar % Perm(i) > 0) THEN
2785
CALL SetDirichtletPoint( StiffMatrix, ForceVector,1,1, &
2786
Solver % Variable % Perm, i, BottomVar % Values(BottomVar % Perm(i)))
2787
END IF
2788
END DO
2789
2790
Norm = DefaultSolve()
2791
2792
DO i=1,ExtrudedMesh % NumberOfNodes
2793
ExtrudedMesh % Nodes % z(i) = Solver % Variable % Values(Solver % Variable % Perm(i))
2794
END DO
2795
2796
!Put var back to avoid screwing up SwitchMesh
2797
Solver % Variable => VariableGet(OldMesh % Variables, Solver % Variable % Name, .TRUE.)
2798
2799
NewMesh => ExtrudedMesh
2800
2801
rt = RealTime() - rt0
2802
IF(ParEnv % MyPE == 0) &
2803
PRINT *, 'Remesh, Time taken to adjust front nodes and HeightSolver: ', rt
2804
rt0 = RealTime()
2805
2806
IF(.FALSE.) THEN
2807
!------------------------------------------------------
2808
! The idea here is to identify degenerate elements in the new mesh. If found,
2809
! go back to the start of the subroutine, but using the NewMesh as the OldMesh,
2810
! and generating a new one. This ISN'T properly implemented - I gave up on it for
2811
! now because it's a very rare issue and I have stuff to be getting on with!
2812
!------------------------------------------------------
2813
! If pursuing this idea, it is essential to consider:
2814
! DisplaceMesh by calving
2815
! GL var, etc for interp...
2816
! Var removal/deallocation?
2817
! Other deallocations
2818
!------------------------------------------------------
2819
2820
!Counter to make sure it doesn't keep trying forever...
2821
DegenCount = DegenCount + 1
2822
IF(DegenCount <= 3) THEN
2823
2824
n = NewMesh % NumberOfBulkElements + NewMesh % NumberOfBoundaryElements
2825
ALLOCATE(Basis(NewMesh % MaxElementNodes))
2826
ALLOCATE(Degenerate(n))
2827
Degenerate = .FALSE.
2828
AnyDegenerate = .FALSE.
2829
2830
DO i=1,NewMesh % NumberOfBulkElements + NewMesh % NumberOfBoundaryElements
2831
2832
Element => NewMesh % Elements(i)
2833
CALL GetElementNodes(Nodes, Element)
2834
2835
!Cycle Gauss points, checking for degenerate element.
2836
!Not sure if/why this needs to be done at the integration
2837
!points...
2838
IP = GaussPoints( Element )
2839
DO j=1,IP % n
2840
IF(.NOT. ElementInfo( Element, Nodes, IP % U(j), IP % V(j), &
2841
IP % W(j), detJ, Basis )) THEN
2842
Degenerate(i) = .TRUE.
2843
EXIT
2844
END IF
2845
END DO
2846
2847
2848
END DO
2849
2850
AnyDegenerate = COUNT(Degenerate) > 0
2851
CALL SParIterAllReduceOR(AnyDegenerate)
2852
2853
IF(AnyDegenerate) THEN
2854
2855
ALLOCATE(MyDegenerateCoords(COUNT(Degenerate),2))
2856
2857
counter = 0
2858
DO i=1,NewMesh % NumberOfBulkElements + NewMesh % NumberOfBoundaryElements
2859
!Cycle nodes, getting range of effect
2860
IF(Degenerate(i)) THEN
2861
2862
counter = counter + 1
2863
MyDegenerateCoords(counter,1) = HUGE(1.0_dp)
2864
MyDegenerateCoords(counter,2) = -HUGE(1.0_dp)
2865
2866
n = Element % TYPE % NumberOfNodes
2867
DO j=1,n
2868
NodeHolder(1) = NewMesh % Nodes % x(Element % NodeIndexes(j))
2869
NodeHolder(2) = NewMesh % Nodes % y(Element % NodeIndexes(j))
2870
NodeHolder(3) = NewMesh % Nodes % z(Element % NodeIndexes(j))
2871
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
2872
2873
PRINT *,ParEnv % MyPE, 'Debug, found degenerate element: ',&
2874
i,' with rotated y: ',NodeHolder(2)
2875
2876
MyDegenerateCoords(counter,1) = MIN(MyDegenerateCoords(counter,1),NodeHolder(2))
2877
MyDegenerateCoords(counter,2) = MAX(MyDegenerateCoords(counter,2),NodeHolder(2))
2878
END DO
2879
END IF
2880
END DO
2881
2882
IF(Parallel) THEN
2883
!MPI Gather count in each part
2884
IF(Boss) ALLOCATE(PartCountDegenerate(ParEnv % PEs))
2885
CALL MPI_GATHER(COUNT(Degenerate),1,MPI_INTEGER,PartCountDegenerate,&
2886
1,MPI_INTEGER, 0, ELMER_COMM_WORLD, ierr)
2887
2888
2889
!MPI GatherV degenerate coords
2890
IF(Boss) THEN
2891
ALLOCATE(DegenerateCoords(SUM(PartCountDegenerate),2))
2892
2893
disps(1) = 0
2894
DO i=2,PEs
2895
disps(i) = disps(i-1) + PartCountDegenerate(i-1)
2896
END DO
2897
END IF
2898
2899
CALL MPI_GATHERV(MyDegenerateCoords(:,1),&
2900
counter,MPI_DOUBLE_PRECISION,&
2901
DegenerateCoords(:,1),PartCountDegenerate,&
2902
disps,MPI_DOUBLE_PRECISION,0,ELMER_COMM_WORLD, ierr)
2903
2904
CALL MPI_GATHERV(MyDegenerateCoords(:,2),&
2905
counter,MPI_DOUBLE_PRECISION,&
2906
DegenerateCoords(:,2),PartCountDegenerate,&
2907
disps,MPI_DOUBLE_PRECISION,0,ELMER_COMM_WORLD, ierr)
2908
ELSE
2909
ALLOCATE(DegenerateCoords(SIZE(MyDegenerateCoords,1),2))
2910
DegenerateCoords = MyDegenerateCoords
2911
ENDIF
2912
2913
IF(Boss) THEN
2914
!Organise the gathered data, including a buffer around either size, probably
2915
ShiftBuffer = 50.0 !50m buffer either side, TODO parameterise
2916
2917
!Remove columns from line etc
2918
END IF
2919
END IF
2920
2921
DEALLOCATE(Basis)
2922
2923
IF(AnyDegenerate) THEN
2924
!TODO: Take care of:
2925
! DisplaceMesh by calving
2926
! GL var, etc for interp...
2927
! Var removal/deallocation?
2928
! Other deallocations
2929
2930
DEALLOCATE(MyDegenerateCoords)
2931
2932
IF(Boss) THEN
2933
DEALLOCATE(PartCountDegenerate)
2934
END IF
2935
2936
CALL Warn(SolverName, "Redoing mesh because of degenerate elements.")
2937
GO TO 8989
2938
END IF
2939
2940
ELSE
2941
2942
CALL Warn(SolverName, "Tried to fix mesh degeneracy 3 times and failed, continuing!")
2943
END IF !DegenCount <= 3
2944
2945
END IF !.FALSE. <- not used
2946
2947
!---------------------------------
2948
!
2949
! Deallocations etc
2950
!
2951
!--------------------------------
2952
2953
!Delete unnecessary meshes
2954
CALL ReleaseMesh(FootprintMesh)
2955
DEALLOCATE(FootprintMesh)
2956
2957
!Remove variables from NewMesh
2958
CALL ReleaseVariableList(NewMesh % Variables)
2959
NULLIFY(NewMesh % Variables)
2960
2961
!free the dummy matrix
2962
CALL FreeMatrix(Solver % Matrix)
2963
2964
!---------------------------------------------------------------
2965
2966
2967
DEALLOCATE( TopPerm, BotPerm, FrontPerm, STIFF, FORCE, &
2968
ExtrudedFrontPerm, UnfoundNodes )
2969
2970
DEALLOCATE(MyFaceNodeNums, BedHeight, IsCalvingNode)
2971
IF(TangleOccurs) DEALLOCATE(LocalTangledNode,LocalCalvingVar)
2972
2973
IF(Boss) THEN
2974
! clear up mesh files
2975
! don't think this does anything because system calls above flush
2976
CLOSE(GeoUnit)
2977
IF(MoveMesh) THEN
2978
2979
TimestepVar => VariableGet( OldMesh % Variables, "Timestep", .TRUE. )
2980
2981
!Make the directory
2982
WRITE(MoveMeshFullPath,'(A,A,I4.4)') TRIM(MoveMeshDir), &
2983
TRIM(filename_root),INT(TimestepVar % Values(1))
2984
2985
WRITE(Message,'(A,A)') "mkdir -p ",TRIM(MoveMeshFullPath)
2986
CALL EXECUTE_COMMAND_LINE( Message, .TRUE., ierr, ier )
2987
2988
WRITE(Message,'(A,A,A,A)') "mv ",TRIM(filename_root),"* ",&
2989
TRIM(MoveMeshFullPath)
2990
CALL EXECUTE_COMMAND_LINE( Message, .TRUE., ierr, ier )
2991
2992
ELSE
2993
WRITE(Message,'(A,A,A,A,A,A)') "rm -r ",TRIM(filename)," ",&
2994
TRIM(filename_root),".msh ",TRIM(filename_root)
2995
CALL EXECUTE_COMMAND_LINE( Message, .TRUE., ierr, ier )
2996
END IF
2997
2998
!Deallocations
2999
DEALLOCATE(FaceNodeNums,&
3000
BackNodes % x, BackNodes % y, BackNodes % z,&
3001
LeftNodes % x, LeftNodes % y, LeftNodes % z,&
3002
RightNodes % x, RightNodes % y, RightNodes % z,&
3003
FaceNodesT % x, FaceNodesT % y, FaceNodesT % z,&
3004
FrontNodes % x, FrontNodes % y, FrontNodes % z)
3005
IF(TangleOccurs) THEN
3006
DEALLOCATE(TangledNode,TangledColumn, TangledZone, FrontCalving1Values)
3007
END IF
3008
END IF
3009
3010
!---------------------------------------------------------------
3011
3012
FirstTime = .FALSE.
3013
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr) !Wait for the boss
3014
3015
rt = RealTime() - rt0
3016
IF(ParEnv % MyPE == 0) &
3017
PRINT *, 'Remesh, Time taken to tidy up etc: ', rt
3018
rt0 = RealTime()
3019
3020
END SUBROUTINE CalvingRemesh
3021
3022
! Sets the value of coordinate variables from
3023
! a given mesh.
3024
SUBROUTINE SetCoordVar(Var, Mesh)
3025
IMPLICIT NONE
3026
3027
TYPE(Variable_t) :: Var
3028
TYPE(Mesh_t) :: Mesh
3029
INTEGER :: int
3030
CHARACTER(MAX_NAME_LEN) :: str
3031
str = Var % Name(12:12)
3032
read( str, '(I1)') int
3033
3034
SELECT CASE(int) !1,2,3
3035
CASE(1)
3036
Var % Values = Mesh % Nodes % x
3037
CASE(2)
3038
Var % Values = Mesh % Nodes % y
3039
CASE(3)
3040
Var % Values = Mesh % Nodes % z
3041
CASE DEFAULT
3042
CALL FATAL("Remesh","Problem setting coordinate variable. This shouldn't have happened.")
3043
END SELECT
3044
3045
END SUBROUTINE SetCoordVar
3046
3047
SUBROUTINE DisplaceCalvingFront(Mesh, CalvingVar, Sign)
3048
TYPE(Mesh_t), POINTER :: Mesh
3049
TYPE(Variable_t), POINTER :: CalvingVar
3050
INTEGER :: Sign, k, i
3051
3052
DO i=1,Mesh % NumberOfNodes
3053
k = CalvingVar % Perm(i)
3054
IF(k <= 0) CYCLE
3055
k = k * CalvingVar % DOFs
3056
3057
Mesh % Nodes % x(i) = Mesh % Nodes % x(i) + &
3058
Sign * CalvingVar % Values(k-2)
3059
3060
Mesh % Nodes % y(i) = Mesh % Nodes % y(i) + &
3061
Sign * CalvingVar % Values(k-1)
3062
3063
Mesh % Nodes % z(i) = Mesh % Nodes % z(i) + &
3064
Sign * CalvingVar % Values(k)
3065
END DO
3066
3067
END SUBROUTINE DisplaceCalvingFront
3068
3069
!Constructs the local matrix for the "d2U/dz2 = 0" Equation
3070
!------------------------------------------------------------------------------
3071
SUBROUTINE LocalMatrix( STIFF, FORCE, Element, n )
3072
!------------------------------------------------------------------------------
3073
REAL(KIND=dp) :: STIFF(:,:), FORCE(:)
3074
INTEGER :: n
3075
TYPE(Element_t), POINTER :: Element
3076
!------------------------------------------------------------------------------
3077
REAL(KIND=dp) :: Basis(n),dBasisdx(n,3),DetJ
3078
LOGICAL :: Stat
3079
INTEGER :: t, p, q, dim
3080
TYPE(GaussIntegrationPoints_t) :: IP
3081
3082
TYPE(Nodes_t) :: Nodes
3083
SAVE Nodes
3084
!------------------------------------------------------------------------------
3085
CALL GetElementNodes( Nodes, Element)
3086
3087
FORCE = 0.0_dp
3088
STIFF = 0.0_dp
3089
3090
dim = CoordinateSystemDimension()
3091
3092
!Numerical integration:
3093
!----------------------
3094
IP = GaussPoints( Element )
3095
DO t=1,IP % n
3096
stat = ElementInfo( Element, Nodes, IP % U(t), IP % V(t), &
3097
IP % W(t), detJ, Basis, dBasisdx )
3098
3099
DO p=1,n
3100
DO q=1,n
3101
STIFF(p,q) = STIFF(p,q) + IP % S(t) * detJ * dBasisdx(q,dim)*dBasisdx(p,dim)
3102
END DO
3103
END DO
3104
END DO
3105
!------------------------------------------------------------------------------
3106
END SUBROUTINE LocalMatrix
3107
3108
!Taken from TwoMeshes
3109
!------------------------------------------------------------------------------
3110
SUBROUTINE SetDirichtletPoint( StiffMatrix, ForceVector,DOF, NDOFs, &
3111
Perm, NodeIndex, NodeValue)
3112
!------------------------------------------------------------------------------
3113
3114
IMPLICIT NONE
3115
3116
TYPE(Matrix_t), POINTER :: StiffMatrix
3117
REAL(KIND=dp) :: ForceVector(:), NodeValue
3118
INTEGER :: DOF, NDOFs, Perm(:), NodeIndex
3119
!------------------------------------------------------------------------------
3120
3121
INTEGER :: PermIndex
3122
REAL(KIND=dp) :: s
3123
3124
!------------------------------------------------------------------------------
3125
3126
PermIndex = Perm(NodeIndex)
3127
3128
IF ( PermIndex > 0 ) THEN
3129
PermIndex = NDOFs * (PermIndex-1) + DOF
3130
3131
IF ( StiffMatrix % FORMAT == MATRIX_SBAND ) THEN
3132
CALL SBand_SetDirichlet( StiffMatrix,ForceVector,PermIndex,NodeValue )
3133
ELSE IF ( StiffMatrix % FORMAT == MATRIX_CRS .AND. &
3134
StiffMatrix % Symmetric ) THEN
3135
CALL CRS_SetSymmDirichlet(StiffMatrix,ForceVector,PermIndex,NodeValue)
3136
ELSE
3137
s = StiffMatrix % Values(StiffMatrix % Diag(PermIndex))
3138
ForceVector(PermIndex) = NodeValue * s
3139
CALL ZeroRow( StiffMatrix,PermIndex )
3140
CALL SetMatrixElement( StiffMatrix,PermIndex,PermIndex,1.0d0*s )
3141
END IF
3142
END IF
3143
3144
!------------------------------------------------------------------------------
3145
END SUBROUTINE SetDirichtletPoint
3146
!------------------------------------------------------------------------------
3147
3148
END SUBROUTINE Remesher
3149
3150