• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trixi-framework / HOHQMesh / 30318764346

28 Jul 2026 12:55AM UTC coverage: 76.972% (+1.1%) from 75.909%
30318764346

Pull #166

github

web-flow
Merge 3346edfd3 into 955f92dca
Pull Request #166: Add L2/H1 boundary optimization

1631 of 1964 new or added lines in 40 files covered. (83.04%)

48 existing lines in 4 files now uncovered.

9563 of 12424 relevant lines covered (76.97%)

634449.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.76
/Source/Mesh/MeshBoundaryMethods.f90
1
! MIT License
2
!
3
! Copyright (c) 2010-present David A. Kopriva and other contributors: AUTHORS.md
4
!
5
! Permission is hereby granted, free of charge, to any person obtaining a copy
6
! of this software and associated documentation files (the "Software"), to deal
7
! in the Software without restriction, including without limitation the rights
8
! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
! copies of the Software, and to permit persons to whom the Software is
10
! furnished to do so, subject to the following conditions:
11
!
12
! The above copyright notice and this permission notice shall be included in all
13
! copies or substantial portions of the Software.
14
!
15
! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
! SOFTWARE.
22
!
23
! --- End License
24
!
25
!////////////////////////////////////////////////////////////////////////
26
!
27
!      MeshBoundaryMethods.f90
28
!      Created: August 26, 2013 6:32 PM
29
!      By: David Kopriva
30
!
31
!////////////////////////////////////////////////////////////////////////
32
!
33
   Module MeshBoundaryMethodsModule
34
   USE MeshSizerClass
35
   USE SMMeshClass
36
   USE ConnectionsModule
37
   IMPLICIT NONE
38
!
39
!-------------------------------------------------------------------
40
! Contains the operations on preparing the boundary edges for
41
! projection onto the model boundaries
42
!-------------------------------------------------------------------
43
!
44
      INTEGER, PARAMETER, PRIVATE :: TEST_NESTING = 1, TEST_INTERSECTION = 2
45
!
46
!     ========
47
      CONTAINS
48
!     ========
49
!
50
!
51
!////////////////////////////////////////////////////////////////////////
52
!
53
      SUBROUTINE generateTemporaryBoundaryArrays( sizer )
48✔
54
!
55
!     --------------------------------------------------------------------
56
!     The winding number calculation requires an array of points
57
!     consecutively ordered. So convert the sizer's discrete curves, which
58
!     have duplicates at the ends of the component curves to
59
!     arrays with no duplicates
60
!     --------------------------------------------------------------------
61
!
62
         USE CurveConversionsModule
63
         IMPLICIT NONE
64
!
65
!        ---------
66
!        Arguments
67
!        ---------
68
!
69
         TYPE(MeshSizer) :: sizer
70
!
71
!        ---------------
72
!        Local Variables
73
!        ---------------
74
!
75
         CLASS(FTObject) , POINTER             :: obj => NULL()
76
         INTEGER                               :: N, k
77
         TYPE  (FTLinkedListIterator), POINTER :: iterator => NULL()
78
         CLASS(ChainedSegmentedCurve), POINTER :: chain => NULL()
79
!
80
!        --------------
81
!        Outer boundary
82
!        --------------
83
!
84
         IF( ASSOCIATED( sizer % outerBoundary ) )     THEN
48✔
85
            outerBoundaryCurve => allocateAndInitSegmentedCurveFromSegmentedChain(sizer % outerBoundary)
40✔
86
         END IF
87
!
88
!        ----------------------------
89
!        Inner boundaries conversions
90
!        ----------------------------
91
!
92
         IF( ASSOCIATED(sizer % innerBoundariesList) )     THEN
48✔
93
            N = sizer % noOfInnerBoundaries
20✔
94
            ALLOCATE( interiorCurves(N) )
86✔
95

96
            ALLOCATE(iterator)
20✔
97
            CALL iterator % initwithFTLinkedListClass( sizer % innerBoundariesList)
20✔
98
            CALL iterator % setToStart()
20✔
99
            k = 0
20✔
100
            DO WHILE( .NOT.iterator % isAtEnd() )
86✔
101
               k = k+1
66✔
102
               obj => iterator % object()
66✔
103
               CALL castToChainedSegmentedCurve(obj,chain)
66✔
104
               interiorCurves(k) % curveArray => allocateAndInitSegmentedCurveFromSegmentedChain(chain)
66✔
105
               CALL iterator % moveToNext()
66✔
106
            END DO
107
            CALL releaseFTLinkedListIterator(iterator)
20✔
108
         END IF
109
!
110
!        --------------------------------
111
!        Interface boundaries conversions
112
!        --------------------------------
113
!
114
         IF( ASSOCIATED(sizer % interfaceBoundariesList) )     THEN
48✔
115
            N = sizer % noOfInterfaceBoundaries
2✔
116
            ALLOCATE( interfaceCurves(N) )
6✔
117

118
            ALLOCATE(iterator)
2✔
119
            CALL iterator % initwithFTLinkedListClass( sizer % interfaceBoundariesList)
2✔
120
            CALL iterator % setToStart()
2✔
121
            k = 0
2✔
122
            DO WHILE( .NOT.iterator % isAtEnd() )
6✔
123
               k = k+1
4✔
124
               obj => iterator % object()
4✔
125
               CALL castToChainedSegmentedCurve(obj,chain)
4✔
126
               interfaceCurves(k) % curveArray => allocateAndInitSegmentedCurveFromSegmentedChain(chain)
4✔
127
               CALL iterator % moveToNext()
4✔
128
            END DO
129
            CALL releaseFTLinkedListIterator(iterator)
2✔
130
         END IF
131

132
      END SUBROUTINE generateTemporaryBoundaryArrays
48✔
133
!
134
!////////////////////////////////////////////////////////////////////////
135
!
136
      SUBROUTINE destroyTemporaryBoundaryArrays
48✔
137
         IMPLICIT NONE
138
         INTEGER                  :: k
139
         CLASS(FTObject), POINTER :: obj
140

141
         IF( ASSOCIATED( outerBoundaryCurve ) ) THEN
48✔
142
            obj => outerBoundaryCurve
40✔
143
            CALL release(obj)
40✔
144
         END IF
145

146
         IF( ASSOCIATED(interiorCurves) )     THEN
48✔
147
            DO k = 1, SIZE(interiorCurves)
86✔
148
               obj => interiorCurves(k) % curveArray
66✔
149
               CALL release(obj)
86✔
150
            END DO
151
         END IF
152

153
         IF( ASSOCIATED(interfaceCurves) )     THEN
48✔
154
            DO k = 1, SIZE(interfaceCurves)
6✔
155
               obj => interfaceCurves(k) % curveArray
4✔
156
               CALL release(obj)
6✔
157
            END DO
158
         END IF
159

160
         NULLIFY( outerBoundaryCurve )
48✔
161
         NULLIFY( interiorCurves )
48✔
162
         NULLIFY( interfaceCurves )
48✔
163
     END SUBROUTINE destroyTemporaryBoundaryArrays
48✔
164
!@mark -
165
!
166
!////////////////////////////////////////////////////////////////////////
167
!
168
      SUBROUTINE AllocateBoundaryEdgesArray(numBoundaries)
85✔
169
      IMPLICIT NONE
170
!
171
!     ---------
172
!     Arguments
173
!     ---------
174
!
175
      INTEGER :: numBoundaries
176
!
177
!     ---------------
178
!     Local variables
179
!     ---------------
180
!
181
      TYPE (FTLinkedList), POINTER :: list => NULL()
182
      CLASS(FTobject)    , POINTER :: obj  => NULL()
183
      INTEGER                      :: k
184
!
185
!     -------------------------------
186
!     Clear out old values if present
187
!     -------------------------------
188
!
189
      IF ( ASSOCIATED(boundaryEdgesArray) )     THEN
85✔
190
         CALL releaseFTMutableObjectArray(self = boundaryEdgesArray)
22✔
191
      END IF
192
      IF ( ALLOCATED(boundaryEdgesType) )     THEN
85✔
193
            DEALLOCATE( boundaryEdgesType )
22✔
194
      END IF
195
!
196
!     -------------------
197
!     Allocate new memory
198
!     -------------------
199
!
200
      ALLOCATE( boundaryEdgesArray )
85✔
201
      CALL boundaryEdgesArray % initwithSize(numBoundaries)
85✔
202
      ALLOCATE(boundaryEdgesType(numBoundaries))
85✔
203
!
204
!     -------------------------
205
!     Initialize the edge lists
206
!     -------------------------
207
!
208
      DO k = 1, numBoundaries
306✔
209
         ALLOCATE(list)
221✔
210
         CALL list % init()
221✔
211
         obj => list
221✔
212
         CALL boundaryEdgesArray % addObject(obj)
221✔
213
         CALL releaseFTLinkedList(list)
306✔
214
      END DO
215

216
      END SUBROUTINE AllocateBoundaryEdgesArray
85✔
217
!
218
!////////////////////////////////////////////////////////////////////////
219
!
220
      SUBROUTINE CollectBoundaryEdges( mesh, errorCode )
85✔
221
         USE MeshOutputMethods
222
         USE, INTRINSIC :: iso_fortran_env, only : stderr => ERROR_UNIT
223
!
224
!     ---------------------------------------------------
225
!     Boundary edges are those who have at least one node
226
!     on a boundary.
227
!     ---------------------------------------------------
228
!
229
         IMPLICIT NONE
230
!
231
!        ---------
232
!        Arguments
233
!        ---------
234
!
235
         TYPE( SMMesh ) :: mesh
236
         INTEGER        :: errorCode
237
!
238
!        ---------------
239
!        Local variables
240
!        ---------------
241
!
242
         TYPE (SMEdge)              , POINTER :: edge     => NULL()
243
         CLASS(FTLinkedListIterator), POINTER :: iterator => NULL()
244
         CLASS(FTObject)            , POINTER :: obj      => NULL()
245
         CLASS(FTLinkedList)        , POINTER :: edgeList => NULL()
246
         INTEGER                              :: curveID, curveSide
247

248
         errorCode = A_OK_ERROR_CODE
85✔
249
!
250
!        --------------------------------
251
!        Gather "exterior" boundary edges
252
!        --------------------------------
253
!
254
         iterator => mesh % edgesIterator
85✔
255
         CALL iterator % setToStart()
85✔
256
         DO WHILE( .NOT.iterator % isAtEnd() )
