• 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

84.53
/Source/Mesh/MeshGeneratorMethods.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
!      MeshGeneratorMethods.f90
28
!      Created: August 21, 2013 1:01 PM
29
!      By: David Kopriva
30
!
31
!////////////////////////////////////////////////////////////////////////
32
!
33
   Module MeshGenerationMethods
34
      USE QuadTreeGridGeneratorModule
35
      USE MeshProjectClass
36
      USE MeshOperationsModule
37
      USE ProgramGlobals
38
      USE MeshBoundaryMethodsModule
39
      USE ErrorTypesModule
40
      USE MeshOutputMethods
41
      USE MeshSizerClass
42
      USE BoundaryErrorModule
43
      IMPLICIT NONE
44

45
!
46
!     ========
47
      CONTAINS
48
!     ========
49
!
50
!
51
!////////////////////////////////////////////////////////////////////////
52
!
53
      SUBROUTINE GenerateQuadMesh(project, errorCode)
24✔
54
         IMPLICIT NONE
55
!
56
!        ---------
57
!        Arguments
58
!        ---------
59
!
60
         CLASS(MeshProject), POINTER :: project
61
         INTEGER                     :: errorCode
62
!
63
!        ---------------
64
!        Local Variables
65
!        ---------------
66
!
67
         INTEGER :: k
68
         LOGICAL :: needsRemesh
69
         INTEGER :: numberOfTries = 3
70
         INTEGER :: j
71

72
         needsRemesh = .FALSE.
24✔
73
         
74
         DO j = 1, numberOfTries 
24✔
75
            CALL GenerateAQuadMesh( project, errorCode )
24✔
76
!
77
!           -----------------------------------
78
!           Gather and set boundary information
79
!           and from that, create boundary 
80
!           polynomial approximations to the 
81
!           boundary chains.
82
!           -----------------------------------
83
!
84
            CALL ComputeBoundaryApproximations(project, .FALSE.)
24✔
85
            CALL ComputeBoundaryErrors(project)
24✔
86
            CALL ResetInverseScales(project, needsRemesh)
24✔
87
!
88
!        -----------------------------------------------
89
!        If the mesh needs to be remeshed because errors
90
!        are too large someplace, do so
91
!        -----------------------------------------------
92
!
93
            IF ( .NOT. needsRemesh )     EXIT
24✔
94
            
NEW
95
            IF( j < numberOfTries) CALL ResetProject(project)
×
NEW
96
            IF(printMessage)       PRINT *, "Remeshing for accuracy..."
×
97
           
98
         END DO 
99
!
100
!        ------------------------------------------------------------------------
101
!        If there is a problem, usually it is because the initial background grid
102
!        is too large. Try again with a smaller background grid, just in case.
103
!        ------------------------------------------------------------------------
104
!
105
         IF ( errorCode > A_OK_ERROR_CODE )     THEN ! Try again at most two times
24✔
106
            DO k = 1, 2
×
107

108
               errorCode = A_OK_ERROR_CODE
×
109
               IF(printMessage)     THEN
×
110
                  PRINT *, "Background grid is too large. Trying again with 1/2 size" ! Throw exception here
×
111
               END IF
112

113
               project % backgroundParams % dx           =   project % backgroundParams % dx/2.0_RP
×
114
               project % backgroundParams % N            = 2*project % backgroundParams % N
×
115
               project % sizer % baseSize                = 0.5_RP*project % sizer % baseSize
×
116
               project % backgroundParams % backgroundGridSize = 0.5_RP*project % backgroundParams % backgroundGridSize
×
117

118
               CALL ResetProject(project)
×
119
               CALL clearBoundaryCurves(project % sizer)
×
NEW
120
               CALL BuildSizerBoundaryCurves(project)
×
121

122
               CALL GenerateAQuadMesh(project,errorCode)
×
123

124
               IF( errorCode == A_OK_ERROR_CODE)     EXIT
×
125
            END DO
126
        END IF
127
!
128
!       -------------------------------------------------------------
129
!       Re-compute the boundary polynomials with the optimized curves
130
!       if that has been requested
131
!       -------------------------------------------------------------
132
!
133
        CALL ResetProjectBoundaryObjects(project)
24✔
134
        CALL ComputeBoundaryApproximations(project, .TRUE.)
24✔
135

136
      END SUBROUTINE GenerateQuadMesh
24✔
137
!
138
!////////////////////////////////////////////////////////////////////////
139
!
140
      SUBROUTINE GenerateAQuadMesh(project, errorCode)
24✔
141
         USE MeshCleaner
142
         IMPLICIT NONE
143
!
144
!        ---------
145
!        Arguments
146
!        ---------
147
!
148
         CLASS(MeshProject), POINTER :: project
149
         INTEGER                     :: errorCode
150
!
151
!        ---------------
152
!        Local Variables
153
!        ---------------
154
!
155
         TYPE (SMModel)       , POINTER     :: model
156
!
157
         IF(PrintMessage) PRINT *, "Generate 2D mesh..."
24✔
158
!
159
!        --------------------------------------------------------------
160
!        Determining exterior quads requires a curve defined in terms
161
!        of an array. Convert and temporarily save the boundary curves.
162
!        ConvertBoundaryCurvesToArrays allocates and fills temporaries
163
!        in the baseMethods module.
164
!        --------------------------------------------------------------
165
!
166
!         CALL generateTemporaryBoundaryArrays( project % sizer )
167

168
         errorCode = A_OK_ERROR_CODE
24✔
169
         CALL GenerateQuadMeshFromGrid( project, errorCode )
24✔
170
         CALL trapExceptions !Abort on fatal exceptions
24✔
171
         IF(errorCode > A_OK_ERROR_CODE)     RETURN
24✔
172
!
173
!        ------------------------
174
!        Perform topology cleanup
175
!        ------------------------
176
!
177
         CALL PerformTopologyCleanup(project % mesh, errorCode)
24✔
178
         IF(errorCode > A_OK_ERROR_CODE)     RETURN
24✔
179
!
180
!        ------------------------
181
!        Smooth mesh if requested
182
!        ------------------------
183
!
184
         IF(Associated(project % smoother))     THEN
24✔
185
            IF(PrintMessage) PRINT *, "   Begin Smoothing..."
23✔
186
            CALL project % smoother % smoothMesh(  project % mesh, project % model, errorCode )
23✔
187
            IF(PrintMessage) PRINT *, "   Smoothing done."
23✔
188
         END IF
189
!
190
!        -------------
191
!        Clean up mesh
192
!        -------------
193
!
194
         IF(PrintMessage) PRINT *, "   Performing final mesh cleanup..."
24✔
195
            CALL PerformFinalMeshCleanup( project % mesh, project % model, errorCode )
24✔
196
            IF(errorCode > A_OK_ERROR_CODE)     RETURN
24✔
197
         IF(PrintMessage) PRINT *, "   Mesh cleanup done."
24✔
198
!
199
!        --------------------------------------
200
!        Smooth mesh one more time if requested
201
!        --------------------------------------
202
!
203
         IF(Associated(project % smoother))     THEN
24✔
204
            IF(PrintMessage) PRINT *, "   Begin Final Smoothing..."
23✔
205
            CALL project % smoother % smoothMesh(  project % mesh, project % model, errorCode )
23✔
206
            IF(errorCode > A_OK_ERROR_CODE)     RETURN
23✔
207
            IF(PrintMessage) PRINT *, "   final Smoothing done."
23✔
208
         END IF
209
!
210
!        ----------------------------------------------------------
211
!        If this is a multi-material mesh, then set each element's
212
!        materialID and materialName
213
!        ----------------------------------------------------------
214
!
215
         IF ( ASSOCIATED(interfaceCurves) .AND. &
24✔
216
            project % runParams % meshFileFormat == ISM_MM)     THEN
217
            CALL SetMaterialProperties(mesh = project % mesh)
1✔
218
         END IF
219
!
220
!        -------------------------------------
221
!        We no longer need the boundary arrays
222
!        -------------------------------------
223
!
224
         CALL destroyTemporaryBoundaryArrays
24✔
225

226
         IF(ALLOCATED(aPointInsideTheCurve)) DEALLOCATE(aPointInsideTheCurve)
24✔
227

228
      END SUBROUTINE GenerateAQuadMesh
24✔
229
!
230
!////////////////////////////////////////////////////////////////////////
231
!
232
      SUBROUTINE GenerateQuadMeshFromGrid( project, errorCode )
24✔
233
      USE BoundaryEdgeCleaningModule
24✔
234
!
235
!     ------------------------------------------------------------
236
!     This is the main procedure that actually generates the mesh.
237
!     ------------------------------------------------------------
238
!
239
      IMPLICIT NONE
240
!
241
!     ---------
242
!     Arguments
243
!     ---------
244
!
245
      CLASS( MeshProject ), POINTER :: project
246
      INTEGER                       :: errorCode
247
!
248
!     ---------------
249
!     Local variables
250
!     ---------------
251
!
252
      TYPE (QuadTreeGrid), POINTER :: grid      => NULL()
253
      TYPE (SMMesh)      , POINTER :: mesh      => NULL()
254
      TYPE (SMModel)     , POINTER :: model     => NULL()
255
      TYPE (MeshSizer)   , POINTER :: sizer     => NULL()
256
      CLASS(FTObject)    , POINTER :: obj       => NULL()
257
      CLASS(FTLinkedList), POINTER :: list      => NULL()
258
      TYPE (FTException) , POINTER :: exception => NULL()
259

260
      INTEGER                      :: numberOfBoundaries,numBoundaryEdgeLists
261
      INTEGER                      :: j
262
      INTEGER                      :: idOfOuterBoundary
263

264
      TYPE (FTLinkedListIterator), POINTER :: iterator
265
      CLASS(SMChainedCurve)      , POINTER :: chain
266
!
267
!     ---------------------
268
!     Generate the quadtree
269
!     ---------------------
270
!
271
      IF(PrintMessage) PRINT *, "   Generate quadtree..."
24✔
272
         CALL GenerateGridWithSizerAndType( project % grid, project % sizer, &
273
                                            project % meshParams % meshType)
24✔
274
         project % numberOfLevelsUsed = highestLevel
24✔
275
         IF(catch() .AND. (maximumErrorSeverity() == FT_ERROR_FATAL)) RETURN
24✔
276
      IF(PrintMessage) PRINT *, "   Quadtree grid generated"
24✔
277
!
278
!     ---------------
279
!     Create the mesh
280
!     ---------------
281
!
282
      ALLOCATE( project % mesh )
24✔
283
      CALL project % mesh % init( )
24✔
284

285
      mesh  => project % mesh
24✔
286
      model => project % model
24✔
287
      grid  => project % grid
24✔
288
      sizer => project % sizer
24✔
289
!
290
!     ----------------------------------------------------------
291
!     Save the polynomial order and the curve names to the mesh.
292
!     Material names are associated with the bounding interface
293
!     curves.
294
!     ----------------------------------------------------------
295
!
296
      mesh % polynomialOrder =  project % runParams % polynomialOrder
24✔
297
      numberOfBoundaries = model % numberOfInnerCurves + model % numberOfOuterCurves &
298
                         + model % numberOfInterfaceCurves
24✔
299

300
      IF ( model % numberOfInterfaceCurves > 0 )     THEN
24✔
301

302
            ALLOCATE(CHARACTER(SM_CURVE_NAME_LENGTH) :: mesh % materialNameForID(0:numberOfBoundaries))
1✔
303
            mesh % materialNameForID = project % backgroundMaterialName
5✔
304

305
            iterator => model % interfaceBoundariesIterator
1✔
306
            CALL iterator % setToStart()
1✔
307
            DO WHILE( .NOT.iterator % isAtEnd() )
3✔
308
               obj => iterator % object()
2✔
309
               CALL castToSMChainedCurve(obj,chain)
2✔
310

311
               mesh % materialNameForID(chain % id()) = chain % curveName()
2✔
312

313
               CALL iterator % moveToNext()
2✔
314
            END DO
315
      END IF
316
!
317
!     -------------------------------------
318
!     Create the nodes, elements, and edges
319
!     from the nested grid
320
!     -------------------------------------
321
!
322
      IF(PrintMessage) PRINT *, "   Generate nodes and elements..."
24✔
323
         CALL GenerateNodesAndElements( mesh, grid )
24✔
324
      IF(PrintMessage) PRINT *, "   Nodes and elements were generated..."
24✔
325
!
326
!     ---------------------------------------------
327
!     Free up memory since grid is no longer needed
328
!     ---------------------------------------------
329
!
330
      CALL releaseGrid(grid)
24✔
331
      NULLIFY(project % grid)
24✔
332
!
333
!     --------------------------------------------------------------
334
!     Now make the mesh conform to exterior or interface boundaries.
335
!
336
!     Determining exterior quads requires a curve defined in terms
337
!     of an array. Convert and temporarily save the boundary curves.
338
!     ConvertBoundaryCurvesToArrays allocates and fills temporaries
339
!     in the baseMethods module.
340
!     --------------------------------------------------------------
341
!
342
      CALL generateTemporaryBoundaryArrays( sizer )
24✔
343
!
344
!     --------------------------------------------
345
!     Mark the elements outside or near boundaries
346
!     then remove.
347
!     --------------------------------------------
348
!
349
      IF( numberOfBoundaries > 0 )     THEN
24✔
350
         ALLOCATE( aPointInsideTheCurve(3,numberOfBoundaries) )
21✔
351
         ALLOCATE( mesh % curveTypeForID(numberOfBoundaries) )
21✔
352
         CALL flagBoundaryTypes(mesh % curveTypeForID)
21✔
353
      END IF
354

355
      CALL MarkExteriorElements ( mesh, project % backgroundParams )
24✔
356
      CALL MarkInterfaceElements( mesh )
24✔
357
!
358
!     ----------------------------------------
359
!     Actually do the deletion. At this point,
360
!     any temoraries are out of sync.
361
!     ----------------------------------------
362
!
363
      CALL DoLazyDelete( mesh )
24✔
364
!
365
!     -------------------------------------------
366
!     Prepare for generating boundary elements by
367
!     building, collecting and ordering boundary
368
!     edges
369
!     -------------------------------------------
370
!
371
      CALL mesh % buildEdgeList()
24✔
372

373
      IF( model % curveCount == 0 )     RETURN
24✔
374

375
      numBoundaryEdgeLists = model%numberOfInnerCurves + &
376
                             model%numberOfOuterCurves + &
377
                             model%numberOfInterfaceCurves
21✔
378

379
      CALL AllocateBoundaryEdgesArray(numBoundaryEdgeLists)
21✔
380
      CALL CollectBoundaryEdges( mesh, errorCode )
21✔
381

382
      IF( errorCode > A_OK_ERROR_CODE)     RETURN
21✔
383

384
      CALL OrderBoundaryEdges( mesh )
21✔
385

386
      IF ( catch(WARNING_ERROR_EXCEPTION) )     THEN  ! Pass the error up the chain
21✔
387
         exception => errorObject()
×
388
         CALL throw(exception)
×
389
         RETURN
×
390
      END IF
391
!
392
!     -------------------------------------------------------
393
!     Smooth the edges along exterior and interior boundaries
394
!     before generating the boundary elements.
395
!     -------------------------------------------------------
396
!
397
      CALL smoothBoundaryEdges
21✔
398
!
399
!     -------------------------------------------------------------
400
!     Associate the boundary edges with elements and see if any
401
!     elements must be eliminated for having nodes too close to
402
!     a boundary, etc.
403
!     -------------------------------------------------------------
404
!
405
      idOfOuterBoundary = UNDEFINED
21✔
406
      IF( ASSOCIATED( sizer % outerBoundary ) ) idOfOuterBoundary = sizer % outerBoundary % id
21✔
407

408
      CALL LocateEdgeImagesOnBoundaries( mesh, model, idOfOuterBoundary, skipInterfaces = .FALSE. )
21✔
409

410
      IF ( catch(FATAL_ERROR_EXCEPTION) )     THEN  ! Pass the error up the chain
21✔
411
         CALL destroyTemporaryBoundaryArrays
×
412
         exception => errorObject()
×
413
         CALL throw(exception)
×
414
         RETURN
×
415
      END IF
416

417
      CALL CleanUpBoundaryCurves( mesh, model, errorCode )
21✔
418
      IF(errorCode > A_OK_ERROR_CODE)     RETURN
21✔
419
!
420
!     ------------------------------------------
421
!     The edge lists are also no longer in sync.
422
!     They may have had edges added or deleted.
423
!     Re-configure and smooth the boundary edges
424
!     ------------------------------------------
425
!
426
      CALL OrderBoundaryEdges( mesh )
21✔
427
      CALL smoothBoundaryEdges
21✔
428
!
429
!     ----------------------------------------------------------------
430
!     Next, find where the edges should project onto the
431
!     boundary. Try to keep them uniformly distributed if possible
432
!     by moving the proposed node locations along the boundary, except
433
!     for the end nodes, which will be attached to corners.
434
!     ----------------------------------------------------------------
435
!
436
      CALL LocateEdgeImagesOnBoundaries( mesh, model, idOfOuterBoundary, skipInterfaces = .true. )
21✔
437
      IF ( catch(FATAL_ERROR_EXCEPTION) )     THEN  ! Pass the error up the chain
21✔
438
         CALL destroyTemporaryBoundaryArrays
×
439
         exception => errorObject()
×
440
         CALL throw(exception)
×
441
         RETURN
×
442
      END IF
443

444
      DO j = 1, boundaryEdgesArray % COUNT()
76✔
445
         obj => boundaryEdgesArray % objectAtIndex(j)
55✔
446
         CALL cast(obj,list)
55✔
447
         CALL FlagEndNodes( list, model )
