Skip to content

Commit 45b8047

Browse files
authored
Merge pull request #96 from Cthonios/bcs/overhaul
Bcs/overhaul
2 parents c534f3c + c15c1c2 commit 45b8047

17 files changed

+690
-317
lines changed

docs/src/boundary_conditions.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,27 @@
1-
# Boundary Conditions
1+
# Boundary Conditions
2+
This section describes the user facing API for boundary conditions
3+
along with the implementation details.
4+
5+
## DirichletBC
6+
We can set up dirichlet boundary conditions on a variable ```u```
7+
and sideset ```sset_1``` with a zero function as follows.
8+
```@repl
9+
using FiniteElementContainers
10+
bc_func(x, t) = 0.
11+
bc = DirichletBC(:u, :sset_1, bc_func)
12+
```
13+
Internally this is eventually converted in a ```DirichletBCContainer```
14+
15+
### API
16+
```@autodocs
17+
Modules = [FiniteElementContainers]
18+
Pages = ["DirichletBCs.jl"]
19+
Order = [:type, :function]
20+
```
21+
22+
## Boundary Condition Implementation Details
23+
```@autodocs
24+
Modules = [FiniteElementContainers]
25+
Pages = ["BoundaryConditions.jl"]
26+
Order = [:type, :function]
27+
```

