Skip to content

Commit

Permalink
Add compat for introduction of facets in Ferrite v1 (#43)
Browse files Browse the repository at this point in the history
Support change in Ferrite.jl where faces are now facets, affecting the Grid datastructure
Ref Ferrite-FEM/Ferrite.jl#914.
Function create_faceset -> create_facetset 
get_ferrite_grid: Keyword argument generate_facesets -> generate_facetsets
  • Loading branch information
KnutAM authored May 21, 2024
1 parent 1f16b44 commit aa1c3f2
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 54 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0]

Required breaking changes to support Ferrite v1 and later.
### Changed
- Keyword arg of `get_ferrite_grid`: `generate_facesets` -> `generate_facetsets`
- Function renamed: `create_faceset` -> `create_facetset`

## [0.1.8]

Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "FerriteMeshParser"
uuid = "0f8c756f-80dd-4a75-85c6-b0a5ab9d4620"
authors = ["Knut Andreas Meyer and contributors"]
version = "0.1.8"
version = "0.2.0"

[deps]
Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992"
Expand Down
2 changes: 1 addition & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ get_ferrite_grid
```

```@docs
create_faceset
create_facetset
```
19 changes: 10 additions & 9 deletions docs/src/literate/compact_tension.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@ println([(key, length(set)) for (key, set) in Ferrite.getcellsets(grid)])

# As we see, in addition to the sets created in Abaqus, the cellsets also include a set
# for each abaqus element type (useful if you for example defined reduced integration
# in only part of the domain and want to have this in Ferrite). Finally, facesets are
# automatically created by default (can be turned off by `generate_facesets=false`
# in only part of the domain and want to have this in Ferrite). Finally, facetsets are
# automatically created by default (can be turned off by `generate_facetsets=false`
# argument) based on the nodesets:
println([(key, length(set)) for (key, set) in Ferrite.getfacesets(grid)])
# Clearly, the faceset `"CrackZone"` doesn't make much sense, but unless the mesh is
# very large it doesn't hurt. The facesets can be created manually from each nodeset
# by using the `create_faceset` function:
faceset = create_faceset(grid, getnodeset(grid,"Hole"));
# This can if desired be merged into the grid by
merge!(Ferrite.getfacesets(grid), Dict("HoleManual" => faceset))
println([(key, length(set)) for (key, set) in Ferrite.getfacesets(grid)])
# Clearly, the facetset `"CrackZone"` doesn't make much sense, but unless the mesh is
# very large it doesn't hurt. The facetsets can be created manually from each nodeset
# by using the `create_facetset` function:
facetset = create_facetset(grid, getnodeset(grid,"Hole"));
# This can, if desired, be merged into the grid by
# ```julia
# addfaceset!(grid, "HoleManual", facetset)
# ```

#
#md # ## [Plain Program](@id compact-tension-plain-program)
Expand Down
56 changes: 40 additions & 16 deletions src/FerriteMeshParser.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module FerriteMeshParser
using Ferrite
using Ferrite:
Ferrite, Grid, Node, Vec,
getcells, getnodes, getcoordinates, getncells

# Convenience when debugging
const DEBUG_PARSE = false

Expand All @@ -23,6 +26,20 @@ struct InvalidFileContent <: Exception
end
Base.showerror(io::IO, e::InvalidFileContent) = println(io, e.msg)

@static if !isdefined(Ferrite, :SerendipityQuadraticHexahedron)
const SerendipityQuadraticHexahedron = Ferrite.Cell{3,20,6}
const SerendipityQuadraticQuadrilateral = Ferrite.Cell{2,8,4}
else
const SerendipityQuadraticHexahedron = Ferrite.SerendipityQuadraticHexahedron
const SerendipityQuadraticQuadrilateral = Ferrite.SerendipityQuadraticQuadrilateral
end

const FacetsDefined = isdefined(Ferrite, :FacetIndex) # Ferrite after v1.0 (Ferrite#914)

const FacetIndex = FacetsDefined ? Ferrite.FacetIndex : Ferrite.FaceIndex
const facets = FacetsDefined ? Ferrite.facets : Ferrite.faces
const addfacetset! = FacetsDefined ? Ferrite.addfacetset! : Ferrite.addfaceset!