55✔
448
         CALL SmoothBoundaryLocations( list, model )
76✔
449
      END DO
450
!
451
!     ------------------------------------------
452
!     Create new elements by projecting onto the
453
!     boundary curves.
454
!     ------------------------------------------
455
!
456
      DO j = 1, boundaryEdgesArray % COUNT()
76✔
457
         IF( boundaryEdgesType(j) == INTERFACE_EDGES ) CYCLE
55✔
458

459
         obj => boundaryEdgesArray % objectAtIndex(j)
53✔
460
         CALL cast(obj,list)
53✔
461
         CALL GenerateBoundaryElements( mesh, model, list )
74✔
462
      END DO
463
!
464
!     -------------------------------
465
!     The edges are no longer in sync
466
!     -------------------------------
467
!
468
      CALL mesh % destroyEdgeArrays()
21✔
469
      CALL DoLazyDelete( mesh )
21✔
470
      CALL unmarkNodesNearBoundaries( mesh % nodesIterator )
21✔
471
      CALL mesh % syncEdges()
21✔
472
      CALL mesh % renumberAllLists()
21✔
473

474
      IF(PrintMessage) PRINT *, "   Nodes and elements generated"
21✔
475

476
      END SUBROUTINE GenerateQuadMeshFromGrid
24✔
477
!
478
!////////////////////////////////////////////////////////////////////////
479
!
480
      RECURSIVE SUBROUTINE GenerateNodesAndElements( mesh, grid )
2,861✔
481
      IMPLICIT NONE
482
!
483
!     ---------
484
!     Arguments
485
!     ---------
486
!
487
      TYPE (QuadTreeGrid), POINTER :: grid
488
      TYPE (SMMesh)      , POINTER :: mesh
489
!
490
!     ---------------
491
!     Local variables
492
!     ---------------
493
!
494
      INTEGER                        :: i, j, k, N, M
495
      INTEGER                        :: elementID
496
      TYPE (SMElement), POINTER      :: e   => NULL()
497
      CLASS(FTObject) , POINTER      :: obj => NULL()
498
      TYPE( SMNodePtr), DIMENSION(4) :: eNodes
14,305✔
499
!
500
!     -------------------------------------------------------
501
!     Top level operations: Remove flags and number the nodes
502
!     -------------------------------------------------------
503
!
504
      IF( grid % level == 0 ) THEN
2,861✔
505
         CALL AssignNodeIdsForGrid_( grid )
24✔
506
         CALL FlagNodeIds( grid, .false. )
24✔
507
      END IF
508
!
509
!     ---------------------------------------------
510
!     Generate the nodes: For now this makes copies
511
!     ---------------------------------------------
512
!
513
      N = grid % N(1)
2,861✔
514
      M = grid % N(2)
2,861✔
515
!
516
!     ----------------------------------------------------------------------------
517
!     For grid: Write out nodes in the order in which they were created <=> nodeID
518
!     Flag with a sign change to ignore aliases as having already been written out.
519
!     ----------------------------------------------------------------------------
520
!
521
      DO j = 0, M
11,751✔
522
         DO i = 0, N
47,434✔
523
            IF(.NOT.ASSOCIATED(grid % nodes(i,j) % node) )            CYCLE
35,683✔
524
            IF( grid % nodes(i,j) % node % refCount() == 1 )          CYCLE  ! => Only the main grid, not quads references this
33,233✔
525
            IF( grid % nodes(i,j) % node % activeStatus == REMOVE )   CYCLE  ! Marked as outside
33,233✔
526
            IF( grid % nodes(i,j) % node % id > 0 )     THEN
42,123✔
527

528
               obj => grid % nodes(i,j) % node
17,485✔
529
               CALL mesh % nodes % add(obj)
17,485✔
530
               grid % nodes(i,j) % node % id = -ABS(grid % nodes(i,j) % node % id)
17,485✔
531
            END IF
532

533
         END DO
534
      END DO
535
!
536
!     -------------------------------------
537
!     Now generate the element connectivity
538
!     -------------------------------------
539
!
540
      DO j = 1, M
8,890✔
541
         DO i = 1, N
29,626✔
542
            IF ( ASSOCIATED(grid % quads(i,j) % quad) )     THEN
26,765✔
543
               IF ( ASSOCIATED(grid % children(i,j) % grid) )  CYCLE
19,511✔
544
               elementID = mesh % newElementID()
16,674✔
545
               DO k = 1, 4
83,370✔
546
                  eNodes(k) % node => grid % quads(i,j) % quad % nodes(k) % node
83,370✔
547
               END DO
548
               ALLOCATE(e)
16,674✔
549
               CALL e % initWithNodesIDAndType(eNodes, elementID, QUAD )
16,674✔
550
               obj => e
16,674✔
551
               CALL mesh % elements % add(obj)
16,674✔
552
               CALL releaseSMElement(e)
16,674✔
553
            END IF
554
         END DO
555
      END DO
556
!
557
!     ------------
558
!     For children
559
!     ------------
560
!
561
      IF( ASSOCIATED(grid % children) )     THEN
2,861✔
562
         DO j = 1, M
8,890✔
563
            DO i = 1, N
29,626✔
564
               IF( ASSOCIATED( grid % children(i,j) % grid ) ) &
20,736✔
565
                   CALL GenerateNodesAndElements( mesh, grid % children(i,j) % grid )
8,866✔
566
            END DO
567
         END DO
568
      END IF
569
!
570
!     --------------------
571
!     Remove node flagging
572
!     --------------------
573
!
574
      IF( grid % level == 0 ) CALL FlagNodeIds( grid, .false. )
2,861✔
575

576
      END SUBROUTINE GenerateNodesAndElements
24✔
577
!
578
!////////////////////////////////////////////////////////////////////////
579
!
580
      SUBROUTINE GenerateBoundaryElements( mesh, model, list )
53✔
581
         USE MeshOutputMethods, ONLY: WriteSkeletonToTecplot
582
         USE, INTRINSIC :: iso_fortran_env, only : stderr => ERROR_UNIT
583
         IMPLICIT NONE
584
!
585
!        ---------
586
!        Arguments
587
!        ---------
588
!
589
         TYPE (SMMesh)      , POINTER :: mesh
590
         TYPE (SMModel)     , POINTER :: model
591
         CLASS(FTLinkedList), POINTER :: list
592
!
593
!        ---------------
594
!        Local Variables
595
!        ---------------
596
!
597
         TYPE(SMNodePtr), DIMENSION(:), POINTER :: nodeArray => NULL()
598

599
         INTEGER, EXTERNAL :: Loop
600

601

602
         TYPE (SMNode)  , POINTER       :: node => NULL(), prevNode => NULL(), jointNode => NULL(), nextNode => NULL()
603
         TYPE (SMNode)  , POINTER       :: startNode => NULL()
604
         TYPE(SMNodePtr), DIMENSION(2)  :: newNodes
159✔
605
         TYPE(SMNodePtr), DIMENSION(2)  :: boundaryEdgeNodes
159✔
606
         TYPE(SMNodePtr), DIMENSION(4)  :: elementNodes
265✔
607

608
         CLASS(SMCurve)       , POINTER :: cStart => NULL(), cEnd => NULL()
609
         CLASS(SMChainedCurve), POINTER :: chain => NULL()
610
         TYPE (SMElement)     , POINTER :: e => NULL()
611
         CLASS(FTObject)      , POINTER :: obj => NULL()
612

613
         INTEGER                        :: k, nodeArraySize
614
         INTEGER                        :: jointType, bCurveSide
615

616
         REAL(KIND=RP)                  :: t, p(3)
617
         REAL(KIND=RP)                  :: tStart, tEnd
618
!
619
!        ---------
620
!        Externals
621
!        ---------
622
!
623
!
624
!        ---------------------------------------
625
!        Step through each edge on this boundary
626
!        and add a new node along the boundary
627
!        itself to make new elements.
628
!        ---------------------------------------
629
!
630
         nodeArray     => GatheredNodes( list )
53✔
631
         nodeArraySize = SIZE(nodeArray)
53✔
632
!
633
!        --------------------------
634
!        Get the first node started
635
!        --------------------------
636
!
637
         node       => nodeArray(1) % node
53✔
638
         bCurveSide =  node % bCurveSide
53✔
639
         t          =  node % whereOnBoundary
53✔
640
         cStart     => model % curveWithID(node % bCurveID, chain)
53✔
641

642
         ALLOCATE(node)
53✔
643
         CALL initBoundaryNode( cStart, chain, t, bCurveSide, mesh % newNodeID(), node )
53✔
644
         obj => node
53✔
645
         CALL mesh % nodes % add(obj)
53✔
646
         CALL releaseSMNode(node)
53✔
647
         prevNode  => node
53✔
648
         startNode => node
53✔
649
!
650
!        ------------------------
651
!        Now work around the loop
652
!        ------------------------
653
!
654
         k = 1
53✔
655
         DO WHILE ( k <= nodeArraySize )
2,851✔
656

657
            IF ( k < nodeArraySize )     THEN
2,798✔
658
               jointType = nodeArray(k+1) % node % nodeType
2,745✔
659
            ELSE
660
               jointType = nodeArray(1) % node % nodeType
53✔
661
            END IF
662

663
            SELECT CASE ( jointType )
2,757✔
664

665
               CASE( NONE, ROW_SIDE )
666
!
667
!                 ---------------------------------------------------------------
668
!                 Create one element by projecting the edge to the boundary curve
669
!                 ---------------------------------------------------------------
670
!
671
                  newNodes(1) % node          => prevNode
2,757✔
672
                  boundaryEdgeNodes(1) % node => nodeArray(k) % node
2,757✔
673
!
674
!                 -----------------------------------------------------------------
675
!                 Before the end of the loop, create a new node along the boundary,
676
!                 otherwise, wrap around to the beginning.
677
!                 -----------------------------------------------------------------
678
!
679
                  IF( k < nodeArraySize )     THEN
2,757✔
680
                     node                        => nodeArray(k+1) % node
2,704✔
681
                     boundaryEdgeNodes(2) % node => nodeArray(k+1) % node
2,704✔
682
                     t                           =  node % whereOnBoundary
2,704✔
683
                     cEnd                        => model % curveWithID(node % bCurveID, chain)
2,704✔
684

685
                     ALLOCATE(node)
2,704✔
686
                     CALL initBoundaryNode( cEnd, chain, t, bCurveSide, mesh % newNodeID(), node )
2,704✔
687

688
                     newNodes(2) % node => node
2,704✔
689
                     obj => node
2,704✔
690
                     CALL mesh % nodes % add(obj)
2,704✔
691
                     CALL releaseSMNode(node)
2,704✔
692
                  ELSE
693
                     boundaryEdgeNodes(2) % node => nodeArray(1) % node
53✔
694
                     newNodes(2) % node          => startNode
53✔
695
                  END IF
696
!
697
!                 ----------------------------------------------------
698
!                 Create element and add element and nodes to the mesh
699
!                 ----------------------------------------------------
700
!
701
                  e   => boundaryElementForNodes( mesh % newElementID(), boundaryEdgeNodes, newNodes )
2,757✔
702
                  obj => e
2,757✔
703
                  CALL mesh % elements % add(obj)
2,757✔
704
                  CALL releaseSMElement(e)
2,757✔
705
                  prevNode => newNodes(2) % node
2,757✔
706

707
               CASE( ROW_END )
708
!
709
!                 ---------------------------------------------------------------
710
!                 A row-end sees the insertion of at least three elements. One is
711
!                 a wedge element whose corner is at the joint between the two
712
!                 curves. The (small) angle is not split this way.
713
!
714
!                 First Projection Element - like a row side
715
!                 ---------------------------------------------------------------
716
!
717
                  newNodes(1) % node          => prevNode ! first corner node
19✔
718
                  boundaryEdgeNodes(1) % node => nodeArray(k) % node
19✔
719
                  IF( k < nodeArraySize )     THEN
19✔
720
                     jointNode => nodeArray(k+1) % node
19✔
721
                  ELSE ! Wrap around
722
                     jointNode => nodeArray(1) % node
×
723
                  END IF
724
                  boundaryEdgeNodes(2) % node => jointNode
19✔
725
                  p = jointNode % x
76✔
726

727
                  tStart = nodeArray(k) % node % whereOnBoundary
19✔
728
                  tEnd   = 1.0_RP
19✔
729
                  cStart => model % curveWithID(nodeArray(k) % node % bCurveID, chain)
19✔
730

731
                  t = 0.5_RP*(tStart + tEnd)
19✔
732

733
                  ALLOCATE(node)
19✔
734
                  CALL initBoundaryNode( cStart, chain, t, bCurveSide, mesh % newNodeID(), node )
19✔
735

736
                  newNodes(2) % node => node
19✔
737
                  obj => node
19✔
738
                  CALL mesh % nodes % add(obj)
19✔
739
                  CALL releaseSMNode(node)
19✔
740
!
741
                  e => boundaryElementForNodes( mesh % newElementID(), boundaryEdgeNodes, newNodes )
19✔
742
                  obj => e
19✔
743
                  CALL mesh % elements % add(obj)
19✔
744
                  CALL releaseSMElement(e)
19✔
745
                  prevNode => newNodes(2) % node
19✔
746
!
747
!                 -------------
748
!                 Wedge Element
749
!                 -------------
750
!
751
                  elementNodes(1) % node => jointNode
19✔
752
                  elementNodes(2) % node => prevNode
19✔
753
!
754
!                 ------------
755
!                 Joint corner
756
!                 ------------
757
!
758
                  t    =  1.0_RP
19✔
759

760
                  ALLOCATE(node)
19✔
761
                  CALL initBoundaryNode( cStart, chain, t, bCurveSide, mesh % newNodeID(), node )
19✔
762
                  elementNodes(3) % node => node
19✔
763
                  obj => node
19✔
764
                  CALL mesh % nodes % add(obj)
19✔
765
                  CALL releaseSMNode(node)
19✔
766
                  elementNodes(3) % node % nodeType = ROW_END
19✔
767
!
768
!                 ----------------------------------------------
769
!                 Closest point on the *next* curve in the chain
770
!                 ----------------------------------------------
771
!
772
                  IF ( k+2 <= nodeArraySize )     THEN
19✔
773
                     nextNode => nodeArray(k+2) % node
18✔
774
                  ELSE
775
                     nextNode => nodeArray(1) % node !Wrap around
1✔
776
                  END IF
777

778
                  cEnd   => model % curveWithID(nextNode % bCurveID, chain)
19✔
779
                  tStart = 0.0_RP
19✔
780
                  tEnd   = nextNode % whereOnBoundary ! No further than the next node along the chain
19✔
781

782
                  t = 0.5_RP*(tStart + tEnd)
19✔
783

784
                  ALLOCATE(node)
19✔
785
                  CALL initBoundaryNode( cEnd, chain, t, bCurveSide, mesh % newNodeID(), node )
19✔
786

787
                  elementNodes(4) % node => node
19✔
788
                  obj => node
19✔
789
                  CALL mesh % nodes % add(obj)
19✔
790
                  CALL releaseSMNode(node)
19✔
791
!
792
!                 ----------------------------------------
793
!                 Create wedge element and add to the mesh
794
!                 ----------------------------------------
795
!
796
                  e   => boundaryElementFor4Nodes( mesh % newElementID(), elementNodes )
19✔
797
                  obj => e
19✔
798
                  CALL mesh % elements % add(obj)
19✔
799
                  CALL releaseSMElement(e)
19✔
800
!
801
!                 ----------------------------------------------------------------
802
!                 Prepare for the next element, which should be a row-side element
803
!                 ----------------------------------------------------------------
804
!
805
                  prevNode => elementNodes(4) % node
19✔
806
!
807

808
               CASE( ROW_CORNER )
809
!
810
!                 -----------------------------------------------------
811
!                 A row corner node needs a single wedge element
812
!                 to avoid creating triangles. The wedge element
813
!                 uses the previous and next nodes along the boundary
814
!                 edge plus the reversal point along the boundary. This
815
!                 requires the prev % node % x to be moved to that point.
816
!                 -----------------------------------------------------
817
!
818
                  elementNodes(1) % node => nodeArray(k) % node
17✔
819
                  IF ( k < nodeArraySize )     THEN
17✔
820
                     elementNodes(4) % node => nodeArray(k+1) % node
17✔
821
                  ELSE
822
                     elementNodes(4) % node => nodeArray(1) % node
×
823
                  END IF
824
                  IF ( k < nodeArraySize-1 )     THEN
17✔
825
                     elementNodes(3) % node => nodeArray(k+2) % node
17✔
826
                  ELSE IF ( k == nodeArraySize-1 )     THEN
×
827
                     elementNodes(3) % node => nodeArray(1) % node
×
828
                  ELSE
829
                     elementNodes(3) % node => nodeArray(2) % node
×
830
                  END IF
831
!
832
!                 -------------------------------------------------
833
!                 Find the location of the joint along the boundary
834
!                 and move the prevNode position to that point. Use
835
!                 that as a corner in the wedge element.
836
!                 -------------------------------------------------
837
!
838
                  cStart => model % curveWithID(nodeArray(k) % node % bCurveID, chain)
17✔
839
                  p                           = cStart % positionAt(1.0_RP)
68✔
840
                  prevNode % x                = p
68✔
841
                  prevNode % whereOnBoundary  = 1.0_RP
17✔
842
                  prevNode % gWhereOnBoundary = chain % ChainTForCurveTInCurve( 1.0_RP, cStart )
17✔
843
                  prevNode % nodeType         = ROW_CORNER
17✔
844

845
                  elementNodes(2) % node => prevNode
17✔
846

847
                  e => boundaryElementFor4Nodes( mesh % newElementID(), elementNodes )
