Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subdomain support for Interfaces + AllocCheck tests for iterators + Sparsitypattern bugfix #1108

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
64eab2d
homeoffice push
AbdAlazezAhmed Nov 7, 2024
050f226
oopsie
AbdAlazezAhmed Nov 7, 2024
ec96036
use Set
AbdAlazezAhmed Nov 8, 2024
6f4f040
now tests shall pass
AbdAlazezAhmed Nov 8, 2024
932b366
runic
AbdAlazezAhmed Nov 8, 2024
b14251e
Init iterator with subdofhandlers
AbdAlazezAhmed Nov 8, 2024
4659dba
oopsie
AbdAlazezAhmed Nov 11, 2024
6044216
now sets are moved from there to here
AbdAlazezAhmed Nov 11, 2024
d0fc3b5
some tests
AbdAlazezAhmed Nov 11, 2024
79d55f4
more tests
AbdAlazezAhmed Nov 11, 2024
9b1cbdd
error paths + trying to test for allocs
AbdAlazezAhmed Nov 11, 2024
be27c62
Make Runic happy
AbdAlazezAhmed Nov 11, 2024
cf7c08e
make alloccheck happy
AbdAlazezAhmed Nov 12, 2024
0b68251
revert oopsie
AbdAlazezAhmed Nov 12, 2024
19f11ea
next: get rid of dynamic dispatches
AbdAlazezAhmed Nov 12, 2024
84a1a14
home office push
AbdAlazezAhmed Nov 12, 2024
261c50e
Bad workarounds, think about them tomorrow :P
AbdAlazezAhmed Nov 13, 2024
4a22dea
add cell type to SubDofHandler as typeparameter
AbdAlazezAhmed Nov 13, 2024
cba4e1d
don't enoforce no-allocs for iterators using DofHandler
AbdAlazezAhmed Nov 13, 2024
5c6495e
undo constrainthandler voodoo
AbdAlazezAhmed Nov 13, 2024
8f281d7
tests for CellIterator
AbdAlazezAhmed Nov 13, 2024
7b97fed
Construct FacetIterator without a set
AbdAlazezAhmed Nov 13, 2024
dcab007
Tests for FacetIterator
AbdAlazezAhmed Nov 13, 2024
a38ed02
Fix bug where boundary sets are broken for mixed grids
AbdAlazezAhmed Nov 13, 2024
94948c2
move AllocCheck to test deps
AbdAlazezAhmed Nov 13, 2024
be9009a
please
AbdAlazezAhmed Nov 13, 2024
0143341
enable interfacevalues test with mixed grid
AbdAlazezAhmed Nov 13, 2024
720d373
avoid looping over interfaces twice for subdomains in sparsitypatterns
AbdAlazezAhmed Nov 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"
TaskLocalValues = "ed4db957-447d-4319-bfb6-7fa9ae7ecf34"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"

