Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/LumpingUtils.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
! * Generic utilities related to components
27
! *
28
! ******************************************************************************
29
! *
30
! * Authors: Juha Ruokolainen
31
! * Email: [email protected]
32
! * Web: http://www.csc.fi/elmer
33
! * Address: CSC - IT Center for Science Ltd.
34
! * Keilaranta 14
35
! * 02101 Espoo, Finland
36
! *
37
! * Original Date: 28 Sep 1998
38
! *
39
! *****************************************************************************/
40
41
!> Generic utilities related to components
42
!------------------------------------------------------------------------------
43
44
!> \ingroup ElmerLib
45
!> \{
46
47
48
MODULE LumpingUtils
49
50
USE Lists
51
USE ElementUtils
52
USE ElementDescription
53
USE ParallelUtils
54
55
IMPLICIT NONE
56
57
CONTAINS
58
59
60
!------------------------------------------------------------------------------
61
!> Compute reduction operators for a given component with given nodal vector field.
62
!> Force is simple sum of nodal forces
63
!> Moment is moment of nodal forces about a given center point
64
!> Torque is moment of nodal forces about a given rotational axis
65
!> If the given field is a elemental (DG) field it may be reduced by
66
!> optional SetPerm reordering for minimal discontinuous set.
67
!------------------------------------------------------------------------------
68
SUBROUTINE ComponentNodalForceReduction(Model, Mesh, CompParams, NF, &
69
Force, Moment, Torque, SetPerm )
70
!------------------------------------------------------------------------------
71
TYPE(Model_t) :: Model
72
TYPE(Mesh_t), POINTER :: Mesh
73
TYPE(ValueList_t), POINTER :: CompParams
74
TYPE(Variable_t), POINTER :: NF
75
REAL(KIND=dp), OPTIONAL :: Moment(3), Force(3), Torque
76
INTEGER, POINTER, OPTIONAL :: SetPerm(:)
77
!------------------------------------------------------------------------------
78
! Local variables
79
!------------------------------------------------------------------------------
80
TYPE(Element_t), POINTER :: Element
81
TYPE(Nodes_t) :: Nodes
82
LOGICAL, ALLOCATABLE :: VisitedNode(:)
83
REAL(KIND=dp) :: Origin(3), Axis(3), P(3), F(3), v1(3), v2(3), &
84
RotorRadius, rad, minrad, maxrad, eps
85
REAL(KIND=dp), POINTER :: Pwrk(:,:)
86
INTEGER :: t, i, j, k, n, dofs, globalnode, AirBody
87
LOGICAL :: ElementalVar, Found, NeedLocation
88
INTEGER, POINTER :: MasterEntities(:),NodeIndexes(:),DofIndexes(:)
89
LOGICAL :: VisitNodeOnlyOnce
90
INTEGER :: FirstElem, LastElem
91
LOGICAL :: BcMode, BulkMode, RotorMode, isParallel
92
CHARACTER(*), PARAMETER :: Caller = 'ComponentNodalForceReduction'
93
94
CALL Info(Caller,'Performing reduction for component: '&
95
//TRIM(ListGetString(CompParams,'Name')),Level=10)
96
97
IF(.NOT. (PRESENT(Torque) .OR. PRESENT(Moment) .OR. PRESENT(Force) ) ) THEN
98
CALL Warn(Caller,'Nothing to compute!')
99
RETURN
100
END IF
101
102
IF( PRESENT(Torque)) Torque = 0.0_dp
103
IF( PRESENT(Moment)) Moment = 0.0_dp
104
IF( PRESENT(Force)) Force = 0.0_dp
105
106
isParallel = CurrentModel % Solver % Parallel
107
108
109
eps = 1.0e-6
110
BcMode = .FALSE.
111
BulkMode = .FALSE.
112
RotorMode = ListGetLogical( CompParams,'Rotor Mode',Found )
113
IF( RotorMode ) THEN
114
RotorRadius = ListGetConstReal( CurrentModel % Simulation,'Rotor Radius',Found )
115
IF(.NOT. Found ) THEN
116
CALL Fatal(Caller,'"Rotor Mode" requires "Rotor Radius"')
117
END IF
118
ELSE
119
MasterEntities => ListGetIntegerArray( CompParams,'Master Bodies',BulkMode )
120
IF( .NOT. BulkMode ) THEN
121
MasterEntities => ListGetIntegerArray( CompParams,'Master Boundaries', BCMode)
122
END IF
123
IF(.NOT. (BulkMode .OR. BCMode ) ) THEN
124
CALL Warn(Caller,'> Master Bodies < or > Master Boundaries < not given')
125
RETURN
126
END IF
127
END IF
128
129
NeedLocation = PRESENT( Moment ) .OR. PRESENT( Torque )
130
131
! User may specific origin and axis for torque computation
132
! By default (0,0,0) is the origin, and (0,0,1) the axis.
133
Pwrk => ListGetConstRealArray( CompParams,'Torque Origin',Found )
134
IF( Found ) THEN
135
IF( SIZE(Pwrk,1) /= 3 .OR. SIZE(Pwrk,2) /= 1 ) THEN
136
CALL Fatal(Caller,'Size of > Torque Origin < should be 3!')
137
END IF
138
Origin = Pwrk(1:3,1)
139
ELSE
140
Origin = 0.0_dp
141
END IF
142
Pwrk => ListGetConstRealArray( CompParams,'Torque Axis',Found )
143
IF( Found ) THEN
144
IF( SIZE(Pwrk,1) /= 3 .OR. SIZE(Pwrk,2) /= 1 ) THEN
145
CALL Fatal(Caller,'Size of > Torque Axis < should be 3!')
146
END IF
147
Axis = Pwrk(1:3,1)
148
! Normalize axis is it should just be used for the direction
149
Axis = Axis / SQRT( SUM( Axis*Axis ) )
150
ELSE
151
Axis = 0.0_dp
152
Axis(3) = 1.0_dp
153
END IF
154
155
ElementalVar = ( NF % TYPE == Variable_on_nodes_on_elements )
156
IF( PRESENT( SetPerm ) .AND. .NOT. ElementalVar ) THEN
157
CALL Fatal(Caller,'SetPerm is usable only for elemental fields')
158
END IF
159
160
dofs = NF % Dofs
161
IF( dofs == 2 ) F(3) = 0.0_dp
162
163
! For nodal field compute only once each node
164
! For DG field each node is visited only once by construction
165
VisitNodeOnlyOnce = .NOT. ElementalVar .OR. PRESENT(SetPerm)
166
IF( VisitNodeOnlyOnce ) THEN
167
IF( PRESENT( SetPerm ) ) THEN
168
n = MAXVAL( SetPerm )
169
ELSE
170
n = Mesh % NumberOfNodes
171
END IF
172
ALLOCATE(VisitedNode( n ) )
173
VisitedNode = .FALSE.
174
END IF
175
176
IF( BcMode ) THEN
177
FirstElem = Mesh % NumberOfBulkElements + 1
178
LastElem = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
179
ELSE
180
FirstElem = 1
181
LastElem = Mesh % NumberOfBulkElements
182
END IF
183
184
! This is a special reduction that only applies to rotors that are surrounded by airgap.
185
! Very special operator for electrical machines that removes the bookkeeping of the bodies
186
! that constitute the rotor.
187
AirBody = 0
188
IF(RotorMode ) THEN
189
AirBody = ListGetInteger( CompParams,'Air Body',Found )
190
IF(AirBody == 0) THEN
191
DO t=FirstElem,LastElem
192
Element => Mesh % Elements(t)
193
n = Element % TYPE % NumberOfNodes
194
CALL CopyElementNodesFromMesh( Nodes, Mesh, n, Element % NodeIndexes)
195
DO i=1,n
196
rad = SQRT(Nodes % x(i)**2 + Nodes % y(i)**2)
197
IF(i==1) THEN
198
minrad = rad
199
maxrad = rad
200
ELSE
201
minrad = MIN(minrad,rad)
202
maxrad = MAX(maxrad,rad)
203
END IF
204
END DO
205
206
! The body is defined by an element that is at and inside the rotor radius.
207
IF(ABS(maxrad-RotorRadius) < eps .AND. minrad < RotorRadius*(1-eps) ) THEN
208
AirBody = Element % BodyId
209
EXIT
210
END IF
211
END DO
212
AirBody = ParallelReduction(AirBody,2)
213
CALL Info(Caller,'Airgap inner body determined to be: '//I2S(AirBody),Level=12)
214
IF(AirBody==0) THEN
215
CALL Fatal(Caller,'Could not define airgap inner body!')
216
ELSE
217
CALL ListAddInteger(CompParams,'Air Body',AirBody)
218
END IF
219
END IF
220
END IF
221
222
223
DO t=FirstElem,LastElem
224
Element => Mesh % Elements(t)
225
226
IF( BcMode ) THEN
227
IF( ALL( MasterEntities /= Element % BoundaryInfo % Constraint ) ) CYCLE
228
ELSE IF( BulkMode ) THEN
229
IF( ALL( MasterEntities /= Element % BodyId ) ) CYCLE
230
ELSE IF( RotorMode ) THEN
231
IF( Element % BodyId == AirBody ) CYCLE
232
CALL CopyElementNodesFromMesh( Nodes, Mesh, n, Element % NodeIndexes)
233
rad = SQRT((SUM(Nodes % x(1:n))/n)**2 + (SUM(Nodes % y(1:n))/n)**2)
234
IF(rad > RotorRadius ) CYCLE
235
END IF
236
237
n = Element % TYPE % NumberOfNodes
238
NodeIndexes => Element % NodeIndexes
239
IF( ElementalVar ) THEN
240
DofIndexes => Element % DGIndexes
241
ELSE
242
DofIndexes => NodeIndexes
243
END IF
244
245
DO i=1,n
246
j = DofIndexes(i)
247
k = NF % Perm(j)
248
IF( k == 0 ) CYCLE
249
250
IF( VisitNodeOnlyOnce ) THEN
251
IF( PRESENT( SetPerm ) ) j = SetPerm(j)
252
IF( VisitedNode(j) ) CYCLE
253
VisitedNode(j) = .TRUE.
254
END IF
255
256
globalnode = NodeIndexes(i)
257
258
! Only compute the parallel reduction once
259
IF( isParallel ) THEN
260
IF( Element % PartIndex /= ParEnv % MyPe ) CYCLE
261
262
! This is (probably) not correct, the "nodal forces"-array is partial and should be summed --> comment out.
263
! IF( VisitNodeOnlyOnce ) THEN
264
! IF( Mesh % ParallelInfo % NeighbourList(globalnode) % Neighbours(1) /= ParEnv % MyPE ) CYCLE
265
! END IF
266
END IF
267
268
F(1) = NF % Values( dofs*(k-1) + 1)
269
F(2) = NF % Values( dofs*(k-1) + 2)
270
IF( dofs == 3 ) THEN
271
F(3) = NF % Values( dofs*(k-1) + 3)
272
END IF
273
274
IF( PRESENT( Force ) ) THEN
275
! calculate simple sum
276
Force = Force + F
277
END IF
278
279
IF( NeedLocation ) THEN
280
P(1) = Mesh % Nodes % x(globalnode)
281
P(2) = Mesh % Nodes % y(globalnode)
282
P(3) = Mesh % Nodes % z(globalnode)
283
284
v1 = P - Origin
285
286
! Calculate moment
287
IF( PRESENT( Moment ) ) THEN
288
Moment = Moment + CrossProduct(v1,F)
289
END IF
290
291
! Calculate torque around an axis
292
IF( PRESENT( Torque ) ) THEN
293
v1 = v1 - SUM(Axis*v1)*Axis
294
v2 = CrossProduct(v1,F)
295
Torque = Torque + SUM(Axis*v2)
296
END IF
297
END IF
298
299
END DO
300
END DO
301
302
IF( isParallel ) THEN
303
IF( PRESENT( Force ) ) THEN
304
DO i=1,3
305
Force(i) = ParallelReduction(Force(i))
306
END DO
307
END IF
308
309
IF( PRESENT( Moment ) ) THEN
310
DO i=1,3
311
Moment(i) = ParallelReduction(Moment(i))
312
END DO
313
END IF
314
315
IF( PRESENT( Torque ) ) THEN
316
Torque = ParallelReduction(Torque)
317
END IF
318
END IF
319
320
!------------------------------------------------------------------------------
321
END SUBROUTINE ComponentNodalForceReduction
322
!------------------------------------------------------------------------------
323
324
325
!------------------------------------------------------------------------------
326
!> Perform reduction from distributed data to components.
327
!> These are similar operations as the stastistical operations in SaveScalars.
328
!------------------------------------------------------------------------------
329
FUNCTION ComponentNodalReduction(Model, Mesh, CompParams, Var, OperName ) RESULT ( OperX )
330
!------------------------------------------------------------------------------
331
TYPE(Model_t) :: Model
332
TYPE(Mesh_t), POINTER :: Mesh
333
TYPE(ValueList_t), POINTER :: CompParams
334
TYPE(Variable_t), POINTER :: Var
335
REAL(KIND=dp) :: OperX
336
CHARACTER(LEN=*) :: OperName
337
!------------------------------------------------------------------------------
338
! Local variables
339
!------------------------------------------------------------------------------
340
TYPE(Element_t), POINTER :: Element
341
LOGICAL, ALLOCATABLE :: VisitedNode(:)
342
INTEGER :: t, i, j, k, l, n, NoDofs, globalnode, sumi
343
REAL(KIND=dp) :: X, Minimum, Maximum, AbsMinimum, AbsMaximum, SumX, SumXX, SumAbsX
344
LOGICAL :: ElementalVar, Found
345
INTEGER, POINTER :: MasterEntities(:),NodeIndexes(:),DofIndexes(:)
346
LOGICAL :: VisitNodeOnlyOnce, Initialized
347
INTEGER :: FirstElem, LastElem
348
LOGICAL :: BcMode
349
350
351
CALL Info('ComponentNodalReduction','Performing reduction for component: '&
352
//TRIM(ListGetString(CompParams,'Name')),Level=10)
353
354
OperX = 0.0_dp
355
356
BcMode = .FALSE.
357
MasterEntities => ListGetIntegerArray( CompParams,'Master Bodies',Found )
358
IF( .NOT. Found ) THEN
359
MasterEntities => ListGetIntegerArray( CompParams,'Master Boundaries',Found )
360
BcMode = .TRUE.
361
END IF
362
363
IF(.NOT. Found ) THEN
364
CALL Warn('ComponentNodalReduction',&
365
'> Master Bodies < or > Master Boundaries < not given')
366
RETURN
367
END IF
368
369
IF( BcMode ) THEN
370
FirstElem = Mesh % NumberOfBulkElements + 1
371
LastElem = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
372
ELSE
373
FirstElem = 1
374
LastElem = Mesh % NumberOfBulkElements
375
END IF
376
377
ElementalVar = ( Var % TYPE == Variable_on_nodes_on_elements )
378
NoDofs = Var % Dofs
379
Initialized = .FALSE.
380
381
! For nodal field compute only once each node
382
! For DG field each node is visited only once by construction
383
VisitNodeOnlyOnce = .NOT. ElementalVar
384
IF( VisitNodeOnlyOnce ) THEN
385
n = Mesh % NumberOfNodes
386
ALLOCATE(VisitedNode( n ) )
387
VisitedNode = .FALSE.
388
END IF
389
390
sumi = 0
391
sumx = 0.0_dp
392
sumxx = 0.0_dp
393
sumabsx = 0.0_dp
394
Maximum = 0.0_dp
395
Minimum = 0.0_dp
396
AbsMaximum = 0.0_dp
397
AbsMinimum = 0.0_dp
398
399
DO t=FirstElem,LastElem
400
Element => Mesh % Elements(t)
401
402
IF( BcMode ) THEN
403
IF( ALL( MasterEntities /= Element % BoundaryInfo % Constraint ) ) CYCLE
404
ELSE
405
IF( ALL( MasterEntities /= Element % BodyId ) ) CYCLE
406
END IF
407
408
n = Element % TYPE % NumberOfNodes
409
NodeIndexes => Element % NodeIndexes
410
IF( ElementalVar ) THEN
411
DofIndexes => Element % DGIndexes
412
ELSE
413
DofIndexes => NodeIndexes
414
END IF
415
416
DO i=1,n
417
j = DofIndexes(i)
418
IF( ASSOCIATED( Var % Perm ) ) THEN
419
k = Var % Perm(j)
420
IF( k == 0 ) CYCLE
421
ELSE
422
k = j
423
END IF
424
425
IF( VisitNodeOnlyOnce ) THEN
426
IF( VisitedNode(j) ) CYCLE
427
VisitedNode(j) = .TRUE.
428
END IF
429
430
globalnode = NodeIndexes(i)
431
432
! Only compute the parallel reduction once
433
IF( ParEnv % PEs > 1 ) THEN
434
IF( Mesh % ParallelInfo % NeighbourList(globalnode) % Neighbours(1) /= ParEnv % MyPE ) CYCLE
435
END IF
436
437
IF(NoDofs <= 1) THEN
438
x = Var % Values(k)
439
ELSE
440
x = 0.0d0
441
DO l=1,NoDofs
442
x = x + Var % Values(NoDofs*(k-1)+l) ** 2
443
END DO
444
x = SQRT(x)
445
END IF
446
447
IF(.NOT. Initialized) THEN
448
Initialized = .TRUE.
449
Maximum = x
450
Minimum = x
451
AbsMaximum = x
452
AbsMinimum = x
453
END IF
454
455
sumi = sumi + 1
456
sumx = sumx + x
457
sumxx = sumxx + x*x
458
sumabsx = sumabsx + ABS( x )
459
Maximum = MAX(x,Maximum)
460
Minimum = MIN(x,Minimum)
461
IF(ABS(x) > ABS(AbsMaximum) ) AbsMaximum = x
462
IF(ABS(x) < ABS(AbsMinimum) ) AbsMinimum = x
463
END DO
464
END DO
465
466
467
sumi = ParallelReduction(sumi)
468
IF( sumi == 0 ) THEN
469
CALL Warn('ComponentNodalReduction','No active nodes to reduced!')
470
RETURN
471
END IF
472
473
474
SELECT CASE(OperName)
475
476
CASE ('sum')
477
sumx = ParallelReduction(sumx)
478
operx = sumx
479
480
CASE ('sum abs')
481
sumx = ParallelReduction(sumabsx)
482
operx = sumabsx
483
484
CASE ('min')
485
minimum = ParallelReduction(minimum,1)
486
operx = Minimum
487
488
CASE ('max')
489
maximum = ParallelReduction(maximum,2)
490
operx = Maximum
491
492
CASE ('min abs')
493
Absminimum = ParallelReduction(AbsMinimum,1)
494
operx = AbsMinimum
495
496
CASE ('max abs')
497
Absmaximum = ParallelReduction(AbsMaximum,2)
498
operx = AbsMaximum
499
500
CASE ('range')
501
minimum = ParallelReduction(minimum,1)
502
maximum = ParallelReduction(maximum,2)
503
operx = Maximum - Minimum
504
505
CASE ('mean')
506
sumx = ParallelReduction(sumx)
507
operx = sumx / sumi
508
509
CASE ('mean abs')
510
sumx = ParallelReduction(sumabsx)
511
operx = sumabsx / sumi
512
513
CASE ('variance')
514
sumx = ParallelReduction(sumx)
515
sumxx = ParallelReduction(sumxx)
516
Operx = SQRT( sumxx/sumi-(sumx*sumx)/(sumi*sumi) )
517
518
CASE DEFAULT
519
CALL Warn('ComponentNodalReduction','Unknown statistical operator!')
520
521
END SELECT
522
523
CALL Info('ComponentNodalReduction','Reduction operator finished',Level=12)
524
525
!------------------------------------------------------------------------------
526
END FUNCTION ComponentNodalReduction
527
!------------------------------------------------------------------------------
528
529
530
!------------------------------------------------------------------------------
531
!> Perform reduction from distributed data to components.
532
!> These are similar operations as the stastistical operations in SaveScalars.
533
!------------------------------------------------------------------------------
534
FUNCTION ComponentIntegralReduction(Model, Mesh, CompParams, Var, &
535
OperName, CoeffName, GotCoeff ) RESULT ( OperX )
536
!------------------------------------------------------------------------------
537
TYPE(Model_t) :: Model
538
TYPE(Mesh_t), POINTER :: Mesh
539
TYPE(ValueList_t), POINTER :: CompParams
540
TYPE(Variable_t), POINTER :: Var
541
CHARACTER(LEN=*) :: OperName, CoeffName
542
LOGICAL :: GotCoeff
543
REAL(KIND=dp) :: OperX
544
!------------------------------------------------------------------------------
545
! Local variables
546
!------------------------------------------------------------------------------
547
TYPE(Element_t), POINTER :: Element
548
INTEGER :: t, i, j, k, n, NoDofs
549
INTEGER, POINTER :: NodeIndexes(:), DofIndexes(:)
550
REAL(KIND=dp) :: SqrtElementMetric,U,V,W,S,x,Grad(3)
551
REAL(KIND=dp) :: func, CoeffAtIp, integral, vol
552
LOGICAL :: ElementalVar, Found, Stat
553
INTEGER :: PermIndexes(Model % MaxElementNodes)
554
REAL(KIND=dp) :: Basis(Model % MaxElementNodes), dBasisdx(Model % MaxElementNodes,3)
555
REAL(KIND=dp) :: Coeff(Model % MaxElementNodes)
556
TYPE(GaussIntegrationPoints_t) :: IntegStuff
557
INTEGER, POINTER :: MasterEntities(:)
558
TYPE(Nodes_t), SAVE :: ElementNodes
559
LOGICAL, SAVE :: AllocationsDone = .FALSE.
560
INTEGER :: FirstElem, LastElem
561
LOGICAL :: BcMode
562
563
564
CALL Info('ComponentIntegralReduction','Performing reduction for component: '&
565
//TRIM(ListGetString(CompParams,'Name')),Level=10)
566
567
OperX = 0.0_dp
568
vol = 0.0_dp
569
integral = 0.0_dp
570
571
BcMode = .FALSE.
572
MasterEntities => ListGetIntegerArray( CompParams,'Master Bodies',Found )
573
IF( .NOT. Found ) THEN
574
MasterEntities => ListGetIntegerArray( CompParams,'Master Boundaries',Found )
575
BcMode = .TRUE.
576
END IF
577
578
IF(.NOT. Found ) THEN
579
CALL Warn('ComponentIntegralReduction',&
580
'> Master Bodies < or > Master Boundaries < not given')
581
RETURN
582
END IF
583
584
IF( BcMode ) THEN
585
FirstElem = Mesh % NumberOfBulkElements + 1
586
LastElem = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
587
ELSE
588
FirstElem = 1
589
LastElem = Mesh % NumberOfBulkElements
590
END IF
591
592
IF(.NOT. AllocationsDone ) THEN
593
n = Model % MaxElementNodes
594
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n) )
595
AllocationsDone = .TRUE.
596
END IF
597
598
ElementalVar = ( Var % TYPE == Variable_on_nodes_on_elements )
599
NoDofs = Var % Dofs
600
CoeffAtIp = 1.0_dp
601
602
DO t=FirstElem,LastElem
603
Element => Mesh % Elements(t)
604
IF( BcMode ) THEN
605
IF( ALL( MasterEntities /= Element % BoundaryInfo % Constraint ) ) CYCLE
606
ELSE
607
IF( ALL( MasterEntities /= Element % BodyId ) ) CYCLE
608
END IF
609
610
n = Element % TYPE % NumberOfNodes
611
NodeIndexes => Element % NodeIndexes
612
IF( ElementalVar ) THEN
613
DofIndexes => Element % DGIndexes
614
ELSE
615
DofIndexes => NodeIndexes
616
END IF
617
618
IF( ASSOCIATED( Var % Perm ) ) THEN
619
PermIndexes = Var % Perm( DofIndexes )
620
ELSE
621
PermIndexes = DofIndexes
622
END IF
623
624
IF ( ANY(PermIndexes == 0 ) ) CYCLE
625
626
ElementNodes % x(1:n) = Mesh % Nodes % x(NodeIndexes(1:n))
627
ElementNodes % y(1:n) = Mesh % Nodes % y(NodeIndexes(1:n))
628
ElementNodes % z(1:n) = Mesh % Nodes % z(NodeIndexes(1:n))
629
630
IF(GotCoeff) THEN
631
k = ListGetInteger( Model % Bodies( Element % BodyId ) % Values, &
632
'Material', Found )
633
Coeff(1:n) = ListGetReal( Model % Materials(k) % Values, &
634
CoeffName, n, NodeIndexes(1:n) )
635
END IF
636
637
!------------------------------------------------------------------------------
638
! Numerical integration
639
!------------------------------------------------------------------------------
640
IntegStuff = GaussPoints( Element )
641
642
DO i=1,IntegStuff % n
643
U = IntegStuff % u(i)
644
V = IntegStuff % v(i)
645
W = IntegStuff % w(i)
646
!------------------------------------------------------------------------------
647
! Basis function values & derivatives at the integration point
648
!------------------------------------------------------------------------------
649
stat = ElementInfo( Element,ElementNodes,U,V,W,SqrtElementMetric, &
650
Basis,dBasisdx )
651
!------------------------------------------------------------------------------
652
! Coordinatesystem dependent info
653
!------------------------------------------------------------------------------
654
s = SqrtElementMetric * IntegStuff % s(i)
655
IF ( CurrentCoordinateSystem() /= Cartesian ) THEN
656
x = 2 * PI * SUM( ElementNodes % x(1:n)*Basis(1:n) )
657
END IF
658
659
IF( GotCoeff ) THEN
660
CoeffAtIp = SUM( Coeff(1:n) * Basis(1:n) )
661
END IF
662
vol = vol + S
663
664
SELECT CASE(OperName)
665
666
CASE ('volume')
667
integral = integral + coeffAtIp * S
668
669
CASE ('int','int mean')
670
func = SUM( Var % Values(PermIndexes(1:n)) * Basis(1:n) )
671
integral = integral + S * coeffAtIp * func
672
673
CASE ('int abs','int abs mean')
674
func = ABS( SUM( Var % Values(PermIndexes(1:n)) * Basis(1:n) ) )
675
integral = integral + S * coeffAtIp * func
676
677
CASE ('diffusive energy')
678
DO j = 1, 3
679
Grad(j) = SUM( dBasisdx(1:n,j) * Var % Values(PermIndexes(1:n) ) )
680
END DO
681
integral = integral + s * CoeffAtIp * SUM( Grad * Grad )
682
683
CASE ('convective energy')
684
func = SUM( Var % Values(PermIndexes(1:n)) * Basis(1:n) )
685
686
IF(NoDofs == 1) THEN
687
func = SUM( Var % Values(PermIndexes(1:n)) * Basis(1:n) )
688
integral = integral + s * coeffAtIp * func**2
689
ELSE
690
func = 0.0d0
691
DO j=1,NoDofs
692
func = SUM( Var % Values(NoDofs*(PermIndexes(1:n)-1)+j) * Basis(1:n) )
693
integral = integral + s * coeffAtIp * func**2
694
END DO
695
END IF
696
697
CASE ('potential energy')
698
func = SUM( Var % Values(PermIndexes(1:n)) * Basis(1:n) )
699
integral = integral + s * coeffAtIp * func
700
701
CASE DEFAULT
702
CALL Warn('ComponentIntegralReduction','Unknown operator')
703
704
END SELECT
705
706
END DO
707
708
END DO
709
710
integral = ParallelReduction( integral )
711
712
SELECT CASE(OperName)
713
714
CASE ('volume')
715
operx = integral
716
717
CASE ('int')
718
operx = integral
719
720
CASE ('int abs')
721
operx = integral
722
723
CASE ('int mean')
724
vol = ParallelReduction( vol )
725
operx = integral / vol
726
727
CASE ('int abs mean')
728
vol = ParallelReduction( vol )
729
operx = integral / vol
730
731
CASE ('diffusive energy')
732
operx = 0.5d0 * integral
733
734
CASE ('convective energy')
735
operx = 0.5d0 * integral
736
737
CASE ('potential energy')
738
operx = integral
739
740
END SELECT
741
742
CALL Info('ComponentIntegralReduction','Reduction operator finished',Level=12)
743
744
!------------------------------------------------------------------------------
745
END FUNCTION ComponentIntegralReduction
746
!------------------------------------------------------------------------------
747
748
749
!------------------------------------------------------------------------------
750
!> Each solver may include a list of dependent components that are updated
751
!> after the solver (or the nonlinear iteration related to it) has been executed.
752
!------------------------------------------------------------------------------
753
SUBROUTINE UpdateDependentComponents( ComponentList )
754
INTEGER, POINTER :: ComponentList(:)
755
756
INTEGER :: i,j,NoVar
757
CHARACTER(:), ALLOCATABLE :: OperName, VarName, CoeffName, TmpOper
758
LOGICAL :: GotVar, GotOper, GotCoeff, VectorResult
759
TYPE(ValueList_t), POINTER :: CompParams
760
REAL(KIND=dp) :: ScalarVal, VectorVal(3), Power, Voltage
761
TYPE(Variable_t), POINTER :: Var
762
763
CALL Info('UpdateDepedentComponents','Updating Components to reflect new solution',Level=6)
764
765
DO j=1,CurrentModel % NumberOfComponents
766
767
IF( ALL( ComponentList /= j ) ) CYCLE
768
769
CALL Info('UpdateDepedentComponents','Updating component: '//I2S(j))
770
CompParams => CurrentModel % Components(j) % Values
771
772
NoVar = 0
773
DO WHILE( .TRUE. )
774
NoVar = NoVar + 1
775
OperName = ListGetString( CompParams,'Operator '//I2S(NoVar), GotOper)
776
VarName = ListGetString( CompParams,'Variable '//I2S(NoVar), GotVar)
777
CoeffName = ListGetString( CompParams,'Coefficient '//I2S(NoVar), GotCoeff)
778
779
IF(.NOT. GotVar .AND. GotOper .AND. OperName == 'electric resistance') THEN
780
VarName = 'Potential'
781
GotVar = .TRUE.
782
CALL Info('UpdateDependentComponents',&
783
'Defaulting field to > Potential < for operator: '//TRIM(OperName),Level=8)
784
END IF
785
786
IF(.NOT. (GotVar .AND. GotOper ) ) EXIT
787
788
Var => VariableGet( CurrentModel % Mesh % Variables, VarName )
789
IF( .NOT. ASSOCIATED( Var ) ) THEN
790
CALL Info('UpdateDependentComponents','Variable not available: '//TRIM(VarName))
791
CYCLE
792
END IF
793
VectorResult = .FALSE.
794
795
SELECT CASE( OperName )
796
797
CASE('electric resistance')
798
IF(.NOT. GotCoeff ) THEN
799
CoeffName = 'electric conductivity'
800
GotCoeff = .TRUE.
801
END IF
802
TmpOper = 'diffusive energy'
803
Power = ComponentIntegralReduction(CurrentModel, CurrentModel % Mesh, CompParams, Var, &
804
TmpOper, CoeffName, GotCoeff )
805
TmpOper = 'range'
806
Voltage = ComponentNodalReduction(CurrentModel, CurrentModel % Mesh, CompParams, Var, &
807
TmpOper )
808
ScalarVal = Voltage**2 / Power
809
CALL ListAddConstReal( CompParams,'res: '//TRIM(OperName),ScalarVal )
810
811
CASE ('sum','sum abs','min','max','min abs','max abs','range','mean','mean abs','variance')
812
ScalarVal = ComponentNodalReduction(CurrentModel, CurrentModel % Mesh, CompParams, Var, &
813
OperName )
814
CALL ListAddConstReal( CompParams,'res: '//TRIM(OperName)//' '//TRIM(VarName),ScalarVal )
815
816
CASE ('volume','int','int abs','int mean','int abs mean','diffusive energy',&
817
'convective energy','potential energy')
818
ScalarVal = ComponentIntegralReduction(CurrentModel, CurrentModel % Mesh, CompParams, Var, &
819
OperName, CoeffName, GotCoeff )
820
CALL ListAddConstReal( CompParams,'res: '//TRIM(OperName)//' '//TRIM(VarName),ScalarVal )
821
822
CASE('force')
823
CALL ComponentNodalForceReduction(CurrentModel, CurrentModel % Mesh, CompParams, Var, &
824
Force = VectorVal )
825
VectorResult = .TRUE.
826
827
CASE('moment')
828
CALL ComponentNodalForceReduction(CurrentModel, CurrentModel % Mesh, CompParams, Var, &
829
Moment = VectorVal )
830
VectorResult = .TRUE.
831
832
CASE('torque')
833
CALL ComponentNodalForceReduction(CurrentModel, CurrentModel % Mesh, CompParams, Var, &
834
Torque = ScalarVal )
835
836
CASE DEFAULT
837
CALL Fatal('UpdateDependentComponents','Uknown operator: '//TRIM(OperName))
838
839
END SELECT
840
841
IF( VectorResult ) THEN
842
DO i=1,3
843
WRITE( Message,'(A,ES15.6)') TRIM(OperName)//': '//TRIM(VarName)//' '&
844
//I2S(i)//': ',ScalarVal
845
CALL Info('UpdateDependentComponents',Message,Level=5)
846
CALL ListAddConstReal( CompParams,'res: '//TRIM(OperName)//': '&
847
//TRIM(VarName)//' '//I2S(i),VectorVal(i) )
848
END DO
849
ELSE
850
WRITE( Message,'(A,ES15.6)') &
851
'comp '//I2S(j)//': '//TRIM(OperName)//': '//TRIM(VarName)//': ',ScalarVal
852
CALL Info('UpdateDependentComponents',Message,Level=5)
853
CALL ListAddConstReal( CurrentModel % Simulation, &
854
'res: comp '//I2S(j)//': '//TRIM(OperName)//' '//TRIM(VarName),ScalarVal )
855
END IF
856
857
END DO
858
END DO
859
860
!------------------------------------------------------------------------------
861
END SUBROUTINE UpdateDependentComponents
862
!------------------------------------------------------------------------------
863
864
!------------------------------------------------------------------------------
865
!> Given a vector field compute line integral of Stokes theorem using
866
!> some geometric heuristics.
867
!------------------------------------------------------------------------------
868
FUNCTION ComponentStokesTheorem(Model, Mesh, Vlist, AVar, Surf ) RESULT ( FL )
869
!------------------------------------------------------------------------------
870
TYPE(Model_t) :: Model
871
TYPE(Mesh_t), POINTER :: Mesh
872
TYPE(ValueList_t), POINTER :: Vlist
873
TYPE(Variable_t), POINTER :: aVar
874
LOGICAL :: Surf
875
REAL(KIND=dp) :: FL
876
877
TYPE(Matrix_t), POINTER :: NodeGraph
878
REAL(KIND=dp), POINTER :: HelperArray(:,:)
879
REAL(KIND=dp) :: Center(3), Coord(3), Coord0(3), Coord1(3), Coord2(3), &
880
Normal(3), Tangent1(3), Tangent2(3), x1, y1, x2, y2, phi, phi1, phi2, &
881
phisum, g, ssum, h, hmin, hmax, htol, hgoal, hrel, r, rmin, rmax, rtol
882
COMPLEX :: gradv(3)
883
REAL(KIND=dp) :: EdgeVector(3), ds, r2, r2min, dsmax, dphi, dphimax, &
884
ReCirc, ImCirc
885
INTEGER :: nsteps, sgn, t, i, j, k, i1, i2, j1, j2, l, &
886
lp, n0, r2ind, imax, kmax, n, InsideBC
887
INTEGER, POINTER :: NodeIndexes(:)
888
INTEGER, POINTER :: TargetBodies(:)
889
LOGICAL :: Found, SaveLoop, Debug, GotHtol, GotRtol, BCMode
890
TYPE(Element_t), POINTER :: Edge, Element
891
LOGICAL, ALLOCATABLE :: NodeActive(:),Inside(:),Outside(:)
892
CHARACTER(*), PARAMETER :: Caller = 'ComponentStokesTheorem'
893
894
TargetBodies => ListGetIntegerArray( VList,'Master Bodies',Found )
895
IF( .NOT. Found ) TargetBodies => ListGetIntegerArray( VList,'Body',Found )
896
IF( .NOT. Found ) CALL Fatal(Caller,'Stokes theorem requires > Master Bodies <')
897
898
HelperArray => ListGetConstRealArray( Vlist, 'Coil Center', UnfoundFatal = .TRUE. )
899
Center(1:3) = HelperArray(1:3,1)
900
901
HelperArray => ListGetConstRealArray( Vlist, 'Coil normal', UnfoundFatal = .TRUE. )
902
Normal(1:3) = HelperArray(1:3,1)
903
CALL TangentDirections(Normal, Tangent1, Tangent2)
904
905
n = Mesh % NumberOfNodes
906
ALLOCATE(NodeActive(n))
907
908
InsideBC = ListGetInteger( Vlist,'Inside Boundary', BCMode )
909
910
CALL SetActiveNodeSet()
911
912
IF(Surf) THEN
913
CALL Info(Caller,'Calculating cylinder integral for: '//TRIM(avar % name))
914
CALL ComputeCylinderIntegral()
915
ELSE
916
CALL Info(Caller,'Calculating line integral for: '//TRIM(avar % name))
917
CALL ComputeLineIntegral()
918
END IF
919
920
FL = ReCirc
921
922
923
CONTAINS
924
925
! Set active nodes such that when we try to create a closed circle we will only check the marked
926
! nodes. We need a quick look-up table since otherwise there is no quick way to determine whether
927
! a node is part of a suitable edge. Also compute the height of the domain for other purposes.
928
!------------------------------------------------------------------------------------------------
929
930
SUBROUTINE SetActiveNodeSet()
931
932
IF( BCMode ) THEN
933
DO t=Mesh % NumberOfBulkElements+1, &
934
Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
935
Element => Mesh % Elements(t)
936
n = Element % TYPE % NumberOfNodes
937
NodeIndexes => Element % NodeIndexes
938
939
IF(.NOT. ASSOCIATED(Element % BoundaryInfo)) CYCLE
940
IF(Element % BoundaryInfo % Constraint == 0) CYCLE
941
DO i=1,CurrentModel % NumberOfBCs
942
IF ( Element % BoundaryInfo % Constraint == CurrentModel % BCs(i) % Tag ) EXIT
943
END DO
944
IF(i /= InsideBC) CYCLE
945
NodeActive(NodeIndexes) = .TRUE.
946
END DO
947
ELSE
948
! No BC elements given, initialize the list of candidate nodes using
949
! an intersection between inside and outside nodes.
950
ALLOCATE(Inside(n), Outside(n))
951
Inside = .FALSE.
952
Outside = .FALSE.
953
954
DO t=1,Mesh % NumberOfBulkElements
955
Element => Mesh % Elements(t)
956
IF( ANY(TargetBodies == Element % BodyId) ) THEN
957
Inside(Element % NodeIndexes) = .TRUE.
958
ELSE
959
Outside(Element % NodeIndexes) = .TRUE.
960
END IF
961
END DO
962
963
! We make the stokes theorem on the interface
964
NodeActive = Inside .AND. Outside
965
DEALLOCATE(Inside,Outside)
966
END IF
967
968
n = COUNT(NodeActive)
969
CALL Info(Caller,'Active nodes for edge patch candidates: '//I2S(n))
970
971
! We may limit the active set of nodes through which path may be drawn.
972
! The idea could be to choose only the upper or lower surface of the coil.
973
htol = ListGetConstReal( Vlist,'Flux linkage height tolerance',GotHtol )
974
rtol = ListGetConstReal( Vlist,'Flux linkage radius tolerance',GotRtol )
975
976
! We always make one round to get the bounding box!
977
IF(.TRUE.) THEN
978
hgoal = ListGetConstReal( Vlist,'Flux linkage relative height',Found )
979
hmin = HUGE(hmin)
980
hmax = -HUGE(hmax)
981
rmin = HUGE(rmin)
982
rmax = -HUGE(rmax)
983
DO k=1,2
984
DO i=1,Mesh % NumberOfNodes
985
IF(.NOT. NodeActive(i)) CYCLE
986
Coord(1) = Mesh % Nodes % x(i)
987
Coord(2) = Mesh % Nodes % y(i)
988
Coord(3) = Mesh % Nodes % z(i)
989
h = SUM(Coord*Normal)
990
r = SQRT(SUM(Coord*Tangent1)**2 + SUM(Coord*Tangent2)**2)
991
992
IF(k==1) THEN
993
hmin = MIN(h,hmin)
994
hmax = MAX(h,hmax)
995
rmin = MIN(r,rmin)
996
rmax = MAX(r,rmax)
997
ELSE
998
IF(GotHtol) THEN
999
hrel = (h-hmin)/(hmax-hmin)
1000
IF( ABS(hrel-hgoal) > htol ) NodeActive(i) = .FALSE.
1001
END IF
1002
IF(GotRtol) THEN
1003
IF( ABS(r-rmin)/rmin > rtol) NodeActive(i) = .FALSE.
1004
END IF
1005
END IF
1006
END DO
1007
1008
IF(k==1) THEN
1009
IF( ParEnv % PEs > 1 ) THEN
1010
hmin = ParallelReduction(hmin,1)
1011
hmax = ParallelReduction(hmax,2)
1012
rmin = ParallelReduction(rmin,1)
1013
rmax = ParallelReduction(rmax,2)
1014
END IF
1015
IF(InfoActive(20)) THEN
1016
PRINT *,'Height interval in n-t system: ',hmin,hmax
1017
PRINT *,'Radius interval in n-t system: ',hmin,hmax
1018
END IF
1019
END IF
1020
1021
IF(.NOT. (GotHTol .OR. GotRTol ) ) EXIT
1022
END DO
1023
END IF
1024
1025
IF(GotHTol .OR. GotRTol ) THEN
1026
n = COUNT(NodeActive)
1027
CALL Info(Caller,'Active nodes after tolerance check (out of '&
1028
//I2S(SIZE(NodeActive))//'): '//I2S(n))
1029
END IF
1030
1031
END SUBROUTINE SetActiveNodeSet
1032
1033
1034
! This compute a line integral. No line may exist so we go through a line created on-the-fly from
1035
! existing node-to-node connections. We rotate through the axis until 360 degrees are passed
1036
! going always to the node among the candidate nodes that is as efficient as possible in terms of angle.
1037
! Unfortunately this logic may fail if the circle is not a true circle.
1038
!------------------------------------------------------------------------------------------------------
1039
SUBROUTINE ComputeLineIntegral()
1040
1041
INTEGER :: WhoActive, PrevWhoActive, NoStat, kprev(3)
1042
LOGICAL :: FileOpen
1043
REAL(KIND=dp) :: r2min_par
1044
CHARACTER(:), ALLOCATABLE :: str
1045
1046
FileOpen = .FALSE.
1047
NoStat = 0
1048
1049
1050
! Create a graph for node-to-edge connectivity
1051
!----------------------------------------------
1052
NodeGraph => AllocateMatrix()
1053
NodeGraph % FORMAT = MATRIX_LIST
1054
DO i = Mesh % NumberOfEdges, 1, -1
1055
Edge => Mesh % Edges(i)
1056
IF(ALL(NodeActive(Edge % NodeIndexes))) THEN
1057
DO j=1, Edge % TYPE % NumberOfNodes
1058
CALL List_AddToMatrixElement( NodeGraph % ListMatrix,Edge % NodeIndexes(j),i,1.0_dp )
1059
END DO
1060
END IF
1061
END DO
1062
CALL List_ToCRSMatrix(NodeGraph)
1063
WRITE(Message,'(A,ES12.3)') 'Nonzeros per row NodeGraph:',&
1064
1.0_dp * SIZE(NodeGraph % Values) / NodeGraph % NumberOfRows
1065
CALL Info(Caller, Message, Level=10)
1066
1067
n0 = Mesh % NumberOfNodes
1068
1069
! Find the node closest to the origin
1070
r2min = HUGE(r2min)
1071
r2ind = 0
1072
DO i=1,Mesh % NumberOfNodes
1073
IF(.NOT. NodeActive(i)) CYCLE
1074
Coord(1) = Mesh % Nodes % x(i)
1075
Coord(2) = Mesh % Nodes % y(i)
1076
Coord(3) = Mesh % Nodes % z(i)
1077
r2 = SUM((Coord-Center)**2)
1078
IF(r2 < r2min) THEN
1079
r2min = r2
1080
r2ind = i
1081
Coord0 = Coord
1082
END IF
1083
END DO
1084
1085
WRITE(Message,'(A,ES10.3)') 'Minimum distance (node '//I2S(r2ind)//'):',SQRT(r2min)
1086
CALL Info(Caller,Message,Level=7)
1087
1088
Debug = .FALSE.
1089
IF( Debug ) THEN
1090
PRINT *,'Center:',Center
1091
PRINT *,'Normal:',Normal
1092
PRINT *,'Tangent1:',Tangent1
1093
PRINT *,'Tangent2:',Tangent2
1094
PRINT *,'Coord0:',Coord0
1095
END IF
1096
1097
phisum = 0.0_dp
1098
nsteps = 0
1099
ReCirc = 0.0_dp
1100
ImCirc = 0.0_dp
1101
ssum = 0.0_dp
1102
1103
Coord1 = Coord0
1104
i1 = r2ind
1105
1106
! We have to assume that one partition finds the entire circle.
1107
! If this is done in many pieces we should generate an algo that passed on the
1108
! process on a joint node that turns out to be otherwise the end of the path.
1109
IF(ParEnv % PEs > 1) THEN
1110
WhoActive = -1
1111
r2min_par = ParallelReduction(r2min,1)
1112
IF( ABS(r2min_par - r2min) < TINY(r2min)) THEN
1113
WhoActive = ParEnv % MyPe
1114
END IF
1115
1116
! We start from this partition.
1117
! I.e. the largest partition index with the minimum distance.
1118
WhoActive = ParallelReduction(WhoActive,2)
1119
1120
! We need Coord0 always in parallel in each partition.
1121
IF(WhoActive /= ParEnv % MyPe ) THEN
1122
Coord0 = -HUGE(Coord0)
1123
r2ind = 0
1124
END IF
1125
DO k=1,3
1126
Coord0(k) = ParallelReduction(Coord0(k),2)
1127
END DO
1128
ELSE
1129
WhoActive = ParEnv % Mype
1130
END IF
1131
PrevWhoActive = WhoActive
1132
1133
10 IF( WhoActive == ParEnv % MyPe ) THEN
1134
x1 = SUM(Coord1*Tangent1)
1135
y1 = SUM(Coord1*Tangent2)
1136
phi1 = (180.0_dp/PI) * ATAN2(y1,x1)
1137
str = ListGetString( Vlist,'Line Integral File',SaveLoop )
1138
ELSE
1139
Coord1 = -HUGE(Coord1)
1140
SaveLoop = .FALSE.
1141
END IF
1142
1143
IF( ParEnv % PEs > 1) THEN
1144
! If this is not active partition go to wait for the active one for further instructions.
1145
IF( WhoActive /= ParEnv % MyPe ) GOTO 20
1146
END IF
1147
1148
IF( SaveLoop ) THEN
1149
! Only open the file once!
1150
IF(.NOT. FileOpen ) THEN
1151
IF( ParEnv % PEs > 1 ) THEN
1152
OPEN (10, FILE=TRIM(str)//'_'//I2S(ParEnv % MyPe) )
1153
ELSE
1154
OPEN (10, FILE=str )
1155
END IF
1156
FileOpen = .TRUE.
1157
END IF
1158
WRITE(10,*) nsteps, Coord1, phi1, phisum, ssum, ReCirc
1159
END IF
1160
1161
kprev = 0
1162
DO WHILE(.TRUE.)
1163
dsmax = -EPSILON(dsmax)
1164
kprev(2:3) = kprev(1:2)
1165
kprev(1) = kmax
1166
kmax = 0
1167
1168
! Among the edges related to node "i1" find the one that has makes us further in
1169
! minimizing the distance.
1170
DO j = NodeGraph % Rows(i1),NodeGraph % Rows(i1+1)-1
1171
k = NodeGraph % Cols(j)
1172
1173
! Do not use any of the previous node again!
1174
IF(ANY(k==kprev)) CYCLE
1175
Edge => Mesh % Edges(k)
1176
NodeIndexes => Edge % NodeIndexes
1177
1178
IF(NodeIndexes(1) == i1) THEN
1179
i2 = NodeIndexes(2)
1180
ELSE IF(NodeIndexes(2) == i1) THEN
1181
i2 = NodeIndexes(1)
1182
ELSE
1183
CALL Fatal(Caller,'Either node index in edge should be i1!')
1184
END IF
1185
IF(.NOT. NodeActive(i2)) CYCLE
1186
1187
Coord(1) = Mesh % Nodes % x(i2)
1188
Coord(2) = Mesh % Nodes % y(i2)
1189
Coord(3) = Mesh % Nodes % z(i2)
1190
1191
x2 = SUM(Coord*Tangent1)
1192
y2 = SUM(Coord*Tangent2)
1193
phi = (180.0_dp/PI) * ATAN2(y2,x2)
1194
1195
dphi = phi1-phi
1196
1197
! Deal with the discontinuity of angle around +/-180 deg
1198
IF(dphi < -180.0_dp) dphi = dphi+360.0_dp
1199
IF(dphi > 180.0_dp) dphi = dphi-360.0_dp
1200
1201
IF(phisum < 315.0_dp ) THEN
1202
! This measure takes the shortest route at least in some case more robustly than some others.
1203
ds = dphi / SUM((Coord1-Coord)**2)
1204
ELSE
1205
! After we only have ~45 degs left we find the node which approaches the starting node
1206
! as well as possible.
1207
ds = SUM( (Coord1-Coord0)**2 - (Coord-Coord0)**2 ) / SUM((Coord1-Coord)**2)
1208
END IF
1209
1210
IF( Debug ) THEN
1211
PRINT *,'kmax:',ds > dsmax, k,i2,ds,dphi,phi,Coord
1212
END IF
1213
1214
! Have we found a better edge candidate? Memorize that!
1215
IF( ds >= dsmax ) THEN
1216
kmax = k
1217
imax = i2
1218
dsmax = ds
1219
Coord2 = Coord
1220
dphimax = dphi
1221
phi2 = phi
1222
END IF
1223
END DO
1224
1225
! When no way to get further we are done!
1226
IF( kmax == 0 ) THEN
1227
IF( ParEnv % PEs > 1 ) THEN
1228
BLOCK
1229
INTEGER, POINTER :: Neig(:)
1230
Neig => Mesh % ParallelInfo % NeighbourList(i1) % Neighbours
1231
IF( SIZE(Neig) > 1 ) THEN
1232
!PRINT *,'Swapping to partition:',Neig
1233
IF( WhoActive == Neig(1) ) THEN
1234
WhoActive = Neig(2)
1235
ELSE
1236
WhoActive = Neig(1)
1237
END IF
1238
EXIT
1239
END IF
1240
END BLOCK
1241
END IF
1242
1243
!PRINT *,'Cands:',i1,NodeGraph % Rows(i1),NodeGraph % Rows(i1+1)-1
1244
NoStat = 1
1245
CALL Warn(Caller,'We had to stop because no route was found!')
1246
EXIT
1247
END IF
1248
1249
nsteps = nsteps + 1
1250
1251
! Edge that goes to the minimum value
1252
k = kmax
1253
Edge => Mesh % Edges(k)
1254
i2 = imax
1255
1256
EdgeVector = Coord2 - Coord1
1257
1258
! Integration length to check optimality of the path
1259
ds = SQRT(SUM(EdgeVector**2))
1260
ssum = ssum + ds
1261
1262
IF( MODULO(avar % dofs,3) == 0 ) THEN
1263
! Integral over nodal field using the mean value
1264
j1 = avar % Perm(i1)
1265
j2 = avar % Perm(i2)
1266
IF(j1==0 .OR. j2==0) CALL Fatal(Caller,'Nodal field missing on path!')
1267
DO k=1,3
1268
IF( avar % dofs == 3 ) THEN
1269
gradv(k) = ( avar % Values(3*(j1-1)+k) + avar % Values(3*(j2-1)+k) ) / 2
1270
ELSE
1271
gradv(k) = CMPLX(avar % Values(6*(j1-1)+k) + avar % Values(6*(j2-1)+k),&
1272
avar % Values(6*(j1-1)+3+k) + avar % Values(6*(j2-1)+3+k) ) / 2
1273
END IF
1274
END DO
1275
ReCirc = ReCirc + REAL(SUM(gradv*EdgeVector))
1276
ImCirc = ImCirc + AIMAG(SUM(gradv*EdgeVector))
1277
ELSE
1278
! Integral over edge field.
1279
1280
! Check the sign if the direction based on global edge direction rules
1281
! If we do the path integral in the wrong direction compared to definition of edge switch the sign
1282
sgn = 1
1283
IF( ParEnv % PEs > 1 ) THEN
1284
i1 = Mesh % ParallelInfo % GlobalDOFs(i1)
1285
i2 = Mesh % ParallelInfo % GlobalDOFs(i2)
1286
END IF
1287
!IF(XOR(Edge % NodeIndexes(1) /= i1, i1 < i2) ) sgn = -sgn
1288
IF(i1 > i2) sgn = -1
1289
1290
j = avar % Perm(n0 + k)
1291
IF( j==0) CALL Fatal(Caller,'Edge field missing on path!')
1292
1293
IF( avar % dofs == 1 ) THEN
1294
ReCirc = ReCirc + sgn * avar % Values(j)
1295
ELSE
1296
ReCirc = ReCirc + sgn * avar % Values(2*j-1)
1297
ImCirc = ImCirc + sgn * avar % Values(2*j)
1298
END IF
1299
END IF
1300
1301
! Do not visit this point a 2nd time!
1302
!NodeActive(i1) = .FALSE.
1303
1304
! Continue from the end point
1305
i1 = imax
1306
Coord1 = Coord2
1307
phi1 = phi2
1308
1309
phisum = phisum + dphimax
1310
1311
IF(Debug) PRINT *,'phisum:',nsteps,Coord1,phisum,ssum,phi1,ReCirc
1312
1313
IF( ABS(phisum) > 720.0_dp ) THEN
1314
CALL Fatal(Caller,'We circled twice around!?')
1315
END IF
1316
1317
IF(SaveLoop) WRITE(10,*) nsteps, Coord1, phi1, phisum, ssum, ReCirc
1318
1319
! We have come home to roost!
1320
IF(i1 == r2ind) THEN
1321
CALL Info(Caller,'Integration over full loop finished!')
1322
EXIT
1323
END IF
1324
END DO
1325
1326
20 IF( ParEnv % PEs > 1 ) THEN
1327
phisum = ParallelReduction(phisum)
1328
!PRINT *,'phisum:',phisum, ParEnv % MyPe
1329
!PRINT *,'whoactive:',whoactive, prevwhoactive, ParEnv % Mype
1330
1331
! Find out the new active partition. It has been set only in the previous active partition.
1332
IF(PrevWhoActive /= ParEnv % MyPe) THEN
1333
WhoActive = -1
1334
END IF
1335
WhoActive = ParallelReduction(WhoActive,2)
1336
1337
!PRINT *,'New whoactive:',WhoActive, PrevWhoActive, ParEnv % MyPe
1338
1339
IF(WhoActive /= PrevWhoActive) THEN
1340
PrevWhoActive = WhoActive
1341
1342
! Find the new starting node.
1343
DO k=1,3
1344
Coord1(k) = ParallelReduction(Coord1(k),2)
1345
END DO
1346
1347
! For the new active partition find a new starting point.
1348
IF( WhoActive == ParEnv % MyPe ) THEN
1349
r2min = HUGE(r2min)
1350
i1 = 0
1351
DO i=1,Mesh % NumberOfNodes
1352
IF(.NOT. NodeActive(i)) CYCLE
1353
Coord(1) = Mesh % Nodes % x(i)
1354
Coord(2) = Mesh % Nodes % y(i)
1355
Coord(3) = Mesh % Nodes % z(i)
1356
r2 = SUM((Coord-Coord1)**2)
1357
IF(r2 < r2min) THEN
1358
r2min = r2
1359
i1 = i
1360
END IF
1361
END DO
1362
!PRINT *,'New starting point:',i1,SQRT(r2min)
1363
ELSE
1364
! Have the counter active only in the active partition.
1365
phisum = 0.0_dp
1366
END IF
1367
1368
!PRINT *,'WhoIsActice:',ParEnv % MyPe, WhoActive, WhoActive == ParEnv % MyPe
1369
GOTO 10
1370
END IF
1371
END IF
1372
1373
IF(ParEnv % PEs > 1 ) THEN
1374
ReCirc = ParallelReduction(ReCirc)
1375
ImCirc = ParallelReduction(ImCirc)
1376
ssum = ParallelReduction(ssum)
1377
END IF
1378
1379
IF(InfoActive(20)) THEN
1380
PRINT *,'PathIntegralLine:',ParEnv % MyPe, avar % dofs, targetbodies, nsteps, phisum, ssum, ReCirc
1381
END IF
1382
1383
NoStat = ParallelReduction(NoStat)
1384
IF(Nostat > 0 ) THEN
1385
CALL Info(Caller,'Setting values to zero because of issues!')
1386
ReCirc = 0.0_dp
1387
ImCirc = 0.0_dp
1388
END IF
1389
1390
CALL FreeMatrix(NodeGraph)
1391
IF(FileOpen) CLOSE(10)
1392
1393
END SUBROUTINE ComputeLineIntegral
1394
1395
1396
SUBROUTINE ComputeCylinderIntegral()
1397
1398
TYPE(Nodes_t), SAVE :: ElementNodes
1399
LOGICAL :: AllocationsDone = .FALSE.
1400
TYPE(GaussIntegrationPoints_t) :: IP
1401
REAL(KIND=dp) :: detJ, Area, s, TestVec(3), ParentNormal(3), Coeff
1402
TYPE(ValueList_t), POINTER :: Params
1403
LOGICAL :: Stat, PiolaVersion, EdgeBasis
1404
INTEGER :: np, nd, EdgeBasisDegree, tmin, tmax
1405
INTEGER, POINTER, SAVE :: Indexes(:)
1406
TYPE(Element_t), POINTER :: Element, Parent, pElem
1407
REAL(KIND=dp), POINTER, SAVE :: Basis(:), SOL(:,:), WBasis(:,:), dBasisdx(:,:), RotWBasis(:,:)
1408
1409
CALL Info(Caller,'Estimating line integral from surface integral!')
1410
1411
IF(.NOT. AllocationsDone ) THEN
1412
n = 2*Model % MaxElementNodes
1413
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n), &
1414
Basis(n), dBasisdx(n,3), WBasis(n,3), RotWBasis(n,3), SOL(6,n), Indexes(n) )
1415
AllocationsDone = .TRUE.
1416
END IF
1417
1418
Area = 0.0_dp
1419
ReCirc = 0.0_dp
1420
ImCirc = 0.0_dp
1421
1422
EdgeBasis = .FALSE.
1423
IF(avar % dofs <= 2) THEN
1424
EdgeBasis = .TRUE.
1425
Params => avar % Solver % Values
1426
CALL EdgeElementStyle(avar % Solver % Values, PiolaVersion, BasisDegree = EdgeBasisDegree )
1427
END IF
1428
1429
IF( BCMode ) THEN
1430
tmin = Mesh % NumberOfBulkElements + 1
1431
tmax = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
1432
ELSE
1433
tmin = 1
1434
tmax = Mesh % NumberOfFaces
1435
END IF
1436
1437
!PRINT *,'Center:',Center
1438
!PRINT *,'Normal:',Normal
1439
!PRINT *,'BCMode:',BCMode,tmin,tmax,InsideBC
1440
1441
DO t=tmin, tmax
1442
IF(BCMode) THEN
1443
Element => Mesh % Elements(t)
1444
ELSE
1445
Element => Mesh % Faces(t)
1446
END IF
1447
n = Element % TYPE % NumberOfNodes
1448
NodeIndexes => Element % NodeIndexes
1449
1450
IF( BCMode ) THEN
1451
IF(.NOT. ASSOCIATED(Element % BoundaryInfo)) CYCLE
1452
IF(Element % BoundaryInfo % Constraint == 0) CYCLE
1453
DO i=1,CurrentModel % NumberOfBCs
1454
IF ( Element % BoundaryInfo % Constraint == CurrentModel % BCs(i) % Tag ) EXIT
1455
END DO
1456
IF(i /= InsideBC) CYCLE
1457
ELSE
1458
IF(.NOT. ALL(NodeActive(NodeIndexes))) CYCLE
1459
END IF
1460
1461
! Check that we have a parent that is on the boundary.
1462
k = 0
1463
DO i=1,2
1464
IF(i==1) THEN
1465
pElem => Element % BoundaryInfo % Left
1466
ELSE
1467
pElem => Element % BoundaryInfo % Right
1468
END IF
1469
IF(ASSOCIATED(pElem)) THEN
1470
IF( ANY( TargetBodies == pElem % BodyId ) ) THEN
1471
k=k+1
1472
Parent => pElem
1473
END IF
1474
END IF
1475
END DO
1476
IF(k/=1) CYCLE
1477
1478
ElementNodes % x(1:n) = Mesh % Nodes % x(NodeIndexes(1:n))
1479
ElementNodes % y(1:n) = Mesh % Nodes % y(NodeIndexes(1:n))
1480
ElementNodes % z(1:n) = Mesh % Nodes % z(NodeIndexes(1:n))
1481
1482
! Normal pointing out of the parent
1483
ParentNormal = NormalVector(Element,ElementNodes,Parent=Parent)
1484
! Normal just in xy-plane
1485
ParentNormal = ParentNormal - SUM(Normal*ParentNormal)*Normal
1486
1487
! Center of face element
1488
Coord1(1) = SUM(ElementNodes % x(1:n)) / n
1489
Coord1(2) = SUM(ElementNodes % y(1:n)) / n
1490
Coord1(3) = SUM(ElementNodes % z(1:n)) / n
1491
! Face element direction vector in xy-plane
1492
Coord1 = Coord1 - Center
1493
Coord1 = Coord1 - SUM(Normal*Coord1)*Normal
1494
! Normalize such that |Coord1| == 1.
1495
ds = SQRT(SUM(Coord1**2))
1496
IF(ds > EPSILON(ds)) Coord1 = Coord1 / ds
1497
1498
! If we are not pointing inwards at all then skip the face element.
1499
Coeff = -SUM(ParentNormal * Coord1)
1500
IF(Coeff < EPSILON(Coeff) ) CYCLE
1501
1502
! Find the maximum distance edge on the boundary in the local coordinates.
1503
! This edge is oriented with the surface having the desired direction.
1504
dsmax = -HUGE(dsmax)
1505
DO i=1,n
1506
Coord1(1) = ElementNodes % x(i)
1507
Coord1(2) = ElementNodes % y(i)
1508
Coord1(3) = ElementNodes % z(i)
1509
Coord1 = Coord1 - Center
1510
Coord1 = Coord1 - SUM(Normal*Coord1)*Normal
1511
DO j=i+1,n
1512
Coord2(1) = ElementNodes % x(j)
1513
Coord2(2) = ElementNodes % y(j)
1514
Coord2(3) = ElementNodes % z(j)
1515
Coord2 = Coord2 - Center
1516
Coord2 = Coord2 - SUM(Normal*Coord2)*Normal
1517
ds = SUM((Coord1-Coord2)**2)
1518
IF(ds > dsmax ) THEN
1519
dsmax = ds
1520
EdgeVector = Coord1-Coord2
1521
END IF
1522
END DO
1523
END DO
1524
EdgeVector = EdgeVector / SQRT(SUM(EdgeVector**2))
1525
1526
TestVec = CrossProduct(Normal,Coord1)
1527
IF(SUM(TestVec*EdgeVector) > 0.0_dp) EdgeVector = -EdgeVector
1528
1529
IF( EdgeBasis ) THEN
1530
nd = mGetElementDofs( Indexes, Uelement = Element, USolver = avar % Solver )
1531
np = COUNT(Indexes(1:nd) <= Mesh % NumberOfNodes)
1532
IP = GaussPoints(Element, EdgeBasis=.TRUE., PReferenceElement=PiolaVersion, &
1533
EdgeBasisDegree=EdgeBasisDegree)
1534
ELSE
1535
Indexes(1:n) = NodeIndexes(1:n)
1536
nd = n
1537
IP = GaussPoints( Element )
1538
END IF
1539
1540
DO i=1,avar % dofs
1541
SOL(i,1:nd) = avar % values(avar % dofs*(avar % Perm(Indexes(1:nd))-1)+i)
1542
END DO
1543
1544
!------------------------------------------------------------------------------
1545
! Numerical integration
1546
!------------------------------------------------------------------------------
1547
1548
DO l=1,IP % n
1549
IF(.NOT. EdgeBasis) THEN
1550
stat = ElementInfo( Element,ElementNodes,&
1551
IP % U(l),IP % V(l),IP % W(l), DetJ, Basis )
1552
ELSE
1553
stat = ElementInfo( Element, ElementNodes, IP % U(l), IP % V(l), &
1554
IP % W(l), detJ, Basis, dBasisdx, EdgeBasis = WBasis, &
1555
RotBasis = RotWBasis, USolver = avar % Solver )
1556
END IF
1557
1558
! stat = EdgeElementInfo(Element, ElementNodes, IP % U(l), IP % V(l), IP % W(l), &
1559
! DetF = DetJ, Basis = Basis, EdgeBasis = WBasis, dBasisdx = dBasisdx, &
1560
! BasisDegree = EdgeBasisDegree, ApplyPiolaTransform = .TRUE.)
1561
! ELSE
1562
! stat = ElementInfo(Element, ElementNodes, IP % U(l), IP % V(l), IP % W(l), &
1563
! detJ, Basis, dBasisdx)
1564
! CALL GetEdgeBasis(Element, WBasis, RotWBasis, Basis, dBasisdx)
1565
! END IF
1566
1567
s = Coeff * DetJ * IP % s(l)
1568
Area = Area + S
1569
1570
SELECT CASE( avar % dofs )
1571
CASE( 1 )
1572
gradv = MATMUL(SOL(1,np+1:nd), WBasis(1:nd-np,:))
1573
CASE( 2 )
1574
gradv = CMPLX( MATMUL(SOL(1,np+1:nd), WBasis(1:nd-np,:)), &
1575
MATMUL(SOL(2,np+1:nd), WBasis(1:nd-np,:)))
1576
CASE( 3 )
1577
gradv = MATMUL(SOL(1:3,1:n),Basis(1:n))
1578
CASE( 6 )
1579
DO i=1,3
1580
gradv(i) = CMPLX( SUM(SOL(2*i-1,1:n)*Basis(1:n)), &
1581
SUM(SOL(2*i,1:n)*Basis(1:n)) )
1582
END DO
1583
END SELECT
1584
1585
ReCirc = ReCirc + s * REAL(SUM(gradv*EdgeVector))
1586
ImCirc = ImCirc + s * AIMAG(SUM(gradv*EdgeVector))
1587
END DO
1588
END DO
1589
1590
! Sum up in parallel.
1591
Area = ParallelReduction(Area)
1592
ReCirc = ParallelReduction(ReCirc)
1593
ImCirc = ParallelReduction(ImCirc)
1594
1595
! Move from surface integral to line integral correspondent by dividing with the height.
1596
ReCirc = ReCirc / (hmax-hmin)
1597
ImCirc = ImCirc / (hmax-hmin)
1598
1599
IF(InfoActive(20)) THEN
1600
PRINT *,'PathIntegralCyl:',avar % dofs, targetbodies, area, &
1601
area/((hmax-hmin)*2*PI), ReCirc, ImCirc
1602
END IF
1603
1604
END SUBROUTINE ComputeCylinderIntegral
1605
1606
END FUNCTION ComponentStokesTheorem
1607
1608
1609
!------------------------------------------------------------------------------
1610
!> Given vector potential and current density compute the energy in the coil.
1611
!> This is actually not energy, but twice the energy, since the values are
1612
!> used to computed inductance matrix.
1613
!------------------------------------------------------------------------------
1614
FUNCTION ComponentCoilEnergy(Model, Mesh, MasterEntities, AVar, CVar, BCMode ) RESULT ( AIintRe )
1615
!------------------------------------------------------------------------------
1616
TYPE(Model_t) :: Model
1617
TYPE(Mesh_t), POINTER :: Mesh
1618
INTEGER, POINTER :: MasterEntities(:)
1619
TYPE(Variable_t), POINTER :: AVar, CVar
1620
LOGICAL, OPTIONAL :: BCMode
1621
REAL(KIND=dp) :: AIintRe
1622
!------------------------------------------------------------------------------
1623
! Local variables
1624
!------------------------------------------------------------------------------
1625
TYPE(Element_t), POINTER :: Element
1626
INTEGER :: t, i, j, k, l, n, np, nd, EdgeBasisDegree, t1, t2
1627
REAL(KIND=dp) :: volume
1628
LOGICAL :: Found
1629
LOGICAL :: Stat, PiolaVersion, EdgeBasis, DoBCs
1630
COMPLEX(KIND=dp) :: AIint
1631
CHARACTER(LEN=MAX_NAME_LEN) :: str
1632
CHARACTER(*), PARAMETER :: Caller = 'ComponentCoilEnergy'
1633
1634
IF(.NOT. ASSOCIATED(MasterEntities)) THEN
1635
CALL Fatal(Caller,'"MasterEntities" not associated!')
1636
END IF
1637
1638
DoBCs = .FALSE.
1639
IF(PRESENT(BcMode)) DoBCs = BCMode
1640
1641
str = I2S(MasterEntities(1))
1642
DO i=2,SIZE(MasterEntities)
1643
str = TRIM(str)//' '//I2S(MasterEntities(i))
1644
END DO
1645
IF( DoBCs ) THEN
1646
CALL Info(Caller,'Performing reduction for BCs: '//TRIM(str),Level=10)
1647
ELSE
1648
CALL Info(Caller,'Performing reduction for bodies: '//TRIM(str),Level=10)
1649
END IF
1650
1651
IF( DoBCs ) THEN
1652
t1 = Mesh % NumberOfBulkElements + 1
1653
t2 = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
1654
ELSE
1655
t1 = 1
1656
t2 = Mesh % NumberOfBulkElements
1657
END IF
1658
1659
EdgeBasis = .FALSE.
1660
IF(avar % dofs <= 2) THEN
1661
EdgeBasis = .TRUE.
1662
CALL EdgeElementStyle(avar % Solver % Values, PiolaVersion, BasisDegree = EdgeBasisDegree )
1663
END IF
1664
1665
AIint = 0.0_dp
1666
volume = 0.0_dp
1667
1668
IF( CVar % Dofs /= 3 ) THEN
1669
CALL Fatal(Caller,'Expecting 3 components for current density!')
1670
END IF
1671
IF( ALL([1,2,3,6] /= AVar % Dofs) ) THEN
1672
CALL Fatal(Caller,'Expecting 1,2,3 or 6 components for vector potential!')
1673
END IF
1674
1675
DO t=t1, t2
1676
Element => Mesh % Elements(t)
1677
IF( DoBCs ) THEN
1678
IF( ALL( MasterEntities /= Element % BoundaryInfo % Constraint ) ) CYCLE
1679
ELSE
1680
IF( ALL( MasterEntities /= Element % BodyId ) ) CYCLE
1681
END IF
1682
CALL LocalIntegElem()
1683
END DO
1684
1685
AIint = ParallelReduction( AIint )
1686
Volume = ParallelReduction( volume )
1687
1688
!PRINT *,'AiInit:',AiInt,Volume
1689
1690
AIIntRe = REAL(AIint)
1691
1692
CALL Info(Caller,'Reduction operator finished',Level=12)
1693
1694
CONTAINS
1695
1696
SUBROUTINE LocalIntegElem()
1697
1698
TYPE(GaussIntegrationPoints_t) :: IP
1699
TYPE(Nodes_t), SAVE :: ElementNodes
1700
LOGICAL, SAVE :: AllocationsDone = .FALSE.
1701
INTEGER, POINTER, SAVE :: EdgeIndexes(:)
1702
INTEGER, POINTER :: NodeIndexes(:), pIndexes(:)
1703
REAL(KIND=dp) :: DetJ,S,Cip(3)
1704
COMPLEX(KIND=dp) :: Aip(3)
1705
REAL(KIND=dp), POINTER, SAVE :: Basis(:), WBasis(:,:), dBasisdx(:,:), RotWBasis(:,:), &
1706
Aelem(:,:), Celem(:,:)
1707
1708
IF(.NOT. AllocationsDone ) THEN
1709
n = 2*Model % MaxElementNodes
1710
ALLOCATE( ElementNodes % x(n), ElementNodes % y(n), ElementNodes % z(n), &
1711
Basis(n), dBasisdx(n,3), WBasis(n,3), RotWBasis(n,3), Aelem(6,n), Celem(3,n), EdgeIndexes(n) )
1712
AllocationsDone = .TRUE.
1713
END IF
1714
1715
1716
n = Element % TYPE % NumberOfNodes
1717
NodeIndexes => Element % NodeIndexes
1718
1719
ElementNodes % x(1:n) = Mesh % Nodes % x(NodeIndexes(1:n))
1720
ElementNodes % y(1:n) = Mesh % Nodes % y(NodeIndexes(1:n))
1721
ElementNodes % z(1:n) = Mesh % Nodes % z(NodeIndexes(1:n))
1722
1723
IF( EdgeBasis ) THEN
1724
nd = mGetElementDofs( EdgeIndexes, Uelement = Element, USolver = avar % Solver )
1725
np = COUNT(EdgeIndexes(1:nd) <= Mesh % NumberOfNodes)
1726
IP = GaussPoints(Element, EdgeBasis=.TRUE., PReferenceElement=PiolaVersion, &
1727
EdgeBasisDegree=EdgeBasisDegree)
1728
pIndexes => EdgeIndexes
1729
ELSE
1730
nd = n
1731
IP = GaussPoints( Element )
1732
pIndexes => NodeIndexes
1733
END IF
1734
1735
! Edge or nodal values of vector potential
1736
DO i=1,avar % dofs
1737
Aelem(i,1:nd) = avar % values(avar % dofs*(avar % Perm(pIndexes(1:nd))-1)+i)
1738
END DO
1739
1740
! Nodal values of current density
1741
IF( cvar % TYPE == Variable_on_nodes_on_elements ) THEN
1742
pIndexes => Element % DGIndexes
1743
ELSE
1744
pIndexes => NodeIndexes
1745
END IF
1746
DO i=1,cvar % dofs
1747
Celem(i,1:n) = cvar % values(cvar % dofs*(cvar % Perm(pIndexes(1:n))-1)+i)
1748
END DO
1749
1750
DO l=1,IP % n
1751
IF(.NOT. EdgeBasis) THEN
1752
stat = ElementInfo( Element,ElementNodes,&
1753
IP % U(l),IP % V(l),IP % W(l), DetJ, Basis )
1754
ELSE
1755
stat = ElementInfo( Element, ElementNodes, IP % U(l), IP % V(l), &
1756
IP % W(l), detJ, Basis, dBasisdx, EdgeBasis = WBasis, &
1757
RotBasis = RotWBasis, USolver = avar % Solver )
1758
END IF
1759
1760
s = DetJ * IP % s(l)
1761
1762
! Vector potential at IP
1763
SELECT CASE( avar % dofs )
1764
CASE( 1 )
1765
Aip = MATMUL(Aelem(1,np+1:nd), WBasis(1:nd-np,:))
1766
CASE( 2 )
1767
Aip = CMPLX( MATMUL(Aelem(1,np+1:nd), WBasis(1:nd-np,:)), &
1768
MATMUL(Aelem(2,np+1:nd), WBasis(1:nd-np,:)))
1769
CASE( 3 )
1770
Aip = MATMUL(Aelem(1:3,1:n),Basis(1:n))
1771
CASE( 6 )
1772
Aip = CMPLX( MATMUL(Aelem(1:5:2,1:n),Basis(1:n)), &
1773
MATMUL(Aelem(2:6:2,1:n),Basis(1:n)) )
1774
END SELECT
1775
1776
! Current density at IP
1777
Cip = MATMUL(Celem(1:3,1:n),Basis(1:n))
1778
1779
!PRINT *,'Ai:',s,SQRT(SUM(REAL(Aip)**2)),SQRT(SUM(AIMAG(Aip)**2)),'c',SQRT(SUM(Cip*Cip))
1780
1781
AIint = AIint + s * SUM(Aip*Cip)
1782
Volume = Volume + s
1783
END DO
1784
1785
END SUBROUTINE LocalIntegElem
1786
1787
!------------------------------------------------------------------------------
1788
END FUNCTION ComponentCoilEnergy
1789
!------------------------------------------------------------------------------
1790
1791
1792
!------------------------------------------------------------------------------
1793
!> Compute integrals for determining the S parameters.
1794
!------------------------------------------------------------------------------
1795
FUNCTION BoundaryWaveFlux(Model, Mesh, MasterEntities, Avar, InFlux, PortImp, PortBC ) &
1796
RESULT ( OutFlux )
1797
!------------------------------------------------------------------------------
1798
TYPE(Model_t) :: Model
1799
TYPE(Mesh_t), POINTER :: Mesh
1800
INTEGER, POINTER :: MasterEntities(:)
1801
TYPE(Variable_t), POINTER :: Avar
1802
COMPLEX(KIND=dp) :: OutFlux
1803
COMPLEX(KIND=dp) :: InFlux
1804
COMPLEX(KIND=dp) :: PortImp
1805
LOGICAL :: PortBC
1806
!------------------------------------------------------------------------------
1807
! Local variables
1808
!------------------------------------------------------------------------------
1809
TYPE(Element_t), POINTER :: Element
1810
INTEGER :: t, i, j, k, l, n, np, nd, EdgeBasisDegree, t1, t2, bc_id
1811
LOGICAL :: Found, InitHandles
1812
LOGICAL :: Stat, PiolaVersion, EdgeBasis, UseGaussLaw
1813
TYPE(ValueList_t), POINTER :: BC
1814
CHARACTER(LEN=MAX_NAME_LEN) :: str
1815
REAL(KIND=dp) :: area, omega
1816
COMPLEX(KIND=dp) :: int_norm, int_el, vol, curr, port_curr, trans, Zimp
1817
CHARACTER(*), PARAMETER :: Caller = 'BoundaryWaveFlux'
1818
1819
area = 0.0_dp
1820
1821
int_norm = 0.0_dp
1822
int_el = 0.0_dp
1823
1824
vol = 0.0_dp
1825
curr = 0.0_dp
1826
port_curr = 0.0_dp
1827
trans = 0.0_dp
1828
1829
IF(.NOT. ASSOCIATED(MasterEntities)) THEN
1830
CALL Fatal(Caller,'"MasterEntities" not associated!')
1831
END IF
1832
1833
str = I2S(MasterEntities(1))
1834
DO i=2,SIZE(MasterEntities)
1835
str = TRIM(str)//' '//I2S(MasterEntities(i))
1836
END DO
1837
CALL Info(Caller,'Performing reduction for BCs: '//TRIM(str),Level=10)
1838
1839
Omega = ListGetAngularFrequency(Found=Found)
1840
IF(.NOT. Found) CALL Fatal(Caller,'We need angular frequency!')
1841
1842
t1 = Mesh % NumberOfBulkElements + 1
1843
t2 = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
1844
1845
EdgeBasis = .FALSE.
1846
UseGaussLaw = .FALSE.
1847
1848
IF(avar % dofs <= 2) THEN
1849
EdgeBasis = .TRUE.
1850
CALL EdgeElementStyle(avar % Solver % Values, PiolaVersion, BasisDegree = EdgeBasisDegree )
1851
UseGaussLaw = ListGetLogical(avar % solver % values, 'Use Gauss Law', Found)
1852
END IF
1853
1854
OutFlux = 0.0_dp
1855
area = 0.0_dp
1856
1857
IF( ALL([1,2,3,6] /= Avar % Dofs) ) THEN
1858
CALL Fatal(Caller,'Invalid number of components for vector potential!')
1859
END IF
1860
1861
InitHandles = .TRUE.
1862
DO t=t1, t2
1863
Element => Mesh % Elements(t)
1864
bc_id = Element % BoundaryInfo % Constraint
1865
IF( ALL( MasterEntities /= bc_id ) ) CYCLE
1866
BC => Model % BCs(bc_id) % Values
1867
1868
IF(EdgeBasis .AND. Element % Type % ElementCode > 300 ) THEN
1869
k = FindBoundaryFaceIndex(Mesh,Element)
1870
Element => Mesh % Faces(k)
1871
END IF
1872
IF(UseGaussLaw) THEN
1873
CALL LocalIntegBC_AV(BC, Element, InitHandles)
1874
ELSE
1875
CALL LocalIntegBC_E(BC, Element, InitHandles)
1876
END IF
1877
END DO
1878
1879
Area = ParallelReduction(Area)
1880
trans = ParallelReduction(trans)
1881
Zimp = 1.0_dp / trans
1882
1883
PortImp = Zimp
1884
1885
IF( UseGaussLaw ) THEN
1886
vol = ParallelReduction(Vol)
1887
curr = ParallelReduction(curr)
1888
port_curr = ParallelReduction(port_curr)
1889
1890
! Still testing these!
1891
curr = -curr
1892
port_curr = -port_curr
1893
1894
vol = vol / area
1895
1896
PRINT *,'LumpedCurr av:',vol,curr,port_curr,Zimp *curr, CONJG(Zimp) * port_curr
1897
1898
OutFlux = (vol + Zimp * curr) / (2*SQRT(REAL(Zimp)))
1899
InFlux = (vol - CONJG(Zimp) * port_curr ) / (2*SQRT(REAL(Zimp)))
1900
1901
! For now use just the average voltages
1902
OutFlux = vol !curr
1903
InFlux = 1.0 !port_curr
1904
PortImp = Zimp
1905
ELSE
1906
int_el = ParallelReduction(int_el)
1907
int_norm = ParallelReduction(int_norm)
1908
OutFlux = int_el
1909
InFlux = int_norm
1910
1911
PRINT *,'LumpedCurr e:',int_el,int_norm,area,trans,Zimp
1912
1913
END IF
1914
1915
1916
CALL Info(Caller,'Reduction operator finished',Level=12)
1917
1918
CONTAINS
1919
1920
!-----------------------------------------------------------------------------
1921
SUBROUTINE LocalIntegBC_E( BC, Element, InitHandles )
1922
!------------------------------------------------------------------------------
1923
TYPE(ValueList_t), POINTER :: BC
1924
TYPE(Element_t), POINTER :: Element
1925
LOGICAL :: InitHandles
1926
!------------------------------------------------------------------------------
1927
COMPLEX(KIND=dp) :: B, Zs, L(3), muinv, MagLoad(3), TemGrad(3), eps, &
1928
e_ip(3), e_ip_norm, e_ip_tan(3), f_ip_tan(3), imu, phi, eps0, mu0inv, epsr, mur
1929
REAL(KIND=dp), ALLOCATABLE :: Basis(:),dBasisdx(:,:),WBasis(:,:),RotWBasis(:,:), e_local(:,:)
1930
REAL(KIND=dp) :: weight, DetJ, Normal(3), cond, u, v, w, x, y, z, rob0
1931
TYPE(Nodes_t), SAVE :: ElementNodes, ParentNodes
1932
INTEGER, POINTER :: NodeIndexes(:), pIndexes(:), ParentIndexes(:)
1933
INTEGER, POINTER, SAVE :: EdgeIndexes(:)
1934
LOGICAL :: Stat, Found
1935
TYPE(GaussIntegrationPoints_t) :: IP
1936
INTEGER :: t, i, j, m, np, p, q, ndofs, n, nd
1937
LOGICAL :: AllocationsDone = .FALSE.
1938
TYPE(Element_t), POINTER :: Parent
1939
TYPE(ValueHandle_t), SAVE :: MagLoad_h, ElRobin_h, MuCoeff_h, Absorb_h, TemRe_h, TemIm_h
1940
TYPE(ValueHandle_t), SAVE :: CondCoeff_h, CurrDens_h, EpsCoeff_h
1941
INTEGER :: nactive
1942
1943
SAVE AllocationsDone, WBasis, RotWBasis, Basis, dBasisdx, e_local, mu0inv, eps0
1944
1945
ndofs = avar % dofs
1946
IF(.NOT. AllocationsDone ) THEN
1947
m = Mesh % MaxElementDOFs
1948
ALLOCATE( ElementNodes % x(m), ElementNodes % y(m), ElementNodes % z(m), EdgeIndexes(m), &
1949
ParentNodes % x(m), ParentNodes % y(m), ParentNodes % z(m), &
1950
WBasis(m,3), RotWBasis(m,3), Basis(m), dBasisdx(m,3), e_local(ndofs,m) )
1951
AllocationsDone = .TRUE.
1952
END IF
1953
1954
IF( InitHandles ) THEN
1955
CALL ListInitElementKeyword( ElRobin_h,'Boundary Condition','Electric Robin Coefficient',InitIm=.TRUE.)
1956
CALL ListInitElementKeyword( MagLoad_h,'Boundary Condition','Magnetic Boundary Load', InitIm=.TRUE.,InitVec3D=.TRUE.)
1957
CALL ListInitElementKeyword( Absorb_h,'Boundary Condition','Absorbing BC')
1958
CALL ListInitElementKeyword( TemRe_h,'Boundary Condition','TEM Potential')
1959
CALL ListInitElementKeyword( TemIm_h,'Boundary Condition','TEM Potential Im')
1960
1961
CALL ListInitElementKeyword( MuCoeff_h,'Material','Relative Reluctivity',InitIm=.TRUE.)
1962
CALL ListInitElementKeyword( EpsCoeff_h,'Material','Relative Permittivity',InitIm=.TRUE.)
1963
CALL ListInitElementKeyword( CondCoeff_h,'Material','Electric Conductivity')
1964
Found = .FALSE.
1965
IF( ASSOCIATED( Model % Constants ) ) THEN
1966
mu0inv = ListGetConstReal( Model % Constants,'Permeability of Vacuum', Found )
1967
IF(mu0inv/=0) mu0inv = 1/mu0inv;
1968
END IF
1969
IF(.NOT. Found ) mu0inv = 1.0_dp / ( PI * 4.0d-7 )
1970
Found = .FALSE.
1971
IF( ASSOCIATED( Model % Constants ) ) THEN
1972
eps0 = ListGetConstReal ( Model % Constants,'Permittivity of Vacuum', Found )
1973
END IF
1974
IF(.NOT. Found ) eps0 = 8.854187817d-12
1975
InitHandles = .FALSE.
1976
END IF
1977
1978
imu = CMPLX(0.0_dp, 1.0_dp)
1979
rob0 = Omega * SQRT( eps0 / mu0inv )
1980
1981
n = Element % TYPE % NumberOfNodes
1982
NodeIndexes => Element % NodeIndexes
1983
1984
ElementNodes % x(1:n) = Mesh % Nodes % x(NodeIndexes(1:n))
1985
ElementNodes % y(1:n) = Mesh % Nodes % y(NodeIndexes(1:n))
1986
ElementNodes % z(1:n) = Mesh % Nodes % z(NodeIndexes(1:n))
1987
1988
Parent => Element % BoundaryInfo % Left
1989
IF(.NOT. ASSOCIATED(Parent)) Parent => Element % BoundaryInfo % Right
1990
IF(.NOT. ASSOCIATED( Parent ) ) THEN
1991
CALL Fatal(Caller,'Model lumping requires parent element!')
1992
END IF
1993
1994
IF( EdgeBasis ) THEN
1995
np = Parent % TYPE % NumberOfNodes
1996
ParentIndexes => Parent % NodeIndexes
1997
ParentNodes % x(1:np) = Mesh % Nodes % x(ParentIndexes(1:np))
1998
ParentNodes % y(1:np) = Mesh % Nodes % y(ParentIndexes(1:np))
1999
ParentNodes % z(1:np) = Mesh % Nodes % z(ParentIndexes(1:np))
2000
2001
nd = mGetElementDofs( EdgeIndexes, Uelement = Parent, USolver = avar % Solver )
2002
np = COUNT(EdgeIndexes(1:nd) <= Mesh % NumberOfNodes)
2003
pIndexes => EdgeIndexes
2004
2005
IP = GaussPoints(Element, EdgeBasis=.TRUE., PReferenceElement=PiolaVersion, &
2006
EdgeBasisDegree=EdgeBasisDegree)
2007
ELSE
2008
nd = n
2009
IP = GaussPoints( Element )
2010
IF( avar % TYPE == Variable_on_nodes_on_elements ) THEN
2011
pIndexes => Element % DGIndexes
2012
ELSE
2013
pIndexes => NodeIndexes
2014
END IF
2015
END IF
2016
2017
! Edge or nodal values of vector potential
2018
DO i=1,avar % dofs
2019
e_local(i,1:nd) = avar % values(avar % dofs*(avar % Perm(pIndexes(1:nd))-1)+i)
2020
END DO
2021
2022
Normal = NormalVector(Element, ElementNodes, Check=.TRUE.)
2023
2024
! Numerical integration:
2025
!-----------------------
2026
DO t=1,IP % n
2027
2028
stat = ElementInfo( Element, ElementNodes, IP % U(t), IP % V(t), &
2029
IP % W(t), detJ, Basis, dBasisdx )
2030
weight = IP % s(t) * detJ
2031
2032
! Get material properties from parent element.
2033
!----------------------------------------------
2034
mur = ListGetElementComplex( MuCoeff_h, Basis, Parent, Found, GaussPoint = t )
2035
IF( .NOT. Found ) mur = 1.0_dp
2036
muinv = mur * mu0inv
2037
2038
epsr = ListGetElementComplex( EpsCoeff_h, Basis, Parent, Found, GaussPoint = t )
2039
IF( .NOT. Found ) epsr = 1.0_dp
2040
eps = epsr * eps0
2041
2042
Cond = ListGetElementReal( CondCoeff_h, Basis, Parent, Found, GaussPoint = t )
2043
2044
IF( ListGetElementLogical( Absorb_h, Element, Found ) ) THEN
2045
B = imu * rob0 * SQRT( epsr / mur )
2046
ELSE
2047
B = ListGetElementComplex( ElRobin_h, Basis, Element, Found, GaussPoint = t )
2048
END IF
2049
2050
Zs = 1.0_dp / (SQRT(REAL(muinv*eps)))
2051
2052
MagLoad = ListGetElementComplex3D( MagLoad_h, Basis, Element, Found, GaussPoint = t )
2053
TemGrad = CMPLX( ListGetElementRealGrad( TemRe_h,dBasisdx,Element,Found), &
2054
ListGetElementRealGrad( TemIm_h,dBasisdx,Element,Found) )
2055
L = ( MagLoad + TemGrad ) / ( 2*B)
2056
2057
IF( EdgeBasis ) THEN
2058
! In order to get the normal component of the electric field we must operate on the
2059
! parent element. The surface element only has tangential components.
2060
CALL FindParentUVW( Element, n, Parent, Parent % TYPE % NumberOfNodes, U, V, W, Basis )
2061
stat = ElementInfo( Parent, ParentNodes, u, v, w, detJ, Basis, dBasisdx, &
2062
EdgeBasis = Wbasis, RotBasis = RotWBasis, USolver = avar % Solver )
2063
e_ip(1:3) = CMPLX(MATMUL(e_local(1,np+1:nd),WBasis(1:nd-np,1:3)), MATMUL(e_local(2,np+1:nd),WBasis(1:nd-np,1:3)))
2064
ELSE
2065
DO i=1,3
2066
e_ip(i) = CMPLX( SUM( Basis(1:n) * e_local(i,1:n) ), SUM( Basis(1:n) * e_local(i+3,1:n) ) )
2067
END DO
2068
END IF
2069
2070
e_ip_norm = SUM(e_ip*Normal)
2071
e_ip_tan = e_ip - e_ip_norm * Normal
2072
2073
! Integral over electric field: This gives the phase
2074
int_el = int_el + weight * SUM(e_ip_tan * CONJG(L) )
2075
2076
! Norm of electric field used for normalization
2077
int_norm = int_norm + weight * ABS( SUM( L * CONJG(L) ) )
2078
2079
trans = trans + B * weight / Omega
2080
area = area + weight
2081
END DO
2082
2083
!------------------------------------------------------------------------------
2084
END SUBROUTINE LocalIntegBC_E
2085
!------------------------------------------------------------------------------
2086
2087
2088
!-----------------------------------------------------------------------------
2089
SUBROUTINE LocalIntegBC_AV( BC, Element, InitHandles )
2090
!------------------------------------------------------------------------------
2091
TYPE(ValueList_t), POINTER :: BC
2092
TYPE(Element_t), POINTER :: Element
2093
LOGICAL :: InitHandles
2094
!------------------------------------------------------------------------------
2095
COMPLEX(KIND=dp) :: tc_ip, cd_ip, v_ip, ep_ip, eps0, eps, mu0inv, muinv, mur, epsr, &
2096
cond_ip, imu
2097
REAL(KIND=dp), ALLOCATABLE :: Basis(:),dBasisdx(:,:),v_local(:,:)
2098
REAL(KIND=dp) :: weight, DetJ
2099
TYPE(Nodes_t), SAVE :: ElementNodes
2100
INTEGER, POINTER :: NodeIndexes(:), pIndexes(:), ParentIndexes(:)
2101
LOGICAL :: Found
2102
TYPE(GaussIntegrationPoints_t) :: IP
2103
INTEGER :: t, i, j, m, np, p, q, ndofs, n, nd
2104
LOGICAL :: AllocationsDone = .FALSE.
2105
TYPE(Element_t), POINTER :: Parent, MatElement
2106
TYPE(ValueHandle_t), SAVE :: MuCoeff_h, EpsCoeff_h, CondCoeff_h, ExtPot_h
2107
TYPE(ValueHandle_t), SAVE :: TransferCoeff_h, ElCurrent_h, BCMat_h
2108
2109
SAVE AllocationsDone, Basis, dBasisdx, v_local, mu0inv, eps0
2110
2111
ndofs = avar % dofs
2112
IF(.NOT. AllocationsDone ) THEN
2113
m = Mesh % MaxElementDOFs
2114
ALLOCATE( ElementNodes % x(m), ElementNodes % y(m), ElementNodes % z(m), &
2115
Basis(m), dBasisdx(m,3), v_local(ndofs,m) )
2116
AllocationsDone = .TRUE.
2117
END IF
2118
2119
! BC given with these:
2120
! Electric Transfer Coefficient
2121
! Electric Current Density / Incident Voltage
2122
IF( InitHandles ) THEN
2123
CALL ListInitElementKeyword( MuCoeff_h,'Material','Relative Reluctivity',InitIm=.TRUE.)
2124
CALL ListInitElementKeyword( EpsCoeff_h,'Material','Relative Permittivity',InitIm=.TRUE.)
2125
CALL ListInitElementKeyword( CondCoeff_h,'Material','Electric Conductivity')
2126
2127
CALL ListInitElementKeyword( TransferCoeff_h,'Boundary Condition','Electric Transfer Coefficient',InitIm=.TRUE.)
2128
CALL ListInitElementKeyword( ElCurrent_h,'Boundary Condition','Electric Current Density',InitIm=.TRUE.)
2129
CALL ListInitElementKeyword( ExtPot_h,'Boundary Condition','Incident Voltage',InitIm=.TRUE.)
2130
2131
CALL ListInitElementKeyword( BCMat_h,'Boundary Condition','Material')
2132
2133
Found = .FALSE.
2134
IF( ASSOCIATED( Model % Constants ) ) THEN
2135
mu0inv = 1.0_dp / ListGetConstReal( Model % Constants,'Permeability of Vacuum', Found )
2136
END IF
2137
IF(.NOT. Found ) mu0inv = 1.0_dp / ( PI * 4.0d-7 )
2138
Found = .FALSE.
2139
IF( ASSOCIATED( Model % Constants ) ) THEN
2140
eps0 = ListGetConstReal ( Model % Constants,'Permittivity of Vacuum', Found )
2141
END IF
2142
IF(.NOT. Found ) eps0 = 8.854187817d-12
2143
InitHandles = .FALSE.
2144
END IF
2145
2146
imu = CMPLX(0.0_dp, 1.0_dp)
2147
2148
n = Element % TYPE % NumberOfNodes
2149
NodeIndexes => Element % NodeIndexes
2150
2151
ElementNodes % x(1:n) = Mesh % Nodes % x(NodeIndexes(1:n))
2152
ElementNodes % y(1:n) = Mesh % Nodes % y(NodeIndexes(1:n))
2153
ElementNodes % z(1:n) = Mesh % Nodes % z(NodeIndexes(1:n))
2154
2155
Parent => Element % BoundaryInfo % Left
2156
IF(.NOT. ASSOCIATED(Parent)) Parent => Element % BoundaryInfo % Right
2157
IF(.NOT. ASSOCIATED( Parent ) ) THEN
2158
CALL Fatal(Caller,'Model lumping requires parent element!')
2159
END IF
2160
2161
! If we have material also define in the BC section then use it
2162
i = ListGetElementInteger( BCMat_h, Element, Found )
2163
IF( i > 0 ) THEN
2164
MatElement => Element
2165
ELSE
2166
MatElement => Parent
2167
END IF
2168
2169
nd = n
2170
IP = GaussPoints( Element )
2171
pIndexes => NodeIndexes
2172
2173
! Nodal values of scalar potential
2174
DO i=1,avar % dofs
2175
v_local(i,1:nd) = avar % values(avar % dofs*(avar % Perm(pIndexes(1:nd))-1)+i)
2176
END DO
2177
2178
! Numerical integration:
2179
!-----------------------
2180
DO t=1,IP % n
2181
2182
stat = ElementInfo( Element, ElementNodes, IP % U(t), IP % V(t), &
2183
IP % W(t), detJ, Basis, dBasisdx )
2184
weight = IP % s(t) * detJ
2185
2186
! Get material properties from parent element.
2187
!----------------------------------------------
2188
mur = ListGetElementComplex( MuCoeff_h, Basis, MatElement, Found, GaussPoint = t )
2189
IF( .NOT. Found ) mur = 1.0_dp
2190
muinv = mur * mu0inv
2191
2192
epsr = ListGetElementComplex( EpsCoeff_h, Basis, MatElement, Found, GaussPoint = t )
2193
IF( .NOT. Found ) epsr = 1.0_dp
2194
eps = epsr * eps0
2195
2196
cond_ip = ListGetElementReal( CondCoeff_h, Basis, MatElement, Found, GaussPoint = t )
2197
cd_ip = ListGetElementComplex( ElCurrent_h, Basis, Element, Found, GaussPoint = t )
2198
2199
tc_ip = ListGetElementComplex( TransferCoeff_h, Basis, Element, Found, GaussPoint = t )
2200
IF(Found) THEN
2201
ep_ip = ListGetElementComplex( ExtPot_h, Basis, Element, Found, GaussPoint = t )
2202
IF(Found) cd_ip = cd_ip + 2 * tc_ip * ep_ip
2203
END IF
2204
v_ip = CMPLX( SUM( Basis(1:n) * v_local(1,1:n) ), SUM( Basis(1:n) * v_local(2,1:n) ) )
2205
2206
area = area + weight
2207
2208
curr = curr - tc_ip * v_ip * weight
2209
port_curr = port_curr + cd_ip * weight
2210
trans = trans + tc_ip * weight
2211
vol = vol + v_ip * weight
2212
END DO
2213
2214
!------------------------------------------------------------------------------
2215
END SUBROUTINE LocalIntegBC_AV
2216
!------------------------------------------------------------------------------
2217
2218
END FUNCTION BoundaryWaveFlux
2219
2220
2221
END MODULE LumpingUtils
2222
!------------------------------------------------------------------------------
2223
2224
2225
!> \}
2226
2227
2228
2229