17✔
848
                  obj => e
17✔
849
                  CALL mesh % elements % add(obj)
17✔
850
                  CALL releaseSMElement(e)
17✔
851
!
852
!                 ----------------------------------------
853
!                 Get ready to move on - skip the k+1 node
854
!                 ----------------------------------------
855
!
856
                  prevNode => elementNodes(2) % node
17✔
857
                  k = k + 1
17✔
858

859
               CASE( ROW_REVERSAL )
860
!
861
!                 --------------------------------------------------------------
862
!                 A Row reversal node needs TWO elements to be created from four
863
!                 consecutive edges.
864
!                 --------------------------------------------------------------
865
!
866
                  elementNodes(1) % node => nodeArray(k) % node
5✔
867
                  elementNodes(2) % node => nodeArray(Loop(k+1,nodeArraySize)) % node
5✔
868
                  elementNodes(3) % node => nodeArray(Loop(k+2,nodeArraySize)) % node
5✔
869
!
870
!                 -------------------------------------------------
871
!                 Find the location of the joint along the boundary
872
!                 and move the prevNode position to that point. Use
873
!                 that as a corner in the wedge element.
874
!                 -------------------------------------------------
875
!
876
                  cStart => model % curveWithID(nodeArray(k) % node % bCurveID, chain)
5✔
877
                  p                           = cStart % positionAt(1.0_RP)
20✔
878
                  prevNode % x                = p
20✔
879
                  prevNode % whereOnBoundary  = 1.0_RP
5✔
880
                  prevNode % gWhereOnBoundary = chain % ChainTForCurveTInCurve( 1.0_RP, cStart )
5✔
881
                  prevNode % nodeType         = ROW_REVERSAL
5✔
882
                  elementNodes(4) % node => prevNode
5✔
883
                  e => boundaryElementFor4Nodes( mesh % newElementID(), elementNodes )
5✔
884
                  obj => e
5✔
885
                  CALL mesh % elements % add(obj)
5✔
886
                  CALL releaseSMElement(e)
5✔
887
!
888
!                 ----------------------------------------------
889
!                 Get ready to move on - skip the next two nodes
890
!                 ----------------------------------------------
891
!
892
                  prevNode => elementNodes(4) % node
5✔
893
                  k = k + 2
5✔
894
!
895
!                 --------------------
896
!                 Second Wedge Element
897
!                 --------------------
898
!
899
                  elementNodes(1) % node => prevNode
5✔
900
                  elementNodes(2) % node => nodeArray(k) % node
5✔
901
                  elementNodes(3) % node => nodeArray(Loop(k+1,nodeArraySize)) % node
5✔
902
                  elementNodes(4) % node => nodeArray(Loop(k+2,nodeArraySize)) % node
5✔
903

904
                  e => boundaryElementFor4Nodes( mesh % newElementID(), elementNodes )
5✔
905
                  obj => e
5✔
906
                  CALL mesh % elements % add(obj)
5✔
907
                  CALL releaseSMElement(e)
5✔
908

909
                  prevNode => elementNodes(1) % node
5✔
910
                  k = k + 1
5✔
911

912
               CASE DEFAULT
913
                 WRITE(stderr,*) " "
×
914
                 WRITE(stderr,*) "**************************************************************************"
×
915
                 WRITE(stderr,*) "An unknown row type has appeared in GenerateBoundaryElements"
×
916
                 WRITE(stderr,*) "Plot the file 'DebugPlot.tec' to check on the mesh topology"
×
917
                 WRITE(stderr,*) "**************************************************************************"
×
918
                 WRITE(stderr,*) " "
×
919
                  CALL WriteSkeletonToTecplot(mesh = mesh,fName = "DebugPlot.tec")
×
920
                  ERROR STOP "Meshing Terminated. See stderr"
2,798✔
921
            END SELECT
922
            k = k + 1
2,798✔
923
         END DO
924

925
         DEALLOCATE( nodeArray )
53✔
926

927
      END SUBROUTINE GenerateBoundaryElements
53✔
928
!@mark -
929
!
930
!////////////////////////////////////////////////////////////////////////
931
!
932
      SUBROUTINE flagBoundaryTypes(curveTypeForID)
21✔
933
!
934
!     ------------------------------------------------------------------------
935
!     Assign a boundary type (EXTERIOR, INTERIOR, INTERFACE) according to the
936
!     curve's id
937
!     ------------------------------------------------------------------------
938
!
939
         IMPLICIT NONE
940
         INTEGER, DIMENSION(:) :: curveTypeForID
941
         INTEGER :: k
942

943
         IF(ASSOCIATED( outerBoundaryCurve ))     THEN
21✔
944
            curveTypeForID(outerBoundaryCurve % id) = OUTER
20✔
945
         END IF
946

947
          IF(ASSOCIATED( interiorCurves ))     THEN
21✔
948
            DO k = 1, SIZE(interiorCurves)
43✔
949
               curveTypeForID(interiorCurves(k) % curveArray % id) = INNER
43✔
950
            END DO
951
         END IF
952

953
         IF( ASSOCIATED( interfaceCurves ) )     THEN
21✔
954
             DO k = 1, SIZE(interfaceCurves)
3✔
955
               curveTypeForID(interfaceCurves(k) % curveArray % id) = INTERIOR_INTERFACE
3✔
956
            END DO
957
         END IF
958

959
      END SUBROUTINE flagBoundaryTypes
53✔
960
!
961
!////////////////////////////////////////////////////////////////////////
962
!
963
      SUBROUTINE MarkExteriorElements( mesh, backgroundParams )
24✔
964
      USE Geometry
965
      IMPLICIT NONE
966
!
967
!        ---------
968
!        Arguments
969
!        ---------
970
!
971
         TYPE (SMMesh)                 , POINTER :: mesh
972
         TYPE(BackgroundGridParameters)          :: backgroundParams
973
!
974
!        ---------------
975
!        Local variables
976
!        ---------------
977
!
978
         CLASS(FTLinkedList)        , POINTER :: elements => NULL()
979
         TYPE (SMElement)           , POINTER :: e => NULL()
980
         CLASS(SegmentedCurveArray) , POINTER :: curveArray => NULL()
981
         TYPE (SMNode)              , POINTER :: node1 => NULL(), node2 => NULL(), node => NULL()
982
         CLASS(FTLinkedListIterator), POINTER :: elementIterator => NULL()
983
         CLASS(FTObject)            , POINTER :: obj => NULL()
984

985
         INTEGER                             :: k, l, j, m, mSteps
986
         INTEGER                             :: w
987
         REAL(KIND=RP)                       :: nodes(3,4)
988
         REAL(KIND=RP)                       :: x1(3), x2(3), xRight, yTop, xLeft, yBottom
989
         REAL(KIND=RP)                       :: h, hMin, dz, z(3), segmentLength
990
         LOGICAL                             :: removedHere
991

992
         elements        => mesh % elements
24✔
993
         elementIterator => mesh % elementsIterator
24✔
994
!
995
!        ------------------------------------------------
996
!        Mark elements outside or near the outer boundary
997
!        ------------------------------------------------
998
!
999
         IF( ASSOCIATED( outerBoundaryCurve ) )    THEN
24✔
1000

1001
            CALL elementIterator % setToStart()
20✔
1002
            DO WHILE(.NOT.elementIterator % isAtEnd())
15,930✔
1003
               obj => elementIterator % object()
15,910✔
1004
               CALL castToSMElement(obj,e)
15,910✔
1005
!
1006
!              -----------------------------------------------
1007
!              Remove element if it has a node that is outside
1008
!              the outer boundary curve (winding number =0)
1009
!              -----------------------------------------------
1010
!
1011
               removedHere = .false.
15,910✔
1012

1013
               DO k = 1, 4
54,534✔
1014

1015
                  w = ACWindingFunction( e % nodes(k) % node % x, outerBoundaryCurve % x, &
1016
                                         outerBoundaryCurve % nSegments )
45,258✔
1017
                  IF ( abs(w) == 0 ) THEN
54,534✔
1018
                     e % remove    = .true.
6,634✔
1019
                     removedHere   = .true.
6,634✔
1020
                     EXIT
6,634✔
1021
                  END IF
1022

1023
               END DO
1024
!
1025
!              --------------------------------------------
1026
!              Let the nodes know which curve they are near
1027
!              --------------------------------------------
1028
!
1029
               IF( e % remove .AND. removedHere )     THEN
15,910✔
1030
                  DO k = 1, 4
33,170✔
1031
                     node => e % nodes(k) % node
26,536✔
1032
                     node % bCurveChainID = outerBoundaryCurve % id
26,536✔
1033
                     node % bCurveSide    = INSIDE
33,170✔
1034
                  END DO
1035
               ELSE
1036
!
1037
!                 -----------------------------------------------
1038
!                 Any node in this element is inside, so mark one
1039
!                 -----------------------------------------------
1040
!
1041
                  node => e % nodes(1) % node
9,276✔
1042
                  aPointInsidetheCurve(:,outerBoundaryCurve % id) = node % x
37,104✔
1043
               END IF
1044

1045
               CALL elementIterator % moveToNext()
15,910✔
1046
            END DO
1047

1048
         ELSE ! we just have the outer box
1049

1050
            xRight  = backgroundParams % x0(1) + backgroundParams % dx(1)*backgroundParams % N(1)
4✔
1051
            yTop    = backgroundParams % x0(2) + backgroundParams % dx(2)*backgroundParams % N(2)
4✔
1052
            xLeft   = backgroundParams % x0(1)
4✔
1053
            yBottom = backgroundParams % x0(2)
4✔
1054

1055
            CALL elementIterator % setToStart()
4✔
1056
            DO WHILE ( .NOT.elementIterator % isAtEnd() )
768✔
1057
               obj => elementIterator % object()
764✔
1058
               CALL castToSMElement(obj,e)
764✔
1059
               removedHere = .false.
764✔
1060

1061
               DO k = 1, 4
3,820✔
1062
                  node1 => e % nodes(edgeMap(1,k)) % node
3,056✔
1063
                  node2 => e % nodes(edgeMap(2,k)) % node
3,056✔
1064

1065
                  x1    = node1 % x
12,224✔
1066
                  x2    = node2 % x
12,224✔
1067
                  IF( AlmostEqual(x1(2),yBottom) .AND. AlmostEqual(x2(2),yBottom) )      THEN
3,820✔
1068
                      node1 % bCurveID      = BOTTOM
36✔
1069
                      node1 % bCurveChainID = OUTER_BOX
36✔
1070
                      node1 % bCurveSide    = INSIDE
36✔
1071
                      node2 % bCurveID      = BOTTOM
36✔
1072
                      node2 % bCurveChainID = OUTER_BOX
36✔
1073
                      node2 % bCurveSide    = INSIDE
36✔
1074
                      node1 % distToBoundary = 0.0_RP
36✔
1075
                      node2 % distToBoundary = 0.0_RP
36✔
1076
                      IF( AlmostEqual(x1(1),xLeft) .OR. AlmostEqual(x1(1), xRight) )     THEN
36✔
1077
                          node1 % nodeType = CORNER_NODE
4✔
1078
                      END IF
1079
                      IF( AlmostEqual(x2(1), xLeft) .OR. AlmostEqual(x2(1), xRight) )    THEN
36✔
1080
                          node2 % nodeType = CORNER_NODE
4✔
1081
                      END IF
1082
                  ELSE IF ( AlmostEqual(x1(2),yTop) .AND. AlmostEqual(x2(2), yTop) )     THEN
3,020✔
1083
                      node1 % bCurveID      = TOP
36✔
1084
                      node1 % bCurveChainID = OUTER_BOX
36✔
1085
                      node1 % bCurveSide    = INSIDE
36✔
1086
                      node2 % bCurveID      = TOP
36✔
1087
                      node2 % bCurveChainID = OUTER_BOX
36✔
1088
                      node2 % bCurveSide    = INSIDE
36✔
1089
                      node1 % distToBoundary = 0.0_RP
36✔
1090
                      node2 % distToBoundary = 0.0_RP
36✔
1091
                      IF( AlmostEqual(x1(1),xLeft) .OR. AlmostEqual(x1(1),xRight))       THEN
36✔
1092
                          node1 % nodeType = CORNER_NODE
4✔
1093
                      END IF
1094
                      IF( AlmostEqual(x2(1),xLeft) .OR. AlmostEqual(x2(1),xRight))       THEN
36✔
1095
                          node2 % nodeType = CORNER_NODE
4✔
1096
                      END IF
1097
                  ELSE IF ( AlmostEqual(x1(1),xLeft) .AND. AlmostEqual(x2(1),xLeft))     THEN
2,984✔
1098
                      node1 % bCurveID      = LEFT
34✔
1099
                      node1 % bCurveChainID = OUTER_BOX
34✔
1100
                      node1 % bCurveSide    = INSIDE
34✔
1101
                      node2 % bCurveID      = LEFT
34✔
1102
                      node2 % bCurveChainID = OUTER_BOX
34✔
1103
                      node2 % bCurveSide    = INSIDE
34✔
1104
                      node1 % distToBoundary = 0.0_RP
34✔
1105
                      node2 % distToBoundary = 0.0_RP
34✔
1106
                      IF( AlmostEqual(x1(2),yTop) .OR. AlmostEqual(x1(2),yBottom) )      THEN
34✔
1107
                          node1 % nodeType = CORNER_NODE
4✔
1108
                      END IF
1109
                      IF( AlmostEqual(x2(2), yTop) .OR. AlmostEqual(x2(2), yBottom) )    THEN
34✔
1110
                          node2 % nodeType = CORNER_NODE
4✔
1111
                      END IF
1112
                  ELSE IF ( AlmostEqual(x1(1),xRight) .AND. AlmostEqual(x2(1),xRight) )  THEN
2,950✔
1113
                      node1 % bCurveID      = RIGHT
34✔
1114
                      node1 % bCurveChainID = OUTER_BOX
34✔
1115
                      node1 % bCurveSide    = INSIDE
34✔
1116
                      node2 % bCurveID      = RIGHT
34✔
1117
                      node2 % bCurveChainID = OUTER_BOX
34✔
1118
                      node2 % bCurveSide    = INSIDE
34✔
1119
                      node1 % distToBoundary = 0.0_RP
34✔
1120
                      node2 % distToBoundary = 0.0_RP
34✔
1121
                      IF( AlmostEqual(x1(2),yTop) .OR. &
34✔
1122
                          AlmostEqual(x1(2),yBottom) )     THEN
1123
                          node1 % nodeType = CORNER_NODE
4✔
1124
                      END IF
1125
                      IF( AlmostEqual(x2(2),yTop) .OR. AlmostEqual(x2(2),yBottom) )     THEN
34✔
1126
                          node2 % nodeType = CORNER_NODE
4✔
1127
                      END IF
1128
                  END IF
1129

1130
               END DO
1131

1132
               CALL elementIterator % moveToNext()
764✔
1133
            END DO
1134
         END IF
1135
!
1136
!        --------------------------------
1137
!        Do the same for inner boundaries
1138
!        --------------------------------
1139
!
1140
         IF( ASSOCIATED( interiorCurves ) )    THEN
24✔
1141
            DO l = 1, SIZE(interiorCurves)
43✔
1142
               curveArray => interiorCurves(l) % curveArray
33✔
1143

1144
               CALL elementIterator % setToStart()
33✔
1145

1146
               DO WHILE ( .NOT.elementIterator % isAtEnd() )
62,092✔
1147
                  obj => elementIterator % object()
62,049✔
1148
                  CALL castToSMElement(obj,e)
62,049✔
1149
                  removedHere = .FALSE.
62,049✔
1150

1151
                  IF( .NOT.e % remove )     THEN
62,049✔
1152

1153
                        DO k = 1, 4
168,653✔
1154

1155
                          node => e % nodes(k) % node
135,428✔
1156
                          w = ACWindingFunction( node % x, curveArray % x, curveArray % nSegments )
135,428✔
1157
                          IF ( ABS(w) >= 1 ) THEN
168,653✔
1158
                             aPointInsidetheCurve(:,curveArray % id) = node % x
6,692✔
1159
                             e % remove                              = .true.
1,673✔
1160
                             removedHere                             = .true.
1,673✔
1161
                             EXIT
1,673✔
1162
                          END IF
1163
                        END DO
1164
!
1165
!                       ----------------------------------------------------------------
1166
!                       For thin, sharp interior objects like an airfoil, it is possible
1167
!                       for all points in an element to be outside, yet boundary points
1168
!                       fall within an element. Check for this, too.
1169
!                       TODO: This is just a linear search through all elements. Should
1170
!                       use a better data structure like a spatial tree.
1171
!                       ----------------------------------------------------------------
1172
!
1173
                        IF( .NOT.e % remove )     THEN
34,898✔
1174

1175
                           DO k = 1, 4
166,125✔
1176
                              nodes(:,k) = e % nodes(k) % node % x
564,825✔
1177
                           END DO
1178
!
1179
!                          --------------------------
1180
!                          See how big the element is
1181
!                          --------------------------
1182
!
1183
                           hMin = SQRT( (nodes(1,1) - nodes(1,4))**2 + (nodes(2,1) - nodes(2,4))**2)
33,225✔
1184
                           DO k = 1, 3
132,900✔
1185
                              h = SQRT( (nodes(1,k+1) - nodes(1,k))**2 + (nodes(2,k+1) - nodes(2,k))**2)
99,675✔
1186
                              hMin = MIN( h, hMin )
132,900✔
1187
                           END DO
1188
!
1189
!                          --------------------------------------------------------------------
1190
!                          See if any of the nodes are inside the element. If the elements
1191
!                          are small compared to the spacing on the curve, linearly interpolate
1192
!                          the curve segment and see if those fall inside the element. HACK:
1193
!                          the decision on how big is too big is arbitrary at the moment.
1194
!                          --------------------------------------------------------------------
1195
!
1196

