diff --git a/test/dofs/test_mixeddof_vararg.jl b/test/dofs/test_mixeddof_vararg.jl new file mode 100644 index 0000000..e5c5932 --- /dev/null +++ b/test/dofs/test_mixeddof_vararg.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 + +""" +Test for variadic MixedDOF type - combining multiple DOF types at type level. + +This demonstrates the **vararg** approach to multi-field elements, where you +mix DOF types together as type parameters (not with field names). +""" + +using Test +using JuliaFEM +using JuliaFEM: Vertex, Edge, Cell, DOF, MixedDOF, create_elements! +using Tensors + +@testset "šŸŽÆ MixedDOF: Variadic Multi-Field Elements" begin + + # Create minimal mesh: 2 tetrahedra sharing a face + # Nodes: 1-5 (vertex IDs) + nodes = [ + Vec(0.0, 0.0, 0.0), # 1 + Vec(1.0, 0.0, 0.0), # 2 + Vec(0.0, 1.0, 0.0), # 3 + Vec(0.0, 0.0, 1.0), # 4 + Vec(1.0, 1.0, 1.0), # 5 + ] + + connectivity = [ + (UInt32(1), UInt32(2), UInt32(3), UInt32(4)), # Tet 1 + (UInt32(2), UInt32(3), UInt32(4), UInt32(5)), # Tet 2 + ] + + mesh = Mesh{4, Tetrahedron{4}}(nodes, connectivity) + + println("\n" * "="^70) + println("šŸŽÆ VARIADIC MixedDOF: Type-Level Multi-Field Composition") + println("="^70) + + # ======================================================================== + # Define MixedDOF with 4 fields (NO field names, just types!) + # ======================================================================== + + println("\nšŸ“‹ Defining MixedDOF (variadic, type-level)...") + + # Simple syntax - no need to repeat DOF! + mixed_type = @MixedDOF{ + (Float64, Vertex), # Field 1: Temperature at vertices + (Vec{3}, Vertex), # Field 2: Displacement at vertices + (Float64, Cell), # Field 3: Pressure at cells + (Float64, Edge) # Field 4: Electric potential on edges + } + + println(" Type: $mixed_type") + println(" āœ“ 4 DOF types mixed together") + println(" āœ“ Access by position (tuple unpacking)") + println(" āœ“ Simple syntax: @MixedDOF{(Float64, Vertex), (Vec{3}, Vertex), ...}") + + # ======================================================================== + # Create elements with MixedDOF + # ======================================================================== + + println("\nšŸ—ļø Creating elements...") + + eltype = Element{Tetrahedron{4}, Lagrange{1}, mixed_type} + elements, mgr = create_elements!(mesh, eltype) + + println(" āœ“ Created $(length(elements)) elements") + @test length(elements) == 2 + + # ======================================================================== + # Test DOF indices structure (tuple of tuples, NOT NamedTuple!) + # ======================================================================== + + println("\nšŸ” Examining DOF indices...") + + elem = first(elements) + println(" Element 1 DOF indices type: $(typeof(elem.dof_indices))") + @test elem.dof_indices isa Tuple + @test length(elem.dof_indices) == 4 # 4 fields + + # Unpack by position + T_dofs, u_dofs, p_dofs, φ_dofs = elem.dof_indices + + println(" āœ“ Temperature DOFs: $T_dofs") + println(" āœ“ Displacement DOFs: $u_dofs") + println(" āœ“ Pressure DOFs: $p_dofs") + println(" āœ“ Electric DOFs: $φ_dofs") + + # Validate structure + @test length(T_dofs) == 4 # 4 vertices + @test length(u_dofs) == 12 # 4 vertices Ɨ 3 components + @test length(p_dofs) == 1 # 1 cell (discontinuous) + @test length(φ_dofs) == 6 # 6 edges + + # ======================================================================== + # Test DOF sharing between elements + # ======================================================================== + + println("\nšŸ”— Testing DOF sharing...") + + elem2 = elements[2] + T_dofs2, u_dofs2, p_dofs2, φ_dofs2 = elem2.dof_indices + + # Vertices 2,3,4 are shared → same DOF indices + shared_nodes_overlap = !isempty(intersect(T_dofs, T_dofs2)) + @test shared_nodes_overlap + println(" āœ“ Shared vertices have same temperature DOFs") + + # Cell DOFs are unique (no sharing) + @test isempty(intersect(p_dofs, p_dofs2)) + println(" āœ“ Cell DOFs are unique (discontinuous)") + + # Edge DOFs: Some edges are shared + shared_edges_overlap = !isempty(intersect(φ_dofs, φ_dofs2)) + @test shared_edges_overlap + println(" āœ“ Shared edges have same electric DOFs") + + # ======================================================================== + # Total system DOFs + # ======================================================================== + + println("\nšŸ“Š System summary:") + + all_T_dofs = unique(vcat([collect(elem.dof_indices[1]) for elem in elements]...)) + all_u_dofs = unique(vcat([collect(elem.dof_indices[2]) for elem in elements]...)) + all_p_dofs = unique(vcat([collect(elem.dof_indices[3]) for elem in elements]...)) + all_φ_dofs = unique(vcat([collect(elem.dof_indices[4]) for elem in elements]...)) + + println(" Temperature DOFs: $(length(all_T_dofs)) (5 unique vertices)") + println(" Displacement DOFs: $(length(all_u_dofs)) (5 vertices Ɨ 3)") + println(" Pressure DOFs: $(length(all_p_dofs)) (2 cells, no sharing)") + println(" Electric DOFs: $(length(all_φ_dofs)) (9 unique edges)") + + total_dofs = length(all_T_dofs) + length(all_u_dofs) + length(all_p_dofs) + length(all_φ_dofs) + println(" ────────────────────────────") + println(" TOTAL: $total_dofs DOFs") + + @test length(all_T_dofs) == 5 + @test length(all_u_dofs) == 15 + @test length(all_p_dofs) == 2 + @test length(all_φ_dofs) == 9 + @test total_dofs == 31 + + # ======================================================================== + # Compare: MixedDOF vs NamedTuple + # ======================================================================== + + println("\nšŸ’” COMPARISON: MixedDOF vs NamedTuple") + println(" ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”") + println(" │ MixedDOF (vararg): │") + println(" │ - Type-level composition │") + println(" │ - Access by position: T_dofs, u_dofs, ... = elem.dof_indices │") + println(" │ - More compact type signature │") + println(" │ │") + println(" │ NamedTuple: │") + println(" │ - Named fields │") + println(" │ - Access by name: elem.dof_indices.T, .u, .p, .φ │") + println(" │ - More readable, self-documenting │") + println(" │ │") + println(" │ BOTH ARE SUPPORTED! Choose based on preference. │") + println(" ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜") + + println("\n" * "="^70) + println("āœ… ALL TESTS PASSED: Variadic MixedDOF works!") + println("="^70) +end