mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-06 04:21:33 +00:00
test(assemblers): add partitioning helper unit tests
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# This file is a part of JuliaFEM.
|
||||
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md
|
||||
|
||||
"""
|
||||
Pure partitioning helpers (`brick_hex_partition_slabs`, `referenced_global_dofs`,
|
||||
`element_counts_by_part`) — correctness vs structured Hex8 ordering and DOF overlap
|
||||
at slab interfaces.
|
||||
"""
|
||||
|
||||
using Test
|
||||
using JuliaFEM
|
||||
using JuliaFEM: DOFBasedCOOAssembler, DOFBasedCOOCache
|
||||
using JuliaFEM: apply_K!, apply_K_contributions!
|
||||
using JuliaFEM: create_elements!, @DOFSet, DOF, Displacement, Vertex
|
||||
using JuliaFEM: element_indices_for_part, validate_partition
|
||||
using JuliaFEM: brick_hex_slab_upper, brick_hex_partition_slabs
|
||||
using JuliaFEM: element_counts_by_part, referenced_global_dofs
|
||||
using JuliaFEM: sum_element_dof_slots, mark_referenced_dofs!, collect_true_indices!
|
||||
using JuliaFEM: fill_referenced_dof_indices!, ghost_dof_mask!, node_partition_owner_min!
|
||||
using JuliaFEM: mark_owned_vertex_field_dofs!
|
||||
using Random
|
||||
using Tensors
|
||||
|
||||
@testset "brick_hex_partition_slabs rejects nparts > ncells on axis" begin
|
||||
@test_throws ArgumentError brick_hex_partition_slabs(1, 1, 1, 4; axis = :x)
|
||||
@test_throws ArgumentError brick_hex_partition_slabs(3, 2, 2, 4; axis = :y)
|
||||
end
|
||||
|
||||
@testset "brick_hex_slab_upper widths" begin
|
||||
@test brick_hex_slab_upper(5, 3) == [2, 4, 5]
|
||||
@test brick_hex_slab_upper(6, 3) == [2, 4, 6]
|
||||
@test brick_hex_slab_upper(1, 4) == [1, 1, 1, 1]
|
||||
end
|
||||
|
||||
@testset "brick_hex_partition_slabs counts + ids" begin
|
||||
nx, ny, nz = 4, 2, 2
|
||||
ne = nx * ny * nz
|
||||
layout = brick_hex_partition_slabs(nx, ny, nz, 3; axis = :x)
|
||||
validate_partition(layout, ne)
|
||||
d = element_counts_by_part(layout)
|
||||
@test sort!(collect(keys(d))) == [1, 2, 3]
|
||||
@test sum(values(d)) == ne
|
||||
# x-slab: part widths along i ≈ 2+1+1 on nx=4
|
||||
@test d[1] == 2 * ny * nz
|
||||
@test d[2] == d[3] == 1 * ny * nz
|
||||
end
|
||||
|
||||
@testset "referenced_global_dofs overlap at x-slabs" begin
|
||||
nx, ny, nz = 4, 2, 2
|
||||
mesh = create_structured_box_mesh(Hex8; nx = nx, ny = ny, nz = nz)
|
||||
S = @DOFSet{u::DOF{Displacement{3}, Vertex}}
|
||||
elements, _handler = create_elements!(mesh, Element{Hex8, Lagrange{1}, S})
|
||||
layout = brick_hex_partition_slabs(nx, ny, nz, 2; axis = :x)
|
||||
e1 = element_indices_for_part(layout, 1)
|
||||
e2 = element_indices_for_part(layout, 2)
|
||||
@assert isempty(intersect(e1, e2))
|
||||
d1 = referenced_global_dofs(elements, e1)
|
||||
d2 = referenced_global_dofs(elements, e2)
|
||||
overlap = intersect(Set(d1), Set(d2))
|
||||
@test !isempty(overlap)
|
||||
end
|
||||
|
||||
@testset "slab-partitioned contributions sum to apply_K!" begin
|
||||
Random.seed!(20260510)
|
||||
nx, ny, nz = 3, 4, 2
|
||||
mesh = create_structured_box_mesh(Hex8; nx = nx, ny = ny, nz = nz)
|
||||
material = LinearElastic(E = 210e9, ν = 0.3)
|
||||
kernel = ContinuumKernel(ContinuumFormulation{FullThreeD}(),
|
||||
material, Displacement{3}())
|
||||
S = @DOFSet{u::DOF{Displacement{3}, Vertex}}
|
||||
elements, dof_mgr = create_elements!(mesh, Element{Hex8, Lagrange{1}, S})
|
||||
asm = DOFBasedCOOAssembler()
|
||||
cache = DOFBasedCOOCache(elements, dof_mgr, mesh, kernel)
|
||||
|
||||
layout = brick_hex_partition_slabs(nx, ny, nz, 3; axis = :y) # ny ≥ nparts
|
||||
validate_partition(layout, length(elements))
|
||||
|
||||
n = cache.ndofs
|
||||
x = randn(n)
|
||||
y_full = zeros(n)
|
||||
apply_K!(y_full, cache, asm, kernel, mesh, x)
|
||||
|
||||
y_sum = zeros(n)
|
||||
for part in 1:3
|
||||
ep = element_indices_for_part(layout, part)
|
||||
apply_K_contributions!(y_sum, cache, asm, kernel, mesh, x, ep)
|
||||
end
|
||||
@test y_sum ≈ y_full rtol = 1e-11 atol = 1e-11
|
||||
end
|
||||
|
||||
@testset "fill_referenced_dof_indices! agrees with referenced_global_dofs" begin
|
||||
nx, ny, nz = 3, 2, 2
|
||||
mesh = create_structured_box_mesh(Hex8; nx = nx, ny = ny, nz = nz)
|
||||
S = @DOFSet{u::DOF{Displacement{3}, Vertex}}
|
||||
elements, handler = create_elements!(mesh, Element{Hex8, Lagrange{1}, S})
|
||||
layout = brick_hex_partition_slabs(nx, ny, nz, 2; axis = :x)
|
||||
ep = element_indices_for_part(layout, 1)
|
||||
ndof = handler.total_dofs
|
||||
|
||||
ref_alloc = referenced_global_dofs(elements, ep)
|
||||
|
||||
mask = falses(ndof)
|
||||
buf = Vector{Int}(undef, ndof)
|
||||
n = fill_referenced_dof_indices!(buf, mask, elements, ep, ndof)
|
||||
@test sort(buf[1:n]) == ref_alloc
|
||||
end
|
||||
|
||||
@testset "ghost mask nonempty for higher partition (min-node ownership)" begin
|
||||
nx, ny, nz = 4, 2, 2
|
||||
mesh = create_structured_box_mesh(Hex8; nx = nx, ny = ny, nz = nz)
|
||||
S = @DOFSet{u::DOF{Displacement{3}, Vertex}}
|
||||
elements, handler = create_elements!(mesh, Element{Hex8, Lagrange{1}, S})
|
||||
layout = brick_hex_partition_slabs(nx, ny, nz, 2; axis = :x)
|
||||
ndof = handler.total_dofs
|
||||
nnodes = length(mesh.nodes)
|
||||
|
||||
node_own = Vector{Int}(undef, nnodes)
|
||||
node_partition_owner_min!(node_own, layout, mesh)
|
||||
|
||||
ep2 = element_indices_for_part(layout, 2)
|
||||
refm = falses(ndof)
|
||||
ownm = falses(ndof)
|
||||
gh = falses(ndof)
|
||||
|
||||
mark_referenced_dofs!(refm, elements, ep2, ndof)
|
||||
mark_owned_vertex_field_dofs!(ownm, handler, node_own, 2)
|
||||
ghost_dof_mask!(gh, refm, ownm)
|
||||
@test any(gh)
|
||||
end
|
||||
Reference in New Issue
Block a user