1197
                           DO j = 0, curveArray % nSegments
11,596,415✔
1198
!
1199
!                             ---------------------------------------------------
1200
!                             See if one of the boundary points is in the element
1201
!                             ---------------------------------------------------
1202
!
1203
                              IF ( PointInQuad( nodes, curveArray % x(:,j) ) )     THEN
11,563,275✔
1204
                                 e % remove  = .true.
85✔
1205
                                 removedHere = .true.
85✔
1206
                                 EXIT
85✔
1207
                              END IF
1208
!!
1209
!                             ------------------------------------------------------------------
1210
!                             If not, interpolate (currently see how much smaller the element is
1211
!                             and divide by 5 )
1212
!                             ------------------------------------------------------------------
1213
!
1214
                              x1 = curveArray % x(:,j)
46,252,760✔
1215
                              IF ( j == curveArray % nSegments  )     THEN
11,563,190✔
1216
                                 x2 = curveArray % x(:,0)
132,560✔
1217
                              ELSE
1218
                                 x2 = curveArray % x(:,j+1)
46,120,200✔
1219
                              END IF
1220
                              segmentLength = SQRT( (x2(1) - x1(1))**2 + (x2(2) - x1(2))**2)
11,563,190✔
1221
                              IF( segmentLength > 0.25_RP*hMin )     THEN
11,596,330✔
1222
                                 mSteps = NINT(segmentLength/hMin)*5
369,700✔
1223
                                 dz = 1.0_RP/DBLE(mSteps)
369,700✔
1224
                                 DO m = 1, mSteps-1
1,280,616✔
1225
                                    z = x1 + m*dz*(x2 - x1)
3,643,664✔
1226
                                    IF ( PointInQuad( nodes, z ) )     THEN
1,280,616✔
1227
                                       e % remove    = .true.
×
1228
                                       removedHere   = .true.
×
1229
                                       EXIT
×
1230
                                    END IF
1231
                                 END DO
1232
                                 IF( e % remove )     EXIT
369,700✔
1233
                              END IF
1234
                           END DO
1235
                        END IF
1236
!
1237
!                       --------------------------------------------
1238
!                       Let the nodes know which curve they are near
1239
!                       --------------------------------------------
1240
!
1241
                        IF ( e % remove .AND. removedHere )     THEN
34,898✔
1242
                          DO k = 1, 4
8,790✔
1243
                              node => e % nodes(k) % node
7,032✔
1244
                              node % bCurveChainID = curveArray % id
7,032✔
1245
                              node % bCurveSide    = OUTSIDE
8,790✔
1246
                          END DO
1247
                        END IF
1248

1249
                  END IF
1250

1251
                  CALL elementIterator % moveToNext()
62,049✔
1252
               END DO
1253

1254
            END DO
1255
!
1256
!           ----------------------------------------------
1257
!           A point guaranteed to be inside an outer curve
1258
!           is inside an inner curve
1259
!           ----------------------------------------------
1260
!
1261
            IF( ASSOCIATED( outerBoundaryCurve ) ) THEN
10✔
1262
               aPointInsidetheCurve(:,1) = aPointInsidetheCurve(:,2)
36✔
1263
            END IF
1264
         END IF
1265

1266
      END SUBROUTINE MarkExteriorElements
24✔
1267
!
1268
!////////////////////////////////////////////////////////////////////////
1269
!
1270
      SUBROUTINE MarkFloaters( mesh )
×
1271
!
1272
!        ------------------------------------------------------------------
1273
!        TODO: A floater is an element that is not attached to any other element,
1274
!        which happens occasionally during the marking process
1275
!        ------------------------------------------------------------------
1276
!
1277
         IMPLICIT NONE
1278
!
1279
!        ---------
1280
!        Arguments
1281
!        ---------
1282
!
1283
         TYPE (SMMesh), POINTER :: mesh
1284
!
1285
!        ---------------
1286
!        Local variables
1287
!        ---------------
1288
!
1289
         CLASS(FTLinkedList)        , POINTER :: elements        => NULL()
1290
!         TYPE (SMElement)           , POINTER :: e               => NULL()
1291
!         TYPE (SMNode)              , POINTER :: node            => NULL()
1292
         CLASS(FTLinkedListIterator), POINTER :: elementIterator => NULL()
1293
!         CLASS(FTObject)            , POINTER :: obj             => NULL()
1294
!         INTEGER                              :: k
1295
!
1296
         elements        => mesh % elements
×
1297
         elementIterator => mesh % elementsIterator
×
1298

1299
         CALL elementIterator % setToStart()
×
1300
         DO WHILE(.NOT.elementIterator % isAtEnd())
×
1301
!            obj => elementIterator % object()
1302
!            CALL cast(obj,e)
1303

1304
!            DO k = 1, 4
1305
!
1306
!            END DO
1307
            CALL elementIterator % moveToNext()
×
1308
         END DO
1309

1310
      END SUBROUTINE MarkFloaters
×
1311
!
1312
!////////////////////////////////////////////////////////////////////////
1313
!
1314
      SUBROUTINE MarkInterfaceElements( mesh )
24✔
1315
      USE Geometry
1316
      IMPLICIT NONE
1317
!
1318
!        ---------
1319
!        Arguments
1320
!        ---------
1321
!
1322
         TYPE (SMMesh)                 , POINTER :: mesh
1323
!
1324
!        ---------------
1325
!        Local variables
1326
!        ---------------
1327
!
1328
         CLASS(FTLinkedList)        , POINTER :: elements        => NULL()
1329
         TYPE (SMElement)           , POINTER :: e               => NULL()
1330
         CLASS(SegmentedCurveArray) , POINTER :: curveArray      => NULL()
1331
         TYPE (SMNode)              , POINTER :: node            => NULL()
1332
         CLASS(FTLinkedListIterator), POINTER :: elementIterator => NULL()
1333
         CLASS(FTObject)            , POINTER :: obj             => NULL()
1334

1335
         INTEGER                              :: k, l, j, m, mSteps
1336
         REAL(KIND=RP)                        :: w
1337
         REAL(KIND=RP)                        :: nodes(3,4)
1338
         REAL(KIND=RP)                        :: x1(3), x2(3) !, xRight, yTop, xLeft, yBottom
1339
         REAL(KIND=RP)                        :: h, hMin, dz, z(3), segmentLength
1340
         LOGICAL                              :: isInterfaceElement
1341
         INTEGER                              :: numInside, numOutside, location(4)
1342

1343
         IF( .NOT.ASSOCIATED( interfaceCurves ) )    RETURN
24✔
1344
!
1345
         elements        => mesh % elements
1✔
1346
         elementIterator => mesh % elementsIterator
1✔
1347
!
1348
!        --------------------------------------
1349
!        Mark elements and nodes for each curve
1350
!        --------------------------------------
1351
!
1352
         DO l = 1, SIZE(interfaceCurves)
3✔
1353
            curveArray => interfaceCurves(l) % curveArray
2✔
1354

1355
            CALL elementIterator % setToStart()
2✔
1356
!
1357
!           -------------------------
1358
!           Loop through each element
1359
!           -------------------------
1360
!
1361
            DO WHILE ( .NOT.elementIterator % isAtEnd() )
1,963✔
1362
               obj => elementIterator % object()
1,960✔
1363
               CALL castToSMElement(obj,e)
1,960✔
1364
               isInterfaceElement = .false.
1,960✔
1365

1366
               IF( .NOT.e % remove )     THEN
1,960✔
1367
!
1368
!                 -----------------------------------------------------
1369
!                 Mark elements that have both inside and outside nodes
1370
!                 -----------------------------------------------------
1371
!
1372
                  numInside  = 0
1,272✔
1373
                  numOutside = 0
1,272✔
1374
                  location   = UNDEFINED
1,272✔
1375
                  DO k = 1, 4
6,360✔
1376
                     node => e % nodes(k) % node
5,088✔
1377
                     w = ACWindingFunction( node % x, curveArray % x, curveArray % nSegments-1 )
5,088✔
1378
                     IF ( ABS(w) > 0.6_RP ) THEN
6,360✔
1379
                        location(k)                   = INSIDE
932✔
1380
                        numInside                     = numInside + 1
932✔
1381
                        aPointInsidetheCurve(:,curveArray % id) = node % x
3,728✔
1382
                      ELSE IF ( abs(w) <= EPSILON(w) ) THEN
4,156✔
1383
                        location(k)                   = OUTSIDE
4,156✔
1384
                        numOutside                    = numOutside + 1
4,156✔
1385
                      END IF
1386
                  END DO
1387
!
1388
!                 -------------------------------------------------------
1389
!                 Those that do straddle the curve are interface elements
1390
!                 Otherwise, check if the element is too small and do
1391
!                 an interpolation to see if a point is inside.
1392
!                 -------------------------------------------------------
1393
!
1394
                  IF ( numInside > 0 .AND. numOutside > 0 )     THEN
1,272✔
1395
                     isInterfaceElement = .TRUE.
70✔
1396
                     DO k = 1,4
350✔
1397
                        e % nodes(k) % node % bCurveSide = location(k) ! INSIDE or OUTSIDE
350✔
1398
                     END DO
1399

1400
                  ELSE
1401
!
1402
!                    ------------------------------------
1403
!                    Grab the four corners of the element
1404
!                    ------------------------------------
1405
!
1406
                     DO k = 1, 4
6,010✔
1407
                        nodes(:,k) = e % nodes(k) % node % x
20,434✔
1408
                     END DO
1409
!
1410
!                    --------------------------
1411
!                    See how big the element is
1412
!                    --------------------------
1413
!
1414
                     hMin = SQRT( (nodes(1,1) - nodes(1,4))**2 + (nodes(2,1) - nodes(2,4))**2)
1,202✔
1415
                     DO k = 1, 3
4,808✔
1416
                        h = SQRT( (nodes(1,k+1) - nodes(1,k))**2 + (nodes(2,k+1) - nodes(2,k))**2)
3,606✔
1417
                        hMin = MIN( h, hMin )
4,808✔
1418
                     END DO
1419
!
1420
!                    ----------------------------------------------------------------------
1421
!                    See if any of the curve points are inside the element. If the elements
1422
!                    are small compared to the spacing on the curve, linearly interpolate
1423
!                    the curve segment and see if those fall inside the element. HACK:
1424
!                    the decision on how big is too big is arbitrary at the moment.
1425
!                    -----------------------------------------------------------------------
1426
!
1427
                     DO j = 1, curveArray % nSegments-1
305,621✔
1428
!
1429
!                       ---------------------------------------------------
1430
!                       See if one of the boundary points is in the element
1431
!                       ---------------------------------------------------
1432
!
1433
                        IF ( PointInQuad( nodes, curveArray % x(:,j) ) )     THEN
304,426✔
1434
                           isInterfaceElement = .true.
7✔
1435
                           EXIT
7✔
1436
                        END IF
1437
!
1438
!                       ------------------------------------------------------------------
1439
!                       If not, interpolate (currently see how much smaller the element is
1440
!                       and divide by 5 )
1441
!                       ------------------------------------------------------------------
1442
!
1443
                        x1 = curveArray % x(:,j)
1,217,676✔
1444
                        x2 = curveArray % x(:,j+1)
1,217,676✔
1445
                        segmentLength = SQRT( (x2(1) - x1(1))**2 + (x2(2) - x1(2))**2)
304,419✔
1446

1447
                        IF( segmentLength > 0.25*hMin )     THEN
305,614✔
1448
                           mSteps = NINT(segmentLength/hMin)*5
28,448✔
1449
                           dz = 1.0_RP/DBLE(mSteps)
28,448✔
1450
                           DO m = 1, mSteps-1
28,448✔
1451
                              z = x1 + m*dz*(x2 - x1)
×
1452
                              IF ( PointInQuad( nodes, z ) )     THEN
28,448✔
1453
                                 isInterfaceElement = .true.
×
1454
                                EXIT
×
1455
                              END IF
1456
                           END DO
1457
                           IF( isInterfaceElement )     EXIT
28,448✔
1458
                        END IF
1459

1460
                     END DO !Loop over segments
1461
                  END IF
1462
               END IF !(.NOT.e%remove)
1463
!
1464
!              --------------------------------------------
1465
!              Let the nodes know which curve they are near
1466
!              --------------------------------------------
1467
!
1468
               IF ( isInterfaceElement )     THEN
1,960✔
1469
                  DO k = 1, 4
385✔
1470
                     node => e % nodes(k) % node
308✔
1471
                     node % bCurveChainID = curveArray % id
308✔
1472
!
1473
!                    ------------------------------------------------------------------
1474
!                    To order boundary edges, we need to know a point inside this curve
1475
!                    ------------------------------------------------------------------
1476
!
1477
                     IF(node % bCurveSide == UNDEFINED )     THEN
385✔
1478
                        w = ACWindingFunction( node % x, curveArray % x, curveArray % nSegments-1 )
17✔
1479

1480
                        IF ( ABS(w) > 0.6_RP ) THEN
17✔
1481
                           aPointInsidetheCurve(:,curveArray % id) = node % x
×
1482
                           node % bCurveSide    = INSIDE
×
1483
                        ELSE IF ( abs(w) <= EPSILON(w) ) THEN
17✔
1484
                           node % bCurveSide    = OUTSIDE
17✔
1485
                        END IF
1486
                     END IF
1487
                  END DO
1488
               END IF
1489

1490
               CALL elementIterator % moveToNext()
1,960✔
1491
            END DO !WHILE ( .NOT.elementIterator % isAtEnd )
1492

1493
         END DO !l = 1, SIZE(interfaceCurves)
1494

1495
      END SUBROUTINE MarkInterfaceElements
1496
!
1497
!////////////////////////////////////////////////////////////////////////
1498
!
1499
      SUBROUTINE SetMaterialProperties( mesh )
1✔
1500
!
1501
!     ---------------------------------------------------------------------
1502
!     This procedure is intended to be called after any topology/element
1503
!     modifications are done. This means the procedure assumes that all
1504
!     elements have nodes either inside, outside or on any interface
1505
!     curves. the temporary interfaceCurves curves array must be available.
1506
!     ---------------------------------------------------------------------
1507
!
1508
      USE Geometry
1509
      IMPLICIT NONE
1510
!
1511
!        ---------
1512
!        Arguments
1513
!        ---------
1514
!
1515
         TYPE (SMMesh)              , POINTER :: mesh
1516
!
1517
!        ---------------
1518
!        Local variables
1519
!        ---------------
1520
!
1521
         CLASS(FTLinkedList)        , POINTER :: elements        => NULL()
1522
         TYPE (SMElement)           , POINTER :: e               => NULL()
1523
         CLASS(SegmentedCurveArray) , POINTER :: curveArray      => NULL()
1524
         TYPE (SMNode)              , POINTER :: node            => NULL()
1525
         CLASS(FTLinkedListIterator), POINTER :: elementIterator => NULL()
1526
         CLASS(FTObject)            , POINTER :: obj             => NULL()
1527

1528
         INTEGER                              :: k, l
1529
         INTEGER                              :: w
1530
         INTEGER                              :: numInside, numOutside, location(4)
1531

1532
         IF( .NOT.ASSOCIATED( interfaceCurves ) )    RETURN
1✔
1533
!
1534
         elements => mesh % elements
1✔
1535
         elementIterator => mesh % elementsIterator
1✔
1536
!
1537
         DO l = 1, SIZE(interfaceCurves)
3✔
1538
            curveArray => interfaceCurves(l) % curveArray
2✔
1539

1540
            CALL elementIterator % setToStart()
2✔
1541
!
1542
!           -------------------------
1543
!           Loop through each element
1544
!           -------------------------
1545
!
1546
            DO WHILE ( .NOT.elementIterator % isAtEnd() )
1,859✔
1547
               obj => elementIterator % object()
1,856✔
1548
               CALL castToSMElement(obj,e)
1,856✔
1549
!
1550
!              -----------------------------------------------------
1551
!              Mark elements that have only inside or boundary nodes
1552
!              -----------------------------------------------------
1553
!
1554
               IF(.NOT.e % remove)     THEN
1,856✔
1555
                  numInside  = 0
1,856✔
1556
                  numOutside = 0
1,856✔
1557
                  location   = UNDEFINED
1,856✔
1558

1559
                  DO k = 1, 4
9,280✔
1560
                     node => e % nodes(k) % node
7,424✔
1561

1562
                     w = ACWindingFunction( node % x, curveArray % x, curveArray % nSegments-1 )
7,424✔
1563
                     IF ( abs(w) == 0 ) THEN
9,280✔
1564
                        location(k)                   = OUTSIDE
6,256✔
1565
                        numOutside                    = numOutside + 1
6,256✔
1566
                     ELSE
1567
                        location(k)                   = INSIDE
1,168✔
1568
                        numInside                     = numInside + 1
1,168✔
1569
                        aPointInsidetheCurve(:,curveArray % id) = node % x
4,672✔
1570
                     END IF
1571
                  END DO
1572
!
1573
!                 -----------------------------------------
1574
!                 Set the material ID as the inner curve ID
1575
!                 -----------------------------------------
1576
!
1577
                  IF ( numInside >= 2 )     THEN
1,856✔
1578
                     e % materialID   = curveArray % id
326✔
1579
                  END IF
1580
               END IF
1581

1582
               CALL elementIterator % moveToNext()
1,856✔
1583
            END DO !WHILE ( .NOT.elementIterator % isAtEnd )
1584

1585
         END DO !l = 1, SIZE(interfaceCurves)
1586

1587
      END SUBROUTINE SetMaterialProperties