88,504✔
257
            obj => iterator % object()
88,419✔
258
            CALL castToSMEdge(obj,edge)
88,419✔
259

260
            edge % edgeType = INSIDE
88,419✔
261
            curveID         = edge % nodes(1) % node % bCurveChainID
88,419✔
262
            curveSide       = edge % nodes(1) % node % bCurveSide
88,419✔
263

264
            IF( .NOT.ASSOCIATED(edge % elements(2) % element ) )    THEN ! "Exterior" boundary
88,419✔
265
               edge % edgeType = ON_BOUNDARY
11,610✔
266

267
               IF( curveID == 0 .OR. curveSide == 0 )     THEN
11,610✔
268
                  IF( printMessage )     THEN
×
269
                     WRITE(stderr,*)  " "
×
270
                     WRITE(stderr,*)  "**************************************************************************"
×
271
                     WRITE(stderr,*)  "Curve not found for boundary point"
×
272
                     WRITE(stderr,*)   edge % nodes(1) % node % x, edge % nodes(2) % node % x
×
273
                     WRITE(stderr,*)  "Plot the file 'DebugPlot.tec' to check on the mesh topology"
×
274
                     WRITE(stderr,*)  "**************************************************************************"
×
275
                     WRITE(stderr,*)  " "
×
276
                     CALL WriteSkeletonToTecplot(mesh = mesh,fName = "DebugPlot.tec")
×
277
                  END IF
278
                  errorCode = CURVE_NOT_FOUND_ERROR_CODE
×
279
                  RETURN
×
280
               END IF
281

282
               IF( IsOnBoundaryCurve(edge % nodes(1) % node) .OR. &
11,610✔
283
                   IsOnBoundaryCurve(edge % nodes(2) % node) )    THEN
284

285
                  obj      => boundaryEdgesArray % objectAtIndex(curveID)
11,290✔
286
                  edgeList => linkedListFromObject(obj)
11,290✔
287
                  IF ( .NOT.ASSOCIATED(edgeList) )     THEN
11,290✔
288
                     IF( printMessage )     THEN
×
289
                        WRITE(stderr,*)  " "
×
290
                        WRITE(stderr,*)  "**************************************************************************"
×
291
                        WRITE(stderr,*)  "edge list not associated"
×
292
                        WRITE(stderr,*)  ASSOCIATED(obj), ASSOCIATED(boundaryEdgesArray), "CurveID = ", curveID
×
293
                        WRITE(stderr,*)  "Plot the file 'DebugPlot.tec' to check on the mesh topology"
×
294
                        WRITE(stderr,*)  "**************************************************************************"
×
295
                        WRITE(stderr,*)  " "
×
296
                        CALL WriteSkeletonToTecplot(mesh = mesh,fName = "DebugPlot.tec")
×
297
                     END IF
298
                     errorCode = UNASSOCIATED_POINTER_ERROR_CODE
×
299
                     RETURN
×
300
                  END IF
301
                  obj => edge
11,290✔
302
                  CALL edgeList % add(obj)
11,290✔
303
                  boundaryEdgesType(curveID) = BOUNDARY_EDGES
11,290✔
304
               END IF
305
            END IF
306

307
            CALL iterator % moveToNext()
88,419✔
308
         END DO
309
!
310
!        -------------------------------
311
!        Gather interior interface edges
312
!        -------------------------------
313
!
314
         CALL iterator % setToStart()
85✔
315
         DO WHILE ( .NOT.iterator % isAtEnd() )
88,504✔
316
            obj => iterator % object()
88,419✔
317
            CALL castToSMEdge(obj,edge)
88,419✔
318

319
            curveID   = edge % nodes(1) % node % bCurveChainID
88,419✔
320
            curveSide = edge % nodes(1) % node % bCurveSide
88,419✔
321

322
            IF(edge % edgeType == ON_BOUNDARY .OR. curveID <= NONE)     THEN
88,419✔
323
               CALL iterator % moveToNext()
86,177✔
324
               CYCLE
86,177✔
325
            END IF
326

327
           IF( mesh % curveTypeForID(curveID) == INTERIOR_INTERFACE )       THEN
2,242✔
328
!
329
!              --------------------------------------------------
330
!              However, the nodes of the edge must both be inside
331
!              for us to be interested in it
332
!              --------------------------------------------------
333
!
334
               IF ( edge % nodes(1) % node % bCurveSide == INSIDE .AND. &
698✔
335
                    edge % nodes(2) % node % bCurveSide == INSIDE )    THEN
336

337
                  edge % edgeType = ON_INTERFACE
248✔
338
                  obj => boundaryEdgesArray % objectAtIndex(curveID)
248✔
339
                  edgeList => linkedListFromObject(obj)
248✔
340
                  obj => edge
248✔
341
                  CALL edgeList % add(obj)
248✔
342
                  boundaryEdgesType(curveID) = INTERFACE_EDGES
248✔
343
               END IF
344
           END IF
345
!
346
            CALL iterator % movetoNext()
2,242✔
347
         END DO
348

349
      END SUBROUTINE CollectBoundaryEdges
85✔
350
!
351
!////////////////////////////////////////////////////////////////////////
352
!
353
      SUBROUTINE OrderBoundaryEdges( mesh )
42✔
354
         USE ErrorTypesModule
85✔
355
         USE MeshOutputMethods
356
         USE Geometry
357
         USE, INTRINSIC :: iso_fortran_env, only : stderr => ERROR_UNIT
358
!
359
!     -----------------------------------------------------------------
360
!     For each boundary in the boundary edge arrays, re-order the edges
361
!     in counter-clockwise order by marching through the nodes that
362
!     are shared by neighboring edges. The result is a circular list
363
!     with the head pointing to the end of the list
364
!     -----------------------------------------------------------------
365
!
366
         IMPLICIT NONE
367
!
368
!        ---------
369
!        Arguments
370
!        ---------
371
!
372
         TYPE( SMMesh ) :: mesh
373
!
374
!        ---------------
375
!        Local variables
376
!        ---------------
377
!
378
         TYPE (SMEdge)              , POINTER     :: edge => NULL(), currentEdge => NULL(), &
379
                                                     edge1 => NULL(), edge2 => NULL(), loopStartEdge => NULL()
380
         CLASS(FTLinkedList)        , POINTER     :: sortedEdges => NULL(), edgeList => NULL()
381
         TYPE (SMNodePtr)           , POINTER     :: sortedNodes(:) => NULL()
382
         CLASS(FTobject)            , POINTER     :: obj            => NULL()
383
         CLASS(FTLinkedListIterator), POINTER     :: iterator       => NULL()
384
         TYPE (SMEdgePtr)           , ALLOCATABLE :: edgeArray(:)
42✔
385
         INTEGER                    , ALLOCATABLE :: nodeArray(:,:)
42✔
386
         REAL(KIND=RP)              , ALLOCATABLE :: sortedNodeLocations(:,:)
42✔
387

388
         INTEGER                          :: numBoundaries, numEdges, numNodes
389
         INTEGER                          :: k, j, m
390
         INTEGER                          :: locRelativeToBoundary, curveDirection
391
         INTEGER                          :: id1, id2, sharedNodeID, eId1, eId2, eId, idMin
392
         REAL(KIND=RP)                    :: x0(3), x1(3), x2(3), c
393
!
394
!        -----------------------------------------------
395
!        Nothing to order if there are no boundary edges
396
!        -----------------------------------------------
397
!
398
         numBoundaries = boundaryEdgesArray % count()
42✔
399
         IF ( numBoundaries == 0 )     RETURN
42✔
400
!
401
         numNodes = mesh % nodes % COUNT()
42✔
402
         ALLOCATE(nodeArray(2,numNodes))
42✔
403
!
404
!        ------------------------------
405
!        Order each boundary edge chain
406
!        ------------------------------
407
!
408
         DO k = 1, numBoundaries
152✔
409
            obj => boundaryEdgesArray % objectAtIndex(k)
110✔
410
            IF( .NOT.ASSOCIATED( obj ) )          CYCLE
110✔
411
            nodeArray(1:2,1:numNodes) = 0
268,712✔
412
!
413
!           ----------------------------------------
414
!           Put edges into an array for fast access.
415
!           Temporary container with no ownership.
416
!           ----------------------------------------
417
!
418
            CALL cast(obj,edgeList)
110✔
419
            numEdges = edgeList % COUNT()
110✔
420
            ALLOCATE(edgeArray(numEdges))
5,872✔
421

422
            ALLOCATE(iterator)
110✔
423
            CALL iterator % initWithFTLinkedListClass(edgeList)
110✔
424
            CALL iterator % setToStart()
110✔
425

426
            j     = 1
110✔
427
            idMin = HUGE(idMin)
110✔
428

429
            DO WHILE ( .NOT.iterator % isAtEnd() )
5,872✔
430
               obj => iterator % object()
5,762✔
431
               CALL castToSMEdge(obj,currentEdge)
5,762✔
432
               edgeArray(j) % edge => currentEdge
5,762✔
433

434
               id1   = currentEdge % nodes(1) % node % id
5,762✔
435
               id2   = currentEdge % nodes(2) % node % id
5,762✔
436
               idMin = MIN(idMin, id1, id2 )
5,762✔
437

438
               IF( nodeArray(1,id1) == 0 )     THEN
5,762✔
439
                  nodeArray(1,id1) = j
2,015✔
440
               ELSE
441
                  nodeArray(2,id1) = j
3,747✔
442
               END IF
443

444
               IF( nodeArray(1,id2) == 0 )     THEN
5,762✔
445
                  nodeArray(1,id2) = j
3,747✔
446
               ELSE
447
                  nodeArray(2,id2) = j
2,015✔
448
               END IF
449

450
               j = j + 1
5,762✔
451
               locRelativeToBoundary = currentEdge % nodes(2) % node % bCurveSide
5,762✔
452
               CALL iterator % moveToNext()
5,762✔
453
            END DO
454
            CALL releaseFTLinkedListIteratorClass(self = iterator)
110✔
455
            sharedNodeID = idMin
110✔
456
!
457
!           -------------------------
458
!           Generate sorted edge list
459
!           -------------------------
460
!
461
            ALLOCATE(sortedEdges)
110✔
462
            CALL sortedEdges % init()
110✔
463
!
464
!           ---------------------------------------------------------
465
!           Find the edges associated with the first node in the list
466
!           ---------------------------------------------------------
467
!
468
            eId1  = nodeArray(2,idMin)
