Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/CutFEMUtils.F90
5264 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: 01 Oct 1996
34
! *
35
! ****************************************************************************/
36
37
!> \ingroup ElmerLib
38
!> \{
39
40
!------------------------------------------------------------------------------
41
!> Module containing utilities for CutFEM style of strategies.
42
!------------------------------------------------------------------------------
43
44
MODULE CutFemUtils
45
USE Types
46
USE Lists
47
USE ElementUtils, ONLY : FreeMatrix
48
USE Interpolation, ONLY : CopyElementNodesFromMesh
49
USE ElementDescription
50
USE MatrixAssembly
51
USE MeshUtils, ONLY : AllocateMesh, FindMeshEdges, MeshStabParams
52
USE ModelDescription, ONLY : FreeMesh
53
USE SolverUtils, ONLY : GaussPointsAdapt, SolveLinearSystem, VectorValuesRange
54
USE ParallelUtils
55
USE MeshUtils, ONLY : PointInMesh
56
57
IMPLICIT NONE
58
59
PRIVATE
60
61
LOGICAL :: CutExtend, CutExtrapolate
62
LOGICAL, ALLOCATABLE :: CutDof(:)
63
INTEGER, POINTER :: ExtendPerm(:) => NULL(), OrigMeshPerm(:) => NULL(), &
64
CutPerm(:) => NULL(), PhiPerm(:) => NULL()
65
REAL(KIND=dp), POINTER :: OrigMeshValues(:) => NULL(), CutValues(:) => NULL(), &
66
ExtendValues(:) => NULL(), PhiValues(:) => NULL(), &
67
OrigPrevMeshValues(:,:) => NULL(), PrevCutValues(:,:) => NULL()
68
INTEGER, POINTER :: OrigActiveElements(:), AddActiveElements(:), UnsplitActiveElements(:)
69
REAL(KIND=dp), ALLOCATABLE, TARGET :: CutInterp(:)
70
TYPE(Matrix_t), POINTER :: NodeMatrix
71
INTEGER :: CutFemBody
72
CHARACTER(:), ALLOCATABLE :: CutStr
73
INTEGER :: CutDofs = 0
74
INTEGER :: nCase(20)
75
76
#define DEBUG_ORIENT 0
77
#if DEBUG_ORIENT
78
REAL(KIND=dp) :: CutFEMCenter(3)
79
#endif
80
81
82
PUBLIC :: CreateCutFEMMatrix, CreateCutFEMMesh, CreateCutFEMPerm, CreateCutFEMAddMesh, &
83
CutFEMVariableFinalize, CutFEMSetOrigMesh, CutFEMSetAddMesh, LevelSetUpdate, &
84
CutInterfaceBC, CutInterfaceBulk, CutInterfaceCheck
85
86
PUBLIC :: CutInterp
87
88
TYPE(Mesh_t), POINTER :: CutFEMOrigMesh => NULL(), CutFEMAddMesh => NULL()
89
90
91
CONTAINS
92
93
94
! Given a levelset function create a permutation that tells which
95
! edges and which nodes are being cut by the zero levelset.
96
! Optionally also create a permutation for the outside mesh.
97
!------------------------------------------------------------------
98
SUBROUTINE CreateCutFEMPerm(Solver,UpdateCoords)
99
TYPE(Solver_t) :: Solver
100
LOGICAL :: UpdateCoords
101
102
TYPE(Mesh_t), POINTER :: Mesh
103
TYPE(ValueList_t), POINTER :: Params
104
105
INTEGER :: i,j,k,nn,ne,body_in,body_out,body_cut,InsideCnt(3),dofs
106
REAL(KIND=dp) :: h1,h2,hprod,Eps,r,MaxRat
107
INTEGER, POINTER :: NodeIndexes(:)
108
TYPE(Variable_t), POINTER :: Var, PhiVar
109
TYPE(Element_t), POINTER :: Element, pElement
110
CHARACTER(:), ALLOCATABLE :: str
111
LOGICAL :: Found, PassiveInside, PassiveOutside, isCut, isMore, UseAbsEps, Hit
112
REAL(KIND=dp), POINTER :: xtmp(:)
113
LOGICAL :: UpdateOrigCoords
114
CHARACTER(*), PARAMETER :: Caller = 'CreateCutFEMPerm'
115
116
117
Params => Solver % Values
118
Mesh => Solver % Mesh
119
120
! Memorize original nodal variable and matrix.
121
OrigMeshValues => NULL()
122
OrigPrevMeshValues => NULL()
123
OrigMeshPerm => NULL()
124
IF(ASSOCIATED(Solver % Variable ) ) THEN
125
IF(ASSOCIATED(Solver % Variable % Perm ) ) THEN
126
OrigMeshValues => Solver % Variable % Values
127
OrigMeshPerm => Solver % Variable % Perm
128
OrigPrevMeshValues => Solver % Variable % PrevValues
129
END IF
130
CutDofs = Solver % Variable % dofs
131
dofs = CutDofs
132
END IF
133
134
CutFEMOrigMesh => Solver % Mesh
135
OrigActiveElements => Solver % ActiveElements
136
137
NodeMatrix => Solver % Matrix
138
139
CutExtend = ListGetLogical( Params,'CutFEM extend',Found )
140
CutExtrapolate = ListGetLogical( Params,'CutFEM extrapolate',Found )
141
UpdateOrigCoords = ListGetLogical( Params,'CutFEM bodyfitted',Found )
142
143
! We always need mesh edges since the new dofs are created in intersections of levelset and edge.
144
IF(ASSOCIATED(Mesh % edges)) THEN
145
CALL Info(Caller,'Mesh edges already created!',Level=12)
146
ELSE
147
CALL Info(Caller,'Create element edges',Level=10)
148
CALL FindMeshEdges( Mesh )
149
150
! We need global numbering for the edges that we use for the unique numbering of new nodes
151
IF( ParEnv % PEs > 1 ) THEN
152
CALL Info(Caller,'Numbering Mesh edges in parallel')
153
CALL SParEdgeNumbering(Mesh)
154
END IF
155
END IF
156
157
nn = Mesh % NumberOfNodes
158
ne = Mesh % NumberOfEdges
159
160
IF( UpdateCoords ) THEN
161
i = SIZE(Mesh % Nodes % x)
162
IF(i < nn + ne ) THEN
163
CALL Info(Caller,'Enlarging node coordinates for edge cuts from '&
164
//I2S(i)//' to '//I2S(nn+ne),Level=7)
165
ALLOCATE(xtmp(nn))
166
xtmp = Mesh % Nodes % x(1:nn)
167
DEALLOCATE(Mesh % Nodes % x)
168
ALLOCATE(Mesh % Nodes % x(nn+ne))
169
Mesh % Nodes % x(1:nn) = xtmp
170
171
xtmp = Mesh % Nodes % y(1:nn)
172
DEALLOCATE(Mesh % Nodes % y)
173
ALLOCATE(Mesh % Nodes % y(nn+ne))
174
Mesh % Nodes % y(1:nn) = xtmp
175
176
xtmp = Mesh % Nodes % z(1:nn)
177
DEALLOCATE(Mesh % Nodes % z)
178
ALLOCATE(Mesh % Nodes % z(nn+ne))
179
Mesh % Nodes % z(1:nn) = xtmp
180
DEALLOCATE(xtmp)
181
END IF
182
END IF
183
184
IF(.NOT. ALLOCATED(CutDof) ) THEN
185
CALL Info(Caller,'Allocating "CutDof" field to indicate levelset cuts!',Level=20)
186
ALLOCATE( CutDof(nn+ne) )
187
END IF
188
CutDof = .FALSE.
189
190
! We store the cut for future interpolation.
191
IF(.NOT. ALLOCATED(CutInterp)) THEN
192
CALL Info(Caller,'Allocating "CutInterp" for edge related interpolation!',Level=20)
193
ALLOCATE(CutInterp(ne))
194
END IF
195
CutInterp = 0.0_dp
196
197
CutStr = ListGetString( Params,'Levelset Variable', Found)
198
IF( .NOT. Found ) CutStr = "surface"
199
200
PhiVar => VariableGet(Mesh % Variables, CutStr, ThisOnly=.TRUE.)
201
IF(.NOT. ASSOCIATED(PhiVar) ) THEN
202
CALL Fatal(Caller,'"Levelset Variable" not available: '//TRIM(CutStr))
203
END IF
204
PhiValues => PhiVar % Values
205
PhiPerm => PhiVar % Perm
206
207
body_in = ListGetInteger( Params,'CutFEm Inside Body',Found )
208
IF(.NOT. Found) body_in = CurrentModel % NumberOfBodies
209
body_out = ListGetInteger( Params,'CutFem Outside Body',Found )
210
IF(.NOT. Found) body_out = body_in+1
211
body_cut = MAX(body_in,body_out)
212
213
! This is a little dirty, we set the interface elements so we recognize them.
214
IF(CutExtend) body_cut = body_cut + 1
215
216
Eps = ListGetCReal(Params,'CutFem Epsilon',Found )
217
IF(.NOT. Found) Eps = 1.0e-3
218
UseAbsEps = ListGetLogical(Params,'CutFEM Epsilon Absolute',Found )
219
220
221
! First mark the cut nodes.
222
! These could maybe be part of the same loop as well but I separated when testing something.
223
DO i=1, Mesh % NumberOfEdges
224
NodeIndexes => Mesh % Edges(i) % NodeIndexes
225
IF(ANY(PhiPerm(NodeIndexes) == 0)) CYCLE
226
h1 = PhiValues(PhiPerm(NodeIndexes(1)))
227
h2 = PhiValues(PhiPerm(NodeIndexes(2)))
228
hprod = h1*h2
229
IF( hprod < 0.0_dp ) THEN
230
r = ABS(h2)/(ABS(h1)+ABS(h2))
231
Hit = .FALSE.
232
IF( UseAbsEps ) THEN
233
IF(ABS(h2) < Eps ) THEN
234
CutDof(NodeIndexes(2)) = .TRUE.
235
Hit = .TRUE.
236
END IF
237
IF(ABS(h1) < Eps ) THEN
238
CutDof(NodeIndexes(1)) = .TRUE.
239
Hit = .TRUE.
240
END IF
241
ELSE
242
IF( r <= Eps ) THEN
243
CutDof(NodeIndexes(2)) = .TRUE.
244
Hit = .TRUE.
245
END IF
246
IF((1.0-r < Eps) ) THEN
247
CutDof(NodeIndexes(1)) = .TRUE.
248
Hit = .TRUE.
249
END IF
250
END IF
251
ELSE IF( ABS(hprod) < 1.0d-20 ) THEN
252
IF(ABS(h1) < 1.0e-20) CutDof(NodeIndexes(1)) = .TRUE.
253
IF(ABS(h2) < 1.0e-20) CutDof(NodeIndexes(2)) = .TRUE.
254
END IF
255
END DO
256
257
258
IF(ParEnv % PEs > 1 ) THEN
259
BLOCK
260
INTEGER, POINTER :: Perm(:)
261
INTEGER :: ni
262
REAL(KIND=dp), POINTER :: CutDofR(:)
263
264
ni = COUNT( CutDof(1:nn) .AND. Mesh % ParallelInfo % GInterface(1:nn) )
265
ni = ParallelReduction( ni )
266
267
IF( ni > 0 ) THEN
268
ALLOCATE(CutDofR(nn),Perm(nn))
269
CutDofR = 0.0_dp
270
DO i=1,nn
271
Perm(i) = i
272
END DO
273
274
WHERE( CutDof(1:nn) )
275
CutDofR = 1.0_dp
276
END WHERE
277
CALL ExchangeNodalVec( Mesh % ParallelInfo, Perm, CutDofR, op = 2)
278
DO i=1,nn
279
IF(CutDofR(i) > 0.5_dp ) CutDof(i) = .TRUE.
280
END DO
281
DEALLOCATE(CutDofR, Perm )
282
END IF
283
END BLOCK
284
END IF
285
286
287
! Then mark the edges trying to avoid nearby cuts.
288
InsideCnt = 0
289
j = 0
290
291
! This is an add'hoc value that represents the maximum aspect ratio of elements in the mesh.
292
MaxRat = 2.0
293
294
DO i=1, Mesh % NumberOfEdges
295
NodeIndexes => Mesh % Edges(i) % NodeIndexes
296
IF(ANY(PhiPerm(NodeIndexes)==0)) CYCLE
297
h1 = PhiValues(PhiPerm(NodeIndexes(1)))
298
h2 = PhiValues(PhiPerm(NodeIndexes(2)))
299
hprod = h1*h2
300
IF( hprod < 0.0_dp ) THEN
301
r = ABS(h2)/(ABS(h1)+ABS(h2))
302
Hit = .FALSE.
303
304
! We may have a sloppier rule if the dof is already cut?
305
! If the rule is exactly the same then no need for separate loop.
306
IF( r <= MaxRat * Eps ) THEN
307
IF(CutDof(NodeIndexes(2))) CYCLE
308
ELSE IF((1.0-r < MaxRat * Eps) ) THEN
309
IF(CutDof(NodeIndexes(1))) CYCLE
310
END IF
311
312
j = j+1
313
CutDof(nn+i) = .TRUE.
314
315
! The interpolation weight should always be [0,1]
316
IF(r < 0.0 .OR. r > 1.0) THEN
317
PRINT *,'Invalid cutinterp:',i,j,r
318
END IF
319
320
CutInterp(i) = r
321
322
! We update nodes so that the element on-the-fly can point to then using NodeIndexes.
323
IF( UpdateCoords ) THEN
324
Mesh % Nodes % x(nn+i) = (1-r) * Mesh % Nodes % x(NodeIndexes(2)) + &
325
r * Mesh % Nodes % x(NodeIndexes(1))
326
Mesh % Nodes % y(nn+i) = (1-r) * Mesh % Nodes % y(NodeIndexes(2)) + &
327
r * Mesh % Nodes % y(NodeIndexes(1))
328
Mesh % Nodes % z(nn+i) = (1-r) * Mesh % Nodes % z(NodeIndexes(2)) + &
329
r * Mesh % Nodes % z(NodeIndexes(1))
330
END IF
331
END IF
332
END DO
333
334
! Should we update the original coords for nodes which closely match the levelset but not exactly.
335
! This would be the case if we want to follow the body fitted shape of a object as closely as possible.
336
! We would not want to do in transient cases
337
IF(UpdateOrigCoords) THEN
338
BLOCK
339
LOGICAL, ALLOCATABLE :: MovedNode(:)
340
REAL(KIND=dp), ALLOCATABLE :: TmpCoords(:,:)
341
ALLOCATE(MovedNode(nn), TmpCoords(nn,3))
342
MovedNode = .FALSE.
343
TmpCoords = 0.0_dp
344
DO i=1, Mesh % NumberOfEdges
345
NodeIndexes => Mesh % Edges(i) % NodeIndexes
346
IF(.NOT. ANY(CutDOF(NodeIndexes))) CYCLE
347
348
h1 = PhiValues(PhiPerm(NodeIndexes(1)))
349
h2 = PhiValues(PhiPerm(NodeIndexes(2)))
350
hprod = h1*h2
351
IF( hprod >= 0.0_dp ) CYCLE
352
353
r = ABS(h2)/(ABS(h1)+ABS(h2))
354
IF( r <= Eps ) THEN
355
j = 2
356
ELSE IF((1.0-r < Eps) ) THEN
357
j = 1
358
ELSE
359
CYCLE
360
END IF
361
362
k = NodeIndexes(j)
363
IF(.NOT. CutDof(k)) CYCLE
364
IF(MovedNode(k)) CYCLE
365
366
TmpCoords(k,1) = (1-r) * Mesh % Nodes % x(NodeIndexes(2)) + &
367
r * Mesh % Nodes % x(NodeIndexes(1))
368
TmpCoords(k,2) = (1-r) * Mesh % Nodes % y(NodeIndexes(2)) + &
369
r * Mesh % Nodes % y(NodeIndexes(1))
370
TmpCoords(k,3) = (1-r) * Mesh % Nodes % z(NodeIndexes(2)) + &
371
r * Mesh % Nodes % z(NodeIndexes(1))
372
MovedNode(k) = .TRUE.
373
END DO
374
k = COUNT(MovedNode)
375
CALL Info(Caller,'Moved cut nodes to be exactly at zero levelset!')
376
377
WHERE(MovedNode(1:nn))
378
Mesh % Nodes % x(1:nn) = TmpCoords(1:nn,1)
379
Mesh % Nodes % y(1:nn) = TmpCoords(1:nn,2)
380
Mesh % Nodes % z(1:nn) = TmpCoords(1:nn,3)
381
END WHERE
382
DEALLOCATE(MovedNode, TmpCoords)
383
END BLOCK
384
END IF
385
386
387
IF(InfoActive(25)) THEN
388
PRINT *,'CutInterp interval:',MINVAL(CutInterp),MAXVAL(CutInterp)
389
PRINT *,'Nodes split',COUNT(CutDof(1:nn))
390
PRINT *,'Edges cut',COUNT(CutDof(nn+1:nn+ne))
391
END IF
392
393
! Set the material for inside/outside or interface material.
394
! This way we do not need to have too complicated material sections.
395
CutFEMBody = 0
396
DO i=1,Mesh % NumberOfBulkElements
397
Element => Mesh % Elements(i)
398
399
NodeIndexes => Element % NodeIndexes
400
IF(ANY(PhiPerm(NodeIndexes) == 0)) CYCLE
401
402
! So far we assume that there is only one body index used to define the CutFEM region.
403
! We are tampering with the index, so we need to store it.
404
IF(CutFEMBody == 0) THEN
405
CutFEMBody = Element % BodyId
406
ELSE
407
IF(CutFemBody /= Element % BodyId ) THEN
408
CALL Fatal(Caller,'Modify code to deal with several bodies!')
409
END IF
410
END IF
411
412
j = -1
413
IF(ANY(CutDof(nn + Element % EdgeIndexes)) ) THEN
414
! Some edge is split => interface element
415
j = body_cut
416
InsideCnt(3) = InsideCnt(3)+1
417
ELSE
418
! Also at interface element if we have diagonal split in a quad.
419
IF(Element % TYPE % ElementCode / 100 == 4 ) THEN
420
IF(ALL(CutDof(NodeIndexes([1,3])))) THEN
421
j = body_cut
422
ELSE IF(ALL(CutDof(NodeIndexes([2,4])))) THEN
423
j = body_cut
424
END IF
425
END IF
426
427
! Ok, no interface. Use the min/max value to indicate whether this is inside/outside.
428
IF(j<0) THEN
429
h1 = MAXVAL(PhiValues(PhiPerm(NodeIndexes)))
430
h2 = MINVAL(PhiValues(PhiPerm(NodeIndexes)))
431
IF(h1 > -h2) THEN
432
InsideCnt(2) = InsideCnt(2)+1
433
j = body_out
434
ELSE
435
InsideCnt(1) = InsideCnt(1)+1
436
j = body_in
437
END IF
438
ELSE
439
InsideCnt(3) = InsideCnt(3)+1
440
END IF
441
END IF
442
443
Element % BodyId = j
444
END DO
445
446
IF(InfoActive(25)) THEN
447
PRINT *,'Inside/Outside count:',InsideCnt
448
END IF
449
450
! CutPerm is the reordered dofs for the CutFEM mesh.
451
IF(.NOT. ASSOCIATED(CutPerm)) THEN
452
ALLOCATE(CutPerm(nn+ne))
453
CALL info(Caller,'Allocated CutPerm of size: '//I2S(nn+ne),Level=20)
454
END IF
455
CutPerm = 0
456
457
PassiveOutside = ListGetLogical( Params,'CutFEM Passive Outside',Found )
458
IF(.NOT. Found ) PassiveOutside = (body_out == 0)
459
PassiveInside = ListGetLogical( Params,'CutFEM Passive Inside',Found )
460
IF(.NOT. Found) PassiveInside = (body_in == 0)
461
462
! Set all cut dofs to exist.
463
WHERE(CutDof)
464
CutPerm = 1
465
END WHERE
466
IF( PassiveOutside ) THEN
467
DO i=1,nn
468
j = PhiPerm(i)
469
IF(j==0) CYCLE
470
IF(PhiValues(j) < 0) CutPerm(i) = 1
471
END DO
472
ELSE IF( PassiveInside ) THEN
473
DO i=1,nn
474
j = PhiPerm(i)
475
IF(j==0) CYCLE
476
IF(PhiValues(j) > 0) CutPerm(i) = 1
477
END DO
478
ELSE
479
! We both inside and outside exist.
480
CutPerm(1:nn) = 1
481
END IF
482
483
j = 0
484
DO i=1,nn+ne
485
IF(CutPerm(i) > 0) THEN
486
j = j+1
487
CutPerm(i) = j
488
END IF
489
END DO
490
k = COUNT(CutPerm(1:nn)>0)
491
CALL Info(Caller,'CutFEM number of nodes: '//I2S(j)//' (original '//I2S(k)//')',Level=7)
492
493
494
! If there is a primary variable associated to the original mesh copy that to the new mesh.
495
IF(ASSOCIATED(OrigMeshValues)) THEN
496
IF(ASSOCIATED(CutValues)) DEALLOCATE(CutValues)
497
ALLOCATE(CutValues(dofs*j))
498
CutValues = 0.0_dp
499
500
DO i=1,dofs
501
WHERE(CutPerm(1:nn) > 0 )
502
CutValues(dofs*(CutPerm-1)+i) = OrigMeshValues(dofs*(OrigMeshPerm-1)+i)
503
END WHERE
504
END DO
505
506
! Point the permutation and values to the newly allocated vectors.
507
! This way
508
Solver % Variable % Perm => CutPerm
509
Solver % Variable % Values => CutValues
510
511
! For transient problems do the same for PrevValues
512
IF(ASSOCIATED(OrigPrevMeshValues)) THEN
513
IF(ASSOCIATED(PrevCutValues)) DEALLOCATE(PrevCutValues)
514
i = SIZE(OrigPrevMeshValues,2)
515
ALLOCATE(PrevCutValues(dofs*j,i))
516
PrevCutValues = 0.0_dp
517
518
! Copy nodal values as initial guess to cut fem values.
519
#if 0
520
! fix this
521
DO l=1,dofs
522
DO i=1,nn
523
j = CutPerm(i)
524
k = OrigMeshPerm(i)
525
IF(j==0 .OR. k==0) CYCLE
526
OrigMeshValues(dofs*(k-1)+l) = CutValues(dofs*(j-1)+l)
527
END DO
528
END DO
529
#endif
530
531
DO i=1,SIZE(OrigPrevMeshValues,2)
532
DO j=1,dofs
533
WHERE(CutPerm(1:nn) > 0 )
534
PrevCutValues(dofs*(CutPerm(1:nn)-1)+j,i) = &
535
OrigPrevMeshValues(dofs*(OrigMeshPerm(1:nn)-1)+j,i)
536
END WHERE
537
END DO
538
END DO
539
Solver % Variable % PrevValues => PrevCutValues
540
END IF
541
END IF
542
543
! This in an optional routine if we want to extend the field values outside
544
! active domain. The reason might be to provide better initial values for the new territory.
545
IF(CutExtend) THEN
546
CALL Info(Caller,'Extending field outside the active domain!',Level=20)
547
r = ListGetCReal( Params,'CutFEM extend width',Found )
548
549
IF(.NOT. ASSOCIATED(ExtendPerm)) THEN
550
ALLOCATE(ExtendPerm(nn+ne))
551
END IF
552
ExtendPerm = 0
553
554
r = ListGetCReal( Params,'CutFEM extend width',Found )
555
556
! Set the material for inside/outside.
557
DO i=1,Mesh % NumberOfBulkElements
558
Element => Mesh % Elements(i)
559
IF(ANY(PhiPerm(Element % NodeIndexes) == 0)) CYCLE
560
561
IF( Element % BodyId == body_cut ) THEN
562
! Mark dofs to extend on elements which lack CutFEM dofs.
563
10 pElement => CutInterfaceBulk(Element,isCut,isMore)
564
IF(ANY(CutPerm(pElement % NodeIndexes) == 0) ) THEN
565
ExtendPerm( pElement % NodeIndexes ) = 1
566
END IF
567
IF(IsMore) GOTO 10
568
! Ok, revert the dirty flag.
569
Element % BodyId = body_cut-1
570
ELSE
571
IF( ALL( CutPerm( Element % NodeIndexes ) == 0) ) THEN
572
IF( Found ) THEN
573
! Mark all dofs within a defined width.
574
IF(MINVAL(ABS(PhiVar % Values(PhiVar % Perm(Element % NodeIndexes )))) < r ) THEN
575
ExtendPerm( Element % NodeIndexes ) = 1
576
END IF
577
ELSE
578
! Mark all elements in the outside region.
579
ExtendPerm( Element % NodeIndexes ) = 1
580
END IF
581
END IF
582
END IF
583
END DO
584
585
j = 0
586
DO i=1,nn+ne
587
IF(ExtendPerm(i) == 0) CYCLE
588
j = j+1
589
ExtendPerm(i) = j
590
END DO
591
592
k = COUNT(ExtendPerm > 0 .AND. CutPerm > 0 )
593
CALL Info(Caller,'Interface dofs '//I2S(j)//' (shared '//I2S(k)//')')
594
END IF
595
596
597
! This is a dirty way to halt the progress when levelset goes beyond the planned
598
! outer boundaries.
599
BLOCK
600
TYPE(ValueList_t), POINTER :: BC
601
INTEGER :: bc_id
602
k = Mesh % NumberOfBulkElements
603
DO i=1,Mesh % NumberOfBoundaryElements
604
Element => Mesh % Elements(k+i)
605
NodeIndexes => Element % NodeIndexes
606
607
DO bc_id=1,CurrentModel % NumberOfBCs
608
IF ( Element % BoundaryInfo % Constraint == CurrentModel % BCs(bc_id) % Tag ) EXIT
609
END DO
610
IF ( bc_id > CurrentModel % NumberOfBCs ) CYCLE
611
612
BC => CurrentModel % BCs(bc_id) % Values
613
IF(ListGetLogical(BC,'CutFem Forbidden Boundary',Found ) ) THEN
614
IF(ANY(CutPerm(nn+Element % EdgeIndexes)>0)) THEN
615
CALL Fatal(Caller,'CutFEM extends beyond forbidden boundaries!')
616
END IF
617
END IF
618
END DO
619
END BLOCK
620
621
#if DEBUG_ORIENT
622
r = HUGE(r)
623
DO i=1,Mesh % NumberOfNodes
624
j = PhiPerm(i)
625
IF(j==0) CYCLE
626
IF(PhiValues(j) < r) THEN
627
r = PhiValues(j)
628
CutFEMCenter(1) = Mesh % Nodes % x(i)
629
CutFEMCenter(2) = Mesh % Nodes % y(i)
630
CutFEMCenter(3) = Mesh % Nodes % z(i)
631
END IF
632
END DO
633
PRINT *,'CutFEMCenter:',CutFEMCenter
634
#endif
635
636
! This is just counter for different split cases while developing the code.
637
nCase = 0
638
639
Solver % CutInterp => CutInterp
640
641
END SUBROUTINE CreateCutFEMPerm
642
643
644
! Given a permutation, create a matrix. We assume simple nodal elements.
645
! Some extra dofs are created since at the interface we assume that
646
! there can be all possible connections.
647
!-----------------------------------------------------------------------
648
FUNCTION CreateCutFemMatrix(Solver,Perm,MimicMat) RESULT ( A )
649
TYPE(Solver_t) :: Solver
650
INTEGER :: Perm(:)
651
TYPE(Matrix_t), POINTER :: A
652
TYPE(Matrix_t), POINTER, OPTIONAL :: MimicMat
653
654
TYPE(Mesh_t), POINTER :: Mesh
655
INTEGER :: i,j,k,l,t,m,n,dofs,nn,active
656
INTEGER, ALLOCATABLE :: BlockInds(:),DofInds(:)
657
TYPE(Element_t), POINTER :: Element
658
INTEGER, SAVE :: AllocVecs(3)
659
CHARACTER(*), PARAMETER :: Caller = 'CreateCutFemMatrix'
660
661
Mesh => Solver % Mesh
662
CutDofs = Solver % Variable % Dofs
663
dofs = CutDofs
664
665
! Create new matrix
666
A => AllocateMatrix()
667
A % FORMAT = MATRIX_LIST
668
669
! Add extreme entry since list matrix likes to be allocated at once.
670
n = dofs * MAXVAL(Perm)
671
IF(n==0) THEN
672
CALL Warn(Caller,'CutFEM matrix size is zero?')
673
A % NumberOfRows = 0
674
RETURN
675
END IF
676
677
CALL Info(Caller,'Size of CutFEM matrix with '//I2S(dofs)//' dofs is: '//I2S(n),Level=10)
678
679
680
CALL List_AddToMatrixElement(A % ListMatrix, n, n, 0.0_dp )
681
682
n = 2*Mesh % MaxElementNodes
683
ALLOCATE(BlockInds(n),DofInds(n*dofs))
684
BlockInds = 0
685
686
nn = Mesh % NumberOfNodes
687
688
DO t=1,Mesh % NumberOfBulkElements
689
Element => Mesh % Elements(t)
690
IF(ANY(PhiPerm(Element % NodeIndexes) == 0)) CYCLE
691
692
! Add active node indexes.
693
m = 0
694
n = Element % TYPE % NumberOfNodes
695
DO i=1,n
696
j = Perm(Element % NodeIndexes(i))
697
IF(j==0) CYCLE
698
m = m+1
699
BlockInds(m) = j
700
END DO
701
702
! Add active edge indexes after node indexes.
703
n = Element % TYPE % NumberOfEdges
704
DO i=1,n
705
j = Perm(nn + Element % EdgeIndexes(i))
706
IF(j==0) CYCLE
707
m = m+1
708
BlockInds(m) = j
709
END DO
710
711
! For vector valued problems add the number of dof indeces.
712
IF( dofs == 1 ) THEN
713
DofInds(1:m) = BlockInds(1:m)
714
ELSE
715
DO i=0,dofs-1
716
DofInds(m*i+1:(m+1)*i) = dofs*(BlockInds(1:m)-1)+(i+1)
717
END DO
718
m = m*dofs
719
END IF
720
721
! Add locations to matrix. We add zeros since we are only creating the topology, not assembling.
722
DO i=1,m
723
DO j=1,m
724
CALL List_AddToMatrixElement(A % ListMatrix,DofInds(i),DofInds(j),0.0_dp)
725
END DO
726
END DO
727
END DO
728
729
! Make a CRS matrix that has now a topology to account for all entries coming from cutfem.
730
CALL List_toCRSMatrix(A)
731
CALL CRS_SortMatrix(A,.FALSE.)
732
733
IF(.NOT. ASSOCIATED(A % rhs)) THEN
734
ALLOCATE(A % rhs(A % NumberOfRows))
735
END IF
736
A % rhs = 0.0_dp
737
738
739
! MimicMat is the matrix which we should replace. So if it is transient, it will have MassValues etc.
740
IF(PRESENT(MimicMat)) THEN
741
! If the matrix does not exist, do not update.
742
IF(ASSOCIATED(MimicMat)) THEN
743
AllocVecs = 0
744
IF(ASSOCIATED(MimicMat % MassValues)) AllocVecs(1) = 1
745
IF(ASSOCIATED(MimicMat % DampValues)) AllocVecs(2) = 1
746
IF(ASSOCIATED(MimicMat % Force)) AllocVecs(3) = SIZE(MimicMat % Force,2)
747
END IF
748
IF(AllocVecs(1) > 0 ) THEN
749
ALLOCATE(A % MassValues(SIZE(A % Values)))
750
A % MassValues = 0.0_dp
751
END IF
752
IF(AllocVecs(2) > 0) THEN
753
ALLOCATE(A % DampValues(SIZE(A % Values)))
754
A % DampValues = 0.0_dp
755
END IF
756
n = AllocVecs(3)
757
IF(n > 0 ) THEN
758
ALLOCATE(A % Force(A % NumberOfRows,n))
759
A % Force = 0.0_dp
760
END IF
761
END IF
762
763
END FUNCTION CreateCutFemMatrix
764
765
766
! This is a routine that just checks whether an element is cut.
767
!----------------------------------------------------------------
768
SUBROUTINE CutInterfaceCheck( Element, IsCut, IsActive, ExtPerm )
769
TYPE(Element_t), POINTER :: Element
770
LOGICAL :: IsCut, IsActive
771
INTEGER, POINTER, OPTIONAL :: ExtPerm(:)
772
773
INTEGER, SAVE :: n_split, n_cut, n_act
774
INTEGER :: j,j2,j3,nn,ne
775
LOGICAL :: Visited = .FALSE.
776
INTEGER, POINTER :: Perm(:)
777
TYPE(Mesh_t), POINTER :: Mesh
778
CHARACTER(*), PARAMETER :: Caller = 'CutInterfaceCheck'
779
780
SAVE Visited, Mesh, nn
781
782
IF(.NOT. Visited) THEN
783
Mesh => CurrentModel % Solver % Mesh
784
nn = Mesh % NumberOfNodes
785
Visited = .TRUE.
786
END IF
787
788
IF( PRESENT(ExtPerm) ) THEN
789
Perm => ExtPerm
790
ELSE
791
Perm => CurrentModel % Solver % Variable % Perm
792
END IF
793
794
n_split = COUNT( CutDof(nn + Element % EdgeIndexes) )
795
n_cut = COUNT( CutDof(Element % NodeIndexes) )
796
797
n_act = COUNT( Perm(Element % NodeIndexes) > 0 )
798
799
IsCut = ( n_split > 0 .OR. n_cut > 1 )
800
IsActive = (n_act == Element % TYPE % numberOfNodes ) .OR. IsCut
801
802
END SUBROUTINE CutInterfaceCheck
803
804
805
! Given Element, levelset function and the CutDof field return information whether the element
806
! is cut and if it, should we call the routine again for the next split.
807
!----------------------------------------------------------------------------------------------
808
FUNCTION CutInterfaceBulk( Element, IsCut, IsMore ) RESULT ( pElement )
809
TYPE(Element_t), POINTER :: Element, pElement
810
LOGICAL :: IsCut
811
LOGICAL :: IsMore
812
813
TYPE(Element_t), TARGET :: Elem303, Elem404, Elem706, Elem808
814
TYPE(Element_t), POINTER :: prevElement
815
INTEGER :: SgnNode, i, n, nn, ElemType, body_out, body_in, CutCnt
816
LOGICAL :: Found
817
REAL(KIND=dp), POINTER :: x(:), y(:), z(:)
818
CHARACTER(:), ALLOCATABLE :: str
819
TYPE(Variable_t), POINTER :: PhiVar !Var
820
TYPE(Mesh_t), POINTER :: Mesh
821
TYPE(Solver_t), POINTER :: Solver => NULL()
822
CHARACTER(*), PARAMETER :: Caller = 'CutInterfaceBulk'
823
TYPE(Nodes_t) :: ElemNodes
824
INTEGER, ALLOCATABLE :: LocalInds(:), ElemInds(:)
825
LOGICAL, ALLOCATABLE :: ElemCut(:)
826
827
828
SAVE Mesh, Solver, x, y, z, Elem303, Elem404, body_in, body_out, &
829
nn, CutCnt, PhiVar, ElemNodes, ElemInds, ElemCut, ElemType, LocalInds, &
830
prevElement
831
832
IF(.NOT. ASSOCIATED( Solver, CurrentModel % Solver ) ) THEN
833
Mesh => CurrentModel % Solver % Mesh
834
Solver => CurrentModel % Solver
835
836
IF(.NOT. ASSOCIATED(ElemNodes % x)) THEN
837
n = 8
838
ALLOCATE( ElemNodes % x(n), ElemNodes % y(n), ElemNodes % z(n), ElemInds(n), &
839
ElemCut(n), LocalInds(4))
840
END IF
841
842
nn = Mesh % NumberOfNodes
843
x => Mesh % Nodes % x
844
y => Mesh % Nodes % y
845
z => Mesh % Nodes % z
846
847
! Create empty element skeletons that are filled when splitting elements.
848
Elem303 % TYPE => GetElementType(303)
849
ALLOCATE(Elem303 % NodeIndexes(3))
850
Elem303 % NodeIndexes = 0
851
852
Elem404 % TYPE => GetElementType(404)
853
ALLOCATE(Elem404 % NodeIndexes(4))
854
Elem404 % NodeIndexes = 0
855
856
PhiVar => VariableGet(Mesh % Variables, CutStr, ThisOnly=.TRUE.)
857
IF(.NOT. ASSOCIATED(PhiVar) ) THEN
858
CALL Fatal(Caller,'"Levelset Variable" not available: '//TRIM(CutStr))
859
END IF
860
861
body_in = ListGetInteger( Solver % Values,'CutFEm Inside Body',Found )
862
IF(.NOT. Found) body_in = CurrentModel % NumberOfBodies
863
body_out = ListGetInteger( Solver % Values,'CutFem Outside Body',Found )
864
IF(.NOT. Found) body_out = body_in+1
865
END IF
866
867
868
! This is the counter for splitting.
869
IF(.NOT. ASSOCIATED(prevElement,Element)) THEN
870
CutCnt = 1
871
prevElement => Element
872
ElemType = Element % Type % ElementCode
873
n = ElemType / 100
874
875
! For triangles & quads these are true, not for others...
876
ElemInds(1:n) = Element % NodeIndexes(1:n)
877
ElemInds(n+1:2*n) = nn + Element % EdgeIndexes(1:n)
878
879
ElemCut(1:2*n) = CutDof(ElemInds(1:2*n))
880
881
ElemNodes % x(1:2*n) = x(ElemInds(1:2*n))
882
ElemNodes % y(1:2*n) = y(ElemInds(1:2*n))
883
ElemNodes % z(1:2*n) = z(ElemInds(1:2*n))
884
ELSE
885
CutCnt = CutCnt+1
886
END IF
887
888
pElement => Element
889
CALL SplitSingleElement(Element, ElemCut, ElemNodes, CutCnt, &
890
IsCut, IsMore, LocalInds, SgnNode )
891
IF(.NOT. IsCut) RETURN
892
893
i = COUNT(LocalInds > 0)
894
SELECT CASE(i)
895
CASE(3)
896
pElement => Elem303
897
CASE(4)
898
pElement => Elem404
899
CASE DEFAULT
900
CALL Fatal('CutInterfaceBulk','Impossible number of nodes!')
901
END SELECT
902
pElement % NodeIndexes(1:i) = ElemInds(LocalInds(1:i))
903
904
! This circumwents some rare case when node is cut.
905
IF( body_out == 0 ) THEN
906
IF( ALL( CutPerm(pElement % NodeIndexes) > 0) ) THEN
907
pElement % BodyId = body_in
908
ELSE
909
pElement % BodyId = body_out
910
END IF
911
ELSE
912
i = PhiVar % Perm(pElement % NodeIndexes(sgnNode))
913
IF( PhiVar % Values(i) > 0.0_dp ) THEN
914
pElement % BodyId = body_out
915
ELSE
916
pElement % BodyId = body_in
917
END IF
918
END IF
919
920
END FUNCTION CutInterfaceBulk
921
922
923
924
! Given Element, levelset function and the CutDof field return the elements created at the interface.
925
! In 2D and also for straight cuts in 3D we should expect to have just one element but it could
926
! be changed. This includes also wedges even if the above does not just to model how they could
927
! work in 3D.
928
!----------------------------------------------------------------------------------------------
929
FUNCTION CutInterfaceBC( Element, IsCut, IsMore ) RESULT ( pElement )
930
TYPE(Element_t), POINTER :: Element, pElement
931
LOGICAL :: IsCut, IsMore
932
933
TYPE(Element_t), TARGET :: Elem202, Elem303, Elem404
934
TYPE(Element_t), POINTER, SAVE :: prevElement
935
INTEGER :: m, n, n_split, n_cut, i, j, j2, j3, j4, nn, SplitCase
936
INTEGER, POINTER :: nIndexes(:), eIndexes(:)
937
TYPE(Mesh_t), POINTER :: Mesh
938
LOGICAL :: Visited = .FALSE., Found, VerticalCut
939
REAL(KIND=dp), POINTER :: x(:), y(:), z(:)
940
TYPE(Solver_t), POINTER :: Solver
941
CHARACTER(*), PARAMETER :: Caller = 'CutInterfaceBC'
942
943
SAVE Visited, Mesh, Solver, nn, x, y, z, n_split, n_cut, &
944
Elem202, Elem303, Elem404, VerticalCut, m
945
946
947
IF(.NOT. Visited) THEN
948
Solver => CurrentModel % Solver
949
Mesh => Solver % Mesh
950
nn = Mesh % NumberOfNodes
951
x => Mesh % Nodes % x
952
y => Mesh % Nodes % y
953
z => Mesh % Nodes % z
954
955
n = ListGetInteger( Solver % Values,'CutFem Interface BC',Found )
956
957
IF( Mesh % MeshDim == 3 ) THEN
958
Elem303 % TYPE => GetElementType(303)
959
ALLOCATE(Elem303 % NodeIndexes(3))
960
Elem303 % NodeIndexes = 0
961
ALLOCATE( Elem303 % BoundaryInfo )
962
Elem303 % BoundaryInfo % Constraint = n
963
964
Elem404 % TYPE => GetElementType(404)
965
ALLOCATE(Elem404 % NodeIndexes(4))
966
Elem404 % NodeIndexes = 0
967
ALLOCATE( Elem404 % BoundaryInfo )
968
Elem404 % BoundaryInfo % Constraint = n
969
970
VerticalCut = ListGetLogical(Solver % Values,'CutFEM vertical cut',Found )
971
ELSE
972
Elem202 % TYPE => GetElementType(202)
973
ALLOCATE(Elem202 % NodeIndexes(2))
974
Elem202 % NodeIndexes = 0
975
ALLOCATE( Elem202 % BoundaryInfo )
976
Elem202 % BoundaryInfo % Constraint = n
977
978
VerticalCut = .FALSE.
979
END IF
980
981
Visited = .TRUE.
982
END IF
983
984
nIndexes => Element % NodeIndexes
985
eIndexes => Element % EdgeIndexes
986
987
988
! This is the counter for splitting.
989
IF(.NOT. ASSOCIATED(prevElement,Element)) THEN
990
m = 1
991
prevElement => Element
992
IF( VerticalCut ) THEN
993
n = SIZE(eIndexes) / 2
994
n_split = COUNT( CutDof(nn + eIndexes(1:n)) )
995
n = SIZE(nIndexes) / 2
996
n_cut = COUNT( CutDof(nIndexes(1:n)) )
997
ELSE
998
n_split = COUNT( CutDof(nn + eIndexes) )
999
n_cut = COUNT( CutDof(nIndexes) )
1000
END IF
1001
ELSE
1002
m = m+1
1003
END IF
1004
1005
IsMore = .FALSE.
1006
1007
IF( n_split == 0 .AND. n_cut <= 1 ) THEN
1008
isCut = .FALSE.
1009
pElement => NULL()
1010
RETURN
1011
END IF
1012
1013
IsCut = .TRUE.
1014
1015
SELECT CASE( Element % TYPE % ElementCode )
1016
CASE( 808 )
1017
pElement => Elem303
1018
CASE( 706 )
1019
pElement => Elem404
1020
CASE( 504 )
1021
pElement => Elem303
1022
CASE( 303, 404 )
1023
pElement => Elem202
1024
CASE DEFAULT
1025
CALL Fatal(Caller,'Unknown element type to split: '//I2S(Element % TYPE % ElementCode)//'!')
1026
END SELECT
1027
pElement % NodeIndexes = 0
1028
1029
1030
! This allows use case to deal with element types, edge splits and node splits at the same time.
1031
SplitCase = 100 * Element % TYPE % ElementCode + 10 * n_split + n_cut
1032
1033
1034
SELECT CASE( SplitCase )
1035
1036
CASE( 30320 )
1037
! Find the both cut edges
1038
DO j=1,3
1039
IF( CutDof( nn + eIndexes(j) ) ) EXIT
1040
END DO
1041
DO j2=1,3
1042
IF(j2==j) CYCLE
1043
IF( CutDof( nn + eIndexes(j2) ) ) EXIT
1044
END DO
1045
pElement % NodeIndexes(1) = nn + eIndexes(j)
1046
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1047
1048
CASE( 30321 )
1049
IF(m==1) THEN
1050
DO j=1,3
1051
IF( CutDof( nn + eIndexes(j) ) ) EXIT
1052
END DO
1053
DO j2=1,3
1054
IF(j2==j) CYCLE
1055
IF( CutDof( nn + eIndexes(j2) ) ) EXIT
1056
END DO
1057
pElement % NodeIndexes(1) = nn + eIndexes(j)
1058
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1059
IsMore = .TRUE.
1060
ELSE
1061
DO j=1,3
1062
IF(CutDof(nIndexes(j))) EXIT
1063
END DO
1064
j2 = j
1065
IF( .NOT. CutDof(nn + eIndexes(j2) ) ) THEN
1066
j2 = MODULO(j-2,3)+1
1067
IF(.NOT. CutDof(nn + eIndexes(j2))) THEN
1068
CALL Fatal('Caller','Could not imagine this 303 case!')
1069
END IF
1070
END IF
1071
pElement % NodeIndexes(1) = nIndexes(j)
1072
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1073
IsMore = .FALSE.
1074
END IF
1075
1076
CASE( 30311 )
1077
! Find the edge and node that is cut
1078
DO j=1,3
1079
IF( CutDof( nn + eIndexes(j) ) ) EXIT
1080
END DO
1081
DO j2=1,3
1082
IF( CutDof( nIndexes(j2) ) ) EXIT
1083
END DO
1084
pElement % NodeIndexes(1) = nn + eIndexes(j)
1085
pElement % NodeIndexes(2) = nIndexes(j2)
1086
1087
CASE( 30302 )
1088
! Find the two nodes that are cut.
1089
DO j=1,3
1090
IF( CutDof( nIndexes(j) ) ) EXIT
1091
END DO
1092
DO j2=1,3
1093
IF(j2==j) CYCLE
1094
IF( CutDof( nIndexes(j2) ) ) EXIT
1095
END DO
1096
pElement % NodeIndexes(1) = nIndexes(j)
1097
pElement % NodeIndexes(2) = nIndexes(j2)
1098
1099
CASE( 40420 )
1100
DO j=1,4
1101
IF(CutDof( nn + eIndexes(j))) EXIT
1102
END DO
1103
DO j2=1,4
1104
IF(j2==j) CYCLE
1105
IF(CutDof( nn + eIndexes(j2))) EXIT
1106
END DO
1107
pElement % NodeIndexes(1) = nn + eIndexes(j)
1108
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1109
1110
CASE( 40421 )
1111
1112
IF(m==1) THEN
1113
DO j=1,4
1114
IF(CutDof( nn + eIndexes(j))) EXIT
1115
END DO
1116
DO j2=1,4
1117
IF(j2==j) CYCLE
1118
IF(CutDof( nn + eIndexes(j2))) EXIT
1119
END DO
1120
pElement % NodeIndexes(1) = nn + eIndexes(j)
1121
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1122
IsMore = .TRUE.
1123
ELSE
1124
DO j=1,4
1125
IF(CutDof(nIndexes(j))) EXIT
1126
END DO
1127
j2 = j
1128
IF( .NOT. CutDof(nn + eIndexes(j2) ) ) THEN
1129
j2 = MODULO(j-2,4)+1
1130
IF(.NOT. CutDof(nn + eIndexes(j2))) THEN
1131
CALL Fatal('Caller','Could not imagine this 404 case!')
1132
END IF
1133
END IF
1134
pElement % NodeIndexes(1) = nIndexes(j)
1135
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1136
IsMore = .FALSE.
1137
END IF
1138
1139
CASE( 40411 )
1140
DO j=1,4
1141
IF(CutDof( nn + eIndexes(j))) EXIT
1142
END DO
1143
DO j2=1,4
1144
IF(CutDof( nIndexes(j2))) EXIT
1145
END DO
1146
pElement % NodeIndexes(1) = nn + eIndexes(j)
1147
pElement % NodeIndexes(2) = nIndexes(j2)
1148
1149
CASE( 40402 )
1150
DO j=1,4
1151
IF(CutDof( nIndexes(j))) EXIT
1152
END DO
1153
DO j2=1,4
1154
IF(j2==j) CYCLE
1155
IF(CutDof( nIndexes(j2))) EXIT
1156
END DO
1157
pElement % NodeIndexes(1) = nIndexes(j)
1158
pElement % NodeIndexes(2) = nIndexes(j2)
1159
1160
CASE( 50440 )
1161
DO j=1,6
1162
IF(CutDof( nn + eIndexes(j))) EXIT
1163
END DO
1164
DO j2=1,6
1165
IF(j2==j) CYCLE
1166
IF(CutDof( nn + eIndexes(j2))) EXIT
1167
END DO
1168
DO j3=1,6
1169
IF(j3==j .OR. j3==j2) CYCLE
1170
IF(CutDof( nn + eIndexes(j3))) EXIT
1171
END DO
1172
DO j4=1,6
1173
IF(j4==j .OR. j4==j2 .OR. j4==j3) CYCLE
1174
IF(CutDof( nn + eIndexes(j4))) EXIT
1175
END DO
1176
1177
IF(m==1) THEN
1178
pElement % NodeIndexes(1) = nn + eIndexes(j)
1179
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1180
pElement % NodeIndexes(3) = nn + eIndexes(j3)
1181
IsMore = .TRUE.
1182
ELSE
1183
pElement % NodeIndexes(1) = nn + eIndexes(j)
1184
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1185
pElement % NodeIndexes(3) = nn + eIndexes(j4)
1186
IsMore = .FALSE.
1187
END IF
1188
1189
CASE( 50430 )
1190
DO j=1,6
1191
IF(CutDof( nn + eIndexes(j))) EXIT
1192
END DO
1193
DO j2=1,6
1194
IF(j2==j) CYCLE
1195
IF(CutDof( nn + eIndexes(j2))) EXIT
1196
END DO
1197
DO j3=1,6
1198
IF(j3==j .OR. j3==j2) CYCLE
1199
IF(CutDof( nn + eIndexes(j3))) EXIT
1200
END DO
1201
pElement % NodeIndexes(1) = nn + eIndexes(j)
1202
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1203
pElement % NodeIndexes(3) = nn + eIndexes(j3)
1204
1205
CASE( 50421 )
1206
DO j=1,6
1207
IF(CutDof( nn + eIndexes(j))) EXIT
1208
END DO
1209
DO j2=1,6
1210
IF(j2==j) CYCLE
1211
IF(CutDof( nn + eIndexes(j2))) EXIT
1212
END DO
1213
DO j3=1,4
1214
IF(CutDof( nIndexes(j3))) EXIT
1215
END DO
1216
pElement % NodeIndexes(1) = nn + eIndexes(j)
1217
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1218
pElement % NodeIndexes(3) = nIndexes(j3)
1219
1220
CASE( 50412 )
1221
DO j=1,6
1222
IF(CutDof( nn + eIndexes(j))) EXIT
1223
END DO
1224
DO j2=1,4
1225
IF(CutDof( nIndexes(j2))) EXIT
1226
END DO
1227
DO j3=1,4
1228
IF(j3==j2) CYCLE
1229
IF(CutDof( nIndexes(j3))) EXIT
1230
END DO
1231
pElement % NodeIndexes(1) = nn + eIndexes(j)
1232
pElement % NodeIndexes(2) = nIndexes(j2)
1233
pElement % NodeIndexes(3) = nIndexes(j3)
1234
1235
CASE( 50403 )
1236
i = 0
1237
DO j=1,4
1238
! We cut all other edges expect "j"
1239
IF(.NOT. CutDof( nIndexes(j))) EXIT
1240
i = i+1
1241
pElement % NodeIndexes(i) = nIndexes(j)
1242
END DO
1243
1244
1245
CASE( 70620 )
1246
! For prisms we currently assumes that the field is cut vertically such that
1247
! we can split the bottom triangle and copy the same split in 3D.
1248
DO j=1,3
1249
IF( CutDof( nn + eIndexes(j) ) ) EXIT
1250
END DO
1251
DO j2=1,3
1252
IF(j2==j) CYCLE
1253
IF( CutDof( nn + eIndexes(j2) ) ) EXIT
1254
END DO
1255
pElement % NodeIndexes(1) = nn + eIndexes(j)
1256
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1257
pElement % NodeIndexes(3) = nn + eIndexes(3+j2)
1258
pElement % NodeIndexes(4) = nn + eIndexes(3+j)
1259
1260
CASE( 70611 )
1261
! Find the edge and node that is cut
1262
DO j=1,3
1263
IF( CutDof( nn + eIndexes(j) ) ) EXIT
1264
END DO
1265
DO j2=1,3
1266
IF( CutDof( nIndexes(j2) ) ) EXIT
1267
END DO
1268
pElement % NodeIndexes(1) = nn + eIndexes(j)
1269
pElement % NodeIndexes(2) = nIndexes(j2)
1270
pElement % NodeIndexes(3) = nIndexes(j2+3)
1271
pElement % NodeIndexes(4) = nn + eIndexes(j+3)
1272
1273
CASE( 70602 )
1274
! Find the two nodes that are cut.
1275
DO j=1,3
1276
IF( CutDof( nIndexes(j) ) ) EXIT
1277
END DO
1278
DO j2=1,3
1279
IF(j2==j) CYCLE
1280
IF( CutDof( nIndexes(j2) ) ) EXIT
1281
END DO
1282
pElement % NodeIndexes(1) = nIndexes(j)
1283
pElement % NodeIndexes(2) = nIndexes(j2)
1284
pElement % NodeIndexes(2) = nIndexes(j2+3)
1285
pElement % NodeIndexes(1) = nIndexes(j+3)
1286
1287
CASE( 80830 )
1288
DO j=1,12
1289
IF( CutDof( nn + eIndexes(j) ) ) EXIT
1290
END DO
1291
DO j2=1,12
1292
IF(j2==j) CYCLE
1293
IF( CutDof( nn + eIndexes(j2) ) ) EXIT
1294
END DO
1295
DO j3=1,12
1296
IF(j3==j .OR. j3==j2) CYCLE
1297
IF( CutDof( nn + eIndexes(j3) ) ) EXIT
1298
END DO
1299
pElement % NodeIndexes(1) = nn + eIndexes(j)
1300
pElement % NodeIndexes(2) = nn + eIndexes(j2)
1301
pElement % NodeIndexes(3) = nn + eIndexes(j3)
1302
1303
CASE DEFAULT
1304
PRINT *,'Unknown SplitCase:',SplitCase
1305
PRINT *,'EdgeCut:',CutDof(nn + Element % EdgeIndexes)
1306
PRINT *,'NodeCut:',CutDof(Element % NodeIndexes)
1307
PRINT *,'Phi:',PhiValues(PhiPerm(Element % NodeIndexes))
1308
CALL Fatal(Caller,'Unknown split case in bc element divisions: '//I2S(SplitCase))
1309
END SELECT
1310
1311
1312
! This is just a tentative routine where we orient the nodes of the segment such that
1313
! inside/outside is always consistently on left/right.
1314
IF(pElement % TYPE % ElementCode == 202 ) THEN
1315
BLOCK
1316
REAL(KIND=dp) :: pmax, p, x0, x1, xp, y0, y1, yp, dir1, dir2
1317
INTEGER :: i,j,imax
1318
1319
! The most trustworthy point to define the sign of the levelset is the one with extreme value.
1320
pmax = 0.0_dp
1321
imax = 0
1322
DO i=1,Element % TYPE % NumberOfNodes
1323
j = PhiPerm(Element % NodeIndexes(i))
1324
IF(j==0) CYCLE
1325
p = PhiValues(j)
1326
IF(ABS(p) > ABS(pmax)) THEN
1327
pmax = p
1328
imax = Element % NodeIndexes(i)
1329
END IF
1330
END DO
1331
1332
! Dir is an indicator one which side of the line segment the point lies.
1333
x0 = Mesh % Nodes % x(pElement % NodeIndexes(1))
1334
y0 = Mesh % Nodes % y(pElement % NodeIndexes(1))
1335
x1 = Mesh % Nodes % x(pElement % NodeIndexes(2))
1336
y1 = Mesh % Nodes % y(pElement % NodeIndexes(2))
1337
xp = Mesh % Nodes % x(imax)
1338
yp = Mesh % Nodes % y(imax)
1339
1340
dir1 = (x1 - x0) * (yp - y0) - (y1 - y0) * (xp - x0)
1341
1342
! Switch the signs so that if the point was found from left/right side of the
1343
! line segment the sign stays the same as previously.
1344
IF(dir1 * pmax < 0.0_dp) THEN
1345
j = pElement % NodeIndexes(1)
1346
pElement % NodeIndexes(1) = pElement % NodeIndexes(2)
1347
pElement % NodeIndexes(2) = j
1348
END IF
1349
1350
#if DEBUG_ORIENT
1351
! Here we can check that the orientation of the edges is consistent.
1352
! This check only applies to convex geometries where the centermost node
1353
! can be used to check the orientation.
1354
x0 = Mesh % Nodes % x(pElement % NodeIndexes(1))
1355
y0 = Mesh % Nodes % y(pElement % NodeIndexes(1))
1356
x1 = Mesh % Nodes % x(pElement % NodeIndexes(2))
1357
y1 = Mesh % Nodes % y(pElement % NodeIndexes(2))
1358
dir2 = (x1 - x0) * (CutFEMCenter(2) - y0) - (y1 - y0) * (CutFEMCenter(1) - x0)
1359
1360
IF( dir2 > 0.0 ) THEN
1361
PRINT *,'WrongDirection:',SplitCase,m,x0,x1,y0,y1
1362
PRINT *,'WrongDirIndexes:',CutDof(nIndexes),'e',CutDof(nn+eIndexes)
1363
PRINT *,'WrongDirPhi:',PhiValues(PhiPerm(nIndexes))
1364
PRINT *,'WrongDirX:',Mesh % Nodes % x(nIndexes)
1365
PRINT *,'WrongDirY:',Mesh % Nodes % y(nIndexes)
1366
PRINT *,'WrongDirImax:',imax,pmax
1367
1368
STOP
1369
j = pElement % NodeIndexes(1)
1370
pElement % NodeIndexes(1) = pElement % NodeIndexes(2)
1371
pElement % NodeIndexes(2) = j
1372
END IF
1373
#endif
1374
1375
END BLOCK
1376
END IF
1377
1378
pElement % BoundaryInfo % Left => NULL() ! Element
1379
pElement % BodyId = 0
1380
1381
END FUNCTION CutInterfaceBC
1382
1383
1384
! This is currently not used.
1385
!-------------------------------------------------------
1386
SUBROUTINE CutFEMElementCount(Solver, Perm, nBulk, nBC )
1387
TYPE(Solver_t) :: Solver
1388
INTEGER, POINTER :: Perm(:)
1389
INTEGER :: nBulk, nBC
1390
1391
TYPE(Mesh_t), POINTER :: Mesh
1392
INTEGER :: Active, t, n, nBulk0, nBC0, t0
1393
TYPE(Element_t), POINTER :: Element, pElement
1394
LOGICAL :: isCut, isMore, isActive
1395
1396
nBulk = 0
1397
nBulk0 = 0
1398
nBC = 0
1399
nBC0 = 0
1400
Mesh => Solver % Mesh
1401
1402
DO t=1,Mesh % NumberOfBulkElements
1403
Element => Mesh % Elements(t)
1404
IF(ANY(PhiPerm(Element % NodeIndexes)==0)) CYCLE
1405
CALL CutInterfaceCheck( Element, IsCut, IsActive, Perm )
1406
IF(.NOT. IsActive) CYCLE
1407
IF(IsCut) THEN
1408
10 pElement => CutInterfaceBulk(Element,isCut,isMore)
1409
IF(ALL(Perm(pElement % NodeIndexes) > 0) ) nBulk = nBulk + 1
1410
IF(IsMore) GOTO 10
1411
ELSE
1412
nBulk0 = nBulk0 + 1
1413
END IF
1414
END DO
1415
1416
1417
! Additional BC elements created on the interface.
1418
DO t=1,Mesh % NumberOfBulkElements
1419
Element => Mesh % Elements(t)
1420
IF(ANY(PhiPerm(Element % NodeIndexes)==0)) CYCLE
1421
CALL CutInterfaceCheck( Element, IsCut, IsActive, Perm )
1422
IF(.NOT. IsActive) CYCLE
1423
20 pElement => CutInterfaceBC(Element,isCut,isMore)
1424
IF(ASSOCIATED(pElement)) THEN
1425
IF(ALL(Perm(pElement % NodeIndexes) > 0) ) nBC = nBC + 1
1426
IF(IsMore) GOTO 20
1427
END IF
1428
END DO
1429
1430
! Remaining original boundary element.
1431
t0 = Mesh % NumberOfBulkElements
1432
DO t=1,Mesh % NumberOfBoundaryElements
1433
Element => Mesh % Elements(t0+t)
1434
IF(ANY(PhiPerm(Element % NodeIndexes)==0)) CYCLE
1435
IF(ALL(Perm(Element % NodeIndexes) > 0) ) nBC0 = nBC0 + 1
1436
END DO
1437
1438
CALL Info('CutFEMElementCount','Bulk elements remaining '//I2S(nBulk0)//' & splitted '//I2S(nBulk),Level=7)
1439
CALL Info('CutFEMElementCount','BC elements remaining '//I2S(nBC0)//' & splitted '//I2S(nBC),Level=7)
1440
1441
nBC = nBC0 + nBC
1442
nBulk = nBulk0 + nBulk
1443
1444
END SUBROUTINE CutFEMElementCount
1445
1446
1447
SUBROUTINE CreateCutFEMAddMesh(Solver)
1448
TYPE(Solver_t) :: Solver
1449
1450
INTEGER :: Sweep, t, n, i
1451
LOGICAL :: IsActive, IsCut
1452
TYPE(Element_t), POINTER :: Element
1453
1454
CALL Info('CreateCutFEMAddMesh','Creating interface mesh from split element',Level=10)
1455
1456
DO Sweep = 0,1
1457
n = 0
1458
DO t=1,CutFEMOrigMesh % NumberOfBulkElements
1459
Element => CutFEMOrigMesh % Elements(t)
1460
IF(ANY(PhiPerm(Element % NodeIndexes)==0)) CYCLE
1461
CALL CutInterfaceCheck( Element, IsCut, IsActive, CutPerm )
1462
IF(IsActive .AND. .NOT. IsCut) THEN
1463
n = n+1
1464
IF(Sweep==1) UnsplitActiveElements(n) = t
1465
END IF
1466
END DO
1467
IF(Sweep == 0) THEN
1468
ALLOCATE(UnsplitActiveElements(n))
1469
END IF
1470
END DO
1471
1472
IF(ASSOCIATED(CutFEMAddMesh)) THEN
1473
NULLIFY(CutFEMAddMesh % Nodes % x)
1474
NULLIFY(CutFEMAddMesh % Nodes % y)
1475
NULLIFY(CutFEMAddMesh % Nodes % z)
1476
CALL FreeMesh(CutFEMAddMesh)
1477
END IF
1478
CutFEMAddMesh => CreateCutFEMMesh(Solver,CutFEMOrigMesh,CutPerm,&
1479
.TRUE.,.TRUE.,.TRUE.,Solver % Values,'dummy variable')
1480
1481
CALL MeshStabParams( CutFEMAddMesh )
1482
1483
n = CutFEMAddMesh % NumberOfBulkElements
1484
ALLOCATE(AddActiveElements(n))
1485
DO i=1,n
1486
AddActiveElements(i) = i
1487
END DO
1488
1489
Solver % ActiveElements => UnsplitActiveElements
1490
Solver % NumberOfActiveElements = SIZE(UnsplitActiveElements)
1491
1492
CALL Info('CreateCutFEMAddMesh','Add mesh created with '//I2S(i)//' active elements!',Level=10)
1493
1494
END SUBROUTINE CreateCutFEMAddMesh
1495
1496
SUBROUTINE CutFEMSetAddMesh(Solver)
1497
TYPE(Solver_t) :: Solver
1498
1499
Solver % Mesh => CutFEMAddMesh
1500
CurrentModel % Mesh => CutFEMAddMesh
1501
Solver % ActiveElements => AddActiveElements
1502
Solver % NumberOfActiveElements = SIZE(Solver % ActiveElements)
1503
Solver % Mesh % Edges => CutFemOrigMesh % Edges
1504
1505
CALL Info('CutFEMSetAddMesh','Swapping CutFEM original mesh to interface mesh!',Level=10)
1506
1507
END SUBROUTINE CutFEMSetAddMesh
1508
1509
SUBROUTINE CutFEMSetOrigMesh(Solver)
1510
TYPE(Solver_t) :: Solver
1511
1512
Solver % Mesh => CutFEMOrigMesh
1513
CurrentModel % Mesh => CutFEMOrigMesh
1514
Solver % ActiveElements => UnsplitActiveElements
1515
Solver % NumberOfActiveElements = SIZE(Solver % ActiveElements)
1516
1517
NULLIFY(CutFEMAddMesh % Edges)
1518
1519
CALL Info('CutFEMSetOrigMesh','Swapping CutFEM interface mesh to original mesh!',Level=10)
1520
1521
END SUBROUTINE CutFEMSetOrigMesh
1522
1523
1524
1525
1526
! Assembly a matrix for extrapolating values outside the active domain.
1527
! Currently this is just diffusion matrix. We could perhaps use convection also.
1528
!------------------------------------------------------------------------------
1529
SUBROUTINE LocalFitMatrix( Element, n )
1530
!------------------------------------------------------------------------------
1531
INTEGER :: n
1532
TYPE(Element_t), POINTER :: Element
1533
!------------------------------------------------------------------------------
1534
REAL(KIND=dp) :: weight, dcoeff
1535
REAL(KIND=dp) :: Basis(n),dBasisdx(n,3),DetJ
1536
REAL(KIND=dp) :: STIFF(n,n), FORCE(n), LOAD(n)
1537
LOGICAL :: Stat,Found,CutElem
1538
INTEGER :: i,j,t,p,q
1539
TYPE(GaussIntegrationPoints_t) :: IP
1540
TYPE(Nodes_t) :: Nodes
1541
1542
SAVE Nodes
1543
!------------------------------------------------------------------------------
1544
1545
! CALL GetElementNodes( Nodes, Element )
1546
CALL CopyElementNodesFromMesh( Nodes, CurrentModel % Solver % Mesh, n, Element % NodeIndexes)
1547
1548
STIFF = 0._dp
1549
FORCE = 0._dp
1550
LOAD = 0.0_dp
1551
1552
dcoeff = 1.0_dp
1553
1554
! Numerical integration:
1555
!-----------------------
1556
IP = GaussPointsAdapt( Element )
1557
DO t=1,IP % n
1558
stat = ElementInfo( Element, Nodes, IP % U(t), IP % V(t), &
1559
IP % W(t), detJ, Basis, dBasisdx )
1560
IF(.NOT. Stat) CYCLE
1561
Weight = IP % s(t) * DetJ
1562
1563
STIFF(1:n,1:n) = STIFF(1:n,1:n) + dcoeff * Weight * &
1564
MATMUL( dBasisdx, TRANSPOSE( dBasisdx ) )
1565
END DO
1566
1567
CutElem = .TRUE.
1568
! CALL DefaultUpdateEquations(STIFF,FORCE,CutElem,Element)
1569
1570
!------------------------------------------------------------------------------
1571
END SUBROUTINE LocalFitMatrix
1572
!------------------------------------------------------------------------------
1573
1574
1575
1576
! This takes a CutFEM variable and either extrapolates it using a FE equation
1577
! (for many element layers) or just extends it by extrapolating on the cut edges.
1578
!---------------------------------------------------------------------------------
1579
SUBROUTINE CutFEMVariableFinalize( Solver )
1580
TYPE(Solver_t) :: Solver
1581
1582
TYPE(Matrix_t), POINTER :: B
1583
TYPE(Mesh_t), POINTER :: Mesh
1584
TYPE(Element_t), POINTER :: pElement, Element
1585
INTEGER :: i,j,k,l,n,t,active,nn,ne,i1,i2,dofs
1586
LOGICAL :: IsCut, IsMore, Found
1587
REAL(KIND=dp) :: s, r, dval, norm
1588
REAL(KIND=dp), ALLOCATABLE :: NodeWeigth(:)
1589
1590
1591
Mesh => Solver % Mesh
1592
nn = Mesh % NumberOfNodes
1593
ne = Mesh % NumberOfEdges
1594
dofs = CutDofs
1595
1596
! If we solve some other equation in between store the original norm.
1597
Norm = Solver % Variable % Norm
1598
1599
! Set values at shared nodes that have been computed.
1600
CALL Info('CutFEMVariableFinalize','Copying values at shared nodes to the original mesh!',Level=10)
1601
DO l=1,dofs
1602
DO i=1,nn
1603
j = CutPerm(i)
1604
k = OrigMeshPerm(i)
1605
IF(j==0 .OR. k==0) CYCLE
1606
OrigMeshValues(dofs*(k-1)+l) = CutValues(dofs*(j-1)+l)
1607
END DO
1608
END DO
1609
1610
1611
! We can only extrapolate using the edges that are cut since they have also one
1612
! known nodal value.
1613
IF(CutExtrapolate) THEN
1614
CALL Info('CutFEMVariableFinalize','Extrapolating values with split elements!',Level=10)
1615
1616
! Extrapolated nodes may have more than one hit. Hence use weigted average.
1617
! This is the weight.
1618
ALLOCATE(NodeWeigth(nn))
1619
NodeWeigth = 0.0_dp
1620
1621
k = 0
1622
DO i=1,Solver % Mesh % NumberOfEdges
1623
j = CutPerm(nn+i)
1624
IF(j==0) CYCLE
1625
r = CutInterp(i)
1626
1627
i1 = Mesh % Edges(i) % NodeIndexes(1)
1628
i2 = Mesh % Edges(i) % NodeIndexes(2)
1629
1630
IF(CutPerm(i1) > 0 .AND. CutPerm(i2) == 0 ) THEN
1631
s = (1-r)
1632
DO k=1,dofs
1633
OrigMeshValues(dofs*(OrigMeshPerm(i2)-1)+k) = OrigMeshValues(dofs*(OrigMeshPerm(i2)-1)+k) + &
1634
s*CutValues(dofs*(CutPerm(i1)-1)+k) + (CutValues(dofs*(j-1)+k)-CutValues(dofs*(CutPerm(i1)-1)+k))
1635
END DO
1636
NodeWeigth(OrigMeshPerm(i2)) = NodeWeigth(OrigMeshPerm(i2)) + s
1637
ELSE IF(CutPerm(i1) == 0 .AND. CutPerm(i2) > 0) THEN
1638
s = r
1639
DO k=1,dofs
1640
OrigMeshValues(dofs*(OrigMeshPerm(i1)-1)+k) = OrigMeshValues(dofs*(OrigMeshPerm(i1)-1)+k) + &
1641
s*CutValues(dofs*(CutPerm(i2)-1)+k) + (CutValues(dofs*(j-1)+k)-CutValues(dofs*(CutPerm(i2)-1)+k))
1642
END DO
1643
NodeWeigth(OrigMeshPerm(i1)) = NodeWeigth(OrigMeshPerm(i1)) + s
1644
END IF
1645
END DO
1646
1647
DO k=1,dofs
1648
WHERE( NodeWeigth(1:nn) > EPSILON(s))
1649
OrigMeshValues(k::dofs) = OrigMeshValues(k::dofs) / NodeWeigth(1:nn)
1650
END WHERE
1651
END DO
1652
END IF
1653
1654
1655
! Extend values using FEM strategies beyond value set above.
1656
! We can extrapolate much but the extrapolation method is nonhysical.
1657
IF( CutExtend ) THEN
1658
CALL Info('CutFEMVariableFinalize','Extending values from inside to outside using FEM!',Level=10)
1659
IF(dofs > 1) THEN
1660
CALL Fatal('CutFEMVariableFinalize','Extending values only coded for one dofs!')
1661
END IF
1662
1663
B => CreateCutFEMMatrix(Solver,ExtendPerm)
1664
ALLOCATE(ExtendValues(B % NumberOfRows))
1665
ExtendValues = 0.0_dp
1666
Solver % Matrix => B
1667
Solver % Variable % Values => ExtendValues
1668
Solver % Variable % Perm => ExtendPerm
1669
1670
DO t=1,Mesh % NumberOfBulkElements
1671
Element => Mesh % Elements(t)
1672
IF(ANY(PhiPerm(Element % NodeIndexes)==0)) CYCLE
1673
1674
n = Element % Type % NumberOfNodes
1675
1676
30 pElement => CutInterfaceBulk(Element,isCut,isMore)
1677
IF(isCut) THEN
1678
n = pElement % Type % NumberOfNodes
1679
IF(ALL(ExtendPerm(pElement % NodeIndexes) > 0) ) THEN
1680
CALL LocalFitMatrix( pElement, n )
1681
END IF
1682
IF(IsMore) GOTO 30
1683
ELSE
1684
IF(ALL(ExtendPerm(Element % NodeIndexes) > 0) ) THEN
1685
CALL LocalFitMatrix( Element, n )
1686
END IF
1687
END IF
1688
END DO
1689
1690
1691
! On the shared nodes of the "inside" and "outside" regions.
1692
! Set Dirichlet conditions in somewhat dirty way for now.
1693
DO i=1,Mesh % NumberOfNodes + Mesh % NumberOfEdges
1694
j = CutPerm(i)
1695
k = ExtendPerm(i)
1696
IF(j==0 .OR. k==0) CYCLE
1697
1698
dval = CutValues(j)
1699
s = B % Values(B % diag(k))
1700
CALL ZeroRow(B, k)
1701
CALL SetMatrixElement(B,k,k,s)
1702
B % rhs(k) = s * dval
1703
END DO
1704
1705
CALL SolveLinearSystem( B, B % rhs, ExtendValues, norm, Solver % Variable % dofs, Solver )
1706
1707
CALL FreeMatrix(B)
1708
Solver % Matrix => NULL()
1709
1710
DO i=1,nn
1711
j = ExtendPerm(i)
1712
k = OrigMeshPerm(i)
1713
IF(j==0 .OR. k==0) CYCLE
1714
OrigMeshValues(k) = ExtendValues(j)
1715
END DO
1716
END IF
1717
1718
! Revert to the original field that is present everywhere.
1719
Solver % Variable % Values => OrigMeshValues
1720
Solver % Variable % Perm => OrigMeshPerm
1721
Solver % Variable % PrevValues => OrigPrevMeshValues
1722
Solver % Variable % Norm = Norm
1723
1724
! Revert to original body id's.
1725
! If we don't do this then ActiveElements is spoiled.
1726
DO t=1,Mesh % NumberOfBulkElements
1727
Element => Mesh % Elements(t)
1728
IF(ALL(PhiPerm(Element % NodeIndexes)>0)) THEN
1729
Element % BodyId = CutFemBody
1730
END IF
1731
END DO
1732
1733
END SUBROUTINE CutFEMVariableFinalize
1734
1735
1736
!------------------------------------------------------------------------------
1737
!> Split a mesh at zero levelset by adding new nodes at the interface.
1738
!> The idea is to be able to better represent shapes that are not initially
1739
!> presented by body fitted finite element mesh. This is a modifieid version
1740
!> of similar routine in MeshUtils that utilizes the CutInterface* routines.
1741
!------------------------------------------------------------------------------
1742
FUNCTION CreateCutFEMMesh(Solver,Mesh,Perm,CreateBC,CreateBulk,&
1743
AddMeshMode, Vlist,ProjectPrefix) RESULT( NewMesh )
1744
!------------------------------------------------------------------------------
1745
TYPE(Solver_t) :: Solver
1746
TYPE(Mesh_t) :: Mesh
1747
INTEGER, POINTER :: Perm(:)
1748
LOGICAL :: CreateBC, CreateBulk, AddMeshMode
1749
TYPE(ValueList_t), POINTER :: Vlist
1750
CHARACTER(*) :: ProjectPrefix
1751
TYPE(Mesh_t), POINTER :: NewMesh
1752
!------------------------------------------------------------------------------
1753
INTEGER :: i, j, k, n
1754
INTEGER :: NodeCnt
1755
INTEGER :: nn, ne, nBC, nBulk, t, ntot, Sweep, InterfaceBC
1756
LOGICAL :: Found, isActive, isMore, isCut
1757
TYPE(Element_t), POINTER :: pElement,Element
1758
REAL(KIND=dp) :: r
1759
INTEGER, POINTER :: MeshPerm(:) => NULL()
1760
REAL(KIND=dp), POINTER :: Values(:)
1761
CHARACTER(:), ALLOCATABLE :: VarName
1762
CHARACTER(*), PARAMETER :: Caller = 'CreateCutFEMMesh'
1763
1764
SAVE MeshPerm
1765
1766
!------------------------------------------------------------------------------
1767
IF(.NOT. (CreateBC .OR. CreateBulk)) THEN
1768
CALL Info(Caller,'Nothing to do!?')
1769
RETURN
1770
END IF
1771
1772
IF( AddMeshMode ) THEN
1773
CALL Info( Caller, 'Creating mesh including splitted elements only!')
1774
ELSE IF(.NOT. CreateBulk ) THEN
1775
CALL Info( Caller, 'Creating mesh including isoline boundary elements only!')
1776
ELSE
1777
CALL Info( Caller, 'Creating actual mesh splitted by zero levelset!')
1778
END IF
1779
1780
1781
CALL ResetTimer(Caller)
1782
1783
! Define the nodes to be included in the new mesh.
1784
!----------------------------------
1785
nn = Mesh % NumberOfNodes
1786
ne = Mesh % NumberOfEdges
1787
1788
IF(.NOT. ( CreateBulk .OR. AddMeshMode ) ) THEN
1789
ALLOCATE(MeshPerm(nn+ne))
1790
MeshPerm = 0
1791
j = 0
1792
DO i=1,nn+ne
1793
IF(CutDof(i)) THEN
1794
IF(AddMeshMode) THEN
1795
MeshPerm(i) = i
1796
ELSE
1797
j=j+1
1798
MeshPerm(i) = j
1799
END IF
1800
END IF
1801
END DO
1802
ELSE
1803
MeshPerm => Perm
1804
END IF
1805
1806
NewMesh => AllocateMesh()
1807
NewMesh % SingleMesh = Mesh % SingleMesh
1808
NewMesh % MaxNDofs = Mesh % MaxNDofs
1809
NewMesh % MeshDim = Mesh % MeshDim
1810
NewMesh % MaxElementNodes = Mesh % MaxElementNodes
1811
1812
IF( AddMeshMode ) THEN
1813
! In add mesh mode we retain the nodes and coordinates of the original mesh
1814
! and just create the elements and their topologies.
1815
! This saves work and memory.
1816
NewMesh % Name = TRIM(Mesh % Name)//'-addmesh'
1817
NodeCnt = Mesh % NumberOfNodes
1818
NewMesh % Nodes % x => Mesh % Nodes % x
1819
NewMesh % Nodes % y => Mesh % Nodes % y
1820
NewMesh % Nodes % z => Mesh % Nodes % z
1821
ELSE
1822
! This mode is intended for the isoline that is created at the levelset.
1823
NewMesh % Name = TRIM(Mesh % Name)//'-cutfem'
1824
NodeCnt = MAXVAL(MeshPerm)
1825
NewMesh % OutputActive = .TRUE.
1826
1827
CALL AllocateVector( NewMesh % Nodes % x, NodeCnt )
1828
CALL AllocateVector( NewMesh % Nodes % y, NodeCnt )
1829
CALL AllocateVector( NewMesh % Nodes % z, NodeCnt )
1830
1831
! This includes already the nodes created at the intersections.
1832
DO i=1,nn+ne
1833
j = MeshPerm(i)
1834
IF(j==0) CYCLE
1835
NewMesh % Nodes % x(j) = Mesh % Nodes % x(i)
1836
NewMesh % Nodes % y(j) = Mesh % Nodes % y(i)
1837
NewMesh % Nodes % z(j) = Mesh % Nodes % z(i)
1838
END DO
1839
END IF
1840
1841
CALL Info(Caller,'Number of nodes in CutFEM mesh: '//I2S(NodeCnt),Level=6)
1842
1843
NewMesh % NumberOfNodes = NodeCnt
1844
NewMesh % Nodes % NumberOfNodes = NodeCnt
1845
1846
InterfaceBC = ListGetInteger( Solver % Values,'CutFEM Interface BC',Found )
1847
1848
! The 1st cycle just compute the number of elements.
1849
! In between allocate the mesh elements.
1850
! The 2nd cycle add the detected elements to the list.
1851
!------------------------------------------------------
1852
DO Sweep=0,1
1853
nBulk = 0
1854
nBC = 0
1855
1856
IF(CreateBulk) THEN
1857
DO t=1, Mesh % NumberOfBulkElements
1858
Element => Mesh % Elements(t)
1859
CALL CutInterfaceCheck( Element, IsCut, IsActive, Perm )
1860
!IF(.NOT. IsActive) CYCLE
1861
IF(IsCut) THEN
1862
10 pElement => CutInterfaceBulk(Element,isCut,isMore)
1863
IF(ALL(Perm(pElement % NodeIndexes) > 0) ) THEN
1864
nBulk = nBulk + 1
1865
IF(Sweep==1) CALL AddElementData(pElement,nBulk)
1866
END IF
1867
IF(IsMore) GOTO 10
1868
ELSE IF(.NOT. AddMeshMode ) THEN
1869
! We we create only interface then the standard bulk elements are not included!
1870
IF(ANY(Perm(Element % NodeIndexes) == 0) ) CYCLE
1871
nBulk = nBulk + 1
1872
IF(Sweep==1) CALL AddElementData(Element,nBulk)
1873
END IF
1874
END DO
1875
END IF
1876
1877
IF(CreateBC) THEN
1878
DO t=1,Mesh % NumberOfBulkElements
1879
Element => Mesh % Elements(t)
1880
!CALL CutInterfaceCheck( Element, IsCut, IsActive, Perm )
1881
!IF(.NOT. IsActive) CYCLE
1882
20 pElement => CutInterfaceBC(Element,isCut,isMore)
1883
IF(isCut) THEN
1884
IF(ASSOCIATED(pElement)) THEN
1885
IF(ASSOCIATED(Perm)) THEN
1886
IF(ALL(Perm(pElement % NodeIndexes) > 0) ) THEN
1887
nBC = nBC + 1
1888
IF(Sweep==1) CALL AddElementData(pElement,nBulk+nBC,InterfaceBC)
1889
END IF
1890
END IF
1891
IF(IsMore) GOTO 20
1892
END IF
1893
END IF
1894
END DO
1895
END IF
1896
1897
1898
IF( Sweep == 0 ) THEN
1899
IF( CreateBulk ) THEN
1900
NewMesh % NumberOfBulkElements = nBulk
1901
NewMesh % NumberOfBoundaryElements = nBC
1902
ELSE
1903
NewMesh % NumberOfBulkElements = nBC
1904
NewMesh % NumberOfBoundaryElements = 0
1905
END IF
1906
1907
IF(InfoActive(25)) THEN
1908
PRINT *,'Old Element Counts:',Mesh % NumberOfBulkElements, Mesh % NumberOfBoundaryElements
1909
PRINT *,'New Element Counts:',nBulk, nBC
1910
END IF
1911
1912
CALL AllocateVector( NewMesh % Elements, nBulk+nBC )
1913
CALL Info(Caller,'New mesh allocated for '//I2S(nBulk+nBc)//' elements', Level=10 )
1914
END IF
1915
1916
END DO
1917
1918
#if 1
1919
! if add mesh mode we can just use oldparallel structures
1920
IF( ParEnv % PEs > 1 .AND. .NOT. AddMeshMode ) CALL CutFEMParallelMesh()
1921
#endif
1922
1923
1924
! If we create interface only then we have original numbering and may use
1925
IF(.NOT. AddMeshMode ) THEN
1926
CALL InterpolateLevelsetVariables()
1927
END IF
1928
1929
IF(.NOT. ( CreateBulk .OR. AddMeshMode) ) DEALLOCATE(MeshPerm)
1930
1931
CALL CheckTimer(Caller,Delete=.TRUE.)
1932
CALL Info(Caller,'Zero levelset mesh was created',Level=8)
1933
1934
CONTAINS
1935
1936
#if 1
1937
! We do not need to update the Mesh % ParallelInfo, only Matrix % ParallelInfo!
1938
1939
SUBROUTINE CutFEMParallelMesh()
1940
1941
INTEGER :: istat,n0,n
1942
1943
CALL Info(Caller,'Creating ParallelInfo for CutFEM mesh structures!',Level=10)
1944
IF(.NOT. ASSOCIATED(Mesh % ParallelInfo % GlobalDOFS) ) THEN
1945
CALL Fatal(Caller,'Original mesh has no GlobalDOFs numbering!')
1946
END IF
1947
IF(.NOT. ASSOCIATED(Mesh % Edges) ) THEN
1948
CALL Fatal(Caller,'Original mesh requires edges!')
1949
END IF
1950
1951
! Use maximum nodal index as the offset for nodes defined on cut edges.
1952
n0 = MAXVAL( Mesh % ParallelInfo % GlobalDOFs )
1953
n0 = ParallelReduction(n0,2)
1954
1955
n = NewMesh % NumberOfNodes
1956
CALL Info(Caller,'Allocating parallel structures for '//I2S(n)//' nodes',Level=10)
1957
1958
ALLOCATE(NewMesh % ParallelInfo % GlobalDOFs(n), STAT=istat )
1959
IF ( istat /= 0 ) &
1960
CALL Fatal( Caller, 'Unable to allocate NewMesh % ParallelInfo % NeighbourList' )
1961
NewMesh % ParallelInfo % GlobalDOFs = 0
1962
ALLOCATE(NewMesh % ParallelInfo % GInterface(n), STAT=istat )
1963
IF ( istat /= 0 ) &
1964
CALL Fatal( Caller, 'Unable to allocate NewMesh % ParallelInfo % NeighbourList' )
1965
NewMesh % ParallelInfo % GInterface = .FALSE.
1966
1967
ALLOCATE(NewMesh % ParallelInfo % NeighbourList(n), STAT=istat )
1968
IF ( istat /= 0 ) &
1969
CALL Fatal( Caller, 'Unable to allocate NewMesh % ParallelInfo % NeighbourList' )
1970
DO i=1,n
1971
NULLIFY(NewMesh % ParallelInfo % NeighbourList(i) % Neighbours)
1972
END DO
1973
1974
DO i=1,nn+ne
1975
j = MeshPerm(i)
1976
IF(j<=0) CYCLE
1977
1978
IF(i<=nn) THEN
1979
NewMesh % ParallelInfo % GInterface(j) = Mesh % ParallelInfo % GInterface(i)
1980
NewMesh % ParallelInfo % GlobalDOFs(j) = Mesh % ParallelInfo % GlobalDOFs(i)
1981
k = SIZE(Mesh % ParallelInfo % NeighbourList(i) % Neighbours)
1982
ALLOCATE(NewMesh % ParallelInfo % NeighbourList(j) % Neighbours(k))
1983
NewMesh % ParallelInfo % NeighbourList(j) % Neighbours = &
1984
Mesh % ParallelInfo % NeighbourList(i) % Neighbours
1985
ELSE
1986
NewMesh % ParallelInfo % GInterface(j) = Mesh % ParallelInfo % EdgeInterface(i-nn)
1987
NewMesh % ParallelInfo % GlobalDOFs(j) = n0 + Mesh % Edges(i-nn) % GElementIndex
1988
1989
k = SIZE(Mesh % ParallelInfo % EdgeNeighbourList(i-nn) % Neighbours)
1990
!PRINT *,'ass1 vals:',ParEnv % MyPe, k,j,Mesh % ParallelInfo % EdgeNeighbourList(i-nn) % Neighbours
1991
ALLOCATE(NewMesh % ParallelInfo % NeighbourList(j) % Neighbours(k))
1992
NewMesh % ParallelInfo % NeighbourList(j) % Neighbours = &
1993
Mesh % ParallelInfo % EdgeNeighbourList(i-nn) % Neighbours
1994
END IF
1995
END DO
1996
1997
1998
DO i = 1, NewMesh % NumberOfNodes
1999
IF(.NOT. ASSOCIATED(NewMesh % ParallelInfo % NeighbourList(i) % Neighbours)) THEN
2000
!PRINT *,ParEnv % MYPE, 'nn:',nn,ne, MAXVAL(MeshPerm), NewMesh % NumberOfNodes, &
2001
! SIZE(MeshPerm)
2002
CALL Fatal('CutFEMParallelMesh','Neighbours not associated: '//I2S(i))
2003
END IF
2004
END DO
2005
2006
END SUBROUTINE CutFEMParallelMesh
2007
2008
#endif
2009
2010
2011
! We can easily interpolate any variable on the new nodes created on the edge.
2012
! if we know values on both nodes.
2013
!-----------------------------------------------------------------------------
2014
SUBROUTINE InterpolateLevelsetVariables()
2015
2016
INTEGER :: iVar, dofs, l
2017
TYPE(Variable_t), POINTER :: Var
2018
REAL(KIND=dp), POINTER :: Values(:)
2019
INTEGER, POINTER :: Perm(:)
2020
LOGICAL :: IsCutVar
2021
2022
DO iVar = -1,100
2023
2024
IF(iVar == -1) THEN
2025
! We want to always interpolate the primary variable!
2026
! This is the only variable living in the "CutFEM" universe.
2027
Var => Solver % Variable
2028
VarName = Solver % Variable % name
2029
IsCutVar = .TRUE.
2030
ELSE
2031
IF(iVar == 0) THEN
2032
! We also want to interpolate the levelset variable.
2033
VarName = CutStr
2034
ELSE
2035
VarName = ListGetString( Vlist,TRIM(ProjectPrefix)//' '//I2S(iVar), Found )
2036
IF(.NOT. Found ) EXIT
2037
2038
! These are cases "-1" and "-0" that are always done!
2039
IF(VarName == Solver % Variable % Name ) CYCLE
2040
IF(VarName == CutStr ) CYCLE
2041
END IF
2042
2043
Var => VariableGet( Mesh % Variables, VarName, ThisOnly = .TRUE. )
2044
IF(.NOT. ASSOCIATED(Var)) THEN
2045
CALL Fatal('InterpolateLevelsetVariable','Could not find variable in 2D mesh: '//TRIM(VarName))
2046
END IF
2047
IsCutVar = .FALSE.
2048
END IF
2049
2050
CALL Info('InterpolateLevelsetVariable','Doing field: '//TRIM(Var % Name))
2051
2052
dofs = Var % dofs
2053
NULLIFY(Values)
2054
ALLOCATE(Values(dofs*NodeCnt))
2055
Values = 0.0_dp
2056
2057
! Create unity permutation vector.
2058
NULLIFY(Perm)
2059
ALLOCATE(Perm(NodeCnt))
2060
DO i=1,NodeCnt
2061
Perm(i) = i
2062
END DO
2063
2064
! If the size of permutation is nn+ne it is a sign that the field is associated
2065
! to the CutFEM field.
2066
IF(IsCutVar) THEN
2067
ntot = nn + ne
2068
ELSE
2069
ntot = nn
2070
END IF
2071
2072
DO i=1,ntot
2073
j = Var % Perm(i)
2074
k = MeshPerm(i)
2075
IF(j==0 .OR. k==0) CYCLE
2076
DO l=1,dofs
2077
Values(dofs*(k-1)+l) = Var % Values(dofs*(j-1)+l)
2078
END DO
2079
END DO
2080
2081
! We do not want to interpolate the cut var that has been computed exactly to the new virtual nodes.
2082
! The interpolated values would be worse than the computed ones.
2083
IF(.NOT. IsCutVar ) THEN
2084
CALL Info(Caller,'Interpolating values: '//TRIM(VarName),Level=10)
2085
DO i=1,ne
2086
k = MeshPerm(nn+i)
2087
IF(k==0) CYCLE
2088
r = CutInterp(i)
2089
2090
Values(k) = 0.0_dp
2091
2092
j = Var % Perm(Mesh % Edges(i) % NodeIndexes(1))
2093
IF(j>0) THEN
2094
DO l=1,dofs
2095
Values(dofs*(k-1)+l) = r*Var % Values(dofs*(j-1)+l)
2096
END DO
2097
END IF
2098
2099
j = Var % Perm(Mesh % Edges(i) % NodeIndexes(2))
2100
IF(j>0) THEN
2101
DO l=1,dofs
2102
Values(dofs*(k-1)+l) = Values(dofs*(k-1)+l) + (1-r)*Var % Values(dofs*(j-1)+l)
2103
END DO
2104
END IF
2105
END DO
2106
END IF
2107
2108
CALL Info(Caller,'Projected variable: '//TRIM(VarName),Level=10)
2109
CALL VariableAddVector( NewMesh % Variables, NewMesh, Solver, VarName, Var % Dofs, Values, Perm )
2110
2111
IF(InfoActive(25)) THEN
2112
PRINT *,'Range:',MINVAL(Values),MAXVAL(Values),SIZE(Values), Var % Dofs, SIZE(Values)
2113
END IF
2114
END DO
2115
2116
END SUBROUTINE InterpolateLevelsetVariables
2117
2118
2119
! Here we just add some data to the new mesh.
2120
!--------------------------------------------
2121
SUBROUTINE AddelementData(pElement,ElemInd,BCtag)
2122
TYPE(Element_t), POINTER :: pElement
2123
INTEGER :: ElemInd
2124
INTEGER, OPTIONAL :: BCTag
2125
2126
TYPE(Element_t), POINTER :: Enew
2127
INTEGER :: n
2128
2129
Enew => NewMesh % Elements(ElemInd)
2130
Enew % PartIndex = pElement % PartIndex
2131
Enew % BodyId = pElement % BodyId
2132
Enew % ElementIndex = ElemInd
2133
2134
n = pElement % TYPE % NumberOfNodes
2135
Enew % TYPE => GetElementType(pElement % TYPE % ElementCode)
2136
2137
CALL AllocateVector( ENew % NodeIndexes, n)
2138
IF( AddMeshMode ) THEN
2139
Enew % NodeIndexes(1:n) = pElement % NodeIndexes(1:n)
2140
ELSE
2141
Enew % NodeIndexes(1:n) = MeshPerm(pElement % NodeIndexes(1:n))
2142
END IF
2143
Enew % NDOFs = n
2144
Enew % EdgeIndexes => NULL()
2145
Enew % FaceIndexes => NULL()
2146
2147
IF(PRESENT(BCTag)) THEN
2148
ALLOCATE(Enew % BoundaryInfo)
2149
Enew % BoundaryInfo % Constraint = BCTag
2150
END IF
2151
2152
! This effects only parallel runs, but testing parallel costs more...
2153
Enew % PartIndex = ParEnv % MyPe
2154
2155
END SUBROUTINE AddelementData
2156
2157
END FUNCTION CreateCutFEMMesh
2158
!------------------------------------------------------------------------------
2159
2160
2161
2162
! Here we create a 1D mesh on the zero level-set and convent it to new location,
2163
! and compute new signed distance.
2164
!--------------------------------------------
2165
SUBROUTINE LevelSetUpdate(Solver,Mesh)
2166
2167
TYPE(Solver_t) :: Solver
2168
TYPE(Mesh_t) :: Mesh
2169
2170
TYPE(Variable_t), POINTER :: PhiVar1D, PhiVar2D, pVar
2171
TYPE(Mesh_t), POINTER :: IsoMesh => NULL()
2172
REAL(KIND=dp), POINTER :: x(:), y(:)
2173
REAL(KIND=dp) :: val, Vx, Vy, dt, VPhi, PhiMax, BW, cosphi0
2174
REAL(KIND=dp), ALLOCATABLE :: NodeDiff(:),ValStore(:)
2175
CHARACTER(:), ALLOCATABLE :: str
2176
LOGICAL :: Found, Nonzero, MovingLevelset, NormalMove, NodeHistory, Positive, CheckLS
2177
LOGICAL, ALLOCATABLE :: Trust(:), DoesElemIntersect(:)
2178
INTEGER :: nVar,i,j,iAvoid,iSolver,k,l,counter,NNeighbours,MyPE
2179
INTEGER, ALLOCATABLE :: LocalPerm(:),Neighbours(:),nSend(:),nRecv(:)
2180
INTEGER, POINTER :: NodeIndexes(:)
2181
2182
TYPE PolylineData_t
2183
INTEGER :: nLines = 0, nNodes = 0
2184
REAL(KIND=dp), ALLOCATABLE :: Vals(:,:)
2185
INTEGER, ALLOCATABLE :: Prev(:,:), Next(:,:)
2186
LOGICAL, ALLOCATABLE :: Intersect(:)
2187
REAL(KIND=dp) :: IsoLineBB(4), MeshBB(4)
2188
END TYPE PolylineData_t
2189
TYPE(PolylineData_t), ALLOCATABLE, TARGET, SAVE :: PolylineData(:)
2190
2191
TYPE Intersects_t
2192
INTEGER :: nIntersects = 0, Size = 0
2193
REAL(KIND=dp), ALLOCATABLE :: Intersection(:,:), LS(:,:)
2194
INTEGER, ALLOCATABLE :: Elements(:,:)
2195
LOGICAL, ALLOCATABLE :: Found(:,:)
2196
END TYPE Intersects_t
2197
TYPE(Intersects_t), ALLOCATABLE, TARGET, SAVE :: Intersects(:)
2198
2199
2200
SAVE IsoMesh
2201
2202
IsoMesh => CreateCutFEMMesh(Solver,Mesh,Solver % Variable % Perm,&
2203
.TRUE.,.FALSE.,.FALSE.,Solver % Values,'isoline variable')
2204
IsoMesh % Name = TRIM(Mesh % Name)//'-isomesh'
2205
2206
pVar => VariableGet( Mesh % Variables,'timestep size' )
2207
dt = pVar % Values(1)
2208
2209
phiVar1D => VariableGet( IsoMesh % Variables, CutStr, ThisOnly = .TRUE.)
2210
IF(.NOT. ASSOCIATED(PhiVar1D)) THEN
2211
CALL Fatal('LevelSetUpdate','Levelset function ("'//TRIM(CutStr)//'") needed in 1D mesh!')
2212
END IF
2213
2214
! This should be ok by construction but some testing does not hurt...
2215
DO i=1,SIZE(phiVar1D % Perm)
2216
IF(i /= PhiVar1D % Perm(i) ) THEN
2217
CALL Fatal('LevelSetUpdate','PhiVar1D permutation not unity map')
2218
END IF
2219
END DO
2220
2221
phiVar2D => VariableGet( Mesh % Variables, CutStr, ThisOnly = .TRUE.)
2222
IF(.NOT. ASSOCIATED(PhiVar2D)) THEN
2223
CALL Fatal('LevelSetUpdate','Levelset function needed in 2D mesh!')
2224
END IF
2225
2226
! We can move the levelset either by moving its coordinates, or
2227
! by adding values to the levelset function. The first should be used if we
2228
! know the direction of the movement and the latter if we know the normal movement.
2229
x => Isomesh % Nodes % x
2230
y => Isomesh % Nodes % y
2231
2232
MovingLevelset = .FALSE.
2233
2234
! It turns out that if the polyline is not a zero levelset but something else
2235
! we need to try to ensure that the direction is almost normal to the element segment.
2236
VPhi = ListGetCReal( Solver % Values,'CutFEM critical angle',Found )
2237
IF(.NOT. Found) Vphi = 60.0_dp
2238
cosphi0 = COS(Vphi*PI/180.0_dp)
2239
2240
Nonzero = ListGetLogical( Solver % Values,'CutFEM signed distance nonzero',Found )
2241
2242
NormalMove = ListGetLogical( Solver % Values,'CutFEM normal move',Found )
2243
2244
NodeHistory = ListGetLogical( Solver % Values,'CutFEM node history',Found )
2245
2246
CheckLS = ListGetLogical( Solver % Values,'CutFEM check ls field',Found )
2247
2248
ALLOCATE(NodeDiff(Mesh % NumberOfNodes), Trust(Mesh % NumberOfNodes))
2249
NodeDiff = 0.0_dp; Trust = .FALSE.
2250
IF(NodeHistory) THEN
2251
IF(.NOT. ALLOCATED(PolylineData)) THEN
2252
ALLOCATE(PolylineData(ParEnv % PEs))
2253
END IF
2254
CALL PopulatePolyline()
2255
2256
DO i=1, Mesh % NumberOfNodes
2257
j = PhiVar2D % Perm(i)
2258
IF(j==0 .AND. .NOT. NonZero) CYCLE
2259
IF(j==0) STOP
2260
2261
val = SignedDistance(i, Trust(i))
2262
2263
IF(Trust(i)) THEN
2264
NodeDiff(i) = PhiVar2D % Values(j) - val
2265
ELSE
2266
NodeDiff(i) = 0.0_dp
2267
END IF
2268
END DO
2269
2270
! Deallocate data, next time this will be different.
2271
DO i=1,ParEnv % PEs
2272
IF(PolylineData(i) % nLines > 0) THEN
2273
DEALLOCATE(PolylineData(i) % Vals)
2274
DEALLOCATE(PolylineData(i) % Prev, &
2275
PolylineData(i) % Next)
2276
END IF
2277
END DO
2278
END IF
2279
2280
2281
! This assumes constant levelset convection. Mainly for testing.
2282
Vx = ListGetCReal( Solver % Values,'Levelset Velocity 1',Found )
2283
IF(Found) THEN
2284
IF(ABS(Vx) > EPSILON(Vx)) THEN
2285
MovingLevelset = .TRUE.
2286
x = x + Vx * dt
2287
END IF
2288
END IF
2289
2290
Vy = ListGetCReal( Solver % Values,'Levelset Velocity 2',Found )
2291
IF(Found) THEN
2292
IF(ABS(Vy) > EPSILON(Vy)) THEN
2293
MovingLevelset = .TRUE.
2294
y = y + Vy * dt
2295
END IF
2296
END IF
2297
2298
2299
! This assumes constant calving speed. Mainly for testing.
2300
VPhi = ListGetCReal( Solver % Values,'Levelset Speed',Found )
2301
IF(Found) THEN
2302
IF(ABS(VPhi) > EPSILON(VPhi)) THEN
2303
PhiVar1D % Values = PhiVar1D % Values + VPhi * dt
2304
MovingLevelset = .TRUE.
2305
NonZero = .TRUE.
2306
END IF
2307
END IF
2308
2309
! Position dependent levelset velocity & calving speed.
2310
str = ListGetString( Solver % Values,'Levelset Velocity Variable',Found )
2311
IF(Found) THEN
2312
pVar => VariableGet( IsoMesh % Variables,TRIM(str)//' 1',UnfoundFatal=.TRUE.)
2313
x = x + pVar % Values * dt
2314
pVar => VariableGet( IsoMesh % Variables,TRIM(str)//' 2',UnfoundFatal=.TRUE.)
2315
y = y + pVar % Values * dt
2316
MovingLevelset = .TRUE.
2317
END IF
2318
2319
str = ListGetString( Solver % Values,'Levelset Speed Variable',Found )
2320
IF(Found) THEN
2321
VPhi = ListGetCReal( Solver % Values,'Levelset Speed Multiplier',Found )
2322
IF(.NOT. Found) VPhi = 1.0_dp
2323
pVar => VariableGet( IsoMesh % Variables,TRIM(str),UnfoundFatal=.TRUE.)
2324
!PRINT *,'Levelset Speed range:',MINVAL(pVar % Values), MAXVAL(pVar % Values)
2325
PhiVar1D % Values = PhiVar1D % Values - pVar % Values * Vphi * dt
2326
!PRINT *,'Levelset range:',MINVAL(PhiVar1D % Values), MAXVAL(PhiVar1D % Values)
2327
MovingLevelset = .TRUE.
2328
Nonzero = .TRUE.
2329
END IF
2330
2331
PhiMax = MAXVAL(ABS(PhiVar1D % Values))
2332
PhiMax = 1.01 * ( PhiMax + SQRT(Vx**2+Vy**2)*dt )
2333
2334
IF(NormalMove .OR. NonZero) THEN
2335
CALL LevelsetNormalMove()
2336
NonZero = .FALSE.
2337
END IF
2338
2339
IF(.NOT. ALLOCATED(PolylineData)) THEN
2340
ALLOCATE(PolylineData(ParEnv % PEs))
2341
END IF
2342
CALL PopulatePolyline()
2343
2344
Trust = .FALSE.
2345
DO i=1, Mesh % NumberOfNodes
2346
j = PhiVar2D % Perm(i)
2347
IF(j==0 .AND. .NOT. NonZero) CYCLE
2348
IF(j==0) STOP
2349
#if 0
2350
val = PhiVar2D % Values(j)
2351
IF(val > BW ) THEN
2352
val = val - BW
2353
ELSE IF(val < -BW ) THEN
2354
val = val + BW
2355
ELSE
2356
val = SignedDistance(i)
2357
END IF
2358
#else
2359
val = SignedDistance(i, Trust(i))
2360
#endif
2361
2362
IF(MovingLevelset) THEN
2363
PhiVar2D % Values(j) = val
2364
END IF
2365
END DO
2366
2367
IF(CheckLS) CALL CheckLSField()
2368
2369
IF(NodeHistory) THEN
2370
PhiVar2D % Values(PhiVar2D % Perm) = PhiVar2D % Values(PhiVar2D % Perm) + NodeDiff
2371
END IF
2372
2373
! Deallocate data, next time this will be different.
2374
DO i=1,ParEnv % PEs
2375
IF(PolylineData(i) % nLines > 0) THEN
2376
DEALLOCATE(PolylineData(i) % Vals)
2377
DEALLOCATE(PolylineData(i) % Prev, &
2378
PolylineData(i) % Next)
2379
END IF
2380
END DO
2381
2382
Solver % Mesh % Next => IsoMesh
2383
2384
CONTAINS
2385
2386
!------------------------------------------------------------------------------
2387
!> Moves levelset coordinates in normal direction.
2388
!> This eliminates the need to compute distance relative to non-zero levelset.
2389
!------------------------------------------------------------------------------
2390
SUBROUTINE LevelsetNormalMove()
2391
!------------------------------------------------------------------------------
2392
REAL(KIND=dp) :: x0,y0,x1,y1,ss,phi0,phi1,Normal(2),wei
2393
INTEGER :: i,j,k,n,i0,i1
2394
2395
REAL(KIND=dp), ALLOCATABLE :: dx(:), dy(:), NodeWeight(:)
2396
2397
n = SIZE(PhiVar1D % Values)
2398
ALLOCATE(dx(n),dy(n),NodeWeight(n))
2399
2400
! Allocate temporal space since we cannot make the computation in-place since the
2401
! coordinates and levelset will be needed more than one times.
2402
dx = 0.0_dp
2403
dy = 0.0_dp
2404
NodeWeight = 0.0_dp
2405
2406
DO i=1,IsoMesh % NumberOfBulkElements
2407
i0 = IsoMesh % Elements(i) % NodeIndexes(1)
2408
i1 = IsoMesh % Elements(i) % NodeIndexes(2)
2409
2410
x0 = x(i0); y0 = y(i0)
2411
x1 = x(i1); y1 = y(i1)
2412
2413
ss = (x0-x1)**2 + (y0-y1)**2
2414
IF(ss < EPSILON(ss) ) CYCLE
2415
2416
ss = SQRT(ss)
2417
Normal(1) = (y1-y0)
2418
Normal(2) = -(x1-x0)
2419
Normal = Normal / ss
2420
2421
wei = 1.0_dp
2422
! wei = ss
2423
2424
phi0 = PhiVar1D % Values(i0)
2425
phi1 = PhiVar1D % Values(i1)
2426
2427
dx(i0) = dx(i0) + Normal(1) * phi0
2428
dy(i0) = dy(i0) + Normal(2) * phi0
2429
NodeWeight(i0) = NodeWeight(i0) + wei
2430
2431
dx(i1) = dx(i1) + Normal(1) * phi1
2432
dy(i1) = dy(i1) + Normal(2) * phi1
2433
NodeWeight(i1) = NodeWeight(i1) + wei
2434
END DO
2435
2436
! Finally move the coordinates to the direction of the averaged normal.
2437
WHERE(NodeWeight > EPSILON(wei))
2438
x = x + dx / NodeWeight
2439
y = y + dy / NodeWeight
2440
END WHERE
2441
PhiVar1D % Values = 0.0_dp
2442
2443
DEALLOCATE(dx,dy,NodeWeight)
2444
2445
END SUBROUTINE LevelsetNormalMove
2446
2447
2448
! post processing for errors when line segs intersect either at nodes or arbitrary
2449
! here we assume if previous elem was all +ve or all -ve then no intersection
2450
! therefore any node that has a different sign to remaining nodes this a dir error
2451
! modify to use bounding box for triangle-polyline intersects
2452
2453
SUBROUTINE CheckLSField()
2454
2455
ALLOCATE(DoesElemIntersect(Mesh % NumberOfBulkElements))
2456
CALL GetElemIntersectLogical(DoesElemIntersect)
2457
2458
DO i=1, Mesh % NumberOfNodes
2459
IF(Trust(i)) CYCLE
2460
2461
FOund = .FALSE.
2462
DO j=1, Mesh % NumberOfBulkElements
2463
NodeIndexes => Mesh % Elements(j) % NodeIndexes
2464
IF(.NOT. ANY(NodeIndexes == i)) CYCLE
2465
IF(DoesElemIntersect(j)) CYCLE
2466
Found=.TRUE.
2467
EXIT
2468
END DO
2469
2470
IF(.NOT. Found) Trust(i) = .TRUE. ! we have no choice but to trust
2471
END DO
2472
2473
counter = 0
2474
2475
! allocate boundary nodes
2476
2477
IF(ParEnv % PEs > 1) THEN
2478
NNeighbours = COUNT(ParEnv % IsNeighbour)
2479
Neighbours = PACK( (/ (i,i=1,ParEnv % PEs) /), ParEnv % IsNeighbour)
2480
2481
ALLOCATE(nSend(NNeighbours), nRecv(NNeighbours))
2482
MyPE = ParEnv % MyPE + 1
2483
ALLOCATE(LocalPerm( MAXVAL(Mesh % ParallelInfo % GlobalDOFs)))
2484
2485
!local perm gdof>local
2486
DO i=1, Mesh % NumberOfNodes
2487
LocalPerm(Mesh % ParallelInfo % GlobalDOFs(i)) = i
2488
END DO
2489
END IF
2490
2491
! all reduce trust here
2492
2493
DO WHILE(.TRUE.)
2494
2495
IF(ALL(Trust) .AND. ParEnv % PEs == 1) EXIT
2496
2497
DO i=1, Mesh % NumberOfBulkElements
2498
IF(DoesElemIntersect(i)) CYCLE
2499
2500
NodeIndexes => Mesh % Elements(i) % NodeIndexes
2501
!loop elems cycle if intersect
2502
2503
IF(COUNT(.NOT. Trust(NodeIndexes)) /= 1) CYCLE ! can only correct one at a time
2504
2505
! all same signs so we must trust all nodes
2506
! only elems with some trusted nodes make it this for so ok
2507
IF(ALL(PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes)) > 0.0_dp) .OR. &
2508
ALL(PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes)) < 0.0_dp)) THEN
2509
Trust(NodeIndexes) = .TRUE.
2510
END IF
2511
2512
Positive = .FALSE.
2513
IF(COUNT(PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes)) > 0.0_dp) > 1) Positive =.TRUE.
2514
DO l=1, Mesh % Elements(i) % TYPE % NumberOfNodes
2515
IF(Positive .AND. PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes(l))) < 0 .AND. &
2516
.NOT. Trust(NodeIndexes(l))) THEN
2517
PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes(l))) = &
2518
-1*PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes(l)))
2519
Trust(NodeIndexes(l)) = .TRUE.
2520
END IF
2521
IF(.NOT. Positive .AND. PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes(l))) > 0 .AND. &
2522
.NOT. Trust(NodeIndexes(l))) THEN
2523
PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes(l))) = &
2524
-1*PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes(l)))
2525
Trust(NodeIndexes(l)) = .TRUE.
2526
END IF
2527
END DO
2528
END DO
2529
2530
2531
IF(ParEnv % PEs > 1) THEN
2532
!parcomm
2533
BLOCK
2534
INTEGER, ALLOCATABLE :: SendIndices(:,:),PTrustGDOFs(:)
2535
REAL(KIND=dp), ALLOCATABLE :: Pvals(:)
2536
INTEGER :: comm, ierr, status(MPI_STATUS_SIZE),Phase,cnt
2537
LOGICAL :: Finish
2538
2539
comm = Solver % Matrix % Comm
2540
2541
! all trust reduce here
2542
Finish = ALL(Trust)
2543
CALL MPI_ALLREDUCE(MPI_IN_PLACE, Finish, 1, MPI_LOGICAL, MPI_LAND, comm, ierr)
2544
2545
IF(Finish) EXIT
2546
2547
CALL MPI_BARRIER(comm, ierr)
2548
2549
DO Phase=0,1
2550
nSend = 0
2551
DO i=1, Mesh % NumberOfNodes
2552
IF(.NOT. Trust(i)) CYCLE
2553
DO j=1, NNeighbours
2554
IF(ANY(Mesh % ParallelInfo % NeighbourList(i) % Neighbours == Neighbours(j)-1)) THEN
2555
nSend(j) = nSend(j) + 1
2556
IF(Phase == 1) SendIndices(j, nSend(j)) = i
2557
END IF
2558
END DO
2559
END DO
2560
2561
IF(Phase == 0) ALLOCATE(SendIndices(NNeighbours, MAXVAL(nSend)))
2562
END DO
2563
2564
DO i=1,NNeighbours
2565
j = Neighbours(i)
2566
2567
CALL MPI_BSEND( nSend(i), 1, MPI_INTEGER,j-1, &
2568
1001, comm, ierr )
2569
2570
IF(nSend(i) == 0) CYCLE
2571
2572
CALL MPI_BSEND( Mesh % ParallelInfo % GlobalDOFS(SendIndices(i, : nSend(i))), nSend(i), MPI_INTEGER,j-1, &
2573
1002, comm, ierr )
2574
CALL MPI_BSEND( PhiVar2D % Values(PhiVar2D % Perm(SendIndices(i, : nSend(i)))), nSend(i), MPI_DOUBLE_PRECISION,j-1, &
2575
1003, comm, ierr )
2576
2577
END DO
2578
2579
DO i=1,NNeighbours
2580
j = Neighbours(i)
2581
2582
CALL MPI_RECV( nRecv(i), 1, MPI_INTEGER,j-1, &
2583
1001, comm, status, ierr )
2584
END DO
2585
2586
ALLOCATE(PTrustGDOFs(SUM(nRecv)), Pvals(SUM(nRecv)))
2587
2588
cnt = 0
2589
DO i=1,NNeighbours
2590
j = Neighbours(i)
2591
IF(nRecv(i) == 0) CYCLE
2592
2593
CALL MPI_RECV( PTrustGDOFs(cnt+1:cnt+nRecv(i)), nRecv(i), MPI_INTEGER,j-1, &
2594
1002, comm, status, ierr )
2595
CALL MPI_RECV( Pvals(cnt+1:cnt+nRecv(i)), nRecv(i), MPI_DOUBLE_PRECISION,j-1, &
2596
1003, comm, status, ierr )
2597
cnt = cnt + nRecv(i)
2598
END DO
2599
2600
2601
CALL MPI_BARRIER( comm, ierr )
2602
2603
Trust(LocalPerm(PTrustGDOFs)) = .TRUE.
2604
PhiVar2D % Values(PhiVar2D % Perm(LocalPerm(PTrustGDOFs))) = Pvals
2605
2606
DEALLOCATE(PTrustGDOFs,Pvals,SendIndices)
2607
2608
END BLOCK
2609
END IF
2610
2611
counter = counter + 1
2612
IF(counter > 10 ) CALL FATAL('CheckLSField','Stuck in loop')
2613
END DO
2614
2615
END SUBROUTINE CheckLSField
2616
2617
!strategy loop through all and mark elem/polyline intersections
2618
! use dir to get whether inside
2619
! remove all until next intersection
2620
SUBROUTINE GetElemIntersectLogical(DoesElemIntersect)
2621
!------------------------------------------------------------------------------
2622
!TYPE(Mesh_t), POINTER :: Mesh
2623
!TYPE(Variable_t), POINTER :: PhiVar2D
2624
!TYPE(PolylineData_t), POINTER :: PolylineData
2625
LOGICAL :: DoesElemIntersect(:)
2626
!------------------------------------------------------------------------------
2627
INTEGER :: i,j,k,nLines
2628
INTEGER, POINTER :: NodeIndexes(:)
2629
REAL(KIND=dp) :: xmin_tri,xmax_tri,ymin_tri,ymax_tri,xmin_seg,xmax_seg,ymin_seg,ymax_seg,&
2630
a1(2),a2(2),intersect_p(2),x0,x1,y0,y1
2631
LOGICAL :: intersect
2632
2633
DoesElemIntersect = .FALSE.
2634
DO i=1,Mesh % NumberOfBulkElements
2635
NodeIndexes => Mesh % Elements(i) % NodeIndexes
2636
2637
xmin_tri = MINVAL(Mesh % Nodes % x(NodeIndexes))
2638
xmax_tri = MAXVAL(Mesh % Nodes % x(NodeIndexes))
2639
ymin_tri = MINVAL(Mesh % Nodes % y(NodeIndexes))
2640
ymax_tri = MAXVAL(Mesh % Nodes % y(NodeIndexes))
2641
2642
IF(ALL(PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes)) > 0.0_dp)) CYCLE
2643
IF(ALL(PhiVar2D % Values(PhiVar2D % Perm(NodeIndexes)) < 0.0_dp)) CYCLE
2644
2645
! can speed this up using local polylines rather than global
2646
Intersect = .FALSE.
2647
DO k=1,ParEnv % PEs
2648
DO j=1, PolylineData(k) % nLines
2649
2650
x0 = PolylineData(k) % Vals(j,1)
2651
x1 = PolylineData(k) % Vals(j,2)
2652
y0 = PolylineData(k) % Vals(j,3)
2653
y1 = PolylineData(k) % Vals(j,4)
2654
2655
xmin_seg = MIN(x0, x1)
2656
xmax_seg = MAX(x0, x1)
2657
ymin_seg = MIN(y0, y1)
2658
ymax_seg = MAX(y0, y1)
2659
2660
! Quick rejection
2661
IF(xmax_seg < xmin_tri .OR. xmax_tri < xmin_seg) CYCLE
2662
IF(ymax_seg < ymin_tri .OR. ymax_tri < ymin_seg) CYCLE
2663
2664
! do we actually intersect?
2665
a1(1) = Mesh % Nodes % x(NodeIndexes(1))
2666
a1(2) = Mesh % Nodes % y(NodeIndexes(1))
2667
a2(1) = Mesh % Nodes % x(NodeIndexes(2))
2668
a2(2) = Mesh % Nodes % y(NodeIndexes(2))
2669
CALL LineSegmentsIntersect(a1,a2,(/x0,y0/),(/x1,y1/),intersect_p,intersect)
2670
2671
IF(.NOT. intersect) THEN !1,3
2672
a2(1) = Mesh % Nodes % x(NodeIndexes(3))
2673
a2(2) = Mesh % Nodes % y(NodeIndexes(3))
2674
CALL LineSegmentsIntersect(a1,a2,(/x0,y0/),(/x1,y1/),intersect_p,intersect)
2675
END IF
2676
2677
IF(.NOT. intersect) THEN !2,3
2678
a1(1) = Mesh % Nodes % x(NodeIndexes(2))
2679
a1(2) = Mesh % Nodes % y(NodeIndexes(2))
2680
CALL LineSegmentsIntersect(a1,a2,(/x0,y0/),(/x1,y1/),intersect_p,intersect)
2681
END IF
2682
2683
IF(Intersect) THEN
2684
DoesElemIntersect(i) = .TRUE.
2685
EXIT
2686
END IF
2687
2688
END DO
2689
IF(Intersect) EXIT
2690
END DO
2691
END DO
2692
2693
2694
END SUBROUTINE GetElemIntersectLogical
2695
2696
!------------------------------------------------------------------------------
2697
!> Computes the signed distance to zero levelset.
2698
!------------------------------------------------------------------------------
2699
SUBROUTINE PopulatePolyline()
2700
!------------------------------------------------------------------------------
2701
REAL(KIND=dp) :: x0,y0,x1,y1,ss,TotLineLen,phi0,phi1,&
2702
x2,x3,y2,y3,xmin0,xmax0,ymin0,ymax0,xmin1,xmax1,ymin1,ymax1,intersect_p(2)
2703
INTEGER :: i,j,k,n,m,i0,i1,nCol,dofs,k2,m2,i2,i3,NNeighbours,neighbour,p0,p1
2704
INTEGER, ALLOCATABLE :: PL_local(:,:),NodeToElem(:,:),NodeElemCount(:),RecvElems(:,:),&
2705
rdisps(:),RecvFrom(:),Neighbours(:)
2706
TYPE(Variable_t), POINTER :: Var1D
2707
INTEGER :: iVar,MyPe,PEs,Phase,nLines,Next,counter,NInter,SharedCount,NextP,nMax,NPInter,Nexts(2)
2708
LOGICAL, ALLOCATABLE :: Skip(:),RemoveLines(:,:)
2709
LOGICAL :: intersect
2710
!------------------------------------------------------------------------------
2711
2712
nCol = 6
2713
2714
nVar = 0
2715
iAvoid = 0
2716
iSolver = 0
2717
TotLineLen = 0.0_dp
2718
2719
DO k = 1,100
2720
str = ListGetString( Solver % Values,'isoline variable '//I2S(k), Found )
2721
IF(.NOT. Found ) EXIT
2722
2723
! The levelset is really computed, do not interpolate it.
2724
IF(str == CutStr) iAvoid = k
2725
IF(str == Solver % Variable % Name) iSolver = k
2726
2727
Var1D => VariableGet( IsoMesh % Variables, str, ThisOnly = .TRUE. )
2728
IF(.NOT. ASSOCIATED(Var1D)) EXIT
2729
nVar = k
2730
nCol = nCol + 2 * Var1D % Dofs
2731
2732
IF(InfoActive(25)) THEN
2733
PRINT *,'Mesh1D Range for '//TRIM(Var1D % Name)//': ',&
2734
MINVAL(Var1D % Values), MAXVAL(Var1D % Values)
2735
END IF
2736
END DO
2737
2738
m = Isomesh % NumberOfBulkElements
2739
MyPe = ParEnv % MyPe + 1
2740
PEs = ParEnv % PEs
2741
2742
IF(PEs > 1) THEN
2743
BLOCK
2744
INTEGER, ALLOCATABLE :: SendCount(:),SendElems(:,:)
2745
INTEGER :: comm, ierr, status(MPI_STATUS_SIZE)
2746
2747
comm = Solver % Matrix % Comm
2748
2749
ALLOCATE(SendCount(PEs))
2750
2751
! if a node is at an intersect can be shared with 3+ parts
2752
SendCount = 0
2753
DO k=1, PEs
2754
IF(k==MyPE) CYCLE
2755
DO i=1, IsoMesh % NumberOfBulkElements
2756
i0 = IsoMesh % Elements(i) % NodeIndexes(1)
2757
i1 = IsoMesh % Elements(i) % NodeIndexes(2)
2758
IF(.NOT. (IsoMesh % ParallelInfo % GInterface(i0) .OR. &
2759
IsoMesh % ParallelInfo % GInterface(i1)) ) CYCLE
2760
IF(IsoMesh % ParallelInfo % GInterface(i0)) THEN
2761
IF(ANY(IsoMesh % ParallelInfo % NeighbourList(i0) % Neighbours == k-1)) THEN
2762
SendCount(k) = SendCount(k) + 1
2763
END IF
2764
END IF
2765
IF(IsoMesh % ParallelInfo % GInterface(i1)) THEN
2766
IF(ANY(IsoMesh % ParallelInfo % NeighbourList(i1) % Neighbours == k-1)) THEN
2767
SendCount(k) = SendCount(k) + 1
2768
END IF
2769
END IF
2770
END DO
2771
END DO
2772
2773
ALLOCATE(SendElems(SUM(SendCount),2))
2774
2775
counter = 0
2776
DO k=1, PEs
2777
IF(k==MyPE) CYCLE
2778
DO i=1, IsoMesh % NumberOfBulkElements
2779
i0 = IsoMesh % Elements(i) % NodeIndexes(1)
2780
i1 = IsoMesh % Elements(i) % NodeIndexes(2)
2781
IF(.NOT. (IsoMesh % ParallelInfo % GInterface(i0) .OR. &
2782
IsoMesh % ParallelInfo % GInterface(i1)) ) CYCLE
2783
IF(IsoMesh % ParallelInfo % GInterface(i0)) THEN
2784
IF(ANY(IsoMesh % ParallelInfo % NeighbourList(i0) % Neighbours == k-1)) THEN
2785
counter = counter + 1
2786
SendElems(counter,1) = i
2787
SendElems(counter,2) = IsoMesh % ParallelInfo % GlobalDOFS(i0)
2788
END IF
2789
END IF
2790
IF(IsoMesh % ParallelInfo % GInterface(i1)) THEN
2791
IF(ANY(IsoMesh % ParallelInfo % NeighbourList(i1) % Neighbours == k-1)) THEN
2792
counter = counter + 1
2793
SendElems(counter,1) = i
2794
SendElems(counter,2) = IsoMesh % ParallelInfo % GlobalDOFS(i1)
2795
END IF
2796
END IF
2797
END DO
2798
END DO
2799
2800
ALLOCATE(rdisps(PEs))
2801
2802
! send count should equal recvcount
2803
!CALL MPI_ALLTOALL(SendCount, 1, MPI_INTEGER, RecvCount, 1, MPI_INTEGER, comm, ierr)
2804
2805
rdisps(1) = 0
2806
DO i=2, PEs
2807
rdisps(i) = rdisps(i-1) + SendCount(i-1)
2808
END DO
2809
2810
SharedCount = SUM(SendCount)
2811
ALLOCATE(RecvElems(SharedCount,2))
2812
2813
! these can be all gather?!
2814
CALL MPI_Alltoallv(SendElems(:,1), SendCount, rdisps, MPI_INTEGER, &
2815
RecvElems(:,1), SendCount, rdisps, MPI_INTEGER, &
2816
comm, ierr)
2817
CALL MPI_Alltoallv(SendElems(:,2), SendCount, rdisps, MPI_INTEGER, &
2818
RecvElems(:,2), SendCount, rdisps, MPI_INTEGER, &
2819
comm, ierr)
2820
2821
ALLOCATE(RecvFrom(SharedCount))
2822
counter = 0
2823
DO i=1,PEs
2824
RecvFrom(counter+1:counter+SendCount(i)) = i
2825
counter = counter + SendCount(i)
2826
END DO
2827
2828
END BLOCK
2829
END IF
2830
2831
! We may find use for bounding boxes later on.
2832
2833
PolylineData(MyPe) % IsoLineBB(1) = MINVAL(IsoMesh % Nodes % x)
2834
PolylineData(MyPe) % IsoLineBB(2) = MAXVAL(IsoMesh % Nodes % x)
2835
PolylineData(MyPe) % IsoLineBB(3) = MINVAL(IsoMesh % Nodes % y)
2836
PolylineData(MyPe) % IsoLineBB(4) = MAXVAL(IsoMesh % Nodes % y)
2837
#if 0
2838
PolylineData(MyPe) % MeshBB(1) = MINVAL(Mesh % Nodes % x)
2839
PolylineData(MyPe) % MeshBB(2) = MAXVAL(Mesh % Nodes % x)
2840
PolylineData(MyPe) % MeshBB(3) = MINVAL(Mesh % Nodes % y)
2841
PolylineData(MyPe) % MeshBB(4) = MAXVAL(Mesh % Nodes % y)
2842
#endif
2843
2844
ALLOCATE(Skip(IsoMesh % NumberOfBulkElements), &
2845
NodeToElem(IsoMesh % NumberOfNodes,2), NodeElemCount(IsoMesh % NumberOfNodes))
2846
2847
IF(.NOT. ALLOCATED(Intersects)) &
2848
ALLOCATE(Intersects(PEs))
2849
CALL InitialiseIntersects(10)
2850
2851
Skip = .FALSE.
2852
NodeToElem = 0; NodeElemCount = 0
2853
NInter = 0
2854
DO Phase=0,1
2855
m = 0
2856
DO i=1,IsoMesh % NumberOfBulkElements
2857
i0 = IsoMesh % Elements(i) % NodeIndexes(1)
2858
i1 = IsoMesh % Elements(i) % NodeIndexes(2)
2859
2860
x0 = x(i0); y0 = y(i0)
2861
x1 = x(i1); y1 = y(i1)
2862
2863
ss = (x0-x1)**2 + (y0-y1)**2
2864
2865
! This is too short for anything useful...
2866
! Particularly difficult it is to decide on left/right if the segment is a stub.
2867
IF(ss < EPSILON(ss) ) THEN
2868
Skip(i) = .TRUE.
2869
CYCLE
2870
END IF
2871
2872
m = m+1
2873
2874
! assumption here is there are no divides or intersections at nodes
2875
! should be fine sine this is only used for arbitray intersections
2876
! shared node intersections dealt with later
2877
IF(Phase == 0) THEN
2878
NodeElemCount(i0) = NodeElemCount(i0) + 1
2879
NodeElemCount(i1) = NodeElemCount(i1) + 1
2880
2881
IF(PEs > 1) THEN
2882
! can't use SIZE(Mesh % ParallelInfo % NeighbourList(i0) % Neighbours)-1
2883
! as this includes all neighbours to the bulk element from Mesh not the ones to CutFEM
2884
! so we have to check against shared globaldofs
2885
IF(IsoMesh % ParallelInfo % GInterface(i0) .OR. &
2886
IsoMesh % ParallelInfo % GInterface(i1)) THEN
2887
DO j=1, SharedCount
2888
IF(IsoMesh % ParallelInfo % GlobalDOFs(i0) == RecvElems(j,2)) THEN
2889
NodeElemCount(i0) = NodeElemCount(i0) + 1
2890
END IF
2891
IF(IsoMesh % ParallelInfo % GlobalDOFs(i1) == RecvElems(j,2)) THEN
2892
NodeElemCount(i1) = NodeElemCount(i1) + 1
2893
END IF
2894
END DO
2895
END IF
2896
END IF
2897
2898
IF(NodeToElem(i0,1) == 0) THEN
2899
NodeToElem(i0,1) = m
2900
ELSE
2901
NodeToElem(i0,2) = m
2902
END IF
2903
2904
IF(NodeToElem(i1,1) == 0) THEN
2905
NodeToElem(i1,1) = m
2906
ELSE
2907
NodeToElem(i1,2) = m
2908
END IF
2909
END IF
2910
2911
2912
IF(Phase==0) CYCLE
2913
2914
!assign neigbours
2915
IF(NodeToElem(i0,1) == i) THEN
2916
PolylineData(MyPe) % Prev(m,1) = NodeToElem(i0,2)
2917
ELSE
2918
PolylineData(MyPe) % Prev(m,1) = NodeToElem(i0,1)
2919
END IF
2920
2921
IF(NodeToElem(i1,1) == i) THEN
2922
PolylineData(MyPe) % Next(m,1) = NodeToElem(i1,2)
2923
ELSE
2924
PolylineData(MyPe) % Next(m,1) = NodeToElem(i1,1)
2925
END IF
2926
2927
IF(PEs > 1) THEN
2928
IF(IsoMesh % ParallelInfo % GInterface(i0) .OR. &
2929
IsoMesh % ParallelInfo % GInterface(i1)) THEN
2930
DO j=1, SharedCount
2931
IF(IsoMesh % ParallelInfo % GlobalDOFs(i0) == RecvElems(j,2)) THEN
2932
PolylineData(MyPe) % Prev(m,1) = RecvElems(j,1)
2933
PolylineData(MyPe) % Prev(m,2) = RecvFrom(j)
2934
END IF
2935
IF(IsoMesh % ParallelInfo % GlobalDOFs(i1) == RecvElems(j,2)) THEN
2936
PolylineData(MyPe) % Next(m,1) = RecvElems(j,1)
2937
PolylineData(MyPe) % Next(m,2) = RecvFrom(j)
2938
END IF
2939
END DO
2940
END IF
2941
END IF
2942
2943
! node on intersection so mark with negative
2944
IF(NodeElemCount(i0) > 2) PolylineData(MyPe) % Prev(m,1) = -1
2945
IF(NodeElemCount(i1) > 2) PolylineData(MyPe) % Next(m,1) = -1
2946
!IF(NodeElemCount(i0) > 2 .OR. NodeElemCount(i1) > 2) &
2947
! PRINT*, ParEnv % MyPE, 'nodeelemcount', NodeElemCount(i0), NodeElemCount(i1), &
2948
! 'indices', i0, i1
2949
2950
PL_local(m,1) = i0
2951
PL_local(m,2) = i1
2952
2953
TotLineLen = TotLineLen + SQRT(ss)
2954
2955
phi0 = PhiVar1D % Values(i0)
2956
phi1 = PhiVar1D % Values(i1)
2957
2958
! Coordinates for the polyline.
2959
PolylineData(MyPe) % Vals(m,1) = x0
2960
PolylineData(MyPe) % Vals(m,2) = x1
2961
PolylineData(MyPe) % Vals(m,3) = y0
2962
PolylineData(MyPe) % Vals(m,4) = y1
2963
2964
! Levelset values for the polyline.
2965
PolylineData(MyPe) % Vals(m,5) = phi0
2966
PolylineData(MyPe) % Vals(m,6) = phi1
2967
2968
2969
! min/max for bounds check
2970
xmax0 = MAX(x0,x1); xmin0 = MIN(x0,x1)
2971
ymax0 = MAX(y0,y1); ymin0 = MIN(y0,y1)
2972
2973
! check and mark intersections
2974
m2 = m
2975
DO j=i+1, IsoMesh % NumberOfBulkElements
2976
IF(Skip(j)) CYCLE
2977
m2 = m2 + 1
2978
2979
i2 = IsoMesh % Elements(j) % NodeIndexes(1)
2980
i3 = IsoMesh % Elements(j) % NodeIndexes(2)
2981
2982
IF(i2==i0 .OR. i2==i1 .OR. i3==i0 .OR. i3==i1) CYCLE ! share node
2983
2984
x2 = x(i2); y2 = y(i2)
2985
x3 = x(i3); y3 = y(i3)
2986
2987
xmax1 = MAX(x2,x3); xmin1 = MIN(x2,x3)
2988
ymax1 = MAX(y2,y3); ymin1 = MIN(y2,y3)
2989
2990
! bounds check can we intersect
2991
IF(xmax1 < xmin0 .OR. xmax0 < xmin1) CYCLE
2992
IF(ymax1 < ymin0 .OR. ymax0 < ymin1) CYCLE
2993
2994
! do we actually intersect?
2995
CALL LineSegmentsIntersect((/x2,y2/),(/x3,y3/),(/x0,y0/),(/x1,y1/),intersect_p,intersect)
2996
2997
IF(Intersect) THEN
2998
2999
Intersects(MyPE) % nIntersects = Intersects(MyPE) % nIntersects + 1
3000
NInter = Intersects(MyPE) % nIntersects
3001
3002
IF(NInter > Intersects(MyPE) % Size) &
3003
CALL DoubleIntersectsAllocation()
3004
3005
Intersects(MyPE) % Elements(NInter,1) = m
3006
Intersects(MyPE) % Elements(NInter,2) = m2
3007
Intersects(MyPE) % Elements(NInter,3) = MyPE
3008
Intersects(MyPE) % Elements(NInter,4) = MyPE
3009
3010
Intersects(MyPE) % Intersection(NInter,1) = intersect_p(1)
3011
Intersects(MyPE) % Intersection(NInter,2) = intersect_p(2)
3012
3013
Found = GetPointLevelset((/x0,y0,0.0_dp/), val)
3014
Intersects(MyPE) % Found(NInter,1) = Found
3015
IF(Found) Intersects(MyPE) % LS(NInter,1) = val
3016
3017
Found = GetPointLevelset((/x1,y1,0.0_dp/), val)
3018
Intersects(MyPE) % Found(NInter,2) = Found
3019
IF(Found) Intersects(MyPE) % LS(NInter,2) = val
3020
3021
Found = GetPointLevelset((/x2,y2,0.0_dp/), val)
3022
Intersects(MyPE) % Found(NInter,3) = Found
3023
IF(Found) Intersects(MyPE) % LS(NInter,3) = val
3024
3025
Found = GetPointLevelset((/x3,y3,0.0_dp/), val)
3026
Intersects(MyPE) % Found(NInter,4) = Found
3027
IF(Found) Intersects(MyPE) % LS(NInter,4) = val
3028
3029
EXIT ! assumption is that each line can only have one intersection
3030
END IF
3031
END DO
3032
3033
j = 7
3034
3035
DO k = 1,nVar
3036
IF(k==iAvoid) CYCLE
3037
3038
str = ListGetString( Solver % Values,'isoline variable '//I2S(k), Found )
3039
Var1D => VariableGet( IsoMesh % Variables, str, ThisOnly = .TRUE. )
3040
3041
dofs = Var1D % Dofs
3042
DO k2=1,dofs
3043
PolylineData(MyPe) % Vals(m,j) = Var1D % Values(dofs*(Var1D % Perm(i0)-1)+k2)
3044
PolylineData(MyPe) % Vals(m,j+1) = Var1D % Values(dofs*(Var1D % Perm(i1)-1)+k2)
3045
j = j+2
3046
END DO
3047
END DO
3048
END DO
3049
3050
3051
IF(Phase==0) THEN
3052
CALL Info('LevelsetUpdate','Allocating PolylineData of size '//I2S(m)//' x '//I2S(nCol),Level=8)
3053
PolylineData(MyPe) % nLines = m
3054
PolylineData(MyPe) % nNodes = Mesh % NumberOfNodes
3055
ALLOCATE(PolylineData(MyPe) % Vals(m,nCol), PolylineData(MyPe) % Next(m,2),&
3056
PolylineData(MyPE) % Prev(m,2), PL_local(m,2))
3057
PolylineData(MyPe) % Vals = 0.0_dp
3058
PolylineData(MyPe) % Next(:,1) = 0
3059
PolylineData(MyPe) % Prev(:,1) = 0
3060
PolylineData(MyPe) % Prev(:,2) = MyPE
3061
PolylineData(MyPe) % Next(:,2) = MyPE
3062
END IF
3063
3064
END DO
3065
3066
!need to get share local intersects
3067
IF(PEs > 1 ) THEN
3068
BLOCK
3069
INTEGER, ALLOCATABLE :: nPar(:)
3070
INTEGER :: comm, ierr, status(MPI_STATUS_SIZE)
3071
3072
ALLOCATE(nPar(PEs))
3073
comm = Solver % Matrix % Comm
3074
3075
nPar = 0
3076
nPar(MyPe) = PolylineData(MyPe) % nLines
3077
CALL MPI_ALLREDUCE(MPI_IN_PLACE, nPar, PEs, MPI_INTEGER, MPI_MAX, comm, ierr)
3078
DO i=1,PEs
3079
PolylineData(i) % nLines = nPar(i)
3080
END DO
3081
3082
nPar = 0
3083
nPar(MyPe) = Mesh % NumberOfNodes
3084
CALL MPI_ALLREDUCE(MPI_IN_PLACE, nPar, PEs, MPI_INTEGER, MPI_MAX, comm, ierr)
3085
DO i=1,PEs
3086
PolylineData(i) % nNodes = nPar(i)
3087
END DO
3088
CALL MPI_BARRIER( comm, ierr )
3089
3090
IF( PolylineData(MyPe) % nNodes > 1) THEN
3091
DO i=1,PEs
3092
IF(i==MyPe) CYCLE
3093
m = PolylineData(i) % nLines
3094
IF(m>0) ALLOCATE(PolylineData(i) % Vals(m,nCol), &
3095
PolylineData(i) % Next(m,2), PolylineData(i) % Prev(m,2))
3096
END DO
3097
END IF
3098
3099
DO i=1,PEs
3100
IF(i==MyPe) CYCLE
3101
IF(PolylineData(MyPe) % nLines == 0 .OR. PolylineData(i) % nNodes == 0 ) CYCLE
3102
3103
! Sent data from partition MyPe to i
3104
k = PolylineData(MyPe) % nLines * nCol
3105
CALL MPI_BSEND( PolylineData(MyPe) % Vals, k, MPI_DOUBLE_PRECISION,i-1, &
3106
1001, comm, ierr )
3107
k = PolylineData(MyPe) % nLines * 2
3108
CALL MPI_BSEND( PolylineData(MyPe) % Next, k, MPI_INTEGER,i-1, &
3109
1002, comm, ierr )
3110
CALL MPI_BSEND( PolylineData(MyPe) % Prev, k, MPI_INTEGER,i-1, &
3111
1003, comm, ierr )
3112
END DO
3113
3114
DO i=1,PEs
3115
IF(i==MyPe) CYCLE
3116
IF(PolylineData(i) % nLines == 0 .OR. PolylineData(MyPe) % nNodes == 0 ) CYCLE
3117
3118
! Receive data from partition i to MyPe
3119
k = PolylineData(i) % nLines * nCol
3120
CALL MPI_RECV( PolylineData(i) % Vals, k, MPI_DOUBLE_PRECISION,i-1, &
3121
1001, comm, status, ierr )
3122
k = PolylineData(i) % nLines * 2
3123
CALL MPI_RECV( PolylineData(i) % Next, k, MPI_INTEGER,i-1, &
3124
1002, comm, status, ierr )
3125
CALL MPI_RECV( PolylineData(i) % Prev, k, MPI_INTEGER,i-1, &
3126
1003, comm, status, ierr )
3127
END DO
3128
3129
CALL MPI_BARRIER( comm, ierr )
3130
3131
!comm local intersects
3132
nPar = 0
3133
nPar(MyPE) = NInter
3134
CALL MPI_ALLREDUCE(MPI_IN_PLACE, nPar, PEs, MPI_INTEGER, MPI_MAX, comm, ierr)
3135
3136
k = SUM( PolylineData(1:PEs) % nLines )
3137
CALL Info('LevelSetUpdate','Number of line segments in parallel system: '//I2S(k),Level=7)
3138
3139
CALL MPI_ALLREDUCE(MPI_IN_PLACE, TotLineLen, PEs, MPI_DOUBLE_PRECISION, MPI_SUM, comm, ierr)
3140
END BLOCK
3141
END IF
3142
3143
! find local-parallel intersects
3144
IF(PEs > 1) THEN
3145
! could instead search through isomesh and get neighbours?
3146
NNeighbours = COUNT(ParEnv % IsNeighbour)
3147
Neighbours = PACK( (/ (i,i=1,PEs) /), ParEnv % IsNeighbour)
3148
3149
! only local to parallel intersections
3150
! then store if smaller part store intersect info
3151
! store intersect direction of local line
3152
nLines = PolylineData(MyPE) % nLines
3153
3154
NPInter = 0
3155
3156
nLines = PolylineData(MyPE) % nLines
3157
3158
DO i=1,nLines
3159
x0 = PolylineData(MyPE) % Vals(i,1)
3160
x1 = PolylineData(MyPE) % Vals(i,2)
3161
y0 = PolylineData(MyPE) % Vals(i,3)
3162
y1 = PolylineData(MyPE) % Vals(i,4)
3163
3164
xmax0 = MAX(x0,x1); xmin0 = MIN(x0,x1)
3165
ymax0 = MAX(y0,y1); ymin0 = MIN(y0,y1)
3166
3167
Intersect = .FALSE.
3168
DO neighbour = 1, NNeighbours
3169
3170
IF(MyPE > Neighbours(neighbour)) CYCLE ! only confirm on lowest part
3171
3172
k = neighbours(neighbour)
3173
3174
! part not in BB
3175
IF(PolylineData(MyPE) % IsoLineBB(MyPE) < xmin0 .OR. &
3176
xmax0 < PolylineData(k) % IsoLineBB(1)) CYCLE
3177
IF(PolylineData(MyPe) % IsoLineBB(MyPE) < ymin0 .OR. &
3178
ymax0 < PolylineData(k) % IsoLineBB(3)) CYCLE
3179
3180
DO j=1, PolylineData(k) % NLines
3181
3182
x2 = PolylineData(k) % Vals(j,1)
3183
x3 = PolylineData(k) % Vals(j,2)
3184
y2 = PolylineData(k) % Vals(j,3)
3185
y3 = PolylineData(k) % Vals(j,4)
3186
3187
!need to discount if either node is the same
3188
IF(ABS(x0-x2) > AEPS .OR. ABS(x0-x3) < AEPS) CYCLE
3189
IF(ABS(x1-x2) > AEPS .OR. ABS(x1-x3) < AEPS) CYCLE
3190
IF(ABS(y0-y2) > AEPS .OR. ABS(y0-y3) < AEPS) CYCLE
3191
IF(ABS(y1-y2) > AEPS .OR. ABS(y1-y3) < AEPS) CYCLE
3192
3193
xmax1 = MAX(x2,x3); xmin1 = MIN(x2,x3)
3194
ymax1 = MAX(y2,y3); ymin1 = MIN(y2,y3)
3195
3196
! bounds check can we intersect
3197
IF(xmax1 < xmin0 .OR. xmax0 < xmin1) CYCLE
3198
IF(ymax1 < ymin0 .OR. ymax0 < ymin1) CYCLE
3199
3200
! do we actually intersect?
3201
CALL LineSegmentsIntersect((/x2,y2/),(/x3,y3/),(/x0,y0/),(/x1,y1/),intersect_p,intersect)
3202
3203
IF(Intersect) THEN
3204
3205
Intersects(MyPE) % nIntersects = Intersects(MyPE) % nIntersects + 1
3206
NInter = Intersects(MyPE) % nIntersects
3207
3208
IF(NInter > Intersects(MyPE) % Size) &
3209
CALL DoubleIntersectsAllocation()
3210
3211
Intersects(MyPE) % Elements(NInter,1) = i
3212
Intersects(MyPE) % Elements(NInter,2) = j
3213
Intersects(MyPE) % Elements(NInter,3) = MyPE
3214
Intersects(MyPE) % Elements(NInter,4) = k
3215
3216
Intersects(MyPE) % Intersection(NInter,1) = intersect_p(1)
3217
Intersects(MyPE) % Intersection(NInter,2) = intersect_p(2)
3218
3219
Found = GetPointLevelset((/x0,y0,0.0_dp/), val)
3220
Intersects(MyPE) % Found(NInter,1) = Found
3221
IF(Found) Intersects(MyPE) % LS(NInter,1) = val
3222
3223
Found = GetPointLevelset((/x1,y1,0.0_dp/), val)
3224
Intersects(MyPE) % Found(NInter,2) = Found
3225
IF(Found) Intersects(MyPE) % LS(NInter,2) = val
3226
3227
Found = GetPointLevelset((/x2,y2,0.0_dp/), val)
3228
Intersects(MyPE) % Found(NInter,3) = Found
3229
IF(Found) Intersects(MyPE) % LS(NInter,3) = val
3230
3231
Found = GetPointLevelset((/x3,y3,0.0_dp/), val)
3232
Intersects(MyPE) % Found(NInter,4) = Found
3233
IF(Found) Intersects(MyPE) % LS(NInter,4) = val
3234
3235
EXIT
3236
END IF
3237
END DO
3238
3239
IF(Intersect) EXIT
3240
END DO
3241
END DO
3242
END IF ! PES > 1
3243
3244
CALL ReduceLocalIntersectMemory()
3245
3246
! this block breaks complier
3247
IF(PEs > 1) THEN
3248
!parallel comm
3249
! share intersects_t
3250
BLOCK
3251
INTEGER, ALLOCATABLE :: nPar(:),UpdateIndices(:,:),AllUpdateIndices(:)
3252
REAL(KIND=dp), ALLOCATABLE :: UpdateVals(:),AllUpdateVals(:)
3253
INTEGER :: comm, ierr, status(MPI_STATUS_SIZE), Updates
3254
3255
ALLOCATE(nPar(PEs))
3256
comm = Solver % Matrix % Comm
3257
3258
nPar = 0
3259
nPar(MyPe) = Intersects(MyPe) % nIntersects
3260
CALL MPI_ALLREDUCE(MPI_IN_PLACE, nPar, PEs, MPI_INTEGER, MPI_MAX, comm, ierr)
3261
DO i=1,PEs
3262
Intersects(i) % nIntersects = nPar(i)
3263
END DO
3264
3265
IF( PolylineData(MyPe) % nNodes > 1) THEN
3266
DO i=1,PEs
3267
IF(i==MyPe) CYCLE
3268
m = Intersects(i) % nIntersects
3269
IF(m>0) ALLOCATE(Intersects(i) % Intersection(m,2), &
3270
Intersects(i) % Elements(m,4), Intersects(i) % Found(m,4), &
3271
Intersects(i) % LS(m,4))
3272
END DO
3273
END IF
3274
3275
DO i=1,PEs
3276
IF(i==MyPe) CYCLE
3277
IF(Intersects(MyPe) % nIntersects == 0 .OR. PolylineData(i) % nNodes == 0 ) CYCLE
3278
3279
! Sent data from partition MyPe to i
3280
k = Intersects(MyPe) % nIntersects * 2
3281
CALL MPI_BSEND( Intersects(MyPe) % Intersection, k, MPI_DOUBLE_PRECISION,i-1, &
3282
1004, comm, ierr )
3283
3284
k = Intersects(MyPe) % nIntersects * 4
3285
CALL MPI_BSEND( Intersects(MyPe) % Elements, k, MPI_INTEGER,i-1, &
3286
1005, comm, ierr )
3287
CALL MPI_BSEND( Intersects(MyPe) % Found, k, MPI_LOGICAL,i-1, &
3288
1006, comm, ierr )
3289
CALL MPI_BSEND( Intersects(MyPe) % LS, k, MPI_DOUBLE_PRECISION,i-1, &
3290
1007, comm, ierr )
3291
END DO
3292
3293
DO i=1,PEs
3294
IF(i==MyPe) CYCLE
3295
IF(Intersects(i) % nIntersects == 0 .OR. PolylineData(MyPe) % nNodes == 0 ) CYCLE
3296
3297
! Receive data from partition i to MyPe
3298
k = Intersects(i) % nIntersects * 2
3299
CALL MPI_RECV( Intersects(i) % Intersection, k, MPI_DOUBLE_PRECISION,i-1, &
3300
1004, comm, status, ierr )
3301
3302
k = Intersects(i) % nIntersects * 4
3303
CALL MPI_RECV( Intersects(i) % Elements, k, MPI_INTEGER,i-1, &
3304
1005, comm, status, ierr )
3305
CALL MPI_RECV( Intersects(i) % Found, k, MPI_LOGICAL,i-1, &
3306
1006, comm, status, ierr )
3307
CALL MPI_RECV( Intersects(i) % LS, k, MPI_DOUBLE_PRECISION,i-1, &
3308
1007, comm, status, ierr )
3309
END DO
3310
3311
CALL MPI_BARRIER( comm, ierr )
3312
3313
3314
!once parcomm over loop through unfound on neighbours
3315
nMax = SUM(Intersects(Neighbours) % nIntersects) ! should be low
3316
ALLOCATE(UpdateIndices(nMax,3),UpdateVals(nMax))
3317
Updates = 0
3318
DO neighbour = 1, NNeighbours
3319
k = neighbours(neighbour)
3320
3321
nInter = Intersects(k) % nIntersects
3322
3323
IF(nInter == 0) CYCLE
3324
3325
DO i=1, nInter
3326
3327
IF(ALL(Intersects(k) % Found(i,:))) CYCLE
3328
3329
i0 = Intersects(k) % Elements(i,1)
3330
p0 = Intersects(k) % Elements(i,3)
3331
i1 = Intersects(k) % Elements(i,2)
3332
p1 = Intersects(k) % Elements(i,4)
3333
3334
DO j=1,4
3335
IF(Intersects(k) % Found(i,j)) CYCLE
3336
3337
IF(j > 2) THEN
3338
i0=i1; p0=p1
3339
END IF
3340
3341
IF(j==1 .OR. j==3) THEN
3342
x0 = PolylineData(p0) % vals(i0,1)
3343
y0 = PolylineData(p0) % vals(i0,3)
3344
ELSE
3345
x0 = PolylineData(p0) % vals(i0,2)
3346
y0 = PolylineData(p0) % vals(i0,4)
3347
END IF
3348
3349
Found = GetPointLevelset((/x0,y0,0.0_dp/), val)
3350
3351
IF(Found) THEN
3352
Intersects(k) % Found(i,j) = Found
3353
Intersects(k) % LS(i,j) = val
3354
3355
Updates = Updates + 1
3356
UpdateIndices(Updates,1) = k
3357
UpdateIndices(Updates,2) = i
3358
UpdateIndices(Updates,3) = j
3359
UpdateVals(Updates) = val
3360
3361
! need to comm this?
3362
END IF
3363
END DO
3364
END DO
3365
END DO
3366
3367
nPar = 0
3368
nPar(MyPE) = Updates
3369
CALL MPI_ALLREDUCE(MPI_IN_PLACE, nPar, PEs, MPI_INTEGER, MPI_MAX, comm, ierr)
3370
3371
rdisps(1) = 0
3372
DO i=2,PEs
3373
rdisps(i) = rdisps(i-1) + nPar(i-1)
3374
END DO
3375
3376
Updates = SUM(nPar)
3377
ALLOCATE(AllUpdateVals(Updates),AllUpdateIndices(Updates*3))
3378
CALL MPI_ALLGATHERV(UpdateVals, nPar(MyPE), MPI_DOUBLE_PRECISION, &
3379
AllUpdateVals, nPar, rdisps, MPI_DOUBLE_PRECISION, comm, ierr)
3380
3381
CALL MPI_ALLGATHERV(UpdateIndices, nPar(MyPE)*3, MPI_INTEGER, &
3382
AllUpdateIndices, nPar*3, rdisps*3, MPI_INTEGER, comm, ierr)
3383
3384
CALL MPI_BARRIER(comm, ierr)
3385
3386
! update values
3387
DO n=1,Updates
3388
k = AllUpdateIndices((n-1)*3 + 1)
3389
i = AllUpdateIndices((n-1)*3 + 2)
3390
j = AllUpdateIndices((n-1)*3 + 3)
3391
Intersects(k) % LS(i,j) = AllUpdateVals(n)
3392
END DO
3393
3394
END BLOCK
3395
END IF ! parallel
3396
3397
nMax = 0
3398
DO i=1,PEs
3399
nLines = PolylineData(i) % nLines
3400
ALLOCATE(PolylineData(i) % Intersect(nLines))
3401
PolylineData(i) % Intersect = .FALSE.
3402
IF(nMax < PolylineData(i) % nLines) nMax = PolylineData(i) % nLines
3403
END DO
3404
3405
DO i=1,PEs
3406
nInter = Intersects(i) % nIntersects
3407
IF(nInter == 0) CYCLE
3408
DO j=1,Ninter
3409
i0 = Intersects(i) % Elements(j,1)
3410
p0 = Intersects(i) % Elements(j,3)
3411
i1 = Intersects(i) % Elements(j,2)
3412
p1 = Intersects(i) % Elements(j,4)
3413
3414
PolylineData(p0) % Intersect(i0) = .TRUE.
3415
PolylineData(p1) % Intersect(i1) = .TRUE.
3416
END DO
3417
END DO
3418
3419
ALLOCATE(RemoveLines(PEs, nMax))
3420
RemoveLines = .FALSE.
3421
! find intersections
3422
DO k = 1, ParEnv % PEs
3423
nInter = Intersects(k) % nIntersects
3424
IF(nInter == 0) CYCLE
3425
3426
DO i = 1, nInter
3427
3428
DO phase=1,2
3429
3430
IF(Phase == 1) THEN
3431
i0 = Intersects(k) % Elements(i,1)
3432
p0 = Intersects(k) % Elements(i,3)
3433
Positive = Intersects(k) % LS(i,1) > Intersects(k) % LS(i,2)
3434
ELSE
3435
i0 = Intersects(k) % Elements(i,2)
3436
p0 = Intersects(k) % Elements(i,4)
3437
Positive = Intersects(k) % LS(i,3) > Intersects(k) % LS(i,4)
3438
END IF
3439
3440
IF(Positive) THEN
3441
next = PolylineData(p0) % Next(i0,1)
3442
nextp = PolylineData(p0) % Next(i0,2)
3443
counter = 0
3444
DO WHILE(.TRUE.)
3445
! if part of an intersect? hashmap?
3446
IF(PolylineData(NextP) % Next(Next,1) > 0 .AND. .NOT. PolylineData(NextP) % Intersect(Next)) THEN
3447
RemoveLines(NextP, Next) = .TRUE.
3448
Nexts = PolylineData(NextP) % Next(Next,:)
3449
!NextP = PolylineData(NextP) % Next(Next,2)
3450
Next = Nexts(1)
3451
NextP = Nexts(2)
3452
ELSE
3453
EXIT
3454
END IF
3455
counter =counter+1
3456
IF(counter>10) CALL FATAL('CutFEM', 'stuck in loop')
3457
END DO
3458
3459
! adjust node
3460
PolylineData(p0) % Vals(i0,2) = Intersects(k) % Intersection(i,1)
3461
PolylineData(p0) % Vals(i0,4) = Intersects(k) % Intersection(i,2)
3462
3463
! adjust mesh for if we want to save
3464
! this shared node could present on multiple parts?
3465
IF(p0==MyPe) THEN
3466
IsoMesh % Nodes % x(PL_local(i0,2)) = Intersects(k) % Intersection(i,1)
3467
IsoMesh % Nodes % y(PL_local(i0,2)) = Intersects(k) % Intersection(i,2)
3468
END IF
3469
3470
ELSE ! negative
3471
3472
next = PolylineData(p0) % Prev(i0,1)
3473
nextp = PolylineData(p0) % Prev(i0,2)
3474
!RemoveLines(NextP,Next) = .TRUE.
3475
DO WHILE(.TRUE.)
3476
IF(PolylineData(NextP) % Prev(Next,1) > 0 .AND. .NOT. PolylineData(NextP) % Intersect(Next)) THEN
3477
RemoveLines(NextP,Next) = .TRUE.
3478
Nexts = PolylineData(NextP) % Prev(Next,:)
3479
! this is changing as Next changes
3480
!NextP = PolylineData(NextP) % Prev(Next,2)
3481
Next = Nexts(1)
3482
NextP = Nexts(2)
3483
ELSE
3484
EXIT
3485
END IF
3486
END DO
3487
3488
! adjust node
3489
PolylineData(p0) % Vals(i0,1) = Intersects(k) % Intersection(i,1)
3490
PolylineData(p0) % Vals(i0,3) = Intersects(k) % Intersection(i,2)
3491
3492
! adjust mesh for if we want to save
3493
! what about shared nodes?
3494
IF(p0==MyPe) THEN
3495
IsoMesh % Nodes % x(PL_local(i0,1)) = Intersects(k) % Intersection(i,1)
3496
IsoMesh % Nodes % y(PL_local(i0,1)) = Intersects(k) % Intersection(i,2)
3497
END IF
3498
3499
END IF
3500
END DO
3501
END DO
3502
END DO
3503
3504
CALL DeallocateMeshLines(IsoMesh, RemoveLines)
3505
CALL DeallocatePolyLines(RemoveLines, nCol)
3506
3507
WRITE(Message,'(A,ES12.3)') 'Cutfem isoline length:',TotLineLen
3508
CALL Info('LevelSetUpdate',Message,Level=6)
3509
3510
CALL ListAddConstReal(CurrentModel % Simulation,'res: cutfem isoline length',TotLineLen )
3511
3512
IF(ALLOCATED(Intersects)) THEN
3513
DO i=1,PEs
3514
IF(ALLOCATED(Intersects(i) % Intersection)) &
3515
DEALLOCATE(Intersects(i) % Intersection)
3516
IF(ALLOCATED(Intersects(i) % Elements)) &
3517
DEALLOCATE(Intersects(i) % Elements)
3518
IF(ALLOCATED(Intersects(i) % Found)) &
3519
DEALLOCATE(Intersects(i) % Found)
3520
IF(ALLOCATED(Intersects(i) % LS)) &
3521
DEALLOCATE(Intersects(i) % LS)
3522
END DO
3523
END IF
3524
3525
DO i=1, PEs
3526
IF(ALLOCATED(PolylineData(i) % Intersect)) &
3527
DEALLOCATE(PolylineData(i) % Intersect)
3528
END DO
3529
3530
IF(InfoActive(25)) THEN
3531
CALL Info('LevelSetUpdate','Polyline interval for Isoline variables')
3532
3533
j = SIZE(PolylineData(MyPe) % Vals(:,1))
3534
CALL VectorValuesRange(PolylineData(MyPe) % Vals(:,1),j,'x')
3535
CALL VectorValuesRange(PolylineData(MyPe) % Vals(:,3),j,'y')
3536
CALL VectorValuesRange(PolylineData(MyPe) % Vals(:,5),j,'phi')
3537
i = 7
3538
DO k = 1,nVar
3539
str = ListGetString( Solver % Values,'isoline variable '//I2S(k), Found )
3540
CALL VectorValuesRange(PolylineData(MyPe) % Vals(:,i),j,TRIM(str))
3541
i = i+2
3542
END DO
3543
END IF
3544
3545
END SUBROUTINE PopulatePolyline
3546
!------------------------------------------------------------------------------
3547
3548
!initialise local intersects
3549
SUBROUTINE InitialiseIntersects(Size)
3550
INTEGER :: Size
3551
!-----------------------------
3552
INTEGER :: MyPE
3553
3554
MyPE = ParEnv % MyPE + 1
3555
3556
ALLOCATE(Intersects(MyPE) % Intersection(Size,2), &
3557
Intersects(MyPE) % Elements(Size,4), &
3558
Intersects(MyPE) % Found(Size,4), &
3559
Intersects(MyPE) % LS(Size,4))
3560
3561
Intersects(MyPE) % nIntersects = 0
3562
Intersects(MyPE) % Size = Size
3563
Intersects(MyPE) % Found = .FALSE.
3564
END SUBROUTINE InitialiseIntersects
3565
3566
! double local intersect memmory on the fly
3567
SUBROUTINE DoubleIntersectsAllocation()
3568
!-----------------------------
3569
REAL(KIND=dp), ALLOCATABLE :: WorkReal(:,:)
3570
LOGICAL, ALLOCATABLE :: WorkLog(:,:)
3571
INTEGER, ALLOCATABLE :: WorkInt(:,:)
3572
INTEGER :: Size,Size2,MyPE
3573
3574
MyPE = ParEnv % MyPE + 1
3575
Size = Intersects(MyPE) % Size
3576
Size2 = Size*2
3577
3578
ALLOCATE(WorkReal(Size,4))
3579
WorkReal = Intersects(MyPE) % LS
3580
DEALLOCATE(Intersects(MyPE) % LS)
3581
ALLOCATE(Intersects(myPE) % LS(Size2,4))
3582
Intersects(MyPE) % LS(:Size,:) = WorkReal
3583
3584
WorkReal(:,1:2) = Intersects(MyPE) % Intersection
3585
DEALLOCATE(Intersects(MyPE) % Intersection)
3586
ALLOCATE(Intersects(MyPE) % Intersection(Size2,2))
3587
Intersects(MyPE) % Intersection(:Size,:) = WorkReal(:,1:2)
3588
DEALLOCATE(WorkReal)
3589
3590
ALLOCATE(WorkLog(Size,4))
3591
WorkLog = Intersects(MyPE) % Found
3592
DEALLOCATE(Intersects(MyPE) % Found)
3593
ALLOCATE(Intersects(myPE) % Found(Size2,4))
3594
Intersects(MyPE) % Found(1:Size,:) = WorkLog
3595
DEALLOCATE(WorkLog)
3596
3597
ALLOCATE(WorkInt(Size,4))
3598
WorkInt = Intersects(MyPE) % Elements
3599
DEALLOCATE(Intersects(MyPE) % Elements)
3600
ALLOCATE(Intersects(MyPE) % Elements(Size2,4))
3601
Intersects(MyPE) % Elements(1:Size,:) = WorkInt
3602
DEALLOCATE(WorkInt)
3603
3604
Intersects(MyPE) % Size = Size2
3605
3606
END SUBROUTINE DoubleIntersectsAllocation
3607
3608
! double local intersect memmory on the fly
3609
SUBROUTINE ReduceLocalIntersectMemory()
3610
!-----------------------------
3611
REAL(KIND=dp), ALLOCATABLE :: WorkReal(:,:)
3612
LOGICAL, ALLOCATABLE :: WorkLog(:,:)
3613
INTEGER, ALLOCATABLE :: WorkInt(:,:)
3614
INTEGER :: Size,Size2,MyPE
3615
3616
MyPE = ParEnv % MyPE + 1
3617
Size = Intersects(MyPE) % Size
3618
Size2 = Intersects(MyPE) % nIntersects
3619
3620
ALLOCATE(WorkReal(Size2,4))
3621
WorkReal = Intersects(MyPE) % LS(1:Size2,:)
3622
DEALLOCATE(Intersects(MyPE) % LS)
3623
ALLOCATE(Intersects(myPE) % LS(Size2,4))
3624
Intersects(MyPE) % LS = WorkReal
3625
3626
WorkReal(:,1:2) = Intersects(MyPE) % Intersection(1:Size2,:)
3627
DEALLOCATE(Intersects(MyPE) % Intersection)
3628
ALLOCATE(Intersects(MyPE) % Intersection(Size2,2))
3629
Intersects(MyPE) % Intersection = WorkReal(:,1:2)
3630
DEALLOCATE(WorkReal)
3631
3632
ALLOCATE(WorkLog(Size2,4))
3633
WorkLog = Intersects(MyPE) % Found(1:Size2,:)
3634
DEALLOCATE(Intersects(MyPE) % Found)
3635
ALLOCATE(Intersects(myPE) % Found(Size2,4))
3636
Intersects(MyPE) % Found = WorkLog
3637
DEALLOCATE(WorkLog)
3638
3639
ALLOCATE(WorkInt(Size,4))
3640
WorkInt = Intersects(MyPE) % Elements(1:Size2,:)
3641
DEALLOCATE(Intersects(MyPE) % Elements)
3642
ALLOCATE(Intersects(MyPE) % Elements(Size2,4))
3643
Intersects(MyPE) % Elements = WorkInt
3644
DEALLOCATE(WorkInt)
3645
3646
Intersects(MyPE) % Size = Size2
3647
3648
END SUBROUTINE ReduceLocalIntersectMemory
3649
3650
SUBROUTINE DeallocateMeshLines(Mesh, RemoveLines)
3651
TYPE(Mesh_t), POINTER :: Mesh
3652
LOGICAL :: RemoveLines(:,:)
3653
!------------------------------
3654
TYPE(Element_t), POINTER :: Element,WorkElements(:)
3655
INTEGER :: i,MyPE,n
3656
3657
MyPE = ParEnv % MyPE + 1
3658
n = Mesh % NumberOfBulkElements
3659
3660
IF( ALL(.NOT. RemoveLines(MyPe, :n)) ) RETURN ! no change
3661
3662
ALLOCATE(WorkElements(COUNT(.NOT. RemoveLines(MyPe, :n))))
3663
WorkElements = PACK(Mesh % Elements, (.NOT. RemoveLines(MyPe,:n)))
3664
3665
DO i=1, Mesh % NumberOfBulkElements
3666
IF(RemoveLines(MyPE, i)) THEN
3667
Element => Mesh % Elements(i)
3668
IF ( ASSOCIATED( Element % NodeIndexes ) ) &
3669
DEALLOCATE( Element % NodeIndexes )
3670
Element % NodeIndexes => NULL()
3671
END IF
3672
END DO
3673
3674
IF(ASSOCIATED(Mesh % Elements)) DEALLOCATE(Mesh % Elements)
3675
Mesh % Elements => WorkElements
3676
Mesh % NumberOfBulkElements = SIZE(WorkElements)
3677
NULLIFY(WorkElements)
3678
3679
END SUBROUTINE DeallocateMeshLines
3680
3681
SUBROUTINE DeallocatePolyLines(RemoveLines, nCol)
3682
LOGICAL :: RemoveLines(:,:)
3683
INTEGER :: nCol
3684
!------------------------------
3685
INTEGER :: i,j,k,p,n,PEs,nMax,counter,nLines,NNeighbours,MyPE
3686
REAL(KIND=dp), ALLOCATABLE :: Vals(:,:)
3687
INTEGER, ALLOCATABLE :: WorkInt(:,:), WorkInt2(:,:),PToN(:),Neighbours(:),ns(:)
3688
LOGICAL, ALLOCATABLE :: WorkLogical(:)
3689
3690
PEs = ParEnv % PEs
3691
MyPE = ParEnv % MyPE + 1
3692
3693
IF(PEs > 1) THEN
3694
ParEnv % IsNeighbour(MyPE) = .TRUE.
3695
Neighbours = PACK( (/ (i,i=1,PEs) /), ParEnv % IsNeighbour)
3696
ParEnv % IsNeighbour(MyPE) = .FALSE.
3697
ELSE
3698
ALLOCATE(Neighbours(1))
3699
Neighbours = MyPE
3700
END IF
3701
3702
NNeighbours = SIZE(Neighbours)
3703
3704
3705
ALLOCATE(PToN(PEs), Ns(PEs))
3706
PToN = 0
3707
3708
nMax = 0
3709
DO i=1, PEs
3710
IF(nMax < PolylineData(i) % nLines) nMax = PolylineData(i) % nLines
3711
ns(i) = COUNT(.NOT. RemoveLines(i, :PolylineData(i) % nLines))
3712
DO j=1,NNeighbours
3713
IF(i == Neighbours(j)) THEN
3714
PToN(i) = j
3715
EXIT
3716
END IF
3717
END DO
3718
END DO
3719
3720
ALLOCATE(Vals(nMax,nCol), WorkInt(nMax,2), WorkLogical(nMax), &
3721
WorkInt2(NNeighbours,nMax))
3722
3723
WorkInt2 = 0
3724
DO i=1, NNeighbours
3725
k = Neighbours(i)
3726
counter = 0
3727
DO j=1, PolylineData(k) % nLines
3728
IF(RemoveLines(k,j)) CYCLE
3729
counter = counter + 1
3730
WorkInt2(k,j) = counter
3731
END DO
3732
END DO
3733
3734
DO i=1, PEs
3735
nLines = PolylineData(i) % nLines
3736
3737
n = Ns(i)
3738
3739
IF(ALL(.NOT. RemoveLines(i,:nLines))) THEN
3740
DO j=1, n
3741
IF (PolylineData(i) % Prev(j,1) > ns(PolylineData(i) % Prev(j,2)) ) &
3742
PolylineData(i) % Prev(j,1) = 0
3743
IF (PolylineData(i) % Next(j,1) > ns(PolylineData(i) % Next(j,2)) ) &
3744
PolylineData(i) % Next(j,1) = 0
3745
END DO
3746
CYCLE ! no change
3747
END IF
3748
3749
counter = 0
3750
IF(n > 0) THEN
3751
IF(PolylineData(i) % nLines > 0) THEN
3752
3753
DO j=1, nCol
3754
Vals(1:n,j) = PACK(PolylineData(i) % Vals(:,j), .NOT. RemoveLines(i,:nLines))
3755
END DO
3756
3757
DEALLOCATE(PolylineData(i) % Vals)
3758
ALLOCATE(PolylineData(i) % Vals(n,nCol))
3759
PolylineData(i) % Vals = Vals(1:n,:)
3760
3761
WorkInt(1:n,1) = PACK(PolylineData(i) % Prev(:,1), .NOT. RemoveLines(i,:nLines))
3762
WorkInt(1:n,2) = PACK(PolylineData(i) % Prev(:,2), .NOT. RemoveLines(i,:nLines))
3763
DEALLOCATE(PolylineData(i) % Prev)
3764
ALLOCATE(PolylineData(i) % Prev(n,2))
3765
PolylineData(i) % Prev(:,1) = WorkInt(1:n,1)
3766
PolylineData(i) % Prev(:,2) = WorkInt(1:n,2)
3767
3768
WorkInt(1:n,1) = PACK(PolylineData(i) % Next(:,1), .NOT. RemoveLines(i,:nLines))
3769
WorkInt(1:n,2) = PACK(PolylineData(i) % Next(:,2), .NOT. RemoveLines(i,:nLines))
3770
DEALLOCATE(PolylineData(i) % Next)
3771
ALLOCATE(PolylineData(i) % Next(n,2))
3772
PolylineData(i) % Next(:,1) = WorkInt(1:n,1)
3773
PolylineData(i) % Next(:,2) = WorkInt(1:n,2)
3774
3775
! update next and prev
3776
DO j=1, n
3777
IF(PolylineData(i) % Prev(j,1) == -1) CYCLE ! shared node
3778
3779
IF(PolylineData(i) % Prev(j,1) /= 0) THEN
3780
k = PToN(PolylineData(i) % Prev(j,2))
3781
PolylineData(i) % Prev(j,1) = WorkInt2(k, PolylineData(i) % Prev(j,1))
3782
IF (PolylineData(i) % Prev(j,1) > ns(PolylineData(i) % Prev(j,2)) ) &
3783
PolylineData(i) % Prev(j,1) = 0
3784
!PolylineData(i) % Prev(j,2) = WorkInt2(PolylineData(i) % Prev(j,2))
3785
END IF
3786
IF(PolylineData(i) % Next(j,1) /= 0) THEN
3787
k = PToN(PolylineData(i) % Next(j,2))
3788
PolylineData(i) % Next(j,1) = WorkInt2(k, PolylineData(i) % Next(j,1))
3789
IF (PolylineData(i) % Next(j,1) > ns(PolylineData(i) % Next(j,2)) ) &
3790
PolylineData(i) % Next(j,1) = 0
3791
!PolylineData(i) % Next(j,2) = WorkInt2(PolylineData(i) % Next(j,2))
3792
END IF
3793
END DO
3794
3795
PolylineData(i) % nLines = n
3796
END IF
3797
END IF
3798
END DO
3799
3800
END SUBROUTINE DeallocatePolyLines
3801
3802
3803
3804
! use old ls field to determine which direction to remove
3805
! positive a1 is +ve compared to a2 on old ls field
3806
FUNCTION GetPointLevelset(Point, val) RESULT(Found)
3807
!----------------------------------------
3808
REAL(KIND=dp) :: Point(3),val
3809
LOGICAL :: Found
3810
!----------------------------------------
3811
REAL(KIND=dp) :: LocalCoords(3),ElementValues(3)
3812
TYPE(Element_t), POINTER :: HitElement
3813
LOGICAL :: FirstTime=.TRUE.
3814
3815
SAVE :: FirstTime
3816
3817
Found = PointInMesh(Solver, Point, LocalCoords, HitElement, ExtInitialize=FirstTime)!, &
3818
!CandElement, ExtInitialize )
3819
3820
IF(.NOT. Found) RETURN
3821
3822
ElementValues = PhiVar2D % Values(PhiVar2D % Perm(HitElement % NodeIndexes))
3823
3824
val = InterpolateInElement( HitElement, ElementValues, &
3825
LocalCoords(1), LocalCoords(2), LocalCoords(3) )
3826
3827
FirstTime = .FALSE.
3828
3829
END FUNCTION GetPointLevelset
3830
3831
!------------------------------------------------------------------------------
3832
!> Computes the signed distance to zero levelset.
3833
!------------------------------------------------------------------------------
3834
FUNCTION SignedDistance(node, trusted) RESULT(phip)
3835
!------------------------------------------------------------------------------
3836
INTEGER :: node
3837
REAL(KIND=dp) :: phip
3838
LOGICAL, INTENT(out) :: trusted
3839
!-----------------------------------------------------------------------------
3840
REAL(KIND=dp) :: xp,yp,x0,y0,x1,y1,xm,ym,a,b,c,d,s,dir1,&
3841
dist2,mindist2,dist,mindist,smin,ss,phim,cosphi,&
3842
x2,x3,y2,y3,denom,ua,ub,xi,yi,dir_int,sgn_sum,seg_vx,seg_vy,&
3843
dx,dy,dir_final,dir0
3844
INTEGER :: i,i0,i1,j,k,n,sgn,m,imin,kmin,dofs,k2,count,next,nextp
3845
TYPE(Variable_t), POINTER :: Var1D, Var2D
3846
INTEGER :: nCol, nLines
3847
REAL(KIND=dp), POINTER :: pValues(:)
3848
!------------------------------------------------------------------------------
3849
mindist2 = HUGE(mindist2)
3850
mindist = HUGE(mindist)
3851
sgn = 1
3852
3853
xp = Mesh % Nodes % x(node)
3854
yp = Mesh % Nodes % y(node)
3855
3856
m = 0
3857
nCol = 7
3858
3859
3860
DO k = 1, ParEnv % PEs
3861
nLines = PolylineData(k) % nLines
3862
IF(nLines == 0) CYCLE
3863
3864
DO i=1,nLines
3865
x0 = PolylineData(k) % Vals(i,1)
3866
x1 = PolylineData(k) % Vals(i,2)
3867
y0 = PolylineData(k) % Vals(i,3)
3868
y1 = PolylineData(k) % Vals(i,4)
3869
3870
a = xp - x0
3871
b = x0 - x1
3872
d = y0 - y1
3873
c = yp - y0
3874
3875
! Find the closest distance with the line segment.
3876
s = -(a*b + c*d) / (b**2 + d**2)
3877
! Intersection can not be beyond the element segment.
3878
s = MIN( MAX( s, 0.0d0), 1.0d0 )
3879
xm = (1-s) * x0 + s * x1
3880
ym = (1-s) * y0 + s * y1
3881
dist2 = (xp - xm)**2 + (yp - ym)**2
3882
3883
IF(nonzero) THEN
3884
a = xp - xm
3885
c = yp - ym
3886
! The signed distance should in a nonzero levelset be computed rather
3887
! perpendicular from the line segment.
3888
cosphi = ABS(a*b + c*d)/SQRT((a**2+c**2)*(b**2+d**2))
3889
IF(cosphi > cosphi0 ) CYCLE
3890
3891
! We need true distances since the offset cannot be added otherwise.
3892
dist2 = SQRT(dist2)
3893
3894
! The line segment including the zero levelset might not be exactly zero...
3895
! By definition we don't have permutation here!
3896
phim = (1-s) * PolylineData(k) % Vals(i,5) + s * PolylineData(k) % Vals(i,6)
3897
3898
! In order to test when need to be close enough.
3899
IF(dist2 > ABS(mindist) + ABS(phim) ) CYCLE
3900
3901
! Dir is an indicator one which side of the line segment the point lies.
3902
! We have ordered the edges so that "dir1" should be consistent.
3903
dir1 = (x1 - x0) * (yp - y0) - (y1 - y0) * (xp - x0)
3904
3905
! If the control point and found point lie on the same side they are inside.
3906
IF(dir1 < 0.0_dp ) THEN
3907
sgn = -1
3908
ELSE
3909
sgn = 1
3910
END IF
3911
3912
dist = sgn * dist2 + phim
3913
! Ok, close but no honey.
3914
IF( ABS(dist) > ABS(mindist) ) CYCLE
3915
3916
mindist = dist
3917
ELSE
3918
! Here we can compare the squares saving one expensive operation.
3919
IF(dist2 > mindist2 ) CYCLE
3920
3921
! Dir is an indicator one which side of the line segment the point lies.
3922
! We have ordered the edges soe that "dir1" should be consistent.
3923
dir1 = (x1 - x0) * (yp - y0) - (y1 - y0) * (xp - x0)
3924
3925
! If the control point and found point lie on the same side they are inside.
3926
IF(dir1 < 0.0_dp ) THEN
3927
sgn = -1
3928
ELSE
3929
sgn = 1
3930
END IF
3931
END IF
3932
3933
! Save these values for interpolation.
3934
m = m+1
3935
mindist2 = dist2
3936
smin = s
3937
imin = i
3938
kmin = k
3939
dir_final = dir1
3940
END DO
3941
END DO
3942
3943
IF(nonzero) THEN
3944
phip = mindist
3945
ELSE
3946
phip = sgn * SQRT(mindist2)
3947
END IF
3948
3949
trusted = .TRUE.
3950
#if 1
3951
! need to have think about how this works for parallel
3952
IF(PolylineData(kmin) % Prev(imin,1) > 0) THEN
3953
next = PolylineData(kmin) % Prev(imin,1)
3954
nextp = PolylineData(kmin) % Prev(imin,2)
3955
3956
x0 = PolylineData(nextp) % Vals(next,1)
3957
x1 = PolylineData(nextp) % Vals(next,2)
3958
y0 = PolylineData(nextp) % Vals(next,3)
3959
y1 = PolylineData(nextp) % Vals(next,4)
3960
dir0 = (x1 - x0) * (yp - y0) - (y1 - y0) * (xp - x0)
3961
3962
! are the signs different?
3963
IF (SIGN(1.0_dp, dir0) /= SIGN(1.0_dp, dir_final)) trusted = .FALSE.
3964
ELSE
3965
trusted = .FALSE.
3966
END IF
3967
3968
IF(trusted) THEN
3969
IF(PolylineData(kmin) % Next(imin,1) > 0) THEN
3970
next = PolylineData(kmin) % Next(imin,1)
3971
nextp = PolylineData(kmin) % Next(imin,2)
3972
3973
x0 = PolylineData(nextp) % Vals(next,1)
3974
x1 = PolylineData(nextp) % Vals(next,2)
3975
y0 = PolylineData(nextp) % Vals(next,3)
3976
y1 = PolylineData(nextp) % Vals(next,4)
3977
dir0 = (x1 - x0) * (yp - y0) - (y1 - y0) * (xp - x0)
3978
3979
! are the signs different?
3980
IF (SIGN(1.0_dp, dir0) /= SIGN(1.0_dp, dir_final)) trusted = .FALSE.
3981
ELSE
3982
trusted = .FALSE.
3983
END IF
3984
END IF
3985
#endif
3986
3987
! We can carry the fields with the zero levelset. This is like pure advection.
3988
! We should make this less laborious my fetching the pointers first...
3989
! Also, do not reinterpolate the nodes that are already ok!
3990
IF( nVar > 0 .AND. CutPerm(node) == 0 .AND. kmin == Parenv % MyPE+1) THEN !.AND. PhiVar2D % Perm(node) == 0 ) THEN
3991
3992
i0 = IsoMesh % Elements(imin) % NodeIndexes(1)
3993
i1 = IsoMesh % Elements(imin) % NodeIndexes(2)
3994
3995
DO i = 1,nVar
3996
IF(i==iAvoid) CYCLE
3997
str = ListGetString( Solver % Values,'isoline variable '//I2S(i), Found )
3998
3999
IF(i==iSolver) THEN
4000
j = OrigMeshPerm(node)
4001
dofs = Solver % Variable % Dofs
4002
pValues => OrigMeshValues
4003
ELSE
4004
Var2D => VariableGet( Mesh % Variables, str, ThisOnly = .TRUE. )
4005
j = Var2D % Perm(node)
4006
dofs = Var2D % dofs
4007
pValues => Var2D % Values
4008
END IF
4009
4010
IF(j==0) THEN
4011
nCol = nCol+2*dofs
4012
PRINT *,'We should maybe not be here?:',TRIM(str)
4013
STOP
4014
END IF
4015
4016
! Interpolate from the closest distance.
4017
! This is done similarly as the interpolation of coordinates.
4018
DO k2 = 1,dofs
4019
pValues(dofs*(j-1)+k2) = &
4020
(1-smin) * PolylineData(kmin) % Vals(imin,nCol) + &
4021
smin * PolylineData(kmin) % Vals(imin,nCol+1)
4022
nCol = nCol+2
4023
END DO
4024
END DO
4025
END IF
4026
4027
!PRINT *,'phip:',phip, m
4028
4029
END FUNCTION SignedDistance
4030
!------------------------------------------------------------------------------
4031
4032
SUBROUTINE LineSegmentsIntersect ( a1, a2, b1, b2, intersect_point, does_intersect, buffer)
4033
! Find if two 2D line segments intersect
4034
! Line segment 'a' runs from point a1 => a2, same for b
4035
4036
IMPLICIT NONE
4037
4038
REAL(KIND=dp) :: a1(2), a2(2), b1(2), b2(2), intersect_point(2)
4039
LOGICAL :: does_intersect
4040
REAL(KIND=dp), OPTIONAL :: buffer
4041
!-----------------------
4042
REAL(KIND=dp) :: r(2), s(2), rxs, bma(2), t, u, err_buffer
4043
4044
does_intersect = .FALSE.
4045
intersect_point = 0.0_dp
4046
4047
r = a2 - a1
4048
s = b2 - b1
4049
4050
rxs = VecCross2D(r,s)
4051
4052
IF(rxs == 0.0_dp) RETURN
4053
4054
bma = b1 - a1
4055
4056
t = VecCross2D(bma,s) / rxs
4057
u = VecCross2D(bma,r) / rxs
4058
4059
IF(PRESENT(Buffer)) THEN
4060
err_buffer = Buffer/rxs
4061
ELSE
4062
err_buffer = AEPS
4063
END IF
4064
IF(t < 0.0_dp-err_buffer .OR. t > 1.0_dp+err_buffer .OR. &
4065
u < 0.0_dp-err_buffer .OR. u > 1.0_dp+err_buffer) RETURN
4066
4067
intersect_point = a1 + (t * r)
4068
does_intersect = .TRUE.
4069
4070
END SUBROUTINE LineSegmentsIntersect
4071
4072
FUNCTION VecCross2D(a, b) RESULT (c)
4073
REAL(KIND=dp) :: a(2), b(2), c
4074
4075
c = a(1)*b(2) - a(2)*b(1)
4076
4077
END FUNCTION VecCross2D
4078
4079
END SUBROUTINE LevelSetUpdate
4080
4081
END MODULE CutFemUtils
4082
4083
!> \} ElmerLib
4084
4085
4086