Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/Interpolation.F90
5235 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 numerical 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 disturbing. 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, SkipFaces )
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
LOGICAL, OPTIONAL :: SkipFaces
862
!------------------------------------------------------------------------------
863
INTEGER, PARAMETER :: MaxEDOFs = 2
864
INTEGER, PARAMETER :: MaxFDOFs = 2
865
866
TYPE(Nodes_t), SAVE :: Nodes
867
TYPE(Element_t), POINTER :: Edge, Face
868
LOGICAL :: PiolaVersion, SecondKindBasis, SecondOrder, Found
869
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
870
INTEGER :: dim, istat, EDOFs, i, j, k, i1, i2, k1, k2, nd, dofi, i0, k0, &
871
vdofs, edgej, facej
872
REAL(KIND=dp) :: PiMat(MaxEDOFs,6), FacePiMat(MaxFDOFs,12)
873
CHARACTER(*), PARAMETER :: Caller = 'NodalToNedelecInterpolation_GlobalMatrix'
874
LOGICAL :: UseNodalPerm, DoFatal, SkipPeriodicSlave, DoFaces
875
INTEGER, POINTER :: VectorPerm(:), NodalPerm(:)
876
!------------------------------------------------------------------------------
877
878
CALL Info(Caller,'Creating interpolation matrix between H1 and H(curl)!')
879
880
DoFatal = .FALSE.
881
IF (.NOT. ASSOCIATED(NodalVar)) THEN
882
CALL Warn(Caller, 'H1 variable is not associated!')
883
DoFatal = .TRUE.
884
END IF
885
IF (.NOT. ASSOCIATED(VectorElementVar)) THEN
886
CALL Warn(Caller, 'H(curl) variable is not associated!')
887
DoFatal = .TRUE.
888
END IF
889
IF(.NOT. ASSOCIATED(Mesh)) THEN
890
CALL Warn(Caller, 'Mesh structure is not associated!')
891
DoFatal = .TRUE.
892
END IF
893
IF (ASSOCIATED(GlobalPiMat)) THEN
894
CALL Warn(Caller, 'Matrix structure has already been created')
895
DoFatal = .TRUE.
896
END IF
897
IF(DoFatal) CALL Fatal(Caller,'Cannot continue with these errors!')
898
899
IF (.NOT. ASSOCIATED(Mesh % Edges)) CALL Fatal(Caller, 'Mesh edges not associated!')
900
901
! We only want to apply the projetor to the master nodes/edges of the conforming system.
902
SkipPeriodicSlave = ASSOCIATED( Mesh % PeriodicPerm )
903
904
DoFaces = ASSOCIATED(Mesh % Faces)
905
IF(PRESENT(SkipFaces)) THEN
906
IF(SkipFaces) DoFaces = .FALSE.
907
END IF
908
909
IF (PRESENT(cdim)) THEN
910
dim = cdim
911
ELSE
912
dim = 3
913
END IF
914
vdofs = VectorElementVar % DOFs
915
IF(vdofs /=1 .AND. vdofs /= 2) THEN
916
CALL Fatal(Caller,'Vector dofs only makes sense for values 1 (real) and 2 (complex)!')
917
END IF
918
919
NodalPerm => NodalVar % Perm
920
UseNodalPerm = .TRUE.
921
IF ( PRESENT(UseNodalPermArg) ) UseNodalPerm = UseNodalPermArg
922
923
VectorPerm => VectorElementVar % Perm
924
925
IF (NodalVar % DOFs /= dim * vdofs) CALL Fatal(Caller, &
926
'Coordinate system dimension and DOF counts are not as expected')
927
928
CALL EdgeElementStyle(VectorElementVar % Solver % Values, PiolaVersion, SecondKindBasis, &
929
SecondOrder, Check = .TRUE.)
930
931
IF (SecondKindBasis) THEN
932
EDOFs = 2
933
ELSE
934
EDOFs = 1
935
END IF
936
937
GlobalPiMat => AllocateMatrix()
938
GlobalPiMat % Format = MATRIX_LIST
939
940
! Add the extreme entry since otherwise the ListMatrix operations may be very slow:
941
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, SIZE(VectorElementVar % Values), &
942
SIZE(NodalVar % Values), 0.0_dp)
943
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, 1, 1, 0.0_dp)
944
945
IF (.NOT. ALLOCATED(Ind)) THEN
946
ALLOCATE( Ind(Mesh % MaxElementDOFs), stat=istat )
947
END IF
948
949
950
! Here we need separate loops over edges, faces and elements so that all DOFs are handled
951
!
952
DO edgej=1, Mesh % NumberOfEdges
953
Edge => Mesh % Edges(edgej)
954
955
! Create the matrix representation of the Nedelec interpolation operator
956
CALL NodalToNedelecPiMatrix(PiMat, Edge, Mesh, dim, SecondKindBasis)
957
958
nd = mGetElementDOFs(Ind, Edge, VectorElementVar % Solver)
959
960
i1 = Edge % NodeIndexes(1)
961
i2 = Edge % NodeIndexes(2)
962
963
IF ( UseNodalPerm ) THEN
964
k1 = NodalPerm(i1)
965
k2 = NodalPerm(i2)
966
ELSE
967
k1 = i1
968
k2 = i2
969
END IF
970
971
DO dofi=1, vdofs
972
DO j=1,EDOFs
973
k = VectorPerm(Ind(j))
974
IF(k==0) CYCLE
975
976
IF(SkipPeriodicSlave) THEN
977
IF(Mesh % PeriodicPerm(Ind(j)) > 0) CYCLE
978
END IF
979
980
k0 = vdofs*(k-1)+dofi
981
DO i=1,dim
982
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k1-1)+vdofs*(i-1)+dofi, PiMat(j,i) )
983
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k2-1)+vdofs*(i-1)+dofi, PiMat(j,3+i) )
984
END DO
985
END DO
986
END DO
987
END DO
988
989
IF (DoFaces) THEN
990
DO facej=1, Mesh % NumberOfFaces
991
Face => Mesh % Faces(facej)
992
IF (Face % BDOFs < 1) CYCLE
993
994
! TEMPORARY FIX FOR TRIANGULAR FACES
995
IF ( Face % Type % ElementCode /100 == 3 ) CYCLE
996
997
nd = mGetElementDOFs(Ind, Face, VectorElementVar % Solver)
998
999
! Count the offset for picking the true face DOFs
1000
!
1001
i0 = 0
1002
DO k=1,Face % Type % NumberOfEdges
1003
Edge => Mesh % Edges(Face % EdgeIndexes(k))
1004
EDOFs = Edge % BDOFs
1005
IF (EDOFs < 1) CYCLE
1006
i0 = i0 + EDOFs
1007
END DO
1008
1009
CALL NodalToNedelecPiMatrix_Faces(FacePiMat, Face, Mesh, dim, BasisDegree = 1)
1010
1011
DO dofi=1, vdofs
1012
DO j=1,Face % BDOFs
1013
k2 = VectorPerm(Ind(j+i0))
1014
IF(k2==0) CYCLE
1015
1016
IF(SkipPeriodicSlave) THEN
1017
IF(Mesh % PeriodicPerm(Ind(j+i0)) > 0) CYCLE
1018
END IF
1019
1020
k0 = vdofs*(k2-1)+dofi
1021
DO i=1,Face % TYPE % NumberOfNodes
1022
k1 = Face % NodeIndexes(i)
1023
IF(UseNodalPerm) k1 = NodalPerm(k1)
1024
DO k=1,dim
1025
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k0, 3*vdofs*(k1-1)+vdofs*(k-1)+dofi, FacePiMat(j,3*(i-1)+k) )
1026
END DO
1027
END DO
1028
END DO
1029
END DO
1030
END DO
1031
END IF
1032
1033
! TO DO: Add loop over elements
1034
1035
! Finally, change to CRS matrix format which is much faster:
1036
CALL List_toCRSMatrix(GlobalPiMat)
1037
1038
CALL Info(Caller, 'Created Projection Matrix: H1 -> H(curl)', Level=6)
1039
!------------------------------------------------------------------------------
1040
END SUBROUTINE NodalToNedelecInterpolation_GlobalMatrix
1041
!------------------------------------------------------------------------------
1042
1043
1044
!------------------------------------------------------------------------------
1045
!> Create a matrix representation of the Nedelec interpolation operator which
1046
!> operates on a gradient field expressed in terms of the nodal basis functions
1047
!> and gives the values of DOFs for obtaining its vector element (Nedelec)
1048
!> interpolant. This subroutine assumes that DOFs are associated
1049
!> with edges, so that the geometric domain of the finite element given as input
1050
!> is supposed to be one-dimensional.
1051
!------------------------------------------------------------------------------
1052
SUBROUTINE NodalGradientToNedelecPiMatrix(PiMat, Edge, Mesh, SecondFamily)
1053
!------------------------------------------------------------------------------
1054
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,2) !< The interpolation operator as a matrix
1055
TYPE(Element_t), POINTER, INTENT(IN) :: Edge !< The element for which the operator is created
1056
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh !< The Edge should belong to the mesh given
1057
LOGICAL, OPTIONAL, INTENT(IN) :: SecondFamily !< To select the Nedelec family
1058
!------------------------------------------------------------------------------
1059
TYPE(Nodes_t), SAVE :: Nodes
1060
TYPE(GaussIntegrationPoints_t) :: IP
1061
LOGICAL :: SecondKindBasis, stat
1062
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
1063
1064
INTEGER :: EDOFs, i, k, p, i1, i2, j1, j2, n
1065
REAL(KIND=dp) :: dBasis(2,3), Basis(2), detJ, s, e(3), t(3), fun(3), u, v
1066
1067
!------------------------------------------------------------------------------
1068
IF ((Edge % Type % ElementCode / 100) /= 2) THEN
1069
CALL Warn('NodalGradientToNedelecPiMatrix', 'A 1-dimensional element expected')
1070
RETURN
1071
END IF
1072
1073
IF (.NOT. ASSOCIATED(Mesh % Edges)) THEN
1074
CALL Fatal('NodalGradientToNedelecPiMatrix', 'Mesh edges are not associated!')
1075
END IF
1076
1077
n = Edge % Type % NumberOfNodes
1078
IF (n /= 2) CALL Fatal('NodalGradientToNedelecPiMatrix', &
1079
'A 2-node element expected ')
1080
1081
IF (PRESENT(SecondFamily)) THEN
1082
SecondKindBasis = SecondFamily
1083
ELSE
1084
SecondKindBasis = .FALSE.
1085
END IF
1086
1087
IF (SecondKindBasis) THEN
1088
EDOFs = 2
1089
ELSE
1090
EDOFs = 1
1091
END IF
1092
1093
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Edge % NodeIndexes)
1094
1095
t(1) = Nodes % x(2) - Nodes % x(1)
1096
t(2) = Nodes % y(2) - Nodes % y(1)
1097
t(3) = Nodes % z(2) - Nodes % z(1)
1098
1099
i1 = Edge % NodeIndexes(1)
1100
i2 = Edge % NodeIndexes(2)
1101
IF (ParEnv % PEs > 1) THEN
1102
j1 = Mesh % ParallelInfo % GlobalDOFs(i1)
1103
j2 = Mesh % ParallelInfo % GlobalDOFs(i2)
1104
ELSE
1105
j1 = i1
1106
j2 = i2
1107
END IF
1108
1109
IF (j2 < j1) t = -t
1110
t = t/SQRT(SUM(t**2))
1111
1112
PiMat = 0.0_dp
1113
IP = GaussPoints(Edge)
1114
DO p=1,IP % n
1115
stat = ElementInfo(Edge, Nodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis, dBasis)
1116
s = IP % s(p) * DetJ
1117
1118
DO i=1,n
1119
fun(:) = dBasis(i,:)
1120
IF (SecondKindBasis) THEN
1121
u = IP % u(p)
1122
v = 0.5d0*(1.0d0-sqrt(3.0d0)*u)
1123
PiMat(1,i) = PiMat(1,i) + s * SUM(fun*t)*v
1124
v = 0.5d0*(1.0d0+sqrt(3.0d0)*u)
1125
PiMat(2,i) = PiMat(2,i) + s * SUM(fun*t)*v
1126
ELSE
1127
PiMat(1,i) = PiMat(1,i) + s * SUM(fun*t)
1128
END IF
1129
END DO
1130
END DO
1131
!------------------------------------------------------------------------------
1132
END SUBROUTINE NodalGradientToNedelecPiMatrix
1133
!------------------------------------------------------------------------------
1134
1135
!------------------------------------------------------------------------------
1136
!> This subroutine is analogous to the subroutine NodalGradientToNedelecPiMatrix,
1137
!> but here the matrix representation of the interpolation operator is created for
1138
!> the DOFs associated with the element faces.
1139
!------------------------------------------------------------------------------
1140
SUBROUTINE NodalGradientToNedelecPiMatrix_Faces(PiMat, Face, Mesh, BasisDegree)
1141
!------------------------------------------------------------------------------
1142
REAL(KIND=dp), INTENT(OUT) :: PiMat(2,4) !< The interpolation operator as a matrix
1143
TYPE(Element_t), POINTER, INTENT(IN) :: Face !< The element for which the operator is created
1144
TYPE(Mesh_t), POINTER, INTENT(IN) :: Mesh !< The Face should belong to the mesh given
1145
INTEGER, OPTIONAL, INTENT(IN) :: BasisDegree !< The order of basis
1146
!------------------------------------------------------------------------------
1147
TYPE(Nodes_t), SAVE :: Nodes, EdgeNodes
1148
TYPE(Element_t), POINTER, SAVE :: Edge => NULL()
1149
TYPE(GaussIntegrationPoints_t) :: IP
1150
LOGICAL :: Parallel, SecondOrder, stat
1151
1152
INTEGER :: FDOFs, istat, i, j, k, p, i1, i2, j1, j2, n
1153
INTEGER :: FaceIndices(4), SquareFaceMap(4)
1154
REAL(KIND=dp) :: t(3), WorkPiMat(2,4), D1, D2
1155
REAL(KIND=dp) :: Basis(4), detJ, s, fun(3), u, v, grad0(4,3)
1156
1157
CHARACTER(*), PARAMETER :: Caller = 'NodalGradientToNedelecPiMatrix_Faces'
1158
!------------------------------------------------------------------------------
1159
IF (.NOT. (Face % Type % ElementCode / 100 /= 3 .OR. &
1160
Face % Type % ElementCode / 100 /= 4)) THEN
1161
CALL Fatal(Caller, 'A 2-dimensional element expected')
1162
END IF
1163
1164
IF (Face % Type % ElementCode / 100 == 3) THEN
1165
CALL Fatal(Caller, 'Cannot handle triangular faces yet')
1166
END IF
1167
1168
IF (.NOT. ASSOCIATED(Mesh % Faces)) THEN
1169
CALL Fatal(Caller, 'Mesh faces are not associated!')
1170
END IF
1171
1172
IF ( PRESENT(BasisDegree) ) THEN
1173
SecondOrder = BasisDegree > 1
1174
IF (SecondOrder) CALL Fatal(Caller, 'Cannot handle higher-order basis yet')
1175
ELSE
1176
SecondOrder = .FALSE.
1177
END IF
1178
1179
n = Face % Type % NumberOfNodes
1180
FDOFs = 2
1181
1182
CALL CopyElementNodesFromMesh(Nodes, Mesh, n, Face % NodeIndexes)
1183
1184
IF (.NOT. ASSOCIATED(EdgeNodes % x)) THEN
1185
ALLOCATE(EdgeNodes % x(2), EdgeNodes % y(2), EdgeNodes % z(2), stat = istat)
1186
END IF
1187
1188
IF (.NOT. ASSOCIATED(Edge)) THEN
1189
ALLOCATE(Edge, stat=istat)
1190
Edge % Type => GetElementType(202, .FALSE.)
1191
END IF
1192
1193
IP = GaussPoints(Edge, EdgeBasis = .TRUE.)
1194
WorkPiMat(:,:) = 0.0_dp
1195
1196
! For the lowest-order case it sufficies to evaluate the gradient at
1197
! the mid-point of the face
1198
!
1199
stat = ElementInfo(Face, Nodes, 0.0_dp, 0.0_dp, 0.0_dp, DetJ, Basis, grad0)
1200
1201
! First create the projection matrix for the basis in the default order
1202
!
1203
DO j=1,FDOFs
1204
!
1205
! Create a virtual edge related to the definition of the face DOF
1206
!
1207
SELECT CASE(j)
1208
CASE(1)
1209
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(2))
1210
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(2))
1211
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(2))
1212
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(4) + Nodes % x(1))
1213
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(4) + Nodes % y(1))
1214
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(4) + Nodes % z(1))
1215
CASE(2)
1216
EdgeNodes % x(2) = 0.5_dp * (Nodes % x(3) + Nodes % x(4))
1217
EdgeNodes % y(2) = 0.5_dp * (Nodes % y(3) + Nodes % y(4))
1218
EdgeNodes % z(2) = 0.5_dp * (Nodes % z(3) + Nodes % z(4))
1219
EdgeNodes % x(1) = 0.5_dp * (Nodes % x(2) + Nodes % x(1))
1220
EdgeNodes % y(1) = 0.5_dp * (Nodes % y(2) + Nodes % y(1))
1221
EdgeNodes % z(1) = 0.5_dp * (Nodes % z(2) + Nodes % z(1))
1222
END SELECT
1223
1224
t(1) = EdgeNodes % x(2) - EdgeNodes % x(1)
1225
t(2) = EdgeNodes % y(2) - EdgeNodes % y(1)
1226
t(3) = EdgeNodes % z(2) - EdgeNodes % z(1)
1227
1228
t = t/SQRT(SUM(t**2))
1229
1230
DO p=1,IP % n
1231
stat = ElementInfo(Edge, EdgeNodes, IP % u(p), IP % v(p), IP % w(p), DetJ, Basis)
1232
s = IP % s(p) * DetJ
1233
1234
DO i=1,Face % Type % NumberOfNodes
1235
fun(:) = grad0(i,:)
1236
WorkPiMat(j,i) = WorkPiMat(j,i) + s * SUM(fun*t)
1237
END DO
1238
END DO
1239
END DO
1240
1241
! Finally change the order/signs
1242
!
1243
SquareFaceMap(:) = (/ 1,2,3,4 /)
1244
FaceIndices(1:n) = Face % NodeIndexes(SquareFaceMap(1:n))
1245
1246
Parallel = ParEnv % PEs > 1
1247
IF (Parallel) FaceIndices(1:n) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(1:n))
1248
1249
CALL SquareFaceDofsOrdering(I1,I2,D1,D2,FaceIndices)
1250
1251
PiMat(1,:) = D1 * WorkPiMat(I1,:)
1252
PiMat(2,:) = D2 * WorkPiMat(I2,:)
1253
!------------------------------------------------------------------------------
1254
END SUBROUTINE NodalGradientToNedelecPiMatrix_Faces
1255
!------------------------------------------------------------------------------
1256
1257
!------------------------------------------------------------------------------
1258
SUBROUTINE NodalGradientToNedelecInterpolation_GlobalMatrix(Mesh, NodalVar, &
1259
VectorElementVar, GlobalPiMat, cdim, UseNodalPermArg )
1260
!------------------------------------------------------------------------------
1261
IMPLICIT NONE
1262
TYPE(Mesh_t), POINTER :: Mesh
1263
TYPE(Variable_t), POINTER, INTENT(IN) :: NodalVar
1264
TYPE(Variable_t), POINTER, INTENT(IN) :: VectorElementVar
1265
TYPE(Matrix_t), POINTER :: GlobalPiMat !< Used for the global representation
1266
INTEGER, OPTIONAL :: cdim
1267
LOGICAL, OPTIONAL :: UseNodalPermArg
1268
!------------------------------------------------------------------------------
1269
INTEGER, PARAMETER :: MaxEDOFs = 2
1270
INTEGER, PARAMETER :: MaxFDOFs = 2
1271
1272
TYPE(Element_t), POINTER :: Edge, Face
1273
LOGICAL :: PiolaVersion, SecondKindBasis, SecondOrder
1274
INTEGER, ALLOCATABLE, SAVE :: Ind(:)
1275
INTEGER :: EDOFs, dof, i, istat, i0, j, k, l, m, nd, ndofs, p, q, vdofs, dim
1276
REAL(KIND=dp) :: PiMat(MaxEDOFs,2), FacePiMat(MaxFDOFs,4)
1277
CHARACTER(*), PARAMETER :: Caller = 'NodalGradientToNedelecInterpolation_GlobalMatrix'
1278
LOGICAL :: UseNodalPerm
1279
INTEGER, POINTER :: VectorPerm(:), NodalPerm(:)
1280
!------------------------------------------------------------------------------
1281
IF (.NOT. ASSOCIATED(NodalVar) .OR. .NOT. ASSOCIATED(VectorElementVar)) THEN
1282
CALL Fatal(Caller, 'H1 or H(curl) variable is not associated')
1283
END IF
1284
1285
IF (ASSOCIATED(Mesh)) THEN
1286
IF (.NOT. ASSOCIATED(Mesh % Edges)) CALL Fatal(Caller, 'Mesh edges not associated!')
1287
ELSE
1288
CALL Fatal(Caller, 'Mesh structure is not associated')
1289
END IF
1290
1291
IF (ASSOCIATED(GlobalPiMat)) THEN
1292
CALL Fatal(Caller, 'Matrix structure has already been created')
1293
END IF
1294
1295
IF (PRESENT(cdim)) THEN
1296
dim = cdim
1297
ELSE
1298
dim = 3
1299
END IF
1300
1301
vdofs = VectorElementVar % DOFs
1302
ndofs = NodalVar % DOFs
1303
IF (ndofs /= dim * vdofs .AND. ndofs /= vdofs) CALL Fatal(Caller, &
1304
'Coordinate system dimension and DOF counts are not as expected')
1305
1306
UseNodalPerm = .TRUE.
1307
NodalPerm => NodalVar % Perm
1308
IF(PRESENT(UseNodalPermArg)) UseNodalPerm = UseNodalPermArg
1309
1310
VectorPerm => VectorElementVar % Perm
1311
1312
CALL EdgeElementStyle(VectorElementVar % Solver % Values, PiolaVersion, SecondKindBasis, &
1313
SecondOrder, Check = .TRUE.)
1314
1315
IF (SecondKindBasis) THEN
1316
EDOFs = 2
1317
ELSE
1318
EDOFs = 1
1319
END IF
1320
1321
GlobalPiMat => AllocateMatrix()
1322
GlobalPiMat % Format = MATRIX_LIST
1323
1324
! Add the extreme entry since otherwise the ListMatrix operations may be very slow:
1325
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, SIZE(VectorElementVar % Values), &
1326
SIZE(NodalVar % Values), 0.0_dp)
1327
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, 1, 1, 0.0_dp)
1328
1329
IF (.NOT. ALLOCATED(Ind)) THEN
1330
ALLOCATE( Ind(Mesh % MaxElementDOFs), stat=istat )
1331
END IF
1332
1333
! Here we need separate loops over edges, faces and elements so that all DOFs are handled
1334
!
1335
DO j=1, Mesh % NumberOfEdges
1336
Edge => Mesh % Edges(j)
1337
1338
! Create the matrix representation of the Nedelec interpolation operator
1339
CALL NodalGradientToNedelecPiMatrix(PiMat, Edge, Mesh, SecondKindBasis)
1340
1341
nd = mGetElementDOFs(Ind, Edge, VectorElementVar % Solver)
1342
1343
DO dof=1,vDOFs
1344
DO i=1,Edge % Type % NumberOfNodes
1345
m = Edge % NodeIndexes(i)
1346
IF ( UseNodalPerm ) m = NodalPerm(m)
1347
l = ndofs*(m-1) + dof
1348
DO p=1,EDOFs
1349
q = VectorPerm(Ind(p))
1350
k = vdofs*(q-1)+dof
1351
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k, l, PiMat(p,i))
1352
END DO
1353
END DO
1354
END DO
1355
END DO
1356
1357
IF (ASSOCIATED(Mesh % Faces)) THEN
1358
DO j=1, Mesh % NumberOfFaces
1359
Face => Mesh % Faces(j)
1360
IF (Face % BDOFs < 1) CYCLE
1361
1362
! TEMPORARY FIX FOR TRIANGULAR FACES
1363
IF ( Face % Type % ElementCode /100 == 3 ) CYCLE
1364
1365
nd = mGetElementDOFs(Ind, Face, VectorElementVar % Solver)
1366
1367
! Count the offset for picking the true face DOFs
1368
!
1369
i0 = 0
1370
DO k=1,Face % Type % NumberOfEdges
1371
Edge => Mesh % Edges(Face % EdgeIndexes(k))
1372
EDOFs = Edge % BDOFs
1373
IF (EDOFs < 1) CYCLE
1374
i0 = i0 + EDOFs
1375
END DO
1376
1377
CALL NodalGradientToNedelecPiMatrix_Faces(FacePiMat, Face, Mesh, BasisDegree = 1)
1378
1379
DO dof=1,vdofs
1380
DO p=1,Face % BDOFs
1381
k = VectorPerm(Ind(p+i0))
1382
k = vdofs*(k-1)+dof
1383
DO i=1,Face % TYPE % NumberOfNodes
1384
m = Face % NodeIndexes(i)
1385
IF ( UseNodalPerm ) m = NodalPerm(m)
1386
l = ndofs*(m-1) + dof
1387
CALL List_AddToMatrixElement(GlobalPiMat % ListMatrix, k, l, FacePiMat(p,i))
1388
END DO
1389
END DO
1390
END DO
1391
END DO
1392
END IF
1393
1394
! TO DO: Add loop over elements
1395
1396
! Finally, change to CRS matrix format which is much faster:
1397
CALL List_toCRSMatrix(GlobalPiMat)
1398
1399
CALL Info(Caller, 'Created Gradient Matrix: grad(H1) -> H(curl)', Level=6)
1400
!------------------------------------------------------------------------------
1401
END SUBROUTINE NodalGradientToNedelecInterpolation_GlobalMatrix
1402
!------------------------------------------------------------------------------
1403
1404
!-------------------------------------------------------------------------------
1405
END MODULE Interpolation
1406
!-------------------------------------------------------------------------------
1407
1408
!> \} ElmerLib
1409
1410