110✔
469
            edge1 => edgeArray(eId1) % edge
110✔
470
            eId2  = nodeArray(1,idMin)
110✔
471
            edge2 => edgeArray(eId2) % edge
110✔
472
!
473
!           ------------------------------------------------
474
!           Order the edges in counter-clockwise orientation
475
!           by checking the cross-product of the two vectors
476
!           relative to the shared node.
477
!           ------------------------------------------------
478
!
479
            id1 = edge1 % nodes(1) % node % id
110✔
480
            id2 = edge1 % nodes(2) % node % id
110✔
481
            IF ( id1 == sharedNodeID )     THEN
110✔
482
               x0 = edge1 % nodes(1) % node % x
380✔
483
               x1 = edge1 % nodes(2) % node % x
380✔
484
            ELSE
485
               x0 = edge1 % nodes(2) % node % x
60✔
486
               x1 = edge1 % nodes(1) % node % x
60✔
487
            END IF
488
            id1 = edge2 % nodes(1) % node % id
110✔
489
            id2 = edge2 % nodes(2) % node % id
110✔
490
            IF ( id1 == sharedNodeID )     THEN
110✔
491
               x2 = edge2 % nodes(2) % node % x
352✔
492
            ELSE
493
               x2 = edge2 % nodes(1) % node % x
88✔
494
            END IF
495
!
496
!           ---------------------------------------------------------------------------------
497
!           Check 1st two segments for possibly giving the proper (counterclockwise) ordering
498
!           will go back and make sure later.
499
!           ---------------------------------------------------------------------------------
500
!
501
            x2 = x2 - x0
440✔
502
            x1 = x1 - x0
440✔
503
            c  = x1(2)*x2(1) - x1(1)*x2(2)
110✔
504
            IF ( c > 0.0_RP )     THEN
110✔
505
               obj => edge1
53✔
506
               CALL sortedEdges % add(obj)
53✔
507
               loopStartEdge => edge1
53✔
508
               obj => edge2
53✔
509
               CALL sortedEdges % add(obj)
53✔
510
               edge => edge2
53✔
511
               eId = eId2
53✔
512
            ELSE
513
               obj => edge2
57✔
514
               CALL sortedEdges % add(obj)
57✔
515
               loopStartEdge => edge2
57✔
516
               obj => edge1
57✔
517
               CALL sortedEdges % add(obj)
57✔
518
               edge => edge1
57✔
519
               eId = eId1
57✔
520
            END IF
521
!
522
!           --------------------------------------------
523
!           Follow the track to add the rest to the list
524
!           --------------------------------------------
525
!
526
            numEdges = SIZE(edgeArray)
110✔
527
            DO m = 1, numEdges-2
5,652✔
528
               id1 = edge % nodes(1) % node % id
5,542✔
529
               id2 = edge % nodes(2) % node % id
5,542✔
530

531
               IF( id1 == sharedNodeID )     THEN
5,542✔
532
                  sharedNodeID = id2
2,789✔
533
               ELSE
534
                  sharedNodeID = id1
2,753✔
535
               END IF
536
               eId1   = nodeArray(1,sharedNodeID)
5,542✔
537
               eId2   = nodeArray(2,sharedNodeID)
5,542✔
538
               IF ( eId1 == eId )     THEN
5,542✔
539
                  eId = eId2
3,607✔
540
               ELSE
541
                  eId = eId1
1,935✔
542
               END IF
543
               IF(eID == 0)     THEN
5,542✔
544
                  WRITE(stderr,*)  " "
×
545
                  WRITE(stderr,*)  "**************************************************************************"
×
546
                  WRITE(stderr,*)  "It appears that an edge is not connected"
×
547
                  WRITE(stderr,*)  "Plot the file 'DebugPlot.tec' to see where additional resolution is needed"
×
548
                  WRITE(stderr,*)  "**************************************************************************"
×
549
                  WRITE(stderr,*)  " "
×
550
                  CALL WriteSkeletonToTecplot( mesh, "DebugPlot.tec" )
×
551
                  ERROR STOP "Meshing Terminated. See stderr"
×
552
               END IF
553
               edge => edgeArray(eId) % edge
5,542✔
554

555
               IF ( .NOT.ASSOCIATED(edge) )     THEN
5,542✔
556
                  CALL ThrowErrorExceptionOfType("OrderBoundaryEdges",&
557
                                                  "Unable to form boundary edge list", &
558
                                                  FT_ERROR_WARNING)
×
559
                  DEALLOCATE(edgeArray)
×
560
                  RETURN
×
561
               END IF
562

563
               IF(ASSOCIATED(POINTER = edge, TARGET = loopStartEdge) ) EXIT  ! Completed the loop
5,542✔
564

565
               obj  => edge
5,542✔
566
               CALL sortedEdges % add(obj)
5,652✔
567
            END DO
568
            DEALLOCATE(edgeArray)
110✔
569
!
570
!           ---------------------------
571
!           Make this edgeList circular
572
!           in the backwards direction.
573
!           ---------------------------
574
!
575
            CALL sortedEdges % makeCircular(.TRUE.)
110✔
576
!
577
!           ----------------------------------------------
578
!           Now actually find the orientation of this edge
579
!           ----------------------------------------------
580
!
581
            sortedNodes => GatheredNodes( sortedEdges )
110✔
582
            ALLOCATE( sortedNodeLocations(3,SIZE(sortedNodes)) )
110✔
583
            DO m = 1, SIZE(sortedNodes)
5,872✔
584
               sortedNodeLocations(:,m) = sortedNodes(m) % node % x
23,158✔
585
            END DO
586
            curveDirection = Circulation(sortedNodeLocations)
110✔
587
            DEALLOCATE( sortedNodes )
110✔
588
            DEALLOCATE( sortedNodeLocations )
110✔
589
!
590
!           ---------------------------------------------------------------
591
!           If the orientation is clockwise (w < 0 ) then reverse the order
592
!           Hopefully this won't happen often...
593
!           ---------------------------------------------------------------
594
!
595
            IF ( curveDirection == CLOCKWISE )     THEN
110✔
596
               CALL sortedEdges % reverse()
24✔
597
            END IF
598
!
599
!           ------------------------------
600
!           Swap sorted array for unsorted
601
!           ------------------------------
602
!
603
            obj => sortedEdges
110✔
604
            CALL boundaryEdgesArray % replaceObjectAtIndexWithObject(k,obj)
110✔
605
            CALL releaseFTLinkedListClass(self = sortedEdges)
152✔
606
         END DO
607
         DEALLOCATE(nodeArray)
42✔
608

609
      END SUBROUTINE OrderBoundaryEdges
42✔
610
!
611
!////////////////////////////////////////////////////////////////////////
612
!
613
      FUNCTION GatheredNodes( list )
42✔
614
!
615
!     -----------------------------------------------------------------------------
616
!     Returns an ordered temporary array containing pointers to the nodes in a
617
!     boundary edgelist. The reference counts of the nodes are not incremented so
618
!     that the array can be simply deallocated when done.
619
!     -----------------------------------------------------------------------------
620
!
621
         IMPLICIT NONE
622
!
623
!        ---------
624
!        Arguments
625
!        ---------
626
!
627
         CLASS(FTLinkedList)          , POINTER :: list
628
         TYPE(SMNodePtr), DIMENSION(:), POINTER :: GatheredNodes
629
!
630
!        ---------------
631
!        Local variables
632
!        ---------------
633
!
634
         CLASS(FTLinkedListIterator)  , POINTER :: iterator => NULL()
635
         CLASS(FTLinkedListRecord)    , POINTER :: cr => NULL()
636
         TYPE (SMEdge)                , POINTER :: currentEdge => NULL(), prevEdge => NULL()
637
         INTEGER                                :: nNodes, id, idP1, idP2, j
638
         CLASS(FTObject)              , POINTER :: obj => NULL()
639

640
         NULLIFY(GatheredNodes)
487✔
641
         IF ( .NOT.ASSOCIATED(list) )     RETURN
487✔
642

643
         nNodes = list % COUNT()
487✔
644
         ALLOCATE( GatheredNodes(nNodes) )
26,201✔
645
         ALLOCATE(iterator)
487✔
646
         CALL iterator % initwithFTLinkedListClass(list)
487✔
647
!
648
!        -------------------------------------------------
649
!        Gather the nodes and positions along the boundary
650
!        The edge list is  circular.
651
!        -------------------------------------------------
652
!
653
         j = 1
487✔
654
         CALL iterator % setToStart()
487✔
655
         DO WHILE ( .NOT.iterator % isAtEnd() )
26,201✔
656
            obj => iterator % object()
25,714✔
657
            CALL castToSMEdge(obj,currentEdge)
25,714✔
658
            cr  => iterator % currentRecord()
25,714✔
659
            obj => cr % previous % recordObject
25,714✔
660
            CALL castToSMEdge(obj,prevEdge)
25,714✔
661

662
            id   = currentEdge % nodes(1) % node % id
25,714✔
663
            idP1 = prevEdge % nodes(1) % node % id
25,714✔
664
            idP2 = prevEdge % nodes(2) % node % id
25,714✔
665
            IF ( id == idP1 .OR. id == idP2 )     THEN
25,714✔
666
               GatheredNodes(j) % node => currentEdge % nodes(1) % node
12,658✔
667
            ELSE
668
               GatheredNodes(j) % node => currentEdge % nodes(2) % node
13,056✔
669
            END IF
670
            CALL iterator % moveToNext()
25,714✔
671
            j = j + 1
25,714✔
672
         END DO
673

674
         CALL releaseFTLinkedListIteratorClass(self = iterator)
487✔
675

676
      END FUNCTION GatheredNodes
677
!
678
!////////////////////////////////////////////////////////////////////////
679
!
680
      SUBROUTINE SmoothEdgeListNodes( list, nPasses )
106✔
681
!
682
!     --------------------------------------------------
683
!     Average the locations of nodes used by an edgeList
684
!     --------------------------------------------------
685
!
686
         IMPLICIT NONE
687
!
688
!        ---------
689
!        Arguments
690
!        ---------
691
!
692
         CLASS(FTLinkedList) , POINTER :: list
693
         INTEGER                       :: nPasses
694
!
695
!        ---------------
696
!        Local variables
697
!        ---------------
698
!
699
         TYPE(SMNodePtr), POINTER     :: nodeArray(:) => NULL()
