From ea00273c5fd5364070dc6424c48774f1ca66a154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Thu, 20 Jun 2024 23:10:55 +0200 Subject: [PATCH 1/7] correction of node partitioning --- src/partitioning.jl | 66 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/partitioning.jl b/src/partitioning.jl index c563066f..85426829 100644 --- a/src/partitioning.jl +++ b/src/partitioning.jl @@ -150,7 +150,7 @@ end Return range of nodes belonging to a given partition. """ function partition_nodes(grid, part) - partnodess=grid[PartitionNodes] + partnodes=grid[PartitionNodes] partnodes[part]:partnodes[part+1]-1 end @@ -468,8 +468,67 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},nc; trivial=false end end end - - + + + partcolors=zeros(Int,num_partitions(grid)) + for col in pcolors(grid) + for part in pcolor_partitions(grid,col) + partcolors[part]=col + end + end + + # Correct situation where a node is a neighbor of + # two different partitions of the same color + # which would lead to clashes in the matrix-vector product + cn = grid[CellNodes] + nc = asparse(atranspose(grid[CellNodes])) + rv=SparseArrays.getrowval(nc) + while true + # Detect the situation, record the corresponding + # pairs of partitions + idpart=Pair{Int,Int}[] + for inode=1:num_nodes(grid) + ipart=nodepartitions[inode] + icol=partcolors[ipart] + for j in nzrange(nc,inode) + icell=rv[j] + for k=1:size(cn,1) + knode=cn[k,icell] + for l in nzrange(nc,knode) + lcell=rv[l] + for m =1:size(cn,1) + mnode=cn[m,lcell] + mpart=nodepartitions[mnode] + mcol=partcolors[mpart] + if (mpart != ipart) && (mcol == icol) + if ipart < mpart + push!(idpart, ipart=>mpart) + else + push!(idpart, mpart=>ipart) + end + end + end + end + end + end + end + idpart=unique(idpart) + # We are done if no such case is left + if length(idpart)==0 + break + end + @show idpart + # Re-assign the respective lower partition numbers for the problem cases. + for inode=1:num_nodes(grid) + part=nodepartitions[inode] + for id in idpart + if part==id[2] + nodepartitions[inode]=id[1] + end + end + end + end + # Create node permutation such that # all nodes belonging to one partition # are contiguous @@ -512,6 +571,7 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},nc; trivial=false if keep_nodepermutation grid[NodePermutation]=nodeperm end + @show [length(partition_nodes(grid,ipart)) for ipart=1:num_partitions(grid)] grid end From b96a9f8bb26cf38e14717156da7480bee09fe984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Fri, 21 Jun 2024 11:24:31 +0200 Subject: [PATCH 2/7] slight reorganization, more comprehensive tests --- ext/ExtendableGridsMetisExt.jl | 11 ++-- src/partitioning.jl | 81 ++++++++++++++++------------ test/partitioning.jl | 97 ++++++++++++++++------------------ 3 files changed, 98 insertions(+), 91 deletions(-) diff --git a/ext/ExtendableGridsMetisExt.jl b/ext/ExtendableGridsMetisExt.jl index c820e0ed..fd408f12 100644 --- a/ext/ExtendableGridsMetisExt.jl +++ b/ext/ExtendableGridsMetisExt.jl @@ -1,11 +1,11 @@ module ExtendableGridsMetisExt import Metis -import ExtendableGrids: partition, partgraph, induce_node_partitioning!, reorder_cells +import ExtendableGrids: dopartition, partgraph, induce_node_partitioning!, reorder_cells using ExtendableGrids, Graphs -function partition(grid::ExtendableGrid{Tc,Ti}, alg::PlainMetisPartitioning) where {Tc,Ti} +function dopartition(grid::ExtendableGrid{Tc,Ti}, alg::PlainMetisPartitioning) where {Tc,Ti} nn = num_nodes(grid) ncells = num_cells(grid) cn = asparse(grid[CellNodes]) @@ -39,12 +39,11 @@ function partition(grid::ExtendableGrid{Tc,Ti}, alg::PlainMetisPartitioning) whe end pgrid=reorder_cells(grid,cellpartitions,ncellpartitions,colpart) - induce_node_partitioning!(pgrid,nc; trivial=!alg.partition_nodes, keep_nodepermutation=alg.keep_nodepermutation) + induce_node_partitioning!(pgrid,cn,nc; trivial=!alg.partition_nodes, keep_nodepermutation=alg.keep_nodepermutation) end function coloredpartition(cc; npart = 4, depth = 0, maxdepth=4, separatorwidth=2) - @info "depth=$depth" mg = Metis.graph(cc) Metis.options[Int(Metis.METIS_OPTION_CCORDER)+1] = 1 @@ -133,14 +132,14 @@ function coloredpartition(cc; npart = 4, depth = 0, maxdepth=4, separatorwidth=2 end -function partition(grid::ExtendableGrid{Tc,Ti}, alg::RecursiveMetisPartitioning) where {Tc,Ti} +function dopartition(grid::ExtendableGrid{Tc,Ti}, alg::RecursiveMetisPartitioning) where {Tc,Ti} cn = asparse(grid[CellNodes]) nc = asparse(atranspose(grid[CellNodes])) cc = nc * cn cellpartitions, colpart = coloredpartition(cc; npart=alg.npart, maxdepth=alg.maxdepth, separatorwidth=alg.separatorwidth) ncellpartitions=maximum(cellpartitions) pgrid=reorder_cells(grid,cellpartitions,ncellpartitions,colpart) - induce_node_partitioning!(pgrid,nc; trivial=!alg.partition_nodes, keep_nodepermutation=alg.keep_nodepermutation) + induce_node_partitioning!(pgrid,cn,nc; trivial=!alg.partition_nodes, keep_nodepermutation=alg.keep_nodepermutation) end diff --git a/src/partitioning.jl b/src/partitioning.jl index 85426829..3a3d4bf2 100644 --- a/src/partitioning.jl +++ b/src/partitioning.jl @@ -173,6 +173,15 @@ function num_cells_per_color(grid) [sum( p->length(partition_cells(grid,p)), pcolor_partitions(grid, col)) for col in pcolors(grid)] end +""" + $(SIGNATURES) + +Return a vector containing the number of nodes for each of +the partitions of the grid partitioning. +""" +function num_nodes_per_partition(grid) + @show [length(partition_nodes(grid,ipart)) for ipart=1:num_partitions(grid)] +end """ check_partitioning(grid; @@ -318,13 +327,6 @@ Trivial partitioning: all grid cells belong to single partition number 1. Base.@kwdef struct TrivialPartitioning <: AbstractPartitioningAlgorithm end -function partition(grid::ExtendableGrid{Tc,Ti}, ::TrivialPartitioning) where {Tc,Ti} - pgrid=ExtendableGrid{Tc,Ti}() - for (k,v) in pairs(grid.components) - pgrid.components[k]=v - end - trivial_partitioning!(pgrid) -end """ @@ -388,10 +390,12 @@ Base.@kwdef struct RecursiveMetisPartitioning <: AbstractPartitioningAlgorithm keep_nodepermutation::Bool=true end + + """ $(SIGNATURES) -(internal) +(Internal utility function) Create cell permutation such that all cells belonging to one partition are contiguous and reorder return grid with reordered cells. """ @@ -441,7 +445,7 @@ number is taken. This algorithm does not always fulfill the following condition (checked among others by [`check_partitioning`](@ref) with `cellpartonly=false`): There is no node which is neigbour of nodes from two different partition with the same color. """ -function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},nc; trivial=false, keep_nodepermutation=true) where {Tc, Ti} +function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},cn,nc; trivial=false, keep_nodepermutation=true) where {Tc, Ti} partcells=grid[PartitionCells] nnodepartitions=length(partcells)-1 if trivial @@ -480,9 +484,8 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},nc; trivial=false # Correct situation where a node is a neighbor of # two different partitions of the same color # which would lead to clashes in the matrix-vector product - cn = grid[CellNodes] - nc = asparse(atranspose(grid[CellNodes])) - rv=SparseArrays.getrowval(nc) + nn=cn*nc + rv=SparseArrays.getrowval(nn) while true # Detect the situation, record the corresponding # pairs of partitions @@ -490,23 +493,17 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},nc; trivial=false for inode=1:num_nodes(grid) ipart=nodepartitions[inode] icol=partcolors[ipart] - for j in nzrange(nc,inode) - icell=rv[j] - for k=1:size(cn,1) - knode=cn[k,icell] - for l in nzrange(nc,knode) - lcell=rv[l] - for m =1:size(cn,1) - mnode=cn[m,lcell] - mpart=nodepartitions[mnode] - mcol=partcolors[mpart] - if (mpart != ipart) && (mcol == icol) - if ipart < mpart - push!(idpart, ipart=>mpart) - else - push!(idpart, mpart=>ipart) - end - end + for j in nzrange(nn,inode) + jnode=rv[j] + for m in nzrange(nn,jnode) + mnode=rv[m] + mpart=nodepartitions[mnode] + mcol=partcolors[mpart] + if (mpart != ipart) && (mcol == icol) + if ipart > mpart + push!(idpart, ipart=>mpart) + else + push!(idpart, mpart=>ipart) end end end @@ -517,13 +514,13 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},nc; trivial=false if length(idpart)==0 break end - @show idpart + @info "Correcting node partitions: $idpart" # Re-assign the respective lower partition numbers for the problem cases. for inode=1:num_nodes(grid) part=nodepartitions[inode] for id in idpart - if part==id[2] - nodepartitions[inode]=id[1] + if part==id[1] + nodepartitions[inode]=id[2] end end end @@ -571,10 +568,11 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},nc; trivial=false if keep_nodepermutation grid[NodePermutation]=nodeperm end - @show [length(partition_nodes(grid,ipart)) for ipart=1:num_partitions(grid)] grid end + + """ partition(grid, alg::AbstractPartitioningAlgorithm) @@ -583,6 +581,16 @@ of partitions is colored in such a way, that all partitions with a given color can be worked on in parallel. """ function partition(grid::ExtendableGrid, alg::AbstractPartitioningAlgorithm) + dopartition(grid,alg) +end + +""" + $(SIGNATURES) + +(Internal utility function) +Core method for partitioning +""" +function dopartition(grid::ExtendableGrid, alg::AbstractPartitioningAlgorithm) if isa(alg,PlainMetisPartitioning) error("Import Metis.jl to allow Metis based partitioning") else @@ -590,3 +598,10 @@ function partition(grid::ExtendableGrid, alg::AbstractPartitioningAlgorithm) end end +function dopartition(grid::ExtendableGrid{Tc,Ti}, ::TrivialPartitioning) where {Tc,Ti} + pgrid=ExtendableGrid{Tc,Ti}() + for (k,v) in pairs(grid.components) + pgrid.components[k]=v + end + trivial_partitioning!(pgrid) +end diff --git a/test/partitioning.jl b/test/partitioning.jl index bd3cde5a..44f790ac 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -1,59 +1,52 @@ import Metis @testset "partitioning" begin - for dim in 1:3 - @info "Test partitioning for dim=$(dim)" - X=0:0.1:10 - if dim==3 - X=0:0.5:10 - end - - XXX=(X for i=1:dim) - grid1=simplexgrid(XXX...) - grid2=partition(grid1,TrivialPartitioning()) - grid3=partition(grid1,PlainMetisPartitioning(npart=1)) - grid3=partition(grid1,RecursiveMetisPartitioning(npart=1)) - - - for grid in (grid1, grid2, grid3) - @test num_pcolors(grid)==1 - @test num_partitions(grid)==1 - @test pcolors(grid) == 1:1 - @test pcolor_partitions(grid,1) == 1:1 - @test partition_cells(grid,1) == 1:num_cells(grid) - @test num_partitions_per_color(grid) == [1] - @test check_partitioning(grid) - end - - # METIS has different results depending on the OS... - for npart in [10,20] - grid4=partition(grid1,PlainMetisPartitioning(npart=npart)) - @test num_pcolors(grid4) > 1 - @test num_partitions(grid4)==npart - @test pcolors(grid4) |> length >0 - @test partition_cells(grid4,1) |> length >0 - @test num_partitions_per_color(grid4) |> length >0 - @test grid4[Coordinates][:,grid4[NodePermutation]]≈grid1[Coordinates] - @test try - check_partitioning(grid4) - catch err - if dim==3 && npart==20 - println("Catched partitioning error: Ok") - true - else - false - end + for h in [0.4,0.5,0.6] + for dim in 1:3 + @info "Test partitioning for dim=$(dim)" + X=0:0.1:10 + if dim==3 + X=0:h:10 + end + + XXX=(X for i=1:dim) + grid1=simplexgrid(XXX...) + grid2=partition(grid1,TrivialPartitioning()) + grid3=partition(grid1,PlainMetisPartitioning(npart=1)) + grid3=partition(grid1,RecursiveMetisPartitioning(npart=1)) + + + for grid in (grid1, grid2, grid3) + @test num_pcolors(grid)==1 + @test num_partitions(grid)==1 + @test pcolors(grid) == 1:1 + @test pcolor_partitions(grid,1) == 1:1 + @test partition_cells(grid,1) == 1:num_cells(grid) + @test num_partitions_per_color(grid) == [1] + @test check_partitioning(grid, verbose=false) + end + + # METIS has different results depending on the OS... + for npart in [10, 15, 20] + grid4=partition(grid1,PlainMetisPartitioning(npart=npart)) + @test num_pcolors(grid4) > 1 + @test num_partitions(grid4)==npart + @test pcolors(grid4) |> length >0 + @test partition_cells(grid4,1) |> length >0 + @test num_partitions_per_color(grid4) |> length >0 + @test grid4[Coordinates][:,grid4[NodePermutation]]≈grid1[Coordinates] + @test check_partitioning(grid4, verbose=false) + end + for npart in [3,4,5,6] + grid4=partition(grid1,RecursiveMetisPartitioning(npart=npart)) + @test num_pcolors(grid4) > 1 + @test num_partitions(grid4)>npart + @test pcolors(grid4) |> length >0 + @test partition_cells(grid4,1) |> length >0 + @test num_partitions_per_color(grid4) |> length >0 + @test grid4[Coordinates][:,grid4[NodePermutation]]≈grid1[Coordinates] + @test check_partitioning(grid4, verbose=false) end - end - for npart in [3] - grid4=partition(grid1,RecursiveMetisPartitioning(npart=npart)) - @test num_pcolors(grid4) > 1 - @test num_partitions(grid4)>npart - @test pcolors(grid4) |> length >0 - @test partition_cells(grid4,1) |> length >0 - @test num_partitions_per_color(grid4) |> length >0 - @test grid4[Coordinates][:,grid4[NodePermutation]]≈grid1[Coordinates] - @test check_partitioning(grid4) end end end From c96c3e600028fa03842d60e0c103629da5d13886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Fri, 21 Jun 2024 16:47:17 +0200 Subject: [PATCH 3/7] Add trivial BFace partitioning --- docs/src/partitioning.md | 10 ++++ src/ExtendableGrids.jl | 2 +- src/partitioning.jl | 98 +++++++++++++++++++++++++++++++--------- test/partitioning.jl | 2 +- 4 files changed, 89 insertions(+), 23 deletions(-) diff --git a/docs/src/partitioning.md b/docs/src/partitioning.md index fcdfddcb..f773f1e3 100644 --- a/docs/src/partitioning.md +++ b/docs/src/partitioning.md @@ -4,12 +4,21 @@ Grid partitioning is an experimental feature. Breaking changes in this realm may occur with minor version updates. +The general idea is that all grids created from ExtendableGrids +can be considered to be partitioned such that the neigboring graph of +the partions is colored so that operations on different partitions of +the same color can be performed in parallel in a multithreading environment. + +The default partitioning is trivial: all cells and nodes belong to one partition, +and the resulting trivial neigborhood graph is colored with one color. + ## API calls ```@docs partition num_pcolors num_partitions num_partitions_per_color +num_nodes_per_partition num_cells_per_color pcolors pcolor_partitions @@ -42,6 +51,7 @@ ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PColorPartitions}) ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PartitionCells}) ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PartitionNodes}) ExtendableGrids.partgraph +ExtendableGrids.dopartition ExtendableGrids.reorder_cells ExtendableGrids.induce_node_partitioning! ``` diff --git a/src/ExtendableGrids.jl b/src/ExtendableGrids.jl index 18570579..eedea7cd 100644 --- a/src/ExtendableGrids.jl +++ b/src/ExtendableGrids.jl @@ -82,7 +82,7 @@ export seemingly_equal, numbers_match include("partitioning.jl") export PColorPartitions, PartitionCells, PartitionNodes, NodePermutation export num_pcolors, num_partitions, pcolors, pcolor_partitions, partition_cells, partition_nodes -export partition, num_partitions_per_color, num_cells_per_color, check_partitioning +export partition, num_partitions_per_color, num_cells_per_color, check_partitioning, num_nodes_per_partition export AbstractPartitioningAlgorithm, TrivialPartitioning, PlainMetisPartitioning, RecursiveMetisPartitioning include("assemblytypes.jl") diff --git a/src/partitioning.jl b/src/partitioning.jl index 3a3d4bf2..cb5ead06 100644 --- a/src/partitioning.jl +++ b/src/partitioning.jl @@ -26,6 +26,19 @@ Let `pc=grid[PartitionCells]`. Then all cells with index """ abstract type PartitionCells <: AbstractGridIntegerArray1D end + +""" + $(TYPEDEF) + +Key type describing the bondary faces of a given partition. + +`grid[PartitionBFaces]` returns an integer vector describing +the boundary faces of a partition given by its number. +Let `pc=grid[PartitionCells]`. Then all cells with index +`i ∈ pc[p]:pc[p+1]-1` belong to partition p. +""" +abstract type PartitionBFaces <: AbstractGridIntegerArray1D end + """ $(TYPEDEF) @@ -64,6 +77,7 @@ Create trivial partitioning: the whole grid is partition #1 with just one color. function trivial_partitioning!(grid::ExtendableGrid{Tc,Ti}) where {Tc,Ti} grid[PColorPartitions]=[1,2] grid[PartitionCells]=Ti[1,num_cells(grid)+1] + grid[PartitionBFaces]=Ti[1,num_bfaces(grid)+1] grid[PartitionNodes]=Ti[1,num_nodes(grid)+1] grid end @@ -80,7 +94,7 @@ function ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PColorPartitio end """ - instantiate(grid::ExtendableGrid, ::Type{PColorPartitions}) + instantiate(grid::ExtendableGrid, ::Type{PartitionCells}) If not given otherwise, instantiate partition data with trivial partitioning. """ @@ -89,6 +103,16 @@ function ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PartitionCells grid[PartitionCells] end +""" + instantiate(grid::ExtendableGrid, ::Type{PartitionBFaces}) + +If not given otherwise, instantiate partition data with trivial partitioning. +""" +function ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PartitionBFaces}) + trivial_partitioning!(grid) + grid[PartitionBFaces] +end + """ instantiate(grid::ExtendableGrid, ::Type{PartitionNodes}) @@ -143,6 +167,16 @@ function partition_cells(grid, part) @inbounds partcells[part]:partcells[part+1]-1 end +""" + $(SIGNATURES) + +Return range of cells belonging to a given partition. +""" +function partition_bfaces(grid, part) + partbfaces=grid[PartitionBFaces] + @inbounds partbfaces[part]:partbfaces[part+1]-1 +end + """ $(SIGNATURES) @@ -335,11 +369,6 @@ end Subdivide grid into `npart` partitions using `Metis.partition` and color the resulting partition neigborhood graph. This requires to import Metis.jl in order to trigger the corresponding extension. -!!! warning - This algorithm is unreliable with respect to the induced node partitioning, see - [`induce_node_partitioning!`](@ref). Check the result with - [`check_partitioning`](@ref) before use. - Parameters: $(TYPEDFIELDS) @@ -360,15 +389,9 @@ end $(TYPEDEF) Subdivide grid into `npart` partitions using `Metis.partition` and calculate cell separators -from this partitioning. The initial partitions get pcolor 1, and the separator gets pcolor 2. +from this partitioning. The initial partitions gets pcolor 1, and the separator gets pcolor 2. This is continued recursively with partitioning of the separator. - -!!! warning - This algorithm is unreliable with respect to the induced node partitioning, see - [`induce_node_partitioning!`](@ref). Check the result with - [`check_partitioning`](@ref) before use. - Parameters: $(TYPEDFIELDS) @@ -419,7 +442,8 @@ function reorder_cells(grid::ExtendableGrid{Tc,Ti}, cellpartitions,ncellpartitio pgrid[CellRegions]=grid[CellRegions][cellperm] pgrid[PColorPartitions]=colpart pgrid[PartitionCells]=partcells - + pgrid[PartitionBFaces]=trivial_partitioning(ncellpartitions,num_bfaces(grid)) + for key in [Coordinates, CellGeometries, BFaceNodes, @@ -431,6 +455,21 @@ function reorder_cells(grid::ExtendableGrid{Tc,Ti}, cellpartitions,ncellpartitio pgrid end +""" + $(SIGNATURES) + +(internal) +Create a trivial partitioning +""" + +function trivial_partitioning(npart,nitems) + part=zeros(Int,npart+1) + part[1]=1 + part[2:end].=nitems+1 + part +end + + """ $(SIGNATURES) @@ -442,17 +481,17 @@ The current algorithm assumes that nodes get the partition number from the parti numbers of the cells having this node in common. If these are differnt, the highest number is taken. -This algorithm does not always fulfill the following condition (checked among others by [`check_partitioning`](@ref) with `cellpartonly=false`): -There is no node which is neigbour of nodes from two different partition with the same color. +This algorithm does not always fulfill the condition that +there is no node which is neigbour of nodes from two different partition with the same color. + +This situation is detected and corrected by joining respective critical partitions. +This sacrifies parallel efficiency for correctness. """ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},cn,nc; trivial=false, keep_nodepermutation=true) where {Tc, Ti} partcells=grid[PartitionCells] nnodepartitions=length(partcells)-1 if trivial - partnodes=zeros(Int,nnodepartitions+1) - partnodes[1]=1 - partnodes[2:end].=num_nodes(grid)+1 - grid[PartitionNodes]=partnodes + grid[PartitionNodes]=trivial_partitioning(nnodepartitions,num_nodes(grid)) if keep_nodepermutation grid[NodePermutation]=1:num_nodes(grid) end @@ -486,6 +525,9 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},cn,nc; trivial=fa # which would lead to clashes in the matrix-vector product nn=cn*nc rv=SparseArrays.getrowval(nn) + showinfo=true + + # Repeat several times until ok. while true # Detect the situation, record the corresponding # pairs of partitions @@ -514,8 +556,18 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},cn,nc; trivial=fa if length(idpart)==0 break end - @info "Correcting node partitions: $idpart" + # Show this only once: + if showinfo + """Ensure that no node is accessed from + two different partitions with same color """ + showinfo=false + end + @info "Renumbering: $idpart" + # Re-assign the respective lower partition numbers for the problem cases. + # The parttions with the higher numbers will be just empty. + # This renders the critical nodes into the same partition, so they + # will be accessed from the same parallel task. for inode=1:num_nodes(grid) part=nodepartitions[inode] for id in idpart @@ -542,10 +594,13 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},cn,nc; trivial=fa partctr[part]+=1 end + # Permute coordinate array nodeperm=invperm(nodeperm) xcoord=similar(coord) xcoord[:,nodeperm].=coord + + # Renumber node indices for cells xcellnodes=similar(cellnodes) for icell=1:num_cells(grid) for k=1:lnodes @@ -553,6 +608,7 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},cn,nc; trivial=fa end end + # Renumber node indices for bfaces xbfacenodes=similar(bfacenodes) for ibface=1:num_bfaces(grid) for k=1:lnodes-1 diff --git a/test/partitioning.jl b/test/partitioning.jl index 44f790ac..86984878 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -26,7 +26,6 @@ import Metis @test check_partitioning(grid, verbose=false) end - # METIS has different results depending on the OS... for npart in [10, 15, 20] grid4=partition(grid1,PlainMetisPartitioning(npart=npart)) @test num_pcolors(grid4) > 1 @@ -37,6 +36,7 @@ import Metis @test grid4[Coordinates][:,grid4[NodePermutation]]≈grid1[Coordinates] @test check_partitioning(grid4, verbose=false) end + for npart in [3,4,5,6] grid4=partition(grid1,RecursiveMetisPartitioning(npart=npart)) @test num_pcolors(grid4) > 1 From 0b485c831444cff60a1cda402e268bf3c0092fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Fri, 21 Jun 2024 22:18:59 +0200 Subject: [PATCH 4/7] refactor simplexgridio; write partitioning to sg file format v2.2 --- src/io.jl | 129 ++++++++++++++++++++++++++++++++------------ src/partitioning.jl | 4 +- test/runtests.jl | 13 +++-- 3 files changed, 104 insertions(+), 42 deletions(-) diff --git a/src/io.jl b/src/io.jl index 1be262ef..bb362603 100644 --- a/src/io.jl +++ b/src/io.jl @@ -41,23 +41,32 @@ end """ $(TYPEDSIGNATURES) -Write grid to file. Currently for pdelib sg and Gmsh formats. +Write grid to file. +Supported formats: +- "*.sg": pdelib sg format + """ -function Base.write(fname::String, g::ExtendableGrid; format = "") +function Base.write(fname::String, g::ExtendableGrid; format = "", kwargs...) (fbase, fext) = splitext(fname) if format == "" format = fext[2:end] end - if format == "msh" - try - simplexgrid_to_gmsh(g; filename = fname) - catch e - throw(ErrorException("Missing Gmsh extension. Add Gmsh.jl to your environment and import it to write $(fext) files.")) - end - return + try + writegrid(fname,g, Val{Symbol(format)}; kwargs...) + catch e + throw(ErrorException("Writing $(fext) files not supported")) + end +end + +function writegrid(filename::String, g::ExtendableGrid, ::Type{Val{:msh}};kwargs...) + try + simplexgrid_to_gmsh(g; filename) + catch e + throw(ErrorException("Missing Gmsh extension. Add Gmsh.jl to your environment and import it to write msh files.")) end - @assert format == "sg" +end +function writegrid(fname::String, g::ExtendableGrid, ::Type{Val{:sg}}; version=v"2.2", kwargs...) dim_g = dim_grid(g) dim_s = dim_space(g) nn = num_nodes(g) @@ -73,9 +82,8 @@ function Base.write(fname::String, g::ExtendableGrid; format = "") open(fname, "w") do file write(file, @sprintf("SimplexGrid")) write(file, @sprintf(" ")) - write(file, @sprintf("2.1\n")) + write(file, @sprintf("%s\n","$version"[1:end-2])) write(file, @sprintf("#created by ExtendableGrids.jl (c) J.Fuhrmann et al\n")) - write(file, @sprintf("#mailto:{fuhrmann|streckenbach}@wias-berlin.de\n")) write(file, @sprintf("#%s\n", Dates.format(Dates.now(), "yyyy-mm-ddTHH-mm-SS"))) write(file, @sprintf("DIMENSION\n%d\n", dim_g)) @@ -103,6 +111,20 @@ function Base.write(fname::String, g::ExtendableGrid; format = "") end write(file, @sprintf("%d\n", bfaceregions[ibface])) end + + if version>v"2.1" + function writeitems(key,label) + data=g[key] + write(file, "$(label)\n$(length(data))\n") + for d in data + write(file, "$d\n") + end + end + writeitems(PColorPartitions,"PCOLORPARTITIONS") + writeitems(PartitionCells,"PARTITIONCELLS") + writeitems(PartitionBFaces,"PARTITIONBFACES") + writeitems(PartitionNodes,"PARTITIONNODES") + end write(file, @sprintf("END\n")) flush(file) flush(file) @@ -114,36 +136,47 @@ end """ $(TYPEDSIGNATURES) -Read grid from file. Currently for pdelib sg and Gmsh formats. +Read grid from file. +Supported formats: +- "*.sg": pdelib sg files +- "*.geo": gmsh geometry description (requires `using Gmsh`) +- "*.msh": gmsh mesh (requires `using Gmsh`) """ -function simplexgrid(file::String; format = "") - Ti = Cint +function simplexgrid(file::String; format = "", kwargs...) (fbase, fext) = splitext(file) if format == "" format = fext[2:end] end - if format == "msh" || format == "geo" - grid = nothing - try - grid = simplexgrid_from_gmsh(file) - catch e - throw(ErrorException("Missing Gmsh extension. Add Gmsh.jl to your environment and import it to read $(fext) files.")) - end - return grid + try + simplexgrid(file,Val{Symbol(format)}; kwargs...) + catch e + throw(ErrorException("Reading $(fext) files not supported")) end +end - @assert format == "sg" +function simplexgrid(file::String, ::Type{Val{:msh}}; kwargs...) + try + simplexgrid_from_gmsh(file) + catch e + throw(ErrorException("Missing Gmsh extension. Add Gmsh.jl to your environment and import it to read msh files.")) + end +end + +function simplexgrid(file::String, ::Type{Val{:geo}}; kwargs...) + try + simplexgrid_from_gmsh(file) + catch e + throw(ErrorException("Missing Gmsh extension. Add Gmsh.jl to your environment and import it to read geo files.")) + end +end +function simplexgrid(file::String, ::Type{Val{:sg}}; kwargs...) + Ti = Cint tks = TokenStream(file) expecttoken(tks, "SimplexGrid") - version = parse(Float64, gettoken(tks)) - version20 = false - - if (version == 2.0) - version20 = true - elseif (version == 2.1) - version20 = false - else + version = VersionNumber(gettoken(tks)) + + if version=v"2.3" error("Read grid: wrong format version: $(version)") end @@ -153,6 +186,13 @@ function simplexgrid(file::String; format = "") regions = Array{Ti, 1}(undef, 0) faces = Array{Ti, 2}(undef, 0, 0) bregions = Array{Ti, 1}(undef, 0) + + pcolorpartitions=Array{Ti, 1}(undef, 0) + partitioncells=Array{Ti, 1}(undef, 0) + partitionbfaces=Array{Ti, 1}(undef, 0) + partitionnodes=Array{Ti, 1}(undef, 0) + + while (true) if (trytoken(tks, "DIMENSION")) dim = parse(Ti, gettoken(tks)) @@ -177,7 +217,7 @@ function simplexgrid(file::String; format = "") cells[inode, icell] = parse(Ti, gettoken(tks)) end regions[icell] = parse(Ti, gettoken(tks)) - if version20 + if version==v"2.0" for j = 1:(dim + 1) gettoken(tks) # skip file format garbage end @@ -192,18 +232,37 @@ function simplexgrid(file::String; format = "") faces[inode, iface] = parse(Ti, gettoken(tks)) end bregions[iface] = parse(Ti, gettoken(tks)) - if (version20) + if version == v"2.0" for j = 1:(dim + 2) gettoken(tks) #skip file format garbage end end end + elseif trytoken(tks, "PCOLORPARTITIONS") && version>v"2.1" + n=parse(Ti, gettoken(tks)) + pcolorpartitions=[parse(Ti, gettoken(tks)) for i=1:n] + elseif trytoken(tks, "PARTITIONCELLS") && version>v"2.1" + n=parse(Ti, gettoken(tks)) + partitioncells=[parse(Ti, gettoken(tks)) for i=1:n] + elseif trytoken(tks, "PARTITIONBFACES") && version>v"2.1" + n=parse(Ti, gettoken(tks)) + partitionbfaces=[parse(Ti, gettoken(tks)) for i=1:n] + elseif trytoken(tks, "PARTITIONNODES") && version>v"2.1" + n=parse(Ti, gettoken(tks)) + partitionnodes=[parse(Ti, gettoken(tks)) for i=1:n] else expecttoken(tks, "END") break end end - simplexgrid(coord, cells, regions, faces, bregions) + g=simplexgrid(coord, cells, regions, faces, bregions) + if version>v"2.1" + g[PColorPartitions]=pcolorpartitions + g[PartitionCells]=partitioncells + g[PartitionBFaces]=partitionbfaces + g[PartitionNodes]=partitionnodes + end + g end function simplexgrid_from_gmsh end diff --git a/src/partitioning.jl b/src/partitioning.jl index cb5ead06..6a2f8589 100644 --- a/src/partitioning.jl +++ b/src/partitioning.jl @@ -459,9 +459,9 @@ end $(SIGNATURES) (internal) -Create a trivial partitioning +Create a trivial partitioning such that all items +fall in the first of nparts """ - function trivial_partitioning(npart,nitems) part=zeros(Int,npart+1) part[1]=1 diff --git a/test/runtests.jl b/test/runtests.jl index 19ec7266..e45e8a19 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -161,19 +161,21 @@ end end end -function testrw(grid, format; confidence = :full) +function testrw(grid, format; confidence = :full, kwargs...) #@warn format ftmp = tempname() * "." * format - write(ftmp, grid) + write(ftmp, grid; kwargs...) grid1 = simplexgrid(ftmp) seemingly_equal(grid1, grid; confidence = confidence) end @testset "Read/Write sg" begin X = collect(0:0.05:1) - @test testrw(simplexgrid(X), "sg") - @test testrw(simplexgrid(X, X), "sg") - @test testrw(simplexgrid(X, X, X), "sg") + for version in [v"2.1", v"2.2"] + @test testrw(simplexgrid(X), "sg"; version) + @test testrw(simplexgrid(X, X), "sg";version) + @test testrw(simplexgrid(X, X, X), "sg"; version) + end end @testset "rectnd" begin @@ -417,3 +419,4 @@ end @test sha_code == "93a31139ccb3ae3017351d7cef0c2639c5def97c9744699543fe8bc58e1ebcea" end + From a614753cffcad13051f6a0204847054f85e60d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Mon, 24 Jun 2024 12:13:18 +0200 Subject: [PATCH 5/7] update show method, fix docs --- docs/src/partitioning.md | 4 ++++ src/ExtendableGrids.jl | 4 ++-- src/extendablegrid.jl | 11 ++++++----- src/io.jl | 10 +++++----- src/partitioning.jl | 8 ++++---- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/src/partitioning.md b/docs/src/partitioning.md index f773f1e3..24705e8f 100644 --- a/docs/src/partitioning.md +++ b/docs/src/partitioning.md @@ -23,6 +23,7 @@ num_cells_per_color pcolors pcolor_partitions partition_cells +partition_bfaces partition_nodes check_partitioning ``` @@ -40,6 +41,7 @@ RecursiveMetisPartitioning ```@docs PColorPartitions PartitionCells +PartitionBFaces PartitionNodes NodePermutation ``` @@ -47,8 +49,10 @@ NodePermutation ## Internal API ```@docs ExtendableGrids.trivial_partitioning! +ExtendableGrids.trivial_partitioning ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PColorPartitions}) ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PartitionCells}) +ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PartitionBFaces}) ExtendableGrids.instantiate(grid::ExtendableGrid, ::Type{PartitionNodes}) ExtendableGrids.partgraph ExtendableGrids.dopartition diff --git a/src/ExtendableGrids.jl b/src/ExtendableGrids.jl index eedea7cd..fde9dee9 100644 --- a/src/ExtendableGrids.jl +++ b/src/ExtendableGrids.jl @@ -80,8 +80,8 @@ export isconsistent, dangling_nodes export seemingly_equal, numbers_match include("partitioning.jl") -export PColorPartitions, PartitionCells, PartitionNodes, NodePermutation -export num_pcolors, num_partitions, pcolors, pcolor_partitions, partition_cells, partition_nodes +export PColorPartitions, PartitionCells, PartitionBFaces, PartitionNodes, NodePermutation +export num_pcolors, num_partitions, pcolors, pcolor_partitions, partition_cells, partition_bfaces, partition_nodes export partition, num_partitions_per_color, num_cells_per_color, check_partitioning, num_nodes_per_partition export AbstractPartitioningAlgorithm, TrivialPartitioning, PlainMetisPartitioning, RecursiveMetisPartitioning diff --git a/src/extendablegrid.jl b/src/extendablegrid.jl index af3a5b34..b35099dc 100644 --- a/src/extendablegrid.jl +++ b/src/extendablegrid.jl @@ -484,12 +484,13 @@ function Base.map(f::Function, grid::ExtendableGrid) end function Base.show(io::IO, grid::ExtendableGrid) + str = @sprintf("%s;\ndim: %d nodes: %d cells: %d bfaces: %d", + typeof(grid), dim_space(grid), num_nodes(grid), num_cells(grid), num_bfaces(grid)) if num_edges(grid) > 0 - str = @sprintf("%s;\ndim: %d nodes: %d cells: %d bfaces: %d, edges: %d\n", - typeof(grid), dim_space(grid), num_nodes(grid), num_cells(grid), num_bfaces(grid), num_edges(grid)) - else - str = @sprintf("%s;\ndim: %d nodes: %d cells: %d bfaces: %d\n", - typeof(grid), dim_space(grid), num_nodes(grid), num_cells(grid), num_bfaces(grid)) + str*=@sprintf(", edges: %d", num_edges(grid)) + end + if num_partitions(grid)>1 + str*="\npartitions/color: $(num_partitions_per_color(grid))" end println(io, str) end diff --git a/src/io.jl b/src/io.jl index bb362603..cd09d3e7 100644 --- a/src/io.jl +++ b/src/io.jl @@ -86,8 +86,8 @@ function writegrid(fname::String, g::ExtendableGrid, ::Type{Val{:sg}}; version=v write(file, @sprintf("#created by ExtendableGrids.jl (c) J.Fuhrmann et al\n")) write(file, @sprintf("#%s\n", Dates.format(Dates.now(), "yyyy-mm-ddTHH-mm-SS"))) - write(file, @sprintf("DIMENSION\n%d\n", dim_g)) - write(file, @sprintf("NODES\n%d %d\n", nn, dim_s)) + write(file, @sprintf("DIMENSION %d\n", dim_g)) + write(file, @sprintf("NODES %d %d\n", nn, dim_s)) for inode = 1:nn for idim = 1:dim_s @@ -96,7 +96,7 @@ function writegrid(fname::String, g::ExtendableGrid, ::Type{Val{:sg}}; version=v end end - write(file, @sprintf("CELLS\n%d\n", nc)) + write(file, @sprintf("CELLS %d\n", nc)) for icell = 1:nc for inode = 1:(dim_g + 1) write(file, @sprintf("%d ", cellnodes[inode, icell])) @@ -104,7 +104,7 @@ function writegrid(fname::String, g::ExtendableGrid, ::Type{Val{:sg}}; version=v write(file, @sprintf("%d\n", cellregions[icell])) end - write(file, @sprintf("FACES\n%d\n", nbf)) + write(file, @sprintf("FACES %d\n", nbf)) for ibface = 1:nbf for inode = 1:dim_g write(file, @sprintf("%d ", bfacenodes[inode, ibface])) @@ -115,7 +115,7 @@ function writegrid(fname::String, g::ExtendableGrid, ::Type{Val{:sg}}; version=v if version>v"2.1" function writeitems(key,label) data=g[key] - write(file, "$(label)\n$(length(data))\n") + write(file, "$(label) $(length(data))\n") for d in data write(file, "$d\n") end diff --git a/src/partitioning.jl b/src/partitioning.jl index 6a2f8589..12530af3 100644 --- a/src/partitioning.jl +++ b/src/partitioning.jl @@ -170,7 +170,7 @@ end """ $(SIGNATURES) -Return range of cells belonging to a given partition. +Return range of boundary faces belonging to a given partition. """ function partition_bfaces(grid, part) partbfaces=grid[PartitionBFaces] @@ -558,11 +558,11 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},cn,nc; trivial=fa end # Show this only once: if showinfo - """Ensure that no node is accessed from - two different partitions with same color """ + @info """Renumber partitions to ensure that no node is neighbor of + two different partitions with same color:\n""" showinfo=false end - @info "Renumbering: $idpart" + @info "Renumbering: $idpart\n" # Re-assign the respective lower partition numbers for the problem cases. # The parttions with the higher numbers will be just empty. From 4204435996a5af46a1d9414afde1b6868468d4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Mon, 24 Jun 2024 12:55:56 +0200 Subject: [PATCH 6/7] improve wording of docs --- docs/src/partitioning.md | 21 ++++++++++++--------- src/partitioning.jl | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/src/partitioning.md b/docs/src/partitioning.md index 24705e8f..d2765b0d 100644 --- a/docs/src/partitioning.md +++ b/docs/src/partitioning.md @@ -5,26 +5,28 @@ in this realm may occur with minor version updates. The general idea is that all grids created from ExtendableGrids -can be considered to be partitioned such that the neigboring graph of -the partions is colored so that operations on different partitions of -the same color can be performed in parallel in a multithreading environment. +can be considered to be partitioned such that the neighborhood graph of +the partitions is colored so that operations (FEM/FVM assembly, sparse matrix-vector multiplication +with SparseMatrixCSC) on different partitions of the same color can be performed in parallel +without writing conflicts in a multithreading environment. + The default partitioning is trivial: all cells and nodes belong to one partition, -and the resulting trivial neigborhood graph is colored with one color. +and the resulting trivial neighborhood graph is colored with one color. ## API calls ```@docs partition -num_pcolors -num_partitions -num_partitions_per_color -num_nodes_per_partition -num_cells_per_color pcolors pcolor_partitions partition_cells partition_bfaces partition_nodes +num_pcolors +num_partitions +num_partitions_per_color +num_nodes_per_partition +num_cells_per_color check_partitioning ``` @@ -47,6 +49,7 @@ NodePermutation ``` ## Internal API +These functions & methods are neither exported nor public. ```@docs ExtendableGrids.trivial_partitioning! ExtendableGrids.trivial_partitioning diff --git a/src/partitioning.jl b/src/partitioning.jl index 12530af3..6a08e828 100644 --- a/src/partitioning.jl +++ b/src/partitioning.jl @@ -558,7 +558,7 @@ function induce_node_partitioning!(grid::ExtendableGrid{Tc,Ti},cn,nc; trivial=fa end # Show this only once: if showinfo - @info """Renumber partitions to ensure that no node is neighbor of + @info """Renumber node partitions such that no node is neighbor of two different partitions with same color:\n""" showinfo=false end From 186166f14de533a55d3b48112445c713c70c8ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Mon, 24 Jun 2024 12:57:54 +0200 Subject: [PATCH 7/7] bump minor version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index cfaec066..3da38360 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ExtendableGrids" uuid = "cfc395e8-590f-11e8-1f13-43a2532b2fa8" authors = ["Juergen Fuhrmann ", "Christian Merdon ", "Johannes Taraz "] -version = "1.7" +version = "1.8" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"