[targets]
test = ["BlockArrays", "Downloads", "FerriteGmsh", "ForwardDiff", "Gmsh", "IterativeSolvers", "Metis", "NBInclude", "OhMyThreads", "ProgressMeter", "Random", "SHA", "TaskLocalValues", "Test", "TimerOutputs", "Logging"]
test = ["BlockArrays", "Downloads", "FerriteGmsh", "ForwardDiff", "Gmsh", "IterativeSolvers", "Metis", "NBInclude", "OhMyThreads", "ProgressMeter", "Random", "SHA", "TaskLocalValues", "Test", "TimerOutputs", "Logging", "AllocCheck"]
4 changes: 2 additions & 2 deletions src/Dofs/DofHandler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Access some grid representation for the dof handler.
"""
get_grid(dh::AbstractDofHandler)

mutable struct SubDofHandler{DH} <: AbstractDofHandler
mutable struct SubDofHandler{DH, CT} <: AbstractDofHandler
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes us avoid dynamic dispatch for getting nodes/coords

# From constructor
const dh::DH
const cellset::OrderedSet{Int}
Expand Down Expand Up @@ -68,7 +68,7 @@ function SubDofHandler(dh::DH, cellset::AbstractVecOrSet{Int}) where {DH <: Abst
end
end
# Construct and insert into the parent dh
sdh = SubDofHandler{typeof(dh)}(dh, convert_to_orderedset(cellset), Symbol[], Interpolation[], Int[], -1)
sdh = SubDofHandler{typeof(dh), CT}(dh, convert_to_orderedset(cellset), Symbol[], Interpolation[], Int[], -1)
push!(dh.subdofhandlers, sdh)
return sdh
end
Expand Down
2 changes: 1 addition & 1 deletion src/Dofs/DofRenumbering.jl
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ function compute_renumber_permutation(dh::DofHandler, _, order::DofOrder.Compone
for sdh in dh.subdofhandlers
dof_ranges = [dof_range(sdh, f) for f in eachindex(sdh.field_names)]
global_idxs = [findfirst(x -> x === f, dh.field_names) for f in sdh.field_names]
for cell in CellIterator(dh, sdh.cellset, flags)
for cell in CellIterator(sdh, flags)
cdofs = celldofs(cell)
for (local_idx, global_idx) in pairs(global_idxs)
rng = dof_ranges[local_idx]
Expand Down
120 changes: 60 additions & 60 deletions src/Dofs/sparsity_pattern.jl
Original file line number Diff line number Diff line change
Expand Up @@ -446,51 +446,53 @@ function _coupling_to_local_dof_coupling(dh::DofHandler, coupling::AbstractMatri
sz == size(coupling, 2) || error("coupling not square")

# Return one matrix per (potential) sub-domain
outs = Matrix{Bool}[]
nsdh = length(dh.subdofhandlers)
outs = Matrix{Matrix{Bool}}(undef, nsdh, nsdh)
field_dims = map(fieldname -> n_components(dh, fieldname), dh.field_names)

for sdh in dh.subdofhandlers
out = zeros(Bool, ndofs_per_cell(sdh), ndofs_per_cell(sdh))
push!(outs, out)
for (sdh1_idx, sdh1) in enumerate(dh.subdofhandlers)
for (sdh2_idx, sdh2) in enumerate(dh.subdofhandlers)
out = zeros(Bool, ndofs_per_cell(sdh1), ndofs_per_cell(sdh2))
outs[sdh1_idx, sdh2_idx] = out

dof_ranges = [dof_range(sdh, f) for f in sdh.field_names]
global_idxs = [findfirst(x -> x === f, dh.field_names) for f in sdh.field_names]
dof_ranges_here = [dof_range(sdh1, f) for f in sdh1.field_names]
dof_ranges_there = [dof_range(sdh2, f) for f in sdh2.field_names]
global_idxs_here = [findfirst(x -> x === f, dh.field_names) for f in sdh1.field_names]
global_idxs_there = [findfirst(x -> x === f, dh.field_names) for f in sdh2.field_names]

if sz == length(dh.field_names) # Coupling given by fields
for (j, jrange) in pairs(dof_ranges), (i, irange) in pairs(dof_ranges)
out[irange, jrange] .= coupling[global_idxs[i], global_idxs[j]]
end
elseif sz == sum(field_dims) # Coupling given by components
component_offsets = pushfirst!(cumsum(field_dims), 0)
for (jf, jrange) in pairs(dof_ranges), (j, J) in pairs(jrange)
jc = mod1(j, field_dims[global_idxs[jf]]) + component_offsets[global_idxs[jf]]
for (i_f, irange) in pairs(dof_ranges), (i, I) in pairs(irange)
ic = mod1(i, field_dims[global_idxs[i_f]]) + component_offsets[global_idxs[i_f]]
out[I, J] = coupling[ic, jc]
if sz == length(dh.field_names) # Coupling given by fields
for (j, jrange) in pairs(dof_ranges_there), (i, irange) in pairs(dof_ranges_here)
out[irange, jrange] .= coupling[global_idxs_here[i], global_idxs_there[j]]
end
elseif sz == sum(field_dims) # Coupling given by components
component_offsets = pushfirst!(cumsum(field_dims), 0)
for (jf, jrange) in pairs(dof_ranges_there), (j, J) in pairs(jrange)
jc = mod1(j, field_dims[global_idxs_there[jf]]) + component_offsets[global_idxs_there[jf]]
for (i_f, irange) in pairs(dof_ranges_here), (i, I) in pairs(irange)
ic = mod1(i, field_dims[global_idxs_here[i_f]]) + component_offsets[global_idxs_here[i_f]]
out[I, J] = coupling[ic, jc]
end
end
elseif sz == ndofs_per_cell(sdh1) # Coupling given by template local matrix
# TODO: coupling[fieldhandler_idx] if different template per subddomain
out .= coupling
else
error("could not create coupling")
end
elseif sz == ndofs_per_cell(sdh) # Coupling given by template local matrix
# TODO: coupling[fieldhandler_idx] if different template per subddomain
out .= coupling
else
error("could not create coupling")
end
end
return outs
end

function _add_cell_entries!(
sp::AbstractSparsityPattern, dh::DofHandler, ch::Union{ConstraintHandler, Nothing},
keep_constrained::Bool, coupling::Union{Vector{<:AbstractMatrix{Bool}}, Nothing},
keep_constrained::Bool, coupling::Union{<:AbstractMatrix{<:AbstractMatrix{Bool}}, Nothing},
)
# Add all connections between dofs for every cell while filtering based
# on a) constraints, and b) field/dof coupling.
cc = CellCache(dh)
for (sdhi, sdh) in pairs(dh.subdofhandlers)
set = BitSet(sdh.cellset)
coupling === nothing || (coupling_sdh = coupling[sdhi])
for cell_id in set
reinit!(cc, cell_id)
for cc in CellIterator(sdh)
for (i, row) in pairs(cc.dofs)
# a) check constraint for row
!keep_constrained && haskey(ch.dofmapping, row) && continue
Expand Down Expand Up @@ -606,41 +608,39 @@ function _add_interface_entries!(
interface_coupling::AbstractMatrix{Bool},
)
couplings = _coupling_to_local_dof_coupling(dh, interface_coupling)
for ic in InterfaceIterator(dh, topology)
# TODO: This looks like it can be optimized for the common case where
# the cells are in the same subdofhandler
sdhs_idx = dh.cell_to_subdofhandler[cellid.([ic.a, ic.b])]
sdhs = dh.subdofhandlers[sdhs_idx]
to_check = Dict{Int, Vector{Int}}()
for (i, sdh) in pairs(sdhs)
sdh_idx = sdhs_idx[i]
coupling_sdh = couplings[sdh_idx]
for cell_field in sdh.field_names
dofrange1 = dof_range(sdh, cell_field)
cell_dofs = celldofs(sdh_idx == 1 ? ic.a : ic.b)
cell_field_dofs = @view cell_dofs[dofrange1]
for neighbor_field in sdh.field_names
sdh2 = sdhs[i == 1 ? 2 : 1]
neighbor_field ∈ sdh2.field_names || continue
dofrange2 = dof_range(sdh2, neighbor_field)
neighbor_dofs = celldofs(sdh_idx == 2 ? ic.a : ic.b)
neighbor_field_dofs = @view neighbor_dofs[dofrange2]

empty!(to_check)
for (j, dof_j) in enumerate(dofrange2)
for (i, dof_i) in enumerate(dofrange1)
coupling_sdh[dof_i, dof_j] || continue
push!(get!(Vector{Int}, to_check, j), i)
for (_i, sdh1) in enumerate(dh.subdofhandlers)
for (_j, sdh2) in enumerate(dh.subdofhandlers)
for ic in InterfaceIterator(sdh1, sdh2, topology)
# TODO: This looks like it can be optimized for the common case where
# the cells are in the same subdofhandler
to_check = Dict{Int, Vector{Int}}()
coupling_sdh = couplings[_i, _j]
for cell_field in sdh1.field_names
dofrange1 = dof_range(sdh1, cell_field)
cell_dofs = celldofs(ic.a)
cell_field_dofs = @view cell_dofs[dofrange1]
for neighbor_field in sdh1.field_names
neighbor_field ∈ sdh2.field_names || continue
dofrange2 = dof_range(sdh2, neighbor_field)
neighbor_dofs = celldofs(ic.b)
neighbor_field_dofs = @view neighbor_dofs[dofrange2]

empty!(to_check)
for (j, dof_j) in enumerate(dofrange2)
for (i, dof_i) in enumerate(dofrange1)
coupling_sdh[dof_i, dof_j] || continue
push!(get!(Vector{Int}, to_check, j), i)
end
end
end

for (j, is) in to_check
# Avoid coupling the shared dof in continuous interpolations as cross-element. They're coupled in the local coupling matrix.
neighbor_field_dofs[j] ∈ cell_dofs && continue
for i in is
cell_field_dofs[i] ∈ neighbor_dofs && continue
_add_interface_entry(sp, cell_field_dofs, neighbor_field_dofs, i, j, keep_constrained, ch)
_add_interface_entry(sp, neighbor_field_dofs, cell_field_dofs, j, i, keep_constrained, ch)
for (j, is) in to_check
# Avoid coupling the shared dof in continuous interpolations as cross-element. They're coupled in the local coupling matrix.
neighbor_field_dofs[j] ∈ cell_dofs && continue
for i in is
cell_field_dofs[i] ∈ neighbor_dofs && continue
_add_interface_entry(sp, cell_field_dofs, neighbor_field_dofs, i, j, keep_constrained, ch)
_add_interface_entry(sp, neighbor_field_dofs, cell_field_dofs, j, i, keep_constrained, ch)
end
end
end
end
Expand Down
7 changes: 7 additions & 0 deletions src/Ferrite.jl
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ struct FacetIndex <: BoundaryIndex
idx::Tuple{Int, Int} # cell and side
end

"""
An `InterfaceIndex` wraps an (Int, Int, Int, Int) and defines an interface by pointing to a (cell_here, facet_here, cell_there, facet_there).
"""
struct InterfaceIndex <: BoundaryIndex
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be unnecessary but I think it's convenient to have it

idx::Tuple{Int, Int, Int, Int} # cell - side - cell - side
end

const AbstractVecOrSet{T} = Union{AbstractSet{T}, AbstractVector{T}}
const IntegerCollection = AbstractVecOrSet{<:Integer}

Expand Down
10 changes: 5 additions & 5 deletions src/Grid/grid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -641,20 +641,20 @@ boundaryfunction(::Type{EdgeIndex}) = edges
boundaryfunction(::Type{VertexIndex}) = vertices
boundaryfunction(::Type{FacetIndex}) = facets

for INDEX in (:VertexIndex, :EdgeIndex, :FaceIndex, :FacetIndex)
for INDEX in (:VertexIndex, :EdgeIndex, :FaceIndex, :FacetIndex, :InterfaceIndex)
@eval begin
#Constructor
($INDEX)(a::Int, b::Int) = ($INDEX)((a, b))
($INDEX)(args...) = ($INDEX)((args...,))

Base.getindex(I::($INDEX), i::Int) = I.idx[i]

#To be able to do a,b = faceidx
Base.iterate(I::($INDEX), state::Int = 1) = (state == 3) ? nothing : (I[state], state + 1)
Base.iterate(I::($INDEX), state::Int = 1) = (state == length(I.idx) + 1) ? nothing : (I[state], state + 1)

# Necessary to check if, e.g. `(cellid, faceidx) in faceset`
Base.isequal(x::$INDEX, y::$INDEX) = x.idx == y.idx
Base.isequal(x::Tuple{Int, Int}, y::$INDEX) = x[1] == y.idx[1] && x[2] == y.idx[2]
Base.isequal(y::$INDEX, x::Tuple{Int, Int}) = x[1] == y.idx[1] && x[2] == y.idx[2]
Base.isequal(x::NTuple{N, Int}, y::$INDEX) where {N} = all(i -> x[i] == y.idx[i], 1:N)
Base.isequal(y::$INDEX, x::NTuple{N, Int}) where {N} = all(i -> x[i] == y.idx[i], 1:N)
Base.hash(x::$INDEX, h::UInt) = hash(x.idx, h)
end
end
Expand Down
1 change: 1 addition & 0 deletions src/Grid/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ function _create_boundaryset(f::Function, grid::AbstractGrid, top::ExclusiveTopo
cell_idx = ff_nh_idx[1]
facet_nr = ff_nh_idx[2]
cell = getcells(grid, cell_idx)
length(facets(cell)) < facet_nr && continue
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix for #1110

facet_nodes = facets(cell)[facet_nr]
for (subentity_idx, subentity_nodes) in pairs(boundaryfunction(BI)(cell))
if Base.all(n -> n in facet_nodes, subentity_nodes)
Expand Down
1 change: 1 addition & 0 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export
EdgeIndex,
VertexIndex,
FacetIndex,
InterfaceIndex,
geometric_interpolation,
ExclusiveTopology,
getneighborhood,
Expand Down
Loading
Loading