700
         REAL(KIND=RP)  , ALLOCATABLE :: x(:,:)
106✔
701
         INTEGER                      :: nNodes, j, k
702

703

704
         nodeArray => GatheredNodes( list )
106✔
705
         nNodes    = SIZE(nodeArray)
106✔
706
         ALLOCATE( x(3,nNodes) )
106✔
707
!
708
!        -----------------------------
709
!        Average the nearest neighbors
710
!        -----------------------------
711
!
712

713
         DO k = 1, nPasses
212✔
714
            DO j = 2, nNodes-1
5,532✔
715
               x(:,j) = (nodeArray(j-1)%node%x + 6*nodeArray(j)%node%x + nodeArray(j+1)%node%x)/8.0_RP
21,810✔
716
            END DO
717
            x(:,1)      = (nodeArray(nNodes)%node%x   + 6*nodeArray(1)%node%x      + nodeArray(2)%node%x)/8.0_RP
424✔
718
            x(:,nNodes) = (nodeArray(nNodes-1)%node%x + 6*nodeArray(nNodes)%node%x + nodeArray(1)%node%x)/8.0_RP
424✔
719
            DO j = 1, nNodes
5,850✔
720
               nodeArray(j)%node%x = x(:,j)
22,658✔
721
            END DO
722
         END DO
723

724
         DEALLOCATE( nodeArray )
106✔
725
         DEALLOCATE( x )
106✔
726

727
      END SUBROUTINE SmoothEdgeListNodes
106✔
728
!
729
!////////////////////////////////////////////////////////////////////////
730
!
731
      SUBROUTINE LocateEdgeImagesOnBoundaries( mesh, model, idOfOuterBoundary, skipInterfaces )
42✔
732
         USE SMModelClass
733
         USE ErrorTypesModule
734
!
735
!        ------------------------------------------------------
736
!        Find location of image of the edges onto the boundary.
737
!
738
!        Throws FATAL_ERROR_EXCEPTION if boundaries and edges
739
!        cannot be matched.
740
!        ------------------------------------------------------
741
!
742
         IMPLICIT NONE
743
!
744
!        ---------
745
!        Arguments
746
!        ---------
747
!
748
         TYPE (SMMesh) , POINTER :: mesh
749
         TYPE (SMModel), POINTER :: model
750
         INTEGER                 :: idOfOuterBoundary
751
         LOGICAL                 :: skipInterfaces
752
!
753
!        ---------------
754
!        Local variables
755
!        ---------------
756
!
757
         CLASS(SMChainedCurve)      , POINTER     :: chain            => NULL()
758
         TYPE (SMEdge)              , POINTER     :: edge             => NULL()
759
         CLASS(FTObject)            , POINTER     :: obj              => NULL()
760
         CLASS(FTLinkedList)        , POINTER     :: edgeList         => NULL()
761
         CLASS(FTLinkedListIterator), POINTER     :: edgeListIterator => NULL()
762

763
         LOGICAL                                  :: isInnerBoundaryCurve
764
         INTEGER                                  :: j
765
         INTEGER                                  :: numBoundaries, chainID
766
         CHARACTER(LEN=ERROR_EXCEPTION_MSG_LENGTH):: msg
767

768
         numBoundaries = boundaryEdgesArray % COUNT()
42✔
769

770
         DO j = 1, numBoundaries
152✔
771

772
            obj => boundaryEdgesArray % objectAtIndex(j)
110✔
773
            IF( .NOT.ASSOCIATED(obj) )                          CYCLE
110✔
774
            IF ( boundaryEdgesType(j) == INTERFACE_EDGES .AND. &
110✔
775
                 skipInterfaces )                               CYCLE
2✔
776

777
            CALL cast(obj,edgeList)
108✔
778
            ALLOCATE(edgeListIterator)
108✔
779
            CALL edgeListIterator % initwithFTLinkedListClass(edgeList)
108✔
780

781
            CALL edgeListIterator % setToStart()
108✔
782

783
            obj     => edgeListIterator % object()
108✔
784
            CALL castToSMEdge(obj,edge)
108✔
785
            chainID = edge % nodes(1) % node % bCurveChainID
108✔
786
!
787
!           ---------------------------------------------------
788
!           Bail if the edge has no associated curve ID defined
789
!           ---------------------------------------------------
790
!
791
            IF (chainID == UNDEFINED )     THEN
108✔
792
               WRITE(msg,*) "Boundary curve chain ",chainID," not assigned for a boundary edge ", edge % id
×
793
               CALL ThrowErrorExceptionOfType("LocateEdgeImagesOnBoundaries",msg,FT_ERROR_FATAL)
×
NEW
794
               CALL releaseFTLinkedListIteratorClass(self = edgeListIterator)
×
795
               RETURN
×
796
            END IF
797

798
            chain => model % chainWithID(chainID)
108✔
799
!
800
!           --------------------------------------------------------
801
!           Bail of the chain for this edge curve ID cannot be found
802
!           --------------------------------------------------------
803
!
804
            IF ( .NOT.ASSOCIATED(chain) )     THEN
108✔
805
               WRITE(msg,*) "Chain with id ", chainID, " Not found in model"
×
806
               CALL ThrowErrorExceptionOfType("LocateEdgeImagesOnBoundaries",msg,FT_ERROR_FATAL)
×
NEW
807
               CALL releaseFTLinkedListIteratorClass(self = edgeListIterator)
×
808
               RETURN
×
809
            END IF
810
!
811
            IF( chainID == idOfOuterBoundary )     THEN
108✔
812
               isInnerBoundaryCurve = .FALSE.
40✔
813
            ELSE IF( edge % nodes(1) % node % bCurveSide == INSIDE )     THEN
68✔
814
               isInnerBoundaryCurve = .FALSE.
2✔
815
            ELSE
816
               isInnerBoundaryCurve = .TRUE.
66✔
817
            END IF
818
!
819
            CALL AssociateBoundaryEdgesToCurve( edgeList, chain, isInnerBoundaryCurve )
108✔
820
            CALL releaseFTLinkedListIteratorClass(self = edgeListIterator)
150✔
821

822
         END DO
823

824
      END SUBROUTINE LocateEdgeImagesOnBoundaries
42✔
825
!
826
!////////////////////////////////////////////////////////////////////////
827
!
828
      SUBROUTINE AssociateBoundaryEdgesToCurve( list, chain, isInnerBoundaryCurve )
108✔
829
      USE SMChainedCurveClass
42✔
830
!
831
!     ---------------------------------------------------------------------
832
!     Find the nearest location on the curve to each point in the edge list
833
!     in the direction of the normal to the curve.
834
!     ---------------------------------------------------------------------
835
!
836
         USE ProgramGlobals, ONLY :edgeLengthFactor
837
         USE Geometry
838
         IMPLICIT NONE
839
!
840
!        ---------
841
!        Arguments
842
!        ---------
843
!
844
         CLASS(FTLinkedList)  , POINTER :: list
845
         CLASS(SMChainedCurve), POINTER :: chain
846
         LOGICAL                        :: isInnerBoundaryCurve
847
!
848
!        ---------------
849
!        Local variables
850
!        ---------------
851
!
852
         CLASS(SMCurve)                 , POINTER :: c => NULL(), c2 => NULL()
853
         TYPE(SMNodePtr), DIMENSION(:)  , POINTER :: nodePtrs => NULL()
854
         TYPE (SMNode)                  , POINTER :: node => NULL()
855
         REAL(KIND=RP)  , DIMENSION(:,:), POINTER :: nHat => NULL()
856
         REAL(KIND=RP)  , DIMENSION(:,:), POINTER :: xCurve => NULL()
857
         REAL(KIND=RP)  , DIMENSION(3)            :: z, p
858
         REAL(KIND=RP)                            :: tStart, tEnd, t
859
         REAL(KIND=RP)                            :: d, dMin, dt
860
         INTEGER                                  :: j, k, M
861
         INTEGER                                  :: totalCurvePoints
862

863
         nodePtrs => GatheredNodes( list )
108✔
864
!
865
!        -----------------------------------------------------------------
866
!        Approximate normal to each node as the normal to the line between
867
!        the two neighboring nodes. [Another possibility is to average the
868
!        normals to the edges on either side.]
869
!        -----------------------------------------------------------------
870
!
871
         ALLOCATE( nHat(3,SIZE(nodePtrs)) )
108✔
872
         M = SIZE(nHat,2)
108✔
873
         DO j = 2, M-1
5,592✔
874
            z         = nodePtrs(j+1) % node % x - nodePtrs(j-1) % node % x
21,936✔
875
            z         = z/SQRT(z(1)**2 + z(2)**2 )
21,936✔
876
            nHat(1,j) = z(2); nHat(2,j) = -z(1); nHat(3,j) = 0.0_RP
5,592✔
877
         END DO
878

879
         z         = nodePtrs(2)%node%x - nodePtrs(M)%node%x
432✔
880
         z         = z/SQRT(z(1)**2 + z(2)**2 )
432✔
881
         nHat(1,1) = z(2); nHat(2,1) = -z(1); nHat(3,1) = 0.0_RP
108✔
882

883
         z         = nodePtrs(1) % node % x - nodePtrs(M-1) % node % x
432✔
884
         z         = z/SQRT(z(1)**2 + z(2)**2 )
432✔
885
         nHat(1,M) = z(2); nHat(2,M) = -z(1); nHat(3,M) = 0.0_RP
108✔
886

887
         IF( isInnerBoundaryCurve ) nHat = -nHat
9,268✔
888
!
889
!        ----------------------------------
890
!        Now find the point along the curve
891
!        ----------------------------------
892
!
893
         totalCurvePoints = 2*numCurvePoints*chain % numberOfCurvesInChain !TODO compute this quantity
108✔
894
         tStart  = 0.0
108✔
895
         tEnd    = 1.0_RP
108✔
896
         dt      = (tEnd - tStart)/totalCurvePoints
108✔
897
!
898
!        -----------------------------------------------------------------
899
!        First, estimate the location with a linear search along
900
!        the boundary. Must do this because there can be local
901
!        minima that fMin cannot deal with. Compute and store points along
902
!        the curve to compare against.
903
!       ------------------------------------------------------------------
904
!
905
         ALLOCATE( xCurve(3,0:totalCurvePoints) )
108✔
906
         DO j = 0, totalCurvePoints
