MODULE ClusteringMethods
USE Lists
USE SParIterGlobals
USE ElementUtils, ONLY : TangentDirections
IMPLICIT NONE
CONTAINS
SUBROUTINE ClusterNodesByDirection(Params,Mesh,Clustering,MaskActive)
TYPE(ValueList_t), POINTER :: Params
TYPE(Mesh_t), POINTER :: Mesh
LOGICAL, OPTIONAL :: MaskActive(:)
INTEGER, POINTER :: Clustering(:)
LOGICAL :: MaskExists,GotIt,Hit
REAL(KIND=dp), ALLOCATABLE :: Measure(:)
INTEGER :: i,j,k,k0,l,ind,n,dim,dir,divs,nsize,elemsinpart,clusters
INTEGER, POINTER :: Iarray(:),Order(:),NodePart(:),NoPart(:)
INTEGER :: Divisions(3),minpart,maxpart,clustersize
REAL(KIND=dp), POINTER :: PArray(:,:), Arrange(:)
REAL(KIND=dp) :: Normal(3), Tangent1(3), Tangent2(3), Coord(3), Weights(3), &
avepart,devpart
CHARACTER(*), PARAMETER :: Caller="ClusterNodesByDirection"
CALL Info(Caller,'Starting clustering',Level=30)
MaskExists = PRESENT(MaskActive)
IF( MaskExists ) THEN
nsize = COUNT( MaskActive )
ELSE
nsize = Mesh % NumberOfNodes
END IF
IF( .NOT. ASSOCIATED( Params ) ) THEN
CALL Fatal(Caller,'No parameter list associated')
END IF
dim = Mesh % MeshDim
Parray => ListGetConstRealArray( Params,'Clustering Normal Vector',GotIt )
IF( GotIt ) THEN
Normal = Parray(1:3,1)
ELSE
Normal(1) = 1.0
Normal(2) = 1.0d-2
IF( dim == 3) Normal(3) = 1.0d-4
END IF
Normal = Normal / SQRT( SUM( Normal ** 2) )
CALL TangentDirections( Normal,Tangent1,Tangent2 )
IF( .FALSE. ) THEN
PRINT *,'Normal:',Normal
PRINT *,'Tangent1:',Tangent1
PRINT *,'Tangent2:',Tangent2
END IF
Iarray => ListGetIntegerArray( Params,'Partitioning Divisions',GotIt )
IF(.NOT. GotIt) Iarray => ListGetIntegerArray( Params,'MG Cluster Divisions',GotIt )
Divisions = 1
IF( GotIt ) THEN
n = MIN( SIZE(Iarray), dim )
Divisions(1:n) = Iarray(1:n)
ELSE
clustersize = ListGetInteger( Params,'Partitioning Size',GotIt)
IF(.NOT. GotIt) clustersize = ListGetInteger( Params,'MG Cluster Size',GotIt)
IF( GotIt .AND. ClusterSize > 0) THEN
IF( dim == 2 ) THEN
Divisions(1) = ( nsize / clustersize ) ** 0.5_dp
Divisions(2) = ( nsize / ( clustersize * Divisions(1) ) )
ELSE
Divisions(1:2) = ( nsize / clustersize ) ** (1.0_dp / 3 )
Divisions(3) = ( nsize / ( clustersize * Divisions(1) * Divisions(2) ) )
END IF
ELSE
CALL Fatal(Caller,'Clustering Divisions not given!')
END IF
END IF
Clusters = Divisions(1) * Divisions(2) * Divisions(3)
IF( .FALSE. ) THEN
PRINT *,'dim:',dim
PRINT *,'divisions:',divisions
PRINT *,'clusters:',clusters
PRINT *,'nsize:',nsize
END IF
ALLOCATE(Order(nsize),Arrange(nsize),NodePart(nsize),NoPart(Clusters))
elemsinpart = nsize
nodepart = 1
DO dir = 1,dim
divs = Divisions(dir)
IF( divs <= 1 ) CYCLE
IF( dir == 1 ) THEN
Weights = Normal
ELSE IF( dir == 2 ) THEN
Weights = Tangent1
ELSE
Weights = Tangent2
END IF
DO i=1,nsize
Order(i) = i
END DO
DO i=1,Mesh % NumberOfNodes
j = i
IF( MaskExists ) THEN
IF( .NOT. MaskActive(j) ) CYCLE
END IF
Coord(1) = Mesh % Nodes % x(i)
Coord(2) = Mesh % Nodes % y(i)
Coord(3) = Mesh % Nodes % z(i)
Arrange(j) = SUM( Weights * Coord )
END DO
CALL SortR(nsize,Order,Arrange)
elemsinpart = elemsinpart / divs
nopart = 0
j = 1
DO i = 1,nsize
ind = Order(i)
k0 = (nodepart(ind)-1) * divs
DO l=1,divs
Hit = .FALSE.
IF( j < divs ) THEN
IF( nopart(k0+j) >= elemsinpart ) THEN
j = j + 1
Hit = .TRUE.
END IF
END IF
IF( j > 1 ) THEN
IF( nopart(k0+j-1) < elemsinpart ) THEN
j = j - 1
Hit = .TRUE.
END IF
END IF
IF(.NOT. Hit) EXIT
END DO
k = k0 + j
nopart(k) = nopart(k) + 1
nodepart(ind) = k
END DO
END DO
minpart = HUGE(minpart)
maxpart = 0
avepart = 1.0_dp * nsize / clusters
devpart = 0.0_dp
DO i=1,clusters
minpart = MIN( minpart, nopart(i))
maxpart = MAX( maxpart, nopart(i))
devpart = devpart + ABS ( nopart(i) - avepart )
END DO
devpart = devpart / clusters
WRITE(Message,'(A,T25,I10)') 'Min nodes in cluster:',minpart
CALL Info(Caller,Message)
WRITE(Message,'(A,T25,I10)') 'Max nodes in cluster:',maxpart
CALL Info(Caller,Message)
WRITE(Message,'(A,T28,F10.2)') 'Average nodes in cluster:',avepart
CALL Info(Caller,Message)
WRITE(Message,'(A,T28,F10.2)') 'Deviation of nodes:',devpart
CALL Info(Caller,Message)
IF( ASSOCIATED(Clustering)) THEN
Clustering = Nodepart
DEALLOCATE(Nodepart)
ELSE
Clustering => Nodepart
NULLIFY( Nodepart )
END IF
DEALLOCATE(Order,Arrange,NoPart)
END SUBROUTINE ClusterNodesByDirection
SUBROUTINE ClusterElementsByDirection(Params,Mesh,Clustering,MaskActive)
TYPE(ValueList_t), POINTER :: Params
TYPE(Mesh_t), POINTER :: Mesh
LOGICAL, OPTIONAL :: MaskActive(:)
INTEGER, POINTER :: Clustering(:)
LOGICAL :: MaskExists,GotIt,Hit
REAL(KIND=dp), ALLOCATABLE :: Measure(:)
INTEGER :: i,j,k,k0,l,ind,n,dim,dir,divs,nsize,elemsinpart,clusters
INTEGER, POINTER :: Iarray(:),Order(:),NodePart(:),NoPart(:)
INTEGER :: Divisions(3),minpart,maxpart,clustersize
REAL(KIND=dp), POINTER :: PArray(:,:), Arrange(:)
REAL(KIND=dp) :: Normal(3), Tangent1(3), Tangent2(3), Coord(3), Weights(3), &
avepart,devpart, dist
TYPE(Element_t), POINTER :: Element
INTEGER, POINTER :: NodeIndexes(:)
CHARACTER(*), PARAMETER :: Caller="ClusterNodesByDirection"
CALL Info(Caller,'Starting Clustering',Level=30)
MaskExists = PRESENT(MaskActive)
IF( MaskExists ) THEN
nsize = COUNT( MaskActive )
ELSE
nsize = Mesh % NumberOfBulkElements
END IF
IF( .NOT. ASSOCIATED( Params ) ) THEN
CALL Fatal(Caller,'No parameter list associated')
END IF
dim = Mesh % MeshDim
Parray => ListGetConstRealArray( Params,'Clustering Normal Vector',GotIt )
IF(.NOT. GotIt) THEN
Parray => ListGetConstRealArray( Params,'Partitioning Normal Vector',GotIt )
END IF
IF( GotIt ) THEN
Normal = Parray(1:3,1)
ELSE
Normal(1) = 1.0
Normal(2) = 1.0d-2
IF( dim == 3) THEN
Normal(3) = 1.0d-4
ELSE
Normal(3) = 0.0_dp
END IF
END IF
Normal = Normal / SQRT( SUM( Normal ** 2) )
CALL TangentDirections( Normal,Tangent1,Tangent2 )
IF( .FALSE. ) THEN
PRINT *,'Normal:',Normal
PRINT *,'Tangent1:',Tangent1
PRINT *,'Tangent2:',Tangent2
END IF
Iarray => ListGetIntegerArray( Params,'Partitioning Divisions',GotIt )
IF(.NOT. GotIt ) THEN
Iarray => ListGetIntegerArray( Params,'MG Cluster Divisions',GotIt )
END IF
Divisions = 1
IF( GotIt ) THEN
n = MIN( SIZE(Iarray), dim )
Divisions(1:n) = Iarray(1:n)
ELSE
clustersize = ListGetInteger( Params,'Partitioning Size',GotIt)
IF(.NOT. GotIt) clustersize = ListGetInteger( Params,'MG Cluster Size',GotIt)
IF( GotIt .AND. ClusterSize > 0) THEN
IF( dim == 2 ) THEN
Divisions(1) = ( nsize / clustersize ) ** 0.5_dp
Divisions(2) = ( nsize / ( clustersize * Divisions(1) ) )
ELSE
Divisions(1:2) = ( nsize / clustersize ) ** (1.0_dp / 3 )
Divisions(3) = ( nsize / ( clustersize * Divisions(1) * Divisions(2) ) )
END IF
ELSE
CALL Fatal(Caller,'Clustering Divisions not given!')
END IF
END IF
Clusters = Divisions(1) * Divisions(2) * Divisions(3)
IF( .FALSE. ) THEN
PRINT *,'dim:',dim
PRINT *,'divisions:',divisions
PRINT *,'clusters:',clusters
PRINT *,'nsize:',nsize
END IF
ALLOCATE(Order(nsize),Arrange(nsize),NodePart(nsize),NoPart(Clusters))
elemsinpart = nsize
nodepart = 1
DO dir = 1,dim
divs = Divisions(dir)
IF( divs <= 1 ) CYCLE
IF( dir == 1 ) THEN
Weights = Normal
ELSE IF( dir == 2 ) THEN
Weights = Tangent1
ELSE
Weights = Tangent2
END IF
j = 0
DO i=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
IF( MaskExists ) THEN
IF( .NOT. MaskActive( i ) ) CYCLE
ELSE
IF( i > Mesh % NumberOfBulkElements ) EXIT
END IF
Element => Mesh % Elements(i)
NodeIndexes => Element % NodeIndexes
n = Element % TYPE % NumberOfNodes
Coord(1) = SUM( Mesh % Nodes % x( NodeIndexes ) ) / n
Coord(2) = SUM( Mesh % Nodes % y( NodeIndexes ) ) / n
Coord(3) = SUM( Mesh % Nodes % z( NodeIndexes ) ) / n
j = j + 1
Arrange(j) = SUM( Weights * Coord )
Order(j) = j
END DO
CALL SortR(nsize,Order,Arrange)
elemsinpart = elemsinpart / divs
nopart = 0
j = 1
DO i = 1,nsize
ind = Order(i)
k0 = (nodepart(ind)-1) * divs
DO l=1,divs
Hit = .FALSE.
IF( j < divs ) THEN
IF( nopart(k0+j) >= elemsinpart ) THEN
j = j + 1
Hit = .TRUE.
END IF
END IF
IF( j > 1 ) THEN
IF( nopart(k0+j-1) < elemsinpart ) THEN
j = j - 1
Hit = .TRUE.
END IF
END IF
IF(.NOT. Hit) EXIT
END DO
k = k0 + j
nopart(k) = nopart(k) + 1
nodepart(ind) = k
END DO
END DO
minpart = HUGE(minpart)
maxpart = 0
avepart = 1.0_dp * nsize / clusters
devpart = 0.0_dp
DO i=1,clusters
minpart = MIN( minpart, nopart(i))
maxpart = MAX( maxpart, nopart(i))
devpart = devpart + ABS ( nopart(i) - avepart )
END DO
devpart = devpart / clusters
WRITE(Message,'(A,T25,I10)') 'Min nodes in cluster:',minpart
CALL Info(Caller,Message)
WRITE(Message,'(A,T25,I10)') 'Max nodes in cluster:',maxpart
CALL Info(Caller,Message)
WRITE(Message,'(A,T28,F10.2)') 'Average nodes in cluster:',avepart
CALL Info(Caller,Message)
WRITE(Message,'(A,T28,F10.2)') 'Deviation of nodes:',devpart
CALL Info(Caller,Message)
IF( ASSOCIATED(Clustering)) THEN
IF( PRESENT( MaskActive ) ) THEN
j = 0
DO i=1, SIZE(MaskActive)
IF( MaskActive(i) ) THEN
j = j + 1
Clustering(i) = Nodepart(j)
END IF
END DO
ELSE
Clustering = Nodepart
END IF
DEALLOCATE(Nodepart)
ELSE
Clustering => Nodepart
NULLIFY( Nodepart )
END IF
DEALLOCATE(Order,Arrange,NoPart)
END SUBROUTINE ClusterElementsByDirection
SUBROUTINE ClusterElementsUniform(Params,Mesh,Clustering,MaskActive,PartitionDivisions)
TYPE(ValueList_t), POINTER :: Params
TYPE(Mesh_t), POINTER :: Mesh
INTEGER, POINTER :: Clustering(:)
LOGICAL, OPTIONAL :: MaskActive(:)
INTEGER, OPTIONAL :: PartitionDivisions(3)
LOGICAL :: MaskExists,UseMaskedBoundingBox,Found
INTEGER :: i,j,k,ind,n,dim,nsize,nmask,clusters
INTEGER, POINTER :: Iarray(:),ElemPart(:)
INTEGER, ALLOCATABLE :: NoPart(:)
INTEGER :: Divisions(3),minpart,maxpart,Inds(3)
REAL(KIND=dp) :: Coord(3), Weights(3), avepart,devpart
TYPE(Element_t), POINTER :: Element
INTEGER, POINTER :: NodeIndexes(:)
REAL(KIND=dp) :: BoundingBox(6)
INTEGER, ALLOCATABLE :: CellCount(:,:,:)
LOGICAL, ALLOCATABLE :: NodeMask(:)
CHARACTER(*),PARAMETER :: Caller="ClusterElementsUniform"
CALL Info(Caller,'Clustering elements uniformly in bounding box',Level=12)
IF( Mesh % NumberOfBulkElements == 0 ) RETURN
MaskExists = PRESENT(MaskActive)
IF( MaskExists ) THEN
nsize = SIZE( MaskActive )
nmask = COUNT( MaskActive )
CALL Info(Caller,'Applying division to masked element: '//I2S(nmask),Level=8)
ELSE
nsize = Mesh % NumberOfBulkElements
nmask = nsize
CALL Info(Caller,'Applying division to all bulk elements: '//I2S(nsize),Level=8)
END IF
IF( .NOT. ASSOCIATED( Params ) ) THEN
CALL Fatal(Caller,'No parameter list associated')
END IF
dim = Mesh % MeshDim
UseMaskedBoundingBox = .FALSE.
IF( MaskExists ) UseMaskedBoundingBox = ListGetLogical( Params,&
'Partition Masked Bounding Box',Found )
IF( UseMaskedBoundingBox ) THEN
ALLOCATE( NodeMask( Mesh % NumberOfNodes ) )
NodeMask = .FALSE.
DO i=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
IF( .NOT. MaskActive( i ) ) CYCLE
Element => Mesh % Elements(i)
NodeIndexes => Element % NodeIndexes
NodeMask( NodeIndexes ) = .TRUE.
END DO
i = COUNT( NodeMask )
CALL Info(Caller,'Masked elements include nodes: '//I2S(i),Level=8)
BoundingBox(1) = MINVAL( Mesh % Nodes % x, NodeMask )
BoundingBox(2) = MAXVAL( Mesh % Nodes % x, NodeMask )
BoundingBox(3) = MINVAL( Mesh % Nodes % y, NodeMask )
BoundingBox(4) = MAXVAL( Mesh % Nodes % y, NodeMask )
BoundingBox(5) = MINVAL( Mesh % Nodes % z, NodeMask )
BoundingBox(6) = MAXVAL( Mesh % Nodes % z, NodeMask )
DEALLOCATE( NodeMask )
ELSE
BoundingBox(1) = MINVAL( Mesh % Nodes % x )
BoundingBox(2) = MAXVAL( Mesh % Nodes % x )
BoundingBox(3) = MINVAL( Mesh % Nodes % y )
BoundingBox(4) = MAXVAL( Mesh % Nodes % y )
BoundingBox(5) = MINVAL( Mesh % Nodes % z )
BoundingBox(6) = MAXVAL( Mesh % Nodes % z )
END IF
IF( PRESENT( PartitionDivisions ) ) THEN
Divisions = PartitionDivisions
ELSE
Iarray => ListGetIntegerArray( Params,'Partitioning Divisions',Found)
IF(.NOT. Found ) THEN
CALL Fatal(Caller,'> Partitioning Divisions < not given!')
END IF
Divisions = 1
IF( Found ) THEN
n = MIN( SIZE(Iarray), dim )
Divisions(1:n) = Iarray(1:n)
END IF
END IF
ALLOCATE( CellCount(Divisions(1), Divisions(2), Divisions(3) ) )
CellCount = 0
Clusters = 1
DO i=1,dim
Clusters = Clusters * Divisions(i)
END DO
IF( .FALSE. ) THEN
PRINT *,'dim:',dim
PRINT *,'divisions:',divisions
PRINT *,'clusters:',clusters
PRINT *,'nsize:',nsize
END IF
ALLOCATE(ElemPart(nsize),NoPart(Clusters))
NoPart = 0
ElemPart = 0
Inds = 1
Coord = 0.0_dp
DO i=1,Mesh % NumberOfBulkElements + Mesh % NumberOfBoundaryElements
IF( MaskExists ) THEN
IF( .NOT. MaskActive( i ) ) CYCLE
ELSE
IF( i > Mesh % NumberOfBulkElements ) EXIT
END IF
Element => Mesh % Elements(i)
NodeIndexes => Element % NodeIndexes
n = Element % TYPE % NumberOfNodes
Coord(1) = SUM( Mesh % Nodes % x( NodeIndexes ) ) / n
Coord(2) = SUM( Mesh % Nodes % y( NodeIndexes ) ) / n
IF( dim == 3 ) THEN
Coord(3) = SUM( Mesh % Nodes % z( NodeIndexes ) ) / n
END IF
Inds = 1
DO j=1,dim
Inds(j) = CEILING( Divisions(j) * &
( Coord(j) - BoundingBox(2*j-1) ) / &
( BoundingBox(2*j) - BoundingBox(2*j-1) ) )
END DO
Inds = MAX( Inds, 1 )
CellCount(Inds(1),Inds(2),Inds(3)) = &
CellCount(Inds(1),Inds(2),Inds(3)) + 1
ind = (Inds(1)-1)*Divisions(2)*Divisions(3) + &
(Inds(2)-1)*Divisions(3) + &
Inds(3)
ElemPart(i) = ind
NoPart(ind) = NoPart(ind) + 1
END DO
n = COUNT( NoPart > 0 )
minpart = HUGE(minpart)
maxpart = 0
avepart = 1.0_dp * nmask / n
devpart = 0.0_dp
DO i=1,clusters
IF( nopart(i) > 0 ) THEN
minpart = MIN( minpart, nopart(i))
maxpart = MAX( maxpart, nopart(i))
devpart = devpart + ABS ( nopart(i) - avepart )
END IF
END DO
devpart = devpart / n
CALL Info(Caller,'Number of partitions: '//I2S(n),Level=8)
CALL Info(Caller,'Min elements in cluster: '//I2S(minpart),Level=8)
CALL Info(Caller,'Max elements in cluster: '//I2S(maxpart),Level=8)
WRITE(Message,'(A,F10.2)') 'Average elements in cluster:',avepart
CALL Info(Caller,Message,Level=8)
WRITE(Message,'(A,F10.2)') 'Average deviation in size:',devpart
CALL Info(Caller,Message,Level=8)
n = 0
DO i=1,clusters
IF( NoPart(i) > 0 ) THEN
n = n + 1
NoPart(i) = n
END IF
END DO
IF( n < clusters ) THEN
DO i=1,nsize
j = ElemPart(i)
IF( j > 0 ) ElemPart(i) = NoPart(j)
END DO
END IF
IF( ASSOCIATED( Clustering ) ) THEN
WHERE( ElemPart > 0 ) Clustering = ElemPart
DEALLOCATE( ElemPart )
ELSE
Clustering => ElemPart
NULLIFY( ElemPart )
END IF
DEALLOCATE(NoPart,CellCount)
CALL Info(Caller,'Clustering of elements finished',Level=10)
END SUBROUTINE ClusterElementsUniform
SUBROUTINE ChooseClusterNodes(Amat, Solver, Components, EliminateDir, CF)
USE MeshUtils, ONLY : DetectExtrudedStructure, DetectExtrudedElements
TYPE(Matrix_t), POINTER :: Amat
TYPE(solver_t), TARGET :: Solver
INTEGER :: Components
LOGICAL :: EliminateDir
INTEGER, POINTER :: CF(:)
INTEGER :: matsize, nodesize, Component1
LOGICAL, POINTER :: Bonds(:), Fixed(:),Passive(:)
INTEGER, POINTER :: Cols(:),Rows(:),CFLayer(:)
LOGICAL :: GotIt
TYPE(Solver_t), POINTER :: PSolver
TYPE(Mesh_t), POINTER :: Mesh
INTEGER, POINTER :: MaskPerm(:)
LOGICAL, POINTER :: MaskActive(:)
CHARACTER(:), ALLOCATABLE :: ClusterMethod
ClusterMethod = ListGetString( Solver % Values,'MG Cluster Method',GotIt)
IF(.NOT. GotIt ) ClusterMethod = 'default'
SELECT CASE ( ClusterMethod )
CASE( 'geometric' )
CALL Info('ChooseClusterNodes','Using geometric clustering')
PSolver => Solver
Mesh => Solver % Mesh
MaskPerm => Solver % Variable % Perm
IF( ASSOCIATED( MaskPerm ) ) THEN
ALLOCATE( MaskActive( SIZE( MaskPerm ) ) )
MaskActive = ( MaskPerm > 0 )
END IF
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfNodes ) THEN
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, geometric clustering works only for two levels!')
END IF
CALL ClusterNodesByDirection(Solver % Values,Mesh,CF,MaskActive)
IF( ASSOCIATED( MaskPerm ) ) DEALLOCATE( MaskActive )
CASE( 'unextrude' )
CALL Info('ChooseClusterNodes','Using dimensional reduction of extruded meshes for clustering')
Mesh => Solver % Mesh
MaskPerm => Solver % Variable % Perm
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfNodes ) THEN
PRINT *,'sizes:',Amat % NumberOfRows, Components, Mesh % NumberOfNodes
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, extruded node clustering works only for two levels!')
END IF
CALL ClusterExtrudedMesh()
CASE( 'edge' )
CALL Info('ChooseClusterNodes','Using dimensional edge reduction of extruded edge meshes')
Mesh => Solver % Mesh
MaskPerm => Solver % Variable % Perm
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfEdges ) THEN
PRINT *,'sizes:',Amat % NumberOfRows, Components, Mesh % NumberOfEdges
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, extruded edge clustering works only for two levels!')
END IF
CALL ClusterExtrudedEdges()
CASE( 'hybrid' )
CALL Info('ChooseClusterNodes','Using hybrid clustering method')
Component1 = 1
IF(Components > 1) THEN
Component1 = ListGetInteger(Solver % Values,'MG Determining Component',&
GotIt,minv=1,maxv=Components)
IF(.NOT. GotIt) Component1 = 1
END IF
Rows => Amat % Rows
Cols => Amat % Cols
matsize = Amat % NumberOfRows
nodesize = matsize / Components
ALLOCATE( Bonds(SIZE(Amat % Cols)), Passive(nodesize),Fixed(nodesize), CF(nodesize) )
PSolver => Solver
Mesh => Solver % Mesh
MaskPerm => Solver % Variable % Perm
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfNodes ) THEN
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, geometric clustering works only for two levels!')
END IF
CALL PassiveExtrudedMesh()
CALL CMGBonds(Amat, Bonds, Passive, Fixed, Components,Component1)
CALL CMGClusterForm(Amat, Bonds, Passive, Fixed, Components, Component1, CFLayer)
DEALLOCATE(Bonds, Fixed)
CALL Info('ChooseClusterNodes','Using dimensional reduction of extruded meshes for clustering')
Mesh => Solver % Mesh
MaskPerm => Solver % Variable % Perm
IF( Amat % NumberOfRows /= Components * Mesh % NumberOfNodes ) THEN
CALL Fatal('ChoosClusterNodes','Mismatch in dimensions, extruded clustering works only for two levels!')
END IF
CALL ClusterExtrudedMesh( CFLayer)
CASE DEFAULT
CALL Info('ChooseClusterNodes','Using clustering based on matrix connections')
Component1 = 1
IF(Components > 1) THEN
Component1 = ListGetInteger(Solver % Values,'MG Determining Component',&
GotIt,minv=1,maxv=Components)
IF(.NOT. GotIt) Component1 = 1
END IF
Rows => Amat % Rows
Cols => Amat % Cols
matsize = Amat % NumberOfRows
nodesize = matsize / Components
ALLOCATE( Bonds(SIZE(Amat % Cols)), Passive(nodesize),Fixed(nodesize), CF(nodesize) )
Passive = .FALSE.
CALL SetParallelPassive()
CALL CMGBonds(Amat, Bonds, Passive, Fixed, Components,Component1)
CALL CMGClusterForm(Amat, Bonds, Passive, Fixed, Components, Component1, CF)
DEALLOCATE(Bonds, Fixed)
END SELECT
IF(.FALSE.) CALL Info('ChooseClusterNodes','Clusters chosen')
CONTAINS
SUBROUTINE SetParallelPassive()
INTEGER :: i,j
IF( ParEnv % PEs <= 1 ) RETURN
DO i=1,nodesize
j = Components*(i-1) + Component1
IF( Amat % ParallelInfo % GInterface(j) ) CYCLE
IF( Amat % ParallelInfo % NeighbourList(j) % Neighbours(1) /= ParEnv % Mype ) &
Passive(i) = .TRUE.
END DO
END SUBROUTINE SetParallelPassive
SUBROUTINE PassiveExtrudedMesh()
INTEGER, POINTER :: TopPointer(:)
INTEGER :: i, j, nsize
TYPE(Variable_t), POINTER :: ExtVar
TYPE(Solver_t), POINTER :: Psolver
PSolver => Solver
nsize = SIZE( MaskPerm )
CALL Info('PassiveExrudedMesh','Setting active nodes on a layer only')
CALL DetectExtrudedStructure( Mesh, PSolver, ExtVar, &
TopNodePointer = TopPointer )
DO i=1,nsize
IF( TopPointer(i) == i ) CYCLE
j = MaskPerm( i )
IF( j == 0 ) CYCLE
Passive( j ) = .TRUE.
END DO
i = COUNT( Passive )
PRINT *,'Number of passive nodes:',i
PRINT *,'Number of active nodes:',nsize-i
DEALLOCATE( TopPointer )
END SUBROUTINE PassiveExtrudedMesh
SUBROUTINE ClusterExtrudedMesh( CFLayer )
INTEGER, POINTER, OPTIONAL :: CFLayer(:)
INTEGER, POINTER :: TopPointer(:), DownPointer(:)
INTEGER :: i, j, k, ind, nsize, NoLayers, cluster, NoClusters, ClusterSize
TYPE(Variable_t), POINTER :: ExtVar
TYPE(Solver_t), POINTER :: Psolver
PSolver => Solver
nsize = SIZE( MaskPerm )
ALLOCATE( CF( nsize) )
CF = 0
ClusterSize = ListGetInteger(Solver % Values,'MG Cluster Size',GotIt)
CALL DetectExtrudedStructure( Mesh, PSolver, ExtVar, &
TopNodePointer = TopPointer, DownNodePointer = DownPointer, &
NumberOfLayers = NoLayers )
IF( PRESENT( CFLayer ) ) THEN
NoClusters = MAXVAL( CFLayer )
CF = CFLayer
ELSE
NoClusters = 0
DO i=1,nsize
IF( TopPointer(i) == i ) THEN
NoClusters = NoClusters + 1
CF( MaskPerm(i) ) = NoClusters
END IF
END DO
END IF
DO i=1,nsize
IF( TopPointer(i) == 0 ) CYCLE
IF( TopPointer(i) == i ) THEN
k = 1
ind = i
cluster = CF( MaskPerm(ind) )
DO j = 1,NoLayers
ind = DownPointer(ind)
IF( k == ClusterSize ) THEN
k = 0
cluster = cluster + NoClusters
END IF
k = k + 1
CF( MaskPerm(ind) ) = cluster
END DO
END IF
END DO
DEALLOCATE( DownPointer, TopPointer )
END SUBROUTINE ClusterExtrudedMesh
SUBROUTINE ClusterExtrudedEdges( CFLayer )
INTEGER, POINTER, OPTIONAL :: CFLayer(:)
INTEGER, POINTER :: TopPointer(:), BotPointer(:)
INTEGER :: i, j, k, j2, nsize, NoLayers, NoClusters
TYPE(Variable_t), POINTER :: ExtVar
TYPE(Solver_t), POINTER :: Psolver
TYPE(Mesh_t), POINTER :: Mesh
TYPE(Element_t), POINTER :: Element, Element2
INTEGER, POINTER :: Indexes(:), Indexes2(:), Perm(:), CFPerm(:)
INTEGER :: Indx, elem, t, t2, dofs, n, n2, m, m2, n0
INTEGER :: Ncount(5)
PSolver => Solver
Mesh => Solver % Mesh
Perm => Solver % Variable % Perm
dofs = Solver % Variable % DOFs
nsize = Solver % Matrix % NumberOfRows / dofs
n0 = Mesh % NumberOfNodes
ALLOCATE( CF( nsize), CFPerm(nsize) )
CF = 0
CFPerm = 0
Ncount = 0
CALL DetectExtrudedElements( Mesh, PSolver, ExtVar, &
TopElemPointer = TopPointer, BotElemPointer = BotPointer, &
NumberOfLayers = NoLayers )
DO elem=1,Solver % NumberOfActiveElements
t = Solver % ActiveElements(elem)
t2 = BotPointer( t )
IF( t2 == t ) THEN
Ncount(1) = Ncount(1) + 1
CYCLE
ELSE
NCount(2) = Ncount(2) +1
END IF
Element => Mesh % Elements( t )
n = Element % TYPE % NumberOFEdges
Indexes => Element % EdgeIndexes
Element2 => Mesh % Elements( t2 )
n2 = Element2 % TYPE % NumberOFEdges
Indexes2 => Element2 % EdgeIndexes
m = Element % TYPE % NumberOFNodes
m2 = Element2 % TYPE % NumberOFNodes
IF( n /= n2 ) CALL Fatal('ClusterExtrudeEdges','Cannot map different number of dofs!')
DO i=1,n
j = Perm(Indexes(i))
j2 = Perm(Indexes2(i))
IF( CF(j) /= j2 ) THEN
Ncount(3) = Ncount(3) + 1
CF(j) = j2
ELSE
Ncount(4) = Ncount(4) + 1
END IF
END DO
END DO
CFPerm = 0
DO i=1,nsize
CFPerm( CF(i) ) = 1
END DO
k = 0
DO i=1,nsize
IF( CFPerm(i) > 0 ) THEN
k = k + 1
CFPerm(i) = k
END IF
END DO
CALL Info('ClusterExtrudedEdges',&
'Number of reduced dofs: '//I2S(k)//' vs. '//I2S(nsize),Level=9)
WRITE(Message,'(A,F10.3)') 'Coarse dofs reduction factor',1.0_dp * nsize / k
CALL Info('ClusterExtrudedEdges', Message, Level=7)
DO i=1,nsize
CF(i) = CFPerm(i)
END DO
PRINT *,'Ncount:',Ncount(1:4)
DEALLOCATE( BotPointer, TopPointer )
END SUBROUTINE ClusterExtrudedEdges
SUBROUTINE CMGBonds(Amat, Bonds, Passive, Fixed, Components, Component1)
LOGICAL, POINTER :: Bonds(:), Passive(:), Fixed(:)
TYPE(Matrix_t), POINTER :: Amat
INTEGER :: Components, Component1
REAL(KIND=dp) :: StrongLim
INTEGER :: nods, cnods, matsize, nodesize, maxconn, newbonds, MaxConns, MinConns
INTEGER :: i,j,k,cj,ci,no,elimnods,strongbonds,nodeind,matind
INTEGER, POINTER :: Cols(:),Rows(:),localind(:)
LOGICAL :: ElimDir, Choose, GotIt
REAL(KIND=dp), POINTER :: Values(:), measures(:)
REAL(KIND=dp) :: maxbond, diagbond, dirlim, meas
StrongLim = ListGetConstReal(Solver % Values,'MG Strong Connection Limit',GotIt)
IF(.NOT. GotIt) StrongLim = 0.06
MaxConns = ListGetInteger(Solver % Values,'MG Strong Connection Maximum',GotIt)
MinConns = ListGetInteger(Solver % Values,'MG Strong Connection Minimum',GotIt)
ElimDir = EliminateDir
DirLim = ListGetConstReal(Solver % Values,'MG Eliminate Dirichlet Limit',GotIt)
IF(.NOT. GotIt) DirLim = 1.0d-8
matsize = Amat % NumberOfRows
Rows => Amat % Rows
Cols => Amat % Cols
Values => Amat % Values
nodesize = matsize / Components
maxconn = 0
DO matind=1,matsize
maxconn = MAX(maxconn,Rows(matind+1)-Rows(matind))
END DO
ALLOCATE(measures(maxconn),localind(maxconn))
Fixed = .FALSE.
Bonds = .FALSE.
strongbonds = 0
elimnods = 0
DO nodeind=1,nodesize
IF( Passive( nodeind ) ) CYCLE
matind = (nodeind-1)*Components + Component1
no = 0
maxbond = 0.0d0
DO j=Rows(matind),Rows(matind+1)-1
cj = Cols(j)
IF( MOD(matind, Components) /= MOD(cj, Components) ) CYCLE
k = ( cj - 1 )/Components + 1
IF( Passive( k ) ) CYCLE
IF(cj /= matind ) THEN
no = no + 1
localind(no) = j
measures(no) = ABS( Values(j) )
ELSE
diagbond = ABS(Values(j))
END IF
END DO
IF(no > 0) maxbond = MAXVAL( measures(1:no) )
IF( ElimDir .AND. maxbond < DirLim * diagbond ) THEN
Fixed(nodeind) = .TRUE.
elimnods = elimnods + 1
CYCLE
END IF
IF(no == 0) CYCLE
Choose = .FALSE.
IF(MaxConns > 0 .OR. MinConns > 0) THEN
newbonds = 0
DO i=1,no
IF( measures(i) >= Stronglim * maxbond) newbonds = newbonds + 1
END DO
IF(MaxConns > 0 .AND. newbonds > MaxConns) Choose = .TRUE.
IF(MinConns > 0 .AND. newbonds < MinConns) Choose = .TRUE.
END IF
IF(.NOT. Choose) THEN
newbonds = 0
DO i=1,no
IF( measures(i) >= StrongLim * maxbond) THEN
newbonds = newbonds + 1
Bonds(LocalInd(i)) = .TRUE.
END IF
END DO
strongbonds = strongbonds + newbonds
ELSE
CALL SortR(no,LocalInd,measures)
IF(MaxConns > 0 .AND. newbonds > MaxConns) THEN
strongbonds = strongbonds + MaxConns
DO i=1,MaxConns
Bonds(LocalInd(i)) = .TRUE.
END DO
END IF
IF(MinConns > 0 .AND. newbonds < MinConns) THEN
no = MIN(MinConns, no)
strongbonds = strongbonds + no
DO i=1,no
Bonds(LocalInd(i)) = .TRUE.
END DO
END IF
END IF
END DO
DEALLOCATE(measures, localind)
IF(elimnods > 0) THEN
WRITE(Message,'(A,I8)') 'Number of eliminated nodes',elimnods
CALL Info('CMGBonds',Message)
END IF
WRITE(Message,'(A,F8.3)') 'Average number of strong bonds',&
1.0*strongbonds/nodesize
CALL Info('CMGBonds',Message)
END SUBROUTINE CMGBonds
SUBROUTINE CMGClusterForm(Amat, Bonds, Passive, Fixed, Components, Component1, CF)
TYPE(Matrix_t), POINTER :: Amat
INTEGER :: Components, Component1
LOGICAL :: Bonds(:),Passive(:),Fixed(:)
INTEGER, POINTER :: CF(:)
INTEGER :: nods, cnods
INTEGER :: i,j,k,l,n,k2,cj,ci,nodeci,nodecj, &
MaxCon, MaxConInd, RefCon, cind, find, rind, &
GatMax, anods, points, neworder, oldorder, nodesize,matsize,&
nextind, sumcon, maxsumcon, ClusterSize, nbonds, &
nsize, nodeind, nodeind2, matind, BoundPoints, ClusterPoints, &
ConnectionPoints, ClusterMode, HalfSize
INTEGER :: LocalCon(100), LocalInd(100)
REAL(KIND=dp) :: LocalVal(100),LocalVal2(100), maxv
INTEGER, POINTER :: Con(:)
INTEGER, POINTER :: Cols(:),Rows(:)
INTEGER, ALLOCATABLE :: GatLims(:), GatNos(:), ConInd(:), RevConInd(:)
REAL(KIND=dp), POINTER :: Values(:)
LOGICAL :: ClusterOrphans, OrphansBest, ClusterGrow, hit, GotIt
BoundPoints = ListGetInteger(Solver % Values,'MG Boundary Priority',GotIt)
IF(.NOT. GotIt) BoundPoints = 1
ClusterPoints = ListGetInteger(Solver % Values,'MG Cluster Priority',GotIt)
IF(.NOT. GotIt) ClusterPoints = MIN(1,BoundPoints+1)
ConnectionPoints = ListGetInteger(Solver % Values,'MG Connection Priority',GotIt)
IF(.NOT. GotIt) ConnectionPoints = 1
ClusterSize = ListGetInteger(Solver % Values,'MG Cluster Size',GotIt)
HalfSize = ListGetInteger(Solver % Values,'MG Cluster Half Size',GotIt)
IF(.NOT. GotIt) HalfSize = (ClusterSize-1)/2
ClusterOrphans = ListGetLogical(Solver % Values,'MG Cluster Orphans',GotIt)
IF(.NOT. GotIt) ClusterOrphans = .TRUE.
OrphansBest = ListGetLogical(Solver % Values,'MG Cluster Orphans Best',GotIt)
ClusterGrow = ListGetLogical(Solver % Values,'MG Cluster Grow',GotIt)
IF(ClusterSize == 0) THEN
ClusterMode = 0
ELSE IF(ClusterSize <= 2) THEN
ClusterMode = 1
ELSE
ClusterMode = 2
END IF
matsize = Amat % NumberOfRows
nodesize = matsize / Components
Rows => Amat % Rows
Cols => Amat % Cols
Values => Amat % Values
CF = 0
ALLOCATE(Con(nodesize))
Con = 0
cnods = 0
cind = 0
DO nodeind = 1, nodesize
IF( Passive( nodeind ) ) CYCLE
IF(Fixed(nodeind)) THEN
Con(nodeind) = 0
ELSE
Con(nodeind) = 1
END IF
END DO
DO nodeind = 1, nodesize
IF( Passive(nodeind) ) CYCLE
IF(CF(nodeind) > 0) THEN
matind = Components*(nodeind-1) + Component1
DO i=Rows(matind),Rows(matind+1)-1
IF(.NOT. Bonds(i)) CYCLE
ci = Cols(i)
nodeci = (ci-Component1) / Components + 1
IF( PASSIVE(nodeci) ) CYCLE
IF(.NOT. Fixed(nodeci) .AND. CF(nodeci) == 0) THEN
Con(nodeci) = Con(nodeci) + ClusterPoints
END IF
END DO
END IF
END DO
IF(BoundPoints /= 0) THEN
DO nodeind = 1, nodesize
IF(Passive(nodeind)) CYCLE
IF(Fixed(nodeind)) CYCLE
matind = Components*(nodeind-1) + Component1
DO i=Rows(matind),Rows(matind+1)-1
IF(.NOT. Bonds(i)) CYCLE
ci = Cols(i)
nodeci = (ci-Component1) / Components + 1
IF(Passive(nodeci)) CYCLE
IF(Fixed(nodeci)) THEN
Con(nodeind) = Con(nodeind) + BoundPoints
END IF
END DO
END DO
END IF
MaxCon = MAXVAL( Con )
GatMax = 2 * MaxCon + 100
ALLOCATE( GatLims(GatMax), GatNos(GatMax) )
GatLims = 0
GatNos = 0
DO nodeind = 1, nodesize
IF(Passive(nodeind)) CYCLE
IF(Con(nodeind) == 0) CYCLE
GatNos(Con(nodeind)) = GatNos(Con(nodeind)) + 1
END DO
anods = SUM(GatNos)
DO nodeind = 2, MaxCon
GatLims(nodeind) = GatLims(nodeind-1) + GatNos(nodeind-1)
END DO
ALLOCATE( ConInd(nodesize), RevConInd(anods) )
ConInd = 0
RevConInd = 0
GatNos = 0
DO nodeind = 1, nodesize
IF(Passive(nodeind)) CYCLE
IF(Con(nodeind) == 0) CYCLE
GatNos(Con(nodeind)) = GatNos(Con(nodeind)) + 1
ConInd(nodeind) = GatNos(Con(nodeind)) + GatLims(Con(nodeind))
END DO
RevConInd = 0
DO nodeind = 1, nodesize
IF(Passive(nodeind)) CYCLE
IF(Con(nodeind) == 0) CYCLE
RevConInd(ConInd(nodeind)) = nodeind
END DO
DO WHILE( MaxCon > 0 )
nodeind = RevConInd(anods)
IF(ClusterSize /= 0) THEN
n = 0
nbonds = 0
matind = Components*(nodeind-1)+Component1
DO j=Rows(matind),Rows(matind+1)-1
IF(Bonds(j)) THEN
cj = Cols(j)
nodecj = (cj-Component1) / Components + 1
IF( Passive(nodecj) ) CYCLE
IF( Fixed(nodecj) ) CYCLE
nbonds = nbonds + 1
IF(CF(nodecj) == 0) THEN
n = n + 1
LocalCon(n) = Con(nodecj)
LocalInd(n) = nodecj
LocalVal(n) = ABS( Values(j) )
END IF
END IF
END DO
IF( n > ClusterSize - 1 ) THEN
maxv = MAXVAL(LocalVal(1:n))
LocalVal(1:n) = 0.999 * ConnectionPoints * LocalVal(1:n) / maxv + LocalCon(1:n)
k = HalfSize
IF( HalfSize > 0) THEN
CALL SortR(n,LocalInd,LocalVal)
END IF
IF( ClusterMode == 2 ) THEN
k = HalfSize
IF(k > 0) LocalVal(1:k) = LocalVal(1:k) + 1000.0d0
IF(.FALSE.) LocalVal(k+1:n) = LocalVal(k+1:n) + 10.0d0
DO l=1,k
nodeind2 = LocalInd(l)
matind = Components*(nodeind2-1)+Component1
DO j=Rows(matind),Rows(matind+1)-1
IF(Bonds(j)) THEN
cj = Cols(j)
nodecj = (cj-Component1) / Components + 1
IF(Passive(nodecj)) CYCLE
DO k2=k+1,n
IF( LocalInd(k2) == nodecj) THEN
LocalVal(k2) = LocalVal(k2) + 10.0d0
EXIT
END IF
END DO
IF(ClusterGrow .AND. k2 > n) THEN
n=n+1
LocalInd(n) = nodecj
LocalVal(n) = 10.0d0
END IF
END IF
END DO
END DO
END IF
CALL SortR(n,LocalInd,LocalVal)
n = ClusterSize - 1
END IF
IF( ClusterOrphans .AND. n == 0 .AND. nbonds > 0) THEN
maxv = 0.0d0
DO j=Rows(matind),Rows(matind+1)-1
IF(Bonds(j)) THEN
cj = Cols(j)
nodecj = (cj-Component1) / Components + 1
IF( Passive(nodecj) ) CYCLE
IF( Fixed(nodecj) ) CYCLE
k = CF(nodecj)
IF( k > 0) THEN
IF( ABS(Values(j)) > maxv) THEN
maxv = ABS( Values(j) )
rind = k
END IF
END IF
END IF
END DO
IF( OrphansBest .AND. nbonds > 2) THEN
LocalInd(1:nbonds) = 0
LocalVal(1:nbonds) = 0.0_dp
DO j=Rows(matind),Rows(matind+1)-1
IF(Bonds(j)) THEN
cj = Cols(j)
nodecj = (cj-Component1) / Components + 1
IF( Passive(nodecj) ) CYCLE
IF( Fixed(nodecj) ) CYCLE
k = CF(nodecj)
IF( k > 0) THEN
hit = .FALSE.
DO k2=1,n
IF( LocalInd(k2) == k) THEN
hit = .TRUE.
EXIT
END IF
END DO
IF(.NOT. hit) THEN
n = n + 1
k2 = n
LocalInd(k2) = k
END IF
LocalVal(k2) = LocalVal(k2) + ABS(Values(j)) / maxv + 0.0_dp
END IF
END IF
END DO
maxv = 0.0_dp
DO k=1,n
IF( LocalVal(k) > maxv) THEN
maxv = LocalVal(k)
k2 = LocalInd(k)
END IF
END DO
n = 0
rind = k2
END IF
ELSE
cind = cind + 1
rind = cind
END IF
DO j=0,n
IF(j==0) THEN
nodecj = nodeind
ELSE
nodecj = LocalInd(j)
END IF
CF(nodecj) = rind
cnods = cnods + 1
cj = Components*(nodecj-1) + Component1
DO i=Rows(cj),Rows(cj+1)-1
IF(.NOT. Bonds(i)) CYCLE
ci = Cols(i)
nodeci = (ci-Component1)/Components + 1
IF( Passive( nodeci) ) CYCLE
IF( Fixed(nodeci) ) CYCLE
IF( CF(nodeci) /= 0) CYCLE
DO l=1,ClusterPoints
points = Con(nodeci) + 1
oldorder = ConInd(nodeci)
IF(GatLims(points) == 0) GatLims(points) = anods
neworder = GatLims(points)
Con(nodeci) = points
GatLims(points) = GatLims(points) - 1
IF(neworder /= oldorder) THEN
k = RevConInd(neworder)
ConInd(nodeci) = neworder
ConInd(k) = oldorder
RevConInd(neworder) = nodeci
RevConInd(oldorder) = k
END IF
END DO
END DO
END DO
END IF
IF(ClusterSize == 0) THEN
sumcon = 0
nbonds = 0
matind = Components*(nodeind-1) + Component1
DO j=Rows(matind),Rows(matind+1)-1
IF(.NOT. Bonds(j)) CYCLE
cj = Cols(j)
nodecj = (cj-Component1)/Components + 1
nbonds = nbonds + 1
IF(CF(nodecj) == 0) sumcon = sumcon + Con(nodecj)
END DO
maxsumcon = sumcon
nextind = nodeind
IF(ClusterOrphans .AND. sumcon == 0 .AND. nbonds > 0) THEN
maxv = 0.0d0
DO j=Rows(matind),Rows(matind+1)-1
IF(.NOT. Bonds(j)) CYCLE
cj = Cols(j)
nodecj = (cj-Component1)/Components + 1
IF( Fixed(nodecj) ) CYCLE
IF( CF(nodecj) > 0) THEN
IF( ABS(Values(j)) > maxv) THEN
maxv = ABS( Values(j) )
rind = CF(nodecj)
END IF
END IF
END DO
ELSE
nbonds = 0
DO j=Rows(matind),Rows(matind+1)-1
IF(.NOT. Bonds(j)) CYCLE
sumcon = 0
cj = Cols(j)
nodecj = (cj-Component1)/Components + 1
IF(.NOT. Fixed(nodecj) .AND. CF(nodecj) == 0) THEN
DO i=Rows(cj),Rows(cj+1)-1
IF(Bonds(i)) THEN
ci = Cols(i)
nodeci = (ci-Component1)/Components + 1
IF(CF(nodeci) == 0) sumcon = sumcon + Con(nodeci)
END IF
END DO
END IF
IF(sumcon > maxsumcon) THEN
maxsumcon = sumcon
nextind = nodecj
END IF
END DO
cind = cind + 1
rind = cind
END IF
nodeind = nextind
CF(nodeind) = rind
cnods = cnods + 1
matind = Components*(nodeind-1) + Component1
DO j=Rows(matind),Rows(matind+1)-1
IF(.NOT. Bonds(j)) CYCLE
cj = Cols(j)
nodecj = (cj-Component1)/Components + 1
IF( Passive(nodecj) ) CYCLE
IF(CF(nodecj) == 0) THEN
CF(nodecj) = cind
cnods = cnods + 1
IF(Fixed(nodecj)) CYCLE
DO i=Rows(cj),Rows(cj+1)-1
IF(.NOT. Bonds(i)) CYCLE
ci = Cols(i)
nodeci = (ci-Component1)/Components + 1
IF( Passive(nodeci) ) CYCLE
IF( Fixed(nodeci) ) CYCLE
IF( CF(nodeci) /= 0) CYCLE
DO l=1,ClusterPoints
points = Con(nodeci) + 1
oldorder = ConInd(nodeci)
IF(GatLims(points) == 0) GatLims(points) = anods
neworder = GatLims(points)
Con(nodeci) = points
GatLims(points) = GatLims(points) - 1
IF(neworder /= oldorder) THEN
k = RevConInd(neworder)
ConInd(nodeci) = neworder
ConInd(k) = oldorder
RevConInd(neworder) = nodeci
RevConInd(oldorder) = k
END IF
END DO
END DO
END IF
END DO
END IF
Refcon = 0
DO WHILE( anods > 0)
IF (CF(RevConInd(anods)) == 0) EXIT
MaxCon = Con(RevConInd(anods))
Refcon = MAX(MaxCon,RefCon)
anods = anods - 1
Refcon = MAX(MaxCon,RefCon)
END DO
IF(anods > 0) THEN
MaxCon = Con(RevConInd(anods))
RefCon = MAX(MaxCon,RefCon)
GatLims((MaxCon+1):(Refcon+1)) = 0
ELSE
MaxCon = 0
END IF
END DO
DEALLOCATE(Con, GatLims, GatNos, ConInd, RevConInd)
WRITE(Message,'(A,I8)') 'Number of clusters',cind
CALL Info('CMGClusterForm',Message)
WRITE(Message,'(A,F8.3)') 'Average size of clusters',1.0*cnods/cind
CALL Info('CMGClusterForm',Message)
END SUBROUTINE CMGClusterForm
END SUBROUTINE ChooseClusterNodes
END MODULE ClusteringMethods