mirror of
https://github.com/JuliaFEM/JuliaFEM.jl.git
synced 2026-08-06 04:21:33 +00:00
130 lines
4.9 KiB
Julia
130 lines
4.9 KiB
Julia
# 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
|