1588
!
1589
!////////////////////////////////////////////////////////////////////////
1590
!
1591
      SUBROUTINE initBoundaryNode( c, chain, t, bCurveSide, id, node )
2,814✔
1592
         IMPLICIT NONE
1593
!
1594
!        ---------
1595
!        Arguments
1596
!        ---------
1597
!
1598
         REAL(KIND=RP)                  :: t
1599
         CLASS(SMCurve)       , POINTER :: c
1600
         CLASS(SMChainedCurve), POINTER :: chain
1601
         TYPE (SMNode)        , POINTER :: node
1602
         INTEGER                        :: bCurveSide, id
1603
!
1604
!        ---------------
1605
!        Local variables
1606
!        ---------------
1607
!
1608
         REAL(KIND=RP) :: p(3)
1609

1610
         p = c % positionAt(t)
11,256✔
1611
         CALL node % initWithLocationAndID(p,id)
2,814✔
1612

1613
         node % whereOnBoundary  = t
2,814✔
1614
         node % gWhereOnBoundary = chain % ChainTForCurveTInCurve( t, c )
2,814✔
1615
         node % distToBoundary   = 0.0_RP
2,814✔
1616
         node % bCurveID         = c % id()
2,814✔
1617
         node % bCurveChainID    = chain % id()
2,814✔
1618
         node % bCurveSide       = bCurveSide
2,814✔
1619
         node % nodeType         = ROW_SIDE !Most will be, change later if necessary
2,814✔
1620

1621
      END SUBROUTINE initBoundaryNode
2,814✔
1622
!
1623
!////////////////////////////////////////////////////////////////////////
1624
!
1625
      FUNCTION boundaryElementForNodes( elementID, oldNodes, newNodes ) RESULT(e)
2,776✔
1626
         USE Geometry
1627
         IMPLICIT NONE
1628
!
1629
!        ---------
1630
!        Arguments
1631
!        ---------
1632
!
1633
         INTEGER                       :: elementID
1634
         TYPE(SMNodePtr), DIMENSION(2) :: oldNodes, newNodes
1635
         TYPE(SMElement), POINTER      :: e
1636
!
1637
!        ---------------
1638
!        Local variables
1639
!        ---------------
1640
!
1641
         REAL(KIND=RP)  , DIMENSION(3,2) :: oldX, newX
1642
         REAL(KIND=RP)  , DIMENSION(3)   :: u, v
1643
         TYPE(SMNodePtr), DIMENSION(4)   :: eNodes
13,880✔
1644

1645

1646
         oldX(:,1) = oldNodes(1) % node % x
11,104✔
1647
         oldX(:,2) = oldNodes(2) % node % x
11,104✔
1648
         newX(:,1) = newNodes(1) % node % x
11,104✔
1649
         newX(:,2) = newNodes(2) % node % x
11,104✔
1650

1651
         u = oldX(:,2) - oldX(:,1)
11,104✔
1652
         v = newX(:,1) - oldX(:,1)
11,104✔
1653
         IF( CrossProductDirection(u,v) == UP )     THEN
2,776✔
1654
            eNodes(1) % node => oldNodes(1) % node
1,124✔
1655
            eNodes(2) % node => oldNodes(2) % node
1,124✔
1656
            eNodes(3) % node => newNodes(2) % node
1,124✔
1657
            eNodes(4) % node => newNodes(1) % node
1,124✔
1658
         ELSE
1659
            eNodes(1) % node => oldNodes(1) % node
1,652✔
1660
            eNodes(2) % node => newNodes(1) % node
1,652✔
1661
            eNodes(3) % node => newNodes(2) % node
1,652✔
1662
            eNodes(4) % node => oldNodes(2) % node
1,652✔
1663
         END IF
1664

1665
         ALLOCATE(e)
2,776✔
1666
         CALL e % initWithNodesIDAndType(eNodes, elementID, QUAD )
2,776✔
1667

1668
      END FUNCTION boundaryElementForNodes
2,776✔
1669
!
1670
!////////////////////////////////////////////////////////////////////////
1671
!
1672
      FUNCTION boundaryElementFor4Nodes( elementID, newNodes ) RESULT(e)
46✔
1673
         USE Geometry
1674
         IMPLICIT NONE
1675
!
1676
!        ---------
1677
!        Arguments
1678
!        ---------
1679
!
1680
         TYPE(SMNodePtr) , DIMENSION(4)   :: newNodes
1681
         TYPE (SMElement), POINTER        :: e
1682
         INTEGER                          :: elementID
1683
!
1684
!        ---------------
1685
!        Local variables
1686
!        ---------------
1687
!
1688
         REAL(KIND=RP)  , DIMENSION(3)   :: x1, x2, x4
1689
         REAL(KIND=RP)  , DIMENSION(3)   :: u, v
1690
         TYPE(SMNodePtr), DIMENSION(4)   :: eNodes
230✔
1691

1692
         ALLOCATE(e)
46✔
1693

1694
         x1 = newNodes(1) % node % x
184✔
1695
         x2 = newNodes(2) % node % x
184✔
1696
         x4 = newNodes(4) % node % x
184✔
1697

1698
         u = x2 - x1
184✔
1699
         v = x4 - x1
184✔
1700
         IF( CrossProductDirection(u,v) == UP )     THEN
46✔
1701
            CALL e % initWithNodesIDAndType(newNodes, elementID, QUAD )
26✔
1702
         ELSE
1703
            eNodes(1) % node => newNodes(1) % node
20✔
1704
            eNodes(2) % node => newNodes(4) % node
20✔
1705
            eNodes(3) % node => newNodes(3) % node
20✔
1706
            eNodes(4) % node => newNodes(2) % node
20✔
1707

1708
            CALL e % initWithNodesIDAndType(eNodes, elementID, QUAD )
20✔
1709
         END IF
1710

1711
      END FUNCTION boundaryElementFor4Nodes
46✔
1712
!
1713
!////////////////////////////////////////////////////////////////////////
1714
!
1715
      SUBROUTINE FlagEndNodes( list, model )
55✔
1716
      IMPLICIT NONE
1717
!
1718
!        ---------
1719
!        Arguments
1720
!        ---------
1721
!
1722
         CLASS(FTLinkedList), POINTER :: list
1723
         TYPE (SMModel)     , POINTER :: model
1724
!
1725
!        ---------------
1726
!        Local variables
1727
!        ---------------
1728
!
1729
         CLASS(SMChainedCurve)         , POINTER   :: boundaryCurve => NULL()
1730
         CLASS(SMCurve)                , POINTER   :: currentCurve  => NULL()
1731
         TYPE (SMNodePtr), DIMENSION(:), POINTER   :: nodeArray     => NULL()
1732
         TYPE (SMNode)                 , POINTER   :: node          => NULL()
1733
         INTEGER                                   :: nodeArraySize
1734
         INTEGER                                   :: j, k, jNode, id, jNode0, jNode1
1735
         REAL(KIND=RP)                             :: t, dt, t0, t1, x, dMin, d
1736
         CHARACTER(LEN=ERROR_EXCEPTION_MSG_LENGTH) :: msg
1737

1738
         nodeArray     => GatheredNodes( list )
55✔
1739
         nodeArraySize = SIZE(nodeArray)
55✔
1740
!
1741
!        -------------------------
1742
!        Set the default node type
1743
!        -------------------------
1744
!
1745
         DO j = 1, nodeArraySize
2,947✔
1746
            nodeArray(j) % node % nodeType = ROW_SIDE
2,947✔
1747
         END DO
1748
!
1749
!        ------------------------
1750
!        Get the associated chain
1751
!        ------------------------
1752
!
1753
         id            = nodeArray(1) % node % bCurveChainID
55✔
1754
         boundaryCurve => model % chainWithID(id)
55✔
1755
!
1756
!        -----------------------------------------------------------
1757
!        For each joint in the boundary curve, find the closest
1758
!        node in terms of "t" to flag as an end node. This will also
1759
!        set the location on the boundary to that joint location
1760
!
1761
!        TODO: If this gets's expensive, can use a quadtree to
1762
!        bin the nodes so that obviously far nodes aren't checked.
1763
!        -----------------------------------------------------------
1764
!
1765
         dt = 1.0_RP/boundaryCurve % numberOfCurvesInChain
55✔
1766
!
1767
!        -------------------
1768
!        Start/End point...
1769
!        TODO: Should bisect
1770
!        -------------------
1771
!
1772
         t0 = HUGE(t0)
55✔
1773
         t1 = -10.0_RP !Doesn't really matter...
55✔
1774

1775
         jnode0 = -1
55✔
1776
         jnode1 = -1
55✔
1777
         DO j = 1, nodeArraySize
2,947✔
1778
            t = nodeArray(j) % node % gWhereOnBoundary
2,892✔
1779
            IF( t < t0 )     THEN
2,892✔
1780
               t0     = t
141✔
1781
               jNode0 = j
141✔
1782
            END IF
1783
            IF( t > t1 )     THEN
2,947✔
1784
               t1     = t
1,119✔
1785
               jNode1 = j
1,119✔
1786
            END IF
1787
         END DO
1788

1789
         IF( t0 < 1.0_RP - t1 )     THEN
55✔
1790
           IF ( jNode0 < 0 )     THEN
37✔
1791
              WRITE(msg,*) "Joint node 0 not found"
×
1792
              CALL ThrowErrorExceptionOfType("FlagEndNodes", msg, FT_ERROR_FATAL)
×
1793
              RETURN
×
1794
           END IF
1795
           nodeArray(jNode0) % node % whereOnBoundary = 0.0_RP
37✔
1796
           nodeArray(jNode0) % node % nodeType        = boundaryCurve % jointClassification(0)
37✔
1797
           nodeArray(jNode0) % node % bCurveID        = boundaryCurve % myCurveIDs(1)
37✔
1798
         ELSE
1799
           IF ( jNode1 < 0 )     THEN
18✔
1800
              WRITE(msg,*) "Joint node 1 not found"
×
1801
              CALL ThrowErrorExceptionOfType("FlagEndNodes", msg, FT_ERROR_FATAL)
×
1802
              RETURN
×
1803
           END IF
1804
           nodeArray(jNode1) % node % whereOnBoundary = 1.0_RP
18✔
1805
           nodeArray(jNode1) % node % nodeType        = boundaryCurve % jointClassification(0)
18✔
1806
         END IF
1807
!
1808
!       --------------------
1809
!       All the other points
1810
!       --------------------
1811
!
1812
         DO k = 1, boundaryCurve % numberOfCurvesInChain-1
91✔
1813
            t = k*dt
36✔
1814
!
1815
!           ----------------------
1816
!           Find the closest point:
1817
!           TODO should bisect
1818
!           ----------------------
1819
!
1820
            dMin  = HUGE(dMin)
36✔
1821
            jNode = -1
36✔
1822
            DO j = 1, SIZE(nodeArray)
2,700✔
1823
               x = nodeArray(j) % node % gWhereOnBoundary
2,664✔
1824
               d = ABS(t - x)
2,664✔
1825
               IF( d < dMin )     THEN
2,700✔
1826
                  dMin  = d
691✔
1827
                  jNode = j
691✔
1828
               END IF
1829
            END DO
1830
           IF ( jNode < 0 )     THEN
36✔
1831
              WRITE(msg,*) "Joint node not found"
×
1832
              CALL ThrowErrorExceptionOfType("FlagEndNodes", msg, FT_ERROR_FATAL)
×
1833
              RETURN
×
1834
           END IF
1835
!
1836
!           ------------------------
1837
!           Set the node information
1838
!           ------------------------
1839
!
1840
            nodeArray(jNode) % node % bCurveID        = boundaryCurve % myCurveIDs(k+1)
36✔
1841
            nodeArray(jNode) % node % whereOnBoundary = 0.0_RP
36✔
1842
            nodeArray(jNode) % node % nodeType        = boundaryCurve % jointClassification(k)
91✔
1843
         END DO
1844
!
1845
!        ---------------------------------------------------------------------------------
1846
!        At Row-Reversal points along a curve, it is possible that the row-reversal point
1847
!        is the closest point along the curve to more than one node. To account for this,
1848
!        mark all points within the tolerance used to find the closest point by the joint
1849
!        classification.
1850
!        ---------------------------------------------------------------------------------
1851
!
1852
         DO k = 0, boundaryCurve % COUNT()-1
146✔
1853

1854
            IF( boundaryCurve % jointClassification(k) == ROW_REVERSAL )     THEN
146✔
1855
               currentCurve => boundaryCurve % curveAtIndex(k+1)
5✔
1856
               t            = boundaryCurve % ChainTForCurveTInCurve(0.0_RP,currentCurve)
5✔
1857
               t1           = boundaryCurve % ChainTForCurveTInCurve(1.0_RP,currentCurve)
5✔
1858

1859
               DO j = 1, SIZE(nodeArray)
461✔
1860
                  node => nodeArray(j) % node
456✔
1861

1862
                  IF ( ABS(node % gWhereOnBoundary - t) <= minimizationTolerance )     THEN
461✔
1863
                     node % whereOnBoundary  = 0.0_RP
11✔
1864
                     node % gWhereOnBoundary = t
11✔
1865
                     node % nodeType         = ROW_REVERSAL
11✔
1866
                  ELSE IF ( ABS(t1 - node % whereOnBoundary) < minimizationTolerance )     THEN
445✔
1867
                     node % whereOnBoundary  = 1.0_RP
×
1868
                     node % gWhereOnBoundary = t1
×
1869
                     node % nodeType         = ROW_REVERSAL
×
1870
                  END IF
1871
               END DO
1872

1873
            END IF
1874

1875
         END DO
1876
!
1877
!        --------
1878
!        Clean up
1879
!        --------
1880
!
1881
         DEALLOCATE( nodeArray )
55✔
1882

1883
      END SUBROUTINE FlagEndNodes
1884
!
1885
!////////////////////////////////////////////////////////////////////////
1886
!
1887
      SUBROUTINE setElementBoundaryInfo(project)
48✔
1888
!
1889
!     ------------------------------------------------------------------------
1890
!     Go through the mesh and gather the boundary information for each element
1891
!     ------------------------------------------------------------------------
1892
!
1893
         IMPLICIT NONE
1894
!
1895
!        ---------
1896
!        Arguments
1897
!        ---------
1898
!
1899
         TYPE(MeshProject) :: project
1900
!
1901
!        ---------------
1902
!        Local Variables
1903
!        ---------------
1904
!
1905
         INTEGER                              :: N
1906
         TYPE (SMMesh)              , POINTER :: mesh
1907
         CLASS(FTLinkedListIterator), POINTER :: iterator
1908
         CLASS(FTObject)            , POINTER :: obj
1909
         TYPE (SMElement)           , POINTER :: e
1910
         TYPE (SMModel)             , POINTER :: model
1911

1912
         N     =  project % runParams % polynomialOrder
48✔
1913
         mesh  => project % mesh
48✔
1914
         model => project % model
48✔
1915

1916
         iterator => mesh % elementsIterator
48✔
1917
         CALL iterator % setToStart()
48✔
1918

1919
         DO WHILE ( .NOT.iterator % isAtEnd() )
22,450✔
1920

1921
            obj => iterator % object()
22,402✔
1922
            CALL castToSMElement(obj,e)
22,402✔
1923
            CALL ElementBoundaryInfoInit( e % boundaryInfo, N)
22,402✔
1924
            CALL gatherElementBoundaryInfo( e, project )
22,402✔
1925

1926
            CALL iterator % moveToNext()
22,402✔
1927
         END DO
1928

1929
      END SUBROUTINE setElementBoundaryInfo
48✔
1930
!
1931
!////////////////////////////////////////////////////////////////////////
1932
!
NEW
1933
      SUBROUTINE gatherElementBoundaryInfoORIG( e, model  )
×
1934
!
1935
!   ATTENTION: THIS IS THE ORIGINAL VERSION THAT COMPUTES THE BOUNDARY LOCATIONS
1936
!              FROM THE CURVES IN THE MODEL. I'M LEAVING IT IN FOR NOW, BUT WITH
1937
!              THE OPTIMIZATIONS, WE WANT TO WRITE OUT THE EVALUATIONS OF THE
1938
!              BOUNDARY POLYNOMIAL APPROXIMATIONS, AS FOUND IN THE NEXT PROCEDURE.
1939
!              PRESUMABLY THIS PROCEDURE CAN BE DELETED.
1940
!     --------------------------------------------------------------------------
1941
!     Gather together the boundary information for the four
1942
!     edges of the element. This info is
1943
!
1944
!     nodeIDs(4)    = integer id # of the 4 corner nodes
1945
!     bCurveFlag(4) = integer ON or OFF of whether a boundary
1946
!                     curve is defined or NOT
1947
!     bCurveName(4) = Name of the 4 boundary curves. Equals "---" IF
1948
!                     the element side is interior,
1949
!     x(3,0:N,4)    = location (x,y) of the j=0:N Chebyshev-Lobatto points
1950
!                     along boundary k = 1,2,3,4. The kth entry will be zero if
1951
!                     bCurveFlag(k) = OFF.
1952
!     -------------------------------------------------------------------------
1953
!
1954
         IMPLICIT NONE
1955
!
1956
!        ---------
1957
!        Arguments
1958
!        ---------
1959
!
1960
         TYPE (SMElement)         , POINTER :: e
1961
         TYPE (SMModel)           , POINTER :: model
1962
!
1963
!        ---------------
1964
!        Local Variables
1965
!        ---------------
1966
!
1967
         INTEGER                        :: j, k
1968
         INTEGER                        :: N
1969
         TYPE (SMNode)        , POINTER :: node1 => NULL(), node2 => NULL()
1970
         CLASS(SMCurve)       , POINTER :: c     => NULL()