180,216✔
907
            t = j*dt
180,108✔
908
            xCurve(:,j) = chain % positionAt(t)
720,540✔
909
         END DO
910
!
911
!        -------------------------------------------------------------
912
!        For each node along the edge boundary, find the closest point
913
!        -------------------------------------------------------------
914
!
915
         DO j = 1, SIZE(nodePtrs)
5,808✔
916
            node => nodePtrs(j)%node
5,700✔
917
            p    = node % x
22,800✔
918
            dMin = HUGE(dMin)
5,700✔
919
            DO k = 0, totalCurvePoints - 1
11,071,808✔
920
               d = SQRT(distanceSquaredBetweenPoints(xCurve(:,k), p, [0.0_RP,0.0_RP,0.0_RP]))
11,066,000✔
921
               IF( d < dMin )     THEN
11,071,700✔
922
                  t                     = k*dt
2,662,821✔
923
                  node%gWhereOnBoundary = t
2,662,821✔
924
                  dMin                  = d
2,662,821✔
925
               END IF
926
            END DO
927
         END DO
928
!
929
!        -------------------------------
930
!        Now zoom precisely to the curve
931
!        -------------------------------
932
!
933
         DO j = 1, SIZE(nodePtrs)
5,808✔
934
            node => nodePtrs(j)%node
5,700✔
935
            p      = node % x
22,800✔
936
            t      = node % gWhereOnBoundary
5,700✔
937
            c      => chain % curveWithLocation(t)
5,700✔
938

939
            tStart = t - dt
5,700✔
940
            tEnd   = t + dt
5,700✔
941
            IF(tStart < 0.0_RP) tStart = 0.0_RP
5,700✔
942
            IF(tEnd> 1.0_RP) tEnd = 1.0_RP
5,700✔
943
!
944
!           --------------------------------------------
945
!           Make sure that the locations do not straddle
946
!           two curves
947
!           --------------------------------------------
948
!
949
            c  => chain % curveWithLocation(tStart)
5,700✔
950
            c2 => chain % curveWithLocation(tend)
5,700✔
951
            IF ( c % id() == c2 % id() )     THEN
5,700✔
952
               tStart = chain % curveTForChainT(tStart)
5,647✔
953
               tEnd   = chain % curveTForChainT(tEnd )
5,647✔
954
               tStart = MAX(tStart, 0.0_RP)
5,647✔
955
               tEnd   = MIN(tEnd, 1.0_RP)
5,647✔
956
               t      = fmin(c, tStart, tEnd, minimizationTolerance, p, [0.0_RP,0.0_RP,0.0_RP])
5,647✔
957
            ELSE
958
               c => chain % curveWithLocation(t)
53✔
959
               t =  chain % curveTForChainT(t)
53✔
960
            END IF
961

962
            d = SQRT(distanceSquared(t, c, p, [0.0_RP,0.0_RP,0.0_RP]))
5,700✔
963

964
            node % bCurveID         = c % id()
5,700✔
965
            node % whereOnBoundary  = t
5,700✔
966
            node % distToBoundary   = d
5,700✔
967
            node % gWhereOnBoundary = chain % ChainTForCurveTInCurve(t,c)
5,808✔
968
         END DO
969
!
970
!        ----
971
!        Done
972
!        ----
973
!
974
         DEALLOCATE(nodePtrs)
108✔
975
         DEALLOCATE(nHat)
108✔
976
         NULLIFY(nodePtrs)
108✔
977
         DEALLOCATE( xCurve )
108✔
978

979
      END SUBROUTINE AssociateBoundaryEdgesToCurve
108✔
980
!
981
!////////////////////////////////////////////////////////////////////////
982
!
983
      SUBROUTINE  SmoothBoundaryLocations( list, model )
55✔
984
         USE SMModelClass
108✔
985
         IMPLICIT NONE
986
!
987
!        ---------
988
!        Arguments
989
!        ---------
990
!
991
         CLASS(FTLinkedList), POINTER :: list
992
         TYPE (SMModel)     , POINTER :: model
993
!
994
!        ---------------
995
!        Local variables
996
!        ---------------
997
!
998
         CLASS(SMChainedCurve)        , POINTER :: chain => NULL()
999
         TYPE(SMNodePtr), DIMENSION(:), POINTER :: nodeArray => NULL()
1000
         TYPE (SMNode)                , POINTER :: node => NULL()
1001
         INTEGER                                :: nodeArraySize, j
1002
         REAL(KIND=RP)                          :: t0, tm, tp, tmpTol = 1.0d-2
1003

1004
         INTEGER, EXTERNAL :: Loop
1005
!
1006
!        ---------------------------------------------------------------
1007
!        If two target points are too close, move one of them to halfway
1008
!        to the next one. This will make the distribution along the
1009
!        boundary more uniform. Pick the left handed corner to move.
1010
!        ---------------------------------------------------------------
1011
!
1012
         nodeArray     => GatheredNodes( list )
55✔
1013
         nodeArraySize =  SIZE(nodeArray)
55✔
1014
         chain         => model % chainWithID(nodeArray(1) % node % bCurveChainID)
55✔
1015

1016
         DO j = 1, nodeArraySize
2,947✔
1017
            node => nodeArray(j) % node
2,892✔
1018
!
1019
!           -------------------------------
1020
!           Move only ROW_SIDE node targets
1021
!           -------------------------------
1022
!
1023
            IF( node % nodetype /= ROW_SIDE )     CYCLE
2,892✔
1024

1025
            t0 = nodeArray(j) % node % gWhereOnBoundary
2,845✔
1026
            tm = nodeArray(Loop(j-1,nodeArraySize)) % node % gWhereOnBoundary
2,845✔
1027
            tp = nodeArray(Loop(j+1,nodeArraySize)) % node % gWhereOnBoundary
2,845✔
1028
!
1029
!           --------------------
1030
!           Massage if necessary
1031
!TODO This section still needs work!!!
1032
!           --------------------
1033
!
1034
            IF( tm > tp )     CYCLE
2,845✔
1035
            IF ( abs(t0 - tm) <= tmpTol*max(t0,tm) )     THEN !Too CLOSE
2,751✔
1036
!               x0 = nodePtrs(j) % node % x
1037
!               xp = nodePtrs(Loop(j+1,nodeArraySize)) % node % x
1038
!               xm = nodePtrs(Loop(j-1,nodeArraySize)) % node % x
1039
!               x0 = x0 - xm
1040
!               xp = xp - x0
1041
!               IF ( CrossProductDirection(x0,xp) == DOWN )     THEN
1042
!                  IF( tm > tp )                  tm = tm - 1.0_RP ! Wrap around
1043
                  t0                                 = 0.5_RP*(t0 + tp)
657✔
1044
                  nodeArray(j) % node % gWhereOnBoundary = t0
657✔
1045
!                  c                                  => chain % curveWithLocation(t0)
1046
                  nodeArray(j) % node % whereOnBoundary  = chain % curveTForChainT(t0)!               END IF
657✔
1047
!            ELSE IF ( tm > t0 )     THEN !Swap the points
1048
!               nodeArray(j) % node % gWhereOnBoundary = tm
1049
!               c                                  => CurveInChain_WithLocation_( chain, tm )
1050
!               nodeArray(j) % node % whereOnBoundary  = CurrentCurveTForChainT( chain, tm )
1051
!
1052
!               nodeArray(Loop(j-1,nodeArraySize)) % node % gWhereOnBoundary = t0
1053
!               c                                  => CurveInChain_WithLocation_( chain, t0 )
1054
!               nodeArray(Loop(j-1,nodeArraySize)) % node % whereOnBoundary  = CurrentCurveTForChainT( chain, t0 )
1055
            END IF
1056

1057
         END DO
1058
!
1059
!        --------
1060
!        Clean up
1061
!        --------
1062
!
1063
         DEALLOCATE( nodeArray )
55✔
1064

1065
      END SUBROUTINE SmoothBoundaryLocations
55✔
1066
!
1067
!////////////////////////////////////////////////////////////////////////
1068
!
1069
      SUBROUTINE unmarkNodesNearBoundaries( nodeIterator )
44✔
1070
!
1071
!     --------------------------------------------------------------------
1072
!     When elements that intersect boundary curves are removed, nodes that
1073
!     were left behind were marked as boundary nodes. Once new elements
1074
!     are generated to project onto the true boundaries, remove the
1075
!     designation that what are now interior nodes are boundary nodes
1076
!     --------------------------------------------------------------------
1077
!
1078
      IMPLICIT NONE
1079
      TYPE (FTLinkedListIterator), POINTER :: nodeIterator
1080
      CLASS(FTObject)            , POINTER :: obj  => NULL()
1081
      TYPE (SMNode)              , POINTER :: node => NULL()
1082
      CALL nodeIterator % setToStart()
44✔
1083
      DO WHILE( .NOT.nodeIterator % isAtEnd() )
30,495✔
1084
         obj => nodeIterator % object()
30,451✔
1085
         CALL castToSMNode(obj,node)
30,451✔
1086

1087
         IF( node % distToBoundary > 0.0_RP )     THEN
30,451✔
1088
             node % bCurveID        = UNDEFINED
23,063✔
1089
             node % bCurveChainID   = UNDEFINED
23,063✔
1090
             node % bCurveSide      = UNDEFINED
23,063✔
1091
             node % whereOnBoundary = -1.0_RP
23,063✔
1092
         END IF
1093

1094
         CALL nodeIterator % moveToNext()
30,451✔
1095
      END DO
1096

1097
      END SUBROUTINE unmarkNodesNearBoundaries
99✔
1098
!
1099
!////////////////////////////////////////////////////////////////////////
1100
!
1101
      SUBROUTINE SetNodeActiveStatus(mesh, model, errorCode)
68✔
1102
!
1103
!     ------------------------------------------------------------------
1104
!     Note: In this routine it should not be necessary to use the edges.
1105
!     Instead, the nodes have enough information to determine
1106
!     status, so we should be able to use the node list. Check on
1107
!     what happens at interface nodes, however, before changing.
1108
!     ------------------------------------------------------------------
1109
!
1110
        USE SMModelClass
1111
        USE, INTRINSIC :: iso_fortran_env, only : stderr => ERROR_UNIT
1112
        IMPLICIT NONE
1113
!
1114
!        ---------
1115
!        Arguments
1116
!        ---------
1117
!
1118
         TYPE (SMMesh) , POINTER :: mesh
