Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/Interpolation.F90
3203 views
1
!/*****************************************************************************/
2
! *
3
! * Elmer, A Finite Element Software for Multiphysical Problems
4
! *
5
! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland
6
! *
7
! * This library is free software; you can redistribute it and/or
8
! * modify it under the terms of the GNU Lesser General Public
9
! * License as published by the Free Software Foundation; either
10
! * version 2.1 of the License, or (at your option) any later version.
11
! *
12
! * This library is distributed in the hope that it will be useful,
13
! * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
! * Lesser General Public License for more details.
16
! *
17
! * You should have received a copy of the GNU Lesser General Public
18
! * License along with this library (in file ../LGPL-2.1); if not, write
19
! * to the Free Software Foundation, Inc., 51 Franklin Street,
20
! * Fifth Floor, Boston, MA 02110-1301 USA
21
! *
22
! *****************************************************************************/
23
!
24
!/******************************************************************************
25
! *
26
! * Authors: Juha Ruokolainen, Ville Savolainen
27
! * Email: [email protected]
28
! * Web: http://www.csc.fi/elmer
29
! * Address: CSC - IT Center for Science Ltd.
30
! * Keilaranta 14
31
! * 02101 Espoo, Finland
32
! *
33
! * Original Date: 01 Oct 1996
34
! *
35
! ****************************************************************************/
36
37
!> \ingroup ElmerLib
38
!> \{
39
40
!------------------------------------------------------------------------------
41
!> Module containing interpolation and quadrant tree routines.
42
!------------------------------------------------------------------------------
43
44
MODULE Interpolation
45
46
USE Types
47
USE SParIterGlobals
48
USE CoordinateSystems
49
USE ElementDescription, ONLY : GlobalToLocal, ElementInfo, GetElementType, &
50
SquareFaceDOFsOrdering, EdgeElementStyle, mGetElementDOFs
51
USE PElementMaps, ONLY : isActivePElement
52
USE Integration, ONLY : GaussIntegrationPoints_t, GaussPoints
53
USE GeneralUtils, ONLY : AllocateMatrix
54
USE ListMatrix, ONLY : List_AddToMatrixElement, List_toCRSMatrix
55
56
IMPLICIT NONE
57
58
CONTAINS
59
60
!------------------------------------------------------------------------------
61
SUBROUTINE FindLeafElements( Point, dim, RootQuadrant, LeafQuadrant )
62
!------------------------------------------------------------------------------
63
REAL(KIND=dp), DIMENSION(3) :: Point
64
REAL(KIND=dp), DIMENSION(6) :: GeometryBoundingBox
65
INTEGER :: dim
66
TYPE(Quadrant_t), POINTER :: RootQuadrant, LeafQuadrant
67
!------------------------------------------------------------------------------
68
69
LeafQuadrant => RootQuadrant
70
GeometryBoundingBox = RootQuadrant % BoundingBox
71
!
72
! Find recursively the last generation
73
! quadrant the point belongs to:
74
! -------------------------------------
75
CALL FindPointsQuadrant(Point, dim, LeafQuadrant)
76
77
CONTAINS
78
79
!------------------------------------------------------------------------------
80
RECURSIVE SUBROUTINE FindPointsQuadrant(Point, dim, MotherQuadrant)
81
!------------------------------------------------------------------------------
82
83
REAL(KIND=dp), DIMENSION(3) :: Point
84
INTEGER :: dim
85
TYPE(Quadrant_t), POINTER :: MotherQuadrant
86
!------------------------------------------------------------------------------
87
TYPE(Quadrant_t), POINTER :: ChildQuadrant
88
INTEGER :: i
89
REAL(KIND=dp) :: BBox(6), eps3
90
REAL(KIND=dp), PARAMETER :: eps2=0.0_dp !!!!!!! *** !!!!!!
91
!------------------------------------------------------------------------------
92
93
! Loop over ChildQuadrants:
94
! -------------------------
95
DO i=1, 2**dim
96
ChildQuadrant => MotherQuadrant % ChildQuadrants(i) % Quadrant
97
BBox = ChildQuadrant % BoundingBox
98
!
99
! ******** NOTE: eps2 set to zero at the moment **********
100
!
101
eps3 = eps2 * MAXVAL(BBox(4:6) - BBox(1:3))
102
BBox(1:3) = BBox(1:3) - eps3
103
BBox(4:6) = BBox(4:6) + eps3
104
!
105
! Is the point in ChildQuadrant(i)?
106
! ----------------------------------
107
IF ( ( Point(1) >= BBox(1)) .AND. (Point(1) <= BBox(4) ) .AND. &
108
( Point(2) >= BBox(2)) .AND. (Point(2) <= BBox(5) ) .AND. &
109
( Point(3) >= BBox(3)) .AND. (Point(3) <= BBox(6) ) ) EXIT
110
END DO
111
112
IF ( i > 2**dim ) THEN
113
! PRINT*,'Warning: point not found in any of the quadrants ?'
114
NULLIFY( MotherQuadrant )
115
RETURN
116
END IF
117
118
MotherQuadrant => ChildQuadrant
119
!
120
! Are we already in the LeafQuadrant ?
121
! If not, search for the next generation
122
! ChildQuadrants for the point:
123
! ---------------------------------------
124
IF ( ASSOCIATED ( MotherQuadrant % ChildQuadrants ) )THEN
125
CALL FindPointsQuadrant( Point, dim, MotherQuadrant )
126
END IF
127
!------------------------------------------------------------------------------
128
END SUBROUTINE FindPointsQuadrant
129
!------------------------------------------------------------------------------
130
131
!------------------------------------------------------------------------------
132
END SUBROUTINE FindLeafElements
133
!------------------------------------------------------------------------------
134
135
136
!------------------------------------------------------------------------------
137
!> Checks whether a given point belongs to a given bulk element.
138
!> If it does, returns the local coordinates in the bulk element
139
!------------------------------------------------------------------------------
140
FUNCTION PointInElement( Element, ElementNodes, Point, &
141
LocalCoordinates, GlobalEps, LocalEps, NumericEps, &
142
GlobalDistance, LocalDistance, EdgeBasis, &
143
USolver ) RESULT(IsInElement)
144
!------------------------------------------------------------------------------
145
TYPE(Element_t), POINTER :: Element !< Bulk element we are checking
146
TYPE(Nodes_t) :: ElementNodes !< The nodal points of the bulk element
147
LOGICAL :: IsInElement !< Whether the node lies within the element
148
REAL(KIND=dp), DIMENSION(:) :: Point !< Point under study.
149
REAL(KIND=dp), DIMENSION(:) :: LocalCoordinates !< Local coordinates corresponding to the global ones.
150
REAL(KIND=dp), OPTIONAL :: GlobalEps !< Required accuracy of global coordinates
151
REAL(KIND=dp), OPTIONAL :: LocalEps !< Required accuracy of local coordinates
152
REAL(KIND=dp), OPTIONAL :: NumericEps !< Accuracy of numberical operations
153
REAL(KIND=dp), OPTIONAL :: GlobalDistance !< Returns the distance from the element in global coordinates.
154
REAL(KIND=dp), OPTIONAL :: LocalDistance !< Returns the distance from the element in local coordinates.
155
LOGICAL, OPTIONAL :: EdgeBasis
156
TYPE(Solver_t), POINTER, OPTIONAL :: USolver
157
!------------------------------------------------------------------------------
158
INTEGER :: n
159
INTEGER :: i
160
LOGICAL :: ComputeDistance, trans
161
REAL(KIND=dp) :: ug,vg,wg,xdist,ydist,zdist,sumdist,eps0,eps1,eps2,escale,&
162
minx,maxx,miny,maxy,minz,maxz
163
!------------------------------------------------------------------------------
164
165
166
! Initialize:
167
! -----------
168
169
IsInElement = .FALSE.
170
n = Element % TYPE % NumberOfNodes
171
172
! The numeric precision
173
IF ( PRESENT(NumericEps) ) THEN
174
eps0 = NumericEps
175
ELSE
176
eps0 = EPSILON( eps0 )
177
END IF
178
179
! The rough check, used for global coordinates
180
IF ( PRESENT(GlobalEps) ) THEN
181
Eps1 = GlobalEps
182
ELSE
183
Eps1 = 1.0e-4
184
END IF
185
186
! The more detailed condition, used for local coordinates
187
IF ( PRESENT(LocalEps) ) THEN
188
Eps2 = LocalEps
189
ELSE
190
Eps2 = 1.0e-10
191
END IF
192
193
IF( PRESENT( LocalDistance ) ) THEN
194
LocalDistance = HUGE( LocalDistance )
195
END IF
196
197
IF( Eps1 < 0.0_dp ) THEN
198
CONTINUE
199
ELSE IF( PRESENT( GlobalDistance ) ) THEN
200
! When distance has to be computed all coordinate directions need to be checked
201
202
minx = MINVAL( ElementNodes % x(1:n) )
203
maxx = MAXVAL( ElementNodes % x(1:n) )
204
205
miny = MINVAL( ElementNodes % y(1:n) )
206
maxy = MAXVAL( ElementNodes % y(1:n) )
207
208
minz = MINVAL( ElementNodes % z(1:n) )
209
maxz = MAXVAL( ElementNodes % z(1:n) )
210
211
xdist = MAX( MAX( Point(1) - maxx, 0.0_dp ), minx - Point(1) )
212
ydist = MAX( MAX( Point(2) - maxy, 0.0_dp ), miny - Point(2) )
213
zdist = MAX( MAX( Point(3) - maxz, 0.0_dp ), minz - Point(3) )
214
215
GlobalDistance = SQRT( xdist**2 + ydist**2 + zdist**2)
216
217
IF( xdist > eps0 + eps1 * (maxx - minx) ) RETURN
218
IF( ydist > eps0 + eps1 * (maxy - miny) ) RETURN
219
IF( zdist > eps0 + eps1 * (maxz - minz) ) RETURN
220
ELSE
221
! Otherwise make decision independently after each coordinate direction
222
223
minx = MINVAL( ElementNodes % x(1:n) )
224
maxx = MAXVAL( ElementNodes % x(1:n) )
225
xdist = MAX( MAX( Point(1) - maxx, 0.0_dp ), minx - Point(1) )
226
IF( xdist > eps0 + eps1 * (maxx - minx) ) RETURN
227
228
miny = MINVAL( ElementNodes % y(1:n) )
229
maxy = MAXVAL( ElementNodes % y(1:n) )
230
ydist = MAX( MAX( Point(2) - maxy, 0.0_dp ), miny - Point(2) )
231
IF( ydist > eps0 + eps1 * (maxy - miny) ) RETURN
232
233
minz = MINVAL( ElementNodes % z(1:n) )
234
maxz = MAXVAL( ElementNodes % z(1:n) )
235
zdist = MAX( MAX( Point(3) - maxz, 0.0_dp ), minz - Point(3) )
236
IF( zdist > eps0 + eps1 * (maxz - minz) ) RETURN
237
END IF
238
239
! Get element local coordinates from global
240
! coordinates of the point:
241
! -----------------------------------------
242
CALL GlobalToLocal( ug, vg, wg, Point(1), Point(2), Point(3), &
243
Element, ElementNodes )
244
245
! Currently the eps of global coordinates is mixed with the eps of local
246
! coordinates which is a bit disturbin. There could be sloppier global
247
! coordinate search and a more rigorous local coordinate search.
248
249
SELECT CASE ( Element % TYPE % ElementCode / 100 )
250
CASE(1)
251
sumdist = ug
252
253
CASE(2)
254
sumdist = MAX( ug - 1.0, MAX( -ug - 1.0, 0.0 ) )
255
256
CASE(3)
257
sumdist = MAX( -ug, 0.0 ) + MAX( -vg, 0.0 )
258
sumdist = sumdist + MAX( ug + vg - 1.0_dp, 0.0 )
259
260
CASE(4)
261
sumdist = MAX( ug - 1.0, MAX( -ug -1.0, 0.0 ) )
262
sumdist = sumdist + MAX( vg - 1.0, MAX( -vg - 1.0, 0.0 ) )
263
264
CASE(5)
265
sumdist = MAX( -ug, 0.0 ) + MAX( -vg, 0.0 ) + MAX( -wg, 0.0 )
266
sumdist = sumdist + MAX( ug + vg + wg - 1.0, 0.0 )
267
268
CASE(7)
269
sumdist = MAX( -ug, 0.0 ) + MAX( -vg, 0.0 )
270
sumdist = sumdist + MAX( ug + vg - 1.0_dp, 0.0 )
271
sumdist = sumdist + MAX( wg - 1.0, MAX( -wg - 1.0, 0.0 ) )
272
273
CASE(8)
274
sumdist = MAX( ug - 1.0, MAX( -ug -1.0, 0.0 ) )
275
sumdist = sumdist + MAX( vg - 1.0, MAX( -vg - 1.0, 0.0 ) )
276
sumdist = sumdist + MAX( wg - 1.0, MAX( -wg - 1.0, 0.0 ) )
277
278
CASE DEFAULT
279
WRITE( Message,'(A,I4)') 'Not implemented for element code',&
280
Element % TYPE % ElementCode
281
CALL Warn('PointInElement',Message)
282
END SELECT
283
284
285
IF( sumdist < eps0 + eps2 ) THEN
286
IsInElement = .TRUE.
287
END IF
288
289
IF( PRESENT( LocalDistance ) ) THEN
290
LocalDistance = sumdist
291
END IF
292
293
294
trans = PRESENT(EdgeBasis)
295
IF(trans) trans=EdgeBasis
296
trans = trans .OR. isActivePElement(Element,USolver)
297
298
IF (trans) THEN
299
SELECT CASE(Element % Type % ElementCode/100)
300
CASE(3)
301
ug = 2*ug + vg - 1
302
vg = SQRT(3._dp)*vg
303
CASE(5)
304
ug = 2*ug + vg + wg - 1
305
vg = SQRT(3._dp)*vg + 1/SQRT(3._dp)*wg
306
wg = 2*SQRT(2/3._dp)*wg
307
CASE(6)
308
wg = SQRT(2._dp)*wg
309
CASE(7)
310
ug = 2*ug + vg - 1
311
vg = SQRT(3._dp)*vg
312
END SELECT
313
END IF
314
315
LocalCoordinates(1) = ug
316
LocalCoordinates(2) = vg
317
LocalCoordinates(3) = wg
318
!------------------------------------------------------------------------------
319
END FUNCTION PointInElement
320
!------------------------------------------------------------------------------
321
322
323
!------------------------------------------------------------------------------
324
!> Builds a tree hierarchy recursively bisectioning the geometry
325
!> bounding box, and partitioning the bulk elements in the
326
!> last level of the tree hierarchy
327
!------------------------------------------------------------------------------
328
SUBROUTINE BuildQuadrantTree(Mesh, BoundingBox, RootQuadrant)
329
!------------------------------------------------------------------------------
330
TYPE(Mesh_t) :: Mesh !< Finite element mesh
331
REAL(KIND=dp), DIMENSION(6) :: BoundingBox !< XMin, YMin, ZMin, XMax, YMax, ZMax
332
TYPE(Quadrant_t), POINTER :: RootQuadrant !< Quadrant tree structure root
333
!------------------------------------------------------------------------------
334
INTEGER :: dim, Generation, i
335
REAL(KIND=dp) :: XMin, XMax, YMin, YMax, ZMin, ZMax
336
TYPE(Quadrant_t), POINTER :: MotherQuadrant
337
INTEGER :: MaxLeafElems
338
339
dim = MAX( Mesh % MeshDim, CoordinateSystemDimension() )
340
341
IF ( dim == 3 ) THEN
342
MaxLeafElems = 16
343
ELSE
344
MaxLeafElems = 8
345
END IF
346
347
Generation = 0
348
349
XMin = BoundingBox(1)
350
XMax = BoundingBox(4)
351
IF ( dim >= 2 ) THEN
352
YMin = BoundingBox(2)
353
YMax = BoundingBox(5)
354
ELSE
355
YMin = 0.d0
356
YMax = 0.d0
357
END IF
358
IF ( dim == 3) THEN
359
ZMin = BoundingBox(3)
360
ZMax = BoundingBox(6)
361
ELSE
362
ZMin = 0.d0
363
ZMax = 0.d0
364
END IF
365
366
! Create Mother of All Quadrants
367
ALLOCATE ( RootQuadrant )
368
369
RootQuadrant % BoundingBox = [ XMin, YMin, ZMin, XMax, YMax, ZMax ]
370
RootQuadrant % NElemsInQuadrant = Mesh % NumberOfBulkElements
371
372
ALLOCATE ( RootQuadrant % Elements( Mesh % NumberOfBulkElements ) )
373
RootQuadrant % Elements = [ (i, i=1,Mesh % NumberOfBulkElements) ]
374
375
! Start building the quadrant tree
376
CALL Info( 'BuildQuandrantTree', 'Start', Level=4 )
377
MotherQuadrant => RootQuadrant
378
CALL CreateChildQuadrants( MotherQuadrant, dim )
379
CALL Info( 'BuildQuandrantTree', 'Ready', Level=4 )
380
381
CONTAINS
382
383
!-------------------------------------------------------------------------------
384
RECURSIVE SUBROUTINE CreateChildQuadrants( MotherQuadrant, dim )
385
!-------------------------------------------------------------------------------
386
387
!-------------------------------------------------------------------------------
388
! Model is automatically available (internal subroutine)
389
!-------------------------------------------------------------------------------
390
TYPE(Quadrant_t), POINTER :: MotherQuadrant
391
INTEGER :: i, dim, n
392
TYPE(QuadrantPointer_t) :: ChildQuadrant(8)
393
REAL(KIND=dp) :: XMin, XMax, YMin, YMax, ZMin, ZMax
394
395
!-------------------------------------------------------------------------------
396
! Create 2**dim child quadrants
397
!-------------------------------------------------------------------------------
398
n = 2**dim
399
ALLOCATE ( MotherQuadrant % ChildQuadrants(n) )
400
DO i=1, n
401
ALLOCATE( MotherQuadrant % ChildQuadrants(i) % Quadrant )
402
ChildQuadrant(i) % Quadrant => &
403
MotherQuadrant % ChildQuadrants(i) % Quadrant
404
ChildQuadrant(i) % Quadrant % NElemsInQuadrant = 0
405
NULLIFY ( ChildQuadrant(i) % Quadrant % Elements )
406
NULLIFY ( ChildQuadrant(i) % Quadrant % ChildQuadrants )
407
END DO
408
!-------------------------------------------------------------------------------
409
XMin = MotherQuadrant % BoundingBox(1)
410
YMin = MotherQuadrant % BoundingBox(2)
411
ZMin = MotherQuadrant % BoundingBox(3)
412
413
XMax = MotherQuadrant % BoundingBox(4)
414
YMax = MotherQuadrant % BoundingBox(5)
415
ZMax = MotherQuadrant % BoundingBox(6)
416
MotherQuadrant % Size = MAX ( MAX( XMax-XMin, YMax-YMin), ZMax-ZMin )
417
418
ChildQuadrant(1) % Quadrant % BoundingBox = [ XMin, YMin, ZMin, &
419
(XMin + XMax)/2.d0, (YMin + YMax)/2.d0, (ZMin + ZMax)/2.d0 ]
420
421
ChildQuadrant(2) % Quadrant % BoundingBox = [ (XMin+XMax)/2.d0, &
422
YMin, ZMin, XMax, (YMin+YMax)/2.d0, (ZMin+ZMax)/2.d0 ]
423
424
IF ( dim >= 2 ) THEN
425
ChildQuadrant(3) % Quadrant % BoundingBox = [ XMin, (YMin+YMax)/2.d0, &
426
ZMin, (XMin+XMax)/2.d0, YMax, (ZMin+ZMax)/2.d0 ]
427
428
ChildQuadrant(4) % Quadrant % BoundingBox = [ (XMin+XMax)/2.d0, &
429
(YMin+YMax)/2.d0, ZMin, XMax, YMax, (ZMin+ZMax)/2.d0 ]
430
END IF
431
432
IF ( dim == 3 ) THEN
433
ChildQuadrant(5) % Quadrant % BoundingBox = [ XMin, YMin, &
434
(ZMin+ZMax)/2.d0, (XMin+XMax)/2.d0, (YMin+YMax)/2.d0, ZMax ]
435
436
ChildQuadrant(6) % Quadrant % BoundingBox = [ (XMin+XMax)/2.d0, YMin, &
437
(ZMin+ZMax)/2.d0, XMax, (YMin+YMax)/2.d0, ZMax ]
438
439
ChildQuadrant(7) % Quadrant % BoundingBox = [ XMin, (YMin+YMax)/2.d0, &
440
(ZMin+ZMax)/2.d0, (XMin+XMax)/2.d0, YMax, ZMax ]
441
442
ChildQuadrant(8) % Quadrant % BoundingBox = [ (XMin+XMax)/2.d0, &
443
(YMin+YMax)/2.d0, (ZMin+ZMax)/2.d0, XMax, YMax, ZMax ]
444
END IF
445
!-------------------------------------------------------------------------------
446
447
448
!-------------------------------------------------------------------------------
449
! Loop over all elements in the mother quadrant,
450
! placing them in one of the 2^dim child quadrants
451
!-------------------------------------------------------------------------------
452
CALL PutElementsInChildQuadrants( ChildQuadrant, MotherQuadrant, dim )
453
454
!-------------------------------------------------------------------------------
455
! Check whether we need to branch for the next level
456
!-------------------------------------------------------------------------------
457
DO i=1,n
458
ChildQuadrant(i) % Quadrant % Size = MotherQuadrant % Size / 2
459
IF ( ChildQuadrant(i) % Quadrant % NElemsInQuadrant > MaxLeafElems ) THEN
460
IF ( ChildQuadrant(i) % Quadrant % Size > &
461
ChildQuadrant(i) % Quadrant % MinElementSize ) THEN
462
IF ( Generation <= 8 ) THEN
463
Generation = Generation + 1
464
CALL CreateChildQuadrants( ChildQuadrant(i) % Quadrant, dim )
465
Generation = Generation - 1
466
END IF
467
END IF
468
END IF
469
END DO
470
471
DEALLOCATE ( MotherQuadrant % Elements )
472
NULLIFY ( MotherQuadrant % Elements )
473
!-------------------------------------------------------------------------------
474
END SUBROUTINE CreateChildQuadrants
475
!-------------------------------------------------------------------------------
476
477
478
!-------------------------------------------------------------------------------
479
!> Loop over all elements in the MotherQuadrant, placing them
480
!> in one of the 2^dim child quadrants.
481
!-------------------------------------------------------------------------------
482
RECURSIVE SUBROUTINE PutElementsInChildQuadrants( ChildQuadrant, &
483
MotherQuadrant, dim )
484
!-------------------------------------------------------------------------------
485
TYPE(QuadrantPointer_t) :: ChildQuadrant(8)
486
TYPE(Quadrant_t), POINTER :: MotherQuadrant
487
INTEGER :: dim
488
REAL(KIND=dp) :: eps3
489
REAL(KIND=dp), PARAMETER :: eps2=2.5d-2
490
!-------------------------------------------------------------------------------
491
492
TYPE(Element_t), POINTER :: CurrentElement
493
INTEGER :: i, j, t, n
494
INTEGER, POINTER :: NodeIndexes(:)
495
INTEGER :: ElementList(2**dim, MotherQuadrant % NElemsInQuadrant)
496
497
LOGICAL :: ElementInQuadrant
498
REAL(KIND=dp) :: BBox(6), XMin, XMax, YMin, YMax, ZMin, ZMax, ElementSize
499
500
!-------------------------------------------------------------------------------
501
502
DO i=1,2**dim
503
ChildQuadrant(i) % Quadrant % NElemsInQuadrant = 0
504
ChildQuadrant(i) % Quadrant % MinElementSize = 1.0d20
505
END DO
506
507
!-------------------------------------------------------------------------------
508
DO t=1, MotherQuadrant % NElemsInQuadrant
509
!-------------------------------------------------------------------------------
510
CurrentElement => Mesh % Elements( MotherQuadrant % Elements(t) )
511
n = CurrentElement % TYPE % NumberOfNodes
512
NodeIndexes => CurrentElement % NodeIndexes
513
514
! Get element coordinates
515
XMin = MINVAL( Mesh % Nodes % x(NodeIndexes) )
516
XMax = MAXVAL( Mesh % Nodes % x(NodeIndexes) )
517
YMin = MINVAL( Mesh % Nodes % y(NodeIndexes) )
518
YMax = MAXVAL( Mesh % Nodes % y(NodeIndexes) )
519
ZMin = MINVAL( Mesh % Nodes % z(NodeIndexes) )
520
ZMax = MAXVAL( Mesh % Nodes % z(NodeIndexes) )
521
ElementSize = MAX( MAX( XMax-XMin, YMax-YMin ), ZMax-ZMin )
522
523
!-------------------------------------------------------------------------------
524
! Is the element in one of the child quadrants?:
525
! Check whether element bounding box crosses any of the child quadrant
526
! bounding boxes:
527
!-------------------------------------------------------------------------------
528
DO i=1, 2**dim ! loop over child quadrants
529
530
BBox = ChildQuadrant(i) % Quadrant % BoundingBox
531
532
eps3 = 0.0d0
533
eps3 = MAX( eps3, BBox(4) - BBox(1) )
534
eps3 = MAX( eps3, BBox(5) - BBox(2) )
535
eps3 = MAX( eps3, BBox(6) - BBox(3) )
536
eps3 = eps2 * eps3
537
538
BBox(1:3) = BBox(1:3) - eps3
539
BBox(4:6) = BBox(4:6) + eps3
540
541
ElementInQuadrant = .TRUE.
542
IF ( XMax < BBox(1) .OR. XMin > BBox(4) .OR. &
543
YMax < BBox(2) .OR. YMin > BBox(5) .OR. &
544
ZMax < BBox(3) .OR. ZMin > BBox(6) ) ElementInQuadrant = .FALSE.
545
546
!-------------------------------------------------------------------------------
547
548
IF ( ElementInQuadrant ) THEN
549
ChildQuadrant(i) % Quadrant % NElemsInQuadrant = &
550
ChildQuadrant(i) % Quadrant % NElemsInQuadrant + 1
551
552
ChildQuadrant(i) % Quadrant % MinElementSize = &
553
MIN(ElementSize, ChildQuadrant(i) % Quadrant % MinElementSize)
554
555
! We allocate and store also the midlevels temporarily
556
! (for the duration of the construction routine):
557
! ----------------------------------------------------
558
ElementList(i,ChildQuadrant(i) % Quadrant % NElemsInQuadrant) = &
559
MotherQuadrant % Elements(t)
560
END IF
561
!-------------------------------------------------------------------------------
562
END DO
563
!-------------------------------------------------------------------------------
564
END DO
565
566
!-------------------------------------------------------------------------------
567
568
DO i=1,2**dim
569
IF ( ChildQuadrant(i) % Quadrant % NElemsInQuadrant /= 0 ) THEN
570
ALLOCATE ( ChildQuadrant(i) % Quadrant % Elements ( &
571
ChildQuadrant(i) % Quadrant % NElemsInQuadrant ) )
572
573
ChildQuadrant(i) % Quadrant % Elements (1: &
574
ChildQuadrant(i) % Quadrant % NElemsInQuadrant) = &
575
ElementList(i,1:ChildQuadrant(i) % Quadrant % NElemsInQuadrant)
576
END IF
577
END DO
578
579
!-------------------------------------------------------------------------------
580
END SUBROUTINE PutElementsInChildQuadrants
581
!-------------------------------------------------------------------------------
582
END SUBROUTINE BuildQuadrantTree
583
!-------------------------------------------------------------------------------
584
585
!-------------------------------------------------------------------------------
586
!> Returns the local coordinate values from the given mesh
587
!> structure for the given set of nodal Indexes.
588
!-------------------------------------------------------------------------------
589
SUBROUTINE CopyElementNodesFromMesh(ElementNodes, Mesh, n, Indexes)
590
!-------------------------------------------------------------------------------
591
TYPE(Nodes_t) :: ElementNodes
592
TYPE(Mesh_t), POINTER :: Mesh
593
INTEGER :: n
594
INTEGER, POINTER :: Indexes(:)
595
!-------------------------------------------------------------------------------
596
INTEGER :: m
597
!-------------------------------------------------------------------------------
598
IF ( .NOT. ASSOCIATED( ElementNodes % x ) ) THEN
599
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n) )
600
ELSE
601
m = SIZE(ElementNodes % x)
602
IF ( m < n ) THEN
603
DEALLOCATE(ElementNodes % x, ElementNodes % y, ElementNodes % z)
604
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n) )
605
ELSE IF( m > n ) THEN
606
ElementNodes % x(n+1:m) = 0.0_dp
607
ElementNodes % y(n+1:m) = 0.0_dp
608
ElementNodes % z(n+1:m) = 0.0_dp
609
END IF
610
END IF
611
612
ElementNodes % x(1:n) = Mesh % Nodes % x(Indexes(1:n))
613
ElementNodes % y(1:n) = Mesh % Nodes % y(Indexes(1:n))
614
ElementNodes % z(1:n) = Mesh % Nodes % z(Indexes(1:n))
615
!-------------------------------------------------------------------------------
616
END SUBROUTINE CopyElementNodesFromMesh
617
!-------------------------------------------------------------------------------
618
619
!------------------------------------------------------------------------------
620
!> Create a matrix representation of the Nedelec interpolation operator which
621
!> operates on a vector field expressed in terms of the nodal basis functions
622
!> and gives the values of DOFs for obtaining its vector element (Nedelec)
623
!> interpolant. This subroutine assumes that DOFs are associated
624
!> with edges, so that the geometric domain of the finite element given as input
625
!> is supposed to be one-dimensional.
626
!------------------------------------------------------------------------------
627
SUBROUTINE NodalToNedelecPiMatrix(PiMat, Edge, Mesh, dim, SecondFamily)
628
!------------------------------------------------------------------------------
629
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,6) !< The interpolation operator as a matrix
630
TYPE(Element_t), POINTER, INTENT(IN) :: Edge !< The element for which the operator is created
631
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh !< The Edge should belong to the mesh given
632
INTEGER, INTENT(IN) :: dim !< The number of components of the vector field
633
LOGICAL, OPTIONAL, INTENT(IN) :: SecondFamily !< To select the Nedelec family
634
!------------------------------------------------------------------------------
635
TYPE(Nodes_t), SAVE :: Nodes
636
TYPE(GaussIntegrationPoints_t) :: IP
637
LOGICAL :: SecondKindBasis, stat
638
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
639
640
INTEGER :: EDOFs, i, k, p, i1, i2, j1, j2, n
641
REAL(KIND=dp) :: Basis(2), detJ, s, e(3), t(3), fun(3), u, v
642
!------------------------------------------------------------------------------
643
IF ((Edge % Type % ElementCode / 100) /= 2) THEN
644
CALL Warn('NodalToNedelecPiMatrix', 'A 1-dimensional element expected')
645
RETURN
646
END IF
647
648
IF (.NOT. ASSOCIATED(Mesh % Edges)) THEN
649
CALL Fatal('NodalToNedelecPiMatrix', 'Mesh edges are not associated!')
650
END IF
651
652
n = Edge % Type % NumberOfNodes
653
IF (n /= 2) CALL Fatal('NodalToNedelecPiMatrix', &
654
'A 2-node element expected ')
655
656
IF (PRESENT(SecondFamily)) THEN
657
SecondKindBasis = SecondFamily
658
ELSE
659
SecondKindBasis = .FALSE.
660
END IF
661
662
IF (SecondKindBasis) THEN
663
EDOFs = 2
664
ELSE
665
EDOFs = 1
666
END IF
667
668
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Edge % NodeIndexes)
669
670
t(1) = Nodes % x(2) - Nodes % x(1)
671
t(2) = Nodes % y(2) - Nodes % y(1)
672
t(3) = Nodes % z(2) - Nodes % z(1)
673
674
i1 = Edge % NodeIndexes(1)
675
i2 = Edge % NodeIndexes(2)
676
IF (ParEnv % PEs > 1) THEN
677
j1 = Mesh % ParallelInfo % GlobalDOFs(i1)
678
j2 = Mesh % ParallelInfo % GlobalDOFs(i2)
679
ELSE
680
j1 = i1
681
j2 = i2
682
END IF
683
684
IF (j2 < j1) t = -t
685
t = t/SQRT(SUM(t**2))
686
687
PiMat = 0.0_dp
688
IP = GaussPoints(Edge)
689
DO p=1,IP % n
690
stat = ElementInfo(Edge, Nodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis)
691
s = IP % s(p) * DetJ
692
693
DO k=1,dim
694
e(:) = 0.0_dp
695
e(k) = 1.0_dp
696
DO i=1,n
697
fun(:) = Basis(i) * e(:)
698
IF (SecondKindBasis) THEN
699
u = IP % u(p)
700
v = 0.5d0*(1.0d0-sqrt(3.0d0)*u)
701
PiMat(1,3*(i-1)+k) = PiMat(1,3*(i-1)+k) + s * SUM(fun*t)*v
702
v = 0.5d0*(1.0d0+sqrt(3.0d0)*u)
703
PiMat(2,3*(i-1)+k) = PiMat(2,3*(i-1)+k) + s * SUM(fun*t)*v
704
ELSE
705
PiMat(1,3*(i-1)+k) = PiMat(1,3*(i-1)+k) + s * SUM(fun*t)
706
END IF
707
END DO
708
END DO
709
END DO
710
!------------------------------------------------------------------------------
711
END SUBROUTINE NodalToNedelecPiMatrix
712
!------------------------------------------------------------------------------
713
714
!------------------------------------------------------------------------------
715
!> This subroutine is analogous to the subroutine NodalToNedelecPiMatrix, but
716
!> here the matrix representation of the interpolation operator is created for
717
!> the DOFs associated with the element faces.
718
!------------------------------------------------------------------------------
719
SUBROUTINE NodalToNedelecPiMatrix_Faces(PiMat, Face, Mesh, dim, BasisDegree)
720
!------------------------------------------------------------------------------
721
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,12) !< The interpolation operator as a matrix
722
TYPE(Element_t), POINTER, INTENT(IN) :: Face !< The element for which the operator is created
723
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh !< The Face should belong to the mesh given
724
INTEGER, INTENT(IN) :: dim !< The number of components of the vector field
725
INTEGER, OPTIONAL, INTENT(IN) :: BasisDegree !< The order of basis
726
!------------------------------------------------------------------------------
727
TYPE(Nodes_t), SAVE :: Nodes, EdgeNodes
728
TYPE(Element_t), POINTER, SAVE :: Edge => NULL()
729
TYPE(GaussIntegrationPoints_t) :: IP
730
LOGICAL :: Parallel, SecondOrder, stat
731
732
INTEGER :: FDOFs, istat, i, j, k, p, i1, i2, j1, j2, n
733
INTEGER :: FaceIndices(4), SquareFaceMap(4)
734
REAL(KIND=dp) :: t(3), WorkPiMat(2,12), D1, D2
735
REAL(KIND=dp) :: Basis(2), detJ, s, e(3), fun(3), wrkfun(3), u, v
736
737
CHARACTER(*), PARAMETER :: Caller = 'NodalToNedelecPiMatrix_Faces'
738
!------------------------------------------------------------------------------
739
IF (.NOT. (Face % Type % ElementCode / 100 /= 3 .OR. &
740
Face % Type % ElementCode / 100 /= 4)) THEN
741
CALL Fatal(Caller, 'A 2-dimensional element expected')
742
END IF
743
744
IF (Face % Type % ElementCode / 100 == 3) THEN
745
CALL Fatal(Caller, 'Cannot handle triangular faces yet')
746
END IF
747
748
IF (.NOT. ASSOCIATED(Mesh % Faces)) THEN
749
CALL Fatal(Caller, 'Mesh faces are not associated!')
750
END IF
751
752
IF ( PRESENT(BasisDegree) ) THEN
753
SecondOrder = BasisDegree > 1
754
IF (SecondOrder) CALL Fatal(Caller, 'Cannot handle higher-order basis yet')
755
ELSE
756
SecondOrder = .FALSE.
757
END IF
758
759
n = Face % Type % NumberOfNodes
760
FDOFs = 2
761
762
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Face % NodeIndexes)
763
764
IF (.NOT. ASSOCIATED(EdgeNodes % x)) THEN
765
ALLOCATE(EdgeNodes % x(2), EdgeNodes % y(2), EdgeNodes % z(2), stat = istat)
766
END IF
767
768
IF (.NOT. ASSOCIATED(Edge)) THEN
769
ALLOCATE(Edge, stat=istat)
770
Edge % Type => GetElementType(202, .FALSE.)
771
END IF
772
773
IP = GaussPoints(Edge, EdgeBasis = .TRUE.)
774
WorkPiMat(:,:) = 0.0_dp
775
776
! First create the projection matrix for the basis in the default order
777
!
778
DO j=1,FDOFs
779
!
780
! Create a virtual edge related to the definition of the face DOF
781
!
782
SELECT CASE(j)
783
CASE(1)
784
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(2))
785
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(2))
786
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(2))
787
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(4) + Nodes % x(1))
788
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(4) + Nodes % y(1))
789
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(4) + Nodes % z(1))
790
CASE(2)
791
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(4))
792
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(4))
793
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(4))
794
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(2) + Nodes % x(1))
795
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(2) + Nodes % y(1))
796
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(2) + Nodes % z(1))
797
END SELECT
798
799
t(1) = EdgeNodes % x(2) - EdgeNodes % x(1)
800
t(2) = EdgeNodes % y(2) - EdgeNodes % y(1)
801
t(3) = EdgeNodes % z(2) - EdgeNodes % z(1)
802
803
t = t/SQRT(SUM(t**2))
804
805
DO p=1,IP % n
806
stat = ElementInfo(Edge, EdgeNodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis)
807
s = IP % s(p) * DetJ
808
809
DO i=1,Face % Type % NumberOfNodes
810
SELECT CASE(i)
811
CASE(1,4)
812
wrkfun = 0.5_dp * Basis(1)
813
CASE(2,3)
814
wrkfun = 0.5_dp * Basis(2)
815
CASE DEFAULT
816
CALL Fatal(Caller, 'The lowest-order mesh supposed')
817
END SELECT
818
819
DO k=1,dim
820
e(:) = 0.0_dp
821
e(k) = 1.0_dp
822
fun(:) = wrkfun * e(:)
823
WorkPiMat(j,3*(i-1)+k) = WorkPiMat(j,3*(i-1)+k) + s * SUM(fun*t)
824
END DO
825
END DO
826
END DO
827
END DO
828
829
! Finally change the order/signs
830
!
831
SquareFaceMap(:) = (/ 1,2,3,4 /)
832
FaceIndices(1:n) = Face % NodeIndexes(SquareFaceMap(1:n))
833
834
Parallel = ParEnv % PEs > 1
835
IF (Parallel) FaceIndices(1:n) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(1:n))
836
837
CALL SquareFaceDofsOrdering(I1,I2,D1,D2,FaceIndices)
838
839
PiMat(1,:) = D1 * WorkPiMat(I1,:)
840
PiMat(2,:) = D2 * WorkPiMat(I2,:)
841
!------------------------------------------------------------------------------
842
END SUBROUTINE NodalToNedelecPiMatrix_Faces
843
!------------------------------------------------------------------------------
844
845
!------------------------------------------------------------------------------
846
!> This subroutine creates a global matrix representation of the Nedelec
847
!> interpolation operator which operates on a vector field expressed in terms of
848
!> the nodal basis functions and gives the values of DOFs for obtaining its vector
849
!> element (Nedelec) interpolant.
850
!------------------------------------------------------------------------------
851
SUBROUTINE NodalToNedelecInterpolation_GlobalMatrix(Mesh, NodalVar, &
852
VectorElementVar, GlobalPiMat, cdim, UseNodalPermArg )
853
!------------------------------------------------------------------------------
854
IMPLICIT NONE
855
TYPE(Mesh_t), POINTER :: Mesh
856
TYPE(Variable_t), POINTER, INTENT(IN) :: NodalVar
857
TYPE(Variable_t), POINTER, INTENT(IN) :: VectorElementVar
858
TYPE(Matrix_t), POINTER :: GlobalPiMat !< Used for the global representation
859
INTEGER, OPTIONAL :: cdim !< The number of spatial coordinates
860
LOGICAL, OPTIONAL :: UseNodalPermArg
861
!------------------------------------------------------------------------------
862
INTEGER, PARAMETER :: MaxEDOFs = 2
863
INTEGER, PARAMETER :: MaxFDOFs = 2
864
865
TYPE(Nodes_t), SAVE :: Nodes
866
TYPE(Element_t), POINTER :: Edge, Face
867
LOGICAL :: PiolaVersion, SecondKindBasis, SecondOrder
868
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
869
INTEGER :: dim, istat, EDOFs, i, j, k, i1, i2, k1, k2, nd, dofi, i0, k0, &
870
vdofs, edgej, facej
871
REAL(KIND=dp) :: PiMat(MaxEDOFs,6), FacePiMat(MaxFDOFs,12)
872
CHARACTER(*), PARAMETER :: Caller = 'NodalToNedelecInterpolation_GlobalMatrix'
873
LOGICAL :: UseNodalPerm, DoFatal
874
INTEGER, POINTER :: VectorPerm(:), NodalPerm(:)
875
!------------------------------------------------------------------------------
876
877
CALL Info(Caller,'Creating interpolation matrix between H1 and H(curl)!')
878
879
DoFatal = .FALSE.
880
IF (.NOT. ASSOCIATED(NodalVar)) THEN
881
CALL Warn(Caller, 'H1 variable is not associated!')
882
DoFatal = .TRUE.
883
END IF
884
IF (.NOT. ASSOCIATED(VectorElementVar)) THEN
885
CALL Warn(Caller, 'H(curl) variable is not associated!')
886
DoFatal = .TRUE.
887
END IF
888
IF(.NOT. ASSOCIATED(Mesh)) THEN
889
CALL Warn(Caller, 'Mesh structure is not associated!')
890
DoFatal = .TRUE.
891
END IF
892
IF (ASSOCIATED(GlobalPiMat)) THEN
893
CALL Warn(Caller, 'Matrix structure has already been created')
894
DoFatal = .TRUE.
895
END IF
896
IF(DoFatal) CALL Fatal(Caller,'Cannot continue with these errors!')
897
898
IF (.NOT. ASSOCIATED(Mesh % Edges)) CALL Fatal(Caller, 'Mesh edges not associated!')
899
900
901
IF (PRESENT(cdim)) THEN
902
dim = cdim
903
ELSE
904
dim = 3
905
END IF
906
vdofs = VectorElementVar % DOFs
907
IF(vdofs /=1 .AND. vdofs /= 2) THEN
908
CALL Fatal(Caller,'Vector dofs only makes sense for values 1 (real) and 2 (complex)!')
909
END IF
910
911
NodalPerm => NodalVar % Perm
912
UseNodalPerm = .TRUE.
913
IF ( PRESENT(UseNodalPermArg) ) UseNodalPerm = UseNodalPermArg
914
915
VectorPerm => VectorElementVar % Perm
916
917
IF (NodalVar % DOFs /= dim * vdofs) CALL Fatal(Caller, &
918
'Coordinate system dimension and DOF counts are not as expected')
919
920
CALL EdgeElementStyle(VectorElementVar % Solver % Values, PiolaVersion, SecondKindBasis, &
921
SecondOrder, Check = .TRUE.)
922
923
IF (SecondKindBasis) THEN
924
EDOFs = 2
925
ELSE
926
EDOFs = 1
927
END IF
928
929
GlobalPiMat => AllocateMatrix()
930
GlobalPiMat % Format = MATRIX_LIST
931
932
! Add the extreme entry since otherwise the ListMatrix operations may be very slow:
933
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, SIZE(VectorElementVar % Values), &
934
SIZE(NodalVar % Values), 0.0_dp)
935
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, 1, 1, 0.0_dp)
936
937
IF (.NOT. ALLOCATED(Ind)) THEN
938
ALLOCATE( Ind(Mesh % MaxElementDOFs), stat=istat )
939
END IF
940
941
! Here we need separate loops over edges, faces and elements so that all DOFs are handled
942
!
943
DO edgej=1, Mesh % NumberOfEdges
944
Edge => Mesh % Edges(edgej)
945
946
! Create the matrix representation of the Nedelec interpolation operator
947
CALL NodalToNedelecPiMatrix(PiMat, Edge, Mesh, dim, SecondKindBasis)
948
949
nd = mGetElementDOFs(Ind, Edge, VectorElementVar % Solver)
950
951
i1 = Edge % NodeIndexes(1)
952
i2 = Edge % NodeIndexes(2)
953
954
IF ( UseNodalPerm ) THEN
955
k1 = NodalPerm(i1)
956
k2 = NodalPerm(i2)
957
ELSE
958
k1 = i1
959
k2 = i2
960
END IF
961
962
DO dofi=1, vdofs
963
DO j=1,EDOFs
964
k = VectorPerm(Ind(j))
965
k0 = vdofs*(k-1)+dofi
966
DO i=1,dim
967
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k1-1)+vdofs*(i-1)+dofi, PiMat(j,i) )
968
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k2-1)+vdofs*(i-1)+dofi, PiMat(j,3+i) )
969
END DO
970
END DO
971
END DO
972
END DO
973
974
IF (ASSOCIATED(Mesh % Faces)) THEN
975
DO facej=1, Mesh % NumberOfFaces
976
Face => Mesh % Faces(facej)
977
IF (Face % BDOFs < 1) CYCLE
978
979
! TEMPORARY FIX FOR TRIANGULAR FACES
980
IF ( Face % Type % ElementCode /100 == 3 ) CYCLE
981
982
nd = mGetElementDOFs(Ind, Face, VectorElementVar % Solver)
983
984
! Count the offset for picking the true face DOFs
985
!
986
i0 = 0
987
DO k=1,Face % Type % NumberOfEdges
988
Edge => Mesh % Edges(Face % EdgeIndexes(k))
989
EDOFs = Edge % BDOFs
990
IF (EDOFs < 1) CYCLE
991
i0 = i0 + EDOFs
992
END DO
993
994
CALL NodalToNedelecPiMatrix_Faces(FacePiMat, Face, Mesh, dim, BasisDegree = 1)
995
996
DO dofi=1, vdofs
997
DO j=1,Face % BDOFs
998
k2 = VectorPerm(Ind(j+i0))
999
k0 = vdofs*(k2-1)+dofi
1000
DO i=1,Face % TYPE % NumberOfNodes
1001
k1 = Face % NodeIndexes(i)
1002
IF(UseNodalPerm) k1 = NodalPerm(k1)
1003
DO k=1,dim
1004
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k1-1)+vdofs*(k-1)+dofi, FacePiMat(j,3*(i-1)+k) )
1005
END DO
1006
END DO
1007
END DO
1008
END DO
1009
END DO
1010
END IF
1011
1012
! TO DO: Add loop over elements
1013
1014
! Finally, change to CRS matrix format which is much faster:
1015
CALL List_toCRSMatrix(GlobalPiMat)
1016
1017
CALL Info(Caller, 'Created Projection Matrix: H1 -> H(curl)', Level=6)
1018
!------------------------------------------------------------------------------
1019
END SUBROUTINE NodalToNedelecInterpolation_GlobalMatrix
1020
!------------------------------------------------------------------------------
1021
1022
1023
!------------------------------------------------------------------------------
1024
!> Create a matrix representation of the Nedelec interpolation operator which
1025
!> operates on a gradient field expressed in terms of the nodal basis functions
1026
!> and gives the values of DOFs for obtaining its vector element (Nedelec)
1027
!> interpolant. This subroutine assumes that DOFs are associated
1028
!> with edges, so that the geometric domain of the finite element given as input
1029
!> is supposed to be one-dimensional.
1030
!------------------------------------------------------------------------------
1031
SUBROUTINE NodalGradientToNedelecPiMatrix(PiMat, Edge, Mesh, SecondFamily)
1032
!------------------------------------------------------------------------------
1033
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,2) !< The interpolation operator as a matrix
1034
TYPE(Element_t), POINTER, INTENT(IN) :: Edge !< The element for which the operator is created
1035
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh !< The Edge should belong to the mesh given
1036
LOGICAL, OPTIONAL, INTENT(IN) :: SecondFamily !< To select the Nedelec family
1037
!------------------------------------------------------------------------------
1038
TYPE(Nodes_t), SAVE :: Nodes
1039
TYPE(GaussIntegrationPoints_t) :: IP
1040
LOGICAL :: SecondKindBasis, stat
1041
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
1042
1043
INTEGER :: EDOFs, i, k, p, i1, i2, j1, j2, n
1044
REAL(KIND=dp) :: dBasis(2,3), Basis(2), detJ, s, e(3), t(3), fun(3), u, v
1045
1046
!------------------------------------------------------------------------------
1047
IF ((Edge % Type % ElementCode / 100) /= 2) THEN
1048
CALL Warn('NodalGradientToNedelecPiMatrix', 'A 1-dimensional element expected')
1049
RETURN
1050
END IF
1051
1052
IF (.NOT. ASSOCIATED(Mesh % Edges)) THEN
1053
CALL Fatal('NodalGradientToNedelecPiMatrix', 'Mesh edges are not associated!')
1054
END IF
1055
1056
n = Edge % Type % NumberOfNodes
1057
IF (n /= 2) CALL Fatal('NodalGradientToNedelecPiMatrix', &
1058
'A 2-node element expected ')
1059
1060
IF (PRESENT(SecondFamily)) THEN
1061
SecondKindBasis = SecondFamily
1062
ELSE
1063
SecondKindBasis = .FALSE.
1064
END IF
1065
1066
IF (SecondKindBasis) THEN
1067
EDOFs = 2
1068
ELSE
1069
EDOFs = 1
1070
END IF
1071
1072
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Edge % NodeIndexes)
1073
1074
t(1) = Nodes % x(2) - Nodes % x(1)
1075
t(2) = Nodes % y(2) - Nodes % y(1)
1076
t(3) = Nodes % z(2) - Nodes % z(1)
1077
1078
i1 = Edge % NodeIndexes(1)
1079
i2 = Edge % NodeIndexes(2)
1080
IF (ParEnv % PEs > 1) THEN
1081
j1 = Mesh % ParallelInfo % GlobalDOFs(i1)
1082
j2 = Mesh % ParallelInfo % GlobalDOFs(i2)
1083
ELSE
1084
j1 = i1
1085
j2 = i2
1086
END IF
1087
1088
IF (j2 < j1) t = -t
1089
t = t/SQRT(SUM(t**2))
1090
1091
PiMat = 0.0_dp
1092
IP = GaussPoints(Edge)
1093
DO p=1,IP % n
1094
stat = ElementInfo(Edge, Nodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis, dBasis)
1095
s = IP % s(p) * DetJ
1096
1097
DO i=1,n
1098
fun(:) = dBasis(i,:)
1099
IF (SecondKindBasis) THEN
1100
u = IP % u(p)
1101
v = 0.5d0*(1.0d0-sqrt(3.0d0)*u)
1102
PiMat(1,i) = PiMat(1,i) + s * SUM(fun*t)*v
1103
v = 0.5d0*(1.0d0+sqrt(3.0d0)*u)
1104
PiMat(2,i) = PiMat(2,i) + s * SUM(fun*t)*v
1105
ELSE
1106
PiMat(1,i) = PiMat(1,i) + s * SUM(fun*t)
1107
END IF
1108
END DO
1109
END DO
1110
!------------------------------------------------------------------------------
1111
END SUBROUTINE NodalGradientToNedelecPiMatrix
1112
!------------------------------------------------------------------------------
1113
1114
!------------------------------------------------------------------------------
1115
!> This subroutine is analogous to the subroutine NodalGradientToNedelecPiMatrix,
1116
!> but here the matrix representation of the interpolation operator is created for
1117
!> the DOFs associated with the element faces.
1118
!------------------------------------------------------------------------------
1119
SUBROUTINE NodalGradientToNedelecPiMatrix_Faces(PiMat, Face, Mesh, BasisDegree)
1120
!------------------------------------------------------------------------------
1121
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,4) !< The interpolation operator as a matrix
1122
TYPE(Element_t), POINTER, INTENT(IN) :: Face !< The element for which the operator is created
1123
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh !< The Face should belong to the mesh given
1124
INTEGER, OPTIONAL, INTENT(IN) :: BasisDegree !< The order of basis
1125
!------------------------------------------------------------------------------
1126
TYPE(Nodes_t), SAVE :: Nodes, EdgeNodes
1127
TYPE(Element_t), POINTER, SAVE :: Edge => NULL()
1128
TYPE(GaussIntegrationPoints_t) :: IP
1129
LOGICAL :: Parallel, SecondOrder, stat
1130
1131
INTEGER :: FDOFs, istat, i, j, k, p, i1, i2, j1, j2, n
1132
INTEGER :: FaceIndices(4), SquareFaceMap(4)
1133
REAL(KIND=dp) :: t(3), WorkPiMat(2,4), D1, D2
1134
REAL(KIND=dp) :: Basis(4), detJ, s, fun(3), u, v, grad0(4,3)
1135
1136
CHARACTER(*), PARAMETER :: Caller = 'NodalGradientToNedelecPiMatrix_Faces'
1137
!------------------------------------------------------------------------------
1138
IF (.NOT. (Face % Type % ElementCode / 100 /= 3 .OR. &
1139
Face % Type % ElementCode / 100 /= 4)) THEN
1140
CALL Fatal(Caller, 'A 2-dimensional element expected')
1141
END IF
1142
1143
IF (Face % Type % ElementCode / 100 == 3) THEN
1144
CALL Fatal(Caller, 'Cannot handle triangular faces yet')
1145
END IF
1146
1147
IF (.NOT. ASSOCIATED(Mesh % Faces)) THEN
1148
CALL Fatal(Caller, 'Mesh faces are not associated!')
1149
END IF
1150
1151
IF ( PRESENT(BasisDegree) ) THEN
1152
SecondOrder = BasisDegree > 1
1153
IF (SecondOrder) CALL Fatal(Caller, 'Cannot handle higher-order basis yet')
1154
ELSE
1155
SecondOrder = .FALSE.
1156
END IF
1157
1158
n = Face % Type % NumberOfNodes
1159
FDOFs = 2
1160
1161
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Face % NodeIndexes)
1162
1163
IF (.NOT. ASSOCIATED(EdgeNodes % x)) THEN
1164
ALLOCATE(EdgeNodes % x(2), EdgeNodes % y(2), EdgeNodes % z(2), stat = istat)
1165
END IF
1166
1167
IF (.NOT. ASSOCIATED(Edge)) THEN
1168
ALLOCATE(Edge, stat=istat)
1169
Edge % Type => GetElementType(202, .FALSE.)
1170
END IF
1171
1172
IP = GaussPoints(Edge, EdgeBasis = .TRUE.)
1173
WorkPiMat(:,:) = 0.0_dp
1174
1175
! For the lowest-order case it sufficies to evaluate the gradient at
1176
! the mid-point of the face
1177
!
1178
stat = ElementInfo(Face, Nodes, 0.0_dp, 0.0_dp, 0.0_dp, DetJ, Basis, grad0)
1179
1180
! First create the projection matrix for the basis in the default order
1181
!
1182
DO j=1,FDOFs
1183
!
1184
! Create a virtual edge related to the definition of the face DOF
1185
!
1186
SELECT CASE(j)
1187
CASE(1)
1188
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(2))
1189
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(2))
1190
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(2))
1191
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(4) + Nodes % x(1))
1192
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(4) + Nodes % y(1))
1193
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(4) + Nodes % z(1))
1194
CASE(2)
1195
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(4))
1196
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(4))
1197
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(4))
1198
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(2) + Nodes % x(1))
1199
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(2) + Nodes % y(1))
1200
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(2) + Nodes % z(1))
1201
END SELECT
1202
1203
t(1) = EdgeNodes % x(2) - EdgeNodes % x(1)
1204
t(2) = EdgeNodes % y(2) - EdgeNodes % y(1)
1205
t(3) = EdgeNodes % z(2) - EdgeNodes % z(1)
1206
1207
t = t/SQRT(SUM(t**2))
1208
1209
DO p=1,IP % n
1210
stat = ElementInfo(Edge, EdgeNodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis)
1211
s = IP % s(p) * DetJ
1212
1213
DO i=1,Face % Type % NumberOfNodes
1214
fun(:) = grad0(i,:)
1215
WorkPiMat(j,i) = WorkPiMat(j,i) + s * SUM(fun*t)
1216
END DO
1217
END DO
1218
END DO
1219
1220
! Finally change the order/signs
1221
!
1222
SquareFaceMap(:) = (/ 1,2,3,4 /)
1223
FaceIndices(1:n) = Face % NodeIndexes(SquareFaceMap(1:n))
1224
1225
Parallel = ParEnv % PEs > 1
1226
IF (Parallel) FaceIndices(1:n) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(1:n))
1227
1228
CALL SquareFaceDofsOrdering(I1,I2,D1,D2,FaceIndices)
1229
1230
PiMat(1,:) = D1 * WorkPiMat(I1,:)
1231
PiMat(2,:) = D2 * WorkPiMat(I2,:)
1232
!------------------------------------------------------------------------------
1233
END SUBROUTINE NodalGradientToNedelecPiMatrix_Faces
1234
!------------------------------------------------------------------------------
1235
1236
!------------------------------------------------------------------------------
1237
SUBROUTINE NodalGradientToNedelecInterpolation_GlobalMatrix(Mesh, NodalVar, &
1238
VectorElementVar, GlobalPiMat, cdim, UseNodalPermArg )
1239
!------------------------------------------------------------------------------
1240
IMPLICIT NONE
1241
TYPE(Mesh_t), POINTER :: Mesh
1242
TYPE(Variable_t), POINTER, INTENT(IN) :: NodalVar
1243
TYPE(Variable_t), POINTER, INTENT(IN) :: VectorElementVar
1244
TYPE(Matrix_t), POINTER :: GlobalPiMat !< Used for the global representation
1245
INTEGER, OPTIONAL :: cdim
1246
LOGICAL, OPTIONAL :: UseNodalPermArg
1247
!------------------------------------------------------------------------------
1248
INTEGER, PARAMETER :: MaxEDOFs = 2
1249
INTEGER, PARAMETER :: MaxFDOFs = 2
1250
1251
TYPE(Element_t), POINTER :: Edge, Face
1252
LOGICAL :: PiolaVersion, SecondKindBasis, SecondOrder
1253
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
1254
INTEGER :: EDOFs, dof, i, istat, i0, j, k, l, m, nd, ndofs, p, q, vdofs, dim
1255
REAL(KIND=dp) :: PiMat(MaxEDOFs,2), FacePiMat(MaxFDOFs,4)
1256
CHARACTER(*), PARAMETER :: Caller = 'NodalGradientToNedelecInterpolation_GlobalMatrix'
1257
LOGICAL :: UseNodalPerm
1258
INTEGER, POINTER :: VectorPerm(:), NodalPerm(:)
1259
!------------------------------------------------------------------------------
1260
IF (.NOT. ASSOCIATED(NodalVar) .OR. .NOT. ASSOCIATED(VectorElementVar)) THEN
1261
CALL Fatal(Caller, 'H1 or H(curl) variable is not associated')
1262
END IF
1263
1264
IF (ASSOCIATED(Mesh)) THEN
1265
IF (.NOT. ASSOCIATED(Mesh % Edges)) CALL Fatal(Caller, 'Mesh edges not associated!')
1266
ELSE
1267
CALL Fatal(Caller, 'Mesh structure is not associated')
1268
END IF
1269
1270
IF (ASSOCIATED(GlobalPiMat)) THEN
1271
CALL Fatal(Caller, 'Matrix structure has already been created')
1272
END IF
1273
1274
IF (PRESENT(cdim)) THEN
1275
dim = cdim
1276
ELSE
1277
dim = 3
1278
END IF
1279
1280
vdofs = VectorElementVar % DOFs
1281
ndofs = NodalVar % DOFs
1282
IF (ndofs /= dim * vdofs .AND. ndofs /= vdofs) CALL Fatal(Caller, &
1283
'Coordinate system dimension and DOF counts are not as expected')
1284
1285
UseNodalPerm = .TRUE.
1286
NodalPerm => NodalVar % Perm
1287
IF(PRESENT(UseNodalPermArg)) UseNodalPerm = UseNodalPermArg
1288
1289
VectorPerm => VectorElementVar % Perm
1290
1291
CALL EdgeElementStyle(VectorElementVar % Solver % Values, PiolaVersion, SecondKindBasis, &
1292
SecondOrder, Check = .TRUE.)
1293
1294
IF (SecondKindBasis) THEN
1295
EDOFs = 2
1296
ELSE
1297
EDOFs = 1
1298
END IF
1299
1300
GlobalPiMat => AllocateMatrix()
1301
GlobalPiMat % Format = MATRIX_LIST
1302
1303
! Add the extreme entry since otherwise the ListMatrix operations may be very slow:
1304
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, SIZE(VectorElementVar % Values), &
1305
SIZE(NodalVar % Values), 0.0_dp)
1306
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, 1, 1, 0.0_dp)
1307
1308
IF (.NOT. ALLOCATED(Ind)) THEN
1309
ALLOCATE( Ind(Mesh % MaxElementDOFs), stat=istat )
1310
END IF
1311
1312
! Here we need separate loops over edges, faces and elements so that all DOFs are handled
1313
!
1314
DO j=1, Mesh % NumberOfEdges
1315
Edge => Mesh % Edges(j)
1316
1317
! Create the matrix representation of the Nedelec interpolation operator
1318
CALL NodalGradientToNedelecPiMatrix(PiMat, Edge, Mesh, SecondKindBasis)
1319
1320
nd = mGetElementDOFs(Ind, Edge, VectorElementVar % Solver)
1321
1322
DO dof=1,vDOFs
1323
DO i=1,Edge % Type % NumberOfNodes
1324
m = Edge % NodeIndexes(i)
1325
IF ( UseNodalPerm ) m = NodalPerm(m)
1326
l = ndofs*(m-1) + dof
1327
DO p=1,EDOFs
1328
q = VectorPerm(Ind(p))
1329
k = vdofs*(q-1)+dof
1330
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k, l, PiMat(p,i))
1331
END DO
1332
END DO
1333
END DO
1334
END DO
1335
1336
IF (ASSOCIATED(Mesh % Faces)) THEN
1337
DO j=1, Mesh % NumberOfFaces
1338
Face => Mesh % Faces(j)
1339
IF (Face % BDOFs < 1) CYCLE
1340
1341
! TEMPORARY FIX FOR TRIANGULAR FACES
1342
IF ( Face % Type % ElementCode /100 == 3 ) CYCLE
1343
1344
nd = mGetElementDOFs(Ind, Face, VectorElementVar % Solver)
1345
1346
! Count the offset for picking the true face DOFs
1347
!
1348
i0 = 0
1349
DO k=1,Face % Type % NumberOfEdges
1350
Edge => Mesh % Edges(Face % EdgeIndexes(k))
1351
EDOFs = Edge % BDOFs
1352
IF (EDOFs < 1) CYCLE
1353
i0 = i0 + EDOFs
1354
END DO
1355
1356
CALL NodalGradientToNedelecPiMatrix_Faces(FacePiMat, Face, Mesh, BasisDegree = 1)
1357
1358
DO dof=1,vdofs
1359
DO p=1,Face % BDOFs
1360
k = VectorPerm(Ind(p+i0))
1361
k = vdofs*(k-1)+dof
1362
DO i=1,Face % TYPE % NumberOfNodes
1363
m = Face % NodeIndexes(i)
1364
IF ( UseNodalPerm ) m = NodalPerm(m)
1365
l = ndofs*(m-1) + dof
1366
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k, l, FacePiMat(p,i))
1367
END DO
1368
END DO
1369
END DO
1370
END DO
1371
END IF
1372
1373
! TO DO: Add loop over elements
1374
1375
! Finally, change to CRS matrix format which is much faster:
1376
CALL List_toCRSMatrix(GlobalPiMat)
1377
1378
CALL Info(Caller, 'Created Gradient Matrix: grad(H1) -> H(curl)', Level=6)
1379
!------------------------------------------------------------------------------
1380
END SUBROUTINE NodalGradientToNedelecInterpolation_GlobalMatrix
1381
!------------------------------------------------------------------------------
1382
1383
!-------------------------------------------------------------------------------
1384
END MODULE Interpolation
1385
!-------------------------------------------------------------------------------
1386
1387
!> \} ElmerLib
1388
1389