include("rawmesh.jl")
include("elements.jl")
include("reading_utils.jl")
Expand All @@ -34,7 +51,7 @@ include("gridcreator.jl")
filename;
meshformat=AutomaticMeshFormat(),
user_elements=Dict{String,DataType}(),
generate_facesets=true
generate_facetsets=true
)
Create a `Ferrite.Grid` by reading in the file specified by `filename`.
Expand All @@ -44,45 +61,46 @@ Optional arguments:
is given in, normally automatically detected by the file extension
* `user_elements`: Used to add extra elements not supported,
might require a separate cell constructor.
* `generate_facesets`: Should facesets be automatically generated from all nodesets?
* `generate_facetsets`: Should facesets be automatically generated from all nodesets?
"""
function get_ferrite_grid(filename; meshformat=AutomaticMeshFormat(), user_elements::Dict{String,DataType}=Dict{String,DataType}(), generate_facesets::Bool=true)
function get_ferrite_grid(filename; meshformat=AutomaticMeshFormat(), user_elements::Dict{String, DataType}=Dict{String, DataType}(), generate_facetsets::Bool=true, generate_facesets=nothing)
generate_facesets !== nothing && error("The keyword generate_facesets is deprecated, use generate_facetsets instead")
detected_format = detect_mesh_format(filename, meshformat)
mesh = read_mesh(filename, detected_format)
checkmesh(mesh)
grid = create_grid(mesh, detected_format, user_elements)
generate_facesets && generate_facesets!(grid)
generate_facetsets && generate_facetsets!(grid)
return grid
end

"""
create_faceset(
create_facetset(
grid::Ferrite.AbstractGrid,
nodeset::Set{Int},
cellset::Union{UnitRange{Int},Set{Int}}=1:getncells(grid)
)
Find the faces in the grid for which all nodes are in `nodeset`. Return them as a `Set{FaceIndex}`.
Find the facets in the grid for which all nodes are in `nodeset`. Return them as a `Set{FacetIndex}`.
A `cellset` can be given to only look only for faces amongst those cells to speed up the computation.
Otherwise the search is over all cells.
This function is normally only required when calling `get_ferrite_grid` with `generate_facesets=false`.
The created `faceset` can be added to the grid as `addfaceset!(grid, "facesetkey", faceset)`
This function is normally only required when calling `get_ferrite_grid` with `generate_facetsets=false`.
The created `facetset` can be added to the grid as `addfacetset!(grid, "facetsetkey", facetset)`
"""
function create_faceset(grid::Ferrite.AbstractGrid, nodeset::AbstractSet{Int}, cellset=1:getncells(grid))
faceset = sizehint!(Set{FaceIndex}(), length(nodeset))
function create_facetset(grid::Ferrite.AbstractGrid, nodeset::AbstractSet{Int}, cellset=1:getncells(grid))
facetset = sizehint!(Set{FacetIndex}(), length(nodeset))
for (cellid, cell) in enumerate(getcells(grid))
cellid cellset || continue
if any(n-> n nodeset, cell.nodes)
for (faceid, face) in enumerate(Ferrite.faces(cell))
if all(n -> n nodeset, face)
push!(faceset, FaceIndex(cellid, faceid))
for (facetid, facet) in enumerate(facets(cell))
if all(n -> n nodeset, facet)
push!(facetset, FacetIndex(cellid, facetid))
end
end
end
end
return faceset
return facetset
end

detect_mesh_format(_, meshformat) = meshformat
Expand All @@ -94,6 +112,12 @@ function detect_mesh_format(filename, ::AutomaticMeshFormat)
end
end

export get_ferrite_grid, create_faceset
export get_ferrite_grid, create_facetset

# Deprecated
function create_faceset(args...)
error("create_faceset is no longer supported, use create_facetset instead")
end
export create_faceset

end
21 changes: 8 additions & 13 deletions src/elements.jl
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
@static if !isdefined(Ferrite, :SerendipityQuadraticHexahedron)
const SerendipityQuadraticHexahedron = Cell{3,20,6}
const SerendipityQuadraticQuadrilateral = Cell{2,8,4}
end

function get_element_type_dict(::AbaqusMeshFormat)

quad = (Quadrilateral, ("CPE4", "CPS4", "CPE4R", "CPS4R"))
quad = (Ferrite.Quadrilateral, ("CPE4", "CPS4", "CPE4R", "CPS4R"))
quad2 = (SerendipityQuadraticQuadrilateral, ("CPS8", "CPS8R", "CPE8", "CPE8R"))
tria = (Triangle, ("CPE3", "CPS3"))
tria2 = (QuadraticTriangle, ("CPE6", "CPS6", "CPE6M", "CPS6M"))
tetra = (Tetrahedron, ("C3D4",))
tetra2 = (QuadraticTetrahedron, ("C3D10",))
hexa = (Hexahedron, ("C3D8","C3D8R"))
tria = (Ferrite.Triangle, ("CPE3", "CPS3"))
tria2 = (Ferrite.QuadraticTriangle, ("CPE6", "CPS6", "CPE6M", "CPS6M"))
tetra = (Ferrite.Tetrahedron, ("C3D4",))
tetra2 = (Ferrite.QuadraticTetrahedron, ("C3D10",))
hexa = (Ferrite.Hexahedron, ("C3D8","C3D8R"))
hexa2 = (SerendipityQuadraticHexahedron, ("C3D20","C3D20R"))

dict = Dict{String,DataType}()
dict = Dict{String, DataType}()
for types in (quad, tria, quad2, tria2, tetra, tetra2, hexa, hexa2)
merge!(dict, Dict(code=>types[1] for code in types[2]))
merge!(dict, Dict(code => types[1] for code in types[2]))
end
return dict
end
Expand Down
20 changes: 11 additions & 9 deletions src/gridcreator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ function create_nodes(rawnodes::RawNodes, ::Val{dim}) where{dim}
return nodes
end

function create_cells(rawelementsdict::Dict{String,RawElements}, user_elements::Dict, format)
function create_cells(rawelementsdict::Dict{String, RawElements}, user_elements::Dict, format)
builtin_elements = get_element_type_dict(format)
num_elements = sum(getnumelements.(values(rawelementsdict)))
num_elements = sum(getnumelements, values(rawelementsdict))
cells_generic = Array{Ferrite.AbstractCell}(undef, num_elements)
for (key, rawelements) in rawelementsdict
if haskey(user_elements, key) # user_elements are prioritized over builtin
Expand All @@ -31,14 +31,14 @@ function create_cells(rawelementsdict::Dict{String,RawElements}, user_elements::
end
end
# Return a Union of cell types as this should be faster to use than a generic cell
cell_type = Union{(typeof.(unique(typeof,cells_generic))...)}
cells = convert(Array{cell_type}, cells_generic)
CellType = Union{(typeof.(unique(typeof, cells_generic))...)}
cells = convert(Array{CellType}, cells_generic)
return cells
end

function addcells!(cells, elementtype, rawelements::RawElements, format)
for (i, element_number) in enumerate(getnumbers(rawelements))
node_numbers = gettopology(rawelements)[:,i]
node_numbers = gettopology(rawelements)[:, i]
cells[element_number] = create_cell(elementtype, node_numbers, format)
end
end
Expand All @@ -56,13 +56,15 @@ end


"""
function generate_facesets!(grid::Ferrite.Grid)
function generate_facetsets!(grid::Ferrite.Grid)
Based on all nodesets in `grid`, generate facesets for those sets.
If there is a cellset in the grid with the same name as the nodeset,
only facets for cells in that cellset with be created.
"""
function generate_facesets!(grid::Ferrite.Grid)
function generate_facetsets!(grid::Ferrite.Grid)
for (key, set) in Ferrite.getnodesets(grid)
cellset = get(Ferrite.getcellsets(grid), key, 1:getncells(grid))
addfaceset!(grid, key, create_faceset(grid, set, cellset))
addfacetset!(grid, key, create_facetset(grid, set, cellset))
end
end
end
17 changes: 12 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ using Aqua

Aqua.test_all(FerriteMeshParser; ambiguities = false)
Aqua.test_ambiguities(FerriteMeshParser) # This excludes Core and Base, which gets many ambiguities with ForwardDiff

# Function to retrieve test fields
gettestfile(args...) = joinpath(@__DIR__, "test_files", args...)

Expand All @@ -22,6 +22,10 @@ if !isdefined(Main, :SerendipityQuadrilateral)
Ferrite.faces(c::SerendipityQuadrilateral) = ((c.nodes[1],c.nodes[2]), (c.nodes[2],c.nodes[3]), (c.nodes[3],c.nodes[4]), (c.nodes[4],c.nodes[1]))
end

if !isdefined(Ferrite, :getfacetset)
getfacetset(args...) = Ferrite.getfaceset(args...)
end

if isdefined(Ferrite, :FieldHandler)
create_cell_values(ip; order=1) = CellScalarValues(QuadratureRule{Ferrite.getdim(ip), Ferrite.getrefshape(ip)}(order), ip, ip)
else # v1.0
Expand Down Expand Up @@ -66,12 +70,12 @@ end
@test _getgridtype(grid_mixed) == Union{Triangle,Quadrilateral}
end

@testset "facesetgeneration" begin
@testset "facet set generation" begin
filename = gettestfile("compact_tension.inp")
grid = get_ferrite_grid(filename)
face_set = create_faceset(grid, getnodeset(grid, "Hole"))
@test getfaceset(grid, "Hole") == face_set
@test face_set == create_faceset(grid, getnodeset(grid, "Hole"), getcellset(grid, "Hole")) # Test that including cells doesn't change the created sets
face_set = create_facetset(grid, getnodeset(grid, "Hole"))
@test getfacetset(grid, "Hole") == face_set
@test face_set == create_facetset(grid, getnodeset(grid, "Hole"), getcellset(grid, "Hole")) # Test that including cells doesn't change the created sets
end

@testset "exceptions" begin
Expand All @@ -96,6 +100,9 @@ end
showerror(io, FerriteMeshParser.UnsupportedElementType(test_string))
@test contains(String(take!(io)), test_string)

grid = get_ferrite_grid(gettestfile("compact_tension.inp"))
nset = getnodeset(grid, "Hole")
@test_throws ErrorException("create_faceset is no longer supported, use create_facetset instead") create_faceset(grid, nset)
end

@testset "ordering" begin
Expand Down

2 comments on commit aa1c3f2

@KnutAM
Copy link
Member Author

@KnutAM KnutAM commented on aa1c3f2 May 21, 2024

Choose a reason for hiding this comment

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

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

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

Registration pull request created: JuliaRegistries/General/107278

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.2.0 -m "<description of version>" aa1c3f2afb8110a08d41cc89d8ac003c9e1a404f
git push origin v0.2.0

Please sign in to comment.