Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ElmerCSC
GitHub Repository: ElmerCSC/elmerfem
Path: blob/devel/fem/src/ClusteringMethods.F90
3203 views
1
!/*****************************************************************************/
2
! *
3
! * Elmer, A Finite Element Software for Multiphysical Problems
4
! *
5
! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland
6
! *
7
! * This library is free software; you can redistribute it and/or
8
! * modify it under the terms of the GNU Lesser General Public
9
! * License as published by the Free Software Foundation; either
10
! * version 2.1 of the License, or (at your option) any later version.
11
! *
12
! * This library is distributed in the hope that it will be useful,
13
! * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
! * Lesser General Public License for more details.
16
! *
17
! * You should have received a copy of the GNU Lesser General Public
18
! * License along with this library (in file ../LGPL-2.1); if not, write
19
! * to the Free Software Foundation, Inc., 51 Franklin Street,
20
! * Fifth Floor, Boston, MA 02110-1301 USA
21
! *
22
! *****************************************************************************/
23
24
!> \ingroup ElmerLib
25
!> \{
26
27
!------------------------------------------------------------------------------
28
!> Module containing various clustering methods that may be used in conjunction
29
!> with algebraic multigrid methods or mesh partitioning, for example.
30
! author Peter RÃ¥back
31
!------------------------------------------------------------------------------
32
33
34
MODULE ClusteringMethods
35
36
USE Lists
37
USE SParIterGlobals
38
USE ElementUtils, ONLY : TangentDirections
39
40
IMPLICIT NONE
41
42
CONTAINS
43
44
45
!---------------------------------------------------------------
46
!> This partitions the mesh into a given number of partitions in each
47
!> direction. It may be used in clustering multigrid or similar,
48
!> and also to internal partitioning within ElmerSolver.
49
!---------------------------------------------------------------
50
SUBROUTINE ClusterNodesByDirection(Params,Mesh,Clustering,MaskActive)
51
52
TYPE(ValueList_t), POINTER :: Params
53
TYPE(Mesh_t), POINTER :: Mesh
54
LOGICAL, OPTIONAL :: MaskActive(:)
55
INTEGER, POINTER :: Clustering(:)
56
!---------------------------------------------------------------
57
LOGICAL :: MaskExists,GotIt,Hit
58
REAL(KIND=dp), ALLOCATABLE :: Measure(:)
59
INTEGER :: i,j,k,k0,l,ind,n,dim,dir,divs,nsize,elemsinpart,clusters
60
INTEGER, POINTER :: Iarray(:),Order(:),NodePart(:),NoPart(:)
61
INTEGER :: Divisions(3),minpart,maxpart,clustersize
62
REAL(KIND=dp), POINTER :: PArray(:,:), Arrange(:)
63
REAL(KIND=dp) :: Normal(3), Tangent1(3), Tangent2(3), Coord(3), Weights(3), &
64
avepart,devpart
65
CHARACTER(*), PARAMETER :: Caller="ClusterNodesByDirection"
66
!---------------------------------------------------------------
67
68
CALL Info(Caller,'Starting clustering',Level=30)
69
70
MaskExists = PRESENT(MaskActive)
71
IF( MaskExists ) THEN
72
nsize = COUNT( MaskActive )
73
ELSE
74
nsize = Mesh % NumberOfNodes
75
END IF
76
77
IF( .NOT. ASSOCIATED( Params ) ) THEN
78
CALL Fatal(Caller,'No parameter list associated')
79
END IF
80
81
dim = Mesh % MeshDim
82
Parray => ListGetConstRealArray( Params,'Clustering Normal Vector',GotIt )
83
IF( GotIt ) THEN
84
Normal = Parray(1:3,1)
85
ELSE
86
Normal(1) = 1.0
87
Normal(2) = 1.0d-2
88
IF( dim == 3) Normal(3) = 1.0d-4
89
END IF
90
Normal = Normal / SQRT( SUM( Normal ** 2) )
91
92
CALL TangentDirections( Normal,Tangent1,Tangent2 )
93
94
95
IF( .FALSE. ) THEN
96
PRINT *,'Normal:',Normal
97
PRINT *,'Tangent1:',Tangent1
98
PRINT *,'Tangent2:',Tangent2
99
END IF
100
101
102
Iarray => ListGetIntegerArray( Params,'Partitioning Divisions',GotIt )
103
IF(.NOT. GotIt) Iarray => ListGetIntegerArray( Params,'MG Cluster Divisions',GotIt )
104
Divisions = 1
105
IF( GotIt ) THEN
106
n = MIN( SIZE(Iarray), dim )
107
Divisions(1:n) = Iarray(1:n)
108
ELSE
109
clustersize = ListGetInteger( Params,'Partitioning Size',GotIt)
110
IF(.NOT. GotIt) clustersize = ListGetInteger( Params,'MG Cluster Size',GotIt)
111
IF( GotIt .AND. ClusterSize > 0) THEN
112
IF( dim == 2 ) THEN
113
Divisions(1) = ( nsize / clustersize ) ** 0.5_dp
114
Divisions(2) = ( nsize / ( clustersize * Divisions(1) ) )
115
ELSE
116
Divisions(1:2) = ( nsize / clustersize ) ** (1.0_dp / 3 )
117
Divisions(3) = ( nsize / ( clustersize * Divisions(1) * Divisions(2) ) )
118
END IF
119
ELSE
120
CALL Fatal(Caller,'Clustering Divisions not given!')
121
END IF
122
END IF
123
124
Clusters = Divisions(1) * Divisions(2) * Divisions(3)
125
126
IF( .FALSE. ) THEN
127
PRINT *,'dim:',dim
128
PRINT *,'divisions:',divisions
129
PRINT *,'clusters:',clusters
130
PRINT *,'nsize:',nsize
131
END IF
132
133
ALLOCATE(Order(nsize),Arrange(nsize),NodePart(nsize),NoPart(Clusters))
134
135
136
! These are needed as an initial value for the loop over dimension
137
elemsinpart = nsize
138
nodepart = 1
139
140
141
! Go through each direction and cumulatively add to the clusters
142
!-----------------------------------------------------------
143
144
DO dir = 1,dim
145
divs = Divisions(dir)
146
IF( divs <= 1 ) CYCLE
147
148
! Use the three principal directions as the weight
149
!-------------------------------------------------
150
IF( dir == 1 ) THEN
151
Weights = Normal
152
ELSE IF( dir == 2 ) THEN
153
Weights = Tangent1
154
ELSE
155
Weights = Tangent2
156
END IF
157
158
! Initialize ordering for the current direction
159
!----------------------------------------------
160
DO i=1,nsize
161
Order(i) = i
162
END DO
163
164
165
! Now compute the weights for each node
166
!----------------------------------------
167
DO i=1,Mesh % NumberOfNodes
168
j = i
169
IF( MaskExists ) THEN
170
IF( .NOT. MaskActive(j) ) CYCLE
171
END IF
172
173
Coord(1) = Mesh % Nodes % x(i)
174
Coord(2) = Mesh % Nodes % y(i)
175
Coord(3) = Mesh % Nodes % z(i)
176
177
Arrange(j) = SUM( Weights * Coord )
178
END DO
179
180
! Order the nodes for given direction
181
!----------------------------------------------
182
CALL SortR(nsize,Order,Arrange)
183
184
! For each direction the number of elements in cluster becomes smaller
185
elemsinpart = elemsinpart / divs
186
187
! initialize the counter partition
188
nopart = 0
189
190
191
! Go through each node and locate it to a cluster taking into consideration
192
! the previous clustering (for 1st direction all one)
193
!------------------------------------------------------------------------
194
j = 1
195
DO i = 1,nsize
196
ind = Order(i)
197
198
! the initial partition offset depends on previous partitioning
199
k0 = (nodepart(ind)-1) * divs
200
201
! Find the correct new partitioning, this loop is just long enough
202
DO l=1,divs
203
Hit = .FALSE.
204
205
! test for increase of local partition
206
IF( j < divs ) THEN
207
IF( nopart(k0+j) >= elemsinpart ) THEN
208
j = j + 1
209
Hit = .TRUE.
210
END IF
211
END IF
212
213
! test for decrease of local partition
214
IF( j > 1 ) THEN
215
IF( nopart(k0+j-1) < elemsinpart ) THEN
216
j = j - 1
217
Hit = .TRUE.
218
END IF
219
END IF
220
221
! If either increase or decrease is needed, this must be ok
222
IF(.NOT. Hit) EXIT
223
END DO
224
225
k = k0 + j
226
nopart(k) = nopart(k) + 1
227
nodepart(ind) = k
228
END DO
229
230
END DO
231
232
233
minpart = HUGE(minpart)
234
maxpart = 0
235
avepart = 1.0_dp * nsize / clusters
236
devpart = 0.0_dp
237
DO i=1,clusters
238
minpart = MIN( minpart, nopart(i))
239
maxpart = MAX( maxpart, nopart(i))
240
devpart = devpart + ABS ( nopart(i) - avepart )
241
END DO
242
devpart = devpart / clusters
243
244
WRITE(Message,'(A,T25,I10)') 'Min nodes in cluster:',minpart
245
CALL Info(Caller,Message)
246
WRITE(Message,'(A,T25,I10)') 'Max nodes in cluster:',maxpart
247
CALL Info(Caller,Message)
248
WRITE(Message,'(A,T28,F10.2)') 'Average nodes in cluster:',avepart
249
CALL Info(Caller,Message)
250
WRITE(Message,'(A,T28,F10.2)') 'Deviation of nodes:',devpart
251
CALL Info(Caller,Message)
252
253
254
IF( ASSOCIATED(Clustering)) THEN
255
Clustering = Nodepart
256
DEALLOCATE(Nodepart)
257
ELSE
258
Clustering => Nodepart
259
NULLIFY( Nodepart )
260
END IF
261
262
DEALLOCATE(Order,Arrange,NoPart)
263
264
265
END SUBROUTINE ClusterNodesByDirection
266
267
268
269
SUBROUTINE ClusterElementsByDirection(Params,Mesh,Clustering,MaskActive)
270
271
TYPE(ValueList_t), POINTER :: Params
272
TYPE(Mesh_t), POINTER :: Mesh
273
LOGICAL, OPTIONAL :: MaskActive(:)
274
INTEGER, POINTER :: Clustering(:)
275
!---------------------------------------------------------------
276
LOGICAL :: MaskExists,GotIt,Hit
277
REAL(KIND=dp), ALLOCATABLE :: Measure(:)
278
INTEGER :: i,j,k,k0,l,ind,n,dim,dir,divs,nsize,elemsinpart,clusters
279
INTEGER, POINTER :: Iarray(:),Order(:),NodePart(:),NoPart(:)
280
INTEGER :: Divisions(3),minpart,maxpart,clustersize
281
REAL(KIND=dp), POINTER :: PArray(:,:), Arrange(:)
282
REAL(KIND=dp) :: Normal(3), Tangent1(3), Tangent2(3), Coord(3), Weights(3), &
283
avepart,devpart, dist
284
TYPE(Element_t), POINTER :: Element
285
INTEGER, POINTER :: NodeIndexes(:)
286
CHARACTER(*), PARAMETER :: Caller="ClusterNodesByDirection"
287
!---------------------------------------------------------------
288
289
CALL Info(Caller,'Starting Clustering',Level=30)
290
291
MaskExists = PRESENT(MaskActive)
292
IF( MaskExists ) THEN
293
nsize = COUNT( MaskActive )
294
ELSE
295
nsize = Mesh % NumberOfBulkElements
296
END IF
297
298
IF( .NOT. ASSOCIATED( Params ) ) THEN
299
CALL Fatal(Caller,'No parameter list associated')
300
END IF
301
302
dim = Mesh % MeshDim
303
Parray => ListGetConstRealArray( Params,'Clustering Normal Vector',GotIt )
304
IF(.NOT. GotIt) THEN
305
Parray => ListGetConstRealArray( Params,'Partitioning Normal Vector',GotIt )
306
END IF
307
IF( GotIt ) THEN
308
Normal = Parray(1:3,1)
309
ELSE
310
Normal(1) = 1.0
311
Normal(2) = 1.0d-2
312
IF( dim == 3) THEN
313
Normal(3) = 1.0d-4
314
ELSE
315
Normal(3) = 0.0_dp
316
END IF
317
END IF
318
Normal = Normal / SQRT( SUM( Normal ** 2) )
319
320
CALL TangentDirections( Normal,Tangent1,Tangent2 )
321
322
IF( .FALSE. ) THEN
323
PRINT *,'Normal:',Normal
324
PRINT *,'Tangent1:',Tangent1
325
PRINT *,'Tangent2:',Tangent2
326
END IF
327
328
Iarray => ListGetIntegerArray( Params,'Partitioning Divisions',GotIt )
329
IF(.NOT. GotIt ) THEN
330
Iarray => ListGetIntegerArray( Params,'MG Cluster Divisions',GotIt )
331
END IF
332
333
Divisions = 1
334
IF( GotIt ) THEN
335
n = MIN( SIZE(Iarray), dim )
336
Divisions(1:n) = Iarray(1:n)
337
ELSE
338
clustersize = ListGetInteger( Params,'Partitioning Size',GotIt)
339
IF(.NOT. GotIt) clustersize = ListGetInteger( Params,'MG Cluster Size',GotIt)
340
IF( GotIt .AND. ClusterSize > 0) THEN
341
IF( dim == 2 ) THEN
342
Divisions(1) = ( nsize / clustersize ) ** 0.5_dp
343
Divisions(2) = ( nsize / ( clustersize * Divisions(1) ) )
344
ELSE
345
Divisions(1:2) = ( nsize / clustersize ) ** (1.0_dp / 3 )
346
Divisions(3) = ( nsize / ( clustersize * Divisions(1) * Divisions(2) ) )
347
END IF
348
ELSE
349
CALL Fatal(Caller,'Clustering Divisions not given!')
350
END IF
351
END IF
352
353
Clusters = Divisions(1) * Divisions(2) * Divisions(3)
354
355
IF( .FALSE. ) THEN
356
PRINT *,'dim:',dim
357
PRINT *,'divisions:',divisions
358
PRINT *,'clusters:',clusters
359
PRINT *,'nsize:',nsize
360
END IF
361
362
ALLOCATE(Order(nsize),Arrange(nsize),NodePart(nsize),NoPart(Clusters))
363
364
365
! These are needed as an initial value for the loop over dimension
366
elemsinpart = nsize
367
nodepart = 1
368
369
370
! Go through each direction and cumulatively add to the clusters
371
!-----------------------------------------------------------
372
373
DO dir = 1,dim
374
divs = Divisions(dir)
375
IF( divs <= 1 ) CYCLE
376
377
! Use the three principal directions as the weight
378
!-------------------------------------------------
379
IF( dir == 1 ) THEN
380
Weights = Normal
381
ELSE IF( dir == 2 ) THEN
382
Weights = Tangent1
383
ELSE
384
Weights = Tangent2
385
END IF
386
387
! Now compute the weights for each node
388
!----------------------------------------
389
j = 0
390
DO i=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
391
IF( MaskExists ) THEN
392
IF( .NOT. MaskActive( i ) ) CYCLE
393
ELSE
394
IF( i > Mesh % NumberOfBulkElements ) EXIT
395
END IF
396
397
Element => Mesh % Elements(i)
398
NodeIndexes => Element % NodeIndexes
399
n = Element % TYPE % NumberOfNodes
400
401
Coord(1) = SUM( Mesh % Nodes % x( NodeIndexes ) ) / n
402
Coord(2) = SUM( Mesh % Nodes % y( NodeIndexes ) ) / n
403
Coord(3) = SUM( Mesh % Nodes % z( NodeIndexes ) ) / n
404
405
j = j + 1
406
Arrange(j) = SUM( Weights * Coord )
407
408
! Initialize ordering for the current direction
409
Order(j) = j
410
END DO
411
412
! Order the distances for given direction, only the active ones
413
!--------------------------------------------------------------
414
CALL SortR(nsize,Order,Arrange)
415
416
! For each direction the number of elements in cluster becomes smaller
417
elemsinpart = elemsinpart / divs
418
419
! initialize the counter partition
420
nopart = 0
421
422
! Go through each node and locate it to a cluster taking into consideration
423
! the previous clustering (for 1st direction all one)
424
!------------------------------------------------------------------------
425
j = 1
426
DO i = 1,nsize
427
ind = Order(i)
428
429
! the initial partition offset depends on previous partitioning
430
k0 = (nodepart(ind)-1) * divs
431
432
! Find the correct new partitioning, this loop is just long enough
433
DO l=1,divs
434
Hit = .FALSE.
435
436
! test for increase of local partition
437
IF( j < divs ) THEN
438
IF( nopart(k0+j) >= elemsinpart ) THEN
439
j = j + 1
440
Hit = .TRUE.
441
END IF
442
END IF
443
444
! test for decrease of local partition
445
IF( j > 1 ) THEN
446
IF( nopart(k0+j-1) < elemsinpart ) THEN
447
j = j - 1
448
Hit = .TRUE.
449
END IF
450
END IF
451
452
! If either increase or decrease is needed, this must be ok
453
IF(.NOT. Hit) EXIT
454
END DO
455
456
k = k0 + j
457
nopart(k) = nopart(k) + 1
458
459
! Now set the partition
460
nodepart(ind) = k
461
END DO
462
463
END DO
464
465
466
minpart = HUGE(minpart)
467
maxpart = 0
468
avepart = 1.0_dp * nsize / clusters
469
devpart = 0.0_dp
470
DO i=1,clusters
471
minpart = MIN( minpart, nopart(i))
472
maxpart = MAX( maxpart, nopart(i))
473
devpart = devpart + ABS ( nopart(i) - avepart )
474
END DO
475
devpart = devpart / clusters
476
477
WRITE(Message,'(A,T25,I10)') 'Min nodes in cluster:',minpart
478
CALL Info(Caller,Message)
479
WRITE(Message,'(A,T25,I10)') 'Max nodes in cluster:',maxpart
480
CALL Info(Caller,Message)
481
WRITE(Message,'(A,T28,F10.2)') 'Average nodes in cluster:',avepart
482
CALL Info(Caller,Message)
483
WRITE(Message,'(A,T28,F10.2)') 'Deviation of nodes:',devpart
484
CALL Info(Caller,Message)
485
486
487
IF( ASSOCIATED(Clustering)) THEN
488
IF( PRESENT( MaskActive ) ) THEN
489
j = 0
490
DO i=1, SIZE(MaskActive)
491
IF( MaskActive(i) ) THEN
492
j = j + 1
493
Clustering(i) = Nodepart(j)
494
END IF
495
END DO
496
ELSE
497
Clustering = Nodepart
498
END IF
499
DEALLOCATE(Nodepart)
500
ELSE
501
Clustering => Nodepart
502
NULLIFY( Nodepart )
503
END IF
504
505
DEALLOCATE(Order,Arrange,NoPart)
506
507
END SUBROUTINE ClusterElementsByDirection
508
509
510
511
SUBROUTINE ClusterElementsUniform(Params,Mesh,Clustering,MaskActive,PartitionDivisions)
512
513
TYPE(ValueList_t), POINTER :: Params
514
TYPE(Mesh_t), POINTER :: Mesh
515
INTEGER, POINTER :: Clustering(:)
516
LOGICAL, OPTIONAL :: MaskActive(:)
517
INTEGER, OPTIONAL :: PartitionDivisions(3)
518
!---------------------------------------------------------------
519
LOGICAL :: MaskExists,UseMaskedBoundingBox,Found
520
INTEGER :: i,j,k,ind,n,dim,nsize,nmask,clusters
521
INTEGER, POINTER :: Iarray(:),ElemPart(:)
522
INTEGER, ALLOCATABLE :: NoPart(:)
523
INTEGER :: Divisions(3),minpart,maxpart,Inds(3)
524
REAL(KIND=dp) :: Coord(3), Weights(3), avepart,devpart
525
TYPE(Element_t), POINTER :: Element
526
INTEGER, POINTER :: NodeIndexes(:)
527
REAL(KIND=dp) :: BoundingBox(6)
528
INTEGER, ALLOCATABLE :: CellCount(:,:,:)
529
LOGICAL, ALLOCATABLE :: NodeMask(:)
530
CHARACTER(*),PARAMETER :: Caller="ClusterElementsUniform"
531
532
CALL Info(Caller,'Clustering elements uniformly in bounding box',Level=12)
533
534
IF( Mesh % NumberOfBulkElements == 0 ) RETURN
535
536
MaskExists = PRESENT(MaskActive)
537
IF( MaskExists ) THEN
538
nsize = SIZE( MaskActive )
539
nmask = COUNT( MaskActive )
540
CALL Info(Caller,'Applying division to masked element: '//I2S(nmask),Level=8)
541
ELSE
542
nsize = Mesh % NumberOfBulkElements
543
nmask = nsize
544
CALL Info(Caller,'Applying division to all bulk elements: '//I2S(nsize),Level=8)
545
END IF
546
547
IF( .NOT. ASSOCIATED( Params ) ) THEN
548
CALL Fatal(Caller,'No parameter list associated')
549
END IF
550
551
dim = Mesh % MeshDim
552
553
! We can use the masked bounding box
554
UseMaskedBoundingBox = .FALSE.
555
IF( MaskExists ) UseMaskedBoundingBox = ListGetLogical( Params,&
556
'Partition Masked Bounding Box',Found )
557
558
IF( UseMaskedBoundingBox ) THEN
559
ALLOCATE( NodeMask( Mesh % NumberOfNodes ) )
560
NodeMask = .FALSE.
561
562
! Add all active nodes to the mask
563
DO i=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
564
IF( .NOT. MaskActive( i ) ) CYCLE
565
Element => Mesh % Elements(i)
566
NodeIndexes => Element % NodeIndexes
567
NodeMask( NodeIndexes ) = .TRUE.
568
END DO
569
570
i = COUNT( NodeMask )
571
CALL Info(Caller,'Masked elements include nodes: '//I2S(i),Level=8)
572
573
! Define the masked bounding box
574
BoundingBox(1) = MINVAL( Mesh % Nodes % x, NodeMask )
575
BoundingBox(2) = MAXVAL( Mesh % Nodes % x, NodeMask )
576
BoundingBox(3) = MINVAL( Mesh % Nodes % y, NodeMask )
577
BoundingBox(4) = MAXVAL( Mesh % Nodes % y, NodeMask )
578
BoundingBox(5) = MINVAL( Mesh % Nodes % z, NodeMask )
579
BoundingBox(6) = MAXVAL( Mesh % Nodes % z, NodeMask )
580
581
DEALLOCATE( NodeMask )
582
ELSE
583
BoundingBox(1) = MINVAL( Mesh % Nodes % x )
584
BoundingBox(2) = MAXVAL( Mesh % Nodes % x )
585
BoundingBox(3) = MINVAL( Mesh % Nodes % y )
586
BoundingBox(4) = MAXVAL( Mesh % Nodes % y )
587
BoundingBox(5) = MINVAL( Mesh % Nodes % z )
588
BoundingBox(6) = MAXVAL( Mesh % Nodes % z )
589
END IF
590
591
592
IF( PRESENT( PartitionDivisions ) ) THEN
593
Divisions = PartitionDivisions
594
ELSE
595
Iarray => ListGetIntegerArray( Params,'Partitioning Divisions',Found)
596
IF(.NOT. Found ) THEN
597
CALL Fatal(Caller,'> Partitioning Divisions < not given!')
598
END IF
599
Divisions = 1
600
IF( Found ) THEN
601
n = MIN( SIZE(Iarray), dim )
602
Divisions(1:n) = Iarray(1:n)
603
END IF
604
END IF
605
606
ALLOCATE( CellCount(Divisions(1), Divisions(2), Divisions(3) ) )
607
CellCount = 0
608
Clusters = 1
609
DO i=1,dim
610
Clusters = Clusters * Divisions(i)
611
END DO
612
613
IF( .FALSE. ) THEN
614
PRINT *,'dim:',dim
615
PRINT *,'divisions:',divisions
616
PRINT *,'clusters:',clusters
617
PRINT *,'nsize:',nsize
618
END IF
619
620
ALLOCATE(ElemPart(nsize),NoPart(Clusters))
621
NoPart = 0
622
ElemPart = 0
623
624
!----------------------------------------
625
Inds = 1
626
Coord = 0.0_dp
627
628
DO i=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
629
IF( MaskExists ) THEN
630
IF( .NOT. MaskActive( i ) ) CYCLE
631
ELSE
632
IF( i > Mesh % NumberOfBulkElements ) EXIT
633
END IF
634
635
Element => Mesh % Elements(i)
636
NodeIndexes => Element % NodeIndexes
637
n = Element % TYPE % NumberOfNodes
638
639
! Find the center of the element
640
Coord(1) = SUM( Mesh % Nodes % x( NodeIndexes ) ) / n
641
Coord(2) = SUM( Mesh % Nodes % y( NodeIndexes ) ) / n
642
IF( dim == 3 ) THEN
643
Coord(3) = SUM( Mesh % Nodes % z( NodeIndexes ) ) / n
644
END IF
645
646
Inds = 1
647
DO j=1,dim
648
Inds(j) = CEILING( Divisions(j) * &
649
( Coord(j) - BoundingBox(2*j-1) ) / &
650
( BoundingBox(2*j) - BoundingBox(2*j-1) ) )
651
END DO
652
Inds = MAX( Inds, 1 )
653
654
CellCount(Inds(1),Inds(2),Inds(3)) = &
655
CellCount(Inds(1),Inds(2),Inds(3)) + 1
656
657
ind = (Inds(1)-1)*Divisions(2)*Divisions(3) + &
658
(Inds(2)-1)*Divisions(3) + &
659
Inds(3)
660
ElemPart(i) = ind
661
NoPart(ind) = NoPart(ind) + 1
662
END DO
663
664
! Compute statistical information of the partitioning
665
n = COUNT( NoPart > 0 )
666
minpart = HUGE(minpart)
667
maxpart = 0
668
avepart = 1.0_dp * nmask / n
669
devpart = 0.0_dp
670
DO i=1,clusters
671
IF( nopart(i) > 0 ) THEN
672
minpart = MIN( minpart, nopart(i))
673
maxpart = MAX( maxpart, nopart(i))
674
devpart = devpart + ABS ( nopart(i) - avepart )
675
END IF
676
END DO
677
devpart = devpart / n
678
679
CALL Info(Caller,'Number of partitions: '//I2S(n),Level=8)
680
CALL Info(Caller,'Min elements in cluster: '//I2S(minpart),Level=8)
681
CALL Info(Caller,'Max elements in cluster: '//I2S(maxpart),Level=8)
682
683
WRITE(Message,'(A,F10.2)') 'Average elements in cluster:',avepart
684
CALL Info(Caller,Message,Level=8)
685
WRITE(Message,'(A,F10.2)') 'Average deviation in size:',devpart
686
CALL Info(Caller,Message,Level=8)
687
688
! Renumber the partitions using only the active ones
689
n = 0
690
DO i=1,clusters
691
IF( NoPart(i) > 0 ) THEN
692
n = n + 1
693
NoPart(i) = n
694
END IF
695
END DO
696
697
! Renumbering only needed if there are empty cells
698
IF( n < clusters ) THEN
699
DO i=1,nsize
700
j = ElemPart(i)
701
IF( j > 0 ) ElemPart(i) = NoPart(j)
702
END DO
703
END IF
704
705
!DO i=1,clusters
706
! PRINT *,'count in part:',i,COUNT( ElemPart(1:nsize) == i )
707
!END DO
708
709
IF( ASSOCIATED( Clustering ) ) THEN
710
WHERE( ElemPart > 0 ) Clustering = ElemPart
711
DEALLOCATE( ElemPart )
712
ELSE
713
Clustering => ElemPart
714
NULLIFY( ElemPart )
715
END IF
716
717
DEALLOCATE(NoPart,CellCount)
718
719
CALL Info(Caller,'Clustering of elements finished',Level=10)
720
721
END SUBROUTINE ClusterElementsUniform
722
723
724
!------------------------------------------------------------------------------
725
!> Subroutine creates clusters of connections in different ways. The default method
726
!> uses only information from the matrix connectios. The geometric version makes
727
!> clusters using recursive splitting in each coordinate direction. There is also
728
!> a version that assumes that the initial mesh was created using extrusion and
729
!> this extrusion may be taken back in clustering.
730
!------------------------------------------------------------------------------
731
SUBROUTINE ChooseClusterNodes(Amat, Solver, Components, EliminateDir, CF)
732
733
USE MeshUtils, ONLY : DetectExtrudedStructure, DetectExtrudedElements
734
735
TYPE(Matrix_t), POINTER :: Amat
736
TYPE(solver_t), TARGET :: Solver
737
INTEGER :: Components
738
LOGICAL :: EliminateDir
739
INTEGER, POINTER :: CF(:)
740
741
INTEGER :: matsize, nodesize, Component1
742
LOGICAL, POINTER :: Bonds(:), Fixed(:),Passive(:)
743
INTEGER, POINTER :: Cols(:),Rows(:),CFLayer(:)
744
LOGICAL :: GotIt
745
746
TYPE(Solver_t), POINTER :: PSolver
747
TYPE(Mesh_t), POINTER :: Mesh
748
INTEGER, POINTER :: MaskPerm(:)
749
LOGICAL, POINTER :: MaskActive(:)
750
CHARACTER(:), ALLOCATABLE :: ClusterMethod
751
752
ClusterMethod = ListGetString( Solver % Values,'MG Cluster Method',GotIt)
753
IF(.NOT. GotIt ) ClusterMethod = 'default'
754
755
SELECT CASE ( ClusterMethod )
756
757
CASE( 'geometric' )
758
CALL Info('ChooseClusterNodes','Using geometric clustering')
759
PSolver => Solver
760
Mesh => Solver % Mesh
761
MaskPerm => Solver % Variable % Perm
762
IF( ASSOCIATED( MaskPerm ) ) THEN
763
ALLOCATE( MaskActive( SIZE( MaskPerm ) ) )
764
MaskActive = ( MaskPerm > 0 )
765
END IF
766
767
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfNodes ) THEN
768
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, geometric clustering works only for two levels!')
769
END IF
770
771
CALL ClusterNodesByDirection(Solver % Values,Mesh,CF,MaskActive)
772
773
IF( ASSOCIATED( MaskPerm ) ) DEALLOCATE( MaskActive )
774
775
CASE( 'unextrude' )
776
CALL Info('ChooseClusterNodes','Using dimensional reduction of extruded meshes for clustering')
777
Mesh => Solver % Mesh
778
MaskPerm => Solver % Variable % Perm
779
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfNodes ) THEN
780
PRINT *,'sizes:',Amat % NumberOfRows, Components, Mesh % NumberOfNodes
781
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, extruded node clustering works only for two levels!')
782
END IF
783
CALL ClusterExtrudedMesh()
784
785
CASE( 'edge' )
786
CALL Info('ChooseClusterNodes','Using dimensional edge reduction of extruded edge meshes')
787
Mesh => Solver % Mesh
788
MaskPerm => Solver % Variable % Perm
789
790
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfEdges ) THEN
791
PRINT *,'sizes:',Amat % NumberOfRows, Components, Mesh % NumberOfEdges
792
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, extruded edge clustering works only for two levels!')
793
END IF
794
795
CALL ClusterExtrudedEdges()
796
797
798
CASE( 'hybrid' )
799
CALL Info('ChooseClusterNodes','Using hybrid clustering method')
800
Component1 = 1
801
IF(Components > 1) THEN
802
Component1 = ListGetInteger(Solver % Values,'MG Determining Component',&
803
GotIt,minv=1,maxv=Components)
804
IF(.NOT. GotIt) Component1 = 1
805
END IF
806
807
Rows => Amat % Rows
808
Cols => Amat % Cols
809
matsize = Amat % NumberOfRows
810
nodesize = matsize / Components
811
ALLOCATE( Bonds(SIZE(Amat % Cols)), Passive(nodesize),Fixed(nodesize), CF(nodesize) )
812
813
PSolver => Solver
814
Mesh => Solver % Mesh
815
MaskPerm => Solver % Variable % Perm
816
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfNodes ) THEN
817
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, geometric clustering works only for two levels!')
818
END IF
819
820
CALL PassiveExtrudedMesh()
821
822
CALL CMGBonds(Amat, Bonds, Passive, Fixed, Components,Component1)
823
CALL CMGClusterForm(Amat, Bonds, Passive, Fixed, Components, Component1, CFLayer)
824
DEALLOCATE(Bonds, Fixed)
825
826
CALL Info('ChooseClusterNodes','Using dimensional reduction of extruded meshes for clustering')
827
Mesh => Solver % Mesh
828
MaskPerm => Solver % Variable % Perm
829
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfNodes ) THEN
830
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, extruded clustering works only for two levels!')
831
END IF
832
CALL ClusterExtrudedMesh( CFLayer)
833
834
CASE DEFAULT
835
CALL Info('ChooseClusterNodes','Using clustering based on matrix connections')
836
Component1 = 1
837
IF(Components > 1) THEN
838
Component1 = ListGetInteger(Solver % Values,'MG Determining Component',&
839
GotIt,minv=1,maxv=Components)
840
IF(.NOT. GotIt) Component1 = 1
841
END IF
842
843
Rows => Amat % Rows
844
Cols => Amat % Cols
845
matsize = Amat % NumberOfRows
846
nodesize = matsize / Components
847
ALLOCATE( Bonds(SIZE(Amat % Cols)), Passive(nodesize),Fixed(nodesize), CF(nodesize) )
848
849
! For parallel cases we cluster only the "own" dofs and communicate the clustering
850
! afterwards. The not own dofs are skipped.
851
Passive = .FALSE.
852
CALL SetParallelPassive()
853
854
CALL CMGBonds(Amat, Bonds, Passive, Fixed, Components,Component1)
855
856
CALL CMGClusterForm(Amat, Bonds, Passive, Fixed, Components, Component1, CF)
857
858
DEALLOCATE(Bonds, Fixed)
859
860
END SELECT
861
862
IF(.FALSE.) CALL Info('ChooseClusterNodes','Clusters chosen')
863
864
CONTAINS
865
866
867
!-------------------------------------------------------------------------------
868
!> This subroutine sets nodes that are now owned by the partition to be passive
869
!> before making the clustering. The idea is to communicate the clustering on
870
!> parallel level based on the ownership.
871
!-------------------------------------------------------------------------------
872
SUBROUTINE SetParallelPassive()
873
INTEGER :: i,j
874
875
! If not parallel then return
876
IF( ParEnv % PEs <= 1 ) RETURN
877
878
! Note that the ParallelInfo for matrices equals the size of the matrix
879
! while for Meshes in equals the number of nodes. Here the former is used.
880
DO i=1,nodesize
881
j = Components*(i-1) + Component1
882
IF( Amat % ParallelInfo % GInterface(j) ) CYCLE
883
IF( Amat % ParallelInfo % NeighbourList(j) % Neighbours(1) /= ParEnv % Mype ) &
884
Passive(i) = .TRUE.
885
END DO
886
END SUBROUTINE SetParallelPassive
887
888
889
890
!-------------------------------------------------------------------------------
891
!> Sets a marker for passive dofs everywhere else than at the top layer.
892
!> This is intended for the hybrid method where a cluster is first made on
893
!> a level and this is then inherited on the extruded levels.
894
!------------------------------------------------------------------------
895
SUBROUTINE PassiveExtrudedMesh()
896
897
INTEGER, POINTER :: TopPointer(:)
898
INTEGER :: i, j, nsize
899
TYPE(Variable_t), POINTER :: ExtVar
900
TYPE(Solver_t), POINTER :: Psolver
901
902
PSolver => Solver
903
nsize = SIZE( MaskPerm )
904
905
CALL Info('PassiveExrudedMesh','Setting active nodes on a layer only')
906
907
CALL DetectExtrudedStructure( Mesh, PSolver, ExtVar, &
908
TopNodePointer = TopPointer )
909
910
DO i=1,nsize
911
! Node at top is active
912
IF( TopPointer(i) == i ) CYCLE
913
914
! All other nodes, if existing, are passive
915
! Here i refers to physical node, and j to corresponding ordering in matrix
916
j = MaskPerm( i )
917
IF( j == 0 ) CYCLE
918
Passive( j ) = .TRUE.
919
END DO
920
921
i = COUNT( Passive )
922
PRINT *,'Number of passive nodes:',i
923
PRINT *,'Number of active nodes:',nsize-i
924
925
DEALLOCATE( TopPointer )
926
927
END SUBROUTINE PassiveExtrudedMesh
928
929
930
!-------------------------------------------------------------------------------
931
!> First detects an extruded structure using mesh information, and
932
!> then clusters the nodes along the extruded directions only.
933
!-------------------------------------------------------------------------------
934
SUBROUTINE ClusterExtrudedMesh( CFLayer )
935
936
INTEGER, POINTER, OPTIONAL :: CFLayer(:)
937
938
INTEGER, POINTER :: TopPointer(:), DownPointer(:)
939
INTEGER :: i, j, k, ind, nsize, NoLayers, cluster, NoClusters, ClusterSize
940
TYPE(Variable_t), POINTER :: ExtVar
941
TYPE(Solver_t), POINTER :: Psolver
942
943
PSolver => Solver
944
945
nsize = SIZE( MaskPerm )
946
ALLOCATE( CF( nsize) )
947
948
CF = 0
949
950
ClusterSize = ListGetInteger(Solver % Values,'MG Cluster Size',GotIt)
951
952
! Find the extruded structure
953
CALL DetectExtrudedStructure( Mesh, PSolver, ExtVar, &
954
TopNodePointer = TopPointer, DownNodePointer = DownPointer, &
955
NumberOfLayers = NoLayers )
956
957
! Numbering of the top layer clusters
958
IF( PRESENT( CFLayer ) ) THEN
959
NoClusters = MAXVAL( CFLayer )
960
CF = CFLayer
961
ELSE
962
NoClusters = 0
963
DO i=1,nsize
964
IF( TopPointer(i) == i ) THEN
965
NoClusters = NoClusters + 1
966
CF( MaskPerm(i) ) = NoClusters
967
END IF
968
END DO
969
END IF
970
971
! Number the nodes not on the top layer also
972
DO i=1,nsize
973
IF( TopPointer(i) == 0 ) CYCLE
974
975
! start from each top layer node
976
IF( TopPointer(i) == i ) THEN
977
k = 1
978
ind = i
979
980
! inherit the first cluster from the top layer
981
cluster = CF( MaskPerm(ind) )
982
DO j = 1,NoLayers
983
ind = DownPointer(ind)
984
985
! when cluster size is full start a new cluster layer with offset of NoClusters
986
IF( k == ClusterSize ) THEN
987
k = 0
988
cluster = cluster + NoClusters
989
END IF
990
k = k + 1
991
CF( MaskPerm(ind) ) = cluster
992
END DO
993
END IF
994
END DO
995
996
DEALLOCATE( DownPointer, TopPointer )
997
998
END SUBROUTINE ClusterExtrudedMesh
999
1000
!-------------------------------------------------------------------------------
1001
!> First detects an extruded elements using mesh information, and
1002
!> then clusters the edges to those of bottom elements,
1003
!-------------------------------------------------------------------------------
1004
SUBROUTINE ClusterExtrudedEdges( CFLayer )
1005
1006
INTEGER, POINTER, OPTIONAL :: CFLayer(:)
1007
1008
INTEGER, POINTER :: TopPointer(:), BotPointer(:)
1009
INTEGER :: i, j, k, j2, nsize, NoLayers, NoClusters
1010
TYPE(Variable_t), POINTER :: ExtVar
1011
TYPE(Solver_t), POINTER :: Psolver
1012
TYPE(Mesh_t), POINTER :: Mesh
1013
TYPE(Element_t), POINTER :: Element, Element2
1014
INTEGER, POINTER :: Indexes(:), Indexes2(:), Perm(:), CFPerm(:)
1015
INTEGER :: Indx, elem, t, t2, dofs, n, n2, m, m2, n0
1016
INTEGER :: Ncount(5)
1017
1018
1019
PSolver => Solver
1020
Mesh => Solver % Mesh
1021
Perm => Solver % Variable % Perm
1022
1023
1024
dofs = Solver % Variable % DOFs
1025
nsize = Solver % Matrix % NumberOfRows / dofs
1026
n0 = Mesh % NumberOfNodes
1027
1028
ALLOCATE( CF( nsize), CFPerm(nsize) )
1029
CF = 0
1030
CFPerm = 0
1031
1032
Ncount = 0
1033
1034
! Find the extruded structure
1035
CALL DetectExtrudedElements( Mesh, PSolver, ExtVar, &
1036
TopElemPointer = TopPointer, BotElemPointer = BotPointer, &
1037
NumberOfLayers = NoLayers )
1038
1039
DO elem=1,Solver % NumberOfActiveElements
1040
1041
! The index of the element to be mapped
1042
t = Solver % ActiveElements(elem)
1043
! The index of the target element
1044
t2 = BotPointer( t )
1045
1046
! Don't map indexes to self
1047
IF( t2 == t ) THEN
1048
Ncount(1) = Ncount(1) + 1
1049
CYCLE
1050
ELSE
1051
NCount(2) = Ncount(2) +1
1052
END IF
1053
1054
! Note that here the treatment of n and Indexes applied only to classical edge elements
1055
Element => Mesh % Elements( t )
1056
n = Element % TYPE % NumberOFEdges
1057
Indexes => Element % EdgeIndexes
1058
1059
Element2 => Mesh % Elements( t2 )
1060
n2 = Element2 % TYPE % NumberOFEdges
1061
Indexes2 => Element2 % EdgeIndexes
1062
1063
m = Element % TYPE % NumberOFNodes
1064
m2 = Element2 % TYPE % NumberOFNodes
1065
1066
IF( n /= n2 ) CALL Fatal('ClusterExtrudeEdges','Cannot map different number of dofs!')
1067
1068
! Here we assume that the numbering of each element is similar!
1069
DO i=1,n
1070
j = Perm(Indexes(i))
1071
j2 = Perm(Indexes2(i))
1072
1073
!PRINT *,'CF:',j,CF(j),j2
1074
1075
IF( CF(j) /= j2 ) THEN
1076
Ncount(3) = Ncount(3) + 1
1077
CF(j) = j2
1078
ELSE
1079
Ncount(4) = Ncount(4) + 1
1080
END IF
1081
END DO
1082
1083
END DO
1084
1085
CFPerm = 0
1086
DO i=1,nsize
1087
CFPerm( CF(i) ) = 1
1088
END DO
1089
1090
k = 0
1091
DO i=1,nsize
1092
IF( CFPerm(i) > 0 ) THEN
1093
k = k + 1
1094
CFPerm(i) = k
1095
END IF
1096
END DO
1097
CALL Info('ClusterExtrudedEdges',&
1098
'Number of reduced dofs: '//I2S(k)//' vs. '//I2S(nsize),Level=9)
1099
1100
WRITE(Message,'(A,F10.3)') 'Coarse dofs reduction factor',1.0_dp * nsize / k
1101
CALL Info('ClusterExtrudedEdges', Message, Level=7)
1102
1103
DO i=1,nsize
1104
CF(i) = CFPerm(i)
1105
END DO
1106
1107
PRINT *,'Ncount:',Ncount(1:4)
1108
1109
1110
DEALLOCATE( BotPointer, TopPointer )
1111
1112
END SUBROUTINE ClusterExtrudedEdges
1113
1114
1115
!------------------------------------------------------------------------------
1116
!> Mark the strong connections based on the relative absolute magnitude of the
1117
!> matrix element.
1118
!------------------------------------------------------------------------------
1119
SUBROUTINE CMGBonds(Amat, Bonds, Passive, Fixed, Components, Component1)
1120
1121
LOGICAL, POINTER :: Bonds(:), Passive(:), Fixed(:)
1122
TYPE(Matrix_t), POINTER :: Amat
1123
INTEGER :: Components, Component1
1124
1125
REAL(KIND=dp) :: StrongLim
1126
INTEGER :: nods, cnods, matsize, nodesize, maxconn, newbonds, MaxConns, MinConns
1127
INTEGER :: i,j,k,cj,ci,no,elimnods,strongbonds,nodeind,matind
1128
INTEGER, POINTER :: Cols(:),Rows(:),localind(:)
1129
LOGICAL :: ElimDir, Choose, GotIt
1130
REAL(KIND=dp), POINTER :: Values(:), measures(:)
1131
REAL(KIND=dp) :: maxbond, diagbond, dirlim, meas
1132
1133
StrongLim = ListGetConstReal(Solver % Values,'MG Strong Connection Limit',GotIt)
1134
IF(.NOT. GotIt) StrongLim = 0.06
1135
MaxConns = ListGetInteger(Solver % Values,'MG Strong Connection Maximum',GotIt)
1136
MinConns = ListGetInteger(Solver % Values,'MG Strong Connection Minimum',GotIt)
1137
1138
ElimDir = EliminateDir
1139
DirLim = ListGetConstReal(Solver % Values,'MG Eliminate Dirichlet Limit',GotIt)
1140
IF(.NOT. GotIt) DirLim = 1.0d-8
1141
1142
matsize = Amat % NumberOfRows
1143
Rows => Amat % Rows
1144
Cols => Amat % Cols
1145
Values => Amat % Values
1146
nodesize = matsize / Components
1147
1148
maxconn = 0
1149
DO matind=1,matsize
1150
maxconn = MAX(maxconn,Rows(matind+1)-Rows(matind))
1151
END DO
1152
ALLOCATE(measures(maxconn),localind(maxconn))
1153
1154
Fixed = .FALSE.
1155
Bonds = .FALSE.
1156
strongbonds = 0
1157
elimnods = 0
1158
1159
DO nodeind=1,nodesize
1160
1161
IF( Passive( nodeind ) ) CYCLE
1162
1163
matind = (nodeind-1)*Components + Component1
1164
no = 0
1165
maxbond = 0.0d0
1166
1167
DO j=Rows(matind),Rows(matind+1)-1
1168
cj = Cols(j)
1169
IF( MOD(matind, Components) /= MOD(cj, Components) ) CYCLE
1170
k = ( cj - 1 )/Components + 1
1171
IF( Passive( k ) ) CYCLE
1172
1173
IF(cj /= matind ) THEN
1174
no = no + 1
1175
localind(no) = j
1176
measures(no) = ABS( Values(j) )
1177
ELSE
1178
diagbond = ABS(Values(j))
1179
END IF
1180
END DO
1181
1182
IF(no > 0) maxbond = MAXVAL( measures(1:no) )
1183
1184
! Mark Dirichlet nodes in order to favor boundaries in future
1185
IF( ElimDir .AND. maxbond < DirLim * diagbond ) THEN
1186
Fixed(nodeind) = .TRUE.
1187
elimnods = elimnods + 1
1188
CYCLE
1189
END IF
1190
1191
IF(no == 0) CYCLE
1192
Choose = .FALSE.
1193
1194
! Check whether the tentative list would give the right number anaway
1195
IF(MaxConns > 0 .OR. MinConns > 0) THEN
1196
newbonds = 0
1197
DO i=1,no
1198
IF( measures(i) >= Stronglim * maxbond) newbonds = newbonds + 1
1199
END DO
1200
IF(MaxConns > 0 .AND. newbonds > MaxConns) Choose = .TRUE.
1201
IF(MinConns > 0 .AND. newbonds < MinConns) Choose = .TRUE.
1202
END IF
1203
1204
IF(.NOT. Choose) THEN
1205
newbonds = 0
1206
DO i=1,no
1207
IF( measures(i) >= StrongLim * maxbond) THEN
1208
newbonds = newbonds + 1
1209
Bonds(LocalInd(i)) = .TRUE.
1210
END IF
1211
END DO
1212
strongbonds = strongbonds + newbonds
1213
ELSE
1214
CALL SortR(no,LocalInd,measures)
1215
IF(MaxConns > 0 .AND. newbonds > MaxConns) THEN
1216
strongbonds = strongbonds + MaxConns
1217
DO i=1,MaxConns
1218
Bonds(LocalInd(i)) = .TRUE.
1219
END DO
1220
END IF
1221
1222
IF(MinConns > 0 .AND. newbonds < MinConns) THEN
1223
no = MIN(MinConns, no)
1224
strongbonds = strongbonds + no
1225
DO i=1,no
1226
Bonds(LocalInd(i)) = .TRUE.
1227
END DO
1228
END IF
1229
END IF
1230
END DO
1231
1232
DEALLOCATE(measures, localind)
1233
1234
IF(elimnods > 0) THEN
1235
WRITE(Message,'(A,I8)') 'Number of eliminated nodes',elimnods
1236
CALL Info('CMGBonds',Message)
1237
END IF
1238
WRITE(Message,'(A,F8.3)') 'Average number of strong bonds',&
1239
1.0*strongbonds/nodesize
1240
CALL Info('CMGBonds',Message)
1241
1242
END SUBROUTINE CMGBonds
1243
1244
1245
!------------------------------------------------------------------------------
1246
!> Creates clusters of nodes using the given list of strong connections.
1247
!> Nodes assigned by the Passive vector may not be included in the
1248
!> clusters - others are ignored. The subroutine returns the vector CF which tells
1249
!> to which cluster each node belongs to.
1250
!------------------------------------------------------------------------------
1251
SUBROUTINE CMGClusterForm(Amat, Bonds, Passive, Fixed, Components, Component1, CF)
1252
1253
TYPE(Matrix_t), POINTER :: Amat
1254
INTEGER :: Components, Component1
1255
LOGICAL :: Bonds(:),Passive(:),Fixed(:)
1256
INTEGER, POINTER :: CF(:)
1257
1258
INTEGER :: nods, cnods
1259
INTEGER :: i,j,k,l,n,k2,cj,ci,nodeci,nodecj, &
1260
MaxCon, MaxConInd, RefCon, cind, find, rind, &
1261
GatMax, anods, points, neworder, oldorder, nodesize,matsize,&
1262
nextind, sumcon, maxsumcon, ClusterSize, nbonds, &
1263
nsize, nodeind, nodeind2, matind, BoundPoints, ClusterPoints, &
1264
ConnectionPoints, ClusterMode, HalfSize
1265
INTEGER :: LocalCon(100), LocalInd(100)
1266
REAL(KIND=dp) :: LocalVal(100),LocalVal2(100), maxv
1267
INTEGER, POINTER :: Con(:)
1268
INTEGER, POINTER :: Cols(:),Rows(:)
1269
INTEGER, ALLOCATABLE :: GatLims(:), GatNos(:), ConInd(:), RevConInd(:)
1270
REAL(KIND=dp), POINTER :: Values(:)
1271
LOGICAL :: ClusterOrphans, OrphansBest, ClusterGrow, hit, GotIt
1272
1273
1274
BoundPoints = ListGetInteger(Solver % Values,'MG Boundary Priority',GotIt)
1275
IF(.NOT. GotIt) BoundPoints = 1
1276
ClusterPoints = ListGetInteger(Solver % Values,'MG Cluster Priority',GotIt)
1277
IF(.NOT. GotIt) ClusterPoints = MIN(1,BoundPoints+1)
1278
ConnectionPoints = ListGetInteger(Solver % Values,'MG Connection Priority',GotIt)
1279
IF(.NOT. GotIt) ConnectionPoints = 1
1280
ClusterSize = ListGetInteger(Solver % Values,'MG Cluster Size',GotIt)
1281
HalfSize = ListGetInteger(Solver % Values,'MG Cluster Half Size',GotIt)
1282
IF(.NOT. GotIt) HalfSize = (ClusterSize-1)/2
1283
1284
! Particularly for small clusters the orphan control seems to be important
1285
ClusterOrphans = ListGetLogical(Solver % Values,'MG Cluster Orphans',GotIt)
1286
IF(.NOT. GotIt) ClusterOrphans = .TRUE.
1287
OrphansBest = ListGetLogical(Solver % Values,'MG Cluster Orphans Best',GotIt)
1288
ClusterGrow = ListGetLogical(Solver % Values,'MG Cluster Grow',GotIt)
1289
1290
IF(ClusterSize == 0) THEN
1291
ClusterMode = 0
1292
ELSE IF(ClusterSize <= 2) THEN
1293
ClusterMode = 1
1294
ELSE
1295
ClusterMode = 2
1296
END IF
1297
1298
matsize = Amat % NumberOfRows
1299
nodesize = matsize / Components
1300
Rows => Amat % Rows
1301
Cols => Amat % Cols
1302
Values => Amat % Values
1303
1304
CF = 0
1305
ALLOCATE(Con(nodesize))
1306
Con = 0
1307
cnods = 0
1308
cind = 0
1309
1310
! Set an initial measure of importance
1311
!--------------------------------------
1312
DO nodeind = 1, nodesize
1313
IF( Passive( nodeind ) ) CYCLE
1314
IF(Fixed(nodeind)) THEN
1315
Con(nodeind) = 0
1316
ELSE
1317
Con(nodeind) = 1
1318
END IF
1319
END DO
1320
1321
! Add a point for each clustered neighbour
1322
!-------------------------------------------
1323
DO nodeind = 1, nodesize
1324
IF( Passive(nodeind) ) CYCLE
1325
IF(CF(nodeind) > 0) THEN
1326
matind = Components*(nodeind-1) + Component1
1327
DO i=Rows(matind),Rows(matind+1)-1
1328
IF(.NOT. Bonds(i)) CYCLE
1329
ci = Cols(i)
1330
nodeci = (ci-Component1) / Components + 1
1331
IF( PASSIVE(nodeci) ) CYCLE
1332
IF(.NOT. Fixed(nodeci) .AND. CF(nodeci) == 0) THEN
1333
Con(nodeci) = Con(nodeci) + ClusterPoints
1334
END IF
1335
END DO
1336
END IF
1337
END DO
1338
1339
! Add some weight if the node is connected to a Dirichlet node
1340
! This way the clustering will follow the natural boundaries
1341
!-------------------------------------------------------------
1342
IF(BoundPoints /= 0) THEN
1343
DO nodeind = 1, nodesize
1344
IF(Passive(nodeind)) CYCLE
1345
IF(Fixed(nodeind)) CYCLE
1346
matind = Components*(nodeind-1) + Component1
1347
DO i=Rows(matind),Rows(matind+1)-1
1348
IF(.NOT. Bonds(i)) CYCLE
1349
ci = Cols(i)
1350
nodeci = (ci-Component1) / Components + 1
1351
IF(Passive(nodeci)) CYCLE
1352
IF(Fixed(nodeci)) THEN
1353
Con(nodeind) = Con(nodeind) + BoundPoints
1354
END IF
1355
END DO
1356
END DO
1357
END IF
1358
1359
MaxCon = MAXVAL( Con )
1360
GatMax = 2 * MaxCon + 100
1361
1362
! Bookkeeping is kept on category boundaries of the points
1363
!---------------------------------------------------------
1364
ALLOCATE( GatLims(GatMax), GatNos(GatMax) )
1365
GatLims = 0
1366
GatNos = 0
1367
1368
! Number of points in different categories
1369
!-----------------------------------------
1370
DO nodeind = 1, nodesize
1371
IF(Passive(nodeind)) CYCLE
1372
IF(Con(nodeind) == 0) CYCLE
1373
GatNos(Con(nodeind)) = GatNos(Con(nodeind)) + 1
1374
END DO
1375
anods = SUM(GatNos)
1376
1377
DO nodeind = 2, MaxCon
1378
GatLims(nodeind) = GatLims(nodeind-1) + GatNos(nodeind-1)
1379
END DO
1380
1381
ALLOCATE( ConInd(nodesize), RevConInd(anods) )
1382
ConInd = 0
1383
RevConInd = 0
1384
1385
GatNos = 0
1386
DO nodeind = 1, nodesize
1387
IF(Passive(nodeind)) CYCLE
1388
IF(Con(nodeind) == 0) CYCLE
1389
GatNos(Con(nodeind)) = GatNos(Con(nodeind)) + 1
1390
ConInd(nodeind) = GatNos(Con(nodeind)) + GatLims(Con(nodeind))
1391
END DO
1392
1393
RevConInd = 0
1394
DO nodeind = 1, nodesize
1395
IF(Passive(nodeind)) CYCLE
1396
IF(Con(nodeind) == 0) CYCLE
1397
RevConInd(ConInd(nodeind)) = nodeind
1398
END DO
1399
1400
DO WHILE( MaxCon > 0 )
1401
nodeind = RevConInd(anods)
1402
1403
IF(ClusterSize /= 0) THEN
1404
! Assume that the cluster consists of the point with maximum weight and its
1405
! closest tightly coupled neighbours.
1406
!---------------------------------------------------------------------------
1407
n = 0
1408
nbonds = 0
1409
matind = Components*(nodeind-1)+Component1
1410
DO j=Rows(matind),Rows(matind+1)-1
1411
1412
IF(Bonds(j)) THEN
1413
cj = Cols(j)
1414
nodecj = (cj-Component1) / Components + 1
1415
1416
IF( Passive(nodecj) ) CYCLE
1417
IF( Fixed(nodecj) ) CYCLE
1418
1419
nbonds = nbonds + 1
1420
IF(CF(nodecj) == 0) THEN
1421
n = n + 1
1422
LocalCon(n) = Con(nodecj)
1423
LocalInd(n) = nodecj
1424
LocalVal(n) = ABS( Values(j) )
1425
END IF
1426
END IF
1427
END DO
1428
1429
! Favor maximum number of connections before strength of connections
1430
!-------------------------------------------------------------------
1431
IF( n > ClusterSize - 1 ) THEN
1432
1433
! Renormalize the effect of strength of connection to [0,1]
1434
maxv = MAXVAL(LocalVal(1:n))
1435
LocalVal(1:n) = 0.999 * ConnectionPoints * LocalVal(1:n) / maxv + LocalCon(1:n)
1436
k = HalfSize
1437
IF( HalfSize > 0) THEN
1438
CALL SortR(n,LocalInd,LocalVal)
1439
END IF
1440
1441
IF( ClusterMode == 2 ) THEN
1442
k = HalfSize
1443
! Ensure that the strongest connections are always at the top of the list
1444
IF(k > 0) LocalVal(1:k) = LocalVal(1:k) + 1000.0d0
1445
1446
! This and the other commented line could be checked for higher dimension
1447
IF(.FALSE.) LocalVal(k+1:n) = LocalVal(k+1:n) + 10.0d0
1448
1449
! Add points for the connection to the already chosen ones
1450
DO l=1,k
1451
nodeind2 = LocalInd(l)
1452
matind = Components*(nodeind2-1)+Component1
1453
DO j=Rows(matind),Rows(matind+1)-1
1454
IF(Bonds(j)) THEN
1455
cj = Cols(j)
1456
nodecj = (cj-Component1) / Components + 1
1457
IF(Passive(nodecj)) CYCLE
1458
1459
! Let the connection dominate over other points
1460
DO k2=k+1,n
1461
IF( LocalInd(k2) == nodecj) THEN
1462
LocalVal(k2) = LocalVal(k2) + 10.0d0
1463
EXIT
1464
END IF
1465
END DO
1466
IF(ClusterGrow .AND. k2 > n) THEN
1467
n=n+1
1468
LocalInd(n) = nodecj
1469
LocalVal(n) = 10.0d0
1470
END IF
1471
END IF
1472
END DO
1473
1474
END DO
1475
END IF
1476
1477
CALL SortR(n,LocalInd,LocalVal)
1478
n = ClusterSize - 1
1479
END IF
1480
1481
1482
! If a tightly coupled node cannot be a start of a new cluster join it
1483
! to the cluster of its most strongly coupled neighbour
1484
! The other option is that the orphan node builds its own cluster.
1485
!----------------------------------------------------------------------
1486
IF( ClusterOrphans .AND. n == 0 .AND. nbonds > 0) THEN
1487
1488
maxv = 0.0d0
1489
DO j=Rows(matind),Rows(matind+1)-1
1490
IF(Bonds(j)) THEN
1491
cj = Cols(j)
1492
nodecj = (cj-Component1) / Components + 1
1493
IF( Passive(nodecj) ) CYCLE
1494
IF( Fixed(nodecj) ) CYCLE
1495
k = CF(nodecj)
1496
IF( k > 0) THEN
1497
IF( ABS(Values(j)) > maxv) THEN
1498
maxv = ABS( Values(j) )
1499
rind = k
1500
END IF
1501
END IF
1502
END IF
1503
END DO
1504
1505
! If there are many possible candidate parents for the orphan take into account the
1506
! sum of all normalized contributions
1507
!----------------------------------------------------------------------------------
1508
IF( OrphansBest .AND. nbonds > 2) THEN
1509
LocalInd(1:nbonds) = 0
1510
LocalVal(1:nbonds) = 0.0_dp
1511
1512
DO j=Rows(matind),Rows(matind+1)-1
1513
IF(Bonds(j)) THEN
1514
cj = Cols(j)
1515
nodecj = (cj-Component1) / Components + 1
1516
1517
IF( Passive(nodecj) ) CYCLE
1518
IF( Fixed(nodecj) ) CYCLE
1519
k = CF(nodecj)
1520
1521
IF( k > 0) THEN
1522
hit = .FALSE.
1523
DO k2=1,n
1524
IF( LocalInd(k2) == k) THEN
1525
hit = .TRUE.
1526
EXIT
1527
END IF
1528
END DO
1529
IF(.NOT. hit) THEN
1530
n = n + 1
1531
k2 = n
1532
LocalInd(k2) = k
1533
END IF
1534
LocalVal(k2) = LocalVal(k2) + ABS(Values(j)) / maxv + 0.0_dp
1535
END IF
1536
END IF
1537
END DO
1538
1539
maxv = 0.0_dp
1540
DO k=1,n
1541
IF( LocalVal(k) > maxv) THEN
1542
maxv = LocalVal(k)
1543
k2 = LocalInd(k)
1544
END IF
1545
END DO
1546
n = 0
1547
rind = k2
1548
END IF
1549
ELSE
1550
cind = cind + 1
1551
rind = cind
1552
END IF
1553
1554
DO j=0,n
1555
IF(j==0) THEN
1556
nodecj = nodeind
1557
ELSE
1558
nodecj = LocalInd(j)
1559
END IF
1560
1561
CF(nodecj) = rind
1562
cnods = cnods + 1
1563
1564
! Recompute the measure of importance for the neighbours
1565
cj = Components*(nodecj-1) + Component1
1566
DO i=Rows(cj),Rows(cj+1)-1
1567
IF(.NOT. Bonds(i)) CYCLE
1568
1569
ci = Cols(i)
1570
nodeci = (ci-Component1)/Components + 1
1571
1572
IF( Passive( nodeci) ) CYCLE
1573
IF( Fixed(nodeci) ) CYCLE
1574
IF( CF(nodeci) /= 0) CYCLE
1575
1576
DO l=1,ClusterPoints
1577
points = Con(nodeci) + 1
1578
oldorder = ConInd(nodeci)
1579
IF(GatLims(points) == 0) GatLims(points) = anods
1580
neworder = GatLims(points)
1581
Con(nodeci) = points
1582
GatLims(points) = GatLims(points) - 1
1583
1584
IF(neworder /= oldorder) THEN
1585
k = RevConInd(neworder)
1586
1587
ConInd(nodeci) = neworder
1588
ConInd(k) = oldorder
1589
1590
RevConInd(neworder) = nodeci
1591
RevConInd(oldorder) = k
1592
END IF
1593
END DO
1594
1595
END DO
1596
END DO
1597
END IF ! ClusterSize /= 0
1598
1599
! Assume that the center of clustering is the node with maximum weight,
1600
! or any of its strongly coupled neighbours. Compute the total weight of
1601
! the candidate clusters.
1602
! Compute first the cluster points for the point with maximum points
1603
!---------------------------------------------------------------------
1604
IF(ClusterSize == 0) THEN
1605
sumcon = 0
1606
nbonds = 0
1607
matind = Components*(nodeind-1) + Component1
1608
DO j=Rows(matind),Rows(matind+1)-1
1609
IF(.NOT. Bonds(j)) CYCLE
1610
1611
cj = Cols(j)
1612
nodecj = (cj-Component1)/Components + 1
1613
nbonds = nbonds + 1
1614
IF(CF(nodecj) == 0) sumcon = sumcon + Con(nodecj)
1615
END DO
1616
maxsumcon = sumcon
1617
nextind = nodeind
1618
1619
IF(ClusterOrphans .AND. sumcon == 0 .AND. nbonds > 0) THEN
1620
! If a tightly coupled node cannot be a start of a new cluster join it
1621
! to the cluster of its most strongly coupled neighbour
1622
maxv = 0.0d0
1623
DO j=Rows(matind),Rows(matind+1)-1
1624
IF(.NOT. Bonds(j)) CYCLE
1625
1626
cj = Cols(j)
1627
nodecj = (cj-Component1)/Components + 1
1628
IF( Fixed(nodecj) ) CYCLE
1629
IF( CF(nodecj) > 0) THEN
1630
IF( ABS(Values(j)) > maxv) THEN
1631
maxv = ABS( Values(j) )
1632
rind = CF(nodecj)
1633
END IF
1634
END IF
1635
END DO
1636
ELSE
1637
! Next compute the cluster points for all its strongly coupled
1638
! unclustered neighbours
1639
nbonds = 0
1640
DO j=Rows(matind),Rows(matind+1)-1
1641
IF(.NOT. Bonds(j)) CYCLE
1642
1643
sumcon = 0
1644
cj = Cols(j)
1645
nodecj = (cj-Component1)/Components + 1
1646
IF(.NOT. Fixed(nodecj) .AND. CF(nodecj) == 0) THEN
1647
DO i=Rows(cj),Rows(cj+1)-1
1648
IF(Bonds(i)) THEN
1649
ci = Cols(i)
1650
nodeci = (ci-Component1)/Components + 1
1651
IF(CF(nodeci) == 0) sumcon = sumcon + Con(nodeci)
1652
END IF
1653
END DO
1654
END IF
1655
IF(sumcon > maxsumcon) THEN
1656
maxsumcon = sumcon
1657
nextind = nodecj
1658
END IF
1659
END DO
1660
cind = cind + 1
1661
rind = cind
1662
END IF
1663
1664
! The new center for clustering is nextind
1665
nodeind = nextind
1666
CF(nodeind) = rind
1667
cnods = cnods + 1
1668
1669
! Go through all strongly bonded neighbours to 'ind'
1670
matind = Components*(nodeind-1) + Component1
1671
DO j=Rows(matind),Rows(matind+1)-1
1672
IF(.NOT. Bonds(j)) CYCLE
1673
cj = Cols(j)
1674
nodecj = (cj-Component1)/Components + 1
1675
1676
IF( Passive(nodecj) ) CYCLE
1677
1678
IF(CF(nodecj) == 0) THEN
1679
CF(nodecj) = cind
1680
cnods = cnods + 1
1681
IF(Fixed(nodecj)) CYCLE
1682
1683
! Recompute the measure of importance for the neighbours
1684
DO i=Rows(cj),Rows(cj+1)-1
1685
IF(.NOT. Bonds(i)) CYCLE
1686
ci = Cols(i)
1687
nodeci = (ci-Component1)/Components + 1
1688
1689
IF( Passive(nodeci) ) CYCLE
1690
IF( Fixed(nodeci) ) CYCLE
1691
IF( CF(nodeci) /= 0) CYCLE
1692
1693
DO l=1,ClusterPoints
1694
points = Con(nodeci) + 1
1695
oldorder = ConInd(nodeci)
1696
IF(GatLims(points) == 0) GatLims(points) = anods
1697
neworder = GatLims(points)
1698
Con(nodeci) = points
1699
GatLims(points) = GatLims(points) - 1
1700
1701
IF(neworder /= oldorder) THEN
1702
k = RevConInd(neworder)
1703
1704
ConInd(nodeci) = neworder
1705
ConInd(k) = oldorder
1706
1707
RevConInd(neworder) = nodeci
1708
RevConInd(oldorder) = k
1709
END IF
1710
END DO
1711
1712
END DO
1713
END IF
1714
END DO
1715
END IF ! ClusterSize == 0
1716
1717
! Shorten the list from top in case the node is already set
1718
Refcon = 0
1719
DO WHILE( anods > 0)
1720
! DO WHILE( anods > 0 .AND. CF(RevConInd(anods)) /= 0)
1721
IF (CF(RevConInd(anods)) == 0) EXIT
1722
MaxCon = Con(RevConInd(anods))
1723
Refcon = MAX(MaxCon,RefCon)
1724
anods = anods - 1
1725
Refcon = MAX(MaxCon,RefCon)
1726
END DO
1727
1728
IF(anods > 0) THEN
1729
MaxCon = Con(RevConInd(anods))
1730
RefCon = MAX(MaxCon,RefCon)
1731
GatLims((MaxCon+1):(Refcon+1)) = 0
1732
ELSE
1733
MaxCon = 0
1734
END IF
1735
END DO
1736
1737
DEALLOCATE(Con, GatLims, GatNos, ConInd, RevConInd)
1738
1739
WRITE(Message,'(A,I8)') 'Number of clusters',cind
1740
CALL Info('CMGClusterForm',Message)
1741
WRITE(Message,'(A,F8.3)') 'Average size of clusters',1.0*cnods/cind
1742
CALL Info('CMGClusterForm',Message)
1743
1744
END SUBROUTINE CMGClusterForm
1745
1746
END SUBROUTINE ChooseClusterNodes
1747
1748
1749
1750
END MODULE ClusteringMethods
1751
1752
1753
!> \} ElmerLib
1754
1755