Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/elmerice/Solvers/Calving3D.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
!Subroutine for predicting calving in 3D
33
34
SUBROUTINE Find_Calving3D ( Model, Solver, dt, TransientSimulation )
35
36
USE CalvingGeometry
37
USE MainUtils
38
USE InterpVarToVar
39
40
IMPLICIT NONE
41
42
!-----------------------------------------------
43
TYPE(Model_t) :: Model
44
TYPE(Solver_t), TARGET :: Solver
45
REAL(KIND=dp) :: dt
46
LOGICAL :: TransientSimulation
47
!-----------------------------------------------
48
TYPE(ValueList_t), POINTER :: Params
49
TYPE(Variable_t), POINTER :: CalvingVar, &
50
DistVar, CIndexVar, HeightVar, CrevVar, TimestepVar, TimeVar
51
TYPE(Solver_t), POINTER :: PCSolver => NULL(), &
52
VTUOutputSolver => NULL(), IsoSolver => NULL()
53
TYPE(Matrix_t), POINTER :: StiffMatrix
54
TYPE(Mesh_t), POINTER :: Mesh, PlaneMesh, IsoMesh, WorkMesh, WorkMesh2
55
TYPE(Element_t), POINTER :: Element, WorkElements(:)
56
TYPE(Nodes_t), TARGET :: WorkNodes, FaceNodesT, LeftNodes, RightNodes, FrontNodes
57
TYPE(Nodes_t), POINTER :: WriteNodes
58
INTEGER :: i,j,k,n,counter, dim, dummyint, TotalNodes, NoNodes, &
59
comm, ierr, Me, PEs, ExtrudedLevels, FaceNodeCount, start, fin, &
60
stride, MaxNN, Next, NodesPerLevel, LeftTgt, RightTgt, &
61
county, PMeshBCNums(3), DOFs, PathCount, ValidPathCount, active,&
62
WriteNodeCount, MeshBC, GroupCount, GroupStart, GroupEnd, col, &
63
FrontLineCount, ShiftIdx, err
64
INTEGER, PARAMETER :: GeoUnit = 11
65
INTEGER, POINTER :: CalvingPerm(:), TopPerm(:)=>NULL(), BotPerm(:)=>NULL(), &
66
LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), FrontPerm(:)=>NULL(), &
67
PlaneFrontPerm(:)=>NULL(), PlaneLeftPerm(:)=>NULL(), &
68
PlaneRightPerm(:)=>NULL(), NodeNums(:), FrontNodeNums(:), RightNodeNums(:), &
69
LeftNodeNums(:), FaceNodeNums(:)=>NULL(), DistPerm(:), ColumnPerm(:), &
70
CIndexPerm(:), BList(:), WorkPerm(:), InterpDim(:),&
71
OrderPerm(:)
72
INTEGER, ALLOCATABLE :: MyFaceNodeNums(:), PFaceNodeCount(:),&
73
FNColumns(:), disps(:), WritePoints(:), FrontColumnList(:)
74
REAL(KIND=dp) :: FrontOrientation(3), MaxHolder, &
75
RotationMatrix(3,3), UnRotationMatrix(3,3), NodeHolder(3), &
76
MaxMeshDist, MeshEdgeMinLC, MeshEdgeMaxLC, MeshLCMinDist, MeshLCMaxDist,&
77
Projection, CrevasseThreshold, search_eps, Norm, MinCalvingSize,&
78
PauseVolumeThresh, BotZ, TopZ, prop, MaxBergVolume, dy, dz, dzdy, &
79
gradLimit, Displace, y_coord(2), ShiftTo, Time, CrevPenetration, &
80
CalvingLimit, PrevValue,&
81
rt0, rt
82
83
REAL(KIND=dp), POINTER :: DistValues(:), CIndexValues(:), WorkReal(:), &
84
CalvingValues(:), ForceVector(:)
85
REAL(KIND=dp), ALLOCATABLE :: STIFF(:,:), FORCE(:), HeightDirich(:), &
86
Rot_y_coords(:,:), Rot_z_coords(:,:)
87
#ifdef ELMER_BROKEN_MPI_IN_PLACE
88
REAL(KIND=dp) :: buffer
89
#endif
90
CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, DistVarname, &
91
CIndexVarName, filename_root, filename,MaskName,&
92
FrontMaskName,TopMaskName,BotMaskName,LeftMaskName,RightMaskName, &
93
MeshDir, PC_EqName, Iso_EqName, VTUSolverName, NameSuffix,&
94
MoveMeshDir,MoveMeshFullPath
95
LOGICAL :: Found, Parallel, Boss, Debug, FirstTime = .TRUE., CalvingOccurs=.FALSE., &
96
SaveParallelActive, InGroup, PauseSolvers, LeftToRight, MovedOne, ShiftSecond, &
97
MoveMesh=.FALSE., FullThickness
98
LOGICAL, POINTER :: UnfoundNodes(:)=>NULL(), PWorkLogical(:)
99
LOGICAL, ALLOCATABLE :: RemoveNode(:), IMOnFront(:), IMOnSide(:), &
100
DeleteMe(:), IsCalvingNode(:), WorkLogical(:)
101
102
TYPE(CrevassePath_t), POINTER :: CrevassePaths, CurrentPath
103
104
SAVE :: FirstTime, SolverName, Params, Parallel, Boss, dim, Debug, &
105
DistVarName, CIndexVarName, PC_EqName, Iso_EqName, &
106
MinCalvingSize, PauseVolumeThresh, gradLimit, MoveMesh
107
108
!---------------Get Variables and Parameters for Solution-----------
109
110
rt0 = RealTime()
111
112
IF(FirstTime) THEN
113
SolverName = "Find_Calving3D"
114
Params => Solver % Values
115
Parallel = (ParEnv % PEs > 1)
116
Boss = (ParEnv % MyPE == 0) .OR. (.NOT. Parallel)
117
Debug = .FALSE.
118
119
dim = CoordinateSystemDimension()
120
IF(dim /= 3) CALL Fatal(SolverName, "Solver only works in 3D!")
121
122
DistVarName = ListGetString(Params,"Distance Variable Name", Found)
123
IF(.NOT. Found) DistVarName = "Distance"
124
125
CIndexVarName = ListGetString(Params,"CIndex Variable Name", Found)
126
IF(.NOT. Found) CIndexVarName = "CIndex"
127
128
PC_EqName = ListGetString(Params,"Project Calving Equation Name",Found, UnfoundFatal=.TRUE.)
129
Iso_EqName = ListGetString(Params,"Isosurface Equation Name",Found, UnfoundFatal=.TRUE.)
130
131
MinCalvingSize = ListGetConstReal(Params, "Minimum Calving Event Size", Found)
132
IF(.NOT. Found) THEN
133
MinCalvingSize = 0.0_dp
134
CALL Warn(SolverName,"No 'Minimum Calving Event Size' given, simulation may run slowly&
135
& due to remeshing after tiny events!")
136
END IF
137
138
PauseVolumeThresh = ListGetConstReal(Params, "Pause Solvers Minimum Iceberg Volume", Found)
139
IF(.NOT. Found) PauseVolumeThresh = 0.0_dp
140
141
gradLimit = ListGetConstReal(Params, "Front Gradient Limit", Found)
142
IF(.NOT. Found) gradLimit = 100.0_dp
143
144
END IF !FirstTime
145
146
Mesh => Model % Mesh
147
148
!TODO, could take default value
149
MeshEdgeMinLC = ListGetConstReal(Params, "Calving Mesh Min LC",Found, UnfoundFatal=.TRUE.)
150
MeshEdgeMaxLC = ListGetConstReal(Params, "Calving Mesh Max LC",Found, UnfoundFatal=.TRUE.)
151
MeshLCMinDist = ListGetConstReal(Params, "Calving Mesh LC Min Dist",Found, UnfoundFatal=.TRUE.)
152
MeshLCMaxDist = ListGetConstReal(Params, "Calving Mesh LC Max Dist",Found, UnfoundFatal=.TRUE.)
153
MaxMeshDist = ListGetConstReal(Params, "Calving Search Distance",Found, UnfoundFatal=.TRUE.)
154
CrevasseThreshold = ListGetConstReal(Params, "Crevasse Penetration Threshold", Found, UnfoundFatal=.TRUE.)
155
156
CrevPenetration = ListGetConstReal(Params, "Crevasse Penetration",Found, DefValue = 1.0_dp)
157
IF(.NOT. Found) CALL Info(SolverName, "No Crevasse Penetration specified so assuming full thickness")
158
FullThickness = CrevPenetration == 1.0_dp
159
PRINT*, 'CrevPenetration: ', CrevPenetration
160
IF(CrevPenetration > 1 .OR. CrevPenetration < 0) CALL FATAL(SolverName, "Crevasse Penetraion must be between 0-1")
161
162
DistVar => VariableGet(Model % Variables, DistVarName, .TRUE., UnfoundFatal=.TRUE.)
163
DistValues => DistVar % Values
164
DistPerm => DistVar % Perm
165
166
CIndexVar => VariableGet(Model % Variables, CIndexVarName, .TRUE., UnfoundFatal=.TRUE.)
167
CIndexValues => CIndexVar % Values
168
CIndexPerm => CIndexVar % Perm
169
170
!This solver's variable, two dofs, holds x and y pseudo mesh update
171
CalvingVar => VariableGet(Model % Variables,"Calving",.TRUE.)
172
IF(.NOT.ASSOCIATED(CalvingVar)) &
173
CALL Fatal(SolverName, "Can't find exported variable 'Calving'.")
174
IF(CalvingVar % DOFs /= 3) &
175
CALL Fatal(SolverName,"Solver variable has wrong number of DOFs")
176
177
CalvingValues => CalvingVar % Values
178
CalvingPerm => CalvingVar % Perm
179
DOFs = CalvingVar % DOFs
180
181
NoNodes = Mesh % NumberOfNodes
182
ALLOCATE( TopPerm(NoNodes), BotPerm(NoNodes), LeftPerm(NoNodes),&
183
RightPerm(NoNodes), FrontPerm(NoNodes))
184
185
TopMaskName = "Top Surface Mask"
186
BotMaskName = "Bottom Surface Mask"
187
LeftMaskName = "Left Sidewall Mask"
188
RightMaskName = "Right Sidewall Mask"
189
FrontMaskName = "Calving Front Mask"
190
191
!Generate perms to quickly get nodes on each boundary
192
CALL MakePermUsingMask( Model, Solver, Mesh, TopMaskName, &
193
.FALSE., TopPerm, dummyint)
194
CALL MakePermUsingMask( Model, Solver, Mesh, BotMaskName, &
195
.FALSE., BotPerm, dummyint)
196
CALL MakePermUsingMask( Model, Solver, Mesh, LeftMaskName, &
197
.FALSE., LeftPerm, dummyint)
198
CALL MakePermUsingMask( Model, Solver, Mesh, RightMaskName, &
199
.FALSE., RightPerm, dummyint)
200
CALL MakePermUsingMask( Model, Solver, Mesh, FrontMaskName, &
201
.FALSE., FrontPerm, FaceNodeCount)
202
203
!Get the orientation of the calving front, compute rotation matrix
204
FrontOrientation = GetFrontOrientation(Model)
205
RotationMatrix = ComputeRotationMatrix(FrontOrientation)
206
UnRotationMatrix = TRANSPOSE(RotationMatrix)
207
208
NameSuffix = ListGetString(Params, "Calving Append Name", Found, UnfoundFatal=.TRUE.)
209
MoveMeshDir = ListGetString(Params, "Calving Move Mesh Dir", Found)
210
IF(Found) THEN
211
CALL Info(SolverName, "Moving temporary mesh files after done")
212
MoveMesh = .TRUE.
213
END IF
214
215
WRITE(filename_root,'(A,A)') "Calving_temp",TRIM(NameSuffix)
216
217
rt = RealTime() - rt0
218
IF(ParEnv % MyPE == 0) &
219
PRINT *, 'Time taken for variable loading, making perms etc: ', rt
220
rt0 = RealTime()
221
222
!-----------------------------------------------------------
223
! Action!
224
!-----------------------------------------------------------
225
226
!-----------------------------------------------------------
227
!
228
! The CIndex "calving criterion":
229
! At its core, the Nye criterion says, in 2D, crevasses exist
230
! if sigma_xx > 0.
231
! Water filled extension: sigma_xx + water_pressure > 0
232
!
233
! 3D extension: If any ( planar principal stress + water_pressure) > 0, crevasse exists
234
!-----------------------------------------------------------
235
236
!--------------Generate 2D plane mesh-------------------
237
238
!Use GetDomainEdge for Front, Left and Right
239
!Returns full domain edge to Boss partition only
240
CALL GetDomainEdge(Model, Mesh, TopPerm, LeftNodes, &
241
LeftNodeNums, Parallel, LeftMaskName, Simplify=.FALSE.)
242
CALL GetDomainEdge(Model, Mesh, TopPerm, RightNodes, &
243
RightNodeNums, Parallel, RightMaskName, Simplify=.FALSE.)
244
CALL GetDomainEdge(Model, Mesh, TopPerm, FrontNodes, &
245
FrontNodeNums, Parallel, FrontMaskName, Simplify=.FALSE.)
246
247
!Determine whether front columns are arranged
248
!left to right, and reorder if not. useful later...
249
IF(Boss) THEN
250
FrontLineCount = SIZE(FrontNodeNums)
251
252
NodeHolder(1) = FrontNodes % x(1)
253
NodeHolder(2) = FrontNodes % y(1)
254
NodeHolder(3) = FrontNodes % z(1)
255
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
256
y_coord(1) = NodeHolder(2)
257
258
NodeHolder(1) = FrontNodes % x(FrontLineCount)
259
NodeHolder(2) = FrontNodes % y(FrontLineCount)
260
NodeHolder(3) = FrontNodes % z(FrontLineCount)
261
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
262
y_coord(2) = NodeHolder(2)
263
264
LeftToRight = y_coord(2) > y_coord(1)
265
266
IF(.NOT. LeftToRight) THEN
267
IF(Debug) PRINT *,'Debug, switching to LeftToRight'
268
FrontNodeNums = FrontNodeNums(FrontLineCount:1:-1)
269
FrontNodes % x = FrontNodes % x(FrontLineCount:1:-1)
270
FrontNodes % y = FrontNodes % y(FrontLineCount:1:-1)
271
FrontNodes % z = FrontNodes % z(FrontLineCount:1:-1)
272
END IF
273
END IF
274
275
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
276
277
IF(Boss) THEN
278
!Remove Left and Right nodes beyond the calving search distance
279
!NOTE: technically this doesn't guarantee that resulting nodes
280
!will be a contiguous subsection of the original, but it almost
281
!certainly will be the case
282
283
IF(ANY(FrontNodeNums == LeftNodeNums(1))) THEN
284
LeftTgt = 1
285
ELSE
286
LeftTgt = LeftNodes % NumberOfNodes
287
END IF
288
289
IF(ANY(FrontNodeNums == RightNodeNums(1))) THEN
290
RightTgt = 1
291
ELSE
292
RightTgt = RightNodes % NumberOfNodes
293
END IF
294
295
ALLOCATE(RemoveNode(LeftNodes % NumberOfNodes))
296
RemoveNode = .FALSE.
297
DO i=1,LeftNodes % NumberOfNodes
298
IF(NodeDist2D(LeftNodes, LeftTgt, i) > MaxMeshDist) &
299
RemoveNode(i) = .TRUE.
300
END DO
301
CALL RemoveNodes(LeftNodes, RemoveNode, LeftNodeNums)
302
DEALLOCATE(RemoveNode)
303
304
ALLOCATE(RemoveNode(RightNodes % NumberOfNodes))
305
RemoveNode = .FALSE.
306
DO i=1,RightNodes % NumberOfNodes
307
IF(NodeDist2D(RightNodes, RightTgt, i) > MaxMeshDist) &
308
RemoveNode(i) = .TRUE.
309
END DO
310
CALL RemoveNodes(RightNodes, RemoveNode, RightNodeNums)
311
DEALLOCATE(RemoveNode)
312
313
END IF
314
315
!---------------------------------------------
316
!
317
! Get maximum coverage of calving front (i.e. vertical shadow of front)
318
!
319
!---------------------------------------------
320
321
!Cycle all boundary elements in current partition, get calving face nodes
322
ALLOCATE(MyFaceNodeNums(FaceNodeCount))
323
j = 0
324
DO i=1, NoNodes
325
IF(FrontPerm(i) <= 0) CYCLE
326
j = j + 1
327
MyFaceNodeNums(j) = i
328
END DO
329
!Now MyFaceNodeNums is a list of all nodes on the calving boundary.
330
331
!Send calving front nodes to boss partition
332
IF(Parallel) THEN
333
334
Me = ParEnv % MyPe
335
PEs = ParEnv % PEs
336
comm = ELMER_COMM_WORLD
337
338
!Send node COUNT
339
IF(Boss) ALLOCATE(PFaceNodeCount(PEs))
340
341
CALL MPI_GATHER(FaceNodeCount,1,MPI_INTEGER,PFaceNodeCount,&
342
1,MPI_INTEGER, 0, comm, ierr)
343
344
IF(Boss) THEN
345
FaceNodesT % NumberOfNodes = SUM(PFaceNodeCount)
346
n = FaceNodesT % NumberOfNodes
347
ALLOCATE(FaceNodeNums(n),&
348
FaceNodesT % x(n),&
349
FaceNodesT % y(n),&
350
FaceNodesT % z(n),&
351
disps(PEs)) !variable to hold array offset from each proc
352
!work out where in array to put data from each proc
353
!how to deal with zero size?
354
disps(1) = 0
355
DO i=2,PEs
356
disps(i) = disps(i-1) + PFaceNodeCount(i-1)
357
END DO
358
END IF
359
360
!Global NodeNumbers
361
CALL MPI_GATHERV(Mesh % ParallelInfo % GlobalDOFs(MyFaceNodeNums),&
362
FaceNodeCount,MPI_INTEGER,&
363
FaceNodeNums,PFaceNodeCount,&
364
disps,MPI_INTEGER,0,comm, ierr)
365
!X coords
366
CALL MPI_GATHERV(Mesh % Nodes % x(MyFaceNodeNums),&
367
FaceNodeCount,MPI_DOUBLE_PRECISION,&
368
FaceNodesT % x,PFaceNodeCount,&
369
disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
370
!Y coords
371
CALL MPI_GATHERV(Mesh % Nodes % y(MyFaceNodeNums),&
372
FaceNodeCount,MPI_DOUBLE_PRECISION,&
373
FaceNodesT % y,PFaceNodeCount,&
374
disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
375
!Z coords
376
CALL MPI_GATHERV(Mesh % Nodes % z(MyFaceNodeNums),&
377
FaceNodeCount,MPI_DOUBLE_PRECISION,&
378
FaceNodesT % z,PFaceNodeCount,&
379
disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
380
381
IF(Boss) THEN
382
IF(Debug) THEN
383
PRINT *, 'Debug Remesh, pre removal nodes: '
384
DO i=1,FaceNodesT % NumberOfNodes
385
PRINT *, FaceNodesT % x(i),FaceNodesT % y(i),FaceNodesT % z(i)
386
END DO
387
END IF
388
389
!Remove duplicates!!!
390
ALLOCATE(RemoveNode(FaceNodesT % NumberOfNodes))
391
RemoveNode = .FALSE.
392
DO i=2,FaceNodesT % NumberOfNodes
393
IF(ANY(FaceNodeNums(1:i-1) == FaceNodeNums(i))) THEN
394
RemoveNode(i) = .TRUE.
395
END IF
396
END DO
397
398
CALL RemoveNodes(FaceNodesT, RemoveNode, FaceNodeNums)
399
400
IF(Debug) THEN
401
PRINT *, 'Size of FaceNodeNums: ', SIZE(FaceNodeNums)
402
PRINT *, 'Debug Calving3D, post removal nodes: '
403
DO i=1,FaceNodesT % NumberOfNodes
404
PRINT *, FaceNodesT % x(i),FaceNodesT % y(i),FaceNodesT % z(i)
405
END DO
406
END IF
407
END IF
408
409
ELSE !Serial
410
n = FaceNodeCount
411
ALLOCATE(FaceNodeNums(n),&
412
FaceNodesT % x(n),&
413
FaceNodesT % y(n),&
414
FaceNodesT % z(n))
415
416
!This seems a little redundant but it saves
417
!some lines of code later on...
418
FaceNodeNums = MyFaceNodeNums
419
FaceNodesT % x = Mesh % Nodes % x(MyFaceNodeNums)
420
FaceNodesT % y = Mesh % Nodes % y(MyFaceNodeNums)
421
FaceNodesT % z = Mesh % Nodes % z(MyFaceNodeNums)
422
END IF
423
424
!--------------------------------------------------------------------
425
426
!Need global mesh structure info
427
IF(Parallel) THEN
428
!Rather than summing NoNodes from each part, we simply find
429
!the maximum global node number
430
CALL MPI_AllReduce(MAXVAL(Mesh % ParallelInfo % GlobalDOFs), TotalNodes, &
431
1, MPI_INTEGER, MPI_MAX, comm,ierr)
432
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
433
434
ELSE
435
TotalNodes = NoNodes
436
END IF
437
438
ExtrudedLevels = GetInteger(CurrentModel % Simulation,'Extruded Mesh Levels',Found)
439
IF(.NOT. Found) ExtrudedLevels = &
440
GetInteger(CurrentModel % Simulation,'Remesh Extruded Mesh Levels',Found)
441
IF(.NOT. Found) CALL Fatal("Remesh",&
442
"Unable to find 'Extruded Mesh Levels' or 'Remesh Extruded Mesh Levels'")
443
IF(MOD(TotalNodes, ExtrudedLevels) /= 0) &
444
CALL Fatal("Remesh","Total number of nodes isn't divisible by number&
445
&of mesh levels. WHY?")
446
NodesPerLevel = TotalNodes / ExtrudedLevels
447
448
IF(Boss) THEN !Master/Slave problem (or serial)
449
450
IF(Debug) THEN
451
PRINT *, 'Debug remesh, Total nodes: ', TotalNodes
452
PRINT *, 'Debug Calving3D, NodesPerLevel: ', NodesPerLevel
453
END IF
454
455
!note, if ExtrudedLevels is messed with in remeshing, it'll need to be
456
!pushed back to simulation
457
458
!Now columns *should* have the same integer FNColumns
459
!TODO NOTE: Think about how messing with BCs following undercutting affects this
460
ALLOCATE(FNColumns(SIZE(FaceNodeNums)))
461
FNColumns = MOD(FaceNodeNums, NodesPerLevel)
462
463
!---------------------------------------------------------------------
464
! Cycle FrontNodeNums, finding column maximum extent and updating locations
465
!---------------------------------------------------------------------
466
FrontNodes % x = 0
467
FrontNodes % y = 0
468
FrontNodes % z = 0
469
DO i=1,SIZE(FrontNodeNums)
470
j = MOD(FrontNodeNums(i), NodesPerLevel)
471
WorkNodes % NumberOfNodes = COUNT(FNColumns == j)
472
n = WorkNodes % NumberOfNodes
473
IF(Debug) THEN
474
PRINT *, 'Debug Calving3D, number of worknodes: ',n
475
END IF
476
IF(n < 2) CALL Fatal("Calving3D",&
477
"Found fewer than 2 nodes for a column of calving face nodes.")
478
479
ALLOCATE(WorkNodes % x(n),&
480
WorkNodes % y(n),&
481
WorkNodes % z(n))
482
483
counter=1
484
DO k=1,SIZE(FNColumns)
485
IF(FNColumns(k)==j) THEN
486
WorkNodes % x(counter) = FaceNodesT % x(k)
487
WorkNodes % y(counter) = FaceNodesT % y(k)
488
WorkNodes % z(counter) = FaceNodesT % z(k)
489
counter = counter + 1
490
END IF
491
END DO
492
493
!Maximum extent of calving front
494
MaxHolder = -HUGE(MaxHolder)
495
MaxNN = 0
496
DO j=1,n
497
NodeHolder(1) = WorkNodes % x(j)
498
NodeHolder(2) = WorkNodes % y(j)
499
NodeHolder(3) = WorkNodes % z(j)
500
501
Projection = SUM(FrontOrientation*NodeHolder)
502
IF(Projection > MaxHolder) THEN
503
MaxHolder = Projection
504
MaxNN = j
505
END IF
506
END DO
507
508
FrontNodes % x(i) = WorkNodes % x(MaxNN)
509
FrontNodes % y(i) = WorkNodes % y(MaxNN)
510
FrontNodes % z(i) = WorkNodes % z(MaxNN)
511
512
DEALLOCATE(WorkNodes % x, WorkNodes % y, WorkNodes % z)
513
END DO
514
IF(Debug) THEN
515
PRINT *, 'Debug Calving3D, FrontNodes: '
516
DO i=1,FrontNodes % NumberOfNodes
517
PRINT *, 'node: ',i,' x: ',FrontNodes % x(i),' y: ', FrontNodes % y(i)
518
END DO
519
END IF
520
521
DEALLOCATE(FNColumns)
522
END IF !Boss
523
524
rt = RealTime() - rt0
525
IF(ParEnv % MyPE == 0) &
526
PRINT *, 'Time taken up to mesh creation: ', rt
527
rt0 = RealTime()
528
529
IF(Boss) THEN
530
531
!Produce gmsh .geo file
532
WRITE(filename,'(A,A)') TRIM(filename_root), ".geo"
533
534
OPEN( UNIT=GeoUnit, FILE=filename, STATUS='UNKNOWN')
535
536
WriteNodeCount = SIZE(FrontNodeNums) + &
537
SIZE(LeftNodeNums) + &
538
SIZE(RightNodeNums) - 2
539
540
!-------------Write Points---------------
541
ALLOCATE(WritePoints(WriteNodeCount))
542
counter = 1
543
DO i=1,3 !cycle boundaries
544
SELECT CASE(i)
545
CASE(1) !left
546
WriteNodes => LeftNodes
547
NodeNums => LeftNodeNums
548
CASE(2) !front
549
WriteNodes => FrontNodes
550
NodeNums => FrontNodeNums
551
CASE(3) !right
552
WriteNodes => RightNodes
553
NodeNums => RightNodeNums
554
END SELECT
555
556
n = WriteNodes % NumberOfNodes
557
IF(n /= SIZE(NodeNums)) CALL Fatal("Calving3D","Size mismatch in perm size")
558
559
!Determine order
560
IF(i==1) THEN !left edge, find which end neighbours calving front
561
IF(ANY(FrontNodeNums == LeftNodeNums(1))) THEN
562
start=n;fin=2;stride=-1
563
next = NodeNums(1)
564
ELSE IF(ANY(FrontNodeNums == LeftNodeNums(SIZE(LeftNodeNums)))) THEN
565
start=1;fin=n-1;stride=1
566
next = NodeNums(n)
567
ELSE
568
CALL Fatal(SolverName,"Problem joining up a closed loop for footprint mesh.")
569
END IF
570
ELSE
571
IF(NodeNums(1)==next) THEN
572
start=1;fin=n-1;stride=1
573
IF(i==3) fin = n
574
next = NodeNums(n)
575
ELSE IF(NodeNums(n)==next) THEN
576
start=n;fin=2;stride=-1
577
IF(i==3) fin = 1
578
next = NodeNums(1)
579
ELSE
580
PRINT *, 'i, NodeNums(1), (n), next: ',i,NodeNums(1), NodeNums(n), next
581
CALL Fatal(SolverName,"Problem joining up a closed loop for footprint mesh.")
582
END IF
583
END IF
584
585
DO j=start,fin,stride !cycle nodes except last, these are overlap
586
WRITE( GeoUnit,'(A,I0,A,ES20.11,A,ES20.11,A,ES20.11,A,ES20.11,A)')&
587
'Point(',NodeNums(j),') = {',&
588
WriteNodes % x(j),',',&
589
WriteNodes % y(j),',',&
590
0.0,',',& !don't need z coord for footprint
591
MeshEdgeMinLC,'};'
592
WritePoints(counter) = NodeNums(j)
593
counter = counter + 1
594
END DO
595
WRITE(GeoUnit,'(A)') ''
596
END DO
597
598
!---------------Write lines------------------
599
DO i=1,WriteNodeCount-1
600
WRITE( GeoUnit,'(A,i0,A,i0,A,i0,A)') 'Line(',WritePoints(i),') = {'&
601
,WritePoints(i),',',WritePoints(i+1),'};'
602
END DO
603
WRITE( GeoUnit,'(A,i0,A,i0,A,i0,A)') 'Line(',WritePoints(WriteNodeCount),') = {',&
604
WritePoints(WriteNodeCount),',',WritePoints(1),'};'
605
606
!------------Write physical lines-------------
607
counter = 1
608
DO i=1,3 !cycle boundaries
609
SELECT CASE(i)
610
CASE(1) !left
611
NodeNums => LeftNodeNums
612
MaskName = LeftMaskName
613
CASE(2) !front
614
NodeNums => FrontNodeNums
615
MaskName = FrontMaskName
616
CASE(3) !right
617
NodeNums => RightNodeNums
618
MaskName = RightMaskName
619
END SELECT
620
621
622
!Find BC number for physical line
623
DO j=1,Model % NumberOfBCs
624
Found = ListCheckPresent(Model % BCs(j) % Values,MaskName)
625
IF(Found) THEN
626
BList => ListGetIntegerArray( Model % BCs(j) % Values, &
627
'Target Boundaries', Found )
628
IF(SIZE(BList)>1) CALL Fatal(SolverName,&
629
"Could not uniquely determine target BC")
630
MeshBC = BList(1)
631
PMeshBCNums(i) = j !Use this later
632
EXIT
633
END IF
634
END DO
635
IF(Debug) THEN
636
PRINT *, 'Debug Calving3D, BC number for ',TRIM(MaskName),' is: ',MeshBC
637
END IF
638
639
WRITE(GeoUnit,'(A,i0,A)') 'Physical Line(',MeshBC,') = {'
640
DO j=1,SIZE(NodeNums)-2
641
WRITE(GeoUnit,'(i0,A)') WritePoints(counter),','
642
counter = counter + 1
643
END DO
644
!Last line
645
WRITE(GeoUnit,'(i0,A)') WritePoints(counter),'};'
646
counter = counter + 1
647
END DO
648
649
!--------------Write Line Loop-----------------
650
WRITE(GeoUnit, '(A)') 'Line Loop(1) = {'
651
DO i=1,WriteNodeCount-1
652
WRITE(GeoUnit,'(i0,A)') WritePoints(i),','
653
END DO
654
WRITE(GeoUnit,'(i0,A)') WritePoints(WriteNodeCount),'};'
655
656
WRITE(GeoUnit,'(A)') 'Plane Surface(1)={1};'
657
WRITE(GeoUnit,'(A)') 'Physical Surface(3)={1};'
658
!TODO, check existing number of bodies, write next, instead of '3'
659
660
!-------------Write attractor etc--------------
661
WRITE(GeoUnit,'(A)') 'Field[1] = Distance;'
662
WRITE(GeoUnit,'(A)') 'Field[1].NNodesByEdge = 100.0;'
663
WRITE(GeoUnit,'(A)') 'Field[1].NodesList = {'
664
DO i=1,SIZE(FrontNodeNums)-1
665
WRITE(GeoUnit,'(I0,A)') FrontNodeNums(i),','
666
END DO
667
WRITE(GeoUnit,'(I0,A)') FrontNodeNums(SIZE(FrontNodeNums)),'};'
668
669
WRITE(GeoUnit, '(A)') 'Field[2] = Threshold;'
670
WRITE(GeoUnit, '(A)') 'Field[2].IField = 1;'
671
WRITE(GeoUnit, '(A,F9.1,A)') 'Field[2].LcMin = ',MeshEdgeMinLC,';'
672
WRITE(GeoUnit, '(A,F9.1,A)') 'Field[2].LcMax = ',MeshEdgeMaxLC,';'
673
WRITE(GeoUnit, '(A,F9.1,A)') 'Field[2].DistMin = ',MeshLCMinDist,';'
674
WRITE(GeoUnit, '(A,F9.1,A)') 'Field[2].DistMax = ',MeshLCMaxDist,';'
675
676
WRITE(GeoUnit, '(A)') 'Background Field = 2;'
677
WRITE(GeoUnit, '(A)') 'Mesh.CharacteristicLengthExtendFromBoundary = 0;'
678
679
rt = RealTime() - rt0
680
IF(ParEnv % MyPE == 0) &
681
PRINT *, 'Time taken to write mesh file: ', rt
682
rt0 = RealTime()
683
684
!-----------system call gmsh------------------
685
!'env -i' obscures all environment variables, so gmsh doesn't see any
686
!MPI stuff and break down.
687
CALL EXECUTE_COMMAND_LINE( "env -i PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH gmsh -2 &
688
-format msh2 "// filename, .TRUE., ierr, err)
689
690
IF(ierr > 1) THEN
691
IF(ierr == 127) THEN
692
CALL Fatal(SolverName, "The 3D Calving implementation depends on GMSH, but this has not been found.")
693
END IF
694
WRITE(Message, '(A,i0)') "Error executing gmsh, error code: ",ierr
695
CALL Fatal(SolverName,Message)
696
END IF
697
698
rt = RealTime() - rt0
699
IF(ParEnv % MyPE == 0) &
700
PRINT *, 'Time taken to execute gmsh: ', rt
701
rt0 = RealTime()
702
703
!-----------system call ElmerGrid------------------
704
WRITE(Message, '(A,A,A)') "ElmerGrid 14 2 ",TRIM(filename_root),".msh"
705
706
CALL EXECUTE_COMMAND_LINE( Message//achar(0), .TRUE., ierr, err )
707
IF(ierr /= 0) THEN
708
WRITE(Message, '(A,i0)') "Error executing ElmerGrid, error code: ",ierr
709
CALL Fatal(SolverName,Message)
710
END IF
711
712
713
END IF !Boss only
714
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
715
716
rt = RealTime() - rt0
717
IF(ParEnv % MyPE == 0) &
718
PRINT *, 'Time taken to execute ElmerGrid: ', rt
719
rt0 = RealTime()
720
721
!Load the mesh
722
MeshDir = ""
723
724
CurrentModel % DIMENSION = 2
725
PlaneMesh => LoadMesh2( Model, MeshDir, filename_root, .FALSE., 1, 0, LoadOnly=.FALSE.)
726
CurrentModel % DIMENSION = 3
727
728
!Isomesh does dodgy things with edgetables, so best to release it here.
729
CALL ReleaseMeshEdgeTables(PlaneMesh)
730
731
rt = RealTime() - rt0
732
IF(ParEnv % MyPE == 0) &
733
PRINT *, 'Time taken to load mesh: ', rt
734
rt0 = RealTime()
735
736
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
737
738
IF(Boss) THEN
739
! clear up mesh files
740
CLOSE(GeoUnit)
741
IF(MoveMesh) THEN
742
743
!Make the directory
744
TimestepVar => VariableGet( Mesh % Variables, "Timestep", .TRUE. )
745
WRITE(MoveMeshFullPath,'(A,A,I4.4)') TRIM(MoveMeshDir), &
746
TRIM(filename_root),INT(TimestepVar % Values(1))
747
748
WRITE(Message,'(A,A)') "mkdir -p ",TRIM(MoveMeshFullPath)
749
CALL EXECUTE_COMMAND_LINE( Message, .TRUE., ierr, err )
750
751
!Move the files
752
753
WRITE(Message,'(A,A,A,A)') "mv ",TRIM(filename_root),"* ",&
754
TRIM(MoveMeshFullPath)
755
CALL EXECUTE_COMMAND_LINE( Message, .TRUE., ierr , err)
756
757
ELSE
758
WRITE(Message,'(A,A,A,A,A,A)') "rm -r ",TRIM(filename)," ",&
759
TRIM(filename_root),".msh ",TRIM(filename_root)
760
CALL EXECUTE_COMMAND_LINE( Message, .FALSE., ierr, err )
761
END IF
762
END IF
763
764
PlaneMesh % Name = "calving_plane"
765
PlaneMesh % OutputActive = .TRUE.
766
PlaneMesh % Changed = .TRUE.
767
768
WorkMesh => Mesh % Next
769
Mesh % Next => PlaneMesh
770
771
CALL CopyIntrinsicVars(Mesh, PlaneMesh)
772
773
rt = RealTime() - rt0
774
IF(ParEnv % MyPE == 0) &
775
PRINT *, 'Time taken to tidy mesh files etc: ', rt
776
rt0 = RealTime()
777
778
!----------------------------------------------------
779
! Project Calving Solver to get % fractured
780
!----------------------------------------------------
781
782
! Locate ProjectCalving Solver
783
DO i=1,Model % NumberOfSolvers
784
IF(GetString(Model % Solvers(i) % Values, 'Equation') == PC_EqName) THEN
785
PCSolver => Model % Solvers(i)
786
EXIT
787
END IF
788
END DO
789
IF(.NOT. ASSOCIATED(PCSolver)) &
790
CALL Fatal("Calving Remesh","Couldn't find Project Calving Solver")
791
PCSolver % Mesh => PlaneMesh
792
PCSolver % dt = dt
793
PCSolver % NumberOfActiveElements = 0 !forces recomputation of matrix, is this necessary?
794
795
n = PlaneMesh % NumberOfNodes
796
ALLOCATE(WorkPerm(n), WorkReal(n))
797
WorkReal = 0.0_dp
798
WorkPerm = [(i,i=1,n)]
799
800
IF(ASSOCIATED(PCSolver % Matrix)) CALL FreeMatrix(PCSolver % Matrix) !shouldn't be required...
801
PCSolver % Matrix => CreateMatrix(Model, PCSolver, PlaneMesh, &
802
WorkPerm, 1, MATRIX_CRS, .TRUE.)
803
804
!NOTE: ave_cindex is a misnomer. It actually contains the proprtion (0-1) of intact ice...
805
CALL VariableAdd(PlaneMesh % Variables, PlaneMesh, PCSolver, "ave_cindex", &
806
1, WorkReal, WorkPerm)
807
NULLIFY(WorkReal)
808
809
!Surf cindex (surface crevasses reaching waterline)
810
ALLOCATE(WorkReal(n))
811
WorkReal = 0.0_dp
812
CALL VariableAdd(PlaneMesh % Variables, PlaneMesh, PCSolver, "surf_cindex", &
813
1, WorkReal, WorkPerm)
814
NULLIFY(WorkReal)
815
816
!Basal cindex (surface and basal crevasses meeting)
817
ALLOCATE(WorkReal(n))
818
WorkReal = 0.0_dp
819
CALL VariableAdd(PlaneMesh % Variables, PlaneMesh, PCSolver, "basal_cindex", &
820
1, WorkReal, WorkPerm)
821
NULLIFY(WorkReal, WorkPerm)
822
823
CrevVar => VariableGet(PlaneMesh % Variables, "ave_cindex", .TRUE.)
824
PCSolver % Variable => CrevVar
825
PCSolver % Matrix % Perm => CrevVar % Perm
826
827
!----------------------------------------------------
828
! Run Project Calving solver
829
!----------------------------------------------------
830
SaveParallelActive = ParEnv % Active(ParEnv % MyPE+1)
831
CALL ParallelActive(.TRUE.)
832
833
Model % Solver => PCSolver
834
CALL SingleSolver( Model, PCSolver, PCSolver % dt, .FALSE. )
835
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
836
837
rt = RealTime() - rt0
838
IF(ParEnv % MyPE == 0) &
839
PRINT *, 'Time taken up to project cindex into plane: ', rt
840
rt0 = RealTime()
841
842
PlaneMesh % OutputActive = .TRUE.
843
844
Model % Solver => Solver
845
Mesh % Next => WorkMesh !Probably null...
846
847
!--------------------------------------------------------------
848
! Isosurface solver to find cindex 0 contour
849
!--------------------------------------------------------------
850
851
IF(Boss) THEN
852
853
IF(.NOT. FullThickness) THEN
854
! save original ave_cindex
855
n = PlaneMesh % NumberOfNodes
856
ALLOCATE(WorkPerm(n), WorkReal(n))
857
WorkReal = CrevVar % Values(CrevVar % Perm)
858
WorkPerm = [(i,i=1,n)]
859
CALL VariableAdd(PlaneMesh % Variables, PlaneMesh, PCSolver, "ave_cindex_fullthickness", &
860
1, WorkReal, WorkPerm)
861
NULLIFY(WorkReal, WorkPerm)
862
863
CalvingLimit = 1.0_dp - CrevPenetration
864
865
!alter ave_cindix so relflect %crevasses indicated in sif
866
! 0 full
867
DO i=1, n
868
IF(CrevVar % Values(CrevVar % Perm(i)) <= CalvingLimit) THEN
869
CrevVar % Values(CrevVar % Perm(i)) = 0.0_dp
870
ELSE
871
PrevValue = CrevVar % Values(CrevVar % Perm(i))
872
CrevVar % Values(CrevVar % Perm(i)) = PrevValue - CalvingLimit
873
END IF
874
END DO
875
END IF
876
877
!Generate PlaneMesh perms to quickly get nodes on each boundary
878
CALL MakePermUsingMask( Model, Solver, PlaneMesh, FrontMaskName, &
879
.FALSE., PlaneFrontPerm, dummyint, ParallelComm=.FALSE.)
880
CALL MakePermUsingMask( Model, Solver, PlaneMesh, LeftMaskName, &
881
.FALSE., PlaneLeftPerm, dummyint, ParallelComm=.FALSE.)
882
CALL MakePermUsingMask( Model, Solver, PlaneMesh, RightMaskName, &
883
.FALSE., PlaneRightPerm, dummyint, ParallelComm=.FALSE.)
884
885
! Set ave_cindex values to 0.0 on front
886
! In fact, right at the very front, ave_cindex is undefined,
887
! but it ensures that isolines will contact the front
888
DO i=1, PlaneMesh % NumberOfNodes
889
IF(PlaneFrontPerm(i) > 0) CrevVar % Values(CrevVar % Perm(i)) = 0.0_dp
890
END DO
891
892
893
! Locate Isosurface Solver
894
DO i=1,Model % NumberOfSolvers
895
IF(GetString(Model % Solvers(i) % Values, 'Equation') == Iso_EqName) THEN
896
IsoSolver => Model % Solvers(i)
897
EXIT
898
END IF
899
END DO
900
IF(.NOT. ASSOCIATED(IsoSolver)) &
901
CALL Fatal("Calving Remesh","Couldn't find Isosurface Solver")
902
903
IsoSolver % Mesh => PlaneMesh
904
IsoSolver % dt = dt
905
IsoSolver % NumberOfActiveElements = 0 !forces recomputation of matrix, is this necessary?
906
907
PEs = ParEnv % PEs
908
ParEnv % PEs = 1
909
910
Model % Solver => IsoSolver
911
CALL SingleSolver( Model, IsoSolver, IsoSolver % dt, .FALSE. )
912
913
Model % Solver => Solver
914
ParEnv % PEs = PEs
915
916
!Immediately following call to Isosurface solver, the resulting
917
!isoline mesh is the last the list. We want to remove it from the
918
!Model % Mesh linked list and attach it to PlaneMesh
919
WorkMesh => Model % Mesh
920
DO WHILE(ASSOCIATED(WorkMesh % Next))
921
WorkMesh2 => WorkMesh
922
WorkMesh => WorkMesh % Next
923
END DO
924
IsoMesh => WorkMesh
925
WorkMesh2 % Next => NULL() !break the list
926
PlaneMesh % Next => IsoMesh !add to planemesh
927
928
!-------------------------------------------------
929
!
930
! Compare rotated isomesh to current calving front
931
! position to see if and where calving occurs
932
!
933
!-------------------------------------------------
934
935
ALLOCATE(IMOnFront(IsoMesh % NumberOfNodes), &
936
IMOnSide(IsoMesh % NumberOfNodes))
937
IMOnFront = .FALSE.; IMOnSide = .FALSE.
938
search_eps = EPSILON(PlaneMesh % Nodes % x(1))
939
940
DO i=1, IsoMesh % NumberOfNodes
941
Found = .FALSE.
942
DO j=1, PlaneMesh % NumberOfNodes
943
IF(ABS(PlaneMesh % Nodes % x(j) - &
944
IsoMesh % Nodes % x(i)) < search_eps) THEN
945
IF(ABS(PlaneMesh % Nodes % y(j) - &
946
IsoMesh % Nodes % y(i)) < search_eps) THEN
947
Found = .TRUE.
948
EXIT
949
END IF
950
END IF
951
END DO
952
IF(.NOT. Found) THEN
953
WRITE(Message,'(A,i0,A)') "Unable to locate isomesh node ",i," in PlaneMesh."
954
CALL Fatal(SolverName, Message)
955
END IF
956
957
IF(PlaneFrontPerm(j) > 0) IMOnFront(i) = .TRUE.
958
IF((PlaneLeftPerm(j) > 0) .OR. &
959
(PlaneRightPerm(j) > 0)) IMOnSide(i) = .TRUE.
960
END DO
961
962
IF(Debug) THEN
963
PRINT *, 'debug, count IMOnFront: ', COUNT(IMOnFront)
964
PRINT *, 'debug, count IMOnSide: ', COUNT(IMOnSide)
965
PRINT *, 'debug, isomesh bulkelements,', IsoMesh % NumberOfBulkElements
966
PRINT *, 'debug, isomesh boundaryelements,', IsoMesh % NumberOfBoundaryElements
967
PRINT *, 'debug, size isomesh elements: ', SIZE(IsoMesh % Elements)
968
END IF
969
970
!-----------------------------------------------------------------
971
! Cycle elements, deleting any which lie wholly on the front (or side)
972
!-----------------------------------------------------------------
973
974
ALLOCATE(DeleteMe( IsoMesh % NumberOfBulkElements ))
975
DeleteMe = .FALSE.
976
DO i=1, IsoMesh % NumberOfBulkElements
977
Element => IsoMesh % Elements(i)
978
N = Element % TYPE % NumberOfNodes
979
IF(ALL(IMOnFront(Element % NodeIndexes(1:N))) .OR. &
980
ALL(IMOnSide(Element % NodeIndexes(1:N)))) THEN
981
!delete the element, we don't need it
982
DeleteMe(i) = .TRUE.
983
END IF
984
END DO
985
986
PRINT *, 'debug, ', COUNT(DeleteMe), ' elements marked for deletion from IsoMesh.'
987
988
ALLOCATE(WorkElements(COUNT(.NOT. DeleteMe)))
989
WorkElements = PACK(IsoMesh % Elements, (.NOT. DeleteMe))
990
991
PRINT *, 'debug, size of workelements: ', SIZE(WorkElements)
992
993
IF(Debug) THEN
994
DO i=1, SIZE(WorkElements)
995
PRINT *, 'Workelements', i, ' nodeindexes: ', &
996
WorkElements(i) % NodeIndexes
997
END DO
998
END IF
999
1000
DO i=1, IsoMesh % NumberOfBulkElements
1001
IF(DeleteMe(i)) CALL DeallocateElement(IsoMesh % Elements(i))
1002
END DO
1003
1004
DEALLOCATE(DeleteMe)
1005
IF(ASSOCIATED(IsoMesh % Elements)) DEALLOCATE(IsoMesh % Elements)
1006
IsoMesh % Elements => WorkElements
1007
IsoMesh % NumberOfBulkElements = SIZE(WorkElements)
1008
NULLIFY(WorkElements)
1009
1010
!Find chains which make contact with front twice
1011
!===============================================
1012
! NOTES:
1013
! Because intersections lie exactly on nodes, IsoSurface
1014
! creates several nodes per real node, and several 202 elems.
1015
! This probably isn't a problem, but we'll see...
1016
! Also, we have deleted unnecessary elements, but their nodes
1017
! remain, but this also isn't a problem as InterpVarToVar
1018
! cycles elements, not nodes.
1019
1020
CALL FindCrevassePaths(IsoMesh, IMOnFront, CrevassePaths, PathCount)
1021
CALL CheckCrevasseNodes(IsoMesh, CrevassePaths)
1022
CALL ValidateCrevassePaths(IsoMesh, CrevassePaths, FrontOrientation, PathCount)
1023
1024
!Debugging statements
1025
IF(Debug) THEN
1026
PRINT *,'Crevasse Path Count: ', PathCount
1027
CurrentPath => CrevassePaths
1028
DO WHILE(ASSOCIATED(CurrentPath))
1029
PRINT *, 'New Crevasse Path:'
1030
PRINT *, 'Current path elems:', CurrentPath % ElementNumbers
1031
PRINT *, 'Current path nodes:', CurrentPath % NodeNumbers
1032
DO i=1, CurrentPath % NumberOfNodes
1033
PRINT *,'path node: ',i,' nodenum: ',CurrentPath % NodeNumbers(i),&
1034
' x: ',IsoMesh % Nodes % x(CurrentPath % NodeNumbers(i)),&
1035
' y: ',IsoMesh % Nodes % y(CurrentPath % NodeNumbers(i))
1036
END DO
1037
PRINT *,'ID, left, right, extent', CurrentPath % ID, CurrentPath % Left, &
1038
CurrentPath % Right, CurrentPath % Extent
1039
PRINT *, ''
1040
CurrentPath => CurrentPath % Next
1041
END DO
1042
END IF
1043
1044
ALLOCATE(DeleteMe(IsoMesh % NumberOfBulkElements))
1045
DeleteMe = .FALSE.
1046
1047
DO i=1, IsoMesh % NumberOfBulkElements
1048
IF(ElementPathID(CrevassePaths, i) == 0) DeleteMe(i) = .TRUE.
1049
END DO
1050
1051
IF(Debug) THEN
1052
WRITE (Message,'(A,i0,A)') "Deleting ",COUNT(DeleteMe)," elements which &
1053
&don't lie on valid crevasse paths."
1054
CALL Info(SolverName, Message)
1055
END IF
1056
1057
ALLOCATE(WorkElements(COUNT(.NOT. DeleteMe)))
1058
WorkElements = PACK(IsoMesh % Elements, (.NOT. DeleteMe))
1059
1060
DO i=1, IsoMesh % NumberOfBulkElements
1061
IF(DeleteMe(i)) CALL DeallocateElement(IsoMesh % Elements(i))
1062
END DO
1063
1064
DEALLOCATE(DeleteMe)
1065
DEALLOCATE(IsoMesh % Elements)
1066
IsoMesh % Elements => WorkElements
1067
IsoMesh % NumberOfBulkElements = SIZE(WorkElements)
1068
NULLIFY(WorkElements)
1069
1070
!InterpVarToVar only looks at boundary elements. In reality, the
1071
!two are equivalent here because all are 202 elements.
1072
IsoMesh % NumberOfBoundaryElements = IsoMesh % NumberOfBulkElements
1073
IsoMesh % NumberOfBulkElements = 0
1074
IsoMesh % MaxElementNodes = 2
1075
ELSE
1076
!not boss, allocate dummy IsoMesh
1077
!all front partitions will ask root for interp
1078
IsoMesh => AllocateMesh()
1079
ALLOCATE(IsoMesh % Nodes % x(0),&
1080
IsoMesh % Nodes % y(0),&
1081
IsoMesh % Nodes % z(0))
1082
END IF !Boss
1083
1084
CALL RotateMesh(IsoMesh, RotationMatrix)
1085
CALL RotateMesh(Mesh, RotationMatrix)
1086
1087
!----------------------------------------------------
1088
!
1089
! Interpolate rotated height (calving front position)
1090
! from IsoMesh ONTO main mesh
1091
!
1092
!----------------------------------------------------
1093
1094
!First create height var on both IsoMesh and Mesh
1095
ALLOCATE(WorkReal(IsoMesh % NumberOfNodes),&
1096
WorkPerm(IsoMesh % NumberOfNodes))
1097
IF(Boss) WorkReal = IsoMesh % Nodes % z
1098
DO i=1,SIZE(WorkPerm); WorkPerm(i) = i;
1099
END DO
1100
1101
CALL VariableAdd(IsoMesh % Variables, IsoMesh, Solver, &
1102
"CalvingHeight", 1, WorkReal, WorkPerm)
1103
1104
NULLIFY(WorkReal, WorkPerm)
1105
1106
ALLOCATE(WorkReal(COUNT(FrontPerm>0)), WorkPerm(Mesh % NumberOfNodes))
1107
WorkReal = 0.0_dp
1108
WorkPerm = FrontPerm
1109
1110
CALL VariableAdd(Mesh % Variables, Mesh, Solver, &
1111
"CalvingHeight", 1, WorkReal, WorkPerm)
1112
1113
ALLOCATE(InterpDim(2)); InterpDim = (/1,3/);
1114
CALL ParallelActive(.TRUE.)
1115
CALL InterpolateVarToVarReduced(IsoMesh, Mesh, "CalvingHeight", InterpDim, UnfoundNodes)
1116
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
1117
1118
HeightVar => VariableGet(Mesh % Variables, "CalvingHeight", .TRUE.)
1119
1120
ALLOCATE(IsCalvingNode(Mesh % NumberOfNodes))
1121
IsCalvingNode = .FALSE.
1122
CalvingValues = 0.0_dp !initialize
1123
1124
!Account for slight lateral shift in nodes due to fully lagrangian mesh update
1125
!Do columns here, if any column nodes hit calving, set others.
1126
1127
ALLOCATE(FNColumns(Mesh % NumberOfNodes))
1128
FNColumns = MOD(Mesh % ParallelInfo % GlobalDOFs, NodesPerLevel)
1129
1130
DO i=1,Mesh % NumberOfNodes
1131
IF(FrontPerm(i) <= 0) CYCLE
1132
IF(HeightVar % Values(HeightVar % Perm(i)) == 0.0_dp) CYCLE
1133
1134
col = FNColumns(i)
1135
1136
DO j=1,Mesh % NumberOfNodes
1137
IF(FNColumns(j) == col) THEN
1138
IF(HeightVar % Values(HeightVar % Perm(j)) == 0.0_dp .AND. Debug) &
1139
PRINT *, ParEnv % MyPE,' Setting node: ',j,' from ',i,&
1140
' calving: ',HeightVar % Values(HeightVar % Perm(i))
1141
HeightVar % Values(HeightVar % Perm(j)) = HeightVar % Values(HeightVar % Perm(i))
1142
UnfoundNodes(j) = .FALSE.
1143
END IF
1144
END DO
1145
END DO
1146
1147
!---------------------------------------------------
1148
! Back-rotate interpolated calving Z to calving X,Y
1149
! and set calving variable values
1150
!---------------------------------------------------
1151
1152
CalvingOccurs = .FALSE.
1153
1154
DO i=1, Mesh % NumberOfNodes
1155
IF(FrontPerm(i) == 0) CYCLE
1156
IF((LeftPerm(i) > 0) .OR. (RightPerm(i) > 0)) CYCLE !Lateral margins don't move
1157
IF(UnfoundNodes(i)) CYCLE !No calving here
1158
IF(HeightVar % Values(HeightVar % Perm(i)) == 0.0_dp) CYCLE
1159
! ^--- UnfoundNodes should make this redundant:
1160
1161
NodeHolder = 0.0_dp
1162
NodeHolder(3) = HeightVar % Values(HeightVar % Perm(i)) - Mesh % Nodes % z(i)
1163
IF(NodeHolder(3) > 0) CYCLE !Front already behind calving (undercutting)
1164
1165
!at least one node has a significant calving event:
1166
IF(NodeHolder(3) < -MinCalvingSize) CalvingOccurs = .TRUE.
1167
1168
!If none of the above, active calving node
1169
IsCalvingNode(i) = .TRUE.
1170
1171
IF(Debug) PRINT *,'debug, ',i,' nodeholder 3: ', NodeHolder(3)
1172
1173
NodeHolder = MATMUL(UnrotationMatrix, NodeHolder)
1174
1175
CalvingValues(CalvingPerm(i)*DOFs-2) = NodeHolder(1)
1176
CalvingValues(CalvingPerm(i)*DOFs-1) = NodeHolder(2)
1177
1178
IF(Debug) PRINT *,'debug, ',i,' rotated nodeholder: ', NodeHolder
1179
END DO
1180
1181
!Pass CalvingOccurs to all processes, add to simulation
1182
CALL SParIterAllReduceOR(CalvingOccurs)
1183
CALL ListAddLogical( Model % Simulation, 'CalvingOccurs', CalvingOccurs )
1184
1185
IF(CalvingOccurs) THEN
1186
CALL Info( SolverName, 'Calving Event',Level=1)
1187
ELSE
1188
!If only insignificant calving events occur, reset everything
1189
CalvingValues = 0.0_dp
1190
IsCalvingNode = .FALSE.
1191
!TODO, check we can just RETURN now...
1192
END IF
1193
1194
CALL RotateMesh(IsoMesh, UnrotationMatrix)
1195
CALL RotateMesh(Mesh, UnrotationMatrix)
1196
1197
!don't need these anymore
1198
CALL VariableRemove(IsoMesh % Variables, "CalvingHeight")
1199
CALL VariableRemove(Mesh % Variables, "CalvingHeight")
1200
1201
!Revert element counts
1202
IsoMesh % NumberOfBulkElements = IsoMesh % NumberOfBoundaryElements
1203
IsoMesh % NumberOfBoundaryElements = 0
1204
1205
IF(CalvingOccurs) THEN
1206
1207
!==========================================
1208
! Look at front element angles relative to front normal to identify
1209
! high gradient regions where calving var needs to be adjusted to prevent
1210
! progressive unprojectability of 2nd and 2nd last columns when corner
1211
! nodes don't move (and others occasionally)
1212
!==========================================
1213
!
1214
! Slight conflict of method here:
1215
! FrontAdvance3D requires at least epsDist horizontal distance between nodes
1216
! Calving3D (here) imposes a maximum angle between two nodes, relative to front
1217
1218
! Technically its the horizontal distance which causes problems with reduce
1219
! dimension interpolation, but these close nodes are introduced by Remesh
1220
! in response to the increasing parallelness of the nodes w.r.t
1221
! the front direction.
1222
1223
CALL MPI_BCAST(FrontLineCount , 1, MPI_INTEGER, 0, ELMER_COMM_WORLD, ierr)
1224
1225
IF(.NOT. Boss) ALLOCATE(FrontNodeNums(FrontLineCount))
1226
CALL MPI_BCAST(FrontNodeNums , FrontLineCount, MPI_INTEGER, 0, ELMER_COMM_WORLD, ierr)
1227
1228
ALLOCATE(FrontColumnList(FrontLineCount))
1229
FrontColumnList = MOD(FrontNodeNums, NodesPerLevel)
1230
1231
!Compute and communicate column bounding box (rotated y and z coords)
1232
ALLOCATE(Rot_y_coords(FrontLineCount,2),&
1233
Rot_z_coords(FrontLineCount,2))
1234
Rot_y_coords(:,1) = HUGE(0.0_dp)
1235
Rot_y_coords(:,2) = -HUGE(0.0_dp)
1236
Rot_z_coords(:,1) = HUGE(0.0_dp)
1237
Rot_z_coords(:,2) = -HUGE(0.0_dp)
1238
1239
DO i=1,FrontLineCount
1240
col = FrontColumnList(i)
1241
1242
DO j=1,Mesh % NumberOfNodes
1243
IF(FNColumns(j) /= col) CYCLE
1244
1245
NodeHolder(1) = Mesh % Nodes % x(j) + CalvingValues((CalvingPerm(j)-1)*DOFs + 1)
1246
NodeHolder(2) = Mesh % Nodes % y(j) + CalvingValues((CalvingPerm(j)-1)*DOFs + 2)
1247
NodeHolder(3) = Mesh % Nodes % z(j) + CalvingValues((CalvingPerm(j)-1)*DOFs + 3)
1248
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1249
1250
Rot_y_coords(i,1) = MIN(Rot_y_coords(i,1), NodeHolder(2))
1251
Rot_y_coords(i,2) = MAX(Rot_y_coords(i,2), NodeHolder(2))
1252
1253
Rot_z_coords(i,1) = MIN(Rot_z_coords(i,1), NodeHolder(3))
1254
Rot_z_coords(i,2) = MAX(Rot_z_coords(i,2), NodeHolder(3))
1255
END DO
1256
1257
!Pass to other partitions
1258
#ifdef ELMER_BROKEN_MPI_IN_PLACE
1259
buffer = Rot_y_coords(i,1)
1260
CALL MPI_AllReduce(buffer, &
1261
#else
1262
CALL MPI_AllReduce(MPI_IN_PLACE, &
1263
#endif
1264
Rot_y_coords(i,1), 1, MPI_DOUBLE_PRECISION, MPI_MIN, ELMER_COMM_WORLD,ierr)
1265
1266
#ifdef ELMER_BROKEN_MPI_IN_PLACE
1267
buffer = Rot_y_coords(i,2)
1268
CALL MPI_AllReduce(buffer, &
1269
#else
1270
CALL MPI_AllReduce(MPI_IN_PLACE, &
1271
#endif
1272
Rot_y_coords(i,2), 1, MPI_DOUBLE_PRECISION, MPI_MAX, ELMER_COMM_WORLD,ierr)
1273
1274
#ifdef ELMER_BROKEN_MPI_IN_PLACE
1275
buffer = Rot_z_coords(i,1)
1276
CALL MPI_AllReduce(buffer, &
1277
#else
1278
CALL MPI_AllReduce(MPI_IN_PLACE, &
1279
#endif
1280
Rot_z_coords(i,1), 1, MPI_DOUBLE_PRECISION, MPI_MIN, ELMER_COMM_WORLD,ierr)
1281
1282
#ifdef ELMER_BROKEN_MPI_IN_PLACE
1283
buffer = Rot_z_coords(i,2)
1284
CALL MPI_AllReduce(buffer, &
1285
#else
1286
CALL MPI_AllReduce(MPI_IN_PLACE, &
1287
#endif
1288
Rot_z_coords(i,2), 1, MPI_DOUBLE_PRECISION, MPI_MAX, ELMER_COMM_WORLD,ierr)
1289
1290
IF(Boss .AND. Debug) PRINT *,'Debug, rot_y_coords: ',i,rot_y_coords(i,:)
1291
IF(Boss .AND. Debug) PRINT *,'Debug, rot_z_coords: ',i,rot_z_coords(i,:)
1292
END DO
1293
1294
MovedOne = .TRUE.
1295
county = 0
1296
DO WHILE(MovedOne)
1297
county = county + 1
1298
IF(county > 100) CALL Fatal(SolverName, "Infinite loop!")
1299
1300
MovedOne = .FALSE.
1301
1302
DO i=2,FrontLineCount
1303
1304
!dy always positive because Front Line Nodes are always ordered Left to Right
1305
dy = Rot_y_coords(i,1) - Rot_y_coords(i-1,2)
1306
IF(dy < 0.0_dp) CALL Fatal(SolverName, &
1307
"Columns are already unprojectable! Programming error")
1308
1309
dz = MAX((Rot_z_coords(i-1,2) - Rot_z_coords(i,1)), &
1310
(Rot_z_coords(i,2) - Rot_z_coords(i-1,1)))
1311
dzdy = dz/dy
1312
1313
IF(Debug) PRINT *,ParEnv % MyPE,'nodes: ',i-1,i,' dzdy: ',dzdy
1314
IF(dzdy > gradLimit) THEN
1315
1316
IF( (i /= 2) .AND. (i /= FrontLineCount)) CALL Warn(SolverName, &
1317
"Calving leads to high gradient, not at corner. Unexpected!")
1318
1319
IF((Rot_z_coords(i-1,2) - Rot_z_coords(i,1)) < &
1320
(Rot_z_coords(i,2) - Rot_z_coords(i-1,1))) THEN
1321
ShiftSecond = .FALSE.
1322
ShiftIdx = i-1
1323
ELSE
1324
ShiftIdx = i
1325
ShiftSecond = .TRUE.
1326
END IF
1327
1328
ShiftTo = MAXVAL(Rot_z_coords(i-1:i,:)) - gradLimit * dy
1329
PRINT *, TRIM(SolverName),' Debug: limiting gradient, ',i,&
1330
' ShiftSecond: ',ShiftSecond,' shiftto: ',ShiftTo
1331
1332
!Update rot_z_coords for next round
1333
!Note, technically these should be updated below, from the actual
1334
!shifted nodes (as they won't be shifted in case it results in a positive
1335
!value for calving). However, it's a lot of code to correct an almost
1336
!entirely negligible error.
1337
DO j=1,2
1338
Rot_z_coords(ShiftIdx,j) = MAX(Rot_z_coords(ShiftIdx,j),ShiftTo)
1339
END DO
1340
1341
DO j=1,Mesh % NumberOfNodes
1342
IF(FNColumns(j) /= FrontColumnList(ShiftIdx)) CYCLE
1343
1344
!Check the unmodified, calved nodal position
1345
NodeHolder(1) = Mesh % Nodes % x(j) + CalvingValues((CalvingPerm(j)-1)*DOFs + 1)
1346
NodeHolder(2) = Mesh % Nodes % y(j) + CalvingValues((CalvingPerm(j)-1)*DOFs + 2)
1347
NodeHolder(3) = Mesh % Nodes % z(j) + CalvingValues((CalvingPerm(j)-1)*DOFs + 3)
1348
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1349
1350
!Node is already within limit
1351
IF(NodeHolder(3) > ShiftTo) THEN
1352
IF(Debug) PRINT *,TRIM(SolverName),' Debug, node: ',j,&
1353
' Nodeholder (inc. calving): ',NodeHolder
1354
CYCLE
1355
END IF
1356
1357
Displace = ShiftTo - NodeHolder(3)
1358
1359
!Check unrotated calving (i.e. HeightVar) against displacement
1360
NodeHolder(1) = CalvingValues((CalvingPerm(j)-1)*DOFs + 1)
1361
NodeHolder(2) = CalvingValues((CalvingPerm(j)-1)*DOFs + 2)
1362
NodeHolder(3) = CalvingValues((CalvingPerm(j)-1)*DOFs + 3)
1363
NodeHolder = MATMUL(RotationMatrix, NodeHolder)
1364
1365
IF(Debug) PRINT *,TRIM(SolverName),' Debug, node: ',j,' Displace: ',&
1366
Displace,' Nodeholder(3): ',NodeHolder(3),' ShiftTo: ',ShiftTo
1367
1368
!Get the 'displaced' calving value
1369
NodeHolder(1:2) = 0.0_dp
1370
NodeHolder(3) = NodeHolder(3) + Displace
1371
1372
!If positive, set to zero. i.e. displacement isn't permitted
1373
!beyond the level equivalent to zero calving size.
1374
IF(NodeHolder(3) >= 0.0_dp) THEN
1375
NodeHolder(3) = 0.0_dp
1376
ELSE
1377
MovedOne = .TRUE.
1378
END IF
1379
1380
!Rotate the displaced calving value and set
1381
NodeHolder = MATMUL(TRANSPOSE(RotationMatrix), NodeHolder)
1382
1383
CalvingValues((CalvingPerm(j)-1)*DOFs + 1) = NodeHolder(1)
1384
CalvingValues((CalvingPerm(j)-1)*DOFs + 2) = NodeHolder(2)
1385
1386
PRINT *,TRIM(SolverName), ParEnv % MyPE, 'Debug, shifting node ',j,&
1387
' col ',ShiftIdx,'xyz: ',&
1388
Mesh % Nodes % x(j)+CalvingValues((CalvingPerm(j)-1)*DOFs + 1),&
1389
Mesh % Nodes % y(j)+CalvingValues((CalvingPerm(j)-1)*DOFs + 2),&
1390
Mesh % Nodes % z(j)+CalvingValues((CalvingPerm(j)-1)*DOFs + 3),&
1391
' by ',Displace,' to ensure projectability.'
1392
1393
END DO
1394
1395
IF(Parallel) CALL SParIterAllReduceOR(MovedOne)
1396
1397
END IF
1398
END DO
1399
1400
END DO
1401
1402
DEALLOCATE(FNColumns)
1403
1404
1405
!=============================================================
1406
! Solve d2height/dz=0 equation to get change in height after calving
1407
! retreat.
1408
!==============================================================
1409
!
1410
! Cycle nodes on front top, and front bottom, adding to
1411
! temporary WorkMesh, then InterpolateVarToVarReduced twice,
1412
! (top and bottom)
1413
!
1414
! Plug the interpolated values on WorkMesh into
1415
! corresponding dirichlet points
1416
!
1417
!--------------------------------------------------------------
1418
1419
ALLOCATE(PWorkLogical(Mesh % NumberOfNodes))
1420
ALLOCATE(HeightDirich(Mesh % NumberOfNodes))
1421
HeightDirich = 0.0_dp
1422
1423
DO j=1,2 !Top, then bottom interp
1424
1425
!logical mask for top and bottom front nodes
1426
ALLOCATE(WorkLogical(Mesh % NumberOfNodes))
1427
IF(j==1) THEN
1428
WorkLogical = (FrontPerm > 0) .AND. (TopPerm > 0)
1429
ELSE
1430
WorkLogical = (FrontPerm > 0) .AND. (BotPerm > 0)
1431
END IF
1432
1433
WorkMesh => AllocateMesh()
1434
WorkMesh % NumberOfNodes = COUNT(WorkLogical)
1435
ALLOCATE(WorkMesh % Nodes % x(WorkMesh % NumberOfNodes),&
1436
WorkMesh % Nodes % y(WorkMesh % NumberOfNodes),&
1437
WorkMesh % Nodes % z(WorkMesh % NumberOfNodes))
1438
1439
county = 1
1440
DO i=1, Mesh % NumberOfNodes
1441
IF(WorkLogical(i)) THEN
1442
WorkMesh % Nodes % x(county) = Mesh % Nodes % x(i) + CalvingValues(CalvingPerm(i)*DOFs - 2)
1443
WorkMesh % Nodes % y(county) = Mesh % Nodes % y(i) + CalvingValues(CalvingPerm(i)*DOFs - 1)
1444
WorkMesh % Nodes % z(county) = Mesh % Nodes % z(i) !not important
1445
county = county + 1
1446
END IF
1447
END DO
1448
1449
!Add CalvingHeight variable to both meshes
1450
! - this time its the actual Z height, rather than the rotated front height
1451
1452
! Add to WorkMesh
1453
ALLOCATE(WorkReal(WorkMesh % NumberOfNodes), WorkPerm(WorkMesh % NumberOfNodes))
1454
WorkPerm = [(i,i=1,SIZE(WorkPerm))]
1455
WorkReal = 0.0_dp
1456
1457
CALL VariableAdd(WorkMesh % Variables, Mesh, Solver, &
1458
"CalvingHeight", 1, WorkReal, WorkPerm)
1459
NULLIFY(WorkPerm, WorkReal)
1460
1461
! Add to main Mesh
1462
ALLOCATE(WorkReal(Mesh % NumberOfNodes), WorkPerm(Mesh % NumberOfNodes))
1463
WorkPerm = [(i,i=1,SIZE(WorkPerm))]
1464
WorkReal = Mesh % Nodes % z
1465
1466
CALL VariableAdd(Mesh % Variables, Mesh, Solver, &
1467
"CalvingHeight", 1, WorkReal, WorkPerm)
1468
NULLIFY(WorkPerm, WorkReal)
1469
1470
!The logical mask to InterpolateVarToVarReduced is inverse, so any TRUE nodes are ignored
1471
IF(j==1) THEN
1472
PWorkLogical = (TopPerm <= 0)
1473
ELSE
1474
PWorkLogical = (BotPerm <= 0)
1475
END IF
1476
1477
!Interpolate from main mesh to temporary workmesh
1478
DEALLOCATE(InterpDim); ALLOCATE(InterpDim(1)); InterpDim = (/3/);
1479
CALL InterpolateVarToVarReduced(Mesh, WorkMesh, "CalvingHeight", InterpDim, &
1480
UnfoundNodes, OldNodeMask=PWorkLogical)
1481
IF(ANY(UnfoundNodes)) THEN
1482
DO i=1, SIZE(UnfoundNodes)
1483
IF(UnfoundNodes(i)) THEN
1484
PRINT *,'Did not find point: ', i, ' x:', WorkMesh % Nodes % x(i),&
1485
' y:', WorkMesh % Nodes % y(i),&
1486
' z:', WorkMesh % Nodes % z(i)
1487
END IF
1488
END DO
1489
CALL Fatal(SolverName,"Failed to find all nodes interpolating onto temporary front workmesh.")
1490
END IF
1491
1492
!Copy interpolated height to CalvingVar
1493
HeightVar => VariableGet(WorkMesh % Variables, "CalvingHeight", .TRUE.)
1494
1495
county=1
1496
DO i=1, Mesh % NumberOfNodes
1497
IF(WorkLogical(i)) THEN
1498
HeightDirich(i) = &
1499
HeightVar % Values(HeightVar % Perm(county))
1500
county = county + 1
1501
END IF
1502
END DO
1503
1504
DEALLOCATE(WorkLogical)
1505
CALL ReleaseMesh(WorkMesh)
1506
CALL VariableRemove(Mesh % Variables, "CalvingHeight")
1507
END DO !top and bottom interp
1508
1509
DEALLOCATE(PWorkLogical)
1510
CALL ParallelActive(SaveParallelActive)
1511
1512
!Now, for any uncalved nodes, set heightdirich from current height
1513
DO i=1, Mesh % NumberOfNodes
1514
IF(IsCalvingNode(i)) CYCLE
1515
IF(FrontPerm(i) <= 0) CYCLE
1516
HeightDirich(i) = Mesh % Nodes % z(i)
1517
END DO
1518
1519
!---------------------------------------------------------
1520
! Cycle columns, analytically finding calving 3
1521
!---------------------------------------------------------
1522
1523
ALLOCATE(FNColumns(Mesh % NumberOfNodes))
1524
1525
FNColumns = MOD(Mesh % ParallelInfo % GlobalDOFs, NodesPerLevel)
1526
1527
DO WHILE(.TRUE.) !cycle columns
1528
1529
!Find a new column
1530
col = -1
1531
DO j=1, Mesh % NumberOfNodes
1532
IF(FrontPerm(j) <= 0) CYCLE
1533
IF(FNColumns(j) /= -1) THEN !not done
1534
col = FNColumns(j)
1535
EXIT
1536
END IF
1537
END DO
1538
IF(col == -1) EXIT !All done
1539
1540
!Gather front nodes in specified column
1541
WorkNodes % NumberOfNodes = COUNT((FrontPerm > 0) .AND. (FNColumns == col))
1542
n = WorkNodes % NumberOfNodes
1543
ALLOCATE(WorkNodes % z(n), ColumnPerm(n))
1544
1545
counter = 1
1546
DO j=1, Mesh % NumberOfNodes
1547
IF(FrontPerm(j) <= 0) CYCLE
1548
IF(FNColumns(j) == col) THEN
1549
WorkNodes % z(counter) = Mesh % Nodes % z(j)
1550
ColumnPerm(counter) = j
1551
counter = counter + 1
1552
END IF
1553
END DO
1554
1555
!Order by ascending WorkNodes % z
1556
!In fact, OrderPerm here is unnecessary, because we no longer order
1557
!by height, but rather by nodenumber, assuming MeshExtrude used, which
1558
!orders nodes by layer.
1559
ALLOCATE(OrderPerm(n))
1560
OrderPerm = [(i,i=1,n)]
1561
1562
IF(.FALSE.) THEN
1563
CALL SortD( n, WorkNodes % z, OrderPerm )
1564
ELSE
1565
!Heuristic test which will warn (or die) if the
1566
!assumption of ordered layers of nodes breaks down
1567
counter = 0
1568
DO j=1, n-1
1569
IF(WorkNodes % z(j) > WorkNodes % z(j+1)) counter = counter + 1
1570
END DO
1571
IF(counter > 0) CALL Warn(SolverName, "Calving front nodes in a column are out of order.&
1572
& This may not be a problem.")
1573
IF( ((1.0 * counter) / n ) > 0.5) THEN
1574
PRINT *,'There are ', counter, ' out of ',n,' nodes out of order.'
1575
CALL Fatal(SolverName, "Majority of nodes in a calving column are out of order (z).&
1576
& This is probably due to not using MeshExtrude, or a change in the way in which &
1577
&MeshExtrude operates.")
1578
END IF
1579
END IF
1580
1581
start = 2
1582
1583
DO WHILE(.TRUE.)
1584
1585
InGroup = .FALSE.
1586
GroupCount = 0
1587
GroupEnd = 0
1588
BotZ = HeightDirich(ColumnPerm(OrderPerm(start-1)))
1589
1590
!Need to be careful with BotZ, TopZ, whether or not these are also calving nodes
1591
! affects count
1592
DO k=start,n
1593
1594
IF(IsCalvingNode(ColumnPerm(OrderPerm(k)))) THEN
1595
1596
IF(.NOT. InGroup) GroupStart = k
1597
InGroup = .TRUE.
1598
1599
IF(k /= n) THEN
1600
GroupEnd = k
1601
GroupCount = GroupCount + 1
1602
ELSE !top node
1603
TopZ = HeightDirich(ColumnPerm(OrderPerm(k)))
1604
start = k + 1 !Forces empty DO loop next time, InGroup = .FALSE.
1605
END IF
1606
1607
ELSE
1608
1609
IF(InGroup) THEN
1610
TopZ = HeightDirich(ColumnPerm(OrderPerm(k)))
1611
start = k + 1
1612
EXIT
1613
ELSE
1614
BotZ = HeightDirich(ColumnPerm(OrderPerm(k)))
1615
END IF
1616
END IF
1617
1618
END DO
1619
1620
IF(.NOT. InGroup) EXIT !didn't find any more calving nodes this round, done
1621
1622
!now: groupcount = number of internal nodes to be spaced between TopZ and BotZ
1623
!NOTE: This produces evenly spaced nodes (vertically). Not an issue because
1624
! will be remeshed anyway...
1625
DO k=GroupStart, GroupEnd
1626
prop = (REAL(k) - (GroupStart - 1)) / (GroupCount + 1) !REAL to force real arithmetic
1627
HeightDirich(ColumnPerm(OrderPerm(k))) = BotZ + ( (TopZ - BotZ) * prop)
1628
IF(Debug) THEN
1629
PRINT *,'debug node: ',ColumnPerm(OrderPerm(k)),' has prop: ',prop,&
1630
'and BotZ, TopZ:', BotZ, TopZ,' and heightdirich: ',&
1631
HeightDirich(ColumnPerm(OrderPerm(k))),' groupstart, end,count, k:',&
1632
groupstart,groupend,groupcount,k
1633
END IF
1634
END DO
1635
END DO
1636
1637
DO i=1,n
1638
k = ColumnPerm(OrderPerm(i))
1639
IF(.NOT. IsCalvingNode(k)) CYCLE
1640
CalvingValues(CalvingPerm(k)*DOFs) = &
1641
HeightDirich(k) - Mesh % Nodes % z(k)
1642
END DO
1643
1644
FNColumns(ColumnPerm) = -1 !mark column nodes as done already
1645
1646
DEALLOCATE(WorkNodes % z, ColumnPerm, OrderPerm)
1647
END DO
1648
1649
DEALLOCATE(HeightDirich, FNColumns)
1650
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
1651
1652
END IF !CalvingOccurs
1653
1654
!=========================================================
1655
! DONE, just plane VTU and deallocations left
1656
!=========================================================
1657
1658
!-----------------------------------------------------------
1659
! Call ResultOutputSolver in faux-serial to output calving
1660
! plane for debugging
1661
!-----------------------------------------------------------
1662
IF(Boss) THEN
1663
VTUSolverName = "calvingresultoutput"
1664
DO i=1,Model % NumberOfSolvers
1665
IF(GetString(Model % Solvers(i) % Values, 'Equation') == VTUSolverName) THEN
1666
VTUOutputSolver => Model % Solvers(i)
1667
EXIT
1668
END IF
1669
END DO
1670
IF(.NOT. ASSOCIATED(VTUOutputSolver)) &
1671
CALL Fatal("Calving Remesh","Couldn't find VTUSolver")
1672
1673
PEs = ParEnv % PEs
1674
ParEnv % PEs = 1
1675
1676
WorkMesh => Model % Mesh
1677
WorkMesh2 => Model % Meshes
1678
1679
Model % Mesh => PlaneMesh
1680
Model % Meshes => PlaneMesh
1681
VTUOutputSolver % Mesh => PlaneMesh
1682
PlaneMesh % Next => IsoMesh
1683
1684
VTUOutputSolver % dt = dt
1685
VTUOutputSolver % NumberOfActiveElements = 0
1686
Model % Solver => VTUOutputSolver
1687
1688
CALL SingleSolver( Model, VTUOutputSolver, VTUOutputSolver % dt, TransientSimulation )
1689
1690
Model % Solver => Solver
1691
Model % Mesh => WorkMesh
1692
Model % Meshes => WorkMesh2
1693
PlaneMesh % Next => NULL()
1694
VTUOutputSolver % Mesh => WorkMesh
1695
1696
ParEnv % PEs = PEs
1697
1698
END IF
1699
1700
rt = RealTime() - rt0
1701
IF(ParEnv % MyPE == 0) &
1702
PRINT *, 'Time taken up to finish up: ', rt
1703
rt0 = RealTime()
1704
1705
!Output some information
1706
CALL CalvingStats(MaxBergVolume)
1707
IF(MaxBergVolume > PauseVolumeThresh) THEN
1708
PauseSolvers = .TRUE.
1709
ELSE
1710
PauseSolvers = .FALSE.
1711
END IF
1712
1713
CALL SParIterAllReduceOR(PauseSolvers) !Really this should just be Boss sending to all
1714
CALL ListAddLogical( Model % Simulation, 'Calving Pause Solvers', PauseSolvers )
1715
IF(Debug) PRINT *,ParEnv % MyPE, ' Calving3D, pause solvers: ', PauseSolvers
1716
1717
rt = RealTime() - rt0
1718
IF(ParEnv % MyPE == 0) &
1719
PRINT *, 'Time taken up to output calving stats: ', rt
1720
rt0 = RealTime()
1721
!-----------------------------------------------------------
1722
! Tidy up and deallocate
1723
!-----------------------------------------------------------
1724
1725
FirstTime = .FALSE.
1726
1727
PCSolver % Variable => NULL()
1728
PCSolver % Matrix % Perm => NULL()
1729
CALL FreeMatrix(PCSolver % Matrix)
1730
CALL ReleaseMesh(PlaneMesh)
1731
1732
DEALLOCATE(TopPerm, BotPerm, LeftPerm, RightPerm, FrontPerm)
1733
1734
IF(Parallel) DEALLOCATE(MyFaceNodeNums)
1735
1736
IF(Boss) THEN
1737
DEALLOCATE(FaceNodeNums, &
1738
FaceNodesT % x, &
1739
FaceNodesT % y, &
1740
FaceNodesT % z, &
1741
RemoveNode,&
1742
PlaneFrontPerm,&
1743
PlaneLeftPerm,&
1744
PlaneRightPerm&
1745
)
1746
1747
IF(Parallel) DEALLOCATE(disps, PFaceNodeCount)
1748
1749
END IF
1750
TimeVar => VariableGet(Model % Variables, 'Time', .TRUE.)
1751
time = TimeVar % Values(1)
1752
CALL ListAddConstReal( Model % Simulation, 'CalvingTime', Time )
1753
IF(Parallel) CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
1754
1755
CONTAINS
1756
1757
!Subroutine to print iceberg information to a file, to be processed in python.
1758
!Also calculates the size of the largest iceberg and returns it
1759
SUBROUTINE CalvingStats(MaxBergVol)
1760
IMPLICIT NONE
1761
1762
TYPE(Nodes_t) :: ElementNodes
1763
TYPE(Element_t), POINTER :: CalvingElements(:), Element
1764
TYPE(GaussIntegrationPoints_t) :: IntegStuff
1765
1766
INTEGER :: i,j,k,county, sendcount, status(MPI_STATUS_SIZE), NoIcebergs,n,&
1767
col, row, elemcorners(4), countcalve, ElemBergID, start, fin,LeftIndex, RightIndex
1768
INTEGER, ALLOCATABLE :: disps(:), FNColumns(:), FNRows(:), FNColumnOrder(:),&
1769
WorkInt(:), IDVector(:)
1770
INTEGER, POINTER :: IcebergID(:), NodeIndexes(:)
1771
INTEGER, PARAMETER :: FileUnit = 57
1772
REAL(KIND=dp), ALLOCATABLE :: AllCalvingValues(:), MyOrderedCalvingValues(:), &
1773
WorkReal(:), CalvingMagnitude(:)
1774
REAL(KIND=dp) :: LeftMost, RightMost, s, U, V, W, Basis(Mesh % MaxElementNodes), &
1775
SqrtElementMetric, MaxBergVol, BergVolume, ElemVolume
1776
LOGICAL, POINTER :: NodesAreNeighbours(:,:), CalvingNeighbours(:,:), BergBoundaryNode(:,:)
1777
LOGICAL :: Visited = .FALSE., IcebergCondition(4), Debug, stat
1778
CHARACTER(MAX_NAME_LEN) :: FileName
1779
1780
SAVE :: Visited
1781
1782
Debug = .FALSE.
1783
MaxBergVol = 0.0_dp
1784
1785
!Boss has:
1786
! FaceNodesT - which is all the frontal nodes
1787
! FaceNodeNums - global front node numbers from all parts
1788
! FNColumns - info about the structure of the mesh
1789
! NodesPerLevel, ExtrudedLevels
1790
1791
!BUT, doesn't have info about neighbours...
1792
!although this could be ascertained from FrontNodeNums, which are in order
1793
1794
IF(Boss) THEN
1795
1796
FileName = TRIM(NameSuffix)//"_IcebergStats.txt"
1797
1798
ALLOCATE(WorkReal(SUM(PFaceNodeCount)*DOFs),&
1799
AllCalvingValues(FaceNodesT % NumberOfNodes * DOFs),&
1800
FNRows(FaceNodesT % NumberOfNodes),&
1801
FNColumns(FaceNodesT % NumberOfNodes),&
1802
FNColumnOrder(NodesPerLevel),&
1803
NodesAreNeighbours(FaceNodesT % NumberOfNodes, FaceNodesT % NumberOfNodes),&
1804
CalvingNeighbours(FaceNodesT % NumberOfNodes, FaceNodesT % NumberOfNodes),&
1805
WorkInt(SIZE(FrontNodeNums)),&
1806
IDvector(FaceNodesT % NumberOfNodes),&
1807
disps(ParEnv % PEs), STAT=ierr)
1808
1809
IDvector = [(i,i=1,FaceNodesT % NumberOfNodes)]
1810
1811
disps(1) = 0
1812
DO i=2,ParEnv % PEs
1813
disps(i) = disps(i-1) + (PFaceNodeCount(i-1)*DOFs)
1814
END DO
1815
1816
1817
!FrontNodeNums (from GetDomainEdge) are ordered, and thus so are the columns
1818
WorkInt = MOD(FrontNodeNums, NodesPerLevel)
1819
DO i=1, SIZE(WorkInt)
1820
FNColumnOrder(WorkInt(i)) = i
1821
END DO
1822
1823
FNRows = (FaceNodeNums - 1) / NodesPerLevel
1824
FNColumns = MOD(FaceNodeNums, NodesPerLevel)
1825
NodesAreNeighbours = .FALSE.
1826
1827
DO i=1,FaceNodesT % NumberOfNodes
1828
DO j=1,FaceNodesT % NumberOfNodes
1829
IF(i==j) CYCLE
1830
IF( ABS(FNColumnOrder(FNColumns(j)) - FNColumnOrder(FNColumns(i))) > 1) CYCLE
1831
IF( ABS(FNRows(j) - FNRows(i)) > 1) CYCLE
1832
!Neighbour must be in either same column or row... i.e. no diag neighbours
1833
IF( (FNRows(j) /= FNRows(i)) .AND. &
1834
(FNColumnOrder(FNColumns(j)) /= FNColumnOrder(FNColumns(i)))) CYCLE
1835
NodesAreNeighbours(i,j) = .TRUE.
1836
END DO
1837
END DO
1838
1839
IF(Debug) PRINT *,'Debug CalvingStats, FaceNodes: ',FaceNodesT % NumberOfNodes,&
1840
' neighbourships: ', COUNT(NodesAreNeighbours)
1841
END IF
1842
1843
ALLOCATE(MyOrderedCalvingValues(COUNT(CalvingPerm>0)*DOFs), STAT=ierr)
1844
MyOrderedCalvingValues = 0.0_dp
1845
1846
!Order the calving values to match front node numbers
1847
county = 0
1848
DO i=1,NoNodes
1849
IF(CalvingPerm(i) <= 0) CYCLE
1850
1851
county = county + 1
1852
1853
MyOrderedCalvingValues((county*DOFs)-2) = CalvingValues((CalvingPerm(i)*DOFs)-2)
1854
MyOrderedCalvingValues((county*DOFs)-1) = CalvingValues((CalvingPerm(i)*DOFs)-1)
1855
MyOrderedCalvingValues(county*DOFs) = CalvingValues(CalvingPerm(i)*DOFs)
1856
END DO
1857
1858
!Gather calving var values
1859
sendcount = COUNT(CalvingPerm>0)*DOFs
1860
IF(Debug) PRINT *,ParEnv % MyPE,'send count: ',sendcount
1861
1862
IF(sendcount > 0) THEN
1863
CALL MPI_BSEND(MyOrderedCalvingValues,sendcount, MPI_DOUBLE_PRECISION,0,&
1864
1000+ParEnv % MyPE, ELMER_COMM_WORLD, ierr)
1865
END IF
1866
1867
IF(BOSS) THEN
1868
IF(Debug) PRINT *,'Debug, size workreal:',SIZE(WorkReal)
1869
DO i=1,ParEnv % PEs
1870
IF(PFaceNodeCount(i) <= 0) CYCLE
1871
1872
start = 1+disps(i)
1873
fin = (disps(i)+PFaceNodeCount(i)*DOFs)
1874
IF(Debug) PRINT *,'Debug, ',i,' start, end',start, fin
1875
1876
CALL MPI_RECV(WorkReal(start:fin), &
1877
PFaceNodeCount(i)*DOFs, MPI_DOUBLE_PRECISION, i-1, &
1878
1000+i-1, ELMER_COMM_WORLD, status, ierr)
1879
END DO
1880
END IF
1881
1882
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
1883
1884
IF(Boss) THEN
1885
!Remove duplicates, using previously computed duplicate positions
1886
county = 0
1887
DO i=1,SIZE(RemoveNode)
1888
IF(RemoveNode(i)) CYCLE
1889
county = county+1
1890
AllCalvingValues((county*DOFs)-2) = WorkReal((i*DOFs)-2)
1891
AllCalvingValues((county*DOFs)-1) = WorkReal((i*DOFs)-1)
1892
AllCalvingValues((county*DOFs)) = WorkReal((i*DOFs))
1893
END DO
1894
1895
IF(Debug) THEN
1896
PRINT *,'Debug, AllCalvingValues: '
1897
DO i=1,FaceNodesT % NumberOfNodes
1898
PRINT *, 'Node: ',i, 'x,y,z: ',AllCalvingValues((i*3)-2), &
1899
AllCalvingValues((i*3)-1), AllCalvingValues(i*3)
1900
END DO
1901
END IF
1902
1903
CalvingNeighbours = NodesAreNeighbours
1904
DO i=1,SIZE(CalvingNeighbours,1)
1905
k = i * DOFs
1906
IF(ALL(AllCalvingValues(k-2:k) == 0.0_dp)) THEN
1907
CalvingNeighbours(i,:) = .FALSE.
1908
CalvingNeighbours(:,i) = .FALSE.
1909
END IF
1910
END DO
1911
1912
!Mark connected calving neighbours with a unique iceberg ID
1913
ALLOCATE(IcebergID(FaceNodesT % NumberOfNodes))
1914
IcebergID = 0
1915
1916
NoIcebergs = 0
1917
DO i=1,FaceNodesT % NumberOfNodes
1918
k = i * DOFs
1919
!pretty sure no perm between CalvingValues and NodesAreNeighbours
1920
IF(ALL(AllCalvingValues(k-2:k) == 0.0_dp)) CYCLE
1921
IF(IcebergID(i) > 0) CYCLE !Got already
1922
1923
!new group
1924
NoIcebergs = NoIcebergs + 1
1925
IcebergID(i) = NoIcebergs
1926
CALL MarkNeighbours(i, CalvingNeighbours, IcebergID, NoIcebergs)
1927
1928
END DO
1929
1930
!Now cycle icebergs
1931
DEALLOCATE(WorkInt)
1932
ALLOCATE(BergBoundaryNode(NoIcebergs, FaceNodesT % NumberOfNodes))
1933
BergBoundaryNode = .FALSE.
1934
1935
!Cycle icebergs, cycle nodes in iceberg, mark all neighbours (edges of iceberg)
1936
DO i=1,NoIcebergs
1937
ALLOCATE(WorkInt(COUNT(IcebergID == i)))
1938
WorkInt = PACK(IDVector, (IcebergID == i))
1939
1940
DO j=1,SIZE(WorkInt)
1941
DO k=1,SIZE(NodesAreNeighbours,1)
1942
IF(.NOT. NodesAreNeighbours(WorkInt(j),k)) CYCLE
1943
1944
!Not a boundary node
1945
IF(IcebergID(k) /= 0) THEN
1946
IF(IcebergID(k) /= i) CALL Fatal("CalvingStats",&
1947
"This shouldn't happen - two adjacent nodes in different icebergs...")
1948
CYCLE
1949
END IF
1950
1951
BergBoundaryNode(i,k) = .TRUE.
1952
END DO
1953
END DO
1954
DEALLOCATE(WorkInt)
1955
END DO
1956
1957
!------------------------------------------
1958
! Scan along/down front, constructing calving elements
1959
!
1960
! Strategy: cycle front nodes, looking for a calving element which
1961
! has node i as a top left corner. NB, it needn't necessarily
1962
! be a calving node itself, so long as three of the 4 are...
1963
!
1964
! elem corners go (tl, tr, bl, br)
1965
!------------------------------------------
1966
ALLOCATE(CalvingElements(FaceNodesT % NumberOfNodes)) !<- excessive, but meh
1967
county = 0
1968
1969
DO i=1,FaceNodesT % NumberOfNodes
1970
!NB use NodesAreNeighbours instead of CalvingNeighbours, check both
1971
elemcorners = 0
1972
elemcorners(1) = i
1973
col = FNColumnOrder(FNColumns(i)) !<-- pay attention
1974
row = FNRows(i)
1975
1976
!gather 3 other nodes
1977
DO j=1,FaceNodesT % NumberOfNodes
1978
!note, this doesn't guarantee left, or right,
1979
!but as long as consistent, doesn't matter
1980
! IF(.NOT. NodesAreNeighbours(i,j)) CYCLE
1981
SELECT CASE(FNColumnOrder(FNColumns(j)) - col)
1982
CASE(1)
1983
!Next column
1984
SELECT CASE(row - FNRows(j))
1985
CASE(1)
1986
!Next Row
1987
elemcorners(4) = j
1988
CASE(0)
1989
!Same Row
1990
elemcorners(2) = j
1991
CASE DEFAULT
1992
CYCLE
1993
END SELECT
1994
CASE(0)
1995
1996
!Same column
1997
SELECT CASE(row - FNRows(j))
1998
CASE(1)
1999
!Next row
2000
elemcorners(3) = j
2001
CASE(0)
2002
!Same row
2003
!same node!!
2004
CYCLE
2005
CASE DEFAULT
2006
CYCLE
2007
END SELECT
2008
CASE DEFAULT
2009
CYCLE
2010
END SELECT
2011
END DO
2012
2013
IF(ANY(ElemCorners == 0)) CYCLE !edge of domain
2014
2015
ElemBergID = MAXVAL(IcebergID(elemcorners))
2016
2017
IF(ElemBergID == 0) CYCLE
2018
2019
IcebergCondition = (IcebergID(elemcorners) == ElemBergID) &
2020
.OR. BergBoundaryNode(ElemBergID,elemcorners)
2021
2022
countcalve = COUNT(IcebergCondition)
2023
2024
IF( countcalve < 3) CYCLE
2025
2026
county = county + 1
2027
ALLOCATE(CalvingElements(county) % NodeIndexes(countcalve))
2028
CalvingElements(county) % TYPE => GetElementType(countcalve*100+countcalve, .FALSE.)
2029
CalvingElements(county) % BodyID = ElemBergID
2030
2031
countcalve = 0
2032
DO j=1,4
2033
IF(IcebergCondition(j)) THEN
2034
countcalve = countcalve+1
2035
CalvingElements(county) % NodeIndexes(countcalve) = elemcorners(j)
2036
END IF
2037
END DO
2038
2039
END DO
2040
2041
!------------------------------------------
2042
! Write info to file
2043
!------------------------------------------
2044
2045
!Find left and rightmost nodes for info
2046
LeftMost = HUGE(0.0_dp)
2047
RightMost = -HUGE(0.0_dp) !??
2048
2049
DO i=1,FaceNodesT % NumberOfNodes
2050
Nodeholder(1) = FaceNodesT % x(i)
2051
Nodeholder(2) = FaceNodesT % y(i)
2052
Nodeholder(3) = FaceNodesT % z(i)
2053
Nodeholder = MATMUL(RotationMatrix, NodeHolder)
2054
2055
IF(Nodeholder(2) < LeftMost) THEN
2056
LeftIndex = i
2057
LeftMost = NodeHolder(2)
2058
END IF
2059
2060
IF(Nodeholder(2) > RightMost) THEN
2061
RightIndex = i
2062
RightMost = NodeHolder(2)
2063
END IF
2064
END DO
2065
2066
IF(Visited) THEN
2067
OPEN( UNIT=FileUnit, FILE=filename, STATUS='UNKNOWN', POSITION='APPEND')
2068
ELSE
2069
OPEN( UNIT=FileUnit, FILE=filename, STATUS='UNKNOWN')
2070
WRITE(FileUnit, '(A,ES20.11,ES20.11,ES20.11)') "FrontOrientation: ",FrontOrientation
2071
END IF
2072
2073
!Write out the left and rightmost points
2074
WRITE(FileUnit, '(A,i0,ES30.21)') 'Time: ',GetTimestep(),GetTime()
2075
WRITE(FileUnit, '(A,ES20.11,ES20.11)') 'Left (xy): ',&
2076
FaceNodesT % x(LeftIndex),&
2077
FaceNodesT % y(LeftIndex)
2078
2079
WRITE(FileUnit, '(A,ES20.11,ES20.11)') 'Right (xy): ',&
2080
FaceNodesT % x(RightIndex),&
2081
FaceNodesT % y(RightIndex)
2082
2083
!Write the iceberg count
2084
WRITE(FileUnit, '(A,i0)') 'Icebergs: ',NoIcebergs
2085
2086
!TODO, write element count
2087
! would need to modify Icebergs.py too
2088
DO i=1,NoIcebergs
2089
county = 0
2090
!count elements
2091
2092
WRITE(FileUnit, '(A,i0)') 'Iceberg ',i
2093
WRITE(FileUnit, '(i0,A,i0)') COUNT(BergBoundaryNode(i,:))," ", COUNT(IceBergID == i)
2094
WRITE(FileUnit, '(A)') "Boundary nodes"
2095
DO j=1,FaceNodesT % NumberOfNodes
2096
IF(BergBoundaryNode(i,j)) THEN
2097
2098
county = county + 1
2099
WRITE(FileUnit,'(i0,A,i0,ES20.11,ES20.11,ES20.11,ES20.11,ES20.11,ES20.11)') &
2100
county," ",j,&
2101
FaceNodesT % x(j), FaceNodesT % y(j), FaceNodesT % z(j),&
2102
0.0_dp, 0.0_dp, 0.0_dp
2103
2104
END IF
2105
END DO
2106
2107
WRITE(FileUnit, '(A)') "Calving nodes"
2108
DO j=1,FaceNodesT % NumberOfNodes
2109
IF(IcebergID(j) == i) THEN
2110
county = county + 1
2111
k = j*DOFs
2112
WRITE(FileUnit,'(i0,A,i0,ES20.11,ES20.11,ES20.11,ES20.11,ES20.11,ES20.11)')&
2113
county," ",j,&
2114
FaceNodesT % x(j),&
2115
FaceNodesT % y(j),&
2116
FaceNodesT % z(j),&
2117
AllCalvingValues(k-2), &
2118
AllCalvingValues(k-1), &
2119
AllCalvingValues(k)
2120
END IF
2121
END DO
2122
2123
WRITE(FileUnit, '(A)') "Elements"
2124
DO j=1,SIZE(CalvingElements)
2125
IF(CalvingElements(j) % BodyID /= i) CYCLE
2126
DO k=1,CalvingElements(j) % TYPE % NumberOfNodes
2127
WRITE(FileUnit,'(i0,A)', ADVANCE="NO") CalvingElements(j) % NodeIndexes(k),' '
2128
END DO
2129
WRITE(FileUnit,'(A)') ''
2130
END DO
2131
2132
END DO
2133
2134
CLOSE(FileUnit)
2135
2136
!------------------------------------------
2137
! Compute berg volumes. Largest berg size determines
2138
! whether the pause the timestep.
2139
!------------------------------------------
2140
n = Mesh % MaxElementNodes
2141
ALLOCATE(ElementNodes % x(n),&
2142
ElementNodes % y(n),&
2143
ElementNodes % z(n),&
2144
CalvingMagnitude(FaceNodesT % NumberOfNodes))
2145
2146
DO i=1,SIZE(CalvingMagnitude)
2147
k = i*DOFs
2148
CalvingMagnitude(i) = ((AllCalvingValues(k-2) ** 2) + &
2149
(AllCalvingValues(k-1) ** 2) + &
2150
(AllCalvingValues(k) ** 2)) ** 0.5
2151
END DO
2152
2153
MaxBergVol = 0.0_dp
2154
DO i=1,NoIcebergs
2155
BergVolume = 0.0_dp
2156
2157
DO j=1,SIZE(CalvingElements)
2158
Element => CalvingElements(j)
2159
2160
IF(Element % BodyID /= i) CYCLE
2161
2162
n = Element % TYPE % NumberOfNodes
2163
NodeIndexes => Element % NodeIndexes
2164
2165
ElementNodes % x(1:n) = FaceNodesT % x(NodeIndexes(1:n))
2166
ElementNodes % y(1:n) = FaceNodesT % y(NodeIndexes(1:n))
2167
ElementNodes % z(1:n) = FaceNodesT % z(NodeIndexes(1:n))
2168
2169
IntegStuff = GaussPoints( Element )
2170
2171
ElemVolume = 0.0_dp
2172
DO k=1,IntegStuff % n
2173
2174
U = IntegStuff % u(k)
2175
V = IntegStuff % v(k)
2176
W = IntegStuff % w(k)
2177
2178
stat = ElementInfo( Element,ElementNodes,U,V,W,SqrtElementMetric, &
2179
Basis )
2180
2181
!assume cartesian here
2182
s = SqrtElementMetric * IntegStuff % s(k)
2183
2184
ElemVolume = ElemVolume + s * SUM(CalvingMagnitude(NodeIndexes(1:n)) * Basis(1:n))
2185
END DO
2186
2187
BergVolume = BergVolume + ElemVolume
2188
END DO
2189
IF(Debug) PRINT *,'Berg ',i,' volume: ', BergVolume
2190
MaxBergVol = MAX(BergVolume, MaxBergVol)
2191
END DO
2192
IF(Debug) PRINT *,'Max berg volume: ',MaxBergVol
2193
END IF
2194
2195
2196
Visited = .TRUE.
2197
DEALLOCATE(MyOrderedCalvingValues)
2198
IF(Boss) THEN
2199
DEALLOCATE(WorkReal,&
2200
AllCalvingValues, &
2201
FNRows,&
2202
FNColumns,&
2203
FNColumnOrder,&
2204
NodesAreNeighbours,&
2205
CalvingNeighbours,&
2206
disps,&
2207
BergBoundaryNode,&
2208
IDVector,&
2209
IcebergID,&
2210
ElementNodes % x,&
2211
ElementNodes % y,&
2212
ElementNodes % z,&
2213
CalvingMagnitude&
2214
)
2215
2216
!Cycle and deallocate element % Nodeindexes, and elements
2217
DO i=1,SIZE(CalvingElements)
2218
IF(ASSOCIATED(CalvingElements(i) % NodeIndexes)) &
2219
DEALLOCATE(CalvingElements(i) % NodeIndexes)
2220
END DO
2221
DEALLOCATE(CalvingElements)
2222
END IF
2223
2224
END SUBROUTINE CalvingStats
2225
END SUBROUTINE Find_Calving3D
2226
2227