1971
         CLASS(SMChainedCurve), POINTER :: chain => NULL()
1972

1973
         REAL(KIND=RP)            :: tStart(4), tEnd(4), t_j, deltaT
1974
         INTEGER                  :: curveId(4)
1975
         CHARACTER(LEN=32)        :: noCurveName(-4:-1) = (/"Right ", "Left  ", "Bottom", "Top   " /)
1976

UNCOV
1977
         N = SIZE(e % boundaryInfo%x,2)-1
×
1978
!
1979
!        -----
1980
!        Nodes
1981
!        -----
1982
!
UNCOV
1983
         DO k = 1, 4
×
UNCOV
1984
            node1 => e % nodes(k) % node
×
UNCOV
1985
            e % boundaryInfo % nodeIDs(k) = node1 % id
×
1986
         END DO
1987
!
1988
!        -----------------------------------
1989
!        Gather up boundary edge information
1990
!        -----------------------------------
1991
!
UNCOV
1992
         e % boundaryInfo % bCurveName = NO_BC_STRING
×
UNCOV
1993
         e % boundaryInfo % bCurveFlag = OFF
×
1994

UNCOV
1995
         DO k = 1, 4
×
UNCOV
1996
            node1 => e % nodes(edgeMap(1,k)) % node
×
UNCOV
1997
            node2 => e % nodes(edgeMap(2,k)) % node
×
1998
!
1999
!           ---------------------------------------------------------------------------
2000
!           See if this edge is on a boundary. One of the two nodes should be
2001
!           a ROW_SIDE, and that one is on a curve rather than the joint of two curves.
2002
!           The edge could be on an outer box, in which case it is a straight line, but
2003
!           still needs boundary name information
2004
!           ---------------------------------------------------------------------------
2005
!
UNCOV
2006
            IF( IsOnBoundaryCurve(node1) .AND. IsOnBoundaryCurve(node2) )     THEN
×
2007
!
2008
!              -----------------------------------------------------------
2009
!              Mark as on a boundary curve needing interpolant information
2010
!              -----------------------------------------------------------
2011
!
UNCOV
2012
               e % boundaryInfo % bCurveFlag(k) = ON
×
UNCOV
2013
               IF( node2 % whereOnBoundary == 0.0_RP .OR. node2 % whereOnBoundary == 1.0_RP )     THEN
×
UNCOV
2014
                  curveID(k)    = node1 % bCurveID
×
UNCOV
2015
                  c             => model % curveWithID(node1 % bCurveID, chain)
×
2016
               ELSE
UNCOV
2017
                  curveID(k)    = node2 % bCurveID
×
UNCOV
2018
                  c             => model % curveWithID(node2 % bCurveID, chain)
×
2019
               END IF
2020

UNCOV
2021
               e % boundaryInfo % bCurveName(k) = c % curveName()
×
UNCOV
2022
               tStart(k)                        = node1 % gWhereOnBoundary
×
UNCOV
2023
               tEnd(k)                          = node2 % gWhereOnBoundary
×
2024

UNCOV
2025
            ELSE IF ( IsOnOuterBox(node1) .AND. IsOnOuterBox(node2) )     THEN
×
2026
!
2027
!              --------------------------------------------------------------
2028
!              Only mark the boundary names for output, no interpolant needed
2029
!              --------------------------------------------------------------
2030
!
UNCOV
2031
               IF( node1 % nodeType == CORNER_NODE )     THEN
×
UNCOV
2032
                  e % boundaryInfo % bCurveName(k) = noCurveName(node2 % bCurveID)
×
2033
               ELSE
UNCOV
2034
                  e % boundaryInfo % bCurveName(k) = noCurveName(node1 % bCurveID)
×
2035
               END IF
2036

2037
            END IF
2038
         END DO
2039
!
2040
!        --------------------------------------------
2041
!        Construct boundary information
2042
!        Use a Chebyshev interpolant for the points.
2043
!        --------------------------------------------
2044
!
UNCOV
2045
         DO k = 1, 4
×
2046

UNCOV
2047
            IF( e % boundaryInfo % bCurveFlag(k) == ON )     THEN ! Use boundary curves to compute interpolant
×
2048

UNCOV
2049
              deltaT = tEnd(k) - tStart(k)
×
UNCOV
2050
              IF( deltaT > maxParameterChange )     THEN !Crossing over the start
×
UNCOV
2051
                 deltaT = deltaT - 1.0_RP
×
UNCOV
2052
              ELSE IF (deltaT < -maxParameterChange ) THEN
×
UNCOV
2053
                 deltaT = 1.0_RP + deltaT
×
2054
              END IF
2055

UNCOV
2056
              DO j = 0, N
×
2057

UNCOV
2058
                  t_j = tStart(k) + deltaT*(1.0_RP - COS(j*PI/N))/2.0_RP
×
UNCOV
2059
                  IF( t_j > 1.0_RP )     THEN
×
UNCOV
2060
                     t_j = t_j - 1.0_RP
×
UNCOV
2061
                  ELSE IF( t_j < 0.0_RP )     THEN
×
UNCOV
2062
                     t_j = t_j + 1.0_RP
×
2063
                  END IF
2064

UNCOV
2065
                  e % boundaryInfo % x(:,j,k) = chain % PositionAt( t_j )
×
2066

2067
                END DO
2068
             ELSE ! Use a straight line between end nodes
2069

UNCOV
2070
               node1 => e % nodes(edgeMap(1,k)) % node
×
UNCOV
2071
               node2 => e % nodes(edgeMap(2,k)) % node
×
2072

UNCOV
2073
               DO j = 0, N
×
UNCOV
2074
                  t_j = (1.0_RP - COS(j*PI/N))/2.0_RP
×
UNCOV
2075
                  e % boundaryInfo % x(:,j,k) = (1.0_RP - t_j)*node1 % x + t_j*node2 % x
×
2076
               END DO
2077

2078
             END IF
2079
         END DO
2080

NEW
2081
      END SUBROUTINE gatherElementBoundaryInfoORIG
×
2082
!
2083
!////////////////////////////////////////////////////////////////////////
2084
!
2085
      SUBROUTINE gatherElementBoundaryInfo( e, project  )
22,402✔
2086
!
2087
!     --------------------------------------------------------------------------
2088
!     Gather together the boundary information for the four
2089
!     edges of the element. This info is
2090
!
2091
!     nodeIDs(4)    = integer id # of the 4 corner nodes
2092
!     bCurveFlag(4) = integer ON or OFF of whether a boundary
2093
!                     curve is defined or NOT
2094
!     bCurveName(4) = Name of the 4 boundary curves. Equals "---" IF
2095
!                     the element side is interior,
2096
!     x(3,0:N,4)    = location (x,y) of the j=0:N Chebyshev-Lobatto points
2097
!                     along boundary k = 1,2,3,4. The kth entry will be zero if
2098
!                     bCurveFlag(k) = OFF.
2099
!     -------------------------------------------------------------------------
2100
!
2101
         IMPLICIT NONE
2102
!
2103
!        ---------
2104
!        Arguments
2105
!        ---------
2106
!
2107
         TYPE (SMElement)         , POINTER :: e
2108
         TYPE (MeshProject)        :: project
2109
!
2110
!        ---------------
2111
!        Local Variables
2112
!        ---------------
2113
!
2114
         TYPE (SMModel)       , POINTER :: model
2115
         INTEGER                        :: j, k
2116
         INTEGER                        :: N
2117
         TYPE (SMNode)        , POINTER :: node1 => NULL(), node2 => NULL()
2118
         CLASS(SMCurve)       , POINTER :: c     => NULL()
2119
         CLASS(SMChainedCurve), POINTER :: chain => NULL()
2120
         CLASS(FTMutableObjectArray), POINTER  :: boundaryPolynomials !An alias
2121
         CLASS(MultiSegmentCurve)   , POINTER  :: boundaryPolynomial
2122
         CLASS(FTObject)               , POINTER  :: obj
2123

2124
         REAL(KIND=RP)            :: tStart(4), tEnd(4), t_j, deltaT
2125
         INTEGER                  :: curveId(4)
2126
         CHARACTER(LEN=32)        :: noCurveName(-4:-1) = (/"Right ", "Left  ", "Bottom", "Top   " /)
2127

2128
         model => project % model
22,402✔
2129
         boundaryPolynomials => project % boundaryPolynomialsArray
22,402✔
2130
         N = SIZE(e % boundaryInfo%x,2)-1
22,402✔
2131
!
2132
!        -----
2133
!        Nodes
2134
!        -----
2135
!
2136
         DO k = 1, 4
112,010✔
2137
            node1 => e % nodes(k) % node
89,608✔
2138
            e % boundaryInfo % nodeIDs(k) = node1 % id
112,010✔
2139
         END DO
2140
!
2141
!        -----------------------------------
2142
!        Gather up boundary edge information
2143
!        -----------------------------------
2144
!
2145
         e % boundaryInfo % bCurveName = NO_BC_STRING
112,010✔
2146
         e % boundaryInfo % bCurveFlag = OFF
112,010✔
2147

2148
         DO k = 1, 4
112,010✔
2149
            node1 => e % nodes(edgeMap(1,k)) % node
89,608✔
2150
            node2 => e % nodes(edgeMap(2,k)) % node
89,608✔
2151
!
2152
!           ---------------------------------------------------------------------------
2153
!           See if this edge is on a boundary. One of the two nodes should be
2154
!           a ROW_SIDE, and that one is on a curve rather than the joint of two curves.
2155
!           The edge could be on an outer box, in which case it is a straight line, but
2156
!           still needs boundary name information
2157
!           ---------------------------------------------------------------------------
2158
!
2159
            IF( IsOnBoundaryCurve(node1) .AND. IsOnBoundaryCurve(node2) )     THEN
112,010✔
2160
!
2161
!              -----------------------------------------------------------
2162
!              Mark as on a boundary curve needing interpolant information
2163
!              -----------------------------------------------------------
2164
!
2165
               e % boundaryInfo % bCurveFlag(k) = ON
5,876✔
2166
               IF( node2 % whereOnBoundary == 0.0_RP .OR. node2 % whereOnBoundary == 1.0_RP )     THEN
5,876✔
2167
                  c             => model % curveWithID(node1 % bCurveID, chain)
214✔
2168
                  curveID(k)    = chain % id()
214✔
2169
               ELSE
2170
                  c             => model % curveWithID(node2 % bCurveID, chain)
5,662✔
2171
                  curveID(k)    = chain % id()
5,662✔
2172
               END IF
2173

2174
               e % boundaryInfo % bCurveName(k) = c % curveName()
5,876✔
2175
               tStart(k)                        = node1 % gWhereOnBoundary
5,876✔
2176
               tEnd(k)                          = node2 % gWhereOnBoundary
5,876✔
2177

2178
            ELSE IF ( IsOnOuterBox(node1) .AND. IsOnOuterBox(node2) )     THEN
83,732✔
2179
!
2180
!              --------------------------------------------------------------
2181
!              Only mark the boundary names for output, no interpolant needed
2182
!              --------------------------------------------------------------
2183
!
2184
               IF( node1 % nodeType == CORNER_NODE )     THEN
280✔
2185
                  e % boundaryInfo % bCurveName(k) = noCurveName(node2 % bCurveID)
32✔
2186
               ELSE
2187
                  e % boundaryInfo % bCurveName(k) = noCurveName(node1 % bCurveID)
248✔
2188
               END IF
2189

2190
            END IF
2191
         END DO
2192
!
2193
!        --------------------------------------------
2194
!        Construct boundary information
2195
!        Use a Chebyshev interpolant for the points.
2196
!        --------------------------------------------
2197
!
2198
         DO k = 1, 4
112,010✔
2199

2200
            IF( e % boundaryInfo % bCurveFlag(k) == ON )     THEN ! Use boundary curves to compute interpolant
112,010✔
2201

2202
              obj => boundaryPolynomials % objectAtIndex(curveID(k))
5,876✔
2203
              CALL castObjToMultiSegmentCurve(obj,boundaryPolynomial)
5,876✔
2204

2205
              deltaT = tEnd(k) - tStart(k)
5,876✔
2206
              IF( deltaT > maxParameterChange )     THEN !Crossing over the start
5,876✔
2207
                 deltaT = deltaT - 1.0_RP
12✔
2208
              ELSE IF (deltaT < -maxParameterChange ) THEN
5,864✔
2209
                 deltaT = 1.0_RP + deltaT
102✔
2210
              END IF
2211
              
2212

2213
              DO j = 0, N
41,052✔
2214

2215
                  t_j = tStart(k) + deltaT*(1.0_RP - COS(j*PI/N))/2.0_RP
35,176✔
2216
                  IF( t_j > 1.0_RP )     THEN
35,176✔
2217
                     t_j = t_j - 1.0_RP
270✔
2218
                  ELSE IF( t_j < 0.0_RP )     THEN
34,906✔
2219
                     t_j = t_j + 1.0_RP
8✔
2220
                  END IF
2221

2222
                  e % boundaryInfo % x(:,j,k) = boundaryPolynomial % PositionAt( t_j )
146,580✔
2223

2224
                END DO
2225
             ELSE ! Use a straight line between end nodes
2226

2227
               node1 => e % nodes(edgeMap(1,k)) % node
83,732✔
2228
               node2 => e % nodes(edgeMap(2,k)) % node
83,732✔
2229

2230
               DO j = 0, N
577,292✔
2231
                  t_j = (1.0_RP - COS(j*PI/N))/2.0_RP
493,560✔
2232
                  e % boundaryInfo % x(:,j,k) = (1.0_RP - t_j)*node1 % x + t_j*node2 % x
3,538,652✔
2233
               END DO
2234

2235
             END IF
2236
         END DO
2237

2238
      END SUBROUTINE gatherElementBoundaryInfo
22,402✔
2239
!
2240
!//////////////////////////////////////////////////////////////////////// 
2241
! 
2242
      SUBROUTINE MakeBoundaryInfoRightHanded(bInfo, a, b, c)  
47✔
2243
         USE LineReflectionModule
2244
         IMPLICIT NONE
2245
!
2246
!        ---------
2247
!        Arguments
2248
!        ---------
2249
!
2250
         TYPE (ElementBoundaryInfo) :: bInfo
2251
         REAL(KIND=RP)              :: a, b, c
2252
!
2253
!        ---------------
2254
!        Local variables
2255
!        ---------------
2256
!
2257
         INTEGER                    :: idSwap
2258
         CHARACTER(LEN=32)          :: strSwap
2259
         REAL(KIND=RP), ALLOCATABLE :: x(:,:,:)
47✔
2260
         INTEGER                    :: j, k, N
2261
!
2262
!        ---------------------
2263
!        Two nodes swap places
2264
!        ---------------------
2265
!
2266
         idSwap             = bInfo % nodeIDs(2)
47✔
2267
         bInfo % nodeIDs(2) = bInfo % nodeIDs(4)
47✔
2268
         bInfo % nodeIDs(4) = idSwap
47✔
2269
!
2270
!        --------------------------
2271
!        All four edges swap places
2272
!        --------------------------
2273
!
2274
         idSwap                = bInfo % bCurveFlag(2)
47✔
2275
         bInfo % bCurveFlag(2) = bInfo % bCurveFlag(3)
47✔
2276
         bInfo % bCurveFlag(3) = idSwap
47✔
2277
         
2278
         idSwap                = bInfo % bCurveFlag(1)
47✔
2279
         bInfo % bCurveFlag(1) = bInfo % bCurveFlag(4)
47✔
2280
         bInfo % bCurveFlag(4) = idSwap
47✔
2281
!
2282
!        --------------------------------
2283
!        All four edges swap places names
2284
!        --------------------------------
2285
!
2286
         strSwap               = bInfo % bCurveName(2)
47✔
2287
         IF(strSwap .ne. NO_BC_STRING) strSwap = TRIM(strSwap)  //"_R"
47✔
2288
         bInfo % bCurveName(2) = bInfo % bCurveName(3)
47✔
2289
         IF(bInfo % bCurveName(2) .ne. NO_BC_STRING) bInfo % bCurveName(2) = TRIM(bInfo % bCurveName(2))//"_R"
47✔
2290
         bInfo % bCurveName(3) = strSwap
47✔
2291
         
2292
         strSwap               = bInfo % bCurveName(1)
47✔
2293
         IF(strSwap .ne. NO_BC_STRING) strSwap = TRIM(strSwap)  //"_R"
47✔
2294
         bInfo % bCurveName(1) = bInfo % bCurveName(4)
47✔
2295
         IF(bInfo % bCurveName(1) .ne. NO_BC_STRING) bInfo % bCurveName(1) = TRIM(bInfo % bCurveName(1))//"_R"
47✔
2296
         bInfo % bCurveName(4) = strSwap
47✔
2297
!
2298
!        --------------------------------------------------------
2299
!        Boundary curves swap and change direction. At the same
2300
!        time, might as well perform the reflection on the points
2301
!        --------------------------------------------------------
2302
!
2303
         IF ( ALLOCATED(bInfo % x) )     THEN
47✔
2304
         
2305
            N = UBOUND(bInfo % x,2) 
47✔
2306
            ALLOCATE(x, source = bInfo % x)
3,995✔
2307
            
2308
            DO k = 1, 3, 2 
141✔
2309
               DO j = 0,N 
611✔
2310
                  x(:,j,k) = reflectAboutLine(bInfo % x(:,j,k), a, b, c)
564✔
2311
               END DO 
2312
            END DO
2313
            DO k = 2, 4, 2 
141✔
2314
               DO j = 0,N 
611✔
2315
                  x(:,j,k) = reflectAboutLine(bInfo % x(:,N-j,k), a, b, c)
