Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/elmerice/Solvers/CalvingGeometry.F90
3203 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
!This moduled, loosely named 'CalvingGeometry' is for basically any
33
!reusable routines for the 3D calving model.
34
35
MODULE CalvingGeometry
36
37
USE Types
38
USE SParIterComm
39
USE MainUtils
40
USE DefUtils
41
42
IMPLICIT NONE
43
44
INTERFACE DoubleIntVectorSize
45
MODULE PROCEDURE DoubleIntVectorSizeP, DoubleIntVectorSizeA
46
END INTERFACE
47
48
INTERFACE Double2DLogSize
49
MODULE PROCEDURE Double2DLogSizeP, Double2DLogSizeA
50
END INTERFACE
51
52
INTERFACE Double3DArraySize
53
MODULE PROCEDURE Double3DArraySizeP, Double3DArraySizeA
54
END INTERFACE
55
56
INTERFACE Double4DArraySize
57
MODULE PROCEDURE Double4DArraySizeP, Double4DArraySizeA
58
END INTERFACE
59
60
INTERFACE DoubleDPVectorSize
61
MODULE PROCEDURE DoubleDPVectorSizeP, DoubleDPVectorSizeA
62
END INTERFACE
63
64
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
65
! Derived type for 3D crevasse group info
66
!
67
! Using the => Next, => Prev format like
68
! variables_t, because there's no way of
69
! knowing, a priori, how many we need.
70
!
71
! Actually the only use of this is borrowed by BasalMelt3D.F90, so its misnamed...
72
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
73
TYPE CrevasseGroup3D_t
74
INTEGER :: NumberOfNodes = 0, ID = 0
75
INTEGER, POINTER :: NodeNumbers(:) => NULL()
76
INTEGER, POINTER :: BoundaryNodes(:) => NULL(), FrontNodes(:) => NULL() !allocatable too?
77
REAL(KIND=dp) :: BoundingBox(4) !min_x, max_x, min_y, max_y
78
79
LOGICAL :: FrontConnected !Does the group touch the terminus?
80
TYPE(CrevasseGroup3D_t), POINTER :: Next => NULL(), Prev => NULL()
81
END TYPE CrevasseGroup3D_t
82
83
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
84
! Derived type for a calving path defined by
85
! the IsoSurface/Line solver.
86
! (doubly linked list)
87
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
88
TYPE CrevassePath_t
89
INTEGER :: NumberOfNodes = 0, NumberOfElements = 0, ID = 0
90
INTEGER, POINTER :: NodeNumbers(:) => NULL(), ElementNumbers(:)=>NULL()
91
! INTEGER :: Ends(2)
92
REAL(KIND=dp) :: Left, Right, Extent, Orientation(2)
93
TYPE(CrevassePath_t), POINTER :: Next => NULL(), Prev => NULL()
94
LOGICAL :: Valid = .TRUE., LeftToRight = .TRUE.
95
END TYPE CrevassePath_t
96
97
CONTAINS
98
99
100
!Returns the neighbours of a specified node using the matrix
101
!provided.
102
!Note the current definition of neighbours:
103
!Two nodes are neighbours if they are in the same bulk element
104
!NOT ONLY if they are joined by a bar...
105
!User may provide an inverse perm (InvPerm_in), or else this will recomputed
106
!each time (which would be pretty inefficient)
107
FUNCTION FindNodeNeighbours(NodeNumber, Matrix, Perm, DOFs, InvPerm_in) RESULT (Neighbours)
108
INTEGER :: NodeNumber, NoNeighbours, i, count, DOFs !<---!!!
109
TYPE(Matrix_t), POINTER :: Matrix
110
INTEGER, POINTER :: Perm(:), Neighbours(:), InvPerm(:)
111
INTEGER, POINTER, OPTIONAL, INTENT(IN) :: InvPerm_in(:)
112
LOGICAL :: Debug
113
Debug = .FALSE.
114
115
IF(PRESENT(InvPerm_in)) THEN
116
InvPerm => InvPerm_in
117
ELSE
118
IF(Debug) PRINT *, 'Debug FindNodeNeighbours, creating InvPerm'
119
InvPerm => CreateInvPerm(Perm)
120
END IF
121
122
NoNeighbours = Matrix % Rows((Perm(NodeNumber)*DOFs)+1) &
123
- Matrix % Rows(Perm(NodeNumber)*DOFs)
124
125
IF(MOD(NoNeighbours, DOFs).NE. 0) &
126
CALL FATAL("Geometry","This shouldn't have happened...")
127
128
!Each neighbour appears once per DOF, and there's also the current node thus: (x/DOFS) - 1...
129
NoNeighbours = (NoNeighbours / DOFs) - 1
130
131
ALLOCATE(Neighbours(NoNeighbours))
132
Neighbours = 0
133
134
count = 0
135
136
DO i=Matrix % Rows(Perm(NodeNumber)*DOFs),&
137
(Matrix % Rows((Perm(NodeNumber)*DOFs)+1)-1) !move along the row
138
IF(MOD(i,DOFs) /= 0) CYCLE !Stored DOF1, DOF2, DOF3, only need every DOFth
139
IF(MOD(Matrix % Cols(i), DOFs) /= 0) CALL Fatal("Geometry:FindNodeNeighbours", &
140
"This is a programming error, Matrix structure is not what was expected.")
141
142
IF(InvPerm(Matrix % Cols(i)/DOFs) == NodeNumber) CYCLE !Not our own neighbour
143
count = count + 1
144
Neighbours(count) = &
145
InvPerm(Matrix % Cols(i)/DOFs)
146
END DO
147
148
IF(.NOT. PRESENT(InvPerm_in)) DEALLOCATE(InvPerm)
149
150
END FUNCTION FindNodeNeighbours
151
152
153
!-----------------------------------------------------------------------------
154
!Returns the 2D (x,y) Cartesian distance between two nodes
155
!NOTE: This isn't well programmed, should probably pass nodes...
156
FUNCTION NodeDist2D(Nodes, NodeNum1, NodeNum2 ) RESULT (dist)
157
TYPE(Nodes_t) :: Nodes
158
INTEGER :: NodeNum1, NodeNum2
159
REAL(KIND=dp) :: xdist,ydist,dist
160
!Pythagoras in 2D
161
xdist = Nodes % x(NodeNum1)&
162
- Nodes % x(NodeNum2)
163
ydist = Nodes % y(NodeNum1)&
164
- Nodes % y(NodeNum2)
165
!TODO: Can this be simplified? See Interpolation.f90
166
dist = ((xdist**2) + (ydist**2))**0.5
167
END FUNCTION NodeDist2D
168
169
!-----------------------------------------------------------------------------
170
!Returns the 3D Cartesian distance between two nodes
171
!NOTE: This isn't well programmed, should probably pass nodes...
172
FUNCTION NodeDist3D( Nodes, Node1, Node2 ) RESULT (dist)
173
TYPE(Nodes_t) :: Nodes
174
INTEGER :: Node1, Node2
175
REAL(KIND=dp) :: xdist,ydist,zdist,xydist,dist
176
!Pythagoras in 3D
177
xdist = Nodes % x(Node1)&
178
- Nodes % x(Node2)
179
ydist = Nodes % y(Node1)&
180
- Nodes % y(Node2)
181
zdist = Nodes % z(Node1)&
182
- Nodes % z(Node2)
183
!TODO: Can this be simplified? See Interpolation.f90
184
xydist = ((xdist**2) + (ydist**2))**0.5
185
dist = ((xydist**2) + (zdist**2))**0.5
186
END FUNCTION NodeDist3D
187
188
FUNCTION PointDist2D( Point1, Point2 ) RESULT (dist)
189
REAL(KIND=dp) :: Point1(2),Point2(2),xdist,ydist,dist
190
!Pythagoras in 3D
191
xdist = Point1(1) - Point2(1)
192
ydist = Point1(2) - Point2(2)
193
!TODO: Can this be simplified? See Interpolation.f90
194
dist = ((xdist**2) + (ydist**2))**0.5
195
END FUNCTION PointDist2D
196
197
FUNCTION PointDist3D( Point1, Point2 ) RESULT (dist)
198
REAL(KIND=dp) :: Point1(3),Point2(3),xdist,ydist,zdist,xydist,dist
199
!Pythagoras in 3D
200
xdist = Point1(1) - Point2(1)
201
ydist = Point1(2) - Point2(2)
202
zdist = Point1(3) - Point2(3)
203
!TODO: Can this be simplified? See Interpolation.f90
204
xydist = ((xdist**2) + (ydist**2))**0.5
205
dist = ((xydist**2) + (zdist**2))**0.5
206
END FUNCTION PointDist3D
207
208
!-----------------------------------------------------------------------------
209
!Returns the inverse permutation table for a given perm and DOFs
210
!NOTE, differs from the definition of InvPerm currently used in
211
!Calving.F90
212
FUNCTION CreateInvPerm(Perm) RESULT(InvPerm)
213
INTEGER, POINTER :: Perm(:), InvPerm(:)
214
INTEGER :: i, j
215
216
ALLOCATE(InvPerm(MAXVAL(Perm)))
217
218
j = 0
219
DO i=1,SIZE(Perm)
220
IF(Perm(i) == 0) CYCLE
221
j = j + 1
222
InvPerm( Perm(i) ) = j
223
END DO
224
225
END FUNCTION CreateInvPerm
226
227
!-----------------------------------------------------------------------------
228
!Returns dx/dy for two given nodes
229
FUNCTION NodesGradXY(Nodes, Node1, Node2)RESULT(dxdy)
230
INTEGER :: Node1, Node2
231
TYPE(Nodes_t) :: Nodes
232
REAL(KIND=dp) :: dx,dy,dxdy
233
234
dx = Nodes % x(Node1) - Nodes % x(Node2)
235
dy = Nodes % y(Node1) - Nodes % y(Node2)
236
dxdy = dx/dy
237
END FUNCTION NodesGradXY
238
239
!-----------------------------------------------------------------------------
240
!Returns the number of decimal places of a real number
241
!which has been read from a text file (.e.g mesh.nodes)
242
!this differs from intrinsic PRECISION() because these
243
!numbers often have trailing 000s or 999s
244
FUNCTION RealAeps(in)RESULT(myaeps)
245
REAL(KIND=dp) :: in, toler, x, myaeps
246
INTEGER :: sigs, mag, decs
247
248
!Find how many decimal places
249
mag = FLOOR(LOG10(ABS(in))) + 1 !Order of magnitude of number
250
decs = PRECISION(in) - mag !total digits - magnitude = decimal places
251
252
toler = 10.0_dp**(-decs)
253
sigs = 0
254
x = in
255
256
DO WHILE (.TRUE.)
257
IF(ABS(x - NINT(x)) < toler) THEN !found the precision limit
258
EXIT
259
ELSE
260
sigs = sigs + 1
261
x = x * 10 !move the decimal point along
262
x = x - FLOOR(x) !convert number to O(1) so FLOOR doesn't reach integer limit
263
toler = toler * 10.0_dp !1 fewer remaining decimal places
264
END IF
265
END DO
266
myaeps = 10.0**(-sigs)
267
END FUNCTION RealAeps
268
269
!-----------------------------------------------------------------------------
270
! Constructs paths of connected isoline (202) elements which intersect the
271
! front. Each path will begin and end with a node where OnFront=.TRUE.
272
!-----------------------------------------------------------------------------
273
SUBROUTINE FindCrevassePaths(IsoMesh, OnFront, CrevassePaths, PathCount)
274
IMPLICIT NONE
275
TYPE(Mesh_t), POINTER :: IsoMesh
276
LOGICAL, ALLOCATABLE :: OnFront(:)
277
TYPE(CrevassePath_t), POINTER :: CrevassePaths
278
INTEGER :: PathCount
279
!----------------------------------------------
280
TYPE(CrevassePath_t), POINTER :: CurrentPath
281
LOGICAL :: Found, Debug
282
INTEGER :: i,j,NodeCount,ElemCount, NextElem
283
INTEGER, ALLOCATABLE :: WorkElems(:), WorkNodes(:)
284
285
Debug = .FALSE.
286
PathCount = 1
287
288
!TODO assert all 202 elements
289
290
ALLOCATE(CrevassePaths)
291
CurrentPath => CrevassePaths
292
293
ALLOCATE(WorkElems(100), WorkNodes(100))
294
WorkElems = 0; WorkNodes = 0
295
296
DO i=1, IsoMesh % NumberOfBulkElements
297
298
IF(ANY(OnFront(Isomesh % Elements(i) % NodeIndexes))) THEN
299
!Found an element with one node on calving front
300
301
IF(ElementPathID(CrevassePaths, i) /= 0) CYCLE !already in a path
302
303
!Starting a new group...
304
CurrentPath % ID = PathCount
305
IF(Debug) PRINT *, 'Potential calving isomesh element: ',i
306
307
ElemCount = 1
308
NextElem = i
309
310
!Identify which of the two nodes are on the front...
311
DO j=1,2
312
IF(OnFront(IsoMesh % Elements(i) % NodeIndexes(j))) EXIT
313
END DO
314
IF(j==3) CALL Fatal("FindCrevassePaths", "Couldn't find node on boundary")
315
316
!... and put it first in the list
317
WorkNodes(1) = IsoMesh % Elements(i) % NodeIndexes(j)
318
NodeCount = 2
319
320
!Follow the chain
321
DO WHILE(.TRUE.)
322
323
WorkElems(ElemCount) = NextElem
324
ElemCount = ElemCount + 1
325
!Put the other node into the list
326
DO j=1,2
327
IF(ANY(WorkNodes == IsoMesh % Elements(NextElem) % NodeIndexes(j))) CYCLE
328
WorkNodes(NodeCount) = IsoMesh % Elements(NextElem) % NodeIndexes(j)
329
NodeCount = NodeCount + 1
330
EXIT
331
END DO
332
333
!Look for element which contains previous element's node
334
Found = .FALSE.
335
DO j=1,IsoMesh % NumberOfBulkElements
336
IF(ANY(IsoMesh % Elements(j) % NodeIndexes == WorkNodes(NodeCount-1))) THEN
337
338
!already in another group (is this possible?)
339
IF(ElementPathID(CrevassePaths, j ) /= 0) CYCLE
340
!Already in current group
341
IF(ANY(WorkElems == j)) CYCLE
342
343
NextElem = j
344
Found = .TRUE.
345
EXIT
346
END IF
347
END DO
348
349
IF(.NOT. Found) EXIT
350
351
IF(ElemCount > SIZE(WorkElems)) THEN
352
IF(Debug) PRINT *, 'FindCrevassePaths, doubling size of element array.'
353
CALL DoubleIntVectorSize(WorkElems)
354
END IF
355
IF(NodeCount > SIZE(WorkNodes)) THEN
356
IF(Debug) PRINT *, 'FindCrevassePaths, doubling size of node array.'
357
CALL DoubleIntVectorSize(WorkNodes)
358
END IF
359
END DO
360
361
ElemCount = ElemCount - 1
362
NodeCount = NodeCount - 1
363
364
CurrentPath % NumberOfNodes = NodeCount
365
CurrentPath % NumberOfElements = ElemCount
366
367
ALLOCATE(CurrentPath % ElementNumbers(ElemCount), &
368
CurrentPath % NodeNumbers(NodeCount))
369
370
CurrentPath % NodeNumbers = WorkNodes(1:NodeCount)
371
CurrentPath % ElementNumbers = WorkElems(1:ElemCount)
372
373
WorkNodes = 0
374
WorkElems = 0
375
376
ALLOCATE(CurrentPath % Next)
377
CurrentPath % Next % Prev => CurrentPath
378
CurrentPath => CurrentPath % Next
379
PathCount = PathCount + 1
380
END IF
381
END DO
382
383
!We always overshoot by one
384
PathCount = PathCount - 1
385
386
IF(PathCount > 0) THEN
387
PRINT *,'Number of crevasse paths: ', PathCount
388
CurrentPath % Prev % Next => NULL()
389
DEALLOCATE(CurrentPath)
390
ELSE
391
PRINT *,'No crevasse paths'
392
DEALLOCATE(CrevassePaths)
393
END IF
394
395
DEALLOCATE(WorkNodes, WorkElems)
396
397
END SUBROUTINE FindCrevassePaths
398
399
!Removes a CrevassePath from a linked list of CrevassePaths
400
SUBROUTINE RemoveCrevassePath(Path)
401
IMPLICIT NONE
402
TYPE(CrevassePath_t), POINTER :: Path
403
!------------------------------------------------
404
IF(ASSOCIATED(Path % Prev)) Path % Prev % Next => Path % Next
405
IF(ASSOCIATED(Path % Next)) Path % Next % Prev => Path % Prev
406
407
IF(ASSOCIATED(Path % NodeNumbers)) DEALLOCATE(Path % NodeNumbers)
408
IF(ASSOCIATED(Path % ElementNumbers)) DEALLOCATE(Path % ElementNumbers)
409
DEALLOCATE(Path)
410
411
END SUBROUTINE RemoveCrevassePath
412
413
!--------------------------------------------------------------------
414
! 'Tidies up' isomesh and the CrevassePaths found by FindCrevassePaths
415
!--------------------------------------------------------------------
416
! This involves removing duplicate nodes, taking care to replace node
417
! indexes in affected elements. This then allows easy removal of
418
! 202 elements with zero length.
419
!
420
! Closed loops are removed from crevasse paths
421
!--------------------------------------------------------------------
422
423
SUBROUTINE CheckCrevasseNodes(Mesh, CrevassePaths, Onleft, OnRight)
424
IMPLICIT NONE
425
TYPE(Mesh_t), POINTER :: Mesh
426
TYPE(CrevassePath_t), POINTER :: CrevassePaths
427
LOGICAL, OPTIONAL :: OnLeft(:),OnRight(:)
428
!-------------------------------------------------
429
TYPE(CrevassePath_t), POINTER :: CurrentPath,WorkPath
430
INTEGER :: i,j,ElNo,counter, ElementNumbers(2)
431
INTEGER, ALLOCATABLE :: ReplaceWithNode(:),WorkInt(:)
432
LOGICAL :: Debug
433
LOGICAL, ALLOCATABLE :: RemoveElement(:), RemoveNode(:), PathRemoveElement(:)
434
435
Debug = .FALSE.
436
437
ALLOCATE(RemoveNode(Mesh % NumberOfNodes),&
438
ReplaceWithNode(Mesh % NumberOfNodes),&
439
RemoveElement(Mesh % NumberOfBulkElements))
440
RemoveNode = .FALSE.
441
RemoveElement = .FALSE.
442
ReplaceWithNode = 0
443
444
!Cycle mesh NODES, looking for duplicates and marking them for deletion
445
DO i=1,Mesh % NumberOfNodes
446
IF(RemoveNode(i)) CYCLE
447
DO j=1,Mesh % NumberOfNodes
448
IF(i==j .OR. RemoveNode(j)) CYCLE
449
IF(Mesh % Nodes % x(i) == Mesh % Nodes % x(j) .AND.&
450
Mesh % Nodes % y(i) == Mesh % Nodes % y(j) .AND.&
451
Mesh % Nodes % z(i) == Mesh % Nodes % z(j)) THEN
452
RemoveNode(j) = .TRUE.
453
ReplaceWithNode(j) = i
454
END IF
455
END DO
456
END DO
457
458
!Replace element nodeindexes where nodes are removed
459
DO i=1,Mesh % NumberOfBulkElements
460
DO j=1,SIZE(Mesh % Elements(i) % NodeIndexes)
461
IF(RemoveNode(Mesh % Elements(i) % NodeIndexes(j))) THEN
462
IF(PRESENT(OnLeft) .AND. OnLeft(Mesh % Elements(i) % NodeIndexes(j))) THEN
463
OnLeft(Mesh % Elements(i) % NodeIndexes(j)) = .FALSE.
464
OnLeft(ReplaceWithNode(Mesh % Elements(i) % NodeIndexes(j))) = .TRUE.
465
END IF
466
IF(PRESENT(OnRight) .AND. OnRight(Mesh % Elements(i) % NodeIndexes(j))) THEN
467
PRINT*, 'replace', Mesh % Elements(i) % NodeIndexes(j),&
468
ReplaceWithNode(Mesh % Elements(i) % NodeIndexes(j))
469
OnRight(Mesh % Elements(i) % NodeIndexes(j)) = .FALSE.
470
OnRight(ReplaceWithNode(Mesh % Elements(i) % NodeIndexes(j))) = .TRUE.
471
END IF
472
Mesh % Elements(i) % NodeIndexes(j) = &
473
ReplaceWithNode(Mesh % Elements(i) % NodeIndexes(j))
474
END IF
475
END DO
476
END DO
477
478
!Mark elements with zero length (duplicate node indexes) for removal
479
DO i=1,Mesh % NumberOfBulkElements
480
IF(Mesh % Elements(i) % NodeIndexes(1) == Mesh % Elements(i) % NodeIndexes(2)) THEN
481
RemoveElement(i) = .TRUE.
482
IF(Debug) PRINT *,'debug, removing element: ',i,' with identical nodes: ',&
483
Mesh % Elements(i) % NodeIndexes(1)
484
END IF
485
END DO
486
487
IF(Debug) PRINT *,'Debug, removing ',COUNT(RemoveElement),' of ',SIZE(RemoveElement),' elements'
488
489
!Cycle paths, looking for nodes which are identical and removing them, joining up elements etc
490
CurrentPath => CrevassePaths
491
DO WHILE(ASSOCIATED(CurrentPath))
492
493
IF(Debug) PRINT *,'Debug, Path: ',CurrentPath % ID,'initial no elems: ',&
494
CurrentPath % NumberOfElements,&
495
' no nodes: ', CurrentPath % NumberOfNodes
496
497
ALLOCATE(WorkInt(CurrentPath % NumberOfElements))
498
WorkInt = 0
499
counter = 0
500
501
!Mark pairs of duplicate elements in path for removal
502
ALLOCATE(PathRemoveElement(CurrentPath % NumberOfElements))
503
PathRemoveElement = .FALSE.
504
505
IF(CurrentPath % NumberOfElements == 1) THEN
506
!Only has one element, remove
507
PathRemoveElement = .TRUE.
508
ELSE
509
DO i=1,CurrentPath % NumberOfElements-1
510
511
IF(PathRemoveElement(i)) CYCLE
512
ElementNumbers(1) = CurrentPath % ElementNumbers(i)
513
IF(RemoveElement(ElementNumbers(1))) CYCLE
514
515
j = i+1
516
IF(PathRemoveElement(j)) CYCLE
517
ElementNumbers(2) = CurrentPath % ElementNumbers(j)
518
IF(RemoveElement(ElementNumbers(2))) CYCLE
519
520
IF( ANY(Mesh % Elements(ElementNumbers(1)) % NodeIndexes == &
521
Mesh % Elements(ElementNumbers(2)) % NodeIndexes(1)) .AND. &
522
ANY(Mesh % Elements(ElementNumbers(1)) % NodeIndexes == &
523
Mesh % Elements(ElementNumbers(2)) % NodeIndexes(2)) ) THEN
524
PathRemoveElement(j) = .TRUE.
525
PathRemoveElement(i) = .TRUE.
526
IF(Debug) PRINT *,'Path: ',CurrentPath % ID,' removing identical elements: ',i,' ',j
527
END IF
528
529
END DO
530
531
532
!Check if entire crevasse group is a closed loop
533
ElementNumbers(1) = CurrentPath % ElementNumbers(1)
534
ElementNumbers(2) = CurrentPath % ElementNumbers(CurrentPath % NumberOfElements)
535
DO i=1,2
536
IF(.NOT. ANY(Mesh % Elements(CurrentPath % ElementNumbers(2)) % NodeIndexes == &
537
Mesh % Elements(ElementNumbers(1)) % NodeIndexes(i))) EXIT
538
END DO
539
IF(i==3) CALL Fatal("CheckCrevassePaths","Programming error: unable to determine first node")
540
IF(ANY(Mesh % Elements(ElementNumbers(2)) % NodeIndexes == &
541
Mesh % Elements(ElementNumbers(1)) % NodeIndexes(i))) THEN
542
PathRemoveElement = .TRUE.
543
IF(Debug) PRINT *,'Debug, removing path ',CurrentPath % ID,' because its entirely closed.'
544
END IF
545
546
!For each element 'i' in turn, cycle backwards through element list looking
547
!for element(i)'s nodes. If found, this indicates a closed loop which should
548
!be removed.
549
DO i=1,CurrentPath % NumberOfElements
550
IF(PathRemoveElement(i)) CYCLE
551
IF(RemoveElement(CurrentPath % ElementNumbers(i))) CYCLE
552
ElementNumbers(1) = CurrentPath % ElementNumbers(i)
553
554
DO j=CurrentPath % NumberOfElements,i+1,-1 !cycle backwards from end to i+1
555
IF(PathRemoveElement(j)) CYCLE
556
IF(RemoveElement(CurrentPath % ElementNumbers(j))) CYCLE
557
ElementNumbers(2) = CurrentPath % ElementNumbers(j)
558
559
IF( ANY(Mesh % Elements(ElementNumbers(1)) % NodeIndexes == &
560
Mesh % Elements(ElementNumbers(2)) % NodeIndexes(1)) .OR. &
561
ANY(Mesh % Elements(ElementNumbers(1)) % NodeIndexes == &
562
Mesh % Elements(ElementNumbers(2)) % NodeIndexes(2)) ) THEN
563
PathRemoveElement(i+1:j-1) = .TRUE.
564
IF(Debug) PRINT *,'CheckCrevasseNodes, &
565
&Removing a closed loop from ',i+1,' to ',j-1
566
END IF
567
568
END DO
569
570
END DO
571
END IF
572
573
!Replace CrevassePath % ElementNumbers based on previous removals
574
DO i=1,CurrentPath % NumberOfElements
575
IF(.NOT.RemoveElement(CurrentPath % ElementNumbers(i)) .AND. &
576
.NOT.PathRemoveElement(i)) THEN
577
counter = counter + 1
578
WorkInt(counter) = CurrentPath % ElementNumbers(i)
579
IF(Debug) THEN
580
PRINT *,'Debug, keeping element: ',i,' from path: ',CurrentPath % ID
581
PRINT *,'Debug, element global: ',CurrentPath % ElementNumbers(i),' and nodes :',&
582
Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes
583
END IF
584
ELSE
585
IF(Debug) THEN
586
PRINT *,'Debug, removing element: ',i,' from path: ',CurrentPath % ID
587
PRINT *,'Debug, element global: ',CurrentPath % ElementNumbers(i),' and nodes :',&
588
Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes
589
END IF
590
END IF
591
END DO
592
IF(counter < CurrentPath % NumberOfElements) THEN
593
IF(Debug) PRINT *,'debug, path loses ',CurrentPath % NumberOfElements - counter,&
594
' of ',CurrentPath % NumberOfElements,' elements.'
595
596
CurrentPath % NumberOfElements = counter
597
DEALLOCATE(CurrentPath % ElementNumbers)
598
ALLOCATE(CurrentPath % ElementNumbers(counter))
599
600
CurrentPath % ElementNumbers = WorkInt(1:counter)
601
END IF
602
DEALLOCATE(WorkInt,PathRemoveElement)
603
604
IF (CurrentPath % NumberOfElements <= 0) THEN
605
WorkPath => CurrentPath % Next
606
607
IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath
608
CALL RemoveCrevassePath(CurrentPath)
609
IF(Debug) CALL Info("CheckCrevasseNodes",&
610
"Removing a crevasse path with no elements")
611
CurrentPath => WorkPath
612
CYCLE
613
END IF
614
615
!Now reconstruct node list for path:
616
DEALLOCATE(CurrentPath % NodeNumbers)
617
CurrentPath % NumberOfNodes = CurrentPath % NumberOfElements + 1
618
ALLOCATE(CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes))
619
CurrentPath % NodeNumbers = 0
620
621
!First node
622
IF(CurrentPath % NumberOfElements >= 2) THEN
623
DO i=1,2
624
IF( ANY(Mesh % Elements(CurrentPath % ElementNumbers(2)) % NodeIndexes == &
625
Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(i))) CYCLE
626
CurrentPath % NodeNumbers(1) = &
627
Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(i)
628
629
IF(i==2) THEN !Reorder so that nodeindexes(1) and (2) are in chain order
630
Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(2) = &
631
Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(1)
632
Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(1) = &
633
CurrentPath % NodeNumbers(1)
634
END IF
635
EXIT
636
END DO
637
ELSE !Rare, single element path, choice of first node is arbitrary...
638
CurrentPath % NodeNumbers(1) = &
639
Mesh % Elements(CurrentPath % ElementNumbers(1)) % NodeIndexes(1)
640
END IF
641
642
IF(Debug) PRINT *,'Path ',CurrentPath % ID,' has first node: ',CurrentPath % NodeNumbers(1)
643
644
!Follow the chain...
645
DO i=1,CurrentPath % NumberOfElements
646
ElNo = CurrentPath % ElementNumbers(i)
647
DO j=1,2
648
IF(ANY(CurrentPath % NodeNumbers == Mesh % Elements(ElNo) % NodeIndexes(j))) CYCLE
649
CurrentPath % NodeNumbers(i+1) = Mesh % Elements(ElNo) % NodeIndexes(j)
650
651
IF(j==1) THEN !Reorder so that nodeindexes(1) and (2) are in chain order
652
Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(1) = &
653
Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(2)
654
Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(2) = &
655
CurrentPath % NodeNumbers(i+1)
656
END IF
657
658
EXIT
659
END DO
660
END DO
661
662
IF(Debug) PRINT *,'Debug, path ',CurrentPath % ID,' has nodes: ',CurrentPath % NodeNumbers
663
IF(ANY(CurrentPath % NodeNumbers == 0)) CALL Fatal("CheckCrevasseNodes","Failed to fill node indexes")
664
CurrentPath => CurrentPath % Next
665
END DO
666
667
END SUBROUTINE CheckCrevasseNodes
668
669
!----------------------------------------------------
670
! Checks paths for projectability and overlap
671
! In case of overlap, smaller enclosed path is deleted
672
! In case of unprojectability, nodes are moved laterally
673
! to restore projectability.
674
!----------------------------------------------------
675
! NOTE: if this breaks, it could be due to two paths
676
! sharing a node. Thinking about it, I see no reason
677
! this should be an issue, but we'll see...
678
SUBROUTINE ValidateCrevassePaths(Mesh, CrevassePaths, FrontOrientation, PathCount, OnLeft, OnRight, EnsureProjectible)
679
IMPLICIT NONE
680
TYPE(Mesh_t), POINTER :: Mesh
681
TYPE(CrevassePath_t), POINTER :: CrevassePaths
682
LOGICAL, OPTIONAL :: OnLeft(:),OnRight(:),EnsureProjectible
683
REAL(KIND=dp) :: FrontOrientation(3)
684
INTEGER :: PathCount, First, Last, LeftIdx, RightIdx
685
!---------------------------------------------------
686
REAL(KIND=dp) :: RotationMatrix(3,3), UnRotationMatrix(3,3), FrontDist, MaxDist, &
687
ShiftTo, Dir1(2), Dir2(2), CCW_value,a1(2),a2(2),b1(2),b2(2),intersect(2)
688
REAL(KIND=dp), ALLOCATABLE :: ConstrictDirection(:,:)
689
TYPE(CrevassePath_t), POINTER :: CurrentPath, OtherPath, WorkPath, LeftPath, RightPath
690
INTEGER :: i,j,k,n,ElNo,ShiftToMe, NodeNums(2),A,B,FirstIndex, LastIndex,Start
691
INTEGER, ALLOCATABLE :: WorkInt(:)
692
LOGICAL :: Debug, Shifted, CCW, ToLeft, Snakey, OtherRight, ShiftRightPath, &
693
DoProjectible, headland
694
LOGICAL, ALLOCATABLE :: PathMoveNode(:), DeleteElement(:), BreakElement(:), &
695
FarNode(:), DeleteNode(:), Constriction(:)
696
CHARACTER(MAX_NAME_LEN) :: FuncName="ValidateCrevassePaths"
697
698
Debug = .FALSE.
699
Snakey = .TRUE.
700
701
IF(PRESENT(EnsureProjectible)) THEN
702
DoProjectible = EnsureProjectible
703
ELSE
704
DoProjectible = .TRUE.
705
END IF
706
707
RotationMatrix = ComputeRotationMatrix(FrontOrientation)
708
UnRotationMatrix = TRANSPOSE(RotationMatrix)
709
710
! Temporarily rotate the mesh
711
CALL RotateMesh(Mesh, RotationMatrix)
712
713
! Find path %left, %right, %extent (width)
714
CALL ComputePathExtent(CrevassePaths, Mesh % Nodes, .TRUE.)
715
716
IF(PRESENT(OnLeft) .OR. PRESENT(OnRight)) THEN
717
CALL Assert((PRESENT(OnLeft) .AND. PRESENT(OnRight)), FuncName, &
718
"Provided only one of OnLeft/OnRight!")
719
720
!Check that crevasse path doesn't begin and end on same lateral margin
721
CurrentPath => CrevassePaths
722
DO WHILE(ASSOCIATED(CurrentPath))
723
!Check node OnLeft, OnRight
724
First = CurrentPath % NodeNumbers(1)
725
Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
726
IF((OnLeft(First) .AND. OnLeft(Last)) .OR. &
727
(OnRight(First) .AND. OnRight(Last))) THEN
728
CurrentPath % Valid = .FALSE.
729
END IF
730
CurrentPath => CurrentPath % Next
731
END DO
732
733
!Actually remove previous marked
734
CurrentPath => CrevassePaths
735
DO WHILE(ASSOCIATED(CurrentPath))
736
WorkPath => CurrentPath % Next
737
738
IF(.NOT. CurrentPath % Valid) THEN
739
IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath
740
CALL RemoveCrevassePath(CurrentPath)
741
IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path which &
742
&starts and ends on same margin")
743
END IF
744
CurrentPath => WorkPath
745
END DO
746
END IF
747
748
IF(Snakey) THEN
749
!-----------------------------------------------------
750
! Paths should not 'snake' inwards in a narrow slit...
751
!-----------------------------------------------------
752
753
!it's insufficient to require that no nodes be
754
!further away than the two edge nodes.
755
!Instead, must ensure that no nodes are further away than any
756
!surrounding nodes.
757
758
!First need to determine path orientation
759
!with respect to front....
760
761
CurrentPath => CrevassePaths
762
DO WHILE(ASSOCIATED(CurrentPath))
763
764
!First and last node on path
765
First = CurrentPath % NodeNumbers(1)
766
Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
767
768
!if ToLeft, the crevasse path goes from right to left, from the
769
!perspective of someone sitting in the fjord, looking at the front
770
ToLeft = Mesh % Nodes % y(Last) > Mesh % Nodes % y(First)
771
772
IF(Debug) THEN
773
FrontDist = NodeDist3D(Mesh % Nodes,First, Last)
774
PRINT *,'PATH: ', CurrentPath % ID, ' FrontDist: ',FrontDist
775
PRINT *,'PATH: ', CurrentPath % ID, &
776
' nonodes: ',CurrentPath % NumberOfNodes,&
777
' noelems: ',CurrentPath % NumberOfElements
778
END IF
779
780
!Cycle path nodes, finding those which are too far away
781
ALLOCATE(FarNode(CurrentPath % NumberOfNodes), &
782
Constriction(CurrentPath % NumberOfNodes),&
783
ConstrictDirection(CurrentPath % NumberOfNodes,2))
784
FarNode = .FALSE.
785
Constriction = .FALSE.
786
ConstrictDirection = 0.0_dp
787
788
!Determine which nodes have the potential to be constriction (based on angle)
789
!and compute constriction direction (i.e. which way the 'pointy bit' points...')
790
DO i=2,CurrentPath % NumberOfNodes-1
791
First = CurrentPath % NodeNumbers(i-1)
792
Last = CurrentPath % NodeNumbers(i+1)
793
n = CurrentPath % NodeNumbers(i)
794
795
CCW_value = ((Mesh % Nodes % y(n) - Mesh % Nodes % y(First)) * &
796
(Mesh % Nodes % z(Last) - Mesh % Nodes % z(First))) - &
797
((Mesh % Nodes % z(n) - Mesh % Nodes % z(First)) * &
798
(Mesh % Nodes % y(Last) - Mesh % Nodes % y(First)))
799
800
CCW = CCW_value > 0.0_dp
801
802
IF((CCW .NEQV. ToLeft) .AND. (ABS(CCW_value) > 10*AEPS)) THEN
803
Constriction(i) = .TRUE.
804
!Calculate constriction direction
805
806
Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(First)
807
Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(First)
808
Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)
809
810
Dir2(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)
811
Dir2(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)
812
Dir2 = Dir2 / ((Dir2(1)**2.0 + Dir2(2)**2.0) ** 0.5)
813
814
ConstrictDirection(i,1) = Dir1(1) + Dir2(1)
815
ConstrictDirection(i,2) = Dir1(2) + Dir2(2)
816
ConstrictDirection(i,:) = ConstrictDirection(i,:) / &
817
((ConstrictDirection(i,1)**2.0 + ConstrictDirection(i,2)**2.0) ** 0.5)
818
819
IF(Debug) PRINT *, 'Debug, node ',i,' dir1,2: ',Dir1, Dir2
820
IF(Debug) PRINT *, 'Debug, node ',i,' constriction direction: ',ConstrictDirection(i,:)
821
IF(Debug) PRINT *, 'Debug, node ',i,' xyz: ',Mesh % Nodes % x(n),Mesh % Nodes % y(n),Mesh % Nodes % z(n)
822
END IF
823
END DO
824
825
!First and last can always be constriction
826
Constriction(1) = .TRUE.
827
Constriction(SIZE(Constriction)) = .TRUE.
828
829
!Compute constriction direction for first and last
830
!We don't have info about the third node, so take orthogonal to 2
831
Last = CurrentPath % NodeNumbers(2)
832
n = CurrentPath % NodeNumbers(1)
833
Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)
834
Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)
835
Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)
836
837
!Depending on which end of the chain we are,
838
!we take either the right or left orthogonal vector
839
IF(ToLeft) THEN
840
ConstrictDirection(1,1) = Dir1(2)
841
ConstrictDirection(1,2) = -1.0 * Dir1(1)
842
ELSE
843
ConstrictDirection(1,1) = -1.0 * Dir1(2)
844
ConstrictDirection(1,2) = Dir1(1)
845
END IF
846
IF(Debug) PRINT *, 'Debug, node 1 constriction direction: ',ConstrictDirection(1,:)
847
848
Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes - 1)
849
n = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
850
851
Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)
852
Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)
853
Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)
854
855
IF(.NOT. ToLeft) THEN
856
ConstrictDirection(CurrentPath % NumberOfNodes,1) = Dir1(2)
857
ConstrictDirection(CurrentPath % NumberOfNodes,2) = -1.0 * Dir1(1)
858
ELSE
859
ConstrictDirection(CurrentPath % NumberOfNodes,1) = -1.0 * Dir1(2)
860
ConstrictDirection(CurrentPath % NumberOfNodes,2) = Dir1(1)
861
END IF
862
IF(Debug) PRINT *, 'Debug, node last constriction direction: ',&
863
ConstrictDirection(CurrentPath % NumberOfNodes,:)
864
865
!---------------------------------------
866
! Now that we have constrictions marked and directions computed, cycle nodes
867
868
DO i=1,CurrentPath % NumberOfNodes
869
IF(.NOT. Constriction(i)) CYCLE
870
871
DO j=CurrentPath % NumberOfNodes,i+1,-1
872
IF(.NOT. Constriction(j)) CYCLE
873
874
875
First = CurrentPath % NodeNumbers(i)
876
Last = CurrentPath % NodeNumbers(j)
877
878
!Check that these constrictions 'face' each other via dot product
879
Dir1(1) = Mesh % Nodes % y(Last) - Mesh % Nodes % y(First)
880
Dir1(2) = Mesh % Nodes % z(Last) - Mesh % Nodes % z(First)
881
Dir2(1) = -Dir1(1)
882
Dir2(2) = -Dir1(2)
883
884
!If the two constrictions aren't roughly facing each other:
885
! < > rather than > <
886
! then skip this combo
887
IF(SUM(ConstrictDirection(i,:)*Dir1) < 0) THEN
888
IF(Debug) PRINT *,'Constrictions ',i,j,' do not face each other 1: ',&
889
SUM(ConstrictDirection(i,:)*Dir1)
890
CYCLE
891
END IF
892
893
IF(SUM(ConstrictDirection(j,:)*Dir2) < 0) THEN
894
IF(Debug) PRINT *,'Constrictions ',j,i,' do not face each other 2: ',&
895
SUM(ConstrictDirection(j,:)*Dir2)
896
CYCLE
897
END IF
898
899
IF(Debug) PRINT *,'Constrictions ',i,j,' do face each other: ',&
900
SUM(ConstrictDirection(i,:)*Dir1)
901
902
!test that the line drawn between the constriction doesn't intersect
903
!any intermediate elements as this indicates
904
!crossing a headland (difficult to draw - but it's bad news)
905
!
906
! - --- ---- -
907
! \/ \ / \/
908
! ----
909
!
910
911
a1(1) = Mesh % Nodes % y(First)
912
a1(2) = Mesh % Nodes % z(First)
913
a2(1) = Mesh % Nodes % y(Last)
914
a2(2) = Mesh % Nodes % z(Last)
915
headland = .FALSE.
916
DO k=i+1,j-2
917
b1(1) = Mesh % Nodes % y(CurrentPath % NodeNumbers(k))
918
b1(2) = Mesh % Nodes % z(CurrentPath % NodeNumbers(k))
919
b2(1) = Mesh % Nodes % y(CurrentPath % NodeNumbers(k+1))
920
b2(2) = Mesh % Nodes % z(CurrentPath % NodeNumbers(k+1))
921
922
CALL LineSegmentsIntersect(a1,a2,b1,b2,intersect,headland)
923
IF(headland .AND. Debug) PRINT*, 'Headland intersect: ', 'a1', a1, &
924
'a2', a2, 'b1', b1, 'b2', b2
925
IF(headland) EXIT
926
END DO
927
IF(headland) CYCLE
928
929
MaxDist = NodeDist3D(Mesh % Nodes,First, Last)
930
931
DO k=i+1,j-1
932
IF(FarNode(k)) CYCLE
933
934
n = CurrentPath % NodeNumbers(k)
935
936
IF((NodeDist3D(Mesh % Nodes, First, n) <= MaxDist) .AND. &
937
(NodeDist3D(Mesh % Nodes, Last, n) <= MaxDist)) CYCLE !within range
938
939
FarNode(k) = .TRUE.
940
IF(Debug) PRINT *,'Far node: ',k,' xyz: ',Mesh % Nodes % x(n),&
941
Mesh % Nodes % y(n),Mesh % Nodes % z(n)
942
943
END DO
944
END DO
945
END DO
946
947
!Cycle elements, marking those which need to be adjusted
948
ALLOCATE(BreakElement(CurrentPath % NumberOfElements),&
949
DeleteElement(CurrentPath % NumberOfElements))
950
BreakElement = .FALSE.
951
DeleteElement = .FALSE.
952
953
DO i=1,CurrentPath % NumberOfElements
954
IF(ANY(FarNode(i:i+1))) THEN
955
IF(ALL(FarNode(i:i+1))) THEN
956
DeleteElement(i) = .TRUE.
957
IF(Debug) PRINT *,'PATH: ', CurrentPath % ID, ' element: ',i,' is deleted.'
958
ELSE
959
BreakElement(i) = .TRUE.
960
IF(Debug) PRINT *,'PATH: ', CurrentPath % ID, ' element: ',i,' is broken.'
961
END IF
962
END IF
963
END DO
964
965
DO i=1,CurrentPath % NumberOfElements
966
IF((.NOT. BreakElement(i)) .OR. DeleteElement(i)) CYCLE
967
!This element needs to be adjusted
968
DO j=i+1,CurrentPath % NumberOfElements
969
IF(.NOT. (BreakElement(j) .OR. DeleteElement(j))) &
970
CALL Fatal("ValidateCrevasseGroups","Programming error in maintaining aspect ratio")
971
IF(DeleteElement(j)) CYCLE
972
!This is the next 'break element' after i
973
!Determine which nodes we keep
974
975
IF((CurrentPath % NodeNumbers(j) /= &
976
Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(1)) .OR. &
977
(CurrentPath % NodeNumbers(j+1) /= &
978
Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(2))) THEN
979
980
CALL Fatal("ValidateCrevassePaths", "Chain building error")
981
END IF
982
983
Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(2) = &
984
Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(2)
985
986
!We now want to delete it, because we only keep one from each broken pair
987
DeleteElement(j) = .TRUE.
988
EXIT !we paired this one, move on
989
END DO
990
END DO
991
992
!Delete the elements and nodes
993
IF(COUNT(DeleteElement) > 0) THEN
994
!elements
995
ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))
996
WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)
997
998
DEALLOCATE(CurrentPath % ElementNumbers)
999
ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))
1000
1001
CurrentPath % ElementNumbers = WorkInt
1002
CurrentPath % NumberOfElements = SIZE(WorkInt)
1003
DEALLOCATE(WorkInt)
1004
1005
!nodes
1006
ALLOCATE(WorkInt(COUNT(.NOT. FarNode)))
1007
WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.FarNode)
1008
1009
DEALLOCATE(CurrentPath % NodeNumbers)
1010
ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))
1011
1012
CurrentPath % NodeNumbers = WorkInt
1013
CurrentPath % NumberOfNodes = SIZE(WorkInt)
1014
DEALLOCATE(WorkInt)
1015
END IF
1016
1017
DEALLOCATE(FarNode, Constriction, ConstrictDirection, BreakElement, DeleteElement)
1018
CurrentPath => CurrentPath % Next
1019
END DO
1020
END IF !Snakey
1021
1022
!Update Left, Right & Extent
1023
CALL ComputePathExtent(CrevassePaths, Mesh % Nodes, .TRUE.)
1024
1025
!-----------------------------------------------------
1026
! Move nodes from crevassepaths which aren't projectable
1027
!-----------------------------------------------------
1028
! 1) Path elements are ordered as a chain
1029
! 2) Path % Element(i) has nodes i, i+1
1030
!
1031
! Go through CrevassePath nodes, marking those
1032
! which are 'shadowed' by further away elements.
1033
!-----------------------------------------------------
1034
1035
IF(DoProjectible) THEN
1036
CurrentPath => CrevassePaths
1037
DO WHILE(ASSOCIATED(CurrentPath))
1038
1039
ALLOCATE(PathMoveNode(CurrentPath % NumberOfNodes))
1040
PathMoveNode = .FALSE.
1041
1042
DO i=1,CurrentPath % NumberOfNodes
1043
n = CurrentPath % NodeNumbers(i)
1044
DO j=1,CurrentPath % NumberOfElements
1045
ElNo = CurrentPath % ElementNumbers(j)
1046
NodeNums = Mesh % Elements(ElNo) % NodeIndexes
1047
IF(ANY(NodeNums == n)) CYCLE !Node is in element, skip
1048
!Check if node lies between element nodes
1049
IF( (Mesh % Nodes % y(NodeNums(1)) > Mesh % Nodes % y(n)) .NEQV. &
1050
(Mesh % Nodes % y(NodeNums(2)) > Mesh % Nodes % y(n)) ) THEN
1051
!Check the node is in front of the element
1052
1053
A = MINLOC(Mesh % Nodes % z(NodeNums),1)
1054
B = MAXLOC(Mesh % Nodes % z(NodeNums),1)
1055
CCW = ((Mesh % Nodes % y(n) - Mesh % Nodes % y(NodeNums(A))) * &
1056
(Mesh % Nodes % z(NodeNums(B)) - Mesh % Nodes % z(NodeNums(A)))) > &
1057
((Mesh % Nodes % z(n) - Mesh % Nodes % z(NodeNums(A))) * &
1058
(Mesh % Nodes % y(NodeNums(B)) - Mesh % Nodes % y(NodeNums(A))))
1059
1060
ToLeft = Mesh % Nodes % y(NodeNums(A)) > Mesh % Nodes % y(NodeNums(B))
1061
1062
IF(CCW .EQV. ToLeft) THEN
1063
!Node should be removed
1064
PathMoveNode(i) = .TRUE.
1065
EXIT
1066
END IF
1067
1068
END IF
1069
END DO
1070
END DO
1071
1072
IF(Debug) THEN
1073
PRINT *,'Path ',CurrentPath % ID,' has ',&
1074
COUNT(PathMoveNode),' nodes which need to be shifted.'
1075
1076
DO i=1,CurrentPath % NumberOfNodes
1077
IF(.NOT. PathMoveNode(i)) CYCLE
1078
PRINT *,'Need to move node: ',i,' y: ',&
1079
Mesh % Nodes % y(CurrentPath % NodeNumbers(i)),&
1080
' z: ',Mesh % Nodes % z(CurrentPath % NodeNumbers(i))
1081
1082
END DO
1083
END IF
1084
1085
!Now that nodes have been marked as shadowed, identify chains
1086
!and the location of the node to which these groups of nodes should be moved.
1087
Shifted = .TRUE.
1088
Start = 1
1089
DO WHILE(Shifted)
1090
Shifted = .FALSE.
1091
1092
DO i=Start,CurrentPath % NumberOfNodes
1093
IF(PathMoveNode(i)) THEN
1094
IF(.NOT. Shifted) THEN
1095
Shifted = .TRUE.
1096
FirstIndex = i
1097
END IF
1098
LastIndex = i
1099
ELSE
1100
IF(Shifted) EXIT
1101
END IF
1102
END DO
1103
IF(.NOT. Shifted) EXIT
1104
1105
!We have identified a chain from FirstIndex to LastIndex which need to be moved.
1106
!They should be moved to either FirstIndex-1 or LastIndex+1
1107
!(Whichever is further back)
1108
!Note special case at start and end of path
1109
IF(FirstIndex == 1) THEN
1110
ShiftToMe = CurrentPath % NodeNumbers(LastIndex+1)
1111
ELSE IF(LastIndex == CurrentPath % NumberOfNodes) THEN
1112
ShiftToMe = CurrentPath % NodeNumbers(FirstIndex-1)
1113
ELSE IF(Mesh % Nodes % z(CurrentPath % NodeNumbers(FirstIndex-1)) <&
1114
Mesh % Nodes % z(CurrentPath % NodeNumbers(LastIndex+1))) THEN
1115
ShiftToMe = CurrentPath % NodeNumbers(FirstIndex-1)
1116
ELSE
1117
ShiftToMe = CurrentPath % NodeNumbers(LastIndex+1)
1118
END IF
1119
1120
Mesh % Nodes % y(CurrentPath % NodeNumbers(FirstIndex:LastIndex)) = &
1121
Mesh % Nodes % y(ShiftToMe)
1122
1123
IF(Debug) PRINT *,'Shifting nodes ',FirstIndex,' to ',LastIndex,&
1124
' to point: ',Mesh % Nodes % y(ShiftToMe)
1125
Start = LastIndex + 1
1126
END DO
1127
1128
DEALLOCATE(PathMoveNode)
1129
CurrentPath => CurrentPath % Next
1130
END DO
1131
END IF !DoProjectible
1132
1133
!NOTE: probably not really necessary here, Shifted nodes don't extend
1134
!the extent
1135
!Update Left, Right & Extent
1136
CALL ComputePathExtent(CrevassePaths, Mesh % Nodes, .TRUE.)
1137
1138
!--------------------------------------------------------
1139
! Remove crevassepaths which are contained within others.
1140
!--------------------------------------------------------
1141
! 1) All crevasse paths start and end on the calving front
1142
! or lateral margin.
1143
! 2) Crevasse paths can't cross each other.
1144
!
1145
! Thus, iff a crevasse path is surrounded laterally by
1146
! another single crevasse path, we remove it, because
1147
! it must be contained by the larger one.
1148
!--------------------------------------------------------
1149
1150
CurrentPath => CrevassePaths
1151
DO WHILE(ASSOCIATED(CurrentPath))
1152
1153
OtherPath => CrevassePaths
1154
DO WHILE(ASSOCIATED(OtherPath))
1155
IF(ASSOCIATED(OtherPath, CurrentPath)) THEN
1156
OtherPath => OtherPath % Next
1157
CYCLE
1158
END IF
1159
1160
IF((CurrentPath % Left >= OtherPath % Left) .AND. &
1161
(CurrentPath % Right <= OtherPath % Right)) THEN!contained within
1162
CurrentPath % Valid = .FALSE.
1163
IF(Debug) PRINT *,'Debug, marked path ',CurrentPath % ID,' for deletion &
1164
&because its contained within path ',OtherPath % ID
1165
END IF
1166
OtherPath => OtherPath % Next
1167
END DO
1168
1169
CurrentPath => CurrentPath % Next
1170
END DO
1171
1172
!Actually remove previous marked
1173
CurrentPath => CrevassePaths
1174
DO WHILE(ASSOCIATED(CurrentPath))
1175
WorkPath => CurrentPath % Next
1176
1177
IF(.NOT. CurrentPath % Valid) THEN
1178
IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath
1179
CALL RemoveCrevassePath(CurrentPath)
1180
IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path")
1181
END IF
1182
CurrentPath => WorkPath
1183
END DO
1184
1185
!-------------------------------------------------
1186
! Check for paths partly obscuring each other
1187
! (fully obscured are dealt with above)
1188
!-------------------------------------------------
1189
! If paths partially overlap, the overlapping nodes
1190
! of whichever path is seaward are moved.
1191
! i.e. the larger calving event takes precedent
1192
!-------------------------------------------------
1193
1194
IF(DoProjectible) THEN
1195
CurrentPath => CrevassePaths
1196
DO WHILE(ASSOCIATED(CurrentPath))
1197
1198
OtherPath => CrevassePaths
1199
DO WHILE(ASSOCIATED(OtherPath))
1200
IF(ASSOCIATED(OtherPath, CurrentPath)) THEN
1201
OtherPath => OtherPath % Next
1202
CYCLE
1203
END IF
1204
1205
IF((CurrentPath % Left < OtherPath % Right) .EQV. &
1206
(OtherPath % Left < CurrentPath % Right)) THEN !overlap
1207
1208
IF(Debug) PRINT *,'Debug, paths: ',CurrentPath % ID, OtherPath % ID,' partially overlap'
1209
1210
!Is the other path to the right or left?
1211
OtherRight = CurrentPath % Right < OtherPath % Right
1212
1213
!Check not fully contained - should have been dealt with above
1214
IF((CurrentPath % Right > OtherPath % Right) .NEQV. &
1215
(CurrentPath % Left > OtherPath % Left)) THEN
1216
CALL Warn("ValidateCrevassePaths","Encountered full overlap which &
1217
&should already have been taken care of! OK if this is rare, &
1218
&otherwise maybe programming error")
1219
END IF
1220
1221
IF(OtherRight) THEN
1222
RightPath => OtherPath
1223
LeftPath => CurrentPath
1224
ELSE
1225
RightPath => CurrentPath
1226
LeftPath => OtherPath
1227
END IF
1228
1229
!Find the left and rightmost nodes of the two paths
1230
DO i=1,LeftPath % NumberOfNodes
1231
IF(Debug) PRINT *,'Debug, node ',i,' of leftpath: ',&
1232
Mesh % Nodes % y(LeftPath % NodeNumbers(i)), LeftPath % Right
1233
1234
IF(Mesh % Nodes % y(LeftPath % NodeNumbers(i)) >= LeftPath % Right) LeftIdx = i
1235
END DO
1236
1237
DO i=1,RightPath % NumberOfNodes
1238
IF(Debug) PRINT *,'Debug, node ',i,' of rightpath: ',&
1239
Mesh % Nodes % y(RightPath % NodeNumbers(i)), RightPath % Left
1240
1241
IF(Mesh % Nodes % y(RightPath % NodeNumbers(i)) <= RightPath % Left) RightIdx = i
1242
END DO
1243
1244
!See which is further forward.
1245
ShiftRightPath = Mesh % Nodes % z(LeftPath % NodeNumbers(LeftIdx)) < &
1246
Mesh % Nodes % z(RightPath % NodeNumbers(RightIdx))
1247
1248
IF(ShiftRightPath) THEN
1249
ShiftTo = Mesh % Nodes % y(LeftPath % NodeNumbers(LeftIdx))
1250
DO i=1,RightPath % NumberOfNodes
1251
IF(Mesh % Nodes % y(RightPath % NodeNumbers(i)) < ShiftTo) THEN
1252
IF(Debug) PRINT *,'Debug, overlap shifting right node ',i,' path '&
1253
,RightPath % ID,' from ', Mesh % Nodes % y(RightPath % NodeNumbers(i)),&
1254
' to ',ShiftTo
1255
Mesh % Nodes % y(RightPath % NodeNumbers(i)) = ShiftTo
1256
END IF
1257
END DO
1258
CALL ComputePathExtent(RightPath, Mesh % Nodes, .FALSE.)
1259
1260
ELSE
1261
ShiftTo = Mesh % Nodes % y(RightPath % NodeNumbers(RightIdx))
1262
DO i=1,LeftPath % NumberOfNodes
1263
IF(Mesh % Nodes % y(LeftPath % NodeNumbers(i)) > ShiftTo) THEN
1264
IF(Debug) PRINT *,'Debug, overlap shifting left node ',i,' path ',&
1265
LeftPath % ID,' from ',Mesh % Nodes % y(LeftPath % NodeNumbers(i)),&
1266
' to ',ShiftTo
1267
Mesh % Nodes % y(LeftPath % NodeNumbers(i)) = ShiftTo
1268
END IF
1269
END DO
1270
CALL ComputePathExtent(LeftPath, Mesh % Nodes, .FALSE.)
1271
1272
END IF
1273
END IF
1274
1275
OtherPath => OtherPath % Next
1276
END DO
1277
1278
CurrentPath => CurrentPath % Next
1279
END DO
1280
1281
!-----------------------------------------------------------------------
1282
! Remove elements whose nodes are in a vertical line
1283
! (to prevent potential issues in interp)
1284
!-----------------------------------------------------------------------
1285
! This occurs due to the shifting which occurs above.
1286
! NOTE: This breaks the assumption that element(i) has nodes (i) & (i+1)
1287
! It also breaks the chain! Currently OK but don't rely on this below this
1288
! point, or in Calving3D.F90
1289
!-----------------------------------------------------------------------
1290
1291
CurrentPath => CrevassePaths
1292
DO WHILE(ASSOCIATED(CurrentPath))
1293
1294
ALLOCATE(DeleteElement(CurrentPath % NumberOfElements),&
1295
DeleteNode(CurrentPath % NumberOfNodes))
1296
DeleteElement = .FALSE.
1297
DeleteNode = .FALSE.
1298
1299
DO i=1,CurrentPath % NumberOfElements
1300
!Element i is composed of nodes i,i+1
1301
IF(Mesh % Nodes % y(CurrentPath % NodeNumbers(i)) == &
1302
Mesh % Nodes % y(CurrentPath % NodeNumbers(i+1))) THEN
1303
DeleteElement(i) = .TRUE.
1304
IF(Debug) PRINT *,'Debug, deleting element: ',i,' from path: ',&
1305
CurrentPath % ID,' because its a straight line'
1306
END IF
1307
END DO
1308
1309
IF(DeleteElement(1)) DeleteNode(1) = .TRUE.
1310
IF(DeleteElement(SIZE(DeleteElement))) DeleteNode(SIZE(DeleteNode)) = .TRUE.
1311
1312
DO i=2,CurrentPath % NumberOfNodes-1
1313
IF(DeleteElement(i-1) .AND. DeleteElement(i)) DeleteNode(i) = .TRUE.
1314
END DO
1315
1316
!Delete them
1317
IF(COUNT(DeleteElement) > 0) THEN
1318
!elements
1319
ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))
1320
WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)
1321
1322
DEALLOCATE(CurrentPath % ElementNumbers)
1323
ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))
1324
1325
CurrentPath % ElementNumbers = WorkInt
1326
CurrentPath % NumberOfElements = SIZE(WorkInt)
1327
DEALLOCATE(WorkInt)
1328
1329
!nodes
1330
ALLOCATE(WorkInt(COUNT(.NOT. DeleteNode)))
1331
WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.DeleteNode)
1332
1333
DEALLOCATE(CurrentPath % NodeNumbers)
1334
ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))
1335
1336
CurrentPath % NodeNumbers = WorkInt
1337
CurrentPath % NumberOfNodes = SIZE(WorkInt)
1338
DEALLOCATE(WorkInt)
1339
END IF
1340
1341
DEALLOCATE(DeleteElement, DeleteNode)
1342
CurrentPath => CurrentPath % Next
1343
END DO
1344
1345
END IF !DoProjectible
1346
1347
!--------------------------------------------------------
1348
! Put the mesh back
1349
!--------------------------------------------------------
1350
CALL RotateMesh(Mesh, UnRotationMatrix)
1351
1352
END SUBROUTINE ValidateCrevassePaths
1353
1354
!Calculates the left and rightmost extent, and the difference (width) of
1355
!Path, given the node locations in Nodes.
1356
SUBROUTINE ComputePathExtent(CrevassePaths, Nodes, DoAll)
1357
TYPE(CrevassePath_t), POINTER :: CrevassePaths
1358
TYPE(Nodes_t), POINTER :: Nodes
1359
LOGICAL :: DoAll
1360
!-----------------------------------------------
1361
TYPE(CrevassePath_t), POINTER :: CurrentPath
1362
INTEGER :: n
1363
1364
CurrentPath => CrevassePaths
1365
DO WHILE(ASSOCIATED(CurrentPath))
1366
CurrentPath % Left = HUGE(1.0_dp)
1367
CurrentPath % Right = -1.0*HUGE(1.0_dp)
1368
1369
n = CurrentPath % NumberOfNodes
1370
1371
CurrentPath % Left = MINVAL(Nodes % y(CurrentPath % NodeNumbers))
1372
CurrentPath % Right = MAXVAL(Nodes % y(CurrentPath % NodeNumbers))
1373
1374
CurrentPath % Extent = CurrentPath % Right - CurrentPath % Left
1375
1376
CurrentPath => CurrentPath % Next
1377
1378
IF(.NOT. DoAll) EXIT
1379
END DO
1380
1381
END SUBROUTINE ComputePathExtent
1382
1383
!-----------------------------------------------------------------------------
1384
! Returns the Path ID of the CrevassePath_t which contains the given element
1385
! 0 if not found
1386
!-----------------------------------------------------------------------------
1387
FUNCTION ElementPathID(CrevassePaths, ElementNo) RESULT(ID)
1388
TYPE(CrevassePath_t), POINTER :: CrevassePaths
1389
INTEGER :: ElementNo, ID
1390
!----------------------------------------------
1391
TYPE(CrevassePath_t), POINTER :: CurrentPath
1392
1393
ID = 0
1394
1395
CurrentPath => CrevassePaths
1396
DO WHILE(ASSOCIATED(CurrentPath))
1397
IF(ASSOCIATED(CurrentPath % ElementNumbers)) THEN
1398
IF(ANY(CurrentPath % ElementNumbers == ElementNo)) THEN
1399
ID = CurrentPath % ID
1400
EXIT
1401
END IF
1402
END IF
1403
CurrentPath => CurrentPath % Next
1404
END DO
1405
1406
END FUNCTION ElementPathID
1407
1408
!--------------------------------------------------------------------------
1409
!tests if a point is Left|On|Right of an infinite line.
1410
! Input: three points a, b, and c
1411
! Return: >0 for c left of the line AB
1412
! =0 for c on the line AB
1413
! <0 for c right of the line AB
1414
! used for winding number algorithm
1415
!---------------------------------------------------------------------------
1416
FUNCTION IsLeft(a, b, c) RESULT(d)
1417
REAL(kind=dp) :: a(2), b(2), c(2), d
1418
1419
d = (b(1)-a(1)) * (c(2)-a(2)) - &
1420
(c(1)-a(1)) * (b(2)-a(2))
1421
1422
END FUNCTION Isleft
1423
1424
!----------------------------------------------------------------------------
1425
! point in polygon - winding number algorithm
1426
!
1427
! input a polygon where polygon(1) = polygon(n) where n = SIZE(polygon)
1428
!----------------------------------------------------------------------------
1429
1430
FUNCTION PointInPolygon2D(Polygon, Point, buffer) RESULT(inside)
1431
REAL(kind=dp) :: polygon(:,:)
1432
REAL(kind=dp), ALLOCATABLE :: ZPolygon(:,:)
1433
REAL(kind=dp) :: left, point(2), ZPoint(2), buf
1434
REAL(kind=dp), OPTIONAL :: buffer
1435
INTEGER :: n, i, windingnumber
1436
LOGICAL :: inside
1437
1438
IF(SIZE(polygon(:,1)) /= 2) CALL FATAL('PointInPolygon2D', 'Please provide a 2D array with x and y coords')
1439
IF(PRESENT(buffer)) THEN
1440
buf = buffer
1441
ELSE
1442
buf = 0.0_dp
1443
END IF
1444
1445
n=SIZE(polygon(1,:))
1446
1447
ZPoint = Point
1448
ALLOCATE(ZPolygon(2,n))
1449
ZPolygon = Polygon
1450
CALL ZeroPolygon(ZPolygon, ZPoint)
1451
1452
windingnumber=100
1453
DO i=1, n-1
1454
! polygon y i <= point y
1455
IF(ZPolygon(2,i) <= ZPoint(2) + buf) THEN !start with y<=P.y
1456
IF(ZPolygon(2, i+1) > ZPoint(2) - buf) THEN !upward crossing
1457
left=IsLeft(ZPolygon(:, i), ZPolygon(:, i+1), ZPoint(:))
1458
IF(left > buf) THEN !p is to left of intersect
1459
windingnumber=windingnumber+1 !valid up intersect
1460
END IF
1461
END IF
1462
ELSE !start at y> point y
1463
IF(ZPolygon(2, i+1) <= ZPoint(2) + buf) THEN ! downward crossing
1464
Left = IsLeft(ZPolygon(:, i), ZPolygon(:, i+1), ZPoint(:))
1465
IF(left < buf) THEN ! p right of edge
1466
windingnumber=windingnumber-1
1467
END IF
1468
END IF
1469
END IF
1470
END DO
1471
1472
IF(windingnumber /= 100) THEN
1473
inside = .TRUE.
1474
ELSE
1475
inside = .FALSE.
1476
END IF
1477
1478
END FUNCTION PointInPolygon2D
1479
1480
!----------------------------------------------------------------------------
1481
! zeros polygon to reduce floating point errors in PointInPolygon2D
1482
!----------------------------------------------------------------------------
1483
1484
SUBROUTINE ZeroPolygon(Polygon, Point)
1485
REAL(kind=dp) :: Polygon(:,:), Point(2)
1486
REAL(kind=dp) :: minx, miny
1487
1488
minx = MINVAL(Polygon(1,:))
1489
miny = MINVAL(Polygon(2,:))
1490
1491
Polygon(1,:) = Polygon(1,:) - minx
1492
Polygon(2,:) = Polygon(2,:) - miny
1493
1494
Point(1) = Point(1) - minx
1495
Point(2) = Point(2) - miny
1496
1497
END SUBROUTINE ZeroPolygon
1498
1499
!-----------------------------------------------------------------------------
1500
! Constructs groups of nodes which fall below a given threshold for some variable
1501
! Used with the result of ProjectCalving, it groups nodes which have crevasse
1502
! penetration beyond the threshold.
1503
!-----------------------------------------------------------------------------
1504
SUBROUTINE FindCrevasseGroups(Mesh, Variable, Neighbours, Threshold, Groups)
1505
IMPLICIT NONE
1506
1507
TYPE(Mesh_t), POINTER :: Mesh
1508
TYPE(Variable_t), POINTER :: Variable
1509
INTEGER, POINTER :: Neighbours(:,:)
1510
TYPE(CrevasseGroup3D_t), POINTER :: Groups, CurrentGroup
1511
REAL(KIND=dp) :: Threshold
1512
!---------------------------------------
1513
INTEGER :: i, ID
1514
REAL(KIND=dp), POINTER :: Values(:)
1515
INTEGER, POINTER :: VPerm(:)
1516
INTEGER, ALLOCATABLE :: WorkInt(:)
1517
LOGICAL, ALLOCATABLE :: Condition(:)
1518
LOGICAL :: First, Debug
1519
1520
Debug = .FALSE.
1521
1522
Values => Variable % Values
1523
VPerm => Variable % Perm
1524
1525
ALLOCATE(Condition(Mesh % NumberOfNodes))
1526
DO i=1, Mesh % NumberOfNodes
1527
1528
IF(VPerm(i) <= 0) THEN
1529
Condition(i) = .FALSE.
1530
ELSE IF(Values(VPerm(i)) < Threshold) THEN
1531
Condition(i) = .TRUE.
1532
ELSE
1533
Condition(i) = .FALSE.
1534
END IF
1535
1536
END DO
1537
1538
First = .TRUE.
1539
ID = 1
1540
DO i=1,Mesh % NumberOfNodes
1541
IF(.NOT. Condition(i)) CYCLE
1542
1543
IF(Debug) PRINT *,'PE:', ParEnv % MyPE,' debug, new group'
1544
1545
IF(First) THEN
1546
ALLOCATE(CurrentGroup)
1547
Groups => CurrentGroup
1548
First = .FALSE.
1549
ELSE
1550
ALLOCATE(CurrentGroup % Next)
1551
CurrentGroup % Next % Prev => CurrentGroup
1552
CurrentGroup => CurrentGroup % Next
1553
END IF
1554
1555
CurrentGroup % ID = ID
1556
ID = ID + 1
1557
1558
ALLOCATE(CurrentGroup % NodeNumbers(500))
1559
CurrentGroup % NumberOfNodes = 1
1560
1561
!Add node to group and switch it off
1562
CurrentGroup % NodeNumbers(CurrentGroup % NumberOfNodes) = i
1563
Condition(i) = .FALSE.
1564
1565
!Search neighbours
1566
CALL SearchNeighbours(i, Neighbours, CurrentGroup, Condition)
1567
1568
ALLOCATE(WorkInt(CurrentGroup % NumberOfNodes))
1569
WorkInt = CurrentGroup % NodeNumbers(1:CurrentGroup % NumberOfNodes)
1570
DEALLOCATE(CurrentGroup % NodeNumbers)
1571
ALLOCATE(CurrentGroup % NodeNumbers(CurrentGroup % NumberOfNodes))
1572
CurrentGroup % NodeNumbers = WorkInt
1573
DEALLOCATE(WorkInt)
1574
1575
CALL UpdateCGrpBB(CurrentGroup, Mesh)
1576
END DO
1577
1578
IF(Debug) THEN
1579
CurrentGroup => Groups
1580
i=1
1581
DO WHILE(ASSOCIATED(CurrentGroup))
1582
PRINT *,'group: ',i,' has ', CurrentGroup % NumberOfNodes,' nodes.'
1583
i = i + 1
1584
CurrentGroup => CurrentGroup % Next
1585
END DO
1586
END IF
1587
END SUBROUTINE FindCrevasseGroups
1588
1589
SUBROUTINE DeallocateCrevasseGroup(CGrp)
1590
TYPE(CrevasseGroup3D_t), POINTER :: CGrp
1591
1592
IF(ASSOCIATED(CGrp % Next)) CGrp % Next % Prev => CGrp % Prev
1593
IF(ASSOCIATED(CGrp % Prev)) CGrp % Prev % Next => CGrp % Next
1594
1595
IF(ASSOCIATED(CGrp % NodeNumbers)) DEALLOCATE(CGrp % NodeNumbers)
1596
IF(ASSOCIATED(CGrp % FrontNodes)) DEALLOCATE(CGrp % FrontNodes)
1597
IF(ASSOCIATED(CGrp % BoundaryNodes)) DEALLOCATE(CGrp % BoundaryNodes)
1598
1599
DEALLOCATE(CGrp)
1600
1601
END SUBROUTINE DeallocateCrevasseGroup
1602
1603
!Update the Bounding Box of a CrevasseGroup
1604
SUBROUTINE UpdateCGrpBB(CGrp, Mesh)
1605
TYPE(CrevasseGroup3D_t), POINTER :: CGrp
1606
TYPE(Mesh_t), POINTER :: Mesh
1607
1608
CGrp % BoundingBox(1) = MINVAL(Mesh % Nodes % x(CGrp % NodeNumbers))
1609
CGrp % BoundingBox(2) = MAXVAL(Mesh % Nodes % x(CGrp % NodeNumbers))
1610
CGrp % BoundingBox(3) = MINVAL(Mesh % Nodes % y(CGrp % NodeNumbers))
1611
CGrp % BoundingBox(4) = MAXVAL(Mesh % Nodes % y(CGrp % NodeNumbers))
1612
1613
END SUBROUTINE UpdateCGrpBB
1614
1615
!Add a list of points to a CrevasseGroup3D object
1616
!Don't need to pass the mesh because we're just adding
1617
!point indices
1618
SUBROUTINE AddNodesToGroup(Group, Points, PointCount)
1619
TYPE(CrevasseGroup3D_t), POINTER :: Group
1620
INTEGER :: Points(:)
1621
INTEGER, POINTER :: NewNodeNumbers(:)
1622
INTEGER :: PointCount, NewNumberOfNodes
1623
1624
NewNumberOfNodes = Group % NumberOfNodes + PointCount
1625
ALLOCATE(NewNodeNumbers(NewNumberOfNodes))
1626
1627
NewNodeNumbers(1:Group % NumberOfNodes) = Group % NodeNumbers
1628
NewNodeNumbers(Group % NumberOfNodes+1:NewNumberOfNodes) = Points(1:PointCount)
1629
1630
!Update count
1631
Group % NumberOfNodes = NewNumberOfNodes
1632
1633
!Point Group to new node list
1634
DEALLOCATE(Group % NodeNumbers)
1635
Group % NodeNumbers => NewNodeNumbers
1636
NULLIFY(NewNodeNumbers)
1637
END SUBROUTINE AddNodesToGroup
1638
1639
!------------------------------------------------------------
1640
! Routine to recursively search neighbours and put them
1641
! in the current group
1642
! Adapted from 2D Calving
1643
!------------------------------------------------------------
1644
RECURSIVE SUBROUTINE SearchNeighbours(nodenum, Neighbours, Group, Condition)
1645
INTEGER :: nodenum
1646
INTEGER, POINTER :: Neighbours(:,:)
1647
TYPE(CrevasseGroup3D_t), POINTER :: Group
1648
LOGICAL, ALLOCATABLE :: Condition(:)
1649
!------------------------------------------------
1650
INTEGER :: i, neighbourindex, NoNeighbours
1651
1652
NoNeighbours = COUNT(Neighbours(nodenum,:) > 0)
1653
DO i = 1,NoNeighbours
1654
neighbourindex = Neighbours(nodenum,i)
1655
IF(.NOT. Condition(neighbourindex)) CYCLE
1656
1657
Group % NumberOfNodes = Group % NumberOfNodes + 1
1658
1659
!check space
1660
IF(Group % NumberOfNodes > SIZE(Group % NodeNumbers)) THEN
1661
PRINT *, 'Debug, need more space, allocating: ', 2*SIZE(Group % NodeNumbers)
1662
CALL DoubleIntVectorSize(Group % NodeNumbers)
1663
PRINT *, 'Debug, new size: ', SIZE(Group % NodeNumbers)
1664
END IF
1665
1666
Group % NodeNumbers(Group % NumberOfNodes) = neighbourindex
1667
1668
!Switch it off so it doesn't get readded
1669
Condition(neighbourindex) = .FALSE.
1670
1671
CALL SearchNeighbours(neighbourindex, Neighbours, Group, Condition)
1672
END DO
1673
1674
END SUBROUTINE SearchNeighbours
1675
1676
!Marks recursive neighbours with same int
1677
RECURSIVE SUBROUTINE MarkNeighbours(nodenum, Neighbours, Array, Mark)
1678
INTEGER :: nodenum
1679
INTEGER, POINTER :: Array(:)
1680
LOGICAL, POINTER :: Neighbours(:,:)
1681
!------------------------------------------------
1682
INTEGER :: i, Mark
1683
1684
DO i = 1,SIZE(Neighbours,1)
1685
IF(.NOT. Neighbours(nodenum,i)) CYCLE
1686
IF(Array(i)==Mark) CYCLE !already got
1687
1688
Array(i) = Mark
1689
CALL MarkNeighbours(i, Neighbours, Array, Mark)
1690
END DO
1691
1692
END SUBROUTINE MarkNeighbours
1693
1694
!-------------------------------------------------------------
1695
! Given a CrevasseGroup3D object, finds and stores boundary nodes
1696
! BoundaryMask is a logical array TRUE where node sits on a
1697
! mesh (not group) boundary
1698
! Note: Not used
1699
!-------------------------------------------------------------
1700
SUBROUTINE GetGroupBoundaryNodes(Group, Neighbours, BoundaryMask)
1701
TYPE(CrevasseGroup3D_t), POINTER :: Group
1702
INTEGER, POINTER :: Neighbours(:,:)
1703
LOGICAL :: BoundaryMask(:)
1704
!-----------------------------------------
1705
INTEGER :: i, j, node, BNodes, NoNeighbours, neighbour
1706
INTEGER, ALLOCATABLE :: WorkInt(:)
1707
LOGICAL :: IsBoundaryNode
1708
1709
IF(ASSOCIATED(Group % BoundaryNodes)) &
1710
DEALLOCATE(Group % BoundaryNodes)
1711
1712
ALLOCATE(Group % BoundaryNodes(100))
1713
Group % BoundaryNodes = 0
1714
BNodes = 0
1715
1716
DO i=1, Group % NumberOfNodes
1717
IsBoundaryNode = .FALSE.
1718
node = Group % NodeNumbers(i)
1719
1720
IF(BoundaryMask(node)) THEN
1721
IsBoundaryNode = .TRUE.
1722
ELSE
1723
NoNeighbours = COUNT(Neighbours(node, :) > 0)
1724
DO j=1,NoNeighbours
1725
neighbour = Neighbours(node, j)
1726
IF(ANY(Group % NodeNumbers == neighbour)) CYCLE
1727
1728
!Only get here if there's a node NOT in the group
1729
IsBoundaryNode = .TRUE.
1730
EXIT
1731
END DO
1732
END IF
1733
1734
IF(IsBoundaryNode) THEN
1735
BNodes = BNodes + 1
1736
IF(BNodes > SIZE(Group % BoundaryNodes)) &
1737
CALL DoubleIntVectorSize(Group % BoundaryNodes)
1738
Group % BoundaryNodes(BNodes) = node
1739
END IF
1740
END DO
1741
1742
ALLOCATE(WorkInt(BNodes))
1743
WorkInt = Group % BoundaryNodes(1:BNodes)
1744
DEALLOCATE(Group % BoundaryNodes)
1745
ALLOCATE(Group % BoundaryNodes(BNodes))
1746
Group % BoundaryNodes = WorkInt
1747
DEALLOCATE(WorkInt)
1748
1749
!TODO: Order boundary nodes (clockwise?)
1750
END SUBROUTINE GetGroupBoundaryNodes
1751
1752
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1753
! Function to detect if a given node lies within
1754
! a 3D crevasse group (physically, not 'graph'ically
1755
! Note: not used...
1756
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1757
FUNCTION NodeInCrevasseGroup(NodeNumber, Nodes, CrevasseGroup) RESULT(InGroup)
1758
INTEGER :: NodeNumber
1759
TYPE(Nodes_t) :: Nodes
1760
TYPE(CrevasseGroup3D_t) :: CrevasseGroup
1761
!--------------------------------------------------
1762
LOGICAL :: InGroup
1763
REAL(KIND=dp) :: node_x,node_y, BB(4)
1764
1765
IF(ANY(CrevasseGroup % NodeNumbers == NodeNumber)) &
1766
CALL Fatal("NodeInCrevasseGroup", "Scanning for node which&
1767
&belongs to CrevasseGroup. This is not intended usage!")
1768
1769
node_x = Nodes % x(NodeNumber)
1770
node_y = Nodes % y(NodeNumber)
1771
1772
BB = CrevasseGroup % BoundingBox
1773
1774
IF(node_x < BB(1) .OR. node_x > BB(2) .OR. &
1775
node_y < BB(3) .OR. node_y > BB(4)) THEN
1776
1777
InGroup = .FALSE.
1778
RETURN
1779
1780
END IF
1781
1782
CALL Fatal("NodeInCrevasseGroup", "Haven't finished implementing this yet!")
1783
1784
!Recursively look at node neighbours, stopping when we reach a group member,
1785
!until we reach freedom (a mesh boundary node) or give up (node is contained
1786
!within crevassegroup, and this tells us about the topology of the group)
1787
1788
!RETURN should not just be a logical, this should be repurposed to inform
1789
!about which boundary it reached.
1790
1791
END FUNCTION NodeInCrevasseGroup
1792
1793
!Doubles the size of a pointer integer array
1794
!This version takes a Pointer argument, should
1795
!be used with care...
1796
SUBROUTINE DoubleIntVectorSizeP(Vec, fill)
1797
INTEGER, POINTER :: Vec(:)
1798
INTEGER, OPTIONAL :: fill
1799
!----------------------------------------
1800
INTEGER, ALLOCATABLE :: WorkVec(:)
1801
1802
ALLOCATE(WorkVec(SIZE(Vec)))
1803
WorkVec = Vec
1804
1805
DEALLOCATE(Vec)
1806
ALLOCATE(Vec(2*SIZE(WorkVec)))
1807
1808
IF(PRESENT(fill)) THEN
1809
Vec = fill
1810
ELSE
1811
Vec = 0
1812
END IF
1813
1814
Vec(1:SIZE(WorkVec)) = WorkVec
1815
1816
END SUBROUTINE DoubleIntVectorSizeP
1817
1818
!Doubles the size of a pointer integer array
1819
!Allocatable array version
1820
SUBROUTINE DoubleIntVectorSizeA(Vec, fill)
1821
INTEGER, ALLOCATABLE :: Vec(:)
1822
INTEGER, OPTIONAL :: fill
1823
!----------------------------------------
1824
INTEGER, ALLOCATABLE :: WorkVec(:)
1825
1826
ALLOCATE(WorkVec(SIZE(Vec)))
1827
WorkVec = Vec
1828
1829
DEALLOCATE(Vec)
1830
ALLOCATE(Vec(2*SIZE(WorkVec)))
1831
1832
IF(PRESENT(fill)) THEN
1833
Vec = fill
1834
ELSE
1835
Vec = 0
1836
END IF
1837
1838
Vec(1:SIZE(WorkVec)) = WorkVec
1839
1840
END SUBROUTINE DoubleIntVectorSizeA
1841
1842
1843
!-----------------------------------------------------------------------------
1844
!Given a Nodes_t object, removes the nodes specified by RemoveLogical array
1845
!Optionally, user may provide a list of node numbers (NodeNums), from which
1846
!relevant nodes will also be removed
1847
SUBROUTINE RemoveNodes(InNodes, RemoveLogical, NodeNums)
1848
TYPE(Nodes_t) :: InNodes, WorkNodes
1849
LOGICAL, ALLOCATABLE :: RemoveLogical(:)
1850
INTEGER :: i,counter
1851
INTEGER, POINTER, OPTIONAL :: NodeNums(:)
1852
INTEGER, ALLOCATABLE :: WorkNodeNums(:)
1853
1854
WorkNodes % NumberOfNodes = SIZE(InNodes % x) - COUNT(RemoveLogical)
1855
1856
ALLOCATE(WorkNodes % x(WorkNodes % NumberOfNodes),&
1857
WorkNodes % y(WorkNodes % NumberOfNodes),&
1858
WorkNodes % z(WorkNodes % NumberOfNodes))
1859
IF(PRESENT(NodeNums)) ALLOCATE(WorkNodeNums(WorkNodes % NumberOfNodes))
1860
1861
counter = 1
1862
DO i=1,InNodes % NumberOfNodes
1863
IF(.NOT. RemoveLogical(i)) THEN
1864
WorkNodes % x(counter) = InNodes % x(i)
1865
WorkNodes % y(counter) = InNodes % y(i)
1866
WorkNodes % z(counter) = InNodes % z(i)
1867
IF(PRESENT(NodeNums)) WorkNodeNums(counter) = NodeNums(i)
1868
1869
counter = counter + 1
1870
END IF
1871
END DO
1872
1873
DEALLOCATE(InNodes % x, InNodes % y, InNodes % z )
1874
ALLOCATE(InNodes % x(WorkNodes % NumberOfNodes), &
1875
InNodes % y(WorkNodes % NumberOfNodes), &
1876
InNodes % z(WorkNodes % NumberOfNodes))
1877
1878
IF(PRESENT(NodeNums)) THEN
1879
DEALLOCATE(NodeNums)
1880
ALLOCATE(NodeNums(WorkNodes % NumberOfNodes))
1881
END IF
1882
1883
InNodes % NumberOfNodes = WorkNodes % NumberOfNodes
1884
InNodes % x = WorkNodes % x
1885
InNodes % y = WorkNodes % y
1886
InNodes % z = WorkNodes % z
1887
IF(PRESENT(NodeNums)) NodeNums = WorkNodeNums
1888
1889
DEALLOCATE(WorkNodes % x, WorkNodes % y, WorkNodes % z)
1890
IF(PRESENT(NodeNums)) DEALLOCATE(WorkNodeNums)
1891
END SUBROUTINE RemoveNodes
1892
1893
!------------------------------------------------------------------------------
1894
!> Sort an index array, and change the order of an real array accordingly.
1895
!> Stolen from GeneralUtils, modified so as to leave the initial index array in order
1896
!------------------------------------------------------------------------------
1897
SUBROUTINE MySortF( n,c,b )
1898
!------------------------------------------------------------------------------
1899
INTEGER :: n,c(:)
1900
INTEGER, ALLOCATABLE :: a(:)
1901
REAL(KIND=dp) :: b(:)
1902
!------------------------------------------------------------------------------
1903
1904
INTEGER :: i,j,l,ir,ra
1905
REAL(KIND=dp) :: rb
1906
!------------------------------------------------------------------------------
1907
1908
ALLOCATE(a(SIZE(c)))
1909
a = c
1910
1911
IF ( n <= 1 ) RETURN
1912
1913
l = n / 2 + 1
1914
ir = n
1915
DO WHILE( .TRUE. )
1916
1917
IF ( l > 1 ) THEN
1918
l = l - 1
1919
ra = a(l)
1920
rb = b(l)
1921
ELSE
1922
ra = a(ir)
1923
rb = b(ir)
1924
a(ir) = a(1)
1925
b(ir) = b(1)
1926
ir = ir - 1
1927
IF ( ir == 1 ) THEN
1928
a(1) = ra
1929
b(1) = rb
1930
RETURN
1931
END IF
1932
END IF
1933
i = l
1934
j = l + l
1935
DO WHILE( j <= ir )
1936
IF ( j<ir ) THEN
1937
IF ( a(j)<a(j+1) ) j = j+1
1938
END IF
1939
IF ( ra<a(j) ) THEN
1940
a(i) = a(j)
1941
b(i) = b(j)
1942
i = j
1943
j = j + i
1944
ELSE
1945
j = ir + 1
1946
END IF
1947
a(i) = ra
1948
b(i) = rb
1949
END DO
1950
END DO
1951
1952
DEALLOCATE(a)
1953
1954
!------------------------------------------------------------------------------
1955
END SUBROUTINE MySortF
1956
!------------------------------------------------------------------------------
1957
1958
1959
!If EdgeMaskName is not provided, returns the ring of nodes which define the extent
1960
!of the upper surface of the mesh, arbitrarily beginning with the nodes from the lowest
1961
!partition (PE).
1962
!If EdgeMaskName is provided, this specifies a lateral margin. Then this returns an
1963
!ordered list of nodenumbers which specify an edge of a domain,
1964
!where the edge is determined by the overlap between the two provided permutations
1965
!NOTE: Returned domain edge is valid only on boss partition (PE=0)
1966
SUBROUTINE GetDomainEdge(Model, Mesh, TopPerm, OrderedNodes, OrderedNodeNums, Parallel, &
1967
EdgeMaskName, Simplify, MinDist)
1968
1969
IMPLICIT NONE
1970
1971
TYPE(Model_t) :: Model
1972
TYPE(Mesh_t), POINTER :: Mesh
1973
INTEGER, POINTER :: TopPerm(:)
1974
TYPE(Nodes_t) :: OrderedNodes, UnorderedNodes
1975
LOGICAL :: Parallel
1976
CHARACTER(MAX_NAME_LEN), OPTIONAL :: EdgeMaskName
1977
LOGICAL, OPTIONAL :: Simplify
1978
REAL(KIND=dp), OPTIONAL :: MinDist
1979
!----------------------------------------------------------------
1980
TYPE(Element_t), POINTER :: Element
1981
TYPE(NeighbourList_T), ALLOCATABLE :: PartNeighbourList(:)
1982
INTEGER :: i,j,k,m,n,prev,next,part_start,find_start,find_fin,find_stride,put_start,&
1983
put_fin, counter,NoNodes, NoNodesOnEdge, NoNeighbours, neigh, Segments, TotSegSplits, &
1984
direction, index, segnum, soff, foff, target_nodenum, next_nodenum, EdgeBCtag, GlobalNN
1985
INTEGER :: comm, ierr !MPI stuff
1986
INTEGER, POINTER :: UnorderedNodeNums(:)=>NULL(), OrderedNodeNums(:), &
1987
UOGlobalNodeNums(:)=>NULL(), OrderedGlobalNodeNums(:)=>NULL()
1988
INTEGER, ALLOCATABLE :: NeighbourPartsList(:), PartNodesOnEdge(:), &
1989
disps(:), nodenum_disps(:), PartOrder(:,:), MyCornerNodes(:), MyNeighbourParts(:), &
1990
NewSegStart(:), PartSegments(:), SegStarts_Gather(:), WorkInt(:), NodeNeighbours(:,:), &
1991
GlobalCorners(:), CornerParts(:), PCornerCounts(:)
1992
LOGICAL :: Debug, ActivePart, Boss, Simpl, NotThis, Found, ThisBC, FullBoundary
1993
LOGICAL, ALLOCATABLE :: OnEdge(:), ActivePartList(:), RemoveNode(:), IsCornerNode(:)
1994
REAL(KIND=dp) :: prec, CCW_value
1995
REAL(KIND=dp), ALLOCATABLE :: WorkReal(:,:)
1996
CHARACTER(MAX_NAME_LEN) :: FuncName
1997
1998
TYPE AllocIntList_t
1999
INTEGER, DIMENSION(:), POINTER :: Indices
2000
END TYPE AllocIntList_t
2001
TYPE(AllocIntList_t), ALLOCATABLE :: PartSegStarts(:)
2002
2003
!------------------------------------------------
2004
! Change in strategy:
2005
!
2006
! Previously, used stiffness matrix to determine connectivity, but
2007
! this causes problems when multiple nodes on the boundary reside
2008
! in the same top surface tri element:
2009
!
2010
! *===*===*---*===*===*
2011
! from this one---^\ /
2012
! *
2013
! ^-- we want this one
2014
!
2015
! Various versions of this issue can occur...
2016
!
2017
! SO, instead of using the stiffness matrix, we should
2018
! check all the boundary elements on the relevant SIDE
2019
! boundary (e.g. calving front, right sidewall...),
2020
! looking for elements containing nodes for which the
2021
! top mask is true.
2022
!
2023
! Because of the extruded structure of the mesh, nodes
2024
! within the same boundary quad will always be neighbours,
2025
! and each node shall have no more than 2 neighbours.
2026
!----------------------------------------------------
2027
2028
FuncName = "GetDomainEdge"
2029
Debug = .FALSE.
2030
ActivePart = .TRUE.
2031
2032
NoNodes = SIZE(TopPerm) !total number of nodes in domain/partition
2033
2034
IF(Parallel) THEN
2035
comm = ELMER_COMM_WORLD
2036
Boss = (ParEnv % MyPE == 0)
2037
ELSE
2038
Boss = .TRUE. !only one part in serial, so it's in charge of computation
2039
END IF
2040
2041
IF(Boss .AND. Debug .AND. PRESENT(EdgeMaskName)) THEN
2042
PRINT *, '================================================='
2043
PRINT *, ' Locating domain edge for ',TRIM(EdgeMaskName)
2044
PRINT *, '================================================='
2045
END IF
2046
2047
IF(PRESENT(Simplify)) THEN
2048
Simpl = Simplify
2049
ELSE
2050
Simpl = .FALSE.
2051
END IF
2052
2053
ALLOCATE(OnEdge(NoNodes), NodeNeighbours(NoNodes,2))
2054
OnEdge = .FALSE.
2055
NodeNeighbours = -1
2056
2057
FullBoundary = .NOT.(PRESENT(EdgeMaskName))
2058
IF(.NOT. FullBoundary) THEN
2059
!Find correct BC from logical
2060
DO i=1,Model % NumberOfBCs
2061
ThisBC = ListGetLogical(Model % BCs(i) % Values,EdgeMaskName,Found)
2062
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
2063
EdgeBCtag = Model % BCs(i) % Tag
2064
EXIT
2065
END DO
2066
END IF
2067
2068
!Cycle boundary elements, marking nodes on edge and finding neighbours
2069
DO i=Mesh % NumberOfBulkElements+1, &
2070
Mesh % NumberOfBulkElements+Mesh % NumberOfBoundaryElements
2071
Element => Mesh % Elements(i)
2072
2073
IF((.NOT. FullBoundary) .AND. Element % BoundaryInfo % Constraint /= EdgeBCtag) &
2074
CYCLE !elem not on lateral boundary
2075
2076
IF(ALL(TopPerm(Element % NodeIndexes) > 0)) CYCLE !not a lateral element
2077
IF(.NOT. ANY(TopPerm(Element % NodeIndexes) > 0)) CYCLE !elem contains no nodes on top
2078
!Logic gates above should leave only lateral elements with some nodes on top.
2079
2080
IF(GetElementFamily(Element) == 1) &
2081
CALL Fatal(FuncName, "101 Elements are supposed to be a thing of the past!")
2082
2083
!Cycle nodes in element
2084
DO j=1,Element % TYPE % NumberOfNodes
2085
IF(.NOT. TopPerm(Element % NodeIndexes(j)) > 0) CYCLE
2086
OnEdge(Element % NodeIndexes(j)) = .TRUE.
2087
2088
!Cycle nodes in element
2089
DO k=1,Element % TYPE % NumberOfNodes
2090
IF(j==k) CYCLE
2091
IF(.NOT. TopPerm(Element % NodeIndexes(k))>0) CYCLE
2092
DO m=1,2 !fill NodeNeighbours
2093
IF(NodeNeighbours(Element % NodeIndexes(j),m) /= -1) CYCLE
2094
NodeNeighbours(Element % NodeIndexes(j),m) = Element % NodeIndexes(k)
2095
EXIT
2096
END DO
2097
IF(.NOT. ANY(NodeNeighbours(Element % NodeIndexes(j),:) == Element % NodeIndexes(k))) &
2098
CALL Fatal(FuncName,'Identified more than two neighbours')
2099
END DO
2100
END DO
2101
2102
END DO
2103
2104
NoNodesOnEdge = COUNT(OnEdge)
2105
IF(NoNodesOnEdge == 1) THEN
2106
CALL Fatal(FuncName, "A single node identified on boundary, should not be possible. &
2107
&Someone is messing around with 101 elements.")
2108
END IF
2109
2110
ALLOCATE(UnorderedNodeNums(NoNodesOnEdge),&
2111
OrderedNodeNums(NoNodesOnEdge))
2112
OrderedNodeNums = -1 !initialize to invalid value
2113
2114
j = 0
2115
DO i=1,NoNodes
2116
IF(.NOT. OnEdge(i)) CYCLE
2117
j = j + 1
2118
UnorderedNodeNums(j) = i
2119
END DO
2120
2121
!Cycle nodes on edge, looking for one with only one neighbour (a corner)
2122
!Edge case = serial fullboundary run, no corner exists, choose arbitrarily
2123
!Rare case (not dealt with!! TODO) = parallel fullboundary, no corners
2124
! (whole mesh edge in one partition)
2125
IF(NoNodesOnEdge > 1) THEN
2126
2127
ALLOCATE(IsCornerNode(NoNodesOnEdge))
2128
IsCornerNode = .FALSE.
2129
2130
DO i=1,NoNodesOnEdge
2131
IsCornerNode(i) = COUNT(NodeNeighbours(UnOrderedNodeNums(i),:) == -1) == 1
2132
IF(COUNT(NodeNeighbours(UnOrderedNodeNums(i),:) == -1) == 2) &
2133
CALL Fatal(FuncName, "Found an isolated node on edge")
2134
END DO
2135
2136
IF(MOD(COUNT(IsCornerNode),2) /= 0) THEN
2137
WRITE(Message,'(A,i0)') "Found an odd number of&
2138
& corner nodes in partition: ",ParEnv % MyPE
2139
CALL Fatal(FuncName, Message)
2140
END IF
2141
2142
IF(FullBoundary .AND. .NOT. Parallel) THEN
2143
2144
!If serial FullBoundary request, no corner exists so just choose the first
2145
!unordered node in the list and loop from there
2146
Segments = 1
2147
ALLOCATE(MyCornerNodes(2))
2148
MyCornerNodes(1) = 1
2149
2150
ELSE
2151
2152
Segments = COUNT(IsCornerNode) / 2
2153
IF(Debug .AND. Segments > 1) PRINT *, &
2154
'Partition ',ParEnv % MyPE, ' has ',Segments,' line segments on boundary.'
2155
2156
ALLOCATE(NewSegStart(Segments-1))
2157
ALLOCATE(MyCornerNodes(COUNT(IsCornerNode)))
2158
2159
counter = 1
2160
DO i=1,NoNodesOnEdge
2161
IF(IsCornerNode(i)) THEN
2162
MyCornerNodes(counter) = i
2163
counter = counter + 1
2164
END IF
2165
END DO
2166
2167
END IF
2168
2169
counter = 1
2170
DO k=1,Segments
2171
2172
IF(k==1) THEN
2173
OrderedNodeNums(counter) = UnorderedNodeNums(MyCornerNodes(1))
2174
ELSE
2175
DO i=2, SIZE(MyCornerNodes)
2176
IF(ANY(OrderedNodeNums == UnorderedNodeNums(MyCornerNodes(i)))) THEN
2177
CYCLE
2178
ELSE
2179
OrderedNodeNums(counter) = UnorderedNodeNums(MyCornerNodes(i))
2180
EXIT
2181
END IF
2182
END DO
2183
END IF
2184
counter = counter + 1
2185
2186
!----------------------------------------------------
2187
! Move along from corner, filling in order
2188
!----------------------------------------------------
2189
DO i=counter,NoNodesOnEdge
2190
Found = .FALSE.
2191
IF(OrderedNodeNums(i-1) == -1) CALL Abort()
2192
2193
DO j=1,2
2194
IF(NodeNeighbours(OrderedNodeNums(i-1),j) == -1) CYCLE !First and last nodes, corner
2195
IF(ANY(OrderedNodeNums(1:i-1) == NodeNeighbours(OrderedNodeNums(i-1),j))) &
2196
CYCLE !already in list
2197
2198
OrderedNodeNums(i) = NodeNeighbours(OrderedNodeNums(i-1),j)
2199
Found = .TRUE.
2200
END DO
2201
2202
IF(.NOT. Found) EXIT
2203
END DO
2204
2205
counter = i
2206
2207
IF(counter >= NoNodesOnEdge) EXIT !this should be redundant...
2208
NewSegStart(k) = counter
2209
END DO
2210
2211
ELSE !Either 1 or 0 nodes found, not an active boundary partition
2212
!0 node case, obvious
2213
!1 node case, if THIS partition only has one node on the boundary,
2214
!this same node must be caught by two other partitions, so we aren't needed.
2215
ALLOCATE(NewSegStart(0), MyCornerNodes(0))
2216
ActivePart = .FALSE.
2217
Segments = 0
2218
NoNodesOnEdge = 0 !simplifies mpi comms later
2219
IF(.NOT.Parallel) CALL Fatal(FuncName,&
2220
"Found either 1 or 0 nodes in a serial run, this isn't a valid boundary edge!")
2221
END IF
2222
2223
2224
!Remember that, in parallel, we're using local rather than global node numbers
2225
IF(Parallel) THEN
2226
2227
!gather corner count - replaces 101 element detection
2228
ALLOCATE(PCornerCounts(ParEnv % PEs),disps(ParEnv % PEs))
2229
2230
CALL MPI_AllGather(SIZE(MyCornerNodes), 1, MPI_INTEGER, PCornerCounts, &
2231
1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
2232
2233
disps(1) = 0
2234
DO i=2, ParEnv % PEs
2235
disps(i) = disps(i-1) + PCornerCounts(i-1)
2236
END DO
2237
2238
ALLOCATE(GlobalCorners(SUM(PCornerCounts)),&
2239
CornerParts(SUM(PCornerCounts)))
2240
2241
!gather corner nodenums
2242
CALL MPI_AllGatherV(Mesh % ParallelInfo % GlobalDOFs(UnorderedNodeNums(MyCornerNodes)), &
2243
SIZE(MyCornerNodes), MPI_INTEGER, GlobalCorners, PCornerCounts, disps, &
2244
MPI_INTEGER, ELMER_COMM_WORLD, ierr)
2245
2246
!note which partition sent each corner node
2247
counter = 1
2248
DO i=1,ParEnv % PEs
2249
IF(PCornerCounts(i) == 0) CYCLE
2250
CornerParts(counter:counter+PCornerCounts(i)-1) = i-1
2251
counter = counter + PCornerCounts(i)
2252
END DO
2253
2254
!Quick check:
2255
DO i=1,SIZE(GlobalCorners)
2256
counter = COUNT(GlobalCorners == GlobalCorners(i))
2257
IF(counter > 2) CALL Fatal(FuncName,"Programming error in partition &
2258
&segment detection, node found too many times!")
2259
END DO
2260
!Now GlobalCorners and CornerParts tell us which partitions found corner nodes
2261
!(i.e. nodes which will join other segments)
2262
2263
IF(ActivePart) THEN
2264
ALLOCATE(MyNeighbourParts(Segments*2))
2265
2266
DO i=1,Segments*2 !Find neighbour partition numbers
2267
2268
IF(i==1) THEN
2269
n = OrderedNodeNums(1)
2270
ELSE IF(i==Segments*2) THEN
2271
n = OrderedNodeNums(NoNodesOnEdge)
2272
ELSE IF(MOD(i,2)==0) THEN
2273
n = OrderedNodeNums(NewSegStart(i/2)-1)
2274
ELSE
2275
n = OrderedNodeNums(NewSegStart(i/2))
2276
END IF
2277
2278
MyNeighbourParts(i) = -1 !default if not caught in loop below
2279
GlobalNN = Mesh % ParallelInfo % GlobalDOFs(n)
2280
DO j=1,SIZE(GlobalCorners)
2281
IF(GlobalCorners(j) /= GlobalNN) CYCLE
2282
IF(CornerParts(j) == ParEnv % MyPE) CYCLE
2283
MyNeighbourParts(i) = CornerParts(j)
2284
IF( .NOT. (ANY(Mesh % ParallelInfo % NeighbourList(n) % Neighbours &
2285
== MyNeighbourParts(i)))) CALL Fatal(FuncName, &
2286
"Failed sanity check on neighbour partition detection.")
2287
END DO
2288
END DO
2289
ELSE
2290
ALLOCATE(MyNeighbourParts(0))
2291
END IF
2292
2293
IF(Boss) ALLOCATE(PartSegments(ParEnv % PEs))
2294
2295
CALL MPI_GATHER(Segments, 1, MPI_INTEGER, PartSegments, &
2296
1, MPI_INTEGER, 0, comm, ierr)
2297
2298
IF(Boss) THEN
2299
2300
TotSegSplits = 0
2301
DO i=1,SIZE(PartSegments)
2302
TotSegSplits = TotSegSplits + MAX(PartSegments(i)-1,0)
2303
END DO
2304
2305
ALLOCATE(nodenum_disps(ParEnv % PEs), &
2306
PartNodesOnEdge(ParEnv % PEs), &
2307
NeighbourPartsList(SUM(PartSegments)*2), &
2308
PartNeighbourList(ParEnv % PEs), &
2309
SegStarts_Gather(TotSegSplits))
2310
2311
DO i=1,ParEnv % PEs
2312
ALLOCATE(PartNeighbourList(i) % Neighbours(PartSegments(i)*2))
2313
END DO
2314
2315
disps(1) = 0
2316
DO i=2, ParEnv % PEs
2317
disps(i) = disps(i-1) + MAX(PartSegments(i-1)-1,0)
2318
END DO
2319
2320
END IF
2321
2322
!Get found count from each part to boss
2323
CALL MPI_GATHER(NoNodesOnEdge, 1, MPI_INTEGER, PartNodesOnEdge, &
2324
1, MPI_INTEGER, 0, comm ,ierr)
2325
IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")
2326
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
2327
2328
IF(Debug .AND. Boss) THEN
2329
PRINT *, 'boss size(SegStarts_Gather): ', SIZE(SegStarts_Gather)
2330
PRINT *, 'boss PartSegments: ', PartSegments
2331
PRINT *, 'boss disps:', disps
2332
DO i=1,ParEnv % PEs
2333
IF(PartNodesOnEdge(i) == 0) CYCLE
2334
PRINT *, 'partition ',i-1,' NoNodesOnEdge: ',PartNodesOnEdge(i)
2335
END DO
2336
END IF
2337
2338
IF(Boss) THEN
2339
ALLOCATE(WorkInt(ParEnv % PEs))
2340
WorkInt = MAX(PartSegments-1,0)
2341
END IF
2342
2343
CALL MPI_GATHERV(NewSegStart, MAX(Segments-1,0), MPI_INTEGER, SegStarts_Gather, &
2344
WorkInt, disps, MPI_INTEGER, 0, comm, ierr)
2345
IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")
2346
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
2347
2348
IF(Boss) THEN
2349
ALLOCATE(PartSegStarts(ParEnv % PEs))
2350
DO i=1,ParEnv % PEs
2351
j = PartSegments(i)
2352
ALLOCATE( PartSegStarts(i) % Indices(MAX((j - 1),0)))
2353
IF(j > 1) THEN
2354
IF(Debug) PRINT *, 'debug disps(i),j', disps(i),j
2355
PartSegStarts(i) % Indices = SegStarts_Gather(1+disps(i) : (1+disps(i) + (j-1)-1) )
2356
END IF
2357
IF(Debug) PRINT *, i,' partsegstarts: ', PartSegStarts(i) % Indices
2358
END DO
2359
2360
disps(1) = 0
2361
DO i=2, ParEnv % PEs
2362
disps(i) = disps(i-1) + PartSegments(i-1)*2
2363
END DO
2364
2365
WorkInt = PartSegments*2
2366
END IF
2367
2368
!Get neighbour part numbers from each part to boss
2369
CALL MPI_GATHERV(MyNeighbourParts, Segments*2, MPI_INTEGER, NeighbourPartsList, &
2370
WorkInt, disps, MPI_INTEGER, 0, comm ,ierr)
2371
IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")
2372
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
2373
2374
IF(Debug .AND. Boss) PRINT *, 'DEBUG, NewSegStart: ', NewSegStart
2375
2376
IF(Boss) THEN
2377
ActivePartList = (PartNodesOnEdge > 0)
2378
2379
!Here we account for shared nodes on partition boundaries
2380
OrderedNodes % NumberOfNodes = SUM(PartNodesOnEdge) - (SIZE(NeighbourPartsList)/2 - 1)
2381
!but they are still present when gathered...
2382
UnorderedNodes % NumberOfNodes = SUM(PartNodesOnEdge)
2383
2384
ALLOCATE(PartOrder(SIZE(NeighbourPartsList)/2,2),&
2385
OrderedNodes % x(OrderedNodes % NumberOfNodes),&
2386
OrderedNodes % y(OrderedNodes % NumberOfNodes),&
2387
OrderedNodes % z(OrderedNodes % NumberOfNodes),&
2388
UnorderedNodes % x(UnorderedNodes % NumberOfNodes),&
2389
UnorderedNodes % y(UnorderedNodes % NumberOfNodes),&
2390
UnorderedNodes % z(UnorderedNodes % NumberOfNodes),&
2391
UOGlobalNodeNums(UnorderedNodes % NumberOfNodes),&
2392
OrderedGlobalNodeNums(OrderedNodes % NumberOfNodes))
2393
2394
nodenum_disps(1) = 0
2395
DO i=2, ParEnv % PEs
2396
nodenum_disps(i) = nodenum_disps(i-1) + PartNodesOnEdge(i-1)
2397
END DO
2398
2399
IF(Debug) THEN
2400
PRINT *, 'debug disps: ', disps
2401
PRINT *, 'debug nodenum_disps: ', nodenum_disps
2402
PRINT *, 'debug neighbourpartslist: ',NeighbourPartsList
2403
PRINT *, 'Partition Segments: ',PartSegments
2404
END IF
2405
END IF
2406
2407
!-----------------------------------------------------------
2408
! Gather node coords from all partitions
2409
! Note, they're going into 'UnorderedNodes': though they are ordered
2410
! within their partition, the partitions aren't ordered...
2411
!-----------------------------------------------------------
2412
2413
!Global Node Numbers
2414
CALL MPI_GATHERV(Mesh % ParallelInfo % GlobalDOFs(OrderedNodeNums),&
2415
NoNodesOnEdge,MPI_INTEGER,&
2416
UOGlobalNodeNums,PartNodesOnEdge,&
2417
nodenum_disps,MPI_INTEGER,0,comm, ierr)
2418
IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")
2419
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
2420
2421
!X coords
2422
CALL MPI_GATHERV(Mesh % Nodes % x(OrderedNodeNums),&
2423
NoNodesOnEdge,MPI_DOUBLE_PRECISION,&
2424
UnorderedNodes % x,PartNodesOnEdge,&
2425
nodenum_disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
2426
IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")
2427
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
2428
2429
!Y coords
2430
CALL MPI_GATHERV(Mesh % Nodes % y(OrderedNodeNums),&
2431
NoNodesOnEdge,MPI_DOUBLE_PRECISION,&
2432
UnorderedNodes % y,PartNodesOnEdge,&
2433
nodenum_disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
2434
IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")
2435
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
2436
2437
!Z coords
2438
CALL MPI_GATHERV(Mesh % Nodes % z(OrderedNodeNums),&
2439
NoNodesOnEdge,MPI_DOUBLE_PRECISION,&
2440
UnorderedNodes % z,PartNodesOnEdge,&
2441
nodenum_disps,MPI_DOUBLE_PRECISION,0,comm, ierr)
2442
IF(ierr /= MPI_SUCCESS) CALL Fatal(FuncName,"MPI Error!")
2443
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
2444
2445
!-----------------------------------------------------------
2446
! Determine order of partitions by linking neighbours and
2447
! checking globalnodenumbers where appropriate
2448
!-----------------------------------------------------------
2449
2450
IF(Boss) THEN
2451
!Notes: NeighbourPartsList is zero indexed, like PEs
2452
!PartOrder is 1 indexed
2453
!disps is 1 indexed. So disps(NeighbourPartsList+1)
2454
2455
PartOrder = 0 !init
2456
direction = 0
2457
prev = -1
2458
next = 0
2459
2460
!First fill in PartNeighbourList % Neighbours
2461
DO i=1,ParEnv % PEs
2462
IF(PartSegments(i)==0) CYCLE
2463
PartNeighbourList(i) % Neighbours = &
2464
NeighbourPartsList( (1+disps(i)) : (1+disps(i) + (PartSegments(i)*2) - 1) )
2465
!There is the possibility of missing an end (-1) due to partition
2466
!landing right on corner
2467
DO j=1,SIZE(PartNeighbourList(i) % Neighbours)
2468
IF(PartNeighbourList(i) % Neighbours(j) == -1) CYCLE
2469
IF(PartSegments(PartNeighbourList(i) % Neighbours(j)+1) < 1) THEN
2470
IF(Debug) PRINT *, 'Neighbour ',PartNeighbourList(i) % Neighbours(j)+1,&
2471
"isn't really on boundary, so changing to -1"
2472
PartNeighbourList(i) % Neighbours(j) = -1
2473
END IF
2474
END DO
2475
2476
IF(Debug) PRINT *, i-1, ': Neighbours: ', PartNeighbourList(i) % Neighbours
2477
!find a corner partition
2478
IF(ANY(PartNeighbourList(i) % Neighbours == prev)) next = i
2479
END DO
2480
2481
!No partition had corner (-1)
2482
IF(next==0) THEN
2483
IF(FullBoundary) THEN !this is expected, a closed loop so no -1
2484
DO i=1,ParEnv % PEs
2485
IF(PartSegments(i)>0) THEN
2486
next = i
2487
prev = PartNeighbourList(i) % Neighbours(1)
2488
EXIT
2489
END IF
2490
END DO
2491
ELSE
2492
CALL Fatal(FuncName,"Error finding corner of requested boundary in partitions.")
2493
END IF
2494
ELSE IF(FullBoundary) THEN
2495
CALL Fatal(FuncName,"Error - found corner but requested FullBoundary&
2496
&- programming mistake.")
2497
END IF
2498
2499
IF(Debug) THEN
2500
PRINT *, 'Debug GetDomainEdge, globalno, unorderednodes % x: '
2501
DO i=1,SIZE(UOGlobalNodeNums)
2502
PRINT *, i, UOGlobalNodeNums(i), UnorderedNodes % x(i)
2503
END DO
2504
2505
PRINT *, 'debug nodenum_disps: '
2506
DO i=1, SIZE(nodenum_disps)
2507
PRINT *, i,' ',nodenum_disps(i)
2508
END DO
2509
END IF
2510
2511
2512
counter = 1
2513
2514
DO WHILE(.TRUE.)
2515
IF(Debug) PRINT *,'Next Partition is: ',next
2516
IF((COUNT(PartNeighbourList(next) % Neighbours == prev) == 1) .OR. &
2517
(prev == -1)) THEN
2518
DO j=1,SIZE(PartNeighbourList(next) % Neighbours)
2519
IF(PartNeighbourList(next) % Neighbours(j) == prev) THEN
2520
index = j
2521
EXIT
2522
END IF
2523
END DO
2524
ELSE !Neighbours on both sides, so need to inspect globalnodenumbers
2525
IF(Debug) PRINT *, 'debug, two matches'
2526
DO j=1,SIZE(PartNeighbourList(next) % Neighbours)
2527
IF(PartNeighbourList(next) % Neighbours(j) == prev) THEN
2528
2529
segnum = ((j-1)/2) + 1
2530
direction = (2 * MOD(j, 2)) - 1
2531
2532
IF(segnum == 1) THEN
2533
soff = 0
2534
ELSE
2535
soff = PartSegStarts(next) % Indices(segnum - 1) - 1
2536
END IF
2537
IF(segnum == PartSegments(next)) THEN
2538
foff = 0
2539
ELSE
2540
foff = -1 * (PartNodesOnEdge(next) - PartSegStarts(next) % Indices(segnum) + 1)
2541
END IF
2542
2543
IF(direction > 0) THEN
2544
next_nodenum = UOGlobalNodeNums(1 + nodenum_disps(next) + soff)
2545
ELSE
2546
!one node before (-1) the next partition's (+1) nodes
2547
IF(next == ParEnv % PEs) THEN
2548
k = SIZE(UOGlobalNodeNums)
2549
ELSE
2550
k = 1 + nodenum_disps(next+1) - 1
2551
END IF
2552
next_nodenum = UOGlobalNodeNums(k + foff)
2553
END IF
2554
IF(Debug) THEN
2555
PRINT *, 'debug, next_nodenum: ', next_nodenum
2556
PRINT *, 'debug, target_nodenum: ', target_nodenum
2557
END IF
2558
IF(next_nodenum == target_nodenum) THEN
2559
index = j
2560
EXIT
2561
END IF
2562
END IF
2563
END DO
2564
END IF
2565
2566
segnum = ((index-1)/2) + 1 !1,2 -> 1, 3,4 -> 2
2567
direction = (2 * MOD(index, 2)) - 1
2568
PartOrder(counter,1) = next - 1
2569
PartOrder(counter,2) = direction * segnum
2570
counter = counter + 1
2571
2572
IF(Debug) THEN
2573
PRINT *, 'index: ', index
2574
PRINT *, 'segnum: ', segnum
2575
PRINT *, 'direction: ',direction
2576
PRINT *, 'next: ', next
2577
PRINT *, 'prev: ', prev
2578
END IF
2579
2580
prev = next - 1
2581
j = next
2582
next = PartNeighbourList(next) % Neighbours(index + direction)
2583
2584
!In case of two matches, need a target node to find
2585
IF(segnum == 1) THEN
2586
soff = 0
2587
ELSE
2588
soff = PartSegStarts(j) % Indices(segnum - 1) - 1
2589
END IF
2590
IF(segnum == PartSegments(j)) THEN
2591
foff = 0
2592
ELSE
2593
foff = -1 * (PartNodesOnEdge(j) - PartSegStarts(j) % Indices(segnum) + 1)
2594
END IF
2595
2596
IF(direction < 0) THEN
2597
target_nodenum = UOGlobalNodeNums(1 + nodenum_disps(prev+1) + soff)
2598
ELSE
2599
IF(prev + 1 == ParEnv % PEs) THEN
2600
k = SIZE(UOGlobalNodeNums)
2601
ELSE
2602
k = 1 + nodenum_disps(prev+1+1) - 1
2603
END IF
2604
!one node before (-1) the next partition's (+1) nodes
2605
target_nodenum = UOGlobalNodeNums(k + foff)
2606
END IF
2607
2608
!wipe them out so we don't accidentally come back this way
2609
PartNeighbourList(j) % Neighbours(index:index+direction:direction) = -2
2610
2611
IF(FullBoundary) THEN
2612
IF(Debug) THEN
2613
PRINT *, 'new index: ', index
2614
PRINT *, 'new segnum: ', segnum
2615
PRINT *, 'new direction: ',direction
2616
PRINT *, 'new next: ', next
2617
PRINT *, 'new prev: ', prev
2618
PRINT *, 'new neighbours: ', PartNeighbourList(next+1) % Neighbours
2619
END IF
2620
2621
IF(ALL(PartNeighbourList(next+1) % Neighbours == -2)) THEN
2622
IF(Debug) PRINT *,'Finished cycling neighbours in FullBoundary'
2623
EXIT
2624
END IF
2625
ELSE IF(next == -1) THEN
2626
EXIT
2627
END IF
2628
2629
next = next + 1
2630
END DO
2631
2632
IF(Debug) PRINT *, 'Debug GetDomainEdge, part order:', PartOrder
2633
2634
END IF
2635
2636
!-----------------------------------------------------------
2637
! Put nodes collected from partitions into order
2638
!-----------------------------------------------------------
2639
2640
IF(Boss) THEN
2641
put_start = 1
2642
2643
DO i=1,SIZE(PartOrder,1)
2644
j = PartOrder(i,1) + 1
2645
segnum = PartOrder(i,2)
2646
2647
IF(j==0) CALL Abort()
2648
2649
foff = 0
2650
soff = 0
2651
IF(PartSegments(j) > 1) THEN
2652
IF(Debug) THEN
2653
PRINT *, 'Debug GetDomainEdge, extracting nodes from segmented partition'
2654
PRINT *, 'Debug GetDomainEdge, segnum: ', segnum
2655
PRINT *, 'Debug GetDomainEdge, partnodes: ', PartNodesOnEdge(j)
2656
PRINT *, 'Debug GetDomainEdge, PartSegStarts(j) % Indices: ',&
2657
PartSegStarts(j) % Indices
2658
PRINT *, 'Debug GetDomainEdge, nodenum_disps(j): ',nodenum_disps(j)
2659
END IF
2660
2661
IF(ABS(segnum) == 1) THEN
2662
2663
soff = 0
2664
ELSE
2665
soff = PartSegStarts(j) % Indices(ABS(segnum) - 1) - 1
2666
END IF
2667
IF(ABS(segnum) == PartSegments(j)) THEN
2668
foff = 0
2669
ELSE
2670
foff = -1 * (PartNodesOnEdge(j) - PartSegStarts(j) % Indices(ABS(segnum)) + 1)
2671
END IF
2672
END IF
2673
2674
part_start = 1 + nodenum_disps(j) !where are this partitions nodes?
2675
IF(segnum > 0) THEN
2676
find_start = part_start + soff
2677
find_fin = part_start + PartNodesOnEdge(j) - 1 + foff
2678
find_stride = 1
2679
ELSE
2680
find_fin = part_start + soff
2681
find_start = part_start + PartNodesOnEdge(j) - 1 + foff
2682
find_stride = -1
2683
END IF
2684
2685
put_fin = put_start + ABS(find_start - find_fin)
2686
IF(Debug) THEN
2687
PRINT *, 'Debug, find start, end: ',find_start, find_fin, find_stride
2688
PRINT *, 'Debug, put start, end: ',put_start, put_fin
2689
PRINT *, 'Total slots: ',SIZE(OrderedNodes % x)
2690
END IF
2691
2692
OrderedNodes % x(put_start:put_fin) = &
2693
UnorderedNodes % x(find_start:find_fin:find_stride)
2694
OrderedNodes % y(put_start:put_fin) = &
2695
UnorderedNodes % y(find_start:find_fin:find_stride)
2696
OrderedNodes % z(put_start:put_fin) = &
2697
UnorderedNodes % z(find_start:find_fin:find_stride)
2698
OrderedGlobalNodeNums(put_start:put_fin) = &
2699
UOGlobalNodeNums(find_start:find_fin:find_stride)
2700
2701
put_start = put_fin !1 node overlap
2702
END DO
2703
2704
IF(FullBoundary) THEN
2705
!In the full boundary case, we've inadvertently saved the first node twice
2706
! (once at the end too) - this sorts that out
2707
n = OrderedNodes % NumberOfNodes - 1
2708
OrderedNodes % NumberOfNodes = n
2709
2710
ALLOCATE(WorkReal(n,3))
2711
WorkReal(:,1) = OrderedNodes % x(1:n)
2712
WorkReal(:,2) = OrderedNodes % y(1:n)
2713
WorkReal(:,3) = OrderedNodes % z(1:n)
2714
DEALLOCATE(OrderedNodes % x, OrderedNodes % y, OrderedNodes % z)
2715
ALLOCATE(OrderedNodes % x(n), OrderedNodes % y(n), OrderedNodes % z(n))
2716
OrderedNodes % x(1:n) = WorkReal(:,1)
2717
OrderedNodes % y(1:n) = WorkReal(:,2)
2718
OrderedNodes % z(1:n) = WorkReal(:,3)
2719
DEALLOCATE(WorkReal)
2720
END IF
2721
2722
DEALLOCATE(OrderedNodeNums)
2723
ALLOCATE(OrderedNodeNums(OrderedNodes % NumberOfNodes))
2724
OrderedNodeNums = OrderedGlobalNodeNums(1:OrderedNodes % NumberOfNodes)
2725
2726
IF(Debug) THEN
2727
PRINT *, 'Debug GetDomainEdge, globalno, orderednodes % x: '
2728
DO i=1,SIZE(OrderedNodes % x)
2729
PRINT *, OrderedNodeNums(i), OrderedNodes % x(i)
2730
END DO
2731
END IF
2732
END IF
2733
2734
ELSE !serial
2735
OrderedNodes % NumberOfNodes = NoNodesOnEdge
2736
ALLOCATE(OrderedNodes % x(OrderedNodes % NumberOfNodes),&
2737
OrderedNodes % y(OrderedNodes % NumberOfNodes),&
2738
OrderedNodes % z(OrderedNodes % NumberOfNodes))
2739
2740
OrderedNodes % x = Mesh % Nodes % x(OrderedNodeNums)
2741
OrderedNodes % y = Mesh % Nodes % y(OrderedNodeNums)
2742
OrderedNodes % z = Mesh % Nodes % z(OrderedNodeNums)
2743
2744
!No action required on OrderedNodeNums...
2745
END IF
2746
2747
!-------------------------------------------------------------
2748
! Simplify geometry by removing interior nodes on any straight
2749
! lines if requested
2750
!-------------------------------------------------------------
2751
IF(Simpl .AND. Boss) THEN
2752
ALLOCATE(RemoveNode(OrderedNodes % NumberOfNodes))
2753
RemoveNode = .FALSE.
2754
2755
DO i=2,OrderedNodes % NumberOfNodes-1 !Test all interior nodes
2756
2757
CCW_value = ((OrderedNodes % y(i) - OrderedNodes % y(i+1)) * &
2758
(OrderedNodes % x(i-1) - OrderedNodes % x(i+1))) - &
2759
((OrderedNodes % x(i) - OrderedNodes % x(i+1)) * &
2760
(OrderedNodes % y(i-1) - OrderedNodes % y(i+1)))
2761
2762
IF(Debug) PRINT *,'Debug simplify node: ',&
2763
OrderedNodes % x(i), OrderedNodes % y(i),' ccw: ',ccw_value
2764
2765
!Need to determine numerical precision of input datapoints
2766
!i.e. after how many decimal places are values constant
2767
!e.g. 0.23000000... or 99999...
2768
prec = MAX(RealAeps(OrderedNodes % x(i)),RealAeps(OrderedNodes % y(i)))
2769
2770
IF(ABS(CCW_value) < 10*AEPS) THEN
2771
RemoveNode(i) = .TRUE.
2772
END IF
2773
END DO
2774
2775
IF(COUNT(RemoveNode) > 0) THEN
2776
2777
CALL RemoveNodes(OrderedNodes, RemoveNode, OrderedNodeNums)
2778
2779
IF(Debug) THEN
2780
PRINT *, 'Debug GetDomainEdge, Simplify removing: ', COUNT(RemoveNode), ' nodes'
2781
DO i=1,OrderedNodes % NumberOfNodes
2782
PRINT *, 'Debug GetDomainEdge, node: ',i
2783
PRINT *, 'x: ',OrderedNodes % x(i),'y: ',OrderedNodes % y(i)
2784
END DO
2785
END IF !debug
2786
2787
END IF !removing any nodes
2788
DEALLOCATE(RemoveNode)
2789
END IF !simplify
2790
2791
!-------------------------------------------------------------
2792
! Remove any nodes which are closer together than MinDist, if
2793
! this is specified.
2794
!-------------------------------------------------------------
2795
IF(PRESENT(MinDist) .AND. Boss) THEN
2796
!Cycle all nodes, remove any too close together
2797
!This won't guarantee that the new domain edge is *within* the old one
2798
!but could be adapted to do so
2799
ALLOCATE(RemoveNode(OrderedNodes % NumberOfNodes))
2800
RemoveNode = .FALSE.
2801
DO i=2,OrderedNodes % NumberOfNodes-1 !Test all interior nodes
2802
j = i - 1
2803
DO WHILE(RemoveNode(j))
2804
j = j-1
2805
END DO
2806
2807
IF(NodeDist2D(OrderedNodes, i, j) < MinDist) THEN
2808
RemoveNode(i) = .TRUE.
2809
IF(Debug) THEN
2810
PRINT *, 'Debug GetDomainEdge, MinDist, removing node ',i,' too close to: ', j
2811
PRINT *, 'Debug GetDomainEdge, MinDist, dist: ',NodeDist2D(OrderedNodes, i, j)
2812
END IF
2813
END IF
2814
END DO
2815
2816
IF(COUNT(RemoveNode) > 0) THEN
2817
2818
CALL RemoveNodes(OrderedNodes, RemoveNode, OrderedNodeNums)
2819
2820
IF(Debug) THEN
2821
PRINT *, 'Debug GetDomainEdge, MinDist removing: ', COUNT(RemoveNode), ' nodes'
2822
DO i=1,OrderedNodes % NumberOfNodes
2823
PRINT *, 'Debug GetDomainEdge, node: ',i
2824
PRINT *, 'x: ',OrderedNodes % x(i),'y: ',OrderedNodes % y(i)
2825
END DO
2826
END IF !debug
2827
2828
END IF !removing any nodes
2829
DEALLOCATE(RemoveNode)
2830
END IF !MinDist
2831
2832
!------------ DEALLOCATIONS ------------------
2833
2834
DEALLOCATE(OnEdge, UnorderedNodeNums, GlobalCorners, CornerParts, PCornerCounts, OrderedNodeNums)
2835
2836
IF(Boss .AND. Parallel) THEN !Deallocations
2837
DEALLOCATE(UnorderedNodes % x, &
2838
UnorderedNodes % y, &
2839
UnorderedNodes % z, &
2840
PartNodesOnEdge, &
2841
disps, nodenum_disps, &
2842
PartOrder, &
2843
UOGlobalNodeNums, &
2844
OrderedGlobalNodeNums)
2845
END IF
2846
2847
IF(.NOT. Boss) DEALLOCATE(UnorderedNodes % x, UnorderedNodes % y, UnorderedNodes % z)
2848
2849
END SUBROUTINE GetDomainEdge
2850
2851
! Copies over time variables and creates coordinate vars. Basically pinched
2852
! from AddMeshCoordinatesAndTime() and Multigrid
2853
SUBROUTINE CopyIntrinsicVars(OldMesh, NewMesh)
2854
IMPLICIT NONE
2855
2856
TYPE(Mesh_t), POINTER :: OldMesh, NewMesh
2857
TYPE(Solver_t), POINTER :: Solver
2858
TYPE(Variable_t), POINTER :: WorkVar
2859
!----------------------------------------------------------
2860
NULLIFY( Solver )
2861
2862
CALL VariableAdd( NewMesh % Variables, NewMesh,Solver, &
2863
'Coordinate 1',1,NewMesh % Nodes % x )
2864
2865
CALL VariableAdd(NewMesh % Variables,NewMesh,Solver, &
2866
'Coordinate 2',1,NewMesh % Nodes % y )
2867
2868
CALL VariableAdd(NewMesh % Variables,NewMesh,Solver, &
2869
'Coordinate 3',1,NewMesh % Nodes % z )
2870
2871
WorkVar => VariableGet( OldMesh % Variables, 'Time', ThisOnly=.TRUE.)
2872
IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Time', 1, WorkVar % Values )
2873
2874
WorkVar => VariableGet( OldMesh % Variables, 'Periodic Time', ThisOnly=.TRUE.)
2875
IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Periodic Time', 1, WorkVar % Values )
2876
2877
WorkVar => VariableGet( OldMesh % Variables, 'Timestep', ThisOnly=.TRUE.)
2878
IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Timestep', 1, WorkVar % Values )
2879
2880
WorkVar => VariableGet( OldMesh % Variables, 'Timestep size', ThisOnly=.TRUE.)
2881
IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Timestep size', 1, WorkVar % Values )
2882
2883
WorkVar => VariableGet( OldMesh % Variables, 'Timestep interval', ThisOnly=.TRUE.)
2884
IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Timestep interval', 1, WorkVar % Values )
2885
2886
WorkVar => VariableGet( OldMesh % Variables, 'Coupled iter', ThisOnly=.TRUE.)
2887
IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Coupled iter', 1, WorkVar % Values )
2888
2889
WorkVar => VariableGet( OldMesh % Variables, 'Nonlin iter', ThisOnly=.TRUE.)
2890
IF(ASSOCIATED(WorkVar)) CALL VariableAdd( NewMesh % Variables, NewMesh, Solver, 'Nonlin iter', 1, WorkVar % Values )
2891
2892
END SUBROUTINE CopyIntrinsicVars
2893
2894
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2895
!Function to rotate a mesh by rotationmatrix
2896
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
2897
SUBROUTINE RotateMesh(Mesh, RotationMatrix)
2898
2899
IMPLICIT NONE
2900
2901
TYPE(Mesh_t) :: Mesh
2902
REAL(KIND=dp) :: RotationMatrix(3,3), NodeHolder(3)
2903
INTEGER :: i
2904
2905
DO i=1,Mesh % NumberOfNodes
2906
NodeHolder(1) = Mesh % Nodes % x(i)
2907
NodeHolder(2) = Mesh % Nodes % y(i)
2908
NodeHolder(3) = Mesh % Nodes % z(i)
2909
2910
NodeHolder = MATMUL(RotationMatrix,NodeHolder)
2911
2912
Mesh % Nodes % x(i) = NodeHolder(1)
2913
Mesh % Nodes % y(i) = NodeHolder(2)
2914
Mesh % Nodes % z(i) = NodeHolder(3)
2915
END DO
2916
2917
END SUBROUTINE RotateMesh
2918
2919
SUBROUTINE DeallocateElement(Element)
2920
2921
IMPLICIT NONE
2922
TYPE(Element_t) :: Element
2923
2924
IF ( ASSOCIATED( Element % NodeIndexes ) ) &
2925
DEALLOCATE( Element % NodeIndexes )
2926
Element % NodeIndexes => NULL()
2927
2928
IF ( ASSOCIATED( Element % EdgeIndexes ) ) &
2929
DEALLOCATE( Element % EdgeIndexes )
2930
Element % EdgeIndexes => NULL()
2931
2932
IF ( ASSOCIATED( Element % FaceIndexes ) ) &
2933
DEALLOCATE( Element % FaceIndexes )
2934
Element % FaceIndexes => NULL()
2935
2936
IF ( ASSOCIATED( Element % DGIndexes ) ) &
2937
DEALLOCATE( Element % DGIndexes )
2938
Element % DGIndexes => NULL()
2939
2940
IF ( ASSOCIATED( Element % BubbleIndexes ) ) &
2941
DEALLOCATE( Element % BubbleIndexes )
2942
Element % BubbleIndexes => NULL()
2943
2944
IF ( ASSOCIATED( Element % PDefs ) ) &
2945
DEALLOCATE( Element % PDefs )
2946
Element % PDefs => NULL()
2947
2948
END SUBROUTINE DeallocateElement
2949
2950
!Identify front elements connected to the bed, which are sufficiently horizontal
2951
!to warrant reclassification as basal elements.
2952
!Note, only does elements currently connected to the bed. i.e. one row per dt
2953
!Returns:
2954
! NewBasalNode(:), LOGICAL true where frontal node becomes basal
2955
! ExFrontalNode(:), LOGICAL true where a frontal node no longer
2956
! belongs to its front column (though it may still be on the front...)
2957
!
2958
! NOTE, if an error in this subroutine, could be element
2959
! which sits between 2 NewBasalElems
2960
2961
SUBROUTINE ConvertFrontalToBasal(Model, Mesh, FrontMaskName, BotMaskName, &
2962
ZThresh, NewBasalNode, FoundSome)
2963
2964
TYPE(Model_t) :: Model
2965
TYPE(Mesh_t), POINTER :: Mesh
2966
REAL(KIND=dp) :: ZThresh
2967
LOGICAL :: FoundSome
2968
LOGICAL, POINTER :: NewBasalNode(:), ExFrontalNode(:), NewBasalElem(:)
2969
CHARACTER(MAX_NAME_LEN) :: FrontMaskName, BotMaskName
2970
!-------------------------------------------------------
2971
TYPE(Nodes_t) :: Nodes
2972
TYPE(Solver_t), POINTER :: NullSolver => NULL()
2973
TYPE(Element_t), POINTER :: Element, New303Elements(:,:), WorkElements(:)
2974
INTEGER :: i,j,k,n,dummyint, ierr, FrontBCtag, BasalBCtag, count303, &
2975
CountSharedExFrontal, CountSharedNewBasal, SharedExGlobal(2), &
2976
SharedNewGlobal(2), OldElemCount, NewElemCount
2977
INTEGER, POINTER :: NodeIndexes(:), AllSharedExGlobal(:)=>NULL(), &
2978
AllSharedNewGlobal(:)=>NULL(), FrontPerm(:), BotPerm(:)
2979
REAL(KIND=dp) :: Normal(3)
2980
LOGICAL :: ThisBC, Found, Debug
2981
CHARACTER(MAX_NAME_LEN) :: FuncName
2982
2983
FoundSome = .FALSE.
2984
FuncName = "ConvertFrontalToBasal"
2985
Debug = .FALSE.
2986
2987
n = Mesh % NumberOfNodes
2988
ALLOCATE(NewBasalNode(n),&
2989
ExFrontalNode(n),&
2990
FrontPerm(n),&
2991
BotPerm(n),&
2992
NewBasalElem(Mesh % NumberOfBulkElements+1: &
2993
Mesh % NumberOfBulkElements+Mesh % NumberOfBoundaryElements))
2994
2995
NewBasalNode = .FALSE.
2996
ExFrontalNode = .FALSE.
2997
NewBasalElem = .FALSE.
2998
2999
CALL MakePermUsingMask( Model, NullSolver, Mesh, BotMaskName, &
3000
.FALSE., BotPerm, dummyint)
3001
CALL MakePermUsingMask( Model, NullSolver, Mesh, FrontMaskName, &
3002
.FALSE., FrontPerm, dummyint)
3003
3004
!Find frontal BC from logical
3005
DO i=1,Model % NumberOfBCs
3006
ThisBC = ListGetLogical(Model % BCs(i) % Values,FrontMaskName,Found)
3007
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
3008
FrontBCtag = Model % BCs(i) % Tag
3009
EXIT
3010
END DO
3011
3012
!Find basal BC from logical
3013
DO i=1,Model % NumberOfBCs
3014
ThisBC = ListGetLogical(Model % BCs(i) % Values,BotMaskName,Found)
3015
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
3016
BasalBCtag = Model % BCs(i) % Tag
3017
EXIT
3018
END DO
3019
3020
CountSharedExFrontal = 0
3021
CountSharedNewBasal = 0
3022
SharedExGlobal = 0
3023
SharedNewGlobal = 0
3024
3025
!---------------------------------------------------
3026
! Find elements for conversion, and set node switches
3027
!---------------------------------------------------
3028
DO i=Mesh % NumberOfBulkElements + 1, &
3029
Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
3030
3031
Element => Mesh % Elements(i)
3032
IF(Element % BoundaryInfo % Constraint /= FrontBCtag) CYCLE !not on front
3033
IF(Element % TYPE % ElementCode == 101) CYCLE
3034
3035
NodeIndexes => Element % NodeIndexes
3036
3037
IF(.NOT. (ANY(BotPerm(NodeIndexes) > 0) )) CYCLE !not connected to bed
3038
3039
n = Element % TYPE % NumberOfNodes
3040
3041
ALLOCATE(Nodes % x(n), Nodes % y(n), Nodes % z(n))
3042
3043
Nodes % x = Mesh % Nodes % x(NodeIndexes)
3044
Nodes % y = Mesh % Nodes % y(NodeIndexes)
3045
Nodes % z = Mesh % Nodes % z(NodeIndexes)
3046
3047
Normal = NormalVector(Element, Nodes)
3048
3049
!compare element normal to threshold
3050
IF(Normal(3) < ZThresh) THEN
3051
FoundSome = .TRUE.
3052
3053
!Nodes currently on bed become 'ex frontal nodes'
3054
!Nodes not currently on bed become 'new basal nodes'
3055
DO j=1,SIZE(NodeIndexes)
3056
3057
IF(BotPerm(NodeIndexes(j)) > 0) THEN
3058
IF(.NOT. ExFrontalNode(NodeIndexes(j))) THEN !maybe already got in another elem
3059
3060
ExFrontalNode(NodeIndexes(j)) = .TRUE.
3061
3062
!If node is in another partition, need to pass this info
3063
IF(SIZE(Mesh % ParallelInfo % NeighbourList(NodeIndexes(j)) % Neighbours)>1) THEN
3064
CountSharedExFrontal = CountSharedExFrontal + 1
3065
IF(CountSharedExFrontal > 2) CALL Fatal(FuncName, &
3066
"Found more than 2 ExFrontalNodes on partition boundary...")
3067
3068
SharedExGlobal(CountSharedExFrontal) = Mesh % ParallelInfo % GlobalDofs(NodeIndexes(j))
3069
END IF
3070
END IF
3071
ELSE
3072
IF(.NOT. NewBasalNode(NodeIndexes(j))) THEN !maybe already got in another elem
3073
3074
NewBasalNode(NodeIndexes(j)) = .TRUE.
3075
3076
!If node is in another partition, need to pass this info
3077
IF(SIZE(Mesh % ParallelInfo % NeighbourList(NodeIndexes(j)) % Neighbours)>1) THEN
3078
CountSharedNewBasal = CountSharedNewBasal + 1
3079
IF(CountSharedNewBasal > 2) CALL Fatal(FuncName, &
3080
"Found more than 2 NewBasalNodes on partition boundary...")
3081
3082
SharedNewGlobal(CountSharedNewBasal) = &
3083
Mesh % ParallelInfo % GlobalDofs(NodeIndexes(j))
3084
END IF
3085
END IF
3086
3087
3088
END IF
3089
3090
END DO
3091
3092
NewBasalElem(i) = .TRUE.
3093
IF(Debug) PRINT *, ParEnv % MyPE, 'Debug, converting element: ',i,&
3094
' with nodes: ', NodeIndexes
3095
END IF
3096
3097
DEALLOCATE(Nodes % x, Nodes % y, Nodes % z)
3098
END DO
3099
3100
!Distribute information about shared frontal nodes
3101
!which are no longer on the front.
3102
!NOTE: we may also need to pass NewBasalNodes...
3103
IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, shared ex frontal nodes: ',SharedExGlobal
3104
IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, shared new basal nodes: ',SharedNewGlobal
3105
3106
ALLOCATE(AllSharedExGlobal(2*ParEnv % PEs),&
3107
AllSharedNewGlobal(2*ParEnv % PEs))
3108
3109
CALL MPI_ALLGATHER(SharedExGlobal,2,MPI_INTEGER,&
3110
AllSharedExGlobal,2,MPI_INTEGER, ELMER_COMM_WORLD, ierr)
3111
CALL MPI_ALLGATHER(SharedNewGlobal,2,MPI_INTEGER,&
3112
AllSharedNewGlobal,2,MPI_INTEGER, ELMER_COMM_WORLD, ierr)
3113
3114
DO i=1,Mesh % NumberOfNodes
3115
IF(FrontPerm(i) <= 0) CYCLE
3116
IF(ANY(AllSharedExGlobal == Mesh % ParallelInfo % GlobalDOFs(i))) THEN
3117
ExFrontalNode(i) = .TRUE.
3118
FoundSome = .TRUE.
3119
IF(Debug) PRINT *, ParEnv % MyPE, ' Debug, received shared exfrontalnode: ',i
3120
END IF
3121
IF(ANY(AllSharedNewGlobal == Mesh % ParallelInfo % GlobalDOFs(i))) THEN
3122
NewBasalNode(i) = .TRUE.
3123
FoundSome = .TRUE.
3124
IF(Debug) PRINT *, ParEnv % MyPE, ' Debug, received shared newbasalnode: ',i
3125
END IF
3126
END DO
3127
3128
!------------------------------------------------------------------------------
3129
! Cycle front elements, looking for those to convert 404 -> 303 for front interp
3130
! And, also, a rare case where one element is sandwiched between shared ExFrontalNodes
3131
! In this case, cycle
3132
!------------------------------------------------------------------------------
3133
DO j=1,2
3134
3135
count303 = 0
3136
DO i=Mesh % NumberOfBulkElements + 1, &
3137
Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
3138
3139
Element => Mesh % Elements(i)
3140
IF(Element % BoundaryInfo % Constraint /= FrontBCtag) CYCLE !not on front
3141
IF(Element % TYPE % ElementCode == 101) CYCLE
3142
IF(NewBasalElem(i)) CYCLE !element disappears from front entirely
3143
3144
NodeIndexes => Element % NodeIndexes
3145
3146
IF(.NOT. (ANY(BotPerm(NodeIndexes) > 0) )) CYCLE
3147
IF(.NOT. ANY(ExFrontalNode(NodeIndexes))) CYCLE !Not affected
3148
3149
IF(j==2 .AND. Debug) PRINT *, ParEnv % MyPE, ' Debug, switching element: ',&
3150
i,' with nodeindexes ', NodeIndexes
3151
3152
IF(COUNT(ExFrontalNode(NodeIndexes)) /= 1) CYCLE
3153
3154
!iff only change one row of elements at at time, we only get here
3155
!through elements to the side which become 303
3156
count303 = count303 + 1
3157
3158
!First time we just count and allocate...
3159
IF(j==2) THEN
3160
DO k=1,2
3161
New303Elements(count303,k) % TYPE => GetElementType( 303, .FALSE. )
3162
New303Elements(count303,k) % NDOFs = 3
3163
New303Elements(count303,k) % ElementIndex = i
3164
New303Elements(count303,k) % BodyID = Element % BodyID
3165
3166
ALLOCATE(New303Elements(count303,k) % NodeIndexes(3))
3167
END DO
3168
3169
!The temporary frontal element
3170
New303Elements(count303,1) % NodeIndexes = &
3171
PACK(NodeIndexes, (.NOT. ExFrontalNode(NodeIndexes)))
3172
3173
!The temporary basal element
3174
New303Elements(count303,2) % NodeIndexes = &
3175
PACK(NodeIndexes, ( (BotPerm(NodeIndexes)>0) .OR. NewBasalNode(NodeIndexes) ) )
3176
3177
DO k=1,2
3178
ALLOCATE(New303Elements(count303,k) % BoundaryInfo)
3179
New303Elements(count303,k) % BoundaryInfo % Left => Element % BoundaryInfo % Left
3180
New303Elements(count303,k) % BoundaryInfo % Right => Element % BoundaryInfo % Right
3181
3182
IF(k==1) THEN
3183
n = FrontBCtag
3184
ELSE
3185
n = BasalBCtag
3186
END IF
3187
3188
New303Elements(count303,k) % BoundaryInfo % Constraint = n
3189
END DO
3190
3191
IF(Debug) PRINT *, ParEnv % MyPE, ' debug, new frontal element ',i,' has nodes: ', &
3192
New303Elements(count303,1) % NodeIndexes
3193
3194
IF(Debug) PRINT *, ParEnv % MyPE, ' debug, new basal element ',i,' has nodes: ', &
3195
New303Elements(count303,2) % NodeIndexes
3196
END IF
3197
END DO
3198
3199
IF(j==1) THEN
3200
ALLOCATE(New303Elements(count303,2))
3201
END IF
3202
3203
END DO
3204
3205
!-------------------------------------------------------
3206
! Now modify mesh % elements accordingly
3207
!-------------------------------------------------------
3208
IF(FoundSome) THEN
3209
3210
OldElemCount = Mesh % NumberOfBulkElements + &
3211
Mesh % NumberOfBoundaryElements
3212
NewElemCount = OldElemCount + count303
3213
3214
ALLOCATE(WorkElements(NewElemCount))
3215
WorkElements(1:OldElemCount) = Mesh % Elements(1:OldElemCount)
3216
3217
DO i=1,count303
3218
n = New303Elements(i,1) % ElementIndex
3219
3220
Element => WorkElements(n)
3221
3222
CALL FreeElementStuff(Element)
3223
3224
Element = New303Elements(i,1)
3225
Element => WorkElements(OldElemCount + i)
3226
3227
Element = New303Elements(i,2)
3228
Element % ElementIndex = OldElemCount + i
3229
END DO
3230
3231
! Change constraint on NewBasalElem
3232
DO i=LBOUND(NewBasalElem,1),UBOUND(NewBasalElem,1)
3233
IF(.NOT. NewBasalElem(i)) CYCLE
3234
WorkElements(i) % BoundaryInfo % Constraint = BasalBCtag
3235
END DO
3236
3237
DEALLOCATE(Mesh % Elements)
3238
Mesh % NumberOfBoundaryElements = Mesh % NumberOfBoundaryElements + count303
3239
Mesh % Elements => WorkElements
3240
END IF
3241
3242
CALL SParIterAllReduceOR(FoundSome)
3243
3244
NULLIFY(WorkElements)
3245
3246
!TODO: Free New303Elements
3247
DEALLOCATE(AllSharedExGlobal, AllSharedNewGlobal, &
3248
NewBasalElem, FrontPerm, BotPerm, ExFrontalNode)
3249
3250
END SUBROUTINE ConvertFrontalToBasal
3251
3252
SUBROUTINE FreeElementStuff(Element)
3253
TYPE(Element_t), POINTER :: Element
3254
IF(ASSOCIATED(Element % NodeIndexes)) DEALLOCATE(Element % NodeIndexes)
3255
IF(ASSOCIATED(Element % EdgeIndexes)) DEALLOCATE(Element % EdgeIndexes)
3256
IF(ASSOCIATED(Element % FaceIndexes)) DEALLOCATE(Element % FaceIndexes)
3257
IF(ASSOCIATED(Element % BubbleIndexes)) DEALLOCATE(Element % BubbleIndexes)
3258
IF(ASSOCIATED(Element % DGIndexes)) DEALLOCATE(Element % DGIndexes)
3259
IF(ASSOCIATED(Element % PDefs)) DEALLOCATE(Element % PDefs)
3260
END SUBROUTINE FreeElementStuff
3261
3262
3263
!Turns off (or back on) a specified solver, and adds a string "Save Exec When"
3264
! to solver % values to allow it to be switched back on to the correct setting.
3265
SUBROUTINE SwitchSolverExec(Solver, Off)
3266
3267
IMPLICIT NONE
3268
3269
TYPE(Solver_t) :: Solver
3270
LOGICAL :: Off
3271
!-----------------------------------------
3272
CHARACTER(MAX_NAME_LEN) :: SaveExecWhen
3273
LOGICAL :: Found
3274
3275
SaveExecWhen = ListGetString(Solver % Values, "Save Exec When", Found)
3276
IF(.NOT. Found) THEN
3277
SaveExecWhen = ListGetString(Solver % Values, 'Exec Solver', Found)
3278
IF(.NOT. Found) SaveExecWhen = 'always'
3279
CALL ListAddString(Solver % Values, 'Save Exec When', SaveExecWhen)
3280
END IF
3281
3282
IF(Off) THEN
3283
3284
!Turning the solver off
3285
Solver % SolverExecWhen = SOLVER_EXEC_NEVER
3286
CALL ListAddString(Solver % Values, 'Exec Solver', 'Never')
3287
3288
ELSE
3289
3290
CALL ListAddString(Solver % Values, 'Exec Solver', SaveExecWhen)
3291
3292
SELECT CASE( SaveExecWhen )
3293
CASE( 'never' )
3294
Solver % SolverExecWhen = SOLVER_EXEC_NEVER
3295
CASE( 'always' )
3296
Solver % SolverExecWhen = SOLVER_EXEC_ALWAYS
3297
CASE( 'after simulation', 'after all' )
3298
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_ALL
3299
CASE( 'before simulation', 'before all' )
3300
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_ALL
3301
CASE( 'before timestep' )
3302
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_TIME
3303
CASE( 'after timestep' )
3304
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_TIME
3305
CASE( 'before saving' )
3306
Solver % SolverExecWhen = SOLVER_EXEC_AHEAD_SAVE
3307
CASE( 'after saving' )
3308
Solver % SolverExecWhen = SOLVER_EXEC_AFTER_SAVE
3309
CASE DEFAULT
3310
CALL Fatal("SwitchSolverExec","Programming error here...")
3311
END SELECT
3312
3313
END IF
3314
3315
END SUBROUTINE SwitchSolverExec
3316
3317
SUBROUTINE PlanePointIntersection ( pp, pnorm, p1, p2, p_intersect, found_intersection )
3318
!Get the intersection point between a line and plane in 3D
3319
! Plane defined by point "pp" and norm "pnorm", line defined by points "p1" and "p2"
3320
! Intersection returned in p_intersect
3321
!found_intersection = .FALSE. if they happen to be parallel
3322
3323
REAL(KIND=dp) :: pp(3), pnorm(3), p1(3), p2(3), p_intersect(3)
3324
LOGICAL :: found_intersection
3325
!----------------------------
3326
REAL(KIND=dp) :: pl(3), dist
3327
3328
pl = p2 - p1
3329
3330
IF(ABS(DOT_PRODUCT(pl,pnorm)) < EPSILON(1.0_dp)) THEN
3331
!Line and plane are parallel...
3332
found_intersection = .FALSE.
3333
RETURN
3334
END IF
3335
3336
dist = DOT_PRODUCT((pp - p1), pnorm) / DOT_PRODUCT(pl,pnorm)
3337
3338
p_intersect = p1 + dist*pl
3339
found_intersection = .TRUE.
3340
3341
END SUBROUTINE PlanePointIntersection
3342
3343
SUBROUTINE LineSegmentsIntersect ( a1, a2, b1, b2, intersect_point, does_intersect )
3344
! Find if two 2D line segments intersect
3345
! Line segment 'a' runs from point a1 => a2, same for b
3346
3347
IMPLICIT NONE
3348
3349
REAL(KIND=dp) :: a1(2), a2(2), b1(2), b2(2), intersect_point(2)
3350
LOGICAL :: does_intersect
3351
!-----------------------
3352
REAL(KIND=dp) :: r(2), s(2), rxs, bma(2), t, u
3353
3354
3355
does_intersect = .FALSE.
3356
intersect_point = 0.0_dp
3357
3358
r = a2 - a1
3359
s = b2 - b1
3360
3361
rxs = VecCross2D(r,s)
3362
3363
IF(rxs == 0.0_dp) RETURN
3364
3365
bma = b1 - a1
3366
3367
t = VecCross2D(bma,s) / rxs
3368
u = VecCross2D(bma,r) / rxs
3369
3370
IF(t < 0.0_dp .OR. t > 1.0_dp .OR. u < 0.0_dp .OR. u > 1.0_dp) RETURN
3371
3372
intersect_point = a1 + (t * r)
3373
does_intersect = .TRUE.
3374
3375
END SUBROUTINE LineSegmentsIntersect
3376
3377
SUBROUTINE LinesIntersect ( a1, a2, b1, b2, intersect_point, does_intersect )
3378
! Find where two 2D lines intersect
3379
! Line 'a' explicitly defined by points a1, a2 which lie on line, same for b
3380
! based on LineSegmentsIntersect above
3381
3382
IMPLICIT NONE
3383
3384
REAL(KIND=dp) :: a1(2), a2(2), b1(2), b2(2), intersect_point(2)
3385
LOGICAL :: does_intersect
3386
!-----------------------
3387
REAL(KIND=dp) :: r(2), s(2), rxs, bma(2), t, u
3388
3389
3390
does_intersect = .TRUE.
3391
3392
intersect_point = 0.0_dp
3393
3394
r = a2 - a1
3395
s = b2 - b1
3396
3397
rxs = VecCross2D(r,s)
3398
3399
IF(rxs == 0.0_dp) THEN
3400
does_intersect = .FALSE.
3401
RETURN
3402
ENDIF
3403
3404
bma = b1 - a1
3405
3406
t = VecCross2D(bma,s) / rxs
3407
u = VecCross2D(bma,r) / rxs
3408
3409
intersect_point = a1 + (t * r)
3410
3411
END SUBROUTINE LinesIntersect
3412
3413
SUBROUTINE LineSegmLineIntersect ( a1, a2, b1, b2, intersect_point, does_intersect )
3414
! Find if two 2D line segments intersect
3415
! Line segment 'a' runs from point a1 => a2
3416
! Line b is defined by vector b1 -> b2
3417
3418
IMPLICIT NONE
3419
3420
REAL(KIND=dp) :: a1(2), a2(2), b1(2), b2(2), intersect_point(2)
3421
LOGICAL :: does_intersect
3422
!-----------------------
3423
REAL(KIND=dp) :: r(2), s(2), rxs, bma(2), t, u
3424
3425
3426
does_intersect = .FALSE.
3427
intersect_point = 0.0_dp
3428
3429
r = a2 - a1
3430
s = b2 - b1
3431
3432
rxs = VecCross2D(r,s)
3433
3434
IF(rxs == 0.0_dp) RETURN
3435
3436
bma = b1 - a1
3437
3438
t = VecCross2D(bma,s) / rxs
3439
3440
IF(t < 0.0_dp .OR. t > 1.0_dp) RETURN
3441
3442
intersect_point = a1 + (t * r)
3443
does_intersect = .TRUE.
3444
3445
END SUBROUTINE LineSegmLineIntersect
3446
3447
FUNCTION VecCross2D(a, b) RESULT (c)
3448
REAL(KIND=dp) :: a(2), b(2), c
3449
3450
c = a(1)*b(2) - a(2)*b(1)
3451
3452
END FUNCTION VecCross2D
3453
3454
!This subroutine should identify discrete calving events for the
3455
!purposes of local remeshing. For now it returns 1
3456
SUBROUTINE CountCalvingEvents(Model, Mesh,CCount)
3457
TYPE(Model_t) :: Model
3458
TYPE(Mesh_t),POINTER :: Mesh
3459
INTEGER :: CCount
3460
3461
Ccount = 1
3462
END SUBROUTINE CountCalvingEvents
3463
3464
! shortest distance of c to segment ab, a b and c are in 2D
3465
FUNCTION PointLineSegmDist2D(a, b, c) RESULT (pdis)
3466
REAL(KIND=dp) :: a(2), b(2), c(2), n(2), v(2), dd, t, pdis
3467
n=b-a ! Vector ab
3468
dd = (n(1)**2.+n(2)**2.) ! Length of ab squared
3469
dd = DOT_PRODUCT(n,n) ! alternative writing
3470
t = DOT_PRODUCT(c-a,b-a)/dd
3471
dd = MAXVAL( (/0.0_dp, MINVAL( (/1.0_dp,t/) ) /) )
3472
v = c - a - dd * n
3473
pdis=sqrt(v(1)**2.+v(2)**2.)
3474
END FUNCTION PointLineSegmDist2D
3475
3476
! Takes two meshes which are assumed to represent the same domain
3477
! and interpolates variables between them. Uses full dimension
3478
! interpolation (InterpolateMeshToMesh) for all nodes, then picks
3479
! up missing boundary nodes using reduced dim
3480
! (InterpolateVarToVarReduced)
3481
SUBROUTINE SwitchMesh(Model, Solver, OldMesh, NewMesh)
3482
3483
IMPLICIT NONE
3484
3485
TYPE(Model_t) :: Model
3486
TYPE(Solver_t) :: Solver
3487
TYPE(Mesh_t), POINTER :: OldMesh, NewMesh
3488
!-------------------------------------------------
3489
TYPE(Solver_t), POINTER :: WorkSolver
3490
TYPE(Variable_t), POINTER :: Var=>NULL(), NewVar=>NULL(), WorkVar=>NULL()
3491
TYPE(Valuelist_t), POINTER :: Params
3492
TYPE(Matrix_t), POINTER :: WorkMatrix=>NULL()
3493
LOGICAL :: Found, Global, GlobalBubbles, Debug, DoPrevValues, &
3494
NoMatrix, DoOptimizeBandwidth, PrimaryVar, HasValuesInPartition, &
3495
PrimarySolver,CreatedParMatrix
3496
LOGICAL, POINTER :: UnfoundNodes(:)=>NULL(), BulkUnfoundNodes(:)=>NULL()
3497
INTEGER :: i,j,k,DOFs, nrows,n, dummyint, ierr
3498
INTEGER, POINTER :: WorkPerm(:)=>NULL(), SolversToIgnore(:)=>NULL(), &
3499
SurfaceMaskPerm(:)=>NULL(), BottomMaskPerm(:)=>NULL()
3500
REAL(KIND=dp), POINTER :: WorkReal(:)=>NULL(), WorkReal2(:)=>NULL(), PArray(:,:) => NULL()
3501
REAL(KIND=dp) :: FrontOrientation(3), RotationMatrix(3,3), UnRotationMatrix(3,3), &
3502
globaleps, localeps
3503
LOGICAL, ALLOCATABLE :: PartActive(:)
3504
CHARACTER(LEN=MAX_NAME_LEN) :: SolverName, WorkName
3505
3506
INTERFACE
3507
SUBROUTINE InterpolateMeshToMesh( OldMesh, NewMesh, OldVariables, &
3508
NewVariables, UseQuadrantTree, Projector, MaskName, UnfoundNodes )
3509
!------------------------------------------------------------------------------
3510
USE Lists
3511
USE SParIterComm
3512
USE Interpolation
3513
USE CoordinateSystems
3514
!-------------------------------------------------------------------------------
3515
TYPE(Mesh_t), TARGET :: OldMesh, NewMesh
3516
TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables, NewVariables
3517
LOGICAL, OPTIONAL :: UseQuadrantTree
3518
LOGICAL, POINTER, OPTIONAL :: UnfoundNodes(:)
3519
TYPE(Projector_t), POINTER, OPTIONAL :: Projector
3520
CHARACTER(LEN=*),OPTIONAL :: MaskName
3521
END SUBROUTINE InterpolateMeshToMesh
3522
END INTERFACE
3523
3524
SolverName = "SwitchMesh"
3525
Debug = .FALSE.
3526
Params => Solver % Values
3527
CALL Info( 'Remesher', ' ',Level=4 )
3528
CALL Info( 'Remesher', '-------------------------------------',Level=4 )
3529
CALL Info( 'Remesher', ' Switching from old to new mesh...',Level=4 )
3530
CALL Info( 'Remesher', '-------------------------------------',Level=4 )
3531
CALL Info( 'Remesher', ' ',Level=4 )
3532
3533
IF(ASSOCIATED(NewMesh % Variables)) CALL Fatal(SolverName,&
3534
"New mesh already has variables associated!")
3535
3536
! need to set Mesh % MaxNDOFs in NewMesh
3537
CALL SetMeshMaxDOFs(NewMesh)
3538
3539
!interpolation epsilons
3540
globaleps = 1.0E-2_dp
3541
localeps = 1.0E-2_dp
3542
3543
!----------------------------------------------
3544
! Get the orientation of the calving front
3545
! & compute rotation matrix
3546
!----------------------------------------------
3547
FrontOrientation = GetFrontOrientation(Model)
3548
RotationMatrix = ComputeRotationMatrix(FrontOrientation)
3549
UnRotationMatrix = TRANSPOSE(RotationMatrix)
3550
3551
!----------------------------------------------
3552
! Action
3553
!----------------------------------------------
3554
3555
CALL CopyIntrinsicVars(OldMesh, NewMesh)
3556
3557
!----------------------------------------------
3558
! Add Variables to NewMesh
3559
!----------------------------------------------
3560
3561
Var => OldMesh % Variables
3562
3563
ALLOCATE(PartActive(ParEnv % PEs))
3564
CreatedParMatrix = .FALSE.
3565
3566
DO WHILE( ASSOCIATED(Var) )
3567
3568
DoPrevValues = ASSOCIATED(Var % PrevValues)
3569
WorkSolver => Var % Solver
3570
HasValuesInPartition = .TRUE.
3571
3572
!Do nothing if it already exists
3573
!e.g. it's a DOF component added previously
3574
NewVar => VariableGet( NewMesh % Variables, Var % Name, ThisOnly = .TRUE.)
3575
IF(ASSOCIATED(NewVar)) THEN
3576
NULLIFY(NewVar)
3577
Var => Var % Next
3578
CYCLE
3579
END IF
3580
3581
DOFs = Var % DOFs
3582
Global = (SIZE(Var % Values) .EQ. DOFs)
3583
3584
!Allocate storage for values and perm
3585
IF(Global) THEN
3586
ALLOCATE(WorkReal(DOFs))
3587
WorkReal = Var % Values
3588
3589
CALL VariableAdd( NewMesh % Variables, NewMesh, &
3590
Var % Solver, TRIM(Var % Name), &
3591
Var % DOFs, WorkReal)
3592
3593
ELSE !Regular field variable
3594
ALLOCATE(WorkPerm(NewMesh % NumberOfNodes))
3595
3596
IF(.NOT. ASSOCIATED(WorkSolver)) THEN
3597
WRITE(Message, '(a,a,a)') "Variable ",Var % Name," has no solver, unexpected."
3598
CALL Fatal(SolverName, Message)
3599
END IF
3600
3601
PrimaryVar = ASSOCIATED(WorkSolver % Variable, Var)
3602
3603
IF(PrimaryVar) THEN !Take care of the matrix
3604
NoMatrix = ListGetLogical( WorkSolver % Values, 'No matrix',Found)
3605
!Issue here, this will recreate matrix for every variable associated w/ solver.
3606
3607
IF(.NOT. NoMatrix) THEN
3608
IF(ParEnv % MyPE == 0) PRINT *, 'Computing matrix for variable: ',TRIM(Var % Name)
3609
3610
DoOptimizeBandwidth = ListGetLogical( WorkSolver % Values, &
3611
'Optimize Bandwidth', Found )
3612
IF ( .NOT. Found ) DoOptimizeBandwidth = .TRUE.
3613
3614
GlobalBubbles = ListGetLogical( WorkSolver % Values, &
3615
'Bubbles in Global System', Found )
3616
IF ( .NOT. Found ) GlobalBubbles = .TRUE.
3617
3618
WorkMatrix => CreateMatrix(Model, WorkSolver, &
3619
NewMesh, WorkPerm, DOFs, MATRIX_CRS, DoOptimizeBandwidth, &
3620
ListGetString( WorkSolver % Values, 'Equation' ), &
3621
GlobalBubbles = GlobalBubbles )
3622
3623
IF(ASSOCIATED(WorkMatrix)) THEN
3624
WorkMatrix % Comm = ELMER_COMM_WORLD
3625
3626
WorkMatrix % Symmetric = ListGetLogical( WorkSolver % Values, &
3627
'Linear System Symmetric', Found )
3628
3629
WorkMatrix % Lumped = ListGetLogical( WorkSolver % Values, &
3630
'Lumped Mass Matrix', Found )
3631
3632
CALL AllocateVector( WorkMatrix % RHS, WorkMatrix % NumberOfRows )
3633
WorkMatrix % RHS = 0.0_dp
3634
WorkMatrix % RHS_im => NULL()
3635
3636
ALLOCATE(WorkMatrix % Force(WorkMatrix % NumberOfRows, WorkSolver % TimeOrder+1))
3637
WorkMatrix % Force = 0.0_dp
3638
ELSE
3639
!No nodes in this partition now
3640
NoMatrix = .TRUE.
3641
END IF
3642
END IF
3643
3644
IF(.NOT. CreatedParMatrix) &
3645
CALL MPI_AllGather(.NOT. NoMatrix, 1, MPI_LOGICAL, PartActive, 1, MPI_LOGICAL, ELMER_COMM_WORLD, ierr)
3646
3647
IF ( ASSOCIATED(Var % EigenValues) ) THEN
3648
n = SIZE(Var % EigenValues)
3649
3650
IF ( n > 0 ) THEN
3651
WorkSolver % NOFEigenValues = n
3652
CALL AllocateVector( NewVar % EigenValues,n )
3653
CALL AllocateArray( NewVar % EigenVectors, n, &
3654
SIZE(NewVar % Values) )
3655
3656
NewVar % EigenValues = 0.0d0
3657
NewVar % EigenVectors = 0.0d0
3658
IF(.NOT.NoMatrix) THEN
3659
CALL AllocateVector( WorkMatrix % MassValues, SIZE(WorkMatrix % Values) )
3660
WorkMatrix % MassValues = 0.0d0
3661
END IF
3662
END IF
3663
END IF
3664
3665
!Check for duplicate solvers with same var
3666
!Nullify/deallocate and repoint the matrix
3667
!Note: previously this DO loop was after the FreeMatrix
3668
!and pointing below, but this caused double free errors
3669
DO j=1,Model % NumberOfSolvers
3670
IF(ASSOCIATED(WorkSolver, Model % Solvers(j))) CYCLE
3671
IF(.NOT. ASSOCIATED(Model % Solvers(j) % Variable)) CYCLE
3672
IF( TRIM(Model % Solvers(j) % Variable % Name) /= TRIM(Var % Name)) CYCLE
3673
3674
!If the other solver's matrix is the same as WorkSolver matrix, we just
3675
!nullify, otherwise we deallocate. After the first timestep, solvers
3676
!with the same variable will have the same matrix
3677
IF(ASSOCIATED(Model % Solvers(j) % Matrix, WorkSolver % Matrix)) THEN
3678
Model % Solvers(j) % Matrix => NULL()
3679
ELSE
3680
CALL FreeMatrix(Model % Solvers(j) % Matrix)
3681
END IF
3682
!Point this other solver % matrix to the matrix we just created
3683
Model % Solvers(j) % Matrix => WorkMatrix
3684
END DO
3685
3686
!Deallocate the old matrix & repoint
3687
IF(ASSOCIATED(WorkSolver % Matrix)) CALL FreeMatrix(WorkSolver % Matrix)
3688
WorkSolver % Matrix => WorkMatrix
3689
3690
! bit of a hack
3691
! since ParEnv become a pointer to ParMatrix we need to ensure one ParMatrix is formed
3692
! it needs to be from a solver present on all parts hence the all gather further up.
3693
! it seems we only need to this once per timestep/interpolation as ParEnv will have some thing
3694
! to point to. If we don't do this ParEnv % PEs, % MyPE etc. all become nans mucking eveything up!
3695
IF ( ASSOCIATED(WorkSolver % Matrix) .and. ALL(PartActive) .and. .NOT. CreatedParMatrix) THEN
3696
IF (.NOT. ASSOCIATED(WorkSolver % Matrix % ParMatrix) ) THEN
3697
WorkSolver % Mesh => NewMesh
3698
3699
CALL ParallelInitMatrix( WorkSolver, WorkSolver % Matrix, WorkPerm)
3700
CreatedParMatrix = .TRUE.
3701
END IF
3702
END IF
3703
3704
NULLIFY(WorkMatrix)
3705
3706
!NOTE: We don't switch Solver % Variable here, because
3707
!Var % Solver % Var doesn't necessarily point to self
3708
!if solver has more than one variable. We do this below.
3709
ELSE
3710
k = InitialPermutation(WorkPerm, Model, WorkSolver, &
3711
NewMesh, ListGetString(WorkSolver % Values,'Equation'))
3712
END IF !Primary var
3713
3714
HasValuesInPartition = COUNT(WorkPerm>0) > 0
3715
IF(HasValuesInPartition) THEN
3716
ALLOCATE(WorkReal(COUNT(WorkPerm>0)*DOFs))
3717
ELSE
3718
!this is silly but it matches AddEquationBasics
3719
ALLOCATE(WorkReal(NewMesh % NumberOfNodes * DOFs))
3720
END IF
3721
3722
WorkReal = 0.0_dp
3723
CALL VariableAdd( NewMesh % Variables, NewMesh, &
3724
Var % Solver, TRIM(Var % Name), &
3725
Var % DOFs, WorkReal, WorkPerm, &
3726
Var % Output, Var % Secondary, Var % TYPE )
3727
3728
END IF !Not global
3729
3730
NewVar => VariableGet( NewMesh % Variables, Var % Name, ThisOnly = .TRUE. )
3731
IF(.NOT.ASSOCIATED(NewVar)) CALL Fatal(SolverName,&
3732
"Problem creating variable on new mesh.")
3733
3734
IF(DoPrevValues) THEN
3735
ALLOCATE(NewVar % PrevValues( SIZE(NewVar % Values), SIZE(Var % PrevValues,2) ))
3736
END IF
3737
3738
!Add the components of variables with more than one DOF
3739
!NOTE, this implementation assumes the vector variable
3740
!comes before the scalar components in the list.
3741
!e.g., we add Mesh Update and so here we add MU 1,2,3
3742
!SO: next time round, new variable (MU 1) already exists
3743
!and so it's CYCLE'd
3744
IF((DOFs > 1) .AND. (.NOT.Global)) THEN
3745
nrows = SIZE(WorkReal)
3746
DO i=1,DOFs
3747
3748
WorkReal2 => WorkReal( i:nrows-DOFs+i:DOFs )
3749
WorkName = ComponentName(TRIM(Var % Name),i)
3750
CALL VariableAdd( NewMesh % Variables, NewMesh, &
3751
Var % Solver, WorkName, &
3752
1, WorkReal2, WorkPerm, &
3753
Var % Output, Var % Secondary, Var % TYPE )
3754
3755
IF(DoPrevValues) THEN
3756
WorkVar => VariableGet( NewMesh % Variables, WorkName, .TRUE. )
3757
IF(.NOT. ASSOCIATED(WorkVar)) CALL Fatal(SolverName, &
3758
"Error allocating Remesh Update PrevValues.")
3759
3760
NULLIFY(WorkVar % PrevValues)
3761
WorkVar % PrevValues => NewVar % PrevValues(i:nrows-DOFs+i:DOFs,:)
3762
END IF
3763
3764
NULLIFY(WorkReal2)
3765
END DO
3766
END IF
3767
3768
NULLIFY(WorkReal, WorkPerm)
3769
Var => Var % Next
3770
END DO
3771
3772
!Go back through and set non-primary variables to have same % perm as the primary var.
3773
!Bit of a hack - would be nice to somehow do this in one loop...
3774
!Set perms equal if: variable has solver, solver has variable, both variables have perm
3775
Var => NewMesh % Variables
3776
DO WHILE (ASSOCIATED(Var))
3777
3778
WorkSolver => Var % Solver
3779
IF(ASSOCIATED(WorkSolver)) THEN
3780
IF(ASSOCIATED(WorkSolver % Variable % Perm)) THEN
3781
WorkVar => VariableGet(NewMesh % Variables, &
3782
WorkSolver % Variable % Name, .TRUE., UnfoundFatal=.TRUE.)
3783
PrimaryVar = ASSOCIATED(WorkSolver % Variable, Var)
3784
IF(ASSOCIATED(WorkVar) .AND. .NOT. PrimaryVar) THEN
3785
IF(ASSOCIATED(WorkVar % Perm) .AND. ASSOCIATED(Var % Perm)) THEN
3786
Var % Perm = WorkVar % Perm
3787
END IF
3788
END IF
3789
END IF
3790
END IF
3791
3792
Var => Var % Next
3793
END DO
3794
3795
!set partitions to active, so variable can be -global -nooutput
3796
CALL ParallelActive(.TRUE.)
3797
!MPI_BSend buffer issue in this call to InterpolateMeshToMesh
3798
!Free quadrant tree to ensure its rebuilt in InterpolateMeshToMesh (bug fix)
3799
CALL FreeQuadrantTree(OldMesh % RootQuadrant)
3800
CALL InterpolateMeshToMesh( OldMesh, NewMesh, OldMesh % Variables, UnfoundNodes=UnfoundNodes)
3801
! unfound nodes are on or near calving front when terminus advances
3802
! 3D interp missing nodes doesn't require projectablility or to interp calving front seperately
3803
! since there are no important variables only present on calving front
3804
! we only need bulk variables.
3805
! these nodes tend to group together eg when a section of the terminus advances
3806
! need to make sure that we don't interp from any other unfound nodes.
3807
IF(ANY(UnfoundNodes)) THEN
3808
PRINT *, ParEnv % MyPE, ' missing ', COUNT(UnfoundNodes),' out of ',SIZE(UnfoundNodes),&
3809
' nodes in SwitchMesh.'
3810
END IF
3811
3812
! only search for 3D advance extrapolation of bulk and non-projected boundaries
3813
CALL MakePermUsingMask( Model, Solver, NewMesh, "Top Surface Mask", &
3814
.FALSE., SurfaceMaskPerm, dummyint)
3815
CALL MakePermUsingMask( Model, Solver, NewMesh, "Bottom Surface Mask", &
3816
.FALSE., BottomMaskPerm, dummyint)
3817
3818
ALLOCATE(BulkUnfoundNodes(NewMesh % NumberOfNodes))
3819
BulkUnfoundNodes = (SurfaceMaskPerm <= 0) .AND. &
3820
(BottomMaskPerm <= 0) .AND. &
3821
UnfoundNodes
3822
3823
!---------------------------------------------------------
3824
! For top, bottom and calving front BC, do reduced dim
3825
! interpolation to avoid epsilon problems
3826
!---------------------------------------------------------
3827
3828
CALL InterpMaskedBCReduced(Model, Solver, OldMesh, NewMesh, OldMesh % Variables, &
3829
"Top Surface Mask",globaleps=globaleps,localeps=localeps)
3830
CALL InterpMaskedBCReduced(Model, Solver, OldMesh, NewMesh, OldMesh % Variables, &
3831
"Bottom Surface Mask",globaleps=globaleps,localeps=localeps)
3832
3833
! could improve by only required procs entering this
3834
! need this after surface interps otherwise surface nodes with inserts nans into the system
3835
CALL InterpAdvanceUnfoundNodes(OldMesh, NewMesh, BulkUnfoundNodes)
3836
3837
! removed as 2d interp on calving front no longer valid since calving front is
3838
! not projectable
3839
3840
!CALL RotateMesh(OldMesh, RotationMatrix)
3841
!CALL RotateMesh(NewMesh, RotationMatrix)
3842
3843
!CHANGE - need to delete UnfoundNOtes from this statement, or front
3844
!variables not copied across. If you get some odd interpolation artefact,
3845
!suspect this
3846
!CALL InterpMaskedBCReduced(Model, Solver, OldMesh, NewMesh, OldMesh % Variables, &
3847
! "Calving Front Mask", globaleps=globaleps,localeps=localeps)
3848
3849
!NOTE: InterpMaskedBCReduced on the calving front will most likely fail to
3850
! find a few points, due to vertical adjustment to account for GroundedSolver.
3851
! Briefly, the 'DoGL' sections of CalvingRemesh adjust the Z coordinate of
3852
! basal nodes which are grounded, to ensure they match the bed dataset.
3853
! Thus, it's not impossible for points on the new mesh to sit slightly outside
3854
! the old.
3855
! However, these points should sit behind or on the old calving front, so
3856
! InterpMaskedBC... on the bed should get them. Thus the only thing that may
3857
! be missed would be variables defined solely on the front. Currently, none
3858
! of these are important for the next timestep, so this should be fine.
3859
3860
!CALL RotateMesh(NewMesh, UnrotationMatrix)
3861
!CALL RotateMesh(OldMesh, UnrotationMatrix)
3862
3863
!-----------------------------------------------
3864
! Point solvers at the correct mesh and variable
3865
!-----------------------------------------------
3866
3867
!CHANGE
3868
!Needs to be told to ignore certain solvers if using multiple meshes
3869
SolversToIgnore => ListGetIntegerArray(Params, 'Solvers To Ignore')
3870
3871
DO i=1,Model % NumberOfSolvers
3872
WorkSolver => Model % Solvers(i)
3873
3874
!CHANGE - see above
3875
IF (ASSOCIATED(SolversToIgnore)) THEN
3876
IF(ANY(SolversToIgnore(1:SIZE(SolversToIgnore))==i)) CYCLE
3877
END IF
3878
3879
WorkSolver % Mesh => NewMesh !note, assumption here that there's only one active mesh
3880
3881
!hack to get SingleSolver to recompute
3882
!should be taken care of by Mesh % Changed, but
3883
!this is reset by CoupledSolver for some reason
3884
WorkSolver % NumberOfActiveElements = -1
3885
3886
IF(.NOT. ASSOCIATED(WorkSolver % Variable)) CYCLE
3887
IF(WorkSolver % Variable % NameLen == 0) CYCLE !dummy !invalid read
3888
3889
!Check for multiple solvers with same var:
3890
!If one of the duplicate solvers is only executed before the simulation (or never),
3891
!then we don't point the variable at this solver. (e.g. initial groundedmask).
3892
!If both solvers are executed during each timestep, we have a problem.
3893
!If neither are, it doesn't matter, and so the the later occurring solver will have
3894
!the variable pointed at it (arbitrary).
3895
PrimarySolver = .TRUE.
3896
DO j=1,Model % NumberOfSolvers
3897
IF(j==i) CYCLE
3898
IF(.NOT. ASSOCIATED(Model % Solvers(j) % Variable)) CYCLE
3899
IF(TRIM(Model % Solvers(j) % Variable % Name) == WorkSolver % Variable % Name) THEN
3900
3901
IF( (WorkSolver % SolverExecWhen == SOLVER_EXEC_NEVER) .OR. &
3902
(WorkSolver % SolverExecWhen == SOLVER_EXEC_AHEAD_ALL) ) THEN
3903
IF((Model % Solvers(j) % SolverExecWhen == SOLVER_EXEC_NEVER) .OR. &
3904
(Model % Solvers(j) % SolverExecWhen == SOLVER_EXEC_AHEAD_ALL) ) THEN
3905
PrimarySolver = .TRUE.
3906
ELSE
3907
PrimarySolver = .FALSE.
3908
WorkSolver % Matrix => NULL()
3909
EXIT
3910
END IF
3911
ELSE
3912
IF( (Model % Solvers(j) % SolverExecWhen == SOLVER_EXEC_NEVER) .OR. &
3913
(Model % Solvers(j) % SolverExecWhen == SOLVER_EXEC_AHEAD_ALL) ) THEN
3914
PrimarySolver = .TRUE.
3915
EXIT
3916
ELSE
3917
WRITE(Message, '(A,A)') "Unable to determine main solver for variable: ", &
3918
TRIM(WorkSolver % Variable % Name)
3919
CALL Fatal(SolverName, Message)
3920
END IF
3921
END IF
3922
3923
END IF
3924
END DO
3925
3926
WorkVar => VariableGet(NewMesh % Variables, &
3927
WorkSolver % Variable % Name, .TRUE.) !invalid read
3928
3929
IF(ASSOCIATED(WorkVar)) THEN
3930
WorkSolver % Variable => WorkVar
3931
IF(PrimarySolver) WorkVar % Solver => WorkSolver
3932
ELSE
3933
WRITE(Message, '(a,a,a)') "Variable ",WorkSolver % Variable % Name," wasn't &
3934
&correctly switched to the new mesh." !invalid read
3935
PRINT *, i,' debug, solver equation: ', ListGetString(WorkSolver % Values, "Equation")
3936
CALL Fatal(SolverName, Message)
3937
END IF
3938
3939
END DO
3940
3941
3942
NewMesh % Next => OldMesh % Next
3943
Model % Meshes => NewMesh
3944
Model % Mesh => NewMesh
3945
Model % Variables => NewMesh % Variables
3946
3947
!Free old mesh and associated variables
3948
CALL ReleaseMesh(OldMesh)
3949
DEALLOCATE(OldMesh)
3950
DEALLOCATE(UnfoundNodes, BulkUnfoundNodes, SurfaceMaskPerm, BottomMaskPerm)
3951
3952
OldMesh => Model % Meshes
3953
3954
END SUBROUTINE SwitchMesh
3955
3956
SUBROUTINE InterpMaskedBCReduced(Model, Solver, OldMesh, NewMesh, Variables, MaskName, &
3957
SeekNodes, globaleps, localeps)
3958
3959
USE InterpVarToVar
3960
3961
IMPLICIT NONE
3962
3963
TYPE(Model_t) :: Model
3964
TYPE(Solver_t) :: Solver
3965
TYPE(Mesh_t), POINTER :: OldMesh, NewMesh
3966
TYPE(Variable_t), POINTER :: Variables
3967
REAL(KIND=dp), OPTIONAL :: globaleps,localeps
3968
LOGICAL, POINTER, OPTIONAL :: SeekNodes(:)
3969
CHARACTER(LEN=*) :: MaskName
3970
!----------------------------
3971
TYPE(Variable_t), POINTER :: Var
3972
INTEGER, POINTER :: OldMaskPerm(:)=>NULL(), NewMaskPerm(:)=>NULL()
3973
INTEGER, POINTER :: InterpDim(:)
3974
INTEGER :: i,j,dummyint,BCTag
3975
REAL(KIND=dp) :: geps,leps
3976
LOGICAL :: Debug, skip, PartMask, Complete, ThisBC, Found
3977
#ifdef ELMER_BROKEN_MPI_IN_PLACE
3978
LOGICAL :: buffer
3979
#endif
3980
LOGICAL, POINTER :: OldMaskLogical(:), NewMaskLogical(:), UnfoundNodes(:)=>NULL(), OldElemMask(:)
3981
LOGICAL, ALLOCATABLE :: PartsMask(:), FoundNode(:)
3982
CHARACTER(LEN=MAX_NAME_LEN) :: HeightName, Solvername
3983
INTEGER, ALLOCATABLE :: PartUnfoundCount(:), AllUnfoundDOFS(:), UnfoundDOFS(:), disps(:), Unique(:), &
3984
FinalDOFs(:), UnfoundIndex(:), UnfoundShared(:), Repeats(:), RemainingDOFs(:)
3985
LOGICAL, ALLOCATABLE :: PartHasUnfoundNodes(:)
3986
INTEGER :: ClusterSize, ierr, UnfoundCount, min_val, max_val, CountDOFs, CountRepeats, Previous, NodeCount
3987
SolverName = 'InterpMaskedBCReduced'
3988
3989
CALL MakePermUsingMask( Model, Solver, NewMesh, MaskName, &
3990
.FALSE., NewMaskPerm, dummyint)
3991
3992
CALL MakePermUsingMask( Model, Solver, OldMesh, MaskName, &
3993
.FALSE., OldMaskPerm, dummyint)
3994
3995
ALLOCATE(OldMaskLogical(SIZE(OldMaskPerm)),&
3996
NewMaskLogical(SIZE(NewMaskPerm)))
3997
3998
OldMaskLogical = (OldMaskPerm <= 0)
3999
NewMaskLogical = (NewMaskPerm <= 0)
4000
IF(PRESENT(SeekNodes)) NewMaskLogical = &
4001
NewMaskLogical .OR. .NOT. SeekNodes
4002
4003
!create mask of elems as with an unstructred mesh all nodes can be in mask but not elem
4004
DO i=1,Model % NumberOfBCs
4005
ThisBC = ListGetLogical(Model % BCs(i) % Values, MaskName, Found)
4006
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
4007
BCtag = Model % BCs(i) % Tag
4008
EXIT
4009
END DO
4010
4011
ALLOCATE(OldElemMask(OldMesh % NumberOfBulkElements &
4012
+ OldMesh % NumberOfBoundaryElements))
4013
OldElemMask = .TRUE.
4014
DO i=OldMesh % NumberOfBulkElements+1, &
4015
OldMesh % NumberOfBulkElements+OldMesh % NumberOfBoundaryElements
4016
IF(OldMesh % Elements(i) % BoundaryInfo % constraint == BCTag) &
4017
OldElemMask(i) = .FALSE.
4018
END DO
4019
4020
4021
IF(PRESENT(globaleps)) THEN
4022
geps = globaleps
4023
ELSE
4024
geps = 1.0E-4
4025
END IF
4026
4027
IF(PRESENT(localeps)) THEN
4028
leps = localeps
4029
ELSE
4030
leps = 1.0E-4
4031
END IF
4032
4033
!Silly hack - InterpolateVarToVarReduced requires a designated 'height' variable
4034
!name which it considers the primary target. A quick fix here is to just find a
4035
!candidate variable and pass its name.
4036
Var => Variables
4037
DO WHILE(ASSOCIATED(Var))
4038
4039
IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global
4040
(Var % DOFs > 1) .OR. & !-multi-dof
4041
Var % Secondary) THEN !-secondary
4042
Var => Var % Next
4043
CYCLE
4044
ELSE IF(LEN(Var % Name) >= 10) THEN
4045
IF(Var % Name(1:10)=='coordinate') THEN !-coord var
4046
Var => Var % Next
4047
CYCLE
4048
END IF
4049
ELSE
4050
HeightName = TRIM(Var % Name)
4051
EXIT
4052
END IF
4053
END DO
4054
4055
IF(Debug) PRINT *, ParEnv % MyPE,'Debug, on boundary: ',TRIM(MaskName),' seeking ',&
4056
COUNT(.NOT. NewMaskLogical),' of ',SIZE(NewMaskLogical),' nodes.'
4057
4058
ALLOCATE(InterpDim(1))
4059
InterpDim(1) = 3
4060
4061
CALL ParallelActive(.TRUE.)
4062
CALL InterpolateVarToVarReduced(OldMesh, NewMesh, HeightName, InterpDim, &
4063
UnfoundNodes, OldMaskLogical, NewMaskLogical, OldElemMask, OldMesh % Variables, &
4064
geps, leps)
4065
4066
4067
UnfoundCount = COUNT(UnfoundNodes)
4068
4069
ClusterSize = ParEnv % PEs
4070
4071
! Gather missing counts at this stage
4072
ALLOCATE(PartUnfoundCount(ClusterSize), &
4073
PartHasUnfoundNodes(ClusterSize))
4074
CALL MPI_AllGather(UnfoundCount, 1, MPI_INTEGER, &
4075
PartUnfoundCount, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
4076
4077
! Process node numbers and global node number important for translation later on
4078
! gather all DOFs from all processes
4079
UnfoundDOFS = PACK(NewMesh % ParallelInfo % GlobalDOFs, UnfoundNodes)
4080
UnfoundIndex = PACK((/ (i,i=1,SIZE(UnfoundNodes)) /),UnfoundNodes .eqv. .TRUE.)
4081
4082
ALLOCATE(disps(ClusterSize))
4083
disps(1) = 0
4084
DO i=2,ClusterSize
4085
disps(i) = disps(i-1) + PartUnfoundCount(i-1)
4086
END DO
4087
ALLOCATE(AllUnfoundDOFS(SUM(PartUnfoundCount)))
4088
CALL MPI_allGatherV(UnfoundDOFS, UnfoundCount, MPI_INTEGER, &
4089
AllUnfoundDOFS, PartUnfoundCount, disps, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
4090
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
4091
4092
! Loop to remove duplicates and order allDOFs in ascending order
4093
CountDOFs=0
4094
CountRepeats=0
4095
IF(SUM(PartUnfoundCount) > 0) THEN
4096
ALLOCATE(unique(SIZE(AllUnfoundDOFS)), repeats(SIZE(AllUnfoundDOFS)))
4097
min_val = minval(AllUnfoundDOFS)-1
4098
max_val = maxval(AllUnfoundDOFS)
4099
4100
DO WHILE (min_val<max_val)
4101
Previous = COUNT(AllUNfoundDOFS>min_val)
4102
CountDOFs = CountDOFs+1
4103
min_val = MINVAL(AllUnfoundDOFS, mask=AllUnfoundDOFS>min_val)
4104
Unique(countDOFs) = min_val
4105
IF(COUNT(AllUnfoundDOFS>min_val) /= Previous-1) THEN
4106
CountRepeats = CountRepeats + 1
4107
Repeats(CountRepeats) = min_val
4108
END IF
4109
END DO
4110
END IF
4111
ALLOCATE(FinalDOFs(CountDOFs), source=Unique(1:countDOFs))
4112
ALLOCATE(UnfoundShared(CountRepeats), source=Repeats(1:CountRepeats))
4113
ALLOCATE(FoundNode(UnfoundCount))
4114
4115
!What you should do here is, rather than looping over the size of UnfoundNodes is
4116
! 1. Construct an ordered list of every GlobalDOF which needs to be found (on ANY partition) (AllMissingGlobal)
4117
! 2. Construct a logical array of the same size which is TRUE where the current partition needs the node (MissingThisGlobal)
4118
! 3. Loop over AllMissingGlobal (possibly with an MPI_Barrier on each loop).
4119
! NOTE - this means you will need to make *every* partition enter this loop (as opposed to just the ones which are missing nodes)
4120
! but this is OK because there's no real performance hit - those partitions would just be waiting anyway
4121
4122
!NewMaskLogical changes purpose, now it masks supporting nodes
4123
NewMaskLogical = (NewMaskPerm <= 0)
4124
4125
PRINT*, ParEnv % MyPE, MaskName, ' NumberofUnfoundpoints', Size(FinalDOFs), UnfoundCount
4126
!Loop through all DOFS with barrier before shared nodes
4127
NodeCount = 0
4128
FoundNode = .FALSE.
4129
4130
Complete = .FALSE.
4131
DO WHILE(.NOT. Complete)
4132
DO i=1, SIZE(FinalDOFs)
4133
IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN
4134
DO j=1, UnfoundCount
4135
IF(UnfoundDOFS(j) == FinalDOFs(i)) THEN
4136
nodecount = j
4137
EXIT
4138
END IF
4139
END DO
4140
ELSE
4141
nodecount = 0
4142
END IF
4143
! no need for a mask since nodes in both arrays in ascending order
4144
IF(ANY(UnfoundShared == FinalDOFs(i))) THEN
4145
! ok to barrier since all parts enter loop and
4146
! have same AllUnfoundDOFs/UnfoundShared
4147
! barrier for shared nodes to endsure these are found at same time
4148
CALL MPI_Barrier(ELMER_COMM_WORLD, ierr)
4149
!nodenumber = UnfoundIndex(nodecount) since different on each process
4150
!always finds correct translation from DOFs to process nodenumber since
4151
!all arrays in ascending order
4152
IF(nodecount == 0) CYCLE
4153
IF(FoundNode(nodecount)) CYCLE
4154
IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN
4155
PRINT *,ParEnv % MyPE,'Didnt find shared point: ', UnfoundIndex(nodecount), &
4156
' x:', NewMesh % Nodes % x(Unfoundindex(nodecount)),&
4157
' y:', NewMesh % Nodes % y(Unfoundindex(nodecount)),&
4158
' z:', NewMesh % Nodes % z(Unfoundindex(nodecount)), &
4159
'GDOF', FinalDOFs(i), &
4160
NewMesh % ParallelInfo % GlobalDOFs(UnfoundIndex(nodecount))
4161
RemainingDOFs = PACK(UnfoundDOFs,.NOT. FoundNode)
4162
CALL InterpolateUnfoundSharedPoint( UnfoundIndex(nodecount), NewMesh, HeightName, InterpDim, &
4163
NodeMask=NewMaskLogical, Variables=NewMesh % Variables, UnfoundDOFS=RemainingDOFs, &
4164
Found=FoundNode(nodecount))
4165
END IF
4166
! no need for a mask since nodes in both arrays in ascending order
4167
ELSE IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN
4168
IF(FoundNode(nodecount)) CYCLE
4169
!nodenumber = UnfoundIndex(nodecount) since different on each process
4170
!always finds correct translation from DOFs to process nodenumber since
4171
!all arrays in ascending order
4172
PRINT *,ParEnv % MyPE,'Didnt find point: ', UnfoundIndex(nodecount), &
4173
' x:', NewMesh % Nodes % x(Unfoundindex(nodecount)),&
4174
' y:', NewMesh % Nodes % y(Unfoundindex(nodecount)),&
4175
' z:', NewMesh % Nodes % z(Unfoundindex(nodecount)), &
4176
'GDOF', FinalDOFs(i), &
4177
NewMesh % ParallelInfo % GlobalDOFs(UnfoundIndex(nodecount))
4178
RemainingDOFs = PACK(UnfoundDOFs,.NOT. FoundNode)
4179
CALL InterpolateUnfoundPoint( UnfoundIndex(nodecount), NewMesh, HeightName, InterpDim, &
4180
NodeMask=NewMaskLogical, Variables=NewMesh % Variables, UnfoundDOFs=RemainingDOFs, &
4181
Found=FoundNode(nodecount))
4182
END IF
4183
END DO
4184
IF(COUNT(FoundNode) == UnfoundCount) Complete = .TRUE.
4185
#ifdef ELMER_BROKEN_MPI_IN_PLACE
4186
buffer = Complete
4187
CALL MPI_AllReduce(buffer, &
4188
#else
4189
CALL MPI_AllReduce(MPI_IN_PLACE, &
4190
#endif
4191
Complete, 1, MPI_LOGICAL, MPI_LAND, ELMER_COMM_WORLD, ierr)
4192
END DO
4193
4194
DEALLOCATE(OldMaskLogical, &
4195
NewMaskLogical, NewMaskPerm, &
4196
OldMaskPerm, UnfoundNodes, &
4197
InterpDim, OldElemMask)
4198
4199
END SUBROUTINE InterpMaskedBCReduced
4200
4201
!Function to return the orientation of a calving front
4202
!If specified in SIF, returns this, otherwise computes it
4203
FUNCTION GetFrontOrientation(Model) RESULT (Orientation)
4204
TYPE(Model_t) :: Model
4205
TYPE(Mesh_t),POINTER :: Mesh
4206
!--------------------------
4207
TYPE(Solver_t), POINTER :: NullSolver => NULL()
4208
TYPE(Variable_t), POINTER :: TimeVar
4209
INTEGER :: i,dummyint,FaceNodeCount, ierr, proc
4210
REAL(KIND=dp) :: Orientation(3),OrientSaved(3),xLeft,yLeft,xRight,yRight
4211
REAL(KIND=dp) :: RecvXL,RecvYL,RecvXR,RecvYR,Temp,PrevTime
4212
REAL(KIND=dp), POINTER :: PArray(:,:) => NULL()
4213
INTEGER, POINTER :: Perm(:), FrontPerm(:)=>NULL(), TopPerm(:)=>NULL(), &
4214
FrontNodeNums(:)=>NULL(),LeftPerm(:)=>NULL(), RightPerm(:)=>NULL()
4215
LOGICAL :: FirstTime=.TRUE.,Constant,Debug=.TRUE.,Parallel,&
4216
HaveRight,HaveLeft, Boss, FirstThisTime
4217
CHARACTER(LEN=MAX_NAME_LEN) :: FrontMaskName, TopMaskName, &
4218
LeftMaskName, RightMaskName
4219
INTEGER :: status(MPI_STATUS_SIZE), iLeft, iRight
4220
SAVE :: FirstTime,Constant,PArray,OrientSaved, Parallel, Boss, FirstThisTime
4221
SAVE :: PrevTime
4222
IF(FirstTime) THEN
4223
FirstTime = .FALSE.
4224
!TODO - this will need to be defined on individual boundary conditions
4225
!if we want to handle multiple calving fronts in same simulation.
4226
PArray => ListGetConstRealArray( Model % Constants,'Front Orientation', &
4227
Constant)
4228
Parallel = (ParEnv % PEs > 1)
4229
Boss = (ParEnv % MyPE == 0) .OR. (.NOT. Parallel)
4230
PrevTime = 0.0_dp
4231
FirstThisTime = .TRUE.
4232
IF(Constant) THEN
4233
CALL Info("GetFrontOrientation","Using predefined Front Orientation from SIF.", Level=6)
4234
DO i=1,3
4235
OrientSaved(i) = PArray(i,1)
4236
END DO
4237
ELSE ! constant not found above
4238
CALL Info("GetFrontOrientation","No predefined Front Orientation, computing instead.", Level=6)
4239
END IF ! constant
4240
END IF ! first time
4241
4242
! check whether already did a front orientation computation this timestep
4243
! Changed Model % Mesh % Variables to avoid segfault as when calling vtusolver after mmg step as
4244
! Model % Variables lost after vtuoutput
4245
TimeVar => VariableGet( Model % Mesh % Variables, 'Timestep' )
4246
IF (Debug) PRINT *, 'Time', TimeVar % Values
4247
IF (Debug) PRINT *, 'PrevTime', PrevTime
4248
IF (Debug) PRINT *, 'FirstThisTime', FirstThisTime
4249
IF (TimeVar % Values(1) > PrevTime ) THEN
4250
FirstThisTime=.TRUE.
4251
END IF
4252
PrevTime = TimeVar % Values(1)
4253
IF (.NOT. FirstThisTime) PRINT *, 'use orientation calculated earlier in this timestep'
4254
IF(Constant .OR. (.NOT. FirstThisTime) ) THEN
4255
Orientation = OrientSaved
4256
RETURN
4257
ELSE
4258
PRINT *, 'computing orientation'
4259
Orientation(3) = 0.0_dp ! always set z-component to 0
4260
Mesh => Model % Mesh
4261
!Get the front line
4262
FrontMaskName = "Calving Front Mask"
4263
TopMaskName = "Top Surface Mask"
4264
CALL MakePermUsingMask( Model, NullSolver, Mesh, TopMaskName, &
4265
.FALSE., TopPerm, dummyint)
4266
CALL MakePermUsingMask( Model, NullSolver, Mesh, FrontMaskName, &
4267
.FALSE., FrontPerm, FaceNodeCount)
4268
LeftMaskName = "Left Sidewall Mask"
4269
RightMaskName = "Right Sidewall Mask"
4270
!Generate perms to quickly get nodes on each boundary
4271
CALL MakePermUsingMask( Model, NullSolver, Mesh, LeftMaskName, &
4272
.FALSE., LeftPerm, dummyint)
4273
CALL MakePermUsingMask( Model, NullSolver, Mesh, RightMaskName, &
4274
.FALSE., RightPerm, dummyint)
4275
iLeft=0
4276
iRight=0
4277
HaveLeft=.FALSE.
4278
HaveRight=.FALSE.
4279
DO i=1,Mesh % NumberOfNodes
4280
IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN
4281
IF( LeftPerm(i) >0 ) THEN
4282
xLeft = Mesh % Nodes % x(i)
4283
yLeft = Mesh % Nodes % y(i)
4284
HaveLeft =.TRUE.
4285
ELSE IF ( RightPerm(i) >0 ) THEN
4286
xRight = Mesh % Nodes % x(i)
4287
yRight = Mesh % Nodes % y(i)
4288
HaveRight =.TRUE.
4289
END IF
4290
END IF
4291
END DO
4292
IF (Debug) PRINT *, 'GetFrontOrientation: HaveLeft, HaveRight', HaveLeft, HaveRight
4293
IF (Parallel) THEN
4294
IF (HaveLeft) PRINT *, 'GetFrontOrientation: xL, yL', xLeft, yLeft
4295
IF (HaveRight) PRINT *, 'GetFrontOrientation: xR, yR', xRight, yRight
4296
IF (Debug) PRINT *, 'communicate the corners'
4297
IF (HaveLeft .AND. (ParEnv % MyPE>0)) THEN ! left not in root
4298
iLeft=ParEnv % MyPE
4299
CALL MPI_BSEND(xLeft, 1, MPI_DOUBLE_PRECISION, &
4300
0 ,7001, ELMER_COMM_WORLD, ierr )
4301
CALL MPI_BSEND(yLeft, 1, MPI_DOUBLE_PRECISION, &
4302
0 ,7002, ELMER_COMM_WORLD, ierr )
4303
IF (Debug) PRINT *, 'sending left'
4304
END IF
4305
IF (HaveRight .AND. (ParEnv % MyPE>0) ) THEN ! right not in root
4306
iRight=ParEnv % MyPE
4307
CALL MPI_BSEND(xRight, 1, MPI_DOUBLE_PRECISION, &
4308
0 , 7003, ELMER_COMM_WORLD, ierr )
4309
CALL MPI_BSEND(yRight, 1, MPI_DOUBLE_PRECISION, &
4310
0 , 7004, ELMER_COMM_WORLD, ierr )
4311
IF (Debug) PRINT *, 'sending right'
4312
END IF
4313
IF (Debug) PRINT *, 'sent the corners'
4314
IF (Boss) THEN
4315
IF (Debug) PRINT *, ParEnv % PEs
4316
IF (.NOT.HaveLeft) THEN
4317
CALL MPI_RECV(RecvXL,1,MPI_DOUBLE_PRECISION,MPI_ANY_SOURCE,&
4318
7001,ELMER_COMM_WORLD, status, ierr )
4319
CALL MPI_RECV(RecvYL,1,MPI_DOUBLE_PRECISION,MPI_ANY_SOURCE,&
4320
7002,ELMER_COMM_WORLD, status, ierr )
4321
xLeft=RecvXL
4322
yLeft=RecvYL
4323
END IF
4324
IF (.NOT. HaveRight) THEN
4325
CALL MPI_RECV(RecvXR,1,MPI_DOUBLE_PRECISION,MPI_ANY_SOURCE,&
4326
7003,ELMER_COMM_WORLD, status, ierr )
4327
CALL MPI_RECV(RecvYR,1,MPI_DOUBLE_PRECISION,MPI_ANY_SOURCE,&
4328
7004,ELMER_COMM_WORLD, status, ierr )
4329
xRight=RecvXR
4330
yRight=RecvYR
4331
END IF
4332
IF (Debug) PRINT *, 'received corners'
4333
IF (Debug) PRINT *, 'GetFrontOrientation: Boss xL, yL, xR, yR', xLeft, yLeft, xRight, yRight
4334
END IF
4335
END IF ! end if parallel
4336
IF (Boss) THEN ! root or not parallel
4337
IF( ABS(xLeft-xRight) < AEPS) THEN
4338
! front orientation is aligned with y-axis
4339
Orientation(2) = 0.0_dp
4340
IF(yRight > yLeft) THEN
4341
Orientation(1)=1.0_dp
4342
ELSE
4343
Orientation(1)=-1.0_dp
4344
END IF
4345
ELSE IF (ABS(yLeft-yRight)<AEPS) THEN
4346
! front orientation is aligned with x-axis
4347
Orientation(1) = 0.0_dp
4348
IF(xRight > xLeft) THEN
4349
Orientation(2)=1.0_dp
4350
ELSE
4351
Orientation(2)=-1.0_dp
4352
END IF
4353
ELSE
4354
! set dot product equal to 0
4355
! no need to ensure it is unit normal, done in ComputeRotation
4356
IF(xRight > xLeft) THEN
4357
Orientation(2)=1.0_dp
4358
ELSE
4359
Orientation(2)=-1.0_dp
4360
END IF
4361
Orientation(1)=Orientation(2)*(yRight-yLeft)/(xLeft-xRight)
4362
END IF
4363
END IF !boss
4364
IF (Parallel) CALL MPI_BCAST(Orientation,3,MPI_DOUBLE_PRECISION, 0, ELMER_COMM_WORLD, ierr)
4365
! deallocations
4366
DEALLOCATE(FrontPerm, TopPerm, LeftPerm, RightPerm)
4367
END IF
4368
Temp=(Orientation(1)**2+Orientation(2)**2+Orientation(3)**2)**0.5
4369
Orientation=Orientation/Temp ! normalized the orientation
4370
IF((.NOT. Constant).AND.Debug) PRINT *, "GetFrontOrientation: ", Orientation,'part',ParEnv % MyPE
4371
FirstThisTime=.FALSE.
4372
OrientSaved=Orientation
4373
END FUNCTION GetFrontOrientation
4374
4375
SUBROUTINE Double2DLogSizeA(Vec, fill)
4376
!only doubles size in one dimension
4377
LOGICAL, ALLOCATABLE :: Vec(:,:)
4378
LOGICAL, OPTIONAL :: fill
4379
!----------------------------------------
4380
LOGICAL, ALLOCATABLE :: WorkVec(:,:)
4381
INTEGER, ALLOCATABLE :: D(:)
4382
4383
ALLOCATE(D(2))
4384
d = SHAPE(Vec)
4385
4386
ALLOCATE(WorkVec(d(1), d(2)))
4387
4388
WorkVec = Vec
4389
4390
DEALLOCATE(Vec)
4391
ALLOCATE(Vec(d(1)*2,d(2)))
4392
4393
IF(PRESENT(fill)) THEN
4394
Vec = fill
4395
ELSE
4396
Vec = .FALSE.
4397
END IF
4398
4399
Vec(1:d(1),:) = WorkVec
4400
4401
END SUBROUTINE Double2DLogSizeA
4402
4403
SUBROUTINE Double2DLogSizeP(Vec, fill)
4404
!only doubles size in one dimension
4405
LOGICAL, POINTER :: Vec(:,:)
4406
LOGICAL, OPTIONAL :: fill
4407
!----------------------------------------
4408
LOGICAL, ALLOCATABLE :: WorkVec(:,:)
4409
INTEGER, ALLOCATABLE :: D(:)
4410
4411
ALLOCATE(D(2))
4412
d = SHAPE(Vec)
4413
4414
ALLOCATE(WorkVec(d(1), d(2)))
4415
4416
WorkVec = Vec
4417
4418
DEALLOCATE(Vec)
4419
ALLOCATE(Vec(d(1)*2,d(2)))
4420
4421
IF(PRESENT(fill)) THEN
4422
Vec = fill
4423
ELSE
4424
Vec = .FALSE.
4425
END IF
4426
4427
Vec(1:d(1),:) = WorkVec
4428
4429
END SUBROUTINE Double2DLogSizeP
4430
4431
SUBROUTINE Double3DArraySizeA(Vec, fill)
4432
!only doubles size in one dimension
4433
INTEGER, ALLOCATABLE :: Vec(:,:,:)
4434
INTEGER, OPTIONAL :: fill
4435
!----------------------------------------
4436
INTEGER, ALLOCATABLE :: WorkVec(:,:,:), D(:)
4437
4438
ALLOCATE(D(3))
4439
d = SHAPE(Vec)
4440
4441
ALLOCATE(WorkVec(d(1), d(2),d(3)))
4442
4443
WorkVec = Vec
4444
4445
DEALLOCATE(Vec)
4446
ALLOCATE(Vec(d(1),d(2),2*d(3)))
4447
4448
IF(PRESENT(fill)) THEN
4449
Vec = fill
4450
ELSE
4451
Vec = 0
4452
END IF
4453
4454
Vec(:,:,1:d(3)) = WorkVec
4455
4456
END SUBROUTINE Double3DArraySizeA
4457
4458
SUBROUTINE Double3DArraySizeP(Vec, fill)
4459
!only doubles size in one dimension
4460
INTEGER, POINTER :: Vec(:,:,:)
4461
INTEGER, OPTIONAL :: fill
4462
!----------------------------------------
4463
INTEGER, ALLOCATABLE :: WorkVec(:,:,:), D(:)
4464
4465
ALLOCATE(D(3))
4466
d = SHAPE(Vec)
4467
4468
ALLOCATE(WorkVec(d(1), d(2),d(3)))
4469
4470
WorkVec = Vec
4471
4472
DEALLOCATE(Vec)
4473
ALLOCATE(Vec(d(1),d(2),2*d(3)))
4474
4475
IF(PRESENT(fill)) THEN
4476
Vec = fill
4477
ELSE
4478
Vec = 0
4479
END IF
4480
4481
Vec(:,:,1:d(3)) = WorkVec
4482
4483
END SUBROUTINE Double3DArraySizeP
4484
4485
SUBROUTINE Double4DArraySizeA(Vec, fill)
4486
!only doubles size in one dimension
4487
INTEGER, ALLOCATABLE :: Vec(:,:,:,:)
4488
INTEGER, OPTIONAL :: fill
4489
!----------------------------------------
4490
INTEGER, ALLOCATABLE :: WorkVec(:,:,:,:), D(:)
4491
4492
ALLOCATE(D(3))
4493
d = SHAPE(Vec)
4494
4495
ALLOCATE(WorkVec(d(1),d(2),d(3),d(4)))
4496
4497
WorkVec = Vec
4498
4499
DEALLOCATE(Vec)
4500
ALLOCATE(Vec(d(1),d(2),d(3),2*d(4)))
4501
4502
IF(PRESENT(fill)) THEN
4503
Vec = fill
4504
ELSE
4505
Vec = 0
4506
END IF
4507
4508
Vec(:,:,:,1:d(4)) = WorkVec
4509
4510
END SUBROUTINE Double4DArraySizeA
4511
4512
SUBROUTINE Double4DArraySizeP(Vec, fill)
4513
!only doubles size in one dimension
4514
INTEGER, POINTER :: Vec(:,:,:,:)
4515
INTEGER, OPTIONAL :: fill
4516
!----------------------------------------
4517
INTEGER, ALLOCATABLE :: WorkVec(:,:,:,:), D(:)
4518
4519
ALLOCATE(D(3))
4520
d = SHAPE(Vec)
4521
4522
ALLOCATE(WorkVec(d(1),d(2),d(3),d(4)))
4523
4524
WorkVec = Vec
4525
4526
DEALLOCATE(Vec)
4527
ALLOCATE(Vec(d(1),d(2),d(3),2*d(4)))
4528
4529
IF(PRESENT(fill)) THEN
4530
Vec = fill
4531
ELSE
4532
Vec = 0
4533
END IF
4534
4535
Vec(:,:,:,1:d(4)) = WorkVec
4536
4537
END SUBROUTINE Double4DArraySizeP
4538
4539
SUBROUTINE GetCalvingEdgeNodes(Mesh, Parallel, Shared, TotalCount)
4540
! Cycle through all 303 elements of GatheredMesh, creating lists of those
4541
! on the top surface, bottom surface, calving front, possibly also lateral
4542
! margins
4543
! Cycle these lists, identifying elements on different boundaries, which
4544
! share nodes (therefore share a theoretical 202 element), construct
4545
! list of these 202 elements
4546
! Add option to Set_MMG3D_Mesh to feed in 202 elements, or find a way to add
4547
! elems after Set_MMG3D_mesh is finished doing its thing
4548
4549
TYPE(Mesh_t),POINTER :: Mesh
4550
TYPE(Element_t),POINTER :: Element
4551
LOGICAL :: Parallel
4552
!---------------
4553
INTEGER :: i,j,k, BoundaryNumber, NumNodes, Match, BoundaryID, TotalCount, &
4554
FirstBdryID, SecondBdryID, CountSoFar
4555
INTEGER, ALLOCATABLE :: ElementNodes(:), Counts(:), BdryNodes(:,:,:), &
4556
CountPairs(:,:),SharedPairs(:,:,:,:),Shared(:, :)
4557
LOGICAL :: Debug, Counted, FirstMatch, SecondMatch, ThirdMatch
4558
CHARACTER(LEN=MAX_NAME_LEN) :: SolverName
4559
SolverName = 'GetCalvingEdgeNodes'
4560
4561
IF (Parallel) CALL Fatal(SolverName, 'Written to run in serial with MMG')
4562
4563
ALLOCATE(Counts(6))
4564
DO i=1,6
4565
Counts(i) = 0
4566
END DO
4567
4568
ALLOCATE(BdryNodes(6,3,100))
4569
4570
DO i=Mesh % NumberOfBulkElements + 1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
4571
Element => Mesh % Elements(i)
4572
ElementNodes = Element % NodeIndexes
4573
BoundaryNumber = Element % BoundaryInfo % constraint
4574
4575
NumNodes = Element % TYPE % NumberOfNodes
4576
IF (NumNodes /= 3) CALL FATAL(Solvername, "BoundaryElements must be 303s")
4577
4578
DO BoundaryID=1,6
4579
IF (BoundaryNumber == BoundaryID) THEN
4580
Counts(BoundaryID) = Counts(BoundaryID) + 1
4581
IF (Counts(BoundaryID) > SIZE(BdryNodes(BoundaryID,1,:))) THEN
4582
IF(Debug) PRINT *, BoundaryID, 'BdryNodes, doubling array size'
4583
CALL Double3DArraySize(BdryNodes)
4584
END IF
4585
!ELSE
4586
! print *, ElementNodes(i), BoundaryNumber
4587
! CALL FATAL(Solvername, "BoundaryElement: has no boundary number")
4588
BdryNodes(BoundaryID,:,Counts(BoundaryID)) = ElementNodes
4589
END IF
4590
END DO
4591
END DO
4592
4593
!set counts for calving and other boundary shared nodes
4594
ALLOCATE(CountPairs(5,5))
4595
CountPairs(:,:) = 0
4596
4597
!set allocatables
4598
ALLOCATE(SharedPairs(5,5,2,100))
4599
4600
! loop for 1-2, 1-3 ... 1-6, 2,3 ... 5,6
4601
!!! assume one is calving front
4602
DO FirstBdryID=1,5
4603
IF (Counts(FirstBdryID) /= 0) THEN
4604
DO i=1, Counts(FirstBdryID)
4605
DO SecondBdryID=FirstBdryID+1,6
4606
IF (Counts(SecondBdryID) /= 0) THEN
4607
DO j=1, Counts(SecondBdryID)
4608
Match = 0
4609
FirstMatch=.FALSE.
4610
SecondMatch=.FALSE.
4611
ThirdMatch=.FALSE.
4612
DO k=1,3
4613
IF (BdryNodes(FirstBdryID,1,i) == BdryNodes(SecondBdryID,k,j)) THEN
4614
FirstMatch=.TRUE.
4615
Match = Match + 1
4616
END IF
4617
IF (BdryNodes(FirstBdryID,2,i) == BdryNodes(SecondBdryID,k,j)) THEN
4618
SecondMatch=.TRUE.
4619
Match = Match + 1
4620
END IF
4621
IF (BdryNodes(FirstBdryID,3,i) == BdryNodes(SecondBdryID,k,j)) THEN
4622
ThirdMatch=.TRUE.
4623
Match = Match + 1
4624
END IF
4625
END DO
4626
IF (Match == 2) THEN
4627
CountPairs(FirstBdryID,SecondBdryID-FirstBdryID) = CountPairs(FirstBdryId,SecondBdryID-FirstBdryID) + 1
4628
IF (CountPairs(FirstBdryID,SecondBdryID-FirstBdryID) > &
4629
SIZE(SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,1,:))) THEN
4630
IF(Debug) PRINT *, 'SharedPairs boundaryIDs-,',FirstBdryID,SecondBdryID,'doubling size of node array.'
4631
CALL Double4DArraySize(SharedPairs)
4632
END IF
4633
IF (FirstMatch .AND. SecondMatch) THEN
4634
SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,:,CountPairs(FirstBdryID,SecondBdryID-FirstBdryID)) &
4635
= BdryNodes(FirstBdryID,1:2,i)
4636
ELSE IF (SecondMatch .AND. ThirdMatch) THEN
4637
SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,:,CountPairs(FirstBdryID,SecondBdryID-FirstBdryID)) &
4638
= BdryNodes(FirstBdryID,2:3,i)
4639
ELSE IF (FirstMatch .AND. ThirdMatch) THEN
4640
SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,1,CountPairs(FirstBdryID,SecondBdryID-FirstBdryID)) &
4641
= BdryNodes(FirstBdryID,1,i)
4642
SharedPairs(FirstBdryID,SecondBdryID-FirstBdryID,2,CountPairs(FirstBdryID,SecondBdryID-FirstBdryID)) &
4643
= BdryNodes(FirstBdryID,3,i)
4644
END IF
4645
ELSE IF (Match == 3) THEN
4646
PRINT*, 'BoundaryElement: Duplicated', FirstBdryID,BdryNodes(FirstBdryID,:,i)
4647
PRINT*, 'BoundaryElement: Duplicated', SecondBdryID,BdryNodes(SecondBdryID,:,j), j
4648
CALL FATAL(Solvername, "BoundaryElement: Duplicated")
4649
END IF
4650
END DO
4651
END IF
4652
END DO
4653
END DO
4654
END IF
4655
END DO
4656
4657
TotalCount=0
4658
DO i=1,5
4659
DO j=1,5
4660
TotalCount=TotalCount+CountPairs(i,j)
4661
END DO
4662
END DO
4663
4664
ALLOCATE(Shared(2, TotalCount))
4665
4666
CountSoFar=0
4667
DO i=1,5
4668
DO j=1,5
4669
Shared(:,1+CountSoFar:CountSoFar+CountPairs(i,j)) = SharedPairs(i,j,:,1:CountPairs(i,j))
4670
CountSoFar = CountSoFar + CountPairs(i,j)
4671
END DO
4672
END DO
4673
4674
END SUBROUTINE GetCalvingEdgeNodes
4675
4676
SUBROUTINE MeshVolume(Mesh, Parallel, Volume, ElemMask, Centroid)
4677
4678
TYPE(Mesh_t), POINTER :: Mesh
4679
LOGICAL :: Parallel
4680
REAL(kind=dp) :: Volume
4681
LOGICAL, OPTIONAL :: ElemMask(:)
4682
REAL(kind=dp), OPTIONAL :: Centroid(3)
4683
!-----------------------------
4684
TYPE(Element_t), POINTER :: Element
4685
INTEGER :: i, j, NBdry, NBulk, n, ierr
4686
INTEGER, ALLOCATABLE :: ElementNodes(:)
4687
REAL(kind=dp), ALLOCATABLE :: Vertices(:,:), Vectors(:,:), PartVolume(:)
4688
REAL(kind=dp) :: det, Centre(3)
4689
4690
NBdry = Mesh % NumberOfBoundaryElements
4691
NBulk = Mesh % NumberOfBulkElements
4692
4693
ALLOCATE(Vertices(4,3), Vectors(3,3))
4694
4695
! calculate volume of each bulk tetra. Add these together to get mesh volume
4696
Volume = 0.0_dp
4697
IF(PRESENT(Centroid)) Centroid = 0.0_dp
4698
DO, i=1, NBulk
4699
IF(PRESENT(ElemMask)) THEN
4700
IF(.NOT. ElemMask(i)) CYCLE
4701
END IF
4702
Element => Mesh % Elements(i)
4703
ElementNodes = Element % NodeIndexes
4704
n = Element % TYPE % NumberOfNodes
4705
4706
IF(n /= 4) CALL FATAL('MeshVolume', 'Only designed for tetra mesh')
4707
4708
! get elem nodes
4709
DO j=1, n
4710
Vertices(j,1) = Mesh % Nodes % x(ElementNodes(j))
4711
Vertices(j,2) = Mesh % Nodes % y(ElementNodes(j))
4712
Vertices(j,3) = Mesh % Nodes % z(ElementNodes(j))
4713
END DO
4714
4715
! calculate vectors AB, AC and AD
4716
! play these in 3x3 matrix
4717
DO j=1,3
4718
Vectors(j,:) = Vertices(1,:) - Vertices(j+1,:)
4719
END DO
4720
4721
! calc matrix det
4722
Det = ABS(Vectors(1,1) * (Vectors(2,2)*Vectors(3,3) - Vectors(2,3)*Vectors(3,2)) &
4723
- Vectors(1,2) * (Vectors(2,1)*Vectors(3,3) - Vectors(2,3)*Vectors(3,1)) &
4724
+ Vectors(1,3) * (Vectors(2,1)*Vectors(3,2) - Vectors(2,2)*Vectors(3,1)))
4725
4726
Centre(1) = SUM(Vertices(:,1))/4
4727
Centre(2) = SUM(Vertices(:,2))/4
4728
Centre(3) = SUM(Vertices(:,3))/4
4729
4730
! tetra volume = det/6
4731
Volume = Volume + Det/6
4732
IF(PRESENT(Centroid)) Centroid = Centroid + Det/6 * Centre
4733
END DO
4734
4735
IF(PRESENT(Centroid)) Centroid = Centroid / Volume
4736
4737
! if parallel calculate total mesh volume over all parts
4738
IF(Parallel) THEN
4739
ALLOCATE(PartVolume(ParEnv % PEs))
4740
CALL MPI_AllGather(Volume, 1, MPI_DOUBLE_PRECISION, &
4741
PartVolume, 1, MPI_DOUBLE_PRECISION, ELMER_COMM_WORLD, ierr)
4742
Volume = SUM(PartVolume)
4743
END IF
4744
4745
END SUBROUTINE MeshVolume
4746
4747
! subroutine to interp variables for missing nodes caused from terminus advance. These are generally on the calving front
4748
! but could be anywhere on the mesh which has advanced beyond the previous timestep
4749
SUBROUTINE InterpAdvanceUnfoundNodes(OldMesh, NewMesh, Unfoundnodes)
4750
4751
IMPLICIT NONE
4752
4753
TYPE(Mesh_t), POINTER :: OldMesh, NewMesh
4754
LOGICAL, POINTER :: UnfoundNodes(:)
4755
!----------------------------
4756
INTEGER :: i,j, UnfoundCount, ClusterSize, ierr, CountDOFs, CountRepeats, min_val, max_val, &
4757
previous, NodeCount
4758
INTEGER, ALLOCATABLE :: PartUnfoundCount(:), UnfoundDOFS(:), UnfoundIndex(:), disps(:), &
4759
AllUnfoundDOFS(:), unique(:), repeats(:), FinalDOFs(:), UnfoundShared(:)
4760
CHARACTER(LEN=MAX_NAME_LEN) :: Solvername
4761
SolverName = 'InterpAdvanceUnfoundNodes'
4762
4763
PRINT*, SolverName
4764
4765
UnfoundCount = COUNT(UnfoundNodes)
4766
4767
ClusterSize = ParEnv % PEs
4768
4769
! Gather missing counts at this stage
4770
ALLOCATE(PartUnfoundCount(ClusterSize))
4771
CALL MPI_AllGather(UnfoundCount, 1, MPI_INTEGER, &
4772
PartUnfoundCount, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
4773
4774
! Process node numbers and global node number important for translation later on
4775
! gather all DOFs from all processes
4776
UnfoundDOFS = PACK(NewMesh % ParallelInfo % GlobalDOFs, UnfoundNodes)
4777
UnfoundIndex = PACK((/ (i,i=1,SIZE(UnfoundNodes)) /),UnfoundNodes .eqv. .TRUE.)
4778
4779
ALLOCATE(disps(ClusterSize))
4780
disps(1) = 0
4781
DO i=2,ClusterSize
4782
disps(i) = disps(i-1) + PartUnfoundCount(i-1)
4783
END DO
4784
ALLOCATE(AllUnfoundDOFS(SUM(PartUnfoundCount)))
4785
CALL MPI_allGatherV(UnfoundDOFS, UnfoundCount, MPI_INTEGER, &
4786
AllUnfoundDOFS, PartUnfoundCount, disps, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
4787
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
4788
4789
! Loop to remove duplicates and order allDOFs in ascending order
4790
CountDOFs=0
4791
CountRepeats=0
4792
IF(SUM(PartUnfoundCount) > 0) THEN
4793
ALLOCATE(unique(SIZE(AllUnfoundDOFS)), repeats(SIZE(AllUnfoundDOFS)))
4794
min_val = minval(AllUnfoundDOFS)-1
4795
max_val = maxval(AllUnfoundDOFS)
4796
4797
DO WHILE (min_val<max_val)
4798
Previous = COUNT(AllUNfoundDOFS>min_val)
4799
CountDOFs = CountDOFs+1
4800
min_val = MINVAL(AllUnfoundDOFS, mask=AllUnfoundDOFS>min_val)
4801
Unique(countDOFs) = min_val
4802
IF(COUNT(AllUnfoundDOFS>min_val) /= Previous-1) THEN
4803
CountRepeats = CountRepeats + 1
4804
Repeats(CountRepeats) = min_val
4805
END IF
4806
END DO
4807
END IF
4808
ALLOCATE(FinalDOFs(CountDOFs), source=Unique(1:countDOFs))
4809
ALLOCATE(UnfoundShared(CountRepeats), source=Repeats(1:CountRepeats))
4810
4811
PRINT*, ParEnv % MyPE, SolverName, Size(FinalDOFs), UnfoundCount
4812
!Loop through all DOFS with barrier before shared nodes
4813
NodeCount = 0
4814
DO i=1, SIZE(FinalDOFs)
4815
IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN
4816
DO j=1, UnfoundCount
4817
IF(UnfoundDOFS(j) == FinalDOFs(i)) nodecount = j
4818
END DO
4819
END IF
4820
! no need for a mask since nodes in both arrays in ascending order
4821
IF(ANY(UnfoundShared == FinalDOFs(i))) THEN
4822
! ok to barrier since all parts enter loop and
4823
! have same AllUnfoundDOFs/UnfoundShared
4824
! barrier for shared nodes to endsure these are found at same time
4825
CALL MPI_Barrier(ELMER_COMM_WORLD, ierr)
4826
!nodenumber = UnfoundIndex(nodecount) since different on each process
4827
!always finds correct translation from DOFs to process nodenumber since
4828
!all arrays in ascending order
4829
IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN
4830
PRINT *,ParEnv % MyPE,'Didnt find shared 3D point: ', UnfoundIndex(nodecount), &
4831
' x:', NewMesh % Nodes % x(Unfoundindex(nodecount)),&
4832
' y:', NewMesh % Nodes % y(Unfoundindex(nodecount)),&
4833
' z:', NewMesh % Nodes % z(Unfoundindex(nodecount)), &
4834
'GDOF', FinalDOFs(i), &
4835
NewMesh % ParallelInfo % GlobalDOFs(UnfoundIndex(nodecount))
4836
CALL InterpolateUnfoundSharedPoint3D( UnfoundIndex(nodecount), NewMesh, &
4837
NewMesh % Variables, FinalDOFs )
4838
END IF
4839
! no need for a mask since nodes in both arrays in ascending order
4840
ELSE IF(ANY(UnfoundDOFS == FinalDOFs(i))) THEN
4841
!nodenumber = UnfoundIndex(nodecount) since different on each process
4842
!always finds correct translation from DOFs to process nodenumber since
4843
!all arrays in ascending order
4844
PRINT *,ParEnv % MyPE,'Didnt find 3D point: ', UnfoundIndex(nodecount), &
4845
' x:', NewMesh % Nodes % x(Unfoundindex(nodecount)),&
4846
' y:', NewMesh % Nodes % y(Unfoundindex(nodecount)),&
4847
' z:', NewMesh % Nodes % z(Unfoundindex(nodecount)), &
4848
'GDOF', FinalDOFs(i), &
4849
NewMesh % ParallelInfo % GlobalDOFs(UnfoundIndex(nodecount))
4850
CALL InterpolateUnfoundPoint3D( UnfoundIndex(nodecount), NewMesh, &
4851
NewMesh % Variables, FinalDOFs )
4852
END IF
4853
END DO
4854
4855
END SUBROUTINE InterpAdvanceUnfoundNodes
4856
4857
SUBROUTINE InterpolateUnfoundPoint3D( NodeNumber, Mesh, Variables, UnfoundDOFS )
4858
4859
! similar process to InterpolateUnfoundPoint but uses bulk element
4860
! 3D interpolation
4861
! also prevents unfound nodes which have yet to be interped from being suppnodes
4862
4863
TYPE(Mesh_t), TARGET, INTENT(INOUT) :: Mesh
4864
TYPE(Variable_t), POINTER, OPTIONAL :: Variables
4865
INTEGER :: NodeNumber
4866
INTEGER, ALLOCATABLE :: UnfoundDOFS(:)
4867
!------------------------------------------------------------------------------
4868
TYPE(Variable_t), POINTER :: Var
4869
TYPE(Element_t),POINTER :: Element
4870
LOGICAL :: Parallel, Debug, HasNeighbours
4871
LOGICAL, ALLOCATABLE :: ValidNode(:), SuppNodeMask(:,:), SuppNodePMask(:,:)
4872
REAL(KIND=dp) :: Point(3), SuppPoint(3), weight, Exponent, distance
4873
REAL(KIND=dp), ALLOCATABLE :: interpedValue(:),SuppNodeWeights(:),SumWeights(:),&
4874
InterpedPValue(:), PSumWeights(:)
4875
INTEGER :: i,j,n,idx,NoNeighbours,NoSuppNodes, MaskCount, PMaskCount
4876
INTEGER, ALLOCATABLE :: WorkInt(:), SuppNodes(:)
4877
INTEGER, POINTER :: Neighbours(:)
4878
Debug = .TRUE.
4879
Parallel = ParEnv % PEs > 1
4880
4881
!The sought point
4882
Point(1) = Mesh % Nodes % x(NodeNumber)
4883
Point(2) = Mesh % Nodes % y(NodeNumber)
4884
Point(3) = Mesh % Nodes % z(NodeNumber)
4885
4886
!IDW exponent
4887
Exponent = 1.0
4888
4889
!Is another partition also contributing to this
4890
NoNeighbours = SIZE(Mesh % ParallelInfo % &
4891
NeighbourList(NodeNumber) % Neighbours) - 1
4892
HasNeighbours = NoNeighbours > 0
4893
4894
IF(HasNeighbours) THEN
4895
! given the complexity of shared point problems put in seperate subroutine
4896
CALL FATAL('InterpolateUnfoundPoint3D', 'Use InterpolateUnfoundSharedPoint3D for shared nodes!')
4897
END IF
4898
4899
ALLOCATE(WorkInt(100))
4900
WorkInt = 0
4901
4902
! cycle trhough bulk elements
4903
NoSuppNodes = 0
4904
DO i=1,Mesh % NumberOfBulkElements
4905
Element => Mesh % Elements(i)
4906
n = Element % TYPE % NumberOfNodes
4907
4908
!Doesn't contain our point
4909
IF(.NOT. ANY(Element % NodeIndexes(1:n)==NodeNumber)) CYCLE
4910
!Cycle element nodes
4911
DO j=1,n
4912
idx = Element % NodeIndexes(j)
4913
IF(idx == NodeNumber) CYCLE
4914
IF(ANY(WorkInt == idx)) CYCLE
4915
! do not include nodes that has yet to be interped
4916
! nodes are interped in GDOF order so if this unfoundnode has a lower
4917
! GDOF then the SuppNode has yet to be interped
4918
IF(ANY(UnfoundDOFS == Mesh % ParallelInfo % GlobalDOFs(idx)) .AND. &
4919
Mesh % ParallelInfo % GlobalDOFs(NodeNumber) < Mesh % ParallelInfo % GlobalDOFs(idx)) CYCLE
4920
4921
NoSuppNodes = NoSuppNodes + 1
4922
WorkInt(NoSuppNodes) = idx
4923
END DO
4924
END DO
4925
4926
ALLOCATE(SuppNodes(NoSuppNodes))
4927
SuppNodes = WorkInt(:NoSuppNodes)
4928
4929
IF(Debug) PRINT *,ParEnv % MyPE,'Debug, seeking nn: ',NodeNumber,' found ',&
4930
NoSuppNodes,' supporting nodes.'
4931
4932
! calculate maskcount and pmaskcount
4933
IF(PRESENT(Variables)) THEN
4934
MaskCount = 0 ! zero since no variables already
4935
PMaskCount = 0
4936
Var => Variables
4937
DO WHILE(ASSOCIATED(Var))
4938
MaskCount = MaskCount + 1
4939
IF(ASSOCIATED(Var % PrevValues)) &
4940
PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)
4941
4942
Var => Var % Next
4943
END DO
4944
END IF
4945
4946
!create suppnode mask and get node values
4947
! get node weights too
4948
ALLOCATE(SuppNodeMask(NoSuppNodes, MaskCount), &
4949
SuppNodePMask(NoSuppNodes, PMaskCount), &
4950
InterpedValue(MaskCount), InterpedPValue(PMaskCount), &
4951
SuppNodeWeights(NoSuppNodes))
4952
SuppNodeMask = .FALSE.; SuppNodePMask = .FALSE.
4953
interpedValue = 0.0_dp; InterpedPValue = 0.0_dp
4954
DO i=1, NoSuppNodes
4955
! SuppNodes for interp
4956
SuppPoint(1) = Mesh % Nodes % x(SuppNodes(i))
4957
SuppPoint(2) = Mesh % Nodes % y(SuppNodes(i))
4958
SuppPoint(3) = Mesh % Nodes % z(SuppNodes(i))
4959
4960
distance = 0.0_dp
4961
DO j=1,3
4962
distance = distance + (Point(j) - SuppPoint(j))**2.0_dp
4963
END DO
4964
distance = distance**0.5_dp
4965
4966
weight = distance**(-exponent)
4967
SuppNodeWeights(i) = weight
4968
4969
IF(PRESENT(Variables)) THEN
4970
MaskCount = 0 ! zero since no variables already
4971
PMaskCount = 0
4972
Var => Variables
4973
DO WHILE(ASSOCIATED(Var))
4974
MaskCount = MaskCount + 1
4975
IF(ASSOCIATED(Var % PrevValues)) &
4976
PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)
4977
IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global
4978
(Var % DOFs > 1) .OR. & !-multi-dof
4979
Var % Secondary) THEN !-secondary
4980
Var => Var % Next
4981
CYCLE
4982
ELSE IF(LEN(Var % Name) >= 10) THEN
4983
IF(Var % Name(1:10)=='coordinate') THEN !-coord var
4984
Var => Var % Next
4985
CYCLE
4986
END IF
4987
END IF
4988
IF(Var % Perm(SuppNodes(i)) <= 0 .OR. &
4989
(Var % Perm(NodeNumber) <= 0)) THEN !-not fully defined here
4990
Var => Var % Next
4991
CYCLE
4992
END IF
4993
4994
SuppNodeMask(i, MaskCount) = .TRUE.
4995
InterpedValue(MaskCount) = interpedvalue(MaskCount) + &
4996
weight * Var % Values(Var % Perm(SuppNodes(i)))
4997
4998
!PrevValues
4999
IF(ASSOCIATED(Var % PrevValues)) THEN
5000
SuppNodePMask(i, PMaskCount) = .TRUE.
5001
DO j=1, SIZE(Var % PrevValues, 2)
5002
n = PMaskCount + j - SIZE(Var % PrevValues, 2)
5003
InterpedPValue(n) = InterpedPValue(n) +&
5004
weight * Var % PrevValues(Var % Perm(SuppNodes(i)), j)
5005
END DO
5006
END IF
5007
5008
Var => Var % Next
5009
END DO
5010
END IF
5011
END DO
5012
5013
!Calculate weights
5014
ALLOCATE(SumWeights(MaskCount), PSumWeights(PMaskCount))
5015
SumWeights = 0.0_dp; PSumWeights = 0.0_dp
5016
DO i=1, NoSuppNodes
5017
DO j=1, MaskCount
5018
!var exists on that node
5019
IF(SuppNodeMask(i,j)) &
5020
SumWeights(j) = SumWeights(j) + SuppNodeWeights(i)
5021
END DO
5022
DO j=1, PMaskCount
5023
IF(SuppNodePMask(i,j)) &
5024
PSumWeights(j) = PSumWeights(j) + SuppNodeWeights(i)
5025
END DO
5026
END DO
5027
5028
interpedValue = interpedValue/SumWeights
5029
InterpedPValue = InterpedPValue/PSumWeights
5030
5031
IF(PRESENT(Variables)) THEN
5032
MaskCount = 0
5033
PMaskCount = 0
5034
Var => Variables
5035
DO WHILE(ASSOCIATED(Var))
5036
MaskCount = MaskCount + 1
5037
IF(ASSOCIATED(Var % PrevValues)) &
5038
PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)
5039
IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global
5040
(Var % DOFs > 1) .OR. & !-multi-dof
5041
Var % Secondary) THEN !-secondary
5042
Var => Var % Next
5043
CYCLE
5044
ELSE IF(LEN(Var % Name) >= 10) THEN
5045
IF(Var % Name(1:10)=='coordinate') THEN !-coord var
5046
Var => Var % Next
5047
CYCLE
5048
END IF
5049
END IF
5050
IF(Var % Perm(NodeNumber) <= 0) THEN !-not fully defined here
5051
Var => Var % Next
5052
CYCLE
5053
END IF
5054
5055
!if any suppnode had variable
5056
IF(ANY(SuppNodeMask(:,MaskCount))) THEN
5057
Var % Values(Var % Perm(NodeNumber)) = interpedValue(MaskCount)
5058
END IF
5059
5060
IF(ASSOCIATED(Var % PrevValues)) THEN
5061
DO j=1, SIZE(Var % PrevValues,2)
5062
n = PMaskCount + j - SIZE(Var % PrevValues, 2)
5063
IF(ANY(SuppNodePMask(:,n))) THEN ! defined at suppnodes
5064
Var % PrevValues(Var % Perm(NodeNumber),j) = InterpedPValue(n)
5065
ELSE
5066
CALL WARN('InterpolateUnfoundPoint3D', 'PrevValues not found on Supp Nodes but defined on node so setting to zero')
5067
Var % PrevValues(Var % Perm(NodeNumber),j) = 0.0_dp
5068
END IF
5069
END DO
5070
END IF
5071
5072
Var => Var % Next
5073
END DO
5074
END IF
5075
5076
END SUBROUTINE InterpolateUnfoundPoint3D
5077
5078
SUBROUTINE InterpolateUnfoundSharedPoint3D( NodeNumber, Mesh, Variables, UnfoundDOFS )
5079
5080
! similar process to InterpolateUnfoundShared Point but uses bulk element
5081
! 3D interpolation
5082
! also prevents unfound nodes which have yet to be interped from being suppnodes
5083
5084
TYPE(Mesh_t), TARGET, INTENT(INOUT) :: Mesh
5085
TYPE(Variable_t), POINTER, OPTIONAL :: Variables
5086
INTEGER :: NodeNumber
5087
INTEGER, ALLOCATABLE :: UnfoundDOFs(:)
5088
!------------------------------------------------------------------------------
5089
TYPE(Variable_t), POINTER :: Var
5090
TYPE(Element_t),POINTER :: Element
5091
LOGICAL :: Parallel, Debug, HasNeighbours
5092
LOGICAL, ALLOCATABLE :: ValidNode(:), SuppNodeMask(:,:), PartSuppNodeMask(:,:,:), &
5093
UseProc(:), SuppNodePMask(:,:), PartSuppNodePMask(:,:,:)
5094
REAL(KIND=dp) :: Point(3), SuppPoint(3), weight, Exponent, distance
5095
REAL(KIND=dp), ALLOCATABLE :: interpedValue(:), PartInterpedValues(:,:), &
5096
SuppNodeWeights(:), PartSuppNodeWeights(:,:), SumWeights(:),&
5097
FinalInterpedValues(:), InterpedPValue(:), PartInterpedPValues(:,:), &
5098
FinalInterpedPValues(:), PSumWeights(:)
5099
INTEGER :: i,j,k,n,idx,NoNeighbours,NoSuppNodes,NoUsedNeighbours,&
5100
proc,status(MPI_STATUS_SIZE), counter, ierr, MaskCount, PMaskCount
5101
INTEGER, ALLOCATABLE :: NeighbourParts(:), WorkInt(:), SuppNodes(:), PartNoSuppNodes(:), WorkInt2(:), &
5102
GDOFs(:), PartGDOFs(:), GDOFLoc(:)
5103
INTEGER, POINTER :: Neighbours(:)
5104
Debug = .TRUE.
5105
Parallel = ParEnv % PEs > 1
5106
5107
!The sought point
5108
Point(1) = Mesh % Nodes % x(NodeNumber)
5109
Point(2) = Mesh % Nodes % y(NodeNumber)
5110
Point(3) = Mesh % Nodes % z(NodeNumber)
5111
5112
!IDW exponent
5113
Exponent = 1.0
5114
5115
!Is another partition also contributing to this
5116
NoNeighbours = SIZE(Mesh % ParallelInfo % &
5117
NeighbourList(NodeNumber) % Neighbours) - 1
5118
HasNeighbours = NoNeighbours > 0
5119
5120
ALLOCATE(WorkInt(100), WorkInt2(100))
5121
WorkInt = 0; WorkInt2 = 0
5122
5123
!Cycle elements containing our node, adding other nodes to list
5124
NoSuppNodes = 0
5125
DO i=1,Mesh % NumberOfBulkElements
5126
Element => Mesh % Elements(i)
5127
n = Element % TYPE % NumberOfNodes
5128
5129
!Doesn't contain our point
5130
IF(.NOT. ANY(Element % NodeIndexes(1:n)==NodeNumber)) CYCLE
5131
!Cycle element nodes
5132
DO j=1,n
5133
idx = Element % NodeIndexes(j)
5134
IF(idx == NodeNumber) CYCLE
5135
IF(ANY(WorkInt == idx)) CYCLE
5136
! do not include nodes that has yet to be interped
5137
! nodes are interped in GDOF order so if this unfoundnode has a lower
5138
! GDOF then the SuppNode has yet to be interped
5139
IF(ANY(UnfoundDOFS == Mesh % ParallelInfo % GlobalDOFs(idx)) .AND. &
5140
Mesh % ParallelInfo % GlobalDOFs(NodeNumber) < Mesh % ParallelInfo % GlobalDOFs(idx)) CYCLE
5141
5142
NoSuppNodes = NoSuppNodes + 1
5143
WorkInt(NoSuppNodes) = idx
5144
WorkInt2(NoSuppNodes) = Mesh % ParallelInfo % GlobalDOFs(idx)
5145
END DO
5146
END DO
5147
5148
ALLOCATE(SuppNodes(NoSuppNodes), GDOFs(NoSuppNodes))
5149
SuppNodes = WorkInt(:NoSuppNodes)
5150
GDOFs = WorkInt2(:NoSuppNodes)
5151
5152
!Create list of neighbour partitions
5153
ALLOCATE(NeighbourParts(NoNeighbours))
5154
counter = 0
5155
DO i=1,NoNeighbours+1
5156
IF(Mesh % ParallelInfo % NeighbourList(NodeNumber) % &
5157
Neighbours(i) == ParEnv % MyPE) CYCLE
5158
counter = counter + 1
5159
NeighbourParts(counter) = Mesh % ParallelInfo &
5160
% NeighbourList(NodeNumber) % Neighbours(i)
5161
END DO
5162
5163
! share number of supp nodes
5164
ALLOCATE(PartNoSuppNodes(NoNeighbours+1))
5165
PartNoSuppNodes(1) = NoSuppNodes
5166
DO i=1, NoNeighbours
5167
proc = NeighbourParts(i)
5168
CALL MPI_BSEND( NoSuppNodes, 1, MPI_INTEGER, proc, &
5169
3998, ELMER_COMM_WORLD,ierr )
5170
CALL MPI_RECV( PartNoSuppNodes(i+1) , 1, MPI_INTEGER, proc, &
5171
3998, ELMER_COMM_WORLD, status, ierr )
5172
END DO
5173
5174
! is the proc used?
5175
NoUsedNeighbours=NoNeighbours
5176
ALLOCATE(UseProc(NoNeighbours+1))
5177
UseProc = .TRUE. ! default is to use proc
5178
IF(ANY(PartNoSuppNodes == 0)) THEN
5179
DO i=1, NoNeighbours+1
5180
IF(PartNoSuppNodes(i) == 0) UseProc(i) = .FALSE.
5181
END DO
5182
!reassign noneighbours to neighbours with suppnodes
5183
NoUsedNeighbours = COUNT(UseProc(2:NoNeighbours+1))
5184
END IF
5185
5186
! change of strategy here. previously supp nodes dropped if a larger
5187
! neighbour present. However this doesn't work for complex geometries often
5188
! resulting from repartitioning. Instead gather global indexes and remove supp
5189
! node if global index present on higher partition
5190
ALLOCATE(PartGDOFs(SUM(PartNoSuppNodes)))
5191
counter = 0
5192
IF(NoSuppNodes /= 0) THEN
5193
PartGDOFs(1:NoSuppNodes) = GDOFs
5194
counter=NoSuppNodes
5195
END IF
5196
DO i=1, NoNeighbours
5197
proc = NeighbourParts(i)
5198
IF(UseProc(1)) THEN ! if this proc has supp nodes send
5199
CALL MPI_BSEND( GDOFs, NoSuppNodes, MPI_INTEGER, proc, &
5200
3999, ELMER_COMM_WORLD,ierr )
5201
END IF
5202
IF(UseProc(i+1)) THEN !neighouring proc has supp nodes
5203
CALL MPI_RECV( PartGDOFs(counter+1:counter+PartNoSuppNodes(i+1)), &
5204
PartNoSuppNodes(i+1), MPI_INTEGER, proc, &
5205
3999, ELMER_COMM_WORLD, status, ierr )
5206
counter=counter+PartNoSuppNodes(i+1)
5207
END IF
5208
END DO
5209
5210
!create list of GDOFS parts
5211
ALLOCATE(GDOFLoc(SUM(PartNoSuppNodes)))
5212
counter=0
5213
DO i=1, NoNeighbours+1
5214
IF(PartNoSuppNodes(i) == 0) CYCLE
5215
IF(i==1) THEN
5216
GDOFLoc(counter+1:counter+PartNoSuppNodes(i)) = ParEnv % MyPE
5217
ELSE
5218
GDOFLoc(counter+1:counter+PartNoSuppNodes(i)) = NeighbourParts(i-1)
5219
END IF
5220
counter = counter + PartNoSuppNodes(i)
5221
END DO
5222
5223
! is global index present on higher part?
5224
DO i=1, NoSuppNodes
5225
DO j=NoSuppNodes+1, SUM(PartNoSuppNodes)
5226
IF(GDOFs(i) == PartGDOFs(j)) THEN
5227
IF(GDOFLoc(j) > ParEnv % MyPE) THEN
5228
WorkInt(i) = 0
5229
END IF
5230
END IF
5231
END DO
5232
END DO
5233
NoSuppNodes = COUNT(WorkInt > 0)
5234
IF(Debug) PRINT *,ParEnv % MyPE, ' Debug, seeking ',NodeNumber,&
5235
' higher partition has node, so deleting...'
5236
5237
DEALLOCATE(SuppNodes)
5238
ALLOCATE(SuppNodes(NoSuppNodes))
5239
SuppNodes = PACK(WorkInt, WorkInt > 0)
5240
DEALLOCATE(WorkInt)
5241
5242
IF(NoSuppNodes == 0) THEN
5243
WRITE(Message, '(i0,A,i0)') ParEnv % MyPE, ' NoSuppNodes = ',NoSuppNodes
5244
CALL WARN('CalvingGeometry', Message)
5245
END IF
5246
5247
!share NoSuppNodes
5248
PartNoSuppNodes(1) = NoSuppNodes
5249
DO i=1, NoNeighbours
5250
proc = NeighbourParts(i)
5251
CALL MPI_BSEND( NoSuppNodes, 1, MPI_INTEGER, proc, &
5252
4000, ELMER_COMM_WORLD,ierr )
5253
CALL MPI_RECV( PartNoSuppNodes(i+1) , 1, MPI_INTEGER, proc, &
5254
4000, ELMER_COMM_WORLD, status, ierr )
5255
END DO
5256
5257
! an mpi_error can occur if one proc has zero supp nodes
5258
! if proc has zero supp nodes it needs to receive mpi info but cannot send any
5259
! therefore neighbours need to allocate less space to avoid nans
5260
NoUsedNeighbours=NoNeighbours
5261
UseProc = .TRUE. ! default is to use proc
5262
IF(ANY(PartNoSuppNodes == 0)) THEN
5263
DO i=1, NoNeighbours+1
5264
IF(PartNoSuppNodes(i) == 0) UseProc(i) = .FALSE.
5265
END DO
5266
!reassign noneighbours to neighbours with suppnodes
5267
NoUsedNeighbours = COUNT(UseProc(2:NoNeighbours+1))
5268
END IF
5269
5270
! calculate maskcount and pmaskcount
5271
IF(PRESENT(Variables)) THEN
5272
MaskCount = 0 ! zero since no variables already
5273
PMaskCount = 0
5274
Var => Variables
5275
DO WHILE(ASSOCIATED(Var))
5276
MaskCount = MaskCount + 1
5277
IF(ASSOCIATED(Var % PrevValues)) &
5278
PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)
5279
5280
Var => Var % Next
5281
END DO
5282
END IF
5283
5284
!create suppnode mask and get node values
5285
! get node weights too
5286
ALLOCATE(SuppNodeMask(NoSuppNodes, MaskCount), &
5287
SuppNodePMask(NoSuppNodes, PMaskCount), &
5288
InterpedValue(MaskCount), InterpedPValue(PMaskCount), &
5289
SuppNodeWeights(NoSuppNodes))
5290
SuppNodeMask = .FALSE.; SuppNodePMask = .FALSE.
5291
interpedValue = 0.0_dp; InterpedPValue = 0.0_dp
5292
DO i=1, NoSuppNodes
5293
! SuppNodes for interp
5294
SuppPoint(1) = Mesh % Nodes % x(SuppNodes(i))
5295
SuppPoint(2) = Mesh % Nodes % y(SuppNodes(i))
5296
SuppPoint(3) = Mesh % Nodes % z(SuppNodes(i))
5297
5298
distance = 0.0_dp
5299
DO j=1,3
5300
distance = distance + (Point(j) - SuppPoint(j))**2.0_dp
5301
END DO
5302
distance = distance**0.5_dp
5303
5304
weight = distance**(-exponent)
5305
SuppNodeWeights(i) = weight
5306
5307
IF(PRESENT(Variables)) THEN
5308
MaskCount = 0 ! zero since no variables already
5309
PMaskCount = 0
5310
Var => Variables
5311
DO WHILE(ASSOCIATED(Var))
5312
MaskCount = MaskCount + 1
5313
IF(ASSOCIATED(Var % PrevValues)) &
5314
PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)
5315
IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global
5316
(Var % DOFs > 1) .OR. & !-multi-dof
5317
Var % Secondary) THEN !-secondary
5318
Var => Var % Next
5319
CYCLE
5320
ELSE IF(LEN(Var % Name) >= 10) THEN
5321
IF(Var % Name(1:10)=='coordinate') THEN !-coord var
5322
Var => Var % Next
5323
CYCLE
5324
END IF
5325
END IF
5326
IF(Var % Perm(SuppNodes(i)) <= 0 .OR. &
5327
(Var % Perm(NodeNumber) <= 0)) THEN !-not fully defined here
5328
Var => Var % Next
5329
CYCLE
5330
END IF
5331
5332
SuppNodeMask(i, MaskCount) = .TRUE.
5333
InterpedValue(MaskCount) = interpedvalue(MaskCount) + &
5334
weight * Var % Values(Var % Perm(SuppNodes(i)))
5335
5336
!PrevValues
5337
IF(ASSOCIATED(Var % PrevValues)) THEN
5338
SuppNodePMask(i, PMaskCount) = .TRUE.
5339
DO j=1, SIZE(Var % PrevValues, 2)
5340
n = PMaskCount + j - SIZE(Var % PrevValues, 2)
5341
InterpedPValue(n) = InterpedPValue(n) +&
5342
weight * Var % PrevValues(Var % Perm(SuppNodes(i)), j)
5343
END DO
5344
END IF
5345
5346
Var => Var % Next
5347
END DO
5348
END IF
5349
END DO
5350
5351
! all parallel communication changed to use NoUsedNeighbours so neighbouring procs
5352
! of those with zero suppnodes (no info) do not over allocate (eg allocate nans)
5353
!share SuppNodeMask
5354
ALLOCATE(PartSuppNodeMask(NoUsedNeighbours+1, 50, MaskCount))
5355
PartSuppNodeMask = .FALSE.
5356
PartSuppNodeMask(1,:NoSuppNodes,:) = SuppNodeMask
5357
counter=0
5358
DO i=1, NoNeighbours
5359
proc = NeighbourParts(i)
5360
IF(UseProc(1)) THEN ! if this proc has supp nodes send
5361
CALL MPI_BSEND( SuppNodeMask, NoSuppNodes*MaskCount, MPI_LOGICAL, proc, &
5362
4001, ELMER_COMM_WORLD,ierr )
5363
END IF
5364
IF(UseProc(i+1)) THEN !neighouring proc has supp nodes
5365
counter=counter+1
5366
CALL MPI_RECV( PartSuppNodeMask(counter+1,:PartNoSuppNodes(i+1),: ) , &
5367
PartNoSuppNodes(i+1)*MaskCount, MPI_LOGICAL, proc, &
5368
4001, ELMER_COMM_WORLD, status, ierr )
5369
END If
5370
END DO
5371
5372
!share SuppNodePMask for prevvalues
5373
ALLOCATE(PartSuppNodePMask(NoUsedNeighbours+1, 50, PMaskCount))
5374
PartSuppNodePMask = .FALSE.
5375
PartSuppNodePMask(1,:NoSuppNodes,:) = SuppNodePMask
5376
counter=0
5377
DO i=1, NoNeighbours
5378
proc = NeighbourParts(i)
5379
IF(UseProc(1)) THEN ! if this proc has supp nodes send
5380
CALL MPI_BSEND( SuppNodePMask, NoSuppNodes*PMaskCount, MPI_LOGICAL, proc, &
5381
4011, ELMER_COMM_WORLD,ierr )
5382
END IF
5383
IF(UseProc(i+1)) THEN !neighouring proc has supp nodes
5384
counter=counter+1
5385
CALL MPI_RECV( PartSuppNodePMask(counter+1,:PartNoSuppNodes(i+1),: ) , &
5386
PartNoSuppNodes(i+1)*PMaskCount, MPI_LOGICAL, proc, &
5387
4011, ELMER_COMM_WORLD, status, ierr )
5388
END If
5389
END DO
5390
5391
!share interped value
5392
ALLOCATE(PartInterpedValues(NoUsedNeighbours+1, MaskCount))
5393
PartInterpedValues(1,1:MaskCount) = InterpedValue
5394
counter=0
5395
DO i=1, NoNeighbours
5396
proc = NeighbourParts(i)
5397
IF(UseProc(1)) THEN ! if this proc has supp nodes send
5398
CALL MPI_BSEND( InterpedValue, MaskCount, MPI_DOUBLE_PRECISION, proc, &
5399
4002, ELMER_COMM_WORLD,ierr )
5400
END IF
5401
IF(UseProc(i+1)) THEN !neighouring prco has supp nodes
5402
counter=counter+1
5403
CALL MPI_RECV( PartInterpedValues(counter+1,:), MaskCount, MPI_DOUBLE_PRECISION, proc, &
5404
4002, ELMER_COMM_WORLD, status, ierr )
5405
END IF
5406
END DO
5407
5408
!share interped prevvalue
5409
ALLOCATE(PartInterpedPValues(NoUsedNeighbours+1, PMaskCount))
5410
PartInterpedPValues(1,1:PMaskCount) = InterpedPValue
5411
counter=0
5412
DO i=1, NoNeighbours
5413
proc = NeighbourParts(i)
5414
IF(UseProc(1)) THEN ! if this proc has supp nodes send
5415
CALL MPI_BSEND( InterpedPValue, PMaskCount, MPI_DOUBLE_PRECISION, proc, &
5416
4012, ELMER_COMM_WORLD,ierr )
5417
END IF
5418
IF(UseProc(i+1)) THEN !neighouring prco has supp nodes
5419
counter=counter+1
5420
CALL MPI_RECV( PartInterpedPValues(counter+1,:), PMaskCount, MPI_DOUBLE_PRECISION, proc, &
5421
4012, ELMER_COMM_WORLD, status, ierr )
5422
END IF
5423
END DO
5424
5425
!share suppnode weights
5426
ALLOCATE(PartSuppNodeWeights(NoUsedNeighbours+1, 25))
5427
PartSuppNodeWeights=0.0_dp
5428
PartSuppNodeWeights(1,1:NoSuppNodes) = SuppNodeWeights
5429
counter=0
5430
DO i=1, NoNeighbours
5431
proc = NeighbourParts(i)
5432
IF(UseProc(1)) THEN ! if this proc has supp nodes send
5433
CALL MPI_BSEND( SuppNodeWeights, NoSuppNodes, MPI_DOUBLE_PRECISION, proc, &
5434
4003, ELMER_COMM_WORLD,ierr )
5435
END IF
5436
IF(UseProc(i+1)) THEN !neighouring prco has supp nodes
5437
counter=counter+1
5438
CALL MPI_RECV( PartSuppNodeWeights(counter+1,1:PartNoSuppNodes(i+1)), &
5439
PartNoSuppNodes(i+1), MPI_DOUBLE_PRECISION, proc, &
5440
4003, ELMER_COMM_WORLD, status, ierr )
5441
END IF
5442
END DO
5443
5444
!calculate interped values
5445
ALLOCATE(FinalInterpedValues(MaskCount), FinalInterpedPValues(PMaskCount))
5446
FinalInterpedValues = 0.0_dp; FinalInterpedPValues = 0.0_dp
5447
! add up interpedvalues
5448
DO i=1, NoUsedNeighbours+1
5449
FinalInterpedValues = FinalInterpedValues + PartInterpedValues(i, :)
5450
FinalInterpedPValues = FinalInterpedPValues + PartInterpedPValues(i, :)
5451
END DO
5452
5453
! convert PartNoSuppNodes to only used procs
5454
ALLOCATE(WorkInt(NoNeighbours+1))
5455
WorkInt=PartNoSuppNodes
5456
DEALLOCATE(PartNoSuppNodes)
5457
ALLOCATE(PartNoSuppNodes(NoUsedNeighbours+1))
5458
counter=0
5459
DO i=1, NoNeighbours+1
5460
IF(i/=1 .AND. .NOT. UseProc(i)) CYCLE
5461
counter=counter+1
5462
PartNoSuppNodes(counter) = WorkInt(i)
5463
END DO
5464
DEALLOCATE(WorkInt)
5465
5466
! calculate weight for each var
5467
ALLOCATE(SumWeights(MaskCount), PSumWeights(PMaskCount))
5468
SumWeights = 0.0_dp; PSumWeights = 0.0_dp
5469
DO i=1, NoUsedNeighbours+1
5470
! loop through procs suppnodes
5471
DO j=1, PartNoSuppNodes(i)
5472
DO k=1, MaskCount
5473
!var exists on that node
5474
IF(PartSuppNodeMask(i,j,k)) THEN
5475
SumWeights(k) = SumWeights(k) + PartSuppNodeWeights(i,j)
5476
END IF
5477
END DO
5478
DO k=1, PMaskCount
5479
!var exists on that node
5480
IF(PartSuppNodePMask(i,j,k)) THEN
5481
PSumWeights(k) = PSumWeights(k) + PartSuppNodeWeights(i,j)
5482
END IF
5483
END DO
5484
END DO
5485
END DO
5486
5487
!interpedvalue/sumweights
5488
FinalInterpedValues = FinalInterpedValues/sumweights
5489
FinalInterpedPValues = FinalInterpedPValues/PSumWeights
5490
5491
!return values
5492
IF(PRESENT(Variables)) THEN
5493
MaskCount = 0; PMaskCount = 0
5494
Var => Variables
5495
DO WHILE(ASSOCIATED(Var))
5496
MaskCount = MaskCount + 1
5497
IF(ASSOCIATED(Var % PrevValues)) &
5498
PMaskCount = PMaskCount + SIZE(Var % PrevValues,2)
5499
5500
IF((SIZE(Var % Values) == Var % DOFs) .OR. & !-global
5501
(Var % DOFs > 1) .OR. & !-multi-dof
5502
Var % Secondary) THEN !-secondary
5503
Var => Var % Next
5504
CYCLE
5505
ELSE IF(LEN(Var % Name) >= 10) THEN
5506
IF(Var % Name(1:10)=='coordinate') THEN !-coord var
5507
Var => Var % Next
5508
CYCLE
5509
END IF
5510
END IF
5511
IF(Var % Perm(NodeNumber) <= 0) THEN !-not fully defined here
5512
Var => Var % Next
5513
CYCLE
5514
END IF
5515
5516
!if any suppnode from any proc has var
5517
IF(ANY(PartSuppNodeMask(:,:,MaskCount))) THEN
5518
Var % Values(Var % Perm(NodeNumber)) = FinalInterpedValues(MaskCount)
5519
END IF
5520
5521
IF(ASSOCIATED(Var % PrevValues)) THEN
5522
DO j=1, SIZE(Var % PrevValues,2)
5523
n = PMaskCount + j - SIZE(Var % PrevValues, 2)
5524
IF(ANY(PartSuppNodePMask(:,:,n))) THEN ! defined at suppnodes
5525
Var % PrevValues(Var % Perm(NodeNumber),j) = FinalInterpedPValues(n)
5526
ELSE
5527
CALL WARN('InterpolateUnfoundSharedPoint3D', &
5528
'PrevValues not found on Supp Nodes but defined on node so setting to zero')
5529
Var % PrevValues(Var % Perm(NodeNumber),j) = 0.0_dp
5530
END IF
5531
END DO
5532
END IF
5533
5534
Var => Var % Next
5535
END DO
5536
END IF
5537
5538
END SUBROUTINE InterpolateUnfoundSharedPoint3D
5539
5540
!Doubles the size of a pointer double precision array
5541
!This version takes a Pointer argument, should
5542
!be used with care...
5543
SUBROUTINE DoubleDPVectorSizeP(Vec, fill)
5544
REAL(kind=dp), POINTER :: Vec(:)
5545
REAL(kind=dp), OPTIONAL :: fill
5546
!----------------------------------------
5547
REAL(kind=dp), ALLOCATABLE :: WorkVec(:)
5548
5549
ALLOCATE(WorkVec(SIZE(Vec)))
5550
WorkVec = Vec
5551
5552
DEALLOCATE(Vec)
5553
ALLOCATE(Vec(2*SIZE(WorkVec)))
5554
5555
IF(PRESENT(fill)) THEN
5556
Vec = fill
5557
ELSE
5558
Vec = 0
5559
END IF
5560
5561
Vec(1:SIZE(WorkVec)) = WorkVec
5562
5563
END SUBROUTINE DoubleDPVectorSizeP
5564
5565
!Doubles the size of a pointer double precision array
5566
!Allocatable array version
5567
SUBROUTINE DoubleDPVectorSizeA(Vec, fill)
5568
REAL(kind=dp), ALLOCATABLE :: Vec(:)
5569
REAL(kind=dp), OPTIONAL :: fill
5570
!----------------------------------------
5571
REAL(kind=dp), ALLOCATABLE :: WorkVec(:)
5572
5573
ALLOCATE(WorkVec(SIZE(Vec)))
5574
WorkVec = Vec
5575
5576
DEALLOCATE(Vec)
5577
ALLOCATE(Vec(2*SIZE(WorkVec)))
5578
5579
IF(PRESENT(fill)) THEN
5580
Vec = fill
5581
ELSE
5582
Vec = 0
5583
END IF
5584
5585
Vec(1:SIZE(WorkVec)) = WorkVec
5586
5587
END SUBROUTINE DoubleDPVectorSizeA
5588
5589
! returns calving polygons if given edge and crevasse info.
5590
! assumes all this is on boss and then broadcast to other procs.
5591
SUBROUTINE GetCalvingPolygons(Mesh, CrevassePaths, EdgeX, EdgeY, Polygon, PolyStart, PolyEnd, GridSize)
5592
IMPLICIT NONE
5593
TYPE(Mesh_t), POINTER :: Mesh
5594
TYPE(CrevassePath_t), POINTER :: CrevassePaths
5595
REAL(kind=dp) :: EdgeX(:), EdgeY(:)
5596
REAL(kind=dp), OPTIONAL :: GridSize
5597
!-------------------------------------------------------------------------
5598
TYPE(CrevassePath_t), POINTER :: CurrentPath
5599
REAL(kind=dp), ALLOCATABLE :: PolyX(:), PolyY(:), Polygon(:,:)
5600
INTEGER, ALLOCATABLE :: PolyStart(:), PolyEnd(:)
5601
INTEGER :: path, i, counter, CrevLen, crop(2), EdgeLen, start, end
5602
REAL(kind=dp) :: StartX, StartY, EndX, EndY, err_buffer
5603
5604
path=0
5605
CurrentPath => CrevassePaths
5606
DO WHILE(ASSOCIATED(CurrentPath))
5607
path=path+1
5608
CurrentPath => CurrentPath % Next
5609
END DO
5610
5611
ALLOCATE(PolyX(100), PolyY(100), PolyStart(path), PolyEnd(path))
5612
counter=0
5613
path=0
5614
CurrentPath => CrevassePaths
5615
DO WHILE(ASSOCIATED(CurrentPath))
5616
path=path+1
5617
5618
start=CurrentPath % NodeNumbers(1)
5619
end=CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
5620
StartX = Mesh % Nodes % x(start)
5621
StartY = Mesh % Nodes % y(start)
5622
EndX = Mesh % Nodes % x(end)
5623
EndY = Mesh % Nodes % y(end)
5624
CrevLen = CurrentPath % NumberOfNodes
5625
5626
crop =0
5627
IF(PRESENT(GridSize)) THEN
5628
err_buffer = GridSize/10
5629
ELSE
5630
err_buffer = 0.0_dp
5631
END IF
5632
5633
DO i=1, SIZE(EdgeX)
5634
IF((EdgeX(i) <= StartX+err_buffer .AND. EdgeX(i) >= StartX-err_buffer) .AND. &
5635
(EdgeY(i) <= StartY+err_buffer .AND. EdgeY(i) >= StartY-err_buffer)) crop(1) = i
5636
IF((EdgeX(i) <= EndX+err_buffer .AND. EdgeX(i) >= EndX-err_buffer) .AND. &
5637
(EdgeY(i) <= EndY+err_buffer .AND. EdgeY(i) >= EndY-err_buffer )) crop(2) = i
5638
END DO
5639
IF(ANY(crop == 0)) CALL FATAL('GetCalvingPolygons', 'Edge not found')
5640
5641
EdgeLen = MAXVAL(crop)-MINVAL(crop)-2+1
5642
5643
DO WHILE(SIZE(PolyX) < Counter+CrevLen+EdgeLen+1)
5644
CALL DoubleDPVectorSize(PolyX)
5645
CALL DoubleDPVectorSize(PolyY)
5646
END DO
5647
5648
PolyStart(path) = Counter+1
5649
DO i=1, CrevLen
5650
counter=counter+1
5651
PolyX(Counter) = Mesh % Nodes % x(CurrentPath % NodeNumbers(i))
5652
PolyY(Counter) = Mesh % Nodes % y(CurrentPath % NodeNumbers(i))
5653
END DO
5654
5655
IF(crop(2) < crop(1)) THEN ! end of crev lines up with start of edge no need to flip edge
5656
PolyX(Counter+1:Counter+EdgeLen) = EdgeX(MINVAL(crop)+1:MAXVAL(crop)-1)
5657
PolyY(Counter+1:Counter+EdgeLen) = EdgeY(MINVAL(crop)+1:MAXVAL(crop)-1)
5658
counter=counter+EdgeLen
5659
ELSE
5660
! since crevasses are plotted left to right if crevasse on part of front facing upstream
5661
! need to add the edge in reverse
5662
DO i=MAXVAL(crop)-1, MINVAL(crop)+1, -1 ! backwards iteration
5663
counter=counter+1
5664
PolyX(Counter) = EdgeX(i)
5665
PolyY(Counter) = EdgeY(i)
5666
END DO
5667
END IF
5668
5669
! add first node in again to complete polygon
5670
counter=counter+1
5671
PolyX(Counter) = StartX
5672
PolyY(counter) = StartY
5673
PolyEnd(path) = Counter
5674
5675
CurrentPath => CurrentPath % Next
5676
END DO
5677
5678
ALLOCATE(Polygon(2, Counter))
5679
Polygon(1,:) = PolyX(1:Counter)
5680
Polygon(2,:) = PolyY(1:Counter)
5681
DEALLOCATE(PolyX, PolyY)
5682
5683
END SUBROUTINE GetCalvingPolygons
5684
5685
SUBROUTINE RemoveInvalidCrevs(Mesh, CrevassePaths, EdgeX, EdgeY, RemoveInsideCrevs, LateralCrevs, &
5686
OnLeft, OnRight, OnFront, GridSize)
5687
IMPLICIT NONE
5688
TYPE(Mesh_t), POINTER :: Mesh
5689
TYPE(CrevassePath_t), POINTER :: CrevassePaths
5690
REAL(kind=dp) :: EdgeX(:), EdgeY(:)
5691
LOGICAL, OPTIONAL :: OnLeft(:),OnRight(:),OnFront(:)
5692
LOGICAL :: RemoveInsideCrevs, LateralCrevs
5693
REAL(kind=dp), OPTIONAL :: GridSize
5694
!-------------------------------------------------
5695
TYPE(CrevassePath_t), POINTER :: CurrentPath, WorkPath, SecondPath
5696
INTEGER :: i,j,k, counter, first, last, path, start, end, startidx, endidx, DeleteEndNodes, spath
5697
REAL(kind=dp), ALLOCATABLE :: Polygons(:,:), PathPoly(:,:)
5698
INTEGER, ALLOCATABLE :: PolyStart(:), PolyEnd(:), WorkInt(:)
5699
REAL(kind=dp) :: xx, yy, StartX, StartY, EndX, EndY, err_buffer, area1, area2
5700
LOGICAL :: inside, debug, Found(2), overlap
5701
LOGICAL, ALLOCATABLE :: DeleteNode(:), DeleteElement(:), OnEdge(:)
5702
5703
IF(.NOT. LateralCrevs) THEN
5704
! assumption here is that invalid crevs with no interior already removed by
5705
! a previous call. If lateral edges have been added to crevs cannot filter using edges.
5706
5707
! if no part of crev is in interior remove
5708
CurrentPath => CrevassePaths
5709
DO WHILE(ASSOCIATED(CurrentPath))
5710
Found = .FALSE.
5711
! buffer for floating point errors
5712
IF(PRESENT(GridSize)) THEN
5713
err_buffer = GridSize/10
5714
ELSE
5715
err_buffer = 0.0_dp
5716
END IF
5717
5718
ALLOCATE(OnEdge(CurrentPath % NumberOfNodes))
5719
OnEdge = .FALSE.
5720
DO i=1, CurrentPath % NumberOfNodes
5721
xx = Mesh % Nodes % x(CurrentPath % NodeNumbers(i))
5722
yy = Mesh % Nodes % y(CurrentPath % NodeNumbers(i))
5723
DO j=1, SIZE(EdgeX)
5724
IF((EdgeX(j) <= xx+err_buffer .AND. EdgeX(j) >= xx-err_buffer) .AND. &
5725
(EdgeY(j) <= yy+err_buffer .AND. EdgeY(j) >= yy-err_buffer)) OnEdge(i) = .TRUE.
5726
END DO
5727
END DO
5728
5729
IF(ALL(OnEdge)) CurrentPath % Valid = .FALSE.
5730
5731
DEALLOCATE(OnEdge)
5732
CurrentPath => CurrentPath % Next
5733
END DO
5734
END IF
5735
5736
! remove paths that end on both lateral boundaries
5737
IF(PRESENT(OnLeft) .OR. PRESENT(OnRight)) THEN
5738
!CALL Assert((PRESENT(OnLeft) .AND. PRESENT(OnRight)), FuncName, &
5739
! "Provided only one of OnLeft/OnRight!")
5740
5741
!Check that crevasse path doesn't begin and end on same lateral margin
5742
CurrentPath => CrevassePaths
5743
DO WHILE(ASSOCIATED(CurrentPath))
5744
!Check node OnLeft, OnRight
5745
First = CurrentPath % NodeNumbers(1)
5746
Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
5747
IF((OnLeft(First) .AND. OnLeft(Last)) .OR. &
5748
(OnRight(First) .AND. OnRight(Last))) THEN
5749
CurrentPath % Valid = .FALSE.
5750
END IF
5751
CurrentPath => CurrentPath % Next
5752
END DO
5753
5754
!Actually remove previous marked
5755
CurrentPath => CrevassePaths
5756
DO WHILE(ASSOCIATED(CurrentPath))
5757
WorkPath => CurrentPath % Next
5758
5759
IF(.NOT. CurrentPath % Valid) THEN
5760
IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath
5761
CALL RemoveCrevassePath(CurrentPath)
5762
IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path which &
5763
&starts and ends on same margin")
5764
END IF
5765
CurrentPath => WorkPath
5766
END DO
5767
END IF
5768
5769
! crop crev path so it ends on edge node
5770
CurrentPath => CrevassePaths
5771
DO WHILE(ASSOCIATED(CurrentPath))
5772
5773
Found = .FALSE.
5774
! buffer for floating point errors
5775
IF(PRESENT(GridSize)) THEN
5776
err_buffer = GridSize/10
5777
ELSE
5778
err_buffer = 0.0_dp
5779
END IF
5780
5781
DO i=1, CurrentPath % NumberOfNodes-1
5782
IF(.NOT. Found(1)) THEN
5783
start=CurrentPath % NodeNumbers(i)
5784
startidx=i
5785
END IF
5786
IF(.NOT. Found(2)) THEN
5787
end=CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes+1-i)
5788
endidx=CurrentPath % NumberOfNodes+1-i
5789
END IF
5790
StartX = Mesh % Nodes % x(start)
5791
StartY = Mesh % Nodes % y(start)
5792
EndX = Mesh % Nodes % x(end)
5793
EndY = Mesh % Nodes % y(end)
5794
DO j=1, SIZE(EdgeX)
5795
IF((EdgeX(j) <= StartX+err_buffer .AND. EdgeX(j) >= StartX-err_buffer) .AND. &
5796
(EdgeY(j) <= StartY+err_buffer .AND. EdgeY(j) >= StartY-err_buffer)) Found(1) = .TRUE.
5797
IF((EdgeX(j) <= EndX+err_buffer .AND. EdgeX(j) >= EndX-err_buffer) .AND. &
5798
(EdgeY(j) <= EndY+err_buffer .AND. EdgeY(j) >= EndY-err_buffer )) Found(2) = .TRUE.
5799
END DO
5800
IF(ALL(Found)) EXIT
5801
END DO
5802
5803
! If crevasses does not intersect edgeline twice remove it
5804
IF(ANY(.NOT. Found)) THEN
5805
CALL WARN('RemoveInvalidCrevs', 'Crev does not intersect edge twice so removing')
5806
CurrentPath % Valid = .FALSE.
5807
END IF
5808
5809
ALLOCATE(DeleteElement(CurrentPath % NumberOfElements),&
5810
DeleteNode(CurrentPath % NumberOfNodes))
5811
DeleteElement = .FALSE.
5812
DeleteNode = .FALSE.
5813
5814
IF(startidx /= 1) THEN
5815
DeleteNode(1:startidx-1) = .TRUE.
5816
DeleteElement(1:startidx-1) = .TRUE.
5817
END IF
5818
IF(endidx /= CurrentPath % NumberOfNodes) THEN
5819
DeleteEndNodes = CurrentPath % NumberOfNodes - endidx
5820
DeleteNode(endidx+1:CurrentPath % NumberOfNodes) = .TRUE.
5821
DeleteElement(CurrentPath % NumberOfElements - DeleteEndNodes:CurrentPath % NumberOfElements) = .TRUE.
5822
END IF
5823
5824
!Delete them
5825
IF(COUNT(DeleteElement) > 0) THEN
5826
!elements
5827
ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))
5828
WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)
5829
5830
DEALLOCATE(CurrentPath % ElementNumbers)
5831
ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))
5832
5833
CurrentPath % ElementNumbers = WorkInt
5834
CurrentPath % NumberOfElements = SIZE(WorkInt)
5835
DEALLOCATE(WorkInt)
5836
5837
!nodes
5838
ALLOCATE(WorkInt(COUNT(.NOT. DeleteNode)))
5839
WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.DeleteNode)
5840
5841
DEALLOCATE(CurrentPath % NodeNumbers)
5842
ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))
5843
5844
CurrentPath % NodeNumbers = WorkInt
5845
CurrentPath % NumberOfNodes = SIZE(WorkInt)
5846
DEALLOCATE(WorkInt)
5847
END IF
5848
5849
DEALLOCATE(DeleteElement, DeleteNode)
5850
CurrentPath => CurrentPath % Next
5851
END DO
5852
5853
! actually remove path
5854
CurrentPath => CrevassePaths
5855
DO WHILE(ASSOCIATED(CurrentPath))
5856
WorkPath => CurrentPath % Next
5857
5858
IF(.NOT. CurrentPath % Valid) THEN
5859
IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath
5860
CALL RemoveCrevassePath(CurrentPath)
5861
IF(Debug) CALL Info("RemoveInvalidCrevs","Removing a crevasse path which doesn't end on the edge")
5862
END IF
5863
CurrentPath => WorkPath
5864
END DO
5865
5866
IF(RemoveInsideCrevs) THEN ! made optional as after validation the largest crev could already be removed etc...
5867
CALL GetCalvingPolygons(Mesh, CrevassePaths, EdgeX, EdgeY, Polygons, PolyStart, PolyEnd, GridSize)
5868
5869
! remove crevs found within other crevasses
5870
CurrentPath => CrevassePaths
5871
path=0
5872
DO WHILE(ASSOCIATED(CurrentPath))
5873
path=path+1
5874
inside = .FALSE.
5875
DO i=1, SIZE(PolyStart)
5876
IF(i==path) CYCLE
5877
ALLOCATE(PathPoly(2, PolyEnd(i)-PolyStart(i)+1))
5878
PathPoly = Polygons(:, PolyStart(i):PolyEnd(i))
5879
DO j=1, CurrentPath % NumberOfNodes
5880
xx = Mesh % Nodes % x(CurrentPath % NodeNumbers(j))
5881
yy = Mesh % Nodes % y(CurrentPath % NodeNumbers(j))
5882
DO k=1, SIZE(PathPoly(1,:))
5883
IF((xx+err_buffer >= PathPoly(1,k) .AND. xx-err_buffer <= PathPoly(1,k)) .AND. &
5884
(yy+err_buffer >= PathPoly(2,k) .AND. yy-err_buffer <= PathPoly(2,k))) THEN
5885
inside=.TRUE.
5886
EXIT
5887
END IF
5888
END DO
5889
END DO
5890
IF(inside) THEN
5891
! area 1 actual gives area*2
5892
area1 = 0.0_dp
5893
xx = Polygons(1,PolyStart(path))
5894
yy = Polygons(2,PolyStart(path))
5895
DO j=PolyStart(path)+1, PolyEnd(path)
5896
area1 = area1 + (Polygons(1,j) * yy - Polygons(2,j) * xx)
5897
xx = Polygons(1,j)
5898
yy = Polygons(2,j)
5899
END DO
5900
area2 = 0.0_dp
5901
xx = Polygons(1,PolyStart(i))
5902
yy = Polygons(2,PolyStart(i))
5903
DO j=PolyStart(i)+1, PolyEnd(i)
5904
area2 = area2 + (Polygons(1,j) * yy - Polygons(2,j) * xx)
5905
xx = Polygons(1,j)
5906
yy = Polygons(2,j)
5907
END DO
5908
IF(ABS(area1) <= ABS(area2)) THEN ! remove this path if smaller
5909
CurrentPath % Valid = .FALSE.
5910
ELSE !remove second path
5911
SecondPath => CrevassePaths
5912
spath=0
5913
DO WHILE(ASSOCIATED(SecondPath))
5914
spath=spath+1
5915
IF(spath==i) SecondPath % Valid = .FALSE.
5916
SecondPath => SecondPath % Next
5917
END DO
5918
END IF
5919
END IF
5920
DEALLOCATE(PathPoly)
5921
IF(inside) EXIT
5922
END DO
5923
5924
CurrentPath => CurrentPath % Next
5925
END DO
5926
5927
!Actually remove previous marked
5928
CurrentPath => CrevassePaths
5929
DO WHILE(ASSOCIATED(CurrentPath))
5930
WorkPath => CurrentPath % Next
5931
5932
IF(.NOT. CurrentPath % Valid) THEN
5933
IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath
5934
CALL RemoveCrevassePath(CurrentPath)
5935
IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path")
5936
END IF
5937
CurrentPath => WorkPath
5938
END DO
5939
5940
DEALLOCATE(Polygons)
5941
END IF
5942
5943
END SUBROUTINE RemoveInvalidCrevs
5944
5945
SUBROUTINE GetFrontCorners(Model, Solver, FrontLeft, FrontRight)
5946
5947
TYPE(Model_t) :: Model
5948
TYPE(Solver_t) :: Solver
5949
!--------------------------
5950
TYPE(Mesh_t), POINTER :: Mesh
5951
TYPE(Solver_t), POINTER :: NullSolver => NULL(), AdvSolver
5952
TYPE(Valuelist_t), POINTER :: SolverParams, AdvParams
5953
INTEGER :: i,j,k, dummyint, LeftRoot, RightRoot, ierr, NNodes,RCounter, LCounter,&
5954
Nl,Nr, Naux, ok, RightTotal, LeftTotal, Nrail, CornersTotal, Counter, side
5955
REAL(KIND=dp) :: FrontLeft(2), FrontRight(2), buffer, xx, yy, mindist, tempdist
5956
INTEGER, POINTER :: FrontPerm(:)=>NULL(), TopPerm(:)=>NULL(), &
5957
LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), SidePerm(:)
5958
LOGICAL :: FoundRight, FoundLeft, reducecorners(2), Found
5959
LOGICAL, ALLOCATABLE :: PFoundRight(:), PFoundLeft(:), InFront(:), Duplicate(:)
5960
INTEGER, ALLOCATABLE :: PRightCount(:), PLeftCount(:), disps(:),&
5961
PCount(:), jmin(:), Corner(:)
5962
REAL(kind=dp), ALLOCATABLE :: xL(:),yL(:),xR(:),yR(:), xRail(:), yRail(:),&
5963
AllCorners(:), PAllCorners(:), MinDists(:)
5964
CHARACTER(LEN=MAX_NAME_LEN) :: FrontMaskName, TopMaskName, &
5965
LeftMaskName, RightMaskName, SolverName = "GetFrontCorners",&
5966
RightRailFName, LeftRailFName, Adv_EqName
5967
INTEGER, PARAMETER :: io=20
5968
5969
NNodes = Model % Mesh % NumberOfNodes
5970
Mesh => Model % Mesh
5971
SolverParams => Solver % Values
5972
5973
ALLOCATE(FrontPerm(NNodes), TopPerm(NNodes), LeftPerm(NNodes),&
5974
RightPerm(NNodes))
5975
FrontMaskName = "Calving Front Mask"
5976
TopMaskName = "Top Surface Mask"
5977
CALL MakePermUsingMask( Model, Solver, Mesh, TopMaskName, &
5978
.FALSE., TopPerm, dummyint)
5979
CALL MakePermUsingMask( Model, Solver, Mesh, FrontMaskName, &
5980
.FALSE., FrontPerm, dummyint)
5981
LeftMaskName = "Left Sidewall Mask"
5982
RightMaskName = "Right Sidewall Mask"
5983
!Generate perms to quickly get nodes on each boundary
5984
CALL MakePermUsingMask( Model, Solver, Mesh, LeftMaskName, &
5985
.FALSE., LeftPerm, dummyint)
5986
CALL MakePermUsingMask( Model, Solver, Mesh, RightMaskName, &
5987
.FALSE., RightPerm, dummyint)
5988
5989
FoundLeft=.FALSE.
5990
FoundRight=.FALSE.
5991
RCounter= 0; LCounter=0
5992
DO i=1,NNodes
5993
IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN
5994
IF( LeftPerm(i) >0 ) THEN
5995
FrontLeft(1) = Mesh % Nodes % x(i)
5996
FrontLeft(2) = Mesh % Nodes % y(i)
5997
LCounter = LCounter + 1
5998
FoundLeft = .TRUE.
5999
ELSE IF ( RightPerm(i) >0 ) THEN
6000
FrontRight(1) = Mesh % Nodes % x(i)
6001
FrontRight(2) = Mesh % Nodes % y(i)
6002
RCounter = RCounter + 1
6003
FoundRight = .TRUE.
6004
END IF
6005
END IF
6006
END DO
6007
6008
ALLOCATE(PFoundRight(ParEnv % PEs), PFoundLeft(ParEnv % PEs))
6009
CALL MPI_ALLGATHER(FoundRight, 1, MPI_LOGICAL, PFoundRight, 1, &
6010
MPI_LOGICAL, ELMER_COMM_WORLD, ierr)
6011
CALL MPI_ALLGATHER(FoundLeft, 1, MPI_LOGICAL, PFoundLeft, 1, &
6012
MPI_LOGICAL, ELMER_COMM_WORLD, ierr)
6013
6014
DO i=1, ParEnv % PEs
6015
IF(.NOT. PFoundLeft(i) .AND. .NOT. PFoundRight(i)) CYCLE
6016
IF(PFoundLeft(i)) LeftRoot = i-1
6017
IF(PFoundRight(i)) RightRoot = i-1
6018
END DO
6019
6020
IF(ALL(.NOT. PFoundLeft)) CALL FATAL(SolverName, 'Unable to find left corner')
6021
IF(ALL(.NOT. PFoundRight)) CALL FATAL(SolverName, 'Unable to find right corner')
6022
6023
ALLOCATE(PRightCount(ParEnv % PEs), PLeftCount(ParEnv % PEs))
6024
CALL MPI_ALLGATHER(RCounter, 1, MPI_LOGICAL, PRightCount, 1, &
6025
MPI_LOGICAL, ELMER_COMM_WORLD, ierr)
6026
CALL MPI_ALLGATHER(LCounter, 1, MPI_LOGICAL, PLeftCount, 1, &
6027
MPI_LOGICAL, ELMER_COMM_WORLD, ierr)
6028
6029
RightTotal = SUM(PRightCount)
6030
LeftTotal = SUM(PLeftCount)
6031
6032
reducecorners=.TRUE.
6033
IF(LeftTotal == 1) THEN
6034
CALL MPI_BCAST(FrontLeft,2,MPI_DOUBLE_PRECISION, LeftRoot, ELMER_COMM_WORLD, ierr)
6035
reducecorners(1) = .FALSE.
6036
END IF
6037
6038
IF(RightTotal == 1) THEN
6039
CALL MPI_BCAST(FrontRight,2,MPI_DOUBLE_PRECISION, RightRoot, ELMER_COMM_WORLD, ierr)
6040
reducecorners(2) = .FALSE.
6041
END IF
6042
6043
IF(ANY(reducecorners)) THEN
6044
6045
Adv_EqName = ListGetString(SolverParams,"Front Advance Solver", DefValue="Front Advance")
6046
! Locate CalvingAdvance Solver
6047
Found = .FALSE.
6048
DO i=1,Model % NumberOfSolvers
6049
IF(GetString(Model % Solvers(i) % Values, 'Equation') == Adv_EqName) THEN
6050
AdvSolver => Model % Solvers(i)
6051
Found = .TRUE.
6052
EXIT
6053
END IF
6054
END DO
6055
IF(.NOT. Found) CALL FATAL(SolverName, "Advance Solver Equation not given")
6056
AdvParams => AdvSolver % Values
6057
6058
buffer = ListGetConstReal(AdvParams, "Rail Buffer", Found, DefValue=0.1_dp)
6059
IF(.NOT. Found) CALL Info(SolverName, "No Rail Buffer set using default 0.1")
6060
6061
LeftRailFName = ListGetString(AdvParams, "Left Rail File Name", Found)
6062
IF(.NOT. Found) THEN
6063
CALL Info(SolverName, "Left Rail File Name not found, assuming './LeftRail.xy'")
6064
LeftRailFName = "LeftRail.xy"
6065
END IF
6066
Nl = ListGetInteger(AdvParams, "Left Rail Number Nodes", Found)
6067
IF(.NOT.Found) THEN
6068
WRITE(Message,'(A,A)') 'Left Rail Number Nodes not found'
6069
CALL FATAL(SolverName, Message)
6070
END IF
6071
!TO DO only do these things if firsttime=true?
6072
OPEN(unit = io, file = TRIM(LeftRailFName), status = 'old',iostat = ok)
6073
IF (ok /= 0) THEN
6074
WRITE(message,'(A,A)') 'Unable to open file ',TRIM(LeftRailFName)
6075
CALL FATAL(Trim(SolverName),Trim(message))
6076
END IF
6077
ALLOCATE(xL(Nl), yL(Nl))
6078
6079
! read data
6080
DO i = 1, Nl
6081
READ(io,*,iostat = ok, end=200) xL(i), yL(i)
6082
END DO
6083
200 Naux = Nl - i
6084
IF (Naux > 0) THEN
6085
WRITE(Message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(LeftRailFName)
6086
CALL INFO(Trim(SolverName),Trim(message))
6087
END IF
6088
CLOSE(io)
6089
RightRailFName = ListGetString(AdvParams, "Right Rail File Name", Found)
6090
IF(.NOT. Found) THEN
6091
CALL Info(SolverName, "Right Rail File Name not found, assuming './RightRail.xy'")
6092
RightRailFName = "RightRail.xy"
6093
END IF
6094
6095
Nr = ListGetInteger(AdvParams, "Right Rail Number Nodes", Found)
6096
IF(.NOT.Found) THEN
6097
WRITE(Message,'(A,A)') 'Right Rail Number Nodes not found'
6098
CALL FATAL(SolverName, Message)
6099
END IF
6100
!TO DO only do these things if firsttime=true?
6101
OPEN(unit = io, file = TRIM(RightRailFName), status = 'old',iostat = ok)
6102
6103
IF (ok /= 0) THEN
6104
WRITE(Message,'(A,A)') 'Unable to open file ',TRIM(RightRailFName)
6105
CALL FATAL(Trim(SolverName),Trim(message))
6106
END IF
6107
ALLOCATE(xR(Nr), yR(Nr))
6108
6109
! read data
6110
DO i = 1, Nr
6111
READ(io,*,iostat = ok, end=100) xR(i), yR(i)
6112
END DO
6113
100 Naux = Nr - i
6114
IF (Naux > 0) THEN
6115
WRITE(message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(RightRailFName)
6116
CALL INFO(Trim(SolverName),Trim(message))
6117
END IF
6118
CLOSE(io)
6119
END IF
6120
6121
DO side=1,2 ! left 1, right 2
6122
6123
IF(.NOT. reducecorners(side)) CYCLE
6124
6125
IF (side==1) THEN
6126
Nrail= Nl
6127
ALLOCATE(xRail(Nrail), yRail(Nrail), PCount(ParEnv % PEs))
6128
xRail = xL
6129
yRail = yL
6130
SidePerm => LeftPerm
6131
Counter = LCounter
6132
CornersTotal = LeftTotal
6133
PCount = PLeftCount
6134
ELSE
6135
Nrail= Nr
6136
ALLOCATE(xRail(Nrail), yRail(Nrail), PCount(ParEnv % PEs))
6137
xRail = xR
6138
yRail = yR ! TO DO use pointers instead?
6139
SidePerm => RightPerm
6140
Counter = RCounter
6141
CornersTotal = RightTotal
6142
PCount = PRightCount
6143
END IF
6144
6145
ALLOCATE(AllCorners(Counter*2))
6146
Counter = 0
6147
DO i=1,NNodes
6148
IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN
6149
IF ( SidePerm(i) >0 ) THEN
6150
Counter = Counter + 1
6151
AllCorners(Counter*2-1) = Mesh % Nodes % x(i)
6152
AllCorners(Counter*2) = Mesh % Nodes % y(i)
6153
END IF
6154
END IF
6155
END DO
6156
6157
ALLOCATE(disps(ParEnv % PEs))
6158
disps(1) = 0
6159
DO i=2,ParEnv % PEs
6160
disps(i) = disps(i-1) + PCount(i-1)*2
6161
END DO
6162
6163
ALLOCATE(PAllCorners(CornersTotal*2))
6164
CALL MPI_ALLGATHERV(AllCorners, Counter*2, MPI_DOUBLE_PRECISION, &
6165
PAllCorners, PCount*2, disps, MPI_DOUBLE_PRECISION, ELMER_COMM_WORLD, ierr)
6166
IF(ierr /= MPI_SUCCESS) CALL Fatal(SolverName,"MPI Error!")
6167
6168
ALLOCATE(Duplicate(CornersTotal*2))
6169
Duplicate = .FALSE.
6170
DO i=1, CornersTotal
6171
IF(Duplicate(i*2)) CYCLE
6172
DO j=1, CornersTotal
6173
IF(i==j) CYCLE
6174
IF(PAllCorners(i*2-1) == PAllCorners(j*2-1) .AND. &
6175
PAllCorners(i*2) == PAllCorners(j*2)) Duplicate(j*2-1:j*2) = .TRUE.
6176
END DO
6177
END DO
6178
6179
DEALLOCATE(AllCorners)
6180
AllCorners = PACK(PAllCorners, .NOT. Duplicate)
6181
CornersTotal = INT(SIZE(AllCorners)/2)
6182
6183
ALLOCATE(jmin(CornersTotal),InFront(CornersTotal),MinDists(CornersTotal))
6184
DO i=1, CornersTotal
6185
6186
xx = AllCorners(i*2-1)
6187
yy = AllCorners(i*2)
6188
6189
MinDist=(xRail(1)-xRail(Nrail))**2.+(yRail(1)-yRail(Nrail))**2.
6190
! MinDist is actually maximum distance, needed for finding closest rail node
6191
DO j=1,Nrail ! Find closest point on rail
6192
TempDist=(xRail(j)-xx)**2.+(yRail(j)-yy)**2.
6193
IF(TempDist < MinDist) THEN
6194
MinDist=TempDist
6195
jmin(i)=j
6196
END IF
6197
END DO
6198
MinDists(i) = MinDist
6199
!check if in front or behind node
6200
InFront(i) = .TRUE.
6201
IF(jmin(i) == Nrail) InFront(i) = .FALSE.
6202
IF(jmin(i) > 1 .AND. jmin(i) /= Nrail) THEN
6203
MinDist = PointLineSegmDist2D((/xRail(jmin(i)),yRail(jmin(i))/), &
6204
(/xRail(jmin(i)+1),yRail(jmin(i)+1)/),(/xx,yy/))
6205
TempDist = PointLineSegmDist2D((/xRail(jmin(i)),yRail(jmin(i))/), &
6206
(/xRail(jmin(i)-1),yRail(jmin(i)-1)/),(/xx,yy/))
6207
IF(MinDist > TempDist) InFront(i) = .FALSE.
6208
END IF
6209
END DO
6210
6211
IF(COUNT(jmin == MAXVAL(jmin)) == 1) THEN
6212
Corner = MAXLOC(jmin)
6213
ELSE IF(COUNT(jmin == MAXVAL(jmin) .AND. InFront) == 1) THEN
6214
Corner = PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin) .AND. InFront)
6215
ELSE IF(ALL(InFront(PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin))))) THEN
6216
ALLOCATE(Corner(1))
6217
MinDist = HUGE(1.0_dp)
6218
DO i=1, CornersTotal
6219
IF(jmin(i) /= MAXVAL(jmin)) CYCLE
6220
IF(.NOT. InFront(i)) CYCLE
6221
IF(MinDists(i) < mindist) THEN
6222
mindist = MinDists(i)
6223
Corner(1) = i
6224
END IF
6225
END DO
6226
ELSE IF(ALL(.NOT. InFront(PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin))))) THEN
6227
ALLOCATE(Corner(1))
6228
MinDist = HUGE(1.0_dp)
6229
DO i=1, CornersTotal
6230
IF(jmin(i) /= MAXVAL(jmin)) CYCLE
6231
IF(MinDists(i) < mindist) THEN
6232
mindist = MinDists(i)
6233
Corner(1) = i
6234
END IF
6235
END DO
6236
ELSE
6237
CALL FATAL(SolverName, 'Problem reducing corners')
6238
END IF
6239
6240
IF(side == 1) THEN
6241
FrontLeft(1) = PAllCorners(Corner(1)*2-1)
6242
FrontLeft(2) = PAllCorners(Corner(1)*2)
6243
ELSE
6244
FrontRight(1) = PAllCorners(Corner(1)*2-1)
6245
FrontRight(2) = PAllCorners(Corner(1)*2)
6246
END IF
6247
6248
DEALLOCATE(xRail,yRail,AllCorners,disps,PAllCorners,jmin,InFront,Corner,MinDists,PCount,Duplicate)
6249
NULLIFY(SidePerm)
6250
END DO
6251
6252
DEALLOCATE(FrontPerm, TopPerm, LeftPerm, RightPerm)
6253
6254
END SUBROUTINE GetFrontCorners
6255
6256
SUBROUTINE ValidateNPCrevassePaths(Mesh, CrevassePaths, OnLeft, OnRight, FrontLeft, FrontRight, &
6257
EdgeX, EdgeY, LatCalvMargins, GridSize)
6258
IMPLICIT NONE
6259
TYPE(Mesh_t), POINTER :: Mesh
6260
TYPE(CrevassePath_t), POINTER :: CrevassePaths
6261
LOGICAL, ALLOCATABLE :: OnLeft(:),OnRight(:)
6262
LOGICAL :: LatCalvMargins
6263
REAL(KIND=dp) :: FrontRight(2), FrontLeft(2), EdgeX(:), EdgeY(:)
6264
REAL(KIND=dp), OPTIONAL :: GridSize
6265
INTEGER :: First, Last, LeftIdx, RightIdx
6266
!---------------------------------------------------
6267
REAL(KIND=dp) :: RotationMatrix(3,3), UnRotationMatrix(3,3), FrontDist, MaxDist, &
6268
ShiftTo, Dir1(2), Dir2(2), CCW_value,a1(2),a2(2),b1(2),b2(2),intersect(2), &
6269
StartX, StartY, EndX, EndY, Orientation(3), temp, NodeHolder(3), err_buffer,&
6270
yy, zz, gradient, c, intersect_z, SideCorner(3), MinDist, TempDist, IsBelowMean,&
6271
PolyMin, PolyMax
6272
REAL(KIND=dp), ALLOCATABLE :: ConstrictDirection(:,:), REdge(:,:), Polygons(:,:)
6273
REAL(KIND=dp), POINTER :: WorkReal(:)
6274
TYPE(CrevassePath_t), POINTER :: CurrentPath, OtherPath, WorkPath, LeftPath, RightPath
6275
TYPE(Element_t), POINTER :: WorkElements(:)
6276
TYPE(Nodes_t) :: WorkNodes
6277
INTEGER :: i,j,k,n,ElNo,ShiftToMe, NodeNums(2),A,B,FirstIndex, LastIndex,Start, path, &
6278
EdgeLength,crop(2),OnSide,SideCornerNum,addnodes,AddEdgeInt(2), CrevEndNode, Sideloops,&
6279
Counter, SideCornerOptions(4), LeftRight, ONNodes
6280
INTEGER, ALLOCATABLE :: WorkInt(:), IsBelow(:), PolyStart(:), PolyEnd(:)
6281
INTEGER, POINTER :: WorkPerm(:), NodeIndexes(:)
6282
LOGICAL :: Debug, Shifted, CCW, ToLeft, Snakey, OtherRight, ShiftRightPath, &
6283
DoProjectible, headland, CrevBelow, LeftToRight, AddLateralMargins, inside
6284
LOGICAL, ALLOCATABLE :: PathMoveNode(:), DeleteElement(:), BreakElement(:), &
6285
FarNode(:), DeleteNode(:), Constriction(:), InRange(:)
6286
CHARACTER(MAX_NAME_LEN) :: FuncName="ValidateNPCrevassePaths"
6287
REAL(kind=dp) :: rt0, rt
6288
6289
rt0 = RealTime()
6290
Debug = .FALSE.
6291
Snakey = .TRUE.
6292
6293
IF(PRESENT(GridSize)) THEN
6294
err_buffer = GridSize/1000.0
6295
ELSE
6296
err_buffer = AEPS
6297
END IF
6298
IF( err_buffer < AEPS) err_buffer = AEPS
6299
6300
! if on lateral margin need to make sure that glacier corner is within crev.
6301
! if it lies outside the crev then the crev isn't really on front but on the lateral corner
6302
! first and last both actually on same lateral margin
6303
CALL GetCalvingPolygons(Mesh, CrevassePaths, EdgeX, EdgeY, Polygons, PolyStart, PolyEnd, GridSize)
6304
CurrentPath => CrevassePaths
6305
path=0
6306
DO WHILE(ASSOCIATED(CurrentPath))
6307
path = path+1
6308
First = CurrentPath % NodeNumbers(1)
6309
Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
6310
IF(OnLeft(First) .OR. OnLeft(Last)) THEN
6311
inside = PointInPolygon2D(Polygons(:,PolyStart(path):PolyEnd(path)),FrontLeft)
6312
IF(.NOT. inside) THEN
6313
CurrentPath % Valid = .FALSE.
6314
CALL WARN(FuncName,'Left sidecorner not in crevasse so removing')
6315
END IF
6316
END IF
6317
IF(OnRight(First) .OR. OnRight(Last)) THEN
6318
inside = PointInPolygon2D(Polygons(:,PolyStart(path):PolyEnd(path)),FrontRight)
6319
IF(.NOT. inside) THEN
6320
CurrentPath % Valid = .FALSE.
6321
CALL WARN(FuncName,'Right sidecorner not in crevasse so removing')
6322
END IF
6323
END IF
6324
CurrentPath => CurrentPath % Next
6325
END DO
6326
6327
!Actually remove previous marked
6328
CurrentPath => CrevassePaths
6329
DO WHILE(ASSOCIATED(CurrentPath))
6330
WorkPath => CurrentPath % Next
6331
6332
IF(.NOT. CurrentPath % Valid) THEN
6333
IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath
6334
CALL RemoveCrevassePath(CurrentPath)
6335
IF(Debug) CALL Info("ValidateCrevassePaths","Removing a crevasse path")
6336
END IF
6337
CurrentPath => WorkPath
6338
END DO
6339
6340
DEALLOCATE(Polygons, PolyStart, PolyEnd)
6341
CALL GetCalvingPolygons(Mesh, CrevassePaths, EdgeX, EdgeY, Polygons, PolyStart, PolyEnd, GridSize)
6342
! invalid lateral crevs must first be removed before this subroutine
6343
CurrentPath => CrevassePaths
6344
path=0
6345
DO WHILE(ASSOCIATED(CurrentPath))
6346
path=path+1
6347
First = CurrentPath % NodeNumbers(1)
6348
Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
6349
StartX = Mesh % Nodes % x(First)
6350
StartY = Mesh % Nodes % y(First)
6351
EndX = Mesh % Nodes % x(Last)
6352
EndY = Mesh % Nodes % y(Last)
6353
! onside = 0, crev not on side
6354
! onside =1, first node on side leftright=1, on left
6355
! onside =2, second node on side leftright=2, on right
6356
! if on both sides corrected in loop
6357
Sideloops = 0; Onside = 0; LeftRight = 0
6358
IF(OnLeft(First)) THEN
6359
StartX = FrontLeft(1)
6360
StartY = FrontLeft(2)
6361
Onside = 1; LeftRight = 1
6362
Sideloops = Sideloops + 1
6363
ELSE IF(OnRight(First)) THEN
6364
StartX = FrontRight(1)
6365
StartY = FrontRight(2)
6366
Onside = 1; LeftRight = 2
6367
Sideloops = Sideloops + 1
6368
END IF
6369
IF(OnLeft(Last)) THEN
6370
EndX = FrontLeft(1)
6371
EndY = FrontLeft(2)
6372
OnSide = 2; LeftRight = 1
6373
Sideloops = Sideloops + 1
6374
ELSE IF(OnRight(Last)) THEN
6375
EndX = FrontRight(1)
6376
EndY = FrontRight(2)
6377
Onside = 2; LeftRight = 2
6378
Sideloops = Sideloops + 1
6379
END IF
6380
6381
AddLateralMargins = .FALSE.
6382
IF(Onside /= 0 .AND. LatCalvMargins) AddLateralMargins = .TRUE.
6383
6384
orientation(3) = 0.0_dp
6385
IF( ABS(StartX-EndX) < err_buffer) THEN
6386
! front orientation is aligned with y-axis
6387
Orientation(2) = 0.0_dp
6388
IF(EndY > StartY) THEN
6389
Orientation(1)=1.0_dp
6390
ELSE
6391
Orientation(1)=-1.0_dp
6392
END IF
6393
ELSE IF (ABS(StartY-EndY)< err_buffer) THEN
6394
! front orientation is aligned with x-axis
6395
Orientation(1) = 0.0_dp
6396
IF(EndX > StartX) THEN
6397
Orientation(2)=1.0_dp
6398
ELSE
6399
Orientation(2)=-1.0_dp
6400
END IF
6401
ELSE
6402
CALL ComputePathExtent(CrevassePaths, Mesh % Nodes, .TRUE.)
6403
! endx always greater than startx
6404
! check if yextent min smaller than starty
6405
6406
PolyMin = MINVAL(Polygons(2,PolyStart(path):PolyEnd(path)))
6407
PolyMax = MAXVAL(Polygons(2,PolyStart(path):PolyEnd(path)))
6408
6409
IF(ABS(CurrentPath % Right - PolyMax) > &
6410
CurrentPath % Left - PolyMin) THEN
6411
Orientation(2)=-1.0_dp
6412
ELSE
6413
Orientation(2)=1.0_dp
6414
END IF
6415
Orientation(1)=Orientation(2)*(EndY-StartY)/(StartX-EndX)
6416
END IF
6417
Temp=(Orientation(1)**2+Orientation(2)**2+Orientation(3)**2)**0.5
6418
Orientation=Orientation/Temp ! normalized the orientation
6419
6420
RotationMatrix = ComputeRotationMatrix(Orientation)
6421
UnRotationMatrix = TRANSPOSE(RotationMatrix)
6422
6423
!save crevasse orientation
6424
CurrentPath % Orientation = Orientation(1:2)
6425
6426
! Temporarily rotate the mesh
6427
CALL RotateMesh(Mesh, RotationMatrix)
6428
6429
! Find path %left, %right, %extent (width)
6430
CALL ComputePathExtent(CurrentPath, Mesh % Nodes, .TRUE.)
6431
6432
! rotate edgex and edgey
6433
EdgeLength = SIZE(EdgeX)
6434
ALLOCATE(REdge(3, EdgeLength))
6435
DO i=1,EdgeLength
6436
NodeHolder(1) = EdgeX(i)
6437
NodeHolder(2) = EdgeY(i)
6438
NodeHolder(3) = 0.0_dp
6439
6440
NodeHolder = MATMUL(RotationMatrix,NodeHolder)
6441
6442
REdge(1,i) = NodeHolder(1)
6443
REdge(2,i) = NodeHolder(2)
6444
REdge(3,i) = NodeHolder(3)
6445
END DO
6446
6447
! crop edge around crev ends
6448
crop=0
6449
DO i=1, EdgeLength
6450
IF((REdge(2,i) <= Mesh % Nodes % y(First) + err_buffer .AND. &
6451
REdge(2,i) >= Mesh % Nodes % y(First) - err_buffer) .AND. &
6452
(REdge(3,i) <= Mesh % Nodes % z(First) + err_buffer .AND. &
6453
REdge(3,i) >= Mesh % Nodes % z(First) - err_buffer)) crop(1) = i
6454
IF((REdge(2,i) <= Mesh % Nodes % y(Last) + err_buffer .AND. &
6455
REdge(2,i) >= Mesh % Nodes % y(Last) - err_buffer) .AND. &
6456
(REdge(3,i) <= Mesh % Nodes % z(Last) + err_buffer .AND. &
6457
REdge(3,i) >= Mesh % Nodes % z(Last) - err_buffer )) crop(2) = i
6458
END DO
6459
IF(ANY(crop == 0)) CALL FATAL(FuncName, 'Edge not found')
6460
6461
! if onside we need to consider that constriction may occur as result of
6462
! narrowing on the fjord walls. Easiest way to do this is add the lateral edge nodes
6463
! to the crevasse permanently
6464
! GetFrontCorners only provides surface edges - is this a problem on a nonvertical front?
6465
! loop as crev may be on both lateral margins
6466
IF(AddLateralMargins) THEN
6467
ONNodes = Mesh % NumberOfNodes
6468
DO j=1,Sideloops
6469
!adjust onside and leftright
6470
!if on both side must be left(first) then right(last)
6471
IF(j==1 .AND. Sideloops==2) THEN
6472
LeftRight = 1; OnSide = 1
6473
ELSE IF(j==2) THEN
6474
LeftRight = 2; OnSide = 2
6475
END IF
6476
6477
! rotate side corner if it exists
6478
IF(LeftRight == 1) THEN
6479
NodeHolder(1) = FrontLeft(1)
6480
NodeHolder(2) = FrontLeft(2)
6481
NodeHolder(3) = 0.0_dp
6482
ELSE IF(LeftRight == 2) THEN
6483
NodeHolder(1) = FrontRight(1)
6484
NodeHolder(2) = FrontRight(2)
6485
NodeHolder(3) = 0.0_dp
6486
END IF
6487
6488
NodeHolder = MATMUL(RotationMatrix,NodeHolder)
6489
6490
SideCorner = NodeHolder
6491
6492
SideCornerNum = 0
6493
SideCornerOptions = 0
6494
MinDist = HUGE(1.0_dp)
6495
Counter = 0
6496
DO i=1, EdgeLength
6497
TempDist = PointDist3D(REdge(:,i), SideCorner)
6498
IF(TempDist < MinDist) THEN
6499
MinDist = TempDist
6500
SideCornerNum = i
6501
END IF
6502
IF(TempDist < GridSize) THEN
6503
counter = counter + 1
6504
IF(counter > 4) CALL FATAL(FuncName, 'More than 4 nodes closer than gridsize to side corner')
6505
SideCornerOptions(counter) = i
6506
END IF
6507
END DO
6508
6509
! this is for when the closest edgenode to the SideCorner is actually on
6510
! the front causing a constriction in crevasse. This moves it back onto the
6511
! lateral margin
6512
IF(counter == 2) THEN
6513
IF(LeftRight == 1) SideCornerNum = MINVAL(SideCornerOptions(1:2))
6514
IF(LeftRight == 2) SideCornerNum = MAXVAL(SideCornerOptions(1:2))
6515
END IF
6516
IF(counter == 3) SideCornerNum = SideCornerOptions(2)
6517
IF(counter == 4) THEN
6518
IF(LeftRight == 1) SideCornerNum = MINVAL(SideCornerOptions)
6519
IF(LeftRight == 2) SideCornerNum = MAXVAL(SideCornerOptions)
6520
END IF
6521
6522
IF(SideCornerNum==0) CALL FATAL(FuncName, 'Side Corner not found')
6523
IF(SideCornerNum > MAXVAL(crop) .OR. SideCornerNum < MINVAL(crop)) THEN
6524
CALL WARN(FuncName, 'Side Corner not in cropped edge range')
6525
! node must be in front of sidecorner which is only based off surface nodes
6526
EXIT
6527
END IF
6528
6529
! see which nodes we want to add
6530
IF(LeftRight == 1) THEN
6531
AddEdgeInt(1) = crop(OnSide) + 1
6532
AddEdgeInt(2) = SideCornerNum
6533
ELSE IF(LeftRight == 2) THEN
6534
AddEdgeInt(1) = SideCornerNum
6535
AddEdgeInt(2) = crop(OnSide) - 1
6536
END IF
6537
crop(Onside) = SideCornerNum
6538
addnodes = AddEdgeInt(2) - AddEdgeInt(1) + 1
6539
6540
IF(addnodes < 0) CALL FATAL(FuncName, 'Problem adding lateral margins - addition is negative')
6541
6542
IF(Onside == 1) CrevEndNode=First
6543
IF(OnSide == 2) CrevEndNode=Last
6544
6545
! add elements to the mesh
6546
ALLOCATE(WorkElements(Mesh % NumberOfBulkElements + addnodes))
6547
WorkElements(1:Mesh % NumberOfBulkElements) = Mesh % Elements
6548
IF(Onside == 1) THEN
6549
DO i=1, addnodes
6550
WorkElements(Mesh % NumberOfBulkElements + i) % ElementIndex = Mesh % NumberOfBulkElements + i
6551
WorkElements(Mesh % NumberOfBulkElements + i) % TYPE => GetElementType(202)
6552
WorkElements(Mesh % NumberOfBulkElements + i) % BodyID = 1
6553
CALL AllocateVector(WorkElements(Mesh % NumberOfBulkElements + i) % NodeIndexes, 2)
6554
NodeIndexes => WorkElements(Mesh % NumberOfBulkElements + i) % NodeIndexes
6555
IF(LeftRight == 1) THEN
6556
IF(i==1) THEN
6557
NodeIndexes(2) = CrevEndNode
6558
ELSE
6559
NodeIndexes(2) = Mesh % NumberOfNodes + i - 1
6560
END IF
6561
NodeIndexes(1) = Mesh % NumberOfNodes + i
6562
ELSE IF(LeftRight == 2) THEN
6563
IF(i==1) THEN
6564
NodeIndexes(2) = CrevEndNode
6565
ELSE
6566
NodeIndexes(2) = Mesh % NumberOfNodes + addnodes - i + 2
6567
END IF
6568
NodeIndexes(1) = Mesh % NumberOfNodes + addnodes - i + 1
6569
END IF
6570
END DO
6571
ELSE IF(OnSide == 2) THEN
6572
DO i=1, addnodes
6573
WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % ElementIndex = &
6574
Mesh % NumberOfBulkElements - i + addnodes + 1
6575
WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % TYPE => GetElementType(202)
6576
WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % BodyID = 1
6577
CALL AllocateVector(WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % NodeIndexes, 2)
6578
NodeIndexes => WorkElements(Mesh % NumberOfBulkElements - i + addnodes + 1) % NodeIndexes
6579
IF(LeftRight == 1) THEN
6580
IF(i==1) THEN
6581
NodeIndexes(1) = CrevEndNode
6582
ELSE
6583
NodeIndexes(1) = Mesh % NumberOfNodes + i - 1
6584
END IF
6585
NodeIndexes(2) = Mesh % NumberOfNodes + i
6586
ELSE IF(LeftRight == 2) THEN
6587
IF(i==1) THEN
6588
NodeIndexes(1) = CrevEndNode
6589
ELSE
6590
NodeIndexes(1) = Mesh % NumberOfNodes - i + addnodes + 2
6591
END IF
6592
NodeIndexes(2) = Mesh % NumberOfNodes - i + addnodes + 1
6593
END IF
6594
END DO
6595
END IF
6596
6597
! add nodes to mesh
6598
WorkNodes % NumberOfNodes = Mesh % NumberOfNodes + addnodes
6599
6600
ALLOCATE(WorkNodes % x(WorkNodes % NumberOfNodes),&
6601
WorkNodes % y(WorkNodes % NumberOfNodes),&
6602
WorkNodes % z(WorkNodes % NumberOfNodes))
6603
WorkNodes % x(1:Mesh % NumberOfNodes) = Mesh % Nodes % x
6604
WorkNodes % y(1:Mesh % NumberOfNodes) = Mesh % Nodes % y
6605
WorkNodes % z(1:Mesh % NumberOfNodes) = Mesh % Nodes % z
6606
DO i=1, addnodes
6607
WorkNodes % x(Mesh % NumberOfNodes + i) = REdge(1,AddEdgeInt(1)+i-1)
6608
WorkNodes % y(Mesh % NumberOfNodes + i) = REdge(2,AddEdgeInt(1)+i-1)
6609
WorkNodes % z(Mesh % NumberOfNodes + i) = REdge(3,AddEdgeInt(1)+i-1)
6610
END DO
6611
6612
IF(ASSOCIATED(Mesh % Elements)) DEALLOCATE(Mesh % Elements)
6613
Mesh % Elements => WorkElements
6614
DEALLOCATE(Mesh % Nodes % x, Mesh % Nodes % y, Mesh % Nodes % z)
6615
ALLOCATE(Mesh % Nodes % x(WorkNodes % NumberOfNodes), &
6616
Mesh % Nodes % y(WorkNodes % NumberOfNodes), &
6617
Mesh % Nodes % z(WorkNodes % NumberOfNodes))
6618
Mesh % NumberOfNodes = WorkNodes % NumberOfNodes
6619
Mesh % Nodes % NumberOfNodes = WorkNodes % NumberOfNodes
6620
Mesh % Nodes % x = WorkNodes % x
6621
Mesh % Nodes % y = WorkNodes % y
6622
Mesh % Nodes % z = WorkNodes % z
6623
Mesh % NumberOfBulkElements = SIZE(WorkElements)
6624
6625
NULLIFY(WorkElements) !nulify as mesh % elements points to this allocation
6626
DEALLOCATE(WorkNodes % x, WorkNodes % y, WorkNodes % z)
6627
6628
!modify crevasse
6629
ALLOCATE(WorkInt(CurrentPath % NumberOfNodes + addnodes))
6630
IF(OnSide == 1 .AND. LeftRight == 1) THEN ! add at start
6631
WorkInt(addnodes+1:CurrentPath % NumberOfNodes+addnodes) = CurrentPath % NodeNumbers
6632
DO i=1,addnodes
6633
WorkInt(i) = Mesh % NumberOfNodes - i + 1 !edge nodes added backwards
6634
END DO
6635
ELSE IF(OnSide == 1 .AND. LeftRight == 2) THEN ! add at start
6636
WorkInt(addnodes+1:CurrentPath % NumberOfNodes+addnodes) = CurrentPath % NodeNumbers
6637
DO i=1,addnodes
6638
WorkInt(i) = Mesh % NumberOfNodes - addnodes + i !edge nodes added forwards
6639
END DO
6640
ELSE IF(OnSide == 2 .AND. LeftRight == 1) THEN
6641
WorkInt(1:CurrentPath % NumberOfNodes) = CurrentPath % NodeNumbers
6642
DO i=1,addnodes
6643
WorkInt(CurrentPath % NumberOfNodes+ i) = Mesh % NumberOfNodes - addnodes + i !edge nodes added forwards
6644
END DO
6645
ELSE IF(OnSide == 2 .AND. LeftRight == 2) THEN
6646
WorkInt(1:CurrentPath % NumberOfNodes) = CurrentPath % NodeNumbers
6647
DO i=1,addnodes
6648
WorkInt(CurrentPath % NumberOfNodes+ i) = Mesh % NumberOfNodes-i+1 !edge nodes added backwards
6649
END DO
6650
END IF
6651
CurrentPath % NumberOfNodes = SIZE(WorkInt)
6652
DEALLOCATE(CurrentPath % NodeNumbers)
6653
ALLOCATE(Currentpath % NodeNumbers(CurrentPath % NumberOfNodes))
6654
CurrentPath % NodeNumbers = WorkInt
6655
DEALLOCATE(WorkInt)
6656
6657
! elements
6658
ALLOCATE(WorkInt(CurrentPath % NumberOfElements + addnodes))
6659
IF(OnSide == 1) THEN
6660
WorkInt(addnodes+1:CurrentPath % NumberOfElements+addnodes) = CurrentPath % ElementNumbers
6661
DO i=1,addnodes
6662
WorkInt(i) = Mesh % NumberOfBulkElements - i + 1 !new nodes always on end
6663
END DO
6664
ELSE IF(OnSide == 2) THEN
6665
WorkInt(1:CurrentPath % NumberOfElements) = CurrentPath % ElementNumbers
6666
DO i=1,addnodes
6667
WorkInt(CurrentPath % NumberOfElements+ i) = Mesh % NumberOfBulkElements - i + 1 !new nodes always on end
6668
END DO
6669
END IF
6670
CurrentPath % NumberOfElements = SIZE(WorkInt)
6671
DEALLOCATE(CurrentPath % ElementNumbers)
6672
ALLOCATE(Currentpath % ElementNumbers(CurrentPath % NumberOfElements))
6673
CurrentPath % ElementNumbers = WorkInt
6674
DEALLOCATE(WorkInt)
6675
END DO
6676
6677
! update first and last
6678
First = CurrentPath % NodeNumbers(1)
6679
Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
6680
6681
! adjust mesh perm
6682
n = Mesh % NumberOfNodes
6683
ALLOCATE(WorkPerm(n), WorkReal(n))
6684
WorkReal = 0.0_dp
6685
WorkPerm = [(i,i=1,n)]
6686
CALL VariableRemove(Mesh % Variables, "isoline id", .TRUE.)
6687
CALL VariableAdd(Mesh % Variables, Mesh, NULL(), "isoline id", 1, WorkReal, WorkPerm)
6688
NULLIFY(WorkPerm, WorkReal) ! new variables points to these allocations
6689
END IF ! end onside
6690
6691
!-----------------------------------------------------
6692
! Paths should not 'snake' inwards in a narrow slit...
6693
!-----------------------------------------------------
6694
6695
!it's insufficient to require that no nodes be
6696
!further away than the two edge nodes.
6697
!Instead, must ensure that no nodes are further away than any
6698
!surrounding nodes.
6699
6700
!First need to determine path orientation
6701
!with respect to front....
6702
6703
!if ToLeft, the crevasse path goes from right to left, from the
6704
!perspective of someone sitting in the fjord, looking at the front
6705
ToLeft = Mesh % Nodes % y(Last) > Mesh % Nodes % y(First)
6706
6707
! since front no longer projectible we must now see if the crev is below or
6708
! above the front (edge of glacier)
6709
6710
! see if crev is above or below glacier edge
6711
ALLOCATE(IsBelow(CurrentPath % NumberOfNodes-2),&
6712
InRange(CurrentPath % NumberOfNodes-2))
6713
IsBelow = 0
6714
InRange = .FALSE.
6715
DO i=2, CurrentPath % NumberOfNodes-1
6716
yy = Mesh % Nodes % y(CurrentPath % NodeNumbers(i))
6717
zz = Mesh % Nodes % z(CurrentPath % NodeNumbers(i))
6718
DO j=MINVAL(crop), MAXVAL(crop)-1
6719
IF((yy >= REdge(2,j) - err_buffer .AND. yy <= REdge(2,j+1) + err_buffer) .OR. &
6720
(yy <= REdge(2,j) + err_buffer .AND. yy >= REdge(2,j+1) - err_buffer)) THEN
6721
IF(REdge(2,j) - err_buffer <= REdge(2,j+1) .AND. &
6722
REdge(2,j) + err_buffer >= REdge(2,j+1)) CYCLE ! vertical
6723
IF(REdge(3,j) - err_buffer <= REdge(3,j+1) .AND. &
6724
REdge(3,j) + err_buffer >= REdge(3,j+1)) THEN ! horizontal
6725
intersect_z = REdge(3,j)
6726
ELSE
6727
gradient = (REdge(3,j)-REdge(3,j+1)) / (REdge(2,j)-REdge(2,j+1))
6728
c = REdge(3,j) - (gradient*REdge(2,j))
6729
intersect_z = gradient * yy + c
6730
END IF
6731
InRange(i-1) = .TRUE. ! found
6732
IF(zz - err_buffer <= intersect_z) THEN
6733
IF(zz + err_buffer >= intersect_z) THEN
6734
IsBelow(i-1) = 1 !in same position as edge
6735
ELSE
6736
IsBelow(i-1) = 2 ! below edge
6737
END IF
6738
EXIT
6739
END IF
6740
END IF
6741
END DO
6742
END DO
6743
6744
! if out of edge range remove
6745
IsBelow = PACK(IsBelow, InRange)
6746
6747
IF(SIZE(IsBelow) == 0) THEN
6748
! occurs when crev is on lateral margin and only had one node on front
6749
CurrentPath % Valid = .FALSE.
6750
CALL WARN(FuncName, 'No crev nodes in range of edge segment')
6751
END IF
6752
6753
IF(.NOT. CurrentPath % Valid) GOTO 10 ! skip constriction
6754
6755
IsBelowMean = SUM(IsBelow)/SIZE(IsBelow)
6756
IF(IsBelowMean >= 1) THEN
6757
CrevBelow = .TRUE.
6758
ELSE IF(IsBelowMean <= 1) THEN
6759
CrevBelow = .FALSE.
6760
ELSE
6761
CALL FATAL(FuncName, 'Some of the crevasse is below and some is above the glacier edge')
6762
END IF
6763
6764
! see if crev runs from its left to right
6765
IF(CrevBelow .AND. ToLeft) THEN
6766
LeftToRight = .TRUE.
6767
ELSE IF(.NOT. CrevBelow .AND. .NOT. ToLeft) THEN
6768
LeftToRight = .TRUE.
6769
ELSE
6770
LeftToRight = .FALSE.
6771
END IF
6772
6773
IF(Debug) PRINT*, 'LeftToRight: ', LeftToRight, CrevBelow, ToLeft
6774
6775
CurrentPath % LeftToRight = LeftToRight
6776
6777
IF(Debug) THEN
6778
FrontDist = NodeDist3D(Mesh % Nodes,First, Last)
6779
PRINT *,'PATH: ', CurrentPath % ID, ' FrontDist: ',FrontDist
6780
PRINT *,'PATH: ', CurrentPath % ID, &
6781
' nonodes: ',CurrentPath % NumberOfNodes,&
6782
' noelems: ',CurrentPath % NumberOfElements
6783
END IF
6784
6785
!Cycle path nodes, finding those which are too far away
6786
ALLOCATE(FarNode(CurrentPath % NumberOfNodes), &
6787
Constriction(CurrentPath % NumberOfNodes),&
6788
ConstrictDirection(CurrentPath % NumberOfNodes,2))
6789
FarNode = .FALSE.
6790
Constriction = .FALSE.
6791
ConstrictDirection = 0.0_dp
6792
6793
!Determine which nodes have the potential to be constriction (based on angle)
6794
!and compute constriction direction (i.e. which way the 'pointy bit' points...')
6795
DO i=2,CurrentPath % NumberOfNodes-1
6796
First = CurrentPath % NodeNumbers(i-1)
6797
Last = CurrentPath % NodeNumbers(i+1)
6798
n = CurrentPath % NodeNumbers(i)
6799
6800
CCW_value = ((Mesh % Nodes % y(n) - Mesh % Nodes % y(First)) * &
6801
(Mesh % Nodes % z(Last) - Mesh % Nodes % z(First))) - &
6802
((Mesh % Nodes % z(n) - Mesh % Nodes % z(First)) * &
6803
(Mesh % Nodes % y(Last) - Mesh % Nodes % y(First)))
6804
6805
CCW = CCW_value > 0.0_dp
6806
6807
IF((CCW .NEQV. LeftToRight) .AND. (ABS(CCW_value) > 10*AEPS)) THEN
6808
Constriction(i) = .TRUE.
6809
!Calculate constriction direction
6810
6811
Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(First)
6812
Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(First)
6813
Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)
6814
6815
Dir2(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)
6816
Dir2(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)
6817
Dir2 = Dir2 / ((Dir2(1)**2.0 + Dir2(2)**2.0) ** 0.5)
6818
6819
ConstrictDirection(i,1) = Dir1(1) + Dir2(1)
6820
ConstrictDirection(i,2) = Dir1(2) + Dir2(2)
6821
! no point normalising just gives floating point errors?
6822
!ConstrictDirection(i,:) = ConstrictDirection(i,:) / &
6823
! ((ConstrictDirection(i,1)**2.0 + ConstrictDirection(i,2)**2.0) ** 0.5)
6824
6825
IF(Debug) PRINT *, 'Debug, node ',i,' dir1,2: ',Dir1, Dir2
6826
IF(Debug) PRINT *, 'Debug, node ',i,' constriction direction: ',ConstrictDirection(i,:)
6827
IF(Debug) PRINT *, 'Debug, node ',i,' xyz: ',Mesh % Nodes % x(n),Mesh % Nodes % y(n),Mesh % Nodes % z(n)
6828
END IF
6829
END DO
6830
6831
!First and last can always be constriction
6832
Constriction(1) = .TRUE.
6833
Constriction(SIZE(Constriction)) = .TRUE.
6834
6835
!Compute constriction direction for first and last
6836
!We don't have info about the third node, so take orthogonal to 2
6837
Last = CurrentPath % NodeNumbers(2)
6838
n = CurrentPath % NodeNumbers(1)
6839
Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)
6840
Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)
6841
Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)
6842
6843
!Depending on which end of the chain we are,
6844
!we take either the right or left orthogonal vector
6845
IF(LeftToRight) THEN
6846
ConstrictDirection(1,1) = Dir1(2)
6847
ConstrictDirection(1,2) = -1.0 * Dir1(1)
6848
ELSE
6849
ConstrictDirection(1,1) = -1.0 * Dir1(2)
6850
ConstrictDirection(1,2) = Dir1(1)
6851
END IF
6852
IF(Debug) PRINT *, 'Debug, node 1 constriction direction: ',ConstrictDirection(1,:)
6853
6854
Last = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes - 1)
6855
n = CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes)
6856
6857
Dir1(1) = Mesh % Nodes % y(n) - Mesh % Nodes % y(Last)
6858
Dir1(2) = Mesh % Nodes % z(n) - Mesh % Nodes % z(Last)
6859
Dir1 = Dir1 / ((Dir1(1)**2.0 + Dir1(2)**2.0) ** 0.5)
6860
6861
IF(.NOT. LeftToRight) THEN
6862
ConstrictDirection(CurrentPath % NumberOfNodes,1) = Dir1(2)
6863
ConstrictDirection(CurrentPath % NumberOfNodes,2) = -1.0 * Dir1(1)
6864
ELSE
6865
ConstrictDirection(CurrentPath % NumberOfNodes,1) = -1.0 * Dir1(2)
6866
ConstrictDirection(CurrentPath % NumberOfNodes,2) = Dir1(1)
6867
END IF
6868
IF(Debug) PRINT *, 'Debug, node last constriction direction: ',&
6869
ConstrictDirection(CurrentPath % NumberOfNodes,:)
6870
6871
!---------------------------------------
6872
! Now that we have constrictions marked and directions computed, cycle nodes
6873
6874
DO i=1,CurrentPath % NumberOfNodes
6875
IF(.NOT. Constriction(i)) CYCLE
6876
6877
DO j=CurrentPath % NumberOfNodes,i+1,-1
6878
IF(.NOT. Constriction(j)) CYCLE
6879
6880
6881
First = CurrentPath % NodeNumbers(i)
6882
Last = CurrentPath % NodeNumbers(j)
6883
6884
!Check that these constrictions 'face' each other via dot product
6885
Dir1(1) = Mesh % Nodes % y(Last) - Mesh % Nodes % y(First)
6886
Dir1(2) = Mesh % Nodes % z(Last) - Mesh % Nodes % z(First)
6887
Dir2(1) = -Dir1(1)
6888
Dir2(2) = -Dir1(2)
6889
6890
!If the two constrictions aren't roughly facing each other:
6891
! < > rather than > <
6892
! then skip this combo
6893
IF(SUM(ConstrictDirection(i,:)*Dir1) < 0.0000001_dp) THEN
6894
IF(Debug) PRINT *,'Constrictions ',i,j,' do not face each other 1: ',&
6895
SUM(ConstrictDirection(i,:)*Dir1)
6896
CYCLE
6897
END IF
6898
6899
IF(SUM(ConstrictDirection(j,:)*Dir2) < 0.0000001_dp) THEN
6900
IF(Debug) PRINT *,'Constrictions ',j,i,' do not face each other 2: ',&
6901
SUM(ConstrictDirection(j,:)*Dir2)
6902
CYCLE
6903
END IF
6904
6905
IF(Debug) PRINT *,'Constrictions ',i,j,' do face each other: ',&
6906
SUM(ConstrictDirection(i,:)*Dir1), SUM(ConstrictDirection(j,:)*Dir2)
6907
6908
!test that the line drawn between the constriction doesn't intersect
6909
!any intermediate elements as this indicates
6910
!crossing a headland (difficult to draw - but it's bad news)
6911
!
6912
! - --- ---- -
6913
! \/ \ / \/
6914
! ----
6915
!
6916
6917
a1(1) = Mesh % Nodes % y(First)
6918
a1(2) = Mesh % Nodes % z(First)
6919
a2(1) = Mesh % Nodes % y(Last)
6920
a2(2) = Mesh % Nodes % z(Last)
6921
headland = .FALSE.
6922
DO k=i+1,j-2
6923
b1(1) = Mesh % Nodes % y(CurrentPath % NodeNumbers(k))
6924
b1(2) = Mesh % Nodes % z(CurrentPath % NodeNumbers(k))
6925
b2(1) = Mesh % Nodes % y(CurrentPath % NodeNumbers(k+1))
6926
b2(2) = Mesh % Nodes % z(CurrentPath % NodeNumbers(k+1))
6927
6928
CALL LineSegmentsIntersect(a1,a2,b1,b2,intersect,headland)
6929
IF(headland .AND. Debug) PRINT*, 'Headland intersect: ', 'a1', a1, &
6930
'a2', a2, 'b1', b1, 'b2', b2
6931
IF(headland) EXIT
6932
END DO
6933
IF(headland) CYCLE
6934
6935
MaxDist = NodeDist3D(Mesh % Nodes,First, Last)
6936
6937
DO k=i+1,j-1
6938
IF(FarNode(k)) CYCLE
6939
6940
n = CurrentPath % NodeNumbers(k)
6941
6942
IF((NodeDist3D(Mesh % Nodes, First, n) <= MaxDist) .AND. &
6943
(NodeDist3D(Mesh % Nodes, Last, n) <= MaxDist)) CYCLE !within range
6944
6945
FarNode(k) = .TRUE.
6946
IF(Debug) PRINT *,'Far node: ',k,' xyz: ',Mesh % Nodes % x(n),&
6947
Mesh % Nodes % y(n),Mesh % Nodes % z(n)
6948
6949
END DO
6950
END DO
6951
END DO
6952
6953
!Cycle elements, marking those which need to be adjusted
6954
ALLOCATE(BreakElement(CurrentPath % NumberOfElements),&
6955
DeleteElement(CurrentPath % NumberOfElements))
6956
BreakElement = .FALSE.
6957
DeleteElement = .FALSE.
6958
6959
DO i=1,CurrentPath % NumberOfElements
6960
IF(ANY(FarNode(i:i+1))) THEN
6961
IF(ALL(FarNode(i:i+1))) THEN
6962
DeleteElement(i) = .TRUE.
6963
IF(Debug) PRINT *,'PATH: ', CurrentPath % ID, ' element: ',i,' is deleted.'
6964
ELSE
6965
BreakElement(i) = .TRUE.
6966
IF(Debug) PRINT *,'PATH: ', CurrentPath % ID, ' element: ',i,' is broken.'
6967
END IF
6968
END IF
6969
END DO
6970
6971
DO i=1,CurrentPath % NumberOfElements
6972
IF((.NOT. BreakElement(i)) .OR. DeleteElement(i)) CYCLE
6973
!This element needs to be adjusted
6974
DO j=i+1,CurrentPath % NumberOfElements
6975
IF(.NOT. (BreakElement(j) .OR. DeleteElement(j))) &
6976
CALL Fatal("ValidateCrevasseGroups","Programming error in maintaining aspect ratio")
6977
IF(DeleteElement(j)) CYCLE
6978
!This is the next 'break element' after i
6979
!Determine which nodes we keep
6980
6981
IF((CurrentPath % NodeNumbers(j) /= &
6982
Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(1)) .OR. &
6983
(CurrentPath % NodeNumbers(j+1) /= &
6984
Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(2))) THEN
6985
6986
CALL Fatal("ValidateCrevassePaths", "Chain building error")
6987
END IF
6988
6989
Mesh % Elements(CurrentPath % ElementNumbers(i)) % NodeIndexes(2) = &
6990
Mesh % Elements(CurrentPath % ElementNumbers(j)) % NodeIndexes(2)
6991
6992
!We now want to delete it, because we only keep one from each broken pair
6993
DeleteElement(j) = .TRUE.
6994
EXIT !we paired this one, move on
6995
END DO
6996
END DO
6997
6998
!Delete the elements and nodes
6999
IF(COUNT(DeleteElement) > 0) THEN
7000
!elements
7001
ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))
7002
WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)
7003
7004
DEALLOCATE(CurrentPath % ElementNumbers)
7005
ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))
7006
7007
CurrentPath % ElementNumbers = WorkInt
7008
CurrentPath % NumberOfElements = SIZE(WorkInt)
7009
DEALLOCATE(WorkInt)
7010
7011
!nodes
7012
ALLOCATE(WorkInt(COUNT(.NOT. FarNode)))
7013
WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.FarNode)
7014
7015
DEALLOCATE(CurrentPath % NodeNumbers)
7016
ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))
7017
7018
CurrentPath % NodeNumbers = WorkInt
7019
CurrentPath % NumberOfNodes = SIZE(WorkInt)
7020
DEALLOCATE(WorkInt)
7021
END IF
7022
7023
DEALLOCATE(FarNode, Constriction, ConstrictDirection, BreakElement, DeleteElement)
7024
7025
! remove excess lateral nodes as this leads to errors in level set
7026
IF(AddLateralMargins) THEN
7027
IF(CurrentPath % NodeNumbers(1) <= ONNodes) THEN
7028
crop(1) = 1
7029
ELSE
7030
DO i=1, CurrentPath % NumberOfNodes-1
7031
IF(CurrentPath % NodeNumbers(i) > ONNodes .AND. &
7032
CurrentPath % NodeNumbers(i+1) <= ONNodes) THEN
7033
crop(1) = i
7034
EXIT
7035
END IF
7036
END DO
7037
END IF
7038
IF(CurrentPath % NodeNumbers(CurrentPath % NumberOfNodes) <= ONNodes) THEN
7039
crop(2) = CurrentPath % NumberOfNodes
7040
ELSE
7041
DO i=CurrentPath % NumberOfNodes, 2, -1
7042
IF(CurrentPath % NodeNumbers(i) > ONNodes .AND. &
7043
CurrentPath % NodeNumbers(i-1) <= ONNodes) THEN
7044
crop(2) = i
7045
EXIT
7046
END IF
7047
END DO
7048
END IF
7049
7050
ALLOCATE(DeleteNode(CurrentPath % NumberOfNodes),&
7051
DeleteElement(CurrentPath % NumberOfElements),&
7052
BreakElement(CurrentPath % NumberOfElements))
7053
DeleteNode = .TRUE.; DeleteElement = .FALSE.; BreakElement = .FALSE.
7054
DeleteNode(crop(1):crop(2)) = .FALSE.
7055
DO i=1,CurrentPath % NumberOfElements
7056
IF(ANY(DeleteNode(i:i+1))) THEN
7057
IF(ALL(DeleteNode(i:i+1))) THEN
7058
DeleteElement(i) = .TRUE.
7059
ELSE
7060
BreakElement(i) = .TRUE.
7061
END IF
7062
END IF
7063
END DO
7064
7065
IF(COUNT(BreakElement) > 1) THEN
7066
IF(Sideloops > 1) THEN
7067
IF(COUNT(BreakElement) > 3) CALL FATAL('ValidateNPCrevassePath', &
7068
'Assumption removing lateral margins does not break elements')
7069
ELSE
7070
IF(COUNT(BreakElement) > 2) CALL FATAL('ValidateNPCrevassePath', &
7071
'Assumption removing lateral margins does not break elements')
7072
IF(DeleteElement(1) .OR. DeleteElement(CurrentPath % NumberOfElements)) &
7073
CALL FATAL('ValidateNPCrevassePath', &
7074
'Assumption removing lateral margins does not break elements')
7075
END IF
7076
END IF
7077
7078
!Delete them
7079
IF(COUNT(DeleteElement) > 0) THEN
7080
!elements
7081
ALLOCATE(WorkInt(COUNT(.NOT. DeleteElement)))
7082
WorkInt = PACK(CurrentPath % ElementNumbers,.NOT.DeleteElement)
7083
7084
DEALLOCATE(CurrentPath % ElementNumbers)
7085
ALLOCATE(CurrentPath % ElementNumbers(SIZE(WorkInt)))
7086
7087
CurrentPath % ElementNumbers = WorkInt
7088
CurrentPath % NumberOfElements = SIZE(WorkInt)
7089
DEALLOCATE(WorkInt)
7090
7091
!nodes
7092
ALLOCATE(WorkInt(COUNT(.NOT. DeleteNode)))
7093
WorkInt = PACK(CurrentPath % NodeNumbers, .NOT.DeleteNode)
7094
7095
DEALLOCATE(CurrentPath % NodeNumbers)
7096
ALLOCATE(CurrentPath % NodeNumbers(SIZE(WorkInt)))
7097
7098
CurrentPath % NodeNumbers = WorkInt
7099
CurrentPath % NumberOfNodes = SIZE(WorkInt)
7100
DEALLOCATE(WorkInt)
7101
END IF
7102
DEALLOCATE(DeleteElement, DeleteNode, BreakElement)
7103
END IF
7104
7105
10 CONTINUE ! if crev was invalid need to rotate mesh back
7106
7107
! deallocations
7108
DEALLOCATE(REdge, IsBelow, InRange)
7109
!--------------------------------------------------------
7110
! Put the mesh back
7111
!--------------------------------------------------------
7112
CALL RotateMesh(Mesh, UnRotationMatrix)
7113
CurrentPath => CurrentPath % Next
7114
END DO
7115
7116
!Actually remove previous marked
7117
CurrentPath => CrevassePaths
7118
DO WHILE(ASSOCIATED(CurrentPath))
7119
WorkPath => CurrentPath % Next
7120
path=path+1
7121
7122
IF(.NOT. CurrentPath % Valid) THEN
7123
IF(ASSOCIATED(CurrentPath,CrevassePaths)) CrevassePaths => WorkPath
7124
CALL RemoveCrevassePath(CurrentPath)
7125
IF(Debug) CALL Info("ValidateNPCrevassePaths","Removing a crevasse path")
7126
END IF
7127
CurrentPath => WorkPath
7128
END DO
7129
7130
rt = RealTime() - rt0
7131
PRINT*, 'time', rt
7132
7133
END SUBROUTINE ValidateNPCrevassePaths
7134
7135
SUBROUTINE CheckMeshQuality(Mesh)
7136
7137
TYPE(Mesh_t), POINTER :: Mesh
7138
TYPE(Nodes_t) :: ElementNodes
7139
TYPE(Element_t),POINTER :: Element, Parent
7140
REAL(KIND=dp) :: U,V,W,detJ,Basis(10), mean
7141
INTEGER, POINTER :: NodeIndexes(:)
7142
INTEGER :: i,j,n,l,k, count
7143
INTEGER, ALLOCATABLE :: counters(:)
7144
LOGICAL :: stat,Debug
7145
CHARACTER(LEN=MAX_NAME_LEN) :: FuncName="CheckMeshQuality"
7146
7147
Debug = .FALSE.
7148
n = Mesh % MaxElementNodes
7149
ALLOCATE(ElementNodes % x(n),&
7150
ElementNodes % y(n),&
7151
ElementNodes % z(n))
7152
7153
!Some debug stats on the number of elements in each body/boundary
7154
IF(Debug) THEN
7155
ALLOCATE(counters(-2:10))
7156
7157
!Some stats
7158
counters = 0
7159
DO i=1,Mesh % NumberOfBulkElements
7160
n = Mesh % Elements(i) % BodyID
7161
counters(n) = counters(n) + 1
7162
END DO
7163
7164
DO i=-2,10
7165
PRINT *,ParEnv % MyPE,' body body id: ',i,' count: ',counters(i),' of ',&
7166
Mesh % NumberOfBulkElements
7167
END DO
7168
7169
7170
counters = 0
7171
DO i=Mesh % NumberOfBulkElements + 1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
7172
n = Mesh % Elements(i) % BodyID
7173
IF(n <= 10 .AND. n > -3) THEN
7174
counters(n) = counters(n) + 1
7175
ELSE
7176
PRINT *,ParEnv % MyPE,' unexpected BC body id: ',n,i
7177
END IF
7178
END DO
7179
7180
DO i=0,4
7181
PRINT *,ParEnv % MyPE,' BC body id: ',i,' count: ',counters(i),' of ',&
7182
Mesh % NumberOfBoundaryElements, REAL(counters(i))/REAL(Mesh % NumberOfBoundaryElements)
7183
END DO
7184
7185
counters = 0
7186
DO i=Mesh % NumberOfBulkElements+1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
7187
n = Mesh % Elements(i) % BoundaryInfo % Constraint
7188
IF(n <= 10 .AND. n > -3) THEN
7189
counters(n) = counters(n) + 1
7190
ELSE
7191
PRINT *,ParEnv % MyPE,' unexpected constraint: ',n,i
7192
END IF
7193
END DO
7194
7195
DO i=0,6
7196
PRINT *,ParEnv % MyPE,' BC constraint: ',i,' count: ',counters(i),' of ',Mesh % NumberOfBoundaryElements,&
7197
REAL(counters(i))/REAL(Mesh % NumberOfBoundaryElements)
7198
END DO
7199
7200
counters = 0
7201
DO i=Mesh % NumberOfBulkElements+1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
7202
n = Mesh % Elements(i) % BoundaryInfo % OutBody
7203
IF(n <= 10 .AND. n > -3) THEN
7204
counters(n) = counters(n) + 1
7205
ELSE
7206
PRINT *,ParEnv % MyPE,' unexpected outbody: ',n,i
7207
END IF
7208
END DO
7209
7210
DO i=-2,10
7211
PRINT *,ParEnv % MyPE,' outbody: ',i,' count: ',counters(i),' of ',Mesh % NumberOfBoundaryElements
7212
END DO
7213
END IF
7214
7215
!Check all BC elements have parents
7216
DO i=Mesh % NumberOfBulkElements+1, Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
7217
Element => Mesh % Elements(i)
7218
Parent => Element % BoundaryInfo % Left
7219
IF( .NOT. ASSOCIATED(Parent) ) THEN
7220
Parent => Element % BoundaryInfo % Right
7221
END IF
7222
IF( .NOT. ASSOCIATED( Parent ) ) THEN
7223
PRINT *,ParEnv % MyPE,i,' BC element without parent! constraint: ',Element % BoundaryInfo % constraint, &
7224
' body id: ',Element % BodyID,' nodes: ',Element % NodeIndexes,&
7225
' global: ',Mesh % ParallelInfo % GlobalDOFs(Element%NodeIndexes)
7226
CALL Fatal(FuncName, "BC Element without parent!")
7227
END IF
7228
END DO
7229
7230
!check for duplicate element & node indices (locally only)
7231
DO i=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
7232
IF(Mesh % Elements(i) % GElementIndex <= 0) CALL Fatal(FuncName, 'Element has ID 0')
7233
DO j=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
7234
IF(i==j) CYCLE
7235
IF(Mesh % Elements(i) % GElementIndex == Mesh % Elements(j) % GElementIndex) THEN
7236
PRINT *,ParEnv % MyPE,' elements ',i,j,' have same GElementIndex: ',&
7237
Mesh % Elements(j) % GElementIndex
7238
CALL Fatal(FuncName, "Duplicate GElementIndexes!")
7239
END IF
7240
END DO
7241
END DO
7242
7243
DO i=1,Mesh % NumberOfNodes
7244
IF(Mesh % ParallelInfo % GlobalDOFs(i) <= 0) THEN
7245
PRINT*, ParEnv % MyPE, 'Node ', i, 'Has no GlobalID'
7246
CALL Fatal(FuncName, 'Node has ID 0')
7247
END IF
7248
DO j=1,Mesh % NumberOfNodes
7249
IF(i==j) CYCLE
7250
IF(Mesh % ParallelInfo % GlobalDOFs(i) == Mesh % ParallelInfo % GlobalDOFs(j)) THEN
7251
PRINT *,ParEnv % MyPE,' nodes ',i,j,' have same GlobalDOF: ',&
7252
Mesh % ParallelInfo % GlobalDOFs(j)
7253
CALL Fatal(FuncName, "Duplicate GlobalDOFs!")
7254
END IF
7255
END DO
7256
END DO
7257
7258
!Check element detj etc
7259
DO j=1,2
7260
IF(j==1) mean = 0.0
7261
DO i=1,Mesh % NumberOfBulkElements
7262
Element => Mesh % Elements(i)
7263
n = Element % TYPE % NumberOfNodes
7264
NodeIndexes => Element % NodeIndexes
7265
7266
!Check element for duplicate node indexes
7267
DO k=1,n
7268
DO l=1,n
7269
IF(l==k) CYCLE
7270
IF(NodeIndexes(k) == NodeIndexes(l)) THEN
7271
WRITE(Message, '(A,i0,A)') "Mesh Element ",i," has duplicate node indexes!"
7272
CALL Fatal(FuncName,Message)
7273
END IF
7274
END DO
7275
END DO
7276
7277
ElementNodes % x(1:n) = Mesh % Nodes % x(NodeIndexes(1:n))
7278
ElementNodes % y(1:n) = Mesh % Nodes % y(NodeIndexes(1:n))
7279
ElementNodes % z(1:n) = Mesh % Nodes % z(NodeIndexes(1:n))
7280
7281
stat = ElementInfo( Element,ElementNodes,U,V,W,detJ, &
7282
Basis )
7283
!Check detj - warn if deviates from mean, fatal if <= 0
7284
IF(j==2) THEN
7285
IF(detj <= 0.0) THEN
7286
WRITE(Message, '(A,i0,A)') "Element ",j," has detj <= 0"
7287
CALL Fatal(FuncName, Message)
7288
ELSE IF(detj < mean/10.0 .OR. detj > mean*10.0) THEN
7289
WRITE(Message, '(i0,A,i0,A,F10.2,A,F10.2,A)') ParEnv % MyPE,' element ',&
7290
i,' detj (',detj,') deviates from mean (',mean,')'
7291
IF(Debug) CALL Warn(FuncName, Message)
7292
END IF
7293
ELSE
7294
mean = mean + detj
7295
END IF
7296
END DO
7297
IF(j==1) mean = mean / Mesh % NumberOfBulkElements
7298
END DO
7299
7300
DEALLOCATE(ElementNodes % x,&
7301
ElementNodes % y,&
7302
ElementNodes % z)
7303
7304
END SUBROUTINE CheckMeshQuality
7305
7306
!Takes a mesh with GroundedMask defined on the base, and
7307
!ensures that grounded nodes remain grounded
7308
!i.e. sets z = min zs bottom wherever GroundedMask>-0.5
7309
SUBROUTINE EnforceGroundedMask(Model, Mesh)
7310
TYPE(Model_t) :: Model
7311
TYPE(Mesh_t), POINTER :: Mesh
7312
!-------------------------
7313
TYPE(Solver_t), POINTER :: NullSolver => NULL()
7314
TYPE(ValueList_t), POINTER :: Material
7315
TYPE(Variable_t), POINTER :: GMaskVar
7316
TYPE(Element_t), POINTER :: Element
7317
REAL(KIND=dp), POINTER :: GMask(:)
7318
REAL(KIND=dp) :: zb, xydist, zdist
7319
INTEGER :: i,j,k,n,BaseBCtag,FrontBCtag, dummyint, counter, NoNeighbours, ierr
7320
INTEGER, POINTER :: GMaskPerm(:), FrontPerm(:)=>NULL()
7321
INTEGER, ALLOCATABLE :: GDOFs(:), PartNoGDOFs(:), PartGDOFs(:), disps(:)
7322
LOGICAL :: ConstraintChanged, ThisBC, Found, HasNeighbours
7323
CHARACTER(MAX_NAME_LEN) :: FuncName="EnforceGroundedMask", GMaskVarName
7324
7325
GMaskVarName = "GroundedMask"
7326
GMaskVar => VariableGet(Mesh % Variables, GMaskVarName, .TRUE.)
7327
IF(.NOT.ASSOCIATED(GMaskVar)) THEN
7328
CALL Info(FuncName, "Didn't find GroundedMask, so not enforcing bed height",Level=5)
7329
RETURN
7330
END IF
7331
7332
CALL MakePermUsingMask( Model, NullSolver, Mesh, "Calving Front Mask", &
7333
.FALSE., FrontPerm, dummyint)
7334
7335
Material => GetMaterial(Mesh % Elements(1)) !TODO, this is not generalised
7336
7337
GMask => GMaskVar % Values
7338
GMaskPerm => GMaskVar % Perm
7339
7340
DO i=1,Model % NumberOfBCs
7341
ThisBC = ListGetLogical(Model % BCs(i) % Values,"Bottom Surface Mask",Found)
7342
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
7343
BaseBCtag = Model % BCs(i) % Tag
7344
EXIT
7345
END DO
7346
7347
DO i=1,Model % NumberOfBCs
7348
ThisBC = ListGetLogical(Model % BCs(i) % Values,"Calving Front Mask",Found)
7349
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
7350
FrontBCtag = Model % BCs(i) % Tag
7351
EXIT
7352
END DO
7353
7354
ALLOCATE(GDOFs(Mesh % NumberOfNodes))
7355
counter=0
7356
DO i=1,Mesh % NumberOfNodes
7357
IF(GMaskPerm(i) == 0) CYCLE
7358
zb = ListGetRealAtNode(Material, "Min Zs Bottom",i,UnfoundFatal=.TRUE.)
7359
7360
NoNeighbours = SIZE(Mesh % ParallelInfo % &
7361
NeighbourList(i) % Neighbours) - 1
7362
HasNeighbours = NoNeighbours > 0
7363
7364
!Floating -> check no penetration
7365
!Grounded -> set to bedrock height
7366
IF(GMask(GMaskPerm(i)) < -0.5) THEN
7367
IF(Mesh % Nodes % z(i) < zb) THEN
7368
Mesh % Nodes % z(i) = zb
7369
IF(HasNeighbours) THEN
7370
counter = counter+1
7371
GDOFs(counter) = Mesh % ParallelInfo % GlobalDOFs(i)
7372
END IF
7373
END IF
7374
ELSE
7375
IF(HasNeighbours) THEN
7376
counter = counter+1
7377
GDOFs(counter) = Mesh % ParallelInfo % GlobalDOFs(i)
7378
END IF
7379
Mesh % Nodes % z(i) = zb
7380
7381
!check element how much this deforms elements near front
7382
!if the element is above a 45 degree vertical angle from xy plane change to front boundary
7383
DO j=Mesh % NumberOfBulkElements +1, &
7384
Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
7385
7386
Element => Mesh % Elements(j)
7387
IF(Element % BoundaryInfo % Constraint /= BaseBCtag) CYCLE
7388
n = Element % TYPE % NumberOfNodes
7389
7390
!Doesn't contain our point
7391
IF(.NOT. ANY(Element % NodeIndexes(1:n)==i)) CYCLE
7392
7393
ConstraintChanged = .FALSE.
7394
7395
DO k=1,n
7396
IF(ConstraintChanged) CYCLE
7397
IF(Element % NodeIndexes(k) == i) CYCLE ! this node
7398
IF(GMask(GMaskPerm(Element % NodeIndexes(k))) >= -0.5) CYCLE ! grounded
7399
IF(FrontPerm(Element % NodeIndexes(k)) == 0) CYCLE ! new node not on front
7400
7401
xydist = NodeDist2D(Mesh % Nodes, i, Element % NodeIndexes(k))
7402
zdist = ABS(Mesh % Nodes % z(i) - Mesh % Nodes % z(Element % NodeIndexes(k)))
7403
7404
IF(zdist > xydist) THEN
7405
CALL WARN(FuncName, "Transferring boundary element to front as it vertically &
7406
angled after GroundedMask has been enforced")
7407
PRINT*, 'For node', i, 'x:', Mesh % Nodes % x(i), 'y:', Mesh % Nodes % y(i),&
7408
'z:', Mesh % Nodes % z(i)
7409
7410
Element % BoundaryInfo % Constraint = FrontBCtag
7411
ConstraintChanged = .TRUE.
7412
END IF
7413
END DO
7414
7415
IF(ConstraintChanged) THEN
7416
FrontPerm(Element % NodeIndexes) = 1
7417
END IF
7418
END DO
7419
END IF
7420
END DO
7421
7422
! sometimes if a shared node is on a partition without a bsae boundary element then
7423
! GMaskPerm will be zero on this partition but be above zero ot other partitions
7424
! therefore we need to share any gdofs that have been moved to ensure they are movoed
7425
! on all partitions so coords are consistent
7426
7427
ALLOCATE(PartNoGDOFs(ParEnv % PEs))
7428
CALL MPI_ALLGATHER(counter, 1, MPI_INTEGER, &
7429
PartNoGDOFs, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
7430
7431
ALLOCATE(disps(ParEnv % PEs))
7432
disps(1) = 0
7433
DO i=2,ParEnv % PEs
7434
disps(i) = disps(i-1) + PartNoGDOFs(i-1)
7435
END DO
7436
7437
ALLOCATE(PartGDOFs(SUM(PartNoGDOFs)))
7438
CALL MPI_AllGatherV(GDOFs(:counter), counter, MPI_INTEGER, &
7439
PartGDOFs, PartNoGDOFs, disps, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
7440
7441
DO i=1, Mesh % NumberOfNodes
7442
IF(ANY(PartGDOFs == Mesh % ParallelInfo % GlobalDOFs(i))) THEN
7443
zb = ListGetRealAtNode(Material, "Min Zs Bottom",i,UnfoundFatal=.TRUE.)
7444
Mesh % Nodes % z(i) = zb
7445
END IF
7446
END DO
7447
7448
DEALLOCATE(FrontPerm)
7449
7450
END SUBROUTINE EnforceGroundedMask
7451
7452
SUBROUTINE ResetMeshUpdate(Model, Solver)
7453
USE MeshUtils
7454
7455
TYPE(Model_t) :: Model
7456
TYPE(Solver_t) :: Solver
7457
! --------------------
7458
TYPE(Variable_t), POINTER :: Var, RefVar
7459
TYPE(ValueList_t), POINTER :: Params
7460
INTEGER :: i, Num
7461
LOGICAL :: Found
7462
CHARACTER(MAX_NAME_LEN) :: SolverName, VarName
7463
SolverName = 'ResetMeshUpdate'
7464
7465
Params => Solver % Values
7466
7467
DO Num = 1,999
7468
WRITE(Message,'(A,I0)') 'Mesh Update Variable ',Num
7469
VarName = ListGetString( Params, Message, Found)
7470
IF( .NOT. Found) EXIT
7471
7472
Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )
7473
IF(.NOT. ASSOCIATED(Var)) THEN
7474
WRITE(Message,'(A,A)') "Listed mesh update variable but can not find: ",VarName
7475
CALL Fatal(SolverName, Message)
7476
END IF
7477
Var % Values = 0.0_dp
7478
END DO
7479
7480
!Turn off free surface solvers for next timestep
7481
!And set values equal to z (or rotated) coordinate
7482
DO Num = 1,999
7483
WRITE(Message,'(A,I0)') 'FreeSurface Variable ',Num
7484
VarName = ListGetString( Params, Message, Found)
7485
IF( .NOT. Found) EXIT
7486
7487
Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )
7488
IF(.NOT. ASSOCIATED(Var)) THEN
7489
WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",VarName
7490
CALL Fatal(SolverName, Message)
7491
END IF
7492
7493
RefVar => VariableGet( Model % Mesh % Variables, "Reference "//TRIM(VarName), .TRUE. )
7494
IF(.NOT. ASSOCIATED(RefVar)) THEN
7495
WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",&
7496
"Reference "//TRIM(VarName)
7497
CALL Fatal(SolverName, Message)
7498
END IF
7499
7500
DO i=1,Model % Mesh % NumberOfNodes
7501
IF(Var % Perm(i) <= 0) CYCLE
7502
Var % Values(Var % Perm(i)) = Model % Mesh % Nodes % z(i)
7503
RefVar % Values(RefVar % Perm(i)) = Model % Mesh % Nodes % z(i)
7504
END DO
7505
END DO
7506
7507
END SUBROUTINE ResetMeshUpdate
7508
7509
SUBROUTINE ReleaseCrevassePaths(CrevassePaths)
7510
TYPE(CrevassePath_t), POINTER :: CrevassePaths,CurrentPath
7511
7512
CurrentPath => CrevassePaths
7513
DO WHILE(ASSOCIATED(CurrentPath))
7514
IF(ASSOCIATED(CurrentPath % NodeNumbers)) THEN
7515
DEALLOCATE(CurrentPath % NodeNumbers)
7516
CurrentPath % NodeNumbers => NULL()
7517
END IF
7518
IF(ASSOCIATED(CurrentPath % ElementNumbers)) THEN
7519
DEALLOCATE(CurrentPath % ElementNumbers)
7520
CurrentPath % ElementNumbers => NULL()
7521
END IF
7522
7523
CurrentPath => CurrentPath % Next
7524
END DO
7525
7526
DEALLOCATE(CrevassePaths)
7527
7528
END SUBROUTINE ReleaseCrevassePaths
7529
7530
SUBROUTINE EnforceLateralMargins(Model, SolverParams)
7531
IMPLICIT NONE
7532
TYPE(Model_t) :: Model
7533
TYPE(Valuelist_t), POINTER :: SolverParams
7534
!-----------------------------------------
7535
TYPE(Solver_t), POINTER :: AdvSolver
7536
TYPE(Valuelist_t), POINTER :: AdvParams
7537
TYPE(Element_t), POINTER :: Element
7538
TYPE(Mesh_t), POINTER :: Mesh
7539
CHARACTER(MAX_NAME_LEN) :: FuncName, Adv_EqName, LeftRailFName, RightRailFName, &
7540
FrontMaskName,LeftMaskName, RightMaskName
7541
INTEGER, POINTER :: FrontPerm(:)=>NULL(), LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), &
7542
NodeIndexes(:)
7543
LOGICAL :: Found, inside, GotNode, ClosestRight
7544
LOGICAL, ALLOCATABLE :: UsedNode(:)
7545
INTEGER :: i,j,k,m,Nl,Nr, Naux, ok, DummyInt, ClosestRail, ClosestNode, counter, node(1), &
7546
closest, DuplicateNode, ierr
7547
REAL(kind=dp), ALLOCATABLE :: xL(:),yL(:),xR(:),yR(:), RailPoly(:,:)
7548
REAL(kind=dp) :: xx,yy, mindist, tempdist, minx, maxx, miny, maxy, &
7549
triangle(4,2,3), area(4), poly(2,4), closestpoint(2), buffer
7550
INTEGER, PARAMETER :: io=20
7551
7552
FuncName = "EnforceLateralMargins"
7553
7554
Mesh => Model % Mesh
7555
7556
Adv_EqName = ListGetString(SolverParams,"Front Advance Solver", DefValue="Front Advance")
7557
! Locate CalvingAdvance Solver
7558
Found = .FALSE.
7559
DO i=1,Model % NumberOfSolvers
7560
IF(GetString(Model % Solvers(i) % Values, 'Equation') == Adv_EqName) THEN
7561
AdvSolver => Model % Solvers(i)
7562
Found = .TRUE.
7563
EXIT
7564
END IF
7565
END DO
7566
IF(.NOT. Found) CALL FATAL(FuncName, "'Front Advance Solver' not given")
7567
AdvParams => AdvSolver % Values
7568
7569
buffer = ListGetConstReal(AdvParams, "Rail Buffer", Found, DefValue=0.1_dp)
7570
IF(.NOT. Found) CALL Info(FuncName, "No Rail Buffer set using default 0.1")
7571
7572
LeftRailFName = ListGetString(AdvParams, "Left Rail File Name", Found)
7573
IF(.NOT. Found) THEN
7574
CALL Info(FuncName, "Left Rail File Name not found, assuming './LeftRail.xy'")
7575
LeftRailFName = "LeftRail.xy"
7576
END IF
7577
Nl = ListGetInteger(AdvParams, "Left Rail Number Nodes", Found)
7578
IF(.NOT.Found) THEN
7579
WRITE(Message,'(A,A)') 'Left Rail Number Nodes not found'
7580
CALL FATAL(FuncName, Message)
7581
END IF
7582
!TO DO only do these things if firsttime=true?
7583
OPEN(unit = io, file = TRIM(LeftRailFName), status = 'old',iostat = ok)
7584
IF (ok /= 0) THEN
7585
WRITE(message,'(A,A)') 'Unable to open file ',TRIM(LeftRailFName)
7586
CALL FATAL(Trim(FuncName),Trim(message))
7587
END IF
7588
ALLOCATE(xL(Nl), yL(Nl))
7589
7590
! read data
7591
DO i = 1, Nl
7592
READ(io,*,iostat = ok, end=200) xL(i), yL(i)
7593
END DO
7594
200 Naux = Nl - i
7595
IF (Naux > 0) THEN
7596
WRITE(Message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(LeftRailFName)
7597
CALL INFO(Trim(FuncName),Trim(message))
7598
END IF
7599
CLOSE(io)
7600
RightRailFName = ListGetString(AdvParams, "Right Rail File Name", Found)
7601
IF(.NOT. Found) THEN
7602
CALL Info(FuncName, "Right Rail File Name not found, assuming './RightRail.xy'")
7603
RightRailFName = "RightRail.xy"
7604
END IF
7605
7606
Nr = ListGetInteger(AdvParams, "Right Rail Number Nodes", Found)
7607
IF(.NOT.Found) THEN
7608
WRITE(Message,'(A,A)') 'Right Rail Number Nodes not found'
7609
CALL FATAL(FuncName, Message)
7610
END IF
7611
!TO DO only do these things if firsttime=true?
7612
OPEN(unit = io, file = TRIM(RightRailFName), status = 'old',iostat = ok)
7613
7614
IF (ok /= 0) THEN
7615
WRITE(Message,'(A,A)') 'Unable to open file ',TRIM(RightRailFName)
7616
CALL FATAL(Trim(FuncName),Trim(message))
7617
END IF
7618
ALLOCATE(xR(Nr), yR(Nr))
7619
7620
! read data
7621
DO i = 1, Nr
7622
READ(io,*,iostat = ok, end=100) xR(i), yR(i)
7623
END DO
7624
100 Naux = Nr - i
7625
IF (Naux > 0) THEN
7626
WRITE(message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(RightRailFName)
7627
CALL INFO(Trim(FuncName),Trim(message))
7628
END IF
7629
CLOSE(io)
7630
7631
ALLOCATE(RailPoly(2, Nl+Nr+1))
7632
RailPoly(1,1:Nl) = xL
7633
RailPoly(2,1:Nl) = yL
7634
counter=0
7635
DO i=Nr, 1, -1
7636
counter=counter+1
7637
RailPoly(1,Nl+counter) = xR(i)
7638
RailPoly(2,Nl+counter) = yR(i)
7639
END DO
7640
RailPoly(1,Nl+Nr+1) = xL(1)
7641
RailPoly(2,Nl+Nr+1) = yL(1)
7642
7643
LeftMaskName = "Left Sidewall Mask"
7644
RightMaskName = "Right Sidewall Mask"
7645
FrontMaskName = "Calving Front Mask"
7646
7647
!Generate perms to quickly get nodes on each boundary
7648
CALL MakePermUsingMask( Model, AdvSolver, Mesh, LeftMaskName, &
7649
.FALSE., LeftPerm, dummyint)
7650
CALL MakePermUsingMask( Model, AdvSolver, Mesh, RightMaskName, &
7651
.FALSE., RightPerm, dummyint)
7652
CALL MakePermUsingMask( Model, AdvSolver, Mesh, FrontMaskName, &
7653
.FALSE., FrontPerm, dummyint)
7654
7655
DO i=1, Mesh % NumberOfNodes
7656
IF(.NOT. (LeftPerm(i) > 0 .OR. RightPerm(i) > 0 .OR. FrontPerm(i) > 0)) CYCLE
7657
xx = Mesh % Nodes % x(i)
7658
yy = Mesh % Nodes % y(i)
7659
7660
IF(LeftPerm(i) > 0) THEN ! check if on left side
7661
mindist = HUGE(1.0_dp)
7662
DO j=1, Nl-1
7663
tempdist = PointLineSegmDist2D((/xL(j), yL(j)/),(/xL(j+1), yL(j+1)/), (/xx, yy/))
7664
IF(tempdist < mindist) THEN
7665
closest = j
7666
mindist = tempdist
7667
END IF
7668
END DO
7669
7670
IF(mindist > buffer) THEN
7671
closestpoint = ClosestPointOfLineSegment((/xL(closest), yL(closest)/),(/xL(closest+1), yL(closest+1)/), (/xx, yy/))
7672
Mesh % Nodes % x(i) = closestpoint(1)
7673
Mesh % Nodes % y(i) = closestpoint(2)
7674
END IF
7675
END IF
7676
7677
IF(RightPerm(i) > 0) THEN ! check if on left side
7678
mindist = HUGE(1.0_dp)
7679
DO j=1, Nr-1
7680
tempdist = PointLineSegmDist2D((/xR(j), yR(j)/),(/xR(j+1), yR(j+1)/), (/xx, yy/))
7681
IF(tempdist < mindist) THEN
7682
closest = j
7683
mindist = tempdist
7684
END IF
7685
END DO
7686
7687
IF(mindist > buffer) THEN
7688
closestpoint = ClosestPointOfLineSegment((/xR(closest), yR(closest)/),(/xR(closest+1), yR(closest+1)/), (/xx, yy/))
7689
Mesh % Nodes % x(i) = closestpoint(1)
7690
Mesh % Nodes % y(i) = closestpoint(2)
7691
END IF
7692
END IF
7693
7694
IF(FrontPerm(i) > 0) THEN ! check if front is on rail eg advance on narrowing rails
7695
inside = PointInPolygon2D(RailPoly, (/xx,yy/))
7696
IF(inside) CYCLE
7697
7698
mindist = HUGE(1.0_dp)
7699
DO j=1, Nr-1
7700
tempdist = PointLineSegmDist2D((/xR(j), yR(j)/),(/xR(j+1), yR(j+1)/), (/xx, yy/))
7701
IF(tempdist < mindist) THEN
7702
closest = j
7703
mindist = tempdist
7704
END IF
7705
END DO
7706
ClosestRight = .TRUE.
7707
DO j=1, Nl-1
7708
tempdist = PointLineSegmDist2D((/xL(j), yL(j)/),(/xL(j+1), yL(j+1)/), (/xx, yy/))
7709
IF(tempdist < mindist) THEN
7710
closest = j
7711
mindist = tempdist
7712
ClosestRight = .FALSE.
7713
END IF
7714
END DO
7715
! check to see if closest point is frontleft to right eg outside the rail polygon
7716
! from the front not over the sides don't need to enforce margins
7717
! check both ends of rails as not sure which way glacier flowing
7718
tempdist = PointLineSegmDist2D((/xL(1), yL(1)/),(/xR(1), yL(1)/), (/xx, yy/))
7719
IF(tempdist < mindist) CYCLE
7720
tempdist = PointLineSegmDist2D((/xL(Nl), yL(Nl)/),(/xR(Nr), yR(Nr)/), (/xx, yy/))
7721
IF(tempdist < mindist) CYCLE
7722
7723
IF(mindist > buffer) THEN
7724
IF(ClosestRight) THEN
7725
closestpoint = ClosestPointOfLineSegment((/xR(closest), yR(closest)/),(/xR(closest+1), yR(closest+1)/), (/xx, yy/))
7726
ELSE
7727
closestpoint = ClosestPointOfLineSegment((/xL(closest), yL(closest)/),(/xL(closest+1), yL(closest+1)/), (/xx, yy/))
7728
END IF
7729
Mesh % Nodes % x(i) = closestpoint(1)
7730
Mesh % Nodes % y(i) = closestpoint(2)
7731
END IF
7732
END IF
7733
END DO
7734
7735
DEALLOCATE(FrontPerm, LeftPerm, RightPerm)
7736
7737
END SUBROUTINE EnforceLateralMargins
7738
7739
! determine the closest point of a line segment to a given point
7740
FUNCTION ClosestPointOfLineSegment(a1, a2, b) RESULT(c)
7741
REAL(kind=dp) :: a1(2), a2(2), b(2), a(2), c(2), dist, nx
7742
7743
a = a2 - a1
7744
dist = a(1)**2 + a(2)**2
7745
nx = ((b(1) - a1(1))*a(1) + (b(2)-a1(2))*a(2)) / dist
7746
7747
c(1) = a(1)*nx + a1(1)
7748
c(2) = a(2)*nx + a1(2)
7749
7750
END FUNCTION ClosestPointOfLineSegment
7751
7752
SUBROUTINE PauseCalvingSolvers(Model, Params, PauseSolvers)
7753
IMPLICIT NONE
7754
TYPE(Model_t) :: Model
7755
TYPE(Valuelist_t), POINTER :: Params
7756
LOGICAL :: PauseSolvers
7757
!---------------------------------------------
7758
TYPE(Variable_t), POINTER :: Var, RefVar
7759
REAL(kind=dp) :: PseudoSSdt, SaveDt, LastRemeshTime
7760
REAL(KIND=dp), POINTER :: TimestepSizes(:,:)
7761
LOGICAL :: CalvingOccurs, Found
7762
INTEGER :: i,j,Num, PauseTimeMax, PauseTimeCount, SaveSSiter, TimeIntervals, &
7763
NewTInterval
7764
CHARACTER(MAX_NAME_LEN) :: VarName, EqName, FuncName = "PauseCalvingSolvers"
7765
7766
SAVE :: SaveDt, SaveSSiter, PseudoSSdt, PauseTimeCount
7767
7768
!Need this for temporarily stopping simulation clock when calving occurs,
7769
! to recheck for multiple calving events triggered in the same timestep
7770
TimestepSizes => ListGetConstRealArray( CurrentModel % Simulation, &
7771
'Timestep Sizes', Found, UnfoundFatal=.TRUE.)
7772
IF(SIZE(TimestepSizes,1) > 1) CALL Fatal(FuncName,&
7773
"Calving solver requires a single constant 'Timestep Sizes'")
7774
7775
SaveDt = TimestepSizes(1,1)
7776
7777
SaveSSiter = ListGetInteger(Model % Simulation, "Steady State Max Iterations", Found)
7778
IF(.NOT. Found) SaveSSiter = 1
7779
7780
! since "Calving solver requires a single constant 'Timestep Sizes'"
7781
TimeIntervals = ListGetInteger(Model % Simulation, "Timestep Intervals", UnfoundFatal = .TRUE.)
7782
7783
PseudoSSdt = ListGetConstReal( Params, 'Pseudo SS dt', Found)
7784
IF(.NOT. Found) THEN
7785
CALL Warn(FuncName,"No value specified for 'Pseudo SS dt', taking 1.0e-10")
7786
PseudoSSdt = 1.0e-10
7787
END IF
7788
7789
PauseTimeMax = ListGetInteger(Params, "Calving Pause Max Steps", Found)
7790
IF(.NOT. Found) THEN
7791
CALL Warn(FuncName,"No value specified for 'Calving Pause Max Steps', using 15")
7792
PauseTimeMax = 15
7793
END IF
7794
7795
IF(PauseSolvers) THEN
7796
PauseTimeCount = PauseTimeCount + 1
7797
IF(PauseTimeCount > PauseTimeMax) THEN
7798
PauseSolvers = .FALSE.
7799
PauseTimeCount = 0
7800
CALL Info(FuncName,"Calving paused steps exceeded given threshold, moving on...")
7801
END IF
7802
ELSE
7803
PauseTimeCount = 0
7804
END IF
7805
7806
DO Num = 1,999
7807
WRITE(Message,'(A,I0)') 'Mesh Update Variable ',Num
7808
VarName = ListGetString( Params, Message, Found)
7809
IF( .NOT. Found) EXIT
7810
7811
Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )
7812
IF(.NOT. ASSOCIATED(Var)) THEN
7813
WRITE(Message,'(A,A)') "Listed mesh update variable but can not find: ",VarName
7814
CALL Fatal(FuncName, Message)
7815
END IF
7816
7817
CALL SwitchSolverExec(Var % Solver, (PauseSolvers))
7818
END DO
7819
7820
!Turn off free surface solvers for next timestep
7821
DO Num = 1,999
7822
WRITE(Message,'(A,I0)') 'FreeSurface Variable ',Num
7823
VarName = ListGetString( Params, Message, Found)
7824
IF( .NOT. Found) EXIT
7825
7826
Var => VariableGet( Model % Mesh % Variables, VarName, .TRUE. )
7827
IF(.NOT. ASSOCIATED(Var)) THEN
7828
WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",VarName
7829
CALL Fatal(FuncName, Message)
7830
END IF
7831
7832
RefVar => VariableGet( Model % Mesh % Variables, "Reference "//TRIM(VarName), .TRUE. )
7833
IF(.NOT. ASSOCIATED(RefVar)) THEN
7834
WRITE(Message,'(A,A)') "Listed FreeSurface variable but can not find: ",&
7835
"Reference "//TRIM(VarName)
7836
CALL Fatal(FuncName, Message)
7837
END IF
7838
7839
!Turn off (or on) the solver
7840
!If CalvingOccurs, (switch) off = .true.
7841
CALL SwitchSolverExec(Var % Solver, (PauseSolvers))
7842
END DO
7843
7844
IF(PauseSolvers) THEN
7845
CALL ListAddConstReal( Model % Simulation, 'Timestep Size', PseudoSSdt)
7846
CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', 1)
7847
CALL ListAddInteger( Model % Simulation, 'Timestep Intervals', TimeIntervals + 1)
7848
ELSE
7849
CALL ListAddConstReal( Model % Simulation, 'Timestep Size', SaveDt)
7850
CALL ListAddInteger( Model % Simulation, 'Steady State Max Iterations', SaveSSiter)
7851
END IF
7852
7853
DO Num = 1,999
7854
WRITE(Message,'(A,I0)') 'Switch Off Equation ',Num
7855
EqName = ListGetString( Params, Message, Found)
7856
IF( .NOT. Found) EXIT
7857
7858
Found = .FALSE.
7859
DO j=1,Model % NumberOfSolvers
7860
IF(ListGetString(Model % Solvers(j) % Values, "Equation") == EqName) THEN
7861
Found = .TRUE.
7862
!Turn off (or on) the solver
7863
!If CalvingOccurs, (switch) off = .true.
7864
CALL SwitchSolverExec(Model % Solvers(j), (PauseSolvers))
7865
EXIT
7866
END IF
7867
END DO
7868
7869
IF(.NOT. Found) THEN
7870
WRITE (Message,'(A,A,A)') "Failed to find Equation Name: ",EqName,&
7871
" to switch off after calving."
7872
CALL Fatal(FuncName,Message)
7873
END IF
7874
END DO
7875
7876
CALL ListAddLogical( Model % Simulation, 'Calving Pause Solvers', PauseSolvers )
7877
7878
IF(PauseSolvers) PRINT*, 'Solvers Paused!'
7879
7880
END SUBROUTINE PauseCalvingSolvers
7881
7882
SUBROUTINE CalvingStatsMMG(Params, Mesh, Mask, ElemMask, FileCreated, MaxBergVolume)
7883
7884
TYPE(Valuelist_t), POINTER :: Params
7885
TYPE(Mesh_t), POINTER :: Mesh
7886
LOGICAL :: Mask(:), ElemMask(:), FileCreated
7887
REAL(kind=dp) :: MaxBergVolume
7888
!-----------------------------
7889
TYPE(Element_t), POINTER :: Element
7890
INTEGER :: i, j, k, idx, NBdry, NBulk, NNodes, index, iceberg, node
7891
INTEGER, ALLOCATABLE :: ElNodes(:), nodes(:)
7892
LOGICAL :: HasNeighbour, NoNewNodes, NewIceBerg, Found
7893
LOGICAL, ALLOCATABLE :: FoundNode(:), UsedElem(:), IcebergElem(:), GotNode(:), &
7894
NodeCount(:)
7895
CHARACTER(LEN=MAX_NAME_LEN) :: Filename
7896
REAL(kind=dp), ALLOCATABLE :: BergVolumes(:), BergExtents(:), BergCentroids(:)
7897
REAL(kind=dp) :: BergVolume, extent(4), Centroid(3)
7898
7899
Filename = ListGetString(Params,"Calving Stats File Name", Found)
7900
IF(.NOT. Found) THEN
7901
CALL WARN('CalvingStat', 'Output file name not given so using CalvingStats.txt')
7902
Filename = "CalvingStats.txt"
7903
END IF
7904
7905
NBdry = Mesh % NumberOfBoundaryElements
7906
NBulk = Mesh % NumberOfBulkElements
7907
NNodes = Mesh % NumberOfNodes
7908
7909
!limit here of 10 possible mesh 'islands'
7910
ALLOCATE(FoundNode(NNodes), NodeCount(NNodes), ElNodes(4), &
7911
UsedElem(NBulk), IceBergElem(NBulk), BergVolumes(100), &
7912
BergExtents(100 * 4), BergCentroids(100*3))
7913
FoundNode = .FALSE.
7914
NodeCount = .NOT. Mask
7915
UsedElem = .FALSE. !count of elems used
7916
IcebergElem = .FALSE.
7917
iceberg=0 ! count of different mesh islands
7918
HasNeighbour=.FALSE. ! whether node has neighour
7919
7920
NoNewNodes = .TRUE.
7921
DO WHILE(COUNT(NodeCount) < NNodes)
7922
IF(NoNewNodes) THEN
7923
NewIceberg = .TRUE.
7924
IcebergElem=.FALSE.
7925
END IF
7926
NoNewNodes = .TRUE.
7927
DO i=1, NBulk
7928
IF(.NOT. ElemMask(i)) CYCLE
7929
IF(UsedElem(i)) CYCLE
7930
Element => Mesh % Elements(i)
7931
ElNodes = Element % NodeIndexes
7932
! if there are not any matching nodes and its not a new iceberg
7933
IF(ALL(.NOT. FoundNode(ElNodes)) .AND. .NOT. NewIceberg) CYCLE
7934
NewIceberg = .FALSE.
7935
UsedElem(i) = .TRUE.
7936
IcebergElem(i) = .TRUE.
7937
FoundNode(ElNodes) = .TRUE.
7938
NodeCount(ElNodes) = .TRUE.
7939
NoNewNodes = .FALSE.
7940
END DO
7941
IF(ALL(.NOT. IcebergElem)) EXIT
7942
IF(COUNT(NodeCount) == NNodes .OR. NoNewNodes) THEN
7943
DO i=1, NBulk
7944
IF(.NOT. ElemMask(i)) CYCLE
7945
IF(UsedElem(i)) CYCLE
7946
Element => Mesh % Elements(i)
7947
ElNodes = Element % NodeIndexes
7948
IF(ANY(.NOT. FoundNode(Elnodes))) CYCLE
7949
IcebergElem(i) = .TRUE.
7950
END DO
7951
iceberg = iceberg + 1
7952
CALL MeshVolume(Mesh, .FALSE., BergVolume, IcebergElem, Centroid)
7953
CALL IcebergExtent(Mesh, IcebergElem, Extent)
7954
7955
IF(SIZE(BergVolumes) < Iceberg) CALL DoubleDPVectorSize(BergVolumes)
7956
BergVolumes(iceberg) = BergVolume
7957
7958
IF(SIZE(BergExtents) < Iceberg*4) CALL DoubleDPVectorSize(BergExtents)
7959
BergExtents(iceberg*4-3:iceberg*4) = Extent
7960
7961
IF(SIZE(BergCentroids) < Iceberg*3) CALL DoubleDPVectorSize(BergCentroids)
7962
BergCentroids(iceberg*3-2:iceberg*3) = Centroid
7963
7964
IF(Iceberg > 0) THEN ! not first time
7965
PRINT*, 'Iceberg no.', Iceberg, BergVolume, 'extent', extent, 'centroid', centroid
7966
END IF
7967
END IF
7968
END DO
7969
7970
MaxBergVolume = MAXVAL(BergVolumes(1:iceberg))
7971
7972
! write to file
7973
IF(FileCreated) THEN
7974
OPEN( 36, FILE=filename, STATUS='UNKNOWN', POSITION='APPEND')
7975
ELSE
7976
OPEN( 36, FILE=filename, STATUS='UNKNOWN')
7977
WRITE(36, '(A)') "Calving Stats Output File"
7978
END IF
7979
7980
!Write out the left and rightmost points
7981
WRITE(36, '(A,i0,ES30.21)') 'Time: ',GetTimestep(),GetTime()
7982
7983
!Write the iceberg count
7984
WRITE(36, '(A,i0)') 'Number of Icebergs: ',Iceberg
7985
7986
DO i=1,iceberg
7987
7988
WRITE(36, '(A,i0,A,F20.0,A,F20.4,F20.4,F20.4,F20.4,A,F20.4,F20.4,F20.4)') &
7989
'Iceberg ',i, ' Volume ', BergVolumes(i),&
7990
' Extent ', BergExtents(i*4-3:i*4), ' Centroid ', BergCentroids(i*3-2:i*3)
7991
7992
END DO
7993
7994
CLOSE(36)
7995
FileCreated = .TRUE.
7996
7997
END SUBROUTINE CalvingStatsMMG
7998
7999
SUBROUTINE IcebergExtent(Mesh, ElemMask, Extent)
8000
8001
TYPE(Mesh_t), POINTER :: Mesh
8002
LOGICAL :: ElemMask(:)
8003
REAL(kind=dp) :: Extent(4)
8004
!-----------------------------
8005
TYPE(Element_t), POINTER :: Element
8006
INTEGER :: i, j, NBulk, n
8007
INTEGER, ALLOCATABLE :: ElementNodes(:)
8008
REAL(kind=dp) :: MinX, MaxX, MinY, MaxY
8009
8010
NBulk = Mesh % NumberOfBulkElements
8011
8012
! calculate volume of each bulk tetra. Add these together to get mesh volume
8013
MinX = HUGE(1.0_dp)
8014
MinY = HUGE(1.0_dp)
8015
MaxX = -HUGE(1.0_dp)
8016
MaxY = -HUGE(1.0_dp)
8017
DO, i=1, NBulk
8018
IF(.NOT. ElemMask(i)) CYCLE
8019
Element => Mesh % Elements(i)
8020
ElementNodes = Element % NodeIndexes
8021
n = Element % TYPE % NumberOfNodes
8022
8023
! get elem nodes
8024
DO j=1, n
8025
MinX = MIN(MinX, Mesh % Nodes % x(ElementNodes(j)))
8026
MinY = MIN(MinY, Mesh % Nodes % y(ElementNodes(j)))
8027
MaxX = MAX(MaxX, Mesh % Nodes % x(ElementNodes(j)))
8028
MaxY = MAX(MaxY, Mesh % Nodes % y(ElementNodes(j)))
8029
END DO
8030
8031
END DO
8032
8033
Extent(1) = MinX
8034
Extent(2) = MaxX
8035
Extent(3) = MinY
8036
Extent(4) = MaxY
8037
8038
END SUBROUTINE IcebergExtent
8039
8040
! check the front boundary elements are connected
8041
! if not (usually due to front advance around a corner) return
8042
! an array of the disconnected front element groups with the nearest lateral boundary constraint
8043
! the isomesh assigns a boundary on these to suppress calving here
8044
SUBROUTINE CheckFrontBoundary(Model, FrontConstraint, RightConstraint, LeftConstraint, ElemConstraint)
8045
8046
TYPE(Model_t) :: Model
8047
INTEGER :: FrontConstraint, RightConstraint, LeftConstraint
8048
INTEGER, ALLOCATABLE :: ElemConstraint(:)
8049
!------------------------------------------
8050
TYPE(Mesh_t), POINTER :: Mesh
8051
TYPE(Element_t), POINTER :: Element
8052
TYPE(Solver_t), POINTER :: NullSolver => NULL()
8053
INTEGER, POINTER :: LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), TopPerm(:)=>NULL(), BottomPerm(:)=>NULL()
8054
INTEGER :: i,j,k,l, counter, NBulk, NBdry, NNodes, LNNodes, RNNodes, group, &
8055
FNElm, ierr, status(MPI_STATUS_SIZE), proc, Neighbour, NGroups, &
8056
NNeighbours, dummyint
8057
INTEGER, ALLOCATABLE :: GroupCounts(:), ElNodes(:), PartGroups(:), PartGroupCounts(:), &
8058
GroupToPart(:), NeighbourList(:), TotalGroupCounts(:), GroupConstraint(:), &
8059
GDOFs(:), PartGDOFs(:), PNNeighbours(:), Order(:), WorkInt(:), PartConstraint(:)
8060
INTEGER, POINTER :: Neighbours(:)
8061
LOGICAL :: NoNewNodes, NewGroup, NoNewParts
8062
LOGICAL, ALLOCATABLE :: UsedElem(:), FoundNode(:), IsNeighbour(:,:), &
8063
PartNeighbours(:,:), GroupNeighbours(:,:), Grouper(:,:), PartGrouper(:,:), GroupElems(:,:)
8064
CHARACTER(MAX_NAME_LEN) :: LeftMaskName, RightMaskName, TopMaskName, BottomMaskName
8065
8066
Mesh => Model % Mesh
8067
NBulk = Mesh % NumberOfBulkElements
8068
NBdry = Mesh % NumberOfBoundaryElements
8069
NNodes = Mesh % NumberOfNodes
8070
8071
LeftMaskName = "Left Sidewall Mask"
8072
RightMaskName = "Right Sidewall Mask"
8073
TopMaskName = "Top Surface Mask"
8074
BottomMaskName = "Bottom Surface Mask"
8075
!Generate perms to quickly get nodes on each boundary
8076
CALL MakePermUsingMask( Model, NullSolver, Mesh, LeftMaskName, &
8077
.FALSE., LeftPerm, LNNodes)
8078
CALL MakePermUsingMask( Model, NullSolver, Mesh, RightMaskName, &
8079
.FALSE., RightPerm, RNNodes)
8080
CALL MakePermUsingMask( Model, NullSolver, Mesh, TopMaskName, &
8081
.FALSE., TopPerm, dummyint)
8082
CALL MakePermUsingMask( Model, NullSolver, Mesh, BottomMaskName, &
8083
.FALSE., BottomPerm, dummyint)
8084
8085
! first step is to isolate any unconnected elements
8086
! two sweep allocate then fill
8087
FNElm=0
8088
DO i=NBulk+1, NBulk + NBdry
8089
Element => Mesh % Elements(i)
8090
IF(Element % BoundaryInfo % Constraint /= FrontConstraint) CYCLE
8091
FNElm = FNElm + 1
8092
END DO
8093
8094
ALLOCATE(UsedElem(NBulk + NBdry), GroupCounts(10), ElNodes(3),&
8095
FoundNode(NNodes), IsNeighbour(10,ParEnv % PEs), &
8096
GroupElems(10, NBulk+NBdry), GDOFs(10), Order(10))
8097
UsedElem = .FALSE.
8098
FoundNode = .FALSE.
8099
GroupElems = .FALSE.
8100
IsNeighbour = .FALSE.
8101
GroupCounts = 0
8102
group = 1
8103
NNeighbours=0
8104
8105
NoNewNodes = .TRUE.
8106
DO WHILE(COUNT(UsedElem) < FNElm)
8107
IF(NoNewNodes) THEN
8108
NewGroup = .TRUE.
8109
Counter=0
8110
8111
!ensure arrays are large enough
8112
IF(SIZE(GroupCounts) < group) THEN
8113
CALL DoubleIntVectorSize(GroupCounts)
8114
CALL Double2DLogSize(IsNeighbour)
8115
CALL Double2DLogSize(GroupElems)
8116
END IF
8117
END IF
8118
NoNewNodes = .TRUE.
8119
DO i=NBulk+1, NBulk + NBdry
8120
IF(UsedElem(i)) CYCLE
8121
Element => Mesh % Elements(i)
8122
ElNodes = Element % NodeIndexes
8123
IF(Element % BoundaryInfo % Constraint /= FrontConstraint) CYCLE
8124
IF(ALL(.NOT. FoundNode(ElNodes)) .AND. .NOT. NewGroup) CYCLE
8125
NewGroup = .FALSE.
8126
UsedElem(i) = .TRUE.
8127
GroupElems(group,i) = .TRUE.
8128
FoundNode(ElNodes) = .TRUE.
8129
NoNewNodes = .FALSE.
8130
counter= counter + 1
8131
! do any nodes have neighbours?
8132
DO j=1, SIZE(ElNodes)
8133
IF(TopPerm(ElNodes(j)) /= 0) CYCLE
8134
IF(BottomPerm(ElNodes(j)) /= 0) CYCLE
8135
IF(LeftPerm(ElNodes(j)) /= 0) CYCLE
8136
IF(RightPerm(ElNodes(j)) /= 0) CYCLE
8137
Neighbours => Mesh % ParallelInfo % NeighbourList(ElNodes(j)) % Neighbours
8138
DO k=1, SIZE(Neighbours)
8139
IF(Neighbours(k) == ParEnv % MyPE) CYCLE
8140
IF(.NOT. IsNeighbour(group, Neighbours(k)+1)) THEN
8141
IsNeighbour(group, Neighbours(k)+1) = .TRUE.
8142
NNeighbours = NNeighbours + 1
8143
IF(SIZE(GDOFs) < NNeighbours) THEN
8144
CALL DoubleIntVectorSize(GDOFs)
8145
CALL DoubleIntVectorSize(Order)
8146
END IF
8147
GDOFs(NNeighbours) = Mesh % ParallelInfo % GlobalDOFs(ElNodes(j))
8148
Order(NNeighbours) = Neighbours(k)+1
8149
END IF
8150
END DO
8151
END DO
8152
8153
END DO
8154
IF(COUNT(UsedElem) == FNElm .OR. NoNewNodes) THEN
8155
DO i=NBulk+1, NBulk + NBdry
8156
IF(UsedElem(i)) CYCLE
8157
Element => Mesh % Elements(i)
8158
IF(Element % BoundaryInfo % Constraint /= FrontConstraint) CYCLE
8159
ElNodes = Element % NodeIndexes
8160
IF(ANY(.NOT. FoundNode(Elnodes))) CYCLE
8161
counter = counter+1
8162
FoundNode(ElNodes) = .TRUE.
8163
END DO
8164
GroupCounts(group) = counter
8165
group = group + 1
8166
END IF
8167
END DO
8168
8169
!overshoot by 1
8170
group = group-1
8171
8172
!order GDOFs into rank order
8173
ALLOCATE(WorkInt(NNeighbours))
8174
counter=0
8175
l=0
8176
DO i=1, group
8177
DO j=1, ParEnv % PEs
8178
IF(.NOT. IsNeighbour(i,j)) CYCLE
8179
counter=counter+1
8180
DO k=l+1, l+COUNT(IsNeighbour(i,:))
8181
IF(Order(k) == j) THEN
8182
WorkInt(counter) = GDOFs(k)
8183
EXIT
8184
END IF
8185
END DO
8186
END DO
8187
l=l+COUNT(IsNeighbour(i,:))
8188
END DO
8189
8190
DEALLOCATE(GDOFs)
8191
ALLOCATE(GDOFs(NNeighbours))
8192
GDOFs = WorkInt
8193
DEALLOCATE(WorkInt)
8194
8195
! gather number of groups on each proc
8196
ALLOCATE(PartGroups(ParEnv % PEs))
8197
CALL MPI_ALLGATHER(group, 1, MPI_INTEGER, &
8198
PartGroups, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
8199
ALLOCATE(PNNeighbours(ParEnv % PEs))
8200
CALL MPI_ALLGATHER(NNeighbours, 1, MPI_INTEGER, &
8201
PNNeighbours, 1, MPI_INTEGER, ELMER_COMM_WORLD, ierr)
8202
8203
NGroups = SUM(PartGroups)
8204
ALLOCATE(GroupToPart(NGroups))
8205
counter=0
8206
DO i=1, ParEnv % PEs
8207
DO j=1, PartGroups(i)
8208
counter=counter+1
8209
GroupToPart(counter) = i
8210
END DO
8211
END DO
8212
8213
! only ranks that have front elements beyond this
8214
IF(group /= 0) THEN
8215
ALLOCATE(PartGroupCounts(NGroups))
8216
counter=0
8217
DO i=1, ParEnv % PEs
8218
proc = i-1
8219
IF(proc==ParEnv % MyPE) THEN
8220
PartGroupCounts(counter+1:counter+group) = GroupCounts(:group)
8221
counter=counter+group
8222
CYCLE
8223
END IF
8224
IF(PartGroups(i) == 0) CYCLE
8225
CALL MPI_BSEND(GroupCounts(:group), group, MPI_INTEGER, &
8226
proc,9000, ELMER_COMM_WORLD, ierr )
8227
CALL MPI_RECV( PartGroupCounts(counter+1:counter+PartGroups(i)), PartGroups(i), MPI_INTEGER, &
8228
proc, 9000, ELMER_COMM_WORLD, status, ierr )
8229
counter = counter + PartGroups(i)
8230
END DO
8231
8232
ALLOCATE(PartGDOFs(SUM(PNNeighbours)))
8233
counter=0
8234
DO i=1, ParEnv % PEs
8235
proc = i-1
8236
IF(proc==ParEnv % MyPE) THEN
8237
PartGDOFs(counter+1:counter+NNeighbours) = GDOFs(:NNeighbours)
8238
counter=counter+NNeighbours
8239
CYCLE
8240
END IF
8241
IF(PartGroups(i) == 0) CYCLE
8242
CALL MPI_BSEND(GDOFs(:NNeighbours), NNeighbours, MPI_INTEGER, &
8243
proc,9001, ELMER_COMM_WORLD, ierr )
8244
CALL MPI_RECV( PartGDOFs(counter+1:counter+PNNeighbours(i)), PNNeighbours(i), MPI_INTEGER,&
8245
proc, 9001, ELMER_COMM_WORLD, status, ierr )
8246
counter = counter + PNNeighbours(i)
8247
END DO
8248
8249
ALLOCATE(PartNeighbours(NGroups, ParEnv % PEs))
8250
PartNeighbours = .FALSE.
8251
counter=1
8252
DO i=1, ParEnv % PEs
8253
proc = i-1
8254
IF(proc==ParEnv % MyPE) THEN
8255
DO j=1,group
8256
PartNeighbours(counter,:) = IsNeighbour(j,:)
8257
counter=counter+1
8258
END DO
8259
CYCLE
8260
END IF
8261
IF(PartGroups(i) == 0) CYCLE
8262
DO j=1, group
8263
CALL MPI_BSEND(IsNeighbour(j,:), ParEnv % PEs, MPI_LOGICAL, &
8264
proc,9100+j, ELMER_COMM_WORLD, ierr )
8265
END DO
8266
DO j=1, PartGroups(i)
8267
CALL MPI_RECV( PartNeighbours(counter,:), &
8268
ParEnv % PEs, MPI_LOGICAL, proc, 9100+j, ELMER_COMM_WORLD, status, ierr )
8269
counter = counter + 1
8270
END DO
8271
END DO
8272
8273
ALLOCATE(GroupNeighbours(group, NGroups))
8274
GroupNeighbours = .FALSE.
8275
DO i=1, group
8276
DO j=1, NGroups
8277
IF(j==1) THEN
8278
counter=0
8279
ELSE
8280
counter = SUM(PNNeighbours(1:GroupToPart(j)-1))
8281
IF(PartGroups(GroupToPart(j))>1) THEN
8282
DO k=1,j-1
8283
IF(GroupToPart(j) /= GroupToPart(k)) CYCLE
8284
counter = counter + COUNT(PartNeighbours(k,:))
8285
END DO
8286
END IF
8287
END IF
8288
IF(.NOT. IsNeighbour(i,GroupToPart(j))) CYCLE
8289
NeighbourList = PACK( (/ (k, k=1, ParEnv % PEs) /), PartNeighbours(j,:))
8290
DO k=1, SIZE(NeighbourList)
8291
counter=counter+1
8292
!check gdof present in group
8293
IF(NeighbourList(k)-1 /= ParEnv % MyPE) CYCLE
8294
DO l=NBulk+1, NBulk + NBdry
8295
IF(.NOT. GroupElems(i, l)) CYCLE
8296
Element => Mesh % Elements(l)
8297
ElNodes = Element % NodeIndexes
8298
IF(.NOT. ANY(Mesh % ParallelInfo % GlobalDOFs(ElNodes) == PartGDOFs(Counter))) CYCLE
8299
GroupNeighbours(i,j) = .TRUE.
8300
EXIT
8301
END DO
8302
END DO
8303
END DO
8304
END DO
8305
8306
DEALLOCATE(PartNeighbours)
8307
ALLOCATE(PartNeighbours(NGroups, NGroups))
8308
PartNeighbours = .FALSE.
8309
counter=1
8310
DO i=1, ParEnv % PEs
8311
proc = i-1
8312
IF(proc==ParEnv % MyPE) THEN
8313
DO j=1,group
8314
PartNeighbours(counter,:) = GroupNeighbours(j,:)
8315
counter=counter+1
8316
END DO
8317
CYCLE
8318
END IF
8319
IF(PartGroups(i) == 0) CYCLE
8320
DO j=1, group
8321
CALL MPI_BSEND(GroupNeighbours(j,:), NGroups, MPI_LOGICAL, &
8322
proc,9200+j, ELMER_COMM_WORLD, ierr )
8323
END DO
8324
DO j=1, PartGroups(i)
8325
CALL MPI_RECV( PartNeighbours(counter,:), &
8326
NGroups, MPI_LOGICAL, proc, 9200+j, ELMER_COMM_WORLD, status, ierr )
8327
counter = counter + 1
8328
END DO
8329
END DO
8330
8331
ALLOCATE(TotalGroupCounts(NGroups), Grouper(group, NGroups))
8332
TotalGroupCounts = PartGroupCounts
8333
counter=0
8334
Grouper = .FALSE.
8335
DO i=1, NGroups
8336
IF(GroupToPart(i) == ParEnv % MyPE + 1) THEN
8337
DO j=1,group
8338
Grouper(j,i+j-1) = .TRUE.
8339
END DO
8340
EXIT
8341
END IF
8342
END DO
8343
DO i=1, group
8344
NoNewParts = .FALSE.
8345
DO WHILE(.NOT. NoNewParts)
8346
NoNewParts = .TRUE.
8347
DO j=1, NGroups
8348
IF(.NOT. Grouper(i,j)) CYCLE
8349
DO k=1, NGroups
8350
IF(.NOT. PartNeighbours(j,k)) CYCLE
8351
IF(.NOT. Grouper(i,k)) NoNewParts = .FALSE.
8352
Grouper(i,k) = .TRUE.
8353
END DO
8354
END DO
8355
END DO
8356
END DO
8357
8358
ALLOCATE(PartGrouper(NGroups, NGroups))
8359
counter=1
8360
DO i=1, ParEnv % PEs
8361
proc = i-1
8362
IF(proc==ParEnv % MyPE) THEN
8363
DO j=1, group
8364
PartGrouper(counter,:) = Grouper(j,:)
8365
counter=counter+1
8366
END DO
8367
CYCLE
8368
END IF
8369
IF(PartGroups(i) == 0) CYCLE
8370
DO j=1, group
8371
CALL MPI_BSEND(Grouper(j,:), NGroups, MPI_LOGICAL, &
8372
proc,9300+j, ELMER_COMM_WORLD, ierr )
8373
END DO
8374
DO j=1, PartGroups(i)
8375
CALL MPI_RECV( PartGrouper(counter,:), &
8376
NGroups, MPI_LOGICAL, proc, 9300+j, ELMER_COMM_WORLD, status, ierr )
8377
counter=counter+1
8378
END DO
8379
END DO
8380
8381
DO i=1, NGroups
8382
TotalGroupCounts(i) = SUM(PartGroupCounts, PartGrouper(i,:))
8383
END DO
8384
8385
!find lateral margin tag
8386
ALLOCATE(GroupConstraint(group))
8387
GroupConstraint=0
8388
DO i=1, group
8389
FoundNode = .FALSE.
8390
DO j=NBulk+1, NBulk+Nbdry
8391
IF(.NOT. GroupElems(i,j)) CYCLE
8392
Element => Mesh % Elements(j)
8393
ElNodes = Element % NodeIndexes
8394
FoundNode(ElNodes) = .TRUE.
8395
END DO
8396
!check if any node indexes are also on lateral boundaries
8397
DO j=1, NNodes
8398
IF(.NOT. FoundNode(j)) CYCLE
8399
IF(RightPerm(j) /= 0) THEN
8400
GroupConstraint(i) = RightConstraint
8401
EXIT
8402
END IF
8403
IF(LeftPerm(j) /= 0) THEN
8404
GroupConstraint(i) = LeftConstraint
8405
EXIT
8406
END IF
8407
END DO
8408
END DO
8409
8410
ALLOCATE(PartConstraint(NGroups))
8411
counter=1
8412
DO i=1, ParEnv % PEs
8413
proc = i-1
8414
IF(proc==ParEnv % MyPE) THEN
8415
DO j=1, group
8416
PartConstraint(counter) = GroupConstraint(j)
8417
counter=counter+1
8418
END DO
8419
CYCLE
8420
END IF
8421
IF(PartGroups(i) == 0) CYCLE
8422
DO j=1, group
8423
CALL MPI_BSEND(GroupConstraint(j), 1, MPI_LOGICAL, &
8424
proc,9400+j, ELMER_COMM_WORLD, ierr )
8425
END DO
8426
DO j=1, PartGroups(i)
8427
CALL MPI_RECV( PartConstraint(counter), &
8428
1, MPI_LOGICAL, proc, 9400+j, ELMER_COMM_WORLD, status, ierr )
8429
counter=counter+1
8430
END DO
8431
END DO
8432
8433
ALLOCATE(ElemConstraint(NBdry+NBulk))
8434
ElemConstraint=0
8435
DO i=1, group
8436
IF(GroupConstraint(i) == 0) THEN
8437
DO j=1, NGroups
8438
IF(.NOT. Grouper(i,j)) CYCLE
8439
IF(PartConstraint(j) == 0) CYCLE
8440
IF(MAXVAL(TotalGroupCounts) == TotalGroupCounts(j)) CYCLE
8441
GroupConstraint(i) = PartConstraint(j)
8442
END DO
8443
END IF
8444
8445
DO j=1, NGroups
8446
IF(GroupToPart(j)-1 /= ParEnv % MyPE) CYCLE
8447
IF(MAXVAL(TotalGroupCounts) == TotalGroupCounts(j)) CYCLE
8448
DO k=NBulk+1, NBulk + NBdry
8449
IF(GroupElems(i,k)) ElemConstraint(k) = GroupConstraint(i)
8450
END DO
8451
END DO
8452
8453
END DO
8454
8455
ELSE
8456
ALLOCATE(ElemConstraint(Nbdry+ NBulk))
8457
ElemConstraint = 0
8458
END IF
8459
8460
DEALLOCATE(LeftPerm, RightPerm, TopPerm, BottomPerm)
8461
8462
CALL MPI_BARRIER(ELMER_COMM_WORLD, ierr)
8463
8464
END SUBROUTINE CheckFrontBoundary
8465
8466
! subroutine to ceck for inverted based elements due to lagrangian movement
8467
SUBROUTINE CheckBaseFreeSurface(Model, Mesh, Buffer)
8468
TYPE(Model_t) :: Model
8469
TYPE(Mesh_t), POINTER :: Mesh
8470
REAL(KIND=dp), OPTIONAL :: Buffer
8471
!-------------------------
8472
TYPE(Solver_t), POINTER :: NullSolver=>NULL()
8473
TYPE(Element_t), POINTER :: Element
8474
TYPE(Nodes_t) :: Nodes
8475
INTEGER, POINTER :: NodeIndexes(:),BottomPerm(:)=>NULL(),FrontPerm(:)=>NULL(),&
8476
LeftPerm(:)=>NULL(),RightPerm(:)=>NULL()
8477
INTEGER :: i,j,n,k, counter,BaseBCtag,FrontBCtag,LeftBCtag,RightBCtag,dummyint
8478
REAL(KIND=dp) :: Normal(3), NBuffer
8479
LOGICAL :: Found, ThisBC
8480
CHARACTER(MAX_NAME_LEN) :: SolverName, BottomMaskName, FrontMaskName,&
8481
LeftMaskName, RightMaskName, Message
8482
SolverName="CheckBaseFreeSurface"
8483
8484
IF(.NOT. PRESENT(Buffer)) THEN
8485
NBuffer = -0.01_dp
8486
ELSE
8487
NBuffer = -Buffer
8488
END IF
8489
8490
FrontMaskName = "Calving Front Mask"
8491
BottomMaskName = "Bottom Surface Mask"
8492
CALL MakePermUsingMask( Model, NullSolver, Mesh, BottomMaskName, &
8493
.FALSE., BottomPerm, dummyint)
8494
CALL MakePermUsingMask( Model, NullSolver, Mesh, FrontMaskName, &
8495
.FALSE., FrontPerm, dummyint)
8496
LeftMaskName = "Left Sidewall Mask"
8497
RightMaskName = "Right Sidewall Mask"
8498
!Generate perms to quickly get nodes on each boundary
8499
CALL MakePermUsingMask( Model, NullSolver, Mesh, LeftMaskName, &
8500
.FALSE., LeftPerm, dummyint)
8501
CALL MakePermUsingMask( Model, NullSolver, Mesh, RightMaskName, &
8502
.FALSE., RightPerm, dummyint)
8503
8504
DO i=1,Model % NumberOfBCs
8505
ThisBC = ListGetLogical(Model % BCs(i) % Values,BottomMaskName,Found)
8506
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
8507
BaseBCtag = Model % BCs(i) % Tag
8508
EXIT
8509
END DO
8510
8511
DO i=1,Model % NumberOfBCs
8512
ThisBC = ListGetLogical(Model % BCs(i) % Values,FrontMaskName,Found)
8513
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
8514
FrontBCtag = Model % BCs(i) % Tag
8515
EXIT
8516
END DO
8517
8518
DO i=1,Model % NumberOfBCs
8519
ThisBC = ListGetLogical(Model % BCs(i) % Values,LeftMaskName,Found)
8520
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
8521
LeftBCtag = Model % BCs(i) % Tag
8522
EXIT
8523
END DO
8524
8525
DO i=1,Model % NumberOfBCs
8526
ThisBC = ListGetLogical(Model % BCs(i) % Values,RightMaskName,Found)
8527
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
8528
RightBCtag = Model % BCs(i) % Tag
8529
EXIT
8530
END DO
8531
8532
!check element how much this deforms elements near front
8533
!if the element is above a 45 degree vertical angle from xy plane change to front boundary
8534
DO i=Mesh % NumberOfBulkElements +1, &
8535
Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
8536
8537
Element => Mesh % Elements(i)
8538
IF(Element % BoundaryInfo % Constraint /= BaseBCtag) CYCLE
8539
n = Element % TYPE % NumberOfNodes
8540
8541
NodeIndexes => Element % NodeIndexes
8542
8543
ALLOCATE(Nodes % x(n), Nodes % y(n), Nodes % z(n))
8544
8545
Nodes % x = Mesh % Nodes % x(NodeIndexes)
8546
Nodes % y = Mesh % Nodes % y(NodeIndexes)
8547
Nodes % z = Mesh % Nodes % z(NodeIndexes)
8548
8549
Normal = NormalVector(Element, Nodes)
8550
8551
IF(Normal(3) > NBuffer) THEN
8552
8553
PRINT*, SolverName,' Inverted base element:',i, 'on part:', ParEnv % MyPE, &
8554
'moving to...'
8555
8556
counter=0
8557
DO k=1,n
8558
IF(LeftPerm(NodeIndexes(k)) > 0) counter = counter+1
8559
END DO
8560
IF(Counter >= 2) THEN
8561
PRINT*, SolverName, ' Left boundary', ParEnv % MyPE
8562
Element % BoundaryInfo % Constraint = LeftBCtag
8563
CYCLE
8564
END IF
8565
8566
counter=0
8567
DO k=1,n
8568
IF(RightPerm(NodeIndexes(k)) > 0) counter = counter+1
8569
END DO
8570
IF(Counter >= 2) THEN
8571
PRINT*, SolverName, ' Right boundary', ParEnv % MyPE
8572
Element % BoundaryInfo % Constraint = RightBCtag
8573
CYCLE
8574
END IF
8575
8576
counter=0
8577
DO k=1,n
8578
IF(FrontPerm(NodeIndexes(k)) > 0) counter = counter+1
8579
END DO
8580
IF(Counter >= 2) THEN
8581
PRINT*, SolverName, ' Front boundary', ParEnv % MyPE
8582
Element % BoundaryInfo % Constraint = FrontBCtag
8583
CYCLE
8584
END IF
8585
8586
IF(Element % BoundaryInfo % Constraint == BaseBCtag) &
8587
CALL WARN(SolverName, 'Inverted base element not on edge so &
8588
cannot tranfer to other boundary')
8589
END IF
8590
8591
DEALLOCATE(Nodes % x, Nodes % y, Nodes % z)
8592
8593
END DO
8594
DEALLOCATE(FrontPerm,BottomPerm,LeftPerm,RightPerm)
8595
8596
END SUBROUTINE CheckBaseFreeSurface
8597
8598
! only serial. Will need to write parallel routine but will only be needed will parallel]
8599
! remeshing properly ingrated into calving routines
8600
SUBROUTINE SaveTerminusPosition(Model, Solver, Mesh, Boss)
8601
8602
IMPLICIT NONE
8603
!------------------------------------------------------------------------------
8604
TYPE(Solver_t) :: Solver
8605
TYPE(Model_t) :: Model
8606
TYPE(Mesh_t) :: Mesh
8607
LOGICAL :: Boss
8608
!------------------------------------------------------------------------------
8609
TYPE(Solver_t), POINTER :: AdvSolver
8610
TYPE(Valuelist_t), POINTER :: SolverParams, AdvParams
8611
INTEGER, POINTER :: TopPerm(:)=>NULL(), FrontPerm(:)=>NULL(), &
8612
LeftPerm(:)=>NULL(), RightPerm(:)=>NULL(), SidePerm(:)=> NULL(),&
8613
NodeIndexes(:)
8614
LOGICAL :: FileCreated = .FALSE.,Found,FoundRight,FoundLeft,FirstTime,reducecorners(2),&
8615
ThisBC
8616
INTEGER :: i,j,k, NNodes, NBulk, NBdry, RCounter, LCounter,dummyint,&
8617
Nl,Nr, Naux, ok, Nrail, Counter,FrontBCtag,side,LastNode,CornersTotal
8618
REAL(KIND=dp) :: buffer, xx, yy, mindist, tempdist
8619
REAL(kind=dp), ALLOCATABLE :: xL(:),yL(:),xR(:),yR(:), xRail(:), yRail(:),&
8620
PAllCorners(:), MinDists(:)
8621
INTEGER, ALLOCATABLE :: FrontRight(:), FrontLeft(:), NodeList(:), jmin(:), Corner(:),&
8622
AllCorners(:)
8623
LOGICAL, ALLOCATABLE :: GotNode(:), InFront(:)
8624
CHARACTER(LEN=MAX_NAME_LEN) :: Filename, SolverName, LeftRailFName, RightRailFName,&
8625
Adv_EqName
8626
INTEGER, PARAMETER :: io=20
8627
8628
SAVE :: FileCreated
8629
8630
SolverName ="SaveTerminusPosition"
8631
SolverParams => Solver % Values
8632
8633
NBulk = Mesh % NumberOfBulkElements
8634
NBdry = Mesh % NumberOfBoundaryElements
8635
NNodes = Mesh % NumberOfNodes
8636
8637
CALL MakePermUsingMask( Model, Solver, Mesh, "Calving Front Mask", &
8638
.FALSE., FrontPerm, dummyint)
8639
CALL MakePermUsingMask( Model, Solver, Mesh, "Top Surface Mask", &
8640
.FALSE., TopPerm, dummyint)
8641
CALL MakePermUsingMask( Model, Solver, Mesh, "Left Sidewall Mask", &
8642
.FALSE., LeftPerm, dummyint)
8643
CALL MakePermUsingMask( Model, Solver, Mesh, "Right Sidewall Mask", &
8644
.FALSE., RightPerm, dummyint)
8645
8646
IF(Boss) THEN
8647
FoundLeft=.FALSE.
8648
FoundRight=.FALSE.
8649
RCounter= 0; LCounter=0
8650
ALLOCATE(FrontRight(100), FrontLeft(100))
8651
DO i=1,NNodes
8652
IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN
8653
IF( LeftPerm(i) >0 ) THEN
8654
LCounter = LCounter + 1
8655
FrontLeft(LCounter) = i
8656
FoundLeft = .TRUE.
8657
ELSE IF ( RightPerm(i) >0 ) THEN
8658
RCounter = RCounter + 1
8659
FrontRight(RCounter) = i
8660
FoundRight = .TRUE.
8661
END IF
8662
END IF
8663
END DO
8664
8665
IF(.NOT. FoundLeft .OR. .NOT. FoundRight) CALL FATAL(SolverName, 'Unable to find terminus corners')
8666
8667
reducecorners = .FALSE.
8668
IF(LCounter > 1) reducecorners(1)=.TRUE.
8669
IF(RCounter > 1) reducecorners(2)=.TRUE.
8670
8671
IF(ANY(reducecorners)) THEN
8672
8673
Adv_EqName = ListGetString(SolverParams,"Front Advance Solver", DefValue="Front Advance")
8674
! Locate CalvingAdvance Solver
8675
Found = .FALSE.
8676
DO i=1,Model % NumberOfSolvers
8677
IF(GetString(Model % Solvers(i) % Values, 'Equation') == Adv_EqName) THEN
8678
AdvSolver => Model % Solvers(i)
8679
Found = .TRUE.
8680
EXIT
8681
END IF
8682
END DO
8683
IF(.NOT. Found) CALL FATAL(SolverName, "Advance Solver Equation not given")
8684
AdvParams => AdvSolver % Values
8685
8686
buffer = ListGetConstReal(AdvParams, "Rail Buffer", Found, DefValue=0.1_dp)
8687
IF(.NOT. Found) CALL Info(SolverName, "No Rail Buffer set using default 0.1")
8688
8689
LeftRailFName = ListGetString(AdvParams, "Left Rail File Name", Found)
8690
IF(.NOT. Found) THEN
8691
CALL Info(SolverName, "Left Rail File Name not found, assuming './LeftRail.xy'")
8692
LeftRailFName = "LeftRail.xy"
8693
END IF
8694
Nl = ListGetInteger(AdvParams, "Left Rail Number Nodes", Found)
8695
IF(.NOT.Found) THEN
8696
WRITE(Message,'(A,A)') 'Left Rail Number Nodes not found'
8697
CALL FATAL(SolverName, Message)
8698
END IF
8699
!TO DO only do these things if firsttime=true?
8700
OPEN(unit = io, file = TRIM(LeftRailFName), status = 'old',iostat = ok)
8701
IF (ok /= 0) THEN
8702
WRITE(message,'(A,A)') 'Unable to open file ',TRIM(LeftRailFName)
8703
CALL FATAL(Trim(SolverName),Trim(message))
8704
END IF
8705
ALLOCATE(xL(Nl), yL(Nl))
8706
8707
! read data
8708
DO i = 1, Nl
8709
READ(io,*,iostat = ok, end=200) xL(i), yL(i)
8710
END DO
8711
200 Naux = Nl - i
8712
IF (Naux > 0) THEN
8713
WRITE(Message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(LeftRailFName)
8714
CALL INFO(Trim(SolverName),Trim(message))
8715
END IF
8716
CLOSE(io)
8717
RightRailFName = ListGetString(AdvParams, "Right Rail File Name", Found)
8718
IF(.NOT. Found) THEN
8719
CALL Info(SolverName, "Right Rail File Name not found, assuming './RightRail.xy'")
8720
RightRailFName = "RightRail.xy"
8721
END IF
8722
8723
Nr = ListGetInteger(AdvParams, "Right Rail Number Nodes", Found)
8724
IF(.NOT.Found) THEN
8725
WRITE(Message,'(A,A)') 'Right Rail Number Nodes not found'
8726
CALL FATAL(SolverName, Message)
8727
END IF
8728
!TO DO only do these things if firsttime=true?
8729
OPEN(unit = io, file = TRIM(RightRailFName), status = 'old',iostat = ok)
8730
8731
IF (ok /= 0) THEN
8732
WRITE(Message,'(A,A)') 'Unable to open file ',TRIM(RightRailFName)
8733
CALL FATAL(Trim(SolverName),Trim(message))
8734
END IF
8735
ALLOCATE(xR(Nr), yR(Nr))
8736
8737
! read data
8738
DO i = 1, Nr
8739
READ(io,*,iostat = ok, end=100) xR(i), yR(i)
8740
END DO
8741
100 Naux = Nr - i
8742
IF (Naux > 0) THEN
8743
WRITE(message,'(I0,A,I0,A,A)') Naux,' out of ',Nl,' datasets in file ', TRIM(RightRailFName)
8744
CALL INFO(Trim(SolverName),Trim(message))
8745
END IF
8746
CLOSE(io)
8747
END IF
8748
8749
DO side=1,2 ! left 1, right 2
8750
8751
IF(.NOT. reducecorners(side)) CYCLE
8752
8753
IF (side==1) THEN
8754
Nrail= Nl
8755
ALLOCATE(xRail(Nrail), yRail(Nrail))
8756
xRail = xL
8757
yRail = yL
8758
SidePerm => LeftPerm
8759
CornersTotal = LCounter
8760
ELSE
8761
Nrail= Nr
8762
ALLOCATE(xRail(Nrail), yRail(Nrail))
8763
xRail = xR
8764
yRail = yR ! TO DO use pointers instead?
8765
SidePerm => RightPerm
8766
CornersTotal = RCounter
8767
END IF
8768
8769
ALLOCATE(AllCorners(CornersTotal))
8770
Counter = 0
8771
DO i=1,NNodes
8772
IF( (TopPerm(i) >0 ) .AND. (FrontPerm(i) >0 )) THEN
8773
IF ( SidePerm(i) >0 ) THEN
8774
Counter = Counter + 1
8775
AllCorners(Counter) = i
8776
END IF
8777
END IF
8778
END DO
8779
8780
ALLOCATE(jmin(CornersTotal),InFront(CornersTotal),MinDists(CornersTotal))
8781
DO i=1, CornersTotal
8782
8783
xx = Mesh % Nodes % x(AllCorners(i))
8784
yy = Mesh % Nodes % y(AllCorners(i))
8785
8786
MinDist=(xRail(1)-xRail(Nrail))**2.+(yRail(1)-yRail(Nrail))**2.
8787
! MinDist is actually maximum distance, needed for finding closest rail node
8788
DO j=1,Nrail ! Find closest point on rail
8789
TempDist=(xRail(j)-xx)**2.+(yRail(j)-yy)**2.
8790
IF(TempDist < MinDist) THEN
8791
MinDist=TempDist
8792
jmin(i)=j
8793
END IF
8794
END DO
8795
MinDists(i) = MinDist
8796
!check if in front or behind node
8797
InFront(i) = .TRUE.
8798
IF(jmin(i) == Nrail) InFront(i) = .FALSE.
8799
IF(jmin(i) > 1 .AND. jmin(i) /= Nrail) THEN
8800
MinDist = PointLineSegmDist2D((/xRail(jmin(i)),yRail(jmin(i))/), &
8801
(/xRail(jmin(i)+1),yRail(jmin(i)+1)/),(/xx,yy/))
8802
TempDist = PointLineSegmDist2D((/xRail(jmin(i)),yRail(jmin(i))/), &
8803
(/xRail(jmin(i)-1),yRail(jmin(i)-1)/),(/xx,yy/))
8804
IF(MinDist > TempDist) InFront(i) = .FALSE.
8805
END IF
8806
END DO
8807
8808
IF(COUNT(jmin == MAXVAL(jmin)) == 1) THEN
8809
Corner = MAXLOC(jmin)
8810
ELSE IF(COUNT(jmin == MAXVAL(jmin) .AND. InFront) == 1) THEN
8811
Corner = PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin) .AND. InFront)
8812
ELSE IF(ALL(InFront(PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin))))) THEN
8813
ALLOCATE(Corner(1))
8814
MinDist = HUGE(1.0_dp)
8815
DO i=1, CornersTotal
8816
IF(jmin(i) /= MAXVAL(jmin)) CYCLE
8817
IF(.NOT. InFront(i)) CYCLE
8818
IF(MinDists(i) < mindist) THEN
8819
mindist = MinDists(i)
8820
Corner(1) = i
8821
END IF
8822
END DO
8823
ELSE IF(ALL(.NOT. InFront(PACK((/ (k, k=1, CornersTotal) /),jmin == MAXVAL(jmin))))) THEN
8824
ALLOCATE(Corner(1))
8825
MinDist = HUGE(1.0_dp)
8826
DO i=1, CornersTotal
8827
IF(jmin(i) /= MAXVAL(jmin)) CYCLE
8828
IF(MinDists(i) < mindist) THEN
8829
mindist = MinDists(i)
8830
Corner(1) = i
8831
END IF
8832
END DO
8833
ELSE
8834
CALL FATAL(SolverName, 'Problem reducing corners')
8835
END IF
8836
8837
IF(side == 1) THEN
8838
FrontLeft(1) = AllCorners(Corner(1))
8839
ELSE
8840
FrontRight(1) = AllCorners(Corner(1))
8841
END IF
8842
8843
DEALLOCATE(xRail, yRail, AllCorners, jmin, InFront, MinDists, Corner)
8844
END DO
8845
8846
DO i=1,Model % NumberOfBCs
8847
ThisBC = ListGetLogical(Model % BCs(i) % Values,"Calving Front Mask",Found)
8848
IF((.NOT. Found) .OR. (.NOT. ThisBC)) CYCLE
8849
FrontBCtag = Model % BCs(i) % Tag
8850
EXIT
8851
END DO
8852
8853
ALLOCATE(GotNode(NNodes), NodeList(NNodes))
8854
FirstTime=.TRUE.
8855
GotNode = .FALSE.
8856
counter = 0
8857
LastNode = 0
8858
DO WHILE(LastNode /= FrontRight(1))
8859
Found = .FALSE.
8860
IF(FirstTime) THEN
8861
LastNode = FrontLeft(1)
8862
GotNode(FrontLeft(1)) = .TRUE.
8863
NodeList(1) = LastNode
8864
counter = counter + 1
8865
END IF
8866
DO i= NBulk+1, NBulk+NBdry
8867
IF(Mesh % Elements(i) % BoundaryInfo % constraint /= FrontBCtag) CYCLE
8868
NodeIndexes => Mesh % Elements(i) % NodeIndexes
8869
IF(.NOT. ANY(NodeIndexes == LastNode)) CYCLE
8870
DO j=1,Mesh % Elements(i) % TYPE % NumberOfNodes
8871
IF(GotNode(NodeIndexes(j))) CYCLE
8872
IF(TopPerm(NodeIndexes(j)) > 0) THEN
8873
LastNode = NodeIndexes(j)
8874
Found = .TRUE.
8875
GotNode(LastNode) = .TRUE.
8876
counter = counter + 1
8877
NodeList(counter) = LastNode
8878
EXIT
8879
END IF
8880
END DO
8881
IF(Found) EXIT
8882
END DO
8883
IF(.NOT. Found) THEN
8884
CALL WARN(SolverName, 'Unable to get terminus loop for this timestep')
8885
EXIT
8886
END IF
8887
IF(ANY(FrontLeft(1:LCounter) == LastNode)) THEN
8888
! reset to first node as gone wrong way
8889
! GotNode should prvent us doing this again
8890
PRINT*, 'gone wrong way...'
8891
LastNode = NodeList(1)
8892
counter = 1
8893
END IF
8894
FirstTime=.FALSE.
8895
END DO
8896
8897
IF(LastNode == FrontRight(1)) THEN ! loop success
8898
Filename = ListGetString(SolverParams,"Output Terminus File Name", Found)
8899
IF(.NOT. Found) THEN
8900
CALL WARN(SolverName, 'Output file name not given so using TerminusPosition.txt')
8901
Filename = "TerminusPosition.txt"
8902
END IF
8903
8904
! write to file
8905
IF(FileCreated) THEN
8906
OPEN( 37, FILE=filename, STATUS='UNKNOWN', POSITION='APPEND')
8907
ELSE
8908
OPEN( 37, FILE=filename, STATUS='UNKNOWN')
8909
WRITE(37, '(A)') "Terminus Position File"
8910
WRITE(37, '(A)') "TimeStep, Time, NumberOfNodes"
8911
WRITE(37, '(A)') "xx, yy"
8912
END IF
8913
8914
!Write out the left and rightmost points
8915
WRITE(37, *) 'NewTime:', GetTimestep(), GetTime(), counter
8916
DO i=1, counter
8917
WRITE(37, *) Mesh % Nodes % x(NodeList(i)), Mesh % Nodes % y(NodeList(i))
8918
END DO
8919
8920
CLOSE(37)
8921
END IF
8922
END IF
8923
8924
FileCreated = .TRUE.
8925
DEALLOCATE(FrontPerm,TopPerm,LeftPerm,RightPerm)
8926
8927
END SUBROUTINE SaveTerminusPosition
8928
8929
END MODULE CalvingGeometry
8930
8931
8932