Skip to content

Commit

Permalink
update docs according to review and discussions
Browse files Browse the repository at this point in the history
  • Loading branch information
koehlerson committed Oct 16, 2021
1 parent 43c1960 commit 7f5e7db
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 34 deletions.
75 changes: 72 additions & 3 deletions docs/src/reference/grid.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
DocTestSetup = :(using Ferrite)
```

# Grid
# Grid & AbstractGrid

## Grid

```@docs
Node
Expand All @@ -14,7 +16,7 @@ FaceIndex
Grid
```

## Utility Functions
### Utility Functions

```@docs
getcells
Expand All @@ -23,18 +25,85 @@ getnodes
getnnodes
Ferrite.nnodes_per_cell
getcellset
getcellsets
getnodeset
getnodesets
getfaceset
getfacesets
getedgeset
getedgesets
getvertexset
getvertexsets
compute_vertex_values
transform!
getcoordinates
getcoordinates!
```

## Grid Sets Utility
### Grid Sets Utility

```@docs
addcellset!
addfaceset!
addnodeset!
```

## AbstractGrid

It can be very useful to use a grid type for a certain special case, e.g. mixed cell types, adaptivity, IGA, etc.
In order to define your own `<: AbstractGrid` you need to fulfill the `AbstractGrid` interface.
In case that certain structures are preserved from the `Ferrite.Grid` type, you don't need to dispatch on your own type, but rather rely on the fallback `AbstractGrid` dispatch.

### Example

As a starting point, we choose a minimal working example from the test suite:

```julia
struct SmallGrid{dim,N,C<:Ferrite.AbstractCell} <: Ferrite.AbstractGrid{dim}
nodes_test::Vector{NTuple{dim,Float64}}
cells_test::NTuple{N,C}
end
```

Here, the names of the fields as well as their underlying datastructure changed compared to the `Grid` type. This would lead to the fact, that any usage
with the utility functions and DoF management will not work. So, we need to feed into the interface how to handle this subtyped datastructure.
We start with the utility functions that are associated with the cells of the grid:

```julia
Ferrite.getcells(grid::SmallGrid) = grid.cells_test
Ferrite.getcells(grid::SmallGrid, v::Union{Int, Vector{Int}}) = grid.cells_test[v]
Ferrite.getncells(grid::SmallGrid{dim,N}) where {dim,N} = N
Ferrite.getcelltype(grid::SmallGrid) = eltype(grid.cells_test)
Ferrite.getcelltype(grid::SmallGrid, i::Int) = typeof(grid.cells_test[i])
```

Next, we define some helper functions that take care of the node handling.

```julia
Ferrite.getnodes(grid::SmallGrid) = grid.nodes_test
Ferrite.getnodes(grid::SmallGrid, v::Union{Int, Vector{Int}}) = grid.nodes_test[v]
Ferrite.getnnodes(grid::SmallGrid) = length(grid.nodes_test)
Ferrite.nnodes_per_cell(grid::SmallGrid, i::Int=1) = Ferrite.nnodes(grid.cells_test[i])
Ferrite.n_faces_per_cell(grid::SmallGrid) = nfaces(eltype(grid.cells_test))
```

Finally, we define `getcoordinates`, which is an important function, if we want to assemble a problem.
The transformation from the reference space to the physical one requires information about the coordinates in order to construct the
Jacobian. The return of this part is later handled over to `reinit!`.

```julia
function Ferrite.getcoordinates!(x::Vector{Vec{dim,T}}, grid::SmallGrid, cell::Int) where {dim,T}
for i in 1:length(x)
x[i] = Vec{dim,T}(grid.nodes_test[grid.cells_test[cell].nodes[i]])
end
end