docs/src/index.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,25 @@
22
CurrentModule = FiniteElementContainers
33
```
44
# FiniteElementContainers
5-
A set of containers to help in the setup of FEM codes.
5+
```FiniteElementContainers.jl``` is a package whose main purpose is to help
6+
researchers develop new finite element method (FEM) applications
7+
for both well known existing techniques and new novel strategies.
8+
9+
This package is specificially designed with the challenging aspects
10+
of computational solid mechanics in mind where meshes deform,
11+
there's path dependence, there's contact between bodies, there are potentially heterogeneous material properties, and other challenges.
12+
13+
If you're primarily interested in writing FEM applications for e.g.
14+
the Poisson equation or heat equation, there's likely more efficient packages (e.g. ```Gridap``` or ```Ferrite```) out there for this purpose in terms of memory and computational efficiency.
15+
16+
However, if you need to solve problems with multiple material models, meshes where
17+
there are mixed elements types, etc. this is likely the only julia package
18+
at the time of writing this that supports such capabilities.
19+
20+
Inspiration for the software design primarily comes from ```fenics``` and ```MOOSE```.
21+
We've specifically designed the interface to get around all the shortcomings of ```fenics```
22+
(e.g. boundary conditions are a pain, mixed element types a plain, different blocks are pain
23+
etc.)
24+
25+
Our goal is also to ensure all of our methods are next generation hardware capable. This
26+
means not only supporting things on CPUs but also GPUs (and that doesn't just mean NVIDIA).

ext/FiniteElementContainersAdaptExt.jl

Lines changed: 73 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,37 @@ module FiniteElementContainersAdaptExt
33
using Adapt
44
using FiniteElementContainers
55

6-
FiniteElementContainersAdaptExt.cpu(x) = Adapt.adapt_structure(Array, x)
6+
FiniteElementContainersAdaptExt.cpu(x) = adapt(Array, x)
77

88
# Assemblers
99
function Adapt.adapt_structure(to, asm::SparseMatrixAssembler)
10-
dof = Adapt.adapt_structure(to, asm.dof)
11-
pattern = Adapt.adapt_structure(to, asm.pattern)
12-
constraint_storage = Adapt.adapt_structure(to, asm.constraint_storage)
13-
damping_storage = Adapt.adapt_structure(to, asm.damping_storage)
14-
mass_storage = Adapt.adapt_structure(to, asm.mass_storage)
15-
residual_storage = Adapt.adapt_structure(to, asm.residual_storage)
16-
residual_unknowns = Adapt.adapt_structure(to, asm.residual_unknowns)
17-
stiffness_storage = Adapt.adapt_structure(to, asm.stiffness_storage)
1810
return SparseMatrixAssembler(
19-
dof, pattern,
20-
constraint_storage,
21-
damping_storage, mass_storage,
22-
residual_storage, residual_unknowns,
23-
stiffness_storage
11+
adapt(to, asm.dof),
12+
adapt(to, asm.pattern),
13+
adapt(to, asm.constraint_storage),
14+
adapt(to, asm.damping_storage),
15+
adapt(to, asm.mass_storage),
16+
adapt(to, asm.residual_storage),
17+
adapt(to, asm.residual_unknowns),
18+
adapt(to, asm.stiffness_storage)
2419
)
2520
end
2621

2722
function Adapt.adapt_structure(to, asm::FiniteElementContainers.SparsityPattern)
28-
Is = Adapt.adapt_structure(to, asm.Is)
29-
Js = Adapt.adapt_structure(to, asm.Js)
30-
unknown_dofs = Adapt.adapt_structure(to, asm.unknown_dofs)
31-
block_sizes = Adapt.adapt_structure(to, asm.block_sizes)
32-
block_offsets = Adapt.adapt_structure(to, asm.block_offsets)
23+
Is = adapt(to, asm.Is)
24+
Js = adapt(to, asm.Js)
25+
unknown_dofs = adapt(to, asm.unknown_dofs)
26+
block_sizes = adapt(to, asm.block_sizes)
27+
block_offsets = adapt(to, asm.block_offsets)
3328
#
34-
klasttouch = Adapt.adapt_structure(to, asm.klasttouch)
35-
csrrowptr = Adapt.adapt_structure(to, asm.csrrowptr)
36-
csrcolval = Adapt.adapt_structure(to, asm.csrcolval)
37-
csrnzval = Adapt.adapt_structure(to, asm.csrnzval)
29+
klasttouch = adapt(to, asm.klasttouch)
30+
csrrowptr = adapt(to, asm.csrrowptr)
31+
csrcolval = adapt(to, asm.csrcolval)
32+
csrnzval = adapt(to, asm.csrnzval)
3833
#
39-
csccolptr = Adapt.adapt_structure(to, asm.csccolptr)
40-
cscrowval = Adapt.adapt_structure(to, asm.cscrowval)
41-
cscnzval = Adapt.adapt_structure(to, asm.cscnzval)
34+
csccolptr = adapt(to, asm.csccolptr)
35+
cscrowval = adapt(to, asm.cscrowval)
36+
cscnzval = adapt(to, asm.cscnzval)
4237
return FiniteElementContainers.SparsityPattern(
4338
Is, Js,
4439
unknown_dofs, block_sizes, block_offsets,
@@ -48,36 +43,27 @@ function Adapt.adapt_structure(to, asm::FiniteElementContainers.SparsityPattern)
4843
end
4944

5045
# Boundary Conditions
51-
function Adapt.adapt_structure(to, bk::FiniteElementContainers.BCBookKeeping{D, S, T, V}) where {D, S, T, V}
52-
blocks = Adapt.adapt_structure(to, bk.blocks)
53-
dofs = Adapt.adapt_structure(to, bk.dofs)
54-
elements = Adapt.adapt_structure(to, bk.elements)
55-
nodes = Adapt.adapt_structure(to, bk.nodes)
56-
sides = Adapt.adapt_structure(to, bk.sides)
57-
return FiniteElementContainers.BCBookKeeping{D, S, T, typeof(blocks)}(blocks, dofs, elements, nodes, sides)
58-
end
59-
60-
function Adapt.adapt_structure(to, bc::DirichletBC{S, B, F}) where {S, B, F}
61-
bk = adapt(to, bc.bookkeeping)
62-
func = adapt(to, bc.func)
63-
return DirichletBC{S, typeof(bk), typeof(func)}(bk, func)
46+
function Adapt.adapt_structure(to, bk::FiniteElementContainers.BCBookKeeping{V}) where V
47+
blocks = adapt(to, bk.blocks)
48+
return FiniteElementContainers.BCBookKeeping{typeof(blocks)}(
49+
blocks,
50+
adapt(to, bk.dofs),
51+
adapt(to, bk.elements),
52+
adapt(to, bk.nodes),
53+
adapt(to, bk.sides)
54+
)
6455
end
6556

6657
function Adapt.adapt_structure(to, bc::FiniteElementContainers.DirichletBCContainer)
6758
return FiniteElementContainers.DirichletBCContainer(
6859
adapt(to, bc.bookkeeping),
69-
adapt(to, bc.funcs),
70-
adapt(to, bc.func_ids),
60+
# adapt(to, bc.funcs),
61+
adapt(to, bc.func),
62+
# adapt(to, bc.func_ids),
7163
adapt(to, bc.vals)
7264
)
7365
end
7466

75-
# function Adapt.adapt_structure(to, bc::FiniteElementContainers.DirichletBCCollection)
76-
# funcs = Adapt.adapt_structure(to, bc.funcs)
77-
# func_ids = Adapt.adapt_structure(to, bc.func_ids)
78-
# return FiniteElementContainers.DirichletBCCollection(funcs, func_ids)
79-
# end
80-
8167
# DofManagers
8268
function Adapt.adapt_structure(to, dof::DofManager{
8369
T, IDs,
@@ -88,19 +74,19 @@ function Adapt.adapt_structure(to, dof::DofManager{
8874
NH1Dofs, NHcurlDofs, NHdivDofs, NL2EDofs, NL2QDofs,
8975
H1Vars, HcurlVars, HdivVars, L2EVars, L2QVars
9076
}
91-
H1_bc_dofs = Adapt.adapt_structure(to, dof.H1_bc_dofs)
92-
H1_unknown_dofs = Adapt.adapt_structure(to, dof.H1_unknown_dofs)
93-
Hcurl_bc_dofs = Adapt.adapt_structure(to, dof.Hcurl_bc_dofs)
94-
Hcurl_unknown_dofs = Adapt.adapt_structure(to, dof.Hcurl_unknown_dofs)
95-
Hdiv_bc_dofs = Adapt.adapt_structure(to, dof.Hdiv_bc_dofs)
96-
Hdiv_unknown_dofs = Adapt.adapt_structure(to, dof.Hdiv_unknown_dofs)
97-
L2_element_dofs = Adapt.adapt_structure(to, dof.L2_element_dofs)
98-
L2_quadrature_dofs = Adapt.adapt_structure(to, dof.L2_quadrature_dofs)
99-
H1_vars = Adapt.adapt_structure(to, dof.H1_vars)
100-
Hcurl_vars = Adapt.adapt_structure(to, dof.Hcurl_vars)
101-
Hdiv_vars = Adapt.adapt_structure(to, dof.Hdiv_vars)
102-
L2_element_vars = Adapt.adapt_structure(to, dof.L2_element_vars)
103-
L2_quadrature_vars = Adapt.adapt_structure(to, dof.L2_quadrature_vars)
77+
H1_bc_dofs = adapt(to, dof.H1_bc_dofs)
78+
H1_unknown_dofs = adapt(to, dof.H1_unknown_dofs)
79+
Hcurl_bc_dofs = adapt(to, dof.Hcurl_bc_dofs)
80+
Hcurl_unknown_dofs = adapt(to, dof.Hcurl_unknown_dofs)
81+
Hdiv_bc_dofs = adapt(to, dof.Hdiv_bc_dofs)
82+
Hdiv_unknown_dofs = adapt(to, dof.Hdiv_unknown_dofs)
83+
L2_element_dofs = adapt(to, dof.L2_element_dofs)
84+
L2_quadrature_dofs = adapt(to, dof.L2_quadrature_dofs)
85+
H1_vars = adapt(to, dof.H1_vars)
86+
Hcurl_vars = adapt(to, dof.Hcurl_vars)
87+
Hdiv_vars = adapt(to, dof.Hdiv_vars)
88+
L2_element_vars = adapt(to, dof.L2_element_vars)
89+
L2_quadrature_vars =adapt(to, dof.L2_quadrature_vars)
10490
return DofManager{
10591
T, typeof(H1_bc_dofs),
10692
NH1Dofs, NHcurlDofs, NHdivDofs, NL2EDofs, NL2QDofs,
@@ -116,24 +102,24 @@ end
116102

117103
# Fields
118104
function Adapt.adapt_structure(to, field::L2ElementField{T, NF, V, S}) where {T, NF, V, S}
119-
vals = Adapt.adapt_structure(to, field.vals)
105+
vals = adapt(to, field.vals)
120106
return L2ElementField{T, NF, typeof(vals), S}(vals)
121107
end
122108

123109
function Adapt.adapt_structure(to, field::H1Field{T, NF, V, S}) where {T, NF, V, S}
124-
vals = Adapt.adapt_structure(to, field.vals)
110+
vals = adapt(to, field.vals)
125111
return H1Field{T, NF, typeof(vals), S}(vals)
126112
end
127113

128114
# Function spaces
129115
function Adapt.adapt_structure(to, fspace::FunctionSpace)
130-
coords = Adapt.adapt_structure(to, fspace.coords)
131-
elem_conns = Adapt.adapt_structure(to, fspace.elem_conns)
132-
elem_id_maps = Adapt.adapt_structure(to, fspace.elem_id_maps)
133-
ref_fes = Adapt.adapt_structure(to, fspace.ref_fes)
134-
sideset_elems = Adapt.adapt_structure(to, fspace.sideset_elems)
135-
sideset_nodes = Adapt.adapt_structure(to, fspace.sideset_nodes)
136-
sideset_sides = Adapt.adapt_structure(to, fspace.sideset_sides)
116+
coords = adapt(to, fspace.coords)
117+
elem_conns = adapt(to, fspace.elem_conns)
118+
elem_id_maps = adapt(to, fspace.elem_id_maps)
119+
ref_fes = adapt(to, fspace.ref_fes)
120+
sideset_elems = adapt(to, fspace.sideset_elems)
121+
sideset_nodes = adapt(to, fspace.sideset_nodes)
122+
sideset_sides = adapt(to, fspace.sideset_sides)
137123
return FunctionSpace(
138124
coords,
139125
elem_conns, elem_id_maps,
@@ -142,78 +128,52 @@ function Adapt.adapt_structure(to, fspace::FunctionSpace)
142128
)
143129
end
144130

145-
# # Mesh - won't work, symbols are mutable
146-
# # function Adapt.adapt_structure(to, mesh::UnstructuredMesh)
147-
# # nodal_coords = Adapt.adapt_structure(to, mesh.nodal_coords)
148-
# # element_block_names = Adapt.adapt_structure(to, mesh.element_block_names)
149-
# # element_types = Adapt.adapt_structure(to, mesh.element_types)
150-
# # element_conns = Adapt.adapt_structure(to, mesh.element_conns)
151-
# # element_id_maps = Adapt.adapt_structure(to, mesh.element_id_maps)
152-
# # nodeset_nodes = Adapt.adapt_structure(to, mesh.nodeset_nodes)
153-
# # return UnstructuredMesh(
154-
# # nodal_coords, element_block_names,
155-
# # element_types, element_conns,
156-
# # element_id_maps, nodeset_nodes
157-
# # )
158-
# # end
159-
160-
# # Functions
131+
# Functions
161132
function Adapt.adapt_structure(to, var::ScalarFunction)
162133
syms = names(var)
163-
fspace = Adapt.adapt_structure(to, var.fspace)
134+
fspace = adapt(to, var.fspace)
164135
return ScalarFunction{syms, typeof(fspace)}(fspace)
165136
end
166137

167138
function Adapt.adapt_structure(to, var::SymmetricTensorFunction)
168139
syms = names(var)
169-
fspace = Adapt.adapt_structure(to, var.fspace)
140+
fspace = adapt(to, var.fspace)
170141
return SymmetricTensorFunction{syms, typeof(fspace)}(fspace)
171142
end
172143

173144
function Adapt.adapt_structure(to, var::TensorFunction)
174145
syms = names(var)
175-
fspace = Adapt.adapt_structure(to, var.fspace)
146+
fspace = adapt(to, var.fspace)
176147
return TensorFunction{syms, typeof(fspace)}(fspace)
177148
end
178149

179150
function Adapt.adapt_structure(to, var::VectorFunction)
180151
syms = names(var)
181-
fspace = Adapt.adapt_structure(to, var.fspace)
152+
fspace = adapt(to, var.fspace)
182153
return VectorFunction{syms, typeof(fspace)}(fspace)
183154
end
184155

185156
function Adapt.adapt_structure(to, p::FiniteElementContainers.Parameters)
186157
return FiniteElementContainers.Parameters(
187-
Adapt.adapt_structure(to, p.dirichlet_bcs),
188-
Adapt.adapt_structure(to, p.neumann_bcs),
189-
Adapt.adapt_structure(to, p.times),
190-
Adapt.adapt_structure(to, p.physics),
191-
Adapt.adapt_structure(to, p.properties),
192-
Adapt.adapt_structure(to, p.state_old),
193-
Adapt.adapt_structure(to, p.state_new),
194-
Adapt.adapt_structure(to, p.h1_dbcs),
195-
Adapt.adapt_structure(to, p.h1_field)
158+
adapt(to, p.dirichlet_bcs),
159+
adapt(to, p.neumann_bcs),
160+
adapt(to, p.times),
161+
adapt(to, p.physics),
162+
adapt(to, p.properties),
163+
adapt(to, p.state_old),
164+
adapt(to, p.state_new),
165+
adapt(to, p.h1_dbcs),
166+
adapt(to, p.h1_field)
196167
)
197168
end
198169

199170
function Adapt.adapt_structure(to, p::FiniteElementContainers.TimeStepper)
200171
return FiniteElementContainers.TimeStepper(
201-
Adapt.adapt_structure(to, p.time_start),
202-
Adapt.adapt_structure(to, p.time_end),
203-
Adapt.adapt_structure(to, p.time_current),
204-
Adapt.adapt_structure(to, p.Δt)
172+
adapt(to, p.time_start),
173+
adapt(to, p.time_end),
174+
adapt(to, p.time_current),
175+
adapt(to, p.Δt)
205176
)
206177
end
207178

208-
# # # # """
209-
# # # # Need to use SparseArrays.allowscalar(false)
210-
# # # # """
211-
# # # # function Adapt.adapt_structure(to, assembler::FiniteElementContainers.StaticAssembler)
212-
# # # # I = FiniteElementContainers.int_type(assembler)
213-
# # # # F = FiniteElementContainers.float_type(assembler)
214-
# # # # R = Adapt.adapt_structure(to, assembler.R)
215-
# # # # K = Adapt.adapt_structure(to, assembler.K)
216-
# # # # return FiniteElementContainers.StaticAssembler{I, F, typeof(R), typeof(K)}(R, K)
217-
# # # # end
218-
219179
end # module

ext/FiniteElementContainersCUDAExt.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ using CUDA
55
using FiniteElementContainers
66
using KernelAbstractions
77

8-
FiniteElementContainers.gpu(x) = Adapt.adapt_structure(CuArray, x)
8+
FiniteElementContainers.gpu(x) = adapt(CuArray, x)
99

1010
# this method need to have the assembler initialized first
1111
# if the stored values in asm.pattern.cscnzval or zero
1212
# CUDA will error out
1313
function CUDA.CUSPARSE.CuSparseMatrixCSC(asm::SparseMatrixAssembler)
1414
@assert typeof(get_backend(asm)) <: CUDABackend "Assembler is not on a CUDA device"
15-
@assert length(asm.pattern.cscnzval) > 0 "Need to assemble the assembler once with SparseArrays.sparse!(assembler)"
16-
@assert all(x -> x != zero(eltype(asm.pattern.cscnzval)), asm.pattern.cscnzval) "Need to assemble the assembler once with SparseArrays.sparse!(assembler)"
15+
# TODO these assert statements are failing for multi dof problems yet
16+
# they are doing the right thing
17+
# @assert length(asm.pattern.cscnzval) > 0 "Need to assemble the assembler once with SparseArrays.sparse!(assembler)"
18+
# @assert all(x -> x != zero(eltype(asm.pattern.cscnzval)), asm.pattern.cscnzval) "Need to assemble the assembler once with SparseArrays.sparse!(assembler)"
1719
n_dofs = FiniteElementContainers.num_unknowns(asm.dof)
1820
return CUDA.CUSPARSE.CuSparseMatrixCSC(
1921
asm.pattern.csccolptr,

0 commit comments

Comments
 (0)