-
Notifications
You must be signed in to change notification settings - Fork 0
/
MeshLib.f90
1665 lines (1491 loc) · 71.2 KB
/
MeshLib.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
module MeshLib
! use stdlib_sorting, only: ord_sort
! use ftlHashSetIntModule
use helpers
implicit none
! ONLY FOR IRREGULAR REFINEMENT
! Edge entity to help with refinement and to avoid duplicate nodes
! Will typically only be created on the fly
type :: MeshEdge
! Global IDs of the 2 connecting vertices (preferably sorted in ASC)
! Default values for the vertices to ensure we can check for a NULL edge
integer :: vertices(2) = (/0,0/)
contains
procedure :: isNullEdge, isEdgeEqual
end type MeshEdge
type :: Vertex
! Coordinates
double precision :: x, y, z
! Global Node Index. NULL vertex will have globalId = -1
integer :: globalId = -1
! A vertex can belong to multiple elements
! Id of parent element on concerned level that vertex belongs to
!!! These ppties below are tighly coupled to an element and a vertex can be shared by several elements
! Id of one of its parent cells/tetrahedrons/elements
! Needed in vertex to parent mapping (v2pe) in BaseMesh. Level 1 vertices are actually on Level 2
integer :: elementLocalId = -1
! intended to be the local ID of this vertex on the current element it bounds
integer :: localId = -1
! local ID of vertex on the element it was interpolated from i.e. its parent, follows the CGNS
! It would also be the same as the local ID of the refined Edge i.e.
! the local ID of the edge in the parent element with `elementLocalId`
! This will now be set during refinement so there'd be less stress when creating
! original mesh vertices
integer :: elementVertexId = -1
! level of parent this vertex was obtained from i.e. element it was
! interpolated from
integer :: elementLevel = -1
! nodes from the parent element that this node was interpolated from
! this node is a midnode of the connecting nodes of this edge
! if this edge is NULL after processing => this NODE is an original domain vertex
type(MeshEdge) :: parentEdge
contains
procedure :: isNullVertex
end type Vertex
! half-face of tetrahedron (3D) - triangle
type :: HalfFacet
! connecting nodes ordered following the
! CFD General Notation System (CGNS)
type(Vertex) :: vertices(3)
! Id of element that owns this HalfFacet on the mesh of the level concerned
integer :: elementLevelId = -1
! local Id of this facet on the element, follows CGNS too
integer :: localId = -1
contains
procedure :: isHfEqual, isNullHf
end type HalfFacet
! Actual 3D element
type :: Tetrahedron
! Connectivity of Vertex
type(Vertex) :: vertices(4)
! HalfFacets that form this element
type(HalfFacet) :: facets(4)
! 6 edges for this tetrahedron, with sorted global node numbers
type(MeshEdge) :: edges(6)
! current refinement level
integer :: level = -1
! global element index
integer :: globalId = -1
! ID of this element on its level
integer :: elementLevelId = -1
! ID of this element's parent on level -1
integer :: parentElementLevelId = -1
! Flag to check if element has been refined
! An element is said to be 'ACTIVE' if it hasn't been refined
! every created element should be active by default
logical :: isActive = .true.
contains
procedure :: isNullElement, elementHasEdge
end type Tetrahedron
type :: MeshConstants
real :: v2peIncrement = 0.2
end type MeshConstants
type :: SiblingHFTuple
! Each cell is a tuple of form:
! <sibling half facet element level Id, sibhfc local Id on element>
! default values of -1 if this tuple is NOT SET in sibhfcs
! values set to 0 if facet is on boundary, i.e. does not have a sibling facet
! integer :: sibhfElemLevelId = -1
! integer :: sibhfLocalId = -1
! Let every sibhfTuple represent that half-facet is all boundary by default
! we change this in sibhfcs only for facets that have actual siblings
integer :: sibhfElemLevelId = 0
integer :: sibhfLocalId = 0
contains
procedure :: isNullSibHFTuple
end type SiblingHFTuple
! For the intermediate structure v2hfs (mapping of vertex to its incident
! half-facets in which the vertex has the largest ID)
type :: Vertex2HalfFacets
integer :: vertexGlobalId = -1
! Array of every HF incident on vertex with `vertexGlobalId`
type(HalfFacet), allocatable :: hfs(:)
end type Vertex2HalfFacets
! for the intermediate structure v2adj (mapping of vertex to its adjacent vertices
! in each of the incident half-facets in v2hfs)
type :: VertexHalfFacet
integer :: vertexGlobalId = -1
integer :: elementGlobalId = -1
integer :: elementLevelId = -1
! local Id of this facet in element with `elementGlobalId`
integer :: facetId = -1
end type VertexHalfFacet
! in v2adj, vertex => [[adjacent vertices on facet1], [adjacent vertices on facet2]]
! [
! v0 => [[VHF1_1, VHF1_2, VHF1_3], [VHF2_1, VHF2_2, VHF2_3]],
! v1 => [[V1HF1_1, V1HF1_2, V1HF1_3]]
!]
type :: Vertex2VertexHalfFacets
integer :: vertexGlobalId = -1
type(VertexHalfFacet), allocatable :: vhf(:,:)
end type Vertex2VertexHalfFacets
type :: Vertex2Elements
integer :: vertexGlobalId = -1
integer, allocatable :: v2elems(:)
end type Vertex2Elements
! Mesh representation for a level using Array-based Half-Facets
type :: LevelMesh
! connectivity matrix for each element of this level. Just topological info
! shape: numOfELementsInLevel x 4 vertice global Ids per element: ((1, 2, 3, 4), (11, 12, 13, 14))
! we can just save the globalIds of the elements here and literally fetch them from: tet= base_mesh%tetrahedrons(globalId)
! For connectivity: tet%vertices(1)%globalId
integer, allocatable :: elementConn(:,:)
! to store the global IDs of all elements on this level for retrieval from the
! base_mesh
integer, allocatable :: elements(:)
! sibling half-facets: AHF data for adjacency queries
! index is levelIndex for elements,
! shape: number of elements on level x 4 facets per element
! Each cell is a tuple of form: <sibling half facet element level Id, sibhfc local Id on element>
! Rough example: [[0, <2,3>, 0, 0], [<3,3>, <1,1>, 0, 0]]
! [[NULL, SHFT<shfElemLevelId:2, shfLocalId3>, NULL, NULL]]
type(SiblingHFTuple), allocatable :: sibhfcs(:,:)
! vertex to half-facets, anchor to locate vertex
! to help determine border vertices easily
! vertex to an incident half-facet
type(HalfFacet), allocatable :: v2hf(:)
! vertex to elements array. a map of v -> hashset of element global IDs
! It's a map of each vertex to every element on a level incident on it
! To help find elements on a level attached to a specified edge
! No hashset support anymore, we use an integer array: 2D array now
! type(ftlHashSetInt), allocatable :: v2es(:)
type(Vertex2Elements), allocatable :: v2es(:)
! Element to Parent Element array for new elements on this level
! e2pe(elementLevelId) -> parentElementLevelId
integer, allocatable :: e2pe(:)
! element to child element mapping. Maps to index of first child on its level
! other children are contiguous to this - array
! for each element onlevel, point to their 1st child on next level
integer, allocatable :: e2ce(:)
! current number of elements on this level
integer :: numElements
! level mesh belongs to
integer :: level
end type LevelMesh
! type :: VertexToParent
! integer ::
! end type VertexToParent
! Hierarchical Array-based Half-Facet(HAHF)
type :: BaseMesh
! Statistics
integer :: numVertices, numElements, numLevels
! mesh hierarchy. All meshes ordered with increasing level
type(LevelMesh), allocatable :: levelMeshes(:)
! To track all elements and to be able to find element for refinement in O(1) time
type(Tetrahedron), allocatable :: tetrahedrons(:)
! Nodes ordered according to global node index. Vertex to Parent Element
! stores tuples of form: <level, elementId, localId>. Stores new vertices.
! Original vertices are exempt i.e. Level 1 vertices are new vertices on L1 obtained by interpolation -> midpoint vertices
type(Vertex), allocatable :: v2pe(:)
! All vertices including original vertices. can be used for the v2pe mapping too
! The index is the node global index mapping to <level, elementId, localId>, for the original vertices <0, 0, localId>
! element 0 is the full domain, level 0 is level for full domain
type(Vertex), allocatable :: vertices(:)
contains
procedure :: addTetrahedron, addTetrahedrons, initialize, initializeLevelMesh
procedure :: allocateCellSpace, mapElementVerticesToParent, expandV2pe, buildLevelMeshConnectivity
procedure :: buildLevelMeshV2hf, buildLevelMeshSibhfcs, buildLevelMeshV2es, getLevelMeshElementFacetAttachedNeighbours
procedure :: getLevelElementsAttachedToEdge, getLevelElementIdsAttachedToVertex, getEdgeMidVertex
procedure :: buildLevelMeshInterLevelConnectivity, getLevelMeshHangingNodes
procedure :: refineLevelElement, createMidVertexOnEdge, localRegularRefinement
procedure :: getLevelMeshNumNodes, getLevelMeshElementNeighbours, getElementLargeNeighbours
end type BaseMesh
contains
!!!!!! FUNCTIONS !!!!!!
function createVertex(x, y, z, globalId, parentLevelId, parentLevel) result(v)
implicit none
double precision, intent(in) :: x, y, z
integer, intent(in) :: globalId, parentLevelId, parentLevel
type(Vertex) :: v
v%x = x
v%y = y
v%z = z
v%globalId = globalId
! this has to be set by the parent that this vertex bounds,
! but then this will change with each parent that shares this vertex that's created
! OR anytime a gro is created, before the element is created, check:
! levelMesh%numElements and add 1 to know the parentElementLevelId and pass that
v%elementLocalId = parentLevelId
v%elementLevel = parentLevel
! local cannonical Id of this vertex on the Parent it was interpolated from, also
! the ID of the edge this vertex is on. Let this be set during refinement
! v%elementVertexId = parentVertexId
! let local ID be set when v2pe is set
end function createVertex
! creates an edge from the vertice global ID arguments
! sorts the global IDs in ASC order in edge%vertices
function createMeshEdgeSorted(v1, v2) result(edge)
implicit none
integer, intent(in) :: v1, v2
type(MeshEdge) :: edge
integer :: tempMax, vertices(2)
vertices = (/v1, v2/)
tempMax = v1
if ( tempMax > v2 ) then
! swap
vertices(1) = v2
vertices(2) = tempMax
end if
edge%vertices = vertices
end function createMeshEdgeSorted
function createHalfFacet(vertices, elementLevelId, localId) result(hf)
implicit none
type(Vertex), intent(in) :: vertices(3)
integer, intent(in) :: elementLevelId, localId
type(HalfFacet) :: hf
hf%vertices = vertices
hf%elementLevelId = elementLevelId
hf%localId = localId
end function createHalfFacet
function createTetrahedron(vertices, elementLevelId, globalId, level, parentElementLevelId) result(tet)
implicit none
type(Vertex), intent(in) :: vertices(4)
integer, intent(in) :: elementLevelId, globalId, level, parentElementLevelId
type(Tetrahedron) :: tet
type(HalfFacet) :: facets(4)
type(MeshEdge) :: edges(6)
! vertices must be ordered following CGNS
tet%vertices = vertices
tet%elementLevelId = elementLevelId
tet%globalId = globalId
tet%level = level
tet%parentElementLevelId = parentElementLevelId
! create half-facets for tet order by CGNS
! counter-clockwise to outward normal from the face, starting from left angle
facets(1) = createHalfFacet((/vertices(1), vertices(3), vertices(2)/), elementLevelId, 1)
facets(2) = createHalfFacet((/vertices(1), vertices(2), vertices(4)/), elementLevelId, 2)
facets(3) = createHalfFacet((/vertices(2), vertices(3), vertices(4)/), elementLevelId, 3)
facets(4) = createHalfFacet((/vertices(3), vertices(1), vertices(4)/), elementLevelId, 4)
tet%facets = facets
! create edges for tet order by CGNS
edges(1) = createMeshEdgeSorted(vertices(1)%globalId, vertices(2)%globalId)
edges(2) = createMeshEdgeSorted(vertices(2)%globalId, vertices(3)%globalId)
edges(3) = createMeshEdgeSorted(vertices(3)%globalId, vertices(1)%globalId)
edges(4) = createMeshEdgeSorted(vertices(1)%globalId, vertices(4)%globalId)
edges(5) = createMeshEdgeSorted(vertices(2)%globalId, vertices(4)%globalId)
edges(6) = createMeshEdgeSorted(vertices(3)%globalId, vertices(4)%globalId)
tet%edges = edges
end function createTetrahedron
function isNullVertex(vtx) result(isNull)
implicit none
class(Vertex), intent(in) :: vtx
logical :: isNull
isNull = .false.
if ( vtx%globalId == -1 ) isNull = .true.
end function isNullVertex
function isNullEdge(edge) result(isNull)
implicit none
class(MeshEdge), intent(in) :: edge
logical :: isNull
integer :: v1, v2
isNull = .false.
v1 = edge%vertices(1)
v2 = edge%vertices(2)
if ( v1 == 0 .and. v2 == 0 ) then
isNull = .true.
end if
end function isNullEdge
! checks if two edges are the same, i.e. are connected by the same 2 vertices
! vertices MUST be sorted in ASC order
function isEdgeEqual(edge, edge2) result(isEqual)
implicit none
class(MeshEdge), intent(in) :: edge
type(MeshEdge), intent(in) :: edge2
logical :: isEqual
isEqual = .false.
if ( edge%vertices(1) == edge2%vertices(1) .and. edge%vertices(2) == edge2%vertices(2)) then
isEqual = .true.
end if
end function isEdgeEqual
function isNullSibHFTuple(sibHft) result(isNull)
class(SiblingHFTuple), intent(in) :: sibHft
logical :: isNull
isNull = .false.
if ( sibHft%sibhfElemLevelId == 0 .or. sibHft%sibhfLocalId == 0 ) then
isNull = .true.
end if
end function isNullSibHFTuple
function getHfVertexWithLargestId(hf) result(vtxMax)
implicit none
type(HalfFacet), intent(in) :: hf
type(Vertex) :: vtxMax
integer :: i
! set max global ID to -1, to make max a null vertex
vtxMax%globalId = -1
! over the vertices of this facet. Finding the MAX
do i = 1, 3
if ( vtxMax%globalId < hf%vertices(i)%globalId ) then
vtxMax = hf%vertices(i)
end if
end do
end function getHfVertexWithLargestId
function isNullHf(hf) result(isNull)
implicit none
class(HalfFacet), intent(in) :: hf
logical :: isNull
isNull = .false.
if ( hf%elementLevelId == -1 .or. hf%localId == -1 ) then
isNull = .true.
end if
end function isNullHf
function isSiblingHFTupleSet(shfTuple) result(isSet)
implicit none
type(SiblingHFTuple), intent(in) :: shfTuple
logical :: isSet
isSet = .false.
if (shfTuple%sibhfElemLevelId > 0 .and. shfTuple%sibhfLocalId > 0) then
isSet = .true.
end if
end function isSiblingHFTupleSet
function isSiblingHFTupleOnBoundary(shfTuple) result(isOnBoundary)
implicit none
type(SiblingHFTuple), intent(in) :: shfTuple
logical :: isOnBoundary
isOnBoundary = .false.
if (shfTuple%sibhfElemLevelId == 0 .and. shfTuple%sibhfLocalId == 0) then
isOnBoundary = .true.
end if
end function isSiblingHFTupleOnBoundary
function getAdjacentVerticesToMaxVtxInFacet(v2adj, vtxMaxGlobalId, hf) result (vhfs)
implicit none
type(Vertex2VertexHalfFacets), allocatable, intent(in) :: v2adj(:)
integer, intent(in) :: vtxMaxGlobalId
type(HalfFacet), intent(in) :: hf
! always 2 adjacent vertices to max vertex per face
type(VertexHalfFacet) :: vhfs(2)
! 2D: number of half-facets adjacent to vtxMax x 2 possible adj. vertices per facet
TYPE(VertexHalfFacet), ALLOCATABLE, DIMENSION(:,:) :: vhf
integer :: i
! To check for adjacent vertices on `hf` where vtx with `vtMaxGlobalId` is max
vhf = v2adj(vtxMaxGlobalId)%vhf
do i = 1, size(vhf)
! check first cell to find facet `hf`, if there's a match, the entire row
! is a set of vertices adj. to vtxMax on facet `hf`
if ( vhf(i,1)%elementLevelId == hf%elementLevelId .and. &
vhf(i,1)%facetId == hf%localId ) then
vhfs = vhf(i,:)
! adjacent vertices found, exit loop
exit
end if
end do
end function getAdjacentVerticesToMaxVtxInFacet
function isHfEqual(hf1, hf2) result(isEqual)
implicit none
class(HalfFacet), intent(in) :: hf1
type(HalfFacet), intent(in) :: hf2
logical :: isEqual
isEqual = .false.
if ( (hf1%localId == hf2%localId) .and. &
(hf1%elementLevelId == hf2%elementLevelId) ) then
isEqual = .true.
end if
end function isHfEqual
function isSameAdjacentVertices(adjVhfs, tempAdjVhfs) result (isSame)
implicit none
TYPE(VertexHalfFacet), DIMENSION(2), intent(in) :: adjVhfs
TYPE(VertexHalfFacet), DIMENSION(2), intent(in) :: tempAdjVhfs
logical :: isSame
! isSame = .false.
! to be true, facetId and elementGlobalId at 1st position of both adjVhfs & tempAdjVhfs
! must match, similar for the 2nd position. To be sure the adjacent vertices are the
! same OR we can just compare the globalId of the Vertices
! isSame = (adjVhfs(1)%facetId == tempAdjVhfs(1)%facetId .and. &
! adjVhfs(1)%elementGlobalId == tempAdjVhfs(1)%elementGlobalId) .and. &
! (adjVhfs(1)%facetId == tempAdjVhfs(1)%facetId .and. &
! adjVhfs(1)%elementGlobalId == tempAdjVhfs(1)%elementGlobalId)
isSame = (adjVhfs(1)%vertexGlobalId == tempAdjVhfs(1)%vertexGlobalId) .and. &
(adjVhfs(2)%vertexGlobalId == tempAdjVhfs(2)%vertexGlobalId)
end function isSameAdjacentVertices
function getHalfFacetsWithSameAdjacentVertices(v2hfs, vtxMaxGlobalId, v2adj, adjVhfs) &
result(hfs)
implicit none
TYPE(Vertex2HalfFacets), ALLOCATABLE, DIMENSION(:), intent(in) :: v2hfs
integer, intent(in) :: vtxMaxGlobalId
TYPE(Vertex2VertexHalfFacets), ALLOCATABLE, DIMENSION(:), intent(in) :: v2adj
! set of adjacent vertices (mapped to the facets) to compare against
TYPE(VertexHalfFacet), DIMENSION(2), intent(in) :: adjVhfs
! array of matching half-facets to be returned
type(HalfFacet), allocatable :: hfs(:)
type(HalfFacet), allocatable :: tempHfs(:)
integer :: i, j, k
TYPE(VertexHalfFacet), DIMENSION(2) :: tempAdjVhfs
type(HalfFacet) :: hf
allocate(hfs(0))
! to track added half-facets
k = 1
! for each half-facet, hf, in v2hfs(vtxMaxGlobalId)%hfs
! get v2adj(vtxMax, hf)
! compare to adjVhfs, if similar, add to hfs array
do i = 1, size(v2hfs(vtxMaxGlobalId)%hfs)
hf = v2hfs(vtxMaxGlobalId)%hfs(i)
tempAdjVhfs = getAdjacentVerticesToMaxVtxInFacet(v2adj, vtxMaxGlobalId, hf)
! they should be sorted, so we can compare positional indices directly
if ( isSameAdjacentVertices(adjVhfs, tempAdjVhfs) ) then
! expand array to contain next element
if (allocated(tempHfs)) deallocate(tempHfs)
allocate(tempHfs(size(hfs) + 1))
! copy to hfs
do j = 1, size(hfs)
tempHfs(j) = hfs(j)
end do
tempHfs(k) = hf
deallocate(hfs)
hfs = tempHfs
k = k + 1
end if
end do
end function getHalfFacetsWithSameAdjacentVertices
function getLevelMeshElementFacetAttachedNeighbours(base_mesh, elementLevelId, meshLevel) &
result(neighbours)
implicit none
class(BaseMesh), intent(in) :: base_mesh
integer, intent(in) :: elementLevelId, meshLevel
type(LevelMesh) :: mesh
! only 4 facets per element -> max of 4 neighbours attached to a facet
! Null tetrahedrons may be included if the facet checked is a border facet
type(Tetrahedron) :: neighbours(4)
integer :: i
type(SiblingHFTuple) :: sibhfTuple
mesh = base_mesh%levelMeshes(meshLevel)
! over the elements facets
do i = 1, 4
sibhfTuple = mesh%sibhfcs(elementLevelId, i)
if ( .not. sibhfTuple%isNullSibHFTuple() ) then
! mesh%elements(elemId) returns the globalId of the element in base_mesh
neighbours(i) = base_mesh%tetrahedrons(mesh%elements(sibhfTuple%sibhfElemLevelId))
end if
end do
end function getLevelMeshElementFacetAttachedNeighbours
function getLevelElementIdsAttachedToVertex(base_mesh, meshLevel, vtxGlobalId) result(elements)
implicit none
class(BaseMesh), intent(in) :: base_mesh
integer, intent(in) :: meshLevel, vtxGlobalId
integer, allocatable :: elements(:)
type(LevelMesh) :: mesh
mesh = base_mesh%levelMeshes(meshLevel)
elements = mesh%v2es(vtxGlobalId)%v2elems
end function getLevelElementIdsAttachedToVertex
function getLevelElementsAttachedToEdge(base_mesh, meshLevel, edge) result(elements)
class(BaseMesh), intent(in) :: base_mesh
integer, intent(in) :: meshLevel
type(MeshEdge), intent(in) :: edge
type(Tetrahedron), allocatable :: elements(:), tempElems(:)
type(LevelMesh) :: mesh
integer, allocatable :: v1Cells(:), v2Cells(:)
integer :: i, j, elemLevelId, k
type(Tetrahedron) :: tet
mesh = base_mesh%levelMeshes(meshLevel)
! get level elements attached to edge%v1 and the ones attached to edge%v2
! find the intersection of these elements
! should have at least one element, the element for which this check happens
! calling for edges or vertices which don't form an element should have weird effects
v1Cells = base_mesh%getLevelElementIdsAttachedToVertex(meshLevel, edge%vertices(1))
v2Cells = base_mesh%getLevelElementIdsAttachedToVertex(meshLevel, edge%vertices(2))
allocate(elements(0))
! find intersection
! a set will allow for constant time search. No set here but we take advantage of the
! idea that the cells will be in increasing order cos mesh%v2es starts comparison
! from 1 to maxNumberElements, => we can use a binary search to do checks in O(log(n)) time
do i = 1, size(v1Cells)
elemLevelId = v1Cells(i)
! search v2Cells
! j -> position of elemLevelId in v2Cells
j = binarySearch(v2Cells, elemLevelId)
if ( j /= -1 ) then
! write(*,*) "for element level ID: ", elemLevelId
! write(*,*) "V2Cells match is : ", v2Cells(j)
tet = base_mesh%tetrahedrons(mesh%elements(elemLevelId))
! to add to the elem
if (allocated(tempElems)) deallocate(tempElems)
allocate(tempElems(size(elements) + 1))
! copy to tempElems
do k = 1, size(elements)
tempElems(k) = elements(k)
end do
! add new element last
tempElems(size(elements) + 1) = tet
if (allocated(elements)) deallocate(elements)
elements = tempElems
end if
end do
end function getLevelElementsAttachedToEdge
! to get both edge and facet-attached neighbours
function getLevelMeshElementNeighbours(base_mesh, elementLevelId, meshLevel) result(neighbours)
implicit none
class(BaseMesh), intent(in) :: base_mesh
integer, intent(in) :: elementLevelId, meshLevel
type(Tetrahedron), allocatable :: tempNeighbours(:), neighbours(:), edgeNeighbours(:)
! type(Tetrahedron) :: facetNeighbours(4)
type(LevelMesh) :: mesh
integer :: i, j, levelId, numNeighbours
type(Tetrahedron) :: checkedTet
mesh = base_mesh%levelMeshes(meshLevel)
! allocate tempNeighbours to the numElements of this level Mesh - using an array as a map
! Necessary to remove duplicate elements quickly, to avoid comparing returned elements cos
! diff. edges and facets can have similar elements incidented on them
allocate(tempNeighbours(mesh%numElements))
! There's actually no need to get the facet Neighbours cos Edge Neighbours will ALWAYS
! have every facet Neighbour Included
! for facet neighbours
! facetNeighbours = base_mesh%getLevelMeshElementFacetAttachedNeighbours(elementLevelId, meshLevel)
! ! facetNeighbours include NULL elements for boundary facets
! do i = 1, 4
! if ( .not. facetNeighbours(i)%isNullElement() ) then
! levelId = facetNeighbours(i)%elementLevelId
! tempNeighbours(levelId) = facetNeighbours(i)
! end if
! end do
checkedTet = base_mesh%tetrahedrons(mesh%elements(elementLevelId))
! for edge neighbours - 6 edges per tet
do i = 1, 6
! get every cell incidented on the edge, excluding the current cell
edgeNeighbours = base_mesh%getLevelElementsAttachedToEdge(meshLevel, checkedTet%edges(i))
! Look through these and either simply update (if there was an element there already)
! tempNeighbours or if it's new slot it in
do j = 1, size(edgeNeighbours)
! edgeNeighbours should not contain any NULL element.
! The checked TET is also included in cells incident on said edge so, check
! to avoid including it too
levelId = edgeNeighbours(j)%elementLevelId
if ( levelId /= checkedTet%elementLevelId ) then
tempNeighbours(levelId) = edgeNeighbours(j)
end if
end do
end do
! To avoid returning NULL elements, we count the actual neighbours found to initialize
! the returned array - neighbours
numNeighbours = 0
do i = 1, mesh%numElements
if ( .not. tempNeighbours(i)%isNullElement() ) then
numNeighbours = numNeighbours + 1
end if
end do
allocate(neighbours(numNeighbours))
! to track position in neighbours
j = 0
do i = 1, mesh%numElements
if ( .not. tempNeighbours(i)%isNullElement() ) then
j = j+1
neighbours(j) = tempNeighbours(i)
end if
end do
write(*,*) "is j same as size of neighbours: ", j == size(neighbours)
end function getLevelMeshElementNeighbours
function getElementLargeNeighbours(base_mesh, tet) result(largeNeighbours)
implicit none
class(BaseMesh), intent(in) :: base_mesh
type(Tetrahedron), intent(in) :: tet
type(Tetrahedron), allocatable :: largeNeighbours(:), tempNeighbours(:), edgeNeighbours(:)
integer :: parentLevel, i, j, levelId, numNeighbours
type(LevelMesh) :: mesh
type(Vertex) :: vtx
! allocate largeNeighbours to size 0 to return that when it checks
allocate(largeNeighbours(0))
! There'd be no largeNeighbours for elements on Level 1
if (tet%level < 2) return
parentLevel = tet%level - 1
mesh = base_mesh%levelMeshes(parentLevel)
! allocate tempNeighbours to numElements on parentLevel mesh to help with avoiding duplicates
! and null values
allocate(tempNeighbours(mesh%numElements))
! get parent edge for every vertex in tet
! over the vertices in tet
do i = 1, 4
vtx = tet%vertices(i)
! We need to check only Vertices INTERPOLATED from the parentLevel
! cos an element can have vertices created higher up the hierarchy too
! but there must be vertices directly interpolated from its parentElement
if ( vtx%elementLevel /= parentLevel) cycle
! get elements incident to those edges
! get neighbours for the parentEdge of this vertex in the parent mesh
! write(*,*) "ParentLevel: ", parentLevel
! write(*,*) "ParentEdge vertices: ", vtx%parentEdge%vertices
edgeNeighbours = base_mesh%getLevelElementsAttachedToEdge(parentLevel, vtx%parentEdge)
do j = 1, size(edgeNeighbours)
! edgeNeighbours should not contain any NULL element.
! The parent of this TET is also included in cells incident on said edge so, check
! to avoid including it too.
! We can't count numNeighbours here cos diff. edges can have the same elements incident on them
! i.e. tempNeighbours(element) can be updated and re-updated, but there'd be just one eventually
levelId = edgeNeighbours(j)%elementLevelId
if ( levelId /= tet%parentElementLevelId ) then
tempNeighbours(levelId) = edgeNeighbours(j)
end if
end do
end do
! To avoid returning NULL elements, we count the actual neighbours found to initialize
! the returned array - neighbours
numNeighbours = 0
do i = 1, mesh%numElements
if ( .not. tempNeighbours(i)%isNullElement() ) then
numNeighbours = numNeighbours + 1
end if
end do
if (allocated(largeNeighbours)) deallocate(largeNeighbours)
allocate(largeNeighbours(numNeighbours))
! to track position in neighbours
j = 0
do i = 1, mesh%numElements
if ( .not. tempNeighbours(i)%isNullElement() ) then
j = j+1
largeNeighbours(j) = tempNeighbours(i)
end if
end do
end function getElementLargeNeighbours
logical function elementHasEdge(tet, edge)
implicit none
class(Tetrahedron), intent(in) :: tet
class(MeshEdge), intent(in) :: edge
integer :: i
elementHasEdge = .false.
do i = 1, 6
if ( tet%edges(i)%isEdgeEqual(edge) ) then
elementHasEdge = .true.
exit
end if
end do
end function elementHasEdge
function getEdgeMidVertex(base_mesh, edge, tet) result(midVertex)
implicit none
class(BaseMesh), intent(in) :: base_mesh
type(MeshEdge), intent(in) :: edge
type(Tetrahedron), intent(in) :: tet
type(Vertex) :: midVertex
integer :: i, j, child1LevelId
type(LevelMesh) :: mesh, nextMesh
type(Tetrahedron) :: childTet
! if tet is a null element, return
if ( tet%isNullElement() ) return
mesh = base_mesh%levelMeshes(tet%level)
! check the children of tet for a node whose parent node is `edge`
! NULL vertex if tet is active => not refined or
! current level e2ce array is of size 0 or the next level doesn't exist
write(*,*) "Check if tet is active: ", tet%isActive
write(*,*) "Check if tet has edge: ", tet%elementHasEdge(edge)
if ( .not. tet%isActive .and. tet%elementHasEdge(edge)) then
! tet's been refined, children should exist
child1LevelId = mesh%e2ce(tet%elementLevelId)
! check the children for a node whose parent is edge
nextMesh = base_mesh%levelMeshes(tet%level + 1)
! the eight children are stored in order
do i = child1LevelId, (child1LevelId + 7)
childTet = base_mesh%tetrahedrons(nextMesh%elements(i))
! over the vertices of child tet
do j = 1, 4
if ( childTet%vertices(j)%parentEdge%isEdgeEqual(edge) ) then
midVertex = childTet%vertices(j)
return
end if
end do
end do
end if
end function getEdgeMidVertex
logical function isNullElement(tet)
implicit none
class(Tetrahedron), intent(in) :: tet
isNullElement = .false.
if ( tet%elementLevelId == -1 .or. tet%level == -1 .or. tet%globalId == -1 ) then
isNullElement = .true.
end if
end function isNullElement
function getLevelMeshHangingNodes(base_mesh, meshLevel) result(hangingNodes)
implicit none
class(BaseMesh), intent(in) :: base_mesh
integer, intent(in) :: meshLevel
type(Vertex), allocatable :: hangingNodes(:), tempNodes(:)
! type(LevelMesh) :: mesh
integer :: i,j,k, numHangingNodes, parentLevel
type(Vertex) :: vtx
type(Tetrahedron), allocatable :: tets(:)
! allocate it's size to 0, so it returns an empty array if no hanging node on level
allocate(hangingNodes(0))
numHangingNodes = 0
parentLevel = (meshLevel - 1)
! No hanging Nodes on original mesh, we expect an initial conformal and manifold mesh
if ( meshLevel > 1 ) then
! allocate size for all vertices, to minimize all the copying and size change
! You'd extract hanging nodes later to hanging nodes by checking if the vertex is null
allocate(tempNodes(base_mesh%numVertices))
! L1 nodes i.e. nodes on L2 interpolated from L1, can only be hanging on L2 with the
! 1-irregularity index rule applied
! get nodes from all nodes with element level = meshLevel-1 i.e. these nodes are new
! nodes from L1 that form elements on L2
! write(*,*) "BaseMesh total vertices: ", base_mesh%numVertices
do i = 1, base_mesh%numVertices
vtx = base_mesh%v2pe(i)
if ( vtx%elementLevel == parentLevel ) then
! this vertex is on `meshLevel`, check it
! get all cells incident on its parent edge in its elementLevel
tets = base_mesh%getLevelElementsAttachedToEdge(parentLevel, vtx%parentEdge)
! if all are refined, not hanging node, if any is not refined, node is hanging
! write(*,*) "Size of edge attached level elements: ", size(tets)
do j = 1, size(tets)
if ( .not. tets(j)%isNullElement() .and. tets(j)%isActive ) then
! this tet is not refined
numHangingNodes = numHangingNodes + 1
tempNodes(i) = vtx
write(*,*) "hanging node is vtx with globalid: ", vtx%globalId
! exit the loop cos hanging node found
exit
end if
end do
end if
if ( vtx%elementLevel > parentLevel ) then
exit
end if
end do
! check if hanging nodes were found
if ( numHangingNodes > 0 ) then
if ( allocated(hangingNodes) ) deallocate(hangingNodes)
allocate(hangingNodes(numHangingNodes))
! extract these nodes from tempNodes
! to track nodes already slotted in hangingNodes
k = 1
do i = 1, base_mesh%numVertices
vtx = tempNodes(i)
if ( .not. vtx%isNullVertex() ) then
hangingNodes(k) = vtx
k = k + 1
end if
end do
! write(*,*) "Is K same as numHangingNodes: ", k==numHangingNodes
! write(*,*) "numHangingNodes: ", numHangingNodes
! write(*,*) "k: ", k
end if
end if
end function getLevelMeshHangingNodes
function createMidVertexOnEdge(base_mesh, edge, parentTet, vtxGlobalId) result(edgeMidVtx)
implicit none
class(BaseMesh), intent(in) :: base_mesh
type(MeshEdge), intent(in) :: edge
type(Tetrahedron), intent(in) :: parentTet
integer, intent(in) :: vtxGlobalId
type(Vertex) :: edgeMidVtx, v1, v2
double precision :: x, y, z
v1 = base_mesh%v2pe(edge%vertices(1))
v2 = base_mesh%v2pe(edge%vertices(2))
x = (v1%x + v2%x) / 2
y = (v1%y + v2%y) / 2
z = (v1%z + v2%z) / 2
edgeMidVtx = createVertex(x, y, z, vtxGlobalId, parentTet%elementLevelId, parentTet%level)
! assign `edge` as parentEdge for this mid vertex
edgeMidVtx%parentEdge = edge
end function createMidVertexOnEdge
function getLevelMeshNumNodes(base_mesh, meshLevel) result(numLevelNodes)
implicit none
class(BaseMesh), intent(in) :: base_mesh
integer, intent(in) :: meshLevel
integer :: j, numLevelNodes
type(Vertex) :: vtx
! to get numNodes for this level, count every node in order with parentLevel < meshLevel
numLevelNodes = 0
do j = 1, base_mesh%numVertices
vtx = base_mesh%v2pe(j)
if ( vtx%elementLevel < meshLevel ) then
numLevelNodes = numLevelNodes + 1
end if
if(vtx%elementLevel >= meshLevel) exit
end do
end function getLevelMeshNumNodes
!!!!! SUBROUTINES !!!!!!
subroutine initialize(base_mesh)
class(BaseMesh), intent(inout) :: base_mesh
! set stat values to zero
base_mesh%numElements = 0
base_mesh%numVertices = 0
base_mesh%numLevels = 0
! allocate arrays
! let vertices start with space for 10
allocate(base_mesh%vertices(10))
allocate(base_mesh%v2pe(10))
allocate(base_mesh%tetrahedrons(0))
allocate(base_mesh%levelMeshes(0))
end subroutine initialize
subroutine initializeLevelMesh(base_mesh)
class(BaseMesh), intent(inout) :: base_mesh
type(LevelMesh) :: mesh
integer :: meshLevel, i
type(LevelMesh) :: levelMeshes(size(base_mesh%levelMeshes) + 1)
base_mesh%numLevels = base_mesh%numLevels + 1
levelMeshes(base_mesh%numLevels) = mesh
do i = 1, size(base_mesh%levelMeshes)
levelMeshes(i) = base_mesh%levelMeshes(i)
end do
base_mesh%levelMeshes = levelMeshes
meshLevel = base_mesh%numLevels
mesh = base_mesh%levelMeshes(meshLevel)
mesh%numElements = 0
mesh%level = meshLevel
! allocate(mesh%elementConn(0, 0))
allocate(mesh%elements(0))
allocate(mesh%sibhfcs(0,0))
allocate(mesh%v2hf(0))
allocate(mesh%v2es(0))
allocate(mesh%e2pe(0))
allocate(mesh%e2ce(0))
base_mesh%levelMeshes(meshLevel) = mesh
end subroutine initializeLevelMesh
! Add an array of tetrahedrons to the base Mesh and
! the levelMesh of the specified level
! A LevelMesh with level `meshLevel` MUST EXIST.
! Handles allocation of Cell Space
subroutine addTetrahedrons(base_mesh, meshLevel, tetrahedrons)
implicit none
class(BaseMesh), intent(inout) :: base_mesh
type(LevelMesh) :: mesh
integer :: meshLevel, i
! type(Vertex) :: tetVertices(4)
! type(Vertex) :: hfVertices(3)
type(Tetrahedron), allocatable :: tetrahedrons(:)
! allocate cells the size of the number of tetrahedrons in `tetrahedrons`
call base_mesh%allocateCellSpace(meshLevel, size(tetrahedrons))
mesh = base_mesh%levelMeshes(meshLevel)
! do for each tetrahedron in tetrahedrons
do i = 1, size(tetrahedrons)
base_mesh%numElements = base_mesh%numElements + 1
mesh%numElements = mesh%numElements + 1
! add this vertices to base_mesh v2pe if mesh level > 1
! if ( meshLevel > 1 ) then
! ! New vertex from interpolation. map to parent
! call base_mesh%mapElementVerticesToParent(tetrahedrons(i))
! end if
! We're now adding original vertices from L1 to v2pe, giving them 0 as parent level Id and parent level
! this routine can run for every vertex now
call base_mesh%mapElementVerticesToParent(tetrahedrons(i))
! add tet globalID to elements of level mesh
! tetrahedrons(i)%globalId = base_mesh%numElements
mesh%elements(mesh%numElements) = tetrahedrons(i)%globalId
! add Tet to base_mesh tetrahedrons
base_mesh%tetrahedrons(base_mesh%numElements) = tetrahedrons(i)
end do
! save mesh to the base mesh
base_mesh%levelMeshes(meshLevel) = mesh
end subroutine addTetrahedrons
subroutine expandV2pe(base_mesh)
implicit none
class(BaseMesh), intent(inout) :: base_mesh
type(Vertex), allocatable :: v2pe(:)
type(MeshConstants) :: meshConstant
integer :: newSize, i
! expand it by 20% of its current size and copy the elements in it to the new one
newSize = (size(base_mesh%v2pe) * meshConstant%v2peIncrement) + size(base_mesh%v2pe)
allocate(v2pe(newSize))
do i = 1, size(base_mesh%v2pe)
v2pe(i) = base_mesh%v2pe(i)
end do
if ( allocated(base_mesh%v2pe) ) deallocate(base_mesh%v2pe)
base_mesh%v2pe = v2pe
end subroutine expandV2pe
! Handle new vertices, map vertex to ONE of its parents
! Add vertex to base Mesh. Expand v2pe. Basically saves the vertices
! As vertices are created, base_mesh%numVertices must keep incrementing to
! maintain the globalId count
subroutine mapElementVerticesToParent(base_mesh, tet)
implicit none
class(BaseMesh), intent(inout) :: base_mesh
type(Tetrahedron), intent(inout) :: tet
integer :: i, globalId
type(Vertex) :: vtx
! we want to assign global IDS to nodes not yet created here
! and attach to the first parent that sends it
! 4 nodes per Tet
do i = 1, 4
! for each node, get the globalId
globalId = tet%vertices(i)%globalId
! By checking if it's been saved already, we avoid updating the parentLevelId and the localId On current element
! if the globalId > size(v2pe) ==> it's never been saved before