1119
         TYPE (SMModel), POINTER :: model
1120
         INTEGER                 :: errorCode
1121
!
1122
!        ---------------
1123
!        Local variables
1124
!        ---------------
1125
!
1126
         INTEGER                      :: numBoundaries
1127
         INTEGER                      :: j
1128
         CLASS(FTLinkedList), POINTER :: currentEdgeList => NULL()
1129
         TYPE (SMEdge)      , POINTER :: currentEdge => NULL()
1130
         CLASS(FTObject)    , POINTER :: obj => NULL()
1131

1132
         CLASS (FTLinkedListIterator), POINTER :: iterator => NULL()
1133
!
1134
!        ---------------------
1135
!        Gather boundary Edges
1136
!        ---------------------
1137
!
1138
         numBoundaries = model % numberOfInnerCurves + model % numberOfOuterCurves &
1139
                       + model % numberOfInterfaceCurves
68✔
1140

1141
         IF ( numBoundaries == 0 )     RETURN
68✔
1142

1143
         CALL AllocateBoundaryEdgesArray(numBoundaries)
64✔
1144

1145
         ALLOCATE(iterator)
64✔
1146
         CALL iterator % init()
64✔
1147

1148
         CALL CollectBoundaryEdges( mesh, errorCode )
64✔
1149
         IF ( errorCode > A_OK_ERROR_CODE )     THEN
64✔
1150
            RETURN
×
1151
         END IF
1152

1153
         CALL MakeNodeToElementConnections( mesh, errorCode )
64✔
1154

1155
         IF( errorCode == A_OK_ERROR_CODE)     THEN
64✔
1156
            CALL MakeElementToEdgeConnections( mesh )
64✔
1157

1158
            IF ( model % numberOfInnerCurves + model % numberOfOuterCurves > 0 )     THEN
64✔
1159

1160
               DO j = 1, numBoundaries
230✔
1161
                  IF( boundaryEdgesType(j) == INTERFACE_EDGES ) CYCLE
166✔
1162

1163
                  obj => boundaryEdgesArray % objectAtIndex(j)
160✔
1164
                  CALL cast(obj,currentEdgeList)
160✔
1165
                  IF(.NOT. ASSOCIATED(currentEdgeList)) THEN
160✔
1166
                     WRITE(stderr,*)  "Unnassociated edgelist in SetNodeActiveStatus number",j
×
1167
                     CYCLE
×
1168
                  END IF
1169

1170
                  CALL iterator % setLinkedListClass(currentEdgeList)
160✔
1171
                  CALL iterator % setToStart()
160✔
1172
                  DO WHILE ( .NOT.iterator % isAtEnd() )
8,706✔
1173
                     obj => iterator % object()
8,482✔
1174
                     CALL castToSMEdge(obj,currentEdge)
8,482✔
1175

1176
                     IF ( currentEdge % edgeType == ON_BOUNDARY )     THEN
8,482✔
1177

1178
                        IF ( currentEdge % nodes(1) % node % nodeType == ROW_SIDE .AND. boundarySlipping )     THEN
8,482✔
1179
                           currentEdge % nodes(1) % node % activeStatus = ACTIVE
×
1180
                        ELSE
1181
                           currentEdge % nodes(1) % node % activeStatus = INACTIVE
8,482✔
1182
                        END IF
1183

1184
                        IF ( currentEdge%nodes(2) % node % nodeType == ROW_SIDE .AND. boundarySlipping )     THEN
8,482✔
1185
                           currentEdge % nodes(2) % node % activeStatus = ACTIVE
×
1186
                        ELSE
1187
                           currentEdge % nodes(2) % node % activeStatus = INACTIVE
8,482✔
1188
                        END IF
1189

1190
                     END IF
1191
!
1192
!                 ----------------------------------------------------
1193
!                 Nevertheless, deactivate when at the end of a curve.
1194
!                 ----------------------------------------------------
1195
!
1196
                      IF ( currentEdge % nodes(1) % node % whereOnBoundary == 0.0_RP .OR. &
8,482✔
1197
                           currentEdge % nodes(1) % node % whereOnBoundary == 1.0_RP)     THEN
1198
                           currentEdge % nodes(1) % node % activeStatus    = INACTIVE
211✔
1199
                      END IF
1200
                      IF ( currentEdge % nodes(2) % node % whereOnBoundary == 0.0_RP .OR. &
8,482✔
1201
                           currentEdge % nodes(2) % node % whereOnBoundary == 1.0_RP)     THEN
1202
                           currentEdge % nodes(2) % node % activeStatus    = INACTIVE
329✔
1203
                      END IF
1204

1205
                     CALL iterator % moveToNext()
8,482✔
1206
                  END DO
1207
               END DO
1208

1209
            END IF
1210
            CALL deallocateElementToEdgeConnections()
64✔
1211
         END IF
1212
         CALL releaseFTLinkedListIteratorClass(self = iterator)
64✔
1213
         CALL deallocateNodeToElementConnections()
64✔
1214

1215
      END SUBROUTINE SetNodeActiveStatus
68✔
1216
!
1217
!////////////////////////////////////////////////////////////////////////
1218
!
1219
      SUBROUTINE FindCurveLocationsforNodes( boundaryNodesList, model )
47✔
1220
         USE SMModelClass
68✔
1221
         IMPLICIT NONE
1222
!
1223
!        ---------
1224
!        Arguments
1225
!        ---------
1226
!
1227
         CLASS(FTLinkedList), POINTER :: boundaryNodesList
1228
         TYPE (SMModel)     , POINTER :: model
1229
!
1230
!        ---------------
1231
!        Local variables
1232
!        ---------------
1233
!
1234
         TYPE (SMNode)              , POINTER :: currentNode => NULL()
1235
         CLASS(SMCurve)             , POINTER :: c => NULL()
1236
         CLASS(FTLinkedListIterator), POINTER :: iterator => NULL()
1237
         CLASS(FTObject)            , POINTER :: obj => NULL()
1238
         REAL(KIND=RP)                        :: tOld,tNew
1239
         REAL(KIND=RP)                        :: pOld(3),pNew(3)
1240
         CLASS(SMChainedCurve)      , POINTER :: chain => NULL()
1241
         INTEGER                              :: curveID
1242
!
1243
!        --------------------------------------------------------------------
1244
!        Loop through the nodes and find the parameter value %whereOnBoundary
1245
!        for all active nodes
1246
!        --------------------------------------------------------------------
1247
!
1248
         ALLOCATE(iterator)
47✔
1249
         CALL iterator % initwithFTLinkedListClass(boundaryNodesList)
47✔
1250
         CALL iterator % setToStart()
47✔
1251

1252
         DO WHILE ( .NOT.iterator % isAtEnd() )
5,844✔
1253
            obj => iterator % object()
5,797✔
1254
            CALL castToSMNode(obj,currentNode)
5,797✔
1255

1256
            IF ( currentNode % activeStatus == INACTIVE )     THEN
5,797✔
1257
               CALL iterator % moveToNext()
5,668✔
1258
               CYCLE
5,668✔
1259
            END IF
1260

1261
            curveID = currentNode % bCurveID
129✔
1262
            c       => model % curveWithID(curveID, chain)
129✔
1263

1264
            pOld =  currentNode % x
516✔
1265
            tOld =  currentNode % whereOnBoundary
129✔
1266

1267
            tNew = ParametrizationAtPointNear( c, pOld, tOld )
129✔
1268
            pNew = c % positionAt(tNew)
516✔
1269

1270
            currentNode % x                = pNew
516✔
1271
            currentNode % whereOnBoundary  = tNew
129✔
1272
            currentNode % distToBoundary   = 0.0_RP
129✔
1273
            currentNode % gWhereOnBoundary = chain % ChainTForCurveTInCurve(tNew,c)
129✔
1274

1275
            CALL iterator % moveToNext()
129✔
1276
         END DO
1277

1278
         CALL releaseFTLinkedListIteratorClass(self = iterator)
47✔
1279

1280
      END SUBROUTINE FindCurveLocationsforNodes
47✔
1281
!
1282
!////////////////////////////////////////////////////////////////////////
1283
!
1284
      SUBROUTINE CheckForBoundaryIntersections(sizer)
24✔
1285
         IMPLICIT NONE
1286
!
1287
!        ---------
1288
!        Arguments
1289
!        ---------
1290
!
1291
         TYPE(MeshSizer), POINTER  :: sizer
1292
!
1293
!        ---------------
1294
!        Local Variables
1295
!        ---------------
1296
!
1297
         INTEGER                               :: k, j
1298
         CLASS(SegmentedCurveArray) , POINTER  :: curveArrayA, curveArrayB
1299
         CHARACTER(LEN=STRING_CONSTANT_LENGTH) :: msg
1300

1301
         CALL generateTemporaryBoundaryArrays( sizer )
24✔
1302
!
1303
!        -------------------------------------------------
1304
!        See if exterior and any interior curves intersect
1305
!        -------------------------------------------------
1306
!
1307
         IF ( ASSOCIATED( outerBoundaryCurve ) )     THEN
24✔
1308
            IF( ASSOCIATED( interiorCurves ) )    THEN
20✔
1309

1310
               DO k = 1, SIZE(interiorCurves)
41✔
1311

1312
                  IF (TwoCurvesIntersect(curveArrayA = outerBoundaryCurve,                    &
32✔
1313
                                         curveArrayB = interiorCurves(k) % curveArray ,       &
1314
                                         testType    = TEST_NESTING     ))                THEN
9✔
1315

1316
                     WRITE(msg,*) "Interior curve ", k," overlaps with exterior curve"
×
1317
                     CALL ThrowErrorExceptionOfType(poster = "CheckForBoundaryIntersections", &
1318
                                                    msg    = msg,                             &
1319
                                                    typ    = FT_ERROR_FATAL)
×
1320
                     CALL destroyTemporaryBoundaryArrays
×
1321
                     RETURN
×
1322

1323
                  END IF
1324
!
1325
               END DO
1326

1327
            END IF
1328
         END IF
1329
!
1330
!        --------------------------------
1331
!        See if interior curves intersect
1332
!        --------------------------------
1333
!
1334
         IF( ASSOCIATED( interiorCurves ) )    THEN
24✔
1335
            DO k = 1, SIZE(interiorCurves)
43✔
1336
               curveArrayA => interiorCurves(k) % curveArray
