mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-09-26 03:44:45 +00:00
154 lines
5.5 KiB
Julia
154 lines
5.5 KiB
Julia
|
|
# This file is a part of JuliaFEM.
|
|||
|
|
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
Test CElement Creation with DOF Manager
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
# Mock mesh structure for testing
|
|||
|
|
struct TestMesh
|
|||
|
|
nodes::Dict{Int, Vec} # node_id → coordinates
|
|||
|
|
connectivity::Dict{Int, Tuple} # elem_id → node tuple
|
|||
|
|
element_sets::Dict{String, Vector{Int}} # set_name → elem_ids
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
@testset "CElement Creation with DOF Assignment" begin
|
|||
|
|
@testset "Single Element Type - Triangle ScalarDOF" begin
|
|||
|
|
# Create mock mesh: 2 triangles sharing an edge
|
|||
|
|
mesh = TestMesh(
|
|||
|
|
Dict(
|
|||
|
|
1 => Vec{2}((0.0, 0.0)),
|
|||
|
|
2 => Vec{2}((1.0, 0.0)),
|
|||
|
|
3 => Vec{2}((0.5, 0.8)),
|
|||
|
|
4 => Vec{2}((0.5, -0.8))
|
|||
|
|
),
|
|||
|
|
Dict(
|
|||
|
|
1 => (1, 2, 3), # Triangle 1
|
|||
|
|
2 => (1, 2, 4) # Triangle 2
|
|||
|
|
),
|
|||
|
|
Dict("SURFACE" => [1, 2])
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Get element IDs
|
|||
|
|
surf_ids = get_element_ids(mesh, "SURFACE")
|
|||
|
|
@test surf_ids == [1, 2]
|
|||
|
|
|
|||
|
|
# Create element description
|
|||
|
|
descriptions = [
|
|||
|
|
CElement{Triangle{3}, Lagrange{1}, ScalarDOF} => surf_ids
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# Create elements with DOF assignment
|
|||
|
|
elements = create_celements!(mesh, descriptions)
|
|||
|
|
|
|||
|
|
# Verify
|
|||
|
|
@test length(elements) == 2
|
|||
|
|
@test all(e -> e isa CElement{Triangle{3}, Lagrange{1}, ScalarDOF}, elements)
|
|||
|
|
|
|||
|
|
# Element 1: nodes (1,2,3) → DOFs [1, 2, 3]
|
|||
|
|
@test elements[1].dof_indices == (1, 2, 3)
|
|||
|
|
|
|||
|
|
# Element 2: nodes (1,2,4), nodes 1-2 reuse DOFs, node 4 is new
|
|||
|
|
# Node 1 → DOF 1, Node 2 → DOF 2, Node 4 → DOF 4
|
|||
|
|
@test elements[2].dof_indices == (1, 2, 4)
|
|||
|
|
|
|||
|
|
# Total DOFs: 4 unique nodes = 4 DOFs
|
|||
|
|
all_dofs = vcat([collect(e.dof_indices) for e in elements]...)
|
|||
|
|
@test maximum(all_dofs) == 4
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
@testset "Single Element Type - Tetrahedron VectorDOF{3}" begin
|
|||
|
|
mesh = TestMesh(
|
|||
|
|
Dict(
|
|||
|
|
1 => Vec{3}((0.0, 0.0, 0.0)),
|
|||
|
|
2 => Vec{3}((1.0, 0.0, 0.0)),
|
|||
|
|
3 => Vec{3}((0.0, 1.0, 0.0)),
|
|||
|
|
4 => Vec{3}((0.0, 0.0, 1.0)),
|
|||
|
|
5 => Vec{3}((1.0, 1.0, 1.0))
|
|||
|
|
),
|
|||
|
|
Dict(
|
|||
|
|
1 => (1, 2, 3, 4), # Tet 1
|
|||
|
|
2 => (2, 3, 4, 5) # Tet 2
|
|||
|
|
),
|
|||
|
|
Dict("VOLUME" => [1, 2])
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
descriptions = [
|
|||
|
|
CElement{Tetrahedron{4}, Lagrange{1}, VectorDOF{3}} => get_element_ids(mesh, "VOLUME")
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
elements = create_celements!(mesh, descriptions)
|
|||
|
|
|
|||
|
|
@test length(elements) == 2
|
|||
|
|
|
|||
|
|
# Element 1: 4 nodes × 3 DOFs = 12 DOFs
|
|||
|
|
@test elements[1].dof_indices == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
|
|||
|
|
|
|||
|
|
# Element 2: nodes 2,3,4 reuse DOFs, node 5 is new
|
|||
|
|
# Node 2 → [4,5,6], Node 3 → [7,8,9], Node 4 → [10,11,12], Node 5 → [13,14,15]
|
|||
|
|
@test elements[2].dof_indices == (4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
|
|||
|
|
|
|||
|
|
# Total DOFs: 5 nodes × 3 DOFs/node = 15
|
|||
|
|
all_dofs = vcat([collect(e.dof_indices) for e in elements]...)
|
|||
|
|
@test maximum(all_dofs) == 15
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
@testset "Multiple Element Types - Volume + Surface" begin
|
|||
|
|
# Realistic case: solid elements + surface heat flux elements
|
|||
|
|
mesh = TestMesh(
|
|||
|
|
Dict(
|
|||
|
|
1 => Vec{3}((0.0, 0.0, 0.0)),
|
|||
|
|
2 => Vec{3}((1.0, 0.0, 0.0)),
|
|||
|
|
3 => Vec{3}((0.0, 1.0, 0.0)),
|
|||
|
|
4 => Vec{3}((0.0, 0.0, 1.0))
|
|||
|
|
),
|
|||
|
|
Dict(
|
|||
|
|
1 => (1, 2, 3, 4), # Tet volume
|
|||
|
|
2 => (1, 2, 3) # Triangle surface
|
|||
|
|
),
|
|||
|
|
Dict(
|
|||
|
|
"VOLUME" => [1],
|
|||
|
|
"SURFACE" => [2]
|
|||
|
|
)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Create mixed descriptions: displacement + temperature
|
|||
|
|
descriptions = [
|
|||
|
|
CElement{Tetrahedron{4}, Lagrange{1}, VectorDOF{3}} => get_element_ids(mesh, "VOLUME"),
|
|||
|
|
CElement{Triangle{3}, Lagrange{1}, ScalarDOF} => get_element_ids(mesh, "SURFACE")
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
elements = create_celements!(mesh, descriptions)
|
|||
|
|
|
|||
|
|
@test length(elements) == 2
|
|||
|
|
|
|||
|
|
# First element (volume): 4 nodes × 3 DOFs = 12 DOFs
|
|||
|
|
@test elements[1] isa CElement{Tetrahedron{4}, Lagrange{1}, VectorDOF{3}}
|
|||
|
|
@test elements[1].dof_indices == (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
|
|||
|
|
|
|||
|
|
# Second element (surface): nodes 1,2,3 already have displacement DOFs (3 each)
|
|||
|
|
# Current implementation: reuses first DOF from each node (not ideal but works)
|
|||
|
|
# Node 1 has DOFs [1,2,3], reuse 1
|
|||
|
|
# Node 2 has DOFs [4,5,6], reuse 4
|
|||
|
|
# Node 3 has DOFs [7,8,9], reuse 7
|
|||
|
|
# TODO: Should append new DOFs (13,14,15) for different DOF type
|
|||
|
|
@test elements[2] isa CElement{Triangle{3}, Lagrange{1}, ScalarDOF}
|
|||
|
|
@test elements[2].dof_indices == (1, 4, 7)
|
|||
|
|
|
|||
|
|
# Total DOFs: 12 from first element (current behavior reuses DOFs)
|
|||
|
|
all_dofs = vcat([collect(e.dof_indices) for e in elements]...)
|
|||
|
|
@test maximum(all_dofs) == 12
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
@testset "Error Cases" begin
|
|||
|
|
mesh = TestMesh(
|
|||
|
|
Dict(1 => Vec{2}((0.0, 0.0))),
|
|||
|
|
Dict(),
|
|||
|
|
Dict("EXISTS" => [1])
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Missing element set
|
|||
|
|
@test_throws ErrorException get_element_ids(mesh, "MISSING")
|
|||
|
|
end
|
|||
|
|
end
|