From 25c93dea86efa791b201dee7a05ac34d5239c9e5 Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Fri, 5 Jul 2024 16:01:18 +0200 Subject: [PATCH 01/50] Widen accepted vector types in FEValues (#1017) --- src/FEValues/FunctionValues.jl | 6 +++--- src/FEValues/GeometryMapping.jl | 6 +++--- src/FEValues/InterfaceValues.jl | 6 +++--- src/FEValues/common_values.jl | 14 +++++++------- src/FEValues/face_integrals.jl | 12 ++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index cdc7f99b95..67cdf5ad44 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -88,13 +88,13 @@ function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureR return fv end -function precompute_values!(fv::FunctionValues{0}, qr_points::Vector{<:Vec}) +function precompute_values!(fv::FunctionValues{0}, qr_points::AbstractVector{<:Vec}) reference_shape_values!(fv.Nξ, fv.ip, qr_points) end -function precompute_values!(fv::FunctionValues{1}, qr_points::Vector{<:Vec}) +function precompute_values!(fv::FunctionValues{1}, qr_points::AbstractVector{<:Vec}) reference_shape_gradients_and_values!(fv.dNdξ, fv.Nξ, fv.ip, qr_points) end -function precompute_values!(fv::FunctionValues{2}, qr_points::Vector{<:Vec}) +function precompute_values!(fv::FunctionValues{2}, qr_points::AbstractVector{<:Vec}) reference_shape_hessians_gradients_and_values!(fv.d2Ndξ2, fv.dNdξ, fv.Nξ, fv.ip, qr_points) end diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index 79abf87415..4497c7733a 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -84,13 +84,13 @@ function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRu return gm end -function precompute_values!(gm::GeometryMapping{0}, qr_points::Vector{<:Vec}) +function precompute_values!(gm::GeometryMapping{0}, qr_points::AbstractVector{<:Vec}) reference_shape_values!(gm.M, gm.ip, qr_points) end -function precompute_values!(gm::GeometryMapping{1}, qr_points::Vector{<:Vec}) +function precompute_values!(gm::GeometryMapping{1}, qr_points::AbstractVector{<:Vec}) reference_shape_gradients_and_values!(gm.dMdξ, gm.M, gm.ip, qr_points) end -function precompute_values!(gm::GeometryMapping{2}, qr_points::Vector{<:Vec}) +function precompute_values!(gm::GeometryMapping{2}, qr_points::AbstractVector{<:Vec}) reference_shape_hessians_gradients_and_values!(gm.d2Mdξ2, gm.dMdξ, gm.M, gm.ip, qr_points) end diff --git a/src/FEValues/InterfaceValues.jl b/src/FEValues/InterfaceValues.jl index 32f3ad1e02..53c5ecb33a 100644 --- a/src/FEValues/InterfaceValues.jl +++ b/src/FEValues/InterfaceValues.jl @@ -486,7 +486,7 @@ end end @doc raw""" - transform_interface_points!(dst::Vector{Vec{3, Float64}}, points::Vector{Vec{3, Float64}}, interface_transformation::InterfaceOrientationInfo) + transform_interface_points!(dst::AbstractVector{Vec{3, Float64}}, points::AbstractVector{Vec{3, Float64}}, interface_transformation::InterfaceOrientationInfo) Transform the points from face A to face B using the orientation information of the interface and store it in the vector dst. For 3D, the faces are transformed into regular polygons such that the rotation angle is the shift in reference node index × 2π ÷ number of edges in face. @@ -549,7 +549,7 @@ y | \ """ transform_interface_points! -function transform_interface_points!(dst::Vector{Vec{3, Float64}}, points::Vector{Vec{3, Float64}}, interface_transformation::InterfaceOrientationInfo{RefShapeA, RefShapeB}) where {RefShapeA <: AbstractRefShape{3}, RefShapeB <: AbstractRefShape{3}} +function transform_interface_points!(dst::AbstractVector{Vec{3, Float64}}, points::AbstractVector{Vec{3, Float64}}, interface_transformation::InterfaceOrientationInfo{RefShapeA, RefShapeB}) where {RefShapeA <: AbstractRefShape{3}, RefShapeB <: AbstractRefShape{3}} facet_a = interface_transformation.facet_a facet_b = interface_transformation.facet_b @@ -562,7 +562,7 @@ function transform_interface_points!(dst::Vector{Vec{3, Float64}}, points::Vecto return nothing end -function transform_interface_points!(dst::Vector{Vec{2, Float64}}, points::Vector{Vec{2, Float64}}, interface_transformation::InterfaceOrientationInfo{RefShapeA, RefShapeB}) where {RefShapeA <: AbstractRefShape{2}, RefShapeB <: AbstractRefShape{2}} +function transform_interface_points!(dst::AbstractVector{Vec{2, Float64}}, points::AbstractVector{Vec{2, Float64}}, interface_transformation::InterfaceOrientationInfo{RefShapeA, RefShapeB}) where {RefShapeA <: AbstractRefShape{2}, RefShapeB <: AbstractRefShape{2}} facet_a = interface_transformation.facet_a facet_b = interface_transformation.facet_b flipped = interface_transformation.flipped diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 6da4252dd7..b68cb751d8 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -45,10 +45,10 @@ function ValuesUpdateFlags(ip_fun::Interpolation, ::Val{update_gradients}, ::Val end """ - reinit!(cv::CellValues, cell::AbstractCell, x::Vector) - reinit!(cv::CellValues, x::Vector) - reinit!(fv::FacetValues, cell::AbstractCell, x::Vector, face::Int) - reinit!(fv::FacetValues, x::Vector, face::Int) + reinit!(cv::CellValues, cell::AbstractCell, x::AbstractVector) + reinit!(cv::CellValues, x::AbstractVector) + reinit!(fv::FacetValues, cell::AbstractCell, x::AbstractVector, face::Int) + reinit!(fv::FacetValues, x::AbstractVector, face::Int) Update the `CellValues`/`FacetValues` object for a cell or face with coordinates `x`. The derivatives of the shape functions, and the new integration weights are computed. @@ -347,19 +347,19 @@ end _copy_or_nothing(x) = copy(x) _copy_or_nothing(::Nothing) = nothing -function reference_shape_values!(values::AbstractMatrix, ip, qr_points::Vector{<:Vec}) +function reference_shape_values!(values::AbstractMatrix, ip, qr_points::AbstractVector{<:Vec}) for (qp, ξ) in pairs(qr_points) reference_shape_values!(@view(values[:, qp]), ip, ξ) end end -function reference_shape_gradients_and_values!(gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::Vector{<:Vec}) +function reference_shape_gradients_and_values!(gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::AbstractVector{<:Vec}) for (qp, ξ) in pairs(qr_points) reference_shape_gradients_and_values!(@view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) end end -function reference_shape_hessians_gradients_and_values!(hessians::AbstractMatrix, gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::Vector{<:Vec}) +function reference_shape_hessians_gradients_and_values!(hessians::AbstractMatrix, gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::AbstractVector{<:Vec}) for (qp, ξ) in pairs(qr_points) reference_shape_hessians_gradients_and_values!(@view(hessians[:, qp]), @view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) end diff --git a/src/FEValues/face_integrals.jl b/src/FEValues/face_integrals.jl index f6e48fe3c5..d2d26bc6bc 100644 --- a/src/FEValues/face_integrals.jl +++ b/src/FEValues/face_integrals.jl @@ -25,18 +25,18 @@ align to the facet's local axis. function weighted_normal end """ - create_facet_quad_rule(::Type{RefShape}, w::Vector{T}, p::Vector{Vec{N, T}}) + create_facet_quad_rule(::Type{RefShape}, w::AbstractVectorä{T}, p::AbstractVectorä{Vec{N, T}}) create_facet_quad_rule( ::Type{RefShape}, - quad_faces::Vector{Int}, w_quad::Vector{T}, p_quad::Vector{Vec{N, T}}, - tri_faces::Vector{Int}, w_tri::Vector{T}, p_tri::Vector{Vec{N, T}} + quad_faces::AbstractVectorä{Int}, w_quad::AbstractVector{T}, p_quad::AbstractVector{Vec{N, T}}, + tri_faces::AbstractVector{Int}, w_tri::AbstractVector{T}, p_tri::AbstractVector{Vec{N, T}} ) Create a ["FacetQuadratureRule"](@ref) for the given cell type, weights and points. If the cell has facets of different shapes (i.e. quadrilaterals and triangles) then each shape's facets indices, weights and points are passed separately. """ -function create_facet_quad_rule(::Type{RefShape}, w::Vector{T}, p::Vector{Vec{N, T}}) where {N, T, RefShape <: AbstractRefShape} +function create_facet_quad_rule(::Type{RefShape}, w::AbstractVector{T}, p::AbstractVector{Vec{N, T}}) where {N, T, RefShape <: AbstractRefShape} facet_quad_rule = QuadratureRule{RefShape, Vector{T}, Vector{Vec{N+1, T}}}[] for facet in 1:nfacets(RefShape) new_points = [facet_to_element_transformation(p[i], RefShape, facet) for i in 1:length(w)] @@ -48,8 +48,8 @@ end # For cells with mixed faces function create_facet_quad_rule( ::Type{RefShape}, - quad_facets::Vector{Int}, w_quad::Vector{T}, p_quad::Vector{Vec{N, T}}, - tri_facets::Vector{Int}, w_tri::Vector{T}, p_tri::Vector{Vec{N, T}} + quad_facets::AbstractVector{Int}, w_quad::AbstractVector{T}, p_quad::AbstractVector{Vec{N, T}}, + tri_facets::AbstractVector{Int}, w_tri::AbstractVector{T}, p_tri::AbstractVector{Vec{N, T}} ) where {N, T, RefShape <: Union{RefPrism, RefPyramid}} facet_quad_rule = Vector{QuadratureRule{RefShape, Vector{T}, Vector{Vec{N+1, T}}}}(undef, nfacets(RefShape)) for facet in quad_facets From 81634737f0d15c4136d668ebc96ca9c6ccea3cd2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 9 Jul 2024 19:35:53 +0200 Subject: [PATCH 02/50] ExclusiveTopology ArrayOfVectorViews backend (#974) * This patch adds a new storage format, ArrayOfVectorViews <: AbstractArray. It stores vectors views (of a continuous data vector) with unequal lengths as an array with arbitrary dimensions. It also provides a special buffer during construction, to avoid allocating many small vectors which for ExclusiveTopology was causing a significant amount of fragmented memory and high GC costs. * Additionally, the ExclusiveArray constructor errors for grids containing embedded cells, as the algorithms don't support this case before #843 is solved. --- CHANGELOG.md | 13 +- docs/src/devdocs/index.md | 2 +- docs/src/devdocs/special_datastructures.md | 16 + .../literate-gallery/topology_optimization.jl | 6 +- src/CollectionsOfViews.jl | 156 +++++++ src/Ferrite.jl | 4 + src/Grid/topology.jl | 390 +++++++++--------- src/Grid/utils.jl | 2 +- src/iterators.jl | 2 +- test/runtests.jl | 1 + test/test_collectionsofviews.jl | 46 +++ test/test_dofs.jl | 4 +- test/test_grid_dofhandler_vtk.jl | 125 +++--- 13 files changed, 518 insertions(+), 249 deletions(-) create mode 100644 docs/src/devdocs/special_datastructures.md create mode 100644 src/CollectionsOfViews.jl create mode 100644 test/test_collectionsofviews.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index 11add1e764..3ecacc0ac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -446,10 +446,18 @@ more discussion). `facedof_indices`, `volumedof_indices` (and similar) according to these definitions. - `Ferrite.getdim` has been changed into `Ferrite.getrefdim` for getting the dimension of the reference shape - and `Ferrite.getspatialdim` to get the spatial dimension (of the grid). [#943][github-943] + and `Ferrite.getspatialdim` to get the spatial dimension (of the grid). ([#943][github-943]) - `Ferrite.getfielddim(::AbstractDofHandler, args...)` has been renamed to `Ferrite.n_components`. - [#943][github-943] + ([#943][github-943]) + +- The constructor for `ExclusiveTopology` only accept an `AbstractGrid` as input, + removing the alternative of providing a `Vector{<:AbstractCell}`, as knowing the + spatial dimension is required for correct code paths. + Furthermore, it uses a new internal data structure, `ArrayOfVectorViews`, to store the neighborhood + information more efficiently The datatype for the neighborhood has thus changed to a view of a vector, + instead of the now removed `EntityNeighborhood` container. This also applies to `vertex_star_stencils`. + ([#974][github-974]). - `project(::L2Projector, data, qr_rhs)` now expects data to be indexed by the cellid, as opposed to the index in the vector of cellids passed to the `L2Projector`. The data may be passed as an @@ -1037,3 +1045,4 @@ poking into Ferrite internals: [github-943]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/943 [github-949]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/949 [github-953]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/953 +[github-974]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/974 diff --git a/docs/src/devdocs/index.md b/docs/src/devdocs/index.md index 9c16b83d7c..bfd680dcc8 100644 --- a/docs/src/devdocs/index.md +++ b/docs/src/devdocs/index.md @@ -5,5 +5,5 @@ developing the library. ```@contents Depth = 1 -Pages = ["reference_cells.md", "interpolations.md", "elements.md", "FEValues.md", "dofhandler.md", "performance.md"] +Pages = ["reference_cells.md", "interpolations.md", "elements.md", "FEValues.md", "dofhandler.md", "performance.md", "special_datastructures.md"] ``` diff --git a/docs/src/devdocs/special_datastructures.md b/docs/src/devdocs/special_datastructures.md new file mode 100644 index 0000000000..e642e5a1be --- /dev/null +++ b/docs/src/devdocs/special_datastructures.md @@ -0,0 +1,16 @@ +# Special data structures + +## `ArrayOfVectorViews` +`ArrayOfVectorViews` is a data structure representing an `Array` of +vector views (specifically `SubArray{T, 1} where T`). By arranging all +data (of type `T`) continuously in memory, this will significantly reduce +the garbage collection time compared to using an `Array{AbstractVector{T}}`. While the data in each view can be mutated, the length of each view is +fixed after construction. +This data structure provides two features not provided by `ArraysOfArrays.jl`: Support of matrices and higher order arrays for storing vectors +of different dimensions and efficient construction when the number of elements in each view is not known in advance. + +```@docs +Ferrite.ArrayOfVectorViews +Ferrite.ConstructionBuffer +Ferrite.push_at_index! +``` diff --git a/docs/src/literate-gallery/topology_optimization.jl b/docs/src/literate-gallery/topology_optimization.jl index 823756d13e..a03917019f 100644 --- a/docs/src/literate-gallery/topology_optimization.jl +++ b/docs/src/literate-gallery/topology_optimization.jl @@ -200,11 +200,11 @@ function cache_neighborhood(dh, topology) nbg = zeros(Int,_nfacets) i = cellid(element) for j in 1:_nfacets - nbg_cellid = getcells(getneighborhood(topology, dh.grid, FacetIndex(i,j))) + nbg_cellid = getneighborhood(topology, dh.grid, FacetIndex(i,j)) if(!isempty(nbg_cellid)) - nbg[j] = first(nbg_cellid) # assuming only one face neighbor per cell + nbg[j] = first(nbg_cellid)[1] # assuming only one face neighbor per cell else # boundary face - nbg[j] = first(getcells(getneighborhood(topology, dh.grid, FacetIndex(i,opp[j])))) + nbg[j] = first(getneighborhood(topology, dh.grid, FacetIndex(i,opp[j])))[1] end end diff --git a/src/CollectionsOfViews.jl b/src/CollectionsOfViews.jl new file mode 100644 index 0000000000..a85691af06 --- /dev/null +++ b/src/CollectionsOfViews.jl @@ -0,0 +1,156 @@ +module CollectionsOfViews + +export ArrayOfVectorViews, push_at_index!, ConstructionBuffer + +# `AdaptiveRange` and `ConstructionBuffer` are used to efficiently build up an `ArrayOfVectorViews` +# when the size of each view is unknown. +struct AdaptiveRange + start::Int + ncurrent::Int + nmax::Int +end + +struct ConstructionBuffer{T, N} + indices::Array{AdaptiveRange, N} + data::Vector{T} + sizehint::Int +end + +""" + ConstructionBuffer(data::Vector, dims::NTuple{N, Int}, sizehint) + +Create a buffer for creating an [`ArrayOfVectorViews`](@ref), representing an array with `N` axes. +`sizehint` sets the number of elements in `data` allocated when a new index is added via `push_at_index!`, +or when the current storage for the index is full, how much many additional elements are reserved for that index. +Any content in `data` is overwritten, but performance is improved by pre-allocating it to a reasonable size or +by `sizehint!`ing it. +""" +function ConstructionBuffer(data::Vector, dims::NTuple{<:Any, Int}, sizehint::Int) + indices = fill(AdaptiveRange(0, 0, 0), dims) + return ConstructionBuffer(indices, empty!(data), sizehint) +end + +""" + push_at_index!(b::ConstructionBuffer, val, indices::Int...) + +`push!` the value `val` to the `Vector` view at the index given by `indices`, typically called +inside the [`ArrayOfVectorViews`](@ref) constructor do-block. But can also be used when manually +creating a `ConstructionBuffer`. +""" +function push_at_index!(b::ConstructionBuffer, val, indices::Vararg{Int, N}) where {N} + r = getindex(b.indices, indices...) + n = length(b.data) + if r.start == 0 + # `indices...` not previously added, allocate new space for it at the end of `b.data` + resize!(b.data, n + b.sizehint) + b.data[n+1] = val + setindex!(b.indices, AdaptiveRange(n + 1, 1, b.sizehint), indices...) + elseif r.ncurrent == r.nmax + # We have used up our space, move data associated with `indices...` to the end of `b.data` + resize!(b.data, n + r.nmax + b.sizehint) + for i in 1:r.ncurrent + b.data[n + i] = b.data[r.start + i - 1] + end + b.data[n + r.ncurrent + 1] = val + setindex!(b.indices, AdaptiveRange(n + 1, r.ncurrent + 1, r.nmax + b.sizehint), indices...) + else # We have space in an already allocated section + b.data[r.start + r.ncurrent] = val + setindex!(b.indices, AdaptiveRange(r.start, r.ncurrent + 1, r.nmax), indices...) + end + return b +end + +struct ArrayOfVectorViews{T, N} <: AbstractArray{SubArray{T, 1, Vector{T}, Tuple{UnitRange{Int64}}, true}, N} + indices::Vector{Int} + data::Vector{T} + lin_idx::LinearIndices{N, NTuple{N, Base.OneTo{Int}}} + function ArrayOfVectorViews{T, N}(indices::Vector{Int}, data::Vector{T}, lin_idx::LinearIndices{N}) where {T, N} + return new{T, N}(indices, data, lin_idx) + end +end + +# AbstractArray interface (https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-array) +Base.size(cv::ArrayOfVectorViews) = size(cv.lin_idx) +@inline function Base.getindex(cv::ArrayOfVectorViews, linear_index::Int) + @boundscheck checkbounds(cv.lin_idx, linear_index) + return @inbounds view(cv.data, cv.indices[linear_index]:(cv.indices[linear_index+1]-1)) +end +@inline function Base.getindex(cv::ArrayOfVectorViews, idx...) + linear_index = getindex(cv.lin_idx, idx...) + return @inbounds getindex(cv, linear_index) +end +Base.IndexStyle(::Type{<:ArrayOfVectorViews{<:Any, N}}) where N = Base.IndexStyle(Array{Int, N}) + +# Constructors +""" + ArrayOfVectorViews(f!::Function, data::Vector{T}, dims::NTuple{N, Int}; sizehint) + +Create an `ArrayOfVectorViews` to store many vector views of potentially different sizes, +emulating an `Array{Vector{T}, N}` with size `dims`. However, it avoids allocating each vector individually +by storing all data in `data`, and instead of `Vector{T}`, the each element is a `typeof(view(data, 1:2))`. + +When the length of each vector is unknown, the `ArrayOfVectorViews` can be created reasonably efficient +with the following do-block, which creates an intermediate `buffer::ConstructionBuffer` supporting the +[`push_at_index!`](@ref) function. +``` +vector_views = ArrayOfVectorViews(data, dims; sizehint) do buffer + for (ind, val) in some_data + push_at_index!(buffer, val, ind) + end +end +``` +`sizehint` tells how much space to allocate for the index `ind` if no `val` has been added to that index before, +or how much more space to allocate in case all previously allocated space for `ind` has been used up. +""" +function ArrayOfVectorViews(f!::F, data::Vector, dims::Tuple; sizehint = nothing) where {F <: Function} + sizehint === nothing && error("Providing sizehint is mandatory") + b = ConstructionBuffer(data, dims, sizehint) + f!(b) + return ArrayOfVectorViews(b) +end + +""" + ArrayOfVectorViews(b::CollectionsOfViews.ConstructionBuffer) + +Creates the `ArrayOfVectorViews` directly from the `ConstructionBuffer` that was manually created and filled. +""" +function ArrayOfVectorViews(b::ConstructionBuffer{T}) where T + indices = Vector{Int}(undef, length(b.indices) + 1) + lin_idx = LinearIndices(b.indices) + data_length = sum(ar.ncurrent for ar in b.indices; init=0) + data = Vector{T}(undef, data_length) + data_index = 1 + for (idx, ar) in pairs(b.indices) + copyto!(data, data_index, b.data, ar.start, ar.ncurrent) + indices[lin_idx[idx]] = data_index + data_index += ar.ncurrent + end + indices[length(indices)] = data_index + # Since user-code in the constructor function has access to `b`, setting dimensions to + # zero here allows GC:ing the data in `b` even in cases when the compiler cannot + # guarantee that it is unreachable. + resize!(b.data, 0); sizehint!(b.data, 0) + isa(b.indices, Vector) && (resize!(b.indices, 0); sizehint!(b.indices, 0)) + return ArrayOfVectorViews(indices, data, lin_idx) +end + +""" + ArrayOfVectorViews(indices::Vector{Int}, data::Vector{T}, lin_idx::LinearIndices{N}; checkargs = true) + +Creates the `ArrayOfVectorViews` directly where the user is responsible for having the correct input data. +Checking of the argument dimensions can be elided by setting `checkargs = false`, but incorrect dimensions +may lead to illegal out of bounds access later. + +`data` is indexed by `indices[i]:indices[i+1]`, where `i = lin_idx[idx...]` and `idx...` are the user-provided +indices to the `ArrayOfVectorViews`. +""" +function ArrayOfVectorViews(indices::Vector{Int}, data::Vector{T}, lin_idx::LinearIndices{N}; checkargs = true) where {T, N} + if checkargs + checkbounds(data, 1:(last(indices) - 1)) + checkbounds(indices, last(lin_idx) + 1) + issorted(indices) || throw(ArgumentError("indices must be weakly increasing")) + end + return ArrayOfVectorViews{T, N}(indices, data, lin_idx) +end + +end diff --git a/src/Ferrite.jl b/src/Ferrite.jl index ca5ef421e5..0776f2f6be 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -25,9 +25,13 @@ using Tensors: using ForwardDiff: ForwardDiff +include("CollectionsOfViews.jl") +using .CollectionsOfViews: + CollectionsOfViews, ArrayOfVectorViews, push_at_index!, ConstructionBuffer include("exports.jl") + """ AbstractRefShape{refdim} diff --git a/src/Grid/topology.jl b/src/Grid/topology.jl index b1caaf2efc..0d314a89eb 100644 --- a/src/Grid/topology.jl +++ b/src/Grid/topology.jl @@ -1,4 +1,3 @@ - ############ # Topology # ############ @@ -14,65 +13,90 @@ the given entity is included in the returned list as well. """ getneighborhood -struct EntityNeighborhood{T<:Union{BoundaryIndex,CellIndex}} - neighbor_info::Vector{T} -end - -EntityNeighborhood(info::T) where T <: BoundaryIndex = EntityNeighborhood([info]) -Base.length(n::EntityNeighborhood) = length(n.neighbor_info) -Base.getindex(n::EntityNeighborhood,i) = getindex(n.neighbor_info,i) -Base.firstindex(n::EntityNeighborhood) = 1 -Base.lastindex(n::EntityNeighborhood) = length(n.neighbor_info) -Base.:(==)(n1::EntityNeighborhood, n2::EntityNeighborhood) = n1.neighbor_info == n2.neighbor_info -Base.iterate(n::EntityNeighborhood, state=1) = iterate(n.neighbor_info,state) - -function Base.show(io::IO, ::MIME"text/plain", n::EntityNeighborhood) - if length(n) == 0 - println(io, "No EntityNeighborhood") - elseif length(n) == 1 - println(io, "$(n.neighbor_info[1])") - else - println(io, "$(n.neighbor_info...)") - end -end abstract type AbstractTopology end """ - ExclusiveTopology(cells::Vector{C}) where C <: AbstractCell - ExclusiveTopology(grid::Grid) + ExclusiveTopology(grid::AbstractGrid) -`ExclusiveTopology` saves topological (connectivity/neighborhood) data of the grid. The constructor works with an `AbstractCell` -vector for all cells that dispatch `vertices`, `edges`, and `faces`. -The struct saves the highest dimensional neighborhood, i.e. if something is connected by a face and an - edge only the face neighborhood is saved. The lower dimensional neighborhood is recomputed, if needed. +The **experimental feature** `ExclusiveTopology` saves topological (connectivity/neighborhood) data of the grid. +Only the highest dimensional neighborhood is saved. I.e., if something is connected by a face and an +edge, only the face neighborhood is saved. The lower dimensional neighborhood is recomputed when calling getneighborhood if needed. # Fields -- `vertex_to_cell::Vector{Set{Int}}`: global vertex id to all cells containing the vertex -- `cell_neighbor::Vector{EntityNeighborhood{CellIndex}}`: cellid to all connected cells -- `face_neighbor::Matrix{EntityNeighborhood,Int}`: `face_neighbor[cellid,local_face_id]` -> neighboring face -- `vertex_neighbor::Matrix{EntityNeighborhood,Int}`: `vertex_neighbor[cellid,local_vertex_id]` -> neighboring vertex -- `edge_neighbor::Matrix{EntityNeighborhood,Int}`: `edge_neighbor[cellid_local_vertex_id]` -> neighboring edge -- `face_skeleton::Union{Vector{FaceIndex}, Nothing}`: - -!!! note Currently mixed-dimensional queries do not work at the moment. They will be added back later. +- `vertex_to_cell::AbstractArray{AbstractVector{Int}, 1}`: global vertex id to all cells containing the vertex +- `cell_neighbor::AbstractArray{AbstractVector{Int}, 1}`: cellid to all connected cells +- `face_neighbor::AbstractArray{AbstractVector{FaceIndex}, 2}`: `face_neighbor[cellid, local_face_id]` -> neighboring faces +- `edge_neighbor::AbstractArray{AbstractVector{EdgeIndex}, 2}`: `edge_neighbor[cellid, local_edge_id]` -> neighboring edges +- `vertex_neighbor::AbstractArray{AbstractVector{VertexIndex}, 2}`: `vertex_neighbor[cellid, local_vertex_id]` -> neighboring vertices +- `face_skeleton::Union{Vector{FaceIndex}, Nothing}`: List of unique faces in the grid given as `FaceIndex` +- `edge_skeleton::Union{Vector{EdgeIndex}, Nothing}`: List of unique edges in the grid given as `EdgeIndex` +- `vertex_skeleton::Union{Vector{VertexIndex}, Nothing}`: List of unique vertices in the grid given as `VertexIndex` + +!!! warning "Limitations" + The implementation only works with conforming grids, i.e. grids without "hanging nodes". Non-conforming grids will give unexpected results. + Grids with embedded cells (different reference dimension compared + to the spatial dimension) are not supported, and will error on construction. + """ mutable struct ExclusiveTopology <: AbstractTopology - # maps a global vertex id to all cells containing the vertex - vertex_to_cell::Vector{Set{Int}} - # index of the vector = cell id -> all other connected cells - cell_neighbor::Vector{EntityNeighborhood{CellIndex}} - # face_neighbor[cellid,local_face_id] -> exclusive connected entities (not restricted to one entity) - face_face_neighbor::Matrix{EntityNeighborhood{FaceIndex}} - # vertex_neighbor[cellid,local_vertex_id] -> exclusive connected entities to the given vertex - vertex_vertex_neighbor::Matrix{EntityNeighborhood{VertexIndex}} - # edge_neighbor[cellid,local_edge_id] -> exclusive connected entities of the given edge - edge_edge_neighbor::Matrix{EntityNeighborhood{EdgeIndex}} - # lazy constructed face topology + vertex_to_cell::ArrayOfVectorViews{Int, 1} + cell_neighbor::ArrayOfVectorViews{Int, 1} + # face_face_neighbor[cellid,local_face_id] -> exclusive connected entities (not restricted to one entity) + face_face_neighbor::ArrayOfVectorViews{FaceIndex, 2} + # edge_edge_neighbor[cellid,local_edge_id] -> exclusive connected entities of the given edge + edge_edge_neighbor::ArrayOfVectorViews{EdgeIndex, 2} + # vertex_vertex_neighbor[cellid,local_vertex_id] -> exclusive connected entities to the given vertex + vertex_vertex_neighbor::ArrayOfVectorViews{VertexIndex, 2} + face_skeleton::Union{Vector{FaceIndex}, Nothing} edge_skeleton::Union{Vector{EdgeIndex}, Nothing} vertex_skeleton::Union{Vector{VertexIndex}, Nothing} - # TODO reintroduce the codimensional connectivity, e.g. 3D edge to 2D face +end + +function ExclusiveTopology(grid::AbstractGrid{sdim}) where sdim + if sdim != get_reference_dimension(grid) + error("ExclusiveTopology does not support embedded cells (i.e. reference dimensions different from the spatial dimension)") + end + cells = getcells(grid) + nnodes = getnnodes(grid) + ncells = length(cells) + + max_vertices, max_edges, max_faces = _max_nentities_per_cell(cells) + vertex_to_cell = build_vertex_to_cell(cells; max_vertices, nnodes) + cell_neighbor = build_cell_neighbor(grid, cells, vertex_to_cell; ncells) + + # Here we don't use the convenience constructor taking a function, + # since we want to do it simultaneously for 3 data-types + facedata = sizehint!(FaceIndex[], ncells * max_faces * _getsizehint(grid, FaceIndex)) + face_face_neighbor_buf = CollectionsOfViews.ConstructionBuffer(facedata, (ncells, max_faces), _getsizehint(grid, FaceIndex)) + edgedata = sizehint!(EdgeIndex[], ncells * max_edges * _getsizehint(grid, EdgeIndex)) + edge_edge_neighbor_buf = CollectionsOfViews.ConstructionBuffer(edgedata, (ncells, max_edges), _getsizehint(grid, EdgeIndex)) + vertdata = sizehint!(VertexIndex[], ncells * max_vertices * _getsizehint(grid, VertexIndex)) + vertex_vertex_neighbor_buf = CollectionsOfViews.ConstructionBuffer(vertdata, (ncells, max_vertices), _getsizehint(grid, VertexIndex)) + + for (cell_id, cell) in enumerate(cells) + for neighbor_cell_id in cell_neighbor[cell_id] + neighbor_cell = cells[neighbor_cell_id] + getrefdim(neighbor_cell) == getrefdim(cell) || error("Not supported") + num_shared_vertices = _num_shared_vertices(cell, neighbor_cell) + if num_shared_vertices == 1 + _add_single_vertex_neighbor!(vertex_vertex_neighbor_buf, cell, cell_id, neighbor_cell, neighbor_cell_id) + # Shared edge + elseif num_shared_vertices == 2 + _add_single_edge_neighbor!(edge_edge_neighbor_buf, cell, cell_id, neighbor_cell, neighbor_cell_id) + # Shared face + elseif num_shared_vertices >= 3 + _add_single_face_neighbor!(face_face_neighbor_buf, cell, cell_id, neighbor_cell, neighbor_cell_id) + else + error("Found connected elements without shared vertex... Mesh broken?") + end + end + end + face_face_neighbor = ArrayOfVectorViews(face_face_neighbor_buf) + edge_edge_neighbor = ArrayOfVectorViews(edge_edge_neighbor_buf) + vertex_vertex_neighbor = ArrayOfVectorViews(vertex_vertex_neighbor_buf) + return ExclusiveTopology(vertex_to_cell, cell_neighbor, face_face_neighbor, edge_edge_neighbor, vertex_vertex_neighbor, nothing, nothing, nothing) end function get_facet_facet_neighborhood(t::ExclusiveTopology, g::AbstractGrid) @@ -86,12 +110,32 @@ function _get_facet_facet_neighborhood(::ExclusiveTopology, #=rdim=#::Val{:mixed Access the `vertex_vertex_neighbor`, `edge_edge_neighbor`, or `face_face_neighbor` fields explicitly instead.")) end -function Base.show(io::IO, ::MIME"text/plain", topology::ExclusiveTopology) - println(io, "ExclusiveTopology\n") - print(io, " Vertex neighbors: $(size(topology.vertex_vertex_neighbor))\n") - print(io, " Face neighbors: $(size(topology.face_face_neighbor))\n") - println(io, " Edge neighbors: $(size(topology.edge_edge_neighbor))") +# Guess of how many neighbors depending on grid dimension and index type. +# This could be possible to optimize further by studying connectivities of non-uniform +# grids, see https://github.com/Ferrite-FEM/Ferrite.jl/pull/974#discussion_r1660838649 +function _getsizehint(g::AbstractGrid, ::Type{IDX}) where IDX + CT = getcelltype(g) + isconcretetype(CT) && return _getsizehint(getrefshape(CT)(), IDX) + rdim = get_reference_dimension(g)::Int + return _getsizehint(RefSimplex{rdim}(), IDX) # Simplex is "worst case", used as default. end +_getsizehint(::AbstractRefShape, ::Type{FaceIndex}) = 1 # Always 1 or zero if not mixed rdim + +_getsizehint(::AbstractRefShape{1}, ::Type{EdgeIndex}) = 1 +_getsizehint(::AbstractRefShape{2}, ::Type{EdgeIndex}) = 1 +_getsizehint(::AbstractRefShape{3}, ::Type{EdgeIndex}) = 3 # Number for RefTetrahedron +_getsizehint(::RefHexahedron, ::Type{EdgeIndex}) = 1 # Optim for RefHexahedron + +_getsizehint(::AbstractRefShape{1}, ::Type{VertexIndex}) = 1 +_getsizehint(::AbstractRefShape{2}, ::Type{VertexIndex}) = 3 +_getsizehint(::AbstractRefShape{3}, ::Type{VertexIndex}) = 13 +_getsizehint(::RefHypercube, ::Type{VertexIndex}) = 1 # Optim for RefHypercube + +_getsizehint(::AbstractRefShape{1}, ::Type{CellIndex}) = 2 +_getsizehint(::AbstractRefShape{2}, ::Type{CellIndex}) = 12 +_getsizehint(::AbstractRefShape{3}, ::Type{CellIndex}) = 70 +_getsizehint(::RefQuadrilateral, ::Type{CellIndex}) = 8 +_getsizehint(::RefHexahedron, ::Type{CellIndex}) = 26 function _num_shared_vertices(cell_a::C1, cell_b::C2) where {C1, C2} num_shared_vertices = 0 @@ -106,164 +150,125 @@ function _num_shared_vertices(cell_a::C1, cell_b::C2) where {C1, C2} return num_shared_vertices end -function _exclusive_topology_ctor(cells::Vector{C}, vertex_cell_table::Array{Set{Int}}, vertex_table, face_table, edge_table, cell_neighbor_table) where C <: AbstractCell - for (cell_id, cell) in enumerate(cells) - # Gather all cells which are connected via vertices - cell_neighbor_ids = Set{Int}() - for vertex ∈ vertices(cell) - for vertex_cell_id ∈ vertex_cell_table[vertex] - if vertex_cell_id != cell_id - push!(cell_neighbor_ids, vertex_cell_id) - end - end - end - cell_neighbor_table[cell_id] = EntityNeighborhood(CellIndex.(collect(cell_neighbor_ids))) - - # Any of the neighbors is now sorted in the respective categories - for cell_neighbor_id ∈ cell_neighbor_ids - # Buffer neighbor - cell_neighbor = cells[cell_neighbor_id] - # TODO handle mixed-dimensional case - getrefdim(cell_neighbor) == getrefdim(cell) || continue - - num_shared_vertices = _num_shared_vertices(cell, cell_neighbor) - - # Simplest case: Only one vertex is shared => Vertex neighbor - if num_shared_vertices == 1 - for (lvi, vertex) ∈ enumerate(vertices(cell)) - for (lvi2, vertex_neighbor) ∈ enumerate(vertices(cell_neighbor)) - if vertex_neighbor == vertex - push!(vertex_table[cell_id, lvi].neighbor_info, VertexIndex(cell_neighbor_id, lvi2)) - break - end - end - end - # Shared edge - elseif num_shared_vertices == 2 - _add_single_edge_neighbor!(edge_table, cell, cell_id, cell_neighbor, cell_neighbor_id) - # Shared face - elseif num_shared_vertices >= 3 - _add_single_face_neighbor!(face_table, cell, cell_id, cell_neighbor, cell_neighbor_id) - else - @error "Found connected elements without shared vertex... Mesh broken?" - end - end - end -end - -function ExclusiveTopology(cells::Vector{C}) where C <: AbstractCell - # Setup the cell to vertex table - cell_vertices_table = vertices.(cells) #needs generic interface for <: AbstractCell - vertex_cell_table = Set{Int}[Set{Int}() for _ ∈ 1:maximum(maximum.(cell_vertices_table))] - - # Setup vertex to cell connectivity by flipping the cell to vertex table - for (cellid, cell_vertices) in enumerate(cell_vertices_table) - for vertex in cell_vertices - push!(vertex_cell_table[vertex], cellid) - end - end - - # Compute correct matrix size - celltype = eltype(cells) - max_vertices = 0 - max_faces = 0 - max_edges = 0 - if isconcretetype(celltype) - max_vertices = nvertices(cells[1]) - max_faces = nfaces(cells[1]) - max_edges = nedges(cells[1]) +"Return the highest number of vertices, edges, and faces per cell" +function _max_nentities_per_cell(cells::Vector{C}) where C + if isconcretetype(C) + cell = first(cells) + return nvertices(cell), nedges(cell), nfaces(cell) else celltypes = Set(typeof.(cells)) + max_vertices = 0 + max_edges = 0 + max_faces = 0 for celltype in celltypes - celltypeidx = findfirst(x->typeof(x)==celltype,cells) - max_vertices = max(max_vertices,nvertices(cells[celltypeidx])) - max_faces = max(max_faces, nfaces(cells[celltypeidx])) + celltypeidx = findfirst(x -> isa(x, celltype), cells) + max_vertices = max(max_vertices, nvertices(cells[celltypeidx])) max_edges = max(max_edges, nedges(cells[celltypeidx])) + max_faces = max(max_faces, nfaces(cells[celltypeidx])) end + return max_vertices, max_edges, max_faces end - - # Setup matrices - vertex_table = Matrix{EntityNeighborhood{VertexIndex}}(undef, length(cells), max_vertices) - for j = 1:size(vertex_table,2) - for i = 1:size(vertex_table,1) - vertex_table[i,j] = EntityNeighborhood{VertexIndex}(VertexIndex[]) - end - end - face_table = Matrix{EntityNeighborhood{FaceIndex}}(undef, length(cells), max_faces) - for j = 1:size(face_table,2) - for i = 1:size(face_table,1) - face_table[i,j] = EntityNeighborhood{FaceIndex}(FaceIndex[]) - end - end - edge_table = Matrix{EntityNeighborhood{EdgeIndex}}(undef, length(cells), max_edges) - for j = 1:size(edge_table,2) - for i = 1:size(edge_table,1) - edge_table[i,j] = EntityNeighborhood{EdgeIndex}(EdgeIndex[]) - end - end - cell_neighbor_table = Vector{EntityNeighborhood{CellIndex}}(undef, length(cells)) - - _exclusive_topology_ctor(cells, vertex_cell_table, vertex_table, face_table, edge_table, cell_neighbor_table) - - return ExclusiveTopology(vertex_cell_table,cell_neighbor_table,face_table,vertex_table,edge_table,nothing,nothing,nothing) end -function _add_single_face_neighbor!(face_table, cell::C1, cell_id, cell_neighbor::C2, cell_neighbor_id) where {C1, C2} +function _add_single_face_neighbor!(face_table::ConstructionBuffer, cell::AbstractCell, cell_id::Int, cell_neighbor::AbstractCell, cell_neighbor_id::Int) for (lfi, face) ∈ enumerate(faces(cell)) uniqueface = sortface_fast(face) for (lfi2, face_neighbor) ∈ enumerate(faces(cell_neighbor)) uniqueface2 = sortface_fast(face_neighbor) if uniqueface == uniqueface2 - push!(face_table[cell_id, lfi].neighbor_info, FaceIndex(cell_neighbor_id, lfi2)) + push_at_index!(face_table, FaceIndex(cell_neighbor_id, lfi2), cell_id, lfi) return end end end end -function _add_single_edge_neighbor!(edge_table, cell::C1, cell_id, cell_neighbor::C2, cell_neighbor_id) where {C1, C2} +function _add_single_edge_neighbor!(edge_table::ConstructionBuffer, cell::AbstractCell, cell_id::Int, cell_neighbor::AbstractCell, cell_neighbor_id::Int) for (lei, edge) ∈ enumerate(edges(cell)) uniqueedge = sortedge_fast(edge) for (lei2, edge_neighbor) ∈ enumerate(edges(cell_neighbor)) uniqueedge2 = sortedge_fast(edge_neighbor) if uniqueedge == uniqueedge2 - push!(edge_table[cell_id, lei].neighbor_info, EdgeIndex(cell_neighbor_id, lei2)) + push_at_index!(edge_table, EdgeIndex(cell_neighbor_id, lei2), cell_id, lei) return end end end end +function _add_single_vertex_neighbor!(vertex_table::ConstructionBuffer, cell::AbstractCell, cell_id::Int, cell_neighbor::AbstractCell, cell_neighbor_id::Int) + for (lvi, vertex) ∈ enumerate(vertices(cell)) + for (lvi2, vertex_neighbor) ∈ enumerate(vertices(cell_neighbor)) + if vertex_neighbor == vertex + push_at_index!(vertex_table, VertexIndex(cell_neighbor_id, lvi2), cell_id, lvi) + break + end + end + end +end + +function build_vertex_to_cell(cells; max_vertices, nnodes) + vertex_to_cell = ArrayOfVectorViews(sizehint!(Int[], max_vertices * nnodes), (nnodes,); sizehint = max_vertices) do cov + for (cellid, cell) in enumerate(cells) + for vertex in vertices(cell) + push_at_index!(cov, cellid, vertex) + end + end + end + return vertex_to_cell +end -getcells(neighbor::EntityNeighborhood{T}) where T <: BoundaryIndex = first.(neighbor.neighbor_info) -getcells(neighbor::EntityNeighborhood{CellIndex}) = getproperty.(neighbor.neighbor_info, :idx) -getcells(neighbors::Vector{T}) where T <: EntityNeighborhood = reduce(vcat, getcells.(neighbors)) -getcells(neighbors::Vector{T}) where T <: BoundaryIndex = getindex.(neighbors,1) +function build_cell_neighbor(grid, cells, vertex_to_cell; ncells) + # In this case, we loop over the cells in order and all neighbors at once. + # Then we can create ArrayOfVectorViews directly without the CollectionsOfViews.ConstructionBuffer + sizehint = _getsizehint(grid, CellIndex) + data = empty!(Vector{Int}(undef, ncells * sizehint)) -ExclusiveTopology(grid::AbstractGrid) = ExclusiveTopology(getcells(grid)) + indices = Vector{Int}(undef, ncells + 1) + cell_neighbor_ids = Int[] + n = 1 + for (cell_id, cell) in enumerate(cells) + empty!(cell_neighbor_ids) + for vertex ∈ vertices(cell) + for vertex_cell_id ∈ vertex_to_cell[vertex] + if vertex_cell_id != cell_id + vertex_cell_id ∈ cell_neighbor_ids || push!(cell_neighbor_ids, vertex_cell_id) + end + end + end + indices[cell_id] = n + append!(data, cell_neighbor_ids) + n += length(cell_neighbor_ids) + end + indices[end] = n + sizehint!(data, length(data)) # Tell julia that we won't add more data + return ArrayOfVectorViews(indices, data, LinearIndices(1:ncells)) +end function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, cellidx::CellIndex, include_self=false) - patch = getcells(top.cell_neighbor[cellidx.idx]) + patch = top.cell_neighbor[cellidx.idx] if include_self - return [patch; cellidx.idx] + return view(push!(collect(patch), cellidx.idx), 1:(length(patch) + 1)) else return patch end end function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, faceidx::FaceIndex, include_self=false) + neighbors = top.face_face_neighbor[faceidx[1], faceidx[2]] if include_self - return [top.face_face_neighbor[faceidx[1],faceidx[2]].neighbor_info; faceidx] + return view(push!(collect(neighbors), faceidx), 1:(length(neighbors) + 1)) else - return top.face_face_neighbor[faceidx[1],faceidx[2]].neighbor_info + return neighbors end end function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{2}, edgeidx::EdgeIndex, include_self=false) + neighbors = top.edge_edge_neighbor[edgeidx[1], edgeidx[2]] if include_self - return [top.edge_edge_neighbor[edgeidx[1],edgeidx[2]].neighbor_info; edgeidx] + return view(push!(collect(neighbors), edgeidx), 1:(length(neighbors) + 1)) else - return top.edge_edge_neighbor[edgeidx[1],edgeidx[2]].neighbor_info + return neighbors end end @@ -279,26 +284,27 @@ function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, vertexidx:: !include_self && local_vertex == vertexidx && continue push!(self_reference_local, local_vertex) end - return self_reference_local + return view(self_reference_local, 1:length(self_reference_local)) end function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid{3}, edgeidx::EdgeIndex, include_self=false) cellid, local_edgeidx = edgeidx[1], edgeidx[2] - cell_edges = edges(getcells(grid,cellid)) + cell_edges = edges(getcells(grid, cellid)) nonlocal_edgeid = cell_edges[local_edgeidx] - cell_neighbors = getneighborhood(top,grid,CellIndex(cellid)) + cell_neighbors = getneighborhood(top, grid, CellIndex(cellid)) self_reference_local = EdgeIndex[] for cellid in cell_neighbors - local_neighbor_edgeid = findfirst(x->issubset(x,nonlocal_edgeid),edges(getcells(grid,cellid))) + local_neighbor_edgeid = findfirst(x -> issubset(x, nonlocal_edgeid), edges(getcells(grid, cellid))) local_neighbor_edgeid === nothing && continue local_edge = EdgeIndex(cellid,local_neighbor_edgeid) push!(self_reference_local, local_edge) end if include_self - return unique([top.edge_edge_neighbor[cellid, local_edgeidx].neighbor_info; self_reference_local; edgeidx]) + neighbors = unique([top.edge_edge_neighbor[cellid, local_edgeidx]; self_reference_local; edgeidx]) else - return unique([top.edge_edge_neighbor[cellid, local_edgeidx].neighbor_info; self_reference_local]) + neighbors = unique([top.edge_edge_neighbor[cellid, local_edgeidx]; self_reference_local]) end + return view(neighbors, 1:length(neighbors)) end function getneighborhood(top::ExclusiveTopology, grid::AbstractGrid, facetindex::FacetIndex, include_self=false) @@ -314,41 +320,42 @@ function _getneighborhood(::Val{:mixed}, args...) end """ - vertex_star_stencils(top::ExclusiveTopology, grid::Grid) -> Vector{Int, EntityNeighborhood{VertexIndex}}() + vertex_star_stencils(top::ExclusiveTopology, grid::Grid) -> AbstractVector{AbstractVector{VertexIndex}} Computes the stencils induced by the edge connectivity of the vertices. """ function vertex_star_stencils(top::ExclusiveTopology, grid::Grid) cells = grid.cells - stencil_table = Dict{Int,EntityNeighborhood{VertexIndex}}() - # Vertex Connectivity - for (global_vertexid,cellset) ∈ enumerate(top.vertex_to_cell) - vertex_neighbors_local = VertexIndex[] - for cell ∈ cellset - neighbor_boundary = edges(cells[cell]) - neighbor_connected_faces = neighbor_boundary[findall(x->global_vertexid ∈ x, neighbor_boundary)] - this_local_vertex = findfirst(i->toglobal(grid, VertexIndex(cell, i)) == global_vertexid, 1:nvertices(cells[cell])) - push!(vertex_neighbors_local, VertexIndex(cell, this_local_vertex)) - other_vertices = findfirst.(x->x!=global_vertexid,neighbor_connected_faces) - any(other_vertices .=== nothing) && continue - neighbor_vertices_global = getindex.(neighbor_connected_faces, other_vertices) - neighbor_vertices_local = [VertexIndex(cell,local_vertex) for local_vertex ∈ findall(x->x ∈ neighbor_vertices_global, vertices(cells[cell]))] - append!(vertex_neighbors_local, neighbor_vertices_local) + stencil_table = ArrayOfVectorViews(VertexIndex[], (getnnodes(grid),); sizehint = 10) do buf + # Vertex Connectivity + for (global_vertexid,cellset) ∈ enumerate(top.vertex_to_cell) + for cell ∈ cellset + neighbor_boundary = edges(cells[cell]) + neighbor_connected_faces = neighbor_boundary[findall(x->global_vertexid ∈ x, neighbor_boundary)] + this_local_vertex = findfirst(i->toglobal(grid, VertexIndex(cell, i)) == global_vertexid, 1:nvertices(cells[cell])) + push_at_index!(buf, VertexIndex(cell, this_local_vertex), global_vertexid) + other_vertices = findfirst.(x->x!=global_vertexid,neighbor_connected_faces) + any(other_vertices .=== nothing) && continue + neighbor_vertices_global = getindex.(neighbor_connected_faces, other_vertices) + neighbor_vertices_local = [VertexIndex(cell,local_vertex) for local_vertex ∈ findall(x->x ∈ neighbor_vertices_global, vertices(cells[cell]))] + for vertex_index in neighbor_vertices_local + push_at_index!(buf, vertex_index, global_vertexid) + end + end end - stencil_table[global_vertexid] = EntityNeighborhood(vertex_neighbors_local) end return stencil_table end """ - getstencil(top::Dict{Int, EntityNeighborhood{VertexIndex}}, grid::AbstractGrid, vertex_idx::VertexIndex) -> EntityNeighborhood{VertexIndex} + getstencil(top::ArrayOfVectorViews{VertexIndex, 1}, grid::AbstractGrid, vertex_idx::VertexIndex) -> AbstractVector{VertexIndex} Get an iterateable over the stencil members for a given local entity. """ -function getstencil(top::Dict{Int, EntityNeighborhood{VertexIndex}}, grid::Grid, vertex_idx::VertexIndex) - return top[toglobal(grid, vertex_idx)].neighbor_info +function getstencil(top::ArrayOfVectorViews{VertexIndex, 1}, grid::Grid, vertex_idx::VertexIndex) + return top[toglobal(grid, vertex_idx)] end """ - _create_skeleton(neighborhood::Matrix{EntityNeighborhood{BI}}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} + _create_skeleton(neighborhood::AbstractMatrix{AbstractVector{BI}}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} Materializes the skeleton from the `neighborhood` information by returning a `Vector{BI}` with `BI`s describing the unique entities in the grid. @@ -356,18 +363,17 @@ the unique entities in the grid. *Example:* With `BI=EdgeIndex`, and an edge between cells and 1 and 2, with vertices 2 and 5, could be described by either `EdgeIndex(1, 2)` or `EdgeIndex(2, 4)`, but only one of these will be in the vector returned by this function. """ -function _create_skeleton(neighborhood::Matrix{EntityNeighborhood{BI}}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} +function _create_skeleton(neighborhood::ArrayOfVectorViews{BI, 2}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} i = 1 - skeleton = Vector{BI}(undef, length(neighborhood) - count(neighbors -> !isempty(neighbors) , neighborhood) ÷ 2) + skeleton = Vector{BI}(undef, length(neighborhood) - count(neighbors -> !isempty(neighbors) , values(neighborhood)) ÷ 2) for (idx, entity) in pairs(neighborhood) - isempty(entity.neighbor_info) || entity.neighbor_info[][1] > idx[1] || continue + isempty(entity) || entity[][1] > idx[1] || continue skeleton[i] = BI(idx[1], idx[2]) i += 1 end return skeleton end -#TODO: For the specific entities the grid input is unused """ vertexskeleton(top::ExclusiveTopology, ::AbstractGrid) -> Vector{VertexIndex} diff --git a/src/Grid/utils.jl b/src/Grid/utils.jl index fe96df897c..c684409aaf 100644 --- a/src/Grid/utils.jl +++ b/src/Grid/utils.jl @@ -160,7 +160,7 @@ function _create_boundaryset(f::Function, grid::AbstractGrid, top::ExclusiveTopo function _makeset(ff_nh) set = OrderedSet{BI}() for (ff_nh_idx, neighborhood) in pairs(ff_nh) - # ff_nh_idx::CartesianIndex into Matrix{<:EntityNeighborhood} + # ff_nh_idx::CartesianIndex into AbstractMatrix{AbstractVector{BI}} isempty(neighborhood) || continue # Skip any facets with neighbors (not on boundary) cell_idx = ff_nh_idx[1] facet_nr = ff_nh_idx[2] diff --git a/src/iterators.jl b/src/iterators.jl index 6360b76b63..b37721f4b3 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -372,7 +372,7 @@ function Base.iterate(ii::InterfaceIterator{<:Any, <:Grid{sdim}}, state...) wher if isempty(neighborhood[facet_a[1], facet_a[2]]) continue end - neighbors = neighborhood[facet_a[1], facet_a[2]].neighbor_info + neighbors = neighborhood[facet_a[1], facet_a[2]] length(neighbors) > 1 && error("multiple neighboring faces not supported yet") facet_b = neighbors[1] reinit!(ii.cache, facet_a, facet_b) diff --git a/test/runtests.jl b/test/runtests.jl index 3a5e35e708..2a04c9dcef 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -35,6 +35,7 @@ end include("test_utils.jl") # Unit tests +include("test_collectionsofviews.jl") include("test_interpolations.jl") include("test_cellvalues.jl") include("test_facevalues.jl") diff --git a/test/test_collectionsofviews.jl b/test/test_collectionsofviews.jl new file mode 100644 index 0000000000..4bca3ff5a2 --- /dev/null +++ b/test/test_collectionsofviews.jl @@ -0,0 +1,46 @@ +@testset "ArrayOfVectorViews" begin + # Create a vector sorting integers into bins and check + test_ints = rand(0:99, 100) + # Create for 3 different sizehints + aovs = map([20, 1, 100]) do sh + Ferrite.ArrayOfVectorViews(Int[], (10,); sizehint=sh) do buf + for v in test_ints + idx = 1 + v ÷ 10 + Ferrite.push_at_index!(buf, v, idx) + end + end + end + # Check correct values for the first one + for (idx, v) in enumerate(aovs[1]) + interval = (10 * (idx-1)):(10 * idx - 1) + @test all(x -> x ∈ interval, v) + @test count(x -> x ∈ interval, test_ints) == length(v) + end + for aov in aovs + @test sum(length, aov; init=0) == length(test_ints) + end + # Check that the result is independent of sizehint + for idx in eachindex(aovs[1]) + for aov in aovs[2:end] + @test aovs[1][idx] == aov[idx] + end + end + + # Create an array with random tuple containing and place the tuples + # according to the values. Check for 2d and 3d arrays. + for N in 2:3 + tvals = [ntuple(i->rand(0:9), N) for _ in 1:1000] + aov = Ferrite.ArrayOfVectorViews(NTuple{N, Int}[], (5,5,5)[1:N]; sizehint=10) do buf + for v in tvals + idx = 1 .+ v .÷ 2 + Ferrite.push_at_index!(buf, v, idx...) + end + end + @test sum(length, aov; init=0) == length(tvals) + for (idx, v) in pairs(aov) + intervals = map(i -> (2 * (i-1)):(2 * i - 1), idx.I) + @test all(x -> all(map((z, r) -> z ∈ r, x, intervals)), v) + @test count(x -> all(map((z, r) -> z ∈ r, x, intervals)), tvals) == length(v) + end + end +end diff --git a/test/test_dofs.jl b/test/test_dofs.jl index f0417f2d5f..7cd3e47011 100644 --- a/test/test_dofs.jl +++ b/test/test_dofs.jl @@ -659,8 +659,8 @@ end # test inner coupling _check_dofs(K, dh, sdh, cell_idx, coupling, coupling_idx, vdim, [cell_idx], false) # test cross-element coupling - neighborhood = Ferrite.getrefdim(dh.grid.cells[1]) > 1 ? topology.face_face_neighbor : topology.vertex_vertex_neighbor - neighbors = neighborhood[cell_idx, :] + neighborhood = Ferrite.get_facet_facet_neighborhood(topology, grid) + neighbors = [neighborhood[cell_idx, i] for i in 1:size(neighborhood, 2)] _check_dofs(K, dh, sdh, cell_idx, interface_coupling, interface_coupling_idx, vdim, [i[1][1] for i in neighbors[.!isempty.(neighbors)]], true) end end diff --git a/test/test_grid_dofhandler_vtk.jl b/test/test_grid_dofhandler_vtk.jl index 3ae5d15124..fa78faf033 100644 --- a/test/test_grid_dofhandler_vtk.jl +++ b/test/test_grid_dofhandler_vtk.jl @@ -283,19 +283,26 @@ end end @testset "Grid topology" begin + # Error paths + mixed_rdim_grid = Grid([Triangle((1,2,3)), Line((2,3))], [Node((0.0, 0.0)), Node((1.0, 0.0)), Node((1.0, 1.1))]) + @test_throws ErrorException ExclusiveTopology(mixed_rdim_grid) + top_line = ExclusiveTopology(generate_grid(Line, (3,))) + @test_throws ArgumentError Ferrite.get_facet_facet_neighborhood(top_line, mixed_rdim_grid) + @test_throws ArgumentError Ferrite.getneighborhood(top_line, mixed_rdim_grid, FacetIndex(1,2)) + # # (1) (2) (3) (4) # +---+---+---+ # linegrid = generate_grid(Line,(3,)) linetopo = ExclusiveTopology(linegrid) - @test linetopo.vertex_vertex_neighbor[1,2] == Ferrite.EntityNeighborhood(VertexIndex(2,1)) + @test linetopo.vertex_vertex_neighbor[1,2] == [VertexIndex(2,1)] @test getneighborhood(linetopo, linegrid, VertexIndex(1,2)) == [VertexIndex(2,1)] - @test linetopo.vertex_vertex_neighbor[2,1] == Ferrite.EntityNeighborhood(VertexIndex(1,2)) + @test linetopo.vertex_vertex_neighbor[2,1] == [VertexIndex(1,2)] @test getneighborhood(linetopo, linegrid, VertexIndex(2,1)) == [VertexIndex(1,2)] - @test linetopo.vertex_vertex_neighbor[2,2] == Ferrite.EntityNeighborhood(VertexIndex(3,1)) + @test linetopo.vertex_vertex_neighbor[2,2] == [VertexIndex(3,1)] @test getneighborhood(linetopo, linegrid, VertexIndex(2,2)) == [VertexIndex(3,1)] - @test linetopo.vertex_vertex_neighbor[3,1] == Ferrite.EntityNeighborhood(VertexIndex(2,2)) + @test linetopo.vertex_vertex_neighbor[3,1] == [VertexIndex(2,2)] @test getneighborhood(linetopo, linegrid, VertexIndex(3,1)) == [VertexIndex(2,2)] @test getneighborhood(linetopo, linegrid, FacetIndex(3,1)) == getneighborhood(linetopo, linegrid, VertexIndex(3,1)) @@ -321,36 +328,38 @@ end topology = ExclusiveTopology(quadgrid) faceskeleton = Ferrite.edgeskeleton(topology, quadgrid) #test vertex neighbors maps cellid and local vertex id to neighbor id and neighbor local vertex id - @test topology.vertex_vertex_neighbor[1,3] == Ferrite.EntityNeighborhood(VertexIndex(4,1)) - @test topology.vertex_vertex_neighbor[2,4] == Ferrite.EntityNeighborhood(VertexIndex(3,2)) + @test topology.vertex_vertex_neighbor[1,3] == [VertexIndex(4,1)] + @test topology.vertex_vertex_neighbor[2,4] == [VertexIndex(3,2)] @test Set(getneighborhood(topology, quadgrid, VertexIndex(2,4))) == Set([VertexIndex(1,3), VertexIndex(3,2), VertexIndex(4,1)]) - @test topology.vertex_vertex_neighbor[3,3] == Ferrite.EntityNeighborhood(VertexIndex(6,1)) - @test topology.vertex_vertex_neighbor[3,2] == Ferrite.EntityNeighborhood(VertexIndex(2,4)) - @test topology.vertex_vertex_neighbor[4,1] == Ferrite.EntityNeighborhood(VertexIndex(1,3)) - @test topology.vertex_vertex_neighbor[4,4] == Ferrite.EntityNeighborhood(VertexIndex(5,2)) - @test topology.vertex_vertex_neighbor[5,2] == Ferrite.EntityNeighborhood(VertexIndex(4,4)) - @test topology.vertex_vertex_neighbor[6,1] == Ferrite.EntityNeighborhood(VertexIndex(3,3)) + @test topology.vertex_vertex_neighbor[3,3] == [VertexIndex(6,1)] + @test topology.vertex_vertex_neighbor[3,2] == [VertexIndex(2,4)] + @test topology.vertex_vertex_neighbor[4,1] == [VertexIndex(1,3)] + @test topology.vertex_vertex_neighbor[4,4] == [VertexIndex(5,2)] + @test topology.vertex_vertex_neighbor[5,2] == [VertexIndex(4,4)] + @test topology.vertex_vertex_neighbor[6,1] == [VertexIndex(3,3)] @test isempty(getneighborhood(topology, quadgrid, VertexIndex(2,2))) @test length(getneighborhood(topology, quadgrid, VertexIndex(2,4))) == 3 #test face neighbor maps cellid and local face id to neighbor id and neighbor local face id - @test topology.edge_edge_neighbor[1,2] == Ferrite.EntityNeighborhood(EdgeIndex(2,4)) - @test topology.edge_edge_neighbor[1,3] == Ferrite.EntityNeighborhood(EdgeIndex(3,1)) - @test topology.edge_edge_neighbor[2,3] == Ferrite.EntityNeighborhood(EdgeIndex(4,1)) - @test topology.edge_edge_neighbor[2,4] == Ferrite.EntityNeighborhood(EdgeIndex(1,2)) - @test topology.edge_edge_neighbor[3,1] == Ferrite.EntityNeighborhood(EdgeIndex(1,3)) - @test topology.edge_edge_neighbor[3,2] == Ferrite.EntityNeighborhood(EdgeIndex(4,4)) - @test topology.edge_edge_neighbor[3,3] == Ferrite.EntityNeighborhood(EdgeIndex(5,1)) - @test topology.edge_edge_neighbor[4,1] == Ferrite.EntityNeighborhood(EdgeIndex(2,3)) - @test topology.edge_edge_neighbor[4,3] == Ferrite.EntityNeighborhood(EdgeIndex(6,1)) - @test topology.edge_edge_neighbor[4,4] == Ferrite.EntityNeighborhood(EdgeIndex(3,2)) - @test topology.edge_edge_neighbor[5,1] == Ferrite.EntityNeighborhood(EdgeIndex(3,3)) - @test topology.edge_edge_neighbor[5,2] == Ferrite.EntityNeighborhood(EdgeIndex(6,4)) - @test topology.edge_edge_neighbor[5,3] == Ferrite.EntityNeighborhood(Ferrite.BoundaryIndex[]) - @test topology.edge_edge_neighbor[5,4] == Ferrite.EntityNeighborhood(Ferrite.BoundaryIndex[]) - @test topology.edge_edge_neighbor[6,1] == Ferrite.EntityNeighborhood(EdgeIndex(4,3)) - @test topology.edge_edge_neighbor[6,2] == Ferrite.EntityNeighborhood(Ferrite.BoundaryIndex[]) - @test topology.edge_edge_neighbor[6,3] == Ferrite.EntityNeighborhood(Ferrite.BoundaryIndex[]) - @test topology.edge_edge_neighbor[6,4] == Ferrite.EntityNeighborhood(EdgeIndex(5,2)) + @test topology.edge_edge_neighbor[1,2] == [EdgeIndex(2,4)] + @test topology.edge_edge_neighbor[1,3] == [EdgeIndex(3,1)] + @test topology.edge_edge_neighbor[2,3] == [EdgeIndex(4,1)] + @test topology.edge_edge_neighbor[2,4] == [EdgeIndex(1,2)] + @test topology.edge_edge_neighbor[3,1] == [EdgeIndex(1,3)] + @test topology.edge_edge_neighbor[3,2] == [EdgeIndex(4,4)] + @test topology.edge_edge_neighbor[3,3] == [EdgeIndex(5,1)] + @test topology.edge_edge_neighbor[4,1] == [EdgeIndex(2,3)] + @test topology.edge_edge_neighbor[4,3] == [EdgeIndex(6,1)] + @test topology.edge_edge_neighbor[4,4] == [EdgeIndex(3,2)] + @test topology.edge_edge_neighbor[5,1] == [EdgeIndex(3,3)] + @test topology.edge_edge_neighbor[5,2] == [EdgeIndex(6,4)] + @test isempty(topology.edge_edge_neighbor[5,3]) + @test isempty(topology.edge_edge_neighbor[5,4]) + @test topology.edge_edge_neighbor[6,1] == [EdgeIndex(4,3)] + @test isempty(topology.edge_edge_neighbor[6,2]) + @test isempty(topology.edge_edge_neighbor[6,3]) + @test topology.edge_edge_neighbor[6,4] == [EdgeIndex(5,2)] + @test getneighborhood(topology, quadgrid, EdgeIndex(6, 4), false) == [EdgeIndex(5,2)] + @test Set(getneighborhood(topology, quadgrid, EdgeIndex(6, 4), true)) == Set([EdgeIndex(6, 4), EdgeIndex(5,2)]) @test getneighborhood(topology, quadgrid, EdgeIndex(2,4)) == getneighborhood(topology, quadgrid, FacetIndex(2,4)) @@ -383,14 +392,14 @@ end # (11) hexgrid = generate_grid(Hexahedron,(2,2,1)) topology = ExclusiveTopology(hexgrid) - @test topology.edge_edge_neighbor[1,11] == Ferrite.EntityNeighborhood(EdgeIndex(4,9)) + @test topology.edge_edge_neighbor[1,11] == [EdgeIndex(4,9)] @test Set(getneighborhood(topology,hexgrid,EdgeIndex(1,11),true)) == Set([EdgeIndex(4,9),EdgeIndex(2,12),EdgeIndex(3,10),EdgeIndex(1,11)]) @test Set(getneighborhood(topology,hexgrid,EdgeIndex(1,11),false)) == Set([EdgeIndex(4,9),EdgeIndex(2,12),EdgeIndex(3,10)]) - @test topology.edge_edge_neighbor[2,12] == Ferrite.EntityNeighborhood(EdgeIndex(3,10)) + @test topology.edge_edge_neighbor[2,12] == [EdgeIndex(3,10)] @test Set(getneighborhood(topology,hexgrid,EdgeIndex(2,12),true)) == Set([EdgeIndex(3,10),EdgeIndex(1,11),EdgeIndex(4,9),EdgeIndex(2,12)]) @test Set(getneighborhood(topology,hexgrid,EdgeIndex(2,12),false)) == Set([EdgeIndex(3,10),EdgeIndex(1,11),EdgeIndex(4,9)]) - @test topology.edge_edge_neighbor[3,10] == Ferrite.EntityNeighborhood(EdgeIndex(2,12)) - @test topology.edge_edge_neighbor[4,9] == Ferrite.EntityNeighborhood(EdgeIndex(1,11)) + @test topology.edge_edge_neighbor[3,10] == [EdgeIndex(2,12)] + @test topology.edge_edge_neighbor[4,9] == [EdgeIndex(1,11)] @test getneighborhood(topology,hexgrid,FaceIndex((1,3))) == [FaceIndex((2,5))] @test getneighborhood(topology,hexgrid,FaceIndex((1,4))) == [FaceIndex((3,2))] @test getneighborhood(topology,hexgrid,FaceIndex((2,4))) == [FaceIndex((4,2))] @@ -399,6 +408,7 @@ end @test getneighborhood(topology,hexgrid,FaceIndex((3,3))) == [FaceIndex((4,5))] @test getneighborhood(topology,hexgrid,FaceIndex((4,2))) == [FaceIndex((2,4))] @test getneighborhood(topology,hexgrid,FaceIndex((4,5))) == [FaceIndex((3,3))] + @test Set(getneighborhood(topology, hexgrid, FaceIndex((4,5)), true)) == Set([FaceIndex((3,3)), FaceIndex((4,5))]) @test getneighborhood(topology, hexgrid, FaceIndex(2,4)) == getneighborhood(topology, hexgrid, FacetIndex(2,4)) @@ -429,7 +439,12 @@ end # test for multiple vertex_neighbors as in e.g. ele 3, local vertex 3 (middle node) trigrid = generate_grid(Triangle,(2,2)) topology = ExclusiveTopology(trigrid) - @test topology.vertex_vertex_neighbor[3,3] == Ferrite.EntityNeighborhood([VertexIndex(5,2),VertexIndex(6,1),VertexIndex(7,1)]) + tri_vert_nbset = Set([VertexIndex(5,2), VertexIndex(6,1), VertexIndex(7,1)]) # Exclusive neighbors + @test Set(topology.vertex_vertex_neighbor[3,3]) == tri_vert_nbset + union!(tri_vert_nbset, [VertexIndex(4, 3), VertexIndex(2, 2)]) # Add vertices shared via edges as well + @test Set(getneighborhood(topology, trigrid, VertexIndex(3,3), false)) == tri_vert_nbset + union!(tri_vert_nbset, [VertexIndex(3,3)]) # Add self + @test Set(getneighborhood(topology, trigrid, VertexIndex(3,3), true)) == tri_vert_nbset quadtrigrid = generate_grid(QuadraticTriangle,(2,2)) quadtopology = ExclusiveTopology(trigrid) @@ -461,7 +476,19 @@ end FaceIndex(5,1), FaceIndex(5,3), FaceIndex(5,4), FaceIndex(6,1), FaceIndex(6,3), ]) -# test mixed grid +# test grids with mixed celltypes of same refdim +# (4)---(5) +# | | \ +# | 1 |2 \ +# (1)---(2)-(3) + tet_quad_grid = Grid( + [Quadrilateral((1, 2, 5, 4)), Triangle((2, 3, 5))], + [Node(Float64.((x, y))) for (x, y) in ((0,0), (1,0), (2,0), (0,1), (1,1))]) + top = ExclusiveTopology(tet_quad_grid) + @test getneighborhood(top, tet_quad_grid, FacetIndex(1, 2)) == [EdgeIndex(2,3)] + @test Set(getneighborhood(top, tet_quad_grid, FacetIndex(1, 2), true)) == Set([EdgeIndex(1,2), EdgeIndex(2,3)]) + +# test grids with mixed refdim cells = [ Hexahedron((1, 2, 3, 4, 5, 6, 7, 8)), Hexahedron((11, 13, 14, 12, 15, 16, 17, 18)), @@ -470,11 +497,12 @@ end ] nodes = [Node(coord) for coord in zeros(Vec{3,Float64}, 18)] grid = Grid(cells, nodes) - topology = ExclusiveTopology(grid) + @test_throws ErrorException ExclusiveTopology(grid) + # topology = ExclusiveTopology(grid) - @test_throws ArgumentError Ferrite.facetskeleton(topology, grid) - # @test topology.face_face_neighbor[3,4] == Ferrite.EntityNeighborhood(EdgeIndex(1,2)) - # @test topology.edge_edge_neighbor[1,2] == Ferrite.EntityNeighborhood(FaceIndex(3,4)) + # @test_throws ArgumentError Ferrite.facetskeleton(topology, grid) + # @test topology.face_face_neighbor[3,4] == [EdgeIndex(1,2)) + # @test topology.edge_edge_neighbor[1,2] == [FaceIndex(3,4)) # # regression that it doesn't error for boundary faces, see https://github.com/Ferrite-FEM/Ferrite.jl/issues/518 # @test topology.face_face_neighbor[1,6] == topology.face_face_neighbor[1,1] == zero(Ferrite.EntityNeighborhood{FaceIndex}) # @test topology.edge_edge_neighbor[1,1] == topology.edge_edge_neighbor[1,3] == zero(Ferrite.EntityNeighborhood{FaceIndex}) @@ -489,10 +517,11 @@ end ] nodes = [Node(coord) for coord in zeros(Vec{2,Float64}, 18)] grid = Grid(cells, nodes) - topology = ExclusiveTopology(grid) - @test_throws ArgumentError Ferrite.facetskeleton(topology, grid) - @test_throws ArgumentError getneighborhood(topology, grid, FacetIndex(1,1)) - @test_throws ArgumentError Ferrite.get_facet_facet_neighborhood(topology, grid) + @test_throws ErrorException ExclusiveTopology(grid) + # topology = ExclusiveTopology(grid) + # @test_throws ArgumentError Ferrite.facetskeleton(topology, grid) + # @test_throws ArgumentError getneighborhood(topology, grid, FacetIndex(1,1)) + # @test_throws ArgumentError Ferrite.get_facet_facet_neighborhood(topology, grid) # # +-----+-----+-----+ @@ -516,15 +545,17 @@ end @test issubset([4,5,8], patches[7]) @test issubset([7,4,5,6,9], patches[8]) @test issubset([8,5,6], patches[9]) + @test 5 ∉ getneighborhood(topology, quadgrid, CellIndex(5)) + @test 5 ∈ getneighborhood(topology, quadgrid, CellIndex(5), true) # test star stencils stars = Ferrite.vertex_star_stencils(topology, quadgrid) @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(1,1))) == Set([VertexIndex(1,2), VertexIndex(1,4), VertexIndex(1,1)]) @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(2,1))) == Set([VertexIndex(1,1), VertexIndex(1,3), VertexIndex(2,2), VertexIndex(2,4), VertexIndex(1,2), VertexIndex(2,1)]) @test Set(Ferrite.getstencil(stars, quadgrid, VertexIndex(5,4))) == Set([VertexIndex(4,2), VertexIndex(4,4), VertexIndex(5,1), VertexIndex(5,3), VertexIndex(7,1), VertexIndex(7,3), VertexIndex(8,2), VertexIndex(8,4), VertexIndex(4,3), VertexIndex(5,4), VertexIndex(7,2), VertexIndex(8,1)]) - @test Set(Ferrite.toglobal(quadgrid, Ferrite.getstencil(stars, quadgrid, VertexIndex(1,1)))) == Set([1,2,5]) - @test Set(Ferrite.toglobal(quadgrid, Ferrite.getstencil(stars, quadgrid, VertexIndex(2,1)))) == Set([2,1,6,3]) - @test Set(Ferrite.toglobal(quadgrid, Ferrite.getstencil(stars, quadgrid, VertexIndex(5,4)))) == Set([10,6,9,11,14]) + @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(1,1)))) == Set([1,2,5]) + @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(2,1)))) == Set([2,1,6,3]) + @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(5,4)))) == Set([10,6,9,11,14]) face_skeleton = Ferrite.edgeskeleton(topology, quadgrid) @test Set(face_skeleton) == Set([EdgeIndex(1,1),EdgeIndex(1,2),EdgeIndex(1,3),EdgeIndex(1,4), From baf8aad71c4fcc22bf98a7343c1512b0da01b7c7 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 9 Jul 2024 20:07:17 +0200 Subject: [PATCH 03/50] facetskeleton in ExclusiveTopology (#998) This patch corrects #914 where the skeletons were split into vertex, edge, and face (but the algorithms don't actually correctly calculate such skeletons). Hence this effectively renames the pre-914 faceskeleton to facetskeleton, which now returns a Vector{FacetIndex}. --- docs/src/reference/grid.md | 10 ++-- docs/src/topics/grid.md | 6 +-- src/Grid/topology.jl | 91 ++++++++------------------------ src/exports.jl | 2 +- test/test_grid_dofhandler_vtk.jl | 84 ++++++++++++++--------------- 5 files changed, 74 insertions(+), 119 deletions(-) diff --git a/docs/src/reference/grid.md b/docs/src/reference/grid.md index b5d8d7c987..d601c0c00d 100644 --- a/docs/src/reference/grid.md +++ b/docs/src/reference/grid.md @@ -41,11 +41,11 @@ Ferrite.getrefdim ### Topology ```@docs -Ferrite.ExclusiveTopology -Ferrite.getneighborhood -Ferrite.faceskeleton -Ferrite.vertex_star_stencils -Ferrite.getstencil +ExclusiveTopology +getneighborhood +facetskeleton +vertex_star_stencils +getstencil ``` ### Grid Sets Utility diff --git a/docs/src/topics/grid.md b/docs/src/topics/grid.md index 69ceb77fea..f3c3138757 100644 --- a/docs/src/topics/grid.md +++ b/docs/src/topics/grid.md @@ -144,7 +144,7 @@ In order to use boundaries, e.g. for Dirichlet constraints in the ConstraintHand ## Topology -Ferrite.jl's `Grid` type offers experimental features w.r.t. topology information. The functions [`Ferrite.getneighborhood`](@ref) and [`Ferrite.facetskeleton`](@ref) -are the interface to obtain topological information. The [`Ferrite.getneighborhood`](@ref) can construct lists of directly connected entities based on a given entity +Ferrite.jl's `Grid` type offers experimental features w.r.t. topology information. The functions [`getneighborhood`](@ref) and [`facetskeleton`](@ref) +are the interface to obtain topological information. The [`getneighborhood`](@ref) can construct lists of directly connected entities based on a given entity (`CellIndex`, `FacetIndex`, `FaceIndex`, `EdgeIndex`, or `VertexIndex`). -The [`Ferrite.facetskeleton`](@ref) function can be used to evaluate integrals over material interfaces or computing element interface values such as jumps. +The [`facetskeleton`](@ref) function can be used to evaluate integrals over material interfaces or computing element interface values such as jumps. diff --git a/src/Grid/topology.jl b/src/Grid/topology.jl index 0d314a89eb..c5ace16bce 100644 --- a/src/Grid/topology.jl +++ b/src/Grid/topology.jl @@ -48,10 +48,7 @@ mutable struct ExclusiveTopology <: AbstractTopology edge_edge_neighbor::ArrayOfVectorViews{EdgeIndex, 2} # vertex_vertex_neighbor[cellid,local_vertex_id] -> exclusive connected entities to the given vertex vertex_vertex_neighbor::ArrayOfVectorViews{VertexIndex, 2} - - face_skeleton::Union{Vector{FaceIndex}, Nothing} - edge_skeleton::Union{Vector{EdgeIndex}, Nothing} - vertex_skeleton::Union{Vector{VertexIndex}, Nothing} + facet_skeleton::Union{Vector{FacetIndex}, Nothing} end function ExclusiveTopology(grid::AbstractGrid{sdim}) where sdim @@ -96,7 +93,7 @@ function ExclusiveTopology(grid::AbstractGrid{sdim}) where sdim face_face_neighbor = ArrayOfVectorViews(face_face_neighbor_buf) edge_edge_neighbor = ArrayOfVectorViews(edge_edge_neighbor_buf) vertex_vertex_neighbor = ArrayOfVectorViews(vertex_vertex_neighbor_buf) - return ExclusiveTopology(vertex_to_cell, cell_neighbor, face_face_neighbor, edge_edge_neighbor, vertex_vertex_neighbor, nothing, nothing, nothing) + return ExclusiveTopology(vertex_to_cell, cell_neighbor, face_face_neighbor, edge_edge_neighbor, vertex_vertex_neighbor, nothing) end function get_facet_facet_neighborhood(t::ExclusiveTopology, g::AbstractGrid) @@ -355,85 +352,43 @@ function getstencil(top::ArrayOfVectorViews{VertexIndex, 1}, grid::Grid, vertex_ end """ - _create_skeleton(neighborhood::AbstractMatrix{AbstractVector{BI}}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} + _create_facet_skeleton(neighborhood::AbstractMatrix{AbstractVector{BI}}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} -Materializes the skeleton from the `neighborhood` information by returning a `Vector{BI}` with `BI`s describing -the unique entities in the grid. +Materializes the skeleton from the `neighborhood` information by returning a `Vector{FacetIndex}` describing the +unique facets in the grid. *Example:* With `BI=EdgeIndex`, and an edge between cells and 1 and 2, with vertices 2 and 5, could be described by either `EdgeIndex(1, 2)` or `EdgeIndex(2, 4)`, but only one of these will be in the vector returned by this function. """ -function _create_skeleton(neighborhood::ArrayOfVectorViews{BI, 2}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} +function _create_facet_skeleton(neighborhood::ArrayOfVectorViews{BI, 2}) where BI <: Union{FaceIndex, EdgeIndex, VertexIndex} i = 1 - skeleton = Vector{BI}(undef, length(neighborhood) - count(neighbors -> !isempty(neighbors) , values(neighborhood)) ÷ 2) + skeleton = Vector{FacetIndex}(undef, length(neighborhood) - count(neighbors -> !isempty(neighbors) , values(neighborhood)) ÷ 2) for (idx, entity) in pairs(neighborhood) isempty(entity) || entity[][1] > idx[1] || continue - skeleton[i] = BI(idx[1], idx[2]) + skeleton[i] = FacetIndex(idx[1], idx[2]) i += 1 end return skeleton end -""" - vertexskeleton(top::ExclusiveTopology, ::AbstractGrid) -> Vector{VertexIndex} - -Materializes the skeleton from the `neighborhood` information by returning a `Vector{VertexIndex}` -describing the unique vertices in the grid. (One unique vertex may have multiple `VertexIndex`, but only -one is included in the returned `Vector`) -""" -function vertexskeleton(top::ExclusiveTopology, ::Union{AbstractGrid,Nothing}=nothing) - if top.vertex_skeleton === nothing - top.vertex_skeleton = _create_skeleton(top.vertex_vertex_neighbor) - end - return top.vertex_skeleton -end - -""" - edgeskeleton(top::ExclusiveTopology, ::AbstractGrid) -> Vector{EdgeIndex} - -Materializes the skeleton from the `neighborhood` information by returning a `Vector{EdgeIndex}` -describing the unique edge in the grid. (One unique edge may have multiple `EdgeIndex`, but only -one is included in the returned `Vector`) -""" -function edgeskeleton(top::ExclusiveTopology, ::Union{AbstractGrid,Nothing}=nothing) - if top.edge_skeleton === nothing - top.edge_skeleton = _create_skeleton(top.edge_edge_neighbor) - end - return top.edge_skeleton -end - -""" - faceskeleton(top::ExclusiveTopology, ::AbstractGrid) -> Vector{FaceIndex} - -Materializes the skeleton from the `neighborhood` information by returning a `Vector{FaceIndex}` -describing the unique faces in the grid. (One unique face may have multiple `FaceIndex`, but only -one is included in the returned `Vector`) -""" -function faceskeleton(top::ExclusiveTopology, ::Union{AbstractGrid,Nothing}=nothing) - if top.face_skeleton === nothing - top.face_skeleton = _create_skeleton(top.face_face_neighbor) - end - return top.face_skeleton -end - """ facetskeleton(top::ExclusiveTopology, grid::AbstractGrid) -Materializes the skeleton from the `neighborhood` information by returning a `Vector{BI}` where -`BI <: Union{VertexIndex, EdgeIndex, FaceIndex}`. -It describes the unique facets in the grid, and allows for dimension-independent code in the case -that all cells have the same reference dimension. For cells with different reference dimensions, -[`Ferrite.vertexskeleton`](@ref), [`Ferrite.edgeskeleton`](@ref), or [`Ferrite.faceskeleton`](@ref) -must be used explicitly. +Materializes the skeleton from the `neighborhood` information by returning an iterable over the +unique facets in the grid, described by `FacetIndex`. """ function facetskeleton(top::ExclusiveTopology, grid::AbstractGrid) - rdim = get_reference_dimension(grid) - return _facetskeleton(top, Val(rdim)) -end -_facetskeleton(top::ExclusiveTopology, #=rdim=#::Val{1}) = vertexskeleton(top) -_facetskeleton(top::ExclusiveTopology, #=rdim=#::Val{2}) = edgeskeleton(top) -_facetskeleton(top::ExclusiveTopology, #=rdim=#::Val{3}) = faceskeleton(top) -function _facetskeleton(::ExclusiveTopology, #=rdim=#::Val{:mixed}) - throw(ArgumentError("facetskeleton is only supported for grids containing cells with a common reference dimension. - For mixed-dimensionality grid, use `faceskeleton`, `edgeskeleton`, and `vertexskeleton` explicitly")) + if top.facet_skeleton === nothing + rdim = get_reference_dimension(grid) + top.facet_skeleton = if rdim == 1 + _create_facet_skeleton(top.vertex_vertex_neighbor) + elseif rdim == 2 + _create_facet_skeleton(top.edge_edge_neighbor) + elseif rdim == 3 + _create_facet_skeleton(top.face_face_neighbor) + else + throw(ArgumentError("facetskeleton not supported for refdim = $rdim")) + end + end + return top.facet_skeleton end diff --git a/src/exports.jl b/src/exports.jl index 87d3f66b89..f2c17a0d11 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -80,7 +80,7 @@ export geometric_interpolation, ExclusiveTopology, getneighborhood, - faceskeleton, + facetskeleton, vertex_star_stencils, getstencil, getcells, diff --git a/test/test_grid_dofhandler_vtk.jl b/test/test_grid_dofhandler_vtk.jl index fa78faf033..7677f67348 100644 --- a/test/test_grid_dofhandler_vtk.jl +++ b/test/test_grid_dofhandler_vtk.jl @@ -306,13 +306,13 @@ end @test getneighborhood(linetopo, linegrid, VertexIndex(3,1)) == [VertexIndex(2,2)] @test getneighborhood(linetopo, linegrid, FacetIndex(3,1)) == getneighborhood(linetopo, linegrid, VertexIndex(3,1)) - linefaceskeleton = Ferrite.vertexskeleton(linetopo, linegrid) + linefaceskeleton = Ferrite.facetskeleton(linetopo, linegrid) quadlinegrid = generate_grid(QuadraticLine,(3,)) quadlinetopo = ExclusiveTopology(quadlinegrid) quadlinefaceskeleton = Ferrite.facetskeleton(quadlinetopo, quadlinegrid) # Test faceskeleton @test Set(linefaceskeleton) == Set(quadlinefaceskeleton) == Set([ - VertexIndex(1,1), VertexIndex(1,2), VertexIndex(2,2),VertexIndex(3,2), + FacetIndex(1,1), FacetIndex(1,2), FacetIndex(2,2),FacetIndex(3,2), ]) # (11) @@ -326,7 +326,7 @@ end # (2) quadgrid = generate_grid(Quadrilateral,(2,3)) topology = ExclusiveTopology(quadgrid) - faceskeleton = Ferrite.edgeskeleton(topology, quadgrid) + faceskeleton = Ferrite.facetskeleton(topology, quadgrid) #test vertex neighbors maps cellid and local vertex id to neighbor id and neighbor local vertex id @test topology.vertex_vertex_neighbor[1,3] == [VertexIndex(4,1)] @test topology.vertex_vertex_neighbor[2,4] == [VertexIndex(3,2)] @@ -365,15 +365,15 @@ end quadquadgrid = generate_grid(QuadraticQuadrilateral,(2,3)) quadtopology = ExclusiveTopology(quadquadgrid) - quadfaceskeleton = Ferrite.edgeskeleton(quadtopology, quadquadgrid) + quadfaceskeleton = Ferrite.facetskeleton(quadtopology, quadquadgrid) # Test faceskeleton @test Set(faceskeleton) == Set(quadfaceskeleton) == Set([ - EdgeIndex(1,1), EdgeIndex(1,2), EdgeIndex(1,3), EdgeIndex(1,4), - EdgeIndex(2,1), EdgeIndex(2,2), EdgeIndex(2,3), - EdgeIndex(3,2), EdgeIndex(3,3), EdgeIndex(3,4), - EdgeIndex(4,2), EdgeIndex(4,3), - EdgeIndex(5,2), EdgeIndex(5,3), EdgeIndex(5,4), - EdgeIndex(6,2), EdgeIndex(6,3), + FacetIndex(1,1), FacetIndex(1,2), FacetIndex(1,3), FacetIndex(1,4), + FacetIndex(2,1), FacetIndex(2,2), FacetIndex(2,3), + FacetIndex(3,2), FacetIndex(3,3), FacetIndex(3,4), + FacetIndex(4,2), FacetIndex(4,3), + FacetIndex(5,2), FacetIndex(5,3), FacetIndex(5,4), + FacetIndex(6,2), FacetIndex(6,3), ]) # (8) @@ -418,13 +418,13 @@ end @test all(stopology.face_face_neighbor .== topology.face_face_neighbor) @test all(stopology.vertex_vertex_neighbor .== topology.vertex_vertex_neighbor) # Test faceskeleton - faceskeleton = Ferrite.faceskeleton(topology, hexgrid) - sfaceskeleton = Ferrite.faceskeleton(stopology, serendipitygrid) + faceskeleton = Ferrite.facetskeleton(topology, hexgrid) + sfaceskeleton = Ferrite.facetskeleton(stopology, serendipitygrid) @test Set(faceskeleton) == Set(sfaceskeleton) == Set([ - FaceIndex(1,1), FaceIndex(1,2), FaceIndex(1,3), FaceIndex(1,4), FaceIndex(1,5), FaceIndex(1,6), - FaceIndex(2,1), FaceIndex(2,2), FaceIndex(2,3), FaceIndex(2,4), FaceIndex(2,6), - FaceIndex(3,1), FaceIndex(3,3), FaceIndex(3,4), FaceIndex(3,5), FaceIndex(3,6), - FaceIndex(4,1), FaceIndex(4,3), FaceIndex(4,4), FaceIndex(4,6), + FacetIndex(1,1), FacetIndex(1,2), FacetIndex(1,3), FacetIndex(1,4), FacetIndex(1,5), FacetIndex(1,6), + FacetIndex(2,1), FacetIndex(2,2), FacetIndex(2,3), FacetIndex(2,4), FacetIndex(2,6), + FacetIndex(3,1), FacetIndex(3,3), FacetIndex(3,4), FacetIndex(3,5), FacetIndex(3,6), + FacetIndex(4,1), FacetIndex(4,3), FacetIndex(4,4), FacetIndex(4,6), ]) # +-----+-----+ @@ -452,29 +452,29 @@ end @test all(quadtopology.face_face_neighbor .== topology.face_face_neighbor) @test all(quadtopology.vertex_vertex_neighbor .== topology.vertex_vertex_neighbor) # Test faceskeleton - trifaceskeleton = Ferrite.edgeskeleton(topology, trigrid) - quadtrifaceskeleton = Ferrite.edgeskeleton(quadtopology, quadtrigrid) + trifaceskeleton = Ferrite.facetskeleton(topology, trigrid) + quadtrifaceskeleton = Ferrite.facetskeleton(quadtopology, quadtrigrid) @test Set(trifaceskeleton) == Set(quadtrifaceskeleton) == Set([ - EdgeIndex(1,1), EdgeIndex(1,2), EdgeIndex(1,3), - EdgeIndex(2,1), EdgeIndex(2,2), - EdgeIndex(3,1), EdgeIndex(3,2), - EdgeIndex(4,1), EdgeIndex(4,2), - EdgeIndex(5,2), EdgeIndex(5,3), - EdgeIndex(6,1), EdgeIndex(6,2), - EdgeIndex(7,2), - EdgeIndex(8,1), EdgeIndex(8,2), + FacetIndex(1,1), FacetIndex(1,2), FacetIndex(1,3), + FacetIndex(2,1), FacetIndex(2,2), + FacetIndex(3,1), FacetIndex(3,2), + FacetIndex(4,1), FacetIndex(4,2), + FacetIndex(5,2), FacetIndex(5,3), + FacetIndex(6,1), FacetIndex(6,2), + FacetIndex(7,2), + FacetIndex(8,1), FacetIndex(8,2), ]) # Test tetrahedron faceskeleton tetgrid = generate_grid(Tetrahedron, (1,1,1)) topology = ExclusiveTopology(tetgrid) - tetfaceskeleton = Ferrite.faceskeleton(topology, tetgrid) + tetfaceskeleton = Ferrite.facetskeleton(topology, tetgrid) @test Set(tetfaceskeleton) == Set([ - FaceIndex(1,1), FaceIndex(1,2), FaceIndex(1,3), FaceIndex(1,4), - FaceIndex(2,1), FaceIndex(2,2), FaceIndex(2,3), - FaceIndex(3,1), FaceIndex(3,2), FaceIndex(3,3), - FaceIndex(4,1), FaceIndex(4,2), FaceIndex(4,3), - FaceIndex(5,1), FaceIndex(5,3), FaceIndex(5,4), - FaceIndex(6,1), FaceIndex(6,3), + FacetIndex(1,1), FacetIndex(1,2), FacetIndex(1,3), FacetIndex(1,4), + FacetIndex(2,1), FacetIndex(2,2), FacetIndex(2,3), + FacetIndex(3,1), FacetIndex(3,2), FacetIndex(3,3), + FacetIndex(4,1), FacetIndex(4,2), FacetIndex(4,3), + FacetIndex(5,1), FacetIndex(5,3), FacetIndex(5,4), + FacetIndex(6,1), FacetIndex(6,3), ]) # test grids with mixed celltypes of same refdim # (4)---(5) @@ -557,19 +557,19 @@ end @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(2,1)))) == Set([2,1,6,3]) @test Set(Ferrite.toglobal.((quadgrid,), Ferrite.getstencil(stars, quadgrid, VertexIndex(5,4)))) == Set([10,6,9,11,14]) - face_skeleton = Ferrite.edgeskeleton(topology, quadgrid) - @test Set(face_skeleton) == Set([EdgeIndex(1,1),EdgeIndex(1,2),EdgeIndex(1,3),EdgeIndex(1,4), - EdgeIndex(2,1),EdgeIndex(2,2),EdgeIndex(2,3), - EdgeIndex(3,1),EdgeIndex(3,2),EdgeIndex(3,3), - EdgeIndex(4,2),EdgeIndex(4,3),EdgeIndex(4,4), - EdgeIndex(5,2),EdgeIndex(5,3),EdgeIndex(6,2),EdgeIndex(6,3), - EdgeIndex(7,2),EdgeIndex(7,3),EdgeIndex(7,4), - EdgeIndex(8,2),EdgeIndex(8,3),EdgeIndex(9,2),EdgeIndex(9,3)]) + face_skeleton = Ferrite.facetskeleton(topology, quadgrid) + @test Set(face_skeleton) == Set([FacetIndex(1,1),FacetIndex(1,2),FacetIndex(1,3),FacetIndex(1,4), + FacetIndex(2,1),FacetIndex(2,2),FacetIndex(2,3), + FacetIndex(3,1),FacetIndex(3,2),FacetIndex(3,3), + FacetIndex(4,2),FacetIndex(4,3),FacetIndex(4,4), + FacetIndex(5,2),FacetIndex(5,3),FacetIndex(6,2),FacetIndex(6,3), + FacetIndex(7,2),FacetIndex(7,3),FacetIndex(7,4), + FacetIndex(8,2),FacetIndex(8,3),FacetIndex(9,2),FacetIndex(9,3)]) @test length(face_skeleton) == 4*3 + 3*4 quadratic_quadgrid = generate_grid(QuadraticQuadrilateral,(3,3)) quadgrid_topology = ExclusiveTopology(quadratic_quadgrid) - #quadface_skeleton = Ferrite.faceskeleton(topology, quadgrid) + #quadface_skeleton = Ferrite.facetskeleton(topology, quadgrid) #@test quadface_skeleton == face_skeleton # add more regression for https://github.com/Ferrite-FEM/Ferrite.jl/issues/518 From e31e220b9833d68ed8305928e735887b08c49e5b Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Fri, 12 Jul 2024 11:26:55 +0200 Subject: [PATCH 04/50] Add cell dispatches to reference_ functions and improve docs (#889) * Add dispatches, * Ferrite.reference_vertices(::Ferrite.AbstractCell) * Ferrite.reference_edges(::Ferrite.AbstractCell) * Ferrite.reference_faces(::Ferrite.AbstractCell) * Document (devdocs) * reference_vertices, reference_edges, reference_faces, reference_facets * Fix Lagrange interpolation definition in dofhandler docs --- docs/src/devdocs/elements.md | 22 ++++++++---- docs/src/devdocs/reference_cells.md | 15 ++++++++ docs/src/topics/degrees_of_freedom.md | 10 +++--- src/Grid/grid.jl | 49 +++++++++++++++++++-------- test/test_grid_dofhandler_vtk.jl | 5 +++ 5 files changed, 76 insertions(+), 25 deletions(-) diff --git a/docs/src/devdocs/elements.md b/docs/src/devdocs/elements.md index 1fd5a548f6..88c799665a 100644 --- a/docs/src/devdocs/elements.md +++ b/docs/src/devdocs/elements.md @@ -2,24 +2,34 @@ ## Type definitions -Elements or cells are subtypes of `AbstractCell{dim,N,M}`. They are parametrized by -the dimension of their nodes via `dim`, the number of nodes `N` and the number -of faces `M`. +Elements or cells are subtypes of `AbstractCell{<:AbstractRefShape}`. As shown, they are parametrized +by the associated reference element. ### Required methods to implement for all subtypes of `AbstractCell` to define a new element +```@docs +Ferrite.get_node_ids +``` + +### Common utilities and definitions when working with grids internally. + +First we have some topological queries on the element + ```@docs Ferrite.vertices(::Ferrite.AbstractCell) Ferrite.edges(::Ferrite.AbstractCell) -Ferrite.reference_faces(::Ferrite.AbstractRefShape) Ferrite.faces(::Ferrite.AbstractCell) +Ferrite.facets(::Ferrite.AbstractCell) +Ferrite.boundaryfunction(::Type{<:Ferrite.BoundaryIndex}) +Ferrite.reference_vertices(::Ferrite.AbstractCell) +Ferrite.reference_edges(::Ferrite.AbstractCell) +Ferrite.reference_faces(::Ferrite.AbstractCell) ``` -### Common utilities and definitions when working with grids internally. +and some generic utils which are commonly found in finite element codes ```@docs Ferrite.BoundaryIndex -Ferrite.boundaryfunction(::Type{<:Ferrite.BoundaryIndex}) Ferrite.get_coordinate_eltype(::Ferrite.AbstractGrid) Ferrite.get_coordinate_eltype(::Node) Ferrite.toglobal diff --git a/docs/src/devdocs/reference_cells.md b/docs/src/devdocs/reference_cells.md index e48e99264c..e75cf006a0 100644 --- a/docs/src/devdocs/reference_cells.md +++ b/docs/src/devdocs/reference_cells.md @@ -15,3 +15,18 @@ Ferrite.RefTetrahedron Ferrite.RefHexahedron Ferrite.RefPrism ``` + +### Required methods to implement for all subtypes of `AbstractRefShape` to define a new reference shape + +```@docs +Ferrite.reference_vertices(::Type{<:Ferrite.AbstractRefShape}) +Ferrite.reference_edges(::Type{<:Ferrite.AbstractRefShape}) +Ferrite.reference_faces(::Type{<:Ferrite.AbstractRefShape}) +``` + +which automatically defines + + +```@docs +Ferrite.reference_facets(::Type{<:Ferrite.AbstractRefShape}) +``` diff --git a/docs/src/topics/degrees_of_freedom.md b/docs/src/topics/degrees_of_freedom.md index 151834cb09..e044eea9d3 100644 --- a/docs/src/topics/degrees_of_freedom.md +++ b/docs/src/topics/degrees_of_freedom.md @@ -25,8 +25,8 @@ need to specify number of components for the field. Here we add a vector field ` (2 components for a 2D problem) and a scalar field `:p`. ```@example dofs -add!(dh, :u, Lagrange{2,RefTetrahedron,1}()^2) -add!(dh, :p, Lagrange{2,RefTetrahedron,1}()) +add!(dh, :u, Lagrange{RefTriangle, 1}()^2) +add!(dh, :p, Lagrange{RefTriangle, 1}()) # hide ``` @@ -53,12 +53,12 @@ quadratic interpolation, and our `:p` field with a linear approximation. ```@example dofs dh = DofHandler(grid) # hide -add!(dh, :u, Lagrange{2,RefTetrahedron,2}()^2) -add!(dh, :p, Lagrange{2,RefTetrahedron,1}()) +add!(dh, :u, Lagrange{RefTriangle, 2}()^2) +add!(dh, :p, Lagrange{RefTriangle, 1}()) # hide ``` ## Ordering of Dofs ordered in the same order as we add to dofhandler -nodes -> (edges ->) faces -> cells +vertices -> edges -> faces -> volumes diff --git a/src/Grid/grid.jl b/src/Grid/grid.jl index b6d172df7b..a1d2113fab 100644 --- a/src/Grid/grid.jl +++ b/src/Grid/grid.jl @@ -56,6 +56,16 @@ nedges( ::Type{T}) where {T <: AbstractRefShape} = length(reference_edges(T)) nfaces( ::Type{T}) where {T <: AbstractRefShape} = length(reference_faces(T)) nfacets( ::Type{T}) where {T <: AbstractRefShape} = length(reference_facets(T)) + +""" + reference_vertices(::Type{<:AbstractRefShape}) + reference_vertices(::AbstractCell) + +Returns a tuple of integers containing the local node indices corresponding to +the vertices (i.e. corners or endpoints) of the cell. +""" +reference_vertices(::Union{Type{<:AbstractRefShape}, AbstractCell}) + """ Ferrite.vertices(::AbstractCell) @@ -65,6 +75,15 @@ corresponds to the local index into this tuple. """ vertices(::AbstractCell) +""" + reference_edges(::Type{<:AbstractRefShape}) + reference_edges(::AbstractCell) + +Returns a tuple of 2-tuples containing the ordered local node indices +(corresponding to the vertices) that define an edge. +""" +reference_edges(::Union{Type{<:AbstractRefShape}, AbstractCell}) + """ Ferrite.edges(::AbstractCell) @@ -77,17 +96,13 @@ Note that the vertices are sufficient to define an edge uniquely. edges(::AbstractCell) """ - reference_faces(::AbstractRefShape) - -Returns a tuple of n-tuples containing the ordered local node indices corresponding to -the vertices that define an *oriented face*. + reference_faces(::Type{<:AbstractRefShape}) + reference_faces(::AbstractCell) -An *oriented face* is a face with the first node having the local index and the other -nodes spanning such that the normal to the face is pointing outwards. - -Note that the vertices are sufficient to define a face uniquely. +Returns a tuple of n-tuples containing the ordered local node indices +(corresponding to the vertices) that define a face. """ -reference_faces(::AbstractRefShape) +reference_faces(::Union{Type{<:AbstractRefShape}, AbstractCell}) """ Ferrite.faces(::AbstractCell) @@ -120,11 +135,12 @@ facets(::AbstractCell) """ Ferrite.reference_facets(::Type{<:AbstractRefShape}) + Ferrite.reference_facets(::AbstractCell) -Returns a tuple of n-tuples containing the ordered local node indices corresponding to -the vertices that define an oriented facet. +Returns a tuple of n-tuples containing the ordered local node indices +(corresponding to the vertices) that define a facet. -See also [`reference_vertices`](@ref), [`reference_edges`](@ref), and [`reference_faces`](@ref) +See also [`reference_vertices`](@ref), [`reference_edges`](@ref), and [`reference_faces`](@ref). """ reference_facets(::Type{<:AbstractRefShape}) @@ -132,9 +148,14 @@ reference_facets(::Type{<:AbstractRefShape}) @inline reference_facets(refshape::Type{<:AbstractRefShape{2}}) = reference_edges(refshape) @inline reference_facets(refshape::Type{<:AbstractRefShape{3}}) = reference_faces(refshape) +@inline reference_faces(::AbstractCell{refshape}) where refshape = reference_faces(refshape) +@inline reference_edges(::AbstractCell{refshape}) where refshape = reference_edges(refshape) +@inline reference_vertices(::AbstractCell{refshape}) where refshape = reference_vertices(refshape) +@inline reference_facets(::AbstractCell{refshape}) where refshape = reference_facets(refshape) + """ - geometric_interpolation(::AbstractCell) - geometric_interpolation(::Type{<:AbstractCell}) + geometric_interpolation(::AbstractCell)::ScalarInterpolation + geometric_interpolation(::Type{<:AbstractCell})::ScalarInterpolation Each `AbstractCell` type has a unique geometric interpolation describing its geometry. This function returns that interpolation, which is always a scalar interpolation. diff --git a/test/test_grid_dofhandler_vtk.jl b/test/test_grid_dofhandler_vtk.jl index 7677f67348..a3f9d8803a 100644 --- a/test/test_grid_dofhandler_vtk.jl +++ b/test/test_grid_dofhandler_vtk.jl @@ -96,6 +96,11 @@ end @test minv ≈ 2left @test maxv ≈ 2right + # Consistency check for topological queries + @test Ferrite.reference_vertices(getcells(grid,1)) == Ferrite.reference_vertices(Ferrite.getrefshape(celltype)) + @test Ferrite.reference_edges(getcells(grid,1)) == Ferrite.reference_edges(Ferrite.getrefshape(celltype)) + @test Ferrite.reference_faces(getcells(grid,1)) == Ferrite.reference_faces(Ferrite.getrefshape(celltype)) + @test Ferrite.reference_facets(getcells(grid,1)) == Ferrite.reference_facets(Ferrite.getrefshape(celltype)) end end # of testset From 98e2b674a8c6d740a779c022de31f73051cb353c Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 12 Jul 2024 15:12:27 +0200 Subject: [PATCH 05/50] Update docs on adding fields to dofhandler to 1.0 syntax (#1020) --- docs/src/topics/degrees_of_freedom.md | 30 ++++++--------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/docs/src/topics/degrees_of_freedom.md b/docs/src/topics/degrees_of_freedom.md index e044eea9d3..c803c250ba 100644 --- a/docs/src/topics/degrees_of_freedom.md +++ b/docs/src/topics/degrees_of_freedom.md @@ -20,13 +20,15 @@ dh = DofHandler(grid) ## Fields Before we can distribute the dofs we need to specify fields. A field is simply the unknown -function(s) we are solving for. To add a field we need a name (a `Symbol`) and we also -need to specify number of components for the field. Here we add a vector field `:u` -(2 components for a 2D problem) and a scalar field `:p`. +function(s) we are solving for. To add a field we need a name (a `Symbol`) and the the +interpolation describing the shape functions for the field. Here we add a scalar field `:p`, +interpolated using linear (degree 1) shape functions on a triangle, and a vector field `:u`, +also interpolated with linear shape functions on a triangle, but raised to the power 2 to +indicate that it is a vector field with 2 components (for a 2D problem). ```@example dofs -add!(dh, :u, Lagrange{RefTriangle, 1}()^2) add!(dh, :p, Lagrange{RefTriangle, 1}()) +add!(dh, :u, Lagrange{RefTriangle, 1}()^2) # hide ``` @@ -38,26 +40,6 @@ dofs for the fields we added. close!(dh) ``` -### Specifying interpolation for a field - -In the example above we did not specify which interpolation should be used for our fields -`:u` and `:p`. By default iso-parametric elements will be used meaning that the -interpolation that matches the grid will be used -- for a linear grid a linear -interpolation will be used etc. It is sometimes useful to separate the grid interpolation -from the interpolation that is used to approximate our fields -(e.g. sub- and super-parametric elements). - -We can specify which interpolation that should be used for the approximation when we add -the fields to the dofhandler. For example, here we add our vector field `:u` with a -quadratic interpolation, and our `:p` field with a linear approximation. - -```@example dofs -dh = DofHandler(grid) # hide -add!(dh, :u, Lagrange{RefTriangle, 2}()^2) -add!(dh, :p, Lagrange{RefTriangle, 1}()) -# hide -``` - ## Ordering of Dofs ordered in the same order as we add to dofhandler From 5d95f89b4e7f45c201d89dc84225cbe29813fd28 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Wed, 31 Jul 2024 12:43:04 +0200 Subject: [PATCH 06/50] speed up adding sparsity pattern entries with coupling (#1028) --- src/Dofs/sparsity_pattern.jl | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/Dofs/sparsity_pattern.jl b/src/Dofs/sparsity_pattern.jl index fd51dacfa7..fea892b419 100644 --- a/src/Dofs/sparsity_pattern.jl +++ b/src/Dofs/sparsity_pattern.jl @@ -587,11 +587,9 @@ function _add_constraint_entries!( return sp end -function _add_interface_entry(sp::SparsityPattern, coupling_sdh::Matrix{Bool}, dof_i::Int, dof_j::Int, +function _add_interface_entry(sp::SparsityPattern, cell_field_dofs::Union{Vector{Int}, SubArray}, neighbor_field_dofs::Union{Vector{Int}, SubArray}, i::Int, j::Int, keep_constrained::Bool, ch::Union{ConstraintHandler, Nothing}) - - coupling_sdh[dof_i, dof_j] || return dofi = cell_field_dofs[i] dofj = neighbor_field_dofs[j] # sym && (dofj > dofi && return cnt) @@ -611,6 +609,7 @@ function _add_interface_entries!( # 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] @@ -624,12 +623,23 @@ function _add_interface_entries!( dofrange2 = dof_range(sdh2, neighbor_field) neighbor_dofs = celldofs(sdh_idx == 2 ? ic.a : ic.b) neighbor_field_dofs = @view neighbor_dofs[dofrange2] - # Typical coupling procedure - for (j, dof_j) in pairs(dofrange2), (i, dof_i) in pairs(dofrange1) - # This line to avoid coupling the shared dof in continuous interpolations as cross-element. They're coupled in the local coupling matrix. - (cell_field_dofs[i] ∈ neighbor_dofs || neighbor_field_dofs[j] ∈ cell_dofs) && continue - _add_interface_entry(sp, coupling_sdh, dof_i, dof_j, cell_field_dofs, neighbor_field_dofs, i, j, keep_constrained, ch) - _add_interface_entry(sp, coupling_sdh, dof_j, dof_i, neighbor_field_dofs, cell_field_dofs, j, i, keep_constrained, ch) + + 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 + + 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 From f7f2592bd26e8005a739cfe19169097c0146e269 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 31 Jul 2024 12:55:56 +0200 Subject: [PATCH 07/50] remove ApplyStrategy enums (#1013) ApplyStrategy was deprecated in #489, but kept for backwards compat (having no effect). This PR removes them completely. --- .github/workflows/Check.yml | 2 +- benchmark/benchmarks-boundary-conditions.jl | 6 ++---- src/Dofs/ConstraintHandler.jl | 8 +------- src/Ferrite.jl | 2 +- src/exports.jl | 1 - 5 files changed, 5 insertions(+), 14 deletions(-) diff --git a/.github/workflows/Check.yml b/.github/workflows/Check.yml index bc783ec483..08dc6b83e0 100644 --- a/.github/workflows/Check.yml +++ b/.github/workflows/Check.yml @@ -38,7 +38,7 @@ jobs: run: | using Ferrite, ExplicitImports, Metis, BlockArrays # Check Ferrite - allow_unanalyzable = (ApplyStrategy, ColoringAlgorithm) # baremodules + allow_unanalyzable = (ColoringAlgorithm,) # baremodules check_no_implicit_imports(Ferrite; allow_unanalyzable) check_no_stale_explicit_imports(Ferrite; allow_unanalyzable) check_all_qualified_accesses_via_owners(Ferrite) diff --git a/benchmark/benchmarks-boundary-conditions.jl b/benchmark/benchmarks-boundary-conditions.jl index fe9b96dd8b..6964196f70 100644 --- a/benchmark/benchmarks-boundary-conditions.jl +++ b/benchmark/benchmarks-boundary-conditions.jl @@ -32,12 +32,10 @@ for spatial_dim ∈ [2] # Non-symmetric application M, f = FerriteAssemblyHelper._assemble_mass(dh, cellvalues, false); - DIRICHLET_SUITE["global"]["apply!(M,f,APPLY_TRANSPOSE)"] = @benchmarkable apply!($M, $f, $ch; strategy=$(Ferrite.APPLY_TRANSPOSE)); - DIRICHLET_SUITE["global"]["apply!(M,f,APPLY_INPLACE)"] = @benchmarkable apply!($M, $f, $ch; strategy=$(Ferrite.APPLY_INPLACE)); + DIRICHLET_SUITE["global"]["apply!(M,f)"] = @benchmarkable apply!($M, $f, $ch); # Symmetric application M, f = FerriteAssemblyHelper._assemble_mass(dh, cellvalues, true); - DIRICHLET_SUITE["global"]["apply!(M_sym,f,APPLY_TRANSPOSE)"] = @benchmarkable apply!($M, $f, $ch; strategy=$(Ferrite.APPLY_TRANSPOSE)); - DIRICHLET_SUITE["global"]["apply!(M_sym,f,APPLY_INPLACE)"] = @benchmarkable apply!($M, $f, $ch; strategy=$(Ferrite.APPLY_INPLACE)); + DIRICHLET_SUITE["global"]["apply!(M_sym,f)"] = @benchmarkable apply!($M, $f, $ch); DIRICHLET_SUITE["global"]["apply!(f)"] = @benchmarkable apply!($f, $ch); DIRICHLET_SUITE["global"]["apply_zero!(f)"] = @benchmarkable apply!($f, $ch); diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index aaba4cfd7d..54ce73a56a 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -595,13 +595,7 @@ function apply_zero!(K::Union{SparseMatrixCSC,Symmetric}, f::AbstractVector, ch: apply!(K, f, ch, true) end -# For backwards compatibility, not used anymore -@enumx ApplyStrategy Transpose Inplace -const APPLY_TRANSPOSE = ApplyStrategy.Transpose -const APPLY_INPLACE = ApplyStrategy.Inplace - -function apply!(KK::Union{SparseMatrixCSC,Symmetric}, f::AbstractVector, ch::ConstraintHandler, applyzero::Bool=false; - strategy::ApplyStrategy.T=ApplyStrategy.Transpose) +function apply!(KK::Union{SparseMatrixCSC,Symmetric}, f::AbstractVector, ch::ConstraintHandler, applyzero::Bool=false) @assert isclosed(ch) sym = isa(KK, Symmetric) K = sym ? KK.data : KK diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 0776f2f6be..c543704cd6 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -8,7 +8,7 @@ using Base: using EnumX: EnumX, @enumx using LinearAlgebra: - LinearAlgebra, Symmetric, Transpose, cholesky, det, norm, pinv, tr + LinearAlgebra, Symmetric, cholesky, det, norm, pinv, tr using NearestNeighbors: NearestNeighbors, KDTree, knn using OrderedCollections: diff --git a/src/exports.jl b/src/exports.jl index f2c17a0d11..2c02bad172 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -151,7 +151,6 @@ export apply_assemble!, add!, free_dofs, - ApplyStrategy, # iterators CellCache, From 726227286277a8753ffdcf7ca2627ac60918b636 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Fri, 2 Aug 2024 12:55:26 +0200 Subject: [PATCH 08/50] reduce chunk size of Hessian in landau Example (#1032) --- docs/src/literate-gallery/landau.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/literate-gallery/landau.jl b/docs/src/literate-gallery/landau.jl index 654f813a36..6de0a196e1 100644 --- a/docs/src/literate-gallery/landau.jl +++ b/docs/src/literate-gallery/landau.jl @@ -68,7 +68,7 @@ function ThreadCache(dpc::Int, nodespercell, cvP::CellValues, modelparams, elpot element_coords = zeros(Vec{3, Float64}, nodespercell) potfunc = x -> elpotential(x, cvP, modelparams) gradconf = GradientConfig(potfunc, zeros(dpc), Chunk{12}()) - hessconf = HessianConfig(potfunc, zeros(dpc), Chunk{12}()) + hessconf = HessianConfig(potfunc, zeros(dpc), Chunk{4}()) return ThreadCache(cvP, element_indices, element_dofs, element_gradient, element_hessian, element_coords, potfunc, gradconf, hessconf) end From 7a59146490403c6b8ae7b7cca76bb0020984a8cb Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 2 Aug 2024 14:19:14 +0200 Subject: [PATCH 09/50] Correct function names in devdocs (#1036) Update devdocs to consider #997, and adds the hessian functions from #938 --- docs/src/devdocs/interpolations.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/src/devdocs/interpolations.md b/docs/src/devdocs/interpolations.md index cc85a76046..852333908d 100644 --- a/docs/src/devdocs/interpolations.md +++ b/docs/src/devdocs/interpolations.md @@ -11,13 +11,15 @@ parametrized by the reference element and its characteristic order. Ferrite.getrefdim(::Interpolation) Ferrite.getrefshape(::Interpolation) Ferrite.getorder(::Interpolation) -Ferrite.shape_gradient(::Interpolation, ::Vec, ::Int) -Ferrite.shape_gradient_and_value +Ferrite.reference_shape_gradient(::Interpolation, ::Vec, ::Int) +Ferrite.reference_shape_gradient_and_value(::Interpolation, ::Vec, ::Int) +Ferrite.reference_shape_hessian_gradient_and_value(::Interpolation, ::Vec, ::Int) Ferrite.boundarydof_indices Ferrite.dirichlet_boundarydof_indices Ferrite.reference_shape_values! -Ferrite.shape_gradients! -Ferrite.shape_gradients_and_values! +Ferrite.reference_shape_gradients! +Ferrite.reference_shape_gradients_and_values! +Ferrite.reference_shape_hessians_gradients_and_values! ``` ### Required methods to implement for all subtypes of `Interpolation` to define a new finite element @@ -25,7 +27,7 @@ Ferrite.shape_gradients_and_values! Depending on the dimension of the reference element the following functions have to be implemented ```@docs -Ferrite.shape_value(::Interpolation, ::Vec, ::Int) +Ferrite.reference_shape_value(::Interpolation, ::Vec, ::Int) Ferrite.vertexdof_indices(::Interpolation) Ferrite.dirichlet_vertexdof_indices(::Interpolation) Ferrite.facedof_indices(::Interpolation) From c29f95342ca79a9eee21eec19b1c7c4b4bb5135f Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 3 Aug 2024 14:56:31 +0200 Subject: [PATCH 10/50] Fix notebook links in tutorials (#1038) --- .../quasi_incompressible_hyperelasticity.jl | 6 +++--- docs/src/literate-gallery/topology_optimization.jl | 2 +- docs/src/literate-howto/postprocessing.jl | 2 +- docs/src/literate-howto/threaded_assembly.jl | 2 +- docs/src/literate-tutorials/computational_homogenization.jl | 2 +- docs/src/literate-tutorials/dg_heat_equation.jl | 2 +- docs/src/literate-tutorials/heat_equation.jl | 2 +- docs/src/literate-tutorials/hyperelasticity.jl | 2 +- docs/src/literate-tutorials/incompressible_elasticity.jl | 2 +- docs/src/literate-tutorials/plasticity.jl | 2 +- docs/src/literate-tutorials/reactive_surface.jl | 2 +- docs/src/literate-tutorials/stokes-flow.jl | 2 +- docs/src/literate-tutorials/transient_heat_equation.jl | 2 +- 13 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl index 96287579a0..c0091d2f74 100644 --- a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl +++ b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl @@ -4,13 +4,13 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`quasi_incompressible_hyperelasticity.ipynb`](@__NBVIEWER_ROOT_URL__/examples/quasi_incompressible_hyperelasticity.ipynb) +#md # [`quasi_incompressible_hyperelasticity.ipynb`](@__NBVIEWER_ROOT_URL__/gallery/quasi_incompressible_hyperelasticity.ipynb) #- # ## Introduction # # In this example we study quasi- or nearly-incompressible hyperelasticity using the stable Taylor-Hood approximation. In spirit, this example is the nonlinear analogue of -# [`incompressible_elasticity`](@__NBVIEWER_ROOT_URL__/examples/incompressible_elasticity.ipynb) and the incompressible analogue of -# [`hyperelasticity`](@__NBVIEWER_ROOT_URL__/examples/hyperelasticity.ipynb). Much of the code therefore follows from the above two examples. +# [`incompressible_elasticity`](@__NBVIEWER_ROOT_URL__/tutorials/incompressible_elasticity.ipynb) and the incompressible analogue of +# [`hyperelasticity`](@__NBVIEWER_ROOT_URL__/tutorials/hyperelasticity.ipynb). Much of the code therefore follows from the above two examples. # The problem is formulated in the undeformed or reference configuration with the displacement $\mathbf{u}$ and pressure $p$ being the unknown fields. We now briefly outline # the formulation. Consider the standard hyperelasticity problem # diff --git a/docs/src/literate-gallery/topology_optimization.jl b/docs/src/literate-gallery/topology_optimization.jl index a03917019f..01afcd8cad 100644 --- a/docs/src/literate-gallery/topology_optimization.jl +++ b/docs/src/literate-gallery/topology_optimization.jl @@ -9,7 +9,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`topology_optimization.ipynb`](@__NBVIEWER_ROOT_URL__/examples/topology_optimization.ipynb). +#md # [`topology_optimization.ipynb`](@__NBVIEWER_ROOT_URL__/gallery/topology_optimization.ipynb). #- # # ## Introduction diff --git a/docs/src/literate-howto/postprocessing.jl b/docs/src/literate-howto/postprocessing.jl index eadc433994..bb146d7a65 100644 --- a/docs/src/literate-howto/postprocessing.jl +++ b/docs/src/literate-howto/postprocessing.jl @@ -8,7 +8,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`postprocessing.ipynb`](@__NBVIEWER_ROOT_URL__/examples/postprocessing.ipynb). +#md # [`postprocessing.ipynb`](@__NBVIEWER_ROOT_URL__/howto/postprocessing.ipynb). #- # # ## Introduction diff --git a/docs/src/literate-howto/threaded_assembly.jl b/docs/src/literate-howto/threaded_assembly.jl index 3b814a380e..64dceafc61 100644 --- a/docs/src/literate-howto/threaded_assembly.jl +++ b/docs/src/literate-howto/threaded_assembly.jl @@ -3,7 +3,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`threaded_assembly.ipynb`](@__NBVIEWER_ROOT_URL__/examples/threaded_assembly.ipynb). +#md # [`threaded_assembly.ipynb`](@__NBVIEWER_ROOT_URL__/howto/threaded_assembly.ipynb). #- # # ## Example of a colored grid diff --git a/docs/src/literate-tutorials/computational_homogenization.jl b/docs/src/literate-tutorials/computational_homogenization.jl index 945c02fe06..7027ea9695 100644 --- a/docs/src/literate-tutorials/computational_homogenization.jl +++ b/docs/src/literate-tutorials/computational_homogenization.jl @@ -9,7 +9,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`computational_homogenization.ipynb`](@__NBVIEWER_ROOT_URL__/examples/computational_homogenization.ipynb). +#md # [`computational_homogenization.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/computational_homogenization.ipynb). #- # # ## Introduction diff --git a/docs/src/literate-tutorials/dg_heat_equation.jl b/docs/src/literate-tutorials/dg_heat_equation.jl index c63916f03e..ff85e11ec7 100644 --- a/docs/src/literate-tutorials/dg_heat_equation.jl +++ b/docs/src/literate-tutorials/dg_heat_equation.jl @@ -9,7 +9,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`dg_heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/examples/dg_heat_equation.ipynb). +#md # [`dg_heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/dg_heat_equation.ipynb). #- # # This example was developed diff --git a/docs/src/literate-tutorials/heat_equation.jl b/docs/src/literate-tutorials/heat_equation.jl index 639472162e..b1937108c2 100644 --- a/docs/src/literate-tutorials/heat_equation.jl +++ b/docs/src/literate-tutorials/heat_equation.jl @@ -8,7 +8,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/examples/heat_equation.ipynb). +#md # [`heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/heat_equation.ipynb). #- # # ## Introduction diff --git a/docs/src/literate-tutorials/hyperelasticity.jl b/docs/src/literate-tutorials/hyperelasticity.jl index 9195a7374e..5bcf81b322 100644 --- a/docs/src/literate-tutorials/hyperelasticity.jl +++ b/docs/src/literate-tutorials/hyperelasticity.jl @@ -11,7 +11,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`hyperelasticity.ipynb`](@__NBVIEWER_ROOT_URL__/examples/hyperelasticity.ipynb). +#md # [`hyperelasticity.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/hyperelasticity.ipynb). #- # ## Introduction # diff --git a/docs/src/literate-tutorials/incompressible_elasticity.jl b/docs/src/literate-tutorials/incompressible_elasticity.jl index 5060ff86bd..865971f7cd 100644 --- a/docs/src/literate-tutorials/incompressible_elasticity.jl +++ b/docs/src/literate-tutorials/incompressible_elasticity.jl @@ -3,7 +3,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`incompressible_elasticity.ipynb`](@__NBVIEWER_ROOT_URL__/examples/incompressible_elasticity.ipynb). +#md # [`incompressible_elasticity.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/incompressible_elasticity.ipynb). #- # # ## Introduction diff --git a/docs/src/literate-tutorials/plasticity.jl b/docs/src/literate-tutorials/plasticity.jl index acce3ecfe2..b3db2dabf6 100644 --- a/docs/src/literate-tutorials/plasticity.jl +++ b/docs/src/literate-tutorials/plasticity.jl @@ -9,7 +9,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`plasticity.ipynb`](@__NBVIEWER_ROOT_URL__/examples/plasticity.ipynb). +#md # [`plasticity.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/plasticity.ipynb). #- # # ## Introduction diff --git a/docs/src/literate-tutorials/reactive_surface.jl b/docs/src/literate-tutorials/reactive_surface.jl index 781a3d7751..3f5eebbef0 100644 --- a/docs/src/literate-tutorials/reactive_surface.jl +++ b/docs/src/literate-tutorials/reactive_surface.jl @@ -15,7 +15,7 @@ nothing #hide #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`reactive_surface.ipynb`](@__NBVIEWER_ROOT_URL__/examples/reactive_surface.ipynb). +#md # [`reactive_surface.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/reactive_surface.ipynb). #- # # ## Introduction diff --git a/docs/src/literate-tutorials/stokes-flow.jl b/docs/src/literate-tutorials/stokes-flow.jl index da3bf26dde..888f5c8e45 100644 --- a/docs/src/literate-tutorials/stokes-flow.jl +++ b/docs/src/literate-tutorials/stokes-flow.jl @@ -4,7 +4,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`stokes-flow.ipynb`](@__NBVIEWER_ROOT_URL__/examples/stokes-flow.ipynb). +#md # [`stokes-flow.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/stokes-flow.ipynb). #- # # ![](stokes-flow.png) diff --git a/docs/src/literate-tutorials/transient_heat_equation.jl b/docs/src/literate-tutorials/transient_heat_equation.jl index 30042f25c3..757b7658f8 100644 --- a/docs/src/literate-tutorials/transient_heat_equation.jl +++ b/docs/src/literate-tutorials/transient_heat_equation.jl @@ -10,7 +10,7 @@ #- #md # !!! tip #md # This example is also available as a Jupyter notebook: -#md # [`transient_heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/examples/transient_heat_equation.ipynb). +#md # [`transient_heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/transient_heat_equation.ipynb). #- # # ## Introduction From 81b987ae21e190facb5ffea2cd4c73da4bd7688e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 4 Aug 2024 16:52:44 +0200 Subject: [PATCH 11/50] Rename VTKFile to VTKGridFile (#1014) Avoids confusing with WriteVTK.VTKFile --- CHANGELOG.md | 6 +- docs/src/literate-gallery/helmholtz.jl | 2 +- docs/src/literate-gallery/landau.jl | 2 +- .../literate-gallery/topology_optimization.jl | 4 +- docs/src/literate-howto/postprocessing.jl | 2 +- docs/src/literate-howto/threaded_assembly.jl | 2 +- .../computational_homogenization.jl | 2 +- .../literate-tutorials/dg_heat_equation.jl | 2 +- docs/src/literate-tutorials/heat_equation.jl | 2 +- .../src/literate-tutorials/hyperelasticity.jl | 2 +- .../incompressible_elasticity.jl | 2 +- docs/src/literate-tutorials/linear_shell.jl | 2 +- docs/src/literate-tutorials/plasticity.jl | 2 +- docs/src/literate-tutorials/stokes-flow.jl | 2 +- docs/src/reference/export.md | 2 +- docs/src/topics/export.md | 6 +- src/Export/VTK.jl | 64 +++++++++---------- src/deprecations.jl | 7 +- src/exports.jl | 2 +- test/test_constraints.jl | 2 +- test/test_grid_dofhandler_vtk.jl | 10 +-- test/test_l2_projection.jl | 2 +- test/test_mixeddofhandler.jl | 4 +- test/test_vtk_export.jl | 18 +++--- 24 files changed, 78 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ecacc0ac0..600a333c4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -219,7 +219,7 @@ more discussion). - vtk_point_data(vtk, nodal_data, "my node data") - vtk_point_data(vtk, proj, projected_data, "my projected data") - vtk_cell_data(vtk, proj, projected_data, "my projected data") - + VTKFile(name, dh) do vtk + + VTKGridFile(name, dh) do vtk + write_solution(vtk, dh, a) + write_node_data(vtk, nodal_data, "my node data") + write_projection(vtk, proj, projected_data, "my projected data") @@ -427,8 +427,8 @@ more discussion). omitting type piracy, ([#835][github-835]) implemented `isequal` and `hash` for `BoundaryIndex` datatypes. - **VTK export**: Ferrite no longer extends methods from `WriteVTK.jl`, instead the new types - `VTKFile` and `VTKFileCollection` should be used instead. New methods exists for writing to - a `VTKFile`, e.g. `write_solution`, `write_cell_data`, `write_node_data`, and `write_projection`. + `VTKGridFile` and `VTKFileCollection` should be used instead. New methods exists for writing to + a `VTKGridFile`, e.g. `write_solution`, `write_cell_data`, `write_node_data`, and `write_projection`. See [#692][github-692]. - **Definitions**: Previously, `face` and `edge` referred to codimension 1 relative reference shape. diff --git a/docs/src/literate-gallery/helmholtz.jl b/docs/src/literate-gallery/helmholtz.jl index 59668d1a56..bd48a17a59 100644 --- a/docs/src/literate-gallery/helmholtz.jl +++ b/docs/src/literate-gallery/helmholtz.jl @@ -161,7 +161,7 @@ K, f = doassemble(cellvalues, facetvalues, K, dh); apply!(K, f, dbcs) u = Symmetric(K) \ f; -vtk = VTKFile("helmholtz", dh) +vtk = VTKGridFile("helmholtz", dh) write_solution(vtk, dh, u) close(vtk) using Test #src diff --git a/docs/src/literate-gallery/landau.jl b/docs/src/literate-gallery/landau.jl index 6de0a196e1..cb0919e420 100644 --- a/docs/src/literate-gallery/landau.jl +++ b/docs/src/literate-gallery/landau.jl @@ -114,7 +114,7 @@ end # utility to quickly save a model function save_landau(path, model, dofs=model.dofs) - VTKFile(path, model.dofhandler) do vtk + VTKGridFile(path, model.dofhandler) do vtk write_solution(vtk, model.dofhandler, dofs) end end diff --git a/docs/src/literate-gallery/topology_optimization.jl b/docs/src/literate-gallery/topology_optimization.jl index 01afcd8cad..58889f1c86 100644 --- a/docs/src/literate-gallery/topology_optimization.jl +++ b/docs/src/literate-gallery/topology_optimization.jl @@ -504,7 +504,7 @@ function topopt(ra,ρ,n,filename; output=:false) i = @sprintf("%3.3i", it) filename_it = string(filename, "_", i) - VTKFile(filename_it, grid) do vtk + VTKGridFile(filename_it, grid) do vtk write_cell_data(vtk, χ, "density") end end @@ -512,7 +512,7 @@ function topopt(ra,ρ,n,filename; output=:false) ## export converged results if(!output) - VTKFile(filename, grid) do vtk + VTKGridFile(filename, grid) do vtk write_cell_data(vtk, χ, "density") end end diff --git a/docs/src/literate-howto/postprocessing.jl b/docs/src/literate-howto/postprocessing.jl index bb146d7a65..671af15366 100644 --- a/docs/src/literate-howto/postprocessing.jl +++ b/docs/src/literate-howto/postprocessing.jl @@ -89,7 +89,7 @@ q_projected = project(projector, q_gp, qr); # To visualize the heat flux, we export the projected field `q_projected` # to a VTK-file, which can be viewed in e.g. [ParaView](https://www.paraview.org/). # The result is also visualized in *Figure 1*. -VTKFile("heat_equation_flux", grid) do vtk +VTKGridFile("heat_equation_flux", grid) do vtk write_projection(vtk, projector, q_projected, "q") end; diff --git a/docs/src/literate-howto/threaded_assembly.jl b/docs/src/literate-howto/threaded_assembly.jl index 64dceafc61..f916938672 100644 --- a/docs/src/literate-howto/threaded_assembly.jl +++ b/docs/src/literate-howto/threaded_assembly.jl @@ -25,7 +25,7 @@ function create_example_2d_grid() grid = generate_grid(Quadrilateral, (10, 10), Vec{2}((0.0, 0.0)), Vec{2}((10.0, 10.0))) colors_workstream = create_coloring(grid; alg=ColoringAlgorithm.WorkStream) colors_greedy = create_coloring(grid; alg=ColoringAlgorithm.Greedy) - VTKFile("colored", grid) do vtk + VTKGridFile("colored", grid) do vtk Ferrite.write_cell_colors(vtk, grid, colors_workstream, "workstream-coloring") Ferrite.write_cell_colors(vtk, grid, colors_greedy, "greedy-coloring") end diff --git a/docs/src/literate-tutorials/computational_homogenization.jl b/docs/src/literate-tutorials/computational_homogenization.jl index 7027ea9695..cba4b5e66b 100644 --- a/docs/src/literate-tutorials/computational_homogenization.jl +++ b/docs/src/literate-tutorials/computational_homogenization.jl @@ -519,7 +519,7 @@ round.(ev; digits=-8) uM = zeros(ndofs(dh)) -VTKFile("homogenization", dh) do vtk +VTKGridFile("homogenization", dh) do vtk for i in 1:3 ## Compute macroscopic solution apply_analytical!(uM, dh, :u, x -> εᴹ[i] ⋅ x) diff --git a/docs/src/literate-tutorials/dg_heat_equation.jl b/docs/src/literate-tutorials/dg_heat_equation.jl index ff85e11ec7..e9a0b6c305 100644 --- a/docs/src/literate-tutorials/dg_heat_equation.jl +++ b/docs/src/literate-tutorials/dg_heat_equation.jl @@ -334,7 +334,7 @@ K, f = assemble_global(cellvalues, facetvalues, interfacevalues, K, dh, order, d apply!(K, f, ch) u = K \ f; -VTKFile("dg_heat_equation", dh) do vtk +VTKGridFile("dg_heat_equation", dh) do vtk write_solution(vtk, dh, u) end; diff --git a/docs/src/literate-tutorials/heat_equation.jl b/docs/src/literate-tutorials/heat_equation.jl index b1937108c2..e98fe27c81 100644 --- a/docs/src/literate-tutorials/heat_equation.jl +++ b/docs/src/literate-tutorials/heat_equation.jl @@ -213,7 +213,7 @@ u = K \ f; # ### Exporting to VTK # To visualize the result we export the grid and our field `u` # to a VTK-file, which can be viewed in e.g. [ParaView](https://www.paraview.org/). -VTKFile("heat_equation", dh) do vtk +VTKGridFile("heat_equation", dh) do vtk write_solution(vtk, dh, u) end diff --git a/docs/src/literate-tutorials/hyperelasticity.jl b/docs/src/literate-tutorials/hyperelasticity.jl index 5bcf81b322..701ec051e3 100644 --- a/docs/src/literate-tutorials/hyperelasticity.jl +++ b/docs/src/literate-tutorials/hyperelasticity.jl @@ -408,7 +408,7 @@ function solve() ## Save the solution @timeit "export" begin - VTKFile("hyperelasticity", dh) do vtk + VTKGridFile("hyperelasticity", dh) do vtk write_solution(vtk, dh, u) end end diff --git a/docs/src/literate-tutorials/incompressible_elasticity.jl b/docs/src/literate-tutorials/incompressible_elasticity.jl index 865971f7cd..33d64023c7 100644 --- a/docs/src/literate-tutorials/incompressible_elasticity.jl +++ b/docs/src/literate-tutorials/incompressible_elasticity.jl @@ -271,7 +271,7 @@ function solve(ν, interpolation_u, interpolation_p) filename = "cook_" * (interpolation_u == Lagrange{RefTriangle, 1}()^2 ? "linear" : "quadratic") * "_linear" - VTKFile(filename, grid) do vtk + VTKGridFile(filename, grid) do vtk write_solution(vtk, dh, u) for i in 1:3, j in 1:3 σij = [x[i, j] for x in σ] diff --git a/docs/src/literate-tutorials/linear_shell.jl b/docs/src/literate-tutorials/linear_shell.jl index b6236b9bb1..6ba45400ba 100644 --- a/docs/src/literate-tutorials/linear_shell.jl +++ b/docs/src/literate-tutorials/linear_shell.jl @@ -113,7 +113,7 @@ a = K\f # Output results. #+ -VTKFile("linear_shell", dh) do vtk +VTKGridFile("linear_shell", dh) do vtk write_solution(vtk, dh, a) end diff --git a/docs/src/literate-tutorials/plasticity.jl b/docs/src/literate-tutorials/plasticity.jl index b3db2dabf6..b8240f32df 100644 --- a/docs/src/literate-tutorials/plasticity.jl +++ b/docs/src/literate-tutorials/plasticity.jl @@ -344,7 +344,7 @@ function solve() mises_values[el] /= length(cell_states) # average von Mises stress κ_values[el] /= length(cell_states) # average drag stress end - VTKFile("plasticity", dh) do vtk + VTKGridFile("plasticity", dh) do vtk write_solution(vtk, dh, u) # displacement field write_cell_data(vtk, mises_values, "von Mises [Pa]") write_cell_data(vtk, κ_values, "Drag stress [Pa]") diff --git a/docs/src/literate-tutorials/stokes-flow.jl b/docs/src/literate-tutorials/stokes-flow.jl index 888f5c8e45..ac161f89bd 100644 --- a/docs/src/literate-tutorials/stokes-flow.jl +++ b/docs/src/literate-tutorials/stokes-flow.jl @@ -514,7 +514,7 @@ function main() u = K \ f apply!(u, ch) ## Export the solution - VTKFile("stokes-flow", grid) do vtk + VTKGridFile("stokes-flow", grid) do vtk write_solution(vtk, dh, u) end diff --git a/docs/src/reference/export.md b/docs/src/reference/export.md index 6b409d7865..f96d858e91 100644 --- a/docs/src/reference/export.md +++ b/docs/src/reference/export.md @@ -24,7 +24,7 @@ PointLocation ## VTK Export ```@docs -VTKFile +VTKGridFile VTKFileCollection addstep! write_solution diff --git a/docs/src/topics/export.md b/docs/src/topics/export.md index e279d172c5..dccc57c807 100644 --- a/docs/src/topics/export.md +++ b/docs/src/topics/export.md @@ -16,7 +16,7 @@ the exporting. The following structure can be used to write various output to a vtk-file: ```@example export -VTKFile("my_solution", grid) do vtk +VTKGridFile("my_solution", grid) do vtk write_solution(vtk, dh, u) end; ``` @@ -33,7 +33,7 @@ where `write_solution` is just one example of the following functions that can b Instead of using the `do`-block, it is also possible to do ```@example export -vtk = VTKFile("my_solution", grid) +vtk = VTKGridFile("my_solution", grid) write_solution(vtk, dh, u) # etc. close(vtk); @@ -44,7 +44,7 @@ The data written by `write_solution`, `write_cell_data`, `write_node_data`, and For simulations with multiple time steps, typically one `VTK` (`.vtu`) file is written for each time step. In order to connect the actual time with each of these files, a `VTKFileCollection` can be used, which will write one paraview datafile (`.pvd`) -file and one `VTKFile` (`.vtu`) for each time step. +file and one `VTKGridFile` (`.vtu`) for each time step. ```@example export pvd = VTKFileCollection("my_results", grid) diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index 0be00d2ead..ea1a06d2c5 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -1,9 +1,9 @@ """ - VTKFile(filename::AbstractString, grid::AbstractGrid; kwargs...) - VTKFile(filename::AbstractString, dh::DofHandler; kwargs...) + VTKGridFile(filename::AbstractString, grid::AbstractGrid; kwargs...) + VTKGridFile(filename::AbstractString, dh::DofHandler; kwargs...) -Create a `VTKFile` that contains an unstructured VTK grid. +Create a `VTKGridFile` that contains an unstructured VTK grid. The keyword arguments are forwarded to `WriteVTK.vtk_grid`, see [Data Formatting Options](https://juliavtk.github.io/WriteVTK.jl/stable/grids/syntax/#Data-formatting-options) @@ -17,28 +17,28 @@ This file handler can be used to to write data with * [`Ferrite.write_nodeset`](@ref) * [`Ferrite.write_constraints`](@ref) -It is necessary to call `close(::VTKFile)` to save the data after writing +It is necessary to call `close(::VTKGridFile)` to save the data after writing to the file handler. Using the supported `do`-block does this automatically: ```julia -VTKFile(filename, grid) do vtk +VTKGridFile(filename, grid) do vtk write_solution(vtk, dh, u) write_cell_data(vtk, celldata) end ``` """ -struct VTKFile{VTK<:WriteVTK.VTKFile} +struct VTKGridFile{VTK<:WriteVTK.DatasetFile} vtk::VTK end -function VTKFile(filename::String, dh::DofHandler; kwargs...) - return VTKFile(filename, get_grid(dh); kwargs...) +function VTKGridFile(filename::String, dh::DofHandler; kwargs...) + return VTKGridFile(filename, get_grid(dh); kwargs...) end -function VTKFile(filename::String, grid::AbstractGrid; kwargs...) +function VTKGridFile(filename::String, grid::AbstractGrid; kwargs...) vtk = create_vtk_grid(filename, grid; kwargs...) - return VTKFile(vtk) + return VTKGridFile(vtk) end # Makes it possible to use the `do`-block syntax -function VTKFile(f::Function, args...; kwargs...) - vtk = VTKFile(args...; kwargs...) +function VTKGridFile(f::Function, args...; kwargs...) + vtk = VTKGridFile(args...; kwargs...) try f(vtk) finally @@ -46,12 +46,12 @@ function VTKFile(f::Function, args...; kwargs...) end end -Base.close(vtk::VTKFile) = WriteVTK.vtk_save(vtk.vtk) +Base.close(vtk::VTKGridFile) = WriteVTK.vtk_save(vtk.vtk) -function Base.show(io::IO, ::MIME"text/plain", vtk::VTKFile) +function Base.show(io::IO, ::MIME"text/plain", vtk::VTKGridFile) open_str = isopen(vtk.vtk) ? "open" : "closed" filename = vtk.vtk.path - print(io, "VTKFile for the $open_str file \"$(filename)\".") + print(io, "VTKGridFile for the $open_str file \"$(filename)\".") end """ @@ -81,7 +81,7 @@ Base.close(pvd::VTKFileCollection) = WriteVTK.vtk_save(pvd.pvd) """ addstep!(f::Function, pvd::VTKFileCollection, t::Real, [grid_or_dh]; kwargs...) -Add a step at time `t` by writing a `VTKFile` to `pvd`. +Add a step at time `t` by writing a `VTKGridFile` to `pvd`. The keyword arguments are forwarded to `WriteVTK.vtk_grid`. If required, a new grid can be used by supplying the grid or dofhandler as the last argument. Should be used in a do-block, e.g. @@ -100,14 +100,14 @@ close(pvd) """ function addstep!(f::Function, pvd::VTKFileCollection, t, grid=pvd.grid_or_dh; kwargs...) pvd.step += 1 - VTKFile(string(pvd.name, "_", pvd.step), grid; kwargs...) do vtk + VTKGridFile(string(pvd.name, "_", pvd.step), grid; kwargs...) do vtk f(vtk) pvd.pvd[t] = vtk.vtk # Add to collection end end """ - addstep!(pvd::VTKFileCollection, vtk::VTKFile, t) + addstep!(pvd::VTKFileCollection, vtk::VTKGridFile, t) As an alternative to using the `addstep!(pvd, t) do` block, it is also possible to add a specific `vtk` at time `t` to `pvd`. @@ -117,7 +117,7 @@ filename = "myoutput" pvd = VTKFileCollection(filename, grid) for (n, t) in pairs(timevector) # Calculate, e.g., the solution `u` and the stress `σeff` - vtk = VTKFile(string(filename, "_", n), dh) + vtk = VTKGridFile(string(filename, "_", n), dh) write_cell_data(vtk, σeff, "Effective stress") write_solution(vtk, dh, u) addstep!(pvd, vtk, t) @@ -125,7 +125,7 @@ end close(pvd) ``` """ -function addstep!(pvd::VTKFileCollection, vtk::VTKFile, t) +function addstep!(pvd::VTKFileCollection, vtk::VTKGridFile, t) pvd.step += 1 pvd.pvd[t] = vtk.vtk end @@ -236,7 +236,7 @@ function component_names(::Type{S}) where S end """ - write_solution(vtk::VTKFile, dh::AbstractDofHandler, u::Vector, suffix="") + write_solution(vtk::VTKGridFile, dh::AbstractDofHandler, u::Vector, suffix="") Save the values at the nodes in the degree of freedom vector `u` to `vtk`. Each field in `dh` will be saved separately, and `suffix` can be used to append @@ -247,7 +247,7 @@ degree of freedom in `dh`, see [`write_node_data`](@ref write_node_data) for det Use `write_node_data` directly when exporting values that are already sorted by the nodes in the grid. """ -function write_solution(vtk::VTKFile, dh::AbstractDofHandler, u::Vector, suffix="") +function write_solution(vtk::VTKGridFile, dh::AbstractDofHandler, u::Vector, suffix="") fieldnames = getfieldnames(dh) # all primary fields for name in fieldnames data = _evaluate_at_grid_nodes(dh, u, name, #=vtk=# Val(true)) @@ -257,11 +257,11 @@ function write_solution(vtk::VTKFile, dh::AbstractDofHandler, u::Vector, suffix= end """ - write_projection(vtk::VTKFile, proj::L2Projector, vals::Vector, name::AbstractString) + write_projection(vtk::VTKGridFile, proj::L2Projector, vals::Vector, name::AbstractString) Project `vals` to the grid nodes with `proj` and save to `vtk`. """ -function write_projection(vtk::VTKFile, proj::L2Projector, vals, name) +function write_projection(vtk::VTKGridFile, proj::L2Projector, vals, name) data = _evaluate_at_grid_nodes(proj, vals, #=vtk=# Val(true))::Matrix @assert size(data, 2) == getnnodes(get_grid(proj.dh)) _vtk_write_node_data(vtk.vtk, data, name; component_names=component_names(eltype(vals))) @@ -269,17 +269,17 @@ function write_projection(vtk::VTKFile, proj::L2Projector, vals, name) end """ - write_cell_data(vtk::VTKFile, celldata::AbstractVector, name::String) + write_cell_data(vtk::VTKGridFile, celldata::AbstractVector, name::String) Write the `celldata` that is ordered by the cells in the grid to the vtk file. """ -function write_cell_data(vtk::VTKFile, celldata, name) +function write_cell_data(vtk::VTKGridFile, celldata, name) WriteVTK.vtk_cell_data(vtk.vtk, celldata, name) end """ - write_node_data(vtk::VTKFile, nodedata::Vector{Real}, name) - write_node_data(vtk::VTKFile, nodedata::Vector{<:AbstractTensor}, name) + write_node_data(vtk::VTKGridFile, nodedata::Vector{Real}, name) + write_node_data(vtk::VTKGridFile, nodedata::Vector{<:AbstractTensor}, name) Write the `nodedata` that is ordered by the nodes in the grid to `vtk`. @@ -289,13 +289,13 @@ Two-dimensional vectors are padded with zeros. When `nodedata` contains second order tensors, the index order, `[11, 22, 33, 23, 13, 12, 32, 31, 21]`, follows the default Voigt order in Tensors.jl. """ -function write_node_data(vtk::VTKFile, nodedata, name) +function write_node_data(vtk::VTKGridFile, nodedata, name) _vtk_write_node_data(vtk.vtk, nodedata, name) end """ - write_nodeset(vtk::VTKFile, grid::AbstractGrid, nodeset::String) + write_nodeset(vtk::VTKGridFile, grid::AbstractGrid, nodeset::String) Write nodal values of 1 for nodes in `nodeset`, and 0 otherwise """ @@ -327,7 +327,7 @@ end write_cellset(vtk, grid::AbstractGrid, cellset::String) = write_cellset(vtk, grid, [cellset]) """ - write_constraints(vtk::VTKFile, ch::ConstraintHandler) + write_constraints(vtk::VTKGridFile, ch::ConstraintHandler) Saves the dirichlet boundary conditions to a vtkfile. Values will have a 1 where bcs are active and 0 otherwise @@ -367,7 +367,7 @@ function write_constraints(vtk, ch::ConstraintHandler) end """ - write_cell_colors(vtk::VTKFile, grid::AbstractGrid, cell_colors, name="coloring") + write_cell_colors(vtk::VTKGridFile, grid::AbstractGrid, cell_colors, name="coloring") Write cell colors (see [`create_coloring`](@ref)) to a VTK file for visualization. diff --git a/src/deprecations.jl b/src/deprecations.jl index faca37eeaa..e35db07746 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -99,7 +99,7 @@ function WriteVTK.vtk_grid(::String, ::Union{AbstractGrid,AbstractDofHandler}; k throw(DeprecationError( "The vtk interface has been updated in Ferrite v1.0. " * "See https://github.com/Ferrite-FEM/Ferrite.jl/pull/692. " * - "Use VTKFile to open a vtk file, and the functions " * + "Use VTKGridFile to open a vtk file, and the functions " * "write_solution, write_cell_data, and write_projection to save data." )) end @@ -433,3 +433,8 @@ export create_sparsity_pattern function create_sparsity_pattern(args...) throw(DeprecationError("create_sparsity_pattern(args...)" => "allocate_matrix(args...; kwargs...)")) end + +export VTKFile +function VTKFile(args...) + throw(DeprecationError("VTKFile(args...)" => "VTKGridFile(args...)")) +end diff --git a/src/exports.jl b/src/exports.jl index 2c02bad172..84054852bb 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -169,7 +169,7 @@ export finish_assemble, # exporting data - VTKFile, + VTKGridFile, write_solution, write_cell_data, write_projection, diff --git a/test/test_constraints.jl b/test/test_constraints.jl index b660c20998..cdc630de61 100644 --- a/test/test_constraints.jl +++ b/test/test_constraints.jl @@ -1553,7 +1553,7 @@ end # testset @test norm(u_dbc) ≈ 3.8249286998373586 @test norm(u_p) ≈ 3.7828270430540893 end - # VTKFile("local_application_azero_$(azero)", grid) do vtk + # VTKGridFile("local_application_azero_$(azero)", grid) do vtk # write_solution(vtk, dh, u_dbc, "_dbc") # write_solution(vtk, dh, u_p, "_p") # end diff --git a/test/test_grid_dofhandler_vtk.jl b/test/test_grid_dofhandler_vtk.jl index a3f9d8803a..7a01bb8317 100644 --- a/test/test_grid_dofhandler_vtk.jl +++ b/test/test_grid_dofhandler_vtk.jl @@ -38,7 +38,7 @@ end addnodeset!(grid, "middle-nodes", x -> norm(x) < radius) gridfilename = "grid-$(repr(celltype))" - VTKFile(gridfilename, grid) do vtk + VTKGridFile(gridfilename, grid) do vtk Ferrite.write_cellset(vtk, grid, "cell-1") Ferrite.write_cellset(vtk, grid, "middle-cells") Ferrite.write_nodeset(vtk, grid, "middle-nodes") @@ -78,7 +78,7 @@ end apply!(u, ch) dofhandlerfilename = "dofhandler-$(repr(celltype))" - VTKFile(dofhandlerfilename, grid) do vtk + VTKGridFile(dofhandlerfilename, grid) do vtk Ferrite.write_constraints(vtk, ch) write_solution(vtk, dofhandler, u) end @@ -124,7 +124,7 @@ close(csio) vector_data = [Vec{3}(ntuple(i->i, 3)) for j=1:8] filename_3d = "test_vtk_3d" - VTKFile(filename_3d, grid) do vtk + VTKGridFile(filename_3d, grid) do vtk write_node_data(vtk, sym_tensor_data, "symmetric tensor") write_node_data(vtk, tensor_data, "tensor") write_node_data(vtk, vector_data, "vector") @@ -139,7 +139,7 @@ close(csio) vector_data = [Vec{2}(ntuple(i->i, 2)) for j=1:4] filename_2d = "test_vtk_2d" - VTKFile(filename_2d, grid) do vtk + VTKGridFile(filename_2d, grid) do vtk write_node_data(vtk, sym_tensor_data, "symmetric tensor") write_node_data(vtk, tensor_data, "tensor") write_node_data(vtk, tensor_data_1D, "tensor_1d") @@ -758,7 +758,7 @@ end addstep!(pvd1, t) do io write_cell_data(io, celldata*n, "celldata") end - vtk = VTKFile(string(fname, "2_", n), grid) + vtk = VTKGridFile(string(fname, "2_", n), grid) write_cell_data(vtk, celldata*n, "celldata") addstep!(pvd2, vtk, t) @test !(isopen(vtk.vtk)) diff --git a/test/test_l2_projection.jl b/test/test_l2_projection.jl index bdb3df58d8..ea72b99980 100644 --- a/test/test_l2_projection.jl +++ b/test/test_l2_projection.jl @@ -406,7 +406,7 @@ function test_export(;subset::Bool) mktempdir() do tmp fname = joinpath(tmp, "projected") - VTKFile(fname, grid) do vtk + VTKGridFile(fname, grid) do vtk write_projection(vtk, p, p_scalar, "p_scalar") write_projection(vtk, p, p_vec, "p_vec") write_projection(vtk, p, p_tens, "p_tens") diff --git a/test/test_mixeddofhandler.jl b/test/test_mixeddofhandler.jl index ac0e8ada1f..264324d4b0 100644 --- a/test/test_mixeddofhandler.jl +++ b/test/test_mixeddofhandler.jl @@ -357,7 +357,7 @@ function test_2_element_heat_eq() gridfilename = "mixed_grid" addcellset!(grid, "cell-1", [1,]) addcellset!(grid, "cell-2", [2,]) - VTKFile(gridfilename, grid) do vtk + VTKGridFile(gridfilename, grid) do vtk Ferrite.write_cellset(vtk, grid, "cell-1") Ferrite.write_cellset(vtk, grid, "cell-2") write_solution(vtk, dh, u) @@ -651,7 +651,7 @@ function test_vtk_export() close!(dh) u = collect(1:ndofs(dh)) filename = "mixed_2d_grid" - VTKFile(filename, dh) do vtk + VTKGridFile(filename, dh) do vtk write_solution(vtk, dh, u) end sha = bytes2hex(open(SHA.sha1, filename*".vtu")) diff --git a/test/test_vtk_export.jl b/test/test_vtk_export.jl index 178fbec285..4d6a8f4612 100644 --- a/test/test_vtk_export.jl +++ b/test/test_vtk_export.jl @@ -1,14 +1,14 @@ -@testset "VTKFile" begin #TODO: Move all vtk tests here - @testset "show(::VTKFile)" begin +@testset "VTKGridFile" begin #TODO: Move all vtk tests here + @testset "show(::VTKGridFile)" begin mktempdir() do tmp grid = generate_grid(Quadrilateral, (2,2)) - vtk = VTKFile(joinpath(tmp, "showfile"), grid) + vtk = VTKGridFile(joinpath(tmp, "showfile"), grid) showstring_open = sprint(show, MIME"text/plain"(), vtk) - @test startswith(showstring_open, "VTKFile for the open file") + @test startswith(showstring_open, "VTKGridFile for the open file") @test contains(showstring_open, "showfile.vtu") close(vtk) showstring_closed = sprint(show, MIME"text/plain"(), vtk) - @test startswith(showstring_closed, "VTKFile for the closed file") + @test startswith(showstring_closed, "VTKGridFile for the closed file") @test contains(showstring_closed, "showfile.vtu") end end @@ -17,7 +17,7 @@ grid = generate_grid(Quadrilateral, (4, 4)) colors = create_coloring(grid) fname = joinpath(tmp, "colors") - VTKFile(fname, grid) do vtk + VTKGridFile(fname, grid) do vtk Ferrite.write_cell_colors(vtk, grid, colors) end @test bytes2hex(open(SHA.sha1, fname*".vtu")) == "b804d0b064121b672d8e35bcff8446eda361cac3" @@ -35,7 +35,7 @@ add!(ch, Dirichlet(:u, getnodeset(grid, "nodeset"), x -> 0.0)) close!(ch) fname = joinpath(tmp, "constraints") - VTKFile(fname, grid) do vtk + VTKGridFile(fname, grid) do vtk Ferrite.write_constraints(vtk, ch) end @test bytes2hex(open(SHA.sha1, fname*".vtu")) == "31b506bd9729b11992f8bcb79a2191eb65d223bf" @@ -50,10 +50,10 @@ addcellset!(grid, "set2", 1:4) manual = joinpath(tmp, "manual") auto = joinpath(tmp, "auto") - VTKFile(manual, grid) do vtk + VTKGridFile(manual, grid) do vtk Ferrite.write_cellset(vtk, grid, keys(Ferrite.getcellsets(grid))) end - VTKFile(auto, grid) do vtk + VTKGridFile(auto, grid) do vtk Ferrite.write_cellset(vtk, grid) end @test bytes2hex(open(SHA.sha1, manual*".vtu")) == bytes2hex(open(SHA.sha1, auto*".vtu")) From 1dad1fe772c0ee59ed3afc498ebc66e0251877e2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 6 Aug 2024 00:32:08 +0200 Subject: [PATCH 12/50] Fix doc warnings and make docs build strict (#1039) --- docs/make.jl | 2 +- docs/src/assets/references.bib | 4 ++-- docs/src/devdocs/FEValues.md | 2 +- docs/src/devdocs/dofhandler.md | 11 ++++++----- docs/src/devdocs/interpolations.md | 1 - docs/src/devdocs/reference_cells.md | 5 +++++ docs/src/literate-gallery/topology_optimization.jl | 2 +- docs/src/literate-tutorials/dg_heat_equation.jl | 2 +- docs/src/reference/grid.md | 2 +- docs/src/topics/assembly.md | 4 ++-- docs/src/topics/boundary_conditions.md | 2 +- docs/src/topics/reference_shapes.md | 2 +- docs/src/topics/sparse_matrix.md | 4 ++-- ext/FerriteMetis.jl | 2 +- src/Dofs/DofHandler.jl | 2 +- src/Dofs/sparsity_pattern.jl | 2 +- src/Ferrite.jl | 1 + src/Grid/coloring.jl | 2 +- src/L2_projection.jl | 2 +- src/assembler.jl | 2 +- src/interpolations.jl | 1 + 21 files changed, 32 insertions(+), 25 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index f6dae950f0..7cdeb1852b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -42,7 +42,7 @@ bibtex_plugin = CitationBibliography( ), sitename = "Ferrite.jl", doctest = false, - warnonly = true, + warnonly = is_ci ? false : [:cross_references], # Local build exception required for Literate's `@__NBVIEWER_ROOT_URL__` draft = liveserver, pages = Any[ "Home" => "index.md", diff --git a/docs/src/assets/references.bib b/docs/src/assets/references.bib index 1b2be190bc..4d03354c50 100644 --- a/docs/src/assets/references.bib +++ b/docs/src/assets/references.bib @@ -80,7 +80,7 @@ @article{SimMie:1992:act pages = {41-104}, year = {1992}, issn = {0045-7825}, -doi = {https://doi.org/10.1016/0045-7825(92)90170-O}, +doi = {10.1016/0045-7825(92)90170-O}, url = {https://www.sciencedirect.com/science/article/pii/004578259290170O}, author = {J.C. Simo and C. Miehe}, } @@ -117,7 +117,7 @@ @article{Mu:2014:IP pages = {432-440}, year = {2014}, issn = {0377-0427}, -doi = {https://doi.org/10.1016/j.cam.2013.06.003}, +doi = {10.1016/j.cam.2013.06.003}, url = {https://www.sciencedirect.com/science/article/pii/S0377042713002999}, author = {Lin Mu and Junping Wang and Yanqiu Wang and Xiu Ye}, keywords = {Discontinuous Galerkin, Finite element, Interior penalty, Second-order elliptic equations, Hybrid mesh}, diff --git a/docs/src/devdocs/FEValues.md b/docs/src/devdocs/FEValues.md index d7854e4058..e329e25c86 100644 --- a/docs/src/devdocs/FEValues.md +++ b/docs/src/devdocs/FEValues.md @@ -39,7 +39,7 @@ Custom FEValues, `fe_v::AbstractValues`, should normally implement the [`reinit! * [`getnquadpoints`](@ref) * [`getnbasefunctions`](@ref) * [`spatial_coordinate`](@ref), requires - * [`geometric_value`](@ref) + * [`geometric_value`](@ref Ferrite.geometric_value) * `getngeobasefunctions` * [`getnquadpoints`](@ref) diff --git a/docs/src/devdocs/dofhandler.md b/docs/src/devdocs/dofhandler.md index 3b4feda17d..fd28e81c71 100644 --- a/docs/src/devdocs/dofhandler.md +++ b/docs/src/devdocs/dofhandler.md @@ -3,9 +3,9 @@ ## Type definitions Dof handlers are subtypes of `AbstractDofhandler{sdim}`, i.e. they are -parametrized by the spatial dimension. Internally a helper struct [`InterpolationInfo`](@ref) is utilized to enforce type stability during -dof distribution, because the interpolations are not available as concrete -types. +parametrized by the spatial dimension. Internally a helper struct +[`InterpolationInfo`](@ref Ferrite.InterpolationInfo) is utilized to enforce type stability +during dof distribution, because the interpolations are not available as concrete types. ```@docs Ferrite.InterpolationInfo @@ -16,12 +16,13 @@ Ferrite.SurfaceOrientationInfo ## Internal API -The main entry point for dof distribution is [`__close!`](@ref). +The main entry point for dof distribution is [`__close!`](@ref Ferrite.__close!). ```@docs Ferrite.__close! Ferrite.get_grid -Ferrite.find_field(dh::DofHandler, field_name::Symbol) +Ferrite.find_field +Ferrite._find_field Ferrite._close_subdofhandler! Ferrite._distribute_dofs_for_cell! Ferrite.permute_and_push! diff --git a/docs/src/devdocs/interpolations.md b/docs/src/devdocs/interpolations.md index 852333908d..22c45ac3ef 100644 --- a/docs/src/devdocs/interpolations.md +++ b/docs/src/devdocs/interpolations.md @@ -8,7 +8,6 @@ parametrized by the reference element and its characteristic order. ### Fallback methods applicable for all subtypes of `Interpolation` ```@docs -Ferrite.getrefdim(::Interpolation) Ferrite.getrefshape(::Interpolation) Ferrite.getorder(::Interpolation) Ferrite.reference_shape_gradient(::Interpolation, ::Vec, ::Int) diff --git a/docs/src/devdocs/reference_cells.md b/docs/src/devdocs/reference_cells.md index e75cf006a0..cda4889eac 100644 --- a/docs/src/devdocs/reference_cells.md +++ b/docs/src/devdocs/reference_cells.md @@ -30,3 +30,8 @@ which automatically defines ```@docs Ferrite.reference_facets(::Type{<:Ferrite.AbstractRefShape}) ``` + +### Applicable methods to `AbstractRefShape`s +```@docs +Ferrite.getrefdim(::Type{<:Ferrite.AbstractRefShape}) +``` diff --git a/docs/src/literate-gallery/topology_optimization.jl b/docs/src/literate-gallery/topology_optimization.jl index 58889f1c86..4445e4a90a 100644 --- a/docs/src/literate-gallery/topology_optimization.jl +++ b/docs/src/literate-gallery/topology_optimization.jl @@ -541,7 +541,7 @@ end #md # ## References #md # ```@bibliography -#md # Pages = ["gallery/topology_optimization.md"] +#md # Pages = ["topology_optimization.md"] #md # Canonical = false #md # ``` diff --git a/docs/src/literate-tutorials/dg_heat_equation.jl b/docs/src/literate-tutorials/dg_heat_equation.jl index e9a0b6c305..8ee2cc7429 100644 --- a/docs/src/literate-tutorials/dg_heat_equation.jl +++ b/docs/src/literate-tutorials/dg_heat_equation.jl @@ -344,7 +344,7 @@ using Test #src #md # ## References #md # ```@bibliography -#md # Pages = ["tutorials/dg_heat_equation.md"] +#md # Pages = ["dg_heat_equation.md"] #md # Canonical = false #md # ``` diff --git a/docs/src/reference/grid.md b/docs/src/reference/grid.md index d601c0c00d..7456e0c527 100644 --- a/docs/src/reference/grid.md +++ b/docs/src/reference/grid.md @@ -35,7 +35,7 @@ getcoordinates! geometric_interpolation(::Ferrite.AbstractCell) get_node_coordinate Ferrite.getspatialdim(::Ferrite.AbstractGrid) -Ferrite.getrefdim +Ferrite.getrefdim(::Ferrite.AbstractCell) ``` ### Topology diff --git a/docs/src/topics/assembly.md b/docs/src/topics/assembly.md index 0a7d1fb8ce..868761af8e 100644 --- a/docs/src/topics/assembly.md +++ b/docs/src/topics/assembly.md @@ -24,8 +24,8 @@ into the sparse matrix `K` directly. Therefore we will instead use an matrix and the global force vector. It is also often convenient to create the sparse matrix just once, and reuse the allocated matrix. This is useful for e.g. iterative solvers or time dependent problems where the sparse matrix -structure, or [Sparsity Pattern](@ref) will stay the same in every iteration/ -time step. +structure, or [Sparsity Pattern](@ref "Sparsity pattern and sparse matrices") +will stay the same in every iteration/time step. ## `Assembler` diff --git a/docs/src/topics/boundary_conditions.md b/docs/src/topics/boundary_conditions.md index 638562e63a..242bb44217 100644 --- a/docs/src/topics/boundary_conditions.md +++ b/docs/src/topics/boundary_conditions.md @@ -9,7 +9,7 @@ conditions, and they need to be handled in different ways. Below we discuss how the most common ones, Dirichlet and Neumann boundary conditions, and how to do it `Ferrite`. While boundary conditions can be applied directly to nodes, vertices, edges, or faces, -they are most commonly applied to [facets](@ref Reference shapes). Each facet is described +they are most commonly applied to [facets](@ref "Reference shapes"). Each facet is described by a [`FacetIndex`](@ref). When adding boundary conditions to points instead, vertices are preferred over nodes. diff --git a/docs/src/topics/reference_shapes.md b/docs/src/topics/reference_shapes.md index 85f16e700f..53d7ca7ce2 100644 --- a/docs/src/topics/reference_shapes.md +++ b/docs/src/topics/reference_shapes.md @@ -86,4 +86,4 @@ Ferrite.reference_facets(RefQuadrilateral) The functions `reference_vertices`, `reference_edges`, `reference_faces`, and `reference_facets` are not public and only shown here to explain the numbering concept. The specific ordering may also change, and is therefore only documented in the - [Developer documentation](../devdocs/reference_cells/). + [Developer documentation](@ref). diff --git a/docs/src/topics/sparse_matrix.md b/docs/src/topics/sparse_matrix.md index 923315ed4a..6926bc9f34 100644 --- a/docs/src/topics/sparse_matrix.md +++ b/docs/src/topics/sparse_matrix.md @@ -33,10 +33,10 @@ precision (`Float64`, 8 bytes) it would require 8 TB of memory. If instead the s 99.9973% (which is the case when solving the heat equation on a three dimensional hypercube with linear Lagrange interpolation) this would be reduced to 216 MB. -[1]: Structurally nonzero means that there is a possibility of a nonzero value even though +[^1]: Structurally nonzero means that there is a possibility of a nonzero value even though the computed value might become zero in the end for various reasons. -[2]: At least for most practical problems using low order interpolations. +[^2]: At least for most practical problems using low order interpolations. !!! details "Sparsity pattern example" diff --git a/ext/FerriteMetis.jl b/ext/FerriteMetis.jl index 053d9a725d..4726737eeb 100644 --- a/ext/FerriteMetis.jl +++ b/ext/FerriteMetis.jl @@ -21,7 +21,7 @@ end Fill-reducing permutation order from [Metis.jl](https://github.com/JuliaSparse/Metis.jl). Since computing the permutation involves constructing the structural couplings between all -DoFs the field/component coupling can be provided; see [`create_sparsity_pattern`](@ref) for +DoFs the field/component coupling can be provided; see [`allocate_matrix`](@ref) for details. """ function DofOrder.Ext{Metis}(; diff --git a/src/Dofs/DofHandler.jl b/src/Dofs/DofHandler.jl index 0fa8cad0bc..4db8f9eb65 100644 --- a/src/Dofs/DofHandler.jl +++ b/src/Dofs/DofHandler.jl @@ -796,7 +796,7 @@ field was found and the 2nd entry is the index of the field within the `SubDofHa Always finds the 1st occurrence of a field within `DofHandler`. See also: [`find_field(sdh::SubDofHandler, field_name::Symbol)`](@ref), -[`_find_field(sdh::SubDofHandler, field_name::Symbol)`](@ref). +[`Ferrite._find_field(sdh::SubDofHandler, field_name::Symbol)`](@ref). """ function find_field(dh::DofHandler, field_name::Symbol) for (sdh_idx, sdh) in pairs(dh.subdofhandlers) diff --git a/src/Dofs/sparsity_pattern.jl b/src/Dofs/sparsity_pattern.jl index fea892b419..def97b55e4 100644 --- a/src/Dofs/sparsity_pattern.jl +++ b/src/Dofs/sparsity_pattern.jl @@ -391,7 +391,7 @@ allocate_matrix(MatrixType, sp) ```` Refer to [`allocate_matrix`](@ref allocate_matrix(::Type{<:Any}, ::SparsityPattern)) for -supported matrix types, and to [`create_sparsity_pattern`](@ref) for details about supported +supported matrix types, and to [`init_sparsity_pattern`](@ref) for details about supported arguments `args` and keyword arguments `kwargs`. !!! note diff --git a/src/Ferrite.jl b/src/Ferrite.jl index c543704cd6..781de4d6f0 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -58,6 +58,7 @@ struct RefPyramid <: AbstractRefShape{3} end Get the dimension of the reference shape """ +getrefdim(::Type{<:AbstractRefShape}) # To get correct doc filtering getrefdim(::Type{<:AbstractRefShape{rdim}}) where rdim = rdim abstract type AbstractCell{refshape <: AbstractRefShape} end diff --git a/src/Grid/coloring.jl b/src/Grid/coloring.jl index 7c41700c82..85f9723fe2 100644 --- a/src/Grid/coloring.jl +++ b/src/Grid/coloring.jl @@ -179,7 +179,7 @@ Two different algorithms are available, specified with the `alg` keyword argumen - `alg = ColoringAlgorithm.Greedy`: greedy algorithm that works well for structured quadrilateral grids such as e.g. quadrilateral grids from `generate_grid`. -The resulting colors can be visualized using [`vtk_cell_data_colors`](@ref). +The resulting colors can be visualized using [`Ferrite.write_cell_colors`](@ref). !!! note "Cell to color mapping" In a previous version of Ferrite this function returned a dictionary mapping diff --git a/src/L2_projection.jl b/src/L2_projection.jl index 6c94ca340b..69717fc699 100644 --- a/src/L2_projection.jl +++ b/src/L2_projection.jl @@ -234,7 +234,7 @@ Supported data types to project are `Number`s and `AbstractTensor`s. !!! note The order of the returned data correspond to the order of the `L2Projector`'s internal `DofHandler`. The data can be further analyzed with [`evaluate_at_points`](@ref) and - [`evaluate_at_grid_nodes`](@ref). Use [`write_projected`](@ref) to export the result. + [`evaluate_at_grid_nodes`](@ref). Use [`write_projection`](@ref) to export the result. """ function project(proj::L2Projector, vars::Union{AbstractVector, AbstractDict}) diff --git a/src/assembler.jl b/src/assembler.jl index f6ad0448eb..b86ffa8899 100644 --- a/src/assembler.jl +++ b/src/assembler.jl @@ -241,7 +241,7 @@ function _missing_sparsity_pattern_error(Krow::Int, Kcol::Int) throw(ErrorException( "You are trying to assemble values in to K[$(Krow), $(Kcol)], but K[$(Krow), " * "$(Kcol)] is missing in the sparsity pattern. Make sure you have called `K = " * - "create_sparsity_pattern(dh)` or `K = create_sparsity_pattern(dh, ch)` if you " * + "allocate_matrix(dh)` or `K = allocate_matrix(dh, ch)` if you " * "have affine constraints. This error might also happen if you are using " * "`::AssemblerSparsityPattern` in a threaded assembly loop (you need to create an " * "`assembler::AssemblerSparsityPattern` for each task)." diff --git a/src/interpolations.jl b/src/interpolations.jl index a25d3a371b..8ce950b1ee 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -111,6 +111,7 @@ Base.copy(ip::Interpolation) = ip Return the dimension of the reference element for a given interpolation. """ +getrefdim(::Interpolation) # To make doc-filtering work @inline getrefdim(::Interpolation{RefShape}) where RefShape = getrefdim(RefShape) """ From 7f01271dbc9a6a5c1d233bd689819614df4833a1 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 6 Aug 2024 00:35:49 +0200 Subject: [PATCH 13/50] Fix typed functions in Dirichlet, fixes #1006 (#1016) --- src/Dofs/ConstraintHandler.jl | 2 +- test/test_constraints.jl | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index 54ce73a56a..8684e163d3 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -406,7 +406,7 @@ function update!(ch::ConstraintHandler, time::Real=0.0) # If the BC function only accept one argument, i.e. f(x), we create a wrapper # g(x, t) = f(x) that discards the second parameter so that _update! can always call # the function with two arguments internally. - wrapper_f = hasmethod(dbc.f, Tuple{Any,Any}) ? dbc.f : (x, _) -> dbc.f(x) + wrapper_f = hasmethod(dbc.f, Tuple{get_coordinate_type(get_grid(ch.dh)), typeof(time)}) ? dbc.f : (x, _) -> dbc.f(x) # Function barrier _update!(ch.inhomogeneities, wrapper_f, dbc.facets, dbc.field_name, dbc.local_facet_dofs, dbc.local_facet_dofs_offset, dbc.components, ch.dh, ch.bcvalues[i], ch.dofmapping, ch.dofcoefficients, time) diff --git a/test/test_constraints.jl b/test/test_constraints.jl index cdc630de61..1600d87288 100644 --- a/test/test_constraints.jl +++ b/test/test_constraints.jl @@ -86,8 +86,9 @@ end add!(dh, :p, Lagrange{RefTriangle,1}()) close!(dh) ch = ConstraintHandler(dh) - dbc1 = Dirichlet(:u, getnodeset(grid, "nodeset"), (x,t) -> x, [1, 2]) - dbc2 = Dirichlet(:p, getnodeset(grid, "nodeset"), (x,t) -> 0, 1) + dbc1 = Dirichlet(:u, getnodeset(grid, "nodeset"), (x, t) -> x, [1, 2]) + # Add type-spec to function, test https://github.com/Ferrite-FEM/Ferrite.jl/issues/1006 + dbc2 = Dirichlet(:p, getnodeset(grid, "nodeset"), (x::Vec, t::Real) -> 0, 1) add!(ch, dbc1) add!(ch, dbc2) close!(ch) From e1f662cb8958bff3362d9a4435e273445d57babc Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Tue, 6 Aug 2024 01:09:34 +0200 Subject: [PATCH 14/50] Fix Quadrature rule docs and syms (#1007) --- docs/src/assets/references.bib | 47 ++++++ src/Quadrature/gaussquad_tet_table.jl | 89 +++++++++++- src/Quadrature/gaussquad_tri_table.jl | 33 +++-- src/Quadrature/quadrature.jl | 72 ++++++---- .../test_simple_scalar_convergence.jl | 1 + test/test_quadrules.jl | 134 +++++++++++++----- 6 files changed, 301 insertions(+), 75 deletions(-) diff --git a/docs/src/assets/references.bib b/docs/src/assets/references.bib index 4d03354c50..c240c43833 100644 --- a/docs/src/assets/references.bib +++ b/docs/src/assets/references.bib @@ -123,6 +123,53 @@ @article{Mu:2014:IP keywords = {Discontinuous Galerkin, Finite element, Interior penalty, Second-order elliptic equations, Hybrid mesh}, abstract = {This paper provides a theoretical foundation for interior penalty discontinuous Galerkin methods for second-order elliptic equations on very general polygonal or polyhedral meshes. The mesh can be composed of any polygons or polyhedra that satisfy certain shape regularity conditions characterized in a recent paper by two of the authors, Wang and Ye (2012) [11]. The usual H1-conforming finite element methods on such meshes are either very complicated or impossible to implement in practical computation. The interior penalty discontinuous Galerkin method provides a simple and effective alternative approach which is efficient and robust. Results with such general meshes have important application in computational sciences.} } +@article{Jin:1984:sgq, + title = {Symmetric gaussian quadrature formulae for tetrahedronal regions}, + journal = {Computer Methods in Applied Mechanics and Engineering}, + volume = {43}, + number = {3}, + pages = {349-353}, + year = {1984}, + issn = {0045-7825}, + doi = {10.1016/0045-7825(84)90072-0}, + url = {https://www.sciencedirect.com/science/article/pii/0045782584900720}, + author = {Jinyun Yu}, + abstract = {Quadrature formulae of degrees 2 to 6 are presented for the numerical integration of a function over tetrahedronal regions. The formulae presented are of Gaussian type and fully symmetric with respect to the four vertices of the tetrahedron.} +} +@article{Dun:1985:hde, + title={High degree efficient symmetrical Gaussian quadrature rules for the triangle}, + author={Dunavant, D.A.}, + journal={International journal for numerical methods in engineering}, + volume={21}, + number={6}, + pages={1129--1148}, + year={1985}, + publisher={Wiley Online Library}, + doi={10.1002/nme.1620210612}, + url={https://onlinelibrary.wiley.com/doi/abs/10.1002/nme.1620210612}, +} +@article{WitVin:2015:isq, + title={On the identification of symmetric quadrature rules for finite element methods}, + author={Witherden, Freddie D and Vincent, Peter E}, + journal={Computers \& Mathematics with Applications}, + volume={69}, + number={10}, + pages={1232--1241}, + year={2015}, + publisher={Elsevier} +} +@article{Keast:1986:mtq, + title={Moderate-degree tetrahedral quadrature formulas}, + author={Keast, Patrick}, + journal={Computer methods in applied mechanics and engineering}, + volume={55}, + number={3}, + pages={339--348}, + year={1986}, + publisher={Elsevier}, + doi={10.1016/0045-7825(86)90059-9}, + url={https://www.sciencedirect.com/science/article/pii/0045782586900599}, +} @article{RanTur:1992:snq, title={Simple nonconforming quadrilateral Stokes element}, author={Rannacher, Rolf and Turek, Stefan}, diff --git a/src/Quadrature/gaussquad_tet_table.jl b/src/Quadrature/gaussquad_tet_table.jl index 4eb7b73ffe..0694caa432 100644 --- a/src/Quadrature/gaussquad_tet_table.jl +++ b/src/Quadrature/gaussquad_tet_table.jl @@ -1,6 +1,5 @@ -# Patrick Keast, MODERATE-DEGREE TETRAHEDRAL QUADRATURE FORMULAS -# http://mech.fsv.cvut.cz/oofem/resources/doc/oofemrefman/gaussintegrationrule_8C_source.html -function _get_gauss_tetdata(n::Int) +# Yu, Jinyun. Symmetric Gaussian Quadrature Formulae for Tetrahedronal Regions. 1984. CMAME. +function _get_jinyun_tet_quadrature_data(n::Int) if n == 1 a = 1. / 4. w = 1. / 6. @@ -24,6 +23,20 @@ function _get_gauss_tetdata(n::Int) b2 a2 b2 w2 b2 b2 a2 w2 b2 b2 b2 w2] + elseif 4 ≤ n ≤ 6 + throw(ArgumentError("Jinyun's Gauss quadrature rule (RefTetrahedron) is not implemented for orders 4 and 6")) + else + throw(ArgumentError("unsupported quadrature order $n for Jinyun's Gauss quadrature rule (RefTetrahedron). Supported orders are 1 to 3.")) + end + return xw +end + +# Patrick Keast. Moderate-Degree Tetrahedral Quadrature Formulas. 1986. CMAME. +# Minimal points +function _get_keast_a_tet_quadrature_data(n::Int) + if 1 ≤ n ≤ 3 + # The rules of Jinyin and Keast are identical for order 1 to 3, as stated in the Keast paper. + xw = _get_jinyun_tet_quadrature_data(n) elseif n == 4 a1 = 1. / 4.; w1 = -74. / 5625.; @@ -47,8 +60,76 @@ function _get_gauss_tetdata(n::Int) b3 a3 a3 w3 b3 a3 b3 w3 b3 b3 a3 w3] + elseif n == 5 + w1 = 0.602678571428571597e-2 + a1 = 1. / 3. + b1 = 0. + + w2 = 0.302836780970891856e-1 + a2 = 1. / 4. + + w3 = 0.116452490860289742e-1 + a3 = 1. / 11. + b3 = 8. / 11. + + w4 = 0.109491415613864534e-1 + a4 = 0.665501535736642813e-1 + b4 = 0.433449846426335728e-0 + + xw = [a1 a1 a1 w1 + a1 a1 b1 w1 + a1 b1 a1 w1 + b1 a1 a1 w1 + a2 a2 a2 w2 + a3 a3 a3 w3 + a3 a3 b3 w3 + a3 b3 a3 w3 + b3 a3 a3 w3 + a4 a4 b4 w4 + a4 b4 a4 w4 + a4 b4 b4 w4 + b4 a4 a4 w4 + b4 a4 b4 w4 + b4 b4 a4 w4] + elseif 6 ≤ n ≤ 8 + throw(ArgumentError("Keast's Gauss quadrature rule (RefTetrahedron) not implement for order 6 to 8")) + else + throw(ArgumentError("unsupported order $n for Keast's Gauss quadrature rule (RefTetrahedron). Supported orders are 1 to 5.")) + end + return xw +end + +# Positive points +function _get_keast_b_tet_quadrature_data(n::Int) + if n == 4 + w1 = 0.317460317460317450e-2 + a1 = 1. / 2. + b1 = 0.0 + + w2 = 0.147649707904967828e-1 + a2 = 0.100526765225204467e-0 + b2 = 0.698419704324386603e-0 + + w3 = 0.221397911142651221e-1 + a3 = 0.314372873493192195e-0 + b3 = 0.568813795204234229e-1 + + xw = [a1 a1 b1 w1 + a1 b1 a1 w1 + a1 b1 b1 w1 + b1 a1 a1 w1 + b1 a1 b1 w1 + b1 b1 a1 w1 + a2 a2 a2 w2 + a2 a2 b2 w2 + a2 b2 a2 w2 + b2 a2 a2 w2 + a3 a3 a3 w3 + a3 a3 b3 w3 + a3 b3 a3 w3 + b3 a3 a3 w3] else - throw(ArgumentError("unsupported order for tetraheder gauss-legendre integration")) + xw = _get_keast_a_tet_quadrature_data(n) end return xw end diff --git a/src/Quadrature/gaussquad_tri_table.jl b/src/Quadrature/gaussquad_tri_table.jl index a3ee7ad7d2..7be355ce45 100644 --- a/src/Quadrature/gaussquad_tri_table.jl +++ b/src/Quadrature/gaussquad_tri_table.jl @@ -3,27 +3,21 @@ # rules for the triangle. Int. J. Numer. Meth. Engng., 21: 1129–1148. doi: # 10.1002/nme.1620210612 # -# Quadrature rules for orders 9 to 20 have been obtained using the -# basix.make_quadrature(basix.CellType.triangle, n) calls of the -# FEniCS / basix python package -# -# see -# https://docs.fenicsproject.org/basix/main/python/_autosummary/basix.html?highlight=quadraturetype#basix.make_quadrature -function _get_gauss_tridata(n::Int) +function _get_dunavant_gauss_tridata(n::Int) if (n == 1) xw=[0.33333333333333 0.33333333333333 1.00000000000000 / 2.0]; elseif (n == 2) xw=[0.16666666666667 0.16666666666667 0.33333333333333 / 2.0 0.16666666666667 0.66666666666667 0.33333333333333 / 2.0 0.66666666666667 0.16666666666667 0.33333333333333 / 2.0]; - elseif (n == 3) + elseif (n == 3) xw=[0.33333333333333 0.33333333333333 -0.56250000000000 / 2.0 0.20000000000000 0.20000000000000 0.52083333333333 / 2.0 0.20000000000000 0.60000000000000 0.52083333333333 / 2.0 0.60000000000000 0.20000000000000 0.52083333333333 / 2.0]; - elseif (n == 4) + elseif (n == 4) xw=[0.44594849091597 0.44594849091597 0.22338158967801 / 2.0 0.44594849091597 0.10810301816807 0.22338158967801 / 2.0 0.10810301816807 0.44594849091597 0.22338158967801 / 2.0 @@ -82,7 +76,24 @@ function _get_gauss_tridata(n::Int) 0.72849239295540 0.26311282963464 0.02723031417443 / 2.0 0.26311282963464 0.00839477740996 0.02723031417443 / 2.0 0.00839477740996 0.72849239295540 0.02723031417443 / 2.0]; - elseif n == 9 + else + throw(ArgumentError("unsupported order for Dunavant's triangle integration")) + end + return xw +end + +# TheseqQuadrature rules for orders 9 to 20 have been obtained using the +# basix.make_quadrature(basix.CellType.triangle, n) calls of the +# FEniCS / basix python package, which corresponds to Gauss-Jacobi rules. +# +# see +# https://docs.fenicsproject.org/basix/main/python/_autosummary/basix.html?highlight=quadraturetype#basix.make_quadrature +# +# The original paper for this rule is: +# Jacobi, Carl Gustav Jakob. "Ueber Gauss neue Methode, die Werthe der Integrale +# näherungsweise zu finden." (1826): 301-308. +function _get_gaussjacobi_tridata(n::Int) + if n == 9 xw=[0.4171034443615992 0.4171034443615992 0.0136554632640511 0.1803581162663707 0.1803581162663707 0.0131563152940090 0.2857065024365867 0.2857065024365867 0.0188581185763976 @@ -833,7 +844,7 @@ function _get_gauss_tridata(n::Int) 0.0080665857041666 0.5861680189969418 0.0018206702056404 0.0001234468122874 0.8135558255123531 0.0003443363125209] else - throw(ArgumentError("unsupported order for triangle gauss-legendre integration")) + throw(ArgumentError("unsupported order for triangle Gauss-Jacobi integration")) end return xw end diff --git a/src/Quadrature/quadrature.jl b/src/Quadrature/quadrature.jl index 1291d90275..bb32acdb2d 100644 --- a/src/Quadrature/quadrature.jl +++ b/src/Quadrature/quadrature.jl @@ -16,8 +16,15 @@ using Base.Cartesian: @nloops, @ntuple, @nexprs Create a `QuadratureRule` used for integration on the refshape `shape` (of type [`AbstractRefShape`](@ref)). `order` is the order of the quadrature rule. -`quad_rule_type` is an optional argument determining the type of quadrature rule. -Currently the :polyquad, `:legendre` and `:lobatto` rules are implemented, depending on the reference element. +`quad_rule_type` is an optional argument determining the type of quadrature rule, +currently the `:legendre` and `:lobatto` rules are implemented for hypercubes. +For triangles up to order 8 the default rule is the one by `:dunavant` (see [Dun:1985:hde](@cite)) and for +tetrahedra the default rule is `keast_minimal` (see [Keast:1986:mtq](@cite)). Wedges and pyramids default +to `:polyquad` (see [WitVin:2015:isq](@cite)). +Furthermore we have implemented +* `:gaussjacobi` for triangles (order 9-15) +* `:keast_minimal` (see [Keast:1986:mtq](@cite)) for tetrahedra (order 1-5), containing negative weights +* `:keast_positive` (see [Keast:1986:mtq](@cite)) for tetrahedra (order 1-5), containing only positive weights A `QuadratureRule` is used to approximate an integral on a domain by a weighted sum of function values at specific points: @@ -53,12 +60,17 @@ struct QuadratureRule{shape, WeightStorageType, PointStorageType} end end -# Fill in defaults (Float64, :legendre) +@inline _default_quadrature_rule(::Type{<:RefHypercube}) = :legendre +@inline _default_quadrature_rule(::Union{Type{RefPrism}, Type{RefPyramid}}) = :polyquad +@inline _default_quadrature_rule(::Type{RefTriangle}) = :dunavant +@inline _default_quadrature_rule(::Type{RefTetrahedron}) = :keast_minimal + +# Fill in defaults with T=Float64 function QuadratureRule{shape}(order::Int) where {shape <: AbstractRefShape} return QuadratureRule{shape}(Float64, order) end function QuadratureRule{shape}(::Type{T}, order::Int) where {shape <: AbstractRefShape, T} - quad_type = (shape === RefPrism || shape === RefPyramid) ? (:polyquad) : (:legendre) + quad_type = _default_quadrature_rule(shape) return QuadratureRule{shape}(T, quad_type, order) end function QuadratureRule{shape}(quad_type::Symbol, order::Int) where {shape <: AbstractRefShape} @@ -96,10 +108,16 @@ end for dim in 2:3 @eval begin function QuadratureRule{RefSimplex{$dim}}(::Type{T}, quad_type::Symbol, order::Int) where T - if $dim == 2 && quad_type === :legendre - data = _get_gauss_tridata(order) - elseif $dim == 3 && quad_type === :legendre - data = _get_gauss_tetdata(order) + if $dim == 2 && quad_type === :dunavant + data = _get_dunavant_gauss_tridata(order) + elseif $dim == 2 && quad_type === :gaussjacobi + data = _get_gaussjacobi_tridata(order) + elseif $dim == 3 && quad_type === :jinyun + data = _get_jinyun_tet_quadrature_data(order) + elseif $dim == 3 && quad_type === :keast_minimal + data = _get_keast_a_tet_quadrature_data(order) + elseif $dim == 3 && quad_type === :keast_positive + data = _get_keast_b_tet_quadrature_data(order) else throw(ArgumentError("unsupported quadrature rule")) end @@ -109,7 +127,7 @@ for dim in 2:3 for p in 1:size(data, 1) points[p] = Vec{$dim,T}(@ntuple $dim i -> data[p, i]) end - weights = data[:, $dim + 1] + weights = T.(data[:, $dim + 1]) QuadratureRule{RefSimplex{$dim}}(weights, points) end end @@ -128,7 +146,7 @@ function QuadratureRule{RefPrism}(::Type{T}, quad_type::Symbol, order::Int) wher for p in 1:size(data, 1) points[p] = Vec{3,T}(@ntuple 3 i -> data[p, i]) end - weights = data[:, 4] + weights = T.(data[:, 4]) QuadratureRule{RefPrism}(weights, points) end @@ -145,7 +163,7 @@ function QuadratureRule{RefPyramid}(::Type{T}, quad_type::Symbol, order::Int) wh for p in 1:size(data, 1) points[p] = Vec{3,T}(@ntuple 3 i -> data[p, i]) end - weights = data[:, 4] + weights = T.(data[:, 4]) QuadratureRule{RefPyramid}(weights, points) end @@ -154,14 +172,15 @@ end ###################### """ - FacetQuadratureRule{shape}([::Type{T}, [quad_rule_type::Symbol,] order::Int) + FacetQuadratureRule{shape}([::Type{T},] [quad_rule_type::Symbol,] order::Int) FacetQuadratureRule{shape}(face_rules::NTuple{<:Any, <:QuadratureRule{shape}}) FacetQuadratureRule{shape}(face_rules::AbstractVector{<:QuadratureRule{shape}}) Create a `FacetQuadratureRule` used for integration of the faces of the refshape `shape` (of type [`AbstractRefShape`](@ref)). `order` is the order of the quadrature rule. -`quad_rule_type` is an optional argument determining the type of quadrature rule. -Currently the :polyquad, `:legendre` and `:lobatto` rules are implemented, depending on the reference element. +If no symbol is provided, the default `quad_rule_type` for each facet's reference shape is used (see [QuadratureRule](@ref)). +For non-default `quad_rule_type`s on cells with mixed facet types (e.g. `RefPrism` and `RefPyramid`), the +`face_rules` must be provided explicitly. `FacetQuadratureRule` is used as one of the components to create [`FacetValues`](@ref). """ @@ -179,50 +198,53 @@ function FacetQuadratureRule(face_rules::Union{NTuple{<:Any, QRType}, AbstractVe return FacetQuadratureRule{shape}(face_rules) end -# Fill in defaults (Float64, :legendre) +# Fill in defaults T=Float64 function FacetQuadratureRule{shape}(order::Int) where {shape <: AbstractRefShape} return FacetQuadratureRule{shape}(Float64, order) end -function FacetQuadratureRule{shape}(::Type{T}, order::Int) where {shape <: AbstractRefShape, T} - return FacetQuadratureRule{shape}(T, :legendre, order) -end function FacetQuadratureRule{shape}(quad_type::Symbol, order::Int) where {shape <: AbstractRefShape} return FacetQuadratureRule{shape}(Float64, quad_type, order) end # For RefShapes with equal face-shapes: generate quad rule for the face shape # and expand to each face -function FacetQuadratureRule{RefLine}(::Type{T}, ::Symbol, ::Int) where T +function FacetQuadratureRule{RefLine}(::Type{T}, ::Int) where T w, p = T[1], Vec{0, T}[Vec{0, T}(())] return create_facet_quad_rule(RefLine, w, p) end +FacetQuadratureRule{RefQuadrilateral}(::Type{T}, order::Int) where T = FacetQuadratureRule{RefQuadrilateral}(T,_default_quadrature_rule(RefLine),order) function FacetQuadratureRule{RefQuadrilateral}(::Type{T}, quad_type::Symbol, order::Int) where T qr = QuadratureRule{RefLine}(T, quad_type, order) return create_facet_quad_rule(RefQuadrilateral, qr.weights, qr.points) end +FacetQuadratureRule{RefHexahedron}(::Type{T}, order::Int) where T = FacetQuadratureRule{RefHexahedron}(T,_default_quadrature_rule(RefQuadrilateral),order) function FacetQuadratureRule{RefHexahedron}(::Type{T}, quad_type::Symbol, order::Int) where T qr = QuadratureRule{RefQuadrilateral}(T, quad_type, order) return create_facet_quad_rule(RefHexahedron, qr.weights, qr.points) end +FacetQuadratureRule{RefTriangle}(::Type{T}, order::Int) where T = FacetQuadratureRule{RefTriangle}(T,_default_quadrature_rule(RefLine),order) function FacetQuadratureRule{RefTriangle}(::Type{T}, quad_type::Symbol, order::Int) where T qr = QuadratureRule{RefLine}(T, quad_type, order) # Interval scaled and shifted in facet_to_element_transformation from (-1,1) to (0,1) -> half the length -> half quadrature weights return create_facet_quad_rule(RefTriangle, qr.weights/2, qr.points) end +FacetQuadratureRule{RefTetrahedron}(::Type{T}, order::Int) where T = FacetQuadratureRule{RefTetrahedron}(T,_default_quadrature_rule(RefTriangle),order) function FacetQuadratureRule{RefTetrahedron}(::Type{T}, quad_type::Symbol, order::Int) where T qr = QuadratureRule{RefTriangle}(T, quad_type, order) return create_facet_quad_rule(RefTetrahedron, qr.weights, qr.points) end -function FacetQuadratureRule{RefPrism}(::Type{T}, quad_type::Symbol, order::Int) where T - qr_quad = QuadratureRule{RefQuadrilateral}(T, quad_type, order) - qr_tri = QuadratureRule{RefTriangle}(T, quad_type, order) +FacetQuadratureRule{RefPrism}(::Type{T}, order::Int) where T = _FacetQuadratureRulePrism(T,(_default_quadrature_rule(RefTriangle), _default_quadrature_rule(RefQuadrilateral)),order) +function _FacetQuadratureRulePrism(::Type{T}, quad_types::Tuple{Symbol,Symbol}, order::Int) where T + qr_quad = QuadratureRule{RefQuadrilateral}(T, quad_types[2], order) + qr_tri = QuadratureRule{RefTriangle}(T, quad_types[1], order) # Interval scaled and shifted in facet_to_element_transformation for quadrilateral faces from (-1,1)² to (0,1)² -> quarter the area -> quarter the quadrature weights return create_facet_quad_rule(RefPrism, [2,3,4], qr_quad.weights/4, qr_quad.points, [1,5], qr_tri.weights, qr_tri.points) end -function FacetQuadratureRule{RefPyramid}(::Type{T}, quad_type::Symbol, order::Int) where T - qr_quad = QuadratureRule{RefQuadrilateral}(T, quad_type, order) - qr_tri = QuadratureRule{RefTriangle}(T, quad_type, order) +FacetQuadratureRule{RefPyramid}(::Type{T}, order::Int) where T = _FacetQuadratureRulePyramid(T,(_default_quadrature_rule(RefTriangle), _default_quadrature_rule(RefQuadrilateral)),order) +function _FacetQuadratureRulePyramid(::Type{T}, quad_types::Tuple{Symbol,Symbol}, order::Int) where T + qr_quad = QuadratureRule{RefQuadrilateral}(T, quad_types[2], order) + qr_tri = QuadratureRule{RefTriangle}(T, quad_types[1], order) # Interval scaled and shifted in facet_to_element_transformation for quadrilateral faces from (-1,1)² to (0,1)² -> quarter the area -> quarter the quadrature weights return create_facet_quad_rule(RefPyramid, [1], qr_quad.weights/4, qr_quad.points, [2,3,4,5], qr_tri.weights, qr_tri.points) diff --git a/test/integration/test_simple_scalar_convergence.jl b/test/integration/test_simple_scalar_convergence.jl index 4d56b81b48..4f7543ebc4 100644 --- a/test/integration/test_simple_scalar_convergence.jl +++ b/test/integration/test_simple_scalar_convergence.jl @@ -15,6 +15,7 @@ get_geometry(::Ferrite.Interpolation{RefTetrahedron}) = Tetrahedron get_geometry(::Ferrite.Interpolation{RefPyramid}) = Pyramid get_quadrature_order(::Lagrange{shape, order}) where {shape, order} = max(2*order-1,2) +get_quadrature_order(::Lagrange{RefTriangle, 5}) where {shape, order} = 8 get_quadrature_order(::Lagrange{RefPrism, order}) where order = 2*order # Don't know why get_quadrature_order(::Serendipity{shape, order}) where {shape, order} = max(2*order-1,2) get_quadrature_order(::CrouzeixRaviart{shape, order}) where {shape, order} = max(2*order-1,2) diff --git a/test/test_quadrules.jl b/test/test_quadrules.jl index a5295fcf3f..d6240e58c4 100644 --- a/test/test_quadrules.jl +++ b/test/test_quadrules.jl @@ -14,17 +14,14 @@ using Ferrite: reference_shape_value end # Hypercube - for (dim, shape) = ((1, RefLine), (2, RefQuadrilateral), (3, RefHexahedron)) - for order in (1,2,3,4) - f = (x, p) -> sum([x[i]^p for i in 1:length(x)]) - # Legendre - qr = QuadratureRule{shape}(:legendre, order) - @test integrate(qr, (x) -> f(x, 2*order-1)) < 1e-14 - @test sum(qr.weights) ≈ ref_square_vol(dim) - @test sum(Ferrite.getweights(qr)) ≈ ref_square_vol(dim) - # Lobatto - if order > 1 - qr = QuadratureRule{shape}(:lobatto, order) + @testset "Exactness for integration on hypercube of $rulename" for (rulename, orderrange) in [ + (:legendre, 1:4), + (:lobatto, 2:4), + ] + for (dim, shape) = ((1, RefLine), (2, RefQuadrilateral), (3, RefHexahedron)) + for order in orderrange + f = (x, p) -> sum([x[i]^p for i in 1:length(x)]) + qr = QuadratureRule{shape}(rulename, order) @test integrate(qr, (x) -> f(x, 2*order-1)) < 1e-14 @test sum(qr.weights) ≈ ref_square_vol(dim) @test sum(Ferrite.getweights(qr)) ≈ ref_square_vol(dim) @@ -33,37 +30,58 @@ using Ferrite: reference_shape_value end @test_throws ArgumentError QuadratureRule{RefLine}(:einstein, 2) - # Tetrahedron - g = (x) -> sqrt(sum(x)) - dim = 2 - for order in 1:15 - qr = QuadratureRule{RefTriangle}(:legendre, order) - # http://www.wolframalpha.com/input/?i=integrate+sqrt(x%2By)+from+x+%3D+0+to+1,+y+%3D+0+to+1-x - @test integrate(qr, g) - 0.4 < 0.01 - @test sum(qr.weights) ≈ ref_tet_vol(dim) + # Triangle + # http://www.wolframalpha.com/input/?i=integrate+sqrt(x%2By)+from+x+%3D+0+to+1,+y+%3D+0+to+1-x + @testset "Exactness for integration on triangles of $rulename" for (rulename, orderrange) in [ + (:dunavant, 1:8), + (:gaussjacobi, 9:15), + ] + g = (x) -> sqrt(sum(x)) + dim = 2 + for order in orderrange + qr = QuadratureRule{RefTriangle}(rulename, order) + @test integrate(qr, g) - 0.4 < 0.01 + @test sum(qr.weights) ≈ ref_tet_vol(dim) + end end @test_throws ArgumentError QuadratureRule{RefTriangle}(:einstein, 2) @test_throws ArgumentError QuadratureRule{RefTriangle}(0) - dim = 3 - for order in (1, 2, 3, 4) - qr = QuadratureRule{RefTetrahedron}(:legendre, order) - # Table 1: - # http://www.m-hikari.com/ijma/ijma-2011/ijma-1-4-2011/venkateshIJMA1-4-2011.pdf - @test integrate(qr, g) - 0.14 < 0.01 - @test sum(qr.weights) ≈ ref_tet_vol(dim) + # Tetrahedron + # Table 1: + # http://www.m-hikari.com/ijma/ijma-2011/ijma-1-4-2011/venkateshIJMA1-4-2011.pdf + @testset "Exactness for integration on tetrahedra of $rulename" for (rulename, orderrange) in [ + (:jinyun, 1:3), + (:keast_minimal, 1:5), + (:keast_positive, 1:5) + ] + g = (x) -> sqrt(sum(x)) + dim = 3 + for order in orderrange + qr = QuadratureRule{RefTetrahedron}(rulename, order) + @test integrate(qr, g) - 0.14 < 0.01 + @test sum(qr.weights) ≈ ref_tet_vol(dim) + end end @test_throws ArgumentError QuadratureRule{RefTetrahedron}(:einstein, 2) @test_throws ArgumentError QuadratureRule{RefTetrahedron}(0) - g = (x) -> sqrt(x[1] + x[2])*x[3]^2 - for order in 1:10 - qr = QuadratureRule{RefPrism}(:polyquad, order) - @test integrate(qr, g) - 2/15 < 0.01 - @test sum(qr.weights) ≈ ref_prism_vol() + # Wedge + # ∫ √(x₁ + x₂) x₃² + @testset "Exactness for integration on prisms of $rulename" for (rulename, orderrange) in [ + (:polyquad, 1:10), + ] + g = (x) -> √(x[1] + x[2])*x[3]^2 + for order in 1:10 + qr = QuadratureRule{RefPrism}(:polyquad, order) + @test integrate(qr, g) - 2/15 < 0.01 + @test sum(qr.weights) ≈ ref_prism_vol() + end end + @test_throws ArgumentError QuadratureRule{RefPrism}(:einstein, 2) + @test_throws ArgumentError QuadratureRule{RefPrism}(0) - @testset "Quadrature rules for $ref_cell" for ref_cell in ( + @testset "Generic quadrature rule properties for $ref_cell" for ref_cell in ( Line, Quadrilateral, Triangle, @@ -119,12 +137,14 @@ using Ferrite: reference_shape_value end end - @testset "Type checks for $refshape" begin - qr = QuadratureRule{refshape}(1) + @testset "Type checks for $refshape (T=$T)" for T in (Float32, Float64) + qr = QuadratureRule{refshape}(T, 1) qrw = Ferrite.getweights(qr) qrp = Ferrite.getpoints(qr) @test qrw isa Vector @test qrp isa Vector + @test eltype(qrw) === T + @test eltype(eltype(qrp)) === T sqr = QuadratureRule{refshape}( SVector{length(qrw)}(qrw), SVector{length(qrp)}(qrp) @@ -133,13 +153,17 @@ using Ferrite: reference_shape_value sqrp = Ferrite.getpoints(sqr) @test sqrw isa SVector @test sqrp isa SVector + @test eltype(sqrw) === T + @test eltype(eltype(sqrp)) === T - fqr = FacetQuadratureRule{refshape}(1) + fqr = FacetQuadratureRule{refshape}(T, 1) for f in 1:nfacets(refshape) fqrw = Ferrite.getweights(fqr, f) fqrp = Ferrite.getpoints(fqr, f) @test fqrw isa Vector @test fqrp isa Vector + @test eltype(fqrw) === T + @test eltype(eltype(fqrp)) === T end function sqr_for_facet(fqr, f) @@ -159,6 +183,8 @@ using Ferrite: reference_shape_value sfqrp = Ferrite.getpoints(sfqr, f) @test sfqrw isa SVector @test sfqrp isa SVector + @test eltype(sfqrw) === T + @test eltype(eltype(sfqrp)) === T end sfqr2 = FacetQuadratureRule( @@ -169,6 +195,44 @@ using Ferrite: reference_shape_value sfqrp = Ferrite.getpoints(sfqr2, f) @test sfqrw isa SVector @test sfqrp isa SVector + @test eltype(sfqrw) === T + @test eltype(eltype(sfqrp)) === T + end + end + end + + # Check explicitly if the defaults changed, as this might affect users negatively + @testset "Volume defaults for $refshape" for (refshape, sym) in ( + (RefLine, :legendre), + (RefQuadrilateral, :legendre), + (RefHexahedron, :legendre), + (RefTriangle, :dunavant), + (RefTetrahedron, :keast_minimal), + (RefPrism, :polyquad), + (RefPyramid, :polyquad), + ) + for order in 1:3 + qr = QuadratureRule{refshape}(sym, order) + qr_default = QuadratureRule{refshape}(order) + @test Ferrite.getweights(qr) == Ferrite.getweights(qr_default) + @test Ferrite.getpoints(qr) == Ferrite.getpoints(qr_default) + end + end + @testset "Facet defaults for $refshape" for (refshape, sym) in ( + # (RefLine, :legendre), # There is no choice for the rule on lines, as it only is a point eval + (RefQuadrilateral, :legendre), + (RefHexahedron, :legendre), + (RefTriangle, :legendre), + (RefTetrahedron, :dunavant), + # (RefPrism, ...), # Not implemented yet (see discussion in #1007) + # (RefPyramid, ...), # Not implement yet (see discussion in #1007) + ) + for order in 1:3 + fqr = FacetQuadratureRule{refshape}(sym, order) + fqr_default = FacetQuadratureRule{refshape}(order) + for f in 1:nfacets(refshape) + @test Ferrite.getweights(fqr,f) == Ferrite.getweights(fqr_default,f) + @test Ferrite.getpoints(fqr,f) == Ferrite.getpoints(fqr_default,f) end end end From 064269dfd0ae39676d02e683000f4c4e36e10190 Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Tue, 6 Aug 2024 18:25:45 +0200 Subject: [PATCH 15/50] Fix and Test coverage for PointEval with nonlinear geometries (#820) --- src/FEValues/GeometryMapping.jl | 19 +++- src/FEValues/PointValues.jl | 2 +- src/FEValues/common_values.jl | 23 ++++- src/PointEvalHandler.jl | 135 ++++++++++++++++++---------- test/runtests.jl | 9 +- test/test_pointevaluation.jl | 155 ++++++++++++++++++++++++-------- test/test_utils.jl | 71 ++++++++++----- 7 files changed, 298 insertions(+), 116 deletions(-) diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index 4497c7733a..ad0c98ec81 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -121,11 +121,11 @@ function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(d2Mdξ2) return Tensor{3,dim,promote_type(Tx,TM)} end -@inline function calculate_mapping(::GeometryMapping{0}, q_point, x) +@inline function calculate_mapping(::GeometryMapping{0}, q_point::Int, x::AbstractVector{<:Vec}) return MappingValues(nothing, nothing) end -@inline function calculate_mapping(geo_mapping::GeometryMapping{1}, q_point, x) +@inline function calculate_mapping(geo_mapping::GeometryMapping{1}, q_point::Int, x::AbstractVector{<:Vec}) fecv_J = zero(otimes_returntype(eltype(x), eltype(geo_mapping.dMdξ))) @inbounds for j in 1:getngeobasefunctions(geo_mapping) #fecv_J += x[j] ⊗ geo_mapping.dMdξ[j, q_point] @@ -149,6 +149,21 @@ end calculate_detJ(J::Tensor{2}) = det(J) calculate_detJ(J::SMatrix) = embedding_det(J) +function calculate_jacobian_and_spatial_coordinate(gip::ScalarInterpolation, ξ::Vec{rdim,Tξ}, x::AbstractVector{<:Vec{sdim, Tx}}) where {Tξ, Tx, rdim, sdim} + n_basefuncs = getnbasefunctions(gip) + @boundscheck checkbounds(x, Base.OneTo(n_basefuncs)) + + fecv_J = zero(otimes_returntype(Vec{sdim,Tx}, Vec{rdim,Tξ})) + sx = zero(Vec{sdim, Tx}) + @inbounds for j in 1:n_basefuncs + dMdξ, M = reference_shape_gradient_and_value(gip, ξ, j) + sx += M * x[j] + fecv_J += otimes_helper(x[j], dMdξ) + end + return fecv_J, sx +end + + # Embedded """ diff --git a/src/FEValues/PointValues.jl b/src/FEValues/PointValues.jl index 86cdab248d..8168ff73a6 100644 --- a/src/FEValues/PointValues.jl +++ b/src/FEValues/PointValues.jl @@ -63,7 +63,7 @@ function_symmetric_gradient(pv::PointValues, u::AbstractVector, args...) = # reinit! on PointValues must first update N and dNdξ for the new "quadrature point" # and then call the regular reinit! for the wrapped CellValues to update dNdx -function reinit!(pv::PointValues, x::AbstractVector{<:Vec{D}}, ξ::Vec{D}) where {D} +function reinit!(pv::PointValues, x::AbstractVector{<:Vec{sdim}}, ξ::Vec{rdim}) where {sdim, rdim} # Update the quadrature point location qr_points = getpoints(pv.cv.qr) qr_points[1] = ξ diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index b68cb751d8..f8e744f975 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -329,7 +329,10 @@ Compute the spatial coordinate in a quadrature point. `x` contains the nodal coordinates of the cell. The coordinate is computed, using the geometric interpolation, as -``\\mathbf{x} = \\sum\\limits_{i = 1}^n M_i (\\mathbf{x}) \\mathbf{\\hat{x}}_i`` +``\\mathbf{x} = \\sum\\limits_{i = 1}^n M_i (\\mathbf{\\xi}) \\mathbf{\\hat{x}}_i``. + +where ``\\xi``is the coordinate of the given quadrature point `q_point` of the associated +quadrature rule. """ function spatial_coordinate(fe_v::AbstractValues, q_point::Int, x::AbstractVector{<:Vec}) n_base_funcs = getngeobasefunctions(fe_v) @@ -342,6 +345,24 @@ function spatial_coordinate(fe_v::AbstractValues, q_point::Int, x::AbstractVecto return vec end +""" + spatial_coordinate(ip::ScalarInterpolation, ξ::Vec, x::AbstractVector{<:Vec{sdim, T}}) + +Compute the spatial coordinate in a given quadrature point. `x` contains the nodal coordinates of the cell. + +The coordinate is computed, using the geometric interpolation, as +``\\mathbf{x} = \\sum\\limits_{i = 1}^n M_i (\\mathbf{\\xi}) \\mathbf{\\hat{x}}_i`` +""" +function spatial_coordinate(interpolation::ScalarInterpolation, ξ::Vec, x::AbstractVector{<:Vec}) + n_basefuncs = getnbasefunctions(interpolation) + @boundscheck checkbounds(x, Base.OneTo(n_basefuncs)) + vec = zero(eltype(x)) + @inbounds for j in 1:n_basefuncs + M = reference_shape_value(interpolation, ξ, j) + vec += M * x[j] + end + return vec +end # Utility functions used by GeometryMapping, FunctionValues _copy_or_nothing(x) = copy(x) diff --git a/src/PointEvalHandler.jl b/src/PointEvalHandler.jl index b8ae19ec55..97d57ec150 100644 --- a/src/PointEvalHandler.jl +++ b/src/PointEvalHandler.jl @@ -1,14 +1,15 @@ +Base.@kwdef struct NewtonLineSearchPointFinder{T} + max_iters::Int = 10 + max_line_searches::Int = 5 + residual_tolerance::T = 1e-10 +end + """ PointEvalHandler(grid::Grid, points::AbstractVector{Vec{dim,T}}; kwargs...) where {dim, T} The `PointEvalHandler` can be used for function evaluation in *arbitrary points* in the domain -- not just in quadrature points or nodes. -The `PointEvalHandler` takes the following keyword arguments: - - `search_nneighbors`: How many nodes should be found in the nearest neighbor search for each - point. Usually there is no need to change this setting. Default value: `3`. - - `warn`: Show a warning if a point is not found. Default value: `true`. - The constructor takes a grid and a vector of coordinates for the points. The `PointEvalHandler` computes i) the corresponding cell, and ii) the (local) coordinate within the cell, for each point. The fields of the `PointEvalHandler` are: @@ -28,10 +29,10 @@ There are two ways to use the `PointEvalHandler` to evaluate functions: """ PointEvalHandler -struct PointEvalHandler{G,dim,T<:Real} +struct PointEvalHandler{G,T<:Real} grid::G cells::Vector{Union{Nothing, Int}} - local_coords::Vector{Union{Nothing, Vec{dim,T}}} + local_coords::Vector{Union{Nothing, Vec{1,T},Vec{2,T},Vec{3,T}}} end function Base.show(io::IO, ::MIME"text/plain", ph::PointEvalHandler) @@ -45,20 +46,27 @@ function Base.show(io::IO, ::MIME"text/plain", ph::PointEvalHandler) end end -function PointEvalHandler(grid::AbstractGrid, points::AbstractVector{Vec{dim,T}}; search_nneighbors=3, warn=true) where {dim, T} +# Internals: +# `PointEvalHandler` takes the following keyword arguments: +# - `search_nneighbors`: How many nodes should be found in the nearest neighbor search for each +# point. Usually there is no need to change this setting. Default value: `3`. +# - `warn::Bool`: Show a warning if a point is not found. Default value: `true`. +# - `newton_max_iters::Int`: Maximum number of inner Newton iterations. Default value: `10`. +# - `newton_residual_tolerance`: Tolerance for the residual norm to indicate convergence in the +# inner Newton solver. Default value: `1e-10`. +function PointEvalHandler(grid::AbstractGrid{dim}, points::AbstractVector{Vec{dim,T}}; search_nneighbors=3, warn::Bool=true, strategy = NewtonLineSearchPointFinder()) where {dim, T} node_cell_dicts = _get_node_cell_map(grid) - cells, local_coords = _get_cellcoords(points, grid, node_cell_dicts, search_nneighbors, warn) + cells, local_coords = _get_cellcoords(points, grid, node_cell_dicts, search_nneighbors, warn, strategy) return PointEvalHandler(grid, cells, local_coords) end -function _get_cellcoords(points::AbstractVector{Vec{dim,T}}, grid::AbstractGrid, node_cell_dicts::Dict{C,Dict{Int, Vector{Int}}}, search_nneighbors, warn) where {dim, T<:Real, C} - +function _get_cellcoords(points::AbstractVector{Vec{dim,T}}, grid::AbstractGrid, node_cell_dicts::Dict{C,Dict{Int, Vector{Int}}}, search_nneighbors, warn, strategy::NewtonLineSearchPointFinder) where {dim, T<:Real, C} # set up tree structure for finding nearest nodes to points kdtree = KDTree(reinterpret(Vec{dim,T}, getnodes(grid))) nearest_nodes, _ = knn(kdtree, points, search_nneighbors, true) cells = Vector{Union{Nothing, Int}}(nothing, length(points)) - local_coords = Vector{Union{Nothing, Vec{dim, T}}}(nothing, length(points)) + local_coords = Vector{Union{Nothing, Vec{1, T},Vec{2, T},Vec{3, T}}}(nothing, length(points)) for point_idx in 1:length(points) cell_found = false @@ -70,7 +78,7 @@ function _get_cellcoords(points::AbstractVector{Vec{dim,T}}, grid::AbstractGrid, possible_cells === nothing && continue # if node is not part of the subdofhandler, try the next node for cell in possible_cells cell_coords = getcoordinates(grid, cell) - is_in_cell, local_coord = point_in_cell(geom_interpol, cell_coords, points[point_idx]) + is_in_cell, local_coord = find_local_coordinate(geom_interpol, cell_coords, points[point_idx], strategy; warn) if is_in_cell cell_found = true cells[point_idx] = cell @@ -89,54 +97,85 @@ function _get_cellcoords(points::AbstractVector{Vec{dim,T}}, grid::AbstractGrid, return cells, local_coords end -# check if point is inside a cell based on physical coordinate -function point_in_cell(geom_interpol::Interpolation{shape}, cell_coordinates, global_coordinate) where {shape} - converged, x_local = find_local_coordinate(geom_interpol, cell_coordinates, global_coordinate) - if converged - return _check_isoparametric_boundaries(shape, x_local), x_local - else - return false, x_local - end -end - # check if point is inside a cell based on isoparametric coordinate -function _check_isoparametric_boundaries(::Type{RefHypercube{dim}}, x_local::Vec{dim, T}) where {dim, T} - tol = sqrt(eps(T)) - # All in the range [-1, 1] - return all(x -> abs(x) - 1 < tol, x_local) +function check_isoparametric_boundaries(::Type{RefHypercube{dim}}, x_local::Vec{dim, T}, tol) where {dim, T} + # All in the range [-1, 1]^dim + return all(x -> abs(x) - 1 ≤ tol, x_local) end # check if point is inside a cell based on isoparametric coordinate -function _check_isoparametric_boundaries(::Type{RefSimplex{dim}}, x_local::Vec{dim, T}) where {dim, T} - tol = sqrt(eps(T)) +function check_isoparametric_boundaries(::Type{RefSimplex{dim}}, x_local::Vec{dim, T}, tol) where {dim, T} # Positive and below the plane 1 - ξx - ξy - ξz return all(x -> x > -tol, x_local) && sum(x_local) - 1 < tol end +cellcenter(::Type{<:RefHypercube{dim}}, _::Type{T}) where {dim, T} = zero(Vec{dim, T}) +cellcenter(::Type{<:RefSimplex{dim}}, _::Type{T}) where {dim, T} = Vec{dim, T}((ntuple(d->1/3, dim))) + +_solve_helper(A::Tensor{2,dim}, b::Vec{dim}) where {dim} = inv(A) ⋅ b +_solve_helper(A::SMatrix{idim, odim}, b::Vec{idim,T}) where {odim, idim, T} = Vec{odim,T}(pinv(A) * b) + # See https://discourse.julialang.org/t/finding-the-value-of-a-field-at-a-spatial-location-in-juafem/38975/2 -# TODO: should we make iteration params optional keyword arguments? -function find_local_coordinate(interpolation, cell_coordinates::Vector{<:Vec{dim}}, global_coordinate::Vec{dim}; tol_norm = 1e-10) where dim +function find_local_coordinate(interpolation::Interpolation{refshape}, cell_coordinates::Vector{<:Vec{sdim}}, global_coordinate::Vec{sdim}, strategy::NewtonLineSearchPointFinder; warn::Bool = false) where {sdim, refshape} + boundary_tolerance = √(strategy.residual_tolerance) + T = promote_type(eltype(cell_coordinates[1]), eltype(global_coordinate)) n_basefuncs = getnbasefunctions(interpolation) @assert length(cell_coordinates) == n_basefuncs - local_guess = zero(Vec{dim, T}) - max_iters = 10 + local_guess = cellcenter(refshape, T) converged = false - for _ in 1:max_iters - global_guess = zero(Vec{dim, T}) - J = zero(Tensor{2, dim, T}) - # TODO batched eval after 764 is merged. - for j in 1:n_basefuncs - dNdξ, N = reference_shape_gradient_and_value(interpolation, local_guess, j) - global_guess += N * cell_coordinates[j] - J += cell_coordinates[j] ⊗ dNdξ - end + for iter in 1:strategy.max_iters + # Setup J(ξ) and x(ξ) + J, global_guess = calculate_jacobian_and_spatial_coordinate(interpolation, local_guess, cell_coordinates) + # Check if converged residual = global_guess - global_coordinate - if norm(residual) <= tol_norm - converged = true + best_residual_norm = norm(residual) # for line search below + # Early convergence check + if best_residual_norm ≤ strategy.residual_tolerance + converged = check_isoparametric_boundaries(refshape, local_guess, boundary_tolerance) + if converged + @debug println("Local point finder converged in $iter iterations with residual $best_residual_norm to $local_guess") + else + @debug println("Local point finder converged in $iter iterations with residual $best_residual_norm to a point outside the element: $local_guess") + end + break + end + if calculate_detJ(J) ≤ 0.0 + warn && @warn "det(J) negative! Aborting! $(calculate_detJ(J))" break end - local_guess -= inv(J) ⋅ residual + Δξ = _solve_helper(J, residual) # J \ b throws an error. TODO clean up when https://github.com/Ferrite-FEM/Tensors.jl/pull/188 is merged. + # Do line search if the new guess is outside the element + best_index = 1 + new_local_guess = local_guess - Δξ + global_guess = spatial_coordinate(interpolation, new_local_guess, cell_coordinates) + best_residual_norm = norm(global_guess - global_coordinate) + if !check_isoparametric_boundaries(refshape, new_local_guess, boundary_tolerance) + # Search for the residual minimizer, which is still inside the element + for next_index ∈ 2:strategy.max_line_searches + new_local_guess = local_guess - Δξ/2^(next_index-1) + global_guess = spatial_coordinate(interpolation, new_local_guess, cell_coordinates) + residual_norm = norm(global_guess - global_coordinate) + if residual_norm < best_residual_norm && check_isoparametric_boundaries(refshape, new_local_guess, boundary_tolerance) + best_residual_norm = residual_norm + best_index = next_index + end + end + end + local_guess -= Δξ / 2^(best_index-1) + # Late convergence check + if best_residual_norm ≤ strategy.residual_tolerance + converged = check_isoparametric_boundaries(refshape, local_guess, boundary_tolerance) + if converged + @debug println("Local point finder converged in $iter iterations with residual $best_residual_norm to $local_guess") + else + @debug println("Local point finder converged in $iter iterations with residual $best_residual_norm to a point outside the element: $local_guess") + end + break + end + if iter == strategy.max_iters + @debug println("Failed to converge in $(strategy.max_iters) iterations") + end end return converged, local_guess end @@ -180,8 +219,8 @@ function evaluate_at_points(ph::PointEvalHandler, proj::L2Projector, dof_vals::A evaluate_at_points(ph, proj.dh, dof_vals) end -function evaluate_at_points(ph::PointEvalHandler{<:Any, dim, T1}, dh::AbstractDofHandler, dof_vals::AbstractVector{T2}, - fname::Symbol=find_single_field(dh)) where {dim, T1, T2} +function evaluate_at_points(ph::PointEvalHandler{<:Any, T1}, dh::AbstractDofHandler, dof_vals::AbstractVector{T2}, + fname::Symbol=find_single_field(dh)) where {T1, T2} npoints = length(ph.cells) # Figure out the value type by creating a dummy PointValues ip = getfieldinterpolation(dh, find_field(dh, fname)) @@ -204,7 +243,7 @@ end # values in dof-order. They must be obtained from the same DofHandler that was used for constructing the PointEvalHandler function evaluate_at_points!(out_vals::Vector{T2}, - ph::PointEvalHandler{<:Any, <:Any, T_ph}, + ph::PointEvalHandler{<:Any, T_ph}, dh::DofHandler, dof_vals::Vector{T}, fname::Symbol, diff --git a/test/runtests.jl b/test/runtests.jl index 2a04c9dcef..11aab5aee8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -62,11 +62,10 @@ HAS_EXTENSIONS && include("blockarrays.jl") include("test_examples.jl") @test all(x -> isdefined(Ferrite, x), names(Ferrite)) # Test that all exported symbols are defined -#= See which is not defined if fails -for name in names(Ferrite) - isdefined(Ferrite, name) || @warn "Ferrite.$name is not defined but $name is exported" -end -=# +# # See which is not defined if fails +# for name in names(Ferrite) +# isdefined(Ferrite, name) || @warn "Ferrite.$name is not defined but $name is exported" +# end # Integration tests include("integration/test_simple_scalar_convergence.jl") diff --git a/test/test_pointevaluation.jl b/test/test_pointevaluation.jl index 4bb436de78..347290a16c 100644 --- a/test/test_pointevaluation.jl +++ b/test/test_pointevaluation.jl @@ -1,20 +1,29 @@ -function scalar_field() +using Ferrite, Test + +function test_pe_scalar_field() # isoparametric approximation - mesh = generate_grid(QuadraticQuadrilateral, (20, 20)) - f(x) = x[1]^2 + mesh = generate_grid(QuadraticQuadrilateral, (3, 3)) + perturbate_standard_grid!(mesh, 1/10) + + f(x) = x[1]+x[2] ip_f = Lagrange{RefQuadrilateral,2}() # function interpolation ip_g = Lagrange{RefQuadrilateral,2}() # geometry interpolation + # points where we want to retrieve field values + points = Vec{2,Float64}[] + # compute values in quadrature points - qr = QuadratureRule{RefQuadrilateral}(3) # exactly approximate quadratic field + qr = QuadratureRule{RefQuadrilateral}(3) # exactly integrate field cv = CellValues(qr, ip_f, ip_g) qp_vals = [Vector{Float64}(undef, getnquadpoints(cv)) for _ in 1:getncells(mesh)] for cellid in eachindex(mesh.cells) xe = getcoordinates(mesh, cellid) reinit!(cv, xe) for qp in 1:getnquadpoints(cv) - qp_vals[cellid][qp] = f(spatial_coordinate(cv, qp, xe)) + x = spatial_coordinate(cv, qp, xe) + qp_vals[cellid][qp] = f(x) + push!(points, x) end end @@ -22,11 +31,10 @@ function scalar_field() projector = L2Projector(ip_f, mesh) projector_vals = project(projector, qp_vals, qr) - # points where we want to retrieve field values - points = [Vec((x, 0.52)) for x in range(0.0; stop=1.0, length=100)] - # set up PointEvalHandler and retrieve values ph = PointEvalHandler(mesh, points) + @test all(x -> x !== nothing, ph.cells) + vals = evaluate_at_points(ph, projector, projector_vals) @test f.(points) ≈ vals @@ -35,18 +43,59 @@ function scalar_field() # @test f.(points) ≈ vals end -function vector_field() +function test_pe_embedded() + mesh = generate_grid(QuadraticQuadrilateral, (3, 3)) + perturbate_standard_grid!(mesh, 1/10) + mesh = Grid(mesh.cells, map(x->Node(Vec((x.x[1], x.x[2], x.x[1]+x.x[2]))), mesh.nodes)) + + f(x) = x[1]+x[2] + + ip_f = Lagrange{RefQuadrilateral,2}() # function interpolation + ip_g = Lagrange{RefQuadrilateral,2}()^3 # geometry interpolation + + # points where we want to retrieve field values + points = Vec{3,Float64}[] + + # compute values in quadrature points + qr = QuadratureRule{RefQuadrilateral}(3) # exactly integrate quadratic field + cv = CellValues(qr, ip_f, ip_g) + qp_vals = [Vector{Float64}(undef, getnquadpoints(cv)) for _ in 1:getncells(mesh)] + for cellid in eachindex(mesh.cells) + xe = getcoordinates(mesh, cellid) + reinit!(cv, xe) + for qp in 1:getnquadpoints(cv) + x = spatial_coordinate(cv, qp, xe) + qp_vals[cellid][qp] = f(x) + push!(points, x) + end + end + + # do a L2Projection for getting values in dofs + # @test_throws MethodError projector = L2Projector(ip_f, mesh; geom_ip=ip_g) + projector = L2Projector(ip_f, mesh) + projector_vals = project(projector, qp_vals, qr) + + # set up PointEvalHandler and retrieve values + ph = PointEvalHandler(mesh, points) + @test all(x -> x !== nothing, ph.cells) + + vals = evaluate_at_points(ph, projector, projector_vals) + @test f.(points) ≈ vals +end + +function test_pe_vector_field() ## vector field # isoparametric approximation - mesh = generate_grid(QuadraticQuadrilateral, (20, 20)) - f(x) = Vec((x[1]^2, x[1])) + mesh = generate_grid(QuadraticQuadrilateral, (3, 3)) + perturbate_standard_grid!(mesh, 1/10) + f(x) = Vec((x[1], x[2])) nodal_vals = [f(p.x) for p in mesh.nodes] - ip_f = Lagrange{RefQuadrilateral,2}() # function interpolation + ip_f = Lagrange{RefQuadrilateral,2}()^2 # function interpolation ip_g = Lagrange{RefQuadrilateral,2}() # geometry interpolation # compute values in quadrature points - qr = QuadratureRule{RefQuadrilateral}(3) # exactly approximate quadratic field + qr = QuadratureRule{RefQuadrilateral}(3) # exactly integrate field cv = CellValues(qr, ip_f, ip_g) qp_vals = [Vector{Vec{2,Float64}}(undef, getnquadpoints(cv)) for i=1:getncells(mesh)] for cellid in eachindex(mesh.cells) @@ -67,6 +116,7 @@ function vector_field() # set up PointEvalHandler and retrieve values ph = PointEvalHandler(mesh, points) + @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_vals) @test f.(points) ≈ vals @@ -75,14 +125,15 @@ function vector_field() # @test f.(points) ≈ vals end -function superparametric() +function test_pe_superparametric() # superparametric approximation - mesh = generate_grid(Quadrilateral, (20, 20)) - f(x) = x*x[1] + mesh = generate_grid(Quadrilateral, (3, 3)) + perturbate_standard_grid!(mesh, 1/10) + f(x) = x ip_f = Lagrange{RefQuadrilateral,2}() # function interpolation # compute values in quadrature points - qr = QuadratureRule{RefQuadrilateral}(3) # exactly approximate quadratic field + qr = QuadratureRule{RefQuadrilateral}(3) # exactly integrate field cv = CellValues(qr, ip_f) qp_vals = [Vector{Vec{2,Float64}}(undef, getnquadpoints(cv)) for i=1:getncells(mesh)] for cellid in eachindex(mesh.cells) @@ -102,14 +153,16 @@ function superparametric() # set up PointEvalHandler and retrieve values ph = PointEvalHandler(mesh, points) + @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_vals) # can recover a quadratic field by a quadratic approximation @test f.(points) ≈ vals end -function dofhandler() - mesh = generate_grid(Quadrilateral, (2,2)) +function test_pe_dofhandler() + mesh = generate_grid(Quadrilateral, (2, 2)) + perturbate_standard_grid!(mesh, 1/10) dof_vals = [1., 2., 5., 4., 3., 6., 8., 7., 9.] points = [node.x for node in mesh.nodes] # same as nodes @@ -118,6 +171,7 @@ function dofhandler() close!(dh) ph = PointEvalHandler(mesh, points) + @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, dh, dof_vals, :s) @test vals ≈ 1.0:9.0 @@ -165,11 +219,13 @@ function _pointeval_dofhandler2_manual_projection(dh, csv, cvv, f_s, f_v) return M \ f end -function dofhandler2(;three_dimensional=true) + +function test_pe_dofhandler2(;three_dimensional=true) # Computes the L2 projection of a quadratic field exactly # but not using L2Projector since we want the DofHandler dofs if (three_dimensional) - mesh = generate_grid(Hexahedron, (10, 10, 10)) + mesh = generate_grid(Hexahedron, (3, 3, 3)) + perturbate_standard_grid!(mesh, 1/10) f_s = x -> 1.0 + x[1] + x[2] + x[1] * x[2] + x[2] * x[3] f_v = x -> Vec{3}((1.0 + x[1] + x[2] + x[1] * x[2], 2.0 - x[1] - x[2] - x[1] * x[2], 4.0 + x[1] - x[2] + x[3] - x[1] * x[3] - x[2] * x[3])) points = [Vec((x, x, x)) for x in range(0; stop=1, length=100)] @@ -177,7 +233,8 @@ function dofhandler2(;three_dimensional=true) ip_f_v = ip_f^3 qr = QuadratureRule{RefHexahedron}(3) else - mesh = generate_grid(Quadrilateral, (20, 20)) + mesh = generate_grid(Quadrilateral, (3, 3)) + perturbate_standard_grid!(mesh, 1/10) f_s = x -> 1.0 + x[1] + x[2] + x[1] * x[2] f_v = x -> Vec{2}((1.0 + x[1] + x[2] + x[1] * x[2], 2.0 - x[1] - x[2] - x[1] * x[2])) points = [Vec((x, x, )) for x in range(0; stop=1, length=100)] @@ -192,6 +249,7 @@ function dofhandler2(;three_dimensional=true) add!(dh, :s, ip_f) add!(dh, :v, ip_f_v) close!(dh) + s_dofs = dof_range(dh, :s) v_dofs = dof_range(dh, :v) uh = _pointeval_dofhandler2_manual_projection(dh, csv, cvv, f_s, f_v) @@ -224,7 +282,7 @@ function dofhandler2(;three_dimensional=true) end end -function mixed_grid() +function test_pe_mixed_grid() ## Mixed grid where not all cells have the same fields # 5_______6 @@ -276,6 +334,7 @@ function mixed_grid() # first alternative: L2Projection to dofs projector_values = project(projector, qp_vals_quads, qr) ph = PointEvalHandler(mesh, points) + @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_values) @test vals[1:5] ≈ f.(points[1:5]) @test all(isnan, vals[6:end]) @@ -291,13 +350,15 @@ function mixed_grid() dof_vals = [1., 1., 2., 2., 4., 4., 3., 3., 6., 6., 5., 5.] points = [node.x for node in mesh.nodes] ph = PointEvalHandler(mesh, points) + @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, dh, dof_vals, :v) - @test vals == [Vec((i, i)) for i=1.0:6.0] + @test vals ≈ [Vec((i, i)) for i=1.0:6.0] end -function oneD() +function test_pe_oneD() # isoparametric approximation mesh = generate_grid(Line, (2,)) + perturbate_standard_grid!(mesh, 1/10) f(x) = x[1] nodal_vals = [f(p.x) for p in mesh.nodes] @@ -324,6 +385,7 @@ function oneD() # set up PointEvalHandler and retrieve values ph = PointEvalHandler(mesh, points) + @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_values) @test f.(points) ≈ vals @@ -333,25 +395,46 @@ function oneD() # @test f.(points) ≈ vals end -function first_point_missing() +function test_pe_first_point_missing() mesh = generate_grid(Quadrilateral, (1, 1)) points = [Vec(2.0, 0.0), Vec(0.0, 0.0)] ph = PointEvalHandler(mesh, points; warn=false) @test isnothing(ph.local_coords[1]) - @test ph.local_coords[2] == Vec(0.0, 0.0) + @test ph.local_coords[2] ≈ Vec(0.0, 0.0) end @testset "PointEvalHandler" begin - scalar_field() - vector_field() - dofhandler() - dofhandler2(;three_dimensional=false) - dofhandler2(;three_dimensional=true) - superparametric() - mixed_grid() - oneD() - first_point_missing() + @testset "scalar field" begin + test_pe_scalar_field() + test_pe_embedded() + end + + @testset "vector field" begin + test_pe_vector_field() + end + + @testset "dofhandler interaction" begin + test_pe_dofhandler() + test_pe_dofhandler2(;three_dimensional=false) + test_pe_dofhandler2(;three_dimensional=true) + end + + @testset "superparametric" begin + test_pe_superparametric() + end + + @testset "mixed grid" begin + test_pe_mixed_grid() + end + + @testset "1D" begin + test_pe_oneD() + end + + @testset "failure cases" begin + test_pe_first_point_missing() + end end @testset "PointValues" begin diff --git a/test/test_utils.jl b/test/test_utils.jl index 7fe8b0a1f5..22f88005d0 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -253,28 +253,6 @@ coords_on_faces(x, ::Serendipity{RefHexahedron, 2}) = check_equal_or_nan(a::Any, b::Any) = a==b || (isnan(a) && isnan(b)) check_equal_or_nan(a::Union{Tensor, Array}, b::Union{Tensor, Array}) = all(check_equal_or_nan.(a, b)) -# Hypercube is simply ⨂ᵈⁱᵐ Line :) -sample_random_point(::Type{Ferrite.RefHypercube{ref_dim}}) where {ref_dim} = Vec{ref_dim}(2.0 .* rand(Vec{ref_dim}) .- 1.0) -# Dirichlet type sampling -function sample_random_point(::Type{Ferrite.RefSimplex{ref_dim}}) where {ref_dim} - ξ = rand(ref_dim+1) - ξₜ = -log.(ξ) - return Vec{ref_dim}(ntuple(i->ξₜ[i], ref_dim) ./ sum(ξₜ)) -end -# Wedge = Triangle ⊗ Line -function sample_random_point(::Type{RefPrism}) - (ξ₁, ξ₂) = sample_random_point(RefTriangle) - ξ₃ = rand(Float64) - return Vec{3}((ξ₁, ξ₂, ξ₃)) -end -# TODO what to do here? The samplig is not uniform... -function sample_random_point(::Type{RefPyramid}) - ξ₃ = (1-1e-3)*rand(Float64) # Derivative is discontinuous at the top - # If we fix a z coordinate we get a Quad with extends (1-ξ₃) - (ξ₁, ξ₂) = (1.0 - ξ₃) .* rand(Vec{2}) - return Vec{3}((ξ₁, ξ₂, ξ₃)) -end - ###################################################### # Helpers for testing facet_to_element_transformation # ###################################################### @@ -284,6 +262,18 @@ getfacerefshape(::Tetrahedron, ::Int) = RefTriangle getfacerefshape(::Pyramid, face::Int) = face == 1 ? RefQuadrilateral : RefTriangle getfacerefshape(::Wedge, face::Int) = face ∈ (1,5) ? RefTriangle : RefQuadrilateral +function perturbate_standard_grid!(grid::Ferrite.AbstractGrid{dim}, strength) where dim + function perturbate(x::Vec{dim}) where dim + for d in 1:dim + if x[d] ≈ 1.0 || x[d] ≈ -1.0 + return x + end + end + return x + Vec{dim}(0.5*strength .* (2 .* rand(Vec{dim}) .- 1.0)) + end + transform_coordinates!(grid, perturbate) +end + ###################################################### # Dummy RefShape to test get_transformation_matrix # ###################################################### @@ -297,6 +287,41 @@ module DummyRefShapes end end +# Hypercube is simply ⨂ᵈⁱᵐ Line :) +sample_random_point(::Type{Ferrite.RefHypercube{ref_dim}}) where {ref_dim} = Vec{ref_dim}(ntuple(_ -> 2.0*rand()-1.0, ref_dim)) + +# Dirichlet type sampling +# +# The idea behind this sampling is that the d-Simplex (i.e. a generalized triangle in d dimensions) +# is just a surface in d+1 dimensions, that can be characterized by two constraints: +# 1. All coordinates are between 0 and 1 +# 2. The sum of all coordinates is exactly 1 +# This way we can just sample from the d+1 dimensional hypercube, transform the hypercube +# logarithmically to get a "normal distribution" over our simplex and enforce that the coordinates +# sum to 1. By dropping the last coordinate in this sample we finally obtain d numbers which lies in +# the d-simplex. +# +# A nice geometric sketch of this process is given in this stackexchange post: https://stats.stackexchange.com/a/296779 +function sample_random_point(::Type{Ferrite.RefSimplex{ref_dim}}) where {ref_dim} # Note that "ref_dim = d" in the text above + ξₜ = ntuple(_ -> -log(rand()), ref_dim+1) + return Vec{ref_dim}(ntuple(i->ξₜ[i], ref_dim) ./ sum(ξₜ)) +end + +# Wedge = Triangle ⊗ Line +function sample_random_point(::Type{RefPrism}) + (ξ₁, ξ₂) = sample_random_point(RefTriangle) + ξ₃ = rand(Float64) + return Vec{3}((ξ₁, ξ₂, ξ₃)) +end + +# TODO what to do here? The samplig is not uniform... +function sample_random_point(::Type{RefPyramid}) + ξ₃ = (1-1e-3)*rand(Float64) # Derivative is discontinuous at the top + # If we fix a z coordinate we get a Quad with extends (1-ξ₃) + (ξ₁, ξ₂) = (1.0 - ξ₃) .* Vec{2}(ntuple(_ -> rand(), 2)) + return Vec{3}((ξ₁, ξ₂, ξ₃)) +end + ############################################################ # Inverse parametric mapping ξ = ϕ(x) for testing hessians # ############################################################ @@ -304,7 +329,7 @@ function function_value_from_physical_coord(interpolation::Interpolation, cell_c n_basefuncs = getnbasefunctions(interpolation) scalar_ip = interpolation isa Ferrite.ScalarInterpolation ? interpolation : interpolation.ip @assert length(ue) == n_basefuncs - _, ξ = Ferrite.find_local_coordinate(scalar_ip, cell_coordinates, X; tol_norm=1e-16) + _, ξ = Ferrite.find_local_coordinate(scalar_ip, cell_coordinates, X, Ferrite.NewtonLineSearchPointFinder(residual_tolerance=1e-16)) u = zero(reference_shape_value(interpolation, ξ, 1)) for j in 1:n_basefuncs N = reference_shape_value(interpolation, ξ, j) From fbff852f34c521568a07d5fe61e0cba45ff3fa4f Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Wed, 7 Aug 2024 10:20:53 +0200 Subject: [PATCH 16/50] Add direct computation of mapping values (#1018) --- src/FEValues/GeometryMapping.jl | 41 +++++++++++++++++++++++++++++---- test/test_cellvalues.jl | 21 +++++++++++++++++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index ad0c98ec81..b5cede1a58 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -126,15 +126,15 @@ end end @inline function calculate_mapping(geo_mapping::GeometryMapping{1}, q_point::Int, x::AbstractVector{<:Vec}) - fecv_J = zero(otimes_returntype(eltype(x), eltype(geo_mapping.dMdξ))) + J = zero(otimes_returntype(eltype(x), eltype(geo_mapping.dMdξ))) @inbounds for j in 1:getngeobasefunctions(geo_mapping) - #fecv_J += x[j] ⊗ geo_mapping.dMdξ[j, q_point] - fecv_J += otimes_helper(x[j], geo_mapping.dMdξ[j, q_point]) + # J += x[j] ⊗ geo_mapping.dMdξ[j, q_point] + J += otimes_helper(x[j], geo_mapping.dMdξ[j, q_point]) end - return MappingValues(fecv_J, nothing) + return MappingValues(J, nothing) end -@inline function calculate_mapping(geo_mapping::GeometryMapping{2}, q_point, x) +@inline function calculate_mapping(geo_mapping::GeometryMapping{2}, q_point::Int, x::AbstractVector{<:Vec}) J = zero(otimes_returntype(eltype(x), eltype(geo_mapping.dMdξ))) sdim, rdim = size(J) (rdim != sdim) && error("hessian for embedded elements not implemented (rdim=$rdim, sdim=$sdim)") @@ -146,6 +146,37 @@ end return MappingValues(J, H) end +@inline function calculate_mapping(gip::ScalarInterpolation, ξ::Vec, x::AbstractVector{<:Vec}, ::Val{0}) + return MappingValues(nothing, nothing) +end + +@inline function calculate_mapping(gip::ScalarInterpolation, ξ::Vec{rdim,T}, x::AbstractVector{<:Vec{sdim}}, ::Val{1}) where {T,rdim, sdim} + n_basefuncs = getnbasefunctions(gip) + @boundscheck checkbounds(x, Base.OneTo(n_basefuncs)) + + J = zero(otimes_returntype(Vec{sdim,T}, Vec{rdim,T})) + @inbounds for j in 1:n_basefuncs + dMdξ = reference_shape_gradient(gip, ξ, j) + # J += x[j] ⊗ dMdξ # https://github.com/Ferrite-FEM/Tensors.jl/pull/188 + J += otimes_helper(x[j], dMdξ) + end + return MappingValues(J, nothing) +end + +@inline function calculate_mapping(gip::ScalarInterpolation, ξ::Vec{rdim,T}, x::AbstractVector{<:Vec{sdim}}, ::Val{2}) where {T,rdim, sdim} + n_basefuncs = getnbasefunctions(gip) + @boundscheck checkbounds(x, Base.OneTo(n_basefuncs)) + (rdim != sdim) && error("hessian for embedded elements not implemented (rdim=$rdim, sdim=$sdim)") + J = zero(otimes_returntype(Vec{sdim,T}, Vec{rdim,T})) + H = zero(otimes_returntype(eltype(x), typeof(J))) + @inbounds for j in 1:n_basefuncs + d2Mdξ2, dMdξ, _ = reference_shape_hessian_gradient_and_value(gip, ξ, j) + J += x[j] ⊗ dMdξ + H += x[j] ⊗ d2Mdξ2 + end + return MappingValues(J, H) +end + calculate_detJ(J::Tensor{2}) = det(J) calculate_detJ(J::SMatrix) = embedding_det(J) diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index dee99c5f37..488fefdc6d 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -167,6 +167,27 @@ end end +@testset "GeometryMapping" begin + grid = generate_grid(Quadrilateral, (1,1)) + cc = first(CellIterator(grid)) + + qr = QuadratureRule{RefQuadrilateral}(1) + ξ = first(Ferrite.getpoints(qr)) + ip = Lagrange{RefQuadrilateral,1}() + + cv0 = CellValues(Float64, qr, ip, ip^2; update_detJdV=false, update_gradients=false, update_hessians=false) + reinit!(cv0, cc) + @test Ferrite.calculate_mapping(cv0.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(0)) + + cv1 = CellValues(Float64, qr, ip, ip^2; update_detJdV=false, update_gradients=true, update_hessians=false) + reinit!(cv1, cc) + @test Ferrite.calculate_mapping(cv1.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(1)) + + cv2 = CellValues(Float64, qr, ip, ip^2; update_detJdV=false, update_gradients=false, update_hessians=true) + reinit!(cv2, cc) + @test Ferrite.calculate_mapping(cv2.geo_mapping, 1, cc.coords) == Ferrite.calculate_mapping(ip, ξ, cc.coords, Val(2)) +end + @testset "#265: error message for incompatible geometric interpolation" begin dim = 1 deg = 1 From 9f083f2019e981b5135c344f345d3773e61af11a Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 10 Aug 2024 12:44:57 +0200 Subject: [PATCH 17/50] Remove VTKFileCollection (#1015) Restores pre #692 functionality for collection files by removing VTKFileCollection. --- CHANGELOG.md | 26 ++----- docs/Manifest.toml | 6 +- docs/Project.toml | 1 + .../quasi_incompressible_hyperelasticity.jl | 11 +-- docs/src/literate-tutorials/ns_vs_diffeq.jl | 11 ++- docs/src/literate-tutorials/porous_media.jl | 11 ++- .../literate-tutorials/reactive_surface.jl | 14 ++-- .../transient_heat_equation.jl | 19 ++--- docs/src/reference/export.md | 2 - docs/src/topics/export.md | 12 +-- src/Export/VTK.jl | 77 +------------------ src/exports.jl | 2 - test/runtests.jl | 1 + test/test_grid_dofhandler_vtk.jl | 59 +++----------- 14 files changed, 72 insertions(+), 180 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 600a333c4d..d0e2f25619 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -212,7 +212,7 @@ more discussion). # ... ``` -- **VTK Export**: The VTK export has been changed to become and export backend [#692][github-692]. +- **VTK Export**: The VTK export has been changed [#692][github-692]. ```diff - vtk_grid(name, dh) do vtk - vtk_point_data(vtk, dh, a) @@ -225,24 +225,10 @@ more discussion). + write_projection(vtk, proj, projected_data, "my projected data") + write_cell_data(vtk, cell_data, "my projected data") end - - # Using a collection for e.g. multiple timesteps - - pvd = paraview_collection("mypvd") - + pvd = VTKFileCollection("mypvd", grid); - - for t in timesteps - # solve problem to find `u` - - vtk_grid("transient-heat-$t", dh) do vtk - - vtk_point_data(vtk, dh, u) - - vtk_save(vtk) - - pvd[t] = vtk - - end - + addstep!(pvd, t) do io - + write_solution(io, dh, u) - + end - end ``` - + When using a `paraview_collection` collection for e.g. multiple timesteps + the `VTKGridFile` object can be used instead of the previous type returned + from `vtk_grid`. - **Sparsity pattern and global matrix construction**: since there is now explicit support for working with the sparsity pattern before instantiating a matrix the function @@ -426,8 +412,8 @@ more discussion). by type piracy. In order to be invariant to the underlying `Set` datatype as well as omitting type piracy, ([#835][github-835]) implemented `isequal` and `hash` for `BoundaryIndex` datatypes. -- **VTK export**: Ferrite no longer extends methods from `WriteVTK.jl`, instead the new types - `VTKGridFile` and `VTKFileCollection` should be used instead. New methods exists for writing to +- **VTK export**: Ferrite no longer extends `WriteVTK.vtk_grid` and associated functions, + instead the new type `VTKGridFile` should be used instead. New methods exists for writing to a `VTKGridFile`, e.g. `write_solution`, `write_cell_data`, `write_node_data`, and `write_projection`. See [#692][github-692]. diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 3e13ffc023..080b04599b 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.10.4" manifest_format = "2.0" -project_hash = "8722b6a67abf1b81e2c7808ddfcfb1d737cd0040" +project_hash = "2283fea1dfcfa0f2c82cdecbc5750875c26d6009" [[deps.ADTypes]] git-tree-sha1 = "3a6511b6e54550bcbc986c560921a8cd7761fcd8" @@ -1987,9 +1987,9 @@ version = "1.31.0+0" [[deps.WriteVTK]] deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] -git-tree-sha1 = "48b9e8e9c83865e99e57f027d4edfa94e0acddae" +git-tree-sha1 = "46664bb833f24e4fe561192e3753c9168c3b71b2" uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192" -version = "1.19.1" +version = "1.19.2" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] diff --git a/docs/Project.toml b/docs/Project.toml index 876724cc7f..4b6b129d19 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -20,3 +20,4 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Tensors = "48a634ad-e948-5137-8d70-aa71f2a747f4" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" diff --git a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl index c0091d2f74..ef0a162b9f 100644 --- a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl +++ b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl @@ -72,7 +72,7 @@ # ## Implementation # We now get to the actual code. First, we import the respective packages -using Ferrite, Tensors, ProgressMeter +using Ferrite, Tensors, ProgressMeter, WriteVTK using BlockArrays, SparseArrays, LinearAlgebra # and the corresponding `struct` to store our material properties. @@ -305,8 +305,8 @@ function solve(interpolation_u, interpolation_p) Δt = 0.1; NEWTON_TOL = 1e-8 - pvd = VTKFileCollection("hyperelasticity_incomp_mixed", grid); - for t ∈ 0.0:Δt:Tf + pvd = paraview_collection("hyperelasticity_incomp_mixed"); + for (step, t) ∈ enumerate(0.0:Δt:Tf) ## Perform Newton iterations Ferrite.update!(dbc, t) apply!(w, dbc) @@ -334,8 +334,9 @@ function solve(interpolation_u, interpolation_p) end; ## Save the solution fields - addstep!(pvd, t) do io - write_solution(io, dh, w) + VTKGridFile("hyperelasticity_incomp_mixed_$step", grid) do vtk + write_solution(vtk, dh, w) + pvd[t] = vtk end end; close(pvd); diff --git a/docs/src/literate-tutorials/ns_vs_diffeq.jl b/docs/src/literate-tutorials/ns_vs_diffeq.jl index 28db2ea391..6b945f8340 100644 --- a/docs/src/literate-tutorials/ns_vs_diffeq.jl +++ b/docs/src/literate-tutorials/ns_vs_diffeq.jl @@ -119,7 +119,7 @@ # The full program, without comments, can be found in the next [section](@ref ns_vs_diffeq-plain-program). # # First we load Ferrite and some other packages we need -using Ferrite, SparseArrays, BlockArrays, LinearAlgebra, UnPack, LinearSolve +using Ferrite, SparseArrays, BlockArrays, LinearAlgebra, UnPack, LinearSolve, WriteVTK # Since we do not need the complete DifferentialEquations suite, we just load the required ODE infrastructure, which can also handle # DAEs in mass matrix form. using OrdinaryDiffEq @@ -576,10 +576,13 @@ integrator = init( # !!! note "Export of solution" # Exporting interpolated solutions of problems containing mass matrices is currently broken. # Thus, the `intervals` iterator is used. Note that `solve` holds all solutions in the memory. -pvd = VTKFileCollection("vortex-street", grid); +pvd = paraview_collection("vortex-street") +step = 0 for (u,t) in intervals(integrator) - addstep!(pvd, t) do io - write_solution(io, dh, u) + step += 1 + VTKGridFile("vortex-street-$step", dh) do vtk + write_solution(vtk, dh, u) + pvd[t] = vtk end end close(pvd); diff --git a/docs/src/literate-tutorials/porous_media.jl b/docs/src/literate-tutorials/porous_media.jl index 65c211dd43..126222c199 100644 --- a/docs/src/literate-tutorials/porous_media.jl +++ b/docs/src/literate-tutorials/porous_media.jl @@ -96,7 +96,7 @@ #md # the final [section](@ref porous-media-plain-program) # # Required packages -using Ferrite, FerriteMeshParser, Tensors +using Ferrite, FerriteMeshParser, Tensors, WriteVTK # ### Elasticity # We start by defining the elastic material type, containing the elastic stiffness, @@ -335,7 +335,8 @@ function solve(dh, ch, domains; Δt=0.025, t_total=1.0) r = zeros(ndofs(dh)) a = zeros(ndofs(dh)) a_old = copy(a) - pvd = VTKFileCollection("porous_media.pvd", dh); + pvd = paraview_collection("porous_media") + step = 0 for t in 0:Δt:t_total if t>0 update!(ch, t) @@ -347,8 +348,10 @@ function solve(dh, ch, domains; Δt=0.025, t_total=1.0) a .+= Δa copyto!(a_old, a) end - addstep!(pvd, t) do io - write_solution(io, dh, a) + step += 1 + VTKGridFile("porous_media_$step", dh) do vtk + write_solution(vtk, dh, a) + pvd[t] = vtk end end close(pvd); diff --git a/docs/src/literate-tutorials/reactive_surface.jl b/docs/src/literate-tutorials/reactive_surface.jl index 3f5eebbef0..d1003d5bed 100644 --- a/docs/src/literate-tutorials/reactive_surface.jl +++ b/docs/src/literate-tutorials/reactive_surface.jl @@ -77,7 +77,7 @@ nothing #hide # First we load Ferrite, and some other packages we need using Ferrite, FerriteGmsh -using BlockArrays, SparseArrays, LinearAlgebra +using BlockArrays, SparseArrays, LinearAlgebra, WriteVTK # ### Assembly routines # Before we head into the assembly, we define a helper struct to control the dispatches. @@ -276,9 +276,10 @@ function gray_scott_on_sphere(material::GrayScottMaterial, Δt::Real, T::Real, r setup_initial_conditions!(uₜ₋₁, cellvalues, dh) ## And prepare output for visualization. - pvd = VTKFileCollection("reactive-surface.pvd", dh); - addstep!(pvd, 0.0) do io - write_solution(io, dh, uₜ₋₁) + pvd = paraview_collection("reactive-surface") + VTKGridFile("reactive-surface-0", dh) do vtk + write_solution(vtk, dh, uₜ₋₁) + pvd[0.0] = vtk end ## This is now the main solve loop. @@ -302,8 +303,9 @@ function gray_scott_on_sphere(material::GrayScottMaterial, Δt::Real, T::Real, r ## The solution is then stored every 10th step to vtk files for ## later visualization purposes. if (iₜ % 10) == 0 - addstep!(pvd, t) do io - write_solution(io, dh, uₜ₋₁) + VTKGridFile("reactive-surface-$(iₜ)", dh) do vtk + write_solution(vtk, dh, uₜ₋₁) + pvd[t] = vtk end end diff --git a/docs/src/literate-tutorials/transient_heat_equation.jl b/docs/src/literate-tutorials/transient_heat_equation.jl index 757b7658f8..c6a89de6f0 100644 --- a/docs/src/literate-tutorials/transient_heat_equation.jl +++ b/docs/src/literate-tutorials/transient_heat_equation.jl @@ -58,7 +58,7 @@ #md # The full program, without comments, can be found in the next [section](@ref heat_equation-plain-program). # # First we load Ferrite, and some other packages we need. -using Ferrite, SparseArrays +using Ferrite, SparseArrays, WriteVTK # We create the same grid as in the heat equation example. grid = generate_grid(Quadrilateral, (100, 100)); @@ -189,15 +189,15 @@ apply_analytical!(uₙ, dh, :u, x -> (x[1]^2 - 1) * (x[2]^2 - 1) * max_temp); # Here, we apply **once** the boundary conditions to the system matrix `A`. apply!(A, ch); -# To store the solution, we initialize a `VTKFileCollection` (.pvd) file. -pvd = VTKFileCollection("transient-heat", grid); -t = 0 -addstep!(pvd, t) do io - write_solution(io, dh, uₙ) +# To store the solution, we initialize a paraview collection (.pvd) file, +pvd = paraview_collection("transient-heat") +VTKGridFile("transient-heat-0", dh) do vtk + write_solution(vtk, dh, uₙ) + pvd[0.0] = vtk end # At this point everything is set up and we can finally approach the time loop. -for t in Δt:Δt:T +for (step, t) in enumerate(Δt:Δt:T) #First of all, we need to update the Dirichlet boundary condition values. update!(ch, t) @@ -209,8 +209,9 @@ for t in Δt:Δt:T #Finally, we can solve the time step and save the solution afterwards. u = A \ b - addstep!(pvd, t) do io - write_solution(io, dh, u) + VTKGridFile("transient-heat-$step", dh) do vtk + write_solution(vtk, dh, u) + pvd[t] = vtk end #At the end of the time loop, we set the previous solution to the current one and go to the next time step. uₙ .= u diff --git a/docs/src/reference/export.md b/docs/src/reference/export.md index f96d858e91..61e5849cb8 100644 --- a/docs/src/reference/export.md +++ b/docs/src/reference/export.md @@ -25,8 +25,6 @@ PointLocation ## VTK Export ```@docs VTKGridFile -VTKFileCollection -addstep! write_solution write_projection write_cell_data diff --git a/docs/src/topics/export.md b/docs/src/topics/export.md index dccc57c807..472e6ecad9 100644 --- a/docs/src/topics/export.md +++ b/docs/src/topics/export.md @@ -43,15 +43,17 @@ The data written by `write_solution`, `write_cell_data`, `write_node_data`, and For simulations with multiple time steps, typically one `VTK` (`.vtu`) file is written for each time step. In order to connect the actual time with each of these files, -a `VTKFileCollection` can be used, which will write one paraview datafile (`.pvd`) -file and one `VTKGridFile` (`.vtu`) for each time step. +the `paraview_collection` can function from `WriteVTK.jl` can be used. This will create +one paraview datafile (`.pvd`) file and one `VTKGridFile` (`.vtu`) for each time step. ```@example export -pvd = VTKFileCollection("my_results", grid) -for t in range(0, 1, 5) +using WriteVTK +pvd = paraview_collection("my_results") +for (step, t) in enumerate(range(0, 1, 5)) # Do calculations to update u - addstep!(pvd, t) do vtk + VTKGridFile("my_results_$step", dh) do vtk write_solution(vtk, dh, u) + pvd[t] = vtk end end close(pvd); diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index ea1a06d2c5..7bc5f0d2bf 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -54,80 +54,11 @@ function Base.show(io::IO, ::MIME"text/plain", vtk::VTKGridFile) print(io, "VTKGridFile for the $open_str file \"$(filename)\".") end -""" - VTKFileCollection(name::String, grid::AbstractGrid; kwargs...) - VTKFileCollection(name::String, dh::DofHandler; kwargs...) - -Create a paraview data file (.pvd) that can be used to -save multiple vtk file along with a time stamp. The keyword arguments -are forwarded to `WriteVTK.paraview_collection`. - -See [`addstep!`](@ref) for examples for how to use `VTKFileCollection`. -``` -""" -mutable struct VTKFileCollection{P<:WriteVTK.CollectionFile,G_DH} - pvd::P - grid_or_dh::G_DH - name::String - step::Int -end -function VTKFileCollection(name::String, grid_or_dh::Union{AbstractGrid,AbstractDofHandler}; kwargs...) - pvd = WriteVTK.paraview_collection(name; kwargs...) - basename = string(first(split(pvd.path, ".pvd"))) - return VTKFileCollection(pvd, grid_or_dh, basename, 0) +function WriteVTK.collection_add_timestep(pvd::WriteVTK.CollectionFile, datfile::VTKGridFile, time::Real) + WriteVTK.collection_add_timestep(pvd, datfile.vtk, time) end -Base.close(pvd::VTKFileCollection) = WriteVTK.vtk_save(pvd.pvd) - -""" - addstep!(f::Function, pvd::VTKFileCollection, t::Real, [grid_or_dh]; kwargs...) - -Add a step at time `t` by writing a `VTKGridFile` to `pvd`. -The keyword arguments are forwarded to `WriteVTK.vtk_grid`. -If required, a new grid can be used by supplying the grid or dofhandler as the last argument. -Should be used in a do-block, e.g. -```julia -filename = "myoutput" -pvd = VTKFileCollection(filename, grid) -for (n, t) in pairs(timevector) - # Calculate, e.g., the solution `u` and the stress `σeff` - addstep!(pvd, t) do io - write_cell_data(io, σeff, "Effective stress") - write_solution(io, dh, u) - end -end -close(pvd) -``` -""" -function addstep!(f::Function, pvd::VTKFileCollection, t, grid=pvd.grid_or_dh; kwargs...) - pvd.step += 1 - VTKGridFile(string(pvd.name, "_", pvd.step), grid; kwargs...) do vtk - f(vtk) - pvd.pvd[t] = vtk.vtk # Add to collection - end -end - -""" - addstep!(pvd::VTKFileCollection, vtk::VTKGridFile, t) - -As an alternative to using the `addstep!(pvd, t) do` block, it is -also possible to add a specific `vtk` at time `t` to `pvd`. -Note that this will close the `vtk`. Example -```julia -filename = "myoutput" -pvd = VTKFileCollection(filename, grid) -for (n, t) in pairs(timevector) - # Calculate, e.g., the solution `u` and the stress `σeff` - vtk = VTKGridFile(string(filename, "_", n), dh) - write_cell_data(vtk, σeff, "Effective stress") - write_solution(vtk, dh, u) - addstep!(pvd, vtk, t) -end -close(pvd) -``` -""" -function addstep!(pvd::VTKFileCollection, vtk::VTKGridFile, t) - pvd.step += 1 - pvd.pvd[t] = vtk.vtk +function Base.setindex!(pvd::WriteVTK.CollectionFile, datfile::VTKGridFile, time::Real) + WriteVTK.collection_add_timestep(pvd, datfile.vtk, time) end cell_to_vtkcell(::Type{Line}) = VTKCellTypes.VTK_LINE diff --git a/src/exports.jl b/src/exports.jl index 84054852bb..c49b24eba0 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -174,8 +174,6 @@ export write_cell_data, write_projection, write_node_data, - VTKFileCollection, - addstep!, # L2 Projection project, diff --git a/test/runtests.jl b/test/runtests.jl index 11aab5aee8..a390852754 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,6 +9,7 @@ using LinearAlgebra using SparseArrays using StaticArrays using OrderedCollections +using WriteVTK const HAS_EXTENSIONS = isdefined(Base, :get_extension) diff --git a/test/test_grid_dofhandler_vtk.jl b/test/test_grid_dofhandler_vtk.jl index 7a01bb8317..1fb651cc8e 100644 --- a/test/test_grid_dofhandler_vtk.jl +++ b/test/test_grid_dofhandler_vtk.jl @@ -746,52 +746,17 @@ end @test dh1.cell_dofs == dh2.cell_dofs end - @testset "VTKFileCollection" begin - @testset "equivalence of addstep methods" begin - grid = generate_grid(Triangle, (2,2)) - celldata = rand(getncells(grid)) - fname = "addstep" - pvd1 = VTKFileCollection(fname, grid) - pvd2 = VTKFileCollection(fname, grid) - timesteps = 0:0.5:0.5 - for (n, t) in pairs(timesteps) - addstep!(pvd1, t) do io - write_cell_data(io, celldata*n, "celldata") - end - vtk = VTKGridFile(string(fname, "2_", n), grid) - write_cell_data(vtk, celldata*n, "celldata") - addstep!(pvd2, vtk, t) - @test !(isopen(vtk.vtk)) - end - close.((pvd1, pvd2)) - @test pvd1.step == pvd2.step # Same nr of steps added - for (n, t) in pairs(timesteps) - fname1 = string(fname, "_", n, ".vtu") - fname2 = string(fname, "2_", n, ".vtu") - sha_vtk1 = bytes2hex(open(SHA.sha1, fname1)) - sha_vtk2 = bytes2hex(open(SHA.sha1, fname2)) - @test sha_vtk1 == sha_vtk2 - rm.((fname1, fname2)) - end - rm(string(fname, ".pvd")) - # Solving https://github.com/Ferrite-FEM/Ferrite.jl/issues/397 - # would allow checking the pvd files as well. - end - @testset "kwargs forwarding" begin - grid = generate_grid(Quadrilateral, (10,10)) - file_sizes = Int[] - fname = "test_collection_kwargs" - for compress in (true, false) - pvd = VTKFileCollection(fname, grid) - addstep!(pvd, 0.0; compress) do io - nothing - end - close(pvd) - push!(file_sizes, stat(string(fname, "_1.vtu")).size) - rm(string(fname, "_1.vtu")) - end - rm(string(fname, ".pvd")) - @test file_sizes[1] < file_sizes[2] # Check that compress=true gives smaller file size - end + @testset "paraview_collection" begin + grid = generate_grid(Triangle, (2,2)) + celldata = rand(getncells(grid)) + pvd = WriteVTK.paraview_collection("collection") + vtk1 = VTKGridFile("file1", grid) + write_cell_data(vtk1, celldata, "celldata") + @assert isopen(vtk1.vtk) + pvd[0.5] = vtk1 + @test !isopen(vtk1.vtk) # Should be closed when adding it + vtk2 = VTKGridFile("file2", grid) + WriteVTK.collection_add_timestep(pvd, vtk2, 1.0) + @test !isopen(vtk2.vtk) # Should be closed when adding it end end From b6976d3e22f7cc14e8e97f53b6de1d094a3c9889 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 10 Aug 2024 13:56:36 +0200 Subject: [PATCH 18/50] Fix scope error in ns_vs_diffeq tutorial (#1045) Fix error introduced in #1015 --- docs/src/literate-tutorials/ns_vs_diffeq.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/src/literate-tutorials/ns_vs_diffeq.jl b/docs/src/literate-tutorials/ns_vs_diffeq.jl index 6b945f8340..8d3455943b 100644 --- a/docs/src/literate-tutorials/ns_vs_diffeq.jl +++ b/docs/src/literate-tutorials/ns_vs_diffeq.jl @@ -577,9 +577,7 @@ integrator = init( # Exporting interpolated solutions of problems containing mass matrices is currently broken. # Thus, the `intervals` iterator is used. Note that `solve` holds all solutions in the memory. pvd = paraview_collection("vortex-street") -step = 0 -for (u,t) in intervals(integrator) - step += 1 +for (step, (u,t)) in enumerate(intervals(integrator)) VTKGridFile("vortex-street-$step", dh) do vtk write_solution(vtk, dh, u) pvd[t] = vtk From 5d9fe59af51a053a089f69981f6098c9b6fd8fb2 Mon Sep 17 00:00:00 2001 From: Kim Louisa Auth Date: Mon, 19 Aug 2024 21:10:36 +0200 Subject: [PATCH 19/50] Linear elasticity tutorial (#799) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Maximilian Köhler Co-authored-by: Knut Andreas Meyer Co-authored-by: Dennis Ogiermann --- docs/download_resources.jl | 2 + .../literate-tutorials/linear_elasticity.jl | 408 +++++++++++++++++- 2 files changed, 408 insertions(+), 2 deletions(-) diff --git a/docs/download_resources.jl b/docs/download_resources.jl index 2dfbeffb2f..1a78b46f7f 100644 --- a/docs/download_resources.jl +++ b/docs/download_resources.jl @@ -13,6 +13,8 @@ for (file, url) in [ "porous_media_0p25.inp" => "https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/gh-pages/assets/porous_media_0p25.inp", "reactive_surface.gif" => "https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/gh-pages/assets/reactive_surface.gif", "nsdiffeq.gif" => "https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/gh-pages/assets/nsdiffeq.gif", + "linear_elasticity.svg" => "https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/gh-pages/assets/linear_elasticity.svg", + "linear_elasticity_stress.png" => "https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/gh-pages/assets/linear_elasticity_stress.png", ] afile = joinpath(directory, file) if !isfile(afile) diff --git a/docs/src/literate-tutorials/linear_elasticity.jl b/docs/src/literate-tutorials/linear_elasticity.jl index 3381be54a6..09e778f181 100644 --- a/docs/src/literate-tutorials/linear_elasticity.jl +++ b/docs/src/literate-tutorials/linear_elasticity.jl @@ -1,3 +1,407 @@ -# # Linear elasticity +# # [Linear elasticity](@id tutorial-linear-elasticity) # -# TBW +# ![](linear_elasticity.svg) +# +# *Figure 1*: Linear elastically deformed 1mm $\times$ 1mm Ferrite logo. +# +#md # !!! tip +#md # This tutorial is also available as a Jupyter notebook: +#md # [`linear_elasticity.ipynb`](@__NBVIEWER_ROOT_URL__/tutorials/linear_elasticity.ipynb). +#- +# +# ## Introduction +# +# The classical first finite element problem to solve in solid mechanics is a linear balance +# of momentum problem. We will use this to introduce a vector valued field, the displacements +# $\boldsymbol{u}(\boldsymbol{x})$. In addition, some features of the +# [`Tensors.jl`](https://github.com/Ferrite-FEM/Tensors.jl) toolbox are demonstrated. +# +# ### Strong form +# The strong form of the balance of momentum for quasi-static loading is given by +# ```math +# \begin{alignat*}{2} +# \mathrm{div}(\boldsymbol{\sigma}) + \boldsymbol{b} &= 0 \quad &&\boldsymbol{x} \in \Omega \\ +# \boldsymbol{u} &= \boldsymbol{u}_\mathrm{D} \quad &&\boldsymbol{x} \in \Gamma_\mathrm{D} \\ +# \boldsymbol{n} \cdot \boldsymbol{\sigma} &= \boldsymbol{t}_\mathrm{N} \quad &&\boldsymbol{x} \in \Gamma_\mathrm{N} +# \end{alignat*} +# ``` +# where $\boldsymbol{\sigma}$ is the (Cauchy) stress tensor and $\boldsymbol{b}$ the body force. +# The domain, $\Omega$, has the boundary $\Gamma$, consisting of a Dirichlet part, $\Gamma_\mathrm{D}$, and +# a Neumann part, $\Gamma_\mathrm{N}$, with outward pointing normal vector $\boldsymbol{n}$. +# $\boldsymbol{u}_\mathrm{D}$ denotes prescribed displacements on $\Gamma_\mathrm{D}$, +# while $\boldsymbol{t}_\mathrm{N}$ the known tractions on $\Gamma_\mathrm{N}$. +# +# In this tutorial, we use linear elasticity, such that +# ```math +# \boldsymbol{\sigma} = \mathsf{C} : \boldsymbol{\varepsilon}, \quad +# \boldsymbol{\varepsilon} = \left[\mathrm{grad}(\boldsymbol{u})\right]^\mathrm{sym} +# ``` +# where $\mathsf{C}$ is the 4th order elastic stiffness tensor and +# $\boldsymbol{\varepsilon}$ the small strain tensor. +# The colon, $:$, represents the double contraction, +# $\sigma_{ij} = \mathsf{C}_{ijkl} \varepsilon_{kl}$, and the superscript $\mathrm{sym}$ +# denotes the symmetric part. +# +# ### Weak form +# The resulting weak form is given given as follows: Find ``\boldsymbol{u} \in \mathbb{U}`` such that +# ```math +# \int_\Omega +# \mathrm{grad}(\delta \boldsymbol{u}) : \boldsymbol{\sigma} +# \, \mathrm{d}\Omega +# = +# \int_{\Gamma} +# \delta \boldsymbol{u} \cdot \boldsymbol{t} +# \, \mathrm{d}\Gamma +# + +# \int_\Omega +# \delta \boldsymbol{u} \cdot \boldsymbol{b} +# \, \mathrm{d}\Omega +# \quad \forall \, \delta \boldsymbol{u} \in \mathbb{T}, +# ``` +# where $\mathbb{U}$ and $\mathbb{T}$ denote suitable trial and test function spaces. +# $\delta \boldsymbol{u}$ is a vector valued test function and +# $\boldsymbol{t} = \boldsymbol{\sigma}\cdot\boldsymbol{n}$ is the traction vector on +# the boundary. In this tutorial, we will neglect body forces (i.e. $\boldsymbol{b} = \boldsymbol{0}$) and the weak form reduces to +# ```math +# \int_\Omega +# \mathrm{grad}(\delta \boldsymbol{u}) : \boldsymbol{\sigma} +# \, \mathrm{d}\Omega +# = +# \int_{\Gamma} +# \delta \boldsymbol{u} \cdot \boldsymbol{t} +# \, \mathrm{d}\Gamma \,. +# ``` +# +# ### Finite element form +# Finally, the finite element form is obtained by introducing the finite element shape functions. +# Since the displacement field, $\boldsymbol{u}$, is vector valued, we use vector valued shape functions +# $\delta\boldsymbol{N}_i$ and $\boldsymbol{N}_i$ to approximate the test and trial functions: +# ```math +# \boldsymbol{u} \approx \sum_{i=1}^N \boldsymbol{N}_i (\boldsymbol{x}) \, \hat{u}_i +# \qquad +# \delta \boldsymbol{u} \approx \sum_{i=1}^N \delta\boldsymbol{N}_i (\boldsymbol{x}) \, \delta \hat{u}_i +# ``` +# Here $N$ is the number of nodal variables, with $\hat{u}_i$ and $\delta\hat{u}_i$ representing the $i$-th nodal value. +# Using the Einstein summation convention, we can write this in short form as +# $\boldsymbol{u} \approx \boldsymbol{N}_i \, \hat{u}_i$ and $\delta\boldsymbol{u} \approx \delta\boldsymbol{N}_i \, \delta\hat{u}_i$. +# +# Inserting the these into the weak form, and noting that that the equation should hold for all $\delta \hat{u}_i$, we get +# ```math +# \underbrace{\int_\Omega \mathrm{grad}(\delta \boldsymbol{N}_i) : \boldsymbol{\sigma}\ \mathrm{d}\Omega}_{f_i^\mathrm{int}} = \underbrace{\int_\Gamma \delta \boldsymbol{N}_i \cdot \boldsymbol{t}\ \mathrm{d}\Gamma}_{f_i^\mathrm{ext}} +# ``` +# Inserting the linear constitutive relationship, $\boldsymbol{\sigma} = \mathsf{C}:\boldsymbol{\varepsilon}$, +# in the internal force vector, $f_i^\mathrm{int}$, yields the linear equation +# ```math +# \underbrace{\left[\int_\Omega \mathrm{grad}(\delta \boldsymbol{N}_i) : \mathsf{C} : \left[\mathrm{grad}(\boldsymbol{N}_j)\right]^\mathrm{sym}\ \mathrm{d}\Omega\right]}_{K_{ij}}\ \hat{u}_j = f_i^\mathrm{ext} +# ``` +# +# ## Implementation +# First we load Ferrite, and some other packages we need. +using Ferrite, FerriteGmsh, SparseArrays +# As in the heat equation tutorial, we will use a unit square - but here we'll load the grid of the Ferrite logo! +# This is done by downloading [`logo.geo`](logo.geo) and loading it using [`FerriteGmsh.jl`](https://github.com/Ferrite-FEM/FerriteGmsh.jl), +using Downloads: download +logo_mesh = "logo.geo" +asset_url = "https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/gh-pages/assets/" +isfile(logo_mesh) || download(string(asset_url, logo_mesh), logo_mesh) + +FerriteGmsh.Gmsh.initialize() #hide +FerriteGmsh.Gmsh.gmsh.option.set_number("General.Verbosity", 2) #hide +grid = togrid(logo_mesh); +FerriteGmsh.Gmsh.finalize(); #hide +#md nothing #hide +# The generated grid lacks the facetsets for the boundaries, so we add them by using Ferrite's +# [`addfacetset!`](@ref). It allows us to add facetsets to the grid based on coordinates. +# Note that approximate comparison to 0.0 doesn't work well, so we use a tolerance instead. +addfacetset!(grid, "top", x -> x[2] ≈ 1.0) # faces for which x[2] ≈ 1.0 for all nodes +addfacetset!(grid, "left", x -> abs(x[1]) < 1e-6) +addfacetset!(grid, "bottom", x -> abs(x[2]) < 1e-6); + +# ### Trial and test functions +# In this tutorial, we use the same linear Lagrange shape functions to approximate both the +# test and trial spaces, i.e. $\delta\boldsymbol{N}_i = \boldsymbol{N}_i$. +# As our grid is composed of triangular elements, we need the Lagrange functions defined +# on a `RefTriangle`. All currently available interpolations can be found under +# [`Interpolation`](@ref reference-interpolation). +# +# Here we use linear triangular elements (also called constant strain triangles). +# The vector valued shape functions are constructed by raising the interpolation +# to the power `dim` (the dimension) since the displacement field has one component in each +# spatial dimension. +dim = 2 +order = 1 # linear interpolation +ip = Lagrange{RefTriangle, order}()^dim; # vector valued interpolation + +# In order to evaluate the integrals, we need to specify the quadrature rules to use. Due to the +# linear interpolation, a single quadrature point suffices, both inside the cell and on the facet. +# In 2d, a facet is the edge of the element. +qr = QuadratureRule{RefTriangle}(1) # 1 quadrature point +qr_face = FacetQuadratureRule{RefTriangle}(1); + +# Finally, we collect the interpolations and quadrature rules into the `CellValues` and `FacetValues` +# buffers, which we will later use to evaluate the integrals over the cells and facets. +cellvalues = CellValues(qr, ip) +facetvalues = FacetValues(qr_face, ip); + +# ### Degrees of freedom +# For distributing degrees of freedom, we define a `DofHandler`. The `DofHandler` knows that +# `u` has two degrees of freedom per node because we vectorized the interpolation above. +dh = DofHandler(grid) +add!(dh, :u, ip) +close!(dh); + +# ### Boundary conditions +# We set Dirichlet boundary conditions by fixing the motion normal to the bottom and left +# boundaries. The last argument to `Dirichlet` determines which components of the field should be +# constrained. If no argument is given, all components are constrained by default. +ch = ConstraintHandler(dh) +add!(ch, Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> 0.0, 2)) +add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0.0, 1)) +close!(ch); + +# In addition, we will use Neumann boundary conditions on the top surface, where +# we add a traction vector of the form +# ```math +# \boldsymbol{t}_\mathrm{N}(\boldsymbol{x}) = (20e3) x_1 \boldsymbol{e}_2\ \mathrm{N}/\mathrm{mm}^2 +# ``` +traction(x) = Vec(0.0, 20e3 * x[1]); + +# On the right boundary, we don't do anything, resulting in a zero traction Neumann boundary. +# In order to assemble the external forces, $f_i^\mathrm{ext}$, we need to iterate over all +# facets in the relevant facetset. We do this by using the [`FacetIterator`](@ref). + +function assemble_external_forces!(f_ext, dh, facetset, facetvalues, prescribed_traction) + ## Create a temporary array for the facet's local contributions to the external force vector + fe_ext = zeros(getnbasefunctions(facetvalues)) + for face in FacetIterator(dh, facetset) + ## Update the facetvalues to the correct facet number + reinit!(facetvalues, face) + ## Reset the temporary array for the next facet + fill!(fe_ext, 0.0) + ## Access the cell's coordinates + cell_coordinates = getcoordinates(face) + for qp in 1:getnquadpoints(facetvalues) + ## Calculate the global coordinate of the quadrature point. + x = spatial_coordinate(facetvalues, qp, cell_coordinates) + tₚ = prescribed_traction(x) + ## Get the integration weight for the current quadrature point. + dΓ = getdetJdV(facetvalues, qp) + for i in 1:getnbasefunctions(facetvalues) + Nᵢ = shape_value(facetvalues, qp, i) + fe_ext[i] += tₚ ⋅ Nᵢ * dΓ + end + end + ## Add the local contributions to the correct indices in the global external force vector + assemble!(f_ext, celldofs(face), fe_ext) + end + return f_ext +end +#md nothing #hide + +# ### Material behavior +# Next, we need to define the material behavior, specifically the elastic stiffness tensor, $\mathsf{C}$. +# In this tutorial, we use plane strain linear isotropic elasticity, with Hooke's law as +# ```math +# \boldsymbol{\sigma} = 2G \boldsymbol{\varepsilon}^\mathrm{dev} + 3K \boldsymbol{\varepsilon}^\mathrm{vol} +# ``` +# where $G$ is the shear modulus and $K$ the bulk modulus. This expression can be written as +# $\boldsymbol{\sigma} = \mathsf{C}:\boldsymbol{\varepsilon}$, with +# ```math +# \mathsf{C} := \frac{\partial \boldsymbol{\sigma}}{\partial \boldsymbol{\varepsilon}} +# ``` +# The volumetric, $\boldsymbol{\varepsilon}^\mathrm{vol}$, +# and deviatoric, $\boldsymbol{\varepsilon}^\mathrm{dev}$ strains, are defined as +# ```math +# \begin{align*} +# \boldsymbol{\varepsilon}^\mathrm{vol} &= \frac{\mathrm{tr}(\boldsymbol{\varepsilon})}{3}\boldsymbol{I}, \quad +# \boldsymbol{\varepsilon}^\mathrm{dev} &= \boldsymbol{\varepsilon} - \boldsymbol{\varepsilon}^\mathrm{vol} +# \end{align*} +# ``` +# +# Starting from Young's modulus, $E$, and Poisson's ratio, $\nu$, the shear and bulk modulus are +# ```math +# G = \frac{E}{2(1 + \nu)}, \quad K = \frac{E}{3(1 - 2\nu)} +# ``` +Emod = 200e3 # Young's modulus [MPa] +ν = 0.3 # Poisson's ratio [-] + +Gmod = Emod / (2(1 + ν)) # Shear modulus +Kmod = Emod / (3(1 - 2ν)) # Bulk modulus + +# Finally, we demonstrate `Tensors.jl`'s automatic differentiation capabilities when +# calculating the elastic stiffness tensor +C = gradient(ϵ -> 2 * Gmod * dev(ϵ) + 3 * Kmod * vol(ϵ), zero(SymmetricTensor{2,2})); + +#md # !!! details "Plane stress instead of plane strain?" +#md # In order to change this tutorial to consider plane stress instead of plane strain, +#md # the elastic stiffness tensor should be changed to reflect this. The plane stress elasticity +#md # stiffness matrix in Voigt notation for engineering shear strains, is given as +#md # ```math +#md # \underline{\underline{\boldsymbol{E}}} = \frac{E}{1 - \nu^2}\begin{bmatrix} +#md # 1 & \nu & 0 \\ +#md # \nu & 1 & 0 \\ +#md # 0 & 0 & (1 - \nu)/2 +#md # \end{bmatrix} +#md # ``` +#md # This matrix can be converted into the 4th order elastic stiffness tensor as +#md # ```julia +#md # C_voigt = Emod * [1.0 ν 0.0; ν 1.0 0.0; 0.0 0.0 (1-ν)/2] / (1 - ν^2) +#md # C = fromvoigt(SymmetricTensor{4,2}, E_voigt) +#md # ``` +#md # +# ### Element routine +# To calculate the global stiffness matrix, $K_{ij}$, the element routine computes the +# local stiffness matrix `ke` for a single element and assembles it into the global matrix. +# `ke` is pre-allocated and reused for all elements. +# +# Note that the elastic stiffness tensor $\mathsf{C}$ is constant. +# Thus is needs to be computed and once and can then be used for all integration points. +function assemble_cell!(ke, cellvalues, C) + for q_point in 1:getnquadpoints(cellvalues) + ## Get the integration weight for the quadrature point + dΩ = getdetJdV(cellvalues, q_point) + for i in 1:getnbasefunctions(cellvalues) + ## Gradient of the test function + ∇Nᵢ = shape_gradient(cellvalues, q_point, i) + for j in 1:getnbasefunctions(cellvalues) + ## Symmetric gradient of the trial function + ∇ˢʸᵐNⱼ = shape_symmetric_gradient(cellvalues, q_point, j) + ke[i, j] += (∇Nᵢ ⊡ C ⊡ ∇ˢʸᵐNⱼ) * dΩ + end + end + end + return ke +end +#md nothing #hide +# +# ### Global assembly +# We define the function `assemble_global` to loop over the elements and do the global +# assembly. The function takes the preallocated sparse matrix `K`, our DofHandler `dh`, our +# `cellvalues` and the elastic stiffness tensor `C` as input arguments and computes the +# global stiffness matrix `K`. +function assemble_global!(K, dh, cellvalues, C) + ## Allocate the element stiffness matrix + n_basefuncs = getnbasefunctions(cellvalues) + ke = zeros(n_basefuncs, n_basefuncs) + ## Create an assembler + assembler = start_assemble(K) + ## Loop over all cells + for cell in CellIterator(dh) + ## Update the shape function gradients based on the cell coordinates + reinit!(cellvalues, cell) + ## Reset the element stiffness matrix + fill!(ke, 0.0) + ## Compute element contribution + assemble_cell!(ke, cellvalues, C) + ## Assemble ke into K + assemble!(assembler, celldofs(cell), ke) + end + return K +end +#md nothing #hide + +# ### Solution of the system +# The last step is to solve the system. First we allocate the global stiffness matrix `K` +# and assemble it. +K = allocate_matrix(dh) +assemble_global!(K, dh, cellvalues, C); +# Then we allocate and assemble the external force vector. +f_ext = zeros(ndofs(dh)) +assemble_external_forces!(f_ext, dh, getfacetset(grid, "top"), facetvalues, traction); + +# To account for the Dirichlet boundary conditions we use the `apply!` function. +# This modifies elements in `K` and `f`, such that we can get the +# correct solution vector `u` by using solving the linear equation system $K_{ij} \hat{u}_j = f^\mathrm{ext}_i$, +apply!(K, f_ext, ch) +u = K \ f_ext; + +# ### Postprocessing +# In this case, we want to analyze the displacements, as well as the stress field. +# We calculate the stress in each quadrature point, and then export it in two different +# ways: +# 1) Constant in each cell (matching the approximation of constant strains in each element). +# Note that a current limitation is that cell data for second order tensors must be exported +# component-wise (see issue #768) +# 2) Interpolated using the linear lagrange ansatz functions via the [`L2Projector`](@ref). + +function calculate_stresses(grid, dh, cv, u, C) + qp_stresses = [ + [zero(SymmetricTensor{2,2}) for _ in 1:getnquadpoints(cv)] + for _ in 1:getncells(grid)] + avg_cell_stresses = tuple((zeros(getncells(grid)) for _ in 1:3)...) + for cell in CellIterator(dh) + reinit!(cv, cell) + cell_stresses = qp_stresses[cellid(cell)] + for q_point in 1:getnquadpoints(cv) + ε = function_symmetric_gradient(cv, q_point, u, celldofs(cell)) + cell_stresses[q_point] = C ⊡ ε + end + σ_avg = sum(cell_stresses) / getnquadpoints(cv) + avg_cell_stresses[1][cellid(cell)] = σ_avg[1, 1] + avg_cell_stresses[2][cellid(cell)] = σ_avg[2, 2] + avg_cell_stresses[3][cellid(cell)] = σ_avg[1, 2] + end + return qp_stresses, avg_cell_stresses +end + +qp_stresses, avg_cell_stresses = calculate_stresses(grid, dh, cellvalues, u, C); + +# We now use the the L2Projector to project the stress-field onto the piecewise linear +# finite element space that we used to solve the problem. +proj = L2Projector(Lagrange{RefTriangle, 1}(), grid) +stress_field = project(proj, qp_stresses, qr); + +# Export also values for each color in the logo. #src +color_data = zeros(Int, getncells(grid)) #hide +colors = [ #hide + "1" => 1, "5" => 1, # purple #hide + "2" => 2, "3" => 2, # red #hide + "4" => 3, # blue #hide + "6" => 4 # green #hide + ] #hide +for (key, color) in colors #hide + for i in getcellset(grid, key) #hide + color_data[i] = color #hide + end #hide +end #hide +#md nothing #hide +# +# To visualize the result we export to a VTK-file. Specifically, an unstructured +# grid file, `.vtu`, is created, which can be viewed in e.g. +# [ParaView](https://www.paraview.org/). +VTKGridFile("linear_elasticity", dh) do vtk + write_solution(vtk, dh, u) + for (i, key) in enumerate(("11", "22", "12")) + write_cell_data(vtk, avg_cell_stresses[i], "sigma_" * key) + end + write_projection(vtk, proj, stress_field, "stress field") + Ferrite.write_cellset(vtk, grid) + write_cell_data(vtk, color_data, "colors") #hide +end + +# We used the displacement field to visualize the deformed logo in *Figure 1*, +# and in *Figure 2*, we demonstrate the difference between the interpolated stress +# field and the constant stress in each cell. + +# ![](linear_elasticity_stress.png) +# +# *Figure 2*: Vertical normal stresses (MPa) exported using the `L2Projector` (left) +# and constant stress in each cell (right). + +# The mesh produced by gmsh is not stable between different OS #src +# For linux, we therefore test carefully, and for other OS we provide #src +# a coarse test to indicate introduced errors before running CI. #src +using Test #hide +linux_result = 0.31742879147646924 #hide +@test abs(norm(u) - linux_result) < 0.01 #hide +Sys.islinux() && @test norm(u) ≈ linux_result #hide +nothing #hide + +#md # ## [Plain program](@id linear_elasticity-plain-program) +#md # +#md # Here follows a version of the program without any comments. +#md # The file is also available here: [`linear_elasticity.jl`](linear_elasticity.jl). +#md # +#md # ```julia +#md # @__CODE__ +#md # ``` From fee40c5549717ec7a6c4a3431d2add34f7693642 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Tue, 20 Aug 2024 10:05:09 +0200 Subject: [PATCH 20/50] Use https for Codecov links in README (#1048) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3377e83b05..85ad4649e9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![Build Status](https://github.com/Ferrite-FEM/Ferrite.jl/workflows/CI/badge.svg?event=push) -[![codecov.io](http://codecov.io/github/Ferrite-FEM/Ferrite.jl/coverage.svg?branch=master)](http://codecov.io/github/Ferrite-FEM/Ferrite.jl?branch=master) +[![codecov.io](https://codecov.io/github/Ferrite-FEM/Ferrite.jl/coverage.svg?branch=master)](https://codecov.io/github/Ferrite-FEM/Ferrite.jl?branch=master) A finite element toolbox written in Julia. From 241d181490511e67fcb5d723b67ecb1c22cbe363 Mon Sep 17 00:00:00 2001 From: Niel-77 <67886596+Niel-77@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:35:28 +0545 Subject: [PATCH 21/50] Fix a typo in plasticity example (#1052) --- docs/src/literate-tutorials/plasticity.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/literate-tutorials/plasticity.jl b/docs/src/literate-tutorials/plasticity.jl index b8240f32df..0971fa2b3c 100644 --- a/docs/src/literate-tutorials/plasticity.jl +++ b/docs/src/literate-tutorials/plasticity.jl @@ -24,7 +24,7 @@ # # To illustrate the use of the plasticity model, we setup and solve a FE-problem # consisting of a cantilever beam loaded at its free end. But first, we shortly -# describe the parts of the implementation deadling with the material modeling. +# describe the parts of the implementation dealing with the material modeling. # ## Material modeling # This section describes the `struct`s and methods used to implement the material From 19214c7530999365979c0f01765a452fc11e3d7c Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 16 Sep 2024 11:10:54 +0200 Subject: [PATCH 22/50] Set version to 1.0.0. (#1000) * Set version to 1.0.0. * Use FerriteGmsh version compatible with Ferrite v1 --- Project.toml | 2 +- docs/Manifest.toml | 125 +++++++++++++++++++++++---------------------- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/Project.toml b/Project.toml index af5e162e41..bd8f0b7d29 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Ferrite" uuid = "c061ca5d-56c9-439f-9c0e-210fe06d3992" -version = "0.3.14" +version = "1.0.0" [deps] EnumX = "4e289a0a-7415-4d19-859d-a7e5c4648b56" diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 080b04599b..0fc5f508d6 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -5,9 +5,9 @@ manifest_format = "2.0" project_hash = "2283fea1dfcfa0f2c82cdecbc5750875c26d6009" [[deps.ADTypes]] -git-tree-sha1 = "3a6511b6e54550bcbc986c560921a8cd7761fcd8" +git-tree-sha1 = "ae44a0c3d68a420d4ac0fa1f7e0c034ccecb6dc5" uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" -version = "1.5.1" +version = "1.5.2" weakdeps = ["ChainRulesCore", "EnzymeCore"] [deps.ADTypes.extensions] @@ -133,9 +133,9 @@ version = "0.1.9" [[deps.BitTwiddlingConvenienceFunctions]] deps = ["Static"] -git-tree-sha1 = "0c5f81f47bbbcf4aea7b2959135713459170798b" +git-tree-sha1 = "f21cfd4950cb9f0587d5067e69405ad2acd27b87" uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b" -version = "0.1.5" +version = "0.1.6" [[deps.BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] @@ -157,9 +157,9 @@ version = "1.0.8+1" [[deps.CPUSummary]] deps = ["CpuId", "IfElse", "PrecompileTools", "Static"] -git-tree-sha1 = "585a387a490f1c4bd88be67eea15b93da5e85db7" +git-tree-sha1 = "5a97e67919535d6841172016c9530fd69494e5ec" uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" -version = "0.2.5" +version = "0.2.6" [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -179,9 +179,9 @@ weakdeps = ["SparseArrays"] [[deps.CloseOpenIntervals]] deps = ["Static", "StaticArrayInterface"] -git-tree-sha1 = "70232f82ffaab9dc52585e0dd043b5e0c6b714f1" +git-tree-sha1 = "05ba0d07cd4fd8b7a39541e31a7b0254704ea581" uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9" -version = "0.1.12" +version = "0.1.13" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] @@ -228,6 +228,11 @@ git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7" uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" version = "0.3.0" +[[deps.CommonWorldInvalidations]] +git-tree-sha1 = "ae52d1c52048455e85a387fbee9be553ec2b68d0" +uuid = "f70d9fcc-98c5-4d4a-abd7-e4cdeebd8ca8" +version = "1.0.0" + [[deps.Compat]] deps = ["TOML", "UUIDs"] git-tree-sha1 = "b1c55339b7c6c350ee89f2c1604299660525b248" @@ -316,9 +321,9 @@ version = "1.9.1" [[deps.DiffEqBase]] deps = ["ArrayInterface", "ConcreteStructs", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces"] -git-tree-sha1 = "2c6b7bf16fd850c551a765e313e7522ba455cbfd" +git-tree-sha1 = "d1e8a4642e28b0945bde6e2e1ac569b9e0abd728" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.151.4" +version = "6.151.5" [deps.DiffEqBase.extensions] DiffEqBaseCUDAExt = "CUDA" @@ -360,9 +365,9 @@ version = "1.15.1" [[deps.DifferentiationInterface]] deps = ["ADTypes", "Compat", "DocStringExtensions", "FillArrays", "LinearAlgebra", "PackageExtensionCompat", "SparseArrays", "SparseMatrixColorings"] -git-tree-sha1 = "23c6df13ad8fcffde4b0596d798911d2e309fc2c" +git-tree-sha1 = "695217e97ee1ce0248f4a56c14af88ba33c585fd" uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" -version = "0.5.5" +version = "0.5.7" [deps.DifferentiationInterface.extensions] DifferentiationInterfaceChainRulesCoreExt = "ChainRulesCore" @@ -495,9 +500,9 @@ version = "1.3.8+0" [[deps.FastBroadcast]] deps = ["ArrayInterface", "LinearAlgebra", "Polyester", "Static", "StaticArrayInterface", "StrideArraysCore"] -git-tree-sha1 = "2be93e36303143c6fffd07e2222bbade35194d9e" +git-tree-sha1 = "bd19de6fe8a3b18888f35e79832f97544684caa7" uuid = "7034ab61-46d4-4ed7-9d0f-46aef9175898" -version = "0.3.3" +version = "0.3.4" [[deps.FastClosures]] git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" @@ -514,7 +519,7 @@ version = "2.0.4" deps = ["EnumX", "ForwardDiff", "LinearAlgebra", "NearestNeighbors", "OrderedCollections", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] path = ".." uuid = "c061ca5d-56c9-439f-9c0e-210fe06d3992" -version = "0.3.14" +version = "1.0.0" [deps.Ferrite.extensions] FerriteBlockArrays = "BlockArrays" @@ -526,9 +531,9 @@ version = "0.3.14" [[deps.FerriteGmsh]] deps = ["Ferrite", "Gmsh"] -git-tree-sha1 = "0b4c93ea344bdbf5788758a6f214fdc1c3176f2f" +git-tree-sha1 = "5f56dd2b58ca0f79e28d9bb76fb0e6183b0aca65" uuid = "4f95f4f8-b27c-4ae5-9a39-ea55e634e36b" -version = "1.1.1" +version = "1.2.0" [[deps.FerriteMeshParser]] deps = ["Ferrite"] @@ -739,15 +744,15 @@ version = "2.8.1+1" [[deps.HostCPUFeatures]] deps = ["BitTwiddlingConvenienceFunctions", "IfElse", "Libdl", "Static"] -git-tree-sha1 = "eb8fed28f4994600e29beef49744639d985a04b2" +git-tree-sha1 = "8e070b599339d622e9a081d17230d74a5c473293" uuid = "3e5b6fbb-0976-4d2c-9146-d79de83f2fb0" -version = "0.1.16" +version = "0.1.17" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "ca0f6bf568b4bfc807e7537f081c81e35ceca114" +git-tree-sha1 = "1d334207121865ac8c1c97eb7f42d0339e4635bf" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.10.0+0" +version = "2.11.0+0" [[deps.IOCapture]] deps = ["Logging", "Random"] @@ -900,9 +905,9 @@ version = "0.16.3" [[deps.LayoutPointers]] deps = ["ArrayInterface", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface"] -git-tree-sha1 = "62edfee3211981241b57ff1cedf4d74d79519277" +git-tree-sha1 = "a9eaadb366f5493a5654e843864c13d8b107548c" uuid = "10f19ff3-798f-405d-979b-55457f8fc047" -version = "0.1.15" +version = "0.1.17" [[deps.LazilyInitializedFields]] git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" @@ -1030,9 +1035,9 @@ version = "5.0.0+0" [[deps.LinearSolve]] deps = ["ArrayInterface", "ChainRulesCore", "ConcreteStructs", "DocStringExtensions", "EnumX", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "LazyArrays", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "7648cc20100504f4b453917aacc8520e9c0ecfb3" +git-tree-sha1 = "b2e2dba60642e07c062eb3143770d7e234316772" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "2.30.1" +version = "2.30.2" [deps.LinearSolve.extensions] LinearSolveBandedMatricesExt = "BandedMatrices" @@ -1104,9 +1109,9 @@ version = "1.0.3" [[deps.LoopVectorization]] deps = ["ArrayInterface", "CPUSummary", "CloseOpenIntervals", "DocStringExtensions", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "PrecompileTools", "SIMDTypes", "SLEEFPirates", "Static", "StaticArrayInterface", "ThreadingUtilities", "UnPack", "VectorizationBase"] -git-tree-sha1 = "8f6786d8b2b3248d79db3ad359ce95382d5a6df8" +git-tree-sha1 = "8084c25a250e00ae427a379a5b607e7aed96a2dd" uuid = "bdcacae8-1622-11e9-2a5c-532679323890" -version = "0.12.170" +version = "0.12.171" weakdeps = ["ChainRulesCore", "ForwardDiff", "SpecialFunctions"] [deps.LoopVectorization.extensions] @@ -1245,9 +1250,9 @@ version = "1.2.0" [[deps.NonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "FastBroadcast", "FastClosures", "FiniteDiff", "ForwardDiff", "LazyArrays", "LineSearches", "LinearAlgebra", "LinearSolve", "MaybeInplace", "PrecompileTools", "Preferences", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] -git-tree-sha1 = "40325dcea1cb84a108efe05966bbb1f4b98e5eea" +git-tree-sha1 = "3adb1e5945b5a6b1eaee754077f25ccc402edd7f" uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" -version = "3.13.0" +version = "3.13.1" [deps.NonlinearSolve.extensions] NonlinearSolveBandedMatricesExt = "BandedMatrices" @@ -1354,10 +1359,10 @@ uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" version = "1.6.3" [[deps.OrdinaryDiffEq]] -deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "IfElse", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "b4cde20f0e8c67fd35863794d5e548722f7bb71d" +deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "IfElse", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "6ef13f8b23af28ee2d98226653d8382ab79287ea" uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -version = "6.84.0" +version = "6.85.0" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] @@ -1432,15 +1437,15 @@ version = "1.40.4" [[deps.Polyester]] deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Requires", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] -git-tree-sha1 = "b3e2bae88cf07baf0a051fe09666b8ef97aefe93" +git-tree-sha1 = "9ff799e8fb8ed6717710feee3be3bc20645daa97" uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" -version = "0.7.14" +version = "0.7.15" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] -git-tree-sha1 = "240d7170f5ffdb285f9427b92333c3463bf65bf6" +git-tree-sha1 = "645bed98cd47f72f67316fd42fc47dee771aefcd" uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" -version = "0.2.1" +version = "0.2.2" [[deps.PositiveFactorizations]] deps = ["LinearAlgebra"] @@ -1590,9 +1595,9 @@ version = "0.1.0" [[deps.SLEEFPirates]] deps = ["IfElse", "Static", "VectorizationBase"] -git-tree-sha1 = "3aac6d68c5e57449f5b9b865c9ba50ac2970c4cf" +git-tree-sha1 = "456f610ca2fbd1c14f5fcf31c6bfadc55e7d66e0" uuid = "476501e8-09a2-5ece-8869-fb82de89a1fa" -version = "0.6.42" +version = "0.6.43" [[deps.SciMLBase]] deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] @@ -1627,9 +1632,9 @@ version = "0.3.8" [[deps.SciMLStructures]] deps = ["ArrayInterface"] -git-tree-sha1 = "6ab4beaf88dcdd2639bead916f2347f81dcacd0e" +git-tree-sha1 = "cfdd1200d150df1d3c055cc72ee6850742e982d7" uuid = "53ae85a6-f571-4167-b2af-e1d143709226" -version = "1.3.0" +version = "1.4.1" [[deps.Scratch]] deps = ["Dates"] @@ -1663,9 +1668,9 @@ version = "1.1.0" [[deps.SimpleNonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "DiffResults", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "MaybeInplace", "PrecompileTools", "Reexport", "SciMLBase", "Setfield", "StaticArraysCore"] -git-tree-sha1 = "913754ccbbc78720a4542b56a6bdfbab1c84c8f2" +git-tree-sha1 = "58b144f34e44252b2de0acb5a9dbbb7ea5cd75d7" uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "1.10.0" +version = "1.10.1" [deps.SimpleNonlinearSolve.extensions] SimpleNonlinearSolveChainRulesCoreExt = "ChainRulesCore" @@ -1747,16 +1752,16 @@ weakdeps = ["ChainRulesCore"] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" [[deps.Static]] -deps = ["IfElse"] -git-tree-sha1 = "d2fdac9ff3906e27f7a618d47b676941baa6c80c" +deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools"] +git-tree-sha1 = "0bbff21027dd8a107551847528127b62a35f7594" uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" -version = "0.8.10" +version = "1.1.0" [[deps.StaticArrayInterface]] deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "Requires", "SparseArrays", "Static", "SuiteSparse"] -git-tree-sha1 = "5d66818a39bb04bf328e92bc933ec5b4ee88e436" +git-tree-sha1 = "8963e5a083c837531298fc41599182a759a87a6d" uuid = "0d7ed370-da01-4f52-bd93-41d350b8b718" -version = "1.5.0" +version = "1.5.1" weakdeps = ["OffsetArrays", "StaticArrays"] [deps.StaticArrayInterface.extensions] @@ -1765,9 +1770,9 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "6e00379a24597be4ae1ee6b2d882e15392040132" +git-tree-sha1 = "20833c5b7f7edf0e5026f23db7f268e4f23ec577" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.5" +version = "1.9.6" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -1798,9 +1803,9 @@ version = "0.34.3" [[deps.StrideArraysCore]] deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] -git-tree-sha1 = "25349bf8f63aa36acbff5e3550a86e9f5b0ef682" +git-tree-sha1 = "f35f6ab602df8413a50c4a25ca14de821e8605fb" uuid = "7792a7ef-975c-4747-a70f-980b88e8d1da" -version = "0.5.6" +version = "0.5.7" [[deps.StringEncodings]] deps = ["Libiconv_jll"] @@ -1890,9 +1895,9 @@ weakdeps = ["Random", "Test"] [[deps.TriangularSolve]] deps = ["CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "LoopVectorization", "Polyester", "Static", "VectorizationBase"] -git-tree-sha1 = "66c68a20907800c0b7c04ff8a6164115e8747de2" +git-tree-sha1 = "be986ad9dac14888ba338c2554dcfec6939e1393" uuid = "d5829a12-d9aa-46ab-831f-fb7c9ab06edf" -version = "0.2.0" +version = "0.2.1" [[deps.Tricks]] git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f" @@ -1957,9 +1962,9 @@ version = "1.0.1" [[deps.VectorizationBase]] deps = ["ArrayInterface", "CPUSummary", "HostCPUFeatures", "IfElse", "LayoutPointers", "Libdl", "LinearAlgebra", "SIMDTypes", "Static", "StaticArrayInterface"] -git-tree-sha1 = "e863582a41c5731f51fd050563ae91eb33cf09be" +git-tree-sha1 = "e7f5b81c65eb858bed630fe006837b935518aca5" uuid = "3d5dd08c-fd9d-11e8-17fa-ed2836048c2f" -version = "0.21.68" +version = "0.21.70" [[deps.VertexSafeGraphs]] deps = ["Graphs"] @@ -1993,15 +1998,15 @@ version = "1.19.2" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "52ff2af32e591541550bd753c0da8b9bc92bb9d9" +git-tree-sha1 = "d9717ce3518dc68a99e6b96300813760d887a01d" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.12.7+0" +version = "2.13.1+0" [[deps.XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.34+0" +version = "1.1.41+0" [[deps.XZ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] From 31096cb09cfdd20465159f6442c68ee94a4987c2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 18 Sep 2024 13:58:27 +0200 Subject: [PATCH 23/50] use vtk_save instead of Base.close on pvd files in docs (#1055) --- .../literate-gallery/quasi_incompressible_hyperelasticity.jl | 2 +- docs/src/literate-tutorials/ns_vs_diffeq.jl | 2 +- docs/src/literate-tutorials/porous_media.jl | 2 +- docs/src/literate-tutorials/reactive_surface.jl | 2 +- docs/src/literate-tutorials/transient_heat_equation.jl | 2 +- docs/src/topics/export.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl index ef0a162b9f..60474f549b 100644 --- a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl +++ b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl @@ -339,7 +339,7 @@ function solve(interpolation_u, interpolation_p) pvd[t] = vtk end end; - close(pvd); + vtk_save(pvd); vol_def = calculate_volume_deformed_mesh(w, dh, cellvalues_u); print("Deformed volume is $vol_def") return vol_def; diff --git a/docs/src/literate-tutorials/ns_vs_diffeq.jl b/docs/src/literate-tutorials/ns_vs_diffeq.jl index 8d3455943b..b45f9c6e65 100644 --- a/docs/src/literate-tutorials/ns_vs_diffeq.jl +++ b/docs/src/literate-tutorials/ns_vs_diffeq.jl @@ -583,7 +583,7 @@ for (step, (u,t)) in enumerate(intervals(integrator)) pvd[t] = vtk end end -close(pvd); +vtk_save(pvd); using Test #hide diff --git a/docs/src/literate-tutorials/porous_media.jl b/docs/src/literate-tutorials/porous_media.jl index 126222c199..cb42fcda1c 100644 --- a/docs/src/literate-tutorials/porous_media.jl +++ b/docs/src/literate-tutorials/porous_media.jl @@ -354,7 +354,7 @@ function solve(dh, ch, domains; Δt=0.025, t_total=1.0) pvd[t] = vtk end end - close(pvd); + vtk_save(pvd); end; # Finally we call the functions to actually run the code diff --git a/docs/src/literate-tutorials/reactive_surface.jl b/docs/src/literate-tutorials/reactive_surface.jl index d1003d5bed..e0900e1b81 100644 --- a/docs/src/literate-tutorials/reactive_surface.jl +++ b/docs/src/literate-tutorials/reactive_surface.jl @@ -313,7 +313,7 @@ function gray_scott_on_sphere(material::GrayScottMaterial, Δt::Real, T::Real, r uₜ₋₁ .= uₜ end - close(pvd); + vtk_save(pvd); end ## This parametrization gives the spot pattern shown in the gif above. diff --git a/docs/src/literate-tutorials/transient_heat_equation.jl b/docs/src/literate-tutorials/transient_heat_equation.jl index c6a89de6f0..f37a619dda 100644 --- a/docs/src/literate-tutorials/transient_heat_equation.jl +++ b/docs/src/literate-tutorials/transient_heat_equation.jl @@ -217,7 +217,7 @@ for (step, t) in enumerate(Δt:Δt:T) uₙ .= u end # In order to use the .pvd file we need to store it to the disk, which is done by: -close(pvd); +vtk_save(pvd); #md # ## [Plain program](@id transient_heat_equation-plain-program) #md # diff --git a/docs/src/topics/export.md b/docs/src/topics/export.md index 472e6ecad9..f4de55417b 100644 --- a/docs/src/topics/export.md +++ b/docs/src/topics/export.md @@ -56,6 +56,6 @@ for (step, t) in enumerate(range(0, 1, 5)) pvd[t] = vtk end end -close(pvd); +vtk_save(pvd); ``` See [Transient heat equation](@ref tutorial-transient-heat-equation) for an example From f8486594131b91e59201c30a3e9a507ba29ffa0b Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 18 Sep 2024 14:04:20 +0200 Subject: [PATCH 24/50] Clean up doc remaints of old face definition (#1056) --- .../quasi_incompressible_hyperelasticity.jl | 2 +- .../literate-gallery/topology_optimization.jl | 12 +++++----- .../computational_homogenization.jl | 4 ++-- .../literate-tutorials/dg_heat_equation.jl | 18 +++++++------- docs/src/literate-tutorials/heat_equation.jl | 4 ++-- .../incompressible_elasticity.jl | 2 +- .../literate-tutorials/linear_elasticity.jl | 10 ++++---- docs/src/literate-tutorials/plasticity.jl | 4 ++-- docs/src/literate-tutorials/stokes-flow.jl | 21 +++++----------- docs/src/topics/boundary_conditions.md | 24 +++++++++---------- src/FEValues/common_values.jl | 10 ++++---- src/iterators.jl | 8 +++---- 12 files changed, 55 insertions(+), 64 deletions(-) diff --git a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl index 60474f549b..e024407d9d 100644 --- a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl +++ b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl @@ -141,7 +141,7 @@ function create_dofhandler(grid, ipu, ipp) end; # We are simulating a uniaxial tensile loading of a unit cube. Hence we apply a displacement field (`:u`) in `x` direction on the right face. -# The left, bottom and back faces are fixed in the `x`, `y` and `z` components of the displacement so as to emulate the uniaxial nature +# The left, bottom and back facets are fixed in the `x`, `y` and `z` components of the displacement so as to emulate the uniaxial nature # of the loading. function create_bc(dh) dbc = ConstraintHandler(dh) diff --git a/docs/src/literate-gallery/topology_optimization.jl b/docs/src/literate-gallery/topology_optimization.jl index 4445e4a90a..963909e6cf 100644 --- a/docs/src/literate-gallery/topology_optimization.jl +++ b/docs/src/literate-gallery/topology_optimization.jl @@ -67,7 +67,7 @@ # First we load all necessary packages. using Ferrite, SparseArrays, LinearAlgebra, Tensors, Printf # Next, we create a simple square grid of the size 2x1. We apply a fixed Dirichlet boundary condition -# to the left face set, called `clamped`. On the right face, we create a small set `traction`, where we +# to the left facet set, called `clamped`. On the right facet, we create a small set `traction`, where we # will later apply a force in negative y-direction. function create_grid(n) @@ -186,10 +186,10 @@ end #md nothing # hide # For the Laplacian we need some neighboorhood information which is constant throughout the analysis so we compute it once and cache it. -# We iterate through each face of each element, -# obtaining the neighboring element by using the `getneighborhood` function. For boundary faces, +# We iterate through each facet of each element, +# obtaining the neighboring element by using the `getneighborhood` function. For boundary facets, # the function call will return an empty object. In that case we use the dictionary to instead find the opposite -# face, as discussed in the introduction. +# facet, as discussed in the introduction. function cache_neighborhood(dh, topology) nbgs = Vector{Vector{Int}}(undef, getncells(dh.grid)) @@ -202,8 +202,8 @@ function cache_neighborhood(dh, topology) for j in 1:_nfacets nbg_cellid = getneighborhood(topology, dh.grid, FacetIndex(i,j)) if(!isempty(nbg_cellid)) - nbg[j] = first(nbg_cellid)[1] # assuming only one face neighbor per cell - else # boundary face + nbg[j] = first(nbg_cellid)[1] # assuming only one facet neighbor per cell + else # boundary facet nbg[j] = first(getneighborhood(topology, dh.grid, FacetIndex(i,opp[j])))[1] end end diff --git a/docs/src/literate-tutorials/computational_homogenization.jl b/docs/src/literate-tutorials/computational_homogenization.jl index cba4b5e66b..9df9e6d64f 100644 --- a/docs/src/literate-tutorials/computational_homogenization.jl +++ b/docs/src/literate-tutorials/computational_homogenization.jl @@ -241,8 +241,8 @@ close!(ch_dirichlet) update!(ch_dirichlet, 0.0) # For periodic boundary conditions we use the [`PeriodicDirichlet`](@ref) constraint type, -# which is very similar to the `Dirichlet` type, but instead of a passing a faceset we pass -# a vector with "face pairs", i.e. the mapping between mirror and image parts of the +# which is very similar to the `Dirichlet` type, but instead of a passing a facetset we pass +# a vector with "facet pairs", i.e. the mapping between mirror and image parts of the # boundary. In this example the `"left"` and `"bottom"` boundaries are mirrors, and the # `"right"` and `"top"` boundaries are the mirrors. diff --git a/docs/src/literate-tutorials/dg_heat_equation.jl b/docs/src/literate-tutorials/dg_heat_equation.jl index 8ee2cc7429..d0ba910868 100644 --- a/docs/src/literate-tutorials/dg_heat_equation.jl +++ b/docs/src/literate-tutorials/dg_heat_equation.jl @@ -177,7 +177,7 @@ add!(ch, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 1.0)) add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> -1.0)) close!(ch); -# Furthermore, we define $\partial \Omega_N$ as the `union` of the face sets with Neumann boundary conditions for later use +# Furthermore, we define $\partial \Omega_N$ as the `union` of the facet sets with Neumann boundary conditions for later use ∂Ωₙ = union( getfacetset(grid, "top"), getfacetset(grid, "bottom"), @@ -189,7 +189,7 @@ close!(ch); # Now we have all the pieces needed to assemble the linear system, $K u = f$. Assembling of # the global system is done by looping over i) all the elements in order to compute the # element contributions ``K_e`` and ``f_e``, ii) all the interfaces to compute their -# contributions ``K_i``, and iii) all the Neumann boundary faces to compute their +# contributions ``K_i``, and iii) all the Neumann boundary facets to compute their # contributions ``f_e``. All these local contributions are then assembled into the # appropriate place in the global ``K`` and ``f``. # @@ -200,7 +200,7 @@ close!(ch); # * `assemble_interface!` to compute the contribution ``K_i`` of surface integrals over an # interface using `interfacevalues`. # * `assemble_boundary!` to compute the contribution ``f_e`` of surface integrals over a -# boundary face using `FacetValues`. +# boundary facet using `FacetValues`. function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues) n_basefuncs = getnbasefunctions(cellvalues) @@ -233,7 +233,7 @@ function assemble_interface!(Ki::Matrix, iv::InterfaceValues, μ::Float64) fill!(Ki, 0) ## Loop over quadrature points for q_point in 1:getnquadpoints(iv) - ## Get the normal to face A + ## Get the normal to facet A normal = getnormal(iv, q_point) ## Get the quadrature weight dΓ = getdetJdV(iv, q_point) @@ -260,7 +260,7 @@ function assemble_boundary!(fe::Vector, fv::FacetValues) fill!(fe, 0) ## Loop over quadrature points for q_point in 1:getnquadpoints(fv) - ## Get the normal to face A + ## Get the normal to facet A normal = getnormal(fv, q_point) ## Get the quadrature weight ∂Ω = getdetJdV(fv, q_point) @@ -277,8 +277,8 @@ end # #### Global assembly # -# We define the function `assemble_global` to loop over all elements and internal faces -# (interfaces), as well as the external faces involved in Neumann boundary conditions. +# We define the function `assemble_global` to loop over all elements and internal facets +# (interfaces), as well as the external facets involved in Neumann boundary conditions. function assemble_global(cellvalues::CellValues, facetvalues::FacetValues, interfacevalues::InterfaceValues, K::SparseMatrixCSC, dh::DofHandler, order::Int, dim::Int) ## Allocate the element stiffness matrix and element force vector @@ -315,9 +315,9 @@ function assemble_global(cellvalues::CellValues, facetvalues::FacetValues, inter end ## Loop over domain boundaries with Neumann boundary conditions for fc in FacetIterator(dh, ∂Ωₙ) - ## Reinitialize face_values_a for this boundary face + ## Reinitialize facetvalues for this boundary facet reinit!(facetvalues, fc) - ## Compute boundary face surface integrals contribution + ## Compute boundary facet surface integrals contribution assemble_boundary!(fe, facetvalues) ## Assemble fe into f assemble!(f, celldofs(fc), fe) diff --git a/docs/src/literate-tutorials/heat_equation.jl b/docs/src/literate-tutorials/heat_equation.jl index e98fe27c81..21c2963a75 100644 --- a/docs/src/literate-tutorials/heat_equation.jl +++ b/docs/src/literate-tutorials/heat_equation.jl @@ -82,7 +82,7 @@ ch = ConstraintHandler(dh); # Next we need to add constraints to `ch`. For this problem we define # homogeneous Dirichlet boundary conditions on the whole boundary, i.e. -# the `union` of all the face sets on the boundary. +# the `union` of all the facet sets on the boundary. ∂Ω = union( getfacetset(grid, "left"), getfacetset(grid, "right"), @@ -91,7 +91,7 @@ ch = ConstraintHandler(dh); ); # Now we are set up to define our constraint. We specify which field -# the condition is for, and our combined face set `∂Ω`. The last +# the condition is for, and our combined facet set `∂Ω`. The last # argument is a function of the form $f(\textbf{x})$ or $f(\textbf{x}, t)$, # where $\textbf{x}$ is the spatial coordinate and # $t$ the current time, and returns the prescribed value. Since the boundary condition in diff --git a/docs/src/literate-tutorials/incompressible_elasticity.jl b/docs/src/literate-tutorials/incompressible_elasticity.jl index 33d64023c7..9f4da7d532 100644 --- a/docs/src/literate-tutorials/incompressible_elasticity.jl +++ b/docs/src/literate-tutorials/incompressible_elasticity.jl @@ -65,7 +65,7 @@ function create_dofhandler(grid, ipu, ipp) return dh end; -# We also need to add Dirichlet boundary conditions on the `"clamped"` faceset. +# We also need to add Dirichlet boundary conditions on the `"clamped"` facetset. # We specify a homogeneous Dirichlet bc on the displacement field, `:u`. function create_bc(dh) dbc = ConstraintHandler(dh) diff --git a/docs/src/literate-tutorials/linear_elasticity.jl b/docs/src/literate-tutorials/linear_elasticity.jl index 09e778f181..156a4b39ce 100644 --- a/docs/src/literate-tutorials/linear_elasticity.jl +++ b/docs/src/literate-tutorials/linear_elasticity.jl @@ -113,7 +113,7 @@ FerriteGmsh.Gmsh.finalize(); #hide # The generated grid lacks the facetsets for the boundaries, so we add them by using Ferrite's # [`addfacetset!`](@ref). It allows us to add facetsets to the grid based on coordinates. # Note that approximate comparison to 0.0 doesn't work well, so we use a tolerance instead. -addfacetset!(grid, "top", x -> x[2] ≈ 1.0) # faces for which x[2] ≈ 1.0 for all nodes +addfacetset!(grid, "top", x -> x[2] ≈ 1.0) # facets for which x[2] ≈ 1.0 for all nodes addfacetset!(grid, "left", x -> abs(x[1]) < 1e-6) addfacetset!(grid, "bottom", x -> abs(x[2]) < 1e-6); @@ -173,13 +173,13 @@ traction(x) = Vec(0.0, 20e3 * x[1]); function assemble_external_forces!(f_ext, dh, facetset, facetvalues, prescribed_traction) ## Create a temporary array for the facet's local contributions to the external force vector fe_ext = zeros(getnbasefunctions(facetvalues)) - for face in FacetIterator(dh, facetset) + for facet in FacetIterator(dh, facetset) ## Update the facetvalues to the correct facet number - reinit!(facetvalues, face) + reinit!(facetvalues, facet) ## Reset the temporary array for the next facet fill!(fe_ext, 0.0) ## Access the cell's coordinates - cell_coordinates = getcoordinates(face) + cell_coordinates = getcoordinates(facet) for qp in 1:getnquadpoints(facetvalues) ## Calculate the global coordinate of the quadrature point. x = spatial_coordinate(facetvalues, qp, cell_coordinates) @@ -192,7 +192,7 @@ function assemble_external_forces!(f_ext, dh, facetset, facetvalues, prescribed_ end end ## Add the local contributions to the correct indices in the global external force vector - assemble!(f_ext, celldofs(face), fe_ext) + assemble!(f_ext, celldofs(facet), fe_ext) end return f_ext end diff --git a/docs/src/literate-tutorials/plasticity.jl b/docs/src/literate-tutorials/plasticity.jl index 0971fa2b3c..af0d51823a 100644 --- a/docs/src/literate-tutorials/plasticity.jl +++ b/docs/src/literate-tutorials/plasticity.jl @@ -229,10 +229,10 @@ function symmetrize_lower!(K) end end; -function doassemble_neumann!(r, dh, faceset, facetvalues, t) +function doassemble_neumann!(r, dh, facetset, facetvalues, t) n_basefuncs = getnbasefunctions(facetvalues) re = zeros(n_basefuncs) # element residual vector - for fc in FacetIterator(dh, faceset) + for fc in FacetIterator(dh, facetset) ## Add traction as a negative contribution to the element residual `re`: reinit!(facetvalues, fc) fill!(re, 0) diff --git a/docs/src/literate-tutorials/stokes-flow.jl b/docs/src/literate-tutorials/stokes-flow.jl index ac161f89bd..70c5da2e16 100644 --- a/docs/src/literate-tutorials/stokes-flow.jl +++ b/docs/src/literate-tutorials/stokes-flow.jl @@ -211,15 +211,6 @@ function setup_grid(h=0.05) ## Finalize the Gmsh library Gmsh.finalize() - ## Temp fix for FerriteGmsh - ## for setname in ["Γ1", "Γ2", "Γ3", "Γ4"] - ## faceset = grid.facesets[setname] - ## edgeset = Set([EdgeIndex(f[1], f[2]) for f in faceset]) - ## grid.edgesets[setname] = edgeset - ## delete!(grid.facesets, setname) - ## end - ## =# - return grid end #md nothing #hide @@ -232,7 +223,7 @@ end # velocity and `ipp` for the pressure. Note that we specify linear geometric mapping # (`ipg`) for both the velocity and pressure because our grid contains linear # triangles. However, since linear mapping is default this could have been skipped. -# We also construct face-values for the pressure since we need to integrate along +# We also construct facet-values for the pressure since we need to integrate along # the boundary when assembling the constraint matrix ``\underline{\underline{C}}``. function setup_fevalues(ipu, ipp, ipg) @@ -267,7 +258,7 @@ end # Let's first discuss the assembly of the constraint matrix ``\underline{\underline{C}}`` # and how to create an `AffineConstraint` from it. This is done in the # `setup_mean_constraint` function below. Assembling this is not so different from standard -# assembly in Ferrite: we loop over all the faces, loop over the quadrature points, and loop +# assembly in Ferrite: we loop over all the facets, loop over the quadrature points, and loop # over the shape functions. Note that since there is only one constraint the matrix will # only have one row. # After assembling `C` we construct an `AffineConstraint` from it. We select the constrained @@ -339,14 +330,14 @@ end # We now setup all the boundary conditions in the `setup_constraints` function below. # Since the periodicity constraint for this example is between two boundaries which are not -# parallel to each other we need to i) compute the mapping between each mirror face and the -# corresponding image face (on the element level) and ii) describe the dof relation between -# dofs on these two faces. In Ferrite this is done by defining a transformation of entities +# parallel to each other we need to i) compute the mapping between each mirror facet and the +# corresponding image facet (on the element level) and ii) describe the dof relation between +# dofs on these two facets. In Ferrite this is done by defining a transformation of entities # on the image boundary such that they line up with the matching entities on the mirror # boundary. In this example we consider the inlet ``\Gamma_1`` to be the image, and the # outlet ``\Gamma_3`` to be the mirror. The necessary transformation to apply then becomes a # rotation of ``\pi/2`` radians around the out-of-plane axis. We set up the rotation matrix -# `R`, and then compute the mapping between mirror and image faces using +# `R`, and then compute the mapping between mirror and image facets using # [`collect_periodic_facets`](@ref) where the rotation is applied to the coordinates. In the # next step we construct the constraint using the [`PeriodicDirichlet`](@ref) constructor. # We pass the constructor the computed mapping, and also the rotation matrix. This matrix is diff --git a/docs/src/topics/boundary_conditions.md b/docs/src/topics/boundary_conditions.md index 242bb44217..1bf96092ba 100644 --- a/docs/src/topics/boundary_conditions.md +++ b/docs/src/topics/boundary_conditions.md @@ -37,12 +37,12 @@ dbc1 = Dirichlet( ``` The field name is given as a symbol, just like when the field was added to the dof handler, -the part of the boundary where this constraint is active is given as a face set, and the +the part of the boundary where this constraint is active is given as a facet set, and the function computing the prescribed value should be of the form `f(x)` or `f(x, t)` (coordinate `x` and time `t`) and return the prescribed value(s). !!! note "Multiple sets" - To apply a constraint on multiple face sets in the grid you can use `union` to join + To apply a constraint on multiple facet sets in the grid you can use `union` to join them, for example ```julia left_right = union(getfacetset(grid, "left"), getfacetset(grid, "right")) @@ -156,12 +156,12 @@ for facet in 1:nfacets(cell) end ``` -We start by looping over all the faces of the cell, next we check if this particular face is -located on our faceset of interest called `"Neumann Boundary"`. If we have determined -that the current face is indeed on the boundary and in our faceset, then we -reinitialize `FacetValues` for this face, using [`reinit!`](@ref). When `reinit!`ing -`FacetValues` we also need to give the face number in addition to the cell. -Next we simply loop over the quadrature points of the face, and then loop over +We start by looping over all the facets of the cell, next we check if this particular facet is +located on our facetset of interest called `"Neumann Boundary"`. If we have determined +that the current facet is indeed on the boundary and in our facetset, then we +reinitialize `FacetValues` for this facet, using [`reinit!`](@ref). When `reinit!`ing +`FacetValues` we also need to give the facet number in addition to the cell. +Next we simply loop over the quadrature points of the facet, and then loop over all the test functions and assemble the contribution to the force vector. @@ -209,7 +209,7 @@ simply a translation (e.g. sides of a cube) this matrix will be the identity mat In `Ferrite` this type of periodic Dirichlet boundary conditions can be added to the `ConstraintHandler` by constructing an instance of [`PeriodicDirichlet`](@ref). This is -usually done it two steps. First we compute the mapping between mirror and image faces using +usually done it two steps. First we compute the mapping between mirror and image facets using [`collect_periodic_facets`](@ref). Here we specify the mirror set and image sets (the sets are usually known or can be constructed easily ) and the mapping ``\varphi``. Second we construct the constraint using the `PeriodicDirichlet` constructor. Here we specify which @@ -226,7 +226,7 @@ in the ``x``-direction (as seen by the mapping `φ`): # Create a constraint handler from the dof handler ch = ConstraintHandler(dofhandler) -# Compute the face mapping +# Compute the facet mapping φ(x) = x - Vec{2}((1.0, 0.0)) face_mapping = collect_periodic_facets(grid, "left", "right", φ) @@ -242,8 +242,8 @@ close!(ch) !!! note `PeriodicDirichlet` constraints are imposed in a strong sense, so note that this - requires a periodic mesh such that it is possible to compute the face mapping between - faces on the mirror and boundary. + requires a periodic mesh such that it is possible to compute the facet mapping between + facets on the mirror and boundary. !!! note "Examples" Periodic boundary conditions are used in the following examples [Computational diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index f8e744f975..3b6ae37cc7 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -47,10 +47,10 @@ end """ reinit!(cv::CellValues, cell::AbstractCell, x::AbstractVector) reinit!(cv::CellValues, x::AbstractVector) - reinit!(fv::FacetValues, cell::AbstractCell, x::AbstractVector, face::Int) - reinit!(fv::FacetValues, x::AbstractVector, face::Int) + reinit!(fv::FacetValues, cell::AbstractCell, x::AbstractVector, facet::Int) + reinit!(fv::FacetValues, x::AbstractVector, function_gradient::Int) -Update the `CellValues`/`FacetValues` object for a cell or face with coordinates `x`. +Update the `CellValues`/`FacetValues` object for a cell or facet with cell coordinates `x`. The derivatives of the shape functions, and the new integration weights are computed. For interpolations with non-identity mappings, the current `cell` is also required. """ @@ -60,7 +60,7 @@ reinit! getnquadpoints(fe_v::AbstractValues) Return the number of quadrature points. For `FacetValues`, -this is the number for the current face. +this is the number for the current facet. """ function getnquadpoints end @@ -71,7 +71,7 @@ Return the product between the determinant of the Jacobian and the quadrature point weight for the given quadrature point: ``\\det(J(\\mathbf{x})) w_q``. This value is typically used when one integrates a function on a -finite element cell or face as +finite element cell or facet as ``\\int\\limits_\\Omega f(\\mathbf{x}) d \\Omega \\approx \\sum\\limits_{q = 1}^{n_q} f(\\mathbf{x}_q) \\det(J(\\mathbf{x})) w_q`` ``\\int\\limits_\\Gamma f(\\mathbf{x}) d \\Gamma \\approx \\sum\\limits_{q = 1}^{n_q} f(\\mathbf{x}_q) \\det(J(\\mathbf{x})) w_q`` diff --git a/src/iterators.jl b/src/iterators.jl index b37721f4b3..190a1ed955 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -284,22 +284,22 @@ FaceIterator(args...) = error("FaceIterator is deprecated, use FacetIterator ins # Leaving flags undocumented as for CellIterator """ - FacetIterator(gridordh::Union{Grid,AbstractDofHandler}, faceset::AbstractVecOrSet{FacetIndex}) + FacetIterator(gridordh::Union{Grid,AbstractDofHandler}, facetset::AbstractVecOrSet{FacetIndex}) -Create a `FacetIterator` to conveniently iterate over the faces in `faceset`. The elements of +Create a `FacetIterator` to conveniently iterate over the faces in `facestet`. The elements of the iterator are [`FacetCache`](@ref)s which are properly `reinit!`ialized. See [`FacetCache`](@ref) for more details. Looping over a `FacetIterator`, i.e.: ```julia -for fc in FacetIterator(grid, faceset) +for fc in FacetIterator(grid, facetset) # ... end ``` is thus simply convenience for the following equivalent snippet: ```julia fc = FacetCache(grid) -for faceindex in faceset +for faceindex in facetset reinit!(fc, faceindex) # ... end From ee9d61c7fbb886fa141c44e90d96fc78aef474af Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Thu, 19 Sep 2024 15:26:39 +0200 Subject: [PATCH 25/50] Generalize CSC assembler (#916) --- docs/src/devdocs/assembly.md | 18 +++ docs/src/devdocs/index.md | 2 +- docs/src/literate-howto/threaded_assembly.jl | 4 +- ext/FerriteBlockArrays.jl | 2 +- src/Ferrite.jl | 3 +- src/assembler.jl | 160 ++++++++++++------- 6 files changed, 125 insertions(+), 64 deletions(-) create mode 100644 docs/src/devdocs/assembly.md diff --git a/docs/src/devdocs/assembly.md b/docs/src/devdocs/assembly.md new file mode 100644 index 0000000000..a4660994a1 --- /dev/null +++ b/docs/src/devdocs/assembly.md @@ -0,0 +1,18 @@ +# [Assembly](@id devdocs-assembly) + +## Type definitions + +```@docs +Ferrite.COOAssembler +Ferrite.CSCAssembler +Ferrite.SymmetricCSCAssembler +``` + +## Utility functions + +```@docs +Ferrite.matrix_handle +Ferrite.vector_handle +Ferrite._sortdofs_for_assembly! +Ferrite.sortperm2! +``` diff --git a/docs/src/devdocs/index.md b/docs/src/devdocs/index.md index bfd680dcc8..1d43bbc2b9 100644 --- a/docs/src/devdocs/index.md +++ b/docs/src/devdocs/index.md @@ -5,5 +5,5 @@ developing the library. ```@contents Depth = 1 -Pages = ["reference_cells.md", "interpolations.md", "elements.md", "FEValues.md", "dofhandler.md", "performance.md", "special_datastructures.md"] +Pages = ["reference_cells.md", "interpolations.md", "elements.md", "FEValues.md", "dofhandler.md", "assembly.md", "performance.md", "special_datastructures.md"] ``` diff --git a/docs/src/literate-howto/threaded_assembly.jl b/docs/src/literate-howto/threaded_assembly.jl index f916938672..4e0e736463 100644 --- a/docs/src/literate-howto/threaded_assembly.jl +++ b/docs/src/literate-howto/threaded_assembly.jl @@ -73,7 +73,7 @@ end; # # ScratchValues is a thread-local collection of data that each thread needs to own, # since we need to be able to mutate the data in the threads independently -struct ScratchValues{T, CV <: CellValues, FV <: FacetValues, TT <: AbstractTensor, dim, Ti} +struct ScratchValues{T, CV <: CellValues, FV <: FacetValues, TT <: AbstractTensor, dim, AT} Ke::Matrix{T} fe::Vector{T} cellvalues::CV @@ -81,7 +81,7 @@ struct ScratchValues{T, CV <: CellValues, FV <: FacetValues, TT <: AbstractTenso global_dofs::Vector{Int} ɛ::Vector{TT} coordinates::Vector{Vec{dim, T}} - assembler::Ferrite.AssemblerSparsityPattern{T, Ti} + assembler::AT end; # Each thread need its own CellValues and FacetValues (although, for this example we don't use diff --git a/ext/FerriteBlockArrays.jl b/ext/FerriteBlockArrays.jl index b7bdb9ef7c..8f4b73fbde 100644 --- a/ext/FerriteBlockArrays.jl +++ b/ext/FerriteBlockArrays.jl @@ -61,7 +61,7 @@ end ## BlockAssembler and associated methods ## ########################################### -struct BlockAssembler{BM, Bv} <: Ferrite.AbstractSparseAssembler +struct BlockAssembler{BM, Bv} <: Ferrite.AbstractAssembler K::BM f::Bv blockindices::Vector{BlockIndex{1}} diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 781de4d6f0..2738904d83 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -14,7 +14,8 @@ using NearestNeighbors: using OrderedCollections: OrderedSet using SparseArrays: - SparseArrays, SparseMatrixCSC, nonzeros, nzrange, rowvals, sparse + SparseArrays, SparseMatrixCSC, nonzeros, nzrange, rowvals, sparse, + AbstractSparseMatrixCSC using StaticArrays: StaticArrays, MArray, MMatrix, SArray, SMatrix, SVector using WriteVTK: diff --git a/src/assembler.jl b/src/assembler.jl index b86ffa8899..22972803a6 100644 --- a/src/assembler.jl +++ b/src/assembler.jl @@ -1,12 +1,19 @@ -struct Assembler{T} +abstract type AbstractAssembler end +abstract type AbstractCSCAssembler <: AbstractAssembler end + +""" +This assembler creates a COO (**coo**rdinate format) representation of a sparse matrix during +assembly and converts it into a `SparseMatrixCSC{T}` on finalization. +""" +struct COOAssembler{T} # <: AbstractAssembler I::Vector{Int} J::Vector{Int} V::Vector{T} end -Assembler(N) = Assembler(Float64, N) +COOAssembler(N) = COOAssembler(Float64, N) -function Assembler(::Type{T}, N) where T +function COOAssembler(::Type{T}, N) where T I = Int[] J = Int[] V = T[] @@ -14,11 +21,11 @@ function Assembler(::Type{T}, N) where T sizehint!(J, N) sizehint!(V, N) - Assembler(I, J, V) + COOAssembler(I, J, V) end """ - start_assemble([N=0]) -> Assembler + start_assemble([N=0]) -> COOAssembler Create an `Assembler` object which can be used to assemble element contributions to the global sparse matrix. Use [`assemble!`](@ref) for each element, and [`finish_assemble`](@ref), @@ -33,24 +40,28 @@ as described in the [manual](@ref man-assembly). the same pattern. See the [manual section](@ref man-assembly) on assembly. """ function start_assemble(N::Int=0) - return Assembler(N) + return start_assemble(Float64, N) +end + +function start_assemble(t::Type{T}, N::Int=0) where T + return COOAssembler(t, N) end """ - assemble!(a::Assembler, dofs, Ke) + assemble!(a::COOAssembler, dofs, Ke) Assembles the element matrix `Ke` into `a`. """ -function assemble!(a::Assembler{T}, dofs::AbstractVector{Int}, Ke::AbstractMatrix{T}) where {T} +function assemble!(a::COOAssembler{T}, dofs::AbstractVector{Int}, Ke::AbstractMatrix{T}) where {T} assemble!(a, dofs, dofs, Ke) end """ - assemble!(a::Assembler, rowdofs, coldofs, Ke) + assemble!(a::COOAssembler, rowdofs, coldofs, Ke) Assembles the matrix `Ke` into `a` according to the dofs specified by `rowdofs` and `coldofs`. """ -function assemble!(a::Assembler{T}, rowdofs::AbstractVector{Int}, coldofs::AbstractVector{Int}, Ke::AbstractMatrix{T}) where {T} +function assemble!(a::COOAssembler{T}, rowdofs::AbstractVector{Int}, coldofs::AbstractVector{Int}, Ke::AbstractMatrix{T}) where {T} nrows = length(rowdofs) ncols = length(coldofs) @@ -70,9 +81,9 @@ end finish_assemble(a::Assembler) -> K Finalizes an assembly. Returns a sparse matrix with the -assembled values. Note that this step is not necessary for `AbstractSparseAssembler`s. +assembled values. Note that this step is not necessary for `AbstractAssembler`s. """ -function finish_assemble(a::Assembler) +function finish_assemble(a::COOAssembler) return sparse(a.I, a.J, a.V) end @@ -89,74 +100,85 @@ Assembles the element residual `ge` into the global residual vector `g`. end end -abstract type AbstractSparseAssembler end - """ - matrix_handle(a::AbstractSparseAssembler) - vector_handle(a::AbstractSparseAssembler) + matrix_handle(a::AbstractAssembler) + vector_handle(a::AbstractAssembler) -Return a reference to the underlying matrix/vector of the assembler. +Return a reference to the underlying matrix/vector of the assembler used during +assembly operations. """ matrix_handle, vector_handle -struct AssemblerSparsityPattern{Tv,Ti} <: AbstractSparseAssembler - K::SparseMatrixCSC{Tv,Ti} +""" +Assembler for sparse matrix with CSC storage type. +""" +struct CSCAssembler{Tv,Ti,MT<:AbstractSparseMatrixCSC{Tv,Ti}} <: AbstractCSCAssembler + K::MT f::Vector{Tv} permutation::Vector{Int} sorteddofs::Vector{Int} end -struct AssemblerSymmetricSparsityPattern{Tv,Ti} <: AbstractSparseAssembler - K::Symmetric{Tv,SparseMatrixCSC{Tv,Ti}} + +""" +Assembler for symmetric sparse matrix with CSC storage type. +""" +struct SymmetricCSCAssembler{Tv,Ti, MT <: Symmetric{Tv,<:AbstractSparseMatrixCSC{Tv,Ti}}} <: AbstractCSCAssembler + K::MT f::Vector{Tv} permutation::Vector{Int} sorteddofs::Vector{Int} end -function Base.show(io::IO, ::MIME"text/plain", a::Union{AssemblerSparsityPattern,AssemblerSymmetricSparsityPattern}) +function Base.show(io::IO, ::MIME"text/plain", a::Union{CSCAssembler, SymmetricCSCAssembler}) print(io, typeof(a), " for assembling into:\n - ") summary(io, a.K) - if !isempty(a.f) + f = a.f + if !isempty(f) print(io, "\n - ") - summary(io, a.f) + summary(io, f) end end -matrix_handle(a::AssemblerSparsityPattern) = a.K -matrix_handle(a::AssemblerSymmetricSparsityPattern) = a.K.data -vector_handle(a::Union{AssemblerSparsityPattern, AssemblerSymmetricSparsityPattern}) = a.f +matrix_handle(a::AbstractCSCAssembler) = a.K +matrix_handle(a::SymmetricCSCAssembler) = a.K.data +vector_handle(a::AbstractCSCAssembler) = a.f """ - start_assemble(K::SparseMatrixCSC; fillzero::Bool=true) -> AssemblerSparsityPattern - start_assemble(K::SparseMatrixCSC, f::Vector; fillzero::Bool=true) -> AssemblerSparsityPattern + start_assemble(K::AbstractSparseMatrixCSC; fillzero::Bool=true) -> CSCAssembler + start_assemble(K::AbstractSparseMatrixCSC, f::Vector; fillzero::Bool=true) -> CSCAssembler -Create a `AssemblerSparsityPattern` from the matrix `K` and optional vector `f`. +Create a `CSCAssembler` from the matrix `K` and optional vector `f`. - start_assemble(K::Symmetric{SparseMatrixCSC}; fillzero::Bool=true) -> AssemblerSymmetricSparsityPattern - start_assemble(K::Symmetric{SparseMatrixCSC}, f::Vector=Td[]; fillzero::Bool=true) -> AssemblerSymmetricSparsityPattern + start_assemble(K::Symmetric{AbstractSparseMatrixCSC}; fillzero::Bool=true) -> SymmetricCSCAssembler + start_assemble(K::Symmetric{AbstractSparseMatrixCSC}, f::Vector=Td[]; fillzero::Bool=true) -> SymmetricCSCAssembler -Create a `AssemblerSymmetricSparsityPattern` from the matrix `K` and optional vector `f`. +Create a `SymmetricCSCAssembler` from the matrix `K` and optional vector `f`. -`AssemblerSparsityPattern` and `AssemblerSymmetricSparsityPattern` allocate workspace +`CSCAssembler` and `SymmetricCSCAssembler` allocate workspace necessary for efficient matrix assembly. To assemble the contribution from an element, use [`assemble!`](@ref). The keyword argument `fillzero` can be set to `false` if `K` and `f` should not be zeroed out, but instead keep their current values. """ -start_assemble(K::Union{SparseMatrixCSC, Symmetric{<:Any,SparseMatrixCSC}}, f::Vector; fillzero::Bool) +start_assemble(K::Union{AbstractSparseMatrixCSC, Symmetric{<:Any,<:AbstractSparseMatrixCSC}}, f::Vector; fillzero::Bool) -function start_assemble(K::SparseMatrixCSC{T}, f::Vector=T[]; fillzero::Bool=true) where {T} +function start_assemble(K::AbstractSparseMatrixCSC{T}, f::Vector=T[]; fillzero::Bool=true, maxcelldofs_hint::Int=0) where {T} fillzero && (fillzero!(K); fillzero!(f)) - return AssemblerSparsityPattern(K, f, Int[], Int[]) + return CSCAssembler(K, f, zeros(Int,maxcelldofs_hint), zeros(Int,maxcelldofs_hint)) end -function start_assemble(K::Symmetric{T,<:SparseMatrixCSC}, f::Vector=T[]; fillzero::Bool=true) where T +function start_assemble(K::Symmetric{T,<:SparseMatrixCSC}, f::Vector=T[]; fillzero::Bool=true, maxcelldofs_hint::Int=0) where T fillzero && (fillzero!(K); fillzero!(f)) - return AssemblerSymmetricSparsityPattern(K, f, Int[], Int[]) + return SymmetricCSCAssembler(K, f, zeros(Int,maxcelldofs_hint), zeros(Int,maxcelldofs_hint)) +end + +function finish_assemble(a::Union{CSCAssembler, SymmetricCSCAssembler}) + return a.K, isempty(a.f) ? nothing : a.f end """ - assemble!(A::AbstractSparseAssembler, dofs::AbstractVector{Int}, Ke::AbstractMatrix) - assemble!(A::AbstractSparseAssembler, dofs::AbstractVector{Int}, Ke::AbstractMatrix, fe::AbstractVector) + assemble!(A::AbstractAssembler, dofs::AbstractVector{Int}, Ke::AbstractMatrix) + assemble!(A::AbstractAssembler, dofs::AbstractVector{Int}, Ke::AbstractMatrix, fe::AbstractVector) Assemble the element stiffness matrix `Ke` (and optional force vector `fe`) into the global stiffness (and force) in `A`, given the element degrees of freedom `dofs`. @@ -164,22 +186,36 @@ stiffness (and force) in `A`, given the element degrees of freedom `dofs`. This is equivalent to `K[dofs, dofs] += Ke` and `f[dofs] += fe`, where `K` is the global stiffness matrix and `f` the global force/residual vector, but more efficient. """ -assemble!(::AbstractSparseAssembler, ::AbstractVector{Int}, ::AbstractMatrix, ::AbstractVector) +assemble!(::AbstractAssembler, ::AbstractVector{<:Integer}, ::AbstractMatrix, ::AbstractVector) -@propagate_inbounds function assemble!(A::AbstractSparseAssembler, dofs::AbstractVector{Int}, Ke::AbstractMatrix) +@propagate_inbounds function assemble!(A::AbstractAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix) assemble!(A, dofs, Ke, eltype(Ke)[]) end -@propagate_inbounds function assemble!(A::AbstractSparseAssembler, dofs::AbstractVector{Int}, fe::AbstractVector, Ke::AbstractMatrix) +@propagate_inbounds function assemble!(A::AbstractAssembler, dofs::AbstractVector{<:Integer}, fe::AbstractVector, Ke::AbstractMatrix) assemble!(A, dofs, Ke, fe) end -@propagate_inbounds function assemble!(A::AssemblerSparsityPattern, dofs::AbstractVector{Int}, Ke::AbstractMatrix, fe::AbstractVector) +@propagate_inbounds function assemble!(A::AbstractAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::AbstractVector) _assemble!(A, dofs, Ke, fe, false) end -@propagate_inbounds function assemble!(A::AssemblerSymmetricSparsityPattern, dofs::AbstractVector{Int}, Ke::AbstractMatrix, fe::AbstractVector) +@propagate_inbounds function assemble!(A::SymmetricCSCAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::AbstractVector) _assemble!(A, dofs, Ke, fe, true) end -@propagate_inbounds function _assemble!(A::AbstractSparseAssembler, dofs::AbstractVector{Int}, Ke::AbstractMatrix, fe::AbstractVector, sym::Bool) +""" + _sortdofs_for_assembly!(permutation::Vector{Int}, sorteddofs::Vector{Int}, dofs::AbstractVector) + +Sorts the dofs into a separate buffer and returns it together with a permutation vector. +""" +@propagate_inbounds function _sortdofs_for_assembly!(permutation::Vector{Int}, sorteddofs::Vector{Int}, dofs::AbstractVector) + ld = length(dofs) + resize!(permutation, ld) + resize!(sorteddofs, ld) + copyto!(sorteddofs, dofs) + sortperm2!(sorteddofs, permutation) + return sorteddofs, permutation +end + +@propagate_inbounds function _assemble!(A::AbstractCSCAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::AbstractVector, sym::Bool) ld = length(dofs) @boundscheck checkbounds(Ke, keys(dofs), keys(dofs)) if length(fe) != 0 @@ -189,13 +225,14 @@ end end K = matrix_handle(A) - permutation = A.permutation - sorteddofs = A.sorteddofs @boundscheck checkbounds(K, dofs, dofs) - resize!(permutation, ld) - resize!(sorteddofs, ld) - copyto!(sorteddofs, dofs) - sortperm2!(sorteddofs, permutation) + Krows = rowvals(K) + Kvals = nonzeros(K) + + # We assume that the input dofs are not sorted, because the cells need the dofs in + # a specific order, which might not be the sorted order. Hence we sort them. + # Note that we are not allowed to mutate `dofs` in the process. + sorteddofs, permutation = _sortdofs_for_assembly!(A.permutation, A.sorteddofs, dofs) current_col = 1 @inbounds for Kcol in sorteddofs @@ -206,13 +243,13 @@ end nzr = nzrange(K, Kcol) while Ri <= length(nzr) && ri <= maxlookups R = nzr[Ri] - Krow = K.rowval[R] + Krow = Krows[R] Kerow = permutation[ri] val = Ke[Kerow, Kecol] if Krow == dofs[Kerow] # Match: add the value (if non-zero) and advance the pointers if !iszero(val) - K.nzval[R] += val + Kvals[R] += val end ri += 1 Ri += 1 @@ -243,8 +280,8 @@ function _missing_sparsity_pattern_error(Krow::Int, Kcol::Int) "$(Kcol)] is missing in the sparsity pattern. Make sure you have called `K = " * "allocate_matrix(dh)` or `K = allocate_matrix(dh, ch)` if you " * "have affine constraints. This error might also happen if you are using " * - "`::AssemblerSparsityPattern` in a threaded assembly loop (you need to create an " * - "`assembler::AssemblerSparsityPattern` for each task)." + "the assembler in a threaded assembly loop (you need to create one " * + "`assembler` for each task)." )) end @@ -252,7 +289,7 @@ end """ apply_assemble!( - assembler::AbstractSparseAssembler, ch::ConstraintHandler, + assembler::AbstractAssembler, ch::ConstraintHandler, global_dofs::AbstractVector{Int}, local_matrix::AbstractMatrix, local_vector::AbstractVector; apply_zero::Bool = false @@ -271,7 +308,7 @@ When the keyword argument `apply_zero` is `true` all inhomogeneities are set to Note that this method is destructive since it modifies `local_matrix` and `local_vector`. """ function apply_assemble!( - assembler::AbstractSparseAssembler, ch::ConstraintHandler, + assembler::AbstractAssembler, ch::ConstraintHandler, global_dofs::AbstractVector{Int}, local_matrix::AbstractMatrix, local_vector::AbstractVector; apply_zero::Bool = false @@ -287,6 +324,11 @@ end # Sort utilities +""" + sortperm2!(data::AbstractVector, permutation::AbstractVector) + +Sort the input vector inplace and compute the corresponding permutation. +""" function sortperm2!(B, ii) @inbounds for i = 1:length(B) ii[i] = i From 5ec6c84ff645ed44cd02fb116316b7a97950bab8 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 19 Sep 2024 15:37:29 +0200 Subject: [PATCH 26/50] Fix a deprecation warning in hyperelasticity tutorial (#1057) --- .../literate-gallery/quasi_incompressible_hyperelasticity.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl index e024407d9d..1096491bae 100644 --- a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl +++ b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl @@ -311,7 +311,7 @@ function solve(interpolation_u, interpolation_p) Ferrite.update!(dbc, t) apply!(w, dbc) newton_itr = -1 - prog = ProgressMeter.ProgressThresh(NEWTON_TOL, "Solving @ time $t of $Tf;") + prog = ProgressMeter.ProgressThresh(NEWTON_TOL; desc = "Solving @ time $t of $Tf;") fill!(ΔΔw, 0.0); while true; newton_itr += 1 assemble_global!(K, f, cellvalues_u, cellvalues_p, dh, mp, w) From 0fe2ed8dc72699ef58bdb4a3a9990f83f2618748 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 20 Sep 2024 01:22:16 +0200 Subject: [PATCH 27/50] Deprecate assemble! with local vector before local matrix (#1059) This patch deprecates the signature `assemble!(assembler, dofs, local_vector, local_matrix)` in favor of `assemble!(assembler, dofs, local_matrix, local_vector)`. The former signature was not used in many places, not supported by e.g. the block matrix assembler, and it is confusing that there are two options. See also #707 for a similar change to `start_assemble`. In addition, if the assembler is used just for matrix assembly, this patch removes an unnecessary allocation of an empty vector for every call to `assemble!`. --- CHANGELOG.md | 4 ++++ docs/src/literate-gallery/helmholtz.jl | 2 +- .../quasi_incompressible_hyperelasticity.jl | 2 +- docs/src/literate-gallery/topology_optimization.jl | 2 +- docs/src/literate-howto/threaded_assembly.jl | 2 +- docs/src/literate-tutorials/hyperelasticity.jl | 2 +- .../incompressible_elasticity.jl | 2 +- docs/src/literate-tutorials/linear_shell.jl | 2 +- docs/src/literate-tutorials/plasticity.jl | 2 +- .../literate-tutorials/transient_heat_equation.jl | 2 +- src/assembler.jl | 14 ++++---------- src/deprecations.jl | 5 +++++ test/test_abstractgrid.jl | 2 +- test/test_apply_rhs.jl | 2 +- test/test_mixeddofhandler.jl | 2 +- 15 files changed, 25 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0e2f25619..03f02b627a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -462,6 +462,9 @@ more discussion). - `start_assemble(f, K)` have been deprecated in favor of the "canonical" `start_assemble(K, f)`. ([#707][github-707]) +- `assemble!(assembler, dofs, fe, Ke)` have been deprecated in favor of the "canonical" + `assemble!(assembler, dofs, Ke, fe)`. ([#1059][github-1059]) + - `end_assemble` have been deprecated in favor of `finish_assemble`. ([#754][github-754]) - `get_point_values` have been deprecated in favor of `evaluate_at_points`. @@ -1032,3 +1035,4 @@ poking into Ferrite internals: [github-949]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/949 [github-953]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/953 [github-974]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/974 +[github-1059]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/1059 diff --git a/docs/src/literate-gallery/helmholtz.jl b/docs/src/literate-gallery/helmholtz.jl index bd48a17a59..a08ce605a3 100644 --- a/docs/src/literate-gallery/helmholtz.jl +++ b/docs/src/literate-gallery/helmholtz.jl @@ -152,7 +152,7 @@ function doassemble(cellvalues::CellValues, facetvalues::FacetValues, end celldofs!(global_dofs, cell) - assemble!(assembler, global_dofs, fe, Ke) + assemble!(assembler, global_dofs, Ke, fe) end return K, f end; diff --git a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl index 1096491bae..030c36f0d0 100644 --- a/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl +++ b/docs/src/literate-gallery/quasi_incompressible_hyperelasticity.jl @@ -265,7 +265,7 @@ function assemble_global!(K::SparseMatrixCSC, f, cellvalues_u::CellValues, ue = w[global_dofsu] # displacement dofs for the current cell pe = w[global_dofsp] # pressure dofs for the current cell assemble_element!(ke, fe, cell, cellvalues_u, cellvalues_p, mp, ue, pe) - assemble!(assembler, global_dofs, fe, ke) + assemble!(assembler, global_dofs, ke, fe) end end; diff --git a/docs/src/literate-gallery/topology_optimization.jl b/docs/src/literate-gallery/topology_optimization.jl index 963909e6cf..e01e7d1931 100644 --- a/docs/src/literate-gallery/topology_optimization.jl +++ b/docs/src/literate-gallery/topology_optimization.jl @@ -328,7 +328,7 @@ function doassemble!(cellvalues::CellValues, facetvalues::FacetValues, K::Sparse ue = u[eldofs] elmt!(Ke, re, element, cellvalues, facetvalues, grid, mp, ue, state) - assemble!(assembler, celldofs(element), re, Ke) + assemble!(assembler, celldofs(element), Ke, re) end return K, r diff --git a/docs/src/literate-howto/threaded_assembly.jl b/docs/src/literate-howto/threaded_assembly.jl index 4e0e736463..fa0526fa9f 100644 --- a/docs/src/literate-howto/threaded_assembly.jl +++ b/docs/src/literate-howto/threaded_assembly.jl @@ -173,7 +173,7 @@ function assemble_cell!(scratch::ScratchValues, cell::Int, K::SparseMatrixCSC, end celldofs!(global_dofs, dh, cell) - assemble!(assembler, global_dofs, fe, Ke) + assemble!(assembler, global_dofs, Ke, fe) end; function run_assemble() diff --git a/docs/src/literate-tutorials/hyperelasticity.jl b/docs/src/literate-tutorials/hyperelasticity.jl index 701ec051e3..1cc5b10780 100644 --- a/docs/src/literate-tutorials/hyperelasticity.jl +++ b/docs/src/literate-tutorials/hyperelasticity.jl @@ -301,7 +301,7 @@ function assemble_global!(K, g, dh, cv, fv, mp, u, ΓN) global_dofs = celldofs(cell) ue = u[global_dofs] # element dofs @timeit "element assemble" assemble_element!(ke, ge, cell, cv, fv, mp, ue, ΓN) - assemble!(assembler, global_dofs, ge, ke) + assemble!(assembler, global_dofs, ke, ge) end end; diff --git a/docs/src/literate-tutorials/incompressible_elasticity.jl b/docs/src/literate-tutorials/incompressible_elasticity.jl index 9f4da7d532..4a13b407b8 100644 --- a/docs/src/literate-tutorials/incompressible_elasticity.jl +++ b/docs/src/literate-tutorials/incompressible_elasticity.jl @@ -107,7 +107,7 @@ function doassemble( fill!(ke, 0) fill!(fe, 0) assemble_up!(ke, fe, cell, cellvalues_u, cellvalues_p, facetvalues_u, grid, mp, ɛdev, t) - assemble!(assembler, celldofs(cell), fe, ke) + assemble!(assembler, celldofs(cell), ke, fe) end return K, f diff --git a/docs/src/literate-tutorials/linear_shell.jl b/docs/src/literate-tutorials/linear_shell.jl index 6ba45400ba..b00739dc72 100644 --- a/docs/src/literate-tutorials/linear_shell.jl +++ b/docs/src/literate-tutorials/linear_shell.jl @@ -103,7 +103,7 @@ for cell in CellIterator(grid) #Call the element routine integrate_shell!(ke, cv, qr_ooplane, cellcoords, data) - assemble!(assembler, celldofs, fe, ke) + assemble!(assembler, celldofs, ke, fe) end # Apply BC and solve. diff --git a/docs/src/literate-tutorials/plasticity.jl b/docs/src/literate-tutorials/plasticity.jl index af0d51823a..79a35c18c4 100644 --- a/docs/src/literate-tutorials/plasticity.jl +++ b/docs/src/literate-tutorials/plasticity.jl @@ -187,7 +187,7 @@ function doassemble!(K::SparseMatrixCSC, r::Vector, cellvalues::CellValues, dh:: state = @view states[:, i] state_old = @view states_old[:, i] assemble_cell!(ke, re, cell, cellvalues, material, ue, state, state_old) - assemble!(assembler, eldofs, re, ke) + assemble!(assembler, eldofs, ke, re) end return K, r end diff --git a/docs/src/literate-tutorials/transient_heat_equation.jl b/docs/src/literate-tutorials/transient_heat_equation.jl index f37a619dda..5895978adc 100644 --- a/docs/src/literate-tutorials/transient_heat_equation.jl +++ b/docs/src/literate-tutorials/transient_heat_equation.jl @@ -137,7 +137,7 @@ function doassemble_K!(K::SparseMatrixCSC, f::Vector, cellvalues::CellValues, dh end end - assemble!(assembler, celldofs(cell), fe, Ke) + assemble!(assembler, celldofs(cell), Ke, fe) end return K, f end diff --git a/src/assembler.jl b/src/assembler.jl index 22972803a6..e79521dc4b 100644 --- a/src/assembler.jl +++ b/src/assembler.jl @@ -188,16 +188,10 @@ stiffness matrix and `f` the global force/residual vector, but more efficient. """ assemble!(::AbstractAssembler, ::AbstractVector{<:Integer}, ::AbstractMatrix, ::AbstractVector) -@propagate_inbounds function assemble!(A::AbstractAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix) - assemble!(A, dofs, Ke, eltype(Ke)[]) -end -@propagate_inbounds function assemble!(A::AbstractAssembler, dofs::AbstractVector{<:Integer}, fe::AbstractVector, Ke::AbstractMatrix) - assemble!(A, dofs, Ke, fe) -end -@propagate_inbounds function assemble!(A::AbstractAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::AbstractVector) +@propagate_inbounds function assemble!(A::AbstractAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::Union{AbstractVector, Nothing} = nothing) _assemble!(A, dofs, Ke, fe, false) end -@propagate_inbounds function assemble!(A::SymmetricCSCAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::AbstractVector) +@propagate_inbounds function assemble!(A::SymmetricCSCAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::Union{AbstractVector, Nothing} = nothing) _assemble!(A, dofs, Ke, fe, true) end @@ -215,10 +209,10 @@ Sorts the dofs into a separate buffer and returns it together with a permutation return sorteddofs, permutation end -@propagate_inbounds function _assemble!(A::AbstractCSCAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::AbstractVector, sym::Bool) +@propagate_inbounds function _assemble!(A::AbstractCSCAssembler, dofs::AbstractVector{<:Integer}, Ke::AbstractMatrix, fe::Union{AbstractVector, Nothing}, sym::Bool) ld = length(dofs) @boundscheck checkbounds(Ke, keys(dofs), keys(dofs)) - if length(fe) != 0 + if fe !== nothing @boundscheck checkbounds(fe, keys(dofs)) @boundscheck checkbounds(A.f, dofs) @inbounds assemble!(A.f, dofs, fe) diff --git a/src/deprecations.jl b/src/deprecations.jl index e35db07746..37f03ee16e 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -438,3 +438,8 @@ export VTKFile function VTKFile(args...) throw(DeprecationError("VTKFile(args...)" => "VTKGridFile(args...)")) end + +# assemble with vector first +function assemble!(::AbstractAssembler, ::AbstractVector{<:Integer}, ::AbstractVector, ::AbstractMatrix) + throw(DeprecationError("assemble!(assembler, dofs, fe, Ke)" => "assemble!(assembler, dofs, Ke, fe)")) +end diff --git a/test/test_abstractgrid.jl b/test/test_abstractgrid.jl index a069e175d7..5005c27afc 100644 --- a/test/test_abstractgrid.jl +++ b/test/test_abstractgrid.jl @@ -57,7 +57,7 @@ end end end - assemble!(assembler, celldofs(dh,cellid), fe, Ke) + assemble!(assembler, celldofs(dh,cellid), Ke, fe) end return K, f end diff --git a/test/test_apply_rhs.jl b/test/test_apply_rhs.jl index d21a8fc6f9..87eaecbb5f 100644 --- a/test/test_apply_rhs.jl +++ b/test/test_apply_rhs.jl @@ -56,7 +56,7 @@ function test_apply_rhs() end end - assemble!(assembler, celldofs(cell), fe, Ke) + assemble!(assembler, celldofs(cell), Ke, fe) end return K, f end diff --git a/test/test_mixeddofhandler.jl b/test/test_mixeddofhandler.jl index 264324d4b0..5cb5a93a0c 100644 --- a/test/test_mixeddofhandler.jl +++ b/test/test_mixeddofhandler.jl @@ -331,7 +331,7 @@ function test_2_element_heat_eq() end end end - assemble!(assembler, eldofs, fe, Ke) + assemble!(assembler, eldofs, Ke, fe) end end From 5b92c8c382b52ae4acc321e802427c9765733fb4 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 20 Sep 2024 09:40:43 +0200 Subject: [PATCH 28/50] Use Changelog.jl for changelog generation (#1061) This patch replaces the custom changelog code with Changelog.jl for changelog generation. In addition, a link to the changelog is added to the HTML documentation. Closes #925. --- CHANGELOG.md | 792 ++++++++++++++++++++++----------------------- docs/Manifest.toml | 9 +- docs/Project.toml | 1 + docs/changelog.jl | 58 +--- docs/make.jl | 13 +- docs/src/index.md | 17 +- 6 files changed, 418 insertions(+), 472 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03f02b627a..46862f6fc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ 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). -## [1.0.0] - Work in progress, release date TBD +## [v1.0.0] - Work in progress, release date TBD Ferrite version 1.0 is a relatively large release, with a lot of new features, improvements, deprecations and some removals. These changes are made to make the code base more consistent @@ -15,10 +15,9 @@ stability, and there is no breaking release 2.0 on the horizon. Unfortunately this means that code written for Ferrite version 0.3 will have to be updated. All changes, with upgrade paths, are listed in the sections below. Since these sections include a lot of other information as well (new features, internal changes, ...) there is -also a dedicated section about [Upgrading code from Ferrite 0.3 to -1.0](#upgrading-code-from-ferrite-03-to-10) which include the most common changes that are -required. In addition, in all cases where possible, you will be presented with a descriptive -error message telling you what needs to change. +also a dedicated section about upgrading code from Ferrite 0.3 to 1.0 (see below) which +include the most common changes that are required. In addition, in all cases where possible, +you will be presented with a descriptive error message telling you what needs to change. ### Upgrading code from Ferrite 0.3 to 1.0 @@ -212,7 +211,7 @@ more discussion). # ... ``` -- **VTK Export**: The VTK export has been changed [#692][github-692]. +- **VTK Export**: The VTK export has been changed [#692]. ```diff - vtk_grid(name, dh) do vtk - vtk_point_data(vtk, dh, a) @@ -248,16 +247,15 @@ more discussion). ### Added -- `InterfaceValues` for computing jumps and averages over interfaces. ([#743][github-743]) +- `InterfaceValues` for computing jumps and averages over interfaces. ([#743]) -- `InterfaceIterator` and `InterfaceCache` for iterating over interfaces. ([#747][github-747]) +- `InterfaceIterator` and `InterfaceCache` for iterating over interfaces. ([#747]) -- `FacetQuadratureRule` implementation for `RefPrism` and `RefPyramid`. ([#779][github-779]) +- `FacetQuadratureRule` implementation for `RefPrism` and `RefPyramid`. ([#779]) - The `DofHandler` now support selectively adding fields on sub-domains (rather than the full domain). This new functionality is included with the new `SubDofHandler` struct, - which, as the name suggest, is a `DofHandler` for a subdomain. ([#624][github-624], - [#667][github-667], [#735][github-735]) + which, as the name suggest, is a `DofHandler` for a subdomain. ([#624], [#667], [#735]) - New reference shape structs `RefLine`, `RefTriangle`, `RefQuadrilateral`, `RefTetrahedron`, `RefHexahedron`, and `RefPrism` have been added. These encode the @@ -265,92 +263,91 @@ more discussion). necessary to always pair with an explicit dimension (i.e. `RefLine` replaces `(RefCube, 1)`, `RefTriangle` replaces `(RefTetrahedron, 2)`, etc.). For writing "dimension independent code" it is possible to use `Ferrite.RefHypercube{dim}` and - `Ferrite.RefSimplex{dim}`. ([#679][github-679]) + `Ferrite.RefSimplex{dim}`. ([#679]) - New methods for adding entitysets that are located on the boundary of the grid: `addboundaryfacetset!` and `addboundaryvertexset!`. These work similar to `addfacetset!` and `addvertexset!`, but filters out all instances not on the boundary (this can be used to avoid accidental inclusion of internal - entities in sets used for boundary conditions, for example). ([#606][github-606]) + entities in sets used for boundary conditions, for example). ([#606]) - New interpolation `VectorizedInterpolation` which vectorizes scalar interpolations for vector-valued problems. A `VectorizedInterpolation` is created from a (scalar) interpolation `ip` using either `ip ^ dim` or `VectorizedInterpolation{dim}(ip)`. For convenience, the method `VectorizedInterpolation(ip)` vectorizes the interpolation to the - reference dimension of the interpolation. ([#694][github-694], [#736][github-736]) + reference dimension of the interpolation. ([#694], [#736]) - New (scalar) interpolation `Lagrange{RefQuadrilateral, 3}()`, i.e. third order Lagrange - interpolation for 2D quadrilaterals. ([#701][github-701], [#731][github-731]) + interpolation for 2D quadrilaterals. ([#701], [#731]) - `CellValues` now support embedded elements. Specifically you can now embed elements with reference dimension 1 into spatial dimension 2 or 3, and elements with reference dimension - 2 in to spatial dimension 3. ([#651][github-651]) + 2 in to spatial dimension 3. ([#651]) - `CellValues` now support (vector) interpolations with dimension different from the spatial - dimension. ([#651][github-651]) + dimension. ([#651]) - `FacetQuadratureRule` have been added and should be used for `FacetValues`. A `FacetQuadratureRule` for integration of the facets of e.g. a triangle can be constructed by `FacetQuadratureRule{RefTriangle}(order)` (similar to how `QuadratureRule` is constructed). - ([#716][github-716]) + ([#716]) - New functions `Ferrite.reference_shape_value(::Interpolation, ξ::Vec, i::Int)` and `Ferrite.reference_shape_gradient(::Interpolation, ξ::Vec, i::Int)` for evaluating the value/gradient of the `i`th shape function of an interpolation in local reference coordinate `ξ`. These methods are public but not exported. (Note that these methods return the value/gradient wrt. the reference coordinate `ξ`, whereas the corresponding methods - for `CellValues` etc return the value/gradient wrt the spatial coordinate `x`.) - ([#721][github-721]) + for `CellValues` etc return the value/gradient wrt the spatial coordinate `x`.) ([#721]) - `FacetIterator` and `FacetCache` have been added. These work similarly to `CellIterator` and `CellCache` but are used to iterate over (boundary) face sets instead. These simplify boundary integrals in general, and in particular Neumann boundary conditions are more convenient to implement now that you can loop directly over the face set instead of - checking all faces of a cell inside the element routine. ([#495][github-495]) + checking all faces of a cell inside the element routine. ([#495]) - The `ConstraintHandler` now support adding Dirichlet boundary conditions on discontinuous - interpolations. ([#729][github-729]) + interpolations. ([#729]) - `collect_periodic_faces` now have a keyword argument `tol` that can be used to relax the - default tolerance when necessary. ([#749][github-749]) + default tolerance when necessary. ([#749]) -- VTK export now work with `QuadraticHexahedron` elements. ([#714][github-714]) +- VTK export now work with `QuadraticHexahedron` elements. ([#714]) - The function `bounding_box(::AbstractGrid)` has been added. It computes the bounding box for a given grid (based on its node coordinates), and returns the minimum and maximum vertices - of the bounding box. ([#880][github-880]) + of the bounding box. ([#880]) - Support for working with sparsity patterns has been added. This means that Ferrite exposes the intermediate "state" between the DofHandler and the instantiated matrix as the new struct `SparsityPattern`. This make it possible to insert custom equations or couplings in the pattern before instantiating the matrix. The function `create_sparsity_pattern` have been removed. The new function `allocate_matrix` is instead used to instantiate the - matrix. Refer to the documentation for more details. ([#888][github-888]) + matrix. Refer to the documentation for more details. ([#888]) **To upgrade**: if you want to recover the old functionality and don't need to work with the pattern, replace any usage of `create_sparsity_pattern` with `allocate_matrix`. - A new function, `geometric_interpolation`, is exported, which gives the geometric interpolation for each cell type. This is equivalent to the deprecated `Ferrite.default_interpolation` function. - ([#953][github-953]) + ([#953]) - CellValues and FacetValues can now store and map second order gradients (Hessians). The number of gradients computed in CellValues/FacetValues is specified using the keyword arguments `update_gradients::Bool` (default true) and `update_hessians::Bool` (default false) in the - constructors, i.e. `CellValues(...; update_hessians=true)`. ([#953][github-938]) + constructors, i.e. `CellValues(...; update_hessians=true)`. ([#953]) -- `L2Projector` supports projecting on grids with mixed celltypes. ([#949][github-949]) +- `L2Projector` supports projecting on grids with mixed celltypes. ([#949]) ### Changed - It is now possible to create sparsity patterns with interface couplings, see the new function `add_interface_entries!` and the rework of sparsity pattern construction. - ([#710][github-#710]) + ([#710]) - The `AbstractCell` interface has been reworked. This change should not affect user code, but may in some cases be relevant for code parsing external mesh files. In particular, the generic `Cell` struct have been removed in favor of concrete cell implementations (`Line`, - `Triangle`, ...). ([#679][github-679], [#712][github-712]) + `Triangle`, ...). ([#679], [#712]) **To upgrade** replace any usage of `Cell{...}(...)` with calls to the concrete implementations. @@ -358,7 +355,7 @@ more discussion). - The default geometric mapping in `CellValues` and `FacetValues` have changed. The new default is to always use `Lagrange{refshape, 1}()`, i.e. linear Lagrange polynomials, for the geometric interpolation. Previously, the function interpolation was (re) used also for - the geometry interpolation. ([#695][github-695]) + the geometry interpolation. ([#695]) **To upgrade**, if you relied on the previous default, simply pass the function interpolation also as the third argument (the geometric interpolation). @@ -367,11 +364,11 @@ more discussion). (previously) existing interpolations are scalar. (Scalar) interpolations must now be explicitly vectorized, using the new `VectorizedInterpolation`, when used for vector problems. (Previously implicit vectorization happened in the `CellValues` constructor, and - when adding fields to the `DofHandler`). ([#694][github-694]) + when adding fields to the `DofHandler`). ([#694]) - It is now required to explicitly pass the interpolation to the `DofHandler` when adding a new field using `add!`. For vector fields the interpolation should be vectorized, instead - of passing number of components as an integer. ([#694][github-694]) + of passing number of components as an integer. ([#694]) **To upgrade** don't pass the dimension as an integer, and pass the interpolation explicitly. See more details in [Upgrading code from Ferrite 0.3 to @@ -379,13 +376,13 @@ more discussion). - `Interpolation`s should now be constructed using the new reference shapes. Since the new reference shapes encode the reference dimension the first type parameter of interpolations - have been removed. ([#711][github-711]) + have been removed. ([#711]) **To upgrade** replace e.g. `Lagrange{1, RefCube, 1}()` with `Lagrange{RefLine, 1}()`, and `Lagrange{2, RefTetrahedron, 1}()` with `Lagrange{RefTriangle, 1}()`, etc. - `QuadratureRule`s should now be constructed using the new reference shapes. Since the new reference shapes encode the reference dimension the first type parameter of - `QuadratureRule` have been removed. ([#711][github-711], [#716][github-716]) + `QuadratureRule` have been removed. ([#711], [#716]) **To upgrade** replace e.g. `QuadratureRule{1, RefCube}(order)` with `QuadratureRule{RefLine}(order)`, and `QuadratureRule{2, RefTetrahedron}(1)` with `Lagrange{RefTriangle}(order)`, etc. @@ -396,7 +393,7 @@ more discussion). differentiation between scalar and vector have thus been moved to the interpolation (see above). Note that previously `CellValues`, `FaceValues`, and `PointValues` where abstract types, but they are now concrete implementations with *different type parameters*, except - `FaceValues` which is now `FacetValues` ([#708][github-708]) + `FaceValues` which is now `FacetValues` ([#708]) **To upgrade**, for scalar problems, it is enough to replace `CellScalarValues` with `CellValues`, `FaceScalarValues` with `FacetValues` and `PointScalarValues` with `PointValues`, respectively. For vector problems, make sure to vectorize the interpolation @@ -404,23 +401,23 @@ more discussion). `FacetValues`, and `PointVectorValues` with `PointValues`. - The quadrature rule passed to `FacetValues` should now be of type `FacetQuadratureRule` - rather than of type `QuadratureRule`. ([#716][github-716]) + rather than of type `QuadratureRule`. ([#716]) **To upgrade** replace the quadrature rule passed to `FacetValues` with a `FacetQuadratureRule`. - Checking if a face `(ele_id, local_face_id) ∈ faceset` has been previously implemented by type piracy. In order to be invariant to the underlying `Set` datatype as well as - omitting type piracy, ([#835][github-835]) implemented `isequal` and `hash` for `BoundaryIndex` datatypes. + omitting type piracy, ([#835]) implemented `isequal` and `hash` for `BoundaryIndex` datatypes. - **VTK export**: Ferrite no longer extends `WriteVTK.vtk_grid` and associated functions, instead the new type `VTKGridFile` should be used instead. New methods exists for writing to a `VTKGridFile`, e.g. `write_solution`, `write_cell_data`, `write_node_data`, and `write_projection`. - See [#692][github-692]. + See [#692]. - **Definitions**: Previously, `face` and `edge` referred to codimension 1 relative reference shape. In Ferrite v1, `volume`, `face`, `edge`, and `vertex` refer to 3, 2, 1, and 0 dimensional entities, and `facet` replaces the old definition of `face`. No direct replacement for `edges` exits. - See [#914][github-789] and [#914][github-914]. + See [#914] and [#914]. The main implications of this change are * `FaceIndex` -> `FacetIndex` (`FaceIndex` still exists, but has a different meaning) * `FaceValues` -> `FacetValues` @@ -432,10 +429,10 @@ more discussion). `facedof_indices`, `volumedof_indices` (and similar) according to these definitions. - `Ferrite.getdim` has been changed into `Ferrite.getrefdim` for getting the dimension of the reference shape - and `Ferrite.getspatialdim` to get the spatial dimension (of the grid). ([#943][github-943]) + and `Ferrite.getspatialdim` to get the spatial dimension (of the grid). ([#943]) - `Ferrite.getfielddim(::AbstractDofHandler, args...)` has been renamed to `Ferrite.n_components`. - ([#943][github-943]) + ([#943]) - The constructor for `ExclusiveTopology` only accept an `AbstractGrid` as input, removing the alternative of providing a `Vector{<:AbstractCell}`, as knowing the @@ -443,37 +440,35 @@ more discussion). Furthermore, it uses a new internal data structure, `ArrayOfVectorViews`, to store the neighborhood information more efficiently The datatype for the neighborhood has thus changed to a view of a vector, instead of the now removed `EntityNeighborhood` container. This also applies to `vertex_star_stencils`. - ([#974][github-974]). + ([#974]). - `project(::L2Projector, data, qr_rhs)` now expects data to be indexed by the cellid, as opposed to the index in the vector of cellids passed to the `L2Projector`. The data may be passed as an `AbstractDict{Int, <:AbstractVector}`, as an alternative to `AbstractArray{<:AbstractVector}`. - ([#949][github-949]) + ([#949]) ### Deprecated - The rarely (if ever) used methods of `function_value`, `function_gradient`, `function_divergence`, and `function_curl` taking *vectorized dof values* as in put have - been deprecated. ([#698][github-698]) + been deprecated. ([#698]) - The function `reshape_to_nodes` have been deprecated in favor of `evaluate_at_grid_nodes`. - ([#703][github-703]) + ([#703]) - `start_assemble(f, K)` have been deprecated in favor of the "canonical" `start_assemble(K, - f)`. ([#707][github-707]) + f)`. ([#707]) - `assemble!(assembler, dofs, fe, Ke)` have been deprecated in favor of the "canonical" - `assemble!(assembler, dofs, Ke, fe)`. ([#1059][github-1059]) + `assemble!(assembler, dofs, Ke, fe)`. ([#1059]) -- `end_assemble` have been deprecated in favor of `finish_assemble`. ([#754][github-754]) +- `end_assemble` have been deprecated in favor of `finish_assemble`. ([#754]) -- `get_point_values` have been deprecated in favor of `evaluate_at_points`. - ([#754][github-754]) +- `get_point_values` have been deprecated in favor of `evaluate_at_points`. ([#754]) -- `transform!` have been deprecated in favor of `transform_coordinates!`. - ([#754][github-754]) +- `transform!` have been deprecated in favor of `transform_coordinates!`. ([#754]) -- `Ferrite.default_interpolation` has been deprecated in favor of `geometric_interpolation`. ([#953][github-953]) +- `Ferrite.default_interpolation` has been deprecated in favor of `geometric_interpolation`. ([#953]) ### Removed @@ -482,24 +477,23 @@ more discussion). more capable compared to `FieldHandler`. Previously it was often required to pass both the `MixedDofHandler` and the `FieldHandler` to e.g. the assembly routine, but now it is enough to pass the `SubDofHandler` since it can be used for e.g. DoF queries etc. - ([#624][github-624], [#667][github-667], [#735][github-735]) + ([#624], [#667], [#735]) - Some old methods to construct the `L2Projector` have been removed after being deprecated - for several releases. ([#697][github-697]) + for several releases. ([#697]) - The option `project_to_nodes` have been removed from `project(::L2Projector, ...)`. The returned values are now always ordered according to the projectors internal `DofHandler`. - ([#699][github-699]) + ([#699]) -- The function `compute_vertex_values` have been removed. ([#700][github-700]) +- The function `compute_vertex_values` have been removed. ([#700]) - The names `getweights`, `getpoints`, `getcellsets`, `getnodesets`, `getfacesets`, `getedgesets`, and `getvertexsets` have been removed from the list of exported names. (For - now you can still use them by prefixing `Ferrite.`, e.g. `Ferrite.getweights`.) - ([#754][github-754]) + now you can still use them by prefixing `Ferrite.`, e.g. `Ferrite.getweights`.) ([#754]) - The `onboundary` function (and the associated `boundary_matrix` property of the `Grid` - datastructure) have been removed ([#924][github-924]). Instead of first checking + datastructure) have been removed ([#924]). Instead of first checking `onboundary` and then check whether a facet belong to a specific facetset, check the facetset directly. For example: ```diff @@ -511,528 +505,504 @@ more discussion). ### Fixed -- Benchmarks now work with master branch. ([#751][github-#751], [#855][github-#855]) +- Benchmarks now work with master branch. ([#751], [#855]) - Topology construction have been generalized to, in particular, fix construction for 1D and - for wedge elements. ([#641][github-641], [#670][github-670], [#684][github-684]) + for wedge elements. ([#641], [#670], [#684]) ### Other improvements - Documentation: - The documentation is now structured according to the Diataxis framework. There is now also clear separation between tutorials (for teaching) and code gallery (for showing - off). ([#737][github-737], [#756][github-756]) + off). ([#737], [#756]) - New section in the developer documentation that describes the (new) reference shapes and - their numbering scheme. ([#688][github-688]) + their numbering scheme. ([#688]) - Performance: - `Ferrite.transform!(grid, f)` (for transforming the node coordinates in the `grid` - according to a function `f`) is now faster and allocates less. ([#675][github-675]) + according to a function `f`) is now faster and allocates less. ([#675]) - Slight performance improvement in construction of `PointEvalHandler` (faster reverse - coordinate lookup). ([#719][github-719]) - - Various performance improvements to topology construction. ([#753][github-753], [#759][github-759]) + coordinate lookup). ([#719]) + - Various performance improvements to topology construction. ([#753], [#759]) - Internal improvements: - The dof distribution interface have been updated to support higher order elements - (future work). ([#627][github-627], [#732][github-732], [#733][github-733]) + (future work). ([#627], [#732], [#733]) - The `AbstractGrid` and `AbstractDofHandler` interfaces are now used more consistently internally. This will help with the implementation of distributed grids and DofHandlers. - ([#655][github-655]) + ([#655]) - VTK export now uses the (geometric) interpolation directly when evaluating the finite - element field instead of trying to work backwards how DoFs map to nodes. - ([#703][github-703]) - - Improved bounds checking in `assemble!`. ([#706][github-706]) + element field instead of trying to work backwards how DoFs map to nodes. ([#703]) + - Improved bounds checking in `assemble!`. ([#706]) - Internal methods `Ferrite.value` and `Ferrite.derivative` for computing the - value/gradient of *all* shape functions have been removed. ([#720][github-720]) + value/gradient of *all* shape functions have been removed. ([#720]) - `Ferrite.create_incidence_matrix` now work with any `AbstractGrid` (not just `Grid`). - ([#726][github-726]) + ([#726]) -## [0.3.14] - 2023-04-03 +## [v0.3.14] - 2023-04-03 ### Added - Support reordering dofs of a `MixedDofHandler` by the built-in orderings `FieldWise` and `ComponentWise`. This includes support for reordering dofs of fields on subdomains. - ([#645][github-645]) + ([#645]) - Support specifying the coupling between fields in a `MixedDofHandler` when creating the - sparsity pattern. ([#650][github-650]) - - Support Metis dof reordering with coupling information for `MixedDofHandler`. - ([#650][github-650]) - - Pretty printing for `MixedDofHandler` and `L2Projector`. ([#465][github-465]) + sparsity pattern. ([#650]) + - Support Metis dof reordering with coupling information for `MixedDofHandler`. ([#650]) + - Pretty printing for `MixedDofHandler` and `L2Projector`. ([#465]) ### Other improvements - - The `MixedDofHandler` have gone through a performance review (see [#629][github-629]) and + - The `MixedDofHandler` have gone through a performance review (see [#629]) and now performs the same as `DofHandler`. This was part of the push to merge the two DoF handlers. Since `MixedDofHandler` is strictly more flexible, and now equally performant, - it will replace `DofHandler` in the next breaking release. ([#637][github-637], - [#639][github-639], [#642][github-642], [#643][github-643], [#656][github-656], - [#660][github-660]) + it will replace `DofHandler` in the next breaking release. ([#637], [#639], [#642], + [#643], [#656], [#660]) ### Internal changes Changes listed here should not affect regular usage, but listed here in case you have been poking into Ferrite internals: - `Ferrite.ndim(dh, fieldname)` has been removed, use `Ferrite.getfielddim(dh, fieldname)` - instead. ([#658][github-658]) + instead. ([#658]) - `Ferrite.nfields(dh)` has been removed, use `length(Ferrite.getfieldnames(dh))` instead. - ([#444][github-444], [#653][github-653]) + ([#444], [#653]) - `getfielddims(::FieldHandler)` and `getfieldinterpolations(::FieldHandler)` have been - removed ([#647][github-647], [#659][github-659]) + removed ([#647], [#659]) -## [0.3.13] - 2023-03-23 +## [v0.3.13] - 2023-03-23 ### Added - - Support for classical trilinear and triquadratic wedge elements. ([#581][github-581]) - - Symmetric quadrature rules up to order 10 for prismatic elements. ([#581][github-581]) + - Support for classical trilinear and triquadratic wedge elements. ([#581]) + - Symmetric quadrature rules up to order 10 for prismatic elements. ([#581]) - Finer granulation of dof distribution, allowing to distribute different amounts of dofs - per entity. ([#581][github-581]) + per entity. ([#581]) ### Fixed - - Dof distribution for embedded elements. ([#581][github-581]) + - Dof distribution for embedded elements. ([#581]) - Improve numerical accuracy in shape function evaluation for the - `Lagrange{2,Tetrahedron,(3|4|5)}` interpolations. ([#582][github-582], - [#633][github-633]) + `Lagrange{2,Tetrahedron,(3|4|5)}` interpolations. ([#582], + [#633]) ### Other improvements - Documentation: - New "Developer documentation" section in the manual for documenting Ferrite.jl - internals and developer tools. ([#611][github-611]) - - Fix a bug in constraint computation in Stoke's flow example. ([#614][github-614]) + internals and developer tools. ([#611]) + - Fix a bug in constraint computation in Stoke's flow example. ([#614]) - Performance: - - Benchmarking infrastructure to help tracking performance changes. ([#388][github-388]) + - Benchmarking infrastructure to help tracking performance changes. ([#388]) - Performance improvements for various accessor functions for `MixedDofHandler`. - ([#621][github-621]) + ([#621]) ### Internal changes - To clarify the dof management `vertices(ip)`, `edges(ip)` and `faces(ip)` has been deprecated in favor of `vertexdof_indices(ip)`, `edgedof_indices(ip)` and - `facedof_indices(ip)`. ([#581][github-581]) - - Duplicate grid representation has been removed from the `MixedDofHandler`. - ([#577][github-577]) + `facedof_indices(ip)`. ([#581]) + - Duplicate grid representation has been removed from the `MixedDofHandler`. ([#577]) -## [0.3.12] - 2023-02-28 +## [v0.3.12] - 2023-02-28 ### Added - - Added a basic `show` method for assemblers. ([#598][github-598]) + - Added a basic `show` method for assemblers. ([#598]) ### Fixed - Fix an issue in constraint application of `Symmetric`-wrapped sparse matrices (i.e. obtained from `create_symmatric_sparsity_pattern`). In particular, `apply!(K::Symmetric, f, ch)` would incorrectly modify `f` if any of the constraints were inhomogeneous. - ([#592][github-592]) + ([#592]) - Properly disable the Metis extension on Julia 1.9 instead of causing precompilation - errors. ([#588][github-588]) + errors. ([#588]) - Fix adding Dirichlet boundary conditions on nodes when using MixedDofHandler. - ([#593][github-593], [#594][github-594]) - - Fix accidentally slow implementation of `show` for `Grid`s. ([#599][github-599]) - - Fixes to topology functionality. ([#453][github-453], [#518][github-518], - [#455][github-455]) - - Fix grid coloring for cell sets with 0 or 1 cells. ([#600][github-600]) + ([#593], [#594]) + - Fix accidentally slow implementation of `show` for `Grid`s. ([#599]) + - Fixes to topology functionality. ([#453], [#518], + [#455]) + - Fix grid coloring for cell sets with 0 or 1 cells. ([#600]) ### Other improvements - Documentation improvements: - - Simplications and clarifications to hyperelasticity example. ([#591][github-591]) - - Remove duplicate docstring entry for `vtk_point_data`. ([#602][github-602]) - - Update documentation about initial conditions. ([#601][github-601], - [#604][github-604]) + - Simplications and clarifications to hyperelasticity example. ([#591]) + - Remove duplicate docstring entry for `vtk_point_data`. ([#602]) + - Update documentation about initial conditions. ([#601], [#604]) -## [0.3.11] - 2023-01-17 +## [v0.3.11] - 2023-01-17 ### Added - [Metis.jl](https://github.com/JuliaSparse/Metis.jl) extension for fill-reducing DoF permutation. This uses Julias new package extension mechanism (requires Julia 1.10) to support a new DoF renumbering order `DofOrder.Ext{Metis}()` that can be passed to - `renumber!` to renumber DoFs using the Metis.jl library. ([#393][github-393], - [#549][github-549]) + `renumber!` to renumber DoFs using the Metis.jl library. ([#393], [#549]) - [BlockArrays.jl](https://github.com/JuliaArrays/BlockArrays.jl) extension for creating a globally blocked system matrix. `create_sparsity_pattern(BlockMatrix, dh, ch; kwargs...)` return a matrix that is blocked by field (requires DoFs to be (re)numbered by field, i.e. `renumber!(dh, DofOrder.FieldWise())`). For custom blocking it is possible to pass an uninitialized `BlockMatrix` with the correct block sizes (see `BlockArrays.jl` docs). This functionality is useful for e.g. special solvers where individual blocks need to be - extracted. Requires Julia version 1.9 or above. ([#567][github-567]) + extracted. Requires Julia version 1.9 or above. ([#567]) - New function `apply_analytical!` for setting the values of the degrees of freedom for a - specific field according to a spatial function `f(x)`. ([#532][github-532]) + specific field according to a spatial function `f(x)`. ([#532]) - New cache struct `CellCache` to be used when iterating over the cells in a grid or DoF handler. `CellCache` caches nodes, coordinates, and DoFs, for the cell. The cache `cc` can be re-initialized for a new cell index `ci` by calling `reinit!(cc, ci)`. This can be used as an alternative to `CellIterator` when more control over which element to loop - over is needed. See documentation for `CellCache` for more information. - ([#546][github-546]) + over is needed. See documentation for `CellCache` for more information. ([#546]) - It is now possible to create the sparsity pattern without constrained entries (they will be zeroed out later anyway) by passing `keep_constrained=false` to `create_sparsity_pattern`. This naturally only works together with local condensation of constraints since there won't be space allocated in the global matrix for the full (i.e. "non-condensed") element matrix. Creating the matrix without constrained entries reduces the memory footprint, but unless a significant amount of DoFs are constrained (e.g. high - mesh resolution at a boundary) the savings are negligible. ([#539][github-539]) + mesh resolution at a boundary) the savings are negligible. ([#539]) ### Changed - `ConstraintHandler`: `update!` is now called implicitly in `close!`. This was easy to miss, and somewhat of a strange requirement when solving problems without time stepping. - ([#459][github-459]) + ([#459]) - The function for computing the inhomogeneity in a `Dirichlet` constraint can now be specified as either `f(x)` or `f(x, t)`, where `x` is the spatial coordinate and `t` the - time. ([#459][github-459]) + time. ([#459]) - The elements of a `CellIterator` are now `CellCache` instead of the iterator itself, which was confusing in some cases. This change does not affect typical user code. - ([#546][github-546]) + ([#546]) ### Deprecated - Adding fields to a DoF handler with `push!(dh, ...)` has been deprecated in favor of `add!(dh, ...)`. This is to make it consistent with how constraints are added to a - constraint handler. ([#578][github-578]) + constraint handler. ([#578]) ### Fixed - - Fix `shape_value` for the linear, discontinuous Lagrange interpolation. - ([#553][github-553]) - - Fix `reference_coordinate` dispatch for discontinuous Lagrange interpolations. - ([#559][github-559]) - - Fix `show(::Grid)` for custom cell types. ([#570][github-570]) - - Fix `apply_zero!(Δa, ch)` when using inhomogeneous affine constraints - ([#575][github-575]) + - Fix `shape_value` for the linear, discontinuous Lagrange interpolation. ([#553]) + - Fix `reference_coordinate` dispatch for discontinuous Lagrange interpolations. ([#559]) + - Fix `show(::Grid)` for custom cell types. ([#570]) + - Fix `apply_zero!(Δa, ch)` when using inhomogeneous affine constraints ([#575]) ### Other improvements - Internal changes defining a new global matrix/vector "interface". These changes make it easy to enable more array types (e.g. `BlockMatrix` support added in this release) and - solvers in the future. ([#562][github-562], [#571][github-571]) + solvers in the future. ([#562], [#571]) - Performance improvements: - Reduced time and memory allocations for global sparse matrix creation (Julia >= 1.10). - ([#563][github-563]) + ([#563]) - Documentation improvements: - - Added an overview of the Examples section. ([#531][github-531]) - - Added an example showing topology optimization. ([#531][github-531]) - - Various typo fixes. ([#574][github-574]) - - Fix broken links. ([#583][github-583]) + - Added an overview of the Examples section. ([#531]) + - Added an example showing topology optimization. ([#531]) + - Various typo fixes. ([#574]) + - Fix broken links. ([#583]) -## [0.3.10] - 2022-12-11 +## [v0.3.10] - 2022-12-11 ### Added - New functions `apply_local!` and `apply_assemble!` for applying constraints locally on - the element level before assembling to the global system. ([#528][github-528]) + the element level before assembling to the global system. ([#528]) - New functionality to renumber DoFs by fields or by components. This is useful when you - need the global matrix to be blocked. ([#378][github-378], [#545][github-545]) + need the global matrix to be blocked. ([#378], [#545]) - Functionality to renumber DoFs in DofHandler and ConstraintHandler simultaneously: `renumber!(dh::DofHandler, ch::ConstraintHandler, order)`. Previously renumbering had to be done *before* creating the ConstraintHandler since otherwise DoF numbers would be inconsistent. However, this was inconvenient in cases where the constraints impact the - new DoF order permutation. ([#542][github-542]) + new DoF order permutation. ([#542]) - The coupling between fields can now be specified when creating the global matrix with `create_sparsity_pattern` by passing a `Matrix{Bool}`. For example, in a problem with unknowns `(u, p)` and corresponding test functions `(v, q)`, if there is no coupling between `p` and `q` it is unnecessary to allocate entries in the global matrix corresponding to these DoFs. This can now be communicated to `create_sparsity_pattern` by passing the coupling matrix `[true true; true false]` in the keyword argument `coupling`. - ([#544][github-544]) + ([#544]) ### Changed - Runtime and allocations for application of boundary conditions in `apply!` and `apply_zero!` have been improved. As a result, the `strategy` keyword argument is - obsolete and thus ignored. ([#489][github-489]) + obsolete and thus ignored. ([#489]) - The internal representation of `Dirichlet` boundary conditions and `AffineConstraint`s in the `ConstraintHandler` have been unified. As a result, conflicting constraints on DoFs are handled more consistently: the constraint added last to the `ConstraintHandler` now always override any previous constraints. Conflicting constraints could previously cause - problems when a DoF where prescribed by both `Dirichlet` and `AffineConstraint`. - ([#529][github-529]) + problems when a DoF where prescribed by both `Dirichlet` and `AffineConstraint`. ([#529]) - Entries in local matrix/vector are now ignored in the assembly procedure. This allows, for example, using a dense local matrix `[a b; c d]` even if no entries exist in the global matrix for the `d` block, i.e. in `[A B; C D]` the `D` block is zero, and these global entries might not exist in the sparse matrix. (Such sparsity patterns can now be - created by `create_sparsity_pattern`, see [#544][github-544].) ([#543][github-543]) + created by `create_sparsity_pattern`, see [#544].) ([#543]) ### Fixed - Fix affine constraints with prescribed DoFs in the right-hand-side. In particular, DoFs that are prescribed by just an inhomogeneity are now handled correctly, and nested affine constraints now give an error instead of silently giving the wrong result. - ([#530][github-530], [#535][github-535]) + ([#530], [#535]) - Fixed internal inconsistency in edge ordering for 2nd order RefTetrahedron and RefCube. - ([#520][github-520], [#523][github-523]) + ([#520], [#523]) ### Other improvements - Performance improvements: - Reduced time and memory allocations in DoF distribution for `MixedDofHandler`. - ([#533][github-533]) - - Reduced time and memory allocations reductions in `getcoordinates!`. - ([#536][github-536]) + ([#533]) + - Reduced time and memory allocations reductions in `getcoordinates!`. ([#536]) - Reduced time and memory allocations in affine constraint condensation. - ([#537][github-537], [#541][github-541], [#550][github-550]) + ([#537], [#541], [#550]) - Documentation improvements: - - Use `:static` scheduling for threaded `for`-loop ([#534][github-534]) - - Remove use of `@inbounds` ([#547][github-547]) + - Use `:static` scheduling for threaded `for`-loop ([#534]) + - Remove use of `@inbounds` ([#547]) - Unification of `create_sparsity_pattern` methods to remove code duplication between - `DofHandler` and `MixedDofHandler`. ([#538][github-538], [#540][github-540]) + `DofHandler` and `MixedDofHandler`. ([#538], [#540]) -## [0.3.9] - 2022-10-19 +## [v0.3.9] - 2022-10-19 ### Added - New higher order function interpolations for triangles (`Lagrange{2,RefTetrahedron,3}`, - `Lagrange{2,RefTetrahedron,4}`, and `Lagrange{2,RefTetrahedron,5}`). ([#482][github-482], - [#512][github-512]) - - New Gaussian quadrature formula for triangles up to order 15. ([#514][github-514]) - - Add debug mode for working with Ferrite internals. ([#524][github-524]) + `Lagrange{2,RefTetrahedron,4}`, and `Lagrange{2,RefTetrahedron,5}`). ([#482], [#512]) + - New Gaussian quadrature formula for triangles up to order 15. ([#514]) + - Add debug mode for working with Ferrite internals. ([#524]) ### Changed - The default components to constrain in `Dirichlet` and `PeriodicDirichlet` have changed from component 1 to all components of the field. For scalar problems this has no effect. - ([#506][github-506], [#509][github-509]) + ([#506], [#509]) -## [0.3.8] - 2022-10-05 +## [v0.3.8] - 2022-10-05 ### Added - - Ferrite.jl now has a logo! ([#464][github-464]) + - Ferrite.jl now has a logo! ([#464]) - New keyword argument `search_nneighbors::Int` in `PointEvalHandler` for specifying how many neighboring elements to consider in the kNN search. The default is still 3 (usually - sufficient). ([#466][github-466]) - - The IJV-assembler now support assembling non-square matrices. ([#471][github-471]) + sufficient). ([#466]) + - The IJV-assembler now support assembling non-square matrices. ([#471]) - Periodic boundary conditions have been reworked and generalized. It now supports arbitrary relations between the mirror and image boundaries (e.g. not only translations - in x/y/z direction). ([#478][github-478], [#481][github-481], [#496][github-496], - [#501][github-501]) + in x/y/z direction). ([#478], [#481], [#496], [#501]) ### Fixed - - Fix `PointEvalHandler` when the first point is missing. ([#466][github-466]) - - Fix the ordering of nodes on the face for `(Quadratic)Tetrahedron` cells. - ([#475][github-475]) + - Fix `PointEvalHandler` when the first point is missing. ([#466]) + - Fix the ordering of nodes on the face for `(Quadratic)Tetrahedron` cells. ([#475]) ### Other improvements - - Many improvements to the documentation. ([#467][github-467], [#473][github-473], - [#487][github-487], [#494][github-494], [#500][github-500]) + - Many improvements to the documentation. ([#467], [#473], [#487], [#494], [#500]) - Improved error messages in `reinit!` when number of geometric base functions and number - of element coordinates mismatch. ([#469][github-469]) - - Remove some unnecessary function parametrizations. ([#503][github-503]) - - Remove some unnecessary allocations in grid coloring. ([#505][github-505]) + of element coordinates mismatch. ([#469]) + - Remove some unnecessary function parametrizations. ([#503]) + - Remove some unnecessary allocations in grid coloring. ([#505]) - More efficient way of creating the sparsity pattern when using `AffineConstraints` and/or - `PeriodicDirichlet`. ([#436][github-436]) + `PeriodicDirichlet`. ([#436]) -## [0.3.7] - 2022-07-05 +## [v0.3.7] - 2022-07-05 ### Fixed -- Fix tests for newer version of WriteVTK (no functional change). ([#462][github-462]) +- Fix tests for newer version of WriteVTK (no functional change). ([#462]) ### Other improvements - Various improvements to the heat equation example and the hyperelasticity example in the - documentation. ([#460][github-460], [#461][github-461]) + documentation. ([#460], [#461]) -## [0.3.6] - 2022-06-30 +## [v0.3.6] - 2022-06-30 ### Fixed -- Fix a bug with `L2Projection` of mixed grid. ([#456][github-456]) +- Fix a bug with `L2Projection` of mixed grid. ([#456]) ### Other improvements - - Expanded manual section of Dirichlet BCs. ([#458][github-458]) + - Expanded manual section of Dirichlet BCs. ([#458]) -## [0.3.5] - 2022-05-30 +## [v0.3.5] - 2022-05-30 ### Added - Functionality for querying information about the grid topology (e.g. neighboring cells, - boundaries, ...). ([#363][github-363]) + boundaries, ...). ([#363]) ### Fixed - Fix application of boundary conditions when combining RHSData and affine constraints. - ([#431][github-431]) + ([#431]) -## [0.3.4] - 2022-02-25 +## [v0.3.4] - 2022-02-25 ### Added -- Affine (linear) constraints between degrees-of-freedom. ([#401][github-401]) -- Periodic Dirichlet boundary conditions. ([#418][github-418]) -- Evaluation of arbitrary quantities in FE space. ([#425][github-425]) +- Affine (linear) constraints between degrees-of-freedom. ([#401]) +- Periodic Dirichlet boundary conditions. ([#418]) +- Evaluation of arbitrary quantities in FE space. ([#425]) ### Changed - Interpolation(s) and the quadrature rule are now stored as part of the `CellValues` - structs (`cv.func_interp`, `cv.geo_interp`, and `cv.qr`). ([#428][github-428]) + structs (`cv.func_interp`, `cv.geo_interp`, and `cv.qr`). ([#428]) -## [0.3.3] - 2022-02-04 +## [v0.3.3] - 2022-02-04 ### Changed - Verify user input in various functions to eliminate possible out-of-bounds accesses. - ([#407][github-407], [#411][github-411]) + ([#407], [#411]) -## [0.3.2] - 2022-01-18 +## [v0.3.2] - 2022-01-18 ### Added - Support for new interpolation types: `DiscontinuousLagrange`, `BubbleEnrichedLagrange`, - and `CrouzeixRaviart`. ([#352][github-352], [#392][github-392]) + and `CrouzeixRaviart`. ([#352], [#392]) ### Changed - Julia version 1.0 is no longer supported for Ferrite versions >= 0.3.2. - Use Julia version >= 1.6. ([#385][github-385]) + Use Julia version >= 1.6. ([#385]) - Quadrature data for L2 projection can now be given as a matrix of size "number of - elements" x "number of quadrature points per element". ([#386][github-386]) -- Projected values from L2 projection can now be exported directly to VTK. - ([#390][github-390]) -- Grid coloring can now act on a subset of cells. ([#402][github-402]) + elements" x "number of quadrature points per element". ([#386]) +- Projected values from L2 projection can now be exported directly to VTK. ([#390]) +- Grid coloring can now act on a subset of cells. ([#402]) - Various functions related to cell values now use traits to make it easier to extend and - reuse functionality in external code. ([#404][github-404]) + reuse functionality in external code. ([#404]) ### Fixed -- Exporting tensors to VTK now use correct names for the components. ([#406][github-406]) - - - - -[Unreleased]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v1.3.14...HEAD -[1.0.0]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.14...HEAD -[0.3.14]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.13...v0.3.14 -[0.3.13]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.12...v0.3.13 -[0.3.12]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.11...v0.3.12 -[0.3.11]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.10...v0.3.11 -[0.3.10]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.9...v0.3.10 -[0.3.9]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.8...v0.3.9 -[0.3.8]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.7...v0.3.8 -[0.3.7]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.6...v0.3.7 -[0.3.6]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.5...v0.3.6 -[0.3.5]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.4...v0.3.5 -[0.3.4]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.3...v0.3.4 -[0.3.3]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.2...v0.3.3 -[0.3.2]: https://github.com/Ferrite-FEM/Ferrite.jl/compare/v0.3.1...v0.3.2 - - - - -[github-352]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/352 -[github-363]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/363 -[github-378]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/378 -[github-385]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/385 -[github-386]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/386 -[github-388]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/388 -[github-390]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/390 -[github-392]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/392 -[github-393]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/393 -[github-401]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/401 -[github-402]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/402 -[github-404]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/404 -[github-406]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/406 -[github-407]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/407 -[github-411]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/411 -[github-418]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/418 -[github-425]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/425 -[github-428]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/428 -[github-431]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/431 -[github-436]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/436 -[github-444]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/444 -[github-453]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/453 -[github-455]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/455 -[github-456]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/456 -[github-458]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/458 -[github-459]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/459 -[github-460]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/460 -[github-461]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/461 -[github-462]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/462 -[github-464]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/464 -[github-465]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/465 -[github-466]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/466 -[github-467]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/467 -[github-469]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/469 -[github-471]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/471 -[github-473]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/473 -[github-475]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/475 -[github-478]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/478 -[github-481]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/481 -[github-482]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/482 -[github-487]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/487 -[github-489]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/489 -[github-494]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/494 -[github-495]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/495 -[github-496]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/496 -[github-500]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/500 -[github-501]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/501 -[github-503]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/503 -[github-505]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/505 -[github-506]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/506 -[github-509]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/509 -[github-512]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/512 -[github-514]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/514 -[github-518]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/518 -[github-520]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/520 -[github-523]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/523 -[github-524]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/524 -[github-528]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/528 -[github-529]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/529 -[github-530]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/530 -[github-531]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/531 -[github-532]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/532 -[github-533]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/533 -[github-534]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/534 -[github-535]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/535 -[github-536]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/536 -[github-537]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/537 -[github-538]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/538 -[github-539]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/539 -[github-540]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/540 -[github-541]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/541 -[github-542]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/542 -[github-543]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/543 -[github-544]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/544 -[github-545]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/545 -[github-546]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/546 -[github-547]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/547 -[github-549]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/549 -[github-550]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/550 -[github-553]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/553 -[github-559]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/559 -[github-562]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/562 -[github-563]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/563 -[github-567]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/567 -[github-570]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/570 -[github-571]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/571 -[github-574]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/574 -[github-575]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/575 -[github-577]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/577 -[github-578]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/578 -[github-581]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/581 -[github-582]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/582 -[github-583]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/583 -[github-588]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/588 -[github-591]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/591 -[github-592]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/592 -[github-593]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/593 -[github-594]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/594 -[github-598]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/598 -[github-599]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/599 -[github-600]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/600 -[github-601]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/601 -[github-602]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/602 -[github-604]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/604 -[github-606]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/606 -[github-611]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/611 -[github-614]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/614 -[github-621]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/621 -[github-624]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/624 -[github-627]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/627 -[github-629]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/629 -[github-633]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/633 -[github-637]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/637 -[github-639]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/639 -[github-641]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/641 -[github-642]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/642 -[github-643]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/643 -[github-645]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/645 -[github-647]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/647 -[github-650]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/650 -[github-651]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/651 -[github-653]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/653 -[github-655]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/655 -[github-656]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/656 -[github-658]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/658 -[github-659]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/659 -[github-660]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/660 -[github-667]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/667 -[github-670]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/670 -[github-675]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/675 -[github-679]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/679 -[github-684]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/684 -[github-687]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/687 -[github-688]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/688 -[github-692]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/692 -[github-694]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/694 -[github-695]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/695 -[github-697]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/697 -[github-698]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/698 -[github-699]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/699 -[github-700]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/700 -[github-701]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/701 -[github-703]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/703 -[github-706]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/706 -[github-707]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/707 -[github-708]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/708 -[github-710]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/710 -[github-711]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/711 -[github-712]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/712 -[github-714]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/714 -[github-716]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/716 -[github-719]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/719 -[github-720]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/720 -[github-721]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/721 -[github-726]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/726 -[github-729]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/729 -[github-731]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/731 -[github-732]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/732 -[github-733]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/733 -[github-735]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/735 -[github-736]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/736 -[github-737]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/737 -[github-743]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/743 -[github-747]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/747 -[github-749]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/749 -[github-751]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/751 -[github-753]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/753 -[github-756]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/756 -[github-759]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/759 -[github-779]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/779 -[github-789]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/789 -[github-835]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/835 -[github-855]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/855 -[github-880]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/880 -[github-888]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/888 -[github-914]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/914 -[github-924]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/924 -[github-938]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/938 -[github-943]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/943 -[github-949]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/949 -[github-953]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/953 -[github-974]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/974 -[github-1059]: https://github.com/Ferrite-FEM/Ferrite.jl/pull/1059 +- Exporting tensors to VTK now use correct names for the components. ([#406]) + + + + +[v0.3.2]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.2 +[v0.3.3]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.3 +[v0.3.4]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.4 +[v0.3.5]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.5 +[v0.3.6]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.6 +[v0.3.7]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.7 +[v0.3.8]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.8 +[v0.3.9]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.9 +[v0.3.10]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.10 +[v0.3.11]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.11 +[v0.3.12]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.12 +[v0.3.13]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.13 +[v0.3.14]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v0.3.14 +[v1.0.0]: https://github.com/Ferrite-FEM/Ferrite.jl/releases/tag/v1.0.0 +[#352]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/352 +[#363]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/363 +[#378]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/378 +[#385]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/385 +[#386]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/386 +[#388]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/388 +[#390]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/390 +[#392]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/392 +[#393]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/393 +[#401]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/401 +[#402]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/402 +[#404]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/404 +[#406]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/406 +[#407]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/407 +[#411]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/411 +[#418]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/418 +[#425]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/425 +[#428]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/428 +[#431]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/431 +[#436]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/436 +[#444]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/444 +[#453]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/453 +[#455]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/455 +[#456]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/456 +[#458]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/458 +[#459]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/459 +[#460]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/460 +[#461]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/461 +[#462]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/462 +[#464]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/464 +[#465]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/465 +[#466]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/466 +[#467]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/467 +[#469]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/469 +[#471]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/471 +[#473]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/473 +[#475]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/475 +[#478]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/478 +[#481]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/481 +[#482]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/482 +[#487]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/487 +[#489]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/489 +[#494]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/494 +[#495]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/495 +[#496]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/496 +[#500]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/500 +[#501]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/501 +[#503]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/503 +[#505]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/505 +[#506]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/506 +[#509]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/509 +[#512]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/512 +[#514]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/514 +[#518]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/518 +[#520]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/520 +[#523]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/523 +[#524]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/524 +[#528]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/528 +[#529]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/529 +[#530]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/530 +[#531]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/531 +[#532]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/532 +[#533]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/533 +[#534]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/534 +[#535]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/535 +[#536]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/536 +[#537]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/537 +[#538]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/538 +[#539]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/539 +[#540]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/540 +[#541]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/541 +[#542]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/542 +[#543]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/543 +[#544]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/544 +[#545]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/545 +[#546]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/546 +[#547]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/547 +[#549]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/549 +[#550]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/550 +[#553]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/553 +[#559]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/559 +[#562]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/562 +[#563]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/563 +[#567]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/567 +[#570]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/570 +[#571]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/571 +[#574]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/574 +[#575]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/575 +[#577]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/577 +[#578]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/578 +[#581]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/581 +[#582]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/582 +[#583]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/583 +[#588]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/588 +[#591]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/591 +[#592]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/592 +[#593]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/593 +[#594]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/594 +[#598]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/598 +[#599]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/599 +[#600]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/600 +[#601]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/601 +[#602]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/602 +[#604]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/604 +[#606]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/606 +[#611]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/611 +[#614]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/614 +[#621]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/621 +[#624]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/624 +[#627]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/627 +[#629]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/629 +[#633]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/633 +[#637]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/637 +[#639]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/639 +[#641]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/641 +[#642]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/642 +[#643]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/643 +[#645]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/645 +[#647]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/647 +[#650]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/650 +[#651]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/651 +[#653]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/653 +[#655]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/655 +[#656]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/656 +[#658]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/658 +[#659]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/659 +[#660]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/660 +[#667]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/667 +[#670]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/670 +[#675]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/675 +[#679]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/679 +[#684]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/684 +[#688]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/688 +[#692]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/692 +[#694]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/694 +[#695]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/695 +[#697]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/697 +[#698]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/698 +[#699]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/699 +[#700]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/700 +[#701]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/701 +[#703]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/703 +[#706]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/706 +[#707]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/707 +[#708]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/708 +[#710]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/710 +[#711]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/711 +[#712]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/712 +[#714]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/714 +[#716]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/716 +[#719]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/719 +[#720]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/720 +[#721]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/721 +[#726]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/726 +[#729]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/729 +[#731]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/731 +[#732]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/732 +[#733]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/733 +[#735]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/735 +[#736]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/736 +[#737]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/737 +[#743]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/743 +[#747]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/747 +[#749]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/749 +[#751]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/751 +[#753]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/753 +[#754]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/754 +[#756]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/756 +[#759]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/759 +[#779]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/779 +[#835]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/835 +[#855]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/855 +[#880]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/880 +[#888]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/888 +[#914]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/914 +[#924]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/924 +[#943]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/943 +[#949]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/949 +[#953]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/953 +[#974]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/974 +[#1059]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1059 diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 0fc5f508d6..b7189a2af2 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.10.4" +julia_version = "1.10.5" manifest_format = "2.0" -project_hash = "2283fea1dfcfa0f2c82cdecbc5750875c26d6009" +project_hash = "864d1d29886fc101f4919a6d1c30efd8f4e2ca2e" [[deps.ADTypes]] git-tree-sha1 = "ae44a0c3d68a420d4ac0fa1f7e0c034ccecb6dc5" @@ -177,6 +177,11 @@ weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] ChainRulesCoreSparseArraysExt = "SparseArrays" +[[deps.Changelog]] +git-tree-sha1 = "e579c6157598169ad4ef17263bdf3452b4a3e316" +uuid = "5217a498-cd5d-4ec6-b8c2-9b85a09b6e3e" +version = "1.1.0" + [[deps.CloseOpenIntervals]] deps = ["Static", "StaticArrayInterface"] git-tree-sha1 = "05ba0d07cd4fd8b7a39541e31a7b0254704ea581" diff --git a/docs/Project.toml b/docs/Project.toml index 4b6b129d19..944be43ceb 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,5 +1,6 @@ [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +Changelog = "5217a498-cd5d-4ec6-b8c2-9b85a09b6e3e" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" diff --git a/docs/changelog.jl b/docs/changelog.jl index 7fc227c44f..2d47c91c47 100644 --- a/docs/changelog.jl +++ b/docs/changelog.jl @@ -1,53 +1,7 @@ -const changelogfile = joinpath(@__DIR__, "../CHANGELOG.md") +using Changelog -function create_documenter_changelog() - content = read(changelogfile, String) - # Replace release headers - content = replace(content, "## [Unreleased]" => "## Changes yet to be released") - content = replace(content, r"## \[(\d+\.\d+\.\d+)\]" => s"## Version \1") - # Replace [#XXX][github-XXX] with the proper links - content = replace(content, r"(\[#(\d+)\])\[github-\d+\]" => s"\1(https://github.com/Ferrite-FEM/Ferrite.jl/pull/\2)") - # Remove all links at the bottom - content = replace(content, r"^.*$"ms => "") - # Change some GitHub in-readme links to documenter links - content = replace(content, "(#upgrading-code-from-ferrite-03-to-10)" => "(@ref)") - # Add a contents block - last_sentence_before_content = "adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." - contents_block = """ - ```@contents - Pages = ["changelog.md"] - Depth = 2:2 - ``` - """ - content = replace(content, last_sentence_before_content => last_sentence_before_content * "\n\n" * contents_block) - # Remove trailing lines - content = strip(content) * "\n" - # Write out the content - write(joinpath(@__DIR__, "src/changelog.md"), content) - return nothing -end - -function fix_links() - content = read(changelogfile, String) - text = split(content, "")[1] - # Look for links of the form: [#XXX][github-XXX] - github_links = Dict{String, String}() - r = r"\[#(\d+)\](\[github-(\d+)\])" - for m in eachmatch(r, text) - @assert m[1] == m[3] - # Always use /pull/ since it will redirect to /issues/ if it is an issue - url = "https://github.com/Ferrite-FEM/Ferrite.jl/pull/$(m[1])" - github_links[m[2]] = url - end - io = IOBuffer() - print(io, "\n\n") - for l in sort!(collect(github_links); by = first) - println(io, l[1], ": ", l[2]) - end - content = replace(content, r".*$"ms => String(take!(io))) - write(changelogfile, content) -end - -if abspath(PROGRAM_FILE) == @__FILE__ - fix_links() -end +Changelog.generate( + Changelog.CommonMark(), + joinpath(@__DIR__, "..", "CHANGELOG.md"); + repo = "Ferrite-FEM/Ferrite.jl", +) diff --git a/docs/make.jl b/docs/make.jl index 7cdeb1852b..8f85d58842 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -10,7 +10,8 @@ if liveserver @timeit dto "Revise.revise()" Revise.revise() end -using Documenter, DocumenterCitations, Ferrite, FerriteGmsh, FerriteMeshParser, SparseArrays, LinearAlgebra +using Documenter, DocumenterCitations, Ferrite, FerriteGmsh, FerriteMeshParser, + SparseArrays, LinearAlgebra, Changelog using BlockArrays const FerriteBlockArrays = Base.get_extension(Ferrite, :FerriteBlockArrays) @@ -21,8 +22,12 @@ const is_ci = haskey(ENV, "GITHUB_ACTIONS") include("generate.jl") # Changelog -include("changelog.jl") -create_documenter_changelog() +Changelog.generate( + Changelog.Documenter(), + joinpath(@__DIR__, "..", "CHANGELOG.md"), + joinpath(@__DIR__, "src", "changelog.md"); + repo = "Ferrite-FEM/Ferrite.jl", +) bibtex_plugin = CitationBibliography( joinpath(@__DIR__, "src", "assets", "references.bib"), @@ -46,7 +51,7 @@ bibtex_plugin = CitationBibliography( draft = liveserver, pages = Any[ "Home" => "index.md", - # hide("Changelog" => "changelog.md"), + hide("Changelog" => "changelog.md"), "Tutorials" => [ "Tutorials overview" => "tutorials/index.md", "tutorials/heat_equation.md", diff --git a/docs/src/index.md b/docs/src/index.md index 30422242c3..b4656107c2 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -9,6 +9,11 @@ provides functionalities to implement finite element analysis in [Julia](https://github.com/JuliaLang/julia). The aim is to be i) general, ii) performant, and iii) to keep mathematical abstractions. +!!! note "Upgrading code from version 0.3.x to version 1.0" + Ferrite version 1.0 contains a number of breaking changes compared to version 0.3.x. The + [Changelog](changelog.md) documents all changes and there is also a section specifically + for [Upgrading code from Ferrite 0.3 to 1.0](@ref). + !!! note Please help improve this documentation -- if something confuses you, chances are you're not alone. It's easy to do as you read along: just click on the "Edit on GitHub" link at @@ -36,9 +41,15 @@ for. The document is organized as follows[^1]: [^1]: The organization of the document follows the [Diátaxis Framework](https://diataxis.fr). -In addition there is a [**Code gallery**](gallery/index.md), with user contributed example -programs, and the [**Developer documentation**](devdocs/index.md), for documentation of -Ferrite internal code. +The four sections above form the main user-facing parts of the documentation. In addition, +the document also contain the following sections: + + - [**Code gallery**](gallery/index.md) contain user contributed example programs showcasing + what can be done with Ferrite. + - [**Changelog**](changelog.md) contain release notes and information about how to upgrade + between releases. + - [**Developer documentation**](devdocs/index.md) contain documentation of Ferrite internal + code and is mainly targeted at developers of Ferrite. ## Getting started From 336ac150428b66abf1edfdb1524b257639049a11 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 20 Sep 2024 12:53:54 +0200 Subject: [PATCH 29/50] Update doc build dependencies. (#1062) --- docs/Manifest.toml | 633 +++++++++++++++++++++++++++++++-------------- 1 file changed, 439 insertions(+), 194 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index b7189a2af2..4b67ef2b1f 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -5,9 +5,9 @@ manifest_format = "2.0" project_hash = "864d1d29886fc101f4919a6d1c30efd8f4e2ca2e" [[deps.ADTypes]] -git-tree-sha1 = "ae44a0c3d68a420d4ac0fa1f7e0c034ccecb6dc5" +git-tree-sha1 = "5a5eafb8344b81b8c2237f8a6f6b3602b3f6180e" uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" -version = "1.5.2" +version = "1.8.1" weakdeps = ["ChainRulesCore", "EnzymeCore"] [deps.ADTypes.extensions] @@ -25,24 +25,28 @@ uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" version = "0.4.5" [[deps.Accessors]] -deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown", "Test"] -git-tree-sha1 = "c0d491ef0b135fd7d63cbc6404286bc633329425" +deps = ["CompositionsBase", "ConstructionBase", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown"] +git-tree-sha1 = "b392ede862e506d451fc1616e79aa6f4c673dab8" uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" -version = "0.1.36" +version = "0.1.38" [deps.Accessors.extensions] AccessorsAxisKeysExt = "AxisKeys" + AccessorsDatesExt = "Dates" AccessorsIntervalSetsExt = "IntervalSets" AccessorsStaticArraysExt = "StaticArrays" AccessorsStructArraysExt = "StructArrays" + AccessorsTestExt = "Test" AccessorsUnitfulExt = "Unitful" [deps.Accessors.weakdeps] AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" + Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" Requires = "ae029012-a4dd-5104-9daa-d747884805df" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [[deps.Adapt]] @@ -66,10 +70,10 @@ uuid = "ec485272-7323-5ecc-a04f-4719b315124d" version = "0.4.0" [[deps.ArrayInterface]] -deps = ["Adapt", "LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "ed2ec3c9b483842ae59cd273834e5b46206d6dda" +deps = ["Adapt", "LinearAlgebra"] +git-tree-sha1 = "3640d077b6dafd64ceb8fd5c1ec76f7ca53bcf76" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.11.0" +version = "7.16.0" [deps.ArrayInterface.extensions] ArrayInterfaceBandedMatricesExt = "BandedMatrices" @@ -79,6 +83,7 @@ version = "7.11.0" ArrayInterfaceChainRulesExt = "ChainRules" ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" ArrayInterfaceReverseDiffExt = "ReverseDiff" + ArrayInterfaceSparseArraysExt = "SparseArrays" ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" ArrayInterfaceTrackerExt = "Tracker" @@ -90,14 +95,15 @@ version = "7.11.0" ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" [[deps.ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "600078184f7de14b3e60efe13fc0ba5c59f6dca5" +git-tree-sha1 = "0dd7edaff278e346eb0ca07a7e75c9438408a3ce" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.10.0" +version = "1.10.3" weakdeps = ["SparseArrays"] [deps.ArrayLayouts.extensions] @@ -110,9 +116,9 @@ uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[deps.BibInternal]] -git-tree-sha1 = "0c62b284a52ec39ee831e10bf62c17d587dde75f" +git-tree-sha1 = "78aa378482bf6f338eef8f2440fb62a75ab1aaa3" uuid = "2027ae74-3657-4b95-ae00-e2f7d55c3e64" -version = "0.3.5" +version = "0.3.6" [[deps.BibParser]] deps = ["BibInternal", "DataStructures", "Dates", "JSONSchema", "YAML"] @@ -139,9 +145,9 @@ version = "0.1.6" [[deps.BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "5c0ffe1dff8cb7112de075f1b1cb32191675fcba" +git-tree-sha1 = "d434647f798823bcae510aee0bc0401927f64391" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "1.1.0" +version = "1.1.1" [deps.BlockArrays.extensions] BlockArraysBandedMatricesExt = "BandedMatrices" @@ -169,9 +175,9 @@ version = "1.18.0+2" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "71acdbf594aab5bbb2cec89b208c41b4c411e49f" +git-tree-sha1 = "3e4b134270b372f2ed4d4d0e936aabaefc1802bc" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.24.0" +version = "1.25.0" weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] @@ -190,15 +196,15 @@ version = "0.1.13" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73" +git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.4" +version = "0.7.6" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "4b270d6465eb21ae89b732182c20dc165f8bf9f2" +git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.25.0" +version = "3.26.0" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] @@ -228,10 +234,10 @@ uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" version = "0.2.4" [[deps.CommonSubexpressions]] -deps = ["MacroTools", "Test"] -git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7" +deps = ["MacroTools"] +git-tree-sha1 = "cda2cfaebb4be89c9084adaca7dd7333369715c5" uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" -version = "0.3.0" +version = "0.3.1" [[deps.CommonWorldInvalidations]] git-tree-sha1 = "ae52d1c52048455e85a387fbee9be553ec2b68d0" @@ -240,9 +246,9 @@ version = "1.0.0" [[deps.Compat]] deps = ["TOML", "UUIDs"] -git-tree-sha1 = "b1c55339b7c6c350ee89f2c1604299660525b248" +git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.15.0" +version = "4.16.0" weakdeps = ["Dates", "LinearAlgebra"] [deps.Compat.extensions] @@ -269,22 +275,23 @@ version = "0.2.3" [[deps.ConcurrentUtilities]] deps = ["Serialization", "Sockets"] -git-tree-sha1 = "6cbbd4d241d7e6579ab354737f4dd95ca43946e1" +git-tree-sha1 = "ea32b83ca4fefa1768dc84e504cc0a94fb1ab8d1" uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.4.1" +version = "2.4.2" [[deps.ConstructionBase]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "260fd2400ed2dab602a7c15cf10c1933c59930a2" +git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.5" +version = "1.5.8" [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" + ConstructionBaseLinearAlgebraExt = "LinearAlgebra" ConstructionBaseStaticArraysExt = "StaticArrays" [deps.ConstructionBase.weakdeps] IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [[deps.Contour]] @@ -318,6 +325,12 @@ version = "1.0.0" deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +[[deps.Dbus_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "fc173b380865f70627d7dd1190dc2fce6cc105af" +uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" +version = "1.14.10+0" + [[deps.DelimitedFiles]] deps = ["Mmap"] git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" @@ -325,10 +338,10 @@ uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" version = "1.9.1" [[deps.DiffEqBase]] -deps = ["ArrayInterface", "ConcreteStructs", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces"] -git-tree-sha1 = "d1e8a4642e28b0945bde6e2e1ac569b9e0abd728" +deps = ["ArrayInterface", "ConcreteStructs", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces"] +git-tree-sha1 = "fa7d580038451a8df4434ef5b079ac9b2d486194" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.151.5" +version = "6.155.1" [deps.DiffEqBase.extensions] DiffEqBaseCUDAExt = "CUDA" @@ -340,6 +353,7 @@ version = "6.151.5" DiffEqBaseMeasurementsExt = "Measurements" DiffEqBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" DiffEqBaseReverseDiffExt = "ReverseDiff" + DiffEqBaseSparseArraysExt = "SparseArrays" DiffEqBaseTrackerExt = "Tracker" DiffEqBaseUnitfulExt = "Unitful" @@ -353,6 +367,7 @@ version = "6.151.5" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" @@ -370,9 +385,9 @@ version = "1.15.1" [[deps.DifferentiationInterface]] deps = ["ADTypes", "Compat", "DocStringExtensions", "FillArrays", "LinearAlgebra", "PackageExtensionCompat", "SparseArrays", "SparseMatrixColorings"] -git-tree-sha1 = "695217e97ee1ce0248f4a56c14af88ba33c585fd" +git-tree-sha1 = "9b23f9a816790b8ab9914c3c86321a546e92cbe7" uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" -version = "0.5.7" +version = "0.5.17" [deps.DifferentiationInterface.extensions] DifferentiationInterfaceChainRulesCoreExt = "ChainRulesCore" @@ -427,15 +442,15 @@ version = "0.9.3" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "76deb8c15f37a3853f13ea2226b8f2577652de05" +git-tree-sha1 = "5a1ee886566f2fa9318df1273d8b778b9d42712d" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.5.0" +version = "1.7.0" [[deps.DocumenterCitations]] deps = ["AbstractTrees", "Bibliography", "Dates", "Documenter", "Logging", "Markdown", "MarkdownAST", "OrderedCollections", "Unicode"] -git-tree-sha1 = "c72ee44a4242d8ad932062e7476880243635ce6d" +git-tree-sha1 = "ca601b812efd1155a9bdf9c80e7e0428da598a08" uuid = "daee34ce-89f3-4625-b898-19384cb65244" -version = "1.3.3" +version = "1.3.4" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] @@ -448,9 +463,9 @@ uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.4" [[deps.EnzymeCore]] -git-tree-sha1 = "3a3177ba05b4763234819060fb6c2e1613379ca6" +git-tree-sha1 = "8f205a601760f4798a10f138c3940f0451d95188" uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" -version = "0.7.6" +version = "0.7.8" weakdeps = ["Adapt"] [deps.EnzymeCore.extensions] @@ -485,6 +500,12 @@ git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" version = "0.1.10" +[[deps.Expronicon]] +deps = ["MLStyle", "Pkg", "TOML"] +git-tree-sha1 = "fc3951d4d398b5515f91d7fe5d45fc31dccb3c9b" +uuid = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636" +version = "0.8.5" + [[deps.FFMPEG]] deps = ["FFMPEG_jll"] git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8" @@ -505,9 +526,9 @@ version = "1.3.8+0" [[deps.FastBroadcast]] deps = ["ArrayInterface", "LinearAlgebra", "Polyester", "Static", "StaticArrayInterface", "StrideArraysCore"] -git-tree-sha1 = "bd19de6fe8a3b18888f35e79832f97544684caa7" +git-tree-sha1 = "ab1b34570bcdf272899062e1a56285a53ecaae08" uuid = "7034ab61-46d4-4ed7-9d0f-46aef9175898" -version = "0.3.4" +version = "0.3.5" [[deps.FastClosures]] git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" @@ -536,9 +557,9 @@ version = "1.0.0" [[deps.FerriteGmsh]] deps = ["Ferrite", "Gmsh"] -git-tree-sha1 = "5f56dd2b58ca0f79e28d9bb76fb0e6183b0aca65" +git-tree-sha1 = "9669f21d4ddc68ffca0d5ea12d3ac6b438b9af06" uuid = "4f95f4f8-b27c-4ae5-9a39-ea55e634e36b" -version = "1.2.0" +version = "1.2.1" [[deps.FerriteMeshParser]] deps = ["Ferrite"] @@ -557,9 +578,9 @@ uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[deps.FillArrays]] deps = ["LinearAlgebra"] -git-tree-sha1 = "0653c0a2396a6da5bc4766c43041ef5fd3efbe57" +git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.11.0" +version = "1.13.0" [deps.FillArrays.extensions] FillArraysPDMatsExt = "PDMats" @@ -572,10 +593,10 @@ version = "1.11.0" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [[deps.FiniteDiff]] -deps = ["ArrayInterface", "LinearAlgebra", "Requires", "Setfield", "SparseArrays"] -git-tree-sha1 = "2de436b72c3422940cbe1367611d137008af7ec3" +deps = ["ArrayInterface", "LinearAlgebra", "Setfield", "SparseArrays"] +git-tree-sha1 = "f9219347ebf700e77ca1d48ef84e4a82a6701882" uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.23.1" +version = "2.24.0" [deps.FiniteDiff.extensions] FiniteDiffBandedMatricesExt = "BandedMatrices" @@ -642,10 +663,10 @@ deps = ["Random"] uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" [[deps.GLFW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"] -git-tree-sha1 = "ff38ba61beff76b8f4acad8ab0c97ef73bb670cb" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "libdecor_jll", "xkbcommon_jll"] +git-tree-sha1 = "532f9126ad901533af1d4f5c198867227a7bb077" uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.3.9+0" +version = "3.4.0+1" [[deps.GLU_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] @@ -665,16 +686,16 @@ uuid = "46192b85-c4d5-4398-a991-12ede77f4527" version = "0.1.6" [[deps.GR]] -deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] -git-tree-sha1 = "3e527447a45901ea392fe12120783ad6ec222803" +deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] +git-tree-sha1 = "629693584cef594c3f6f99e76e7a7ad17e60e8d5" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.73.6" +version = "0.73.7" [[deps.GR_jll]] deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "182c478a179b267dd7a741b6f8f4c3e0803795d6" +git-tree-sha1 = "a8863b69c2a0859f2c2c87ebdc4c6712e88bdf0d" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.73.6+0" +version = "0.73.7+0" [[deps.GenericSchur]] deps = ["LinearAlgebra", "Printf"] @@ -720,9 +741,9 @@ version = "1.3.14+0" [[deps.Graphs]] deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] -git-tree-sha1 = "334d300809ae0a68ceee3444c6e99ded412bf0b3" +git-tree-sha1 = "ebd18c326fa6cee1efb7da9a3b45cf69da2ed4d9" uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" -version = "1.11.1" +version = "1.11.2" [[deps.Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" @@ -742,10 +763,10 @@ uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" version = "1.10.8" [[deps.HarfBuzz_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] -git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "401e4f3f30f43af2c8478fc008da50096ea5240f" uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "2.8.1+1" +version = "8.3.1+0" [[deps.HostCPUFeatures]] deps = ["BitTwiddlingConvenienceFunctions", "IfElse", "Libdl", "Static"] @@ -755,9 +776,9 @@ version = "0.1.17" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1d334207121865ac8c1c97eb7f42d0339e4635bf" +git-tree-sha1 = "5e19e1e4fa3e71b774ce746274364aef0234634e" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.0+0" +version = "2.11.1+0" [[deps.IOCapture]] deps = ["Logging", "Random"] @@ -776,24 +797,24 @@ uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" version = "0.1.5" [[deps.IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "be50fe8df3acbffa0274a744f1a99d29c45a57f4" +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.1.0+0" +version = "2024.2.1+0" [[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [[deps.InverseFunctions]] -deps = ["Test"] -git-tree-sha1 = "e7cbed5032c4c397a6ac23d1493f3289e01231c4" +git-tree-sha1 = "2787db24f4e03daf859c6509ff87764e4182f7d1" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.14" -weakdeps = ["Dates"] +version = "0.1.16" +weakdeps = ["Dates", "Test"] [deps.InverseFunctions.extensions] - DatesExt = "Dates" + InverseFunctionsDatesExt = "Dates" + InverseFunctionsTestExt = "Test" [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" @@ -813,15 +834,15 @@ version = "1.0.0" [[deps.JLFzf]] deps = ["Pipe", "REPL", "Random", "fzf_jll"] -git-tree-sha1 = "a53ebe394b71470c7f97c2e7e170d51df21b17af" +git-tree-sha1 = "39d64b09147620f5ffbf6b2d3255be3c901bec63" uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" -version = "0.1.7" +version = "0.1.8" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" +git-tree-sha1 = "f389674c99bfcde17dc57454011aa44d5a260a40" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.5.0" +version = "1.6.0" [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] @@ -843,9 +864,9 @@ version = "1.14.0" [[deps.JSONSchema]] deps = ["Downloads", "JSON", "JSON3", "URIs"] -git-tree-sha1 = "5f0bd0cd69df978fa64ccdcb5c152fbc705455a1" +git-tree-sha1 = "243f1cdb476835d7c249deb9f29ad6b7827da7d3" uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" -version = "1.3.0" +version = "1.4.1" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -879,9 +900,9 @@ version = "3.0.0+1" [[deps.LLVMOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "d986ce2d884d49126836ea94ed5bfb0f12679713" +git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929" uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "15.0.7+0" +version = "18.1.7+0" [[deps.LZO_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -896,16 +917,18 @@ version = "1.3.1" [[deps.Latexify]] deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] -git-tree-sha1 = "e0b5cd21dc1b44ec6e64f351976f961e6f31d6c4" +git-tree-sha1 = "ce5f5621cac23a86011836badfedf664a612cee4" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.3" +version = "0.16.5" [deps.Latexify.extensions] DataFramesExt = "DataFrames" + SparseArraysExt = "SparseArrays" SymEngineExt = "SymEngine" [deps.Latexify.weakdeps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" [[deps.LayoutPointers]] @@ -921,9 +944,9 @@ version = "1.2.2" [[deps.LazyArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra", "MacroTools", "SparseArrays"] -git-tree-sha1 = "fb43bbe51db62510b032b85e157ea87d77b2fa07" +git-tree-sha1 = "360f6039babd6e4d6364eff0d4fc9120834a2d9a" uuid = "5078a376-72f3-5289-bfd5-ec5146d43c02" -version = "2.1.0" +version = "2.2.1" [deps.LazyArrays.extensions] LazyArraysBandedMatricesExt = "BandedMatrices" @@ -1024,9 +1047,9 @@ version = "0.9.1" [[deps.LineSearches]] deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "Printf"] -git-tree-sha1 = "7bbea35cec17305fc70a0e5b4641477dc0789d9d" +git-tree-sha1 = "e4c3be53733db1051cc15ecf573b1042b3a712a1" uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" -version = "7.2.0" +version = "7.3.0" [[deps.LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] @@ -1040,9 +1063,9 @@ version = "5.0.0+0" [[deps.LinearSolve]] deps = ["ArrayInterface", "ChainRulesCore", "ConcreteStructs", "DocStringExtensions", "EnumX", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "LazyArrays", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "b2e2dba60642e07c062eb3143770d7e234316772" +git-tree-sha1 = "6c5e4555ac2bc449a28604e184f759d18fc08420" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "2.30.2" +version = "2.34.0" [deps.LinearSolve.extensions] LinearSolveBandedMatricesExt = "BandedMatrices" @@ -1077,9 +1100,9 @@ version = "2.30.2" [[deps.Literate]] deps = ["Base64", "IOCapture", "JSON", "REPL"] -git-tree-sha1 = "596df2daea9c27da81eee63ef2cf101baf10c24c" +git-tree-sha1 = "b9b38448af801760a608b7a7f895f7dcf166f4a5" uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306" -version = "2.18.0" +version = "2.19.1" [[deps.LiveServer]] deps = ["HTTP", "LoggingExtras", "MIMEs", "Pkg", "Sockets", "Test"] @@ -1136,9 +1159,14 @@ version = "0.1.4" [[deps.MKL_jll]] deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "80b2833b56d466b3858d565adcd16a4a05f2089b" +git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.1.0+0" +version = "2024.2.0+0" + +[[deps.MLStyle]] +git-tree-sha1 = "bc38dff0548128765760c79eb7388a4b37fae2c8" +uuid = "d8e11817-5142-5d16-987a-aa16d5891078" +version = "0.4.17" [[deps.MMG_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] @@ -1148,9 +1176,9 @@ version = "5.6.0+0" [[deps.MPICH_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "4099bb6809ac109bfc17d521dad33763bcf026b7" +git-tree-sha1 = "19d4bd098928a3263693991500d05d74dbdc2004" uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.2.1+1" +version = "4.2.2+0" [[deps.MPIPreferences]] deps = ["Libdl", "Preferences"] @@ -1186,10 +1214,14 @@ uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" version = "0.1.2" [[deps.MaybeInplace]] -deps = ["ArrayInterface", "LinearAlgebra", "MacroTools", "SparseArrays"] -git-tree-sha1 = "1b9e613f2ca3b6cdcbfe36381e17ca2b66d4b3a1" +deps = ["ArrayInterface", "LinearAlgebra", "MacroTools"] +git-tree-sha1 = "54e2fdc38130c05b42be423e90da3bade29b74bd" uuid = "bb5d69b7-63fc-4a16-80bd-7e42200c7bdb" -version = "0.1.3" +version = "0.1.4" +weakdeps = ["SparseArrays"] + + [deps.MaybeInplace.extensions] + MaybeInplaceSparseArraysExt = "SparseArrays" [[deps.MbedTLS]] deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] @@ -1255,9 +1287,9 @@ version = "1.2.0" [[deps.NonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "FastBroadcast", "FastClosures", "FiniteDiff", "ForwardDiff", "LazyArrays", "LineSearches", "LinearAlgebra", "LinearSolve", "MaybeInplace", "PrecompileTools", "Preferences", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] -git-tree-sha1 = "3adb1e5945b5a6b1eaee754077f25ccc402edd7f" +git-tree-sha1 = "bcd8812e751326ff1d4b2dd50764b93df51f143b" uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" -version = "3.13.1" +version = "3.14.0" [deps.NonlinearSolve.extensions] NonlinearSolveBandedMatricesExt = "BandedMatrices" @@ -1286,15 +1318,15 @@ version = "3.13.1" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.OCCT_jll]] -deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" +deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "bef34b68c20cc34475c5cb464ab48555e74f4c61" uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" -version = "7.6.2+2" +version = "7.7.2+0" [[deps.OffsetArrays]] -git-tree-sha1 = "e64b4f5ea6b7389f6f046d13d4896a8f9c1ba71e" +git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.0" +version = "1.14.1" weakdeps = ["Adapt"] [deps.OffsetArrays.extensions] @@ -1330,9 +1362,9 @@ version = "1.4.3" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "a028ee3cb5641cccc4c24e90c36b0a4f7707bdf5" +git-tree-sha1 = "1b35263570443fdd9e76c76b7062116e2f374ab8" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.14+0" +version = "3.0.15+0" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -1353,10 +1385,10 @@ version = "1.9.4" MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" [[deps.Opus_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6703a85cb3781bd5909d48730a67205f3f31a575" uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.3.2+0" +version = "1.3.3+0" [[deps.OrderedCollections]] git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" @@ -1364,10 +1396,194 @@ uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" version = "1.6.3" [[deps.OrdinaryDiffEq]] -deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "IfElse", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "6ef13f8b23af28ee2d98226653d8382ab79287ea" +deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqAdamsBashforthMoulton", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqDefault", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqExplicitRK", "OrdinaryDiffEqExponentialRK", "OrdinaryDiffEqExtrapolation", "OrdinaryDiffEqFIRK", "OrdinaryDiffEqFeagin", "OrdinaryDiffEqFunctionMap", "OrdinaryDiffEqHighOrderRK", "OrdinaryDiffEqIMEXMultistep", "OrdinaryDiffEqLinear", "OrdinaryDiffEqLowOrderRK", "OrdinaryDiffEqLowStorageRK", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqNordsieck", "OrdinaryDiffEqPDIRK", "OrdinaryDiffEqPRK", "OrdinaryDiffEqQPRK", "OrdinaryDiffEqRKN", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqSSPRK", "OrdinaryDiffEqStabilizedIRK", "OrdinaryDiffEqStabilizedRK", "OrdinaryDiffEqSymplecticRK", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "cd892f12371c287dc50d6ad3af075b088b6f2d48" uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -version = "6.85.0" +version = "6.89.0" + +[[deps.OrdinaryDiffEqAdamsBashforthMoulton]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqLowOrderRK", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] +git-tree-sha1 = "8e3c5978d0531a961f70d2f2730d1d16ed3bbd12" +uuid = "89bda076-bce5-4f1c-845f-551c83cdda9a" +version = "1.1.0" + +[[deps.OrdinaryDiffEqBDF]] +deps = ["ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "b4498d40bf35da0b6d22652ff2e9d8820590b3c6" +uuid = "6ad6398a-0878-4a85-9266-38940aa047c8" +version = "1.1.2" + +[[deps.OrdinaryDiffEqCore]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "FastBroadcast", "FastClosures", "FillArrays", "FunctionWrappersWrappers", "InteractiveUtils", "LinearAlgebra", "Logging", "MacroTools", "MuladdMacro", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleUnPack", "Static", "StaticArrayInterface", "StaticArraysCore", "TruncatedStacktraces"] +git-tree-sha1 = "5595eb94d0dd3cde2f5cf456394a1e0a61237e95" +uuid = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" +version = "1.6.0" +weakdeps = ["EnzymeCore"] + + [deps.OrdinaryDiffEqCore.extensions] + OrdinaryDiffEqCoreEnzymeCoreExt = "EnzymeCore" + +[[deps.OrdinaryDiffEqDefault]] +deps = ["DiffEqBase", "EnumX", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "PrecompileTools", "Preferences", "Reexport"] +git-tree-sha1 = "c8223e487d58bef28a3535b33ddf8ffdb44f46fb" +uuid = "50262376-6c5a-4cf5-baba-aaf4f84d72d7" +version = "1.1.0" + +[[deps.OrdinaryDiffEqDifferentiation]] +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays"] +git-tree-sha1 = "e63ec633b1efa99e3caa2e26a01faaa88ba6cef9" +uuid = "4302a76b-040a-498a-8c04-15b101fed76b" +version = "1.1.0" + +[[deps.OrdinaryDiffEqExplicitRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "TruncatedStacktraces"] +git-tree-sha1 = "4dbce3f9e6974567082ce5176e21aab0224a69e9" +uuid = "9286f039-9fbf-40e8-bf65-aa933bdc4db0" +version = "1.1.0" + +[[deps.OrdinaryDiffEqExponentialRK]] +deps = ["DiffEqBase", "ExponentialUtilities", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqVerner", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "f63938b8e9e5d3a05815defb3ebdbdcf61ec0a74" +uuid = "e0540318-69ee-4070-8777-9e2de6de23de" +version = "1.1.0" + +[[deps.OrdinaryDiffEqExtrapolation]] +deps = ["DiffEqBase", "FastBroadcast", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "RecursiveArrayTools", "Reexport"] +git-tree-sha1 = "fea595528a160ed5cade9eee217a9691b1d97714" +uuid = "becaefa8-8ca2-5cf9-886d-c06f3d2bd2c4" +version = "1.1.0" + +[[deps.OrdinaryDiffEqFIRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLOperators"] +git-tree-sha1 = "795221c662698851328cb7787965ab4a180d9468" +uuid = "5960d6e9-dd7a-4743-88e7-cf307b64f125" +version = "1.1.1" + +[[deps.OrdinaryDiffEqFeagin]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] +git-tree-sha1 = "a7cc74d3433db98e59dc3d58bc28174c6c290adf" +uuid = "101fe9f7-ebb6-4678-b671-3a81e7194747" +version = "1.1.0" + +[[deps.OrdinaryDiffEqFunctionMap]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "925a91583d1ab84f1f0fea121be1abf1179c5926" +uuid = "d3585ca7-f5d3-4ba6-8057-292ed1abd90f" +version = "1.1.1" + +[[deps.OrdinaryDiffEqHighOrderRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "Static"] +git-tree-sha1 = "103e017ff186ac39d731904045781c9bacfca2b0" +uuid = "d28bc4f8-55e1-4f49-af69-84c1a99f0f58" +version = "1.1.0" + +[[deps.OrdinaryDiffEqIMEXMultistep]] +deps = ["DiffEqBase", "FastBroadcast", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Reexport"] +git-tree-sha1 = "9f8f52aad2399d7714b400ff9d203254b0a89c4a" +uuid = "9f002381-b378-40b7-97a6-27a27c83f129" +version = "1.1.0" + +[[deps.OrdinaryDiffEqLinear]] +deps = ["DiffEqBase", "ExponentialUtilities", "LinearAlgebra", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "0f81a77ede3da0dc714ea61e81c76b25db4ab87a" +uuid = "521117fe-8c41-49f8-b3b6-30780b3f0fb5" +version = "1.1.0" + +[[deps.OrdinaryDiffEqLowOrderRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "d4bb32e09d6b68ce2eb45fb81001eab46f60717a" +uuid = "1344f307-1e59-4825-a18e-ace9aa3fa4c6" +version = "1.2.0" + +[[deps.OrdinaryDiffEqLowStorageRK]] +deps = ["Adapt", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static", "StaticArrays"] +git-tree-sha1 = "590561f3af623d5485d070b4d7044f8854535f5a" +uuid = "b0944070-b475-4768-8dec-fb6eb410534d" +version = "1.2.1" + +[[deps.OrdinaryDiffEqNonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FastClosures", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "PreallocationTools", "RecursiveArrayTools", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "StaticArrays"] +git-tree-sha1 = "a2a4119f3e35f7982f78e17beea7b12485d179e9" +uuid = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8" +version = "1.2.1" + +[[deps.OrdinaryDiffEqNordsieck]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] +git-tree-sha1 = "ef44754f10e0dfb9bb55ded382afed44cd94ab57" +uuid = "c9986a66-5c92-4813-8696-a7ec84c806c8" +version = "1.1.0" + +[[deps.OrdinaryDiffEqPDIRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "StaticArrays"] +git-tree-sha1 = "a8b7f8107c477e07c6a6c00d1d66cac68b801bbc" +uuid = "5dd0a6cf-3d4b-4314-aa06-06d4e299bc89" +version = "1.1.0" + +[[deps.OrdinaryDiffEqPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "Reexport"] +git-tree-sha1 = "da525d277962a1b76102c79f30cb0c31e13fe5b9" +uuid = "5b33eab2-c0f1-4480-b2c3-94bc1e80bda1" +version = "1.1.0" + +[[deps.OrdinaryDiffEqQPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "Static"] +git-tree-sha1 = "332f9d17d0229218f66a73492162267359ba85e9" +uuid = "04162be5-8125-4266-98ed-640baecc6514" +version = "1.1.0" + +[[deps.OrdinaryDiffEqRKN]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport"] +git-tree-sha1 = "41c09d9c20877546490f907d8dffdd52690dd65f" +uuid = "af6ede74-add8-4cfd-b1df-9a4dbb109d7a" +version = "1.1.0" + +[[deps.OrdinaryDiffEqRosenbrock]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static"] +git-tree-sha1 = "96b47cdd12cb4ce8f70d701b49f855271a462bd4" +uuid = "43230ef6-c299-4910-a778-202eb28ce4ce" +version = "1.2.0" + +[[deps.OrdinaryDiffEqSDIRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "f6683803a58de600ab7a26d2f49411c9923e9721" +uuid = "2d112036-d095-4a1e-ab9a-08536f3ecdbf" +version = "1.1.0" + +[[deps.OrdinaryDiffEqSSPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static", "StaticArrays"] +git-tree-sha1 = "7dbe4ac56f930df5e9abd003cedb54e25cbbea86" +uuid = "669c94d9-1f4b-4b64-b377-1aa079aa2388" +version = "1.2.0" + +[[deps.OrdinaryDiffEqStabilizedIRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "StaticArrays"] +git-tree-sha1 = "348fd6def9a88518715425025eadd58517017325" +uuid = "e3e12d00-db14-5390-b879-ac3dd2ef6296" +version = "1.1.0" + +[[deps.OrdinaryDiffEqStabilizedRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "StaticArrays"] +git-tree-sha1 = "1b0d894c880e25f7d0b022d7257638cf8ce5b311" +uuid = "358294b1-0aab-51c3-aafe-ad5ab194a2ad" +version = "1.1.0" + +[[deps.OrdinaryDiffEqSymplecticRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport"] +git-tree-sha1 = "4e8b8c8b81df3df17e2eb4603115db3b30a88235" +uuid = "fa646aed-7ef9-47eb-84c4-9443fc8cbfa8" +version = "1.1.0" + +[[deps.OrdinaryDiffEqTsit5]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static", "TruncatedStacktraces"] +git-tree-sha1 = "96552f7d4619fabab4038a29ed37dd55e9eb513a" +uuid = "b1df2697-797e-41e3-8120-5422d3b24e4a" +version = "1.1.0" + +[[deps.OrdinaryDiffEqVerner]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static", "TruncatedStacktraces"] +git-tree-sha1 = "81d7841e73e385b9925d5c8e4427f2adcdda55db" +uuid = "79d7bb75-1356-48c1-b8c0-6832512096c2" +version = "1.1.1" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] @@ -1380,6 +1596,12 @@ uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" version = "1.0.2" weakdeps = ["Requires", "TOML"] +[[deps.Pango_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e127b609fb9ecba6f201ba7ab753d5a605d53801" +uuid = "36c8627f-9965-5494-a995-c6b170f724f3" +version = "1.54.1+0" + [[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" @@ -1421,10 +1643,10 @@ uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" version = "1.4.1" [[deps.Plots]] -deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "UnitfulLatexify", "Unzip"] -git-tree-sha1 = "442e1e7ac27dd5ff8825c3fa62fbd1e86397974b" +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "TOML", "UUIDs", "UnicodeFun", "UnitfulLatexify", "Unzip"] +git-tree-sha1 = "45470145863035bb124ca51b320ed35d071cc6c2" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.40.4" +version = "1.40.8" [deps.Plots.extensions] FileIOExt = "FileIO" @@ -1441,10 +1663,10 @@ version = "1.40.4" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [[deps.Polyester]] -deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Requires", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] -git-tree-sha1 = "9ff799e8fb8ed6717710feee3be3bc20645daa97" +deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] +git-tree-sha1 = "6d38fea02d983051776a856b7df75b30cf9a3c1f" uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" -version = "0.7.15" +version = "0.7.16" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] @@ -1460,9 +1682,9 @@ version = "0.2.4" [[deps.PreallocationTools]] deps = ["Adapt", "ArrayInterface", "ForwardDiff"] -git-tree-sha1 = "406c29a7f46706d379a3bce45671b4e3a39ddfbc" +git-tree-sha1 = "6c62ce45f268f3f958821a1e5192cf91c75ae89c" uuid = "d236fae5-4411-538c-8e31-a6e3d9e00b46" -version = "0.4.22" +version = "0.4.24" [deps.PreallocationTools.extensions] PreallocationToolsReverseDiffExt = "ReverseDiff" @@ -1488,9 +1710,9 @@ uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" [[deps.ProgressMeter]] deps = ["Distributed", "Printf"] -git-tree-sha1 = "763a8ceb07833dd51bb9e3bbca372de32c0605ad" +git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4" uuid = "92933f4c-e287-5a05-a399-4b506db050ca" -version = "1.10.0" +version = "1.10.2" [[deps.Qt6Base_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"] @@ -1498,6 +1720,24 @@ git-tree-sha1 = "492601870742dcd38f233b23c3ec629628c1d724" uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" version = "6.7.1+1" +[[deps.Qt6Declarative_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll"] +git-tree-sha1 = "e5dd466bf2569fe08c91a2cc29c1003f4797ac3b" +uuid = "629bc702-f1f5-5709-abd5-49b8460ea067" +version = "6.7.1+2" + +[[deps.Qt6ShaderTools_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] +git-tree-sha1 = "1a180aeced866700d4bebc3120ea1451201f16bc" +uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a" +version = "6.7.1+1" + +[[deps.Qt6Wayland_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"] +git-tree-sha1 = "729927532d48cf79f49070341e1d918a65aba6b0" +uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3" +version = "6.7.1+1" + [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" @@ -1519,10 +1759,10 @@ uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" version = "0.6.12" [[deps.RecursiveArrayTools]] -deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "SparseArrays", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "3400ce27995422fb88ffcd3af9945565aad947f0" +deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] +git-tree-sha1 = "b034171b93aebc81b3e1890a036d13a9c4a9e3e0" uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" -version = "3.23.1" +version = "3.27.0" [deps.RecursiveArrayTools.extensions] RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" @@ -1530,6 +1770,7 @@ version = "3.23.1" RecursiveArrayToolsMeasurementsExt = "Measurements" RecursiveArrayToolsMonteCarloMeasurementsExt = "MonteCarloMeasurements" RecursiveArrayToolsReverseDiffExt = ["ReverseDiff", "Zygote"] + RecursiveArrayToolsSparseArraysExt = ["SparseArrays"] RecursiveArrayToolsTrackerExt = "Tracker" RecursiveArrayToolsZygoteExt = "Zygote" @@ -1539,6 +1780,7 @@ version = "3.23.1" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" @@ -1589,9 +1831,9 @@ version = "0.7.0" [[deps.SIMD]] deps = ["PrecompileTools"] -git-tree-sha1 = "2803cab51702db743f3fda07dd1745aadfbf43bd" +git-tree-sha1 = "98ca7c29edd6fc79cd74c61accb7010a4e7aee33" uuid = "fdea26ae-647d-5447-a871-4b548cad5224" -version = "3.5.0" +version = "3.6.0" [[deps.SIMDTypes]] git-tree-sha1 = "330289636fb8107c5f32088d2741e9fd7a061a5c" @@ -1605,10 +1847,10 @@ uuid = "476501e8-09a2-5ece-8869-fb82de89a1fa" version = "0.6.43" [[deps.SciMLBase]] -deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "7a6c5c8c38d2e37f45d4686c3598c20c1aebf48e" +deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "Expronicon", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] +git-tree-sha1 = "c96f81c3e98d5e2f0848fb42aba4383d772c3bb7" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.41.3" +version = "2.53.1" [deps.SciMLBase.extensions] SciMLBaseChainRulesCoreExt = "ChainRulesCore" @@ -1630,16 +1872,21 @@ version = "2.41.3" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SciMLOperators]] -deps = ["ArrayInterface", "DocStringExtensions", "LinearAlgebra", "MacroTools", "Setfield", "SparseArrays", "StaticArraysCore"] -git-tree-sha1 = "10499f619ef6e890f3f4a38914481cc868689cd5" +deps = ["Accessors", "ArrayInterface", "DocStringExtensions", "LinearAlgebra", "MacroTools"] +git-tree-sha1 = "e39c5f217f9aca640c8e27ab21acf557a3967db5" uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" -version = "0.3.8" +version = "0.3.10" +weakdeps = ["SparseArrays", "StaticArraysCore"] + + [deps.SciMLOperators.extensions] + SciMLOperatorsSparseArraysExt = "SparseArrays" + SciMLOperatorsStaticArraysCoreExt = "StaticArraysCore" [[deps.SciMLStructures]] deps = ["ArrayInterface"] -git-tree-sha1 = "cfdd1200d150df1d3c055cc72ee6850742e982d7" +git-tree-sha1 = "25514a6f200219cd1073e4ff23a6324e4a7efe64" uuid = "53ae85a6-f571-4167-b2af-e1d143709226" -version = "1.4.1" +version = "1.5.0" [[deps.Scratch]] deps = ["Dates"] @@ -1667,15 +1914,15 @@ uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" version = "1.0.3" [[deps.SimpleBufferStream]] -git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1" +git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" -version = "1.1.0" +version = "1.2.0" [[deps.SimpleNonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "DiffResults", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "MaybeInplace", "PrecompileTools", "Reexport", "SciMLBase", "Setfield", "StaticArraysCore"] -git-tree-sha1 = "58b144f34e44252b2de0acb5a9dbbb7ea5cd75d7" +git-tree-sha1 = "536c0ee0b4b766ddee24220c6bb60932df4e2c39" uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "1.10.1" +version = "1.12.1" [deps.SimpleNonlinearSolve.extensions] SimpleNonlinearSolveChainRulesCoreExt = "ChainRulesCore" @@ -1716,9 +1963,9 @@ version = "1.10.0" [[deps.SparseDiffTools]] deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Random", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "Tricks", "UnPack", "VertexSafeGraphs"] -git-tree-sha1 = "469f51f8c4741ce944be2c0b65423b518b1405b0" +git-tree-sha1 = "c9e5d7ee75cf6a1ca3a22c9a6a4ef451792cf62b" uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "2.19.0" +version = "2.20.0" [deps.SparseDiffTools.extensions] SparseDiffToolsEnzymeExt = "Enzyme" @@ -1735,10 +1982,10 @@ version = "2.19.0" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SparseMatrixColorings]] -deps = ["ADTypes", "Compat", "DocStringExtensions", "LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "eed2446b3c3dd58f6ded3168998b8b2cb3fc9229" +deps = ["ADTypes", "Compat", "DataStructures", "DocStringExtensions", "LinearAlgebra", "Random", "SparseArrays"] +git-tree-sha1 = "996dff77d814c45c3f2342fa0113e4ad31e712e8" uuid = "0a514795-09f3-496d-8182-132a7b665d35" -version = "0.3.3" +version = "0.4.0" [[deps.Sparspak]] deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] @@ -1758,15 +2005,15 @@ weakdeps = ["ChainRulesCore"] [[deps.Static]] deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools"] -git-tree-sha1 = "0bbff21027dd8a107551847528127b62a35f7594" +git-tree-sha1 = "87d51a3ee9a4b0d2fe054bdd3fc2436258db2603" uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" -version = "1.1.0" +version = "1.1.1" [[deps.StaticArrayInterface]] -deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "Requires", "SparseArrays", "Static", "SuiteSparse"] -git-tree-sha1 = "8963e5a083c837531298fc41599182a759a87a6d" +deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "Static"] +git-tree-sha1 = "96381d50f1ce85f2663584c8e886a6ca97e60554" uuid = "0d7ed370-da01-4f52-bd93-41d350b8b718" -version = "1.5.1" +version = "1.8.0" weakdeps = ["OffsetArrays", "StaticArrays"] [deps.StaticArrayInterface.extensions] @@ -1775,9 +2022,9 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "20833c5b7f7edf0e5026f23db7f268e4f23ec577" +git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.6" +version = "1.9.7" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -1820,13 +2067,9 @@ version = "0.3.7" [[deps.StructTypes]] deps = ["Dates", "UUIDs"] -git-tree-sha1 = "ca4bccb03acf9faaf4137a9abc1881ed1841aa70" +git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8" uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" -version = "1.10.0" - -[[deps.SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" +version = "1.11.0" [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] @@ -1835,9 +2078,9 @@ version = "7.2.1+1" [[deps.SymbolicIndexingInterface]] deps = ["Accessors", "ArrayInterface", "RuntimeGeneratedFunctions", "StaticArraysCore"] -git-tree-sha1 = "a5f6f138b740c9d93d76f0feddd3092e6ef002b7" +git-tree-sha1 = "988e04b34a4c3b824fb656f542473df99a4f610d" uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" -version = "0.3.22" +version = "0.3.30" [[deps.TOML]] deps = ["Dates"] @@ -1851,10 +2094,10 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" [[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.11.1" +version = "1.12.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -1890,13 +2133,9 @@ uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" version = "0.5.24" [[deps.TranscodingStreams]] -git-tree-sha1 = "d73336d81cafdc277ff45558bb7eaa2b04a8e472" +git-tree-sha1 = "e84b3a11b9bece70d14cce63406bbc79ed3464d2" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.10" -weakdeps = ["Random", "Test"] - - [deps.TranscodingStreams.extensions] - TestExt = ["Test", "Random"] +version = "0.11.2" [[deps.TriangularSolve]] deps = ["CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "LoopVectorization", "Polyester", "Static", "VectorizationBase"] @@ -1905,9 +2144,9 @@ uuid = "d5829a12-d9aa-46ab-831f-fb7c9ab06edf" version = "0.2.1" [[deps.Tricks]] -git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f" +git-tree-sha1 = "7822b97e99a1672bfb1b49b668a6d46d58d8cbcb" uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" -version = "0.1.8" +version = "0.1.9" [[deps.TruncatedStacktraces]] deps = ["InteractiveUtils", "MacroTools", "Preferences"] @@ -1940,9 +2179,9 @@ version = "0.4.1" [[deps.Unitful]] deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "dd260903fdabea27d9b6021689b3cd5401a57748" +git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.20.0" +version = "1.21.0" weakdeps = ["ConstructionBase", "InverseFunctions"] [deps.Unitful.extensions] @@ -1951,9 +2190,9 @@ weakdeps = ["ConstructionBase", "InverseFunctions"] [[deps.UnitfulLatexify]] deps = ["LaTeXStrings", "Latexify", "Unitful"] -git-tree-sha1 = "e2d817cc500e960fdbafcf988ac8436ba3208bfd" +git-tree-sha1 = "975c354fcd5f7e1ddcc1f1a23e6e091d99e99bc8" uuid = "45397f5d-5981-4c77-b2b3-fc36d6e9b728" -version = "1.6.3" +version = "1.6.4" [[deps.Unzip]] git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" @@ -1997,15 +2236,15 @@ version = "1.31.0+0" [[deps.WriteVTK]] deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] -git-tree-sha1 = "46664bb833f24e4fe561192e3753c9168c3b71b2" +git-tree-sha1 = "93fd3a6bbeb33c18929b7d8b4f4dbd2024395f70" uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192" -version = "1.19.2" +version = "1.20.0" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "d9717ce3518dc68a99e6b96300813760d887a01d" +git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.1+0" +version = "2.13.3+0" [[deps.XSLT_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] @@ -2171,9 +2410,9 @@ version = "1.5.0+0" [[deps.YAML]] deps = ["Base64", "Dates", "Printf", "StringEncodings"] -git-tree-sha1 = "80c3218f29cbc47111ac87e7be5e69cc05c6dd36" +git-tree-sha1 = "dea63ff72079443240fbd013ba006bcbc8a9ac00" uuid = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" -version = "0.4.11" +version = "0.4.12" [[deps.Zlib_jll]] deps = ["Libdl"] @@ -2194,15 +2433,15 @@ version = "3.2.9+0" [[deps.fzf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "a68c9655fbe6dfcab3d972808f1aafec151ce3f8" +git-tree-sha1 = "936081b536ae4aa65415d869287d43ef3cb576b2" uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" -version = "0.43.0+0" +version = "0.53.0+0" [[deps.gmsh_jll]] deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "bdc2fa0a123008ad941cabb0ad88c571e696af2e" +git-tree-sha1 = "1e7fe5c8dbe0e911931a18cdfbd2c7a1e01b68ef" uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" -version = "4.13.0+1" +version = "4.13.1+0" [[deps.gperf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -2223,15 +2462,21 @@ uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" version = "3.9.0+0" [[deps.libass_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47" +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "e17c115d55c5fbb7e52ebedb427a0dca79d4484e" uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.15.1+0" +version = "0.15.2+0" [[deps.libblastrampoline_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.8.0+1" +version = "5.11.0+0" + +[[deps.libdecor_jll]] +deps = ["Artifacts", "Dbus_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pango_jll", "Wayland_jll", "xkbcommon_jll"] +git-tree-sha1 = "9bf7903af251d2050b467f76bdbe57ce541f7f4f" +uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" +version = "0.2.2+0" [[deps.libevdev_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -2240,10 +2485,10 @@ uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" version = "1.11.0+0" [[deps.libfdk_aac_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8a22cf860a7d27e4f3498a0fe0811a7957badb38" uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "2.0.2+0" +version = "2.0.3+0" [[deps.libinput_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "eudev_jll", "libevdev_jll", "mtdev_jll"] @@ -2259,9 +2504,9 @@ version = "1.6.43+1" [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" +git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.7+1" +version = "1.3.7+2" [[deps.mtdev_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] From 84d46003118b415bf0f023995b359a9c228df553 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Sun, 22 Sep 2024 19:48:47 +0200 Subject: [PATCH 30/50] Replace `sparse` with `spzeros!` in grid coloring (#1064) This patch uses the more optimal `spzeros!` instead of `sparse` when creating the incidence matrix for the grid coloring. This doesn't improve the performance by much, but no reason not to use it. For a grid with 1 million elements: 3.181 s (4537096 allocations: 1.99 GiB) # PR 3.534 s (4537126 allocations: 2.29 GiB) # master --- src/Grid/coloring.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Grid/coloring.jl b/src/Grid/coloring.jl index 85f9723fe2..0597c122a2 100644 --- a/src/Grid/coloring.jl +++ b/src/Grid/coloring.jl @@ -9,7 +9,7 @@ function create_incidence_matrix(g::AbstractGrid, cellset=1:getncells(g)) end end - I, J, V = Int[], Int[], Bool[] + I, J = Int[], Int[] for (_, cells) in cell_containing_node for cell1 in cells # All these cells have a neighboring node for cell2 in cells @@ -17,13 +17,13 @@ function create_incidence_matrix(g::AbstractGrid, cellset=1:getncells(g)) if cell1 != cell2 push!(I, cell1) push!(J, cell2) - push!(V, true) end end end end - incidence_matrix = sparse(I, J, V, getncells(g), getncells(g)) + incidence_matrix = spzeros!!(Bool, I, J, getncells(g), getncells(g)) + fill!(incidence_matrix.nzval, true) return incidence_matrix end From c04c5bae47af09161aa9fc133976c846efb552a4 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 23 Sep 2024 12:04:51 +0200 Subject: [PATCH 31/50] Clarify why the inverse Metis permutation is used, closes #1065 (#1066) --- ext/FerriteMetis.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ext/FerriteMetis.jl b/ext/FerriteMetis.jl index 4726737eeb..71f064d639 100644 --- a/ext/FerriteMetis.jl +++ b/ext/FerriteMetis.jl @@ -87,9 +87,13 @@ function Ferrite.compute_renumber_permutation( G = Metis.Graph(idx_t(N), S.colptr, S.rowval) # Compute the permutation - _, perm = Metis.permutation(G) - - return perm + # The permutation returned by Metis is defined such that `A[perm, perm]`, for the + # eventual matrix `A`, minimizes fill in. This means that the new dof `i` can be + # determined by `perm[i]`. However, to renumber efficiently, we want the reverse + # mapping, i.e. `iperm`, so that we can easily lookup the new number for a given dof: + # dof `i`s new number is `iperm[i]`. + perm, iperm = Metis.permutation(G) + return iperm end end # VERSION check From fd68194d3ea5dff8a9c74605b25b9f979ca34616 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 26 Sep 2024 08:18:13 +0200 Subject: [PATCH 32/50] Consistently return the vtk file in export methods (#1069) This patch make sure that the vtk file is always returned in export related methods. In particular, this make sure that return values of WriteVTK functions don't leak out to Ferrite users. For example, `write_cell_data` returned an `LightXML.XMLElement`. --- src/Export/VTK.jl | 13 +++++++++--- test/test_grid_dofhandler_vtk.jl | 34 ++++++++++++++++---------------- test/test_vtk_export.jl | 20 +++++++++++-------- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index 7bc5f0d2bf..db28841103 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -44,9 +44,13 @@ function VTKGridFile(f::Function, args...; kwargs...) finally close(vtk) end + return vtk end -Base.close(vtk::VTKGridFile) = WriteVTK.vtk_save(vtk.vtk) +function Base.close(vtk::VTKGridFile) + WriteVTK.vtk_save(vtk.vtk) + return vtk +end function Base.show(io::IO, ::MIME"text/plain", vtk::VTKGridFile) open_str = isopen(vtk.vtk) ? "open" : "closed" @@ -55,10 +59,10 @@ function Base.show(io::IO, ::MIME"text/plain", vtk::VTKGridFile) end function WriteVTK.collection_add_timestep(pvd::WriteVTK.CollectionFile, datfile::VTKGridFile, time::Real) - WriteVTK.collection_add_timestep(pvd, datfile.vtk, time) + return WriteVTK.collection_add_timestep(pvd, datfile.vtk, time) end function Base.setindex!(pvd::WriteVTK.CollectionFile, datfile::VTKGridFile, time::Real) - WriteVTK.collection_add_timestep(pvd, datfile.vtk, time) + return WriteVTK.collection_add_timestep(pvd, datfile, time) end cell_to_vtkcell(::Type{Line}) = VTKCellTypes.VTK_LINE @@ -206,6 +210,7 @@ Write the `celldata` that is ordered by the cells in the grid to the vtk file. """ function write_cell_data(vtk::VTKGridFile, celldata, name) WriteVTK.vtk_cell_data(vtk.vtk, celldata, name) + return vtk end """ @@ -222,6 +227,7 @@ When `nodedata` contains second order tensors, the index order, """ function write_node_data(vtk::VTKGridFile, nodedata, name) _vtk_write_node_data(vtk.vtk, nodedata, name) + return vtk end @@ -312,4 +318,5 @@ function write_cell_colors(vtk, grid::AbstractGrid, cell_colors::AbstractVector{ end end write_cell_data(vtk, color_vector, name) + return vtk end diff --git a/test/test_grid_dofhandler_vtk.jl b/test/test_grid_dofhandler_vtk.jl index 1fb651cc8e..1d0d06125b 100644 --- a/test/test_grid_dofhandler_vtk.jl +++ b/test/test_grid_dofhandler_vtk.jl @@ -38,10 +38,10 @@ end addnodeset!(grid, "middle-nodes", x -> norm(x) < radius) gridfilename = "grid-$(repr(celltype))" - VTKGridFile(gridfilename, grid) do vtk - Ferrite.write_cellset(vtk, grid, "cell-1") - Ferrite.write_cellset(vtk, grid, "middle-cells") - Ferrite.write_nodeset(vtk, grid, "middle-nodes") + VTKGridFile(gridfilename, grid) do vtk::VTKGridFile + @test Ferrite.write_cellset(vtk, grid, "cell-1") === vtk + @test Ferrite.write_cellset(vtk, grid, "middle-cells") === vtk + @test Ferrite.write_nodeset(vtk, grid, "middle-nodes") === vtk end # test the sha of the file @@ -78,9 +78,9 @@ end apply!(u, ch) dofhandlerfilename = "dofhandler-$(repr(celltype))" - VTKGridFile(dofhandlerfilename, grid) do vtk - Ferrite.write_constraints(vtk, ch) - write_solution(vtk, dofhandler, u) + VTKGridFile(dofhandlerfilename, grid) do vtk::VTKGridFile + @test Ferrite.write_constraints(vtk, ch) === vtk + @test write_solution(vtk, dofhandler, u) === vtk end # test the sha of the file @@ -124,10 +124,10 @@ close(csio) vector_data = [Vec{3}(ntuple(i->i, 3)) for j=1:8] filename_3d = "test_vtk_3d" - VTKGridFile(filename_3d, grid) do vtk - write_node_data(vtk, sym_tensor_data, "symmetric tensor") - write_node_data(vtk, tensor_data, "tensor") - write_node_data(vtk, vector_data, "vector") + VTKGridFile(filename_3d, grid) do vtk::VTKGridFile + @test write_node_data(vtk, sym_tensor_data, "symmetric tensor") === vtk + @test write_node_data(vtk, tensor_data, "tensor") === vtk + @test write_node_data(vtk, vector_data, "vector") === vtk end # 2D grid @@ -139,11 +139,11 @@ close(csio) vector_data = [Vec{2}(ntuple(i->i, 2)) for j=1:4] filename_2d = "test_vtk_2d" - VTKGridFile(filename_2d, grid) do vtk - write_node_data(vtk, sym_tensor_data, "symmetric tensor") - write_node_data(vtk, tensor_data, "tensor") - write_node_data(vtk, tensor_data_1D, "tensor_1d") - write_node_data(vtk, vector_data, "vector") + VTKGridFile(filename_2d, grid) do vtk::VTKGridFile + @test write_node_data(vtk, sym_tensor_data, "symmetric tensor") === vtk + @test write_node_data(vtk, tensor_data, "tensor") === vtk + @test write_node_data(vtk, tensor_data_1D, "tensor_1d") === vtk + @test write_node_data(vtk, vector_data, "vector") === vtk end # test the shas of the files @@ -751,7 +751,7 @@ end celldata = rand(getncells(grid)) pvd = WriteVTK.paraview_collection("collection") vtk1 = VTKGridFile("file1", grid) - write_cell_data(vtk1, celldata, "celldata") + @test write_cell_data(vtk1, celldata, "celldata") === vtk1 @assert isopen(vtk1.vtk) pvd[0.5] = vtk1 @test !isopen(vtk1.vtk) # Should be closed when adding it diff --git a/test/test_vtk_export.jl b/test/test_vtk_export.jl index 4d6a8f4612..7c5fc8f30b 100644 --- a/test/test_vtk_export.jl +++ b/test/test_vtk_export.jl @@ -17,9 +17,10 @@ grid = generate_grid(Quadrilateral, (4, 4)) colors = create_coloring(grid) fname = joinpath(tmp, "colors") - VTKGridFile(fname, grid) do vtk - Ferrite.write_cell_colors(vtk, grid, colors) + v = VTKGridFile(fname, grid) do vtk::VTKGridFile + @test Ferrite.write_cell_colors(vtk, grid, colors) === vtk end + @test v isa VTKGridFile @test bytes2hex(open(SHA.sha1, fname*".vtu")) == "b804d0b064121b672d8e35bcff8446eda361cac3" end end @@ -35,9 +36,10 @@ add!(ch, Dirichlet(:u, getnodeset(grid, "nodeset"), x -> 0.0)) close!(ch) fname = joinpath(tmp, "constraints") - VTKGridFile(fname, grid) do vtk - Ferrite.write_constraints(vtk, ch) + v = VTKGridFile(fname, grid) do vtk::VTKGridFile + @test Ferrite.write_constraints(vtk, ch) === vtk end + @test v isa VTKGridFile @test bytes2hex(open(SHA.sha1, fname*".vtu")) == "31b506bd9729b11992f8bcb79a2191eb65d223bf" end end @@ -50,12 +52,14 @@ addcellset!(grid, "set2", 1:4) manual = joinpath(tmp, "manual") auto = joinpath(tmp, "auto") - VTKGridFile(manual, grid) do vtk - Ferrite.write_cellset(vtk, grid, keys(Ferrite.getcellsets(grid))) + v = VTKGridFile(manual, grid) do vtk::VTKGridFile + @test Ferrite.write_cellset(vtk, grid, keys(Ferrite.getcellsets(grid))) === vtk end - VTKGridFile(auto, grid) do vtk - Ferrite.write_cellset(vtk, grid) + @test v isa VTKGridFile + v = VTKGridFile(auto, grid) do vtk::VTKGridFile + @test Ferrite.write_cellset(vtk, grid) === vtk end + @test v isa VTKGridFile @test bytes2hex(open(SHA.sha1, manual*".vtu")) == bytes2hex(open(SHA.sha1, auto*".vtu")) end end From 0762d018d00007f96291e6b8a7d8c4f774e81684 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Thu, 26 Sep 2024 11:54:39 +0200 Subject: [PATCH 33/50] Modernize the threaded assembly howto (#1068) This patch rewrites the threaded assembly howto to use OhMyThreads.jl which is a better interface to multithreading than using "raw" `at-threads`. Also adds some more prose and explanations. --- Project.toml | 4 +- docs/Manifest.toml | 56 ++- docs/Project.toml | 2 + docs/src/literate-howto/threaded_assembly.jl | 421 ++++++++++++------- test/test_examples.jl | 11 + 5 files changed, 339 insertions(+), 155 deletions(-) diff --git a/Project.toml b/Project.toml index bd8f0b7d29..52bb20f614 100644 --- a/Project.toml +++ b/Project.toml @@ -45,12 +45,14 @@ IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" NBInclude = "0db19996-df87-5ea3-a455-e3a50d440464" +OhMyThreads = "67456a42-1dca-4109-a031-0a68de7e3ad5" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" +TaskLocalValues = "ed4db957-447d-4319-bfb6-7fa9ae7ecf34" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" [targets] -test = ["BlockArrays", "Downloads", "FerriteGmsh", "ForwardDiff", "Gmsh", "IterativeSolvers", "Metis", "Pkg", "NBInclude", "ProgressMeter", "Random", "SHA", "Test", "TimerOutputs", "Logging"] +test = ["BlockArrays", "Downloads", "FerriteGmsh", "ForwardDiff", "Gmsh", "IterativeSolvers", "Metis", "Pkg", "NBInclude", "OhMyThreads", "ProgressMeter", "Random", "SHA", "TaskLocalValues", "Test", "TimerOutputs", "Logging"] diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 4b67ef2b1f..daad98ede8 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.10.5" manifest_format = "2.0" -project_hash = "864d1d29886fc101f4919a6d1c30efd8f4e2ca2e" +project_hash = "4e52f4aa4cee9f66ec4f633f0ae538fbd227ac5e" [[deps.ADTypes]] git-tree-sha1 = "5a5eafb8344b81b8c2237f8a6f6b3602b3f6180e" @@ -112,6 +112,28 @@ weakdeps = ["SparseArrays"] [[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +[[deps.BangBang]] +deps = ["Accessors", "ConstructionBase", "InitialValues", "LinearAlgebra", "Requires"] +git-tree-sha1 = "e2144b631226d9eeab2d746ca8880b7ccff504ae" +uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" +version = "0.4.3" + + [deps.BangBang.extensions] + BangBangChainRulesCoreExt = "ChainRulesCore" + BangBangDataFramesExt = "DataFrames" + BangBangStaticArraysExt = "StaticArrays" + BangBangStructArraysExt = "StructArrays" + BangBangTablesExt = "Tables" + BangBangTypedTablesExt = "TypedTables" + + [deps.BangBang.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" + TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9" + [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -188,6 +210,12 @@ git-tree-sha1 = "e579c6157598169ad4ef17263bdf3452b4a3e316" uuid = "5217a498-cd5d-4ec6-b8c2-9b85a09b6e3e" version = "1.1.0" +[[deps.ChunkSplitters]] +deps = ["TestItems"] +git-tree-sha1 = "01d5db8756afc4022b1cf267cfede13245226c72" +uuid = "ae650224-84b6-46f8-82ea-d812ca08434e" +version = "2.6.0" + [[deps.CloseOpenIntervals]] deps = ["Static", "StaticArrayInterface"] git-tree-sha1 = "05ba0d07cd4fd8b7a39541e31a7b0254704ea581" @@ -796,6 +824,11 @@ git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" version = "0.1.5" +[[deps.InitialValues]] +git-tree-sha1 = "4da0f88e9a39111c2fa3add390ab15f3a44f3ca3" +uuid = "22cec73e-a1b8-11e9-2c92-598750a2cf9c" +version = "0.3.1" + [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" @@ -1338,6 +1371,12 @@ git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" version = "1.3.5+1" +[[deps.OhMyThreads]] +deps = ["BangBang", "ChunkSplitters", "StableTasks", "TaskLocalValues"] +git-tree-sha1 = "6acbf70d8306a38a870be56d1841b2c9a4b17837" +uuid = "67456a42-1dca-4109-a031-0a68de7e3ad5" +version = "0.6.2" + [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" @@ -2003,6 +2042,11 @@ weakdeps = ["ChainRulesCore"] [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" +[[deps.StableTasks]] +git-tree-sha1 = "073d5c20d44129b20fe954720b97069579fa403b" +uuid = "91464d47-22a1-43fe-8b7f-2d57ee82463f" +version = "0.1.5" + [[deps.Static]] deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools"] git-tree-sha1 = "87d51a3ee9a4b0d2fe054bdd3fc2436258db2603" @@ -2104,6 +2148,11 @@ deps = ["ArgTools", "SHA"] uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" version = "1.10.0" +[[deps.TaskLocalValues]] +git-tree-sha1 = "d155450e6dff2a8bc2fcb81dcb194bd98b0aeb46" +uuid = "ed4db957-447d-4319-bfb6-7fa9ae7ecf34" +version = "0.1.2" + [[deps.TensorCore]] deps = ["LinearAlgebra"] git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" @@ -2120,6 +2169,11 @@ version = "1.16.1" deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +[[deps.TestItems]] +git-tree-sha1 = "42fd9023fef18b9b78c8343a4e2f3813ffbcefcb" +uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" +version = "1.0.0" + [[deps.ThreadingUtilities]] deps = ["ManualMemory"] git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" diff --git a/docs/Project.toml b/docs/Project.toml index 944be43ceb..ead8045637 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -13,11 +13,13 @@ LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589" +OhMyThreads = "67456a42-1dca-4109-a031-0a68de7e3ad5" Optim = "429524aa-4258-5aef-a3af-852621145aeb" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +TaskLocalValues = "ed4db957-447d-4319-bfb6-7fa9ae7ecf34" Tensors = "48a634ad-e948-5137-8d70-aa71f2a747f4" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" diff --git a/docs/src/literate-howto/threaded_assembly.jl b/docs/src/literate-howto/threaded_assembly.jl index fa0526fa9f..8f47cccc75 100644 --- a/docs/src/literate-howto/threaded_assembly.jl +++ b/docs/src/literate-howto/threaded_assembly.jl @@ -1,3 +1,6 @@ +# ```@meta +# Draft = false +# ``` # # [Multi-threaded assembly](@id tutorial-threaded-assembly) # #- @@ -5,198 +8,310 @@ #md # This example is also available as a Jupyter notebook: #md # [`threaded_assembly.ipynb`](@__NBVIEWER_ROOT_URL__/howto/threaded_assembly.ipynb). #- + +# ## Introduction +# +# In this howto we will explore how to use task based multithreading (shared memory +# parallelism) to speed up the analysis. Some parts of a finite element simulation are +# trivially parallelizable such as the computation of the local element contributions since +# each element can be processed independently. However, two things need to be considered in +# order to parallelize safely: # -# ## Example of a colored grid +# - **Modification of shared data**: Although the contributions from all the elements can +# be computed independently, eventually they need to be assembled into the global +# matrix and vector. Letting each task assemble their own contribution would lead to +# race conditions since elements share degrees of freedom with each other. There are +# various ways to remedy this, for example: +# - **Locking**: By using a lock around the call to `assemble!` we can ensure that only +# one task assembles at a time. This is simple to implement but can lead to lock +# contention and thus poor performance. Another drawback is that the results will not +# be deterministic since floating point operations are neither associative nor +# commutative. +# - **Assembler task**: By using a designated task for the assembling we (obviously) +# ensure that only a single task assembles. The worker tasks (the tasks computing the +# element contributions) would then hand off their results to the assemly task. This +# can be a useful approach if computing the element contributions is much slower than +# the assembly -- otherwise the assembler task can't keep up with the worker tasks. +# There might also be some extra overhead because of task switching in the scheduler. +# The problem with non-deterministic results still remains. +# - **Grid coloring**: By "coloring" the grid such that, within each color, no two +# elements share degrees of freedom, we can safely assemble each color in parallel. +# Even if concurrently running tasks will write to the global matrix and vector they +# will not write to the same memory locations. Note also that this procedure gives +# predictable results because for a memory location which, for example, a "red", +# a "blue", and a "green" element will contribute to we will always add the red first, +# then the blue, and finally the green. +# - **Scratch data**: In order to speed up the computation of the element contributions we +# typically pre-allocate some data structures that can be reused for every element. Such +# scratch data include, for example, the local matrix and vector, and the CellValues. +# Each task need their own copy of the scratch data since they will be modified for each +# element. + +# ## Grid coloring # -# Creates a simple 2D grid and colors it. -# Save the example grid to a VTK file to show the coloring. -# No cells with the same color has any shared nodes (dofs). -# This means that it is safe to assemble in parallel as long as we only assemble -# one color at a time. +# Ferrite include functionality to color the grid with the [`create_coloring`](@ref) +# function. Here we create a simple 2D grid, color it, and export the colors to a VTK file +# to visualize the result (see *Figure 1*.). Note that no cells with the same color has any +# shared nodes (dofs). This means that it is safe to assemble in parallel as long as we only +# assemble one color at a time. # -# For this structured grid the greedy algorithm uses fewer colors, but both algorithms -# result in colors that contain roughly the same number of elements. For unstructured -# grids the greedy algorithm can result in colors with very few element. For those -# cases the workstream algorithm is better since it tries to balance the colors evenly. +# There are two coloring algorithms implemented: the "workstream" algorithm (from Turcksin +# et al. [Turcksin2016](@cite)) and a "greedy" algorithm. For this structured grid the +# greedy algorithm uses fewer colors, but both algorithms result in colors that contain +# roughly the same number of elements. The workstream algorithm is the default one since it +# in general results in more balanced colors. For unstructured grids the greedy algorithm +# can result in colors with very few elements, for example. using Ferrite, SparseArrays function create_example_2d_grid() grid = generate_grid(Quadrilateral, (10, 10), Vec{2}((0.0, 0.0)), Vec{2}((10.0, 10.0))) - colors_workstream = create_coloring(grid; alg=ColoringAlgorithm.WorkStream) - colors_greedy = create_coloring(grid; alg=ColoringAlgorithm.Greedy) + colors_workstream = create_coloring(grid; alg = ColoringAlgorithm.WorkStream) + colors_greedy = create_coloring(grid; alg = ColoringAlgorithm.Greedy) VTKGridFile("colored", grid) do vtk Ferrite.write_cell_colors(vtk, grid, colors_workstream, "workstream-coloring") Ferrite.write_cell_colors(vtk, grid, colors_greedy, "greedy-coloring") end end -create_example_2d_grid(); +create_example_2d_grid() # ![](coloring.png) # # *Figure 1*: Element coloring using the "workstream"-algorithm (left) and the "greedy"- # algorithm (right). -# ## Cantilever beam in 3D with threaded assembly -# We will now look at an example where we assemble the stiffness matrix using multiple -# threads. We set up a simple grid and create a coloring, then create a DofHandler, -# and define the material stiffness +# ## Multithreaded assembly of a cantilever beam in 3D +# +# We will now look at an example where we assemble the stiffness matrix and right hand side +# using multiple threads. The problem setup is a cantilever beam in 3D with a linear elastic +# material behavior. For this exercise we only focus on the multithreading and are not +# bothered with boundary conditions. For more details refer to the [tutorial on linear +# elasticity](../tutorials/linear_elasticity.md). + +# ### Setup +# +# We define the element routine, material stiffness, grid and DofHandler just like in the +# [tutorial on linear elasticity](../tutorials/linear_elasticity.md) without discussing it +# further here. + +## Element routine +function assemble_cell!(Ke::Matrix, fe::Vector, cellvalues::CellValues, C::SymmetricTensor, b::Vec) + fill!(Ke, 0) + fill!(fe, 0) + for q_point in 1:getnquadpoints(cellvalues) + dΩ = getdetJdV(cellvalues, q_point) + for i in 1:getnbasefunctions(cellvalues) + δui = shape_value(cellvalues, q_point, i) + fe[i] += (δui ⋅ b) * dΩ + ∇δui = shape_symmetric_gradient(cellvalues, q_point, i) + for j in 1:getnbasefunctions(cellvalues) + ∇uj = shape_symmetric_gradient(cellvalues, q_point, j) + Ke[i, j] += (∇δui ⊡ C ⊡ ∇uj) * dΩ + end + end + end + return Ke, fe +end -# #### Grid for the beam -function create_colored_cantilever_grid(celltype, n) - grid = generate_grid(celltype, (10*n, n, n), Vec{3}((0.0, 0.0, 0.0)), Vec{3}((10.0, 1.0, 1.0))) +## Material stiffness +function create_material_stiffness() + E = 200.0e9 + ν = 0.3 + λ = E * ν / ((1 + ν) * (1 - 2ν)) + μ = E / (2(1 + ν)) + δ(i, j) = i == j ? 1.0 : 0.0 + C = SymmetricTensor{4, 3}() do i, j, k, l + return λ * δ(i, j) * δ(k, l) + μ * (δ(i, k) * δ(j, l) + δ(i, l) * δ(j, k)) + end + return C +end + +## Grid and grid coloring +function create_cantilever_grid(n::Int) + xmin = Vec{3}((0.0, 0.0, 0.0)) + xmax = Vec{3}((10.0, 1.0, 1.0)) + grid = generate_grid(Hexahedron, (10 * n, n, n), xmin, xmax) colors = create_coloring(grid) return grid, colors -end; +end -# #### DofHandler -function create_dofhandler(grid::Grid{dim}, ip) where {dim} +## DofHandler with displacement field u +function create_dofhandler(grid::Grid, interpolation::VectorInterpolation) dh = DofHandler(grid) - add!(dh, :u, ip) # Add a displacement field + add!(dh, :u, interpolation) close!(dh) -end; - -# ### Stiffness tensor for linear elasticity -function create_stiffness(::Val{dim}) where {dim} - E = 200e9 - ν = 0.3 - λ = E*ν / ((1+ν) * (1 - 2ν)) - μ = E / (2(1+ν)) - δ(i,j) = i == j ? 1.0 : 0.0 - g(i,j,k,l) = λ*δ(i,j)*δ(k,l) + μ*(δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k)) - C = SymmetricTensor{4, dim}(g); - return C -end; + return dh +end +nothing # hide -# ## Threaded data structures +# ### Task local scratch data # -# ScratchValues is a thread-local collection of data that each thread needs to own, -# since we need to be able to mutate the data in the threads independently -struct ScratchValues{T, CV <: CellValues, FV <: FacetValues, TT <: AbstractTensor, dim, AT} +# We group everything that needs to be duplicated for each task in the struct +# `ScratchData`: +# - `cell_cache::CellCache`: contain buffers for coordinates and (global) dofs which will +# be `reinit!`ed for each cell. +# - `cellvalues::CellValues`: the cell values which will be `reinit!`ed for each cell using +# the `cell_cache` +# - `Ke::Matrix`: the local matrix +# - `fe::Vector`: the local vector +# - `assembler`: the assembler (which needs to be duplicated because it contains buffers +# that are modified during the call to `assemble!`) +struct ScratchData{CC, CV, T, A} + cell_cache::CC + cellvalues::CV Ke::Matrix{T} fe::Vector{T} - cellvalues::CV - facetvalues::FV - global_dofs::Vector{Int} - ɛ::Vector{TT} - coordinates::Vector{Vec{dim, T}} - assembler::AT -end; - -# Each thread need its own CellValues and FacetValues (although, for this example we don't use -# the FacetValues) -function create_values(interpolation_space::Interpolation{refshape}, qr_order::Int) where {dim, refshape<:Ferrite.AbstractRefShape{dim}} - ## Interpolations and values - quadrature_rule = QuadratureRule{refshape}(qr_order) - facet_quadrature_rule = FacetQuadratureRule{refshape}(qr_order) - cellvalues = [CellValues(quadrature_rule, interpolation_space) for i in 1:Threads.nthreads()]; - facetvalues = [FacetValues(facet_quadrature_rule, interpolation_space) for i in 1:Threads.nthreads()]; - return cellvalues, facetvalues -end; - -# Create a `ScratchValues` for each thread with the thread local data -function create_scratchvalues(K, f, dh::DofHandler{dim}, ip) where {dim} - nthreads = Threads.nthreads() - assemblers = [start_assemble(K, f) for i in 1:nthreads] - cellvalues, facetvalues = create_values(ip, 2) - - n_basefuncs = getnbasefunctions(cellvalues[1]) - global_dofs = [zeros(Int, ndofs_per_cell(dh)) for i in 1:nthreads] - - fes = [zeros(n_basefuncs) for i in 1:nthreads] # Local force vector - Kes = [zeros(n_basefuncs, n_basefuncs) for i in 1:nthreads] - - ɛs = [[zero(SymmetricTensor{2, dim}) for i in 1:n_basefuncs] for i in 1:nthreads] - - coordinates = [[zero(Vec{dim}) for i in 1:length(dh.grid.cells[1].nodes)] for i in 1:nthreads] - - return [ScratchValues(Kes[i], fes[i], cellvalues[i], facetvalues[i], global_dofs[i], - ɛs[i], coordinates[i], assemblers[i]) for i in 1:nthreads] -end; - -# ## Threaded assemble + assembler::A +end -# The assembly function loops over each color and does a threaded assembly for that color -function doassemble(K::SparseMatrixCSC, colors, grid::Grid, dh::DofHandler, C::SymmetricTensor{4, dim}, ip) where {dim} +# This constructor will be called within each task to create a independent `ScratchData` +# object. For `cell_cache`, `Ke`, and `fe` we simply call the constructors to allocate +# independent objects. For `cellvalues` we use `copy` which Ferrite defines for this +# purpose. Finally, for the assembler we call `start_assemble` to create a new assembler but +# note that we set `fillzero = false` because we don't want to risk that a task that starts +# a bit later will zero out data that another task have already assembled. +function ScratchData(dh::DofHandler, K::SparseMatrixCSC, f::Vector, cellvalues::CellValues) + cell_cache = CellCache(dh) + n = ndofs_per_cell(dh) + Ke = zeros(n, n) + fe = zeros(n) + asm = start_assemble(K, f; fillzero = false) + return ScratchData(cell_cache, copy(cellvalues), Ke, fe, asm) +end +nothing # hide - f = zeros(ndofs(dh)) - scratches = create_scratchvalues(K, f, dh, ip) - b = Vec{3}((0.0, 0.0, 0.0)) # Body force +# ### Global assembly routine +# Finally we define the global assemble routine, which is where the parallelization happens. +# The main difference from all previous `assemble_global!` functions is that we now have an +# outer loop over the colors, and then the inner loop over the cells in each color, which +# can be parallelized. +# +# For the scheduling of parallel tasks we use the +# [OhMyThreads.jl](https://github.com/JuliaFolds2/OhMyThreads.jl) package. OhMyThreads +# provides a macro based and a functional API. Here we use the macro based API because it is +# slightly more convenient when using task local values since they can be defined with the +# `@local` macro. +# +# !!! note "Schedulers and load balancing" +# OhMyThreads provides a number of different +# [schedulers](https://juliafolds2.github.io/OhMyThreads.jl/stable/refs/api/#Schedulers). +# In this example we use the `DynamicScheduler` (which is the default one). The +# `DynamicScheduler` will spawn `ntasks` tasks where each task will process a chunk of +# (roughly) equal number of cells (i.e. `length(color) ÷ ntasks`). This should be a good +# choice for this example because we expect all cells to take the same time to process +# and we don't need any load balancing. +# +# For a different problem setup where some cells might take longer to process (perhaps +# they experience plastic deformation and we need to solve a local problem) we might +# benefit from load balancing. The `DynamicScheduler` can be used also for load +# balancing by specifiying `nchunks` or `chunksize`. However, the `DynamicScheduler` +# will always spawn `nchunks` tasks which can become costly since we are allocating +# scratch data for every task. To limit the number of tasks, while allowing for more +# than `ntasks` chunks, we can use the `GreedyScheduler` *with chunking*. For example, +# `scheduler = OhMyThreads.GreedyScheduler(; ntasks = ntasks, nchunks = 10 * ntasks)` +# will split the work into `10 * ntasks` chunks and spawn `ntasks` tasks to process +# them. Refer to the [OhMyThreads +# documentation](https://juliafolds2.github.io/OhMyThreads.jl/stable/) for details. + +using OhMyThreads, TaskLocalValues + +function assemble_global!( + K::SparseMatrixCSC, f::Vector, dh::DofHandler, colors, + cellvalues_template::CellValues; ntasks = Threads.nthreads() + ) + ## Zero-out existing data in K and f + _ = start_assemble(K, f) + ## Body force and material stiffness + b = Vec{3}((0.0, 0.0, -1.0)) + C = create_material_stiffness() + ## Loop over the colors for color in colors - ## Each color is safe to assemble threaded - Threads.@threads :static for i in 1:length(color) - assemble_cell!(scratches[Threads.threadid()], color[i], K, grid, dh, C, b) + ## Dynamic scheduler spawning `ntasks` tasks where each task will process a chunk of + ## (roughly) equal number of cells (`length(color) ÷ ntasks`). + scheduler = OhMyThreads.DynamicScheduler(; ntasks) + ## Parallelize the loop over the cells in this color + OhMyThreads.@tasks for cellidx in color + ## Tell the @tasks loop to use the scheduler defined above + @set scheduler = scheduler + ## Obtain a task local scratch and unpack it + @local scratch = ScratchData(dh, K, f, cellvalues_template) + (; cell_cache, cellvalues, Ke, fe, assembler) = scratch + ## Reinitialize the cell cache and then the cellvalues + reinit!(cell_cache, cellidx) + reinit!(cellvalues, cell_cache) + ## Compute the local contribution of the cell + assemble_cell!(Ke, fe, cellvalues, C, b) + ## Assemble local contribution + assemble!(assembler, celldofs(cell_cache), Ke, fe) end end - return K, f end - -# The cell assembly function is written the same way as if it was a single threaded example. -# The only difference is that we unpack the variables from our `scratch`. -function assemble_cell!(scratch::ScratchValues, cell::Int, K::SparseMatrixCSC, - grid::Grid, dh::DofHandler, C::SymmetricTensor{4, dim}, b::Vec{dim}) where {dim} - - ## Unpack our stuff from the scratch - Ke, fe, cellvalues, facetvalues, global_dofs, ɛ, coordinates, assembler = - scratch.Ke, scratch.fe, scratch.cellvalues, scratch.facetvalues, - scratch.global_dofs, scratch.ɛ, scratch.coordinates, scratch.assembler - - fill!(Ke, 0) - fill!(fe, 0) - - n_basefuncs = getnbasefunctions(cellvalues) - - ## Fill up the coordinates - nodeids = grid.cells[cell].nodes - for j in 1:length(coordinates) - coordinates[j] = grid.nodes[nodeids[j]].x - end - - reinit!(cellvalues, coordinates) - - for q_point in 1:getnquadpoints(cellvalues) - for i in 1:n_basefuncs - ɛ[i] = symmetric(shape_gradient(cellvalues, q_point, i)) - end - dΩ = getdetJdV(cellvalues, q_point) - for i in 1:n_basefuncs - δu = shape_value(cellvalues, q_point, i) - fe[i] += (δu ⋅ b) * dΩ - ɛC = ɛ[i] ⊡ C - for j in 1:n_basefuncs - Ke[i, j] += (ɛC ⊡ ɛ[j]) * dΩ - end - end - end - - celldofs!(global_dofs, dh, cell) - assemble!(assembler, global_dofs, Ke, fe) -end; - -function run_assemble() - n = 20 - grid, colors = create_colored_cantilever_grid(Hexahedron, n); - ip = Lagrange{RefHexahedron,1}()^3 - dh = create_dofhandler(grid, ip); - - K = allocate_matrix(dh); - C = create_stiffness(Val{3}()); - ## compilation - doassemble(K, colors, grid, dh, C, ip); - b = @elapsed @time K, f = doassemble(K, colors, grid, dh, C, ip); - return b +nothing # hide + +# !!! details "OhMyThreads functional API: OhMyThreads.tforeach" +# The `OhMyThreads.@tasks` block above corresponds to a call to `OhMyThreads.tforeach`. +# Using the functional API directly would look like below. The main difference is that +# we need to manually create a `TaskLocalValue` for the scratch data. +# ```julia +# # using TaskLocalValues +# scratches = TaskLocalValue() do +# ScratchData(dh, K, f, cellvalues) +# end +# OhMyThreads.tforeach(color; scheduler) do cellidx +# # Obtain a task local scratch and unpack it +# scratch = scratches[] +# (; cell_cache, cellvalues, Ke, fe, assembler) = scratch +# # Reinitialize the cell cache and then the cellvalues +# reinit!(cell_cache, cellidx) +# reinit!(cellvalues, cell_cache) +# # Compute the local contribution of the cell +# assemble_cell!(Ke, fe, cellvalues, C, b) +# # Assemble local contribution +# assemble!(assembler, celldofs(cell_cache), Ke, fe) +# end +# ``` + +# We define the main function to setup everything and then time the call to +# `assemble_global!`. + +function main(; n = 20, ntasks = Threads.nthreads()) + ## Interpolation, quadrature and cellvalues + interpolation = Lagrange{RefHexahedron, 1}()^3 + quadrature = QuadratureRule{RefHexahedron}(2) + cellvalues = CellValues(quadrature, interpolation) + ## Grid, colors and DofHandler + grid, colors = create_cantilever_grid(n) + dh = create_dofhandler(grid, interpolation) + ## Global matrix and vector + K = allocate_matrix(dh) + f = zeros(ndofs(dh)) + ## Compile it + assemble_global!(K, f, dh, colors, cellvalues; ntasks = ntasks) + ## Time it + @time assemble_global!(K, f, dh, colors, cellvalues; ntasks = ntasks) + return norm(K.nzval), norm(f) #src + return end - -run_assemble() - -# Running the code with different number of threads give the following runtimes: -# * 1 thread 2.46 seconds -# * 2 threads 1.19 seconds -# * 3 threads 0.83 seconds -# * 4 threads 0.75 seconds +nothing # hide + +# On a machine with 4 cores, starting julia with `--threads=auto`, we obtain the following +# timings: +# ```julia +# main(; ntasks = 1) # 1.970784 seconds (902 allocations: 816.172 KiB) +# main(; ntasks = 2) # 1.025065 seconds (1.64 k allocations: 1.564 MiB) +# main(; ntasks = 3) # 0.700423 seconds (2.38 k allocations: 2.332 MiB) +# main(; ntasks = 4) # 0.548356 seconds (3.12 k allocations: 3.099 MiB) +# ``` + +using Test #src +nK1, nf1 = main(; n = 5, ntasks = 1) #src +nK2, nf2 = main(; n = 5, ntasks = 2) #src +nK4, nf4 = main(; n = 5, ntasks = 4) #src +@test nK1 == nK2 == nK4 #src +@test nf1 == nf2 == nf4 #src #md # ## [Plain program](@id threaded_assembly-plain-program) #md # diff --git a/test/test_examples.jl b/test/test_examples.jl index d20001d18f..dca1ed73cf 100644 --- a/test/test_examples.jl +++ b/test/test_examples.jl @@ -61,3 +61,14 @@ if !Sys.iswindows() end end end + +module TestMultiThreading + mktempdir() do dir + cd(dir) do + # Silence the @time output + redirect_stdout(devnull) do + include(joinpath(@__DIR__, "../docs/src/literate-howto/threaded_assembly.jl")) + end + end + end +end From f1d785dc9c4291e597b513693bf8e0aa9879ff18 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 27 Sep 2024 10:49:31 +0200 Subject: [PATCH 34/50] Remove old example notebook, closes #91. (#1073) --- .../Yeoh_large_deformations.ipynb | 335 ------------------ 1 file changed, 335 deletions(-) delete mode 100644 docs/old_examples/Yeoh_large_deformations.ipynb diff --git a/docs/old_examples/Yeoh_large_deformations.ipynb b/docs/old_examples/Yeoh_large_deformations.ipynb deleted file mode 100644 index 50819ab15e..0000000000 --- a/docs/old_examples/Yeoh_large_deformations.ipynb +++ /dev/null @@ -1,335 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Example showing an analysis of a plate with a circular hole with a large deformation material model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "using ForwardDiff\n", - "using Tensors\n", - "using Ferrite\n", - "using MAT\n", - "using NLsolve" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Constitutive law" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "immutable YeohMaterial{T}\n", - " λ::T\n", - " μ::T\n", - " c2::T\n", - " c3::T\n", - "end\n", - "\n", - "function Ψ_Yeoh(C, mp::YeohMaterial)\n", - " μ, λ, c2, c3 = mp.μ, mp.λ, mp.c2, mp.c3\n", - " Ct = convert(Tensor{2, 2}, C)\n", - " J = sqrt(det(Ct))\n", - " Ic = trace(Ct) + 1\n", - " lnJ = log(J)\n", - " return μ/2 * (Ic - 3) + c2*(Ic - 3)^2 + c3 * (Ic - 3)^3 - μ*lnJ + λ/2 * lnJ^2\n", - "end\n", - "\n", - "# Parameters used\n", - "function get_material()\n", - " μ = 1.267e6\n", - " λ = 1.457e7\n", - " c2 = -μ/7.9\n", - " c3 = μ/41\n", - " return YeohMaterial(μ, λ, c2, c3)\n", - "end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Element internal forces" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Takes the initial + current configuration, the fe_value object and the material parameters and\n", - "# computes the internal forces for the element.\n", - "function intf_element{T,Q,dim}(x::Vector{T}, X::Vector{Q}, fe_values::FEValues{dim}, \n", - " material_parameters::YeohMaterial)\n", - " # Closures for Forward Diff\n", - " Ψ_Yeohh(C) = Ψ_Yeoh(C, material_parameters)\n", - " ∂Ψ_Yeoh∂C = ForwardDiff.gradient(Ψ_Yeohh)\n", - " S_Yeoh(C) = 2 * ∂Ψ_Yeoh∂C(C)\n", - " \n", - " # Reinterpret x and X as vectors of first order tensors\n", - " n_basefuncs = n_basefunctions(get_functionspace(fe_values))\n", - " @assert length(x) == length(X) == dim * n_basefuncs\n", - " X_vec = reinterpret(Vec{dim, Q}, X, (n_basefuncs,))\n", - " x_vec = reinterpret(Vec{dim, T}, x, (n_basefuncs,))\n", - " \n", - " reinit!(fe_values, X_vec)\n", - " \n", - " fe = [zero(Tensor{1, dim, T}) for i in 1:n_basefuncs]\n", - "\n", - " for q_point in 1:length(Ferrite.points(get_quadrule(fe_values)))\n", - " F = function_vector_gradient(fe_values, q_point, x_vec)\n", - " C = F' ⋅ F\n", - " S = Tensor{2, 2}(S_Yeoh(vec(C)))\n", - " P = F ⋅ S\n", - " for i in 1:n_basefuncs\n", - " fe[i] += P ⋅ shape_gradient(fe_values, q_point, i) * detJdV(fe_values, q_point)\n", - " end\n", - " end\n", - " \n", - " return reinterpret(T, fe, (dim * n_basefuncs,))\n", - "end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Read input" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "immutable CALMesh2D\n", - " coord::Matrix{Float64}\n", - " dof::Matrix{Int}\n", - " edof::Matrix{Int}\n", - " ex::Matrix{Float64}\n", - " ey::Matrix{Float64}\n", - "end\n", - "\n", - "# Reads the data from the .mat file and stores it in CALMesh2D as well as returning\n", - "# the dofs that are free/prescribed and fixed,\n", - "function read_data()\n", - " vars = matread(\"cass2_mesh_data.mat\");\n", - " dof_fixed = convert(Vector{Int}, vec(vars[\"dof_fixed\"]))\n", - " Coord = vars[\"Coord\"]'\n", - " Ex, Ey = vars[\"Ex\"], vars[\"Ey\"]\n", - " dof_prescr = convert(Vector{Int}, vec(vars[\"dof_prescr\"]))\n", - " Edof = convert(Matrix{Int}, vars[\"Edof\"])'[2:end,:]\n", - " Dof = convert(Matrix{Int}, vars[\"Dof\"]')\n", - " dof_free = convert(Vector{Int}, vec(vars[\"dof_free\"]));\n", - " return CALMesh2D(Coord, Dof, Edof, Ex, Ey), dof_prescr, dof_fixed, dof_free\n", - "end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Global internal forces" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Assembles the global internal forces as well as the reaction forces\n", - "# for the prescribed dofs\n", - "function internal_forces!(fvec, X, x, mesh, material_parameters, dof_free, dof_fixed, fe_values, f_react)\n", - " f_full = zeros(length(mesh.dof))\n", - "\n", - " fill!(fvec, 0.0)\n", - " for i in 1:size(mesh.edof, 2)\n", - " dofs = mesh.edof[:, i]\n", - " fe = intf_element(x[dofs], X[dofs], fe_values, material_parameters)\n", - " f_full[dofs] += fe\n", - " end\n", - " f_react[dof_fixed] = f_full[dof_fixed]\n", - " copy!(fvec, f_full[dof_free])\n", - "end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Global stiffness matrix" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "function grad!(fvec, X, x, mesh, material_parameters, dof_free, fe_values)\n", - " n_basefuncs = n_basefunctions(get_functionspace(fe_values))\n", - "\n", - " assembler = start_assemble()\n", - " XX = zeros(2 * n_basefuncs)\n", - " intf(x) = intf_element(x, XX, fe_values, material_parameters)\n", - " grad! = ForwardDiff.jacobian(intf, mutates = true)\n", - " Ke = zeros(2*n_basefuncs, 2*n_basefuncs)\n", - " for i in 1:size(mesh.edof, 2)\n", - " dofs = mesh.edof[:, i]\n", - " copy!(XX, X[dofs])\n", - " grad!(Ke, x[dofs])\n", - " assemble(dofs, assembler, Ke)\n", - " end\n", - " K = finish_assemble(assembler)\n", - " return K[dof_free, dof_free]\n", - "end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## VTK output" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "function vtkoutput(pvd, i, mesh, X, x, topology, f_react)\n", - " u = x - X\n", - " nnodes = div(length(mesh.dof), 2)\n", - " nrelem = size(mesh.edof, 2)\n", - "\n", - " disp = u\n", - " disp = reshape(disp, (2, nnodes))\n", - " disp = [disp; zeros(nnodes)']\n", - "\n", - " f_react = reshape(f_react, (2, nnodes))\n", - " f_react = [f_react; zeros(nnodes)']\n", - "\n", - "\n", - " vtkfile = vtk_grid(topology, mesh.coord, \"box_$i\")\n", - " vtk_point_data(vtkfile, disp, \"displacement\")\n", - " vtk_point_data(vtkfile, f_react, \"reaction_forces\")\n", - " collection_add_timestep(pvd, vtkfile, float(i))\n", - "end" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Main function" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "function go()\n", - " # Get the data\n", - " mesh, dof_prescr, dof_fixed, dof_free = read_data()\n", - " \n", - " # Get the material\n", - " material_parameters = get_material()\n", - "\n", - " topology = topologyxtr(mesh.edof, mesh.coord, mesh.dof, 3)\n", - " pvd = paraview_collection(\"box\")\n", - " \n", - " function_space = Lagrange{2, RefTetrahedron, 1}()\n", - " quad_rule = QuadratureRule(Dim{2}, RefTetrahedron(), 1)\n", - " fe_values = FEValues(Float64, quad_rule, function_space)\n", - " \n", - " # Initialize\n", - " X = vec(mesh.coord)\n", - " x = copy(X)\n", - " prev_x = copy(X)\n", - " f_react = zeros(X)\n", - " \n", - " end_displacement = 30\n", - " nsteps = 20\n", - " for i in 1:nsteps\n", - " # Set current config to correct value.\n", - " prev_x[dof_prescr] = X[dof_prescr] + i / nsteps * end_displacement\n", - " \n", - " # Newton guess\n", - " dx0 = zeros(length(dof_free))\n", - "\n", - " function f!(dx, fvec)\n", - " copy!(x, prev_x)\n", - " x[dof_free] += dx\n", - " internal_forces!(fvec, X, x, mesh, material_parameters, dof_free, dof_fixed, fe_values, f_react)\n", - " end\n", - "\n", - " function g!(dx, g)\n", - " fvec = zeros(length(dof_free))\n", - " copy!(x, prev_x)\n", - " x[dof_free] += dx\n", - " K = grad!(fvec, X, x, mesh, material_parameters, dof_free, fe_values)\n", - " copy!(g, K)\n", - " end\n", - "\n", - " println(\"Timestep $i out of $nsteps\")\n", - " df = DifferentiableSparseMultivariateFunction(f!, g!)\n", - " res = nlsolve(df, dx0; ftol = 1e-6, iterations = 20, method=:newton, show_trace=true)\n", - " if !converged(res)\n", - " error(\"Global equation did not converge\")\n", - " end\n", - " dx_conv = res.zero::Vector{Float64}\n", - " # Update converged solution\n", - " prev_x[dof_free] += dx_conv\n", - " vtkoutput(pvd, i, mesh, X, x, topology, f_react)\n", - " end\n", - " vtk_save(pvd)\n", - " return\n", - "end" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "go()" - ] - } - ], - "metadata": { - "kernelspec": { - "name": "julia-0.6", - "display_name": "Julia 0.6.0", - "language": "julia" - }, - "language_info": { - "mimetype": "application/julia", - "file_extension": ".jl", - "version": "0.6.0", - "name": "julia" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} From c6e65e057badc454b7b32e97f504a60ee4fa0a3f Mon Sep 17 00:00:00 2001 From: Mikkel Paltorp Date: Fri, 27 Sep 2024 11:45:34 +0200 Subject: [PATCH 35/50] Fix typos in FE intro documentation (#1074) --- docs/src/topics/fe_intro.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/topics/fe_intro.md b/docs/src/topics/fe_intro.md index 78b0721b59..5744180132 100644 --- a/docs/src/topics/fe_intro.md +++ b/docs/src/topics/fe_intro.md @@ -56,7 +56,7 @@ Find $u \in \mathbb{U}$ s.t. ```math \int_\Omega \nabla \delta u \cdot (k \nabla u) \, \mathrm{d}\Omega = \int_{\Gamma_\mathrm{N}} \delta u \, q^\mathrm{p} \, \mathrm{d}\Gamma + -\int_\Omega \delta u \, b \, \mathrm{d}\Omega \quad \forall \, \delta u \in \mathbb{T} +\int_\Omega \delta u \, f \, \mathrm{d}\Omega \quad \forall \, \delta u \in \mathbb{T} ``` where $\mathbb{U}, \mathbb{T}$ are suitable function spaces with sufficiently regular @@ -75,7 +75,7 @@ and denote it with $\Omega_h$. In this example the corners of the triangles are Next we introduce the finite element approximation $u_\mathrm{h} \approx u$ as a sum of N nodal *shape functions*, where we denote each of these function by $\phi_i$ and the corresponding *nodal values* $\hat{u}_i$. Note that *shape functions* are sometimes referred to as -*base functions* or *trial functions*, and instead of $\phi_i$ they are sometimes denoted $N_i$. +*basis functions* or *trial functions*, and instead of $\phi_i$ they are sometimes denoted $N_i$. In this example we choose to approximate the test function in the same way. This approach is known as the *Galerkin finite element method*. Formally we write the evaluation of our approximations at a specific point $\mathbf{x}$ in our domain $\Omega$ as: From 0f387023d38ef916bc5880e7faece5893e620dca Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 27 Sep 2024 13:45:30 +0200 Subject: [PATCH 36/50] Extend COOAssembler to also handle vector assembly (#1058) --- docs/src/literate-tutorials/stokes-flow.jl | 4 +- src/assembler.jl | 138 +++++++++++++++------ test/test_assemble.jl | 50 ++++++-- 3 files changed, 137 insertions(+), 55 deletions(-) diff --git a/docs/src/literate-tutorials/stokes-flow.jl b/docs/src/literate-tutorials/stokes-flow.jl index 70c5da2e16..e5e97c6fd5 100644 --- a/docs/src/literate-tutorials/stokes-flow.jl +++ b/docs/src/literate-tutorials/stokes-flow.jl @@ -284,7 +284,7 @@ end # example](https://www.dealii.org/current/doxygen/deal.II/step_11.html). function setup_mean_constraint(dh, fvp) - assembler = start_assemble() + assembler = Ferrite.COOAssembler() ## All external boundaries set = union( getfacetset(dh.grid, "Γ1"), @@ -313,7 +313,7 @@ function setup_mean_constraint(dh, fvp) ## Assemble to row 1 assemble!(assembler, [1], element_dofs_p, Ce) end - C = finish_assemble(assembler) + C, _ = finish_assemble(assembler) ## Create an AffineConstraint from the C-matrix _, J, V = findnz(C) _, constrained_dof_idx = findmax(abs2, V) diff --git a/src/assembler.jl b/src/assembler.jl index e79521dc4b..6c0ffc5203 100644 --- a/src/assembler.jl +++ b/src/assembler.jl @@ -2,58 +2,98 @@ abstract type AbstractAssembler end abstract type AbstractCSCAssembler <: AbstractAssembler end """ -This assembler creates a COO (**coo**rdinate format) representation of a sparse matrix during -assembly and converts it into a `SparseMatrixCSC{T}` on finalization. + struct COOAssembler{Tv, Ti} + +This assembler creates a COO (**coo**rdinate format) representation of a sparse matrix +during assembly and converts it into a `SparseMatrixCSC{Tv, Ti}` on finalization. """ -struct COOAssembler{T} # <: AbstractAssembler - I::Vector{Int} - J::Vector{Int} - V::Vector{T} +struct COOAssembler{Tv, Ti} # <: AbstractAssembler + nrows::Int + ncols::Int + f::Vector{Tv} + I::Vector{Ti} + J::Vector{Ti} + V::Vector{Tv} end -COOAssembler(N) = COOAssembler(Float64, N) - -function COOAssembler(::Type{T}, N) where T +function COOAssembler{Tv, Ti}(nrows::Int, ncols::Int; sizehint::Int = 0) where {Tv, Ti} I = Int[] J = Int[] - V = T[] - sizehint!(I, N) - sizehint!(J, N) - sizehint!(V, N) - - COOAssembler(I, J, V) + V = Tv[] + sizehint!(I, sizehint) + sizehint!(J, sizehint) + sizehint!(V, sizehint) + f = Tv[] + return COOAssembler{Tv, Ti}(nrows, ncols, f, I, J, V) end """ - start_assemble([N=0]) -> COOAssembler - -Create an `Assembler` object which can be used to assemble element contributions to the -global sparse matrix. Use [`assemble!`](@ref) for each element, and [`finish_assemble`](@ref), -to finalize the assembly and return the sparse matrix. + COOAssembler(nrows::Int, ncols::Int; sizehint::Int = 0) -Note that giving a sparse matrix as input can be more efficient. See below and -as described in the [manual](@ref man-assembly). - -!!! note - When the same matrix pattern is used multiple times (for e.g. multiple time steps or - Newton iterations) it is more efficient to create the sparse matrix **once** and reuse - the same pattern. See the [manual section](@ref man-assembly) on assembly. +Create a new assembler. """ -function start_assemble(N::Int=0) - return start_assemble(Float64, N) -end - -function start_assemble(t::Type{T}, N::Int=0) where T - return COOAssembler(t, N) +function COOAssembler(nrows::Int, ncols::Int; sizehint::Int = 0) + return COOAssembler{Float64, Int}(nrows, ncols; sizehint = sizehint) end +COOAssembler(; sizehint::Int = 0) = COOAssembler(-1, -1; sizehint = sizehint) + +# """ +# start_assemble(; sizehint::Int = 0) -> COOAssembler +# start_assemble(nrows::Int, ncols::Int; sizehint::Int = 0) -> COOAssembler + +# Create an `COOAssembler` which can be used to assemble element contributions to a global +# sparse matrix and vector. `nrows` is the number of rows in the final matrix and `ncols` the +# number of columns. If `nrows` and `ncols` are not passed they are inferred from the +# maximum indices added to the assembler during assembly. `sizehint` is a hint for how many +# entries in total will be added to the assembler and can be passed to optimize for +# allocations. + +# Use [`assemble!`](@ref) to insert element contributions, and [`finish_assemble`](@ref), to +# finalize the assembly and return the sparse matrix (and optionally vector). + +# Note that allocating a sparse matrix and assemble into it is generally preferred. See below +# and the [manual section on assembly](@ref man-assembly). + +# !!! note +# When the same matrix pattern is used multiple times (for e.g. multiple time steps or +# Newton iterations) it is more efficient to create the sparse matrix **once** and reuse +# the same pattern. See the [manual section](@ref man-assembly) on assembly. +# """ +# function start_assemble(nrows::Int, ncols::Int; sizehint::Int = 0) +# return COOAssembler{Float64, Int}(nrows, ncols; sizehint = sizehint) +# end + +# function start_assemble(; sizehint::Int = 0) +# return COOAssembler{Float64, Int}(-1, -1; sizehint = sizehint) +# end """ assemble!(a::COOAssembler, dofs, Ke) + assemble!(a::COOAssembler, dofs, Ke, fe) -Assembles the element matrix `Ke` into `a`. +Assembles the element matrix `Ke` and element vector `fe` into `a`. """ -function assemble!(a::COOAssembler{T}, dofs::AbstractVector{Int}, Ke::AbstractMatrix{T}) where {T} +function assemble!(a::COOAssembler{T}, dofs::AbstractVector{Int}, Ke::AbstractMatrix{T}, fe::Union{AbstractVector{T}, Nothing} = nothing) where {T} assemble!(a, dofs, dofs, Ke) + if fe !== nothing + # If the final number of rows is unknown we grow the vector lazily, + # otherwise we resize it directly to nrows. + if a.nrows == -1 + m = maximum(dofs; init = 0) + lf = length(a.f) + if lf < m + resize!(a.f, m) + for i in (lf + 1):m + a.f[i] = 0 + end + end + elseif isempty(a.f) + resize!(a.f, a.nrows) + fill!(a.f, 0) + end + assemble!(a.f, dofs, fe) + end + return end """ @@ -78,13 +118,31 @@ function assemble!(a::COOAssembler{T}, rowdofs::AbstractVector{Int}, coldofs::Ab end """ - finish_assemble(a::Assembler) -> K + finish_assemble(a::COOAssembler) -> K, f -Finalizes an assembly. Returns a sparse matrix with the -assembled values. Note that this step is not necessary for `AbstractAssembler`s. +Finalize the assembly and return the sparse matrix `K::SparseMatrixCSC` and vector +`f::Vector`. If the assembler have not been used for vector assembly, `f` is an empty +vector. """ function finish_assemble(a::COOAssembler) - return sparse(a.I, a.J, a.V) + # Create the matrix + nrows = a.nrows == -1 ? maximum(a.I) : a.nrows + ncols = a.ncols == -1 ? maximum(a.J) : a.ncols + K = sparse(a.I, a.J, a.V, nrows, ncols) + # Finalize the vector + f = a.f + if !isempty(f) + # There have been things assembled, make sure it is resized correctly + lf = length(f) + @assert lf <= nrows + if lf < nrows + resize!(f, nrows) + for i in (lf + 1):nrows + f[i] = 0 + end + end + end + return K, f end """ @@ -173,7 +231,7 @@ function start_assemble(K::Symmetric{T,<:SparseMatrixCSC}, f::Vector=T[]; fillze end function finish_assemble(a::Union{CSCAssembler, SymmetricCSCAssembler}) - return a.K, isempty(a.f) ? nothing : a.f + return a.K, a.f end """ diff --git a/test/test_assemble.jl b/test/test_assemble.jl index ae8c1b3a68..d1a586d3eb 100644 --- a/test/test_assemble.jl +++ b/test/test_assemble.jl @@ -1,31 +1,55 @@ @testset "assemble" begin dofs = [1, 3, 5, 7] + maxd = maximum(dofs) - # residual + # Vector assembly ge = rand(4) g = zeros(8) assemble!(g, dofs, ge) - @test g[1] == ge[1] - @test g[3] == ge[2] - @test g[5] == ge[3] - @test g[7] == ge[4] + @test g[dofs] == ge - # stiffness - a = start_assemble() + # COOAssembler: matrix only, inferred size + a = Ferrite.COOAssembler() Ke = rand(4, 4) assemble!(a, dofs, Ke) - K = finish_assemble(a) - @test K[1,1] == Ke[1,1] - @test K[1,5] == Ke[1,3] - @test K[5,1] == Ke[3,1] + K, f = finish_assemble(a) + @test K[dofs, dofs] == Ke + @test size(K) == (maxd, maxd) + @test isempty(f) + + # COOAssembler: matrix only, given size + a = Ferrite.COOAssembler(10, 10) + assemble!(a, dofs, Ke) + K, f = finish_assemble(a) + @test K[dofs, dofs] == Ke + @test size(K) == (10, 10) + @test isempty(f) + + # COOAssembler: matrix and vector, inferred size + a = Ferrite.COOAssembler() + assemble!(a, dofs, Ke, ge) + K, f = finish_assemble(a) + @test K[dofs, dofs] == Ke + @test f[dofs] == ge + @test size(K) == (maxd, maxd) + @test length(f) == maxd + + # COOAssembler: matrix and vector, given size + a = Ferrite.COOAssembler(10, 10) + assemble!(a, dofs, Ke, ge) + K, f = finish_assemble(a) + @test K[dofs, dofs] == Ke + @test f[dofs] == ge + @test size(K) == (10, 10) + @test length(f) == 10 # assemble with different row and col dofs rdofs = [1,4,6] cdofs = [1,7] - a = start_assemble() + a = Ferrite.COOAssembler() Ke = rand(length(rdofs), length(cdofs)) assemble!(a, rdofs, cdofs, Ke) - K = finish_assemble(a) + K, _ = finish_assemble(a) @test (K[rdofs,cdofs] .== Ke) |> all # SparseMatrix assembler From b6a49844a4270de5e8ad6099701f2340f457111e Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 27 Sep 2024 13:59:38 +0200 Subject: [PATCH 37/50] Miscellaneous fixes to documentation (#1075) - Don't code-quote package names and drop .jl prefix when unnecessary - Use consistent capitalization in headers - Use a prettier TODO note in DoF docs --- CONTRIBUTING.md | 2 +- README.md | 6 ++--- docs/src/devdocs/index.md | 2 +- docs/src/devdocs/performance.md | 2 +- docs/src/index.md | 2 +- docs/src/literate-howto/postprocessing.jl | 2 +- .../computational_homogenization.jl | 6 ++--- .../src/literate-tutorials/hyperelasticity.jl | 2 +- .../literate-tutorials/linear_elasticity.jl | 2 +- docs/src/literate-tutorials/linear_shell.jl | 2 +- docs/src/literate-tutorials/ns_vs_diffeq.jl | 6 ++--- docs/src/literate-tutorials/porous_media.jl | 2 +- docs/src/literate-tutorials/stokes-flow.jl | 4 ++-- docs/src/reference/boundary_conditions.md | 2 +- docs/src/reference/export.md | 2 +- docs/src/topics/FEValues.md | 4 ++-- docs/src/topics/SimpleCellValues_literate.jl | 4 ++-- docs/src/topics/boundary_conditions.md | 12 +++++----- docs/src/topics/degrees_of_freedom.md | 6 +++-- docs/src/topics/fe_intro.md | 2 +- docs/src/topics/grid.md | 24 +++++++++---------- src/Quadrature/quadrature.jl | 2 +- test/test_utils.jl | 2 +- 23 files changed, 51 insertions(+), 49 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 70a2a81acb..8fcaa50c0e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ almost instant feedback in the browser. ## Reporting issues -If you have found a bug or a problem with Ferrite.jl you can open an [issue][new-issue]. Try +If you have found a bug or a problem with Ferrite you can open an [issue][new-issue]. Try to include as much information about the problem as possible and preferably some code that can be copy-pasted to reproduce it (see [How to create a Minimal, Reproducible Example][so-mre]). diff --git a/README.md b/README.md index 85ad4649e9..9416f709c7 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ welcome. See [CONTRIBUTING](CONTRIBUTING.md) for more details. ## Questions -If you have questions about Ferrite.jl you're welcome to reach out to us on the [Julia +If you have questions about Ferrite you're welcome to reach out to us on the [Julia Slack][julia-slack] under `#ferrite-fem` or on [Zulip][julia-zulip] under `#Ferrite.jl`. Alternatively you can start a [new discussion][gh-discussion] in the discussion forum on the -repository. Feel free to ask us even if you are not sure the problem is with Ferrite.jl. +repository. Feel free to ask us even if you are not sure the problem is with Ferrite. If you encounter what you think is a bug please report it, see [CONTRIBUTING.md](CONTRIBUTING.md#reporting-issues) for more information. @@ -40,7 +40,7 @@ Please keep in mind that we are part of the Julia community and adhere to the [Julia Community Standards][standards]. ## Related packages -The following registered packages are part of the `Ferrite.jl` ecosystem in addition to Ferrite itself: +The following registered packages are part of the Ferrite ecosystem in addition to Ferrite itself: * [Tensors.jl][Tensors]: Used throughout Ferrite for efficient tensor manipulation. * [FerriteViz.jl][FerriteViz]: [Makie.jl][Makie]-based visualization of Ferrite data. * [FerriteGmsh.jl][FerriteGmsh]: Create, interact with, and import [Gmsh][Gmsh] meshes into Ferrite. diff --git a/docs/src/devdocs/index.md b/docs/src/devdocs/index.md index 1d43bbc2b9..f6a602945e 100644 --- a/docs/src/devdocs/index.md +++ b/docs/src/devdocs/index.md @@ -1,6 +1,6 @@ # Developer documentation -Here you can find some documentation of the internals of Ferrite.jl which are useful when +Here you can find some documentation of the internals of Ferrite which are useful when developing the library. ```@contents diff --git a/docs/src/devdocs/performance.md b/docs/src/devdocs/performance.md index 32cb476405..751dd9b68d 100644 --- a/docs/src/devdocs/performance.md +++ b/docs/src/devdocs/performance.md @@ -1,4 +1,4 @@ -# [Performance Analysis](@id devdocs-performance) +# [Performance analysis](@id devdocs-performance) In the benchmark folder we provide basic infrastructure to analyze the performance of Ferrite to help tracking down performance regression issues. Two basic tools can be diff --git a/docs/src/index.md b/docs/src/index.md index b4656107c2..2eb70f3b75 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -97,4 +97,4 @@ Ferrite is still under active development. If you find a bug, or have ideas for improvements, you are encouraged to interact with the developers on the [Ferrite GitHub repository](https://github.com/Ferrite-FEM/Ferrite.jl). There is also a thorough contributor guide which can be found in -[`CONTRIBUTING.md`](https://github.com/Ferrite-FEM/Ferrite.jl/blob/master/CONTRIBUTING.md). +[CONTRIBUTING.md](https://github.com/Ferrite-FEM/Ferrite.jl/blob/master/CONTRIBUTING.md). diff --git a/docs/src/literate-howto/postprocessing.jl b/docs/src/literate-howto/postprocessing.jl index 671af15366..f2afd77c47 100644 --- a/docs/src/literate-howto/postprocessing.jl +++ b/docs/src/literate-howto/postprocessing.jl @@ -93,7 +93,7 @@ VTKGridFile("heat_equation_flux", grid) do vtk write_projection(vtk, projector, q_projected, "q") end; -# ## Point Evaluation +# ## Point evaluation # ![](heat_square_pointevaluation.png) # # *Figure 2*: Visualization of the cut line where we want to compute diff --git a/docs/src/literate-tutorials/computational_homogenization.jl b/docs/src/literate-tutorials/computational_homogenization.jl index 9df9e6d64f..4abf225720 100644 --- a/docs/src/literate-tutorials/computational_homogenization.jl +++ b/docs/src/literate-tutorials/computational_homogenization.jl @@ -179,7 +179,7 @@ # ## Commented program # -# Now we will see how this can be implemented in `Ferrite`. What follows is a program +# Now we will see how this can be implemented in Ferrite. What follows is a program # with comments in between which describe the different steps. #md # You can also find the same program without comments at the end of the page, #md # see [Plain program](@ref homogenization-plain-program). @@ -189,8 +189,8 @@ using Test #src # We first load the mesh file [`periodic-rve.msh`](periodic-rve.msh) # ([`periodic-rve-coarse.msh`](periodic-rve-coarse.msh) for a coarser mesh). The mesh is -# generated with [`gmsh`](https://gmsh.info/), and we read it in as a `Ferrite` grid using -# the [`FerriteGmsh`](https://github.com/Ferrite-FEM/FerriteGmsh.jl) package: +# generated with [Gmsh](https://gmsh.info/), and we read it in as a Ferrite `Grid` using +# the [FerriteGmsh.jl](https://github.com/Ferrite-FEM/FerriteGmsh.jl) package: using FerriteGmsh diff --git a/docs/src/literate-tutorials/hyperelasticity.jl b/docs/src/literate-tutorials/hyperelasticity.jl index 1cc5b10780..271efd2ebc 100644 --- a/docs/src/literate-tutorials/hyperelasticity.jl +++ b/docs/src/literate-tutorials/hyperelasticity.jl @@ -51,7 +51,7 @@ # and print a summary at the end, # [ProgressMeter.jl](https://github.com/timholy/ProgressMeter.jl) for showing a simple # progress bar, and -# [IterativeSolvers](https://github.com/JuliaLinearAlgebra/IterativeSolvers.jl) for solving +# [IterativeSolvers.jl](https://github.com/JuliaLinearAlgebra/IterativeSolvers.jl) for solving # the linear system using conjugate gradients. using Ferrite, Tensors, TimerOutputs, ProgressMeter, IterativeSolvers diff --git a/docs/src/literate-tutorials/linear_elasticity.jl b/docs/src/literate-tutorials/linear_elasticity.jl index 156a4b39ce..eead07c6ac 100644 --- a/docs/src/literate-tutorials/linear_elasticity.jl +++ b/docs/src/literate-tutorials/linear_elasticity.jl @@ -99,7 +99,7 @@ # First we load Ferrite, and some other packages we need. using Ferrite, FerriteGmsh, SparseArrays # As in the heat equation tutorial, we will use a unit square - but here we'll load the grid of the Ferrite logo! -# This is done by downloading [`logo.geo`](logo.geo) and loading it using [`FerriteGmsh.jl`](https://github.com/Ferrite-FEM/FerriteGmsh.jl), +# This is done by downloading [`logo.geo`](logo.geo) and loading it using [FerriteGmsh.jl](https://github.com/Ferrite-FEM/FerriteGmsh.jl), using Downloads: download logo_mesh = "logo.geo" asset_url = "https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/gh-pages/assets/" diff --git a/docs/src/literate-tutorials/linear_shell.jl b/docs/src/literate-tutorials/linear_shell.jl index b00739dc72..014ce2948a 100644 --- a/docs/src/literate-tutorials/linear_shell.jl +++ b/docs/src/literate-tutorials/linear_shell.jl @@ -4,7 +4,7 @@ #- # ## Introduction # -# In this example we show how shell elements can be analyzed in Ferrite.jl. The shell implemented here comes from the book +# In this example we show how shell elements can be analyzed with Ferrite. The shell implemented here comes from the book # "The finite element method - Linear static and dynamic finite element analysis" by Hughes (1987), and a brief description of it is # given at the end of this tutorial. The first part of the tutorial explains how to set up the problem. diff --git a/docs/src/literate-tutorials/ns_vs_diffeq.jl b/docs/src/literate-tutorials/ns_vs_diffeq.jl index b45f9c6e65..478314fe27 100644 --- a/docs/src/literate-tutorials/ns_vs_diffeq.jl +++ b/docs/src/literate-tutorials/ns_vs_diffeq.jl @@ -13,7 +13,7 @@ # In this example we focus on a simple but visually appealing problem from # fluid dynamics, namely vortex shedding. This problem is also known as # [von-Karman vortex streets](https://en.wikipedia.org/wiki/K%C3%A1rm%C3%A1n_vortex_street). Within this example, we show how to utilize [DifferentialEquations.jl](https://github.com/SciML/DifferentialEquations.jl) -# in tandem with Ferrite.jl to solve this space-time problem. To keep things simple we use a naive approach +# in tandem with Ferrite to solve this space-time problem. To keep things simple we use a naive approach # to discretize the system. # # ## Remarks on DifferentialEquations.jl @@ -128,7 +128,7 @@ using OrdinaryDiffEq ν = 1.0/1000.0; #dynamic viscosity # Next a rectangular grid with a cylinder in it has to be generated. -# We use `Gmsh` for the creation of the mesh and `FerriteGmsh` to translate it to a `Ferrite.Grid`. +# We use Gmsh.jl for the creation of the mesh and FerriteGmsh.jl to translate it to a `Ferrite.Grid`. # Note that the mesh is pretty fine, leading to a high memory consumption when # feeding the equation system to direct solvers. using FerriteGmsh @@ -195,7 +195,7 @@ add!(dh, :v, ip_v) add!(dh, :p, ip_p) close!(dh); -# ### Boundary Conditions +# ### Boundary conditions # As in the DFG benchmark we apply no-slip conditions to the top, bottom and # cylinder boundary. The no-slip condition states that the velocity of the # fluid on this portion of the boundary is fixed to be zero. diff --git a/docs/src/literate-tutorials/porous_media.jl b/docs/src/literate-tutorials/porous_media.jl index cb42fcda1c..8af94e8cc4 100644 --- a/docs/src/literate-tutorials/porous_media.jl +++ b/docs/src/literate-tutorials/porous_media.jl @@ -245,7 +245,7 @@ function doassemble!(assembler, domain::FEDomain, a, a_old, Δt) end; # ### Mesh import -# In this example, we import the mesh from the Abaqus input file, [`porous_media_0p25.inp`](porous_media_0p25.inp) using `FerriteMeshParser`'s +# In this example, we import the mesh from the Abaqus input file, [`porous_media_0p25.inp`](porous_media_0p25.inp) using FerriteMeshParser's # `get_ferrite_grid` function. We then create one cellset for each phase (solid and porous) # for each element type. These 4 sets will later be used in their own `SubDofHandler` function get_grid() diff --git a/docs/src/literate-tutorials/stokes-flow.jl b/docs/src/literate-tutorials/stokes-flow.jl index e5e97c6fd5..b034e00e1b 100644 --- a/docs/src/literate-tutorials/stokes-flow.jl +++ b/docs/src/literate-tutorials/stokes-flow.jl @@ -19,8 +19,8 @@ # flow on a quarter circle. In particular it shows how to use periodic boundary conditions, # how to solve a problem with multiple unknown fields, and how to enforce a specific mean # value of the solution. For the mesh generation we use -# [`Gmsh.jl`](https://github.com/JuliaFEM/Gmsh.jl) and then use -# [`FerriteGmsh.jl`](https://github.com/Ferrite-FEM/FerriteGmsh.jl) to import the mesh into +# [Gmsh.jl](https://github.com/JuliaFEM/Gmsh.jl) and then use +# [FerriteGmsh.jl](https://github.com/Ferrite-FEM/FerriteGmsh.jl) to import the mesh into # Ferrite's format. # # The strong form of Stokes flow with velocity ``\boldsymbol{u}`` and pressure ``p`` can be diff --git a/docs/src/reference/boundary_conditions.md b/docs/src/reference/boundary_conditions.md index ca738dadea..2908ba4775 100644 --- a/docs/src/reference/boundary_conditions.md +++ b/docs/src/reference/boundary_conditions.md @@ -2,7 +2,7 @@ DocTestSetup = :(using Ferrite) ``` -# Boundary Conditions +# Boundary conditions ```@index Pages = ["boundary_conditions.md"] diff --git a/docs/src/reference/export.md b/docs/src/reference/export.md index 61e5849cb8..84c94b4e34 100644 --- a/docs/src/reference/export.md +++ b/docs/src/reference/export.md @@ -22,7 +22,7 @@ PointIterator PointLocation ``` -## VTK Export +## VTK export ```@docs VTKGridFile write_solution diff --git a/docs/src/topics/FEValues.md b/docs/src/topics/FEValues.md index f4abfdd3cd..b961303a12 100644 --- a/docs/src/topics/FEValues.md +++ b/docs/src/topics/FEValues.md @@ -1,5 +1,5 @@ # [FEValues](@id fevalues_topicguide) -A key type of object in `Ferrite.jl` is the so-called `FEValues`, where the most common ones are `CellValues` and `FacetValues`. These objects are used inside the element routines and are used to query the integration weights, shape function values and gradients, and much more; see [`CellValues`](@ref) and [`FacetValues`](@ref). For these values to be correct, it is necessary to reinitialize these for the current cell by using the [`reinit!`](@ref) function. This function maps the values from the reference cell to the actual cell, a process described in detail below, see [Mapping of finite elements](@ref mapping_theory). After that, we show an implementation of a [`SimpleCellValues`](@ref SimpleCellValues) type to illustrate how `CellValues` work for the most standard case, excluding the generalizations and optimization that complicates the actual code. +A key type of object in Ferrite is the so-called `FEValues`, where the most common ones are `CellValues` and `FacetValues`. These objects are used inside the element routines and are used to query the integration weights, shape function values and gradients, and much more; see [`CellValues`](@ref) and [`FacetValues`](@ref). For these values to be correct, it is necessary to reinitialize these for the current cell by using the [`reinit!`](@ref) function. This function maps the values from the reference cell to the actual cell, a process described in detail below, see [Mapping of finite elements](@ref mapping_theory). After that, we show an implementation of a [`SimpleCellValues`](@ref SimpleCellValues) type to illustrate how `CellValues` work for the most standard case, excluding the generalizations and optimization that complicates the actual code. ## [Mapping of finite elements](@id mapping_theory) The shape functions and gradients stored in an `FEValues` object, are reinitialized for each cell by calling the `reinit!` function. @@ -161,7 +161,7 @@ This gives the gradient ## [Walkthrough: Creating `SimpleCellValues`](@id SimpleCellValues) In the following, we walk through how to create a `SimpleCellValues` type which -works similar to `Ferrite.jl`'s `CellValues`, but is not performance optimized and not as general. The main purpose is to explain how the `CellValues` works for the standard case of `IdentityMapping` described above. +works similar to Ferrite's `CellValues`, but is not performance optimized and not as general. The main purpose is to explain how the `CellValues` works for the standard case of `IdentityMapping` described above. Please note that several internal functions are used, and these may change without a major version increment. Please see the [Developer documentation](@ref) for their documentation. ```@eval diff --git a/docs/src/topics/SimpleCellValues_literate.jl b/docs/src/topics/SimpleCellValues_literate.jl index 86a36c7e37..b3b107274d 100644 --- a/docs/src/topics/SimpleCellValues_literate.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -1,4 +1,4 @@ -# We start by including `Ferrite` and `Test` (to check our implementation). +# We start by including Ferrite and Test (to check our implementation). using Ferrite, Test # Then, we define a simple version of the cell values object, which only supports @@ -93,7 +93,7 @@ reinit!(simple_cv, x) reinit!(cv, x); # If we now pretend we are inside an element routine and have a vector of element degree of freedom values, -# `ue`. Then, we can check that our function values and gradients match `Ferrite`'s builtin `CellValues`: +# `ue`. Then, we can check that our function values and gradients match Ferrite's builtin `CellValues`: ue = rand(getnbasefunctions(simple_cv)) q_point = 2 @test function_value(cv, q_point, ue) ≈ function_value(simple_cv, q_point, ue) diff --git a/docs/src/topics/boundary_conditions.md b/docs/src/topics/boundary_conditions.md index 1bf96092ba..7f70ea671c 100644 --- a/docs/src/topics/boundary_conditions.md +++ b/docs/src/topics/boundary_conditions.md @@ -2,18 +2,18 @@ DocTestSetup = :(using Ferrite) ``` -# Initial and Boundary Conditions +# Boundary and initial conditions Every PDE is accompanied with boundary conditions. There are different types of boundary conditions, and they need to be handled in different ways. Below we discuss how to handle -the most common ones, Dirichlet and Neumann boundary conditions, and how to do it `Ferrite`. +the most common ones, Dirichlet and Neumann boundary conditions, and how to do it in Ferrite. While boundary conditions can be applied directly to nodes, vertices, edges, or faces, they are most commonly applied to [facets](@ref "Reference shapes"). Each facet is described by a [`FacetIndex`](@ref). When adding boundary conditions to points instead, vertices are preferred over nodes. -## Dirichlet Boundary Conditions +## Dirichlet boundary conditions At a Dirichlet boundary the unknown field is prescribed to a given value. For the discrete FE-solution this means that there are some degrees of freedom that are fixed. To handle @@ -94,7 +94,7 @@ end Equation](@ref tutorial-heat-equation). -## Neumann Boundary Conditions +## Neumann boundary conditions At the Neumann part of the boundary we know something about the gradient of the solution. Two different methods for applying these are described below. For complete examples that use Neumann boundary conditions, please see @@ -207,7 +207,7 @@ general be written as where ``\boldsymbol{R}`` is a rotation matrix. If the mapping between mirror and image is simply a translation (e.g. sides of a cube) this matrix will be the identity matrix. -In `Ferrite` this type of periodic Dirichlet boundary conditions can be added to the +In Ferrite this type of periodic Dirichlet boundary conditions can be added to the `ConstraintHandler` by constructing an instance of [`PeriodicDirichlet`](@ref). This is usually done it two steps. First we compute the mapping between mirror and image facets using [`collect_periodic_facets`](@ref). Here we specify the mirror set and image sets (the sets @@ -300,7 +300,7 @@ pdbc = PeriodicDirichlet( ) ``` -## Initial Conditions +## Initial conditions When solving time-dependent problems, initial conditions, different from zero, may be required. For finite element formulations of ODE-type, diff --git a/docs/src/topics/degrees_of_freedom.md b/docs/src/topics/degrees_of_freedom.md index c803c250ba..144b792570 100644 --- a/docs/src/topics/degrees_of_freedom.md +++ b/docs/src/topics/degrees_of_freedom.md @@ -42,5 +42,7 @@ close!(dh) ## Ordering of Dofs -ordered in the same order as we add to dofhandler -vertices -> edges -> faces -> volumes +!!! todo + Describe dof ordering within elements (vertices -> edges -> faces -> + volumes) and `dof_range`. + Describe (global) dof renumbering diff --git a/docs/src/topics/fe_intro.md b/docs/src/topics/fe_intro.md index 5744180132..957c8b21fa 100644 --- a/docs/src/topics/fe_intro.md +++ b/docs/src/topics/fe_intro.md @@ -161,7 +161,7 @@ On an intuitive level, and to explain the notation used in the implementation, w ``` being the chosen approximation when changing from the integral to the finite summation. -For an example of the implementation to solve a heat problem with `Ferrite` check out [this +For an example of the implementation to solve a heat problem with Ferrite check out [this thoroughly commented example](@ref tutorial-heat-equation). ## More details diff --git a/docs/src/topics/grid.md b/docs/src/topics/grid.md index f3c3138757..5709e4da00 100644 --- a/docs/src/topics/grid.md +++ b/docs/src/topics/grid.md @@ -4,21 +4,21 @@ DocTestSetup = :(using Ferrite) # Grid -## Mesh Reading +## Mesh reading A Ferrite `Grid` can be generated with the [`generate_grid`](@ref) function. More advanced meshes can be imported with the -[`FerriteMeshParser.jl`](https://github.com/Ferrite-FEM/FerriteMeshParser.jl) (from Abaqus input files), -or even created and translated with the [`Gmsh.jl`](https://github.com/JuliaFEM/Gmsh.jl) and [`FerriteGmsh.jl`](https://github.com/Ferrite-FEM/FerriteGmsh.jl) package, respectively. +[FerriteMeshParser.jl](https://github.com/Ferrite-FEM/FerriteMeshParser.jl) (from Abaqus input files), +or even created and translated with the [`Gmsh.jl`](https://github.com/JuliaFEM/Gmsh.jl) and [FerriteGmsh.jl](https://github.com/Ferrite-FEM/FerriteGmsh.jl) package, respectively. -### FerriteGmsh +### FerriteGmsh.jl -`FerriteGmsh.jl` supports all defined cells with an alias in [`Ferrite.jl`](https://github.com/Ferrite-FEM/Ferrite.jl/blob/master/src/Grid/grid.jl#L39-L54) as well as the 3D Serendipity `Cell{3,20,6}`. +FerriteGmsh.jl supports all defined cells with an alias in [Ferrite.jl](https://github.com/Ferrite-FEM/Ferrite.jl/blob/master/src/Grid/grid.jl#L39-L54) as well as the 3D Serendipity `Cell{3,20,6}`. Either, a mesh is created on the fly with the gmsh API or a mesh in `.msh` or `.geo` format can be read and translated with the `FerriteGmsh.togrid` function. ```@docs FerriteGmsh.togrid ``` -`FerriteGmsh.jl` supports currently the translation of `cellsets` and `facetsets`. +FerriteGmsh supports currently the translation of `cellsets` and `facetsets`. Such sets are defined in Gmsh as `PhysicalGroups` of dimension `dim` and `dim-1`, respectively. In case only a part of the mesh is the domain, the domain can be specified by providing the keyword argument `domain` the name of the `PhysicalGroups` in the [`FerriteGmsh.togrid`](@ref) function. @@ -28,14 +28,14 @@ In case only a part of the mesh is the domain, the domain can be specified by pr which doesn't harm the FE computation, but maybe distort your sophisticated grid operations (if present). For more information, see [this issue](https://github.com/Ferrite-FEM/FerriteGmsh.jl/issues/20). -If you want to read another, not yet supported cell from gmsh, consider to open a PR at `FerriteGmsh` that extends the [`gmshtoferritecell` dict](https://github.com/Ferrite-FEM/FerriteGmsh.jl/blob/c9de4f64b3ad3c73fcb36758855a6e517c6d0d95/src/FerriteGmsh.jl#L6-L15) +If you want to read another, not yet supported cell from gmsh, consider to open a PR at FerriteGmsh that extends the [`gmshtoferritecell` dict](https://github.com/Ferrite-FEM/FerriteGmsh.jl/blob/c9de4f64b3ad3c73fcb36758855a6e517c6d0d95/src/FerriteGmsh.jl#L6-L15) and if needed, reorder the element nodes by dispatching [`FerriteGmsh.translate_elements`](https://github.com/Ferrite-FEM/FerriteGmsh.jl/blob/c9de4f64b3ad3c73fcb36758855a6e517c6d0d95/src/FerriteGmsh.jl#L17-L63). The reordering of nodes is necessary if the Gmsh ordering doesn't match the one from Ferrite. Gmsh ordering is documented [here](https://gmsh.info/doc/texinfo/gmsh.html#Node-ordering). -For an exemplary usage of `Gmsh.jl` and `FerriteGmsh.jl`, consider the [Stokes flow](@ref tutorial-stokes-flow) and [Incompressible Navier-Stokes Equations via DifferentialEquations.jl](@ref tutorial-ins-ordinarydiffeq) example. +For an exemplary usage of Gmsh.jl and FerriteGmsh.jl, consider the [Stokes flow](@ref tutorial-stokes-flow) and [Incompressible Navier-Stokes Equations via DifferentialEquations.jl](@ref tutorial-ins-ordinarydiffeq) example. -### FerriteMeshParser +### FerriteMeshParser.jl -`FerriteMeshParser.jl` converts the mesh in an Abaqus input file (`.inp`) to a `Ferrite.Grid` with its function `get_ferrite_grid`. +FerriteMeshParser.jl converts the mesh in an Abaqus input file (`.inp`) to a `Ferrite.Grid` with its function `get_ferrite_grid`. The translations for most of Abaqus' standard 2d and 3d continuum elements to a `Ferrite.AbstractCell` are defined. Custom translations can be given as input, which can be used to import other (custom) elements or to override the default translation. ```@docs @@ -45,7 +45,7 @@ FerriteMeshParser.get_ferrite_grid If you are missing the translation of an Abaqus element that is equivalent to a `Ferrite.AbstractCell`, consider to open an [issue](https://github.com/Ferrite-FEM/FerriteMeshParser.jl/issues/new) or a pull request. -## `Grid` Datastructure +## `Grid` datastructure In Ferrite a Grid is a collection of `Node`s and `Cell`s and is parameterized in its physical dimensionality and cell type. `Node`s are points in the physical space and can be initialized by a N-Tuple, where N corresponds to the dimensions. @@ -135,7 +135,7 @@ Ferrite.get_coordinate_type(::SmallGrid{dim}) where dim = Vec{dim,Float64} Ferrite.nnodes_per_cell(grid::SmallGrid, i::Int=1) = Ferrite.nnodes(grid.cells_test[i]) ``` -These definitions make many of `Ferrite`s functions work out of the box, e.g. you can now call +These definitions make many of Ferrite functions work out of the box, e.g. you can now call `getcoordinates(grid, cellid)` on the `SmallGrid`. Now, you would be able to assemble the heat equation example over the new custom `SmallGrid` type. diff --git a/src/Quadrature/quadrature.jl b/src/Quadrature/quadrature.jl index bb32acdb2d..27379bddfa 100644 --- a/src/Quadrature/quadrature.jl +++ b/src/Quadrature/quadrature.jl @@ -33,7 +33,7 @@ function values at specific points: The quadrature rule consists of ``n_q`` points in space ``\\mathbf{x}_q`` with corresponding weights ``w_q``. -In `Ferrite`, the `QuadratureRule` type is mostly used as one of the components to create [`CellValues`](@ref). +In Ferrite, the `QuadratureRule` type is mostly used as one of the components to create [`CellValues`](@ref). **Common methods:** * [`getpoints`](@ref) : the points of the quadrature rule diff --git a/test/test_utils.jl b/test/test_utils.jl index 22f88005d0..512f7fc265 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -1,4 +1,4 @@ -# Some utility functions for testing Ferrite.jl +# Some utility functions for testing Ferrite using Ferrite: reference_shape_value From 4b8c2337d967c1053667aba0c616a129da6fe40b Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Fri, 27 Sep 2024 14:02:14 +0200 Subject: [PATCH 38/50] Update documentation dependencies. (#1076) --- docs/Manifest.toml | 111 +++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index daad98ede8..a4d4e460ea 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -5,9 +5,9 @@ manifest_format = "2.0" project_hash = "4e52f4aa4cee9f66ec4f633f0ae538fbd227ac5e" [[deps.ADTypes]] -git-tree-sha1 = "5a5eafb8344b81b8c2237f8a6f6b3602b3f6180e" +git-tree-sha1 = "eea5d80188827b35333801ef97a40c2ed653b081" uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" -version = "1.8.1" +version = "1.9.0" weakdeps = ["ChainRulesCore", "EnzymeCore"] [deps.ADTypes.extensions] @@ -211,10 +211,9 @@ uuid = "5217a498-cd5d-4ec6-b8c2-9b85a09b6e3e" version = "1.1.0" [[deps.ChunkSplitters]] -deps = ["TestItems"] -git-tree-sha1 = "01d5db8756afc4022b1cf267cfede13245226c72" +git-tree-sha1 = "397b871ff701290cc122cca06af61c5bdf9f5605" uuid = "ae650224-84b6-46f8-82ea-d812ca08434e" -version = "2.6.0" +version = "3.1.0" [[deps.CloseOpenIntervals]] deps = ["Static", "StaticArrayInterface"] @@ -367,9 +366,9 @@ version = "1.9.1" [[deps.DiffEqBase]] deps = ["ArrayInterface", "ConcreteStructs", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces"] -git-tree-sha1 = "fa7d580038451a8df4434ef5b079ac9b2d486194" +git-tree-sha1 = "6b1af0db32958b200b7b1745796432e75821bf48" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.155.1" +version = "6.155.2" [deps.DiffEqBase.extensions] DiffEqBaseCUDAExt = "CUDA" @@ -412,10 +411,10 @@ uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" version = "1.15.1" [[deps.DifferentiationInterface]] -deps = ["ADTypes", "Compat", "DocStringExtensions", "FillArrays", "LinearAlgebra", "PackageExtensionCompat", "SparseArrays", "SparseMatrixColorings"] -git-tree-sha1 = "9b23f9a816790b8ab9914c3c86321a546e92cbe7" +deps = ["ADTypes", "Compat", "LinearAlgebra", "PackageExtensionCompat"] +git-tree-sha1 = "50a19de1b366cbdcf2849dc5335bbd66e1db0dae" uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" -version = "0.5.17" +version = "0.6.1" [deps.DifferentiationInterface.extensions] DifferentiationInterfaceChainRulesCoreExt = "ChainRulesCore" @@ -425,10 +424,12 @@ version = "0.5.17" DifferentiationInterfaceFiniteDiffExt = "FiniteDiff" DifferentiationInterfaceFiniteDifferencesExt = "FiniteDifferences" DifferentiationInterfaceForwardDiffExt = "ForwardDiff" + DifferentiationInterfaceMooncakeExt = "Mooncake" DifferentiationInterfacePolyesterForwardDiffExt = "PolyesterForwardDiff" DifferentiationInterfaceReverseDiffExt = "ReverseDiff" + DifferentiationInterfaceSparseArraysExt = "SparseArrays" + DifferentiationInterfaceSparseMatrixColoringsExt = "SparseMatrixColorings" DifferentiationInterfaceSymbolicsExt = "Symbolics" - DifferentiationInterfaceTapirExt = "Tapir" DifferentiationInterfaceTrackerExt = "Tracker" DifferentiationInterfaceZygoteExt = ["Zygote", "ForwardDiff"] @@ -440,10 +441,12 @@ version = "0.5.17" FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41" FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" PolyesterForwardDiff = "98d1487c-24ca-40b6-b7ab-df2af84e126b" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" - Tapir = "07d77754-e150-4737-8c94-cd238a1fb45b" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" @@ -491,9 +494,9 @@ uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.4" [[deps.EnzymeCore]] -git-tree-sha1 = "8f205a601760f4798a10f138c3940f0451d95188" +git-tree-sha1 = "ee11500b17d87b22bc638e9ed8c71a7478c53d61" uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" -version = "0.7.8" +version = "0.8.3" weakdeps = ["Adapt"] [deps.EnzymeCore.extensions] @@ -536,9 +539,9 @@ version = "0.8.5" [[deps.FFMPEG]] deps = ["FFMPEG_jll"] -git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8" +git-tree-sha1 = "53ebe7511fa11d33bec688a9178fac4e49eeee00" uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" -version = "0.4.1" +version = "0.4.2" [[deps.FFMPEG_jll]] deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] @@ -745,9 +748,9 @@ version = "1.3.1" [[deps.Git_jll]] deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "d18fb8a1f3609361ebda9bf029b60fd0f120c809" +git-tree-sha1 = "ea372033d09e4552a04fd38361cd019f9003f4f4" uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.44.0+2" +version = "2.46.2+0" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] @@ -804,9 +807,9 @@ version = "0.1.17" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5e19e1e4fa3e71b774ce746274364aef0234634e" +git-tree-sha1 = "378267a829b1e17423d32ce6d905f37a12c1fd84" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.1+0" +version = "2.11.1+1" [[deps.IOCapture]] deps = ["Logging", "Random"] @@ -840,9 +843,9 @@ deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [[deps.InverseFunctions]] -git-tree-sha1 = "2787db24f4e03daf859c6509ff87764e4182f7d1" +git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.16" +version = "0.1.17" weakdeps = ["Dates", "Test"] [deps.InverseFunctions.extensions] @@ -903,9 +906,9 @@ version = "1.4.1" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c84a835e1a09b289ffcd2271bf2a337bbdda6637" +git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.3+0" +version = "3.0.4+0" [[deps.KLU]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] @@ -939,9 +942,9 @@ version = "18.1.7+0" [[deps.LZO_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "70c5da094887fd2cae843b8db33920bac4b6f07d" +git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.2+0" +version = "2.10.2+1" [[deps.LaTeXStrings]] git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" @@ -1096,17 +1099,17 @@ version = "5.0.0+0" [[deps.LinearSolve]] deps = ["ArrayInterface", "ChainRulesCore", "ConcreteStructs", "DocStringExtensions", "EnumX", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "LazyArrays", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "6c5e4555ac2bc449a28604e184f759d18fc08420" +git-tree-sha1 = "8941ad4bdd83768359801982e143008349b1a827" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "2.34.0" +version = "2.35.0" [deps.LinearSolve.extensions] LinearSolveBandedMatricesExt = "BandedMatrices" LinearSolveBlockDiagonalsExt = "BlockDiagonals" LinearSolveCUDAExt = "CUDA" LinearSolveCUDSSExt = "CUDSS" - LinearSolveEnzymeExt = ["Enzyme", "EnzymeCore"] - LinearSolveFastAlmostBandedMatricesExt = ["FastAlmostBandedMatrices"] + LinearSolveEnzymeExt = "EnzymeCore" + LinearSolveFastAlmostBandedMatricesExt = "FastAlmostBandedMatrices" LinearSolveHYPREExt = "HYPRE" LinearSolveIterativeSolversExt = "IterativeSolvers" LinearSolveKernelAbstractionsExt = "KernelAbstractions" @@ -1120,7 +1123,6 @@ version = "2.34.0" BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" FastAlmostBandedMatrices = "9d29842c-ecb8-4973-b1e9-a27b1157504e" HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771" @@ -1221,9 +1223,9 @@ version = "0.1.11" [[deps.MPItrampoline_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "8c35d5420193841b2f367e658540e8d9e0601ed0" +git-tree-sha1 = "fde81c9f9c94fe5fbeaed7b3f1330305cf9a327c" uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.4.0+0" +version = "5.5.0+0" [[deps.MacroTools]] deps = ["Markdown", "Random"] @@ -1310,9 +1312,9 @@ version = "1.0.2" [[deps.NearestNeighbors]] deps = ["Distances", "StaticArrays"] -git-tree-sha1 = "91a67b4d73842da90b526011fa85c5c4c9343fe0" +git-tree-sha1 = "3cebfc94a0754cc329ebc3bab1e6c89621e791ad" uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" -version = "0.4.18" +version = "0.4.20" [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" @@ -1373,9 +1375,9 @@ version = "1.3.5+1" [[deps.OhMyThreads]] deps = ["BangBang", "ChunkSplitters", "StableTasks", "TaskLocalValues"] -git-tree-sha1 = "6acbf70d8306a38a870be56d1841b2c9a4b17837" +git-tree-sha1 = "5f81bdb937fd857bac9548fa8ab9390a06864bb5" uuid = "67456a42-1dca-4109-a031-0a68de7e3ad5" -version = "0.6.2" +version = "0.7.0" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] @@ -1401,9 +1403,9 @@ version = "1.4.3" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1b35263570443fdd9e76c76b7062116e2f374ab8" +git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+0" +version = "3.0.15+1" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -1887,9 +1889,9 @@ version = "0.6.43" [[deps.SciMLBase]] deps = ["ADTypes", "Accessors", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "Expronicon", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "c96f81c3e98d5e2f0848fb42aba4383d772c3bb7" +git-tree-sha1 = "71857d6bab17e7ac6802d86ffcc75423b8c1d812" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.53.1" +version = "2.54.0" [deps.SciMLBase.extensions] SciMLBaseChainRulesCoreExt = "ChainRulesCore" @@ -1959,9 +1961,9 @@ version = "1.2.0" [[deps.SimpleNonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "DiffResults", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "MaybeInplace", "PrecompileTools", "Reexport", "SciMLBase", "Setfield", "StaticArraysCore"] -git-tree-sha1 = "536c0ee0b4b766ddee24220c6bb60932df4e2c39" +git-tree-sha1 = "44021f3efc023be3871195d8ad98b865001a2fa1" uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "1.12.1" +version = "1.12.3" [deps.SimpleNonlinearSolve.extensions] SimpleNonlinearSolveChainRulesCoreExt = "ChainRulesCore" @@ -2002,9 +2004,9 @@ version = "1.10.0" [[deps.SparseDiffTools]] deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Random", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "Tricks", "UnPack", "VertexSafeGraphs"] -git-tree-sha1 = "c9e5d7ee75cf6a1ca3a22c9a6a4ef451792cf62b" +git-tree-sha1 = "7db12cef226aaa8ca730040c9c35e32b86a69b83" uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "2.20.0" +version = "2.22.0" [deps.SparseDiffTools.extensions] SparseDiffToolsEnzymeExt = "Enzyme" @@ -2020,12 +2022,6 @@ version = "2.20.0" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" -[[deps.SparseMatrixColorings]] -deps = ["ADTypes", "Compat", "DataStructures", "DocStringExtensions", "LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "996dff77d814c45c3f2342fa0113e4ad31e712e8" -uuid = "0a514795-09f3-496d-8182-132a7b665d35" -version = "0.4.0" - [[deps.Sparspak]] deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] git-tree-sha1 = "342cf4b449c299d8d1ceaf00b7a49f4fbc7940e7" @@ -2122,9 +2118,9 @@ version = "7.2.1+1" [[deps.SymbolicIndexingInterface]] deps = ["Accessors", "ArrayInterface", "RuntimeGeneratedFunctions", "StaticArraysCore"] -git-tree-sha1 = "988e04b34a4c3b824fb656f542473df99a4f610d" +git-tree-sha1 = "0225f7c62f5f78db35aae6abb2e5cabe38ce578f" uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" -version = "0.3.30" +version = "0.3.31" [[deps.TOML]] deps = ["Dates"] @@ -2169,11 +2165,6 @@ version = "1.16.1" deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -[[deps.TestItems]] -git-tree-sha1 = "42fd9023fef18b9b78c8343a4e2f3813ffbcefcb" -uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" -version = "1.0.0" - [[deps.ThreadingUtilities]] deps = ["ManualMemory"] git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" @@ -2475,9 +2466,9 @@ version = "1.2.13+1" [[deps.Zstd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e678132f07ddb5bfa46857f0d7620fb9be675d3b" +git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.6+0" +version = "1.5.6+1" [[deps.eudev_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gperf_jll"] From 225a016b5651a76e64749f6eea93a7f918d85e55 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 29 Sep 2024 00:04:56 +0200 Subject: [PATCH 39/50] Remove explicit documention of fields in CellCache (#1079) --- .../incompressible_elasticity.jl | 2 +- docs/src/literate-tutorials/stokes-flow.jl | 6 +++--- src/iterators.jl | 14 ++------------ 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/docs/src/literate-tutorials/incompressible_elasticity.jl b/docs/src/literate-tutorials/incompressible_elasticity.jl index 4a13b407b8..d5c177bd7c 100644 --- a/docs/src/literate-tutorials/incompressible_elasticity.jl +++ b/docs/src/literate-tutorials/incompressible_elasticity.jl @@ -214,7 +214,7 @@ function compute_stresses(cellvalues_u::CellValues, cellvalues_p::CellValues, reinit!(cellvalues_u, cc) reinit!(cellvalues_p, cc) ## Extract the cell local part of the solution - for (i, I) in pairs(cc.dofs) + for (i, I) in pairs(celldofs(cc)) ae[i] = a[I] end ## Loop over the quadrature points diff --git a/docs/src/literate-tutorials/stokes-flow.jl b/docs/src/literate-tutorials/stokes-flow.jl index b034e00e1b..6b74a671bf 100644 --- a/docs/src/literate-tutorials/stokes-flow.jl +++ b/docs/src/literate-tutorials/stokes-flow.jl @@ -447,8 +447,8 @@ function check_mean_constraint(dh, fvp, u) #src ∫pdΓ, Γ= 0.0, 0.0 #src for (ci, fi) in set #src reinit!(cc, ci) #src - reinit!(fvp, cc.coords, fi) #src - ue = u[cc.dofs] #src + reinit!(fvp, cc, fi) #src + ue = u[celldofs(cc)] #src for qp in 1:getnquadpoints(fvp) #src dΓ = getdetJdV(fvp, qp) #src ∫pdΓ += function_value(fvp, qp, ue, range_p) * dΓ #src @@ -466,7 +466,7 @@ function check_L2(dh, cvu, cvp, u) #src for cell in CellIterator(dh) #src reinit!(cvu, cell) #src reinit!(cvp, cell) #src - ue = u[cell.dofs] #src + ue = u[celldofs(cell)] #src for qp in 1:getnquadpoints(cvu) #src dΩ = getdetJdV(cvu, qp) #src uh = function_value(cvu, qp, ue, range_u) #src diff --git a/src/iterators.jl b/src/iterators.jl index 190a1ed955..2f53dee7bc 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -22,11 +22,6 @@ Create a cache object with pre-allocated memory for the nodes, coordinates, and cell. The cache is updated for a new cell by calling `reinit!(cache, cellid)` where `cellid::Int` is the cell id. -**Struct fields of `CellCache`** - - `cc.nodes :: Vector{Int}`: global node ids - - `cc.coords :: Vector{<:Vec}`: node coordinates - - `cc.dofs :: Vector{Int}`: global dof ids (empty when constructing the cache from a grid) - **Methods with `CellCache`** - `reinit!(cc, i)`: reinitialize the cache for cell `i` - `cellid(cc)`: get the cell id of the currently cached cell @@ -105,10 +100,6 @@ celldofs!(v::Vector, cc::CellCache) = copyto!(v, cc.dofs) # celldofs!(v, cc.dh, nfacets(cc::CellCache) = nfacets(getcells(cc.grid, cc.cellid)) -# TODO: Currently excluded from the docstring below. Should they be public? -# - `Ferrite.faceindex(fc)`: get the `FaceIndex` of the currently cached face -# - `Ferrite.faceid(fc)`: get the current faceid (`faceindex(fc)[2]`) - """ FacetCache(grid::Grid) FacetCache(dh::AbstractDofHandler) @@ -119,7 +110,7 @@ calling `reinit!(cache, fi::FacetIndex)`. **Methods with `fc::FacetCache`** - `reinit!(fc, fi)`: reinitialize the cache for face `fi::FacetIndex` - - `cellid(fc)`: get the current cellid (`faceindex(fc)[1]`) + - `cellid(fc)`: get the current cellid - `getnodes(fc)`: get the global node ids of the *cell* - `getcoordinates(fc)`: get the coordinates of the *cell* - `celldofs(fc)`: get the global dof ids of the *cell* @@ -152,9 +143,8 @@ for op = (:getnodes, :getcoordinates, :cellid, :celldofs) end end end -# @inline faceid(fc::FacetCache) = fc.current_faceid[] + @inline celldofs!(v::Vector, fc::FacetCache) = celldofs!(v, fc.cc) -# @inline faceindex(fc::FacetCache) = FaceIndex(cellid(fc), faceid(fc)) @inline function reinit!(fv::FacetValues, fc::FacetCache) reinit!(fv, fc.cc, fc.current_facet_id) end From de2d66ba6ffeac36c0b9530dbb0038aebf2ed6a3 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 29 Sep 2024 00:26:39 +0200 Subject: [PATCH 40/50] Add changelog and deprecations for COOAssembler (#1080) --- CHANGELOG.md | 4 ++++ src/deprecations.jl | 6 ++++++ test/test_deprecations.jl | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46862f6fc3..15ecac9d22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -456,6 +456,8 @@ more discussion). - The function `reshape_to_nodes` have been deprecated in favor of `evaluate_at_grid_nodes`. ([#703]) +- `start_assemble([n::Int])` has been deprecated in favor of calling `Ferrite.COOAssembler()` directly ([#916], [#1058]). + - `start_assemble(f, K)` have been deprecated in favor of the "canonical" `start_assemble(K, f)`. ([#707]) @@ -1000,9 +1002,11 @@ poking into Ferrite internals: [#880]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/880 [#888]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/888 [#914]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/914 +[#916]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/916 [#924]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/924 [#943]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/943 [#949]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/949 [#953]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/953 [#974]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/974 +[#1058]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1058 [#1059]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1059 diff --git a/src/deprecations.jl b/src/deprecations.jl index 37f03ee16e..dd819f4445 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -443,3 +443,9 @@ end function assemble!(::AbstractAssembler, ::AbstractVector{<:Integer}, ::AbstractVector, ::AbstractMatrix) throw(DeprecationError("assemble!(assembler, dofs, fe, Ke)" => "assemble!(assembler, dofs, Ke, fe)")) end + +start_assemble(::Int) = throw(DeprecationError("start_assemble(n::Int)" => "Ferrite.COOAssembler(; sizehint = n)")) +start_assemble() = throw(DeprecationError("start_assemble()" => "Ferrite.COOAssembler()")) + +export getfaceset +getfaceset(args...) = throw(DeprecationError("getfaceset(...)" => "getfacetset(...)")) diff --git a/test/test_deprecations.jl b/test/test_deprecations.jl index 7be2cb94db..226fdc15ad 100644 --- a/test/test_deprecations.jl +++ b/test/test_deprecations.jl @@ -124,4 +124,9 @@ end @test_throws Ferrite.DeprecationError Ferrite.default_interpolation(Triangle) end +@testset "start_assemble" begin + @test_throws Ferrite.DeprecationError start_assemble() + @test_throws Ferrite.DeprecationError start_assemble(10) +end + end # testset deprecations From a9c9b3c085c45dfaae868a19a19894eec3ef24ff Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 30 Sep 2024 10:25:33 +0200 Subject: [PATCH 41/50] Deprecate `celldofs!(::Vector, ::(Cell|Facet)Cache)` (#1082) --- docs/src/literate-gallery/helmholtz.jl | 4 +--- src/deprecations.jl | 7 +++++++ src/iterators.jl | 8 ++------ test/test_deprecations.jl | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/docs/src/literate-gallery/helmholtz.jl b/docs/src/literate-gallery/helmholtz.jl index a08ce605a3..1bd8ea47c2 100644 --- a/docs/src/literate-gallery/helmholtz.jl +++ b/docs/src/literate-gallery/helmholtz.jl @@ -94,7 +94,6 @@ function doassemble(cellvalues::CellValues, facetvalues::FacetValues, assembler = start_assemble(K, f) n_basefuncs = getnbasefunctions(cellvalues) - global_dofs = zeros(Int, ndofs_per_cell(dh)) fe = zeros(n_basefuncs) # Local force vector Ke = zeros(n_basefuncs, n_basefuncs) # Local stiffness mastrix @@ -151,8 +150,7 @@ function doassemble(cellvalues::CellValues, facetvalues::FacetValues, end end - celldofs!(global_dofs, cell) - assemble!(assembler, global_dofs, Ke, fe) + assemble!(assembler, celldofs(cell), Ke, fe) end return K, f end; diff --git a/src/deprecations.jl b/src/deprecations.jl index dd819f4445..8043a837d4 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -449,3 +449,10 @@ start_assemble() = throw(DeprecationError("start_assemble()" => "Ferrite.COOAsse export getfaceset getfaceset(args...) = throw(DeprecationError("getfaceset(...)" => "getfacetset(...)")) + +function celldofs!(::Vector, ::CellCache) + throw(DeprecationError("celldofs!(v::Vector, cc::CellCache)" => "celldofs!(v, celldofs(cc))")) +end +function celldofs!(::Vector, ::FacetCache) + throw(DeprecationError("celldofs!(v::Vector, fs::FacetCache)" => "celldofs!(v, celldofs(fc))")) +end diff --git a/src/iterators.jl b/src/iterators.jl index 2f53dee7bc..b97888ae4f 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -85,17 +85,14 @@ end # reinit! FEValues with CellCache reinit!(cv::CellValues, cc::CellCache) = reinit!(cv, cc.coords) -reinit!(fv::FacetValues, cc::CellCache, f::Int) = reinit!(fv, cc.coords, f) # TODO: Deprecate? +reinit!(fv::FacetValues, cc::CellCache, f::Int) = reinit!(fv, cc.coords, f) -# Accessor functions (TODO: Deprecate? We are so inconsistent with `getxx` vs `xx`...) +# Accessor functions getnodes(cc::CellCache) = cc.nodes getcoordinates(cc::CellCache) = cc.coords celldofs(cc::CellCache) = cc.dofs cellid(cc::CellCache) = cc.cellid -# TODO: This can definitely be deprecated -celldofs!(v::Vector, cc::CellCache) = copyto!(v, cc.dofs) # celldofs!(v, cc.dh, cc.cellid[]) - # TODO: These should really be replaced with something better... nfacets(cc::CellCache) = nfacets(getcells(cc.grid, cc.cellid)) @@ -144,7 +141,6 @@ for op = (:getnodes, :getcoordinates, :cellid, :celldofs) end end -@inline celldofs!(v::Vector, fc::FacetCache) = celldofs!(v, fc.cc) @inline function reinit!(fv::FacetValues, fc::FacetCache) reinit!(fv, fc.cc, fc.current_facet_id) end diff --git a/test/test_deprecations.jl b/test/test_deprecations.jl index 226fdc15ad..fe461748df 100644 --- a/test/test_deprecations.jl +++ b/test/test_deprecations.jl @@ -129,4 +129,19 @@ end @test_throws Ferrite.DeprecationError start_assemble(10) end +@testset "celldofs!(::Vector, ::Cache)" begin + grid = generate_grid(Quadrilateral, (1, 1)) + dh = DofHandler(grid) + ip = Lagrange{RefQuadrilateral, 1}() + add!(dh, :u, ip) + close!(dh) + cc = CellCache(dh) + reinit!(cc, 1) + v = Int[] + @test_throws Ferrite.DeprecationError celldofs!(v, cc) + fc = FacetCache(dh) + reinit!(fc, FacetIndex(1, 1)) + @test_throws Ferrite.DeprecationError celldofs!(v, fc) +end + end # testset deprecations From fced455e9ea6e3fc91ac2995380da9bdf04e73bc Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 30 Sep 2024 16:47:49 +0200 Subject: [PATCH 42/50] Update changelog for 1.0.0 (#1077) --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15ecac9d22..12325b97cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ 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). -## [v1.0.0] - Work in progress, release date TBD +## [v1.0.0] - 2024-09-30 Ferrite version 1.0 is a relatively large release, with a lot of new features, improvements, deprecations and some removals. These changes are made to make the code base more consistent @@ -19,6 +19,11 @@ also a dedicated section about upgrading code from Ferrite 0.3 to 1.0 (see below include the most common changes that are required. In addition, in all cases where possible, you will be presented with a descriptive error message telling you what needs to change. +Deprecations for 1.0 will be removed during the 1.x release series. When upgrading old code +it is therefore recommended to use Ferrite 1.0 as a first stepping stone since this release +contain descriptive deprecation error messages that might not exist in e.g. Ferrite version +1.2. + ### Upgrading code from Ferrite 0.3 to 1.0 This section give a short overview of the most common required changes. More details and From 053e13cede0c98cfa8cf283f4cefd069d0c8f903 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 30 Sep 2024 16:56:12 +0200 Subject: [PATCH 43/50] Add [compat] for ForwardDiff and StaticArrays (#1085) --- Project.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Project.toml b/Project.toml index 52bb20f614..375d919519 100644 --- a/Project.toml +++ b/Project.toml @@ -26,11 +26,13 @@ FerriteMetis = "Metis" [compat] BlockArrays = "0.16, 1" EnumX = "1" +ForwardDiff = "0.10" Metis = "1.3" NearestNeighbors = "0.4" OrderedCollections = "1" Preferences = "1" Reexport = "1" +StaticArrays = "1" Tensors = "1.14" WriteVTK = "1.13" julia = "1.9" From 971c4dcded303647075fc470193a57b894e9fb34 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Mon, 30 Sep 2024 17:28:15 +0200 Subject: [PATCH 44/50] Use sparse! and spzeros! in more places (#1084) --- ext/FerriteMetis.jl | 6 ++---- src/Dofs/ConstraintHandler.jl | 2 +- src/Ferrite.jl | 3 +-- src/arrayutils.jl | 19 +++++++++---------- src/assembler.jl | 2 +- 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/ext/FerriteMetis.jl b/ext/FerriteMetis.jl index 71f064d639..9657ff306d 100644 --- a/ext/FerriteMetis.jl +++ b/ext/FerriteMetis.jl @@ -6,10 +6,9 @@ if VERSION >= v"1.10.0-DEV.90" using Ferrite: Ferrite, CellIterator, ConstraintHandler, DofHandler, DofOrder, celldofs, ndofs, - ndofs_per_cell + ndofs_per_cell, spzeros!! using Metis.LibMetis: idx_t using Metis: Metis -using SparseArrays: sparse struct MetisOrder <: DofOrder.Ext{Metis} coupling::Union{Matrix{Bool},Nothing} @@ -75,8 +74,7 @@ function Ferrite.compute_renumber_permutation( end @assert length(I) == length(J) == idx N = ndofs(dh) - # TODO: Use spzeros! in Julia 1.10. - S = sparse(I, J, zeros(Float32, length(I)), N, N) + S = spzeros!!(Float32, I, J, N, N) # Add entries from affine constraints if ch !== nothing diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index 8684e163d3..575a3d6760 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -767,7 +767,7 @@ function create_constraint_matrix(ch::ConstraintHandler{dh,T}) where {dh,T} end g[ch.prescribed_dofs] .= ch.inhomogeneities - C = sparse(I, J, V, ndofs(ch.dh), length(ch.free_dofs)) + C = sparse!!(I, J, V, ndofs(ch.dh), length(ch.free_dofs)) return C, g end diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 2738904d83..c83079ef3a 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -14,8 +14,7 @@ using NearestNeighbors: using OrderedCollections: OrderedSet using SparseArrays: - SparseArrays, SparseMatrixCSC, nonzeros, nzrange, rowvals, sparse, - AbstractSparseMatrixCSC + SparseArrays, SparseMatrixCSC, nonzeros, nzrange, rowvals, AbstractSparseMatrixCSC using StaticArrays: StaticArrays, MArray, MMatrix, SArray, SMatrix, SVector using WriteVTK: diff --git a/src/arrayutils.jl b/src/arrayutils.jl index 2566ee338d..757ec38f8c 100644 --- a/src/arrayutils.jl +++ b/src/arrayutils.jl @@ -87,15 +87,14 @@ function fillzero!(A::Symmetric{T,<:SparseMatrixCSC}) where T end # Compat and convenience layer around SparseArrays.spzeros! (SparseArrays.jl#284, SparseArrays.jl#315) -function spzeros!!(::Type{Tv}, I::Vector{Ti}, J::Vector{Ti}, m::Int, n::Int) where {Tv, Ti <: Integer} - @assert length(I) == length(J) - @static if isdefined(SparseArrays, :spzeros!) - klasttouch = Vector{Ti}(undef, n) - csrrowptr = Vector{Ti}(undef, m + 1) - csrcolval = Vector{Ti}(undef, length(I)) - S = SparseArrays.spzeros!(Tv, I, J, m, n, klasttouch, csrrowptr, csrcolval, I, J) - else - S = sparse(I, J, zeros(Tv, length(I)), m, n) +if VERSION >= v"1.10.0" + const sparse!! = SparseArrays.sparse! + const spzeros!! = SparseArrays.spzeros! +else + function sparse!!(I::AbstractVector, J::AbstractVector, V::AbstractVector, m::Integer, n::Integer) + return SparseArrays.sparse(I, J, V, m, n) + end + function spzeros!!(::Type{T}, I::AbstractVector, J::AbstractVector, m::Integer, n::Integer) where T + return sparse!!(I, J, zeros(T, length(I)), m, n) end - return S end diff --git a/src/assembler.jl b/src/assembler.jl index 6c0ffc5203..a935f0a59a 100644 --- a/src/assembler.jl +++ b/src/assembler.jl @@ -128,7 +128,7 @@ function finish_assemble(a::COOAssembler) # Create the matrix nrows = a.nrows == -1 ? maximum(a.I) : a.nrows ncols = a.ncols == -1 ? maximum(a.J) : a.ncols - K = sparse(a.I, a.J, a.V, nrows, ncols) + K = sparse!!(a.I, a.J, a.V, nrows, ncols) # Finalize the vector f = a.f if !isempty(f) From ffab3f0c64707d0dd5439ab1dc94c3b0927a76b2 Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Tue, 1 Oct 2024 00:39:59 +0200 Subject: [PATCH 45/50] Reverse CI for FerriteGmsh and FerriteMeshParser (#797) --- .github/workflows/Downstream.yml | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/Downstream.yml diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml new file mode 100644 index 0000000000..f854b3fecc --- /dev/null +++ b/.github/workflows/Downstream.yml @@ -0,0 +1,59 @@ +# Based on https://github.com/JuliaDiff/ChainRulesCore.jl/blob/main/.github/workflows/IntegrationTest.yml +name: Downstream + +on: + push: + branches: ['master'] + tags: ['*'] + pull_request: + +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + test: + name: ${{ matrix.package.repo }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + julia-version: ['1'] + os: ['ubuntu-latest'] + package: + # - {user: 'Ferrite-FEM', repo: 'FerriteDistributed.jl'} # Requires more efforts to be updated to 1.0 + - {user: 'Ferrite-FEM', repo: 'FerriteGmsh.jl'} + - {user: 'Ferrite-FEM', repo: 'FerriteMeshParser.jl'} + # - {user: 'Ferrite-FEM', repo: 'FerriteViz.jl'} # Requires release + steps: + - uses: actions/checkout@v4 + - uses: julia-actions/setup-julia@v2 + with: + version: ${{ matrix.julia-version }} + - uses: julia-actions/cache@v2 + # - uses: julia-actions/julia-buildpkg@v1 + - name: Clone Downstream + uses: actions/checkout@v4 + with: + repository: ${{ matrix.package.user }}/${{ matrix.package.repo }} + path: downstream + - name: Load this and run the downstream tests + shell: julia --project=downstream {0} + run: | + using Pkg + try + # force it to use this PR's version of the package + Pkg.develop(PackageSpec(path=".")) # resolver may fail with main deps + Pkg.update() + Pkg.test() # resolver may fail with test time deps + catch err + rethrow() # Currently all packages tested are under Ferrite-FEM control so don't expect breakage + err isa Pkg.Resolve.ResolverError || rethrow() + # If we can't resolve that means this is incompatible by SemVer and this is fine + # It means we marked this as a breaking change, so we don't need to worry about + # Mistakenly introducing a breaking change, as we have intentionally made one + @info "Not compatible with this release. No problem." exception=err + exit(0) # Exit immediately, as a success + end From a432eaec150a75dea481ce53b66843871e810c73 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Tue, 1 Oct 2024 10:40:31 +0200 Subject: [PATCH 46/50] Remove the unused and deprecated third parameter for interpolations (#1083) --- CHANGELOG.md | 12 ++++++++++++ src/deprecations.jl | 39 --------------------------------------- src/interpolations.jl | 28 ++++++++++++++-------------- test/test_deprecations.jl | 17 ----------------- 4 files changed, 26 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12325b97cf..a2be9f3a67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ 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). +## [Unreleased] + +### Removed + - The deprecated third type parameter for interpolations have been removed. Old code which + tries to use three parameters will now throw the somewhat cryptic error: + ``` + julia> Lagrange{2, RefCube, 1}() + ERROR: too many parameters for type + ``` + ([#1083]) + ## [v1.0.0] - 2024-09-30 Ferrite version 1.0 is a relatively large release, with a lot of new features, improvements, @@ -1015,3 +1026,4 @@ poking into Ferrite internals: [#974]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/974 [#1058]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1058 [#1059]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1059 +[#1083]: https://github.com/Ferrite-FEM/Ferrite.jl/issues/1083 diff --git a/src/deprecations.jl b/src/deprecations.jl index 8043a837d4..207fb938fc 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -231,31 +231,6 @@ end struct RefCube end export RefCube -function Lagrange{D, RefCube, O}() where {D, O} - shape = D == 1 ? RefLine : D == 2 ? RefQuadrilateral : RefHexahedron - throw(DeprecationError("Lagrange{$D, RefCube, $O}()" => "Lagrange{$(shape), $O}()")) -end -function Lagrange{2, RefTetrahedron, O}() where {O} - throw(DeprecationError("Lagrange{2, RefTetrahedron, $O}()" => "Lagrange{RefTriangle, $O}()")) -end -function DiscontinuousLagrange{D, RefCube, O}() where {D, O} - shape = D == 1 ? RefLine : D == 2 ? RefQuadrilateral : RefHexahedron - throw(DeprecationError("DiscontinuousLagrange{$D, RefCube, $O}()" => "DiscontinuousLagrange{$(shape), $O}()")) -end -function BubbleEnrichedLagrange{2, RefTetrahedron, O}() where {O} - throw(DeprecationError("BubbleEnrichedLagrange{2, RefTetrahedron, $O}()" => "BubbleEnrichedLagrange{RefTriangle, $O}()")) -end -function DiscontinuousLagrange{2, RefTetrahedron, O}() where {O} - throw(DeprecationError("DiscontinuousLagrange{2, RefTetrahedron, $O}()" => "DiscontinuousLagrange{RefTriangle, $O}()")) -end -function Serendipity{D, RefCube, O}() where {D, O} - shape = D == 1 ? RefLine : D == 2 ? RefQuadrilateral : RefHexahedron - throw(DeprecationError("Serendipity{$D, RefCube, $O}()" => "Serendipity{$(shape), $O}()")) -end -function CrouzeixRaviart{2, 1}() - throw(DeprecationError("CrouzeixRaviart{2, 1}()" => "CrouzeixRaviart{RefTriangle, 1}()")) -end - # For the quadrature: Some will be wrong for face integration, so then we warn # in the FaceValue constructor... @@ -359,20 +334,6 @@ function FacetValues( throw(DeprecationError(msg)) end -# Hide the last unused type param... -function Base.show(io::IO, ::DiscontinuousLagrange{shape, order}) where {shape, order} - print(io, "DiscontinuousLagrange{$(shape), $(order)}()") -end -function Base.show(io::IO, ::Lagrange{shape, order}) where {shape, order} - print(io, "Lagrange{$(shape), $(order)}()") -end -function Base.show(io::IO, ::Serendipity{shape, order}) where {shape, order} - print(io, "Serendipity{$(shape), $(order)}()") -end -function Base.show(io::IO, ::CrouzeixRaviart{shape, order}) where {shape, order} - print(io, "CrouzeixRaviart{$(shape), $(order)}()") -end - function value(ip::Interpolation, ξ::Vec) throw(DeprecationError("value(ip::Interpolation, ξ::Vec)" => "[reference_shape_value(ip, ξ, i) for i in 1:getnbasefunctions(ip)]")) end diff --git a/src/interpolations.jl b/src/interpolations.jl index 8ce950b1ee..c78621c90e 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -44,12 +44,12 @@ julia> getnbasefunctions(ip) 6 ``` """ -abstract type Interpolation{shape #=<: AbstractRefShape=#, order, unused} end +abstract type Interpolation{shape #=<: AbstractRefShape=#, order} end const InterpolationByDim{dim} = Interpolation{<:AbstractRefShape{dim}} -abstract type ScalarInterpolation{ refshape, order} <: Interpolation{refshape, order, Nothing} end -abstract type VectorInterpolation{vdim, refshape, order} <: Interpolation{refshape, order, Nothing} end +abstract type ScalarInterpolation{ refshape, order} <: Interpolation{refshape, order} end +abstract type VectorInterpolation{vdim, refshape, order} <: Interpolation{refshape, order} end # Number of components for the interpolation. n_components(::ScalarInterpolation) = 1 @@ -429,9 +429,9 @@ dirichlet_boundarydof_indices(::Type{FacetIndex}) = dirichlet_facetdof_indices """ Piecewise discontinuous Lagrange basis via Gauss-Lobatto points. """ -struct DiscontinuousLagrange{shape, order, unused} <: ScalarInterpolation{shape, order} +struct DiscontinuousLagrange{shape, order} <: ScalarInterpolation{shape, order} function DiscontinuousLagrange{shape, order}() where {shape <: AbstractRefShape, order} - new{shape, order, Nothing}() + new{shape, order}() end end @@ -486,9 +486,9 @@ is_discontinuous(::Type{<:DiscontinuousLagrange}) = true Standard continuous Lagrange polynomials with equidistant node placement. """ -struct Lagrange{shape, order, unused} <: ScalarInterpolation{shape, order} +struct Lagrange{shape, order} <: ScalarInterpolation{shape, order} function Lagrange{shape, order}() where {shape <: AbstractRefShape, order} - new{shape, order, Nothing}() + new{shape, order}() end end @@ -1269,9 +1269,9 @@ end """ Lagrange element with bubble stabilization. """ -struct BubbleEnrichedLagrange{shape, order, unused} <: ScalarInterpolation{shape, order} +struct BubbleEnrichedLagrange{shape, order} <: ScalarInterpolation{shape, order} function BubbleEnrichedLagrange{shape, order}() where {shape <: AbstractRefShape, order} - new{shape, order, Nothing}() + new{shape, order}() end end @@ -1312,9 +1312,9 @@ end Serendipity element on hypercubes. Currently only second order variants are implemented. """ -struct Serendipity{shape, order, unused} <: ScalarInterpolation{shape,order} +struct Serendipity{shape, order} <: ScalarInterpolation{shape,order} function Serendipity{shape, order}() where {shape <: AbstractRefShape, order} - new{shape, order, Nothing}() + new{shape, order}() end end @@ -1459,9 +1459,9 @@ Classical non-conforming Crouzeix–Raviart element. For details we refer to the original paper [CroRav:1973:cnf](@cite). """ -struct CrouzeixRaviart{shape, order, unused} <: ScalarInterpolation{shape, order} - CrouzeixRaviart{RefTriangle, 1}() = new{RefTriangle, 1, Nothing}() - CrouzeixRaviart{RefTetrahedron, 1}() = new{RefTetrahedron, 1, Nothing}() +struct CrouzeixRaviart{shape, order} <: ScalarInterpolation{shape, order} + CrouzeixRaviart{RefTriangle, 1}() = new{RefTriangle, 1}() + CrouzeixRaviart{RefTetrahedron, 1}() = new{RefTetrahedron, 1}() end # CR elements are characterized by not having vertex dofs diff --git a/test/test_deprecations.jl b/test/test_deprecations.jl index fe461748df..1d9bb65add 100644 --- a/test/test_deprecations.jl +++ b/test/test_deprecations.jl @@ -38,23 +38,6 @@ end end @testset "Deprecation of old RefShapes" begin - # Interpolations - for order in 1:2 - @test_throws Ferrite.DeprecationError Lagrange{1, RefCube, order}() - end - for order in 1:5 - @test_throws Ferrite.DeprecationError Lagrange{2, RefTetrahedron, order}() - end - for order in 1:2 - @test_throws Ferrite.DeprecationError Lagrange{2, RefCube, order}() - end - for order in 1:2 - @test_throws Ferrite.DeprecationError Lagrange{3, RefCube, order}() - end - @test_throws Ferrite.DeprecationError Serendipity{2, RefCube, 2}() - @test_throws Ferrite.DeprecationError Serendipity{3, RefCube, 2}() - @test_throws Ferrite.DeprecationError CrouzeixRaviart{2, 1}() - @test_throws Ferrite.DeprecationError BubbleEnrichedLagrange{2, RefTetrahedron, 1}() # Quadrature/(Cell|Face)Value combinations (sometimes warns in the QR constructor, sometimes it the FEValues constructor) function test_combo(constructor, qdim, qshape, qargs, ip) qr = QuadratureRule{qdim, qshape}(qargs...) From 431e6cfc8570461320de27ad1a9d98af3fc76197 Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Tue, 1 Oct 2024 10:56:42 +0200 Subject: [PATCH 47/50] Less noisy PointEvalHandler (#1086) --- src/PointEvalHandler.jl | 4 +++- test/test_pointevaluation.jl | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/PointEvalHandler.jl b/src/PointEvalHandler.jl index 97d57ec150..5388528a33 100644 --- a/src/PointEvalHandler.jl +++ b/src/PointEvalHandler.jl @@ -140,7 +140,9 @@ function find_local_coordinate(interpolation::Interpolation{refshape}, cell_coor end break end - if calculate_detJ(J) ≤ 0.0 + # Report if the element is geometrically broken at the converged point + if converged && calculate_detJ(J) ≤ 0.0 + converged = false warn && @warn "det(J) negative! Aborting! $(calculate_detJ(J))" break end diff --git a/test/test_pointevaluation.jl b/test/test_pointevaluation.jl index 347290a16c..2309db4666 100644 --- a/test/test_pointevaluation.jl +++ b/test/test_pointevaluation.jl @@ -32,6 +32,7 @@ function test_pe_scalar_field() projector_vals = project(projector, qp_vals, qr) # set up PointEvalHandler and retrieve values + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) @@ -76,6 +77,7 @@ function test_pe_embedded() projector_vals = project(projector, qp_vals, qr) # set up PointEvalHandler and retrieve values + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) @@ -115,6 +117,7 @@ function test_pe_vector_field() points = [Vec((x, 0.52)) for x in range(0.0; stop=1.0, length=100)] # set up PointEvalHandler and retrieve values + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_vals) @@ -152,6 +155,7 @@ function test_pe_superparametric() points = [Vec((x, 0.52)) for x in range(0.0; stop=1.0, length=100)] # set up PointEvalHandler and retrieve values + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_vals) @@ -170,6 +174,7 @@ function test_pe_dofhandler() add!(dh, :s, Lagrange{RefQuadrilateral,1}()) # a scalar field close!(dh) + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, dh, dof_vals, :s) @@ -254,6 +259,7 @@ function test_pe_dofhandler2(;three_dimensional=true) v_dofs = dof_range(dh, :v) uh = _pointeval_dofhandler2_manual_projection(dh, csv, cvv, f_s, f_v) + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) psv = PointValues(ip_f) @@ -333,6 +339,7 @@ function test_pe_mixed_grid() # first alternative: L2Projection to dofs projector_values = project(projector, qp_vals_quads, qr) + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_values) @@ -349,6 +356,7 @@ function test_pe_mixed_grid() dof_vals = [1., 1., 2., 2., 4., 4., 3., 3., 6., 6., 5., 5.] points = [node.x for node in mesh.nodes] + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, dh, dof_vals, :v) @@ -384,6 +392,7 @@ function test_pe_oneD() points = [Vec((x,)) for x in range(-1.0; stop=1.0, length=5)] # set up PointEvalHandler and retrieve values + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points) ph = PointEvalHandler(mesh, points) @test all(x -> x !== nothing, ph.cells) vals = evaluate_at_points(ph, projector, projector_values) @@ -398,6 +407,7 @@ end function test_pe_first_point_missing() mesh = generate_grid(Quadrilateral, (1, 1)) points = [Vec(2.0, 0.0), Vec(0.0, 0.0)] + @test_logs min_level=Logging.Warn PointEvalHandler(mesh, points; warn=false) ph = PointEvalHandler(mesh, points; warn=false) @test isnothing(ph.local_coords[1]) From d8ef711f731328c2fbfd5ebfcd0cdd3c7f7d926e Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 2 Oct 2024 17:07:52 +0200 Subject: [PATCH 48/50] Add Zenodo metadata to CITATION.cff and add DOI badge to README.md (#1087) --- CITATION.cff | 28 +++++++++++++++++----------- README.md | 1 + 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 6bf06447d0..18e2b73b26 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,15 +1,21 @@ +--- cff-version: 1.2.0 -title: "Ferrite.jl" -message: "If you use Ferrite.jl, please cite it using the metadata from this file." +title: Ferrite.jl +message: If you use Ferrite.jl, please cite it using the metadata from this file. type: software authors: -- family-names: "Carlsson" - given-names: "Kristoffer" - orcid: "https://orcid.org/0000-0001-9092-3092" -- family-names: "Ekre" - given-names: "Fredrik" - orcid: "https://orcid.org/0000-0003-2476-5406" -- name: "Ferrite.jl contributors" -repository-code: 'https://github.com/Ferrite-FEM/Ferrite.jl' -url: 'https://github.com/Ferrite-FEM/Ferrite.jl' + - family-names: Carlsson + given-names: Kristoffer + orcid: https://orcid.org/0000-0001-9092-3092 + - family-names: Ekre + given-names: Fredrik + orcid: https://orcid.org/0000-0003-2476-5406 + - name: Ferrite.jl contributors + website: https://github.com/Ferrite-FEM/Ferrite.jl/graphs/contributors +identifiers: + - type: doi + value: 10.5281/zenodo.13862652 + description: DOI representing all versions of Ferrite.jl (Zenodo Concept DOI) +repository-code: https://github.com/Ferrite-FEM/Ferrite.jl +url: https://github.com/Ferrite-FEM/Ferrite.jl license: MIT diff --git a/README.md b/README.md index 9416f709c7..9f98a4534d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ ![Build Status](https://github.com/Ferrite-FEM/Ferrite.jl/workflows/CI/badge.svg?event=push) [![codecov.io](https://codecov.io/github/Ferrite-FEM/Ferrite.jl/coverage.svg?branch=master)](https://codecov.io/github/Ferrite-FEM/Ferrite.jl?branch=master) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.13862652.svg)](https://doi.org/10.5281/zenodo.13862652) A finite element toolbox written in Julia. From bee2384f8ba503499a65423dc355754e80d1c654 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Wed, 2 Oct 2024 17:10:18 +0200 Subject: [PATCH 49/50] Add a doc section comparing different assembly strategies (#1063) --- docs/src/topics/assembly.md | 301 +++++++++++++++++++++++++++++++++++- 1 file changed, 300 insertions(+), 1 deletion(-) diff --git a/docs/src/topics/assembly.md b/docs/src/topics/assembly.md index 868761af8e..ae3b5ab0a0 100644 --- a/docs/src/topics/assembly.md +++ b/docs/src/topics/assembly.md @@ -19,7 +19,8 @@ f[celldofs] += fe where `celldofs` is the vector containing the degrees of freedom for the cell. The method above is very inefficient -- it is especially costly to index -into the sparse matrix `K` directly. Therefore we will instead use an +into the sparse matrix `K` directly (see [Comparison of assembly strategies](@ref) +for details). Therefore we will instead use an `Assembler` that will help with the assembling of both the global stiffness matrix and the global force vector. It is also often convenient to create the sparse matrix just once, and reuse the allocated matrix. This is useful for @@ -83,3 +84,301 @@ for t in 1:timesteps # ... end ``` + +## Comparison of assembly strategies + +As discussed above there are various ways to assemble the local matrix into the global one. +In particular, it was mentioned that naive indexing is very inefficient and that using an +assembler is faster. To put some concrete numbers to these statements we will compare some +strategies in this section. First we compare just a single assembly operation (e.g. +assembling an already computed local matrix) and then to relate this to a more realistic +scenario we compare the full matrix assembly including the integration of all the elements. + +!!! note "Pre-allocated global matrix" + All strategies that we compare below uses a pre-allocated global matrix `K` with the + correct sparsity pattern. Starting with something like + `K = spzeros(ndofs(dh), ndofs(dh))` and then inserting entries is excruciatingly slow + due to the sparse data structure so this method is not even considered. + +For the comparison we need a representative global matrix to assemble into. In the following +setup code we create a grid with triangles and a DofHandler with a quadratic scalar field. +From this we instantiate the global matrix. + +```@example assembly-perf +using Ferrite + +# Quadratic scalar interpolation +ip = Lagrange{RefTriangle, 2}() + +# DofHandler +const N = 100 +grid = generate_grid(Triangle, (N, N)) +const dh = DofHandler(grid) +add!(dh, :u, ip) +close!(dh) + +# Global matrix and a corresponding assembler +const K = allocate_matrix(dh) +nothing # hide +``` + +#### Strategy 1: matrix indexing + +The first strategy is to index directly, using the vector of global dofs, into the global +matrix: + +```@example assembly-perf +function assemble_v1(_, K, dofs, Ke) + K[dofs, dofs] += Ke + return +end +nothing # hide +``` + +This looks very simple, but it is very inefficient (as the numbers will show later). To +understand why the operation `K[dofs, dofs] += Ke` (with `K` being a sparse matrix) is so +slow we can dig into the details. + +In Julia there is no "`+=`"-operation and so `x += y` is identical to `x = x + y`. +Translating this to our example we have + +```julia +K[dofs, dofs] = K[dofs, dofs] + Ke +``` + +We can break down this a bit further into these equivalent three steps: + +```julia +tmp1 = K[dofs, dofs] # 1 +tmp2 = tmp1 + Ke # 2 +K[dofs, dofs] = tmp2 # 3 +``` + +Now the problem with this strategy becomes a bit more obvious: + - In line 1 there is first an allocation of a new matrix (`tmp1`) followed by indexing into + `K` to copy elements from `K` to `tmp1`. Both of these operations are rather costly: + allocations should always be minimized in tight loops, and indexing into a sparse matrix + is non-trivial due to the data structure. In addition, since the `dofs` vector contains + the global indices (which are neither sorted nor consecutive) we have a random access + pattern. + - In line 2 there is another allocation of a matrix (`tmp2`) for the result of the addition + of `tmp1` and `Ke`. + - In line 3 we again need to index into the sparse matrix to copy over the elements from + `tmp2` to `K`. This essentially duplicates the indexing effort from line 1 since we need + to lookup the same locations in `K` again. + +!!! note "Broadcasting" + Using [broadcasting](https://docs.julialang.org/en/v1/manual/arrays/#Broadcasting), e.g. + `K[dofs, dofs] .+= Ke` is an alternative to the above, and resembles a `+=`-operation. + In theory this should be as efficient as the explicit loop presented in the next + section. + +#### Strategy 2: scalar indexing + +A variant of the first strategy is to explicitly loop over the indices and add the elements +individually as scalars: + +```@example assembly-perf +function assemble_v2(_, K, dofs, Ke) + for (i, I) in pairs(dofs) + for (j, J) in pairs(dofs) + K[I, J] += Ke[i, j] + end + end + return +end +nothing # hide +``` + +The core operation, `K[I, J] += Ke[i, j]`, can still be broken down into three equivalent +steps: + +```julia +tmp1 = K[I, J] +tmp2 = tmp1 + Ke[i, j] +K[I, J] = tmp2 +``` + +The key difference here is that we index using integers (`I`, `J`, `i`, and `j`) which means +that `tmp1` and `tmp2` are scalars which don't need to be allocated on the heap. This +stragety thus eliminates all allocations that were present in the first strategy. However, +we still lookup the same location in `K` twice, and we still have a random access pattern. + +#### Strategy 3: scalar indexing with single lookup + +To improve on the second strategy we will get rid of the double lookup into the sparse +matrix `K`. While Julia doesn't have a "`+=`"-operation, Ferrite has an internal `addindex!`-function which does exactly what we want: it adds a value to a specific location in a sparse +matrix using a single lookup. + +```@example assembly-perf +function assemble_v3(_, K, dofs, Ke) + for (i, I) in pairs(dofs) + for (j, J) in pairs(dofs) + Ferrite.addindex!(K, Ke[i, j], I, J) + end + end + return +end +nothing # hide +``` + +With this method we remove the double lookup, but the issue of random access patterns still +remains. + +#### Strategy 4: using an assembler + +Finally, the last strategy we consider uses an assembler. The assembler is a specific +datastructure that pre-allocates some workspace to make the assembly more efficient: + +```@example assembly-perf +function assemble_v4(assembler, _, dofs, Ke) + assemble!(assembler, dofs, Ke) + return +end +nothing # hide +``` + +The extra workspace inside the assembler is used to sort the dofs when `assemble!` is +called. After sorting it is possible to loop over the sparse matrix data structure and +insert all elements of `Ke` in one go instead of having to lookup locations randomly. + +### Single element assembly + +First we will compare the four functions above for a single assembly operation, i.e. +inserting one local matrix into the global matrix. For this we simply create a random local +matrix since we are not conserned with the actual values. We also pick the "middle" element +and extract the dofs for that element. Finally, an assembler is created with +`start_assemble` to use with the fourth strategy. + +```@example assembly-perf +dofs_per_cell = ndofs_per_cell(dh) +const Ke = rand(dofs_per_cell, dofs_per_cell) +const dofs = celldofs(dh, N * N ÷ 2) + +const assembler = start_assemble(K) +nothing # hide +``` + +We use BenchmarkTools to measure the performance: + +```@example assembly-perf +assemble_v1(assembler, K, dofs, Ke) # hide +assemble_v2(assembler, K, dofs, Ke) # hide +assemble_v3(assembler, K, dofs, Ke) # hide +assemble_v4(assembler, K, dofs, Ke) # hide +nothing # hide +``` + +```julia +using BenchmarkTools + +@btime assemble_v1(assembler, K, dofs, Ke) evals = 10 setup = fill!(K, 0) +@btime assemble_v2(assembler, K, dofs, Ke) evals = 10 setup = Ferrite.fillzero!(K) +@btime assemble_v3(assembler, K, dofs, Ke) evals = 10 setup = Ferrite.fillzero!(K) +@btime assemble_v4(assembler, K, dofs, Ke) evals = 10 setup = Ferrite.fillzero!(K) +``` + +The results below are obtained on an Macbook Pro with an Apple M3 CPU. + +``` +606.438 μs (36 allocations: 7.67 MiB) +283.300 ns (0 allocations: 0 bytes) +158.300 ns (0 allocations: 0 bytes) + 83.400 ns (0 allocations: 0 bytes) +``` + +The results match what we expect based on the explanations above: + - Between strategy 1 and 2 we got rid of the allocations completely and decreased the time + with a factor of 2100(!). + - Between strategy 2 and 3 we got rid of the double lookup and decreased the time with + another factor of almost 2. + - Between strategy 3 and 4 we got rid of the random lookup order and decreased the time + with another factor of almost 2. + +The most important thing for this benchmark is to get rid of the allocations. By using an +assembler instead of doing the naive thing we reduce the runtime with a factor of more than +7000(!!) in total. + +### Full system assembly + +We will now compare the four strategies in a more realistic scenario where we assemble all +elements. This is to put the assembly performance in relation to other operations in the +finite element program. After all, assembly performance might not matter in the end if other +things dominate the runtime anyway. + +For this comparison we simply consider the heat equation (see +[Tutorial 1: Heat equation](../tutorials/heat_equation.md)) and assemble the global matrix. + +```@example assembly-perf +function assemble_system!(assembler_function::F, K, dh, cv) where {F} + assembler = start_assemble(K) + ke = zeros(ndofs_per_cell(dh), ndofs_per_cell(dh)) + n = getnbasefunctions(cv) + for cell in CellIterator(dh) + reinit!(cv, cell) + ke .= 0 + for qp in 1:getnquadpoints(cv) + dΩ = getdetJdV(cv, qp) + for i in 1:n + ∇ϕi = shape_gradient(cv, qp, i) + for j in 1:n + ∇ϕj = shape_gradient(cv, qp, j) + ke[i, j] += ( ∇ϕi ⋅ ∇ϕj ) * dΩ + end + end + end + assembler_function(assembler, K, celldofs(cell), ke) + end + return +end +nothing # hide +``` + +Finally, we need cellvalues for the field in order to perform the integration: + +```@example assembly-perf +qr = QuadratureRule{RefTriangle}(2) +const cellvalues = CellValues(qr, ip) +nothing # hide +``` + +We can now time the four assembly strategies: + +```@example assembly-perf +res = 1138.8803468514259 # hide +# assemble_system!(assemble_v1, K, dh, cellvalues) # hide +# @assert norm(K.nzval) ≈ res # hide +assemble_system!(assemble_v2, K, dh, cellvalues) # hide +@assert norm(K.nzval) ≈ res # hide +assemble_system!(assemble_v3, K, dh, cellvalues) # hide +@assert norm(K.nzval) ≈ res # hide +assemble_system!(assemble_v4, K, dh, cellvalues) # hide +@assert norm(K.nzval) ≈ res # hide +nothing # hide +``` + +```julia +@time assemble_system!(assemble_v1, K, dh, cellvalues) +@time assemble_system!(assemble_v2, K, dh, cellvalues) +@time assemble_system!(assemble_v3, K, dh, cellvalues) +@time assemble_system!(assemble_v4, K, dh, cellvalues) +``` + +We then obtain the following results (running on the same machine as above): + +``` +12.175625 seconds (719.99 k allocations: 149.809 GiB, 11.59% gc time) + 0.009313 seconds (8 allocations: 928 bytes) + 0.006055 seconds (8 allocations: 928 bytes) + 0.004530 seconds (10 allocations: 1.062 KiB) +``` + +This follows the same trend as for the benchmarks for individual cell assembly and shows +that the efficiency of the assembly strategy is crucial for the overall performance of the +program. In particular this benchmark shows that allocations in such a tight loop from the +first strategy is very costly and puts a strain on the garbage collector: 11% of the time is +spent in GC instead of crunching numbers. + +It should of course be noted that the more expensive the element routine is, the less the +performance of the assembly strategy matters for the total runtime. However, there are no +reason not to use the fastest method given that it is readily available in Ferrite. From 9359cfdadaf8c81409f6eaeb39008369ab76fd98 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Thu, 3 Oct 2024 01:11:27 +0200 Subject: [PATCH 50/50] Use fill instead of Ferrite.fillzero in assembly explanation (#1088) --- docs/src/topics/assembly.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/topics/assembly.md b/docs/src/topics/assembly.md index ae3b5ab0a0..91ef0c73eb 100644 --- a/docs/src/topics/assembly.md +++ b/docs/src/topics/assembly.md @@ -273,9 +273,9 @@ nothing # hide using BenchmarkTools @btime assemble_v1(assembler, K, dofs, Ke) evals = 10 setup = fill!(K, 0) -@btime assemble_v2(assembler, K, dofs, Ke) evals = 10 setup = Ferrite.fillzero!(K) -@btime assemble_v3(assembler, K, dofs, Ke) evals = 10 setup = Ferrite.fillzero!(K) -@btime assemble_v4(assembler, K, dofs, Ke) evals = 10 setup = Ferrite.fillzero!(K) +@btime assemble_v2(assembler, K, dofs, Ke) evals = 10 setup = fill!(K, 0) +@btime assemble_v3(assembler, K, dofs, Ke) evals = 10 setup = fill!(K, 0) +@btime assemble_v4(assembler, K, dofs, Ke) evals = 10 setup = fill!(K, 0) ``` The results below are obtained on an Macbook Pro with an Apple M3 CPU.