diff --git a/test/mesh/test_refine.jl b/test/mesh/test_refine.jl new file mode 100644 index 0000000..32dad48 --- /dev/null +++ b/test/mesh/test_refine.jl @@ -0,0 +1,166 @@ +# This file is a part of JuliaFEM. +# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md + +using Test +using JuliaFEM +using Tensors + +@testset "Mesh refinement" begin + @testset "Single Hex8 refinement - 1 level" begin + # Create a single hex element: unit cube [0,1]³ + nodes = [ + Vec(0.0, 0.0, 0.0), Vec(1.0, 0.0, 0.0), + Vec(1.0, 1.0, 0.0), Vec(0.0, 1.0, 0.0), + Vec(0.0, 0.0, 1.0), Vec(1.0, 0.0, 1.0), + Vec(1.0, 1.0, 1.0), Vec(0.0, 1.0, 1.0) + ] + connectivity = [(UInt32(1), UInt32(2), UInt32(3), UInt32(4), + UInt32(5), UInt32(6), UInt32(7), UInt32(8))] + + mesh = Mesh{Hex8}(nodes, connectivity) + + # Refine once + strategy = LongestEdgeBisection(1) + refined = refine(mesh, strategy) + + # After 1 refinement: 1 element → 2 elements + @test nelements(refined) == 2 + + # Should have original 8 nodes + 4 new midpoint nodes = 12 nodes + @test nnodes_total(refined) == 12 + + # Check that all elements have 8 nodes + for conn in refined.connectivity + @test length(conn) == 8 + end + + println("1 level refinement: ", nelements(mesh), " → ", nelements(refined), " elements") + end + + @testset "Single Hex8 refinement - 2 levels" begin + # Create a single hex element + nodes = [ + Vec(0.0, 0.0, 0.0), Vec(1.0, 0.0, 0.0), + Vec(1.0, 1.0, 0.0), Vec(0.0, 1.0, 0.0), + Vec(0.0, 0.0, 1.0), Vec(1.0, 0.0, 1.0), + Vec(1.0, 1.0, 1.0), Vec(0.0, 1.0, 1.0) + ] + connectivity = [(UInt32(1), UInt32(2), UInt32(3), UInt32(4), + UInt32(5), UInt32(6), UInt32(7), UInt32(8))] + + mesh = Mesh{Hex8}(nodes, connectivity) + + # Refine twice + strategy = LongestEdgeBisection(2) + refined = refine(mesh, strategy) + + # After 2 refinements: 1 → 2 → 4 elements + @test nelements(refined) == 4 + + println("2 level refinement: ", nelements(mesh), " → ", nelements(refined), " elements") + end + + @testset "Single Hex8 refinement - 3 levels" begin + # Create a single hex element + nodes = [ + Vec(0.0, 0.0, 0.0), Vec(1.0, 0.0, 0.0), + Vec(1.0, 1.0, 0.0), Vec(0.0, 1.0, 0.0), + Vec(0.0, 0.0, 1.0), Vec(1.0, 0.0, 1.0), + Vec(1.0, 1.0, 1.0), Vec(0.0, 1.0, 1.0) + ] + connectivity = [(UInt32(1), UInt32(2), UInt32(3), UInt32(4), + UInt32(5), UInt32(6), UInt32(7), UInt32(8))] + + mesh = Mesh{Hex8}(nodes, connectivity) + + # Refine 3 times + strategy = LongestEdgeBisection(3) + refined = refine(mesh, strategy) + + # After 3 refinements: 1 → 2 → 4 → 8 elements + @test nelements(refined) == 8 + + println("3 level refinement: ", nelements(mesh), " → ", nelements(refined), " elements") + end + + @testset "Rectangular Hex8 refinement" begin + # Create elongated hex element: [0,2] × [0,1] × [0,1] + # Should split along X first (longest dimension) + nodes = [ + Vec(0.0, 0.0, 0.0), Vec(2.0, 0.0, 0.0), + Vec(2.0, 1.0, 0.0), Vec(0.0, 1.0, 0.0), + Vec(0.0, 0.0, 1.0), Vec(2.0, 0.0, 1.0), + Vec(2.0, 1.0, 1.0), Vec(0.0, 1.0, 1.0) + ] + connectivity = [(UInt32(1), UInt32(2), UInt32(3), UInt32(4), + UInt32(5), UInt32(6), UInt32(7), UInt32(8))] + + mesh = Mesh{Hex8}(nodes, connectivity) + strategy = LongestEdgeBisection(1) + refined = refine(mesh, strategy) + + @test nelements(refined) == 2 + + # Check that split happened along X (should create nodes near x=1.0) + # New nodes should be at midpoints + midpoint_nodes = refined.nodes[9:12] # Last 4 nodes are new + for node in midpoint_nodes + # All midpoint nodes should have x ≈ 1.0 (midpoint of [0,2]) + @test node[1] ≈ 1.0 + end + + println("Rectangular refinement along longest dimension successful") + end + + @testset "Multiple element refinement" begin + # Create 2 hex elements side by side + nodes = [ + Vec(0.0, 0.0, 0.0), Vec(1.0, 0.0, 0.0), + Vec(1.0, 1.0, 0.0), Vec(0.0, 1.0, 0.0), + Vec(0.0, 0.0, 1.0), Vec(1.0, 0.0, 1.0), + Vec(1.0, 1.0, 1.0), Vec(0.0, 1.0, 1.0), + # Second element nodes + Vec(2.0, 0.0, 0.0), Vec(2.0, 1.0, 0.0), + Vec(2.0, 0.0, 1.0), Vec(2.0, 1.0, 1.0) + ] + connectivity = [ + (UInt32(1), UInt32(2), UInt32(3), UInt32(4), + UInt32(5), UInt32(6), UInt32(7), UInt32(8)), + (UInt32(2), UInt32(9), UInt32(10), UInt32(3), + UInt32(6), UInt32(11), UInt32(12), UInt32(7)) + ] + + mesh = Mesh{Hex8}(nodes, connectivity) + strategy = LongestEdgeBisection(1) + refined = refine(mesh, strategy) + + # 2 elements → 4 elements after 1 refinement + @test nelements(refined) == 4 + + println("Multiple element refinement: ", nelements(mesh), " → ", nelements(refined), " elements") + end + + @testset "Element set preservation" begin + # Create mesh with element sets + nodes = [ + Vec(0.0, 0.0, 0.0), Vec(1.0, 0.0, 0.0), + Vec(1.0, 1.0, 0.0), Vec(0.0, 1.0, 0.0), + Vec(0.0, 0.0, 1.0), Vec(1.0, 0.0, 1.0), + Vec(1.0, 1.0, 1.0), Vec(0.0, 1.0, 1.0) + ] + connectivity = [(UInt32(1), UInt32(2), UInt32(3), UInt32(4), + UInt32(5), UInt32(6), UInt32(7), UInt32(8))] + + element_sets = Dict(:volume => Set(UInt32[1])) + mesh = Mesh{Hex8}(nodes, connectivity, element_sets) + + strategy = LongestEdgeBisection(2) + refined = refine(mesh, strategy) + + # Element set should be preserved and expanded + @test haskey(refined.element_sets, :volume) + @test length(refined.element_sets[:volume]) == 4 # 1 → 2 → 4 elements + + println("Element set preservation: OK") + end +end