diff --git a/CHANGELOG.md b/CHANGELOG.md index 5285067a..dd5db217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ # Changelog +## [1.14.1] - 2025-11-14 +- fix dangling nodes in full ringsector +- add `isconsistent` checks to CI tests +- somewhat extend `isconsistent` checks + ## [1.14.0] - 2025-07-01 - add `trim!`/`trim` function to remove precomputed grid components for lightweight storage diff --git a/Project.toml b/Project.toml index 8a7e8c94..2bb8a254 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 ", "Patrick Jaap "] -version = "1.14.0" +version = "1.14.1" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/examples/examples1d.jl b/examples/examples1d.jl index e95694d4..efd519e5 100644 --- a/examples/examples1d.jl +++ b/examples/examples1d.jl @@ -49,6 +49,11 @@ end using Test function runtests() + @test isconsistent(interval_from_vector()) + @test isconsistent(interval_localref()) + @test isconsistent(interval_multiregion()) + @test isconsistent(interval_subgrid()) + @test numbers_match(interval_from_vector(), 21, 20, 2) @test numbers_match(interval_localref(), 27, 26, 2) @test numbers_match(interval_multiregion(), 21, 20, 3) diff --git a/examples/examples2d.jl b/examples/examples2d.jl index 71cb4bda..84f9fc56 100644 --- a/examples/examples2d.jl +++ b/examples/examples2d.jl @@ -98,6 +98,13 @@ end # Unit tests using Test function runtests() + + @test isconsistent(rectangle()) + @test isconsistent(rectangle_localref()) + @test isconsistent(rectangle_multiregion()) + @test isconsistent(rectangle_subgrid()) + @test isconsistent(rect2d_bregion_function()) + @test numbers_match(rectangle(), 441, 800, 80) @test numbers_match(rectangle_localref(), 729, 1352, 104) @test numbers_match(rectangle_multiregion(), 441, 800, 100) @@ -105,6 +112,8 @@ function runtests() @test numbers_match(rect2d_bregion_function(), 79, 112, 44) g, sg, sf = sorted_subgrid() + @test isconsistent(g) + @test isconsistent(sg) @test numbers_match(g, 187, 306, 66) @test numbers_match(sg, 17, 16, 0) @test issorted(view(sg[Coordinates], 1, :)) diff --git a/examples/examples3d.jl b/examples/examples3d.jl index 220660b3..28143d60 100644 --- a/examples/examples3d.jl +++ b/examples/examples3d.jl @@ -44,6 +44,10 @@ end using Test function runtests() + @test isconsistent(quadrilateral()) + @test isconsistent(cross3d()) + + @test numbers_match(quadrilateral(), 330, 1200, 440) @test mask_bedges() @test numbers_match(cross3d(), 189, 480, 344) diff --git a/src/adjacency.jl b/src/adjacency.jl index 63e4e67e..a83c2ab4 100644 --- a/src/adjacency.jl +++ b/src/adjacency.jl @@ -130,6 +130,20 @@ function Base.append!(adj::VariableTargetAdjacency, column) return push!(adj.colstart, length(adj.colentries) + 1) end +""" +$(TYPEDSIGNATURES) + +Maximum for VariableTargetAdjacency +""" +Base.maximum(adj::VariableTargetAdjacency) = Base.maximum(adj.colentries) + +""" +$(TYPEDSIGNATURES) + +Minimum for VariableTargetAdjacency +""" +Base.minimum(adj::VariableTargetAdjacency) = Base.minimum(adj.colentries) + """ $(TYPEDEF) diff --git a/src/commongrids.jl b/src/commongrids.jl index fd0d3712..dc4d720d 100644 --- a/src/commongrids.jl +++ b/src/commongrids.jl @@ -174,12 +174,12 @@ end """ - ringsector(rad,ang; eltype=Triangle2D) + ringsector(rad,ang) Sector of ring or full ring (if `ang[begin]-ang[end]≈2π`) """ -function ringsector(rad, ang; eltype = Triangle2D) - Tv = Float32 +function ringsector(rad, ang) + Tv = promote_type(eltype(rad), eltype(ang)) Ti = Int32 coord = ElasticArray{Tv, 2}(undef, 2, 0) @@ -201,7 +201,9 @@ function ringsector(rad, ang; eltype = Triangle2D) x = cos(ϕ) y = sin(ϕ) for irad in 1:nrad - append!(coord, (rad[irad] * x, rad[irad] * y)) + if iarc < narc || !fullcircle + append!(coord, (rad[irad] * x, rad[irad] * y)) + end icoord = size(coord, 2) if irad < nrad if iarc < narc diff --git a/src/extendablegrid.jl b/src/extendablegrid.jl index df0327ce..edf42691 100644 --- a/src/extendablegrid.jl +++ b/src/extendablegrid.jl @@ -459,6 +459,30 @@ or, if `warnoly==true`, return false. """ function isconsistent(grid; warnonly = false) consistent = true + nnodes = num_nodes(grid) + + if maximum(grid[CellNodes]) != nnodes + @warn "maximum(grid[CellNodes])!=nnodes" + consistent = false + end + + if minimum(grid[CellNodes]) != 1 + @warn "minimum(grid[CellNodes])!=1" + consistent = false + end + + if length(grid[BFaceNodes]) > 0 + if maximum(grid[BFaceNodes]) > nnodes + @warn "maximum(grid[BFaceNodes])>nnodes" + consistent = false + end + + if minimum(grid[BFaceNodes]) < 1 + @warn "minimum(grid[BFaceNodes])<1" + consistent = false + end + end + dnodes = dangling_nodes(grid) if !isnothing(dnodes) @warn "Found dangling nodes: $(dnodes)" diff --git a/test/gmsh.jl b/test/gmsh.jl index e812052f..6851a77d 100644 --- a/test/gmsh.jl +++ b/test/gmsh.jl @@ -51,41 +51,51 @@ end ExtendableGrids.simplexgrid_to_gmsh(grid1; filename = joinpath(path, "testfile.msh")) grid2 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "testfile.msh"); Tc = Float32, Ti = Int64) + @test isconsistent(grid2) @test seemingly_equal(grid2, grid1; sort = true, confidence = :low) @test seemingly_equal(grid2, grid1; sort = true, confidence = :full) grid1 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "sto_2d.msh"); Tc = Float64, Ti = Int64) + @test isconsistent(grid1) ExtendableGrids.simplexgrid_to_gmsh(grid1; filename = joinpath(path, "testfile.msh")) grid2 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "testfile.msh"); Tc = Float64, Ti = Int64) + @test isconsistent(grid2) @test seemingly_equal(grid1, grid2; sort = true, confidence = :low) @test seemingly_equal(grid1, grid2; sort = true, confidence = :full) grid1 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "sto_3d.msh"); Tc = Float32, Ti = Int64) + @test isconsistent(grid1) ExtendableGrids.simplexgrid_to_gmsh(grid1; filename = joinpath(path, "testfile.msh")) grid2 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "testfile.msh"); Tc = Float64, Ti = Int32) + @test isconsistent(grid2) @test seemingly_equal(grid1, grid2; sort = true, confidence = :low) @test seemingly_equal(grid1, grid2; sort = true, confidence = :full) grid1 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "sto_2d.msh")) + @test isconsistent(grid1) grid2 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "sto_3d.msh"); Tc = Float32, Ti = Int32) + @test isconsistent(grid2) @test !seemingly_equal(grid1, grid2; sort = true, confidence = :low) @test !seemingly_equal(grid1, grid2; sort = true, confidence = :full) grid1 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "testmesh.gmsh"); incomplete = true) ExtendableGrids.seal!(grid1; encode = false) + @test isconsistent(grid1) ExtendableGrids.simplexgrid_to_gmsh(grid1; filename = joinpath(path, "completed_testfile.msh")) grid2 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "completed_testfile.msh")) + @test isconsistent(grid2) grid3 = ExtendableGrids.simplexgrid_from_gmsh(joinpath(path, "testmesh.gmsh"); incomplete = true) ExtendableGrids.seal!(grid3; encode = true) + @test isconsistent(grid3) @test seemingly_equal(grid1, grid2; sort = true, confidence = :low) @test seemingly_equal(grid1, grid2; sort = true, confidence = :full) @@ -98,7 +108,9 @@ end grid2 = simplexgrid(x, x) grid3 = simplexgrid(x, x) ExtendableGrids.seal!(grid2) + @test isconsistent(grid2) ExtendableGrids.seal!(grid3; encode = false) + @test isconsistent(grid3) @test seemingly_equal(grid2, grid1; sort = true, confidence = :low) @test seemingly_equal(grid2, grid1; sort = true, confidence = :full) @@ -109,9 +121,11 @@ end @testset "Read/write mixed gmsh 2d" begin path = joinpath(pkgdir(ExtendableGrids), "test") grid1 = ExtendableGrids.mixedgrid_from_gmsh(joinpath(path, "mixedgrid_2d.msh"); Tc = Float64, Ti = Int64) + @test_broken isconsistent(grid1) ExtendableGrids.mixedgrid_to_gmsh(grid1; filename = joinpath(path, "testfile.msh")) grid2 = ExtendableGrids.mixedgrid_from_gmsh(joinpath(path, "testfile.msh"); Tc = Float32, Ti = UInt64) + @test_broken isconsistent(grid2) @test seemingly_equal(grid1, grid2; sort = true, confidence = :low) @test seemingly_equal(grid1, grid2; sort = true, confidence = :full) @@ -121,6 +135,9 @@ end path = joinpath(pkgdir(ExtendableGrids), "test") grid = simplexgrid(joinpath(path, "disk1hole.geo")) @test num_cells(grid) > 0 + @test_broken isconsistent(grid) + grid = simplexgrid(joinpath(path, "cube6.geo")) @test num_cells(grid) > 0 + @test isconsistent(grid) end diff --git a/test/partitioning.jl b/test/partitioning.jl index feb5b751..ba34f5d3 100644 --- a/test/partitioning.jl +++ b/test/partitioning.jl @@ -17,6 +17,7 @@ import Metis for grid in (grid1, grid2, grid3) + @test isconsistent(grid) @test num_pcolors(grid) == 1 @test num_partitions(grid) == 1 @test pcolors(grid) == 1:1 @@ -28,6 +29,7 @@ import Metis for npart in [10, 15, 20] grid4 = partition(grid1, PlainMetisPartitioning(npart = npart); nodes = true, keep_nodepermutation = true) + @test isconsistent(grid4) @test num_pcolors(grid4) > 1 @test num_partitions(grid4) == npart @test pcolors(grid4) |> length > 0 @@ -39,6 +41,7 @@ import Metis for npart in [3, 4, 5, 6] grid4 = partition(grid1, RecursiveMetisPartitioning(npart = npart); nodes = true, keep_nodepermutation = true) + @test isconsistent(grid4) @test num_pcolors(grid4) > 1 @test num_partitions(grid4) > npart @test pcolors(grid4) |> length > 0 diff --git a/test/runtests.jl b/test/runtests.jl index 41c02f66..497fd9b5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -200,6 +200,10 @@ end end end +@testset "Common grids" begin + include("test_commongrids.jl") +end + @testset "Base.map" begin X = 0:0.1:1 fx(x) = x @@ -291,11 +295,13 @@ end sub2 = subgen(; region = 3) @test numbers_match(subgen(), 756, 3000, 950) + @test isconsistent(subgen()) X = collect(0:0.25:1) gxy = simplexgrid(X, X) gxyz = simplexgrid(gxy, X) g = simplexgrid(X, X, X) + @test isconsistent(gxyz) @test numbers_match(gxyz, 125, 384, 192) @test g[Coordinates] ≈ gxyz[Coordinates] end @@ -306,6 +312,7 @@ end grid = grid_unitsquare(Triangle2D) grid[CellRegions] = Int32[1, 2, 2, 1] sgrid = subgrid(grid, [1]) + @test isconsistent(sgrid) @test sgrid[ParentGridRelation] == SubGrid{ON_CELLS} ## check if CellParents are assigned correctly @@ -324,9 +331,11 @@ end @testset "ParentGridRelation-RefinedGrid" begin ## generate a refined grid = grid_unitsquare(Triangle2D) + @test isconsistent(grid) ## check uniform refinement rgrid = uniform_refine(grid) + @test isconsistent(rgrid) @test rgrid[ParentGridRelation] == RefinedGrid ## check if CellParents and BFaceParents are set @@ -335,6 +344,7 @@ end ## check barycentric refinement rgrid = barycentric_refine(grid) + @test isconsistent(rgrid) @test rgrid[ParentGridRelation] == RefinedGrid ## check if CellParents and BFaceParents are set @@ -373,7 +383,9 @@ function tglue(; dim = 2, breg = 0) g2 = simplexgrid(X2, Y2, Z2) end - return glue(g1, g2; interface = breg) + grid = glue(g1, g2; interface = breg) + @test isconsistent(grid) + return grid end @testset "Glue" begin diff --git a/test/test_commongrids.jl b/test/test_commongrids.jl new file mode 100644 index 00000000..0480fdc5 --- /dev/null +++ b/test/test_commongrids.jl @@ -0,0 +1,26 @@ +@test isconsistent(reference_domain(Edge1D)) +@test isconsistent(reference_domain(Triangle2D)) +@test isconsistent(reference_domain(Parallelogram2D)) +@test isconsistent(reference_domain(Tetrahedron3D)) +@test isconsistent(reference_domain(Hexahedron3D)) + +@test isconsistent(grid_unitcube(Parallelepiped3D)) +@test isconsistent(grid_unitcube(Tetrahedron3D)) +@test isconsistent(grid_unitsquare(Triangle2D)) +@test isconsistent(grid_unitsquare(Parallelogram2D)) +@test isconsistent(grid_lshape(Triangle2D)) +@test isconsistent(grid_unitsquare_mixedgeometries()) + +@test isconsistent( + ringsector( + range(0, 1, length = 10), + range(0, π / 2, length = 10) + ) +) + +@test isconsistent( + ringsector( + range(0, 1, length = 10), + range(0, 2π, length = 10) + ) +) diff --git a/test/test_gridstuff.jl b/test/test_gridstuff.jl index 6d628dbb..78fc1275 100644 --- a/test/test_gridstuff.jl +++ b/test/test_gridstuff.jl @@ -123,7 +123,7 @@ end function check_uniform_refinement(xgrid, bary::Bool) xgrid2 = bary ? barycentric_refine(xgrid) : uniform_refine(xgrid) minvol = minimum(xgrid2[CellVolumes]) - return minvol > 0 + return minvol > 0 && isconsistent(xgrid2) end @@ -170,5 +170,6 @@ function run_grid_tests() @test check_uniform_refinement(reference_domain(Triangle2D), true) @test check_uniform_refinement(reference_domain(Parallelogram2D), false) @test check_uniform_refinement(reference_domain(Tetrahedron3D), false) - return @test check_uniform_refinement(reference_domain(Tetrahedron3D), true) + @test check_uniform_refinement(reference_domain(Tetrahedron3D), true) + return nothing end diff --git a/test/tritet.jl b/test/tritet.jl index 9418a027..ee2c55c6 100644 --- a/test/tritet.jl +++ b/test/tritet.jl @@ -41,7 +41,9 @@ end @testset "Triangulate" begin @test isa(example_domain_regions(), ExtendableGrid) + @test isconsistent(example_domain_regions()) @test isa(example_domain_holes(), ExtendableGrid) + @test isconsistent(example_domain_holes()) end function prism(vol = 2) @@ -109,5 +111,7 @@ end @testset "TetGen" begin @test isa(prism(), ExtendableGrid) + @test isconsistent(prism()) @test isa(material_prism(), ExtendableGrid) + @test isconsistent(material_prism()) end