564✔
2316
               END DO 
2317
            END DO
2318
            
2319
            bInfo % x(:,:,4) = x(:,:,1)
987✔
2320
            bInfo % x(:,:,1) = x(:,:,4)
987✔
2321
            bInfo % x(:,:,3) = x(:,:,2)
987✔
2322
            bInfo % x(:,:,2) = x(:,:,3)
987✔
2323

2324
         END IF 
2325

2326
          
2327
      END SUBROUTINE MakeBoundaryInfoRightHanded
47✔
2328
!
2329
!////////////////////////////////////////////////////////////////////////
2330
!
2331
      SUBROUTINE ComputeFacePatches(project)
48✔
2332
!
2333
!     ---------------------------------------------------------------
2334
!     Compute the boundary and interior face patch interpolant points
2335
!     ---------------------------------------------------------------
2336
!
2337
         USE CurveInterpolantClass
2338
         USE TransfiniteMapClass
2339
         IMPLICIT NONE
2340
!
2341
!        ---------
2342
!        Arguments
2343
!        ---------
2344
!
2345
         TYPE(MeshProject)     :: project
2346
!
2347
!        ---------------
2348
!        Local variables
2349
!        ---------------
2350
!
2351
         INTEGER                              :: N, j, k
2352
         TYPE (SMMesh)              , POINTER :: mesh
2353
         CLASS(FTLinkedListIterator), POINTER :: iterator
2354
         CLASS(FTObject)            , POINTER :: obj
2355
         TYPE (SMElement)           , POINTER :: e
2356
         TYPE(TransfiniteQuadMap)             :: quadMap
2357
         
2358
         TYPE(CurveInterpolant)       , POINTER     :: boundaryCurves(:)
2359
         REAL(KIND=RP), DIMENSION(:)  , ALLOCATABLE :: nodes
48✔
2360
         REAL(KIND=RP), DIMENSION(:,:), ALLOCATABLE :: values
48✔
2361
!
2362
!        -------------------
2363
!        Interior face patch
2364
!        -------------------
2365
!
2366
         N     =  project % runParams % polynomialOrder
48✔
2367
         mesh  => project % mesh
48✔
2368

2369
         iterator => mesh % elementsIterator
48✔
2370
         CALL iterator % setToStart()
48✔
2371

2372
         ALLOCATE( boundaryCurves(4) )
240✔
2373
         ALLOCATE( nodes(0:N) )
48✔
2374
         ALLOCATE( values(0:N,3) )
48✔
2375

2376
         DO j = 0, N
310✔
2377
            nodes(j) = -COS(j*PI/N)
310✔
2378
         END DO
2379
         values = 0.0_RP
978✔
2380
         DO k = 1, 4
240✔
2381
            CALL Construct( boundaryCurves(k), N, nodes, values )
240✔
2382
         END DO
2383
         quadMap = NewTransfiniteQuadMap( boundaryCurves )
48✔
2384

2385
         DO WHILE ( .NOT.iterator % isAtEnd() )
22,450✔
2386

2387
            obj => iterator % object()
22,402✔
2388
            CALL castToSMElement(obj,e)
22,402✔
2389

2390
            e % N = N
22,402✔
2391
            CALL ComputeElementFacePatch(e, quadMap, nodes, N)
22,402✔
2392

2393
            CALL iterator % moveToNext()
22,402✔
2394
         END DO
2395

2396
         DEALLOCATE( boundaryCurves)
48✔
2397

2398
      END SUBROUTINE ComputeFacePatches
96✔
2399
!
2400
!////////////////////////////////////////////////////////////////////////
2401
!
2402
      SUBROUTINE Perform2DMeshTransformations(project)
24✔
2403
         IMPLICIT NONE
2404
!
2405
!        ---------
2406
!        Arguments
2407
!        ---------
2408
!
2409
         TYPE(MeshProject)     :: project
2410
!
2411
!        ---------------
2412
!        Local Variables
2413
!        ---------------
2414
!
2415
         TYPE (SMMesh)              , POINTER :: mesh
2416

2417
         mesh => project % mesh
24✔
2418

2419
         IF ( .NOT. isIdentityRotation(self = project % rotationTransformer) )     THEN
24✔
2420
            CALL RotationTransformMesh(mesh = mesh, rotationTransformer = project % rotationTransformer)
×
2421
         END IF
2422

2423
         IF ( .NOT. isIdentityScale(self = project % scaleTransformer) )     THEN
24✔
2424
            CALL scaleMesh(mesh = mesh, scalingTransformer = project % scaleTransformer)
×
2425
         END IF
2426

2427
      END SUBROUTINE Perform2DMeshTransformations
24✔
2428
!
2429
!//////////////////////////////////////////////////////////////////////// 
2430
! 
2431
      SUBROUTINE ReflectMesh( mesh, symmetryCurve)
1✔
2432
         USE ConnectionsModule
2433
         USE LineReflectionModule
2434
         USE MeshCleaner, ONLY: MakeElement_RightHanded
2435
         IMPLICIT NONE
2436
!
2437
!        ---------
2438
!        Arguments
2439
!        ---------
2440
!
2441
         TYPE(SMMesh)  , POINTER :: mesh
2442
         CLASS(SMCurve), POINTER :: symmetryCurve
2443
!
2444
!        ---------------
2445
!        Local Variables
2446
!        ---------------
2447
!
2448
         REAL(KIND=RP)                       :: x0(3), x1(3), x(3)
2449
         REAL(KIND=RP)                       :: a, b, c
2450
         INTEGER                             :: nodeID, elemID
2451
         INTEGER                             :: bCurveID
2452
         INTEGER                             :: errorCode
2453
         CLASS(FTLinkedList)       , POINTER :: newNodes
2454
         CLASS(FTLinkedList)       , POINTER :: savedElements
2455
         TYPE(FTLinkedListIterator), POINTER :: nodeItr, elemItr
2456
         CLASS(FTObject)           , POINTER :: obj
2457
         TYPE(SMNode)              , POINTER :: oldNode, newNode
2458
         TYPE(SMElement)           , POINTER :: oldElement, newElement, e
2459
         INTEGER                             :: i, j
2460
         CHARACTER(SM_CURVE_NAME_LENGTH)     :: str
2461
!
2462
!        -------------------------------------------------
2463
!        Determine the mapping function for the reflection
2464
!        -------------------------------------------------
2465
!
2466
         x0 = symmetryCurve % positionAt(t = 0.0_RP)
4✔
2467
         x1 = symmetryCurve % positionAt(t = 1.0_RP)
4✔
2468
         CALL ComputeLineCoefs(x0,x1,a,b,c)
1✔
2469
         
2470
         bCurveID = symmetryCurve % id()
1✔
2471
!
2472
!        -----------------------------------------------------------
2473
!        Copy current elements to save for adding back into the mesh
2474
!        While we're at it, flag nodes along the symmetry boundary
2475
!        with the symmetry boundary ID, since corner points might
2476
!        be associated with another boundary.
2477
!        -----------------------------------------------------------
2478
!
2479
         ALLOCATE(savedElements)
1✔
2480
         CALL savedElements % init()
1✔
2481
         
2482
         elemItr => mesh % elementsIterator
1✔
2483
         elemID  = newElementID(mesh)
1✔
2484
         
2485
         CALL elemItr % setToStart()
1✔
2486
                  
2487
         DO WHILE(.NOT. elemItr % isAtEnd())
48✔
2488
            
2489
            obj => elemItr % object()
47✔
2490
            CALL castToSMElement(obj,oldElement)
47✔
2491
            
2492
            newElement => SMElementCopy(oldElement)
47✔
2493
            obj        => newElement
47✔
2494
            newElement % id = elemID
47✔
2495

2496
            CALL savedElements % add(obj)
47✔
2497
            CALL release(obj)
47✔
2498
!
2499
!           -----------------------------------------------------
2500
!           Flag symmetry points and clear the associated edge as
2501
!           being a boundary anymore.
2502
!           -----------------------------------------------------
2503
!
2504
            DO j = 1, 4
235✔
2505
               str = oldElement % boundaryInfo % bCurveName(j)
188✔
2506
               CALL toLower(str)
188✔
2507
               IF ( str == SYMMETRY_CURVE_NAME )     THEN
235✔
2508
                  oldElement % nodes(edgeMap(1,j)) % node % bCurveID = bCurveID 
6✔
2509
                  oldElement % nodes(edgeMap(2,j)) % node % bCurveID = bCurveID 
6✔
2510
                  oldElement % boundaryInfo % bCurveName(j)          = NO_BC_STRING
6✔
2511
                  newElement % boundaryInfo % bCurveName(j)          = NO_BC_STRING
6✔
2512
                  oldElement % boundaryInfo % bCurveFlag(j)          = NONE
6✔
2513
                  newElement % boundaryInfo % bCurveFlag(j)          = NONE
6✔
2514
               END IF 
2515
            END DO
2516
            
2517
            elemID = elemID + 1
47✔
2518
            CALL elemItr % MoveToNext()
47✔
2519
            
2520
         END DO
2521
!
2522
!        ---------------------------------------------------
2523
!        Copy nodes, except those along the symmetryCurve
2524
!        and reassign node pointers in the elements
2525
!        ---------------------------------------------------
2526
!
2527
         CALL deallocateNodeToElementConnections
