Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/InterpolateMeshToMesh.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, Peter RÃ¥back, Joe Todd, Mika Malinen
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
! *
34
! ******************************************************************************/
35
36
37
!------------------------------------------------------------------------------
38
!> Map results from mesh to mesh. The from-mesh is stored in an octree from
39
!> which it is relatively fast to find the to-nodes. When the node is found
40
!> interpolation is performed. Optionally there may be an existing projector
41
!> that speeds up the interpolation.
42
!------------------------------------------------------------------------------
43
SUBROUTINE InterpolateMeshToMesh( OldMesh, NewMesh, OldVariables, &
44
NewVariables, UseQuadrantTree, Projector, MaskName, UnfoundNodes )
45
!------------------------------------------------------------------------------
46
USE Lists
47
USE SParIterComm
48
USE Interpolation
49
USE CoordinateSystems
50
USE MeshUtils, ONLY: ReleaseMesh
51
!-------------------------------------------------------------------------------
52
TYPE(Mesh_t), TARGET :: OldMesh, NewMesh
53
TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables, NewVariables
54
LOGICAL, OPTIONAL :: UseQuadrantTree
55
TYPE(Projector_t), POINTER, OPTIONAL :: Projector
56
CHARACTER(LEN=*),OPTIONAL :: MaskName
57
LOGICAL, POINTER, OPTIONAL :: UnfoundNodes(:)
58
!-------------------------------------------------------------------------------
59
INTEGER, ALLOCATABLE :: perm(:), vperm(:)
60
INTEGER, POINTER :: nperm(:)
61
LOGICAL, ALLOCATABLE :: FoundNodes(:), FoundNodesPar(:)
62
TYPE(Mesh_t), POINTER :: nMesh
63
TYPE(VAriable_t), POINTER :: Var, nVar
64
INTEGER :: i,j,k,l,nfound,maxrecv,n,ierr,nvars,npart,proc,status(MPI_STATUS_SIZE)
65
REAL(KIND=dp) :: myBB(6), eps2, dn
66
REAL(KIND=dp), POINTER :: store(:)
67
REAL(KIND=dp), ALLOCATABLE, TARGET :: astore(:),vstore(:,:), BB(:,:), &
68
nodes_x(:),nodes_y(:),nodes_z(:), xpart(:), ypart(:), zpart(:)
69
LOGICAL :: al, Stat
70
INTEGER :: PassiveCoordinate
71
72
TYPE ProcRecv_t
73
INTEGER :: n = 0
74
REAL(KIND=dp), ALLOCATABLE :: nodes_x(:),nodes_y(:),nodes_z(:)
75
END TYPE ProcRecv_t
76
TYPE(ProcRecv_t), ALLOCATABLE, TARGET :: ProcRecv(:)
77
78
TYPE ProcSend_t
79
INTEGER :: n = 0
80
INTEGER, ALLOCATABLE :: perm(:)
81
END TYPE ProcSend_t
82
TYPE(ProcSend_t), ALLOCATABLE :: ProcSend(:)
83
84
!-------------------------------------------------------------------------------
85
INTERFACE
86
SUBROUTINE InterpolateMeshToMeshQ( OldMesh, NewMesh, OldVariables, NewVariables, &
87
UseQuadrantTree, Projector, MaskName, FoundNodes, NewMaskPerm, KeepUnfoundNodes )
88
USE Types
89
TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables, NewVariables
90
TYPE(Mesh_t), TARGET :: OldMesh, NewMesh
91
LOGICAL, OPTIONAL :: UseQuadrantTree,FoundNodes(:)
92
CHARACTER(LEN=*),OPTIONAL :: MaskName
93
TYPE(Projector_t), POINTER, OPTIONAL :: Projector
94
INTEGER, OPTIONAL, POINTER :: NewMaskPerm(:) !< Mask the new variable set by the given MaskName when trying to define the interpolation.
95
LOGICAL, OPTIONAL :: KeepUnfoundNodes !< Do not disregard unfound nodes from projector
96
END SUBROUTINE InterpolateMeshToMeshQ
97
END INTERFACE
98
!-------------------------------------------------------------------------------
99
100
ALLOCATE( FoundNodes(NewMesh % NumberOfNodes) ); FoundNodes=.FALSE.
101
102
IF(PRESENT(UnfoundNodes)) THEN
103
IF(ASSOCIATED(UnfoundNodes)) DEALLOCATE(UnfoundNodes)
104
ALLOCATE(UnfoundNodes(NewMesh % NumberOfNodes))
105
END IF
106
107
! In serial interpolation is simple
108
!------------------------------------
109
IF ( ParEnv % PEs<=1 ) THEN
110
CALL InterpolateMeshToMeshQ( OldMesh, NewMesh, OldVariables, &
111
NewVariables, UseQuadrantTree, Projector, MaskName, FoundNodes )
112
113
IF( InfoActive(20) ) THEN
114
n = COUNT(.NOT. FoundNodes )
115
IF(n>0) CALL Info('InterpolateMeshToMesh','Number of unfound nodes in serial: '//I2S(n))
116
END IF
117
118
IF(PRESENT(UnfoundNodes)) UnfoundNodes = .NOT. FoundNodes
119
RETURN
120
END IF
121
122
! Passive coordinate is needed also here in order not to use that direction
123
! for the bounding box checks.
124
!---------------------------------------------------------------------------
125
PassiveCoordinate = ListGetInteger( CurrentModel % Simulation, &
126
'Interpolation Passive Coordinate', Stat )
127
IF (.NOT. Stat .AND. ASSOCIATED(CurrentModel % Solver)) THEN
128
PassiveCoordinate = ListGetInteger( CurrentModel % Solver % Values, &
129
'Interpolation Passive Coordinate', Stat )
130
END IF
131
132
! Interpolate within our own partition, flag the points we found:
133
! ---------------------------------------------------------------
134
CALL InterpolateMeshToMeshQ( OldMesh, NewMesh, OldVariables, &
135
NewVariables, UseQuadrantTree, MaskName=MaskName, FoundNodes=FoundNodes )
136
137
IF(PRESENT(UnfoundNodes)) UnfoundNodes = .NOT. FoundNodes
138
139
! special case "all found":
140
!--------------------------
141
n = COUNT(.NOT.FoundNodes); dn = n
142
143
IF( InfoActive(20) ) THEN
144
IF(n>0) CALL Info('InterpolateMeshToMesh','Number of unfound nodes in own partition: '//I2S(n))
145
END IF
146
147
AL = .FALSE.
148
IF (.NOT.ASSOCIATED(ParEnv % Active) ) THEN
149
ALLOCATE(Parenv % Active(PArEnv % PEs))
150
AL = .TRUE.
151
ParEnv % Active = .TRUE.
152
END IF
153
154
CALL SParActiveSUM(dn,2)
155
IF ( dn==0 ) RETURN
156
157
! No use to continue even in parallel, since the OldMeshes are all the same!
158
IF( OldMesh % SingleMesh ) THEN
159
CALL Warn('InterpolateMeshToMesh','Could not find all dofs in single mesh: '//I2S(NINT(dn)))
160
RETURN
161
END IF
162
163
! Exchange partition bounding boxes:
164
! This is needed to eliminate the amount of data to send among partitions.
165
! ------------------------------------------------------------------------
166
myBB = HUGE(mybb(1))
167
IF(OldMesh % NumberOfNodes /= 0) THEN
168
myBB(1) = MINVAL(OldMesh % Nodes % x)
169
myBB(2) = MINVAL(OldMesh % Nodes % y)
170
myBB(3) = MINVAL(OldMesh % Nodes % z)
171
myBB(4) = MAXVAL(OldMesh % Nodes % x)
172
myBB(5) = MAXVAL(OldMesh % Nodes % y)
173
myBB(6) = MAXVAL(OldMesh % Nodes % z)
174
175
eps2 = 0.1_dp * MAXVAL(myBB(4:6)-myBB(1:3))
176
myBB(1:3) = myBB(1:3) - eps2
177
myBB(4:6) = myBB(4:6) + eps2
178
END IF
179
180
ALLOCATE(BB(6,ParEnv % PEs))
181
CALL CheckBuffer(ParEnv % PEs*(6+MPI_BSEND_OVERHEAD))
182
DO i=1,ParEnv % PEs
183
IF ( Parenv % mype == i-1 .OR. .NOT. ParEnv % Active(i) ) CYCLE
184
proc = i-1
185
CALL MPI_BSEND( myBB, 6, MPI_DOUBLE_PRECISION, proc, &
186
999, ELMER_COMM_WORLD, ierr )
187
END DO
188
DO i=1,COUNT(ParEnv % Active)-1
189
CALL MPI_RECV( myBB, 6, MPI_DOUBLE_PRECISION, MPI_ANY_SOURCE, &
190
999, ELMER_COMM_WORLD, status, ierr )
191
proc = status(MPI_SOURCE)
192
BB(:,proc+1) = myBB
193
END DO
194
195
CALL CheckBuffer(Parenv % PEs*(n*(3 * 2 + 2)+MPI_BSEND_OVERHEAD)) !3 x double precision coord, 2 x count
196
197
IF ( n==0 ) THEN
198
! We have found all nodes, nothing to do except sent the info to others!
199
!----------------------------------------------------------------------
200
DEALLOCATE(BB)
201
DO i=1,ParEnv % PEs
202
IF ( Parenv % mype == i-1 .OR. .NOT. ParEnv % Active(i) ) CYCLE
203
proc = i-1
204
CALL MPI_BSEND( n, 1, MPI_INTEGER, proc, &
205
1001, ELMER_COMM_WORLD, ierr )
206
END DO
207
ELSE
208
! Extract nodes that we didn't find from our own partition...
209
! ------------------------------------------------------------
210
ALLOCATE( Perm(n), nodes_x(n), nodes_y(n),nodes_z(n) ); Perm=0
211
j = 0
212
DO i=1,NewMesh % NumberOfNodes
213
IF ( FoundNodes(i) ) CYCLE
214
j = j + 1
215
perm(j) = i
216
nodes_x(j) = NewMesh % Nodes % x(i)
217
nodes_y(j) = NewMesh % Nodes % y(i)
218
nodes_z(j) = NewMesh % Nodes % z(i)
219
END DO
220
221
! ...and ask those from others
222
! -------------------------------
223
ALLOCATE(ProcSend(ParEnv % PEs))
224
DO i=1,ParEnv % PEs
225
IF ( Parenv % mype == i-1 .OR. .NOT. ParEnv % Active(i) ) CYCLE
226
proc = i-1
227
228
! extract those of the missing nodes that are within the other
229
! partitions bounding box:
230
! ------------------------------------------------------------
231
myBB = BB(:,i)
232
npart = 0
233
DO j=1,n
234
IF ( ( nodes_x(j)<myBB(1) .OR. nodes_x(j)>myBB(4) ) .AND. PassiveCoordinate /= 1 ) CYCLE
235
IF ( ( nodes_y(j)<myBB(2) .OR. nodes_y(j)>myBB(5) ) .AND. PassiveCoordinate /= 2 ) CYCLE
236
IF ( ( nodes_z(j)<myBB(3) .OR. nodes_z(j)>myBB(6) ) .AND. PassiveCoordinate /= 3 ) CYCLE
237
npart = npart+1
238
END DO
239
ProcSend(proc+1) % n = npart
240
IF ( npart>0 ) THEN
241
ALLOCATE( xpart(npart),ypart(npart),zpart(npart),ProcSend(proc+1) % perm(npart) )
242
npart = 0
243
DO j=1,n
244
IF ( ( nodes_x(j)<myBB(1) .OR. nodes_x(j)>myBB(4) ) .AND. PassiveCoordinate /= 1 ) CYCLE
245
IF ( ( nodes_y(j)<myBB(2) .OR. nodes_y(j)>myBB(5) ) .AND. PassiveCoordinate /= 2 ) CYCLE
246
IF ( ( nodes_z(j)<myBB(3) .OR. nodes_z(j)>myBB(6) ) .AND. PassiveCoordinate /= 3 ) CYCLE
247
npart=npart+1
248
ProcSend(proc+1) % perm(npart)=j
249
xpart(npart) = Nodes_x(j)
250
ypart(npart) = Nodes_y(j)
251
zpart(npart) = Nodes_z(j)
252
END DO
253
END IF
254
255
! send count...
256
! -------------
257
CALL MPI_BSEND( npart, 1, MPI_INTEGER, proc, &
258
1001, ELMER_COMM_WORLD, ierr )
259
260
IF ( npart==0 ) CYCLE
261
262
! ...and points
263
! -------------
264
CALL MPI_BSEND( xpart, npart, MPI_DOUBLE_PRECISION, proc, &
265
1002, ELMER_COMM_WORLD, ierr )
266
CALL MPI_BSEND( ypart, npart, MPI_DOUBLE_PRECISION, proc, &
267
1003, ELMER_COMM_WORLD, ierr )
268
CALL MPI_BSEND( zpart, npart, MPI_DOUBLE_PRECISION, proc, &
269
1004, ELMER_COMM_WORLD, ierr )
270
271
DEALLOCATE(xpart,ypart,zpart)
272
END DO
273
DEALLOCATE(nodes_x,nodes_y,nodes_z,BB)
274
END IF
275
276
277
! receive points from others:
278
! ----------------------------
279
ALLOCATE(ProcRecv(Parenv % Pes))
280
DO i=1,COUNT(ParEnv % Active)-1
281
CALL MPI_RECV( n, 1, MPI_INTEGER, MPI_ANY_SOURCE, &
282
1001, ELMER_COMM_WORLD, status, ierr )
283
284
proc = status(MPI_SOURCE)
285
ProcRecv(proc+1) % n = n
286
287
IF ( n<=0 ) CYCLE
288
289
ALLOCATE(ProcRecv(proc+1) % Nodes_x(n), &
290
ProcRecv(proc+1) % Nodes_y(n),ProcRecv(proc+1) % Nodes_z(n))
291
292
CALL MPI_RECV( ProcRecv(proc+1) % nodes_x, n, MPI_DOUBLE_PRECISION, proc, &
293
1002, ELMER_COMM_WORLD, status, ierr )
294
CALL MPI_RECV( ProcRecv(proc+1) % nodes_y, n, MPI_DOUBLE_PRECISION, proc, &
295
1003, ELMER_COMM_WORLD, status, ierr )
296
CALL MPI_RECV( ProcRecv(proc+1) % nodes_z, n, MPI_DOUBLE_PRECISION, proc, &
297
1004, ELMER_COMM_WORLD, status, ierr )
298
END DO
299
300
! Count variables and received nodes, and check MPI buffer is
301
! sufficiently large:
302
! -----------------------------------------------------------
303
Var => OldVariables
304
nvars = 0
305
DO WHILE(ASSOCIATED(Var))
306
IF(LegitInterpVar(Var)) THEN
307
nvars = nvars + 1
308
IF ( ASSOCIATED(Var % PrevValues) ) THEN
309
j = SIZE(Var % PrevValues,2)
310
nvars = nvars+j
311
END IF
312
END IF
313
Var => Var % Next
314
END DO
315
316
maxrecv = 0
317
DO i=1,SIZE(ProcRecv)
318
maxrecv = MAX(maxrecv, ProcRecv(i) % n)
319
END DO
320
321
!For each node, we send a single integer perm and
322
!a real(dp) per variable. Also sending two counts
323
CALL CheckBuffer(SIZE(ProcRecv) * maxrecv * ((2 * nvars) + 1) + 2)
324
325
! Check the received points and extract values for the to-be-interpolated-
326
! variables, if we have the points within our domain:
327
! ------------------------------------------------------------------------
328
DO i=1,ParEnv % PEs
329
IF ( Parenv % mype == i-1 .OR. .NOT. ParEnv % Active(i) ) CYCLE
330
331
proc = i-1
332
n = ProcRecv(i) % n
333
334
IF ( n==0 ) THEN
335
CALL MPI_BSEND( n, 1, MPI_INTEGER, proc, 2001, ELMER_COMM_WORLD, ierr )
336
CYCLE
337
END IF
338
339
! Construct temporary mesh structure for the received points:
340
! -----------------------------------------------------------
341
Nmesh => AllocateMesh()
342
Nmesh % Nodes % x => ProcRecv(i) % nodes_x
343
Nmesh % Nodes % y => ProcRecv(i) % nodes_y
344
Nmesh % Nodes % z => ProcRecv(i) % nodes_z
345
Nmesh % NumberOfNodes = n
346
347
ALLOCATE(nperm(n))
348
DO j=1,n
349
nPerm(j)=j
350
END DO
351
352
Var => OldVariables
353
nvars = 0
354
DO WHILE(ASSOCIATED(Var))
355
IF(LegitInterpVar(Var)) THEN
356
ALLOCATE(store(n)); store=0
357
nvars = nvars+1
358
CALL VariableAdd(nMesh % Variables,nMesh,Var % Solver, &
359
Var % Name,1,store,nperm )
360
IF ( ASSOCIATED(Var % PrevValues) ) THEN
361
j = SIZE(Var % PrevValues,2)
362
nvars = nvars+j
363
Nvar => VariableGet( Nmesh % Variables,Var % Name,ThisOnly=.TRUE.)
364
ALLOCATE(Nvar % PrevValues(n,j))
365
Nvar % PrevValues = 0._dp
366
END IF
367
END IF
368
Var => Var % Next
369
END DO
370
371
! try interpolating values for the points:
372
! ----------------------------------------
373
ALLOCATE( FoundNodesPar(n) ); FoundNodesPar=.FALSE.
374
375
CALL InterpolateMeshToMeshQ( OldMesh, nMesh, OldVariables, &
376
nMesh % Variables, UseQuadrantTree, MaskName=MaskName, FoundNodes=FoundNodesPar )
377
378
nfound = COUNT(FoundNodesPar)
379
380
CALL MPI_BSEND( nfound, 1, MPI_INTEGER, proc, 2001, ELMER_COMM_WORLD, ierr )
381
382
! send interpolated values back to the owner:
383
! -------------------------------------------
384
IF ( nfound>0 ) THEN
385
ALLOCATE(vstore(nfound,nvars), vperm(nfound)); vstore=0
386
k = 0
387
DO j=1,n
388
IF ( .NOT.FoundNodesPar(j)) CYCLE
389
k = k + 1
390
vperm(k) = j
391
Var => OldVariables
392
nvars = 0
393
DO WHILE(ASSOCIATED(Var))
394
IF(LegitInterpVar(Var)) THEN
395
Nvar => VariableGet( Nmesh % Variables,Var % Name,ThisOnly=.TRUE.)
396
nvars=nvars+1
397
vstore(k,nvars)=Nvar % Values(j)
398
IF ( ASSOCIATED(Var % PrevValues) ) THEN
399
DO l=1,SIZE(Var % PrevValues,2)
400
nvars = nvars+1
401
vstore(k,nvars)=Nvar % PrevValues(j,l)
402
END DO
403
END IF
404
END IF
405
Var => Var % Next
406
END DO
407
END DO
408
409
CALL MPI_BSEND( vperm, nfound, MPI_INTEGER, proc, &
410
2002, ELMER_COMM_WORLD, ierr )
411
412
DO j=1,nvars
413
CALL MPI_BSEND( vstore(:,j), nfound, MPI_DOUBLE_PRECISION, proc, &
414
2002+j, ELMER_COMM_WORLD, ierr )
415
END DO
416
417
DEALLOCATE(vstore, vperm)
418
END IF
419
420
!Pointers to ProcRev, deallocated automatically below
421
NULLIFY(Nmesh % Nodes % x,&
422
Nmesh % Nodes % y,&
423
Nmesh % Nodes % z)
424
425
CALL ReleaseMesh(Nmesh)
426
DEALLOCATE(FoundNodesPar, Nmesh)
427
END DO
428
DEALLOCATE(ProcRecv)
429
430
! Receive interpolated values:
431
! ----------------------------
432
DO i=1,COUNT(ParEnv % Active)-1
433
434
! recv count:
435
! -----------
436
CALL MPI_RECV( n, 1, MPI_INTEGER, MPI_ANY_SOURCE, &
437
2001, ELMER_COMM_WORLD, status, ierr )
438
439
proc = status(MPI_SOURCE)
440
IF ( n<=0 ) THEN
441
IF ( ALLOCATED(ProcSend) ) THEN
442
IF ( ALLOCATED(ProcSend(proc+1) % Perm)) &
443
DEALLOCATE(ProcSend(proc+1) % Perm)
444
END IF
445
CYCLE
446
END IF
447
448
ALLOCATE(astore(n),vperm(n))
449
450
! recv permutation (where in the original array the
451
! points the partition found are):
452
! --------------------------------------------------
453
CALL MPI_RECV( vperm, n, MPI_INTEGER, proc, &
454
2002, ELMER_COMM_WORLD, status, ierr )
455
456
!Mark nodes as found
457
DO j=1,n
458
k=perm(ProcSend(proc+1) % Perm(vperm(j)))
459
FoundNodes(k) = .TRUE.
460
END DO
461
462
! recv values and store:
463
! ----------------------
464
Var => OldVariables
465
nvars=0
466
DO WHILE(ASSOCIATED(Var))
467
IF(LegitInterpVar(Var)) THEN
468
nvars=nvars+1
469
CALL MPI_RECV( astore, n, MPI_DOUBLE_PRECISION, proc, &
470
2002+nvars, ELMER_COMM_WORLD, status, ierr )
471
472
Nvar => VariableGet( NewMesh % Variables,Var % Name,ThisOnly=.TRUE.)
473
474
IF ( ASSOCIATED(Nvar) ) THEN
475
DO j=1,n
476
k=perm(ProcSend(proc+1) % Perm(vperm(j)))
477
IF(ASSOCIATED(nvar % Perm)) THEN
478
IF ( Nvar % perm(k)>0 ) &
479
Nvar % Values(Nvar % Perm(k)) = astore(j)
480
ELSE
481
Nvar % Values(k) = astore(j)
482
END IF
483
END DO
484
END IF
485
486
IF ( ASSOCIATED(Var % PrevValues) ) THEN
487
DO l=1,SIZE(Var % PrevValues,2)
488
nvars=nvars+1
489
CALL MPI_RECV( astore, n, MPI_DOUBLE_PRECISION, proc, &
490
2002+nvars, ELMER_COMM_WORLD, status, ierr )
491
492
IF ( ASSOCIATED(Nvar) ) THEN
493
DO j=1,n
494
k=perm(ProcSend(proc+1) % Perm(vperm(j)))
495
IF ( ASSOCIATED(Nvar % perm)) THEN
496
IF ( Nvar % perm(k)>0 ) &
497
Nvar % PrevValues(Nvar % Perm(k),l) = astore(j)
498
ELSE
499
Nvar % PrevValues(k,l) = astore(j)
500
END IF
501
END DO
502
END IF
503
END DO
504
END IF
505
END IF
506
Var => Var % Next
507
END DO
508
DEALLOCATE(astore,vperm,ProcSend(proc+1) % perm)
509
END DO
510
IF ( ALLOCATED(Perm) ) DEALLOCATE(Perm,ProcSend)
511
512
CALL MPI_BARRIER(ParEnv % ActiveComm,ierr)
513
514
IF(AL) THEN
515
DEALLOCATE(Parenv % Active)
516
ParEnv % Active => NULL()
517
END IF
518
519
n = COUNT(.NOT. FoundNodes )
520
IF(n>0) CALL Info('InterpolateMeshToMesh',&
521
'Number of unfound nodes in all partitions: '//I2S(n),Level=6)
522
523
IF(PRESENT(UnfoundNodes)) UnfoundNodes = .NOT. FoundNodes
524
DEALLOCATE( FoundNodes )
525
526
527
CONTAINS
528
529
! Collect here all the historical ways how a variable might be not good for interpolation.
530
!-----------------------------------------------------------------------------------------
531
FUNCTION LegitInterpVar(Var) RESULT ( IsLegit )
532
TYPE(Variable_t), POINTER :: Var
533
LOGICAL :: IsLegit
534
535
IF(.NOT.ASSOCIATED(Var)) THEN
536
IsLegit = .FALSE.
537
RETURN
538
END IF
539
540
! Only nodal and discontinuous galerkin fields can be interpolated as for now.
541
IsLegit = ( Var % TYPE == Variable_on_nodes_on_elements .OR. Var % Type == Variable_on_nodes )
542
! Even for vectors the interpolation is done for each scalar component.
543
IF( Var % Dofs > 1 ) IsLegit = .FALSE.
544
!IF( Var % Secondary ) IsLegit = .FALSE.
545
! Coordinates are special and should not be interpolated.
546
IF(LEN(Var % Name) >= 10) THEN
547
IF( Var % Name(1:10) == 'coordinate' ) IsLegit = .FALSE.
548
END IF
549
! This is global variable for which the type has not been properly set.
550
IF(.NOT. ASSOCIATED(Var % Perm) ) THEN
551
IF(ASSOCIATED(Var % Values) ) THEN
552
IF (SIZE(Var % Values)==1 ) IsLegit = .FALSE.
553
END IF
554
END IF
555
556
END FUNCTION LegitInterpVar
557
558
559
!------------------------------------------------------------------------------
560
FUNCTION AllocateMesh() RESULT(Mesh)
561
!------------------------------------------------------------------------------
562
TYPE(Mesh_t), POINTER :: Mesh
563
!------------------------------------------------------------------------------
564
INTEGER :: istat
565
566
ALLOCATE( Mesh, STAT=istat )
567
IF ( istat /= 0 ) &
568
CALL Fatal( 'AllocateMesh', 'Unable to allocate a few bytes of memory?' )
569
570
! Nothing computed on this mesh yet!
571
! ----------------------------------
572
Mesh % SavesDone = 0
573
Mesh % OutputActive = .FALSE.
574
575
Mesh % AdaptiveDepth = 0
576
Mesh % Changed = .FALSE. ! TODO: Change this sometime
577
578
Mesh % Stabilize = .FALSE.
579
580
Mesh % Variables => NULL()
581
Mesh % Parent => NULL()
582
Mesh % Child => NULL()
583
Mesh % Next => NULL()
584
Mesh % RootQuadrant => NULL()
585
Mesh % Elements => NULL()
586
Mesh % Edges => NULL()
587
Mesh % Faces => NULL()
588
Mesh % Projector => NULL()
589
Mesh % NumberOfEdges = 0
590
Mesh % NumberOfFaces = 0
591
Mesh % NumberOfNodes = 0
592
Mesh % NumberOfBulkElements = 0
593
Mesh % NumberOfBoundaryElements = 0
594
595
Mesh % MaxFaceDOFs = 0
596
Mesh % MaxEdgeDOFs = 0
597
Mesh % MaxBDOFs = 0
598
Mesh % MaxElementDOFs = 0
599
Mesh % MaxElementNodes = 0
600
601
Mesh % ViewFactors => NULL()
602
603
ALLOCATE( Mesh % Nodes, STAT=istat )
604
IF ( istat /= 0 ) &
605
CALL Fatal( 'AllocateMesh', 'Unable to allocate a few bytes of memory?' )
606
NULLIFY( Mesh % Nodes % x )
607
NULLIFY( Mesh % Nodes % y )
608
NULLIFY( Mesh % Nodes % z )
609
Mesh % Nodes % NumberOfNodes = 0
610
611
Mesh % ParallelInfo % NumberOfIfDOFs = 0
612
NULLIFY( Mesh % ParallelInfo % GlobalDOFs )
613
NULLIFY( Mesh % ParallelInfo % GInterface )
614
NULLIFY( Mesh % ParallelInfo % NeighbourList )
615
616
END FUNCTION AllocateMesh
617
!-------------------------------------------------------------------------------
618
END SUBROUTINE InterpolateMeshToMesh
619
!-------------------------------------------------------------------------------
620
621
622
!------------------------------------------------------------------------------
623
!> Interpolates values of all variables from a mesh associated with
624
!> the old model to the mesh of the new model.
625
!------------------------------------------------------------------------------
626
SUBROUTINE InterpolateMeshToMeshQ( OldMesh, NewMesh, OldVariables, NewVariables, &
627
UseQuadrantTree, Projector, MaskName, FoundNodes, NewMaskPerm, KeepUnfoundNodes )
628
!------------------------------------------------------------------------------
629
USE DefUtils
630
!-------------------------------------------------------------------------------
631
TYPE(Mesh_t), TARGET :: OldMesh !< Old mesh structure
632
TYPE(Mesh_t), TARGET :: NewMesh !< New mesh structure
633
TYPE(Variable_t), POINTER, OPTIONAL :: OldVariables !< Old model variable structure
634
TYPE(Variable_t), POINTER, OPTIONAL :: NewVariables !< New model variable structure. NewVariables defines the variables to be interpolated
635
LOGICAL, OPTIONAL :: UseQuadrantTree !< If true use the RootQuadrant of the old mesh in interpolation.
636
TYPE(Projector_t), POINTER, OPTIONAL :: Projector !< Use projector between meshes for interpolation, if available
637
CHARACTER(LEN=*),OPTIONAL :: MaskName !< Mask the old variable set by the given MaskName when trying to define the interpolation.
638
LOGICAL, OPTIONAL :: FoundNodes(:) !< List of nodes where the interpolation was a success
639
INTEGER, OPTIONAL, POINTER :: NewMaskPerm(:) !< Mask the new variable set by the given MaskName when trying to define the interpolation.
640
LOGICAL, OPTIONAL :: KeepUnfoundNodes !< Do not disregard unfound nodes from projector
641
!------------------------------------------------------------------------------
642
INTEGER :: dim
643
TYPE(Nodes_t) :: ElementNodes
644
INTEGER :: nBulk, i, j, k, l, n, np, bf_id, QTreeFails, TotFails, FoundCnt
645
REAL(KIND=dp), DIMENSION(3) :: Point
646
INTEGER, POINTER :: Indexes(:)
647
REAL(KIND=dp), DIMENSION(3) :: LocalCoordinates
648
TYPE(Variable_t), POINTER :: OldSol, NewSol, Var
649
REAL(KIND=dp), POINTER :: OldValue(:), NewValue(:), ElementValues(:)
650
TYPE(Quadrant_t), POINTER :: LeafQuadrant
651
TYPE(Element_t),POINTER :: Element, Parent
652
653
REAL(KIND=dp), ALLOCATABLE :: Basis(:),Vals(:),dVals(:,:), &
654
RotWBasis(:,:), WBasis(:,:)
655
REAL(KIND=dp) :: BoundingBox(6), detJ, u,v,w,s,val,rowsum, F(3,3), G(3,3)
656
657
LOGICAL :: UseQTree, TryQTree, Stat, UseProjector, EdgeBasis, PiolaT, Parallel, &
658
TryLinear, KeepUnfoundNodesL, InterpolatePartial
659
TYPE(Quadrant_t), POINTER :: RootQuadrant
660
661
INTEGER, POINTER CONTIG :: Rows(:), Cols(:)
662
INTEGER, POINTER :: Diag(:), OldPerm(:), NewPerm(:)
663
664
TYPE Epntr_t
665
TYPE(Element_t), POINTER :: Element
666
END TYPE Epntr_t
667
668
TYPE(Epntr_t), ALLOCATABLE :: ElemPtrs(:)
669
670
INTEGER, ALLOCATABLE, TARGET :: RInd(:), Unitperm(:)
671
LOGICAL :: Found, EpsAbsGiven,EpsRelGiven, MaskExists, CylProject, ProjectorAllocated
672
INTEGER :: eps_tries, nrow, PassiveCoordinate
673
REAL(KIND=dp) :: eps1 = 0.1, eps2, eps_global, eps_local, eps_basis,eps_numeric
674
REAL(KIND=dp), POINTER CONTIG :: Values(:)
675
REAL(KIND=dp), POINTER :: LocalU(:), LocalV(:), LocalW(:)
676
677
TYPE(Nodes_t), SAVE :: Nodes
678
INTEGER, ALLOCATABLE :: OneDGIndex(:)
679
680
!$OMP THREADPRIVATE(eps1,Nodes)
681
682
!------------------------------------------------------------------------------
683
684
Parallel = (ParEnv % PEs > 1)
685
686
FoundCnt = 0
687
IF ( OldMesh % NumberOfNodes == 0 ) RETURN
688
!
689
! If projector argument given, search for existing
690
! projector matrix, or generate new projector, if
691
! not already there:
692
! ------------------------------------------------
693
IF ( PRESENT(Projector) ) THEN
694
Projector => NewMesh % Projector
695
696
DO WHILE( ASSOCIATED( Projector ) )
697
IF ( ASSOCIATED(Projector % Mesh, OldMesh) ) THEN
698
CALL Info('InterpolateMesh2Mesh','Applying exiting projector in interpolation',Level=12)
699
IF ( PRESENT(OldVariables) ) CALL ApplyProjector()
700
RETURN
701
END IF
702
Projector => Projector % Next
703
END DO
704
705
n = NewMesh % NumberOfNodes
706
ALLOCATE( LocalU(n), LocalV(n), LocalW(n), ElemPtrs(n) )
707
DO i=1,n
708
NULLIFY( ElemPtrs(i) % Element )
709
END DO
710
END IF
711
!
712
! Check if using the spatial division hierarchy for the search:
713
! -------------------------------------------------------------
714
715
RootQuadrant => OldMesh % RootQuadrant
716
dim = CoordinateSystemDimension()
717
718
dim = MAX(dim,OldMesh % MeshDim)
719
720
IF ( .NOT. PRESENT( UseQuadrantTree ) ) THEN
721
UseQTree = .TRUE.
722
ELSE
723
UseQTree = UseQuadrantTree
724
ENDIF
725
726
IF ( UseQTree ) THEN
727
IF ( .NOT.ASSOCIATED( RootQuadrant ) ) THEN
728
BoundingBox(1) = MINVAL(OldMesh % Nodes % x)
729
BoundingBox(2) = MINVAL(OldMesh % Nodes % y)
730
BoundingBox(3) = MINVAL(OldMesh % Nodes % z)
731
BoundingBox(4) = MAXVAL(OldMesh % Nodes % x)
732
BoundingBox(5) = MAXVAL(OldMesh % Nodes % y)
733
BoundingBox(6) = MAXVAL(OldMesh % Nodes % z)
734
735
eps2 = 0.1_dp * MAXVAL(BoundingBox(4:6)-BoundingBox(1:3))
736
BoundingBox(1:3) = BoundingBox(1:3) - eps2
737
BoundingBox(4:6) = BoundingBox(4:6) + eps2
738
739
CALL Info('InterpolateMeshToMeshQ','Creating quadrant tree for faster interpolation!',Level=10)
740
CALL BuildQuadrantTree( OldMesh,BoundingBox,OldMesh % RootQuadrant)
741
RootQuadrant => OldMesh % RootQuadrant
742
END IF
743
END IF
744
745
! Use mask or not
746
!---------------------------------------
747
MaskExists = PRESENT( MaskName )
748
749
!------------------------------------------------------------------------------
750
751
n = OldMesh % MaxElementNodes
752
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), &
753
ElementNodes % z(n), ElementValues(n) )
754
755
eps_global = ListGetConstReal( CurrentModel % Simulation, &
756
'Interpolation Global Epsilon', Stat)
757
IF(.NOT. Stat) eps_global = 2.0d-10
758
759
eps_local = ListGetConstReal( CurrentModel % Simulation, &
760
'Interpolation Local Epsilon', Stat )
761
IF(.NOT. Stat) eps_local = 1.0d-10
762
763
eps_tries = ListGetInteger( CurrentModel % Simulation, &
764
'Interpolation Max Iterations', Stat )
765
IF(.NOT. Stat) eps_tries = 12
766
767
eps_numeric = ListGetConstReal( CurrentModel % Simulation, &
768
'Interpolation Numeric Epsilon', Stat)
769
IF(.NOT. Stat) eps_numeric = 1.0e-10
770
771
PassiveCoordinate = ListGetInteger( CurrentModel % Simulation, &
772
'Interpolation Passive Coordinate', Stat )
773
IF (.NOT. Stat .AND. ASSOCIATED(CurrentModel % Solver)) THEN
774
PassiveCoordinate = ListGetInteger( CurrentModel % Solver % Values, &
775
'Interpolation Passive Coordinate', Stat )
776
END IF
777
778
CylProject = ListGetLogical( CurrentModel % Simulation, &
779
'Interpolation Cylindric', Stat )
780
IF (.NOT. Stat .AND. ASSOCIATED(CurrentModel % Solver)) THEN
781
CylProject = ListGetLogical( CurrentModel % Solver % Values, &
782
'Interpolation Cylindric', Stat )
783
END IF
784
785
InterpolatePartial = ListGetLogical( CurrentModel % Simulation, &
786
'Interpolation Partial Hit', Stat )
787
788
789
QTreeFails = 0
790
TotFails = 0
791
792
EdgeBasis = .FALSE.
793
IF (ASSOCIATED(CurrentModel % Solver)) &
794
EdgeBasis = ListGetLogical(CurrentModel % Solver % Values,'Edge Basis',Found)
795
796
PiolaT = .FALSE.
797
IF (EdgeBasis) THEN
798
IF (.NOT. ASSOCIATED(CurrentModel % Solver % Mesh)) CALL Fatal('InterpolateMeshToMeshQ', &
799
'Edge basis functions need an associated mesh')
800
PiolaT = ListGetLogical(CurrentModel % Solver % Values,'Use Piola Transform',Found)
801
END IF
802
803
TryLinear = ListGetLogical( CurrentModel % Simulation, 'Try Linear Search If Qtree Fails', Found)
804
IF(.NOT.Found) TryLinear = .TRUE.
805
806
IF ( PRESENT(KeepUnfoundNodes) ) THEN
807
KeepUnfoundNodesL = KeepUnfoundNodes
808
ELSE
809
KeepUnfoundNodesL = .TRUE.
810
END IF
811
812
FoundCnt = 0
813
814
i = MAX(NewMesh % NumberOfNodes,OldMesh % NumberOfNodes)
815
ALLOCATE(Unitperm(i))
816
Unitperm = [(j,j=1,i)]
817
!------------------------------------------------------------------------------
818
! Loop over all nodes in the new mesh
819
!------------------------------------------------------------------------------
820
DO i=1,NewMesh % NumberOfNodes
821
!------------------------------------------------------------------------------
822
823
! Only get the variable for the requested nodes
824
IF( PRESENT( NewMaskPerm ) ) THEN
825
IF( NewMaskPerm(i) == 0 ) CYCLE
826
END IF
827
828
Point(1) = NewMesh % Nodes % x(i)
829
Point(2) = NewMesh % Nodes % y(i)
830
Point(3) = NewMesh % Nodes % z(i)
831
832
IF( PassiveCoordinate /= 0 ) THEN
833
Point(PassiveCoordinate) = 0.0_dp
834
END IF
835
836
IF( CylProject ) THEN
837
Point(1) = SQRT( Point(1)**2 + Point(2)**2 )
838
Point(2) = Point(3)
839
Point(3) = 0.0_dp
840
END IF
841
842
!------------------------------------------------------------------------------
843
! Find in which old mesh bulk element the point belongs to
844
!------------------------------------------------------------------------------
845
Found = .FALSE.
846
TryQTree = ASSOCIATED(RootQuadrant) .AND. UseQTree
847
848
IF( TryQTree ) THEN
849
!------------------------------------------------------------------------------
850
! Find the last existing quadrant that the point belongs to
851
!------------------------------------------------------------------------------
852
Element => NULL()
853
CALL FindLeafElements(Point, dim, RootQuadrant, LeafQuadrant)
854
855
IF ( ASSOCIATED(LeafQuadrant) ) THEN
856
! Go through the bulk elements in the last ChildQuadrant
857
! only. Try to find matching element with progressively
858
! sloppier tests. Allow at most 100 % of slack:
859
! -------------------------------------------------------
860
Eps1 = eps_global
861
Eps2 = eps_local
862
863
DO j=1,eps_tries
864
DO k=1, LeafQuadrant % NElemsInQuadrant
865
Element => OldMesh % Elements(LeafQuadrant % Elements(k))
866
867
IF( MaskExists ) THEN
868
bf_id = ListGetInteger( CurrentModel % Bodies(Element % BodyId) % Values, &
869
'Body Force', Found )
870
IF( .NOT. Found ) CYCLE
871
IF(.NOT. ListCheckPresent( &
872
CurrentModel % BodyForces(bf_id) % Values,MaskName) ) CYCLE
873
END IF
874
875
Indexes => Element % NodeIndexes
876
n = Element % TYPE % NumberOfNodes
877
878
ElementNodes % x(1:n) = OldMesh % Nodes % x(Indexes)
879
ElementNodes % y(1:n) = OldMesh % Nodes % y(Indexes)
880
ElementNodes % z(1:n) = OldMesh % Nodes % z(Indexes)
881
882
IF( PassiveCoordinate > 0 ) THEN
883
IF( PassiveCoordinate == 1 ) THEN
884
ElementNodes % x(1:n) = 0.0_dp
885
ELSE IF( PassiveCoordinate == 2 ) THEN
886
ElementNodes % y(1:n) = 0.0_dp
887
ELSE IF( PassiveCoordinate == 3 ) THEN
888
ElementNodes % z(1:n) = 0.0_dp
889
END IF
890
END IF
891
892
Found = PointInElement( Element, ElementNodes, &
893
Point, LocalCoordinates, Eps1, Eps2, NumericEps=eps_numeric,EdgeBasis=PiolaT)
894
IF ( Found ) EXIT
895
END DO
896
IF ( Found ) EXIT
897
898
Eps1 = 10 * Eps1
899
Eps2 = 10 * Eps2
900
IF( Eps1 > 1.0_dp ) EXIT
901
END DO
902
END IF
903
END IF
904
905
IF( .NOT. TryQTree .OR. (.NOT. Found .AND. .NOT. Parallel .AND. TryLinear ) ) THEN
906
!------------------------------------------------------------------------------
907
! Go through all old mesh bulk elements
908
!------------------------------------------------------------------------------
909
DO k=1,OldMesh % NumberOfBulkElements
910
Element => OldMesh % Elements(k)
911
912
n = Element % TYPE % NumberOfNodes
913
Indexes => Element % NodeIndexes
914
915
ElementNodes % x(1:n) = OldMesh % Nodes % x(Indexes)
916
ElementNodes % y(1:n) = OldMesh % Nodes % y(Indexes)
917
ElementNodes % z(1:n) = OldMesh % Nodes % z(Indexes)
918
919
IF( PassiveCoordinate > 0 ) THEN
920
IF( PassiveCoordinate == 1 ) THEN
921
ElementNodes % x(1:n) = 0.0_dp
922
ELSE IF( PassiveCoordinate == 2 ) THEN
923
ElementNodes % y(1:n) = 0.0_dp
924
ELSE IF( PassiveCoordinate == 3 ) THEN
925
ElementNodes % z(1:n) = 0.0_dp
926
END IF
927
END IF
928
929
Found = PointInElement( Element, ElementNodes, &
930
Point, LocalCoordinates )
931
IF( Found ) THEN
932
IF( TryQTree ) QTreeFails = QtreeFails + 1
933
EXIT
934
END IF
935
END DO
936
END IF
937
938
IF (.NOT.Found) THEN
939
Element => NULL()
940
IF (.NOT. Parallel ) THEN
941
WRITE( Message,'(A,I0,A,3ES10.2,A)' ) 'Point ',i,' at ',Point,' not found!'
942
CALL Info( 'InterpolateMeshToMesh', Message, Level=30 )
943
TotFails = TotFails + 1
944
END IF
945
CYCLE
946
END IF
947
IF ( PRESENT(FoundNodes) ) FoundNodes(i) = .TRUE.
948
949
!------------------------------------------------------------------------------
950
!
951
! Found Element in OldModel:
952
! ---------------------------------
953
954
IF ( PRESENT(Projector) ) THEN
955
FoundCnt = FoundCnt + 1
956
IF ( KeepUnfoundNodesL ) THEN
957
ElemPtrs(i) % Element => Element
958
LocalU(i) = LocalCoordinates(1)
959
LocalV(i) = LocalCoordinates(2)
960
LocalW(i) = LocalCoordinates(3)
961
ELSE
962
ElemPtrs(FoundCnt) % Element => Element
963
LocalU(FoundCnt) = LocalCoordinates(1)
964
LocalV(FoundCnt) = LocalCoordinates(2)
965
LocalW(FoundCnt) = LocalCoordinates(3)
966
END IF
967
END IF
968
969
IF ( .NOT.PRESENT(OldVariables) .OR. PRESENT(Projector) ) CYCLE
970
!------------------------------------------------------------------------------
971
!
972
! Go through all variables to be interpolated:
973
! --------------------------------------------
974
975
976
Var => OldVariables
977
DO WHILE( ASSOCIATED( Var ) )
978
979
IF(LegitInterpVar(Var)) THEN
980
981
!------------------------------------------------------------------------------
982
!
983
! Interpolate variable at Point in Element:
984
! ------------------------------------------------
985
986
NewSol => VariableGet( NewMesh % Variables, Var % Name, .TRUE. )
987
IF ( .NOT. ASSOCIATED( NewSol ) ) THEN
988
Var => Var % Next
989
CYCLE
990
END IF
991
992
IF(PRESENT(OldVariables)) THEN
993
OldSol => VariableGet( OldVariables, Var % Name, .TRUE. )
994
ELSE
995
OldSol => VariableGet( OldMesh % Variables, Var % Name, .TRUE. )
996
END IF
997
IF( .NOT. ASSOCIATED( OldSol ) ) THEN
998
CALL Fatal('InterpolateMeshToMesh','Variable not associated: '//TRIM(Var % Name))
999
END IF
1000
1001
1002
! Check that the node was found in the old mesh:
1003
! ----------------------------------------------
1004
IF ( ASSOCIATED (Element) ) THEN
1005
!------------------------------------------------------------------------------
1006
!
1007
! Check for rounding errors:
1008
! --------------------------
1009
IF( OldSol % TYPE == Variable_on_nodes_on_elements ) THEN
1010
Indexes => Element % DGIndexes
1011
ELSE
1012
Indexes => Element % NodeIndexes
1013
END IF
1014
1015
OldPerm => OldSol % Perm
1016
IF (.NOT.ASSOCIATED(OldPerm)) OldPerm => Unitperm
1017
1018
NewPerm => NewSol % Perm
1019
IF (.NOT.ASSOCIATED(NewPerm)) NewPerm => Unitperm
1020
1021
k = COUNT( OldPerm(Indexes) > 0 )
1022
1023
IF ( k == SIZE(Indexes) .OR. (InterpolatePartial .AND. k>0) ) THEN
1024
IF( NewSol % TYPE == Variable_on_nodes_on_elements ) THEN
1025
IF(.NOT. ALLOCATED(OneDGIndex) ) THEN
1026
CALL CreateOneDGIndex()
1027
END IF
1028
IF( OneDGIndex(i) > 0 ) THEN
1029
k = NewPerm( OneDGIndex(i) )
1030
ELSE
1031
k = 0
1032
END IF
1033
ELSE
1034
k = NewPerm(i)
1035
END IF
1036
1037
IF ( k /= 0 ) THEN
1038
WHERE( OldPerm(Indexes(1:n)) > 0 )
1039
ElementValues(1:n) = OldSol % Values(OldPerm(Indexes))
1040
ELSE WHERE
1041
ElementValues(1:n) = 0.0_dp
1042
END WHERE
1043
1044
val = InterpolateInElement( Element, ElementValues, &
1045
LocalCoordinates(1), LocalCoordinates(2), LocalCoordinates(3) )
1046
1047
NewSol % Values(k) = val
1048
1049
IF ( ASSOCIATED( OldSol % PrevValues ) ) THEN
1050
DO j=1,SIZE(OldSol % PrevValues,2)
1051
1052
WHERE( OldPerm(Indexes(1:n)) > 0 )
1053
ElementValues(1:n) = OldSol % PrevValues(OldPerm(Indexes),j)
1054
END WHERE
1055
1056
val = InterpolateInElement( Element, ElementValues, &
1057
LocalCoordinates(1), LocalCoordinates(2), LocalCoordinates(3) )
1058
1059
NewSol % PrevValues(k,j) = val
1060
END DO
1061
END IF
1062
END IF
1063
END IF
1064
ELSE
1065
IF ( NewPerm(i)/=0 ) NewValue(NewPerm(i))=0.0_dp
1066
END IF
1067
1068
!------------------------------------------------------------------------------
1069
END IF
1070
Var => Var % Next
1071
END DO
1072
!------------------------------------------------------------------------------
1073
END DO
1074
1075
IF( .NOT. Parallel ) THEN
1076
IF( QtreeFails > 0 ) THEN
1077
WRITE( Message,'(A,I0)' ) 'Number of points not found in quadtree: ',QtreeFails
1078
CALL Info( 'InterpolateMeshToMesh', Message )
1079
IF( TotFails == 0 ) THEN
1080
CALL Info( 'InterpolateMeshToMesh','All nodes still found by N^2 dummy search!' )
1081
END IF
1082
END IF
1083
IF( TotFails == 0 ) THEN
1084
CALL Info('InterpolateMeshToMesh','Found all nodes in the target mesh',Level=6)
1085
ELSE
1086
WRITE( Message,'(A,I0,A,I0,A)') 'Points not found: ',TotFails,' (found ',&
1087
NewMesh % NumberOfNodes - TotFails,')'
1088
CALL Warn( 'InterpolateMeshToMesh', Message )
1089
IF( ListGetLogical( CurrentModel % Simulation,'Interpolation Abort Not Found',Stat) ) THEN
1090
CALL Fatal('InterpolateMeshToMesh','Can not continue with incomplete interpolation!')
1091
END IF
1092
END IF
1093
END IF
1094
1095
!------------------------------------------------------------------------------
1096
!
1097
! Construct mesh projector, if requested. Next time around
1098
! will use the existing projector to interpolate values:
1099
! ---------------------------------------------------------
1100
IF ( PRESENT(Projector) ) THEN
1101
1102
IF ( KeepUnfoundNodesL ) THEN
1103
n = NewMesh % NumberOfNodes
1104
ELSE
1105
n = FoundCnt
1106
END IF
1107
ALLOCATE( Basis(100),Vals(100), Indexes(100))
1108
1109
! The critical value of basis function that is accepted to the
1110
! projector. Note that the sum of weights is one, so this
1111
! we know the scale for this one.
1112
eps_basis = ListGetConstReal( CurrentModel % Simulation, &
1113
'Interpolation Basis Epsilon', Stat )
1114
IF(.NOT. Stat) eps_basis = 0.0d-12
1115
1116
1117
ALLOCATE( Rows(n+1) )
1118
Rows(1) = 1
1119
ProjectorAllocated = .FALSE.
1120
1121
100 nrow = 1
1122
1123
DO i=1,n
1124
1125
IF(EdgeBasis.AND.ASSOCIATED(OldMesh % Parent)) THEN
1126
Element => OldMesh % Parent % Faces(ElemPtrs(i) % Element % ElementIndex)
1127
IF(ASSOCIATED(Element % BoundaryInfo)) THEN
1128
Parent => Element % BoundaryInfo% Left
1129
IF (ASSOCIATED(Parent)) THEN
1130
k = Element % TYPE % NumberOfNodes
1131
np = Parent % TYPE % NumberOfNodes
1132
END IF
1133
END IF
1134
ELSE
1135
Element => ElemPtrs(i) % Element
1136
END IF
1137
Found = ASSOCIATED( Element )
1138
1139
IF( .NOT. Found ) THEN
1140
! It seems unnecessary to make a matrix entry in case no target element is found!
1141
IF(.FALSE.) THEN
1142
IF( ProjectorAllocated ) THEN
1143
Cols(nrow) = 1
1144
Values(nrow) = 0._dp
1145
END IF
1146
nrow = nrow + 1
1147
END IF
1148
ELSE
1149
1150
u = LocalU(i)
1151
v = LocalV(i)
1152
w = LocalW(i)
1153
1154
IF (EdgeBasis) THEN
1155
CALL GetElementNodes(Nodes,Element)
1156
ELSE
1157
CALL GetElementNodes(Nodes,Element,UMesh=OldMesh)
1158
END IF
1159
1160
np = GetElementNOFNodes(Element)
1161
IF (EdgeBasis) THEN
1162
k = GetElementDOFs( Indexes, Element, NotDG=.TRUE.)
1163
ELSE
1164
!
1165
! In this case calling GetElementDOFs appears to generate warnings
1166
! (since CurrentModel % Solver % Mesh may not be associated for some reason),
1167
! now we proceed silently assuming the Lagrange interpolation
1168
!
1169
k = np
1170
Indexes(1:k) = Element % NodeIndexes(1:k)
1171
END IF
1172
1173
IF (ANY(Indexes(1:np)>Element % NodeIndexes)) np=0
1174
1175
IF( EdgeBasis) THEN
1176
IF(.NOT.ALLOCATED(dVals)) THEN
1177
ALLOCATE(dVals(k,3),WBasis(k,3),RotWBasis(k,3))
1178
ELSE IF(SIZE(dVals,1)<k) THEN
1179
DEALLOCATE(dVals,WBasis,RotWBasis)
1180
ALLOCATE(dVals(k,3),WBasis(k,3),RotWBasis(k,3))
1181
END IF
1182
1183
IF(PiolaT) THEN
1184
stat = ElementInfo(Element,Nodes,u,v,w,detJ,Vals,EdgeBasis=WBasis )
1185
ELSE
1186
stat = ElementInfo(Element,Nodes,u,v,w,detJ,Vals,dVals)
1187
CALL GetEdgeBasis(Element,WBasis,RotWBasis,Vals,dVals)
1188
END IF
1189
ELSE
1190
stat = ElementInfo(Element,Nodes,u,v,w,detJ,Vals)
1191
END IF
1192
1193
1194
rowsum = 0.0_dp
1195
DO j=1,k
1196
! IF( ABS( vals(j) ) < eps_basis ) CYCLE
1197
IF(j<=np) rowsum = rowsum + vals(j)
1198
IF (.NOT. ProjectorAllocated) THEN
1199
IF (.NOT.EdgeBasis.OR.(EdgeBasis.AND.j<=np)) THEN
1200
nrow = nrow+1
1201
ELSE
1202
nrow = nrow+3
1203
END IF
1204
END IF
1205
END DO
1206
1207
1208
IF( ProjectorAllocated ) THEN
1209
DO j=1,k
1210
! IF( ABS( vals(j) ) < eps_basis ) CYCLE
1211
IF(.NOT.EdgeBasis) Rind(Indexes(j)) = Rind(Indexes(j)) + 1
1212
1213
! Always normalize the weights to one!
1214
IF (.NOT.EdgeBasis.OR.(EdgeBasis.AND.j<=np)) THEN
1215
Cols(nrow) = Indexes(j)
1216
Values(nrow) = vals(j) / rowsum
1217
nrow = nrow + 1
1218
ELSE
1219
Cols(nrow) = -Indexes(j)
1220
Values(nrow) = WBasis(j-np,1)
1221
nrow = nrow + 1
1222
Cols(nrow) = -Indexes(j)
1223
Values(nrow) = WBasis(j-np,2)
1224
nrow = nrow + 1
1225
Cols(nrow) = -Indexes(j)
1226
Values(nrow) = WBasis(j-np,3)
1227
nrow = nrow + 1
1228
END IF
1229
END DO
1230
END IF
1231
END IF
1232
1233
Rows(i+1) = nrow
1234
END DO
1235
1236
IF( .NOT. ProjectorAllocated ) THEN
1237
ALLOCATE( Cols(Rows(n+1)-1), Values(Rows(n+1)-1) )
1238
Cols = 0
1239
Values = 0
1240
1241
ALLOCATE( Projector )
1242
Projector % Matrix => AllocateMatrix()
1243
Projector % Matrix % NumberOfRows = n
1244
Projector % Matrix % Rows => Rows
1245
Projector % Matrix % Cols => Cols
1246
Projector % Matrix % Values => Values
1247
1248
Projector % Next => NewMesh % Projector
1249
NewMesh % Projector => Projector
1250
NewMesh % Projector % Mesh => OldMesh
1251
1252
IF( .NOT.EdgeBasis) THEN
1253
ALLOCATE(Rind(OldMesh % NumberOfNodes)); Rind = 0
1254
END IF
1255
1256
ProjectorAllocated = .TRUE.
1257
1258
GOTO 100
1259
END IF
1260
1261
DEALLOCATE( Basis, Vals, ElemPtrs, LocalU, LocalV, LocalW, Indexes )
1262
1263
! Store also the transpose of the projector:
1264
! ------------------------------------------
1265
Projector % TMatrix => NULL()
1266
IF(.NOT.EdgeBasis) THEN
1267
IF ( Found ) THEN
1268
n = OldMesh % NumberOfNodes
1269
! Needed for some matrices
1270
n = MAX( n, MAXVAL( Projector % Matrix % Cols ) )
1271
1272
ALLOCATE( Rows(n+1) )
1273
Rows(1) = 1
1274
DO i=2,n+1
1275
Rows(i) = Rows(i-1)+Rind(i-1)
1276
END DO
1277
1278
ALLOCATE( Cols(Rows(n+1)-1), Values(Rows(n+1)-1) )
1279
Projector % TMatrix => AllocateMatrix()
1280
Projector % TMatrix % NumberOfRows = n
1281
Projector % TMatrix % Rows => Rows
1282
Projector % TMatrix % Cols => Cols
1283
Projector % TMatrix % Values => Values
1284
1285
RInd = 0
1286
DO i=1,Projector % Matrix % NumberOfRows
1287
DO j=Projector % Matrix % Rows(i), Projector % Matrix % Rows(i+1)-1
1288
k = Projector % Matrix % Cols(j)
1289
l = Rows(k) + Rind(k)
1290
Rind(k) = Rind(k) + 1
1291
Cols(l) = i
1292
Values(l) = Projector % Matrix % Values(j)
1293
END DO
1294
END DO
1295
END IF
1296
1297
DEALLOCATE(Rind)
1298
END IF
1299
1300
IF ( PRESENT(OldVariables) ) CALL ApplyProjector
1301
END IF
1302
1303
DEALLOCATE( ElementNodes % x, ElementNodes % y, &
1304
ElementNodes % z, ElementValues )
1305
1306
CONTAINS
1307
1308
FUNCTION LegitInterpVar(Var) RESULT ( IsLegit )
1309
TYPE(Variable_t), POINTER :: Var
1310
LOGICAL :: IsLegit
1311
1312
IsLegit = ( Var % TYPE == Variable_on_nodes_on_elements .OR. Var % Type == Variable_on_nodes )
1313
IF( Var % Dofs > 1 ) IsLegit = .FALSE.
1314
!IF( Var % Secondary ) IsLegit = .FALSE.
1315
IF(LEN(Var % Name) >= 10) THEN
1316
IF( Var % Name(1:10) == 'coordinate' ) IsLegit = .FALSE.
1317
END IF
1318
IF(.NOT. ASSOCIATED(Var % Perm) .AND. SIZE(Var % Values) == 1 ) IsLegit = .FALSE.
1319
1320
END FUNCTION LegitInterpVar
1321
1322
1323
1324
! Create a representative dg index to be used for interpolation.
1325
! This is cheating since it does not work in general. It does work
1326
! for the reduced basis DG. Even there it works only at intersections
1327
! if there is an additional mask that is used to pick the correct element.
1328
! For generic cases we would need a table to all DG indexes.
1329
!------------------------------------------------------------------------
1330
SUBROUTINE CreateOneDGIndex()
1331
INTEGER :: i,j,k,t,n
1332
TYPE(Element_t), POINTER :: Element,Parent
1333
INTEGER, TARGET :: TmpIndexes(20)
1334
INTEGER, POINTER :: pIndexes(:)
1335
1336
1337
CALL Info('InterpolateMesh2Mesh','Creating representative DG reindexing table!',Level=12)
1338
1339
ALLOCATE(OneDGIndex(NewMesh % NumberOfNodes))
1340
OneDGIndex = 0
1341
1342
DO t=1,NewMesh % NumberOfBulkElements + NewMesh % NumberOfBoundaryElements
1343
Element => NewMesh % Elements(t)
1344
1345
! This might take away all bulk elements so we need to be able to deal with
1346
! boundary elements as well.
1347
IF( PRESENT( NewMaskPerm ) ) THEN
1348
IF( ANY( NewMaskPerm(Element % NodeIndexes) == 0 ) ) CYCLE
1349
END IF
1350
1351
n = Element % Type % NumberOfNodes
1352
IF( ASSOCIATED( Element % DGIndexes ) ) THEN
1353
pIndexes => Element % DGIndexes
1354
ELSE IF( ASSOCIATED( Element % BoundaryInfo) ) THEN
1355
pIndexes => TmpIndexes
1356
pIndexes(1:n) = 0
1357
DO k=1,2
1358
IF(k==1) THEN
1359
Parent => Element % BoundaryInfo % Left
1360
ELSE
1361
Parent => Element % BoundaryInfo % Right
1362
END IF
1363
IF(.NOT. ASSOCIATED(Parent)) CYCLE
1364
DO i=1,n
1365
IF(pIndexes(i) > 0 ) CYCLE
1366
DO j=1,Parent % TYPE % NumberOfNodes
1367
IF(Element % NodeIndexes(i) == Parent % NodeIndexes(j) ) THEN
1368
pIndexes(i) = Parent % DGIndexes(j)
1369
EXIT
1370
END IF
1371
END DO
1372
END DO
1373
END DO
1374
ELSE
1375
CYCLE
1376
END IF
1377
1378
DO i=1,n
1379
j = Element % NodeIndexes(i)
1380
IF( OneDGIndex(j) > 0) CYCLE
1381
OneDGIndex(j) = pIndexes(i)
1382
END DO
1383
END DO
1384
1385
1386
END SUBROUTINE CreateOneDGIndex
1387
1388
1389
1390
!------------------------------------------------------------------------------
1391
SUBROUTINE ApplyProjector
1392
!------------------------------------------------------------------------------
1393
INTEGER :: i
1394
TYPE(Variable_t), POINTER :: Var
1395
!------------------------------------------------------------------------------
1396
Var => OldVariables
1397
DO WHILE( ASSOCIATED(Var) )
1398
IF(LegitInterpVar(Var)) THEN
1399
OldSol => VariableGet( OldMesh % Variables, Var % Name, .TRUE. )
1400
NewSol => VariableGet( NewMesh % Variables, Var % Name, .TRUE. )
1401
IF ( .NOT. (ASSOCIATED (NewSol) ) ) THEN
1402
Var => Var % Next
1403
CYCLE
1404
END IF
1405
1406
CALL CRS_ApplyProjector( Projector % Matrix, &
1407
OldSol % Values, OldSol % Perm, &
1408
NewSol % Values, NewSol % Perm )
1409
1410
IF ( ASSOCIATED( OldSol % PrevValues ) ) THEN
1411
DO i=1,SIZE(OldSol % PrevValues,2)
1412
CALL CRS_ApplyProjector( Projector % Matrix, &
1413
OldSol % PrevValues(:,i), OldSol % Perm, &
1414
NewSol % PrevValues(:,i), NewSol % Perm )
1415
END DO
1416
END IF
1417
END IF
1418
Var => Var % Next
1419
END DO
1420
1421
!------------------------------------------------------------------------------
1422
END SUBROUTINE ApplyProjector
1423
!------------------------------------------------------------------------------
1424
END SUBROUTINE InterpolateMeshToMeshQ
1425
!------------------------------------------------------------------------------
1426
1427
1428
1429
!---------------------------------------------------------------------------
1430
!> Create a projector for mapping between interfaces using the Galerkin method
1431
!> A temporal mesh structure with a node for each Gaussian integration point is
1432
!> created. This projector matrix is transferred to a projector on the nodal
1433
!> coordinates.
1434
!> Note that this approach is very suboptimal compared to the version where
1435
!> a temporal supermesh is used for in the integration.
1436
!---------------------------------------------------------------------------
1437
FUNCTION WeightedProjector(BMesh2, BMesh1, InvPerm2, InvPerm1, &
1438
UseQuadrantTree, Repeating, AntiRepeating, PeriodicScale, &
1439
NodalJump ) &
1440
RESULT ( Projector )
1441
!---------------------------------------------------------------------------
1442
USE DefUtils
1443
USE MeshUtils, ONLY : PreRotationalProjector, PostRotationalProjector
1444
IMPLICIT NONE
1445
1446
TYPE(Mesh_t), POINTER :: BMesh1, BMesh2
1447
REAL(KIND=dp) :: PeriodicScale
1448
INTEGER, POINTER :: InvPerm1(:), InvPerm2(:)
1449
LOGICAL :: UseQuadrantTree, Repeating, AntiRepeating
1450
TYPE(Matrix_t), POINTER :: Projector
1451
LOGICAL :: NodalJump
1452
!--------------------------------------------------------------------------
1453
LOGICAL, ALLOCATABLE :: MirrorNode(:)
1454
TYPE(Matrix_t), POINTER :: GaussProjector
1455
TYPE(Nodes_t), POINTER :: GaussNodes, RealNodes, ElementNodes
1456
TYPE(Element_t), POINTER :: Element
1457
INTEGER :: i,j,k,l,n,p,q,NoNodes, NoGaussPoints,Indexes(100),&
1458
nodesize, totsize, eqindsize, RelOrder
1459
INTEGER, POINTER :: NodeIndexes(:),Rows(:),Cols(:)
1460
REAL(KIND=dp), POINTER :: Basis(:), Values(:)
1461
REAL(KIND=dp) :: u,v,w,val,detJ,weight,x
1462
TYPE(GaussIntegrationPoints_t) :: IntegStuff
1463
LOGICAL :: Stat, EdgeBasis,Found,AxisSym, PiolaT
1464
TYPE(Nodes_t), SAVE :: Nodes
1465
1466
REAL(KIND=dp) :: vq(3),wq(3),f(3,3),g(3,3)
1467
REAL(KIND=dp), ALLOCATABLE ::WBasis(:,:),RotWbasis(:,:),dBasisdx(:,:)
1468
1469
INTEGER :: ind,np,qq,pp
1470
INTEGER, ALLOCATABLE :: Eqind(:), xPerm(:)
1471
1472
1473
CALL Info('WeightedProjector','Creating Galerkin projector between two boundaries',Level=7)
1474
1475
ALLOCATE( GaussNodes, ElementNodes )
1476
RealNodes => Bmesh1 % Nodes
1477
NoNodes = Bmesh1 % NumberOfNodes
1478
1479
EdgeBasis = .FALSE.
1480
IF (ASSOCIATED(CurrentModel % Solver)) THEN
1481
EdgeBasis = ListGetLogical(CurrentModel % Solver % Values,'Edge Basis',Found)
1482
END IF
1483
1484
PiolaT = .FALSE.
1485
IF( EdgeBasis ) THEN
1486
PiolaT = ListGetLogical( CurrentModel % Solver % Values, 'Use Piola Transform', Found)
1487
CALL Info('weightedProjector','Accounting for edge elements in projector.',Level=7)
1488
END IF
1489
1490
RelOrder = ListGetInteger( CurrentModel % Solver % Values, &
1491
'Projector Relative Integration Order', Found, minv=-1,maxv=1)
1492
1493
! Calculate the total number of Gaussian integration points
1494
! and allocate space for the node structures.
1495
!----------------------------------------------------------
1496
NoGaussPoints = 0
1497
DO i=1, BMesh1 % NumberOfBulkElements
1498
Element => BMesh1 % Elements(i)
1499
IntegStuff = GaussPoints( Element, RelOrder=RelOrder, EdgeBasis=PiolaT )
1500
NoGaussPoints = NoGaussPoints + IntegStuff % n
1501
END DO
1502
1503
WRITE( Message,'(A,I0,A,I0)') 'Number of nodes and gauss points:'&
1504
,NoNodes,' and ',NoGaussPoints
1505
CALL Info('WeightedProjector',Message,Level=10)
1506
1507
ALLOCATE( GaussNodes % x(NoGaussPoints), GaussNodes % y(NoGaussPoints), GaussNodes % z(NoGaussPoints))
1508
1509
! Change the local coordinates of the BMesh2 to match to corresponding faces:
1510
! ---------------------------------------------------------------------------
1511
IF(EdgeBasis) THEN
1512
ALLOCATE(xPerm(Bmesh2 % Parent % NumberofNodes)); xPerm=0
1513
DO i=1,SIZE(InvPerm2)
1514
xPerm(InvPerm2(i)) = i
1515
END DO
1516
1517
DO i=1, BMesh2 % NumberOfBulkElements
1518
Element => BMesh2 % Parent % Faces(BMesh2 % Elements(i) % ElementIndex)
1519
BMesh2 % Elements(i) % NodeIndexes = xPerm(Element % NodeIndexes)
1520
END DO
1521
END IF
1522
1523
AxisSym = .FALSE.
1524
IF ( CurrentCoordinateSystem() == AxisSymmetric .OR. &
1525
CurrentCoordinateSystem() == CylindricSymmetric ) THEN
1526
IF( NodalJump ) THEN
1527
AxisSym = .TRUE.
1528
ELSE IF (ASSOCIATED(CurrentModel % Solver)) THEN
1529
AxisSym = ListGetLogical(CurrentModel % Solver % Values,'Projector Metrics',Found)
1530
END IF
1531
IF( AxisSym ) CALL Info('weightedProjector','Projector will be weighted for axi symmetry',Level=7)
1532
END IF
1533
1534
1535
nodesize = BMesh1 % Parent % NumberOfNodes
1536
totsize = BMesh1 % Parent % NumberOfNodes + BMesh1 % Parent % NumberOfEdges
1537
IF(ASSOCIATED(CurrentModel % Solver)) THEN
1538
totsize = CurrentModel % Solver % Matrix % NumberOfRows / &
1539
CurrentModel % Solver % Variable % Dofs
1540
END IF
1541
1542
IF(EdgeBasis) THEN
1543
n = BMesh1 % Parent % MaxElementDOFs
1544
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n), &
1545
Basis(n), dBasisdx(n,3), WBasis(n,3), RotWBasis(n,3) )
1546
eqindsize = totsize
1547
ELSE
1548
n = BMesh1 % MaxElementNodes
1549
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n), Basis(n) )
1550
eqindsize = BMesh1 % NumberOfNodes
1551
END IF
1552
1553
eqindsize = 0
1554
DO i=1, BMesh1 % NumberOfBulkElements
1555
IF(EdgeBasis) THEN
1556
Element => BMesh1 % Parent % Faces(BMesh1 % Elements(i) % ElementIndex)
1557
n = GetElementDOFs(Indexes,Element)
1558
np = GetElementNOFNodes(Element)
1559
ELSE
1560
Element => BMesh1 % Elements(i)
1561
n = Element % TYPE % NumberOfNodes
1562
np = n
1563
Indexes(1:n) = Element % NodeIndexes
1564
END IF
1565
eqindsize = MAX( eqindsize, MAXVAL(Indexes(1:n)) )
1566
END DO
1567
1568
1569
! Create the nodal coordinates for all Gaussian integration points
1570
!-----------------------------------------------------------------
1571
NoGaussPoints = 0
1572
DO i=1, BMesh1 % NumberOfBulkElements
1573
Element => BMesh1 % Elements(i)
1574
n = Element % TYPE % NumberOfNodes
1575
NodeIndexes => Element % NodeIndexes
1576
ElementNodes % x(1:n) = RealNodes % x(NodeIndexes(1:n))
1577
ElementNodes % y(1:n) = RealNodes % y(NodeIndexes(1:n))
1578
ElementNodes % z(1:n) = RealNodes % z(NodeIndexes(1:n))
1579
1580
IntegStuff = GaussPoints( Element, RelOrder=RelOrder, EdgeBasis=PiolaT )
1581
DO j=1,IntegStuff % n
1582
NoGaussPoints = NoGaussPoints + 1
1583
u = IntegStuff % u(j)
1584
v = IntegStuff % v(j)
1585
w = IntegStuff % w(j)
1586
IF(PiolaT) THEN
1587
stat = ElementInfo( Element, ElementNodes, u,v,w, &
1588
detJ, Basis, EdgeBasis=WBasis )
1589
ELSE
1590
Stat = ElementInfo( Element, ElementNodes, u, v, w, detJ, Basis )
1591
END IF
1592
GaussNodes % x(NoGaussPoints) = SUM( Basis(1:n) * ElementNodes % x(1:n) )
1593
GaussNodes % y(NoGaussPoints) = SUM( Basis(1:n) * ElementNodes % y(1:n) )
1594
GaussNodes % z(NoGaussPoints) = SUM( Basis(1:n) * ElementNodes % z(1:n) )
1595
END DO
1596
END DO
1597
1598
BMesh1 % Nodes => GaussNodes
1599
BMesh1 % NumberOfNodes = NoGaussPoints
1600
1601
! Create the mirror node flag and map the nodes of Mesh1 to be
1602
! in the interval of Mesh2.
1603
!-----------------------------------------------------------------
1604
IF( Repeating ) THEN
1605
IF( AntiRepeating ) THEN
1606
ALLOCATE( MirrorNode( BMesh1 % NumberOfNodes ) )
1607
MirrorNode = .FALSE.
1608
END IF
1609
CALL PreRotationalProjector(BMesh1, BMesh2, MirrorNode )
1610
END IF
1611
1612
! Create the projector for Gaussian integration points
1613
!-----------------------------------------------------------------
1614
GaussProjector => MeshProjector( BMesh2, BMesh1, UseQuadrantTree )
1615
Rows => GaussProjector % Rows
1616
Cols => GaussProjector % Cols
1617
Values => GaussProjector % Values
1618
1619
! If there are mirror nodes change the sign
1620
!-----------------------------------------------------------------------------
1621
IF( AntiRepeating ) THEN
1622
CALL PostRotationalProjector( GaussProjector, MirrorNode )
1623
IF( ALLOCATED( MirrorNode) ) DEALLOCATE( MirrorNode )
1624
END IF
1625
1626
! Transfer the projector on the Gaussian points to that on
1627
! nodal points utilizing the flexibility of the list matrix.
1628
!-----------------------------------------------------------
1629
Projector => AllocateMatrix()
1630
Projector % FORMAT = MATRIX_LIST
1631
Projector % ProjectorType = PROJECTOR_TYPE_GALERKIN
1632
1633
ALLOCATE(Eqind(eqindsize))
1634
EqInd = 0
1635
1636
ALLOCATE(Projector % InvPerm(eqindsize))
1637
Projector % InvPerm = 0
1638
1639
Ind = 0
1640
1641
NoGaussPoints = 0
1642
DO i=1, BMesh1 % NumberOfBulkElements
1643
IF(EdgeBasis) THEN
1644
Element => BMesh1 % Parent % Faces(BMesh1 % Elements(i) % ElementIndex)
1645
n = GetElementDOFs(Indexes,Element)
1646
np = GetElementNOFNodes(Element)
1647
IF(ANY(Indexes(1:np)>Element % NodeIndexes)) np=0
1648
CALL GetElementNodes(Nodes,Element)
1649
ELSE
1650
Element => BMesh1 % Elements(i)
1651
n = Element % TYPE % NumberOfNodes
1652
np = n
1653
Indexes(1:n) = Element % NodeIndexes
1654
ElementNodes % x(1:n) = RealNodes % x(Indexes(1:n))
1655
ElementNodes % y(1:n) = RealNodes % y(Indexes(1:n))
1656
ElementNodes % z(1:n) = RealNodes % z(Indexes(1:n))
1657
END IF
1658
1659
IntegStuff = GaussPoints(Element, RelOrder=RelOrder, EdgeBasis=PiolaT )
1660
DO j=1,IntegStuff % n
1661
NoGaussPoints = NoGaussPoints + 1
1662
u = IntegStuff % u(j)
1663
v = IntegStuff % v(j)
1664
w = IntegStuff % w(j)
1665
1666
IF(EdgeBasis) THEN
1667
IF(PiolaT) THEN
1668
stat = ElementInfo( Element,Nodes,u,v,w,detJ,Basis,EdgeBasis=WBasis)
1669
ELSE
1670
Stat = ElementInfo(Element, Nodes, u, v, w, detJ, Basis,dBasisdx)
1671
CALL GetEdgeBasis(Element,WBasis,RotWBasis,Basis,dBasisdx)
1672
END IF
1673
ELSE
1674
Stat = ElementInfo(Element, ElementNodes, u, v, w, detJ, Basis)
1675
END IF
1676
1677
! Modify weight so that the projector is consistent with the coordinate system.
1678
weight = detJ * IntegStuff % s(j)
1679
IF( AxisSym ) THEN
1680
IF( EdgeBasis ) THEN
1681
x = SUM( Basis(1:np) * Nodes % x(1:np) )
1682
ELSE
1683
x = SUM( Basis(1:np) * ElementNodes % x(1:np) )
1684
END IF
1685
weight = weight * x
1686
END IF
1687
1688
1689
! Do the numbering of new dofs
1690
! This needs to be done here because the nodal jump
1691
! needs the index related to (p,q) pair.
1692
DO p=1,np
1693
IF (EQind(Indexes(p))==0) THEN
1694
Ind = Ind+1
1695
EQind(Indexes(p)) = Ind
1696
IF( EdgeBasis ) THEN
1697
Projector % InvPerm(Ind) = Indexes(p)
1698
ELSE
1699
Projector % InvPerm(Ind) = InvPerm1(Indexes(p))
1700
END IF
1701
END IF
1702
END DO
1703
1704
DO p=1,np
1705
val = weight * Basis(p)
1706
1707
DO q=1,np
1708
qq = Indexes(q)
1709
IF(.NOT.EdgeBasis) qq=InvPerm1(qq)
1710
CALL List_AddToMatrixElement(Projector % ListMatrix, EQind(Indexes(p)), qq, Basis(q) * val )
1711
1712
! Add a diagonal entry to the future constrained system.
1713
! This will enable a jump to the discontinuous boundary.
1714
! So far no value is added just the sparse matrix entry.
1715
!IF( NodalJump ) THEN
1716
! IF( Indexes(p) <= nodesize .AND. Indexes(q) <= nodesize ) THEN
1717
! CALL List_AddToMatrixElement(Projector % ListMatrix, EQind(Indexes(p)),&
1718
! totsize + EQInd(Indexes(q)), 0.0_dp )
1719
! END IF
1720
!END IF
1721
END DO
1722
1723
DO q = Rows(NoGaussPoints), Rows(NoGaussPoints+1)-1
1724
qq = Cols(q)
1725
IF (qq<=0) EXIT
1726
IF(.NOT.EdgeBasis) qq=InvPerm2(qq)
1727
CALL List_AddToMatrixElement(Projector % ListMatrix, &
1728
EQind(Indexes(p)), qq, -PeriodicScale * Values(q) * val )
1729
END DO
1730
END DO
1731
1732
IF(EdgeBasis)THEN
1733
DO p=np+1,n
1734
pp=p-np
1735
1736
wq = WBasis(pp,:)
1737
1738
IF (EQind(Indexes(p))==0) THEN
1739
Ind = Ind+1
1740
EQind(Indexes(p)) = Ind
1741
Projector % InvPerm(Ind) = Indexes(p)
1742
END IF
1743
1744
DO q=np+1,n
1745
qq = q-np
1746
1747
vq = Wbasis(qq,:)
1748
CALL List_AddToMatrixElement(Projector % ListMatrix, EQind(Indexes(p)),&
1749
Indexes(q), weight * SUM(vq*wq))
1750
END DO
1751
1752
1753
DO q = Rows(NoGaussPoints)+np, Rows(NoGaussPoints+1)-1,3
1754
IF(Cols(q)>=0) STOP 'q'
1755
1756
vq(1) = Values(q)
1757
vq(2) = Values(q+1)
1758
vq(3) = Values(q+2)
1759
1760
CALL List_AddToMatrixElement( Projector % ListMatrix,&
1761
EQind(Indexes(p)), -Cols(q), -PeriodicScale * weight * SUM(vq*wq))
1762
END DO
1763
END DO
1764
ENDIF
1765
END DO
1766
END DO
1767
1768
1769
CALL List_ToCRSMatrix(Projector)
1770
1771
BMesh1 % Nodes => RealNodes
1772
BMesh1 % NumberOfNodes = NoNodes
1773
1774
! Finally, deallocate permanent storage
1775
!----------------------------------------------------------------
1776
DEALLOCATE( ElementNodes % x, ElementNodes % y, ElementNodes % z )
1777
DEALLOCATE( ElementNodes )
1778
1779
DEALLOCATE( GaussNodes % x, GaussNodes % y, GaussNodes % z)
1780
DEALLOCATE( GaussNodes )
1781
1782
DEALLOCATE( Basis )
1783
IF(EdgeBasis) DEALLOCATE( dBasisdx, WBasis, RotWBasis )
1784
1785
!------------------------------------------------------------------------------
1786
END FUNCTION WeightedProjector
1787
!------------------------------------------------------------------------------
1788
1789
1790
!------------------------------------------------------------------------------
1791
!> When we have a field defined on IP points we may temporarily swap it to be a field
1792
!> defined on DG points. This is done by solving small linear system for each element.
1793
!------------------------------------------------------------------------------------
1794
SUBROUTINE Ip2DgFieldInElement( Mesh, Element, nip, fip, ndg, fdg )
1795
!------------------------------------------------------------------------------
1796
USE Types
1797
USE Integration
1798
USE ElementDescription
1799
IMPLICIT NONE
1800
1801
TYPE(Mesh_t), POINTER :: Mesh
1802
TYPE(Element_t), POINTER :: Element
1803
INTEGER :: nip, ndg
1804
REAL(KIND=dp) :: fip(:), fdg(:)
1805
!------------------------------------------------------------------------------
1806
REAL(KIND=dp) :: Weight, DetJ
1807
REAL(KIND=dp), ALLOCATABLE :: Basis(:), MASS(:,:), LOAD(:)
1808
INTEGER :: i,t,p,q,n
1809
TYPE(GaussIntegrationPoints_t) :: IP
1810
TYPE(Nodes_t) :: Nodes
1811
LOGICAL :: Stat, CSymmetry, AllocationsDone = .FALSE.
1812
CHARACTER(*), PARAMETER :: Caller = 'Ip2DgFieldInElement'
1813
TYPE(Element_t), POINTER :: PrevElement => NULL()
1814
1815
SAVE Nodes, Basis, MASS, LOAD, CSymmetry, PrevElement, AllocationsDone
1816
!------------------------------------------------------------------------------
1817
1818
IF( .NOT. AllocationsDone ) THEN
1819
n = Mesh % MaxElementNodes
1820
ALLOCATE( Basis(n), LOAD(n), MASS(n,n) )
1821
CSymmetry = CurrentCoordinateSystem() == AxisSymmetric .OR. &
1822
CurrentCoordinateSystem() == CylindricSymmetric
1823
ALLOCATE( Nodes % x(n), Nodes % y(n), Nodes % z(n) )
1824
AllocationsDone = .TRUE.
1825
END IF
1826
1827
IF(nip == 0) THEN
1828
CALL Warn(Caller,'No IP variables given')
1829
fdg(1:ndg) = 0.0_dp
1830
RETURN
1831
END IF
1832
1833
n = Element % TYPE % NumberOfNodes
1834
IF( n /= ndg ) CALL Fatal(Caller,'Mismatch in sizes!')
1835
1836
! We could probably do more to utilize the previous visit to save resources...
1837
IF(.NOT. ASSOCIATED( Element, PrevElement ) ) THEN
1838
Nodes % x(1:n) = Mesh % Nodes % x(Element % NodeIndexes)
1839
Nodes % y(1:n) = Mesh % Nodes % y(Element % NodeIndexes)
1840
Nodes % z(1:n) = Mesh % Nodes % z(Element % NodeIndexes)
1841
PrevElement => Element
1842
END IF
1843
1844
MASS = 0._dp
1845
LOAD = 0._dp
1846
1847
! Numerical integration:
1848
!-----------------------
1849
IP = GaussPoints( Element, nip )
1850
1851
DO t=1,IP % n
1852
stat = ElementInfo( Element, Nodes, IP % U(t), IP % V(t), IP % W(t), detJ, Basis )
1853
Weight = IP % s(t) * DetJ
1854
1855
IF( CSymmetry ) THEN
1856
Weight = Weight * SUM( Basis(1:n) * Nodes % x(1:n) )
1857
END IF
1858
1859
DO p=1,n
1860
LOAD(p) = LOAD(p) + Weight * Basis(p) * fip(t)
1861
DO q=1,n
1862
MASS(p,q) = MASS(p,q) + Weight * Basis(q) * Basis(p)
1863
END DO
1864
END DO
1865
END DO
1866
1867
CALL LuSolve(n,MASS,LOAD)
1868
1869
fdg(1:n) = LOAD(1:n)
1870
!------------------------------------------------------------------------------
1871
END SUBROUTINE Ip2DgFieldInElement
1872
!------------------------------------------------------------------------------
1873
1874
!------------------------------------------------------------------------------
1875
!> Given a finite element field expressed in terms of the hierarchic p-basis
1876
!> find its elementwise Lagrange interpolant of a specified degree. At the moment
1877
!> this subroutine has limited functionality, since the coordinates of the Lagrange
1878
!> nodes are not yet defined for all element types in the case of high p.
1879
!------------------------------------------------------------------------------
1880
SUBROUTINE HierarchicPToLagrange(PElement, Degree, PSol, LSol, DOFs, PSolver)
1881
!------------------------------------------------------------------------------
1882
USE MeshUtils, ONLY: AllocateElement
1883
USE ElementDescription
1884
IMPLICIT NONE
1885
1886
TYPE(Element_t), POINTER, INTENT(IN) :: PElement !< An element structure capable for p-approximation
1887
INTEGER, INTENT(IN) :: Degree !< The desired order of the Lagrange interpolant
1888
REAL(KIND=dp), INTENT(IN) :: PSol(:,:) !< The DOFs of p-solution
1889
REAL(KIND=dp), INTENT(INOUT) :: LSol(:,:) !< The DOFs for the Lagrange interpolation
1890
INTEGER, OPTIONAL, INTENT(IN) :: DOFs !< The first dimension of PSol
1891
TYPE(Solver_t), POINTER, OPTIONAL, INTENT(IN) :: PSolver !< For seeking p-definitions from this solver
1892
!------------------------------------------------------------------------------
1893
INTEGER, PARAMETER :: MAX_LINE_DEGREE = 8
1894
INTEGER, PARAMETER :: MAX_TRIANGLE_DEGREE = 8
1895
INTEGER, PARAMETER :: MAX_TRIANGLE_NODES = 45
1896
INTEGER, PARAMETER :: MAX_QUAD_DEGREE = 8
1897
INTEGER, PARAMETER :: MAX_TETRA_DEGREE = 4
1898
INTEGER, PARAMETER :: MAX_TETRA_NODES = 35
1899
INTEGER, PARAMETER :: MAX_PYRAMID_DEGREE = 2
1900
INTEGER, PARAMETER :: MAX_PYRAMID_NODES = 15
1901
INTEGER, PARAMETER :: MAX_PRISM_DEGREE = 4
1902
INTEGER, PARAMETER :: MAX_PRISM_NODES = 75
1903
INTEGER, PARAMETER :: MAX_BRICK_DEGREE = 8
1904
INTEGER, PARAMETER :: MAX_LAGRANGE_NODES = 729
1905
1906
INTEGER, PARAMETER :: MAX_DEGREE = 8 ! The maximal polynomial degree over any element shape
1907
1908
TYPE(Mesh_t), POINTER :: Mesh
1909
TYPE(Nodes_t) :: PNodes
1910
TYPE(Element_t), POINTER :: LElement => NULL()
1911
LOGICAL :: AllocationsDone = .FALSE.
1912
LOGICAL :: PVersion, stat
1913
INTEGER, ALLOCATABLE :: BasisDegree(:)
1914
INTEGER :: Family, Fields, MaxFields, i, j, k, n, t, nd
1915
INTEGER :: i_start, i_end
1916
INTEGER :: Offsets(MAX_DEGREE)
1917
INTEGER :: Edge(2), Face(4)
1918
REAL(KIND=dp), ALLOCATABLE :: PBasis(:)
1919
REAL(KIND=dp), POINTER :: NodesU(:), NodesV(:), NodesW(:)
1920
REAL(KIND=dp) :: ut, vt, wt, detJ
1921
REAL(KIND=dp) :: r(3), delta
1922
1923
REAL(KIND=dp), TARGET, SAVE :: VTKLineU(MAX_LINE_DEGREE+1,MAX_LINE_DEGREE+1)
1924
1925
REAL(KIND=dp), TARGET, SAVE :: VTKTriangleU(MAX_TRIANGLE_DEGREE,MAX_TRIANGLE_NODES)
1926
REAL(KIND=dp), TARGET, SAVE :: VTKTriangleV(MAX_TRIANGLE_DEGREE,MAX_TRIANGLE_NODES)
1927
REAL(KIND=dp), TARGET, SAVE :: VTKTriangleW(MAX_TRIANGLE_NODES)
1928
INTEGER, SAVE :: TriangleNodeCounts(MAX_TRIANGLE_DEGREE)
1929
1930
REAL(KIND=dp), TARGET, SAVE :: VTKQuadU(MAX_QUAD_DEGREE,(MAX_QUAD_DEGREE+1)**2)
1931
REAL(KIND=dp), TARGET, SAVE :: VTKQuadV(MAX_QUAD_DEGREE,(MAX_QUAD_DEGREE+1)**2)
1932
REAL(KIND=dp), TARGET, SAVE :: VTKQuadW((MAX_QUAD_DEGREE+1)**2)
1933
INTEGER, SAVE :: QuadNodeCounts(MAX_QUAD_DEGREE)
1934
1935
REAL(KIND=dp), TARGET, SAVE :: VTKTetraU(MAX_TETRA_DEGREE,MAX_TETRA_NODES)
1936
REAL(KIND=dp), TARGET, SAVE :: VTKTetraV(MAX_TETRA_DEGREE,MAX_TETRA_NODES)
1937
REAL(KIND=dp), TARGET, SAVE :: VTKTetraW(MAX_TETRA_DEGREE,MAX_TETRA_NODES)
1938
INTEGER, SAVE :: TetraNodeCounts(MAX_TETRA_DEGREE)
1939
INTEGER :: VTKTetraFaceMap(4,3)
1940
1941
REAL(KIND=dp), TARGET, SAVE :: VTKPyramidU(MAX_PYRAMID_DEGREE,MAX_PYRAMID_NODES)
1942
REAL(KIND=dp), TARGET, SAVE :: VTKPyramidV(MAX_PYRAMID_DEGREE,MAX_PYRAMID_NODES)
1943
REAL(KIND=dp), TARGET, SAVE :: VTKPyramidW(MAX_PYRAMID_DEGREE,MAX_PYRAMID_NODES)
1944
INTEGER, SAVE :: PyramidNodeCounts(MAX_PYRAMID_DEGREE)
1945
1946
REAL(KIND=dp), TARGET, SAVE :: VTKPrismU(MAX_PRISM_DEGREE,MAX_PRISM_NODES)
1947
REAL(KIND=dp), TARGET, SAVE :: VTKPrismV(MAX_PRISM_DEGREE,MAX_PRISM_NODES)
1948
REAL(KIND=dp), TARGET, SAVE :: VTKPrismW(MAX_PRISM_DEGREE,MAX_PRISM_NODES)
1949
INTEGER, SAVE :: PrismNodeCounts(MAX_PRISM_DEGREE)
1950
1951
REAL(KIND=dp), TARGET, SAVE :: VTKBrickU(MAX_BRICK_DEGREE,(MAX_BRICK_DEGREE+1)**3)
1952
REAL(KIND=dp), TARGET, SAVE :: VTKBrickV(MAX_BRICK_DEGREE,(MAX_BRICK_DEGREE+1)**3)
1953
REAL(KIND=dp), TARGET, SAVE :: VTKBrickW(MAX_BRICK_DEGREE,(MAX_BRICK_DEGREE+1)**3)
1954
INTEGER, SAVE :: BrickNodeCounts(MAX_BRICK_DEGREE)
1955
INTEGER :: VTKBrickFaceMap(6,4)
1956
1957
CHARACTER(*), PARAMETER :: Caller = 'HierarchicPToLagrange'
1958
1959
SAVE PNodes, LElement, AllocationsDone, PBasis, BasisDegree
1960
!------------------------------------------------------------------------------
1961
LSol = 0.0d0
1962
1963
MaxFields = MIN(SIZE(PSol,1), SIZE(LSol,1))
1964
IF (PRESENT(DOFs)) THEN
1965
IF (DOFs > MaxFields) THEN
1966
CALL Warn(Caller, 'Too small arrays to write all fields')
1967
Fields = MaxFields
1968
ELSE
1969
Fields = DOFs
1970
END IF
1971
ELSE
1972
Fields = MaxFields ! Write as many fields as seems to be possible
1973
END IF
1974
1975
Family = PElement % TYPE % ElementCode / 100
1976
1977
IF (Degree == 1) THEN
1978
DO k=1,Fields
1979
LSol(k,1:Family) = PSol(k,1:Family)
1980
END DO
1981
RETURN
1982
END IF
1983
1984
Mesh => CurrentModel % Solver % Mesh
1985
IF (PRESENT(PSolver)) THEN
1986
IF(ASSOCIATED(PSolver)) Mesh => PSolver % Mesh
1987
END IF
1988
1989
IF (.NOT. AllocationsDone) THEN
1990
LElement => AllocateElement()
1991
n = Mesh % MaxElementDOFs
1992
ALLOCATE(PBasis(n), BasisDegree(n), PNodes % x(n), &
1993
PNodes % y(n), PNodes % z(n))
1994
1995
! --------------------------------------------------------------------------
1996
! Create the nodes for line elements (works for any given degree):
1997
! --------------------------------------------------------------------------
1998
VTKLineU(1:MAX_LINE_DEGREE,1) = -1.0d0
1999
VTKLineU(1:MAX_LINE_DEGREE,2) = 1.0d0
2000
DO k=2,MAX_LINE_DEGREE
2001
delta = 2.0d0/k
2002
ut = -1.0d0
2003
DO i=1,k-1
2004
ut = ut + delta
2005
VTKLineU(k,i+2) = ut
2006
END DO
2007
END DO
2008
VTKLineU(MAX_LINE_DEGREE+1,:) = 0.0d0 ! One extra row to get zero values for non-active parameters
2009
2010
! --------------------------------------------------------------------------
2011
! Create the nodes for triangles (basically works for any given degree):
2012
! --------------------------------------------------------------------------
2013
VTKTriangleU(1:MAX_TRIANGLE_DEGREE,1) = -1.0d0
2014
VTKTriangleU(1:MAX_TRIANGLE_DEGREE,2) = 1.0d0
2015
VTKTriangleU(1:MAX_TRIANGLE_DEGREE,3) = 0.0d0
2016
VTKTriangleV(1:MAX_TRIANGLE_DEGREE,1) = 0.0d0
2017
VTKTriangleV(1:MAX_TRIANGLE_DEGREE,2) = 0.0d0
2018
VTKTriangleV(1:MAX_TRIANGLE_DEGREE,3) = SQRT(3.0d0)
2019
VTKTriangleW = 0.0d0
2020
2021
! The edges:
2022
TriangleNodeCounts = 3
2023
DO k=1,3
2024
Edge = GetTriangleEdgeMap(k)
2025
Offsets(1:MAX_TRIANGLE_DEGREE) = TriangleNodeCounts(1:MAX_TRIANGLE_DEGREE)
2026
CALL NodesOnEdge(VTKTriangleU, VTKTriangleV, Edge(1), Edge(2), MAX_TRIANGLE_DEGREE, &
2027
Offsets, TriangleNodeCounts)
2028
END DO
2029
2030
! The bubbles:
2031
VTKTriangleU(3,10) = 0.0d0
2032
VTKTriangleV(3,10) = SQRT(3.0d0)/3.0d0
2033
TriangleNodeCounts(3) = TriangleNodeCounts(3) + 1
2034
2035
DO j=4,MAX_TRIANGLE_DEGREE
2036
!
2037
! We can re-use the nodes of (j-3)th order triangle:
2038
!
2039
i_start = 3*j + 1
2040
SELECT CASE(j)
2041
CASE(4)
2042
n = 3
2043
CASE(5)
2044
n = 6
2045
CASE(6)
2046
n = 10
2047
CASE(7)
2048
n = 15
2049
CASE(8)
2050
n = 21
2051
CASE DEFAULT
2052
CALL Fatal(Caller, 'Triangle interior nodes supported up to degree 8')
2053
END SELECT
2054
i_end = 3*j + n
2055
2056
VTKTriangleU(j,i_start:i_end) = (j-3.0d0)/j * VTKTriangleU(j-3,1:n)
2057
vt = SQRT(3.0d0)/j
2058
VTKTriangleV(j,i_start:i_end) = vt + (j-3.0d0)/j * VTKTriangleV(j-3,1:n)
2059
TriangleNodeCounts(j) = TriangleNodeCounts(j) + n
2060
END DO
2061
2062
! --------------------------------------------------------------------------
2063
! Create the nodes for quads (works for any given degree):
2064
! --------------------------------------------------------------------------
2065
VTKQuadU(1:MAX_QUAD_DEGREE,1) = -1.0d0
2066
VTKQuadU(1:MAX_QUAD_DEGREE,2:3) = 1.0d0
2067
VTKQuadU(1:MAX_QUAD_DEGREE,4) = -1.0d0
2068
VTKQuadV(1:MAX_QUAD_DEGREE,1:2) = -1.0d0
2069
VTKQuadV(1:MAX_QUAD_DEGREE,3:4) = 1.0d0
2070
2071
! The edges:
2072
QuadNodeCounts = 4
2073
DO k=1,4
2074
Edge = GetQuadEdgeMap(k)
2075
Offsets(1:MAX_QUAD_DEGREE) = QuadNodeCounts(1:MAX_QUAD_DEGREE)
2076
CALL NodesOnEdge(VTKQuadU, VTKQuadV, Edge(1), Edge(2), MAX_QUAD_DEGREE, &
2077
Offsets, QuadNodeCounts)
2078
END DO
2079
2080
! The bubbles:
2081
Offsets(1:MAX_QUAD_DEGREE) = QuadNodeCounts(1:MAX_QUAD_DEGREE)
2082
DO k=2,MAX_QUAD_DEGREE
2083
delta = 2.0d0/k
2084
vt = -1.0d0
2085
DO i=1,k-1
2086
vt = vt + delta
2087
ut = -1.0d0
2088
DO j=1,k-1
2089
ut = ut + delta
2090
VTKQuadU(k,j+Offsets(k)) = ut
2091
VTKQuadV(k,j+Offsets(k)) = vt
2092
QuadNodeCounts(k) = QuadNodeCounts(k) + 1
2093
END DO
2094
Offsets(k) = QuadNodeCounts(k)
2095
END DO
2096
END DO
2097
VTKQuadW = 0.0d0
2098
2099
! --------------------------------------------------------------------------
2100
! Create the nodes for tetrahedra:
2101
! --------------------------------------------------------------------------
2102
LElement % Type => GetElementType(504, .FALSE.)
2103
CALL GetRefPElementNodes(LElement % Type, VTKTetraU(1,1:4), &
2104
VTKTetraV(1,1:4), VTKTetraW(1,1:4))
2105
2106
DO k=2,MAX_TETRA_DEGREE
2107
VTKTetraU(k,1:4) = VTKTetraU(1,1:4)
2108
VTKTetraV(k,1:4) = VTKTetraV(1,1:4)
2109
VTKTetraW(k,1:4) = VTKTetraW(1,1:4)
2110
END DO
2111
2112
! The edges:
2113
TetraNodeCounts = 4
2114
DO k=1,6
2115
Edge = GetTetraEdgeMap(k)
2116
Offsets(1:MAX_TETRA_DEGREE) = TetraNodeCounts(1:MAX_TETRA_DEGREE)
2117
IF (k == 3) THEN
2118
CALL NodesOnEdge(VTKTetraU, VTKTetraV, Edge(2), Edge(1), MAX_TETRA_DEGREE, &
2119
Offsets, TetraNodeCounts, VTKTetraW)
2120
ELSE
2121
CALL NodesOnEdge(VTKTetraU, VTKTetraV, Edge(1), Edge(2), MAX_TETRA_DEGREE, &
2122
Offsets, TetraNodeCounts, VTKTetraW)
2123
END IF
2124
END DO
2125
2126
! The faces:
2127
Offsets(1:MAX_TETRA_DEGREE) = TetraNodeCounts(1:MAX_TETRA_DEGREE)
2128
VTKTetraFaceMap(1,:) = (/ 1,2,4 /)
2129
VTKTetraFaceMap(2,:) = (/ 3,4,2 /)
2130
VTKTetraFaceMap(3,:) = (/ 1,4,3 /)
2131
VTKTetraFaceMap(4,:) = (/ 1,3,2 /)
2132
2133
DO j=3,MAX_TETRA_DEGREE
2134
SELECT CASE(j)
2135
CASE(3)
2136
i_start = 10
2137
i_end = 10
2138
CASE(4)
2139
i_start = 13
2140
i_end = 15
2141
CASE DEFAULT
2142
CALL Fatal(Caller, 'Tetra face nodes supported up to degree 4')
2143
END SELECT
2144
n = i_end - i_start + 1 ! The number of new nodes per face
2145
2146
DO k=1,4
2147
DO i=1,n
2148
! Pick the positions of face nodes from a lower dimensional element
2149
r(1) = VTKTriangleU(j,i+i_start-1)
2150
r(2) = VTKTriangleV(j,i+i_start-1)
2151
ut = 0.0d0
2152
vt = 0.0d0
2153
wt = 0.0d0
2154
DO t=1,3
2155
PBasis(1) = TriangleNodalPBasis(t, r(1), r(2))
2156
ut = ut + PBasis(1) * VTKTetraU(1,VTKTetraFaceMap(k,t))
2157
vt = vt + PBasis(1) * VTKTetraV(1,VTKTetraFaceMap(k,t))
2158
wt = wt + PBasis(1) * VTKTetraW(1,VTKTetraFaceMap(k,t))
2159
END DO
2160
VTKTetraU(j,i+Offsets(j)) = ut
2161
VTKTetraV(j,i+Offsets(j)) = vt
2162
VTKTetraW(j,i+Offsets(j)) = wt
2163
TetraNodeCounts(j) = TetraNodeCounts(j) + 1
2164
END DO
2165
Offsets(j) = TetraNodeCounts(j)
2166
END DO
2167
END DO
2168
2169
! Bubbles:
2170
!DO j=4,MAX_TETRA_DEGREE
2171
r(1) = SUM(VTKTetraU(1,1:4))/4
2172
r(2) = SUM(VTKTetraV(1,1:4))/4
2173
r(3) = SUM(VTKTetraW(1,1:4))/4
2174
VTKTetraU(4,1+Offsets(4)) = r(1)
2175
VTKTetraV(4,1+Offsets(4)) = r(2)
2176
VTKTetraW(4,1+Offsets(4)) = r(3)
2177
TetraNodeCounts(4) = TetraNodeCounts(4) + 1
2178
!END DO
2179
2180
! --------------------------------------------------------------------------
2181
! Create the nodes for pyramids:
2182
! --------------------------------------------------------------------------
2183
LElement % Type => GetElementType(605, .FALSE.)
2184
CALL GetRefPElementNodes(LElement % Type, VTKPyramidU(1,1:5), &
2185
VTKPyramidV(1,1:5), VTKPyramidW(1,1:5))
2186
2187
DO k=2,MAX_PYRAMID_DEGREE
2188
VTKPyramidU(k,1:5) = VTKPyramidU(1,1:5)
2189
VTKPyramidV(k,1:5) = VTKPyramidV(1,1:5)
2190
VTKPyramidW(k,1:5) = VTKPyramidW(1,1:5)
2191
END DO
2192
2193
! The edges:
2194
PyramidNodeCounts = 5
2195
DO k=1,8
2196
Edge = GetPyramidEdgeMap(k)
2197
Offsets(1:MAX_PYRAMID_DEGREE) = PyramidNodeCounts(1:MAX_PYRAMID_DEGREE)
2198
CALL NodesOnEdge(VTKPyramidU, VTKPyramidV, Edge(1), Edge(2), MAX_PYRAMID_DEGREE, &
2199
Offsets, PyramidNodeCounts, VTKPyramidW)
2200
END DO
2201
Offsets(1:MAX_PYRAMID_DEGREE) = PyramidNodeCounts(1:MAX_PYRAMID_DEGREE)
2202
2203
! The quad face:
2204
Face = GetPyramidFaceMap(1)
2205
DO j=2,MAX_PYRAMID_DEGREE
2206
SELECT CASE(j)
2207
CASE(2)
2208
i_start = 9
2209
i_end = 9
2210
CASE(3)
2211
i_start = 13
2212
i_end = 16
2213
CASE DEFAULT
2214
CALL Fatal(Caller, 'Pyramid (quad) face nodes supported up to degree 3')
2215
END SELECT
2216
n = i_end - i_start + 1 ! The number of new nodes per face
2217
2218
DO i=1,n
2219
! Pick the positions of face nodes from a lower dimensional element
2220
r(1) = VTKQuadU(j,i+i_start-1)
2221
r(2) = VTKQuadV(j,i+i_start-1)
2222
ut = 0.0d0
2223
vt = 0.0d0
2224
wt = 0.0d0
2225
DO t=1,4
2226
PBasis(1) = QuadNodalPBasis(t, r(1), r(2))
2227
ut = ut + PBasis(1) * VTKPyramidU(1,Face(t))
2228
vt = vt + PBasis(1) * VTKPyramidV(1,Face(t))
2229
wt = wt + PBasis(1) * VTKPyramidW(1,Face(t))
2230
END DO
2231
VTKPyramidU(j,i+Offsets(j)) = ut
2232
VTKPyramidV(j,i+Offsets(j)) = vt
2233
VTKPyramidW(j,i+Offsets(j)) = wt
2234
PyramidNodeCounts(j) = PyramidNodeCounts(j) + 1
2235
END DO
2236
Offsets(j) = PyramidNodeCounts(j)
2237
END DO
2238
2239
! The triangular faces (TO DO: support for degrees p > 3):
2240
2241
! The bubbles (TO DO: support for degrees p > 2):
2242
r(1) = SUM(VTKPyramidU(1,1:5))/5
2243
r(2) = SUM(VTKPyramidV(1,1:5))/5
2244
r(3) = SUM(VTKPyramidW(1,1:5))/5
2245
VTKPyramidU(2,1+Offsets(2)) = r(1)
2246
VTKPyramidV(2,1+Offsets(2)) = r(2)
2247
VTKPyramidW(2,1+Offsets(2)) = r(3)
2248
PyramidNodeCounts(2) = PyramidNodeCounts(2) + 1
2249
2250
! --------------------------------------------------------------------------
2251
! Create the nodes for prisms:
2252
! --------------------------------------------------------------------------
2253
LElement % Type => GetElementType(706, .FALSE.)
2254
CALL GetRefPElementNodes(LElement % Type, VTKPrismU(1,1:6), &
2255
VTKPrismV(1,1:6), VTKPrismW(1,1:6))
2256
2257
DO k=2,MAX_PRISM_DEGREE
2258
VTKPrismU(k,1:6) = VTKPrismU(1,1:6)
2259
VTKPrismV(k,1:6) = VTKPrismV(1,1:6)
2260
VTKPrismW(k,1:6) = VTKPrismW(1,1:6)
2261
END DO
2262
2263
! The edges:
2264
PrismNodeCounts = 6
2265
DO k=1,9
2266
Edge = GetWedgeEdgeMap(k)
2267
Offsets(1:MAX_PRISM_DEGREE) = PrismNodeCounts(1:MAX_PRISM_DEGREE)
2268
CALL NodesOnEdge(VTKPrismU, VTKPrismV, Edge(1), Edge(2), MAX_PRISM_DEGREE, &
2269
Offsets, PrismNodeCounts, VTKPrismW)
2270
END DO
2271
Offsets(1:MAX_PRISM_DEGREE) = PrismNodeCounts(1:MAX_PRISM_DEGREE)
2272
2273
! The triangular faces:
2274
DO j=3,MAX_PRISM_DEGREE
2275
SELECT CASE(j)
2276
CASE(3)
2277
i_start = 10
2278
i_end = 10
2279
CASE(4)
2280
i_start = 13
2281
i_end = 15
2282
CASE DEFAULT
2283
CALL Fatal(Caller, 'Prism (triangular) face nodes supported up to degree 4')
2284
END SELECT
2285
n = i_end - i_start + 1 ! The number of new nodes per face
2286
DO k=1,2
2287
Face = GetWedgeFaceMap(k)
2288
DO i=1,n
2289
! Pick the positions of face nodes from a lower dimensional element
2290
r(1) = VTKTriangleU(j,i+i_start-1)
2291
r(2) = VTKTriangleV(j,i+i_start-1)
2292
ut = 0.0d0
2293
vt = 0.0d0
2294
wt = 0.0d0
2295
DO t=1,3
2296
PBasis(1) = TriangleNodalPBasis(t, r(1), r(2))
2297
ut = ut + PBasis(1) * VTKPrismU(1,Face(t))
2298
vt = vt + PBasis(1) * VTKPrismV(1,Face(t))
2299
wt = wt + PBasis(1) * VTKPrismW(1,Face(t))
2300
END DO
2301
VTKPrismU(j,i+Offsets(j)) = ut
2302
VTKPrismV(j,i+Offsets(j)) = vt
2303
VTKPrismW(j,i+Offsets(j)) = wt
2304
PrismNodeCounts(j) = PrismNodeCounts(j) + 1
2305
END DO
2306
Offsets(j) = PrismNodeCounts(j)
2307
END DO
2308
END DO
2309
2310
! The quad faces:
2311
DO j=2,MAX_PRISM_DEGREE
2312
i_start = 4*j + 1
2313
i_end = (j+1)**2
2314
n = (j-1)**2 ! The number of new nodes per face n = i_end - i_start + 1
2315
2316
DO k=3,5
2317
Face = GetWedgeFaceMap(k)
2318
DO i=1,n
2319
! Pick the positions of face nodes from a lower dimensional element
2320
r(1) = VTKQuadU(j,i+i_start-1)
2321
r(2) = VTKQuadV(j,i+i_start-1)
2322
ut = 0.0d0
2323
vt = 0.0d0
2324
wt = 0.0d0
2325
DO t=1,4
2326
PBasis(1) = QuadNodalPBasis(t, r(1), r(2))
2327
ut = ut + PBasis(1) * VTKPrismU(1,Face(t))
2328
vt = vt + PBasis(1) * VTKPrismV(1,Face(t))
2329
wt = wt + PBasis(1) * VTKPrismW(1,Face(t))
2330
END DO
2331
VTKPrismU(j,i+Offsets(j)) = ut
2332
VTKPrismV(j,i+Offsets(j)) = vt
2333
VTKPrismW(j,i+Offsets(j)) = wt
2334
PrismNodeCounts(j) = PrismNodeCounts(j) + 1
2335
END DO
2336
Offsets(j) = PrismNodeCounts(j)
2337
END DO
2338
END DO
2339
2340
DO j=3,MAX_PRISM_DEGREE
2341
i_start = 3*j + 1
2342
n = (j-1)*(j-2)/2
2343
i_end = 3*j + n
2344
DO i=1,j-1
2345
VTKPrismU(j,Offsets(j)+1:Offsets(j)+n) = VTKTriangleU(j,i_start:i_end)
2346
VTKPrismV(j,Offsets(j)+1:Offsets(j)+n) = VTKTriangleV(j,i_start:i_end)
2347
VTKPrismW(j,Offsets(j)+1:Offsets(j)+n) = VTKLineU(j,2+i)
2348
PrismNodeCounts(j) = PrismNodeCounts(j) + n
2349
Offsets(j) = PrismNodeCounts(j)
2350
END DO
2351
END DO
2352
2353
! --------------------------------------------------------------------------
2354
! Create the nodes for hexahedra:
2355
! --------------------------------------------------------------------------
2356
LElement % Type => GetElementType(808, .FALSE.)
2357
CALL GetRefPElementNodes(LElement % Type, VTKBrickU(1,1:8), &
2358
VTKBrickV(1,1:8), VTKBrickW(1,1:8))
2359
2360
DO k=2,MAX_BRICK_DEGREE
2361
VTKBrickU(k,1:8) = VTKBrickU(1,1:8)
2362
VTKBrickV(k,1:8) = VTKBrickV(1,1:8)
2363
VTKBrickW(k,1:8) = VTKBrickW(1,1:8)
2364
END DO
2365
BrickNodeCounts = 8
2366
2367
! The edges:
2368
! It seems that VTK cell types 72 and 12/29 are not interchangeable.
2369
! We first pick the edge map which works for 72 and then re-run
2370
! the same code up to the degree 2 with a different edge map to obtain consistency.
2371
DO k=1,12
2372
! The following is needed for 72:
2373
SELECT CASE(k)
2374
CASE(11)
2375
Edge = GetBrickEdgeMap(12)
2376
CASE(12)
2377
Edge = GetBrickEdgeMap(11)
2378
CASE DEFAULT
2379
Edge = GetBrickEdgeMap(k)
2380
END SELECT
2381
Offsets(1:MAX_BRICK_DEGREE) = BrickNodeCounts(1:MAX_BRICK_DEGREE)
2382
CALL NodesOnEdge(VTKBrickU, VTKBrickV, Edge(1), Edge(2), MAX_BRICK_DEGREE, &
2383
Offsets, BrickNodeCounts, VTKBrickW)
2384
END DO
2385
! The re-run needs a reset:
2386
BrickNodeCounts(2) = 8
2387
DO k=1,12
2388
! The following is needed for 29:
2389
Edge = GetBrickEdgeMap(k)
2390
Offsets(2) = BrickNodeCounts(2)
2391
CALL NodesOnEdge(VTKBrickU, VTKBrickV, Edge(1), Edge(2), 2, &
2392
Offsets, BrickNodeCounts, VTKBrickW)
2393
END DO
2394
2395
! The faces:
2396
Offsets(1:MAX_BRICK_DEGREE) = BrickNodeCounts(1:MAX_BRICK_DEGREE)
2397
VTKBrickFaceMap(1,:) = (/ 1,4,8,5 /)
2398
VTKBrickFaceMap(2,:) = (/ 2,3,7,6 /)
2399
VTKBrickFaceMap(3,:) = (/ 1,2,6,5 /)
2400
VTKBrickFaceMap(4,:) = (/ 4,3,7,8 /)
2401
VTKBrickFaceMap(5,:) = (/ 1,2,3,4 /)
2402
VTKBrickFaceMap(6,:) = (/ 5,6,7,8 /)
2403
2404
DO j=2,MAX_BRICK_DEGREE
2405
i_start = 4*j + 1
2406
i_end = (j+1)**2
2407
n = (j-1)**2 ! The number of new nodes per face n = i_end - i_start + 1
2408
2409
DO k=1,6
2410
DO i=1,n
2411
! Pick the positions of face nodes from a lower dimensional element
2412
r(1) = VTKQuadU(j,i+i_start-1)
2413
r(2) = VTKQuadV(j,i+i_start-1)
2414
ut = 0.0d0
2415
vt = 0.0d0
2416
wt = 0.0d0
2417
DO t=1,4
2418
PBasis(1) = QuadNodalPBasis(t, r(1), r(2))
2419
ut = ut + PBasis(1) * VTKBrickU(1,VTKBrickFaceMap(k,t))
2420
vt = vt + PBasis(1) * VTKBrickV(1,VTKBrickFaceMap(k,t))
2421
wt = wt + PBasis(1) * VTKBrickW(1,VTKBrickFaceMap(k,t))
2422
END DO
2423
VTKBrickU(j,i+Offsets(j)) = ut
2424
VTKBrickV(j,i+Offsets(j)) = vt
2425
VTKBrickW(j,i+Offsets(j)) = wt
2426
BrickNodeCounts(j) = BrickNodeCounts(j) + 1
2427
END DO
2428
Offsets(j) = BrickNodeCounts(j)
2429
END DO
2430
END DO
2431
2432
! Bubbles:
2433
VTKBrickU(2,1+Offsets(2)) = 0.0d0
2434
VTKBrickV(2,1+Offsets(2)) = 0.0d0
2435
VTKBrickW(2,1+Offsets(2)) = 0.0d0
2436
BrickNodeCounts(2) = BrickNodeCounts(2) + 1
2437
2438
DO j=3,MAX_BRICK_DEGREE
2439
i_start = 4*j + 1
2440
i_end = (j+1)**2
2441
n = (j-1)**2
2442
DO i=1,j-1
2443
VTKBrickU(j,Offsets(j)+1:Offsets(j)+n) = VTKQuadU(j,i_start:i_end)
2444
VTKBrickV(j,Offsets(j)+1:Offsets(j)+n) = VTKQuadV(j,i_start:i_end)
2445
VTKBrickW(j,Offsets(j)+1:Offsets(j)+n) = VTKLineU(j,2+i)
2446
BrickNodeCounts(j) = BrickNodeCounts(j) + n
2447
Offsets(j) = BrickNodeCounts(j)
2448
END DO
2449
END DO
2450
AllocationsDone = .TRUE.
2451
END IF
2452
2453
IF (PRESENT(PSolver)) THEN
2454
PVersion = IsActivePElement(PElement, PSolver)
2455
ELSE
2456
PVersion = IsPElement(PElement)
2457
END IF
2458
2459
IF (.NOT. PVersion) THEN
2460
CALL Warn(Caller, 'The input element is not a p-element, returning')
2461
RETURN
2462
END IF
2463
2464
PNodes % x(:) = 0.0d0
2465
PNodes % y(:) = 0.0d0
2466
PNodes % z(:) = 0.0d0
2467
!
2468
! NOTE: We shall not need the derivatives of basis functions or
2469
! the determinant of the element mapping. Hence using the following
2470
! nodal coordinates is sufficient, although in principle we
2471
! might disregard some values
2472
!
2473
n = PElement % TYPE % NumberOfNodes
2474
PNodes % x(1:n) = Mesh % Nodes % x(PElement % NodeIndexes(1:n))
2475
PNodes % y(1:n) = Mesh % Nodes % y(PElement % NodeIndexes(1:n))
2476
PNodes % z(1:n) = Mesh % Nodes % z(PElement % NodeIndexes(1:n))
2477
2478
SELECT CASE(Family)
2479
CASE(2)
2480
IF (Degree > MAX_LINE_DEGREE) THEN
2481
CALL Fatal(Caller, 'Lagrange Element Degree (lines) exceeds the supported maximum')
2482
END IF
2483
n = Degree + 1
2484
NodesU => VTKLineU(Degree,1:n)
2485
NodesV => VTKLineU(MAX_LINE_DEGREE+1,1:n)
2486
NodesW => VTKLineU(MAX_LINE_DEGREE+1,1:n)
2487
2488
CASE(3)
2489
IF (Degree > MAX_TRIANGLE_DEGREE) THEN
2490
CALL Fatal(Caller, 'Lagrange Element Degree (triangles) exceeds the supported maximum')
2491
END IF
2492
n = TriangleNodeCounts(Degree)
2493
NodesU => VTKTriangleU(Degree,1:n)
2494
NodesV => VTKTriangleV(Degree,1:n)
2495
NodesW => VTKTriangleW(1:n)
2496
2497
CASE(4)
2498
IF (Degree > MAX_QUAD_DEGREE) THEN
2499
CALL Fatal(Caller, 'Lagrange Element Degree (quads) exceeds the supported maximum')
2500
END IF
2501
n = QuadNodeCounts(Degree)
2502
NodesU => VTKQuadU(Degree,1:n)
2503
NodesV => VTKQuadV(Degree,1:n)
2504
NodesW => VTKQuadW(1:n)
2505
2506
CASE(5)
2507
IF (Degree > MAX_TETRA_DEGREE) THEN
2508
CALL Fatal(Caller, 'Lagrange Element Degree (tetrahedra) exceeds the supported maximum')
2509
END IF
2510
n = TetraNodeCounts(Degree)
2511
NodesU => VTKTetraU(Degree,1:n)
2512
NodesV => VTKTetraV(Degree,1:n)
2513
NodesW => VTKTetraW(Degree,1:n)
2514
2515
CASE(6)
2516
IF (Degree > MAX_PYRAMID_DEGREE) THEN
2517
CALL Fatal(Caller, 'Lagrange Element Degree (pyramids) exceeds the supported maximum')
2518
END IF
2519
n = PyramidNodeCounts(Degree)
2520
NodesU => VTKPyramidU(Degree,1:n)
2521
NodesV => VTKPyramidV(Degree,1:n)
2522
NodesW => VTKPyramidW(Degree,1:n)
2523
2524
CASE(7)
2525
IF (Degree > MAX_PRISM_DEGREE) THEN
2526
CALL Fatal(Caller, 'Lagrange Element Degree (prisms) exceeds the supported maximum')
2527
END IF
2528
n = PrismNodeCounts(Degree)
2529
NodesU => VTKPrismU(Degree,1:n)
2530
NodesV => VTKPrismV(Degree,1:n)
2531
NodesW => VTKPrismW(Degree,1:n)
2532
2533
CASE(8)
2534
IF (Degree > MAX_BRICK_DEGREE) THEN
2535
CALL Fatal(Caller, 'Lagrange Element Degree (bricks) exceeds the supported maximum')
2536
END IF
2537
n = BrickNodeCounts(Degree)
2538
NodesU => VTKBrickU(Degree,1:n)
2539
NodesV => VTKBrickV(Degree,1:n)
2540
NodesW => VTKBrickW(Degree,1:n)
2541
2542
CASE DEFAULT
2543
CALL Fatal(Caller, 'An unknown element family')
2544
END SELECT
2545
2546
BasisDegree(:) = 0
2547
2548
DO t = 1,n
2549
ut = NodesU(t)
2550
vt = NodesV(t)
2551
wt = NodesW(t)
2552
2553
!PRINT *, 'CALLING AT POINT ', t,ut,vt,wt
2554
2555
! It seems that the p-basis for pyramids cannot be evaluated at the fifth node,
2556
! so we make a small "error":
2557
IF (Family == 6 .AND. t==5) wt = wt - 1.0d-8
2558
2559
stat = ElementInfo(PElement, PNodes, ut, vt, wt, detJ, PBasis, BasisDegree=BasisDegree, &
2560
USolver=PSolver)
2561
IF (t == 1) THEN
2562
nd = COUNT(BasisDegree > 0)
2563
IF (nd == 0) CALL Fatal(Caller, 'p-basis needed but the classical basis returned')
2564
! PRINT *, 'p-basis dimension ', nd, SUM( PBasis(1:nd)), SUM(PBasis(1:PElement % Type % NumberOfNodes))
2565
END IF
2566
2567
DO k = 1,Fields
2568
LSol(k,t) = SUM(PBasis(1:nd) * PSol(k,1:nd))
2569
END DO
2570
END DO
2571
2572
CONTAINS
2573
2574
! --------------------------------------------------------------------------------
2575
! Given the vertices of a 2-node edge in the first rows of the coordinate arrays
2576
! NodesU, NodesV and NodesW, create additional nodes corresponding to the Lagrange
2577
! interpolation up to the polynomial degree Maxdegree. The edge is defined by
2578
! specifying its start and end indices and is assumed to be of length 2.
2579
! The array Offsets gives the node counts before adding new nodes on the edge,
2580
! while the array NodeCounts gives the counts after this action.
2581
! --------------------------------------------------------------------------------
2582
SUBROUTINE NodesOnEdge(NodesU, NodesV, ind_start, ind_end, MaxDegree, Offsets, &
2583
NodeCounts, NodesW)
2584
2585
IMPLICIT NONE
2586
REAL(KIND=dp), INTENT(INOUT):: NodesU(:,:), NodesV(:,:)
2587
INTEGER, INTENT(IN) :: ind_start, ind_end
2588
INTEGER, INTENT(IN) :: MaxDegree
2589
INTEGER, INTENT(IN) :: OffSets(:)
2590
INTEGER, INTENT(INOUT) :: NodeCounts(:)
2591
REAL(KIND=dp), OPTIONAL, INTENT(INOUT):: NodesW(:,:)
2592
! --------------------------------------------------------------
2593
REAL(KIND=dp), PARAMETER :: L = 2.0_dp
2594
LOGICAL :: CreateW
2595
INTEGER :: i, k
2596
REAL(KIND=dp) :: r(3), ut, vt, wt, delta
2597
! --------------------------------------------------------------
2598
CreateW = PRESENT(NodesW)
2599
r(1) = NodesU(1,ind_end) - NodesU(1,ind_start)
2600
r(2) = NodesV(1,ind_end) - NodesV(1,ind_start)
2601
IF (CreateW) THEN
2602
r(3) = NodesW(1,ind_end) - NodesW(1,ind_start)
2603
k = 3
2604
ELSE
2605
k = 2
2606
END IF
2607
r(1:k) = r(1:k)/SQRT(SUM(r(1:k)**2))
2608
2609
DO k=2,MaxDegree
2610
delta = L/k
2611
ut = NodesU(1,ind_start)
2612
vt = NodesV(1,ind_start)
2613
IF (CreateW) wt = NodesW(1,ind_start)
2614
DO i=1,k-1
2615
ut = ut + delta * r(1)
2616
vt = vt + delta * r(2)
2617
NodesU(k,i+Offsets(k)) = ut
2618
NodesV(k,i+Offsets(k)) = vt
2619
IF (CreateW) THEN
2620
wt = wt + delta * r(3)
2621
NodesW(k,i+Offsets(k)) = wt
2622
END IF
2623
NodeCounts(k) = NodeCounts(k) + 1
2624
END DO
2625
END DO
2626
END SUBROUTINE NodesOnEdge
2627
2628
!------------------------------------------------------------------------------
2629
END SUBROUTINE HierarchicPToLagrange
2630
!------------------------------------------------------------------------------
2631
2632