function Ferrite.getcoordinates(grid::SmallGrid{dim}, cell::Int) where dim
nodeidx = grid.cells_test[cell].nodes
return [Vec{dim,Float64}(grid.nodes_test[i]) for i in nodeidx]::Vector{Vec{dim,Float64}}
end
```

Now, you would be able to assemble the heat equation example over the new custom `SmallGrid` type.
Note that this particular subtype isn't able to handle boundary entity sets and so, you can't describe boundaries with it.
In order to use boundaries, e.g. for Dirichlet constraints in the ConstraintHandler, you would need to dispatch the `AbstractGrid` sets utility functions on `SmallGrid`.
110 changes: 79 additions & 31 deletions src/Grid/grid.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ abstract type AbstractCell{dim,N,M} end
"""
Cell{dim,N,M} <: AbstractCell{dim,N,M}
A `Cell` is a sub-domain defined by a collection of `Node`s as it's vertices.
However, a `cell` is not defined by the nodes but rather by the node ids
However, a `cell` is not defined by the nodes but rather by the global node ids
# Fields
- `nodes::Ntuple{N,Int}`: N-tuple that stores the node ids
Expand Down Expand Up @@ -85,14 +85,16 @@ abstract type AbstractGrid{dim} end
Grid{dim, C<:AbstractCell, T<:Real} <: AbstractGrid}
A `Grid` is a collection of `Cells` and `Node`s which covers the computational domain, together with Sets of cells, nodes and faces.
There are multiple helper structures to apply boundary conditions or define subdomains. They are gathered in the `cellsets`, `nodesets`,
`facesets`, `edgesets` and `vertexsets`.
# Fields
- `cells::Vector{C}`: stores all cells of the grid
- `nodes::Vector{Node{dim,T}}`: stores the `dim` dimensional nodes of the grid
- `cellsets::Dict{String,Set{CellIndex}}`: maps a `String` key to a `Set` of cell ids
- `cellsets::Dict{String,Set{Int}}`: maps a `String` key to a `Set` of cell ids
- `nodesets::Dict{String,Set{Int}}`: maps a `String` key to a `Set` of global node ids
- `facesets::Dict{String,Set{FaceIndex}}`: maps a `String` to a `Set` of `Tuple{Int,Int} (global_cell_id, local_face_id)`
_ `edgesets::Dict{String,Set{EdgeIndex}}`: maps a `String` to a `Set` of `Set{EdgeIndex} (global_cell_id, local_edge_id`
- `facesets::Dict{String,Set{FaceIndex}}`: maps a `String` to a `Set` of `Set{FaceIndex} (global_cell_id, local_face_id)`
- `edgesets::Dict{String,Set{EdgeIndex}}`: maps a `String` to a `Set` of `Set{EdgeIndex} (global_cell_id, local_edge_id`
- `vertexsets::Dict{String,Set{VertexIndex}}`: maps a `String` key to a `Set` of local vertex ids
- `boundary_matrix::SparseMatrixCSC{Bool,Int}`: optional, only needed by `onboundary` to check if a cell is on the boundary, see, e.g. Helmholtz example
"""
Expand Down Expand Up @@ -127,55 +129,100 @@ end
"""
getcells(grid::AbstractGrid)
getcells(grid::AbstractGrid, v::Union{Int,Vector{Int}}
getcells(grid::AbstractGrid, set::String)
getcells(grid::AbstractGrid, setname::String)
Returns either all `cells::Vector{C<:AbstractCell}` of a `grid` or a subset based on an `Int`, `Vector{Int}` or `String`.
Whereas the last option tries to call a `cellset` of the `grid`.
Returns either all `cells::Collection{C<:AbstractCell}` of a `<:AbstractGrid` or a subset based on an `Int`, `Vector{Int}` or `String`.
Whereas the last option tries to call a `cellset` of the `grid`. `Collection` can be any indexable type, for `Grid` it is `Vector{C<:AbstractCell}`.
"""
@inline getcells(grid::AbstractGrid) = grid.cells
@inline getcells(grid::AbstractGrid, v::Union{Int, Vector{Int}}) = grid.cells[v]
@inline getcells(grid::AbstractGrid, set::String) = grid.cells[collect(grid.cellsets[set])]
"Returns and `Int` corresponding to how many cells are in the `grid`."
@inline getcells(grid::AbstractGrid, setname::String) = grid.cells[collect(getcellset(grid,setname))]
"Returns the number of cells in the `<:AbstractGrid`."
@inline getncells(grid::AbstractGrid) = length(grid.cells)
"Returns the celltype of the `grid`."
"Returns the celltype of the `<:AbstractGrid`."
@inline getcelltype(grid::AbstractGrid) = eltype(grid.cells)
@inline getcelltype(grid::AbstractGrid, i::Int) = typeof(grid.cells[i])

"""
getnodes(grid::AbstractGrid)
getnodes(grid::AbstractGrid, v::Union{Int,Vector{Int}}
getnodes(grid::AbstractGrid, set::String)
getnodes(grid::AbstractGrid, setname::String)
Returns either all `nodes::Vector{Node{dim,T}}` of a `grid` or a subset based on an `Int`, `Vector{Int}` or `String`.
The last option tries to call a `nodeset` of the `grid`.
Returns either all `nodes::Collection{N}` of a `<:AbstractGrid` or a subset based on an `Int`, `Vector{Int}` or `String`.
The last option tries to call a `nodeset` of the `<:AbstractGrid`. `Collection{N}` refers to some indexable collection where each element corresponds
to a Node.
"""
@inline getnodes(grid::AbstractGrid) = grid.nodes
@inline getnodes(grid::AbstractGrid, v::Union{Int, Vector{Int}}) = grid.nodes[v]
@inline getnodes(grid::AbstractGrid, set::String) = grid.nodes[collect(grid.nodesets[set])]
"returns an `Int` corresponding to how many nodes are in the `grid`"
@inline getnodes(grid::AbstractGrid, setname::String) = grid.nodes[collect(getnodeset(grid,setname))]
"Returns the number of nodes in the grid"
@inline getnnodes(grid::AbstractGrid) = length(grid.nodes)
"returns an `Int` of how many nodes are in one `cell`"
"Returns the number of nodes of the `i`-th cell"
@inline nnodes_per_cell(grid::AbstractGrid, i::Int=1) = nnodes(grid.cells[i])

"Accesses the cellset which is mapped to the key `set::String`"
@inline getcellset(grid::AbstractGrid, set::String) = grid.cellsets[set]
"Returns all cellsets of the `grid`"
"""
getcellset(grid::AbstractGrid, setname::String)
Returns all cells as cellid in a `Set` of a given `setname`
"""
@inline getcellset(grid::AbstractGrid, setname::String) = grid.cellsets[setname]
"""
getcellsets(grid::AbstractGrid)
Returns all cellsets of the `grid`
"""
@inline getcellsets(grid::AbstractGrid) = grid.cellsets

"Accesses the nodeset which is mapped to the key `set::String`"
@inline getnodeset(grid::AbstractGrid, set::String) = grid.nodesets[set]
"Returns all nodesets of the `grid`"
"""
getnodeset(grid::AbstractGrid, setname::String)
Returns all nodes as nodeid in a `Set` of a given `setname`
"""
@inline getnodeset(grid::AbstractGrid, setname::String) = grid.nodesets[setname]
"""
getnodesets(grid::AbstractGrid)
Returns all nodesets of the `grid`
"""
@inline getnodesets(grid::AbstractGrid) = grid.nodesets

"Accesses the faceset which is mapped to the key `set::String`"
@inline getfaceset(grid::AbstractGrid, set::String) = grid.facesets[set]
"Returns all facesets of the `grid`"
"""
getfaceset(grid::AbstractGrid, setname::String)
Returns all faces as `FaceIndex` in a `Set` of a given `setname`
"""
@inline getfaceset(grid::AbstractGrid, setname::String) = grid.facesets[setname]
"""
getfacesets(grid::AbstractGrid)
Returns all facesets of the `grid`
"""
@inline getfacesets(grid::AbstractGrid) = grid.facesets

@inline getedgeset(grid::AbstractGrid, set::String) = grid.edgesets[set]
"""
getedgeset(grid::AbstractGrid, setname::String)
Returns all edges as `EdgeIndex` in a `Set` of a given `setname`
"""
@inline getedgeset(grid::AbstractGrid, setname::String) = grid.edgesets[setname]
"""
getedgesets(grid::AbstractGrid)
Returns all edge sets of the grid
"""
@inline getedgesets(grid::AbstractGrid) = grid.edgesets

@inline getvertexset(grid::AbstractGrid, set::String) = grid.vertexsets[set]
"""
getedgeset(grid::AbstractGrid, setname::String)
Returns all vertices as `VertexIndex` in a `Set` of a given `setname`
"""
@inline getvertexset(grid::AbstractGrid, setname::String) = grid.vertexsets[setname]
"""
getvertexsets(grid::AbstractGrid)
Returns all vertex sets of the grid
"""
@inline getvertexsets(grid::AbstractGrid) = grid.vertexsets

n_faces_per_cell(grid::Grid) = nfaces(eltype(grid.cells))
Expand Down Expand Up @@ -236,8 +283,9 @@ _warn_emptyset(set) = length(set) == 0 && @warn("no entities added to set")
addcellset!(grid::AbstractGrid, name::String, cellid::Union{Set{Int}, Vector{Int}})
addcellset!(grid::AbstractGrid, name::String, f::function; all::Bool=true)
Adds a `cellset::Dict{String,Set{Int}}` to the `grid` with key `name`.
Cellsets can be used to specify a boundary for `Dirichlet`, which is needed by the `ConstraintHandler`
Adds a cellset to the grid with key `name`.
Cellsets are typically used to define subdomains of the problem, e.g. two materials in the computational domain.
The `MixedDofHandler` can construct different fields which live not on the whole domain, but rather on a cellset.
```julia
addcellset!(grid, "left", Set((1,3))) #add cells with id 1 and 3 to cellset left
Expand Down Expand Up @@ -273,8 +321,8 @@ end
addfaceset!(grid::AbstractGrid, name::String, faceid::Union{Set{FaceIndex},Vector{FaceIndex}})
addfaceset!(grid::AbstractGrid, name::String, f::Function; all::Bool=true)
Adds a `faceset::Dict{String, Set{Tuple{Int,Int}}` to the `grid` with key `name`.
A `faceset` maps a `String` key to a `Set` of tuples corresponding to `(global_cell_id, local_face_id)`.
Adds a faceset to the grid with key `name`.
A faceset maps a `String` key to a `Set` of tuples corresponding to `(global_cell_id, local_face_id)`.
Facesets are used to initialize `Dirichlet` structs, that are needed to specify the boundary for the `ConstraintHandler`.
```julia
Expand Down

0 comments on commit 7f5e7db

Please sign in to comment.