Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/GeometryFitting.F90
5192 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
27
! * Email: [email protected]
28
! * Web: http://www.csc.fi/elmer
29
! * Address: CSC - IT Center for Science Ltd.
30
! * Keilaranta 14
31
! * 02101 Espoo, Finland
32
! *
33
! * Original Date: 02 Apr 2001
34
! *
35
! *****************************************************************************/
36
37
!> \ingroup ElmerLib
38
!> \{
39
40
!------------------------------------------------------------------------------
41
!> Utilities used for interface conditions of various types including mortar.
42
!------------------------------------------------------------------------------
43
44
MODULE GeometryFitting
45
46
USE Types
47
USE Messages
48
USE ElementDescription
49
USE ElementUtils, ONLY : TangentDirections
50
USE Interpolation, ONLY : CopyElementNodesFromMesh
51
USE Lists
52
USE ParallelUtils
53
IMPLICIT NONE
54
55
CONTAINS
56
57
58
!---------------------------------------------------------------------------
59
! Simply fitting of cylinder into a point cloud. This is done in two phases.
60
! 1) The axis of the cylinder is found by minimizing the \sum((n_i*t)^2)
61
! for each component of of t where n_i:s are the surface normals.
62
! This is fully generic and assumes no positions.
63
! 2) The radius and center point of the cylinder are found by fitting a circle
64
! in the chosen plane to three representative points. Currently the fitting
65
! can only be done in x-y plane.
66
!---------------------------------------------------------------------------
67
SUBROUTINE CylinderFit(PMesh, PParams, BCind, dim, FitParams)
68
!---------------------------------------------------------------------------
69
TYPE(Mesh_t), POINTER :: PMesh
70
TYPE(Valuelist_t), POINTER :: PParams
71
INTEGER, OPTIONAL :: BCind
72
INTEGER, OPTIONAL :: dim
73
REAL(KIND=dp), OPTIONAL :: FitParams(:)
74
75
INTEGER :: i,j,k,n,t,AxisI,iter,cdim,ierr
76
INTEGER, POINTER :: NodeIndexes(:)
77
TYPE(Element_t), POINTER :: Element
78
TYPE(Nodes_t) :: Nodes
79
REAL(KIND=dp) :: NiNj(9),A(3,3),F(3),M11,M12,M13,M14
80
REAL(KIND=dp) :: d1,d2,MinDist,MaxDist,Dist,X0,Y0,Rad
81
REAL(KIND=dp) :: Normal(3), AxisNormal(3), Tangent1(3), Tangent2(3), Coord(3), &
82
CircleCoord(9)
83
#ifdef ELMER_BROKEN_MPI_IN_PLACE
84
REAL(KIND=dp) :: buffer(9)
85
#endif
86
INTEGER :: CircleInd(3)
87
LOGICAL :: BCMode, DoIt, GotNormal, GotCenter, GotRadius
88
INTEGER :: Tag, t1, t2
89
LOGICAL, ALLOCATABLE :: ActiveNode(:)
90
REAL(KIND=dp), POINTER :: rArray(:,:)
91
92
BCMode = PRESENT( BCind )
93
94
! Set the range for the possible active elements.
95
IF( BCMode ) THEN
96
t1 = PMesh % NumberOfBulkElements + 1
97
t2 = PMesh % NumberOfBulkElements + PMesh % NumberOfBoundaryElements
98
Tag = CurrentModel % BCs(BCind) % Tag
99
ALLOCATE( ActiveNode( PMesh % NumberOfNodes ) )
100
ActiveNode = .FALSE.
101
ELSE
102
t1 = 1
103
t2 = PMesh % NumberOfBulkElements
104
END IF
105
106
! If this is a line mesh there is really no need to figure out the
107
! direction of the rotational axis. It can only be aligned with the z-axis.
108
DO t=t1, t2
109
Element => PMesh % Elements(t)
110
IF( BCMode ) THEN
111
IF( .NOT. ASSOCIATED( Element % BoundaryInfo ) ) CYCLE
112
IF ( Element % BoundaryInfo % Constraint /= Tag ) CYCLE
113
END IF
114
IF( Element % TYPE % ElementCode < 300 ) THEN
115
cdim = 2
116
ELSE
117
cdim = 3
118
END IF
119
EXIT
120
END DO
121
122
IF( BcMode ) THEN
123
cdim = ParallelReduction( cdim, 2 )
124
END IF
125
126
AxisNormal = 0.0_dp
127
IF( cdim == 2 ) THEN
128
GotNormal = .TRUE.
129
AxisNormal(3) = 1.0_dp
130
ELSE
131
rArray => ListGetConstRealArray( PParams,'Cylinder Normal',GotNormal)
132
IF( GotNormal) AxisNormal(1:3) = rArray(1:3,1)
133
END IF
134
135
Coord = 0.0_dp
136
rArray => ListGetConstRealArray( PParams,'Cylinder Center',GotCenter)
137
IF( GotCenter) Coord(1:cdim) = rArray(1:cdim,1)
138
139
Rad = ListGetConstReal( PParams,'Cylinder Radius',GotRadius)
140
141
! Do we have the fitting done already?
142
IF( GotNormal .AND. GotCenter .AND. GotRadius ) THEN
143
IF( PRESENT(FitParams) ) THEN
144
CALL Info('CylinderFit','Using cylinder parameters from list',Level=25)
145
FitParams(1:cdim) = Coord(1:cdim)
146
IF( cdim == 2 ) THEN
147
FitParams(3) = Rad
148
ELSE
149
FitParams(4:6) = AxisNormal
150
FitParams(7) = Rad
151
END IF
152
END IF
153
RETURN
154
END IF
155
156
n = PMesh % MaxElementNodes
157
ALLOCATE( Nodes % x(n), Nodes % y(n), Nodes % z(n) )
158
159
160
! Compute the inner product of <N*N> for the elements
161
NiNj = 0.0_dp
162
DO t=t1, t2
163
Element => PMesh % Elements(t)
164
165
n = Element % TYPE % NumberOfNodes
166
NodeIndexes => Element % NodeIndexes
167
168
IF( BCMode ) THEN
169
IF( .NOT. ASSOCIATED( Element % BoundaryInfo ) ) CYCLE
170
IF ( Element % BoundaryInfo % Constraint /= Tag ) CYCLE
171
ActiveNode(Element % NodeIndexes(1:n)) = .TRUE.
172
END IF
173
174
! If we know the Normal we only tag the boundary nodes
175
IF(GotNormal) CYCLE
176
177
Nodes % x(1:n) = PMesh % Nodes % x(NodeIndexes(1:n))
178
Nodes % y(1:n) = PMesh % Nodes % y(NodeIndexes(1:n))
179
Nodes % z(1:n) = PMesh % Nodes % z(NodeIndexes(1:n))
180
181
Normal = NormalVector( Element, Nodes, Check = .FALSE. )
182
DO i=1,3
183
DO j=1,3
184
NiNj(3*(i-1)+j) = NiNj(3*(i-1)+j) + Normal(i) * Normal(j)
185
END DO
186
END DO
187
END DO
188
189
IF(GotNormal) GOTO 100
190
191
! Only in BC mode we do currently parallel reduction.
192
! This could be altered too.
193
IF( BCMode ) THEN
194
#ifdef ELMER_BROKEN_MPI_IN_PLACE
195
buffer = NiNj
196
CALL MPI_ALLREDUCE(buffer, &
197
#else
198
CALL MPI_ALLREDUCE(MPI_IN_PLACE, &
199
#endif
200
NiNj,9,MPI_DOUBLE_PRECISION,MPI_SUM,ELMER_COMM_WORLD,ierr)
201
END IF
202
203
! The potential direction for the cylinder axis is the direction with
204
! least hits for the normal.
205
AxisI = 1
206
DO i=2,3
207
IF( NiNj(3*(i-1)+i) < NiNj(3*(AxisI-1)+AxisI) ) AxisI = i
208
END DO
209
210
CALL Info('CylinderFit','Axis coordinate set to be: '//I2S(AxisI))
211
212
! Keep the dominating direction fixed and iteratively solve the two other directions
213
AxisNormal = 0.0_dp
214
AxisNormal(AxisI) = 1.0_dp
215
216
! Basically we could solve from equation Ax=0 the tangent but only up to a constant.
217
! Thus we enforce the axis direction to one by manipulation the matrix equation
218
! thereby can get a unique solution.
219
DO i=1,3
220
DO j=1,3
221
A(i,j) = NiNj(3*(i-1)+j)
222
END DO
223
END DO
224
A(AxisI,1:3) = 0.0_dp
225
A(AxisI,AxisI) = 1.0_dp
226
CALL InvertMatrix( A, 3 )
227
AxisNormal = A(1:3,AxisI)
228
229
! Normalize the axis normal length to one
230
AxisNormal = AxisNormal / SQRT( SUM( AxisNormal ** 2 ) )
231
IF( 1.0_dp - MAXVAL( ABS( AxisNormal ) ) > 1.0d-5 ) THEN
232
CALL Warn('CylinderFit','The cylinder axis is not aligned with any axis!')
233
END IF
234
235
100 CALL TangentDirections( AxisNormal,Tangent1,Tangent2 )
236
237
IF( InfoActive(25) .AND. ParEnv % MyPe == 0 ) THEN
238
PRINT *,'Axis Normal:',AxisNormal
239
PRINT *,'Axis Tangent 1:',Tangent1
240
PRINT *,'Axis Tangent 2:',Tangent2
241
i = PMesh % NumberOfNodes
242
IF(BcMode) THEN
243
PRINT *,'Active nodes: ',i,COUNT(ActiveNode)
244
END IF
245
END IF
246
247
! Finding three points with maximum distance in the tangent directions
248
249
! First, find the single extremum point in the first tangent direction
250
! Save the local coordinates in the N-T system of the cylinder
251
MinDist = HUGE(MinDist)
252
MaxDist = -HUGE(MaxDist)
253
254
CIrcleInd = 0
255
DO i=1, PMesh % NumberOfNodes
256
IF( BCMode ) THEN
257
IF( .NOT. ActiveNode(i) ) CYCLE
258
END IF
259
260
Coord(1) = PMesh % Nodes % x(i)
261
Coord(2) = PMesh % Nodes % y(i)
262
Coord(3) = PMesh % Nodes % z(i)
263
264
d1 = SUM( Tangent1 * Coord )
265
IF( d1 < MinDist ) THEN
266
MinDist = d1
267
CircleInd(1) = i
268
END IF
269
IF( d1 > MaxDist ) THEN
270
MaxDist = d1
271
CircleInd(2) = i
272
END IF
273
END DO
274
275
CircleCoord = -HUGE(CircleCoord)
276
DO j=1,2
277
i = CircleInd(j)
278
279
IF( BCMode .AND. ParEnv % PEs > 1 ) THEN
280
IF(j==1) THEN
281
Dist = ParallelReduction( MinDist, 1 )
282
IF(ABS(MinDist-Dist) > 1.0e-8) CYCLE
283
ELSE IF(j==2) THEN
284
Dist = ParallelReduction( MaxDist, 2)
285
IF(ABS(MaxDist-Dist) > 1.0e-8) CYCLE
286
END IF
287
END IF
288
289
Coord(1) = PMesh % Nodes % x(i)
290
Coord(2) = PMesh % Nodes % y(i)
291
Coord(3) = PMesh % Nodes % z(i)
292
293
CircleCoord(3*(j-1)+1) = SUM( Tangent1 * Coord )
294
CircleCoord(3*(j-1)+2) = SUM( Tangent2 * Coord )
295
CircleCoord(3*(j-1)+3) = SUM( AxisNormal * Coord )
296
END DO
297
298
IF( BCMode .AND. ParEnv % PEs > 1 ) THEN
299
#ifdef ELMER_BROKEN_MPI_IN_PLACE
300
buffer = CircleCoord
301
CALL MPI_ALLREDUCE(buffer, &
302
#else
303
CALL MPI_ALLREDUCE(MPI_IN_PLACE, &
304
#endif
305
CircleCoord,6,MPI_DOUBLE_PRECISION,MPI_MAX,ELMER_COMM_WORLD,ierr)
306
END IF
307
308
IF( InfoActive(25) .AND. ParEnv % MyPe == 0 ) THEN
309
PRINT *,'Circle Coord:',CircleCoord(1:6)
310
END IF
311
312
! Find one more point such that their minimum distance to the previous point(s)
313
! is maximized. This takes some time but the further the nodes are apart the more
314
! accurate it will be to fit the circle to the points. Also if there is just
315
! a symmetric section of the cylinder it is important to find the points rigorously.
316
j = 3
317
! The maximum minimum distance of any node from the previously defined nodes
318
MaxDist = 0.0_dp
319
DO i=1, PMesh % NumberOfNodes
320
IF( BCMode ) THEN
321
IF( .NOT. ActiveNode(i) ) CYCLE
322
END IF
323
Coord(1) = PMesh % Nodes % x(i)
324
Coord(2) = PMesh % Nodes % y(i)
325
Coord(3) = PMesh % Nodes % z(i)
326
327
! Minimum distance from the previously defined nodes
328
MinDist = HUGE(MinDist)
329
DO k=1,j-1
330
d1 = SUM( Tangent1 * Coord )
331
d2 = SUM( Tangent2 * Coord )
332
Dist = ( d1 - CircleCoord(3*(k-1)+1) )**2 + ( d2 - CircleCoord(3*(k-1)+2) )**2
333
MinDist = MIN( Dist, MinDist )
334
END DO
335
336
! If the minimum distance to either previous selelected nodes
337
! is greater than in any other node, choose this
338
IF( MaxDist < MinDist ) THEN
339
MaxDist = MinDist
340
CircleInd(j) = i
341
END IF
342
END DO
343
344
! Ok, we have found the point now set the circle coordinates
345
DoIt = .TRUE.
346
IF( BCMode .AND. ParEnv % PEs > 1 ) THEN
347
Dist = ParallelReduction( MaxDist, 2 )
348
DoIt = ( ABS(MaxDist-Dist) < 1.0e-8 )
349
END IF
350
351
IF( DoIt ) THEN
352
i = CircleInd(j)
353
Coord(1) = PMesh % Nodes % x(i)
354
Coord(2) = PMesh % Nodes % y(i)
355
Coord(3) = PMesh % Nodes % z(i)
356
357
CircleCoord(3*(j-1)+1) = SUM( Tangent1 * Coord )
358
CircleCoord(3*(j-1)+2) = SUM( Tangent2 * Coord )
359
CircleCoord(3*(j-1)+3) = SUM( AxisNormal * Coord )
360
END IF
361
362
IF( BCMode .AND. ParEnv % PEs > 1 ) THEN
363
#ifdef ELMER_BROKEN_MPI_IN_PLACE
364
buffer = CircleCoord
365
CALL MPI_ALLREDUCE(buffer, &
366
#else
367
CALL MPI_ALLREDUCE(MPI_IN_PLACE, &
368
#endif
369
CircleCoord,9,MPI_DOUBLE_PRECISION,MPI_MAX,ELMER_COMM_WORLD,ierr)
370
END IF
371
372
IF( InfoActive(25) .AND. ParEnv % MyPe == 0 ) THEN
373
DO i=1,3
374
PRINT *,'Circle Coord:',i,CircleInd(i),CircleCoord(3*i-2:3*i)
375
END DO
376
END IF
377
378
! Given three nodes it is possible to analytically compute the center point and
379
! radius of the cylinder from a 4x4 determinant equation. The matrices values
380
! m1i are the determinants of the comatrices.
381
382
A(1:3,1) = CircleCoord(1::3) ! x
383
A(1:3,2) = CircleCoord(2::3) ! y
384
A(1:3,3) = 1.0_dp
385
m11 = Det3x3( a )
386
387
A(1:3,1) = CircleCoord(1::3)**2 + CircleCoord(2::3)**2 ! x^2+y^2
388
A(1:3,2) = CircleCoord(2::3) ! y
389
A(1:3,3) = 1.0_dp
390
m12 = Det3x3( a )
391
392
A(1:3,1) = CircleCoord(1::3)**2 + CircleCoord(2::3)**2 ! x^2+y^2
393
A(1:3,2) = CircleCoord(1::3) ! x
394
A(1:3,3) = 1.0_dp
395
m13 = Det3x3( a )
396
397
A(1:3,1) = CircleCoord(1::3)**2 + CircleCoord(2::3)**2 ! x^2+y^2
398
A(1:3,2) = CircleCoord(1::3) ! x
399
A(1:3,3) = CircleCoord(2::3) ! y
400
m14 = Det3x3( a )
401
402
IF(InfoActive(25) .AND. ParEnv % Mype == 0 ) THEN
403
PRINT *,'CylinderFit determinants:',m11,m12,m13,m14
404
END IF
405
406
IF( ABS( m11 ) < EPSILON( m11 ) ) THEN
407
CALL Fatal('CylinderFit','Points cannot be an a circle')
408
END IF
409
410
X0 = 0.5_dp * m12 / m11
411
Y0 = -0.5_dp * m13 / m11
412
rad = SQRT( x0**2 + y0**2 + m14/m11 )
413
414
Coord = x0 * Tangent1 + y0 * Tangent2
415
416
IF( InfoActive(25) .AND. ParEnv % MyPe == 0) THEN
417
PRINT *,'Cylinder center and radius:',Coord, rad
418
END IF
419
420
ALLOCATE( rArray(3,1) )
421
rArray(1:3,1) = Coord
422
CALL ListAddConstRealArray( PParams,'Cylinder Center', 3, 1, rArray )
423
IF(.NOT. GotNormal ) THEN
424
rArray(1:3,1) = AxisNormal
425
CALL ListAddConstRealArray( PParams,'Cylinder Normal', 3, 1, rArray )
426
END IF
427
DEALLOCATE( rArray )
428
CALL ListAddConstReal( PParams,'Cylinder Radius',rad )
429
430
IF( PRESENT( FitParams ) ) THEN
431
IF( cdim == 2 ) THEN
432
FitParams(1:2) = Coord(1:2)
433
FitParams(3) = rad
434
ELSE
435
FitParams(1:3) = Coord(1:3)
436
FitParams(4:6) = AxisNormal(1:3)
437
FitParams(7) = rad
438
END IF
439
440
IF( InfoActive(25) .AND. ParEnv % MyPe == 0) THEN
441
PRINT *,'Cylinder FitParams: ',FitParams
442
END IF
443
444
END IF
445
446
DEALLOCATE( Nodes % x, Nodes % y, Nodes % z )
447
448
END SUBROUTINE CylinderFit
449
450
451
! Computes the center of a mesh or given set of bodies.
452
!----------------------------------------------------------------------------
453
SUBROUTINE ComputeEntityCenter(Mesh, Center, TargetBodies, TargetBCs)
454
TYPE(Mesh_t), POINTER :: Mesh
455
REAL(KIND=dp) :: Center(3)
456
INTEGER, POINTER, OPTIONAL :: TargetBodies(:)
457
INTEGER, POINTER, OPTIONAL :: TargetBCs(:)
458
459
REAL(KIND=dp), ALLOCATABLE :: Basis(:)
460
REAL(KIND=dp) :: DetJ,r(3),s
461
INTEGER :: t,t1,tend,i,j,k,n,ierr
462
LOGICAL :: stat
463
TYPE(Element_t), POINTER :: Element
464
TYPE(Nodes_t), SAVE :: Nodes
465
TYPE(GaussIntegrationPoints_t) :: IP
466
REAL(KIND=dp) :: Volume,SerTmp(4),ParTmp(4)
467
468
n = Mesh % MaxElementNodes
469
ALLOCATE( Basis(n) )
470
471
Volume = 0.0_dp
472
Center = 0.0_dp
473
474
IF(PRESENT(TargetBCs)) THEN
475
t1 = Mesh % NumberOfBulkElements+1
476
tend = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
477
ELSE
478
t1 = 1
479
tend = Mesh % NumberOfBulkElements
480
END IF
481
482
DO t=t1, tend
483
Element => Mesh % Elements(t)
484
IF( PRESENT( TargetBodies ) ) THEN
485
IF( ALL( TargetBodies /= Element % BodyId ) ) CYCLE
486
END IF
487
IF( PRESENT( TargetBCs ) ) THEN
488
IF(.NOT. ASSOCIATED(Element % BoundaryInfo) ) CYCLE
489
i = Element % BoundaryInfo % Constraint
490
IF( ALL( TargetBCs /= i ) ) CYCLE
491
END IF
492
493
n = Element % Type % NumberOfNodes
494
CALL CopyElementNodesFromMesh(Nodes,Mesh,n,Element % NodeIndexes)
495
496
! Numerical integration:
497
!----------------------
498
IP = GaussPoints(Element)
499
500
DO k=1,IP % n
501
! Basis function values & derivatives at the integration point:
502
!--------------------------------------------------------------
503
stat = ElementInfo( Element, Nodes, IP % U(k), IP % V(k), &
504
IP % W(k), detJ, Basis )
505
506
r(1) = SUM(Nodes % x(1:n) * Basis(1:n))
507
r(2) = SUM(Nodes % y(1:n) * Basis(1:n))
508
r(3) = SUM(Nodes % z(1:n) * Basis(1:n))
509
s = IP % s(k) * detJ
510
511
Volume = Volume + s
512
Center = Center + s * r
513
END DO
514
END DO
515
516
IF( ParEnv % PEs > 1 ) THEN
517
SerTmp(1:3) = Center
518
SerTmp(4) = Volume
519
CALL MPI_ALLREDUCE(SerTmp,ParTmp,4,MPI_DOUBLE_PRECISION,MPI_SUM,ELMER_COMM_WORLD,ierr)
520
Center = ParTmp(1:3)
521
Volume = ParTmp(4)
522
END IF
523
524
IF( Volume < EPSILON( Volume ) ) CALL Fatal('ComputeEntityCenter','Entity has no volume!')
525
526
Center = Center / Volume
527
528
WRITE( Message,'(A,ES12.4)') 'Body volume:',Volume
529
CALL Info('ComputeEntityCenter',Message,Level=20)
530
531
WRITE( Message,'(A,3ES12.4)') 'Body center:',Center
532
CALL Info('ComputeEntityCenter',Message,Level=20)
533
534
END SUBROUTINE ComputeEntityCenter
535
536
537
! Computes the normal of inertia of a mesh or given set of bodies.
538
!----------------------------------------------------------------------------
539
SUBROUTINE ComputeEntityInertiaNormal(Mesh, Center, INormal, TargetBodies, TargetBCs)
540
TYPE(Mesh_t), POINTER :: Mesh
541
REAL(KIND=dp) :: Center(3)
542
REAL(KIND=dp) :: INormal(3)
543
INTEGER, POINTER, OPTIONAL :: TargetBodies(:)
544
INTEGER, POINTER, OPTIONAL :: TargetBCs(:)
545
546
REAL(KIND=dp), ALLOCATABLE :: Basis(:)
547
REAL(KIND=dp) :: DetJ,r(3),s
548
INTEGER :: t,t1,tend,i,j,k,n,ierr
549
LOGICAL :: stat
550
TYPE(Element_t), POINTER :: Element
551
TYPE(Nodes_t), SAVE :: Nodes
552
REAL(KIND=dp) :: Imoment(9), EigVec(3,3), EigVal(3), ParTmp(9), CP(3)
553
REAL(KIND=dp) :: EigWrk(20)
554
INTEGER :: EigInfo, Three
555
TYPE(GaussIntegrationPoints_t) :: IP
556
557
n = Mesh % MaxElementNodes
558
ALLOCATE( Basis(n) )
559
Imoment = 0.0_dp
560
561
IF(PRESENT(TargetBCs)) THEN
562
t1 = Mesh % NumberOfBulkElements+1
563
tend = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
564
ELSE
565
t1 = 1
566
tend = Mesh % NumberOfBulkElements
567
END IF
568
569
DO t=t1,tend
570
Element => Mesh % Elements(t)
571
IF( PRESENT( TargetBodies ) ) THEN
572
IF( ALL( TargetBodies /= Element % BodyId ) ) CYCLE
573
END IF
574
575
n = Element % Type % NumberOfNodes
576
CALL CopyElementNodesFromMesh(Nodes,Mesh,n,Element % NodeIndexes)
577
578
! Numerical integration:
579
!----------------------
580
IP = GaussPoints(Element)
581
DO k=1,IP % n
582
! Basis function values & derivatives at the integration point:
583
!--------------------------------------------------------------
584
stat = ElementInfo( Element, Nodes, IP % U(k), IP % V(k), &
585
IP % W(k), detJ, Basis )
586
587
r(1) = SUM(Nodes % x(1:n) * Basis(1:n))
588
r(2) = SUM(Nodes % y(1:n) * Basis(1:n))
589
r(3) = SUM(Nodes % z(1:n) * Basis(1:n))
590
s = IP % s(k) * detJ
591
r = r - Center
592
593
DO i=1,3
594
Imoment(3*(i-1)+i) = Imoment(3*(i-1)+i) + s * SUM( r**2 )
595
DO j=1,3
596
Imoment(3*(i-1)+j) = Imoment(3*(i-1)+j) - s * r(i) * r(j)
597
END DO
598
END DO
599
END DO
600
END DO
601
602
IF( ParEnv % PEs > 1 ) THEN
603
CALL MPI_ALLREDUCE(Imoment,ParTmp,9,MPI_DOUBLE_PRECISION,MPI_SUM,ELMER_COMM_WORLD,ierr)
604
Imoment = ParTmp
605
END IF
606
607
s = 1.0_dp
608
DO i=1,3
609
DO j=1,3
610
EigVec(i,j) = Imoment(3*(i-1)+j)
611
END DO
612
EigVec(i,i) = EigVec(i,i) - s
613
END DO
614
615
EigInfo = 0
616
Three = 3
617
618
CALL DSYEV( 'V','U', Three, EigVec, Three, EigVal, EigWrk, SIZE(EigWrk), EigInfo )
619
IF (EigInfo /= 0) THEN
620
CALL Fatal('ComputeEntityIntertiaNormal', 'DSYEV cannot generate eigen basis')
621
END IF
622
623
WRITE( Message,'(A,3ES12.4)') 'Mesh inertia eigenvalues:',EigVal
624
CALL Info('ComputeEntityIntertiaNormal',Message,Level=30)
625
INormal = EigVec(:,3) ! axis of maximum inertia
626
627
! Check the sign of the normal using the right-hand-rule.
628
! This is not generic but a rule is still a rule
629
CP = CrossProduct( Center, INormal )
630
j = 1
631
DO i = 2, 3
632
IF( ABS( CP(i) ) > ABS( CP(j) ) ) j = i
633
END DO
634
IF( CP(j) < 0 ) THEN
635
CALL Info('ComputeEntityIntertiaNormal','Inverting sign of normal',Level=20)
636
INormal = -INormal
637
END IF
638
639
END SUBROUTINE ComputeEntityInertiaNormal
640
641
642
643
!---------------------------------------------------------------------------
644
SUBROUTINE TorusFit(PMesh, PParams, BCind, FitParams)
645
!---------------------------------------------------------------------------
646
TYPE(Mesh_t), POINTER :: PMesh
647
TYPE(Valuelist_t), POINTER :: PParams
648
INTEGER, OPTIONAL :: BCind
649
REAL(KIND=dp), OPTIONAL :: FitParams(:)
650
651
REAL(KIND=dp) :: Center(3), Normal(3), Rminor, Rmajor, rArray(3,1)
652
LOGICAL :: Found
653
INTEGER, POINTER :: EntityInds(:)
654
REAL(KIND=dp), POINTER :: pArray(:,:)
655
656
ALLOCATE(EntityInds(1))
657
EntityInds(1) = BCInd
658
659
pArray => ListGetConstRealArray( PParams,'Torus Center',Found)
660
IF(Found ) THEN
661
Center(1:3) = pArray(1:3,1)
662
ELSE
663
CALL ComputeEntityCenter(PMesh, Center, TargetBCs = EntityInds )
664
rArray(1:3,1) = Center
665
CALL ListAddConstRealArray( PParams,'Torus Center',3,1,rArray)
666
END IF
667
668
pArray => ListGetConstRealArray( PParams,'Torus Normal',Found )
669
IF(Found ) THEN
670
Normal(1:3) = pArray(1:3,1)
671
ELSE
672
CALL ComputeEntityInertiaNormal(PMesh, Center, Normal, TargetBCs = EntityInds )
673
rArray(1:3,1) = Normal
674
CALL ListAddConstRealArray( PParams,'Torus Normal',3,1,rArray)
675
END IF
676
677
Rmajor = ListGetConstReal( PParams,'Torus Radius',UnfoundFatal=.TRUE.)
678
Rminor = ListGetConstReal( PParams,'Torus Minor Radius',UnfoundFatal=.TRUE.)
679
680
FitParams(1:3) = Center
681
FitParams(4:6) = Normal
682
FitParams(7) = Rmajor
683
FitParams(8) = Rminor
684
685
DEALLOCATE(EntityInds)
686
687
END SUBROUTINE TorusFit
688
689
690
! Code for fitting a sphere. Not yet used.
691
!-------------------------------------------------------------------------
692
SUBROUTINE SphereFit(Mesh, Params, BCind, FitParams )
693
TYPE(Mesh_t), POINTER :: Mesh
694
TYPE(ValueList_t), POINTER :: Params
695
INTEGER, OPTIONAL :: BCind
696
REAL(KIND=dp), OPTIONAL :: FitParams(:)
697
698
INTEGER :: i,j,t,t1,t2,NoNodes,Tag
699
LOGICAL :: BCMode
700
LOGICAL, ALLOCATABLE :: ActiveNode(:)
701
TYPE(Element_t), POINTER :: Element
702
REAL(KIND=dp), POINTER :: x(:),y(:),z(:)
703
REAL(KIND=dp) :: xc,yc,zc,Rad
704
705
IF( PRESENT( FitParams ) ) THEN
706
IF( ListCheckPresent( Params,'Sphere Radius') ) THEN
707
CALL Info('SphereFit','Using predefined values for sphere parameters',Level=20)
708
FitParams(1) = ListGetConstReal( Params,'Sphere Center X')
709
FitParams(2) = ListGetConstReal( Params,'Sphere Center Y')
710
FitParams(3) = ListGetConstReal( Params,'Sphere Center Z')
711
FitParams(4) = ListGetConstReal( Params,'Sphere Radius')
712
RETURN
713
END IF
714
END IF
715
716
CALL Info('SphereFit','Trying to fit a sphere to element patch',Level=6)
717
718
! Set the range for the possible active elements.
719
IF( PRESENT( BCind ) ) THEN
720
BCMode = .TRUE.
721
t1 = Mesh % NumberOfBulkElements + 1
722
t2 = Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
723
Tag = CurrentModel % BCs(BCind) % Tag
724
ALLOCATE( ActiveNode( Mesh % NumberOfNodes ) )
725
ActiveNode = .FALSE.
726
ELSE
727
BCMode = .FALSE.
728
t1 = 1
729
t2 = Mesh % NumberOfBulkElements
730
END IF
731
732
! Mark the nodes that belong to the active elements.
733
! 1) Either we only have bulk elements in which case we use all of the nodes or
734
! 2) We are given a boundary index and only use the nodes related to it.
735
DO t=t1,t2
736
Element => Mesh % Elements(t)
737
IF( BCMode ) THEN
738
IF( .NOT. ASSOCIATED( Element % BoundaryInfo ) ) CYCLE
739
IF ( Element % BoundaryInfo % Constraint /= Tag ) CYCLE
740
ActiveNode(Element % NodeIndexes) = .TRUE.
741
END IF
742
END DO
743
744
! If all nodes are active just use pointers to the nodes.
745
! Otherwise create list of the nodes.
746
IF( BCMode ) THEN
747
NoNodes = COUNT( ActiveNode )
748
ALLOCATE( x(NoNodes), y(NoNodes), z(NoNodes) )
749
j = 0
750
DO i=1,Mesh % NumberOfNodes
751
IF(.NOT. ActiveNode(i) ) CYCLE
752
j = j + 1
753
x(j) = Mesh % Nodes % x(i)
754
y(j) = Mesh % Nodes % y(i)
755
z(j) = Mesh % Nodes % z(i)
756
END DO
757
ELSE
758
NoNodes = Mesh % NumberOfNodes
759
x => Mesh % Nodes % x
760
y => Mesh % Nodes % y
761
z => Mesh % Nodes % z
762
END IF
763
764
! Call the function to set the sphere parameters for the nodes.
765
CALL SphereFitfun(NoNodes,x,y,z,xc,yc,zc,Rad)
766
767
IF( BCMode ) THEN
768
DEALLOCATE(ActiveNode,x,y,z)
769
END IF
770
771
! Add the sphere parameters to the list so that they can be used later
772
! directly without having to fit the parameters again.
773
CALL ListAddConstReal( Params,'Sphere Center X',xc )
774
CALL ListAddConstReal( Params,'Sphere Center Y',yc )
775
CALL ListAddConstReal( Params,'Sphere Center Z',zc )
776
CALL ListAddConstReal( Params,'Sphere Radius',Rad )
777
778
IF( PRESENT( FitParams ) ) THEN
779
FitParams(1) = xc
780
FitParams(2) = yc
781
FitParams(3) = zc
782
FitParams(4) = Rad
783
END IF
784
785
CONTAINS
786
787
788
! Sumith YD: Fast Geometric Fit Algorithm for Sphere Using Exact Solution
789
!------------------------------------------------------------------------
790
SUBROUTINE SphereFitfun(n,x,y,z,xc,yc,zc,R)
791
INTEGER :: n
792
REAL(KIND=dp), POINTER :: x(:),y(:),z(:)
793
REAL(KIND=dp) :: xc,yc,zc,R
794
795
REAL(KIND=dp) :: Sx,Sy,Sz,Sxx,Syy,Szz,Sxy,Sxz,Syz,&
796
Sxxx,Syyy,Szzz,Syzz,Sxyy,Sxzz,Sxxy,Sxxz,Syyz,&
797
A1,a,b,c,d,e,f,g,h,j,k,l,m,delta
798
799
Sx = SUM(x); Sy = SUM(y); Sz = SUM(z);
800
Sxx = SUM(x*x); Syy = SUM(y*y);
801
Szz = SUM(z*z); Sxy = SUM(x*y);
802
Sxz = SUM(x*z); Syz = SUM(y*z);
803
Sxxx = SUM(x*x*x); Syyy = SUM(y*y*y);
804
Szzz = SUM(z*z*z); Sxyy = SUM(x*y*y);
805
Sxzz = SUM(x*z*z); Sxxy = SUM(x*x*y);
806
Sxxz = SUM(x*x*z); Syyz = SUM(y*y*z);
807
Syzz = SUM(y*z*z);
808
809
! We must do parallel reduction here if the surface is split among
810
! several MPI processes.
811
IF( BCMode .AND. ParEnv % PEs > 1 ) THEN
812
Sx = ParallelReduction(Sx); Sy = ParallelReduction(Sy); Sz = ParallelReduction(Sz);
813
Sxx = ParallelReduction(Sxx); Syy = ParallelReduction(Syy);
814
Szz = ParallelReduction(Szz); Sxy = ParallelReduction(Sxy);
815
Sxz = ParallelReduction(Sxz); Syz = ParallelReduction(Syz);
816
Sxxx = ParallelReduction(Sxxx); Syyy = ParallelReduction(Syyy);
817
Szzz = ParallelReduction(Szzz); Sxyy = ParallelReduction(Sxyy);
818
Sxzz = ParallelReduction(Sxzz); Sxxy = ParallelReduction(Sxxy);
819
Sxxz = ParallelReduction(Sxxz); Syyz = ParallelReduction(Syyz);
820
Syzz = ParallelReduction(Syzz);
821
END IF
822
823
A1 = Sxx +Syy +Szz;
824
a = 2*Sx*Sx-2*N*Sxx;
825
b = 2*Sx*Sy-2*N*Sxy;
826
c = 2*Sx*Sz-2*N*Sxz;
827
d = -N*(Sxxx +Sxyy +Sxzz)+A1*Sx;
828
e = 2*Sx*Sy-2*N*Sxy;
829
f = 2*Sy*Sy-2*N*Syy;
830
g = 2*Sy*Sz-2*N*Syz;
831
h = -N*(Sxxy +Syyy +Syzz)+A1*Sy;
832
j = 2*Sx*Sz-2*N*Sxz;
833
k = 2*Sy*Sz-2*N*Syz;
834
l = 2*Sz*Sz-2*N*Szz;
835
m = -N*(Sxxz +Syyz + Szzz)+A1*Sz;
836
delta = a*(f*l - g*k)-e*(b*l-c*k) + j*(b*g-c*f);
837
838
xc = (d*(f*l-g*k) -h*(b*l-c*k) +m*(b*g-c*f))/delta;
839
yc = (a*(h*l-m*g) -e*(d*l-m*c) +j*(d*g-h*c))/delta;
840
zc = (a*(f*m-h*k) -e*(b*m-d*k) +j*(b*h-d*f))/delta;
841
R = SQRT(xc*xc+yc*yc+zc*zc+(A1-2*(xc*Sx+yc*Sy+zc*Sz))/N);
842
843
END SUBROUTINE SphereFitfun
844
845
END SUBROUTINE SphereFit
846
847
848
849
END MODULE GeometryFitting
850
851
852