33✔
1337

1338
               DO j = k+1, SIZE(interiorCurves)
104✔
1339

1340
                  curveArrayB => interiorCurves(j) % curveArray
61✔
1341

1342
                  IF (TwoCurvesIntersect(curveArrayA = interiorCurves(k) % curveArray,        &
61✔
1343
                                         curveArrayB = interiorCurves(j) % curveArray,        &
1344
                                         testType    = TEST_INTERSECTION     ))        THEN
33✔
1345

1346
                     WRITE(msg,*) "Interior curves ", k," and ", j, "overlap"
×
1347
                     CALL ThrowErrorExceptionOfType(poster = "CheckForBoundaryIntersections", &
1348
                                                    msg    = msg,                       &
1349
                                                    typ    = FT_ERROR_FATAL)
×
1350
                     CALL destroyTemporaryBoundaryArrays
×
1351
                     RETURN
×
1352

1353
                  END IF
1354

1355
               END DO
1356
            END DO
1357
         END IF
1358

1359
         CALL destroyTemporaryBoundaryArrays
24✔
1360

1361
      END SUBROUTINE CheckForBoundaryIntersections
47✔
1362
!
1363
!////////////////////////////////////////////////////////////////////////
1364
!
1365
      LOGICAL FUNCTION TwoCurvesIntersect(curveArrayA, curveArrayB, testType)
93✔
1366
         IMPLICIT NONE
1367
!
1368
!        ---------
1369
!        Arguments
1370
!        ---------
1371
!
1372
         CLASS(SegmentedCurveArray) , POINTER  :: curveArrayA, curveArrayB
1373
         INTEGER                               :: testType
1374
!
1375
!        ---------------
1376
!        Local Variables
1377
!        ---------------
1378
!
1379
         LOGICAL                      :: intersectionFound
1380
         REAL(KIND=RP)                :: intersection(6)
1381
         REAL(KIND=RP), ALLOCATABLE   :: crv1(:,:), crv2(:,:)
93✔
1382

1383
         TwoCurvesIntersect = .FALSE.
93✔
1384

1385
         IF ( testType == TEST_NESTING )     THEN ! Easy Full curve BBox check
93✔
1386
            intersectionFound = .NOT.BBoxIsNested(BoxA = curveArrayA % boundingBox, &
1387
                                                  BoxB = curveArrayB % boundingBox)
32✔
1388
         ELSE
1389
            intersectionFound = BBoxIntersects(BoxA = curveArrayA % boundingBox, &
1390
                                               BoxB = curveArrayB % boundingBox)
61✔
1391
         END IF
1392

1393
         IF ( intersectionFound )     THEN ! Check further
93✔
1394

1395
            ALLOCATE(crv1(3,0:curveArrayA % nSegments))
×
1396
            crv1 = curveArrayA % x
×
1397
            ALLOCATE(crv2(3,0:curveArrayB % nSegments))
×
1398
            crv2 = curveArrayB % x
×
1399

1400
            intersection = IntersectionOfBBoxes(boxA = curveArrayA % boundingBox, &
1401
                                                boxB = curveArrayB % boundingBox)
×
1402

1403
            TwoCurvesIntersect = TwoCurvesAsPointsIntersect(testCurve1   = crv1,                      &
1404
                                                            nPts1        = curveArrayA % nSegments,   &
1405
                                                            testCurve2   = crv2,                      &
1406
                                                            nPts2        = curveArrayB % nSegments,   &
1407
                                                            intersection = intersection)
×
1408

1409
            IF(TwoCurvesIntersect)     RETURN
×
1410

1411
         END IF
1412

1413
      END FUNCTION TwoCurvesIntersect
93✔
1414
!
1415
!////////////////////////////////////////////////////////////////////////
1416
!
1417
      FUNCTION TwoCurvesAsPointsIntersect(testCurve1, nPts1, &
×
1418
                                                    testCurve2, nPts2, &
×
1419
                                                    intersection) RESULT(theyCross)
1420
!
1421
!     --------------------------------------------------------
1422
!     Call to see if two curves stored as point arrays overlap
1423
!     --------------------------------------------------------
1424
!
1425
         IMPLICIT NONE
1426
!
1427
!        ---------
1428
!        Arguments
1429
!        ---------
1430
!
1431
         INTEGER        :: nPts1, nPts2
1432
         REAL(KIND=RP)  :: testCurve1(3,0:nPts1), testCurve2(3,0:nPts2)
1433
         LOGICAL        :: theyCross
1434
!
1435
!        ---------------
1436
!        Local Variables
1437
!        ---------------
1438
!
1439
         REAL(KIND=RP)              :: intersection(6)
1440
         REAL(KIND=RP), ALLOCATABLE :: crv1inBox(:,:), crv2inBox(:,:)
×
1441
         INTEGER                    :: nptsC1, nptsC2
1442
         REAL(KIND=RP)              :: subBox1(6), subBox2(6)
1443

1444
         theyCross = .TRUE.
×
1445

1446
         ALLOCATE(crv1inBox(3,0:nPts1))
×
1447

1448
         CALL CollectArrayPointsInBox(array        = testCurve1,    &
1449
                                      bBox         = intersection,  &
1450
                                      collected    = crv1inBox,     &
1451
                                      nCollected   = nPtsC1,        &
1452
                                      collectedBox = subBox1)
×
1453

1454
         IF ( nPtsC1 == 0)     THEN ! No actual points in curve1 in the intersection box
×
1455
            theyCross = .FALSE.
×
1456
            RETURN
×
1457
         END IF
1458

1459
         ALLOCATE(crv2inBox(3,0:nPts2))
×
1460
         CALL CollectArrayPointsInBox(array        = testCurve2,    &
1461
                                      bBox         = intersection,  &
1462
                                      collected    = crv2inBox,     &
1463
                                      nCollected   = nPtsC2,         &
1464
                                      collectedBox = subBox2)
×
1465

1466
         IF ( nPtsC2 == 0 )     THEN ! No actual points in curve1 in the intersection box
×
1467
            theyCross = .FALSE.
×
1468
            RETURN
×
1469
         END IF
1470
!
1471
!        ---------------------------------------------------------------------------
1472
!        One last quick check: The boxes outlining the sets of points must intersect
1473
!        for there to be a crossing
1474
!        ---------------------------------------------------------------------------
1475
!
1476
         IF(.NOT.BBoxIntersects(BoxA = subBox1, BoxB = subBox2) )     THEN
×
1477
            theyCross = .FALSE.
×
1478
            RETURN
×
1479
         END IF
1480
!
1481
!        -------------------------------------------------
1482
!        Now we have to work... Go through the points and
1483
!        see if the curves actually cross
1484
!        -------------------------------------------------
1485
!
1486
         theyCross = TestPointsForCrossing( crv1inBox, nptsC1, crv2inBox, nptsC2 )
×
1487
         IF(theyCross)     RETURN
×
1488
         theyCross = TestPointsForCrossing( crv2inBox, nptsC2, crv1inBox, nptsC1 )
×
1489

1490
      END FUNCTION TwoCurvesAsPointsIntersect
×
1491
!
1492
!////////////////////////////////////////////////////////////////////////
1493
!
1494
      SUBROUTINE CollectArrayPointsInBox(array, bBox, collected, nCollected, collectedBox)
×
1495
         IMPLICIT NONE
1496
!
1497
!        ---------
1498
!        Arguments
1499
!        ---------
1500
!
1501
         REAL(KIND=RP), INTENT(IN)  :: array(:,0:)
1502
         REAL(KIND=RP), INTENT(OUT) :: collected(:,0:)
1503
         REAL(KIND=RP), INTENT(IN)  :: bBox(6)
1504
         INTEGER      , INTENT(OUT) :: nCollected
1505
         REAL(KIND=RP), INTENT(OUT) :: collectedBox(6)
1506
!
1507
!        ---------------
1508
!        Local variables
1509
!        ---------------
1510
!
1511
         INTEGER :: j
1512
         REAL(KIND=RP) :: xMin, xMax, yMin, yMax
1513

1514
         xMin =  HUGE(xMin)
×
1515
         xMax = -HUGE(xMax)
×
1516
         yMin =  HUGE(xMin)
×
1517
         yMax = -HUGE(xMax)
×
1518

1519
         nCollected = 0
×
1520
         collected  = 0.0_RP
×
1521
         DO j = 0, SIZE(array,2)-1
×
1522
            IF ( Point_IsInsideBox(p = array(:,j),bBox = bBox) )     THEN
×
1523
               collected(:,nCollected) = array(:,j)
×
1524

1525
               xMax = MAX(collected(1,nCollected),xMax)
×
1526
               xMin = MIN(collected(1,nCollected),xMin)
×
1527
               yMax = MAX(collected(2,nCollected),yMax)
×
1528
               yMin = MIN(collected(2,nCollected),yMin)
×
1529

1530
               nCollected = nCollected + 1
×
1531
            END IF
1532
         END DO
1533
         nCollected = MAX(nCollected - 1,0) ! Gives the upper bound on the array.
×
1534

1535
         collectedBox(BBOX_TOP)    = yMax
×
1536
         collectedBox(BBOX_BOTTOM) = yMin
×
1537
         collectedBox(BBOX_LEFT)   = xMin
×
1538
         collectedBox(BBOX_RIGHT)  = xMax
×
1539

1540
      END SUBROUTINE CollectArrayPointsInBox
×
1541
!
1542
!////////////////////////////////////////////////////////////////////////
1543
!
1544
      LOGICAL FUNCTION TestPointsForCrossing(c1, n1, c2, n2)
×
1545
         IMPLICIT NONE
1546
!
1547
!        ----------
1548
!        Arguments
1549
!        ----------
1550
!
1551
         INTEGER       :: n1, n2
1552
         REAL(KIND=RP) :: c1(3,0:n1), c2(3,0:n2)
1553
!
1554
!        ---------------
1555
!        Local variables
1556
!        ---------------
1557
!
1558
         INTEGER       :: i, j
1559
         REAL(KIND=RP) :: bBox(6)
1560

1561
         bBox = 0.0_RP
×
1562
         TestPointsForCrossing = .FALSE.
×
1563

1564
         DO i = 0, n1-1
×
1565

1566
            bBox(BBOX_LEFT)   = MIN(c1(1,i),c1(1,i+1))
×
1567
            bBox(BBOX_BOTTOM) = MIN(c1(2,i),c1(2,i+1))