1✔
2528
         !(Ignore error code since ReflectMesh won't be called otherwise.)
2529
         CALL makeNodeToElementConnections(mesh, errorCode) 
1✔
2530
         
2531
         ALLOCATE(newNodes)
1✔
2532
         CALL newNodes % init()
1✔
2533
         
2534
         nodeItr => mesh % nodesIterator
1✔
2535
         nodeID  = newNodeID(mesh)
1✔
2536
         
2537
         CALL nodeItr % setToStart()
1✔
2538
         
2539
         DO WHILE( .NOT. nodeItr % isAtEnd() )
62✔
2540
         
2541
            obj => nodeItr % object()
61✔
2542
            CALL castToSMNode(obj,oldNode)
61✔
2543
!
2544
            IF ( oldNode % bCurveID /= bCurveID )     THEN !Skip over nodes along the symmetry axis
61✔
2545
!
2546
!              -----------------------------------------------------
2547
!              Make a new node that is the reflection of the old one
2548
!              and add it to the list.
2549
!              -----------------------------------------------------
2550
!
2551
               ALLOCATE(newNode)
54✔
2552
               CALL newNode % init()
54✔
2553
               CALL copyNodeType(oldNode,newNode)
54✔
2554
   
2555
               newNode % x  = reflectAboutLine(oldNode % x, a, b, c)
216✔
2556
               newNode % id = nodeID
54✔
2557
               nodeID       = nodeID + 1
54✔
2558
               
2559
               obj => newNode
54✔
2560
               CALL newNodes % add(obj)
54✔
2561
               CALL release(obj)
54✔
2562
!
2563
!              ---------------------------------------------------------------------
2564
!              Reassign pointers in the elements that use the oldNode to the newNode
2565
!              ---------------------------------------------------------------------
2566
!
2567
               DO i = 1, numElementsForNode(oldNode % id) 
229✔
2568
                  e => elementsForNodes(i, oldNode % id) % element
175✔
2569
                  DO j = 1, e % eType 
929✔
2570
                     IF ( ASSOCIATED(e % nodes(j) % node, oldNode) )     THEN
875✔
2571
                        e % nodes(j) % node => newNode 
175✔
2572
                        e % boundaryInfo % nodeIDs(j) = newNode % id
175✔
2573
                        CALL newNode % retain()
175✔
2574
                     END IF 
2575
                  END DO 
2576
               END DO 
2577
            END IF
2578
            
2579
            CALL nodeItr % moveToNext()
61✔
2580
         END DO 
2581
!
2582
!        ---------------------------------------------------------------
2583
!        Finish up by
2584
!        1. Adding the new nodes to the mesh's node list
2585
!        2. Making the flipped elements right handed
2586
!        3. Fixing the flipped element's boundaryInfo block due to #2
2587
!        4. Flipping the element's face patch
2588
!        5. Adding back the original elements to the mesh
2589
!        6. Rebuild the edge list
2590
!        ---------------------------------------------------------------
2591
!
2592
         CALL mesh % nodes % addObjectsFromList(newNodes)
1✔
2593
         CALL releaseFTLinkedListClass(newNodes)
1✔
2594
         
2595
         CALL elemItr % setToStart()
1✔
2596
         DO WHILE(.NOT. elemItr % isAtEnd()) 
48✔
2597
         
2598
            obj => elemItr % object()
47✔
2599
            CALL castToSMElement(obj,e)
47✔
2600
            DO j = 1, e % eType 
235✔
2601
               e % boundaryInfo % nodeIDs(j) = e % nodes(j) % node % id 
235✔
2602
            END DO 
2603
            IF ( .NOT. elementIsRightHanded(e) )     THEN
47✔
2604
               CALL MakeElement_RightHanded(e)
47✔
2605
               CALL MakeBoundaryInfoRightHanded(e % boundaryInfo, a, b, c)
47✔
2606
            END IF 
2607

2608
            IF ( ALLOCATED(e % xPatch) )     THEN
47✔
2609
               DO j = 0, UBOUND(e % xPatch,3) 
282✔
2610
                  DO i = 0, UBOUND(e % xPatch, 2)
1,457✔
2611
                     x = reflectAboutLine(e % xPatch(:,i,j),a,b,c)
1,175✔
2612
                     e % xPatch(:,i,j) = x
4,935✔
2613
                  END DO  
2614
               END DO 
2615
            END IF 
2616
            
2617
            CALL elemItr % moveToNext()
47✔
2618
         END DO 
2619
         
2620
         CALL mesh % elements % addObjectsFromList(savedElements)
1✔
2621
         
2622
         CALL releaseFTLinkedListIterator(mesh % edgesIterator)
1✔
2623
         CALL releaseFTLinkedList(mesh % edges)
1✔
2624
         ALLOCATE(mesh % edges)
1✔
2625
         ALLOCATE(mesh % edgesIterator)
1✔
2626
         CALL mesh % edges % init()
1✔
2627
         CALL mesh % edgesIterator % initWithFTLinkedList(mesh % edges)
1✔
2628
         CALL buildEdgeList(mesh)
1✔
2629
!
2630
!        --------
2631
!        Clean up
2632
!        --------
2633
!
2634
         CALL releaseFTLinkedListClass(newNodes)
1✔
2635
         CALL releaseFTLinkedListClass(savedElements)
1✔
2636
         CALL deallocateNodeToElementConnections
1✔
2637
         
2638
      END SUBROUTINE ReflectMesh
1✔
2639
!
2640
!////////////////////////////////////////////////////////////////////////
2641
!
2642
      SUBROUTINE ComputeElementFacePatch(e, quadMap, nodes, N)
22,402✔
2643
         USE TransfiniteMapClass
1✔
2644
         USE CurveInterpolantClass
2645
         IMPLICIT NONE
2646
!
2647
!        ---------
2648
!        Arguments
2649
!        ---------
2650
!
2651
         TYPE (SMElement), POINTER :: e
2652
         INTEGER                   :: N
2653
         TYPE(TransfiniteQuadMap)  :: quadMap
2654
         REAL(KIND=RP)             :: nodes(0:N)
2655
!
2656
!        ---------------
2657
!        Local Variables
2658
!        ---------------
2659
!
2660
         REAL(KIND=RP) :: values(0:N,3)
44,804✔
2661
         INTEGER       :: i, j, k
2662

2663
         IF(.NOT. ALLOCATED(e % xPatch)) ALLOCATE(e % xPatch(3,0:N,0:N))
22,402✔
2664
!
2665
!        -------------------
2666
!        Set up the quad map
2667
!        -------------------
2668
!
2669
         DO k = 1, 4
112,010✔
2670
            DO j = 0, N
618,344✔
2671
               DO i = 1,3
2,204,552✔
2672
                  values(j,i) = e % boundaryInfo % x(i,j,k)
2,114,944✔
2673
               END DO
2674
            END DO
2675

2676
            CALL SetValues( quadMap % boundaryCurves(k), values )
112,010✔
2677
         END DO
2678
         nodes = quadMap % boundaryCurves(1) % nodes
154,586✔
2679

2680
         DO j = 0, N
154,586✔
2681
            DO i = 0, N
956,262✔
2682
               CALL EvaluateTransfiniteMapAt(this = quadMap,  &
2683
                                             xi   = nodes(i), &
2684
                                             eta  = nodes(j), &
2685
                                             res  = e % xPatch(:,i,j) )
933,860✔
2686
            END DO
2687
         END DO
2688

2689
      END SUBROUTINE ComputeElementFacePatch
22,402✔
2690
!
2691
!////////////////////////////////////////////////////////////////////////
2692
!
2693
      SUBROUTINE scaleMesh(mesh, scalingTransformer)
×
2694
         IMPLICIT NONE
2695
!
2696
!        ---------
2697
!        Arguments
2698
!        ---------
2699
!
2700
         TYPE (SMMesh)        :: mesh
2701
         TYPE(ScaleTransform) :: scalingTransformer
2702
!
2703
!        ---------------
2704
!        Local Variables
2705
!        ---------------
2706
!
2707
         TYPE (FTLinkedListIterator), POINTER :: nodeIterator, elementIterator
2708
         CLASS(FTObject)            , POINTER :: obj  => NULL()
2709
         TYPE (SMNode)              , POINTER :: node => NULL()
2710
         TYPE (SMElement)           , POINTER :: e
2711
         REAL(KIND=RP)                        :: xFormed(3)
2712
         INTEGER                              :: i, j, k, N
2713
!
2714
!        ---------------
2715
!        Scale the nodes
2716
!        ---------------
2717
!
2718
         nodeIterator => mesh % nodesIterator
×
2719
         CALL nodeIterator % setToStart()
×
2720
         DO WHILE( .NOT.nodeIterator % isAtEnd() )
×
2721
            obj => nodeIterator % object()
×
2722
            CALL castToSMNode(obj,node)
×
2723

2724
            xFormed = PerformScaleTransformation(x              = node % x, &
2725
                                                 transformation = scalingTransformer)
×
2726
            node % x = xFormed
×
2727
            CALL nodeIterator % moveToNext()
×
2728
         END DO
2729
!
2730
!        ---------------------------
2731
!        Scale element-stored values
2732
!        ---------------------------
2733
!
2734
         elementIterator => mesh % elementsIterator
×
2735
         CALL elementIterator % setToStart()
×
2736

2737
         DO WHILE(.NOT.elementIterator % isAtEnd())
×
2738

2739
            obj => elementIterator % object()
×
2740
            CALL castToSMElement(obj,e)
×
2741
            N = e % N
×
2742
!
2743
!           -------------
2744
!           Patch scaling
2745
!           -------------
2746
!
2747
            DO j = 0, N
×
2748
               DO i = 0, N
×
2749
                  xFormed = PerformScaleTransformation(x              = e % xPatch(:,i,j), &
2750
                                                       transformation = scalingTransformer)
×
2751
                  e % xPatch(:,i,j) = xFormed
×
2752
               END DO
2753
            END DO
2754
!
2755
!           ----------------------
2756
!           Boundary point scaling
2757
!           ----------------------
2758
!
2759
            DO k = 1,4
×
2760
               DO j = 0, N
×
2761
                  xFormed = PerformScaleTransformation(x              = e % boundaryInfo % x(:,j,k), &
2762
                                                       transformation = scalingTransformer)
×
2763
                  e % boundaryInfo % x(:,j,k) = xFormed
×
2764
               END DO
2765
            END DO
2766

2767
            CALL elementIterator % moveToNext()
×
2768
         END DO
2769

2770
      END SUBROUTINE scaleMesh
×
2771
!
2772
!////////////////////////////////////////////////////////////////////////
2773
!
2774
      SUBROUTINE RotationTransformMesh(mesh, rotationTransformer)
×
2775
         IMPLICIT NONE
2776
!
2777
!        ---------
2778
!        Arguments
2779
!        ---------
2780
!
2781
         TYPE (SMMesh)         :: mesh
2782
         TYPE(RotationTransform) :: rotationTransformer
2783
!
2784
!        ---------------
2785
!        Local Variables
2786
!        ---------------
2787
!
2788
         TYPE (FTLinkedListIterator), POINTER :: nodeIterator, elementIterator
2789
         CLASS(FTObject)            , POINTER :: obj  => NULL()
2790
         TYPE (SMNode)              , POINTER :: node => NULL()
2791
         TYPE (SMElement)           , POINTER :: e
2792
         REAL(KIND=RP)                        :: xFormed(3)
2793
         INTEGER                              :: i, j, k, N
2794
!
2795
!        ---------------
2796
!        Scale the nodes
2797
!        ---------------
2798
!
2799
         nodeIterator => mesh % nodesIterator
×
2800
         CALL nodeIterator % setToStart()
×
2801
         DO WHILE( .NOT.nodeIterator % isAtEnd() )
×
2802
            obj => nodeIterator % object()
×
2803
            CALL castToSMNode(obj,node)
×
2804

2805
            xFormed = PerformRotationTransform(x              = node % x, &
2806
                                               transformation = rotationTransformer)
×
2807
            node % x = xFormed
×
2808
            CALL nodeIterator % moveToNext()
×
2809
         END DO
2810
!
2811
!        ---------------------------
2812
!        Scale element-stored values
2813
!        ---------------------------
2814
!
2815
         elementIterator => mesh % elementsIterator
×
2816
         CALL elementIterator % setToStart()
×
2817

2818
         DO WHILE(.NOT.elementIterator % isAtEnd())
×
2819

2820
            obj => elementIterator % object()
×
2821
            CALL castToSMElement(obj,e)
×
2822
            N = e % N
×
2823
!
2824
!           -------------
2825
!           Patch scaling
2826
!           -------------
2827
!
2828
            DO j = 0, N
×
2829
               DO i = 0, N
×
2830
                  xFormed = PerformRotationTransform(x              = e % xPatch(:,i,j), &
2831
                                                     transformation = rotationTransformer)
×
2832
                  e % xPatch(:,i,j) = xFormed
×
2833
               END DO
2834
            END DO
2835
!
2836
!           ----------------------
2837
!           Boundary point scaling
2838
!           ----------------------
2839
!
2840
            DO k = 1,4
×
2841
               DO j = 0, N
×
2842
                  xFormed = PerformRotationTransform(x              = e % boundaryInfo % x(:,j,k), &
2843
                                                     transformation = rotationTransformer)
×
2844
                  e % boundaryInfo % x(:,j,k) = xFormed
×
2845
               END DO
2846
            END DO
2847

2848
            CALL elementIterator % moveToNext()
×
2849
         END DO
2850

2851
      END SUBROUTINE RotationTransformMesh
×
2852
!
2853
!//////////////////////////////////////////////////////////////////////// 
2854
! 
2855
      SUBROUTINE ComputeBoundaryPolynomials( project, chainNodesArray, performOptimization)
48✔
2856
!
2857
!     -----------------------------------------------------------------
2858
!     Generate multisegment curves that span the curve segments in the 
2859
!     chainNodesArray. This procedure creates polynomial approximations
2860
!     to the model's curves referenced in allCurves and stores them in
2861
!     the project's boundaryPolynomialsArray. The approximation error
2862
!     can be computed from the difference between the two.
2863
!     -----------------------------------------------------------------
2864
!
2865
         USE MultiSegmentNodalCurveClass
2866
         USE MultiSegmentModalCurveClass
2867
         USE CurveOptimization
2868
         IMPLICIT NONE
2869
!
2870
!        ---------
2871
!        Arguments
2872
!        ---------
2873
!
2874
         TYPE(MeshProject)     :: project
2875
         TYPE(JaggedNodeArray) :: chainNodesArray(:) !Array of arrays of nodes along the boundary
2876
         LOGICAL               :: performOptimization
2877
!
2878
!        ---------------
2879
!        Local variables
2880
!        ---------------
2881
!
2882
         
2883
         CLASS(SMModel)                , POINTER  :: model               !An alias
2884
         CLASS(FTMutableObjectArray)   , POINTER  :: boundaryPolynomials !An alias
2885
         CLASS(FTMutableObjectArray)   , POINTER  :: modelChains         !An alias
2886
         CLASS(FTObject)               , POINTER  :: obj                 !An Alias
2887
         
2888
         CLASS(SMChainedCurve)         , POINTER  :: modelChain
2889
         CLASS(MultiSegmentNodalCurve) , POINTER  :: boundaryPolynomial
2890
         CLASS(MultiSegmentModalCurve) , POINTER  :: optimizedCurve
2891
         CLASS(SMCurve)                , POINTER  :: crv, crvPtr
2892
         
2893
         TYPE(OptimizerOptions)                   :: options
2894
         REAL(KIND=RP)              , ALLOCATABLE :: nodeTs(:), chebyPoints(:)
48✔
2895
         REAL(KIND=RP)              , ALLOCATABLE :: values(:,:)
48✔
2896
         INTEGER                    , ALLOCATABLE :: ends(:)
48✔
2897
         
2898
         INTEGER                                  :: j, k, nNodes, N, m
2899
         INTEGER                                  :: nChains, nCurves
2900
         REAL(KIND=RP)                            :: t, dt
2901
         REAL(KIND=RP)                            :: nodeTol = 1.0d-12 ! TODO: put into a constant.
2902
!
2903
!        -----------
2904
!        Set aliases
2905
!        -----------
2906
!
2907
         model               => project % model
48✔
2908
         modelChains         => model % allChains
48✔
2909
         boundaryPolynomials => project % boundaryPolynomialsArray
48✔
2910
         
2911
         nChains = model % numberOfChains()
48✔
2912
         N       = project % runParams % polynomialOrder
48✔
2913
!
2914
!        ---------------------------------------
2915
!        Allocate Chebyshev Gauss-Lobatto points
2916
!        for use with non-optimized boundary
2917
!        curves.
2918
!        ---------------------------------------
2919
!
2920
         ALLOCATE(chebyPoints(0:N))
48✔
2921
         ALLOCATE(values(0:N,3))
48✔
2922
         DO m = 0, N 
310✔
2923
            chebyPoints(m) = (1.0_RP - COS(m*PI/N))/2.0_RP
310✔
2924
         END DO 
2925
!
2926
!        ---------------------------------------------------------------------------------
2927
!        For each chained curve, generate a new curve from the
2928
!        curves in the source chain. The new curves added to the chain includes
2929
!        (a) pointers to a new SMCurve of type MultiSegmentModalCurve if the curve
2930
!            is to be optimized. This may include coalescing several curves in the chain
2931
!            if the user requests them to be connected
2932
!        (b) pointers to a new MultiSegmentNodalCurve subclasses for polynomials defined
2933
!            between two pairs of nodes along boundaries.
2934
!        ---------------------------------------------------------------------------------
2935
!
2936
         DO j = 1, nChains ! For each boundary curve chain
158✔
2937
            boundaryPolynomial => NULL()
110✔
2938
            optimizedCurve     => NULL()
110✔
2939
!
2940
!           -----------------------------------------------------
2941
!           Get the curve chain from the model.
2942
!           modelChain is the exact chain.
2943
!           boundaryPolynomial is its approximation by a (PW) polynomial
2944
!           which is saved in the boundaryPolynomialsArray of the
2945
!           project at the same index.
2946
!           -----------------------------------------------------
2947
!
2948
            obj => modelChains % objectAtIndex(j)
110✔
2949
            CALL castToSMChainedCurve(obj, modelChain)
110✔
2950
!
2951
!           ----------------------------------------------------
2952
!           Collect all the parametric locations along the chain
2953
!           ----------------------------------------------------
2954
!
2955
            nNodes = SIZE(chainNodesArray(j) % array)
110✔
2956
            ALLOCATE(nodeTs(0:nNodes))
110✔
2957
            
2958
            IF ( chainNodesArray(j) % array(1) % node % gWhereOnBoundary == 0.0_RP )     THEN
110✔
2959
               DO k = 0, nNodes-1
2,188✔
2960
                  nodeTs(k) = chainNodesArray(j) % array(k+1) % node % gWhereOnBoundary
2,188✔
2961
               END DO  
2962
               nodeTs(nNodes) = 1.0_RP
44✔
2963
            ELSE 
2964
               nodeTs(0) = 0.0_RP
66✔
2965
               DO k = 1, nNodes
3,674✔
2966
                  nodeTs(k) = chainNodesArray(j) % array(k) % node % gWhereOnBoundary
3,674✔
2967
               END DO  
2968
            END IF
2969
!
2970
!           ---------------------------------------------
2971
!           Find the indices of the ends of each segment
2972
!           in the model chain.
2973
!           ---------------------------------------------
2974
!
2975
            nCurves = modelChain % COUNT()
110✔
2976
            dt      = 1.0_RP/REAL(nCurves, RP)
110✔
2977
            
2978
            ALLOCATE(ends(0:nCurves), source = 0)
402✔
2979
            ends(0)       = 0
110✔
2980
            ends(nCurves) = nNodes
110✔
2981
            
2982
            m = 1
110✔
2983
            DO k = 1, nNodes-1
5,752✔
2984
               t = m*dt
5,642✔
2985
               IF ( ABS(nodeTs(k) - t) <= nodeTol )     THEN
5,752✔
2986
                  ends(m)   = k
70✔
2987
                  nodeTs(k) = t
70✔
2988
                  m = m + 1 
70✔
2989
               END IF 
2990
            END DO
2991
!
2992
!           -------------------------------------------------------------
2993
!           Create a polynomial approximation for each curve in the chain
2994
!           depending on if optimization is requested or not.
2995
!           -------------------------------------------------------------
2996
!
2997
            crv => modelChain
110✔
2998
            IF ( modelChain % optimization /= NONE .AND. performOptimization)     THEN
110✔
2999
      
NEW
3000
               CALL SetDefaultOptions(options)
×
NEW
3001
               options % whichNorm = modelChain % optimization
×
3002
               CALL OptimizeCurve(curve              = crv,                     &
3003
                                  polyOrder          = N,                       &
3004
                                  cuts               = nodeTs,                  &
3005
                                  breakIndices       = ends,                    &
3006
                                  options            = options,                 &
3007
                                  newName            = modelChain % curveName(),&
3008
                                  newID              = modelChain % id(),       &
NEW
3009
                                  optimized          = crvPtr)
×
NEW
3010
               CALL castToMultiSegmentModalCurve(crvPtr, optimizedCurve)
×
NEW
3011
               obj => optimizedCurve
×
NEW
3012
               CALL boundaryPolynomials % addObject(obj)
×
3013
            ELSE 
3014
               ALLOCATE(boundaryPolynomial)
110✔
3015
               CALL boundaryPolynomial % ConstructMultiSegmentNodalCurve(crv, nodeTs, N, &
3016
                                                      modelChain % curveName(), modelChain % id() )
110✔
3017
               obj => boundaryPolynomial
110✔
3018
               CALL boundaryPolynomials % addObject(obj)
110✔
3019
            END IF 
3020
!
3021
!           -------
3022
!           Cleanup
3023
!           -------
3024
!
3025
            DEALLOCATE(ends)
110✔
3026
            DEALLOCATE(nodeTs)
110✔
3027
            IF(ASSOCIATED(boundaryPolynomial)) CALL releaseMultiSegmentNodalCurve(boundaryPolynomial)
110✔
3028
            IF(ASSOCIATED(optimizedCurve))     CALL releaseMultiSegmentModalCurve(optimizedCurve)
158✔
3029
           
3030
         END DO !All boundary curves
3031
         
3032
      END SUBROUTINE ComputeBoundaryPolynomials
48✔
3033
!
3034
!//////////////////////////////////////////////////////////////////////// 
3035
! 
3036
   SUBROUTINE MakeMeshSymmetric(project, symmetryCurve) 
1✔
3037
      IMPLICIT NONE  
3038
!
3039
!     -----------------------------------------------------------
3040
!     Perform symmetric transform if there is a symmetry boundary
3041
!     -----------------------------------------------------------
3042
!
3043
!
3044
!      ---------
3045
!      Arguments
3046
!      ---------
3047
!
3048
       CLASS(SMCurve)      , POINTER :: symmetryCurve
3049
       CLASS( MeshProject ), POINTER :: project
3050
!
3051
!      ---------------
3052
!      Local variables
3053
!      ---------------
3054
!
3055
       INTEGER :: errorCode
3056
            
3057
       IF ( allSymmetryCurvesAreColinear(project % model) )     THEN
1✔
3058
          CALL ReflectMesh(project % mesh, symmetryCurve)
1✔
3059
!
3060
!         --------------------------------
3061
!         Smooth mesh (again) if requested
3062
!         --------------------------------
3063
!
3064
          IF(ASSOCIATED(project % smoother))     THEN
1✔
3065
            IF(PrintMessage) PRINT *, "   Begin Smoothing (after reflection across a symmetry boundary)..."
1✔
3066
            CALL project % smoother % smoothMesh(  project % mesh, project % model, errorCode )
1✔
3067
            IF(PrintMessage) PRINT *, "   Smoothing done."
1✔
3068
          END IF
3069
       ELSE
3070
           CALL ThrowErrorExceptionOfType(poster = "HOHQMesh", &
3071
                                          msg    = "A symmetry curve is is not straight or colinear. Ignoring...", &
NEW
3072
                                          typ    = FT_ERROR_WARNING)
×
3073
       END IF
3074
         
3075
   END SUBROUTINE MakeMeshSymmetric
1✔
3076
!
3077
!//////////////////////////////////////////////////////////////////////// 
3078
! 
3079
   SUBROUTINE ComputeBoundaryApproximations(project, optimize)  
48✔
3080
      IMPLICIT NONE  
3081
      
3082
      CLASS(MeshProject), POINTER :: project
3083
      LOGICAL                     :: optimize
3084
!
3085
!     ---------------
3086
!     Local Variables
3087
!     ---------------
3088
!
3089
      TYPE(JaggedNodeArray), ALLOCATABLE :: chainNodesArray(:)
48✔
3090
      TYPE (SMModel)       , POINTER     :: model
3091
      INTEGER                            :: numBoundaryChains
3092
!
3093
!     -----------------------------------
3094
!     Gather and set boundary information
3095
!     and from that, create boundary 
3096
!     polynomial approximations to the 
3097
!     boundary chains.
3098
!     -----------------------------------
3099
!
3100
      model => project % model
48✔
3101
      numBoundaryChains = model % numberOfChains()
48✔
3102
      ALLOCATE(chainNodesArray(numBoundaryChains))
158✔
3103
      CALL SortBoundaryNodesToChains(project % mesh % nodesIterator, &
3104
                                     numBoundaryChains, chainNodesArray)
48✔
3105
      CALL ComputeBoundaryPolynomials( project, chainNodesArray, optimize)
48✔
3106
      CALL setElementBoundaryInfo(project)
48✔
3107
!
3108
!     -----------------------------------------
3109
!     Compute the patch of interpolated points
3110
!     inside the elements
3111
!     -----------------------------------------
3112
!
3113
      CALL ComputeFacePatches(project)
48✔
3114
      
3115
      DEALLOCATE(chainNodesArray)
206✔
3116
      
3117
   END SUBROUTINE ComputeBoundaryApproximations
48✔
3118
   
3119
   END MODULE MeshGenerationMethods
3120
!
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