×
1568
            bBox(BBOX_RIGHT)  = MAX(c1(1,i),c1(1,i+1))
×
1569
            bBox(BBOX_TOP)    = MAX(c1(1,i),c1(1,i+1))
×
1570

1571
            DO j = 0, n2
×
1572
              IF ( Point_IsInsideBox(p = c2(:,j),bBox = bBox) ) THEN
×
1573
                 TestPointsForCrossing = .TRUE.
×
1574
                 RETURN
×
1575
              END IF
1576
            END DO
1577
         END DO
1578

1579
      END FUNCTION TestPointsForCrossing
×
1580
!
1581
!////////////////////////////////////////////////////////////////////////
1582
!
1583
      SUBROUTINE CollectBoundaryAndInterfaceNodes(allNodesIterator,boundaryNodesList)
95✔
1584
         IMPLICIT NONE
1585
!
1586
!        ---------
1587
!        Arguments
1588
!        ---------
1589
!
1590
         TYPE (FTLinkedListIterator), POINTER :: allNodesIterator
1591
         CLASS(FTLinkedList)        , POINTER :: boundaryNodesList
1592
!
1593
!        ---------------
1594
!        Local variables
1595
!        ---------------
1596
!
1597
         TYPE (SMNode)  , POINTER :: currentNode => NULL()
1598
         CLASS(FTObject), POINTER :: obj => NULL()
1599
!
1600
!        ------------------------------------------------------
1601
!        Loop through all the nodes and add those whose
1602
!        distance to a boundary is zero to the boundaryNodeList
1603
!        ------------------------------------------------------
1604
!
1605
         CALL allNodesIterator % setToStart()
95✔
1606
         DO WHILE ( .NOT.allNodesIterator % isAtEnd() )
50,644✔
1607
            obj => allNodesIterator % object()
50,549✔
1608
            CALL castToSMNode(obj,currentNode)
50,549✔
1609
            IF ( IsOnBoundaryCurve(currentNode) .AND. &
50,549✔
1610
                 currentNode%distToBoundary == 0.0_RP )     THEN
1611
               CALL boundaryNodesList % add(obj)
11,549✔
1612
            END IF
1613
            CALL allNodesIterator % moveToNext()
50,549✔
1614
         END DO
1615

1616
      END SUBROUTINE CollectBoundaryAndInterfaceNodes
95✔
1617
!
1618
!////////////////////////////////////////////////////////////////////////
1619
!
1620
      SUBROUTINE SortBoundaryNodesToChains(nodesIterator, numBoundaryChains, chainNodesArray)
48✔
1621
!
1622
!     ---------------------------------------------------------------------------
1623
!     Gather up all the boundary nodes and arrange them in arrays by chainID. The
1624
!     returned chainNodesArray contains pointers to all the nodes that fall along
1625
!     a given curve chain. Thus, chainNodesArray(j) % array is an ordered array
1626
!     of all nodes along boundary chain j.
1627
!     ---------------------------------------------------------------------------
1628
!
1629
         IMPLICIT NONE
1630
!
1631
!        ---------
1632
!        Arguments
1633
!        ---------
1634
!
1635
         INTEGER                                 :: numBoundaryChains
1636
         TYPE(FTLinkedListIterator), POINTER     :: nodesIterator
1637
         TYPE(JaggedNodeArray)                   :: chainNodesArray(:)
1638
!
1639
!        ---------------
1640
!        Local Variables
1641
!        ---------------
1642
!
1643
         TYPE(LinkedListPtr)        , POINTER :: boundaryNodesArray(:)
1644
         CLASS(FTLinkedList)        , POINTER :: boundaryNodesList => NULL()
1645
         CLASS(FTLinkedList)        , POINTER :: list
1646
         CLASS(FTLinkedListIterator), POINTER :: boundaryNodesIterator
1647
         CLASS(FTLinkedListIterator), POINTER :: listIterator
1648
         TYPE (SMNode)              , POINTER :: currentNode => NULL()
1649
         CLASS(FTObject)            , POINTER :: obj => NULL()
1650
         CLASS(FTLinkedListRecord)  , POINTER :: previousRecord, currentRecord
1651
         INTEGER                              :: id, j
1652
         REAL(KIND=RP)                        :: prevT, currentT
1653
         LOGICAL                              :: reOrder
1654
!
1655
!
1656
!        -----------------------------------------------------
1657
!        Temporary storage for the nodes along each curve chain
1658
!        -----------------------------------------------------
1659
!
1660
         ALLOCATE(boundaryNodesArray(numBoundaryChains))
158✔
1661
         DO id = 1, numBoundaryChains
158✔
1662
            ALLOCATE(boundaryNodesArray(id) % list)
110✔
1663
            CALL boundaryNodesArray(id) % list % init()
158✔
1664
         END DO
1665
!
1666
!        ------------------------------
1667
!        Collect all the boundary nodes
1668
!        ------------------------------
1669
!
1670
         ALLOCATE(boundaryNodesList)
48✔
1671
         CALL boundaryNodesList % init()
48✔
1672
         CALL CollectBoundaryAndInterfaceNodes( nodesIterator, boundaryNodesList )
48✔
1673
!
1674
!        --------------------------------------------------------
1675
!        Separate into a single node list for each boundary chain
1676
!        --------------------------------------------------------
1677
!
1678
         ALLOCATE(boundaryNodesIterator)
48✔
1679
         CALL boundaryNodesIterator % initwithFTLinkedListClass(boundaryNodesList)
48✔
1680
         CALL boundaryNodesIterator % setToStart()
48✔
1681

1682
         DO WHILE ( .NOT.boundaryNodesIterator % isAtEnd() )
5,800✔
1683
            obj => boundaryNodesIterator % object()
5,752✔
1684
            CALL castToSMNode(obj,currentNode)
5,752✔
1685
            id = currentNode % bCurveChainID
5,752✔
1686
            CALL boundaryNodesArray(id) % list % add(obj)
5,752✔
1687
            CALL boundaryNodesIterator % moveToNext()
5,752✔
1688
         END DO
1689
!
1690
!        ------------------------------------------------------------------------
1691
!        The nodes in each chain will be ordered, but do not necessarily start
1692
!        at parameter value of zero. Since the nodes describe a closed (periodic)
1693
!        curve, we can just reset the pointers in the linked lists so that the
1694
!        parameter values are strictly increasing.
1695
!        ------------------------------------------------------------------------
1696
!
1697
         ALLOCATE(listIterator)
48✔
1698
         CALL listIterator % init()
48✔
1699
         DO id = 1, SIZE(boundaryNodesArray)
158✔
1700
            list => boundaryNodesArray(id) % list
110✔
1701
            CALL listIterator % setLinkedListClass(list)
110✔
1702

1703
            previousRecord => listIterator % currentRecord()
110✔
1704
            obj            => listIterator % object()
110✔
1705
            CALL castToSMNode(obj,currentNode)
110✔
1706
            prevT = currentNode % gWhereOnBoundary
110✔
1707
            CALL listIterator % moveToNext()
110✔
1708

1709
            DO WHILE ( .NOT.listIterator % isAtEnd() )
2,226✔
1710
               currentRecord => listIterator % currentRecord()
2,226✔
1711
               obj           => listIterator % object()
2,226✔
1712
               CALL castToSMNode(obj,currentNode)
2,226✔
1713
               currentT = currentNode % gWhereOnBoundary
2,226✔
1714
!
1715
!              -----------------------------------------------
1716
!              If the parameter, t, is starting over, then
1717
!              the list needs to be re-ordered. Stop here and
1718
!              do the re-ordering after exit
1719
!              -----------------------------------------------
1720
!
1721
               IF ( currentT < prevT )     THEN ! The parameter t is starting over
2,226✔
1722
                  reOrder = .TRUE.
110✔
1723
                  EXIT
110✔
1724
               END IF
1725

1726
               previousRecord => currentRecord
2,116✔
1727
               CALL listIterator % moveToNext()
2,116✔
1728
            END DO
1729

1730
            IF ( reOrder )     THEN
110✔
1731
               list % tail % next    => list % head
110✔
1732
               list % head           => currentRecord
110✔
1733
               list % tail           => previousRecord
110✔
1734
               previousRecord % next => NULL()
110✔
1735
            END IF
1736
!
1737
!           ------------------------------------
1738
!           Copy each chain into an array
1739
!           *Note that these are weak references
1740
!            and can go away at any time*
1741
!           ------------------------------------
1742
!
1743
            ALLOCATE( chainNodesArray(id) % array(list % count()))
5,862✔
1744
            CALL listIterator % setToStart()
110✔
1745
            j = 0
110✔
1746
            DO WHILE ( .NOT.listIterator % isAtEnd() )
5,910✔
1747
               j = j + 1
5,752✔
1748
               obj => listIterator % object()
5,752✔
1749
               CALL castToSMNode(obj,currentNode)
5,752✔
1750
               chainNodesArray(id) % array(j) % node => currentNode
5,752✔
1751
               CALL listIterator % moveToNext()
5,752✔
1752
            END DO
1753
         END DO
1754
!
1755
!        -------
1756
!        Cleanup
1757
!        -------
1758
!
1759
         DO id = 1, numBoundaryChains
158✔
1760
            CALL releaseFTLinkedListClass(boundaryNodesArray(id) % list)
158✔
1761
         END DO
1762
         DEALLOCATE( boundaryNodesArray )
48✔
1763

1764
         CALL releaseFTLinkedListIteratorClass(listIterator)
48✔
1765
         CALL releaseFTLinkedListIteratorClass(boundaryNodesIterator)
48✔
1766
         CALL releaseFTLinkedListClass(boundaryNodesList)
48✔
1767
!
1768
!         !DEBUG
1769
!         DO id = 1, numBoundaryChains
1770
!            DO j = 1, SIZE(chainNodesArray(id) % array)
1771
!            currentNode => chainNodesArray(id) % array(j) % node
1772
!            WRITE(0,*) currentNode % gWhereOnBoundary, currentNode % bCurveChainID, &
1773
!                       currentNode % bCurveID, currentNode % whereOnBoundary, currentNode % x(1:2)
1774
!            END DO
1775
!            WRITE(0,*)
1776
!         END DO
1777

1778
      END SUBROUTINE SortBoundaryNodesToChains
48✔
1779

1780
   END MODULE MeshBoundaryMethodsModule
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc