From c44c02d03848d7ef561424c6692c82c0c9c69794 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 12 Jul 2023 10:33:11 +0200 Subject: [PATCH 001/172] Introduce FunctionValues and GeometryValues --- src/FEValues/CellValues.jl | 63 +++++++++++++++++++++++++ src/FEValues/FunctionValues.jl | 78 +++++++++++++++++++++++++++++++ src/FEValues/GeometryValues.jl | 84 ++++++++++++++++++++++++++++++++++ src/FEValues/common_values.jl | 15 +----- src/Ferrite.jl | 3 +- src/PointEval/point_values.jl | 12 ++--- test/test_cellvalues.jl | 13 ++++-- 7 files changed, 243 insertions(+), 25 deletions(-) create mode 100644 src/FEValues/CellValues.jl create mode 100644 src/FEValues/FunctionValues.jl create mode 100644 src/FEValues/GeometryValues.jl diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl new file mode 100644 index 0000000000..7cc83fbff3 --- /dev/null +++ b/src/FEValues/CellValues.jl @@ -0,0 +1,63 @@ +include("GeometryValues.jl") +include("FunctionValues.jl") + +function default_geometric_interpolation(::Interpolation{shape}) where {dim, shape <: AbstractRefShape{dim}} + return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) +end + +struct CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCellValues + geo_values::GeometryValues{dMdξ_t, GIP, T} + fun_values::FunctionValues{IP, N_t, dNdx_t, dNdξ_t} + qr::QR +end +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T + geo_values = GeometryValues(T, ip_geo.ip, qr) + fun_values = FunctionValues(T, ip_fun, qr, ip_geo) + return CellValues(geo_values, fun_values, qr) +end + +CellValues(qr::QuadratureRule, ip::Interpolation, args...) = CellValues(Float64, qr, ip, args...) +function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation=default_geometric_interpolation(ip)) where T + return CellValues(T, qr, ip, VectorizedInterpolation(ip_geo)) +end + +# Access geometry values +for op = (:getdetJdV, :getngeobasefunctions, :geometric_value) + eval(quote + @propagate_inbounds $op(cv::CellValues, args...) = $op(cv.geo_values, args...) + end) +end + +# Accessors for function values +getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) +for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient, :shape_curl) + eval(quote + @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) + end) +end +# Access quadrature rule values +getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) + +function reinit!(cv::CellValues, x::AbstractVector{<:Vec}) + geo_values = cv.geo_values + n_geom_basefuncs = getngeobasefunctions(geo_values) + if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs + throw_incompatible_coord_length(length(x), n_geom_basefuncs) + end + @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) + @inline Jinv = calculate_mapping(geo_values, q_point, w, x) + apply_mapping!(cv.fun_values, q_point, Jinv) + end + return nothing +end + +function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) + rdim = getdim(cv.geo_values.ip) + vdim = isa(shape_value(cv, 1, 1), Vec) ? length(shape_value(cv, 1, 1)) : 0 + sdim = length(shape_gradient(cv, 1, 1)) ÷ length(shape_value(cv, 1, 1)) + vstr = vdim==0 ? "scalar" : "vdim=$vdim" + print(io, "CellValues(", vstr, ", rdim=$rdim, and sdim=$sdim): ") + print(io, getnquadpoints(cv), " quadrature points") + print(io, "\n Function interpolation: "); show(io, d, cv.fun_values.ip) + print(io, "\nGeometric interpolation: "); show(io, d, cv.geo_values.ip^sdim) +end \ No newline at end of file diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl new file mode 100644 index 0000000000..2d2d38c488 --- /dev/null +++ b/src/FEValues/FunctionValues.jl @@ -0,0 +1,78 @@ +# Helpers to get the correct types for FunctionValues for the given function and, if needed, geometric interpolations. +struct SInterpolationDims{rdim,sdim} end +struct VInterpolationDims{rdim,sdim,vdim} end +function InterpolationDims(::ScalarInterpolation, ip_geo::VectorizedInterpolation{sdim}) where sdim + return SInterpolationDims{getdim(ip_geo),sdim}() +end +function InterpolationDims(::VectorInterpolation{vdim}, ip_geo::VectorizedInterpolation{sdim}) where {vdim,sdim} + return VInterpolationDims{getdim(ip_geo),sdim,vdim}() +end + +typeof_N(::Type{T}, ::SInterpolationDims) where T = T +typeof_N(::Type{T}, ::VInterpolationDims{<:Any,dim,dim}) where {T,dim} = Vec{dim,T} +typeof_N(::Type{T}, ::VInterpolationDims{<:Any,<:Any,vdim}) where {T,vdim} = SVector{vdim,T} # Why not ::Vec here? + +typeof_dNdx(::Type{T}, ::SInterpolationDims{dim,dim}) where {T,dim} = Vec{dim,T} +typeof_dNdx(::Type{T}, ::SInterpolationDims{<:Any,sdim}) where {T,sdim} = SVector{sdim,T} # Why not ::Vec here? +typeof_dNdx(::Type{T}, ::VInterpolationDims{dim,dim,dim}) where {T,dim} = Tensor{2,dim,T} +typeof_dNdx(::Type{T}, ::VInterpolationDims{<:Any,sdim,vdim}) where {T,sdim,vdim} = SMatrix{vdim,sdim,T} # If vdim=sdim!=rdim Tensor would be possible... + +typeof_dNdξ(::Type{T}, ::SInterpolationDims{dim,dim}) where {T,dim} = Vec{dim,T} +typeof_dNdξ(::Type{T}, ::SInterpolationDims{rdim}) where {T,rdim} = SVector{rdim,T} # Why not ::Vec here? +typeof_dNdξ(::Type{T}, ::VInterpolationDims{dim,dim,dim}) where {T,dim} = Tensor{2,dim,T} +typeof_dNdξ(::Type{T}, ::VInterpolationDims{rdim,<:Any,vdim}) where {T,rdim,vdim} = SMatrix{vdim,rdim,T} # If vdim=rdim!=sdim Tensor would be possible... + +struct FunctionValues{IP, N_t, dNdx_t, dNdξ_t} + N::Matrix{N_t} + dNdx::Matrix{dNdx_t} + dNdξ::Matrix{dNdξ_t} + ip::IP +end +function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T + ip_dims = InterpolationDims(ip, ip_geo) + N_t = typeof_N(T, ip_dims) + dNdx_t = typeof_dNdx(T, ip_dims) + dNdξ_t = typeof_dNdξ(T, ip_dims) + n_shape = getnbasefunctions(ip) + n_qpoints = getnquadpoints(qr) + + N = zeros(N_t, n_shape, n_qpoints) + dNdξ = zeros(dNdξ_t, n_shape, n_qpoints) + dNdx = fill(zero(dNdx_t) * T(NaN), n_shape, n_qpoints) + fv = FunctionValues(N, dNdx, dNdξ, ip) + precompute_values!(fv, qr) # Precompute N and dNdξ + return fv +end + +function precompute_values!(fv::FunctionValues, qr::QuadratureRule) + n_shape = getnbasefunctions(fv.ip) + for (qp, ξ) in pairs(getpoints(qr)) + for i in 1:n_shape + fv.dNdξ[i, qp], fv.N[i, qp] = shape_gradient_and_value(fv.ip, ξ, i) + end + end +end + +getnbasefunctions(funvals::FunctionValues) = size(funvals.N, 1) +@propagate_inbounds shape_value(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.N[base_func, q_point] +@propagate_inbounds shape_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.dNdx[base_func, q_point] +@propagate_inbounds shape_symmetric_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = symmetric(shape_gradient(funvals, q_point, base_func)) + + +# Hotfix to get the dots right for embedded elements until mixed tensors are merged. +# Scalar/Vector interpolations with sdim == rdim (== vdim) +dothelper(A, B) = A ⋅ B +# Vector interpolations with sdim == rdim != vdim +dothelper(A::SMatrix{vdim, dim}, B::Tensor{2, dim}) where {vdim, dim} = A * SMatrix{dim, dim}(B) +# Scalar interpolations with sdim > rdim +dothelper(A::SVector{rdim}, B::SMatrix{rdim, sdim}) where {rdim, sdim} = B' * A +# Vector interpolations with sdim > rdim +dothelper(B::SMatrix{vdim, rdim}, A::SMatrix{rdim, sdim}) where {vdim, rdim, sdim} = B * A + +function apply_mapping!(funvals::FunctionValues, q_point::Int, Jinv) + @inbounds for j in 1:getnbasefunctions(funvals) + #funvals.dNdx[j, q_point] = funvals.dNdξ[j, q_point] ⋅ Jinv # TODO via Tensors.jl + funvals.dNdx[j, q_point] = dothelper(funvals.dNdξ[j, q_point], Jinv) + end + return nothing +end \ No newline at end of file diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl new file mode 100644 index 0000000000..0464a11eef --- /dev/null +++ b/src/FEValues/GeometryValues.jl @@ -0,0 +1,84 @@ +struct GeometryValues{dMdξ_t, GIP, T} + M::Matrix{T} + dMdξ::Matrix{dMdξ_t} + detJdV::Vector{T} + ip::GIP +end +function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T + n_shape = getnbasefunctions(ip) + n_qpoints = getnquadpoints(qr) + VT = Vec{getdim(ip),T} + M = zeros(T, n_shape, n_qpoints) + dMdξ = zeros(VT, n_shape, n_qpoints) + for (qp, ξ) in pairs(getpoints(qr)) + for i in 1:n_shape + dMdξ[i, qp], M[i, qp] = shape_gradient_and_value(ip, ξ, i) + end + end + detJdV::Vector{T} = fill(T(NaN), n_qpoints) + return GeometryValues(M, dMdξ, detJdV, ip) +end + +getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) +@propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] +@propagate_inbounds getdetJdV(geovals::GeometryValues, q_point::Int) = geovals.detJdV[q_point] + +function calculate_mapping(geo_values::GeometryValues{<:Vec{dim,T}}, q_point, w, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} + fecv_J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) + @inbounds for j in 1:getngeobasefunctions(geo_values) + fecv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] + end + detJ = det(fecv_J) + detJ > 0.0 || throw_detJ_not_pos(detJ) + @inbounds geo_values.detJdV[q_point] = detJ*w + return inv(fecv_J) +end + + +# Embedded + +""" + embedding_det(J::SMatrix{3, 2}) + +Embedding determinant for surfaces in 3D. + +TLDR: "det(J) =" ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ + +The transformation theorem for some function f on a 2D surface in 3D space leads to + ∫ f ⋅ dS = ∫ f ⋅ (∂x/∂ξ₁ × ∂x/∂ξ₂) dξ₁dξ₂ = ∫ f ⋅ n ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ dξ₁dξ₂ +where ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ is "detJ" and n is the unit normal. +See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-dimensional-functions-on-finite-element-surfaces for simple explanation. +For more details see e.g. the doctoral thesis by Mirza Cenanovic **Finite element methods for surface problems* (2017), Ch. 2 **Trangential Calculus**. +""" +embedding_det(J::SMatrix{3,2}) = norm(J[:,1] × J[:,2]) + +""" + embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) + +Embedding determinant for curves in 2D and 3D. + +TLDR: "det(J) =" ||∂x/∂ξ||₂ + +The transformation theorem for some function f on a 1D curve in 2D and 3D space leads to + ∫ f ⋅ dE = ∫ f ⋅ ∂x/∂ξ dξ = ∫ f ⋅ t ||∂x/∂ξ||₂ dξ +where ||∂x/∂ξ||₂ is "detJ" and t is "the unit tangent". +See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-dimensional-functions-on-finite-element-surfaces for simple explanation. +""" +embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) = norm(J) + +function calculate_mapping(geo_values::GeometryValues{<:Vec{rdim,T}}, q_point, w, x::AbstractVector{<:Vec{sdim,T}}) where {rdim,sdim,T} + n_geom_basefuncs = getngeobasefunctions(geo_values) + fecv_J = zero(MMatrix{sdim, rdim, T}) # TODO replace with MixedTensor (see https://github.com/Ferrite-FEM/Tensors.jl/pull/188) + for j in 1:n_geom_basefuncs + #fecv_J += x[j] ⊗ geo_values.dMdξ[j, i] # TODO via Tensors.jl + for k in 1:sdim, l in 1:rdim + fecv_J[k, l] += x[j][k] * geo_values.dMdξ[j, q_point][l] + end + end + fecv_J = SMatrix(fecv_J) + detJ = embedding_det(fecv_J) + detJ > 0.0 || throw_detJ_not_pos(detJ) + @inbounds geo_values.detJdV[q_point] = detJ * w + # Compute "left inverse" of J + return pinv(fecv_J) +end \ No newline at end of file diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index e7d43de843..f11b9a76a0 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -38,13 +38,6 @@ The derivatives of the shape functions, and the new integration weights are comp """ reinit! -""" - getnquadpoints(cv::CellValues) - -Return the number of quadrature points in `cv`'s quadrature rule. -""" -getnquadpoints(fe::CellValues) = getnquadpoints(fe.qr) - """ getnquadpoints(fv::FaceValues) @@ -66,7 +59,6 @@ finite element cell or face as ``\\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`` """ -@propagate_inbounds getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] @propagate_inbounds getdetJdV(bv::FaceValues, q_point::Int) = bv.detJdV[q_point, bv.current_face[]] """ @@ -75,10 +67,8 @@ finite element cell or face as Return the value of shape function `base_function` evaluated in quadrature point `q_point`. """ -@propagate_inbounds shape_value(cv::CellValues, q_point::Int, base_func::Int) = cv.N[base_func, q_point] @propagate_inbounds shape_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.N[base_func, q_point, bv.current_face[]] -@propagate_inbounds geometric_value(cv::CellValues, q_point::Int, base_func::Int) = cv.M[base_func, q_point] @propagate_inbounds geometric_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.M[base_func, q_point, bv.current_face[]] """ @@ -87,7 +77,6 @@ quadrature point `q_point`. Return the gradient of shape function `base_function` evaluated in quadrature point `q_point`. """ -@propagate_inbounds shape_gradient(cv::CellValues, q_point::Int, base_func::Int) = cv.dNdx[base_func, q_point] @propagate_inbounds shape_gradient(bv::FaceValues, q_point::Int, base_func::Int) = bv.dNdx[base_func, q_point, bv.current_face[]] """ @@ -96,7 +85,7 @@ quadrature point `q_point`. Return the symmetric gradient of shape function `base_function` evaluated in quadrature point `q_point`. """ -@propagate_inbounds shape_symmetric_gradient(cv::CellValues, q_point::Int, base_func::Int) = symmetric(shape_gradient(cv, q_point, base_func)) +function shape_symmetric_gradient end """ shape_divergence(fe_v::AbstractValues, q_point::Int, base_function::Int) @@ -273,7 +262,7 @@ function Base.show(io::IO, ::MIME"text/plain", fe_v::AbstractValues) end # copy -for ValueType in (CellValues, FaceValues) +for ValueType in (GeometryValues, FunctionValues, CellValues, FaceValues) args = [:(copy(cv.$fname)) for fname in fieldnames(ValueType)] @eval begin function Base.copy(cv::$ValueType) diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 533f90f05e..deabdc8406 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -81,7 +81,8 @@ include("interpolations.jl") include("Quadrature/quadrature.jl") # FEValues -include("FEValues/cell_values.jl") +#include("FEValues/cell_values.jl") +include("FEValues/CellValues.jl") include("FEValues/face_values.jl") include("PointEval/point_values.jl") include("FEValues/common_values.jl") diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 473bd1dda8..2e6eb31860 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -24,7 +24,7 @@ struct PointValues{CV} <: AbstractValues PointValues{CV}(cv::CV) where {CV} = new{CV}(cv) end -PointValues(cv::CellValues) = PointValues(eltype(cv.M), cv.ip, cv.gip) +PointValues(cv::CellValues) = PointValues(eltype(shape_value(cv,1,1)), cv.fun_values.ip, cv.geo_values.ip) function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip)) return PointValues(Float64, ip, ipg) end @@ -57,11 +57,11 @@ 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} - qp = 1 # PointValues only have a single qp - # TODO: Does M need to be updated too? - for i in 1:getnbasefunctions(pv.cv.ip) - pv.cv.dNdξ[i, qp], pv.cv.N[i, qp] = shape_gradient_and_value(pv.cv.ip, ξ, i) - end + # Update the quadrature point location + qr_points = getpoints(pv.cv.qr) + qr_points[1] = ξ + precompute_values!(pv.cv.fun_values, pv.cv.qr) # See Issue #763, should also update dMdξ!, but separate issue + # Regular reinit reinit!(pv.cv, x) return nothing end diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 73fc0605e0..382d97e9af 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -86,7 +86,8 @@ for (scalar_interpol, quad_rule) in ( @test spatial_coordinate(cv, i, x) ≈ qp_x end - # test copy + # test copy: Disable with new structure. TODO: Re-enable + #= cvc = copy(cv) @test typeof(cv) == typeof(cvc) for fname in fieldnames(typeof(cv)) @@ -97,6 +98,7 @@ for (scalar_interpol, quad_rule) in ( end @test v == vc end + =# end end @@ -279,6 +281,7 @@ end @testset "CellValues constructor entry points" begin qr = QuadratureRule{RefTriangle}(1) + _get_geo_ip(cv::CellValues) = cv.geo_values.ip for fun_ip in (Lagrange{RefTriangle, 1}(), Lagrange{RefTriangle, 2}()^2) value_type(T) = fun_ip isa ScalarInterpolation ? T : Vec{2, T} grad_type(T) = fun_ip isa ScalarInterpolation ? Vec{2, T} : Tensor{2, 2, T, 4} @@ -286,24 +289,24 @@ end cv = CellValues(qr, fun_ip) @test Ferrite.shape_value_type(cv) == value_type(Float64) @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) - @test cv.gip == Lagrange{RefTriangle, 1}() + @test _get_geo_ip(cv) == Lagrange{RefTriangle, 1}() # Numeric type + quadrature + scalar function cv = CellValues(Float32, qr, fun_ip) @test Ferrite.shape_value_type(cv) == value_type(Float32) @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) - @test cv.gip == Lagrange{RefTriangle, 1}() + @test _get_geo_ip(cv) == Lagrange{RefTriangle, 1}() for geo_ip in (Lagrange{RefTriangle, 2}(), Lagrange{RefTriangle, 2}()^2) scalar_ip(ip) = ip isa VectorizedInterpolation ? ip.ip : ip # Quadrature + scalar function + geo cv = CellValues(qr, fun_ip, geo_ip) @test Ferrite.shape_value_type(cv) == value_type(Float64) @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) - @test cv.gip == scalar_ip(geo_ip) + @test _get_geo_ip(cv) == scalar_ip(geo_ip) # Numeric type + quadrature + scalar function + scalar geo cv = CellValues(Float32, qr, fun_ip, geo_ip) @test Ferrite.shape_value_type(cv) == value_type(Float32) @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) - @test cv.gip == scalar_ip(geo_ip) + @test _get_geo_ip(cv) == scalar_ip(geo_ip) end end end From 1d1f7c62c975cead096b15242d8214411f936f5e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 12 Jul 2023 10:51:55 +0200 Subject: [PATCH 002/172] Add benchmark and temporarily support renamed old types --- cv_benchmark.jl | 65 +++++++++++++++++++++++++ src/FEValues/CellValues.jl | 11 ++++- src/FEValues/FunctionValues.jl | 8 +-- src/FEValues/cell_values.jl | 89 ++++++++++------------------------ 4 files changed, 104 insertions(+), 69 deletions(-) create mode 100644 cv_benchmark.jl diff --git a/cv_benchmark.jl b/cv_benchmark.jl new file mode 100644 index 0000000000..fd8b426a3a --- /dev/null +++ b/cv_benchmark.jl @@ -0,0 +1,65 @@ +using Ferrite, BenchmarkTools, StaticArrays + +function get_values(CellType, ::Val{dim}, q_order=2) where dim + grid = generate_grid(CellType, ntuple(Returns(2), dim)) + ip = Ferrite.default_interpolation(getcelltype(grid)) + RefShape = Ferrite.getrefshape(ip) + qr = QuadratureRule{RefShape}(q_order) + cv_u = CellValues(qr, ip^dim, ip) + cv_p = CellValues(qr, ip, ip) + return cv_u, cv_p, getcoordinates(grid, 1) +end + +function reinit_masterfix!(cv::Ferrite.OldCellValues{<:Any, <:Any, <:Tensor, <:Tensor, T, Vec{dim,T}}, x::AbstractVector{Vec{dim,T}}) where {dim, T} + n_geom_basefuncs = Ferrite.getngeobasefunctions(cv) + n_func_basefuncs = Ferrite.getnbasefunctions(cv) + length(x) == n_geom_basefuncs || Ferrite.throw_incompatible_coord_length(length(x), n_geom_basefuncs) + + @inbounds for (i, w) in pairs(getweights(cv.qr)) + fecv_J = zero(Tensor{2,dim,T}) + for j in 1:n_geom_basefuncs + fecv_J += x[j] ⊗ cv.dMdξ[j, i] + end + detJ = det(fecv_J) + detJ > 0.0 || Ferrite.throw_detJ_not_pos(detJ) + cv.detJdV[i] = detJ * w + Jinv = inv(fecv_J) + for j in 1:n_func_basefuncs + # cv.dNdx[j, i] = cv.dNdξ[j, i] ⋅ Jinv + cv.dNdx[j, i] = Ferrite.dothelper(cv.dNdξ[j, i], Jinv) + end + end +end + +#for (CT, dim) in ((Triangle,2), (QuadraticTriangle,2), (Hexahedron,3), (Tetrahedron,3)) +for (CT, dim) in ((Triangle,2),) + # 2 and 4 fields in 2D + cv_u, cv_p, x = get_values(CT, Val(dim), 2) + ocv_u = Ferrite.OldCellValues(cv_u) + ocv_p = Ferrite.OldCellValues(cv_p) + + print("Scalar : $CT in $(dim)D"); println() + print("1 PR : "); @btime reinit!($cv_p, $x); + print("1 master : "); @btime reinit!($ocv_p, $x); + print("1 master (fix): "); @btime reinit_masterfix!($ocv_p, $x); + + print("Vector : $CT in $(dim)D"); println() + print("1 PR : "); @btime reinit!($cv_u, $x); + print("1 master : "); @btime reinit!($ocv_u, $x); + print("1 master (fix): "); @btime reinit_masterfix!($ocv_u, $x); + # =# + #= + println() + print("2 CellValues : "); @btime reinit2!($cv_u, $cv_p, $x) + print("2 MultiCellValues : "); @btime reinit!($mcv_pu, $x) + print("2 MultiCellValues2 : "); @btime reinit!($mcv2_pu, $x) + println() + # =# + #= + print("4 CellValues : "); @btime reinit4!($cv_u, $cv_p, $cv_u2, $cv_p2, $x) + print("4 MultiValues : "); @btime reinit!($cv4, $x) + print("4 Tuple{CV} : "); @btime reinit_multiple!($x, $cv_u, $cv_p, $cv_u2, $cv_p2) + print("4 ValuesGroup : "); @btime reinit!($cvg4, $x); + println() + # =# +end \ No newline at end of file diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 7cc83fbff3..9215ddd4ed 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -46,7 +46,7 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) @inline Jinv = calculate_mapping(geo_values, q_point, w, x) - apply_mapping!(cv.fun_values, q_point, Jinv) + @inline apply_mapping!(cv.fun_values, q_point, Jinv) end return nothing end @@ -60,4 +60,13 @@ function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) print(io, getnquadpoints(cv), " quadrature points") print(io, "\n Function interpolation: "); show(io, d, cv.fun_values.ip) print(io, "\nGeometric interpolation: "); show(io, d, cv.geo_values.ip^sdim) +end + +# Temporary for benchmark/test +include("cell_values.jl") +function OldCellValues(cv::CellValues) + ip = cv.fun_values.ip + sdim = length(shape_gradient(cv, 1, 1)) ÷ length(shape_value(cv, 1, 1)) + ip_geo = cv.geo_values.ip^sdim + return OldCellValues(cv.qr, ip, ip_geo) end \ No newline at end of file diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 2d2d38c488..3dde38888c 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -61,13 +61,13 @@ getnbasefunctions(funvals::FunctionValues) = size(funvals.N, 1) # Hotfix to get the dots right for embedded elements until mixed tensors are merged. # Scalar/Vector interpolations with sdim == rdim (== vdim) -dothelper(A, B) = A ⋅ B +@inline dothelper(A, B) = A ⋅ B # Vector interpolations with sdim == rdim != vdim -dothelper(A::SMatrix{vdim, dim}, B::Tensor{2, dim}) where {vdim, dim} = A * SMatrix{dim, dim}(B) +@inline dothelper(A::SMatrix{vdim, dim}, B::Tensor{2, dim}) where {vdim, dim} = A * SMatrix{dim, dim}(B) # Scalar interpolations with sdim > rdim -dothelper(A::SVector{rdim}, B::SMatrix{rdim, sdim}) where {rdim, sdim} = B' * A +@inline dothelper(A::SVector{rdim}, B::SMatrix{rdim, sdim}) where {rdim, sdim} = B' * A # Vector interpolations with sdim > rdim -dothelper(B::SMatrix{vdim, rdim}, A::SMatrix{rdim, sdim}) where {vdim, rdim, sdim} = B * A +@inline dothelper(B::SMatrix{vdim, rdim}, A::SMatrix{rdim, sdim}) where {vdim, rdim, sdim} = B * A function apply_mapping!(funvals::FunctionValues, q_point::Int, Jinv) @inbounds for j in 1:getnbasefunctions(funvals) diff --git a/src/FEValues/cell_values.jl b/src/FEValues/cell_values.jl index 953ed36e7b..ebd7fde6f2 100644 --- a/src/FEValues/cell_values.jl +++ b/src/FEValues/cell_values.jl @@ -1,7 +1,7 @@ """ - CellValues([::Type{T},] quad_rule::QuadratureRule, func_interpol::Interpolation, [geom_interpol::Interpolation]) + OldCellValues([::Type{T},] quad_rule::QuadratureRule, func_interpol::Interpolation, [geom_interpol::Interpolation]) -A `CellValues` object facilitates the process of evaluating values of shape functions, gradients of shape functions, +A `OldCellValues` object facilitates the process of evaluating values of shape functions, gradients of shape functions, values of nodal functions, gradients and divergences of nodal functions etc. in the finite element cell. **Arguments:** @@ -29,13 +29,13 @@ values of nodal functions, gradients and divergences of nodal functions etc. in * [`function_divergence`](@ref) * [`spatial_coordinate`](@ref) """ -CellValues +OldCellValues function default_geometric_interpolation(::Interpolation{shape}) where {dim, shape <: AbstractRefShape{dim}} return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) end -struct CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCellValues +struct OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCellValues N::Matrix{N_t} dNdx::Matrix{dNdx_t} dNdξ::Matrix{dNdξ_t} @@ -47,8 +47,8 @@ struct CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCell gip::GIP end -# Common initializer code for constructing CellValues after the types have been determined -function CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(qr::QR, ip::IP, gip::GIP) where { +# Common initializer code for constructing OldCellValues after the types have been determined +function OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(qr::QR, ip::IP, gip::GIP) where { IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP, } @assert isconcretetype(IP) && isconcretetype(N_t) && isconcretetype(dNdx_t) && @@ -78,27 +78,27 @@ function CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(qr::QR, ip::I detJdV = fill(T(NaN), n_qpoints) - CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(N, dNdx, dNdξ, detJdV, M, dMdξ, qr, ip, gip) + OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(N, dNdx, dNdξ, detJdV, M, dMdξ, qr, ip, gip) end # Common entry point that fills in the numeric type and geometric interpolation -function CellValues(qr::QuadratureRule, ip::Interpolation, +function OldCellValues(qr::QuadratureRule, ip::Interpolation, gip::Interpolation = default_geometric_interpolation(ip)) - return CellValues(Float64, qr, ip, gip) + return OldCellValues(Float64, qr, ip, gip) end # Common entry point that fills in the geometric interpolation -function CellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation) where {T} - return CellValues(T, qr, ip, default_geometric_interpolation(ip)) +function OldCellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation) where {T} + return OldCellValues(T, qr, ip, default_geometric_interpolation(ip)) end # Common entry point that vectorizes an input scalar geometric interpolation -function CellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation, sgip::ScalarInterpolation) where {T} - return CellValues(T, qr, ip, VectorizedInterpolation(sgip)) +function OldCellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation, sgip::ScalarInterpolation) where {T} + return OldCellValues(T, qr, ip, VectorizedInterpolation(sgip)) end # Entrypoint for `ScalarInterpolation`s (rdim == sdim) -function CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { +function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { dim, shape <: AbstractRefShape{dim}, T, QR <: QuadratureRule{shape}, IP <: ScalarInterpolation{shape}, @@ -111,11 +111,11 @@ function CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { # Geometry interpolation M_t = T dMdξ_t = Vec{dim, T} - return CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) + return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) end # Entrypoint for `VectorInterpolation`s (vdim == rdim == sdim) -function CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { +function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { dim, shape <: AbstractRefShape{dim}, T, QR <: QuadratureRule{shape}, IP <: VectorInterpolation{dim, shape}, @@ -128,11 +128,11 @@ function CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { # Geometry interpolation M_t = T dMdξ_t = Vec{dim, T} - return CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) + return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) end # Entrypoint for `VectorInterpolation`s (vdim != rdim == sdim) -function CellValues(::Type{T}, qr::QR, ip::IP, vgip::VGIP) where { +function OldCellValues(::Type{T}, qr::QR, ip::IP, vgip::VGIP) where { vdim, dim, shape <: AbstractRefShape{dim}, T, QR <: QuadratureRule{shape}, IP <: VectorInterpolation{vdim, shape}, @@ -145,11 +145,11 @@ function CellValues(::Type{T}, qr::QR, ip::IP, vgip::VGIP) where { # Geometry interpolation M_t = T dMdξ_t = Vec{dim, T} - return CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, vgip.ip) + return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, vgip.ip) end # reinit! for regular (non-embedded) elements (rdim == sdim) -function reinit!(cv::CellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{Vec{dim,T}}) where { +function reinit!(cv::OldCellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{Vec{dim,T}}) where { dim, T, vdim, N_t <: Union{Number, Vec{dim}, SVector{vdim} }, dNdx_t <: Union{Vec{dim}, Tensor{2, dim}, SMatrix{vdim, dim}}, @@ -175,18 +175,8 @@ function reinit!(cv::CellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{ end end -# Hotfix to get the dots right for embedded elements until mixed tensors are merged. -# Scalar/Vector interpolations with sdim == rdim (== vdim) -@inline dothelper(A, B) = A ⋅ B -# Vector interpolations with sdim == rdim != vdim -@inline dothelper(A::SMatrix{vdim, dim}, B::Tensor{2, dim}) where {vdim, dim} = A * SMatrix{dim, dim}(B) -# Scalar interpolations with sdim > rdim -@inline dothelper(A::SVector{rdim}, B::SMatrix{rdim, sdim}) where {rdim, sdim} = B' * A -# Vector interpolations with sdim > rdim -@inline dothelper(B::SMatrix{vdim, rdim}, A::SMatrix{rdim, sdim}) where {vdim, rdim, sdim} = B * A - # Entrypoint for embedded `ScalarInterpolation`s (rdim < sdim) -function CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { +function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { sdim, rdim, shape <: AbstractRefShape{rdim}, T, QR <: QuadratureRule{shape}, IP <: ScalarInterpolation{shape}, @@ -201,11 +191,11 @@ function CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { # Geometry interpolation M_t = T dMdξ_t = Vec{rdim, T} - return CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) + return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) end # Entrypoint for embedded `VectorInterpolation`s (rdim < sdim) -function CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { +function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { sdim, vdim, rdim, shape <: AbstractRefShape{rdim}, T, QR <: QuadratureRule{shape}, IP <: VectorInterpolation{vdim, shape}, @@ -220,40 +210,11 @@ function CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { # Geometry interpolation M_t = T dMdξ_t = Vec{rdim, T} - return CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) + return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) end -""" - embedding_det(J::SMatrix{3, 2}) - -Embedding determinant for surfaces in 3D. - -TLDR: "det(J) =" ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ - -The transformation theorem for some function f on a 2D surface in 3D space leads to - ∫ f ⋅ dS = ∫ f ⋅ (∂x/∂ξ₁ × ∂x/∂ξ₂) dξ₁dξ₂ = ∫ f ⋅ n ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ dξ₁dξ₂ -where ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ is "detJ" and n is the unit normal. -See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-dimensional-functions-on-finite-element-surfaces for simple explanation. -For more details see e.g. the doctoral thesis by Mirza Cenanovic **Finite element methods for surface problems* (2017), Ch. 2 **Trangential Calculus**. -""" -embedding_det(J::SMatrix{3,2}) = norm(J[:,1] × J[:,2]) - -""" - embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) - -Embedding determinant for curves in 2D and 3D. - -TLDR: "det(J) =" ||∂x/∂ξ||₂ - -The transformation theorem for some function f on a 1D curve in 2D and 3D space leads to - ∫ f ⋅ dE = ∫ f ⋅ ∂x/∂ξ dξ = ∫ f ⋅ t ||∂x/∂ξ||₂ dξ -where ||∂x/∂ξ||₂ is "detJ" and t is "the unit tangent". -See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-dimensional-functions-on-finite-element-surfaces for simple explanation. -""" -embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) = norm(J) - # reinit! for embedded elements, rdim < sdim -function reinit!(cv::CellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{Vec{sdim,T}}) where { +function reinit!(cv::OldCellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{Vec{sdim,T}}) where { rdim, sdim, vdim, T, N_t <: Union{Number, SVector{vdim}}, dNdx_t <: Union{SVector{sdim, T}, SMatrix{vdim, sdim, T}}, From 92b9779dd233e9797aee5c38c89e2901d12eb91e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 12 Jul 2023 11:12:41 +0200 Subject: [PATCH 003/172] Fix inline annotation for julia 1.6 ? --- src/FEValues/CellValues.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 9215ddd4ed..0ffe18de37 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -45,7 +45,7 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}) throw_incompatible_coord_length(length(x), n_geom_basefuncs) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) - @inline Jinv = calculate_mapping(geo_values, q_point, w, x) + Jinv = @inline calculate_mapping(geo_values, q_point, w, x) @inline apply_mapping!(cv.fun_values, q_point, Jinv) end return nothing From 8fef34dda0eaba9130044a75f76a8ea008449973 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 12 Jul 2023 11:23:31 +0200 Subject: [PATCH 004/172] Move callsite inline to function definitions --- cv_benchmark.jl | 2 +- src/FEValues/CellValues.jl | 4 ++-- src/FEValues/FunctionValues.jl | 2 +- src/FEValues/GeometryValues.jl | 4 ++-- src/FEValues/cell_values.jl | 4 ---- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cv_benchmark.jl b/cv_benchmark.jl index fd8b426a3a..db5870c996 100644 --- a/cv_benchmark.jl +++ b/cv_benchmark.jl @@ -1,7 +1,7 @@ using Ferrite, BenchmarkTools, StaticArrays function get_values(CellType, ::Val{dim}, q_order=2) where dim - grid = generate_grid(CellType, ntuple(Returns(2), dim)) + grid = generate_grid(CellType, ntuple(_->2, dim)) ip = Ferrite.default_interpolation(getcelltype(grid)) RefShape = Ferrite.getrefshape(ip) qr = QuadratureRule{RefShape}(q_order) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 0ffe18de37..c565e40521 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -45,8 +45,8 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}) throw_incompatible_coord_length(length(x), n_geom_basefuncs) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) - Jinv = @inline calculate_mapping(geo_values, q_point, w, x) - @inline apply_mapping!(cv.fun_values, q_point, Jinv) + Jinv = calculate_mapping(geo_values, q_point, w, x) + apply_mapping!(cv.fun_values, q_point, Jinv) end return nothing end diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 3dde38888c..14a56a0e52 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -69,7 +69,7 @@ getnbasefunctions(funvals::FunctionValues) = size(funvals.N, 1) # Vector interpolations with sdim > rdim @inline dothelper(B::SMatrix{vdim, rdim}, A::SMatrix{rdim, sdim}) where {vdim, rdim, sdim} = B * A -function apply_mapping!(funvals::FunctionValues, q_point::Int, Jinv) +@inline function apply_mapping!(funvals::FunctionValues, q_point::Int, Jinv) @inbounds for j in 1:getnbasefunctions(funvals) #funvals.dNdx[j, q_point] = funvals.dNdξ[j, q_point] ⋅ Jinv # TODO via Tensors.jl funvals.dNdx[j, q_point] = dothelper(funvals.dNdξ[j, q_point], Jinv) diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 0464a11eef..e9ad79daa2 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -23,7 +23,7 @@ getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) @propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] @propagate_inbounds getdetJdV(geovals::GeometryValues, q_point::Int) = geovals.detJdV[q_point] -function calculate_mapping(geo_values::GeometryValues{<:Vec{dim,T}}, q_point, w, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} +@inline function calculate_mapping(geo_values::GeometryValues{<:Vec{dim,T}}, q_point, w, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} fecv_J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) @inbounds for j in 1:getngeobasefunctions(geo_values) fecv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] @@ -66,7 +66,7 @@ See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-di """ embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) = norm(J) -function calculate_mapping(geo_values::GeometryValues{<:Vec{rdim,T}}, q_point, w, x::AbstractVector{<:Vec{sdim,T}}) where {rdim,sdim,T} +@inline function calculate_mapping(geo_values::GeometryValues{<:Vec{rdim,T}}, q_point, w, x::AbstractVector{<:Vec{sdim,T}}) where {rdim,sdim,T} n_geom_basefuncs = getngeobasefunctions(geo_values) fecv_J = zero(MMatrix{sdim, rdim, T}) # TODO replace with MixedTensor (see https://github.com/Ferrite-FEM/Tensors.jl/pull/188) for j in 1:n_geom_basefuncs diff --git a/src/FEValues/cell_values.jl b/src/FEValues/cell_values.jl index ebd7fde6f2..b50c23193a 100644 --- a/src/FEValues/cell_values.jl +++ b/src/FEValues/cell_values.jl @@ -31,10 +31,6 @@ values of nodal functions, gradients and divergences of nodal functions etc. in """ OldCellValues -function default_geometric_interpolation(::Interpolation{shape}) where {dim, shape <: AbstractRefShape{dim}} - return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) -end - struct OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCellValues N::Matrix{N_t} dNdx::Matrix{dNdx_t} From 506a6f7bdee7d3ffc45142c2acc7ced7849c2766 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 12 Jul 2023 11:42:42 +0200 Subject: [PATCH 005/172] Fix shell example, used internals of cellvalues --- docs/src/literate-tutorials/linear_shell.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/src/literate-tutorials/linear_shell.jl b/docs/src/literate-tutorials/linear_shell.jl index e5927b42e3..637436dbea 100644 --- a/docs/src/literate-tutorials/linear_shell.jl +++ b/docs/src/literate-tutorials/linear_shell.jl @@ -264,6 +264,8 @@ end; # ##### Main element routine # Below is the main routine that calculates the stiffness matrix of the shell element. # Since it is a so called degenerate shell element, the code is similar to that for an standard continuum element. +shape_reference_gradient(cv::CellValues, q_point, i) = cv.fun_values.dNdξ[i, q_point] + function integrate_shell!(ke, cv, qr_ooplane, X, data) nnodes = getnbasefunctions(cv) ndofs = nnodes*5 @@ -281,9 +283,9 @@ function integrate_shell!(ke, cv, qr_ooplane, X, data) ef1, ef2, ef3 = fiber_coordsys(p) for iqp in 1:getnquadpoints(cv) - N = cv.N[:,iqp] - dNdξ = cv.dNdξ[:,iqp] - dNdx = cv.dNdx[:,iqp] + N = [shape_value(cv, iqp, i) for i in 1:nnodes] + dNdξ = [shape_reference_gradient(cv, iqp, i) for i in 1:nnodes] + dNdx = [shape_gradient(cv, iqp, i) for i in 1:nnodes] for oqp in 1:length(qr_ooplane.weights) ζ = qr_ooplane.points[oqp][1] From d2eda0fe4df3c9899f5673bcca5a0c0b951dbc55 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 12 Jul 2023 14:56:44 +0200 Subject: [PATCH 006/172] Use FunctionValues in FaceValues too --- src/FEValues/FaceValues.jl | 174 ++++++++++++++++++++++++++++++++++ src/FEValues/common_values.jl | 10 +- src/FEValues/face_values.jl | 45 ++++----- src/Ferrite.jl | 3 +- 4 files changed, 204 insertions(+), 28 deletions(-) create mode 100644 src/FEValues/FaceValues.jl diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl new file mode 100644 index 0000000000..6b8becfd17 --- /dev/null +++ b/src/FEValues/FaceValues.jl @@ -0,0 +1,174 @@ +struct FaceGeometryValues{dMdξ_t, GIP, T, Normal_t} + M::Matrix{T} + dMdξ::Matrix{dMdξ_t} + detJdV::Vector{T} + normals::Vector{Normal_t} + ip::GIP +end +function FaceGeometryValues(::Type{T}, ip_vec::VectorizedInterpolation{sdim}, qr::QuadratureRule) where {T,sdim} + ip = ip_vec.ip + n_shape = getnbasefunctions(ip) + n_qpoints = getnquadpoints(qr) + M = zeros(T, n_shape, n_qpoints) + dMdξ = zeros(Vec{getdim(ip),T}, n_shape, n_qpoints) + for (qp, ξ) in pairs(getpoints(qr)) + for i in 1:n_shape + dMdξ[i, qp], M[i, qp] = shape_gradient_and_value(ip, ξ, i) + end + end + normals = fill(zero(Vec{sdim,T})*T(NaN), n_qpoints) + detJdV = fill(T(NaN), n_qpoints) + return FaceGeometryValues(M, dMdξ, detJdV, normals, ip) +end + +getngeobasefunctions(geovals::FaceGeometryValues) = size(geovals.M, 1) +@propagate_inbounds geometric_value(geovals::FaceGeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] +@propagate_inbounds getdetJdV(geovals::FaceGeometryValues, q_point::Int) = geovals.detJdV[q_point] +@propagate_inbounds getnormal(geovals::FaceGeometryValues, q_point::Int) = geovals.normals[q_point] + +@inline function calculate_mapping(geo_values::FaceGeometryValues{<:Vec{dim,T}}, face_nr::Int, q_point, w, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} + fefv_J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) + @inbounds for j in 1:getngeobasefunctions(geo_values) + fefv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] + end + weight_norm = weighted_normal(fefv_J, getrefshape(geo_values.ip), face_nr) + detJ = norm(weight_norm) + detJ > 0.0 || throw_detJ_not_pos(detJ) + @inbounds geo_values.detJdV[q_point] = detJ*w + @inbounds geo_values.normals[q_point] = weight_norm / norm(weight_norm) + return inv(fefv_J) +end + +struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: AbstractFaceValues + geo_values::Vector{FaceGeometryValues{dMdξ_t, GIP, T, Normal_t}} + fun_values::Vector{FunctionValues{IP, N_t, dNdx_t, dNdξ_t}} + qr::QR # FaceQuadratureRule + current_face::ScalarWrapper{Int} +end + +function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation=default_geometric_interpolation(ip_fun)) where T + geo_values = [FaceGeometryValues(T, ip_geo, qr) for qr in fqr.face_rules] + fun_values = [FunctionValues(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] + return FaceValues(geo_values, fun_values, fqr, ScalarWrapper(1)) +end + +FaceValues(qr::FaceQuadratureRule, ip::Interpolation, args...) = FaceValues(Float64, qr, ip, args...) +function FaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation, ip_geo::ScalarInterpolation) where T + return FaceValues(T, qr, ip, VectorizedInterpolation(ip_geo)) +end + +getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_values(fv)) +getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) +getnquadpoints(fv::FaceValues) = getnquadpoints(fv.qr, getcurrentface(fv)) + +get_geo_values(fv::FaceValues) = @inbounds fv.geo_values[getcurrentface(fv)] +for op = (:getdetJdV, :getngeobasefunctions, :geometric_value) + eval(quote + @propagate_inbounds $op(fv::FaceValues, args...) = $op(get_geo_values(fv), args...) + end) +end + +get_fun_values(fv::FaceValues) = @inbounds fv.fun_values[getcurrentface(fv)] +for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient, :shape_curl) + eval(quote + @propagate_inbounds $op(fv::FaceValues, i::Int, q_point::Int) = $op(get_fun_values(fv), i, q_point) + end) +end + +""" + getcurrentface(fv::FaceValues) + +Return the current active face of the `FaceValues` object (from last `reinit!`). + +""" +getcurrentface(fv::FaceValues) = fv.current_face[] + +""" + getnormal(fv::FaceValues, qp::Int) + +Return the normal at the quadrature point `qp` for the active face of the +`FaceValues` object(from last `reinit!`). +""" +getnormal(fv::FaceValues, qp::Int) = getnormal(fv.geo_values[getcurrentface(fv)], qp) + +nfaces(fv::FaceValues) = length(fv.geo_values) + +function checkface(fv::FaceValues, face::Int) + 0 < face <= nfaces(fv) || error("Face index out of range.") + return nothing +end + +function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int) where {dim, T} + @boundscheck checkface(fv, face_nr) + n_geom_basefuncs = getngeobasefunctions(fv) + length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) + @inbounds geo_values = fv.geo_values[face_nr] + @inbounds fun_values = fv.fun_values[face_nr] + + fv.current_face[] = face_nr + + @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) + Jinv = calculate_mapping(geo_values, face_nr, q_point, w, x) + apply_mapping!(fun_values, q_point, Jinv) + end +end + +""" + BCValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Union{Type{<:BoundaryIndex}}) + +`BCValues` stores the shape values at all faces/edges/vertices (depending on `boundary_type`) for the geomatric interpolation (`geom_interpol`), +for each dof-position determined by the `func_interpol`. Used mainly by the `ConstrainHandler`. +""" +struct BCValues{T} + M::Array{T,3} + nqp::Array{Int} + current_entity::ScalarWrapper{Int} +end + +BCValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Type{<:BoundaryIndex} = Ferrite.FaceIndex) = + BCValues(Float64, func_interpol, geom_interpol, boundary_type) + +function BCValues(::Type{T}, func_interpol::Interpolation{refshape}, geom_interpol::Interpolation{refshape}, boundary_type::Type{<:BoundaryIndex} = Ferrite.FaceIndex) where {T,dim,refshape <: AbstractRefShape{dim}} + # set up quadrature rules for each boundary entity with dof-positions + # (determined by func_interpol) as the quadrature points + interpolation_coords = reference_coordinates(func_interpol) + + qrs = QuadratureRule{refshape,T,dim}[] + for boundarydofs in dirichlet_boundarydof_indices(boundary_type)(func_interpol) + dofcoords = Vec{dim,T}[] + for boundarydof in boundarydofs + push!(dofcoords, interpolation_coords[boundarydof]) + end + qrf = QuadratureRule{refshape,T}(fill(T(NaN), length(dofcoords)), dofcoords) # weights will not be used + push!(qrs, qrf) + end + + n_boundary_entities = length(qrs) + n_qpoints = n_boundary_entities == 0 ? 0 : maximum(qr->length(getweights(qr)), qrs) # Bound number of qps correctly. + n_geom_basefuncs = getnbasefunctions(geom_interpol) + M = fill(zero(T) * T(NaN), n_geom_basefuncs, n_qpoints, n_boundary_entities) + nqp = zeros(Int,n_boundary_entities) + + for n_boundary_entity in 1:n_boundary_entities + for (qp, ξ) in enumerate(qrs[n_boundary_entity].points), i in 1:n_geom_basefuncs + M[i, qp, n_boundary_entity] = shape_value(geom_interpol, ξ, i) + end + nqp[n_boundary_entity] = length(qrs[n_boundary_entity].points) + end + + BCValues{T}(M, nqp, ScalarWrapper(0)) +end + +getnquadpoints(bcv::BCValues) = bcv.nqp[bcv.current_entity.x] +function spatial_coordinate(bcv::BCValues, q_point::Int, xh::AbstractVector{Vec{dim,T}}) where {dim,T} + n_base_funcs = size(bcv.M, 1) + length(xh) == n_base_funcs || throw_incompatible_coord_length(length(xh), n_base_funcs) + x = zero(Vec{dim,T}) + face = bcv.current_entity[] + @inbounds for i in 1:n_base_funcs + x += bcv.M[i,q_point,face] * xh[i] # geometric_value(fe_v, q_point, i) * xh[i] + end + return x +end + +include("face_values.jl") \ No newline at end of file diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index f11b9a76a0..eac205a62c 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -44,7 +44,7 @@ reinit! Return the number of quadrature points in `fv`s quadrature for the current (most recently [`reinit!`](@ref)ed) face. """ -getnquadpoints(fe::FaceValues) = getnquadpoints(fe.qr, fe.current_face[]) +function getnquadpoints end """ getdetJdV(fe_v::AbstractValues, q_point::Int) @@ -59,7 +59,7 @@ finite element cell or face as ``\\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`` """ -@propagate_inbounds getdetJdV(bv::FaceValues, q_point::Int) = bv.detJdV[q_point, bv.current_face[]] +#@propagate_inbounds getdetJdV(bv::FaceValues, q_point::Int) = bv.detJdV[q_point, bv.current_face[]] """ shape_value(fe_v::AbstractValues, q_point::Int, base_function::Int) @@ -67,9 +67,9 @@ finite element cell or face as Return the value of shape function `base_function` evaluated in quadrature point `q_point`. """ -@propagate_inbounds shape_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.N[base_func, q_point, bv.current_face[]] +#@propagate_inbounds shape_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.N[base_func, q_point, bv.current_face[]] -@propagate_inbounds geometric_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.M[base_func, q_point, bv.current_face[]] +#@propagate_inbounds geometric_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.M[base_func, q_point, bv.current_face[]] """ shape_gradient(fe_v::AbstractValues, q_point::Int, base_function::Int) @@ -77,7 +77,7 @@ quadrature point `q_point`. Return the gradient of shape function `base_function` evaluated in quadrature point `q_point`. """ -@propagate_inbounds shape_gradient(bv::FaceValues, q_point::Int, base_func::Int) = bv.dNdx[base_func, q_point, bv.current_face[]] +#@propagate_inbounds shape_gradient(bv::FaceValues, q_point::Int, base_func::Int) = bv.dNdx[base_func, q_point, bv.current_face[]] """ shape_symmetric_gradient(fe_v::AbstractValues, q_point::Int, base_function::Int) diff --git a/src/FEValues/face_values.jl b/src/FEValues/face_values.jl index c0dc14a3f1..342a5161d0 100644 --- a/src/FEValues/face_values.jl +++ b/src/FEValues/face_values.jl @@ -31,7 +31,7 @@ values of nodal functions, gradients and divergences of nodal functions etc. on """ FaceValues -struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: AbstractFaceValues +struct OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: AbstractFaceValues N::Array{N_t, 3} dNdx::Array{dNdx_t, 3} dNdξ::Array{dNdξ_t, 3} @@ -45,8 +45,8 @@ struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: Ab geo_interp::GIP end -# Common initializer code for constructing FaceValues after the types have been determined -function FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP}(qr::QR, ip::IP, gip::GIP) where { +# Common initializer code for constructing OldFaceValues after the types have been determined +function OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP}(qr::QR, ip::IP, gip::GIP) where { IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP, } @assert isconcretetype(IP) && isconcretetype(N_t) && isconcretetype(dNdx_t) && @@ -81,27 +81,27 @@ function FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP}(qr: detJdV = fill(T(NaN), max_n_qpoints, n_faces) - FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP}(N, dNdx, dNdξ, detJdV, normals, M, dMdξ, qr, ScalarWrapper(0), ip, gip) + OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP}(N, dNdx, dNdξ, detJdV, normals, M, dMdξ, qr, ScalarWrapper(0), ip, gip) end # Common entry point that fills in the numeric type and geometric interpolation -function FaceValues(qr::FaceQuadratureRule, ip::Interpolation, +function OldFaceValues(qr::FaceQuadratureRule, ip::Interpolation, gip::Interpolation = default_geometric_interpolation(ip)) - return FaceValues(Float64, qr, ip, gip) + return OldFaceValues(Float64, qr, ip, gip) end # Common entry point that fills in the geometric interpolation -function FaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation) where {T} - return FaceValues(T, qr, ip, default_geometric_interpolation(ip)) +function OldFaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation) where {T} + return OldFaceValues(T, qr, ip, default_geometric_interpolation(ip)) end # Common entry point that vectorizes an input scalar geometric interpolation -function FaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation, sgip::ScalarInterpolation) where {T} - return FaceValues(T, qr, ip, VectorizedInterpolation(sgip)) +function OldFaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation, sgip::ScalarInterpolation) where {T} + return OldFaceValues(T, qr, ip, VectorizedInterpolation(sgip)) end # Entrypoint for `ScalarInterpolation`s (rdim == sdim) -function FaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { +function OldFaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { dim, shape <: AbstractRefShape{dim}, T, QR <: FaceQuadratureRule{shape}, IP <: ScalarInterpolation{shape}, @@ -116,11 +116,11 @@ function FaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { # Geometry interpolation M_t = T dMdξ_t = Vec{dim, T} - return FaceValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, Normal_t, GIP}(qr, ip, gip.ip) + return OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, Normal_t, GIP}(qr, ip, gip.ip) end # Entrypoint for `VectorInterpolation`s (vdim == rdim == sdim) -function FaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { +function OldFaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { dim, shape <: AbstractRefShape{dim}, T, QR <: FaceQuadratureRule{shape}, IP <: VectorInterpolation{dim, shape}, @@ -135,10 +135,10 @@ function FaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { # Geometry interpolation M_t = T dMdξ_t = Vec{dim, T} - return FaceValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, Normal_t, GIP}(qr, ip, gip.ip) + return OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, Normal_t, GIP}(qr, ip, gip.ip) end -function reinit!(fv::FaceValues{<:Any, N_t, dNdx_t}, x::AbstractVector{Vec{dim,T}}, face::Int) where { +function reinit!(fv::OldFaceValues{<:Any, N_t, dNdx_t}, x::AbstractVector{Vec{dim,T}}, face::Int) where { dim, T, N_t <: Union{Number, Vec{dim}}, dNdx_t <: Union{Vec{dim}, Tensor{2,dim}} @@ -171,21 +171,21 @@ function reinit!(fv::FaceValues{<:Any, N_t, dNdx_t}, x::AbstractVector{Vec{dim,T end """ - getcurrentface(fv::FaceValues) + getcurrentface(fv::OldFaceValues) -Return the current active face of the `FaceValues` object (from last `reinit!`). +Return the current active face of the `OldFaceValues` object (from last `reinit!`). """ -getcurrentface(fv::FaceValues) = fv.current_face[] +getcurrentface(fv::OldFaceValues) = fv.current_face[] """ - getnormal(fv::FaceValues, qp::Int) + getnormal(fv::OldFaceValues, qp::Int) Return the normal at the quadrature point `qp` for the active face of the -`FaceValues` object(from last `reinit!`). +`OldFaceValues` object(from last `reinit!`). """ -getnormal(fv::FaceValues, qp::Int) = fv.normals[qp] - +getnormal(fv::OldFaceValues, qp::Int) = fv.normals[qp] +#= Moved to FaceValues.jl """ BCValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Union{Type{<:BoundaryIndex}}) @@ -250,3 +250,4 @@ function checkface(fv::FaceValues, face::Int) 0 < face <= nfaces(fv) || error("Face index out of range.") return nothing end +=# \ No newline at end of file diff --git a/src/Ferrite.jl b/src/Ferrite.jl index deabdc8406..62aad67ef0 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -83,7 +83,8 @@ include("Quadrature/quadrature.jl") # FEValues #include("FEValues/cell_values.jl") include("FEValues/CellValues.jl") -include("FEValues/face_values.jl") +#include("FEValues/face_values.jl") +include("FEValues/FaceValues.jl") include("PointEval/point_values.jl") include("FEValues/common_values.jl") include("FEValues/face_integrals.jl") From 744ba44896f6dfb20b25f91774351b9dc379f484 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 18 Jul 2023 15:07:55 +0200 Subject: [PATCH 007/172] Use get_x_values for facevalues --- src/FEValues/FaceValues.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 6b8becfd17..27f33e3de7 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -89,7 +89,7 @@ getcurrentface(fv::FaceValues) = fv.current_face[] Return the normal at the quadrature point `qp` for the active face of the `FaceValues` object(from last `reinit!`). """ -getnormal(fv::FaceValues, qp::Int) = getnormal(fv.geo_values[getcurrentface(fv)], qp) +getnormal(fv::FaceValues, qp::Int) = getnormal(get_geo_values(fv), qp) nfaces(fv::FaceValues) = length(fv.geo_values) @@ -102,11 +102,11 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int) wh @boundscheck checkface(fv, face_nr) n_geom_basefuncs = getngeobasefunctions(fv) length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) - @inbounds geo_values = fv.geo_values[face_nr] - @inbounds fun_values = fv.fun_values[face_nr] - - fv.current_face[] = face_nr + fv.current_face[] = face_nr + + geo_values = get_geo_values(fv) + fun_values = get_fun_values(fv) @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) Jinv = calculate_mapping(geo_values, face_nr, q_point, w, x) apply_mapping!(fun_values, q_point, Jinv) From 5cb7052f9c0b6664c5004bd51962d9edbc460cb1 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 11 Aug 2023 19:21:04 +0200 Subject: [PATCH 008/172] Initial work - some ex works, not all tests --- docs/Manifest.toml | 505 +++++++++++++++++++-------------- src/FEValues/CellValues.jl | 15 +- src/FEValues/FaceValues.jl | 66 ++--- src/FEValues/FunctionValues.jl | 39 ++- src/FEValues/GeometryValues.jl | 31 +- test/test_facevalues.jl | 1 + 6 files changed, 372 insertions(+), 285 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index e8cfd843f7..d798928845 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,9 +1,14 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.9.1" +julia_version = "1.9.2" manifest_format = "2.0" project_hash = "36aa80ebfd72e2016135d6d7b3122eb6efdc74ea" +[[deps.ADTypes]] +git-tree-sha1 = "f5c25e8a5b29b5e941b7408bc8cc79fea4d9ef9a" +uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" +version = "0.1.6" + [[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" @@ -16,9 +21,9 @@ version = "0.4.4" [[deps.Adapt]] deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "cc37d689f599e8df4f464b2fa3870ff7db7492ef" +git-tree-sha1 = "76289dc51920fdc6e0013c872ba9551d54961c24" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "3.6.1" +version = "3.6.2" weakdeps = ["StaticArrays"] [deps.Adapt.extensions] @@ -35,10 +40,10 @@ uuid = "ec485272-7323-5ecc-a04f-4719b315124d" version = "0.2.0" [[deps.ArrayInterface]] -deps = ["Adapt", "LinearAlgebra", "Requires", "SnoopPrecompile", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "38911c7737e123b28182d89027f4216cfc8a9da7" +deps = ["Adapt", "LinearAlgebra", "Requires", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "f83ec24f76d4c8f525099b2ac475fc098138ec31" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.4.3" +version = "7.4.11" [deps.ArrayInterface.extensions] ArrayInterfaceBandedMatricesExt = "BandedMatrices" @@ -64,9 +69,9 @@ version = "0.1.29" [[deps.ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "4efc22e4c299e49995a38d503d9dbb0544a37838" +git-tree-sha1 = "6189f7819e6345bcc097331c7db571f2f211364f" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.0.4" +version = "1.1.1" [[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" @@ -87,9 +92,9 @@ version = "0.1.5" [[deps.BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "c7d7789c2c6ec98ec78ea5e017485549a95b051e" +git-tree-sha1 = "174b4970af15a500a29e76151f5c53195784b9d4" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "0.16.27" +version = "0.16.36" [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -98,10 +103,10 @@ uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" version = "1.0.8+0" [[deps.CPUSummary]] -deps = ["CpuId", "IfElse", "Static"] -git-tree-sha1 = "2c144ddb46b552f72d7eafe7cc2f50746e41ea21" +deps = ["CpuId", "IfElse", "PrecompileTools", "Static"] +git-tree-sha1 = "89e0654ed8c7aebad6d5ad235d6242c2d737a928" uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" -version = "0.2.2" +version = "0.2.3" [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -123,15 +128,15 @@ version = "0.1.12" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83" +git-tree-sha1 = "02aa26a4cf76381be7f66e020a3eddeb27b0a092" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.1" +version = "0.7.2" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "be6ab11021cd29f0344d5c4357b163af05a48cba" +git-tree-sha1 = "d9a8f86737b665e15a9641ecbac64deef9ce6724" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.21.0" +version = "3.23.0" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] @@ -140,10 +145,14 @@ uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" version = "0.11.4" [[deps.ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"] -git-tree-sha1 = "600cc5508d66b78aae350f7accdb58763ac18589" +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.9.10" +version = "0.10.0" +weakdeps = ["SpecialFunctions"] + + [deps.ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] @@ -152,9 +161,9 @@ uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" version = "0.12.10" [[deps.CommonSolve]] -git-tree-sha1 = "9441451ee712d1aec22edad62db1a9af3dc8d852" +git-tree-sha1 = "0eee5eb66b1cf62cd6ad1b460238e60e4b09400c" uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" -version = "0.2.3" +version = "0.2.4" [[deps.CommonSubexpressions]] deps = ["MacroTools", "Test"] @@ -164,9 +173,9 @@ version = "0.3.0" [[deps.Compat]] deps = ["UUIDs"] -git-tree-sha1 = "7a60c856b9fa189eb34f5f8a6f6b5529b7942957" +git-tree-sha1 = "e460f044ca8b99be31d35fe54fc33a5c33dd8ed7" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.6.1" +version = "4.9.0" weakdeps = ["Dates", "LinearAlgebra"] [deps.Compat.extensions] @@ -175,19 +184,19 @@ weakdeps = ["Dates", "LinearAlgebra"] [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.0.2+0" +version = "1.0.5+0" [[deps.ConcurrentUtilities]] deps = ["Serialization", "Sockets"] -git-tree-sha1 = "b306df2650947e9eb100ec125ff8c65ca2053d30" +git-tree-sha1 = "5372dbbf8f0bdb8c700db5367132925c0771ef7e" uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.1.1" +version = "2.2.1" [[deps.ConstructionBase]] deps = ["LinearAlgebra"] -git-tree-sha1 = "738fec4d684a9a6ee9598a8bfee305b26831f28c" +git-tree-sha1 = "fe2838a593b5f776e1597e086dcd47560d94e816" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.2" +version = "1.5.3" [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" @@ -209,15 +218,15 @@ uuid = "adafc99b-e345-5852-983c-f28acb93d879" version = "0.3.1" [[deps.DataAPI]] -git-tree-sha1 = "e8119c1a33d267e16108be441a287a6981ba1630" +git-tree-sha1 = "8da84edb865b0b5b0100c0666a9bc9a0b71c553c" uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.14.0" +version = "1.15.0" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "d1fff3a548102f48987a52a2e0d114fa97d730f0" +git-tree-sha1 = "3dbd312d370723b6bb43ba9d02fc36abade4518d" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.13" +version = "0.18.15" [[deps.DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" @@ -235,10 +244,10 @@ uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" version = "1.9.1" [[deps.DiffEqBase]] -deps = ["ArrayInterface", "ChainRulesCore", "DataStructures", "DocStringExtensions", "EnumX", "FastBroadcast", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "Printf", "RecursiveArrayTools", "Reexport", "Requires", "SciMLBase", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces", "ZygoteRules"] -git-tree-sha1 = "ed1108bd9a68977d5e0cbd8b2882293337c15f1c" +deps = ["ArrayInterface", "ChainRulesCore", "DataStructures", "DocStringExtensions", "EnumX", "FastBroadcast", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "Printf", "RecursiveArrayTools", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces", "ZygoteRules"] +git-tree-sha1 = "ed586656058844e48660c6d6fdb384e83afc50db" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.124.0" +version = "6.128.0" [deps.DiffEqBase.extensions] DiffEqBaseDistributionsExt = "Distributions" @@ -270,15 +279,19 @@ version = "1.1.0" [[deps.DiffRules]] deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] -git-tree-sha1 = "a4ad7ef19d2cdc2eff57abbbe68032b1cd0bd8f8" +git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" -version = "1.13.0" +version = "1.15.1" [[deps.Distances]] -deps = ["LinearAlgebra", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "49eba9ad9f7ead780bfb7ee319f962c811c6d3b2" +deps = ["LinearAlgebra", "Statistics", "StatsAPI"] +git-tree-sha1 = "b6def76ffad15143924a2199f72a5cd883a2e8a9" uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.8" +version = "0.10.9" +weakdeps = ["SparseArrays"] + + [deps.Distances.extensions] + DistancesSparseArraysExt = "SparseArrays" [[deps.Distributed]] deps = ["Random", "Serialization", "Sockets"] @@ -291,8 +304,8 @@ uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" version = "0.9.3" [[deps.Documenter]] -deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "REPL", "SnoopPrecompile", "Test", "Unicode"] -git-tree-sha1 = "2afe1f1706b90fd4a8593d70b5324d04ddefed69" +deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "PrecompileTools", "REPL", "Test", "Unicode"] +git-tree-sha1 = "34e4566ad9f151fcc9a9a9c1868b2dc865fa195c" repo-rev = "master" repo-url = "https://github.com/JuliaDocs/Documenter.jl.git" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" @@ -308,11 +321,17 @@ git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.4" +[[deps.ExceptionUnwrapping]] +deps = ["Test"] +git-tree-sha1 = "e90caa41f5a86296e014e148ee061bd6c3edec96" +uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" +version = "0.1.9" + [[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bad72f730e9e91c08d9427d5e8db95478a3c323d" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.4.8+0" +version = "2.5.0+0" [[deps.ExponentialUtilities]] deps = ["Adapt", "ArrayInterface", "GPUArraysCore", "GenericSchur", "LinearAlgebra", "Printf", "SnoopPrecompile", "SparseArrays", "libblastrampoline_jll"] @@ -321,9 +340,9 @@ uuid = "d4d017d3-3776-5f7e-afef-a10c40355c18" version = "1.24.0" [[deps.ExprTools]] -git-tree-sha1 = "c1d06d129da9f55715c6c212866f5b1bddc5fa00" +git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" -version = "0.1.9" +version = "0.1.10" [[deps.FFMPEG]] deps = ["FFMPEG_jll"] @@ -345,9 +364,9 @@ version = "1.3.8+0" [[deps.FastBroadcast]] deps = ["ArrayInterface", "LinearAlgebra", "Polyester", "Static", "StaticArrayInterface", "StrideArraysCore"] -git-tree-sha1 = "d1248fceea0b26493fd33e8e9e8c553270da03bd" +git-tree-sha1 = "aa9925a229d45fe3018715238956766fa21804d1" uuid = "7034ab61-46d4-4ed7-9d0f-46aef9175898" -version = "0.2.5" +version = "0.2.6" [[deps.FastClosures]] git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" @@ -356,9 +375,9 @@ version = "0.3.2" [[deps.FastLapackInterface]] deps = ["LinearAlgebra"] -git-tree-sha1 = "c1293a93193f0ae94be7cf338d33e162c39d8788" +git-tree-sha1 = "b12f05108e405dadcc2aff0008db7f831374e051" uuid = "29a986be-02c6-4525-aec4-84b980013641" -version = "1.2.9" +version = "2.0.0" [[deps.Ferrite]] deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] @@ -376,9 +395,9 @@ version = "0.3.14" [[deps.FerriteGmsh]] deps = ["Ferrite", "Gmsh"] -git-tree-sha1 = "ab52b403356f18bbf0a533a7e8f26c86a8da70f6" +git-tree-sha1 = "702427f9f6b2d3e39da3bfab4eea7d02f459e404" uuid = "4f95f4f8-b27c-4ae5-9a39-ea55e634e36b" -version = "1.0.0" +version = "1.0.1" [[deps.FerriteMeshParser]] deps = ["Ferrite"] @@ -391,15 +410,25 @@ uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[deps.FillArrays]] deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"] -git-tree-sha1 = "fc86b4fd3eff76c3ce4f5e96e2fdfa6282722885" +git-tree-sha1 = "f372472e8672b1d993e93dada09e23139b509f9e" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.0.0" +version = "1.5.0" [[deps.FiniteDiff]] -deps = ["ArrayInterface", "LinearAlgebra", "Requires", "Setfield", "SparseArrays", "StaticArrays"] -git-tree-sha1 = "6604e18a0220650dbbea7854938768f15955dd8e" +deps = ["ArrayInterface", "LinearAlgebra", "Requires", "Setfield", "SparseArrays"] +git-tree-sha1 = "c6e4a1fbe73b31a3dea94b1da449503b8830c306" uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.20.0" +version = "2.21.1" + + [deps.FiniteDiff.extensions] + FiniteDiffBandedMatricesExt = "BandedMatrices" + FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" + FiniteDiffStaticArraysExt = "StaticArrays" + + [deps.FiniteDiff.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" [[deps.FixedPointNumbers]] deps = ["Statistics"] @@ -421,19 +450,19 @@ version = "0.4.2" [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] -git-tree-sha1 = "00e252f4d706b3d55a8863432e742bf5717b498d" +git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.35" +version = "0.10.36" weakdeps = ["StaticArrays"] [deps.ForwardDiff.extensions] ForwardDiffStaticArraysExt = "StaticArrays" [[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "87eb71354d8ec1a96d4a7636bd57a7347dde3ef9" +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.10.4+0" +version = "2.13.1+0" [[deps.FriBidi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -475,21 +504,21 @@ version = "6.2.1+2" [[deps.GPUArraysCore]] deps = ["Adapt"] -git-tree-sha1 = "1cd7f0af1aa58abc02ea1d872953a97359cb87fa" +git-tree-sha1 = "2d6ca471a6c7b536127afccfa7564b5b39227fe0" uuid = "46192b85-c4d5-4398-a991-12ede77f4527" -version = "0.1.4" +version = "0.1.5" [[deps.GR]] deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "UUIDs", "p7zip_jll"] -git-tree-sha1 = "efaac003187ccc71ace6c755b197284cd4811bfe" +git-tree-sha1 = "d73afa4a2bb9de56077242d98cf763074ab9a970" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.72.4" +version = "0.72.9" [[deps.GR_jll]] -deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt5Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "4486ff47de4c18cb511a0da420efebb314556316" +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 = "1596bab77f4f073a14c62424283e7ebff3072eca" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.72.4+0" +version = "0.72.9+1" [[deps.GenericSchur]] deps = ["LinearAlgebra", "Printf"] @@ -539,10 +568,10 @@ uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" version = "1.12.2+2" [[deps.HTTP]] -deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "69182f9a2d6add3736b7a06ab6416aafdeec2196" +deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] +git-tree-sha1 = "cb56ccdd481c0dd7f975ad2b3b62d9eda088f7e2" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.8.0" +version = "1.9.14" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] @@ -552,15 +581,15 @@ version = "2.8.1+1" [[deps.HostCPUFeatures]] deps = ["BitTwiddlingConvenienceFunctions", "IfElse", "Libdl", "Static"] -git-tree-sha1 = "734fd90dd2f920a2f1921d5388dcebe805b262dc" +git-tree-sha1 = "eb8fed28f4994600e29beef49744639d985a04b2" uuid = "3e5b6fbb-0976-4d2c-9146-d79de83f2fb0" -version = "0.1.14" +version = "0.1.16" [[deps.IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" +git-tree-sha1 = "d75853a0bdbfb1ac815478bacd89cd27b550ace6" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.2" +version = "0.2.3" [[deps.IfElse]] git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" @@ -624,15 +653,9 @@ version = "0.4.0" [[deps.Krylov]] deps = ["LinearAlgebra", "Printf", "SparseArrays"] -git-tree-sha1 = "dd90aacbfb622f898a97c2a4411ac49101ebab8a" +git-tree-sha1 = "fbda7c58464204d92f3b158578fb0b3d4224cea5" uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" -version = "0.9.0" - -[[deps.KrylovKit]] -deps = ["ChainRulesCore", "GPUArraysCore", "LinearAlgebra", "Printf"] -git-tree-sha1 = "1a5e1d9941c783b0119897d29f2eb665d876ecf3" -uuid = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" -version = "0.6.0" +version = "0.9.3" [[deps.LAME_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -665,9 +688,9 @@ version = "1.3.0" [[deps.Latexify]] deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Printf", "Requires"] -git-tree-sha1 = "099e356f267354f46ba65087981a77da23a279b7" +git-tree-sha1 = "f428ae552340899a935973270b8d98e5a31c49fe" uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.0" +version = "0.16.1" [deps.Latexify.extensions] DataFramesExt = "DataFrames" @@ -748,10 +771,10 @@ uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" version = "2.35.0+0" [[deps.Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "3eb79b0ca5764d4799c06699573fd8f533259713" +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "2da088d113af58221c52828a80378e16be7d037a" uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.4.0+0" +version = "4.5.1+1" [[deps.Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -782,34 +805,44 @@ uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" version = "5.0.0+0" [[deps.LinearSolve]] -deps = ["ArrayInterface", "DocStringExtensions", "EnumX", "FastLapackInterface", "GPUArraysCore", "IterativeSolvers", "KLU", "Krylov", "KrylovKit", "LinearAlgebra", "Preferences", "RecursiveFactorization", "Reexport", "SciMLBase", "SciMLOperators", "Setfield", "SnoopPrecompile", "SparseArrays", "Sparspak", "SuiteSparse", "UnPack"] -git-tree-sha1 = "4a4f8cc7a59fadbb02d1852d1e0cef5dca3a9460" +deps = ["ArrayInterface", "DocStringExtensions", "EnumX", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "Libdl", "LinearAlgebra", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "SuiteSparse", "UnPack"] +git-tree-sha1 = "f746a5b9522815bf098049f9cbfbfcae53f29450" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "1.42.0" +version = "2.5.0" [deps.LinearSolve.extensions] - LinearSolveHYPRE = "HYPRE" + LinearSolveCUDAExt = "CUDA" + LinearSolveHYPREExt = "HYPRE" + LinearSolveIterativeSolversExt = "IterativeSolvers" + LinearSolveKrylovKitExt = "KrylovKit" + LinearSolveMKLExt = "MKL_jll" + LinearSolvePardisoExt = "Pardiso" [deps.LinearSolve.weakdeps] + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771" + IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" + KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" + MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7" + Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" [[deps.Literate]] deps = ["Base64", "IOCapture", "JSON", "REPL"] -git-tree-sha1 = "1c4418beaa6664041e0f9b48f0710f57bff2fcbe" +git-tree-sha1 = "a1a0d4ff9f785a2184baca7a5c89e664f144143d" uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306" -version = "2.14.0" +version = "2.14.1" [[deps.LiveServer]] -deps = ["HTTP", "MIMEs", "Pkg", "Sockets"] -git-tree-sha1 = "494010f12dd98fa0d558cb7679b7c21d3a0a430a" +deps = ["HTTP", "LoggingExtras", "MIMEs", "Pkg", "Sockets", "Test"] +git-tree-sha1 = "24d05efe53436b22a42bf2ae459f47c48b0c2603" uuid = "16fef848-5104-11e9-1b77-fb7a48bbb589" -version = "1.2.0" +version = "1.2.7" [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "0a1b7c2863e44523180fdb3146534e265a91870b" +git-tree-sha1 = "c3ce8e7420b3a6e071e0fe4745f5d4300e37b13f" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.23" +version = "0.3.24" [deps.LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -832,9 +865,9 @@ version = "1.0.0" [[deps.LoopVectorization]] deps = ["ArrayInterface", "ArrayInterfaceCore", "CPUSummary", "CloseOpenIntervals", "DocStringExtensions", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "PrecompileTools", "SIMDTypes", "SLEEFPirates", "Static", "StaticArrayInterface", "ThreadingUtilities", "UnPack", "VectorizationBase"] -git-tree-sha1 = "e7ce3cdc520da8135e73d7cb303e0617a19f582b" +git-tree-sha1 = "c88a4afe1703d731b1c4fdf4e3c7e77e3b176ea2" uuid = "bdcacae8-1622-11e9-2a5c-532679323890" -version = "0.12.158" +version = "0.12.165" weakdeps = ["ChainRulesCore", "ForwardDiff", "SpecialFunctions"] [deps.LoopVectorization.extensions] @@ -942,10 +975,10 @@ uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" [[deps.NonlinearSolve]] -deps = ["ArrayInterface", "DiffEqBase", "EnumX", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SnoopPrecompile", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "a6000c813371cd3cd9cbbdf8a356fc3a97138d92" +deps = ["ArrayInterface", "DiffEqBase", "EnumX", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "PrecompileTools", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "UnPack"] +git-tree-sha1 = "23dabe80f8ebec9a68b0db4cd02f2d2cdbc4f653" uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" -version = "1.6.0" +version = "1.9.0" [[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"] @@ -955,9 +988,9 @@ version = "7.6.2+2" [[deps.OffsetArrays]] deps = ["Adapt"] -git-tree-sha1 = "82d7c9e310fe55aa54996e6f7f94674e2a38fcb4" +git-tree-sha1 = "2ac17d29c523ce1cd38e27785a7d23024853a4bb" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.12.9" +version = "1.12.10" [[deps.Ogg_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -982,10 +1015,10 @@ uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" version = "1.4.1" [[deps.OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9ff31d101d987eb9d66bd8b176ac7c277beccd09" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "bbb5c2115d63c2f1451cb70e5ef75e8fe4707019" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "1.1.20+0" +version = "1.1.22+0" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -995,9 +1028,9 @@ version = "0.5.5+0" [[deps.Optim]] deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "PositiveFactorizations", "Printf", "SparseArrays", "StatsBase"] -git-tree-sha1 = "a89b11f0f354f06099e4001c151dffad7ebab015" +git-tree-sha1 = "e3a6546c1577bfd701771b477b794a52949e7594" uuid = "429524aa-4258-5aef-a3af-852621145aeb" -version = "1.7.5" +version = "1.7.6" [[deps.Opus_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -1006,21 +1039,27 @@ uuid = "91d4177d-7536-5919-b921-800302f37372" version = "1.3.2+0" [[deps.OrderedCollections]] -git-tree-sha1 = "d321bf2de576bf25ec4d3e4360faca399afca282" +git-tree-sha1 = "2e73fe17cac3c62ad1aebe70d44c963c3cfdc3e3" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.0" +version = "1.6.2" [[deps.OrdinaryDiffEq]] -deps = ["Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "IfElse", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "LoopVectorization", "MacroTools", "MuladdMacro", "NLsolve", "NonlinearSolve", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLNLSolve", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "47fc5cf4174a7d45fa541669abc5405d9ef6b8df" +deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "IfElse", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "LoopVectorization", "MacroTools", "MuladdMacro", "NLsolve", "NonlinearSolve", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLNLSolve", "SciMLOperators", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "47c560dcb059570bdbd9f887a6b8958190e498a4" uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -version = "6.51.1" +version = "6.53.4" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+0" +[[deps.PackageExtensionCompat]] +git-tree-sha1 = "f9b1e033c2b1205cf30fd119f4e50881316c1923" +uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" +version = "1.0.1" +weakdeps = ["Requires", "TOML"] + [[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" @@ -1028,10 +1067,10 @@ uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" version = "0.12.3" [[deps.Parsers]] -deps = ["Dates", "SnoopPrecompile"] -git-tree-sha1 = "478ac6c952fddd4399e71d4779797c538d0ff2bf" +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "716e24b21538abc91f6205fd1d8363f39b442851" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.5.8" +version = "2.7.2" [[deps.Pipe]] git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" @@ -1039,15 +1078,15 @@ uuid = "b98c9c47-44ae-5843-9183-064241ee97a0" version = "1.3.0" [[deps.Pixman_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87" uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.40.1+0" +version = "0.42.2+0" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.9.0" +version = "1.9.2" [[deps.PlotThemes]] deps = ["PlotUtils", "Statistics"] @@ -1062,10 +1101,10 @@ uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" version = "1.3.5" [[deps.Plots]] -deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Preferences", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "Unzip"] -git-tree-sha1 = "6c7f47fd112001fc95ea1569c2757dffd9e81328" +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Preferences", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "UnitfulLatexify", "Unzip"] +git-tree-sha1 = "9f8675a55b37a70aa23177ec110f6e3f4dd68466" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.38.11" +version = "1.38.17" [deps.Plots.extensions] FileIOExt = "FileIO" @@ -1083,9 +1122,9 @@ version = "1.38.11" [[deps.Polyester]] deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Requires", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] -git-tree-sha1 = "0fe4e7c4d8ff4c70bfa507f0dd96fa161b115777" +git-tree-sha1 = "3d811babe092a6e7b130beee84998fe7663348b6" uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" -version = "0.7.3" +version = "0.7.5" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] @@ -1113,9 +1152,9 @@ version = "0.4.12" [[deps.PrecompileTools]] deps = ["Preferences"] -git-tree-sha1 = "259e206946c293698122f63e2b513a7c99a244e8" +git-tree-sha1 = "9673d39decc5feece56ef3940e5dafba15ba0f81" uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.1.1" +version = "1.1.2" [[deps.Preferences]] deps = ["TOML"] @@ -1133,11 +1172,11 @@ git-tree-sha1 = "d7a7aef8f8f2d537104f170139553b14dfe39fe9" uuid = "92933f4c-e287-5a05-a399-4b506db050ca" version = "1.7.2" -[[deps.Qt5Base_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] -git-tree-sha1 = "0c03844e2231e12fda4d0086fd7cbe4098ee8dc5" -uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1" -version = "5.15.3+2" +[[deps.Qt6Base_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] +git-tree-sha1 = "364898e8f13f7eaaceec55fd3d08680498c0aa6e" +uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" +version = "6.4.2+3" [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] @@ -1161,9 +1200,9 @@ version = "0.6.12" [[deps.RecursiveArrayTools]] deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "Requires", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "68078e9fa9130a6a768815c48002d0921a232c11" +git-tree-sha1 = "7ed35fb5f831aaf09c2d7c8736d44667a1afdcb0" uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" -version = "2.38.4" +version = "2.38.7" [deps.RecursiveArrayTools.extensions] RecursiveArrayToolsMeasurementsExt = "Measurements" @@ -1176,10 +1215,10 @@ version = "2.38.4" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.RecursiveFactorization]] -deps = ["LinearAlgebra", "LoopVectorization", "Polyester", "SnoopPrecompile", "StrideArraysCore", "TriangularSolve"] -git-tree-sha1 = "9088515ad915c99026beb5436d0a09cd8c18163e" +deps = ["LinearAlgebra", "LoopVectorization", "Polyester", "PrecompileTools", "StrideArraysCore", "TriangularSolve"] +git-tree-sha1 = "2b6d4a40339aa02655b1743f4cd7c03109f520c1" uuid = "f2c3362d-daeb-58d1-803e-2bc74f2840b4" -version = "0.2.18" +version = "0.2.20" [[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" @@ -1200,9 +1239,9 @@ version = "1.3.0" [[deps.RuntimeGeneratedFunctions]] deps = ["ExprTools", "SHA", "Serialization"] -git-tree-sha1 = "d7d9ebe28062161c1e314ed643097b0c6fe657d9" +git-tree-sha1 = "6aacc5eefe8415f47b3e34214c1d79d2674a0ba2" uuid = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" -version = "0.5.7" +version = "0.5.12" [[deps.SCOTCH_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] @@ -1215,10 +1254,10 @@ uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" version = "0.7.0" [[deps.SIMD]] -deps = ["SnoopPrecompile"] -git-tree-sha1 = "8b20084a97b004588125caebf418d8cab9e393d1" +deps = ["PrecompileTools"] +git-tree-sha1 = "0e270732477b9e551d884e6b07e23bb2ec947790" uuid = "fdea26ae-647d-5447-a871-4b548cad5224" -version = "3.4.4" +version = "3.4.5" [[deps.SIMDTypes]] git-tree-sha1 = "330289636fb8107c5f32088d2741e9fd7a061a5c" @@ -1227,27 +1266,27 @@ version = "0.1.0" [[deps.SLEEFPirates]] deps = ["IfElse", "Static", "VectorizationBase"] -git-tree-sha1 = "cda0aece8080e992f6370491b08ef3909d1c04e7" +git-tree-sha1 = "4b8586aece42bee682399c4c4aee95446aa5cd19" uuid = "476501e8-09a2-5ece-8869-fb82de89a1fa" -version = "0.6.38" +version = "0.6.39" [[deps.SciMLBase]] -deps = ["ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "Preferences", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "SnoopPrecompile", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables", "TruncatedStacktraces"] -git-tree-sha1 = "392d3e28b05984496af37100ded94dc46fa6c8de" +deps = ["ADTypes", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables", "TruncatedStacktraces"] +git-tree-sha1 = "04370090604cd399db5bebddb636d80ab9d338e9" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "1.91.7" +version = "1.94.0" [[deps.SciMLNLSolve]] deps = ["DiffEqBase", "LineSearches", "NLsolve", "Reexport", "SciMLBase"] -git-tree-sha1 = "a8eb97c56cac50c21096582afb2a0110784dc36e" +git-tree-sha1 = "9dfc8e9e3d58c0c74f1a821c762b5349da13eccf" uuid = "e9a6253c-8580-4d32-9898-8661bb511710" -version = "0.1.6" +version = "0.1.8" [[deps.SciMLOperators]] deps = ["ArrayInterface", "DocStringExtensions", "Lazy", "LinearAlgebra", "Setfield", "SparseArrays", "StaticArraysCore", "Tricks"] -git-tree-sha1 = "5950ad7bec86ba22e4861db61d031625a26a9ec3" +git-tree-sha1 = "65c2e6ced6f62ea796af251eb292a0e131a3613b" uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" -version = "0.2.3" +version = "0.3.6" [[deps.Scratch]] deps = ["Dates"] @@ -1280,13 +1319,13 @@ uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" version = "1.1.0" [[deps.SimpleNonlinearSolve]] -deps = ["ArrayInterface", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "Reexport", "Requires", "SciMLBase", "SnoopPrecompile", "StaticArraysCore"] -git-tree-sha1 = "54c78ac3cc0343a16785adabe5bbf4063c737967" +deps = ["ArrayInterface", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "PackageExtensionCompat", "PrecompileTools", "Reexport", "SciMLBase", "StaticArraysCore"] +git-tree-sha1 = "20aa9831d654bab67ed561e78917047143ecb9bf" uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "0.1.14" +version = "0.1.19" [deps.SimpleNonlinearSolve.extensions] - SimpleBatchedNonlinearSolveExt = "NNlib" + SimpleNonlinearSolveNNlibExt = "NNlib" [deps.SimpleNonlinearSolve.weakdeps] NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" @@ -1313,19 +1352,25 @@ uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[deps.SortingAlgorithms]] deps = ["DataStructures"] -git-tree-sha1 = "a4ada03f999bd01b3a25dcaa30b2d929fe537e00" +git-tree-sha1 = "c60ec5c62180f27efea3ba2908480f8055e17cee" uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.1.0" +version = "1.1.1" [[deps.SparseArrays]] deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[deps.SparseDiffTools]] -deps = ["Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "Requires", "SparseArrays", "StaticArrays", "VertexSafeGraphs"] -git-tree-sha1 = "e19ac47477c9a8fcca06dab5e5471417d5d9d723" +deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "Reexport", "Requires", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "Tricks", "VertexSafeGraphs"] +git-tree-sha1 = "4c1a57bcbc0b795fbfdc2009e70f9c2fd2815bfe" uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "1.31.0" +version = "2.4.1" + + [deps.SparseDiffTools.extensions] + SparseDiffToolsZygoteExt = "Zygote" + + [deps.SparseDiffTools.weakdeps] + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.Sparspak]] deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] @@ -1335,9 +1380,9 @@ version = "0.3.9" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "ef28127915f4229c971eb43f3fc075dd3fe91880" +git-tree-sha1 = "7beb031cf8145577fbccacd94b8a8f4ce78428d3" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.2.0" +version = "2.3.0" weakdeps = ["ChainRulesCore"] [deps.SpecialFunctions.extensions] @@ -1345,9 +1390,9 @@ weakdeps = ["ChainRulesCore"] [[deps.Static]] deps = ["IfElse"] -git-tree-sha1 = "08be5ee09a7632c32695d954a602df96a877bf0d" +git-tree-sha1 = "f295e0a1da4ca425659c57441bcb59abb035a4bc" uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" -version = "0.8.6" +version = "0.8.8" [[deps.StaticArrayInterface]] deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "Requires", "SnoopPrecompile", "SparseArrays", "Static", "SuiteSparse"] @@ -1361,15 +1406,19 @@ weakdeps = ["OffsetArrays", "StaticArrays"] StaticArrayInterfaceStaticArraysExt = "StaticArrays" [[deps.StaticArrays]] -deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"] -git-tree-sha1 = "c262c8e978048c2b095be1672c9bee55b4619521" +deps = ["LinearAlgebra", "Random", "StaticArraysCore"] +git-tree-sha1 = "9cabadf6e7cd2349b6cf49f1915ad2028d65e881" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.5.24" +version = "1.6.2" +weakdeps = ["Statistics"] + + [deps.StaticArrays.extensions] + StaticArraysStatisticsExt = "Statistics" [[deps.StaticArraysCore]] -git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a" +git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.0" +version = "1.4.2" [[deps.Statistics]] deps = ["LinearAlgebra", "SparseArrays"] @@ -1384,15 +1433,15 @@ version = "1.6.0" [[deps.StatsBase]] deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "d1bf48bfcc554a3761a133fe3a9bb01488e06916" +git-tree-sha1 = "75ebe04c5bed70b91614d684259b661c9e6274a4" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.33.21" +version = "0.34.0" [[deps.StrideArraysCore]] deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] -git-tree-sha1 = "b3e9c174a9df77ed7b66fc0aa605def3351a0653" +git-tree-sha1 = "f02eb61eb5c97b48c153861c72fbbfdddc607e06" uuid = "7792a7ef-975c-4747-a70f-980b88e8d1da" -version = "0.4.13" +version = "0.4.17" [[deps.SuiteSparse]] deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] @@ -1438,10 +1487,10 @@ uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" version = "0.1.1" [[deps.Tensors]] -deps = ["ForwardDiff", "LinearAlgebra", "SIMD", "SnoopPrecompile", "StaticArrays", "Statistics"] -git-tree-sha1 = "71f054343e85ab1eab12bf8336004309002ff82d" +deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] +git-tree-sha1 = "bcbb366323add300742c9e4a5447e584640aeff2" uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" -version = "1.13.1" +version = "1.15.0" [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] @@ -1449,9 +1498,9 @@ uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [[deps.ThreadingUtilities]] deps = ["ManualMemory"] -git-tree-sha1 = "c97f60dd4f2331e1a495527f80d242501d2f9865" +git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" -version = "0.5.1" +version = "0.5.2" [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] @@ -1478,14 +1527,14 @@ version = "0.1.7" [[deps.TruncatedStacktraces]] deps = ["InteractiveUtils", "MacroTools", "Preferences"] -git-tree-sha1 = "7bc1632a4eafbe9bd94cf1a784a9a4eb5e040a91" +git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" uuid = "781d530d-4396-4725-bb49-402e4bee1e77" -version = "1.3.0" +version = "1.4.0" [[deps.URIs]] -git-tree-sha1 = "074f993b0ca030848b897beff716d93aca60f06a" +git-tree-sha1 = "b7a5e99f24892b6824a954199a45e9ffcc1c70f0" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.4.2" +version = "1.5.0" [[deps.UUIDs]] deps = ["Random", "SHA"] @@ -1505,6 +1554,26 @@ git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" version = "0.4.1" +[[deps.Unitful]] +deps = ["Dates", "LinearAlgebra", "Random"] +git-tree-sha1 = "64eb17acef1d9734cf09967539818f38093d9b35" +uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" +version = "1.16.2" + + [deps.Unitful.extensions] + ConstructionBaseUnitfulExt = "ConstructionBase" + InverseFunctionsUnitfulExt = "InverseFunctions" + + [deps.Unitful.weakdeps] + ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.UnitfulLatexify]] +deps = ["LaTeXStrings", "Latexify", "Unitful"] +git-tree-sha1 = "e2d817cc500e960fdbafcf988ac8436ba3208bfd" +uuid = "45397f5d-5981-4c77-b2b3-fc36d6e9b728" +version = "1.6.3" + [[deps.Unzip]] git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" @@ -1557,17 +1626,23 @@ git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" version = "1.1.34+0" +[[deps.XZ_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "cf2c7de82431ca6f39250d2fc4aacd0daa1675c0" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.4.4+0" + [[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.6.9+4" +version = "1.8.6+0" [[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.9+4" +version = "1.0.11+0" [[deps.Xorg_libXcursor_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] @@ -1576,10 +1651,10 @@ uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" version = "1.2.0+4" [[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.3+4" +version = "1.1.4+0" [[deps.Xorg_libXext_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] @@ -1624,22 +1699,22 @@ uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" version = "0.9.10+4" [[deps.Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.0+3" +version = "0.1.1+0" [[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6" +deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "b4bfde5d5b652e22b9c790ad00af08b6d042b97d" uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.13.0+3" +version = "1.15.0+0" [[deps.Xorg_libxkbfile_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "730eeca102434283c50ccf7d1ecdadf521a765a4" uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" -version = "1.1.0+4" +version = "1.1.2+0" [[deps.Xorg_xcb_util_image_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] @@ -1672,22 +1747,22 @@ uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" version = "0.4.1+1" [[deps.Xorg_xkbcomp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"] -git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] +git-tree-sha1 = "330f955bc41bb8f5270a369c473fc4a5a4e4d3cb" uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" -version = "1.4.2+4" +version = "1.4.6+0" [[deps.Xorg_xkeyboard_config_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"] -git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] +git-tree-sha1 = "691634e5453ad362044e2ad653e79f3ee3bb98c3" uuid = "33bec58e-1273-512f-9401-5d533626f822" -version = "2.27.0+4" +version = "2.39.0+0" [[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.4.0+3" +version = "1.5.0+0" [[deps.Zlib_jll]] deps = ["Libdl"] diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index c565e40521..bdb6c5bdad 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -1,3 +1,4 @@ + include("GeometryValues.jl") include("FunctionValues.jl") @@ -7,13 +8,15 @@ end struct CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCellValues geo_values::GeometryValues{dMdξ_t, GIP, T} + detJdV::Vector{T} fun_values::FunctionValues{IP, N_t, dNdx_t, dNdξ_t} qr::QR end function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T geo_values = GeometryValues(T, ip_geo.ip, qr) fun_values = FunctionValues(T, ip_fun, qr, ip_geo) - return CellValues(geo_values, fun_values, qr) + detJdV = fill(T(NaN), length(getweights(qr))) + return CellValues(geo_values, detJdV, fun_values, qr) end CellValues(qr::QuadratureRule, ip::Interpolation, args...) = CellValues(Float64, qr, ip, args...) @@ -22,11 +25,12 @@ function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolatio end # Access geometry values -for op = (:getdetJdV, :getngeobasefunctions, :geometric_value) +for op = (:getngeobasefunctions, :geometric_value) eval(quote @propagate_inbounds $op(cv::CellValues, args...) = $op(cv.geo_values, args...) end) end +getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] # Accessors for function values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) @@ -45,8 +49,11 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}) throw_incompatible_coord_length(length(x), n_geom_basefuncs) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) - Jinv = calculate_mapping(geo_values, q_point, w, x) - apply_mapping!(cv.fun_values, q_point, Jinv) + mapping = calculate_mapping(geo_values, q_point, x) + detJ = calculate_detJ(getjacobian(mapping)) + detJ > 0.0 || throw_detJ_not_pos(detJ) + @inbounds cv.detJdV[q_point] = detJ*w + apply_mapping!(cv.fun_values, q_point, mapping) end return nothing end diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 27f33e3de7..9514f471ea 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -1,55 +1,18 @@ -struct FaceGeometryValues{dMdξ_t, GIP, T, Normal_t} - M::Matrix{T} - dMdξ::Matrix{dMdξ_t} +struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: AbstractFaceValues + geo_values::Vector{GeometryValues{dMdξ_t, GIP, T}} detJdV::Vector{T} normals::Vector{Normal_t} - ip::GIP -end -function FaceGeometryValues(::Type{T}, ip_vec::VectorizedInterpolation{sdim}, qr::QuadratureRule) where {T,sdim} - ip = ip_vec.ip - n_shape = getnbasefunctions(ip) - n_qpoints = getnquadpoints(qr) - M = zeros(T, n_shape, n_qpoints) - dMdξ = zeros(Vec{getdim(ip),T}, n_shape, n_qpoints) - for (qp, ξ) in pairs(getpoints(qr)) - for i in 1:n_shape - dMdξ[i, qp], M[i, qp] = shape_gradient_and_value(ip, ξ, i) - end - end - normals = fill(zero(Vec{sdim,T})*T(NaN), n_qpoints) - detJdV = fill(T(NaN), n_qpoints) - return FaceGeometryValues(M, dMdξ, detJdV, normals, ip) -end - -getngeobasefunctions(geovals::FaceGeometryValues) = size(geovals.M, 1) -@propagate_inbounds geometric_value(geovals::FaceGeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] -@propagate_inbounds getdetJdV(geovals::FaceGeometryValues, q_point::Int) = geovals.detJdV[q_point] -@propagate_inbounds getnormal(geovals::FaceGeometryValues, q_point::Int) = geovals.normals[q_point] - -@inline function calculate_mapping(geo_values::FaceGeometryValues{<:Vec{dim,T}}, face_nr::Int, q_point, w, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} - fefv_J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) - @inbounds for j in 1:getngeobasefunctions(geo_values) - fefv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] - end - weight_norm = weighted_normal(fefv_J, getrefshape(geo_values.ip), face_nr) - detJ = norm(weight_norm) - detJ > 0.0 || throw_detJ_not_pos(detJ) - @inbounds geo_values.detJdV[q_point] = detJ*w - @inbounds geo_values.normals[q_point] = weight_norm / norm(weight_norm) - return inv(fefv_J) -end - -struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: AbstractFaceValues - geo_values::Vector{FaceGeometryValues{dMdξ_t, GIP, T, Normal_t}} fun_values::Vector{FunctionValues{IP, N_t, dNdx_t, dNdξ_t}} qr::QR # FaceQuadratureRule current_face::ScalarWrapper{Int} end -function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation=default_geometric_interpolation(ip_fun)) where T - geo_values = [FaceGeometryValues(T, ip_geo, qr) for qr in fqr.face_rules] +function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun)) where {T,sdim} + geo_values = [GeometryValues(T, ip_geo.ip, qr) for qr in fqr.face_rules] fun_values = [FunctionValues(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] - return FaceValues(geo_values, fun_values, fqr, ScalarWrapper(1)) + detJdV = fill(T(NaN), maximum(qr->length(getweights(qr)), fqr.face_rules)) + normals = fill(zero(Vec{sdim,T})*T(NaN), length(geo_values)) + return FaceValues(geo_values, detJdV, normals, fun_values, fqr, ScalarWrapper(1)) end FaceValues(qr::FaceQuadratureRule, ip::Interpolation, args...) = FaceValues(Float64, qr, ip, args...) @@ -60,9 +23,10 @@ end getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_values(fv)) getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) getnquadpoints(fv::FaceValues) = getnquadpoints(fv.qr, getcurrentface(fv)) +getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] get_geo_values(fv::FaceValues) = @inbounds fv.geo_values[getcurrentface(fv)] -for op = (:getdetJdV, :getngeobasefunctions, :geometric_value) +for op = (:getngeobasefunctions, :geometric_value) eval(quote @propagate_inbounds $op(fv::FaceValues, args...) = $op(get_geo_values(fv), args...) end) @@ -89,7 +53,7 @@ getcurrentface(fv::FaceValues) = fv.current_face[] Return the normal at the quadrature point `qp` for the active face of the `FaceValues` object(from last `reinit!`). """ -getnormal(fv::FaceValues, qp::Int) = getnormal(get_geo_values(fv), qp) +getnormal(fv::FaceValues, qp::Int) = fv.normals[qp] nfaces(fv::FaceValues) = length(fv.geo_values) @@ -108,8 +72,14 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int) wh geo_values = get_geo_values(fv) fun_values = get_fun_values(fv) @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) - Jinv = calculate_mapping(geo_values, face_nr, q_point, w, x) - apply_mapping!(fun_values, q_point, Jinv) + mapping = calculate_mapping(geo_values, q_point, x) + J = getjacobian(mapping) + weight_norm = weighted_normal(J, getrefshape(geo_values.ip), face_nr) + detJ = norm(weight_norm) + detJ > 0.0 || throw_detJ_not_pos(detJ) + @inbounds fv.detJdV[q_point] = detJ*w + @inbounds fv.normals[q_point] = weight_norm / norm(weight_norm) + apply_mapping!(fun_values, q_point, mapping) end end diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 14a56a0e52..95245cb494 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -58,6 +58,26 @@ getnbasefunctions(funvals::FunctionValues) = size(funvals.N, 1) @propagate_inbounds shape_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.dNdx[base_func, q_point] @propagate_inbounds shape_symmetric_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = symmetric(shape_gradient(funvals, q_point, base_func)) +# Mapping types +struct IdentityMapping end +struct CovariantPiolaMapping end +struct ContravariantPiolaMapping end +# Not yet implemented: +# struct DoubleCovariantPiolaMapping end +# struct DoubleContravariantPiolaMapping end + +get_mapping_type(::FunctionValues{<:ScalarInterpolation}) = IdentityMapping() +get_mapping_type(::FunctionValues{<:VectorizedInterpolation}) = IdentityMapping() +#get_mapping_type(::FunctionValues{<:HdivIP}) = ContravariantPiolaMapping() +#get_mapping_type(::FunctionValues{<:HcurlIP}) = CovariantPiolaMapping() + +requires_hessian(::IdentityMapping) = false +requires_hessian(::ContravariantPiolaMapping) = true +requires_hessian(::CovariantPiolaMapping) = true + + +calculate_Jinv(J::Tensor{2}) = inv(J) +calculate_Jinv(J::SMatrix) = pinv(J) # Hotfix to get the dots right for embedded elements until mixed tensors are merged. # Scalar/Vector interpolations with sdim == rdim (== vdim) @@ -69,10 +89,25 @@ getnbasefunctions(funvals::FunctionValues) = size(funvals.N, 1) # Vector interpolations with sdim > rdim @inline dothelper(B::SMatrix{vdim, rdim}, A::SMatrix{rdim, sdim}) where {vdim, rdim, sdim} = B * A -@inline function apply_mapping!(funvals::FunctionValues, q_point::Int, Jinv) +@inline function apply_mapping!(funvals::FunctionValues, args...) + return apply_mapping!(funvals, get_mapping_type(funvals), args...) +end + +@inline function apply_mapping!(funvals::FunctionValues, ::IdentityMapping, q_point::Int, mapping_values) + Jinv = calculate_Jinv(getjacobian(mapping_values)) @inbounds for j in 1:getnbasefunctions(funvals) #funvals.dNdx[j, q_point] = funvals.dNdξ[j, q_point] ⋅ Jinv # TODO via Tensors.jl funvals.dNdx[j, q_point] = dothelper(funvals.dNdξ[j, q_point], Jinv) end return nothing -end \ No newline at end of file +end +#= +@inline function apply_mapping!(funvals::FunctionValues, ::IdentityMapping, q_point::Int, mapping_values) + Jinv = inv(getjacobian(mapping_values)) + @inbounds for j in 1:getnbasefunctions(funvals) + #funvals.dNdx[j, q_point] = funvals.dNdξ[j, q_point] ⋅ Jinv # TODO via Tensors.jl + funvals.dNdx[j, q_point] = dothelper(funvals.dNdξ[j, q_point], Jinv) + end + return nothing +end +=# \ No newline at end of file diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index e9ad79daa2..e36855b3e0 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -1,7 +1,14 @@ +struct MappingValues{JT, HT<:Union{Nothing,AbstractTensor{3}}} + J::JT # dx/dξ # Jacobian + H::HT # dJ/dξ # Hessian +end +@inline getjacobian(mv::MappingValues) = mv.J +@inline gethessian(mv::MappingValues) = mv.H + + struct GeometryValues{dMdξ_t, GIP, T} M::Matrix{T} dMdξ::Matrix{dMdξ_t} - detJdV::Vector{T} ip::GIP end function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T @@ -15,25 +22,22 @@ function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) dMdξ[i, qp], M[i, qp] = shape_gradient_and_value(ip, ξ, i) end end - detJdV::Vector{T} = fill(T(NaN), n_qpoints) - return GeometryValues(M, dMdξ, detJdV, ip) + return GeometryValues(M, dMdξ, ip) end getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) @propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] -@propagate_inbounds getdetJdV(geovals::GeometryValues, q_point::Int) = geovals.detJdV[q_point] -@inline function calculate_mapping(geo_values::GeometryValues{<:Vec{dim,T}}, q_point, w, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} +@inline function calculate_mapping(geo_values::GeometryValues{<:Vec{dim,T}}, q_point, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} fecv_J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) @inbounds for j in 1:getngeobasefunctions(geo_values) fecv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] end - detJ = det(fecv_J) - detJ > 0.0 || throw_detJ_not_pos(detJ) - @inbounds geo_values.detJdV[q_point] = detJ*w - return inv(fecv_J) + return MappingValues(fecv_J, nothing) end +calculate_detJ(J::Tensor{2}) = det(J) +calculate_detJ(J::SMatrix) = embedding_det(J) # Embedded @@ -66,7 +70,7 @@ See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-di """ embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) = norm(J) -@inline function calculate_mapping(geo_values::GeometryValues{<:Vec{rdim,T}}, q_point, w, x::AbstractVector{<:Vec{sdim,T}}) where {rdim,sdim,T} +@inline function calculate_mapping(geo_values::GeometryValues{<:Vec{rdim,T}}, q_point, x::AbstractVector{<:Vec{sdim,T}}) where {rdim,sdim,T} n_geom_basefuncs = getngeobasefunctions(geo_values) fecv_J = zero(MMatrix{sdim, rdim, T}) # TODO replace with MixedTensor (see https://github.com/Ferrite-FEM/Tensors.jl/pull/188) for j in 1:n_geom_basefuncs @@ -75,10 +79,5 @@ embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) = norm(J) fecv_J[k, l] += x[j][k] * geo_values.dMdξ[j, q_point][l] end end - fecv_J = SMatrix(fecv_J) - detJ = embedding_det(fecv_J) - detJ > 0.0 || throw_detJ_not_pos(detJ) - @inbounds geo_values.detJdV[q_point] = detJ * w - # Compute "left inverse" of J - return pinv(fecv_J) + return MappingValues(SMatrix(fecv_J), nothing) end \ No newline at end of file diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index 1ab9ef9594..7e68c46314 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -102,6 +102,7 @@ for (scalar_interpol, quad_rule) in ( if hasmethod(pointer, Tuple{typeof(v)}) @test pointer(v) != pointer(vc) end + v != vc && println(typeof(fv)) @test v == vc end end From bb47cdb6fec5c4a8e571c52d68dca3de808b8d3f Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 12 Aug 2023 11:46:54 +0200 Subject: [PATCH 009/172] Fix construction of FaceValues --- src/FEValues/FaceValues.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 9514f471ea..6ad5db4ef0 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -10,8 +10,9 @@ end function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun)) where {T,sdim} geo_values = [GeometryValues(T, ip_geo.ip, qr) for qr in fqr.face_rules] fun_values = [FunctionValues(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] - detJdV = fill(T(NaN), maximum(qr->length(getweights(qr)), fqr.face_rules)) - normals = fill(zero(Vec{sdim,T})*T(NaN), length(geo_values)) + max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) + detJdV = fill(T(NaN), max_nquadpoints) + normals = fill(zero(Vec{sdim,T})*T(NaN), max_nquadpoints) return FaceValues(geo_values, detJdV, normals, fun_values, fqr, ScalarWrapper(1)) end From 7ae714e410f2e5a403c0e68f945f2f0dccc7aafe Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 28 Aug 2023 15:25:22 +0200 Subject: [PATCH 010/172] Trying out to get RT working --- src/FEValues/FunctionValues.jl | 5 +++-- src/interpolations.jl | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 95245cb494..30ed0ebfe8 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -66,8 +66,9 @@ struct ContravariantPiolaMapping end # struct DoubleCovariantPiolaMapping end # struct DoubleContravariantPiolaMapping end -get_mapping_type(::FunctionValues{<:ScalarInterpolation}) = IdentityMapping() -get_mapping_type(::FunctionValues{<:VectorizedInterpolation}) = IdentityMapping() +get_mapping_type(fv::FunctionValues) = get_mapping_type(fv.ip) +#get_mapping_type(::FunctionValues{<:ScalarInterpolation}) = IdentityMapping() +#get_mapping_type(::FunctionValues{<:VectorizedInterpolation}) = IdentityMapping() #get_mapping_type(::FunctionValues{<:HdivIP}) = ContravariantPiolaMapping() #get_mapping_type(::FunctionValues{<:HcurlIP}) = CovariantPiolaMapping() diff --git a/src/interpolations.jl b/src/interpolations.jl index 46e4a2576a..2803e82eac 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1374,3 +1374,25 @@ function shape_gradient_and_value(ipv::VectorizedInterpolation{vdim, shape}, ξ: end reference_coordinates(ip::VectorizedInterpolation) = reference_coordinates(ip.ip) + + +get_mapping_type(::Interpolation) = IdentityMapping() + +# https://defelement.com/elements/raviart-thomas.html +# https://defelement.com/elements/qdiv.html +struct RaviartThomas{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end + +facedof_interior_indices(ip::RaviartThomas) = facedof_indices(ip) + +getnbasefunctions(::RaviartThomas{2,RefTriangle,1}) = 3 +facedof_indices(::RaviartThomas{2,RefTriangle,1}) = ((1,), (2,), (3,)) +adjust_dofs_during_distribution(::RaviartThomas) = false # Not sure how this works, but should be done for higher orders +# https://defelement.com/elements/examples/triangle-N1div-1.html +function shape_value(ip::RaviartThomas{2,RefTriangle,1}, ξ::Vec{2}, i::Int) + ξ_x, ξ_y = ξ + i == 1 && return -ξ + i == 2 && return Vec(ξ_x-1, ξ_y) + i == 3 && return Vec(-ξ_x, 1 - ξ_y) + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + From 7f484ebc28fa17875a931d6ed556c13ef2acf734 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 28 Aug 2023 21:05:35 +0200 Subject: [PATCH 011/172] Add definition of positive faces and edges --- src/Grid/grid.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Grid/grid.jl b/src/Grid/grid.jl index d21e0962b5..2fe1f78422 100644 --- a/src/Grid/grid.jl +++ b/src/Grid/grid.jl @@ -732,6 +732,22 @@ end ################################# #### Orientation of Entities #### ################################# +function face_sign(::AbstractCell{<:AbstractRefShape{1}}, facenr) + error("Not defined") +end +function face_sign(cell::AbstractCell{<:AbstractRefShape{2}}, facenr) + vertices = faces(cell)[facenr] + return vertices[1] < vertices[2] ? 1 : -1 +end +function face_sign(::AbstractCell{<:AbstractRefShape{3}}, facenr) + error("Implement me") +end + +function edge_sign(cell::AbstractCell, edgenr) + vertices = edges(cell)[edgenr] + return vertices[1] < vertices[2] ? 1 : -1 +end + # @TODO merge this code with into the logic in `ConstraintHandler`. """ From 6daaf656815a3e7bd94ff701b0ed7633777f8b88 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 17 Sep 2023 00:40:13 +0200 Subject: [PATCH 012/172] Fix show to old values --- src/FEValues/cell_values.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FEValues/cell_values.jl b/src/FEValues/cell_values.jl index d6393c5e39..d7480df1d8 100644 --- a/src/FEValues/cell_values.jl +++ b/src/FEValues/cell_values.jl @@ -243,7 +243,7 @@ function reinit!(cv::OldCellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVect return nothing end -function Base.show(io::IO, m::MIME"text/plain", cv::CellValues) +function Base.show(io::IO, m::MIME"text/plain", cv::OldCellValues) println(io, "CellValues with") println(io, "- Quadrature rule with ", getnquadpoints(cv), " points") print(io, "- Function interpolation: "); show(io, m, cv.ip) From 2a6aee03430b78d924fbe10e06f11e13a994f4ff Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 18 Sep 2023 23:33:32 +0200 Subject: [PATCH 013/172] Seemingly working triangle and nedelec --- docs/Manifest.toml | 645 ++++++++++++++++++- docs/Project.toml | 1 + docs/generate.jl | 2 +- docs/make.jl | 15 +- docs/src/literate-tutorials/maxwell.jl | 64 ++ docs/src/literate-tutorials/maxwell_refel.jl | 60 ++ src/FEValues/CellValues.jl | 11 +- src/FEValues/FunctionValues.jl | 34 +- src/FEValues/GeometryValues.jl | 41 +- src/interpolations.jl | 34 +- 10 files changed, 852 insertions(+), 55 deletions(-) create mode 100644 docs/src/literate-tutorials/maxwell.jl create mode 100644 docs/src/literate-tutorials/maxwell_refel.jl diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 5943cd517a..d1f841f815 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2,18 +2,34 @@ julia_version = "1.9.2" manifest_format = "2.0" -project_hash = "a0caf5d07899f1863658c7736f8530d20d28db07" +project_hash = "0444e709e1c6f42386a9ac2b8c455eb5004d33dc" [[deps.ADTypes]] -git-tree-sha1 = "f2b16fe1a3491b295105cae080c2a5f77a842718" +git-tree-sha1 = "5d2e21d7b0d8c22f67483ef95ebdc39c0e6b6003" uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" -version = "0.2.3" +version = "0.2.4" [[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" +[[deps.AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.5.0" +weakdeps = ["ChainRulesCore", "Test"] + + [deps.AbstractFFTs.extensions] + AbstractFFTsChainRulesCoreExt = "ChainRulesCore" + AbstractFFTsTestExt = "Test" + +[[deps.AbstractLattices]] +git-tree-sha1 = "f35684b7349da49fcc8a9e520e30e45dbb077166" +uuid = "398f06c4-4d28-53ec-89ca-5b2656b7603d" +version = "0.2.1" + [[deps.AbstractTrees]] git-tree-sha1 = "faa260e4cb5aba097a73fab382dd4b5819d8ec8c" uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -29,6 +45,12 @@ weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" +[[deps.Animations]] +deps = ["Colors"] +git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d" +uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340" +version = "0.4.1" + [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" @@ -80,6 +102,24 @@ weakdeps = ["SparseArrays"] [[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +[[deps.Automa]] +deps = ["TranscodingStreams"] +git-tree-sha1 = "ef9997b3d5547c48b41c7bd8899e812a917b409d" +uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" +version = "0.8.4" + +[[deps.AxisAlgorithms]] +deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] +git-tree-sha1 = "66771c8d21c8ff5e3a93379480a2307ac36863f7" +uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950" +version = "1.0.1" + +[[deps.AxisArrays]] +deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"] +git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f" +uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9" +version = "0.4.7" + [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -123,18 +163,56 @@ git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" version = "1.0.8+0" +[[deps.CEnum]] +git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.4.2" + [[deps.CPUSummary]] deps = ["CpuId", "IfElse", "PrecompileTools", "Static"] git-tree-sha1 = "601f7e7b3d36f18790e2caf83a882d88e9b71ff1" uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" version = "0.2.4" +[[deps.CRC32c]] +uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" + +[[deps.CRlibm]] +deps = ["CRlibm_jll"] +git-tree-sha1 = "32abd86e3c2025db5172aa182b982debed519834" +uuid = "96374032-68de-5a5b-8d9e-752f78720389" +version = "1.0.1" + +[[deps.CRlibm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" +uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" +version = "1.0.1+0" + +[[deps.Cairo]] +deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] +git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b" +uuid = "159f3aea-2a34-519c-b102-8c37f9878175" +version = "1.0.5" + +[[deps.CairoMakie]] +deps = ["Base64", "Cairo", "Colors", "FFTW", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools", "SHA"] +git-tree-sha1 = "696e7931bd6f5c773418452cbe5fd241cb85ac2a" +uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +version = "0.10.9" + [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" version = "1.16.1+1" +[[deps.Calculus]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" +uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" +version = "0.5.1" + [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] git-tree-sha1 = "e30f2f4e20f7f186dc36529910beaedc60cfa644" @@ -153,6 +231,12 @@ git-tree-sha1 = "02aa26a4cf76381be7f66e020a3eddeb27b0a092" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" version = "0.7.2" +[[deps.ColorBrewer]] +deps = ["Colors", "JSON", "Test"] +git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4" +uuid = "a2cac450-b92f-5266-8821-25eda20663c8" +version = "0.4.0" + [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] git-tree-sha1 = "d9a8f86737b665e15a9641ecbac64deef9ce6724" @@ -166,14 +250,10 @@ uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" version = "0.11.4" [[deps.ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] -git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"] +git-tree-sha1 = "600cc5508d66b78aae350f7accdb58763ac18589" uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.10.0" -weakdeps = ["SpecialFunctions"] - - [deps.ColorVectorSpace.extensions] - SpecialFunctionsExt = "SpecialFunctions" +version = "0.9.10" [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] @@ -181,6 +261,11 @@ git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" version = "0.12.10" +[[deps.Combinatorics]] +git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" +uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" +version = "1.0.2" + [[deps.CommonSolve]] git-tree-sha1 = "0eee5eb66b1cf62cd6ad1b460238e60e4b09400c" uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" @@ -218,15 +303,12 @@ deps = ["LinearAlgebra"] git-tree-sha1 = "c53fc348ca4d40d7b371e71fd52251839080cbc9" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" version = "1.5.4" +weakdeps = ["IntervalSets", "StaticArrays"] [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" ConstructionBaseStaticArraysExt = "StaticArrays" - [deps.ConstructionBase.weakdeps] - IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - [[deps.Contour]] git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" @@ -258,6 +340,12 @@ version = "1.0.0" deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +[[deps.DelaunayTriangulation]] +deps = ["DataStructures", "EnumX", "ExactPredicates", "Random", "SimpleGraphs"] +git-tree-sha1 = "bea7984f7e09aeb28a3b071c420a0186cb4fabad" +uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" +version = "0.8.8" + [[deps.DelimitedFiles]] deps = ["Mmap"] git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" @@ -318,6 +406,20 @@ weakdeps = ["SparseArrays"] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +[[deps.Distributions]] +deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns", "Test"] +git-tree-sha1 = "938fe2981db009f531b6332e31c58e9584a2f9bd" +uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" +version = "0.25.100" + + [deps.Distributions.extensions] + DistributionsChainRulesCoreExt = "ChainRulesCore" + DistributionsDensityInterfaceExt = "DensityInterface" + + [deps.Distributions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" + [[deps.DocStringExtensions]] deps = ["LibGit2"] git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" @@ -332,22 +434,43 @@ version = "1.0.0" [[deps.DocumenterCitations]] deps = ["AbstractTrees", "Bibliography", "Documenter", "Markdown", "MarkdownAST", "OrderedCollections", "Unicode"] -git-tree-sha1 = "5f815482eac14b068725bc1fa8752876ae850ee8" -repo-rev = "fe/Documenter-v1" -repo-url = "https://github.com/JuliaDocs/DocumenterCitations.jl" +git-tree-sha1 = "375b49c31c2c3676f3ddb65efea31c7dbea42af1" uuid = "daee34ce-89f3-4625-b898-19384cb65244" -version = "1.1.0-dev" +version = "1.2.0" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" +[[deps.DualNumbers]] +deps = ["Calculus", "NaNMath", "SpecialFunctions"] +git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566" +uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" +version = "0.6.8" + +[[deps.EarCut_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053" +uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +version = "2.2.4+0" + [[deps.EnumX]] git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.4" +[[deps.ErrorfreeArithmetic]] +git-tree-sha1 = "d6863c556f1142a061532e79f611aa46be201686" +uuid = "90fa49ef-747e-5e6f-a989-263ba693cf1a" +version = "0.5.2" + +[[deps.ExactPredicates]] +deps = ["IntervalArithmetic", "Random", "StaticArraysCore", "Test"] +git-tree-sha1 = "276e83bc8b21589b79303b9985c321024ffdf59c" +uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" +version = "2.2.5" + [[deps.ExceptionUnwrapping]] deps = ["Test"] git-tree-sha1 = "e90caa41f5a86296e014e148ee061bd6c3edec96" @@ -371,6 +494,11 @@ git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" version = "0.1.10" +[[deps.Extents]] +git-tree-sha1 = "5e1e4c53fa39afe63a7d356e30452249365fba99" +uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" +version = "0.1.1" + [[deps.FFMPEG]] deps = ["FFMPEG_jll"] git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8" @@ -383,6 +511,18 @@ git-tree-sha1 = "74faea50c1d007c85837327f6775bea60b5492dd" uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" version = "4.4.2+2" +[[deps.FFTW]] +deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "b4fbdd20c889804969571cc589900803edda16b7" +uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +version = "1.7.1" + +[[deps.FFTW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" +uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" +version = "3.3.10+0" + [[deps.FLTK_jll]] deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "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 = "72a4842f93e734f378cf381dae2ca4542f019d23" @@ -406,6 +546,12 @@ git-tree-sha1 = "b12f05108e405dadcc2aff0008db7f831374e051" uuid = "29a986be-02c6-4525-aec4-84b980013641" version = "2.0.0" +[[deps.FastRounding]] +deps = ["ErrorfreeArithmetic", "LinearAlgebra"] +git-tree-sha1 = "6344aa18f654196be82e62816935225b3b9abe44" +uuid = "fa42c844-2597-5d31-933b-ebd51ab2693f" +version = "0.3.1" + [[deps.Ferrite]] deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] path = ".." @@ -496,12 +642,24 @@ weakdeps = ["StaticArrays"] [deps.ForwardDiff.extensions] ForwardDiffStaticArraysExt = "StaticArrays" +[[deps.FreeType]] +deps = ["CEnum", "FreeType2_jll"] +git-tree-sha1 = "50351f83f95282cf903e968d7c6e8d44a5f83d0b" +uuid = "b38be410-82b0-50bf-ab77-7b57e271db43" +version = "4.1.0" + [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" version = "2.13.1+0" +[[deps.FreeTypeAbstraction]] +deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] +git-tree-sha1 = "38a92e40157100e796690421e34a11c107205c86" +uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" +version = "0.10.0" + [[deps.FriBidi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" @@ -564,6 +722,18 @@ git-tree-sha1 = "fb69b2a645fa69ba5f474af09221b9308b160ce6" uuid = "c145ed77-6b09-5dd9-b285-bf645a82121e" version = "0.5.3" +[[deps.GeoInterface]] +deps = ["Extents"] +git-tree-sha1 = "bb198ff907228523f3dee1070ceee63b9359b6ab" +uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" +version = "1.3.1" + +[[deps.GeometryBasics]] +deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "424a5a6ce7c5d97cca7bcc4eac551b97294c54af" +uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +version = "0.4.9" + [[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" @@ -582,6 +752,12 @@ git-tree-sha1 = "4d4dedef84147934837c683538467cea54c44d44" uuid = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" version = "0.2.2" +[[deps.Graphics]] +deps = ["Colors", "LinearAlgebra", "NaNMath"] +git-tree-sha1 = "d61890399bc535850c4bf08e4e0d3a7ad0f21cbd" +uuid = "a2bd30eb-e257-5431-a919-1863eab51364" +version = "1.1.2" + [[deps.Graphite2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" @@ -594,6 +770,12 @@ git-tree-sha1 = "1cf1d7dcb4bc32d7b4a5add4232db3750c27ecb4" uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" version = "1.8.0" +[[deps.GridLayoutBase]] +deps = ["GeometryBasics", "InteractiveUtils", "Observables"] +git-tree-sha1 = "f57a64794b336d4990d90f80b147474b869b1bc4" +uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" +version = "0.9.2" + [[deps.Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" @@ -623,6 +805,12 @@ git-tree-sha1 = "eb8fed28f4994600e29beef49744639d985a04b2" uuid = "3e5b6fbb-0976-4d2c-9146-d79de83f2fb0" version = "0.1.16" +[[deps.HypergeometricFunctions]] +deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] +git-tree-sha1 = "f218fe3736ddf977e0e772bc9a586b2383da2685" +uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" +version = "0.3.23" + [[deps.IOCapture]] deps = ["Logging", "Random"] git-tree-sha1 = "d75853a0bdbfb1ac815478bacd89cd27b550ace6" @@ -634,20 +822,105 @@ git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" version = "0.1.1" +[[deps.ImageAxes]] +deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"] +git-tree-sha1 = "2e4520d67b0cef90865b3ef727594d2a58e0e1f8" +uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac" +version = "0.6.11" + +[[deps.ImageBase]] +deps = ["ImageCore", "Reexport"] +git-tree-sha1 = "b51bb8cae22c66d0f6357e3bcb6363145ef20835" +uuid = "c817782e-172a-44cc-b673-b171935fbb9e" +version = "0.1.5" + +[[deps.ImageCore]] +deps = ["AbstractFFTs", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Graphics", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "Reexport"] +git-tree-sha1 = "acf614720ef026d38400b3817614c45882d75500" +uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534" +version = "0.9.4" + +[[deps.ImageIO]] +deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs"] +git-tree-sha1 = "bca20b2f5d00c4fbc192c3212da8fa79f4688009" +uuid = "82e4d734-157c-48bb-816b-45c225c6df19" +version = "0.6.7" + +[[deps.ImageMetadata]] +deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] +git-tree-sha1 = "355e2b974f2e3212a75dfb60519de21361ad3cb7" +uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49" +version = "0.9.9" + +[[deps.Imath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "3d09a9f60edf77f8a4d99f9e015e8fbf9989605d" +uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" +version = "3.1.7+0" + +[[deps.IndirectArrays]] +git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" +uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" +version = "1.0.0" + [[deps.Inflate]] git-tree-sha1 = "5cd07aab533df5170988219191dfad0519391428" uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" version = "0.1.3" +[[deps.IntegerMathUtils]] +git-tree-sha1 = "b8ffb903da9f7b8cf695a8bead8e01814aa24b30" +uuid = "18e54dd8-cb9d-406c-a71d-865a43cbb235" +version = "0.1.2" + +[[deps.IntelOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "ad37c091f7d7daf900963171600d7c1c5c3ede32" +uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" +version = "2023.2.0+0" + [[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +[[deps.Interpolations]] +deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] +git-tree-sha1 = "721ec2cf720536ad005cb38f50dbba7b02419a15" +uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" +version = "0.14.7" + +[[deps.IntervalArithmetic]] +deps = ["CRlibm", "FastRounding", "LinearAlgebra", "Markdown", "Random", "RecipesBase", "RoundingEmulator", "SetRounding", "StaticArrays"] +git-tree-sha1 = "5ab7744289be503d76a944784bac3f2df7b809af" +uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +version = "0.20.9" + +[[deps.IntervalSets]] +deps = ["Dates", "Random"] +git-tree-sha1 = "8e59ea773deee525c99a8018409f64f19fb719e6" +uuid = "8197267c-284f-5f27-9208-e0e47529a953" +version = "0.7.7" +weakdeps = ["Statistics"] + + [deps.IntervalSets.extensions] + IntervalSetsStatisticsExt = "Statistics" + [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" version = "0.2.2" +[[deps.Isoband]] +deps = ["isoband_jll"] +git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137" +uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4" +version = "0.1.1" + +[[deps.IterTools]] +git-tree-sha1 = "4ced6667f9974fc5c5943fa5e2ef1ca43ea9e450" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.8.0" + [[deps.IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] git-tree-sha1 = "1169632f425f79429f245113b775a0e3d121457c" @@ -689,6 +962,12 @@ git-tree-sha1 = "5f0bd0cd69df978fa64ccdcb5c152fbc705455a1" uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" version = "1.3.0" +[[deps.JpegTurbo]] +deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"] +git-tree-sha1 = "327713faef2a3e5c80f96bf38d1fa26f7a6ae29e" +uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0" +version = "0.1.3" + [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "6f2675ef130a300a112286de91973805fcc5ffbc" @@ -701,6 +980,12 @@ git-tree-sha1 = "884c2968c2e8e7e6bf5956af88cb46aa745c854b" uuid = "ef3ab10e-7fda-4108-b977-705223b18434" version = "0.4.1" +[[deps.KernelDensity]] +deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] +git-tree-sha1 = "90442c50e202a5cdf21a7899c66b240fdef14035" +uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" +version = "0.6.7" + [[deps.Krylov]] deps = ["LinearAlgebra", "Printf", "SparseArrays"] git-tree-sha1 = "17e462054b42dcdda73e9a9ba0c67754170c88ae" @@ -767,6 +1052,15 @@ git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f" uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" version = "0.15.1" +[[deps.LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[deps.LazyModules]] +git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e" +uuid = "8cdb02fc-e678-4876-92c5-9defec4f444e" +version = "0.3.1" + [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -853,6 +1147,12 @@ version = "7.2.0" deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +[[deps.LinearAlgebraX]] +deps = ["LinearAlgebra", "Mods", "Permutations", "Primes", "SimplePolynomials"] +git-tree-sha1 = "558a338f1eeabe933f9c2d4052aa7c2c707c3d52" +uuid = "9b3f67b0-2d00-526e-9884-9e4938f8fb88" +version = "0.1.12" + [[deps.LinearElasticity_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" @@ -942,6 +1242,12 @@ git-tree-sha1 = "65f28ad4b594aebe22157d6fac869786a255b7eb" uuid = "6c6e2e6c-3030-632d-7369-2d6c69616d65" version = "0.1.4" +[[deps.MKL_jll]] +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] +git-tree-sha1 = "eb006abbd7041c28e0d16260e50a24f8f9104913" +uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" +version = "2023.2.0+0" + [[deps.MMG_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" @@ -954,11 +1260,28 @@ git-tree-sha1 = "9ee1618cbf5240e6d4e0371d6f24065083f60c48" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.11" +[[deps.Makie]] +deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FixedPointNumbers", "Formatting", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "InteractiveUtils", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "Match", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Setfield", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "StableHashTraits", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun"] +git-tree-sha1 = "ecc334efc4a8a68800776b0d85ab7bb2fff63f7a" +uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" +version = "0.19.9" + +[[deps.MakieCore]] +deps = ["Observables"] +git-tree-sha1 = "1efb1166dd9398f2ccf6d728f896658c9c84733e" +uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" +version = "0.6.6" + [[deps.ManualMemory]] git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" uuid = "d125e4d3-2237-4719-b19c-fa641b8a4667" version = "0.1.8" +[[deps.MappedArrays]] +git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" +uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" +version = "0.4.2" + [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" @@ -969,6 +1292,17 @@ git-tree-sha1 = "e8513266815200c0c8f522d6d44ffb5e9b366ae4" uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" version = "0.1.1" +[[deps.Match]] +git-tree-sha1 = "1d9bc5c1a6e7ee24effb93f175c9342f9154d97f" +uuid = "7eb4fadd-790c-5f42-8a69-bfa0b872bfbf" +version = "1.2.0" + +[[deps.MathTeXEngine]] +deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "Test", "UnicodeFun"] +git-tree-sha1 = "8f52dbaa1351ce4cb847d95568cb29e62a307d93" +uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" +version = "0.5.6" + [[deps.MbedTLS]] deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "Random", "Sockets"] git-tree-sha1 = "03a9b9718f5682ecb107ac9f7308991db4ce395b" @@ -994,6 +1328,17 @@ version = "1.1.0" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +[[deps.Mods]] +git-tree-sha1 = "61be59e4daffff43a8cec04b5e0dc773cbb5db3a" +uuid = "7475f97c-0381-53b1-977b-4c60186c8d62" +version = "1.3.3" + +[[deps.MosaicViews]] +deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] +git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe" +uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389" +version = "0.3.4" + [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2022.10.11" @@ -1003,6 +1348,11 @@ git-tree-sha1 = "cac9cc5499c25554cba55cd3c30543cff5ca4fab" uuid = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" version = "0.2.4" +[[deps.Multisets]] +git-tree-sha1 = "8d852646862c96e226367ad10c8af56099b4047e" +uuid = "3b2b4ff1-bcff-5658-a3ee-dbcf1ce5ac09" +version = "0.4.4" + [[deps.NLSolversBase]] deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" @@ -1027,6 +1377,12 @@ git-tree-sha1 = "2c3726ceb3388917602169bed973dbc97f1b51a8" uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" version = "0.4.13" +[[deps.Netpbm]] +deps = ["FileIO", "ImageCore", "ImageMetadata"] +git-tree-sha1 = "d92b107dbb887293622df7697a2223f9f8176fcd" +uuid = "f09324ee-3d7c-5217-9330-fc30815ba969" +version = "1.1.1" + [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" @@ -1043,6 +1399,11 @@ git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" version = "7.6.2+2" +[[deps.Observables]] +git-tree-sha1 = "6862738f9796b3edc1c09d0890afce4eca9e7e93" +uuid = "510215fc-4207-5dde-b226-833fc4488ee2" +version = "0.5.4" + [[deps.OffsetArrays]] deps = ["Adapt"] git-tree-sha1 = "2ac17d29c523ce1cd38e27785a7d23024853a4bb" @@ -1060,6 +1421,18 @@ deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" version = "0.3.21+4" +[[deps.OpenEXR]] +deps = ["Colors", "FileIO", "OpenEXR_jll"] +git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633" +uuid = "52e1d378-f018-4a11-a4be-720524705ac7" +version = "0.3.2" + +[[deps.OpenEXR_jll]] +deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "a4ca623df1ae99d09bc9868b008262d0c0ac1e4f" +uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" +version = "3.1.4+0" + [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" @@ -1111,12 +1484,42 @@ deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+0" +[[deps.PDMats]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "67eae2738d63117a196f497d7db789821bce61d1" +uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" +version = "0.11.17" + +[[deps.PNGFiles]] +deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"] +git-tree-sha1 = "9b02b27ac477cad98114584ff964e3052f656a0f" +uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" +version = "0.4.0" + [[deps.PackageExtensionCompat]] git-tree-sha1 = "f9b1e033c2b1205cf30fd119f4e50881316c1923" uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" version = "1.0.1" weakdeps = ["Requires", "TOML"] +[[deps.Packing]] +deps = ["GeometryBasics"] +git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501" +uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566" +version = "0.5.0" + +[[deps.PaddedViews]] +deps = ["OffsetArrays"] +git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f" +uuid = "5432bcbf-9aad-5242-b902-cca2824c8663" +version = "0.5.12" + +[[deps.Pango_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "4745216e94f71cb768d58330b059c9b76f32cb66" +uuid = "36c8627f-9965-5494-a995-c6b170f724f3" +version = "1.50.14+0" + [[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" @@ -1129,6 +1532,12 @@ git-tree-sha1 = "716e24b21538abc91f6205fd1d8363f39b442851" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.7.2" +[[deps.Permutations]] +deps = ["Combinatorics", "LinearAlgebra", "Random"] +git-tree-sha1 = "25e2bb0973689836bf164ecb960762f1bb8794dd" +uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" +version = "0.4.17" + [[deps.Pipe]] git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" uuid = "b98c9c47-44ae-5843-9183-064241ee97a0" @@ -1145,6 +1554,12 @@ deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", " uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" version = "1.9.2" +[[deps.PkgVersion]] +deps = ["Pkg"] +git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da" +uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" +version = "0.3.3" + [[deps.PlotThemes]] deps = ["PlotUtils", "Statistics"] git-tree-sha1 = "1f03a2d339f42dca4a4da149c7e15e9b896ad899" @@ -1189,6 +1604,29 @@ git-tree-sha1 = "240d7170f5ffdb285f9427b92333c3463bf65bf6" uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" version = "0.2.1" +[[deps.PolygonOps]] +git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" +uuid = "647866c9-e3ac-4575-94e7-e3d426903924" +version = "0.1.2" + +[[deps.Polynomials]] +deps = ["LinearAlgebra", "RecipesBase", "Setfield", "SparseArrays"] +git-tree-sha1 = "ea78a2764f31715093de7ab495e12c0187f231d1" +uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" +version = "4.0.4" + + [deps.Polynomials.extensions] + PolynomialsChainRulesCoreExt = "ChainRulesCore" + PolynomialsFFTWExt = "FFTW" + PolynomialsMakieCoreExt = "MakieCore" + PolynomialsMutableArithmeticsExt = "MutableArithmetics" + + [deps.Polynomials.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" + MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b" + MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" + [[deps.PositiveFactorizations]] deps = ["LinearAlgebra"] git-tree-sha1 = "17275485f373e6673f7e7f97051f703ed5b15b20" @@ -1219,6 +1657,12 @@ git-tree-sha1 = "7eb1686b4f04b82f96ed7a4ea5890a4f0c7a09f1" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.4.0" +[[deps.Primes]] +deps = ["IntegerMathUtils"] +git-tree-sha1 = "4c9f306e5d6603ae203c2000dd460d81a5251489" +uuid = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" +version = "0.5.4" + [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -1229,12 +1673,24 @@ git-tree-sha1 = "00099623ffee15972c16111bcf84c58a0051257c" uuid = "92933f4c-e287-5a05-a399-4b506db050ca" version = "1.9.0" +[[deps.QOI]] +deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] +git-tree-sha1 = "18e8f4d1426e965c7b532ddd260599e1510d26ce" +uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" +version = "1.0.0" + [[deps.Qt6Base_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] git-tree-sha1 = "364898e8f13f7eaaceec55fd3d08680498c0aa6e" uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" version = "6.4.2+3" +[[deps.QuadGK]] +deps = ["DataStructures", "LinearAlgebra"] +git-tree-sha1 = "6ec7ac8412e83d57e313393220879ede1740f9ee" +uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +version = "2.8.2" + [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" @@ -1243,6 +1699,21 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" deps = ["SHA", "Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +[[deps.RangeArrays]] +git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" +uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d" +version = "0.3.2" + +[[deps.Ratios]] +deps = ["Requires"] +git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b" +uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" +version = "0.4.5" +weakdeps = ["FixedPointNumbers"] + + [deps.Ratios.extensions] + RatiosFixedPointNumbersExt = "FixedPointNumbers" + [[deps.RecipesBase]] deps = ["PrecompileTools"] git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" @@ -1302,6 +1773,29 @@ git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" +[[deps.RingLists]] +deps = ["Random"] +git-tree-sha1 = "f39da63aa6d2d88e0c1bd20ed6a3ff9ea7171ada" +uuid = "286e9d63-9694-5540-9e3c-4e6708fa07b2" +version = "0.2.8" + +[[deps.Rmath]] +deps = ["Random", "Rmath_jll"] +git-tree-sha1 = "f65dcb5fa46aee0cf9ed6274ccbd597adc49aa7b" +uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" +version = "0.7.1" + +[[deps.Rmath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6ed52fdd3382cf21947b15e8870ac0ddbff736da" +uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" +version = "0.4.0+0" + +[[deps.RoundingEmulator]] +git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" +uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" +version = "0.2.1" + [[deps.RuntimeGeneratedFunctions]] deps = ["ExprTools", "SHA", "Serialization"] git-tree-sha1 = "6aacc5eefe8415f47b3e34214c1d79d2674a0ba2" @@ -1368,12 +1862,23 @@ version = "1.2.0" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +[[deps.SetRounding]] +git-tree-sha1 = "d7a25e439d07a17b7cdf97eecee504c50fedf5f6" +uuid = "3cc68bcd-71a2-5612-b932-767ffbe40ab0" +version = "0.2.1" + [[deps.Setfield]] deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] git-tree-sha1 = "e2cc6d8c88613c05e1defb55170bf5ff211fbeac" uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" version = "1.1.1" +[[deps.ShaderAbstractions]] +deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "0d15c3e7b2003f4451714f08ffec2b77badc2dc4" +uuid = "65257c39-d410-5151-9873-9b3e5be5013e" +version = "0.3.0" + [[deps.SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" @@ -1384,11 +1889,23 @@ git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" version = "1.0.3" +[[deps.SignedDistanceFields]] +deps = ["Random", "Statistics", "Test"] +git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" +uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" +version = "0.4.0" + [[deps.SimpleBufferStream]] git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1" uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" version = "1.1.0" +[[deps.SimpleGraphs]] +deps = ["AbstractLattices", "Combinatorics", "DataStructures", "IterTools", "LightXML", "LinearAlgebra", "LinearAlgebraX", "Optim", "Primes", "Random", "RingLists", "SimplePartitions", "SimplePolynomials", "SimpleRandom", "SparseArrays", "Statistics"] +git-tree-sha1 = "b608903049d11cc557c45e03b3a53e9260579c19" +uuid = "55797a34-41de-5266-9ec1-32ac4eb504d3" +version = "0.8.4" + [[deps.SimpleNonlinearSolve]] deps = ["ArrayInterface", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "PackageExtensionCompat", "PrecompileTools", "Reexport", "SciMLBase", "StaticArraysCore"] git-tree-sha1 = "20aa9831d654bab67ed561e78917047143ecb9bf" @@ -1401,6 +1918,24 @@ version = "0.1.19" [deps.SimpleNonlinearSolve.weakdeps] NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" +[[deps.SimplePartitions]] +deps = ["AbstractLattices", "DataStructures", "Permutations"] +git-tree-sha1 = "dcc02923a53f316ab97da8ef3136e80b4543dbf1" +uuid = "ec83eff0-a5b5-5643-ae32-5cbf6eedec9d" +version = "0.3.0" + +[[deps.SimplePolynomials]] +deps = ["Mods", "Multisets", "Polynomials", "Primes"] +git-tree-sha1 = "d537c31cf9995236166e3e9afc424a5a1c59ff9d" +uuid = "cc47b68c-3164-5771-a705-2bc0097375a0" +version = "0.2.14" + +[[deps.SimpleRandom]] +deps = ["Distributions", "LinearAlgebra", "Random"] +git-tree-sha1 = "3a6fb395e37afab81aeea85bae48a4db5cd7244a" +uuid = "a6525b86-64cd-54fa-8f65-62fc48bdc0e8" +version = "0.3.1" + [[deps.SimpleTraits]] deps = ["InteractiveUtils", "MacroTools"] git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" @@ -1412,6 +1947,12 @@ git-tree-sha1 = "58e6353e72cde29b90a69527e56df1b5c3d8c437" uuid = "ce78b400-467f-4804-87d8-8f486da07d0a" version = "1.1.0" +[[deps.Sixel]] +deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"] +git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6" +uuid = "45858cf5-a6b0-47a3-bbea-62219f50df47" +version = "0.1.3" + [[deps.SnoopPrecompile]] deps = ["Preferences"] git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c" @@ -1463,6 +2004,18 @@ weakdeps = ["ChainRulesCore"] [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" +[[deps.StableHashTraits]] +deps = ["Compat", "SHA", "Tables", "TupleTools"] +git-tree-sha1 = "19df33ca14f24a3ad2df9e89124bd5f5cc8467a2" +uuid = "c5dd0088-6c3f-4803-b00e-f31a60c170fa" +version = "1.0.1" + +[[deps.StackViews]] +deps = ["OffsetArrays"] +git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" +uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15" +version = "0.1.1" + [[deps.Static]] deps = ["IfElse"] git-tree-sha1 = "f295e0a1da4ca425659c57441bcb59abb035a4bc" @@ -1512,6 +2065,20 @@ git-tree-sha1 = "75ebe04c5bed70b91614d684259b661c9e6274a4" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" version = "0.34.0" +[[deps.StatsFuns]] +deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] +git-tree-sha1 = "f625d686d5a88bcd2b15cd81f18f98186fdc0c9a" +uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" +version = "1.3.0" + + [deps.StatsFuns.extensions] + StatsFunsChainRulesCoreExt = "ChainRulesCore" + StatsFunsInverseFunctionsExt = "InverseFunctions" + + [deps.StatsFuns.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + [[deps.StrideArraysCore]] deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] git-tree-sha1 = "f02eb61eb5c97b48c153861c72fbbfdddc607e06" @@ -1524,6 +2091,12 @@ git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb" uuid = "69024149-9ee7-55f6-a4c4-859efe599b68" version = "0.3.7" +[[deps.StructArrays]] +deps = ["Adapt", "ConstructionBase", "DataAPI", "GPUArraysCore", "StaticArraysCore", "Tables"] +git-tree-sha1 = "0a3db38e4cce3c54fe7a71f831cd7b6194a54213" +uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" +version = "0.6.16" + [[deps.StructTypes]] deps = ["Dates", "UUIDs"] git-tree-sha1 = "ca4bccb03acf9faaf4137a9abc1881ed1841aa70" @@ -1575,7 +2148,7 @@ version = "0.1.1" [[deps.Tensors]] deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] -git-tree-sha1 = "bcbb366323add300742c9e4a5447e584640aeff2" +path = "C:\\Users\\meyer\\.julia\\dev\\Tensors" uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" version = "1.15.0" @@ -1589,6 +2162,12 @@ git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" version = "0.5.2" +[[deps.TiffImages]] +deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "UUIDs"] +git-tree-sha1 = "8621f5c499a8aa4aa970b1ae381aae0ef1576966" +uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" +version = "0.6.4" + [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] git-tree-sha1 = "f548a9e9c490030e545f72074a41edfd0e5bcdd7" @@ -1612,12 +2191,22 @@ git-tree-sha1 = "aadb748be58b492045b4f56166b5188aa63ce549" uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" version = "0.1.7" +[[deps.TriplotBase]] +git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" +uuid = "981d1d27-644d-49a2-9326-4793e63143c3" +version = "0.1.0" + [[deps.TruncatedStacktraces]] deps = ["InteractiveUtils", "MacroTools", "Preferences"] git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" +[[deps.TupleTools]] +git-tree-sha1 = "155515ed4c4236db30049ac1495e2969cc06be9d" +uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" +version = "1.4.3" + [[deps.URIs]] git-tree-sha1 = "b7a5e99f24892b6824a954199a45e9ffcc1c70f0" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" @@ -1695,6 +2284,12 @@ git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da" uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" version = "1.25.0+0" +[[deps.WoodburyMatrices]] +deps = ["LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "de67fa59e33ad156a590055375a30b23c40299d3" +uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6" +version = "0.5.5" + [[deps.WriteVTK]] deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] git-tree-sha1 = "7b46936613e41cfe1c6a5897d243ddcab8feabec" @@ -1886,6 +2481,12 @@ git-tree-sha1 = "d4cf3bb87fa0669f569e51f6f06cd083771bab65" uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" version = "4.10.2+1" +[[deps.isoband_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" +uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" +version = "0.2.3+0" + [[deps.libaom_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" @@ -1915,6 +2516,12 @@ git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" version = "1.6.38+0" +[[deps.libsixel_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] +git-tree-sha1 = "d4f63314c8aa1e48cd22aa0c17ed76cd1ae48c3c" +uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" +version = "1.10.3+0" + [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" diff --git a/docs/Project.toml b/docs/Project.toml index 2d83ab8a20..01eb9b476d 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,5 +1,6 @@ [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" diff --git a/docs/generate.jl b/docs/generate.jl index 8bcb08dc78..e06d3a14e9 100644 --- a/docs/generate.jl +++ b/docs/generate.jl @@ -22,7 +22,7 @@ include("download_resources.jl") # Run Literate on all examples @timeit dto "Literate." for (IN, OUT) in [(TUTORIALS_IN, TUTORIALS_OUT), (HOWTO_IN, HOWTO_OUT), (GALLERY_IN, GALLERY_OUT)], program in readdir(IN; join=true) name = basename(program) - if endswith(program, ".jl") + if endswith(program, "maxwell.jl") if !liveserver script = @timeit dto "script()" @timeit dto name Literate.script(program, OUT) code = strip(read(script, String)) diff --git a/docs/make.jl b/docs/make.jl index d7a082af71..e1eefa6a17 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -45,8 +45,9 @@ bibtex_plugin = CitationBibliography( "Home" => "index.md", # hide("Changelog" => "changelog.md"), "Tutorials" => [ - "Tutorials overview" => "tutorials/index.md", - "tutorials/heat_equation.md", + # "Tutorials overview" => "tutorials/index.md", + "tutorials/maxwell.md", + #= "tutorials/heat_equation.md", "tutorials/linear_elasticity.md", "tutorials/incompressible_elasticity.md", "tutorials/hyperelasticity.md", @@ -55,9 +56,9 @@ bibtex_plugin = CitationBibliography( "tutorials/computational_homogenization.md", "tutorials/stokes-flow.md", "tutorials/ns_vs_diffeq.md", - "tutorials/linear_shell.md", + "tutorials/linear_shell.md",=# ], - "Topic guides" => [ +#= "Topic guides" => [ "Topic guide overview" => "topics/index.md", "topics/fe_intro.md", "topics/degrees_of_freedom.md", @@ -66,7 +67,7 @@ bibtex_plugin = CitationBibliography( "topics/constraints.md", "topics/grid.md", "topics/export.md" - ], + ],=# "Reference" => [ "Reference overview" => "reference/index.md", "reference/quadrature.md", @@ -79,11 +80,11 @@ bibtex_plugin = CitationBibliography( "reference/export.md", "reference/utils.md", ], - "How-to guides" => [ + #= "How-to guides" => [ "How-to guide overview" => "howto/index.md", "howto/postprocessing.md", "howto/threaded_assembly.md", - ], + ],=# "gallery/index.md", # "Code gallery" => [ # "Code gallery overview" => "gallery/index.md", diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl new file mode 100644 index 0000000000..3a8a224f91 --- /dev/null +++ b/docs/src/literate-tutorials/maxwell.jl @@ -0,0 +1,64 @@ +# Maxwell's equations +# For simplicity, we start with the very basic example from +# https://www.math.colostate.edu/~bangerth/videos.676.33.html +# Specifically, +# ```math +# \int_\Omega \left[\mathrm{curl}(\boldsymbol{\delta u}) \cdot \mathrm{curl}(\boldsymbol{u}) +# + \mathrm{div}(\boldsymbol{\delta u}) \mathrm{div}(\boldsymbol{u})\right] \mathrm{d}\Omega = 0 +# ``` +# As noted in the lecture, standard Lagrange elements are not sufficient to solve this problem accurately, +# and we therefore use the Nedelec interpolation. +using Ferrite +import Ferrite: Nedelec +import CairoMakie as M +ip = Nedelec{2,RefTriangle,1}() +grid = generate_grid(Triangle, (2,2)) +dh = DofHandler(grid) +add!(dh, :B, ip) +close!(dh) + +ip_geo = Ferrite.default_interpolation(Triangle) +qr = QuadratureRule{RefTriangle}(10) +cv = CellValues(qr, ip, ip_geo) + +n_qp = getncells(grid)*getnquadpoints(cv) +coords = (zeros(n_qp), zeros(n_qp)) +vectors = (zeros(n_qp), zeros(n_qp)) + +for nr in 1:(ndofs(dh)) + u = zeros(ndofs(dh)) + u[nr] = 1.0 + + for cell_nr in 1:getncells(grid) + x = getcoordinates(grid, cell_nr) + reinit!(cv, x, getcells(grid, cell_nr)) + ue = u[celldofs(dh, cell_nr)] + for q_point in 1:getnquadpoints(cv) + i = getnquadpoints(cv)*(cell_nr-1) + q_point + qp_x = spatial_coordinate(cv, q_point, x) + v = function_value(cv, q_point, ue) + sfac = norm(v) ≈ 0 ? NaN : 1.0 # Skip plotting zero-vector points + coords[1][i] = sfac*qp_x[1] + coords[2][i] = sfac*qp_x[2] + vectors[1][i] = v[1] + vectors[2][i] = v[2] + end + end + + fig = M.Figure() + ax = M.Axis(fig[1,1]; aspect=M.DataAspect()) + for cellnr in 1:getncells(grid) + x = getcoordinates(grid, cellnr) + push!(x, x[1]) + M.lines!(ax, first.(x), last.(x), color=:black) + end + M.arrows!(ax, coords..., vectors...; lengthscale=0.1) + display(fig) +end +#= +mutable struct NewCellCache{T,dim,CT} + const x::Vector{Vec{dim,T}} + const dofs::Vector{Int} + cell::CT +end +=# \ No newline at end of file diff --git a/docs/src/literate-tutorials/maxwell_refel.jl b/docs/src/literate-tutorials/maxwell_refel.jl new file mode 100644 index 0000000000..d3fdb10c88 --- /dev/null +++ b/docs/src/literate-tutorials/maxwell_refel.jl @@ -0,0 +1,60 @@ +# Maxwell's equations +# For simplicity, we start with the very basic example from +# https://www.math.colostate.edu/~bangerth/videos.676.33.html +# Specifically, +# ```math +# \int_\Omega \left[\mathrm{curl}(\boldsymbol{\delta u}) \cdot \mathrm{curl}(\boldsymbol{u}) +# + \mathrm{div}(\boldsymbol{\delta u}) \mathrm{div}(\boldsymbol{u})\right] \mathrm{d}\Omega = 0 +# ``` +# As noted in the lecture, standard Lagrange elements are not sufficient to solve this problem accurately, +# and we therefore use the Nedelec interpolation. +import CairoMakie as M +using Ferrite +import Ferrite: Nedelec +import Ferrite: reference_coordinates +ip = Nedelec{2,RefTriangle,1}() +ip_geo = Lagrange{RefTriangle,1}() +ref_x = reference_coordinates(ip_geo) + +x_vertices = [ref_x..., ref_x[1]] + +function create_testpoints(npts) + inside(x) = (1 - x[1]) > x[2] + x = Vec{2,Float64}[] + for i in 0:(npts-1) + for j in i:(npts-1) + push!(x, Vec((i/(npts-1)), 1 - j/(npts-1))) + end + end + return x +end +x = create_testpoints(10) + +w = ones(length(x))*0.5/length(x) +qr = QuadratureRule{RefTriangle}(w, x) + +fig = M.Figure(resolution=(1000,1000)); +for i in 1:3 + ax=M.Axis(fig[i,1]; aspect=M.DataAspect()); + M.lines!(ax, first.(x_vertices), last.(x_vertices)) + v = shape_value.((ip,), x, i) + M.scatter!(ax, first.(x), last.(x)) + M.arrows!(ax, first.(x), last.(x), first.(v), last.(v); lengthscale=0.25) +end +display(fig) + +fig2 = M.Figure(resolution=(1000,1000)) +cv = CellValues(qr, ip, ip_geo) +ref_x[1] = Vec(4.0,0.0) +reinit!(cv, ref_x) +x_vertices = [ref_x..., ref_x[1]] +for i in 1:3 + ax=M.Axis(fig2[i,1]; aspect=M.DataAspect()); + M.lines!(ax, first.(x_vertices), last.(x_vertices)) + x_qp = spatial_coordinate.((cv,), 1:length(x), (ref_x,)) + @show x_qp ≈ x + v = shape_value.((cv,), 1:length(x), i) + M.scatter!(ax, first.(x_qp), last.(x_qp)) + M.arrows!(ax, first.(x_qp), last.(x_qp), first.(v), last.(v); lengthscale=0.25) +end +fig2 \ No newline at end of file diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index bdb6c5bdad..5eed0c56f2 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -13,7 +13,8 @@ struct CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCell qr::QR end function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T - geo_values = GeometryValues(T, ip_geo.ip, qr) + mapping_type = get_mapping_type(ip_fun) + geo_values = GeometryValues(T, ip_geo.ip, qr, RequiresHessian(requires_hessian(mapping_type))) fun_values = FunctionValues(T, ip_fun, qr, ip_geo) detJdV = fill(T(NaN), length(getweights(qr))) return CellValues(geo_values, detJdV, fun_values, qr) @@ -42,18 +43,20 @@ end # Access quadrature rule values getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) -function reinit!(cv::CellValues, x::AbstractVector{<:Vec}) +function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) geo_values = cv.geo_values + mapping_type = get_mapping_type(cv.fun_values) + map_req = RequiresHessian(requires_hessian(mapping_type)) n_geom_basefuncs = getngeobasefunctions(geo_values) if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs throw_incompatible_coord_length(length(x), n_geom_basefuncs) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) - mapping = calculate_mapping(geo_values, q_point, x) + mapping = calculate_mapping(map_req, geo_values, q_point, x) detJ = calculate_detJ(getjacobian(mapping)) detJ > 0.0 || throw_detJ_not_pos(detJ) @inbounds cv.detJdV[q_point] = detJ*w - apply_mapping!(cv.fun_values, q_point, mapping) + apply_mapping!(cv.fun_values, q_point, mapping, geo_values, cell) end return nothing end diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 30ed0ebfe8..ef71dbaf31 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -23,7 +23,8 @@ typeof_dNdξ(::Type{T}, ::VInterpolationDims{dim,dim,dim}) where {T,dim} = Tenso typeof_dNdξ(::Type{T}, ::VInterpolationDims{rdim,<:Any,vdim}) where {T,rdim,vdim} = SMatrix{vdim,rdim,T} # If vdim=rdim!=sdim Tensor would be possible... struct FunctionValues{IP, N_t, dNdx_t, dNdξ_t} - N::Matrix{N_t} + N_x::Matrix{N_t} + N_ξ::Matrix{N_t} dNdx::Matrix{dNdx_t} dNdξ::Matrix{dNdξ_t} ip::IP @@ -36,10 +37,15 @@ function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) - N = zeros(N_t, n_shape, n_qpoints) + N_ξ = zeros(N_t, n_shape, n_qpoints) + if isa(get_mapping_type(ip), IdentityMapping) + N_x = N_ξ + else + N_x = zeros(N_t, n_shape, n_qpoints) + end dNdξ = zeros(dNdξ_t, n_shape, n_qpoints) dNdx = fill(zero(dNdx_t) * T(NaN), n_shape, n_qpoints) - fv = FunctionValues(N, dNdx, dNdξ, ip) + fv = FunctionValues(N_x, N_ξ, dNdx, dNdξ, ip) precompute_values!(fv, qr) # Precompute N and dNdξ return fv end @@ -48,13 +54,13 @@ function precompute_values!(fv::FunctionValues, qr::QuadratureRule) n_shape = getnbasefunctions(fv.ip) for (qp, ξ) in pairs(getpoints(qr)) for i in 1:n_shape - fv.dNdξ[i, qp], fv.N[i, qp] = shape_gradient_and_value(fv.ip, ξ, i) + fv.dNdξ[i, qp], fv.N_ξ[i, qp] = shape_gradient_and_value(fv.ip, ξ, i) end end end -getnbasefunctions(funvals::FunctionValues) = size(funvals.N, 1) -@propagate_inbounds shape_value(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.N[base_func, q_point] +getnbasefunctions(funvals::FunctionValues) = size(funvals.N_x, 1) +@propagate_inbounds shape_value(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.N_x[base_func, q_point] @propagate_inbounds shape_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.dNdx[base_func, q_point] @propagate_inbounds shape_symmetric_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = symmetric(shape_gradient(funvals, q_point, base_func)) @@ -94,7 +100,7 @@ calculate_Jinv(J::SMatrix) = pinv(J) return apply_mapping!(funvals, get_mapping_type(funvals), args...) end -@inline function apply_mapping!(funvals::FunctionValues, ::IdentityMapping, q_point::Int, mapping_values) +@inline function apply_mapping!(funvals::FunctionValues, ::IdentityMapping, q_point::Int, mapping_values, args...) Jinv = calculate_Jinv(getjacobian(mapping_values)) @inbounds for j in 1:getnbasefunctions(funvals) #funvals.dNdx[j, q_point] = funvals.dNdξ[j, q_point] ⋅ Jinv # TODO via Tensors.jl @@ -102,13 +108,19 @@ end end return nothing end -#= -@inline function apply_mapping!(funvals::FunctionValues, ::IdentityMapping, q_point::Int, mapping_values) + +@inline function apply_mapping!(funvals::FunctionValues, ::CovariantPiolaMapping, q_point::Int, mapping_values, geovals::GeometryValues, cell) + H = gethessian(mapping_values) Jinv = inv(getjacobian(mapping_values)) @inbounds for j in 1:getnbasefunctions(funvals) + face_vertices = faces(cell)[j] + d = face_vertices[2] > face_vertices[1] ? 1 : -1 + #d = 1 #funvals.dNdx[j, q_point] = funvals.dNdξ[j, q_point] ⋅ Jinv # TODO via Tensors.jl - funvals.dNdx[j, q_point] = dothelper(funvals.dNdξ[j, q_point], Jinv) + dNdξ = funvals.dNdξ[j, q_point] + N_ξ = funvals.N_ξ[j, q_point] + funvals.N_x[j, q_point] = d*(N_ξ ⋅ Jinv) + funvals.dNdx[j, q_point] = d*(Jinv' ⋅ dNdξ ⋅ Jinv - Jinv' ⋅ (N_ξ ⋅ Jinv ⋅ H ⋅ Jinv)) end return nothing end -=# \ No newline at end of file diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index e36855b3e0..3ef7971a36 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -5,30 +5,43 @@ end @inline getjacobian(mv::MappingValues) = mv.J @inline gethessian(mv::MappingValues) = mv.H +struct RequiresHessian{B} end +RequiresHessian(B) = RequiresHessian{B}() -struct GeometryValues{dMdξ_t, GIP, T} - M::Matrix{T} - dMdξ::Matrix{dMdξ_t} - ip::GIP +struct GeometryValues{dMdξ_t, GIP, T, d2Mdξ2_t} + M::Matrix{T} # value of geometric shape function + dMdξ::Matrix{dMdξ_t} # gradient of geometric shape function in ref-domain + d2Mdξ2::Matrix{d2Mdξ2_t} # hessian of geometric shape function in ref-domain + ip::GIP # geometric interpolation end -function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T +function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, ::RequiresHessian{RH}) where {T,RH} n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) - VT = Vec{getdim(ip),T} + VT = Vec{getdim(ip),T} M = zeros(T, n_shape, n_qpoints) dMdξ = zeros(VT, n_shape, n_qpoints) + if RH + HT = Tensor{2,getdim(ip),T} + dM2dξ2 = zeros(HT, n_shape, n_qpoints) + else + dM2dξ2 = Matrix{Nothing}(undef,0,0) + end for (qp, ξ) in pairs(getpoints(qr)) for i in 1:n_shape - dMdξ[i, qp], M[i, qp] = shape_gradient_and_value(ip, ξ, i) + if RH + dM2dξ2[i, qp], dMdξ[i, qp], M[i, qp] = shape_hessian_gradient_and_value(ip, ξ, i) + else + dMdξ[i, qp], M[i, qp] = shape_gradient_and_value(ip, ξ, i) + end end end - return GeometryValues(M, dMdξ, ip) + return GeometryValues(M, dMdξ, dM2dξ2, ip) end getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) @propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] -@inline function calculate_mapping(geo_values::GeometryValues{<:Vec{dim,T}}, q_point, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} +@inline function calculate_mapping(::RequiresHessian{false}, geo_values::GeometryValues{<:Vec{dim,T}}, q_point, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} fecv_J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) @inbounds for j in 1:getngeobasefunctions(geo_values) fecv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] @@ -36,6 +49,16 @@ getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) return MappingValues(fecv_J, nothing) end +@inline function calculate_mapping(::RequiresHessian{true}, geo_values::GeometryValues{<:Vec{dim,T}}, q_point, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} + J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) + H = zero(Tensor{3,dim,T}) + @inbounds for j in 1:getngeobasefunctions(geo_values) + J += x[j] ⊗ geo_values.dMdξ[j, q_point] + H += x[j] ⊗ geo_values.d2Mdξ2[j, q_point] + end + return MappingValues(J, H) +end + calculate_detJ(J::Tensor{2}) = det(J) calculate_detJ(J::SMatrix) = embedding_det(J) diff --git a/src/interpolations.jl b/src/interpolations.jl index 6e6ededefa..a235e7bb9c 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -237,6 +237,16 @@ function shape_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int) return gradient(x -> shape_value(ip, x, i), ξ, :all) end +""" + shape_hessian_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int) + +Optimized version combining the evaluation [`Ferrite.shape_value(::Interpolation)`](@ref), +[`Ferrite.shape_gradient(::Interpolation)`](@ref), and the gradient of the latter. +""" +function shape_hessian_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int) + return hessian(x -> shape_value(ip, x, i), ξ, :all) +end + """ reference_coordinates(ip::Interpolation) @@ -1504,8 +1514,7 @@ function shape_gradient_and_value(ipv::VectorizedInterpolation{vdim, shape}, ξ: end reference_coordinates(ip::VectorizedInterpolation) = reference_coordinates(ip.ip) - - +is_discontinuous(::Type{<:VectorizedInterpolation{<:Any, <:Any, <:Any, ip}}) where {ip} = is_discontinuous(ip) get_mapping_type(::Interpolation) = IdentityMapping() # https://defelement.com/elements/raviart-thomas.html @@ -1517,7 +1526,7 @@ facedof_interior_indices(ip::RaviartThomas) = facedof_indices(ip) getnbasefunctions(::RaviartThomas{2,RefTriangle,1}) = 3 facedof_indices(::RaviartThomas{2,RefTriangle,1}) = ((1,), (2,), (3,)) adjust_dofs_during_distribution(::RaviartThomas) = false # Not sure how this works, but should be done for higher orders -# https://defelement.com/elements/examples/triangle-N1div-1.html +# https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-1.html function shape_value(ip::RaviartThomas{2,RefTriangle,1}, ξ::Vec{2}, i::Int) ξ_x, ξ_y = ξ i == 1 && return -ξ @@ -1526,4 +1535,21 @@ function shape_value(ip::RaviartThomas{2,RefTriangle,1}, ξ::Vec{2}, i::Int) throw(ArgumentError("no shape function $i for interpolation $ip")) end -is_discontinuous(::Type{<:VectorizedInterpolation{<:Any, <:Any, <:Any, ip}}) where {ip} = is_discontinuous(ip) + +struct Nedelec{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end +# https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html +function shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) + ξ_x, ξ_y = ξ + i == 1 && return Vec( - ξ_y, ξ_x) + i == 2 && return Vec( - ξ_y, ξ_x - 1) + i == 3 && return Vec(1 - ξ_y, ξ_x) + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +get_mapping_type(::Nedelec) = CovariantPiolaMapping() +#facedof_interior_indices(ip::Nedelec) = facedof_indices(ip) + +getnbasefunctions(::Nedelec{2,RefTriangle,1}) = 3 +facedof_indices(::Nedelec{2,RefTriangle,1}) = ((), (), ()) +facedof_interior_indices(::Nedelec{2,RefTriangle,1}) = ((1,), (2,), (3,)) +adjust_dofs_during_distribution(::Nedelec) = false # Not sure how this works, but should be done for higher orders From ac047245983e7a57900c0e8e861bc88b533995f3 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 19 Sep 2023 21:43:40 +0200 Subject: [PATCH 014/172] Add notes for new iterator --- docs/src/literate-tutorials/maxwell.jl | 1 + src/iterators.jl | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl index 3a8a224f91..159190c0cb 100644 --- a/docs/src/literate-tutorials/maxwell.jl +++ b/docs/src/literate-tutorials/maxwell.jl @@ -55,6 +55,7 @@ for nr in 1:(ndofs(dh)) M.arrows!(ax, coords..., vectors...; lengthscale=0.1) display(fig) end + #= mutable struct NewCellCache{T,dim,CT} const x::Vector{Vec{dim,T}} diff --git a/src/iterators.jl b/src/iterators.jl index dbbc504376..e623ab0d82 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -406,3 +406,16 @@ function _check_same_celltype(grid::AbstractGrid, faceset::Set{FaceIndex}) error("The cells in the faceset are not all of the same celltype.") end end + + +mutable struct CellIterator2{CC,DH,IC} + cache::CC # const + dh::SDH # const + cells::IC # const + current_cell::Int +end +mutable struct CellCache2{T,dim,CT} + const x::Vector{Vec{dim,T}} + const dofs::Vector{Int} + cell::CT +end From 4994e6e08b5955361b9fddf37e91eb7d03fd64e3 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 20 Sep 2023 21:48:42 +0200 Subject: [PATCH 015/172] Generalize and support 2nd order Nedelec on triangle --- docs/src/literate-tutorials/maxwell.jl | 19 ++-- docs/src/literate-tutorials/maxwell_refel.jl | 2 +- src/FEValues/CellValues.jl | 6 +- src/FEValues/FunctionValues.jl | 7 +- src/FEValues/GeometryValues.jl | 8 +- src/interpolations.jl | 103 +++++++++++++++++-- src/iterators.jl | 19 ++-- 7 files changed, 127 insertions(+), 37 deletions(-) diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl index 159190c0cb..7c295e4068 100644 --- a/docs/src/literate-tutorials/maxwell.jl +++ b/docs/src/literate-tutorials/maxwell.jl @@ -11,14 +11,20 @@ using Ferrite import Ferrite: Nedelec import CairoMakie as M -ip = Nedelec{2,RefTriangle,1}() -grid = generate_grid(Triangle, (2,2)) +ip = Nedelec{2,RefTriangle,2}() +grid = generate_grid(Triangle, (1,2)) dh = DofHandler(grid) add!(dh, :B, ip) close!(dh) ip_geo = Ferrite.default_interpolation(Triangle) qr = QuadratureRule{RefTriangle}(10) + +qr_points = Vec{2,Float64}[]; n=6 +append!(qr_points, [Vec((0.0, i/(n+1))) for i in 1:n]) +append!(qr_points, [Vec((i/(n+1), 0.0)) for i in 1:n]) +append!(qr_points, [Vec((i/(n+1), 1 - i/(n+1))) for i in 1:n]) +qr = QuadratureRule{RefTriangle}(zeros(length(qr_points)), qr_points) cv = CellValues(qr, ip, ip_geo) n_qp = getncells(grid)*getnquadpoints(cv) @@ -56,10 +62,9 @@ for nr in 1:(ndofs(dh)) display(fig) end + +# Remaining tasks #= -mutable struct NewCellCache{T,dim,CT} - const x::Vector{Vec{dim,T}} - const dofs::Vector{Int} - cell::CT -end +✓ 2nd order Nedelec +* =# \ No newline at end of file diff --git a/docs/src/literate-tutorials/maxwell_refel.jl b/docs/src/literate-tutorials/maxwell_refel.jl index d3fdb10c88..c61849f88b 100644 --- a/docs/src/literate-tutorials/maxwell_refel.jl +++ b/docs/src/literate-tutorials/maxwell_refel.jl @@ -46,7 +46,7 @@ display(fig) fig2 = M.Figure(resolution=(1000,1000)) cv = CellValues(qr, ip, ip_geo) ref_x[1] = Vec(4.0,0.0) -reinit!(cv, ref_x) +reinit!(cv, ref_x, Triangle((1,2,3))) x_vertices = [ref_x..., ref_x[1]] for i in 1:3 ax=M.Axis(fig2[i,1]; aspect=M.DataAspect()); diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 5eed0c56f2..beaf76ce82 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -45,8 +45,8 @@ getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) geo_values = cv.geo_values - mapping_type = get_mapping_type(cv.fun_values) - map_req = RequiresHessian(requires_hessian(mapping_type)) + fun_values = cv.fun_values + map_req = RequiresHessian(fun_values.ip, geo_values.ip) n_geom_basefuncs = getngeobasefunctions(geo_values) if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs throw_incompatible_coord_length(length(x), n_geom_basefuncs) @@ -56,7 +56,7 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) detJ = calculate_detJ(getjacobian(mapping)) detJ > 0.0 || throw_detJ_not_pos(detJ) @inbounds cv.detJdV[q_point] = detJ*w - apply_mapping!(cv.fun_values, q_point, mapping, geo_values, cell) + apply_mapping!(fun_values, q_point, mapping, cell) end return nothing end diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index ef71dbaf31..1e91d13279 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -109,14 +109,11 @@ end return nothing end -@inline function apply_mapping!(funvals::FunctionValues, ::CovariantPiolaMapping, q_point::Int, mapping_values, geovals::GeometryValues, cell) +@inline function apply_mapping!(funvals::FunctionValues, ::CovariantPiolaMapping, q_point::Int, mapping_values, cell) H = gethessian(mapping_values) Jinv = inv(getjacobian(mapping_values)) @inbounds for j in 1:getnbasefunctions(funvals) - face_vertices = faces(cell)[j] - d = face_vertices[2] > face_vertices[1] ? 1 : -1 - #d = 1 - #funvals.dNdx[j, q_point] = funvals.dNdξ[j, q_point] ⋅ Jinv # TODO via Tensors.jl + d = get_direction(funvals.ip, j, cell) dNdξ = funvals.dNdξ[j, q_point] N_ξ = funvals.N_ξ[j, q_point] funvals.N_x[j, q_point] = d*(N_ξ ⋅ Jinv) diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 3ef7971a36..1bcbacd3b8 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -6,7 +6,13 @@ end @inline gethessian(mv::MappingValues) = mv.H struct RequiresHessian{B} end -RequiresHessian(B) = RequiresHessian{B}() +RequiresHessian(B::Bool) = RequiresHessian{B}() +function RequiresHessian(ip_fun::Interpolation, ip_geo::Interpolation) + # Leave ip_geo as input, because for later the hessian can also be avoided + # for fully linear geometric elements (e.g. triangle and tetrahedron) + # This optimization is left out for now. + RequiresHessian(requires_hessian(get_mapping_type(ip_fun))) +end struct GeometryValues{dMdξ_t, GIP, T, d2Mdξ2_t} M::Matrix{T} # value of geometric shape function diff --git a/src/interpolations.jl b/src/interpolations.jl index a235e7bb9c..2aa7f31e73 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1515,7 +1515,10 @@ end reference_coordinates(ip::VectorizedInterpolation) = reference_coordinates(ip.ip) is_discontinuous(::Type{<:VectorizedInterpolation{<:Any, <:Any, <:Any, ip}}) where {ip} = is_discontinuous(ip) -get_mapping_type(::Interpolation) = IdentityMapping() +get_mapping_type(::ScalarInterpolation) = IdentityMapping() +get_mapping_type(::VectorizedInterpolation) = IdentityMapping() + +#= Just notes for now for RaviartThomas. Nedelec below tbd first # https://defelement.com/elements/raviart-thomas.html # https://defelement.com/elements/qdiv.html @@ -1534,22 +1537,108 @@ function shape_value(ip::RaviartThomas{2,RefTriangle,1}, ξ::Vec{2}, i::Int) i == 3 && return Vec(-ξ_x, 1 - ξ_y) throw(ArgumentError("no shape function $i for interpolation $ip")) end +=# +##################################### +# Nedelec (1st kind), H(curl) # +##################################### struct Nedelec{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end +get_mapping_type(::Nedelec) = CovariantPiolaMapping() + +# RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html function shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) ξ_x, ξ_y = ξ i == 1 && return Vec( - ξ_y, ξ_x) - i == 2 && return Vec( - ξ_y, ξ_x - 1) + i == 2 && return Vec( - ξ_y, ξ_x - 1) # Changed signed, follow Ferrite's sign convention i == 3 && return Vec(1 - ξ_y, ξ_x) throw(ArgumentError("no shape function $i for interpolation $ip")) end -get_mapping_type(::Nedelec) = CovariantPiolaMapping() -#facedof_interior_indices(ip::Nedelec) = facedof_indices(ip) - getnbasefunctions(::Nedelec{2,RefTriangle,1}) = 3 -facedof_indices(::Nedelec{2,RefTriangle,1}) = ((), (), ()) facedof_interior_indices(::Nedelec{2,RefTriangle,1}) = ((1,), (2,), (3,)) -adjust_dofs_during_distribution(::Nedelec) = false # Not sure how this works, but should be done for higher orders +adjust_dofs_during_distribution(::Nedelec{2,RefTriangle,1}) = false + +function get_direction(::Nedelec{2,RefTriangle,1}, j, cell) + face_vertices = faces(cell)[j] + return face_vertices[2] > face_vertices[1] ? 1 : -1 +end + +# RefTriangle, 2nd order Lagrange +# https://defelement.com/elements/examples/triangle-nedelec1-lagrange-2.html +function shape_value(ip::Nedelec{2,RefTriangle,2}, ξ::Vec{2}, i::Int) + ξ_x, ξ_y = ξ + # Face 1 + i == 1 && return Vec( 2*ξ_y*(1 - 4*ξ_x), + 4*ξ_x*(2*ξ_x - 1)) + i == 2 && return Vec( 4*ξ_y*(1 - 2*ξ_y), + 2*ξ_x*(4*ξ_y - 1)) + # Face 2 (flip order and sign compared to defelement) + i == 3 && return Vec( 4*ξ_y*(1 - 2*ξ_y), + 8*ξ_x*ξ_y - 2*ξ_x - 6*ξ_y + 2) + i == 4 && return Vec( 2*ξ_y*(4*ξ_x + 4*ξ_y - 3), + -8*ξ_x^2 - 8*ξ_x*ξ_y + 12*ξ_x + 6*ξ_y - 4) + # Face 3 + i == 5 && return Vec( 8*ξ_x*ξ_y - 6*ξ_x + 8*ξ_y^2 - 12*ξ_y + 4, + 2*ξ_x*(-4*ξ_x - 4*ξ_y + 3)) + i == 6 && return Vec(-8*ξ_x*ξ_y + 6*ξ_x + 2*ξ_y - 2, + 4*ξ_x*(2*ξ_x - 1)) + # Cell + i == 7 && return Vec( 8*ξ_y*(-ξ_x - 2*ξ_y + 2), + 8*ξ_x*( ξ_x + 2*ξ_y - 1)) + i == 8 && return Vec( 8*ξ_y*( 2*ξ_x + ξ_y - 1), + 8*ξ_x*(-2*ξ_x - ξ_y + 2)) + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::Nedelec{2,RefTriangle,2}) = 8 +facedof_interior_indices(::Nedelec{2,RefTriangle,2}) = ((1,2), (3,4), (5,6)) +celldof_interior_indices(::Nedelec{2,RefTriangle,2}) = (7,8) +adjust_dofs_during_distribution(::Nedelec{2,RefTriangle,2}) = true + +function get_direction(::Nedelec{2,RefTriangle,2}, j, cell) + j>6 && return 1 + facenr = (j+1)÷2 + face_vertices = faces(cell)[facenr] + return face_vertices[2] > face_vertices[1] ? 1 : -1 +end + +# RefTetrahedron, 1st order Lagrange - 𝐍𝐎𝐓 𝐓𝐄𝐒𝐓𝐄𝐃 +# https://defelement.com/elements/examples/tetrahedron-nedelec1-lagrange-1.html +function shape_value(ip::Nedelec{2,RefTetrahedron,1}, ξ::Vec{3,T}, i::Int) where T + x, y, z = ξ + # Edge 1 (defelement 5, positive) + i == 1 && return Vec(- y - z + 1, x, x) + # Edge 2 (defelement 2, positive) + i == 2 && return Vec(-y, x, zero(T)) + # Edge 3 (defelement 4, negative) + i == 3 && return Vec(-y, x + z - 1, -y) + # Edge 4 (defelement 3, positive) + i == 4 && return Vec(z, z, -x - y + 1) + # Edge 5 (defelement 1, positive) + i == 5 && return Vec(-z, zero(T), x) + # Edge 6 (defelement 0, positive) + i == 6 && return Vec(zero(T), -z, y) + + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::Nedelec{2,RefTetrahedron,1}) = 6 +facedof_interior_indices(::Nedelec{2,RefTetrahedron,1}) = ntuple(i->(i,), 6) +adjust_dofs_during_distribution(::Nedelec{2,RefTetrahedron,1}) = false + +function get_direction(::Nedelec{2,RefTetrahedron,1}, j, cell) + edge_vertices = edges(cell)[j] + return edge_vertices[2] > edge_vertices[1] ? 1 : -1 +end + +# RefTetrahedron, 2nd order Lagrange +# https://defelement.com/elements/examples/tetrahedron-nedelec1-lagrange-2.html +#= Notes +The dofs belong to faces seem to require extra consideration, but not sure. +Basically, we need to make sure they are aligned with the same edges that they refer to. +It might be that this works automatically, following `adjust_dofs_during_distribution`, +but test cases need to be developed first to make sure. +No point in implementing guesses that cannot be verified... +=# \ No newline at end of file diff --git a/src/iterators.jl b/src/iterators.jl index e623ab0d82..258230623b 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -374,7 +374,6 @@ function Base.iterate(ii::InterfaceIterator, state...) end end - # Iterator interface for CellIterator/FaceIterator const GridIterators{C} = Union{CellIterator{C}, FaceIterator{C}, InterfaceIterator{C}} @@ -407,15 +406,9 @@ function _check_same_celltype(grid::AbstractGrid, faceset::Set{FaceIndex}) end end - -mutable struct CellIterator2{CC,DH,IC} - cache::CC # const - dh::SDH # const - cells::IC # const - current_cell::Int -end -mutable struct CellCache2{T,dim,CT} - const x::Vector{Vec{dim,T}} - const dofs::Vector{Int} - cell::CT -end +#= Remaining tasks +1) Test on quadrilateral +2) Generalize to 2nd order case +3) Generalize to 3d-face +4) Consider RaviartThomas elements for 0-3 as well. +=# \ No newline at end of file From f62961c12b869834630ad68c036496fe40325aa6 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 20 Sep 2023 21:53:22 +0200 Subject: [PATCH 016/172] Add citation from master --- src/FEValues/GeometryValues.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 1bcbacd3b8..d1a6300d04 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -81,7 +81,7 @@ The transformation theorem for some function f on a 2D surface in 3D space leads ∫ f ⋅ dS = ∫ f ⋅ (∂x/∂ξ₁ × ∂x/∂ξ₂) dξ₁dξ₂ = ∫ f ⋅ n ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ dξ₁dξ₂ where ||∂x/∂ξ₁ × ∂x/∂ξ₂||₂ is "detJ" and n is the unit normal. See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-dimensional-functions-on-finite-element-surfaces for simple explanation. -For more details see e.g. the doctoral thesis by Mirza Cenanovic **Finite element methods for surface problems* (2017), Ch. 2 **Trangential Calculus**. +For more details see e.g. the doctoral thesis by Mirza Cenanovic **Tangential Calculus** [Cenanovic2017](@cite). """ embedding_det(J::SMatrix{3,2}) = norm(J[:,1] × J[:,2]) From eeb1245fbca2cb85afded775047635dc0d5c386e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 20 Sep 2023 21:53:40 +0200 Subject: [PATCH 017/172] Empty cell_values.jl --- src/FEValues/cell_values.jl | 252 ------------------------------------ 1 file changed, 252 deletions(-) diff --git a/src/FEValues/cell_values.jl b/src/FEValues/cell_values.jl index d7480df1d8..e69de29bb2 100644 --- a/src/FEValues/cell_values.jl +++ b/src/FEValues/cell_values.jl @@ -1,252 +0,0 @@ -""" - OldCellValues([::Type{T},] quad_rule::QuadratureRule, func_interpol::Interpolation, [geom_interpol::Interpolation]) - -A `OldCellValues` object facilitates the process of evaluating values of shape functions, gradients of shape functions, -values of nodal functions, gradients and divergences of nodal functions etc. in the finite element cell. - -**Arguments:** -* `T`: an optional argument (default to `Float64`) to determine the type the internal data is stored as. -* `quad_rule`: an instance of a [`QuadratureRule`](@ref) -* `func_interpol`: an instance of an [`Interpolation`](@ref) used to interpolate the approximated function -* `geom_interpol`: an optional instance of a [`Interpolation`](@ref) which is used to interpolate the geometry. - By default linear Lagrange interpolation is used. For embedded elements the geometric interpolations should - be vectorized to the spatial dimension. - -**Common methods:** - -* [`reinit!`](@ref) -* [`getnquadpoints`](@ref) -* [`getdetJdV`](@ref) - -* [`shape_value`](@ref) -* [`shape_gradient`](@ref) -* [`shape_symmetric_gradient`](@ref) -* [`shape_divergence`](@ref) - -* [`function_value`](@ref) -* [`function_gradient`](@ref) -* [`function_symmetric_gradient`](@ref) -* [`function_divergence`](@ref) -* [`spatial_coordinate`](@ref) -""" -OldCellValues - -struct OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCellValues - N::Matrix{N_t} - dNdx::Matrix{dNdx_t} - dNdξ::Matrix{dNdξ_t} - detJdV::Vector{T} - M::Matrix{T} - dMdξ::Matrix{dMdξ_t} - qr::QR - ip::IP - gip::GIP -end - -# Common initializer code for constructing OldCellValues after the types have been determined -function OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(qr::QR, ip::IP, gip::GIP) where { - IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP, -} - @assert isconcretetype(IP) && isconcretetype(N_t) && isconcretetype(dNdx_t) && - isconcretetype(dNdξ_t) && isconcretetype(T) && isconcretetype(dMdξ_t) && - isconcretetype(QR) && isconcretetype(GIP) - n_qpoints = getnquadpoints(qr) - - # Field interpolation - n_func_basefuncs = getnbasefunctions(ip) - N = fill(zero(N_t) * T(NaN), n_func_basefuncs, n_qpoints) - dNdx = fill(zero(dNdx_t) * T(NaN), n_func_basefuncs, n_qpoints) - dNdξ = fill(zero(dNdξ_t) * T(NaN), n_func_basefuncs, n_qpoints) - - # Geometry interpolation - n_geom_basefuncs = getnbasefunctions(gip) - M = fill(zero(T) * T(NaN), n_geom_basefuncs, n_qpoints) - dMdξ = fill(zero(dMdξ_t) * T(NaN), n_geom_basefuncs, n_qpoints) - - for (qp, ξ) in pairs(getpoints(qr)) - for basefunc in 1:n_func_basefuncs - dNdξ[basefunc, qp], N[basefunc, qp] = shape_gradient_and_value(ip, ξ, basefunc) - end - for basefunc in 1:n_geom_basefuncs - dMdξ[basefunc, qp], M[basefunc, qp] = shape_gradient_and_value(gip, ξ, basefunc) - end - end - - detJdV = fill(T(NaN), n_qpoints) - - OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(N, dNdx, dNdξ, detJdV, M, dMdξ, qr, ip, gip) -end - -# Common entry point that fills in the numeric type and geometric interpolation -function OldCellValues(qr::QuadratureRule, ip::Interpolation, - gip::Interpolation = default_geometric_interpolation(ip)) - return OldCellValues(Float64, qr, ip, gip) -end - -# Common entry point that fills in the geometric interpolation -function OldCellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation) where {T} - return OldCellValues(T, qr, ip, default_geometric_interpolation(ip)) -end - -# Common entry point that vectorizes an input scalar geometric interpolation -function OldCellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation, sgip::ScalarInterpolation) where {T} - return OldCellValues(T, qr, ip, VectorizedInterpolation(sgip)) -end - -# Entrypoint for `ScalarInterpolation`s (rdim == sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - dim, shape <: AbstractRefShape{dim}, T, - QR <: QuadratureRule{shape}, - IP <: ScalarInterpolation{shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{dim, shape, <:Any, GIP}, -} - # Function interpolation - N_t = T - dNdx_t = dNdξ_t = Vec{dim, T} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{dim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) -end - -# Entrypoint for `VectorInterpolation`s (vdim == rdim == sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - dim, shape <: AbstractRefShape{dim}, T, - QR <: QuadratureRule{shape}, - IP <: VectorInterpolation{dim, shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{dim, shape, <:Any, GIP}, -} - # Field interpolation - N_t = Vec{dim, T} - dNdx_t = dNdξ_t = Tensor{2, dim, T, Tensors.n_components(Tensor{2,dim})} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{dim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) -end - -# Entrypoint for `VectorInterpolation`s (vdim != rdim == sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, vgip::VGIP) where { - vdim, dim, shape <: AbstractRefShape{dim}, T, - QR <: QuadratureRule{shape}, - IP <: VectorInterpolation{vdim, shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{dim, shape, <:Any, GIP}, -} - # Field interpolation - N_t = SVector{vdim, T} - dNdx_t = dNdξ_t = SMatrix{vdim, dim, T, vdim*dim} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{dim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, vgip.ip) -end - -# reinit! for regular (non-embedded) elements (rdim == sdim) -function reinit!(cv::OldCellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{Vec{dim,T}}) where { - dim, T, vdim, - N_t <: Union{Number, Vec{dim}, SVector{vdim} }, - dNdx_t <: Union{Vec{dim}, Tensor{2, dim}, SMatrix{vdim, dim}}, - dNdξ_t <: Union{Vec{dim}, Tensor{2, dim}, SMatrix{vdim, dim}}, -} - n_geom_basefuncs = getngeobasefunctions(cv) - n_func_basefuncs = getnbasefunctions(cv) - length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) - - @inbounds for (i, w) in pairs(getweights(cv.qr)) - fecv_J = zero(Tensor{2,dim,T}) - for j in 1:n_geom_basefuncs - fecv_J += x[j] ⊗ cv.dMdξ[j, i] - end - detJ = det(fecv_J) - detJ > 0.0 || throw_detJ_not_pos(detJ) - cv.detJdV[i] = detJ * w - Jinv = inv(fecv_J) - for j in 1:n_func_basefuncs - # cv.dNdx[j, i] = cv.dNdξ[j, i] ⋅ Jinv - cv.dNdx[j, i] = dothelper(cv.dNdξ[j, i], Jinv) - end - end -end - -# Entrypoint for embedded `ScalarInterpolation`s (rdim < sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - sdim, rdim, shape <: AbstractRefShape{rdim}, T, - QR <: QuadratureRule{shape}, - IP <: ScalarInterpolation{shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{sdim, shape, <:Any, GIP}, -} - @assert sdim > rdim - # Function interpolation - N_t = T - dNdx_t = SVector{sdim, T} - dNdξ_t = SVector{rdim, T} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{rdim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) -end - -# Entrypoint for embedded `VectorInterpolation`s (rdim < sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - sdim, vdim, rdim, shape <: AbstractRefShape{rdim}, T, - QR <: QuadratureRule{shape}, - IP <: VectorInterpolation{vdim, shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{sdim, shape, <:Any, GIP}, -} - @assert sdim > rdim - # Function interpolation - N_t = SVector{vdim, T} - dNdx_t = SMatrix{vdim, sdim, T, vdim*sdim} - dNdξ_t = SMatrix{vdim, rdim, T, vdim*rdim} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{rdim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) -end - -# reinit! for embedded elements, rdim < sdim -function reinit!(cv::OldCellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{Vec{sdim,T}}) where { - rdim, sdim, vdim, T, - N_t <: Union{Number, SVector{vdim}}, - dNdx_t <: Union{SVector{sdim, T}, SMatrix{vdim, sdim, T}}, - dNdξ_t <: Union{SVector{rdim, T}, SMatrix{vdim, rdim, T}}, -} - @assert sdim > rdim "This reinit only works for embedded elements. Maybe you swapped the reference and spatial dimensions?" - n_geom_basefuncs = getngeobasefunctions(cv) - n_func_basefuncs = getnbasefunctions(cv) - length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) - - @inbounds for (i, w) in pairs(getweights(cv.qr)) - fecv_J = zero(MMatrix{sdim, rdim, T}) # TODO replace with MixedTensor (see https://github.com/Ferrite-FEM/Tensors.jl/pull/188) - for j in 1:n_geom_basefuncs - #fecv_J += x[j] ⊗ cv.dMdξ[j, i] # TODO via Tensors.jl - for k in 1:sdim, l in 1:rdim - fecv_J[k, l] += x[j][k] * cv.dMdξ[j, i][l] - end - end - fecv_J = SMatrix(fecv_J) - detJ = embedding_det(fecv_J) - detJ > 0.0 || throw_detJ_not_pos(detJ) - cv.detJdV[i] = detJ * w - # Compute "left inverse" of J - Jinv = pinv(fecv_J) - for j in 1:n_func_basefuncs - #cv.dNdx[j, i] = cv.dNdξ[j, i] ⋅ Jinv # TODO via Tensors.jl - cv.dNdx[j, i] = dothelper(cv.dNdξ[j, i], Jinv) - end - end - return nothing -end - -function Base.show(io::IO, m::MIME"text/plain", cv::OldCellValues) - println(io, "CellValues with") - println(io, "- Quadrature rule with ", getnquadpoints(cv), " points") - print(io, "- Function interpolation: "); show(io, m, cv.ip) - println(io) - print(io, "- Geometric interpolation: "); show(io, m, cv.gip) -end \ No newline at end of file From 6f7f98813e128006d3ba135e15f3170aee727bea Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 20 Sep 2023 22:27:06 +0200 Subject: [PATCH 018/172] Use mapping requirement info in GeometryValues --- src/FEValues/CellValues.jl | 7 +++---- src/FEValues/FaceValues.jl | 10 +++++----- src/FEValues/GeometryValues.jl | 6 +++++- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index beaf76ce82..8cdf9b1ba0 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -6,8 +6,8 @@ function default_geometric_interpolation(::Interpolation{shape}) where {dim, sha return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) end -struct CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCellValues - geo_values::GeometryValues{dMdξ_t, GIP, T} +struct CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP, d2Mdξ2_t} <: AbstractCellValues + geo_values::GeometryValues{dMdξ_t, GIP, T, d2Mdξ2_t} detJdV::Vector{T} fun_values::FunctionValues{IP, N_t, dNdx_t, dNdξ_t} qr::QR @@ -46,13 +46,12 @@ getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) geo_values = cv.geo_values fun_values = cv.fun_values - map_req = RequiresHessian(fun_values.ip, geo_values.ip) n_geom_basefuncs = getngeobasefunctions(geo_values) if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs throw_incompatible_coord_length(length(x), n_geom_basefuncs) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) - mapping = calculate_mapping(map_req, geo_values, q_point, x) + mapping = calculate_mapping(geo_values, q_point, x) detJ = calculate_detJ(getjacobian(mapping)) detJ > 0.0 || throw_detJ_not_pos(detJ) @inbounds cv.detJdV[q_point] = detJ*w diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 6ad5db4ef0..9d8ebec0c3 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -1,5 +1,5 @@ -struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: AbstractFaceValues - geo_values::Vector{GeometryValues{dMdξ_t, GIP, T}} +struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP, d2Mdξ2_t} <: AbstractFaceValues + geo_values::Vector{GeometryValues{dMdξ_t, GIP, T, d2Mdξ2_t}} detJdV::Vector{T} normals::Vector{Normal_t} fun_values::Vector{FunctionValues{IP, N_t, dNdx_t, dNdξ_t}} @@ -8,7 +8,7 @@ struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: Ab end function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun)) where {T,sdim} - geo_values = [GeometryValues(T, ip_geo.ip, qr) for qr in fqr.face_rules] + geo_values = [GeometryValues(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) for qr in fqr.face_rules] fun_values = [FunctionValues(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) detJdV = fill(T(NaN), max_nquadpoints) @@ -63,7 +63,7 @@ function checkface(fv::FaceValues, face::Int) return nothing end -function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int) where {dim, T} +function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, cell=nothing) where {dim, T} @boundscheck checkface(fv, face_nr) n_geom_basefuncs = getngeobasefunctions(fv) length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) @@ -80,7 +80,7 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int) wh detJ > 0.0 || throw_detJ_not_pos(detJ) @inbounds fv.detJdV[q_point] = detJ*w @inbounds fv.normals[q_point] = weight_norm / norm(weight_norm) - apply_mapping!(fun_values, q_point, mapping) + apply_mapping!(fun_values, q_point, mapping, cell) end end diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index d1a6300d04..349c1bbc9c 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -43,10 +43,14 @@ function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, end return GeometryValues(M, dMdξ, dM2dξ2, ip) end +RequiresHessian(::GeometryValues{<:Any,<:Any,<:Any,Nothing}) = RequiresHessian(false) +RequiresHessian(::GeometryValues) = RequiresHessian(true) getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) @propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] +@propagate_inbounds calculate_mapping(geovals::GeometryValues, args...) = calculate_mapping(RequiresHessian(geovals), geovals, args...) + @inline function calculate_mapping(::RequiresHessian{false}, geo_values::GeometryValues{<:Vec{dim,T}}, q_point, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} fecv_J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) @inbounds for j in 1:getngeobasefunctions(geo_values) @@ -99,7 +103,7 @@ See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-di """ embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) = norm(J) -@inline function calculate_mapping(geo_values::GeometryValues{<:Vec{rdim,T}}, q_point, x::AbstractVector{<:Vec{sdim,T}}) where {rdim,sdim,T} +@inline function calculate_mapping(::RequiresHessian{false}, geo_values::GeometryValues{<:Vec{rdim,T}}, q_point, x::AbstractVector{<:Vec{sdim,T}}) where {rdim,sdim,T} n_geom_basefuncs = getngeobasefunctions(geo_values) fecv_J = zero(MMatrix{sdim, rdim, T}) # TODO replace with MixedTensor (see https://github.com/Ferrite-FEM/Tensors.jl/pull/188) for j in 1:n_geom_basefuncs From f1020f3da5a9dafef8a57772f5bf5e759d900775 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Thu, 21 Sep 2023 18:20:44 +0200 Subject: [PATCH 019/172] Add test for gradient and continuity of Nedelec --- docs/Manifest.toml | 20 ++--- docs/Project.toml | 1 + docs/src/literate-tutorials/maxwell.jl | 73 +++++++++------- src/interpolations.jl | 10 +-- test/InterpolationTestUtils.jl | 116 +++++++++++++++++++++++++ test/test_interpolations.jl | 35 ++++++++ 6 files changed, 210 insertions(+), 45 deletions(-) create mode 100644 test/InterpolationTestUtils.jl diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 97f82d95b6..00a9f3dfcf 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.9.2" manifest_format = "2.0" -project_hash = "0444e709e1c6f42386a9ac2b8c455eb5004d33dc" +project_hash = "f4ba79c26c9d5fa21c707a95d5a76911eedc0ccf" [[deps.ADTypes]] git-tree-sha1 = "5d2e21d7b0d8c22f67483ef95ebdc39c0e6b6003" @@ -460,6 +460,12 @@ git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.4" +[[deps.EpollShim_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643" +uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" +version = "0.0.20230411+0" + [[deps.ErrorfreeArithmetic]] git-tree-sha1 = "d6863c556f1142a061532e79f611aa46be201686" uuid = "90fa49ef-747e-5e6f-a989-263ba693cf1a" @@ -1680,8 +1686,8 @@ uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" version = "1.0.0" [[deps.Qt6Base_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] -git-tree-sha1 = "ea513c73c8f657985e8fb9b8067dd7cf306adc35" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] +git-tree-sha1 = "364898e8f13f7eaaceec55fd3d08680498c0aa6e" uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" version = "6.4.2+3" @@ -2272,14 +2278,8 @@ git-tree-sha1 = "8351f8d73d7e880bfc042a8b6922684ebeafb35c" uuid = "19fa3120-7c27-5ec5-8db8-b0b0aa330d6f" version = "0.2.0" -[[deps.Vulkan_Loader_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] -git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" -uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" -version = "1.3.243+0" - [[deps.Wayland_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] +deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] git-tree-sha1 = "ed8d92d9774b077c53e1da50fd81a36af3744c1c" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" version = "1.21.0+0" diff --git a/docs/Project.toml b/docs/Project.toml index 01eb9b476d..92114eb028 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -17,6 +17,7 @@ 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" Tensors = "48a634ad-e948-5137-8d70-aa71f2a747f4" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl index 7c295e4068..a09a5631e2 100644 --- a/docs/src/literate-tutorials/maxwell.jl +++ b/docs/src/literate-tutorials/maxwell.jl @@ -8,6 +8,9 @@ # ``` # As noted in the lecture, standard Lagrange elements are not sufficient to solve this problem accurately, # and we therefore use the Nedelec interpolation. +import LinearAlgebra: normalize +using StaticArrays +using Test using Ferrite import Ferrite: Nedelec import CairoMakie as M @@ -27,44 +30,54 @@ append!(qr_points, [Vec((i/(n+1), 1 - i/(n+1))) for i in 1:n]) qr = QuadratureRule{RefTriangle}(zeros(length(qr_points)), qr_points) cv = CellValues(qr, ip, ip_geo) -n_qp = getncells(grid)*getnquadpoints(cv) -coords = (zeros(n_qp), zeros(n_qp)) -vectors = (zeros(n_qp), zeros(n_qp)) +function plot_shapes(dh, cv) + grid = dh.grid + n_qp = getncells(grid)*getnquadpoints(cv) + coords = (zeros(n_qp), zeros(n_qp)) + vectors = (zeros(n_qp), zeros(n_qp)) -for nr in 1:(ndofs(dh)) - u = zeros(ndofs(dh)) - u[nr] = 1.0 + for nr in 1:(ndofs(dh)) + u = zeros(ndofs(dh)) + u[nr] = 1.0 - for cell_nr in 1:getncells(grid) - x = getcoordinates(grid, cell_nr) - reinit!(cv, x, getcells(grid, cell_nr)) - ue = u[celldofs(dh, cell_nr)] - for q_point in 1:getnquadpoints(cv) - i = getnquadpoints(cv)*(cell_nr-1) + q_point - qp_x = spatial_coordinate(cv, q_point, x) - v = function_value(cv, q_point, ue) - sfac = norm(v) ≈ 0 ? NaN : 1.0 # Skip plotting zero-vector points - coords[1][i] = sfac*qp_x[1] - coords[2][i] = sfac*qp_x[2] - vectors[1][i] = v[1] - vectors[2][i] = v[2] + for cell_nr in 1:getncells(grid) + x = getcoordinates(grid, cell_nr) + reinit!(cv, x, getcells(grid, cell_nr)) + ue = u[celldofs(dh, cell_nr)] + for q_point in 1:getnquadpoints(cv) + i = getnquadpoints(cv)*(cell_nr-1) + q_point + qp_x = spatial_coordinate(cv, q_point, x) + v = function_value(cv, q_point, ue) + sfac = norm(v) ≈ 0 ? NaN : 1.0 # Skip plotting zero-vector points + coords[1][i] = sfac*qp_x[1] + coords[2][i] = sfac*qp_x[2] + vectors[1][i] = v[1] + vectors[2][i] = v[2] + end end - end - fig = M.Figure() - ax = M.Axis(fig[1,1]; aspect=M.DataAspect()) - for cellnr in 1:getncells(grid) - x = getcoordinates(grid, cellnr) - push!(x, x[1]) - M.lines!(ax, first.(x), last.(x), color=:black) + fig = M.Figure() + ax = M.Axis(fig[1,1]; aspect=M.DataAspect()) + for cellnr in 1:getncells(grid) + x = getcoordinates(grid, cellnr) + push!(x, x[1]) + M.lines!(ax, first.(x), last.(x), color=:black) + end + M.arrows!(ax, coords..., vectors...; lengthscale=0.1) + display(fig) end - M.arrows!(ax, coords..., vectors...; lengthscale=0.1) - display(fig) + return nothing end +# plot_shapes(dh, cv) # Remaining tasks #= ✓ 2nd order Nedelec -* -=# \ No newline at end of file +* Develop test cases for + a) Continuity + b) Gradient check +=# + + +test_gradient(dh, 1) diff --git a/src/interpolations.jl b/src/interpolations.jl index 2aa7f31e73..285c19dca5 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1606,7 +1606,7 @@ end # RefTetrahedron, 1st order Lagrange - 𝐍𝐎𝐓 𝐓𝐄𝐒𝐓𝐄𝐃 # https://defelement.com/elements/examples/tetrahedron-nedelec1-lagrange-1.html -function shape_value(ip::Nedelec{2,RefTetrahedron,1}, ξ::Vec{3,T}, i::Int) where T +function shape_value(ip::Nedelec{3,RefTetrahedron,1}, ξ::Vec{3,T}, i::Int) where T x, y, z = ξ # Edge 1 (defelement 5, positive) i == 1 && return Vec(- y - z + 1, x, x) @@ -1624,11 +1624,11 @@ function shape_value(ip::Nedelec{2,RefTetrahedron,1}, ξ::Vec{3,T}, i::Int) wher throw(ArgumentError("no shape function $i for interpolation $ip")) end -getnbasefunctions(::Nedelec{2,RefTetrahedron,1}) = 6 -facedof_interior_indices(::Nedelec{2,RefTetrahedron,1}) = ntuple(i->(i,), 6) -adjust_dofs_during_distribution(::Nedelec{2,RefTetrahedron,1}) = false +getnbasefunctions(::Nedelec{3,RefTetrahedron,1}) = 6 +edgedof_interior_indices(::Nedelec{3,RefTetrahedron,1}) = ntuple(i->(i,), 6) +adjust_dofs_during_distribution(::Nedelec{3,RefTetrahedron,1}) = false -function get_direction(::Nedelec{2,RefTetrahedron,1}, j, cell) +function get_direction(::Nedelec{3,RefTetrahedron,1}, j, cell) edge_vertices = edges(cell)[j] return edge_vertices[2] > edge_vertices[1] ? 1 : -1 end diff --git a/test/InterpolationTestUtils.jl b/test/InterpolationTestUtils.jl new file mode 100644 index 0000000000..612be6df5b --- /dev/null +++ b/test/InterpolationTestUtils.jl @@ -0,0 +1,116 @@ +module InterpolationTestUtils + using Ferrite + using Test + import LinearAlgebra: normalize + import Random: randperm + + function find_matching_face(grid, face::FaceIndex) + cell, facenr = face + face_vertices = Set(Ferrite.faces(getcells(grid, cell))[facenr]) + for cnr in 1:getncells(grid) + cnr == cell && continue + for (i, f_vert) in enumerate(Ferrite.faces(getcells(grid, cnr))) + face_vertices == Set(f_vert) && return FaceIndex(cnr, i) + end + end + return nothing + end + + function test_continuity(dh::DofHandler, face::FaceIndex; + transformation_function::Function=identity, + value_function::Function=function_value) + # transformation_function: (v,n) -> z + # Examples + # * Tangential continuity: fun(v, n) = v - (v ⋅ n)*n + # * Normal continuity: fun(v, n) = v ⋅ n + # value_function: (fe_v, q_point, ue) -> z + + # Check validity of input + @assert length(dh.subdofhandlers) == 1 + @assert Ferrite.nfields(dh) == 1 + + # Find the matching FaceIndex + cellnr, facenr = face + face2 = find_matching_face(dh.grid, face) + face2 === nothing && return false + + # Pick "random" points on the face + cell = getcells(dh.grid, cellnr) + RefShape = Ferrite.getrefshape(getcells(dh.grid, cellnr)) + ip_geo = Ferrite.default_interpolation(typeof(cell)) + ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) + fqr = FaceQuadratureRule{RefShape}(10) + fv = FaceValues(fqr, ip_fun, ip_geo) + cell_coords = getcoordinates(dh.grid, cellnr) + inds = randperm(getnquadpoints(fv))[1:min(4, getnquadpoints(fv))] + + # Random dof vector to test continuity + u = rand(ndofs(dh)) + + # Calculate coordinates and function values for these + point_coords = zeros(eltype(cell_coords), length(inds)) + point_normal = similar(point_coords) + fun_vals = zeros(typeof(shape_value(fv, 1, 1)), length(inds)) + reinit!(fv, cell_coords, facenr, cell) + ue = u[celldofs(dh, cellnr)] + for (i, q_point) in enumerate(inds) + point_coords[i] = spatial_coordinate(fv, q_point, cell_coords) + point_normal[i] = getnormal(fv, q_point) + fun_vals[i] = value_function(fv, q_point, ue) + end + + # Calculate function values on the other cell + cell2 = getcells(dh.grid, face2[1]) + cell_coords2 = getcoordinates(dh.grid, face2[1]) + local_coords = map(x->Ferrite.find_local_coordinate(ip_geo, cell_coords2, x), point_coords) + @assert all(first.(local_coords)) # check that find_local_coordinate converged + ξs = collect(last.(local_coords)) # Extract the local coordinates + qr = QuadratureRule{RefShape}(zeros(length(ξs)), ξs) + cv = CellValues(qr, ip_fun, ip_geo) + reinit!(cv, cell_coords2, cell2) + fun_vals2 = similar(fun_vals) + ue2 = u[celldofs(dh, face2[1])] + for q_point in 1:getnquadpoints(cv) + @assert spatial_coordinate(cv, q_point, cell_coords2) ≈ point_coords[q_point] + fun_vals2[q_point] = value_function(cv, q_point, ue2) + end + + d1 = map((v,n)->transformation_function(v,n), fun_vals, point_normal) + d2 = map((v,n)->transformation_function(v,n), fun_vals2, point_normal) + @test d1 ≈ d2 + return true + end + + function create_gradcheck_qr(ip_geo::Interpolation{RefShape}, ΔL) where RefShape + dim = Ferrite.getdim(ip_geo) + xref = Ferrite.reference_coordinates(ip_geo) + xc = sum(xref)/length(xref) + ws = rand(length(xref))*((1-ΔL)/length(xref)) + xp = xc + sum(map((x,w) -> w*(x - xc), xref, ws)) + v = normalize(rand(Vec{dim}) - ones(Vec{dim})/2) + x1 = xp + ΔL*v + qr_w = [NaN, NaN] + qr_x = [xp, x1] + return QuadratureRule{RefShape}(qr_w, qr_x) + end + + function test_gradient(dh, cellnr; ΔL=1e-6) + ue = rand(ndofs_per_cell(dh, cellnr)) + x = getcoordinates(dh.grid, cellnr) + cell = getcells(dh.grid, cellnr) + ip_geo = Ferrite.default_interpolation(typeof(cell)) + ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) + qr = create_gradcheck_qr(ip_geo, ΔL) + cv = CellValues(qr, ip_fun, ip_geo) + reinit!(cv, x, cell) + Δu_num = function_value(cv, 2, ue) - function_value(cv, 1, ue) + Δx = spatial_coordinate(cv, 2, x) - spatial_coordinate(cv, 1, x) + ∇u1 = function_gradient(cv, 1, ue) + ∇u2 = function_gradient(cv, 2, ue) + Δu_ana = 0.5*(∇u1+∇u2) ⋅ Δx + # Δu_ana_var = 0.5*(∇u2-∇u1) ⋅ Δx # Relevant to compare magnitude if test fails + @test Δu_num ≈ Δu_ana + return nothing + end + +end \ No newline at end of file diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index b6653b4491..ed8f20f225 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,3 +1,4 @@ + @testset "interpolations" begin @testset "$interpolation" for interpolation in ( @@ -178,4 +179,38 @@ end @test Ferrite.is_discontinuous(ip_t) == false @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true + +@testset "Nedelec" begin + include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) + import .InterpolationTestUtils as ITU + nel = 3 + for CT in (Triangle, QuadraticTriangle, Tetrahedron) + dim = Ferrite.getdim(CT) + p1, p2 = (rand(Vec{dim}), ones(Vec{dim})+rand(Vec{dim})) + grid = generate_grid(CT, ntuple(_->nel, dim), p1, p2) + # Distort grid, important to properly test geometry mapping + # for 2nd order elements. Make sure distortion is less than + # a 10th of the element size. + transform_coordinates!(grid, x->(x + rand(x)/(10*nel))) + RefShape = Ferrite.getrefshape(getcells(grid, 1)) + for order in (1, 2) + dim == 3 && order > 1 && continue + ip = Nedelec{dim,RefShape,order}() + @testset "$CT, $ip" begin + dh = DofHandler(grid) + add!(dh, :u, ip) + close!(dh) + cellnr = getncells(grid)÷2 # Should be a cell in the center + for facenr in 1:nfaces(RefShape) + fi = FaceIndex(cellnr, facenr) + # Check continuity of tangential function value + ITU.test_continuity(dh, fi; transformation_function=(v,n)-> v - n*(v⋅n)) + end + # Check gradient calculation + ITU.test_gradient(dh, cellnr) + end + end + end end +end + From 02051eebe1518656d8fba1245000cf174180c33c Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Thu, 21 Sep 2023 20:13:53 +0200 Subject: [PATCH 020/172] Basic RaviartThomas implementation, gradient mapping seems to have an error though (test fails) --- docs/src/literate-tutorials/maxwell.jl | 17 +---- docs/src/literate-tutorials/maxwell_refel.jl | 5 +- src/FEValues/FunctionValues.jl | 24 +++++-- src/FEValues/GeometryValues.jl | 5 +- src/exports.jl | 2 + src/interpolations.jl | 72 +++++++++++--------- test/runtests.jl | 3 +- test/test_interpolations.jl | 39 ++++++----- 8 files changed, 96 insertions(+), 71 deletions(-) diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl index a09a5631e2..20d5498b79 100644 --- a/docs/src/literate-tutorials/maxwell.jl +++ b/docs/src/literate-tutorials/maxwell.jl @@ -12,9 +12,10 @@ import LinearAlgebra: normalize using StaticArrays using Test using Ferrite -import Ferrite: Nedelec +import Ferrite: Nedelec, RaviartThomas import CairoMakie as M ip = Nedelec{2,RefTriangle,2}() +ip = RaviartThomas{2,RefTriangle,1}() grid = generate_grid(Triangle, (1,2)) dh = DofHandler(grid) add!(dh, :B, ip) @@ -68,16 +69,4 @@ function plot_shapes(dh, cv) end return nothing end - -# plot_shapes(dh, cv) - -# Remaining tasks -#= -✓ 2nd order Nedelec -* Develop test cases for - a) Continuity - b) Gradient check -=# - - -test_gradient(dh, 1) +plot_shapes(dh, cv) diff --git a/docs/src/literate-tutorials/maxwell_refel.jl b/docs/src/literate-tutorials/maxwell_refel.jl index c61849f88b..7bbc56c73a 100644 --- a/docs/src/literate-tutorials/maxwell_refel.jl +++ b/docs/src/literate-tutorials/maxwell_refel.jl @@ -10,9 +10,10 @@ # and we therefore use the Nedelec interpolation. import CairoMakie as M using Ferrite -import Ferrite: Nedelec +import Ferrite: Nedelec, RaviartThomas import Ferrite: reference_coordinates ip = Nedelec{2,RefTriangle,1}() +ip = RaviartThomas{2,RefTriangle,1}() ip_geo = Lagrange{RefTriangle,1}() ref_x = reference_coordinates(ip_geo) @@ -52,7 +53,7 @@ for i in 1:3 ax=M.Axis(fig2[i,1]; aspect=M.DataAspect()); M.lines!(ax, first.(x_vertices), last.(x_vertices)) x_qp = spatial_coordinate.((cv,), 1:length(x), (ref_x,)) - @show x_qp ≈ x + @show x_qp ≈ x # should be false v = shape_value.((cv,), 1:length(x), i) M.scatter!(ax, first.(x_qp), last.(x_qp)) M.arrows!(ax, first.(x_qp), last.(x_qp), first.(v), last.(v); lengthscale=0.25) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 1e91d13279..99daa6d168 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -73,16 +73,11 @@ struct ContravariantPiolaMapping end # struct DoubleContravariantPiolaMapping end get_mapping_type(fv::FunctionValues) = get_mapping_type(fv.ip) -#get_mapping_type(::FunctionValues{<:ScalarInterpolation}) = IdentityMapping() -#get_mapping_type(::FunctionValues{<:VectorizedInterpolation}) = IdentityMapping() -#get_mapping_type(::FunctionValues{<:HdivIP}) = ContravariantPiolaMapping() -#get_mapping_type(::FunctionValues{<:HcurlIP}) = CovariantPiolaMapping() requires_hessian(::IdentityMapping) = false requires_hessian(::ContravariantPiolaMapping) = true requires_hessian(::CovariantPiolaMapping) = true - calculate_Jinv(J::Tensor{2}) = inv(J) calculate_Jinv(J::SMatrix) = pinv(J) @@ -121,3 +116,22 @@ end end return nothing end + +@inline function apply_mapping!(funvals::FunctionValues, ::ContravariantPiolaMapping, q_point::Int, mapping_values, cell) + H = gethessian(mapping_values) + J = getjacobian(mapping_values) + Jinv = inv(J) + detJ = det(J) + I2 = one(J) + H_Jinv = H⋅Jinv + A1 = (H_Jinv ⊡ (otimesl(I2,I2))) / detJ + A2 = (Jinv' ⊡ H_Jinv) / detJ + @inbounds for j in 1:getnbasefunctions(funvals) + d = get_direction(funvals.ip, j, cell) + dNdξ = funvals.dNdξ[j, q_point] + N_ξ = funvals.N_ξ[j, q_point] + funvals.N_x[j, q_point] = d*(J ⋅ N_ξ)/detJ + funvals.dNdx[j, q_point] = d*(Jinv ⋅ dNdξ ⋅ Jinv/detJ + A1 ⋅ N_ξ - (J ⋅ N_ξ) ⊗ A2) + end + return nothing +end diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 349c1bbc9c..71b73d45f3 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -3,7 +3,10 @@ struct MappingValues{JT, HT<:Union{Nothing,AbstractTensor{3}}} H::HT # dJ/dξ # Hessian end @inline getjacobian(mv::MappingValues) = mv.J -@inline gethessian(mv::MappingValues) = mv.H +@inline gethessian(mv::MappingValues{<:Any,<:AbstractTensor}) = mv.H + +@inline gethessian(::MappingValues{JT,Nothing}) where JT = _make_hessian(JT) +@inline _make_hessian(::Type{Tensor{2,dim,T}}) where {dim,T} = zero(Tensor{3,dim,T}) struct RequiresHessian{B} end RequiresHessian(B::Bool) = RequiresHessian{B}() diff --git a/src/exports.jl b/src/exports.jl index 09a0ab1e5e..a25d7e8420 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -16,6 +16,8 @@ export Lagrange, DiscontinuousLagrange, Serendipity, + Nedelec, + RaviartThomas, getnbasefunctions, # Quadrature diff --git a/src/interpolations.jl b/src/interpolations.jl index 285c19dca5..224118b2dc 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1518,26 +1518,34 @@ is_discontinuous(::Type{<:VectorizedInterpolation{<:Any, <:Any, <:Any, ip}}) whe get_mapping_type(::ScalarInterpolation) = IdentityMapping() get_mapping_type(::VectorizedInterpolation) = IdentityMapping() -#= Just notes for now for RaviartThomas. Nedelec below tbd first +##################################### +# RaviartThomas (1st kind), H(div) # +##################################### # https://defelement.com/elements/raviart-thomas.html # https://defelement.com/elements/qdiv.html struct RaviartThomas{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end +get_mapping_type(::RaviartThomas) = ContravariantPiolaMapping() -facedof_interior_indices(ip::RaviartThomas) = facedof_indices(ip) - -getnbasefunctions(::RaviartThomas{2,RefTriangle,1}) = 3 -facedof_indices(::RaviartThomas{2,RefTriangle,1}) = ((1,), (2,), (3,)) -adjust_dofs_during_distribution(::RaviartThomas) = false # Not sure how this works, but should be done for higher orders +# RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-1.html +# Signs changed when needed to make positive direction outwards function shape_value(ip::RaviartThomas{2,RefTriangle,1}, ξ::Vec{2}, i::Int) - ξ_x, ξ_y = ξ - i == 1 && return -ξ - i == 2 && return Vec(ξ_x-1, ξ_y) - i == 3 && return Vec(-ξ_x, 1 - ξ_y) + x, y = ξ + i == 1 && return ξ # Flip sign + i == 2 && return Vec(x-1, y) # Keep sign + i == 3 && return Vec(x, y - 1) # Flip sign throw(ArgumentError("no shape function $i for interpolation $ip")) end -=# + +getnbasefunctions(::RaviartThomas{2,RefTriangle,1}) = 3 +facedof_interior_indices(::RaviartThomas{2,RefTriangle,1}) = ((1,), (2,), (3,)) +adjust_dofs_during_distribution(::RaviartThomas) = false + +function get_direction(::RaviartThomas{2,RefTriangle,1}, j, cell) + face_vertices = faces(cell)[j] + return face_vertices[2] > face_vertices[1] ? 1 : -1 +end ##################################### @@ -1549,10 +1557,10 @@ get_mapping_type(::Nedelec) = CovariantPiolaMapping() # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html function shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) - ξ_x, ξ_y = ξ - i == 1 && return Vec( - ξ_y, ξ_x) - i == 2 && return Vec( - ξ_y, ξ_x - 1) # Changed signed, follow Ferrite's sign convention - i == 3 && return Vec(1 - ξ_y, ξ_x) + x, y = ξ + i == 1 && return Vec( - y, x) + i == 2 && return Vec( - y, x - 1) # Changed signed, follow Ferrite's sign convention + i == 3 && return Vec(1 - y, x) throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1568,27 +1576,27 @@ end # RefTriangle, 2nd order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-2.html function shape_value(ip::Nedelec{2,RefTriangle,2}, ξ::Vec{2}, i::Int) - ξ_x, ξ_y = ξ + x, y = ξ # Face 1 - i == 1 && return Vec( 2*ξ_y*(1 - 4*ξ_x), - 4*ξ_x*(2*ξ_x - 1)) - i == 2 && return Vec( 4*ξ_y*(1 - 2*ξ_y), - 2*ξ_x*(4*ξ_y - 1)) + i == 1 && return Vec( 2*y*(1 - 4*x), + 4*x*(2*x - 1)) + i == 2 && return Vec( 4*y*(1 - 2*y), + 2*x*(4*y - 1)) # Face 2 (flip order and sign compared to defelement) - i == 3 && return Vec( 4*ξ_y*(1 - 2*ξ_y), - 8*ξ_x*ξ_y - 2*ξ_x - 6*ξ_y + 2) - i == 4 && return Vec( 2*ξ_y*(4*ξ_x + 4*ξ_y - 3), - -8*ξ_x^2 - 8*ξ_x*ξ_y + 12*ξ_x + 6*ξ_y - 4) + i == 3 && return Vec( 4*y*(1 - 2*y), + 8*x*y - 2*x - 6*y + 2) + i == 4 && return Vec( 2*y*(4*x + 4*y - 3), + -8*x^2 - 8*x*y + 12*x + 6*y - 4) # Face 3 - i == 5 && return Vec( 8*ξ_x*ξ_y - 6*ξ_x + 8*ξ_y^2 - 12*ξ_y + 4, - 2*ξ_x*(-4*ξ_x - 4*ξ_y + 3)) - i == 6 && return Vec(-8*ξ_x*ξ_y + 6*ξ_x + 2*ξ_y - 2, - 4*ξ_x*(2*ξ_x - 1)) + i == 5 && return Vec( 8*x*y - 6*x + 8*y^2 - 12*y + 4, + 2*x*(-4*x - 4*y + 3)) + i == 6 && return Vec(-8*x*y + 6*x + 2*y - 2, + 4*x*(2*x - 1)) # Cell - i == 7 && return Vec( 8*ξ_y*(-ξ_x - 2*ξ_y + 2), - 8*ξ_x*( ξ_x + 2*ξ_y - 1)) - i == 8 && return Vec( 8*ξ_y*( 2*ξ_x + ξ_y - 1), - 8*ξ_x*(-2*ξ_x - ξ_y + 2)) + i == 7 && return Vec( 8*y*(-x - 2*y + 2), + 8*x*( x + 2*y - 1)) + i == 8 && return Vec( 8*y*( 2*x + y - 1), + 8*x*(-2*x - y + 2)) throw(ArgumentError("no shape function $i for interpolation $ip")) end diff --git a/test/runtests.jl b/test/runtests.jl index d11ed6d582..e98ca2daa4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,7 +19,7 @@ end include("test_utils.jl") include("test_interpolations.jl") -include("test_cellvalues.jl") +#=include("test_cellvalues.jl") include("test_facevalues.jl") include("test_quadrules.jl") include("test_assemble.jl") @@ -38,3 +38,4 @@ include("test_deprecations.jl") HAS_EXTENSIONS && include("blockarrays.jl") include("test_examples.jl") @test all(x -> isdefined(Ferrite, x), names(Ferrite)) # Test that all exported symbols are defined +=# \ No newline at end of file diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index ed8f20f225..fe22eff22c 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,6 +1,6 @@ @testset "interpolations" begin - +#= @testset "$interpolation" for interpolation in ( Lagrange{RefLine, 1}(), Lagrange{RefLine, 2}(), @@ -179,11 +179,14 @@ end @test Ferrite.is_discontinuous(ip_t) == false @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true - -@testset "Nedelec" begin +=# +@testset "Hcurl and Hdiv" begin include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) import .InterpolationTestUtils as ITU nel = 3 + transformation_functions = Dict( + Nedelec=>(v,n)-> v - n*(v⋅n), # Hcurl (tangent continuity) + RaviartThomas=>(v,n) -> v ⋅ n) # Hdiv (normal continuity) for CT in (Triangle, QuadraticTriangle, Tetrahedron) dim = Ferrite.getdim(CT) p1, p2 = (rand(Vec{dim}), ones(Vec{dim})+rand(Vec{dim})) @@ -194,20 +197,24 @@ end transform_coordinates!(grid, x->(x + rand(x)/(10*nel))) RefShape = Ferrite.getrefshape(getcells(grid, 1)) for order in (1, 2) - dim == 3 && order > 1 && continue - ip = Nedelec{dim,RefShape,order}() - @testset "$CT, $ip" begin - dh = DofHandler(grid) - add!(dh, :u, ip) - close!(dh) - cellnr = getncells(grid)÷2 # Should be a cell in the center - for facenr in 1:nfaces(RefShape) - fi = FaceIndex(cellnr, facenr) - # Check continuity of tangential function value - ITU.test_continuity(dh, fi; transformation_function=(v,n)-> v - n*(v⋅n)) + for IPT in (Nedelec, RaviartThomas) + dim == 3 && order > 1 && continue + IPT == RaviartThomas && (dim == 3 || order > 1) && continue + transformation_function=transformation_functions[IPT] + ip = IPT{dim,RefShape,order}() + @testset "$CT, $ip" begin + dh = DofHandler(grid) + add!(dh, :u, ip) + close!(dh) + cellnr = getncells(grid)÷2 # Should be a cell in the center + for facenr in 1:nfaces(RefShape) + fi = FaceIndex(cellnr, facenr) + # Check continuity of tangential function value + ITU.test_continuity(dh, fi; transformation_function) + end + # Check gradient calculation + ITU.test_gradient(dh, cellnr) end - # Check gradient calculation - ITU.test_gradient(dh, cellnr) end end end From df83ae84579d81ce3403446ec4e973e0d24d9c99 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 22 Sep 2023 23:29:24 +0200 Subject: [PATCH 021/172] Fix error in ContravariantPiolaMapping gradient calculation --- src/FEValues/FunctionValues.jl | 2 +- test/InterpolationTestUtils.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 99daa6d168..2290e310d4 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -131,7 +131,7 @@ end dNdξ = funvals.dNdξ[j, q_point] N_ξ = funvals.N_ξ[j, q_point] funvals.N_x[j, q_point] = d*(J ⋅ N_ξ)/detJ - funvals.dNdx[j, q_point] = d*(Jinv ⋅ dNdξ ⋅ Jinv/detJ + A1 ⋅ N_ξ - (J ⋅ N_ξ) ⊗ A2) + funvals.dNdx[j, q_point] = d*(J ⋅ dNdξ ⋅ Jinv/detJ + A1 ⋅ N_ξ - (J ⋅ N_ξ) ⊗ A2) end return nothing end diff --git a/test/InterpolationTestUtils.jl b/test/InterpolationTestUtils.jl index 612be6df5b..6cace40b44 100644 --- a/test/InterpolationTestUtils.jl +++ b/test/InterpolationTestUtils.jl @@ -27,7 +27,7 @@ module InterpolationTestUtils # Check validity of input @assert length(dh.subdofhandlers) == 1 - @assert Ferrite.nfields(dh) == 1 + @assert length(Ferrite.getfieldnames(dh)) == 1 # Find the matching FaceIndex cellnr, facenr = face From c4e1d63d299a7e7bd55105c6c17e1311018948c0 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 23 Sep 2023 10:58:20 +0200 Subject: [PATCH 022/172] Reenable tests, setup tmp ci, add mapping desc to devdocs --- .github/workflows/ci.yml | 8 +- docs/Manifest.toml | 4 +- docs/src/assets/fe_mapping.svg | 3089 ++++++++++++++++++++++++++++++++ docs/src/devdocs/index.md | 2 +- docs/src/devdocs/mapping.md | 103 ++ test/runtests.jl | 3 +- test/test_interpolations.jl | 4 +- 7 files changed, 3206 insertions(+), 7 deletions(-) create mode 100644 docs/src/assets/fe_mapping.svg create mode 100644 docs/src/devdocs/mapping.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2401b36ad..99e406eb71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,13 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.julia-version }} - - uses: julia-actions/julia-buildpkg@v1 + #- uses: julia-actions/julia-buildpkg@v1 + - run: | + julia --color=yes --project=. -e ' + using Pkg + Pkg.add(url="https://github.com/KnutAM/Tensors.jl.git", rev="kam/3rd_order") + Pkg.instantiate()' + shell: bash - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 with: diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 00a9f3dfcf..073d66c467 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2154,7 +2154,9 @@ version = "0.1.1" [[deps.Tensors]] deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] -path = "C:\\Users\\meyer\\.julia\\dev\\Tensors" +git-tree-sha1 = "09650779b3c5124b8742986904d6e69f854be0bd" +repo-rev = "kam/3rd_order" +repo-url = "https://github.com/KnutAM/Tensors.jl.git" uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" version = "1.15.0" diff --git a/docs/src/assets/fe_mapping.svg b/docs/src/assets/fe_mapping.svg new file mode 100644 index 0000000000..39fd0e70fb --- /dev/null +++ b/docs/src/assets/fe_mapping.svg @@ -0,0 +1,3089 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/devdocs/index.md b/docs/src/devdocs/index.md index 3560eff4a1..75b50844c6 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", "dofhandler.md", "performance.md"] +Pages = ["reference_cells.md", "interpolations.md", "elements.md", "mapping.md", "dofhandler.md", "performance.md"] ``` diff --git a/docs/src/devdocs/mapping.md b/docs/src/devdocs/mapping.md new file mode 100644 index 0000000000..9d741fb839 --- /dev/null +++ b/docs/src/devdocs/mapping.md @@ -0,0 +1,103 @@ +# Mapping of finite elements +The shape functions and gradients stored in an `FEValues` object, is reinitialized for each cell by calling the `reinit!` function. The main part of this calculation, considers how to map the functions described on the reference cell, to the actual cell. + +The geometric mapping of a finite element from the reference coordinates to the real coordinates is shown in the following illustration. + +![mapping_figure](../assets/fe_mapping.svg) + +This mapping is given by the geometric shape functions, $\hat{N}_i^g(\boldsymbol{\xi})$, such that +```math +\begin{align*} + \boldsymbol{x}(\boldsymbol{\xi}) =& \sum_{i}^N \hat{\boldsymbol{x}}_i \hat{N}_i^g(\boldsymbol{\xi}) \\ + \boldsymbol{J} :=& \frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}\boldsymbol{\xi}} = \sum_{i}^N \hat{\boldsymbol{x}}_i \otimes \frac{\mathrm{d} \hat{N}_i^g}{\mathrm{d}\boldsymbol{\xi}}\\ + \boldsymbol{\mathcal{H}} :=& + \frac{\mathrm{d} \boldsymbol{J}}{\mathrm{d} \boldsymbol{\xi}} = \sum_{\alpha=1}^N \hat{\boldsymbol{x}}_\alpha \otimes \frac{\mathrm{d}^2 \hat{N}^g_\alpha}{\mathrm{d} \boldsymbol{\xi}^2} +\end{align*} +``` +where the defined $\boldsymbol{J}$ is the jacobian of the mapping, and in some cases we will also need the corresponding hessian, $\boldsymbol{\mathcal{H}}$ (3rd order tensor). + +We require that the mapping from reference coordinates to real coordinates is bijective, meaning that we can express $\boldsymbol{x} = \boldsymbol{x}(\boldsymbol{\xi}(\boldsymbol{x}))$, such that +```math +\begin{align*} + \frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}\boldsymbol{x}} = \boldsymbol{I} &= \frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}\boldsymbol{\xi}} \cdot \frac{\mathrm{d}\boldsymbol{\xi}}{\mathrm{d}\boldsymbol{x}} + \quad\Rightarrow\quad + \frac{\mathrm{d}\boldsymbol{\xi}}{\mathrm{d}\boldsymbol{x}} = \left[\frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}\boldsymbol{\xi}}\right]^{-1} = \boldsymbol{J}^{-1} +\end{align*} +``` +Depending on the function interpolation, we may want different types of mappings to conserve certain properties of the fields. This results in the different mapping types described below. + +## Identity mapping +`Ferrite.IdentityMapping` + +For scalar fields, we always use scalar base functions. For tensorial fields (non-scalar, e.g. vector-fields), the base functions can be constructed from scalar base functions, by using e.g. `VectorizedInterpolation`. From the perspective of the mapping, however, each component is mapped as an individual scalar base function. And for scalar base functions, we only require that the value of the base function is invariant to the element shape (real coordinate), and only depends on the reference coordinate, i.e. +```math +\begin{align*} + N(\boldsymbol{x}) &= \hat{N}(\boldsymbol{\xi}(\boldsymbol{x}))\nonumber \\ + \mathrm{grad}(N(\boldsymbol{x})) &= \frac{\mathrm{d}\hat{N}}{\mathrm{d}\boldsymbol{\xi}} \cdot \boldsymbol{J}^{-1} +\end{align*} +``` + +## Covariant Piola mapping, H(curl) +`Ferrite.CovariantPiolaMapping` + +The covariant Piola mapping of a vectorial base function preserves the tangential components. For the value, the mapping is defined as +```math +\begin{align*} + \boldsymbol{N}(\boldsymbol{x}) = \boldsymbol{J}^{-\mathrm{T}} \cdot \hat{\boldsymbol{N}}(\boldsymbol{\xi}(\boldsymbol{x})) +\end{align*} +``` +which yields the gradient, +```math +\begin{align*} + \mathrm{grad}(\boldsymbol{N}(\boldsymbol{x})) &= \boldsymbol{J}^{-T} \cdot \frac{\mathrm{d} \hat{\boldsymbol{N}}}{\mathrm{d} \boldsymbol{\xi}} \cdot \boldsymbol{J}^{-1} - \boldsymbol{J}^{-T} \cdot \left[\hat{\boldsymbol{N}}(\boldsymbol{\xi}(\boldsymbol{x}))\cdot \boldsymbol{J}^{-1} \cdot \boldsymbol{\mathcal{H}}\cdot \boldsymbol{J}^{-1}\right] +\end{align*} +``` + +!!! details "Derivation" + Expressing the gradient, $\mathrm{grad}(\boldsymbol{N})$, in index notation, + ```math + \begin{align*} + \frac{\mathrm{d} N_i}{\mathrm{d} x_j} &= \frac{\mathrm{d}}{\mathrm{d} x_j} \left[J^{-\mathrm{T}}_{ik} \hat{N}_k\right] = \frac{\mathrm{d} J^{-\mathrm{T}}_{ik}}{\mathrm{d} x_j} \hat{N}_k + J^{-\mathrm{T}}_{ik} \frac{\mathrm{d} \hat{N}_k}{\mathrm{d} \xi_l} J_{lj}^{-1} + \end{align*} + ``` + + Except for a few elements, $\boldsymbol{J}$ varies as a function of $\boldsymbol{x}$. The derivative can be calculated as + ```math + \begin{align*} + \frac{\mathrm{d} J^{-\mathrm{T}}_{ik}}{\mathrm{d} x_j} &= \frac{\mathrm{d} J^{-\mathrm{T}}_{ik}}{\mathrm{d} J_{mn}} \frac{\mathrm{d} J_{mn}}{\mathrm{d} x_j} = - J_{km}^{-1} J_{in}^{-T} \frac{\mathrm{d} J_{mn}}{\mathrm{d} x_j} \nonumber \\ + \frac{\mathrm{d} J_{mn}}{\mathrm{d} x_j} &= \mathcal{H}_{mno} J_{oj}^{-1} + \end{align*} + ``` + +## Contravariant Piola mapping, H(div) +`Ferrite.ContravariantPiolaMapping` + +The covariant Piola mapping of a vectorial base function preserves the normal components. For the value, the mapping is defined as +```math +\begin{align*} + \boldsymbol{N}(\boldsymbol{x}) = \frac{\boldsymbol{J}}{\det(\boldsymbol{J})} \cdot \hat{\boldsymbol{N}}(\boldsymbol{\xi}(\boldsymbol{x})) +\end{align*} +``` +This gives the gradient +```math +\begin{align*} + \mathrm{grad}(\boldsymbol{N}(\boldsymbol{x})) = [\boldsymbol{\mathcal{H}}\cdot\boldsymbol{J}^{-1}] : \frac{[\boldsymbol{I} \underline{\otimes} \boldsymbol{I}] \cdot \hat{\boldsymbol{N}}}{\det(\boldsymbol{J})} + - \left[\frac{\boldsymbol{J} \cdot \hat{\boldsymbol{N}}}{\det(\boldsymbol{J})}\right] \otimes \left[\boldsymbol{J}^{-T} : \boldsymbol{\mathcal{H}} \cdot \boldsymbol{J}^{-1}\right] + + \boldsymbol{J} \cdot \frac{\mathrm{d} \hat{\boldsymbol{N}}}{\mathrm{d} \boldsymbol{\xi}} \cdot \frac{\boldsymbol{J}^{-1}}{\det(\boldsymbol{J})} +\end{align*} +``` + +!!! details "Derivation" + Expressing the gradient, $\mathrm{grad}(\boldsymbol{N})$, in index notation, + ```math + \begin{align*} + \frac{\mathrm{d} N_i}{\mathrm{d} x_j} &= \frac{\mathrm{d}}{\mathrm{d} x_j} \left[\frac{J_{ik}}{\det(\boldsymbol{J})} \hat{N}_k\right] =\nonumber\\ + &= \frac{\mathrm{d} J_{ik}}{\mathrm{d} x_j} \frac{\hat{N}_k}{\det(\boldsymbol{J})} + - \frac{\mathrm{d} \det(\boldsymbol{J})}{\mathrm{d} x_j} \frac{J_{ik} \hat{N}_k}{\det(\boldsymbol{J})^2} + + \frac{J_{ik}}{\det(\boldsymbol{J})} \frac{\mathrm{d} \hat{N}_k}{\mathrm{d} \xi_l} J_{lj}^{-1} \\ + &= \mathcal{H}_{ikl} J^{-1}_{lj} \frac{\hat{N}_k}{\det(\boldsymbol{J})} + - J^{-T}_{mn} \mathcal{H}_{mnl} J^{-1}_{lj} \frac{J_{ik} \hat{N}_k}{\det(\boldsymbol{J})} + + \frac{J_{ik}}{\det(\boldsymbol{J})} \frac{\mathrm{d} \hat{N}_k}{\mathrm{d} \xi_l} J_{lj}^{-1} + \end{align*} + ``` + diff --git a/test/runtests.jl b/test/runtests.jl index e98ca2daa4..d11ed6d582 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,7 +19,7 @@ end include("test_utils.jl") include("test_interpolations.jl") -#=include("test_cellvalues.jl") +include("test_cellvalues.jl") include("test_facevalues.jl") include("test_quadrules.jl") include("test_assemble.jl") @@ -38,4 +38,3 @@ include("test_deprecations.jl") HAS_EXTENSIONS && include("blockarrays.jl") include("test_examples.jl") @test all(x -> isdefined(Ferrite, x), names(Ferrite)) # Test that all exported symbols are defined -=# \ No newline at end of file diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index fe22eff22c..da462b27f9 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,6 +1,6 @@ @testset "interpolations" begin -#= + @testset "$interpolation" for interpolation in ( Lagrange{RefLine, 1}(), Lagrange{RefLine, 2}(), @@ -179,7 +179,7 @@ end @test Ferrite.is_discontinuous(ip_t) == false @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true -=# + @testset "Hcurl and Hdiv" begin include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) import .InterpolationTestUtils as ITU From 562041a275ffd3a256d281cf0b09fb382a21b4c2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 25 Sep 2023 18:28:40 +0200 Subject: [PATCH 023/172] bijective to diffeomorphic in theory section --- docs/src/devdocs/mapping.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/devdocs/mapping.md b/docs/src/devdocs/mapping.md index 9d741fb839..0d9dca4302 100644 --- a/docs/src/devdocs/mapping.md +++ b/docs/src/devdocs/mapping.md @@ -16,7 +16,7 @@ This mapping is given by the geometric shape functions, $\hat{N}_i^g(\boldsymbol ``` where the defined $\boldsymbol{J}$ is the jacobian of the mapping, and in some cases we will also need the corresponding hessian, $\boldsymbol{\mathcal{H}}$ (3rd order tensor). -We require that the mapping from reference coordinates to real coordinates is bijective, meaning that we can express $\boldsymbol{x} = \boldsymbol{x}(\boldsymbol{\xi}(\boldsymbol{x}))$, such that +We require that the mapping from reference coordinates to real coordinates is [diffeomorphic](https://en.wikipedia.org/wiki/Diffeomorphism), meaning that we can express $\boldsymbol{x} = \boldsymbol{x}(\boldsymbol{\xi}(\boldsymbol{x}))$, such that ```math \begin{align*} \frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}\boldsymbol{x}} = \boldsymbol{I} &= \frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}\boldsymbol{\xi}} \cdot \frac{\mathrm{d}\boldsymbol{\xi}}{\mathrm{d}\boldsymbol{x}} From cf44f6c9a8ea0da87a55091b129f9cc8d01d967e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 27 Sep 2023 12:08:36 +0200 Subject: [PATCH 024/172] Work on maxwell eigenproblem, but doesn't give correct results --- docs/generate.jl | 2 +- docs/make.jl | 2 +- .../literate-tutorials/maxwell_eigenvalue.jl | 103 ++++++++++++++++++ ...ll.jl => nedelec_raviartthomas_testing.jl} | 0 ...=> nedelec_raviartthomas_testing_refel.jl} | 0 src/FEValues/CellValues.jl | 2 +- src/FEValues/common_values.jl | 1 + src/interpolations.jl | 6 +- 8 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 docs/src/literate-tutorials/maxwell_eigenvalue.jl rename docs/src/literate-tutorials/{maxwell.jl => nedelec_raviartthomas_testing.jl} (100%) rename docs/src/literate-tutorials/{maxwell_refel.jl => nedelec_raviartthomas_testing_refel.jl} (100%) diff --git a/docs/generate.jl b/docs/generate.jl index e06d3a14e9..fabe1410b5 100644 --- a/docs/generate.jl +++ b/docs/generate.jl @@ -22,7 +22,7 @@ include("download_resources.jl") # Run Literate on all examples @timeit dto "Literate." for (IN, OUT) in [(TUTORIALS_IN, TUTORIALS_OUT), (HOWTO_IN, HOWTO_OUT), (GALLERY_IN, GALLERY_OUT)], program in readdir(IN; join=true) name = basename(program) - if endswith(program, "maxwell.jl") + if endswith(program, "maxwell_eigenvalue.jl") if !liveserver script = @timeit dto "script()" @timeit dto name Literate.script(program, OUT) code = strip(read(script, String)) diff --git a/docs/make.jl b/docs/make.jl index dc12933932..a737ad81e8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -46,7 +46,7 @@ bibtex_plugin = CitationBibliography( # hide("Changelog" => "changelog.md"), "Tutorials" => [ # "Tutorials overview" => "tutorials/index.md", - "tutorials/maxwell.md", + "tutorials/maxwell_eigenvalue.md", #= "tutorials/heat_equation.md", "tutorials/linear_elasticity.md", "tutorials/incompressible_elasticity.md", diff --git a/docs/src/literate-tutorials/maxwell_eigenvalue.jl b/docs/src/literate-tutorials/maxwell_eigenvalue.jl new file mode 100644 index 0000000000..782937ae01 --- /dev/null +++ b/docs/src/literate-tutorials/maxwell_eigenvalue.jl @@ -0,0 +1,103 @@ +# The Maxwell eigenvalue problem +# Following the Fenics tutorial, +# [*Stable and unstable finite elements for the Maxwell eigenvalue problem*](https://fenicsproject.org/olddocs/dolfin/2019.1.0/python/demos/maxwell-eigenvalues/demo_maxwell-eigenvalues.py.html), +# we show how Nedelec elements can be used +# with Ferrite.jl +# ## Problem description +# ### Strong form +# +# ### Weak form +# ```math +# \int_\Omega \mathrm{curl}(\boldsymbol{\delta u}) \cdot \mathrm{curl}(\boldsymbol{u})\, \mathrm{d}\Omega = \lambda \int_\Omega \boldsymbol{\delta u}\cdot \boldsymbol{u}\ \mathrm{d}\Omega +# ``` +# ### FE form +# ```math +# \begin{align*} +# \int_\Omega \mathrm{curl}(\boldsymbol{\delta N}_i) \cdot \mathrm{curl}(\boldsymbol{N}_j)\, \mathrm{d}\Omega a_j &= \lambda \int_\Omega \boldsymbol{\delta N}_i\cdot \boldsymbol{N}_j\ \mathrm{d}\Omega a_j \\ +# A_{ij} a_j &= \lambda B_{ij} a_j +# \end{align*} +# ``` + +# https://iterativesolvers.julialinearalgebra.org/dev/eigenproblems/lobpcg/ +using Ferrite +import Ferrite: Nedelec, RaviartThomas +import IterativeSolvers: lobpcg +using LinearAlgebra + +function assemble_cell!(Ae, Be, cv) + n = getnbasefunctions(cv) + for q_point in 1:getnquadpoints(cv) + dΩ = getdetJdV(cv, q_point) + for i in 1:n + δNi = shape_value(cv, q_point, i) + curl_δNi = shape_curl(cv, q_point, i) + for j in 1:n + Nj = shape_value(cv, q_point, j) + curl_Nj = shape_curl(cv, q_point, j) + Ae[i,j] += (curl_δNi ⋅ curl_Nj)*dΩ + Be[i,j] += (δNi ⋅ Nj)*dΩ + end + end + end + return Ae, Be +end + +function doassemble(dh::DofHandler, cv::CellValues) + grid = dh.grid + A, B = create_sparsity_pattern.((dh, dh)) + assemA, assemB = start_assemble.((A, B)) + x = getcoordinates(grid, 1) + n_el_dofs = ndofs_per_cell(dh, 1) + dofs = zeros(Int, n_el_dofs) + Ae, Be = [zeros(n_el_dofs, n_el_dofs) for _ in 1:2] + + for (ic, cell) in pairs(getcells(grid)) + getcoordinates!(x, grid, cell) + celldofs!(dofs, dh, ic) + reinit!(cv, x, cell) + fill!.((Ae, Be), 0) + assemble_cell!(Ae, Be, cv) + assemble!(assemA, dofs, Ae) + assemble!(assemB, dofs, Be) + end + return A, B +end + +function get_matrices(ip::Interpolation; CT=Quadrilateral, nel=40, usebc=true) + RefShape = Ferrite.getrefshape(ip) + grid = generate_grid(CT, (nel,nel), zero(Vec{2}), π*ones(Vec{2})) + dh = DofHandler(grid) + add!(dh, :u, ip) + close!(dh) + ip_geo = Ferrite.default_interpolation(CT) + cv = CellValues(QuadratureRule{RefShape}(2), ip, ip_geo) + A, B = doassemble(dh, cv) + if usebc + ch = ConstraintHandler(dh) + dΩh = union(getfaceset(grid, "left"), getfaceset(grid, "right")) + dΩv = union(getfaceset(grid, "top"), getfaceset(grid, "bottom")) + if ip isa VectorizedInterpolation + add!(ch, Dirichlet(:u, dΩh, Returns(0.0), 2)) # y-component on left-right + add!(ch, Dirichlet(:u, dΩv, Returns(0.0), 1)) # x-component on top-bottom + else + add!(ch, Dirichlet(:u, union!(dΩh,dΩv), Returns(0.0))) + end + close!(ch) + update!(ch, 0.0) + apply!(A, ch) + apply!(B, ch) + end + return A, B +end + +function solve(ip; num_values, kwargs...) + A, B = get_matrices(ip; kwargs...) + n = size(A,1) + r = lobpcg(Symmetric(A), Symmetric(B), false, zeros(n,num_values)) + return r.λ +end + +ip = Nedelec{2,RefTriangle,1}() +#ip = Lagrange{RefTriangle,1}()^2 + +λ = solve(ip; CT=Triangle, num_values=10) \ No newline at end of file diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/nedelec_raviartthomas_testing.jl similarity index 100% rename from docs/src/literate-tutorials/maxwell.jl rename to docs/src/literate-tutorials/nedelec_raviartthomas_testing.jl diff --git a/docs/src/literate-tutorials/maxwell_refel.jl b/docs/src/literate-tutorials/nedelec_raviartthomas_testing_refel.jl similarity index 100% rename from docs/src/literate-tutorials/maxwell_refel.jl rename to docs/src/literate-tutorials/nedelec_raviartthomas_testing_refel.jl diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 8cdf9b1ba0..2c09019414 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -35,7 +35,7 @@ getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] # Accessors for function values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) -for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient, :shape_curl) +for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient) eval(quote @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) end) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index eac205a62c..683cc36511 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -103,6 +103,7 @@ function shape_curl(cv::AbstractValues, q_point::Int, base_func::Int) return curl_from_gradient(shape_gradient(cv, q_point, base_func)) end curl_from_gradient(∇v::SecondOrderTensor{3}) = Vec{3}((∇v[3,2] - ∇v[2,3], ∇v[1,3] - ∇v[3,1], ∇v[2,1] - ∇v[1,2])) +curl_from_gradient(∇v::SecondOrderTensor{2}) = Vec{1}((∇v[2,1] - ∇v[1,2],)) # Alternatively define as Vec{3}((0,0,v)) """ function_value(fe_v::AbstractValues, q_point::Int, u::AbstractVector) diff --git a/src/interpolations.jl b/src/interpolations.jl index 224118b2dc..f8235f2bb0 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -53,7 +53,6 @@ n_components(::ScalarInterpolation) = 1 n_components(::VectorInterpolation{vdim}) where {vdim} = vdim # Number of components that are allowed to prescribe in e.g. Dirichlet BC n_dbc_components(ip::Interpolation) = n_components(ip) -# n_dbc_components(::Union{RaviartThomas,Nedelec}) = 1 # TODO: Remove: this is a hotfix to apply constraints to embedded elements. edges(ip::InterpolationByDim{2}) = faces(ip) @@ -1526,6 +1525,7 @@ get_mapping_type(::VectorizedInterpolation) = IdentityMapping() # https://defelement.com/elements/qdiv.html struct RaviartThomas{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end get_mapping_type(::RaviartThomas) = ContravariantPiolaMapping() +n_dbc_components(::RaviartThomas) = 1 # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-1.html @@ -1553,7 +1553,9 @@ end ##################################### struct Nedelec{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end get_mapping_type(::Nedelec) = CovariantPiolaMapping() - +reference_coordinates(ip::Nedelec{vdim}) where vdim = fill(NaN*zero(Vec{vdim}), getnbasefunctions(ip)) +dirichlet_facedof_indices(ip::Nedelec) = facedof_interior_indices(ip) +n_dbc_components(::Nedelec) = 1 # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html function shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) From bafd9cefddbf64bd219806c74798a5bfe4a8ee40 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 3 Oct 2023 12:04:55 +0200 Subject: [PATCH 025/172] Remove unintended comments --- src/iterators.jl | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/iterators.jl b/src/iterators.jl index 5e31f934c8..5915dcd02e 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -405,11 +405,4 @@ function _check_same_celltype(grid::AbstractGrid, faceset::Set{FaceIndex}) if !all(getcelltype(grid, face[1]) == celltype for face in faceset) error("The cells in the faceset are not all of the same celltype.") end -end - -#= Remaining tasks -1) Test on quadrilateral -2) Generalize to 2nd order case -3) Generalize to 3d-face -4) Consider RaviartThomas elements for 0-3 as well. -=# \ No newline at end of file +end \ No newline at end of file From eed2740544586e9d8c0c3929659f049da2658c2f Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 7 Oct 2023 16:40:57 +0200 Subject: [PATCH 026/172] Create mixed formulation, RaviartThomas example --- docs/generate.jl | 2 +- docs/make.jl | 17 +- .../literate-tutorials/heat_equation_rt.jl | 268 ++++++++++++++++++ .../heat_equation_triangle.jl | 229 +++++++++++++++ notes/heat_equation_rt_theory.md | 34 +++ .../maxwell_eigenvalue.jl | 62 +++- .../nedelec_raviartthomas_testing.jl | 0 .../nedelec_raviartthomas_testing_refel.jl | 0 8 files changed, 601 insertions(+), 11 deletions(-) create mode 100644 docs/src/literate-tutorials/heat_equation_rt.jl create mode 100644 docs/src/literate-tutorials/heat_equation_triangle.jl create mode 100644 notes/heat_equation_rt_theory.md rename {docs/src/literate-tutorials => notes}/maxwell_eigenvalue.jl (64%) rename {docs/src/literate-tutorials => notes}/nedelec_raviartthomas_testing.jl (100%) rename {docs/src/literate-tutorials => notes}/nedelec_raviartthomas_testing_refel.jl (100%) diff --git a/docs/generate.jl b/docs/generate.jl index fabe1410b5..8bcb08dc78 100644 --- a/docs/generate.jl +++ b/docs/generate.jl @@ -22,7 +22,7 @@ include("download_resources.jl") # Run Literate on all examples @timeit dto "Literate." for (IN, OUT) in [(TUTORIALS_IN, TUTORIALS_OUT), (HOWTO_IN, HOWTO_OUT), (GALLERY_IN, GALLERY_OUT)], program in readdir(IN; join=true) name = basename(program) - if endswith(program, "maxwell_eigenvalue.jl") + if endswith(program, ".jl") if !liveserver script = @timeit dto "script()" @timeit dto name Literate.script(program, OUT) code = strip(read(script, String)) diff --git a/docs/make.jl b/docs/make.jl index a737ad81e8..5f949e897a 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -45,9 +45,10 @@ bibtex_plugin = CitationBibliography( "Home" => "index.md", # hide("Changelog" => "changelog.md"), "Tutorials" => [ - # "Tutorials overview" => "tutorials/index.md", - "tutorials/maxwell_eigenvalue.md", - #= "tutorials/heat_equation.md", + "Tutorials overview" => "tutorials/index.md", + "tutorials/heat_equation.md", + "tutorials/heat_equation_rt.md", + "tutorials/heat_equation_triangle.md", "tutorials/linear_elasticity.md", "tutorials/incompressible_elasticity.md", "tutorials/hyperelasticity.md", @@ -56,9 +57,9 @@ bibtex_plugin = CitationBibliography( "tutorials/computational_homogenization.md", "tutorials/stokes-flow.md", "tutorials/ns_vs_diffeq.md", - "tutorials/linear_shell.md",=# + "tutorials/linear_shell.md", ], -#= "Topic guides" => [ + "Topic guides" => [ "Topic guide overview" => "topics/index.md", "topics/fe_intro.md", "topics/degrees_of_freedom.md", @@ -67,7 +68,7 @@ bibtex_plugin = CitationBibliography( "topics/constraints.md", "topics/grid.md", "topics/export.md" - ],=# + ], "Reference" => [ "Reference overview" => "reference/index.md", "reference/quadrature.md", @@ -80,11 +81,11 @@ bibtex_plugin = CitationBibliography( "reference/export.md", "reference/utils.md", ], - #= "How-to guides" => [ + "How-to guides" => [ "How-to guide overview" => "howto/index.md", "howto/postprocessing.md", "howto/threaded_assembly.md", - ],=# + ], "gallery/index.md", # "Code gallery" => [ # "Code gallery overview" => "gallery/index.md", diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl new file mode 100644 index 0000000000..8d000047bf --- /dev/null +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -0,0 +1,268 @@ +# # [Heat equation](@id tutorial-heat-equation) +# +# ![](heat_square.png) +# +# *Figure 1*: Temperature field on the unit square with an internal uniform heat source +# solved with homogeneous Dirichlet boundary conditions on the boundary. +# +#- +#md # !!! tip +#md # This example is also available as a Jupyter notebook: +#md # [`heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/examples/heat_equation.ipynb). +#- +# +# ## Introduction +# +# ## Strong form +# ```math +# \nabla \cdot \boldsymbol{q} = h \in \Omega \\ +# \boldsymbol{q} = - k\ \nabla u \in \Omega \\ +# \boldsymbol{q}\cdot \boldsymbol{n} = q_n \in \Gamma_\mathrm{N}\\ +# u = u_\mathrm{D} \in \Gamma_\mathrm{D} +# ``` +# +# ## Weak form +# ### Part 1 +# ```math +# \int_{\Omega} \delta u \nabla \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ +# \int_{\Gamma} \delta u \boldsymbol{n} \cdot \boldsymbol{q}\ \mathrm{d}\Gamma - +# \int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ +# ``` +# +# ### Part 2 +# ```math +# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega = - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega +# ``` +# where no Green-Gauss theorem is applied. +# +# ### Summary +# The weak form becomes, find $u\in H^1$ and $\boldsymbol{q} \in H\mathrm{(div)}$, such that +# ```math +# \begin{align*} +# -\int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega - +# \int_{\Gamma} \delta u\ q_\mathrm{n}\ \mathrm{d}\Gamma +# \quad +# \forall\ \delta u \in \delta H^1 \\ +# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega +# \quad \forall\ \boldsymbol{\delta q} \in \delta H\mathrm{(div)} +# \end{align*} +# ``` +# +# ## Commented Program +# +# Now we solve the problem in Ferrite. What follows is a program spliced with comments. +#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 +# We start by generating a simple grid with 20x20 quadrilateral elements +# using `generate_grid`. The generator defaults to the unit square, +# so we don't need to specify the corners of the domain. +grid = generate_grid(Triangle, (100, 100)); + +# ### Trial and test functions +# A `CellValues` facilitates the process of evaluating values and gradients of +# test and trial functions (among other things). To define +# this we need to specify an interpolation space for the shape functions. +# We use Lagrange functions +# based on the two-dimensional reference quadrilateral. We also define a quadrature rule based on +# the same reference element. We combine the interpolation and the quadrature rule +# to a `CellValues` object. +ip_geo = Lagrange{RefTriangle, 1}() +ipu = Lagrange{RefTriangle, 1}() +ipq = RaviartThomas{2,RefTriangle,1}() +qr = QuadratureRule{RefTriangle}(2) +cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) + +# ### Degrees of freedom +# Next we need to define a `DofHandler`, which will take care of numbering +# and distribution of degrees of freedom for our approximated fields. +# We create the `DofHandler` and then add a single scalar field called `:u` based on +# our interpolation `ip` defined above. +# Lastly we `close!` the `DofHandler`, it is now that the dofs are distributed +# for all the elements. +dh = DofHandler(grid) +add!(dh, :u, ipu) +add!(dh, :q, ipq) +close!(dh); + +# Now that we have distributed all our dofs we can create our tangent matrix, +# using `create_sparsity_pattern`. This function returns a sparse matrix +# with the correct entries stored. +K = create_sparsity_pattern(dh) + +# ### Boundary conditions +# In Ferrite constraints like Dirichlet boundary conditions +# are handled by a `ConstraintHandler`. +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. +∂Ω = union( + getfaceset(grid, "left"), + getfaceset(grid, "right"), + getfaceset(grid, "top"), + getfaceset(grid, "bottom"), +); + +# Now we are set up to define our constraint. We specify which field +# the condition is for, and our combined face 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 +# this case do not depend on time we define our function as $f(\textbf{x}) = 0$, i.e. +# no matter what $\textbf{x}$ we return $0$. When we have +# specified our constraint we `add!` it to `ch`. +dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) +add!(ch, dbc); + +# Finally we also need to `close!` our constraint handler. When we call `close!` +# the dofs corresponding to our constraints are calculated and stored +# in our `ch` object. +close!(ch) + +# Note that if one or more of the constraints are time dependent we would use +# [`update!`](@ref) to recompute prescribed values in each new timestep. + +# ### Assembling the linear system +# +# 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 all the elements in order to +# compute the element contributions ``K_e`` and ``f_e``, which are then assembled to the +# appropriate place in the global ``K`` and ``f``. +# +# #### Element assembly +# We define the function `assemble_element!` (see below) which computes the contribution for +# an element. The function takes pre-allocated `ke` and `fe` (they are allocated once and +# then reused for all elements) so we first need to make sure that they are all zeroes at +# the start of the function by using `fill!`. Then we loop over all the quadrature points, +# and for each quadrature point we loop over all the (local) shape functions. We need the +# value and gradient of the test function, `δu` and also the gradient of the trial function +# `u`. We get all of these from `cellvalues`. +# +# !!! note "Notation" +# Comparing with the brief finite element introduction in [Introduction to FEM](@ref), +# the variables `δu`, `∇δu` and `∇u` are actually $\phi_i(\textbf{x}_q)$, $\nabla +# \phi_i(\textbf{x}_q)$ and $\nabla \phi_j(\textbf{x}_q)$, i.e. the evaluation of the +# trial and test functions in the quadrature point ``\textbf{x}_q``. However, to +# underline the strong parallel between the weak form and the implementation, this +# example uses the symbols appearing in the weak form. + +function assemble_element!(Ke::Matrix, fe::Vector, cv::NamedTuple, dr::NamedTuple) + cvu = cv[:u] + cvq = cv[:q] + dru = dr[:u] + drq = dr[:q] + n_basefuncs = getnbasefunctions(cvu) + ## Loop over quadrature points + for q_point in 1:getnquadpoints(cvu) + ## Get the quadrature weight + dΩ = getdetJdV(cvu, q_point) + ## Loop over test shape functions + for (iu, Iu) in pairs(dru) + δu = shape_value(cvu, q_point, iu) + ∇δu = shape_gradient(cvu, q_point, iu) + ## Add contribution to fe + fe[Iu] += δu * dΩ + ## Loop over trial shape functions + for (jq, Jq) in pairs(drq) + q = shape_value(cvq, q_point, jq) + ## Add contribution to Ke + Ke[Iu, Jq] -= (∇δu ⋅ q) * dΩ + end + end + for (iq, Iq) in pairs(drq) + δq = shape_value(cvq, q_point, iq) + for (ju, Ju) in pairs(dru) + ∇u = shape_gradient(cvu, q_point, ju) + Ke[Iq, Ju] += (δq ⋅ ∇u) * dΩ + end + for (jq, Jq) in pairs(drq) + q = shape_value(cvq, q_point, jq) + Ke[Iq, Jq] += (δq ⋅ q) * dΩ + end + end + end + return Ke, fe +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 our `cellvalues`, the sparse matrix `K`, and our DofHandler +# as input arguments and returns the assembled global stiffness matrix, and the assembled +# global force vector. We start by allocating `Ke`, `fe`, and the global force vector `f`. +# We also create an assembler by using `start_assemble`. The assembler lets us assemble into +# `K` and `f` efficiently. We then start the loop over all the elements. In each loop +# iteration we reinitialize `cellvalues` (to update derivatives of shape functions etc.), +# compute the element contribution with `assemble_element!`, and then assemble into the +# global `K` and `f` with `assemble!`. +# +# !!! note "Notation" +# Comparing again with [Introduction to FEM](@ref), `f` and `u` correspond to +# $\underline{\hat{f}}$ and $\underline{\hat{u}}$, since they represent the discretized +# versions. However, through the code we use `f` and `u` instead to reflect the strong +# connection between the weak form and the Ferrite implementation. + +function assemble_global(cellvalues, K::SparseMatrixCSC, dh::DofHandler) + ## Allocate the element stiffness matrix and element force vector + dofranges = (u = dof_range(dh, :u), q = dof_range(dh, :q)) + ncelldofs = ndofs_per_cell(dh) + Ke = zeros(ncelldofs, ncelldofs) + fe = zeros(ncelldofs) + ## Allocate global force vector f + f = zeros(ndofs(dh)) + ## Create an assembler + assembler = start_assemble(K, f) + x = copy(getcoordinates(dh.grid, 1)) + dofs = copy(celldofs(dh, 1)) + ## Loop over all cels + for cellnr in 1:getncells(dh.grid) + ## Reinitialize cellvalues for this cell + cell = getcells(dh.grid, cellnr) + getcoordinates!(x, grid, cell) + celldofs!(dofs, dh, cellnr) + reinit!(cellvalues[:u], x, cell) + reinit!(cellvalues[:q], x, cell) + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues, dofranges) + ## Assemble Ke and fe into K and f + assemble!(assembler, dofs, Ke, fe) + end + return K, f +end +#md nothing # hide + +# ### Solution of the system +# The last step is to solve the system. First we call `assemble_global` +# to obtain the global stiffness matrix `K` and force vector `f`. +K, f = assemble_global(cellvalues, K, dh); + +# To account for the boundary conditions we use the `apply!` function. +# This modifies elements in `K` and `f` respectively, such that +# we can get the correct solution vector `u` by using `\`. +apply!(K, f, ch) +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/). +u_nodes = evaluate_at_grid_nodes(dh, u, :u) +vtk_grid("heat_equation_rt", dh) do vtk + vtk_point_data(vtk, u_nodes, "u") +end + +@show norm(u_nodes)/length(u_nodes) + +#md # ## [Plain program](@id heat_equation-plain-program) +#md # +#md # Here follows a version of the program without any comments. +#md # The file is also available here: [`heat_equation.jl`](heat_equation.jl). +#md # +#md # ```julia +#md # @__CODE__ +#md # ``` diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl new file mode 100644 index 0000000000..d5ea45e4df --- /dev/null +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -0,0 +1,229 @@ +# # [Heat equation](@id tutorial-heat-equation) +# +# ![](heat_square.png) +# +# *Figure 1*: Temperature field on the unit square with an internal uniform heat source +# solved with homogeneous Dirichlet boundary conditions on the boundary. +# +#- +#md # !!! tip +#md # This example is also available as a Jupyter notebook: +#md # [`heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/examples/heat_equation.ipynb). +#- +# +# ## Introduction +# +# The heat equation is the "Hello, world!" equation of finite elements. +# Here we solve the equation on a unit square, with a uniform internal source. +# The strong form of the (linear) heat equation is given by +# +# ```math +# -\nabla \cdot (k \nabla u) = f \quad \textbf{x} \in \Omega, +# ``` +# +# where $u$ is the unknown temperature field, $k$ the heat conductivity, +# $f$ the heat source and $\Omega$ the domain. For simplicity we set $f = 1$ +# and $k = 1$. We will consider homogeneous Dirichlet boundary conditions such that +# ```math +# u(\textbf{x}) = 0 \quad \textbf{x} \in \partial \Omega, +# ``` +# where $\partial \Omega$ denotes the boundary of $\Omega$. +# The resulting weak form is given given as follows: Find ``u \in \mathbb{U}`` such that +# ```math +# \int_{\Omega} \nabla \delta u \cdot \nabla u \ d\Omega = \int_{\Omega} \delta u \ d\Omega \quad \forall \delta u \in \mathbb{T}, +# ``` +# where $\delta u$ is a test function, and where $\mathbb{U}$ and $\mathbb{T}$ are suitable +# trial and test function sets, respectively. +#- +# ## Commented Program +# +# Now we solve the problem in Ferrite. What follows is a program spliced with comments. +#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 +# We start by generating a simple grid with 20x20 quadrilateral elements +# using `generate_grid`. The generator defaults to the unit square, +# so we don't need to specify the corners of the domain. +grid = generate_grid(Triangle, (100, 100)); + +# ### Trial and test functions +# A `CellValues` facilitates the process of evaluating values and gradients of +# test and trial functions (among other things). To define +# this we need to specify an interpolation space for the shape functions. +# We use Lagrange functions +# based on the two-dimensional reference quadrilateral. We also define a quadrature rule based on +# the same reference element. We combine the interpolation and the quadrature rule +# to a `CellValues` object. +ip = Lagrange{RefTriangle, 1}() +qr = QuadratureRule{RefTriangle}(2) +cellvalues = CellValues(qr, ip); + +# ### Degrees of freedom +# Next we need to define a `DofHandler`, which will take care of numbering +# and distribution of degrees of freedom for our approximated fields. +# We create the `DofHandler` and then add a single scalar field called `:u` based on +# our interpolation `ip` defined above. +# Lastly we `close!` the `DofHandler`, it is now that the dofs are distributed +# for all the elements. +dh = DofHandler(grid) +add!(dh, :u, ip) +close!(dh); + +# Now that we have distributed all our dofs we can create our tangent matrix, +# using `create_sparsity_pattern`. This function returns a sparse matrix +# with the correct entries stored. +K = create_sparsity_pattern(dh) + +# ### Boundary conditions +# In Ferrite constraints like Dirichlet boundary conditions +# are handled by a `ConstraintHandler`. +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. +∂Ω = union( + getfaceset(grid, "left"), + getfaceset(grid, "right"), + getfaceset(grid, "top"), + getfaceset(grid, "bottom"), +); + +# Now we are set up to define our constraint. We specify which field +# the condition is for, and our combined face 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 +# this case do not depend on time we define our function as $f(\textbf{x}) = 0$, i.e. +# no matter what $\textbf{x}$ we return $0$. When we have +# specified our constraint we `add!` it to `ch`. +dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) +add!(ch, dbc); + +# Finally we also need to `close!` our constraint handler. When we call `close!` +# the dofs corresponding to our constraints are calculated and stored +# in our `ch` object. +close!(ch) + +# Note that if one or more of the constraints are time dependent we would use +# [`update!`](@ref) to recompute prescribed values in each new timestep. + +# ### Assembling the linear system +# +# 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 all the elements in order to +# compute the element contributions ``K_e`` and ``f_e``, which are then assembled to the +# appropriate place in the global ``K`` and ``f``. +# +# #### Element assembly +# We define the function `assemble_element!` (see below) which computes the contribution for +# an element. The function takes pre-allocated `ke` and `fe` (they are allocated once and +# then reused for all elements) so we first need to make sure that they are all zeroes at +# the start of the function by using `fill!`. Then we loop over all the quadrature points, +# and for each quadrature point we loop over all the (local) shape functions. We need the +# value and gradient of the test function, `δu` and also the gradient of the trial function +# `u`. We get all of these from `cellvalues`. +# +# !!! note "Notation" +# Comparing with the brief finite element introduction in [Introduction to FEM](@ref), +# the variables `δu`, `∇δu` and `∇u` are actually $\phi_i(\textbf{x}_q)$, $\nabla +# \phi_i(\textbf{x}_q)$ and $\nabla \phi_j(\textbf{x}_q)$, i.e. the evaluation of the +# trial and test functions in the quadrature point ``\textbf{x}_q``. However, to +# underline the strong parallel between the weak form and the implementation, this +# example uses the symbols appearing in the weak form. + +function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues) + n_basefuncs = getnbasefunctions(cellvalues) + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Loop over quadrature points + for q_point in 1:getnquadpoints(cellvalues) + ## Get the quadrature weight + dΩ = getdetJdV(cellvalues, q_point) + ## Loop over test shape functions + for i in 1:n_basefuncs + δu = shape_value(cellvalues, q_point, i) + ∇δu = shape_gradient(cellvalues, q_point, i) + ## Add contribution to fe + fe[i] += δu * dΩ + ## Loop over trial shape functions + for j in 1:n_basefuncs + ∇u = shape_gradient(cellvalues, q_point, j) + ## Add contribution to Ke + Ke[i, j] += (∇δu ⋅ ∇u) * dΩ + end + end + end + return Ke, fe +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 our `cellvalues`, the sparse matrix `K`, and our DofHandler +# as input arguments and returns the assembled global stiffness matrix, and the assembled +# global force vector. We start by allocating `Ke`, `fe`, and the global force vector `f`. +# We also create an assembler by using `start_assemble`. The assembler lets us assemble into +# `K` and `f` efficiently. We then start the loop over all the elements. In each loop +# iteration we reinitialize `cellvalues` (to update derivatives of shape functions etc.), +# compute the element contribution with `assemble_element!`, and then assemble into the +# global `K` and `f` with `assemble!`. +# +# !!! note "Notation" +# Comparing again with [Introduction to FEM](@ref), `f` and `u` correspond to +# $\underline{\hat{f}}$ and $\underline{\hat{u}}$, since they represent the discretized +# versions. However, through the code we use `f` and `u` instead to reflect the strong +# connection between the weak form and the Ferrite implementation. + +function assemble_global(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHandler) + ## Allocate the element stiffness matrix and element force vector + n_basefuncs = getnbasefunctions(cellvalues) + Ke = zeros(n_basefuncs, n_basefuncs) + fe = zeros(n_basefuncs) + ## Allocate global force vector f + f = zeros(ndofs(dh)) + ## Create an assembler + assembler = start_assemble(K, f) + ## Loop over all cels + for cell in CellIterator(dh) + ## Reinitialize cellvalues for this cell + reinit!(cellvalues, cell) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues) + ## Assemble Ke and fe into K and f + assemble!(assembler, celldofs(cell), Ke, fe) + end + return K, f +end +#md nothing # hide + +# ### Solution of the system +# The last step is to solve the system. First we call `assemble_global` +# to obtain the global stiffness matrix `K` and force vector `f`. +K, f = assemble_global(cellvalues, K, dh); + +# To account for the boundary conditions we use the `apply!` function. +# This modifies elements in `K` and `f` respectively, such that +# we can get the correct solution vector `u` by using `\`. +apply!(K, f, ch) +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/). +vtk_grid("heat_equation", dh) do vtk + vtk_point_data(vtk, dh, u) +end + +@show norm(u)/length(u) + +#md # ## [Plain program](@id heat_equation-plain-program) +#md # +#md # Here follows a version of the program without any comments. +#md # The file is also available here: [`heat_equation.jl`](heat_equation.jl). +#md # +#md # ```julia +#md # @__CODE__ +#md # ``` diff --git a/notes/heat_equation_rt_theory.md b/notes/heat_equation_rt_theory.md new file mode 100644 index 0000000000..28088bbd3c --- /dev/null +++ b/notes/heat_equation_rt_theory.md @@ -0,0 +1,34 @@ +## Strong form +$$ +\nabla \cdot \boldsymbol{q} = h \in \Omega \\ +\boldsymbol{q} = - k\ \nabla u \in \Omega \\ +\boldsymbol{q}\cdot \boldsymbol{n} = q_n \in \Gamma_\mathrm{N}\\ +u = u_\mathrm{D} \in \Gamma_\mathrm{D} +$$ + +## Weak form +### Part 1 +$$ +\int_{\Omega} \delta u \nabla \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ +\int_{\Gamma} \delta u \boldsymbol{n} \cdot \boldsymbol{q}\ \mathrm{d}\Gamma - +\int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ +$$ + +### Part 2 +$$ +\int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega = - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega +$$ +where no Green-Gauss theorem is applied. + +### Summary +The weak form becomes, find $u\in H^1$ and $\boldsymbol{q} \in H\mathrm{(div)}$, such that +$$ +\begin{align*} +-\int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega - +\int_{\Gamma} \delta u\ q_\mathrm{n}\ \mathrm{d}\Gamma +\quad +\forall\ \delta u \in \delta H^1 \\ +\int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega + \quad \forall\ \boldsymbol{\delta q} \in \delta H\mathrm{(div)} +\end{align*} +$$ diff --git a/docs/src/literate-tutorials/maxwell_eigenvalue.jl b/notes/maxwell_eigenvalue.jl similarity index 64% rename from docs/src/literate-tutorials/maxwell_eigenvalue.jl rename to notes/maxwell_eigenvalue.jl index 782937ae01..2c64dfa140 100644 --- a/docs/src/literate-tutorials/maxwell_eigenvalue.jl +++ b/notes/maxwell_eigenvalue.jl @@ -23,6 +23,7 @@ using Ferrite import Ferrite: Nedelec, RaviartThomas import IterativeSolvers: lobpcg using LinearAlgebra +import CairoMakie as M function assemble_cell!(Ae, Be, cv) n = getnbasefunctions(cv) @@ -42,6 +43,49 @@ function assemble_cell!(Ae, Be, cv) return Ae, Be end +function plot_shapes(dh, ip, a) + cv = CellValues(QuadratureRule{RefTriangle}(1), ip, Lagrange{RefTriangle,1}()) + grid = dh.grid + n_cells = getncells(grid) + coords = (zeros(n_cells), zeros(n_cells)) + vectors = (zeros(n_cells), zeros(n_cells)) + + for cell_nr in 1:getncells(grid) + x = getcoordinates(grid, cell_nr) + reinit!(cv, x, getcells(grid, cell_nr)) + ue = a[celldofs(dh, cell_nr)] + for q_point in 1:getnquadpoints(cv) + #i = getnquadpoints(cv)*(cell_nr-1) + q_point + i = cell_nr + qp_x = spatial_coordinate(cv, q_point, x) + v = function_value(cv, q_point, ue) + sfac = norm(v) ≈ 0 ? NaN : 1.0 # Skip plotting zero-vector points + coords[1][i] = sfac*qp_x[1] + coords[2][i] = sfac*qp_x[2] + vectors[1][i] = v[1] + vectors[2][i] = v[2] + end + end + vtk_grid("tmp", dh.grid) do vtk + vtk_cell_data(vtk, vectors[1], "u1") + vtk_cell_data(vtk, vectors[2], "u2") + end + nothing, nothing + #= + fig = M.Figure() + for i in 1:2 + ax = M.Axis(fig[i,1]; aspect=M.DataAspect()) + #=for cellnr in 1:getncells(grid) + x = getcoordinates(grid, cellnr) + push!(x, x[1]) + M.lines!(ax, first.(x), last.(x), color=:black) + end=# + M.scatter!(ax, coords..., vectors[i]; lengthscale=0.1) + end + return fig + =# +end + function doassemble(dh::DofHandler, cv::CellValues) grid = dh.grid A, B = create_sparsity_pattern.((dh, dh)) @@ -87,7 +131,7 @@ function get_matrices(ip::Interpolation; CT=Quadrilateral, nel=40, usebc=true) apply!(A, ch) apply!(B, ch) end - return A, B + return A, B, dh end function solve(ip; num_values, kwargs...) @@ -97,7 +141,21 @@ function solve(ip; num_values, kwargs...) return r.λ end +function solve_single(ip, λ=2; kwargs...) + A, B, dh = get_matrices(ip; kwargs...) + a = (A-λ*B)\zeros(size(A,1)) + return dh, a +end + ip = Nedelec{2,RefTriangle,1}() #ip = Lagrange{RefTriangle,1}()^2 +dh, a = solve_single(ip, CT=Triangle) +cv = CellValues(QuadratureRule{RefTriangle}(2), ip, Lagrange{RefTriangle,1}()) +fig = plot_shapes(dh, ip, a) +#λ = solve(ip; CT=Triangle, num_values=10) -λ = solve(ip; CT=Triangle, num_values=10) \ No newline at end of file +m, n = (1,1) +λ=m^2+n^2 +u(x,y)=Vec((sin(m*x),sin(n*y))) +a = zeros(ndofs) +apply_analytical!() \ No newline at end of file diff --git a/docs/src/literate-tutorials/nedelec_raviartthomas_testing.jl b/notes/nedelec_raviartthomas_testing.jl similarity index 100% rename from docs/src/literate-tutorials/nedelec_raviartthomas_testing.jl rename to notes/nedelec_raviartthomas_testing.jl diff --git a/docs/src/literate-tutorials/nedelec_raviartthomas_testing_refel.jl b/notes/nedelec_raviartthomas_testing_refel.jl similarity index 100% rename from docs/src/literate-tutorials/nedelec_raviartthomas_testing_refel.jl rename to notes/nedelec_raviartthomas_testing_refel.jl From 026c753aa02bc9e4ee38841cb925aa3c9d642876 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 7 Oct 2023 17:11:17 +0200 Subject: [PATCH 027/172] Update docs manifest --- docs/Manifest.toml | 264 +++++++++++++++++++++++++++++---------------- 1 file changed, 171 insertions(+), 93 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 073d66c467..52fe3bffa6 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -91,9 +91,9 @@ version = "0.1.29" [[deps.ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "0d61921af2799487b80453a44abb57db7a0c1381" +git-tree-sha1 = "9a731850434825d183af39c6e6cd0a1c32dd7e20" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.4.1" +version = "1.4.2" weakdeps = ["SparseArrays"] [deps.ArrayLayouts.extensions] @@ -197,9 +197,9 @@ version = "1.0.5" [[deps.CairoMakie]] deps = ["Base64", "Cairo", "Colors", "FFTW", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools", "SHA"] -git-tree-sha1 = "696e7931bd6f5c773418452cbe5fd241cb85ac2a" +git-tree-sha1 = "74384dc4aba2b377e22703e849154252930c434d" uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" -version = "0.10.9" +version = "0.10.11" [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -239,9 +239,9 @@ version = "0.4.0" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "d9a8f86737b665e15a9641ecbac64deef9ce6724" +git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.23.0" +version = "3.24.0" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] @@ -279,9 +279,9 @@ version = "0.3.0" [[deps.Compat]] deps = ["UUIDs"] -git-tree-sha1 = "e460f044ca8b99be31d35fe54fc33a5c33dd8ed7" +git-tree-sha1 = "8a62af3e248a8c4bad6b32cbbe663ae02275e32c" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.9.0" +version = "4.10.0" weakdeps = ["Dates", "LinearAlgebra"] [deps.Compat.extensions] @@ -292,6 +292,11 @@ deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" version = "1.0.5+0" +[[deps.ConcreteStructs]] +git-tree-sha1 = "f749037478283d372048690eb3b5f92a79432b34" +uuid = "2569d6c7-a4a2-43d3-a901-331e8e4be471" +version = "0.2.3" + [[deps.ConcurrentUtilities]] deps = ["Serialization", "Sockets"] git-tree-sha1 = "5372dbbf8f0bdb8c700db5367132925c0771ef7e" @@ -353,13 +358,14 @@ uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" version = "1.9.1" [[deps.DiffEqBase]] -deps = ["ArrayInterface", "ChainRulesCore", "DataStructures", "DocStringExtensions", "EnumX", "FastBroadcast", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "Printf", "RecursiveArrayTools", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces", "ZygoteRules"] -git-tree-sha1 = "dee066b8dce741815729f5973b6db757416948b7" +deps = ["ArrayInterface", "ChainRulesCore", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces", "ZygoteRules"] +git-tree-sha1 = "766ab4574433d22ff75ab28e9081114e73cef5d5" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.128.4" +version = "6.132.0" [deps.DiffEqBase.extensions] DiffEqBaseDistributionsExt = "Distributions" + DiffEqBaseEnzymeExt = "Enzyme" DiffEqBaseGeneralizedGeneratedExt = "GeneralizedGenerated" DiffEqBaseMPIExt = "MPI" DiffEqBaseMeasurementsExt = "Measurements" @@ -371,6 +377,7 @@ version = "6.128.4" [deps.DiffEqBase.weakdeps] Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" @@ -394,12 +401,13 @@ version = "1.15.1" [[deps.Distances]] deps = ["LinearAlgebra", "Statistics", "StatsAPI"] -git-tree-sha1 = "b6def76ffad15143924a2199f72a5cd883a2e8a9" +git-tree-sha1 = "5225c965635d8c21168e32a12954675e7bea1151" uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.9" -weakdeps = ["SparseArrays"] +version = "0.10.10" +weakdeps = ["ChainRulesCore", "SparseArrays"] [deps.Distances.extensions] + DistancesChainRulesCoreExt = "ChainRulesCore" DistancesSparseArraysExt = "SparseArrays" [[deps.Distributed]] @@ -408,9 +416,9 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[deps.Distributions]] deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns", "Test"] -git-tree-sha1 = "938fe2981db009f531b6332e31c58e9584a2f9bd" +git-tree-sha1 = "3d5873f811f582873bb9871fc9c451784d5dc8c7" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.100" +version = "0.25.102" [deps.Distributions.extensions] DistributionsChainRulesCoreExt = "ChainRulesCore" @@ -428,15 +436,15 @@ version = "0.9.3" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "Dates", "DocStringExtensions", "Downloads", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "Test", "Unicode"] -git-tree-sha1 = "43a76dfac223a3a0d7d33443f7c9154fe75bb264" +git-tree-sha1 = "f667b805e90d643aeb1ca70189827f991a7cc115" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.0.0" +version = "1.1.0" [[deps.DocumenterCitations]] deps = ["AbstractTrees", "Bibliography", "Documenter", "Markdown", "MarkdownAST", "OrderedCollections", "Unicode"] -git-tree-sha1 = "375b49c31c2c3676f3ddb65efea31c7dbea42af1" +git-tree-sha1 = "0c5c141a66807796d580ef4fe592647132832f39" uuid = "daee34ce-89f3-4625-b898-19384cb65244" -version = "1.2.0" +version = "1.2.1" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] @@ -460,6 +468,12 @@ git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.4" +[[deps.EnzymeCore]] +deps = ["Adapt"] +git-tree-sha1 = "1091d4bbc2f2f7840a65fc0496c782b955dd82fb" +uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" +version = "0.6.0" + [[deps.EpollShim_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643" @@ -580,9 +594,9 @@ version = "1.0.1" [[deps.FerriteMeshParser]] deps = ["Ferrite"] -git-tree-sha1 = "34366b4cc58c6513dff733f5c2dfd3a4f07e5d40" +git-tree-sha1 = "8b948577bc4066e9c8693438fd511309c7383761" uuid = "0f8c756f-80dd-4a75-85c6-b0a5ab9d4620" -version = "0.1.6" +version = "0.1.7" [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] @@ -712,15 +726,15 @@ version = "0.1.5" [[deps.GR]] deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "UUIDs", "p7zip_jll"] -git-tree-sha1 = "d73afa4a2bb9de56077242d98cf763074ab9a970" +git-tree-sha1 = "27442171f28c952804dede8ff72828a96f2bfc1f" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.72.9" +version = "0.72.10" [[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 = "1596bab77f4f073a14c62424283e7ebff3072eca" +git-tree-sha1 = "025d171a2847f616becc0f84c8dc62fe18f0f6dd" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.72.9+1" +version = "0.72.10+0" [[deps.GenericSchur]] deps = ["LinearAlgebra", "Printf"] @@ -730,9 +744,9 @@ version = "0.5.3" [[deps.GeoInterface]] deps = ["Extents"] -git-tree-sha1 = "bb198ff907228523f3dee1070ceee63b9359b6ab" +git-tree-sha1 = "d53480c0793b13341c40199190f92c611aa2e93c" uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" -version = "1.3.1" +version = "1.3.2" [[deps.GeometryBasics]] deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] @@ -772,9 +786,9 @@ version = "1.3.14+0" [[deps.Graphs]] deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] -git-tree-sha1 = "1cf1d7dcb4bc32d7b4a5add4232db3750c27ecb4" +git-tree-sha1 = "899050ace26649433ef1af25bc17a815b3db52b7" uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" -version = "1.8.0" +version = "1.9.0" [[deps.GridLayoutBase]] deps = ["GeometryBasics", "InteractiveUtils", "Observables"] @@ -795,9 +809,9 @@ version = "1.12.2+2" [[deps.HTTP]] deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "19e974eced1768fb46fd6020171f2cec06b1edb5" +git-tree-sha1 = "5eab648309e2e060198b45820af1a37182de3cce" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.9.15" +version = "1.10.0" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] @@ -970,9 +984,9 @@ version = "1.3.0" [[deps.JpegTurbo]] deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"] -git-tree-sha1 = "327713faef2a3e5c80f96bf38d1fa26f7a6ae29e" +git-tree-sha1 = "d65930fa2bc96b07d7691c652d701dcbe7d9cf0b" uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0" -version = "0.1.3" +version = "0.1.4" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1166,26 +1180,30 @@ uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" version = "5.0.0+0" [[deps.LinearSolve]] -deps = ["ArrayInterface", "DocStringExtensions", "EnumX", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "Libdl", "LinearAlgebra", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "SuiteSparse", "UnPack"] -git-tree-sha1 = "69cbd612e6e67ba2f8121bc8725bc9d04d803599" +deps = ["ArrayInterface", "ConcreteStructs", "DocStringExtensions", "EnumX", "EnzymeCore", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "Libdl", "LinearAlgebra", "MKL_jll", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "SuiteSparse", "UnPack"] +git-tree-sha1 = "435ab14ca589757a0feae6e3e347bc37addda42d" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "2.5.1" +version = "2.9.2" [deps.LinearSolve.extensions] + LinearSolveBlockDiagonalsExt = "BlockDiagonals" LinearSolveCUDAExt = "CUDA" + LinearSolveEnzymeExt = "Enzyme" LinearSolveHYPREExt = "HYPRE" LinearSolveIterativeSolversExt = "IterativeSolvers" + LinearSolveKernelAbstractionsExt = "KernelAbstractions" LinearSolveKrylovKitExt = "KrylovKit" - LinearSolveMKLExt = "MKL_jll" LinearSolveMetalExt = "Metal" LinearSolvePardisoExt = "Pardiso" [deps.LinearSolve.weakdeps] + BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" + KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" - MKL_jll = "856f044c-d86e-5d09-b602-aeab76dc8ba7" Metal = "dde4c033-4e86-420c-a63e-0dd931031962" Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" @@ -1222,9 +1240,9 @@ uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" [[deps.LoggingExtras]] deps = ["Dates", "Logging"] -git-tree-sha1 = "0d097476b6c381ab7906460ef1ef1638fbce1d91" +git-tree-sha1 = "c1dd6d7978c12545b4179fb6153b9250c96b0075" uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" -version = "1.0.2" +version = "1.0.3" [[deps.LoopVectorization]] deps = ["ArrayInterface", "ArrayInterfaceCore", "CPUSummary", "CloseOpenIntervals", "DocStringExtensions", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "PrecompileTools", "SIMDTypes", "SLEEFPirates", "Static", "StaticArrayInterface", "ThreadingUtilities", "UnPack", "VectorizationBase"] @@ -1268,15 +1286,15 @@ version = "0.5.11" [[deps.Makie]] deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FixedPointNumbers", "Formatting", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "InteractiveUtils", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "Match", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Setfield", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "StableHashTraits", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun"] -git-tree-sha1 = "ecc334efc4a8a68800776b0d85ab7bb2fff63f7a" +git-tree-sha1 = "1d16d20279a145119899b4205258332f0fbeaa94" uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" -version = "0.19.9" +version = "0.19.11" [[deps.MakieCore]] -deps = ["Observables"] -git-tree-sha1 = "1efb1166dd9398f2ccf6d728f896658c9c84733e" +deps = ["Observables", "REPL"] +git-tree-sha1 = "a94bf3fef9c690a2a4ac1d09d86a59ab89c7f8e4" uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" -version = "0.6.6" +version = "0.6.8" [[deps.ManualMemory]] git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" @@ -1294,9 +1312,9 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[deps.MarkdownAST]] deps = ["AbstractTrees", "Markdown"] -git-tree-sha1 = "e8513266815200c0c8f522d6d44ffb5e9b366ae4" +git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" -version = "0.1.1" +version = "0.1.2" [[deps.Match]] git-tree-sha1 = "1d9bc5c1a6e7ee24effb93f175c9342f9154d97f" @@ -1394,10 +1412,10 @@ uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" [[deps.NonlinearSolve]] -deps = ["ArrayInterface", "DiffEqBase", "EnumX", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "PrecompileTools", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "ee53089df81a6bdf3c06c17cf674e90931b10a73" +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "EnumX", "FiniteDiff", "ForwardDiff", "LineSearches", "LinearAlgebra", "LinearSolve", "PrecompileTools", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "UnPack"] +git-tree-sha1 = "445a7ba86794e1f8ee9da3b3b7becf284e2625fd" uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" -version = "1.10.0" +version = "2.1.0" [[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"] @@ -1481,9 +1499,9 @@ version = "1.6.2" [[deps.OrdinaryDiffEq]] deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "IfElse", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "LoopVectorization", "MacroTools", "MuladdMacro", "NLsolve", "NonlinearSolve", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLNLSolve", "SciMLOperators", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] -git-tree-sha1 = "ba3ed480f991b846cf9a8118d3370d9752e7166d" +git-tree-sha1 = "def999a7447854f0e9ca9fdda235e04a65916b76" uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -version = "6.55.0" +version = "6.58.0" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] @@ -1492,9 +1510,9 @@ version = "10.42.0+0" [[deps.PDMats]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "67eae2738d63117a196f497d7db789821bce61d1" +git-tree-sha1 = "fcf8fd477bd7f33cb8dbb1243653fb0d415c256c" uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.17" +version = "0.11.25" [[deps.PNGFiles]] deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"] @@ -1503,9 +1521,9 @@ uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" version = "0.4.0" [[deps.PackageExtensionCompat]] -git-tree-sha1 = "f9b1e033c2b1205cf30fd119f4e50881316c1923" +git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" -version = "1.0.1" +version = "1.0.2" weakdeps = ["Requires", "TOML"] [[deps.Packing]] @@ -1600,9 +1618,9 @@ version = "1.39.0" [[deps.Polyester]] deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Requires", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] -git-tree-sha1 = "3d811babe092a6e7b130beee84998fe7663348b6" +git-tree-sha1 = "c7dc9720390fcc296bf757b3f833f9e41c68a086" uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" -version = "0.7.5" +version = "0.7.7" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] @@ -1659,9 +1677,9 @@ version = "1.2.0" [[deps.Preferences]] deps = ["TOML"] -git-tree-sha1 = "7eb1686b4f04b82f96ed7a4ea5890a4f0c7a09f1" +git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.0" +version = "1.4.1" [[deps.Primes]] deps = ["IntegerMathUtils"] @@ -1686,16 +1704,16 @@ uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" version = "1.0.0" [[deps.Qt6Base_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"] -git-tree-sha1 = "364898e8f13f7eaaceec55fd3d08680498c0aa6e" +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"] +git-tree-sha1 = "7c29f0e8c575428bd84dc3c72ece5178caa67336" uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" -version = "6.4.2+3" +version = "6.5.2+2" [[deps.QuadGK]] deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "6ec7ac8412e83d57e313393220879ede1740f9ee" +git-tree-sha1 = "9ebcd48c498668c7fa0e97a9cae873fbee7bfee1" uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.8.2" +version = "2.9.1" [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] @@ -1837,21 +1855,27 @@ version = "0.6.39" [[deps.SciMLBase]] deps = ["ADTypes", "ArrayInterface", "ChainRulesCore", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FillArrays", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables", "TruncatedStacktraces", "ZygoteRules"] -git-tree-sha1 = "6de099dba3c80e23bde1d19011161ea91a23ed6b" +git-tree-sha1 = "1e09c5c89f5502eb4e3657730b0fb6817a1bca8d" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "1.98.0" +version = "2.3.0" [deps.SciMLBase.extensions] - ZygoteExt = "Zygote" + SciMLBasePyCallExt = "PyCall" + SciMLBasePythonCallExt = "PythonCall" + SciMLBaseRCallExt = "RCall" + SciMLBaseZygoteExt = "Zygote" [deps.SciMLBase.weakdeps] + PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" + PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" + RCall = "6f49c342-dc21-5d91-9882-a32aef131414" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SciMLNLSolve]] deps = ["DiffEqBase", "LineSearches", "NLsolve", "Reexport", "SciMLBase"] -git-tree-sha1 = "9dfc8e9e3d58c0c74f1a821c762b5349da13eccf" +git-tree-sha1 = "765b788339abd7d983618c09cfc0192e2b6b15fd" uuid = "e9a6253c-8580-4d32-9898-8661bb511710" -version = "0.1.8" +version = "0.1.9" [[deps.SciMLOperators]] deps = ["ArrayInterface", "DocStringExtensions", "Lazy", "LinearAlgebra", "Setfield", "SparseArrays", "StaticArraysCore", "Tricks"] @@ -1881,9 +1905,9 @@ version = "1.1.1" [[deps.ShaderAbstractions]] deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "0d15c3e7b2003f4451714f08ffec2b77badc2dc4" +git-tree-sha1 = "db0219befe4507878b1a90e07820fed3e62c289d" uuid = "65257c39-d410-5151-9873-9b3e5be5013e" -version = "0.3.0" +version = "0.4.0" [[deps.SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] @@ -1914,9 +1938,9 @@ version = "0.8.4" [[deps.SimpleNonlinearSolve]] deps = ["ArrayInterface", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "PackageExtensionCompat", "PrecompileTools", "Reexport", "SciMLBase", "StaticArraysCore"] -git-tree-sha1 = "20aa9831d654bab67ed561e78917047143ecb9bf" +git-tree-sha1 = "4d53b83af904049c493daaf2a225bcae994a3c59" uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "0.1.19" +version = "0.1.20" [deps.SimpleNonlinearSolve.extensions] SimpleNonlinearSolveNNlibExt = "NNlib" @@ -1979,10 +2003,10 @@ deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[deps.SparseDiffTools]] -deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "Tricks", "UnPack", "VertexSafeGraphs"] -git-tree-sha1 = "42d131931906bf4f0af97a7113c8456d0a8aff9d" +deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Random", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "Tricks", "UnPack", "VertexSafeGraphs"] +git-tree-sha1 = "0a4538040f6eeae9016043104056dc6c13e1d8c1" uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "2.6.0" +version = "2.7.0" [deps.SparseDiffTools.extensions] SparseDiffToolsEnzymeExt = "Enzyme" @@ -2041,9 +2065,9 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "Random", "StaticArraysCore"] -git-tree-sha1 = "51621cca8651d9e334a659443a74ce50a3b6dfab" +git-tree-sha1 = "0adf069a2a490c47273727e029371b31d44b72b2" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.6.3" +version = "1.6.5" weakdeps = ["Statistics"] [deps.StaticArrays.extensions] @@ -2067,9 +2091,9 @@ version = "1.7.0" [[deps.StatsBase]] deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "75ebe04c5bed70b91614d684259b661c9e6274a4" +git-tree-sha1 = "1d77abd07f617c4868c33d4f5b9e1dbb2643c9cf" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.0" +version = "0.34.2" [[deps.StatsFuns]] deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] @@ -2136,10 +2160,10 @@ uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" version = "1.0.1" [[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits", "Test"] -git-tree-sha1 = "1544b926975372da01227b382066ab70e574a3ec" +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "a1f34829d5ac0ef499f6d84428bd6b4c71f02ead" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.10.1" +version = "1.11.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -2172,9 +2196,9 @@ version = "0.5.2" [[deps.TiffImages]] deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "UUIDs"] -git-tree-sha1 = "8621f5c499a8aa4aa970b1ae381aae0ef1576966" +git-tree-sha1 = "7fd97bd1c5b1ff53a291cbd351d1d3d6ff4da5a5" uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" -version = "0.6.4" +version = "0.6.7" [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] @@ -2280,11 +2304,17 @@ git-tree-sha1 = "8351f8d73d7e880bfc042a8b6922684ebeafb35c" uuid = "19fa3120-7c27-5ec5-8db8-b0b0aa330d6f" version = "0.2.0" +[[deps.Vulkan_Loader_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] +git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" +uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" +version = "1.3.243+0" + [[deps.Wayland_jll]] deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "ed8d92d9774b077c53e1da50fd81a36af3744c1c" +git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.21.0+0" +version = "1.21.0+1" [[deps.Wayland_protocols_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -2306,9 +2336,9 @@ version = "1.18.0" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "04a51d15436a572301b5abbb9d099713327e9fc4" +git-tree-sha1 = "24b81b59bd35b3c42ab84fa589086e19be919916" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.10.4+0" +version = "2.11.5+0" [[deps.XSLT_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] @@ -2322,6 +2352,18 @@ git-tree-sha1 = "cf2c7de82431ca6f39250d2fc4aacd0daa1675c0" uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" version = "5.4.4+0" +[[deps.Xorg_libICE_jll]] +deps = ["Libdl", "Pkg"] +git-tree-sha1 = "e5becd4411063bdcac16be8b66fc2f9f6f1e8fe5" +uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" +version = "1.0.10+1" + +[[deps.Xorg_libSM_jll]] +deps = ["Libdl", "Pkg", "Xorg_libICE_jll"] +git-tree-sha1 = "4a9d9e4c180e1e8119b5ffc224a7b59d3a7f7e18" +uuid = "c834827a-8449-5923-a945-d239c165b7dd" +version = "1.2.3+0" + [[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" @@ -2406,6 +2448,12 @@ git-tree-sha1 = "730eeca102434283c50ccf7d1ecdadf521a765a4" uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" version = "1.1.2+0" +[[deps.Xorg_xcb_util_cursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] +git-tree-sha1 = "04341cb870f29dcd5e39055f895c39d016e18ccd" +uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" +version = "0.1.4+0" + [[deps.Xorg_xcb_util_image_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97" @@ -2477,6 +2525,12 @@ git-tree-sha1 = "977aed5d006b840e2e40c0b48984f7463109046d" uuid = "700de1a5-db45-46bc-99cf-38207098b444" version = "0.2.3" +[[deps.eudev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gperf_jll"] +git-tree-sha1 = "431b678a28ebb559d224c0b6b6d01afce87c51ba" +uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" +version = "3.2.9+0" + [[deps.fzf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "868e669ccb12ba16eaf50cb2957ee2ff61261c56" @@ -2489,6 +2543,12 @@ git-tree-sha1 = "d4cf3bb87fa0669f569e51f6f06cd083771bab65" uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" version = "4.10.2+1" +[[deps.gperf_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3516a5630f741c9eecb3720b1ec9d8edc3ecc033" +uuid = "1a1c6b14-54f6-533d-8383-74cd7377aa70" +version = "3.1.1+0" + [[deps.isoband_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" @@ -2512,12 +2572,24 @@ deps = ["Artifacts", "Libdl"] uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" version = "5.8.0+0" +[[deps.libevdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "141fe65dc3efabb0b1d5ba74e91f6ad26f84cc22" +uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" +version = "1.11.0+0" + [[deps.libfdk_aac_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" version = "2.0.2+0" +[[deps.libinput_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "eudev_jll", "libevdev_jll", "mtdev_jll"] +git-tree-sha1 = "ad50e5b90f222cfe78aa3d5183a20a12de1322ce" +uuid = "36db933b-70db-51c0-b978-0f229ee0e533" +version = "1.18.0+0" + [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" @@ -2536,6 +2608,12 @@ git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" version = "1.3.7+1" +[[deps.mtdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" +uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" +version = "1.1.6+0" + [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" @@ -2560,6 +2638,6 @@ version = "3.5.0+0" [[deps.xkbcommon_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] -git-tree-sha1 = "9ebfc140cc56e8c2156a15ceac2f0302e327ac0a" +git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37" uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" -version = "1.4.1+0" +version = "1.4.1+1" From 4df280ea4c8477484eebc3003b7ad2b2221f3d4d Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 7 Oct 2023 17:16:47 +0200 Subject: [PATCH 028/172] Instantiate new Manifest --- docs/Manifest.toml | 137 +-------------------------------------------- 1 file changed, 3 insertions(+), 134 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 36b5ba92b4..476cdcfa74 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -92,10 +92,8 @@ version = "0.1.29" [[deps.ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra"] git-tree-sha1 = "9a731850434825d183af39c6e6cd0a1c32dd7e20" -git-tree-sha1 = "9a731850434825d183af39c6e6cd0a1c32dd7e20" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" version = "1.4.2" -version = "1.4.2" weakdeps = ["SparseArrays"] [deps.ArrayLayouts.extensions] @@ -242,10 +240,8 @@ version = "0.4.0" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129" -git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" version = "3.24.0" -version = "3.24.0" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] @@ -284,10 +280,8 @@ version = "0.3.0" [[deps.Compat]] deps = ["UUIDs"] git-tree-sha1 = "8a62af3e248a8c4bad6b32cbbe663ae02275e32c" -git-tree-sha1 = "8a62af3e248a8c4bad6b32cbbe663ae02275e32c" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" version = "4.10.0" -version = "4.10.0" weakdeps = ["Dates", "LinearAlgebra"] [deps.Compat.extensions] @@ -366,16 +360,12 @@ version = "1.9.1" [[deps.DiffEqBase]] deps = ["ArrayInterface", "ChainRulesCore", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces", "ZygoteRules"] git-tree-sha1 = "766ab4574433d22ff75ab28e9081114e73cef5d5" -deps = ["ArrayInterface", "ChainRulesCore", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces", "ZygoteRules"] -git-tree-sha1 = "766ab4574433d22ff75ab28e9081114e73cef5d5" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.132.0" version = "6.132.0" [deps.DiffEqBase.extensions] DiffEqBaseDistributionsExt = "Distributions" DiffEqBaseEnzymeExt = "Enzyme" - DiffEqBaseEnzymeExt = "Enzyme" DiffEqBaseGeneralizedGeneratedExt = "GeneralizedGenerated" DiffEqBaseMPIExt = "MPI" DiffEqBaseMeasurementsExt = "Measurements" @@ -388,7 +378,6 @@ version = "6.132.0" [deps.DiffEqBase.weakdeps] Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" @@ -413,16 +402,12 @@ version = "1.15.1" [[deps.Distances]] deps = ["LinearAlgebra", "Statistics", "StatsAPI"] git-tree-sha1 = "5225c965635d8c21168e32a12954675e7bea1151" -git-tree-sha1 = "5225c965635d8c21168e32a12954675e7bea1151" uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" version = "0.10.10" -weakdeps = ["ChainRulesCore", "SparseArrays"] -version = "0.10.10" weakdeps = ["ChainRulesCore", "SparseArrays"] [deps.Distances.extensions] DistancesChainRulesCoreExt = "ChainRulesCore" - DistancesChainRulesCoreExt = "ChainRulesCore" DistancesSparseArraysExt = "SparseArrays" [[deps.Distributed]] @@ -452,18 +437,14 @@ version = "0.9.3" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "Dates", "DocStringExtensions", "Downloads", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "Test", "Unicode"] git-tree-sha1 = "f667b805e90d643aeb1ca70189827f991a7cc115" -git-tree-sha1 = "f667b805e90d643aeb1ca70189827f991a7cc115" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" version = "1.1.0" -version = "1.1.0" [[deps.DocumenterCitations]] deps = ["AbstractTrees", "Bibliography", "Documenter", "Markdown", "MarkdownAST", "OrderedCollections", "Unicode"] git-tree-sha1 = "0c5c141a66807796d580ef4fe592647132832f39" -git-tree-sha1 = "0c5c141a66807796d580ef4fe592647132832f39" uuid = "daee34ce-89f3-4625-b898-19384cb65244" version = "1.2.1" -version = "1.2.1" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] @@ -592,8 +573,8 @@ uuid = "fa42c844-2597-5d31-933b-ebd51ab2693f" version = "0.3.1" [[deps.Ferrite]] -deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] -path = ".." +deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "Tensors", "WriteVTK"] +git-tree-sha1 = "a31b9d4dd58e00686e5da175dab11667af3108b6" uuid = "c061ca5d-56c9-439f-9c0e-210fe06d3992" version = "0.3.14" @@ -614,10 +595,8 @@ version = "1.0.1" [[deps.FerriteMeshParser]] deps = ["Ferrite"] git-tree-sha1 = "8b948577bc4066e9c8693438fd511309c7383761" -git-tree-sha1 = "8b948577bc4066e9c8693438fd511309c7383761" uuid = "0f8c756f-80dd-4a75-85c6-b0a5ab9d4620" version = "0.1.7" -version = "0.1.7" [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] @@ -748,18 +727,14 @@ version = "0.1.5" [[deps.GR]] deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "UUIDs", "p7zip_jll"] git-tree-sha1 = "27442171f28c952804dede8ff72828a96f2bfc1f" -git-tree-sha1 = "27442171f28c952804dede8ff72828a96f2bfc1f" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" version = "0.72.10" -version = "0.72.10" [[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 = "025d171a2847f616becc0f84c8dc62fe18f0f6dd" -git-tree-sha1 = "025d171a2847f616becc0f84c8dc62fe18f0f6dd" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" version = "0.72.10+0" -version = "0.72.10+0" [[deps.GenericSchur]] deps = ["LinearAlgebra", "Printf"] @@ -812,7 +787,6 @@ version = "1.3.14+0" [[deps.Graphs]] deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] git-tree-sha1 = "899050ace26649433ef1af25bc17a815b3db52b7" -git-tree-sha1 = "899050ace26649433ef1af25bc17a815b3db52b7" uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" version = "1.9.0" @@ -836,10 +810,8 @@ version = "1.12.2+2" [[deps.HTTP]] deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] git-tree-sha1 = "5eab648309e2e060198b45820af1a37182de3cce" -git-tree-sha1 = "5eab648309e2e060198b45820af1a37182de3cce" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" version = "1.10.0" -version = "1.10.0" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] @@ -1210,36 +1182,27 @@ version = "5.0.0+0" [[deps.LinearSolve]] deps = ["ArrayInterface", "ConcreteStructs", "DocStringExtensions", "EnumX", "EnzymeCore", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "Libdl", "LinearAlgebra", "MKL_jll", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "SuiteSparse", "UnPack"] git-tree-sha1 = "435ab14ca589757a0feae6e3e347bc37addda42d" -deps = ["ArrayInterface", "ConcreteStructs", "DocStringExtensions", "EnumX", "EnzymeCore", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "Libdl", "LinearAlgebra", "MKL_jll", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "SuiteSparse", "UnPack"] -git-tree-sha1 = "435ab14ca589757a0feae6e3e347bc37addda42d" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "2.9.2" version = "2.9.2" [deps.LinearSolve.extensions] LinearSolveBlockDiagonalsExt = "BlockDiagonals" - LinearSolveBlockDiagonalsExt = "BlockDiagonals" LinearSolveCUDAExt = "CUDA" LinearSolveEnzymeExt = "Enzyme" - LinearSolveEnzymeExt = "Enzyme" LinearSolveHYPREExt = "HYPRE" LinearSolveIterativeSolversExt = "IterativeSolvers" LinearSolveKernelAbstractionsExt = "KernelAbstractions" - LinearSolveKernelAbstractionsExt = "KernelAbstractions" LinearSolveKrylovKitExt = "KrylovKit" LinearSolveMetalExt = "Metal" LinearSolvePardisoExt = "Pardiso" [deps.LinearSolve.weakdeps] BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" - BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" - KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" Metal = "dde4c033-4e86-420c-a63e-0dd931031962" Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" @@ -1278,10 +1241,8 @@ uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" [[deps.LoggingExtras]] deps = ["Dates", "Logging"] git-tree-sha1 = "c1dd6d7978c12545b4179fb6153b9250c96b0075" -git-tree-sha1 = "c1dd6d7978c12545b4179fb6153b9250c96b0075" uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" version = "1.0.3" -version = "1.0.3" [[deps.LoopVectorization]] deps = ["ArrayInterface", "ArrayInterfaceCore", "CPUSummary", "CloseOpenIntervals", "DocStringExtensions", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "PrecompileTools", "SIMDTypes", "SLEEFPirates", "Static", "StaticArrayInterface", "ThreadingUtilities", "UnPack", "VectorizationBase"] @@ -1311,12 +1272,6 @@ git-tree-sha1 = "eb006abbd7041c28e0d16260e50a24f8f9104913" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" version = "2023.2.0+0" -[[deps.MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"] -git-tree-sha1 = "eb006abbd7041c28e0d16260e50a24f8f9104913" -uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2023.2.0+0" - [[deps.MMG_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" @@ -1358,7 +1313,6 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" [[deps.MarkdownAST]] deps = ["AbstractTrees", "Markdown"] git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" -git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" version = "0.1.2" @@ -1460,11 +1414,8 @@ version = "1.2.0" [[deps.NonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "EnumX", "FiniteDiff", "ForwardDiff", "LineSearches", "LinearAlgebra", "LinearSolve", "PrecompileTools", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "UnPack"] git-tree-sha1 = "445a7ba86794e1f8ee9da3b3b7becf284e2625fd" -deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "EnumX", "FiniteDiff", "ForwardDiff", "LineSearches", "LinearAlgebra", "LinearSolve", "PrecompileTools", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "445a7ba86794e1f8ee9da3b3b7becf284e2625fd" uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" version = "2.1.0" -version = "2.1.0" [[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"] @@ -1549,10 +1500,8 @@ version = "1.6.2" [[deps.OrdinaryDiffEq]] deps = ["ADTypes", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "IfElse", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "LoopVectorization", "MacroTools", "MuladdMacro", "NLsolve", "NonlinearSolve", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLNLSolve", "SciMLOperators", "SimpleNonlinearSolve", "SimpleUnPack", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] git-tree-sha1 = "def999a7447854f0e9ca9fdda235e04a65916b76" -git-tree-sha1 = "def999a7447854f0e9ca9fdda235e04a65916b76" uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" version = "6.58.0" -version = "6.58.0" [[deps.PCRE2_jll]] deps = ["Artifacts", "Libdl"] @@ -1573,10 +1522,8 @@ version = "0.4.0" [[deps.PackageExtensionCompat]] git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" -git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" version = "1.0.2" -version = "1.0.2" weakdeps = ["Requires", "TOML"] [[deps.Packing]] @@ -1672,10 +1619,8 @@ version = "1.39.0" [[deps.Polyester]] deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Requires", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] git-tree-sha1 = "c7dc9720390fcc296bf757b3f833f9e41c68a086" -git-tree-sha1 = "c7dc9720390fcc296bf757b3f833f9e41c68a086" uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" version = "0.7.7" -version = "0.7.7" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] @@ -1733,7 +1678,6 @@ version = "1.2.0" [[deps.Preferences]] deps = ["TOML"] git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" -git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.4.1" @@ -1762,8 +1706,6 @@ version = "1.0.0" [[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"] git-tree-sha1 = "7c29f0e8c575428bd84dc3c72ece5178caa67336" -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"] -git-tree-sha1 = "7c29f0e8c575428bd84dc3c72ece5178caa67336" uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" version = "6.5.2+2" @@ -1914,9 +1856,7 @@ version = "0.6.39" [[deps.SciMLBase]] deps = ["ADTypes", "ArrayInterface", "ChainRulesCore", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FillArrays", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables", "TruncatedStacktraces", "ZygoteRules"] git-tree-sha1 = "1e09c5c89f5502eb4e3657730b0fb6817a1bca8d" -git-tree-sha1 = "1e09c5c89f5502eb4e3657730b0fb6817a1bca8d" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.3.0" version = "2.3.0" [deps.SciMLBase.extensions] @@ -1924,27 +1864,18 @@ version = "2.3.0" SciMLBasePythonCallExt = "PythonCall" SciMLBaseRCallExt = "RCall" SciMLBaseZygoteExt = "Zygote" - SciMLBasePyCallExt = "PyCall" - SciMLBasePythonCallExt = "PythonCall" - SciMLBaseRCallExt = "RCall" - SciMLBaseZygoteExt = "Zygote" [deps.SciMLBase.weakdeps] PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" RCall = "6f49c342-dc21-5d91-9882-a32aef131414" - PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" - PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" - RCall = "6f49c342-dc21-5d91-9882-a32aef131414" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SciMLNLSolve]] deps = ["DiffEqBase", "LineSearches", "NLsolve", "Reexport", "SciMLBase"] git-tree-sha1 = "765b788339abd7d983618c09cfc0192e2b6b15fd" -git-tree-sha1 = "765b788339abd7d983618c09cfc0192e2b6b15fd" uuid = "e9a6253c-8580-4d32-9898-8661bb511710" version = "0.1.9" -version = "0.1.9" [[deps.SciMLOperators]] deps = ["ArrayInterface", "DocStringExtensions", "Lazy", "LinearAlgebra", "Setfield", "SparseArrays", "StaticArraysCore", "Tricks"] @@ -2008,9 +1939,7 @@ version = "0.8.4" [[deps.SimpleNonlinearSolve]] deps = ["ArrayInterface", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "PackageExtensionCompat", "PrecompileTools", "Reexport", "SciMLBase", "StaticArraysCore"] git-tree-sha1 = "4d53b83af904049c493daaf2a225bcae994a3c59" -git-tree-sha1 = "4d53b83af904049c493daaf2a225bcae994a3c59" uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "0.1.20" version = "0.1.20" [deps.SimpleNonlinearSolve.extensions] @@ -2076,10 +2005,7 @@ uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[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 = "0a4538040f6eeae9016043104056dc6c13e1d8c1" -deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Random", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "Tricks", "UnPack", "VertexSafeGraphs"] -git-tree-sha1 = "0a4538040f6eeae9016043104056dc6c13e1d8c1" uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "2.7.0" version = "2.7.0" [deps.SparseDiffTools.extensions] @@ -2140,10 +2066,8 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "Random", "StaticArraysCore"] git-tree-sha1 = "0adf069a2a490c47273727e029371b31d44b72b2" -git-tree-sha1 = "0adf069a2a490c47273727e029371b31d44b72b2" uuid = "90137ffa-7385-5640-81b9-e52037218182" version = "1.6.5" -version = "1.6.5" weakdeps = ["Statistics"] [deps.StaticArrays.extensions] @@ -2168,7 +2092,6 @@ version = "1.7.0" [[deps.StatsBase]] deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] git-tree-sha1 = "1d77abd07f617c4868c33d4f5b9e1dbb2643c9cf" -git-tree-sha1 = "1d77abd07f617c4868c33d4f5b9e1dbb2643c9cf" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" version = "0.34.2" @@ -2239,11 +2162,8 @@ version = "1.0.1" [[deps.Tables]] deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] git-tree-sha1 = "a1f34829d5ac0ef499f6d84428bd6b4c71f02ead" -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "a1f34829d5ac0ef499f6d84428bd6b4c71f02ead" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" version = "1.11.0" -version = "1.11.0" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -2258,9 +2178,7 @@ version = "0.1.1" [[deps.Tensors]] deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] -git-tree-sha1 = "09650779b3c5124b8742986904d6e69f854be0bd" -repo-rev = "kam/3rd_order" -repo-url = "https://github.com/KnutAM/Tensors.jl.git" +git-tree-sha1 = "bcbb366323add300742c9e4a5447e584640aeff2" uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" version = "1.15.0" @@ -2393,11 +2311,8 @@ version = "1.3.243+0" [[deps.Wayland_jll]] deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703" -deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" version = "1.21.0+1" -version = "1.21.0+1" [[deps.Wayland_protocols_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -2420,10 +2335,8 @@ version = "1.18.0" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] git-tree-sha1 = "24b81b59bd35b3c42ab84fa589086e19be919916" -git-tree-sha1 = "24b81b59bd35b3c42ab84fa589086e19be919916" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" version = "2.11.5+0" -version = "2.11.5+0" [[deps.XSLT_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] @@ -2449,18 +2362,6 @@ git-tree-sha1 = "4a9d9e4c180e1e8119b5ffc224a7b59d3a7f7e18" uuid = "c834827a-8449-5923-a945-d239c165b7dd" version = "1.2.3+0" -[[deps.Xorg_libICE_jll]] -deps = ["Libdl", "Pkg"] -git-tree-sha1 = "e5becd4411063bdcac16be8b66fc2f9f6f1e8fe5" -uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" -version = "1.0.10+1" - -[[deps.Xorg_libSM_jll]] -deps = ["Libdl", "Pkg", "Xorg_libICE_jll"] -git-tree-sha1 = "4a9d9e4c180e1e8119b5ffc224a7b59d3a7f7e18" -uuid = "c834827a-8449-5923-a945-d239c165b7dd" -version = "1.2.3+0" - [[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" @@ -2551,12 +2452,6 @@ git-tree-sha1 = "04341cb870f29dcd5e39055f895c39d016e18ccd" uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" version = "0.1.4+0" -[[deps.Xorg_xcb_util_cursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] -git-tree-sha1 = "04341cb870f29dcd5e39055f895c39d016e18ccd" -uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" -version = "0.1.4+0" - [[deps.Xorg_xcb_util_image_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97" @@ -2634,12 +2529,6 @@ git-tree-sha1 = "431b678a28ebb559d224c0b6b6d01afce87c51ba" uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" version = "3.2.9+0" -[[deps.eudev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gperf_jll"] -git-tree-sha1 = "431b678a28ebb559d224c0b6b6d01afce87c51ba" -uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" -version = "3.2.9+0" - [[deps.fzf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "868e669ccb12ba16eaf50cb2957ee2ff61261c56" @@ -2687,12 +2576,6 @@ git-tree-sha1 = "141fe65dc3efabb0b1d5ba74e91f6ad26f84cc22" uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" version = "1.11.0+0" -[[deps.libevdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "141fe65dc3efabb0b1d5ba74e91f6ad26f84cc22" -uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" -version = "1.11.0+0" - [[deps.libfdk_aac_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" @@ -2705,12 +2588,6 @@ git-tree-sha1 = "ad50e5b90f222cfe78aa3d5183a20a12de1322ce" uuid = "36db933b-70db-51c0-b978-0f229ee0e533" version = "1.18.0+0" -[[deps.libinput_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "eudev_jll", "libevdev_jll", "mtdev_jll"] -git-tree-sha1 = "ad50e5b90f222cfe78aa3d5183a20a12de1322ce" -uuid = "36db933b-70db-51c0-b978-0f229ee0e533" -version = "1.18.0+0" - [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" @@ -2735,12 +2612,6 @@ git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" version = "1.1.6+0" -[[deps.mtdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" -uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" -version = "1.1.6+0" - [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" @@ -2766,7 +2637,5 @@ version = "3.5.0+0" [[deps.xkbcommon_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37" -git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37" uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" version = "1.4.1+1" -version = "1.4.1+1" From 2557b3e3974fa7083fe0b245d5b84069517398f6 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 7 Oct 2023 19:52:05 +0200 Subject: [PATCH 029/172] Correct Manifest update oopsi --- docs/Manifest.toml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 476cdcfa74..52fe3bffa6 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -573,8 +573,8 @@ uuid = "fa42c844-2597-5d31-933b-ebd51ab2693f" version = "0.3.1" [[deps.Ferrite]] -deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "Tensors", "WriteVTK"] -git-tree-sha1 = "a31b9d4dd58e00686e5da175dab11667af3108b6" +deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] +path = ".." uuid = "c061ca5d-56c9-439f-9c0e-210fe06d3992" version = "0.3.14" @@ -2178,7 +2178,9 @@ version = "0.1.1" [[deps.Tensors]] deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] -git-tree-sha1 = "bcbb366323add300742c9e4a5447e584640aeff2" +git-tree-sha1 = "09650779b3c5124b8742986904d6e69f854be0bd" +repo-rev = "kam/3rd_order" +repo-url = "https://github.com/KnutAM/Tensors.jl.git" uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" version = "1.15.0" From 27a22ccdcba2c4cff46df77c7d16f55c5cc489b6 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 7 Oct 2023 21:02:47 +0200 Subject: [PATCH 030/172] Update examples to calculate flux --- .../literate-tutorials/heat_equation_rt.jl | 62 ++++++++++++++++++- .../heat_equation_triangle.jl | 30 ++++++++- 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl index 8d000047bf..6070db81f7 100644 --- a/docs/src/literate-tutorials/heat_equation_rt.jl +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -58,7 +58,7 @@ using Ferrite, SparseArrays # We start by generating a simple grid with 20x20 quadrilateral elements # using `generate_grid`. The generator defaults to the unit square, # so we don't need to specify the corners of the domain. -grid = generate_grid(Triangle, (100, 100)); +grid = generate_grid(Triangle, (20, 20)); # ### Trial and test functions # A `CellValues` facilitates the process of evaluating values and gradients of @@ -154,7 +154,6 @@ function assemble_element!(Ke::Matrix, fe::Vector, cv::NamedTuple, dr::NamedTupl cvq = cv[:q] dru = dr[:u] drq = dr[:q] - n_basefuncs = getnbasefunctions(cvu) ## Loop over quadrature points for q_point in 1:getnquadpoints(cvu) ## Get the quadrature weight @@ -252,12 +251,71 @@ u = K \ f; # 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/). u_nodes = evaluate_at_grid_nodes(dh, u, :u) +∂Ω_cells = zeros(Int, getncells(grid)) +for (cellnr, facenr) in ∂Ω + ∂Ω_cells[cellnr] = 1 +end vtk_grid("heat_equation_rt", dh) do vtk vtk_point_data(vtk, u_nodes, "u") + vtk_cell_data(vtk, ∂Ω_cells, "dO") end @show norm(u_nodes)/length(u_nodes) +# ## Postprocess the total flux +function calculate_flux(dh, dΩ, ip, a) + qr = FaceQuadratureRule{RefTriangle}(4) + fv = FaceValues(qr, ip, Lagrange{RefTriangle,1}()) + grid = dh.grid + dofrange = dof_range(dh, :q) + flux = 0.0 + dofs = celldofs(dh, 1) + ae = zeros(length(dofs)) + x = getcoordinates(grid, 1) + for (cellnr, facenr) in dΩ + getcoordinates!(x, grid, cellnr) + cell = getcells(grid, cellnr) + celldofs!(dofs, dh, cellnr) + map!(i->a[i], ae, dofs) + reinit!(fv, x, facenr, cell) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + q = function_value(fv, q_point, ae, dofrange) + flux += (q ⋅ n)*dΓ + end + end + return flux +end + +function calculate_flux_lag(dh, dΩ, ip, a) + qr = FaceQuadratureRule{RefTriangle}(4) + fv = FaceValues(qr, ip, Lagrange{RefTriangle,1}()) + grid = dh.grid + dofrange = dof_range(dh, :u) + flux = 0.0 + dofs = celldofs(dh, 1) + ae = zeros(length(dofs)) + x = getcoordinates(grid, 1) + for (cellnr, facenr) in dΩ + getcoordinates!(x, grid, cellnr) + cell = getcells(grid, cellnr) + celldofs!(dofs, dh, cellnr) + map!(i->a[i], ae, dofs) + reinit!(fv, x, facenr, cell) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + q = function_gradient(fv, q_point, ae, dofrange) + flux -= (q ⋅ n)*dΓ + end + end + return flux +end + +flux = calculate_flux(dh, ∂Ω, ipq, u) +flux_lag = calculate_flux_lag(dh, ∂Ω, ipu, u) +@show flux, flux_lag #md # ## [Plain program](@id heat_equation-plain-program) #md # #md # Here follows a version of the program without any comments. diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl index d5ea45e4df..e2ec7c3ed0 100644 --- a/docs/src/literate-tutorials/heat_equation_triangle.jl +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -45,7 +45,7 @@ using Ferrite, SparseArrays # We start by generating a simple grid with 20x20 quadrilateral elements # using `generate_grid`. The generator defaults to the unit square, # so we don't need to specify the corners of the domain. -grid = generate_grid(Triangle, (100, 100)); +grid = generate_grid(Triangle, (20, 20)); # ### Trial and test functions # A `CellValues` facilitates the process of evaluating values and gradients of @@ -219,6 +219,34 @@ end @show norm(u)/length(u) +function calculate_flux_lag(dh, dΩ, ip, a) + qr = FaceQuadratureRule{RefTriangle}(2) + fv = FaceValues(qr, ip, Lagrange{RefTriangle,1}()) + grid = dh.grid + dofrange = dof_range(dh, :u) + flux = 0.0 + dofs = celldofs(dh, 1) + ae = zeros(length(dofs)) + x = getcoordinates(grid, 1) + for (cellnr, facenr) in dΩ + getcoordinates!(x, grid, cellnr) + cell = getcells(grid, cellnr) + celldofs!(dofs, dh, cellnr) + map!(i->a[i], ae, dofs) + reinit!(fv, x, facenr, cell) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + q = function_gradient(fv, q_point, ae, dofrange) + flux -= (q ⋅ n)*dΓ + end + end + return flux +end + +flux = calculate_flux_lag(dh, ∂Ω, ip, u) +@show flux + #md # ## [Plain program](@id heat_equation-plain-program) #md # #md # Here follows a version of the program without any comments. From 3facc077eb1032f8e3f0d506247d5a9562dc7a74 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 7 Oct 2023 23:07:00 +0200 Subject: [PATCH 031/172] Change parameterization and fix test tolerances --- benchmark/helper.jl | 6 ++--- docs/src/literate-howto/postprocessing.jl | 2 +- .../incompressible_elasticity.jl | 6 ++--- src/Dofs/DofHandler.jl | 2 +- src/FEValues/CellValues.jl | 17 +++++++----- src/FEValues/FaceValues.jl | 22 ++++++++------- src/FEValues/FunctionValues.jl | 5 ++++ src/FEValues/GeometryValues.jl | 1 + src/FEValues/common_values.jl | 27 ++++++++++++++++--- src/deprecations.jl | 3 ++- test/InterpolationTestUtils.jl | 2 +- test/runtests.jl | 2 +- 12 files changed, 65 insertions(+), 30 deletions(-) diff --git a/benchmark/helper.jl b/benchmark/helper.jl index 5a8b44c4b2..faba95f1c4 100644 --- a/benchmark/helper.jl +++ b/benchmark/helper.jl @@ -66,17 +66,17 @@ function _generalized_ritz_galerkin_assemble_local_matrix(grid::Ferrite.Abstract end # Minimal Petrov-Galerkin type local assembly loop. We assume that both function spaces share the same integration rule. Test is applied from the left. -function _generalized_petrov_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, cellvalues_shape::CellValues{<: Ferrite.InterpolationByDim{dim}}, f_shape, cellvalues_test::CellValues{<: Ferrite.InterpolationByDim{dim}}, f_test, op) where {dim} +function _generalized_petrov_galerkin_assemble_local_matrix(grid::Ferrite.AbstractGrid, cellvalues_shape::CellValues, f_shape, cellvalues_test::CellValues, f_test, op) n_basefuncs_shape = getnbasefunctions(cellvalues_shape) n_basefuncs_test = getnbasefunctions(cellvalues_test) Ke = zeros(n_basefuncs_test, n_basefuncs_shape) #implicit assumption: Same geometry! - X_shape = zeros(Vec{dim,Float64}, Ferrite.getngeobasefunctions(cellvalues_shape)) + X_shape = zeros(get_coordinate_type(grid), Ferrite.getngeobasefunctions(cellvalues_shape)) getcoordinates!(X_shape, grid, 1) reinit!(cellvalues_shape, X_shape) - X_test = zeros(Vec{dim,Float64}, Ferrite.getngeobasefunctions(cellvalues_test)) + X_test = zeros(get_coordinate_type(grid), Ferrite.getngeobasefunctions(cellvalues_test)) getcoordinates!(X_test, grid, 1) reinit!(cellvalues_test, X_test) diff --git a/docs/src/literate-howto/postprocessing.jl b/docs/src/literate-howto/postprocessing.jl index 57c524a47f..79586f0640 100644 --- a/docs/src/literate-howto/postprocessing.jl +++ b/docs/src/literate-howto/postprocessing.jl @@ -46,7 +46,7 @@ include("../tutorials/heat_equation.jl"); # Next we define a function that computes the heat flux for each integration point in the domain. # Fourier's law is adopted, where the conductivity tensor is assumed to be isotropic with unit # conductivity ``\lambda = 1 ⇒ q = - \nabla u``, where ``u`` is the temperature. -function compute_heat_fluxes(cellvalues::CellValues{<:ScalarInterpolation}, dh::DofHandler, a::AbstractVector{T}) where T +function compute_heat_fluxes(cellvalues::CellValues, dh::DofHandler, a::AbstractVector{T}) where T n = getnbasefunctions(cellvalues) cell_dofs = zeros(Int, n) diff --git a/docs/src/literate-tutorials/incompressible_elasticity.jl b/docs/src/literate-tutorials/incompressible_elasticity.jl index 53154f6c52..e69b8a6577 100644 --- a/docs/src/literate-tutorials/incompressible_elasticity.jl +++ b/docs/src/literate-tutorials/incompressible_elasticity.jl @@ -85,9 +85,9 @@ end # use a `PseudoBlockArray` from `BlockArrays.jl`. function doassemble( - cellvalues_u::CellValues{<:VectorInterpolation}, - cellvalues_p::CellValues{<:ScalarInterpolation}, - facevalues_u::FaceValues{<:VectorInterpolation}, + cellvalues_u::CellValues, + cellvalues_p::CellValues, + facevalues_u::FaceValues, K::SparseMatrixCSC, grid::Grid, dh::DofHandler, mp::LinearElasticity ) f = zeros(ndofs(dh)) diff --git a/src/Dofs/DofHandler.jl b/src/Dofs/DofHandler.jl index a653c8a563..ec17ef13e1 100644 --- a/src/Dofs/DofHandler.jl +++ b/src/Dofs/DofHandler.jl @@ -915,7 +915,7 @@ function _evaluate_at_grid_nodes!(data::Union{Vector,Matrix}, sdh::SubDofHandler u::Vector{T}, cv::CellValues, drange::UnitRange, ::Type{RT}) where {T, RT} ue = zeros(T, length(drange)) # TODO: Remove this hack when embedding works... - if RT <: Vec && cv isa CellValues{<:ScalarInterpolation} + if RT <: Vec && get_function_interpolation(cv) isa ScalarInterpolation uer = reinterpret(RT, ue) else uer = ue diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 2c09019414..929a13ca6a 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -6,18 +6,18 @@ function default_geometric_interpolation(::Interpolation{shape}) where {dim, sha return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) end -struct CellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP, d2Mdξ2_t} <: AbstractCellValues - geo_values::GeometryValues{dMdξ_t, GIP, T, d2Mdξ2_t} - detJdV::Vector{T} - fun_values::FunctionValues{IP, N_t, dNdx_t, dNdξ_t} - qr::QR +struct CellValues{FV, GV, QR, detT<:AbstractVector} <: AbstractCellValues + fun_values::FV # FunctionValues + geo_values::GV # GeometryValues + qr::QR # QuadratureRule + detJdV::detT # AbstractVector{<:Number} end function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T mapping_type = get_mapping_type(ip_fun) geo_values = GeometryValues(T, ip_geo.ip, qr, RequiresHessian(requires_hessian(mapping_type))) fun_values = FunctionValues(T, ip_fun, qr, ip_geo) detJdV = fill(T(NaN), length(getweights(qr))) - return CellValues(geo_values, detJdV, fun_values, qr) + return CellValues(fun_values, geo_values, qr, detJdV) end CellValues(qr::QuadratureRule, ip::Interpolation, args...) = CellValues(Float64, qr, ip, args...) @@ -35,6 +35,11 @@ getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] # Accessors for function values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) +get_function_interpolation(cv::CellValues) = get_function_interpolation(cv.fun_values) +get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_values) +shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) +shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) + for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient) eval(quote @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 9d8ebec0c3..0316ef729d 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -1,9 +1,9 @@ -struct FaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP, d2Mdξ2_t} <: AbstractFaceValues - geo_values::Vector{GeometryValues{dMdξ_t, GIP, T, d2Mdξ2_t}} - detJdV::Vector{T} - normals::Vector{Normal_t} - fun_values::Vector{FunctionValues{IP, N_t, dNdx_t, dNdξ_t}} - qr::QR # FaceQuadratureRule +struct FaceValues{FV, GV, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GV<:AbstractVector{GV}} <: AbstractFaceValues + fun_values::V_FV # AbstractVector{FunctionValues} + geo_values::V_GV # AbstractVector{GeometryValues} + qr::QR # FaceQuadratureRule + detJdV::detT # AbstractVector{<:Number} + normals::nT # AbstractVector{<:Vec} current_face::ScalarWrapper{Int} end @@ -13,18 +13,22 @@ function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, i max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) detJdV = fill(T(NaN), max_nquadpoints) normals = fill(zero(Vec{sdim,T})*T(NaN), max_nquadpoints) - return FaceValues(geo_values, detJdV, normals, fun_values, fqr, ScalarWrapper(1)) + return FaceValues(fun_values, geo_values, fqr, detJdV, normals, ScalarWrapper(1)) end FaceValues(qr::FaceQuadratureRule, ip::Interpolation, args...) = FaceValues(Float64, qr, ip, args...) function FaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation, ip_geo::ScalarInterpolation) where T return FaceValues(T, qr, ip, VectorizedInterpolation(ip_geo)) end - getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_values(fv)) getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) getnquadpoints(fv::FaceValues) = getnquadpoints(fv.qr, getcurrentface(fv)) -getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] +getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] + +shape_value_type(fv::FaceValues) = shape_value_type(get_fun_values(fv)) +shape_gradient_type(fv::FaceValues) = shape_gradient_type(get_fun_values(fv)) +get_function_interpolation(fv::FaceValues) = get_function_interpolation(get_fun_values(fv)) +get_geometric_interpolation(fv::FaceValues) = get_geometric_interpolation(get_geo_values(fv)) get_geo_values(fv::FaceValues) = @inbounds fv.geo_values[getcurrentface(fv)] for op = (:getngeobasefunctions, :geometric_value) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 2290e310d4..47fa2702b5 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -64,6 +64,11 @@ getnbasefunctions(funvals::FunctionValues) = size(funvals.N_x, 1) @propagate_inbounds shape_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.dNdx[base_func, q_point] @propagate_inbounds shape_symmetric_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = symmetric(shape_gradient(funvals, q_point, base_func)) +get_function_interpolation(funvals::FunctionValues) = funvals.ip + +shape_value_type(funvals::FunctionValues) = eltype(funvals.N_x) +shape_gradient_type(funvals::FunctionValues) = eltype(funvals.dNdx) + # Mapping types struct IdentityMapping end struct CovariantPiolaMapping end diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 71b73d45f3..65719d6cc1 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -51,6 +51,7 @@ RequiresHessian(::GeometryValues) = RequiresHessian(true) getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) @propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] +get_geometric_interpolation(geovals::GeometryValues) = geovals.ip @propagate_inbounds calculate_mapping(geovals::GeometryValues, args...) = calculate_mapping(RequiresHessian(geovals), geovals, args...) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 683cc36511..6c0a42f0ea 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -67,9 +67,16 @@ finite element cell or face as Return the value of shape function `base_function` evaluated in quadrature point `q_point`. """ +function shape_value end #@propagate_inbounds shape_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.N[base_func, q_point, bv.current_face[]] -#@propagate_inbounds geometric_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.M[base_func, q_point, bv.current_face[]] +""" + geometric_value(fe_v::AbstractValues, q_point, base_function::Int) + +Return the value of the geometric shape function `base_function` evaluated in +quadrature point `q_point`. +""" +function geometric_value end """ shape_gradient(fe_v::AbstractValues, q_point::Int, base_function::Int) @@ -77,7 +84,7 @@ quadrature point `q_point`. Return the gradient of shape function `base_function` evaluated in quadrature point `q_point`. """ -#@propagate_inbounds shape_gradient(bv::FaceValues, q_point::Int, base_func::Int) = bv.dNdx[base_func, q_point, bv.current_face[]] +function shape_gradient end """ shape_symmetric_gradient(fe_v::AbstractValues, q_point::Int, base_function::Int) @@ -132,7 +139,13 @@ end # TODO: Implement fallback or require this to be defined? # Alt: shape_value_type(cv) = typeof(shape_value(cv, qp=1, i=1)) -shape_value_type(::Union{CellValues{<:Any, N_t}, FaceValues{<:Any, N_t}}) where N_t = N_t +""" + shape_value_type(fe_v::AbstractValues) + +Return the type of `shape_value(fe_v, q_point, base_function)` +""" +function shape_value_type end + function_value_init(cv::AbstractValues, ::AbstractVector{T}) where {T} = zero(shape_value_type(cv)) * zero(T) """ @@ -177,7 +190,13 @@ end # TODO: Implement fallback or require this to be defined? # Alt: shape_gradient_type(cv) = typeof(shape_gradient(cv, qp=1, i=1)) -shape_gradient_type(::Union{CellValues{<:Any, <:Any, dNdx_t}, FaceValues{<:Any, <:Any, dNdx_t}}) where dNdx_t = dNdx_t +""" + shape_gradient_type(fe_v::AbstractValues) + +Return the type of `shape_gradient(fe_v, q_point, base_function)` +""" +function shape_gradient_type end + function function_gradient_init(cv::AbstractValues, ::AbstractVector{T}) where {T} return zero(shape_gradient_type(cv)) * zero(T) end diff --git a/src/deprecations.jl b/src/deprecations.jl index 9c8d832644..cb0c2f3cd8 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -154,8 +154,9 @@ for VT in ( end end +# TODO: Are these needed to be deprecated - harder? with the new parameterization # (Cell|Face)Values with vector dofs -const _VectorValues = Union{CellValues{<:VectorInterpolation}, FaceValues{<:VectorInterpolation}} +const _VectorValues = Union{CellValues{<:FV}, FaceValues{<:FV}} where {FV <: FunctionValues{<:VectorInterpolation}} @deprecate function_value(fe_v::_VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}}) where {dim,T} function_value(fe_v, q_point, reinterpret(T, u)) @deprecate function_gradient(fe_v::_VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}}) where {dim,T} function_gradient(fe_v, q_point, reinterpret(T, u)) @deprecate function_divergence(fe_v::_VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}}) where {dim,T} function_divergence(fe_v, q_point, reinterpret(T, u)) diff --git a/test/InterpolationTestUtils.jl b/test/InterpolationTestUtils.jl index 6cace40b44..9d98ceaa19 100644 --- a/test/InterpolationTestUtils.jl +++ b/test/InterpolationTestUtils.jl @@ -77,7 +77,7 @@ module InterpolationTestUtils d1 = map((v,n)->transformation_function(v,n), fun_vals, point_normal) d2 = map((v,n)->transformation_function(v,n), fun_vals2, point_normal) - @test d1 ≈ d2 + @test isapprox(d1, d2; rtol=1e-6) # Approximate points can contribute to the inexactness return true end diff --git a/test/runtests.jl b/test/runtests.jl index d11ed6d582..832afced7d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -19,7 +19,7 @@ end include("test_utils.jl") include("test_interpolations.jl") -include("test_cellvalues.jl") +include("test_cellvalues.jl") include("test_facevalues.jl") include("test_quadrules.jl") include("test_assemble.jl") From a6e450511e5fa1ebb4a692c54f8f6ea7bd3b6b73 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 11:07:33 +0200 Subject: [PATCH 032/172] Relax type-constraints in GeometryValues and FunctionValues --- src/FEValues/FunctionValues.jl | 13 +++---- src/FEValues/GeometryValues.jl | 63 +++++++++++++++++++--------------- test/test_interpolations.jl | 9 ++--- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 47fa2702b5..c9b3a671c4 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -23,11 +23,11 @@ typeof_dNdξ(::Type{T}, ::VInterpolationDims{dim,dim,dim}) where {T,dim} = Tenso typeof_dNdξ(::Type{T}, ::VInterpolationDims{rdim,<:Any,vdim}) where {T,rdim,vdim} = SMatrix{vdim,rdim,T} # If vdim=rdim!=sdim Tensor would be possible... struct FunctionValues{IP, N_t, dNdx_t, dNdξ_t} - N_x::Matrix{N_t} - N_ξ::Matrix{N_t} - dNdx::Matrix{dNdx_t} - dNdξ::Matrix{dNdξ_t} - ip::IP + ip::IP # ::Interpolation + N_x::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} + N_ξ::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} + dNdx::dNdx_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} + dNdξ::dNdξ_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} end function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T ip_dims = InterpolationDims(ip, ip_geo) @@ -45,7 +45,7 @@ function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo end dNdξ = zeros(dNdξ_t, n_shape, n_qpoints) dNdx = fill(zero(dNdx_t) * T(NaN), n_shape, n_qpoints) - fv = FunctionValues(N_x, N_ξ, dNdx, dNdξ, ip) + fv = FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) precompute_values!(fv, qr) # Precompute N and dNdξ return fv end @@ -83,6 +83,7 @@ requires_hessian(::IdentityMapping) = false requires_hessian(::ContravariantPiolaMapping) = true requires_hessian(::CovariantPiolaMapping) = true +# Support for embedded elements calculate_Jinv(J::Tensor{2}) = inv(J) calculate_Jinv(J::SMatrix) = pinv(J) diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 65719d6cc1..4ed3b0fc12 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -17,11 +17,11 @@ function RequiresHessian(ip_fun::Interpolation, ip_geo::Interpolation) RequiresHessian(requires_hessian(get_mapping_type(ip_fun))) end -struct GeometryValues{dMdξ_t, GIP, T, d2Mdξ2_t} - M::Matrix{T} # value of geometric shape function - dMdξ::Matrix{dMdξ_t} # gradient of geometric shape function in ref-domain - d2Mdξ2::Matrix{d2Mdξ2_t} # hessian of geometric shape function in ref-domain - ip::GIP # geometric interpolation +struct GeometryValues{IP, M_t, dMdξ_t, d2Mdξ2_t} + ip::IP # ::Interpolation Geometric interpolation + M::M_t # ::AbstractVector{<:Number} Values of geometric shape functions + dMdξ::dMdξ_t # ::AbstractVector{<:Vec} Gradients of geometric shape functions in ref-domain + d2Mdξ2::d2Mdξ2_t # ::AbstractVector{<:Tensor{2}} Hessians of geometric shape functions in ref-domain end function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, ::RequiresHessian{RH}) where {T,RH} n_shape = getnbasefunctions(ip) @@ -33,7 +33,7 @@ function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, HT = Tensor{2,getdim(ip),T} dM2dξ2 = zeros(HT, n_shape, n_qpoints) else - dM2dξ2 = Matrix{Nothing}(undef,0,0) + dM2dξ2 = nothing end for (qp, ξ) in pairs(getpoints(qr)) for i in 1:n_shape @@ -44,28 +44,49 @@ function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, end end end - return GeometryValues(M, dMdξ, dM2dξ2, ip) + return GeometryValues(ip, M, dMdξ, dM2dξ2) end -RequiresHessian(::GeometryValues{<:Any,<:Any,<:Any,Nothing}) = RequiresHessian(false) -RequiresHessian(::GeometryValues) = RequiresHessian(true) getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) @propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] get_geometric_interpolation(geovals::GeometryValues) = geovals.ip +RequiresHessian(geovals::GeometryValues) = RequiresHessian(geovals.d2Mdξ2 !== nothing) + +# Hot-fixes to support embedded elements before MixedTensors are available +# See https://github.com/Ferrite-FEM/Tensors.jl/pull/188 +@inline otimes_helper(x::Vec{dim}, dMdξ::Vec{dim}) where dim = x ⊗ dMdξ +@inline function otimes_helper(x::Vec{sdim}, dMdξ::Vec{rdim}) where {sdim, rdim} + SMatrix{sdim,rdim}((x[i]*dMdξ[j] for i in 1:sdim, j in 1:rdim)...) +end +# End of embedded hot-fixes + +# For creating initial value +function otimes_returntype(#=typeof(x)=#::Type{<:Vec{sdim,Tx}}, #=typeof(dMdξ)=#::Type{<:Vec{rdim,TM}}) where {sdim,rdim,Tx,TM} + return SMatrix{sdim,rdim,promote_type(Tx,TM)} +end +function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(dMdξ)=#::Type{<:Vec{dim,TM}}) where {dim, Tx, TM} + return Tensor{2,dim,promote_type(Tx,TM)} +end +function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(d2Mdξ2)=#::Type{<:Tensor{2,dim,TM}}) where {dim, Tx, TM} + return Tensor{3,dim,promote_type(Tx,TM)} +end + @propagate_inbounds calculate_mapping(geovals::GeometryValues, args...) = calculate_mapping(RequiresHessian(geovals), geovals, args...) -@inline function calculate_mapping(::RequiresHessian{false}, geo_values::GeometryValues{<:Vec{dim,T}}, q_point, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} - fecv_J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) +@inline function calculate_mapping(::RequiresHessian{false}, geo_values::GeometryValues, q_point, x) + #fecv_J = zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) + fecv_J = zero(otimes_returntype(eltype(x), eltype(geo_values.dMdξ))) @inbounds for j in 1:getngeobasefunctions(geo_values) - fecv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] + #fecv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] + fecv_J += otimes_helper(x[j], geo_values.dMdξ[j, q_point]) end return MappingValues(fecv_J, nothing) end -@inline function calculate_mapping(::RequiresHessian{true}, geo_values::GeometryValues{<:Vec{dim,T}}, q_point, x::AbstractVector{<:Vec{dim,T}}) where {dim,T} - J = zero(Tensor{2,dim,T}) # zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) - H = zero(Tensor{3,dim,T}) +@inline function calculate_mapping(::RequiresHessian{true}, geo_values::GeometryValues, q_point, x) + J = zero(otimes_returntype(eltype(x), eltype(geo_values.dMdξ))) + H = zero(otimes_returntype(eltype(x), eltype(geo_values.d2Mdξ2))) @inbounds for j in 1:getngeobasefunctions(geo_values) J += x[j] ⊗ geo_values.dMdξ[j, q_point] H += x[j] ⊗ geo_values.d2Mdξ2[j, q_point] @@ -106,15 +127,3 @@ where ||∂x/∂ξ||₂ is "detJ" and t is "the unit tangent". See e.g. https://scicomp.stackexchange.com/questions/41741/integration-of-d-1-dimensional-functions-on-finite-element-surfaces for simple explanation. """ embedding_det(J::Union{SMatrix{2, 1}, SMatrix{3, 1}}) = norm(J) - -@inline function calculate_mapping(::RequiresHessian{false}, geo_values::GeometryValues{<:Vec{rdim,T}}, q_point, x::AbstractVector{<:Vec{sdim,T}}) where {rdim,sdim,T} - n_geom_basefuncs = getngeobasefunctions(geo_values) - fecv_J = zero(MMatrix{sdim, rdim, T}) # TODO replace with MixedTensor (see https://github.com/Ferrite-FEM/Tensors.jl/pull/188) - for j in 1:n_geom_basefuncs - #fecv_J += x[j] ⊗ geo_values.dMdξ[j, i] # TODO via Tensors.jl - for k in 1:sdim, l in 1:rdim - fecv_J[k, l] += x[j][k] * geo_values.dMdξ[j, q_point][l] - end - end - return MappingValues(SMatrix(fecv_J), nothing) -end \ No newline at end of file diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index da462b27f9..5e7dccca4a 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -191,10 +191,11 @@ end dim = Ferrite.getdim(CT) p1, p2 = (rand(Vec{dim}), ones(Vec{dim})+rand(Vec{dim})) grid = generate_grid(CT, ntuple(_->nel, dim), p1, p2) - # Distort grid, important to properly test geometry mapping - # for 2nd order elements. Make sure distortion is less than - # a 10th of the element size. - transform_coordinates!(grid, x->(x + rand(x)/(10*nel))) + # Smoothly distort grid (to avoid spuriously badly deformed elements). + # A distorted grid is important to properly test the geometry mapping + # for 2nd order elements. + transfun(x) = typeof(x)(i->sinpi(x[mod(i, length(x))+1]+i/3))/10 + transform_coordinates!(grid, x->(x + transfun(x))) RefShape = Ferrite.getrefshape(getcells(grid, 1)) for order in (1, 2) for IPT in (Nedelec, RaviartThomas) From 85bf3f7564ea75082c3298b6ddaf4437844d36f7 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 11:09:45 +0200 Subject: [PATCH 033/172] Create extra titles for heat eq modifications --- docs/src/literate-tutorials/heat_equation_rt.jl | 15 +-------------- .../literate-tutorials/heat_equation_triangle.jl | 8 +------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl index 6070db81f7..8ff5f2d75c 100644 --- a/docs/src/literate-tutorials/heat_equation_rt.jl +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -1,17 +1,4 @@ -# # [Heat equation](@id tutorial-heat-equation) -# -# ![](heat_square.png) -# -# *Figure 1*: Temperature field on the unit square with an internal uniform heat source -# solved with homogeneous Dirichlet boundary conditions on the boundary. -# -#- -#md # !!! tip -#md # This example is also available as a Jupyter notebook: -#md # [`heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/examples/heat_equation.ipynb). -#- -# -# ## Introduction +# # [Heat equation (Mixed, RaviartThomas)](@id tutorial-heat-equation-rt) # # ## Strong form # ```math diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl index e2ec7c3ed0..fee7ac52b6 100644 --- a/docs/src/literate-tutorials/heat_equation_triangle.jl +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -1,16 +1,10 @@ -# # [Heat equation](@id tutorial-heat-equation) +# # [Heat equation (Triangle)](@id tutorial-heat-equation-triangle) # # ![](heat_square.png) # # *Figure 1*: Temperature field on the unit square with an internal uniform heat source # solved with homogeneous Dirichlet boundary conditions on the boundary. # -#- -#md # !!! tip -#md # This example is also available as a Jupyter notebook: -#md # [`heat_equation.ipynb`](@__NBVIEWER_ROOT_URL__/examples/heat_equation.ipynb). -#- -# # ## Introduction # # The heat equation is the "Hello, world!" equation of finite elements. From 06a097dbcdc5368aed6c0d95bb4fb47675bdb44d Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 11:26:53 +0200 Subject: [PATCH 034/172] Use RequiresHessian in CellValues, and comment out unused future functions --- src/FEValues/CellValues.jl | 3 +-- src/FEValues/GeometryValues.jl | 7 +++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 929a13ca6a..5e2a9c4986 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -13,8 +13,7 @@ struct CellValues{FV, GV, QR, detT<:AbstractVector} <: AbstractCellValues detJdV::detT # AbstractVector{<:Number} end function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T - mapping_type = get_mapping_type(ip_fun) - geo_values = GeometryValues(T, ip_geo.ip, qr, RequiresHessian(requires_hessian(mapping_type))) + geo_values = GeometryValues(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) fun_values = FunctionValues(T, ip_fun, qr, ip_geo) detJdV = fill(T(NaN), length(getweights(qr))) return CellValues(fun_values, geo_values, qr, detJdV) diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 4ed3b0fc12..7dcc74a23a 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -5,8 +5,10 @@ end @inline getjacobian(mv::MappingValues) = mv.J @inline gethessian(mv::MappingValues{<:Any,<:AbstractTensor}) = mv.H -@inline gethessian(::MappingValues{JT,Nothing}) where JT = _make_hessian(JT) -@inline _make_hessian(::Type{Tensor{2,dim,T}}) where {dim,T} = zero(Tensor{3,dim,T}) +# This will be needed for optimizing away the hessian calculation/updates +# for cases when this is known to be zero (due to the geometric interpolation) +#@inline gethessian(::MappingValues{JT,Nothing}) where JT = _make_hessian(JT) +#@inline _make_hessian(::Type{Tensor{2,dim,T}}) where {dim,T} = zero(Tensor{3,dim,T}) struct RequiresHessian{B} end RequiresHessian(B::Bool) = RequiresHessian{B}() @@ -22,6 +24,7 @@ struct GeometryValues{IP, M_t, dMdξ_t, d2Mdξ2_t} M::M_t # ::AbstractVector{<:Number} Values of geometric shape functions dMdξ::dMdξ_t # ::AbstractVector{<:Vec} Gradients of geometric shape functions in ref-domain d2Mdξ2::d2Mdξ2_t # ::AbstractVector{<:Tensor{2}} Hessians of geometric shape functions in ref-domain + # ::Nothing When hessians are not required end function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, ::RequiresHessian{RH}) where {T,RH} n_shape = getnbasefunctions(ip) From bcb25d33fb58c029554e951535ca8e887e236097 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 12:57:02 +0200 Subject: [PATCH 035/172] Fix copy for CellValues and FaceValues --- src/FEValues/CellValues.jl | 4 ++++ src/FEValues/FaceValues.jl | 7 ++++++ src/FEValues/FunctionValues.jl | 6 ++++++ src/FEValues/GeometryValues.jl | 5 +++++ src/FEValues/common_values.jl | 10 --------- test/test_cellvalues.jl | 33 ++++++++++++++++++---------- test/test_facevalues.jl | 39 +++++++++++++++++++++++++--------- 7 files changed, 73 insertions(+), 31 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 5e2a9c4986..7327b581d0 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -24,6 +24,10 @@ function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolatio return CellValues(T, qr, ip, VectorizedInterpolation(ip_geo)) end +function Base.copy(cv::CellValues) + return CellValues(copy(cv.fun_values), copy(cv.geo_values), copy(cv.qr), copy(cv.detJdV)) +end + # Access geometry values for op = (:getngeobasefunctions, :geometric_value) eval(quote diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 0316ef729d..5202e596e3 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -20,6 +20,13 @@ FaceValues(qr::FaceQuadratureRule, ip::Interpolation, args...) = FaceValues(Floa function FaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation, ip_geo::ScalarInterpolation) where T return FaceValues(T, qr, ip, VectorizedInterpolation(ip_geo)) end + +function Base.copy(fv::FaceValues) + fun_values = map(copy, fv.fun_values) + geo_values = map(copy, fv.geo_values) + return FaceValues(fun_values, geo_values, copy(fv.qr), copy(fv.detJdV), copy(fv.normals), copy(fv.current_face)) +end + getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_values(fv)) getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) getnquadpoints(fv::FaceValues) = getnquadpoints(fv.qr, getcurrentface(fv)) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index c9b3a671c4..993751cee5 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -49,6 +49,12 @@ function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo precompute_values!(fv, qr) # Precompute N and dNdξ return fv end +function Base.copy(funvals::FunctionValues) + (;ip, N_ξ, N_x, dNdξ, dNdx) = funvals + N_ξ_copy = copy(N_ξ) + N_x_copy = N_ξ === N_x ? N_ξ_copy : copy(N_x) # Preserve aliasing + return FunctionValues(copy(ip), N_x_copy, N_ξ_copy, copy(dNdx), copy(dNdξ)) +end function precompute_values!(fv::FunctionValues, qr::QuadratureRule) n_shape = getnbasefunctions(fv.ip) diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 7dcc74a23a..6c33fa4bfe 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -49,6 +49,11 @@ function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, end return GeometryValues(ip, M, dMdξ, dM2dξ2) end +function Base.copy(geovals::GeometryValues) + (;ip, M, dMdξ, d2Mdξ2) = geovals + d2Mdξ2_copy = d2Mdξ2 === nothing ? nothing : copy(d2Mdξ2) + return GeometryValues(copy(ip), copy(M), copy(dMdξ), d2Mdξ2_copy) +end getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) @propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 6c0a42f0ea..2e5c2f565f 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -280,13 +280,3 @@ end function Base.show(io::IO, ::MIME"text/plain", fe_v::AbstractValues) print(io, "$(typeof(fe_v)) with $(getnbasefunctions(fe_v)) shape functions and $(getnquadpoints(fe_v)) quadrature points") end - -# copy -for ValueType in (GeometryValues, FunctionValues, CellValues, FaceValues) - args = [:(copy(cv.$fname)) for fname in fieldnames(ValueType)] - @eval begin - function Base.copy(cv::$ValueType) - return typeof(cv)($(args...)) - end - end -end diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 9e10fa2fff..9428684f9b 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -88,19 +88,30 @@ for (scalar_interpol, quad_rule) in ( @test spatial_coordinate(cv, i, x) ≈ qp_x end - # test copy: Disable with new structure. TODO: Re-enable - #= - cvc = copy(cv) - @test typeof(cv) == typeof(cvc) - for fname in fieldnames(typeof(cv)) - v = getfield(cv, fname) - vc = getfield(cvc, fname) - if hasmethod(pointer, Tuple{typeof(v)}) - @test pointer(getfield(cv, fname)) != pointer(getfield(cvc, fname)) + @testset "copy(::CellValues)" begin + cvc = copy(cv) + @test typeof(cv) == typeof(cvc) + + # Test that all mutable types in FunctionValues and GeometryValues have been copied + for key in (:fun_values, :geo_values) + val = getfield(cv, key) + valc = getfield(cvc, key) + for fname in fieldnames(typeof(val)) + v = getfield(val, fname) + vc = getfield(valc, fname) + isbits(v) || @test v !== vc + @test v == vc + end + end + # Test that qr and detJdV is copied as expected. + # Note that qr remain aliased, as defined by `copy(qr)=qr`, see quadrature.jl. + for fname in (:qr, :detJdV) + v = getfield(cv, fname) + vc = getfield(cvc, fname) + fname === :qr || @test v !== vc + @test v == vc end - @test v == vc end - =# end end diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index da01ba5b4e..5510ccc4a6 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -93,17 +93,36 @@ for (scalar_interpol, quad_rule) in ( end - # test copy - fvc = copy(fv) - @test typeof(fv) == typeof(fvc) - for fname in fieldnames(typeof(fv)) - v = getfield(fv, fname) - v isa Ferrite.ScalarWrapper && continue - vc = getfield(fvc, fname) - if hasmethod(pointer, Tuple{typeof(v)}) - @test pointer(v) != pointer(vc) + @testset "copy(::FaceValues)" begin + fvc = copy(fv) + @test typeof(fv) == typeof(fvc) + + # Test that all mutable types in FunctionValues and GeometryValues have been copied + for key in (:fun_values, :geo_values) + for i in eachindex(getfield(fv, key)) + val = getfield(fv, key)[i] + valc = getfield(fvc, key)[i] + for fname in fieldnames(typeof(val)) + v = getfield(val, fname) + vc = getfield(valc, fname) + isbits(v) || @test v !== vc + @test v == vc + end + end + end + # Test that qr, detJdV, normals, and current_face are copied as expected. + # Note that qr remain aliased, as defined by `copy(qr)=qr`, see quadrature.jl. + # Make it easy to test scalar wrapper equality + _mock_isequal(a, b) = a == b + _mock_isequal(a::T, b::T) where {T<:Ferrite.ScalarWrapper} = a[] == b[] + for fname in (:qr, :detJdV, :normals, :current_face) + v = getfield(fv, fname) + vc = getfield(fvc, fname) + if fname !== :qr # Test unaliased + @test v !== vc + end + @test _mock_isequal(v, vc) end - @test check_equal_or_nan(v, vc) end end end From 5c33c5bde6181b4f2e18c5e38a2bc39cbba5bec5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:14:11 +0200 Subject: [PATCH 036/172] Fix show of values --- src/FEValues/FaceValues.jl | 17 +++++++++++++++++ src/FEValues/common_values.jl | 4 ---- src/PointEval/point_values.jl | 5 +++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 5202e596e3..6a6e04b838 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -95,6 +95,23 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, ce end end +function Base.show(io::IO, d::MIME"text/plain", fv::FaceValues) + ip_geo = get_geometric_interpolation(fv) + rdim = getdim(ip_geo) + vdim = isa(shape_value(fv, 1, 1), Vec) ? length(shape_value(fv, 1, 1)) : 0 + sdim = length(shape_gradient(fv, 1, 1)) ÷ length(shape_value(fv, 1, 1)) + vstr = vdim==0 ? "scalar" : "vdim=$vdim" + print(io, "FaceValues(", vstr, ", rdim=$rdim, sdim=$sdim): ") + nqp = getnquadpoints.(fv.qr.face_rules) + if all(n==first(nqp) for n in nqp) + println(io, first(nqp), " quadrature points per face") + else + println(io, tuple(nqp...), " quadrature points on each face") + end + print(io, " Function interpolation: "); show(io, d, get_function_interpolation(fv)) + print(io, "\nGeometric interpolation: "); show(io, d, ip_geo^sdim) +end + """ BCValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Union{Type{<:BoundaryIndex}}) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 2e5c2f565f..dc198f3600 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -276,7 +276,3 @@ function spatial_coordinate(fe_v::AbstractValues, q_point::Int, x::AbstractVecto end return vec end - -function Base.show(io::IO, ::MIME"text/plain", fe_v::AbstractValues) - print(io, "$(typeof(fe_v)) with $(getnbasefunctions(fe_v)) shape functions and $(getnquadpoints(fe_v)) quadrature points") -end diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 4d603e93ba..239db54ea5 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -92,3 +92,8 @@ function reinit!(pv::PointValuesInternal{IP}, coord::Vec{dim}) where {dim, shape end return nothing end + +function Base.show(io::IO, d::MIME"text/plain", cv::PointValues) + println(io, "PointValues containing a") + show(io, d, cv.cv) +end \ No newline at end of file From 8b16b97bb834539904276f681b53f660e67b8d8a Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:27:08 +0200 Subject: [PATCH 037/172] Fix test on julia 1.6 --- src/FEValues/FunctionValues.jl | 9 ++++----- src/FEValues/GeometryValues.jl | 7 +++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 993751cee5..8f334ccc33 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -49,11 +49,10 @@ function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo precompute_values!(fv, qr) # Precompute N and dNdξ return fv end -function Base.copy(funvals::FunctionValues) - (;ip, N_ξ, N_x, dNdξ, dNdx) = funvals - N_ξ_copy = copy(N_ξ) - N_x_copy = N_ξ === N_x ? N_ξ_copy : copy(N_x) # Preserve aliasing - return FunctionValues(copy(ip), N_x_copy, N_ξ_copy, copy(dNdx), copy(dNdξ)) +function Base.copy(v::FunctionValues) + N_ξ_copy = copy(v.N_ξ) + N_x_copy = v.N_ξ === v.N_x ? N_ξ_copy : copy(v.N_x) # Preserve aliasing + return FunctionValues(copy(v.ip), N_x_copy, N_ξ_copy, copy(v.dNdx), copy(v.dNdξ)) end function precompute_values!(fv::FunctionValues, qr::QuadratureRule) diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index 6c33fa4bfe..ec47cc6a29 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -49,10 +49,9 @@ function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, end return GeometryValues(ip, M, dMdξ, dM2dξ2) end -function Base.copy(geovals::GeometryValues) - (;ip, M, dMdξ, d2Mdξ2) = geovals - d2Mdξ2_copy = d2Mdξ2 === nothing ? nothing : copy(d2Mdξ2) - return GeometryValues(copy(ip), copy(M), copy(dMdξ), d2Mdξ2_copy) +function Base.copy(v::GeometryValues) + d2Mdξ2_copy = v.d2Mdξ2 === nothing ? nothing : copy(v.d2Mdξ2) + return GeometryValues(copy(v.ip), copy(v.M), copy(v.dMdξ), d2Mdξ2_copy) end getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) From ab28d25234656b1765636c2a5d7f3eea22013fe2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:34:22 +0200 Subject: [PATCH 038/172] Relax tolerance for gradient check a bit --- test/InterpolationTestUtils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/InterpolationTestUtils.jl b/test/InterpolationTestUtils.jl index 9d98ceaa19..11e5c08b68 100644 --- a/test/InterpolationTestUtils.jl +++ b/test/InterpolationTestUtils.jl @@ -109,7 +109,7 @@ module InterpolationTestUtils ∇u2 = function_gradient(cv, 2, ue) Δu_ana = 0.5*(∇u1+∇u2) ⋅ Δx # Δu_ana_var = 0.5*(∇u2-∇u1) ⋅ Δx # Relevant to compare magnitude if test fails - @test Δu_num ≈ Δu_ana + @test isapprox(Δu_num, Δu_ana; rtol=1e-6) return nothing end From d5f71917ecfc8565d11b5e04ad3a1e90b41f793d Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:41:21 +0200 Subject: [PATCH 039/172] Move to old files --- src/FEValues/CellValues.jl | 89 ----------- src/FEValues/FaceValues.jl | 173 --------------------- src/FEValues/cell_values.jl | 292 ++++++++---------------------------- src/FEValues/face_values.jl | 277 +++++++++++----------------------- 4 files changed, 151 insertions(+), 680 deletions(-) delete mode 100644 src/FEValues/CellValues.jl delete mode 100644 src/FEValues/FaceValues.jl diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl deleted file mode 100644 index 7327b581d0..0000000000 --- a/src/FEValues/CellValues.jl +++ /dev/null @@ -1,89 +0,0 @@ - -include("GeometryValues.jl") -include("FunctionValues.jl") - -function default_geometric_interpolation(::Interpolation{shape}) where {dim, shape <: AbstractRefShape{dim}} - return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) -end - -struct CellValues{FV, GV, QR, detT<:AbstractVector} <: AbstractCellValues - fun_values::FV # FunctionValues - geo_values::GV # GeometryValues - qr::QR # QuadratureRule - detJdV::detT # AbstractVector{<:Number} -end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T - geo_values = GeometryValues(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) - fun_values = FunctionValues(T, ip_fun, qr, ip_geo) - detJdV = fill(T(NaN), length(getweights(qr))) - return CellValues(fun_values, geo_values, qr, detJdV) -end - -CellValues(qr::QuadratureRule, ip::Interpolation, args...) = CellValues(Float64, qr, ip, args...) -function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation=default_geometric_interpolation(ip)) where T - return CellValues(T, qr, ip, VectorizedInterpolation(ip_geo)) -end - -function Base.copy(cv::CellValues) - return CellValues(copy(cv.fun_values), copy(cv.geo_values), copy(cv.qr), copy(cv.detJdV)) -end - -# Access geometry values -for op = (:getngeobasefunctions, :geometric_value) - eval(quote - @propagate_inbounds $op(cv::CellValues, args...) = $op(cv.geo_values, args...) - end) -end -getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] - -# Accessors for function values -getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) -get_function_interpolation(cv::CellValues) = get_function_interpolation(cv.fun_values) -get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_values) -shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) -shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) - -for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient) - eval(quote - @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) - end) -end -# Access quadrature rule values -getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) - -function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) - geo_values = cv.geo_values - fun_values = cv.fun_values - n_geom_basefuncs = getngeobasefunctions(geo_values) - if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs - throw_incompatible_coord_length(length(x), n_geom_basefuncs) - end - @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) - mapping = calculate_mapping(geo_values, q_point, x) - detJ = calculate_detJ(getjacobian(mapping)) - detJ > 0.0 || throw_detJ_not_pos(detJ) - @inbounds cv.detJdV[q_point] = detJ*w - apply_mapping!(fun_values, q_point, mapping, cell) - end - return nothing -end - -function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) - rdim = getdim(cv.geo_values.ip) - vdim = isa(shape_value(cv, 1, 1), Vec) ? length(shape_value(cv, 1, 1)) : 0 - sdim = length(shape_gradient(cv, 1, 1)) ÷ length(shape_value(cv, 1, 1)) - vstr = vdim==0 ? "scalar" : "vdim=$vdim" - print(io, "CellValues(", vstr, ", rdim=$rdim, and sdim=$sdim): ") - print(io, getnquadpoints(cv), " quadrature points") - print(io, "\n Function interpolation: "); show(io, d, cv.fun_values.ip) - print(io, "\nGeometric interpolation: "); show(io, d, cv.geo_values.ip^sdim) -end - -# Temporary for benchmark/test -include("cell_values.jl") -function OldCellValues(cv::CellValues) - ip = cv.fun_values.ip - sdim = length(shape_gradient(cv, 1, 1)) ÷ length(shape_value(cv, 1, 1)) - ip_geo = cv.geo_values.ip^sdim - return OldCellValues(cv.qr, ip, ip_geo) -end \ No newline at end of file diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl deleted file mode 100644 index 6a6e04b838..0000000000 --- a/src/FEValues/FaceValues.jl +++ /dev/null @@ -1,173 +0,0 @@ -struct FaceValues{FV, GV, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GV<:AbstractVector{GV}} <: AbstractFaceValues - fun_values::V_FV # AbstractVector{FunctionValues} - geo_values::V_GV # AbstractVector{GeometryValues} - qr::QR # FaceQuadratureRule - detJdV::detT # AbstractVector{<:Number} - normals::nT # AbstractVector{<:Vec} - current_face::ScalarWrapper{Int} -end - -function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun)) where {T,sdim} - geo_values = [GeometryValues(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) for qr in fqr.face_rules] - fun_values = [FunctionValues(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] - max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) - detJdV = fill(T(NaN), max_nquadpoints) - normals = fill(zero(Vec{sdim,T})*T(NaN), max_nquadpoints) - return FaceValues(fun_values, geo_values, fqr, detJdV, normals, ScalarWrapper(1)) -end - -FaceValues(qr::FaceQuadratureRule, ip::Interpolation, args...) = FaceValues(Float64, qr, ip, args...) -function FaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation, ip_geo::ScalarInterpolation) where T - return FaceValues(T, qr, ip, VectorizedInterpolation(ip_geo)) -end - -function Base.copy(fv::FaceValues) - fun_values = map(copy, fv.fun_values) - geo_values = map(copy, fv.geo_values) - return FaceValues(fun_values, geo_values, copy(fv.qr), copy(fv.detJdV), copy(fv.normals), copy(fv.current_face)) -end - -getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_values(fv)) -getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) -getnquadpoints(fv::FaceValues) = getnquadpoints(fv.qr, getcurrentface(fv)) -getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] - -shape_value_type(fv::FaceValues) = shape_value_type(get_fun_values(fv)) -shape_gradient_type(fv::FaceValues) = shape_gradient_type(get_fun_values(fv)) -get_function_interpolation(fv::FaceValues) = get_function_interpolation(get_fun_values(fv)) -get_geometric_interpolation(fv::FaceValues) = get_geometric_interpolation(get_geo_values(fv)) - -get_geo_values(fv::FaceValues) = @inbounds fv.geo_values[getcurrentface(fv)] -for op = (:getngeobasefunctions, :geometric_value) - eval(quote - @propagate_inbounds $op(fv::FaceValues, args...) = $op(get_geo_values(fv), args...) - end) -end - -get_fun_values(fv::FaceValues) = @inbounds fv.fun_values[getcurrentface(fv)] -for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient, :shape_curl) - eval(quote - @propagate_inbounds $op(fv::FaceValues, i::Int, q_point::Int) = $op(get_fun_values(fv), i, q_point) - end) -end - -""" - getcurrentface(fv::FaceValues) - -Return the current active face of the `FaceValues` object (from last `reinit!`). - -""" -getcurrentface(fv::FaceValues) = fv.current_face[] - -""" - getnormal(fv::FaceValues, qp::Int) - -Return the normal at the quadrature point `qp` for the active face of the -`FaceValues` object(from last `reinit!`). -""" -getnormal(fv::FaceValues, qp::Int) = fv.normals[qp] - -nfaces(fv::FaceValues) = length(fv.geo_values) - -function checkface(fv::FaceValues, face::Int) - 0 < face <= nfaces(fv) || error("Face index out of range.") - return nothing -end - -function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, cell=nothing) where {dim, T} - @boundscheck checkface(fv, face_nr) - n_geom_basefuncs = getngeobasefunctions(fv) - length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) - - fv.current_face[] = face_nr - - geo_values = get_geo_values(fv) - fun_values = get_fun_values(fv) - @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) - mapping = calculate_mapping(geo_values, q_point, x) - J = getjacobian(mapping) - weight_norm = weighted_normal(J, getrefshape(geo_values.ip), face_nr) - detJ = norm(weight_norm) - detJ > 0.0 || throw_detJ_not_pos(detJ) - @inbounds fv.detJdV[q_point] = detJ*w - @inbounds fv.normals[q_point] = weight_norm / norm(weight_norm) - apply_mapping!(fun_values, q_point, mapping, cell) - end -end - -function Base.show(io::IO, d::MIME"text/plain", fv::FaceValues) - ip_geo = get_geometric_interpolation(fv) - rdim = getdim(ip_geo) - vdim = isa(shape_value(fv, 1, 1), Vec) ? length(shape_value(fv, 1, 1)) : 0 - sdim = length(shape_gradient(fv, 1, 1)) ÷ length(shape_value(fv, 1, 1)) - vstr = vdim==0 ? "scalar" : "vdim=$vdim" - print(io, "FaceValues(", vstr, ", rdim=$rdim, sdim=$sdim): ") - nqp = getnquadpoints.(fv.qr.face_rules) - if all(n==first(nqp) for n in nqp) - println(io, first(nqp), " quadrature points per face") - else - println(io, tuple(nqp...), " quadrature points on each face") - end - print(io, " Function interpolation: "); show(io, d, get_function_interpolation(fv)) - print(io, "\nGeometric interpolation: "); show(io, d, ip_geo^sdim) -end - -""" - BCValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Union{Type{<:BoundaryIndex}}) - -`BCValues` stores the shape values at all faces/edges/vertices (depending on `boundary_type`) for the geomatric interpolation (`geom_interpol`), -for each dof-position determined by the `func_interpol`. Used mainly by the `ConstrainHandler`. -""" -struct BCValues{T} - M::Array{T,3} - nqp::Array{Int} - current_entity::ScalarWrapper{Int} -end - -BCValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Type{<:BoundaryIndex} = Ferrite.FaceIndex) = - BCValues(Float64, func_interpol, geom_interpol, boundary_type) - -function BCValues(::Type{T}, func_interpol::Interpolation{refshape}, geom_interpol::Interpolation{refshape}, boundary_type::Type{<:BoundaryIndex} = Ferrite.FaceIndex) where {T,dim,refshape <: AbstractRefShape{dim}} - # set up quadrature rules for each boundary entity with dof-positions - # (determined by func_interpol) as the quadrature points - interpolation_coords = reference_coordinates(func_interpol) - - qrs = QuadratureRule{refshape,T,dim}[] - for boundarydofs in dirichlet_boundarydof_indices(boundary_type)(func_interpol) - dofcoords = Vec{dim,T}[] - for boundarydof in boundarydofs - push!(dofcoords, interpolation_coords[boundarydof]) - end - qrf = QuadratureRule{refshape,T}(fill(T(NaN), length(dofcoords)), dofcoords) # weights will not be used - push!(qrs, qrf) - end - - n_boundary_entities = length(qrs) - n_qpoints = n_boundary_entities == 0 ? 0 : maximum(qr->length(getweights(qr)), qrs) # Bound number of qps correctly. - n_geom_basefuncs = getnbasefunctions(geom_interpol) - M = fill(zero(T) * T(NaN), n_geom_basefuncs, n_qpoints, n_boundary_entities) - nqp = zeros(Int,n_boundary_entities) - - for n_boundary_entity in 1:n_boundary_entities - for (qp, ξ) in enumerate(qrs[n_boundary_entity].points), i in 1:n_geom_basefuncs - M[i, qp, n_boundary_entity] = shape_value(geom_interpol, ξ, i) - end - nqp[n_boundary_entity] = length(qrs[n_boundary_entity].points) - end - - BCValues{T}(M, nqp, ScalarWrapper(0)) -end - -getnquadpoints(bcv::BCValues) = bcv.nqp[bcv.current_entity.x] -function spatial_coordinate(bcv::BCValues, q_point::Int, xh::AbstractVector{Vec{dim,T}}) where {dim,T} - n_base_funcs = size(bcv.M, 1) - length(xh) == n_base_funcs || throw_incompatible_coord_length(length(xh), n_base_funcs) - x = zero(Vec{dim,T}) - face = bcv.current_entity[] - @inbounds for i in 1:n_base_funcs - x += bcv.M[i,q_point,face] * xh[i] # geometric_value(fe_v, q_point, i) * xh[i] - end - return x -end - -include("face_values.jl") \ No newline at end of file diff --git a/src/FEValues/cell_values.jl b/src/FEValues/cell_values.jl index d7480df1d8..333c2b5863 100644 --- a/src/FEValues/cell_values.jl +++ b/src/FEValues/cell_values.jl @@ -1,252 +1,80 @@ -""" - OldCellValues([::Type{T},] quad_rule::QuadratureRule, func_interpol::Interpolation, [geom_interpol::Interpolation]) -A `OldCellValues` object facilitates the process of evaluating values of shape functions, gradients of shape functions, -values of nodal functions, gradients and divergences of nodal functions etc. in the finite element cell. +include("GeometryValues.jl") +include("FunctionValues.jl") -**Arguments:** -* `T`: an optional argument (default to `Float64`) to determine the type the internal data is stored as. -* `quad_rule`: an instance of a [`QuadratureRule`](@ref) -* `func_interpol`: an instance of an [`Interpolation`](@ref) used to interpolate the approximated function -* `geom_interpol`: an optional instance of a [`Interpolation`](@ref) which is used to interpolate the geometry. - By default linear Lagrange interpolation is used. For embedded elements the geometric interpolations should - be vectorized to the spatial dimension. - -**Common methods:** - -* [`reinit!`](@ref) -* [`getnquadpoints`](@ref) -* [`getdetJdV`](@ref) - -* [`shape_value`](@ref) -* [`shape_gradient`](@ref) -* [`shape_symmetric_gradient`](@ref) -* [`shape_divergence`](@ref) - -* [`function_value`](@ref) -* [`function_gradient`](@ref) -* [`function_symmetric_gradient`](@ref) -* [`function_divergence`](@ref) -* [`spatial_coordinate`](@ref) -""" -OldCellValues - -struct OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP} <: AbstractCellValues - N::Matrix{N_t} - dNdx::Matrix{dNdx_t} - dNdξ::Matrix{dNdξ_t} - detJdV::Vector{T} - M::Matrix{T} - dMdξ::Matrix{dMdξ_t} - qr::QR - ip::IP - gip::GIP +function default_geometric_interpolation(::Interpolation{shape}) where {dim, shape <: AbstractRefShape{dim}} + return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) end -# Common initializer code for constructing OldCellValues after the types have been determined -function OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(qr::QR, ip::IP, gip::GIP) where { - IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP, -} - @assert isconcretetype(IP) && isconcretetype(N_t) && isconcretetype(dNdx_t) && - isconcretetype(dNdξ_t) && isconcretetype(T) && isconcretetype(dMdξ_t) && - isconcretetype(QR) && isconcretetype(GIP) - n_qpoints = getnquadpoints(qr) - - # Field interpolation - n_func_basefuncs = getnbasefunctions(ip) - N = fill(zero(N_t) * T(NaN), n_func_basefuncs, n_qpoints) - dNdx = fill(zero(dNdx_t) * T(NaN), n_func_basefuncs, n_qpoints) - dNdξ = fill(zero(dNdξ_t) * T(NaN), n_func_basefuncs, n_qpoints) - - # Geometry interpolation - n_geom_basefuncs = getnbasefunctions(gip) - M = fill(zero(T) * T(NaN), n_geom_basefuncs, n_qpoints) - dMdξ = fill(zero(dMdξ_t) * T(NaN), n_geom_basefuncs, n_qpoints) - - for (qp, ξ) in pairs(getpoints(qr)) - for basefunc in 1:n_func_basefuncs - dNdξ[basefunc, qp], N[basefunc, qp] = shape_gradient_and_value(ip, ξ, basefunc) - end - for basefunc in 1:n_geom_basefuncs - dMdξ[basefunc, qp], M[basefunc, qp] = shape_gradient_and_value(gip, ξ, basefunc) - end - end - - detJdV = fill(T(NaN), n_qpoints) - - OldCellValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, GIP}(N, dNdx, dNdξ, detJdV, M, dMdξ, qr, ip, gip) +struct CellValues{FV, GV, QR, detT<:AbstractVector} <: AbstractCellValues + fun_values::FV # FunctionValues + geo_values::GV # GeometryValues + qr::QR # QuadratureRule + detJdV::detT # AbstractVector{<:Number} end - -# Common entry point that fills in the numeric type and geometric interpolation -function OldCellValues(qr::QuadratureRule, ip::Interpolation, - gip::Interpolation = default_geometric_interpolation(ip)) - return OldCellValues(Float64, qr, ip, gip) +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T + geo_values = GeometryValues(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) + fun_values = FunctionValues(T, ip_fun, qr, ip_geo) + detJdV = fill(T(NaN), length(getweights(qr))) + return CellValues(fun_values, geo_values, qr, detJdV) end -# Common entry point that fills in the geometric interpolation -function OldCellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation) where {T} - return OldCellValues(T, qr, ip, default_geometric_interpolation(ip)) +CellValues(qr::QuadratureRule, ip::Interpolation, args...) = CellValues(Float64, qr, ip, args...) +function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation=default_geometric_interpolation(ip)) where T + return CellValues(T, qr, ip, VectorizedInterpolation(ip_geo)) end -# Common entry point that vectorizes an input scalar geometric interpolation -function OldCellValues(::Type{T}, qr::QuadratureRule, ip::Interpolation, sgip::ScalarInterpolation) where {T} - return OldCellValues(T, qr, ip, VectorizedInterpolation(sgip)) +function Base.copy(cv::CellValues) + return CellValues(copy(cv.fun_values), copy(cv.geo_values), copy(cv.qr), copy(cv.detJdV)) end -# Entrypoint for `ScalarInterpolation`s (rdim == sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - dim, shape <: AbstractRefShape{dim}, T, - QR <: QuadratureRule{shape}, - IP <: ScalarInterpolation{shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{dim, shape, <:Any, GIP}, -} - # Function interpolation - N_t = T - dNdx_t = dNdξ_t = Vec{dim, T} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{dim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) +# Access geometry values +for op = (:getngeobasefunctions, :geometric_value) + eval(quote + @propagate_inbounds $op(cv::CellValues, args...) = $op(cv.geo_values, args...) + end) end - -# Entrypoint for `VectorInterpolation`s (vdim == rdim == sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - dim, shape <: AbstractRefShape{dim}, T, - QR <: QuadratureRule{shape}, - IP <: VectorInterpolation{dim, shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{dim, shape, <:Any, GIP}, -} - # Field interpolation - N_t = Vec{dim, T} - dNdx_t = dNdξ_t = Tensor{2, dim, T, Tensors.n_components(Tensor{2,dim})} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{dim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) +getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] + +# Accessors for function values +getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) +get_function_interpolation(cv::CellValues) = get_function_interpolation(cv.fun_values) +get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_values) +shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) +shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) + +for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient) + eval(quote + @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) + end) end - -# Entrypoint for `VectorInterpolation`s (vdim != rdim == sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, vgip::VGIP) where { - vdim, dim, shape <: AbstractRefShape{dim}, T, - QR <: QuadratureRule{shape}, - IP <: VectorInterpolation{vdim, shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{dim, shape, <:Any, GIP}, -} - # Field interpolation - N_t = SVector{vdim, T} - dNdx_t = dNdξ_t = SMatrix{vdim, dim, T, vdim*dim} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{dim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, vgip.ip) -end - -# reinit! for regular (non-embedded) elements (rdim == sdim) -function reinit!(cv::OldCellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{Vec{dim,T}}) where { - dim, T, vdim, - N_t <: Union{Number, Vec{dim}, SVector{vdim} }, - dNdx_t <: Union{Vec{dim}, Tensor{2, dim}, SMatrix{vdim, dim}}, - dNdξ_t <: Union{Vec{dim}, Tensor{2, dim}, SMatrix{vdim, dim}}, -} - n_geom_basefuncs = getngeobasefunctions(cv) - n_func_basefuncs = getnbasefunctions(cv) - length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) - - @inbounds for (i, w) in pairs(getweights(cv.qr)) - fecv_J = zero(Tensor{2,dim,T}) - for j in 1:n_geom_basefuncs - fecv_J += x[j] ⊗ cv.dMdξ[j, i] - end - detJ = det(fecv_J) - detJ > 0.0 || throw_detJ_not_pos(detJ) - cv.detJdV[i] = detJ * w - Jinv = inv(fecv_J) - for j in 1:n_func_basefuncs - # cv.dNdx[j, i] = cv.dNdξ[j, i] ⋅ Jinv - cv.dNdx[j, i] = dothelper(cv.dNdξ[j, i], Jinv) - end +# Access quadrature rule values +getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) + +function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) + geo_values = cv.geo_values + fun_values = cv.fun_values + n_geom_basefuncs = getngeobasefunctions(geo_values) + if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs + throw_incompatible_coord_length(length(x), n_geom_basefuncs) end -end - -# Entrypoint for embedded `ScalarInterpolation`s (rdim < sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - sdim, rdim, shape <: AbstractRefShape{rdim}, T, - QR <: QuadratureRule{shape}, - IP <: ScalarInterpolation{shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{sdim, shape, <:Any, GIP}, -} - @assert sdim > rdim - # Function interpolation - N_t = T - dNdx_t = SVector{sdim, T} - dNdξ_t = SVector{rdim, T} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{rdim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) -end - -# Entrypoint for embedded `VectorInterpolation`s (rdim < sdim) -function OldCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - sdim, vdim, rdim, shape <: AbstractRefShape{rdim}, T, - QR <: QuadratureRule{shape}, - IP <: VectorInterpolation{vdim, shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{sdim, shape, <:Any, GIP}, -} - @assert sdim > rdim - # Function interpolation - N_t = SVector{vdim, T} - dNdx_t = SMatrix{vdim, sdim, T, vdim*sdim} - dNdξ_t = SMatrix{vdim, rdim, T, vdim*rdim} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{rdim, T} - return OldCellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) -end - -# reinit! for embedded elements, rdim < sdim -function reinit!(cv::OldCellValues{<:Any, N_t, dNdx_t, dNdξ_t}, x::AbstractVector{Vec{sdim,T}}) where { - rdim, sdim, vdim, T, - N_t <: Union{Number, SVector{vdim}}, - dNdx_t <: Union{SVector{sdim, T}, SMatrix{vdim, sdim, T}}, - dNdξ_t <: Union{SVector{rdim, T}, SMatrix{vdim, rdim, T}}, -} - @assert sdim > rdim "This reinit only works for embedded elements. Maybe you swapped the reference and spatial dimensions?" - n_geom_basefuncs = getngeobasefunctions(cv) - n_func_basefuncs = getnbasefunctions(cv) - length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) - - @inbounds for (i, w) in pairs(getweights(cv.qr)) - fecv_J = zero(MMatrix{sdim, rdim, T}) # TODO replace with MixedTensor (see https://github.com/Ferrite-FEM/Tensors.jl/pull/188) - for j in 1:n_geom_basefuncs - #fecv_J += x[j] ⊗ cv.dMdξ[j, i] # TODO via Tensors.jl - for k in 1:sdim, l in 1:rdim - fecv_J[k, l] += x[j][k] * cv.dMdξ[j, i][l] - end - end - fecv_J = SMatrix(fecv_J) - detJ = embedding_det(fecv_J) + @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) + mapping = calculate_mapping(geo_values, q_point, x) + detJ = calculate_detJ(getjacobian(mapping)) detJ > 0.0 || throw_detJ_not_pos(detJ) - cv.detJdV[i] = detJ * w - # Compute "left inverse" of J - Jinv = pinv(fecv_J) - for j in 1:n_func_basefuncs - #cv.dNdx[j, i] = cv.dNdξ[j, i] ⋅ Jinv # TODO via Tensors.jl - cv.dNdx[j, i] = dothelper(cv.dNdξ[j, i], Jinv) - end + @inbounds cv.detJdV[q_point] = detJ*w + apply_mapping!(fun_values, q_point, mapping, cell) end return nothing end -function Base.show(io::IO, m::MIME"text/plain", cv::OldCellValues) - println(io, "CellValues with") - println(io, "- Quadrature rule with ", getnquadpoints(cv), " points") - print(io, "- Function interpolation: "); show(io, m, cv.ip) - println(io) - print(io, "- Geometric interpolation: "); show(io, m, cv.gip) +function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) + rdim = getdim(cv.geo_values.ip) + vdim = isa(shape_value(cv, 1, 1), Vec) ? length(shape_value(cv, 1, 1)) : 0 + sdim = length(shape_gradient(cv, 1, 1)) ÷ length(shape_value(cv, 1, 1)) + vstr = vdim==0 ? "scalar" : "vdim=$vdim" + print(io, "CellValues(", vstr, ", rdim=$rdim, and sdim=$sdim): ") + print(io, getnquadpoints(cv), " quadrature points") + print(io, "\n Function interpolation: "); show(io, d, cv.fun_values.ip) + print(io, "\nGeometric interpolation: "); show(io, d, cv.geo_values.ip^sdim) end \ No newline at end of file diff --git a/src/FEValues/face_values.jl b/src/FEValues/face_values.jl index 6bd56105af..e922e0759b 100644 --- a/src/FEValues/face_values.jl +++ b/src/FEValues/face_values.jl @@ -1,191 +1,117 @@ -""" - FaceValues([::Type{T}], quad_rule::FaceQuadratureRule, func_interpol::Interpolation, [geom_interpol::Interpolation]) - -A `FaceValues` object facilitates the process of evaluating values of shape functions, gradients of shape functions, -values of nodal functions, gradients and divergences of nodal functions etc. on the faces of finite elements. - -**Arguments:** - -* `T`: an optional argument to determine the type the internal data is stored as. -* `quad_rule`: an instance of a [`FaceQuadratureRule`](@ref) -* `func_interpol`: an instance of an [`Interpolation`](@ref) used to interpolate the approximated function -* `geom_interpol`: an optional instance of an [`Interpolation`](@ref) which is used to interpolate the geometry. - By default linear Lagrange interpolation is used. - -**Common methods:** - -* [`reinit!`](@ref) -* [`getnquadpoints`](@ref) -* [`getdetJdV`](@ref) - -* [`shape_value`](@ref) -* [`shape_gradient`](@ref) -* [`shape_symmetric_gradient`](@ref) -* [`shape_divergence`](@ref) - -* [`function_value`](@ref) -* [`function_gradient`](@ref) -* [`function_symmetric_gradient`](@ref) -* [`function_divergence`](@ref) -* [`spatial_coordinate`](@ref) -""" -FaceValues - -struct OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP} <: AbstractFaceValues - N::Array{N_t, 3} - dNdx::Array{dNdx_t, 3} - dNdξ::Array{dNdξ_t, 3} - detJdV::Matrix{T} - normals::Vector{Normal_t} - M::Array{T, 3} - dMdξ::Array{dMdξ_t, 3} - qr::QR +struct FaceValues{FV, GV, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GV<:AbstractVector{GV}} <: AbstractFaceValues + fun_values::V_FV # AbstractVector{FunctionValues} + geo_values::V_GV # AbstractVector{GeometryValues} + qr::QR # FaceQuadratureRule + detJdV::detT # AbstractVector{<:Number} + normals::nT # AbstractVector{<:Vec} current_face::ScalarWrapper{Int} - func_interp::IP - geo_interp::GIP end -# Common initializer code for constructing OldFaceValues after the types have been determined -function OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP}(qr::QR, ip::IP, gip::GIP) where { - IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP, -} - @assert isconcretetype(IP) && isconcretetype(N_t) && isconcretetype(dNdx_t) && - isconcretetype(dNdξ_t) && isconcretetype(T) && isconcretetype(dMdξ_t) && - isconcretetype(QR) && isconcretetype(Normal_t) && isconcretetype(GIP) - # Quadrature - max_n_qpoints = maximum(getnquadpoints, qr.face_rules; init = 0) - n_faces = length(qr.face_rules) +function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun)) where {T,sdim} + geo_values = [GeometryValues(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) for qr in fqr.face_rules] + fun_values = [FunctionValues(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] + max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) + detJdV = fill(T(NaN), max_nquadpoints) + normals = fill(zero(Vec{sdim,T})*T(NaN), max_nquadpoints) + return FaceValues(fun_values, geo_values, fqr, detJdV, normals, ScalarWrapper(1)) +end - # Normals - normals = zeros(Normal_t, max_n_qpoints) +FaceValues(qr::FaceQuadratureRule, ip::Interpolation, args...) = FaceValues(Float64, qr, ip, args...) +function FaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation, ip_geo::ScalarInterpolation) where T + return FaceValues(T, qr, ip, VectorizedInterpolation(ip_geo)) +end - # Field interpolation - n_func_basefuncs = getnbasefunctions(ip) - N = fill(zero(N_t) * T(NaN), n_func_basefuncs, max_n_qpoints, n_faces) - dNdx = fill(zero(dNdx_t) * T(NaN), n_func_basefuncs, max_n_qpoints, n_faces) - dNdξ = fill(zero(dNdξ_t) * T(NaN), n_func_basefuncs, max_n_qpoints, n_faces) +function Base.copy(fv::FaceValues) + fun_values = map(copy, fv.fun_values) + geo_values = map(copy, fv.geo_values) + return FaceValues(fun_values, geo_values, copy(fv.qr), copy(fv.detJdV), copy(fv.normals), copy(fv.current_face)) +end - # Geometry interpolation - n_geom_basefuncs = getnbasefunctions(gip) - M = fill(zero(T) * T(NaN), n_geom_basefuncs, max_n_qpoints, n_faces) - dMdξ = fill(zero(dMdξ_t) * T(NaN), n_geom_basefuncs, max_n_qpoints, n_faces) +getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_values(fv)) +getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) +getnquadpoints(fv::FaceValues) = getnquadpoints(fv.qr, getcurrentface(fv)) +getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] + +shape_value_type(fv::FaceValues) = shape_value_type(get_fun_values(fv)) +shape_gradient_type(fv::FaceValues) = shape_gradient_type(get_fun_values(fv)) +get_function_interpolation(fv::FaceValues) = get_function_interpolation(get_fun_values(fv)) +get_geometric_interpolation(fv::FaceValues) = get_geometric_interpolation(get_geo_values(fv)) + +get_geo_values(fv::FaceValues) = @inbounds fv.geo_values[getcurrentface(fv)] +for op = (:getngeobasefunctions, :geometric_value) + eval(quote + @propagate_inbounds $op(fv::FaceValues, args...) = $op(get_geo_values(fv), args...) + end) +end - for face in 1:n_faces, (qp, ξ) in pairs(getpoints(qr, face)) - for basefunc in 1:n_func_basefuncs - dNdξ[basefunc, qp, face], N[basefunc, qp, face] = shape_gradient_and_value(ip, ξ, basefunc) - end - for basefunc in 1:n_geom_basefuncs - dMdξ[basefunc, qp, face], M[basefunc, qp, face] = shape_gradient_and_value(gip, ξ, basefunc) - end - end +get_fun_values(fv::FaceValues) = @inbounds fv.fun_values[getcurrentface(fv)] +for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient, :shape_curl) + eval(quote + @propagate_inbounds $op(fv::FaceValues, i::Int, q_point::Int) = $op(get_fun_values(fv), i, q_point) + end) +end - detJdV = fill(T(NaN), max_n_qpoints, n_faces) +""" + getcurrentface(fv::FaceValues) - OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, T, dMdξ_t, QR, Normal_t, GIP}(N, dNdx, dNdξ, detJdV, normals, M, dMdξ, qr, ScalarWrapper(0), ip, gip) -end +Return the current active face of the `FaceValues` object (from last `reinit!`). -# Common entry point that fills in the numeric type and geometric interpolation -function OldFaceValues(qr::FaceQuadratureRule, ip::Interpolation, - gip::Interpolation = default_geometric_interpolation(ip)) - return OldFaceValues(Float64, qr, ip, gip) -end +""" +getcurrentface(fv::FaceValues) = fv.current_face[] -# Common entry point that fills in the geometric interpolation -function OldFaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation) where {T} - return OldFaceValues(T, qr, ip, default_geometric_interpolation(ip)) -end +""" + getnormal(fv::FaceValues, qp::Int) -# Common entry point that vectorizes an input scalar geometric interpolation -function OldFaceValues(::Type{T}, qr::FaceQuadratureRule, ip::Interpolation, sgip::ScalarInterpolation) where {T} - return OldFaceValues(T, qr, ip, VectorizedInterpolation(sgip)) -end +Return the normal at the quadrature point `qp` for the active face of the +`FaceValues` object(from last `reinit!`). +""" +getnormal(fv::FaceValues, qp::Int) = fv.normals[qp] -# Entrypoint for `ScalarInterpolation`s (rdim == sdim) -function OldFaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - dim, shape <: AbstractRefShape{dim}, T, - QR <: FaceQuadratureRule{shape}, - IP <: ScalarInterpolation{shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{dim, shape, <:Any, GIP}, -} - # Normals - Normal_t = Vec{dim, T} - # Function interpolation - N_t = T - dNdx_t = dNdξ_t = Vec{dim, T} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{dim, T} - return OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, Normal_t, GIP}(qr, ip, gip.ip) -end +nfaces(fv::FaceValues) = length(fv.geo_values) -# Entrypoint for `VectorInterpolation`s (vdim == rdim == sdim) -function OldFaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - dim, shape <: AbstractRefShape{dim}, T, - QR <: FaceQuadratureRule{shape}, - IP <: VectorInterpolation{dim, shape}, - GIP <: ScalarInterpolation{shape}, - VGIP <: VectorizedInterpolation{dim, shape, <:Any, GIP}, -} - # Normals - Normal_t = Vec{dim, T} - # Function interpolation - N_t = Vec{dim, T} - dNdx_t = dNdξ_t = Tensor{2, dim, T, Tensors.n_components(Tensor{2,dim})} - # Geometry interpolation - M_t = T - dMdξ_t = Vec{dim, T} - return OldFaceValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, Normal_t, GIP}(qr, ip, gip.ip) +function checkface(fv::FaceValues, face::Int) + 0 < face <= nfaces(fv) || error("Face index out of range.") + return nothing end -function reinit!(fv::OldFaceValues{<:Any, N_t, dNdx_t}, x::AbstractVector{Vec{dim,T}}, face::Int) where { - dim, T, - N_t <: Union{Number, Vec{dim}}, - dNdx_t <: Union{Vec{dim}, Tensor{2,dim}} -} +function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, cell=nothing) where {dim, T} + @boundscheck checkface(fv, face_nr) n_geom_basefuncs = getngeobasefunctions(fv) - n_func_basefuncs = getnbasefunctions(fv) length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) - @boundscheck checkface(fv, face) - - fv.current_face[] = face - cb = getcurrentface(fv) - - @inbounds for (i, w) in pairs(getweights(fv.qr, cb)) - fefv_J = zero(Tensor{2,dim}) - for j in 1:n_geom_basefuncs - fefv_J += x[j] ⊗ fv.dMdξ[j, i, cb] - end - weight_norm = weighted_normal(fefv_J, fv, cb) - fv.normals[i] = weight_norm / norm(weight_norm) + + fv.current_face[] = face_nr + + geo_values = get_geo_values(fv) + fun_values = get_fun_values(fv) + @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) + mapping = calculate_mapping(geo_values, q_point, x) + J = getjacobian(mapping) + weight_norm = weighted_normal(J, getrefshape(geo_values.ip), face_nr) detJ = norm(weight_norm) - detJ > 0.0 || throw_detJ_not_pos(detJ) - fv.detJdV[i, cb] = detJ * w - Jinv = inv(fefv_J) - for j in 1:n_func_basefuncs - fv.dNdx[j, i, cb] = fv.dNdξ[j, i, cb] ⋅ Jinv - end + @inbounds fv.detJdV[q_point] = detJ*w + @inbounds fv.normals[q_point] = weight_norm / norm(weight_norm) + apply_mapping!(fun_values, q_point, mapping, cell) end - return nothing end -""" - getcurrentface(fv::OldFaceValues) - -Return the current active face of the `OldFaceValues` object (from last `reinit!`). - -""" -getcurrentface(fv::OldFaceValues) = fv.current_face[] +function Base.show(io::IO, d::MIME"text/plain", fv::FaceValues) + ip_geo = get_geometric_interpolation(fv) + rdim = getdim(ip_geo) + vdim = isa(shape_value(fv, 1, 1), Vec) ? length(shape_value(fv, 1, 1)) : 0 + sdim = length(shape_gradient(fv, 1, 1)) ÷ length(shape_value(fv, 1, 1)) + vstr = vdim==0 ? "scalar" : "vdim=$vdim" + print(io, "FaceValues(", vstr, ", rdim=$rdim, sdim=$sdim): ") + nqp = getnquadpoints.(fv.qr.face_rules) + if all(n==first(nqp) for n in nqp) + println(io, first(nqp), " quadrature points per face") + else + println(io, tuple(nqp...), " quadrature points on each face") + end + print(io, " Function interpolation: "); show(io, d, get_function_interpolation(fv)) + print(io, "\nGeometric interpolation: "); show(io, d, ip_geo^sdim) +end -""" - getnormal(fv::OldFaceValues, qp::Int) - -Return the normal at the quadrature point `qp` for the active face of the -`OldFaceValues` object(from last `reinit!`). -""" -getnormal(fv::OldFaceValues, qp::Int) = fv.normals[qp] -#= Moved to FaceValues.jl """ BCValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Union{Type{<:BoundaryIndex}}) @@ -242,25 +168,4 @@ function spatial_coordinate(bcv::BCValues, q_point::Int, xh::AbstractVector{Vec{ x += bcv.M[i,q_point,face] * xh[i] # geometric_value(fe_v, q_point, i) * xh[i] end return x -end - -nfaces(fv::FaceValues) = size(fv.N, 3) - -function checkface(fv::FaceValues, face::Int) - 0 < face <= nfaces(fv) || error("Face index out of range.") - return nothing -end -=# - -function Base.show(io::IO, m::MIME"text/plain", fv::OldFaceValues) - println(io, "FaceValues with") - nqp = getnquadpoints.(fv.qr.face_rules) - if all(n==first(nqp) for n in nqp) - println(io, "- Quadrature rule with ", first(nqp), " points per face") - else - println(io, "- Quadrature rule with ", tuple(nqp...), " points on each face") - end - print(io, "- Function interpolation: "); show(io, m, fv.func_interp) - println(io) - print(io, "- Geometric interpolation: "); show(io, m, fv.geo_interp) -end +end \ No newline at end of file From 7f17ffedcf3ea969de5bddefc5a4826e7b138524 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:41:50 +0200 Subject: [PATCH 040/172] Rename files --- src/FEValues/{cell_values.jl => CellValues.jl} | 0 src/FEValues/{face_values.jl => FaceValues.jl} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/FEValues/{cell_values.jl => CellValues.jl} (100%) rename src/FEValues/{face_values.jl => FaceValues.jl} (100%) diff --git a/src/FEValues/cell_values.jl b/src/FEValues/CellValues.jl similarity index 100% rename from src/FEValues/cell_values.jl rename to src/FEValues/CellValues.jl diff --git a/src/FEValues/face_values.jl b/src/FEValues/FaceValues.jl similarity index 100% rename from src/FEValues/face_values.jl rename to src/FEValues/FaceValues.jl From fbed8e5430614072eee57482c967b70e7435e1a5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:47:54 +0200 Subject: [PATCH 041/172] Move includes to top level --- src/FEValues/CellValues.jl | 4 ---- src/Ferrite.jl | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 333c2b5863..bad4d4b093 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -1,7 +1,3 @@ - -include("GeometryValues.jl") -include("FunctionValues.jl") - function default_geometric_interpolation(::Interpolation{shape}) where {dim, shape <: AbstractRefShape{dim}} return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) end diff --git a/src/Ferrite.jl b/src/Ferrite.jl index cdba5ee48d..6965d631ab 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -82,9 +82,9 @@ include("interpolations.jl") include("Quadrature/quadrature.jl") # FEValues -#include("FEValues/cell_values.jl") +include("FEValues/GeometryValues.jl") +include("FEValues/FunctionValues.jl") include("FEValues/CellValues.jl") -#include("FEValues/face_values.jl") include("FEValues/FaceValues.jl") include("PointEval/point_values.jl") include("FEValues/common_values.jl") From 7bbc5e4b89bf1c4bc48aa0c8224c3766cd55bf85 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:49:28 +0200 Subject: [PATCH 042/172] Remove unused example files and notes --- notes/heat_equation_rt_theory.md | 34 ---- notes/maxwell_eigenvalue.jl | 161 ------------------- notes/nedelec_raviartthomas_testing.jl | 72 --------- notes/nedelec_raviartthomas_testing_refel.jl | 61 ------- 4 files changed, 328 deletions(-) delete mode 100644 notes/heat_equation_rt_theory.md delete mode 100644 notes/maxwell_eigenvalue.jl delete mode 100644 notes/nedelec_raviartthomas_testing.jl delete mode 100644 notes/nedelec_raviartthomas_testing_refel.jl diff --git a/notes/heat_equation_rt_theory.md b/notes/heat_equation_rt_theory.md deleted file mode 100644 index 28088bbd3c..0000000000 --- a/notes/heat_equation_rt_theory.md +++ /dev/null @@ -1,34 +0,0 @@ -## Strong form -$$ -\nabla \cdot \boldsymbol{q} = h \in \Omega \\ -\boldsymbol{q} = - k\ \nabla u \in \Omega \\ -\boldsymbol{q}\cdot \boldsymbol{n} = q_n \in \Gamma_\mathrm{N}\\ -u = u_\mathrm{D} \in \Gamma_\mathrm{D} -$$ - -## Weak form -### Part 1 -$$ -\int_{\Omega} \delta u \nabla \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ -\int_{\Gamma} \delta u \boldsymbol{n} \cdot \boldsymbol{q}\ \mathrm{d}\Gamma - -\int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ -$$ - -### Part 2 -$$ -\int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega = - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega -$$ -where no Green-Gauss theorem is applied. - -### Summary -The weak form becomes, find $u\in H^1$ and $\boldsymbol{q} \in H\mathrm{(div)}$, such that -$$ -\begin{align*} --\int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega - -\int_{\Gamma} \delta u\ q_\mathrm{n}\ \mathrm{d}\Gamma -\quad -\forall\ \delta u \in \delta H^1 \\ -\int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega - \quad \forall\ \boldsymbol{\delta q} \in \delta H\mathrm{(div)} -\end{align*} -$$ diff --git a/notes/maxwell_eigenvalue.jl b/notes/maxwell_eigenvalue.jl deleted file mode 100644 index 2c64dfa140..0000000000 --- a/notes/maxwell_eigenvalue.jl +++ /dev/null @@ -1,161 +0,0 @@ -# The Maxwell eigenvalue problem -# Following the Fenics tutorial, -# [*Stable and unstable finite elements for the Maxwell eigenvalue problem*](https://fenicsproject.org/olddocs/dolfin/2019.1.0/python/demos/maxwell-eigenvalues/demo_maxwell-eigenvalues.py.html), -# we show how Nedelec elements can be used -# with Ferrite.jl -# ## Problem description -# ### Strong form -# -# ### Weak form -# ```math -# \int_\Omega \mathrm{curl}(\boldsymbol{\delta u}) \cdot \mathrm{curl}(\boldsymbol{u})\, \mathrm{d}\Omega = \lambda \int_\Omega \boldsymbol{\delta u}\cdot \boldsymbol{u}\ \mathrm{d}\Omega -# ``` -# ### FE form -# ```math -# \begin{align*} -# \int_\Omega \mathrm{curl}(\boldsymbol{\delta N}_i) \cdot \mathrm{curl}(\boldsymbol{N}_j)\, \mathrm{d}\Omega a_j &= \lambda \int_\Omega \boldsymbol{\delta N}_i\cdot \boldsymbol{N}_j\ \mathrm{d}\Omega a_j \\ -# A_{ij} a_j &= \lambda B_{ij} a_j -# \end{align*} -# ``` - -# https://iterativesolvers.julialinearalgebra.org/dev/eigenproblems/lobpcg/ -using Ferrite -import Ferrite: Nedelec, RaviartThomas -import IterativeSolvers: lobpcg -using LinearAlgebra -import CairoMakie as M - -function assemble_cell!(Ae, Be, cv) - n = getnbasefunctions(cv) - for q_point in 1:getnquadpoints(cv) - dΩ = getdetJdV(cv, q_point) - for i in 1:n - δNi = shape_value(cv, q_point, i) - curl_δNi = shape_curl(cv, q_point, i) - for j in 1:n - Nj = shape_value(cv, q_point, j) - curl_Nj = shape_curl(cv, q_point, j) - Ae[i,j] += (curl_δNi ⋅ curl_Nj)*dΩ - Be[i,j] += (δNi ⋅ Nj)*dΩ - end - end - end - return Ae, Be -end - -function plot_shapes(dh, ip, a) - cv = CellValues(QuadratureRule{RefTriangle}(1), ip, Lagrange{RefTriangle,1}()) - grid = dh.grid - n_cells = getncells(grid) - coords = (zeros(n_cells), zeros(n_cells)) - vectors = (zeros(n_cells), zeros(n_cells)) - - for cell_nr in 1:getncells(grid) - x = getcoordinates(grid, cell_nr) - reinit!(cv, x, getcells(grid, cell_nr)) - ue = a[celldofs(dh, cell_nr)] - for q_point in 1:getnquadpoints(cv) - #i = getnquadpoints(cv)*(cell_nr-1) + q_point - i = cell_nr - qp_x = spatial_coordinate(cv, q_point, x) - v = function_value(cv, q_point, ue) - sfac = norm(v) ≈ 0 ? NaN : 1.0 # Skip plotting zero-vector points - coords[1][i] = sfac*qp_x[1] - coords[2][i] = sfac*qp_x[2] - vectors[1][i] = v[1] - vectors[2][i] = v[2] - end - end - vtk_grid("tmp", dh.grid) do vtk - vtk_cell_data(vtk, vectors[1], "u1") - vtk_cell_data(vtk, vectors[2], "u2") - end - nothing, nothing - #= - fig = M.Figure() - for i in 1:2 - ax = M.Axis(fig[i,1]; aspect=M.DataAspect()) - #=for cellnr in 1:getncells(grid) - x = getcoordinates(grid, cellnr) - push!(x, x[1]) - M.lines!(ax, first.(x), last.(x), color=:black) - end=# - M.scatter!(ax, coords..., vectors[i]; lengthscale=0.1) - end - return fig - =# -end - -function doassemble(dh::DofHandler, cv::CellValues) - grid = dh.grid - A, B = create_sparsity_pattern.((dh, dh)) - assemA, assemB = start_assemble.((A, B)) - x = getcoordinates(grid, 1) - n_el_dofs = ndofs_per_cell(dh, 1) - dofs = zeros(Int, n_el_dofs) - Ae, Be = [zeros(n_el_dofs, n_el_dofs) for _ in 1:2] - - for (ic, cell) in pairs(getcells(grid)) - getcoordinates!(x, grid, cell) - celldofs!(dofs, dh, ic) - reinit!(cv, x, cell) - fill!.((Ae, Be), 0) - assemble_cell!(Ae, Be, cv) - assemble!(assemA, dofs, Ae) - assemble!(assemB, dofs, Be) - end - return A, B -end - -function get_matrices(ip::Interpolation; CT=Quadrilateral, nel=40, usebc=true) - RefShape = Ferrite.getrefshape(ip) - grid = generate_grid(CT, (nel,nel), zero(Vec{2}), π*ones(Vec{2})) - dh = DofHandler(grid) - add!(dh, :u, ip) - close!(dh) - ip_geo = Ferrite.default_interpolation(CT) - cv = CellValues(QuadratureRule{RefShape}(2), ip, ip_geo) - A, B = doassemble(dh, cv) - if usebc - ch = ConstraintHandler(dh) - dΩh = union(getfaceset(grid, "left"), getfaceset(grid, "right")) - dΩv = union(getfaceset(grid, "top"), getfaceset(grid, "bottom")) - if ip isa VectorizedInterpolation - add!(ch, Dirichlet(:u, dΩh, Returns(0.0), 2)) # y-component on left-right - add!(ch, Dirichlet(:u, dΩv, Returns(0.0), 1)) # x-component on top-bottom - else - add!(ch, Dirichlet(:u, union!(dΩh,dΩv), Returns(0.0))) - end - close!(ch) - update!(ch, 0.0) - apply!(A, ch) - apply!(B, ch) - end - return A, B, dh -end - -function solve(ip; num_values, kwargs...) - A, B = get_matrices(ip; kwargs...) - n = size(A,1) - r = lobpcg(Symmetric(A), Symmetric(B), false, zeros(n,num_values)) - return r.λ -end - -function solve_single(ip, λ=2; kwargs...) - A, B, dh = get_matrices(ip; kwargs...) - a = (A-λ*B)\zeros(size(A,1)) - return dh, a -end - -ip = Nedelec{2,RefTriangle,1}() -#ip = Lagrange{RefTriangle,1}()^2 -dh, a = solve_single(ip, CT=Triangle) -cv = CellValues(QuadratureRule{RefTriangle}(2), ip, Lagrange{RefTriangle,1}()) -fig = plot_shapes(dh, ip, a) -#λ = solve(ip; CT=Triangle, num_values=10) - -m, n = (1,1) -λ=m^2+n^2 -u(x,y)=Vec((sin(m*x),sin(n*y))) -a = zeros(ndofs) -apply_analytical!() \ No newline at end of file diff --git a/notes/nedelec_raviartthomas_testing.jl b/notes/nedelec_raviartthomas_testing.jl deleted file mode 100644 index 20d5498b79..0000000000 --- a/notes/nedelec_raviartthomas_testing.jl +++ /dev/null @@ -1,72 +0,0 @@ -# Maxwell's equations -# For simplicity, we start with the very basic example from -# https://www.math.colostate.edu/~bangerth/videos.676.33.html -# Specifically, -# ```math -# \int_\Omega \left[\mathrm{curl}(\boldsymbol{\delta u}) \cdot \mathrm{curl}(\boldsymbol{u}) -# + \mathrm{div}(\boldsymbol{\delta u}) \mathrm{div}(\boldsymbol{u})\right] \mathrm{d}\Omega = 0 -# ``` -# As noted in the lecture, standard Lagrange elements are not sufficient to solve this problem accurately, -# and we therefore use the Nedelec interpolation. -import LinearAlgebra: normalize -using StaticArrays -using Test -using Ferrite -import Ferrite: Nedelec, RaviartThomas -import CairoMakie as M -ip = Nedelec{2,RefTriangle,2}() -ip = RaviartThomas{2,RefTriangle,1}() -grid = generate_grid(Triangle, (1,2)) -dh = DofHandler(grid) -add!(dh, :B, ip) -close!(dh) - -ip_geo = Ferrite.default_interpolation(Triangle) -qr = QuadratureRule{RefTriangle}(10) - -qr_points = Vec{2,Float64}[]; n=6 -append!(qr_points, [Vec((0.0, i/(n+1))) for i in 1:n]) -append!(qr_points, [Vec((i/(n+1), 0.0)) for i in 1:n]) -append!(qr_points, [Vec((i/(n+1), 1 - i/(n+1))) for i in 1:n]) -qr = QuadratureRule{RefTriangle}(zeros(length(qr_points)), qr_points) -cv = CellValues(qr, ip, ip_geo) - -function plot_shapes(dh, cv) - grid = dh.grid - n_qp = getncells(grid)*getnquadpoints(cv) - coords = (zeros(n_qp), zeros(n_qp)) - vectors = (zeros(n_qp), zeros(n_qp)) - - for nr in 1:(ndofs(dh)) - u = zeros(ndofs(dh)) - u[nr] = 1.0 - - for cell_nr in 1:getncells(grid) - x = getcoordinates(grid, cell_nr) - reinit!(cv, x, getcells(grid, cell_nr)) - ue = u[celldofs(dh, cell_nr)] - for q_point in 1:getnquadpoints(cv) - i = getnquadpoints(cv)*(cell_nr-1) + q_point - qp_x = spatial_coordinate(cv, q_point, x) - v = function_value(cv, q_point, ue) - sfac = norm(v) ≈ 0 ? NaN : 1.0 # Skip plotting zero-vector points - coords[1][i] = sfac*qp_x[1] - coords[2][i] = sfac*qp_x[2] - vectors[1][i] = v[1] - vectors[2][i] = v[2] - end - end - - fig = M.Figure() - ax = M.Axis(fig[1,1]; aspect=M.DataAspect()) - for cellnr in 1:getncells(grid) - x = getcoordinates(grid, cellnr) - push!(x, x[1]) - M.lines!(ax, first.(x), last.(x), color=:black) - end - M.arrows!(ax, coords..., vectors...; lengthscale=0.1) - display(fig) - end - return nothing -end -plot_shapes(dh, cv) diff --git a/notes/nedelec_raviartthomas_testing_refel.jl b/notes/nedelec_raviartthomas_testing_refel.jl deleted file mode 100644 index 7bbc56c73a..0000000000 --- a/notes/nedelec_raviartthomas_testing_refel.jl +++ /dev/null @@ -1,61 +0,0 @@ -# Maxwell's equations -# For simplicity, we start with the very basic example from -# https://www.math.colostate.edu/~bangerth/videos.676.33.html -# Specifically, -# ```math -# \int_\Omega \left[\mathrm{curl}(\boldsymbol{\delta u}) \cdot \mathrm{curl}(\boldsymbol{u}) -# + \mathrm{div}(\boldsymbol{\delta u}) \mathrm{div}(\boldsymbol{u})\right] \mathrm{d}\Omega = 0 -# ``` -# As noted in the lecture, standard Lagrange elements are not sufficient to solve this problem accurately, -# and we therefore use the Nedelec interpolation. -import CairoMakie as M -using Ferrite -import Ferrite: Nedelec, RaviartThomas -import Ferrite: reference_coordinates -ip = Nedelec{2,RefTriangle,1}() -ip = RaviartThomas{2,RefTriangle,1}() -ip_geo = Lagrange{RefTriangle,1}() -ref_x = reference_coordinates(ip_geo) - -x_vertices = [ref_x..., ref_x[1]] - -function create_testpoints(npts) - inside(x) = (1 - x[1]) > x[2] - x = Vec{2,Float64}[] - for i in 0:(npts-1) - for j in i:(npts-1) - push!(x, Vec((i/(npts-1)), 1 - j/(npts-1))) - end - end - return x -end -x = create_testpoints(10) - -w = ones(length(x))*0.5/length(x) -qr = QuadratureRule{RefTriangle}(w, x) - -fig = M.Figure(resolution=(1000,1000)); -for i in 1:3 - ax=M.Axis(fig[i,1]; aspect=M.DataAspect()); - M.lines!(ax, first.(x_vertices), last.(x_vertices)) - v = shape_value.((ip,), x, i) - M.scatter!(ax, first.(x), last.(x)) - M.arrows!(ax, first.(x), last.(x), first.(v), last.(v); lengthscale=0.25) -end -display(fig) - -fig2 = M.Figure(resolution=(1000,1000)) -cv = CellValues(qr, ip, ip_geo) -ref_x[1] = Vec(4.0,0.0) -reinit!(cv, ref_x, Triangle((1,2,3))) -x_vertices = [ref_x..., ref_x[1]] -for i in 1:3 - ax=M.Axis(fig2[i,1]; aspect=M.DataAspect()); - M.lines!(ax, first.(x_vertices), last.(x_vertices)) - x_qp = spatial_coordinate.((cv,), 1:length(x), (ref_x,)) - @show x_qp ≈ x # should be false - v = shape_value.((cv,), 1:length(x), i) - M.scatter!(ax, first.(x_qp), last.(x_qp)) - M.arrows!(ax, first.(x_qp), last.(x_qp), first.(v), last.(v); lengthscale=0.25) -end -fig2 \ No newline at end of file From d322caf189e3ac3ae9e74ee4e5828dc8e836c619 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:54:57 +0200 Subject: [PATCH 043/172] Rename back and re-add docstrings --- .../{CellValues.jl => cell_values.jl} | 33 +++++++++++++++++++ .../{FaceValues.jl => face_values.jl} | 33 +++++++++++++++++++ 2 files changed, 66 insertions(+) rename src/FEValues/{CellValues.jl => cell_values.jl} (73%) rename src/FEValues/{FaceValues.jl => face_values.jl} (86%) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/cell_values.jl similarity index 73% rename from src/FEValues/CellValues.jl rename to src/FEValues/cell_values.jl index bad4d4b093..2bf29cc797 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/cell_values.jl @@ -1,3 +1,36 @@ +""" + CellValues([::Type{T},] quad_rule::QuadratureRule, func_interpol::Interpolation, [geom_interpol::Interpolation]) + +A `CellValues` object facilitates the process of evaluating values of shape functions, gradients of shape functions, +values of nodal functions, gradients and divergences of nodal functions etc. in the finite element cell. + +**Arguments:** +* `T`: an optional argument (default to `Float64`) to determine the type the internal data is stored as. +* `quad_rule`: an instance of a [`QuadratureRule`](@ref) +* `func_interpol`: an instance of an [`Interpolation`](@ref) used to interpolate the approximated function +* `geom_interpol`: an optional instance of a [`Interpolation`](@ref) which is used to interpolate the geometry. + By default linear Lagrange interpolation is used. For embedded elements the geometric interpolations should + be vectorized to the spatial dimension. + +**Common methods:** + +* [`reinit!`](@ref) +* [`getnquadpoints`](@ref) +* [`getdetJdV`](@ref) + +* [`shape_value`](@ref) +* [`shape_gradient`](@ref) +* [`shape_symmetric_gradient`](@ref) +* [`shape_divergence`](@ref) + +* [`function_value`](@ref) +* [`function_gradient`](@ref) +* [`function_symmetric_gradient`](@ref) +* [`function_divergence`](@ref) +* [`spatial_coordinate`](@ref) +""" +CellValues + function default_geometric_interpolation(::Interpolation{shape}) where {dim, shape <: AbstractRefShape{dim}} return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) end diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/face_values.jl similarity index 86% rename from src/FEValues/FaceValues.jl rename to src/FEValues/face_values.jl index e922e0759b..9aea41af06 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/face_values.jl @@ -1,3 +1,36 @@ +""" + FaceValues([::Type{T}], quad_rule::FaceQuadratureRule, func_interpol::Interpolation, [geom_interpol::Interpolation]) + +A `FaceValues` object facilitates the process of evaluating values of shape functions, gradients of shape functions, +values of nodal functions, gradients and divergences of nodal functions etc. on the faces of finite elements. + +**Arguments:** + +* `T`: an optional argument to determine the type the internal data is stored as. +* `quad_rule`: an instance of a [`FaceQuadratureRule`](@ref) +* `func_interpol`: an instance of an [`Interpolation`](@ref) used to interpolate the approximated function +* `geom_interpol`: an optional instance of an [`Interpolation`](@ref) which is used to interpolate the geometry. + By default linear Lagrange interpolation is used. + +**Common methods:** + +* [`reinit!`](@ref) +* [`getnquadpoints`](@ref) +* [`getdetJdV`](@ref) + +* [`shape_value`](@ref) +* [`shape_gradient`](@ref) +* [`shape_symmetric_gradient`](@ref) +* [`shape_divergence`](@ref) + +* [`function_value`](@ref) +* [`function_gradient`](@ref) +* [`function_symmetric_gradient`](@ref) +* [`function_divergence`](@ref) +* [`spatial_coordinate`](@ref) +""" +FaceValues + struct FaceValues{FV, GV, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GV<:AbstractVector{GV}} <: AbstractFaceValues fun_values::V_FV # AbstractVector{FunctionValues} geo_values::V_GV # AbstractVector{GeometryValues} From 8384d01b898a62c7becb2f30b327edfc1947e184 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 13:55:37 +0200 Subject: [PATCH 044/172] Remove CairoMakie from docs --- docs/Manifest.toml | 607 +-------------------------------------------- docs/Project.toml | 1 - 2 files changed, 5 insertions(+), 603 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 52fe3bffa6..08e315defd 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.9.2" manifest_format = "2.0" -project_hash = "f4ba79c26c9d5fa21c707a95d5a76911eedc0ccf" +project_hash = "8722b6a67abf1b81e2c7808ddfcfb1d737cd0040" [[deps.ADTypes]] git-tree-sha1 = "5d2e21d7b0d8c22f67483ef95ebdc39c0e6b6003" @@ -14,22 +14,6 @@ git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" -[[deps.AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" -uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.5.0" -weakdeps = ["ChainRulesCore", "Test"] - - [deps.AbstractFFTs.extensions] - AbstractFFTsChainRulesCoreExt = "ChainRulesCore" - AbstractFFTsTestExt = "Test" - -[[deps.AbstractLattices]] -git-tree-sha1 = "f35684b7349da49fcc8a9e520e30e45dbb077166" -uuid = "398f06c4-4d28-53ec-89ca-5b2656b7603d" -version = "0.2.1" - [[deps.AbstractTrees]] git-tree-sha1 = "faa260e4cb5aba097a73fab382dd4b5819d8ec8c" uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -45,12 +29,6 @@ weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" -[[deps.Animations]] -deps = ["Colors"] -git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d" -uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340" -version = "0.4.1" - [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.1" @@ -102,24 +80,6 @@ weakdeps = ["SparseArrays"] [[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -[[deps.Automa]] -deps = ["TranscodingStreams"] -git-tree-sha1 = "ef9997b3d5547c48b41c7bd8899e812a917b409d" -uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" -version = "0.8.4" - -[[deps.AxisAlgorithms]] -deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] -git-tree-sha1 = "66771c8d21c8ff5e3a93379480a2307ac36863f7" -uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950" -version = "1.0.1" - -[[deps.AxisArrays]] -deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"] -git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f" -uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9" -version = "0.4.7" - [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" @@ -163,56 +123,18 @@ git-tree-sha1 = "19a35467a82e236ff51bc17a3a44b69ef35185a2" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" version = "1.0.8+0" -[[deps.CEnum]] -git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90" -uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" -version = "0.4.2" - [[deps.CPUSummary]] deps = ["CpuId", "IfElse", "PrecompileTools", "Static"] git-tree-sha1 = "601f7e7b3d36f18790e2caf83a882d88e9b71ff1" uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" version = "0.2.4" -[[deps.CRC32c]] -uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" - -[[deps.CRlibm]] -deps = ["CRlibm_jll"] -git-tree-sha1 = "32abd86e3c2025db5172aa182b982debed519834" -uuid = "96374032-68de-5a5b-8d9e-752f78720389" -version = "1.0.1" - -[[deps.CRlibm_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" -uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" -version = "1.0.1+0" - -[[deps.Cairo]] -deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] -git-tree-sha1 = "d0b3f8b4ad16cb0a2988c6788646a5e6a17b6b1b" -uuid = "159f3aea-2a34-519c-b102-8c37f9878175" -version = "1.0.5" - -[[deps.CairoMakie]] -deps = ["Base64", "Cairo", "Colors", "FFTW", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools", "SHA"] -git-tree-sha1 = "74384dc4aba2b377e22703e849154252930c434d" -uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" -version = "0.10.11" - [[deps.Cairo_jll]] deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" version = "1.16.1+1" -[[deps.Calculus]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" -uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" -version = "0.5.1" - [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra", "SparseArrays"] git-tree-sha1 = "e30f2f4e20f7f186dc36529910beaedc60cfa644" @@ -231,12 +153,6 @@ git-tree-sha1 = "02aa26a4cf76381be7f66e020a3eddeb27b0a092" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" version = "0.7.2" -[[deps.ColorBrewer]] -deps = ["Colors", "JSON", "Test"] -git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4" -uuid = "a2cac450-b92f-5266-8821-25eda20663c8" -version = "0.4.0" - [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129" @@ -261,11 +177,6 @@ git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" version = "0.12.10" -[[deps.Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" - [[deps.CommonSolve]] git-tree-sha1 = "0eee5eb66b1cf62cd6ad1b460238e60e4b09400c" uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" @@ -308,12 +219,15 @@ deps = ["LinearAlgebra"] git-tree-sha1 = "c53fc348ca4d40d7b371e71fd52251839080cbc9" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" version = "1.5.4" -weakdeps = ["IntervalSets", "StaticArrays"] [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" ConstructionBaseStaticArraysExt = "StaticArrays" + [deps.ConstructionBase.weakdeps] + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + [[deps.Contour]] git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" @@ -345,12 +259,6 @@ version = "1.0.0" deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -[[deps.DelaunayTriangulation]] -deps = ["DataStructures", "EnumX", "ExactPredicates", "Random", "SimpleGraphs"] -git-tree-sha1 = "bea7984f7e09aeb28a3b071c420a0186cb4fabad" -uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" -version = "0.8.8" - [[deps.DelimitedFiles]] deps = ["Mmap"] git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" @@ -414,20 +322,6 @@ weakdeps = ["ChainRulesCore", "SparseArrays"] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -[[deps.Distributions]] -deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns", "Test"] -git-tree-sha1 = "3d5873f811f582873bb9871fc9c451784d5dc8c7" -uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.102" - - [deps.Distributions.extensions] - DistributionsChainRulesCoreExt = "ChainRulesCore" - DistributionsDensityInterfaceExt = "DensityInterface" - - [deps.Distributions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" - [[deps.DocStringExtensions]] deps = ["LibGit2"] git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" @@ -451,18 +345,6 @@ deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" -[[deps.DualNumbers]] -deps = ["Calculus", "NaNMath", "SpecialFunctions"] -git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566" -uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" -version = "0.6.8" - -[[deps.EarCut_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053" -uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" -version = "2.2.4+0" - [[deps.EnumX]] git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" @@ -480,17 +362,6 @@ git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643" uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" version = "0.0.20230411+0" -[[deps.ErrorfreeArithmetic]] -git-tree-sha1 = "d6863c556f1142a061532e79f611aa46be201686" -uuid = "90fa49ef-747e-5e6f-a989-263ba693cf1a" -version = "0.5.2" - -[[deps.ExactPredicates]] -deps = ["IntervalArithmetic", "Random", "StaticArraysCore", "Test"] -git-tree-sha1 = "276e83bc8b21589b79303b9985c321024ffdf59c" -uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" -version = "2.2.5" - [[deps.ExceptionUnwrapping]] deps = ["Test"] git-tree-sha1 = "e90caa41f5a86296e014e148ee061bd6c3edec96" @@ -514,11 +385,6 @@ git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" version = "0.1.10" -[[deps.Extents]] -git-tree-sha1 = "5e1e4c53fa39afe63a7d356e30452249365fba99" -uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" -version = "0.1.1" - [[deps.FFMPEG]] deps = ["FFMPEG_jll"] git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8" @@ -531,18 +397,6 @@ git-tree-sha1 = "74faea50c1d007c85837327f6775bea60b5492dd" uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" version = "4.4.2+2" -[[deps.FFTW]] -deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "b4fbdd20c889804969571cc589900803edda16b7" -uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.7.1" - -[[deps.FFTW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" -uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+0" - [[deps.FLTK_jll]] deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "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 = "72a4842f93e734f378cf381dae2ca4542f019d23" @@ -566,12 +420,6 @@ git-tree-sha1 = "b12f05108e405dadcc2aff0008db7f831374e051" uuid = "29a986be-02c6-4525-aec4-84b980013641" version = "2.0.0" -[[deps.FastRounding]] -deps = ["ErrorfreeArithmetic", "LinearAlgebra"] -git-tree-sha1 = "6344aa18f654196be82e62816935225b3b9abe44" -uuid = "fa42c844-2597-5d31-933b-ebd51ab2693f" -version = "0.3.1" - [[deps.Ferrite]] deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] path = ".." @@ -662,24 +510,12 @@ weakdeps = ["StaticArrays"] [deps.ForwardDiff.extensions] ForwardDiffStaticArraysExt = "StaticArrays" -[[deps.FreeType]] -deps = ["CEnum", "FreeType2_jll"] -git-tree-sha1 = "50351f83f95282cf903e968d7c6e8d44a5f83d0b" -uuid = "b38be410-82b0-50bf-ab77-7b57e271db43" -version = "4.1.0" - [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" version = "2.13.1+0" -[[deps.FreeTypeAbstraction]] -deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] -git-tree-sha1 = "38a92e40157100e796690421e34a11c107205c86" -uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" -version = "0.10.0" - [[deps.FriBidi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" @@ -742,18 +578,6 @@ git-tree-sha1 = "fb69b2a645fa69ba5f474af09221b9308b160ce6" uuid = "c145ed77-6b09-5dd9-b285-bf645a82121e" version = "0.5.3" -[[deps.GeoInterface]] -deps = ["Extents"] -git-tree-sha1 = "d53480c0793b13341c40199190f92c611aa2e93c" -uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" -version = "1.3.2" - -[[deps.GeometryBasics]] -deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "424a5a6ce7c5d97cca7bcc4eac551b97294c54af" -uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.4.9" - [[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" @@ -772,12 +596,6 @@ git-tree-sha1 = "4d4dedef84147934837c683538467cea54c44d44" uuid = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" version = "0.2.2" -[[deps.Graphics]] -deps = ["Colors", "LinearAlgebra", "NaNMath"] -git-tree-sha1 = "d61890399bc535850c4bf08e4e0d3a7ad0f21cbd" -uuid = "a2bd30eb-e257-5431-a919-1863eab51364" -version = "1.1.2" - [[deps.Graphite2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" @@ -790,12 +608,6 @@ git-tree-sha1 = "899050ace26649433ef1af25bc17a815b3db52b7" uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" version = "1.9.0" -[[deps.GridLayoutBase]] -deps = ["GeometryBasics", "InteractiveUtils", "Observables"] -git-tree-sha1 = "f57a64794b336d4990d90f80b147474b869b1bc4" -uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" -version = "0.9.2" - [[deps.Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" @@ -825,12 +637,6 @@ git-tree-sha1 = "eb8fed28f4994600e29beef49744639d985a04b2" uuid = "3e5b6fbb-0976-4d2c-9146-d79de83f2fb0" version = "0.1.16" -[[deps.HypergeometricFunctions]] -deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "f218fe3736ddf977e0e772bc9a586b2383da2685" -uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.23" - [[deps.IOCapture]] deps = ["Logging", "Random"] git-tree-sha1 = "d75853a0bdbfb1ac815478bacd89cd27b550ace6" @@ -842,57 +648,11 @@ git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" version = "0.1.1" -[[deps.ImageAxes]] -deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"] -git-tree-sha1 = "2e4520d67b0cef90865b3ef727594d2a58e0e1f8" -uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac" -version = "0.6.11" - -[[deps.ImageBase]] -deps = ["ImageCore", "Reexport"] -git-tree-sha1 = "b51bb8cae22c66d0f6357e3bcb6363145ef20835" -uuid = "c817782e-172a-44cc-b673-b171935fbb9e" -version = "0.1.5" - -[[deps.ImageCore]] -deps = ["AbstractFFTs", "ColorVectorSpace", "Colors", "FixedPointNumbers", "Graphics", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "Reexport"] -git-tree-sha1 = "acf614720ef026d38400b3817614c45882d75500" -uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534" -version = "0.9.4" - -[[deps.ImageIO]] -deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs"] -git-tree-sha1 = "bca20b2f5d00c4fbc192c3212da8fa79f4688009" -uuid = "82e4d734-157c-48bb-816b-45c225c6df19" -version = "0.6.7" - -[[deps.ImageMetadata]] -deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] -git-tree-sha1 = "355e2b974f2e3212a75dfb60519de21361ad3cb7" -uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49" -version = "0.9.9" - -[[deps.Imath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3d09a9f60edf77f8a4d99f9e015e8fbf9989605d" -uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" -version = "3.1.7+0" - -[[deps.IndirectArrays]] -git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" -uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" -version = "1.0.0" - [[deps.Inflate]] git-tree-sha1 = "5cd07aab533df5170988219191dfad0519391428" uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" version = "0.1.3" -[[deps.IntegerMathUtils]] -git-tree-sha1 = "b8ffb903da9f7b8cf695a8bead8e01814aa24b30" -uuid = "18e54dd8-cb9d-406c-a71d-865a43cbb235" -version = "0.1.2" - [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "ad37c091f7d7daf900963171600d7c1c5c3ede32" @@ -903,44 +663,11 @@ version = "2023.2.0+0" deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -[[deps.Interpolations]] -deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] -git-tree-sha1 = "721ec2cf720536ad005cb38f50dbba7b02419a15" -uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" -version = "0.14.7" - -[[deps.IntervalArithmetic]] -deps = ["CRlibm", "FastRounding", "LinearAlgebra", "Markdown", "Random", "RecipesBase", "RoundingEmulator", "SetRounding", "StaticArrays"] -git-tree-sha1 = "5ab7744289be503d76a944784bac3f2df7b809af" -uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" -version = "0.20.9" - -[[deps.IntervalSets]] -deps = ["Dates", "Random"] -git-tree-sha1 = "8e59ea773deee525c99a8018409f64f19fb719e6" -uuid = "8197267c-284f-5f27-9208-e0e47529a953" -version = "0.7.7" -weakdeps = ["Statistics"] - - [deps.IntervalSets.extensions] - IntervalSetsStatisticsExt = "Statistics" - [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" version = "0.2.2" -[[deps.Isoband]] -deps = ["isoband_jll"] -git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137" -uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4" -version = "0.1.1" - -[[deps.IterTools]] -git-tree-sha1 = "4ced6667f9974fc5c5943fa5e2ef1ca43ea9e450" -uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.8.0" - [[deps.IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] git-tree-sha1 = "1169632f425f79429f245113b775a0e3d121457c" @@ -982,12 +709,6 @@ git-tree-sha1 = "5f0bd0cd69df978fa64ccdcb5c152fbc705455a1" uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" version = "1.3.0" -[[deps.JpegTurbo]] -deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"] -git-tree-sha1 = "d65930fa2bc96b07d7691c652d701dcbe7d9cf0b" -uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0" -version = "0.1.4" - [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "6f2675ef130a300a112286de91973805fcc5ffbc" @@ -1000,12 +721,6 @@ git-tree-sha1 = "884c2968c2e8e7e6bf5956af88cb46aa745c854b" uuid = "ef3ab10e-7fda-4108-b977-705223b18434" version = "0.4.1" -[[deps.KernelDensity]] -deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] -git-tree-sha1 = "90442c50e202a5cdf21a7899c66b240fdef14035" -uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" -version = "0.6.7" - [[deps.Krylov]] deps = ["LinearAlgebra", "Printf", "SparseArrays"] git-tree-sha1 = "17e462054b42dcdda73e9a9ba0c67754170c88ae" @@ -1076,11 +791,6 @@ version = "0.15.1" deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -[[deps.LazyModules]] -git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e" -uuid = "8cdb02fc-e678-4876-92c5-9defec4f444e" -version = "0.3.1" - [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -1167,12 +877,6 @@ version = "7.2.0" deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -[[deps.LinearAlgebraX]] -deps = ["LinearAlgebra", "Mods", "Permutations", "Primes", "SimplePolynomials"] -git-tree-sha1 = "558a338f1eeabe933f9c2d4052aa7c2c707c3d52" -uuid = "9b3f67b0-2d00-526e-9884-9e4938f8fb88" -version = "0.1.12" - [[deps.LinearElasticity_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" @@ -1284,28 +988,11 @@ git-tree-sha1 = "9ee1618cbf5240e6d4e0371d6f24065083f60c48" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.11" -[[deps.Makie]] -deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FixedPointNumbers", "Formatting", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "InteractiveUtils", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "Match", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Setfield", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "StableHashTraits", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun"] -git-tree-sha1 = "1d16d20279a145119899b4205258332f0fbeaa94" -uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" -version = "0.19.11" - -[[deps.MakieCore]] -deps = ["Observables", "REPL"] -git-tree-sha1 = "a94bf3fef9c690a2a4ac1d09d86a59ab89c7f8e4" -uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" -version = "0.6.8" - [[deps.ManualMemory]] git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" uuid = "d125e4d3-2237-4719-b19c-fa641b8a4667" version = "0.1.8" -[[deps.MappedArrays]] -git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" -uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" -version = "0.4.2" - [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" @@ -1316,17 +1003,6 @@ git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" version = "0.1.2" -[[deps.Match]] -git-tree-sha1 = "1d9bc5c1a6e7ee24effb93f175c9342f9154d97f" -uuid = "7eb4fadd-790c-5f42-8a69-bfa0b872bfbf" -version = "1.2.0" - -[[deps.MathTeXEngine]] -deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "Test", "UnicodeFun"] -git-tree-sha1 = "8f52dbaa1351ce4cb847d95568cb29e62a307d93" -uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" -version = "0.5.6" - [[deps.MbedTLS]] deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "Random", "Sockets"] git-tree-sha1 = "03a9b9718f5682ecb107ac9f7308991db4ce395b" @@ -1352,17 +1028,6 @@ version = "1.1.0" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" -[[deps.Mods]] -git-tree-sha1 = "61be59e4daffff43a8cec04b5e0dc773cbb5db3a" -uuid = "7475f97c-0381-53b1-977b-4c60186c8d62" -version = "1.3.3" - -[[deps.MosaicViews]] -deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] -git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe" -uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389" -version = "0.3.4" - [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2022.10.11" @@ -1372,11 +1037,6 @@ git-tree-sha1 = "cac9cc5499c25554cba55cd3c30543cff5ca4fab" uuid = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" version = "0.2.4" -[[deps.Multisets]] -git-tree-sha1 = "8d852646862c96e226367ad10c8af56099b4047e" -uuid = "3b2b4ff1-bcff-5658-a3ee-dbcf1ce5ac09" -version = "0.4.4" - [[deps.NLSolversBase]] deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" @@ -1401,12 +1061,6 @@ git-tree-sha1 = "2c3726ceb3388917602169bed973dbc97f1b51a8" uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" version = "0.4.13" -[[deps.Netpbm]] -deps = ["FileIO", "ImageCore", "ImageMetadata"] -git-tree-sha1 = "d92b107dbb887293622df7697a2223f9f8176fcd" -uuid = "f09324ee-3d7c-5217-9330-fc30815ba969" -version = "1.1.1" - [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" @@ -1423,11 +1077,6 @@ git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" version = "7.6.2+2" -[[deps.Observables]] -git-tree-sha1 = "6862738f9796b3edc1c09d0890afce4eca9e7e93" -uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.4" - [[deps.OffsetArrays]] deps = ["Adapt"] git-tree-sha1 = "2ac17d29c523ce1cd38e27785a7d23024853a4bb" @@ -1445,18 +1094,6 @@ deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" version = "0.3.21+4" -[[deps.OpenEXR]] -deps = ["Colors", "FileIO", "OpenEXR_jll"] -git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633" -uuid = "52e1d378-f018-4a11-a4be-720524705ac7" -version = "0.3.2" - -[[deps.OpenEXR_jll]] -deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "a4ca623df1ae99d09bc9868b008262d0c0ac1e4f" -uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" -version = "3.1.4+0" - [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" @@ -1508,42 +1145,12 @@ deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+0" -[[deps.PDMats]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "fcf8fd477bd7f33cb8dbb1243653fb0d415c256c" -uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.25" - -[[deps.PNGFiles]] -deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"] -git-tree-sha1 = "9b02b27ac477cad98114584ff964e3052f656a0f" -uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" -version = "0.4.0" - [[deps.PackageExtensionCompat]] git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" version = "1.0.2" weakdeps = ["Requires", "TOML"] -[[deps.Packing]] -deps = ["GeometryBasics"] -git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501" -uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566" -version = "0.5.0" - -[[deps.PaddedViews]] -deps = ["OffsetArrays"] -git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f" -uuid = "5432bcbf-9aad-5242-b902-cca2824c8663" -version = "0.5.12" - -[[deps.Pango_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4745216e94f71cb768d58330b059c9b76f32cb66" -uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.50.14+0" - [[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" @@ -1556,12 +1163,6 @@ git-tree-sha1 = "716e24b21538abc91f6205fd1d8363f39b442851" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.7.2" -[[deps.Permutations]] -deps = ["Combinatorics", "LinearAlgebra", "Random"] -git-tree-sha1 = "25e2bb0973689836bf164ecb960762f1bb8794dd" -uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" -version = "0.4.17" - [[deps.Pipe]] git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" uuid = "b98c9c47-44ae-5843-9183-064241ee97a0" @@ -1578,12 +1179,6 @@ deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", " uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" version = "1.9.2" -[[deps.PkgVersion]] -deps = ["Pkg"] -git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da" -uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" -version = "0.3.3" - [[deps.PlotThemes]] deps = ["PlotUtils", "Statistics"] git-tree-sha1 = "1f03a2d339f42dca4a4da149c7e15e9b896ad899" @@ -1628,29 +1223,6 @@ git-tree-sha1 = "240d7170f5ffdb285f9427b92333c3463bf65bf6" uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" version = "0.2.1" -[[deps.PolygonOps]] -git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" -uuid = "647866c9-e3ac-4575-94e7-e3d426903924" -version = "0.1.2" - -[[deps.Polynomials]] -deps = ["LinearAlgebra", "RecipesBase", "Setfield", "SparseArrays"] -git-tree-sha1 = "ea78a2764f31715093de7ab495e12c0187f231d1" -uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" -version = "4.0.4" - - [deps.Polynomials.extensions] - PolynomialsChainRulesCoreExt = "ChainRulesCore" - PolynomialsFFTWExt = "FFTW" - PolynomialsMakieCoreExt = "MakieCore" - PolynomialsMutableArithmeticsExt = "MutableArithmetics" - - [deps.Polynomials.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" - MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b" - MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" - [[deps.PositiveFactorizations]] deps = ["LinearAlgebra"] git-tree-sha1 = "17275485f373e6673f7e7f97051f703ed5b15b20" @@ -1681,12 +1253,6 @@ git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.4.1" -[[deps.Primes]] -deps = ["IntegerMathUtils"] -git-tree-sha1 = "4c9f306e5d6603ae203c2000dd460d81a5251489" -uuid = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" -version = "0.5.4" - [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -1697,24 +1263,12 @@ git-tree-sha1 = "00099623ffee15972c16111bcf84c58a0051257c" uuid = "92933f4c-e287-5a05-a399-4b506db050ca" version = "1.9.0" -[[deps.QOI]] -deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] -git-tree-sha1 = "18e8f4d1426e965c7b532ddd260599e1510d26ce" -uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" -version = "1.0.0" - [[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"] git-tree-sha1 = "7c29f0e8c575428bd84dc3c72ece5178caa67336" uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" version = "6.5.2+2" -[[deps.QuadGK]] -deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "9ebcd48c498668c7fa0e97a9cae873fbee7bfee1" -uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.9.1" - [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" @@ -1723,21 +1277,6 @@ uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" deps = ["SHA", "Serialization"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -[[deps.RangeArrays]] -git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" -uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d" -version = "0.3.2" - -[[deps.Ratios]] -deps = ["Requires"] -git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b" -uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" -version = "0.4.5" -weakdeps = ["FixedPointNumbers"] - - [deps.Ratios.extensions] - RatiosFixedPointNumbersExt = "FixedPointNumbers" - [[deps.RecipesBase]] deps = ["PrecompileTools"] git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" @@ -1797,29 +1336,6 @@ git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" -[[deps.RingLists]] -deps = ["Random"] -git-tree-sha1 = "f39da63aa6d2d88e0c1bd20ed6a3ff9ea7171ada" -uuid = "286e9d63-9694-5540-9e3c-4e6708fa07b2" -version = "0.2.8" - -[[deps.Rmath]] -deps = ["Random", "Rmath_jll"] -git-tree-sha1 = "f65dcb5fa46aee0cf9ed6274ccbd597adc49aa7b" -uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" -version = "0.7.1" - -[[deps.Rmath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6ed52fdd3382cf21947b15e8870ac0ddbff736da" -uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.4.0+0" - -[[deps.RoundingEmulator]] -git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" -uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" -version = "0.2.1" - [[deps.RuntimeGeneratedFunctions]] deps = ["ExprTools", "SHA", "Serialization"] git-tree-sha1 = "6aacc5eefe8415f47b3e34214c1d79d2674a0ba2" @@ -1892,23 +1408,12 @@ version = "1.2.0" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -[[deps.SetRounding]] -git-tree-sha1 = "d7a25e439d07a17b7cdf97eecee504c50fedf5f6" -uuid = "3cc68bcd-71a2-5612-b932-767ffbe40ab0" -version = "0.2.1" - [[deps.Setfield]] deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] git-tree-sha1 = "e2cc6d8c88613c05e1defb55170bf5ff211fbeac" uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" version = "1.1.1" -[[deps.ShaderAbstractions]] -deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "db0219befe4507878b1a90e07820fed3e62c289d" -uuid = "65257c39-d410-5151-9873-9b3e5be5013e" -version = "0.4.0" - [[deps.SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" @@ -1919,23 +1424,11 @@ git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" version = "1.0.3" -[[deps.SignedDistanceFields]] -deps = ["Random", "Statistics", "Test"] -git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" -uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" -version = "0.4.0" - [[deps.SimpleBufferStream]] git-tree-sha1 = "874e8867b33a00e784c8a7e4b60afe9e037b74e1" uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" version = "1.1.0" -[[deps.SimpleGraphs]] -deps = ["AbstractLattices", "Combinatorics", "DataStructures", "IterTools", "LightXML", "LinearAlgebra", "LinearAlgebraX", "Optim", "Primes", "Random", "RingLists", "SimplePartitions", "SimplePolynomials", "SimpleRandom", "SparseArrays", "Statistics"] -git-tree-sha1 = "b608903049d11cc557c45e03b3a53e9260579c19" -uuid = "55797a34-41de-5266-9ec1-32ac4eb504d3" -version = "0.8.4" - [[deps.SimpleNonlinearSolve]] deps = ["ArrayInterface", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "PackageExtensionCompat", "PrecompileTools", "Reexport", "SciMLBase", "StaticArraysCore"] git-tree-sha1 = "4d53b83af904049c493daaf2a225bcae994a3c59" @@ -1948,24 +1441,6 @@ version = "0.1.20" [deps.SimpleNonlinearSolve.weakdeps] NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" -[[deps.SimplePartitions]] -deps = ["AbstractLattices", "DataStructures", "Permutations"] -git-tree-sha1 = "dcc02923a53f316ab97da8ef3136e80b4543dbf1" -uuid = "ec83eff0-a5b5-5643-ae32-5cbf6eedec9d" -version = "0.3.0" - -[[deps.SimplePolynomials]] -deps = ["Mods", "Multisets", "Polynomials", "Primes"] -git-tree-sha1 = "d537c31cf9995236166e3e9afc424a5a1c59ff9d" -uuid = "cc47b68c-3164-5771-a705-2bc0097375a0" -version = "0.2.14" - -[[deps.SimpleRandom]] -deps = ["Distributions", "LinearAlgebra", "Random"] -git-tree-sha1 = "3a6fb395e37afab81aeea85bae48a4db5cd7244a" -uuid = "a6525b86-64cd-54fa-8f65-62fc48bdc0e8" -version = "0.3.1" - [[deps.SimpleTraits]] deps = ["InteractiveUtils", "MacroTools"] git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" @@ -1977,12 +1452,6 @@ git-tree-sha1 = "58e6353e72cde29b90a69527e56df1b5c3d8c437" uuid = "ce78b400-467f-4804-87d8-8f486da07d0a" version = "1.1.0" -[[deps.Sixel]] -deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"] -git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6" -uuid = "45858cf5-a6b0-47a3-bbea-62219f50df47" -version = "0.1.3" - [[deps.SnoopPrecompile]] deps = ["Preferences"] git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c" @@ -2034,18 +1503,6 @@ weakdeps = ["ChainRulesCore"] [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" -[[deps.StableHashTraits]] -deps = ["Compat", "SHA", "Tables", "TupleTools"] -git-tree-sha1 = "19df33ca14f24a3ad2df9e89124bd5f5cc8467a2" -uuid = "c5dd0088-6c3f-4803-b00e-f31a60c170fa" -version = "1.0.1" - -[[deps.StackViews]] -deps = ["OffsetArrays"] -git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" -uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15" -version = "0.1.1" - [[deps.Static]] deps = ["IfElse"] git-tree-sha1 = "f295e0a1da4ca425659c57441bcb59abb035a4bc" @@ -2095,20 +1552,6 @@ git-tree-sha1 = "1d77abd07f617c4868c33d4f5b9e1dbb2643c9cf" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" version = "0.34.2" -[[deps.StatsFuns]] -deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "f625d686d5a88bcd2b15cd81f18f98186fdc0c9a" -uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.3.0" - - [deps.StatsFuns.extensions] - StatsFunsChainRulesCoreExt = "ChainRulesCore" - StatsFunsInverseFunctionsExt = "InverseFunctions" - - [deps.StatsFuns.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - [[deps.StrideArraysCore]] deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] git-tree-sha1 = "f02eb61eb5c97b48c153861c72fbbfdddc607e06" @@ -2121,12 +1564,6 @@ git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb" uuid = "69024149-9ee7-55f6-a4c4-859efe599b68" version = "0.3.7" -[[deps.StructArrays]] -deps = ["Adapt", "ConstructionBase", "DataAPI", "GPUArraysCore", "StaticArraysCore", "Tables"] -git-tree-sha1 = "0a3db38e4cce3c54fe7a71f831cd7b6194a54213" -uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" -version = "0.6.16" - [[deps.StructTypes]] deps = ["Dates", "UUIDs"] git-tree-sha1 = "ca4bccb03acf9faaf4137a9abc1881ed1841aa70" @@ -2194,12 +1631,6 @@ git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" version = "0.5.2" -[[deps.TiffImages]] -deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "UUIDs"] -git-tree-sha1 = "7fd97bd1c5b1ff53a291cbd351d1d3d6ff4da5a5" -uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" -version = "0.6.7" - [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] git-tree-sha1 = "f548a9e9c490030e545f72074a41edfd0e5bcdd7" @@ -2223,22 +1654,12 @@ git-tree-sha1 = "aadb748be58b492045b4f56166b5188aa63ce549" uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" version = "0.1.7" -[[deps.TriplotBase]] -git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" -uuid = "981d1d27-644d-49a2-9326-4793e63143c3" -version = "0.1.0" - [[deps.TruncatedStacktraces]] deps = ["InteractiveUtils", "MacroTools", "Preferences"] git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" -[[deps.TupleTools]] -git-tree-sha1 = "155515ed4c4236db30049ac1495e2969cc06be9d" -uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" -version = "1.4.3" - [[deps.URIs]] git-tree-sha1 = "b7a5e99f24892b6824a954199a45e9ffcc1c70f0" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" @@ -2322,12 +1743,6 @@ git-tree-sha1 = "4528479aa01ee1b3b4cd0e6faef0e04cf16466da" uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" version = "1.25.0+0" -[[deps.WoodburyMatrices]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "de67fa59e33ad156a590055375a30b23c40299d3" -uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6" -version = "0.5.5" - [[deps.WriteVTK]] deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] git-tree-sha1 = "7b46936613e41cfe1c6a5897d243ddcab8feabec" @@ -2549,12 +1964,6 @@ git-tree-sha1 = "3516a5630f741c9eecb3720b1ec9d8edc3ecc033" uuid = "1a1c6b14-54f6-533d-8383-74cd7377aa70" version = "3.1.1+0" -[[deps.isoband_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" -uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" -version = "0.2.3+0" - [[deps.libaom_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" @@ -2596,12 +2005,6 @@ git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" version = "1.6.38+0" -[[deps.libsixel_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] -git-tree-sha1 = "d4f63314c8aa1e48cd22aa0c17ed76cd1ae48c3c" -uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" -version = "1.10.3+0" - [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" diff --git a/docs/Project.toml b/docs/Project.toml index 92114eb028..876724cc7f 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,5 @@ [deps] BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" From 97eebbebc9820821e4e2104f40ad6bd55e6734d8 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 14:56:45 +0200 Subject: [PATCH 045/172] Add better error message if sdim don't match during reinit --- src/FEValues/FunctionValues.jl | 26 +++++++++++++++++++++++++- src/FEValues/GeometryValues.jl | 1 - src/FEValues/cell_values.jl | 9 ++++++--- src/FEValues/face_values.jl | 1 + src/Ferrite.jl | 4 ++-- test/test_cellvalues.jl | 2 ++ test/test_facevalues.jl | 2 ++ 7 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 8f334ccc33..48f2a4e0e3 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -1,4 +1,11 @@ -# Helpers to get the correct types for FunctionValues for the given function and, if needed, geometric interpolations. +################################################################# +# Note on dimensions: # +# sdim = spatial dimension (dimension of the grid nodes) # +# rdim = reference dimension (dimension in isoparametric space) # +# vdim = vector dimension (dimension of the field) # +################################################################# + +# Helpers to get the correct types for FunctionValues for the given function and, if needed, geometric interpolations. struct SInterpolationDims{rdim,sdim} end struct VInterpolationDims{rdim,sdim,vdim} end function InterpolationDims(::ScalarInterpolation, ip_geo::VectorizedInterpolation{sdim}) where sdim @@ -74,6 +81,23 @@ get_function_interpolation(funvals::FunctionValues) = funvals.ip shape_value_type(funvals::FunctionValues) = eltype(funvals.N_x) shape_gradient_type(funvals::FunctionValues) = eltype(funvals.dNdx) + +# Checks that the user provides the right dimension of coordinates to reinit! methods to ensure good error messages if not +sdim_from_gradtype(::Type{<:AbstractTensor{<:Any,sdim}}) where sdim = sdim +sdim_from_gradtype(::Type{<:SVector{sdim}}) where sdim = sdim +sdim_from_gradtype(::Type{<:SMatrix{<:Any,sdim}}) where sdim = sdim + +# For performance, these must be fully inferrable for the compiler. +# args: valname (:CellValues or :FaceValues), shape_gradient_type, eltype(x) +function check_reinit_sdim_consistency(valname, gradtype, ::Type{<:Vec{sdim}}) where {sdim} + check_reinit_sdim_consistency(valname, Val(sdim_from_gradtype(gradtype)), Val(sdim)) +end +check_reinit_sdim_consistency(_, ::Val{sdim}, ::Val{sdim}) where sdim = nothing +function check_reinit_sdim_consistency(valname, ::Val{sdim_val}, ::Val{sdim_x}) where {sdim_val, sdim_x} + error("""The $valname and coordinates have different spatial dimensions, $sdim_val and $sdim_x. + Could it be that you forgot to vectorize the geometric interpolation when constructing the $valname?""") +end + # Mapping types struct IdentityMapping end struct CovariantPiolaMapping end diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryValues.jl index ec47cc6a29..6df8cb639e 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryValues.jl @@ -82,7 +82,6 @@ end @propagate_inbounds calculate_mapping(geovals::GeometryValues, args...) = calculate_mapping(RequiresHessian(geovals), geovals, args...) @inline function calculate_mapping(::RequiresHessian{false}, geo_values::GeometryValues, q_point, x) - #fecv_J = zero(Tensors.getreturntype(⊗, eltype(x), eltype(geo_values.dMdξ))) fecv_J = zero(otimes_returntype(eltype(x), eltype(geo_values.dMdξ))) @inbounds for j in 1:getngeobasefunctions(geo_values) #fecv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] diff --git a/src/FEValues/cell_values.jl b/src/FEValues/cell_values.jl index 2bf29cc797..8c22470612 100644 --- a/src/FEValues/cell_values.jl +++ b/src/FEValues/cell_values.jl @@ -81,6 +81,7 @@ end getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) + check_reinit_sdim_consistency(:CellValues, shape_gradient_type(cv), eltype(x)) geo_values = cv.geo_values fun_values = cv.fun_values n_geom_basefuncs = getngeobasefunctions(geo_values) @@ -98,12 +99,14 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) end function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) - rdim = getdim(cv.geo_values.ip) + ip_geo = get_geometric_interpolation(cv) + ip_fun = get_function_interpolation(cv) + rdim = getdim(ip_geo) vdim = isa(shape_value(cv, 1, 1), Vec) ? length(shape_value(cv, 1, 1)) : 0 sdim = length(shape_gradient(cv, 1, 1)) ÷ length(shape_value(cv, 1, 1)) vstr = vdim==0 ? "scalar" : "vdim=$vdim" print(io, "CellValues(", vstr, ", rdim=$rdim, and sdim=$sdim): ") print(io, getnquadpoints(cv), " quadrature points") - print(io, "\n Function interpolation: "); show(io, d, cv.fun_values.ip) - print(io, "\nGeometric interpolation: "); show(io, d, cv.geo_values.ip^sdim) + print(io, "\n Function interpolation: "); show(io, d, ip_fun) + print(io, "\nGeometric interpolation: "); show(io, d, ip_geo^sdim) end \ No newline at end of file diff --git a/src/FEValues/face_values.jl b/src/FEValues/face_values.jl index 9aea41af06..ea985af3a7 100644 --- a/src/FEValues/face_values.jl +++ b/src/FEValues/face_values.jl @@ -108,6 +108,7 @@ function checkface(fv::FaceValues, face::Int) end function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, cell=nothing) where {dim, T} + check_reinit_sdim_consistency(:FaceValues, shape_gradient_type(fv), eltype(x)) @boundscheck checkface(fv, face_nr) n_geom_basefuncs = getngeobasefunctions(fv) length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 6965d631ab..419229c993 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -84,8 +84,8 @@ include("Quadrature/quadrature.jl") # FEValues include("FEValues/GeometryValues.jl") include("FEValues/FunctionValues.jl") -include("FEValues/CellValues.jl") -include("FEValues/FaceValues.jl") +include("FEValues/cell_values.jl") +include("FEValues/face_values.jl") include("PointEval/point_values.jl") include("FEValues/common_values.jl") include("FEValues/face_integrals.jl") diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 9428684f9b..9c59c68e16 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -329,7 +329,9 @@ end cv_quad = CellValues(QuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()^2) cv_wedge = CellValues(QuadratureRule{RefPrism}(2), Lagrange{RefPrism,2}()) show(stdout, MIME"text/plain"(), cv_quad) + println(stdout) show(stdout, MIME"text/plain"(), cv_wedge) + println(stdout) end end # of testset diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index 5510ccc4a6..f4bb30301f 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -131,9 +131,11 @@ end # Just smoke test to make sure show doesn't error. fv = FaceValues(FaceQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()) show(stdout, MIME"text/plain"(), fv) + println(stdout) fv.qr.face_rules[1] = deepcopy(fv.qr.face_rules[1]) push!(Ferrite.getweights(fv.qr.face_rules[1]), 1) show(stdout, MIME"text/plain"(), fv) + println(stdout) end end # of testset From 0cee93618b951c988a20eae56e804bef3078cf2f Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 15:05:15 +0200 Subject: [PATCH 046/172] Docfixes --- src/FEValues/common_values.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index dc198f3600..b1c9b5d537 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -30,11 +30,12 @@ end end """ - reinit!(cv::CellValues, x::Vector) - reinit!(bv::FaceValues, x::Vector, face::Int) + reinit!(cv::CellValues, x::Vector, cell::Union{AbstractCell,Nothing}=nothing) + reinit!(bv::FaceValues, x::Vector, face::Int, cell::Union{AbstractCell,Nothing}=nothing) Update the `CellValues`/`FaceValues` object for a cell or face with 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. """ reinit! @@ -59,7 +60,7 @@ finite element cell or face as ``\\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`` """ -#@propagate_inbounds getdetJdV(bv::FaceValues, q_point::Int) = bv.detJdV[q_point, bv.current_face[]] +function getdetJdV end """ shape_value(fe_v::AbstractValues, q_point::Int, base_function::Int) @@ -68,7 +69,6 @@ Return the value of shape function `base_function` evaluated in quadrature point `q_point`. """ function shape_value end -#@propagate_inbounds shape_value(bv::FaceValues, q_point::Int, base_func::Int) = bv.N[base_func, q_point, bv.current_face[]] """ geometric_value(fe_v::AbstractValues, q_point, base_function::Int) From 90ccab1db2bd2d2e2bb93303ea07892bc606a5b8 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 15:11:26 +0200 Subject: [PATCH 047/172] Show check for triangle mesh for the heat equation --- docs/src/literate-tutorials/heat_equation_triangle.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl index fee7ac52b6..bf312ef04a 100644 --- a/docs/src/literate-tutorials/heat_equation_triangle.jl +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -213,6 +213,8 @@ end @show norm(u)/length(u) +# ### Postprocessing the total flux + function calculate_flux_lag(dh, dΩ, ip, a) qr = FaceQuadratureRule{RefTriangle}(2) fv = FaceValues(qr, ip, Lagrange{RefTriangle,1}()) From 3f6cd9b6827df831c06de083744089906e1c0146 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 15:51:48 +0200 Subject: [PATCH 048/172] Remove benchmark script --- cv_benchmark.jl | 65 ------------------------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 cv_benchmark.jl diff --git a/cv_benchmark.jl b/cv_benchmark.jl deleted file mode 100644 index db5870c996..0000000000 --- a/cv_benchmark.jl +++ /dev/null @@ -1,65 +0,0 @@ -using Ferrite, BenchmarkTools, StaticArrays - -function get_values(CellType, ::Val{dim}, q_order=2) where dim - grid = generate_grid(CellType, ntuple(_->2, dim)) - ip = Ferrite.default_interpolation(getcelltype(grid)) - RefShape = Ferrite.getrefshape(ip) - qr = QuadratureRule{RefShape}(q_order) - cv_u = CellValues(qr, ip^dim, ip) - cv_p = CellValues(qr, ip, ip) - return cv_u, cv_p, getcoordinates(grid, 1) -end - -function reinit_masterfix!(cv::Ferrite.OldCellValues{<:Any, <:Any, <:Tensor, <:Tensor, T, Vec{dim,T}}, x::AbstractVector{Vec{dim,T}}) where {dim, T} - n_geom_basefuncs = Ferrite.getngeobasefunctions(cv) - n_func_basefuncs = Ferrite.getnbasefunctions(cv) - length(x) == n_geom_basefuncs || Ferrite.throw_incompatible_coord_length(length(x), n_geom_basefuncs) - - @inbounds for (i, w) in pairs(getweights(cv.qr)) - fecv_J = zero(Tensor{2,dim,T}) - for j in 1:n_geom_basefuncs - fecv_J += x[j] ⊗ cv.dMdξ[j, i] - end - detJ = det(fecv_J) - detJ > 0.0 || Ferrite.throw_detJ_not_pos(detJ) - cv.detJdV[i] = detJ * w - Jinv = inv(fecv_J) - for j in 1:n_func_basefuncs - # cv.dNdx[j, i] = cv.dNdξ[j, i] ⋅ Jinv - cv.dNdx[j, i] = Ferrite.dothelper(cv.dNdξ[j, i], Jinv) - end - end -end - -#for (CT, dim) in ((Triangle,2), (QuadraticTriangle,2), (Hexahedron,3), (Tetrahedron,3)) -for (CT, dim) in ((Triangle,2),) - # 2 and 4 fields in 2D - cv_u, cv_p, x = get_values(CT, Val(dim), 2) - ocv_u = Ferrite.OldCellValues(cv_u) - ocv_p = Ferrite.OldCellValues(cv_p) - - print("Scalar : $CT in $(dim)D"); println() - print("1 PR : "); @btime reinit!($cv_p, $x); - print("1 master : "); @btime reinit!($ocv_p, $x); - print("1 master (fix): "); @btime reinit_masterfix!($ocv_p, $x); - - print("Vector : $CT in $(dim)D"); println() - print("1 PR : "); @btime reinit!($cv_u, $x); - print("1 master : "); @btime reinit!($ocv_u, $x); - print("1 master (fix): "); @btime reinit_masterfix!($ocv_u, $x); - # =# - #= - println() - print("2 CellValues : "); @btime reinit2!($cv_u, $cv_p, $x) - print("2 MultiCellValues : "); @btime reinit!($mcv_pu, $x) - print("2 MultiCellValues2 : "); @btime reinit!($mcv2_pu, $x) - println() - # =# - #= - print("4 CellValues : "); @btime reinit4!($cv_u, $cv_p, $cv_u2, $cv_p2, $x) - print("4 MultiValues : "); @btime reinit!($cv4, $x) - print("4 Tuple{CV} : "); @btime reinit_multiple!($x, $cv_u, $cv_p, $cv_u2, $cv_p2) - print("4 ValuesGroup : "); @btime reinit!($cvg4, $x); - println() - # =# -end \ No newline at end of file From 3aad963b0726571052c10053e91415bdd207aa0a Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 8 Oct 2023 18:39:24 +0200 Subject: [PATCH 049/172] Generalize example to try with 2nd order Lagrange --- .../literate-tutorials/heat_equation_rt.jl | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl index 8ff5f2d75c..4679cfa4f4 100644 --- a/docs/src/literate-tutorials/heat_equation_rt.jl +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -45,6 +45,7 @@ using Ferrite, SparseArrays # We start by generating a simple grid with 20x20 quadrilateral elements # using `generate_grid`. The generator defaults to the unit square, # so we don't need to specify the corners of the domain. +#grid = generate_grid(QuadraticTriangle, (20, 20)); grid = generate_grid(Triangle, (20, 20)); # ### Trial and test functions @@ -55,8 +56,8 @@ grid = generate_grid(Triangle, (20, 20)); # based on the two-dimensional reference quadrilateral. We also define a quadrature rule based on # the same reference element. We combine the interpolation and the quadrature rule # to a `CellValues` object. -ip_geo = Lagrange{RefTriangle, 1}() -ipu = Lagrange{RefTriangle, 1}() +ip_geo = Ferrite.default_interpolation(getcelltype(grid)) +ipu = Lagrange{RefTriangle, 1}() # Why does it "explode" for 2nd order ipu? ipq = RaviartThomas{2,RefTriangle,1}() qr = QuadratureRule{RefTriangle}(2) cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) @@ -150,12 +151,12 @@ function assemble_element!(Ke::Matrix, fe::Vector, cv::NamedTuple, dr::NamedTupl δu = shape_value(cvu, q_point, iu) ∇δu = shape_gradient(cvu, q_point, iu) ## Add contribution to fe - fe[Iu] += δu * dΩ + fe[Iu] -= δu * dΩ ## Loop over trial shape functions for (jq, Jq) in pairs(drq) q = shape_value(cvq, q_point, jq) ## Add contribution to Ke - Ke[Iu, Jq] -= (∇δu ⋅ q) * dΩ + Ke[Iu, Jq] += (∇δu ⋅ q) * dΩ end end for (iq, Iq) in pairs(drq) @@ -192,6 +193,7 @@ end # connection between the weak form and the Ferrite implementation. function assemble_global(cellvalues, K::SparseMatrixCSC, dh::DofHandler) + grid = dh.grid ## Allocate the element stiffness matrix and element force vector dofranges = (u = dof_range(dh, :u), q = dof_range(dh, :q)) ncelldofs = ndofs_per_cell(dh) @@ -201,12 +203,12 @@ function assemble_global(cellvalues, K::SparseMatrixCSC, dh::DofHandler) f = zeros(ndofs(dh)) ## Create an assembler assembler = start_assemble(K, f) - x = copy(getcoordinates(dh.grid, 1)) + x = copy(getcoordinates(grid, 1)) dofs = copy(celldofs(dh, 1)) ## Loop over all cels - for cellnr in 1:getncells(dh.grid) + for cellnr in 1:getncells(grid) ## Reinitialize cellvalues for this cell - cell = getcells(dh.grid, cellnr) + cell = getcells(grid, cellnr) getcoordinates!(x, grid, cell) celldofs!(dofs, dh, cellnr) reinit!(cellvalues[:u], x, cell) @@ -251,9 +253,11 @@ end # ## Postprocess the total flux function calculate_flux(dh, dΩ, ip, a) - qr = FaceQuadratureRule{RefTriangle}(4) - fv = FaceValues(qr, ip, Lagrange{RefTriangle,1}()) grid = dh.grid + qr = FaceQuadratureRule{RefTriangle}(4) + ip_geo = Ferrite.default_interpolation(getcelltype(grid)) + fv = FaceValues(qr, ip, ip_geo) + dofrange = dof_range(dh, :q) flux = 0.0 dofs = celldofs(dh, 1) @@ -276,9 +280,10 @@ function calculate_flux(dh, dΩ, ip, a) end function calculate_flux_lag(dh, dΩ, ip, a) - qr = FaceQuadratureRule{RefTriangle}(4) - fv = FaceValues(qr, ip, Lagrange{RefTriangle,1}()) grid = dh.grid + qr = FaceQuadratureRule{RefTriangle}(4) + ip_geo = Ferrite.default_interpolation(getcelltype(grid)) + fv = FaceValues(qr, ip, ip_geo) dofrange = dof_range(dh, :u) flux = 0.0 dofs = celldofs(dh, 1) @@ -303,6 +308,25 @@ end flux = calculate_flux(dh, ∂Ω, ipq, u) flux_lag = calculate_flux_lag(dh, ∂Ω, ipu, u) @show flux, flux_lag + + +function get_Ke(dh, cellvalues; cellnr=1) + dofranges = (u = dof_range(dh, :u), q = dof_range(dh, :q)) + ncelldofs = ndofs_per_cell(dh) + Ke = zeros(ncelldofs, ncelldofs) + fe = zeros(ncelldofs) + x = getcoordinates(grid, cellnr) + cell = getcells(grid, cellnr) + reinit!(cellvalues[:u], x, cell) + reinit!(cellvalues[:q], x, cell) + + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues, dofranges) + return Ke +end #md # ## [Plain program](@id heat_equation-plain-program) #md # #md # Here follows a version of the program without any comments. From 0668d460b4d1f846e9f83aff73fdad94a15efcf4 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 16:25:39 +0200 Subject: [PATCH 050/172] Rename GeometryValues -> GeometryMapping --- .../{GeometryValues.jl => GeometryMapping.jl} | 42 +++++++++---------- src/FEValues/cell_values.jl | 18 ++++---- src/FEValues/face_values.jl | 26 ++++++------ src/Ferrite.jl | 2 +- src/PointEval/point_values.jl | 2 +- test/test_cellvalues.jl | 6 +-- test/test_facevalues.jl | 4 +- 7 files changed, 50 insertions(+), 50 deletions(-) rename src/FEValues/{GeometryValues.jl => GeometryMapping.jl} (76%) diff --git a/src/FEValues/GeometryValues.jl b/src/FEValues/GeometryMapping.jl similarity index 76% rename from src/FEValues/GeometryValues.jl rename to src/FEValues/GeometryMapping.jl index 6df8cb639e..4038f1f460 100644 --- a/src/FEValues/GeometryValues.jl +++ b/src/FEValues/GeometryMapping.jl @@ -19,14 +19,14 @@ function RequiresHessian(ip_fun::Interpolation, ip_geo::Interpolation) RequiresHessian(requires_hessian(get_mapping_type(ip_fun))) end -struct GeometryValues{IP, M_t, dMdξ_t, d2Mdξ2_t} +struct GeometryMapping{IP, M_t, dMdξ_t, d2Mdξ2_t} ip::IP # ::Interpolation Geometric interpolation M::M_t # ::AbstractVector{<:Number} Values of geometric shape functions dMdξ::dMdξ_t # ::AbstractVector{<:Vec} Gradients of geometric shape functions in ref-domain d2Mdξ2::d2Mdξ2_t # ::AbstractVector{<:Tensor{2}} Hessians of geometric shape functions in ref-domain # ::Nothing When hessians are not required end -function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, ::RequiresHessian{RH}) where {T,RH} +function GeometryMapping(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, ::RequiresHessian{RH}) where {T,RH} n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) VT = Vec{getdim(ip),T} @@ -47,18 +47,18 @@ function GeometryValues(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, end end end - return GeometryValues(ip, M, dMdξ, dM2dξ2) + return GeometryMapping(ip, M, dMdξ, dM2dξ2) end -function Base.copy(v::GeometryValues) +function Base.copy(v::GeometryMapping) d2Mdξ2_copy = v.d2Mdξ2 === nothing ? nothing : copy(v.d2Mdξ2) - return GeometryValues(copy(v.ip), copy(v.M), copy(v.dMdξ), d2Mdξ2_copy) + return GeometryMapping(copy(v.ip), copy(v.M), copy(v.dMdξ), d2Mdξ2_copy) end -getngeobasefunctions(geovals::GeometryValues) = size(geovals.M, 1) -@propagate_inbounds geometric_value(geovals::GeometryValues, q_point::Int, base_func::Int) = geovals.M[base_func, q_point] -get_geometric_interpolation(geovals::GeometryValues) = geovals.ip +getngeobasefunctions(geo_mapping::GeometryMapping) = size(geo_mapping.M, 1) +@propagate_inbounds geometric_value(geo_mapping::GeometryMapping, q_point::Int, base_func::Int) = geo_mapping.M[base_func, q_point] +get_geometric_interpolation(geo_mapping::GeometryMapping) = geo_mapping.ip -RequiresHessian(geovals::GeometryValues) = RequiresHessian(geovals.d2Mdξ2 !== nothing) +RequiresHessian(geo_mapping::GeometryMapping) = RequiresHessian(geo_mapping.d2Mdξ2 !== nothing) # Hot-fixes to support embedded elements before MixedTensors are available # See https://github.com/Ferrite-FEM/Tensors.jl/pull/188 @@ -79,23 +79,23 @@ function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(d2Mdξ2) return Tensor{3,dim,promote_type(Tx,TM)} end -@propagate_inbounds calculate_mapping(geovals::GeometryValues, args...) = calculate_mapping(RequiresHessian(geovals), geovals, args...) +@propagate_inbounds calculate_mapping(geo_mapping::GeometryMapping, args...) = calculate_mapping(RequiresHessian(geo_mapping), geo_mapping, args...) -@inline function calculate_mapping(::RequiresHessian{false}, geo_values::GeometryValues, q_point, x) - fecv_J = zero(otimes_returntype(eltype(x), eltype(geo_values.dMdξ))) - @inbounds for j in 1:getngeobasefunctions(geo_values) - #fecv_J += x[j] ⊗ geo_values.dMdξ[j, q_point] - fecv_J += otimes_helper(x[j], geo_values.dMdξ[j, q_point]) +@inline function calculate_mapping(::RequiresHessian{false}, geo_mapping::GeometryMapping, q_point, x) + 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] + fecv_J += otimes_helper(x[j], geo_mapping.dMdξ[j, q_point]) end return MappingValues(fecv_J, nothing) end -@inline function calculate_mapping(::RequiresHessian{true}, geo_values::GeometryValues, q_point, x) - J = zero(otimes_returntype(eltype(x), eltype(geo_values.dMdξ))) - H = zero(otimes_returntype(eltype(x), eltype(geo_values.d2Mdξ2))) - @inbounds for j in 1:getngeobasefunctions(geo_values) - J += x[j] ⊗ geo_values.dMdξ[j, q_point] - H += x[j] ⊗ geo_values.d2Mdξ2[j, q_point] +@inline function calculate_mapping(::RequiresHessian{true}, geo_mapping::GeometryMapping, q_point, x) + J = zero(otimes_returntype(eltype(x), eltype(geo_mapping.dMdξ))) + H = zero(otimes_returntype(eltype(x), eltype(geo_mapping.d2Mdξ2))) + @inbounds for j in 1:getngeobasefunctions(geo_mapping) + J += x[j] ⊗ geo_mapping.dMdξ[j, q_point] + H += x[j] ⊗ geo_mapping.d2Mdξ2[j, q_point] end return MappingValues(J, H) end diff --git a/src/FEValues/cell_values.jl b/src/FEValues/cell_values.jl index 8c22470612..f57c05d145 100644 --- a/src/FEValues/cell_values.jl +++ b/src/FEValues/cell_values.jl @@ -37,15 +37,15 @@ end struct CellValues{FV, GV, QR, detT<:AbstractVector} <: AbstractCellValues fun_values::FV # FunctionValues - geo_values::GV # GeometryValues + geo_mapping::GV # GeometryMapping qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} end function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T - geo_values = GeometryValues(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) + geo_mapping = GeometryMapping(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) fun_values = FunctionValues(T, ip_fun, qr, ip_geo) detJdV = fill(T(NaN), length(getweights(qr))) - return CellValues(fun_values, geo_values, qr, detJdV) + return CellValues(fun_values, geo_mapping, qr, detJdV) end CellValues(qr::QuadratureRule, ip::Interpolation, args...) = CellValues(Float64, qr, ip, args...) @@ -54,13 +54,13 @@ function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolatio end function Base.copy(cv::CellValues) - return CellValues(copy(cv.fun_values), copy(cv.geo_values), copy(cv.qr), copy(cv.detJdV)) + return CellValues(copy(cv.fun_values), copy(cv.geo_mapping), copy(cv.qr), copy(cv.detJdV)) end # Access geometry values for op = (:getngeobasefunctions, :geometric_value) eval(quote - @propagate_inbounds $op(cv::CellValues, args...) = $op(cv.geo_values, args...) + @propagate_inbounds $op(cv::CellValues, args...) = $op(cv.geo_mapping, args...) end) end getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] @@ -68,7 +68,7 @@ getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] # Accessors for function values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) get_function_interpolation(cv::CellValues) = get_function_interpolation(cv.fun_values) -get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_values) +get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_mapping) shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) @@ -82,14 +82,14 @@ getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) check_reinit_sdim_consistency(:CellValues, shape_gradient_type(cv), eltype(x)) - geo_values = cv.geo_values + geo_mapping = cv.geo_mapping fun_values = cv.fun_values - n_geom_basefuncs = getngeobasefunctions(geo_values) + n_geom_basefuncs = getngeobasefunctions(geo_mapping) if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs throw_incompatible_coord_length(length(x), n_geom_basefuncs) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) - mapping = calculate_mapping(geo_values, q_point, x) + mapping = calculate_mapping(geo_mapping, q_point, x) detJ = calculate_detJ(getjacobian(mapping)) detJ > 0.0 || throw_detJ_not_pos(detJ) @inbounds cv.detJdV[q_point] = detJ*w diff --git a/src/FEValues/face_values.jl b/src/FEValues/face_values.jl index ea985af3a7..a2ae02c175 100644 --- a/src/FEValues/face_values.jl +++ b/src/FEValues/face_values.jl @@ -33,7 +33,7 @@ FaceValues struct FaceValues{FV, GV, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GV<:AbstractVector{GV}} <: AbstractFaceValues fun_values::V_FV # AbstractVector{FunctionValues} - geo_values::V_GV # AbstractVector{GeometryValues} + geo_mapping::V_GV # AbstractVector{GeometryMapping} qr::QR # FaceQuadratureRule detJdV::detT # AbstractVector{<:Number} normals::nT # AbstractVector{<:Vec} @@ -41,12 +41,12 @@ struct FaceValues{FV, GV, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GV<:Abstract end function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun)) where {T,sdim} - geo_values = [GeometryValues(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) for qr in fqr.face_rules] + geo_mapping = [GeometryMapping(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) for qr in fqr.face_rules] fun_values = [FunctionValues(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) detJdV = fill(T(NaN), max_nquadpoints) normals = fill(zero(Vec{sdim,T})*T(NaN), max_nquadpoints) - return FaceValues(fun_values, geo_values, fqr, detJdV, normals, ScalarWrapper(1)) + return FaceValues(fun_values, geo_mapping, fqr, detJdV, normals, ScalarWrapper(1)) end FaceValues(qr::FaceQuadratureRule, ip::Interpolation, args...) = FaceValues(Float64, qr, ip, args...) @@ -56,11 +56,11 @@ end function Base.copy(fv::FaceValues) fun_values = map(copy, fv.fun_values) - geo_values = map(copy, fv.geo_values) - return FaceValues(fun_values, geo_values, copy(fv.qr), copy(fv.detJdV), copy(fv.normals), copy(fv.current_face)) + geo_mapping = map(copy, fv.geo_mapping) + return FaceValues(fun_values, geo_mapping, copy(fv.qr), copy(fv.detJdV), copy(fv.normals), copy(fv.current_face)) end -getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_values(fv)) +getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_mapping(fv)) getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) getnquadpoints(fv::FaceValues) = getnquadpoints(fv.qr, getcurrentface(fv)) getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] @@ -68,12 +68,12 @@ getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] shape_value_type(fv::FaceValues) = shape_value_type(get_fun_values(fv)) shape_gradient_type(fv::FaceValues) = shape_gradient_type(get_fun_values(fv)) get_function_interpolation(fv::FaceValues) = get_function_interpolation(get_fun_values(fv)) -get_geometric_interpolation(fv::FaceValues) = get_geometric_interpolation(get_geo_values(fv)) +get_geometric_interpolation(fv::FaceValues) = get_geometric_interpolation(get_geo_mapping(fv)) -get_geo_values(fv::FaceValues) = @inbounds fv.geo_values[getcurrentface(fv)] +get_geo_mapping(fv::FaceValues) = @inbounds fv.geo_mapping[getcurrentface(fv)] for op = (:getngeobasefunctions, :geometric_value) eval(quote - @propagate_inbounds $op(fv::FaceValues, args...) = $op(get_geo_values(fv), args...) + @propagate_inbounds $op(fv::FaceValues, args...) = $op(get_geo_mapping(fv), args...) end) end @@ -100,7 +100,7 @@ Return the normal at the quadrature point `qp` for the active face of the """ getnormal(fv::FaceValues, qp::Int) = fv.normals[qp] -nfaces(fv::FaceValues) = length(fv.geo_values) +nfaces(fv::FaceValues) = length(fv.geo_mapping) function checkface(fv::FaceValues, face::Int) 0 < face <= nfaces(fv) || error("Face index out of range.") @@ -115,12 +115,12 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, ce fv.current_face[] = face_nr - geo_values = get_geo_values(fv) + geo_mapping = get_geo_mapping(fv) fun_values = get_fun_values(fv) @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) - mapping = calculate_mapping(geo_values, q_point, x) + mapping = calculate_mapping(geo_mapping, q_point, x) J = getjacobian(mapping) - weight_norm = weighted_normal(J, getrefshape(geo_values.ip), face_nr) + weight_norm = weighted_normal(J, getrefshape(geo_mapping.ip), face_nr) detJ = norm(weight_norm) detJ > 0.0 || throw_detJ_not_pos(detJ) @inbounds fv.detJdV[q_point] = detJ*w diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 419229c993..d30de2ba7e 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -82,7 +82,7 @@ include("interpolations.jl") include("Quadrature/quadrature.jl") # FEValues -include("FEValues/GeometryValues.jl") +include("FEValues/GeometryMapping.jl") include("FEValues/FunctionValues.jl") include("FEValues/cell_values.jl") include("FEValues/face_values.jl") diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 239db54ea5..083e482990 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -24,7 +24,7 @@ struct PointValues{CV} <: AbstractValues PointValues{CV}(cv::CV) where {CV} = new{CV}(cv) end -PointValues(cv::CellValues) = PointValues(eltype(shape_value(cv,1,1)), cv.fun_values.ip, cv.geo_values.ip) +PointValues(cv::CellValues) = PointValues(eltype(shape_value(cv,1,1)), cv.fun_values.ip, cv.geo_mapping.ip) function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip)) return PointValues(Float64, ip, ipg) end diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 9c59c68e16..b6c4b200d0 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -92,8 +92,8 @@ for (scalar_interpol, quad_rule) in ( cvc = copy(cv) @test typeof(cv) == typeof(cvc) - # Test that all mutable types in FunctionValues and GeometryValues have been copied - for key in (:fun_values, :geo_values) + # Test that all mutable types in FunctionValues and GeometryMapping have been copied + for key in (:fun_values, :geo_mapping) val = getfield(cv, key) valc = getfield(cvc, key) for fname in fieldnames(typeof(val)) @@ -294,7 +294,7 @@ end @testset "CellValues constructor entry points" begin qr = QuadratureRule{RefTriangle}(1) - _get_geo_ip(cv::CellValues) = cv.geo_values.ip + _get_geo_ip(cv::CellValues) = cv.geo_mapping.ip for fun_ip in (Lagrange{RefTriangle, 1}(), Lagrange{RefTriangle, 2}()^2) value_type(T) = fun_ip isa ScalarInterpolation ? T : Vec{2, T} grad_type(T) = fun_ip isa ScalarInterpolation ? Vec{2, T} : Tensor{2, 2, T, 4} diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index f4bb30301f..6eb875e500 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -97,8 +97,8 @@ for (scalar_interpol, quad_rule) in ( fvc = copy(fv) @test typeof(fv) == typeof(fvc) - # Test that all mutable types in FunctionValues and GeometryValues have been copied - for key in (:fun_values, :geo_values) + # Test that all mutable types in FunctionValues and GeometryMapping have been copied + for key in (:fun_values, :geo_mapping) for i in eachindex(getfield(fv, key)) val = getfield(fv, key)[i] valc = getfield(fvc, key)[i] From cf5d39882106eea1e730864e2de34369face409d Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 16:52:38 +0200 Subject: [PATCH 051/172] Correct i to alpha in mapping documentation --- docs/src/assets/fe_mapping.svg | 2470 +++++++++++++++++--------------- docs/src/devdocs/mapping.md | 4 +- 2 files changed, 1349 insertions(+), 1125 deletions(-) diff --git a/docs/src/assets/fe_mapping.svg b/docs/src/assets/fe_mapping.svg index 39fd0e70fb..2933e1b945 100644 --- a/docs/src/assets/fe_mapping.svg +++ b/docs/src/assets/fe_mapping.svg @@ -20,16 +20,16 @@ bordercolor="#666666" borderopacity="1.0" inkscape:pageshadow="2" - inkscape:pageopacity="0.0" + inkscape:pageopacity="1" inkscape:pagecheckerboard="0" inkscape:document-units="mm" showgrid="false" inkscape:snap-object-midpoints="true" - inkscape:zoom="0.69397447" - inkscape:cx="322.05796" - inkscape:cy="271.62383" - inkscape:window-width="1920" - inkscape:window-height="1017" + inkscape:zoom="1.3879489" + inkscape:cx="475.16157" + inkscape:cy="179.04117" + inkscape:window-width="2560" + inkscape:window-height="1377" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" @@ -96,26 +96,233 @@ d="M 5.77,0 -2.88,5 V -5 Z" id="path2269" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + transform="translate(-5.21261,-14.7368)" + style="display:inline"> + x="29.166241" + y="45.750965" /> + id="g11698"> + id="id-15f19f0d-41e7-4032-b7e3-782b12434f56"> - - - - - - - - - - - - - - - - - - + id="id-6af54135-8bcd-4717-8a49-46c60c244028"> + id="id-b7a76413-959c-426d-8a58-c79a4916b1cd"> + id="id-3c273db9-c685-48f3-a7ca-b970ee326867" /> + id="id-440c0518-bed2-4f8b-a8cd-1873505ecfd5"> + id="id-41947899-61f8-4a64-9a9c-0f7fe1e6f761" /> + id="id-e1574a1e-a987-430d-8d9a-dad442b747be"> + id="id-aba619b8-09d6-4d28-916f-ac0957ddf522" /> + id="id-83e6337b-4488-47b1-8b4c-c294e4da8758"> + id="id-71479173-f99d-4ce1-94a2-936d428acd66" /> + id="id-f54d63ac-f44e-4e0f-9864-5210040a0b37"> + id="id-ef97d618-0e56-41ed-a654-977546b94614" /> + id="id-d1dbac30-55b6-494e-9c24-8d93ae6dcb6c"> + id="id-1a050a46-1993-4393-be46-fdb05672a18a" /> + id="id-98b0dd7a-2314-4a20-bdb2-e641ca95df49"> + id="id-7cfb9f67-bb8c-4549-bb25-a429341bbb03" /> + id="id-10fcf22d-3c97-4af9-8241-fa73430a9ee8"> + id="id-4f5968df-f426-463b-95f1-b42c2dc4c6fc" /> + id="id-fb7fb267-ee85-4dd0-8f98-34784de9854b"> + id="id-3fb57a85-22ad-4818-9f0b-0922d601af97" /> + id="id-a47adeea-c04a-4918-ad8d-dfd9f876a972"> + id="id-6062163f-6084-4f38-9b51-9e34cd5d8c64" /> + id="id-ed5139f4-a469-417f-bd1f-ff48504777fd"> + id="id-bb15efae-2920-40e7-9dbf-5c726a3a5de6" /> + id="id-d79faa9b-1e80-4613-8454-b5ecf8fa03de"> - - - - - - + id="id-a53a05fb-d456-444b-a0a0-c86f2c8fd25b" /> + id="id-20bf41f0-01ee-45a1-8fe1-be5e5e59255e"> - - - - - - - - - - - - + id="id-4e022c0b-f4af-4f6a-9582-a15ea3cb089a" /> - - - - - - - - - - + id="id-a4e28b5f-9333-4eda-8be3-144c781fc96e" + transform="translate(-149.056,-127.734)"> + id="id-9eb5e9b5-4459-4641-bf9f-217a6be3b048"> + transform="translate(148.712,134.765)" + id="g11634"> + id="id-08dff843-fb55-4f0b-9e12-6804f75762f8" /> + transform="translate(156.045,134.765)" + id="g11637"> + id="id-bdba2f3b-9884-45c8-843e-31c675b4d9ef" /> + transform="translate(160.473,134.765)" + id="g11640"> + id="id-8f1e324b-2608-4159-ba0f-22aa9f55f485" /> + transform="translate(163.517,134.765)" + id="g11643"> + id="id-d84bade3-aae7-49a2-a397-d19f3333a9cf" /> + transform="translate(167.945,134.765)" + id="g11646"> + id="id-ff6cad7a-a35b-4418-8f6e-bdef7088cd4e" /> + transform="translate(171.847,134.765)" + id="g11649"> + id="id-65bdc089-6734-4ddf-ab23-4bdef0ea82a5" /> + transform="translate(176.275,134.765)" + id="g11652"> + id="id-e6384bd2-dfd9-4b00-9503-772cc3cbd907" /> + transform="translate(181.81,134.765)" + id="g11655"> + id="id-4c4e9262-42fa-412f-adc1-b2d0871d86d2" /> + transform="translate(186.237,134.765)" + id="g11658"> + id="id-94283dae-c759-4b79-b6cf-72a8a8cf2202" /> + id="id-6f93f427-b2f7-4db5-bf43-e3a0ff39c096"> + transform="translate(193.982,134.765)" + id="g11662"> + id="id-30f5076d-7138-4a57-b45b-456af6fb4d4f" /> + + + transform="translate(198.419,134.765)" + id="g11666"> + id="id-3cca727d-4ce1-4d5b-a231-16f77f56a592" /> + id="id-55588960-0bff-4a76-8fac-59614f7f45d4"> + transform="translate(203.67,134.765)" + id="g11670"> + id="id-786dfa6d-43dc-4440-850a-d43261749dd4" /> + transform="translate(208.651,134.765)" + id="g11673"> + id="id-fc89f5d9-d260-4a38-8e5c-5f17901da55e" /> + transform="translate(212.553,134.765)" + id="g11676"> + id="id-cb79a0c3-ba8a-4eae-9833-dbba61a9a7fb" /> + transform="translate(218.089,134.765)" + id="g11679"> + id="id-1fd9255d-f090-4ff8-a4dc-dca75e92b96f" /> + transform="translate(220.856,134.765)" + id="g11682"> + id="id-6214131c-cf3f-410d-ab38-5e4f867c2217" /> + transform="translate(226.391,134.765)" + id="g11685"> + id="id-efa276b5-8b41-49f4-ae72-3c8859cbd682" /> + transform="translate(231.373,134.765)" + id="g11688"> + id="id-05329713-06d1-46e7-8d16-ef7e64e8647a" /> + transform="translate(235.247,134.765)" + id="g11691"> - - - - - - - - - - + id="id-349b3d70-9af3-45e5-83b8-e416edd3516d" /> - - + transform="translate(239.675,134.765)" + id="g11694"> + d="m 2.078125,-1.9375 c 0.21875,0.046875 1.03125,0.203125 1.03125,0.921875 0,0.5 -0.34375,0.90625 -1.125,0.90625 -0.84375,0 -1.203125,-0.5625 -1.390625,-1.421875 C 0.5625,-1.65625 0.5625,-1.6875 0.453125,-1.6875 c -0.125,0 -0.125,0.0625 -0.125,0.234375 V -0.125 c 0,0.171875 0,0.234375 0.109375,0.234375 0.046875,0 0.0625,-0.015625 0.25,-0.203125 0.015625,-0.015625 0.015625,-0.03125 0.203125,-0.21875 0.4375,0.40625 0.890625,0.421875 1.09375,0.421875 1.140625,0 1.609375,-0.671875 1.609375,-1.390625 0,-0.515625 -0.296875,-0.828125 -0.421875,-0.9375 C 2.84375,-2.546875 2.453125,-2.625 2.03125,-2.703125 1.46875,-2.8125 0.8125,-2.9375 0.8125,-3.515625 c 0,-0.359375 0.25,-0.765625 1.109375,-0.765625 1.09375,0 1.15625,0.90625 1.171875,1.203125 0,0.09375 0.09375,0.09375 0.109375,0.09375 0.140625,0 0.140625,-0.046875 0.140625,-0.234375 v -1.015625 c 0,-0.15625 0,-0.234375 -0.109375,-0.234375 -0.046875,0 -0.078125,0 -0.203125,0.125 -0.03125,0.03125 -0.125,0.125 -0.171875,0.15625 -0.375,-0.28125 -0.78125,-0.28125 -0.9375,-0.28125 -1.21875,0 -1.59375,0.671875 -1.59375,1.234375 0,0.34375 0.15625,0.625 0.421875,0.84375 0.328125,0.25 0.609375,0.3125 1.328125,0.453125 z m 0,0" + id="id-1a82d367-6190-4182-9971-0f9be2c10cf3" /> + + + + - + id="id-1e234f8c-7f91-4497-8115-e46870f835f1"> + - - - - + style="stroke:none" + d="" + id="id-bd12d29c-ebd6-4c8a-bbc9-74fb05618652" /> + + - - + id="id-8a2757fc-24e0-4a2e-af4e-747d61d24c2d" /> + + - + id="id-1fc3232e-09e0-4ec7-b156-b34329db0477" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + transform="translate(148.712,134.765)" + id="g13299"> + style="stroke:none" + d="M 2.234375,-3.515625 V -6.09375 c 0,-0.234375 0,-0.359375 0.21875,-0.390625 C 2.546875,-6.5 2.84375,-6.5 3.046875,-6.5 c 0.890625,0 2,0.046875 2,1.484375 0,0.6875 -0.234375,1.5 -1.703125,1.5 z m 2.109375,0.125 C 5.296875,-3.625 6.078125,-4.234375 6.078125,-5.015625 6.078125,-5.96875 4.9375,-6.8125 3.484375,-6.8125 H 0.34375 V -6.5 h 0.25 c 0.765625,0 0.78125,0.109375 0.78125,0.46875 v 5.25 c 0,0.359375 -0.015625,0.46875 -0.78125,0.46875 h -0.25 V 0 c 0.359375,-0.03125 1.078125,-0.03125 1.453125,-0.03125 0.390625,0 1.109375,0 1.46875,0.03125 v -0.3125 h -0.25 c -0.765625,0 -0.78125,-0.109375 -0.78125,-0.46875 V -3.296875 H 3.375 c 0.15625,0 0.578125,0 0.9375,0.34375 0.375,0.34375 0.375,0.65625 0.375,1.328125 0,0.640625 0,1.046875 0.40625,1.421875 0.40625,0.359375 0.953125,0.421875 1.25,0.421875 0.78125,0 0.953125,-0.8125 0.953125,-1.09375 0,-0.0625 0,-0.171875 -0.125,-0.171875 -0.109375,0 -0.109375,0.09375 -0.125,0.15625 C 6.984375,-0.171875 6.640625,0 6.390625,0 5.90625,0 5.828125,-0.515625 5.6875,-1.4375 L 5.546875,-2.234375 C 5.375,-2.875 4.890625,-3.203125 4.34375,-3.390625 Z m 0,0" + id="id-8702d867-e4e8-4b32-9f10-9fca9134d3ba" /> + transform="translate(156.045,134.765)" + id="g13302"> + id="id-5689e5eb-0a1e-4c88-bd3e-c1a890b4c68c" /> + transform="translate(160.473,134.765)" + id="g13305"> + + + + + + + + + + + + + + + + + + + + id="id-1a883094-e2d7-4700-bdb1-97b6e1ae153a" /> + transform="translate(190.111,134.765)" + id="g13325"> + style="stroke:none" + d="m 3.78125,-0.546875 v 0.65625 L 5.25,0 v -0.3125 c -0.6875,0 -0.78125,-0.0625 -0.78125,-0.5625 V -6.921875 L 3.046875,-6.8125 V -6.5 c 0.6875,0 0.765625,0.0625 0.765625,0.5625 v 2.15625 c -0.28125,-0.359375 -0.71875,-0.625 -1.25,-0.625 -1.171875,0 -2.21875,0.984375 -2.21875,2.265625 0,1.265625 0.96875,2.25 2.109375,2.25 0.640625,0 1.078125,-0.34375 1.328125,-0.65625 z m 0,-2.671875 v 2.046875 c 0,0.171875 0,0.1875 -0.109375,0.359375 C 3.375,-0.328125 2.9375,-0.109375 2.5,-0.109375 2.046875,-0.109375 1.6875,-0.375 1.453125,-0.75 1.203125,-1.15625 1.171875,-1.71875 1.171875,-2.140625 1.171875,-2.5 1.1875,-3.09375 1.46875,-3.546875 1.6875,-3.859375 2.0625,-4.1875 2.609375,-4.1875 c 0.34375,0 0.765625,0.15625 1.0625,0.59375 0.109375,0.171875 0.109375,0.1875 0.109375,0.375 z m 0,0" + id="id-26fa981a-8c6e-4180-9fb2-e7f42820742d" /> + transform="translate(195.646,134.765)" + id="g13328"> + + + + id="id-dd647909-9dbf-4393-9d77-a9a5e46d571c" /> + transform="translate(203.949,134.765)" + id="g13334"> + style="stroke:none" + d="m 3.3125,-0.75 c 0.046875,0.390625 0.3125,0.8125 0.78125,0.8125 0.21875,0 0.828125,-0.140625 0.828125,-0.953125 v -0.5625 h -0.25 v 0.5625 c 0,0.578125 -0.25,0.640625 -0.359375,0.640625 -0.328125,0 -0.375,-0.453125 -0.375,-0.5 v -1.984375 c 0,-0.421875 0,-0.8125 -0.359375,-1.1875 C 3.1875,-4.3125 2.6875,-4.46875 2.21875,-4.46875 c -0.828125,0 -1.515625,0.46875 -1.515625,1.125 0,0.296875 0.203125,0.46875 0.46875,0.46875 0.28125,0 0.453125,-0.203125 0.453125,-0.453125 0,-0.125 -0.046875,-0.453125 -0.515625,-0.453125 C 1.390625,-4.140625 1.875,-4.25 2.1875,-4.25 c 0.5,0 1.0625,0.390625 1.0625,1.28125 v 0.359375 c -0.515625,0.03125 -1.203125,0.0625 -1.828125,0.359375 -0.75,0.34375 -1,0.859375 -1,1.296875 0,0.8125 0.96875,1.0625 1.59375,1.0625 0.65625,0 1.109375,-0.40625 1.296875,-0.859375 z M 3.25,-2.390625 v 1 c 0,0.9375 -0.71875,1.28125 -1.171875,1.28125 -0.484375,0 -0.890625,-0.34375 -0.890625,-0.84375 0,-0.546875 0.421875,-1.375 2.0625,-1.4375 z m 0,0" + id="id-27737682-37c6-4a52-b4d0-b1af517d06ca" /> + transform="translate(208.93,134.765)" + id="g13337"> + + + + id="id-708f7958-e595-4606-a161-bd5a0a2c6f75" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id="id-810f602b-b392-411a-990c-4697877ac70d"> + transform="translate(148.712,134.765)" + id="g15017"> + style="stroke:none" + d="M 2.3125,-6.671875 C 2.21875,-6.796875 2.21875,-6.8125 2.03125,-6.8125 H 0.328125 V -6.5 H 0.625 c 0.140625,0 0.34375,0.015625 0.484375,0.015625 0.234375,0.03125 0.25,0.046875 0.25,0.234375 v 5.203125 c 0,0.265625 0,0.734375 -1.03125,0.734375 V 0 C 0.671875,-0.015625 1.171875,-0.03125 1.5,-0.03125 c 0.328125,0 0.8125,0.015625 1.15625,0.03125 v -0.3125 c -1.015625,0 -1.015625,-0.46875 -1.015625,-0.734375 v -5.1875 c 0.046875,0.046875 0.046875,0.0625 0.09375,0.125 L 5.796875,-0.125 C 5.890625,-0.015625 5.90625,0 5.96875,0 6.109375,0 6.109375,-0.0625 6.109375,-0.265625 v -5.5 c 0,-0.265625 0,-0.734375 1.03125,-0.734375 v -0.3125 c -0.359375,0.015625 -0.84375,0.03125 -1.171875,0.03125 -0.328125,0 -0.8125,-0.015625 -1.15625,-0.03125 V -6.5 c 1.015625,0 1.015625,0.46875 1.015625,0.734375 V -1.5 Z m 0,0" + id="id-0ceb136d-c240-4454-aea6-6178e0c09d3a" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + - + id="id-948d42bf-a27e-409e-81e6-b9368bb7df2b"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + d="M 2.5,-6.921875 1.15625,-5.5625 1.328125,-5.390625 2.5,-6.40625 3.640625,-5.390625 3.8125,-5.5625 Z m 0,0" + id="id-7adf7637-0d68-4776-af25-eb40a4c044de" /> + + - - + d="m 1.71875,-3.75 v -0.65625 l -1.4375,0.109375 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5 v 4.65625 C 1.0625,1.625 0.953125,1.625 0.28125,1.625 V 1.9375 C 0.625,1.921875 1.140625,1.90625 1.390625,1.90625 c 0.28125,0 0.78125,0.015625 1.125,0.03125 V 1.625 C 1.859375,1.625 1.75,1.625 1.75,1.171875 V -0.59375 c 0.046875,0.171875 0.46875,0.703125 1.21875,0.703125 1.1875,0 2.21875,-0.984375 2.21875,-2.265625 0,-1.265625 -0.953125,-2.25 -2.078125,-2.25 -0.78125,0 -1.203125,0.4375 -1.390625,0.65625 z M 1.75,-1.140625 v -2.21875 C 2.03125,-3.875 2.515625,-4.15625 3.03125,-4.15625 c 0.734375,0 1.328125,0.875 1.328125,2 0,1.203125 -0.6875,2.046875 -1.421875,2.046875 -0.40625,0 -0.78125,-0.203125 -1.046875,-0.609375 C 1.75,-0.921875 1.75,-0.9375 1.75,-1.140625 Z m 0,0" + id="id-f6511eb1-8893-4b6d-9074-9670b39f9763" /> + + - - + d="m 2.078125,-1.9375 c 0.21875,0.046875 1.03125,0.203125 1.03125,0.921875 0,0.5 -0.34375,0.90625 -1.125,0.90625 -0.84375,0 -1.203125,-0.5625 -1.390625,-1.421875 C 0.5625,-1.65625 0.5625,-1.6875 0.453125,-1.6875 c -0.125,0 -0.125,0.0625 -0.125,0.234375 V -0.125 c 0,0.171875 0,0.234375 0.109375,0.234375 0.046875,0 0.0625,-0.015625 0.25,-0.203125 0.015625,-0.015625 0.015625,-0.03125 0.203125,-0.21875 0.4375,0.40625 0.890625,0.421875 1.09375,0.421875 1.140625,0 1.609375,-0.671875 1.609375,-1.390625 0,-0.515625 -0.296875,-0.828125 -0.421875,-0.9375 C 2.84375,-2.546875 2.453125,-2.625 2.03125,-2.703125 1.46875,-2.8125 0.8125,-2.9375 0.8125,-3.515625 c 0,-0.359375 0.25,-0.765625 1.109375,-0.765625 1.09375,0 1.15625,0.90625 1.171875,1.203125 0,0.09375 0.09375,0.09375 0.109375,0.09375 0.140625,0 0.140625,-0.046875 0.140625,-0.234375 v -1.015625 c 0,-0.15625 0,-0.234375 -0.109375,-0.234375 -0.046875,0 -0.078125,0 -0.203125,0.125 -0.03125,0.03125 -0.125,0.125 -0.171875,0.15625 -0.375,-0.28125 -0.78125,-0.28125 -0.9375,-0.28125 -1.21875,0 -1.59375,0.671875 -1.59375,1.234375 0,0.34375 0.15625,0.625 0.421875,0.84375 0.328125,0.25 0.609375,0.3125 1.328125,0.453125 z m 0,0" + id="id-841cd4b3-7a29-4aac-9841-2de42f95f0a0" /> + + - - + d="M 1.765625,-6.921875 0.328125,-6.8125 V -6.5 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 C 0.65625,-0.015625 1.1875,-0.03125 1.4375,-0.03125 c 0.25,0 0.734375,0.015625 1.109375,0.03125 v -0.3125 c -0.671875,0 -0.78125,0 -0.78125,-0.4375 z m 0,0" + id="id-611d1728-db5f-41dd-b47d-cd12e2021c09" /> + + - - + d="" + id="id-1fd6624c-4f22-4c4a-be50-20bd183d193f" /> + + - - + d="m 2.328125,-3.328125 c -0.0625,-0.15625 -0.09375,-0.34375 -0.09375,-0.515625 0,-0.515625 0.296875,-1.28125 0.84375,-1.65625 0.21875,0.296875 0.5,0.296875 0.734375,0.296875 0.28125,0 1.078125,0 1.078125,-0.53125 0,-0.421875 -0.53125,-0.421875 -0.953125,-0.421875 -0.09375,0 -0.34375,0 -0.625,0.03125 0,-0.3125 0.0625,-0.53125 0.0625,-0.546875 0.03125,-0.09375 0.03125,-0.09375 0.03125,-0.125 0,-0.203125 -0.203125,-0.21875 -0.21875,-0.21875 -0.328125,0 -0.328125,0.59375 -0.328125,0.78125 0,0.0625 0,0.15625 0.015625,0.21875 C 1.625,-5.640625 1,-4.75 1,-3.984375 c 0,0.46875 0.265625,0.84375 0.53125,1.03125 C 0.484375,-2.25 0.234375,-1.3125 0.234375,-0.9375 c 0,0.8125 0.734375,1.21875 1.328125,1.421875 L 2.703125,0.875 c 0.25,0.078125 0.671875,0.234375 0.75,0.265625 0.078125,0.046875 0.125,0.140625 0.125,0.21875 0,0.03125 -0.015625,0.296875 -0.3125,0.296875 -0.0625,0 -0.546875,-0.015625 -1,-0.25 C 2.203125,1.359375 2.1875,1.359375 2.140625,1.359375 2,1.359375 1.9375,1.46875 1.9375,1.53125 c 0,0.25 0.84375,0.484375 1.34375,0.484375 0.78125,0 1.1875,-0.71875 1.1875,-1.1875 0,-0.421875 -0.296875,-0.671875 -0.484375,-0.75 C 3.84375,0.03125 2.90625,-0.296875 2.65625,-0.375 L 2,-0.59375 c -0.34375,-0.125 -0.921875,-0.328125 -0.921875,-0.796875 0,-0.59375 0.75,-1.296875 0.875,-1.296875 0.015625,0 0.015625,0 0.078125,0.015625 C 2.515625,-2.5 2.828125,-2.5 3.09375,-2.5 c 0.3125,0 1.140625,0 1.140625,-0.546875 0,-0.40625 -0.625,-0.40625 -0.953125,-0.40625 -0.1875,0 -0.5,0 -0.953125,0.125 z M 3.40625,-5.71875 c 0.203125,-0.078125 0.375,-0.078125 0.515625,-0.078125 0.3125,0 0.34375,0 0.5,0.0625 0,0.015625 0,0.015625 -0.03125,0.0625 C 4.296875,-5.5625 3.921875,-5.5625 3.8125,-5.5625 c -0.15625,0 -0.3125,0 -0.40625,-0.15625 z M 2.578125,-3 c 0.265625,-0.09375 0.5,-0.09375 0.65625,-0.09375 0.328125,0 0.359375,0 0.515625,0.0625 -0.015625,0.015625 -0.015625,0.046875 -0.03125,0.0625 C 3.625,-2.859375 3.25,-2.859375 3.125,-2.859375 c -0.15625,0 -0.375,0 -0.546875,-0.140625 z m 0,0" + id="id-0754931a-e470-490c-a1d5-47b865d1892e" /> + + - + d="m 5.34375,-4 c -0.359375,0.109375 -0.53125,0.4375 -0.53125,0.6875 0,0.21875 0.15625,0.46875 0.484375,0.46875 0.359375,0 0.71875,-0.296875 0.71875,-0.78125 C 6.015625,-4.15625 5.5,-4.5 4.890625,-4.5 4.3125,-4.5 3.953125,-4.078125 3.8125,-3.890625 3.5625,-4.3125 3.015625,-4.5 2.4375,-4.5 c -1.25,0 -1.921875,1.21875 -1.921875,1.546875 0,0.140625 0.140625,0.140625 0.234375,0.140625 0.125,0 0.1875,0 0.234375,-0.125 0.28125,-0.90625 1,-1.203125 1.40625,-1.203125 0.375,0 0.5625,0.171875 0.5625,0.484375 0,0.203125 -0.140625,0.75 -0.234375,1.109375 L 2.375,-1.1875 c -0.140625,0.609375 -0.5,0.90625 -0.84375,0.90625 -0.046875,0 -0.28125,0 -0.46875,-0.140625 0.359375,-0.109375 0.53125,-0.453125 0.53125,-0.6875 0,-0.21875 -0.171875,-0.46875 -0.5,-0.46875 -0.34375,0 -0.71875,0.296875 -0.71875,0.78125 0,0.53125 0.53125,0.875 1.140625,0.875 0.5625,0 0.9375,-0.421875 1.0625,-0.609375 0.25,0.421875 0.8125,0.609375 1.375,0.609375 1.265625,0 1.9375,-1.21875 1.9375,-1.546875 0,-0.140625 -0.15625,-0.140625 -0.234375,-0.140625 -0.125,0 -0.1875,0 -0.234375,0.125 -0.28125,0.90625 -1,1.203125 -1.421875,1.203125 -0.375,0 -0.546875,-0.171875 -0.546875,-0.5 0,-0.203125 0.125,-0.734375 0.21875,-1.109375 0.0625,-0.25 0.296875,-1.1875 0.34375,-1.34375 0.15625,-0.609375 0.5,-0.90625 0.84375,-0.90625 0.0625,0 0.28125,0 0.484375,0.140625 z m 0,0" + id="id-6dab6b52-72f4-4e79-b04a-2b7965dc9088" /> + + + - - - + id="id-cc2e8c70-51ce-404f-b202-d0f76ddead5a"> + transform="translate(231.983,154.092)" + id="g8083"> + d="m 1.53125,-3.046875 c -1.078125,0.71875 -1.296875,1.65625 -1.296875,2.015625 0,0.46875 0.265625,0.75 0.28125,0.78125 0.328125,0.328125 0.40625,0.375 1.15625,0.65625 L 2.875,0.875 C 3.03125,0.9375 3.234375,1 3.234375,1.296875 c 0,0.234375 -0.1875,0.53125 -0.5,0.53125 -0.4375,0 -0.765625,-0.25 -0.875,-0.328125 C 1.796875,1.46875 1.796875,1.453125 1.75,1.453125 c -0.078125,0 -0.09375,0.078125 -0.09375,0.109375 0,0.125 0.53125,0.484375 1.078125,0.484375 0.625,0 1.046875,-0.578125 1.046875,-1.0625 C 3.78125,0.5 3.40625,0.3125 3.296875,0.265625 3.15625,0.21875 2.828125,0.09375 2.6875,0.046875 2.5,-0.046875 2.296875,-0.125 2.078125,-0.1875 l -0.59375,-0.234375 c -0.453125,-0.1875 -0.75,-0.46875 -0.75,-0.890625 0,-0.40625 0.390625,-1.28125 1.234375,-1.703125 C 2.328125,-2.875 2.625,-2.875 2.84375,-2.875 c 0.296875,0 0.921875,0 0.921875,-0.3125 0,-0.25 -0.421875,-0.265625 -0.8125,-0.265625 -0.1875,0 -0.46875,0 -0.84375,0.109375 -0.25,-0.25 -0.296875,-0.578125 -0.296875,-0.765625 0,-0.5625 0.359375,-1.328125 1.109375,-1.703125 0.171875,0.21875 0.40625,0.21875 0.640625,0.21875 0.25,0 0.890625,0 0.890625,-0.3125 0,-0.25 -0.453125,-0.265625 -0.828125,-0.265625 -0.140625,0 -0.375,0 -0.640625,0.046875 C 2.953125,-6.203125 2.9375,-6.28125 2.9375,-6.453125 c 0,-0.140625 0.046875,-0.34375 0.046875,-0.359375 0,-0.078125 -0.046875,-0.140625 -0.109375,-0.140625 -0.1875,0 -0.1875,0.46875 -0.1875,0.5 0,0.1875 0.0625,0.34375 0.078125,0.375 -1.078125,0.3125 -1.75,1.109375 -1.75,1.875 0,0.359375 0.1875,0.78125 0.65625,1.046875 z m 1.625,-2.859375 C 3.3125,-5.953125 3.515625,-5.953125 3.625,-5.953125 c 0.34375,0 0.375,0.015625 0.5625,0.0625 C 4.109375,-5.859375 4,-5.8125 3.5625,-5.8125 c -0.1875,0 -0.296875,0 -0.40625,-0.09375 z M 2.375,-3.1875 c 0.234375,-0.046875 0.453125,-0.046875 0.5625,-0.046875 0.359375,0 0.390625,0 0.578125,0.046875 -0.09375,0.046875 -0.1875,0.09375 -0.640625,0.09375 -0.234375,0 -0.328125,0 -0.5,-0.09375 z m 0,0" + id="id-2e36e4db-2a89-4e04-827d-7187497fd3b7" /> + id="id-84a3c078-2212-404c-bcd1-c8b95516cbae"> + transform="translate(236.342,155.586)" + id="g8087"> - - - + d="m 2.265625,-4.359375 c 0,-0.109375 -0.09375,-0.265625 -0.28125,-0.265625 -0.1875,0 -0.390625,0.1875 -0.390625,0.390625 0,0.109375 0.078125,0.265625 0.28125,0.265625 0.1875,0 0.390625,-0.203125 0.390625,-0.390625 z M 0.84375,-0.8125 c -0.03125,0.09375 -0.0625,0.171875 -0.0625,0.296875 0,0.328125 0.265625,0.578125 0.65625,0.578125 0.6875,0 1,-0.953125 1,-1.0625 0,-0.09375 -0.09375,-0.09375 -0.109375,-0.09375 -0.09375,0 -0.109375,0.046875 -0.140625,0.125 -0.15625,0.5625 -0.453125,0.84375 -0.734375,0.84375 -0.140625,0 -0.171875,-0.09375 -0.171875,-0.25 0,-0.15625 0.046875,-0.28125 0.109375,-0.4375 C 1.46875,-1 1.546875,-1.1875 1.609375,-1.375 1.671875,-1.546875 1.9375,-2.171875 1.953125,-2.265625 1.984375,-2.328125 2,-2.40625 2,-2.484375 2,-2.8125 1.71875,-3.078125 1.34375,-3.078125 0.640625,-3.078125 0.328125,-2.125 0.328125,-2 c 0,0.078125 0.09375,0.078125 0.125,0.078125 0.09375,0 0.09375,-0.03125 0.125,-0.109375 C 0.75,-2.625 1.0625,-2.875 1.3125,-2.875 c 0.109375,0 0.171875,0.046875 0.171875,0.234375 0,0.171875 -0.03125,0.265625 -0.203125,0.703125 z m 0,0" + id="id-6adee405-1540-437d-b2bd-6212fb26e3d7" /> + id="id-2130f285-c366-4089-bccf-f83deaa31fc1"> + transform="translate(254.189,154.092)" + id="g8091"> + d="M 2.234375,-3.515625 V -6.09375 c 0,-0.234375 0,-0.359375 0.21875,-0.390625 C 2.546875,-6.5 2.84375,-6.5 3.046875,-6.5 c 0.890625,0 2,0.046875 2,1.484375 0,0.6875 -0.234375,1.5 -1.703125,1.5 z m 2.109375,0.125 C 5.296875,-3.625 6.078125,-4.234375 6.078125,-5.015625 6.078125,-5.96875 4.9375,-6.8125 3.484375,-6.8125 H 0.34375 V -6.5 h 0.25 c 0.765625,0 0.78125,0.109375 0.78125,0.46875 v 5.25 c 0,0.359375 -0.015625,0.46875 -0.78125,0.46875 h -0.25 V 0 c 0.359375,-0.03125 1.078125,-0.03125 1.453125,-0.03125 0.390625,0 1.109375,0 1.46875,0.03125 v -0.3125 h -0.25 c -0.765625,0 -0.78125,-0.109375 -0.78125,-0.46875 V -3.296875 H 3.375 c 0.15625,0 0.578125,0 0.9375,0.34375 0.375,0.34375 0.375,0.65625 0.375,1.328125 0,0.640625 0,1.046875 0.40625,1.421875 0.40625,0.359375 0.953125,0.421875 1.25,0.421875 0.78125,0 0.953125,-0.8125 0.953125,-1.09375 0,-0.0625 0,-0.171875 -0.125,-0.171875 -0.109375,0 -0.109375,0.09375 -0.125,0.15625 C 6.984375,-0.171875 6.640625,0 6.390625,0 5.90625,0 5.828125,-0.515625 5.6875,-1.4375 L 5.546875,-2.234375 C 5.375,-2.875 4.890625,-3.203125 4.34375,-3.390625 Z m 0,0" + id="id-d0a82a5f-967c-4e65-8d67-ed5c1736346b" /> + transform="translate(261.522,154.092)" + id="g8094"> + id="id-d76e93ad-0571-4418-b7d2-d36d1169ce91" /> - - + transform="translate(265.95,154.092)" + id="g8097"> + d="m 1.75,-4.296875 v -1.15625 c 0,-0.875 0.46875,-1.359375 0.90625,-1.359375 0.03125,0 0.1875,0 0.328125,0.078125 C 2.875,-6.703125 2.6875,-6.5625 2.6875,-6.3125 c 0,0.21875 0.15625,0.421875 0.4375,0.421875 0.28125,0 0.4375,-0.203125 0.4375,-0.4375 0,-0.375 -0.375,-0.703125 -0.90625,-0.703125 -0.6875,0 -1.546875,0.53125 -1.546875,1.59375 v 1.140625 h -0.78125 v 0.3125 h 0.78125 V -0.75 C 1.109375,-0.3125 1,-0.3125 0.34375,-0.3125 V 0 c 0.390625,-0.015625 0.859375,-0.03125 1.125,-0.03125 0.40625,0 0.875,0 1.265625,0.03125 V -0.3125 H 2.53125 c -0.734375,0 -0.75,-0.109375 -0.75,-0.46875 v -3.203125 h 1.125 v -0.3125 z m 0,0" + id="id-0416874a-8b89-4710-95c1-df269ec95895" /> - - + transform="translate(268.994,154.092)" + id="g8100"> + d="M 1.109375,-2.515625 C 1.171875,-4 2.015625,-4.25 2.359375,-4.25 c 1.015625,0 1.125,1.34375 1.125,1.734375 z m 0,0.21875 h 2.78125 c 0.21875,0 0.25,0 0.25,-0.21875 0,-0.984375 -0.546875,-1.953125 -1.78125,-1.953125 -1.15625,0 -2.078125,1.03125 -2.078125,2.28125 0,1.328125 1.046875,2.296875 2.1875,2.296875 C 3.6875,0.109375 4.140625,-1 4.140625,-1.1875 4.140625,-1.28125 4.0625,-1.3125 4,-1.3125 c -0.078125,0 -0.109375,0.0625 -0.125,0.140625 -0.34375,1.03125 -1.25,1.03125 -1.34375,1.03125 -0.5,0 -0.890625,-0.296875 -1.125,-0.671875 -0.296875,-0.46875 -0.296875,-1.125 -0.296875,-1.484375 z m 0,0" + id="id-56d71ba4-346d-41b6-a37d-34e20e178554" /> - - + transform="translate(273.422,154.092)" + id="g8103"> + d="M 1.671875,-3.3125 V -4.40625 L 0.28125,-4.296875 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.390625,-0.015625 0.859375,-0.03125 1.140625,-0.03125 0.390625,0 0.859375,0 1.265625,0.03125 V -0.3125 H 2.46875 c -0.734375,0 -0.75,-0.109375 -0.75,-0.46875 V -2.3125 c 0,-0.984375 0.421875,-1.875 1.171875,-1.875 0.0625,0 0.09375,0 0.109375,0.015625 -0.03125,0 -0.234375,0.125 -0.234375,0.390625 0,0.265625 0.21875,0.421875 0.4375,0.421875 0.171875,0 0.421875,-0.125 0.421875,-0.4375 0,-0.3125 -0.3125,-0.609375 -0.734375,-0.609375 -0.734375,0 -1.09375,0.671875 -1.21875,1.09375 z m 0,0" + id="id-84e66721-29db-42e3-8b28-816190dc9798" /> - - + transform="translate(277.324,154.092)" + id="g8106"> + d="M 1.109375,-2.515625 C 1.171875,-4 2.015625,-4.25 2.359375,-4.25 c 1.015625,0 1.125,1.34375 1.125,1.734375 z m 0,0.21875 h 2.78125 c 0.21875,0 0.25,0 0.25,-0.21875 0,-0.984375 -0.546875,-1.953125 -1.78125,-1.953125 -1.15625,0 -2.078125,1.03125 -2.078125,2.28125 0,1.328125 1.046875,2.296875 2.1875,2.296875 C 3.6875,0.109375 4.140625,-1 4.140625,-1.1875 4.140625,-1.28125 4.0625,-1.3125 4,-1.3125 c -0.078125,0 -0.109375,0.0625 -0.125,0.140625 -0.34375,1.03125 -1.25,1.03125 -1.34375,1.03125 -0.5,0 -0.890625,-0.296875 -1.125,-0.671875 -0.296875,-0.46875 -0.296875,-1.125 -0.296875,-1.484375 z m 0,0" + id="id-f972af51-70c0-4a44-b7a5-d0da05a87dc1" /> + transform="translate(281.752,154.092)" + id="g8109"> + d="M 1.09375,-3.421875 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.359375,-0.015625 0.859375,-0.03125 1.140625,-0.03125 0.25,0 0.765625,0.015625 1.109375,0.03125 v -0.3125 c -0.671875,0 -0.78125,0 -0.78125,-0.4375 V -2.59375 C 1.78125,-3.625 2.5,-4.1875 3.125,-4.1875 c 0.640625,0 0.75,0.53125 0.75,1.109375 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.34375,-0.015625 0.859375,-0.03125 1.125,-0.03125 0.25,0 0.78125,0.015625 1.109375,0.03125 v -0.3125 c -0.515625,0 -0.765625,0 -0.765625,-0.296875 v -1.90625 c 0,-0.859375 0,-1.15625 -0.3125,-1.515625 -0.140625,-0.171875 -0.46875,-0.375 -1.046875,-0.375 C 2.46875,-4.40625 2,-3.984375 1.71875,-3.359375 V -4.40625 L 0.3125,-4.296875 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 z m 0,0" + id="id-44873e1d-0a60-4cb4-8698-76997f840b45" /> + transform="translate(287.287,154.092)" + id="g8112"> + d="m 1.171875,-2.171875 c 0,-1.625 0.8125,-2.046875 1.34375,-2.046875 0.09375,0 0.71875,0.015625 1.0625,0.375 -0.40625,0.03125 -0.46875,0.328125 -0.46875,0.453125 0,0.265625 0.1875,0.453125 0.453125,0.453125 0.265625,0 0.46875,-0.15625 0.46875,-0.46875 0,-0.671875 -0.765625,-1.0625 -1.53125,-1.0625 -1.25,0 -2.15625,1.078125 -2.15625,2.3125 0,1.28125 0.984375,2.265625 2.140625,2.265625 1.328125,0 1.65625,-1.203125 1.65625,-1.296875 0,-0.09375 -0.109375,-0.09375 -0.140625,-0.09375 -0.078125,0 -0.109375,0.03125 -0.125,0.09375 -0.28125,0.921875 -0.9375,1.046875 -1.296875,1.046875 -0.53125,0 -1.40625,-0.421875 -1.40625,-2.03125 z m 0,0" + id="id-935e9b74-900e-4dd5-8ddd-5c49b0218401" /> + transform="translate(291.714,154.092)" + id="g8115"> + d="M 1.109375,-2.515625 C 1.171875,-4 2.015625,-4.25 2.359375,-4.25 c 1.015625,0 1.125,1.34375 1.125,1.734375 z m 0,0.21875 h 2.78125 c 0.21875,0 0.25,0 0.25,-0.21875 0,-0.984375 -0.546875,-1.953125 -1.78125,-1.953125 -1.15625,0 -2.078125,1.03125 -2.078125,2.28125 0,1.328125 1.046875,2.296875 2.1875,2.296875 C 3.6875,0.109375 4.140625,-1 4.140625,-1.1875 4.140625,-1.28125 4.0625,-1.3125 4,-1.3125 c -0.078125,0 -0.109375,0.0625 -0.125,0.140625 -0.34375,1.03125 -1.25,1.03125 -1.34375,1.03125 -0.5,0 -0.890625,-0.296875 -1.125,-0.671875 -0.296875,-0.46875 -0.296875,-1.125 -0.296875,-1.484375 z m 0,0" + id="id-89ea73b6-b193-4dcd-9fbf-21f18fffc306" /> + id="id-e5f8db58-8bf4-4c0b-a694-b694039adceb"> + transform="translate(299.459,154.092)" + id="g8119"> + id="id-593860ab-5b79-4961-b25a-2abf78fc867a" /> + transform="translate(303.886,154.092)" + id="g8122"> + id="id-b0cdcacd-1c56-40f9-b836-1014216f35e8" /> + id="id-9cb6dc5a-dce2-4ce4-a226-2e65d9c7c88e"> + transform="translate(309.147,154.092)" + id="g8126"> + id="id-3898f850-b7b6-44ae-a204-0f669f9e23a4" /> + transform="translate(314.128,154.092)" + id="g8129"> + id="id-7a16b17d-642b-49fe-b247-10ee7500a5a0" /> + transform="translate(318.03,154.092)" + id="g8132"> + id="id-6401e858-4025-4fba-9a65-882ed23d274c" /> + transform="translate(323.566,154.092)" + id="g8135"> + id="id-1bbc9017-f40d-4fe4-9abd-117b761707fb" /> + transform="translate(326.333,154.092)" + id="g8138"> + id="id-8fa718e7-1297-46fc-8030-e9091c4471c4" /> + transform="translate(331.868,154.092)" + id="g8141"> + id="id-d8fb2528-f64e-4ee6-a6a6-3824b02bac0f" /> + transform="translate(336.85,154.092)" + id="g8144"> + id="id-8d2ad06b-9876-4366-bf37-46d1b060f1bd" /> + transform="translate(340.724,154.092)" + id="g8147"> + id="id-3016b8a9-8c50-49e9-bd0d-2d46d018afa5" /> + id="id-946cebb3-1a53-4319-ac7b-14b338bcda0e"> + transform="translate(348.474,154.092)" + id="g8151"> + id="id-e469ad4b-e5b4-4800-ab2a-a41d662321b5" /> + id="id-5211a403-f8b7-4c86-bf46-d61a9bacd905"> + transform="translate(233.448,164.597)" + id="g8155"> + id="id-05a4b714-b7ff-40bc-bd5e-41c7846fdc63" /> + id="id-66f08953-bffb-418e-83e8-538fee8fb9f7"> + transform="translate(231.983,167.226)" + id="g8159"> + d="m 2.328125,-3.328125 c -0.0625,-0.15625 -0.09375,-0.34375 -0.09375,-0.515625 0,-0.515625 0.296875,-1.28125 0.84375,-1.65625 0.21875,0.296875 0.5,0.296875 0.734375,0.296875 0.28125,0 1.078125,0 1.078125,-0.53125 0,-0.421875 -0.53125,-0.421875 -0.953125,-0.421875 -0.09375,0 -0.34375,0 -0.625,0.03125 0,-0.3125 0.0625,-0.53125 0.0625,-0.546875 0.03125,-0.09375 0.03125,-0.09375 0.03125,-0.125 0,-0.203125 -0.203125,-0.21875 -0.21875,-0.21875 -0.328125,0 -0.328125,0.59375 -0.328125,0.78125 0,0.0625 0,0.15625 0.015625,0.21875 C 1.625,-5.640625 1,-4.75 1,-3.984375 c 0,0.46875 0.265625,0.84375 0.53125,1.03125 C 0.484375,-2.25 0.234375,-1.3125 0.234375,-0.9375 c 0,0.8125 0.734375,1.21875 1.328125,1.421875 L 2.703125,0.875 c 0.25,0.078125 0.671875,0.234375 0.75,0.265625 0.078125,0.046875 0.125,0.140625 0.125,0.21875 0,0.03125 -0.015625,0.296875 -0.3125,0.296875 -0.0625,0 -0.546875,-0.015625 -1,-0.25 C 2.203125,1.359375 2.1875,1.359375 2.140625,1.359375 2,1.359375 1.9375,1.46875 1.9375,1.53125 c 0,0.25 0.84375,0.484375 1.34375,0.484375 0.78125,0 1.1875,-0.71875 1.1875,-1.1875 0,-0.421875 -0.296875,-0.671875 -0.484375,-0.75 C 3.84375,0.03125 2.90625,-0.296875 2.65625,-0.375 L 2,-0.59375 c -0.34375,-0.125 -0.921875,-0.328125 -0.921875,-0.796875 0,-0.59375 0.75,-1.296875 0.875,-1.296875 0.015625,0 0.015625,0 0.078125,0.015625 C 2.515625,-2.5 2.828125,-2.5 3.09375,-2.5 c 0.3125,0 1.140625,0 1.140625,-0.546875 0,-0.40625 -0.625,-0.40625 -0.953125,-0.40625 -0.1875,0 -0.5,0 -0.953125,0.125 z M 3.40625,-5.71875 c 0.203125,-0.078125 0.375,-0.078125 0.515625,-0.078125 0.3125,0 0.34375,0 0.5,0.0625 0,0.015625 0,0.015625 -0.03125,0.0625 C 4.296875,-5.5625 3.921875,-5.5625 3.8125,-5.5625 c -0.15625,0 -0.3125,0 -0.40625,-0.15625 z M 2.578125,-3 c 0.265625,-0.09375 0.5,-0.09375 0.65625,-0.09375 0.328125,0 0.359375,0 0.515625,0.0625 -0.015625,0.015625 -0.015625,0.046875 -0.03125,0.0625 C 3.625,-2.859375 3.25,-2.859375 3.125,-2.859375 c -0.15625,0 -0.375,0 -0.546875,-0.140625 z m 0,0" + id="id-8196edc7-ab5b-4fcc-9ae1-d8112ecad80d" /> + id="id-ffb7853b-b2d8-4cb1-989c-64933e0a3dde"> + transform="translate(237.047,168.721)" + id="g8163"> + d="m 3.8125,-0.984375 c 0.640625,-0.65625 0.890625,-1.59375 0.890625,-1.65625 0,-0.09375 -0.078125,-0.09375 -0.109375,-0.09375 C 4.5,-2.734375 4.5,-2.71875 4.453125,-2.5625 4.328125,-2.109375 4.09375,-1.671875 3.796875,-1.296875 3.796875,-1.40625 3.78125,-1.875 3.765625,-1.9375 3.65625,-2.625 3.140625,-3.078125 2.4375,-3.078125 c -1.015625,0 -2,0.953125 -2,1.9375 0,0.640625 0.46875,1.203125 1.265625,1.203125 0.640625,0 1.21875,-0.28125 1.609375,-0.578125 0.171875,0.5 0.515625,0.578125 0.734375,0.578125 0.40625,0 0.65625,-0.328125 0.65625,-0.484375 C 4.703125,-0.5 4.609375,-0.5 4.578125,-0.5 c -0.09375,0 -0.109375,0.03125 -0.125,0.0625 -0.09375,0.265625 -0.3125,0.3125 -0.390625,0.3125 -0.09375,0 -0.21875,0 -0.25,-0.859375 z M 3.265625,-0.75 C 2.578125,-0.1875 2,-0.125 1.734375,-0.125 c -0.46875,0 -0.71875,-0.3125 -0.71875,-0.78125 0,-0.1875 0.09375,-0.953125 0.453125,-1.4375 C 1.796875,-2.765625 2.1875,-2.875 2.4375,-2.875 c 0.546875,0 0.71875,0.53125 0.765625,0.96875 0.046875,0.296875 0.03125,0.796875 0.0625,1.15625 z m 0,0" + id="id-ee0539ad-d967-4b57-bc5d-5591c011cab6" /> + id="id-66088037-ca49-416f-9c98-259d328d780d"> + transform="translate(254.189,167.226)" + id="g8167"> + id="id-0a50712f-7ea7-4ae6-8d2d-8daf19945ac2" /> + transform="translate(261.522,167.226)" + id="g8170"> + id="id-3a0f8bfa-94fd-4ebe-a05b-96c6ac2b2844" /> + transform="translate(265.95,167.226)" + id="g8173"> + d="m 1.75,-4.296875 v -1.15625 c 0,-0.875 0.46875,-1.359375 0.90625,-1.359375 0.03125,0 0.1875,0 0.328125,0.078125 C 2.875,-6.703125 2.6875,-6.5625 2.6875,-6.3125 c 0,0.21875 0.15625,0.421875 0.4375,0.421875 0.28125,0 0.4375,-0.203125 0.4375,-0.4375 0,-0.375 -0.375,-0.703125 -0.90625,-0.703125 -0.6875,0 -1.546875,0.53125 -1.546875,1.59375 v 1.140625 h -0.78125 v 0.3125 h 0.78125 V -0.75 C 1.109375,-0.3125 1,-0.3125 0.34375,-0.3125 V 0 c 0.390625,-0.015625 0.859375,-0.03125 1.125,-0.03125 0.40625,0 0.875,0 1.265625,0.03125 V -0.3125 H 2.53125 c -0.734375,0 -0.75,-0.109375 -0.75,-0.46875 v -3.203125 h 1.125 v -0.3125 z m 0,0" + id="id-f954832d-4f98-412c-a9fe-c885b97117cc" /> + transform="translate(268.994,167.226)" + id="g8176"> + d="M 1.109375,-2.515625 C 1.171875,-4 2.015625,-4.25 2.359375,-4.25 c 1.015625,0 1.125,1.34375 1.125,1.734375 z m 0,0.21875 h 2.78125 c 0.21875,0 0.25,0 0.25,-0.21875 0,-0.984375 -0.546875,-1.953125 -1.78125,-1.953125 -1.15625,0 -2.078125,1.03125 -2.078125,2.28125 0,1.328125 1.046875,2.296875 2.1875,2.296875 C 3.6875,0.109375 4.140625,-1 4.140625,-1.1875 4.140625,-1.28125 4.0625,-1.3125 4,-1.3125 c -0.078125,0 -0.109375,0.0625 -0.125,0.140625 -0.34375,1.03125 -1.25,1.03125 -1.34375,1.03125 -0.5,0 -0.890625,-0.296875 -1.125,-0.671875 -0.296875,-0.46875 -0.296875,-1.125 -0.296875,-1.484375 z m 0,0" + id="id-eaa0b83d-7fd0-493b-b9dd-21a4ddc87da3" /> - - + transform="translate(273.422,167.226)" + id="g8179"> + d="M 1.671875,-3.3125 V -4.40625 L 0.28125,-4.296875 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.390625,-0.015625 0.859375,-0.03125 1.140625,-0.03125 0.390625,0 0.859375,0 1.265625,0.03125 V -0.3125 H 2.46875 c -0.734375,0 -0.75,-0.109375 -0.75,-0.46875 V -2.3125 c 0,-0.984375 0.421875,-1.875 1.171875,-1.875 0.0625,0 0.09375,0 0.109375,0.015625 -0.03125,0 -0.234375,0.125 -0.234375,0.390625 0,0.265625 0.21875,0.421875 0.4375,0.421875 0.171875,0 0.421875,-0.125 0.421875,-0.4375 0,-0.3125 -0.3125,-0.609375 -0.734375,-0.609375 -0.734375,0 -1.09375,0.671875 -1.21875,1.09375 z m 0,0" + id="id-b48fa349-fc40-4e4d-aa7a-af4131af83c9" /> - - + transform="translate(277.324,167.226)" + id="g8182"> + d="M 1.109375,-2.515625 C 1.171875,-4 2.015625,-4.25 2.359375,-4.25 c 1.015625,0 1.125,1.34375 1.125,1.734375 z m 0,0.21875 h 2.78125 c 0.21875,0 0.25,0 0.25,-0.21875 0,-0.984375 -0.546875,-1.953125 -1.78125,-1.953125 -1.15625,0 -2.078125,1.03125 -2.078125,2.28125 0,1.328125 1.046875,2.296875 2.1875,2.296875 C 3.6875,0.109375 4.140625,-1 4.140625,-1.1875 4.140625,-1.28125 4.0625,-1.3125 4,-1.3125 c -0.078125,0 -0.109375,0.0625 -0.125,0.140625 -0.34375,1.03125 -1.25,1.03125 -1.34375,1.03125 -0.5,0 -0.890625,-0.296875 -1.125,-0.671875 -0.296875,-0.46875 -0.296875,-1.125 -0.296875,-1.484375 z m 0,0" + id="id-6ed3104d-6051-46d2-98b4-d2edea9a2462" /> + transform="translate(281.752,167.226)" + id="g8185"> + d="M 1.09375,-3.421875 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.359375,-0.015625 0.859375,-0.03125 1.140625,-0.03125 0.25,0 0.765625,0.015625 1.109375,0.03125 v -0.3125 c -0.671875,0 -0.78125,0 -0.78125,-0.4375 V -2.59375 C 1.78125,-3.625 2.5,-4.1875 3.125,-4.1875 c 0.640625,0 0.75,0.53125 0.75,1.109375 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.34375,-0.015625 0.859375,-0.03125 1.125,-0.03125 0.25,0 0.78125,0.015625 1.109375,0.03125 v -0.3125 c -0.515625,0 -0.765625,0 -0.765625,-0.296875 v -1.90625 c 0,-0.859375 0,-1.15625 -0.3125,-1.515625 -0.140625,-0.171875 -0.46875,-0.375 -1.046875,-0.375 C 2.46875,-4.40625 2,-3.984375 1.71875,-3.359375 V -4.40625 L 0.3125,-4.296875 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 z m 0,0" + id="id-4f329ae3-e646-4269-bf27-90ff3690a42e" /> + transform="translate(287.287,167.226)" + id="g8188"> + d="m 1.171875,-2.171875 c 0,-1.625 0.8125,-2.046875 1.34375,-2.046875 0.09375,0 0.71875,0.015625 1.0625,0.375 -0.40625,0.03125 -0.46875,0.328125 -0.46875,0.453125 0,0.265625 0.1875,0.453125 0.453125,0.453125 0.265625,0 0.46875,-0.15625 0.46875,-0.46875 0,-0.671875 -0.765625,-1.0625 -1.53125,-1.0625 -1.25,0 -2.15625,1.078125 -2.15625,2.3125 0,1.28125 0.984375,2.265625 2.140625,2.265625 1.328125,0 1.65625,-1.203125 1.65625,-1.296875 0,-0.09375 -0.109375,-0.09375 -0.140625,-0.09375 -0.078125,0 -0.109375,0.03125 -0.125,0.09375 -0.28125,0.921875 -0.9375,1.046875 -1.296875,1.046875 -0.53125,0 -1.40625,-0.421875 -1.40625,-2.03125 z m 0,0" + id="id-15eb8a25-d439-43ca-beab-f461abba9c2d" /> + transform="translate(291.714,167.226)" + id="g8191"> + d="M 1.109375,-2.515625 C 1.171875,-4 2.015625,-4.25 2.359375,-4.25 c 1.015625,0 1.125,1.34375 1.125,1.734375 z m 0,0.21875 h 2.78125 c 0.21875,0 0.25,0 0.25,-0.21875 0,-0.984375 -0.546875,-1.953125 -1.78125,-1.953125 -1.15625,0 -2.078125,1.03125 -2.078125,2.28125 0,1.328125 1.046875,2.296875 2.1875,2.296875 C 3.6875,0.109375 4.140625,-1 4.140625,-1.1875 4.140625,-1.28125 4.0625,-1.3125 4,-1.3125 c -0.078125,0 -0.109375,0.0625 -0.125,0.140625 -0.34375,1.03125 -1.25,1.03125 -1.34375,1.03125 -0.5,0 -0.890625,-0.296875 -1.125,-0.671875 -0.296875,-0.46875 -0.296875,-1.125 -0.296875,-1.484375 z m 0,0" + id="id-67b2dfd3-db4d-4b49-bf63-b27adce020f4" /> + + + transform="translate(299.459,167.226)" + id="g8195"> + d="m 1.71875,-3.75 v -0.65625 l -1.4375,0.109375 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5 v 4.65625 C 1.0625,1.625 0.953125,1.625 0.28125,1.625 V 1.9375 C 0.625,1.921875 1.140625,1.90625 1.390625,1.90625 c 0.28125,0 0.78125,0.015625 1.125,0.03125 V 1.625 C 1.859375,1.625 1.75,1.625 1.75,1.171875 V -0.59375 c 0.046875,0.171875 0.46875,0.703125 1.21875,0.703125 1.1875,0 2.21875,-0.984375 2.21875,-2.265625 0,-1.265625 -0.953125,-2.25 -2.078125,-2.25 -0.78125,0 -1.203125,0.4375 -1.390625,0.65625 z M 1.75,-1.140625 v -2.21875 C 2.03125,-3.875 2.515625,-4.15625 3.03125,-4.15625 c 0.734375,0 1.328125,0.875 1.328125,2 0,1.203125 -0.6875,2.046875 -1.421875,2.046875 -0.40625,0 -0.78125,-0.203125 -1.046875,-0.609375 C 1.75,-0.921875 1.75,-0.9375 1.75,-1.140625 Z m 0,0" + id="id-70a73384-b133-4b99-a737-6a83b8b3017a" /> + + + transform="translate(305.273,167.226)" + id="g8199"> + id="id-8685d907-800a-4b25-b149-058664f29e36" /> + transform="translate(310.255,167.226)" + id="g8202"> + d="m 2.078125,-1.9375 c 0.21875,0.046875 1.03125,0.203125 1.03125,0.921875 0,0.5 -0.34375,0.90625 -1.125,0.90625 -0.84375,0 -1.203125,-0.5625 -1.390625,-1.421875 C 0.5625,-1.65625 0.5625,-1.6875 0.453125,-1.6875 c -0.125,0 -0.125,0.0625 -0.125,0.234375 V -0.125 c 0,0.171875 0,0.234375 0.109375,0.234375 0.046875,0 0.0625,-0.015625 0.25,-0.203125 0.015625,-0.015625 0.015625,-0.03125 0.203125,-0.21875 0.4375,0.40625 0.890625,0.421875 1.09375,0.421875 1.140625,0 1.609375,-0.671875 1.609375,-1.390625 0,-0.515625 -0.296875,-0.828125 -0.421875,-0.9375 C 2.84375,-2.546875 2.453125,-2.625 2.03125,-2.703125 1.46875,-2.8125 0.8125,-2.9375 0.8125,-3.515625 c 0,-0.359375 0.25,-0.765625 1.109375,-0.765625 1.09375,0 1.15625,0.90625 1.171875,1.203125 0,0.09375 0.09375,0.09375 0.109375,0.09375 0.140625,0 0.140625,-0.046875 0.140625,-0.234375 v -1.015625 c 0,-0.15625 0,-0.234375 -0.109375,-0.234375 -0.046875,0 -0.078125,0 -0.203125,0.125 -0.03125,0.03125 -0.125,0.125 -0.171875,0.15625 -0.375,-0.28125 -0.78125,-0.28125 -0.9375,-0.28125 -1.21875,0 -1.59375,0.671875 -1.59375,1.234375 0,0.34375 0.15625,0.625 0.421875,0.84375 0.328125,0.25 0.609375,0.3125 1.328125,0.453125 z m 0,0" + id="id-0d60ed0e-6c91-49bb-abf9-f9160145e9ba" /> - - + transform="translate(314.184,167.226)" + id="g8205"> + d="M 1.765625,-4.40625 0.375,-4.296875 v 0.3125 c 0.640625,0 0.734375,0.0625 0.734375,0.546875 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 C 0.640625,-0.015625 1.1875,-0.03125 1.421875,-0.03125 1.78125,-0.03125 2.125,-0.015625 2.46875,0 v -0.3125 c -0.671875,0 -0.703125,-0.046875 -0.703125,-0.4375 z m 0.03125,-1.734375 c 0,-0.3125 -0.234375,-0.53125 -0.515625,-0.53125 -0.3125,0 -0.53125,0.265625 -0.53125,0.53125 0,0.265625 0.21875,0.53125 0.53125,0.53125 0.28125,0 0.515625,-0.21875 0.515625,-0.53125 z m 0,0" + id="id-df75e786-320d-4b4a-a2cc-1439a4188dd7" /> + transform="translate(316.951,167.226)" + id="g8208"> + d="m 1.71875,-3.984375 h 1.4375 v -0.3125 H 1.71875 V -6.125 h -0.25 c 0,0.8125 -0.296875,1.875 -1.28125,1.921875 v 0.21875 h 0.84375 v 2.75 c 0,1.21875 0.9375,1.34375 1.296875,1.34375 0.703125,0 0.984375,-0.703125 0.984375,-1.34375 v -0.5625 h -0.25 V -1.25 c 0,0.734375 -0.296875,1.109375 -0.671875,1.109375 -0.671875,0 -0.671875,-0.90625 -0.671875,-1.078125 z m 0,0" + id="id-3829cce5-3a8a-41c7-bea0-5563e2b90e42" /> - - + transform="translate(320.826,167.226)" + id="g8211"> + d="M 1.765625,-4.40625 0.375,-4.296875 v 0.3125 c 0.640625,0 0.734375,0.0625 0.734375,0.546875 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 C 0.640625,-0.015625 1.1875,-0.03125 1.421875,-0.03125 1.78125,-0.03125 2.125,-0.015625 2.46875,0 v -0.3125 c -0.671875,0 -0.703125,-0.046875 -0.703125,-0.4375 z m 0.03125,-1.734375 c 0,-0.3125 -0.234375,-0.53125 -0.515625,-0.53125 -0.3125,0 -0.53125,0.265625 -0.53125,0.53125 0,0.265625 0.21875,0.53125 0.53125,0.53125 0.28125,0 0.515625,-0.21875 0.515625,-0.53125 z m 0,0" + id="id-432273b7-6f03-4b59-afeb-ab5da89dccc5" /> + transform="translate(323.593,167.226)" + id="g8214"> - - - - - + id="id-322c7e22-0ef4-4262-bea3-c4f3c2c4cd13" /> + transform="translate(328.575,167.226)" + id="g8217"> + d="M 1.09375,-3.421875 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.359375,-0.015625 0.859375,-0.03125 1.140625,-0.03125 0.25,0 0.765625,0.015625 1.109375,0.03125 v -0.3125 c -0.671875,0 -0.78125,0 -0.78125,-0.4375 V -2.59375 C 1.78125,-3.625 2.5,-4.1875 3.125,-4.1875 c 0.640625,0 0.75,0.53125 0.75,1.109375 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.34375,-0.015625 0.859375,-0.03125 1.125,-0.03125 0.25,0 0.78125,0.015625 1.109375,0.03125 v -0.3125 c -0.515625,0 -0.765625,0 -0.765625,-0.296875 v -1.90625 c 0,-0.859375 0,-1.15625 -0.3125,-1.515625 -0.140625,-0.171875 -0.46875,-0.375 -1.046875,-0.375 C 2.46875,-4.40625 2,-3.984375 1.71875,-3.359375 V -4.40625 L 0.3125,-4.296875 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 z m 0,0" + id="id-b48c988c-3b51-4538-95d9-045d81e9d9e0" /> + id="id-3e2fb019-f8ba-439a-ab68-15eb7799b05a"> + transform="translate(337.428,167.226)" + id="g8221"> + d="M 4.6875,-2.140625 C 4.6875,-3.40625 3.703125,-4.46875 2.5,-4.46875 c -1.25,0 -2.21875,1.09375 -2.21875,2.328125 0,1.296875 1.03125,2.25 2.203125,2.25 1.203125,0 2.203125,-0.984375 2.203125,-2.25 z m -2.1875,2 c -0.4375,0 -0.875,-0.203125 -1.140625,-0.671875 -0.25,-0.4375 -0.25,-1.046875 -0.25,-1.40625 0,-0.390625 0,-0.921875 0.234375,-1.359375 C 1.609375,-4.03125 2.078125,-4.25 2.484375,-4.25 c 0.4375,0 0.859375,0.21875 1.125,0.65625 0.265625,0.421875 0.265625,1 0.265625,1.375 0,0.359375 0,0.90625 -0.21875,1.34375 C 3.421875,-0.421875 2.984375,-0.140625 2.5,-0.140625 Z m 0,0" + id="id-2faeb17e-a143-4b20-8165-43384d277c32" /> - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - + id="id-5609ade9-e83b-4089-b981-c7f3b0a8f73e" /> + + + + - - + d="M 1.09375,-3.421875 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.359375,-0.015625 0.859375,-0.03125 1.140625,-0.03125 0.25,0 0.765625,0.015625 1.109375,0.03125 v -0.3125 c -0.671875,0 -0.78125,0 -0.78125,-0.4375 V -2.59375 C 1.78125,-3.625 2.5,-4.1875 3.125,-4.1875 c 0.640625,0 0.75,0.53125 0.75,1.109375 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.34375,-0.015625 0.859375,-0.03125 1.125,-0.03125 0.25,0 0.78125,0.015625 1.109375,0.03125 v -0.3125 c -0.515625,0 -0.765625,0 -0.765625,-0.296875 v -1.90625 c 0,-0.859375 0,-1.15625 -0.3125,-1.515625 -0.140625,-0.171875 -0.46875,-0.375 -1.046875,-0.375 C 2.46875,-4.40625 2,-3.984375 1.71875,-3.359375 V -4.40625 L 0.3125,-4.296875 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 z m 0,0" + id="id-d1d151f0-7487-442a-8a50-332e17bf0aea" /> + + - + d="M 4.6875,-2.140625 C 4.6875,-3.40625 3.703125,-4.46875 2.5,-4.46875 c -1.25,0 -2.21875,1.09375 -2.21875,2.328125 0,1.296875 1.03125,2.25 2.203125,2.25 1.203125,0 2.203125,-0.984375 2.203125,-2.25 z m -2.1875,2 c -0.4375,0 -0.875,-0.203125 -1.140625,-0.671875 -0.25,-0.4375 -0.25,-1.046875 -0.25,-1.40625 0,-0.390625 0,-0.921875 0.234375,-1.359375 C 1.609375,-4.03125 2.078125,-4.25 2.484375,-4.25 c 0.4375,0 0.859375,0.21875 1.125,0.65625 0.265625,0.421875 0.265625,1 0.265625,1.375 0,0.359375 0,0.90625 -0.21875,1.34375 C 3.421875,-0.421875 2.984375,-0.140625 2.5,-0.140625 Z m 0,0" + id="id-e889be02-5714-4212-bd7a-2814a10869fd" /> + - - + id="id-4c716cb2-9589-4e1d-98eb-785da8605724"> + transform="translate(359.566,167.226)" + id="g8235"> + d="m 3.78125,-0.546875 v 0.65625 L 5.25,0 v -0.3125 c -0.6875,0 -0.78125,-0.0625 -0.78125,-0.5625 V -6.921875 L 3.046875,-6.8125 V -6.5 c 0.6875,0 0.765625,0.0625 0.765625,0.5625 v 2.15625 c -0.28125,-0.359375 -0.71875,-0.625 -1.25,-0.625 -1.171875,0 -2.21875,0.984375 -2.21875,2.265625 0,1.265625 0.96875,2.25 2.109375,2.25 0.640625,0 1.078125,-0.34375 1.328125,-0.65625 z m 0,-2.671875 v 2.046875 c 0,0.171875 0,0.1875 -0.109375,0.359375 C 3.375,-0.328125 2.9375,-0.109375 2.5,-0.109375 2.046875,-0.109375 1.6875,-0.375 1.453125,-0.75 1.203125,-1.15625 1.171875,-1.71875 1.171875,-2.140625 1.171875,-2.5 1.1875,-3.09375 1.46875,-3.546875 1.6875,-3.859375 2.0625,-4.1875 2.609375,-4.1875 c 0.34375,0 0.765625,0.15625 1.0625,0.59375 0.109375,0.171875 0.109375,0.1875 0.109375,0.375 z m 0,0" + id="id-97935f58-85f7-4b37-8bca-2fd773b27ecf" /> + transform="translate(365.102,167.226)" + id="g8238"> + id="id-edc1ccd0-1901-4d53-ae6f-202d44cf6720" /> + + + transform="translate(372.855,167.226)" + id="g8242"> + d="m 4.75,-2.359375 c 0,-1.5625 -0.921875,-2.046875 -1.65625,-2.046875 -1.375,0 -2.6875,1.421875 -2.6875,2.828125 0,0.9375 0.59375,1.6875 1.625,1.6875 0.625,0 1.34375,-0.234375 2.09375,-0.84375 0.125,0.53125 0.453125,0.84375 0.90625,0.84375 0.53125,0 0.84375,-0.546875 0.84375,-0.703125 0,-0.078125 -0.0625,-0.109375 -0.125,-0.109375 -0.0625,0 -0.09375,0.03125 -0.125,0.109375 -0.1875,0.484375 -0.546875,0.484375 -0.5625,0.484375 -0.3125,0 -0.3125,-0.78125 -0.3125,-1.015625 0,-0.203125 0,-0.234375 0.109375,-0.34375 C 5.796875,-2.65625 6,-3.8125 6,-3.8125 6,-3.84375 5.984375,-3.921875 5.875,-3.921875 c -0.09375,0 -0.09375,0.03125 -0.140625,0.21875 -0.1875,0.625 -0.515625,1.375 -0.984375,1.96875 z m -0.65625,1.375 c -0.890625,0.765625 -1.65625,0.875 -2.046875,0.875 -0.59375,0 -0.90625,-0.453125 -0.90625,-1.09375 0,-0.484375 0.265625,-1.5625 0.578125,-2.0625 C 2.1875,-4 2.734375,-4.1875 3.078125,-4.1875 c 0.984375,0 0.984375,1.3125 0.984375,2.078125 0,0.375 0,0.953125 0.03125,1.125 z m 0,0" + id="id-f699c56d-2063-458c-b2ea-2d35db7fd3f3" /> + + + transform="translate(231.983,179.181)" + id="g8246"> + d="M 3.328125,-3.015625 C 3.390625,-3.265625 3.625,-4.1875 4.3125,-4.1875 c 0.046875,0 0.296875,0 0.5,0.125 C 4.53125,-4 4.34375,-3.765625 4.34375,-3.515625 c 0,0.15625 0.109375,0.34375 0.375,0.34375 0.21875,0 0.53125,-0.171875 0.53125,-0.578125 0,-0.515625 -0.578125,-0.65625 -0.921875,-0.65625 -0.578125,0 -0.921875,0.53125 -1.046875,0.75 -0.25,-0.65625 -0.78125,-0.75 -1.078125,-0.75 -1.03125,0 -1.609375,1.28125 -1.609375,1.53125 0,0.109375 0.109375,0.109375 0.125,0.109375 0.078125,0 0.109375,-0.03125 0.125,-0.109375 0.34375,-1.0625 1,-1.3125 1.34375,-1.3125 0.1875,0 0.53125,0.09375 0.53125,0.671875 0,0.3125 -0.171875,0.96875 -0.53125,2.375 -0.15625,0.609375 -0.515625,1.03125 -0.953125,1.03125 -0.0625,0 -0.28125,0 -0.5,-0.125 0.25,-0.0625 0.46875,-0.265625 0.46875,-0.546875 0,-0.265625 -0.21875,-0.34375 -0.359375,-0.34375 -0.3125,0 -0.546875,0.25 -0.546875,0.578125 0,0.453125 0.484375,0.65625 0.921875,0.65625 0.671875,0 1.03125,-0.703125 1.046875,-0.75 0.125,0.359375 0.484375,0.75 1.078125,0.75 1.03125,0 1.59375,-1.28125 1.59375,-1.53125 0,-0.109375 -0.078125,-0.109375 -0.109375,-0.109375 -0.09375,0 -0.109375,0.046875 -0.140625,0.109375 -0.328125,1.078125 -1,1.3125 -1.3125,1.3125 -0.390625,0 -0.546875,-0.3125 -0.546875,-0.65625 0,-0.21875 0.046875,-0.4375 0.15625,-0.875 z m 0,0" + id="id-99a0bbf4-08a2-4f3a-b6c1-7f8ca684be0b" /> + + + transform="translate(237.677,180.676)" + id="g8250"> + d="m 2.265625,-4.359375 c 0,-0.109375 -0.09375,-0.265625 -0.28125,-0.265625 -0.1875,0 -0.390625,0.1875 -0.390625,0.390625 0,0.109375 0.078125,0.265625 0.28125,0.265625 0.1875,0 0.390625,-0.203125 0.390625,-0.390625 z M 0.84375,-0.8125 c -0.03125,0.09375 -0.0625,0.171875 -0.0625,0.296875 0,0.328125 0.265625,0.578125 0.65625,0.578125 0.6875,0 1,-0.953125 1,-1.0625 0,-0.09375 -0.09375,-0.09375 -0.109375,-0.09375 -0.09375,0 -0.109375,0.046875 -0.140625,0.125 -0.15625,0.5625 -0.453125,0.84375 -0.734375,0.84375 -0.140625,0 -0.171875,-0.09375 -0.171875,-0.25 0,-0.15625 0.046875,-0.28125 0.109375,-0.4375 C 1.46875,-1 1.546875,-1.1875 1.609375,-1.375 1.671875,-1.546875 1.9375,-2.171875 1.953125,-2.265625 1.984375,-2.328125 2,-2.40625 2,-2.484375 2,-2.8125 1.71875,-3.078125 1.34375,-3.078125 0.640625,-3.078125 0.328125,-2.125 0.328125,-2 c 0,0.078125 0.09375,0.078125 0.125,0.078125 0.09375,0 0.09375,-0.03125 0.125,-0.109375 C 0.75,-2.625 1.0625,-2.875 1.3125,-2.875 c 0.109375,0 0.171875,0.046875 0.171875,0.234375 0,0.171875 -0.03125,0.265625 -0.203125,0.703125 z m 0,0" + id="id-cf07ff32-2ace-4010-bb88-70f446a2bc6f" /> + + + transform="translate(254.189,179.181)" + id="g8254"> + d="M 2.234375,-3.515625 V -6.09375 c 0,-0.234375 0,-0.359375 0.21875,-0.390625 C 2.546875,-6.5 2.84375,-6.5 3.046875,-6.5 c 0.890625,0 2,0.046875 2,1.484375 0,0.6875 -0.234375,1.5 -1.703125,1.5 z m 2.109375,0.125 C 5.296875,-3.625 6.078125,-4.234375 6.078125,-5.015625 6.078125,-5.96875 4.9375,-6.8125 3.484375,-6.8125 H 0.34375 V -6.5 h 0.25 c 0.765625,0 0.78125,0.109375 0.78125,0.46875 v 5.25 c 0,0.359375 -0.015625,0.46875 -0.78125,0.46875 h -0.25 V 0 c 0.359375,-0.03125 1.078125,-0.03125 1.453125,-0.03125 0.390625,0 1.109375,0 1.46875,0.03125 v -0.3125 h -0.25 c -0.765625,0 -0.78125,-0.109375 -0.78125,-0.46875 V -3.296875 H 3.375 c 0.15625,0 0.578125,0 0.9375,0.34375 0.375,0.34375 0.375,0.65625 0.375,1.328125 0,0.640625 0,1.046875 0.40625,1.421875 0.40625,0.359375 0.953125,0.421875 1.25,0.421875 0.78125,0 0.953125,-0.8125 0.953125,-1.09375 0,-0.0625 0,-0.171875 -0.125,-0.171875 -0.109375,0 -0.109375,0.09375 -0.125,0.15625 C 6.984375,-0.171875 6.640625,0 6.390625,0 5.90625,0 5.828125,-0.515625 5.6875,-1.4375 L 5.546875,-2.234375 C 5.375,-2.875 4.890625,-3.203125 4.34375,-3.390625 Z m 0,0" + id="id-150d0f94-f582-4f17-bdf3-da1882451c39" /> + transform="translate(261.522,179.181)" + id="g8257"> + d="M 1.109375,-2.515625 C 1.171875,-4 2.015625,-4.25 2.359375,-4.25 c 1.015625,0 1.125,1.34375 1.125,1.734375 z m 0,0.21875 h 2.78125 c 0.21875,0 0.25,0 0.25,-0.21875 0,-0.984375 -0.546875,-1.953125 -1.78125,-1.953125 -1.15625,0 -2.078125,1.03125 -2.078125,2.28125 0,1.328125 1.046875,2.296875 2.1875,2.296875 C 3.6875,0.109375 4.140625,-1 4.140625,-1.1875 4.140625,-1.28125 4.0625,-1.3125 4,-1.3125 c -0.078125,0 -0.109375,0.0625 -0.125,0.140625 -0.34375,1.03125 -1.25,1.03125 -1.34375,1.03125 -0.5,0 -0.890625,-0.296875 -1.125,-0.671875 -0.296875,-0.46875 -0.296875,-1.125 -0.296875,-1.484375 z m 0,0" + id="id-c2e5441f-802a-486c-ab5b-9891bd4d5aff" /> + transform="translate(265.95,179.181)" + id="g8260"> + d="m 3.3125,-0.75 c 0.046875,0.390625 0.3125,0.8125 0.78125,0.8125 0.21875,0 0.828125,-0.140625 0.828125,-0.953125 v -0.5625 h -0.25 v 0.5625 c 0,0.578125 -0.25,0.640625 -0.359375,0.640625 -0.328125,0 -0.375,-0.453125 -0.375,-0.5 v -1.984375 c 0,-0.421875 0,-0.8125 -0.359375,-1.1875 C 3.1875,-4.3125 2.6875,-4.46875 2.21875,-4.46875 c -0.828125,0 -1.515625,0.46875 -1.515625,1.125 0,0.296875 0.203125,0.46875 0.46875,0.46875 0.28125,0 0.453125,-0.203125 0.453125,-0.453125 0,-0.125 -0.046875,-0.453125 -0.515625,-0.453125 C 1.390625,-4.140625 1.875,-4.25 2.1875,-4.25 c 0.5,0 1.0625,0.390625 1.0625,1.28125 v 0.359375 c -0.515625,0.03125 -1.203125,0.0625 -1.828125,0.359375 -0.75,0.34375 -1,0.859375 -1,1.296875 0,0.8125 0.96875,1.0625 1.59375,1.0625 0.65625,0 1.109375,-0.40625 1.296875,-0.859375 z M 3.25,-2.390625 v 1 c 0,0.9375 -0.71875,1.28125 -1.171875,1.28125 -0.484375,0 -0.890625,-0.34375 -0.890625,-0.84375 0,-0.546875 0.421875,-1.375 2.0625,-1.4375 z m 0,0" + id="id-692d1d99-7863-4951-9f89-878aa5b83625" /> + transform="translate(270.931,179.181)" + id="g8263"> + d="M 1.765625,-6.921875 0.328125,-6.8125 V -6.5 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 C 0.65625,-0.015625 1.1875,-0.03125 1.4375,-0.03125 c 0.25,0 0.734375,0.015625 1.109375,0.03125 v -0.3125 c -0.671875,0 -0.78125,0 -0.78125,-0.4375 z m 0,0" + id="id-0de5f172-90a1-4897-842e-bff1c8e6ee97" /> + id="id-c0869544-46ec-4eae-a661-bac16838dac9"> + transform="translate(277.016,179.181)" + id="g8267"> + id="id-0517ee75-14bc-4c78-b2a0-2c99ec360bd5" /> - - + transform="translate(281.444,179.181)" + id="g8270"> + id="id-7e373297-496a-4f3a-84c5-7bf5af444adb" /> + id="id-e5a32e64-1a43-49ac-9a07-e0c3dd738d98"> + transform="translate(286.704,179.181)" + id="g8274"> + id="id-da91b9da-bf93-4b8a-8205-2c6e0e99e2a4" /> + transform="translate(291.685,179.181)" + id="g8277"> + id="id-c1aec226-e6c0-484e-8865-44dc91ae8c50" /> + transform="translate(295.588,179.181)" + id="g8280"> + id="id-30ebfd7f-749b-483a-80ec-0c3e01c1662e" /> + transform="translate(301.123,179.181)" + id="g8283"> + id="id-27867fa5-1891-44ab-9a30-e9ef37e614be" /> + transform="translate(303.89,179.181)" + id="g8286"> + id="id-d168d2da-14e6-4ea4-ab56-33ed3eb630ca" /> + transform="translate(309.426,179.181)" + id="g8289"> + id="id-4c15d512-097c-4630-8b01-ca70d1dcf31d" /> + transform="translate(314.407,179.181)" + id="g8292"> + id="id-51e6e9c9-2488-49b3-aeb0-02272ed22463" /> + transform="translate(318.281,179.181)" + id="g8295"> + id="id-83efe587-a2f7-4f9e-a3c2-939b741f85a0" /> + + + transform="translate(326.031,179.181)" + id="g8299"> + d="m 2.828125,-6.234375 c 0,-0.203125 -0.140625,-0.359375 -0.359375,-0.359375 -0.28125,0 -0.546875,0.265625 -0.546875,0.53125 0,0.1875 0.140625,0.359375 0.375,0.359375 0.234375,0 0.53125,-0.234375 0.53125,-0.53125 z m -0.75,3.75 c 0.109375,-0.28125 0.109375,-0.3125 0.21875,-0.578125 0.078125,-0.203125 0.125,-0.34375 0.125,-0.53125 0,-0.4375 -0.3125,-0.8125 -0.8125,-0.8125 -0.9375,0 -1.3125,1.453125 -1.3125,1.53125 0,0.109375 0.09375,0.109375 0.109375,0.109375 0.109375,0 0.109375,-0.03125 0.15625,-0.1875 0.28125,-0.9375 0.671875,-1.234375 1.015625,-1.234375 0.078125,0 0.25,0 0.25,0.3125 0,0.21875 -0.078125,0.421875 -0.109375,0.53125 -0.078125,0.25 -0.53125,1.40625 -0.6875,1.84375 -0.109375,0.25 -0.234375,0.578125 -0.234375,0.796875 0,0.46875 0.34375,0.8125 0.8125,0.8125 0.9375,0 1.3125,-1.4375 1.3125,-1.53125 0,-0.109375 -0.09375,-0.109375 -0.125,-0.109375 -0.09375,0 -0.09375,0.03125 -0.140625,0.1875 -0.1875,0.625 -0.515625,1.234375 -1.015625,1.234375 -0.171875,0 -0.25,-0.09375 -0.25,-0.328125 0,-0.25 0.0625,-0.390625 0.296875,-1 z m 0,0" + id="id-1c942f0e-88d3-4f50-8456-52b4f13a5454" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="fill:#000000;fill-opacity:1" + id="id-1535c85d-b147-4556-ab34-a783297e0cb5"> + - - + style="stroke:none;stroke-width:0" + d="M 2.5,-6.921875 1.15625,-5.5625 1.328125,-5.390625 2.5,-6.40625 3.640625,-5.390625 3.8125,-5.5625 Z m 0,0" + id="id-10ac5f40-cf20-462d-b838-805fe7ad0f95" /> + + + + - - + style="stroke:none;stroke-width:0" + d="m 5.34375,-4 c -0.359375,0.109375 -0.53125,0.4375 -0.53125,0.6875 0,0.21875 0.15625,0.46875 0.484375,0.46875 0.359375,0 0.71875,-0.296875 0.71875,-0.78125 C 6.015625,-4.15625 5.5,-4.5 4.890625,-4.5 4.3125,-4.5 3.953125,-4.078125 3.8125,-3.890625 3.5625,-4.3125 3.015625,-4.5 2.4375,-4.5 c -1.25,0 -1.921875,1.21875 -1.921875,1.546875 0,0.140625 0.140625,0.140625 0.234375,0.140625 0.125,0 0.1875,0 0.234375,-0.125 0.28125,-0.90625 1,-1.203125 1.40625,-1.203125 0.375,0 0.5625,0.171875 0.5625,0.484375 0,0.203125 -0.140625,0.75 -0.234375,1.109375 L 2.375,-1.1875 c -0.140625,0.609375 -0.5,0.90625 -0.84375,0.90625 -0.046875,0 -0.28125,0 -0.46875,-0.140625 0.359375,-0.109375 0.53125,-0.453125 0.53125,-0.6875 0,-0.21875 -0.171875,-0.46875 -0.5,-0.46875 -0.34375,0 -0.71875,0.296875 -0.71875,0.78125 0,0.53125 0.53125,0.875 1.140625,0.875 0.5625,0 0.9375,-0.421875 1.0625,-0.609375 0.25,0.421875 0.8125,0.609375 1.375,0.609375 1.265625,0 1.9375,-1.21875 1.9375,-1.546875 0,-0.140625 -0.15625,-0.140625 -0.234375,-0.140625 -0.125,0 -0.1875,0 -0.234375,0.125 -0.28125,0.90625 -1,1.203125 -1.421875,1.203125 -0.375,0 -0.546875,-0.171875 -0.546875,-0.5 0,-0.203125 0.125,-0.734375 0.21875,-1.109375 0.0625,-0.25 0.296875,-1.1875 0.34375,-1.34375 0.15625,-0.609375 0.5,-0.90625 0.84375,-0.90625 0.0625,0 0.28125,0 0.484375,0.140625 z m 0,0" + id="id-3c104b3c-9cd9-4eb9-9732-d549110240b9" /> + + + + - + style="stroke:none;stroke-width:0" + d="m 3.8125,-0.984375 c 0.640625,-0.65625 0.890625,-1.59375 0.890625,-1.65625 0,-0.09375 -0.078125,-0.09375 -0.109375,-0.09375 C 4.5,-2.734375 4.5,-2.71875 4.453125,-2.5625 4.328125,-2.109375 4.09375,-1.671875 3.796875,-1.296875 3.796875,-1.40625 3.78125,-1.875 3.765625,-1.9375 3.65625,-2.625 3.140625,-3.078125 2.4375,-3.078125 c -1.015625,0 -2,0.953125 -2,1.9375 0,0.640625 0.46875,1.203125 1.265625,1.203125 0.640625,0 1.21875,-0.28125 1.609375,-0.578125 0.171875,0.5 0.515625,0.578125 0.734375,0.578125 0.40625,0 0.65625,-0.328125 0.65625,-0.484375 C 4.703125,-0.5 4.609375,-0.5 4.578125,-0.5 c -0.09375,0 -0.109375,0.03125 -0.125,0.0625 -0.09375,0.265625 -0.3125,0.3125 -0.390625,0.3125 -0.09375,0 -0.21875,0 -0.25,-0.859375 z M 3.265625,-0.75 C 2.578125,-0.1875 2,-0.125 1.734375,-0.125 c -0.46875,0 -0.71875,-0.3125 -0.71875,-0.78125 0,-0.1875 0.09375,-0.953125 0.453125,-1.4375 C 1.796875,-2.765625 2.1875,-2.875 2.4375,-2.875 c 0.546875,0 0.71875,0.53125 0.765625,0.96875 0.046875,0.296875 0.03125,0.796875 0.0625,1.15625 z m 0,0" + id="id-9365e058-f61e-46d9-a219-da7090ec0b9c" /> + - - + id="id-4d2d98ea-bc96-468b-a4e4-1f59ac7fd4d8"> + transform="translate(254.189,191.137)" + id="g8315"> + id="id-ee0eae7d-aafd-44c0-a691-929b638e1830" /> + transform="translate(261.522,191.137)" + id="g8318"> + id="id-867d9392-4e7e-4823-8bb5-a9f9a413eca6" /> + transform="translate(265.95,191.137)" + id="g8321"> + id="id-93d34c5f-12c4-4b67-a843-a4671113c35a" /> + transform="translate(270.931,191.137)" + id="g8324"> + id="id-1e6ca4cb-d874-4da8-b52b-c6b4f17f761b" /> - - - + id="id-d68b3e3e-95be-4d63-b6e2-9dfe71315d12"> + transform="translate(277.016,191.137)" + id="g8328"> + style="stroke:none;stroke-width:0" + d="m 1.71875,-3.75 v -0.65625 l -1.4375,0.109375 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5 v 4.65625 C 1.0625,1.625 0.953125,1.625 0.28125,1.625 V 1.9375 C 0.625,1.921875 1.140625,1.90625 1.390625,1.90625 c 0.28125,0 0.78125,0.015625 1.125,0.03125 V 1.625 C 1.859375,1.625 1.75,1.625 1.75,1.171875 V -0.59375 c 0.046875,0.171875 0.46875,0.703125 1.21875,0.703125 1.1875,0 2.21875,-0.984375 2.21875,-2.265625 0,-1.265625 -0.953125,-2.25 -2.078125,-2.25 -0.78125,0 -1.203125,0.4375 -1.390625,0.65625 z M 1.75,-1.140625 v -2.21875 C 2.03125,-3.875 2.515625,-4.15625 3.03125,-4.15625 c 0.734375,0 1.328125,0.875 1.328125,2 0,1.203125 -0.6875,2.046875 -1.421875,2.046875 -0.40625,0 -0.78125,-0.203125 -1.046875,-0.609375 C 1.75,-0.921875 1.75,-0.9375 1.75,-1.140625 Z m 0,0" + id="id-b672f3a2-646d-4d2f-b7e9-fa0bef7bbc66" /> + id="id-2a2daca9-d900-4bb6-8b84-f4de897fd4e6"> + transform="translate(282.83,191.137)" + id="g8332"> - - - + id="id-d044561c-8602-4e07-ab0d-aada7dde496d" /> + transform="translate(287.812,191.137)" + id="g8335"> + style="stroke:none;stroke-width:0" + d="m 2.078125,-1.9375 c 0.21875,0.046875 1.03125,0.203125 1.03125,0.921875 0,0.5 -0.34375,0.90625 -1.125,0.90625 -0.84375,0 -1.203125,-0.5625 -1.390625,-1.421875 C 0.5625,-1.65625 0.5625,-1.6875 0.453125,-1.6875 c -0.125,0 -0.125,0.0625 -0.125,0.234375 V -0.125 c 0,0.171875 0,0.234375 0.109375,0.234375 0.046875,0 0.0625,-0.015625 0.25,-0.203125 0.015625,-0.015625 0.015625,-0.03125 0.203125,-0.21875 0.4375,0.40625 0.890625,0.421875 1.09375,0.421875 1.140625,0 1.609375,-0.671875 1.609375,-1.390625 0,-0.515625 -0.296875,-0.828125 -0.421875,-0.9375 C 2.84375,-2.546875 2.453125,-2.625 2.03125,-2.703125 1.46875,-2.8125 0.8125,-2.9375 0.8125,-3.515625 c 0,-0.359375 0.25,-0.765625 1.109375,-0.765625 1.09375,0 1.15625,0.90625 1.171875,1.203125 0,0.09375 0.09375,0.09375 0.109375,0.09375 0.140625,0 0.140625,-0.046875 0.140625,-0.234375 v -1.015625 c 0,-0.15625 0,-0.234375 -0.109375,-0.234375 -0.046875,0 -0.078125,0 -0.203125,0.125 -0.03125,0.03125 -0.125,0.125 -0.171875,0.15625 -0.375,-0.28125 -0.78125,-0.28125 -0.9375,-0.28125 -1.21875,0 -1.59375,0.671875 -1.59375,1.234375 0,0.34375 0.15625,0.625 0.421875,0.84375 0.328125,0.25 0.609375,0.3125 1.328125,0.453125 z m 0,0" + id="id-79f93dae-6f67-4e74-9357-df605184e3cb" /> + transform="translate(291.741,191.137)" + id="g8338"> - - - - - - + id="id-6d2a8b49-705c-4236-a728-799a0ee29b97" /> + transform="translate(294.509,191.137)" + id="g8341"> + id="id-a66412af-6df2-46c0-9002-d5d70bcb84e8" /> + transform="translate(298.383,191.137)" + id="g8344"> + style="stroke:none;stroke-width:0" + d="M 1.765625,-4.40625 0.375,-4.296875 v 0.3125 c 0.640625,0 0.734375,0.0625 0.734375,0.546875 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 C 0.640625,-0.015625 1.1875,-0.03125 1.421875,-0.03125 1.78125,-0.03125 2.125,-0.015625 2.46875,0 v -0.3125 c -0.671875,0 -0.703125,-0.046875 -0.703125,-0.4375 z m 0.03125,-1.734375 c 0,-0.3125 -0.234375,-0.53125 -0.515625,-0.53125 -0.3125,0 -0.53125,0.265625 -0.53125,0.53125 0,0.265625 0.21875,0.53125 0.53125,0.53125 0.28125,0 0.515625,-0.21875 0.515625,-0.53125 z m 0,0" + id="id-23d8c37f-e12a-431c-ad8d-decb6cc9da04" /> - - - - - - - - - - - - - - - + transform="translate(301.151,191.137)" + id="g8347"> - - - - - - - - - - - + id="id-9eec9787-b2ed-462f-9dfd-52d85d6428b0" /> + + - + id="id-2c59c728-62c3-47a9-93ff-f11cc36155ab" /> + - - - - - + id="id-ad9a1a2f-957d-4836-968f-fd1b00a89254"> + transform="translate(314.985,191.137)" + id="g8354"> + id="id-ed7aedaf-c7ea-432b-a4b4-1397233179e1" /> + transform="translate(319.966,191.137)" + id="g8357"> + style="stroke:none;stroke-width:0" + d="m 1.75,-4.296875 v -1.15625 c 0,-0.875 0.46875,-1.359375 0.90625,-1.359375 0.03125,0 0.1875,0 0.328125,0.078125 C 2.875,-6.703125 2.6875,-6.5625 2.6875,-6.3125 c 0,0.21875 0.15625,0.421875 0.4375,0.421875 0.28125,0 0.4375,-0.203125 0.4375,-0.4375 0,-0.375 -0.375,-0.703125 -0.90625,-0.703125 -0.6875,0 -1.546875,0.53125 -1.546875,1.59375 v 1.140625 h -0.78125 v 0.3125 h 0.78125 V -0.75 C 1.109375,-0.3125 1,-0.3125 0.34375,-0.3125 V 0 c 0.390625,-0.015625 0.859375,-0.03125 1.125,-0.03125 0.40625,0 0.875,0 1.265625,0.03125 V -0.3125 H 2.53125 c -0.734375,0 -0.75,-0.109375 -0.75,-0.46875 v -3.203125 h 1.125 v -0.3125 z m 0,0" + id="id-2c6007b0-ebce-4ff7-816c-d412bbf7220e" /> + + + transform="translate(326.338,191.137)" + id="g8361"> + style="stroke:none;stroke-width:0" + d="M 1.09375,-3.421875 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.359375,-0.015625 0.859375,-0.03125 1.140625,-0.03125 0.25,0 0.765625,0.015625 1.109375,0.03125 v -0.3125 c -0.671875,0 -0.78125,0 -0.78125,-0.4375 V -2.59375 C 1.78125,-3.625 2.5,-4.1875 3.125,-4.1875 c 0.640625,0 0.75,0.53125 0.75,1.109375 V -0.75 c 0,0.4375 -0.109375,0.4375 -0.78125,0.4375 V 0 c 0.34375,-0.015625 0.859375,-0.03125 1.125,-0.03125 0.25,0 0.78125,0.015625 1.109375,0.03125 v -0.3125 c -0.515625,0 -0.765625,0 -0.765625,-0.296875 v -1.90625 c 0,-0.859375 0,-1.15625 -0.3125,-1.515625 -0.140625,-0.171875 -0.46875,-0.375 -1.046875,-0.375 C 2.46875,-4.40625 2,-3.984375 1.71875,-3.359375 V -4.40625 L 0.3125,-4.296875 v 0.3125 c 0.703125,0 0.78125,0.0625 0.78125,0.5625 z m 0,0" + id="id-4227d877-23fb-4915-aed9-f9f204f41c8b" /> + transform="translate(331.873,191.137)" + id="g8364"> + style="stroke:none;stroke-width:0" + d="M 4.6875,-2.140625 C 4.6875,-3.40625 3.703125,-4.46875 2.5,-4.46875 c -1.25,0 -2.21875,1.09375 -2.21875,2.328125 0,1.296875 1.03125,2.25 2.203125,2.25 1.203125,0 2.203125,-0.984375 2.203125,-2.25 z m -2.1875,2 c -0.4375,0 -0.875,-0.203125 -1.140625,-0.671875 -0.25,-0.4375 -0.25,-1.046875 -0.25,-1.40625 0,-0.390625 0,-0.921875 0.234375,-1.359375 C 1.609375,-4.03125 2.078125,-4.25 2.484375,-4.25 c 0.4375,0 0.859375,0.21875 1.125,0.65625 0.265625,0.421875 0.265625,1 0.265625,1.375 0,0.359375 0,0.90625 -0.21875,1.34375 C 3.421875,-0.421875 2.984375,-0.140625 2.5,-0.140625 Z m 0,0" + id="id-058b2714-0322-4ce5-b128-0ced3d7e6ae9" /> + + + transform="translate(337.124,191.137)" + id="g8368"> + style="stroke:none;stroke-width:0" + d="m 3.78125,-0.546875 v 0.65625 L 5.25,0 v -0.3125 c -0.6875,0 -0.78125,-0.0625 -0.78125,-0.5625 V -6.921875 L 3.046875,-6.8125 V -6.5 c 0.6875,0 0.765625,0.0625 0.765625,0.5625 v 2.15625 c -0.28125,-0.359375 -0.71875,-0.625 -1.25,-0.625 -1.171875,0 -2.21875,0.984375 -2.21875,2.265625 0,1.265625 0.96875,2.25 2.109375,2.25 0.640625,0 1.078125,-0.34375 1.328125,-0.65625 z m 0,-2.671875 v 2.046875 c 0,0.171875 0,0.1875 -0.109375,0.359375 C 3.375,-0.328125 2.9375,-0.109375 2.5,-0.109375 2.046875,-0.109375 1.6875,-0.375 1.453125,-0.75 1.203125,-1.15625 1.171875,-1.71875 1.171875,-2.140625 1.171875,-2.5 1.1875,-3.09375 1.46875,-3.546875 1.6875,-3.859375 2.0625,-4.1875 2.609375,-4.1875 c 0.34375,0 0.765625,0.15625 1.0625,0.59375 0.109375,0.171875 0.109375,0.1875 0.109375,0.375 z m 0,0" + id="id-2d0214e4-555b-4c0c-be95-04c62486a47a" /> + transform="translate(342.659,191.137)" + id="g8371"> + style="stroke:none;stroke-width:0" + d="M 1.109375,-2.515625 C 1.171875,-4 2.015625,-4.25 2.359375,-4.25 c 1.015625,0 1.125,1.34375 1.125,1.734375 z m 0,0.21875 h 2.78125 c 0.21875,0 0.25,0 0.25,-0.21875 0,-0.984375 -0.546875,-1.953125 -1.78125,-1.953125 -1.15625,0 -2.078125,1.03125 -2.078125,2.28125 0,1.328125 1.046875,2.296875 2.1875,2.296875 C 3.6875,0.109375 4.140625,-1 4.140625,-1.1875 4.140625,-1.28125 4.0625,-1.3125 4,-1.3125 c -0.078125,0 -0.109375,0.0625 -0.125,0.140625 -0.34375,1.03125 -1.25,1.03125 -1.34375,1.03125 -0.5,0 -0.890625,-0.296875 -1.125,-0.671875 -0.296875,-0.46875 -0.296875,-1.125 -0.296875,-1.484375 z m 0,0" + id="id-4dafd578-c0a2-4fb1-b797-3aa30eb2603f" /> + + + transform="translate(350.411,191.137)" + id="g8375"> + style="stroke:none;stroke-width:0" + d="m 4.75,-2.359375 c 0,-1.5625 -0.921875,-2.046875 -1.65625,-2.046875 -1.375,0 -2.6875,1.421875 -2.6875,2.828125 0,0.9375 0.59375,1.6875 1.625,1.6875 0.625,0 1.34375,-0.234375 2.09375,-0.84375 0.125,0.53125 0.453125,0.84375 0.90625,0.84375 0.53125,0 0.84375,-0.546875 0.84375,-0.703125 0,-0.078125 -0.0625,-0.109375 -0.125,-0.109375 -0.0625,0 -0.09375,0.03125 -0.125,0.109375 -0.1875,0.484375 -0.546875,0.484375 -0.5625,0.484375 -0.3125,0 -0.3125,-0.78125 -0.3125,-1.015625 0,-0.203125 0,-0.234375 0.109375,-0.34375 C 5.796875,-2.65625 6,-3.8125 6,-3.8125 6,-3.84375 5.984375,-3.921875 5.875,-3.921875 c -0.09375,0 -0.09375,0.03125 -0.140625,0.21875 -0.1875,0.625 -0.515625,1.375 -0.984375,1.96875 z m -0.65625,1.375 c -0.890625,0.765625 -1.65625,0.875 -2.046875,0.875 -0.59375,0 -0.90625,-0.453125 -0.90625,-1.09375 0,-0.484375 0.265625,-1.5625 0.578125,-2.0625 C 2.1875,-4 2.734375,-4.1875 3.078125,-4.1875 c 0.984375,0 0.984375,1.3125 0.984375,2.078125 0,0.375 0,0.953125 0.03125,1.125 z m 0,0" + id="id-b6f5d68d-a184-43ca-a866-f045fdcc57a1" /> - - diff --git a/docs/src/devdocs/mapping.md b/docs/src/devdocs/mapping.md index 0d9dca4302..8cf8dba0fd 100644 --- a/docs/src/devdocs/mapping.md +++ b/docs/src/devdocs/mapping.md @@ -8,8 +8,8 @@ The geometric mapping of a finite element from the reference coordinates to the This mapping is given by the geometric shape functions, $\hat{N}_i^g(\boldsymbol{\xi})$, such that ```math \begin{align*} - \boldsymbol{x}(\boldsymbol{\xi}) =& \sum_{i}^N \hat{\boldsymbol{x}}_i \hat{N}_i^g(\boldsymbol{\xi}) \\ - \boldsymbol{J} :=& \frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}\boldsymbol{\xi}} = \sum_{i}^N \hat{\boldsymbol{x}}_i \otimes \frac{\mathrm{d} \hat{N}_i^g}{\mathrm{d}\boldsymbol{\xi}}\\ + \boldsymbol{x}(\boldsymbol{\xi}) =& \sum_{\alpha=1}^N \hat{\boldsymbol{x}}_\alpha \hat{N}_\alpha^g(\boldsymbol{\xi}) \\ + \boldsymbol{J} :=& \frac{\mathrm{d}\boldsymbol{x}}{\mathrm{d}\boldsymbol{\xi}} = \sum_{\alpha=1}^N \hat{\boldsymbol{x}}_\alpha \otimes \frac{\mathrm{d} \hat{N}_\alpha^g}{\mathrm{d}\boldsymbol{\xi}}\\ \boldsymbol{\mathcal{H}} :=& \frac{\mathrm{d} \boldsymbol{J}}{\mathrm{d} \boldsymbol{\xi}} = \sum_{\alpha=1}^N \hat{\boldsymbol{x}}_\alpha \otimes \frac{\mathrm{d}^2 \hat{N}^g_\alpha}{\mathrm{d} \boldsymbol{\xi}^2} \end{align*} From 2c67048f2b9b101c442a956761a585d3c17690df Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 17:37:09 +0200 Subject: [PATCH 052/172] Add white background to make visible in dark mode --- docs/src/assets/fe_mapping.svg | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/src/assets/fe_mapping.svg b/docs/src/assets/fe_mapping.svg index 2933e1b945..a377c77528 100644 --- a/docs/src/assets/fe_mapping.svg +++ b/docs/src/assets/fe_mapping.svg @@ -3,8 +3,8 @@ + + + Date: Fri, 13 Oct 2023 17:55:33 +0200 Subject: [PATCH 053/172] Add error if cell===nothing for non-identity mappings --- src/FEValues/cell_values.jl | 6 +++++- src/FEValues/face_values.jl | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/FEValues/cell_values.jl b/src/FEValues/cell_values.jl index f57c05d145..9a507e8830 100644 --- a/src/FEValues/cell_values.jl +++ b/src/FEValues/cell_values.jl @@ -81,10 +81,14 @@ end getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) - check_reinit_sdim_consistency(:CellValues, shape_gradient_type(cv), eltype(x)) geo_mapping = cv.geo_mapping fun_values = cv.fun_values n_geom_basefuncs = getngeobasefunctions(geo_mapping) + + check_reinit_sdim_consistency(:CellValues, shape_gradient_type(cv), eltype(x)) + if cell === nothing && !isa(get_mapping_type(fun_values), IdentityMapping) + throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) + end if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs throw_incompatible_coord_length(length(x), n_geom_basefuncs) end diff --git a/src/FEValues/face_values.jl b/src/FEValues/face_values.jl index a2ae02c175..acd32c932b 100644 --- a/src/FEValues/face_values.jl +++ b/src/FEValues/face_values.jl @@ -109,14 +109,22 @@ end function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, cell=nothing) where {dim, T} check_reinit_sdim_consistency(:FaceValues, shape_gradient_type(fv), eltype(x)) - @boundscheck checkface(fv, face_nr) + checkface(fv, face_nr) + fv.current_face[] = face_nr + n_geom_basefuncs = getngeobasefunctions(fv) - length(x) == n_geom_basefuncs || throw_incompatible_coord_length(length(x), n_geom_basefuncs) + if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs + throw_incompatible_coord_length(length(x), n_geom_basefuncs) + end - fv.current_face[] = face_nr - + # Must be done after setting current face, which should be done after checkface geo_mapping = get_geo_mapping(fv) fun_values = get_fun_values(fv) + + if cell === nothing && !isa(get_mapping_type(fun_values), IdentityMapping) + throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) + end + @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) mapping = calculate_mapping(geo_mapping, q_point, x) J = getjacobian(mapping) From 35e87903494733652d1b5ace0352ac8bdd325568 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 19:51:22 +0200 Subject: [PATCH 054/172] Move mapping docs to topics and add walkthrough of SimpleCellValues --- docs/make.jl | 2 + docs/src/assets/references.bib | 11 ++- docs/src/devdocs/index.md | 2 +- .../mapping.md => topics/FEValues.md} | 34 ++++++- docs/src/topics/SimpleCellValues.jl | 98 +++++++++++++++++++ 5 files changed, 141 insertions(+), 6 deletions(-) rename docs/src/{devdocs/mapping.md => topics/FEValues.md} (74%) create mode 100644 docs/src/topics/SimpleCellValues.jl diff --git a/docs/make.jl b/docs/make.jl index fb4338b7a3..ced312cb19 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -63,6 +63,8 @@ bibtex_plugin = CitationBibliography( "Topic guides" => [ "Topic guide overview" => "topics/index.md", "topics/fe_intro.md", + #"topics/interpolations.md", + "topics/FEValues.md", "topics/degrees_of_freedom.md", "topics/assembly.md", "topics/boundary_conditions.md", diff --git a/docs/src/assets/references.bib b/docs/src/assets/references.bib index 2366a92481..a237a86296 100644 --- a/docs/src/assets/references.bib +++ b/docs/src/assets/references.bib @@ -60,6 +60,15 @@ @article{Scroggs2022 @phdthesis{Cenanovic2017, author={Mirza Cenanovic}, title={Finite element methods for surface problems}, - school={J\o{}pk\o{}ping University, School of Engineering}, + school={Jönköping University, School of Engineering}, year=2017 } +@misc{Kirby2017, + title={A general approach to transforming finite elements}, + author={Robert C. Kirby}, + year={2017}, + eprint={1706.09017}, + doi={10.48550/arXiv.1706.09017}, + archivePrefix={arXiv}, + primaryClass={math.NA} +} \ No newline at end of file diff --git a/docs/src/devdocs/index.md b/docs/src/devdocs/index.md index 75b50844c6..3560eff4a1 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", "mapping.md", "dofhandler.md", "performance.md"] +Pages = ["reference_cells.md", "interpolations.md", "elements.md", "dofhandler.md", "performance.md"] ``` diff --git a/docs/src/devdocs/mapping.md b/docs/src/topics/FEValues.md similarity index 74% rename from docs/src/devdocs/mapping.md rename to docs/src/topics/FEValues.md index 8cf8dba0fd..2273c528d2 100644 --- a/docs/src/devdocs/mapping.md +++ b/docs/src/topics/FEValues.md @@ -1,4 +1,7 @@ -# Mapping of finite elements +# FEValues +A key type of object in `Ferrite.jl` are the so-called `FEValues`, where the most common ones are `CellValues` and `FaceValues`. 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 [`FaceValues`](@ref). In order for these values to be correct, it is necessary to reinitialize these for the current cell by using the [`reinit!`](@ref) function. This maps the values from the reference cell to the actual cell, a process which is described in detail below, see [Mapping of finite elements](@ref mapping_theory). Thereafter, we show an implementation of a [`SimpleCellValues`](@ref SimpleCellValues) type for the most standard case, excluding the generalizations and optimization that complicates the code for e.g. `CellValues`. + +## [Mapping of finite elements](@id mapping_theory) The shape functions and gradients stored in an `FEValues` object, is reinitialized for each cell by calling the `reinit!` function. The main part of this calculation, considers how to map the functions described on the reference cell, to the actual cell. The geometric mapping of a finite element from the reference coordinates to the real coordinates is shown in the following illustration. @@ -26,7 +29,7 @@ We require that the mapping from reference coordinates to real coordinates is [d ``` Depending on the function interpolation, we may want different types of mappings to conserve certain properties of the fields. This results in the different mapping types described below. -## Identity mapping +### Identity mapping `Ferrite.IdentityMapping` For scalar fields, we always use scalar base functions. For tensorial fields (non-scalar, e.g. vector-fields), the base functions can be constructed from scalar base functions, by using e.g. `VectorizedInterpolation`. From the perspective of the mapping, however, each component is mapped as an individual scalar base function. And for scalar base functions, we only require that the value of the base function is invariant to the element shape (real coordinate), and only depends on the reference coordinate, i.e. @@ -37,7 +40,7 @@ For scalar fields, we always use scalar base functions. For tensorial fields (no \end{align*} ``` -## Covariant Piola mapping, H(curl) +### Covariant Piola mapping, H(curl) `Ferrite.CovariantPiolaMapping` The covariant Piola mapping of a vectorial base function preserves the tangential components. For the value, the mapping is defined as @@ -69,7 +72,7 @@ which yields the gradient, \end{align*} ``` -## Contravariant Piola mapping, H(div) +### Contravariant Piola mapping, H(div) `Ferrite.ContravariantPiolaMapping` The covariant Piola mapping of a vectorial base function preserves the normal components. For the value, the mapping is defined as @@ -101,3 +104,26 @@ This gives the gradient \end{align*} ``` +## [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. +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 +# Include the example here, but modify the Literate output to suit being embedded +using Literate, Markdown +filename = "SimpleCellValues" +Literate.markdown(filename*".jl"; execute=true) +contents = read(filename*".md", String) +Literate.script(filename*".jl"; name="SimpleCellValues") +rm(filename*".jl") +rm(filename*".md") +header_end = last(findnext("```", contents, 4))+1 +Markdown.parse(replace(contents[header_end:end], + "*This page was generated using [Literate.jl]"=>"*This example was generated using [Literate.jl]") + ) +``` + +## Further reading +* [defelement.com](https://defelement.com/ciarlet.html#Mapping+finite+elements) +* Kirby (2017) [Kirby2017](@cite) diff --git a/docs/src/topics/SimpleCellValues.jl b/docs/src/topics/SimpleCellValues.jl new file mode 100644 index 0000000000..968c5b8a61 --- /dev/null +++ b/docs/src/topics/SimpleCellValues.jl @@ -0,0 +1,98 @@ +# We start by including `Ferrite` and `Test`, +# to allow us to verify our implementation. +using Ferrite, Test + +# Define a simple version of the cell values object, which only supports +# identity mapping of scalar interpolations, without embedding. +struct SimpleCellValues{T, dim} <: Ferrite.AbstractCellValues + N::Matrix{T} # Precalculated shape values + dNdξ::Matrix{Vec{dim,T}} # Precalculated shape gradients in the reference domain + dNdx::Matrix{Vec{dim,T}} # Cache for shape gradients in the real domain + M::Matrix{T} # Precalculated geometric shape values + dMdξ::Matrix{Vec{dim,T}} # Precalculated geometric shape gradients + weights::Vector{T} # Given quadrature weights in the reference domain + detJdV::Vector{T} # Cache for quadrature weights in the real domain +end; + +# To make it easier to initiate this struct, we create a constructor function +# with the same input as `CellValues`. However, we skip some consistency checking here. +function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Interpolation, T=Float64) + dim = Ferrite.getdim(ip_fun) + ## Quadrature weights and coordinates (in reference cell) + weights = Ferrite.getweights(qr) + n_qpoints = length(weights) + + ## Function interpolation + n_func_basefuncs = getnbasefunctions(ip_fun) + N = zeros(T, n_func_basefuncs, n_qpoints) + dNdx = zeros(Vec{dim,T}, n_func_basefuncs, n_qpoints) + dNdξ = zeros(Vec{dim,T}, n_func_basefuncs, n_qpoints) + + ## Geometry interpolation + n_geom_basefuncs = getnbasefunctions(ip_geo) + M = zeros(T, n_geom_basefuncs, n_qpoints) + dMdξ = zeros(Vec{dim,T}, n_geom_basefuncs, n_qpoints) + + ## Precalculate function and geometric shape values and gradients + for (qp, ξ) in pairs(Ferrite.getpoints(qr)) + for i in 1:n_func_basefuncs + dNdξ[i, qp], N[i, qp] = Ferrite.shape_gradient_and_value(ip_fun, ξ, i) + end + for i in 1:n_geom_basefuncs + dMdξ[i, qp], M[i, qp] = Ferrite.shape_gradient_and_value(ip_geo, ξ, i) + end + end + + detJdV = zeros(T, n_qpoints) + SimpleCellValues(N, dNdξ, dNdx, M, dMdξ, weights, detJdV) +end; + +# We first define the `getnbasefunctions` +Ferrite.getnbasefunctions(cv::SimpleCellValues) = size(cv.N, 1); + +# Before we define the `reinit!` function to calculate the cached +# values `dNdx` and `detJdV` for the current cell +function Ferrite.reinit!(cv::SimpleCellValues, x::Vector{Vec{dim,T}}) where {dim,T} + for (q_point, w) in pairs(cv.weights) # Loop over each quadrature point + ## Calculate the jacobian, J + J = zero(Tensor{2,dim,T}) + for i in eachindex(x) + J += x[i] ⊗ cv.dMdξ[i, q_point] + end + ## Calculate the correct integration weight for the current q_point + cv.detJdV[q_point] = det(J)*w + ## map the shape gradients to the current geometry + Jinv = inv(J) + for i in 1:getnbasefunctions(cv) + cv.dNdx[i, q_point] = cv.dNdξ[i, q_point] ⋅ Jinv + end + end +end; + +# To make our `SimpleCellValues` work in standard Ferrite code, we need to define how +# to get the shape value and graident: +Ferrite.shape_value(cv::SimpleCellValues, q_point::Int, i::Int) = cv.N[i, q_point] +Ferrite.shape_gradient(cv::SimpleCellValues, q_point::Int, i::Int) = cv.dNdx[i, q_point] + +# Currently, we must also define the following functions (TODO in Ferrite.jl) +Ferrite.shape_value_type(cv::SimpleCellValues) = typeof(shape_value(cv, 1, 1)) +Ferrite.shape_gradient_type(cv::SimpleCellValues) = typeof(shape_gradient(cv, 1, 1)) + +# We are now ready to test, so let's create an instance of our new and the regular cell values +qr = QuadratureRule{RefQuadrilateral}(2) +ip = Lagrange{RefQuadrilateral,1}() +simple_cv = SimpleCellValues(qr, ip, ip) +cv = CellValues(qr, ip, ip) + +# The first thing to try is to reinitialize the cell values to a given cell +grid = generate_grid(Quadrilateral, (2,2)) +x = getcoordinates(grid, 2) +reinit!(simple_cv, x) +reinit!(cv, x) + +# If we now pretend we are inside an element, where we have a vector of element +# degree of freedom values, we can check that the function values and gradients match +ue = rand(getnbasefunctions(simple_cv)) +q_point = 2 +@test function_value(cv, q_point, ue) ≈ function_value(simple_cv, q_point, ue) +@test function_gradient(cv, q_point, ue) ≈ function_gradient(simple_cv, q_point, ue) \ No newline at end of file From c76c0afa7cd80502e8e5ed686369997998b776de Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 20:11:52 +0200 Subject: [PATCH 055/172] Add performance annotations and rename checkface to boundscheck_face --- src/FEValues/face_values.jl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/FEValues/face_values.jl b/src/FEValues/face_values.jl index acd32c932b..0f67746926 100644 --- a/src/FEValues/face_values.jl +++ b/src/FEValues/face_values.jl @@ -62,8 +62,8 @@ end getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_mapping(fv)) getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) -getnquadpoints(fv::FaceValues) = getnquadpoints(fv.qr, getcurrentface(fv)) -getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] +getnquadpoints(fv::FaceValues) = @inbounds getnquadpoints(fv.qr, getcurrentface(fv)) +@propagate_inbounds getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] shape_value_type(fv::FaceValues) = shape_value_type(get_fun_values(fv)) shape_gradient_type(fv::FaceValues) = shape_gradient_type(get_fun_values(fv)) @@ -102,14 +102,17 @@ getnormal(fv::FaceValues, qp::Int) = fv.normals[qp] nfaces(fv::FaceValues) = length(fv.geo_mapping) -function checkface(fv::FaceValues, face::Int) - 0 < face <= nfaces(fv) || error("Face index out of range.") +function boundscheck_face(fv::FaceValues, face_nr::Int) + checkbounds(Bool, 1:nfaces(fv), face_nr) || throw(ArgumentError("Face index out of range.")) return nothing end function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, cell=nothing) where {dim, T} check_reinit_sdim_consistency(:FaceValues, shape_gradient_type(fv), eltype(x)) - checkface(fv, face_nr) + + # Checking face_nr before setting current_face allows us to use @inbounds + # when indexing by getcurrentface(fv) in other places! + boundscheck_face(fv, face_nr) fv.current_face[] = face_nr n_geom_basefuncs = getngeobasefunctions(fv) @@ -117,7 +120,8 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, ce throw_incompatible_coord_length(length(x), n_geom_basefuncs) end - # Must be done after setting current face, which should be done after checkface + # Must be done after setting current face, + # which should be done after boundscheck_face geo_mapping = get_geo_mapping(fv) fun_values = get_fun_values(fv) From b5e412165ee501d78ab578dd74f043e9558f5176 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 20:24:42 +0200 Subject: [PATCH 056/172] Improve PointValues constructor from CellValues --- src/PointEval/point_values.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 083e482990..ee4842c7d5 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -24,7 +24,12 @@ struct PointValues{CV} <: AbstractValues PointValues{CV}(cv::CV) where {CV} = new{CV}(cv) end -PointValues(cv::CellValues) = PointValues(eltype(shape_value(cv,1,1)), cv.fun_values.ip, cv.geo_mapping.ip) +function PointValues(cv::CellValues) + T = typeof(getdetJdV(cv, 1)) + ip_fun = get_function_interpolation(cv) + ip_geo = get_geometric_interpolation(cv) + return PointValues(T, ip_fun, ip_geo) +end function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip)) return PointValues(Float64, ip, ipg) end From 089c42a2a9847ce355ac263d4ca990cd234438e7 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 20:25:05 +0200 Subject: [PATCH 057/172] Use internal functions for geometric ip when testing cellvalues --- test/test_cellvalues.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index b6c4b200d0..21ce9a1f84 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -294,7 +294,7 @@ end @testset "CellValues constructor entry points" begin qr = QuadratureRule{RefTriangle}(1) - _get_geo_ip(cv::CellValues) = cv.geo_mapping.ip + for fun_ip in (Lagrange{RefTriangle, 1}(), Lagrange{RefTriangle, 2}()^2) value_type(T) = fun_ip isa ScalarInterpolation ? T : Vec{2, T} grad_type(T) = fun_ip isa ScalarInterpolation ? Vec{2, T} : Tensor{2, 2, T, 4} @@ -302,24 +302,24 @@ end cv = CellValues(qr, fun_ip) @test Ferrite.shape_value_type(cv) == value_type(Float64) @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) - @test _get_geo_ip(cv) == Lagrange{RefTriangle, 1}() + @test Ferrite.get_geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() # Numeric type + quadrature + scalar function cv = CellValues(Float32, qr, fun_ip) @test Ferrite.shape_value_type(cv) == value_type(Float32) @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) - @test _get_geo_ip(cv) == Lagrange{RefTriangle, 1}() + @test Ferrite.get_geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() for geo_ip in (Lagrange{RefTriangle, 2}(), Lagrange{RefTriangle, 2}()^2) scalar_ip(ip) = ip isa VectorizedInterpolation ? ip.ip : ip # Quadrature + scalar function + geo cv = CellValues(qr, fun_ip, geo_ip) @test Ferrite.shape_value_type(cv) == value_type(Float64) @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) - @test _get_geo_ip(cv) == scalar_ip(geo_ip) + @test Ferrite.get_geometric_interpolation(cv) == scalar_ip(geo_ip) # Numeric type + quadrature + scalar function + scalar geo cv = CellValues(Float32, qr, fun_ip, geo_ip) @test Ferrite.shape_value_type(cv) == value_type(Float32) @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) - @test _get_geo_ip(cv) == scalar_ip(geo_ip) + @test Ferrite.get_geometric_interpolation(cv) == scalar_ip(geo_ip) end end end From 07e3851b6d4a83c8428672107ee0f32340f2d142 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 21:33:58 +0200 Subject: [PATCH 058/172] Add devdocs, other docfixes, and fallback for shape value and gradient type --- docs/src/devdocs/FEValues.md | 17 +++++++++++++++++ docs/src/devdocs/index.md | 2 +- docs/src/devdocs/interpolations.md | 1 + docs/src/topics/FEValues.md | 2 +- ...lValues.jl => SimpleCellValues_literate.jl} | 4 ---- src/FEValues/FunctionValues.jl | 15 +++++++++++++++ src/FEValues/GeometryMapping.jl | 18 ++++++++++++++++++ src/FEValues/common_values.jl | 14 ++++++++------ src/PointEval/point_values.jl | 2 +- src/interpolations.jl | 11 +++++++++++ test/test_cellvalues.jl | 4 ++++ 11 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 docs/src/devdocs/FEValues.md rename docs/src/topics/{SimpleCellValues.jl => SimpleCellValues_literate.jl} (94%) diff --git a/docs/src/devdocs/FEValues.md b/docs/src/devdocs/FEValues.md new file mode 100644 index 0000000000..81a349ac27 --- /dev/null +++ b/docs/src/devdocs/FEValues.md @@ -0,0 +1,17 @@ +# [FEValues](@id devdocs-fevalues) + +## Type definitions +* `AbstractValues` + * `AbstractCellValues` + * [`CellValues`](@ref) + * `AbstractFaceValues` + * [`FaceValues`](@ref) + * [`PointValues`](@ref) + * `PointValuesInternal` (Optimized version of PointValues) + +## Internal types +```@docs +Ferrite.GeometryMapping +Ferrite.MappingValues +Ferrite.FunctionValues +``` \ No newline at end of file diff --git a/docs/src/devdocs/index.md b/docs/src/devdocs/index.md index 3560eff4a1..9c16b83d7c 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", "dofhandler.md", "performance.md"] +Pages = ["reference_cells.md", "interpolations.md", "elements.md", "FEValues.md", "dofhandler.md", "performance.md"] ``` diff --git a/docs/src/devdocs/interpolations.md b/docs/src/devdocs/interpolations.md index 23b3ec1a0a..3a54d5c92a 100644 --- a/docs/src/devdocs/interpolations.md +++ b/docs/src/devdocs/interpolations.md @@ -37,6 +37,7 @@ Ferrite.reference_coordinates(::Interpolation) Ferrite.face_to_element_transformation Ferrite.is_discontinuous(::Interpolation) Ferrite.adjust_dofs_during_distribution(::Interpolation) +Ferrite.get_mapping_type ``` for all entities which exist on that reference element. The dof functions default to having no diff --git a/docs/src/topics/FEValues.md b/docs/src/topics/FEValues.md index 2273c528d2..67826b7000 100644 --- a/docs/src/topics/FEValues.md +++ b/docs/src/topics/FEValues.md @@ -112,7 +112,7 @@ Please note that several internal functions are used, and these may change witho ```@eval # Include the example here, but modify the Literate output to suit being embedded using Literate, Markdown -filename = "SimpleCellValues" +filename = "SimpleCellValues_literate" Literate.markdown(filename*".jl"; execute=true) contents = read(filename*".md", String) Literate.script(filename*".jl"; name="SimpleCellValues") diff --git a/docs/src/topics/SimpleCellValues.jl b/docs/src/topics/SimpleCellValues_literate.jl similarity index 94% rename from docs/src/topics/SimpleCellValues.jl rename to docs/src/topics/SimpleCellValues_literate.jl index 968c5b8a61..8f6b2469a7 100644 --- a/docs/src/topics/SimpleCellValues.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -74,10 +74,6 @@ end; Ferrite.shape_value(cv::SimpleCellValues, q_point::Int, i::Int) = cv.N[i, q_point] Ferrite.shape_gradient(cv::SimpleCellValues, q_point::Int, i::Int) = cv.dNdx[i, q_point] -# Currently, we must also define the following functions (TODO in Ferrite.jl) -Ferrite.shape_value_type(cv::SimpleCellValues) = typeof(shape_value(cv, 1, 1)) -Ferrite.shape_gradient_type(cv::SimpleCellValues) = typeof(shape_gradient(cv, 1, 1)) - # We are now ready to test, so let's create an instance of our new and the regular cell values qr = QuadratureRule{RefQuadrilateral}(2) ip = Lagrange{RefQuadrilateral,1}() diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 48f2a4e0e3..0a2d84cba6 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -29,6 +29,14 @@ typeof_dNdξ(::Type{T}, ::SInterpolationDims{rdim}) where {T,rdim} = SVector{rdi typeof_dNdξ(::Type{T}, ::VInterpolationDims{dim,dim,dim}) where {T,dim} = Tensor{2,dim,T} typeof_dNdξ(::Type{T}, ::VInterpolationDims{rdim,<:Any,vdim}) where {T,rdim,vdim} = SMatrix{vdim,rdim,T} # If vdim=rdim!=sdim Tensor would be possible... +""" + FunctionValues(::Type{T}, ip_fun, qr::QuadratureRule, ip_geo::VectorizedInterpolation) + +Create a `FunctionValues` object containing the shape values and gradients for both the reference +cell (precalculated) and the real cell (updated in `reinit!`). +""" +FunctionValues + struct FunctionValues{IP, N_t, dNdx_t, dNdξ_t} ip::IP # ::Interpolation N_x::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} @@ -108,6 +116,13 @@ struct ContravariantPiolaMapping end get_mapping_type(fv::FunctionValues) = get_mapping_type(fv.ip) +""" + requires_hessian(mapping) + +Does the `mapping` type require the hessian, d²M/dx², +to map the function values and gradients from the reference cell +to the real cell geometry? +""" requires_hessian(::IdentityMapping) = false requires_hessian(::ContravariantPiolaMapping) = true requires_hessian(::CovariantPiolaMapping) = true diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index 4038f1f460..4673427401 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -1,3 +1,13 @@ +""" + MappingValues(J, H) + +The mapping values are calculated based on a +`geometric_mapping::GeometryMapping` along with the cell coordinates, +and the stored jacobian, `J`, and potentially hessian, `H`, are +used when mapping the `FunctionValues` to the current cell during `reinit!`. +""" +MappingValues + struct MappingValues{JT, HT<:Union{Nothing,AbstractTensor{3}}} J::JT # dx/dξ # Jacobian H::HT # dJ/dξ # Hessian @@ -19,6 +29,14 @@ function RequiresHessian(ip_fun::Interpolation, ip_geo::Interpolation) RequiresHessian(requires_hessian(get_mapping_type(ip_fun))) end +""" + GeometryMapping(::Type{T}, ip_geo, qr::QuadratureRule, ::RequiresHessian{B}) + +Create a `GeometryMapping` object which contains the geometric shape, gradients, and, +if `B==true`, the hessian values. `T<:AbstractFloat` gives the numeric type of the values. +""" +GeometryMapping + struct GeometryMapping{IP, M_t, dMdξ_t, d2Mdξ2_t} ip::IP # ::Interpolation Geometric interpolation M::M_t # ::AbstractVector{<:Number} Values of geometric shape functions diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index b1c9b5d537..e5aa90a4c8 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -137,14 +137,15 @@ function function_value(fe_v::AbstractValues, q_point::Int, u::AbstractVector, d return val end -# TODO: Implement fallback or require this to be defined? -# Alt: shape_value_type(cv) = typeof(shape_value(cv, qp=1, i=1)) """ shape_value_type(fe_v::AbstractValues) Return the type of `shape_value(fe_v, q_point, base_function)` """ -function shape_value_type end +function shape_value_type(fe_v::AbstractValues) + # Default fallback + return typeof(shape_value(fe_v, 1, 1)) +end function_value_init(cv::AbstractValues, ::AbstractVector{T}) where {T} = zero(shape_value_type(cv)) * zero(T) @@ -188,14 +189,15 @@ function function_gradient(fe_v::AbstractValues, q_point::Int, u::AbstractVector return grad end -# TODO: Implement fallback or require this to be defined? -# Alt: shape_gradient_type(cv) = typeof(shape_gradient(cv, qp=1, i=1)) """ shape_gradient_type(fe_v::AbstractValues) Return the type of `shape_gradient(fe_v, q_point, base_function)` """ -function shape_gradient_type end +function shape_gradient_type(fe_v::AbstractValues) + # Default fallback + return typeof(shape_gradient(fe_v, 1, 1)) +end function function_gradient_init(cv::AbstractValues, ::AbstractVector{T}) where {T} return zero(shape_gradient_type(cv)) * zero(T) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index ee4842c7d5..13e75b1104 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -71,7 +71,7 @@ function reinit!(pv::PointValues, x::AbstractVector{<:Vec{D}}, ξ::Vec{D}) where return nothing end -# Optimized version of PointScalarValues which avoids i) recomputation of dNdξ and +# Optimized version of PointValues which avoids i) recomputation of dNdξ and # ii) recomputation of dNdx. Only allows function evaluation (no gradients) which is # what is used in evaluate_at_points. struct PointValuesInternal{IP, N_t} <: AbstractValues diff --git a/src/interpolations.jl b/src/interpolations.jl index f8235f2bb0..009ab6b7bd 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1514,6 +1514,17 @@ end reference_coordinates(ip::VectorizedInterpolation) = reference_coordinates(ip.ip) is_discontinuous(::Type{<:VectorizedInterpolation{<:Any, <:Any, <:Any, ip}}) where {ip} = is_discontinuous(ip) + +""" + get_mapping_type(ip::Interpolation) + +Get the type of mapping from the reference cell to the real cell for an +interpolation `ip`. Subtypes of `ScalarInterpolation` and `VectorizedInterpolation` +return `IdentityMapping()`, but other non-scalar interpolations may request different +mapping types. +""" +function get_mapping_type end + get_mapping_type(::ScalarInterpolation) = IdentityMapping() get_mapping_type(::VectorizedInterpolation) = IdentityMapping() diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 21ce9a1f84..eecccb7dca 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -334,4 +334,8 @@ end println(stdout) end +@testset "SimpleCellValues" begin + include(joinpath(@__DIR__, "../docs/src/topics/SimpleCellValues_literate.jl")) +end + end # of testset From f3e6564cc1f2dcc6dbf6448a091145422508a987 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 22:15:34 +0200 Subject: [PATCH 059/172] Fix so that API docs for FEValues is ok again --- docs/src/reference/fevalues.md | 24 +++++++++++++++--------- src/FEValues/common_values.jl | 10 +++++----- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/src/reference/fevalues.md b/docs/src/reference/fevalues.md index 9aa00fee29..757760fbc6 100644 --- a/docs/src/reference/fevalues.md +++ b/docs/src/reference/fevalues.md @@ -5,12 +5,22 @@ DocTestSetup = :(using Ferrite) # FEValues -## [CellValues](@id reference-cellvalues) +## Main types +[`CellValues`](@ref) and [`FaceValues`](@ref) are the most common +subtypes of `Ferrite.AbstractValues`. ```@docs CellValues +FaceValues +``` + +## Applicable functions +The following functions are applicable to both `CellValues` +and `FaceValues`. + +```@docs reinit! -getnquadpoints(::CellValues) +getnquadpoints getdetJdV shape_value @@ -25,13 +35,9 @@ function_divergence spatial_coordinate ``` -## [FaceValues](@id reference-facevalues) - -All of the methods for [`CellValues`](@ref) apply for `FaceValues` as well. -In addition, there are some methods that are unique for `FaecValues`: +In addition, there are some methods that are unique for `FaceValues`. ```@docs -FaceValues -getcurrentface -getnquadpoints(::FaceValues) +Ferrite.getcurrentface +getnormal ``` diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index e5aa90a4c8..82e0632606 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -1,4 +1,4 @@ -# Common methods for all `Values` objects +# Common methods for all `AbstractValues` objects using Base: @propagate_inbounds @@ -31,7 +31,7 @@ end """ reinit!(cv::CellValues, x::Vector, cell::Union{AbstractCell,Nothing}=nothing) - reinit!(bv::FaceValues, x::Vector, face::Int, cell::Union{AbstractCell,Nothing}=nothing) + reinit!(fv::FaceValues, x::Vector, face::Int, cell::Union{AbstractCell,Nothing}=nothing) Update the `CellValues`/`FaceValues` object for a cell or face with coordinates `x`. The derivatives of the shape functions, and the new integration weights are computed. @@ -40,10 +40,10 @@ For interpolations with non-identity mappings, the current `cell` is also requir reinit! """ - getnquadpoints(fv::FaceValues) + getnquadpoints(fe_v::AbstractValues) -Return the number of quadrature points in `fv`s quadrature for the current -(most recently [`reinit!`](@ref)ed) face. +Return the number of quadrature points. For `FaceValues`, +this is the number for the current face. """ function getnquadpoints end From f5c662c85df3a45bba3357ea979220e29e25333c Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 22:20:56 +0200 Subject: [PATCH 060/172] Rename xx_values.jl to XxValues.jl --- src/FEValues/{cell_values.jl => CellValues.jl} | 0 src/FEValues/{face_values.jl => FaceValues.jl} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/FEValues/{cell_values.jl => CellValues.jl} (100%) rename src/FEValues/{face_values.jl => FaceValues.jl} (100%) diff --git a/src/FEValues/cell_values.jl b/src/FEValues/CellValues.jl similarity index 100% rename from src/FEValues/cell_values.jl rename to src/FEValues/CellValues.jl diff --git a/src/FEValues/face_values.jl b/src/FEValues/FaceValues.jl similarity index 100% rename from src/FEValues/face_values.jl rename to src/FEValues/FaceValues.jl From f9e9aa5e7ef302321d344186d11ca64a114a2878 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 22:21:35 +0200 Subject: [PATCH 061/172] Rename at include too --- src/Ferrite.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ferrite.jl b/src/Ferrite.jl index d30de2ba7e..a5e9507155 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -84,8 +84,8 @@ include("Quadrature/quadrature.jl") # FEValues include("FEValues/GeometryMapping.jl") include("FEValues/FunctionValues.jl") -include("FEValues/cell_values.jl") -include("FEValues/face_values.jl") +include("FEValues/CellValues.jl") +include("FEValues/FaceValues.jl") include("PointEval/point_values.jl") include("FEValues/common_values.jl") include("FEValues/face_integrals.jl") From c418594fc38111c9bbadef4886e57dfe02a24ab0 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 22:30:24 +0200 Subject: [PATCH 062/172] Merge docs/Manifest from master --- docs/Manifest.toml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 08e315defd..e79fd3ef2e 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.9.2" +julia_version = "1.9.1" manifest_format = "2.0" -project_hash = "8722b6a67abf1b81e2c7808ddfcfb1d737cd0040" +project_hash = "a0caf5d07899f1863658c7736f8530d20d28db07" [[deps.ADTypes]] git-tree-sha1 = "5d2e21d7b0d8c22f67483ef95ebdc39c0e6b6003" @@ -166,10 +166,14 @@ uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" version = "0.11.4" [[deps.ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"] -git-tree-sha1 = "600cc5508d66b78aae350f7accdb58763ac18589" +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.9.10" +version = "0.10.0" +weakdeps = ["SpecialFunctions"] + + [deps.ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] @@ -201,7 +205,7 @@ weakdeps = ["Dates", "LinearAlgebra"] [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.0.5+0" +version = "1.0.2+0" [[deps.ConcreteStructs]] git-tree-sha1 = "f749037478283d372048690eb3b5f92a79432b34" @@ -1177,7 +1181,7 @@ version = "0.42.2+0" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.9.2" +version = "1.9.0" [[deps.PlotThemes]] deps = ["PlotUtils", "Statistics"] @@ -1615,9 +1619,7 @@ version = "0.1.1" [[deps.Tensors]] deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] -git-tree-sha1 = "09650779b3c5124b8742986904d6e69f854be0bd" -repo-rev = "kam/3rd_order" -repo-url = "https://github.com/KnutAM/Tensors.jl.git" +git-tree-sha1 = "bcbb366323add300742c9e4a5447e584640aeff2" uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" version = "1.15.0" From d47ae33fcda924232fb3b382ea4ca8c55df1c9bd Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 23:11:29 +0200 Subject: [PATCH 063/172] Minor formatting fixes according to review --- src/FEValues/FaceValues.jl | 3 ++- src/PointEval/point_values.jl | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 0f67746926..3681d78ce1 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -132,6 +132,7 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, ce @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) mapping = calculate_mapping(geo_mapping, q_point, x) J = getjacobian(mapping) + # See the `Ferrite.embedded_det` docstring for more background weight_norm = weighted_normal(J, getrefshape(geo_mapping.ip), face_nr) detJ = norm(weight_norm) detJ > 0.0 || throw_detJ_not_pos(detJ) @@ -214,4 +215,4 @@ function spatial_coordinate(bcv::BCValues, q_point::Int, xh::AbstractVector{Vec{ x += bcv.M[i,q_point,face] * xh[i] # geometric_value(fe_v, q_point, i) * xh[i] end return x -end \ No newline at end of file +end diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 13e75b1104..b2f025574e 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -101,4 +101,4 @@ end function Base.show(io::IO, d::MIME"text/plain", cv::PointValues) println(io, "PointValues containing a") show(io, d, cv.cv) -end \ No newline at end of file +end From b5085561ee3f0b549f15635bf3fc61380bf7a735 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 13 Oct 2023 23:46:36 +0200 Subject: [PATCH 064/172] Forgot rename of type param --- src/FEValues/CellValues.jl | 4 ++-- src/FEValues/FaceValues.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 9a507e8830..484d0f4131 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -35,9 +35,9 @@ function default_geometric_interpolation(::Interpolation{shape}) where {dim, sha return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) end -struct CellValues{FV, GV, QR, detT<:AbstractVector} <: AbstractCellValues +struct CellValues{FV, GM, QR, detT<:AbstractVector} <: AbstractCellValues fun_values::FV # FunctionValues - geo_mapping::GV # GeometryMapping + geo_mapping::GM # GeometryMapping qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} end diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 3681d78ce1..1b1e473242 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -31,9 +31,9 @@ values of nodal functions, gradients and divergences of nodal functions etc. on """ FaceValues -struct FaceValues{FV, GV, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GV<:AbstractVector{GV}} <: AbstractFaceValues +struct FaceValues{FV, GM, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:AbstractVector{GM}} <: AbstractFaceValues fun_values::V_FV # AbstractVector{FunctionValues} - geo_mapping::V_GV # AbstractVector{GeometryMapping} + geo_mapping::V_GM # AbstractVector{GeometryMapping} qr::QR # FaceQuadratureRule detJdV::detT # AbstractVector{<:Number} normals::nT # AbstractVector{<:Vec} From 4451fac323b2f2600d8927d25f9727c9ff11c104 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 16 Oct 2023 18:34:54 +0200 Subject: [PATCH 065/172] Use new functions --- src/FEValues/FunctionValues.jl | 2 +- src/FEValues/GeometryMapping.jl | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 1098ef296e..54e7075ae8 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -72,7 +72,7 @@ end function precompute_values!(fv::FunctionValues, qr::QuadratureRule) for (qp, ξ) in pairs(getpoints(qr)) - shape_gradients_and_values!(@view(fv.dNdξ[:, qp]), @view(fv.N[:, qp]), fv.ip, ξ) + shape_gradients_and_values!(@view(fv.dNdξ[:, qp]), @view(fv.N_ξ[:, qp]), fv.ip, ξ) end end diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index ab4538f249..0b71be0abc 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -53,11 +53,11 @@ function GeometryMapping(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, # Hessian (if needed, else nothing) HT = Tensor{2,getdim(ip),T} - dM2dξ2 = RH ? zeros(HT, n_shape, n_qpoints) : nothing + d2Mdξ2 = RH ? zeros(HT, n_shape, n_qpoints) : nothing - geo_mapping = GeometryMapping(ip, M, dMdξ, dM2dξ2) + geo_mapping = GeometryMapping(ip, M, dMdξ, d2Mdξ2) precompute_values!(geo_mapping, qr) - return GeometryMapping(ip, M, dMdξ, dM2dξ2) + return geo_mapping end function Base.copy(v::GeometryMapping) d2Mdξ2_copy = v.d2Mdξ2 === nothing ? nothing : copy(v.d2Mdξ2) @@ -77,7 +77,7 @@ function precompute_values!(gm::GeometryMapping, ::RequiresHessian{true}, qr::Qu ip = get_geometric_interpolation(gm) for (qp, ξ) in pairs(getpoints(qr)) for i in 1:getngeobasefunctions(gm) - gm.dM2dξ2[i, qp], gm.dMdξ[i, qp], gm.M[i, qp] = shape_hessian_gradient_and_value(ip, ξ, i) + gm.d2Mdξ2[i, qp], gm.dMdξ[i, qp], gm.M[i, qp] = shape_hessian_gradient_and_value(ip, ξ, i) end end end From 72e4404998da68bfe6ecb8a88692f7b6d0e35e57 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 16 Oct 2023 20:01:07 +0200 Subject: [PATCH 066/172] Update docs manifest --- docs/Manifest.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index e79fd3ef2e..fbf0d5570e 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.9.1" +julia_version = "1.9.3" manifest_format = "2.0" -project_hash = "a0caf5d07899f1863658c7736f8530d20d28db07" +project_hash = "8722b6a67abf1b81e2c7808ddfcfb1d737cd0040" [[deps.ADTypes]] git-tree-sha1 = "5d2e21d7b0d8c22f67483ef95ebdc39c0e6b6003" @@ -205,7 +205,7 @@ weakdeps = ["Dates", "LinearAlgebra"] [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.0.2+0" +version = "1.0.5+0" [[deps.ConcreteStructs]] git-tree-sha1 = "f749037478283d372048690eb3b5f92a79432b34" @@ -1181,7 +1181,7 @@ version = "0.42.2+0" [[deps.Pkg]] deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.9.0" +version = "1.9.2" [[deps.PlotThemes]] deps = ["PlotUtils", "Statistics"] @@ -1619,9 +1619,9 @@ version = "0.1.1" [[deps.Tensors]] deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] -git-tree-sha1 = "bcbb366323add300742c9e4a5447e584640aeff2" +git-tree-sha1 = "3b0c974579e89b0dd35a6ee6a9f10caf5e304d6c" uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" -version = "1.15.0" +version = "1.16.0" [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] From 3049dc43152027eced98bb274f871a32c2a5ce1d Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 14:28:36 +0200 Subject: [PATCH 067/172] Move svg to gh-pages --- .github/workflows/ci.yml | 8 +- docs/Manifest.toml | 118 +- docs/src/assets/fe_mapping.svg | 3325 -------------------------------- docs/src/topics/FEValues.md | 2 +- 4 files changed, 69 insertions(+), 3384 deletions(-) delete mode 100644 docs/src/assets/fe_mapping.svg diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 516ff96937..4679271fad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,13 +26,7 @@ jobs: - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.julia-version }} - #- uses: julia-actions/julia-buildpkg@v1 - - run: | - julia --color=yes --project=. -e ' - using Pkg - Pkg.add(url="https://github.com/KnutAM/Tensors.jl.git", rev="kam/3rd_order") - Pkg.instantiate()' - shell: bash + - uses: julia-actions/julia-buildpkg@v1 - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 with: diff --git a/docs/Manifest.toml b/docs/Manifest.toml index fbf0d5570e..2c9f66289c 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -84,9 +84,9 @@ uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" [[deps.BibInternal]] -git-tree-sha1 = "3a760b38ba8da19e64d29244f06104823ff26f25" +git-tree-sha1 = "0c62b284a52ec39ee831e10bf62c17d587dde75f" uuid = "2027ae74-3657-4b95-ae00-e2f7d55c3e64" -version = "0.3.4" +version = "0.3.5" [[deps.BibParser]] deps = ["BibInternal", "DataStructures", "Dates", "JSONSchema", "YAML"] @@ -96,9 +96,9 @@ version = "0.2.1" [[deps.Bibliography]] deps = ["BibInternal", "BibParser", "DataStructures", "Dates", "FileIO", "YAML"] -git-tree-sha1 = "b506db2482a8e110622ddf1fd0f78bce381af032" +git-tree-sha1 = "520c679daed011ce835d9efa7778863aad6687ed" uuid = "f1be7e48-bf82-45af-a471-ae754a193061" -version = "0.2.19" +version = "0.2.20" [[deps.BitFlags]] git-tree-sha1 = "43b1a4a8f797c1cddadf60499a8a077d4af2cd2d" @@ -136,10 +136,14 @@ uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" version = "1.16.1+1" [[deps.ChainRulesCore]] -deps = ["Compat", "LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "e30f2f4e20f7f186dc36529910beaedc60cfa644" +deps = ["Compat", "LinearAlgebra"] +git-tree-sha1 = "e0af648f0692ec1691b5d094b8724ba1346281cf" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.16.0" +version = "1.18.0" +weakdeps = ["SparseArrays"] + + [deps.ChainRulesCore.extensions] + ChainRulesCoreSparseArraysExt = "SparseArrays" [[deps.CloseOpenIntervals]] deps = ["Static", "StaticArrayInterface"] @@ -149,9 +153,9 @@ version = "0.1.12" [[deps.CodecZlib]] deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "02aa26a4cf76381be7f66e020a3eddeb27b0a092" +git-tree-sha1 = "cd67fc487743b2f0fd4380d4cbd3a24660d0eec8" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.2" +version = "0.7.3" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] @@ -271,9 +275,9 @@ version = "1.9.1" [[deps.DiffEqBase]] deps = ["ArrayInterface", "ChainRulesCore", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces", "ZygoteRules"] -git-tree-sha1 = "766ab4574433d22ff75ab28e9081114e73cef5d5" +git-tree-sha1 = "95b6df71e218379a831874215b0effaac791d7d7" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.132.0" +version = "6.133.1" [deps.DiffEqBase.extensions] DiffEqBaseDistributionsExt = "Distributions" @@ -334,9 +338,9 @@ version = "0.9.3" [[deps.Documenter]] deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "Dates", "DocStringExtensions", "Downloads", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "Test", "Unicode"] -git-tree-sha1 = "f667b805e90d643aeb1ca70189827f991a7cc115" +git-tree-sha1 = "147a3cbb6ddcd9448fe5e6c426b347efc68f9c86" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.1.0" +version = "1.1.1" [[deps.DocumenterCitations]] deps = ["AbstractTrees", "Bibliography", "Documenter", "Markdown", "MarkdownAST", "OrderedCollections", "Unicode"] @@ -356,9 +360,9 @@ version = "1.0.4" [[deps.EnzymeCore]] deps = ["Adapt"] -git-tree-sha1 = "1091d4bbc2f2f7840a65fc0496c782b955dd82fb" +git-tree-sha1 = "d8701002a745c450c03b890f10d53636d1a8a7ea" uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" -version = "0.6.0" +version = "0.6.2" [[deps.EpollShim_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -409,9 +413,9 @@ version = "1.3.8+0" [[deps.FastBroadcast]] deps = ["ArrayInterface", "LinearAlgebra", "Polyester", "Static", "StaticArrayInterface", "StrideArraysCore"] -git-tree-sha1 = "aa9925a229d45fe3018715238956766fa21804d1" +git-tree-sha1 = "9d77cb1caf03e67514ba60bcfc47c6e131b1950c" uuid = "7034ab61-46d4-4ed7-9d0f-46aef9175898" -version = "0.2.6" +version = "0.2.7" [[deps.FastClosures]] git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" @@ -461,9 +465,9 @@ uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" [[deps.FillArrays]] deps = ["LinearAlgebra", "Random"] -git-tree-sha1 = "a20eaa3ad64254c61eeb5f230d9306e937405434" +git-tree-sha1 = "35f0c0f345bff2c6d636f95fdb136323b5a796ef" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.6.1" +version = "1.7.0" weakdeps = ["SparseArrays", "Statistics"] [deps.FillArrays.extensions] @@ -653,9 +657,9 @@ uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" version = "0.1.1" [[deps.Inflate]] -git-tree-sha1 = "5cd07aab533df5170988219191dfad0519391428" +git-tree-sha1 = "ea8031dea4aff6bd41f1df8f2fdfb25b33626381" uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.3" +version = "0.1.4" [[deps.IntelOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -685,9 +689,9 @@ version = "1.0.0" [[deps.JLFzf]] deps = ["Pipe", "REPL", "Random", "fzf_jll"] -git-tree-sha1 = "f377670cda23b6b7c1c0b3893e37451c5c1a2185" +git-tree-sha1 = "9fb0b890adab1c0a4a475d4210d51f228bfc250d" uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" -version = "0.1.5" +version = "0.1.6" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] @@ -889,11 +893,12 @@ version = "5.0.0+0" [[deps.LinearSolve]] deps = ["ArrayInterface", "ConcreteStructs", "DocStringExtensions", "EnumX", "EnzymeCore", "FastLapackInterface", "GPUArraysCore", "InteractiveUtils", "KLU", "Krylov", "Libdl", "LinearAlgebra", "MKL_jll", "PrecompileTools", "Preferences", "RecursiveFactorization", "Reexport", "Requires", "SciMLBase", "SciMLOperators", "Setfield", "SparseArrays", "Sparspak", "SuiteSparse", "UnPack"] -git-tree-sha1 = "435ab14ca589757a0feae6e3e347bc37addda42d" +git-tree-sha1 = "158e45dd35cec1ecade0e554c0104ee89e772d82" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "2.9.2" +version = "2.11.1" [deps.LinearSolve.extensions] + LinearSolveBandedMatricesExt = "BandedMatrices" LinearSolveBlockDiagonalsExt = "BlockDiagonals" LinearSolveCUDAExt = "CUDA" LinearSolveEnzymeExt = "Enzyme" @@ -903,8 +908,10 @@ version = "2.9.2" LinearSolveKrylovKitExt = "KrylovKit" LinearSolveMetalExt = "Metal" LinearSolvePardisoExt = "Pardiso" + LinearSolveRecursiveArrayToolsExt = "RecursiveArrayTools" [deps.LinearSolve.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" @@ -914,6 +921,7 @@ version = "2.9.2" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" Metal = "dde4c033-4e86-420c-a63e-0dd931031962" Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" + RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" [[deps.Literate]] deps = ["Base64", "IOCapture", "JSON", "REPL"] @@ -1071,9 +1079,17 @@ version = "1.2.0" [[deps.NonlinearSolve]] deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "EnumX", "FiniteDiff", "ForwardDiff", "LineSearches", "LinearAlgebra", "LinearSolve", "PrecompileTools", "RecursiveArrayTools", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseDiffTools", "StaticArraysCore", "UnPack"] -git-tree-sha1 = "445a7ba86794e1f8ee9da3b3b7becf284e2625fd" +git-tree-sha1 = "9203b3333c9610664de2e8cbc23cfd726663df7d" uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" -version = "2.1.0" +version = "2.4.0" + + [deps.NonlinearSolve.extensions] + NonlinearSolveFastLevenbergMarquardtExt = "FastLevenbergMarquardt" + NonlinearSolveLeastSquaresOptimExt = "LeastSquaresOptim" + + [deps.NonlinearSolve.weakdeps] + FastLevenbergMarquardt = "7a0df574-e128-4d35-8cbd-3d84502bf7ce" + LeastSquaresOptim = "0fc2ff8b-aaa3-5acd-a817-1944a5e08891" [[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"] @@ -1123,9 +1139,9 @@ version = "0.5.5+0" [[deps.Optim]] deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "PositiveFactorizations", "Printf", "SparseArrays", "StatsBase"] -git-tree-sha1 = "963b004d15216f8129f6c0f7d187efa136570be0" +git-tree-sha1 = "01f85d9269b13fedc61e63cc72ee2213565f7a72" uuid = "429524aa-4258-5aef-a3af-852621145aeb" -version = "1.7.7" +version = "1.7.8" [[deps.Opus_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -1217,9 +1233,9 @@ version = "1.39.0" [[deps.Polyester]] deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Requires", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] -git-tree-sha1 = "c7dc9720390fcc296bf757b3f833f9e41c68a086" +git-tree-sha1 = "398f91235beaac50445557c937ecb0145d171842" uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" -version = "0.7.7" +version = "0.7.8" [[deps.PolyesterWeave]] deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] @@ -1330,9 +1346,9 @@ version = "0.1.0" [[deps.RelocatableFolders]] deps = ["SHA", "Scratch"] -git-tree-sha1 = "90bc7a7c96410424509e4263e277e43250c05691" +git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "1.0.0" +version = "1.0.1" [[deps.Requires]] deps = ["UUIDs"] @@ -1375,9 +1391,9 @@ version = "0.6.39" [[deps.SciMLBase]] deps = ["ADTypes", "ArrayInterface", "ChainRulesCore", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FillArrays", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "PrecompileTools", "Preferences", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLOperators", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables", "TruncatedStacktraces", "ZygoteRules"] -git-tree-sha1 = "1e09c5c89f5502eb4e3657730b0fb6817a1bca8d" +git-tree-sha1 = "151c322c309d879d114d1c0bee69c61d5933357f" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.3.0" +version = "2.4.3" [deps.SciMLBase.extensions] SciMLBasePyCallExt = "PyCall" @@ -1435,9 +1451,9 @@ version = "1.1.0" [[deps.SimpleNonlinearSolve]] deps = ["ArrayInterface", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "PackageExtensionCompat", "PrecompileTools", "Reexport", "SciMLBase", "StaticArraysCore"] -git-tree-sha1 = "4d53b83af904049c493daaf2a225bcae994a3c59" +git-tree-sha1 = "15ff97fa4881133caa324dacafe28b5ac47ad8a2" uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "0.1.20" +version = "0.1.23" [deps.SimpleNonlinearSolve.extensions] SimpleNonlinearSolveNNlibExt = "NNlib" @@ -1467,9 +1483,9 @@ uuid = "6462fe0b-24de-5631-8697-dd941f90decc" [[deps.SortingAlgorithms]] deps = ["DataStructures"] -git-tree-sha1 = "c60ec5c62180f27efea3ba2908480f8055e17cee" +git-tree-sha1 = "5165dfb9fd131cf0c6957a3a7605dede376e7b63" uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.1.1" +version = "1.2.0" [[deps.SparseArrays]] deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] @@ -1477,9 +1493,9 @@ uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" [[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 = "0a4538040f6eeae9016043104056dc6c13e1d8c1" +git-tree-sha1 = "336fd944a1bbb8873bfa8171387608ca93317d68" uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "2.7.0" +version = "2.8.0" [deps.SparseDiffTools.extensions] SparseDiffToolsEnzymeExt = "Enzyme" @@ -1602,9 +1618,9 @@ version = "1.0.1" [[deps.Tables]] deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "a1f34829d5ac0ef499f6d84428bd6b4c71f02ead" +git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.11.0" +version = "1.11.1" [[deps.Tar]] deps = ["ArgTools", "SHA"] @@ -1652,9 +1668,9 @@ uuid = "d5829a12-d9aa-46ab-831f-fb7c9ab06edf" version = "0.1.19" [[deps.Tricks]] -git-tree-sha1 = "aadb748be58b492045b4f56166b5188aa63ce549" +git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f" uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" -version = "0.1.7" +version = "0.1.8" [[deps.TruncatedStacktraces]] deps = ["InteractiveUtils", "MacroTools", "Preferences"] @@ -1663,9 +1679,9 @@ uuid = "781d530d-4396-4725-bb49-402e4bee1e77" version = "1.4.0" [[deps.URIs]] -git-tree-sha1 = "b7a5e99f24892b6824a954199a45e9ffcc1c70f0" +git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.5.0" +version = "1.5.1" [[deps.UUIDs]] deps = ["Random", "SHA"] @@ -1747,9 +1763,9 @@ version = "1.25.0+0" [[deps.WriteVTK]] deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] -git-tree-sha1 = "7b46936613e41cfe1c6a5897d243ddcab8feabec" +git-tree-sha1 = "41f0dc2a8f6fd860c266b91fd5cdf4fead65ae69" uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192" -version = "1.18.0" +version = "1.18.1" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] @@ -1950,9 +1966,9 @@ version = "3.2.9+0" [[deps.fzf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "868e669ccb12ba16eaf50cb2957ee2ff61261c56" +git-tree-sha1 = "47cf33e62e138b920039e8ff9f9841aafe1b733e" uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" -version = "0.29.0+0" +version = "0.35.1+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", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] diff --git a/docs/src/assets/fe_mapping.svg b/docs/src/assets/fe_mapping.svg deleted file mode 100644 index a377c77528..0000000000 --- a/docs/src/assets/fe_mapping.svg +++ /dev/null @@ -1,3325 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/src/topics/FEValues.md b/docs/src/topics/FEValues.md index 67826b7000..2039f2846c 100644 --- a/docs/src/topics/FEValues.md +++ b/docs/src/topics/FEValues.md @@ -6,7 +6,7 @@ The shape functions and gradients stored in an `FEValues` object, is reinitializ The geometric mapping of a finite element from the reference coordinates to the real coordinates is shown in the following illustration. -![mapping_figure](../assets/fe_mapping.svg) +![mapping_figure](https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/gh-pages/assets/fe_mapping.svg) This mapping is given by the geometric shape functions, $\hat{N}_i^g(\boldsymbol{\xi})$, such that ```math From 24591b5ce2e7641d25e71f074739a32fcfda6b12 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 14:55:56 +0200 Subject: [PATCH 068/172] Use shape_x_x_and_x instead of precompute_values --- src/FEValues/FunctionValues.jl | 30 +++++++++----------------- src/FEValues/GeometryMapping.jl | 38 ++++++++++----------------------- src/FEValues/common_values.jl | 13 +++++++++++ src/interpolations.jl | 15 +++++++++++++ 4 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 54e7075ae8..7ff052a8db 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -46,36 +46,26 @@ struct FunctionValues{IP, N_t, dNdx_t, dNdξ_t} end function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T ip_dims = InterpolationDims(ip, ip_geo) - N_t = typeof_N(T, ip_dims) - dNdx_t = typeof_dNdx(T, ip_dims) - dNdξ_t = typeof_dNdξ(T, ip_dims) n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) - N_ξ = zeros(N_t, n_shape, n_qpoints) - if isa(get_mapping_type(ip), IdentityMapping) - N_x = N_ξ - else - N_x = zeros(N_t, n_shape, n_qpoints) - end - dNdξ = zeros(dNdξ_t, n_shape, n_qpoints) - dNdx = fill(zero(dNdx_t) * T(NaN), n_shape, n_qpoints) - fv = FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) - precompute_values!(fv, qr) # Precompute N and dNdξ - return fv + N_ξ = zeros(typeof_N(T, ip_dims), n_shape, n_qpoints) + N_x = isa(get_mapping_type(ip), IdentityMapping) ? N_ξ : similar(N_ξ) + + dNdξ = zeros(typeof_dNdξ(T, ip_dims), n_shape, n_qpoints) + dNdx = fill(zero(typeof_dNdx(T, ip_dims)) * T(NaN), n_shape, n_qpoints) + + shape_gradients_and_values!(dNdξ, N_ξ, ip, qr) # Compute N_ξ and dNdξ + + return FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) end + function Base.copy(v::FunctionValues) N_ξ_copy = copy(v.N_ξ) N_x_copy = v.N_ξ === v.N_x ? N_ξ_copy : copy(v.N_x) # Preserve aliasing return FunctionValues(copy(v.ip), N_x_copy, N_ξ_copy, copy(v.dNdx), copy(v.dNdξ)) end -function precompute_values!(fv::FunctionValues, qr::QuadratureRule) - for (qp, ξ) in pairs(getpoints(qr)) - shape_gradients_and_values!(@view(fv.dNdξ[:, qp]), @view(fv.N_ξ[:, qp]), fv.ip, ξ) - end -end - getnbasefunctions(funvals::FunctionValues) = size(funvals.N_x, 1) @propagate_inbounds shape_value(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.N_x[base_func, q_point] @propagate_inbounds shape_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.dNdx[base_func, q_point] diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index 0b71be0abc..519f6b8d8d 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -47,41 +47,25 @@ end function GeometryMapping(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, ::RequiresHessian{RH}) where {T,RH} n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) - VT = Vec{getdim(ip),T} - M = zeros(T, n_shape, n_qpoints) - dMdξ = zeros(VT, n_shape, n_qpoints) + M = zeros(T, n_shape, n_qpoints) + dMdξ = zeros(Vec{getdim(ip),T}, n_shape, n_qpoints) - # Hessian (if needed, else nothing) - HT = Tensor{2,getdim(ip),T} - d2Mdξ2 = RH ? zeros(HT, n_shape, n_qpoints) : nothing + if RH # RequiresHessian + d2Mdξ2 = zeros(Tensor{2,getdim(ip),T}, n_shape, n_qpoints) + shape_hessians_gradients_and_values!(d2Mdξ2, dMdξ, M, ip, qr) + else + d2Mdξ2 = nothing + shape_gradients_and_values!(dMdξ, M, ip, qr) + end - geo_mapping = GeometryMapping(ip, M, dMdξ, d2Mdξ2) - precompute_values!(geo_mapping, qr) - return geo_mapping + return GeometryMapping(ip, M, dMdξ, d2Mdξ2) end + function Base.copy(v::GeometryMapping) d2Mdξ2_copy = v.d2Mdξ2 === nothing ? nothing : copy(v.d2Mdξ2) return GeometryMapping(copy(v.ip), copy(v.M), copy(v.dMdξ), d2Mdξ2_copy) end -precompute_values!(gm::GeometryMapping, qr::QuadratureRule) = precompute_values!(gm, RequiresHessian(gm), qr) - -function precompute_values!(gm::GeometryMapping, ::RequiresHessian{false}, qr::QuadratureRule) - ip = get_geometric_interpolation(gm) - for (qp, ξ) in pairs(getpoints(qr)) - shape_gradients_and_values!(@view(gm.dMdξ[:, qp]), @view(gm.M[:, qp]), ip, ξ) - end -end - -function precompute_values!(gm::GeometryMapping, ::RequiresHessian{true}, qr::QuadratureRule) - ip = get_geometric_interpolation(gm) - for (qp, ξ) in pairs(getpoints(qr)) - for i in 1:getngeobasefunctions(gm) - gm.d2Mdξ2[i, qp], gm.dMdξ[i, qp], gm.M[i, qp] = shape_hessian_gradient_and_value(ip, ξ, i) - end - end -end - getngeobasefunctions(geo_mapping::GeometryMapping) = size(geo_mapping.M, 1) @propagate_inbounds geometric_value(geo_mapping::GeometryMapping, q_point::Int, base_func::Int) = geo_mapping.M[base_func, q_point] get_geometric_interpolation(geo_mapping::GeometryMapping) = geo_mapping.ip diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 82e0632606..9cf44e0c0a 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -278,3 +278,16 @@ function spatial_coordinate(fe_v::AbstractValues, q_point::Int, x::AbstractVecto end return vec end + + +function shape_gradients_and_values!(gradients::AbstractMatrix, values::AbstractMatrix, ip, qr::QuadratureRule) + for (qp, ξ) in pairs(getpoints(qr)) + shape_gradients_and_values!(@view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) + end +end + +function shape_hessians_gradients_and_values!(hessians::AbstractMatrix, gradients::AbstractMatrix, values::AbstractMatrix, ip, qr::QuadratureRule) + for (qp, ξ) in pairs(getpoints(qr)) + shape_hessians_gradients_and_values!(@view(hessians[:, qp]), @view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) + end +end \ No newline at end of file diff --git a/src/interpolations.jl b/src/interpolations.jl index 1f60b118c9..5f96c181c2 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -243,6 +243,21 @@ function shape_gradients_and_values!(gradients::GAT, values::SAT, ip::IP, ξ::Ve end end +""" + shape_hessians_gradients_and_values!(hessians::AbstractVector, gradients::AbstractVector, values::AbstractVector, ip::Interpolation, ξ::Vec) + +Evaluate all shape function hessians, gradients and values of `ip` at once at the reference point `ξ` +and store them in `hessians`, `gradients`, and `values`. +""" +@propagate_inbounds function shape_hessians_gradients_and_values!(hessians::AbstractVector, gradients::AbstractVector, values::AbstractVector, ip::Interpolation, ξ::Vec) + @boundscheck checkbounds(hessians, 1:getnbasefunctions(ip)) + @boundscheck checkbounds(gradients, 1:getnbasefunctions(ip)) + @boundscheck checkbounds(values, 1:getnbasefunctions(ip)) + @inbounds for i in 1:getnbasefunctions(ip) + hessians[i], gradients[i], values[i] = shape_hessian_gradient_and_value(ip, ξ, i) + end +end + """ shape_value(ip::Interpolation, ξ::Vec, i::Int) From 9dfceeba9c3bb21a64b29e079cdcefbf690950d2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 15:41:39 +0200 Subject: [PATCH 069/172] Reintroduce precompute_values --- src/FEValues/CellValues.jl | 12 ++++++++++++ src/FEValues/FunctionValues.jl | 10 +++++++--- src/FEValues/GeometryMapping.jl | 22 +++++++++++++--------- src/PointEval/point_values.jl | 3 ++- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 484d0f4131..091813651d 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -57,6 +57,18 @@ function Base.copy(cv::CellValues) return CellValues(copy(cv.fun_values), copy(cv.geo_mapping), copy(cv.qr), copy(cv.detJdV)) end +""" + precompute_values!(cv::CellValues) + +Precompute all values for the current quadrature rule in `cv`. This method allows you to modify +the quadrature positions, and then update all relevant parts of `cv` accordingly. +Used by `PointValues`. +""" +function precompute_values!(cv::CellValues) + precompute_values!(cv.fun_values, cv.qr) + precompute_values!(cv.geo_mapping, cv.qr) +end + # Access geometry values for op = (:getngeobasefunctions, :geometric_value) eval(quote diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 7ff052a8db..3ad7f657ad 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -55,9 +55,13 @@ function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo dNdξ = zeros(typeof_dNdξ(T, ip_dims), n_shape, n_qpoints) dNdx = fill(zero(typeof_dNdx(T, ip_dims)) * T(NaN), n_shape, n_qpoints) - shape_gradients_and_values!(dNdξ, N_ξ, ip, qr) # Compute N_ξ and dNdξ - - return FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) + fv = FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) + precompute_values!(fv, qr) # Separate function for qr point update in PointValues + return fv +end + +function precompute_values!(fv::FunctionValues, qr) + shape_gradients_and_values!(fv.dNdξ, fv.N_ξ, fv.ip, qr) end function Base.copy(v::FunctionValues) diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index 519f6b8d8d..fe3cb0a442 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -47,18 +47,22 @@ end function GeometryMapping(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, ::RequiresHessian{RH}) where {T,RH} n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) + M = zeros(T, n_shape, n_qpoints) dMdξ = zeros(Vec{getdim(ip),T}, n_shape, n_qpoints) - - if RH # RequiresHessian - d2Mdξ2 = zeros(Tensor{2,getdim(ip),T}, n_shape, n_qpoints) - shape_hessians_gradients_and_values!(d2Mdξ2, dMdξ, M, ip, qr) - else - d2Mdξ2 = nothing - shape_gradients_and_values!(dMdξ, M, ip, qr) - end + d2Mdξ2 = RH ? zeros(Tensor{2,getdim(ip),T}, n_shape, n_qpoints) : nothing - return GeometryMapping(ip, M, dMdξ, d2Mdξ2) + gm = GeometryMapping(ip, M, dMdξ, d2Mdξ2) + precompute_values!(gm, qr) # Separate function for qr point update in PointValues + return gm +end + +precompute_values!(gm::GeometryMapping, qr) = precompute_values!(gm, qr, RequiresHessian(gm)) +function precompute_values!(gm::GeometryMapping, qr, ::RequiresHessian{false}) + shape_gradients_and_values!(gm.dMdξ, gm.M, gm.ip, qr) +end +function precompute_values!(gm::GeometryMapping, qr, ::RequiresHessian{true}) + shape_hessians_gradients_and_values!(gm.d2Mdξ2, gm.dMdξ, gm.M, gm.ip, qr) end function Base.copy(v::GeometryMapping) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 57f16864fb..0be54d0c0b 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -65,7 +65,8 @@ function reinit!(pv::PointValues, x::AbstractVector{<:Vec{D}}, ξ::Vec{D}) where # Update the quadrature point location qr_points = getpoints(pv.cv.qr) qr_points[1] = ξ - precompute_values!(pv.cv.fun_values, pv.cv.qr) # See Issue #763, should also update dMdξ!, but separate issue + # Precompute all values again to reflect the updated ξ coordinate + precompute_values!(pv.cv) # Regular reinit reinit!(pv.cv, x) return nothing From ea7e33efad7a36874db24537fe4d938d78f99fe2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 16:22:01 +0200 Subject: [PATCH 070/172] Fix test of show --- test/test_cellvalues.jl | 12 +++++++----- test/test_facevalues.jl | 10 ++++++---- test/test_utils.jl | 7 +++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index f85245c07e..25ba7992bf 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -325,13 +325,15 @@ end end @testset "show" begin - # Just smoke test cv_quad = CellValues(QuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()^2) + showstring = show_as_string(cv_quad) + @test startswith(showstring, "CellValues(vdim=2, rdim=2, and sdim=2): 4 quadrature points") + @test contains(showstring, "Lagrange{RefQuadrilateral, 2}()^2") + cv_wedge = CellValues(QuadratureRule{RefPrism}(2), Lagrange{RefPrism,2}()) - show(stdout, MIME"text/plain"(), cv_quad) - println(stdout) - show(stdout, MIME"text/plain"(), cv_wedge) - println(stdout) + showstring = show_as_string(cv_wedge) + @test startswith(showstring, "CellValues(scalar, rdim=3, and sdim=3): 5 quadrature points") + @test contains(showstring, "Lagrange{RefPrism, 2}()") end @testset "SimpleCellValues" begin diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index 6eb875e500..e4b113b224 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -130,12 +130,14 @@ end @testset "show" begin # Just smoke test to make sure show doesn't error. fv = FaceValues(FaceQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()) - show(stdout, MIME"text/plain"(), fv) - println(stdout) + showstring = show_as_string(fv) + @test startswith(showstring, "FaceValues(scalar, rdim=2, sdim=2): 2 quadrature points per face") + @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()") + @test contains(showstring, "Geometric interpolation: Lagrange{RefQuadrilateral, 1}()^2") fv.qr.face_rules[1] = deepcopy(fv.qr.face_rules[1]) push!(Ferrite.getweights(fv.qr.face_rules[1]), 1) - show(stdout, MIME"text/plain"(), fv) - println(stdout) + showstring = show_as_string(fv) + @test startswith(showstring, "FaceValues(scalar, rdim=2, sdim=2): (3, 2, 2, 2) quadrature points on each face") end end # of testset diff --git a/test/test_utils.jl b/test/test_utils.jl index d51fb8866c..a5a4d6ba8e 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -259,3 +259,10 @@ getfacerefshape(::Hexahedron, ::Int) = RefQuadrilateral getfacerefshape(::Tetrahedron, ::Int) = RefTriangle getfacerefshape(::Pyramid, face::Int) = face == 1 ? RefQuadrilateral : RefTriangle getfacerefshape(::Wedge, face::Int) = face ∈ (1,5) ? RefTriangle : RefQuadrilateral + +# For testing of show of various types +function show_as_string(value, mime=MIME"text/plain"()) + io = IOBuffer() + show(IOContext(io), mime, value) + return String(take!(io)) +end \ No newline at end of file From 1a7e9c91012e65ce87a937a1d8840ed2509af01e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 17:06:20 +0200 Subject: [PATCH 071/172] Remove unused internal methods --- src/FEValues/common_values.jl | 3 --- src/FEValues/face_integrals.jl | 5 +---- src/PointEval/point_values.jl | 1 + 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 9cf44e0c0a..a180f3c294 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -4,9 +4,6 @@ using Base: @propagate_inbounds @noinline throw_detJ_not_pos(detJ) = throw(ArgumentError("det(J) is not positive: det(J) = $(detJ)")) -getnbasefunctions(cv::AbstractValues) = size(cv.N, 1) -getngeobasefunctions(cv::AbstractValues) = size(cv.M, 1) - function checkquadpoint(cv::Union{CellValues, FaceValues, PointValues}, qp::Int) 0 < qp <= getnquadpoints(cv) || error("quadrature point out of range") return nothing diff --git a/src/FEValues/face_integrals.jl b/src/FEValues/face_integrals.jl index e001eb5f3d..a4974110c0 100644 --- a/src/FEValues/face_integrals.jl +++ b/src/FEValues/face_integrals.jl @@ -7,16 +7,13 @@ cell's face. face_to_element_transformation """ - weighted_normal(J::AbstractTensor, fv::FaceValues, face::Int) weighted_normal(J::AbstractTensor, ::Type{<:AbstractRefShape}, face::Int) Compute the vector normal to the face weighted by the area ratio between the face and the reference face. This is computed by taking the cross product of the Jacobian components that align to the face local axis. """ -function weighted_normal(J::AbstractTensor, fv::FaceValues, face::Int) - return weighted_normal(J, getrefshape(fv.func_interp), face) -end +function weighted_normal end """ create_face_quad_rule(::Type{RefShape}, w::Vector{T}, p::Vector{Vec{N, T}}) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 0be54d0c0b..5128c1b459 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -86,6 +86,7 @@ function PointValuesInternal(ξ::Vec{dim, T}, ip::IP) where {dim, T, shape <: Ab return PointValuesInternal{IP, eltype(N)}(N, ip) end +getnbasefunctions(pv::PointValuesInternal) = size(pv.N, 1) getnquadpoints(pv::PointValuesInternal) = 1 shape_value_type(::PointValuesInternal{<:Any, N_t}) where {N_t} = N_t shape_value(pv::PointValuesInternal, qp::Int, i::Int) = (@assert qp == 1; pv.N[i]) From 41ae092166dfe1e37f8f1095b745802dc1c42ac8 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 17:40:19 +0200 Subject: [PATCH 072/172] Test error paths --- src/FEValues/FunctionValues.jl | 3 +-- test/test_cellvalues.jl | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 3ad7f657ad..a73c07107d 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -93,8 +93,7 @@ function check_reinit_sdim_consistency(valname, gradtype, ::Type{<:Vec{sdim}}) w end check_reinit_sdim_consistency(_, ::Val{sdim}, ::Val{sdim}) where sdim = nothing function check_reinit_sdim_consistency(valname, ::Val{sdim_val}, ::Val{sdim_x}) where {sdim_val, sdim_x} - error("""The $valname and coordinates have different spatial dimensions, $sdim_val and $sdim_x. - Could it be that you forgot to vectorize the geometric interpolation when constructing the $valname?""") + throw(ArgumentError("The $valname (sdim=$sdim_val) and coordinates (sdim=$sdim_x) have different spatial dimensions.")) end # Mapping types diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 25ba7992bf..316c0f8d41 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -137,18 +137,26 @@ end @testset "error paths in function_* and reinit!" begin dim = 2 qp = 1 + cell = Triangle((1,2,3)) ip = Lagrange{RefTriangle,1}() + ip_nedelec = Nedelec{2,RefTriangle,1}() qr = QuadratureRule{RefTriangle}(1) qr_f = FaceQuadratureRule{RefTriangle}(1) csv = CellValues(qr, ip) cvv = CellValues(qr, VectorizedInterpolation(ip)) + csv_embedded = CellValues(qr, ip, ip^3) + cv_nedelec = CellValues(qr, ip_nedelec, ip) fsv = FaceValues(qr_f, ip) fvv = FaceValues(qr_f, VectorizedInterpolation(ip)) + fsv_embedded = FaceValues(qr_f, ip, ip^3) + fv_nedelec = FaceValues(qr_f, ip_nedelec, ip) x, n = valid_coordinates_and_normals(ip) reinit!(csv, x) reinit!(cvv, x) reinit!(fsv, x, 1) reinit!(fvv, x, 1) + reinit!(cv_nedelec, x, cell) + reinit!(fv_nedelec, x, 1, cell) # Wrong number of coordinates xx = [x; x] @@ -162,6 +170,14 @@ end @test_throws ArgumentError spatial_coordinate(fsv, qp, xx) @test_throws ArgumentError spatial_coordinate(fvv, qp, xx) + # Wrong dimension of coordinates + @test_throws ArgumentError reinit!(csv_embedded, x) + @test_throws ArgumentError reinit!(fsv_embedded, x, 1) + + # Missing cell input when required + @test_throws ArgumentError reinit!(cv_nedelec, x) + @test_throws ArgumentError reinit!(fv_nedelec, x, 1) + # Wrong number of (local) dofs # Scalar values, scalar dofs ue = rand(getnbasefunctions(csv) + 1) From 9b020da0de310792945c40672a23d5171cc837a7 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 17:52:18 +0200 Subject: [PATCH 073/172] Remove unused test-code --- src/Grid/grid.jl | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Grid/grid.jl b/src/Grid/grid.jl index 310fbc13ac..d3b7bc68cd 100644 --- a/src/Grid/grid.jl +++ b/src/Grid/grid.jl @@ -773,22 +773,6 @@ end ################################# #### Orientation of Entities #### ################################# -function face_sign(::AbstractCell{<:AbstractRefShape{1}}, facenr) - error("Not defined") -end -function face_sign(cell::AbstractCell{<:AbstractRefShape{2}}, facenr) - vertices = faces(cell)[facenr] - return vertices[1] < vertices[2] ? 1 : -1 -end -function face_sign(::AbstractCell{<:AbstractRefShape{3}}, facenr) - error("Implement me") -end - -function edge_sign(cell::AbstractCell, edgenr) - vertices = edges(cell)[edgenr] - return vertices[1] < vertices[2] ? 1 : -1 -end - # @TODO merge this code with into the logic in `ConstraintHandler`. """ From 00e2e4fff01d9432f23385d289ef25afd6fc743e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 17:54:11 +0200 Subject: [PATCH 074/172] Test show(::PointValues) --- test/test_cellvalues.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 316c0f8d41..728588d34f 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -350,6 +350,11 @@ end showstring = show_as_string(cv_wedge) @test startswith(showstring, "CellValues(scalar, rdim=3, and sdim=3): 5 quadrature points") @test contains(showstring, "Lagrange{RefPrism, 2}()") + + pv = PointValues(cv_wedge) + pv_showstring = show_as_string(pv) + @test startswith(pv_showstring, "PointValues containing") + @test contains(pv_showstring, showstring) end @testset "SimpleCellValues" begin From 09cf607cbfaa0517b08ffa4be0ae55ffb456daf5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 17:59:09 +0200 Subject: [PATCH 075/172] Fix test of show(::PointValues) --- test/test_cellvalues.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 728588d34f..5ff1e72f24 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -344,17 +344,17 @@ end cv_quad = CellValues(QuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()^2) showstring = show_as_string(cv_quad) @test startswith(showstring, "CellValues(vdim=2, rdim=2, and sdim=2): 4 quadrature points") - @test contains(showstring, "Lagrange{RefQuadrilateral, 2}()^2") + @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()^2") cv_wedge = CellValues(QuadratureRule{RefPrism}(2), Lagrange{RefPrism,2}()) showstring = show_as_string(cv_wedge) @test startswith(showstring, "CellValues(scalar, rdim=3, and sdim=3): 5 quadrature points") - @test contains(showstring, "Lagrange{RefPrism, 2}()") + @test contains(showstring, "Function interpolation: Lagrange{RefPrism, 2}()") pv = PointValues(cv_wedge) pv_showstring = show_as_string(pv) @test startswith(pv_showstring, "PointValues containing") - @test contains(pv_showstring, showstring) + @test contains(pv_showstring, "Function interpolation: Lagrange{RefPrism, 2}()") end @testset "SimpleCellValues" begin From 43970f35f6632d776dcf49805c12ce6d8dc1d7b2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 21 Oct 2023 18:59:31 +0200 Subject: [PATCH 076/172] Remove unused methods --- src/FEValues/CellValues.jl | 11 +++++------ src/FEValues/FaceValues.jl | 6 +----- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 091813651d..3f7e00d1a8 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -70,17 +70,15 @@ function precompute_values!(cv::CellValues) end # Access geometry values -for op = (:getngeobasefunctions, :geometric_value) - eval(quote - @propagate_inbounds $op(cv::CellValues, args...) = $op(cv.geo_mapping, args...) - end) -end +@propagate_inbounds getngeobasefunctions(cv::CellValues) = getngeobasefunctions(cv.geo_mapping) +@propagate_inbounds geometric_value(cv::CellValues, args...) = geometric_value(cv.geo_mapping, args...) +get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_mapping) + getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] # Accessors for function values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) get_function_interpolation(cv::CellValues) = get_function_interpolation(cv.fun_values) -get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_mapping) shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) @@ -89,6 +87,7 @@ for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient) @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) end) end + # Access quadrature rule values getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 1b1e473242..04b99a3ea2 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -71,11 +71,7 @@ get_function_interpolation(fv::FaceValues) = get_function_interpolation(get_fun_ get_geometric_interpolation(fv::FaceValues) = get_geometric_interpolation(get_geo_mapping(fv)) get_geo_mapping(fv::FaceValues) = @inbounds fv.geo_mapping[getcurrentface(fv)] -for op = (:getngeobasefunctions, :geometric_value) - eval(quote - @propagate_inbounds $op(fv::FaceValues, args...) = $op(get_geo_mapping(fv), args...) - end) -end +@propagate_inbounds geometric_value(fv::FaceValues, args...) = geometric_value(get_geo_mapping(fv), args...) get_fun_values(fv::FaceValues) = @inbounds fv.fun_values[getcurrentface(fv)] for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient, :shape_curl) From 005659af63fe16a38d5f985d731bebe63621a344 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 23 Oct 2023 17:45:04 +0200 Subject: [PATCH 077/172] Grammar and stylistic fixes --- docs/make.jl | 1 - docs/src/assets/references.bib | 2 +- docs/src/reference/fevalues.md | 3 ++- docs/src/topics/FEValues.md | 8 +++++--- docs/src/topics/SimpleCellValues_literate.jl | 2 +- src/FEValues/CellValues.jl | 2 +- test/test_utils.jl | 2 +- 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index ced312cb19..6a8635ea4f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -63,7 +63,6 @@ bibtex_plugin = CitationBibliography( "Topic guides" => [ "Topic guide overview" => "topics/index.md", "topics/fe_intro.md", - #"topics/interpolations.md", "topics/FEValues.md", "topics/degrees_of_freedom.md", "topics/assembly.md", diff --git a/docs/src/assets/references.bib b/docs/src/assets/references.bib index a237a86296..46f189bb15 100644 --- a/docs/src/assets/references.bib +++ b/docs/src/assets/references.bib @@ -71,4 +71,4 @@ @misc{Kirby2017 doi={10.48550/arXiv.1706.09017}, archivePrefix={arXiv}, primaryClass={math.NA} -} \ No newline at end of file +} diff --git a/docs/src/reference/fevalues.md b/docs/src/reference/fevalues.md index 757760fbc6..3bc1e5c988 100644 --- a/docs/src/reference/fevalues.md +++ b/docs/src/reference/fevalues.md @@ -7,7 +7,8 @@ DocTestSetup = :(using Ferrite) ## Main types [`CellValues`](@ref) and [`FaceValues`](@ref) are the most common -subtypes of `Ferrite.AbstractValues`. +subtypes of `Ferrite.AbstractValues`. For more details about how +these work, please see the related [topic guide](@ref fevalues_topicguide). ```@docs CellValues diff --git a/docs/src/topics/FEValues.md b/docs/src/topics/FEValues.md index 2039f2846c..11831e91f2 100644 --- a/docs/src/topics/FEValues.md +++ b/docs/src/topics/FEValues.md @@ -1,8 +1,10 @@ -# FEValues -A key type of object in `Ferrite.jl` are the so-called `FEValues`, where the most common ones are `CellValues` and `FaceValues`. 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 [`FaceValues`](@ref). In order for these values to be correct, it is necessary to reinitialize these for the current cell by using the [`reinit!`](@ref) function. This maps the values from the reference cell to the actual cell, a process which is described in detail below, see [Mapping of finite elements](@ref mapping_theory). Thereafter, we show an implementation of a [`SimpleCellValues`](@ref SimpleCellValues) type for the most standard case, excluding the generalizations and optimization that complicates the code for e.g. `CellValues`. +# [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 `FaceValues`. 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 [`FaceValues`](@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, is reinitialized for each cell by calling the `reinit!` function. The main part of this calculation, considers how to map the functions described on the reference cell, to the actual cell. +The shape functions and gradients stored in an `FEValues` object, are reinitialized for each cell by calling the `reinit!` function. +The main part of this calculation, considers how to map the values and derivatives of the shape functions, +defined on the reference cell, to the actual cell. The geometric mapping of a finite element from the reference coordinates to the real coordinates is shown in the following illustration. diff --git a/docs/src/topics/SimpleCellValues_literate.jl b/docs/src/topics/SimpleCellValues_literate.jl index 8f6b2469a7..f648f728eb 100644 --- a/docs/src/topics/SimpleCellValues_literate.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -91,4 +91,4 @@ reinit!(cv, x) ue = rand(getnbasefunctions(simple_cv)) q_point = 2 @test function_value(cv, q_point, ue) ≈ function_value(simple_cv, q_point, ue) -@test function_gradient(cv, q_point, ue) ≈ function_gradient(simple_cv, q_point, ue) \ No newline at end of file +@test function_gradient(cv, q_point, ue) ≈ function_gradient(simple_cv, q_point, ue) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 3f7e00d1a8..a363cc7f3d 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -124,4 +124,4 @@ function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) print(io, getnquadpoints(cv), " quadrature points") print(io, "\n Function interpolation: "); show(io, d, ip_fun) print(io, "\nGeometric interpolation: "); show(io, d, ip_geo^sdim) -end \ No newline at end of file +end diff --git a/test/test_utils.jl b/test/test_utils.jl index a5a4d6ba8e..1a40b54c84 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -265,4 +265,4 @@ function show_as_string(value, mime=MIME"text/plain"()) io = IOBuffer() show(IOContext(io), mime, value) return String(take!(io)) -end \ No newline at end of file +end From 69b50b42bfc67019f87b851c9637447001c4c531 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 23 Oct 2023 18:00:04 +0200 Subject: [PATCH 078/172] Add further devdoc explanations --- docs/src/devdocs/FEValues.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/src/devdocs/FEValues.md b/docs/src/devdocs/FEValues.md index 81a349ac27..9bc4a93571 100644 --- a/docs/src/devdocs/FEValues.md +++ b/docs/src/devdocs/FEValues.md @@ -14,4 +14,15 @@ Ferrite.GeometryMapping Ferrite.MappingValues Ferrite.FunctionValues -``` \ No newline at end of file +``` + +## Interface +* The function interpolation, `ip_fun`, determines how it should be mapped, by defining `get_mapping_type(ip_fun)` for its type. +* The mapping type, e.g. `IdentityMapping`, decides the requirements for `GeometryValues`, specifically if the `hessian` $\partial^2M/\partial\xi^2$, + of the geometric shape functions, $M(\xi)$, on the reference cell should be precalculated or not. + ***Note:*** *This should also in the future be determined by the required order of derivatives to be mapped in `FunctionValues`* +* As the first part of `reinit!`, the `MappingValues` are calculated based on the cell's coordinates. If the `GeometricMapping` contains the hessian + on the reference cell, the `hessian` on the actual cell, $\partial^2M/\partial x^2$, is calculated and returned in `MappingValues`. Otherwise, only + the jacobian, $\partial M/\partial x$, is calculated. +* In the second part of `reinit!`, The `MappingValues`, containing sufficient information for the current quadrature point, is given to `apply_mapping!`. + This allows the shape values and gradients stored in `FunctionValues`, to be mapped to the current cell by calling `apply_mapping!`. From f829e8881df0ebde666259f0d8170c081a39c0fd Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 4 Nov 2023 13:07:16 +0100 Subject: [PATCH 079/172] Describe FEValues interface --- docs/src/devdocs/FEValues.md | 16 +++++++++++++++- docs/src/topics/SimpleCellValues_literate.jl | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/src/devdocs/FEValues.md b/docs/src/devdocs/FEValues.md index 9bc4a93571..04beb263ad 100644 --- a/docs/src/devdocs/FEValues.md +++ b/docs/src/devdocs/FEValues.md @@ -16,7 +16,7 @@ Ferrite.MappingValues Ferrite.FunctionValues ``` -## Interface +## How CellValues and FaceValues works * The function interpolation, `ip_fun`, determines how it should be mapped, by defining `get_mapping_type(ip_fun)` for its type. * The mapping type, e.g. `IdentityMapping`, decides the requirements for `GeometryValues`, specifically if the `hessian` $\partial^2M/\partial\xi^2$, of the geometric shape functions, $M(\xi)$, on the reference cell should be precalculated or not. @@ -26,3 +26,17 @@ Ferrite.FunctionValues the jacobian, $\partial M/\partial x$, is calculated. * In the second part of `reinit!`, The `MappingValues`, containing sufficient information for the current quadrature point, is given to `apply_mapping!`. This allows the shape values and gradients stored in `FunctionValues`, to be mapped to the current cell by calling `apply_mapping!`. + +## Custom FEValues +Custom FEValues, `fe_v`, should normally implement the `reinit!` method. +Additionally, for normal functionality the `getnquadpoints` should be implemented. +Note that asking for the `n`th quadrature point must be inside array bounds if +`1<=n<:getnquadpoints(fe_v)` +(`checkquadpoint` can, alternatively, be dispatched to check that `n` is inbounds.) + +Supporting `function_value`, `function_gradient`, `function_symmetric_gradient`, `function_divergence`, and `function_curl`, +requires implementing `getnbasefunctions`, `shape_value`, and `shape_gradient`. +Note that asking for the `i`th shape value or gradient must be inside array bounds if `1<=i<:getnbasefunctions(fe_v)` + +Supporting `spatial_coordinate` requires implementing `getngeobasefunctions` and `geometric_value`. +Note that asking for the `i`th geometric value must be inside array bounds if `1<=i<:getngeobasefunctions(fe_v)` \ No newline at end of file diff --git a/docs/src/topics/SimpleCellValues_literate.jl b/docs/src/topics/SimpleCellValues_literate.jl index f648f728eb..0bbbe9a220 100644 --- a/docs/src/topics/SimpleCellValues_literate.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -47,8 +47,9 @@ function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Int SimpleCellValues(N, dNdξ, dNdx, M, dMdξ, weights, detJdV) end; -# We first define the `getnbasefunctions` +# We first define `getnbasefunctions` and `getnquadpoints` Ferrite.getnbasefunctions(cv::SimpleCellValues) = size(cv.N, 1); +Ferrite.getnquadpoints(cv::SimpleCellValues) = size(cv.N, 2); # Before we define the `reinit!` function to calculate the cached # values `dNdx` and `detJdV` for the current cell From d4c36c25dd2bb713d5f4bd40e800243945db1058 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 4 Nov 2023 13:08:24 +0100 Subject: [PATCH 080/172] Delete checkquadpoint method that causes illegal (at)inbounds --- src/FEValues/common_values.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index a180f3c294..07e94c0fd4 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -4,11 +4,10 @@ using Base: @propagate_inbounds @noinline throw_detJ_not_pos(detJ) = throw(ArgumentError("det(J) is not positive: det(J) = $(detJ)")) -function checkquadpoint(cv::Union{CellValues, FaceValues, PointValues}, qp::Int) - 0 < qp <= getnquadpoints(cv) || error("quadrature point out of range") +function checkquadpoint(fe_v::AbstractValues, qp::Int) + 0 < qp <= getnquadpoints(fe_v) || error("quadrature point out of range") return nothing end -checkquadpoint(_, _::Int) = nothing @noinline function throw_incompatible_dof_length(length_ue, n_base_funcs) throw(ArgumentError( From 2550a75b209fb94a75e009f13ce9fa81772326c0 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 4 Nov 2023 15:49:07 +0100 Subject: [PATCH 081/172] Fix typo in devdocs --- docs/src/devdocs/FEValues.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/devdocs/FEValues.md b/docs/src/devdocs/FEValues.md index 04beb263ad..d4cbc8c9bf 100644 --- a/docs/src/devdocs/FEValues.md +++ b/docs/src/devdocs/FEValues.md @@ -31,12 +31,12 @@ Ferrite.FunctionValues Custom FEValues, `fe_v`, should normally implement the `reinit!` method. Additionally, for normal functionality the `getnquadpoints` should be implemented. Note that asking for the `n`th quadrature point must be inside array bounds if -`1<=n<:getnquadpoints(fe_v)` +`1 <= n <= getnquadpoints(fe_v)` (`checkquadpoint` can, alternatively, be dispatched to check that `n` is inbounds.) Supporting `function_value`, `function_gradient`, `function_symmetric_gradient`, `function_divergence`, and `function_curl`, requires implementing `getnbasefunctions`, `shape_value`, and `shape_gradient`. -Note that asking for the `i`th shape value or gradient must be inside array bounds if `1<=i<:getnbasefunctions(fe_v)` +Note that asking for the `i`th shape value or gradient must be inside array bounds if `1 <= i <= getnbasefunctions(fe_v)` Supporting `spatial_coordinate` requires implementing `getngeobasefunctions` and `geometric_value`. -Note that asking for the `i`th geometric value must be inside array bounds if `1<=i<:getngeobasefunctions(fe_v)` \ No newline at end of file +Note that asking for the `i`th geometric value must be inside array bounds if `1 <= i <= getngeobasefunctions(fe_v)` \ No newline at end of file From acefcde0d082197db6987088f69889f3c23af5ca Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 5 Nov 2023 13:26:05 +0100 Subject: [PATCH 082/172] Add type specification to shape_value and shape_gradient docstring spec --- docs/src/reference/fevalues.md | 4 ++-- src/FEValues/common_values.jl | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/src/reference/fevalues.md b/docs/src/reference/fevalues.md index 3bc1e5c988..98f85f252b 100644 --- a/docs/src/reference/fevalues.md +++ b/docs/src/reference/fevalues.md @@ -24,8 +24,8 @@ reinit! getnquadpoints getdetJdV -shape_value -shape_gradient +shape_value(::Ferrite.AbstractValues, ::Int, ::Int) +shape_gradient(::Ferrite.AbstractValues, ::Int, ::Int) shape_symmetric_gradient shape_divergence diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 07e94c0fd4..fec2c19080 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -64,7 +64,7 @@ function getdetJdV end Return the value of shape function `base_function` evaluated in quadrature point `q_point`. """ -function shape_value end +shape_value(fe_v::AbstractValues, q_point::Int, base_function::Int) """ geometric_value(fe_v::AbstractValues, q_point, base_function::Int) @@ -72,7 +72,7 @@ function shape_value end Return the value of the geometric shape function `base_function` evaluated in quadrature point `q_point`. """ -function geometric_value end +geometric_value(fe_v::AbstractValues, q_point::Int, base_function::Int) """ shape_gradient(fe_v::AbstractValues, q_point::Int, base_function::Int) @@ -80,7 +80,7 @@ function geometric_value end Return the gradient of shape function `base_function` evaluated in quadrature point `q_point`. """ -function shape_gradient end +shape_gradient(fe_v::AbstractValues, q_point::Int, base_function::Int) """ shape_symmetric_gradient(fe_v::AbstractValues, q_point::Int, base_function::Int) From 94c174f11e628fb7daeeb384bf99bfb4b3d050b2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 10 Nov 2023 19:49:22 +0100 Subject: [PATCH 083/172] Testing it out --- src/FEValues/CellValues.jl | 10 +++- src/FEValues/FaceValues.jl | 11 +++- src/FEValues/FunctionValues.jl | 87 ++++++++++++++++++++++++----- src/FEValues/GeometryMapping.jl | 98 +++++++++++++++++++++------------ src/FEValues/common_values.jl | 5 ++ 5 files changed, 155 insertions(+), 56 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index a363cc7f3d..9fc4997d19 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -41,9 +41,13 @@ struct CellValues{FV, GM, QR, detT<:AbstractVector} <: AbstractCellValues qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation) where T - geo_mapping = GeometryMapping(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) - fun_values = FunctionValues(T, ip_fun, qr, ip_geo) +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; difforder=Val(1)) where T + _difforder(::Val{N}) where N = N + _difforder(N::Int) = N + GeoDiffOrder = increased_diff_order(get_mapping_type(ip_fun)) + _difforder(difforder) + FunDiffOrder = _difforder(difforder) + geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) + fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) detJdV = fill(T(NaN), length(getweights(qr))) return CellValues(fun_values, geo_mapping, qr, detJdV) end diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 04b99a3ea2..3ca56c39e0 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -40,9 +40,14 @@ struct FaceValues{FV, GM, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:Abstract current_face::ScalarWrapper{Int} end -function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun)) where {T,sdim} - geo_mapping = [GeometryMapping(T, ip_geo.ip, qr, RequiresHessian(ip_fun, ip_geo)) for qr in fqr.face_rules] - fun_values = [FunctionValues(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] +function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun); difforder=Val(1)) where {T,sdim} + _difforder(::Val{N}) where N = N + _difforder(N::Int) = N + GeoDiffOrder = increased_diff_order(get_mapping_type(ip_fun)) + _difforder(difforder) + FunDiffOrder = _difforder(difforder) + + geo_mapping = [GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) for qr in fqr.face_rules] + fun_values = [FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) detJdV = fill(T(NaN), max_nquadpoints) normals = fill(zero(Vec{sdim,T})*T(NaN), max_nquadpoints) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index a73c07107d..82da88c074 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -37,14 +37,35 @@ cell (precalculated) and the real cell (updated in `reinit!`). """ FunctionValues -struct FunctionValues{IP, N_t, dNdx_t, dNdξ_t} +struct FunctionValues{DiffOrder, IP, N_t, dNdx_t, dNdξ_t} ip::IP # ::Interpolation N_x::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} N_ξ::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} - dNdx::dNdx_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} - dNdξ::dNdξ_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} + dNdx::dNdx_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} or Nothing + dNdξ::dNdξ_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} or Nothing + function FunctionValues(ip::Interpolation, N_x::N_t, N_ξ::N_t, ::Nothing, ::Nothing) where {N_t<:AbstractMatrix} + return new{0, typeof(ip), N_t, Nothing, Nothing}(ip, N_x, N_ξ, nothing, nothing) + end + function FunctionValues(ip::Interpolation, N_x::N_t, N_ξ::N_t, dNdx::AbstractMatrix, dNdξ::AbstractMatrix) where {N_t<:AbstractMatrix} + return new{1, typeof(ip), N_t, typeof(dNdx), typeof(dNdξ)}(ip, N_x, N_ξ, dNdx, dNdξ) + end end function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T + return FunctionValues{1}(T, ip, qr, ip_geo) +end +function FunctionValues{0}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T + ip_dims = InterpolationDims(ip, ip_geo) + n_shape = getnbasefunctions(ip) + n_qpoints = getnquadpoints(qr) + + N_ξ = zeros(typeof_N(T, ip_dims), n_shape, n_qpoints) + N_x = isa(get_mapping_type(ip), IdentityMapping) ? N_ξ : similar(N_ξ) + + fv = FunctionValues(ip, N_x, N_ξ, nothing, nothing) + precompute_values!(fv, qr) # Separate function for qr point update in PointValues + return fv +end +function FunctionValues{1}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T ip_dims = InterpolationDims(ip, ip_geo) n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) @@ -60,14 +81,19 @@ function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo return fv end -function precompute_values!(fv::FunctionValues, qr) +function precompute_values!(fv::FunctionValues{0}, qr) + shape_values!(fv.N_ξ, fv.ip, qr) +end +function precompute_values!(fv::FunctionValues{1}, qr) shape_gradients_and_values!(fv.dNdξ, fv.N_ξ, fv.ip, qr) end function Base.copy(v::FunctionValues) N_ξ_copy = copy(v.N_ξ) N_x_copy = v.N_ξ === v.N_x ? N_ξ_copy : copy(v.N_x) # Preserve aliasing - return FunctionValues(copy(v.ip), N_x_copy, N_ξ_copy, copy(v.dNdx), copy(v.dNdξ)) + copy_or_nothing(x) = copy(x) + copy_or_nothing(::Nothing) = nothing + return FunctionValues(copy(v.ip), N_x_copy, N_ξ_copy, copy_or_nothing(v.dNdx), copy_or_nothing(v.dNdξ)) end getnbasefunctions(funvals::FunctionValues) = size(funvals.N_x, 1) @@ -107,15 +133,15 @@ struct ContravariantPiolaMapping end get_mapping_type(fv::FunctionValues) = get_mapping_type(fv.ip) """ - requires_hessian(mapping) + increased_diff_order(mapping) -Does the `mapping` type require the hessian, d²M/dx², +How many order higher geometric derivatives are required to to map the function values and gradients from the reference cell to the real cell geometry? """ -requires_hessian(::IdentityMapping) = false -requires_hessian(::ContravariantPiolaMapping) = true -requires_hessian(::CovariantPiolaMapping) = true +increased_diff_order(::IdentityMapping) = 0 +increased_diff_order(::ContravariantPiolaMapping) = 1 +increased_diff_order(::CovariantPiolaMapping) = 1 # Support for embedded elements calculate_Jinv(J::Tensor{2}) = inv(J) @@ -131,11 +157,19 @@ calculate_Jinv(J::SMatrix) = pinv(J) # Vector interpolations with sdim > rdim @inline dothelper(B::SMatrix{vdim, rdim}, A::SMatrix{rdim, sdim}) where {vdim, rdim, sdim} = B * A -@inline function apply_mapping!(funvals::FunctionValues, args...) - return apply_mapping!(funvals, get_mapping_type(funvals), args...) +# ============= +# Apply mapping +# ============= +@inline function apply_mapping!(funvals::FunctionValues, q_point::Int, args...) + return apply_mapping!(funvals, get_mapping_type(funvals), q_point, args...) end -@inline function apply_mapping!(funvals::FunctionValues, ::IdentityMapping, q_point::Int, mapping_values, args...) +# Identity mapping +@inline function apply_mapping!(::FunctionValues{0}, ::IdentityMapping, ::Int, mapping_values, args...) + return nothing +end + +@inline function apply_mapping!(funvals::FunctionValues{1}, ::IdentityMapping, q_point::Int, mapping_values, args...) Jinv = calculate_Jinv(getjacobian(mapping_values)) @inbounds for j in 1:getnbasefunctions(funvals) #funvals.dNdx[j, q_point] = funvals.dNdξ[j, q_point] ⋅ Jinv # TODO via Tensors.jl @@ -144,7 +178,18 @@ end return nothing end -@inline function apply_mapping!(funvals::FunctionValues, ::CovariantPiolaMapping, q_point::Int, mapping_values, cell) +# Covariant Piola Mapping +@inline function apply_mapping!(funvals::FunctionValues{0}, ::CovariantPiolaMapping, q_point::Int, mapping_values, cell) + Jinv = inv(getjacobian(mapping_values)) + @inbounds for j in 1:getnbasefunctions(funvals) + d = get_direction(funvals.ip, j, cell) + N_ξ = funvals.N_ξ[j, q_point] + funvals.N_x[j, q_point] = d*(N_ξ ⋅ Jinv) + end + return nothing +end + +@inline function apply_mapping!(funvals::FunctionValues{1}, ::CovariantPiolaMapping, q_point::Int, mapping_values, cell) H = gethessian(mapping_values) Jinv = inv(getjacobian(mapping_values)) @inbounds for j in 1:getnbasefunctions(funvals) @@ -157,7 +202,19 @@ end return nothing end -@inline function apply_mapping!(funvals::FunctionValues, ::ContravariantPiolaMapping, q_point::Int, mapping_values, cell) +# Contravariant Piola Mapping +@inline function apply_mapping!(funvals::FunctionValues{0}, ::ContravariantPiolaMapping, q_point::Int, mapping_values, cell) + J = getjacobian(mapping_values) + detJ = det(J) + @inbounds for j in 1:getnbasefunctions(funvals) + d = get_direction(funvals.ip, j, cell) + N_ξ = funvals.N_ξ[j, q_point] + funvals.N_x[j, q_point] = d*(J ⋅ N_ξ)/detJ + end + return nothing +end + +@inline function apply_mapping!(funvals::FunctionValues{1}, ::ContravariantPiolaMapping, q_point::Int, mapping_values, cell) H = gethessian(mapping_values) J = getjacobian(mapping_values) Jinv = inv(J) diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index fe3cb0a442..6c72563373 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -8,74 +8,100 @@ used when mapping the `FunctionValues` to the current cell during `reinit!`. """ MappingValues -struct MappingValues{JT, HT<:Union{Nothing,AbstractTensor{3}}} +struct MappingValues{JT, HT} J::JT # dx/dξ # Jacobian H::HT # dJ/dξ # Hessian end -@inline getjacobian(mv::MappingValues) = mv.J +@inline getjacobian(mv::MappingValues{<:Union{AbstractTensor, SMatrix}}) = mv.J @inline gethessian(mv::MappingValues{<:Any,<:AbstractTensor}) = mv.H -# This will be needed for optimizing away the hessian calculation/updates -# for cases when this is known to be zero (due to the geometric interpolation) -#@inline gethessian(::MappingValues{JT,Nothing}) where JT = _make_hessian(JT) -#@inline _make_hessian(::Type{Tensor{2,dim,T}}) where {dim,T} = zero(Tensor{3,dim,T}) +""" + GeometryMapping{DiffOrder}(::Type{T}, ip_geo, qr::QuadratureRule) -struct RequiresHessian{B} end -RequiresHessian(B::Bool) = RequiresHessian{B}() -function RequiresHessian(ip_fun::Interpolation, ip_geo::Interpolation) - # Leave ip_geo as input, because for later the hessian can also be avoided - # for fully linear geometric elements (e.g. triangle and tetrahedron) - # This optimization is left out for now. - RequiresHessian(requires_hessian(get_mapping_type(ip_fun))) -end +Create a `GeometryMapping` object which contains the geometric -""" - GeometryMapping(::Type{T}, ip_geo, qr::QuadratureRule, ::RequiresHessian{B}) +* shape values +* gradient values (if DiffOrder ≥ 1) +* hessians values (if DiffOrder ≥ 2) -Create a `GeometryMapping` object which contains the geometric shape, gradients, and, -if `B==true`, the hessian values. `T<:AbstractFloat` gives the numeric type of the values. +`T<:AbstractFloat` gives the numeric type of the values. """ GeometryMapping -struct GeometryMapping{IP, M_t, dMdξ_t, d2Mdξ2_t} +struct GeometryMapping{DiffOrder, IP, M_t, dMdξ_t, d2Mdξ2_t} ip::IP # ::Interpolation Geometric interpolation - M::M_t # ::AbstractVector{<:Number} Values of geometric shape functions - dMdξ::dMdξ_t # ::AbstractVector{<:Vec} Gradients of geometric shape functions in ref-domain - d2Mdξ2::d2Mdξ2_t # ::AbstractVector{<:Tensor{2}} Hessians of geometric shape functions in ref-domain - # ::Nothing When hessians are not required + M::M_t # ::AbstractMatrix{<:Number} Values of geometric shape functions + dMdξ::dMdξ_t # ::AbstractMatrix{<:Vec} Gradients of geometric shape functions in ref-domain + d2Mdξ2::d2Mdξ2_t # ::AbstractMatrix{<:Tensor{2}} Hessians of geometric shape functions in ref-domain + # ::Nothing When not required + function GeometryMapping( + ip::IP, M::M_t, ::Nothing, ::Nothing + ) where {IP <: ScalarInterpolation, M_t<:AbstractMatrix{<:Number}} + return new{0, IP, M_t, Nothing, Nothing}(ip, M, nothing, nothing) + end + function GeometryMapping( + ip::IP, M::M_t, dMdξ::dMdξ_t, ::Nothing + ) where {IP <: ScalarInterpolation, M_t<:AbstractMatrix{<:Number}, dMdξ_t <: AbstractMatrix{<:Vec}} + return new{1, IP, M_t, dMdξ_t, Nothing}(ip, M, dMdξ, nothing) + end + function GeometryMapping( + ip::IP, M::M_t, dMdξ::dMdξ_t, d2Mdξ2::d2Mdξ2_t) where + {IP <: ScalarInterpolation, M_t<:AbstractMatrix{<:Number}, + dMdξ_t <: AbstractMatrix{<:Vec}, d2Mdξ2_t <: AbstractMatrix{<:Tensor{2}}} + return new{2, IP, M_t, dMdξ_t, d2Mdξ2_t}(ip, M, dMdξ, d2Mdξ2) + end +end +function GeometryMapping{0}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T + n_shape = getnbasefunctions(ip) + n_qpoints = getnquadpoints(qr) + gm = GeometryMapping(ip, zeros(T, n_shape, n_qpoints), nothing, nothing) + precompute_values!(gm, qr) # Separate function for qr point update in PointValues + return gm end -function GeometryMapping(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule, ::RequiresHessian{RH}) where {T,RH} +function GeometryMapping{1}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) M = zeros(T, n_shape, n_qpoints) dMdξ = zeros(Vec{getdim(ip),T}, n_shape, n_qpoints) - d2Mdξ2 = RH ? zeros(Tensor{2,getdim(ip),T}, n_shape, n_qpoints) : nothing + + gm = GeometryMapping(ip, M, dMdξ, nothing) + precompute_values!(gm, qr) # Separate function for qr point update in PointValues + return gm +end +function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T + n_shape = getnbasefunctions(ip) + n_qpoints = getnquadpoints(qr) + + M = zeros(T, n_shape, n_qpoints) + dMdξ = zeros(Vec{getdim(ip),T}, n_shape, n_qpoints) + d2Mdξ2 = zeros(Tensor{2,getdim(ip),T}, n_shape, n_qpoints) gm = GeometryMapping(ip, M, dMdξ, d2Mdξ2) precompute_values!(gm, qr) # Separate function for qr point update in PointValues return gm end -precompute_values!(gm::GeometryMapping, qr) = precompute_values!(gm, qr, RequiresHessian(gm)) -function precompute_values!(gm::GeometryMapping, qr, ::RequiresHessian{false}) +function precompute_values!(gm::GeometryMapping{0}, qr) + shape_values!(gm.M, gm.ip, qr) +end +function precompute_values!(gm::GeometryMapping{1}, qr) shape_gradients_and_values!(gm.dMdξ, gm.M, gm.ip, qr) end -function precompute_values!(gm::GeometryMapping, qr, ::RequiresHessian{true}) +function precompute_values!(gm::GeometryMapping{2}, qr) shape_hessians_gradients_and_values!(gm.d2Mdξ2, gm.dMdξ, gm.M, gm.ip, qr) end function Base.copy(v::GeometryMapping) - d2Mdξ2_copy = v.d2Mdξ2 === nothing ? nothing : copy(v.d2Mdξ2) - return GeometryMapping(copy(v.ip), copy(v.M), copy(v.dMdξ), d2Mdξ2_copy) + copy_or_nothing(x) = copy(x) + copy_or_nothing(::Nothing) = nothing + return GeometryMapping(copy(v.ip), copy(v.M), copy_or_nothing(v.dMdξ), copy_or_nothing(v.d2Mdξ2)) end getngeobasefunctions(geo_mapping::GeometryMapping) = size(geo_mapping.M, 1) @propagate_inbounds geometric_value(geo_mapping::GeometryMapping, q_point::Int, base_func::Int) = geo_mapping.M[base_func, q_point] get_geometric_interpolation(geo_mapping::GeometryMapping) = geo_mapping.ip -RequiresHessian(geo_mapping::GeometryMapping) = RequiresHessian(geo_mapping.d2Mdξ2 !== nothing) - # Hot-fixes to support embedded elements before MixedTensors are available # See https://github.com/Ferrite-FEM/Tensors.jl/pull/188 @inline otimes_helper(x::Vec{dim}, dMdξ::Vec{dim}) where dim = x ⊗ dMdξ @@ -95,9 +121,11 @@ function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(d2Mdξ2) return Tensor{3,dim,promote_type(Tx,TM)} end -@propagate_inbounds calculate_mapping(geo_mapping::GeometryMapping, args...) = calculate_mapping(RequiresHessian(geo_mapping), geo_mapping, args...) +@inline function calculate_mapping(::GeometryMapping{0}, q_point, x) + return MappingValues(nothing, nothing) +end -@inline function calculate_mapping(::RequiresHessian{false}, geo_mapping::GeometryMapping, q_point, x) +@inline function calculate_mapping(geo_mapping::GeometryMapping{1}, q_point, x) 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] @@ -106,7 +134,7 @@ end return MappingValues(fecv_J, nothing) end -@inline function calculate_mapping(::RequiresHessian{true}, geo_mapping::GeometryMapping, q_point, x) +@inline function calculate_mapping(geo_mapping::GeometryMapping{2}, q_point, x) J = zero(otimes_returntype(eltype(x), eltype(geo_mapping.dMdξ))) H = zero(otimes_returntype(eltype(x), eltype(geo_mapping.d2Mdξ2))) @inbounds for j in 1:getngeobasefunctions(geo_mapping) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index fec2c19080..6d43e9b441 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -275,6 +275,11 @@ function spatial_coordinate(fe_v::AbstractValues, q_point::Int, x::AbstractVecto return vec end +function shape_gradients_and_values!(values::AbstractMatrix, ip, qr::QuadratureRule) + for (qp, ξ) in pairs(getpoints(qr)) + shape_values!(@view(values[:, qp]), ip, ξ) + end +end function shape_gradients_and_values!(gradients::AbstractMatrix, values::AbstractMatrix, ip, qr::QuadratureRule) for (qp, ξ) in pairs(getpoints(qr)) From c36ffdaa5595d87931341c4f15e2478f5218cbe2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 11 Nov 2023 22:08:29 +0100 Subject: [PATCH 084/172] Passing tests --- src/FEValues/CellValues.jl | 25 +++++++++++++++---------- src/FEValues/FunctionValues.jl | 4 +++- src/FEValues/common_values.jl | 2 +- src/deprecations.jl | 2 +- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 9fc4997d19..b790966db5 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -35,26 +35,26 @@ function default_geometric_interpolation(::Interpolation{shape}) where {dim, sha return VectorizedInterpolation{dim}(Lagrange{shape, 1}()) end -struct CellValues{FV, GM, QR, detT<:AbstractVector} <: AbstractCellValues +struct CellValues{FV, GM, QR, detT} <: AbstractCellValues fun_values::FV # FunctionValues geo_mapping::GM # GeometryMapping qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; difforder=Val(1)) where T +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; difforder=Val(1), save_detJ=true) where T _difforder(::Val{N}) where N = N _difforder(N::Int) = N - GeoDiffOrder = increased_diff_order(get_mapping_type(ip_fun)) + _difforder(difforder) + GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + _difforder(difforder), save_detJ) FunDiffOrder = _difforder(difforder) geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) - detJdV = fill(T(NaN), length(getweights(qr))) + detJdV = save_detJ ? fill(T(NaN), length(getweights(qr))) : nothing return CellValues(fun_values, geo_mapping, qr, detJdV) end -CellValues(qr::QuadratureRule, ip::Interpolation, args...) = CellValues(Float64, qr, ip, args...) -function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation=default_geometric_interpolation(ip)) where T - return CellValues(T, qr, ip, VectorizedInterpolation(ip_geo)) +CellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) = CellValues(Float64, qr, ip, args...; kwargs...) +function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation=default_geometric_interpolation(ip); kwargs...) where T + return CellValues(T, qr, ip, VectorizedInterpolation(ip_geo); kwargs...) end function Base.copy(cv::CellValues) @@ -95,6 +95,13 @@ end # Access quadrature rule values getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) +@propagate_inbounds function _update_detJdV!(detJvec::AbstractVector, q_point::Int, w, mapping) + detJ = calculate_detJ(getjacobian(mapping)) + detJ > 0.0 || throw_detJ_not_pos(detJ) + @inbounds detJvec[q_point] = detJ*w +end +@inline _update_detJdV!(::Nothing, q_point, w, mapping) = nothing + function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) geo_mapping = cv.geo_mapping fun_values = cv.fun_values @@ -109,9 +116,7 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) mapping = calculate_mapping(geo_mapping, q_point, x) - detJ = calculate_detJ(getjacobian(mapping)) - detJ > 0.0 || throw_detJ_not_pos(detJ) - @inbounds cv.detJdV[q_point] = detJ*w + @inline _update_detJdV!(cv.detJdV, q_point, w, mapping) apply_mapping!(fun_values, q_point, mapping, cell) end return nothing diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 82da88c074..5b51140b72 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -105,6 +105,7 @@ get_function_interpolation(funvals::FunctionValues) = funvals.ip shape_value_type(funvals::FunctionValues) = eltype(funvals.N_x) shape_gradient_type(funvals::FunctionValues) = eltype(funvals.dNdx) +shape_gradient_type(::FunctionValues{0}) = nothing # Checks that the user provides the right dimension of coordinates to reinit! methods to ensure good error messages if not @@ -114,9 +115,10 @@ sdim_from_gradtype(::Type{<:SMatrix{<:Any,sdim}}) where sdim = sdim # For performance, these must be fully inferrable for the compiler. # args: valname (:CellValues or :FaceValues), shape_gradient_type, eltype(x) -function check_reinit_sdim_consistency(valname, gradtype, ::Type{<:Vec{sdim}}) where {sdim} +function check_reinit_sdim_consistency(valname, gradtype::Type, ::Type{<:Vec{sdim}}) where {sdim} check_reinit_sdim_consistency(valname, Val(sdim_from_gradtype(gradtype)), Val(sdim)) end +check_reinit_sdim_consistency(_, ::Nothing, ::Type{<:Vec}) = nothing # gradient not stored, cannot check check_reinit_sdim_consistency(_, ::Val{sdim}, ::Val{sdim}) where sdim = nothing function check_reinit_sdim_consistency(valname, ::Val{sdim_val}, ::Val{sdim_x}) where {sdim_val, sdim_x} throw(ArgumentError("The $valname (sdim=$sdim_val) and coordinates (sdim=$sdim_x) have different spatial dimensions.")) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 6d43e9b441..55b01381d2 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -275,7 +275,7 @@ function spatial_coordinate(fe_v::AbstractValues, q_point::Int, x::AbstractVecto return vec end -function shape_gradients_and_values!(values::AbstractMatrix, ip, qr::QuadratureRule) +function shape_values!(values::AbstractMatrix, ip, qr::QuadratureRule) for (qp, ξ) in pairs(getpoints(qr)) shape_values!(@view(values[:, qp]), ip, ξ) end diff --git a/src/deprecations.jl b/src/deprecations.jl index cb0c2f3cd8..7f81cf443d 100644 --- a/src/deprecations.jl +++ b/src/deprecations.jl @@ -156,7 +156,7 @@ end # TODO: Are these needed to be deprecated - harder? with the new parameterization # (Cell|Face)Values with vector dofs -const _VectorValues = Union{CellValues{<:FV}, FaceValues{<:FV}} where {FV <: FunctionValues{<:VectorInterpolation}} +const _VectorValues = Union{CellValues{<:FV}, FaceValues{<:FV}} where {FV <: FunctionValues{<:Any,<:VectorInterpolation}} @deprecate function_value(fe_v::_VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}}) where {dim,T} function_value(fe_v, q_point, reinterpret(T, u)) @deprecate function_gradient(fe_v::_VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}}) where {dim,T} function_gradient(fe_v, q_point, reinterpret(T, u)) @deprecate function_divergence(fe_v::_VectorValues, q_point::Int, u::AbstractVector{Vec{dim,T}}) where {dim,T} function_divergence(fe_v, q_point, reinterpret(T, u)) From 4ab9ba2354263675750e6e84603ed6fd875f9371 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 11 Nov 2023 22:24:25 +0100 Subject: [PATCH 085/172] Fix performance annotations to come on par with master --- src/FEValues/CellValues.jl | 4 ++-- src/FEValues/FunctionValues.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index b790966db5..60448364c7 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -95,7 +95,7 @@ end # Access quadrature rule values getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) -@propagate_inbounds function _update_detJdV!(detJvec::AbstractVector, q_point::Int, w, mapping) +@inline function _update_detJdV!(detJvec::AbstractVector, q_point::Int, w, mapping) detJ = calculate_detJ(getjacobian(mapping)) detJ > 0.0 || throw_detJ_not_pos(detJ) @inbounds detJvec[q_point] = detJ*w @@ -116,7 +116,7 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) mapping = calculate_mapping(geo_mapping, q_point, x) - @inline _update_detJdV!(cv.detJdV, q_point, w, mapping) + _update_detJdV!(cv.detJdV, q_point, w, mapping) apply_mapping!(fun_values, q_point, mapping, cell) end return nothing diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 5b51140b72..902a81c3be 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -146,8 +146,8 @@ increased_diff_order(::ContravariantPiolaMapping) = 1 increased_diff_order(::CovariantPiolaMapping) = 1 # Support for embedded elements -calculate_Jinv(J::Tensor{2}) = inv(J) -calculate_Jinv(J::SMatrix) = pinv(J) +@inline calculate_Jinv(J::Tensor{2}) = inv(J) +@inline calculate_Jinv(J::SMatrix) = pinv(J) # Hotfix to get the dots right for embedded elements until mixed tensors are merged. # Scalar/Vector interpolations with sdim == rdim (== vdim) From cc0c806e020161d58b6cb7b4e1f3830ef1505cf1 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 21 Nov 2023 17:55:21 +0100 Subject: [PATCH 086/172] Move utils to common_values --- src/FEValues/CellValues.jl | 6 ++---- src/FEValues/FaceValues.jl | 14 ++++++-------- src/FEValues/FunctionValues.jl | 15 ++++++--------- src/FEValues/GeometryMapping.jl | 4 +--- src/FEValues/common_values.jl | 8 ++++++++ 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 60448364c7..3777fe6fcd 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -42,10 +42,8 @@ struct CellValues{FV, GM, QR, detT} <: AbstractCellValues detJdV::detT # AbstractVector{<:Number} end function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; difforder=Val(1), save_detJ=true) where T - _difforder(::Val{N}) where N = N - _difforder(N::Int) = N - GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + _difforder(difforder), save_detJ) - FunDiffOrder = _difforder(difforder) + GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + _extract_val(difforder), save_detJ) + FunDiffOrder = _extract_val(difforder) geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) detJdV = save_detJ ? fill(T(NaN), length(getweights(qr))) : nothing diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 3ca56c39e0..77d8f7a4ce 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -32,19 +32,17 @@ values of nodal functions, gradients and divergences of nodal functions etc. on FaceValues struct FaceValues{FV, GM, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:AbstractVector{GM}} <: AbstractFaceValues - fun_values::V_FV # AbstractVector{FunctionValues} + fun_values::V_FV # AbstractVector{FunctionValues} geo_mapping::V_GM # AbstractVector{GeometryMapping} - qr::QR # FaceQuadratureRule - detJdV::detT # AbstractVector{<:Number} - normals::nT # AbstractVector{<:Vec} + qr::QR # FaceQuadratureRule + detJdV::detT # AbstractVector{<:Number} + normals::nT # AbstractVector{<:Vec} current_face::ScalarWrapper{Int} end function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun); difforder=Val(1)) where {T,sdim} - _difforder(::Val{N}) where N = N - _difforder(N::Int) = N - GeoDiffOrder = increased_diff_order(get_mapping_type(ip_fun)) + _difforder(difforder) - FunDiffOrder = _difforder(difforder) + GeoDiffOrder = increased_diff_order(get_mapping_type(ip_fun)) + _extract_val(difforder) + FunDiffOrder = _extract_val(difforder) geo_mapping = [GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) for qr in fqr.face_rules] fun_values = [FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 902a81c3be..dfe3f3243c 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -30,10 +30,10 @@ typeof_dNdξ(::Type{T}, ::VInterpolationDims{dim,dim,dim}) where {T,dim} = Tenso typeof_dNdξ(::Type{T}, ::VInterpolationDims{rdim,<:Any,vdim}) where {T,rdim,vdim} = SMatrix{vdim,rdim,T} # If vdim=rdim!=sdim Tensor would be possible... """ - FunctionValues(::Type{T}, ip_fun, qr::QuadratureRule, ip_geo::VectorizedInterpolation) + FunctionValues{DiffOrder}(::Type{T}, ip_fun, qr::QuadratureRule, ip_geo::VectorizedInterpolation) -Create a `FunctionValues` object containing the shape values and gradients for both the reference -cell (precalculated) and the real cell (updated in `reinit!`). +Create a `FunctionValues` object containing the shape values and gradients (up to order `DiffOrder`) +for both the reference cell (precalculated) and the real cell (updated in `reinit!`). """ FunctionValues @@ -50,9 +50,6 @@ struct FunctionValues{DiffOrder, IP, N_t, dNdx_t, dNdξ_t} return new{1, typeof(ip), N_t, typeof(dNdx), typeof(dNdξ)}(ip, N_x, N_ξ, dNdx, dNdξ) end end -function FunctionValues(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T - return FunctionValues{1}(T, ip, qr, ip_geo) -end function FunctionValues{0}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T ip_dims = InterpolationDims(ip, ip_geo) n_shape = getnbasefunctions(ip) @@ -91,9 +88,9 @@ end function Base.copy(v::FunctionValues) N_ξ_copy = copy(v.N_ξ) N_x_copy = v.N_ξ === v.N_x ? N_ξ_copy : copy(v.N_x) # Preserve aliasing - copy_or_nothing(x) = copy(x) - copy_or_nothing(::Nothing) = nothing - return FunctionValues(copy(v.ip), N_x_copy, N_ξ_copy, copy_or_nothing(v.dNdx), copy_or_nothing(v.dNdξ)) + dNdx_copy = _copy_or_nothing(v.dNdx) + dNdξ_copy = _copy_or_nothing(v.dNdξ) + return FunctionValues(copy(v.ip), N_x_copy, N_ξ_copy, dNdx_copy, dNdξ_copy) end getnbasefunctions(funvals::FunctionValues) = size(funvals.N_x, 1) diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index 6c72563373..ee6386519f 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -93,9 +93,7 @@ function precompute_values!(gm::GeometryMapping{2}, qr) end function Base.copy(v::GeometryMapping) - copy_or_nothing(x) = copy(x) - copy_or_nothing(::Nothing) = nothing - return GeometryMapping(copy(v.ip), copy(v.M), copy_or_nothing(v.dMdξ), copy_or_nothing(v.d2Mdξ2)) + return GeometryMapping(copy(v.ip), copy(v.M), _copy_or_nothing(v.dMdξ), _copy_or_nothing(v.d2Mdξ2)) end getngeobasefunctions(geo_mapping::GeometryMapping) = size(geo_mapping.M, 1) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 55b01381d2..da4158d92c 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -275,6 +275,14 @@ function spatial_coordinate(fe_v::AbstractValues, q_point::Int, x::AbstractVecto return vec end + +# Utility functions used by GeometryMapping, FunctionValues, FaceValues, CellValues +_copy_or_nothing(x) = copy(x) +_copy_or_nothing(::Nothing) = nothing + +_extract_val(v) = v +_extract_val(::Val{N}) where N = N + function shape_values!(values::AbstractMatrix, ip, qr::QuadratureRule) for (qp, ξ) in pairs(getpoints(qr)) shape_values!(@view(values[:, qp]), ip, ξ) From e109c9bf8c2903f4416e337bb12abd609e76e3bf Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 21 Nov 2023 19:18:30 +0100 Subject: [PATCH 087/172] Remove PointValuesInternal --- src/FEValues/CellValues.jl | 15 +++++++------ src/FEValues/FaceValues.jl | 1 + src/FEValues/FunctionValues.jl | 2 +- src/PointEval/PointEvalHandler.jl | 12 ++++++----- src/PointEval/point_values.jl | 36 ++++++------------------------- 5 files changed, 24 insertions(+), 42 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 3777fe6fcd..1f9d64dfaa 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -41,12 +41,12 @@ struct CellValues{FV, GM, QR, detT} <: AbstractCellValues qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; difforder=Val(1), save_detJ=true) where T - GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + _extract_val(difforder), save_detJ) +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; difforder=Val(1), save_detJ=Val(true)) where T + GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + _extract_val(difforder), _extract_val(save_detJ)) FunDiffOrder = _extract_val(difforder) geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) - detJdV = save_detJ ? fill(T(NaN), length(getweights(qr))) : nothing + detJdV = _extract_val(save_detJ) ? fill(T(NaN), length(getweights(qr))) : nothing return CellValues(fun_values, geo_mapping, qr, detJdV) end @@ -81,6 +81,7 @@ getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] # Accessors for function values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) get_function_interpolation(cv::CellValues) = get_function_interpolation(cv.fun_values) +get_function_difforder(cv::CellValues) = get_function_difforder(cv.fun_values) shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) @@ -125,10 +126,12 @@ function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) ip_fun = get_function_interpolation(cv) rdim = getdim(ip_geo) vdim = isa(shape_value(cv, 1, 1), Vec) ? length(shape_value(cv, 1, 1)) : 0 - sdim = length(shape_gradient(cv, 1, 1)) ÷ length(shape_value(cv, 1, 1)) + GradT = shape_gradient_type(cv) + sdim = GradT === nothing ? nothing : sdim_from_gradtype(GradT) vstr = vdim==0 ? "scalar" : "vdim=$vdim" print(io, "CellValues(", vstr, ", rdim=$rdim, and sdim=$sdim): ") print(io, getnquadpoints(cv), " quadrature points") print(io, "\n Function interpolation: "); show(io, d, ip_fun) - print(io, "\nGeometric interpolation: "); show(io, d, ip_geo^sdim) -end + print(io, "\nGeometric interpolation: "); + sdim === nothing ? show(io, d, ip_geo) : show(io, d, ip_geo^sdim) +end \ No newline at end of file diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 77d8f7a4ce..c902612e30 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -71,6 +71,7 @@ getnquadpoints(fv::FaceValues) = @inbounds getnquadpoints(fv.qr, getcurrentface( shape_value_type(fv::FaceValues) = shape_value_type(get_fun_values(fv)) shape_gradient_type(fv::FaceValues) = shape_gradient_type(get_fun_values(fv)) get_function_interpolation(fv::FaceValues) = get_function_interpolation(get_fun_values(fv)) +get_function_difforder(fv::FaceValues) = get_function_difforder(get_fun_values(fv)) get_geometric_interpolation(fv::FaceValues) = get_geometric_interpolation(get_geo_mapping(fv)) get_geo_mapping(fv::FaceValues) = @inbounds fv.geo_mapping[getcurrentface(fv)] diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index dfe3f3243c..b3a7a69a0f 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -99,7 +99,7 @@ getnbasefunctions(funvals::FunctionValues) = size(funvals.N_x, 1) @propagate_inbounds shape_symmetric_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = symmetric(shape_gradient(funvals, q_point, base_func)) get_function_interpolation(funvals::FunctionValues) = funvals.ip - +get_function_difforder(::FunctionValues{DiffOrder}) where DiffOrder = DiffOrder shape_value_type(funvals::FunctionValues) = eltype(funvals.N_x) shape_gradient_type(funvals::FunctionValues) = eltype(funvals.dNdx) shape_gradient_type(::FunctionValues{0}) = nothing diff --git a/src/PointEval/PointEvalHandler.jl b/src/PointEval/PointEvalHandler.jl index db9d16fc93..0d23f0aafb 100644 --- a/src/PointEval/PointEvalHandler.jl +++ b/src/PointEval/PointEvalHandler.jl @@ -183,9 +183,9 @@ 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} npoints = length(ph.cells) - # Figure out the value type by creating a dummy PointValuesInternal + # Figure out the value type by creating a dummy PointValues ip = getfieldinterpolation(dh, find_field(dh, fname)) - pv = PointValuesInternal(zero(Vec{dim, T1}), ip) + pv = PointValues(T1, ip; difforder=Val(0)) zero_val = function_value_init(pv, dof_vals) # Allocate the output as NaNs nanv = convert(typeof(zero_val), NaN * zero_val) @@ -241,11 +241,12 @@ function _evaluate_at_points!( # preallocate some stuff specific to this cellset idx = findfirst(!isnothing, local_coords) idx === nothing && return out_vals - pv = PointValuesInternal(local_coords[idx], ip) + pv = PointValues(local_coords[idx], ip; difforder=Val(0)) first_cell = cellset === nothing ? 1 : first(cellset) cell_dofs = Vector{Int}(undef, ndofs_per_cell(dh, first_cell)) u_e = Vector{T}(undef, ndofs_per_cell(dh, first_cell)) - + grid = get_grid(dh) + x = getcoordinates(grid, first_cell) # compute point values for pointid in eachindex(ph.cells) cellid = ph.cells[pointid] @@ -255,7 +256,8 @@ function _evaluate_at_points!( for (i, I) in pairs(cell_dofs) u_e[i] = dof_vals[I] end - reinit!(pv, local_coords[pointid]) + getcoordinates!(x, grid, cellid) + reinit!(pv, x, local_coords[pointid]) out_vals[pointid] = function_value(pv, 1, u_e, dofrange) end return out_vals diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 5128c1b459..29d668fe96 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -28,18 +28,19 @@ function PointValues(cv::CellValues) T = typeof(getdetJdV(cv, 1)) ip_fun = get_function_interpolation(cv) ip_geo = get_geometric_interpolation(cv) - return PointValues(T, ip_fun, ip_geo) + FunDiffOrder = get_function_difforder(cv) + return PointValues(T, ip_fun, ip_geo; difforder=Val(FunDiffOrder)) end -function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip)) - return PointValues(Float64, ip, ipg) +function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip); kwargs...) + return PointValues(Float64, ip, ipg; kwargs...) end -function PointValues(::Type{T}, ip::IP, ipg::GIP = default_geometric_interpolation(ip)) where { +function PointValues(::Type{T}, ip::IP, ipg::GIP = default_geometric_interpolation(ip); kwargs...) where { T, dim, shape <: AbstractRefShape{dim}, IP <: Interpolation{shape}, GIP <: Interpolation{shape} } qr = QuadratureRule{shape, T}([one(T)], [zero(Vec{dim, T})]) - cv = CellValues(T, qr, ip, ipg) + cv = CellValues(T, qr, ip, ipg; save_detJ=Val(false), kwargs...) return PointValues{typeof(cv)}(cv) end @@ -72,31 +73,6 @@ function reinit!(pv::PointValues, x::AbstractVector{<:Vec{D}}, ξ::Vec{D}) where return nothing end -# Optimized version of PointValues which avoids i) recomputation of dNdξ and -# ii) recomputation of dNdx. Only allows function evaluation (no gradients) which is -# what is used in evaluate_at_points. -struct PointValuesInternal{IP, N_t} <: AbstractValues - N::Vector{N_t} - ip::IP -end - -function PointValuesInternal(ξ::Vec{dim, T}, ip::IP) where {dim, T, shape <: AbstractRefShape{dim}, IP <: Interpolation{shape}} - n_func_basefuncs = getnbasefunctions(ip) - N = [shape_value(ip, ξ, i) for i in 1:n_func_basefuncs] - return PointValuesInternal{IP, eltype(N)}(N, ip) -end - -getnbasefunctions(pv::PointValuesInternal) = size(pv.N, 1) -getnquadpoints(pv::PointValuesInternal) = 1 -shape_value_type(::PointValuesInternal{<:Any, N_t}) where {N_t} = N_t -shape_value(pv::PointValuesInternal, qp::Int, i::Int) = (@assert qp == 1; pv.N[i]) - -# allow on-the-fly updating -function reinit!(pv::PointValuesInternal{IP}, coord::Vec{dim}) where {dim, shape <: AbstractRefShape{dim}, IP <: Interpolation{shape}} - shape_values!(pv.N, pv.ip, coord) - return nothing -end - function Base.show(io::IO, d::MIME"text/plain", cv::PointValues) println(io, "PointValues containing a") show(io, d, cv.cv) From 34beb70d9971d724bfd1b616fbd8f64dbc7807cb Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 21 Nov 2023 20:23:32 +0100 Subject: [PATCH 088/172] Fix test --- src/PointEval/PointEvalHandler.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/PointEval/PointEvalHandler.jl b/src/PointEval/PointEvalHandler.jl index 0d23f0aafb..6bf47a615e 100644 --- a/src/PointEval/PointEvalHandler.jl +++ b/src/PointEval/PointEvalHandler.jl @@ -241,11 +241,12 @@ function _evaluate_at_points!( # preallocate some stuff specific to this cellset idx = findfirst(!isnothing, local_coords) idx === nothing && return out_vals - pv = PointValues(local_coords[idx], ip; difforder=Val(0)) first_cell = cellset === nothing ? 1 : first(cellset) - cell_dofs = Vector{Int}(undef, ndofs_per_cell(dh, first_cell)) - u_e = Vector{T}(undef, ndofs_per_cell(dh, first_cell)) grid = get_grid(dh) + ip_geo = default_interpolation(getcelltype(grid, first_cell)) + pv = PointValues(eltype(local_coords[idx]), ip, ip_geo; difforder=Val(0)) + cell_dofs = Vector{Int}(undef, ndofs_per_cell(dh, first_cell)) + u_e = Vector{T}(undef, ndofs_per_cell(dh, first_cell)) x = getcoordinates(grid, first_cell) # compute point values for pointid in eachindex(ph.cells) From 05fab53f7125491e88bc90420f6c5b94346e5b66 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 22 Nov 2023 18:26:29 +0100 Subject: [PATCH 089/172] Add custom values instructions to devdocs --- docs/src/devdocs/FEValues.md | 41 +++++++++++++++++------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/docs/src/devdocs/FEValues.md b/docs/src/devdocs/FEValues.md index d4cbc8c9bf..dc7b4005f6 100644 --- a/docs/src/devdocs/FEValues.md +++ b/docs/src/devdocs/FEValues.md @@ -16,27 +16,24 @@ Ferrite.MappingValues Ferrite.FunctionValues ``` -## How CellValues and FaceValues works -* The function interpolation, `ip_fun`, determines how it should be mapped, by defining `get_mapping_type(ip_fun)` for its type. -* The mapping type, e.g. `IdentityMapping`, decides the requirements for `GeometryValues`, specifically if the `hessian` $\partial^2M/\partial\xi^2$, - of the geometric shape functions, $M(\xi)$, on the reference cell should be precalculated or not. - ***Note:*** *This should also in the future be determined by the required order of derivatives to be mapped in `FunctionValues`* -* As the first part of `reinit!`, the `MappingValues` are calculated based on the cell's coordinates. If the `GeometricMapping` contains the hessian - on the reference cell, the `hessian` on the actual cell, $\partial^2M/\partial x^2$, is calculated and returned in `MappingValues`. Otherwise, only - the jacobian, $\partial M/\partial x$, is calculated. -* In the second part of `reinit!`, The `MappingValues`, containing sufficient information for the current quadrature point, is given to `apply_mapping!`. - This allows the shape values and gradients stored in `FunctionValues`, to be mapped to the current cell by calling `apply_mapping!`. - ## Custom FEValues -Custom FEValues, `fe_v`, should normally implement the `reinit!` method. -Additionally, for normal functionality the `getnquadpoints` should be implemented. -Note that asking for the `n`th quadrature point must be inside array bounds if -`1 <= n <= getnquadpoints(fe_v)` -(`checkquadpoint` can, alternatively, be dispatched to check that `n` is inbounds.) - -Supporting `function_value`, `function_gradient`, `function_symmetric_gradient`, `function_divergence`, and `function_curl`, -requires implementing `getnbasefunctions`, `shape_value`, and `shape_gradient`. -Note that asking for the `i`th shape value or gradient must be inside array bounds if `1 <= i <= getnbasefunctions(fe_v)` +Custom FEValues, `fe_v::AbstractValues`, should normally implement the [`reinit!`](@ref) method. Subtypes of `AbstractValues` have default implementations for some functions, but require some lower-level access functions, specifically + +* [`function_value`](@ref), requires + * [`shape_value`](@ref) + * [`getnquadpoints`](@ref) + * [`getnbasefunctions`](@ref) +* [`function_gradient`](@ref), [`function_divergence`](@ref), [`function_symmetric_gradient`](@ref), and [`function_curl`](@ref) requires + * [`shape_gradient`](@ref) + * [`getnquadpoints`](@ref) + * [`getnbasefunctions`](@ref) +* [`spatial_coordinate`](@ref), requires + * [`geometric_value`](@ref) + * [`getngeobasefunctions`](@ref) + * [`getnquadpoints`](@ref) + -Supporting `spatial_coordinate` requires implementing `getngeobasefunctions` and `geometric_value`. -Note that asking for the `i`th geometric value must be inside array bounds if `1 <= i <= getngeobasefunctions(fe_v)` \ No newline at end of file +### Array bounds +* Asking for the `n`th quadrature point must be inside array bounds if `1 <= n <= getnquadpoints(fe_v)`. (`checkquadpoint` can, alternatively, be dispatched to check that `n` is inbounds.) +* Asking for the `i`th shape value or gradient must be inside array bounds if `1 <= i <= getnbasefunctions(fe_v)` +* Asking for the `i`th geometric value must be inside array bounds if `1 <= i <= getngeobasefunctions(fe_v)` From be6daf8a903084dacef4e10072285654c06001b5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 22 Nov 2023 18:26:53 +0100 Subject: [PATCH 090/172] Add test that check instructions in devdocs for custom FEValues --- test/test_cellvalues.jl | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 5ff1e72f24..7edebe90bc 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -357,8 +357,41 @@ end @test contains(pv_showstring, "Function interpolation: Lagrange{RefPrism, 2}()") end -@testset "SimpleCellValues" begin - include(joinpath(@__DIR__, "../docs/src/topics/SimpleCellValues_literate.jl")) +@testset "CustomCellValues" begin + + @testset "SimpleCellValues" begin + include(joinpath(@__DIR__, "../docs/src/topics/SimpleCellValues_literate.jl")) + end + + @testset "TestCustomCellValues" begin + + struct TestCustomCellValues{CV<:CellValues} <: Ferrite.AbstractValues + cv::CV + end + # Check that the list in devdocs/FEValues.md is true + # If changes are made that makes the following tests fails, + # the devdocs should be updated accordingly. + for op = (:shape_value, :shape_gradient, :getnquadpoints, :getnbasefunctions, :geometric_value, :getngeobasefunctions) + eval(quote + Ferrite.$op(cv::TestCustomCellValues, args...; kwargs...) = Ferrite.$op(cv.cv, args...; kwargs...) + end) + end + ip = Lagrange{RefQuadrilateral,1}()^2 + qr = QuadratureRule{RefQuadrilateral}(2) + cv = CellValues(qr, ip) + grid = generate_grid(Quadrilateral, (1,1)) + x = getcoordinates(grid, 1) + cell = getcells(grid, 1) + reinit!(cv, x, cell) + ae = rand(getnbasefunctions(cv)) + q_point = rand(1:getnquadpoints(cv)) + cv_custom = TestCustomCellValues(cv) + for fun in (function_value, function_gradient, + function_divergence, function_symmetric_gradient, function_curl) + @test fun(cv_custom, q_point, ae) == fun(cv, q_point, ae) + end + @test spatial_coordinate(cv_custom, q_point, x) == spatial_coordinate(cv, q_point, x) + end end end # of testset From 66bf56d4a48f682933e4703e5fb6b95abf2e9ec2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 24 Nov 2023 23:33:28 +0100 Subject: [PATCH 091/172] Address Dennis' comments on SimpleCellValues --- docs/src/topics/SimpleCellValues_literate.jl | 39 ++++++++++++-------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/docs/src/topics/SimpleCellValues_literate.jl b/docs/src/topics/SimpleCellValues_literate.jl index 0bbbe9a220..ecfb71b549 100644 --- a/docs/src/topics/SimpleCellValues_literate.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -3,25 +3,32 @@ using Ferrite, Test # Define a simple version of the cell values object, which only supports -# identity mapping of scalar interpolations, without embedding. +# scalar interpolations using an identity mapping from reference to physical +# elements. Here we assume that the element has the same dimension +# as the physical space, which excludes for example surface elements. struct SimpleCellValues{T, dim} <: Ferrite.AbstractCellValues - N::Matrix{T} # Precalculated shape values - dNdξ::Matrix{Vec{dim,T}} # Precalculated shape gradients in the reference domain - dNdx::Matrix{Vec{dim,T}} # Cache for shape gradients in the real domain - M::Matrix{T} # Precalculated geometric shape values - dMdξ::Matrix{Vec{dim,T}} # Precalculated geometric shape gradients - weights::Vector{T} # Given quadrature weights in the reference domain - detJdV::Vector{T} # Cache for quadrature weights in the real domain + N::Matrix{T} # Precalculated shape values, N[i, q_point] where i is the +# # shape function number and q_point the integration point + dNdξ::Matrix{Vec{dim,T}} # Precalculated shape gradients in the reference domain, dNdξ[i, q_point] + dNdx::Matrix{Vec{dim,T}} # Cache for shape gradients in the physical domain, dNdx[i, q_point] + M::Matrix{T} # Precalculated geometric shape values, M[j, q_point] where j is the +# # geometric shape function number + dMdξ::Matrix{Vec{dim,T}} # Precalculated geometric shape gradients, dMdξ[j, q_point] + weights::Vector{T} # Given quadrature weights in the reference domain, weights[q_point] + detJdV::Vector{T} # Cache for quadrature weights in the physical domain, detJdV[q_point], i.e. +# # det(J)*weight[q_point], where J is the jacobian of the geometric mapping +# # at the quadrature point, q_point. end; -# To make it easier to initiate this struct, we create a constructor function +# To make it easier to initialize this struct, we create a constructor function # with the same input as `CellValues`. However, we skip some consistency checking here. -function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Interpolation, T=Float64) +function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Interpolation) dim = Ferrite.getdim(ip_fun) ## Quadrature weights and coordinates (in reference cell) weights = Ferrite.getweights(qr) n_qpoints = length(weights) - + T = eltype(weights) + ## Function interpolation n_func_basefuncs = getnbasefunctions(ip_fun) N = zeros(T, n_func_basefuncs, n_qpoints) @@ -47,12 +54,13 @@ function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Int SimpleCellValues(N, dNdξ, dNdx, M, dMdξ, weights, detJdV) end; -# We first define `getnbasefunctions` and `getnquadpoints` +# We first create a dispatch of `getnbasefunctions` and `getnquadpoints` +# for our SimpleCellValues Ferrite.getnbasefunctions(cv::SimpleCellValues) = size(cv.N, 1); Ferrite.getnquadpoints(cv::SimpleCellValues) = size(cv.N, 2); -# Before we define the `reinit!` function to calculate the cached -# values `dNdx` and `detJdV` for the current cell +# Before we define the dispatch of `reinit!` function to calculate +# the cached values `dNdx` and `detJdV` for the current cell function Ferrite.reinit!(cv::SimpleCellValues, x::Vector{Vec{dim,T}}) where {dim,T} for (q_point, w) in pairs(cv.weights) # Loop over each quadrature point ## Calculate the jacobian, J @@ -81,7 +89,8 @@ ip = Lagrange{RefQuadrilateral,1}() simple_cv = SimpleCellValues(qr, ip, ip) cv = CellValues(qr, ip, ip) -# The first thing to try is to reinitialize the cell values to a given cell +# The first thing to try is to reinitialize the cell +# values to a given cell, in this case cell nr. 2 grid = generate_grid(Quadrilateral, (2,2)) x = getcoordinates(grid, 2) reinit!(simple_cv, x) From b31e821c091befd417bc45bff495e80e02c14514 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 24 Nov 2023 23:36:28 +0100 Subject: [PATCH 092/172] Address Dennis' comments on common values --- src/FEValues/common_values.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index fec2c19080..9395867f28 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -26,8 +26,10 @@ end end """ - reinit!(cv::CellValues, x::Vector, cell::Union{AbstractCell,Nothing}=nothing) - reinit!(fv::FaceValues, x::Vector, face::Int, cell::Union{AbstractCell,Nothing}=nothing) + reinit!(cv::CellValues, x::Vector, cell::AbstractCell) + reinit!(cv::CellValues, x::Vector) + reinit!(fv::FaceValues, x::Vector, face::Int, cell::AbstractCell) + reinit!(fv::FaceValues, x::Vector, face::Int) Update the `CellValues`/`FaceValues` object for a cell or face with coordinates `x`. The derivatives of the shape functions, and the new integration weights are computed. @@ -286,4 +288,4 @@ function shape_hessians_gradients_and_values!(hessians::AbstractMatrix, gradient for (qp, ξ) in pairs(getpoints(qr)) shape_hessians_gradients_and_values!(@view(hessians[:, qp]), @view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) end -end \ No newline at end of file +end From 0bbc09dad3c513d81fa7825caa51bbeb231f7872 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 1 Dec 2023 11:09:14 +0100 Subject: [PATCH 093/172] Simplifications --- src/FEValues/CellValues.jl | 7 +++---- src/FEValues/FaceValues.jl | 6 ++---- src/FEValues/FunctionValues.jl | 2 +- src/FEValues/common_values.jl | 5 +---- src/PointEval/point_values.jl | 5 ++--- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 1f9d64dfaa..04fce8a816 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -41,12 +41,11 @@ struct CellValues{FV, GM, QR, detT} <: AbstractCellValues qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; difforder=Val(1), save_detJ=Val(true)) where T - GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + _extract_val(difforder), _extract_val(save_detJ)) - FunDiffOrder = _extract_val(difforder) +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; FunDiffOrder=1, save_detJ=true) where T + GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + FunDiffOrder, save_detJ) geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) - detJdV = _extract_val(save_detJ) ? fill(T(NaN), length(getweights(qr))) : nothing + detJdV = save_detJ ? fill(T(NaN), length(getweights(qr))) : nothing return CellValues(fun_values, geo_mapping, qr, detJdV) end diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index c902612e30..575c592e8a 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -40,10 +40,8 @@ struct FaceValues{FV, GM, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:Abstract current_face::ScalarWrapper{Int} end -function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun); difforder=Val(1)) where {T,sdim} - GeoDiffOrder = increased_diff_order(get_mapping_type(ip_fun)) + _extract_val(difforder) - FunDiffOrder = _extract_val(difforder) - +function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun); FunDiffOrder=1) where {T,sdim} + GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + FunDiffOrder, 1) geo_mapping = [GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) for qr in fqr.face_rules] fun_values = [FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index b3a7a69a0f..bdca47c460 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -136,7 +136,7 @@ get_mapping_type(fv::FunctionValues) = get_mapping_type(fv.ip) How many order higher geometric derivatives are required to to map the function values and gradients from the reference cell -to the real cell geometry? +to the physical cell geometry? """ increased_diff_order(::IdentityMapping) = 0 increased_diff_order(::ContravariantPiolaMapping) = 1 diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index da4158d92c..d507ca0545 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -276,13 +276,10 @@ function spatial_coordinate(fe_v::AbstractValues, q_point::Int, x::AbstractVecto end -# Utility functions used by GeometryMapping, FunctionValues, FaceValues, CellValues +# Utility functions used by GeometryMapping, FunctionValues _copy_or_nothing(x) = copy(x) _copy_or_nothing(::Nothing) = nothing -_extract_val(v) = v -_extract_val(::Val{N}) where N = N - function shape_values!(values::AbstractMatrix, ip, qr::QuadratureRule) for (qp, ξ) in pairs(getpoints(qr)) shape_values!(@view(values[:, qp]), ip, ξ) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 29d668fe96..8c1c2d8e06 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -28,8 +28,7 @@ function PointValues(cv::CellValues) T = typeof(getdetJdV(cv, 1)) ip_fun = get_function_interpolation(cv) ip_geo = get_geometric_interpolation(cv) - FunDiffOrder = get_function_difforder(cv) - return PointValues(T, ip_fun, ip_geo; difforder=Val(FunDiffOrder)) + return PointValues(T, ip_fun, ip_geo; FunDiffOrder = get_function_difforder(cv)) end function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip); kwargs...) return PointValues(Float64, ip, ipg; kwargs...) @@ -40,7 +39,7 @@ function PointValues(::Type{T}, ip::IP, ipg::GIP = default_geometric_interpolati GIP <: Interpolation{shape} } qr = QuadratureRule{shape, T}([one(T)], [zero(Vec{dim, T})]) - cv = CellValues(T, qr, ip, ipg; save_detJ=Val(false), kwargs...) + cv = CellValues(T, qr, ip, ipg; save_detJ=false, kwargs...) return PointValues{typeof(cv)}(cv) end From ae80cfbc6bccf92c6ae5ffc9938956c7a9c282e1 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 3 Dec 2023 13:08:53 +0100 Subject: [PATCH 094/172] Fix PointValues usage in PointEvalHandler --- src/PointEval/PointEvalHandler.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PointEval/PointEvalHandler.jl b/src/PointEval/PointEvalHandler.jl index 6bf47a615e..551b18b11b 100644 --- a/src/PointEval/PointEvalHandler.jl +++ b/src/PointEval/PointEvalHandler.jl @@ -185,7 +185,7 @@ function evaluate_at_points(ph::PointEvalHandler{<:Any, dim, T1}, dh::AbstractDo npoints = length(ph.cells) # Figure out the value type by creating a dummy PointValues ip = getfieldinterpolation(dh, find_field(dh, fname)) - pv = PointValues(T1, ip; difforder=Val(0)) + pv = PointValues(T1, ip; FunDiffOrder=0) zero_val = function_value_init(pv, dof_vals) # Allocate the output as NaNs nanv = convert(typeof(zero_val), NaN * zero_val) @@ -244,7 +244,7 @@ function _evaluate_at_points!( first_cell = cellset === nothing ? 1 : first(cellset) grid = get_grid(dh) ip_geo = default_interpolation(getcelltype(grid, first_cell)) - pv = PointValues(eltype(local_coords[idx]), ip, ip_geo; difforder=Val(0)) + pv = PointValues(eltype(local_coords[idx]), ip, ip_geo; FunDiffOrder=0) cell_dofs = Vector{Int}(undef, ndofs_per_cell(dh, first_cell)) u_e = Vector{T}(undef, ndofs_per_cell(dh, first_cell)) x = getcoordinates(grid, first_cell) From 1d5b9f175ed79cbf9b277712237da6686d4507e3 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 3 Dec 2023 14:26:26 +0100 Subject: [PATCH 095/172] Fix tests after merge --- src/FEValues/CellValues.jl | 12 ------------ src/FEValues/FunctionValues.jl | 12 ++++++------ src/FEValues/GeometryMapping.jl | 18 +++++++++--------- src/FEValues/common_values.jl | 12 ++++++------ src/FEValues/interface_values.jl | 7 +++---- src/PointEval/point_values.jl | 3 ++- test/test_interfacevalues.jl | 8 ++++---- 7 files changed, 30 insertions(+), 42 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 04fce8a816..c9cc811917 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -58,18 +58,6 @@ function Base.copy(cv::CellValues) return CellValues(copy(cv.fun_values), copy(cv.geo_mapping), copy(cv.qr), copy(cv.detJdV)) end -""" - precompute_values!(cv::CellValues) - -Precompute all values for the current quadrature rule in `cv`. This method allows you to modify -the quadrature positions, and then update all relevant parts of `cv` accordingly. -Used by `PointValues`. -""" -function precompute_values!(cv::CellValues) - precompute_values!(cv.fun_values, cv.qr) - precompute_values!(cv.geo_mapping, cv.qr) -end - # Access geometry values @propagate_inbounds getngeobasefunctions(cv::CellValues) = getngeobasefunctions(cv.geo_mapping) @propagate_inbounds geometric_value(cv::CellValues, args...) = geometric_value(cv.geo_mapping, args...) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index bdca47c460..ffbff26449 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -59,7 +59,7 @@ function FunctionValues{0}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_ N_x = isa(get_mapping_type(ip), IdentityMapping) ? N_ξ : similar(N_ξ) fv = FunctionValues(ip, N_x, N_ξ, nothing, nothing) - precompute_values!(fv, qr) # Separate function for qr point update in PointValues + precompute_values!(fv, getpoints(qr)) # Separate function for qr point update in PointValues return fv end function FunctionValues{1}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T @@ -74,15 +74,15 @@ function FunctionValues{1}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_ dNdx = fill(zero(typeof_dNdx(T, ip_dims)) * T(NaN), n_shape, n_qpoints) fv = FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) - precompute_values!(fv, qr) # Separate function for qr point update in PointValues + precompute_values!(fv, getpoints(qr)) # Separate function for qr point update in PointValues return fv end -function precompute_values!(fv::FunctionValues{0}, qr) - shape_values!(fv.N_ξ, fv.ip, qr) +function precompute_values!(fv::FunctionValues{0}, qr_points::Vector{<:Vec}) + shape_values!(fv.N_ξ, fv.ip, qr_points) end -function precompute_values!(fv::FunctionValues{1}, qr) - shape_gradients_and_values!(fv.dNdξ, fv.N_ξ, fv.ip, qr) +function precompute_values!(fv::FunctionValues{1}, qr_points::Vector{<:Vec}) + shape_gradients_and_values!(fv.dNdξ, fv.N_ξ, fv.ip, qr_points) end function Base.copy(v::FunctionValues) diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index ee6386519f..b3a283dfed 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -55,7 +55,7 @@ function GeometryMapping{0}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRu n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) gm = GeometryMapping(ip, zeros(T, n_shape, n_qpoints), nothing, nothing) - precompute_values!(gm, qr) # Separate function for qr point update in PointValues + precompute_values!(gm, getpoints(qr)) return gm end function GeometryMapping{1}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T @@ -66,7 +66,7 @@ function GeometryMapping{1}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRu dMdξ = zeros(Vec{getdim(ip),T}, n_shape, n_qpoints) gm = GeometryMapping(ip, M, dMdξ, nothing) - precompute_values!(gm, qr) # Separate function for qr point update in PointValues + precompute_values!(gm, getpoints(qr)) return gm end function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T @@ -78,18 +78,18 @@ function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRu d2Mdξ2 = zeros(Tensor{2,getdim(ip),T}, n_shape, n_qpoints) gm = GeometryMapping(ip, M, dMdξ, d2Mdξ2) - precompute_values!(gm, qr) # Separate function for qr point update in PointValues + precompute_values!(gm, getpoints(qr)) return gm end -function precompute_values!(gm::GeometryMapping{0}, qr) - shape_values!(gm.M, gm.ip, qr) +function precompute_values!(gm::GeometryMapping{0}, qr_points::Vector{<:Vec}) + shape_values!(gm.M, gm.ip, qr_points) end -function precompute_values!(gm::GeometryMapping{1}, qr) - shape_gradients_and_values!(gm.dMdξ, gm.M, gm.ip, qr) +function precompute_values!(gm::GeometryMapping{1}, qr_points::Vector{<:Vec}) + shape_gradients_and_values!(gm.dMdξ, gm.M, gm.ip, qr_points) end -function precompute_values!(gm::GeometryMapping{2}, qr) - shape_hessians_gradients_and_values!(gm.d2Mdξ2, gm.dMdξ, gm.M, gm.ip, qr) +function precompute_values!(gm::GeometryMapping{2}, qr_points::Vector{<:Vec}) + shape_hessians_gradients_and_values!(gm.d2Mdξ2, gm.dMdξ, gm.M, gm.ip, qr_points) end function Base.copy(v::GeometryMapping) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index f9d6fa44c0..9d8292d051 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -288,20 +288,20 @@ end _copy_or_nothing(x) = copy(x) _copy_or_nothing(::Nothing) = nothing -function shape_values!(values::AbstractMatrix, ip, qr::QuadratureRule) - for (qp, ξ) in pairs(getpoints(qr)) +function shape_values!(values::AbstractMatrix, ip, qr_points::Vector{<:Vec}) + for (qp, ξ) in pairs(qr_points) shape_values!(@view(values[:, qp]), ip, ξ) end end -function shape_gradients_and_values!(gradients::AbstractMatrix, values::AbstractMatrix, ip, qr::QuadratureRule) - for (qp, ξ) in pairs(getpoints(qr)) +function shape_gradients_and_values!(gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::Vector{<:Vec}) + for (qp, ξ) in pairs(qr_points) shape_gradients_and_values!(@view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) end end -function shape_hessians_gradients_and_values!(hessians::AbstractMatrix, gradients::AbstractMatrix, values::AbstractMatrix, ip, qr::QuadratureRule) - for (qp, ξ) in pairs(getpoints(qr)) +function shape_hessians_gradients_and_values!(hessians::AbstractMatrix, gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::Vector{<:Vec}) + for (qp, ξ) in pairs(qr_points) shape_hessians_gradients_and_values!(@view(hessians[:, qp]), @view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) end end \ No newline at end of file diff --git a/src/FEValues/interface_values.jl b/src/FEValues/interface_values.jl index 1765b51fb2..3f13140e92 100644 --- a/src/FEValues/interface_values.jl +++ b/src/FEValues/interface_values.jl @@ -127,18 +127,17 @@ function reinit!( reinit!(iv.here, coords_here, face_here) dim == 1 && return reinit!(iv.there, coords_there, face_there) # Transform the quadrature points from the here side to the there side - set_current_face!(iv.there, face_there) + set_current_face!(iv.there, face_there) # Includes boundscheck interface_transformation = InterfaceOrientationInfo(cell_here, cell_there, face_here, face_there) quad_points_a = getpoints(iv.here.qr, face_here) quad_points_b = getpoints(iv.there.qr, face_there) transform_interface_points!(quad_points_b, quad_points_a, interface_transformation) - @boundscheck checkface(iv.there, face_there) # TODO: This is the bottleneck, cache it? @assert length(quad_points_a) <= length(quad_points_b) # Re-evalutate shape functions in the transformed quadrature points - precompute_values!(iv.there.fun_values, quad_points_b) - precompute_values!(iv.there.geo_mapping, quad_points_b) + precompute_values!(get_fun_values(iv.there), quad_points_b) + precompute_values!(get_geo_mapping(iv.there), quad_points_b) # reinit! the "there" side reinit!(iv.there, coords_there, face_there) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 8c1c2d8e06..aa3d3b5401 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -66,7 +66,8 @@ function reinit!(pv::PointValues, x::AbstractVector{<:Vec{D}}, ξ::Vec{D}) where qr_points = getpoints(pv.cv.qr) qr_points[1] = ξ # Precompute all values again to reflect the updated ξ coordinate - precompute_values!(pv.cv) + precompute_values!(pv.cv.fun_values, qr_points) + precompute_values!(pv.cv.geo_mapping, qr_points) # Regular reinit reinit!(pv.cv, x) return nothing diff --git a/test/test_interfacevalues.jl b/test/test_interfacevalues.jl index cfdf467a7c..6a19eafa25 100644 --- a/test/test_interfacevalues.jl +++ b/test/test_interfacevalues.jl @@ -1,7 +1,7 @@ @testset "InterfaceValues" begin function test_interfacevalues(grid::Ferrite.AbstractGrid, iv::InterfaceValues; tol = 0) - ip_here = iv.here.func_interp - ip_there = iv.there.func_interp + ip_here = Ferrite.get_function_interpolation(iv.here) + ip_there = Ferrite.get_function_interpolation(iv.there) ndim = Ferrite.getdim(ip_here) n_basefuncs = getnbasefunctions(ip_here) + getnbasefunctions(ip_there) @@ -252,8 +252,8 @@ @test_throws ArgumentError("transformation is not implemented") Ferrite.get_transformation_matrix(it) end @testset "show" begin - # Just smoke test to make sure show doesn't error. iv = InterfaceValues(FaceQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()) - show(stdout, MIME"text/plain"(), iv) + showstring = show_as_string(iv) + @test contains(showstring, "InterfaceValues with") end end # of testset From 76a9312dc1cf3c6cb643c6947db812a045eed34a Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 3 Dec 2023 15:04:05 +0100 Subject: [PATCH 096/172] Fix missing double-comment for literate --- docs/src/topics/SimpleCellValues_literate.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/topics/SimpleCellValues_literate.jl b/docs/src/topics/SimpleCellValues_literate.jl index ecfb71b549..78429af17e 100644 --- a/docs/src/topics/SimpleCellValues_literate.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -8,16 +8,16 @@ using Ferrite, Test # as the physical space, which excludes for example surface elements. struct SimpleCellValues{T, dim} <: Ferrite.AbstractCellValues N::Matrix{T} # Precalculated shape values, N[i, q_point] where i is the -# # shape function number and q_point the integration point +## # shape function number and q_point the integration point dNdξ::Matrix{Vec{dim,T}} # Precalculated shape gradients in the reference domain, dNdξ[i, q_point] dNdx::Matrix{Vec{dim,T}} # Cache for shape gradients in the physical domain, dNdx[i, q_point] M::Matrix{T} # Precalculated geometric shape values, M[j, q_point] where j is the -# # geometric shape function number +## # geometric shape function number dMdξ::Matrix{Vec{dim,T}} # Precalculated geometric shape gradients, dMdξ[j, q_point] weights::Vector{T} # Given quadrature weights in the reference domain, weights[q_point] detJdV::Vector{T} # Cache for quadrature weights in the physical domain, detJdV[q_point], i.e. -# # det(J)*weight[q_point], where J is the jacobian of the geometric mapping -# # at the quadrature point, q_point. +## # det(J)*weight[q_point], where J is the jacobian of the geometric mapping +## # at the quadrature point, q_point. end; # To make it easier to initialize this struct, we create a constructor function From 679a4ac01f4c80253b8a5db57c686e7fb59b1f33 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 3 Dec 2023 16:26:32 +0100 Subject: [PATCH 097/172] Improved naming/interfaces --- src/FEValues/CellValues.jl | 11 ++++++----- src/FEValues/FaceValues.jl | 15 +++++++-------- src/FEValues/FunctionValues.jl | 14 +++++++------- src/FEValues/interface_values.jl | 4 ++-- src/PointEval/point_values.jl | 2 +- test/test_facevalues.jl | 8 ++++---- 6 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index c9cc811917..4bbdf33deb 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -39,13 +39,13 @@ struct CellValues{FV, GM, QR, detT} <: AbstractCellValues fun_values::FV # FunctionValues geo_mapping::GM # GeometryMapping qr::QR # QuadratureRule - detJdV::detT # AbstractVector{<:Number} + detJdV::detT # AbstractVector{<:Number} or Nothing end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; FunDiffOrder=1, save_detJ=true) where T - GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + FunDiffOrder, save_detJ) +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; FunDiffOrder=1, save_detJdV=true) where T + GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), save_detJdV) geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) - detJdV = save_detJ ? fill(T(NaN), length(getweights(qr))) : nothing + detJdV = save_detJdV ? fill(T(NaN), length(getweights(qr))) : nothing return CellValues(fun_values, geo_mapping, qr, detJdV) end @@ -55,7 +55,7 @@ function CellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolatio end function Base.copy(cv::CellValues) - return CellValues(copy(cv.fun_values), copy(cv.geo_mapping), copy(cv.qr), copy(cv.detJdV)) + return CellValues(copy(cv.fun_values), copy(cv.geo_mapping), copy(cv.qr), _copy_or_nothing(cv.detJdV)) end # Access geometry values @@ -64,6 +64,7 @@ end get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_mapping) getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] +getdetJdV(::CellValues{<:Any, <:Any, <:Any, Nothing}, ::Int) = throw(ArgumentError("detJdV is not saved in CellValues")) # Accessors for function values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 6b2090e4e4..f788f11dda 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -31,17 +31,17 @@ values of nodal functions, gradients and divergences of nodal functions etc. on """ FaceValues -struct FaceValues{FV, GM, QR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:AbstractVector{GM}} <: AbstractFaceValues +struct FaceValues{FV, GM, FQR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:AbstractVector{GM}} <: AbstractFaceValues fun_values::V_FV # AbstractVector{FunctionValues} geo_mapping::V_GM # AbstractVector{GeometryMapping} - qr::QR # FaceQuadratureRule + fqr::FQR # FaceQuadratureRule detJdV::detT # AbstractVector{<:Number} normals::nT # AbstractVector{<:Vec} current_face::ScalarWrapper{Int} end function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun); FunDiffOrder=1) where {T,sdim} - GeoDiffOrder = max(increased_diff_order(get_mapping_type(ip_fun)) + FunDiffOrder, 1) + GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), 1) geo_mapping = [GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) for qr in fqr.face_rules] fun_values = [FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) @@ -58,12 +58,12 @@ end function Base.copy(fv::FaceValues) fun_values = map(copy, fv.fun_values) geo_mapping = map(copy, fv.geo_mapping) - return FaceValues(fun_values, geo_mapping, copy(fv.qr), copy(fv.detJdV), copy(fv.normals), copy(fv.current_face)) + return FaceValues(fun_values, geo_mapping, copy(fv.fqr), copy(fv.detJdV), copy(fv.normals), copy(fv.current_face)) end getngeobasefunctions(fv::FaceValues) = getngeobasefunctions(get_geo_mapping(fv)) getnbasefunctions(fv::FaceValues) = getnbasefunctions(get_fun_values(fv)) -getnquadpoints(fv::FaceValues) = @inbounds getnquadpoints(fv.qr, getcurrentface(fv)) +getnquadpoints(fv::FaceValues) = @inbounds getnquadpoints(fv.fqr, getcurrentface(fv)) @propagate_inbounds getdetJdV(fv::FaceValues, q_point) = fv.detJdV[q_point] shape_value_type(fv::FaceValues) = shape_value_type(get_fun_values(fv)) @@ -86,7 +86,6 @@ end getcurrentface(fv::FaceValues) Return the current active face of the `FaceValues` object (from last `reinit!`). - """ getcurrentface(fv::FaceValues) = fv.current_face[] @@ -127,7 +126,7 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, ce throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) end - @inbounds for (q_point, w) in pairs(getweights(fv.qr, face_nr)) + @inbounds for (q_point, w) in pairs(getweights(fv.fqr, face_nr)) mapping = calculate_mapping(geo_mapping, q_point, x) J = getjacobian(mapping) # See the `Ferrite.embedded_det` docstring for more background @@ -147,7 +146,7 @@ function Base.show(io::IO, d::MIME"text/plain", fv::FaceValues) sdim = length(shape_gradient(fv, 1, 1)) ÷ length(shape_value(fv, 1, 1)) vstr = vdim==0 ? "scalar" : "vdim=$vdim" print(io, "FaceValues(", vstr, ", rdim=$rdim, sdim=$sdim): ") - nqp = getnquadpoints.(fv.qr.face_rules) + nqp = getnquadpoints.(fv.fqr.face_rules) if all(n==first(nqp) for n in nqp) println(io, first(nqp), " quadrature points per face") else diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index ffbff26449..c4e08b4c81 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -132,15 +132,15 @@ struct ContravariantPiolaMapping end get_mapping_type(fv::FunctionValues) = get_mapping_type(fv.ip) """ - increased_diff_order(mapping) + required_geo_diff_order(fun_mapping, fun_diff_order::Int) -How many order higher geometric derivatives are required to -to map the function values and gradients from the reference cell -to the physical cell geometry? +Return the required order of geometric derivatives to map +the function values and gradients from the reference cell +to the physical cell geometry. """ -increased_diff_order(::IdentityMapping) = 0 -increased_diff_order(::ContravariantPiolaMapping) = 1 -increased_diff_order(::CovariantPiolaMapping) = 1 +required_geo_diff_order(::IdentityMapping, fun_diff_order::Int) = fun_diff_order +required_geo_diff_order(::ContravariantPiolaMapping, fun_diff_order::Int) = 1 + fun_diff_order +required_geo_diff_order(::CovariantPiolaMapping, fun_diff_order::Int) = 1 + fun_diff_order # Support for embedded elements @inline calculate_Jinv(J::Tensor{2}) = inv(J) diff --git a/src/FEValues/interface_values.jl b/src/FEValues/interface_values.jl index 3f13140e92..dcee07d045 100644 --- a/src/FEValues/interface_values.jl +++ b/src/FEValues/interface_values.jl @@ -129,8 +129,8 @@ function reinit!( # Transform the quadrature points from the here side to the there side set_current_face!(iv.there, face_there) # Includes boundscheck interface_transformation = InterfaceOrientationInfo(cell_here, cell_there, face_here, face_there) - quad_points_a = getpoints(iv.here.qr, face_here) - quad_points_b = getpoints(iv.there.qr, face_there) + quad_points_a = getpoints(iv.here.fqr, face_here) + quad_points_b = getpoints(iv.there.fqr, face_there) transform_interface_points!(quad_points_b, quad_points_a, interface_transformation) # TODO: This is the bottleneck, cache it? @assert length(quad_points_a) <= length(quad_points_b) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index aa3d3b5401..268b8cdbaf 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -39,7 +39,7 @@ function PointValues(::Type{T}, ip::IP, ipg::GIP = default_geometric_interpolati GIP <: Interpolation{shape} } qr = QuadratureRule{shape, T}([one(T)], [zero(Vec{dim, T})]) - cv = CellValues(T, qr, ip, ipg; save_detJ=false, kwargs...) + cv = CellValues(T, qr, ip, ipg; save_detJdV=false, kwargs...) return PointValues{typeof(cv)}(cv) end diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index e4b113b224..1b09c2db75 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -115,10 +115,10 @@ for (scalar_interpol, quad_rule) in ( # Make it easy to test scalar wrapper equality _mock_isequal(a, b) = a == b _mock_isequal(a::T, b::T) where {T<:Ferrite.ScalarWrapper} = a[] == b[] - for fname in (:qr, :detJdV, :normals, :current_face) + for fname in (:fqr, :detJdV, :normals, :current_face) v = getfield(fv, fname) vc = getfield(fvc, fname) - if fname !== :qr # Test unaliased + if fname !== :fqr # Test unaliased @test v !== vc end @test _mock_isequal(v, vc) @@ -134,8 +134,8 @@ end @test startswith(showstring, "FaceValues(scalar, rdim=2, sdim=2): 2 quadrature points per face") @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()") @test contains(showstring, "Geometric interpolation: Lagrange{RefQuadrilateral, 1}()^2") - fv.qr.face_rules[1] = deepcopy(fv.qr.face_rules[1]) - push!(Ferrite.getweights(fv.qr.face_rules[1]), 1) + fv.fqr.face_rules[1] = deepcopy(fv.fqr.face_rules[1]) + push!(Ferrite.getweights(fv.fqr.face_rules[1]), 1) showstring = show_as_string(fv) @test startswith(showstring, "FaceValues(scalar, rdim=2, sdim=2): (3, 2, 2, 2) quadrature points on each face") end From a913c9bfee6b2e98740cf4dd9e9a1863f8fe733f Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 3 Dec 2023 16:32:42 +0100 Subject: [PATCH 098/172] Fix formatting of SimpleCellValues example --- docs/src/topics/SimpleCellValues_literate.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/topics/SimpleCellValues_literate.jl b/docs/src/topics/SimpleCellValues_literate.jl index 78429af17e..e604ecf81a 100644 --- a/docs/src/topics/SimpleCellValues_literate.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -8,16 +8,16 @@ using Ferrite, Test # as the physical space, which excludes for example surface elements. struct SimpleCellValues{T, dim} <: Ferrite.AbstractCellValues N::Matrix{T} # Precalculated shape values, N[i, q_point] where i is the -## # shape function number and q_point the integration point + ## shape function number and q_point the integration point dNdξ::Matrix{Vec{dim,T}} # Precalculated shape gradients in the reference domain, dNdξ[i, q_point] dNdx::Matrix{Vec{dim,T}} # Cache for shape gradients in the physical domain, dNdx[i, q_point] M::Matrix{T} # Precalculated geometric shape values, M[j, q_point] where j is the -## # geometric shape function number + ## geometric shape function number dMdξ::Matrix{Vec{dim,T}} # Precalculated geometric shape gradients, dMdξ[j, q_point] weights::Vector{T} # Given quadrature weights in the reference domain, weights[q_point] detJdV::Vector{T} # Cache for quadrature weights in the physical domain, detJdV[q_point], i.e. -## # det(J)*weight[q_point], where J is the jacobian of the geometric mapping -## # at the quadrature point, q_point. + ## det(J)*weight[q_point], where J is the jacobian of the geometric mapping + ## at the quadrature point, q_point. end; # To make it easier to initialize this struct, we create a constructor function From 8b6c097aeacd08e40da247fdb4a085cc5deec357 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 3 Dec 2023 16:53:18 +0100 Subject: [PATCH 099/172] Simplify text in SimpleCellValues example --- docs/src/topics/SimpleCellValues_literate.jl | 49 +++++++++----------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/docs/src/topics/SimpleCellValues_literate.jl b/docs/src/topics/SimpleCellValues_literate.jl index e604ecf81a..06ba44b779 100644 --- a/docs/src/topics/SimpleCellValues_literate.jl +++ b/docs/src/topics/SimpleCellValues_literate.jl @@ -1,11 +1,11 @@ -# We start by including `Ferrite` and `Test`, -# to allow us to verify our implementation. +# We start by including `Ferrite` and `Test` (to check our implementation). using Ferrite, Test -# Define a simple version of the cell values object, which only supports -# scalar interpolations using an identity mapping from reference to physical -# elements. Here we assume that the element has the same dimension -# as the physical space, which excludes for example surface elements. +# Then, we define a simple version of the cell values object, which only supports +# * Scalar interpolations +# * Identity mapping from reference to physical cell. +# * The cell shape has the same dimension as the physical space (excludes so-called embedded cells). + struct SimpleCellValues{T, dim} <: Ferrite.AbstractCellValues N::Matrix{T} # Precalculated shape values, N[i, q_point] where i is the ## shape function number and q_point the integration point @@ -20,8 +20,7 @@ struct SimpleCellValues{T, dim} <: Ferrite.AbstractCellValues ## at the quadrature point, q_point. end; -# To make it easier to initialize this struct, we create a constructor function -# with the same input as `CellValues`. However, we skip some consistency checking here. +# Next, we create a constructor with the same input as `CellValues` function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Interpolation) dim = Ferrite.getdim(ip_fun) ## Quadrature weights and coordinates (in reference cell) @@ -54,13 +53,16 @@ function SimpleCellValues(qr::QuadratureRule, ip_fun::Interpolation, ip_geo::Int SimpleCellValues(N, dNdξ, dNdx, M, dMdξ, weights, detJdV) end; -# We first create a dispatch of `getnbasefunctions` and `getnquadpoints` -# for our SimpleCellValues -Ferrite.getnbasefunctions(cv::SimpleCellValues) = size(cv.N, 1); -Ferrite.getnquadpoints(cv::SimpleCellValues) = size(cv.N, 2); +# To make our `SimpleCellValues` work in standard Ferrite code, +# we need to dispatch some access functions: +Ferrite.getnbasefunctions(cv::SimpleCellValues) = size(cv.N, 1) +Ferrite.getnquadpoints(cv::SimpleCellValues) = size(cv.N, 2) +Ferrite.shape_value(cv::SimpleCellValues, q_point::Int, i::Int) = cv.N[i, q_point] +Ferrite.shape_gradient(cv::SimpleCellValues, q_point::Int, i::Int) = cv.dNdx[i, q_point]; -# Before we define the dispatch of `reinit!` function to calculate -# the cached values `dNdx` and `detJdV` for the current cell +# The last step is then to dispatch `reinit!` for our `SimpleCellValues` to calculate +# the cached values `dNdx` and `detJdV` for the current cell according to the +# theory for `IdentityMapping` above. function Ferrite.reinit!(cv::SimpleCellValues, x::Vector{Vec{dim,T}}) where {dim,T} for (q_point, w) in pairs(cv.weights) # Loop over each quadrature point ## Calculate the jacobian, J @@ -78,27 +80,22 @@ function Ferrite.reinit!(cv::SimpleCellValues, x::Vector{Vec{dim,T}}) where {dim end end; -# To make our `SimpleCellValues` work in standard Ferrite code, we need to define how -# to get the shape value and graident: -Ferrite.shape_value(cv::SimpleCellValues, q_point::Int, i::Int) = cv.N[i, q_point] -Ferrite.shape_gradient(cv::SimpleCellValues, q_point::Int, i::Int) = cv.dNdx[i, q_point] - -# We are now ready to test, so let's create an instance of our new and the regular cell values +# To test our implementation, we create instances of our `SimpleCellValues` and the standard `CellValues`: qr = QuadratureRule{RefQuadrilateral}(2) ip = Lagrange{RefQuadrilateral,1}() simple_cv = SimpleCellValues(qr, ip, ip) -cv = CellValues(qr, ip, ip) +cv = CellValues(qr, ip, ip); -# The first thing to try is to reinitialize the cell -# values to a given cell, in this case cell nr. 2 +# The first thing to try is to reinitialize the cell values to a given cell, in this case cell nr. 2 grid = generate_grid(Quadrilateral, (2,2)) x = getcoordinates(grid, 2) reinit!(simple_cv, x) -reinit!(cv, x) +reinit!(cv, x); -# If we now pretend we are inside an element, where we have a vector of element -# degree of freedom values, we can check that the function values and gradients match +# 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 = rand(getnbasefunctions(simple_cv)) q_point = 2 @test function_value(cv, q_point, ue) ≈ function_value(simple_cv, q_point, ue) @test function_gradient(cv, q_point, ue) ≈ function_gradient(simple_cv, q_point, ue) + From 96a9766c1028817e211765c4662f67d1cf4de63f Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 3 Dec 2023 17:17:31 +0100 Subject: [PATCH 100/172] Consistent code formatting and minimize diff --- src/FEValues/CellValues.jl | 6 +++--- src/FEValues/FaceValues.jl | 17 ++++++----------- src/PointEval/PointEvalHandler.jl | 4 ++-- src/PointEval/point_values.jl | 2 +- src/iterators.jl | 3 ++- test/runtests.jl | 2 +- 6 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 4bbdf33deb..6d7f1522ce 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -41,7 +41,7 @@ struct CellValues{FV, GM, QR, detT} <: AbstractCellValues qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} or Nothing end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; FunDiffOrder=1, save_detJdV=true) where T +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; FunDiffOrder = 1, save_detJdV = true) where T GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), save_detJdV) geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) @@ -85,7 +85,7 @@ getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) @inline function _update_detJdV!(detJvec::AbstractVector, q_point::Int, w, mapping) detJ = calculate_detJ(getjacobian(mapping)) detJ > 0.0 || throw_detJ_not_pos(detJ) - @inbounds detJvec[q_point] = detJ*w + @inbounds detJvec[q_point] = detJ * w end @inline _update_detJdV!(::Nothing, q_point, w, mapping) = nothing @@ -98,7 +98,7 @@ function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) if cell === nothing && !isa(get_mapping_type(fun_values), IdentityMapping) throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) end - if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs + if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x) != n_geom_basefuncs throw_incompatible_coord_length(length(x), n_geom_basefuncs) end @inbounds for (q_point, w) in enumerate(getweights(cv.qr)) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index f788f11dda..25d31c3009 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -40,13 +40,13 @@ struct FaceValues{FV, GM, FQR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:Abstrac current_face::ScalarWrapper{Int} end -function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}=default_geometric_interpolation(ip_fun); FunDiffOrder=1) where {T,sdim} +function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = default_geometric_interpolation(ip_fun); FunDiffOrder = 1) where {T,sdim} GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), 1) geo_mapping = [GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) for qr in fqr.face_rules] fun_values = [FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) - detJdV = fill(T(NaN), max_nquadpoints) - normals = fill(zero(Vec{sdim,T})*T(NaN), max_nquadpoints) + detJdV = fill(T(NaN), max_nquadpoints) + normals = fill(zero(Vec{sdim, T}) * T(NaN), max_nquadpoints) return FaceValues(fun_values, geo_mapping, fqr, detJdV, normals, ScalarWrapper(1)) end @@ -106,19 +106,14 @@ function set_current_face!(fv::FaceValues, face_nr::Int) fv.current_face[] = face_nr end -function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, cell=nothing) where {dim, T} +function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim, T}}, face_nr::Int, cell = nothing) where {dim, T} check_reinit_sdim_consistency(:FaceValues, shape_gradient_type(fv), eltype(x)) - - set_current_face!(fv, face_nr) - n_geom_basefuncs = getngeobasefunctions(fv) - if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x)!=n_geom_basefuncs + if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x) != n_geom_basefuncs throw_incompatible_coord_length(length(x), n_geom_basefuncs) end - # Must be done after setting current face, - # which should be done after boundscheck_face geo_mapping = get_geo_mapping(fv) fun_values = get_fun_values(fv) @@ -133,7 +128,7 @@ function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim,T}}, face_nr::Int, ce weight_norm = weighted_normal(J, getrefshape(geo_mapping.ip), face_nr) detJ = norm(weight_norm) detJ > 0.0 || throw_detJ_not_pos(detJ) - @inbounds fv.detJdV[q_point] = detJ*w + @inbounds fv.detJdV[q_point] = detJ * w @inbounds fv.normals[q_point] = weight_norm / norm(weight_norm) apply_mapping!(fun_values, q_point, mapping, cell) end diff --git a/src/PointEval/PointEvalHandler.jl b/src/PointEval/PointEvalHandler.jl index 551b18b11b..4da29b765b 100644 --- a/src/PointEval/PointEvalHandler.jl +++ b/src/PointEval/PointEvalHandler.jl @@ -244,9 +244,9 @@ function _evaluate_at_points!( first_cell = cellset === nothing ? 1 : first(cellset) grid = get_grid(dh) ip_geo = default_interpolation(getcelltype(grid, first_cell)) - pv = PointValues(eltype(local_coords[idx]), ip, ip_geo; FunDiffOrder=0) + pv = PointValues(eltype(local_coords[idx]), ip, ip_geo; FunDiffOrder = 0) cell_dofs = Vector{Int}(undef, ndofs_per_cell(dh, first_cell)) - u_e = Vector{T}(undef, ndofs_per_cell(dh, first_cell)) + u_e = Vector{T}(undef, ndofs_per_cell(dh, first_cell)) x = getcoordinates(grid, first_cell) # compute point values for pointid in eachindex(ph.cells) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 268b8cdbaf..20b43842c9 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -39,7 +39,7 @@ function PointValues(::Type{T}, ip::IP, ipg::GIP = default_geometric_interpolati GIP <: Interpolation{shape} } qr = QuadratureRule{shape, T}([one(T)], [zero(Vec{dim, T})]) - cv = CellValues(T, qr, ip, ipg; save_detJdV=false, kwargs...) + cv = CellValues(T, qr, ip, ipg; save_detJdV = false, kwargs...) return PointValues{typeof(cv)}(cv) end diff --git a/src/iterators.jl b/src/iterators.jl index ba0f36e6b5..7a1e4617a0 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -365,6 +365,7 @@ function InterfaceIterator(gridordh::Union{Grid,AbstractDofHandler}, return InterfaceIterator(InterfaceCache(gridordh), grid, topology) end + # Iterator interface function Base.iterate(ii::InterfaceIterator, state...) grid_dim = getdim(ii.grid) @@ -415,4 +416,4 @@ function _check_same_celltype(grid::AbstractGrid, faceset::Set{FaceIndex}) if !all(getcelltype(grid, face[1]) == celltype for face in faceset) error("The cells in the faceset are not all of the same celltype.") end -end \ No newline at end of file +end diff --git a/test/runtests.jl b/test/runtests.jl index 3393e295f7..e31028893a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,7 +21,7 @@ include("test_utils.jl") # Unit tests include("test_interpolations.jl") -include("test_cellvalues.jl") +include("test_cellvalues.jl") include("test_facevalues.jl") include("test_interfacevalues.jl") include("test_quadrules.jl") From a807738d05edb855b534d64e0f294870a57f89a2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sun, 3 Dec 2023 17:19:09 +0100 Subject: [PATCH 101/172] Removed wrong whitespace --- src/iterators.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iterators.jl b/src/iterators.jl index 7a1e4617a0..ddab91900b 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -365,7 +365,6 @@ function InterfaceIterator(gridordh::Union{Grid,AbstractDofHandler}, return InterfaceIterator(InterfaceCache(gridordh), grid, topology) end - # Iterator interface function Base.iterate(ii::InterfaceIterator, state...) grid_dim = getdim(ii.grid) @@ -386,6 +385,7 @@ function Base.iterate(ii::InterfaceIterator, state...) end end + # Iterator interface for CellIterator/FaceIterator const GridIterators{C} = Union{CellIterator{C}, FaceIterator{C}, InterfaceIterator{C}} From 9833fe80c39874dd58738a2773cd9430d5d7ba9a Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 12:54:08 +0100 Subject: [PATCH 102/172] Use sprint(show, ...) instead of helper fun for testing --- test/test_cellvalues.jl | 6 +++--- test/test_facevalues.jl | 4 ++-- test/test_interfacevalues.jl | 2 +- test/test_utils.jl | 7 ------- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 7edebe90bc..7f90d0ccde 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -342,17 +342,17 @@ end @testset "show" begin cv_quad = CellValues(QuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()^2) - showstring = show_as_string(cv_quad) + showstring = sprint(show, MIME"text/plain"(), cv_quad) @test startswith(showstring, "CellValues(vdim=2, rdim=2, and sdim=2): 4 quadrature points") @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()^2") cv_wedge = CellValues(QuadratureRule{RefPrism}(2), Lagrange{RefPrism,2}()) - showstring = show_as_string(cv_wedge) + showstring = sprint(show, MIME"text/plain"(), cv_wedge) @test startswith(showstring, "CellValues(scalar, rdim=3, and sdim=3): 5 quadrature points") @test contains(showstring, "Function interpolation: Lagrange{RefPrism, 2}()") pv = PointValues(cv_wedge) - pv_showstring = show_as_string(pv) + pv_showstring = sprint(show, MIME"text/plain"(), pv) @test startswith(pv_showstring, "PointValues containing") @test contains(pv_showstring, "Function interpolation: Lagrange{RefPrism, 2}()") end diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index 1b09c2db75..bb781a51ff 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -130,13 +130,13 @@ end @testset "show" begin # Just smoke test to make sure show doesn't error. fv = FaceValues(FaceQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()) - showstring = show_as_string(fv) + showstring = sprint(show, MIME"text/plain"(), fv) @test startswith(showstring, "FaceValues(scalar, rdim=2, sdim=2): 2 quadrature points per face") @test contains(showstring, "Function interpolation: Lagrange{RefQuadrilateral, 2}()") @test contains(showstring, "Geometric interpolation: Lagrange{RefQuadrilateral, 1}()^2") fv.fqr.face_rules[1] = deepcopy(fv.fqr.face_rules[1]) push!(Ferrite.getweights(fv.fqr.face_rules[1]), 1) - showstring = show_as_string(fv) + showstring = sprint(show, MIME"text/plain"(), fv) @test startswith(showstring, "FaceValues(scalar, rdim=2, sdim=2): (3, 2, 2, 2) quadrature points on each face") end diff --git a/test/test_interfacevalues.jl b/test/test_interfacevalues.jl index 6a19eafa25..f69a49c227 100644 --- a/test/test_interfacevalues.jl +++ b/test/test_interfacevalues.jl @@ -253,7 +253,7 @@ end @testset "show" begin iv = InterfaceValues(FaceQuadratureRule{RefQuadrilateral}(2), Lagrange{RefQuadrilateral,2}()) - showstring = show_as_string(iv) + showstring = sprint(show, MIME"text/plain"(), iv) @test contains(showstring, "InterfaceValues with") end end # of testset diff --git a/test/test_utils.jl b/test/test_utils.jl index 43a172bd24..140954df8b 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -282,13 +282,6 @@ getfacerefshape(::Tetrahedron, ::Int) = RefTriangle getfacerefshape(::Pyramid, face::Int) = face == 1 ? RefQuadrilateral : RefTriangle getfacerefshape(::Wedge, face::Int) = face ∈ (1,5) ? RefTriangle : RefQuadrilateral -# For testing of show of various types -function show_as_string(value, mime=MIME"text/plain"()) - io = IOBuffer() - show(IOContext(io), mime, value) - return String(take!(io)) -end - ###################################################### # Dummy RefShape to test get_transformation_matrix # ###################################################### From c76b57f7fe10feccddb6540f8642be9d7262a6c1 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 12:56:14 +0100 Subject: [PATCH 103/172] Use (at)eval instead of eval(quote --- src/FEValues/CellValues.jl | 4 +--- src/FEValues/FaceValues.jl | 4 +--- test/test_cellvalues.jl | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 6d7f1522ce..556a855213 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -74,9 +74,7 @@ shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient) - eval(quote - @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) - end) + @eval @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) end # Access quadrature rule values diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 25d31c3009..28d6818a93 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -77,9 +77,7 @@ get_geo_mapping(fv::FaceValues) = @inbounds fv.geo_mapping[getcurrentface(fv)] get_fun_values(fv::FaceValues) = @inbounds fv.fun_values[getcurrentface(fv)] for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient, :shape_curl) - eval(quote - @propagate_inbounds $op(fv::FaceValues, i::Int, q_point::Int) = $op(get_fun_values(fv), i, q_point) - end) + @eval @propagate_inbounds $op(fv::FaceValues, i::Int, q_point::Int) = $op(get_fun_values(fv), i, q_point) end """ diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 7f90d0ccde..a6cefb2e47 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -372,9 +372,7 @@ end # If changes are made that makes the following tests fails, # the devdocs should be updated accordingly. for op = (:shape_value, :shape_gradient, :getnquadpoints, :getnbasefunctions, :geometric_value, :getngeobasefunctions) - eval(quote - Ferrite.$op(cv::TestCustomCellValues, args...; kwargs...) = Ferrite.$op(cv.cv, args...; kwargs...) - end) + @eval Ferrite.$op(cv::TestCustomCellValues, args...; kwargs...) = Ferrite.$op(cv.cv, args...; kwargs...) end ip = Lagrange{RefQuadrilateral,1}()^2 qr = QuadratureRule{RefQuadrilateral}(2) From 6fe3fc697578fdf82d5d0d55abef7f247c200139 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 13:19:47 +0100 Subject: [PATCH 104/172] Include missed code from master merge --- src/FEValues/FaceValues.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 28d6818a93..ec4ad5d0d1 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -186,8 +186,8 @@ function BCValues(::Type{T}, func_interpol::Interpolation{refshape}, geom_interp nqp = zeros(Int,n_boundary_entities) for n_boundary_entity in 1:n_boundary_entities - for (qp, ξ) in enumerate(qrs[n_boundary_entity].points), i in 1:n_geom_basefuncs - M[i, qp, n_boundary_entity] = shape_value(geom_interpol, ξ, i) + for (qp, ξ) in pairs(qrs[n_boundary_entity].points) + shape_values!(@view(M[:, qp, n_boundary_entity]), geom_interpol, ξ) end nqp[n_boundary_entity] = length(qrs[n_boundary_entity].points) end From 7d497d0eba18c7461a4fc28e7fab859e2801c75f Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 13:50:13 +0100 Subject: [PATCH 105/172] Change order or args for reinit --- docs/src/literate-tutorials/heat_equation_rt.jl | 12 ++++++------ .../src/literate-tutorials/heat_equation_triangle.jl | 2 +- src/FEValues/CellValues.jl | 6 +++++- src/FEValues/FaceValues.jl | 6 +++++- test/InterpolationTestUtils.jl | 6 +++--- test/test_cellvalues.jl | 4 ++-- 6 files changed, 22 insertions(+), 14 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl index 4679cfa4f4..25bcb5999b 100644 --- a/docs/src/literate-tutorials/heat_equation_rt.jl +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -211,8 +211,8 @@ function assemble_global(cellvalues, K::SparseMatrixCSC, dh::DofHandler) cell = getcells(grid, cellnr) getcoordinates!(x, grid, cell) celldofs!(dofs, dh, cellnr) - reinit!(cellvalues[:u], x, cell) - reinit!(cellvalues[:q], x, cell) + reinit!(cellvalues[:u], cell, x) + reinit!(cellvalues[:q], cell, x) ## Reset to 0 fill!(Ke, 0) fill!(fe, 0) @@ -268,7 +268,7 @@ function calculate_flux(dh, dΩ, ip, a) cell = getcells(grid, cellnr) celldofs!(dofs, dh, cellnr) map!(i->a[i], ae, dofs) - reinit!(fv, x, facenr, cell) + reinit!(fv, cell, x, facenr) for q_point in 1:getnquadpoints(fv) dΓ = getdetJdV(fv, q_point) n = getnormal(fv, q_point) @@ -294,7 +294,7 @@ function calculate_flux_lag(dh, dΩ, ip, a) cell = getcells(grid, cellnr) celldofs!(dofs, dh, cellnr) map!(i->a[i], ae, dofs) - reinit!(fv, x, facenr, cell) + reinit!(fv, cell, x, facenr) for q_point in 1:getnquadpoints(fv) dΓ = getdetJdV(fv, q_point) n = getnormal(fv, q_point) @@ -317,8 +317,8 @@ function get_Ke(dh, cellvalues; cellnr=1) fe = zeros(ncelldofs) x = getcoordinates(grid, cellnr) cell = getcells(grid, cellnr) - reinit!(cellvalues[:u], x, cell) - reinit!(cellvalues[:q], x, cell) + reinit!(cellvalues[:u], cell, x) + reinit!(cellvalues[:q], cell, x) ## Reset to 0 fill!(Ke, 0) diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl index bf312ef04a..414a7f9195 100644 --- a/docs/src/literate-tutorials/heat_equation_triangle.jl +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -229,7 +229,7 @@ function calculate_flux_lag(dh, dΩ, ip, a) cell = getcells(grid, cellnr) celldofs!(dofs, dh, cellnr) map!(i->a[i], ae, dofs) - reinit!(fv, x, facenr, cell) + reinit!(fv, cell, x, facenr) for q_point in 1:getnquadpoints(fv) dΓ = getdetJdV(fv, q_point) n = getnormal(fv, q_point) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 556a855213..48aac986ee 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -87,7 +87,11 @@ getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) end @inline _update_detJdV!(::Nothing, q_point, w, mapping) = nothing -function reinit!(cv::CellValues, x::AbstractVector{<:Vec}, cell=nothing) +@inline function reinit!(cv::CellValues, x::AbstractVector) + return reinit!(cv::FaceValues, nothing, x::AbstractVector) +end + +function reinit!(cv::CellValues, cell, x::AbstractVector{<:Vec}) geo_mapping = cv.geo_mapping fun_values = cv.fun_values n_geom_basefuncs = getngeobasefunctions(geo_mapping) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index ec4ad5d0d1..68d56a2374 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -104,7 +104,11 @@ function set_current_face!(fv::FaceValues, face_nr::Int) fv.current_face[] = face_nr end -function reinit!(fv::FaceValues, x::AbstractVector{Vec{dim, T}}, face_nr::Int, cell = nothing) where {dim, T} +@inline function reinit!(fv::FaceValues, x::AbstractVector, face_nr::Int) + return reinit!(fv::FaceValues, nothing, x::AbstractVector, face_nr::Int) +end + +function reinit!(fv::FaceValues, cell, x::AbstractVector{Vec{dim, T}}, face_nr::Int) where {dim, T} check_reinit_sdim_consistency(:FaceValues, shape_gradient_type(fv), eltype(x)) set_current_face!(fv, face_nr) n_geom_basefuncs = getngeobasefunctions(fv) diff --git a/test/InterpolationTestUtils.jl b/test/InterpolationTestUtils.jl index 11e5c08b68..563241e042 100644 --- a/test/InterpolationTestUtils.jl +++ b/test/InterpolationTestUtils.jl @@ -51,7 +51,7 @@ module InterpolationTestUtils point_coords = zeros(eltype(cell_coords), length(inds)) point_normal = similar(point_coords) fun_vals = zeros(typeof(shape_value(fv, 1, 1)), length(inds)) - reinit!(fv, cell_coords, facenr, cell) + reinit!(fv, cell, cell_coords, facenr) ue = u[celldofs(dh, cellnr)] for (i, q_point) in enumerate(inds) point_coords[i] = spatial_coordinate(fv, q_point, cell_coords) @@ -67,7 +67,7 @@ module InterpolationTestUtils ξs = collect(last.(local_coords)) # Extract the local coordinates qr = QuadratureRule{RefShape}(zeros(length(ξs)), ξs) cv = CellValues(qr, ip_fun, ip_geo) - reinit!(cv, cell_coords2, cell2) + reinit!(cv, cell2, cell_coords2) fun_vals2 = similar(fun_vals) ue2 = u[celldofs(dh, face2[1])] for q_point in 1:getnquadpoints(cv) @@ -102,7 +102,7 @@ module InterpolationTestUtils ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) qr = create_gradcheck_qr(ip_geo, ΔL) cv = CellValues(qr, ip_fun, ip_geo) - reinit!(cv, x, cell) + reinit!(cv, cell, x) Δu_num = function_value(cv, 2, ue) - function_value(cv, 1, ue) Δx = spatial_coordinate(cv, 2, x) - spatial_coordinate(cv, 1, x) ∇u1 = function_gradient(cv, 1, ue) diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index a6cefb2e47..44378383b7 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -155,8 +155,8 @@ end reinit!(cvv, x) reinit!(fsv, x, 1) reinit!(fvv, x, 1) - reinit!(cv_nedelec, x, cell) - reinit!(fv_nedelec, x, 1, cell) + reinit!(cv_nedelec, cell, x) + reinit!(fv_nedelec, cell, x, 1) # Wrong number of coordinates xx = [x; x] From 86655d97faec7acceef0d97845818b57aff7b5e9 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 14:36:06 +0100 Subject: [PATCH 106/172] Fix errors due to changed order --- src/FEValues/CellValues.jl | 2 +- src/FEValues/FaceValues.jl | 2 +- test/test_cellvalues.jl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 48aac986ee..61fb61fa87 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -88,7 +88,7 @@ end @inline _update_detJdV!(::Nothing, q_point, w, mapping) = nothing @inline function reinit!(cv::CellValues, x::AbstractVector) - return reinit!(cv::FaceValues, nothing, x::AbstractVector) + return reinit!(cv, nothing, x) end function reinit!(cv::CellValues, cell, x::AbstractVector{<:Vec}) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 68d56a2374..78b6b4df70 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -105,7 +105,7 @@ function set_current_face!(fv::FaceValues, face_nr::Int) end @inline function reinit!(fv::FaceValues, x::AbstractVector, face_nr::Int) - return reinit!(fv::FaceValues, nothing, x::AbstractVector, face_nr::Int) + return reinit!(fv, nothing, x, face_nr) end function reinit!(fv::FaceValues, cell, x::AbstractVector{Vec{dim, T}}, face_nr::Int) where {dim, T} diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 44378383b7..851855b3a8 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -380,7 +380,7 @@ end grid = generate_grid(Quadrilateral, (1,1)) x = getcoordinates(grid, 1) cell = getcells(grid, 1) - reinit!(cv, x, cell) + reinit!(cv, cell, x) ae = rand(getnbasefunctions(cv)) q_point = rand(1:getnquadpoints(cv)) cv_custom = TestCustomCellValues(cv) From 8f160664658ee1bc52c239e5bd20912d483edb92 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 14:53:07 +0100 Subject: [PATCH 107/172] Update docstrings to new order in reinit --- src/FEValues/common_values.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 2e651b2ba9..dad7f8b50c 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -26,9 +26,9 @@ end end """ - reinit!(cv::CellValues, x::Vector, cell::AbstractCell) + reinit!(cv::CellValues, cell::AbstractCell, x::Vector) reinit!(cv::CellValues, x::Vector) - reinit!(fv::FaceValues, x::Vector, face::Int, cell::AbstractCell) + reinit!(fv::FaceValues, cell::AbstractCell, x::Vector, face::Int) reinit!(fv::FaceValues, x::Vector, face::Int) Update the `CellValues`/`FaceValues` object for a cell or face with coordinates `x`. From 08a67afd5e95ca3a1f9a506d656471f63379f907 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 15:30:29 +0100 Subject: [PATCH 108/172] Change input kwargs --- src/FEValues/CellValues.jl | 9 ++++++--- src/FEValues/FaceValues.jl | 5 ++++- src/PointEval/PointEvalHandler.jl | 4 ++-- src/PointEval/point_values.jl | 5 +++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 61fb61fa87..df989fc7bd 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -41,11 +41,14 @@ struct CellValues{FV, GM, QR, detT} <: AbstractCellValues qr::QR # QuadratureRule detJdV::detT # AbstractVector{<:Number} or Nothing end -function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; FunDiffOrder = 1, save_detJdV = true) where T - GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), save_detJdV) +function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; + update_gradients = true, update_detJdV = true) where T + + FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs + GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), update_detJdV) geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) - detJdV = save_detJdV ? fill(T(NaN), length(getweights(qr))) : nothing + detJdV = update_detJdV ? fill(T(NaN), length(getweights(qr))) : nothing return CellValues(fun_values, geo_mapping, qr, detJdV) end diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index 78b6b4df70..e170b4a716 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -40,7 +40,10 @@ struct FaceValues{FV, GM, FQR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:Abstrac current_face::ScalarWrapper{Int} end -function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = default_geometric_interpolation(ip_fun); FunDiffOrder = 1) where {T,sdim} +function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = default_geometric_interpolation(ip_fun); + update_gradients = true) where {T,sdim} + + FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), 1) geo_mapping = [GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) for qr in fqr.face_rules] fun_values = [FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] diff --git a/src/PointEval/PointEvalHandler.jl b/src/PointEval/PointEvalHandler.jl index 4da29b765b..e8da115a07 100644 --- a/src/PointEval/PointEvalHandler.jl +++ b/src/PointEval/PointEvalHandler.jl @@ -185,7 +185,7 @@ function evaluate_at_points(ph::PointEvalHandler{<:Any, dim, T1}, dh::AbstractDo npoints = length(ph.cells) # Figure out the value type by creating a dummy PointValues ip = getfieldinterpolation(dh, find_field(dh, fname)) - pv = PointValues(T1, ip; FunDiffOrder=0) + pv = PointValues(T1, ip; update_gradients = false) zero_val = function_value_init(pv, dof_vals) # Allocate the output as NaNs nanv = convert(typeof(zero_val), NaN * zero_val) @@ -244,7 +244,7 @@ function _evaluate_at_points!( first_cell = cellset === nothing ? 1 : first(cellset) grid = get_grid(dh) ip_geo = default_interpolation(getcelltype(grid, first_cell)) - pv = PointValues(eltype(local_coords[idx]), ip, ip_geo; FunDiffOrder = 0) + pv = PointValues(eltype(local_coords[idx]), ip, ip_geo; update_gradients = false) cell_dofs = Vector{Int}(undef, ndofs_per_cell(dh, first_cell)) u_e = Vector{T}(undef, ndofs_per_cell(dh, first_cell)) x = getcoordinates(grid, first_cell) diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 20b43842c9..044a851afd 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -28,7 +28,8 @@ function PointValues(cv::CellValues) T = typeof(getdetJdV(cv, 1)) ip_fun = get_function_interpolation(cv) ip_geo = get_geometric_interpolation(cv) - return PointValues(T, ip_fun, ip_geo; FunDiffOrder = get_function_difforder(cv)) + update_gradients = get_function_difforder(cv) == 1 + return PointValues(T, ip_fun, ip_geo; update_gradients) end function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip); kwargs...) return PointValues(Float64, ip, ipg; kwargs...) @@ -39,7 +40,7 @@ function PointValues(::Type{T}, ip::IP, ipg::GIP = default_geometric_interpolati GIP <: Interpolation{shape} } qr = QuadratureRule{shape, T}([one(T)], [zero(Vec{dim, T})]) - cv = CellValues(T, qr, ip, ipg; save_detJdV = false, kwargs...) + cv = CellValues(T, qr, ip, ipg; update_detJdV = false, kwargs...) return PointValues{typeof(cv)}(cv) end From be5d910a71786fec50b1be16ae3580f2ea5d8652 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 15:46:31 +0100 Subject: [PATCH 109/172] Simplify code for FunctionValues construction --- src/FEValues/FunctionValues.jl | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index c4e08b4c81..03edcc191f 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -50,30 +50,24 @@ struct FunctionValues{DiffOrder, IP, N_t, dNdx_t, dNdξ_t} return new{1, typeof(ip), N_t, typeof(dNdx), typeof(dNdξ)}(ip, N_x, N_ξ, dNdx, dNdξ) end end -function FunctionValues{0}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T +function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where {DiffOrder, T} ip_dims = InterpolationDims(ip, ip_geo) n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) N_ξ = zeros(typeof_N(T, ip_dims), n_shape, n_qpoints) N_x = isa(get_mapping_type(ip), IdentityMapping) ? N_ξ : similar(N_ξ) - - fv = FunctionValues(ip, N_x, N_ξ, nothing, nothing) - precompute_values!(fv, getpoints(qr)) # Separate function for qr point update in PointValues - return fv -end -function FunctionValues{1}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where T - ip_dims = InterpolationDims(ip, ip_geo) - n_shape = getnbasefunctions(ip) - n_qpoints = getnquadpoints(qr) - - N_ξ = zeros(typeof_N(T, ip_dims), n_shape, n_qpoints) - N_x = isa(get_mapping_type(ip), IdentityMapping) ? N_ξ : similar(N_ξ) - - dNdξ = zeros(typeof_dNdξ(T, ip_dims), n_shape, n_qpoints) - dNdx = fill(zero(typeof_dNdx(T, ip_dims)) * T(NaN), n_shape, n_qpoints) - - fv = FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) + + if DiffOrder == 0 + dNdξ = dNdx = nothing + elseif DiffOrder > 1 + throw(ArgumentError("Currently only values and gradients can be updated in FunctionValues")) + else + dNdξ = zeros(typeof_dNdξ(T, ip_dims), n_shape, n_qpoints) + dNdx = fill(zero(typeof_dNdx(T, ip_dims)) * T(NaN), n_shape, n_qpoints) + end + + fv = FunctionValues(ip, N_x, N_ξ, dNdξ, dNdx) precompute_values!(fv, getpoints(qr)) # Separate function for qr point update in PointValues return fv end From 41c92c4669f81bd8aebec2a2ca69a17d726cf253 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 17:05:01 +0100 Subject: [PATCH 110/172] Fix order of args --- src/FEValues/FunctionValues.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 03edcc191f..955de431c5 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -67,7 +67,7 @@ function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureR dNdx = fill(zero(typeof_dNdx(T, ip_dims)) * T(NaN), n_shape, n_qpoints) end - fv = FunctionValues(ip, N_x, N_ξ, dNdξ, dNdx) + fv = FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) precompute_values!(fv, getpoints(qr)) # Separate function for qr point update in PointValues return fv end From 36c018ecf7666dbca5b4d087aaea7c2c4c1bd7f5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 4 Dec 2023 17:17:49 +0100 Subject: [PATCH 111/172] More logic if in FunctionValues --- src/FEValues/FunctionValues.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 955de431c5..398336b23b 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -60,11 +60,11 @@ function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureR if DiffOrder == 0 dNdξ = dNdx = nothing - elseif DiffOrder > 1 - throw(ArgumentError("Currently only values and gradients can be updated in FunctionValues")) - else + elseif DiffOrder == 1 dNdξ = zeros(typeof_dNdξ(T, ip_dims), n_shape, n_qpoints) dNdx = fill(zero(typeof_dNdx(T, ip_dims)) * T(NaN), n_shape, n_qpoints) + else + throw(ArgumentError("Currently only values and gradients can be updated in FunctionValues")) end fv = FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) From c7d7a0e770d61ee93ae4f83ff4320ab2fcb988ae Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 5 Dec 2023 11:31:39 +0100 Subject: [PATCH 112/172] Remove use of (at)eval and be explicit instead --- src/FEValues/CellValues.jl | 6 +++--- src/FEValues/FaceValues.jl | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index df989fc7bd..ef1bfebef8 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -76,9 +76,9 @@ get_function_difforder(cv::CellValues) = get_function_difforder(cv.fun_values) shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) -for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient) - @eval @propagate_inbounds $op(cv::CellValues, i::Int, q_point::Int) = $op(cv.fun_values, i, q_point) -end +@propagate_inbounds shape_value(cv::CellValues, i::Int, q_point::Int) = shape_value(cv.fun_values, i, q_point) +@propagate_inbounds shape_gradient(cv::CellValues, i::Int, q_point::Int) = shape_gradient(cv.fun_values, i, q_point) +@propagate_inbounds shape_symmetric_gradient(cv::CellValues, i::Int, q_point::Int) = shape_symmetric_gradient(cv.fun_values, i, q_point) # Access quadrature rule values getnquadpoints(cv::CellValues) = getnquadpoints(cv.qr) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index e170b4a716..c6a0e50f6e 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -79,9 +79,10 @@ get_geo_mapping(fv::FaceValues) = @inbounds fv.geo_mapping[getcurrentface(fv)] @propagate_inbounds geometric_value(fv::FaceValues, args...) = geometric_value(get_geo_mapping(fv), args...) get_fun_values(fv::FaceValues) = @inbounds fv.fun_values[getcurrentface(fv)] -for op = (:shape_value, :shape_gradient, :shape_symmetric_gradient, :shape_curl) - @eval @propagate_inbounds $op(fv::FaceValues, i::Int, q_point::Int) = $op(get_fun_values(fv), i, q_point) -end + +@propagate_inbounds shape_value(fv::FaceValues, i::Int, q_point::Int) = shape_value(get_fun_values(fv), i, q_point) +@propagate_inbounds shape_gradient(fv::FaceValues, i::Int, q_point::Int) = shape_gradient(get_fun_values(fv), i, q_point) +@propagate_inbounds shape_symmetric_gradient(fv::FaceValues, i::Int, q_point::Int) = shape_symmetric_gradient(get_fun_values(fv), i, q_point) """ getcurrentface(fv::FaceValues) From 65e916ec78300a0073c9621b5c7b3b6b13f878fe Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 5 Dec 2023 13:24:35 +0100 Subject: [PATCH 113/172] Remove credit from SimpleCellValues literate --- docs/src/topics/FEValues.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/docs/src/topics/FEValues.md b/docs/src/topics/FEValues.md index 11831e91f2..c2c8a23f53 100644 --- a/docs/src/topics/FEValues.md +++ b/docs/src/topics/FEValues.md @@ -113,17 +113,10 @@ Please note that several internal functions are used, and these may change witho ```@eval # Include the example here, but modify the Literate output to suit being embedded -using Literate, Markdown -filename = "SimpleCellValues_literate" -Literate.markdown(filename*".jl"; execute=true) -contents = read(filename*".md", String) -Literate.script(filename*".jl"; name="SimpleCellValues") -rm(filename*".jl") -rm(filename*".md") -header_end = last(findnext("```", contents, 4))+1 -Markdown.parse(replace(contents[header_end:end], - "*This page was generated using [Literate.jl]"=>"*This example was generated using [Literate.jl]") - ) +using Literate +Literate.markdown("SimpleCellValues_literate.jl"; name = "SimpleCellValues", + execute = true, credit = false) +Literate.script("SimpleCellValues_literate.jl"; name="SimpleCellValues") ``` ## Further reading From 8faecd4916fd2bdf9e990e57b2f777cf051e3bd5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Tue, 5 Dec 2023 15:22:29 +0100 Subject: [PATCH 114/172] Parse the generated file and return the MD string to show it --- docs/src/topics/FEValues.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/src/topics/FEValues.md b/docs/src/topics/FEValues.md index c2c8a23f53..1a4f836be8 100644 --- a/docs/src/topics/FEValues.md +++ b/docs/src/topics/FEValues.md @@ -113,10 +113,13 @@ Please note that several internal functions are used, and these may change witho ```@eval # Include the example here, but modify the Literate output to suit being embedded -using Literate -Literate.markdown("SimpleCellValues_literate.jl"; name = "SimpleCellValues", - execute = true, credit = false) -Literate.script("SimpleCellValues_literate.jl"; name="SimpleCellValues") +using Literate, Markdown +base_name = "SimpleCellValues_literate" +Literate.markdown(string(base_name, ".jl"); name = base_name, execute = true, credit = false, documenter=false) +content = read(string(base_name, ".md"), String) +rm(string(base_name, ".md")) +rm(string(base_name, ".jl")) +Markdown.parse(content) ``` ## Further reading From 5c757703555b00d9356366fb4e5a695cf7462123 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 6 Dec 2023 08:59:40 +0100 Subject: [PATCH 115/172] Apply review suggestions --- docs/src/devdocs/interpolations.md | 2 +- src/Dofs/DofHandler.jl | 2 +- src/FEValues/CellValues.jl | 18 ++++----- src/FEValues/FaceValues.jl | 18 ++++----- src/FEValues/FunctionValues.jl | 62 +++++++++++++++--------------- src/FEValues/GeometryMapping.jl | 2 +- src/PointEval/point_values.jl | 6 +-- src/interpolations.jl | 12 +++--- test/test_cellvalues.jl | 8 ++-- test/test_interfacevalues.jl | 4 +- 10 files changed, 67 insertions(+), 67 deletions(-) diff --git a/docs/src/devdocs/interpolations.md b/docs/src/devdocs/interpolations.md index 6d8cfafcce..540d288280 100644 --- a/docs/src/devdocs/interpolations.md +++ b/docs/src/devdocs/interpolations.md @@ -39,7 +39,7 @@ Ferrite.getnbasefunctions(::Interpolation) Ferrite.reference_coordinates(::Interpolation) Ferrite.is_discontinuous(::Interpolation) Ferrite.adjust_dofs_during_distribution(::Interpolation) -Ferrite.get_mapping_type +Ferrite.mapping_type ``` for all entities which exist on that reference element. The dof functions default to having no diff --git a/src/Dofs/DofHandler.jl b/src/Dofs/DofHandler.jl index 2ac3d8e9ea..e6e88dff19 100644 --- a/src/Dofs/DofHandler.jl +++ b/src/Dofs/DofHandler.jl @@ -957,7 +957,7 @@ function _evaluate_at_grid_nodes!(data::Union{Vector,Matrix}, sdh::SubDofHandler u::Vector{T}, cv::CellValues, drange::UnitRange, ::Type{RT}) where {T, RT} ue = zeros(T, length(drange)) # TODO: Remove this hack when embedding works... - if RT <: Vec && get_function_interpolation(cv) isa ScalarInterpolation + if RT <: Vec && function_interpolation(cv) isa ScalarInterpolation uer = reinterpret(RT, ue) else uer = ue diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index ef1bfebef8..362f14ab40 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -42,10 +42,10 @@ struct CellValues{FV, GM, QR, detT} <: AbstractCellValues detJdV::detT # AbstractVector{<:Number} or Nothing end function CellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; - update_gradients = true, update_detJdV = true) where T + update_gradients::Bool = true, update_detJdV::Bool = true) where T FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs - GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), update_detJdV) + GeoDiffOrder = max(required_geo_diff_order(mapping_type(ip_fun), FunDiffOrder), update_detJdV) geo_mapping = GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) fun_values = FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) detJdV = update_detJdV ? fill(T(NaN), length(getweights(qr))) : nothing @@ -64,15 +64,15 @@ end # Access geometry values @propagate_inbounds getngeobasefunctions(cv::CellValues) = getngeobasefunctions(cv.geo_mapping) @propagate_inbounds geometric_value(cv::CellValues, args...) = geometric_value(cv.geo_mapping, args...) -get_geometric_interpolation(cv::CellValues) = get_geometric_interpolation(cv.geo_mapping) +geometric_interpolation(cv::CellValues) = geometric_interpolation(cv.geo_mapping) getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] getdetJdV(::CellValues{<:Any, <:Any, <:Any, Nothing}, ::Int) = throw(ArgumentError("detJdV is not saved in CellValues")) # Accessors for function values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) -get_function_interpolation(cv::CellValues) = get_function_interpolation(cv.fun_values) -get_function_difforder(cv::CellValues) = get_function_difforder(cv.fun_values) +function_interpolation(cv::CellValues) = function_interpolation(cv.fun_values) +function_difforder(cv::CellValues) = function_difforder(cv.fun_values) shape_value_type(cv::CellValues) = shape_value_type(cv.fun_values) shape_gradient_type(cv::CellValues) = shape_gradient_type(cv.fun_values) @@ -94,13 +94,13 @@ end return reinit!(cv, nothing, x) end -function reinit!(cv::CellValues, cell, x::AbstractVector{<:Vec}) +function reinit!(cv::CellValues, cell::Union{AbstractCell, Nothing}, x::AbstractVector{<:Vec}) geo_mapping = cv.geo_mapping fun_values = cv.fun_values n_geom_basefuncs = getngeobasefunctions(geo_mapping) check_reinit_sdim_consistency(:CellValues, shape_gradient_type(cv), eltype(x)) - if cell === nothing && !isa(get_mapping_type(fun_values), IdentityMapping) + if cell === nothing && !isa(mapping_type(fun_values), IdentityMapping) throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) end if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x) != n_geom_basefuncs @@ -115,8 +115,8 @@ function reinit!(cv::CellValues, cell, x::AbstractVector{<:Vec}) end function Base.show(io::IO, d::MIME"text/plain", cv::CellValues) - ip_geo = get_geometric_interpolation(cv) - ip_fun = get_function_interpolation(cv) + ip_geo = geometric_interpolation(cv) + ip_fun = function_interpolation(cv) rdim = getdim(ip_geo) vdim = isa(shape_value(cv, 1, 1), Vec) ? length(shape_value(cv, 1, 1)) : 0 GradT = shape_gradient_type(cv) diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/FaceValues.jl index c6a0e50f6e..69bf96e29f 100644 --- a/src/FEValues/FaceValues.jl +++ b/src/FEValues/FaceValues.jl @@ -41,10 +41,10 @@ struct FaceValues{FV, GM, FQR, detT, nT, V_FV<:AbstractVector{FV}, V_GM<:Abstrac end function FaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = default_geometric_interpolation(ip_fun); - update_gradients = true) where {T,sdim} + update_gradients::Bool = true) where {T,sdim} FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs - GeoDiffOrder = max(required_geo_diff_order(get_mapping_type(ip_fun), FunDiffOrder), 1) + GeoDiffOrder = max(required_geo_diff_order(mapping_type(ip_fun), FunDiffOrder), 1) geo_mapping = [GeometryMapping{GeoDiffOrder}(T, ip_geo.ip, qr) for qr in fqr.face_rules] fun_values = [FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo) for qr in fqr.face_rules] max_nquadpoints = maximum(qr->length(getweights(qr)), fqr.face_rules) @@ -71,9 +71,9 @@ getnquadpoints(fv::FaceValues) = @inbounds getnquadpoints(fv.fqr, getcurrentface shape_value_type(fv::FaceValues) = shape_value_type(get_fun_values(fv)) shape_gradient_type(fv::FaceValues) = shape_gradient_type(get_fun_values(fv)) -get_function_interpolation(fv::FaceValues) = get_function_interpolation(get_fun_values(fv)) -get_function_difforder(fv::FaceValues) = get_function_difforder(get_fun_values(fv)) -get_geometric_interpolation(fv::FaceValues) = get_geometric_interpolation(get_geo_mapping(fv)) +function_interpolation(fv::FaceValues) = function_interpolation(get_fun_values(fv)) +function_difforder(fv::FaceValues) = function_difforder(get_fun_values(fv)) +geometric_interpolation(fv::FaceValues) = geometric_interpolation(get_geo_mapping(fv)) get_geo_mapping(fv::FaceValues) = @inbounds fv.geo_mapping[getcurrentface(fv)] @propagate_inbounds geometric_value(fv::FaceValues, args...) = geometric_value(get_geo_mapping(fv), args...) @@ -112,7 +112,7 @@ end return reinit!(fv, nothing, x, face_nr) end -function reinit!(fv::FaceValues, cell, x::AbstractVector{Vec{dim, T}}, face_nr::Int) where {dim, T} +function reinit!(fv::FaceValues, cell::Union{AbstractCell, Nothing}, x::AbstractVector{Vec{dim, T}}, face_nr::Int) where {dim, T} check_reinit_sdim_consistency(:FaceValues, shape_gradient_type(fv), eltype(x)) set_current_face!(fv, face_nr) n_geom_basefuncs = getngeobasefunctions(fv) @@ -123,7 +123,7 @@ function reinit!(fv::FaceValues, cell, x::AbstractVector{Vec{dim, T}}, face_nr:: geo_mapping = get_geo_mapping(fv) fun_values = get_fun_values(fv) - if cell === nothing && !isa(get_mapping_type(fun_values), IdentityMapping) + if cell === nothing && !isa(mapping_type(fun_values), IdentityMapping) throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) end @@ -141,7 +141,7 @@ function reinit!(fv::FaceValues, cell, x::AbstractVector{Vec{dim, T}}, face_nr:: end function Base.show(io::IO, d::MIME"text/plain", fv::FaceValues) - ip_geo = get_geometric_interpolation(fv) + ip_geo = geometric_interpolation(fv) rdim = getdim(ip_geo) vdim = isa(shape_value(fv, 1, 1), Vec) ? length(shape_value(fv, 1, 1)) : 0 sdim = length(shape_gradient(fv, 1, 1)) ÷ length(shape_value(fv, 1, 1)) @@ -153,7 +153,7 @@ function Base.show(io::IO, d::MIME"text/plain", fv::FaceValues) else println(io, tuple(nqp...), " quadrature points on each face") end - print(io, " Function interpolation: "); show(io, d, get_function_interpolation(fv)) + print(io, " Function interpolation: "); show(io, d, function_interpolation(fv)) print(io, "\nGeometric interpolation: "); show(io, d, ip_geo^sdim) end diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 398336b23b..c45a50df61 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -39,15 +39,15 @@ FunctionValues struct FunctionValues{DiffOrder, IP, N_t, dNdx_t, dNdξ_t} ip::IP # ::Interpolation - N_x::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} - N_ξ::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} + Nx::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} + Nξ::N_t # ::AbstractMatrix{Union{<:Tensor,<:Number}} dNdx::dNdx_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} or Nothing dNdξ::dNdξ_t # ::AbstractMatrix{Union{<:Tensor,<:StaticArray}} or Nothing - function FunctionValues(ip::Interpolation, N_x::N_t, N_ξ::N_t, ::Nothing, ::Nothing) where {N_t<:AbstractMatrix} - return new{0, typeof(ip), N_t, Nothing, Nothing}(ip, N_x, N_ξ, nothing, nothing) + function FunctionValues(ip::Interpolation, Nx::N_t, Nξ::N_t, ::Nothing, ::Nothing) where {N_t<:AbstractMatrix} + return new{0, typeof(ip), N_t, Nothing, Nothing}(ip, Nx, Nξ, nothing, nothing) end - function FunctionValues(ip::Interpolation, N_x::N_t, N_ξ::N_t, dNdx::AbstractMatrix, dNdξ::AbstractMatrix) where {N_t<:AbstractMatrix} - return new{1, typeof(ip), N_t, typeof(dNdx), typeof(dNdξ)}(ip, N_x, N_ξ, dNdx, dNdξ) + function FunctionValues(ip::Interpolation, Nx::N_t, Nξ::N_t, dNdx::AbstractMatrix, dNdξ::AbstractMatrix) where {N_t<:AbstractMatrix} + return new{1, typeof(ip), N_t, typeof(dNdx), typeof(dNdξ)}(ip, Nx, Nξ, dNdx, dNdξ) end end function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureRule, ip_geo::VectorizedInterpolation) where {DiffOrder, T} @@ -55,8 +55,8 @@ function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureR n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) - N_ξ = zeros(typeof_N(T, ip_dims), n_shape, n_qpoints) - N_x = isa(get_mapping_type(ip), IdentityMapping) ? N_ξ : similar(N_ξ) + Nξ = zeros(typeof_N(T, ip_dims), n_shape, n_qpoints) + Nx = isa(mapping_type(ip), IdentityMapping) ? Nξ : similar(Nξ) if DiffOrder == 0 dNdξ = dNdx = nothing @@ -67,34 +67,34 @@ function FunctionValues{DiffOrder}(::Type{T}, ip::Interpolation, qr::QuadratureR throw(ArgumentError("Currently only values and gradients can be updated in FunctionValues")) end - fv = FunctionValues(ip, N_x, N_ξ, dNdx, dNdξ) + fv = FunctionValues(ip, Nx, Nξ, dNdx, dNdξ) precompute_values!(fv, getpoints(qr)) # Separate function for qr point update in PointValues return fv end function precompute_values!(fv::FunctionValues{0}, qr_points::Vector{<:Vec}) - shape_values!(fv.N_ξ, fv.ip, qr_points) + shape_values!(fv.Nξ, fv.ip, qr_points) end function precompute_values!(fv::FunctionValues{1}, qr_points::Vector{<:Vec}) - shape_gradients_and_values!(fv.dNdξ, fv.N_ξ, fv.ip, qr_points) + shape_gradients_and_values!(fv.dNdξ, fv.Nξ, fv.ip, qr_points) end function Base.copy(v::FunctionValues) - N_ξ_copy = copy(v.N_ξ) - N_x_copy = v.N_ξ === v.N_x ? N_ξ_copy : copy(v.N_x) # Preserve aliasing + Nξ_copy = copy(v.Nξ) + Nx_copy = v.Nξ === v.Nx ? Nξ_copy : copy(v.Nx) # Preserve aliasing dNdx_copy = _copy_or_nothing(v.dNdx) dNdξ_copy = _copy_or_nothing(v.dNdξ) - return FunctionValues(copy(v.ip), N_x_copy, N_ξ_copy, dNdx_copy, dNdξ_copy) + return FunctionValues(copy(v.ip), Nx_copy, Nξ_copy, dNdx_copy, dNdξ_copy) end -getnbasefunctions(funvals::FunctionValues) = size(funvals.N_x, 1) -@propagate_inbounds shape_value(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.N_x[base_func, q_point] +getnbasefunctions(funvals::FunctionValues) = size(funvals.Nx, 1) +@propagate_inbounds shape_value(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.Nx[base_func, q_point] @propagate_inbounds shape_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = funvals.dNdx[base_func, q_point] @propagate_inbounds shape_symmetric_gradient(funvals::FunctionValues, q_point::Int, base_func::Int) = symmetric(shape_gradient(funvals, q_point, base_func)) -get_function_interpolation(funvals::FunctionValues) = funvals.ip -get_function_difforder(::FunctionValues{DiffOrder}) where DiffOrder = DiffOrder -shape_value_type(funvals::FunctionValues) = eltype(funvals.N_x) +function_interpolation(funvals::FunctionValues) = funvals.ip +function_difforder(::FunctionValues{DiffOrder}) where DiffOrder = DiffOrder +shape_value_type(funvals::FunctionValues) = eltype(funvals.Nx) shape_gradient_type(funvals::FunctionValues) = eltype(funvals.dNdx) shape_gradient_type(::FunctionValues{0}) = nothing @@ -123,7 +123,7 @@ struct ContravariantPiolaMapping end # struct DoubleCovariantPiolaMapping end # struct DoubleContravariantPiolaMapping end -get_mapping_type(fv::FunctionValues) = get_mapping_type(fv.ip) +mapping_type(fv::FunctionValues) = mapping_type(fv.ip) """ required_geo_diff_order(fun_mapping, fun_diff_order::Int) @@ -154,7 +154,7 @@ required_geo_diff_order(::CovariantPiolaMapping, fun_diff_order::Int) = 1 + # Apply mapping # ============= @inline function apply_mapping!(funvals::FunctionValues, q_point::Int, args...) - return apply_mapping!(funvals, get_mapping_type(funvals), q_point, args...) + return apply_mapping!(funvals, mapping_type(funvals), q_point, args...) end # Identity mapping @@ -176,8 +176,8 @@ end Jinv = inv(getjacobian(mapping_values)) @inbounds for j in 1:getnbasefunctions(funvals) d = get_direction(funvals.ip, j, cell) - N_ξ = funvals.N_ξ[j, q_point] - funvals.N_x[j, q_point] = d*(N_ξ ⋅ Jinv) + Nξ = funvals.Nξ[j, q_point] + funvals.Nx[j, q_point] = d*(Nξ ⋅ Jinv) end return nothing end @@ -188,9 +188,9 @@ end @inbounds for j in 1:getnbasefunctions(funvals) d = get_direction(funvals.ip, j, cell) dNdξ = funvals.dNdξ[j, q_point] - N_ξ = funvals.N_ξ[j, q_point] - funvals.N_x[j, q_point] = d*(N_ξ ⋅ Jinv) - funvals.dNdx[j, q_point] = d*(Jinv' ⋅ dNdξ ⋅ Jinv - Jinv' ⋅ (N_ξ ⋅ Jinv ⋅ H ⋅ Jinv)) + Nξ = funvals.Nξ[j, q_point] + funvals.Nx[j, q_point] = d*(Nξ ⋅ Jinv) + funvals.dNdx[j, q_point] = d*(Jinv' ⋅ dNdξ ⋅ Jinv - Jinv' ⋅ (Nξ ⋅ Jinv ⋅ H ⋅ Jinv)) end return nothing end @@ -201,8 +201,8 @@ end detJ = det(J) @inbounds for j in 1:getnbasefunctions(funvals) d = get_direction(funvals.ip, j, cell) - N_ξ = funvals.N_ξ[j, q_point] - funvals.N_x[j, q_point] = d*(J ⋅ N_ξ)/detJ + Nξ = funvals.Nξ[j, q_point] + funvals.Nx[j, q_point] = d*(J ⋅ Nξ)/detJ end return nothing end @@ -219,9 +219,9 @@ end @inbounds for j in 1:getnbasefunctions(funvals) d = get_direction(funvals.ip, j, cell) dNdξ = funvals.dNdξ[j, q_point] - N_ξ = funvals.N_ξ[j, q_point] - funvals.N_x[j, q_point] = d*(J ⋅ N_ξ)/detJ - funvals.dNdx[j, q_point] = d*(J ⋅ dNdξ ⋅ Jinv/detJ + A1 ⋅ N_ξ - (J ⋅ N_ξ) ⊗ A2) + Nξ = funvals.Nξ[j, q_point] + funvals.Nx[j, q_point] = d*(J ⋅ Nξ)/detJ + funvals.dNdx[j, q_point] = d*(J ⋅ dNdξ ⋅ Jinv/detJ + A1 ⋅ Nξ - (J ⋅ Nξ) ⊗ A2) end return nothing end diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index b3a283dfed..682d56a114 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -98,7 +98,7 @@ end getngeobasefunctions(geo_mapping::GeometryMapping) = size(geo_mapping.M, 1) @propagate_inbounds geometric_value(geo_mapping::GeometryMapping, q_point::Int, base_func::Int) = geo_mapping.M[base_func, q_point] -get_geometric_interpolation(geo_mapping::GeometryMapping) = geo_mapping.ip +geometric_interpolation(geo_mapping::GeometryMapping) = geo_mapping.ip # Hot-fixes to support embedded elements before MixedTensors are available # See https://github.com/Ferrite-FEM/Tensors.jl/pull/188 diff --git a/src/PointEval/point_values.jl b/src/PointEval/point_values.jl index 044a851afd..62107b4343 100644 --- a/src/PointEval/point_values.jl +++ b/src/PointEval/point_values.jl @@ -26,9 +26,9 @@ end function PointValues(cv::CellValues) T = typeof(getdetJdV(cv, 1)) - ip_fun = get_function_interpolation(cv) - ip_geo = get_geometric_interpolation(cv) - update_gradients = get_function_difforder(cv) == 1 + ip_fun = function_interpolation(cv) + ip_geo = geometric_interpolation(cv) + update_gradients = function_difforder(cv) == 1 return PointValues(T, ip_fun, ip_geo; update_gradients) end function PointValues(ip::Interpolation, ipg::Interpolation = default_geometric_interpolation(ip); kwargs...) diff --git a/src/interpolations.jl b/src/interpolations.jl index b2f519dd59..e394b7052c 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1559,17 +1559,17 @@ reference_coordinates(ip::VectorizedInterpolation) = reference_coordinates(ip.ip is_discontinuous(::Type{<:VectorizedInterpolation{<:Any, <:Any, <:Any, ip}}) where {ip} = is_discontinuous(ip) """ - get_mapping_type(ip::Interpolation) + mapping_type(ip::Interpolation) Get the type of mapping from the reference cell to the real cell for an interpolation `ip`. Subtypes of `ScalarInterpolation` and `VectorizedInterpolation` return `IdentityMapping()`, but other non-scalar interpolations may request different mapping types. """ -function get_mapping_type end +function mapping_type end -get_mapping_type(::ScalarInterpolation) = IdentityMapping() -get_mapping_type(::VectorizedInterpolation) = IdentityMapping() +mapping_type(::ScalarInterpolation) = IdentityMapping() +mapping_type(::VectorizedInterpolation) = IdentityMapping() ##################################### @@ -1578,7 +1578,7 @@ get_mapping_type(::VectorizedInterpolation) = IdentityMapping() # https://defelement.com/elements/raviart-thomas.html # https://defelement.com/elements/qdiv.html struct RaviartThomas{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end -get_mapping_type(::RaviartThomas) = ContravariantPiolaMapping() +mapping_type(::RaviartThomas) = ContravariantPiolaMapping() n_dbc_components(::RaviartThomas) = 1 # RefTriangle, 1st order Lagrange @@ -1606,7 +1606,7 @@ end # Nedelec (1st kind), H(curl) # ##################################### struct Nedelec{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end -get_mapping_type(::Nedelec) = CovariantPiolaMapping() +mapping_type(::Nedelec) = CovariantPiolaMapping() reference_coordinates(ip::Nedelec{vdim}) where vdim = fill(NaN*zero(Vec{vdim}), getnbasefunctions(ip)) dirichlet_facedof_indices(ip::Nedelec) = facedof_interior_indices(ip) n_dbc_components(::Nedelec) = 1 diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 851855b3a8..9f66ccbbc4 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -318,24 +318,24 @@ end cv = CellValues(qr, fun_ip) @test Ferrite.shape_value_type(cv) == value_type(Float64) @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) - @test Ferrite.get_geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() + @test Ferrite.geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() # Numeric type + quadrature + scalar function cv = CellValues(Float32, qr, fun_ip) @test Ferrite.shape_value_type(cv) == value_type(Float32) @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) - @test Ferrite.get_geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() + @test Ferrite.geometric_interpolation(cv) == Lagrange{RefTriangle, 1}() for geo_ip in (Lagrange{RefTriangle, 2}(), Lagrange{RefTriangle, 2}()^2) scalar_ip(ip) = ip isa VectorizedInterpolation ? ip.ip : ip # Quadrature + scalar function + geo cv = CellValues(qr, fun_ip, geo_ip) @test Ferrite.shape_value_type(cv) == value_type(Float64) @test Ferrite.shape_gradient_type(cv) == grad_type(Float64) - @test Ferrite.get_geometric_interpolation(cv) == scalar_ip(geo_ip) + @test Ferrite.geometric_interpolation(cv) == scalar_ip(geo_ip) # Numeric type + quadrature + scalar function + scalar geo cv = CellValues(Float32, qr, fun_ip, geo_ip) @test Ferrite.shape_value_type(cv) == value_type(Float32) @test Ferrite.shape_gradient_type(cv) == grad_type(Float32) - @test Ferrite.get_geometric_interpolation(cv) == scalar_ip(geo_ip) + @test Ferrite.geometric_interpolation(cv) == scalar_ip(geo_ip) end end end diff --git a/test/test_interfacevalues.jl b/test/test_interfacevalues.jl index f69a49c227..6c99c8eeb1 100644 --- a/test/test_interfacevalues.jl +++ b/test/test_interfacevalues.jl @@ -1,7 +1,7 @@ @testset "InterfaceValues" begin function test_interfacevalues(grid::Ferrite.AbstractGrid, iv::InterfaceValues; tol = 0) - ip_here = Ferrite.get_function_interpolation(iv.here) - ip_there = Ferrite.get_function_interpolation(iv.there) + ip_here = Ferrite.function_interpolation(iv.here) + ip_there = Ferrite.function_interpolation(iv.there) ndim = Ferrite.getdim(ip_here) n_basefuncs = getnbasefunctions(ip_here) + getnbasefunctions(ip_there) From b3ce313c086023a24fd9b5d3eeaa10e68307f41a Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 6 Dec 2023 11:49:07 +0100 Subject: [PATCH 116/172] Fix type instability in point eval --- src/PointEval/PointEvalHandler.jl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/PointEval/PointEvalHandler.jl b/src/PointEval/PointEvalHandler.jl index e8da115a07..7e27417d5a 100644 --- a/src/PointEval/PointEvalHandler.jl +++ b/src/PointEval/PointEvalHandler.jl @@ -204,12 +204,12 @@ 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, + ph::PointEvalHandler{<:Any, <:Any, T_ph}, dh::DofHandler, dof_vals::Vector{T}, fname::Symbol, func_interpolations - ) where {T2, T} + ) where {T2, T_ph, T} # TODO: I don't think this is correct?? length(dof_vals) == ndofs(dh) || error("You must supply values for all $(ndofs(dh)) dofs.") @@ -219,35 +219,38 @@ function evaluate_at_points!(out_vals::Vector{T2}, if ip !== nothing dofrange = dof_range(sdh, fname) cellset = sdh.cellset - _evaluate_at_points!(out_vals, dof_vals, ph, dh, ip, cellset, dofrange) + ip_geo = default_interpolation(getcelltype(sdh)) + pv = PointValues(T_ph, ip, ip_geo; update_gradients = false) + _evaluate_at_points!(out_vals, dof_vals, ph, dh, pv, cellset, dofrange) end end return out_vals end -# function barrier with concrete type of interpolation +# function barrier with concrete type of PointValues function _evaluate_at_points!( out_vals::Vector{T2}, dof_vals::Vector{T}, ph::PointEvalHandler, dh::AbstractDofHandler, - ip::Interpolation, + pv::PointValues, cellset::Union{Nothing, Set{Int}}, dofrange::AbstractRange{Int}, ) where {T2,T} # extract variables local_coords = ph.local_coords + # preallocate some stuff specific to this cellset idx = findfirst(!isnothing, local_coords) idx === nothing && return out_vals - first_cell = cellset === nothing ? 1 : first(cellset) + grid = get_grid(dh) - ip_geo = default_interpolation(getcelltype(grid, first_cell)) - pv = PointValues(eltype(local_coords[idx]), ip, ip_geo; update_gradients = false) + first_cell = cellset === nothing ? 1 : first(cellset) cell_dofs = Vector{Int}(undef, ndofs_per_cell(dh, first_cell)) u_e = Vector{T}(undef, ndofs_per_cell(dh, first_cell)) x = getcoordinates(grid, first_cell) + # compute point values for pointid in eachindex(ph.cells) cellid = ph.cells[pointid] From 2cbd2b96d625e6d84f9ddcde88035a5ead5c5bf5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 6 Dec 2023 13:29:29 +0100 Subject: [PATCH 117/172] Include cell in interface reinits --- src/FEValues/interface_values.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FEValues/interface_values.jl b/src/FEValues/interface_values.jl index dcee07d045..d7c07eff3d 100644 --- a/src/FEValues/interface_values.jl +++ b/src/FEValues/interface_values.jl @@ -124,8 +124,8 @@ function reinit!( ) where {dim, T} # reinit! the here side as normal - reinit!(iv.here, coords_here, face_here) - dim == 1 && return reinit!(iv.there, coords_there, face_there) + reinit!(iv.here, cell_here, coords_here, face_here) + dim == 1 && return reinit!(iv.there, cell_there, coords_there, face_there) # Transform the quadrature points from the here side to the there side set_current_face!(iv.there, face_there) # Includes boundscheck interface_transformation = InterfaceOrientationInfo(cell_here, cell_there, face_here, face_there) @@ -140,7 +140,7 @@ function reinit!( precompute_values!(get_geo_mapping(iv.there), quad_points_b) # reinit! the "there" side - reinit!(iv.there, coords_there, face_there) + reinit!(iv.there, cell_there, coords_there, face_there) return iv end From 3da6bf00ab40f19d5b240e58fb09f206a1a50450 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 6 Dec 2023 13:47:44 +0100 Subject: [PATCH 118/172] Add 2nd order RT ip on triangle --- src/interpolations.jl | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/interpolations.jl b/src/interpolations.jl index e394b7052c..6b1a8224fd 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1601,6 +1601,59 @@ function get_direction(::RaviartThomas{2,RefTriangle,1}, j, cell) return face_vertices[2] > face_vertices[1] ? 1 : -1 end +# RefTriangle, 2st order Lagrange +#= +----------------+-------------------- +Vertex numbers: | Vertex coordinates: + 2 | + | \ | v1: 𝛏 = (1.0, 0.0) + | \ | v2: 𝛏 = (0.0, 1.0) +ξ₂^ | \ | v3: 𝛏 = (0.0, 0.0) + | 3-------1 | + +--> ξ₁ | +----------------+-------------------- +Face numbers: | Face identifiers: + + | + | \ | f1: (v1, v2) + 2 1 | f2: (v2, v3) + | \ | f3: (v3, v1) + +---3---+ | +----------------+-------------------- +``` +""" +RefTriangle +=# +# https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-2.html +# Signs changed when needed to make positive direction outwards +function shape_value(ip::RaviartThomas{2,RefTriangle,2}, ξ::Vec{2}, i::Int) + x, y = ξ + # Face 1 (keep ordering, flip sign) + i == 1 && return Vec(4x*(2x-1), 2y*(4x-1)) + i == 2 && return Vec(2x*(4y-1), 4y*(2y-1)) + # Face 2 (flip ordering, keep signs) + i == 3 && return Vec( 8x*y - 2x - 6y + 2, 4y*(2y - 1)) + i == 4 && return Vec(-8x^2 - 8x*y + 12x + 6y - 4, 2y*(-4x - 4y + 3)) + # Face 3 (keep ordering, flip sign) + i == 5 && return Vec(2x*(3 - 4x - 4y), -8x*y + 6x - 8y^2 + 12y - 4) + i == 6 && return Vec(4x*(2x-1), 8x*y - 6x - 2y + 2) + # Cell + i == 7 && return Vec(8x*(-2x - y + 2), 8y*(-2x - y + 1)) + i == 8 && return Vec(8x*(-2y - x + 1), 8y*(-2y - x + 2)) + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::RaviartThomas{2,RefTriangle,2}) = 8 +facedof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = ((1, 2), (3, 4), (5, 6)) +celldof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = (7,8) +adjust_dofs_during_distribution(::RaviartThomas{2,RefTriangle,2}) = true + +function get_direction(::RaviartThomas{2,RefTriangle,2}, j, cell) + j>6 && return 1 + facenr = (j+1)÷2 + face_vertices = faces(cell)[facenr] + return face_vertices[2] > face_vertices[1] ? 1 : -1 +end + ##################################### # Nedelec (1st kind), H(curl) # From 2710cbb803d45b7066df159d31d3bc499e4bb39a Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 6 Dec 2023 15:06:09 +0100 Subject: [PATCH 119/172] Use old naming --- src/FEValues/{CellValues.jl => cell_values.jl} | 0 src/FEValues/{FaceValues.jl => face_values.jl} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/FEValues/{CellValues.jl => cell_values.jl} (100%) rename src/FEValues/{FaceValues.jl => face_values.jl} (100%) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/cell_values.jl similarity index 100% rename from src/FEValues/CellValues.jl rename to src/FEValues/cell_values.jl diff --git a/src/FEValues/FaceValues.jl b/src/FEValues/face_values.jl similarity index 100% rename from src/FEValues/FaceValues.jl rename to src/FEValues/face_values.jl From 988f91649e910758d9b9e4612ee4f3dfefe17c67 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 6 Dec 2023 15:15:18 +0100 Subject: [PATCH 120/172] Add files forgotten to save --- src/Ferrite.jl | 5 ----- test/test_cellvalues.jl | 14 -------------- 2 files changed, 19 deletions(-) diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 44e3aea7d8..78ad079feb 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -86,13 +86,8 @@ include("Quadrature/quadrature.jl") # FEValues include("FEValues/GeometryMapping.jl") include("FEValues/FunctionValues.jl") -<<<<<<< HEAD -include("FEValues/CellValues.jl") -include("FEValues/FaceValues.jl") -======= include("FEValues/cell_values.jl") include("FEValues/face_values.jl") ->>>>>>> master include("FEValues/interface_values.jl") include("PointEval/point_values.jl") diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 51db8e744c..9f66ccbbc4 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -145,30 +145,19 @@ end csv = CellValues(qr, ip) cvv = CellValues(qr, VectorizedInterpolation(ip)) csv_embedded = CellValues(qr, ip, ip^3) -<<<<<<< HEAD cv_nedelec = CellValues(qr, ip_nedelec, ip) fsv = FaceValues(qr_f, ip) fvv = FaceValues(qr_f, VectorizedInterpolation(ip)) fsv_embedded = FaceValues(qr_f, ip, ip^3) fv_nedelec = FaceValues(qr_f, ip_nedelec, ip) -======= - fsv = FaceValues(qr_f, ip) - fvv = FaceValues(qr_f, VectorizedInterpolation(ip)) - fsv_embedded = FaceValues(qr_f, ip, ip^3) - ->>>>>>> master x, n = valid_coordinates_and_normals(ip) reinit!(csv, x) reinit!(cvv, x) reinit!(fsv, x, 1) reinit!(fvv, x, 1) -<<<<<<< HEAD reinit!(cv_nedelec, cell, x) reinit!(fv_nedelec, cell, x, 1) -======= - ->>>>>>> master # Wrong number of coordinates xx = [x; x] @test_throws ArgumentError reinit!(csv, xx) @@ -185,13 +174,10 @@ end @test_throws ArgumentError reinit!(csv_embedded, x) @test_throws ArgumentError reinit!(fsv_embedded, x, 1) -<<<<<<< HEAD # Missing cell input when required @test_throws ArgumentError reinit!(cv_nedelec, x) @test_throws ArgumentError reinit!(fv_nedelec, x, 1) -======= ->>>>>>> master # Wrong number of (local) dofs # Scalar values, scalar dofs ue = rand(getnbasefunctions(csv) + 1) From 3a6289395de87b9feeaf6adf76c19053d1ae7bc0 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 6 Dec 2023 15:21:25 +0100 Subject: [PATCH 121/172] merge --- docs/make.jl | 2 + .../literate-tutorials/heat_equation_rt.jl | 337 ++++++++++++++++++ .../heat_equation_triangle.jl | 253 +++++++++++++ src/FEValues/FunctionValues.jl | 66 +++- src/FEValues/GeometryMapping.jl | 24 +- src/FEValues/common_values.jl | 3 - src/exports.jl | 2 + src/interpolations.jl | 215 ++++++++++- test/InterpolationTestUtils.jl | 116 ++++++ test/test_cellvalues.jl | 13 +- test/test_interpolations.jl | 43 +++ 11 files changed, 1047 insertions(+), 27 deletions(-) create mode 100644 docs/src/literate-tutorials/heat_equation_rt.jl create mode 100644 docs/src/literate-tutorials/heat_equation_triangle.jl create mode 100644 test/InterpolationTestUtils.jl diff --git a/docs/make.jl b/docs/make.jl index 13fc362d79..6a8635ea4f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -47,6 +47,8 @@ bibtex_plugin = CitationBibliography( "Tutorials" => [ "Tutorials overview" => "tutorials/index.md", "tutorials/heat_equation.md", + "tutorials/heat_equation_rt.md", + "tutorials/heat_equation_triangle.md", "tutorials/linear_elasticity.md", "tutorials/incompressible_elasticity.md", "tutorials/hyperelasticity.md", diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl new file mode 100644 index 0000000000..25bcb5999b --- /dev/null +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -0,0 +1,337 @@ +# # [Heat equation (Mixed, RaviartThomas)](@id tutorial-heat-equation-rt) +# +# ## Strong form +# ```math +# \nabla \cdot \boldsymbol{q} = h \in \Omega \\ +# \boldsymbol{q} = - k\ \nabla u \in \Omega \\ +# \boldsymbol{q}\cdot \boldsymbol{n} = q_n \in \Gamma_\mathrm{N}\\ +# u = u_\mathrm{D} \in \Gamma_\mathrm{D} +# ``` +# +# ## Weak form +# ### Part 1 +# ```math +# \int_{\Omega} \delta u \nabla \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ +# \int_{\Gamma} \delta u \boldsymbol{n} \cdot \boldsymbol{q}\ \mathrm{d}\Gamma - +# \int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ +# ``` +# +# ### Part 2 +# ```math +# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega = - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega +# ``` +# where no Green-Gauss theorem is applied. +# +# ### Summary +# The weak form becomes, find $u\in H^1$ and $\boldsymbol{q} \in H\mathrm{(div)}$, such that +# ```math +# \begin{align*} +# -\int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega - +# \int_{\Gamma} \delta u\ q_\mathrm{n}\ \mathrm{d}\Gamma +# \quad +# \forall\ \delta u \in \delta H^1 \\ +# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega +# \quad \forall\ \boldsymbol{\delta q} \in \delta H\mathrm{(div)} +# \end{align*} +# ``` +# +# ## Commented Program +# +# Now we solve the problem in Ferrite. What follows is a program spliced with comments. +#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 +# We start by generating a simple grid with 20x20 quadrilateral elements +# using `generate_grid`. The generator defaults to the unit square, +# so we don't need to specify the corners of the domain. +#grid = generate_grid(QuadraticTriangle, (20, 20)); +grid = generate_grid(Triangle, (20, 20)); + +# ### Trial and test functions +# A `CellValues` facilitates the process of evaluating values and gradients of +# test and trial functions (among other things). To define +# this we need to specify an interpolation space for the shape functions. +# We use Lagrange functions +# based on the two-dimensional reference quadrilateral. We also define a quadrature rule based on +# the same reference element. We combine the interpolation and the quadrature rule +# to a `CellValues` object. +ip_geo = Ferrite.default_interpolation(getcelltype(grid)) +ipu = Lagrange{RefTriangle, 1}() # Why does it "explode" for 2nd order ipu? +ipq = RaviartThomas{2,RefTriangle,1}() +qr = QuadratureRule{RefTriangle}(2) +cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) + +# ### Degrees of freedom +# Next we need to define a `DofHandler`, which will take care of numbering +# and distribution of degrees of freedom for our approximated fields. +# We create the `DofHandler` and then add a single scalar field called `:u` based on +# our interpolation `ip` defined above. +# Lastly we `close!` the `DofHandler`, it is now that the dofs are distributed +# for all the elements. +dh = DofHandler(grid) +add!(dh, :u, ipu) +add!(dh, :q, ipq) +close!(dh); + +# Now that we have distributed all our dofs we can create our tangent matrix, +# using `create_sparsity_pattern`. This function returns a sparse matrix +# with the correct entries stored. +K = create_sparsity_pattern(dh) + +# ### Boundary conditions +# In Ferrite constraints like Dirichlet boundary conditions +# are handled by a `ConstraintHandler`. +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. +∂Ω = union( + getfaceset(grid, "left"), + getfaceset(grid, "right"), + getfaceset(grid, "top"), + getfaceset(grid, "bottom"), +); + +# Now we are set up to define our constraint. We specify which field +# the condition is for, and our combined face 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 +# this case do not depend on time we define our function as $f(\textbf{x}) = 0$, i.e. +# no matter what $\textbf{x}$ we return $0$. When we have +# specified our constraint we `add!` it to `ch`. +dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) +add!(ch, dbc); + +# Finally we also need to `close!` our constraint handler. When we call `close!` +# the dofs corresponding to our constraints are calculated and stored +# in our `ch` object. +close!(ch) + +# Note that if one or more of the constraints are time dependent we would use +# [`update!`](@ref) to recompute prescribed values in each new timestep. + +# ### Assembling the linear system +# +# 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 all the elements in order to +# compute the element contributions ``K_e`` and ``f_e``, which are then assembled to the +# appropriate place in the global ``K`` and ``f``. +# +# #### Element assembly +# We define the function `assemble_element!` (see below) which computes the contribution for +# an element. The function takes pre-allocated `ke` and `fe` (they are allocated once and +# then reused for all elements) so we first need to make sure that they are all zeroes at +# the start of the function by using `fill!`. Then we loop over all the quadrature points, +# and for each quadrature point we loop over all the (local) shape functions. We need the +# value and gradient of the test function, `δu` and also the gradient of the trial function +# `u`. We get all of these from `cellvalues`. +# +# !!! note "Notation" +# Comparing with the brief finite element introduction in [Introduction to FEM](@ref), +# the variables `δu`, `∇δu` and `∇u` are actually $\phi_i(\textbf{x}_q)$, $\nabla +# \phi_i(\textbf{x}_q)$ and $\nabla \phi_j(\textbf{x}_q)$, i.e. the evaluation of the +# trial and test functions in the quadrature point ``\textbf{x}_q``. However, to +# underline the strong parallel between the weak form and the implementation, this +# example uses the symbols appearing in the weak form. + +function assemble_element!(Ke::Matrix, fe::Vector, cv::NamedTuple, dr::NamedTuple) + cvu = cv[:u] + cvq = cv[:q] + dru = dr[:u] + drq = dr[:q] + ## Loop over quadrature points + for q_point in 1:getnquadpoints(cvu) + ## Get the quadrature weight + dΩ = getdetJdV(cvu, q_point) + ## Loop over test shape functions + for (iu, Iu) in pairs(dru) + δu = shape_value(cvu, q_point, iu) + ∇δu = shape_gradient(cvu, q_point, iu) + ## Add contribution to fe + fe[Iu] -= δu * dΩ + ## Loop over trial shape functions + for (jq, Jq) in pairs(drq) + q = shape_value(cvq, q_point, jq) + ## Add contribution to Ke + Ke[Iu, Jq] += (∇δu ⋅ q) * dΩ + end + end + for (iq, Iq) in pairs(drq) + δq = shape_value(cvq, q_point, iq) + for (ju, Ju) in pairs(dru) + ∇u = shape_gradient(cvu, q_point, ju) + Ke[Iq, Ju] += (δq ⋅ ∇u) * dΩ + end + for (jq, Jq) in pairs(drq) + q = shape_value(cvq, q_point, jq) + Ke[Iq, Jq] += (δq ⋅ q) * dΩ + end + end + end + return Ke, fe +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 our `cellvalues`, the sparse matrix `K`, and our DofHandler +# as input arguments and returns the assembled global stiffness matrix, and the assembled +# global force vector. We start by allocating `Ke`, `fe`, and the global force vector `f`. +# We also create an assembler by using `start_assemble`. The assembler lets us assemble into +# `K` and `f` efficiently. We then start the loop over all the elements. In each loop +# iteration we reinitialize `cellvalues` (to update derivatives of shape functions etc.), +# compute the element contribution with `assemble_element!`, and then assemble into the +# global `K` and `f` with `assemble!`. +# +# !!! note "Notation" +# Comparing again with [Introduction to FEM](@ref), `f` and `u` correspond to +# $\underline{\hat{f}}$ and $\underline{\hat{u}}$, since they represent the discretized +# versions. However, through the code we use `f` and `u` instead to reflect the strong +# connection between the weak form and the Ferrite implementation. + +function assemble_global(cellvalues, K::SparseMatrixCSC, dh::DofHandler) + grid = dh.grid + ## Allocate the element stiffness matrix and element force vector + dofranges = (u = dof_range(dh, :u), q = dof_range(dh, :q)) + ncelldofs = ndofs_per_cell(dh) + Ke = zeros(ncelldofs, ncelldofs) + fe = zeros(ncelldofs) + ## Allocate global force vector f + f = zeros(ndofs(dh)) + ## Create an assembler + assembler = start_assemble(K, f) + x = copy(getcoordinates(grid, 1)) + dofs = copy(celldofs(dh, 1)) + ## Loop over all cels + for cellnr in 1:getncells(grid) + ## Reinitialize cellvalues for this cell + cell = getcells(grid, cellnr) + getcoordinates!(x, grid, cell) + celldofs!(dofs, dh, cellnr) + reinit!(cellvalues[:u], cell, x) + reinit!(cellvalues[:q], cell, x) + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues, dofranges) + ## Assemble Ke and fe into K and f + assemble!(assembler, dofs, Ke, fe) + end + return K, f +end +#md nothing # hide + +# ### Solution of the system +# The last step is to solve the system. First we call `assemble_global` +# to obtain the global stiffness matrix `K` and force vector `f`. +K, f = assemble_global(cellvalues, K, dh); + +# To account for the boundary conditions we use the `apply!` function. +# This modifies elements in `K` and `f` respectively, such that +# we can get the correct solution vector `u` by using `\`. +apply!(K, f, ch) +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/). +u_nodes = evaluate_at_grid_nodes(dh, u, :u) +∂Ω_cells = zeros(Int, getncells(grid)) +for (cellnr, facenr) in ∂Ω + ∂Ω_cells[cellnr] = 1 +end +vtk_grid("heat_equation_rt", dh) do vtk + vtk_point_data(vtk, u_nodes, "u") + vtk_cell_data(vtk, ∂Ω_cells, "dO") +end + +@show norm(u_nodes)/length(u_nodes) + +# ## Postprocess the total flux +function calculate_flux(dh, dΩ, ip, a) + grid = dh.grid + qr = FaceQuadratureRule{RefTriangle}(4) + ip_geo = Ferrite.default_interpolation(getcelltype(grid)) + fv = FaceValues(qr, ip, ip_geo) + + dofrange = dof_range(dh, :q) + flux = 0.0 + dofs = celldofs(dh, 1) + ae = zeros(length(dofs)) + x = getcoordinates(grid, 1) + for (cellnr, facenr) in dΩ + getcoordinates!(x, grid, cellnr) + cell = getcells(grid, cellnr) + celldofs!(dofs, dh, cellnr) + map!(i->a[i], ae, dofs) + reinit!(fv, cell, x, facenr) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + q = function_value(fv, q_point, ae, dofrange) + flux += (q ⋅ n)*dΓ + end + end + return flux +end + +function calculate_flux_lag(dh, dΩ, ip, a) + grid = dh.grid + qr = FaceQuadratureRule{RefTriangle}(4) + ip_geo = Ferrite.default_interpolation(getcelltype(grid)) + fv = FaceValues(qr, ip, ip_geo) + dofrange = dof_range(dh, :u) + flux = 0.0 + dofs = celldofs(dh, 1) + ae = zeros(length(dofs)) + x = getcoordinates(grid, 1) + for (cellnr, facenr) in dΩ + getcoordinates!(x, grid, cellnr) + cell = getcells(grid, cellnr) + celldofs!(dofs, dh, cellnr) + map!(i->a[i], ae, dofs) + reinit!(fv, cell, x, facenr) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + q = function_gradient(fv, q_point, ae, dofrange) + flux -= (q ⋅ n)*dΓ + end + end + return flux +end + +flux = calculate_flux(dh, ∂Ω, ipq, u) +flux_lag = calculate_flux_lag(dh, ∂Ω, ipu, u) +@show flux, flux_lag + + +function get_Ke(dh, cellvalues; cellnr=1) + dofranges = (u = dof_range(dh, :u), q = dof_range(dh, :q)) + ncelldofs = ndofs_per_cell(dh) + Ke = zeros(ncelldofs, ncelldofs) + fe = zeros(ncelldofs) + x = getcoordinates(grid, cellnr) + cell = getcells(grid, cellnr) + reinit!(cellvalues[:u], cell, x) + reinit!(cellvalues[:q], cell, x) + + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues, dofranges) + return Ke +end +#md # ## [Plain program](@id heat_equation-plain-program) +#md # +#md # Here follows a version of the program without any comments. +#md # The file is also available here: [`heat_equation.jl`](heat_equation.jl). +#md # +#md # ```julia +#md # @__CODE__ +#md # ``` diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl new file mode 100644 index 0000000000..414a7f9195 --- /dev/null +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -0,0 +1,253 @@ +# # [Heat equation (Triangle)](@id tutorial-heat-equation-triangle) +# +# ![](heat_square.png) +# +# *Figure 1*: Temperature field on the unit square with an internal uniform heat source +# solved with homogeneous Dirichlet boundary conditions on the boundary. +# +# ## Introduction +# +# The heat equation is the "Hello, world!" equation of finite elements. +# Here we solve the equation on a unit square, with a uniform internal source. +# The strong form of the (linear) heat equation is given by +# +# ```math +# -\nabla \cdot (k \nabla u) = f \quad \textbf{x} \in \Omega, +# ``` +# +# where $u$ is the unknown temperature field, $k$ the heat conductivity, +# $f$ the heat source and $\Omega$ the domain. For simplicity we set $f = 1$ +# and $k = 1$. We will consider homogeneous Dirichlet boundary conditions such that +# ```math +# u(\textbf{x}) = 0 \quad \textbf{x} \in \partial \Omega, +# ``` +# where $\partial \Omega$ denotes the boundary of $\Omega$. +# The resulting weak form is given given as follows: Find ``u \in \mathbb{U}`` such that +# ```math +# \int_{\Omega} \nabla \delta u \cdot \nabla u \ d\Omega = \int_{\Omega} \delta u \ d\Omega \quad \forall \delta u \in \mathbb{T}, +# ``` +# where $\delta u$ is a test function, and where $\mathbb{U}$ and $\mathbb{T}$ are suitable +# trial and test function sets, respectively. +#- +# ## Commented Program +# +# Now we solve the problem in Ferrite. What follows is a program spliced with comments. +#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 +# We start by generating a simple grid with 20x20 quadrilateral elements +# using `generate_grid`. The generator defaults to the unit square, +# so we don't need to specify the corners of the domain. +grid = generate_grid(Triangle, (20, 20)); + +# ### Trial and test functions +# A `CellValues` facilitates the process of evaluating values and gradients of +# test and trial functions (among other things). To define +# this we need to specify an interpolation space for the shape functions. +# We use Lagrange functions +# based on the two-dimensional reference quadrilateral. We also define a quadrature rule based on +# the same reference element. We combine the interpolation and the quadrature rule +# to a `CellValues` object. +ip = Lagrange{RefTriangle, 1}() +qr = QuadratureRule{RefTriangle}(2) +cellvalues = CellValues(qr, ip); + +# ### Degrees of freedom +# Next we need to define a `DofHandler`, which will take care of numbering +# and distribution of degrees of freedom for our approximated fields. +# We create the `DofHandler` and then add a single scalar field called `:u` based on +# our interpolation `ip` defined above. +# Lastly we `close!` the `DofHandler`, it is now that the dofs are distributed +# for all the elements. +dh = DofHandler(grid) +add!(dh, :u, ip) +close!(dh); + +# Now that we have distributed all our dofs we can create our tangent matrix, +# using `create_sparsity_pattern`. This function returns a sparse matrix +# with the correct entries stored. +K = create_sparsity_pattern(dh) + +# ### Boundary conditions +# In Ferrite constraints like Dirichlet boundary conditions +# are handled by a `ConstraintHandler`. +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. +∂Ω = union( + getfaceset(grid, "left"), + getfaceset(grid, "right"), + getfaceset(grid, "top"), + getfaceset(grid, "bottom"), +); + +# Now we are set up to define our constraint. We specify which field +# the condition is for, and our combined face 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 +# this case do not depend on time we define our function as $f(\textbf{x}) = 0$, i.e. +# no matter what $\textbf{x}$ we return $0$. When we have +# specified our constraint we `add!` it to `ch`. +dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) +add!(ch, dbc); + +# Finally we also need to `close!` our constraint handler. When we call `close!` +# the dofs corresponding to our constraints are calculated and stored +# in our `ch` object. +close!(ch) + +# Note that if one or more of the constraints are time dependent we would use +# [`update!`](@ref) to recompute prescribed values in each new timestep. + +# ### Assembling the linear system +# +# 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 all the elements in order to +# compute the element contributions ``K_e`` and ``f_e``, which are then assembled to the +# appropriate place in the global ``K`` and ``f``. +# +# #### Element assembly +# We define the function `assemble_element!` (see below) which computes the contribution for +# an element. The function takes pre-allocated `ke` and `fe` (they are allocated once and +# then reused for all elements) so we first need to make sure that they are all zeroes at +# the start of the function by using `fill!`. Then we loop over all the quadrature points, +# and for each quadrature point we loop over all the (local) shape functions. We need the +# value and gradient of the test function, `δu` and also the gradient of the trial function +# `u`. We get all of these from `cellvalues`. +# +# !!! note "Notation" +# Comparing with the brief finite element introduction in [Introduction to FEM](@ref), +# the variables `δu`, `∇δu` and `∇u` are actually $\phi_i(\textbf{x}_q)$, $\nabla +# \phi_i(\textbf{x}_q)$ and $\nabla \phi_j(\textbf{x}_q)$, i.e. the evaluation of the +# trial and test functions in the quadrature point ``\textbf{x}_q``. However, to +# underline the strong parallel between the weak form and the implementation, this +# example uses the symbols appearing in the weak form. + +function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues) + n_basefuncs = getnbasefunctions(cellvalues) + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Loop over quadrature points + for q_point in 1:getnquadpoints(cellvalues) + ## Get the quadrature weight + dΩ = getdetJdV(cellvalues, q_point) + ## Loop over test shape functions + for i in 1:n_basefuncs + δu = shape_value(cellvalues, q_point, i) + ∇δu = shape_gradient(cellvalues, q_point, i) + ## Add contribution to fe + fe[i] += δu * dΩ + ## Loop over trial shape functions + for j in 1:n_basefuncs + ∇u = shape_gradient(cellvalues, q_point, j) + ## Add contribution to Ke + Ke[i, j] += (∇δu ⋅ ∇u) * dΩ + end + end + end + return Ke, fe +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 our `cellvalues`, the sparse matrix `K`, and our DofHandler +# as input arguments and returns the assembled global stiffness matrix, and the assembled +# global force vector. We start by allocating `Ke`, `fe`, and the global force vector `f`. +# We also create an assembler by using `start_assemble`. The assembler lets us assemble into +# `K` and `f` efficiently. We then start the loop over all the elements. In each loop +# iteration we reinitialize `cellvalues` (to update derivatives of shape functions etc.), +# compute the element contribution with `assemble_element!`, and then assemble into the +# global `K` and `f` with `assemble!`. +# +# !!! note "Notation" +# Comparing again with [Introduction to FEM](@ref), `f` and `u` correspond to +# $\underline{\hat{f}}$ and $\underline{\hat{u}}$, since they represent the discretized +# versions. However, through the code we use `f` and `u` instead to reflect the strong +# connection between the weak form and the Ferrite implementation. + +function assemble_global(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHandler) + ## Allocate the element stiffness matrix and element force vector + n_basefuncs = getnbasefunctions(cellvalues) + Ke = zeros(n_basefuncs, n_basefuncs) + fe = zeros(n_basefuncs) + ## Allocate global force vector f + f = zeros(ndofs(dh)) + ## Create an assembler + assembler = start_assemble(K, f) + ## Loop over all cels + for cell in CellIterator(dh) + ## Reinitialize cellvalues for this cell + reinit!(cellvalues, cell) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues) + ## Assemble Ke and fe into K and f + assemble!(assembler, celldofs(cell), Ke, fe) + end + return K, f +end +#md nothing # hide + +# ### Solution of the system +# The last step is to solve the system. First we call `assemble_global` +# to obtain the global stiffness matrix `K` and force vector `f`. +K, f = assemble_global(cellvalues, K, dh); + +# To account for the boundary conditions we use the `apply!` function. +# This modifies elements in `K` and `f` respectively, such that +# we can get the correct solution vector `u` by using `\`. +apply!(K, f, ch) +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/). +vtk_grid("heat_equation", dh) do vtk + vtk_point_data(vtk, dh, u) +end + +@show norm(u)/length(u) + +# ### Postprocessing the total flux + +function calculate_flux_lag(dh, dΩ, ip, a) + qr = FaceQuadratureRule{RefTriangle}(2) + fv = FaceValues(qr, ip, Lagrange{RefTriangle,1}()) + grid = dh.grid + dofrange = dof_range(dh, :u) + flux = 0.0 + dofs = celldofs(dh, 1) + ae = zeros(length(dofs)) + x = getcoordinates(grid, 1) + for (cellnr, facenr) in dΩ + getcoordinates!(x, grid, cellnr) + cell = getcells(grid, cellnr) + celldofs!(dofs, dh, cellnr) + map!(i->a[i], ae, dofs) + reinit!(fv, cell, x, facenr) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + q = function_gradient(fv, q_point, ae, dofrange) + flux -= (q ⋅ n)*dΓ + end + end + return flux +end + +flux = calculate_flux_lag(dh, ∂Ω, ip, u) +@show flux + +#md # ## [Plain program](@id heat_equation-plain-program) +#md # +#md # Here follows a version of the program without any comments. +#md # The file is also available here: [`heat_equation.jl`](heat_equation.jl). +#md # +#md # ```julia +#md # @__CODE__ +#md # ``` diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index 7250116892..c45a50df61 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -117,9 +117,9 @@ end # Mapping types struct IdentityMapping end +struct CovariantPiolaMapping end +struct ContravariantPiolaMapping end # Not yet implemented: -# struct CovariantPiolaMapping end # PR798 -# struct ContravariantPiolaMapping end # PR798 # struct DoubleCovariantPiolaMapping end # struct DoubleContravariantPiolaMapping end @@ -133,9 +133,8 @@ the function values and gradients from the reference cell to the physical cell geometry. """ required_geo_diff_order(::IdentityMapping, fun_diff_order::Int) = fun_diff_order -#required_geo_diff_order(::ContravariantPiolaMapping, fun_diff_order::Int) = 1 + fun_diff_order # PR798 -#required_geo_diff_order(::CovariantPiolaMapping, fun_diff_order::Int) = 1 + fun_diff_order # PR798 - +required_geo_diff_order(::ContravariantPiolaMapping, fun_diff_order::Int) = 1 + fun_diff_order +required_geo_diff_order(::CovariantPiolaMapping, fun_diff_order::Int) = 1 + fun_diff_order # Support for embedded elements @inline calculate_Jinv(J::Tensor{2}) = inv(J) @@ -172,6 +171,57 @@ end return nothing end -# TODO in PR798, apply_mapping! for -# * CovariantPiolaMapping -# * ContravariantPiolaMapping +# Covariant Piola Mapping +@inline function apply_mapping!(funvals::FunctionValues{0}, ::CovariantPiolaMapping, q_point::Int, mapping_values, cell) + Jinv = inv(getjacobian(mapping_values)) + @inbounds for j in 1:getnbasefunctions(funvals) + d = get_direction(funvals.ip, j, cell) + Nξ = funvals.Nξ[j, q_point] + funvals.Nx[j, q_point] = d*(Nξ ⋅ Jinv) + end + return nothing +end + +@inline function apply_mapping!(funvals::FunctionValues{1}, ::CovariantPiolaMapping, q_point::Int, mapping_values, cell) + H = gethessian(mapping_values) + Jinv = inv(getjacobian(mapping_values)) + @inbounds for j in 1:getnbasefunctions(funvals) + d = get_direction(funvals.ip, j, cell) + dNdξ = funvals.dNdξ[j, q_point] + Nξ = funvals.Nξ[j, q_point] + funvals.Nx[j, q_point] = d*(Nξ ⋅ Jinv) + funvals.dNdx[j, q_point] = d*(Jinv' ⋅ dNdξ ⋅ Jinv - Jinv' ⋅ (Nξ ⋅ Jinv ⋅ H ⋅ Jinv)) + end + return nothing +end + +# Contravariant Piola Mapping +@inline function apply_mapping!(funvals::FunctionValues{0}, ::ContravariantPiolaMapping, q_point::Int, mapping_values, cell) + J = getjacobian(mapping_values) + detJ = det(J) + @inbounds for j in 1:getnbasefunctions(funvals) + d = get_direction(funvals.ip, j, cell) + Nξ = funvals.Nξ[j, q_point] + funvals.Nx[j, q_point] = d*(J ⋅ Nξ)/detJ + end + return nothing +end + +@inline function apply_mapping!(funvals::FunctionValues{1}, ::ContravariantPiolaMapping, q_point::Int, mapping_values, cell) + H = gethessian(mapping_values) + J = getjacobian(mapping_values) + Jinv = inv(J) + detJ = det(J) + I2 = one(J) + H_Jinv = H⋅Jinv + A1 = (H_Jinv ⊡ (otimesl(I2,I2))) / detJ + A2 = (Jinv' ⊡ H_Jinv) / detJ + @inbounds for j in 1:getnbasefunctions(funvals) + d = get_direction(funvals.ip, j, cell) + dNdξ = funvals.dNdξ[j, q_point] + Nξ = funvals.Nξ[j, q_point] + funvals.Nx[j, q_point] = d*(J ⋅ Nξ)/detJ + funvals.dNdx[j, q_point] = d*(J ⋅ dNdξ ⋅ Jinv/detJ + A1 ⋅ Nξ - (J ⋅ Nξ) ⊗ A2) + end + return nothing +end diff --git a/src/FEValues/GeometryMapping.jl b/src/FEValues/GeometryMapping.jl index b6346b7550..682d56a114 100644 --- a/src/FEValues/GeometryMapping.jl +++ b/src/FEValues/GeometryMapping.jl @@ -12,10 +12,8 @@ struct MappingValues{JT, HT} J::JT # dx/dξ # Jacobian H::HT # dJ/dξ # Hessian end - @inline getjacobian(mv::MappingValues{<:Union{AbstractTensor, SMatrix}}) = mv.J -# @inline gethessian(mv::MappingValues{<:Any,<:AbstractTensor}) = mv.H # PR798 - +@inline gethessian(mv::MappingValues{<:Any,<:AbstractTensor}) = mv.H """ GeometryMapping{DiffOrder}(::Type{T}, ip_geo, qr::QuadratureRule) @@ -46,12 +44,12 @@ struct GeometryMapping{DiffOrder, IP, M_t, dMdξ_t, d2Mdξ2_t} ) where {IP <: ScalarInterpolation, M_t<:AbstractMatrix{<:Number}, dMdξ_t <: AbstractMatrix{<:Vec}} return new{1, IP, M_t, dMdξ_t, Nothing}(ip, M, dMdξ, nothing) end -#= function GeometryMapping( + function GeometryMapping( ip::IP, M::M_t, dMdξ::dMdξ_t, d2Mdξ2::d2Mdξ2_t) where {IP <: ScalarInterpolation, M_t<:AbstractMatrix{<:Number}, dMdξ_t <: AbstractMatrix{<:Vec}, d2Mdξ2_t <: AbstractMatrix{<:Tensor{2}}} return new{2, IP, M_t, dMdξ_t, d2Mdξ2_t}(ip, M, dMdξ, d2Mdξ2) - end =# # PR798 + end end function GeometryMapping{0}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T n_shape = getnbasefunctions(ip) @@ -71,7 +69,7 @@ function GeometryMapping{1}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRu precompute_values!(gm, getpoints(qr)) return gm end -#= function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T +function GeometryMapping{2}(::Type{T}, ip::ScalarInterpolation, qr::QuadratureRule) where T n_shape = getnbasefunctions(ip) n_qpoints = getnquadpoints(qr) @@ -82,7 +80,7 @@ end gm = GeometryMapping(ip, M, dMdξ, d2Mdξ2) precompute_values!(gm, getpoints(qr)) return gm -end =# # PR798 +end function precompute_values!(gm::GeometryMapping{0}, qr_points::Vector{<:Vec}) shape_values!(gm.M, gm.ip, qr_points) @@ -90,9 +88,9 @@ end function precompute_values!(gm::GeometryMapping{1}, qr_points::Vector{<:Vec}) 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::Vector{<:Vec}) shape_hessians_gradients_and_values!(gm.d2Mdξ2, gm.dMdξ, gm.M, gm.ip, qr_points) -end =# # PR798 +end function Base.copy(v::GeometryMapping) return GeometryMapping(copy(v.ip), copy(v.M), _copy_or_nothing(v.dMdξ), _copy_or_nothing(v.d2Mdξ2)) @@ -117,9 +115,9 @@ end function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(dMdξ)=#::Type{<:Vec{dim,TM}}) where {dim, Tx, TM} return Tensor{2,dim,promote_type(Tx,TM)} end -#= function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(d2Mdξ2)=#::Type{<:Tensor{2,dim,TM}}) where {dim, Tx, TM} +function otimes_returntype(#=typeof(x)=#::Type{<:Vec{dim,Tx}}, #=typeof(d2Mdξ2)=#::Type{<:Tensor{2,dim,TM}}) where {dim, Tx, TM} return Tensor{3,dim,promote_type(Tx,TM)} -end =# # PR798 +end @inline function calculate_mapping(::GeometryMapping{0}, q_point, x) return MappingValues(nothing, nothing) @@ -134,7 +132,7 @@ end return MappingValues(fecv_J, nothing) end -#= @inline function calculate_mapping(geo_mapping::GeometryMapping{2}, q_point, x) +@inline function calculate_mapping(geo_mapping::GeometryMapping{2}, q_point, x) J = zero(otimes_returntype(eltype(x), eltype(geo_mapping.dMdξ))) H = zero(otimes_returntype(eltype(x), eltype(geo_mapping.d2Mdξ2))) @inbounds for j in 1:getngeobasefunctions(geo_mapping) @@ -142,7 +140,7 @@ end H += x[j] ⊗ geo_mapping.d2Mdξ2[j, q_point] end return MappingValues(J, H) -end =# # PR798 +end calculate_detJ(J::Tensor{2}) = det(J) calculate_detJ(J::SMatrix) = embedding_det(J) diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 181e54b03c..dad7f8b50c 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -302,11 +302,8 @@ function shape_gradients_and_values!(gradients::AbstractMatrix, values::Abstract end end -#= PR798 function shape_hessians_gradients_and_values!(hessians::AbstractMatrix, gradients::AbstractMatrix, values::AbstractMatrix, ip, qr_points::Vector{<:Vec}) for (qp, ξ) in pairs(qr_points) shape_hessians_gradients_and_values!(@view(hessians[:, qp]), @view(gradients[:, qp]), @view(values[:, qp]), ip, ξ) end end -=# - diff --git a/src/exports.jl b/src/exports.jl index dab7e368fb..ae96f39c71 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -16,6 +16,8 @@ export Lagrange, DiscontinuousLagrange, Serendipity, + Nedelec, + RaviartThomas, getnbasefunctions, getrefshape, diff --git a/src/interpolations.jl b/src/interpolations.jl index 0d74256e44..2e4893848c 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -246,6 +246,21 @@ and store them in `hessians`, `gradients`, and `values`. end =# +""" + shape_hessians_gradients_and_values!(hessians::AbstractVector, gradients::AbstractVector, values::AbstractVector, ip::Interpolation, ξ::Vec) + +Evaluate all shape function hessians, gradients and values of `ip` at once at the reference point `ξ` +and store them in `hessians`, `gradients`, and `values`. +""" +@propagate_inbounds function shape_hessians_gradients_and_values!(hessians::AbstractVector, gradients::AbstractVector, values::AbstractVector, ip::Interpolation, ξ::Vec) + @boundscheck checkbounds(hessians, 1:getnbasefunctions(ip)) + @boundscheck checkbounds(gradients, 1:getnbasefunctions(ip)) + @boundscheck checkbounds(values, 1:getnbasefunctions(ip)) + @inbounds for i in 1:getnbasefunctions(ip) + hessians[i], gradients[i], values[i] = shape_hessian_gradient_and_value(ip, ξ, i) + end +end + """ shape_value(ip::Interpolation, ξ::Vec, i::Int) @@ -291,6 +306,16 @@ function shape_hessian_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int) end =# +""" + shape_hessian_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int) + +Optimized version combining the evaluation [`Ferrite.shape_value(::Interpolation)`](@ref), +[`Ferrite.shape_gradient(::Interpolation)`](@ref), and the gradient of the latter. +""" +function shape_hessian_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int) + return hessian(x -> shape_value(ip, x, i), ξ, :all) +end + """ reference_coordinates(ip::Interpolation) @@ -1560,7 +1585,6 @@ function shape_gradient_and_value(ipv::VectorizedInterpolation{vdim, shape}, ξ: end reference_coordinates(ip::VectorizedInterpolation) = reference_coordinates(ip.ip) - is_discontinuous(::Type{<:VectorizedInterpolation{<:Any, <:Any, <:Any, ip}}) where {ip} = is_discontinuous(ip) """ @@ -1575,3 +1599,192 @@ function mapping_type end mapping_type(::ScalarInterpolation) = IdentityMapping() mapping_type(::VectorizedInterpolation) = IdentityMapping() + + +##################################### +# RaviartThomas (1st kind), H(div) # +##################################### +# https://defelement.com/elements/raviart-thomas.html +# https://defelement.com/elements/qdiv.html +struct RaviartThomas{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end +mapping_type(::RaviartThomas) = ContravariantPiolaMapping() +n_dbc_components(::RaviartThomas) = 1 + +# RefTriangle, 1st order Lagrange +# https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-1.html +# Signs changed when needed to make positive direction outwards +function shape_value(ip::RaviartThomas{2,RefTriangle,1}, ξ::Vec{2}, i::Int) + x, y = ξ + i == 1 && return ξ # Flip sign + i == 2 && return Vec(x-1, y) # Keep sign + i == 3 && return Vec(x, y - 1) # Flip sign + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::RaviartThomas{2,RefTriangle,1}) = 3 +facedof_interior_indices(::RaviartThomas{2,RefTriangle,1}) = ((1,), (2,), (3,)) +adjust_dofs_during_distribution(::RaviartThomas) = false + +function get_direction(::RaviartThomas{2,RefTriangle,1}, j, cell) + face_vertices = faces(cell)[j] + return face_vertices[2] > face_vertices[1] ? 1 : -1 +end + +# RefTriangle, 2st order Lagrange +#= +----------------+-------------------- +Vertex numbers: | Vertex coordinates: + 2 | + | \ | v1: 𝛏 = (1.0, 0.0) + | \ | v2: 𝛏 = (0.0, 1.0) +ξ₂^ | \ | v3: 𝛏 = (0.0, 0.0) + | 3-------1 | + +--> ξ₁ | +----------------+-------------------- +Face numbers: | Face identifiers: + + | + | \ | f1: (v1, v2) + 2 1 | f2: (v2, v3) + | \ | f3: (v3, v1) + +---3---+ | +----------------+-------------------- +``` +""" +RefTriangle +=# +# https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-2.html +# Signs changed when needed to make positive direction outwards +function shape_value(ip::RaviartThomas{2,RefTriangle,2}, ξ::Vec{2}, i::Int) + x, y = ξ + # Face 1 (keep ordering, flip sign) + i == 1 && return Vec(4x*(2x-1), 2y*(4x-1)) + i == 2 && return Vec(2x*(4y-1), 4y*(2y-1)) + # Face 2 (flip ordering, keep signs) + i == 3 && return Vec( 8x*y - 2x - 6y + 2, 4y*(2y - 1)) + i == 4 && return Vec(-8x^2 - 8x*y + 12x + 6y - 4, 2y*(-4x - 4y + 3)) + # Face 3 (keep ordering, flip sign) + i == 5 && return Vec(2x*(3 - 4x - 4y), -8x*y + 6x - 8y^2 + 12y - 4) + i == 6 && return Vec(4x*(2x-1), 8x*y - 6x - 2y + 2) + # Cell + i == 7 && return Vec(8x*(-2x - y + 2), 8y*(-2x - y + 1)) + i == 8 && return Vec(8x*(-2y - x + 1), 8y*(-2y - x + 2)) + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::RaviartThomas{2,RefTriangle,2}) = 8 +facedof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = ((1, 2), (3, 4), (5, 6)) +celldof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = (7,8) +adjust_dofs_during_distribution(::RaviartThomas{2,RefTriangle,2}) = true + +function get_direction(::RaviartThomas{2,RefTriangle,2}, j, cell) + j>6 && return 1 + facenr = (j+1)÷2 + face_vertices = faces(cell)[facenr] + return face_vertices[2] > face_vertices[1] ? 1 : -1 +end + + +##################################### +# Nedelec (1st kind), H(curl) # +##################################### +struct Nedelec{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end +mapping_type(::Nedelec) = CovariantPiolaMapping() +reference_coordinates(ip::Nedelec{vdim}) where vdim = fill(NaN*zero(Vec{vdim}), getnbasefunctions(ip)) +dirichlet_facedof_indices(ip::Nedelec) = facedof_interior_indices(ip) +n_dbc_components(::Nedelec) = 1 +# RefTriangle, 1st order Lagrange +# https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html +function shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) + x, y = ξ + i == 1 && return Vec( - y, x) + i == 2 && return Vec( - y, x - 1) # Changed signed, follow Ferrite's sign convention + i == 3 && return Vec(1 - y, x) + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::Nedelec{2,RefTriangle,1}) = 3 +facedof_interior_indices(::Nedelec{2,RefTriangle,1}) = ((1,), (2,), (3,)) +adjust_dofs_during_distribution(::Nedelec{2,RefTriangle,1}) = false + +function get_direction(::Nedelec{2,RefTriangle,1}, j, cell) + face_vertices = faces(cell)[j] + return face_vertices[2] > face_vertices[1] ? 1 : -1 +end + +# RefTriangle, 2nd order Lagrange +# https://defelement.com/elements/examples/triangle-nedelec1-lagrange-2.html +function shape_value(ip::Nedelec{2,RefTriangle,2}, ξ::Vec{2}, i::Int) + x, y = ξ + # Face 1 + i == 1 && return Vec( 2*y*(1 - 4*x), + 4*x*(2*x - 1)) + i == 2 && return Vec( 4*y*(1 - 2*y), + 2*x*(4*y - 1)) + # Face 2 (flip order and sign compared to defelement) + i == 3 && return Vec( 4*y*(1 - 2*y), + 8*x*y - 2*x - 6*y + 2) + i == 4 && return Vec( 2*y*(4*x + 4*y - 3), + -8*x^2 - 8*x*y + 12*x + 6*y - 4) + # Face 3 + i == 5 && return Vec( 8*x*y - 6*x + 8*y^2 - 12*y + 4, + 2*x*(-4*x - 4*y + 3)) + i == 6 && return Vec(-8*x*y + 6*x + 2*y - 2, + 4*x*(2*x - 1)) + # Cell + i == 7 && return Vec( 8*y*(-x - 2*y + 2), + 8*x*( x + 2*y - 1)) + i == 8 && return Vec( 8*y*( 2*x + y - 1), + 8*x*(-2*x - y + 2)) + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::Nedelec{2,RefTriangle,2}) = 8 +facedof_interior_indices(::Nedelec{2,RefTriangle,2}) = ((1,2), (3,4), (5,6)) +celldof_interior_indices(::Nedelec{2,RefTriangle,2}) = (7,8) +adjust_dofs_during_distribution(::Nedelec{2,RefTriangle,2}) = true + +function get_direction(::Nedelec{2,RefTriangle,2}, j, cell) + j>6 && return 1 + facenr = (j+1)÷2 + face_vertices = faces(cell)[facenr] + return face_vertices[2] > face_vertices[1] ? 1 : -1 +end + +# RefTetrahedron, 1st order Lagrange - 𝐍𝐎𝐓 𝐓𝐄𝐒𝐓𝐄𝐃 +# https://defelement.com/elements/examples/tetrahedron-nedelec1-lagrange-1.html +function shape_value(ip::Nedelec{3,RefTetrahedron,1}, ξ::Vec{3,T}, i::Int) where T + x, y, z = ξ + # Edge 1 (defelement 5, positive) + i == 1 && return Vec(- y - z + 1, x, x) + # Edge 2 (defelement 2, positive) + i == 2 && return Vec(-y, x, zero(T)) + # Edge 3 (defelement 4, negative) + i == 3 && return Vec(-y, x + z - 1, -y) + # Edge 4 (defelement 3, positive) + i == 4 && return Vec(z, z, -x - y + 1) + # Edge 5 (defelement 1, positive) + i == 5 && return Vec(-z, zero(T), x) + # Edge 6 (defelement 0, positive) + i == 6 && return Vec(zero(T), -z, y) + + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::Nedelec{3,RefTetrahedron,1}) = 6 +edgedof_interior_indices(::Nedelec{3,RefTetrahedron,1}) = ntuple(i->(i,), 6) +adjust_dofs_during_distribution(::Nedelec{3,RefTetrahedron,1}) = false + +function get_direction(::Nedelec{3,RefTetrahedron,1}, j, cell) + edge_vertices = edges(cell)[j] + return edge_vertices[2] > edge_vertices[1] ? 1 : -1 +end + +# RefTetrahedron, 2nd order Lagrange +# https://defelement.com/elements/examples/tetrahedron-nedelec1-lagrange-2.html +#= Notes +The dofs belong to faces seem to require extra consideration, but not sure. +Basically, we need to make sure they are aligned with the same edges that they refer to. +It might be that this works automatically, following `adjust_dofs_during_distribution`, +but test cases need to be developed first to make sure. +No point in implementing guesses that cannot be verified... +=# diff --git a/test/InterpolationTestUtils.jl b/test/InterpolationTestUtils.jl new file mode 100644 index 0000000000..563241e042 --- /dev/null +++ b/test/InterpolationTestUtils.jl @@ -0,0 +1,116 @@ +module InterpolationTestUtils + using Ferrite + using Test + import LinearAlgebra: normalize + import Random: randperm + + function find_matching_face(grid, face::FaceIndex) + cell, facenr = face + face_vertices = Set(Ferrite.faces(getcells(grid, cell))[facenr]) + for cnr in 1:getncells(grid) + cnr == cell && continue + for (i, f_vert) in enumerate(Ferrite.faces(getcells(grid, cnr))) + face_vertices == Set(f_vert) && return FaceIndex(cnr, i) + end + end + return nothing + end + + function test_continuity(dh::DofHandler, face::FaceIndex; + transformation_function::Function=identity, + value_function::Function=function_value) + # transformation_function: (v,n) -> z + # Examples + # * Tangential continuity: fun(v, n) = v - (v ⋅ n)*n + # * Normal continuity: fun(v, n) = v ⋅ n + # value_function: (fe_v, q_point, ue) -> z + + # Check validity of input + @assert length(dh.subdofhandlers) == 1 + @assert length(Ferrite.getfieldnames(dh)) == 1 + + # Find the matching FaceIndex + cellnr, facenr = face + face2 = find_matching_face(dh.grid, face) + face2 === nothing && return false + + # Pick "random" points on the face + cell = getcells(dh.grid, cellnr) + RefShape = Ferrite.getrefshape(getcells(dh.grid, cellnr)) + ip_geo = Ferrite.default_interpolation(typeof(cell)) + ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) + fqr = FaceQuadratureRule{RefShape}(10) + fv = FaceValues(fqr, ip_fun, ip_geo) + cell_coords = getcoordinates(dh.grid, cellnr) + inds = randperm(getnquadpoints(fv))[1:min(4, getnquadpoints(fv))] + + # Random dof vector to test continuity + u = rand(ndofs(dh)) + + # Calculate coordinates and function values for these + point_coords = zeros(eltype(cell_coords), length(inds)) + point_normal = similar(point_coords) + fun_vals = zeros(typeof(shape_value(fv, 1, 1)), length(inds)) + reinit!(fv, cell, cell_coords, facenr) + ue = u[celldofs(dh, cellnr)] + for (i, q_point) in enumerate(inds) + point_coords[i] = spatial_coordinate(fv, q_point, cell_coords) + point_normal[i] = getnormal(fv, q_point) + fun_vals[i] = value_function(fv, q_point, ue) + end + + # Calculate function values on the other cell + cell2 = getcells(dh.grid, face2[1]) + cell_coords2 = getcoordinates(dh.grid, face2[1]) + local_coords = map(x->Ferrite.find_local_coordinate(ip_geo, cell_coords2, x), point_coords) + @assert all(first.(local_coords)) # check that find_local_coordinate converged + ξs = collect(last.(local_coords)) # Extract the local coordinates + qr = QuadratureRule{RefShape}(zeros(length(ξs)), ξs) + cv = CellValues(qr, ip_fun, ip_geo) + reinit!(cv, cell2, cell_coords2) + fun_vals2 = similar(fun_vals) + ue2 = u[celldofs(dh, face2[1])] + for q_point in 1:getnquadpoints(cv) + @assert spatial_coordinate(cv, q_point, cell_coords2) ≈ point_coords[q_point] + fun_vals2[q_point] = value_function(cv, q_point, ue2) + end + + d1 = map((v,n)->transformation_function(v,n), fun_vals, point_normal) + d2 = map((v,n)->transformation_function(v,n), fun_vals2, point_normal) + @test isapprox(d1, d2; rtol=1e-6) # Approximate points can contribute to the inexactness + return true + end + + function create_gradcheck_qr(ip_geo::Interpolation{RefShape}, ΔL) where RefShape + dim = Ferrite.getdim(ip_geo) + xref = Ferrite.reference_coordinates(ip_geo) + xc = sum(xref)/length(xref) + ws = rand(length(xref))*((1-ΔL)/length(xref)) + xp = xc + sum(map((x,w) -> w*(x - xc), xref, ws)) + v = normalize(rand(Vec{dim}) - ones(Vec{dim})/2) + x1 = xp + ΔL*v + qr_w = [NaN, NaN] + qr_x = [xp, x1] + return QuadratureRule{RefShape}(qr_w, qr_x) + end + + function test_gradient(dh, cellnr; ΔL=1e-6) + ue = rand(ndofs_per_cell(dh, cellnr)) + x = getcoordinates(dh.grid, cellnr) + cell = getcells(dh.grid, cellnr) + ip_geo = Ferrite.default_interpolation(typeof(cell)) + ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) + qr = create_gradcheck_qr(ip_geo, ΔL) + cv = CellValues(qr, ip_fun, ip_geo) + reinit!(cv, cell, x) + Δu_num = function_value(cv, 2, ue) - function_value(cv, 1, ue) + Δx = spatial_coordinate(cv, 2, x) - spatial_coordinate(cv, 1, x) + ∇u1 = function_gradient(cv, 1, ue) + ∇u2 = function_gradient(cv, 2, ue) + Δu_ana = 0.5*(∇u1+∇u2) ⋅ Δx + # Δu_ana_var = 0.5*(∇u2-∇u1) ⋅ Δx # Relevant to compare magnitude if test fails + @test isapprox(Δu_num, Δu_ana; rtol=1e-6) + return nothing + end + +end \ No newline at end of file diff --git a/test/test_cellvalues.jl b/test/test_cellvalues.jl index 052251c1f7..9f66ccbbc4 100644 --- a/test/test_cellvalues.jl +++ b/test/test_cellvalues.jl @@ -137,22 +137,27 @@ end @testset "error paths in function_* and reinit!" begin dim = 2 qp = 1 + cell = Triangle((1,2,3)) ip = Lagrange{RefTriangle,1}() + ip_nedelec = Nedelec{2,RefTriangle,1}() qr = QuadratureRule{RefTriangle}(1) qr_f = FaceQuadratureRule{RefTriangle}(1) csv = CellValues(qr, ip) cvv = CellValues(qr, VectorizedInterpolation(ip)) csv_embedded = CellValues(qr, ip, ip^3) + cv_nedelec = CellValues(qr, ip_nedelec, ip) fsv = FaceValues(qr_f, ip) fvv = FaceValues(qr_f, VectorizedInterpolation(ip)) fsv_embedded = FaceValues(qr_f, ip, ip^3) - + fv_nedelec = FaceValues(qr_f, ip_nedelec, ip) x, n = valid_coordinates_and_normals(ip) reinit!(csv, x) reinit!(cvv, x) reinit!(fsv, x, 1) reinit!(fvv, x, 1) - + reinit!(cv_nedelec, cell, x) + reinit!(fv_nedelec, cell, x, 1) + # Wrong number of coordinates xx = [x; x] @test_throws ArgumentError reinit!(csv, xx) @@ -169,6 +174,10 @@ end @test_throws ArgumentError reinit!(csv_embedded, x) @test_throws ArgumentError reinit!(fsv_embedded, x, 1) + # Missing cell input when required + @test_throws ArgumentError reinit!(cv_nedelec, x) + @test_throws ArgumentError reinit!(fv_nedelec, x, 1) + # Wrong number of (local) dofs # Scalar values, scalar dofs ue = rand(getnbasefunctions(csv) + 1) diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index b67e0599a1..061e0b96bd 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,3 +1,4 @@ + @testset "interpolations" begin @testset "$interpolation" for interpolation in ( @@ -208,4 +209,46 @@ end @test Ferrite.is_discontinuous(ip_t) == false @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true + +@testset "Hcurl and Hdiv" begin + include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) + import .InterpolationTestUtils as ITU + nel = 3 + transformation_functions = Dict( + Nedelec=>(v,n)-> v - n*(v⋅n), # Hcurl (tangent continuity) + RaviartThomas=>(v,n) -> v ⋅ n) # Hdiv (normal continuity) + for CT in (Triangle, QuadraticTriangle, Tetrahedron) + dim = Ferrite.getdim(CT) + p1, p2 = (rand(Vec{dim}), ones(Vec{dim})+rand(Vec{dim})) + grid = generate_grid(CT, ntuple(_->nel, dim), p1, p2) + # Smoothly distort grid (to avoid spuriously badly deformed elements). + # A distorted grid is important to properly test the geometry mapping + # for 2nd order elements. + transfun(x) = typeof(x)(i->sinpi(x[mod(i, length(x))+1]+i/3))/10 + transform_coordinates!(grid, x->(x + transfun(x))) + RefShape = Ferrite.getrefshape(getcells(grid, 1)) + for order in (1, 2) + for IPT in (Nedelec, RaviartThomas) + dim == 3 && order > 1 && continue + IPT == RaviartThomas && (dim == 3 || order > 1) && continue + transformation_function=transformation_functions[IPT] + ip = IPT{dim,RefShape,order}() + @testset "$CT, $ip" begin + dh = DofHandler(grid) + add!(dh, :u, ip) + close!(dh) + cellnr = getncells(grid)÷2 # Should be a cell in the center + for facenr in 1:nfaces(RefShape) + fi = FaceIndex(cellnr, facenr) + # Check continuity of tangential function value + ITU.test_continuity(dh, fi; transformation_function) + end + # Check gradient calculation + ITU.test_gradient(dh, cellnr) + end + end + end + end end +end + From 3f5721e8c7c0afba463668e4808c86fe7ded323e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 28 Feb 2024 18:34:36 +0100 Subject: [PATCH 122/172] Fix after merge --- test/test_facevalues.jl | 2 +- test/test_interpolations.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_facevalues.jl b/test/test_facevalues.jl index 056cc542e2..e7ed791257 100644 --- a/test/test_facevalues.jl +++ b/test/test_facevalues.jl @@ -17,7 +17,7 @@ for (scalar_interpol, quad_rule) in ( for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)) geom_interpol = scalar_interpol # Tests below assume this n_basefunc_base = getnbasefunctions(scalar_interpol) - fv = if VERSION ≥ v"1.9" + fv = if false # VERSION ≥ v"1.9" # TODO: Lost type stability again... @inferred FaceValues(quad_rule, func_interpol, geom_interpol) else # Type unstable on 1.6, but works at least for 1.9 and later. PR882 FaceValues(quad_rule, func_interpol, geom_interpol) diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index ed1a6155b2..99e352af06 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -224,6 +224,7 @@ @test Ferrite.is_discontinuous(ip_t) == false @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true +end @testset "Hcurl and Hdiv" begin include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) From 90dbf686b1592119be4f080e4aa53b1b507683d9 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Thu, 21 Mar 2024 11:43:27 +0100 Subject: [PATCH 123/172] Make CellCache reinit work --- src/iterators.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/iterators.jl b/src/iterators.jl index e1431de711..70793d45ab 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -89,9 +89,12 @@ function reinit!(cc::CellCache, i::Int) end # reinit! FEValues with CellCache -reinit!(cv::CellValues, cc::CellCache) = reinit!(cv, cc.coords) +# TODO: Should we provide a fast path if the cell is not required for reinit? +#reinit!(cv::CellValues, cc::CellCache) = reinit!(cv, cc.coords) +reinit!(cv::CellValues, cc::CellCache) = reinit!(cv, getcells(cc.grid, cellid(cc)), cc.coords) reinit!(fv::FaceValues, cc::CellCache, f::Int) = reinit!(fv, cc.coords, f) # TODO: Deprecate? + # Accessor functions (TODO: Deprecate? We are so inconsistent with `getxx` vs `xx`...) getnodes(cc::CellCache) = cc.nodes getcoordinates(cc::CellCache) = cc.coords From 10b2dfd2a651c97487b86ffce3aa91b5e77dc23b Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Thu, 21 Mar 2024 17:28:41 +0100 Subject: [PATCH 124/172] Nonworking Nedelec on Hexahedron --- src/interpolations.jl | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/interpolations.jl b/src/interpolations.jl index 0670aee199..2ffe98624f 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1751,7 +1751,6 @@ function get_direction(::Nedelec{2,RefTriangle,2}, j, cell) return face_vertices[2] > face_vertices[1] ? 1 : -1 end -# RefTetrahedron, 1st order Lagrange - 𝐍𝐎𝐓 𝐓𝐄𝐒𝐓𝐄𝐃 # https://defelement.com/elements/examples/tetrahedron-nedelec1-lagrange-1.html function shape_value(ip::Nedelec{3,RefTetrahedron,1}, ξ::Vec{3,T}, i::Int) where T x, y, z = ξ @@ -1789,3 +1788,43 @@ It might be that this works automatically, following `adjust_dofs_during_distrib but test cases need to be developed first to make sure. No point in implementing guesses that cannot be verified... =# + +# https://defelement.com/elements/examples/hexahedron-nedelec1-lagrange-1.html +function shape_value(ip::Nedelec{3,RefHexahedron,1}, ξ::Vec{3,T}, i::Int) where T + x, y, z = 2ξ - ones(ξ) + # Edge 1 (defelement 0, positive) + i == 1 && return Vec(- y*z - y - z + 1, zero(T), zero(T)) + # Edge 2 (defelement 3, positive) + i == 2 && return Vec(zero(T), x * (1 - z), zero(T)) + # Edge 3 (defelement 5, negative) + i == 3 && return Vec(-y*(1-z), zero(T), zero(T)) + # Edge 4 (defelement 1, negative) + i == 4 && return Vec(zero(T), - x*z + x + z - 1, zero(T)) + # Edge 5 (defelement 8, positive) + i == 5 && return Vec(z*(1-y), zero(T), zero(T)) + # Edge 6 (defelement 10, positive) + i == 6 && return Vec(zero(T), z * z, zero(T)) + # Edge 7 (defelement 11, negative) + i == 7 && return Vec(- y*z, zero(T), zero(T)) + # Edge 8 (defelement 9, negative) + i == 8 && return Vec(zero(T), - z * (1 - x), zero(T)) + # Edge 9 (defelement 2, positive) + i == 9 && return Vec(zero(T), zero(T), x*y - x - y + 1) + # Edge 10 (defelement 4, positive) + i == 10 && return Vec(zero(T), zero(T), x * (1 - y)) + # Edge 11 (defelement 7, positive) + i == 11 && return Vec(zero(T), zero(T), x * y) + # Edge 12 (defelement 6, positive) + i == 12 && return Vec(zero(T), zero(T), y * (1 - x)) + + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::Nedelec{3,RefHexahedron,1}) = 12 +edgedof_interior_indices(::Nedelec{3,RefHexahedron,1}) = ntuple(i->(i,), 12) +adjust_dofs_during_distribution(::Nedelec{3,RefHexahedron,1}) = false + +function get_direction(::Nedelec{3,RefHexahedron,1}, j, cell) + edge_vertices = edges(cell)[j] + return edge_vertices[2] > edge_vertices[1] ? 1 : -1 +end From fc38951b0c3867ab659bfa8c01deafa36920453e Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 22 Mar 2024 14:30:10 +0100 Subject: [PATCH 125/172] Fix Nedelec{3,RefHexahedron} --- src/interpolations.jl | 10 +++++++--- test/test_interpolations.jl | 5 +++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/interpolations.jl b/src/interpolations.jl index 2ffe98624f..087b1dcf1b 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1790,10 +1790,14 @@ No point in implementing guesses that cannot be verified... =# # https://defelement.com/elements/examples/hexahedron-nedelec1-lagrange-1.html +# TODO: Need to "map" from defelement [0,1] refcell to Ferrite [-1,1], which +# for Nedelec should be scaling with J^-T where J=2I, i.e. divide by 2 +# For RT elements with Hdiv, this should instead be J/det(J) => 2/2^dim, +# i.e. also divide by 2 in 3d, no scaling in 2d, and multiply by 2 in 1d. function shape_value(ip::Nedelec{3,RefHexahedron,1}, ξ::Vec{3,T}, i::Int) where T - x, y, z = 2ξ - ones(ξ) + x, y, z = (ξ + ones(ξ))/2 # Edge 1 (defelement 0, positive) - i == 1 && return Vec(- y*z - y - z + 1, zero(T), zero(T)) + i == 1 && return Vec(y*z - y - z + 1, zero(T), zero(T)) # Edge 2 (defelement 3, positive) i == 2 && return Vec(zero(T), x * (1 - z), zero(T)) # Edge 3 (defelement 5, negative) @@ -1803,7 +1807,7 @@ function shape_value(ip::Nedelec{3,RefHexahedron,1}, ξ::Vec{3,T}, i::Int) where # Edge 5 (defelement 8, positive) i == 5 && return Vec(z*(1-y), zero(T), zero(T)) # Edge 6 (defelement 10, positive) - i == 6 && return Vec(zero(T), z * z, zero(T)) + i == 6 && return Vec(zero(T), x * z, zero(T)) # Edge 7 (defelement 11, negative) i == 7 && return Vec(- y*z, zero(T), zero(T)) # Edge 8 (defelement 9, negative) diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 99e352af06..d470084e8d 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -233,7 +233,7 @@ end transformation_functions = Dict( Nedelec=>(v,n)-> v - n*(v⋅n), # Hcurl (tangent continuity) RaviartThomas=>(v,n) -> v ⋅ n) # Hdiv (normal continuity) - for CT in (Triangle, QuadraticTriangle, Tetrahedron) + for CT in (Triangle, QuadraticTriangle, Tetrahedron, Hexahedron) dim = Ferrite.getdim(CT) p1, p2 = (rand(Vec{dim}), ones(Vec{dim})+rand(Vec{dim})) grid = generate_grid(CT, ntuple(_->nel, dim), p1, p2) @@ -247,13 +247,14 @@ end for IPT in (Nedelec, RaviartThomas) dim == 3 && order > 1 && continue IPT == RaviartThomas && (dim == 3 || order > 1) && continue + IPT == RaviartThomas && (CT == Hexahedron) && continue transformation_function=transformation_functions[IPT] ip = IPT{dim,RefShape,order}() @testset "$CT, $ip" begin dh = DofHandler(grid) add!(dh, :u, ip) close!(dh) - cellnr = getncells(grid)÷2 # Should be a cell in the center + cellnr = getncells(grid)÷2 + 1 # Should be a cell in the center for facenr in 1:nfaces(RefShape) fi = FaceIndex(cellnr, facenr) # Check continuity of tangential function value From d356a429728596a6198ecca046e9bd7304925c1a Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 22 Mar 2024 14:30:40 +0100 Subject: [PATCH 126/172] Add temporary visualization script and project --- visualization/Manifest.toml | 1690 ++++++++++++++++++++++++++++ visualization/Project.toml | 4 + visualization/reference_cell_ip.jl | 85 ++ 3 files changed, 1779 insertions(+) create mode 100644 visualization/Manifest.toml create mode 100644 visualization/Project.toml create mode 100644 visualization/reference_cell_ip.jl diff --git a/visualization/Manifest.toml b/visualization/Manifest.toml new file mode 100644 index 0000000000..ec88abc416 --- /dev/null +++ b/visualization/Manifest.toml @@ -0,0 +1,1690 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.10.2" +manifest_format = "2.0" +project_hash = "9dc2d1ce07b87cc5107b7fa3d34838a5e8d48de1" + +[[deps.AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.5.0" +weakdeps = ["ChainRulesCore", "Test"] + + [deps.AbstractFFTs.extensions] + AbstractFFTsChainRulesCoreExt = "ChainRulesCore" + AbstractFFTsTestExt = "Test" + +[[deps.AbstractLattices]] +git-tree-sha1 = "222ee9e50b98f51b5d78feb93dd928880df35f06" +uuid = "398f06c4-4d28-53ec-89ca-5b2656b7603d" +version = "0.3.0" + +[[deps.AbstractTrees]] +git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.4.5" + +[[deps.Adapt]] +deps = ["LinearAlgebra", "Requires"] +git-tree-sha1 = "6a55b747d1812e699320963ffde36f1ebdda4099" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "4.0.4" +weakdeps = ["StaticArrays"] + + [deps.Adapt.extensions] + AdaptStaticArraysExt = "StaticArrays" + +[[deps.Animations]] +deps = ["Colors"] +git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d" +uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340" +version = "0.4.1" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" + +[[deps.ArrayInterface]] +deps = ["Adapt", "LinearAlgebra", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "44691067188f6bd1b2289552a23e4b7572f4528d" +uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" +version = "7.9.0" + + [deps.ArrayInterface.extensions] + ArrayInterfaceBandedMatricesExt = "BandedMatrices" + ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" + ArrayInterfaceCUDAExt = "CUDA" + ArrayInterfaceChainRulesExt = "ChainRules" + ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" + ArrayInterfaceReverseDiffExt = "ReverseDiff" + ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" + ArrayInterfaceTrackerExt = "Tracker" + + [deps.ArrayInterface.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + +[[deps.Automa]] +deps = ["PrecompileTools", "TranscodingStreams"] +git-tree-sha1 = "588e0d680ad1d7201d4c6a804dcb1cd9cba79fbb" +uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" +version = "1.0.3" + +[[deps.AxisAlgorithms]] +deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] +git-tree-sha1 = "01b8ccb13d68535d73d2b0c23e39bd23155fb712" +uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950" +version = "1.1.0" + +[[deps.AxisArrays]] +deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"] +git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f" +uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9" +version = "0.4.7" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[deps.Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.8+1" + +[[deps.CEnum]] +git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.5.0" + +[[deps.CRC32c]] +uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" + +[[deps.CRlibm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" +uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" +version = "1.0.1+0" + +[[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"] +git-tree-sha1 = "a4c43f59baa34011e303e76f5c8c91bf58415aaf" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.18.0+1" + +[[deps.Calculus]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" +uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" +version = "0.5.1" + +[[deps.ChainRulesCore]] +deps = ["Compat", "LinearAlgebra"] +git-tree-sha1 = "575cd02e080939a33b6df6c5853d14924c08e35b" +uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" +version = "1.23.0" +weakdeps = ["SparseArrays"] + + [deps.ChainRulesCore.extensions] + ChainRulesCoreSparseArraysExt = "SparseArrays" + +[[deps.CodecZlib]] +deps = ["TranscodingStreams", "Zlib_jll"] +git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73" +uuid = "944b1d66-785c-5afd-91f1-9de20f533193" +version = "0.7.4" + +[[deps.ColorBrewer]] +deps = ["Colors", "JSON", "Test"] +git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4" +uuid = "a2cac450-b92f-5266-8821-25eda20663c8" +version = "0.4.0" + +[[deps.ColorSchemes]] +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] +git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129" +uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" +version = "3.24.0" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.11.4" + +[[deps.ColorVectorSpace]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" +uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" +version = "0.10.0" +weakdeps = ["SpecialFunctions"] + + [deps.ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" + +[[deps.Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] +git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.12.10" + +[[deps.Combinatorics]] +git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" +uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" +version = "1.0.2" + +[[deps.CommonSubexpressions]] +deps = ["MacroTools", "Test"] +git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7" +uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" +version = "0.3.0" + +[[deps.Compat]] +deps = ["TOML", "UUIDs"] +git-tree-sha1 = "c955881e3c981181362ae4088b35995446298b80" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "4.14.0" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.1.0+0" + +[[deps.ConstructionBase]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "260fd2400ed2dab602a7c15cf10c1933c59930a2" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.5.5" +weakdeps = ["IntervalSets", "StaticArrays"] + + [deps.ConstructionBase.extensions] + ConstructionBaseIntervalSetsExt = "IntervalSets" + ConstructionBaseStaticArraysExt = "StaticArrays" + +[[deps.Contour]] +git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" +uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" +version = "0.6.2" + +[[deps.DataAPI]] +git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.16.0" + +[[deps.DataStructures]] +deps = ["Compat", "InteractiveUtils", "OrderedCollections"] +git-tree-sha1 = "0f4b5d62a88d8f59003e43c25a8a90de9eb76317" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.18.18" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[deps.DelaunayTriangulation]] +deps = ["DataStructures", "EnumX", "ExactPredicates", "Random", "SimpleGraphs"] +git-tree-sha1 = "d4e9dc4c6106b8d44e40cd4faf8261a678552c7c" +uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" +version = "0.8.12" + +[[deps.DiffResults]] +deps = ["StaticArraysCore"] +git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" +uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" +version = "1.1.0" + +[[deps.DiffRules]] +deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] +git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" +uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" +version = "1.15.1" + +[[deps.Distances]] +deps = ["LinearAlgebra", "Statistics", "StatsAPI"] +git-tree-sha1 = "66c4c81f259586e8f002eacebc177e1fb06363b0" +uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" +version = "0.10.11" +weakdeps = ["ChainRulesCore", "SparseArrays"] + + [deps.Distances.extensions] + DistancesChainRulesCoreExt = "ChainRulesCore" + DistancesSparseArraysExt = "SparseArrays" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[deps.Distributions]] +deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] +git-tree-sha1 = "7c302d7a5fec5214eb8a5a4c466dcf7a51fcf169" +uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" +version = "0.25.107" + + [deps.Distributions.extensions] + DistributionsChainRulesCoreExt = "ChainRulesCore" + DistributionsDensityInterfaceExt = "DensityInterface" + DistributionsTestExt = "Test" + + [deps.Distributions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.DocStringExtensions]] +deps = ["LibGit2"] +git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.9.3" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[deps.DualNumbers]] +deps = ["Calculus", "NaNMath", "SpecialFunctions"] +git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566" +uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" +version = "0.6.8" + +[[deps.EarCut_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053" +uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +version = "2.2.4+0" + +[[deps.EnumX]] +git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" +uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" +version = "1.0.4" + +[[deps.ExactPredicates]] +deps = ["IntervalArithmetic", "Random", "StaticArrays"] +git-tree-sha1 = "b3f2ff58735b5f024c392fde763f29b057e4b025" +uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" +version = "2.2.8" + +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.5.0+0" + +[[deps.Extents]] +git-tree-sha1 = "2140cd04483da90b2da7f99b2add0750504fc39c" +uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" +version = "0.1.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"] +git-tree-sha1 = "ab3f7e1819dba9434a3a5126510c8fda3a4e7000" +uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" +version = "6.1.1+0" + +[[deps.FFTW]] +deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" +uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +version = "1.8.0" + +[[deps.FFTW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" +uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" +version = "3.3.10+0" + +[[deps.Ferrite]] +deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] +path = ".." +uuid = "c061ca5d-56c9-439f-9c0e-210fe06d3992" +version = "0.3.14" + + [deps.Ferrite.extensions] + FerriteBlockArrays = "BlockArrays" + FerriteMetis = "Metis" + + [deps.Ferrite.weakdeps] + BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" + Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" + +[[deps.FerriteViz]] +deps = ["Ferrite", "GeometryBasics", "LinearAlgebra", "Makie", "Reexport", "ShaderAbstractions", "StaticArrays", "Tensors"] +path = "C:\\Users\\meyer\\.julia\\dev\\FerriteViz" +uuid = "59d0093e-b1f1-4fb7-ac85-ab57e45f39d9" +version = "0.2.1" + +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.16.3" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + +[[deps.FillArrays]] +deps = ["LinearAlgebra", "Random"] +git-tree-sha1 = "5b93957f6dcd33fc343044af3d48c215be2562f1" +uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" +version = "1.9.3" +weakdeps = ["PDMats", "SparseArrays", "Statistics"] + + [deps.FillArrays.extensions] + FillArraysPDMatsExt = "PDMats" + FillArraysSparseArraysExt = "SparseArrays" + FillArraysStatisticsExt = "Statistics" + +[[deps.FiniteDiff]] +deps = ["ArrayInterface", "LinearAlgebra", "Requires", "Setfield", "SparseArrays"] +git-tree-sha1 = "bc0c5092d6caaea112d3c8e3b238d61563c58d5f" +uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" +version = "2.23.0" + + [deps.FiniteDiff.extensions] + FiniteDiffBandedMatricesExt = "BandedMatrices" + FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" + FiniteDiffStaticArraysExt = "StaticArrays" + + [deps.FiniteDiff.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.FixedPointNumbers]] +deps = ["Statistics"] +git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.4" + +[[deps.Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.13.93+0" + +[[deps.Formatting]] +deps = ["Logging", "Printf"] +git-tree-sha1 = "fb409abab2caf118986fc597ba84b50cbaf00b87" +uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" +version = "0.4.3" + +[[deps.ForwardDiff]] +deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] +git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad" +uuid = "f6369f11-7733-5829-9624-2563aa707210" +version = "0.10.36" +weakdeps = ["StaticArrays"] + + [deps.ForwardDiff.extensions] + ForwardDiffStaticArraysExt = "StaticArrays" + +[[deps.FreeType]] +deps = ["CEnum", "FreeType2_jll"] +git-tree-sha1 = "907369da0f8e80728ab49c1c7e09327bf0d6d999" +uuid = "b38be410-82b0-50bf-ab77-7b57e271db43" +version = "4.1.1" + +[[deps.FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.13.1+0" + +[[deps.FreeTypeAbstraction]] +deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] +git-tree-sha1 = "055626e1a35f6771fe99060e835b72ca61a52621" +uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" +version = "0.10.1" + +[[deps.FriBidi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" +uuid = "559328eb-81f9-559d-9380-de523a88c83c" +version = "1.0.10+0" + +[[deps.Future]] +deps = ["Random"] +uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" + +[[deps.GLFW]] +deps = ["GLFW_jll"] +git-tree-sha1 = "35dbc482f0967d8dceaa7ce007d16f9064072166" +uuid = "f7f18e0c-5ee9-5ccd-a5bf-e8befd85ed98" +version = "3.4.1" + +[[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" +uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" +version = "3.3.9+0" + +[[deps.GLMakie]] +deps = ["ColorTypes", "Colors", "FileIO", "FixedPointNumbers", "FreeTypeAbstraction", "GLFW", "GeometryBasics", "LinearAlgebra", "Makie", "Markdown", "MeshIO", "ModernGL", "Observables", "PrecompileTools", "Printf", "ShaderAbstractions", "StaticArrays"] +git-tree-sha1 = "8236ce4eda9837d15bab49573bba16ba0652b486" +uuid = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" +version = "0.8.12" + +[[deps.GeoInterface]] +deps = ["Extents"] +git-tree-sha1 = "d4f85701f569584f2cff7ba67a137d03f0cfb7d0" +uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" +version = "1.3.3" + +[[deps.GeometryBasics]] +deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "5694b56ccf9d15addedc35e9a4ba9c317721b788" +uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +version = "0.4.10" + +[[deps.Gettext_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" +uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" +version = "0.21.0+0" + +[[deps.Glib_jll]] +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "359a1ba2e320790ddbe4ee8b4d54a305c0ea2aff" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.80.0+0" + +[[deps.Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.14+0" + +[[deps.GridLayoutBase]] +deps = ["GeometryBasics", "InteractiveUtils", "Observables"] +git-tree-sha1 = "f57a64794b336d4990d90f80b147474b869b1bc4" +uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" +version = "0.9.2" + +[[deps.Grisu]] +git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" +uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" +version = "1.0.2" + +[[deps.HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] +git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "2.8.1+1" + +[[deps.HypergeometricFunctions]] +deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] +git-tree-sha1 = "f218fe3736ddf977e0e772bc9a586b2383da2685" +uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" +version = "0.3.23" + +[[deps.ImageAxes]] +deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"] +git-tree-sha1 = "2e4520d67b0cef90865b3ef727594d2a58e0e1f8" +uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac" +version = "0.6.11" + +[[deps.ImageBase]] +deps = ["ImageCore", "Reexport"] +git-tree-sha1 = "eb49b82c172811fd2c86759fa0553a2221feb909" +uuid = "c817782e-172a-44cc-b673-b171935fbb9e" +version = "0.1.7" + +[[deps.ImageCore]] +deps = ["ColorVectorSpace", "Colors", "FixedPointNumbers", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "PrecompileTools", "Reexport"] +git-tree-sha1 = "b2a7eaa169c13f5bcae8131a83bc30eff8f71be0" +uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534" +version = "0.10.2" + +[[deps.ImageIO]] +deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs"] +git-tree-sha1 = "bca20b2f5d00c4fbc192c3212da8fa79f4688009" +uuid = "82e4d734-157c-48bb-816b-45c225c6df19" +version = "0.6.7" + +[[deps.ImageMetadata]] +deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] +git-tree-sha1 = "355e2b974f2e3212a75dfb60519de21361ad3cb7" +uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49" +version = "0.9.9" + +[[deps.Imath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "3d09a9f60edf77f8a4d99f9e015e8fbf9989605d" +uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" +version = "3.1.7+0" + +[[deps.IndirectArrays]] +git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" +uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" +version = "1.0.0" + +[[deps.Inflate]] +git-tree-sha1 = "ea8031dea4aff6bd41f1df8f2fdfb25b33626381" +uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" +version = "0.1.4" + +[[deps.IntegerMathUtils]] +git-tree-sha1 = "b8ffb903da9f7b8cf695a8bead8e01814aa24b30" +uuid = "18e54dd8-cb9d-406c-a71d-865a43cbb235" +version = "0.1.2" + +[[deps.IntelOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "5fdf2fe6724d8caabf43b557b84ce53f3b7e2f6b" +uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" +version = "2024.0.2+0" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[deps.Interpolations]] +deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] +git-tree-sha1 = "88a101217d7cb38a7b481ccd50d21876e1d1b0e0" +uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" +version = "0.15.1" + + [deps.Interpolations.extensions] + InterpolationsUnitfulExt = "Unitful" + + [deps.Interpolations.weakdeps] + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.IntervalArithmetic]] +deps = ["CRlibm_jll", "RoundingEmulator"] +git-tree-sha1 = "552505ed27d2a90ff04c15b0ecf4634e0ab5547b" +uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +version = "0.22.9" +weakdeps = ["DiffRules", "ForwardDiff", "RecipesBase"] + + [deps.IntervalArithmetic.extensions] + IntervalArithmeticDiffRulesExt = "DiffRules" + IntervalArithmeticForwardDiffExt = "ForwardDiff" + IntervalArithmeticRecipesBaseExt = "RecipesBase" + +[[deps.IntervalSets]] +git-tree-sha1 = "dba9ddf07f77f60450fe5d2e2beb9854d9a49bd0" +uuid = "8197267c-284f-5f27-9208-e0e47529a953" +version = "0.7.10" +weakdeps = ["Random", "RecipesBase", "Statistics"] + + [deps.IntervalSets.extensions] + IntervalSetsRandomExt = "Random" + IntervalSetsRecipesBaseExt = "RecipesBase" + IntervalSetsStatisticsExt = "Statistics" + +[[deps.IrrationalConstants]] +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.2.2" + +[[deps.Isoband]] +deps = ["isoband_jll"] +git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137" +uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4" +version = "0.1.1" + +[[deps.IterTools]] +git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.10.0" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[deps.JLLWrappers]] +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.5.0" + +[[deps.JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.4" + +[[deps.JpegTurbo]] +deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"] +git-tree-sha1 = "fa6d0bcff8583bac20f1ffa708c3913ca605c611" +uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0" +version = "0.1.5" + +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "3336abae9a713d2210bb57ab484b1e065edd7d23" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "3.0.2+0" + +[[deps.KernelDensity]] +deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] +git-tree-sha1 = "fee018a29b60733876eb557804b5b109dd3dd8a7" +uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" +version = "0.6.8" + +[[deps.LAME_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" +uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" +version = "3.100.1+0" + +[[deps.LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d986ce2d884d49126836ea94ed5bfb0f12679713" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "15.0.7+0" + +[[deps.LZO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" +version = "2.10.1+0" + +[[deps.LaTeXStrings]] +git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.3.1" + +[[deps.LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[deps.LazyModules]] +git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e" +uuid = "8cdb02fc-e678-4876-92c5-9defec4f444e" +version = "0.3.1" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.4.0+0" + +[[deps.LibGit2]] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.6.4+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.2.2+1" + +[[deps.Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.7+0" + +[[deps.Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.6.0+0" + +[[deps.Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.42.0+0" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.17.0+0" + +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "dae976433497a2f841baadea93d27e68f1a12a97" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.39.3+0" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0a04a1318df1bf510beb2562cf90fb0c386f58c4" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.39.3+1" + +[[deps.LightXML]] +deps = ["Libdl", "XML2_jll"] +git-tree-sha1 = "3a994404d3f6709610701c7dabfc03fed87a81f8" +uuid = "9c8b4983-aa76-5018-a973-4c85ecc9e179" +version = "0.9.1" + +[[deps.LineSearches]] +deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "Printf"] +git-tree-sha1 = "7bbea35cec17305fc70a0e5b4641477dc0789d9d" +uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" +version = "7.2.0" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.LinearAlgebraX]] +deps = ["LinearAlgebra", "Mods", "Primes", "SimplePolynomials"] +git-tree-sha1 = "d76cec8007ec123c2b681269d40f94b053473fcf" +uuid = "9b3f67b0-2d00-526e-9884-9e4938f8fb88" +version = "0.2.7" + +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.27" + + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[deps.MKL_jll]] +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "72dc3cf284559eb8f53aa593fe62cb33f83ed0c0" +uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" +version = "2024.0.0+0" + +[[deps.MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.13" + +[[deps.Makie]] +deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FixedPointNumbers", "Formatting", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "InteractiveUtils", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Setfield", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "StableHashTraits", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun"] +git-tree-sha1 = "35fa3c150cd96fd77417a23965b7037b90d6ffc9" +uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" +version = "0.19.12" + +[[deps.MakieCore]] +deps = ["Observables", "REPL"] +git-tree-sha1 = "9b11acd07f21c4d035bd4156e789532e8ee2cc70" +uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" +version = "0.6.9" + +[[deps.MappedArrays]] +git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" +uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" +version = "0.4.2" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[deps.MathTeXEngine]] +deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] +git-tree-sha1 = "96ca8a313eb6437db5ffe946c457a401bbb8ce1d" +uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" +version = "0.5.7" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+1" + +[[deps.MeshIO]] +deps = ["ColorTypes", "FileIO", "GeometryBasics", "Printf"] +git-tree-sha1 = "8c26ab950860dfca6767f2bbd90fdf1e8ddc678b" +uuid = "7269a6da-0436-5bbc-96c2-40638cbb6118" +version = "0.4.11" + +[[deps.Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "f66bdc5de519e8f8ae43bdc598782d35a25b1272" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.1.0" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[deps.ModernGL]] +deps = ["Libdl"] +git-tree-sha1 = "b76ea40b5c0f45790ae09492712dd326208c28b2" +uuid = "66fc600b-dfda-50eb-8b99-91cfa97b1301" +version = "1.1.7" + +[[deps.Mods]] +git-tree-sha1 = "924f962b524a71eef7a21dae1e6853817f9b658f" +uuid = "7475f97c-0381-53b1-977b-4c60186c8d62" +version = "2.2.4" + +[[deps.MosaicViews]] +deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] +git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe" +uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389" +version = "0.3.4" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2023.1.10" + +[[deps.Multisets]] +git-tree-sha1 = "8d852646862c96e226367ad10c8af56099b4047e" +uuid = "3b2b4ff1-bcff-5658-a3ee-dbcf1ce5ac09" +version = "0.4.4" + +[[deps.NLSolversBase]] +deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] +git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" +uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" +version = "7.8.3" + +[[deps.NaNMath]] +deps = ["OpenLibm_jll"] +git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "1.0.2" + +[[deps.NearestNeighbors]] +deps = ["Distances", "StaticArrays"] +git-tree-sha1 = "ded64ff6d4fdd1cb68dfcbb818c69e144a5b2e4c" +uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" +version = "0.4.16" + +[[deps.Netpbm]] +deps = ["FileIO", "ImageCore", "ImageMetadata"] +git-tree-sha1 = "d92b107dbb887293622df7697a2223f9f8176fcd" +uuid = "f09324ee-3d7c-5217-9330-fc30815ba969" +version = "1.1.1" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[deps.Observables]] +git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" +uuid = "510215fc-4207-5dde-b226-833fc4488ee2" +version = "0.5.5" + +[[deps.OffsetArrays]] +git-tree-sha1 = "6a731f2b5c03157418a20c12195eb4b74c8f8621" +uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +version = "1.13.0" +weakdeps = ["Adapt"] + + [deps.OffsetArrays.extensions] + OffsetArraysAdaptExt = "Adapt" + +[[deps.Ogg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" +uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" +version = "1.3.5+1" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.23+4" + +[[deps.OpenEXR]] +deps = ["Colors", "FileIO", "OpenEXR_jll"] +git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633" +uuid = "52e1d378-f018-4a11-a4be-720524705ac7" +version = "0.3.2" + +[[deps.OpenEXR_jll]] +deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "a4ca623df1ae99d09bc9868b008262d0c0ac1e4f" +uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" +version = "3.1.4+0" + +[[deps.OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.1+2" + +[[deps.OpenSSL_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "60e3045590bd104a16fefb12836c00c0ef8c7f8c" +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "3.0.13+0" + +[[deps.OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[deps.Optim]] +deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", "NLSolversBase", "NaNMath", "PackageExtensionCompat", "Parameters", "PositiveFactorizations", "Printf", "SparseArrays", "StatsBase"] +git-tree-sha1 = "d1223e69af90b6d26cea5b6f3b289b3148ba702c" +uuid = "429524aa-4258-5aef-a3af-852621145aeb" +version = "1.9.3" + + [deps.Optim.extensions] + OptimMOIExt = "MathOptInterface" + + [deps.Optim.weakdeps] + MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" + +[[deps.Opus_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" +uuid = "91d4177d-7536-5919-b921-800302f37372" +version = "1.3.2+0" + +[[deps.OrderedCollections]] +git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.6.3" + +[[deps.PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.42.0+1" + +[[deps.PDMats]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "949347156c25054de2db3b166c52ac4728cbad65" +uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" +version = "0.11.31" + +[[deps.PNGFiles]] +deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"] +git-tree-sha1 = "67186a2bc9a90f9f85ff3cc8277868961fb57cbd" +uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" +version = "0.4.3" + +[[deps.PackageExtensionCompat]] +git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" +uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" +version = "1.0.2" +weakdeps = ["Requires", "TOML"] + +[[deps.Packing]] +deps = ["GeometryBasics"] +git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501" +uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566" +version = "0.5.0" + +[[deps.PaddedViews]] +deps = ["OffsetArrays"] +git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f" +uuid = "5432bcbf-9aad-5242-b902-cca2824c8663" +version = "0.5.12" + +[[deps.Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.3" + +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.8.1" + +[[deps.Permutations]] +deps = ["Combinatorics", "LinearAlgebra", "Random"] +git-tree-sha1 = "eb3f9df2457819bf0a9019bd93cc451697a0751e" +uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" +version = "0.4.20" + +[[deps.PikaParser]] +deps = ["DocStringExtensions"] +git-tree-sha1 = "d6ff87de27ff3082131f31a714d25ab6d0a88abf" +uuid = "3bbf5609-3e7b-44cd-8549-7c69f321e792" +version = "0.6.1" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.42.2+0" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.10.0" + +[[deps.PkgVersion]] +deps = ["Pkg"] +git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da" +uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" +version = "0.3.3" + +[[deps.PlotUtils]] +deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] +git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" +uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" +version = "1.4.1" + +[[deps.PolygonOps]] +git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" +uuid = "647866c9-e3ac-4575-94e7-e3d426903924" +version = "0.1.2" + +[[deps.Polynomials]] +deps = ["LinearAlgebra", "RecipesBase", "Setfield", "SparseArrays"] +git-tree-sha1 = "a9c7a523d5ed375be3983db190f6a5874ae9286d" +uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" +version = "4.0.6" + + [deps.Polynomials.extensions] + PolynomialsChainRulesCoreExt = "ChainRulesCore" + PolynomialsFFTWExt = "FFTW" + PolynomialsMakieCoreExt = "MakieCore" + PolynomialsMutableArithmeticsExt = "MutableArithmetics" + + [deps.Polynomials.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" + MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b" + MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" + +[[deps.PositiveFactorizations]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "17275485f373e6673f7e7f97051f703ed5b15b20" +uuid = "85a6dd25-e78a-55b7-8502-1745935b8125" +version = "0.2.4" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.1" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.4.3" + +[[deps.Primes]] +deps = ["IntegerMathUtils"] +git-tree-sha1 = "cb420f77dc474d23ee47ca8d14c90810cafe69e7" +uuid = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" +version = "0.5.6" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[deps.ProgressMeter]] +deps = ["Distributed", "Printf"] +git-tree-sha1 = "763a8ceb07833dd51bb9e3bbca372de32c0605ad" +uuid = "92933f4c-e287-5a05-a399-4b506db050ca" +version = "1.10.0" + +[[deps.QOI]] +deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] +git-tree-sha1 = "18e8f4d1426e965c7b532ddd260599e1510d26ce" +uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" +version = "1.0.0" + +[[deps.QuadGK]] +deps = ["DataStructures", "LinearAlgebra"] +git-tree-sha1 = "9b23c31e76e333e6fb4c1595ae6afa74966a729e" +uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +version = "2.9.4" + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[deps.Random]] +deps = ["SHA"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[deps.RangeArrays]] +git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" +uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d" +version = "0.3.2" + +[[deps.Ratios]] +deps = ["Requires"] +git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b" +uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" +version = "0.4.5" +weakdeps = ["FixedPointNumbers"] + + [deps.Ratios.extensions] + RatiosFixedPointNumbersExt = "FixedPointNumbers" + +[[deps.RecipesBase]] +deps = ["PrecompileTools"] +git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "1.3.4" + +[[deps.Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[deps.RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "1.0.1" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.0" + +[[deps.RingLists]] +deps = ["Random"] +git-tree-sha1 = "f39da63aa6d2d88e0c1bd20ed6a3ff9ea7171ada" +uuid = "286e9d63-9694-5540-9e3c-4e6708fa07b2" +version = "0.2.8" + +[[deps.Rmath]] +deps = ["Random", "Rmath_jll"] +git-tree-sha1 = "f65dcb5fa46aee0cf9ed6274ccbd597adc49aa7b" +uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" +version = "0.7.1" + +[[deps.Rmath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "6ed52fdd3382cf21947b15e8870ac0ddbff736da" +uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" +version = "0.4.0+0" + +[[deps.RoundingEmulator]] +git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" +uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" +version = "0.2.1" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.SIMD]] +deps = ["PrecompileTools"] +git-tree-sha1 = "d8911cc125da009051fb35322415641d02d9e37f" +uuid = "fdea26ae-647d-5447-a871-4b548cad5224" +version = "3.4.6" + +[[deps.Scratch]] +deps = ["Dates"] +git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.2.1" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[deps.Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] +git-tree-sha1 = "e2cc6d8c88613c05e1defb55170bf5ff211fbeac" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "1.1.1" + +[[deps.ShaderAbstractions]] +deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "79123bc60c5507f035e6d1d9e563bb2971954ec8" +uuid = "65257c39-d410-5151-9873-9b3e5be5013e" +version = "0.4.1" + +[[deps.SharedArrays]] +deps = ["Distributed", "Mmap", "Random", "Serialization"] +uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[[deps.Showoff]] +deps = ["Dates", "Grisu"] +git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" +uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" +version = "1.0.3" + +[[deps.SignedDistanceFields]] +deps = ["Random", "Statistics", "Test"] +git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" +uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" +version = "0.4.0" + +[[deps.SimpleGraphs]] +deps = ["AbstractLattices", "Combinatorics", "DataStructures", "IterTools", "LightXML", "LinearAlgebra", "LinearAlgebraX", "Optim", "Primes", "Random", "RingLists", "SimplePartitions", "SimplePolynomials", "SimpleRandom", "SparseArrays", "Statistics"] +git-tree-sha1 = "f65caa24a622f985cc341de81d3f9744435d0d0f" +uuid = "55797a34-41de-5266-9ec1-32ac4eb504d3" +version = "0.8.6" + +[[deps.SimplePartitions]] +deps = ["AbstractLattices", "DataStructures", "Permutations"] +git-tree-sha1 = "e182b9e5afb194142d4668536345a365ea19363a" +uuid = "ec83eff0-a5b5-5643-ae32-5cbf6eedec9d" +version = "0.3.2" + +[[deps.SimplePolynomials]] +deps = ["Mods", "Multisets", "Polynomials", "Primes"] +git-tree-sha1 = "7063828369cafa93f3187b3d0159f05582011405" +uuid = "cc47b68c-3164-5771-a705-2bc0097375a0" +version = "0.2.17" + +[[deps.SimpleRandom]] +deps = ["Distributions", "LinearAlgebra", "Random"] +git-tree-sha1 = "3a6fb395e37afab81aeea85bae48a4db5cd7244a" +uuid = "a6525b86-64cd-54fa-8f65-62fc48bdc0e8" +version = "0.3.1" + +[[deps.SimpleTraits]] +deps = ["InteractiveUtils", "MacroTools"] +git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" +uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" +version = "0.9.4" + +[[deps.Sixel]] +deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"] +git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6" +uuid = "45858cf5-a6b0-47a3-bbea-62219f50df47" +version = "0.1.3" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[deps.SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "66e0a8e672a0bdfca2c3f5937efb8538b9ddc085" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.2.1" + +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.10.0" + +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "2.3.1" +weakdeps = ["ChainRulesCore"] + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + +[[deps.StableHashTraits]] +deps = ["Compat", "PikaParser", "SHA", "Tables", "TupleTools"] +git-tree-sha1 = "10dc702932fe05a0e09b8e5955f00794ea1e8b12" +uuid = "c5dd0088-6c3f-4803-b00e-f31a60c170fa" +version = "1.1.8" + +[[deps.StackViews]] +deps = ["OffsetArrays"] +git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" +uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15" +version = "0.1.1" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] +git-tree-sha1 = "bf074c045d3d5ffd956fa0a461da38a44685d6b2" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.9.3" +weakdeps = ["ChainRulesCore", "Statistics"] + + [deps.StaticArrays.extensions] + StaticArraysChainRulesCoreExt = "ChainRulesCore" + StaticArraysStatisticsExt = "Statistics" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.2" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.10.0" + +[[deps.StatsAPI]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" +uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" +version = "1.7.0" + +[[deps.StatsBase]] +deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "1d77abd07f617c4868c33d4f5b9e1dbb2643c9cf" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.34.2" + +[[deps.StatsFuns]] +deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] +git-tree-sha1 = "cef0472124fab0695b58ca35a77c6fb942fdab8a" +uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" +version = "1.3.1" + + [deps.StatsFuns.extensions] + StatsFunsChainRulesCoreExt = "ChainRulesCore" + StatsFunsInverseFunctionsExt = "InverseFunctions" + + [deps.StatsFuns.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.StructArrays]] +deps = ["ConstructionBase", "DataAPI", "Tables"] +git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be" +uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" +version = "0.6.18" + + [deps.StructArrays.extensions] + StructArraysAdaptExt = "Adapt" + StructArraysGPUArraysCoreExt = "GPUArraysCore" + StructArraysSparseArraysExt = "SparseArrays" + StructArraysStaticArraysExt = "StaticArrays" + + [deps.StructArrays.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.SuiteSparse]] +deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] +uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" + +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.2.1+1" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.11.1" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[deps.TensorCore]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" +uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" +version = "0.1.1" + +[[deps.Tensors]] +deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] +git-tree-sha1 = "957f256fb380cad64cae4da39e562ecfb5c3fec9" +uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" +version = "1.16.1" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.TiffImages]] +deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "UUIDs"] +git-tree-sha1 = "34cc045dd0aaa59b8bbe86c644679bc57f1d5bd0" +uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" +version = "0.6.8" + +[[deps.TranscodingStreams]] +git-tree-sha1 = "a09c933bebed12501890d8e92946bbab6a1690f1" +uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" +version = "0.10.5" +weakdeps = ["Random", "Test"] + + [deps.TranscodingStreams.extensions] + TestExt = ["Test", "Random"] + +[[deps.TriplotBase]] +git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" +uuid = "981d1d27-644d-49a2-9326-4793e63143c3" +version = "0.1.0" + +[[deps.TupleTools]] +git-tree-sha1 = "41d61b1c545b06279871ef1a4b5fcb2cac2191cd" +uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" +version = "1.5.0" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[deps.UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.UnicodeFun]] +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" +uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" +version = "0.4.1" + +[[deps.VTKBase]] +git-tree-sha1 = "c2d0db3ef09f1942d08ea455a9e252594be5f3b6" +uuid = "4004b06d-e244-455f-a6ce-a5f9919cc534" +version = "1.0.1" + +[[deps.WoodburyMatrices]] +deps = ["LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511" +uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6" +version = "1.0.0" + +[[deps.WriteVTK]] +deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] +git-tree-sha1 = "17877c404fd20090e3998a66f6f44cf01e2b1e60" +uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192" +version = "1.18.3" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] +git-tree-sha1 = "07e470dabc5a6a4254ffebc29a1b3fc01464e105" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.12.5+0" + +[[deps.XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.34+0" + +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.8.6+0" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.11+0" + +[[deps.Xorg_libXcursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" +uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" +version = "1.2.0+4" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.4+0" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.4+4" + +[[deps.Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "5.0.3+4" + +[[deps.Xorg_libXi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" +uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" +version = "1.7.10+4" + +[[deps.Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] +git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.4+4" + +[[deps.Xorg_libXrandr_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" +uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" +version = "1.5.2+4" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.10+4" + +[[deps.Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.1+0" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "b4bfde5d5b652e22b9c790ad00af08b6d042b97d" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.15.0+0" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.5.0+0" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+1" + +[[deps.isoband_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" +uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" +version = "0.2.3+0" + +[[deps.libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" +uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" +version = "3.4.0+0" + +[[deps.libass_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47" +uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" +version = "0.15.1+0" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.8.0+1" + +[[deps.libfdk_aac_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" +uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" +version = "2.0.2+0" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "d7015d2e18a5fd9a4f47de711837e980519781a4" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.43+1" + +[[deps.libsixel_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] +git-tree-sha1 = "d4f63314c8aa1e48cd22aa0c17ed76cd1ae48c3c" +uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" +version = "1.10.3+0" + +[[deps.libvorbis_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] +git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" +uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" +version = "1.3.7+1" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.52.0+1" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+2" + +[[deps.x264_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2" +uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" +version = "2021.5.5+0" + +[[deps.x265_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9" +uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" +version = "3.5.0+0" diff --git a/visualization/Project.toml b/visualization/Project.toml new file mode 100644 index 0000000000..c5b64e5103 --- /dev/null +++ b/visualization/Project.toml @@ -0,0 +1,4 @@ +[deps] +Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" +FerriteViz = "59d0093e-b1f1-4fb7-ac85-ab57e45f39d9" +GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" diff --git a/visualization/reference_cell_ip.jl b/visualization/reference_cell_ip.jl new file mode 100644 index 0000000000..b43cc6a8a0 --- /dev/null +++ b/visualization/reference_cell_ip.jl @@ -0,0 +1,85 @@ +using Ferrite +import FerriteViz as Viz +import GLMakie as Plt + + +function refshape_shapevalues(ip::VectorInterpolation{3, RefHexahedron}, shape_nr; npoints = 5) + x, y, z, u, v, w = (zeros(npoints^3) for _ in 1:6) + idx = 1 + for i in 1:npoints + for j in 1:npoints + for k in 1:npoints + x[idx], y[idx], z[idx] = (i-1, j-1, k-1) .* (2/(npoints-1)) .- 1 + u[idx], v[idx], w[idx] = shape_value(ip, Vec((x[idx], y[idx], z[idx])), shape_nr) + idx += 1 + end + end + end + return x, y, z, u, v, w +end + +refcell(::Type{RefHexahedron}) = Hexahedron(ntuple(i->i, 8)) +refcell(::Type{RefTetrahedron}) = Tetrahedron(ntuple(i->i, 4)) +refip(::Type{RefShape}) where {RefShape <: Ferrite.AbstractRefShape} = Lagrange{RefShape,1}() + + +function plot_refcell(::Type{RefShape}; kwargs...) where {RefShape <: Ferrite.AbstractRefShape{3}} + fig = Plt.Figure() + ax = Plt.Axis3(fig[1,1]; xlabel="ξ₁", ylabel="ξ₂", zlabel="ξ₃") + plot_refcell!(ax, RefShape; kwargs...) + return fig, ax +end + +function plot_refcell!(ax, ::Type{RefShape}; vertices=true, edges=true, faces=true) where {RefShape <: Ferrite.AbstractRefShape{3}} + plot_vertices!(ax, RefShape; label=vertices) + plot_edges!(ax, RefShape; label=edges) + plot_faces!(ax, RefShape; label=faces) + return ax +end + +function plot_vertices!(ax, ::Type{RefShape}; label) where {RefShape <: Ferrite.AbstractRefShape{3}} + ξ = Ferrite.reference_coordinates(refip(RefShape)) + ξ1, ξ2, ξ3 = (getindex.(ξ, i) for i in 1:3) + Plt.scatter!(ax, ξ1, ξ2, ξ3) + if label + Plt.text!(ax, ξ1, ξ2, ξ3; text=[string(i) for i in 1:length(ξ)]) + end + return ax +end + +function plot_edges!(ax, ::Type{RefShape}; label) where {RefShape <: Ferrite.AbstractRefShape{3}} + arrowheadpos = 2/3 + cell = refcell(RefShape) + ξ = Ferrite.reference_coordinates(refip(RefShape)) + x, y, z, u, v, w = (zeros(length(Ferrite.edges(cell))) for _ in 1:6) + for (k, e) in enumerate(Ferrite.edges(cell)) + ξa, ξb = getindex.((ξ,), e) + Plt.lines!(ax, ([ξa[i], ξb[i]] for i in 1:3)...) + x[k], y[k], z[k] = ξa + u[k], v[k], w[k] = ξb - ξa + end + Plt.arrows!(ax, x, y, z, u * arrowheadpos, v * arrowheadpos, w * arrowheadpos; linewidth=0.05, arrowsize=0.1) + if label + s = arrowheadpos + (1-arrowheadpos)/6 + Plt.text!(ax, x + u*s, y + v*s, z + w*s; text=[string(i) for i in 1:length(x)]) + end +end + +plot_faces!(ax, RefShape; label) = nothing + +function testit(nshape=1) + ip = Nedelec{3,RefHexahedron,1}(); + + fig, ax = plot_refcell(RefHexahedron; vertices=true, edges=true); + vals = refshape_shapevalues(ip, nshape) + lengths = sqrt.(vals[4].^2 + vals[5].^2 + vals[6].^2) + Plt.arrows!(ax, vals...; lengthscale=0.1, arrowsize=0.1, color=lengths) + return fig +end + +# Possible tests +#= +1) Check shape_value(ip, ξ, i) ⋅ v_edge[i] = |shape_value(ip, ξ, i)| (checks alignment) +2) Check ∫ Ni ⋅ v dL = 1 on each edge +3) Check shape_value(ip, ξ, i) ⋅ v_edge[j] = 0 for i≠j +=# \ No newline at end of file From 876bea96747685138e9011f4dafe2176914461ed Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 22 Mar 2024 15:03:05 +0100 Subject: [PATCH 127/172] Disable JET testing on nightly --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 72f7d71e53..b8aadba69c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,7 +17,7 @@ if HAS_EXTENSIONS && MODULE_CAN_BE_TYPE_PARAMETER import Metis end -const RUN_JET_TESTS = VERSION >= v"1.9" +const RUN_JET_TESTS = (VERSION >= v"1.9" && isempty(VERSION.prerelease)) # Temporarily disable on nightly since JET fails if RUN_JET_TESTS using JET: @test_call From a2819946ec4675ce6b0428c0f21edb963fc196e4 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 22 Mar 2024 20:02:03 +0100 Subject: [PATCH 128/172] Reenable JET, doesn't help anyways --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index b8aadba69c..72f7d71e53 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -17,7 +17,7 @@ if HAS_EXTENSIONS && MODULE_CAN_BE_TYPE_PARAMETER import Metis end -const RUN_JET_TESTS = (VERSION >= v"1.9" && isempty(VERSION.prerelease)) # Temporarily disable on nightly since JET fails +const RUN_JET_TESTS = VERSION >= v"1.9" if RUN_JET_TESTS using JET: @test_call From 230864ae6b26afd802251ea4bb629b2cff0c7737 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Fri, 22 Mar 2024 21:35:23 +0100 Subject: [PATCH 129/172] Add grid pertubations to interpolation tests --- test/test_interpolations.jl | 59 +++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index d470084e8d..718b99a9a6 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -226,6 +226,37 @@ @test Ferrite.is_discontinuous(d_ip_t) == true end + +tupleshift(t::NTuple{N}, shift::Int) where N = ntuple(i -> t[mod(i - 1 - shift, N) + 1], N) +#tupleshift(t::NTuple, shift::Int) = tuple(circshift(SVector(t), shift)...) +cell_permutations(cell::Quadrilateral) = (Quadrilateral(tupleshift(cell.nodes, shift)) for shift in 0:3) +cell_permutations(cell::Triangle) = ( Triangle(tupleshift(cell.nodes, shift)) for shift in 0:2) +cell_permutations(cell::QuadraticTriangle) = (QuadraticTriangle((tupleshift(cell.nodes[1:3], shift)..., tupleshift(cell.nodes[4:6], shift)...)) for shift in 0:3) + +function cell_permutations(cell::Hexahedron) + idx = ( #Logic on refshape: Select 1st and 2nd vertex (must be neighbours) + # The next follows to create inward vector with RHR, and then 4th is in same plane. + # The last four must be the neighbours on the other plane to the first four (same order) + (1,2,3,4,5,6,7,8), (1,4,8,5,2,3,7,6), (1,5,6,2,4,8,7,3), + (2,1,5,6,3,4,8,7), (2,3,4,1,6,7,8,5), (2,6,7,3,1,5,8,4), + (3,2,6,7,4,1,5,8), (3,4,1,2,7,8,5,6), (3,7,8,4,2,6,5,1), + (4,1,2,3,8,5,6,7), (4,3,7,8,1,2,6,5), (4,8,5,1,3,7,6,1), + (5,1,4,8,6,2,3,7), (5,6,2,1,8,7,3,4), (5,8,7,6,1,4,3,2), + (6,2,1,5,7,3,4,8), (6,5,8,7,2,1,4,3), (6,7,3,2,5,8,4,1), + (7,3,2,6,8,4,1,5), (7,6,5,8,3,2,1,4), (7,8,4,3,6,5,1,2), + (8,4,3,7,5,1,2,6), (8,5,1,4,7,6,2,3), (8,7,6,5,4,3,2,1), + ) + return (Hexahedron(ntuple(i -> cell.nodes[perm[i]], 8)) for perm in idx) +end + +function cell_permutations(cell::Tetrahedron) + idx = ( (1,2,3,4), (1,3,4,2), (1,4,2,3), + (2,1,4,3), (2,3,1,4), (2,4,3,1), + (3,1,2,4), (3,2,4,1), (3,4,1,2), + (4,1,3,2), (4,3,2,1), (4,2,1,3)) + return (Tetrahedron(ntuple(i -> cell.nodes[perm[i]], 4)) for perm in idx) +end + @testset "Hcurl and Hdiv" begin include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) import .InterpolationTestUtils as ITU @@ -242,26 +273,30 @@ end # for 2nd order elements. transfun(x) = typeof(x)(i->sinpi(x[mod(i, length(x))+1]+i/3))/10 transform_coordinates!(grid, x->(x + transfun(x))) - RefShape = Ferrite.getrefshape(getcells(grid, 1)) + cellnr = getncells(grid)÷2 + 1 # Should be a cell in the center + basecell = getcells(grid, cellnr) + RefShape = Ferrite.getrefshape(basecell) for order in (1, 2) for IPT in (Nedelec, RaviartThomas) dim == 3 && order > 1 && continue IPT == RaviartThomas && (dim == 3 || order > 1) && continue - IPT == RaviartThomas && (CT == Hexahedron) && continue + IPT == RaviartThomas && (RefShape == RefHexahedron) && continue transformation_function=transformation_functions[IPT] ip = IPT{dim,RefShape,order}() @testset "$CT, $ip" begin - dh = DofHandler(grid) - add!(dh, :u, ip) - close!(dh) - cellnr = getncells(grid)÷2 + 1 # Should be a cell in the center - for facenr in 1:nfaces(RefShape) - fi = FaceIndex(cellnr, facenr) - # Check continuity of tangential function value - ITU.test_continuity(dh, fi; transformation_function) + for testcell in cell_permutations(basecell) + grid.cells[cellnr] = testcell + dh = DofHandler(grid) + add!(dh, :u, ip) + close!(dh) + for facenr in 1:nfaces(RefShape) + fi = FaceIndex(cellnr, facenr) + # Check continuity of tangential function value + ITU.test_continuity(dh, fi; transformation_function) + end + # Check gradient calculation + ITU.test_gradient(dh, cellnr) end - # Check gradient calculation - ITU.test_gradient(dh, cellnr) end end end From f2278e0f3eb3e226ebca88ca0573777bfa9755fa Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Sat, 23 Mar 2024 00:07:56 +0100 Subject: [PATCH 130/172] Add tests for basic properties and fullfill them --- src/interpolations.jl | 31 +++++++++++------------ test/test_interpolations.jl | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/interpolations.jl b/src/interpolations.jl index 087b1dcf1b..04236b2fa6 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1789,37 +1789,34 @@ but test cases need to be developed first to make sure. No point in implementing guesses that cannot be verified... =# -# https://defelement.com/elements/examples/hexahedron-nedelec1-lagrange-1.html -# TODO: Need to "map" from defelement [0,1] refcell to Ferrite [-1,1], which -# for Nedelec should be scaling with J^-T where J=2I, i.e. divide by 2 -# For RT elements with Hdiv, this should instead be J/det(J) => 2/2^dim, -# i.e. also divide by 2 in 3d, no scaling in 2d, and multiply by 2 in 1d. +# https://defelement.com/elements/examples/hexahedron-nedelec1-lagrange-1.html +# Note: Divide by 2 since J=2I compared to DefElement's reference shape, and mapping is N ⋅ J^-T function shape_value(ip::Nedelec{3,RefHexahedron,1}, ξ::Vec{3,T}, i::Int) where T x, y, z = (ξ + ones(ξ))/2 # Edge 1 (defelement 0, positive) - i == 1 && return Vec(y*z - y - z + 1, zero(T), zero(T)) + i == 1 && return Vec(y*z - y - z + 1, zero(T), zero(T))/2 # Edge 2 (defelement 3, positive) - i == 2 && return Vec(zero(T), x * (1 - z), zero(T)) + i == 2 && return Vec(zero(T), x * (1 - z), zero(T))/2 # Edge 3 (defelement 5, negative) - i == 3 && return Vec(-y*(1-z), zero(T), zero(T)) + i == 3 && return Vec(-y*(1-z), zero(T), zero(T))/2 # Edge 4 (defelement 1, negative) - i == 4 && return Vec(zero(T), - x*z + x + z - 1, zero(T)) + i == 4 && return Vec(zero(T), - x*z + x + z - 1, zero(T))/2 # Edge 5 (defelement 8, positive) - i == 5 && return Vec(z*(1-y), zero(T), zero(T)) + i == 5 && return Vec(z*(1-y), zero(T), zero(T))/2 # Edge 6 (defelement 10, positive) - i == 6 && return Vec(zero(T), x * z, zero(T)) + i == 6 && return Vec(zero(T), x * z, zero(T))/2 # Edge 7 (defelement 11, negative) - i == 7 && return Vec(- y*z, zero(T), zero(T)) + i == 7 && return Vec(- y*z, zero(T), zero(T))/2 # Edge 8 (defelement 9, negative) - i == 8 && return Vec(zero(T), - z * (1 - x), zero(T)) + i == 8 && return Vec(zero(T), - z * (1 - x), zero(T))/2 # Edge 9 (defelement 2, positive) - i == 9 && return Vec(zero(T), zero(T), x*y - x - y + 1) + i == 9 && return Vec(zero(T), zero(T), x*y - x - y + 1)/2 # Edge 10 (defelement 4, positive) - i == 10 && return Vec(zero(T), zero(T), x * (1 - y)) + i == 10 && return Vec(zero(T), zero(T), x * (1 - y))/2 # Edge 11 (defelement 7, positive) - i == 11 && return Vec(zero(T), zero(T), x * y) + i == 11 && return Vec(zero(T), zero(T), x * y)/2 # Edge 12 (defelement 6, positive) - i == 12 && return Vec(zero(T), zero(T), y * (1 - x)) + i == 12 && return Vec(zero(T), zero(T), y * (1 - x))/2 throw(ArgumentError("no shape function $i for interpolation $ip")) end diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 718b99a9a6..6581649668 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -226,6 +226,56 @@ @test Ferrite.is_discontinuous(d_ip_t) == true end +reference_cell(::Type{RefTriangle}) = Triangle((1,2,3)) +reference_cell(::Type{RefQuadrilateral}) = Quadrilateral((1,2,3,4)) +reference_cell(::Type{RefTetrahedron}) = Tetrahedron((1,2,3,4)) +reference_cell(::Type{RefHexahedron}) = Hexahedron((ntuple(identity, 8))) + +function line_integral(qr::QuadratureRule{RefLine, T}, ip, shape_nr, x0, Δx, L, v, f) where T + s = zero(T) + for (ξ1d, w) in zip(Ferrite.getpoints(qr), Ferrite.getweights(qr)) + ξ = x0 + (ξ1d[1]/2) * Δx + s += (shape_value(ip, ξ, shape_nr) ⋅ v) * (w*L/2) * f((ξ1d[1]+1)/2) + end + return s +end + +# Required properties of shape value Nⱼ of an edge-elements (Hcurl) on an edge with direction v, length L, and dofs ∈ 𝔇 +# 1) Unit property: ∑_{j∈𝔇} ∫(Nⱼ ⋅ v f(s) dS) = 1 +# Where f(s) = 1 for linear interpolation and f(s)=1-s and f(s)=s for 2nd order interpolation (first and second shape function) +# And s is the path parameter ∈[0,1] along the positive direction of the path. +# 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 +@testset "Nedelec" begin + lineqr = QuadratureRule{RefLine}(20) + for ip in (Nedelec{2,RefTriangle,1}(), Nedelec{2,RefTriangle,2}(), Nedelec{3,RefTetrahedron,1}(), Nedelec{3,RefHexahedron,1}()) + cell = reference_cell(getrefshape(ip)) + edges = Ferrite.getdim(ip) == 2 ? Ferrite.faces(cell) : Ferrite.edges(cell) + dofs = Ferrite.getdim(ip) == 2 ? Ferrite.facedof_interior_indices(ip) : Ferrite.edgedof_interior_indices(ip) + x = Ferrite.reference_coordinates(Ferrite.default_interpolation(typeof(cell))) + @testset "$(getrefshape(ip)), order=$(Ferrite.getorder(ip))" begin + for (edge_nr, (i1, i2)) in enumerate(edges) + Δx = x[i2]-x[i1] + x0 = (x[i1]+x[i2])/2 + L = norm(Δx) + v = Δx/L + for (idof, shape_nr) in enumerate(dofs[edge_nr]) + nedgedofs = length(dofs[edge_nr]) + f(x) = nedgedofs == 1 ? 1.0 : (idof == 1 ? 1-x : x) + s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, f) + @test s ≈ one(s) + end + for (j_edge, shape_nrs) in enumerate(dofs) + j_edge == edge_nr && continue + for shape_nr in shape_nrs + for ξ in (x[i1] + r*Δx for r in [0.0, rand(3)..., 1.0]) + @test abs(shape_value(ip, ξ, shape_nr) ⋅ v) < eps()*100 + end + end + end + end + end + end +end tupleshift(t::NTuple{N}, shift::Int) where N = ntuple(i -> t[mod(i - 1 - shift, N) + 1], N) #tupleshift(t::NTuple, shift::Int) = tuple(circshift(SVector(t), shift)...) From 4567f0c2e6eeb6a68ad7bc79b0ec073fc280bac5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Mon, 19 Aug 2024 12:26:47 +0200 Subject: [PATCH 131/172] Fix merge errors --- .../literate-tutorials/heat_equation_rt.jl | 48 ++++----- .../heat_equation_triangle.jl | 4 +- src/interpolations.jl | 97 +++++++++---------- src/iterators.jl | 2 +- test/InterpolationTestUtils.jl | 70 ++++++------- test/test_interpolations.jl | 22 ++--- 6 files changed, 120 insertions(+), 123 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl index 25bcb5999b..3d6fb2cacf 100644 --- a/docs/src/literate-tutorials/heat_equation_rt.jl +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -7,7 +7,7 @@ # \boldsymbol{q}\cdot \boldsymbol{n} = q_n \in \Gamma_\mathrm{N}\\ # u = u_\mathrm{D} \in \Gamma_\mathrm{D} # ``` -# +# # ## Weak form # ### Part 1 # ```math @@ -15,13 +15,13 @@ # \int_{\Gamma} \delta u \boldsymbol{n} \cdot \boldsymbol{q}\ \mathrm{d}\Gamma - # \int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ # ``` -# +# # ### Part 2 # ```math # \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega = - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega # ``` -# where no Green-Gauss theorem is applied. -# +# where no Green-Gauss theorem is applied. +# # ### Summary # The weak form becomes, find $u\in H^1$ and $\boldsymbol{q} \in H\mathrm{(div)}$, such that # ```math @@ -34,7 +34,7 @@ # \quad \forall\ \boldsymbol{\delta q} \in \delta H\mathrm{(div)} # \end{align*} # ``` -# +# # ## Commented Program # # Now we solve the problem in Ferrite. What follows is a program spliced with comments. @@ -56,11 +56,11 @@ grid = generate_grid(Triangle, (20, 20)); # based on the two-dimensional reference quadrilateral. We also define a quadrature rule based on # the same reference element. We combine the interpolation and the quadrature rule # to a `CellValues` object. -ip_geo = Ferrite.default_interpolation(getcelltype(grid)) +ip_geo = geometric_interpolation(getcelltype(grid)) ipu = Lagrange{RefTriangle, 1}() # Why does it "explode" for 2nd order ipu? ipq = RaviartThomas{2,RefTriangle,1}() qr = QuadratureRule{RefTriangle}(2) -cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) +cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) # ### Degrees of freedom # Next we need to define a `DofHandler`, which will take care of numbering @@ -77,7 +77,7 @@ close!(dh); # Now that we have distributed all our dofs we can create our tangent matrix, # using `create_sparsity_pattern`. This function returns a sparse matrix # with the correct entries stored. -K = create_sparsity_pattern(dh) +K = allocate_matrix(dh) # ### Boundary conditions # In Ferrite constraints like Dirichlet boundary conditions @@ -86,12 +86,12 @@ 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 boundary facet sets. ∂Ω = union( - getfaceset(grid, "left"), - getfaceset(grid, "right"), - getfaceset(grid, "top"), - getfaceset(grid, "bottom"), + getfacetset(grid, "left"), + getfacetset(grid, "right"), + getfacetset(grid, "top"), + getfacetset(grid, "bottom"), ); # Now we are set up to define our constraint. We specify which field @@ -241,12 +241,12 @@ u = K \ f; # to a VTK-file, which can be viewed in e.g. [ParaView](https://www.paraview.org/). u_nodes = evaluate_at_grid_nodes(dh, u, :u) ∂Ω_cells = zeros(Int, getncells(grid)) -for (cellnr, facenr) in ∂Ω +for (cellnr, _) in ∂Ω ∂Ω_cells[cellnr] = 1 end -vtk_grid("heat_equation_rt", dh) do vtk - vtk_point_data(vtk, u_nodes, "u") - vtk_cell_data(vtk, ∂Ω_cells, "dO") +VTKGridFile("heat_equation_rt", dh) do vtk + write_node_data(vtk, u_nodes, "u") + write_cell_data(vtk, ∂Ω_cells, "dO") end @show norm(u_nodes)/length(u_nodes) @@ -254,10 +254,10 @@ end # ## Postprocess the total flux function calculate_flux(dh, dΩ, ip, a) grid = dh.grid - qr = FaceQuadratureRule{RefTriangle}(4) - ip_geo = Ferrite.default_interpolation(getcelltype(grid)) - fv = FaceValues(qr, ip, ip_geo) - + qr = FacetQuadratureRule{RefTriangle}(4) + ip_geo = geometric_interpolation(getcelltype(grid)) + fv = FacetValues(qr, ip, ip_geo) + dofrange = dof_range(dh, :q) flux = 0.0 dofs = celldofs(dh, 1) @@ -281,9 +281,9 @@ end function calculate_flux_lag(dh, dΩ, ip, a) grid = dh.grid - qr = FaceQuadratureRule{RefTriangle}(4) - ip_geo = Ferrite.default_interpolation(getcelltype(grid)) - fv = FaceValues(qr, ip, ip_geo) + qr = FacetQuadratureRule{RefTriangle}(4) + ip_geo = geometric_interpolation(getcelltype(grid)) + fv = FacetValues(qr, ip, ip_geo) dofrange = dof_range(dh, :u) flux = 0.0 dofs = celldofs(dh, 1) diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl index 414a7f9195..28234d4ade 100644 --- a/docs/src/literate-tutorials/heat_equation_triangle.jl +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -216,8 +216,8 @@ end # ### Postprocessing the total flux function calculate_flux_lag(dh, dΩ, ip, a) - qr = FaceQuadratureRule{RefTriangle}(2) - fv = FaceValues(qr, ip, Lagrange{RefTriangle,1}()) + qr = FacetQuadratureRule{RefTriangle}(2) + fv = FacetValues(qr, ip, Lagrange{RefTriangle,1}()) grid = dh.grid dofrange = dof_range(dh, :u) flux = 0.0 diff --git a/src/interpolations.jl b/src/interpolations.jl index a29f4e0ba7..4086ef8b66 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -253,7 +253,7 @@ Optimized version combining the evaluation [`Ferrite.shape_value(::Interpolation [`Ferrite.shape_gradient(::Interpolation)`](@ref), and the gradient of the latter. """ function shape_hessian_gradient_and_value(ip::Interpolation, ξ::Vec, i::Int) - return hessian(x -> shape_value(ip, x, i), ξ, :all) + return hessian(x -> reference_shape_value(ip, x, i), ξ, :all) end """ @@ -1739,7 +1739,7 @@ n_dbc_components(::RaviartThomas) = 1 # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-1.html # Signs changed when needed to make positive direction outwards -function shape_value(ip::RaviartThomas{2,RefTriangle,1}, ξ::Vec{2}, i::Int) +function reference_shape_value(ip::RaviartThomas{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) x, y = ξ i == 1 && return ξ # Flip sign i == 2 && return Vec(x-1, y) # Keep sign @@ -1747,13 +1747,13 @@ function shape_value(ip::RaviartThomas{2,RefTriangle,1}, ξ::Vec{2}, i::Int) throw(ArgumentError("no shape function $i for interpolation $ip")) end -getnbasefunctions(::RaviartThomas{2,RefTriangle,1}) = 3 -facedof_interior_indices(::RaviartThomas{2,RefTriangle,1}) = ((1,), (2,), (3,)) +getnbasefunctions(::RaviartThomas{2, RefTriangle, 1}) = 3 +edgedof_interior_indices(::RaviartThomas{2, RefTriangle, 1}) = ((1,), (2,), (3,)) adjust_dofs_during_distribution(::RaviartThomas) = false function get_direction(::RaviartThomas{2,RefTriangle,1}, j, cell) - face_vertices = faces(cell)[j] - return face_vertices[2] > face_vertices[1] ? 1 : -1 + edge = edges(cell)[j] + return ifelse(edge[2] > edge[1], 1, -1) end # RefTriangle, 2st order Lagrange @@ -1767,11 +1767,11 @@ Vertex numbers: | Vertex coordinates: | 3-------1 | +--> ξ₁ | ----------------+-------------------- -Face numbers: | Face identifiers: +Edge numbers: | Edge identifiers: + | - | \ | f1: (v1, v2) - 2 1 | f2: (v2, v3) - | \ | f3: (v3, v1) + | \ | e1: (v1, v2) + 2 1 | e2: (v2, v3) + | \ | e3: (v3, v1) +---3---+ | ----------------+-------------------- ``` @@ -1780,7 +1780,7 @@ RefTriangle =# # https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-2.html # Signs changed when needed to make positive direction outwards -function shape_value(ip::RaviartThomas{2,RefTriangle,2}, ξ::Vec{2}, i::Int) +function reference_shape_value(ip::RaviartThomas{2, RefTriangle, 2}, ξ::Vec{2}, i::Int) x, y = ξ # Face 1 (keep ordering, flip sign) i == 1 && return Vec(4x*(2x-1), 2y*(4x-1)) @@ -1798,18 +1798,16 @@ function shape_value(ip::RaviartThomas{2,RefTriangle,2}, ξ::Vec{2}, i::Int) end getnbasefunctions(::RaviartThomas{2,RefTriangle,2}) = 8 -facedof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = ((1, 2), (3, 4), (5, 6)) -celldof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = (7,8) +edgedof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = ((1, 2), (3, 4), (5, 6)) +volumedof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = (7,8) adjust_dofs_during_distribution(::RaviartThomas{2,RefTriangle,2}) = true -function get_direction(::RaviartThomas{2,RefTriangle,2}, j, cell) - j>6 && return 1 - facenr = (j+1)÷2 - face_vertices = faces(cell)[facenr] - return face_vertices[2] > face_vertices[1] ? 1 : -1 +function get_direction(::RaviartThomas{2, RefTriangle, 2}, j, cell) + j > 6 && return 1 + edge = edges(cell)[(j + 1) ÷ 2] + return ifelse(edge[2] > edge[1], 1, -1) end - ##################################### # Nedelec (1st kind), H(curl) # ##################################### @@ -1820,7 +1818,7 @@ dirichlet_facedof_indices(ip::Nedelec) = facedof_interior_indices(ip) n_dbc_components(::Nedelec) = 1 # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html -function shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) +function reference_shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) x, y = ξ i == 1 && return Vec( - y, x) i == 2 && return Vec( - y, x - 1) # Changed signed, follow Ferrite's sign convention @@ -1828,18 +1826,18 @@ function shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) throw(ArgumentError("no shape function $i for interpolation $ip")) end -getnbasefunctions(::Nedelec{2,RefTriangle,1}) = 3 -facedof_interior_indices(::Nedelec{2,RefTriangle,1}) = ((1,), (2,), (3,)) -adjust_dofs_during_distribution(::Nedelec{2,RefTriangle,1}) = false +getnbasefunctions(::Nedelec{2, RefTriangle, 1}) = 3 +edgedof_interior_indices(::Nedelec{2, RefTriangle, 1}) = ((1,), (2,), (3,)) +adjust_dofs_during_distribution(::Nedelec{2, RefTriangle, 1}) = false -function get_direction(::Nedelec{2,RefTriangle,1}, j, cell) - face_vertices = faces(cell)[j] - return face_vertices[2] > face_vertices[1] ? 1 : -1 +function get_direction(::Nedelec{2, RefTriangle, 1}, j, cell) + edge = edges(cell)[j] + return ifelse(edge[2] > edge[1], 1, -1) end # RefTriangle, 2nd order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-2.html -function shape_value(ip::Nedelec{2,RefTriangle,2}, ξ::Vec{2}, i::Int) +function reference_shape_value(ip::Nedelec{2,RefTriangle,2}, ξ::Vec{2}, i::Int) x, y = ξ # Face 1 i == 1 && return Vec( 2*y*(1 - 4*x), @@ -1864,20 +1862,19 @@ function shape_value(ip::Nedelec{2,RefTriangle,2}, ξ::Vec{2}, i::Int) throw(ArgumentError("no shape function $i for interpolation $ip")) end -getnbasefunctions(::Nedelec{2,RefTriangle,2}) = 8 -facedof_interior_indices(::Nedelec{2,RefTriangle,2}) = ((1,2), (3,4), (5,6)) -celldof_interior_indices(::Nedelec{2,RefTriangle,2}) = (7,8) -adjust_dofs_during_distribution(::Nedelec{2,RefTriangle,2}) = true +getnbasefunctions(::Nedelec{2, RefTriangle, 2}) = 8 +edgedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = ((1,2), (3,4), (5,6)) +volumedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = (7,8) +adjust_dofs_during_distribution(::Nedelec{2, RefTriangle, 2}) = true -function get_direction(::Nedelec{2,RefTriangle,2}, j, cell) - j>6 && return 1 - facenr = (j+1)÷2 - face_vertices = faces(cell)[facenr] - return face_vertices[2] > face_vertices[1] ? 1 : -1 +function get_direction(::Nedelec{2, RefTriangle, 2}, j, cell) + j > 6 && return 1 + edge = edges(cell)[(j + 1) ÷ 2] + return ifelse(edge[2] > edge[1], 1, -1) end # https://defelement.com/elements/examples/tetrahedron-nedelec1-lagrange-1.html -function shape_value(ip::Nedelec{3,RefTetrahedron,1}, ξ::Vec{3,T}, i::Int) where T +function reference_shape_value(ip::Nedelec{3, RefTetrahedron, 1}, ξ::Vec{3, T}, i::Int) where T x, y, z = ξ # Edge 1 (defelement 5, positive) i == 1 && return Vec(- y - z + 1, x, x) @@ -1895,13 +1892,13 @@ function shape_value(ip::Nedelec{3,RefTetrahedron,1}, ξ::Vec{3,T}, i::Int) wher throw(ArgumentError("no shape function $i for interpolation $ip")) end -getnbasefunctions(::Nedelec{3,RefTetrahedron,1}) = 6 -edgedof_interior_indices(::Nedelec{3,RefTetrahedron,1}) = ntuple(i->(i,), 6) -adjust_dofs_during_distribution(::Nedelec{3,RefTetrahedron,1}) = false +getnbasefunctions(::Nedelec{3, RefTetrahedron, 1}) = 6 +edgedof_interior_indices(::Nedelec{3, RefTetrahedron, 1}) = ntuple(i->(i,), 6) +adjust_dofs_during_distribution(::Nedelec{3, RefTetrahedron, 1}) = false -function get_direction(::Nedelec{3,RefTetrahedron,1}, j, cell) - edge_vertices = edges(cell)[j] - return edge_vertices[2] > edge_vertices[1] ? 1 : -1 +function get_direction(::Nedelec{3, RefTetrahedron, 1}, j, cell) + edge = edges(cell)[j] + return ifelse(edge[2] > edge[1], 1, -1) end # RefTetrahedron, 2nd order Lagrange @@ -1916,7 +1913,7 @@ No point in implementing guesses that cannot be verified... # https://defelement.com/elements/examples/hexahedron-nedelec1-lagrange-1.html # Note: Divide by 2 since J=2I compared to DefElement's reference shape, and mapping is N ⋅ J^-T -function shape_value(ip::Nedelec{3,RefHexahedron,1}, ξ::Vec{3,T}, i::Int) where T +function reference_shape_value(ip::Nedelec{3, RefHexahedron, 1}, ξ::Vec{3, T}, i::Int) where T x, y, z = (ξ + ones(ξ))/2 # Edge 1 (defelement 0, positive) i == 1 && return Vec(y*z - y - z + 1, zero(T), zero(T))/2 @@ -1946,11 +1943,11 @@ function shape_value(ip::Nedelec{3,RefHexahedron,1}, ξ::Vec{3,T}, i::Int) where throw(ArgumentError("no shape function $i for interpolation $ip")) end -getnbasefunctions(::Nedelec{3,RefHexahedron,1}) = 12 -edgedof_interior_indices(::Nedelec{3,RefHexahedron,1}) = ntuple(i->(i,), 12) -adjust_dofs_during_distribution(::Nedelec{3,RefHexahedron,1}) = false +getnbasefunctions(::Nedelec{3, RefHexahedron, 1}) = 12 +edgedof_interior_indices(::Nedelec{3, RefHexahedron, 1}) = ntuple(i->(i,), 12) +adjust_dofs_during_distribution(::Nedelec{3, RefHexahedron, 1}) = false -function get_direction(::Nedelec{3,RefHexahedron,1}, j, cell) - edge_vertices = edges(cell)[j] - return edge_vertices[2] > edge_vertices[1] ? 1 : -1 +function get_direction(::Nedelec{3, RefHexahedron, 1}, j, cell) + edge = edges(cell)[j] + return ifelse(edge[2] > edge[1], 1, -1) end diff --git a/src/iterators.jl b/src/iterators.jl index 0a05e0da5d..570a247c7e 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -92,7 +92,7 @@ end # TODO: Should we provide a fast path if the cell is not required for reinit? #reinit!(cv::CellValues, cc::CellCache) = reinit!(cv, cc.coords) reinit!(cv::CellValues, cc::CellCache) = reinit!(cv, getcells(cc.grid, cellid(cc)), cc.coords) -reinit!(fv::FaceValues, cc::CellCache, f::Int) = reinit!(fv, cc.coords, f) # TODO: Deprecate? +reinit!(fv::FacetValues, cc::CellCache, f::Int) = reinit!(fv, cc.coords, f) # TODO: Deprecate? # Accessor functions (TODO: Deprecate? We are so inconsistent with `getxx` vs `xx`...) diff --git a/test/InterpolationTestUtils.jl b/test/InterpolationTestUtils.jl index 563241e042..a983edacc9 100644 --- a/test/InterpolationTestUtils.jl +++ b/test/InterpolationTestUtils.jl @@ -4,85 +4,85 @@ module InterpolationTestUtils import LinearAlgebra: normalize import Random: randperm - function find_matching_face(grid, face::FaceIndex) - cell, facenr = face - face_vertices = Set(Ferrite.faces(getcells(grid, cell))[facenr]) + function find_matching_facet(grid, facet::FacetIndex) + cell, facetnr = facet + facet_vertices = Set(Ferrite.facets(getcells(grid, cell))[facetnr]) for cnr in 1:getncells(grid) cnr == cell && continue - for (i, f_vert) in enumerate(Ferrite.faces(getcells(grid, cnr))) - face_vertices == Set(f_vert) && return FaceIndex(cnr, i) + for (i, f_vert) in enumerate(Ferrite.facets(getcells(grid, cnr))) + facet_vertices == Set(f_vert) && return FacetIndex(cnr, i) end end return nothing end - - function test_continuity(dh::DofHandler, face::FaceIndex; + + function test_continuity(dh::DofHandler, facet::FacetIndex; transformation_function::Function=identity, value_function::Function=function_value) # transformation_function: (v,n) -> z - # Examples + # Examples # * Tangential continuity: fun(v, n) = v - (v ⋅ n)*n # * Normal continuity: fun(v, n) = v ⋅ n # value_function: (fe_v, q_point, ue) -> z - + # Check validity of input @assert length(dh.subdofhandlers) == 1 @assert length(Ferrite.getfieldnames(dh)) == 1 - + # Find the matching FaceIndex - cellnr, facenr = face - face2 = find_matching_face(dh.grid, face) - face2 === nothing && return false - - # Pick "random" points on the face + cellnr, facetnr = facet + facet2 = find_matching_facet(dh.grid, facet) + facet2 === nothing && return false + + # Pick "random" points on the facet cell = getcells(dh.grid, cellnr) RefShape = Ferrite.getrefshape(getcells(dh.grid, cellnr)) - ip_geo = Ferrite.default_interpolation(typeof(cell)) + ip_geo = geometric_interpolation(typeof(cell)) ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) - fqr = FaceQuadratureRule{RefShape}(10) - fv = FaceValues(fqr, ip_fun, ip_geo) + fqr = FacetQuadratureRule{RefShape}(8) + fv = FacetValues(fqr, ip_fun, ip_geo) cell_coords = getcoordinates(dh.grid, cellnr) inds = randperm(getnquadpoints(fv))[1:min(4, getnquadpoints(fv))] - - # Random dof vector to test continuity + + # Random dof vector to test continuity u = rand(ndofs(dh)) - + # Calculate coordinates and function values for these point_coords = zeros(eltype(cell_coords), length(inds)) point_normal = similar(point_coords) fun_vals = zeros(typeof(shape_value(fv, 1, 1)), length(inds)) - reinit!(fv, cell, cell_coords, facenr) + reinit!(fv, cell, cell_coords, facetnr) ue = u[celldofs(dh, cellnr)] for (i, q_point) in enumerate(inds) point_coords[i] = spatial_coordinate(fv, q_point, cell_coords) point_normal[i] = getnormal(fv, q_point) fun_vals[i] = value_function(fv, q_point, ue) end - + # Calculate function values on the other cell - cell2 = getcells(dh.grid, face2[1]) - cell_coords2 = getcoordinates(dh.grid, face2[1]) - local_coords = map(x->Ferrite.find_local_coordinate(ip_geo, cell_coords2, x), point_coords) + cell2 = getcells(dh.grid, facet2[1]) + cell_coords2 = getcoordinates(dh.grid, facet2[1]) + local_coords = map(x->Ferrite.find_local_coordinate(ip_geo, cell_coords2, x, Ferrite.NewtonLineSearchPointFinder()), point_coords) @assert all(first.(local_coords)) # check that find_local_coordinate converged ξs = collect(last.(local_coords)) # Extract the local coordinates qr = QuadratureRule{RefShape}(zeros(length(ξs)), ξs) cv = CellValues(qr, ip_fun, ip_geo) reinit!(cv, cell2, cell_coords2) fun_vals2 = similar(fun_vals) - ue2 = u[celldofs(dh, face2[1])] + ue2 = u[celldofs(dh, facet2[1])] for q_point in 1:getnquadpoints(cv) @assert spatial_coordinate(cv, q_point, cell_coords2) ≈ point_coords[q_point] fun_vals2[q_point] = value_function(cv, q_point, ue2) end - - d1 = map((v,n)->transformation_function(v,n), fun_vals, point_normal) + + d1 = map((v,n)->transformation_function(v,n), fun_vals, point_normal) d2 = map((v,n)->transformation_function(v,n), fun_vals2, point_normal) - @test isapprox(d1, d2; rtol=1e-6) # Approximate points can contribute to the inexactness + @test isapprox(d1, d2; rtol=1e-6) # Approximate points can contribute to the inexactness return true end - + function create_gradcheck_qr(ip_geo::Interpolation{RefShape}, ΔL) where RefShape - dim = Ferrite.getdim(ip_geo) + dim = Ferrite.getrefdim(ip_geo) xref = Ferrite.reference_coordinates(ip_geo) xc = sum(xref)/length(xref) ws = rand(length(xref))*((1-ΔL)/length(xref)) @@ -93,12 +93,12 @@ module InterpolationTestUtils qr_x = [xp, x1] return QuadratureRule{RefShape}(qr_w, qr_x) end - + function test_gradient(dh, cellnr; ΔL=1e-6) ue = rand(ndofs_per_cell(dh, cellnr)) x = getcoordinates(dh.grid, cellnr) cell = getcells(dh.grid, cellnr) - ip_geo = Ferrite.default_interpolation(typeof(cell)) + ip_geo = geometric_interpolation(typeof(cell)) ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) qr = create_gradcheck_qr(ip_geo, ΔL) cv = CellValues(qr, ip_fun, ip_geo) @@ -113,4 +113,4 @@ module InterpolationTestUtils return nothing end -end \ No newline at end of file +end diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index d22a1c9f28..0ef4483c50 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -229,11 +229,11 @@ reference_cell(::Type{RefQuadrilateral}) = Quadrilateral((1,2,3,4)) reference_cell(::Type{RefTetrahedron}) = Tetrahedron((1,2,3,4)) reference_cell(::Type{RefHexahedron}) = Hexahedron((ntuple(identity, 8))) -function line_integral(qr::QuadratureRule{RefLine, T}, ip, shape_nr, x0, Δx, L, v, f) where T - s = zero(T) +function line_integral(qr::QuadratureRule{RefLine}, ip, shape_nr, x0, Δx, L, v, f) + s = 0.0 for (ξ1d, w) in zip(Ferrite.getpoints(qr), Ferrite.getweights(qr)) ξ = x0 + (ξ1d[1]/2) * Δx - s += (shape_value(ip, ξ, shape_nr) ⋅ v) * (w*L/2) * f((ξ1d[1]+1)/2) + s += (reference_shape_value(ip, ξ, shape_nr) ⋅ v) * (w*L/2) * f((ξ1d[1]+1)/2) end return s end @@ -247,9 +247,9 @@ end lineqr = QuadratureRule{RefLine}(20) for ip in (Nedelec{2,RefTriangle,1}(), Nedelec{2,RefTriangle,2}(), Nedelec{3,RefTetrahedron,1}(), Nedelec{3,RefHexahedron,1}()) cell = reference_cell(getrefshape(ip)) - edges = Ferrite.getdim(ip) == 2 ? Ferrite.faces(cell) : Ferrite.edges(cell) - dofs = Ferrite.getdim(ip) == 2 ? Ferrite.facedof_interior_indices(ip) : Ferrite.edgedof_interior_indices(ip) - x = Ferrite.reference_coordinates(Ferrite.default_interpolation(typeof(cell))) + edges = Ferrite.edges(cell) + dofs = Ferrite.edgedof_interior_indices(ip) + x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) @testset "$(getrefshape(ip)), order=$(Ferrite.getorder(ip))" begin for (edge_nr, (i1, i2)) in enumerate(edges) Δx = x[i2]-x[i1] @@ -266,7 +266,7 @@ end j_edge == edge_nr && continue for shape_nr in shape_nrs for ξ in (x[i1] + r*Δx for r in [0.0, rand(3)..., 1.0]) - @test abs(shape_value(ip, ξ, shape_nr) ⋅ v) < eps()*100 + @test abs(reference_shape_value(ip, ξ, shape_nr) ⋅ v) < eps()*100 end end end @@ -313,7 +313,7 @@ end Nedelec=>(v,n)-> v - n*(v⋅n), # Hcurl (tangent continuity) RaviartThomas=>(v,n) -> v ⋅ n) # Hdiv (normal continuity) for CT in (Triangle, QuadraticTriangle, Tetrahedron, Hexahedron) - dim = Ferrite.getdim(CT) + dim = Ferrite.getrefdim(CT) # dim = sdim = rdim p1, p2 = (rand(Vec{dim}), ones(Vec{dim})+rand(Vec{dim})) grid = generate_grid(CT, ntuple(_->nel, dim), p1, p2) # Smoothly distort grid (to avoid spuriously badly deformed elements). @@ -330,15 +330,15 @@ end IPT == RaviartThomas && (dim == 3 || order > 1) && continue IPT == RaviartThomas && (RefShape == RefHexahedron) && continue transformation_function=transformation_functions[IPT] - ip = IPT{dim,RefShape,order}() + ip = IPT{dim, RefShape, order}() @testset "$CT, $ip" begin for testcell in cell_permutations(basecell) grid.cells[cellnr] = testcell dh = DofHandler(grid) add!(dh, :u, ip) close!(dh) - for facenr in 1:nfaces(RefShape) - fi = FaceIndex(cellnr, facenr) + for facetnr in 1:nfacets(RefShape) + fi = FacetIndex(cellnr, facetnr) # Check continuity of tangential function value ITU.test_continuity(dh, fi; transformation_function) end From d2a9699635c9b482cce15ca98f0f631a493392c1 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 29 Oct 2024 21:27:07 +0100 Subject: [PATCH 132/172] Add some refs --- .../literate-tutorials/heat_equation_rt.jl | 27 ++++++++++++++++++- .../heat_equation_triangle.jl | 14 +++++----- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl index 3d6fb2cacf..48f6fee6f4 100644 --- a/docs/src/literate-tutorials/heat_equation_rt.jl +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -1,4 +1,29 @@ # # [Heat equation (Mixed, RaviartThomas)](@id tutorial-heat-equation-rt) +# Note, there are a lot to consider here it seems like. Good ref, +# @book{Gatica2014, +# title = {A Simple Introduction to the Mixed Finite Element Method: Theory and Applications}, +# ISBN = {9783319036953}, +# ISSN = {2191-8201}, +# url = {http://dx.doi.org/10.1007/978-3-319-03695-3}, +# DOI = {10.1007/978-3-319-03695-3}, +# journal = {SpringerBriefs in Mathematics}, +# publisher = {Springer International Publishing}, +# author = {Gatica, Gabriel N.}, +# year = {2014} +# } +# See also, +# @book{Boffi2013, +# title = {Mixed Finite Element Methods and Applications}, +# ISBN = {9783642365195}, +# ISSN = {0179-3632}, +# url = {http://dx.doi.org/10.1007/978-3-642-36519-5}, +# DOI = {10.1007/978-3-642-36519-5}, +# journal = {Springer Series in Computational Mathematics}, +# publisher = {Springer Berlin Heidelberg}, +# author = {Boffi, Daniele and Brezzi, Franco and Fortin, Michel}, +# year = {2013} +# } +# for a(n even) more comprehensive book. # # ## Strong form # ```math @@ -58,7 +83,7 @@ grid = generate_grid(Triangle, (20, 20)); # to a `CellValues` object. ip_geo = geometric_interpolation(getcelltype(grid)) ipu = Lagrange{RefTriangle, 1}() # Why does it "explode" for 2nd order ipu? -ipq = RaviartThomas{2,RefTriangle,1}() +ipq = RaviartThomas{2,RefTriangle, 1}() qr = QuadratureRule{RefTriangle}(2) cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl index 28234d4ade..52d463b108 100644 --- a/docs/src/literate-tutorials/heat_equation_triangle.jl +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -67,7 +67,7 @@ close!(dh); # Now that we have distributed all our dofs we can create our tangent matrix, # using `create_sparsity_pattern`. This function returns a sparse matrix # with the correct entries stored. -K = create_sparsity_pattern(dh) +K = allocate_matrix(dh) # ### Boundary conditions # In Ferrite constraints like Dirichlet boundary conditions @@ -78,10 +78,10 @@ ch = ConstraintHandler(dh); # homogeneous Dirichlet boundary conditions on the whole boundary, i.e. # the `union` of all the face sets on the boundary. ∂Ω = union( - getfaceset(grid, "left"), - getfaceset(grid, "right"), - getfaceset(grid, "top"), - getfaceset(grid, "bottom"), + getfacetset(grid, "left"), + getfacetset(grid, "right"), + getfacetset(grid, "top"), + getfacetset(grid, "bottom"), ); # Now we are set up to define our constraint. We specify which field @@ -207,8 +207,8 @@ 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/). -vtk_grid("heat_equation", dh) do vtk - vtk_point_data(vtk, dh, u) +VTKGridFile("heat_equation", dh) do vtk + write_solution(vtk, dh, u) end @show norm(u)/length(u) From 35529ec242e0ef0d9239ab17be6e50e0b211e3f1 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Thu, 31 Oct 2024 18:47:19 +0100 Subject: [PATCH 133/172] Fix formatting using pre-commit --- visualization/reference_cell_ip.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/visualization/reference_cell_ip.jl b/visualization/reference_cell_ip.jl index b43cc6a8a0..d60e354674 100644 --- a/visualization/reference_cell_ip.jl +++ b/visualization/reference_cell_ip.jl @@ -77,9 +77,9 @@ function testit(nshape=1) return fig end -# Possible tests -#= +# Possible tests +#= 1) Check shape_value(ip, ξ, i) ⋅ v_edge[i] = |shape_value(ip, ξ, i)| (checks alignment) -2) Check ∫ Ni ⋅ v dL = 1 on each edge -3) Check shape_value(ip, ξ, i) ⋅ v_edge[j] = 0 for i≠j -=# \ No newline at end of file +2) Check ∫ Ni ⋅ v dL = 1 on each edge +3) Check shape_value(ip, ξ, i) ⋅ v_edge[j] = 0 for i≠j +=# From fa2a3176fa871617a82429ef80bdba77301e546f Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Fri, 1 Nov 2024 19:52:50 +0100 Subject: [PATCH 134/172] Started adding BrezziDouglasMarini --- docs/liveserver.jl | 2 +- .../literate-tutorials/heat_equation_rt.jl | 82 +- .../heat_equation_triangle.jl | 1 - src/interpolations.jl | 53 +- test/test_interpolations.jl | 53 +- .../2d_vector_interpolation_checkplots.jl | 23 + visualization/Manifest.toml | 978 ++++++++---------- visualization/Project.toml | 3 +- 8 files changed, 603 insertions(+), 592 deletions(-) create mode 100644 visualization/2d_vector_interpolation_checkplots.jl diff --git a/docs/liveserver.jl b/docs/liveserver.jl index a589e910c1..c1e83b4de9 100755 --- a/docs/liveserver.jl +++ b/docs/liveserver.jl @@ -14,7 +14,7 @@ push!(ARGS, "liveserver") # Run LiveServer.servedocs(...) import LiveServer LiveServer.servedocs(; - host = "0.0.0.0", + # host = "0.0.0.0", # Documentation root where make.jl and src/ are located foldername = joinpath(repo_root, "docs"), # Extra source folder to watch for changes diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl index 48f6fee6f4..2d22955038 100644 --- a/docs/src/literate-tutorials/heat_equation_rt.jl +++ b/docs/src/literate-tutorials/heat_equation_rt.jl @@ -1,5 +1,6 @@ # # [Heat equation (Mixed, RaviartThomas)](@id tutorial-heat-equation-rt) -# Note, there are a lot to consider here it seems like. Good ref, +# Note, there are a lot to consider here it seems like. Good refs, +# ``` # @book{Gatica2014, # title = {A Simple Introduction to the Mixed Finite Element Method: Theory and Applications}, # ISBN = {9783319036953}, @@ -24,54 +25,63 @@ # year = {2013} # } # for a(n even) more comprehensive book. -# -# ## Strong form -# ```math -# \nabla \cdot \boldsymbol{q} = h \in \Omega \\ -# \boldsymbol{q} = - k\ \nabla u \in \Omega \\ -# \boldsymbol{q}\cdot \boldsymbol{n} = q_n \in \Gamma_\mathrm{N}\\ -# u = u_\mathrm{D} \in \Gamma_\mathrm{D} -# ``` -# -# ## Weak form -# ### Part 1 -# ```math -# \int_{\Omega} \delta u \nabla \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ -# \int_{\Gamma} \delta u \boldsymbol{n} \cdot \boldsymbol{q}\ \mathrm{d}\Gamma - -# \int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega = \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega \\ # ``` # -# ### Part 2 +# ## Theory +# We start with the strong form of the heat equation: Find the temperature, $u(\boldsymbol{x})$, and heat flux, $\boldsymbol{q}(x)$, +# such that # ```math -# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega = - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega +# \begin{align*} +# \boldsymbol{\nabla}\cdot \boldsymbol{q} &= h(\boldsymbol{x}), \quad \forall \boldsymbol{x} \in \Omega \\ +# \boldsymbol{q}(\boldsymbol{x}) &= - k\ \boldsymbol{\nabla} u(\boldsymbol{x}), \quad \forall \boldsymbol{x} \in \Omega \\ +# \boldsymbol{q}(\boldsymbol{x})\cdot \boldsymbol{n}(\boldsymbol{x}) &= q_n, \quad \forall \boldsymbol{x} \in \Gamma_\mathrm{N}\\ +# u(\boldsymbol{x}) &= u_\mathrm{D}, \quad \forall \boldsymbol{x} \in \Gamma_\mathrm{D} +# \end{align*} # ``` -# where no Green-Gauss theorem is applied. # -# ### Summary -# The weak form becomes, find $u\in H^1$ and $\boldsymbol{q} \in H\mathrm{(div)}$, such that +# From this strong form, we can formulate the weak form as a mixed formulation. +# Find $u \in \mathbb{U}$ and $\boldsymbol{q}\in\mathbb{Q}$ such that # ```math # \begin{align*} -# -\int_{\Omega} \nabla (\delta u) \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= \int_{\Omega} \delta u\ h\ \mathrm{d}\Omega - -# \int_{\Gamma} \delta u\ q_\mathrm{n}\ \mathrm{d}\Gamma -# \quad -# \forall\ \delta u \in \delta H^1 \\ -# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= - \int_{\Omega} \boldsymbol{\delta q} \cdot \left[k\ \nabla u\right]\ \mathrm{d}\Omega -# \quad \forall\ \boldsymbol{\delta q} \in \delta H\mathrm{(div)} +# \int_{\Omega} \delta u [\boldsymbol{\nabla} \cdot \boldsymbol{q}]\ \mathrm{d}\Omega &= - \int_\Omega \delta u h\ \mathrm{d}\Omega, \quad \forall\ \delta u \in \delta\mathbb{U} \\ +# %\int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= \int_\Omega \boldsymbol{\delta q} \cdot [k\ \boldsymbol{\nabla} u]\ \mathrm{d}\Omega \\ +# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega + \int_{\Omega} [\boldsymbol{\nabla} \cdot \boldsymbol{\delta q}] k u \ \mathrm{d}\Omega &= +# \int_\Gamma \boldsymbol{\delta q} \cdot \boldsymbol{n} k\ u\ \mathrm{d}\Omega, \quad \forall\ \boldsymbol{\delta q} \in \delta\mathbb{Q} # \end{align*} # ``` +# where we have the function spaces +# * $\mathbb{U} = \delta\mathbb{U} = L^2$ +# * $\mathbb{Q} = \lbrace \boldsymbol{q} \in H(\mathrm{div}) \text{such that} \boldsymbol{q}\cdot\boldsymbol{n} = q_\mathrm{n} \text{ on } \Gamma_\mathrm{D}\rbrace$ +# * $\mathbb{Q} = \lbrace \boldsymbol{q} \in H(\mathrm{div}) \text{such that} \boldsymbol{q}\cdot\boldsymbol{n} = 0 \text{ on } \Gamma_\mathrm{D}\rbrace$ +# +# A stable choice of finite element spaces for this problem on grid with triangles is using +# * `DiscontinuousLagrange{RefTriangle, k-1}` for approximating $L^2$ +# * `BrezziDouglasMarini{RefTriangle, k}` for approximating $H(\mathrm{div})$ +# following [fenics](https://fenicsproject.org/olddocs/dolfin/1.4.0/python/demo/documented/mixed-poisson/python/documentation.html). +# For further details, see Boffi2013. +# We will also see what happens if we instead use `Lagrange` elements which are a subspace of $H^1$ instead of $H(\mathrm{div})$ elements. # # ## Commented Program # # Now we solve the problem in Ferrite. What follows is a program spliced with comments. -#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 -# We start by generating a simple grid with 20x20 quadrilateral elements -# using `generate_grid`. The generator defaults to the unit square, -# so we don't need to specify the corners of the domain. -#grid = generate_grid(QuadraticTriangle, (20, 20)); -grid = generate_grid(Triangle, (20, 20)); +# First we load Ferrite, +using Ferrite +# And define our grid, representing a channel with a central part having a lower +# conductivity, $k$, than the surrounding. +function create_grid(ny::Int) + width = 10.0 + length = 40.0 + center_width = 5.0 + center_length = 20.0 + upper_right = Vec((length / 2, width / 2)) + grid = generate_grid(Triangle, (round(Int, ny * length / width), ny), -upper_right, upper_right); + addcellset!(grid, "center", x -> abs(x[1]) < center_width/2 && abs(x[2]) < center_length / 2) + addcellset!(grid, "around", setdiff(1:getncells(grid), getcellset(grid, "center"))) + return grid +end + +grid = create_grid(10) # ### Trial and test functions # A `CellValues` facilitates the process of evaluating values and gradients of @@ -82,7 +92,7 @@ grid = generate_grid(Triangle, (20, 20)); # the same reference element. We combine the interpolation and the quadrature rule # to a `CellValues` object. ip_geo = geometric_interpolation(getcelltype(grid)) -ipu = Lagrange{RefTriangle, 1}() # Why does it "explode" for 2nd order ipu? +ipu = DiscontinuousLagrange{RefTriangle, 1}() # Why does it "explode" for 2nd order ipu? ipq = RaviartThomas{2,RefTriangle, 1}() qr = QuadratureRule{RefTriangle}(2) cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl index 52d463b108..c5505ef026 100644 --- a/docs/src/literate-tutorials/heat_equation_triangle.jl +++ b/docs/src/literate-tutorials/heat_equation_triangle.jl @@ -32,7 +32,6 @@ # ## Commented Program # # Now we solve the problem in Ferrite. What follows is a program spliced with comments. -#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 diff --git a/src/interpolations.jl b/src/interpolations.jl index 9c5a0f9037..b6fc6c4f9b 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1855,13 +1855,64 @@ function get_direction(::RaviartThomas{2, RefTriangle, 2}, j, cell) return ifelse(edge[2] > edge[1], 1, -1) end +##################################### +# Brezzi-Douglas–Marini, H(div) # +##################################### +struct BrezziDouglasMarini{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end +mapping_type(::BrezziDouglasMarini) = ContravariantPiolaMapping() +reference_coordinates(ip::BrezziDouglasMarini{vdim}) where vdim = fill(NaN*zero(Vec{vdim}), getnbasefunctions(ip)) +dirichlet_facedof_indices(ip::BrezziDouglasMarini) = facetdof_interior_indices(ip) +n_dbc_components(::BrezziDouglasMarini) = 1 +#= +----------------+-------------------- +Vertex numbers: | Vertex coordinates: + 2 | + | \ | v1: 𝛏 = (1.0, 0.0) + | \ | v2: 𝛏 = (0.0, 1.0) +ξ₂^ | \ | v3: 𝛏 = (0.0, 0.0) + | 3-------1 | + +--> ξ₁ | +----------------+-------------------- +Edge numbers: | Edge identifiers: + + | + | \ | e1: (v1, v2) + 2 1 | e2: (v2, v3) + | \ | e3: (v3, v1) + +---3---+ | +----------------+-------------------- +=# + +# RefTriangle, 1st order Lagrange +function reference_shape_value(ip::BrezziDouglasMarini{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) + x, y = ξ + # Edge 1 + i == 1 && return Vec(4x, -2y) # Changed sign to make positive outwards + i == 2 && return Vec(-2x, 4y) # Changed sign to make positive outwards + # Edge 2 (reverse order to follow Ferrite convention) + i == 3 && return Vec(-2x - 6y + 2, 4y) + i == 4 && return Vec(4x + 6y - 4, -2y) + # Edge 3 + i == 5 && return Vec(-2x, 6x + 4y - 4) # Changed sign to make positive outwards + i == 6 && return Vec(4x, -6x - 2y + 2) # Changed sign to make positive outwards + throw(ArgumentError("no shape function $i for interpolation $ip")) +end + +getnbasefunctions(::BrezziDouglasMarini{2, RefTriangle, 1}) = 6 +edgedof_interior_indices(::BrezziDouglasMarini{2, RefTriangle, 1}) = ((1, 2), (3, 4), (5, 6)) +adjust_dofs_during_distribution(::BrezziDouglasMarini{2, RefTriangle, 1}) = false + +function get_direction(::BrezziDouglasMarini{2, RefTriangle, 1}, j, cell) + edge = edges(cell)[(j + 1) ÷ 2] + return ifelse(edge[2] > edge[1], 1, -1) +end + ##################################### # Nedelec (1st kind), H(curl) # ##################################### struct Nedelec{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end mapping_type(::Nedelec) = CovariantPiolaMapping() reference_coordinates(ip::Nedelec{vdim}) where vdim = fill(NaN*zero(Vec{vdim}), getnbasefunctions(ip)) -dirichlet_facedof_indices(ip::Nedelec) = facedof_interior_indices(ip) +dirichlet_facedof_indices(ip::Nedelec) = facetdof_interior_indices(ip) n_dbc_components(::Nedelec) = 1 # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 1610b1d8f4..6ca757869f 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -270,7 +270,7 @@ end # Where f(s) = 1 for linear interpolation and f(s)=1-s and f(s)=s for 2nd order interpolation (first and second shape function) # And s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 -@testset "Nedelec" begin +@testset "H(curl) on RefCell" begin lineqr = QuadratureRule{RefLine}(20) for ip in (Nedelec{2,RefTriangle,1}(), Nedelec{2,RefTriangle,2}(), Nedelec{3,RefTetrahedron,1}(), Nedelec{3,RefHexahedron,1}()) cell = reference_cell(getrefshape(ip)) @@ -302,6 +302,46 @@ end end end +# Required properties of shape value Nⱼ of an edge-elements (Hdiv) on an edge with normal n, length L, and dofs ∈ 𝔇 +# 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 +# Where f(s) = 1 for single shape function on edge, and f(s)=1-s and f(s)=s for two shape functions on edge +# s is the path parameter ∈[0,1] along the positive direction of the path. +# 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 +@testset "H(div) on RefCell" begin + lineqr = QuadratureRule{RefLine}(20) + for ip in (RaviartThomas{2, RefTriangle, 1}(), Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), ) + cell = reference_cell(getrefshape(ip)) + cell_facets = Ferrite.facets(cell) + dofs = Ferrite.facetdof_interior_indices(ip) + x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) + normals = reference_normals(geometric_interpolation(typeof(cell))) + @testset "$ip" begin + for (facet_nr, (i1, i2)) in enumerate(cell_facets) + @testset "Facet $facet_nr" begin + Δx = x[i2]-x[i1] + x0 = (x[i1]+x[i2])/2 + L = norm(Δx) + n = normals[facet_nr] + for (idof, shape_nr) in enumerate(dofs[facet_nr]) + nfacetdofs = length(dofs[facet_nr]) + f(x) = nfacetdofs == 1 ? 1.0 : (idof == 1 ? 1-x : x) + s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, f) + @test s ≈ one(s) + end + for (j_facet, shape_nrs) in enumerate(dofs) + j_facet == facet_nr && continue + for shape_nr in shape_nrs + for ξ in (x[i1] + r*Δx for r in [0.0, rand(3)..., 1.0]) + @test abs(reference_shape_value(ip, ξ, shape_nr) ⋅ n) < eps()*100 + end + end + end + end + end + end + end +end + tupleshift(t::NTuple{N}, shift::Int) where N = ntuple(i -> t[mod(i - 1 - shift, N) + 1], N) #tupleshift(t::NTuple, shift::Int) = tuple(circshift(SVector(t), shift)...) cell_permutations(cell::Quadrilateral) = (Quadrilateral(tupleshift(cell.nodes, shift)) for shift in 0:3) @@ -336,9 +376,10 @@ end include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) import .InterpolationTestUtils as ITU nel = 3 - transformation_functions = Dict( - Nedelec=>(v,n)-> v - n*(v⋅n), # Hcurl (tangent continuity) - RaviartThomas=>(v,n) -> v ⋅ n) # Hdiv (normal continuity) + hdiv_check(v, n) = v ⋅ n # Hdiv (normal continuity) + hcurl_check(v, n) = v - n*(v⋅n) # Hcurl (tangent continuity) + transformation_functions = ((Nedelec, hcurl_check), (RaviartThomas, hdiv_check), (Ferrite.BrezziDouglasMarini, hdiv_check)) + for CT in (Triangle, QuadraticTriangle, Tetrahedron, Hexahedron) dim = Ferrite.getrefdim(CT) # dim = sdim = rdim p1, p2 = (rand(Vec{dim}), ones(Vec{dim})+rand(Vec{dim})) @@ -352,11 +393,11 @@ end basecell = getcells(grid, cellnr) RefShape = Ferrite.getrefshape(basecell) for order in (1, 2) - for IPT in (Nedelec, RaviartThomas) + for (IPT, transformation_function) in transformation_functions dim == 3 && order > 1 && continue IPT == RaviartThomas && (dim == 3 || order > 1) && continue IPT == RaviartThomas && (RefShape == RefHexahedron) && continue - transformation_function=transformation_functions[IPT] + IPT == Ferrite.BrezziDouglasMarini && !(RefShape == RefTriangle && order == 1) && continue ip = IPT{dim, RefShape, order}() @testset "$CT, $ip" begin for testcell in cell_permutations(basecell) diff --git a/visualization/2d_vector_interpolation_checkplots.jl b/visualization/2d_vector_interpolation_checkplots.jl new file mode 100644 index 0000000000..d51487bbc3 --- /dev/null +++ b/visualization/2d_vector_interpolation_checkplots.jl @@ -0,0 +1,23 @@ +using Ferrite +import CairoMakie as Plt + +function plot_shape_function(ip::VectorInterpolation{2, RefShape}, qr::Int, i::Int) where {RefShape<:Ferrite.AbstractRefShape{2}} + return plot_shape_function(ip, QuadratureRule{RefShape}(qr), i) +end + +function plot_shape_function(ip::VectorInterpolation{2, RefShape}, qr::QuadratureRule{RefShape}, i::Int) where {RefShape<:Ferrite.AbstractRefShape{2}} + points = Ferrite.getpoints(qr) + N = map(ξ -> Ferrite.reference_shape_value(ip, ξ, i), points) + vertices = Ferrite.reference_coordinates(Lagrange{RefShape, 1}()) + push!(vertices, first(vertices)) + + fig = Plt.Figure() + ax = Plt.Axis(fig[1,1]) + Plt.lines!(ax, first.(vertices), last.(vertices)) + Plt.arrows!(ax, first.(points), last.(points), first.(N), last.(N); lengthscale = 0.3) + return fig +end + +function plot_global_shape_function(ip, qr, nx, ny, i) + #TODO: Plot a single global shape function to investigate continuity +end diff --git a/visualization/Manifest.toml b/visualization/Manifest.toml index ec88abc416..96331314ab 100644 --- a/visualization/Manifest.toml +++ b/visualization/Manifest.toml @@ -1,8 +1,8 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.10.2" +julia_version = "1.11.1" manifest_format = "2.0" -project_hash = "9dc2d1ce07b87cc5107b7fa3d34838a5e8d48de1" +project_hash = "f8140ea440af404cde81e65338add89dc118d0fc" [[deps.AbstractFFTs]] deps = ["LinearAlgebra"] @@ -15,11 +15,6 @@ weakdeps = ["ChainRulesCore", "Test"] AbstractFFTsChainRulesCoreExt = "ChainRulesCore" AbstractFFTsTestExt = "Test" -[[deps.AbstractLattices]] -git-tree-sha1 = "222ee9e50b98f51b5d78feb93dd928880df35f06" -uuid = "398f06c4-4d28-53ec-89ca-5b2656b7603d" -version = "0.3.0" - [[deps.AbstractTrees]] git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -27,14 +22,25 @@ version = "0.4.5" [[deps.Adapt]] deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "6a55b747d1812e699320963ffde36f1ebdda4099" +git-tree-sha1 = "50c3c56a52972d78e8be9fd135bfb91c9574c140" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "4.0.4" +version = "4.1.1" weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" +[[deps.AdaptivePredicates]] +git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6" +uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" +version = "1.2.0" + +[[deps.AliasTables]] +deps = ["PtrArrays", "Random"] +git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" +uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" +version = "1.1.3" + [[deps.Animations]] deps = ["Colors"] git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d" @@ -43,42 +49,17 @@ version = "0.4.1" [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.1" - -[[deps.ArrayInterface]] -deps = ["Adapt", "LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "44691067188f6bd1b2289552a23e4b7572f4528d" -uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.9.0" - - [deps.ArrayInterface.extensions] - ArrayInterfaceBandedMatricesExt = "BandedMatrices" - ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" - ArrayInterfaceCUDAExt = "CUDA" - ArrayInterfaceChainRulesExt = "ChainRules" - ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" - ArrayInterfaceReverseDiffExt = "ReverseDiff" - ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" - ArrayInterfaceTrackerExt = "Tracker" - - [deps.ArrayInterface.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" - CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" - ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" - GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" - ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" - StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" - Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" +version = "1.1.2" [[deps.Artifacts]] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" [[deps.Automa]] -deps = ["PrecompileTools", "TranscodingStreams"] -git-tree-sha1 = "588e0d680ad1d7201d4c6a804dcb1cd9cba79fbb" +deps = ["PrecompileTools", "SIMD", "TranscodingStreams"] +git-tree-sha1 = "a8f503e8e1a5f583fbef15a8440c8c7e32185df2" uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" -version = "1.0.3" +version = "1.1.0" [[deps.AxisAlgorithms]] deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] @@ -94,12 +75,13 @@ version = "0.4.7" [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +version = "1.11.0" [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" +git-tree-sha1 = "8873e196c2eb87962a2048b3b8e08946535864a1" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+1" +version = "1.0.8+2" [[deps.CEnum]] git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc" @@ -108,6 +90,7 @@ version = "0.5.0" [[deps.CRC32c]] uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" +version = "1.11.0" [[deps.CRlibm_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -115,23 +98,29 @@ git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" version = "1.0.1+0" +[[deps.Cairo]] +deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] +git-tree-sha1 = "7b6ad8c35f4bc3bca8eb78127c8b99719506a5fb" +uuid = "159f3aea-2a34-519c-b102-8c37f9878175" +version = "1.1.0" + +[[deps.CairoMakie]] +deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] +git-tree-sha1 = "fbfdb7cbe17bd14b60646c14c27a16e5038cde54" +uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +version = "0.12.15" + [[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"] -git-tree-sha1 = "a4c43f59baa34011e303e76f5c8c91bf58415aaf" +git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.0+1" - -[[deps.Calculus]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" -uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" -version = "0.5.1" +version = "1.18.2+1" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "575cd02e080939a33b6df6c5853d14924c08e35b" +git-tree-sha1 = "3e4b134270b372f2ed4d4d0e936aabaefc1802bc" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.23.0" +version = "1.25.0" weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] @@ -139,9 +128,9 @@ weakdeps = ["SparseArrays"] [[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.ColorBrewer]] deps = ["Colors", "JSON", "Test"] @@ -151,15 +140,15 @@ version = "0.4.0" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129" +git-tree-sha1 = "13951eb68769ad1cd460cdb2e64e5e95f1bf123d" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.24.0" +version = "3.27.0" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" +git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d" uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.4" +version = "0.11.5" [[deps.ColorVectorSpace]] deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] @@ -173,26 +162,21 @@ weakdeps = ["SpecialFunctions"] [[deps.Colors]] deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a" +git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.10" - -[[deps.Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" +version = "0.12.11" [[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.Compat]] deps = ["TOML", "UUIDs"] -git-tree-sha1 = "c955881e3c981181362ae4088b35995446298b80" +git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.14.0" +version = "4.16.0" weakdeps = ["Dates", "LinearAlgebra"] [deps.Compat.extensions] @@ -201,23 +185,23 @@ weakdeps = ["Dates", "LinearAlgebra"] [[deps.CompilerSupportLibraries_jll]] deps = ["Artifacts", "Libdl"] uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.0+0" +version = "1.1.1+0" [[deps.ConstructionBase]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "260fd2400ed2dab602a7c15cf10c1933c59930a2" +git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.5" -weakdeps = ["IntervalSets", "StaticArrays"] +version = "1.5.8" +weakdeps = ["IntervalSets", "LinearAlgebra", "StaticArrays"] [deps.ConstructionBase.extensions] ConstructionBaseIntervalSetsExt = "IntervalSets" + ConstructionBaseLinearAlgebraExt = "LinearAlgebra" ConstructionBaseStaticArraysExt = "StaticArrays" [[deps.Contour]] -git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" +git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.6.2" +version = "0.6.3" [[deps.DataAPI]] git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" @@ -226,9 +210,9 @@ version = "1.16.0" [[deps.DataStructures]] deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "0f4b5d62a88d8f59003e43c25a8a90de9eb76317" +git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.18" +version = "0.18.20" [[deps.DataValueInterfaces]] git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" @@ -238,12 +222,13 @@ version = "1.0.0" [[deps.Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +version = "1.11.0" [[deps.DelaunayTriangulation]] -deps = ["DataStructures", "EnumX", "ExactPredicates", "Random", "SimpleGraphs"] -git-tree-sha1 = "d4e9dc4c6106b8d44e40cd4faf8261a678552c7c" +deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "PrecompileTools", "Random"] +git-tree-sha1 = "89df54fbe66e5872d91d8c2cd3a375f660c3fd64" uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" -version = "0.8.12" +version = "1.6.1" [[deps.DiffResults]] deps = ["StaticArraysCore"] @@ -259,9 +244,9 @@ version = "1.15.1" [[deps.Distances]] deps = ["LinearAlgebra", "Statistics", "StatsAPI"] -git-tree-sha1 = "66c4c81f259586e8f002eacebc177e1fb06363b0" +git-tree-sha1 = "c7e3a542b999843086e2f29dac96a618c105be1d" uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.11" +version = "0.10.12" weakdeps = ["ChainRulesCore", "SparseArrays"] [deps.Distances.extensions] @@ -271,12 +256,13 @@ weakdeps = ["ChainRulesCore", "SparseArrays"] [[deps.Distributed]] deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +version = "1.11.0" [[deps.Distributions]] -deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "7c302d7a5fec5214eb8a5a4c466dcf7a51fcf169" +deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] +git-tree-sha1 = "d7477ecdafb813ddee2ae727afa94e9dcb5f3fb0" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.107" +version = "0.25.112" [deps.Distributions.extensions] DistributionsChainRulesCoreExt = "ChainRulesCore" @@ -299,12 +285,6 @@ deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" -[[deps.DualNumbers]] -deps = ["Calculus", "NaNMath", "SpecialFunctions"] -git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566" -uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" -version = "0.6.8" - [[deps.EarCut_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053" @@ -324,20 +304,20 @@ version = "2.2.8" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" +git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.5.0+0" +version = "2.6.2+0" [[deps.Extents]] -git-tree-sha1 = "2140cd04483da90b2da7f99b2add0750504fc39c" +git-tree-sha1 = "81023caa0021a41712685887db1fc03db26f41f5" uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" -version = "0.1.2" +version = "0.1.4" [[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"] -git-tree-sha1 = "ab3f7e1819dba9434a3a5126510c8fda3a4e7000" +git-tree-sha1 = "8cc47f299902e13f90405ddb5bf87e5d474c0d38" uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "6.1.1+0" +version = "6.1.2+0" [[deps.FFTW]] deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] @@ -347,15 +327,15 @@ version = "1.8.0" [[deps.FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c6033cc3892d0ef5bb9cd29b7f2f0331ea5184ea" +git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+0" +version = "3.3.10+1" [[deps.Ferrite]] -deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] +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" @@ -365,26 +345,38 @@ version = "0.3.14" BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" -[[deps.FerriteViz]] -deps = ["Ferrite", "GeometryBasics", "LinearAlgebra", "Makie", "Reexport", "ShaderAbstractions", "StaticArrays", "Tensors"] -path = "C:\\Users\\meyer\\.julia\\dev\\FerriteViz" -uuid = "59d0093e-b1f1-4fb7-ac85-ab57e45f39d9" -version = "0.2.1" - [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" +git-tree-sha1 = "62ca0547a14c57e98154423419d8a342dca75ca9" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.3" +version = "1.16.4" + +[[deps.FilePaths]] +deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"] +git-tree-sha1 = "919d9412dbf53a2e6fe74af62a73ceed0bce0629" +uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824" +version = "0.8.3" + +[[deps.FilePathsBase]] +deps = ["Compat", "Dates"] +git-tree-sha1 = "7878ff7172a8e6beedd1dea14bd27c3c6340d361" +uuid = "48062228-2e41-5def-b9a4-89aafe57970f" +version = "0.9.22" +weakdeps = ["Mmap", "Test"] + + [deps.FilePathsBase.extensions] + FilePathsBaseMmapExt = "Mmap" + FilePathsBaseTestExt = "Test" [[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +version = "1.11.0" [[deps.FillArrays]] -deps = ["LinearAlgebra", "Random"] -git-tree-sha1 = "5b93957f6dcd33fc343044af3d48c215be2562f1" +deps = ["LinearAlgebra"] +git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.9.3" +version = "1.13.0" weakdeps = ["PDMats", "SparseArrays", "Statistics"] [deps.FillArrays.extensions] @@ -392,45 +384,28 @@ weakdeps = ["PDMats", "SparseArrays", "Statistics"] FillArraysSparseArraysExt = "SparseArrays" FillArraysStatisticsExt = "Statistics" -[[deps.FiniteDiff]] -deps = ["ArrayInterface", "LinearAlgebra", "Requires", "Setfield", "SparseArrays"] -git-tree-sha1 = "bc0c5092d6caaea112d3c8e3b238d61563c58d5f" -uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.23.0" - - [deps.FiniteDiff.extensions] - FiniteDiffBandedMatricesExt = "BandedMatrices" - FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" - FiniteDiffStaticArraysExt = "StaticArrays" - - [deps.FiniteDiff.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - [[deps.FixedPointNumbers]] deps = ["Statistics"] -git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" +git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.4" +version = "0.8.5" [[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] +git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.93+0" +version = "2.13.96+0" -[[deps.Formatting]] -deps = ["Logging", "Printf"] -git-tree-sha1 = "fb409abab2caf118986fc597ba84b50cbaf00b87" -uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" -version = "0.4.3" +[[deps.Format]] +git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" +uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" +version = "1.3.7" [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] -git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad" +git-tree-sha1 = "a9ce73d3c827adab2d70bf168aaece8cce196898" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.36" +version = "0.10.37" weakdeps = ["StaticArrays"] [deps.ForwardDiff.extensions] @@ -444,55 +419,38 @@ version = "4.1.1" [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" +git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.1+0" +version = "2.13.2+0" [[deps.FreeTypeAbstraction]] deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] -git-tree-sha1 = "055626e1a35f6771fe99060e835b72ca61a52621" +git-tree-sha1 = "84dfe824bd6fdf2a5d73bb187ff31b5549b2a79c" uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" -version = "0.10.1" +version = "0.10.4" [[deps.FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.10+0" +version = "1.0.14+0" -[[deps.Future]] -deps = ["Random"] -uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" - -[[deps.GLFW]] -deps = ["GLFW_jll"] -git-tree-sha1 = "35dbc482f0967d8dceaa7ce007d16f9064072166" -uuid = "f7f18e0c-5ee9-5ccd-a5bf-e8befd85ed98" -version = "3.4.1" - -[[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" -uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.3.9+0" - -[[deps.GLMakie]] -deps = ["ColorTypes", "Colors", "FileIO", "FixedPointNumbers", "FreeTypeAbstraction", "GLFW", "GeometryBasics", "LinearAlgebra", "Makie", "Markdown", "MeshIO", "ModernGL", "Observables", "PrecompileTools", "Printf", "ShaderAbstractions", "StaticArrays"] -git-tree-sha1 = "8236ce4eda9837d15bab49573bba16ba0652b486" -uuid = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" -version = "0.8.12" +[[deps.GeoFormatTypes]] +git-tree-sha1 = "59107c179a586f0fe667024c5eb7033e81333271" +uuid = "68eda718-8dee-11e9-39e7-89f7f65f511f" +version = "0.4.2" [[deps.GeoInterface]] -deps = ["Extents"] -git-tree-sha1 = "d4f85701f569584f2cff7ba67a137d03f0cfb7d0" +deps = ["Extents", "GeoFormatTypes"] +git-tree-sha1 = "2f6fce56cdb8373637a6614e14a5768a88450de2" uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" -version = "1.3.3" +version = "1.3.7" [[deps.GeometryBasics]] deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "5694b56ccf9d15addedc35e9a4ba9c317721b788" +git-tree-sha1 = "b62f2b2d76cee0d61a2ef2b3118cd2a3215d3134" uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.4.10" +version = "0.4.11" [[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] @@ -500,11 +458,23 @@ git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" version = "0.21.0+0" +[[deps.Giflib_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0224cce99284d997f6880a42ef715a37c99338d1" +uuid = "59f7168a-df46-5410-90c8-f2779963d0ec" +version = "5.2.2+0" + [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "359a1ba2e320790ddbe4ee8b4d54a305c0ea2aff" +git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.0+0" +version = "2.80.5+0" + +[[deps.Graphics]] +deps = ["Colors", "LinearAlgebra", "NaNMath"] +git-tree-sha1 = "d61890399bc535850c4bf08e4e0d3a7ad0f21cbd" +uuid = "a2bd30eb-e257-5431-a919-1863eab51364" +version = "1.1.2" [[deps.Graphite2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -514,9 +484,9 @@ version = "1.3.14+0" [[deps.GridLayoutBase]] deps = ["GeometryBasics", "InteractiveUtils", "Observables"] -git-tree-sha1 = "f57a64794b336d4990d90f80b147474b869b1bc4" +git-tree-sha1 = "fc713f007cff99ff9e50accba6373624ddd33588" uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" -version = "0.9.2" +version = "0.11.0" [[deps.Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" @@ -524,16 +494,16 @@ uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" version = "1.0.2" [[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.HypergeometricFunctions]] -deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "f218fe3736ddf977e0e772bc9a586b2383da2685" +deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] +git-tree-sha1 = "7c4195be1649ae622304031ed46a2f4df989f1eb" uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.23" +version = "0.3.24" [[deps.ImageAxes]] deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"] @@ -554,10 +524,10 @@ uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534" version = "0.10.2" [[deps.ImageIO]] -deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs"] -git-tree-sha1 = "bca20b2f5d00c4fbc192c3212da8fa79f4688009" +deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs", "WebP"] +git-tree-sha1 = "696144904b76e1ca433b886b4e7edd067d76cbf7" uuid = "82e4d734-157c-48bb-816b-45c225c6df19" -version = "0.6.7" +version = "0.6.9" [[deps.ImageMetadata]] deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] @@ -567,9 +537,9 @@ version = "0.9.9" [[deps.Imath_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3d09a9f60edf77f8a4d99f9e015e8fbf9989605d" +git-tree-sha1 = "0936ba688c6d201805a83da835b55c61a180db52" uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" -version = "3.1.7+0" +version = "3.1.11+0" [[deps.IndirectArrays]] git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" @@ -577,60 +547,74 @@ uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" version = "1.0.0" [[deps.Inflate]] -git-tree-sha1 = "ea8031dea4aff6bd41f1df8f2fdfb25b33626381" +git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.4" - -[[deps.IntegerMathUtils]] -git-tree-sha1 = "b8ffb903da9f7b8cf695a8bead8e01814aa24b30" -uuid = "18e54dd8-cb9d-406c-a71d-865a43cbb235" -version = "0.1.2" +version = "0.1.5" [[deps.IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5fdf2fe6724d8caabf43b557b84ce53f3b7e2f6b" +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.0.2+0" +version = "2024.2.1+0" [[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +version = "1.11.0" [[deps.Interpolations]] deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] git-tree-sha1 = "88a101217d7cb38a7b481ccd50d21876e1d1b0e0" uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" version = "0.15.1" +weakdeps = ["Unitful"] [deps.Interpolations.extensions] InterpolationsUnitfulExt = "Unitful" - [deps.Interpolations.weakdeps] - Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" - [[deps.IntervalArithmetic]] -deps = ["CRlibm_jll", "RoundingEmulator"] -git-tree-sha1 = "552505ed27d2a90ff04c15b0ecf4634e0ab5547b" +deps = ["CRlibm_jll", "LinearAlgebra", "MacroTools", "RoundingEmulator"] +git-tree-sha1 = "c59c57c36683aa17c563be6edaac888163f35285" uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" -version = "0.22.9" -weakdeps = ["DiffRules", "ForwardDiff", "RecipesBase"] +version = "0.22.18" [deps.IntervalArithmetic.extensions] IntervalArithmeticDiffRulesExt = "DiffRules" IntervalArithmeticForwardDiffExt = "ForwardDiff" + IntervalArithmeticIntervalSetsExt = "IntervalSets" IntervalArithmeticRecipesBaseExt = "RecipesBase" + [deps.IntervalArithmetic.weakdeps] + DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + [[deps.IntervalSets]] git-tree-sha1 = "dba9ddf07f77f60450fe5d2e2beb9854d9a49bd0" uuid = "8197267c-284f-5f27-9208-e0e47529a953" version = "0.7.10" -weakdeps = ["Random", "RecipesBase", "Statistics"] [deps.IntervalSets.extensions] IntervalSetsRandomExt = "Random" IntervalSetsRecipesBaseExt = "RecipesBase" IntervalSetsStatisticsExt = "Statistics" + [deps.IntervalSets.weakdeps] + Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.InverseFunctions]] +git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.17" +weakdeps = ["Dates", "Test"] + + [deps.InverseFunctions.extensions] + InverseFunctionsDatesExt = "Dates" + InverseFunctionsTestExt = "Test" + [[deps.IrrationalConstants]] git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" @@ -654,9 +638,9 @@ version = "1.0.0" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" +git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.5.0" +version = "1.6.1" [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] @@ -672,42 +656,49 @@ version = "0.1.5" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3336abae9a713d2210bb57ab484b1e065edd7d23" +git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.2+0" +version = "3.0.4+0" [[deps.KernelDensity]] deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] -git-tree-sha1 = "fee018a29b60733876eb557804b5b109dd3dd8a7" +git-tree-sha1 = "7d703202e65efa1369de1279c162b915e245eed1" uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" -version = "0.6.8" +version = "0.6.9" [[deps.LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.1+0" +version = "3.100.2+0" + +[[deps.LERC_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "36bdbc52f13a7d1dcb0f3cd694e01677a515655b" +uuid = "88015f11-f218-50d7-93a8-a6af411a945d" +version = "4.0.0+0" [[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", "Pkg"] -git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.1+0" +version = "2.10.2+1" [[deps.LaTeXStrings]] -git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" +git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.3.1" +version = "1.4.0" [[deps.LazyArtifacts]] deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" +version = "1.11.0" [[deps.LazyModules]] git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e" @@ -722,16 +713,17 @@ version = "0.6.4" [[deps.LibCURL_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.4.0+0" +version = "8.6.0+0" [[deps.LibGit2]] deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" [[deps.LibGit2_jll]] deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.6.4+0" +version = "1.7.2+0" [[deps.LibSSH2_jll]] deps = ["Artifacts", "Libdl", "MbedTLS_jll"] @@ -740,6 +732,7 @@ version = "1.11.0+1" [[deps.Libdl]] uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" [[deps.Libffi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -748,10 +741,10 @@ uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" version = "3.2.2+1" [[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] +git-tree-sha1 = "8be878062e0ffa2c3f67bb58a595375eda5de80b" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.7+0" +version = "1.11.0+0" [[deps.Libglvnd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] @@ -760,28 +753,34 @@ uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" version = "1.6.0+0" [[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c6ce1e19f3aec9b59186bdf06cdf3c4fc5f5f3e6" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.42.0+0" +version = "1.50.0+0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" +git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+0" +version = "1.17.0+1" [[deps.Libmount_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dae976433497a2f841baadea93d27e68f1a12a97" +git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.39.3+0" +version = "2.40.1+0" + +[[deps.Libtiff_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a" +uuid = "89763e89-9b03-5906-acba-b20f662cd828" +version = "4.7.0+0" [[deps.Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0a04a1318df1bf510beb2562cf90fb0c386f58c4" +git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.39.3+1" +version = "2.40.1+0" [[deps.LightXML]] deps = ["Libdl", "XML2_jll"] @@ -789,27 +788,16 @@ git-tree-sha1 = "3a994404d3f6709610701c7dabfc03fed87a81f8" uuid = "9c8b4983-aa76-5018-a973-4c85ecc9e179" version = "0.9.1" -[[deps.LineSearches]] -deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "Printf"] -git-tree-sha1 = "7bbea35cec17305fc70a0e5b4641477dc0789d9d" -uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" -version = "7.2.0" - [[deps.LinearAlgebra]] deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[deps.LinearAlgebraX]] -deps = ["LinearAlgebra", "Mods", "Primes", "SimplePolynomials"] -git-tree-sha1 = "d76cec8007ec123c2b681269d40f94b053473fcf" -uuid = "9b3f67b0-2d00-526e-9884-9e4938f8fb88" -version = "0.2.7" +version = "1.11.0" [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" +git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.27" +version = "0.3.28" [deps.LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -823,12 +811,13 @@ version = "0.3.27" [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +version = "1.11.0" [[deps.MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "72dc3cf284559eb8f53aa593fe62cb33f83ed0c0" +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] +git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.0.0+0" +version = "2024.2.0+0" [[deps.MacroTools]] deps = ["Markdown", "Random"] @@ -837,16 +826,16 @@ uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.13" [[deps.Makie]] -deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FixedPointNumbers", "Formatting", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageIO", "InteractiveUtils", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Setfield", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "StableHashTraits", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun"] -git-tree-sha1 = "35fa3c150cd96fd77417a23965b7037b90d6ffc9" +deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "InverseFunctions", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"] +git-tree-sha1 = "f7907907eb914138cc9e9ee66ab46f7a9efac8e8" uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" -version = "0.19.12" +version = "0.21.15" [[deps.MakieCore]] -deps = ["Observables", "REPL"] -git-tree-sha1 = "9b11acd07f21c4d035bd4156e789532e8ee2cc70" +deps = ["ColorTypes", "GeometryBasics", "IntervalSets", "Observables"] +git-tree-sha1 = "4604f03e5b057e8e62a95a44929cafc9585b0fe9" uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" -version = "0.6.9" +version = "0.8.9" [[deps.MappedArrays]] git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" @@ -856,43 +845,28 @@ version = "0.4.2" [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +version = "1.11.0" [[deps.MathTeXEngine]] deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] -git-tree-sha1 = "96ca8a313eb6437db5ffe946c457a401bbb8ce1d" +git-tree-sha1 = "f45c8916e8385976e1ccd055c9874560c257ab13" uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" -version = "0.5.7" +version = "0.6.2" [[deps.MbedTLS_jll]] deps = ["Artifacts", "Libdl"] uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.2+1" - -[[deps.MeshIO]] -deps = ["ColorTypes", "FileIO", "GeometryBasics", "Printf"] -git-tree-sha1 = "8c26ab950860dfca6767f2bbd90fdf1e8ddc678b" -uuid = "7269a6da-0436-5bbc-96c2-40638cbb6118" -version = "0.4.11" +version = "2.28.6+0" [[deps.Missings]] deps = ["DataAPI"] -git-tree-sha1 = "f66bdc5de519e8f8ae43bdc598782d35a25b1272" +git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.1.0" +version = "1.2.0" [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[deps.ModernGL]] -deps = ["Libdl"] -git-tree-sha1 = "b76ea40b5c0f45790ae09492712dd326208c28b2" -uuid = "66fc600b-dfda-50eb-8b99-91cfa97b1301" -version = "1.1.7" - -[[deps.Mods]] -git-tree-sha1 = "924f962b524a71eef7a21dae1e6853817f9b658f" -uuid = "7475f97c-0381-53b1-977b-4c60186c8d62" -version = "2.2.4" +version = "1.11.0" [[deps.MosaicViews]] deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] @@ -902,18 +876,7 @@ version = "0.3.4" [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.1.10" - -[[deps.Multisets]] -git-tree-sha1 = "8d852646862c96e226367ad10c8af56099b4047e" -uuid = "3b2b4ff1-bcff-5658-a3ee-dbcf1ce5ac09" -version = "0.4.4" - -[[deps.NLSolversBase]] -deps = ["DiffResults", "Distributed", "FiniteDiff", "ForwardDiff"] -git-tree-sha1 = "a0b464d183da839699f4c79e7606d9d186ec172c" -uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" -version = "7.8.3" +version = "2023.12.12" [[deps.NaNMath]] deps = ["OpenLibm_jll"] @@ -923,9 +886,9 @@ version = "1.0.2" [[deps.NearestNeighbors]] deps = ["Distances", "StaticArrays"] -git-tree-sha1 = "ded64ff6d4fdd1cb68dfcbb818c69e144a5b2e4c" +git-tree-sha1 = "3cebfc94a0754cc329ebc3bab1e6c89621e791ad" uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" -version = "0.4.16" +version = "0.4.20" [[deps.Netpbm]] deps = ["FileIO", "ImageCore", "ImageMetadata"] @@ -943,9 +906,9 @@ uuid = "510215fc-4207-5dde-b226-833fc4488ee2" version = "0.5.5" [[deps.OffsetArrays]] -git-tree-sha1 = "6a731f2b5c03157418a20c12195eb4b74c8f8621" +git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.13.0" +version = "1.14.1" weakdeps = ["Adapt"] [deps.OffsetArrays.extensions] @@ -960,7 +923,7 @@ version = "1.3.5+1" [[deps.OpenBLAS_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.23+4" +version = "0.3.27+1" [[deps.OpenEXR]] deps = ["Colors", "FileIO", "OpenEXR_jll"] @@ -970,9 +933,9 @@ version = "0.3.2" [[deps.OpenEXR_jll]] deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "a4ca623df1ae99d09bc9868b008262d0c0ac1e4f" +git-tree-sha1 = "8292dd5c8a38257111ada2174000a33745b06d4e" uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" -version = "3.1.4+0" +version = "3.2.4+0" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] @@ -981,9 +944,9 @@ version = "0.8.1+2" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "60e3045590bd104a16fefb12836c00c0ef8c7f8c" +git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.13+0" +version = "3.0.15+1" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -991,23 +954,11 @@ git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" version = "0.5.5+0" -[[deps.Optim]] -deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", "NLSolversBase", "NaNMath", "PackageExtensionCompat", "Parameters", "PositiveFactorizations", "Printf", "SparseArrays", "StatsBase"] -git-tree-sha1 = "d1223e69af90b6d26cea5b6f3b289b3148ba702c" -uuid = "429524aa-4258-5aef-a3af-852621145aeb" -version = "1.9.3" - - [deps.Optim.extensions] - OptimMOIExt = "MathOptInterface" - - [deps.Optim.weakdeps] - 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" @@ -1031,12 +982,6 @@ git-tree-sha1 = "67186a2bc9a90f9f85ff3cc8277868961fb57cbd" uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" version = "0.4.3" -[[deps.PackageExtensionCompat]] -git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" -uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" -version = "1.0.2" -weakdeps = ["Requires", "TOML"] - [[deps.Packing]] deps = ["GeometryBasics"] git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501" @@ -1049,11 +994,11 @@ git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f" uuid = "5432bcbf-9aad-5242-b902-cca2824c8663" version = "0.5.12" -[[deps.Parameters]] -deps = ["OrderedCollections", "UnPack"] -git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.3" +[[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.Parsers]] deps = ["Dates", "PrecompileTools", "UUIDs"] @@ -1061,28 +1006,20 @@ git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.8.1" -[[deps.Permutations]] -deps = ["Combinatorics", "LinearAlgebra", "Random"] -git-tree-sha1 = "eb3f9df2457819bf0a9019bd93cc451697a0751e" -uuid = "2ae35dd2-176d-5d53-8349-f30d82d94d4f" -version = "0.4.20" - -[[deps.PikaParser]] -deps = ["DocStringExtensions"] -git-tree-sha1 = "d6ff87de27ff3082131f31a714d25ab6d0a88abf" -uuid = "3bbf5609-3e7b-44cd-8549-7c69f321e792" -version = "0.6.1" - [[deps.Pixman_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87" +git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.42.2+0" +version = "0.43.4+0" [[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.10.0" +version = "1.11.0" +weakdeps = ["REPL"] + + [deps.Pkg.extensions] + REPLExt = "REPL" [[deps.PkgVersion]] deps = ["Pkg"] @@ -1091,40 +1028,16 @@ uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" version = "0.3.3" [[deps.PlotUtils]] -deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" +deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "StableRNGs", "Statistics"] +git-tree-sha1 = "3ca9a356cd2e113c420f2c13bea19f8d3fb1cb18" uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.4.1" +version = "1.4.3" [[deps.PolygonOps]] git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" uuid = "647866c9-e3ac-4575-94e7-e3d426903924" version = "0.1.2" -[[deps.Polynomials]] -deps = ["LinearAlgebra", "RecipesBase", "Setfield", "SparseArrays"] -git-tree-sha1 = "a9c7a523d5ed375be3983db190f6a5874ae9286d" -uuid = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" -version = "4.0.6" - - [deps.Polynomials.extensions] - PolynomialsChainRulesCoreExt = "ChainRulesCore" - PolynomialsFFTWExt = "FFTW" - PolynomialsMakieCoreExt = "MakieCore" - PolynomialsMutableArithmeticsExt = "MutableArithmetics" - - [deps.Polynomials.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" - MakieCore = "20f20a25-4f0e-4fdf-b5d1-57303727442b" - MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0" - -[[deps.PositiveFactorizations]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "17275485f373e6673f7e7f97051f703ed5b15b20" -uuid = "85a6dd25-e78a-55b7-8502-1745935b8125" -version = "0.2.4" - [[deps.PrecompileTools]] deps = ["Preferences"] git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" @@ -1137,21 +1050,21 @@ git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" uuid = "21216c6a-2e73-6563-6e65-726566657250" version = "1.4.3" -[[deps.Primes]] -deps = ["IntegerMathUtils"] -git-tree-sha1 = "cb420f77dc474d23ee47ca8d14c90810cafe69e7" -uuid = "27ebfcd6-29c5-5fa9-bf4b-fb8fc14df3ae" -version = "0.5.6" - [[deps.Printf]] deps = ["Unicode"] uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +version = "1.11.0" [[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.PtrArrays]] +git-tree-sha1 = "77a42d78b6a92df47ab37e177b2deac405e1c88f" +uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" +version = "1.2.1" [[deps.QOI]] deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] @@ -1161,17 +1074,25 @@ version = "1.0.0" [[deps.QuadGK]] deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "9b23c31e76e333e6fb4c1595ae6afa74966a729e" +git-tree-sha1 = "cda3b045cf9ef07a08ad46731f5a3165e56cf3da" uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.9.4" +version = "2.11.1" + + [deps.QuadGK.extensions] + QuadGKEnzymeExt = "Enzyme" + + [deps.QuadGK.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" [[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +version = "1.11.0" [[deps.Random]] deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +version = "1.11.0" [[deps.RangeArrays]] git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" @@ -1188,12 +1109,6 @@ weakdeps = ["FixedPointNumbers"] [deps.Ratios.extensions] RatiosFixedPointNumbersExt = "FixedPointNumbers" -[[deps.RecipesBase]] -deps = ["PrecompileTools"] -git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" -uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.4" - [[deps.Reexport]] git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" uuid = "189a3867-3050-52da-a836-e630ba90ab69" @@ -1211,23 +1126,17 @@ git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" -[[deps.RingLists]] -deps = ["Random"] -git-tree-sha1 = "f39da63aa6d2d88e0c1bd20ed6a3ff9ea7171ada" -uuid = "286e9d63-9694-5540-9e3c-4e6708fa07b2" -version = "0.2.8" - [[deps.Rmath]] deps = ["Random", "Rmath_jll"] -git-tree-sha1 = "f65dcb5fa46aee0cf9ed6274ccbd597adc49aa7b" +git-tree-sha1 = "852bd0f55565a9e973fcfee83a84413270224dc4" uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" -version = "0.7.1" +version = "0.8.0" [[deps.Rmath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6ed52fdd3382cf21947b15e8870ac0ddbff736da" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8" uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.4.0+0" +version = "0.5.1+0" [[deps.RoundingEmulator]] git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" @@ -1240,9 +1149,9 @@ version = "0.7.0" [[deps.SIMD]] deps = ["PrecompileTools"] -git-tree-sha1 = "d8911cc125da009051fb35322415641d02d9e37f" +git-tree-sha1 = "98ca7c29edd6fc79cd74c61accb7010a4e7aee33" uuid = "fdea26ae-647d-5447-a871-4b548cad5224" -version = "3.4.6" +version = "3.6.0" [[deps.Scratch]] deps = ["Dates"] @@ -1252,12 +1161,7 @@ version = "1.2.1" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.Setfield]] -deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] -git-tree-sha1 = "e2cc6d8c88613c05e1defb55170bf5ff211fbeac" -uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" -version = "1.1.1" +version = "1.11.0" [[deps.ShaderAbstractions]] deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] @@ -1268,6 +1172,7 @@ version = "0.4.1" [[deps.SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" +version = "1.11.0" [[deps.Showoff]] deps = ["Dates", "Grisu"] @@ -1281,30 +1186,6 @@ git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" version = "0.4.0" -[[deps.SimpleGraphs]] -deps = ["AbstractLattices", "Combinatorics", "DataStructures", "IterTools", "LightXML", "LinearAlgebra", "LinearAlgebraX", "Optim", "Primes", "Random", "RingLists", "SimplePartitions", "SimplePolynomials", "SimpleRandom", "SparseArrays", "Statistics"] -git-tree-sha1 = "f65caa24a622f985cc341de81d3f9744435d0d0f" -uuid = "55797a34-41de-5266-9ec1-32ac4eb504d3" -version = "0.8.6" - -[[deps.SimplePartitions]] -deps = ["AbstractLattices", "DataStructures", "Permutations"] -git-tree-sha1 = "e182b9e5afb194142d4668536345a365ea19363a" -uuid = "ec83eff0-a5b5-5643-ae32-5cbf6eedec9d" -version = "0.3.2" - -[[deps.SimplePolynomials]] -deps = ["Mods", "Multisets", "Polynomials", "Primes"] -git-tree-sha1 = "7063828369cafa93f3187b3d0159f05582011405" -uuid = "cc47b68c-3164-5771-a705-2bc0097375a0" -version = "0.2.17" - -[[deps.SimpleRandom]] -deps = ["Distributions", "LinearAlgebra", "Random"] -git-tree-sha1 = "3a6fb395e37afab81aeea85bae48a4db5cd7244a" -uuid = "a6525b86-64cd-54fa-8f65-62fc48bdc0e8" -version = "0.3.1" - [[deps.SimpleTraits]] deps = ["InteractiveUtils", "MacroTools"] git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" @@ -1319,6 +1200,7 @@ version = "0.1.3" [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +version = "1.11.0" [[deps.SortingAlgorithms]] deps = ["DataStructures"] @@ -1329,23 +1211,23 @@ version = "1.2.1" [[deps.SparseArrays]] deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.10.0" +version = "1.11.0" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" +git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.3.1" +version = "2.4.0" weakdeps = ["ChainRulesCore"] [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" -[[deps.StableHashTraits]] -deps = ["Compat", "PikaParser", "SHA", "Tables", "TupleTools"] -git-tree-sha1 = "10dc702932fe05a0e09b8e5955f00794ea1e8b12" -uuid = "c5dd0088-6c3f-4803-b00e-f31a60c170fa" -version = "1.1.8" +[[deps.StableRNGs]] +deps = ["Random"] +git-tree-sha1 = "83e6cce8324d49dfaf9ef059227f91ed4441a8e5" +uuid = "860ef19b-820b-49d6-a774-d7a799459cd3" +version = "1.0.2" [[deps.StackViews]] deps = ["OffsetArrays"] @@ -1355,9 +1237,9 @@ version = "0.1.1" [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "bf074c045d3d5ffd956fa0a461da38a44685d6b2" +git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.3" +version = "1.9.8" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -1365,14 +1247,19 @@ weakdeps = ["ChainRulesCore", "Statistics"] StaticArraysStatisticsExt = "Statistics" [[deps.StaticArraysCore]] -git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.2" +version = "1.4.3" [[deps.Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] +deps = ["LinearAlgebra"] +git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.10.0" +version = "1.11.1" +weakdeps = ["SparseArrays"] + + [deps.Statistics.extensions] + SparseArraysExt = ["SparseArrays"] [[deps.StatsAPI]] deps = ["LinearAlgebra"] @@ -1382,24 +1269,21 @@ version = "1.7.0" [[deps.StatsBase]] deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "1d77abd07f617c4868c33d4f5b9e1dbb2643c9cf" +git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.2" +version = "0.34.3" [[deps.StatsFuns]] deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "cef0472124fab0695b58ca35a77c6fb942fdab8a" +git-tree-sha1 = "b423576adc27097764a90e163157bcfc9acf0f46" uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.3.1" +version = "1.3.2" +weakdeps = ["ChainRulesCore", "InverseFunctions"] [deps.StatsFuns.extensions] StatsFunsChainRulesCoreExt = "ChainRulesCore" StatsFunsInverseFunctionsExt = "InverseFunctions" - [deps.StatsFuns.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - [[deps.StructArrays]] deps = ["ConstructionBase", "DataAPI", "Tables"] git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be" @@ -1418,6 +1302,10 @@ version = "0.6.18" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +[[deps.StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" +version = "1.11.0" + [[deps.SuiteSparse]] deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" @@ -1425,7 +1313,7 @@ uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.2.1+1" +version = "7.7.0+0" [[deps.TOML]] deps = ["Dates"] @@ -1439,10 +1327,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"] @@ -1464,43 +1352,32 @@ version = "1.16.1" [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +version = "1.11.0" [[deps.TiffImages]] -deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "UUIDs"] -git-tree-sha1 = "34cc045dd0aaa59b8bbe86c644679bc57f1d5bd0" +deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"] +git-tree-sha1 = "6ee0c220d0aecad18792c277ae358129cc50a475" uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" -version = "0.6.8" +version = "0.11.0" [[deps.TranscodingStreams]] -git-tree-sha1 = "a09c933bebed12501890d8e92946bbab6a1690f1" +git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.5" -weakdeps = ["Random", "Test"] - - [deps.TranscodingStreams.extensions] - TestExt = ["Test", "Random"] +version = "0.11.3" [[deps.TriplotBase]] git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" uuid = "981d1d27-644d-49a2-9326-4793e63143c3" version = "0.1.0" -[[deps.TupleTools]] -git-tree-sha1 = "41d61b1c545b06279871ef1a4b5fcb2cac2191cd" -uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" -version = "1.5.0" - [[deps.UUIDs]] deps = ["Random", "SHA"] uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.UnPack]] -git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" -uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" -version = "1.0.2" +version = "1.11.0" [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" [[deps.UnicodeFun]] deps = ["REPL"] @@ -1508,11 +1385,28 @@ git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" version = "0.4.1" +[[deps.Unitful]] +deps = ["Dates", "LinearAlgebra", "Random"] +git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd" +uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" +version = "1.21.0" +weakdeps = ["ConstructionBase", "InverseFunctions"] + + [deps.Unitful.extensions] + ConstructionBaseUnitfulExt = "ConstructionBase" + InverseFunctionsUnitfulExt = "InverseFunctions" + [[deps.VTKBase]] git-tree-sha1 = "c2d0db3ef09f1942d08ea455a9e252594be5f3b6" uuid = "4004b06d-e244-455f-a6ce-a5f9919cc534" version = "1.0.1" +[[deps.WebP]] +deps = ["CEnum", "ColorTypes", "FileIO", "FixedPointNumbers", "ImageCore", "libwebp_jll"] +git-tree-sha1 = "f1f6d497ff84039deeb37f264396dac0c2250497" +uuid = "e3aaa7dc-3e4b-44e0-be63-ffb868ccd7c1" +version = "0.1.2" + [[deps.WoodburyMatrices]] deps = ["LinearAlgebra", "SparseArrays"] git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511" @@ -1521,21 +1415,27 @@ version = "1.0.0" [[deps.WriteVTK]] deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] -git-tree-sha1 = "17877c404fd20090e3998a66f6f44cf01e2b1e60" +git-tree-sha1 = "1d8042d58334ab7947ce505709df7009da6f3375" uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192" -version = "1.18.3" +version = "1.21.1" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "07e470dabc5a6a4254ffebc29a1b3fc01464e105" +git-tree-sha1 = "6a451c6f33a176150f315726eba8b92fbfdb9ae7" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.12.5+0" +version = "2.13.4+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"] +git-tree-sha1 = "15e637a697345f6743674f1322beefbc5dcd5cfc" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.6.3+0" [[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] @@ -1549,12 +1449,6 @@ git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" version = "1.0.11+0" -[[deps.Xorg_libXcursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" -uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" -version = "1.2.0+4" - [[deps.Xorg_libXdmcp_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" @@ -1562,40 +1456,16 @@ uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" version = "1.1.4+0" [[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.4+4" - -[[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" - -[[deps.Xorg_libXi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] -git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" -uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" -version = "1.7.10+4" - -[[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" - -[[deps.Xorg_libXrandr_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" -uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" -version = "1.5.2+4" +version = "1.3.6+0" [[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.10+4" +version = "0.9.11+0" [[deps.Xorg_libpthread_stubs_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1605,9 +1475,9 @@ version = "0.1.1+0" [[deps.Xorg_libxcb_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "b4bfde5d5b652e22b9c790ad00af08b6d042b97d" +git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.15.0+0" +version = "1.17.0+0" [[deps.Xorg_xtrans_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1620,6 +1490,12 @@ deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" version = "1.2.13+1" +[[deps.Zstd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b" +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.6+1" + [[deps.isoband_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" @@ -1627,50 +1503,62 @@ uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" version = "0.2.3+0" [[deps.libaom_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.4.0+0" +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.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.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "d7015d2e18a5fd9a4f47de711837e980519781a4" +git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.43+1" +version = "1.6.44+0" [[deps.libsixel_jll]] deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] -git-tree-sha1 = "d4f63314c8aa1e48cd22aa0c17ed76cd1ae48c3c" +git-tree-sha1 = "7dfa0fd9c783d3d0cc43ea1af53d69ba45c447df" uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" -version = "1.10.3+0" +version = "1.10.3+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.libwebp_jll]] +deps = ["Artifacts", "Giflib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Libtiff_jll", "libpng_jll"] +git-tree-sha1 = "ccbb625a89ec6195856a50aa2b668a5c08712c94" +uuid = "c5f90fcd-3b7e-5836-afba-fc50a0988cb2" +version = "1.4.0+0" [[deps.nghttp2_jll]] deps = ["Artifacts", "Libdl"] uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.52.0+1" +version = "1.59.0+0" + +[[deps.oneTBB_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" +uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" +version = "2021.12.0+0" [[deps.p7zip_jll]] deps = ["Artifacts", "Libdl"] @@ -1678,13 +1566,13 @@ uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" version = "17.4.0+2" [[deps.x264_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "35976a1216d6c066ea32cba2150c4fa682b276fc" uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" -version = "2021.5.5+0" +version = "10164.0.0+0" [[deps.x265_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "dcc541bb19ed5b0ede95581fb2e41ecf179527d2" uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" -version = "3.5.0+0" +version = "3.6.0+0" diff --git a/visualization/Project.toml b/visualization/Project.toml index c5b64e5103..ecf047c111 100644 --- a/visualization/Project.toml +++ b/visualization/Project.toml @@ -1,4 +1,3 @@ [deps] +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" -FerriteViz = "59d0093e-b1f1-4fb7-ac85-ab57e45f39d9" -GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a" From a696fe00b92ea27ebdf613d1064850c579fb5c63 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 12 Nov 2024 11:00:49 +0100 Subject: [PATCH 135/172] Fix BrezziDouglasMarini --- src/interpolations.jl | 10 ++--- test/test_interpolations.jl | 6 ++- .../2d_vector_interpolation_checkplots.jl | 44 +++++++++++++++++-- visualization/Manifest.toml | 2 +- visualization/Project.toml | 1 + 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/interpolations.jl b/src/interpolations.jl index b6fc6c4f9b..4513674073 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1861,7 +1861,7 @@ end struct BrezziDouglasMarini{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end mapping_type(::BrezziDouglasMarini) = ContravariantPiolaMapping() reference_coordinates(ip::BrezziDouglasMarini{vdim}) where vdim = fill(NaN*zero(Vec{vdim}), getnbasefunctions(ip)) -dirichlet_facedof_indices(ip::BrezziDouglasMarini) = facetdof_interior_indices(ip) +#dirichlet_facedof_indices(ip::BrezziDouglasMarini) = facetdof_interior_indices(ip) n_dbc_components(::BrezziDouglasMarini) = 1 #= ----------------+-------------------- @@ -1886,11 +1886,11 @@ Edge numbers: | Edge identifiers: function reference_shape_value(ip::BrezziDouglasMarini{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) x, y = ξ # Edge 1 - i == 1 && return Vec(4x, -2y) # Changed sign to make positive outwards + i == 1 && return Vec(4x, -2y) # Changed sign to integrated value positive outwards i == 2 && return Vec(-2x, 4y) # Changed sign to make positive outwards # Edge 2 (reverse order to follow Ferrite convention) - i == 3 && return Vec(-2x - 6y + 2, 4y) - i == 4 && return Vec(4x + 6y - 4, -2y) + i == 3 && return Vec(-2x - 6y + 2, 4y) # N ⋅ n = (6y - 2) + i == 4 && return Vec(4x + 6y - 4, -2y) # N ⋅ n = (4 - 6y) # Edge 3 i == 5 && return Vec(-2x, 6x + 4y - 4) # Changed sign to make positive outwards i == 6 && return Vec(4x, -6x - 2y + 2) # Changed sign to make positive outwards @@ -1899,7 +1899,7 @@ end getnbasefunctions(::BrezziDouglasMarini{2, RefTriangle, 1}) = 6 edgedof_interior_indices(::BrezziDouglasMarini{2, RefTriangle, 1}) = ((1, 2), (3, 4), (5, 6)) -adjust_dofs_during_distribution(::BrezziDouglasMarini{2, RefTriangle, 1}) = false +adjust_dofs_during_distribution(::BrezziDouglasMarini{2, RefTriangle, 1}) = true function get_direction(::BrezziDouglasMarini{2, RefTriangle, 1}, j, cell) edge = edges(cell)[(j + 1) ÷ 2] diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 6ca757869f..17b52e55ff 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -309,7 +309,11 @@ end # 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 @testset "H(div) on RefCell" begin lineqr = QuadratureRule{RefLine}(20) - for ip in (RaviartThomas{2, RefTriangle, 1}(), Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), ) + for ip in ( + RaviartThomas{2, RefTriangle, 1}(), + RaviartThomas{2, RefTriangle, 2}(), + Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), + ) cell = reference_cell(getrefshape(ip)) cell_facets = Ferrite.facets(cell) dofs = Ferrite.facetdof_interior_indices(ip) diff --git a/visualization/2d_vector_interpolation_checkplots.jl b/visualization/2d_vector_interpolation_checkplots.jl index d51487bbc3..d48b4ad360 100644 --- a/visualization/2d_vector_interpolation_checkplots.jl +++ b/visualization/2d_vector_interpolation_checkplots.jl @@ -1,6 +1,13 @@ using Ferrite import CairoMakie as Plt +function facet_quadrature(::Type{RefShape}, nqp::Int) where RefShape + fqr = FacetQuadratureRule{RefShape}(nqp) + points = [p for rule in fqr.face_rules for p in rule.points] + weights = [w for rule in fqr.face_rules for w in rule.weights] + return QuadratureRule{RefShape}(weights, points) +end + function plot_shape_function(ip::VectorInterpolation{2, RefShape}, qr::Int, i::Int) where {RefShape<:Ferrite.AbstractRefShape{2}} return plot_shape_function(ip, QuadratureRule{RefShape}(qr), i) end @@ -12,12 +19,41 @@ function plot_shape_function(ip::VectorInterpolation{2, RefShape}, qr::Quadratur push!(vertices, first(vertices)) fig = Plt.Figure() - ax = Plt.Axis(fig[1,1]) + ax = Plt.Axis(fig[1,1]; aspect=Plt.DataAspect()) + Plt.xlims!(ax, (-1.0, 1.5)) Plt.lines!(ax, first.(vertices), last.(vertices)) - Plt.arrows!(ax, first.(points), last.(points), first.(N), last.(N); lengthscale = 0.3) + Plt.arrows!(ax, first.(points), last.(points), first.(N), last.(N); lengthscale = 0.1) + Plt.scatter!(ax, first.(points), last.(points)) return fig end -function plot_global_shape_function(ip, qr, nx, ny, i) - #TODO: Plot a single global shape function to investigate continuity +function plot_global_shape_function(ip::VectorInterpolation{2, RefShape}; qr_order::Int=0, nel, i, qr=nothing) where RefShape + fig = Plt.Figure() + ax = Plt.Axis(fig[1,1]) + _qr = qr === nothing ? QuadratureRule{RefShape}(qr_order) : qr + CT = RefShape === RefTriangle ? Triangle : Quadrilateral + grid = generate_grid(CT, (nel, nel)) + dh = close!(add!(DofHandler(grid), :u, ip)) + points = Vec{2, Float64}[] + directions = Vec{2, Float64}[] + cv = CellValues(_qr, ip, Lagrange{RefShape, 1}()) + cell_contour = getcoordinates(grid, 1) + resize!(cell_contour, length(cell_contour) + 1) + for cell in CellIterator(dh) + copyto!(cell_contour, getcoordinates(cell)) + cell_contour[end] = cell_contour[1] # Close contour + Plt.lines!(ax, first.(cell_contour), last.(cell_contour)) + if i ∈ celldofs(cell) + reinit!(cv, cell) + for q_point in 1:getnquadpoints(cv) + x = spatial_coordinate(cv, q_point, getcoordinates(cell)) + shape_index = findfirst(x -> x == i, celldofs(cell)) + push!(points, x) + push!(directions, shape_value(cv, q_point, shape_index)) + end + end + end + Plt.arrows!(ax, first.(points), last.(points), first.(directions), last.(directions); lengthscale = 0.1) + Plt.scatter!(ax, first.(points), last.(points)) + return fig end diff --git a/visualization/Manifest.toml b/visualization/Manifest.toml index 96331314ab..dfda582b2d 100644 --- a/visualization/Manifest.toml +++ b/visualization/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.11.1" manifest_format = "2.0" -project_hash = "f8140ea440af404cde81e65338add89dc118d0fc" +project_hash = "3f9b12f33e28e03dfcb3edf6f53efa43b44e834b" [[deps.AbstractFFTs]] deps = ["LinearAlgebra"] diff --git a/visualization/Project.toml b/visualization/Project.toml index ecf047c111..e101770250 100644 --- a/visualization/Project.toml +++ b/visualization/Project.toml @@ -1,3 +1,4 @@ [deps] CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" From 80f5b251fb9b58bf41be465906b0a27989776189 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 12 Nov 2024 15:03:29 +0100 Subject: [PATCH 136/172] Work through tutorial --- docs/make.jl | 3 +- docs/src/assets/references.bib | 22 ++ .../literate-tutorials/heat_equation_hdiv.jl | 233 +++++++++++ .../literate-tutorials/heat_equation_rt.jl | 372 ------------------ .../heat_equation_triangle.jl | 252 ------------ test/test_interpolations.jl | 4 +- 6 files changed, 258 insertions(+), 628 deletions(-) create mode 100644 docs/src/literate-tutorials/heat_equation_hdiv.jl delete mode 100644 docs/src/literate-tutorials/heat_equation_rt.jl delete mode 100644 docs/src/literate-tutorials/heat_equation_triangle.jl diff --git a/docs/make.jl b/docs/make.jl index 8876469345..1d5a7b6a10 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -55,8 +55,7 @@ bibtex_plugin = CitationBibliography( "Tutorials" => [ "Tutorials overview" => "tutorials/index.md", "tutorials/heat_equation.md", - "tutorials/heat_equation_rt.md", - "tutorials/heat_equation_triangle.md", + "tutorials/heat_equation_hdiv.md", "tutorials/linear_elasticity.md", "tutorials/incompressible_elasticity.md", "tutorials/hyperelasticity.md", diff --git a/docs/src/assets/references.bib b/docs/src/assets/references.bib index c240c43833..0678618983 100644 --- a/docs/src/assets/references.bib +++ b/docs/src/assets/references.bib @@ -190,3 +190,25 @@ @article{CroRav:1973:cnf year={1973}, publisher={EDP Sciences} } +@book{Gatica2014, +title = {A Simple Introduction to the Mixed Finite Element Method: Theory and Applications}, +ISBN = {9783319036953}, +ISSN = {2191-8201}, +url = {http://dx.doi.org/10.1007/978-3-319-03695-3}, +DOI = {10.1007/978-3-319-03695-3}, +journal = {SpringerBriefs in Mathematics}, +publisher = {Springer International Publishing}, +author = {Gatica, Gabriel N.}, +year = {2014} +} +@book{Boffi2013, + title = {Mixed Finite Element Methods and Applications}, + ISBN = {9783642365195}, + ISSN = {0179-3632}, + url = {http://dx.doi.org/10.1007/978-3-642-36519-5}, + DOI = {10.1007/978-3-642-36519-5}, + journal = {Springer Series in Computational Mathematics}, + publisher = {Springer Berlin Heidelberg}, + author = {Boffi, Daniele and Brezzi, Franco and Fortin, Michel}, + year = {2013} +} diff --git a/docs/src/literate-tutorials/heat_equation_hdiv.jl b/docs/src/literate-tutorials/heat_equation_hdiv.jl new file mode 100644 index 0000000000..155e42bd66 --- /dev/null +++ b/docs/src/literate-tutorials/heat_equation_hdiv.jl @@ -0,0 +1,233 @@ +# # [Heat equation - Mixed H(div) conforming formulation)](@id tutorial-heat-equation-hdiv) +# As an alternative to the standard formulation for solving the heat equation used in +# the [heat equation tutorial](@ref tutorial-heat-equation), we can used a mixed formulation +# where both the temperature, $u(\mathbf{x})$, and the heat flux, $\boldsymbol{q}(\boldsymbol{x})$, +# are primary variables. From a theoretical standpoint, there are many details on e.g. which combinations +# of interpolations that are stable. See e.g. [Gatica2014](@cite) and [Boffi2013](@cite) for further reading. +# This tutorial is based on the theory in +# [Fenics' mixed poisson example](https://fenicsproject.org/olddocs/dolfin/1.4.0/python/demo/documented/mixed-poisson/python/documentation.html). +# +# ![Temperature solution](https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/refs/heads/gh-pages/assets/heat_equation_hdiv.png) +# **Figure:** Temperature distribution considering a central part with lower heat conductivity. +# +# The advantage with the mixed formulation is that the heat flux is approximated better. However, the +# temperature becomes discontinuous where the conductivity is discontinuous. +# +# ## Theory +# We start with the strong form of the heat equation: Find the temperature, $u(\boldsymbol{x})$, and heat flux, $\boldsymbol{q}(x)$, +# such that +# ```math +# \begin{align*} +# \boldsymbol{\nabla}\cdot \boldsymbol{q} &= h(\boldsymbol{x}), \quad \forall \boldsymbol{x} \in \Omega \\ +# \boldsymbol{q}(\boldsymbol{x}) &= - k\ \boldsymbol{\nabla} u(\boldsymbol{x}), \quad \forall \boldsymbol{x} \in \Omega \\ +# \boldsymbol{q}(\boldsymbol{x})\cdot \boldsymbol{n}(\boldsymbol{x}) &= q_n, \quad \forall \boldsymbol{x} \in \Gamma_\mathrm{N}\\ +# u(\boldsymbol{x}) &= u_\mathrm{D}, \quad \forall \boldsymbol{x} \in \Gamma_\mathrm{D} +# \end{align*} +# ``` +# +# From this strong form, we can formulate the weak form as a mixed formulation. +# Find $u \in \mathbb{U}$ and $\boldsymbol{q}\in\mathbb{Q}$ such that +# ```math +# \begin{align*} +# \int_{\Omega} \delta u [\boldsymbol{\nabla} \cdot \boldsymbol{q}]\ \mathrm{d}\Omega &= \int_\Omega \delta u h\ \mathrm{d}\Omega, \quad \forall\ \delta u \in \delta\mathbb{U} \\ +# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= -\int_\Omega \boldsymbol{\delta q} \cdot [k\ \boldsymbol{\nabla} u]\ \mathrm{d}\Omega \\ +# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega - \int_{\Omega} [\boldsymbol{\nabla} \cdot \boldsymbol{\delta q}] k u \ \mathrm{d}\Omega &= +# -\int_\Gamma \boldsymbol{\delta q} \cdot \boldsymbol{n} k\ u\ \mathrm{d}\Omega, \quad \forall\ \boldsymbol{\delta q} \in \delta\mathbb{Q} +# \end{align*} +# ``` +# where we have the function spaces, +# ```math +# \begin{align*} +# \mathbb{U} &= \delta\mathbb{U} = L^2 \\ +# \mathbb{Q} &= \lbrace \boldsymbol{q} \in H(\mathrm{div}) \text{ such that } \boldsymbol{q}\cdot\boldsymbol{n} = q_\mathrm{n} \text{ on } \Gamma_\mathrm{D}\rbrace \\ +# \delta\mathbb{Q} &= \lbrace \boldsymbol{q} \in H(\mathrm{div}) \text{ such that } \boldsymbol{q}\cdot\boldsymbol{n} = 0 \text{ on } \Gamma_\mathrm{D}\rbrace +# \end{align*} +# ``` +# A stable choice of finite element spaces for this problem on grid with triangles is using +# * `DiscontinuousLagrange{RefTriangle, k-1}` for approximating $L^2$ +# * `BrezziDouglasMarini{RefTriangle, k}` for approximating $H(\mathrm{div})$ +# +# We will also investigate the consequences of using $H^1$ `Lagrange` instead of $H(\mathrm{div})$ interpolations. +# +# ## Commented Program +# +# Now we solve the problem in Ferrite. What follows is a program spliced with comments. +# +# First we load Ferrite, +using Ferrite +# And define our grid, representing a channel with a central part having a lower +# conductivity, $k$, than the surrounding. +function create_grid(ny::Int) + width = 10.0 + length = 40.0 + center_width = 5.0 + center_length = 20.0 + upper_right = Vec((length / 2, width / 2)) + grid = generate_grid(Triangle, (round(Int, ny * length / width), ny), -upper_right, upper_right); + addcellset!(grid, "center", x -> abs(x[1]) < center_length/2 && abs(x[2]) < center_width / 2) + addcellset!(grid, "around", setdiff(1:getncells(grid), getcellset(grid, "center"))) + return grid +end + +grid = create_grid(100) + +# ### Setup +# We define one `CellValues` for each field which share the same quadrature rule. +ip_geo = geometric_interpolation(getcelltype(grid)) +ipu = DiscontinuousLagrange{RefTriangle, 0}() +ipq = Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}() +qr = QuadratureRule{RefTriangle}(2) +cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) + +# Distribute the degrees of freedom +dh = DofHandler(grid) +add!(dh, :u, ipu) +add!(dh, :q, ipq) +close!(dh); + +# In this problem, we have a zero temperature on the boundary, Γ, which is enforced +# via zero Neumann boundary conditions. Hence, we don't need a `Constrainthandler`. +Γ = union((getfacetset(grid, name) for name in ("left", "right", "bottom", "top"))...) + +# ### Element implementation + +function assemble_element!(Ke::Matrix, fe::Vector, cv::NamedTuple, dr::NamedTuple, k::Number) + cvu = cv[:u] + cvq = cv[:q] + dru = dr[:u] + drq = dr[:q] + h = 1.0 # Heat source + ## Loop over quadrature points + for q_point in 1:getnquadpoints(cvu) + ## Get the quadrature weight + dΩ = getdetJdV(cvu, q_point) + ## Loop over test shape functions + for (iu, Iu) in pairs(dru) + δNu = shape_value(cvu, q_point, iu) + ## Add contribution to fe + fe[Iu] += δNu * h * dΩ + ## Loop over trial shape functions + for (jq, Jq) in pairs(drq) + div_Nq = shape_divergence(cvq, q_point, jq) + ## Add contribution to Ke + Ke[Iu, Jq] += (δNu * div_Nq) * dΩ + end + end + for (iq, Iq) in pairs(drq) + δNq = shape_value(cvq, q_point, iq) + div_δNq = shape_divergence(cvq, q_point, iq) + for (ju, Ju) in pairs(dru) + Nu = shape_value(cvu, q_point, ju) + Ke[Iq, Ju] -= div_δNq * k * Nu * dΩ + end + for (jq, Jq) in pairs(drq) + Nq = shape_value(cvq, q_point, jq) + Ke[Iq, Jq] += (δNq ⋅ Nq) * dΩ + end + end + end + return Ke, fe +end +#md nothing # hide + +# ### Global assembly + +function assemble_global(cellvalues, dh::DofHandler) + grid = dh.grid + ## Allocate the element stiffness matrix and element force vector + dofranges = (u = dof_range(dh, :u), q = dof_range(dh, :q)) + ncelldofs = ndofs_per_cell(dh) + Ke = zeros(ncelldofs, ncelldofs) + fe = zeros(ncelldofs) + ## Allocate global system matrix and vector + K = allocate_matrix(dh) + f = zeros(ndofs(dh)) + ## Create an assembler + assembler = start_assemble(K, f) + x = copy(getcoordinates(grid, 1)) + dofs = copy(celldofs(dh, 1)) + ## Loop over all cells + for (cells, k) in ( + (getcellset(grid, "center"), 0.1), + (getcellset(grid, "around"), 1.00)) + for cellnr in cells + ## Reinitialize cellvalues for this cell + cell = getcells(grid, cellnr) + getcoordinates!(x, grid, cell) + celldofs!(dofs, dh, cellnr) + reinit!(cellvalues[:u], cell, x) + reinit!(cellvalues[:q], cell, x) + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues, dofranges, k) + ## Assemble Ke and fe into K and f + assemble!(assembler, dofs, Ke, fe) + end + end + return K, f +end +#md nothing # hide + +# ### Solution of the system +K, f = assemble_global(cellvalues, dh); +u = K \ f; + +# ### Exporting to VTK +# Currently, exporting discontinuous interpolations is not supported. +# Since in this case, we have a single temperature degree of freedom +# per cell, we work around this by calculating the per-cell temperature. +temperature_dof = first(dof_range(dh, :u)) +u_cells = map(1:getncells(grid)) do i + u[celldofs(dh, i)[temperature_dof]] +end +VTKGridFile("heat_equation_hdiv", dh) do vtk + write_cell_data(vtk, u_cells, "temperature") +end + +# ## Postprocess the total flux +# We applied a constant unit heat source to the body, and the +# total heat flux exiting across the boundary should therefore +# match the area for the considered stationary case. +function calculate_flux(dh, boundary_facets, ip, a) + grid = dh.grid + qr = FacetQuadratureRule{RefTriangle}(4) + ip_geo = geometric_interpolation(getcelltype(grid)) + fv = FacetValues(qr, ip, ip_geo) + + dofrange = dof_range(dh, :q) + flux = 0.0 + dofs = celldofs(dh, 1) + ae = zeros(length(dofs)) + x = getcoordinates(grid, 1) + for (cellnr, facetnr) in boundary_facets + getcoordinates!(x, grid, cellnr) + cell = getcells(grid, cellnr) + celldofs!(dofs, dh, cellnr) + map!(i->a[i], ae, dofs) + reinit!(fv, cell, x, facetnr) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + q = function_value(fv, q_point, ae, dofrange) + flux += (q ⋅ n)*dΓ + end + end + return flux +end + +println("Outward flux: ", calculate_flux(dh, Γ, ipq, u)) + +# Note that this is not the case for the standard [Heat equation](@id tutorial-heat-equation), +# as the flux terms are less accurately approximated. A fine mesh is required to converge in that case. +# However, the present example gives a worse approximation of the temperature field. + +#md # ## [Plain program](@id tutorial-heat-equation-hdiv-plain) +#md # +#md # Here follows a version of the program without any comments. +#md # The file is also available here: [`heat_equation_hdiv.jl`](heat_equation_hdiv.jl). +#md # +#md # ```julia +#md # @__CODE__ +#md # ``` diff --git a/docs/src/literate-tutorials/heat_equation_rt.jl b/docs/src/literate-tutorials/heat_equation_rt.jl deleted file mode 100644 index 2d22955038..0000000000 --- a/docs/src/literate-tutorials/heat_equation_rt.jl +++ /dev/null @@ -1,372 +0,0 @@ -# # [Heat equation (Mixed, RaviartThomas)](@id tutorial-heat-equation-rt) -# Note, there are a lot to consider here it seems like. Good refs, -# ``` -# @book{Gatica2014, -# title = {A Simple Introduction to the Mixed Finite Element Method: Theory and Applications}, -# ISBN = {9783319036953}, -# ISSN = {2191-8201}, -# url = {http://dx.doi.org/10.1007/978-3-319-03695-3}, -# DOI = {10.1007/978-3-319-03695-3}, -# journal = {SpringerBriefs in Mathematics}, -# publisher = {Springer International Publishing}, -# author = {Gatica, Gabriel N.}, -# year = {2014} -# } -# See also, -# @book{Boffi2013, -# title = {Mixed Finite Element Methods and Applications}, -# ISBN = {9783642365195}, -# ISSN = {0179-3632}, -# url = {http://dx.doi.org/10.1007/978-3-642-36519-5}, -# DOI = {10.1007/978-3-642-36519-5}, -# journal = {Springer Series in Computational Mathematics}, -# publisher = {Springer Berlin Heidelberg}, -# author = {Boffi, Daniele and Brezzi, Franco and Fortin, Michel}, -# year = {2013} -# } -# for a(n even) more comprehensive book. -# ``` -# -# ## Theory -# We start with the strong form of the heat equation: Find the temperature, $u(\boldsymbol{x})$, and heat flux, $\boldsymbol{q}(x)$, -# such that -# ```math -# \begin{align*} -# \boldsymbol{\nabla}\cdot \boldsymbol{q} &= h(\boldsymbol{x}), \quad \forall \boldsymbol{x} \in \Omega \\ -# \boldsymbol{q}(\boldsymbol{x}) &= - k\ \boldsymbol{\nabla} u(\boldsymbol{x}), \quad \forall \boldsymbol{x} \in \Omega \\ -# \boldsymbol{q}(\boldsymbol{x})\cdot \boldsymbol{n}(\boldsymbol{x}) &= q_n, \quad \forall \boldsymbol{x} \in \Gamma_\mathrm{N}\\ -# u(\boldsymbol{x}) &= u_\mathrm{D}, \quad \forall \boldsymbol{x} \in \Gamma_\mathrm{D} -# \end{align*} -# ``` -# -# From this strong form, we can formulate the weak form as a mixed formulation. -# Find $u \in \mathbb{U}$ and $\boldsymbol{q}\in\mathbb{Q}$ such that -# ```math -# \begin{align*} -# \int_{\Omega} \delta u [\boldsymbol{\nabla} \cdot \boldsymbol{q}]\ \mathrm{d}\Omega &= - \int_\Omega \delta u h\ \mathrm{d}\Omega, \quad \forall\ \delta u \in \delta\mathbb{U} \\ -# %\int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= \int_\Omega \boldsymbol{\delta q} \cdot [k\ \boldsymbol{\nabla} u]\ \mathrm{d}\Omega \\ -# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega + \int_{\Omega} [\boldsymbol{\nabla} \cdot \boldsymbol{\delta q}] k u \ \mathrm{d}\Omega &= -# \int_\Gamma \boldsymbol{\delta q} \cdot \boldsymbol{n} k\ u\ \mathrm{d}\Omega, \quad \forall\ \boldsymbol{\delta q} \in \delta\mathbb{Q} -# \end{align*} -# ``` -# where we have the function spaces -# * $\mathbb{U} = \delta\mathbb{U} = L^2$ -# * $\mathbb{Q} = \lbrace \boldsymbol{q} \in H(\mathrm{div}) \text{such that} \boldsymbol{q}\cdot\boldsymbol{n} = q_\mathrm{n} \text{ on } \Gamma_\mathrm{D}\rbrace$ -# * $\mathbb{Q} = \lbrace \boldsymbol{q} \in H(\mathrm{div}) \text{such that} \boldsymbol{q}\cdot\boldsymbol{n} = 0 \text{ on } \Gamma_\mathrm{D}\rbrace$ -# -# A stable choice of finite element spaces for this problem on grid with triangles is using -# * `DiscontinuousLagrange{RefTriangle, k-1}` for approximating $L^2$ -# * `BrezziDouglasMarini{RefTriangle, k}` for approximating $H(\mathrm{div})$ -# following [fenics](https://fenicsproject.org/olddocs/dolfin/1.4.0/python/demo/documented/mixed-poisson/python/documentation.html). -# For further details, see Boffi2013. -# We will also see what happens if we instead use `Lagrange` elements which are a subspace of $H^1$ instead of $H(\mathrm{div})$ elements. -# -# ## Commented Program -# -# Now we solve the problem in Ferrite. What follows is a program spliced with comments. -# -# First we load Ferrite, -using Ferrite -# And define our grid, representing a channel with a central part having a lower -# conductivity, $k$, than the surrounding. -function create_grid(ny::Int) - width = 10.0 - length = 40.0 - center_width = 5.0 - center_length = 20.0 - upper_right = Vec((length / 2, width / 2)) - grid = generate_grid(Triangle, (round(Int, ny * length / width), ny), -upper_right, upper_right); - addcellset!(grid, "center", x -> abs(x[1]) < center_width/2 && abs(x[2]) < center_length / 2) - addcellset!(grid, "around", setdiff(1:getncells(grid), getcellset(grid, "center"))) - return grid -end - -grid = create_grid(10) - -# ### Trial and test functions -# A `CellValues` facilitates the process of evaluating values and gradients of -# test and trial functions (among other things). To define -# this we need to specify an interpolation space for the shape functions. -# We use Lagrange functions -# based on the two-dimensional reference quadrilateral. We also define a quadrature rule based on -# the same reference element. We combine the interpolation and the quadrature rule -# to a `CellValues` object. -ip_geo = geometric_interpolation(getcelltype(grid)) -ipu = DiscontinuousLagrange{RefTriangle, 1}() # Why does it "explode" for 2nd order ipu? -ipq = RaviartThomas{2,RefTriangle, 1}() -qr = QuadratureRule{RefTriangle}(2) -cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) - -# ### Degrees of freedom -# Next we need to define a `DofHandler`, which will take care of numbering -# and distribution of degrees of freedom for our approximated fields. -# We create the `DofHandler` and then add a single scalar field called `:u` based on -# our interpolation `ip` defined above. -# Lastly we `close!` the `DofHandler`, it is now that the dofs are distributed -# for all the elements. -dh = DofHandler(grid) -add!(dh, :u, ipu) -add!(dh, :q, ipq) -close!(dh); - -# Now that we have distributed all our dofs we can create our tangent matrix, -# using `create_sparsity_pattern`. This function returns a sparse matrix -# with the correct entries stored. -K = allocate_matrix(dh) - -# ### Boundary conditions -# In Ferrite constraints like Dirichlet boundary conditions -# are handled by a `ConstraintHandler`. -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 boundary facet sets. -∂Ω = union( - getfacetset(grid, "left"), - getfacetset(grid, "right"), - getfacetset(grid, "top"), - getfacetset(grid, "bottom"), -); - -# Now we are set up to define our constraint. We specify which field -# the condition is for, and our combined face 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 -# this case do not depend on time we define our function as $f(\textbf{x}) = 0$, i.e. -# no matter what $\textbf{x}$ we return $0$. When we have -# specified our constraint we `add!` it to `ch`. -dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) -add!(ch, dbc); - -# Finally we also need to `close!` our constraint handler. When we call `close!` -# the dofs corresponding to our constraints are calculated and stored -# in our `ch` object. -close!(ch) - -# Note that if one or more of the constraints are time dependent we would use -# [`update!`](@ref) to recompute prescribed values in each new timestep. - -# ### Assembling the linear system -# -# 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 all the elements in order to -# compute the element contributions ``K_e`` and ``f_e``, which are then assembled to the -# appropriate place in the global ``K`` and ``f``. -# -# #### Element assembly -# We define the function `assemble_element!` (see below) which computes the contribution for -# an element. The function takes pre-allocated `ke` and `fe` (they are allocated once and -# then reused for all elements) so we first need to make sure that they are all zeroes at -# the start of the function by using `fill!`. Then we loop over all the quadrature points, -# and for each quadrature point we loop over all the (local) shape functions. We need the -# value and gradient of the test function, `δu` and also the gradient of the trial function -# `u`. We get all of these from `cellvalues`. -# -# !!! note "Notation" -# Comparing with the brief finite element introduction in [Introduction to FEM](@ref), -# the variables `δu`, `∇δu` and `∇u` are actually $\phi_i(\textbf{x}_q)$, $\nabla -# \phi_i(\textbf{x}_q)$ and $\nabla \phi_j(\textbf{x}_q)$, i.e. the evaluation of the -# trial and test functions in the quadrature point ``\textbf{x}_q``. However, to -# underline the strong parallel between the weak form and the implementation, this -# example uses the symbols appearing in the weak form. - -function assemble_element!(Ke::Matrix, fe::Vector, cv::NamedTuple, dr::NamedTuple) - cvu = cv[:u] - cvq = cv[:q] - dru = dr[:u] - drq = dr[:q] - ## Loop over quadrature points - for q_point in 1:getnquadpoints(cvu) - ## Get the quadrature weight - dΩ = getdetJdV(cvu, q_point) - ## Loop over test shape functions - for (iu, Iu) in pairs(dru) - δu = shape_value(cvu, q_point, iu) - ∇δu = shape_gradient(cvu, q_point, iu) - ## Add contribution to fe - fe[Iu] -= δu * dΩ - ## Loop over trial shape functions - for (jq, Jq) in pairs(drq) - q = shape_value(cvq, q_point, jq) - ## Add contribution to Ke - Ke[Iu, Jq] += (∇δu ⋅ q) * dΩ - end - end - for (iq, Iq) in pairs(drq) - δq = shape_value(cvq, q_point, iq) - for (ju, Ju) in pairs(dru) - ∇u = shape_gradient(cvu, q_point, ju) - Ke[Iq, Ju] += (δq ⋅ ∇u) * dΩ - end - for (jq, Jq) in pairs(drq) - q = shape_value(cvq, q_point, jq) - Ke[Iq, Jq] += (δq ⋅ q) * dΩ - end - end - end - return Ke, fe -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 our `cellvalues`, the sparse matrix `K`, and our DofHandler -# as input arguments and returns the assembled global stiffness matrix, and the assembled -# global force vector. We start by allocating `Ke`, `fe`, and the global force vector `f`. -# We also create an assembler by using `start_assemble`. The assembler lets us assemble into -# `K` and `f` efficiently. We then start the loop over all the elements. In each loop -# iteration we reinitialize `cellvalues` (to update derivatives of shape functions etc.), -# compute the element contribution with `assemble_element!`, and then assemble into the -# global `K` and `f` with `assemble!`. -# -# !!! note "Notation" -# Comparing again with [Introduction to FEM](@ref), `f` and `u` correspond to -# $\underline{\hat{f}}$ and $\underline{\hat{u}}$, since they represent the discretized -# versions. However, through the code we use `f` and `u` instead to reflect the strong -# connection between the weak form and the Ferrite implementation. - -function assemble_global(cellvalues, K::SparseMatrixCSC, dh::DofHandler) - grid = dh.grid - ## Allocate the element stiffness matrix and element force vector - dofranges = (u = dof_range(dh, :u), q = dof_range(dh, :q)) - ncelldofs = ndofs_per_cell(dh) - Ke = zeros(ncelldofs, ncelldofs) - fe = zeros(ncelldofs) - ## Allocate global force vector f - f = zeros(ndofs(dh)) - ## Create an assembler - assembler = start_assemble(K, f) - x = copy(getcoordinates(grid, 1)) - dofs = copy(celldofs(dh, 1)) - ## Loop over all cels - for cellnr in 1:getncells(grid) - ## Reinitialize cellvalues for this cell - cell = getcells(grid, cellnr) - getcoordinates!(x, grid, cell) - celldofs!(dofs, dh, cellnr) - reinit!(cellvalues[:u], cell, x) - reinit!(cellvalues[:q], cell, x) - ## Reset to 0 - fill!(Ke, 0) - fill!(fe, 0) - ## Compute element contribution - assemble_element!(Ke, fe, cellvalues, dofranges) - ## Assemble Ke and fe into K and f - assemble!(assembler, dofs, Ke, fe) - end - return K, f -end -#md nothing # hide - -# ### Solution of the system -# The last step is to solve the system. First we call `assemble_global` -# to obtain the global stiffness matrix `K` and force vector `f`. -K, f = assemble_global(cellvalues, K, dh); - -# To account for the boundary conditions we use the `apply!` function. -# This modifies elements in `K` and `f` respectively, such that -# we can get the correct solution vector `u` by using `\`. -apply!(K, f, ch) -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/). -u_nodes = evaluate_at_grid_nodes(dh, u, :u) -∂Ω_cells = zeros(Int, getncells(grid)) -for (cellnr, _) in ∂Ω - ∂Ω_cells[cellnr] = 1 -end -VTKGridFile("heat_equation_rt", dh) do vtk - write_node_data(vtk, u_nodes, "u") - write_cell_data(vtk, ∂Ω_cells, "dO") -end - -@show norm(u_nodes)/length(u_nodes) - -# ## Postprocess the total flux -function calculate_flux(dh, dΩ, ip, a) - grid = dh.grid - qr = FacetQuadratureRule{RefTriangle}(4) - ip_geo = geometric_interpolation(getcelltype(grid)) - fv = FacetValues(qr, ip, ip_geo) - - dofrange = dof_range(dh, :q) - flux = 0.0 - dofs = celldofs(dh, 1) - ae = zeros(length(dofs)) - x = getcoordinates(grid, 1) - for (cellnr, facenr) in dΩ - getcoordinates!(x, grid, cellnr) - cell = getcells(grid, cellnr) - celldofs!(dofs, dh, cellnr) - map!(i->a[i], ae, dofs) - reinit!(fv, cell, x, facenr) - for q_point in 1:getnquadpoints(fv) - dΓ = getdetJdV(fv, q_point) - n = getnormal(fv, q_point) - q = function_value(fv, q_point, ae, dofrange) - flux += (q ⋅ n)*dΓ - end - end - return flux -end - -function calculate_flux_lag(dh, dΩ, ip, a) - grid = dh.grid - qr = FacetQuadratureRule{RefTriangle}(4) - ip_geo = geometric_interpolation(getcelltype(grid)) - fv = FacetValues(qr, ip, ip_geo) - dofrange = dof_range(dh, :u) - flux = 0.0 - dofs = celldofs(dh, 1) - ae = zeros(length(dofs)) - x = getcoordinates(grid, 1) - for (cellnr, facenr) in dΩ - getcoordinates!(x, grid, cellnr) - cell = getcells(grid, cellnr) - celldofs!(dofs, dh, cellnr) - map!(i->a[i], ae, dofs) - reinit!(fv, cell, x, facenr) - for q_point in 1:getnquadpoints(fv) - dΓ = getdetJdV(fv, q_point) - n = getnormal(fv, q_point) - q = function_gradient(fv, q_point, ae, dofrange) - flux -= (q ⋅ n)*dΓ - end - end - return flux -end - -flux = calculate_flux(dh, ∂Ω, ipq, u) -flux_lag = calculate_flux_lag(dh, ∂Ω, ipu, u) -@show flux, flux_lag - - -function get_Ke(dh, cellvalues; cellnr=1) - dofranges = (u = dof_range(dh, :u), q = dof_range(dh, :q)) - ncelldofs = ndofs_per_cell(dh) - Ke = zeros(ncelldofs, ncelldofs) - fe = zeros(ncelldofs) - x = getcoordinates(grid, cellnr) - cell = getcells(grid, cellnr) - reinit!(cellvalues[:u], cell, x) - reinit!(cellvalues[:q], cell, x) - - ## Reset to 0 - fill!(Ke, 0) - fill!(fe, 0) - ## Compute element contribution - assemble_element!(Ke, fe, cellvalues, dofranges) - return Ke -end -#md # ## [Plain program](@id heat_equation-plain-program) -#md # -#md # Here follows a version of the program without any comments. -#md # The file is also available here: [`heat_equation.jl`](heat_equation.jl). -#md # -#md # ```julia -#md # @__CODE__ -#md # ``` diff --git a/docs/src/literate-tutorials/heat_equation_triangle.jl b/docs/src/literate-tutorials/heat_equation_triangle.jl deleted file mode 100644 index c5505ef026..0000000000 --- a/docs/src/literate-tutorials/heat_equation_triangle.jl +++ /dev/null @@ -1,252 +0,0 @@ -# # [Heat equation (Triangle)](@id tutorial-heat-equation-triangle) -# -# ![](heat_square.png) -# -# *Figure 1*: Temperature field on the unit square with an internal uniform heat source -# solved with homogeneous Dirichlet boundary conditions on the boundary. -# -# ## Introduction -# -# The heat equation is the "Hello, world!" equation of finite elements. -# Here we solve the equation on a unit square, with a uniform internal source. -# The strong form of the (linear) heat equation is given by -# -# ```math -# -\nabla \cdot (k \nabla u) = f \quad \textbf{x} \in \Omega, -# ``` -# -# where $u$ is the unknown temperature field, $k$ the heat conductivity, -# $f$ the heat source and $\Omega$ the domain. For simplicity we set $f = 1$ -# and $k = 1$. We will consider homogeneous Dirichlet boundary conditions such that -# ```math -# u(\textbf{x}) = 0 \quad \textbf{x} \in \partial \Omega, -# ``` -# where $\partial \Omega$ denotes the boundary of $\Omega$. -# The resulting weak form is given given as follows: Find ``u \in \mathbb{U}`` such that -# ```math -# \int_{\Omega} \nabla \delta u \cdot \nabla u \ d\Omega = \int_{\Omega} \delta u \ d\Omega \quad \forall \delta u \in \mathbb{T}, -# ``` -# where $\delta u$ is a test function, and where $\mathbb{U}$ and $\mathbb{T}$ are suitable -# trial and test function sets, respectively. -#- -# ## Commented Program -# -# Now we solve the problem in Ferrite. What follows is a program spliced with comments. -# -# First we load Ferrite, and some other packages we need -using Ferrite, SparseArrays -# We start by generating a simple grid with 20x20 quadrilateral elements -# using `generate_grid`. The generator defaults to the unit square, -# so we don't need to specify the corners of the domain. -grid = generate_grid(Triangle, (20, 20)); - -# ### Trial and test functions -# A `CellValues` facilitates the process of evaluating values and gradients of -# test and trial functions (among other things). To define -# this we need to specify an interpolation space for the shape functions. -# We use Lagrange functions -# based on the two-dimensional reference quadrilateral. We also define a quadrature rule based on -# the same reference element. We combine the interpolation and the quadrature rule -# to a `CellValues` object. -ip = Lagrange{RefTriangle, 1}() -qr = QuadratureRule{RefTriangle}(2) -cellvalues = CellValues(qr, ip); - -# ### Degrees of freedom -# Next we need to define a `DofHandler`, which will take care of numbering -# and distribution of degrees of freedom for our approximated fields. -# We create the `DofHandler` and then add a single scalar field called `:u` based on -# our interpolation `ip` defined above. -# Lastly we `close!` the `DofHandler`, it is now that the dofs are distributed -# for all the elements. -dh = DofHandler(grid) -add!(dh, :u, ip) -close!(dh); - -# Now that we have distributed all our dofs we can create our tangent matrix, -# using `create_sparsity_pattern`. This function returns a sparse matrix -# with the correct entries stored. -K = allocate_matrix(dh) - -# ### Boundary conditions -# In Ferrite constraints like Dirichlet boundary conditions -# are handled by a `ConstraintHandler`. -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. -∂Ω = union( - getfacetset(grid, "left"), - getfacetset(grid, "right"), - getfacetset(grid, "top"), - getfacetset(grid, "bottom"), -); - -# Now we are set up to define our constraint. We specify which field -# the condition is for, and our combined face 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 -# this case do not depend on time we define our function as $f(\textbf{x}) = 0$, i.e. -# no matter what $\textbf{x}$ we return $0$. When we have -# specified our constraint we `add!` it to `ch`. -dbc = Dirichlet(:u, ∂Ω, (x, t) -> 0) -add!(ch, dbc); - -# Finally we also need to `close!` our constraint handler. When we call `close!` -# the dofs corresponding to our constraints are calculated and stored -# in our `ch` object. -close!(ch) - -# Note that if one or more of the constraints are time dependent we would use -# [`update!`](@ref) to recompute prescribed values in each new timestep. - -# ### Assembling the linear system -# -# 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 all the elements in order to -# compute the element contributions ``K_e`` and ``f_e``, which are then assembled to the -# appropriate place in the global ``K`` and ``f``. -# -# #### Element assembly -# We define the function `assemble_element!` (see below) which computes the contribution for -# an element. The function takes pre-allocated `ke` and `fe` (they are allocated once and -# then reused for all elements) so we first need to make sure that they are all zeroes at -# the start of the function by using `fill!`. Then we loop over all the quadrature points, -# and for each quadrature point we loop over all the (local) shape functions. We need the -# value and gradient of the test function, `δu` and also the gradient of the trial function -# `u`. We get all of these from `cellvalues`. -# -# !!! note "Notation" -# Comparing with the brief finite element introduction in [Introduction to FEM](@ref), -# the variables `δu`, `∇δu` and `∇u` are actually $\phi_i(\textbf{x}_q)$, $\nabla -# \phi_i(\textbf{x}_q)$ and $\nabla \phi_j(\textbf{x}_q)$, i.e. the evaluation of the -# trial and test functions in the quadrature point ``\textbf{x}_q``. However, to -# underline the strong parallel between the weak form and the implementation, this -# example uses the symbols appearing in the weak form. - -function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues) - n_basefuncs = getnbasefunctions(cellvalues) - ## Reset to 0 - fill!(Ke, 0) - fill!(fe, 0) - ## Loop over quadrature points - for q_point in 1:getnquadpoints(cellvalues) - ## Get the quadrature weight - dΩ = getdetJdV(cellvalues, q_point) - ## Loop over test shape functions - for i in 1:n_basefuncs - δu = shape_value(cellvalues, q_point, i) - ∇δu = shape_gradient(cellvalues, q_point, i) - ## Add contribution to fe - fe[i] += δu * dΩ - ## Loop over trial shape functions - for j in 1:n_basefuncs - ∇u = shape_gradient(cellvalues, q_point, j) - ## Add contribution to Ke - Ke[i, j] += (∇δu ⋅ ∇u) * dΩ - end - end - end - return Ke, fe -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 our `cellvalues`, the sparse matrix `K`, and our DofHandler -# as input arguments and returns the assembled global stiffness matrix, and the assembled -# global force vector. We start by allocating `Ke`, `fe`, and the global force vector `f`. -# We also create an assembler by using `start_assemble`. The assembler lets us assemble into -# `K` and `f` efficiently. We then start the loop over all the elements. In each loop -# iteration we reinitialize `cellvalues` (to update derivatives of shape functions etc.), -# compute the element contribution with `assemble_element!`, and then assemble into the -# global `K` and `f` with `assemble!`. -# -# !!! note "Notation" -# Comparing again with [Introduction to FEM](@ref), `f` and `u` correspond to -# $\underline{\hat{f}}$ and $\underline{\hat{u}}$, since they represent the discretized -# versions. However, through the code we use `f` and `u` instead to reflect the strong -# connection between the weak form and the Ferrite implementation. - -function assemble_global(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHandler) - ## Allocate the element stiffness matrix and element force vector - n_basefuncs = getnbasefunctions(cellvalues) - Ke = zeros(n_basefuncs, n_basefuncs) - fe = zeros(n_basefuncs) - ## Allocate global force vector f - f = zeros(ndofs(dh)) - ## Create an assembler - assembler = start_assemble(K, f) - ## Loop over all cels - for cell in CellIterator(dh) - ## Reinitialize cellvalues for this cell - reinit!(cellvalues, cell) - ## Compute element contribution - assemble_element!(Ke, fe, cellvalues) - ## Assemble Ke and fe into K and f - assemble!(assembler, celldofs(cell), Ke, fe) - end - return K, f -end -#md nothing # hide - -# ### Solution of the system -# The last step is to solve the system. First we call `assemble_global` -# to obtain the global stiffness matrix `K` and force vector `f`. -K, f = assemble_global(cellvalues, K, dh); - -# To account for the boundary conditions we use the `apply!` function. -# This modifies elements in `K` and `f` respectively, such that -# we can get the correct solution vector `u` by using `\`. -apply!(K, f, ch) -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/). -VTKGridFile("heat_equation", dh) do vtk - write_solution(vtk, dh, u) -end - -@show norm(u)/length(u) - -# ### Postprocessing the total flux - -function calculate_flux_lag(dh, dΩ, ip, a) - qr = FacetQuadratureRule{RefTriangle}(2) - fv = FacetValues(qr, ip, Lagrange{RefTriangle,1}()) - grid = dh.grid - dofrange = dof_range(dh, :u) - flux = 0.0 - dofs = celldofs(dh, 1) - ae = zeros(length(dofs)) - x = getcoordinates(grid, 1) - for (cellnr, facenr) in dΩ - getcoordinates!(x, grid, cellnr) - cell = getcells(grid, cellnr) - celldofs!(dofs, dh, cellnr) - map!(i->a[i], ae, dofs) - reinit!(fv, cell, x, facenr) - for q_point in 1:getnquadpoints(fv) - dΓ = getdetJdV(fv, q_point) - n = getnormal(fv, q_point) - q = function_gradient(fv, q_point, ae, dofrange) - flux -= (q ⋅ n)*dΓ - end - end - return flux -end - -flux = calculate_flux_lag(dh, ∂Ω, ip, u) -@show flux - -#md # ## [Plain program](@id heat_equation-plain-program) -#md # -#md # Here follows a version of the program without any comments. -#md # The file is also available here: [`heat_equation.jl`](heat_equation.jl). -#md # -#md # ```julia -#md # @__CODE__ -#md # ``` diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 17b52e55ff..4405a09b76 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -266,7 +266,7 @@ function line_integral(qr::QuadratureRule{RefLine}, ip, shape_nr, x0, Δx, L, v, end # Required properties of shape value Nⱼ of an edge-elements (Hcurl) on an edge with direction v, length L, and dofs ∈ 𝔇 -# 1) Unit property: ∑_{j∈𝔇} ∫(Nⱼ ⋅ v f(s) dS) = 1 +# 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1 ∀ ∈ 𝔇 # Where f(s) = 1 for linear interpolation and f(s)=1-s and f(s)=s for 2nd order interpolation (first and second shape function) # And s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 @@ -303,7 +303,7 @@ end end # Required properties of shape value Nⱼ of an edge-elements (Hdiv) on an edge with normal n, length L, and dofs ∈ 𝔇 -# 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 +# 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 ∀ ∈ 𝔇 # Where f(s) = 1 for single shape function on edge, and f(s)=1-s and f(s)=s for two shape functions on edge # s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 From c60e53042375c1d9e55a5d118a3ee88e59c3682f Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 12 Nov 2024 16:02:02 +0100 Subject: [PATCH 137/172] Clean and run runic --- .../literate-tutorials/heat_equation_hdiv.jl | 17 +- src/FEValues/FunctionValues.jl | 16 +- src/interpolations.jl | 128 +- test/InterpolationTestUtils.jl | 202 +-- test/test_interpolations.jl | 326 ++-- .../2d_vector_interpolation_checkplots.jl | 59 - visualization/Manifest.toml | 1578 ----------------- visualization/Project.toml | 4 - visualization/reference_cell_ip.jl | 85 - 9 files changed, 355 insertions(+), 2060 deletions(-) delete mode 100644 visualization/2d_vector_interpolation_checkplots.jl delete mode 100644 visualization/Manifest.toml delete mode 100644 visualization/Project.toml delete mode 100644 visualization/reference_cell_ip.jl diff --git a/docs/src/literate-tutorials/heat_equation_hdiv.jl b/docs/src/literate-tutorials/heat_equation_hdiv.jl index 155e42bd66..e3f2bee293 100644 --- a/docs/src/literate-tutorials/heat_equation_hdiv.jl +++ b/docs/src/literate-tutorials/heat_equation_hdiv.jl @@ -63,8 +63,8 @@ function create_grid(ny::Int) center_width = 5.0 center_length = 20.0 upper_right = Vec((length / 2, width / 2)) - grid = generate_grid(Triangle, (round(Int, ny * length / width), ny), -upper_right, upper_right); - addcellset!(grid, "center", x -> abs(x[1]) < center_length/2 && abs(x[2]) < center_width / 2) + grid = generate_grid(Triangle, (round(Int, ny * length / width), ny), -upper_right, upper_right) + addcellset!(grid, "center", x -> abs(x[1]) < center_length / 2 && abs(x[2]) < center_width / 2) addcellset!(grid, "around", setdiff(1:getncells(grid), getcellset(grid, "center"))) return grid end @@ -77,7 +77,7 @@ ip_geo = geometric_interpolation(getcelltype(grid)) ipu = DiscontinuousLagrange{RefTriangle, 0}() ipq = Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}() qr = QuadratureRule{RefTriangle}(2) -cellvalues = (u=CellValues(qr, ipu, ip_geo), q=CellValues(qr, ipq, ip_geo)) +cellvalues = (u = CellValues(qr, ipu, ip_geo), q = CellValues(qr, ipq, ip_geo)) # Distribute the degrees of freedom dh = DofHandler(grid) @@ -103,7 +103,7 @@ function assemble_element!(Ke::Matrix, fe::Vector, cv::NamedTuple, dr::NamedTupl dΩ = getdetJdV(cvu, q_point) ## Loop over test shape functions for (iu, Iu) in pairs(dru) - δNu = shape_value(cvu, q_point, iu) + δNu = shape_value(cvu, q_point, iu) ## Add contribution to fe fe[Iu] += δNu * h * dΩ ## Loop over trial shape functions @@ -148,8 +148,9 @@ function assemble_global(cellvalues, dh::DofHandler) dofs = copy(celldofs(dh, 1)) ## Loop over all cells for (cells, k) in ( - (getcellset(grid, "center"), 0.1), - (getcellset(grid, "around"), 1.00)) + (getcellset(grid, "center"), 0.1), + (getcellset(grid, "around"), 1.0), + ) for cellnr in cells ## Reinitialize cellvalues for this cell cell = getcells(grid, cellnr) @@ -205,13 +206,13 @@ function calculate_flux(dh, boundary_facets, ip, a) getcoordinates!(x, grid, cellnr) cell = getcells(grid, cellnr) celldofs!(dofs, dh, cellnr) - map!(i->a[i], ae, dofs) + map!(i -> a[i], ae, dofs) reinit!(fv, cell, x, facetnr) for q_point in 1:getnquadpoints(fv) dΓ = getdetJdV(fv, q_point) n = getnormal(fv, q_point) q = function_value(fv, q_point, ae, dofrange) - flux += (q ⋅ n)*dΓ + flux += (q ⋅ n) * dΓ end end return flux diff --git a/src/FEValues/FunctionValues.jl b/src/FEValues/FunctionValues.jl index d0bf3d5183..a4726801ed 100644 --- a/src/FEValues/FunctionValues.jl +++ b/src/FEValues/FunctionValues.jl @@ -226,7 +226,7 @@ end @inbounds for j in 1:getnbasefunctions(funvals) d = get_direction(funvals.ip, j, cell) Nξ = funvals.Nξ[j, q_point] - funvals.Nx[j, q_point] = d*(Nξ ⋅ Jinv) + funvals.Nx[j, q_point] = d * (Nξ ⋅ Jinv) end return nothing end @@ -238,8 +238,8 @@ end d = get_direction(funvals.ip, j, cell) dNdξ = funvals.dNdξ[j, q_point] Nξ = funvals.Nξ[j, q_point] - funvals.Nx[j, q_point] = d*(Nξ ⋅ Jinv) - funvals.dNdx[j, q_point] = d*(Jinv' ⋅ dNdξ ⋅ Jinv - Jinv' ⋅ (Nξ ⋅ Jinv ⋅ H ⋅ Jinv)) + funvals.Nx[j, q_point] = d * (Nξ ⋅ Jinv) + funvals.dNdx[j, q_point] = d * (Jinv' ⋅ dNdξ ⋅ Jinv - Jinv' ⋅ (Nξ ⋅ Jinv ⋅ H ⋅ Jinv)) end return nothing end @@ -251,7 +251,7 @@ end @inbounds for j in 1:getnbasefunctions(funvals) d = get_direction(funvals.ip, j, cell) Nξ = funvals.Nξ[j, q_point] - funvals.Nx[j, q_point] = d*(J ⋅ Nξ)/detJ + funvals.Nx[j, q_point] = d * (J ⋅ Nξ) / detJ end return nothing end @@ -262,15 +262,15 @@ end Jinv = inv(J) detJ = det(J) I2 = one(J) - H_Jinv = H⋅Jinv - A1 = (H_Jinv ⊡ (otimesl(I2,I2))) / detJ + H_Jinv = H ⋅ Jinv + A1 = (H_Jinv ⊡ (otimesl(I2, I2))) / detJ A2 = (Jinv' ⊡ H_Jinv) / detJ @inbounds for j in 1:getnbasefunctions(funvals) d = get_direction(funvals.ip, j, cell) dNdξ = funvals.dNdξ[j, q_point] Nξ = funvals.Nξ[j, q_point] - funvals.Nx[j, q_point] = d*(J ⋅ Nξ)/detJ - funvals.dNdx[j, q_point] = d*(J ⋅ dNdξ ⋅ Jinv/detJ + A1 ⋅ Nξ - (J ⋅ Nξ) ⊗ A2) + funvals.Nx[j, q_point] = d * (J ⋅ Nξ) / detJ + funvals.dNdx[j, q_point] = d * (J ⋅ dNdξ ⋅ Jinv / detJ + A1 ⋅ Nξ - (J ⋅ Nξ) ⊗ A2) end return nothing end diff --git a/src/interpolations.jl b/src/interpolations.jl index d4e65174f9..1dc0876c1e 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1800,7 +1800,7 @@ n_dbc_components(::RaviartThomas) = 1 function reference_shape_value(ip::RaviartThomas{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) x, y = ξ i == 1 && return ξ # Flip sign - i == 2 && return Vec(x-1, y) # Keep sign + i == 2 && return Vec(x - 1, y) # Keep sign i == 3 && return Vec(x, y - 1) # Flip sign throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1809,7 +1809,7 @@ getnbasefunctions(::RaviartThomas{2, RefTriangle, 1}) = 3 edgedof_interior_indices(::RaviartThomas{2, RefTriangle, 1}) = ((1,), (2,), (3,)) adjust_dofs_during_distribution(::RaviartThomas) = false -function get_direction(::RaviartThomas{2,RefTriangle,1}, j, cell) +function get_direction(::RaviartThomas{2, RefTriangle, 1}, j, cell) edge = edges(cell)[j] return ifelse(edge[2] > edge[1], 1, -1) end @@ -1841,24 +1841,24 @@ RefTriangle function reference_shape_value(ip::RaviartThomas{2, RefTriangle, 2}, ξ::Vec{2}, i::Int) x, y = ξ # Face 1 (keep ordering, flip sign) - i == 1 && return Vec(4x*(2x-1), 2y*(4x-1)) - i == 2 && return Vec(2x*(4y-1), 4y*(2y-1)) + i == 1 && return Vec(4x * (2x - 1), 2y * (4x - 1)) + i == 2 && return Vec(2x * (4y - 1), 4y * (2y - 1)) # Face 2 (flip ordering, keep signs) - i == 3 && return Vec( 8x*y - 2x - 6y + 2, 4y*(2y - 1)) - i == 4 && return Vec(-8x^2 - 8x*y + 12x + 6y - 4, 2y*(-4x - 4y + 3)) + i == 3 && return Vec(8x * y - 2x - 6y + 2, 4y * (2y - 1)) + i == 4 && return Vec(-8x^2 - 8x * y + 12x + 6y - 4, 2y * (-4x - 4y + 3)) # Face 3 (keep ordering, flip sign) - i == 5 && return Vec(2x*(3 - 4x - 4y), -8x*y + 6x - 8y^2 + 12y - 4) - i == 6 && return Vec(4x*(2x-1), 8x*y - 6x - 2y + 2) + i == 5 && return Vec(2x * (3 - 4x - 4y), -8x * y + 6x - 8y^2 + 12y - 4) + i == 6 && return Vec(4x * (2x - 1), 8x * y - 6x - 2y + 2) # Cell - i == 7 && return Vec(8x*(-2x - y + 2), 8y*(-2x - y + 1)) - i == 8 && return Vec(8x*(-2y - x + 1), 8y*(-2y - x + 2)) + i == 7 && return Vec(8x * (-2x - y + 2), 8y * (-2x - y + 1)) + i == 8 && return Vec(8x * (-2y - x + 1), 8y * (-2y - x + 2)) throw(ArgumentError("no shape function $i for interpolation $ip")) end -getnbasefunctions(::RaviartThomas{2,RefTriangle,2}) = 8 -edgedof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = ((1, 2), (3, 4), (5, 6)) -volumedof_interior_indices(::RaviartThomas{2,RefTriangle,2}) = (7,8) -adjust_dofs_during_distribution(::RaviartThomas{2,RefTriangle,2}) = true +getnbasefunctions(::RaviartThomas{2, RefTriangle, 2}) = 8 +edgedof_interior_indices(::RaviartThomas{2, RefTriangle, 2}) = ((1, 2), (3, 4), (5, 6)) +volumedof_interior_indices(::RaviartThomas{2, RefTriangle, 2}) = (7, 8) +adjust_dofs_during_distribution(::RaviartThomas{2, RefTriangle, 2}) = true function get_direction(::RaviartThomas{2, RefTriangle, 2}, j, cell) j > 6 && return 1 @@ -1871,7 +1871,7 @@ end ##################################### struct BrezziDouglasMarini{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end mapping_type(::BrezziDouglasMarini) = ContravariantPiolaMapping() -reference_coordinates(ip::BrezziDouglasMarini{vdim}) where vdim = fill(NaN*zero(Vec{vdim}), getnbasefunctions(ip)) +reference_coordinates(ip::BrezziDouglasMarini{vdim}) where {vdim} = fill(NaN * zero(Vec{vdim}), getnbasefunctions(ip)) #dirichlet_facedof_indices(ip::BrezziDouglasMarini) = facetdof_interior_indices(ip) n_dbc_components(::BrezziDouglasMarini) = 1 #= @@ -1922,16 +1922,16 @@ end ##################################### struct Nedelec{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end mapping_type(::Nedelec) = CovariantPiolaMapping() -reference_coordinates(ip::Nedelec{vdim}) where vdim = fill(NaN*zero(Vec{vdim}), getnbasefunctions(ip)) +reference_coordinates(ip::Nedelec{vdim}) where {vdim} = fill(NaN * zero(Vec{vdim}), getnbasefunctions(ip)) dirichlet_facedof_indices(ip::Nedelec) = facetdof_interior_indices(ip) n_dbc_components(::Nedelec) = 1 # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html -function reference_shape_value(ip::Nedelec{2,RefTriangle,1}, ξ::Vec{2}, i::Int) +function reference_shape_value(ip::Nedelec{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) x, y = ξ - i == 1 && return Vec( - y, x) - i == 2 && return Vec( - y, x - 1) # Changed signed, follow Ferrite's sign convention - i == 3 && return Vec(1 - y, x) + i == 1 && return Vec(- y, x) + i == 2 && return Vec(- y, x - 1) # Changed signed, follow Ferrite's sign convention + i == 3 && return Vec(1 - y, x) throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1946,34 +1946,50 @@ end # RefTriangle, 2nd order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-2.html -function reference_shape_value(ip::Nedelec{2,RefTriangle,2}, ξ::Vec{2}, i::Int) +function reference_shape_value(ip::Nedelec{2, RefTriangle, 2}, ξ::Vec{2}, i::Int) x, y = ξ # Face 1 - i == 1 && return Vec( 2*y*(1 - 4*x), - 4*x*(2*x - 1)) - i == 2 && return Vec( 4*y*(1 - 2*y), - 2*x*(4*y - 1)) + i == 1 && return Vec( + 2 * y * (1 - 4 * x), + 4 * x * (2 * x - 1) + ) + i == 2 && return Vec( + 4 * y * (1 - 2 * y), + 2 * x * (4 * y - 1) + ) # Face 2 (flip order and sign compared to defelement) - i == 3 && return Vec( 4*y*(1 - 2*y), - 8*x*y - 2*x - 6*y + 2) - i == 4 && return Vec( 2*y*(4*x + 4*y - 3), - -8*x^2 - 8*x*y + 12*x + 6*y - 4) + i == 3 && return Vec( + 4 * y * (1 - 2 * y), + 8 * x * y - 2 * x - 6 * y + 2 + ) + i == 4 && return Vec( + 2 * y * (4 * x + 4 * y - 3), + -8 * x^2 - 8 * x * y + 12 * x + 6 * y - 4 + ) # Face 3 - i == 5 && return Vec( 8*x*y - 6*x + 8*y^2 - 12*y + 4, - 2*x*(-4*x - 4*y + 3)) - i == 6 && return Vec(-8*x*y + 6*x + 2*y - 2, - 4*x*(2*x - 1)) + i == 5 && return Vec( + 8 * x * y - 6 * x + 8 * y^2 - 12 * y + 4, + 2 * x * (-4 * x - 4 * y + 3) + ) + i == 6 && return Vec( + -8 * x * y + 6 * x + 2 * y - 2, + 4 * x * (2 * x - 1) + ) # Cell - i == 7 && return Vec( 8*y*(-x - 2*y + 2), - 8*x*( x + 2*y - 1)) - i == 8 && return Vec( 8*y*( 2*x + y - 1), - 8*x*(-2*x - y + 2)) + i == 7 && return Vec( + 8 * y * (-x - 2 * y + 2), + 8 * x * (x + 2 * y - 1) + ) + i == 8 && return Vec( + 8 * y * (2 * x + y - 1), + 8 * x * (-2 * x - y + 2) + ) throw(ArgumentError("no shape function $i for interpolation $ip")) end getnbasefunctions(::Nedelec{2, RefTriangle, 2}) = 8 -edgedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = ((1,2), (3,4), (5,6)) -volumedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = (7,8) +edgedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = ((1, 2), (3, 4), (5, 6)) +volumedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = (7, 8) adjust_dofs_during_distribution(::Nedelec{2, RefTriangle, 2}) = true function get_direction(::Nedelec{2, RefTriangle, 2}, j, cell) @@ -1983,7 +1999,7 @@ function get_direction(::Nedelec{2, RefTriangle, 2}, j, cell) end # https://defelement.com/elements/examples/tetrahedron-nedelec1-lagrange-1.html -function reference_shape_value(ip::Nedelec{3, RefTetrahedron, 1}, ξ::Vec{3, T}, i::Int) where T +function reference_shape_value(ip::Nedelec{3, RefTetrahedron, 1}, ξ::Vec{3, T}, i::Int) where {T} x, y, z = ξ # Edge 1 (defelement 5, positive) i == 1 && return Vec(- y - z + 1, x, x) @@ -2002,7 +2018,7 @@ function reference_shape_value(ip::Nedelec{3, RefTetrahedron, 1}, ξ::Vec{3, T}, end getnbasefunctions(::Nedelec{3, RefTetrahedron, 1}) = 6 -edgedof_interior_indices(::Nedelec{3, RefTetrahedron, 1}) = ntuple(i->(i,), 6) +edgedof_interior_indices(::Nedelec{3, RefTetrahedron, 1}) = ntuple(i -> (i,), 6) adjust_dofs_during_distribution(::Nedelec{3, RefTetrahedron, 1}) = false function get_direction(::Nedelec{3, RefTetrahedron, 1}, j, cell) @@ -2022,38 +2038,38 @@ No point in implementing guesses that cannot be verified... # https://defelement.com/elements/examples/hexahedron-nedelec1-lagrange-1.html # Note: Divide by 2 since J=2I compared to DefElement's reference shape, and mapping is N ⋅ J^-T -function reference_shape_value(ip::Nedelec{3, RefHexahedron, 1}, ξ::Vec{3, T}, i::Int) where T - x, y, z = (ξ + ones(ξ))/2 +function reference_shape_value(ip::Nedelec{3, RefHexahedron, 1}, ξ::Vec{3, T}, i::Int) where {T} + x, y, z = (ξ + ones(ξ)) / 2 # Edge 1 (defelement 0, positive) - i == 1 && return Vec(y*z - y - z + 1, zero(T), zero(T))/2 + i == 1 && return Vec(y * z - y - z + 1, zero(T), zero(T)) / 2 # Edge 2 (defelement 3, positive) - i == 2 && return Vec(zero(T), x * (1 - z), zero(T))/2 + i == 2 && return Vec(zero(T), x * (1 - z), zero(T)) / 2 # Edge 3 (defelement 5, negative) - i == 3 && return Vec(-y*(1-z), zero(T), zero(T))/2 + i == 3 && return Vec(-y * (1 - z), zero(T), zero(T)) / 2 # Edge 4 (defelement 1, negative) - i == 4 && return Vec(zero(T), - x*z + x + z - 1, zero(T))/2 + i == 4 && return Vec(zero(T), - x * z + x + z - 1, zero(T)) / 2 # Edge 5 (defelement 8, positive) - i == 5 && return Vec(z*(1-y), zero(T), zero(T))/2 + i == 5 && return Vec(z * (1 - y), zero(T), zero(T)) / 2 # Edge 6 (defelement 10, positive) - i == 6 && return Vec(zero(T), x * z, zero(T))/2 + i == 6 && return Vec(zero(T), x * z, zero(T)) / 2 # Edge 7 (defelement 11, negative) - i == 7 && return Vec(- y*z, zero(T), zero(T))/2 + i == 7 && return Vec(- y * z, zero(T), zero(T)) / 2 # Edge 8 (defelement 9, negative) - i == 8 && return Vec(zero(T), - z * (1 - x), zero(T))/2 + i == 8 && return Vec(zero(T), - z * (1 - x), zero(T)) / 2 # Edge 9 (defelement 2, positive) - i == 9 && return Vec(zero(T), zero(T), x*y - x - y + 1)/2 + i == 9 && return Vec(zero(T), zero(T), x * y - x - y + 1) / 2 # Edge 10 (defelement 4, positive) - i == 10 && return Vec(zero(T), zero(T), x * (1 - y))/2 + i == 10 && return Vec(zero(T), zero(T), x * (1 - y)) / 2 # Edge 11 (defelement 7, positive) - i == 11 && return Vec(zero(T), zero(T), x * y)/2 + i == 11 && return Vec(zero(T), zero(T), x * y) / 2 # Edge 12 (defelement 6, positive) - i == 12 && return Vec(zero(T), zero(T), y * (1 - x))/2 + i == 12 && return Vec(zero(T), zero(T), y * (1 - x)) / 2 throw(ArgumentError("no shape function $i for interpolation $ip")) end getnbasefunctions(::Nedelec{3, RefHexahedron, 1}) = 12 -edgedof_interior_indices(::Nedelec{3, RefHexahedron, 1}) = ntuple(i->(i,), 12) +edgedof_interior_indices(::Nedelec{3, RefHexahedron, 1}) = ntuple(i -> (i,), 12) adjust_dofs_during_distribution(::Nedelec{3, RefHexahedron, 1}) = false function get_direction(::Nedelec{3, RefHexahedron, 1}, j, cell) diff --git a/test/InterpolationTestUtils.jl b/test/InterpolationTestUtils.jl index a983edacc9..4acf153026 100644 --- a/test/InterpolationTestUtils.jl +++ b/test/InterpolationTestUtils.jl @@ -1,116 +1,118 @@ module InterpolationTestUtils - using Ferrite - using Test - import LinearAlgebra: normalize - import Random: randperm +using Ferrite +using Test +import LinearAlgebra: normalize +import Random: randperm - function find_matching_facet(grid, facet::FacetIndex) - cell, facetnr = facet - facet_vertices = Set(Ferrite.facets(getcells(grid, cell))[facetnr]) - for cnr in 1:getncells(grid) - cnr == cell && continue - for (i, f_vert) in enumerate(Ferrite.facets(getcells(grid, cnr))) - facet_vertices == Set(f_vert) && return FacetIndex(cnr, i) - end +function find_matching_facet(grid, facet::FacetIndex) + cell, facetnr = facet + facet_vertices = Set(Ferrite.facets(getcells(grid, cell))[facetnr]) + for cnr in 1:getncells(grid) + cnr == cell && continue + for (i, f_vert) in enumerate(Ferrite.facets(getcells(grid, cnr))) + facet_vertices == Set(f_vert) && return FacetIndex(cnr, i) end - return nothing end + return nothing +end - function test_continuity(dh::DofHandler, facet::FacetIndex; - transformation_function::Function=identity, - value_function::Function=function_value) - # transformation_function: (v,n) -> z - # Examples - # * Tangential continuity: fun(v, n) = v - (v ⋅ n)*n - # * Normal continuity: fun(v, n) = v ⋅ n - # value_function: (fe_v, q_point, ue) -> z - - # Check validity of input - @assert length(dh.subdofhandlers) == 1 - @assert length(Ferrite.getfieldnames(dh)) == 1 - - # Find the matching FaceIndex - cellnr, facetnr = facet - facet2 = find_matching_facet(dh.grid, facet) - facet2 === nothing && return false +function test_continuity( + dh::DofHandler, facet::FacetIndex; + transformation_function::Function = identity, + value_function::Function = function_value + ) + # transformation_function: (v,n) -> z + # Examples + # * Tangential continuity: fun(v, n) = v - (v ⋅ n)*n + # * Normal continuity: fun(v, n) = v ⋅ n + # value_function: (fe_v, q_point, ue) -> z - # Pick "random" points on the facet - cell = getcells(dh.grid, cellnr) - RefShape = Ferrite.getrefshape(getcells(dh.grid, cellnr)) - ip_geo = geometric_interpolation(typeof(cell)) - ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) - fqr = FacetQuadratureRule{RefShape}(8) - fv = FacetValues(fqr, ip_fun, ip_geo) - cell_coords = getcoordinates(dh.grid, cellnr) - inds = randperm(getnquadpoints(fv))[1:min(4, getnquadpoints(fv))] + # Check validity of input + @assert length(dh.subdofhandlers) == 1 + @assert length(Ferrite.getfieldnames(dh)) == 1 - # Random dof vector to test continuity - u = rand(ndofs(dh)) + # Find the matching FaceIndex + cellnr, facetnr = facet + facet2 = find_matching_facet(dh.grid, facet) + facet2 === nothing && return false - # Calculate coordinates and function values for these - point_coords = zeros(eltype(cell_coords), length(inds)) - point_normal = similar(point_coords) - fun_vals = zeros(typeof(shape_value(fv, 1, 1)), length(inds)) - reinit!(fv, cell, cell_coords, facetnr) - ue = u[celldofs(dh, cellnr)] - for (i, q_point) in enumerate(inds) - point_coords[i] = spatial_coordinate(fv, q_point, cell_coords) - point_normal[i] = getnormal(fv, q_point) - fun_vals[i] = value_function(fv, q_point, ue) - end + # Pick "random" points on the facet + cell = getcells(dh.grid, cellnr) + RefShape = Ferrite.getrefshape(getcells(dh.grid, cellnr)) + ip_geo = geometric_interpolation(typeof(cell)) + ip_fun = Ferrite.getfieldinterpolation(dh, (1, 1)) + fqr = FacetQuadratureRule{RefShape}(8) + fv = FacetValues(fqr, ip_fun, ip_geo) + cell_coords = getcoordinates(dh.grid, cellnr) + inds = randperm(getnquadpoints(fv))[1:min(4, getnquadpoints(fv))] - # Calculate function values on the other cell - cell2 = getcells(dh.grid, facet2[1]) - cell_coords2 = getcoordinates(dh.grid, facet2[1]) - local_coords = map(x->Ferrite.find_local_coordinate(ip_geo, cell_coords2, x, Ferrite.NewtonLineSearchPointFinder()), point_coords) - @assert all(first.(local_coords)) # check that find_local_coordinate converged - ξs = collect(last.(local_coords)) # Extract the local coordinates - qr = QuadratureRule{RefShape}(zeros(length(ξs)), ξs) - cv = CellValues(qr, ip_fun, ip_geo) - reinit!(cv, cell2, cell_coords2) - fun_vals2 = similar(fun_vals) - ue2 = u[celldofs(dh, facet2[1])] - for q_point in 1:getnquadpoints(cv) - @assert spatial_coordinate(cv, q_point, cell_coords2) ≈ point_coords[q_point] - fun_vals2[q_point] = value_function(cv, q_point, ue2) - end + # Random dof vector to test continuity + u = rand(ndofs(dh)) - d1 = map((v,n)->transformation_function(v,n), fun_vals, point_normal) - d2 = map((v,n)->transformation_function(v,n), fun_vals2, point_normal) - @test isapprox(d1, d2; rtol=1e-6) # Approximate points can contribute to the inexactness - return true + # Calculate coordinates and function values for these + point_coords = zeros(eltype(cell_coords), length(inds)) + point_normal = similar(point_coords) + fun_vals = zeros(typeof(shape_value(fv, 1, 1)), length(inds)) + reinit!(fv, cell, cell_coords, facetnr) + ue = u[celldofs(dh, cellnr)] + for (i, q_point) in enumerate(inds) + point_coords[i] = spatial_coordinate(fv, q_point, cell_coords) + point_normal[i] = getnormal(fv, q_point) + fun_vals[i] = value_function(fv, q_point, ue) end - function create_gradcheck_qr(ip_geo::Interpolation{RefShape}, ΔL) where RefShape - dim = Ferrite.getrefdim(ip_geo) - xref = Ferrite.reference_coordinates(ip_geo) - xc = sum(xref)/length(xref) - ws = rand(length(xref))*((1-ΔL)/length(xref)) - xp = xc + sum(map((x,w) -> w*(x - xc), xref, ws)) - v = normalize(rand(Vec{dim}) - ones(Vec{dim})/2) - x1 = xp + ΔL*v - qr_w = [NaN, NaN] - qr_x = [xp, x1] - return QuadratureRule{RefShape}(qr_w, qr_x) + # Calculate function values on the other cell + cell2 = getcells(dh.grid, facet2[1]) + cell_coords2 = getcoordinates(dh.grid, facet2[1]) + local_coords = map(x -> Ferrite.find_local_coordinate(ip_geo, cell_coords2, x, Ferrite.NewtonLineSearchPointFinder()), point_coords) + @assert all(first.(local_coords)) # check that find_local_coordinate converged + ξs = collect(last.(local_coords)) # Extract the local coordinates + qr = QuadratureRule{RefShape}(zeros(length(ξs)), ξs) + cv = CellValues(qr, ip_fun, ip_geo) + reinit!(cv, cell2, cell_coords2) + fun_vals2 = similar(fun_vals) + ue2 = u[celldofs(dh, facet2[1])] + for q_point in 1:getnquadpoints(cv) + @assert spatial_coordinate(cv, q_point, cell_coords2) ≈ point_coords[q_point] + fun_vals2[q_point] = value_function(cv, q_point, ue2) end - function test_gradient(dh, cellnr; ΔL=1e-6) - ue = rand(ndofs_per_cell(dh, cellnr)) - x = getcoordinates(dh.grid, cellnr) - cell = getcells(dh.grid, cellnr) - ip_geo = geometric_interpolation(typeof(cell)) - ip_fun = Ferrite.getfieldinterpolation(dh, (1,1)) - qr = create_gradcheck_qr(ip_geo, ΔL) - cv = CellValues(qr, ip_fun, ip_geo) - reinit!(cv, cell, x) - Δu_num = function_value(cv, 2, ue) - function_value(cv, 1, ue) - Δx = spatial_coordinate(cv, 2, x) - spatial_coordinate(cv, 1, x) - ∇u1 = function_gradient(cv, 1, ue) - ∇u2 = function_gradient(cv, 2, ue) - Δu_ana = 0.5*(∇u1+∇u2) ⋅ Δx - # Δu_ana_var = 0.5*(∇u2-∇u1) ⋅ Δx # Relevant to compare magnitude if test fails - @test isapprox(Δu_num, Δu_ana; rtol=1e-6) - return nothing - end + d1 = map((v, n) -> transformation_function(v, n), fun_vals, point_normal) + d2 = map((v, n) -> transformation_function(v, n), fun_vals2, point_normal) + @test isapprox(d1, d2; rtol = 1.0e-6) # Approximate points can contribute to the inexactness + return true +end + +function create_gradcheck_qr(ip_geo::Interpolation{RefShape}, ΔL) where {RefShape} + dim = Ferrite.getrefdim(ip_geo) + xref = Ferrite.reference_coordinates(ip_geo) + xc = sum(xref) / length(xref) + ws = rand(length(xref)) * ((1 - ΔL) / length(xref)) + xp = xc + sum(map((x, w) -> w * (x - xc), xref, ws)) + v = normalize(rand(Vec{dim}) - ones(Vec{dim}) / 2) + x1 = xp + ΔL * v + qr_w = [NaN, NaN] + qr_x = [xp, x1] + return QuadratureRule{RefShape}(qr_w, qr_x) +end + +function test_gradient(dh, cellnr; ΔL = 1.0e-6) + ue = rand(ndofs_per_cell(dh, cellnr)) + x = getcoordinates(dh.grid, cellnr) + cell = getcells(dh.grid, cellnr) + ip_geo = geometric_interpolation(typeof(cell)) + ip_fun = Ferrite.getfieldinterpolation(dh, (1, 1)) + qr = create_gradcheck_qr(ip_geo, ΔL) + cv = CellValues(qr, ip_fun, ip_geo) + reinit!(cv, cell, x) + Δu_num = function_value(cv, 2, ue) - function_value(cv, 1, ue) + Δx = spatial_coordinate(cv, 2, x) - spatial_coordinate(cv, 1, x) + ∇u1 = function_gradient(cv, 1, ue) + ∇u2 = function_gradient(cv, 2, ue) + Δu_ana = 0.5 * (∇u1 + ∇u2) ⋅ Δx + # Δu_ana_var = 0.5*(∇u2-∇u1) ⋅ Δx # Relevant to compare magnitude if test fails + @test isapprox(Δu_num, Δu_ana; rtol = 1.0e-6) + return nothing +end end diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 4405a09b76..53f552feb7 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -225,8 +225,8 @@ using Ferrite: reference_shape_value, reference_shape_gradient end @testset "Correctness of AD of embedded interpolations" begin - ip = Lagrange{RefHexahedron,2}()^3 - ξ = rand(Vec{3,Float64}) + ip = Lagrange{RefHexahedron, 2}()^3 + ξ = rand(Vec{3, Float64}) for I in 1:getnbasefunctions(ip) #Call StaticArray-version H_sa, G_sa, V_sa = Ferrite._reference_shape_hessian_gradient_and_value_static_array(ip, ξ, I) @@ -251,92 +251,93 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end -reference_cell(::Type{RefTriangle}) = Triangle((1,2,3)) -reference_cell(::Type{RefQuadrilateral}) = Quadrilateral((1,2,3,4)) -reference_cell(::Type{RefTetrahedron}) = Tetrahedron((1,2,3,4)) -reference_cell(::Type{RefHexahedron}) = Hexahedron((ntuple(identity, 8))) + reference_cell(::Type{RefTriangle}) = Triangle((1, 2, 3)) + reference_cell(::Type{RefQuadrilateral}) = Quadrilateral((1, 2, 3, 4)) + reference_cell(::Type{RefTetrahedron}) = Tetrahedron((1, 2, 3, 4)) + reference_cell(::Type{RefHexahedron}) = Hexahedron((ntuple(identity, 8))) -function line_integral(qr::QuadratureRule{RefLine}, ip, shape_nr, x0, Δx, L, v, f) - s = 0.0 - for (ξ1d, w) in zip(Ferrite.getpoints(qr), Ferrite.getweights(qr)) - ξ = x0 + (ξ1d[1]/2) * Δx - s += (reference_shape_value(ip, ξ, shape_nr) ⋅ v) * (w*L/2) * f((ξ1d[1]+1)/2) + function line_integral(qr::QuadratureRule{RefLine}, ip, shape_nr, x0, Δx, L, v, f) + s = 0.0 + for (ξ1d, w) in zip(Ferrite.getpoints(qr), Ferrite.getweights(qr)) + ξ = x0 + (ξ1d[1] / 2) * Δx + s += (reference_shape_value(ip, ξ, shape_nr) ⋅ v) * (w * L / 2) * f((ξ1d[1] + 1) / 2) + end + return s end - return s -end - -# Required properties of shape value Nⱼ of an edge-elements (Hcurl) on an edge with direction v, length L, and dofs ∈ 𝔇 -# 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1 ∀ ∈ 𝔇 -# Where f(s) = 1 for linear interpolation and f(s)=1-s and f(s)=s for 2nd order interpolation (first and second shape function) -# And s is the path parameter ∈[0,1] along the positive direction of the path. -# 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 -@testset "H(curl) on RefCell" begin - lineqr = QuadratureRule{RefLine}(20) - for ip in (Nedelec{2,RefTriangle,1}(), Nedelec{2,RefTriangle,2}(), Nedelec{3,RefTetrahedron,1}(), Nedelec{3,RefHexahedron,1}()) - cell = reference_cell(getrefshape(ip)) - edges = Ferrite.edges(cell) - dofs = Ferrite.edgedof_interior_indices(ip) - x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) - @testset "$(getrefshape(ip)), order=$(Ferrite.getorder(ip))" begin - for (edge_nr, (i1, i2)) in enumerate(edges) - Δx = x[i2]-x[i1] - x0 = (x[i1]+x[i2])/2 - L = norm(Δx) - v = Δx/L - for (idof, shape_nr) in enumerate(dofs[edge_nr]) - nedgedofs = length(dofs[edge_nr]) - f(x) = nedgedofs == 1 ? 1.0 : (idof == 1 ? 1-x : x) - s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, f) - @test s ≈ one(s) - end - for (j_edge, shape_nrs) in enumerate(dofs) - j_edge == edge_nr && continue - for shape_nr in shape_nrs - for ξ in (x[i1] + r*Δx for r in [0.0, rand(3)..., 1.0]) - @test abs(reference_shape_value(ip, ξ, shape_nr) ⋅ v) < eps()*100 + + # Required properties of shape value Nⱼ of an edge-elements (Hcurl) on an edge with direction v, length L, and dofs ∈ 𝔇 + # 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1 ∀ ∈ 𝔇 + # Where f(s) = 1 for linear interpolation and f(s)=1-s and f(s)=s for 2nd order interpolation (first and second shape function) + # And s is the path parameter ∈[0,1] along the positive direction of the path. + # 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 + @testset "H(curl) on RefCell" begin + lineqr = QuadratureRule{RefLine}(20) + for ip in (Nedelec{2, RefTriangle, 1}(), Nedelec{2, RefTriangle, 2}(), Nedelec{3, RefTetrahedron, 1}(), Nedelec{3, RefHexahedron, 1}()) + cell = reference_cell(getrefshape(ip)) + edges = Ferrite.edges(cell) + dofs = Ferrite.edgedof_interior_indices(ip) + x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) + @testset "$(getrefshape(ip)), order=$(Ferrite.getorder(ip))" begin + for (edge_nr, (i1, i2)) in enumerate(edges) + Δx = x[i2] - x[i1] + x0 = (x[i1] + x[i2]) / 2 + L = norm(Δx) + v = Δx / L + for (idof, shape_nr) in enumerate(dofs[edge_nr]) + nedgedofs = length(dofs[edge_nr]) + f(x) = nedgedofs == 1 ? 1.0 : (idof == 1 ? 1 - x : x) + s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, f) + @test s ≈ one(s) + end + for (j_edge, shape_nrs) in enumerate(dofs) + j_edge == edge_nr && continue + for shape_nr in shape_nrs + for ξ in (x[i1] + r * Δx for r in [0.0, rand(3)..., 1.0]) + @test abs(reference_shape_value(ip, ξ, shape_nr) ⋅ v) < eps() * 100 + end end end end end end end -end - -# Required properties of shape value Nⱼ of an edge-elements (Hdiv) on an edge with normal n, length L, and dofs ∈ 𝔇 -# 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 ∀ ∈ 𝔇 -# Where f(s) = 1 for single shape function on edge, and f(s)=1-s and f(s)=s for two shape functions on edge -# s is the path parameter ∈[0,1] along the positive direction of the path. -# 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 -@testset "H(div) on RefCell" begin - lineqr = QuadratureRule{RefLine}(20) - for ip in ( - RaviartThomas{2, RefTriangle, 1}(), - RaviartThomas{2, RefTriangle, 2}(), - Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), + + # Required properties of shape value Nⱼ of an edge-elements (Hdiv) on an edge with normal n, length L, and dofs ∈ 𝔇 + # 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 ∀ ∈ 𝔇 + # Where f(s) = 1 for single shape function on edge, and f(s)=1-s and f(s)=s for two shape functions on edge + # s is the path parameter ∈[0,1] along the positive direction of the path. + # 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 + @testset "H(div) on RefCell" begin + lineqr = QuadratureRule{RefLine}(20) + for ip in ( + RaviartThomas{2, RefTriangle, 1}(), + RaviartThomas{2, RefTriangle, 2}(), + Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), ) - cell = reference_cell(getrefshape(ip)) - cell_facets = Ferrite.facets(cell) - dofs = Ferrite.facetdof_interior_indices(ip) - x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) - normals = reference_normals(geometric_interpolation(typeof(cell))) - @testset "$ip" begin - for (facet_nr, (i1, i2)) in enumerate(cell_facets) - @testset "Facet $facet_nr" begin - Δx = x[i2]-x[i1] - x0 = (x[i1]+x[i2])/2 - L = norm(Δx) - n = normals[facet_nr] - for (idof, shape_nr) in enumerate(dofs[facet_nr]) - nfacetdofs = length(dofs[facet_nr]) - f(x) = nfacetdofs == 1 ? 1.0 : (idof == 1 ? 1-x : x) - s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, f) - @test s ≈ one(s) - end - for (j_facet, shape_nrs) in enumerate(dofs) - j_facet == facet_nr && continue - for shape_nr in shape_nrs - for ξ in (x[i1] + r*Δx for r in [0.0, rand(3)..., 1.0]) - @test abs(reference_shape_value(ip, ξ, shape_nr) ⋅ n) < eps()*100 + cell = reference_cell(getrefshape(ip)) + cell_facets = Ferrite.facets(cell) + dofs = Ferrite.facetdof_interior_indices(ip) + x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) + normals = reference_normals(geometric_interpolation(typeof(cell))) + @testset "$ip" begin + for (facet_nr, (i1, i2)) in enumerate(cell_facets) + @testset "Facet $facet_nr" begin + Δx = x[i2] - x[i1] + x0 = (x[i1] + x[i2]) / 2 + L = norm(Δx) + n = normals[facet_nr] + for (idof, shape_nr) in enumerate(dofs[facet_nr]) + nfacetdofs = length(dofs[facet_nr]) + f(x) = nfacetdofs == 1 ? 1.0 : (idof == 1 ? 1 - x : x) + s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, f) + @test s ≈ one(s) + end + for (j_facet, shape_nrs) in enumerate(dofs) + j_facet == facet_nr && continue + for shape_nr in shape_nrs + for ξ in (x[i1] + r * Δx for r in [0.0, rand(3)..., 1.0]) + @test abs(reference_shape_value(ip, ξ, shape_nr) ⋅ n) < eps() * 100 + end end end end @@ -344,98 +345,99 @@ end end end end -end - -tupleshift(t::NTuple{N}, shift::Int) where N = ntuple(i -> t[mod(i - 1 - shift, N) + 1], N) -#tupleshift(t::NTuple, shift::Int) = tuple(circshift(SVector(t), shift)...) -cell_permutations(cell::Quadrilateral) = (Quadrilateral(tupleshift(cell.nodes, shift)) for shift in 0:3) -cell_permutations(cell::Triangle) = ( Triangle(tupleshift(cell.nodes, shift)) for shift in 0:2) -cell_permutations(cell::QuadraticTriangle) = (QuadraticTriangle((tupleshift(cell.nodes[1:3], shift)..., tupleshift(cell.nodes[4:6], shift)...)) for shift in 0:3) - -function cell_permutations(cell::Hexahedron) - idx = ( #Logic on refshape: Select 1st and 2nd vertex (must be neighbours) - # The next follows to create inward vector with RHR, and then 4th is in same plane. - # The last four must be the neighbours on the other plane to the first four (same order) - (1,2,3,4,5,6,7,8), (1,4,8,5,2,3,7,6), (1,5,6,2,4,8,7,3), - (2,1,5,6,3,4,8,7), (2,3,4,1,6,7,8,5), (2,6,7,3,1,5,8,4), - (3,2,6,7,4,1,5,8), (3,4,1,2,7,8,5,6), (3,7,8,4,2,6,5,1), - (4,1,2,3,8,5,6,7), (4,3,7,8,1,2,6,5), (4,8,5,1,3,7,6,1), - (5,1,4,8,6,2,3,7), (5,6,2,1,8,7,3,4), (5,8,7,6,1,4,3,2), - (6,2,1,5,7,3,4,8), (6,5,8,7,2,1,4,3), (6,7,3,2,5,8,4,1), - (7,3,2,6,8,4,1,5), (7,6,5,8,3,2,1,4), (7,8,4,3,6,5,1,2), - (8,4,3,7,5,1,2,6), (8,5,1,4,7,6,2,3), (8,7,6,5,4,3,2,1), - ) - return (Hexahedron(ntuple(i -> cell.nodes[perm[i]], 8)) for perm in idx) -end - -function cell_permutations(cell::Tetrahedron) - idx = ( (1,2,3,4), (1,3,4,2), (1,4,2,3), - (2,1,4,3), (2,3,1,4), (2,4,3,1), - (3,1,2,4), (3,2,4,1), (3,4,1,2), - (4,1,3,2), (4,3,2,1), (4,2,1,3)) - return (Tetrahedron(ntuple(i -> cell.nodes[perm[i]], 4)) for perm in idx) -end - -@testset "Hcurl and Hdiv" begin - include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) - import .InterpolationTestUtils as ITU - nel = 3 - hdiv_check(v, n) = v ⋅ n # Hdiv (normal continuity) - hcurl_check(v, n) = v - n*(v⋅n) # Hcurl (tangent continuity) - transformation_functions = ((Nedelec, hcurl_check), (RaviartThomas, hdiv_check), (Ferrite.BrezziDouglasMarini, hdiv_check)) - - for CT in (Triangle, QuadraticTriangle, Tetrahedron, Hexahedron) - dim = Ferrite.getrefdim(CT) # dim = sdim = rdim - p1, p2 = (rand(Vec{dim}), ones(Vec{dim})+rand(Vec{dim})) - grid = generate_grid(CT, ntuple(_->nel, dim), p1, p2) - # Smoothly distort grid (to avoid spuriously badly deformed elements). - # A distorted grid is important to properly test the geometry mapping - # for 2nd order elements. - transfun(x) = typeof(x)(i->sinpi(x[mod(i, length(x))+1]+i/3))/10 - transform_coordinates!(grid, x->(x + transfun(x))) - cellnr = getncells(grid)÷2 + 1 # Should be a cell in the center - basecell = getcells(grid, cellnr) - RefShape = Ferrite.getrefshape(basecell) - for order in (1, 2) - for (IPT, transformation_function) in transformation_functions - dim == 3 && order > 1 && continue - IPT == RaviartThomas && (dim == 3 || order > 1) && continue - IPT == RaviartThomas && (RefShape == RefHexahedron) && continue - IPT == Ferrite.BrezziDouglasMarini && !(RefShape == RefTriangle && order == 1) && continue - ip = IPT{dim, RefShape, order}() - @testset "$CT, $ip" begin - for testcell in cell_permutations(basecell) - grid.cells[cellnr] = testcell - dh = DofHandler(grid) - add!(dh, :u, ip) - close!(dh) - for facetnr in 1:nfacets(RefShape) - fi = FacetIndex(cellnr, facetnr) - # Check continuity of tangential function value - ITU.test_continuity(dh, fi; transformation_function) + + tupleshift(t::NTuple{N}, shift::Int) where {N} = ntuple(i -> t[mod(i - 1 - shift, N) + 1], N) + #tupleshift(t::NTuple, shift::Int) = tuple(circshift(SVector(t), shift)...) + cell_permutations(cell::Quadrilateral) = (Quadrilateral(tupleshift(cell.nodes, shift)) for shift in 0:3) + cell_permutations(cell::Triangle) = (Triangle(tupleshift(cell.nodes, shift)) for shift in 0:2) + cell_permutations(cell::QuadraticTriangle) = (QuadraticTriangle((tupleshift(cell.nodes[1:3], shift)..., tupleshift(cell.nodes[4:6], shift)...)) for shift in 0:3) + + function cell_permutations(cell::Hexahedron) + idx = ( #Logic on refshape: Select 1st and 2nd vertex (must be neighbours) + # The next follows to create inward vector with RHR, and then 4th is in same plane. + # The last four must be the neighbours on the other plane to the first four (same order) + (1, 2, 3, 4, 5, 6, 7, 8), (1, 4, 8, 5, 2, 3, 7, 6), (1, 5, 6, 2, 4, 8, 7, 3), + (2, 1, 5, 6, 3, 4, 8, 7), (2, 3, 4, 1, 6, 7, 8, 5), (2, 6, 7, 3, 1, 5, 8, 4), + (3, 2, 6, 7, 4, 1, 5, 8), (3, 4, 1, 2, 7, 8, 5, 6), (3, 7, 8, 4, 2, 6, 5, 1), + (4, 1, 2, 3, 8, 5, 6, 7), (4, 3, 7, 8, 1, 2, 6, 5), (4, 8, 5, 1, 3, 7, 6, 1), + (5, 1, 4, 8, 6, 2, 3, 7), (5, 6, 2, 1, 8, 7, 3, 4), (5, 8, 7, 6, 1, 4, 3, 2), + (6, 2, 1, 5, 7, 3, 4, 8), (6, 5, 8, 7, 2, 1, 4, 3), (6, 7, 3, 2, 5, 8, 4, 1), + (7, 3, 2, 6, 8, 4, 1, 5), (7, 6, 5, 8, 3, 2, 1, 4), (7, 8, 4, 3, 6, 5, 1, 2), + (8, 4, 3, 7, 5, 1, 2, 6), (8, 5, 1, 4, 7, 6, 2, 3), (8, 7, 6, 5, 4, 3, 2, 1), + ) + return (Hexahedron(ntuple(i -> cell.nodes[perm[i]], 8)) for perm in idx) + end + + function cell_permutations(cell::Tetrahedron) + idx = ( + (1, 2, 3, 4), (1, 3, 4, 2), (1, 4, 2, 3), + (2, 1, 4, 3), (2, 3, 1, 4), (2, 4, 3, 1), + (3, 1, 2, 4), (3, 2, 4, 1), (3, 4, 1, 2), + (4, 1, 3, 2), (4, 3, 2, 1), (4, 2, 1, 3), + ) + return (Tetrahedron(ntuple(i -> cell.nodes[perm[i]], 4)) for perm in idx) + end + + @testset "Hcurl and Hdiv" begin + include(joinpath(@__DIR__, "InterpolationTestUtils.jl")) + import .InterpolationTestUtils as ITU + nel = 3 + hdiv_check(v, n) = v ⋅ n # Hdiv (normal continuity) + hcurl_check(v, n) = v - n * (v ⋅ n) # Hcurl (tangent continuity) + transformation_functions = ((Nedelec, hcurl_check), (RaviartThomas, hdiv_check), (Ferrite.BrezziDouglasMarini, hdiv_check)) + + for CT in (Triangle, QuadraticTriangle, Tetrahedron, Hexahedron) + dim = Ferrite.getrefdim(CT) # dim = sdim = rdim + p1, p2 = (rand(Vec{dim}), ones(Vec{dim}) + rand(Vec{dim})) + grid = generate_grid(CT, ntuple(_ -> nel, dim), p1, p2) + # Smoothly distort grid (to avoid spuriously badly deformed elements). + # A distorted grid is important to properly test the geometry mapping + # for 2nd order elements. + transfun(x) = typeof(x)(i -> sinpi(x[mod(i, length(x)) + 1] + i / 3)) / 10 + transform_coordinates!(grid, x -> (x + transfun(x))) + cellnr = getncells(grid) ÷ 2 + 1 # Should be a cell in the center + basecell = getcells(grid, cellnr) + RefShape = Ferrite.getrefshape(basecell) + for order in (1, 2) + for (IPT, transformation_function) in transformation_functions + dim == 3 && order > 1 && continue + IPT == RaviartThomas && (dim == 3 || order > 1) && continue + IPT == RaviartThomas && (RefShape == RefHexahedron) && continue + IPT == Ferrite.BrezziDouglasMarini && !(RefShape == RefTriangle && order == 1) && continue + ip = IPT{dim, RefShape, order}() + @testset "$CT, $ip" begin + for testcell in cell_permutations(basecell) + grid.cells[cellnr] = testcell + dh = DofHandler(grid) + add!(dh, :u, ip) + close!(dh) + for facetnr in 1:nfacets(RefShape) + fi = FacetIndex(cellnr, facetnr) + # Check continuity of tangential function value + ITU.test_continuity(dh, fi; transformation_function) + end + # Check gradient calculation + ITU.test_gradient(dh, cellnr) end - # Check gradient calculation - ITU.test_gradient(dh, cellnr) end end end end end -end -@testset "Errors for entitydof_indices on VectorizedInterpolations" begin - ip = Lagrange{RefQuadrilateral, 2}()^2 - @test_throws ArgumentError Ferrite.vertexdof_indices(ip) - @test_throws ArgumentError Ferrite.edgedof_indices(ip) - @test_throws ArgumentError Ferrite.facedof_indices(ip) - @test_throws ArgumentError Ferrite.facetdof_indices(ip) + @testset "Errors for entitydof_indices on VectorizedInterpolations" begin + ip = Lagrange{RefQuadrilateral, 2}()^2 + @test_throws ArgumentError Ferrite.vertexdof_indices(ip) + @test_throws ArgumentError Ferrite.edgedof_indices(ip) + @test_throws ArgumentError Ferrite.facedof_indices(ip) + @test_throws ArgumentError Ferrite.facetdof_indices(ip) - @test_throws ArgumentError Ferrite.edgedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.facedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.volumedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.facetdof_interior_indices(ip) -end + @test_throws ArgumentError Ferrite.edgedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.facedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.volumedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.facetdof_interior_indices(ip) + end end # testset diff --git a/visualization/2d_vector_interpolation_checkplots.jl b/visualization/2d_vector_interpolation_checkplots.jl deleted file mode 100644 index d48b4ad360..0000000000 --- a/visualization/2d_vector_interpolation_checkplots.jl +++ /dev/null @@ -1,59 +0,0 @@ -using Ferrite -import CairoMakie as Plt - -function facet_quadrature(::Type{RefShape}, nqp::Int) where RefShape - fqr = FacetQuadratureRule{RefShape}(nqp) - points = [p for rule in fqr.face_rules for p in rule.points] - weights = [w for rule in fqr.face_rules for w in rule.weights] - return QuadratureRule{RefShape}(weights, points) -end - -function plot_shape_function(ip::VectorInterpolation{2, RefShape}, qr::Int, i::Int) where {RefShape<:Ferrite.AbstractRefShape{2}} - return plot_shape_function(ip, QuadratureRule{RefShape}(qr), i) -end - -function plot_shape_function(ip::VectorInterpolation{2, RefShape}, qr::QuadratureRule{RefShape}, i::Int) where {RefShape<:Ferrite.AbstractRefShape{2}} - points = Ferrite.getpoints(qr) - N = map(ξ -> Ferrite.reference_shape_value(ip, ξ, i), points) - vertices = Ferrite.reference_coordinates(Lagrange{RefShape, 1}()) - push!(vertices, first(vertices)) - - fig = Plt.Figure() - ax = Plt.Axis(fig[1,1]; aspect=Plt.DataAspect()) - Plt.xlims!(ax, (-1.0, 1.5)) - Plt.lines!(ax, first.(vertices), last.(vertices)) - Plt.arrows!(ax, first.(points), last.(points), first.(N), last.(N); lengthscale = 0.1) - Plt.scatter!(ax, first.(points), last.(points)) - return fig -end - -function plot_global_shape_function(ip::VectorInterpolation{2, RefShape}; qr_order::Int=0, nel, i, qr=nothing) where RefShape - fig = Plt.Figure() - ax = Plt.Axis(fig[1,1]) - _qr = qr === nothing ? QuadratureRule{RefShape}(qr_order) : qr - CT = RefShape === RefTriangle ? Triangle : Quadrilateral - grid = generate_grid(CT, (nel, nel)) - dh = close!(add!(DofHandler(grid), :u, ip)) - points = Vec{2, Float64}[] - directions = Vec{2, Float64}[] - cv = CellValues(_qr, ip, Lagrange{RefShape, 1}()) - cell_contour = getcoordinates(grid, 1) - resize!(cell_contour, length(cell_contour) + 1) - for cell in CellIterator(dh) - copyto!(cell_contour, getcoordinates(cell)) - cell_contour[end] = cell_contour[1] # Close contour - Plt.lines!(ax, first.(cell_contour), last.(cell_contour)) - if i ∈ celldofs(cell) - reinit!(cv, cell) - for q_point in 1:getnquadpoints(cv) - x = spatial_coordinate(cv, q_point, getcoordinates(cell)) - shape_index = findfirst(x -> x == i, celldofs(cell)) - push!(points, x) - push!(directions, shape_value(cv, q_point, shape_index)) - end - end - end - Plt.arrows!(ax, first.(points), last.(points), first.(directions), last.(directions); lengthscale = 0.1) - Plt.scatter!(ax, first.(points), last.(points)) - return fig -end diff --git a/visualization/Manifest.toml b/visualization/Manifest.toml deleted file mode 100644 index dfda582b2d..0000000000 --- a/visualization/Manifest.toml +++ /dev/null @@ -1,1578 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.11.1" -manifest_format = "2.0" -project_hash = "3f9b12f33e28e03dfcb3edf6f53efa43b44e834b" - -[[deps.AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" -uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.5.0" -weakdeps = ["ChainRulesCore", "Test"] - - [deps.AbstractFFTs.extensions] - AbstractFFTsChainRulesCoreExt = "ChainRulesCore" - AbstractFFTsTestExt = "Test" - -[[deps.AbstractTrees]] -git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.4.5" - -[[deps.Adapt]] -deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "50c3c56a52972d78e8be9fd135bfb91c9574c140" -uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "4.1.1" -weakdeps = ["StaticArrays"] - - [deps.Adapt.extensions] - AdaptStaticArraysExt = "StaticArrays" - -[[deps.AdaptivePredicates]] -git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6" -uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" -version = "1.2.0" - -[[deps.AliasTables]] -deps = ["PtrArrays", "Random"] -git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" -uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" -version = "1.1.3" - -[[deps.Animations]] -deps = ["Colors"] -git-tree-sha1 = "e81c509d2c8e49592413bfb0bb3b08150056c79d" -uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340" -version = "0.4.1" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.2" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" -version = "1.11.0" - -[[deps.Automa]] -deps = ["PrecompileTools", "SIMD", "TranscodingStreams"] -git-tree-sha1 = "a8f503e8e1a5f583fbef15a8440c8c7e32185df2" -uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" -version = "1.1.0" - -[[deps.AxisAlgorithms]] -deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] -git-tree-sha1 = "01b8ccb13d68535d73d2b0c23e39bd23155fb712" -uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950" -version = "1.1.0" - -[[deps.AxisArrays]] -deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"] -git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f" -uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9" -version = "0.4.7" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" -version = "1.11.0" - -[[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "8873e196c2eb87962a2048b3b8e08946535864a1" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+2" - -[[deps.CEnum]] -git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc" -uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" -version = "0.5.0" - -[[deps.CRC32c]] -uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" -version = "1.11.0" - -[[deps.CRlibm_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" -uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" -version = "1.0.1+0" - -[[deps.Cairo]] -deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] -git-tree-sha1 = "7b6ad8c35f4bc3bca8eb78127c8b99719506a5fb" -uuid = "159f3aea-2a34-519c-b102-8c37f9878175" -version = "1.1.0" - -[[deps.CairoMakie]] -deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] -git-tree-sha1 = "fbfdb7cbe17bd14b60646c14c27a16e5038cde54" -uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" -version = "0.12.15" - -[[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"] -git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.2+1" - -[[deps.ChainRulesCore]] -deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "3e4b134270b372f2ed4d4d0e936aabaefc1802bc" -uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.25.0" -weakdeps = ["SparseArrays"] - - [deps.ChainRulesCore.extensions] - ChainRulesCoreSparseArraysExt = "SparseArrays" - -[[deps.CodecZlib]] -deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" -uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.6" - -[[deps.ColorBrewer]] -deps = ["Colors", "JSON", "Test"] -git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4" -uuid = "a2cac450-b92f-5266-8821-25eda20663c8" -version = "0.4.0" - -[[deps.ColorSchemes]] -deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "13951eb68769ad1cd460cdb2e64e5e95f1bf123d" -uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.27.0" - -[[deps.ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.5" - -[[deps.ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] -git-tree-sha1 = "a1f44953f2382ebb937d60dafbe2deea4bd23249" -uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.10.0" -weakdeps = ["SpecialFunctions"] - - [deps.ColorVectorSpace.extensions] - SpecialFunctionsExt = "SpecialFunctions" - -[[deps.Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "362a287c3aa50601b0bc359053d5c2468f0e7ce0" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.11" - -[[deps.CommonSubexpressions]] -deps = ["MacroTools"] -git-tree-sha1 = "cda2cfaebb4be89c9084adaca7dd7333369715c5" -uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" -version = "0.3.1" - -[[deps.Compat]] -deps = ["TOML", "UUIDs"] -git-tree-sha1 = "8ae8d32e09f0dcf42a36b90d4e17f5dd2e4c4215" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.16.0" -weakdeps = ["Dates", "LinearAlgebra"] - - [deps.Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.1.1+0" - -[[deps.ConstructionBase]] -git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" -uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.8" -weakdeps = ["IntervalSets", "LinearAlgebra", "StaticArrays"] - - [deps.ConstructionBase.extensions] - ConstructionBaseIntervalSetsExt = "IntervalSets" - ConstructionBaseLinearAlgebraExt = "LinearAlgebra" - ConstructionBaseStaticArraysExt = "StaticArrays" - -[[deps.Contour]] -git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" -uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.6.3" - -[[deps.DataAPI]] -git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" -uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.16.0" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "1d0a14036acb104d9e89698bd408f63ab58cdc82" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.20" - -[[deps.DataValueInterfaces]] -git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" -uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" -version = "1.0.0" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" -version = "1.11.0" - -[[deps.DelaunayTriangulation]] -deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "PrecompileTools", "Random"] -git-tree-sha1 = "89df54fbe66e5872d91d8c2cd3a375f660c3fd64" -uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" -version = "1.6.1" - -[[deps.DiffResults]] -deps = ["StaticArraysCore"] -git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" -uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" -version = "1.1.0" - -[[deps.DiffRules]] -deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] -git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" -uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" -version = "1.15.1" - -[[deps.Distances]] -deps = ["LinearAlgebra", "Statistics", "StatsAPI"] -git-tree-sha1 = "c7e3a542b999843086e2f29dac96a618c105be1d" -uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.12" -weakdeps = ["ChainRulesCore", "SparseArrays"] - - [deps.Distances.extensions] - DistancesChainRulesCoreExt = "ChainRulesCore" - DistancesSparseArraysExt = "SparseArrays" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" -version = "1.11.0" - -[[deps.Distributions]] -deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "d7477ecdafb813ddee2ae727afa94e9dcb5f3fb0" -uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.112" - - [deps.Distributions.extensions] - DistributionsChainRulesCoreExt = "ChainRulesCore" - DistributionsDensityInterfaceExt = "DensityInterface" - DistributionsTestExt = "Test" - - [deps.Distributions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[deps.EarCut_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053" -uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" -version = "2.2.4+0" - -[[deps.EnumX]] -git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" -uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" -version = "1.0.4" - -[[deps.ExactPredicates]] -deps = ["IntervalArithmetic", "Random", "StaticArrays"] -git-tree-sha1 = "b3f2ff58735b5f024c392fde763f29b057e4b025" -uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" -version = "2.2.8" - -[[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.6.2+0" - -[[deps.Extents]] -git-tree-sha1 = "81023caa0021a41712685887db1fc03db26f41f5" -uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" -version = "0.1.4" - -[[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"] -git-tree-sha1 = "8cc47f299902e13f90405ddb5bf87e5d474c0d38" -uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "6.1.2+0" - -[[deps.FFTW]] -deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] -git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" -uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" -version = "1.8.0" - -[[deps.FFTW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" -uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+1" - -[[deps.Ferrite]] -deps = ["EnumX", "ForwardDiff", "LinearAlgebra", "NearestNeighbors", "OrderedCollections", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] -path = ".." -uuid = "c061ca5d-56c9-439f-9c0e-210fe06d3992" -version = "1.0.0" - - [deps.Ferrite.extensions] - FerriteBlockArrays = "BlockArrays" - FerriteMetis = "Metis" - - [deps.Ferrite.weakdeps] - BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" - Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" - -[[deps.FileIO]] -deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "62ca0547a14c57e98154423419d8a342dca75ca9" -uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.4" - -[[deps.FilePaths]] -deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"] -git-tree-sha1 = "919d9412dbf53a2e6fe74af62a73ceed0bce0629" -uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824" -version = "0.8.3" - -[[deps.FilePathsBase]] -deps = ["Compat", "Dates"] -git-tree-sha1 = "7878ff7172a8e6beedd1dea14bd27c3c6340d361" -uuid = "48062228-2e41-5def-b9a4-89aafe57970f" -version = "0.9.22" -weakdeps = ["Mmap", "Test"] - - [deps.FilePathsBase.extensions] - FilePathsBaseMmapExt = "Mmap" - FilePathsBaseTestExt = "Test" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" -version = "1.11.0" - -[[deps.FillArrays]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.13.0" -weakdeps = ["PDMats", "SparseArrays", "Statistics"] - - [deps.FillArrays.extensions] - FillArraysPDMatsExt = "PDMats" - FillArraysSparseArraysExt = "SparseArrays" - FillArraysStatisticsExt = "Statistics" - -[[deps.FixedPointNumbers]] -deps = ["Statistics"] -git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.5" - -[[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] -git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.96+0" - -[[deps.Format]] -git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" -uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" -version = "1.3.7" - -[[deps.ForwardDiff]] -deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] -git-tree-sha1 = "a9ce73d3c827adab2d70bf168aaece8cce196898" -uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.37" -weakdeps = ["StaticArrays"] - - [deps.ForwardDiff.extensions] - ForwardDiffStaticArraysExt = "StaticArrays" - -[[deps.FreeType]] -deps = ["CEnum", "FreeType2_jll"] -git-tree-sha1 = "907369da0f8e80728ab49c1c7e09327bf0d6d999" -uuid = "b38be410-82b0-50bf-ab77-7b57e271db43" -version = "4.1.1" - -[[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.2+0" - -[[deps.FreeTypeAbstraction]] -deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] -git-tree-sha1 = "84dfe824bd6fdf2a5d73bb187ff31b5549b2a79c" -uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" -version = "0.10.4" - -[[deps.FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" -uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.14+0" - -[[deps.GeoFormatTypes]] -git-tree-sha1 = "59107c179a586f0fe667024c5eb7033e81333271" -uuid = "68eda718-8dee-11e9-39e7-89f7f65f511f" -version = "0.4.2" - -[[deps.GeoInterface]] -deps = ["Extents", "GeoFormatTypes"] -git-tree-sha1 = "2f6fce56cdb8373637a6614e14a5768a88450de2" -uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" -version = "1.3.7" - -[[deps.GeometryBasics]] -deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "b62f2b2d76cee0d61a2ef2b3118cd2a3215d3134" -uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" -version = "0.4.11" - -[[deps.Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - -[[deps.Giflib_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0224cce99284d997f6880a42ef715a37c99338d1" -uuid = "59f7168a-df46-5410-90c8-f2779963d0ec" -version = "5.2.2+0" - -[[deps.Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.5+0" - -[[deps.Graphics]] -deps = ["Colors", "LinearAlgebra", "NaNMath"] -git-tree-sha1 = "d61890399bc535850c4bf08e4e0d3a7ad0f21cbd" -uuid = "a2bd30eb-e257-5431-a919-1863eab51364" -version = "1.1.2" - -[[deps.Graphite2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" -uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" -version = "1.3.14+0" - -[[deps.GridLayoutBase]] -deps = ["GeometryBasics", "InteractiveUtils", "Observables"] -git-tree-sha1 = "fc713f007cff99ff9e50accba6373624ddd33588" -uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" -version = "0.11.0" - -[[deps.Grisu]] -git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" -uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" -version = "1.0.2" - -[[deps.HarfBuzz_jll]] -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 = "8.3.1+0" - -[[deps.HypergeometricFunctions]] -deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "7c4195be1649ae622304031ed46a2f4df989f1eb" -uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.24" - -[[deps.ImageAxes]] -deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"] -git-tree-sha1 = "2e4520d67b0cef90865b3ef727594d2a58e0e1f8" -uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac" -version = "0.6.11" - -[[deps.ImageBase]] -deps = ["ImageCore", "Reexport"] -git-tree-sha1 = "eb49b82c172811fd2c86759fa0553a2221feb909" -uuid = "c817782e-172a-44cc-b673-b171935fbb9e" -version = "0.1.7" - -[[deps.ImageCore]] -deps = ["ColorVectorSpace", "Colors", "FixedPointNumbers", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "PrecompileTools", "Reexport"] -git-tree-sha1 = "b2a7eaa169c13f5bcae8131a83bc30eff8f71be0" -uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534" -version = "0.10.2" - -[[deps.ImageIO]] -deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs", "WebP"] -git-tree-sha1 = "696144904b76e1ca433b886b4e7edd067d76cbf7" -uuid = "82e4d734-157c-48bb-816b-45c225c6df19" -version = "0.6.9" - -[[deps.ImageMetadata]] -deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] -git-tree-sha1 = "355e2b974f2e3212a75dfb60519de21361ad3cb7" -uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49" -version = "0.9.9" - -[[deps.Imath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0936ba688c6d201805a83da835b55c61a180db52" -uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" -version = "3.1.11+0" - -[[deps.IndirectArrays]] -git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" -uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" -version = "1.0.0" - -[[deps.Inflate]] -git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" -uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" -version = "0.1.5" - -[[deps.IntelOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] -git-tree-sha1 = "10bd689145d2c3b2a9844005d01087cc1194e79e" -uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" -version = "2024.2.1+0" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" -version = "1.11.0" - -[[deps.Interpolations]] -deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] -git-tree-sha1 = "88a101217d7cb38a7b481ccd50d21876e1d1b0e0" -uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" -version = "0.15.1" -weakdeps = ["Unitful"] - - [deps.Interpolations.extensions] - InterpolationsUnitfulExt = "Unitful" - -[[deps.IntervalArithmetic]] -deps = ["CRlibm_jll", "LinearAlgebra", "MacroTools", "RoundingEmulator"] -git-tree-sha1 = "c59c57c36683aa17c563be6edaac888163f35285" -uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" -version = "0.22.18" - - [deps.IntervalArithmetic.extensions] - IntervalArithmeticDiffRulesExt = "DiffRules" - IntervalArithmeticForwardDiffExt = "ForwardDiff" - IntervalArithmeticIntervalSetsExt = "IntervalSets" - IntervalArithmeticRecipesBaseExt = "RecipesBase" - - [deps.IntervalArithmetic.weakdeps] - DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b" - ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" - IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" - RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" - -[[deps.IntervalSets]] -git-tree-sha1 = "dba9ddf07f77f60450fe5d2e2beb9854d9a49bd0" -uuid = "8197267c-284f-5f27-9208-e0e47529a953" -version = "0.7.10" - - [deps.IntervalSets.extensions] - IntervalSetsRandomExt = "Random" - IntervalSetsRecipesBaseExt = "RecipesBase" - IntervalSetsStatisticsExt = "Statistics" - - [deps.IntervalSets.weakdeps] - Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[[deps.InverseFunctions]] -git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb" -uuid = "3587e190-3f89-42d0-90ee-14403ec27112" -version = "0.1.17" -weakdeps = ["Dates", "Test"] - - [deps.InverseFunctions.extensions] - InverseFunctionsDatesExt = "Dates" - InverseFunctionsTestExt = "Test" - -[[deps.IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" - -[[deps.Isoband]] -deps = ["isoband_jll"] -git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137" -uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4" -version = "0.1.1" - -[[deps.IterTools]] -git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" -uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" -version = "1.10.0" - -[[deps.IteratorInterfaceExtensions]] -git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" -uuid = "82899510-4779-5014-852e-03e436cf321d" -version = "1.0.0" - -[[deps.JLLWrappers]] -deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "be3dc50a92e5a386872a493a10050136d4703f9b" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.6.1" - -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.4" - -[[deps.JpegTurbo]] -deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"] -git-tree-sha1 = "fa6d0bcff8583bac20f1ffa708c3913ca605c611" -uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0" -version = "0.1.5" - -[[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.4+0" - -[[deps.KernelDensity]] -deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] -git-tree-sha1 = "7d703202e65efa1369de1279c162b915e245eed1" -uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" -version = "0.6.9" - -[[deps.LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" -uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.2+0" - -[[deps.LERC_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "36bdbc52f13a7d1dcb0f3cd694e01677a515655b" -uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "4.0.0+0" - -[[deps.LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "78211fb6cbc872f77cad3fc0b6cf647d923f4929" -uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "18.1.7+0" - -[[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.2+1" - -[[deps.LaTeXStrings]] -git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.4.0" - -[[deps.LazyArtifacts]] -deps = ["Artifacts", "Pkg"] -uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" -version = "1.11.0" - -[[deps.LazyModules]] -git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e" -uuid = "8cdb02fc-e678-4876-92c5-9defec4f444e" -version = "0.3.1" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.6.0+0" - -[[deps.LibGit2]] -deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" -version = "1.11.0" - -[[deps.LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.7.2+0" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" -version = "1.11.0" - -[[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" - -[[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] -git-tree-sha1 = "8be878062e0ffa2c3f67bb58a595375eda5de80b" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.11.0+0" - -[[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.6.0+0" - -[[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "c6ce1e19f3aec9b59186bdf06cdf3c4fc5f5f3e6" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.50.0+0" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+1" - -[[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.40.1+0" - -[[deps.Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a" -uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.7.0+0" - -[[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.40.1+0" - -[[deps.LightXML]] -deps = ["Libdl", "XML2_jll"] -git-tree-sha1 = "3a994404d3f6709610701c7dabfc03fed87a81f8" -uuid = "9c8b4983-aa76-5018-a973-4c85ecc9e179" -version = "0.9.1" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -version = "1.11.0" - -[[deps.LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.28" - - [deps.LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [deps.LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" -version = "1.11.0" - -[[deps.MKL_jll]] -deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] -git-tree-sha1 = "f046ccd0c6db2832a9f639e2c669c6fe867e5f4f" -uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" -version = "2024.2.0+0" - -[[deps.MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.13" - -[[deps.Makie]] -deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "InverseFunctions", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"] -git-tree-sha1 = "f7907907eb914138cc9e9ee66ab46f7a9efac8e8" -uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" -version = "0.21.15" - -[[deps.MakieCore]] -deps = ["ColorTypes", "GeometryBasics", "IntervalSets", "Observables"] -git-tree-sha1 = "4604f03e5b057e8e62a95a44929cafc9585b0fe9" -uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" -version = "0.8.9" - -[[deps.MappedArrays]] -git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" -uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" -version = "0.4.2" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" -version = "1.11.0" - -[[deps.MathTeXEngine]] -deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] -git-tree-sha1 = "f45c8916e8385976e1ccd055c9874560c257ab13" -uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" -version = "0.6.2" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.6+0" - -[[deps.Missings]] -deps = ["DataAPI"] -git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.2.0" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" -version = "1.11.0" - -[[deps.MosaicViews]] -deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] -git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe" -uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389" -version = "0.3.4" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.12.12" - -[[deps.NaNMath]] -deps = ["OpenLibm_jll"] -git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" -uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.2" - -[[deps.NearestNeighbors]] -deps = ["Distances", "StaticArrays"] -git-tree-sha1 = "3cebfc94a0754cc329ebc3bab1e6c89621e791ad" -uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" -version = "0.4.20" - -[[deps.Netpbm]] -deps = ["FileIO", "ImageCore", "ImageMetadata"] -git-tree-sha1 = "d92b107dbb887293622df7697a2223f9f8176fcd" -uuid = "f09324ee-3d7c-5217-9330-fc30815ba969" -version = "1.1.1" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[deps.Observables]] -git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" -uuid = "510215fc-4207-5dde-b226-833fc4488ee2" -version = "0.5.5" - -[[deps.OffsetArrays]] -git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" -uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.1" -weakdeps = ["Adapt"] - - [deps.OffsetArrays.extensions] - OffsetArraysAdaptExt = "Adapt" - -[[deps.Ogg_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" -uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" -version = "1.3.5+1" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.27+1" - -[[deps.OpenEXR]] -deps = ["Colors", "FileIO", "OpenEXR_jll"] -git-tree-sha1 = "327f53360fdb54df7ecd01e96ef1983536d1e633" -uuid = "52e1d378-f018-4a11-a4be-720524705ac7" -version = "0.3.2" - -[[deps.OpenEXR_jll]] -deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "8292dd5c8a38257111ada2174000a33745b06d4e" -uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" -version = "3.2.4+0" - -[[deps.OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+1" - -[[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" - -[[deps.Opus_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6703a85cb3781bd5909d48730a67205f3f31a575" -uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.3.3+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.3" - -[[deps.PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+1" - -[[deps.PDMats]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "949347156c25054de2db3b166c52ac4728cbad65" -uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.31" - -[[deps.PNGFiles]] -deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"] -git-tree-sha1 = "67186a2bc9a90f9f85ff3cc8277868961fb57cbd" -uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" -version = "0.4.3" - -[[deps.Packing]] -deps = ["GeometryBasics"] -git-tree-sha1 = "ec3edfe723df33528e085e632414499f26650501" -uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566" -version = "0.5.0" - -[[deps.PaddedViews]] -deps = ["OffsetArrays"] -git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f" -uuid = "5432bcbf-9aad-5242-b902-cca2824c8663" -version = "0.5.12" - -[[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.Parsers]] -deps = ["Dates", "PrecompileTools", "UUIDs"] -git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.8.1" - -[[deps.Pixman_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "35621f10a7531bc8fa58f74610b1bfb70a3cfc6b" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.43.4+0" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.11.0" -weakdeps = ["REPL"] - - [deps.Pkg.extensions] - REPLExt = "REPL" - -[[deps.PkgVersion]] -deps = ["Pkg"] -git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da" -uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" -version = "0.3.3" - -[[deps.PlotUtils]] -deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "StableRNGs", "Statistics"] -git-tree-sha1 = "3ca9a356cd2e113c420f2c13bea19f8d3fb1cb18" -uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.4.3" - -[[deps.PolygonOps]] -git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" -uuid = "647866c9-e3ac-4575-94e7-e3d426903924" -version = "0.1.2" - -[[deps.PrecompileTools]] -deps = ["Preferences"] -git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" -uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.1" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.3" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" -version = "1.11.0" - -[[deps.ProgressMeter]] -deps = ["Distributed", "Printf"] -git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4" -uuid = "92933f4c-e287-5a05-a399-4b506db050ca" -version = "1.10.2" - -[[deps.PtrArrays]] -git-tree-sha1 = "77a42d78b6a92df47ab37e177b2deac405e1c88f" -uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" -version = "1.2.1" - -[[deps.QOI]] -deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] -git-tree-sha1 = "18e8f4d1426e965c7b532ddd260599e1510d26ce" -uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" -version = "1.0.0" - -[[deps.QuadGK]] -deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "cda3b045cf9ef07a08ad46731f5a3165e56cf3da" -uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.11.1" - - [deps.QuadGK.extensions] - QuadGKEnzymeExt = "Enzyme" - - [deps.QuadGK.weakdeps] - Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" -version = "1.11.0" - -[[deps.Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -version = "1.11.0" - -[[deps.RangeArrays]] -git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" -uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d" -version = "0.3.2" - -[[deps.Ratios]] -deps = ["Requires"] -git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b" -uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" -version = "0.4.5" -weakdeps = ["FixedPointNumbers"] - - [deps.Ratios.extensions] - RatiosFixedPointNumbersExt = "FixedPointNumbers" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.RelocatableFolders]] -deps = ["SHA", "Scratch"] -git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" -uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "1.0.1" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[deps.Rmath]] -deps = ["Random", "Rmath_jll"] -git-tree-sha1 = "852bd0f55565a9e973fcfee83a84413270224dc4" -uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" -version = "0.8.0" - -[[deps.Rmath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8" -uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.5.1+0" - -[[deps.RoundingEmulator]] -git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" -uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" -version = "0.2.1" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[deps.SIMD]] -deps = ["PrecompileTools"] -git-tree-sha1 = "98ca7c29edd6fc79cd74c61accb7010a4e7aee33" -uuid = "fdea26ae-647d-5447-a871-4b548cad5224" -version = "3.6.0" - -[[deps.Scratch]] -deps = ["Dates"] -git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" -uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.2.1" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" -version = "1.11.0" - -[[deps.ShaderAbstractions]] -deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] -git-tree-sha1 = "79123bc60c5507f035e6d1d9e563bb2971954ec8" -uuid = "65257c39-d410-5151-9873-9b3e5be5013e" -version = "0.4.1" - -[[deps.SharedArrays]] -deps = ["Distributed", "Mmap", "Random", "Serialization"] -uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" -version = "1.11.0" - -[[deps.Showoff]] -deps = ["Dates", "Grisu"] -git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" -uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" -version = "1.0.3" - -[[deps.SignedDistanceFields]] -deps = ["Random", "Statistics", "Test"] -git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" -uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" -version = "0.4.0" - -[[deps.SimpleTraits]] -deps = ["InteractiveUtils", "MacroTools"] -git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" -uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" -version = "0.9.4" - -[[deps.Sixel]] -deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"] -git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6" -uuid = "45858cf5-a6b0-47a3-bbea-62219f50df47" -version = "0.1.3" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" -version = "1.11.0" - -[[deps.SortingAlgorithms]] -deps = ["DataStructures"] -git-tree-sha1 = "66e0a8e672a0bdfca2c3f5937efb8538b9ddc085" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.2.1" - -[[deps.SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.11.0" - -[[deps.SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.4.0" -weakdeps = ["ChainRulesCore"] - - [deps.SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - -[[deps.StableRNGs]] -deps = ["Random"] -git-tree-sha1 = "83e6cce8324d49dfaf9ef059227f91ed4441a8e5" -uuid = "860ef19b-820b-49d6-a774-d7a799459cd3" -version = "1.0.2" - -[[deps.StackViews]] -deps = ["OffsetArrays"] -git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" -uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15" -version = "0.1.1" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "777657803913ffc7e8cc20f0fd04b634f871af8f" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.8" -weakdeps = ["ChainRulesCore", "Statistics"] - - [deps.StaticArrays.extensions] - StaticArraysChainRulesCoreExt = "ChainRulesCore" - StaticArraysStatisticsExt = "Statistics" - -[[deps.StaticArraysCore]] -git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.3" - -[[deps.Statistics]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "ae3bb1eb3bba077cd276bc5cfc337cc65c3075c0" -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.11.1" -weakdeps = ["SparseArrays"] - - [deps.Statistics.extensions] - SparseArraysExt = ["SparseArrays"] - -[[deps.StatsAPI]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" -uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.7.0" - -[[deps.StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" -uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.3" - -[[deps.StatsFuns]] -deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "b423576adc27097764a90e163157bcfc9acf0f46" -uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.3.2" -weakdeps = ["ChainRulesCore", "InverseFunctions"] - - [deps.StatsFuns.extensions] - StatsFunsChainRulesCoreExt = "ChainRulesCore" - StatsFunsInverseFunctionsExt = "InverseFunctions" - -[[deps.StructArrays]] -deps = ["ConstructionBase", "DataAPI", "Tables"] -git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be" -uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" -version = "0.6.18" - - [deps.StructArrays.extensions] - StructArraysAdaptExt = "Adapt" - StructArraysGPUArraysCoreExt = "GPUArraysCore" - StructArraysSparseArraysExt = "SparseArrays" - StructArraysStaticArraysExt = "StaticArrays" - - [deps.StructArrays.weakdeps] - Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" - GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - -[[deps.StyledStrings]] -uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" -version = "1.11.0" - -[[deps.SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" - -[[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.7.0+0" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[deps.TableTraits]] -deps = ["IteratorInterfaceExtensions"] -git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" -uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" -version = "1.0.1" - -[[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "598cd7c1f68d1e205689b1c2fe65a9f85846f297" -uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.12.0" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[deps.TensorCore]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" -uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" -version = "0.1.1" - -[[deps.Tensors]] -deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"] -git-tree-sha1 = "957f256fb380cad64cae4da39e562ecfb5c3fec9" -uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4" -version = "1.16.1" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" -version = "1.11.0" - -[[deps.TiffImages]] -deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"] -git-tree-sha1 = "6ee0c220d0aecad18792c277ae358129cc50a475" -uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" -version = "0.11.0" - -[[deps.TranscodingStreams]] -git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" -uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.11.3" - -[[deps.TriplotBase]] -git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" -uuid = "981d1d27-644d-49a2-9326-4793e63143c3" -version = "0.1.0" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -version = "1.11.0" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" -version = "1.11.0" - -[[deps.UnicodeFun]] -deps = ["REPL"] -git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" -uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" -version = "0.4.1" - -[[deps.Unitful]] -deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd" -uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.21.0" -weakdeps = ["ConstructionBase", "InverseFunctions"] - - [deps.Unitful.extensions] - ConstructionBaseUnitfulExt = "ConstructionBase" - InverseFunctionsUnitfulExt = "InverseFunctions" - -[[deps.VTKBase]] -git-tree-sha1 = "c2d0db3ef09f1942d08ea455a9e252594be5f3b6" -uuid = "4004b06d-e244-455f-a6ce-a5f9919cc534" -version = "1.0.1" - -[[deps.WebP]] -deps = ["CEnum", "ColorTypes", "FileIO", "FixedPointNumbers", "ImageCore", "libwebp_jll"] -git-tree-sha1 = "f1f6d497ff84039deeb37f264396dac0c2250497" -uuid = "e3aaa7dc-3e4b-44e0-be63-ffb868ccd7c1" -version = "0.1.2" - -[[deps.WoodburyMatrices]] -deps = ["LinearAlgebra", "SparseArrays"] -git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511" -uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6" -version = "1.0.0" - -[[deps.WriteVTK]] -deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] -git-tree-sha1 = "1d8042d58334ab7947ce505709df7009da6f3375" -uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192" -version = "1.21.1" - -[[deps.XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "6a451c6f33a176150f315726eba8b92fbfdb9ae7" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.4+0" - -[[deps.XSLT_jll]] -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.41+0" - -[[deps.XZ_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "15e637a697345f6743674f1322beefbc5dcd5cfc" -uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.6.3+0" - -[[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+0" - -[[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.11+0" - -[[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.4+0" - -[[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.6+0" - -[[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.11+0" - -[[deps.Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.1+0" - -[[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.0+0" - -[[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.0+0" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+1" - -[[deps.Zstd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b" -uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.6+1" - -[[deps.isoband_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" -uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" -version = "0.2.3+0" - -[[deps.libaom_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" -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", "Zlib_jll"] -git-tree-sha1 = "e17c115d55c5fbb7e52ebedb427a0dca79d4484e" -uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.15.2+0" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.11.0+0" - -[[deps.libfdk_aac_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8a22cf860a7d27e4f3498a0fe0811a7957badb38" -uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "2.0.3+0" - -[[deps.libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.44+0" - -[[deps.libsixel_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] -git-tree-sha1 = "7dfa0fd9c783d3d0cc43ea1af53d69ba45c447df" -uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" -version = "1.10.3+1" - -[[deps.libvorbis_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" -uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.7+2" - -[[deps.libwebp_jll]] -deps = ["Artifacts", "Giflib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Libtiff_jll", "libpng_jll"] -git-tree-sha1 = "ccbb625a89ec6195856a50aa2b668a5c08712c94" -uuid = "c5f90fcd-3b7e-5836-afba-fc50a0988cb2" -version = "1.4.0+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.59.0+0" - -[[deps.oneTBB_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7d0ea0f4895ef2f5cb83645fa689e52cb55cf493" -uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" -version = "2021.12.0+0" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+2" - -[[deps.x264_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "35976a1216d6c066ea32cba2150c4fa682b276fc" -uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" -version = "10164.0.0+0" - -[[deps.x265_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dcc541bb19ed5b0ede95581fb2e41ecf179527d2" -uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" -version = "3.6.0+0" diff --git a/visualization/Project.toml b/visualization/Project.toml deleted file mode 100644 index e101770250..0000000000 --- a/visualization/Project.toml +++ /dev/null @@ -1,4 +0,0 @@ -[deps] -CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" -Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" -ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" diff --git a/visualization/reference_cell_ip.jl b/visualization/reference_cell_ip.jl deleted file mode 100644 index d60e354674..0000000000 --- a/visualization/reference_cell_ip.jl +++ /dev/null @@ -1,85 +0,0 @@ -using Ferrite -import FerriteViz as Viz -import GLMakie as Plt - - -function refshape_shapevalues(ip::VectorInterpolation{3, RefHexahedron}, shape_nr; npoints = 5) - x, y, z, u, v, w = (zeros(npoints^3) for _ in 1:6) - idx = 1 - for i in 1:npoints - for j in 1:npoints - for k in 1:npoints - x[idx], y[idx], z[idx] = (i-1, j-1, k-1) .* (2/(npoints-1)) .- 1 - u[idx], v[idx], w[idx] = shape_value(ip, Vec((x[idx], y[idx], z[idx])), shape_nr) - idx += 1 - end - end - end - return x, y, z, u, v, w -end - -refcell(::Type{RefHexahedron}) = Hexahedron(ntuple(i->i, 8)) -refcell(::Type{RefTetrahedron}) = Tetrahedron(ntuple(i->i, 4)) -refip(::Type{RefShape}) where {RefShape <: Ferrite.AbstractRefShape} = Lagrange{RefShape,1}() - - -function plot_refcell(::Type{RefShape}; kwargs...) where {RefShape <: Ferrite.AbstractRefShape{3}} - fig = Plt.Figure() - ax = Plt.Axis3(fig[1,1]; xlabel="ξ₁", ylabel="ξ₂", zlabel="ξ₃") - plot_refcell!(ax, RefShape; kwargs...) - return fig, ax -end - -function plot_refcell!(ax, ::Type{RefShape}; vertices=true, edges=true, faces=true) where {RefShape <: Ferrite.AbstractRefShape{3}} - plot_vertices!(ax, RefShape; label=vertices) - plot_edges!(ax, RefShape; label=edges) - plot_faces!(ax, RefShape; label=faces) - return ax -end - -function plot_vertices!(ax, ::Type{RefShape}; label) where {RefShape <: Ferrite.AbstractRefShape{3}} - ξ = Ferrite.reference_coordinates(refip(RefShape)) - ξ1, ξ2, ξ3 = (getindex.(ξ, i) for i in 1:3) - Plt.scatter!(ax, ξ1, ξ2, ξ3) - if label - Plt.text!(ax, ξ1, ξ2, ξ3; text=[string(i) for i in 1:length(ξ)]) - end - return ax -end - -function plot_edges!(ax, ::Type{RefShape}; label) where {RefShape <: Ferrite.AbstractRefShape{3}} - arrowheadpos = 2/3 - cell = refcell(RefShape) - ξ = Ferrite.reference_coordinates(refip(RefShape)) - x, y, z, u, v, w = (zeros(length(Ferrite.edges(cell))) for _ in 1:6) - for (k, e) in enumerate(Ferrite.edges(cell)) - ξa, ξb = getindex.((ξ,), e) - Plt.lines!(ax, ([ξa[i], ξb[i]] for i in 1:3)...) - x[k], y[k], z[k] = ξa - u[k], v[k], w[k] = ξb - ξa - end - Plt.arrows!(ax, x, y, z, u * arrowheadpos, v * arrowheadpos, w * arrowheadpos; linewidth=0.05, arrowsize=0.1) - if label - s = arrowheadpos + (1-arrowheadpos)/6 - Plt.text!(ax, x + u*s, y + v*s, z + w*s; text=[string(i) for i in 1:length(x)]) - end -end - -plot_faces!(ax, RefShape; label) = nothing - -function testit(nshape=1) - ip = Nedelec{3,RefHexahedron,1}(); - - fig, ax = plot_refcell(RefHexahedron; vertices=true, edges=true); - vals = refshape_shapevalues(ip, nshape) - lengths = sqrt.(vals[4].^2 + vals[5].^2 + vals[6].^2) - Plt.arrows!(ax, vals...; lengthscale=0.1, arrowsize=0.1, color=lengths) - return fig -end - -# Possible tests -#= -1) Check shape_value(ip, ξ, i) ⋅ v_edge[i] = |shape_value(ip, ξ, i)| (checks alignment) -2) Check ∫ Ni ⋅ v dL = 1 on each edge -3) Check shape_value(ip, ξ, i) ⋅ v_edge[j] = 0 for i≠j -=# From c26cad639a328f5abd09f6197622a272687a8c47 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 12 Nov 2024 16:23:11 +0100 Subject: [PATCH 138/172] Fix link and imports --- docs/src/literate-tutorials/heat_equation_hdiv.jl | 2 +- src/Ferrite.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_hdiv.jl b/docs/src/literate-tutorials/heat_equation_hdiv.jl index e3f2bee293..7fceeb6ad2 100644 --- a/docs/src/literate-tutorials/heat_equation_hdiv.jl +++ b/docs/src/literate-tutorials/heat_equation_hdiv.jl @@ -220,7 +220,7 @@ end println("Outward flux: ", calculate_flux(dh, Γ, ipq, u)) -# Note that this is not the case for the standard [Heat equation](@id tutorial-heat-equation), +# Note that this is not the case for the standard [Heat equation](@ref tutorial-heat-equation), # as the flux terms are less accurately approximated. A fine mesh is required to converge in that case. # However, the present example gives a worse approximation of the temperature field. diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 7b358463be..f30ea21f84 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -21,7 +21,7 @@ using WriteVTK: WriteVTK, VTKCellTypes using Tensors: Tensors, AbstractTensor, SecondOrderTensor, SymmetricTensor, Tensor, Vec, gradient, - rotation_tensor, symmetric, tovoigt!, hessian, otimesu + rotation_tensor, symmetric, tovoigt!, hessian, otimesu, otimesl using ForwardDiff: ForwardDiff From e0935ddd3111752076826ad1052497d5c812540b Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 13 Nov 2024 17:57:09 +0100 Subject: [PATCH 139/172] Add nonworking maxwell eigenvalues example --- docs/Manifest.toml | 30 ++++++++- docs/Project.toml | 2 + docs/make.jl | 1 + docs/src/literate-tutorials/maxwell.jl | 91 ++++++++++++++++++++++++++ src/interpolations.jl | 7 +- 5 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 docs/src/literate-tutorials/maxwell.jl diff --git a/docs/Manifest.toml b/docs/Manifest.toml index d627674895..b19c814d6a 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -2,7 +2,7 @@ julia_version = "1.11.1" manifest_format = "2.0" -project_hash = "4e52f4aa4cee9f66ec4f633f0ae538fbd227ac5e" +project_hash = "c8cd4f9010376e633064839ca68de8e4b622021f" [[deps.ADTypes]] git-tree-sha1 = "eea5d80188827b35333801ef97a40c2ed653b081" @@ -69,6 +69,18 @@ git-tree-sha1 = "d57bd3762d308bded22c3b82d033bff85f6195c6" uuid = "ec485272-7323-5ecc-a04f-4719b315124d" version = "0.4.0" +[[deps.Arpack]] +deps = ["Arpack_jll", "Libdl", "LinearAlgebra", "Logging"] +git-tree-sha1 = "9b9b347613394885fd1c8c7729bfc60528faa436" +uuid = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97" +version = "0.5.4" + +[[deps.Arpack_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS_jll", "Pkg"] +git-tree-sha1 = "5ba6c757e8feccf03a1554dfaf3e26b3cfc7fd5e" +uuid = "68821587-b530-5797-8361-c406ea357684" +version = "3.5.1+1" + [[deps.ArrayInterface]] deps = ["Adapt", "LinearAlgebra"] git-tree-sha1 = "3640d077b6dafd64ceb8fd5c1ec76f7ca53bcf76" @@ -931,6 +943,16 @@ git-tree-sha1 = "267dad6b4b7b5d529c76d40ff48d33f7e94cb834" uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" version = "0.9.6" +[[deps.KrylovKit]] +deps = ["GPUArraysCore", "LinearAlgebra", "PackageExtensionCompat", "Printf", "Random", "VectorInterface"] +git-tree-sha1 = "3ba86a12066c19a6ed0bc963984bcf89d495133e" +uuid = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" +version = "0.8.2" +weakdeps = ["ChainRulesCore"] + + [deps.KrylovKit.extensions] + KrylovKitChainRulesCoreExt = "ChainRulesCore" + [[deps.LAME_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "170b660facf5df5de098d866564877e119141cbd" @@ -2325,6 +2347,12 @@ git-tree-sha1 = "c2d0db3ef09f1942d08ea455a9e252594be5f3b6" uuid = "4004b06d-e244-455f-a6ce-a5f9919cc534" version = "1.0.1" +[[deps.VectorInterface]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "cea8abaa6e43f72f97a09cf95b80c9eb53ff75cf" +uuid = "409d34a3-91d5-4945-b6ec-7529ddf182d8" +version = "0.4.9" + [[deps.VectorizationBase]] deps = ["ArrayInterface", "CPUSummary", "HostCPUFeatures", "IfElse", "LayoutPointers", "Libdl", "LinearAlgebra", "SIMDTypes", "Static", "StaticArrayInterface"] git-tree-sha1 = "e7f5b81c65eb858bed630fe006837b935518aca5" diff --git a/docs/Project.toml b/docs/Project.toml index ead8045637..d21f86c895 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,5 @@ [deps] +Arpack = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97" BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" Changelog = "5217a498-cd5d-4ec6-b8c2-9b85a09b6e3e" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" @@ -9,6 +10,7 @@ FerriteMeshParser = "0f8c756f-80dd-4a75-85c6-b0a5ab9d4620" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" +KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" diff --git a/docs/make.jl b/docs/make.jl index 1d5a7b6a10..ed61182d43 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -68,6 +68,7 @@ bibtex_plugin = CitationBibliography( "tutorials/reactive_surface.md", "tutorials/linear_shell.md", "tutorials/dg_heat_equation.md", + "tutorials/maxwell.md", ], "Topic guides" => [ "Topic guide overview" => "topics/index.md", diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl new file mode 100644 index 0000000000..a68967cb16 --- /dev/null +++ b/docs/src/literate-tutorials/maxwell.jl @@ -0,0 +1,91 @@ +#= +# Solving Maxwell's equations +```math +\begin{align*} +\nabla \cdot \boldsymbol{D} &= \rho_\mathrm{f} \\ +\nabla \times \boldsymbol{H} &= \boldsymbol{J}_\mathrm{f} + \frac{\partial \boldsymbol{D}}{\partial t} \\ +\nabla \cdot \boldsymbol{B} &= 0 \\ +\nabla \times \boldsymbol{E} &= -\frac{\partial \boldsymbol{B}}{\partial t} +\end{align*} +``` +=# + +#= +# Maxwell eigenvalue problem +Strong form +```math +\begin{align*} +\mathrm{curl}(\mathrm{curl}(\boldsymbol{u})) &= \lambda \boldsymbol{u} \text{ in } \Omega \\ +\boldsymbol{u} \times \boldsymbol{n} &= \boldsymbol{0} \text{ on } \partial\Omega +\end{align*} +``` +Weak form +```math +\int_\Omega \mathrm{curl}(\boldsymbol{\delta u}) \cdot \mathrm{curl}(\boldsymbol{u})\ \mathrm{d}\Omega = \lambda \int_\Omega \boldsymbol{\delta u} \cdot \boldsymbol{u}\ \mathrm{d}\Omega \quad \forall\ \boldsymbol{\delta u} \in H_0(\text{curl}) +``` +Finite element formulation +```math +\underbrace{\int_\Omega \mathrm{curl}(\boldsymbol{\delta N}_i) \cdot \mathrm{curl}(\boldsymbol{N_j})\ \mathrm{d}\Omega}_{A_{ij}}\ x_j = \lambda \underbrace{\int_\Omega \boldsymbol{\delta N_i} \cdot \boldsymbol{N_j}\ \mathrm{d}\Omega}_{B_{ij}}\ x_j +``` +=# + +# ## Implementation +using Ferrite, Tensors, KrylovKit +using Arpack: Arpack +# ### Element routine +function element_routine!(Ae, Be, cv::CellValues) + for q_point in 1:getnquadpoints(cv) + dΩ = getdetJdV(cv, q_point) + for i in 1:getnbasefunctions(cv) + δN = shape_value(cv, q_point, i) + curl_δN = shape_curl(cv, q_point, i) + for j in 1:getnbasefunctions(cv) + N = shape_value(cv, q_point, j) + curl_N = shape_curl(cv, q_point, j) + Ae[i, j] = (curl_δN ⋅ curl_N) * dΩ + Be[i, j] = (δN ⋅ N) * dΩ + end + end + end + return +end + +# ### FE setup +function doassemble!(A, B, dh, cv) + n = ndofs_per_cell(dh) + Ae = zeros(n, n) + Be = zeros(n, n) + a_assem, b_assem = start_assemble.((A, B)) + for cc in CellIterator(dh) + cell = getcells(dh.grid, cellid(cc)) + reinit!(cv, cell, getcoordinates(cc)) + element_routine!(Ae, Be, cv) + assemble!(a_assem, celldofs(cc), Ae) + assemble!(b_assem, celldofs(cc), Be) + end + return A, B +end + +function setup_and_assemble(ip::VectorInterpolation{2, RefTriangle}) + grid = generate_grid(Triangle, (10, 10)) + cv = CellValues(QuadratureRule{RefTriangle}(2), ip, geometric_interpolation(Triangle)) + dh = close!(add!(DofHandler(grid), :u, ip)) + ∂Ω = union((getfacetset(grid, k) for k in ("left", "top", "right", "bottom"))...) + dbc = Dirichlet(:u, ∂Ω, ip isa VectorizedInterpolation ? Returns([0.0, 0.0]) : Returns(0.0)) + ch = close!(add!(ConstraintHandler(dh), dbc)) + sp = init_sparsity_pattern(dh) + add_sparsity_entries!(sp, dh) + A = allocate_matrix(sp) + B = allocate_matrix(sp) + doassemble!(A, B, dh, cv) + Ferrite.zero_out_rows!(B, ch.dofmapping) + Ferrite.zero_out_columns!(B, ch.prescribed_dofs) + return A, B, dh +end + +ip = Nedelec{2, RefTriangle, 1}() + +A, B, dh = setup_and_assemble(ip) + +vals, vecs, info = geneigsolve((A, B), 1, EigSorter(x -> abs(x - 5.0)); maxiter = 1000); +# λ, ϕ = Arpack.eigs(A, B, nev = 2, sigma=5.5); diff --git a/src/interpolations.jl b/src/interpolations.jl index 1dc0876c1e..f9a4a2ba6c 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1793,6 +1793,8 @@ mapping_type(::VectorizedInterpolation) = IdentityMapping() struct RaviartThomas{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end mapping_type(::RaviartThomas) = ContravariantPiolaMapping() n_dbc_components(::RaviartThomas) = 1 +dirichlet_edgedof_indices(ip::RaviartThomas{2}) = edgedof_interior_indices(ip) +dirichlet_facedof_indices(ip::RaviartThomas{3}) = facedof_interior_indices(ip) # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-1.html @@ -1807,6 +1809,7 @@ end getnbasefunctions(::RaviartThomas{2, RefTriangle, 1}) = 3 edgedof_interior_indices(::RaviartThomas{2, RefTriangle, 1}) = ((1,), (2,), (3,)) + adjust_dofs_during_distribution(::RaviartThomas) = false function get_direction(::RaviartThomas{2, RefTriangle, 1}, j, cell) @@ -1872,7 +1875,7 @@ end struct BrezziDouglasMarini{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end mapping_type(::BrezziDouglasMarini) = ContravariantPiolaMapping() reference_coordinates(ip::BrezziDouglasMarini{vdim}) where {vdim} = fill(NaN * zero(Vec{vdim}), getnbasefunctions(ip)) -#dirichlet_facedof_indices(ip::BrezziDouglasMarini) = facetdof_interior_indices(ip) +dirichlet_edgedof_indices(ip::BrezziDouglasMarini{2}) = edgedof_interior_indices(ip) n_dbc_components(::BrezziDouglasMarini) = 1 #= ----------------+-------------------- @@ -1923,7 +1926,7 @@ end struct Nedelec{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end mapping_type(::Nedelec) = CovariantPiolaMapping() reference_coordinates(ip::Nedelec{vdim}) where {vdim} = fill(NaN * zero(Vec{vdim}), getnbasefunctions(ip)) -dirichlet_facedof_indices(ip::Nedelec) = facetdof_interior_indices(ip) +dirichlet_edgedof_indices(ip::Nedelec) = edgedof_interior_indices(ip) n_dbc_components(::Nedelec) = 1 # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html From f0a8d1bcfe84d2834f97ff3090c445a90da458e9 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 13 Nov 2024 17:57:32 +0100 Subject: [PATCH 140/172] Reduce elements in hdiv example --- docs/src/literate-tutorials/heat_equation_hdiv.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/literate-tutorials/heat_equation_hdiv.jl b/docs/src/literate-tutorials/heat_equation_hdiv.jl index 7fceeb6ad2..81ce3203b0 100644 --- a/docs/src/literate-tutorials/heat_equation_hdiv.jl +++ b/docs/src/literate-tutorials/heat_equation_hdiv.jl @@ -69,7 +69,7 @@ function create_grid(ny::Int) return grid end -grid = create_grid(100) +grid = create_grid(10) # ### Setup # We define one `CellValues` for each field which share the same quadrature rule. From 5140c9822fbedb8238d985a985477dac332d885b Mon Sep 17 00:00:00 2001 From: Knut Andreas Meyer Date: Wed, 13 Nov 2024 18:08:14 +0100 Subject: [PATCH 141/172] Disable failing Maxwell eigenvalue solve --- docs/src/literate-tutorials/maxwell.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl index a68967cb16..b6a459b3d3 100644 --- a/docs/src/literate-tutorials/maxwell.jl +++ b/docs/src/literate-tutorials/maxwell.jl @@ -87,5 +87,5 @@ ip = Nedelec{2, RefTriangle, 1}() A, B, dh = setup_and_assemble(ip) -vals, vecs, info = geneigsolve((A, B), 1, EigSorter(x -> abs(x - 5.0)); maxiter = 1000); +# vals, vecs, info = geneigsolve((A, B), 1, EigSorter(x -> abs(x - 5.0)); maxiter = 1000); # λ, ϕ = Arpack.eigs(A, B, nev = 2, sigma=5.5); From 7911cbdedeba762ad5e10a6b94be1d92a78ec0f4 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Thu, 14 Nov 2024 19:12:45 +0100 Subject: [PATCH 142/172] Add test for correct BC indices, non-homogeneous not yet working --- src/interpolations.jl | 2 +- test/test_interpolations.jl | 54 +++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/interpolations.jl b/src/interpolations.jl index f9a4a2ba6c..eee217678f 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1793,6 +1793,7 @@ mapping_type(::VectorizedInterpolation) = IdentityMapping() struct RaviartThomas{vdim, shape, order} <: VectorInterpolation{vdim, shape, order} end mapping_type(::RaviartThomas) = ContravariantPiolaMapping() n_dbc_components(::RaviartThomas) = 1 +reference_coordinates(ip::RaviartThomas{vdim}) where {vdim} = fill(NaN * zero(Vec{vdim}), getnbasefunctions(ip)) dirichlet_edgedof_indices(ip::RaviartThomas{2}) = edgedof_interior_indices(ip) dirichlet_facedof_indices(ip::RaviartThomas{3}) = facedof_interior_indices(ip) @@ -1809,7 +1810,6 @@ end getnbasefunctions(::RaviartThomas{2, RefTriangle, 1}) = 3 edgedof_interior_indices(::RaviartThomas{2, RefTriangle, 1}) = ((1,), (2,), (3,)) - adjust_dofs_during_distribution(::RaviartThomas) = false function get_direction(::RaviartThomas{2, RefTriangle, 1}, j, cell) diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 53f552feb7..35cc50bd21 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,7 +1,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @testset "interpolations" begin - + #= @testset "Value Type $value_type" for value_type in (Float32, Float64) @testset "Correctness of $interpolation" for interpolation in ( Lagrange{RefLine, 1}(), @@ -223,7 +223,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true end - + =# @testset "Correctness of AD of embedded interpolations" begin ip = Lagrange{RefHexahedron, 2}()^3 ξ = rand(Vec{3, Float64}) @@ -425,6 +425,56 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end + @testset "Hcurl and Hdiv BC" begin + hdiv_ips = ( + RaviartThomas{2, RefTriangle, 1}(), + RaviartThomas{2, RefTriangle, 2}(), + Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), + ) + hdiv_check(v, n) = v ⋅ n + + hcurl_ips = ( + Nedelec{2, RefTriangle, 1}(), + Nedelec{2, RefTriangle, 2}(), + ) + function hcurl_check(v, n::Vec{2}) # 3d not supported yet + t = rotate(n, π / 2) + return v ⋅ t + end + for (f, interpolations) in ((hdiv_check, hdiv_ips), (hcurl_check, hcurl_ips)) + for ip in interpolations + @testset "$ip" begin + RefShape = Ferrite.getrefshape(ip) + CT = typeof(reference_cell(RefShape)) + dim = Ferrite.getrefdim(CT) # dim=sdim=vdim + grid = generate_grid(CT, ntuple(Returns(2), dim)) + qr = FacetQuadratureRule{RefShape}(4) + fv = FacetValues(qr, ip, geometric_interpolation(CT)) + dh = close!(add!(DofHandler(grid), :u, ip)) + for bval in (0.0,) # 1.0) # Need to add mapping in _update! for this to work correctly + a = rand(ndofs(dh)) + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:u, getfacetset(grid, "left"), Returns(bval))) + close!(ch) + apply!(a, ch) + test_val = 0.0 + for (cellidx, facetidx) in getfacetset(grid, "left") + reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) + ae = a[celldofs(dh, cellidx)] + val = 0.0 + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + val += f(function_value(fv, q_point, ae), getnormal(fv, q_point)) * dΓ + end + test_val += f === hdiv_check ? val : abs(val) + end + println(bval, ": ", test_val) + @test abs(test_val - 2 * bval) < 1.0e-6 + end + end + end + end + end @testset "Errors for entitydof_indices on VectorizedInterpolations" begin ip = Lagrange{RefQuadrilateral, 2}()^2 From 01a830664418be917ab4ed47b7f0c9c4433a9139 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Thu, 14 Nov 2024 19:16:11 +0100 Subject: [PATCH 143/172] Reenable tests --- test/test_interpolations.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 35cc50bd21..7b5e0eedc1 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,7 +1,6 @@ using Ferrite: reference_shape_value, reference_shape_gradient @testset "interpolations" begin - #= @testset "Value Type $value_type" for value_type in (Float32, Float64) @testset "Correctness of $interpolation" for interpolation in ( Lagrange{RefLine, 1}(), @@ -223,7 +222,6 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true end - =# @testset "Correctness of AD of embedded interpolations" begin ip = Lagrange{RefHexahedron, 2}()^3 ξ = rand(Vec{3, Float64}) From 508eec7b7932c6373c3209f0e40c0815e4559a2f Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Fri, 15 Nov 2024 11:30:21 +0100 Subject: [PATCH 144/172] Improve tutorial formatting --- .../literate-tutorials/heat_equation_hdiv.jl | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_hdiv.jl b/docs/src/literate-tutorials/heat_equation_hdiv.jl index 81ce3203b0..93c4bd3f90 100644 --- a/docs/src/literate-tutorials/heat_equation_hdiv.jl +++ b/docs/src/literate-tutorials/heat_equation_hdiv.jl @@ -18,10 +18,10 @@ # such that # ```math # \begin{align*} -# \boldsymbol{\nabla}\cdot \boldsymbol{q} &= h(\boldsymbol{x}), \quad \forall \boldsymbol{x} \in \Omega \\ -# \boldsymbol{q}(\boldsymbol{x}) &= - k\ \boldsymbol{\nabla} u(\boldsymbol{x}), \quad \forall \boldsymbol{x} \in \Omega \\ -# \boldsymbol{q}(\boldsymbol{x})\cdot \boldsymbol{n}(\boldsymbol{x}) &= q_n, \quad \forall \boldsymbol{x} \in \Gamma_\mathrm{N}\\ -# u(\boldsymbol{x}) &= u_\mathrm{D}, \quad \forall \boldsymbol{x} \in \Gamma_\mathrm{D} +# \boldsymbol{\nabla}\cdot \boldsymbol{q} &= h(\boldsymbol{x}), \quad \text{in } \Omega \\ +# \boldsymbol{q}(\boldsymbol{x}) &= - k\ \boldsymbol{\nabla} u(\boldsymbol{x}), \quad \text{in } \Omega \\ +# \boldsymbol{q}(\boldsymbol{x})\cdot \boldsymbol{n}(\boldsymbol{x}) &= q_n, \quad \text{on } \Gamma_\mathrm{N}\\ +# u(\boldsymbol{x}) &= u_\mathrm{D}, \quad \text{on } \Gamma_\mathrm{D} # \end{align*} # ``` # @@ -30,9 +30,9 @@ # ```math # \begin{align*} # \int_{\Omega} \delta u [\boldsymbol{\nabla} \cdot \boldsymbol{q}]\ \mathrm{d}\Omega &= \int_\Omega \delta u h\ \mathrm{d}\Omega, \quad \forall\ \delta u \in \delta\mathbb{U} \\ -# \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= -\int_\Omega \boldsymbol{\delta q} \cdot [k\ \boldsymbol{\nabla} u]\ \mathrm{d}\Omega \\ +# % \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega &= -\int_\Omega \boldsymbol{\delta q} \cdot [k\ \boldsymbol{\nabla} u]\ \mathrm{d}\Omega \\ # \int_{\Omega} \boldsymbol{\delta q} \cdot \boldsymbol{q}\ \mathrm{d}\Omega - \int_{\Omega} [\boldsymbol{\nabla} \cdot \boldsymbol{\delta q}] k u \ \mathrm{d}\Omega &= -# -\int_\Gamma \boldsymbol{\delta q} \cdot \boldsymbol{n} k\ u\ \mathrm{d}\Omega, \quad \forall\ \boldsymbol{\delta q} \in \delta\mathbb{Q} +# -\int_\Gamma \boldsymbol{\delta q} \cdot \boldsymbol{n} k\ u\ \mathrm{d}\Gamma, \quad \forall\ \boldsymbol{\delta q} \in \delta\mathbb{Q} # \end{align*} # ``` # where we have the function spaces, @@ -47,8 +47,6 @@ # * `DiscontinuousLagrange{RefTriangle, k-1}` for approximating $L^2$ # * `BrezziDouglasMarini{RefTriangle, k}` for approximating $H(\mathrm{div})$ # -# We will also investigate the consequences of using $H^1$ `Lagrange` instead of $H(\mathrm{div})$ interpolations. -# # ## Commented Program # # Now we solve the problem in Ferrite. What follows is a program spliced with comments. @@ -70,6 +68,7 @@ function create_grid(ny::Int) end grid = create_grid(10) +#md nothing # hide # ### Setup # We define one `CellValues` for each field which share the same quadrature rule. @@ -78,16 +77,19 @@ ipu = DiscontinuousLagrange{RefTriangle, 0}() ipq = Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}() qr = QuadratureRule{RefTriangle}(2) cellvalues = (u = CellValues(qr, ipu, ip_geo), q = CellValues(qr, ipq, ip_geo)) +#md nothing # hide # Distribute the degrees of freedom dh = DofHandler(grid) add!(dh, :u, ipu) add!(dh, :q, ipq) -close!(dh); +close!(dh) +#md nothing # hide # In this problem, we have a zero temperature on the boundary, Γ, which is enforced # via zero Neumann boundary conditions. Hence, we don't need a `Constrainthandler`. Γ = union((getfacetset(grid, name) for name in ("left", "right", "bottom", "top"))...) +#md nothing # hide # ### Element implementation @@ -131,7 +133,6 @@ end #md nothing # hide # ### Global assembly - function assemble_global(cellvalues, dh::DofHandler) grid = dh.grid ## Allocate the element stiffness matrix and element force vector @@ -173,7 +174,8 @@ end # ### Solution of the system K, f = assemble_global(cellvalues, dh); -u = K \ f; +u = K \ f +#md nothing # hide # ### Exporting to VTK # Currently, exporting discontinuous interpolations is not supported. @@ -186,6 +188,7 @@ end VTKGridFile("heat_equation_hdiv", dh) do vtk write_cell_data(vtk, u_cells, "temperature") end +#md nothing # hide # ## Postprocess the total flux # We applied a constant unit heat source to the body, and the @@ -219,6 +222,7 @@ function calculate_flux(dh, boundary_facets, ip, a) end println("Outward flux: ", calculate_flux(dh, Γ, ipq, u)) +#md nothing # hide # Note that this is not the case for the standard [Heat equation](@ref tutorial-heat-equation), # as the flux terms are less accurately approximated. A fine mesh is required to converge in that case. From 5ebcccff78b0b35069dbabd6ff0fc14b10f8cd05 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Fri, 15 Nov 2024 16:00:43 +0100 Subject: [PATCH 145/172] Add link to potential test case to check for maxwell problem --- docs/src/literate-tutorials/maxwell.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl index b6a459b3d3..a959416094 100644 --- a/docs/src/literate-tutorials/maxwell.jl +++ b/docs/src/literate-tutorials/maxwell.jl @@ -11,6 +11,9 @@ =# #= +**Not working:** Maybe try the standard laplace eigenvalue problem +https://www.cambridge.org/core/services/aop-cambridge-core/content/view/4BD87CC520C7E11CF402981AA58D77E2/S0962492910000012a.pdf/finite-element-approximation-of-eigenvalue-problems.pdf + # Maxwell eigenvalue problem Strong form ```math From 5c184503bc80babb5e05a91525f4ce61357a4b21 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 20 Nov 2024 17:43:49 +0100 Subject: [PATCH 146/172] WIP --- .../literate-tutorials/heat_equation_hdiv.jl | 21 +++++++- src/Dofs/ConstraintHandler.jl | 17 +++++-- test/test_interpolations.jl | 48 ++++++++++--------- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_hdiv.jl b/docs/src/literate-tutorials/heat_equation_hdiv.jl index 93c4bd3f90..6326c27b2d 100644 --- a/docs/src/literate-tutorials/heat_equation_hdiv.jl +++ b/docs/src/literate-tutorials/heat_equation_hdiv.jl @@ -5,7 +5,7 @@ # are primary variables. From a theoretical standpoint, there are many details on e.g. which combinations # of interpolations that are stable. See e.g. [Gatica2014](@cite) and [Boffi2013](@cite) for further reading. # This tutorial is based on the theory in -# [Fenics' mixed poisson example](https://fenicsproject.org/olddocs/dolfin/1.4.0/python/demo/documented/mixed-poisson/python/documentation.html). +# [FEniCSx' mixed poisson example](https://docs.fenicsproject.org/dolfinx/v0.9.0/python/demos/demo_mixed-poisson.html). # # ![Temperature solution](https://raw.githubusercontent.com/Ferrite-FEM/Ferrite.jl/refs/heads/gh-pages/assets/heat_equation_hdiv.png) # **Figure:** Temperature distribution considering a central part with lower heat conductivity. @@ -46,6 +46,25 @@ # A stable choice of finite element spaces for this problem on grid with triangles is using # * `DiscontinuousLagrange{RefTriangle, k-1}` for approximating $L^2$ # * `BrezziDouglasMarini{RefTriangle, k}` for approximating $H(\mathrm{div})$ +#= +# Dirichlet BC theory for hdiv interpolations. +For a field representing a flux, we in general set the boundary condition on the normal component of this +flux. Consider the field $\boldsymbol{q}(\boldsymbol{x})$, then we want to prescribe +$q_\mathrm{n}(\boldsymbol{x}) = \boldsymbol{q}(\boldsymbol{x}) \cdot \boldsymbol{n}$, which we can calculate as +```math +q_\mathrm{n}(\boldsymbol{x}) = [\boldsymbol{N}_i(\boldsymbol{x}) \cdot \boldsymbol{n}] a_i +``` +However, for $H(\mathrm{div})$ interpolations, we don't have distinct algebraic nodal coordinates, +$\boldsymbol{x}_j$, fulfilling $\vert\boldsymbol{N}_i(\boldsymbol{x}_j)\vert = \delta_{ij}$. Instead, +we have +```math +\begin{align*} +\int_0^1 s (4-6s) \mathrm{d}s = [2s^2 - 2 s^3] = 0\\ +\int_0^1 (1-s) (4-6s) \mathrm{d}s = \int_0^1 4 - 10s + 6s^2 \mathrm{d}s = [4s - 5s^2 + 2s^3] = 1 +\end{align*} +``` +=# +# # # ## Commented Program # diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index a48cb3940f..c77cfa12d0 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -410,9 +410,12 @@ function update!(ch::ConstraintHandler, time::Real = 0.0) # 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{get_coordinate_type(get_grid(ch.dh)), typeof(time)}) ? dbc.f : (x, _) -> dbc.f(x) + #TODO: Temporary way to get the ip, not safe as it just get the first sdh with field_name in it. + field_idx = find_field(ch.dh, dbc.field_name) + ip = getfieldinterpolation(ch.dh, field_idx) # Function barrier _update!( - ch.inhomogeneities, wrapper_f, dbc.facets, dbc.field_name, dbc.local_facet_dofs, dbc.local_facet_dofs_offset, + ch.inhomogeneities, wrapper_f, dbc.facets, ip, dbc.local_facet_dofs, dbc.local_facet_dofs_offset, dbc.components, ch.dh, ch.bcvalues[i], ch.dofmapping, ch.dofcoefficients, time ) end @@ -440,7 +443,7 @@ end # for facets, vertices, faces and edges function _update!( - inhomogeneities::Vector{T}, f::Function, boundary_entities::AbstractVecOrSet{<:BoundaryIndex}, field::Symbol, local_facet_dofs::Vector{Int}, local_facet_dofs_offset::Vector{Int}, + inhomogeneities::Vector{T}, f::Function, boundary_entities::AbstractVecOrSet{<:BoundaryIndex}, ip::Interpolation, local_facet_dofs::Vector{Int}, local_facet_dofs_offset::Vector{Int}, components::Vector{Int}, dh::AbstractDofHandler, boundaryvalues::BCValues, dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real ) where {T} @@ -456,6 +459,12 @@ function _update!( r = local_facet_dofs_offset[entityidx]:(local_facet_dofs_offset[entityidx + 1] - 1) counter = 1 for location in 1:getnquadpoints(boundaryvalues) + sign = if mapping_type(ip) isa IdentityMapping + 1 + else + cell = getcells(cc.grid, cellidx) + get_direction(ip, location, cell) + end x = spatial_coordinate(boundaryvalues, location, cc.coords) bc_value = f(x, time) @assert length(bc_value) == length(components) @@ -469,7 +478,7 @@ function _update!( # Only DBC dofs are currently update!-able so don't modify inhomogeneities # for affine constraints if dofcoefficients[dbc_index] === nothing - inhomogeneities[dbc_index] = bc_value[i] + inhomogeneities[dbc_index] = sign * bc_value[i] @debug println("prescribing value $(bc_value[i]) on global dof $(globaldof)") end end @@ -480,7 +489,7 @@ end # for nodes function _update!( - inhomogeneities::Vector{T}, f::Function, ::AbstractVecOrSet{Int}, field::Symbol, nodeidxs::Vector{Int}, globaldofs::Vector{Int}, + inhomogeneities::Vector{T}, f::Function, ::AbstractVecOrSet{Int}, _::Interpolation, nodeidxs::Vector{Int}, globaldofs::Vector{Int}, components::Vector{Int}, dh::AbstractDofHandler, facetvalues::BCValues, dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real ) where {T} diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 7b5e0eedc1..5b3cf30dbf 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,6 +1,6 @@ using Ferrite: reference_shape_value, reference_shape_gradient -@testset "interpolations" begin +@testset "interpolations" begin #= @testset "Value Type $value_type" for value_type in (Float32, Float64) @testset "Correctness of $interpolation" for interpolation in ( Lagrange{RefLine, 1}(), @@ -221,7 +221,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test Ferrite.is_discontinuous(ip_t) == false @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true - end + end =# @testset "Correctness of AD of embedded interpolations" begin ip = Lagrange{RefHexahedron, 2}()^3 ξ = rand(Vec{3, Float64}) @@ -426,14 +426,14 @@ using Ferrite: reference_shape_value, reference_shape_gradient @testset "Hcurl and Hdiv BC" begin hdiv_ips = ( RaviartThomas{2, RefTriangle, 1}(), - RaviartThomas{2, RefTriangle, 2}(), - Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), + #RaviartThomas{2, RefTriangle, 2}(), + #Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), ) hdiv_check(v, n) = v ⋅ n hcurl_ips = ( - Nedelec{2, RefTriangle, 1}(), - Nedelec{2, RefTriangle, 2}(), + # Nedelec{2, RefTriangle, 1}(), + #Nedelec{2, RefTriangle, 2}(), ) function hcurl_check(v, n::Vec{2}) # 3d not supported yet t = rotate(n, π / 2) @@ -449,25 +449,27 @@ using Ferrite: reference_shape_value, reference_shape_gradient qr = FacetQuadratureRule{RefShape}(4) fv = FacetValues(qr, ip, geometric_interpolation(CT)) dh = close!(add!(DofHandler(grid), :u, ip)) - for bval in (0.0,) # 1.0) # Need to add mapping in _update! for this to work correctly - a = rand(ndofs(dh)) - ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, getfacetset(grid, "left"), Returns(bval))) - close!(ch) - apply!(a, ch) - test_val = 0.0 - for (cellidx, facetidx) in getfacetset(grid, "left") - reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) - ae = a[celldofs(dh, cellidx)] - val = 0.0 - for q_point in 1:getnquadpoints(fv) - dΓ = getdetJdV(fv, q_point) - val += f(function_value(fv, q_point, ae), getnormal(fv, q_point)) * dΓ + for bval in (#=0.0,=# 1.0,) # Need to add mapping in _update! for this to work correctly + for side in ("left", "right", "top", "bottom") + a = zeros(ndofs(dh)) + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:u, getfacetset(grid, "left"), Returns(bval))) + close!(ch) + apply!(a, ch) + test_val = 0.0 + for (cellidx, facetidx) in getfacetset(grid, "left") + reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) + ae = a[celldofs(dh, cellidx)] + val = 0.0 + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + val += f(function_value(fv, q_point, ae), getnormal(fv, q_point)) * dΓ + end + test_val += f === hdiv_check ? val : abs(val) end - test_val += f === hdiv_check ? val : abs(val) + println(typeof(ip), " ($side): ", test_val) + #@test abs(test_val - 2 * bval) < 1.0e-6 end - println(bval, ": ", test_val) - @test abs(test_val - 2 * bval) < 1.0e-6 end end end From 35eec64b113437e69841285a3fab5c467780d91f Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Fri, 22 Nov 2024 23:44:17 +0100 Subject: [PATCH 147/172] Make Dirichlet for single shape fun per edge work --- src/Dofs/ConstraintHandler.jl | 3 ++- test/test_interpolations.jl | 17 ++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index c77cfa12d0..6af348ecef 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -463,7 +463,8 @@ function _update!( 1 else cell = getcells(cc.grid, cellidx) - get_direction(ip, location, cell) + shape_number = local_facet_dofs[r[counter]] + get_direction(ip, shape_number, cell) end x = spatial_coordinate(boundaryvalues, location, cc.coords) bc_value = f(x, time) diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 5b3cf30dbf..749646f72b 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -303,6 +303,8 @@ using Ferrite: reference_shape_value, reference_shape_gradient # Required properties of shape value Nⱼ of an edge-elements (Hdiv) on an edge with normal n, length L, and dofs ∈ 𝔇 # 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 ∀ ∈ 𝔇 # Where f(s) = 1 for single shape function on edge, and f(s)=1-s and f(s)=s for two shape functions on edge + # It also seems like unit property should hold for multiple shape functions per edge, and that + # it should be zero for the "reverse" (e.g. shape fun 1 unit for f(s)=1-s but zero for f(s)=s) # s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 @testset "H(div) on RefCell" begin @@ -426,6 +428,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @testset "Hcurl and Hdiv BC" begin hdiv_ips = ( RaviartThomas{2, RefTriangle, 1}(), + # More than 1 per edge doesn't work. #RaviartThomas{2, RefTriangle, 2}(), #Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), ) @@ -433,7 +436,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient hcurl_ips = ( # Nedelec{2, RefTriangle, 1}(), - #Nedelec{2, RefTriangle, 2}(), + # Nedelec{2, RefTriangle, 2}(), ) function hcurl_check(v, n::Vec{2}) # 3d not supported yet t = rotate(n, π / 2) @@ -449,26 +452,26 @@ using Ferrite: reference_shape_value, reference_shape_gradient qr = FacetQuadratureRule{RefShape}(4) fv = FacetValues(qr, ip, geometric_interpolation(CT)) dh = close!(add!(DofHandler(grid), :u, ip)) - for bval in (#=0.0,=# 1.0,) # Need to add mapping in _update! for this to work correctly - for side in ("left", "right", "top", "bottom") + for bval in (#=0.0,=# 1.0,) + for side in ("left",) # "right", "top", "bottom") a = zeros(ndofs(dh)) ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, getfacetset(grid, "left"), Returns(bval))) + add!(ch, Dirichlet(:u, getfacetset(grid, side), Returns(bval))) close!(ch) apply!(a, ch) test_val = 0.0 - for (cellidx, facetidx) in getfacetset(grid, "left") + for (cellidx, facetidx) in getfacetset(grid, side) reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) ae = a[celldofs(dh, cellidx)] val = 0.0 + @show ae for q_point in 1:getnquadpoints(fv) dΓ = getdetJdV(fv, q_point) val += f(function_value(fv, q_point, ae), getnormal(fv, q_point)) * dΓ end test_val += f === hdiv_check ? val : abs(val) end - println(typeof(ip), " ($side): ", test_val) - #@test abs(test_val - 2 * bval) < 1.0e-6 + @test abs(test_val - 2 * bval) < 1.0e-6 end end end From f5f3f971f22b13cc157a67b27386f07e49a11c90 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sat, 23 Nov 2024 00:42:58 +0100 Subject: [PATCH 148/172] Working DBC for Nedelec/RT/BDM (triangles) --- src/interpolations.jl | 44 ++++++++++++++-------------- test/test_interpolations.jl | 57 +++++++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/src/interpolations.jl b/src/interpolations.jl index eee217678f..2c9fc1c959 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1844,17 +1844,17 @@ RefTriangle function reference_shape_value(ip::RaviartThomas{2, RefTriangle, 2}, ξ::Vec{2}, i::Int) x, y = ξ # Face 1 (keep ordering, flip sign) - i == 1 && return Vec(4x * (2x - 1), 2y * (4x - 1)) - i == 2 && return Vec(2x * (4y - 1), 4y * (2y - 1)) + i == 1 && return Vec(4x * (2x - 1), 2y * (4x - 1)) / 2 + i == 2 && return Vec(2x * (4y - 1), 4y * (2y - 1)) / 2 # Face 2 (flip ordering, keep signs) - i == 3 && return Vec(8x * y - 2x - 6y + 2, 4y * (2y - 1)) - i == 4 && return Vec(-8x^2 - 8x * y + 12x + 6y - 4, 2y * (-4x - 4y + 3)) + i == 3 && return Vec(8x * y - 2x - 6y + 2, 4y * (2y - 1)) / 2 + i == 4 && return Vec(-8x^2 - 8x * y + 12x + 6y - 4, 2y * (-4x - 4y + 3)) / 2 # Face 3 (keep ordering, flip sign) - i == 5 && return Vec(2x * (3 - 4x - 4y), -8x * y + 6x - 8y^2 + 12y - 4) - i == 6 && return Vec(4x * (2x - 1), 8x * y - 6x - 2y + 2) + i == 5 && return Vec(2x * (3 - 4x - 4y), -8x * y + 6x - 8y^2 + 12y - 4) / 2 + i == 6 && return Vec(4x * (2x - 1), 8x * y - 6x - 2y + 2) / 2 # Cell - i == 7 && return Vec(8x * (-2x - y + 2), 8y * (-2x - y + 1)) - i == 8 && return Vec(8x * (-2y - x + 1), 8y * (-2y - x + 2)) + i == 7 && return Vec(8x * (-2x - y + 2), 8y * (-2x - y + 1)) / 2 + i == 8 && return Vec(8x * (-2y - x + 1), 8y * (-2y - x + 2)) / 2 throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1900,14 +1900,14 @@ Edge numbers: | Edge identifiers: function reference_shape_value(ip::BrezziDouglasMarini{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) x, y = ξ # Edge 1 - i == 1 && return Vec(4x, -2y) # Changed sign to integrated value positive outwards - i == 2 && return Vec(-2x, 4y) # Changed sign to make positive outwards + i == 1 && return Vec(4x, -2y) / 2 # Changed sign to integrated value positive outwards + i == 2 && return Vec(-2x, 4y) / 2 # Changed sign to make positive outwards # Edge 2 (reverse order to follow Ferrite convention) - i == 3 && return Vec(-2x - 6y + 2, 4y) # N ⋅ n = (6y - 2) - i == 4 && return Vec(4x + 6y - 4, -2y) # N ⋅ n = (4 - 6y) + i == 3 && return Vec(-2x - 6y + 2, 4y) / 2 # N ⋅ n = (6y - 2) + i == 4 && return Vec(4x + 6y - 4, -2y) / 2 # N ⋅ n = (4 - 6y) # Edge 3 - i == 5 && return Vec(-2x, 6x + 4y - 4) # Changed sign to make positive outwards - i == 6 && return Vec(4x, -6x - 2y + 2) # Changed sign to make positive outwards + i == 5 && return Vec(-2x, 6x + 4y - 4) / 2 # Changed sign to make positive outwards + i == 6 && return Vec(4x, -6x - 2y + 2) / 2 # Changed sign to make positive outwards throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1955,38 +1955,38 @@ function reference_shape_value(ip::Nedelec{2, RefTriangle, 2}, ξ::Vec{2}, i::In i == 1 && return Vec( 2 * y * (1 - 4 * x), 4 * x * (2 * x - 1) - ) + ) / 2 i == 2 && return Vec( 4 * y * (1 - 2 * y), 2 * x * (4 * y - 1) - ) + ) / 2 # Face 2 (flip order and sign compared to defelement) i == 3 && return Vec( 4 * y * (1 - 2 * y), 8 * x * y - 2 * x - 6 * y + 2 - ) + ) / 2 i == 4 && return Vec( 2 * y * (4 * x + 4 * y - 3), -8 * x^2 - 8 * x * y + 12 * x + 6 * y - 4 - ) + ) / 2 # Face 3 i == 5 && return Vec( 8 * x * y - 6 * x + 8 * y^2 - 12 * y + 4, 2 * x * (-4 * x - 4 * y + 3) - ) + ) / 2 i == 6 && return Vec( -8 * x * y + 6 * x + 2 * y - 2, 4 * x * (2 * x - 1) - ) + ) / 2 # Cell i == 7 && return Vec( 8 * y * (-x - 2 * y + 2), 8 * x * (x + 2 * y - 1) - ) + ) / 2 i == 8 && return Vec( 8 * y * (2 * x + y - 1), 8 * x * (-2 * x - y + 2) - ) + ) / 2 throw(ArgumentError("no shape function $i for interpolation $ip")) end diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 749646f72b..c0941b61f2 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -255,18 +255,25 @@ using Ferrite: reference_shape_value, reference_shape_gradient reference_cell(::Type{RefHexahedron}) = Hexahedron((ntuple(identity, 8))) function line_integral(qr::QuadratureRule{RefLine}, ip, shape_nr, x0, Δx, L, v, f) - s = 0.0 + val = 0.0 for (ξ1d, w) in zip(Ferrite.getpoints(qr), Ferrite.getweights(qr)) - ξ = x0 + (ξ1d[1] / 2) * Δx - s += (reference_shape_value(ip, ξ, shape_nr) ⋅ v) * (w * L / 2) * f((ξ1d[1] + 1) / 2) + ξ = x0 + (ξ1d[1] / 2) * Δx # ::Vec + s = (ξ1d[1] + 1) / 2 # ∈ [0, 1] + Nξ = reference_shape_value(ip, ξ, shape_nr) + dΩ = (w * L / 2) + val += (Nξ ⋅ v) * f(s) * dΩ end - return s + return val end # Required properties of shape value Nⱼ of an edge-elements (Hcurl) on an edge with direction v, length L, and dofs ∈ 𝔇 - # 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1 ∀ ∈ 𝔇 - # Where f(s) = 1 for linear interpolation and f(s)=1-s and f(s)=s for 2nd order interpolation (first and second shape function) - # And s is the path parameter ∈[0,1] along the positive direction of the path. + # 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1/length(𝔇) ∀ ∈ 𝔇 + # Must hold for + # length(𝔇) ≥ 1: f(s) = 1 + # length(𝔇) = 2: f(s) = 1 - s or f(s) = s for 1st and 2nd dof, respectively. + # Additionally, should be zero for + # length(𝔇) = 2: f(s) = s or f(s) = 1 - s for 1st and 2nd dof, respectively. + # s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 @testset "H(curl) on RefCell" begin lineqr = QuadratureRule{RefLine}(20) @@ -285,7 +292,11 @@ using Ferrite: reference_shape_value, reference_shape_gradient nedgedofs = length(dofs[edge_nr]) f(x) = nedgedofs == 1 ? 1.0 : (idof == 1 ? 1 - x : x) s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, f) - @test s ≈ one(s) + @test s ≈ one(s) / nedgedofs + if nedgedofs == 2 + g(x) = idof == 1 ? x : 1 - x + @test 1 ≈ 1 + line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, g) + end end for (j_edge, shape_nrs) in enumerate(dofs) j_edge == edge_nr && continue @@ -301,10 +312,12 @@ using Ferrite: reference_shape_value, reference_shape_gradient end # Required properties of shape value Nⱼ of an edge-elements (Hdiv) on an edge with normal n, length L, and dofs ∈ 𝔇 - # 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 ∀ ∈ 𝔇 - # Where f(s) = 1 for single shape function on edge, and f(s)=1-s and f(s)=s for two shape functions on edge - # It also seems like unit property should hold for multiple shape functions per edge, and that - # it should be zero for the "reverse" (e.g. shape fun 1 unit for f(s)=1-s but zero for f(s)=s) + # 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1/length(𝔇) ∀ j ∈ 𝔇 + # Must hold for + # length(𝔇) ≥ 1: f(s) = 1 + # length(𝔇) = 2: f(s) = 1 - s or f(s) = s for 1st and 2nd dof, respectively. + # Additionally, should be zero for + # length(𝔇) = 2: f(s) = s or f(s) = 1 - s for 1st and 2nd dof, respectively. # s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 @testset "H(div) on RefCell" begin @@ -330,7 +343,11 @@ using Ferrite: reference_shape_value, reference_shape_gradient nfacetdofs = length(dofs[facet_nr]) f(x) = nfacetdofs == 1 ? 1.0 : (idof == 1 ? 1 - x : x) s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, f) - @test s ≈ one(s) + @test s ≈ one(s) / nfacetdofs + if nfacetdofs == 2 + g(x) = idof == 1 ? x : 1 - x + @test 1 ≈ 1 + line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, g) + end end for (j_facet, shape_nrs) in enumerate(dofs) j_facet == facet_nr && continue @@ -428,15 +445,14 @@ using Ferrite: reference_shape_value, reference_shape_gradient @testset "Hcurl and Hdiv BC" begin hdiv_ips = ( RaviartThomas{2, RefTriangle, 1}(), - # More than 1 per edge doesn't work. - #RaviartThomas{2, RefTriangle, 2}(), - #Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), + RaviartThomas{2, RefTriangle, 2}(), + Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), ) hdiv_check(v, n) = v ⋅ n hcurl_ips = ( - # Nedelec{2, RefTriangle, 1}(), - # Nedelec{2, RefTriangle, 2}(), + Nedelec{2, RefTriangle, 1}(), + Nedelec{2, RefTriangle, 2}(), ) function hcurl_check(v, n::Vec{2}) # 3d not supported yet t = rotate(n, π / 2) @@ -452,8 +468,8 @@ using Ferrite: reference_shape_value, reference_shape_gradient qr = FacetQuadratureRule{RefShape}(4) fv = FacetValues(qr, ip, geometric_interpolation(CT)) dh = close!(add!(DofHandler(grid), :u, ip)) - for bval in (#=0.0,=# 1.0,) - for side in ("left",) # "right", "top", "bottom") + for bval in (0.0, 1.0) + for side in ("left", "right", "top", "bottom") a = zeros(ndofs(dh)) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfacetset(grid, side), Returns(bval))) @@ -464,7 +480,6 @@ using Ferrite: reference_shape_value, reference_shape_gradient reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) ae = a[celldofs(dh, cellidx)] val = 0.0 - @show ae for q_point in 1:getnquadpoints(fv) dΓ = getdetJdV(fv, q_point) val += f(function_value(fv, q_point, ae), getnormal(fv, q_point)) * dΓ From 0f8bf8e737797a7a567360ca9e5946b94d697321 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sun, 24 Nov 2024 19:54:49 +0100 Subject: [PATCH 149/172] Add some notes --- docs/src/devdocs/interpolations.md | 20 ++++++++++++++++++++ src/interpolations.jl | 14 +++++++------- test/test_interpolations.jl | 16 ++++++++++------ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/docs/src/devdocs/interpolations.md b/docs/src/devdocs/interpolations.md index 5c3f441bc4..702833d4f7 100644 --- a/docs/src/devdocs/interpolations.md +++ b/docs/src/devdocs/interpolations.md @@ -49,3 +49,23 @@ dofs defined on a specific entity. Hence, not overloading of the dof functions w element with zero dofs. Also, it should always be double checked that everything is consistent as specified in the docstring of the corresponding function, as inconsistent implementations can lead to bugs which are really difficult to track down. + +## Vector interpolation properties +### Hdiv interpolations + +On a facet, ``\Gamma``, with normal, ``\boldsymbol{n}``, +the set of ``H(\mathrm{div})`` interpolation functions, +``\boldsymbol{N}_i(\boldsymbol{\xi})``, should fullfill +```math +\begin{align*} +\sum_{i = 1}^N \int_\Gamma \boldsymbol{N}_i(\boldsymbol{\xi}) \cdot \boldsymbol{n} &= 1 \\ +\sum_{i = 1}^N \int_\Gamma f_i(\boldsymbol{\xi}) \boldsymbol{N}_i(\boldsymbol{\xi}) \cdot \boldsymbol{n} &= 1 \\ +\int_\Gamma f_i(\boldsymbol{\xi}) \boldsymbol{N}_j(\boldsymbol{\xi}) \cdot \boldsymbol{n} &= 0, \quad i \neq j +``` +The moment-weighting functions ``f_i(\boldsymbol{\xi})`` depend on how many base functions there are per +facet and the reference shape of the facet (`RefLine`, `RefTriangle`, or `RefQuadrilateral`). + +These integral quantities apply to arbitrarily sized cell facets, and hence the actual value of the base functions +will scale depending on the size (smaller facet ``\rightarrow`` higher values). Consequently, when applying BCs, +we need to consider the actual facet size to be able to prescribe the average flux. Therefore, we include the +`GeometryMapping` values in the `BCValues` object. diff --git a/src/interpolations.jl b/src/interpolations.jl index 2c9fc1c959..a76cebeaca 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1899,15 +1899,15 @@ Edge numbers: | Edge identifiers: # RefTriangle, 1st order Lagrange function reference_shape_value(ip::BrezziDouglasMarini{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) x, y = ξ - # Edge 1 - i == 1 && return Vec(4x, -2y) / 2 # Changed sign to integrated value positive outwards - i == 2 && return Vec(-2x, 4y) / 2 # Changed sign to make positive outwards - # Edge 2 (reverse order to follow Ferrite convention) + # Edge 1: y=1-x, n = [1, 1]/√2 (Flip sign, pos. integration outwards) + i == 1 && return Vec(4x, -2y) / 2 # N ⋅ n = (2√2 x - √2 (1-x)) = 3√2 x - √2 + i == 2 && return Vec(-2x, 4y) / 2 # N ⋅ n = (-√2x + 2√2 (1-x)) = 2√2 - 3√2x + # Edge 2: x=0, n = [-1, 0] (reverse order to follow Ferrite convention) i == 3 && return Vec(-2x - 6y + 2, 4y) / 2 # N ⋅ n = (6y - 2) i == 4 && return Vec(4x + 6y - 4, -2y) / 2 # N ⋅ n = (4 - 6y) - # Edge 3 - i == 5 && return Vec(-2x, 6x + 4y - 4) / 2 # Changed sign to make positive outwards - i == 6 && return Vec(4x, -6x - 2y + 2) / 2 # Changed sign to make positive outwards + # Edge 3: y=0, n = [0, -1] (Flip sign, post. integration outwards) + i == 5 && return Vec(-2x, 6x + 4y - 4) / 2 # N ⋅ n = (4 - 6x) + i == 6 && return Vec(4x, -6x - 2y + 2) / 2 # N ⋅ n = (6x - 2) throw(ArgumentError("no shape function $i for interpolation $ip")) end diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index c0941b61f2..b92e0d95d1 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,6 +1,6 @@ using Ferrite: reference_shape_value, reference_shape_gradient -@testset "interpolations" begin #= +@testset "interpolations" begin # #= @testset "Value Type $value_type" for value_type in (Float32, Float64) @testset "Correctness of $interpolation" for interpolation in ( Lagrange{RefLine, 1}(), @@ -221,7 +221,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test Ferrite.is_discontinuous(ip_t) == false @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true - end =# + end # =# @testset "Correctness of AD of embedded interpolations" begin ip = Lagrange{RefHexahedron, 2}()^3 ξ = rand(Vec{3, Float64}) @@ -464,18 +464,20 @@ using Ferrite: reference_shape_value, reference_shape_gradient RefShape = Ferrite.getrefshape(ip) CT = typeof(reference_cell(RefShape)) dim = Ferrite.getrefdim(CT) # dim=sdim=vdim - grid = generate_grid(CT, ntuple(Returns(2), dim)) + #grid = generate_grid(CT, ntuple(Returns(2), dim), - rand(Vec{dim}), rand(Vec{dim})) + grid = generate_grid(CT, ntuple(Returns(2), dim), -Vec((-0.25, -0.25)), Vec((0.2, 0.2))) qr = FacetQuadratureRule{RefShape}(4) fv = FacetValues(qr, ip, geometric_interpolation(CT)) dh = close!(add!(DofHandler(grid), :u, ip)) - for bval in (0.0, 1.0) - for side in ("left", "right", "top", "bottom") + for bval in (1.0,) #(0.0, 1.0) + for side in ("left",) # "right", "top", "bottom") a = zeros(ndofs(dh)) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfacetset(grid, side), Returns(bval))) close!(ch) apply!(a, ch) test_val = 0.0 + test_area = 0.0 for (cellidx, facetidx) in getfacetset(grid, side) reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) ae = a[celldofs(dh, cellidx)] @@ -483,10 +485,12 @@ using Ferrite: reference_shape_value, reference_shape_gradient for q_point in 1:getnquadpoints(fv) dΓ = getdetJdV(fv, q_point) val += f(function_value(fv, q_point, ae), getnormal(fv, q_point)) * dΓ + test_area += dΓ end test_val += f === hdiv_check ? val : abs(val) end - @test abs(test_val - 2 * bval) < 1.0e-6 + @show (test_val, test_area) + #@test abs(test_val - test_area * bval) < 1.0e-6 end end end From e528efb19016a597c9bfa400210e72d089e1c6c3 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sun, 24 Nov 2024 20:32:07 +0100 Subject: [PATCH 150/172] Start thinking about BoundaryDofValues --- docs/src/devdocs/interpolations.md | 5 ++- src/FEValues/BoundaryValues.jl | 63 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/FEValues/BoundaryValues.jl diff --git a/docs/src/devdocs/interpolations.md b/docs/src/devdocs/interpolations.md index 702833d4f7..2988ca5171 100644 --- a/docs/src/devdocs/interpolations.md +++ b/docs/src/devdocs/interpolations.md @@ -67,5 +67,6 @@ facet and the reference shape of the facet (`RefLine`, `RefTriangle`, or `RefQua These integral quantities apply to arbitrarily sized cell facets, and hence the actual value of the base functions will scale depending on the size (smaller facet ``\rightarrow`` higher values). Consequently, when applying BCs, -we need to consider the actual facet size to be able to prescribe the average flux. Therefore, we include the -`GeometryMapping` values in the `BCValues` object. +we need to consider the actual facet size to be able to prescribe the average flux. +Consider making a new generalized `BCValues`: `BoundaryDofValues` object that stores the required info. +Develop as separate first, could possible replace the current `BCValues`... diff --git a/src/FEValues/BoundaryValues.jl b/src/FEValues/BoundaryValues.jl new file mode 100644 index 0000000000..e83a458e6c --- /dev/null +++ b/src/FEValues/BoundaryValues.jl @@ -0,0 +1,63 @@ +""" + BoundaryDofValues([::Type{T},] func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Union{Type{<:BoundaryIndex}}) + +`BoundaryDofValues` stores the information required to apply constraints to the dofs at each boundary entity +(i.e. all facets, faces, edges, or vertices depending on `boundary_type`). What information that is required depends on +the type of function interpolation. + +Formally, we need to store information required to evaluate the functionals, lᵢ in ℒ, +defining a "degree of freedom" according to the Ciarlet finite element definition. + +For standard scalar or vectorized interpolations, the functionals, lᵢ, are defined as the +value at a fixed coordinate, and only the geometric shape function values at those coordinates +in the reference shape are required. + +For vector interpolations, the functionals are typically defined as an integral quantity over the +boundary entity, and further information (such as the size of the entity) is required. +""" +mutable struct BoundaryDofValues{V_GM, FQR} + const geo_mapping::V_GM # AbstractVector{GeometryMapping} + const boundary_qr::V_QR # AbstractVector{QuadratureRule} + current_entity::Int + const boundary_type::Symbol # For information only + const entity_dofs::Vector{Vector{Int}} # Or smth like this - could make getting dofs for specific entities much easier/reusable... +end + +function BoundaryDofValues(func_interpol::Interpolation, geom_interpol::Interpolation, boundary_type::Type{<:BoundaryIndex} = FacetIndex) + return BoundaryDofValues(Float64, func_interpol, geom_interpol, boundary_type) +end + +function BoundaryDofValues(::Type{T}, func_interpol::Interpolation{refshape}, geom_interpol::Interpolation{refshape}, boundary_type::Type{<:BoundaryIndex} = FacetIndex) where {T, dim, refshape <: AbstractRefShape{dim}} + # set up quadrature rules for each boundary entity with dof-positions + # (determined by func_interpol) as the quadrature points + interpolation_coords = reference_coordinates(func_interpol) + + # qrs = QuadratureRule{refshape,T,dim}[] + qrs = map(collect(dirichlet_boundarydof_indices(boundary_type)(func_interpol))) do boundarydofs + # for boundarydofs in dirichlet_boundarydof_indices(boundary_type)(func_interpol) + dofcoords = Vec{dim, T}[] + for boundarydof in boundarydofs + push!(dofcoords, interpolation_coords[boundarydof]) + end + QuadratureRule{refshape}(fill(T(NaN), length(dofcoords)), dofcoords) # weights will not be used + # qrf = QuadratureRule{refshape}(fill(T(NaN), length(dofcoords)), dofcoords) # weights will not be used + # push!(qrs, qrf) + end + geo_mapping = [GeometryMapping{0}(T, geom_interpol, qr) for qr in qrs] + + return BCValues(geo_mapping, fqr, 1) +end + +@inline nfacets(bcv::BoundaryDofValues) = length(bcv.geo_mapping) +@inline getcurrentfacet(bcv) = bcv.current_facet + +function set_current_facet!(bcv::BoundaryDofValues, facet_nr::Int) + # Checking facet_nr before setting current_facet allows @inbounds in getcurrentfacet(fv) + checkbounds(Bool, 1:nfacets(bcv), facet_nr) || throw(ArgumentError("Facet index out of range.")) + return bcv.current_facet = facet_nr +end + +@inline get_geo_mapping(bcv::BoundaryDofValues) = @inbounds bcv.geo_mapping[getcurrentfacet(bcv)] +@inline geometric_value(bcv::BoundaryDofValues, q_point::Int, base_func::Int) = geometric_value(get_geo_mapping(bcv), q_point, base_func) +@inline getngeobasefunctions(bcv::BoundaryDofValues) = getngeobasefunctions(get_geo_mapping(bcv)) +@inline getnquadpoints(bcv::BoundaryDofValues) = @inbounds getnquadpoints(get_geo_mapping(bcv)) From 3e0ffb25605d1f73db7ab6232224ad45a902d4a5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Mon, 2 Dec 2024 22:35:45 +0100 Subject: [PATCH 151/172] Simplify tests, export BrezziDoublasMarini --- .../literate-tutorials/heat_equation_hdiv.jl | 2 +- src/exports.jl | 1 + src/interpolations.jl | 48 +++++------ test/test_interpolations.jl | 84 ++++++++----------- 4 files changed, 62 insertions(+), 73 deletions(-) diff --git a/docs/src/literate-tutorials/heat_equation_hdiv.jl b/docs/src/literate-tutorials/heat_equation_hdiv.jl index 6326c27b2d..2720945de8 100644 --- a/docs/src/literate-tutorials/heat_equation_hdiv.jl +++ b/docs/src/literate-tutorials/heat_equation_hdiv.jl @@ -93,7 +93,7 @@ grid = create_grid(10) # We define one `CellValues` for each field which share the same quadrature rule. ip_geo = geometric_interpolation(getcelltype(grid)) ipu = DiscontinuousLagrange{RefTriangle, 0}() -ipq = Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}() +ipq = BrezziDouglasMarini{2, RefTriangle, 1}() qr = QuadratureRule{RefTriangle}(2) cellvalues = (u = CellValues(qr, ipu, ip_geo), q = CellValues(qr, ipq, ip_geo)) #md nothing # hide diff --git a/src/exports.jl b/src/exports.jl index 446c975549..786fbf8b29 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -19,6 +19,7 @@ export Serendipity, Nedelec, RaviartThomas, + BrezziDouglasMarini, getnbasefunctions, getrefshape, diff --git a/src/interpolations.jl b/src/interpolations.jl index a76cebeaca..a0af4a0eb6 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1803,7 +1803,7 @@ dirichlet_facedof_indices(ip::RaviartThomas{3}) = facedof_interior_indices(ip) function reference_shape_value(ip::RaviartThomas{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) x, y = ξ i == 1 && return ξ # Flip sign - i == 2 && return Vec(x - 1, y) # Keep sign + i == 2 && return Vec(x - 1, y) # Keep sign i == 3 && return Vec(x, y - 1) # Flip sign throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1844,17 +1844,17 @@ RefTriangle function reference_shape_value(ip::RaviartThomas{2, RefTriangle, 2}, ξ::Vec{2}, i::Int) x, y = ξ # Face 1 (keep ordering, flip sign) - i == 1 && return Vec(4x * (2x - 1), 2y * (4x - 1)) / 2 - i == 2 && return Vec(2x * (4y - 1), 4y * (2y - 1)) / 2 + i == 1 && return Vec(4x * (2x - 1), 2y * (4x - 1)) + i == 2 && return Vec(2x * (4y - 1), 4y * (2y - 1)) # Face 2 (flip ordering, keep signs) - i == 3 && return Vec(8x * y - 2x - 6y + 2, 4y * (2y - 1)) / 2 - i == 4 && return Vec(-8x^2 - 8x * y + 12x + 6y - 4, 2y * (-4x - 4y + 3)) / 2 + i == 3 && return Vec(8x * y - 2x - 6y + 2, 4y * (2y - 1)) + i == 4 && return Vec(-8x^2 - 8x * y + 12x + 6y - 4, 2y * (-4x - 4y + 3)) # Face 3 (keep ordering, flip sign) - i == 5 && return Vec(2x * (3 - 4x - 4y), -8x * y + 6x - 8y^2 + 12y - 4) / 2 - i == 6 && return Vec(4x * (2x - 1), 8x * y - 6x - 2y + 2) / 2 + i == 5 && return Vec(2x * (3 - 4x - 4y), -8x * y + 6x - 8y^2 + 12y - 4) + i == 6 && return Vec(4x * (2x - 1), 8x * y - 6x - 2y + 2) # Cell - i == 7 && return Vec(8x * (-2x - y + 2), 8y * (-2x - y + 1)) / 2 - i == 8 && return Vec(8x * (-2y - x + 1), 8y * (-2y - x + 2)) / 2 + i == 7 && return Vec(8x * (-2x - y + 2), 8y * (-2x - y + 1)) + i == 8 && return Vec(8x * (-2y - x + 1), 8y * (-2y - x + 2)) throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1900,14 +1900,14 @@ Edge numbers: | Edge identifiers: function reference_shape_value(ip::BrezziDouglasMarini{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) x, y = ξ # Edge 1: y=1-x, n = [1, 1]/√2 (Flip sign, pos. integration outwards) - i == 1 && return Vec(4x, -2y) / 2 # N ⋅ n = (2√2 x - √2 (1-x)) = 3√2 x - √2 - i == 2 && return Vec(-2x, 4y) / 2 # N ⋅ n = (-√2x + 2√2 (1-x)) = 2√2 - 3√2x + i == 1 && return Vec(4x, -2y) # N ⋅ n = (2√2 x - √2 (1-x)) = 3√2 x - √2 + i == 2 && return Vec(-2x, 4y) # N ⋅ n = (-√2x + 2√2 (1-x)) = 2√2 - 3√2x # Edge 2: x=0, n = [-1, 0] (reverse order to follow Ferrite convention) - i == 3 && return Vec(-2x - 6y + 2, 4y) / 2 # N ⋅ n = (6y - 2) - i == 4 && return Vec(4x + 6y - 4, -2y) / 2 # N ⋅ n = (4 - 6y) - # Edge 3: y=0, n = [0, -1] (Flip sign, post. integration outwards) - i == 5 && return Vec(-2x, 6x + 4y - 4) / 2 # N ⋅ n = (4 - 6x) - i == 6 && return Vec(4x, -6x - 2y + 2) / 2 # N ⋅ n = (6x - 2) + i == 3 && return Vec(-2x - 6y + 2, 4y) # N ⋅ n = (6y - 2) + i == 4 && return Vec(4x + 6y - 4, -2y) # N ⋅ n = (4 - 6y) + # Edge 3: y=0, n = [0, -1] (Flip sign, pos. integration outwards) + i == 5 && return Vec(-2x, 6x + 4y - 4) # N ⋅ n = (4 - 6x) + i == 6 && return Vec(4x, -6x - 2y + 2) # N ⋅ n = (6x - 2) throw(ArgumentError("no shape function $i for interpolation $ip")) end @@ -1955,38 +1955,38 @@ function reference_shape_value(ip::Nedelec{2, RefTriangle, 2}, ξ::Vec{2}, i::In i == 1 && return Vec( 2 * y * (1 - 4 * x), 4 * x * (2 * x - 1) - ) / 2 + ) i == 2 && return Vec( 4 * y * (1 - 2 * y), 2 * x * (4 * y - 1) - ) / 2 + ) # Face 2 (flip order and sign compared to defelement) i == 3 && return Vec( 4 * y * (1 - 2 * y), 8 * x * y - 2 * x - 6 * y + 2 - ) / 2 + ) i == 4 && return Vec( 2 * y * (4 * x + 4 * y - 3), -8 * x^2 - 8 * x * y + 12 * x + 6 * y - 4 - ) / 2 + ) # Face 3 i == 5 && return Vec( 8 * x * y - 6 * x + 8 * y^2 - 12 * y + 4, 2 * x * (-4 * x - 4 * y + 3) - ) / 2 + ) i == 6 && return Vec( -8 * x * y + 6 * x + 2 * y - 2, 4 * x * (2 * x - 1) - ) / 2 + ) # Cell i == 7 && return Vec( 8 * y * (-x - 2 * y + 2), 8 * x * (x + 2 * y - 1) - ) / 2 + ) i == 8 && return Vec( 8 * y * (2 * x + y - 1), 8 * x * (-2 * x - y + 2) - ) / 2 + ) throw(ArgumentError("no shape function $i for interpolation $ip")) end diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index b92e0d95d1..0c5b670f44 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -265,9 +265,11 @@ using Ferrite: reference_shape_value, reference_shape_gradient end return val end - + # TODO: 3D H(curl) not tested for BC (need edge integrals) + Hcurl_interpolations = [Nedelec{2, RefTriangle, 1}(), Nedelec{2, RefTriangle, 2}()] # Nedelec{3, RefTetrahedron, 1}(), Nedelec{3, RefHexahedron, 1}()] + Hdiv_interpolations = [RaviartThomas{2, RefTriangle, 1}(), RaviartThomas{2, RefTriangle, 2}(), BrezziDouglasMarini{2, RefTriangle, 1}()] # Required properties of shape value Nⱼ of an edge-elements (Hcurl) on an edge with direction v, length L, and dofs ∈ 𝔇 - # 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1/length(𝔇) ∀ ∈ 𝔇 + # 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1 ∀ ∈ 𝔇 # Must hold for # length(𝔇) ≥ 1: f(s) = 1 # length(𝔇) = 2: f(s) = 1 - s or f(s) = s for 1st and 2nd dof, respectively. @@ -277,7 +279,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient # 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 @testset "H(curl) on RefCell" begin lineqr = QuadratureRule{RefLine}(20) - for ip in (Nedelec{2, RefTriangle, 1}(), Nedelec{2, RefTriangle, 2}(), Nedelec{3, RefTetrahedron, 1}(), Nedelec{3, RefHexahedron, 1}()) + for ip in Hcurl_interpolations cell = reference_cell(getrefshape(ip)) edges = Ferrite.edges(cell) dofs = Ferrite.edgedof_interior_indices(ip) @@ -292,7 +294,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient nedgedofs = length(dofs[edge_nr]) f(x) = nedgedofs == 1 ? 1.0 : (idof == 1 ? 1 - x : x) s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, f) - @test s ≈ one(s) / nedgedofs + @test s ≈ one(s) if nedgedofs == 2 g(x) = idof == 1 ? x : 1 - x @test 1 ≈ 1 + line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, g) @@ -322,11 +324,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient # 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 @testset "H(div) on RefCell" begin lineqr = QuadratureRule{RefLine}(20) - for ip in ( - RaviartThomas{2, RefTriangle, 1}(), - RaviartThomas{2, RefTriangle, 2}(), - Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), - ) + for ip in Hdiv_interpolations cell = reference_cell(getrefshape(ip)) cell_facets = Ferrite.facets(cell) dofs = Ferrite.facetdof_interior_indices(ip) @@ -343,7 +341,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient nfacetdofs = length(dofs[facet_nr]) f(x) = nfacetdofs == 1 ? 1.0 : (idof == 1 ? 1 - x : x) s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, f) - @test s ≈ one(s) / nfacetdofs + @test s ≈ one(s) if nfacetdofs == 2 g(x) = idof == 1 ? x : 1 - x @test 1 ≈ 1 + line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, g) @@ -401,27 +399,28 @@ using Ferrite: reference_shape_value, reference_shape_gradient nel = 3 hdiv_check(v, n) = v ⋅ n # Hdiv (normal continuity) hcurl_check(v, n) = v - n * (v ⋅ n) # Hcurl (tangent continuity) - transformation_functions = ((Nedelec, hcurl_check), (RaviartThomas, hdiv_check), (Ferrite.BrezziDouglasMarini, hdiv_check)) - for CT in (Triangle, QuadraticTriangle, Tetrahedron, Hexahedron) - dim = Ferrite.getrefdim(CT) # dim = sdim = rdim - p1, p2 = (rand(Vec{dim}), ones(Vec{dim}) + rand(Vec{dim})) - grid = generate_grid(CT, ntuple(_ -> nel, dim), p1, p2) - # Smoothly distort grid (to avoid spuriously badly deformed elements). - # A distorted grid is important to properly test the geometry mapping - # for 2nd order elements. - transfun(x) = typeof(x)(i -> sinpi(x[mod(i, length(x)) + 1] + i / 3)) / 10 - transform_coordinates!(grid, x -> (x + transfun(x))) - cellnr = getncells(grid) ÷ 2 + 1 # Should be a cell in the center - basecell = getcells(grid, cellnr) - RefShape = Ferrite.getrefshape(basecell) - for order in (1, 2) - for (IPT, transformation_function) in transformation_functions - dim == 3 && order > 1 && continue - IPT == RaviartThomas && (dim == 3 || order > 1) && continue - IPT == RaviartThomas && (RefShape == RefHexahedron) && continue - IPT == Ferrite.BrezziDouglasMarini && !(RefShape == RefTriangle && order == 1) && continue - ip = IPT{dim, RefShape, order}() + cell_types = Dict( + RefTriangle => [Triangle, QuadraticTriangle], + RefQuadrilateral => [Quadrilateral, QuadraticQuadrilateral], + RefTetrahedron => [Tetrahedron], + RefHexahedron => [Hexahedron] + ) + + for (ips, check_function) in ((Hcurl_interpolations, hcurl_check), (Hdiv_interpolations, hdiv_check)) + for ip in ips + RefShape = getrefshape(ip) + dim = Ferrite.getrefdim(ip) # dim = sdim = rdim + p1, p2 = (rand(Vec{dim}), ones(Vec{dim}) + rand(Vec{dim})) + transfun(x) = typeof(x)(i -> sinpi(x[mod(i, length(x)) + 1] + i / 3)) / 10 + for CT in cell_types[RefShape] + grid = generate_grid(CT, ntuple(_ -> nel, dim), p1, p2) + # Smoothly distort grid (to avoid spuriously badly deformed elements). + # A distorted grid is important to properly test the geometry mapping + # for 2nd order elements. + transform_coordinates!(grid, x -> (x + transfun(x))) + cellnr = getncells(grid) ÷ 2 + 1 # Should be a cell in the center + basecell = getcells(grid, cellnr) @testset "$CT, $ip" begin for testcell in cell_permutations(basecell) grid.cells[cellnr] = testcell @@ -430,8 +429,8 @@ using Ferrite: reference_shape_value, reference_shape_gradient close!(dh) for facetnr in 1:nfacets(RefShape) fi = FacetIndex(cellnr, facetnr) - # Check continuity of tangential function value - ITU.test_continuity(dh, fi; transformation_function) + # Check continuity of function value according to check_function + ITU.test_continuity(dh, fi; transformation_function = check_function) end # Check gradient calculation ITU.test_gradient(dh, cellnr) @@ -443,34 +442,24 @@ using Ferrite: reference_shape_value, reference_shape_gradient end @testset "Hcurl and Hdiv BC" begin - hdiv_ips = ( - RaviartThomas{2, RefTriangle, 1}(), - RaviartThomas{2, RefTriangle, 2}(), - Ferrite.BrezziDouglasMarini{2, RefTriangle, 1}(), - ) hdiv_check(v, n) = v ⋅ n - - hcurl_ips = ( - Nedelec{2, RefTriangle, 1}(), - Nedelec{2, RefTriangle, 2}(), - ) function hcurl_check(v, n::Vec{2}) # 3d not supported yet t = rotate(n, π / 2) return v ⋅ t end - for (f, interpolations) in ((hdiv_check, hdiv_ips), (hcurl_check, hcurl_ips)) + for (f, interpolations) in ((hdiv_check, Hdiv_interpolations), (hcurl_check, Hcurl_interpolations)) for ip in interpolations + ip isa Nedelec && Ferrite.getrefdim(ip) == 3 && continue # skip 3d nedelec @testset "$ip" begin RefShape = Ferrite.getrefshape(ip) CT = typeof(reference_cell(RefShape)) dim = Ferrite.getrefdim(CT) # dim=sdim=vdim - #grid = generate_grid(CT, ntuple(Returns(2), dim), - rand(Vec{dim}), rand(Vec{dim})) grid = generate_grid(CT, ntuple(Returns(2), dim), -Vec((-0.25, -0.25)), Vec((0.2, 0.2))) qr = FacetQuadratureRule{RefShape}(4) fv = FacetValues(qr, ip, geometric_interpolation(CT)) dh = close!(add!(DofHandler(grid), :u, ip)) - for bval in (1.0,) #(0.0, 1.0) - for side in ("left",) # "right", "top", "bottom") + for bval in (0.0,) # TODO: Currently only zero-valued BC supported + for side in ("left", "right", "top", "bottom") a = zeros(ndofs(dh)) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfacetset(grid, side), Returns(bval))) @@ -489,8 +478,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient end test_val += f === hdiv_check ? val : abs(val) end - @show (test_val, test_area) - #@test abs(test_val - test_area * bval) < 1.0e-6 + @test abs(test_val - test_area * bval) < 1.0e-6 end end end From dbbc2232dd71453094db35e9253b0d2b3dc709fd Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 11 Dec 2024 22:21:37 +0100 Subject: [PATCH 152/172] Improve testing of hdiv and hcurl --- src/interpolations.jl | 23 ++++- test/test_interpolations.jl | 170 ++++++++++++++++++++++++++++++++---- 2 files changed, 173 insertions(+), 20 deletions(-) diff --git a/src/interpolations.jl b/src/interpolations.jl index a0af4a0eb6..e41681e36d 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -1797,6 +1797,10 @@ reference_coordinates(ip::RaviartThomas{vdim}) where {vdim} = fill(NaN * zero(Ve dirichlet_edgedof_indices(ip::RaviartThomas{2}) = edgedof_interior_indices(ip) dirichlet_facedof_indices(ip::RaviartThomas{3}) = facedof_interior_indices(ip) +# RefTriangle +edgedof_indices(ip::RaviartThomas{2, RefTriangle}) = edgedof_interior_indices(ip) +facedof_indices(ip::RaviartThomas{2, RefTriangle}) = (ntuple(i -> i, getnbasefunctions(ip)),) + # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-raviart-thomas-lagrange-1.html # Signs changed when needed to make positive direction outwards @@ -1810,6 +1814,7 @@ end getnbasefunctions(::RaviartThomas{2, RefTriangle, 1}) = 3 edgedof_interior_indices(::RaviartThomas{2, RefTriangle, 1}) = ((1,), (2,), (3,)) +facedof_interior_indices(::RaviartThomas{2, RefTriangle, 1}) = ((),) adjust_dofs_during_distribution(::RaviartThomas) = false function get_direction(::RaviartThomas{2, RefTriangle, 1}, j, cell) @@ -1860,7 +1865,7 @@ end getnbasefunctions(::RaviartThomas{2, RefTriangle, 2}) = 8 edgedof_interior_indices(::RaviartThomas{2, RefTriangle, 2}) = ((1, 2), (3, 4), (5, 6)) -volumedof_interior_indices(::RaviartThomas{2, RefTriangle, 2}) = (7, 8) +facedof_interior_indices(::RaviartThomas{2, RefTriangle, 2}) = ((7, 8),) adjust_dofs_during_distribution(::RaviartThomas{2, RefTriangle, 2}) = true function get_direction(::RaviartThomas{2, RefTriangle, 2}, j, cell) @@ -1877,6 +1882,11 @@ mapping_type(::BrezziDouglasMarini) = ContravariantPiolaMapping() reference_coordinates(ip::BrezziDouglasMarini{vdim}) where {vdim} = fill(NaN * zero(Vec{vdim}), getnbasefunctions(ip)) dirichlet_edgedof_indices(ip::BrezziDouglasMarini{2}) = edgedof_interior_indices(ip) n_dbc_components(::BrezziDouglasMarini) = 1 + +# RefTriangle +edgedof_indices(ip::BrezziDouglasMarini{2, RefTriangle}) = edgedof_interior_indices(ip) +facedof_indices(ip::BrezziDouglasMarini{2, RefTriangle}) = (ntuple(i -> i, getnbasefunctions(ip)),) + #= ----------------+-------------------- Vertex numbers: | Vertex coordinates: @@ -1928,6 +1938,11 @@ mapping_type(::Nedelec) = CovariantPiolaMapping() reference_coordinates(ip::Nedelec{vdim}) where {vdim} = fill(NaN * zero(Vec{vdim}), getnbasefunctions(ip)) dirichlet_edgedof_indices(ip::Nedelec) = edgedof_interior_indices(ip) n_dbc_components(::Nedelec) = 1 +edgedof_indices(ip::Nedelec) = edgedof_interior_indices(ip) + +# 2D refshape (rdim == vdim for Nedelec) +facedof_indices(ip::Nedelec{2, <:AbstractRefShape{2}}) = (ntuple(i -> i, getnbasefunctions(ip)),) + # RefTriangle, 1st order Lagrange # https://defelement.com/elements/examples/triangle-nedelec1-lagrange-1.html function reference_shape_value(ip::Nedelec{2, RefTriangle, 1}, ξ::Vec{2}, i::Int) @@ -1992,7 +2007,7 @@ end getnbasefunctions(::Nedelec{2, RefTriangle, 2}) = 8 edgedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = ((1, 2), (3, 4), (5, 6)) -volumedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = (7, 8) +facedof_interior_indices(::Nedelec{2, RefTriangle, 2}) = ((7, 8),) adjust_dofs_during_distribution(::Nedelec{2, RefTriangle, 2}) = true function get_direction(::Nedelec{2, RefTriangle, 2}, j, cell) @@ -2024,6 +2039,8 @@ getnbasefunctions(::Nedelec{3, RefTetrahedron, 1}) = 6 edgedof_interior_indices(::Nedelec{3, RefTetrahedron, 1}) = ntuple(i -> (i,), 6) adjust_dofs_during_distribution(::Nedelec{3, RefTetrahedron, 1}) = false +#TODO: facedof_indices + function get_direction(::Nedelec{3, RefTetrahedron, 1}, j, cell) edge = edges(cell)[j] return ifelse(edge[2] > edge[1], 1, -1) @@ -2075,6 +2092,8 @@ getnbasefunctions(::Nedelec{3, RefHexahedron, 1}) = 12 edgedof_interior_indices(::Nedelec{3, RefHexahedron, 1}) = ntuple(i -> (i,), 12) adjust_dofs_during_distribution(::Nedelec{3, RefHexahedron, 1}) = false +#TODO: facedof_indices + function get_direction(::Nedelec{3, RefHexahedron, 1}, j, cell) edge = edges(cell)[j] return ifelse(edge[2] > edge[1], 1, -1) diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 0c5b670f44..cd707c007e 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,6 +1,6 @@ using Ferrite: reference_shape_value, reference_shape_gradient -@testset "interpolations" begin # #= +@testset "interpolations" begin #= @testset "Value Type $value_type" for value_type in (Float32, Float64) @testset "Correctness of $interpolation" for interpolation in ( Lagrange{RefLine, 1}(), @@ -186,7 +186,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end end - end=# + end # VectorizedInterpolation v_interpolation_1 = interpolation^2 @@ -203,7 +203,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test @inferred(reference_shape_value(v_interpolation_1, x, dof)) isa Vec{2, value_type} @test @inferred(reference_shape_gradient(v_interpolation_3, x, dof)) isa Tensor{2, ref_dim, value_type} end - end # correctness testset + end # correctness testset =# @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefTriangle, 0}()) ≈ [Vec{2, Float64}((1 / 3, 1 / 3))] @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefQuadrilateral, 0}()) ≈ [Vec{2, Float64}((0, 0))] @@ -265,9 +265,91 @@ using Ferrite: reference_shape_value, reference_shape_gradient end return val end - # TODO: 3D H(curl) not tested for BC (need edge integrals) + + # There is some overlap with tests above, this tests some properties more carefully, + # and does not assume nodal base functions. + function test_interpolation_numbering(ip::Interpolation) + RefShape = getrefshape(ip) + collect_all_dofs(t::Tuple) = vcat(Int[], collect.(t)...) + vdofs = collect_all_dofs(Ferrite.vertexdof_indices(ip)) + edofs = collect_all_dofs(Ferrite.edgedof_indices(ip)) + fdofs = collect_all_dofs(Ferrite.facedof_indices(ip)) + edofs_i = collect_all_dofs(Ferrite.edgedof_interior_indices(ip)) + fdofs_i = collect_all_dofs(Ferrite.facedof_interior_indices(ip)) + voldofs_i = Ferrite.volumedof_interior_indices(ip) + + # Check match to reference shape + @test length(Ferrite.vertexdof_indices(ip)) == Ferrite.nvertices(RefShape) + @test length(Ferrite.edgedof_indices(ip)) == Ferrite.nedges(RefShape) + @test length(Ferrite.edgedof_interior_indices(ip)) == Ferrite.nedges(RefShape) + @test length(Ferrite.facedof_indices(ip)) == Ferrite.nfaces(RefShape) + @test length(Ferrite.facedof_interior_indices(ip)) == Ferrite.nfaces(RefShape) + + # Check numbering convention + # Vertices numbered first + @test all(vdofs .== 1:length(vdofs)) + # Edges numbered next, no gaps or missing numbers. Sorted by edge number. + all_edofs_i = vcat(collect.(edofs_i)...) + @test all(all_edofs_i .== length(vdofs) .+ (1:length(all_edofs_i))) + # - all edge dofs include both vertexdofs and interior edegdofs, and nothing more. + all_edofs = vcat(collect.(edofs)...) + @test all(all_edofs .== 1:length(all_edofs)) + @test length(all_edofs) == length(vdofs) + length(all_edofs_i) + # - test each edge invidividually + for i in 1:Ferrite.nedges(RefShape) + vdofs_e = Int[] + for j in Ferrite.reference_edges(RefShape)[i] + vdof_indices = Ferrite.vertexdof_indices(ip)[j] + isempty(vdof_indices) || append!(vdofs_e, collect(vdof_indices)) + end + edofs_e = collect(Ferrite.edgedof_interior_indices(ip)[i]) + @test Set(Ferrite.edgedof_indices(ip)[i]) == Set(vcat(vdofs_e, edofs_e)) + end + + # Face numbered next, no gaps or missing numbers. Sorted by face number. + all_fdofs_i = vcat(collect.(fdofs_i)...) + @test all(all_fdofs_i .== length(all_edofs) .+ (1:length(all_fdofs_i))) + # - all face dofs include edgedofs and interior facedofs, and nothing more. + all_fdofs = vcat(collect.(fdofs)...) + @test all(all_fdofs .== 1:length(all_fdofs)) + # - test each face individually + for i in 1:Ferrite.nfaces(RefShape) + face_verts = Ferrite.reference_faces(RefShape)[i] + vdofs_f = Int[] + for j in face_verts + vdof_indices = Ferrite.vertexdof_indices(ip)[j] + isempty(vdof_indices) || append!(vdofs_f, collect(vdof_indices)) + end + edofs_f = Int[] + for (edgenr, edge) in enumerate(Ferrite.reference_edges(RefShape)) + (edge[1] ∈ face_verts && edge[2] ∈ face_verts) || continue + append!(edofs_f, collect(Ferrite.edgedof_interior_indices(ip)[edgenr])) + end + fdofs_f = collect(Ferrite.facedof_interior_indices(ip)[i]) + @test Set(Ferrite.facedof_indices(ip)[i]) == Set(vcat(vdofs_f, edofs_f, fdofs_f)) + end + + # Volumedofs numbered last + voldofs = vcat(all_fdofs, collect(voldofs_i)) + @test length(voldofs) == getnbasefunctions(ip) # Correct total number of dofs + @test all(voldofs .== 1:length(voldofs)) # Numbering convention + + # All base functions implemented. Argument errors for 0th and n+1 indices. + ξ = zero(Vec{Ferrite.getrefdim(ip)}) + @test_throws ArgumentError Ferrite.reference_shape_value(ip, ξ, 0) + for i in 1:getnbasefunctions(ip) + @test Ferrite.reference_shape_value(ip, ξ, i) isa Ferrite.shape_value_type(ip, Float64) + end + @test_throws ArgumentError Ferrite.reference_shape_value(ip, ξ, getnbasefunctions(ip) + 1) + + end + Hcurl_interpolations = [Nedelec{2, RefTriangle, 1}(), Nedelec{2, RefTriangle, 2}()] # Nedelec{3, RefTetrahedron, 1}(), Nedelec{3, RefHexahedron, 1}()] Hdiv_interpolations = [RaviartThomas{2, RefTriangle, 1}(), RaviartThomas{2, RefTriangle, 2}(), BrezziDouglasMarini{2, RefTriangle, 1}()] + + test_interpolation_numbering.(Hcurl_interpolations) + test_interpolation_numbering.(Hdiv_interpolations) + # Required properties of shape value Nⱼ of an edge-elements (Hcurl) on an edge with direction v, length L, and dofs ∈ 𝔇 # 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1 ∀ ∈ 𝔇 # Must hold for @@ -313,8 +395,56 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end + function facet_parameterization(::Type{RefShape}, ξ, facet_id) where {RefShape <: Ferrite.AbstractRefShape{2}} + # facet = edge + return edge_parameterization(RefShape, ξ, facet_id) + end + """ + edge_parameterization(::Type{<:AbstractRefShape}, ξ, edge_id) + + An edge is parameterized by the normalized curve coordinate `s [0, 1]`, + increasing in the positive edge direction. + """ + function edge_parameterization(::Type{RefShape}, ξ, edge_id) where {RefShape <: Ferrite.AbstractRefShape} + ipg = Lagrange{RefShape, 1}() # Reference shape always described by 1st order Lagrange ip. + refcoords = Ferrite.reference_coordinates(ipg) + i1, i2 = Ferrite.edgedof_indices(ipg)[edge_id] + ξ1, ξ2 = (refcoords[i1], refcoords[i2]) + Δξ = ξ2 - ξ1 + L = norm(Δξ) + s = (ξ - ξ1) ⋅ normalize(Δξ) / L + @assert norm(ξ - ξ1) ≈ L * s # Ensure ξ is on the line ξ1 - ξ2 + @assert -eps(L) ≤ s ≤ (1 + eps(L)) # Ensure ξ is between ξ1 and ξ2 + return s + end + + function facet_parameterization(::Type{<:Ferrite.AbstractRefShape{3}}, ξ, facet_id) + # Not implemented (not yet defined in Ferrite what this should be), + # but to support testing interpolations with a single facedof interior index, + # we return `nothing` just to allow running the code as long as the output isn't used. + return nothing + end + + function integrate_facet(fv::FacetValues, f::Function, shapenr::Int, cell::Ferrite.AbstractCell{RefShape}) where {RefShape} + facet_id = Ferrite.getcurrentfacet(fv) + function qpoint_contribution(q_point) + ξ = Ferrite.getpoints(fv.fqr, facet_id)[q_point] + # facet parameterization: 1D [0, 1], 2D ([0, 1], [0, 1]) + s = facet_parameterization(RefShape, ξ, facet_id) + n = getnormal(fv, q_point) + facet_sign = Ferrite.get_direction(Ferrite.function_interpolation(fv), shapenr, cell) + N = shape_value(fv, q_point, shapenr) * facet_sign # Ensure no reorientation. + return f(s, N, n) * getdetJdV(fv, q_point) + end + val = qpoint_contribution(1) + for q_point in 2:getnquadpoints(fv) + val += qpoint_contribution(q_point) + end + return val + end + # Required properties of shape value Nⱼ of an edge-elements (Hdiv) on an edge with normal n, length L, and dofs ∈ 𝔇 - # 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1/length(𝔇) ∀ j ∈ 𝔇 + # 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 ∀ j ∈ 𝔇 # Must hold for # length(𝔇) ≥ 1: f(s) = 1 # length(𝔇) = 2: f(s) = 1 - s or f(s) = s for 1st and 2nd dof, respectively. @@ -323,28 +453,32 @@ using Ferrite: reference_shape_value, reference_shape_gradient # s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero normal component on other edges: Nⱼ ⋅ n = 0 if j∉𝔇 @testset "H(div) on RefCell" begin - lineqr = QuadratureRule{RefLine}(20) + reference_moment_functions(::RaviartThomas{2, RefTriangle, 1}) = (Returns(1.0),) + reference_moment_functions(::RaviartThomas{2, RefTriangle, 2}) = (s -> 1 - s, s -> s) + reference_moment_functions(::BrezziDouglasMarini{2, RefTriangle, 1}) = (s -> 1 - s, s -> s) + for ip in Hdiv_interpolations cell = reference_cell(getrefshape(ip)) + fqr = FacetQuadratureRule{getrefshape(ip)}(4) + fv = FacetValues(fqr, ip, Lagrange{getrefshape(ip), 1}()) cell_facets = Ferrite.facets(cell) dofs = Ferrite.facetdof_interior_indices(ip) x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) normals = reference_normals(geometric_interpolation(typeof(cell))) @testset "$ip" begin for (facet_nr, (i1, i2)) in enumerate(cell_facets) + reinit!(fv, reference_cell(getrefshape(ip)), x, facet_nr) @testset "Facet $facet_nr" begin - Δx = x[i2] - x[i1] - x0 = (x[i1] + x[i2]) / 2 - L = norm(Δx) n = normals[facet_nr] - for (idof, shape_nr) in enumerate(dofs[facet_nr]) - nfacetdofs = length(dofs[facet_nr]) - f(x) = nfacetdofs == 1 ? 1.0 : (idof == 1 ? 1 - x : x) - s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, f) - @test s ≈ one(s) - if nfacetdofs == 2 - g(x) = idof == 1 ? x : 1 - x - @test 1 ≈ 1 + line_integral(lineqr, ip, shape_nr, x0, Δx, L, n, g) + Δx = x[i2] - x[i1] + for (rmf_idx, rm_fun) in enumerate(reference_moment_functions(ip)) + f(s, N, nq) = rm_fun(s) * (N ⋅ nq) + for (idof, shape_nr) in enumerate(dofs[facet_nr]) + if idof == rmf_idx + @test 1 ≈ integrate_facet(fv, f, shape_nr, cell) + else + @test 1 ≈ 1 + integrate_facet(fv, f, shape_nr, cell) + end end end for (j_facet, shape_nrs) in enumerate(dofs) @@ -497,7 +631,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test_throws ArgumentError Ferrite.facedof_interior_indices(ip) @test_throws ArgumentError Ferrite.volumedof_interior_indices(ip) @test_throws ArgumentError Ferrite.facetdof_interior_indices(ip) - end + end # =# end # testset From ac0d5ad98fdde12a6441165c427f8c89b45bed3a Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 11 Dec 2024 22:36:23 +0100 Subject: [PATCH 153/172] Try using K[fdofs,fdofs] for maxwell eigenvalue, but didn't work... --- docs/src/literate-tutorials/maxwell.jl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl index a959416094..dac321d35d 100644 --- a/docs/src/literate-tutorials/maxwell.jl +++ b/docs/src/literate-tutorials/maxwell.jl @@ -70,7 +70,7 @@ function doassemble!(A, B, dh, cv) end function setup_and_assemble(ip::VectorInterpolation{2, RefTriangle}) - grid = generate_grid(Triangle, (10, 10)) + grid = generate_grid(Triangle, (40, 40), zero(Vec{2}), π * ones(Vec{2})) cv = CellValues(QuadratureRule{RefTriangle}(2), ip, geometric_interpolation(Triangle)) dh = close!(add!(DofHandler(grid), :u, ip)) ∂Ω = union((getfacetset(grid, k) for k in ("left", "top", "right", "bottom"))...) @@ -81,14 +81,17 @@ function setup_and_assemble(ip::VectorInterpolation{2, RefTriangle}) A = allocate_matrix(sp) B = allocate_matrix(sp) doassemble!(A, B, dh, cv) - Ferrite.zero_out_rows!(B, ch.dofmapping) - Ferrite.zero_out_columns!(B, ch.prescribed_dofs) - return A, B, dh + #Ferrite.zero_out_rows!(B, ch.dofmapping) + #Ferrite.zero_out_columns!(B, ch.prescribed_dofs) + fdofs = ch.free_dofs + return A, B, dh, fdofs end ip = Nedelec{2, RefTriangle, 1}() +#ip = Lagrange{RefTriangle, 1}()^2 -A, B, dh = setup_and_assemble(ip) - -# vals, vecs, info = geneigsolve((A, B), 1, EigSorter(x -> abs(x - 5.0)); maxiter = 1000); -# λ, ϕ = Arpack.eigs(A, B, nev = 2, sigma=5.5); +A, B, dh, fdofs = setup_and_assemble(ip) +Aff = A[fdofs, fdofs] +Bff = B[fdofs, fdofs] +# vals, vecs, info = geneigsolve((Aff, Bff), 10, EigSorter(x -> abs(x - 5.0)); maxiter = 1000); +# λ, ϕ = Arpack.eigs(Aff, Bff, nev = 2, sigma=5.5); From f866a41665c708ba8d000cb18ec4588527da7d52 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sun, 22 Dec 2024 12:00:34 +0100 Subject: [PATCH 154/172] Fix link --- docs/src/literate-tutorials/maxwell.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl index dac321d35d..c429e146d9 100644 --- a/docs/src/literate-tutorials/maxwell.jl +++ b/docs/src/literate-tutorials/maxwell.jl @@ -12,7 +12,7 @@ #= **Not working:** Maybe try the standard laplace eigenvalue problem -https://www.cambridge.org/core/services/aop-cambridge-core/content/view/4BD87CC520C7E11CF402981AA58D77E2/S0962492910000012a.pdf/finite-element-approximation-of-eigenvalue-problems.pdf +[https://www.cambridge.org/core/services/aop-cambridge-core/content/view/4BD87CC520C7E11CF402981AA58D77E2/S0962492910000012a.pdf/finite-element-approximation-of-eigenvalue-problems.pdf](https://www.cambridge.org/core/services/aop-cambridge-core/content/view/4BD87CC520C7E11CF402981AA58D77E2/S0962492910000012a.pdf/finite-element-approximation-of-eigenvalue-problems.pdf) # Maxwell eigenvalue problem Strong form From 92b60391d46d5f97ef1f2459cd0e39a71d0904fb Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Mon, 23 Dec 2024 16:25:34 +0100 Subject: [PATCH 155/172] Added new tutorial attempt for nedelec, WIP and docs will fail due to missing deps --- docs/Manifest.toml | 572 +++++++++++++++++- docs/Project.toml | 5 + .../maxwell_good_bad_ugly.jl | 286 +++++++++ 3 files changed, 851 insertions(+), 12 deletions(-) create mode 100644 docs/src/literate-tutorials/maxwell_good_bad_ugly.jl diff --git a/docs/Manifest.toml b/docs/Manifest.toml index b19c814d6a..3f807e169b 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.11.1" +julia_version = "1.11.2" manifest_format = "2.0" -project_hash = "c8cd4f9010376e633064839ca68de8e4b622021f" +project_hash = "3d1b9374e40999c3125a0a2372919c3af4f3212e" [[deps.ADTypes]] git-tree-sha1 = "eea5d80188827b35333801ef97a40c2ed653b081" @@ -19,6 +19,17 @@ git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" +[[deps.AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.5.0" +weakdeps = ["ChainRulesCore", "Test"] + + [deps.AbstractFFTs.extensions] + AbstractFFTsChainRulesCoreExt = "ChainRulesCore" + AbstractFFTsTestExt = "Test" + [[deps.AbstractTrees]] git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -59,6 +70,23 @@ weakdeps = ["StaticArrays"] [deps.Adapt.extensions] AdaptStaticArraysExt = "StaticArrays" +[[deps.AdaptivePredicates]] +git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6" +uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" +version = "1.2.0" + +[[deps.AliasTables]] +deps = ["PtrArrays", "Random"] +git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" +uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" +version = "1.1.3" + +[[deps.Animations]] +deps = ["Colors"] +git-tree-sha1 = "e092fa223bf66a3c41f9c022bd074d916dc303e7" +uuid = "27a7e980-b3e6-11e9-2bcd-0b925532e340" +version = "0.4.2" + [[deps.ArgTools]] uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" version = "1.1.2" @@ -125,6 +153,24 @@ weakdeps = ["SparseArrays"] uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" version = "1.11.0" +[[deps.Automa]] +deps = ["PrecompileTools", "SIMD", "TranscodingStreams"] +git-tree-sha1 = "a8f503e8e1a5f583fbef15a8440c8c7e32185df2" +uuid = "67c07d97-cdcb-5c2c-af73-a7f9c32a568b" +version = "1.1.0" + +[[deps.AxisAlgorithms]] +deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"] +git-tree-sha1 = "01b8ccb13d68535d73d2b0c23e39bd23155fb712" +uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950" +version = "1.1.0" + +[[deps.AxisArrays]] +deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"] +git-tree-sha1 = "16351be62963a67ac4083f748fdb3cca58bfd52f" +uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9" +version = "0.4.7" + [[deps.BangBang]] deps = ["Accessors", "ConstructionBase", "InitialValues", "LinearAlgebra", "Requires"] git-tree-sha1 = "e2144b631226d9eeab2d746ca8880b7ccff504ae" @@ -197,12 +243,39 @@ git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" version = "1.0.8+1" +[[deps.CEnum]] +git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.5.0" + [[deps.CPUSummary]] deps = ["CpuId", "IfElse", "PrecompileTools", "Static"] git-tree-sha1 = "5a97e67919535d6841172016c9530fd69494e5ec" uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" version = "0.2.6" +[[deps.CRC32c]] +uuid = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" +version = "1.11.0" + +[[deps.CRlibm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" +uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" +version = "1.0.1+0" + +[[deps.Cairo]] +deps = ["Cairo_jll", "Colors", "Glib_jll", "Graphics", "Libdl", "Pango_jll"] +git-tree-sha1 = "71aa551c5c33f1a4415867fe06b7844faadb0ae9" +uuid = "159f3aea-2a34-519c-b102-8c37f9878175" +version = "1.1.1" + +[[deps.CairoMakie]] +deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] +git-tree-sha1 = "7947d2b61995eda7d5ca50c697b12bb578b918e5" +uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +version = "0.12.14" + [[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"] git-tree-sha1 = "009060c9a6168704143100f36ab08f06c2af4642" @@ -241,6 +314,12 @@ git-tree-sha1 = "bce6804e5e6044c6daab27bb533d1295e4a2e759" uuid = "944b1d66-785c-5afd-91f1-9de20f533193" version = "0.7.6" +[[deps.ColorBrewer]] +deps = ["Colors", "JSON", "Test"] +git-tree-sha1 = "61c5334f33d91e570e1d0c3eb5465835242582c4" +uuid = "a2cac450-b92f-5266-8821-25eda20663c8" +version = "0.4.0" + [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" @@ -324,17 +403,13 @@ version = "2.4.2" git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" version = "1.5.8" +weakdeps = ["IntervalSets", "LinearAlgebra", "StaticArrays"] [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]] git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" @@ -373,6 +448,12 @@ git-tree-sha1 = "fc173b380865f70627d7dd1190dc2fce6cc105af" uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" version = "1.14.10+0" +[[deps.DelaunayTriangulation]] +deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"] +git-tree-sha1 = "e1371a23fd9816080c828d0ce04373857fe73d33" +uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" +version = "1.6.3" + [[deps.DelimitedFiles]] deps = ["Mmap"] git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" @@ -481,6 +562,22 @@ deps = ["Random", "Serialization", "Sockets"] uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" version = "1.11.0" +[[deps.Distributions]] +deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] +git-tree-sha1 = "4b138e4643b577ccf355377c2bc70fa975af25de" +uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" +version = "0.25.115" + + [deps.Distributions.extensions] + DistributionsChainRulesCoreExt = "ChainRulesCore" + DistributionsDensityInterfaceExt = "DensityInterface" + DistributionsTestExt = "Test" + + [deps.Distributions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + [[deps.DocStringExtensions]] deps = ["LibGit2"] git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" @@ -504,6 +601,12 @@ deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" version = "1.6.0" +[[deps.EarCut_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e3290f2d49e661fbd94046d7e3726ffcb2d41053" +uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5" +version = "2.2.4+0" + [[deps.EnumX]] git-tree-sha1 = "bdb1942cd4c45e3c678fd11569d5cccd80976237" uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" @@ -524,6 +627,12 @@ git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643" uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" version = "0.0.20230411+0" +[[deps.ExactPredicates]] +deps = ["IntervalArithmetic", "Random", "StaticArrays"] +git-tree-sha1 = "b3f2ff58735b5f024c392fde763f29b057e4b025" +uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" +version = "2.2.8" + [[deps.ExceptionUnwrapping]] deps = ["Test"] git-tree-sha1 = "dcb08a0d93ec0b1cdc4af184b26b591e9695423a" @@ -553,6 +662,11 @@ git-tree-sha1 = "fc3951d4d398b5515f91d7fe5d45fc31dccb3c9b" uuid = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636" version = "0.8.5" +[[deps.Extents]] +git-tree-sha1 = "81023caa0021a41712685887db1fc03db26f41f5" +uuid = "411431e0-e8b7-467b-b5e0-f676ba4f2910" +version = "0.1.4" + [[deps.FFMPEG]] deps = ["FFMPEG_jll"] git-tree-sha1 = "53ebe7511fa11d33bec688a9178fac4e49eeee00" @@ -565,6 +679,18 @@ git-tree-sha1 = "466d45dc38e15794ec7d5d63ec03d776a9aff36e" uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" version = "4.4.4+1" +[[deps.FFTW]] +deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "4820348781ae578893311153d69049a93d05f39d" +uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +version = "1.8.0" + +[[deps.FFTW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" +uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" +version = "3.3.10+1" + [[deps.FLTK_jll]] deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "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 = "72a4842f93e734f378cf381dae2ca4542f019d23" @@ -602,6 +728,12 @@ version = "1.0.0" BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b" +[[deps.FerriteAssembly]] +deps = ["Ferrite", "ForwardDiff", "MaterialModelsBase"] +path = "../../FerriteAssembly" +uuid = "fd21fc07-c509-4fe1-9468-19963fd5935d" +version = "0.3.4" + [[deps.FerriteGmsh]] deps = ["Ferrite", "Gmsh"] git-tree-sha1 = "9669f21d4ddc68ffca0d5ea12d3ac6b438b9af06" @@ -614,12 +746,35 @@ git-tree-sha1 = "54a647bf423475c6a54d1960bf694880953d27e9" uuid = "0f8c756f-80dd-4a75-85c6-b0a5ab9d4620" version = "0.2.0" +[[deps.FerriteTriangulation]] +deps = ["Ferrite", "Tensors"] +path = "../../FerriteTriangulation" +uuid = "b200c708-61b9-46d9-917a-515144ac7aac" +version = "0.1.0" + [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" version = "1.16.3" +[[deps.FilePaths]] +deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"] +git-tree-sha1 = "919d9412dbf53a2e6fe74af62a73ceed0bce0629" +uuid = "8fc22ac5-c921-52a6-82fd-178b2807b824" +version = "0.8.3" + +[[deps.FilePathsBase]] +deps = ["Compat", "Dates"] +git-tree-sha1 = "7878ff7172a8e6beedd1dea14bd27c3c6340d361" +uuid = "48062228-2e41-5def-b9a4-89aafe57970f" +version = "0.9.22" +weakdeps = ["Mmap", "Test"] + + [deps.FilePathsBase.extensions] + FilePathsBaseMmapExt = "Mmap" + FilePathsBaseTestExt = "Test" + [[deps.FileWatching]] uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" version = "1.11.0" @@ -629,17 +784,13 @@ deps = ["LinearAlgebra"] git-tree-sha1 = "6a70198746448456524cb442b8af316927ff3e1a" uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" version = "1.13.0" +weakdeps = ["PDMats", "SparseArrays", "Statistics"] [deps.FillArrays.extensions] FillArraysPDMatsExt = "PDMats" FillArraysSparseArraysExt = "SparseArrays" FillArraysStatisticsExt = "Statistics" - [deps.FillArrays.weakdeps] - PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" - SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - [[deps.FiniteDiff]] deps = ["ArrayInterface", "LinearAlgebra", "Setfield"] git-tree-sha1 = "b10bdafd1647f57ace3885143936749d61638c3b" @@ -685,12 +836,24 @@ weakdeps = ["StaticArrays"] [deps.ForwardDiff.extensions] ForwardDiffStaticArraysExt = "StaticArrays" +[[deps.FreeType]] +deps = ["CEnum", "FreeType2_jll"] +git-tree-sha1 = "907369da0f8e80728ab49c1c7e09327bf0d6d999" +uuid = "b38be410-82b0-50bf-ab77-7b57e271db43" +version = "4.1.1" + [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" version = "2.13.2+0" +[[deps.FreeTypeAbstraction]] +deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] +git-tree-sha1 = "d52e255138ac21be31fa633200b65e4e71d26802" +uuid = "663a7486-cb36-511b-a19d-713bb74d65c9" +version = "0.10.6" + [[deps.FriBidi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" @@ -754,12 +917,35 @@ git-tree-sha1 = "af49a0851f8113fcfae2ef5027c6d49d0acec39b" uuid = "c145ed77-6b09-5dd9-b285-bf645a82121e" version = "0.5.4" +[[deps.GeoFormatTypes]] +git-tree-sha1 = "59107c179a586f0fe667024c5eb7033e81333271" +uuid = "68eda718-8dee-11e9-39e7-89f7f65f511f" +version = "0.4.2" + +[[deps.GeoInterface]] +deps = ["DataAPI", "Extents", "GeoFormatTypes"] +git-tree-sha1 = "f4ee66b6b1872a4ca53303fbb51d158af1bf88d4" +uuid = "cf35fbd7-0cd7-5166-be24-54bfbe79505f" +version = "1.4.0" + +[[deps.GeometryBasics]] +deps = ["EarCut_jll", "Extents", "GeoInterface", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "b62f2b2d76cee0d61a2ef2b3118cd2a3215d3134" +uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326" +version = "0.4.11" + [[deps.Gettext_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" version = "0.21.0+0" +[[deps.Giflib_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0224cce99284d997f6880a42ef715a37c99338d1" +uuid = "59f7168a-df46-5410-90c8-f2779963d0ec" +version = "5.2.2+0" + [[deps.Git]] deps = ["Git_jll"] git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" @@ -784,6 +970,12 @@ git-tree-sha1 = "6d815101e62722f4e323514c9fc704007d4da2e3" uuid = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" version = "0.3.1" +[[deps.Graphics]] +deps = ["Colors", "LinearAlgebra", "NaNMath"] +git-tree-sha1 = "a641238db938fff9b2f60d08ed9030387daf428c" +uuid = "a2bd30eb-e257-5431-a919-1863eab51364" +version = "1.1.3" + [[deps.Graphite2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" @@ -796,6 +988,12 @@ git-tree-sha1 = "1dc470db8b1131cfc7fb4c115de89fe391b9e780" uuid = "86223c79-3864-5bf0-83f7-82e725a168b6" version = "1.12.0" +[[deps.GridLayoutBase]] +deps = ["GeometryBasics", "InteractiveUtils", "Observables"] +git-tree-sha1 = "dc6bed05c15523624909b3953686c5f5ffa10adc" +uuid = "3955a311-db13-416c-9275-1d80ed98e5e9" +version = "0.11.1" + [[deps.Grisu]] git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" @@ -831,6 +1029,12 @@ git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" version = "2.11.2+0" +[[deps.HypergeometricFunctions]] +deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] +git-tree-sha1 = "b1c2585431c382e3fe5805874bda6aea90a95de9" +uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" +version = "0.3.25" + [[deps.IOCapture]] deps = ["Logging", "Random"] git-tree-sha1 = "b6d6bfdd7ce25b0f9b2f6b3dd56b2673a66c8770" @@ -842,6 +1046,47 @@ git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" version = "0.1.1" +[[deps.ImageAxes]] +deps = ["AxisArrays", "ImageBase", "ImageCore", "Reexport", "SimpleTraits"] +git-tree-sha1 = "e12629406c6c4442539436581041d372d69c55ba" +uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac" +version = "0.6.12" + +[[deps.ImageBase]] +deps = ["ImageCore", "Reexport"] +git-tree-sha1 = "eb49b82c172811fd2c86759fa0553a2221feb909" +uuid = "c817782e-172a-44cc-b673-b171935fbb9e" +version = "0.1.7" + +[[deps.ImageCore]] +deps = ["ColorVectorSpace", "Colors", "FixedPointNumbers", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "PrecompileTools", "Reexport"] +git-tree-sha1 = "8c193230235bbcee22c8066b0374f63b5683c2d3" +uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534" +version = "0.10.5" + +[[deps.ImageIO]] +deps = ["FileIO", "IndirectArrays", "JpegTurbo", "LazyModules", "Netpbm", "OpenEXR", "PNGFiles", "QOI", "Sixel", "TiffImages", "UUIDs", "WebP"] +git-tree-sha1 = "696144904b76e1ca433b886b4e7edd067d76cbf7" +uuid = "82e4d734-157c-48bb-816b-45c225c6df19" +version = "0.6.9" + +[[deps.ImageMetadata]] +deps = ["AxisArrays", "ImageAxes", "ImageBase", "ImageCore"] +git-tree-sha1 = "2a81c3897be6fbcde0802a0ebe6796d0562f63ec" +uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49" +version = "0.9.10" + +[[deps.Imath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0936ba688c6d201805a83da835b55c61a180db52" +uuid = "905a6f67-0a94-5f89-b386-d35d92009cd1" +version = "3.1.11+0" + +[[deps.IndirectArrays]] +git-tree-sha1 = "012e604e1c7458645cb8b436f8fba789a51b257f" +uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959" +version = "1.0.0" + [[deps.Inflate]] git-tree-sha1 = "d1b1b796e47d94588b3757fe84fbf65a5ec4a80d" uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" @@ -863,6 +1108,40 @@ deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" version = "1.11.0" +[[deps.Interpolations]] +deps = ["Adapt", "AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"] +git-tree-sha1 = "88a101217d7cb38a7b481ccd50d21876e1d1b0e0" +uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" +version = "0.15.1" +weakdeps = ["Unitful"] + + [deps.Interpolations.extensions] + InterpolationsUnitfulExt = "Unitful" + +[[deps.IntervalArithmetic]] +deps = ["CRlibm_jll", "LinearAlgebra", "MacroTools", "RoundingEmulator"] +git-tree-sha1 = "3a272409c5f7fb864f830b3bb231b0a4d32747eb" +uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +version = "0.22.20" +weakdeps = ["DiffRules", "ForwardDiff", "IntervalSets", "RecipesBase"] + + [deps.IntervalArithmetic.extensions] + IntervalArithmeticDiffRulesExt = "DiffRules" + IntervalArithmeticForwardDiffExt = "ForwardDiff" + IntervalArithmeticIntervalSetsExt = "IntervalSets" + IntervalArithmeticRecipesBaseExt = "RecipesBase" + +[[deps.IntervalSets]] +git-tree-sha1 = "dba9ddf07f77f60450fe5d2e2beb9854d9a49bd0" +uuid = "8197267c-284f-5f27-9208-e0e47529a953" +version = "0.7.10" +weakdeps = ["Random", "RecipesBase", "Statistics"] + + [deps.IntervalSets.extensions] + IntervalSetsRandomExt = "Random" + IntervalSetsRecipesBaseExt = "RecipesBase" + IntervalSetsStatisticsExt = "Statistics" + [[deps.InverseFunctions]] git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb" uuid = "3587e190-3f89-42d0-90ee-14403ec27112" @@ -878,6 +1157,17 @@ git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" version = "0.2.2" +[[deps.Isoband]] +deps = ["isoband_jll"] +git-tree-sha1 = "f9b6d97355599074dc867318950adaa6f9946137" +uuid = "f1662d9f-8043-43de-a69a-05efc1cc6ff4" +version = "0.1.1" + +[[deps.IterTools]] +git-tree-sha1 = "42d5f897009e7ff2cf88db414a389e5ed1bdd023" +uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e" +version = "1.10.0" + [[deps.IterativeSolvers]] deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] git-tree-sha1 = "59545b0a2b27208b0650df0a46b8e3019f85055b" @@ -925,6 +1215,12 @@ git-tree-sha1 = "243f1cdb476835d7c249deb9f29ad6b7827da7d3" uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692" version = "1.4.1" +[[deps.JpegTurbo]] +deps = ["CEnum", "FileIO", "ImageCore", "JpegTurbo_jll", "TOML"] +git-tree-sha1 = "fa6d0bcff8583bac20f1ffa708c3913ca605c611" +uuid = "b835a17e-a41a-41e7-81f0-2f016b05efe0" +version = "0.1.5" + [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" @@ -937,6 +1233,12 @@ git-tree-sha1 = "07649c499349dad9f08dde4243a4c597064663e9" uuid = "ef3ab10e-7fda-4108-b977-705223b18434" version = "0.6.0" +[[deps.KernelDensity]] +deps = ["Distributions", "DocStringExtensions", "FFTW", "Interpolations", "StatsBase"] +git-tree-sha1 = "7d703202e65efa1369de1279c162b915e245eed1" +uuid = "5ab0869b-81aa-558d-bb23-cbf5423bbe9b" +version = "0.6.9" + [[deps.Krylov]] deps = ["LinearAlgebra", "Printf", "SparseArrays"] git-tree-sha1 = "267dad6b4b7b5d529c76d40ff48d33f7e94cb834" @@ -1032,6 +1334,11 @@ deps = ["Artifacts", "Pkg"] uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" version = "1.11.0" +[[deps.LazyModules]] +git-tree-sha1 = "a560dd966b386ac9ae60bdd3a3d3a326062d3c3e" +uuid = "8cdb02fc-e678-4876-92c5-9defec4f444e" +version = "0.3.1" + [[deps.LibCURL]] deps = ["LibCURL_jll", "MozillaCACerts_jll"] uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" @@ -1279,11 +1586,28 @@ git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.13" +[[deps.Makie]] +deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"] +git-tree-sha1 = "3df66da15ba7b37b34f6557b7e1c95a3ff5c670b" +uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" +version = "0.21.14" + +[[deps.MakieCore]] +deps = ["ColorTypes", "GeometryBasics", "IntervalSets", "Observables"] +git-tree-sha1 = "4604f03e5b057e8e62a95a44929cafc9585b0fe9" +uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" +version = "0.8.9" + [[deps.ManualMemory]] git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" uuid = "d125e4d3-2237-4719-b19c-fa641b8a4667" version = "0.1.8" +[[deps.MappedArrays]] +git-tree-sha1 = "2dab0221fe2b0f2cb6754eaa743cc266339f527e" +uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" +version = "0.4.2" + [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" @@ -1295,6 +1619,18 @@ git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" version = "0.1.2" +[[deps.MaterialModelsBase]] +deps = ["StaticArrays", "Tensors"] +path = "../../MaterialModelsBase" +uuid = "af893363-701d-44dc-8b1e-d9a2c129bfc9" +version = "0.2.2" + +[[deps.MathTeXEngine]] +deps = ["AbstractTrees", "Automa", "DataStructures", "FreeTypeAbstraction", "GeometryBasics", "LaTeXStrings", "REPL", "RelocatableFolders", "UnicodeFun"] +git-tree-sha1 = "f45c8916e8385976e1ccd055c9874560c257ab13" +uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" +version = "0.6.2" + [[deps.MaybeInplace]] deps = ["ArrayInterface", "LinearAlgebra", "MacroTools"] git-tree-sha1 = "54e2fdc38130c05b42be423e90da3bade29b74bd" @@ -1337,6 +1673,12 @@ version = "1.2.0" uuid = "a63ad114-7e13-5084-954f-fe012c677804" version = "1.11.0" +[[deps.MosaicViews]] +deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"] +git-tree-sha1 = "7b86a5d4d70a9f5cdf2dacb3cbe6d251d1a61dbe" +uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389" +version = "0.3.4" + [[deps.MozillaCACerts_jll]] uuid = "14a3606d-f60d-562e-9121-12d972cd8159" version = "2023.12.12" @@ -1364,6 +1706,12 @@ git-tree-sha1 = "3cebfc94a0754cc329ebc3bab1e6c89621e791ad" uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" version = "0.4.20" +[[deps.Netpbm]] +deps = ["FileIO", "ImageCore", "ImageMetadata"] +git-tree-sha1 = "d92b107dbb887293622df7697a2223f9f8176fcd" +uuid = "f09324ee-3d7c-5217-9330-fc30815ba969" +version = "1.1.1" + [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" @@ -1402,6 +1750,11 @@ git-tree-sha1 = "bef34b68c20cc34475c5cb464ab48555e74f4c61" uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" version = "7.7.2+0" +[[deps.Observables]] +git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" +uuid = "510215fc-4207-5dde-b226-833fc4488ee2" +version = "0.5.5" + [[deps.OffsetArrays]] git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" @@ -1428,6 +1781,18 @@ deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" version = "0.3.27+1" +[[deps.OpenEXR]] +deps = ["Colors", "FileIO", "OpenEXR_jll"] +git-tree-sha1 = "97db9e07fe2091882c765380ef58ec553074e9c7" +uuid = "52e1d378-f018-4a11-a4be-720524705ac7" +version = "0.3.3" + +[[deps.OpenEXR_jll]] +deps = ["Artifacts", "Imath_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "8292dd5c8a38257111ada2174000a33745b06d4e" +uuid = "18a262bb-aa17-5467-a713-aee519bc75cb" +version = "3.2.4+0" + [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" @@ -1675,12 +2040,36 @@ deps = ["Artifacts", "Libdl"] uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" version = "10.42.0+1" +[[deps.PDMats]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "949347156c25054de2db3b166c52ac4728cbad65" +uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" +version = "0.11.31" + +[[deps.PNGFiles]] +deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"] +git-tree-sha1 = "67186a2bc9a90f9f85ff3cc8277868961fb57cbd" +uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883" +version = "0.4.3" + [[deps.PackageExtensionCompat]] git-tree-sha1 = "fb28e33b8a95c4cee25ce296c817d89cc2e53518" uuid = "65ce6f38-6b18-4e1d-a461-8949797d7930" version = "1.0.2" weakdeps = ["Requires", "TOML"] +[[deps.Packing]] +deps = ["GeometryBasics"] +git-tree-sha1 = "bc5bf2ea3d5351edf285a06b0016788a121ce92c" +uuid = "19eb6ba3-879d-56ad-ad62-d5c202156566" +version = "0.5.1" + +[[deps.PaddedViews]] +deps = ["OffsetArrays"] +git-tree-sha1 = "0fac6313486baae819364c52b4f483450a9d793f" +uuid = "5432bcbf-9aad-5242-b902-cca2824c8663" +version = "0.5.12" + [[deps.Pango_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] git-tree-sha1 = "e127b609fb9ecba6f201ba7ab753d5a605d53801" @@ -1719,6 +2108,12 @@ weakdeps = ["REPL"] [deps.Pkg.extensions] REPLExt = "REPL" +[[deps.PkgVersion]] +deps = ["Pkg"] +git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da" +uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" +version = "0.3.3" + [[deps.PlotThemes]] deps = ["PlotUtils", "Statistics"] git-tree-sha1 = "6e55c6841ce3411ccb3457ee52fc48cb698d6fb0" @@ -1763,6 +2158,11 @@ git-tree-sha1 = "645bed98cd47f72f67316fd42fc47dee771aefcd" uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" version = "0.2.2" +[[deps.PolygonOps]] +git-tree-sha1 = "77b3d3605fc1cd0b42d95eba87dfcd2bf67d5ff6" +uuid = "647866c9-e3ac-4575-94e7-e3d426903924" +version = "0.1.2" + [[deps.PositiveFactorizations]] deps = ["LinearAlgebra"] git-tree-sha1 = "17275485f373e6673f7e7f97051f703ed5b15b20" @@ -1804,6 +2204,17 @@ git-tree-sha1 = "8f6bc219586aef8baf0ff9a5fe16ee9c70cb65e4" uuid = "92933f4c-e287-5a05-a399-4b506db050ca" version = "1.10.2" +[[deps.PtrArrays]] +git-tree-sha1 = "77a42d78b6a92df47ab37e177b2deac405e1c88f" +uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" +version = "1.2.1" + +[[deps.QOI]] +deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] +git-tree-sha1 = "8b3fc30bc0390abdce15f8822c889f669baed73d" +uuid = "4b34888f-f399-49d4-9bb3-47ed5cae4e65" +version = "1.0.1" + [[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"] git-tree-sha1 = "492601870742dcd38f233b23c3ec629628c1d724" @@ -1828,6 +2239,18 @@ git-tree-sha1 = "729927532d48cf79f49070341e1d918a65aba6b0" uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3" version = "6.7.1+1" +[[deps.QuadGK]] +deps = ["DataStructures", "LinearAlgebra"] +git-tree-sha1 = "cda3b045cf9ef07a08ad46731f5a3165e56cf3da" +uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +version = "2.11.1" + + [deps.QuadGK.extensions] + QuadGKEnzymeExt = "Enzyme" + + [deps.QuadGK.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + [[deps.REPL]] deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" @@ -1838,6 +2261,21 @@ deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" version = "1.11.0" +[[deps.RangeArrays]] +git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5" +uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d" +version = "0.3.2" + +[[deps.Ratios]] +deps = ["Requires"] +git-tree-sha1 = "1342a47bf3260ee108163042310d26f2be5ec90b" +uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439" +version = "0.4.5" +weakdeps = ["FixedPointNumbers"] + + [deps.Ratios.extensions] + RatiosFixedPointNumbersExt = "FixedPointNumbers" + [[deps.RecipesBase]] deps = ["PrecompileTools"] git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" @@ -1905,6 +2343,23 @@ git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" uuid = "ae029012-a4dd-5104-9daa-d747884805df" version = "1.3.0" +[[deps.Rmath]] +deps = ["Random", "Rmath_jll"] +git-tree-sha1 = "852bd0f55565a9e973fcfee83a84413270224dc4" +uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" +version = "0.8.0" + +[[deps.Rmath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "58cdd8fb2201a6267e1db87ff148dd6c1dbd8ad8" +uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" +version = "0.5.1+0" + +[[deps.RoundingEmulator]] +git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" +uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" +version = "0.2.1" + [[deps.RuntimeGeneratedFunctions]] deps = ["ExprTools", "SHA", "Serialization"] git-tree-sha1 = "04c968137612c4a5629fa531334bb81ad5680f00" @@ -2002,6 +2457,12 @@ git-tree-sha1 = "e2cc6d8c88613c05e1defb55170bf5ff211fbeac" uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" version = "1.1.1" +[[deps.ShaderAbstractions]] +deps = ["ColorTypes", "FixedPointNumbers", "GeometryBasics", "LinearAlgebra", "Observables", "StaticArrays", "StructArrays", "Tables"] +git-tree-sha1 = "79123bc60c5507f035e6d1d9e563bb2971954ec8" +uuid = "65257c39-d410-5151-9873-9b3e5be5013e" +version = "0.4.1" + [[deps.SharedArrays]] deps = ["Distributed", "Mmap", "Random", "Serialization"] uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383" @@ -2013,6 +2474,12 @@ git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" version = "1.0.3" +[[deps.SignedDistanceFields]] +deps = ["Random", "Statistics", "Test"] +git-tree-sha1 = "d263a08ec505853a5ff1c1ebde2070419e3f28e9" +uuid = "73760f76-fbc4-59ce-8f25-708e95d2df96" +version = "0.4.0" + [[deps.SimpleBufferStream]] git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" @@ -2047,6 +2514,12 @@ git-tree-sha1 = "58e6353e72cde29b90a69527e56df1b5c3d8c437" uuid = "ce78b400-467f-4804-87d8-8f486da07d0a" version = "1.1.0" +[[deps.Sixel]] +deps = ["Dates", "FileIO", "ImageCore", "IndirectArrays", "OffsetArrays", "REPL", "libsixel_jll"] +git-tree-sha1 = "2da10356e31327c7096832eb9cd86307a50b1eb6" +uuid = "45858cf5-a6b0-47a3-bbea-62219f50df47" +version = "0.1.3" + [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" version = "1.11.0" @@ -2129,6 +2602,12 @@ git-tree-sha1 = "073d5c20d44129b20fe954720b97069579fa403b" uuid = "91464d47-22a1-43fe-8b7f-2d57ee82463f" version = "0.1.5" +[[deps.StackViews]] +deps = ["OffsetArrays"] +git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c" +uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15" +version = "0.1.1" + [[deps.Static]] deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools"] git-tree-sha1 = "87d51a3ee9a4b0d2fe054bdd3fc2436258db2603" @@ -2184,6 +2663,17 @@ git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" version = "0.34.3" +[[deps.StatsFuns]] +deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] +git-tree-sha1 = "b423576adc27097764a90e163157bcfc9acf0f46" +uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" +version = "1.3.2" +weakdeps = ["ChainRulesCore", "InverseFunctions"] + + [deps.StatsFuns.extensions] + StatsFunsChainRulesCoreExt = "ChainRulesCore" + StatsFunsInverseFunctionsExt = "InverseFunctions" + [[deps.StrideArraysCore]] deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] git-tree-sha1 = "f35f6ab602df8413a50c4a25ca14de821e8605fb" @@ -2196,6 +2686,19 @@ git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb" uuid = "69024149-9ee7-55f6-a4c4-859efe599b68" version = "0.3.7" +[[deps.StructArrays]] +deps = ["ConstructionBase", "DataAPI", "Tables"] +git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be" +uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" +version = "0.6.18" +weakdeps = ["Adapt", "GPUArraysCore", "SparseArrays", "StaticArrays"] + + [deps.StructArrays.extensions] + StructArraysAdaptExt = "Adapt" + StructArraysGPUArraysCoreExt = "GPUArraysCore" + StructArraysSparseArraysExt = "SparseArrays" + StructArraysStaticArraysExt = "StaticArrays" + [[deps.StructTypes]] deps = ["Dates", "UUIDs"] git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8" @@ -2206,6 +2709,10 @@ version = "1.11.0" uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" version = "1.11.0" +[[deps.SuiteSparse]] +deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] +uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" + [[deps.SuiteSparse_jll]] deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" @@ -2267,6 +2774,12 @@ git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" version = "0.5.2" +[[deps.TiffImages]] +deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "ProgressMeter", "SIMD", "UUIDs"] +git-tree-sha1 = "3c0faa42f2bd3c6d994b06286bba2328eae34027" +uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69" +version = "0.11.2" + [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] git-tree-sha1 = "3a6f063d690135f5c1ba351412c82bae4d1402bf" @@ -2289,6 +2802,11 @@ git-tree-sha1 = "7822b97e99a1672bfb1b49b668a6d46d58d8cbcb" uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" version = "0.1.9" +[[deps.TriplotBase]] +git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" +uuid = "981d1d27-644d-49a2-9326-4793e63143c3" +version = "0.1.0" + [[deps.TruncatedStacktraces]] deps = ["InteractiveUtils", "MacroTools", "Preferences"] git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" @@ -2383,6 +2901,18 @@ git-tree-sha1 = "93f43ab61b16ddfb2fd3bb13b3ce241cafb0e6c9" uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" version = "1.31.0+0" +[[deps.WebP]] +deps = ["CEnum", "ColorTypes", "FileIO", "FixedPointNumbers", "ImageCore", "libwebp_jll"] +git-tree-sha1 = "aa1ca3c47f119fbdae8770c29820e5e6119b83f2" +uuid = "e3aaa7dc-3e4b-44e0-be63-ffb868ccd7c1" +version = "0.1.3" + +[[deps.WoodburyMatrices]] +deps = ["LinearAlgebra", "SparseArrays"] +git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511" +uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6" +version = "1.0.0" + [[deps.WriteVTK]] deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] git-tree-sha1 = "e453383a1486a9020a3323bf3665ea31106ebe9a" @@ -2598,6 +3128,12 @@ git-tree-sha1 = "3516a5630f741c9eecb3720b1ec9d8edc3ecc033" uuid = "1a1c6b14-54f6-533d-8383-74cd7377aa70" version = "3.1.1+0" +[[deps.isoband_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "51b5eeb3f98367157a7a12a1fb0aa5328946c03c" +uuid = "9a68df92-36a6-505f-a73e-abb412b6bfb4" +version = "0.2.3+0" + [[deps.libaec_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" @@ -2651,12 +3187,24 @@ git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" version = "1.6.44+0" +[[deps.libsixel_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] +git-tree-sha1 = "7dfa0fd9c783d3d0cc43ea1af53d69ba45c447df" +uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" +version = "1.10.3+1" + [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] git-tree-sha1 = "490376214c4721cdaca654041f635213c6165cb3" uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" version = "1.3.7+2" +[[deps.libwebp_jll]] +deps = ["Artifacts", "Giflib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Libtiff_jll", "libpng_jll"] +git-tree-sha1 = "ccbb625a89ec6195856a50aa2b668a5c08712c94" +uuid = "c5f90fcd-3b7e-5836-afba-fc50a0988cb2" +version = "1.4.0+0" + [[deps.mtdev_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" diff --git a/docs/Project.toml b/docs/Project.toml index d21f86c895..abf2abb9ca 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,13 +1,17 @@ [deps] Arpack = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97" BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" Changelog = "5217a498-cd5d-4ec6-b8c2-9b85a09b6e3e" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" +FerriteAssembly = "fd21fc07-c509-4fe1-9468-19963fd5935d" FerriteGmsh = "4f95f4f8-b27c-4ae5-9a39-ea55e634e36b" FerriteMeshParser = "0f8c756f-80dd-4a75-85c6-b0a5ab9d4620" +FerriteTriangulation = "b200c708-61b9-46d9-917a-515144ac7aac" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" Gmsh = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" @@ -15,6 +19,7 @@ LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589" +MaterialModelsBase = "af893363-701d-44dc-8b1e-d9a2c129bfc9" OhMyThreads = "67456a42-1dca-4109-a031-0a68de7e3ad5" Optim = "429524aa-4258-5aef-a3af-852621145aeb" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" diff --git a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl new file mode 100644 index 0000000000..1d8040bea1 --- /dev/null +++ b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl @@ -0,0 +1,286 @@ +#= +# Maxwell Discretizations: The Good, The Bad, and The Ugly +This tutorial is based on Jay Gopalakrishnan (Portland State University) +[Maxwell Discretizations: The Good, The Bad & The Ugly](https://web.pdx.edu/~gjay/pub/MaxwellGoodBadUgly.html) +from the graduate course MTH 653: Advanced Numerical Analysis (Spring 2019) + +The purpose of the tutorial is to demonstrate how `Nedelec` vector interpolations will converge to the correct +solution for a Maxwell problem, when vectorized `Lagrange` interpolations converge to an incorrect solution. + +# ## Loading packages +We start by adding the required packages for this tutorial +=# +import Pkg +# Pkg.add(;url = ...) +using Ferrite, Tensors, ForwardDiff +using Gmsh, FerriteGmsh +using FerriteTriangulation: Triangulation, SubTriangulation +import CairoMakie as Plt +import GeometryBasics as GB + +using FerriteAssembly + +#= +## Introduction +Specifically, we will consider the problem to determine the vector-valued field +``\boldsymbol{E}``, such that +```math +\begin{align*} +\mathrm{curl}(\mathrm{curl}(\boldsymbol{E})) &= 0\quad \text{in }\Omega \\ +\mathrm{div}(\boldsymbol{E}) &= 0\quad \text{in }\Omega \\ +\boldsymbol{E}\cdot \boldsymbol{t} &= g\quad \text{on }\Gamma +\end{align*} +``` +where ``\boldsymbol{t}`` is the normalized tangential vector along +the boundary, ``\Gamma``, of the domain, ``\Omega``. +The rotated L-shaped domain is located such that the sharp internal corner +is at the origin. +=# +fig = Plt.Figure() +ax = Plt.Axis(fig[1, 1]; xlabel = "x", ylabel = "y") +points = [(0, 0), (1, 0), (1, 1), (-1, 1), (-1, -1), (0, -1), (0, 0)] +Plt.lines!(ax, first.(points), last.(points)) +fig #hide + +#= +## Exact solution +In this example, we choose an exact solution, ``\boldsymbol{E}_\mathrm{exact}(\boldsymbol{x})``, +that fullfills the differential equations. We then use ``\boldsymbol{E}_\mathrm{exact}`` to +find ``g`` to insert into the Dirichlet boundary conditions. Specifically, we choose +```math +\boldsymbol{E}_\mathrm{exact}(\boldsymbol{x}) = \mathrm{grad}(r^{2/3}\sin(2\theta/3)) +``` +where ``r`` and ``\theta`` are the polar coordinates. Where ``\theta`` is defined as the positive +angle measured from the ``x``-axis. + +The first PDE is directly satisfied by using the gradient to define the vector-valued solution, +since ``\mathrm{curl}(\mathrm{grad}(u)) = 0`` holds for any field ``u``. Furthermore, +along the lines, ``\theta = 0`` and ``\theta = 3\pi/2``, ``\sin(2\theta/3) = 0``, such that the +``\boldsymbol{E}_\mathrm{exact} \cdot \boldsymbol{t} = 0``. Consequently, even if we have a +singularity at ``\boldsymbol{x} = \boldsymbol{0}``, this doesn't enter the boundary conditions. +Finally, due to the singularity, the components of ``\boldsymbol{E}_\mathrm{exact}`` are not in +``H^1(\Omega)``. * **TODO:** Explain why `div(E)`` is fullfilled, use divergence theorem?* + +## Lagrange interpolation +Following the notes in the linked example, the lagrange problem becomes to solve +```math +\begin{align*} +\int_\Omega \mathrm{curl}(\delta \boldsymbol{E}) \cdot \mathrm{curl}(\boldsymbol{E})\ \mathrm{d}\Omega ++ \int_\Omega \mathrm{div}(\delta \boldsymbol{E}) \mathrm{div}(\boldsymbol{E})\ \mathrm{d}\Omega &= 0 \quad \forall\ \delta\boldsymbol{E} \in H^1 \\ +\boldsymbol{E}\cdot\boldsymbol{t} &= g\quad \text{on }\Gamma +\end{align*} +``` + +## Nedelec interpolation +To be completed... +=# + +# We then use `FerriteGmsh.jl` to create the grid +function setup_grid(h = 0.2; origin_refinement = 1) + # Initialize gmsh + Gmsh.initialize() + gmsh.option.set_number("General.Verbosity", 2) + + # Add the points, finer grid at the discontinuity + o = gmsh.model.geo.add_point(0.0, 0.0, 0.0, h / origin_refinement) + p1 = gmsh.model.geo.add_point(1.0, 0.0, 0.0, h) + p2 = gmsh.model.geo.add_point(1.0, 1.0, 0.0, h) + p3 = gmsh.model.geo.add_point(-1.0, 1.0, 0.0, h) + p4 = gmsh.model.geo.add_point(-1.0, -1.0, 0.0, h) + p5 = gmsh.model.geo.add_point(0.0, -1.0, 0.0, h) + + pts = [o, p1, p2, p3, p4, p5, o] + # Add the lines + lines = [gmsh.model.geo.add_line(pts[i - 1], pts[i]) for i in 2:length(pts)] + + # Create the closed curve loop and the surface + loop = gmsh.model.geo.add_curve_loop(lines) + gmsh.model.geo.add_plane_surface([loop]) + + # Synchronize the model + gmsh.model.geo.synchronize() + + # Generate a 2D mesh + gmsh.model.mesh.generate(2) + + # Save the mesh, and read back in as a Ferrite Grid + grid = mktempdir() do dir + path = joinpath(dir, "mesh.msh") + gmsh.write(path) + togrid(path) + end + + # Finalize the Gmsh library + Gmsh.finalize() + + # Add boundary parts + addfacetset!(grid, "vertical_facets", x -> abs((x[1] - 1) * x[1] * (x[1] + 1)) ≤ 1.0e-6) + addfacetset!(grid, "horizontal_facets", x -> abs((x[2] - 1) * x[2] * (x[2] + 1)) ≤ 1.0e-6) + + return grid +end + +# And prepare some functions to process and plot the data on the grid +# by using `FerriteTriangulation.jl` +function _create_data!(f, data, grid, a, cvs, subtria::SubTriangulation) + c1 = first(subtria.faces)[1] + x = copy(getcoordinates(grid, c1)) + dofs = copy(celldofs(dh, c1)) + ae = zeros(eltype(a), length(dofs)) + for (i, (cellnr, facenr)) in enumerate(subtria.faces) + cv = cvs[facenr] + getcoordinates!(x, grid, cellnr) + reinit!(cv, getcells(grid, cellnr), x) + celldofs!(dofs, dh, cellnr) + copyto!(ae, view(a, dofs)) + node_idxs = subtria.face_nodes[i]:(subtria.face_nodes[i + 1] - 1) + for q_point in 1:getnquadpoints(cv) + data[node_idxs[q_point]] = f(function_value(cv, q_point, ae)) + end + end + return +end + +""" + create_data(tr::Triangulation, grid::AbstractGrid, a::Vector{<:Number}, ::NTuple{N, <:Interpolation}; + f = identity) + +Create scalar data by evaluating `f(function_value(...))` at each triangulation node in the `grid`. +""" +function create_data(tr::Triangulation, grid, a, ips; f = identity) + data = zeros(length(tr.nodes)) + for (ip, subtria) in zip(ips, tr.sub_triangulation) + cvs = [CellValues(cr, ip, geometric_interpolation(getcelltype(subtria.sdh))) for cr in subtria.rules] + _create_data!(f, data, grid, a, cvs, subtria) + end + return data +end + +grid = setup_grid(0.00390625; origin_refinement = 1) + +# +ip = DiscontinuousLagrange{RefTriangle, 1}()^2 +dh = close!(add!(DofHandler(grid), :u, ip)) + +function analytical_potential(x::Vec{2}) # Analytical potential to be differentiated + Δθ = -3π / 4 # Rotate discontinuous line to 4th quadrant + xp = rotate(x, Δθ) + r = sqrt(x ⋅ x + eps()) + θ = r ≤ 1.0e-6 ? zero(eltype(x)) : (atan(xp[2], xp[1]) - Δθ) + return r^(2 // 3) * sin(2θ / 3) +end + +a = zeros(ndofs(dh)) + +apply_analytical!(a, dh, :u, x -> gradient(analytical_potential, x)) + +tr = Triangulation(dh, 2) +data = create_data(tr, grid, a, (ip,); f = first) + +fig = Plt.Figure() +ax = Plt.Axis(fig[1, 1]; aspect = Plt.DataAspect()) + +nodes = [GB.Point(x.data) for x in tr.nodes] +m = Plt.mesh!( + ax, nodes, reshape(tr.triangles, :); color = data, + colormap = Plt.Makie.wong_colors(), + interpolate = true, + colorrange = (-2.0, 0.0), +) +Plt.Colorbar(fig[1, 2], m) +#= +for i in 2:length(tr.tri_edges) + Plt.lines!(view(nodes, view(tr.edges, tr.tri_edges[i-1]:(tr.tri_edges[i]-1))); color=:black) +end # =# + +# Error calculator +mutable struct L2Error{F} + l2error::Float64 + volume::Float64 + const exact_fun::F +end + +function FerriteAssembly.integrate_cell!(vals::L2Error{F}, state, ae, material, cv, cellbuffer) where {F} + for q_point in 1:getnquadpoints(cv) + Eh = function_value(cv, q_point, ae) + x = spatial_coordinate(cv, q_point, getcoordinates(cellbuffer)) + dΩ = getdetJdV(cv, q_point) + vals.l2error += norm(Eh - vals.exact_fun(x))^2 * dΩ + vals.volume += dΩ + end + return +end + +# ## Lagrange solution +struct LagrangeMaterial end +function FerriteAssembly.element_routine!(Ke, re, s, ae, ::LagrangeMaterial, cv, cellbuffer) + for q_point in 1:getnquadpoints(cv) + dΩ = getdetJdV(cv, q_point) + for i in 1:getnbasefunctions(cv) + div_δNi = shape_divergence(cv, q_point, i) + curl_δNi = shape_curl(cv, q_point, i) + for j in 1:getnbasefunctions(cv) + div_Nj = shape_divergence(cv, q_point, j) + curl_Nj = shape_curl(cv, q_point, j) + Ke[i, j] += (curl_δNi ⋅ curl_Nj + div_δNi * div_Nj) * dΩ + end + end + end + return +end + +function solve_lagrange(dh, ip) + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:E, getfacetset(grid, "horizontal_facets"), (x, _) -> gradient(analytical_potential, x)[2], [2])) + add!(ch, Dirichlet(:E, getfacetset(grid, "vertical_facets"), (x, _) -> gradient(analytical_potential, x)[1], [1])) + close!(ch) + + cv = CellValues(QuadratureRule{RefTriangle}(1), ip) + K = allocate_matrix(dh) + f = zeros(ndofs(dh)) + db = setup_domainbuffer(DomainSpec(dh, LagrangeMaterial(), cv)) + as = start_assemble(K, f) + work!(as, db) + apply!(K, f, ch) + a = K \ f + l2_vals = L2Error(0.0, 0.0, x -> gradient(analytical_potential, x)) + work!(Integrator(l2_vals), db; a) + return a, sqrt(l2_vals.l2error) / l2_vals.volume +end + +ip = Lagrange{RefTriangle, 1}()^2 +dh = close!(add!(DofHandler(grid), :E, ip)) +a_lagrange, e_lagrange = solve_lagrange(dh, ip) + +function lagrange_error(h::Float64) + grid = setup_grid(h; origin_refinement = 1) + ip = Lagrange{RefTriangle, 1}()^2 + dh = close!(add!(DofHandler(grid), :E, ip)) + _, e = solve_lagrange(dh, ip) + return e +end + +#= +mesh_sizes = (1/2) .^(3:8) +lagrange_errors = Float64[] +for h in mesh_sizes + println("h = $h") + e = @time lagrange_error(h) + push!(lagrange_errors, e) +end +=# + +tr = Triangulation(dh, 2) +data = create_data(tr, grid, a_lagrange, (ip,); f = first) + +ax = Plt.Axis(fig[2, 1]; aspect = Plt.DataAspect()) + +nodes = [GB.Point(x.data) for x in tr.nodes] +m = Plt.mesh!( + ax, nodes, reshape(tr.triangles, :); color = data, + colormap = Plt.Makie.wong_colors(), + interpolate = true, + colorrange = (-2.0, 0.0), +) +Plt.Colorbar(fig[2, 2], m) +fig From 61e4c70d30b7030f3cd202801de3e219d48bc8b2 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sun, 5 Jan 2025 20:11:19 +0100 Subject: [PATCH 156/172] Introduce EdgeValues --- docs/Manifest.toml | 777 ++++++++++++++++------------- src/FEValues/EdgeValues.jl | 174 +++++++ src/FEValues/boundary_integrals.jl | 413 +++++++++++++++ src/Ferrite.jl | 4 +- src/Quadrature/quadrature.jl | 44 +- src/exports.jl | 3 + test/test_interpolations.jl | 268 +++++----- 7 files changed, 1231 insertions(+), 452 deletions(-) create mode 100644 src/FEValues/EdgeValues.jl create mode 100644 src/FEValues/boundary_integrals.jl diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 3f807e169b..77a05dfd23 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -5,13 +5,14 @@ manifest_format = "2.0" project_hash = "3d1b9374e40999c3125a0a2372919c3af4f3212e" [[deps.ADTypes]] -git-tree-sha1 = "eea5d80188827b35333801ef97a40c2ed653b081" +git-tree-sha1 = "72af59f5b8f09faee36b4ec48e014a79210f2f4f" uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" -version = "1.9.0" -weakdeps = ["ChainRulesCore", "EnzymeCore"] +version = "1.11.0" +weakdeps = ["ChainRulesCore", "ConstructionBase", "EnzymeCore"] [deps.ADTypes.extensions] ADTypesChainRulesCoreExt = "ChainRulesCore" + ADTypesConstructionBaseExt = "ConstructionBase" ADTypesEnzymeCoreExt = "EnzymeCore" [[deps.ANSIColoredPrinters]] @@ -37,9 +38,9 @@ version = "0.4.5" [[deps.Accessors]] deps = ["CompositionsBase", "ConstructionBase", "InverseFunctions", "LinearAlgebra", "MacroTools", "Markdown"] -git-tree-sha1 = "b392ede862e506d451fc1616e79aa6f4c673dab8" +git-tree-sha1 = "96bed9b1b57cf750cca50c311a197e306816a1cc" uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" -version = "0.1.38" +version = "0.1.39" [deps.Accessors.extensions] AccessorsAxisKeysExt = "AxisKeys" @@ -62,9 +63,9 @@ version = "0.1.38" [[deps.Adapt]] deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "6a55b747d1812e699320963ffde36f1ebdda4099" +git-tree-sha1 = "50c3c56a52972d78e8be9fd135bfb91c9574c140" uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "4.0.4" +version = "4.1.1" weakdeps = ["StaticArrays"] [deps.Adapt.extensions] @@ -111,15 +112,16 @@ version = "3.5.1+1" [[deps.ArrayInterface]] deps = ["Adapt", "LinearAlgebra"] -git-tree-sha1 = "3640d077b6dafd64ceb8fd5c1ec76f7ca53bcf76" +git-tree-sha1 = "017fcb757f8e921fb44ee063a7aafe5f89b86dd1" uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.16.0" +version = "7.18.0" [deps.ArrayInterface.extensions] ArrayInterfaceBandedMatricesExt = "BandedMatrices" ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" ArrayInterfaceCUDAExt = "CUDA" ArrayInterfaceCUDSSExt = "CUDSS" + ArrayInterfaceChainRulesCoreExt = "ChainRulesCore" ArrayInterfaceChainRulesExt = "ChainRules" ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" ArrayInterfaceReverseDiffExt = "ReverseDiff" @@ -133,6 +135,7 @@ version = "7.16.0" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -141,9 +144,9 @@ version = "7.16.0" [[deps.ArrayLayouts]] deps = ["FillArrays", "LinearAlgebra"] -git-tree-sha1 = "0dd7edaff278e346eb0ca07a7e75c9438408a3ce" +git-tree-sha1 = "2bf6e01f453284cb61c312836b4680331ddfc44b" uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" -version = "1.10.3" +version = "1.11.0" weakdeps = ["SparseArrays"] [deps.ArrayLayouts.extensions] @@ -198,15 +201,16 @@ uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" version = "1.11.0" [[deps.BibInternal]] -git-tree-sha1 = "78aa378482bf6f338eef8f2440fb62a75ab1aaa3" +deps = ["TestItems"] +git-tree-sha1 = "b3107800faf461eca3281f89f8d768f4b3e99969" uuid = "2027ae74-3657-4b95-ae00-e2f7d55c3e64" -version = "0.3.6" +version = "0.3.7" [[deps.BibParser]] -deps = ["BibInternal", "DataStructures", "Dates", "JSONSchema", "YAML"] -git-tree-sha1 = "f24884311dceb5f8eafe11809b6f1d867b489a46" +deps = ["BibInternal", "DataStructures", "Dates", "JSONSchema", "TestItems", "YAML"] +git-tree-sha1 = "33478bed83bd124ea8ecd9161b3918fb4c70e529" uuid = "13533e5b-e1c2-4e57-8cef-cac5e52f6474" -version = "0.2.1" +version = "0.2.2" [[deps.Bibliography]] deps = ["BibInternal", "BibParser", "DataStructures", "Dates", "FileIO", "YAML"] @@ -227,9 +231,9 @@ version = "0.1.6" [[deps.BlockArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra"] -git-tree-sha1 = "d434647f798823bcae510aee0bc0401927f64391" +git-tree-sha1 = "b406207917260364a2e0287b42e4c6772cb9db88" uuid = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" -version = "1.1.1" +version = "1.3.0" [deps.BlockArrays.extensions] BlockArraysBandedMatricesExt = "BandedMatrices" @@ -237,11 +241,21 @@ version = "1.1.1" [deps.BlockArrays.weakdeps] BandedMatrices = "aae01518-5342-5314-be14-df237901396f" +[[deps.BracketingNonlinearSolve]] +deps = ["CommonSolve", "ConcreteStructs", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "95cb19c37ea427617e9795655667712f03058d98" +uuid = "70df07ce-3d50-431d-a3e7-ca6ddb60ac1e" +version = "1.1.0" +weakdeps = ["ForwardDiff"] + + [deps.BracketingNonlinearSolve.extensions] + BracketingNonlinearSolveForwardDiffExt = "ForwardDiff" + [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" +git-tree-sha1 = "35abeca13bc0425cff9e59e229d971f5231323bf" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+1" +version = "1.0.8+3" [[deps.CEnum]] git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc" @@ -272,9 +286,9 @@ version = "1.1.1" [[deps.CairoMakie]] deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"] -git-tree-sha1 = "7947d2b61995eda7d5ca50c697b12bb578b918e5" +git-tree-sha1 = "0afa2b4ac444b9412130d68493941e1af462e26a" uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" -version = "0.12.14" +version = "0.12.18" [[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"] @@ -322,9 +336,9 @@ version = "0.4.0" [[deps.ColorSchemes]] deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "b5278586822443594ff615963b0c09755771b3e0" +git-tree-sha1 = "c785dfb1b3bfddd1da557e861b919819b82bbe5b" uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.26.0" +version = "3.27.1" [[deps.ColorTypes]] deps = ["FixedPointNumbers", "Random"] @@ -395,9 +409,9 @@ version = "0.2.3" [[deps.ConcurrentUtilities]] deps = ["Serialization", "Sockets"] -git-tree-sha1 = "ea32b83ca4fefa1768dc84e504cc0a94fb1ab8d1" +git-tree-sha1 = "f36e5e8fdffcb5646ea5da81495a5a7566005127" uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" -version = "2.4.2" +version = "2.4.3" [[deps.ConstructionBase]] git-tree-sha1 = "76219f1ed5771adbb096743bff43fb5fdd4c1157" @@ -461,10 +475,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", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "Statistics", "Tricks", "TruncatedStacktraces"] -git-tree-sha1 = "8977ef8249b602e4cb46ddbaf3c51e6adc2958c7" +deps = ["ArrayInterface", "ConcreteStructs", "DataStructures", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "FastPower", "ForwardDiff", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "Parameters", "PreallocationTools", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "Statistics", "TruncatedStacktraces"] +git-tree-sha1 = "b1e23a7fe7371934d9d538114a7e7166c1d09e05" uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" -version = "6.157.0" +version = "6.161.0" [deps.DiffEqBase.extensions] DiffEqBaseCUDAExt = "CUDA" @@ -507,32 +521,35 @@ uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" version = "1.15.1" [[deps.DifferentiationInterface]] -deps = ["ADTypes", "Compat", "LinearAlgebra", "PackageExtensionCompat"] -git-tree-sha1 = "75d1716eb46e1b77304f7d79ec9ac6a4e6185d02" +deps = ["ADTypes", "LinearAlgebra"] +git-tree-sha1 = "5df172ce4e9da710603591f48c7af74eef288892" uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" -version = "0.6.7" +version = "0.6.28" [deps.DifferentiationInterface.extensions] DifferentiationInterfaceChainRulesCoreExt = "ChainRulesCore" DifferentiationInterfaceDiffractorExt = "Diffractor" - DifferentiationInterfaceEnzymeExt = "Enzyme" + DifferentiationInterfaceEnzymeExt = ["EnzymeCore", "Enzyme"] DifferentiationInterfaceFastDifferentiationExt = "FastDifferentiation" DifferentiationInterfaceFiniteDiffExt = "FiniteDiff" DifferentiationInterfaceFiniteDifferencesExt = "FiniteDifferences" - DifferentiationInterfaceForwardDiffExt = "ForwardDiff" + DifferentiationInterfaceForwardDiffExt = ["ForwardDiff", "DiffResults"] DifferentiationInterfaceMooncakeExt = "Mooncake" DifferentiationInterfacePolyesterForwardDiffExt = "PolyesterForwardDiff" - DifferentiationInterfaceReverseDiffExt = "ReverseDiff" + DifferentiationInterfaceReverseDiffExt = ["ReverseDiff", "DiffResults"] DifferentiationInterfaceSparseArraysExt = "SparseArrays" DifferentiationInterfaceSparseMatrixColoringsExt = "SparseMatrixColorings" + DifferentiationInterfaceStaticArraysExt = "StaticArrays" DifferentiationInterfaceSymbolicsExt = "Symbolics" DifferentiationInterfaceTrackerExt = "Tracker" DifferentiationInterfaceZygoteExt = ["Zygote", "ForwardDiff"] [deps.DifferentiationInterface.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DiffResults = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" Diffractor = "9f5e2b26-1114-432f-b630-d3fe2085c51c" Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" FastDifferentiation = "eb9bf01b-bf85-4b60-bf87-ee5de06c00be" FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41" FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" @@ -542,15 +559,16 @@ version = "0.6.7" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.Distances]] deps = ["LinearAlgebra", "Statistics", "StatsAPI"] -git-tree-sha1 = "66c4c81f259586e8f002eacebc177e1fb06363b0" +git-tree-sha1 = "c7e3a542b999843086e2f29dac96a618c105be1d" uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.11" +version = "0.10.12" weakdeps = ["ChainRulesCore", "SparseArrays"] [deps.Distances.extensions] @@ -586,15 +604,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 = "5a1ee886566f2fa9318df1273d8b778b9d42712d" +git-tree-sha1 = "d0ea2c044963ed6f37703cead7e29f70cba13d7e" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.7.0" +version = "1.8.0" [[deps.DocumenterCitations]] deps = ["AbstractTrees", "Bibliography", "Dates", "Documenter", "Logging", "Markdown", "MarkdownAST", "OrderedCollections", "Unicode"] -git-tree-sha1 = "ca601b812efd1155a9bdf9c80e7e0428da598a08" +git-tree-sha1 = "5a72f3f804deb1431cb79f5636163e4fdf8ed8ed" uuid = "daee34ce-89f3-4625-b898-19384cb65244" -version = "1.3.4" +version = "1.3.5" [[deps.Downloads]] deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] @@ -613,9 +631,9 @@ uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" version = "1.0.4" [[deps.EnzymeCore]] -git-tree-sha1 = "9c3a42611e525352e9ad5e4134ddca5c692ff209" +git-tree-sha1 = "0cdb7af5c39e92d78a0ee8d0a447d32f7593137e" uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" -version = "0.8.4" +version = "0.8.8" weakdeps = ["Adapt"] [deps.EnzymeCore.extensions] @@ -623,9 +641,9 @@ weakdeps = ["Adapt"] [[deps.EpollShim_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643" +git-tree-sha1 = "8a4be429317c42cfae6a7fc03c31bad1970c310d" uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" -version = "0.0.20230411+0" +version = "0.0.20230411+1" [[deps.ExactPredicates]] deps = ["IntervalArithmetic", "Random", "StaticArrays"] @@ -635,21 +653,25 @@ version = "2.2.8" [[deps.ExceptionUnwrapping]] deps = ["Test"] -git-tree-sha1 = "dcb08a0d93ec0b1cdc4af184b26b591e9695423a" +git-tree-sha1 = "d36f682e590a83d63d1c7dbd287573764682d12a" uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" -version = "0.1.10" +version = "0.1.11" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1c6317308b9dc757616f0b5cb379db10494443a7" +git-tree-sha1 = "f42a5b1e20e009a43c3646635ed81a9fcaccb287" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.6.2+0" +version = "2.6.4+2" [[deps.ExponentialUtilities]] deps = ["Adapt", "ArrayInterface", "GPUArraysCore", "GenericSchur", "LinearAlgebra", "PrecompileTools", "Printf", "SparseArrays", "libblastrampoline_jll"] -git-tree-sha1 = "8e18940a5ba7f4ddb41fe2b79b6acaac50880a86" +git-tree-sha1 = "cae251c76f353e32d32d76fae2fea655eab652af" uuid = "d4d017d3-3776-5f7e-afef-a10c40355c18" -version = "1.26.1" +version = "1.27.0" +weakdeps = ["StaticArrays"] + + [deps.ExponentialUtilities.extensions] + ExponentialUtilitiesStaticArraysExt = "StaticArrays" [[deps.ExprTools]] git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" @@ -687,9 +709,9 @@ version = "1.8.0" [[deps.FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" +git-tree-sha1 = "5cf2433259aa3985046792e2afc01fcec076b549" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+1" +version = "3.3.10+2" [[deps.FLTK_jll]] deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -708,12 +730,39 @@ git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" uuid = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" version = "0.3.2" +[[deps.FastGaussQuadrature]] +deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" +uuid = "442a2c76-b920-505d-bb47-c5924d526838" +version = "1.0.2" + [[deps.FastLapackInterface]] deps = ["LinearAlgebra"] git-tree-sha1 = "cbf5edddb61a43669710cbc2241bc08b36d9e660" uuid = "29a986be-02c6-4525-aec4-84b980013641" version = "2.0.4" +[[deps.FastPower]] +git-tree-sha1 = "58c3431137131577a7c379d00fea00be524338fb" +uuid = "a4df4552-cc26-4903-aec0-212e50a0e84b" +version = "1.1.1" + + [deps.FastPower.extensions] + FastPowerEnzymeExt = "Enzyme" + FastPowerForwardDiffExt = "ForwardDiff" + FastPowerMeasurementsExt = "Measurements" + FastPowerMonteCarloMeasurementsExt = "MonteCarloMeasurements" + FastPowerReverseDiffExt = "ReverseDiff" + FastPowerTrackerExt = "Tracker" + + [deps.FastPower.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + [[deps.Ferrite]] deps = ["EnumX", "ForwardDiff", "LinearAlgebra", "NearestNeighbors", "OrderedCollections", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"] path = ".." @@ -754,9 +803,13 @@ version = "0.1.0" [[deps.FileIO]] deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" +git-tree-sha1 = "2dd20384bf8c6d411b5c7370865b1e9b26cb2ea3" uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.3" +version = "1.16.6" +weakdeps = ["HTTP"] + + [deps.FileIO.extensions] + HTTPExt = "HTTP" [[deps.FilePaths]] deps = ["FilePathsBase", "MacroTools", "Reexport", "Requires"] @@ -793,9 +846,9 @@ weakdeps = ["PDMats", "SparseArrays", "Statistics"] [[deps.FiniteDiff]] deps = ["ArrayInterface", "LinearAlgebra", "Setfield"] -git-tree-sha1 = "b10bdafd1647f57ace3885143936749d61638c3b" +git-tree-sha1 = "84e3a47db33be7248daa6274b287507dd6ff84e8" uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" -version = "2.26.0" +version = "2.26.2" [deps.FiniteDiff.extensions] FiniteDiffBandedMatricesExt = "BandedMatrices" @@ -817,9 +870,9 @@ version = "0.8.5" [[deps.Fontconfig_jll]] deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] -git-tree-sha1 = "db16beca600632c95fc8aca29890d83788dd8b23" +git-tree-sha1 = "21fac3c77d7b5a9fc03b0ec503aa1a6392c34d2b" uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.96+0" +version = "2.15.0+0" [[deps.Format]] git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" @@ -828,9 +881,9 @@ version = "1.3.7" [[deps.ForwardDiff]] deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] -git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad" +git-tree-sha1 = "a2df1b776752e3f344e5116c06d75a10436ab853" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.36" +version = "0.10.38" weakdeps = ["StaticArrays"] [deps.ForwardDiff.extensions] @@ -844,9 +897,9 @@ version = "4.1.1" [[deps.FreeType2_jll]] deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "5c1d8ae0efc6c2e7b1fc502cbe25def8f661b7bc" +git-tree-sha1 = "786e968a8d2fb167f2e4880baba62e0e26bd8e4e" uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.2+0" +version = "2.13.3+1" [[deps.FreeTypeAbstraction]] deps = ["ColorVectorSpace", "Colors", "FreeType", "GeometryBasics"] @@ -856,9 +909,9 @@ version = "0.10.6" [[deps.FriBidi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1ed150b39aebcc805c26b93a8d0122c940f64ce2" +git-tree-sha1 = "846f7026a9decf3679419122b49f8a1fdb48d2d5" uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.14+0" +version = "1.0.16+0" [[deps.FunctionWrappers]] git-tree-sha1 = "d62485945ce5ae9c0c48f124a84998d755bae00e" @@ -878,9 +931,9 @@ version = "1.11.0" [[deps.GLFW_jll]] 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" +git-tree-sha1 = "fcb0584ff34e25155876418979d4c8971243bb89" uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.4.0+1" +version = "3.4.0+2" [[deps.GLU_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] @@ -895,21 +948,21 @@ version = "6.3.0+0" [[deps.GPUArraysCore]] deps = ["Adapt"] -git-tree-sha1 = "ec632f177c0d990e64d955ccc1b8c04c485a0950" +git-tree-sha1 = "83cf05ab16a73219e5f6bd1bdfa9848fa24ac627" uuid = "46192b85-c4d5-4398-a991-12ede77f4527" -version = "0.1.6" +version = "0.2.0" [[deps.GR]] 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" +git-tree-sha1 = "424c8f76017e39fdfcdbb5935a8e6742244959e8" uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.73.7" +version = "0.73.10" [[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 = "a8863b69c2a0859f2c2c87ebdc4c6712e88bdf0d" +git-tree-sha1 = "b90934c8cb33920a8dc66736471dc3961b42ec9f" uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.73.7+0" +version = "0.73.10+0" [[deps.GenericSchur]] deps = ["LinearAlgebra", "Printf"] @@ -942,9 +995,9 @@ version = "0.21.0+0" [[deps.Giflib_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0224cce99284d997f6880a42ef715a37c99338d1" +git-tree-sha1 = "7141135f9073f135e68c5ee8df44fb0fb80689b8" uuid = "59f7168a-df46-5410-90c8-f2779963d0ec" -version = "5.2.2+0" +version = "5.2.2+1" [[deps.Git]] deps = ["Git_jll"] @@ -954,15 +1007,15 @@ 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 = "ea372033d09e4552a04fd38361cd019f9003f4f4" +git-tree-sha1 = "399f4a308c804b446ae4c91eeafadb2fe2c54ff9" uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.46.2+0" +version = "2.47.1+0" [[deps.Glib_jll]] deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "674ff0db93fffcd11a3573986e550d66cd4fd71f" +git-tree-sha1 = "b0036b392358c80d2d2124746c2bf3d48d457938" uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.5+0" +version = "2.82.4+0" [[deps.Gmsh]] deps = ["gmsh_jll"] @@ -978,9 +1031,9 @@ version = "1.1.3" [[deps.Graphite2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" +git-tree-sha1 = "01979f9b37367603e2848ea225918a3b3861b606" uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" -version = "1.3.14+0" +version = "1.3.14+1" [[deps.Graphs]] deps = ["ArnoldiMethod", "Compat", "DataStructures", "Distributed", "Inflate", "LinearAlgebra", "Random", "SharedArrays", "SimpleTraits", "SparseArrays", "Statistics"] @@ -1006,16 +1059,16 @@ uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" version = "1.14.3+3" [[deps.HTTP]] -deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] -git-tree-sha1 = "d1d712be3164d61d1fb98e7ce9bcbc6cc06b45ed" +deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] +git-tree-sha1 = "c67b33b085f6e2faf8bf79a61962e7339a81129c" uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "1.10.8" +version = "1.10.15" [[deps.HarfBuzz_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] -git-tree-sha1 = "401e4f3f30f43af2c8478fc008da50096ea5240f" +git-tree-sha1 = "55c53be97790242c29031e5cd45e8ac296dadda3" uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "8.3.1+0" +version = "8.5.0+0" [[deps.HostCPUFeatures]] deps = ["BitTwiddlingConvenienceFunctions", "IfElse", "Libdl", "Static"] @@ -1025,9 +1078,9 @@ version = "0.1.17" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dd3b49277ec2bb2c6b94eb1604d4d0616016f7a6" +git-tree-sha1 = "290232556f4ffb60ac3e476acf28e1a46e764742" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.2+0" +version = "2.11.2+2" [[deps.HypergeometricFunctions]] deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] @@ -1120,9 +1173,9 @@ weakdeps = ["Unitful"] [[deps.IntervalArithmetic]] deps = ["CRlibm_jll", "LinearAlgebra", "MacroTools", "RoundingEmulator"] -git-tree-sha1 = "3a272409c5f7fb864f830b3bb231b0a4d32747eb" +git-tree-sha1 = "ffb76d09ab0dc9f5a27edac2acec13c74a876cc6" uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" -version = "0.22.20" +version = "0.22.21" weakdeps = ["DiffRules", "ForwardDiff", "IntervalSets", "RecipesBase"] [deps.IntervalArithmetic.extensions] @@ -1181,15 +1234,15 @@ version = "1.0.0" [[deps.JLFzf]] deps = ["Pipe", "REPL", "Random", "fzf_jll"] -git-tree-sha1 = "39d64b09147620f5ffbf6b2d3255be3c901bec63" +git-tree-sha1 = "71b48d857e86bf7a1838c4736545699974ce79a2" uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" -version = "0.1.8" +version = "0.1.9" [[deps.JLLWrappers]] deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "f389674c99bfcde17dc57454011aa44d5a260a40" +git-tree-sha1 = "a007feb38b422fbdab534406aeca1b86823cb4d6" uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.6.0" +version = "1.7.0" [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] @@ -1199,9 +1252,9 @@ version = "0.21.4" [[deps.JSON3]] deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"] -git-tree-sha1 = "eb3edce0ed4fa32f75a0a11217433c31d56bd48b" +git-tree-sha1 = "1d322381ef7b087548321d3f878cb4c9bd8f8f9b" uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" -version = "1.14.0" +version = "1.14.1" [deps.JSON3.extensions] JSON3ArrowExt = ["ArrowTypes"] @@ -1223,9 +1276,9 @@ version = "0.1.5" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "25ee0be4d43d0269027024d75a24c24d6c6e590c" +git-tree-sha1 = "3447a92280ecaad1bd93d3fce3d408b6cfff8913" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.4+0" +version = "3.1.0+1" [[deps.KLU]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] @@ -1241,15 +1294,15 @@ version = "0.6.9" [[deps.Krylov]] deps = ["LinearAlgebra", "Printf", "SparseArrays"] -git-tree-sha1 = "267dad6b4b7b5d529c76d40ff48d33f7e94cb834" +git-tree-sha1 = "4f20a2df85a9e5d55c9e84634bbf808ed038cabd" uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" -version = "0.9.6" +version = "0.9.8" [[deps.KrylovKit]] -deps = ["GPUArraysCore", "LinearAlgebra", "PackageExtensionCompat", "Printf", "Random", "VectorInterface"] -git-tree-sha1 = "3ba86a12066c19a6ed0bc963984bcf89d495133e" +deps = ["LinearAlgebra", "PackageExtensionCompat", "Printf", "Random", "VectorInterface"] +git-tree-sha1 = "d7ed24a88732689f26d3f12a817d181d4024bf44" uuid = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" -version = "0.8.2" +version = "0.8.3" weakdeps = ["ChainRulesCore"] [deps.KrylovKit.extensions] @@ -1262,10 +1315,10 @@ uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" version = "3.100.2+0" [[deps.LERC_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "78e0f4b5270c4ae09c7c5f78e77b904199038945" uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "3.0.0+1" +version = "4.0.0+2" [[deps.LLVMOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1280,9 +1333,9 @@ uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" version = "2.10.2+1" [[deps.LaTeXStrings]] -git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" +git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.3.1" +version = "1.4.0" [[deps.Latexify]] deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] @@ -1307,15 +1360,15 @@ uuid = "10f19ff3-798f-405d-979b-55457f8fc047" version = "0.1.17" [[deps.LazilyInitializedFields]] -git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" +git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" -version = "1.2.2" +version = "1.3.0" [[deps.LazyArrays]] deps = ["ArrayLayouts", "FillArrays", "LinearAlgebra", "MacroTools", "SparseArrays"] -git-tree-sha1 = "360f6039babd6e4d6364eff0d4fc9120834a2d9a" +git-tree-sha1 = "f289bee714e11708df257c57514585863aa02b33" uuid = "5078a376-72f3-5289-bfd5-ec5146d43c02" -version = "2.2.1" +version = "2.3.1" [deps.LazyArrays.extensions] LazyArraysBandedMatricesExt = "BandedMatrices" @@ -1370,51 +1423,51 @@ version = "1.11.0" [[deps.Libffi_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +git-tree-sha1 = "27ecae93dd25ee0909666e6835051dd684cc035e" uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" +version = "3.2.2+2" [[deps.Libgcrypt_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll"] -git-tree-sha1 = "9fd170c4bbfd8b935fdc5f8b7aa33532c991a673" +git-tree-sha1 = "8be878062e0ffa2c3f67bb58a595375eda5de80b" uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.11+0" +version = "1.11.0+0" [[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "ff3b4b9d35de638936a525ecd36e86a8bb919d11" uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.6.0+0" +version = "1.7.0+0" [[deps.Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "fbb1f2bef882392312feb1ede3615ddc1e9b99ed" +git-tree-sha1 = "a7f43994b47130e4f491c3b2dbe78fe9e2aed2b3" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.49.0+0" +version = "1.51.0+0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" +git-tree-sha1 = "61dfdba58e585066d8bce214c5a51eaa0539f269" uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+0" +version = "1.17.0+1" [[deps.Libmount_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0c4f9c4f1a50d8f35048fa0532dabbadf702f81e" +git-tree-sha1 = "84eef7acd508ee5b3e956a2ae51b05024181dee0" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.40.1+0" +version = "2.40.2+0" [[deps.Libtiff_jll]] deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "2da088d113af58221c52828a80378e16be7d037a" +git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a" uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.5.1+1" +version = "4.7.0+0" [[deps.Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "5ee6203157c120d79034c748a2acba45b82b8807" +git-tree-sha1 = "edbf5309f9ddf1cab25afc344b1e8150b7c832f9" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.40.1+0" +version = "2.40.2+0" [[deps.LightXML]] deps = ["Libdl", "XML2_jll"] @@ -1424,9 +1477,9 @@ version = "0.9.1" [[deps.LineSearch]] deps = ["ADTypes", "CommonSolve", "ConcreteStructs", "FastClosures", "LinearAlgebra", "MaybeInplace", "SciMLBase", "SciMLJacobianOperators", "StaticArraysCore"] -git-tree-sha1 = "dc82b3c3640a4362f09e4d7594b4387a820857e4" +git-tree-sha1 = "97d502765cc5cf3a722120f50da03c2474efce04" uuid = "87fe0de2-c867-4266-b59a-2f0a94fc965b" -version = "0.1.3" +version = "0.1.4" weakdeps = ["LineSearches"] [deps.LineSearch.extensions] @@ -1451,9 +1504,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 = "8941ad4bdd83768359801982e143008349b1a827" +git-tree-sha1 = "9d5872d134bd33dd3e120767004f760770958863" uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" -version = "2.35.0" +version = "2.38.0" [deps.LinearSolve.extensions] LinearSolveBandedMatricesExt = "BandedMatrices" @@ -1492,16 +1545,16 @@ uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306" version = "2.20.1" [[deps.LiveServer]] -deps = ["HTTP", "LoggingExtras", "MIMEs", "Pkg", "Sockets", "Test"] -git-tree-sha1 = "1e46b873b8ef176e23ee43f96e72cd45c20bafb4" +deps = ["HTTP", "LoggingExtras", "MIMEs", "Sockets", "Test"] +git-tree-sha1 = "564a436267fb1fc768f815dad64c4386c46623f8" uuid = "16fef848-5104-11e9-1b77-fb7a48bbb589" -version = "1.3.1" +version = "1.4.0" [[deps.LogExpFunctions]] deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea" +git-tree-sha1 = "13ca9e2586b89836fd20cccf56e57e2b9ae7f38f" uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.28" +version = "0.3.29" [deps.LogExpFunctions.extensions] LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" @@ -1519,9 +1572,9 @@ version = "1.11.0" [[deps.LoggingExtras]] deps = ["Dates", "Logging"] -git-tree-sha1 = "c1dd6d7978c12545b4179fb6153b9250c96b0075" +git-tree-sha1 = "f02b56007b064fbfddb4c9cd60161b6dd0f40df3" uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" -version = "1.0.3" +version = "1.1.0" [[deps.LoopVectorization]] deps = ["ArrayInterface", "CPUSummary", "CloseOpenIntervals", "DocStringExtensions", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "PrecompileTools", "SIMDTypes", "SLEEFPirates", "Static", "StaticArrayInterface", "ThreadingUtilities", "UnPack", "VectorizationBase"] @@ -1536,9 +1589,9 @@ weakdeps = ["ChainRulesCore", "ForwardDiff", "SpecialFunctions"] [[deps.METIS_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" +git-tree-sha1 = "1c20a46719c0dc4ec4e7021ca38f53e1ec9268d9" uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" -version = "5.1.2+0" +version = "5.1.2+1" [[deps.MIMEs]] git-tree-sha1 = "65f28ad4b594aebe22157d6fac869786a255b7eb" @@ -1587,16 +1640,16 @@ uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" version = "0.5.13" [[deps.Makie]] -deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"] -git-tree-sha1 = "3df66da15ba7b37b34f6557b7e1c95a3ff5c670b" +deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "InverseFunctions", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"] +git-tree-sha1 = "be3051d08b78206fb5e688e8d70c9e84d0264117" uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" -version = "0.21.14" +version = "0.21.18" [[deps.MakieCore]] deps = ["ColorTypes", "GeometryBasics", "IntervalSets", "Observables"] -git-tree-sha1 = "4604f03e5b057e8e62a95a44929cafc9585b0fe9" +git-tree-sha1 = "9019b391d7d086e841cbeadc13511224bd029ab3" uuid = "20f20a25-4f0e-4fdf-b5d1-57303727442b" -version = "0.8.9" +version = "0.8.12" [[deps.ManualMemory]] git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" @@ -1659,9 +1712,9 @@ version = "0.3.2" [[deps.MicrosoftMPI_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01" +git-tree-sha1 = "bc95bf4149bf535c09602e3acdf950d9b4376227" uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" -version = "10.1.4+2" +version = "10.1.4+3" [[deps.Missings]] deps = ["DataAPI"] @@ -1702,9 +1755,9 @@ version = "1.0.2" [[deps.NearestNeighbors]] deps = ["Distances", "StaticArrays"] -git-tree-sha1 = "3cebfc94a0754cc329ebc3bab1e6c89621e791ad" +git-tree-sha1 = "8a3271d8309285f4db73b4f662b1b290c715e85e" uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" -version = "0.4.20" +version = "0.4.21" [[deps.Netpbm]] deps = ["FileIO", "ImageCore", "ImageMetadata"] @@ -1717,32 +1770,86 @@ uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" version = "1.2.0" [[deps.NonlinearSolve]] -deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FastClosures", "FiniteDiff", "ForwardDiff", "LazyArrays", "LineSearch", "LineSearches", "LinearAlgebra", "LinearSolve", "MaybeInplace", "PrecompileTools", "Preferences", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLJacobianOperators", "SciMLOperators", "Setfield", "SimpleNonlinearSolve", "SparseArrays", "SparseConnectivityTracer", "SparseMatrixColorings", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] -git-tree-sha1 = "4d8944f32db2b07a2bdf8477e878bcb9c9ea2308" +deps = ["ADTypes", "ArrayInterface", "BracketingNonlinearSolve", "CommonSolve", "ConcreteStructs", "DiffEqBase", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "LinearSolve", "NonlinearSolveBase", "NonlinearSolveFirstOrder", "NonlinearSolveQuasiNewton", "NonlinearSolveSpectralMethods", "PrecompileTools", "Preferences", "Reexport", "SciMLBase", "SimpleNonlinearSolve", "SparseArrays", "SparseMatrixColorings", "StaticArraysCore", "SymbolicIndexingInterface"] +git-tree-sha1 = "d0caebdb5a31e1a11ca9f7f189cdbf341ac89f0e" uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" -version = "3.15.1" +version = "4.3.0" [deps.NonlinearSolve.extensions] - NonlinearSolveBandedMatricesExt = "BandedMatrices" NonlinearSolveFastLevenbergMarquardtExt = "FastLevenbergMarquardt" NonlinearSolveFixedPointAccelerationExt = "FixedPointAcceleration" NonlinearSolveLeastSquaresOptimExt = "LeastSquaresOptim" NonlinearSolveMINPACKExt = "MINPACK" NonlinearSolveNLSolversExt = "NLSolvers" - NonlinearSolveNLsolveExt = "NLsolve" + NonlinearSolveNLsolveExt = ["NLsolve", "LineSearches"] + NonlinearSolvePETScExt = ["PETSc", "MPI"] NonlinearSolveSIAMFANLEquationsExt = "SIAMFANLEquations" NonlinearSolveSpeedMappingExt = "SpeedMapping" + NonlinearSolveSundialsExt = "Sundials" [deps.NonlinearSolve.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" FastLevenbergMarquardt = "7a0df574-e128-4d35-8cbd-3d84502bf7ce" FixedPointAcceleration = "817d07cb-a79a-5c30-9a31-890123675176" LeastSquaresOptim = "0fc2ff8b-aaa3-5acd-a817-1944a5e08891" + LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" MINPACK = "4854310b-de5a-5eb6-a2a5-c1dee2bd17f9" + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" NLSolvers = "337daf1e-9722-11e9-073e-8b9effe078ba" NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" + PETSc = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0" SIAMFANLEquations = "084e46ad-d928-497d-ad5e-07fa361a48c4" SpeedMapping = "f1835b91-879b-4a3f-a438-e4baacf14412" + Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4" + +[[deps.NonlinearSolveBase]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "Compat", "ConcreteStructs", "DifferentiationInterface", "EnzymeCore", "FastClosures", "LinearAlgebra", "Markdown", "MaybeInplace", "Preferences", "Printf", "RecursiveArrayTools", "SciMLBase", "SciMLJacobianOperators", "SciMLOperators", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] +git-tree-sha1 = "5bca24ce7b0c034dcbdc6ad6d658b02e0eed566e" +uuid = "be0214bd-f91f-a760-ac4e-3421ce2b2da0" +version = "1.4.0" + + [deps.NonlinearSolveBase.extensions] + NonlinearSolveBaseBandedMatricesExt = "BandedMatrices" + NonlinearSolveBaseDiffEqBaseExt = "DiffEqBase" + NonlinearSolveBaseForwardDiffExt = "ForwardDiff" + NonlinearSolveBaseLineSearchExt = "LineSearch" + NonlinearSolveBaseLinearSolveExt = "LinearSolve" + NonlinearSolveBaseSparseArraysExt = "SparseArrays" + NonlinearSolveBaseSparseMatrixColoringsExt = "SparseMatrixColorings" + + [deps.NonlinearSolveBase.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + LineSearch = "87fe0de2-c867-4266-b59a-2f0a94fc965b" + LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" + +[[deps.NonlinearSolveFirstOrder]] +deps = ["ADTypes", "ArrayInterface", "CommonSolve", "ConcreteStructs", "DiffEqBase", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLJacobianOperators", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "a1ea35ab0bcc99753e26d574ba1e339f19d100fa" +uuid = "5959db7a-ea39-4486-b5fe-2dd0bf03d60d" +version = "1.2.0" + +[[deps.NonlinearSolveQuasiNewton]] +deps = ["ArrayInterface", "CommonSolve", "ConcreteStructs", "DiffEqBase", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLOperators", "StaticArraysCore"] +git-tree-sha1 = "8f14b848afcfc0a2941cd3cca1bef04c987465bb" +uuid = "9a2c21bd-3a47-402d-9113-8faf9a0ee114" +version = "1.1.0" +weakdeps = ["ForwardDiff"] + + [deps.NonlinearSolveQuasiNewton.extensions] + NonlinearSolveQuasiNewtonForwardDiffExt = "ForwardDiff" + +[[deps.NonlinearSolveSpectralMethods]] +deps = ["CommonSolve", "ConcreteStructs", "DiffEqBase", "LineSearch", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "f28b1ab17b5f15eb2b174eaf8813cf17f0b3e6c0" +uuid = "26075421-4e9a-44e1-8bd1-420ed7ad02b2" +version = "1.1.0" +weakdeps = ["ForwardDiff"] + + [deps.NonlinearSolveSpectralMethods.extensions] + NonlinearSolveSpectralMethodsForwardDiffExt = "ForwardDiff" [[deps.OCCT_jll]] 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"] @@ -1756,9 +1863,9 @@ uuid = "510215fc-4207-5dde-b226-833fc4488ee2" version = "0.5.5" [[deps.OffsetArrays]] -git-tree-sha1 = "1a27764e945a152f7ca7efa04de513d473e9542e" +git-tree-sha1 = "5e1897147d1ff8d98883cda2be2187dcf57d8f0c" uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.14.1" +version = "1.15.0" weakdeps = ["Adapt"] [deps.OffsetArrays.extensions] @@ -1812,9 +1919,9 @@ version = "1.4.3" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" +git-tree-sha1 = "f58782a883ecbf9fb48dcd363f9ccd65f36c23a8" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+1" +version = "3.0.15+2" [[deps.OpenSpecFun_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] @@ -1824,9 +1931,9 @@ version = "0.5.5+0" [[deps.Optim]] deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "PositiveFactorizations", "Printf", "SparseArrays", "StatsBase"] -git-tree-sha1 = "d9b79c4eed437421ac4285148fcadf42e0700e89" +git-tree-sha1 = "ab7edad78cdef22099f43c54ef77ac63c2c9cc64" uuid = "429524aa-4258-5aef-a3af-852621145aeb" -version = "1.9.4" +version = "1.10.0" [deps.Optim.extensions] OptimMOIExt = "MathOptInterface" @@ -1841,15 +1948,15 @@ uuid = "91d4177d-7536-5919-b921-800302f37372" version = "1.3.3+0" [[deps.OrderedCollections]] -git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" +git-tree-sha1 = "12f1439c4f986bb868acda6ea33ebc78e19b95ad" uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.3" +version = "1.7.0" [[deps.OrdinaryDiffEq]] 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" +git-tree-sha1 = "36ce9bfc14a4b3dcf1490e80b5f1f4d35bfddf39" uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" -version = "6.89.0" +version = "6.90.1" [[deps.OrdinaryDiffEqAdamsBashforthMoulton]] deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqLowOrderRK", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1858,16 +1965,16 @@ 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" +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "86ccea2bd0fdfa5570172a9717061f6417e21dea" uuid = "6ad6398a-0878-4a85-9266-38940aa047c8" -version = "1.1.2" +version = "1.2.0" [[deps.OrdinaryDiffEqCore]] -deps = ["ADTypes", "Accessors", "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 = "33e4292e832d439c3706410ae867f3c091d79155" +deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "FastBroadcast", "FastClosures", "FastPower", "FillArrays", "FunctionWrappersWrappers", "InteractiveUtils", "LinearAlgebra", "Logging", "MacroTools", "MuladdMacro", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleUnPack", "Static", "StaticArrayInterface", "StaticArraysCore", "SymbolicIndexingInterface", "TruncatedStacktraces"] +git-tree-sha1 = "72c77ae685fddb6291fff22dba13f4f32602475c" uuid = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" -version = "1.7.0" +version = "1.14.1" weakdeps = ["EnzymeCore"] [deps.OrdinaryDiffEqCore.extensions] @@ -1881,9 +1988,9 @@ 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" +git-tree-sha1 = "e4adff09a6d47b74c16e7e08f10fa32000e2e5ed" uuid = "4302a76b-040a-498a-8c04-15b101fed76b" -version = "1.1.0" +version = "1.3.0" [[deps.OrdinaryDiffEqExplicitRK]] deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "TruncatedStacktraces"] @@ -1892,22 +1999,22 @@ 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" +deps = ["ADTypes", "DiffEqBase", "ExponentialUtilities", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqVerner", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "8659d5fdfe0798bbb5bcd8dc8d08092744b6dfa4" uuid = "e0540318-69ee-4070-8777-9e2de6de23de" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqExtrapolation]] -deps = ["DiffEqBase", "FastBroadcast", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "RecursiveArrayTools", "Reexport"] -git-tree-sha1 = "fea595528a160ed5cade9eee217a9691b1d97714" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastPower", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "RecursiveArrayTools", "Reexport"] +git-tree-sha1 = "b8d852b23246b1427178520442e8e7d89aa1c64c" uuid = "becaefa8-8ca2-5cf9-886d-c06f3d2bd2c4" -version = "1.1.0" +version = "1.3.0" [[deps.OrdinaryDiffEqFIRK]] -deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLOperators"] -git-tree-sha1 = "795221c662698851328cb7787965ab4a180d9468" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastGaussQuadrature", "FastPower", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "39a52e278e21018585fc02c04a54f0ab98d03369" uuid = "5960d6e9-dd7a-4743-88e7-cf307b64f125" -version = "1.1.1" +version = "1.6.0" [[deps.OrdinaryDiffEqFeagin]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1928,10 +2035,10 @@ uuid = "d28bc4f8-55e1-4f49-af69-84c1a99f0f58" version = "1.1.0" [[deps.OrdinaryDiffEqIMEXMultistep]] -deps = ["DiffEqBase", "FastBroadcast", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Reexport"] -git-tree-sha1 = "9f8f52aad2399d7714b400ff9d203254b0a89c4a" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Reexport"] +git-tree-sha1 = "b35b6db127ad50bc502f72dbd5a84a1e1d1bbf01" uuid = "9f002381-b378-40b7-97a6-27a27c83f129" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqLinear]] deps = ["DiffEqBase", "ExponentialUtilities", "LinearAlgebra", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] @@ -1953,9 +2060,9 @@ 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" +git-tree-sha1 = "3a3eb0b7ef3f996c468d6f8013eac9525bcfd788" uuid = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8" -version = "1.2.1" +version = "1.3.0" [[deps.OrdinaryDiffEqNordsieck]] deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "Polyester", "RecursiveArrayTools", "Reexport", "Static"] @@ -1964,10 +2071,10 @@ 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" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "StaticArrays"] +git-tree-sha1 = "70e348c116ce62df4e4b4f90f3c8bb4a8164df31" uuid = "5dd0a6cf-3d4b-4314-aa06-06d4e299bc89" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqPRK]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "Reexport"] @@ -1989,15 +2096,15 @@ 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" +git-tree-sha1 = "749518f27e886164ee07e6df49b631beaca8c9ac" uuid = "43230ef6-c299-4910-a778-202eb28ce4ce" -version = "1.2.0" +version = "1.4.0" [[deps.OrdinaryDiffEqSDIRK]] -deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] -git-tree-sha1 = "f6683803a58de600ab7a26d2f49411c9923e9721" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "45eed1444fbfa510e1806d4153f83bd862d2d035" uuid = "2d112036-d095-4a1e-ab9a-08536f3ecdbf" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqSSPRK]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "Static", "StaticArrays"] @@ -2006,10 +2113,10 @@ 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" +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "RecursiveArrayTools", "Reexport", "StaticArrays"] +git-tree-sha1 = "f2533f086540db6eb3a5eddbecf963cbc4ab6015" uuid = "e3e12d00-db14-5390-b879-ac3dd2ef6296" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqStabilizedRK]] deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "StaticArrays"] @@ -2072,9 +2179,9 @@ version = "0.5.12" [[deps.Pango_jll]] deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e127b609fb9ecba6f201ba7ab753d5a605d53801" +git-tree-sha1 = "ed6834e95bd326c52d5675b4181386dfbe885afb" uuid = "36c8627f-9965-5494-a995-c6b170f724f3" -version = "1.54.1+0" +version = "1.55.5+0" [[deps.Parameters]] deps = ["OrderedCollections", "UnPack"] @@ -2116,21 +2223,21 @@ version = "0.3.3" [[deps.PlotThemes]] deps = ["PlotUtils", "Statistics"] -git-tree-sha1 = "6e55c6841ce3411ccb3457ee52fc48cb698d6fb0" +git-tree-sha1 = "41031ef3a1be6f5bbbf3e8073f210556daeae5ca" uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" -version = "3.2.0" +version = "3.3.0" [[deps.PlotUtils]] -deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" +deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "StableRNGs", "Statistics"] +git-tree-sha1 = "3ca9a356cd2e113c420f2c13bea19f8d3fb1cb18" uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.4.1" +version = "1.4.3" [[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", "TOML", "UUIDs", "UnicodeFun", "UnitfulLatexify", "Unzip"] -git-tree-sha1 = "45470145863035bb124ca51b320ed35d071cc6c2" +git-tree-sha1 = "dae01f8c2e069a683d3a6e17bbae5070ab94786f" uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.40.8" +version = "1.40.9" [deps.Plots.extensions] FileIOExt = "FileIO" @@ -2290,9 +2397,9 @@ version = "0.6.12" [[deps.RecursiveArrayTools]] deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "b034171b93aebc81b3e1890a036d13a9c4a9e3e0" +git-tree-sha1 = "32f824db4e5bab64e25a12b22483a30a6b813d08" uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" -version = "3.27.0" +version = "3.27.4" [deps.RecursiveArrayTools.extensions] RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" @@ -2301,6 +2408,7 @@ version = "3.27.0" RecursiveArrayToolsMonteCarloMeasurementsExt = "MonteCarloMeasurements" RecursiveArrayToolsReverseDiffExt = ["ReverseDiff", "Zygote"] RecursiveArrayToolsSparseArraysExt = ["SparseArrays"] + RecursiveArrayToolsStructArraysExt = "StructArrays" RecursiveArrayToolsTrackerExt = "Tracker" RecursiveArrayToolsZygoteExt = "Zygote" @@ -2311,6 +2419,7 @@ version = "3.27.0" MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" @@ -2378,9 +2487,9 @@ version = "0.7.0" [[deps.SIMD]] deps = ["PrecompileTools"] -git-tree-sha1 = "98ca7c29edd6fc79cd74c61accb7010a4e7aee33" +git-tree-sha1 = "52af86e35dd1b177d051b12681e1c581f53c281b" uuid = "fdea26ae-647d-5447-a871-4b548cad5224" -version = "3.6.0" +version = "3.7.0" [[deps.SIMDTypes]] git-tree-sha1 = "330289636fb8107c5f32088d2741e9fd7a061a5c" @@ -2394,10 +2503,10 @@ uuid = "476501e8-09a2-5ece-8869-fb82de89a1fa" 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 = "50ed64cd5ad79b0bef71fdb6a11d10c3448bfef0" +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"] +git-tree-sha1 = "3e5a9c5d6432b77a271646b4ada2573f239ac5c4" uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" -version = "2.56.1" +version = "2.70.0" [deps.SciMLBase.extensions] SciMLBaseChainRulesCoreExt = "ChainRulesCore" @@ -2419,16 +2528,16 @@ version = "2.56.1" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SciMLJacobianOperators]] -deps = ["ADTypes", "ConcreteStructs", "ConstructionBase", "DifferentiationInterface", "FastClosures", "LinearAlgebra", "SciMLBase", "SciMLOperators"] -git-tree-sha1 = "991d2a8900e687e2c693d587daa739c8fda01366" +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DifferentiationInterface", "FastClosures", "LinearAlgebra", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "f66048bb969e67bd7d1bdd03cd0b81219642bbd0" uuid = "19f34311-ddf3-4b8b-af20-060888a46c0e" -version = "0.1.0" +version = "0.1.1" [[deps.SciMLOperators]] deps = ["Accessors", "ArrayInterface", "DocStringExtensions", "LinearAlgebra", "MacroTools"] -git-tree-sha1 = "e39c5f217f9aca640c8e27ab21acf557a3967db5" +git-tree-sha1 = "6149620767866d4b0f0f7028639b6e661b6a1e44" uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" -version = "0.3.10" +version = "0.3.12" weakdeps = ["SparseArrays", "StaticArraysCore"] [deps.SciMLOperators.extensions] @@ -2437,9 +2546,9 @@ weakdeps = ["SparseArrays", "StaticArraysCore"] [[deps.SciMLStructures]] deps = ["ArrayInterface"] -git-tree-sha1 = "25514a6f200219cd1073e4ff23a6324e4a7efe64" +git-tree-sha1 = "0444a37a25fab98adbd90baa806ee492a3af133a" uuid = "53ae85a6-f571-4167-b2af-e1d143709226" -version = "1.5.0" +version = "1.6.1" [[deps.Scratch]] deps = ["Dates"] @@ -2486,22 +2595,22 @@ uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" 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 = "44021f3efc023be3871195d8ad98b865001a2fa1" +deps = ["ADTypes", "ArrayInterface", "BracketingNonlinearSolve", "CommonSolve", "ConcreteStructs", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "a3868a6add9f5989d1f4bd21de0333ef89fb9d9f" uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" -version = "1.12.3" +version = "2.1.0" [deps.SimpleNonlinearSolve.extensions] SimpleNonlinearSolveChainRulesCoreExt = "ChainRulesCore" + SimpleNonlinearSolveDiffEqBaseExt = "DiffEqBase" SimpleNonlinearSolveReverseDiffExt = "ReverseDiff" SimpleNonlinearSolveTrackerExt = "Tracker" - SimpleNonlinearSolveZygoteExt = "Zygote" [deps.SimpleNonlinearSolve.weakdeps] ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" - Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SimpleTraits]] deps = ["InteractiveUtils", "MacroTools"] @@ -2535,31 +2644,11 @@ deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" version = "1.11.0" -[[deps.SparseConnectivityTracer]] -deps = ["ADTypes", "DocStringExtensions", "FillArrays", "LinearAlgebra", "Random", "Requires", "SparseArrays"] -git-tree-sha1 = "e5efbf152d3e14a513f19a9119b810340b7ac86b" -uuid = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" -version = "0.6.6" - - [deps.SparseConnectivityTracer.extensions] - SparseConnectivityTracerDataInterpolationsExt = "DataInterpolations" - SparseConnectivityTracerLogExpFunctionsExt = "LogExpFunctions" - SparseConnectivityTracerNNlibExt = "NNlib" - SparseConnectivityTracerNaNMathExt = "NaNMath" - SparseConnectivityTracerSpecialFunctionsExt = "SpecialFunctions" - - [deps.SparseConnectivityTracer.weakdeps] - DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" - LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688" - NNlib = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" - NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" - SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" - [[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 = "7db12cef226aaa8ca730040c9c35e32b86a69b83" +deps = ["ADTypes", "Adapt", "ArrayInterface", "Compat", "DataStructures", "FiniteDiff", "ForwardDiff", "Graphs", "LinearAlgebra", "PackageExtensionCompat", "Random", "Reexport", "SciMLOperators", "Setfield", "SparseArrays", "StaticArrayInterface", "StaticArrays", "UnPack", "VertexSafeGraphs"] +git-tree-sha1 = "d79802152d958607f414f5447cb25e314db979c0" uuid = "47a9eef4-7e08-11e9-0b38-333d64bd3804" -version = "2.22.0" +version = "2.23.1" [deps.SparseDiffTools.extensions] SparseDiffToolsEnzymeExt = "Enzyme" @@ -2576,10 +2665,14 @@ version = "2.22.0" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [[deps.SparseMatrixColorings]] -deps = ["ADTypes", "Compat", "DataStructures", "DocStringExtensions", "LinearAlgebra", "Random", "SparseArrays"] -git-tree-sha1 = "ad17e2069015839e58a1f9275608b405fae1f28e" +deps = ["ADTypes", "DataStructures", "DocStringExtensions", "LinearAlgebra", "Random", "SparseArrays"] +git-tree-sha1 = "76b44c879661552d64f382acf66faa29ab56b3d9" uuid = "0a514795-09f3-496d-8182-132a7b665d35" -version = "0.4.6" +version = "0.4.10" +weakdeps = ["Colors"] + + [deps.SparseMatrixColorings.extensions] + SparseMatrixColoringsColorsExt = "Colors" [[deps.Sparspak]] deps = ["Libdl", "LinearAlgebra", "Logging", "OffsetArrays", "Printf", "SparseArrays", "Test"] @@ -2589,14 +2682,20 @@ version = "0.3.9" [[deps.SpecialFunctions]] deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14" +git-tree-sha1 = "64cca0c26b4f31ba18f13f6c12af7c85f478cfde" uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.4.0" +version = "2.5.0" weakdeps = ["ChainRulesCore"] [deps.SpecialFunctions.extensions] SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" +[[deps.StableRNGs]] +deps = ["Random"] +git-tree-sha1 = "83e6cce8324d49dfaf9ef059227f91ed4441a8e5" +uuid = "860ef19b-820b-49d6-a774-d7a799459cd3" +version = "1.0.2" + [[deps.StableTasks]] git-tree-sha1 = "073d5c20d44129b20fe954720b97069579fa403b" uuid = "91464d47-22a1-43fe-8b7f-2d57ee82463f" @@ -2627,9 +2726,9 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "eeafab08ae20c62c44c8399ccb9354a04b80db50" +git-tree-sha1 = "7c01731da8ab6d3094c4d44c9057b00932459255" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.7" +version = "1.9.9" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -2658,10 +2757,10 @@ uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" version = "1.7.0" [[deps.StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "5cf7606d6cef84b543b483848d4ae08ad9832b21" +deps = ["AliasTables", "DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "29321314c920c26684834965ec2ce0dacc9cf8e5" uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.34.3" +version = "0.34.4" [[deps.StatsFuns]] deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] @@ -2688,17 +2787,25 @@ version = "0.3.7" [[deps.StructArrays]] deps = ["ConstructionBase", "DataAPI", "Tables"] -git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be" +git-tree-sha1 = "9537ef82c42cdd8c5d443cbc359110cbb36bae10" uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" -version = "0.6.18" -weakdeps = ["Adapt", "GPUArraysCore", "SparseArrays", "StaticArrays"] +version = "0.6.21" [deps.StructArrays.extensions] StructArraysAdaptExt = "Adapt" - StructArraysGPUArraysCoreExt = "GPUArraysCore" + StructArraysGPUArraysCoreExt = ["GPUArraysCore", "KernelAbstractions"] + StructArraysLinearAlgebraExt = "LinearAlgebra" StructArraysSparseArraysExt = "SparseArrays" StructArraysStaticArraysExt = "StaticArrays" + [deps.StructArrays.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + [[deps.StructTypes]] deps = ["Dates", "UUIDs"] git-tree-sha1 = "159331b30e94d7b11379037feeb9b690950cace8" @@ -2720,9 +2827,9 @@ version = "7.7.0+0" [[deps.SymbolicIndexingInterface]] deps = ["Accessors", "ArrayInterface", "RuntimeGeneratedFunctions", "StaticArraysCore"] -git-tree-sha1 = "0225f7c62f5f78db35aae6abb2e5cabe38ce578f" +git-tree-sha1 = "8db233b54917e474165d582bef2244fa040e0a56" uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" -version = "0.3.31" +version = "0.3.36" [[deps.TOML]] deps = ["Dates"] @@ -2768,6 +2875,11 @@ deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" version = "1.11.0" +[[deps.TestItems]] +git-tree-sha1 = "42fd9023fef18b9b78c8343a4e2f3813ffbcefcb" +uuid = "1c621080-faea-4a02-84b6-bbd5e436b8fe" +version = "1.0.0" + [[deps.ThreadingUtilities]] deps = ["ManualMemory"] git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" @@ -2782,9 +2894,9 @@ version = "0.11.2" [[deps.TimerOutputs]] deps = ["ExprTools", "Printf"] -git-tree-sha1 = "3a6f063d690135f5c1ba351412c82bae4d1402bf" +git-tree-sha1 = "d7298ebdfa1654583468a487e8e83fae9d72dac3" uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" -version = "0.5.25" +version = "0.5.26" [[deps.TranscodingStreams]] git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" @@ -2797,11 +2909,6 @@ git-tree-sha1 = "be986ad9dac14888ba338c2554dcfec6939e1393" uuid = "d5829a12-d9aa-46ab-831f-fb7c9ab06edf" version = "0.2.1" -[[deps.Tricks]] -git-tree-sha1 = "7822b97e99a1672bfb1b49b668a6d46d58d8cbcb" -uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775" -version = "0.1.9" - [[deps.TriplotBase]] git-tree-sha1 = "4d4ed7f294cda19382ff7de4c137d24d16adc89b" uuid = "981d1d27-644d-49a2-9326-4793e63143c3" @@ -2840,9 +2947,9 @@ version = "0.4.1" [[deps.Unitful]] deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "d95fe458f26209c66a187b1114df96fd70839efd" +git-tree-sha1 = "01915bfcd62be15329c9a07235447a89d588327c" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.21.0" +version = "1.21.1" weakdeps = ["ConstructionBase", "InverseFunctions"] [deps.Unitful.extensions] @@ -2873,9 +2980,9 @@ version = "0.4.9" [[deps.VectorizationBase]] deps = ["ArrayInterface", "CPUSummary", "HostCPUFeatures", "IfElse", "LayoutPointers", "Libdl", "LinearAlgebra", "SIMDTypes", "Static", "StaticArrayInterface"] -git-tree-sha1 = "e7f5b81c65eb858bed630fe006837b935518aca5" +git-tree-sha1 = "4ab62a49f1d8d9548a1c8d1a75e5f55cf196f64e" uuid = "3d5dd08c-fd9d-11e8-17fa-ed2836048c2f" -version = "0.21.70" +version = "0.21.71" [[deps.VertexSafeGraphs]] deps = ["Graphs"] @@ -2891,15 +2998,15 @@ version = "1.3.243+0" [[deps.Wayland_jll]] deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703" +git-tree-sha1 = "85c7811eddec9e7f22615371c3cc81a504c508ee" uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.21.0+1" +version = "1.21.0+2" [[deps.Wayland_protocols_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "93f43ab61b16ddfb2fd3bb13b3ce241cafb0e6c9" +git-tree-sha1 = "5db3e9d307d32baba7067b13fc7b5aa6edd4a19a" uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" -version = "1.31.0+0" +version = "1.36.0+0" [[deps.WebP]] deps = ["CEnum", "ColorTypes", "FileIO", "FixedPointNumbers", "ImageCore", "libwebp_jll"] @@ -2915,27 +3022,27 @@ version = "1.0.0" [[deps.WriteVTK]] deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] -git-tree-sha1 = "e453383a1486a9020a3323bf3665ea31106ebe9a" +git-tree-sha1 = "1d8042d58334ab7947ce505709df7009da6f3375" uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192" -version = "1.21.0" +version = "1.21.1" [[deps.XML2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "1165b0443d0eca63ac1e32b8c0eb69ed2f4f8127" +git-tree-sha1 = "a2fccc6559132927d4c5dc183e3e01048c6dcbd6" uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.13.3+0" +version = "2.13.5+0" [[deps.XSLT_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "a54ee957f4c86b526460a720dbc882fa5edcbefc" +git-tree-sha1 = "7d1671acbe47ac88e981868a078bd6b4e27c5191" uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.41+0" +version = "1.1.42+0" [[deps.XZ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "ac88fb95ae6447c8dda6a5503f3bafd496ae8632" +git-tree-sha1 = "15e637a697345f6743674f1322beefbc5dcd5cfc" uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.4.6+0" +version = "5.6.3+0" [[deps.Xorg_libICE_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -2951,39 +3058,39 @@ version = "1.2.4+0" [[deps.Xorg_libX11_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" +git-tree-sha1 = "9dafcee1d24c4f024e7edc92603cedba72118283" uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+0" +version = "1.8.6+1" [[deps.Xorg_libXau_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" +git-tree-sha1 = "2b0e27d52ec9d8d483e2ca0b72b3cb1a8df5c27a" uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.11+0" +version = "1.0.11+1" [[deps.Xorg_libXcursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "807c226eaf3651e7b2c468f687ac788291f9a89b" uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" -version = "1.2.0+4" +version = "1.2.3+0" [[deps.Xorg_libXdmcp_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" +git-tree-sha1 = "02054ee01980c90297412e4c809c8694d7323af3" uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.4+0" +version = "1.1.4+1" [[deps.Xorg_libXext_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "d2d1a5c49fae4ba39983f63de6afcbea47194e85" +git-tree-sha1 = "d7155fea91a4123ef59f42c4afb5ab3b4ca95058" uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.6+0" +version = "1.3.6+1" [[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "6fcc21d5aea1a0b7cce6cab3e62246abd1949b86" uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" +version = "6.0.0+0" [[deps.Xorg_libXft_jll]] deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] @@ -2992,46 +3099,46 @@ uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" version = "2.3.3+1" [[deps.Xorg_libXi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] -git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "984b313b049c89739075b8e2a94407076de17449" uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" -version = "1.7.10+4" +version = "1.8.2+0" [[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll"] +git-tree-sha1 = "a1a7eaf6c3b5b05cb903e35e8372049b107ac729" uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" +version = "1.1.5+0" [[deps.Xorg_libXrandr_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "b6f664b7b2f6a39689d822a6300b14df4668f0f4" uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" -version = "1.5.2+4" +version = "1.5.4+0" [[deps.Xorg_libXrender_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "47e45cd78224c53109495b3e324df0c37bb61fbe" +git-tree-sha1 = "a490c6212a0e90d2d55111ac956f7c4fa9c277a6" uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.11+0" +version = "0.9.11+1" [[deps.Xorg_libpthread_stubs_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" +git-tree-sha1 = "fee57a273563e273f0f53275101cd41a8153517a" uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.1+0" +version = "0.1.1+1" [[deps.Xorg_libxcb_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "bcd466676fef0878338c61e655629fa7bbc69d8e" +git-tree-sha1 = "1a74296303b6524a0472a8cb12d3d87a78eb3612" uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.0+0" +version = "1.17.0+1" [[deps.Xorg_libxkbfile_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "730eeca102434283c50ccf7d1ecdadf521a765a4" +git-tree-sha1 = "dbc53e4cf7701c6c7047c51e17d6e64df55dca94" uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" -version = "1.1.2+0" +version = "1.1.2+1" [[deps.Xorg_xcb_util_cursor_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] @@ -3071,9 +3178,9 @@ version = "0.4.1+1" [[deps.Xorg_xkbcomp_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] -git-tree-sha1 = "330f955bc41bb8f5270a369c473fc4a5a4e4d3cb" +git-tree-sha1 = "ab2221d309eda71020cdda67a973aa582aa85d69" uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" -version = "1.4.6+0" +version = "1.4.6+1" [[deps.Xorg_xkeyboard_config_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] @@ -3083,9 +3190,9 @@ version = "2.39.0+0" [[deps.Xorg_xtrans_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" +git-tree-sha1 = "b9ead2d2bdb27330545eb14234a2e300da61232e" uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.0+0" +version = "1.5.0+1" [[deps.YAML]] deps = ["Base64", "Dates", "Printf", "StringEncodings"] @@ -3112,9 +3219,9 @@ version = "3.2.9+0" [[deps.fzf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "936081b536ae4aa65415d869287d43ef3cb576b2" +git-tree-sha1 = "6e50f145003024df4f5cb96c7fce79466741d601" uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" -version = "0.53.0+0" +version = "0.56.3+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"] @@ -3124,9 +3231,9 @@ version = "4.13.1+0" [[deps.gperf_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3516a5630f741c9eecb3720b1ec9d8edc3ecc033" +git-tree-sha1 = "0ba42241cb6809f1a278d0bcb976e0483c3f1f2d" uuid = "1a1c6b14-54f6-533d-8383-74cd7377aa70" -version = "3.1.1+0" +version = "3.1.1+1" [[deps.isoband_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] @@ -3183,15 +3290,15 @@ version = "1.18.0+0" [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "b70c870239dc3d7bc094eb2d6be9b73d27bef280" +git-tree-sha1 = "9c42636e3205e555e5785e902387be0061e7efc1" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.44+0" +version = "1.6.44+1" [[deps.libsixel_jll]] deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] -git-tree-sha1 = "7dfa0fd9c783d3d0cc43ea1af53d69ba45c447df" +git-tree-sha1 = "80c5ae2c7b5163441018f4666b179f1ffca194c1" uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" -version = "1.10.3+1" +version = "1.10.3+2" [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] @@ -3241,6 +3348,6 @@ version = "3.5.0+0" [[deps.xkbcommon_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] -git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37" +git-tree-sha1 = "63406453ed9b33a0df95d570816d5366c92b7809" uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" -version = "1.4.1+1" +version = "1.4.1+2" diff --git a/src/FEValues/EdgeValues.jl b/src/FEValues/EdgeValues.jl new file mode 100644 index 0000000000..1c6de9168f --- /dev/null +++ b/src/FEValues/EdgeValues.jl @@ -0,0 +1,174 @@ +""" + EdgeValues([::Type{T}], quad_rule::EdgeQuadratureRule, func_interpol::Interpolation, [geom_interpol::Interpolation]) + +A `EdgeValues` object facilitates the process of evaluating values of shape functions, gradients of shape functions, +values of nodal functions, gradients and divergences of nodal functions etc. on the edges of finite elements. + +**Arguments:** + +* `T`: an optional argument (default to `Float64`) to determine the type the internal data is stored as. +* `quad_rule`: an instance of a [`EdgeQuadratureRule`](@ref) +* `func_interpol`: an instance of an [`Interpolation`](@ref) used to interpolate the approximated function +* `geom_interpol`: an optional instance of an [`Interpolation`](@ref) which is used to interpolate the geometry. + By default linear Lagrange interpolation is used. + +**Keyword arguments:** The following keyword arguments are experimental and may change in future minor releases + +* `update_gradients`: Specifies if the gradients of the shape functions should be updated (default true) +* `update_hessians`: Specifies if the hessians of the shape functions should be updated (default false) + +**Common methods:** + +* [`reinit!`](@ref) +* [`getnquadpoints`](@ref) +* [`getdetJdV`](@ref) + +* [`shape_value`](@ref) +* [`shape_gradient`](@ref) +* [`shape_symmetric_gradient`](@ref) +* [`shape_divergence`](@ref) + +* [`function_value`](@ref) +* [`function_gradient`](@ref) +* [`function_symmetric_gradient`](@ref) +* [`function_divergence`](@ref) +* [`spatial_coordinate`](@ref) +* [`gettangent`](@ref) +""" +EdgeValues + +mutable struct EdgeValues{FV, GM, EQR, detT, tT, V_FV <: AbstractVector{FV}, V_GM <: AbstractVector{GM}} <: AbstractValues + const fun_values::V_FV # AbstractVector{FunctionValues} + const geo_mapping::V_GM # AbstractVector{GeometryMapping} + const eqr::EQR # EdgeQuadratureRule + const detJdV::detT # AbstractVector{<:Number} + const tangents::tT # AbstractVector{<:Vec} + current_edge::Int +end + +function EdgeValues( + ::Type{T}, eqr::EdgeQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}, + ::ValuesUpdateFlags{FunDiffOrder, GeoDiffOrder} + ) where {T, sdim, FunDiffOrder, GeoDiffOrder} + + # max(GeoDiffOrder, 1) ensures that we get the jacobian needed to calculate the tangent. + geo_mapping = map(qr -> GeometryMapping{max(GeoDiffOrder, 1)}(T, ip_geo.ip, qr), eqr.edge_rules) + fun_values = map(qr -> FunctionValues{FunDiffOrder}(T, ip_fun, qr, ip_geo), eqr.edge_rules) + max_nquadpoints = maximum(qr -> length(getweights(qr)), eqr.edge_rules) + # detJdV always calculated, since we needed to calculate the jacobian anyways for the tangent. + detJdV = fill(T(NaN), max_nquadpoints) + tangents = fill(zero(Vec{sdim, T}) * T(NaN), max_nquadpoints) + return EdgeValues(fun_values, geo_mapping, eqr, detJdV, tangents, 1) +end + +EdgeValues(qr::EdgeQuadratureRule, ip::Interpolation, args...; kwargs...) = EdgeValues(Float64, qr, ip, args...; kwargs...) +function EdgeValues(::Type{T}, qr::EdgeQuadratureRule, ip::Interpolation, ip_geo::ScalarInterpolation; kwargs...) where {T} + return EdgeValues(T, qr, ip, VectorizedInterpolation(ip_geo); kwargs...) +end +function EdgeValues(::Type{T}, qr::EdgeQuadratureRule, ip::Interpolation, ip_geo::VectorizedInterpolation = default_geometric_interpolation(ip); kwargs...) where {T} + return EdgeValues(T, qr, ip, ip_geo, ValuesUpdateFlags(ip; kwargs...)) +end + +function Base.copy(ev::EdgeValues) + fun_values = map(copy, ev.fun_values) + geo_mapping = map(copy, ev.geo_mapping) + return EdgeValues(fun_values, geo_mapping, copy(ev.eqr), copy(ev.detJdV), copy(ev.tangents), ev.current_edge) +end + +getngeobasefunctions(ev::EdgeValues) = getngeobasefunctions(get_geo_mapping(ev)) +getnbasefunctions(ev::EdgeValues) = getnbasefunctions(get_fun_values(ev)) +getnquadpoints(ev::EdgeValues) = @inbounds getnquadpoints(ev.eqr, getcurrentedge(ev)) +@propagate_inbounds getdetJdV(ev::EdgeValues, q_point) = ev.detJdV[q_point] + +shape_value_type(ev::EdgeValues) = shape_value_type(get_fun_values(ev)) +shape_gradient_type(ev::EdgeValues) = shape_gradient_type(get_fun_values(ev)) +function_interpolation(ev::EdgeValues) = function_interpolation(get_fun_values(ev)) +function_difforder(ev::EdgeValues) = function_difforder(get_fun_values(ev)) +geometric_interpolation(ev::EdgeValues) = geometric_interpolation(get_geo_mapping(ev)) + +get_geo_mapping(ev::EdgeValues) = @inbounds ev.geo_mapping[getcurrentedge(ev)] +@propagate_inbounds geometric_value(ev::EdgeValues, args...) = geometric_value(get_geo_mapping(ev), args...) + +get_fun_values(ev::EdgeValues) = @inbounds ev.fun_values[getcurrentedge(ev)] + +@propagate_inbounds shape_value(ev::EdgeValues, q_point::Int, i::Int) = shape_value(get_fun_values(ev), q_point, i) +@propagate_inbounds shape_gradient(ev::EdgeValues, q_point::Int, i::Int) = shape_gradient(get_fun_values(ev), q_point, i) +@propagate_inbounds shape_hessian(ev::EdgeValues, q_point::Int, i::Int) = shape_hessian(get_fun_values(ev), q_point, i) +@propagate_inbounds shape_symmetric_gradient(ev::EdgeValues, q_point::Int, i::Int) = shape_symmetric_gradient(get_fun_values(ev), q_point, i) + +""" + getcurrentedge(ev::EdgeValues) + +Return the current active edge of the `EdgeValues` object (from last `reinit!`). +""" +getcurrentedge(ev::EdgeValues) = ev.current_edge[] + +""" + gettangent(ev::EdgeValues, qp::Int) + +Return the edge tangent at the quadrature point `qp` for the active edge of the +`EdgeValues` object(from last `reinit!`). +""" +gettangent(ev::EdgeValues, qp::Int) = ev.tangents[qp] + +nedges(ev::EdgeValues) = length(ev.geo_mapping) + +function set_current_edge!(ev::EdgeValues, edge_nr::Int) + # Checking edge_nr before setting current_edge allows us to use @inbounds + # when indexing by getcurrentedge(ev) in other places! + checkbounds(Bool, 1:nedges(ev), edge_nr) || throw(ArgumentError("Edge index out of range.")) + ev.current_edge = edge_nr + return +end + +@inline function reinit!(ev::EdgeValues, x::AbstractVector, edge_nr::Int) + return reinit!(ev, nothing, x, edge_nr) +end + +function reinit!(ev::EdgeValues, cell::Union{AbstractCell, Nothing}, x::AbstractVector{Vec{dim, T}}, edge_nr::Int) where {dim, T} + check_reinit_sdim_consistency(:EdgeValues, shape_gradient_type(ev), eltype(x)) + set_current_edge!(ev, edge_nr) + n_geom_basefuncs = getngeobasefunctions(ev) + if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x) != n_geom_basefuncs + throw_incompatible_coord_length(length(x), n_geom_basefuncs) + end + + geo_mapping = get_geo_mapping(ev) + fun_values = get_fun_values(ev) + + if cell === nothing && !isa(mapping_type(fun_values), IdentityMapping) + throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) + end + + @inbounds for (q_point, w) in pairs(getweights(ev.eqr, edge_nr)) + mapping = calculate_mapping(geo_mapping, q_point, x) + J = getjacobian(mapping) + Wt = weighted_tangent(J, getrefshape(geo_mapping.ip), edge_nr) + detJ = norm(Wt) + @inbounds ev.detJdV[q_point] = detJ * w + @inbounds ev.tangents[q_point] = Wt / detJ + apply_mapping!(fun_values, q_point, mapping, cell) + end + return +end + +function Base.show(io::IO, d::MIME"text/plain", ev::EdgeValues) + ip_geo = geometric_interpolation(ev) + rdim = getrefdim(ip_geo) + vdim = isa(shape_value(ev, 1, 1), Vec) ? length(shape_value(ev, 1, 1)) : 0 + GradT = shape_gradient_type(ev) + sdim = GradT === nothing ? nothing : sdim_from_gradtype(GradT) + vstr = vdim == 0 ? "scalar" : "vdim=$vdim" + print(io, "EdgeValues(", vstr, ", rdim=$rdim, sdim=$sdim): ") + nqp = getnquadpoints.(ev.eqr.face_rules) + if all(n == first(nqp) for n in nqp) + println(io, first(nqp), " quadrature points per edge") + else + println(io, tuple(nqp...), " quadrature points on each edge") + end + print(io, " Function interpolation: "); show(io, d, function_interpolation(ev)) + print(io, "\nGeometric interpolation: ") + return sdim === nothing ? show(io, d, ip_geo) : show(io, d, ip_geo^sdim) + sdim === nothing ? show(io, d, ip_geo) : show(io, d, ip_geo^sdim) + return +end diff --git a/src/FEValues/boundary_integrals.jl b/src/FEValues/boundary_integrals.jl new file mode 100644 index 0000000000..918d09e33b --- /dev/null +++ b/src/FEValues/boundary_integrals.jl @@ -0,0 +1,413 @@ +# Generalization of `face[t]_integrals.jl` +# Here we use consistent naming, so quick backwards compat for testing, +facet_to_element_transformation(ξ, ::Type{RS}, nr) where {RS} = facet_to_cell_transformation(ξ, RS, nr) +element_to_facet_transformation(ξ, ::Type{RS}, nr) where {RS} = cell_to_facet_transformation(ξ, RS, nr) + +""" + edge_to_cell_transformation(ξ::Vec{1}, cell_refshape::Type{<:AbstractRefShape{rdim}}, edgenr::Int) where rdim + +Transform local point, `ξ`, from the edge's reference coordinates to the cell's +reference coordinates. The returned `Vec{rdim}` is located on the cell's edge `edgenr`. +""" +edge_to_cell_transformation + +""" + cell_to_edge_transformation(ξ::Vec{rdim}, ::Type{<:AbstractRefShape{rdim}}, edgenr::Int) where rdim + +This is the inverse of `edge_to_cell_transformation`, returning a `Vec{1}`. +`ξ` must be located on edge `edgenr` for correct transformation. +""" +cell_to_edge_transformation + +""" + face_to_cell_transformation(ξ::Vec{2}, ::Type{<:AbstractRefShape{rdim}}, facenr::Int) where rdim + +Transform local point, `ξ`, from the face's reference coordinates to the cell's +reference coordinates. The returned `Vec{rdim}` is located on the cell's face `facenr`. +""" +face_to_cell_transformation + +""" + cell_to_face_transformation(ξ::Vec{rdim}, ::Type{<:AbstractRefShape{rdim}}, facenr::Int) where rdim + +This is the inverse of `face_to_cell_transformation`, returning a `Vec{2}`. +`ξ` must be located on face `facenr` for correct transformation. +""" +cell_to_face_transformation + +""" + weighted_normal(J::AbstractTensor, fv::FacetValues, face::Int) + weighted_normal(J::AbstractTensor, ::Type{<:AbstractRefShape}, face::Int) + +Compute the vector normal to the facet weighted by the area ratio between the facet and the +reference facet. This is computed by taking the cross product of the Jacobian components that +align to the facet's local axis. +""" +function weighted_normal end + +@doc raw""" + weighted_tangent(J, RefShape, edgenr::Int) + +To integrate a function, `f(x)=f̂(ξ(x))`, along the edge, ``E``, +using a quadrature rule described in the reference coordinates, `ξ`, +we transform the integral to an integral over the reference edge, ``\hat{E}`` as, +```math +\int_{E} \boldsymbol{f} ⋅ \mathrm{d}\boldsymbol{E} = \int_{\hat{E}} \boldsymbol{f} ⋅ \boldsymbol{J} ⋅ \mathrm{d}\hat{\boldsymbol{E}}, +``` +where ``\boldsymbol{J} = \frac{\partial x}{\partial \xi}`` is the jacobian of the mapping. +When integrating over one edge on the reference element, the direction, ``d\hat{\boldsymbol{E}}``, +is known and constant. W can thus express this integral by using the weighted tangent, +``\boldsymbol{W}_t``, and the scalar differential, ``\mathrm{d}s``, such that +```math +\boldsymbol{W}_t \mathrm{d}s = \boldsymbol{J} ⋅ \mathrm{d}\hat{\boldsymbol{E}} +``` +""" +function weighted_tangent end + +""" + create_facet_quad_rule(::Type{RefShape}, w::AbstractVectorä{T}, p::AbstractVectorä{Vec{N, T}}) + create_facet_quad_rule( + ::Type{RefShape}, + 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::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)] + push!(facet_quad_rule, QuadratureRule{RefShape}(copy(w), new_points)) + end + return FacetQuadratureRule(facet_quad_rule) +end + +# For cells with mixed faces +function create_facet_quad_rule( + ::Type{RefShape}, + 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 + new_points = [facet_to_element_transformation(p_quad[i], RefShape, facet) for i in 1:length(w_quad)] + facet_quad_rule[facet] = QuadratureRule{RefShape}(copy(w_quad), new_points) + end + for facet in tri_facets + new_points = [facet_to_element_transformation(p_tri[i], RefShape, facet) for i in 1:length(w_tri)] + facet_quad_rule[facet] = QuadratureRule{RefShape}(copy(w_tri), new_points) + end + return FacetQuadratureRule(facet_quad_rule) +end + +function create_edge_quad_rule(::Type{RefShape}, w::AbstractVector{T}, p::AbstractVector{<:Vec{1, T}}) where {T, RefShape <: AbstractRefShape} + edge_quad_rule = ntuple(nedges(RefShape)) do edgenr + new_points = map(ξ -> edge_to_cell_transformation(ξ, RefShape, edgenr), p) + QuadratureRule{RefShape}(copy(w), new_points) + end + return EdgeQuadratureRule(SVector(edge_quad_rule)) +end + +# facet definitions for differenent reference dimensions +# rdim = 2, facet == edge +function facet_to_cell_transformation(ξ::Vec{1}, ::Type{RS}, facetnr::Int) where {RS <: AbstractRefShape{2}} + return edge_to_cell_transformation(ξ, RS, facetnr) +end +function cell_to_facet_transformation(ξ::Vec{2}, ::Type{RS}, facetnr::Int) where {RS <: AbstractRefShape{2}} + return cell_to_edge_transformation(ξ, RS, facetnr) +end +# rdim = 3, facet == face +function facet_to_cell_transformation(ξ::Vec{2}, ::Type{RS}, facetnr::Int) where {RS <: AbstractRefShape{3}} + return face_to_cell_transformation(ξ, RS, facetnr) +end +function cell_to_facet_transformation(ξ::Vec{3}, ::Type{RS}, facetnr::Int) where {RS <: AbstractRefShape{3}} + return cell_to_face_transformation(ξ, RS, facetnr) +end + +################## +# All 1D RefLine # +################## +# Special cases since we don't have cell_to_vertex and vertex_to_cell +# Mapping from to 0D node to 1D line vertex. +function facet_to_element_transformation(::Union{Vec{0, T}, Vec{1, T}}, ::Type{RefLine}, face::Int) where {T} + face == 1 && return Vec{1, T}((-one(T),)) + face == 2 && return Vec{1, T}((one(T),)) + throw(ArgumentError("unknown facet number")) +end + +# Mapping from 1D line to point. +function element_to_facet_transformation(ξ::Vec{1, T}, ::Type{RefLine}, face::Int) where {T} + x = ξ[1] + face == 1 && return Vec(-x) + face == 2 && return Vec(x) + throw(ArgumentError("unknown facet number")) +end + +function weighted_normal(::Tensor{2, 1, T}, ::Type{RefLine}, face::Int) where {T} + face == 1 && return Vec{1, T}((-one(T),)) + face == 2 && return Vec{1, T}((one(T),)) + throw(ArgumentError("unknown facet number")) +end + +function weighted_tangent(J::Tensor{2, 1}, ::Type{RefLine}, edgenr::Int) + @inbounds begin + edgenr == 1 && return Vec((J[1, 1],)) + end + throw(ArgumentError("RefLine requires edgenr == 1")) +end + +########################### +# All 2D RefQuadrilateral # +########################### + +# Mapping from 1D line to 2D face of a quadrilateral. +function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefQuadrilateral}, edge::Int) where {T} + x = ξ[1] + face == 1 && return Vec{2, T}((x, -one(T))) + face == 2 && return Vec{2, T}((one(T), x)) + face == 3 && return Vec{2, T}((-x, one(T))) + face == 4 && return Vec{2, T}((-one(T), -x)) + throw(ArgumentError("unknown edge number")) +end + +# Mapping from 2D face of a quadrilateral to 1D line. +function cell_to_edge_transformation(ξ::Vec{2, T}, ::Type{RefQuadrilateral}, edge::Int) where {T} + x, y = ξ + face == 1 && return Vec(x) + face == 2 && return Vec(y) + face == 3 && return Vec(-x) + face == 4 && return Vec(-y) + throw(ArgumentError("unknown edge number")) +end + +function weighted_normal(J::Tensor{2, 2}, ::Type{RefQuadrilateral}, face::Int) + @inbounds begin + face == 1 && return Vec{2}((J[2, 1], -J[1, 1])) + face == 2 && return Vec{2}((J[2, 2], -J[1, 2])) + face == 3 && return Vec{2}((-J[2, 1], J[1, 1])) + face == 4 && return Vec{2}((-J[2, 2], J[1, 2])) + end + throw(ArgumentError("unknown facet number")) +end + +function weighted_tangent(J::Tensor{2, 2}, ::Type{RefQuadrilateral}, edgenr::Int) + @inbounds begin + edgenr == 1 && return J[:, 1] # dÊ = [ 1, 0] ds + edgenr == 2 && return J[:, 2] # dÊ = [ 0, 1] ds + edgenr == 3 && return -J[:, 1] # dÊ = [-1, 0] ds + edgenr == 4 && return -J[:, 2] # dÊ = [ 0, -1] ds + end + throw(ArgumentError("RefQuadrilateral requires edgenr ∈ 1:4")) +end + +###################### +# All RefTriangle 2D # +###################### + +# Mapping from 1D line to 2D face of a triangle. +function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefTriangle}, edgenr::Int) where {T} + x = (ξ[1] + one(T)) / 2 + edgenr == 1 && return Vec{2, T}((one(T) - x, x)) + edgenr == 2 && return Vec{2, T}((zero(T), one(T) - x)) + edgenr == 3 && return Vec{2, T}((x, zero(T))) + throw(ArgumentError("unknown edgenr number")) +end + +# Mapping from 2D face of a triangle to 1D line. +function cell_to_edge_transformation(ξ::Vec{2, T}, ::Type{RefTriangle}, facet::Int) where {T} + x, y = ξ + edgenr == 1 && return Vec(one(T) - x * 2) + edgenr == 2 && return Vec(one(T) - y * 2) + edgenr == 3 && return Vec(x * 2 - one(T)) + throw(ArgumentError("unknown edgenr number")) +end + +function weighted_normal(J::Tensor{2, 2}, ::Type{RefTriangle}, facet::Int) + @inbounds begin + facet == 1 && return Vec{2}((-(J[2, 1] - J[2, 2]), J[1, 1] - J[1, 2])) + facet == 2 && return Vec{2}((-J[2, 2], J[1, 2])) + facet == 3 && return Vec{2}((J[2, 1], -J[1, 1])) + end + throw(ArgumentError("unknown facet number")) +end + +function weighted_tangent(J::Tensor{2, 2}, ::Type{RefTriangle}, edgenr::Int) + @inbounds begin + edgenr == 1 && return (J[:, 2] - J[:, 1]) # dÊ = [-1, 1] ds + edgenr == 2 && return -J[:, 2] # dÊ = [0, -1] ds + edgenr == 3 && return J[:, 1] # dÊ = [1, 0] ds + end + throw(ArgumentError("RefTriangle requires edgenr ∈ 1:3")) +end + +######################## +# All RefHexahedron 3D # +######################## + +# TODO: Mapping from 1D line to 3D edge of a hexahedron +# function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefHexahedron}, edgenr::Int) where {T} +# TODO: Mapping from 3D edge of a hexahedron to 1D line +# function cell_to_edge_transformation(ξ::Vec{3, T}, ::Type{RefHexahedron}, edgenr::Int) where {T} + +# Mapping from 2D quadrilateral to 3D face of a hexahedron. +function face_to_cell_transformation(ξ::Vec{2, T}, ::Type{RefHexahedron}, facenr::Int) where {T} + x, y = ξ + facenr == 1 && return Vec{3, T}((y, x, -one(T))) + facenr == 2 && return Vec{3, T}((x, -one(T), y)) + facenr == 3 && return Vec{3, T}((one(T), x, y)) + facenr == 4 && return Vec{3, T}((-x, one(T), y)) + facenr == 5 && return Vec{3, T}((-one(T), y, x)) + facenr == 6 && return Vec{3, T}((x, y, one(T))) + throw(ArgumentError("unknown face number")) +end + +# Mapping from 3D face of a hexahedron to 2D quadrilateral. +function cell_to_face_transformation(ξ::Vec{3, T}, ::Type{RefHexahedron}, facenr::Int) where {T} + x, y, z = ξ + facenr == 1 && return Vec(y, x) + facenr == 2 && return Vec(x, z) + facenr == 3 && return Vec(y, z) + facenr == 4 && return Vec(-x, z) + facenr == 5 && return Vec(z, y) + facenr == 6 && return Vec(x, y) + throw(ArgumentError("unknown face number")) +end + +function weighted_normal(J::Tensor{2, 3}, ::Type{RefHexahedron}, face::Int) + @inbounds begin + face == 1 && return J[:, 2] × J[:, 1] + face == 2 && return J[:, 1] × J[:, 3] + face == 3 && return J[:, 2] × J[:, 3] + face == 4 && return J[:, 3] × J[:, 1] + face == 5 && return J[:, 3] × J[:, 2] + face == 6 && return J[:, 1] × J[:, 2] + end + throw(ArgumentError("unknown facet number")) +end + +######################### +# All RefTetrahedron 3D # +######################### + +# TODO: Mapping from 1D line to 3D edge of a tetrahedron +# function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefTetrahedron}, edgenr::Int) where {T} +# TODO: Mapping from 3D edge of a tetrahedron to 1D line +# function cell_to_edge_transformation(ξ::Vec{3, T}, ::Type{RefTetrahedron}, edgenr::Int) where {T} + +# Mapping from 2D triangle to 3D face of a tetrahedon. +function face_to_cell_transformation(ξ::Vec{2, T}, ::Type{RefTetrahedron}, facenr::Int) where {T} + x, y = ξ + facenr == 1 && return Vec{3, T}((one(T) - x - y, y, zero(T))) + facenr == 2 && return Vec{3, T}((y, zero(T), one(T) - x - y)) + facenr == 3 && return Vec{3, T}((x, y, one(T) - x - y)) + facenr == 4 && return Vec{3, T}((zero(T), one(T) - x - y, y)) + throw(ArgumentError("unknown facet number")) +end + +# Mapping from 3D face of a tetrahedon to 2D triangle. +function cell_to_face_transformation(ξ::Vec{3, T}, ::Type{RefTetrahedron}, facenr::Int) where {T} + x, y, z = ξ + facenr == 1 && return Vec(one(T) - x - y, y) + facenr == 2 && return Vec(one(T) - z - x, x) + facenr == 3 && return Vec(x, y) + facenr == 4 && return Vec(one(T) - y - z, z) + throw(ArgumentError("unknown face number")) +end + +function weighted_normal(J::Tensor{2, 3}, ::Type{RefTetrahedron}, face::Int) + @inbounds begin + face == 1 && return J[:, 2] × J[:, 1] + face == 2 && return J[:, 1] × J[:, 3] + face == 3 && return (J[:, 1] - J[:, 3]) × (J[:, 2] - J[:, 3]) + face == 4 && return J[:, 3] × J[:, 2] + end + throw(ArgumentError("unknown facet number")) +end + +################### +# All RefPrism 3D # +################### + +# TODO: Mapping from 1D line to 3D edge of a prism +# function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefPrism}, edgenr::Int) where {T} +# TODO: Mapping from 3D edge of a prism to 1D line +# function cell_to_edge_transformation(ξ::Vec{3, T}, ::Type{RefPrism}, edgenr::Int) where {T} + +# Mapping from 2D quadrilateral/triangle to 3D face of a wedge. +function face_to_cell_transformation(ξ::Vec{2, T}, ::Type{RefPrism}, face::Int) where {T} + # Note that for quadrilaterals the domain is [-1, 1]² but for triangles it is [0, 1]² + x, y = ξ + face == 1 && return Vec{3, T}((one(T) - x - y, y, zero(T))) + face == 2 && return Vec{3, T}(((one(T) + x) / 2, zero(T), (one(T) + y) / 2)) + face == 3 && return Vec{3, T}((zero(T), one(T) - (one(T) + x) / 2, (one(T) + y) / 2)) + face == 4 && return Vec{3, T}((one(T) - (one(T) + x) / 2, (one(T) + x) / 2, (one(T) + y) / 2)) + face == 5 && return Vec{3, T}((y, one(T) - x - y, one(T))) + throw(ArgumentError("unknown facet number")) +end + +# Mapping from 3D face of a wedge to 2D triangle or 2D quadrilateral. +function cell_to_face_transformation(ξ::Vec{3, T}, ::Type{RefPrism}, face::Int) where {T} + x, y, z = ξ + face == 1 && return Vec(one(T) - x - y, y) + face == 2 && return Vec(2 * x - one(T), 2 * z - one(T)) + face == 3 && return Vec(2 * (one(T) - y) - one(T), 2 * z - one(T)) + face == 4 && return Vec(2 * y - one(T), 2 * z - one(T)) + face == 5 && return Vec(one(T) - x - y, x) + throw(ArgumentError("unknown facet number")) +end + +function weighted_normal(J::Tensor{2, 3}, ::Type{RefPrism}, face::Int) + @inbounds begin + face == 1 && return J[:, 2] × J[:, 1] + face == 2 && return J[:, 1] × J[:, 3] + face == 3 && return J[:, 3] × J[:, 2] + face == 4 && return (J[:, 2] - J[:, 1]) × J[:, 3] + face == 5 && return J[:, 1] × J[:, 2] + end + throw(ArgumentError("unknown facet number")) +end + +##################### +# All RefPyramid 3D # +##################### + +# TODO: Mapping from 1D line to 3D edge of a pyramid +# function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefPyramid}, edgenr::Int) where {T} +# TODO: Mapping from 3D edge of a pyramid to 1D line +# function cell_to_edge_transformation(ξ::Vec{3, T}, ::Type{RefPyramid}, edgenr::Int) where {T} + +# Mapping from 2D face to 3D face of a pyramid. +function face_to_cell_transformation(ξ::Vec{2, T}, ::Type{RefPyramid}, face::Int) where {T} + x, y = ξ + face == 1 && return Vec{3, T}(((y + one(T)) / 2, (x + one(T)) / 2, zero(T))) + face == 2 && return Vec{3, T}((y, zero(T), one(T) - x - y)) + face == 3 && return Vec{3, T}((zero(T), one(T) - x - y, y)) + face == 4 && return Vec{3, T}((x + y, y, one(T) - x - y)) + face == 5 && return Vec{3, T}((one(T) - x - y, one(T) - y, y)) + throw(ArgumentError("unknown facet number")) +end + +# Mapping from 3D face of a pyramid to 2D triangle or 2D quadrilateral. +function cell_to_face_transformation(ξ::Vec{3, T}, ::Type{RefPyramid}, face::Int) where {T} + x, y, z = ξ + face == 1 && return Vec(2 * y - one(T), 2 * x - one(T)) + face == 2 && return Vec(one(T) - z - x, x) + face == 3 && return Vec(one(T) - y - z, z) + face == 4 && return Vec(x - y, y) + face == 5 && return Vec(one(T) - x - z, z) + throw(ArgumentError("unknown facet number")) +end + +function weighted_normal(J::Tensor{2, 3}, ::Type{RefPyramid}, face::Int) + @inbounds begin + face == 1 && return J[:, 2] × J[:, 1] + face == 2 && return J[:, 1] × J[:, 3] + face == 3 && return J[:, 3] × J[:, 2] + face == 4 && return J[:, 2] × (J[:, 3] - J[:, 1]) + face == 5 && return (J[:, 3] - J[:, 2]) × J[:, 1] + end + throw(ArgumentError("unknown facet number")) +end diff --git a/src/Ferrite.jl b/src/Ferrite.jl index f30ea21f84..cd2b2131b5 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -128,10 +128,12 @@ include("FEValues/GeometryMapping.jl") include("FEValues/FunctionValues.jl") include("FEValues/CellValues.jl") include("FEValues/FacetValues.jl") +include("FEValues/EdgeValues.jl") include("FEValues/InterfaceValues.jl") include("FEValues/PointValues.jl") include("FEValues/common_values.jl") -include("FEValues/face_integrals.jl") +#include("FEValues/face_integrals.jl") +include("FEValues/boundary_integrals.jl") # Grid include("Grid/grid.jl") diff --git a/src/Quadrature/quadrature.jl b/src/Quadrature/quadrature.jl index dae3cc9795..b4368a15f3 100644 --- a/src/Quadrature/quadrature.jl +++ b/src/Quadrature/quadrature.jl @@ -256,6 +256,44 @@ function _FacetQuadratureRulePyramid(::Type{T}, quad_types::Tuple{Symbol, Symbol ) end +struct EdgeQuadratureRule{RefShape, EdgeRulesType} + edge_rules::EdgeRulesType # E.g. Tuple{QuadratureRule{RefLine,...}, QuadratureRule{RefLine,...}} + function EdgeQuadratureRule{RefShape}(edge_rules::Union{NTuple{<:Any, QRType}, AbstractVector{QRType}}) where {RefShape, QRType <: QuadratureRule{RefShape}} + if length(edge_rules) != nedges(RefShape) + throw(ArgumentError("number of quadrature rules does not not match number of edges (#rules=$(length(edge_rules)) != #edges=$(nedges(RefShape)))")) + end + return new{RefShape, typeof(edge_rules)}(edge_rules) + end +end + +function EdgeQuadratureRule(edge_rules::Union{NTuple{<:Any, QRType}, AbstractVector{QRType}}) where {RefShape, QRType <: QuadratureRule{RefShape}} + return EdgeQuadratureRule{RefShape}(edge_rules) +end + +# Fill in defaults T=Float64 +function EdgeQuadratureRule{RefShape}(order::Int) where {RefShape <: AbstractRefShape} + return EdgeQuadratureRule{RefShape}(Float64, order) +end +function EdgeQuadratureRule{RefShape}(quad_type::Symbol, order::Int) where {RefShape <: AbstractRefShape} + return EdgeQuadratureRule{RefShape}(Float64, quad_type, order) +end +function EdgeQuadratureRule{RefShape}(::Type{T}, order::Int) where {T, RefShape <: AbstractRefShape} + return EdgeQuadratureRule{RefShape}(T, _default_quadrature_rule(RefLine), order) +end +function EdgeQuadratureRule{RefShape}(::Type{T}, quad_type::Symbol, order::Int) where {T, RefShape <: AbstractRefShape} + qr = QuadratureRule{RefLine}(quad_type, order) + #TODO: The following scaling should be fixed in `create_edge_quad_rule`, + # but the way below mirrors `create_facet_quad_rule`. + weights = if RefShape === RefTriangle + getweights(qr) / 2 # (-1, 1) -> (0, 1) + elseif RefShape === RefTetrahedron || RefShape === RefPrism || RefShape === RefPyramid + getweights(qr) / 4 # (-1, 1)² -> (0, 1)² + else + getweights(qr) + end + return create_edge_quad_rule(RefShape, weights, getpoints(qr)) +end + ################## # Common methods # ################## @@ -273,6 +311,7 @@ getnquadpoints(qr::QuadratureRule) = length(getweights(qr)) Return the number of quadrature points in `qr` for local face index `face`. """ getnquadpoints(qr::FacetQuadratureRule, face::Int) = getnquadpoints(qr.face_rules[face]) +getnquadpoints(qr::EdgeQuadratureRule, edge::Int) = getnquadpoints(qr.edge_rules[edge]) """ getweights(qr::QuadratureRule) @@ -293,7 +332,7 @@ julia> getweights(qr) """ getweights(qr::QuadratureRule) = qr.weights getweights(qr::FacetQuadratureRule, face::Int) = getweights(qr.face_rules[face]) - +getweights(qr::EdgeQuadratureRule, edge::Int) = getweights(qr.edge_rules[edge]) """ getpoints(qr::QuadratureRule) @@ -314,8 +353,9 @@ julia> getpoints(qr) """ getpoints(qr::QuadratureRule) = qr.points getpoints(qr::FacetQuadratureRule, face::Int) = getpoints(qr.face_rules[face]) +getpoints(qr::EdgeQuadratureRule, edge::Int) = getpoints(qr.edge_rules[edge]) getrefshape(::QuadratureRule{RefShape}) where {RefShape} = RefShape # TODO: This is used in copy(::(Cell|Face)Values), but it it useful to get an actual copy? -Base.copy(qr::Union{QuadratureRule, FacetQuadratureRule}) = qr +Base.copy(qr::Union{QuadratureRule, FacetQuadratureRule, EdgeQuadratureRule}) = qr diff --git a/src/exports.jl b/src/exports.jl index 786fbf8b29..3a196c768d 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -26,6 +26,7 @@ export # Quadrature QuadratureRule, FacetQuadratureRule, + EdgeQuadratureRule, getnquadpoints, # FEValues @@ -33,6 +34,7 @@ export AbstractFacetValues, CellValues, FacetValues, + EdgeValues, InterfaceValues, reinit!, shape_value, @@ -48,6 +50,7 @@ export function_curl, spatial_coordinate, getnormal, + gettangent, getdetJdV, shape_value_average, shape_value_jump, diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index cd707c007e..65047ad375 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -253,15 +253,68 @@ using Ferrite: reference_shape_value, reference_shape_gradient reference_cell(::Type{RefQuadrilateral}) = Quadrilateral((1, 2, 3, 4)) reference_cell(::Type{RefTetrahedron}) = Tetrahedron((1, 2, 3, 4)) reference_cell(::Type{RefHexahedron}) = Hexahedron((ntuple(identity, 8))) + function facet_parameterization(::Type{RefShape}, ξ, facet_id) where {RefShape <: Ferrite.AbstractRefShape{2}} + # facet = edge + return edge_parameterization(RefShape, ξ, facet_id) + end + + """ + edge_parameterization(::Type{<:AbstractRefShape}, ξ, edge_id) + + An edge is parameterized by the normalized curve coordinate `s [0, 1]`, + increasing in the positive edge direction. + """ + function edge_parameterization(::Type{RefShape}, ξ, edge_id) where {RefShape <: Ferrite.AbstractRefShape} + ipg = Lagrange{RefShape, 1}() # Reference shape always described by 1st order Lagrange ip. + refcoords = Ferrite.reference_coordinates(ipg) + i1, i2 = Ferrite.edgedof_indices(ipg)[edge_id] + ξ1, ξ2 = (refcoords[i1], refcoords[i2]) + Δξ = ξ2 - ξ1 + L = norm(Δξ) + s = (ξ - ξ1) ⋅ normalize(Δξ) / L + @assert norm(ξ - ξ1) ≈ L * s # Ensure ξ is on the line ξ1 - ξ2 + @assert -eps(L) ≤ s ≤ (1 + eps(L)) # Ensure ξ is between ξ1 and ξ2 + return s + end + + function facet_parameterization(::Type{<:Ferrite.AbstractRefShape{3}}, ξ, facet_id) + # Not implemented (not yet defined in Ferrite what this should be), + # but to support testing interpolations with a single facedof interior index, + # we return `nothing` just to allow running the code as long as the output isn't used. + return nothing + end - function line_integral(qr::QuadratureRule{RefLine}, ip, shape_nr, x0, Δx, L, v, f) - val = 0.0 - for (ξ1d, w) in zip(Ferrite.getpoints(qr), Ferrite.getweights(qr)) - ξ = x0 + (ξ1d[1] / 2) * Δx # ::Vec - s = (ξ1d[1] + 1) / 2 # ∈ [0, 1] - Nξ = reference_shape_value(ip, ξ, shape_nr) - dΩ = (w * L / 2) - val += (Nξ ⋅ v) * f(s) * dΩ + function integrate_facet(fv::FacetValues, f::Function, shapenr::Int, cell::Ferrite.AbstractCell{RefShape}) where {RefShape} + facet_id = Ferrite.getcurrentfacet(fv) + function qpoint_contribution(q_point) + ξ = Ferrite.getpoints(fv.fqr, facet_id)[q_point] + # facet parameterization: 1D [0, 1], 2D ([0, 1], [0, 1]) + s = facet_parameterization(RefShape, ξ, facet_id) + n = getnormal(fv, q_point) + facet_sign = Ferrite.get_direction(Ferrite.function_interpolation(fv), shapenr, cell) + N = shape_value(fv, q_point, shapenr) * facet_sign # Ensure no reorientation. + return f(s, N, n) * getdetJdV(fv, q_point) + end + val = qpoint_contribution(1) + for q_point in 2:getnquadpoints(fv) + val += qpoint_contribution(q_point) + end + return val + end + + function integrate_edge(ev::EdgeValues, f::Function, shapenr::Int, cell::Ferrite.AbstractCell{RefShape}) where {RefShape} + edge_id = Ferrite.getcurrentedge(ev) + function qpoint_contribution(q_point) + ξ = Ferrite.getpoints(ev.eqr, edge_id)[q_point] + s = edge_parameterization(RefShape, ξ, edge_id) + t = Ferrite.gettangent(ev, q_point) + edge_sign = Ferrite.get_direction(Ferrite.function_interpolation(ev), shapenr, cell) + N = shape_value(ev, q_point, shapenr) * edge_sign # Ensure no reorientation + return f(s, N, t) * getdetJdV(ev, q_point) + end + val = qpoint_contribution(1) + for q_point in 2:getnquadpoints(ev) + val += qpoint_contribution(q_point) end return val end @@ -360,33 +413,36 @@ using Ferrite: reference_shape_value, reference_shape_gradient # s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 @testset "H(curl) on RefCell" begin - lineqr = QuadratureRule{RefLine}(20) - for ip in Hcurl_interpolations + for ip in Hcurl_interpolations[1:1] cell = reference_cell(getrefshape(ip)) + geo_ip = geometric_interpolation(cell) + ev = EdgeValues(EdgeQuadratureRule{getrefshape(ip)}(20), ip, geo_ip) edges = Ferrite.edges(cell) dofs = Ferrite.edgedof_interior_indices(ip) - x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) - @testset "$(getrefshape(ip)), order=$(Ferrite.getorder(ip))" begin + x = Ferrite.reference_coordinates(geo_ip) + test_points_line = [Vec((ξ,)) for ξ in [-1.0, rand(3)..., 1.0]] + @testset "$ip" begin for (edge_nr, (i1, i2)) in enumerate(edges) - Δx = x[i2] - x[i1] - x0 = (x[i1] + x[i2]) / 2 - L = norm(Δx) - v = Δx / L + reinit!(ev, cell, x, edge_nr) for (idof, shape_nr) in enumerate(dofs[edge_nr]) nedgedofs = length(dofs[edge_nr]) - f(x) = nedgedofs == 1 ? 1.0 : (idof == 1 ? 1 - x : x) - s = line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, f) - @test s ≈ one(s) - if nedgedofs == 2 - g(x) = idof == 1 ? x : 1 - x - @test 1 ≈ 1 + line_integral(lineqr, ip, shape_nr, x0, Δx, L, v, g) + if nedgedofs == 1 + @test 1 ≈ integrate_edge(ev, (_, N, t) -> N ⋅ t, shape_nr, cell) + elseif nedgedofs == 2 + f(s, N, t) = (idof == 1 ? 1 - s : s) * N ⋅ t + @test 1 ≈ integrate_edge(ev, f, shape_nr, cell) + g(s, N, t) = (idof == 1 ? s : 1 - s) * N ⋅ t + @test 1 ≈ 1 + integrate_edge(ev, g, shape_nr, cell) end end - for (j_edge, shape_nrs) in enumerate(dofs) - j_edge == edge_nr && continue - for shape_nr in shape_nrs - for ξ in (x[i1] + r * Δx for r in [0.0, rand(3)..., 1.0]) - @test abs(reference_shape_value(ip, ξ, shape_nr) ⋅ v) < eps() * 100 + # Check that tangential component is zero for dofs not belong to edge + t = gettangent(ev, 1) # Constant tangent since we work on ref cell + for ξ_1d in test_points_line + ξ = Ferrite.edge_to_cell_transformation(ξ_1d, getrefshape(ip), edge_nr) + for (j_edge, shape_nrs) in enumerate(dofs) + j_edge == edge_nr && continue + for shape_nr in shape_nrs + @test reference_shape_value(ip, ξ, shape_nr) ⋅ t + 1 ≈ 1 end end end @@ -395,54 +451,6 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end - function facet_parameterization(::Type{RefShape}, ξ, facet_id) where {RefShape <: Ferrite.AbstractRefShape{2}} - # facet = edge - return edge_parameterization(RefShape, ξ, facet_id) - end - """ - edge_parameterization(::Type{<:AbstractRefShape}, ξ, edge_id) - - An edge is parameterized by the normalized curve coordinate `s [0, 1]`, - increasing in the positive edge direction. - """ - function edge_parameterization(::Type{RefShape}, ξ, edge_id) where {RefShape <: Ferrite.AbstractRefShape} - ipg = Lagrange{RefShape, 1}() # Reference shape always described by 1st order Lagrange ip. - refcoords = Ferrite.reference_coordinates(ipg) - i1, i2 = Ferrite.edgedof_indices(ipg)[edge_id] - ξ1, ξ2 = (refcoords[i1], refcoords[i2]) - Δξ = ξ2 - ξ1 - L = norm(Δξ) - s = (ξ - ξ1) ⋅ normalize(Δξ) / L - @assert norm(ξ - ξ1) ≈ L * s # Ensure ξ is on the line ξ1 - ξ2 - @assert -eps(L) ≤ s ≤ (1 + eps(L)) # Ensure ξ is between ξ1 and ξ2 - return s - end - - function facet_parameterization(::Type{<:Ferrite.AbstractRefShape{3}}, ξ, facet_id) - # Not implemented (not yet defined in Ferrite what this should be), - # but to support testing interpolations with a single facedof interior index, - # we return `nothing` just to allow running the code as long as the output isn't used. - return nothing - end - - function integrate_facet(fv::FacetValues, f::Function, shapenr::Int, cell::Ferrite.AbstractCell{RefShape}) where {RefShape} - facet_id = Ferrite.getcurrentfacet(fv) - function qpoint_contribution(q_point) - ξ = Ferrite.getpoints(fv.fqr, facet_id)[q_point] - # facet parameterization: 1D [0, 1], 2D ([0, 1], [0, 1]) - s = facet_parameterization(RefShape, ξ, facet_id) - n = getnormal(fv, q_point) - facet_sign = Ferrite.get_direction(Ferrite.function_interpolation(fv), shapenr, cell) - N = shape_value(fv, q_point, shapenr) * facet_sign # Ensure no reorientation. - return f(s, N, n) * getdetJdV(fv, q_point) - end - val = qpoint_contribution(1) - for q_point in 2:getnquadpoints(fv) - val += qpoint_contribution(q_point) - end - return val - end - # Required properties of shape value Nⱼ of an edge-elements (Hdiv) on an edge with normal n, length L, and dofs ∈ 𝔇 # 1) Unit property: ∫(Nⱼ ⋅ n f(s) dS) = 1 ∀ j ∈ 𝔇 # Must hold for @@ -465,12 +473,12 @@ using Ferrite: reference_shape_value, reference_shape_gradient dofs = Ferrite.facetdof_interior_indices(ip) x = Ferrite.reference_coordinates(geometric_interpolation(typeof(cell))) normals = reference_normals(geometric_interpolation(typeof(cell))) + test_points_facet = [Vec((ξ,)) for ξ in [-1.0, rand(3)..., 1.0]] #TODO: generalize to work for faces and not only lines @testset "$ip" begin for (facet_nr, (i1, i2)) in enumerate(cell_facets) reinit!(fv, reference_cell(getrefshape(ip)), x, facet_nr) @testset "Facet $facet_nr" begin n = normals[facet_nr] - Δx = x[i2] - x[i1] for (rmf_idx, rm_fun) in enumerate(reference_moment_functions(ip)) f(s, N, nq) = rm_fun(s) * (N ⋅ nq) for (idof, shape_nr) in enumerate(dofs[facet_nr]) @@ -484,8 +492,9 @@ using Ferrite: reference_shape_value, reference_shape_gradient for (j_facet, shape_nrs) in enumerate(dofs) j_facet == facet_nr && continue for shape_nr in shape_nrs - for ξ in (x[i1] + r * Δx for r in [0.0, rand(3)..., 1.0]) - @test abs(reference_shape_value(ip, ξ, shape_nr) ⋅ n) < eps() * 100 + for ξ_onfacet in test_points_facet + ξ = Ferrite.facet_to_cell_transformation(ξ_onfacet, getrefshape(ip), facet_nr) + @test reference_shape_value(ip, ξ, shape_nr) ⋅ n + 1 ≈ 1 end end end @@ -575,45 +584,76 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end - @testset "Hcurl and Hdiv BC" begin - hdiv_check(v, n) = v ⋅ n - function hcurl_check(v, n::Vec{2}) # 3d not supported yet - t = rotate(n, π / 2) - return v ⋅ t + @testset "Hdiv BC" begin + for ip in Hdiv_interpolations + @testset "$ip" begin + RefShape = Ferrite.getrefshape(ip) + CT = typeof(reference_cell(RefShape)) + dim = Ferrite.getrefdim(CT) # dim=sdim=vdim + grid = generate_grid(CT, ntuple(Returns(2), dim), -0.25 * ones(Vec{dim}), 0.2 * ones(Vec{dim})) + qr = FacetQuadratureRule{RefShape}(4) + fv = FacetValues(qr, ip, geometric_interpolation(CT)) + dh = close!(add!(DofHandler(grid), :u, ip)) + for bval in (0.0,) # TODO: Currently only zero-valued BC supported + for (side, facetset) in grid.facetsets + a = zeros(ndofs(dh)) + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:u, facetset, Returns(bval))) + close!(ch) + apply!(a, ch) + test_val = 0.0 + test_area = 0.0 + for (cellidx, facetidx) in facetset + reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) + ae = a[celldofs(dh, cellidx)] + val = 0.0 + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + val += (function_value(fv, q_point, ae) ⋅ getnormal(fv, q_point)) * dΓ + test_area += dΓ + end + test_val += val + end + @test abs(test_val - test_area * bval) < 1.0e-6 + end + end + end end - for (f, interpolations) in ((hdiv_check, Hdiv_interpolations), (hcurl_check, Hcurl_interpolations)) - for ip in interpolations - ip isa Nedelec && Ferrite.getrefdim(ip) == 3 && continue # skip 3d nedelec - @testset "$ip" begin - RefShape = Ferrite.getrefshape(ip) - CT = typeof(reference_cell(RefShape)) - dim = Ferrite.getrefdim(CT) # dim=sdim=vdim - grid = generate_grid(CT, ntuple(Returns(2), dim), -Vec((-0.25, -0.25)), Vec((0.2, 0.2))) - qr = FacetQuadratureRule{RefShape}(4) - fv = FacetValues(qr, ip, geometric_interpolation(CT)) - dh = close!(add!(DofHandler(grid), :u, ip)) - for bval in (0.0,) # TODO: Currently only zero-valued BC supported - for side in ("left", "right", "top", "bottom") - a = zeros(ndofs(dh)) - ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, getfacetset(grid, side), Returns(bval))) - close!(ch) - apply!(a, ch) - test_val = 0.0 - test_area = 0.0 - for (cellidx, facetidx) in getfacetset(grid, side) - reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) - ae = a[celldofs(dh, cellidx)] - val = 0.0 - for q_point in 1:getnquadpoints(fv) - dΓ = getdetJdV(fv, q_point) - val += f(function_value(fv, q_point, ae), getnormal(fv, q_point)) * dΓ - test_area += dΓ - end - test_val += f === hdiv_check ? val : abs(val) + end + + @testset "Hcurl BC" begin + for ip in Hcurl_interpolations + @testset "$ip" begin + RefShape = Ferrite.getrefshape(ip) + CT = typeof(reference_cell(RefShape)) + dim = Ferrite.getrefdim(CT) # dim=sdim=vdim + grid = generate_grid(CT, ntuple(Returns(2), dim), -0.25 * ones(Vec{dim}), 0.2 * ones(Vec{dim})) + qr = EdgeQuadratureRule{RefShape}(4) + ev = EdgeValues(qr, ip, geometric_interpolation(CT)) + dh = close!(add!(DofHandler(grid), :u, ip)) + @assert dim == 2 # Only 2d supported right now... + for bval in (0.0,) # TODO: Currently only zero-valued BC supported + for (side, facetset) in grid.facetsets + set = OrderedSet(EdgeIndex(a, b) for (a, b) in facetset) + a = zeros(ndofs(dh)) + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:u, getfacetset(grid, side), Returns(bval))) + close!(ch) + apply!(a, ch) + test_val = 0.0 + test_area = 0.0 + for (cellidx, facetidx) in getfacetset(grid, side) + reinit!(ev, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) + ae = a[celldofs(dh, cellidx)] + val = 0.0 + for q_point in 1:getnquadpoints(ev) + dΓ = getdetJdV(ev, q_point) + val += (function_value(ev, q_point, ae) ⋅ Ferrite.gettangent(ev, q_point)) * dΓ + test_area += dΓ end - @test abs(test_val - test_area * bval) < 1.0e-6 + test_val += val end + @test abs(test_val - test_area * bval) < 1.0e-6 end end end From 368e277809d3c85e8ff5a7acf230d67e06d46dff Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 7 Jan 2025 23:58:44 +0100 Subject: [PATCH 157/172] Work on maxwell example, theoretical progress --- docs/make.jl | 1 + docs/src/assets/references.bib | 11 ++++ docs/src/devdocs/elements.md | 6 ++- .../maxwell_good_bad_ugly.jl | 54 +++++++++++++++++-- 4 files changed, 65 insertions(+), 7 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index ed61182d43..b9ec9c3f8b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -69,6 +69,7 @@ bibtex_plugin = CitationBibliography( "tutorials/linear_shell.md", "tutorials/dg_heat_equation.md", "tutorials/maxwell.md", + "tutorials/maxwell_good_bad_ugly.md", ], "Topic guides" => [ "Topic guide overview" => "topics/index.md", diff --git a/docs/src/assets/references.bib b/docs/src/assets/references.bib index 0678618983..a8c43495ec 100644 --- a/docs/src/assets/references.bib +++ b/docs/src/assets/references.bib @@ -212,3 +212,14 @@ @book{Boffi2013 author = {Boffi, Daniele and Brezzi, Franco and Fortin, Michel}, year = {2013} } +@book{Monk2003, + author = {Monk, Peter}, + title = {Finite Element Methods for Maxwell's Equations}, + publisher = {Oxford University Press}, + year = {2003}, + month = {04}, + abstract = {Since the middle of the last century, computing power has increased sufficiently that the direct numerical approximation of Maxwell’s equations is now an increasingly important tool in science and engineering. Parallel to the increasing use of numerical methods in computational electromagnetism, there has also been considerable progress in the mathematical understanding of the properties of Maxwell’s equations relevant to numerical analysis. The aim of this book is to provide an up-to-date and sound theoretical foundation for finite element methods in computational electromagnetism. The emphasis is on finite element methods for scattering problems that involve the solution of Maxwell’s equations on infinite domains. Suitable variational formulations are developed and justified mathematically. An error analysis of edge finite element methods that are particularly well suited to Maxwell’s equations is the main focus of the book. The analysis involves a complete justification of the discrete de Rham diagram and discrete compactness of edge elements. The numerical methods are justified for Lipschitz polyhedral domains that can cause strong singularities in the solution. The book ends with a short introduction to inverse problems in electromagnetism.}, + isbn = {9780198508885}, + doi = {10.1093/acprof:oso/9780198508885.001.0001}, + url = {https://doi.org/10.1093/acprof:oso/9780198508885.001.0001}, +} diff --git a/docs/src/devdocs/elements.md b/docs/src/devdocs/elements.md index 88c799665a..0fb891a181 100644 --- a/docs/src/devdocs/elements.md +++ b/docs/src/devdocs/elements.md @@ -37,8 +37,10 @@ Ferrite.sortface Ferrite.sortface_fast Ferrite.sortedge Ferrite.sortedge_fast -Ferrite.element_to_facet_transformation -Ferrite.facet_to_element_transformation +Ferrite.cell_to_edge_transformation +Ferrite.edge_to_cell_transformation +Ferrite.cell_to_face_transformation +Ferrite.face_to_cell_transformation Ferrite.InterfaceOrientationInfo Ferrite.transform_interface_points! Ferrite.get_transformation_matrix diff --git a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl index 1d8040bea1..10042af0b6 100644 --- a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl +++ b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl @@ -7,11 +7,11 @@ from the graduate course MTH 653: Advanced Numerical Analysis (Spring 2019) The purpose of the tutorial is to demonstrate how `Nedelec` vector interpolations will converge to the correct solution for a Maxwell problem, when vectorized `Lagrange` interpolations converge to an incorrect solution. -# ## Loading packages +## Loading packages We start by adding the required packages for this tutorial =# import Pkg -# Pkg.add(;url = ...) +# `Pkg.add(;url = ...)` using Ferrite, Tensors, ForwardDiff using Gmsh, FerriteGmsh using FerriteTriangulation: Triangulation, SubTriangulation @@ -42,6 +42,31 @@ points = [(0, 0), (1, 0), (1, 1), (-1, 1), (-1, -1), (0, -1), (0, 0)] Plt.lines!(ax, first.(points), last.(points)) fig #hide +#= +## Theoretical background +We have the following partial integration rules, where ``\boldsymbol{n}`` is the outward pointing normal vector, + following Monk (2003) [Monk2003; Eqs. (3.47) and (3.51)](@cite), +```math +\begin{align*} +\int_\Gamma \left[\boldsymbol{n}\times\boldsymbol{u}\right]\cdot\boldsymbol{\phi}\ \mathrm{d}\Gamma &= +\int_\Omega \left[\nabla \times \boldsymbol{u}\right]\cdot \boldsymbol{\phi}\ \mathrm{d}\Omega - +\int_\Omega \boldsymbol{u}\cdot\left[ \nabla \times \boldsymbol{\phi} \right]\ \mathrm{d}\Omega, \quad \boldsymbol{u} \in H(\mathrm{curl}),\ \boldsymbol{\phi} \in (\mathcal{C}^1)^3\\ +\int_\Gamma \left[\boldsymbol{n}\times\boldsymbol{u}\right]\cdot\left[\left[\boldsymbol{n}\times\boldsymbol{\phi}\right]\times\boldsymbol{n}\right] \mathrm{d}\Gamma &= +\int_\Omega \left[\nabla \times \boldsymbol{u}\right]\cdot \boldsymbol{\phi}\ \mathrm{d}\Omega - +\int_\Omega \boldsymbol{u}\cdot\left[ \nabla \times \boldsymbol{\phi} \right]\ \mathrm{d}\Omega, \quad \boldsymbol{u},\ \boldsymbol{\phi}\in H(\mathrm{curl}), +\end{align*} +``` +respectively. Note that [Monk2003; Eq. (3.27)](@cite), requiring that ``\boldsymbol{u} \in (\mathcal{C}^1)^3``, +is a special case of [Monk2003; Eq. (3.47)](@cite) as ``(\mathcal{C}^1)^3 \subset H(\mathrm{curl})``. + +We remark that in 2D for ``\boldsymbol{u}`` pointing out of the plane, ``\boldsymbol{u} = u_3 \boldsymbol{e}_3``, +and ``\boldsymbol{\phi}`` in the plane, ``\boldsymbol{\phi} \cdot \boldsymbol{e}_3 = 0``, we have +```math +\left[\boldsymbol{n}\times\boldsymbol{u}\right]\cdot\left[\left[\boldsymbol{n}\times\boldsymbol{\phi}\right]\times\boldsymbol{n}\right] = -u_3 \boldsymbol{\phi} \cdot \boldsymbol{t} +``` +where ``\boldsymbol{t} = [-n_2,\ n_1]`` is the counter-clockwise tangent vector. + +=# #= ## Exact solution In this example, we choose an exact solution, ``\boldsymbol{E}_\mathrm{exact}(\boldsymbol{x})``, @@ -59,7 +84,8 @@ along the lines, ``\theta = 0`` and ``\theta = 3\pi/2``, ``\sin(2\theta/3) = 0`` ``\boldsymbol{E}_\mathrm{exact} \cdot \boldsymbol{t} = 0``. Consequently, even if we have a singularity at ``\boldsymbol{x} = \boldsymbol{0}``, this doesn't enter the boundary conditions. Finally, due to the singularity, the components of ``\boldsymbol{E}_\mathrm{exact}`` are not in -``H^1(\Omega)``. * **TODO:** Explain why `div(E)`` is fullfilled, use divergence theorem?* +``H^1(\Omega)``. +**TODO:** *Explain why ``\mathrm{div}(\boldsymbol{E})=0`` is fullfilled, use divergence theorem?* ## Lagrange interpolation Following the notes in the linked example, the lagrange problem becomes to solve @@ -72,7 +98,20 @@ Following the notes in the linked example, the lagrange problem becomes to solve ``` ## Nedelec interpolation -To be completed... +Using a Lagrange multiplier, ``\phi``, to weakly enforce the divergence equation, +we obtain the following problem: +Find ``\boldsymbol{E}\in H(\mathrm{div})`` and ``\phi\in H_0^1``, such that +```math +\begin{align*} +\int_\Gamma \left[\left[\boldsymbol{n}\times\delta\boldsymbol{E}\right]\times\boldsymbol{n}\right]\cdot +\left[\boldsymbol{n}\cdot\mathrm{curl}(\boldsymbol{E})\right]\ \mathrm{d}\Gamma + +\int_\Omega \mathrm{curl}(\delta\boldsymbol{E})\cdot\mathrm{curl}(\boldsymbol{E})\ \mathrm{d}\Omega + +\int_\Omega \delta\boldsymbol{E}\cdot\mathrm{grad}(\phi)\ \mathrm{d}\Omega &= 0\\ +\int_\Omega \mathrm{grad}(\delta\phi) \cdot \boldsymbol{E}\ \mathrm{d}\Omega &= 0 \\ +\boldsymbol{E}\cdot \boldsymbol{t} &= g\; \text{on }\Gamma +\end{align*} +``` +for all ``\delta\boldsymbol{E}\in H_0(\mathrm{curl})`` and ``\delta\phi\in H_0^1``. =# # We then use `FerriteGmsh.jl` to create the grid @@ -189,9 +228,12 @@ m = Plt.mesh!( ) Plt.Colorbar(fig[1, 2], m) #= +```julia for i in 2:length(tr.tri_edges) Plt.lines!(view(nodes, view(tr.edges, tr.tri_edges[i-1]:(tr.tri_edges[i]-1))); color=:black) -end # =# +end +``` +=# # Error calculator mutable struct L2Error{F} @@ -261,6 +303,7 @@ function lagrange_error(h::Float64) end #= +```julia mesh_sizes = (1/2) .^(3:8) lagrange_errors = Float64[] for h in mesh_sizes @@ -268,6 +311,7 @@ for h in mesh_sizes e = @time lagrange_error(h) push!(lagrange_errors, e) end +``` =# tr = Triangulation(dh, 2) From 6cf5a926426c5c3fb4baa7109f6b49ca6807b53d Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 8 Jan 2025 00:20:18 +0100 Subject: [PATCH 158/172] Fix some typos in variable names --- src/FEValues/boundary_integrals.jl | 48 +++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/FEValues/boundary_integrals.jl b/src/FEValues/boundary_integrals.jl index 918d09e33b..b4e520ab85 100644 --- a/src/FEValues/boundary_integrals.jl +++ b/src/FEValues/boundary_integrals.jl @@ -132,23 +132,23 @@ end ################## # Special cases since we don't have cell_to_vertex and vertex_to_cell # Mapping from to 0D node to 1D line vertex. -function facet_to_element_transformation(::Union{Vec{0, T}, Vec{1, T}}, ::Type{RefLine}, face::Int) where {T} - face == 1 && return Vec{1, T}((-one(T),)) - face == 2 && return Vec{1, T}((one(T),)) +function facet_to_element_transformation(::Union{Vec{0, T}, Vec{1, T}}, ::Type{RefLine}, facetnr::Int) where {T} + facetnr == 1 && return Vec{1, T}((-one(T),)) + facetnr == 2 && return Vec{1, T}((one(T),)) throw(ArgumentError("unknown facet number")) end # Mapping from 1D line to point. -function element_to_facet_transformation(ξ::Vec{1, T}, ::Type{RefLine}, face::Int) where {T} +function element_to_facet_transformation(ξ::Vec{1, T}, ::Type{RefLine}, facetnr::Int) where {T} x = ξ[1] - face == 1 && return Vec(-x) - face == 2 && return Vec(x) + facetnr == 1 && return Vec(-x) + facetnr == 2 && return Vec(x) throw(ArgumentError("unknown facet number")) end -function weighted_normal(::Tensor{2, 1, T}, ::Type{RefLine}, face::Int) where {T} - face == 1 && return Vec{1, T}((-one(T),)) - face == 2 && return Vec{1, T}((one(T),)) +function weighted_normal(::Tensor{2, 1, T}, ::Type{RefLine}, facetnr::Int) where {T} + facetnr == 1 && return Vec{1, T}((-one(T),)) + facetnr == 2 && return Vec{1, T}((one(T),)) throw(ArgumentError("unknown facet number")) end @@ -164,31 +164,31 @@ end ########################### # Mapping from 1D line to 2D face of a quadrilateral. -function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefQuadrilateral}, edge::Int) where {T} +function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefQuadrilateral}, edgenr::Int) where {T} x = ξ[1] - face == 1 && return Vec{2, T}((x, -one(T))) - face == 2 && return Vec{2, T}((one(T), x)) - face == 3 && return Vec{2, T}((-x, one(T))) - face == 4 && return Vec{2, T}((-one(T), -x)) + edgenr == 1 && return Vec{2, T}((x, -one(T))) + edgenr == 2 && return Vec{2, T}((one(T), x)) + edgenr == 3 && return Vec{2, T}((-x, one(T))) + edgenr == 4 && return Vec{2, T}((-one(T), -x)) throw(ArgumentError("unknown edge number")) end # Mapping from 2D face of a quadrilateral to 1D line. -function cell_to_edge_transformation(ξ::Vec{2, T}, ::Type{RefQuadrilateral}, edge::Int) where {T} +function cell_to_edge_transformation(ξ::Vec{2, T}, ::Type{RefQuadrilateral}, edgenr::Int) where {T} x, y = ξ - face == 1 && return Vec(x) - face == 2 && return Vec(y) - face == 3 && return Vec(-x) - face == 4 && return Vec(-y) + edgenr == 1 && return Vec(x) + edgenr == 2 && return Vec(y) + edgenr == 3 && return Vec(-x) + edgenr == 4 && return Vec(-y) throw(ArgumentError("unknown edge number")) end -function weighted_normal(J::Tensor{2, 2}, ::Type{RefQuadrilateral}, face::Int) +function weighted_normal(J::Tensor{2, 2}, ::Type{RefQuadrilateral}, edgenr::Int) @inbounds begin - face == 1 && return Vec{2}((J[2, 1], -J[1, 1])) - face == 2 && return Vec{2}((J[2, 2], -J[1, 2])) - face == 3 && return Vec{2}((-J[2, 1], J[1, 1])) - face == 4 && return Vec{2}((-J[2, 2], J[1, 2])) + edgenr == 1 && return Vec{2}((J[2, 1], -J[1, 1])) + edgenr == 2 && return Vec{2}((J[2, 2], -J[1, 2])) + edgenr == 3 && return Vec{2}((-J[2, 1], J[1, 1])) + edgenr == 4 && return Vec{2}((-J[2, 2], J[1, 2])) end throw(ArgumentError("unknown facet number")) end From 24c7c092b56821ea2fcbb9ffae4d6df60e964582 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 8 Jan 2025 21:09:14 +0100 Subject: [PATCH 159/172] Start IntegrateableDirichlet and try fixing doc build temporarily --- docs/Manifest.toml | 139 +++++++++--------- docs/Project.toml | 5 + .../maxwell_good_bad_ugly.jl | 19 ++- docs/src/reference/boundary_conditions.md | 1 + src/Dofs/ConstraintHandler.jl | 43 ++++++ src/exports.jl | 1 + 6 files changed, 131 insertions(+), 77 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 77a05dfd23..2a597842a1 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -253,9 +253,9 @@ weakdeps = ["ForwardDiff"] [[deps.Bzip2_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "35abeca13bc0425cff9e59e229d971f5231323bf" +git-tree-sha1 = "8873e196c2eb87962a2048b3b8e08946535864a1" uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+3" +version = "1.0.8+4" [[deps.CEnum]] git-tree-sha1 = "389ad5c84de1ae7cf0e28e381131c98ea87d54fc" @@ -298,9 +298,9 @@ version = "1.18.2+1" [[deps.ChainRulesCore]] deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "3e4b134270b372f2ed4d4d0e936aabaefc1802bc" +git-tree-sha1 = "1713c74e00545bfe14605d2a2be1712de8fbcb58" uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.25.0" +version = "1.25.1" weakdeps = ["SparseArrays"] [deps.ChainRulesCore.extensions] @@ -582,9 +582,9 @@ version = "1.11.0" [[deps.Distributions]] deps = ["AliasTables", "FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "4b138e4643b577ccf355377c2bc70fa975af25de" +git-tree-sha1 = "7901a6117656e29fa2c74a58adb682f380922c47" uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.115" +version = "0.25.116" [deps.Distributions.extensions] DistributionsChainRulesCoreExt = "ChainRulesCore" @@ -659,9 +659,9 @@ version = "0.1.11" [[deps.Expat_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f42a5b1e20e009a43c3646635ed81a9fcaccb287" +git-tree-sha1 = "e51db81749b0777b2147fbe7b783ee79045b8e99" uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.6.4+2" +version = "2.6.4+3" [[deps.ExponentialUtilities]] deps = ["Adapt", "ArrayInterface", "GPUArraysCore", "GenericSchur", "LinearAlgebra", "PrecompileTools", "Printf", "SparseArrays", "libblastrampoline_jll"] @@ -709,9 +709,9 @@ version = "1.8.0" [[deps.FFTW_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "5cf2433259aa3985046792e2afc01fcec076b549" +git-tree-sha1 = "4d81ed14783ec49ce9f2e168208a12ce1815aa25" uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" -version = "3.3.10+2" +version = "3.3.10+3" [[deps.FLTK_jll]] deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] @@ -779,7 +779,9 @@ version = "1.0.0" [[deps.FerriteAssembly]] deps = ["Ferrite", "ForwardDiff", "MaterialModelsBase"] -path = "../../FerriteAssembly" +git-tree-sha1 = "0a1071e3b1cb7dc08e10cb2ed4e3171fd500894a" +repo-rev = "main" +repo-url = "https://github.com/KnutAM/FerriteAssembly.jl" uuid = "fd21fc07-c509-4fe1-9468-19963fd5935d" version = "0.3.4" @@ -797,7 +799,9 @@ version = "0.2.0" [[deps.FerriteTriangulation]] deps = ["Ferrite", "Tensors"] -path = "../../FerriteTriangulation" +git-tree-sha1 = "3b7f694d319bdbe4a98d7028a3758444a24eb868" +repo-rev = "main" +repo-url = "https://github.com/KnutAM/FerriteTriangulation.jl" uuid = "b200c708-61b9-46d9-917a-515144ac7aac" version = "0.1.0" @@ -995,9 +999,9 @@ version = "0.21.0+0" [[deps.Giflib_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "7141135f9073f135e68c5ee8df44fb0fb80689b8" +git-tree-sha1 = "6570366d757b50fabae9f4315ad74d2e40c0560a" uuid = "59f7168a-df46-5410-90c8-f2779963d0ec" -version = "5.2.2+1" +version = "5.2.3+0" [[deps.Git]] deps = ["Git_jll"] @@ -1078,9 +1082,9 @@ version = "0.1.17" [[deps.Hwloc_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "290232556f4ffb60ac3e476acf28e1a46e764742" +git-tree-sha1 = "50aedf345a709ab75872f80a2779568dc0bb461b" uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.11.2+2" +version = "2.11.2+3" [[deps.HypergeometricFunctions]] deps = ["LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] @@ -1276,9 +1280,9 @@ version = "0.1.5" [[deps.JpegTurbo_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3447a92280ecaad1bd93d3fce3d408b6cfff8913" +git-tree-sha1 = "eac1206917768cb54957c65a615460d87b455fc1" uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.1.0+1" +version = "3.1.1+0" [[deps.KLU]] deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] @@ -1316,9 +1320,9 @@ version = "3.100.2+0" [[deps.LERC_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "78e0f4b5270c4ae09c7c5f78e77b904199038945" +git-tree-sha1 = "aaafe88dccbd957a8d82f7d05be9b69172e0cee3" uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "4.0.0+2" +version = "4.0.1+0" [[deps.LLVMOpenMP_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1330,7 +1334,7 @@ version = "18.1.7+0" deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "854a9c268c43b77b0a27f22d7fab8d33cdb3a731" uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.2+1" +version = "2.10.2+3" [[deps.LaTeXStrings]] git-tree-sha1 = "dda21b8cbd6a6c40d9d02a73230f9d70fed6918c" @@ -1441,9 +1445,9 @@ version = "1.7.0+0" [[deps.Libgpg_error_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "a7f43994b47130e4f491c3b2dbe78fe9e2aed2b3" +git-tree-sha1 = "df37206100d39f79b3376afb6b9cee4970041c61" uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.51.0+0" +version = "1.51.1+0" [[deps.Libiconv_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -1455,19 +1459,19 @@ version = "1.17.0+1" deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "84eef7acd508ee5b3e956a2ae51b05024181dee0" uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.40.2+0" +version = "2.40.2+2" [[deps.Libtiff_jll]] deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "b404131d06f7886402758c9ce2214b636eb4d54a" +git-tree-sha1 = "4ab7581296671007fc33f07a721631b8855f4b1d" uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.7.0+0" +version = "4.7.1+0" [[deps.Libuuid_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "edbf5309f9ddf1cab25afc344b1e8150b7c832f9" uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.40.2+0" +version = "2.40.2+2" [[deps.LightXML]] deps = ["Libdl", "XML2_jll"] @@ -1631,13 +1635,12 @@ version = "0.1.11" deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] git-tree-sha1 = "70e830dab5d0775183c99fc75e4c24c614ed7142" uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.5.1+0" +version = "5.5.1+2" [[deps.MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" +git-tree-sha1 = "72aebe0b5051e5143a079a4685a46da330a40472" uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.13" +version = "0.5.15" [[deps.Makie]] deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "InverseFunctions", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "MakieCore", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "Packing", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"] @@ -1674,7 +1677,9 @@ version = "0.1.2" [[deps.MaterialModelsBase]] deps = ["StaticArrays", "Tensors"] -path = "../../MaterialModelsBase" +git-tree-sha1 = "1a3591c7472ec7690b5a6d3ced4504f4a7a314d3" +repo-rev = "main" +repo-url = "https://github.com/KnutAM/MaterialModelsBase.jl" uuid = "af893363-701d-44dc-8b1e-d9a2c129bfc9" version = "0.2.2" @@ -1919,15 +1924,15 @@ version = "1.4.3" [[deps.OpenSSL_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f58782a883ecbf9fb48dcd363f9ccd65f36c23a8" +git-tree-sha1 = "7493f61f55a6cce7325f197443aa80d32554ba10" uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.15+2" +version = "3.0.15+3" [[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1346c9208249809840c91b26703912dff463d335" uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" +version = "0.5.6+0" [[deps.Optim]] deps = ["Compat", "FillArrays", "ForwardDiff", "LineSearches", "LinearAlgebra", "NLSolversBase", "NaNMath", "Parameters", "PositiveFactorizations", "Printf", "SparseArrays", "StatsBase"] @@ -1981,10 +1986,10 @@ weakdeps = ["EnzymeCore"] OrdinaryDiffEqCoreEnzymeCoreExt = "EnzymeCore" [[deps.OrdinaryDiffEqDefault]] -deps = ["DiffEqBase", "EnumX", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "PrecompileTools", "Preferences", "Reexport"] -git-tree-sha1 = "c8223e487d58bef28a3535b33ddf8ffdb44f46fb" +deps = ["ADTypes", "DiffEqBase", "EnumX", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "PrecompileTools", "Preferences", "Reexport"] +git-tree-sha1 = "2ee6ef0bbed24976e4acfccf609801f8a5bf8223" uuid = "50262376-6c5a-4cf5-baba-aaf4f84d72d7" -version = "1.1.0" +version = "1.2.0" [[deps.OrdinaryDiffEqDifferentiation]] deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SparseArrays", "SparseDiffTools", "StaticArrayInterface", "StaticArrays"] @@ -2726,9 +2731,9 @@ weakdeps = ["OffsetArrays", "StaticArrays"] [[deps.StaticArrays]] deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "7c01731da8ab6d3094c4d44c9057b00932459255" +git-tree-sha1 = "47091a0340a675c738b1304b58161f3b0839d454" uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.9" +version = "1.9.10" weakdeps = ["ChainRulesCore", "Statistics"] [deps.StaticArrays.extensions] @@ -2827,9 +2832,9 @@ version = "7.7.0+0" [[deps.SymbolicIndexingInterface]] deps = ["Accessors", "ArrayInterface", "RuntimeGeneratedFunctions", "StaticArraysCore"] -git-tree-sha1 = "8db233b54917e474165d582bef2244fa040e0a56" +git-tree-sha1 = "fd2d4f0499f6bb4a0d9f5030f5c7d61eed385e03" uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" -version = "0.3.36" +version = "0.3.37" [[deps.TOML]] deps = ["Dates"] @@ -2947,9 +2952,9 @@ version = "0.4.1" [[deps.Unitful]] deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "01915bfcd62be15329c9a07235447a89d588327c" +git-tree-sha1 = "c0667a8e676c53d390a09dc6870b3d8d6650e2bf" uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.21.1" +version = "1.22.0" weakdeps = ["ConstructionBase", "InverseFunctions"] [deps.Unitful.extensions] @@ -3040,9 +3045,9 @@ version = "1.1.42+0" [[deps.XZ_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "15e637a697345f6743674f1322beefbc5dcd5cfc" +git-tree-sha1 = "beef98d5aad604d9e7d60b2ece5181f7888e2fd6" uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.6.3+0" +version = "5.6.4+0" [[deps.Xorg_libICE_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] @@ -3060,13 +3065,13 @@ version = "1.2.4+0" deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] git-tree-sha1 = "9dafcee1d24c4f024e7edc92603cedba72118283" uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+1" +version = "1.8.6+3" [[deps.Xorg_libXau_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "2b0e27d52ec9d8d483e2ca0b72b3cb1a8df5c27a" uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.11+1" +version = "1.0.11+3" [[deps.Xorg_libXcursor_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] @@ -3078,13 +3083,13 @@ version = "1.2.3+0" deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "02054ee01980c90297412e4c809c8694d7323af3" uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.4+1" +version = "1.1.4+3" [[deps.Xorg_libXext_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] git-tree-sha1 = "d7155fea91a4123ef59f42c4afb5ab3b4ca95058" uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.6+1" +version = "1.3.6+3" [[deps.Xorg_libXfixes_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] @@ -3126,13 +3131,13 @@ version = "0.9.11+1" deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "fee57a273563e273f0f53275101cd41a8153517a" uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.1+1" +version = "0.1.1+3" [[deps.Xorg_libxcb_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] git-tree-sha1 = "1a74296303b6524a0472a8cb12d3d87a78eb3612" uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.17.0+1" +version = "1.17.0+3" [[deps.Xorg_libxkbfile_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] @@ -3192,7 +3197,7 @@ version = "2.39.0+0" deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "b9ead2d2bdb27330545eb14234a2e300da61232e" uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.0+1" +version = "1.5.0+3" [[deps.YAML]] deps = ["Base64", "Dates", "Printf", "StringEncodings"] @@ -3207,9 +3212,9 @@ version = "1.2.13+1" [[deps.Zstd_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "555d1076590a6cc2fdee2ef1469451f872d8b41b" +git-tree-sha1 = "622cf78670d067c738667aaa96c553430b65e269" uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.6+1" +version = "1.5.7+0" [[deps.eudev_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gperf_jll"] @@ -3245,13 +3250,13 @@ version = "0.2.3+0" deps = ["Artifacts", "JLLWrappers", "Libdl"] git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" -version = "1.1.2+0" +version = "1.1.2+2" [[deps.libaom_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "1827acba325fdcdf1d2647fc8d5301dd9ba43a9d" +git-tree-sha1 = "522c1df09d05a71785765d19c9524661234738e9" uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.9.0+0" +version = "3.11.0+0" [[deps.libass_jll]] deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] @@ -3290,15 +3295,15 @@ version = "1.18.0+0" [[deps.libpng_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "9c42636e3205e555e5785e902387be0061e7efc1" +git-tree-sha1 = "b7bfd3ab9d2c58c3829684142f5804e4c6499abc" uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.44+1" +version = "1.6.45+0" [[deps.libsixel_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "libpng_jll"] -git-tree-sha1 = "80c5ae2c7b5163441018f4666b179f1ffca194c1" +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "libpng_jll"] +git-tree-sha1 = "1e53ffe8941ee486739f3c0cf11208c26637becd" uuid = "075b6546-f08a-558a-be8f-8157d0f608a5" -version = "1.10.3+2" +version = "1.10.4+0" [[deps.libvorbis_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] @@ -3308,9 +3313,9 @@ version = "1.3.7+2" [[deps.libwebp_jll]] deps = ["Artifacts", "Giflib_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Libtiff_jll", "libpng_jll"] -git-tree-sha1 = "ccbb625a89ec6195856a50aa2b668a5c08712c94" +git-tree-sha1 = "d2408cac540942921e7bd77272c32e58c33d8a77" uuid = "c5f90fcd-3b7e-5836-afba-fc50a0988cb2" -version = "1.4.0+0" +version = "1.5.0+0" [[deps.mtdev_jll]] deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] diff --git a/docs/Project.toml b/docs/Project.toml index abf2abb9ca..74b05da123 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -31,3 +31,8 @@ Tensors = "48a634ad-e948-5137-8d70-aa71f2a747f4" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" + +[sources] +MaterialModelsBase = {url = "https://github.com/KnutAM/MaterialModelsBase.jl"} +FerriteAssembly = {url = "https://github.com/KnutAM/FerriteAssembly.jl"} +FerriteTriangulation = {url = "https://github.com/KnutAM/FerriteTriangulation.jl"} diff --git a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl index 10042af0b6..f3437c3016 100644 --- a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl +++ b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl @@ -116,11 +116,11 @@ for all ``\delta\boldsymbol{E}\in H_0(\mathrm{curl})`` and ``\delta\phi\in H_0^1 # We then use `FerriteGmsh.jl` to create the grid function setup_grid(h = 0.2; origin_refinement = 1) - # Initialize gmsh + ## Initialize gmsh Gmsh.initialize() gmsh.option.set_number("General.Verbosity", 2) - # Add the points, finer grid at the discontinuity + ## Add the points, finer grid at the discontinuity o = gmsh.model.geo.add_point(0.0, 0.0, 0.0, h / origin_refinement) p1 = gmsh.model.geo.add_point(1.0, 0.0, 0.0, h) p2 = gmsh.model.geo.add_point(1.0, 1.0, 0.0, h) @@ -129,30 +129,30 @@ function setup_grid(h = 0.2; origin_refinement = 1) p5 = gmsh.model.geo.add_point(0.0, -1.0, 0.0, h) pts = [o, p1, p2, p3, p4, p5, o] - # Add the lines + ## Add the lines lines = [gmsh.model.geo.add_line(pts[i - 1], pts[i]) for i in 2:length(pts)] - # Create the closed curve loop and the surface + ## Create the closed curve loop and the surface loop = gmsh.model.geo.add_curve_loop(lines) gmsh.model.geo.add_plane_surface([loop]) - # Synchronize the model + ## Synchronize the model gmsh.model.geo.synchronize() - # Generate a 2D mesh + ## Generate a 2D mesh gmsh.model.mesh.generate(2) - # Save the mesh, and read back in as a Ferrite Grid + ## Save the mesh, and read back in as a Ferrite Grid grid = mktempdir() do dir path = joinpath(dir, "mesh.msh") gmsh.write(path) togrid(path) end - # Finalize the Gmsh library + ## Finalize the Gmsh library Gmsh.finalize() - # Add boundary parts + ## Add boundary parts addfacetset!(grid, "vertical_facets", x -> abs((x[1] - 1) * x[1] * (x[1] + 1)) ≤ 1.0e-6) addfacetset!(grid, "horizontal_facets", x -> abs((x[2] - 1) * x[2] * (x[2] + 1)) ≤ 1.0e-6) @@ -197,7 +197,6 @@ end grid = setup_grid(0.00390625; origin_refinement = 1) -# ip = DiscontinuousLagrange{RefTriangle, 1}()^2 dh = close!(add!(DofHandler(grid), :u, ip)) diff --git a/docs/src/reference/boundary_conditions.md b/docs/src/reference/boundary_conditions.md index 2908ba4775..969682c892 100644 --- a/docs/src/reference/boundary_conditions.md +++ b/docs/src/reference/boundary_conditions.md @@ -11,6 +11,7 @@ Pages = ["boundary_conditions.md"] ```@docs ConstraintHandler Dirichlet +IntegrateableDirichlet PeriodicDirichlet collect_periodic_facets collect_periodic_facets! diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index 6af348ecef..be621f37e3 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -61,6 +61,49 @@ function __to_components(c) return components end +@doc raw""" + IntegrateableDirichlet(field_name::Symbol, facets, f::Function; qr_order = -1) + +An `IntegrateableDirichlet` conditions enforces conditions for `field_name` on the boundary for +non-nodal interpolations (e.g. ``H(\mathrm{div})`` or ``H(\mathrm{curl})``) in an integral sense. + +For ``H(\mathrm{div})`` interpolations, we have conditions on the form +```math +\boldsymbol{N}^f_i a^f_i \cdot \boldsymbol{n}^f = q_n = f(\boldsymbol{x}, t) +``` +These are enforced as +```math +\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \cdot \boldsymbol{n}^f\ \mathrm{d}\Gamma += \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) q_n\ \mathrm{d}\Gamma +``` +(no sum on ``\alpha``) where the weighting functions, ``g_\alpha(\boldsymbol{x})``, are defined such that +``\sum_{\alpha} g_\alpha(\boldsymbol{x}) = 1`` at all points on the facet. +These weighting functions are defined by each interpolation. + +For ``H(\mathrm{curl})`` interpolations, the conditions are on the form +```math +\boldsymbol{N}^f_i a^f_i \times \boldsymbol{n}^f = \boldsymbol{q}_t = \boldsymbol{f}(\boldsymbol{x}, t) +``` +These are similarly enforced as +```math +\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \times \boldsymbol{n}^f\ \mathrm{d}\Gamma += \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) \boldsymbol{q}_t\ \mathrm{d}\Gamma +``` +(no sum on ``\alpha``) with equivalent weighting functions as for ``H(\mathrm{div})`` interpolations. +""" +mutable struct IntegrateableDirichlet + const f::Function + const facets::OrderedSet{FacetIndex} + const field_name::Symbol + const qr_order::Int + # Created during `add!` + fv::Union{Nothing, FacetValues} + facet_dofs::Union{Nothing, ArrayOfVectorViews{Int, 1}} +end +function IntegrateableDirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function; qr_order = -1) + return IntegrateableDirichlet(f, convert_to_orderedset(facets), field_name, qr_order, nothing, nothing) +end + const DofCoefficients{T} = Vector{Pair{Int, T}} """ AffineConstraint(constrained_dof::Int, entries::Vector{Pair{Int,T}}, b::T) where T diff --git a/src/exports.jl b/src/exports.jl index 3a196c768d..48c685bd2c 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -143,6 +143,7 @@ export # Constraints ConstraintHandler, Dirichlet, + IntegrateableDirichlet, PeriodicDirichlet, collect_periodic_facets, collect_periodic_facets!, From 2fe350d6dc4a7d379c4c8e2b72255d8fb1a7af15 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 8 Jan 2025 21:16:24 +0100 Subject: [PATCH 160/172] Try fix test failures --- src/FEValues/boundary_integrals.jl | 12 ++++++------ test/test_interfacevalues.jl | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/FEValues/boundary_integrals.jl b/src/FEValues/boundary_integrals.jl index b4e520ab85..87bbf97eb4 100644 --- a/src/FEValues/boundary_integrals.jl +++ b/src/FEValues/boundary_integrals.jl @@ -217,7 +217,7 @@ function edge_to_cell_transformation(ξ::Vec{1, T}, ::Type{RefTriangle}, edgenr: end # Mapping from 2D face of a triangle to 1D line. -function cell_to_edge_transformation(ξ::Vec{2, T}, ::Type{RefTriangle}, facet::Int) where {T} +function cell_to_edge_transformation(ξ::Vec{2, T}, ::Type{RefTriangle}, edgenr::Int) where {T} x, y = ξ edgenr == 1 && return Vec(one(T) - x * 2) edgenr == 2 && return Vec(one(T) - y * 2) @@ -225,13 +225,13 @@ function cell_to_edge_transformation(ξ::Vec{2, T}, ::Type{RefTriangle}, facet:: throw(ArgumentError("unknown edgenr number")) end -function weighted_normal(J::Tensor{2, 2}, ::Type{RefTriangle}, facet::Int) +function weighted_normal(J::Tensor{2, 2}, ::Type{RefTriangle}, edgenr::Int) @inbounds begin - facet == 1 && return Vec{2}((-(J[2, 1] - J[2, 2]), J[1, 1] - J[1, 2])) - facet == 2 && return Vec{2}((-J[2, 2], J[1, 2])) - facet == 3 && return Vec{2}((J[2, 1], -J[1, 1])) + edgenr == 1 && return Vec{2}((-(J[2, 1] - J[2, 2]), J[1, 1] - J[1, 2])) + edgenr == 2 && return Vec{2}((-J[2, 2], J[1, 2])) + edgenr == 3 && return Vec{2}((J[2, 1], -J[1, 1])) end - throw(ArgumentError("unknown facet number")) + throw(ArgumentError("unknown edgenr number")) end function weighted_tangent(J::Tensor{2, 2}, ::Type{RefTriangle}, edgenr::Int) diff --git a/test/test_interfacevalues.jl b/test/test_interfacevalues.jl index 47c8671e0e..0042d59bc0 100644 --- a/test/test_interfacevalues.jl +++ b/test/test_interfacevalues.jl @@ -145,8 +145,8 @@ @testset "error paths" begin cell = getcells(grid, 1) dim == 1 && @test_throws ErrorException("1D elements don't use transformations for interfaces.") Ferrite.InterfaceOrientationInfo(cell, cell, 1, 1) - @test_throws ArgumentError("unknown facet number") Ferrite.element_to_facet_transformation(Vec{dim, Float64}(ntuple(_ -> 0.0, dim)), Ferrite.getrefshape(cell), 100) - @test_throws ArgumentError("unknown facet number") Ferrite.facet_to_element_transformation(Vec{dim - 1, Float64}(ntuple(_ -> 0.0, dim - 1)), Ferrite.getrefshape(cell), 100) + @test_throws ArgumentError Ferrite.element_to_facet_transformation(Vec{dim, Float64}(ntuple(_ -> 0.0, dim)), Ferrite.getrefshape(cell), 100) + @test_throws ArgumentError Ferrite.facet_to_element_transformation(Vec{dim - 1, Float64}(ntuple(_ -> 0.0, dim - 1)), Ferrite.getrefshape(cell), 100) end func_interpol = scalar_interpol for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)) @@ -177,8 +177,8 @@ end @testset "error paths" begin cell = getcells(grid, 1) - @test_throws ArgumentError("unknown facet number") Ferrite.element_to_facet_transformation(Vec{dim, Float64}(ntuple(_ -> 0.0, dim)), Ferrite.getrefshape(cell), 100) - @test_throws ArgumentError("unknown facet number") Ferrite.facet_to_element_transformation(Vec{dim - 1, Float64}(ntuple(_ -> 0.0, dim - 1)), Ferrite.getrefshape(cell), 100) + @test_throws ArgumentError Ferrite.element_to_facet_transformation(Vec{dim, Float64}(ntuple(_ -> 0.0, dim)), Ferrite.getrefshape(cell), 100) + @test_throws ArgumentError Ferrite.facet_to_element_transformation(Vec{dim - 1, Float64}(ntuple(_ -> 0.0, dim - 1)), Ferrite.getrefshape(cell), 100) end for func_interpol in (scalar_interpol, VectorizedInterpolation(scalar_interpol)) iv = InterfaceValues(quad_rule, func_interpol) From 0e7183fa1d407607c53c1f3c7fa193e004e30a5d Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Fri, 10 Jan 2025 15:30:25 +0100 Subject: [PATCH 161/172] Start work in IntegrateableDirichlet --- src/Dofs/ConstraintHandler.jl | 51 ++-------- src/Dofs/IntegrateableDirichlet.jl | 145 +++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+), 44 deletions(-) create mode 100644 src/Dofs/IntegrateableDirichlet.jl diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index be621f37e3..c03d8697bf 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -61,48 +61,7 @@ function __to_components(c) return components end -@doc raw""" - IntegrateableDirichlet(field_name::Symbol, facets, f::Function; qr_order = -1) - -An `IntegrateableDirichlet` conditions enforces conditions for `field_name` on the boundary for -non-nodal interpolations (e.g. ``H(\mathrm{div})`` or ``H(\mathrm{curl})``) in an integral sense. - -For ``H(\mathrm{div})`` interpolations, we have conditions on the form -```math -\boldsymbol{N}^f_i a^f_i \cdot \boldsymbol{n}^f = q_n = f(\boldsymbol{x}, t) -``` -These are enforced as -```math -\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \cdot \boldsymbol{n}^f\ \mathrm{d}\Gamma -= \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) q_n\ \mathrm{d}\Gamma -``` -(no sum on ``\alpha``) where the weighting functions, ``g_\alpha(\boldsymbol{x})``, are defined such that -``\sum_{\alpha} g_\alpha(\boldsymbol{x}) = 1`` at all points on the facet. -These weighting functions are defined by each interpolation. - -For ``H(\mathrm{curl})`` interpolations, the conditions are on the form -```math -\boldsymbol{N}^f_i a^f_i \times \boldsymbol{n}^f = \boldsymbol{q}_t = \boldsymbol{f}(\boldsymbol{x}, t) -``` -These are similarly enforced as -```math -\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \times \boldsymbol{n}^f\ \mathrm{d}\Gamma -= \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) \boldsymbol{q}_t\ \mathrm{d}\Gamma -``` -(no sum on ``\alpha``) with equivalent weighting functions as for ``H(\mathrm{div})`` interpolations. -""" -mutable struct IntegrateableDirichlet - const f::Function - const facets::OrderedSet{FacetIndex} - const field_name::Symbol - const qr_order::Int - # Created during `add!` - fv::Union{Nothing, FacetValues} - facet_dofs::Union{Nothing, ArrayOfVectorViews{Int, 1}} -end -function IntegrateableDirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function; qr_order = -1) - return IntegrateableDirichlet(f, convert_to_orderedset(facets), field_name, qr_order, nothing, nothing) -end +include(joinpath(@__DIR__, "IntegrateableDirichlet.jl")) const DofCoefficients{T} = Vector{Pair{Int, T}} """ @@ -126,6 +85,7 @@ A collection of constraints associated with the dof handler `dh`. """ mutable struct ConstraintHandler{DH <: AbstractDofHandler, T} const dbcs::Vector{Dirichlet} + const idbcs::Vector{IntegrateableDirichlet} const prescribed_dofs::Vector{Int} const free_dofs::Vector{Int} const inhomogeneities::Vector{T} @@ -146,8 +106,8 @@ ConstraintHandler(dh::AbstractDofHandler) = ConstraintHandler(Float64, dh) function ConstraintHandler(::Type{T}, dh::AbstractDofHandler) where {T <: Number} @assert isclosed(dh) return ConstraintHandler( - Dirichlet[], Int[], Int[], T[], Union{Nothing, T}[], Union{Nothing, DofCoefficients{T}}[], - Dict{Int, Int}(), BCValues{T}[], dh, false, + Dirichlet[], IntegrateableDirichlet[], Int[], Int[], T[], Union{Nothing, T}[], + Union{Nothing, DofCoefficients{T}}[], Dict{Int, Int}(), BCValues{T}[], dh, false, ) end @@ -462,6 +422,9 @@ function update!(ch::ConstraintHandler, time::Real = 0.0) dbc.components, ch.dh, ch.bcvalues[i], ch.dofmapping, ch.dofcoefficients, time ) end + for (i, dbc) in pairs(ch.idbcs) + _update!(ch.inhomogeneities, dbc.f, dbc.facets, dbc.fv, dbc.facet_dofs, ch.dh, ch.dofmapping, ch.dofcoefficients, time) + end # Compute effective inhomogeneity for affine constraints with prescribed dofs in the # RHS. For example, in u2 = w3 * u3 + w4 * u4 + b2 we allow e.g. u3 to be prescribed by # a trivial constraint with just an inhomogeneity (e.g. DBC), for example u3 = f(t). diff --git a/src/Dofs/IntegrateableDirichlet.jl b/src/Dofs/IntegrateableDirichlet.jl new file mode 100644 index 0000000000..ade41dc007 --- /dev/null +++ b/src/Dofs/IntegrateableDirichlet.jl @@ -0,0 +1,145 @@ +@doc raw""" + IntegrateableDirichlet(field_name::Symbol, facets, f::Function; qr_order = -1) + +An `IntegrateableDirichlet` conditions enforces conditions for `field_name` on the boundary for +non-nodal interpolations (e.g. ``H(\mathrm{div})`` or ``H(\mathrm{curl})``) in an integral sense. + +For ``H(\mathrm{div})`` interpolations, we have conditions on the form +```math +\boldsymbol{N}^f_i a^f_i \cdot \boldsymbol{n}^f = q_n = f(\boldsymbol{x}, t) +``` +These are enforced as +```math +\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \cdot \boldsymbol{n}^f\ \mathrm{d}\Gamma += \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) q_n\ \mathrm{d}\Gamma +``` +(no sum on ``\alpha``) where the weighting functions, ``g_\alpha(\boldsymbol{x})``, are defined such that +``\sum_{\alpha} g_\alpha(\boldsymbol{x}) = 1`` at all points on the facet. +These weighting functions are defined by each interpolation. + +For ``H(\mathrm{curl})`` interpolations, the conditions are on the form +```math +\boldsymbol{N}^f_i a^f_i \times \boldsymbol{n}^f = \boldsymbol{q}_t = \boldsymbol{f}(\boldsymbol{x}, t) +``` +These are similarly enforced as +```math +\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \times \boldsymbol{n}^f\ \mathrm{d}\Gamma += \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) \boldsymbol{q}_t\ \mathrm{d}\Gamma +``` +(no sum on ``\alpha``) with equivalent weighting functions as for ``H(\mathrm{div})`` interpolations. +""" +mutable struct IntegrateableDirichlet + const f::Function + const facets::OrderedSet{FacetIndex} + const field_name::Symbol + const qr_order::Int + # Created during `add!` + fv::Union{Nothing, FacetValues} + facet_dofs::Union{Nothing, ArrayOfVectorViews{Int, 1}} + ip::Union{Nothing, Interpolation} +end +function IntegrateableDirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function; qr_order = -1) + return IntegrateableDirichlet(f, convert_to_orderedset(facets), field_name, qr_order, nothing, nothing) +end + +function _default_bc_qr_order(user_provided::Int, ip::Interpolation) + user_provided > 0 && return user_provided + return _default_bc_qr_order(ip) +end +# Q&D default, should be more elaborated +_default_bc_qr_order(::Interpolation{<:Any, order}) where {order} = order + +function add!(ch::ConstraintHandler, dbc::IntegrateableDirichlet) + # Duplicate the Dirichlet constraint for every SubDofHandler + dbc_added = false + for sdh in ch.dh.subdofhandlers + # Skip if the constrained field does not live on this sub domain + dbc.field_name in sdh.field_names || continue + # Compute the intersection between dbc.set and the cellset of this + # SubDofHandler and skip if the set is empty + filtered_set = filter_dbc_set(get_grid(ch.dh), sdh.cellset, dbc.facets) + isempty(filtered_set) && continue + # Fetch information about the field on this SubDofHandler + field_idx = find_field(sdh, dbc.field_name) + interpolation = getfieldinterpolation(sdh, field_idx) + CT = getcelltype(sdh) # Same celltype enforced in SubDofHandler constructor + qr_order = _default_qr_order(dbc.qr_order, interpolation) + fqr = FacetQuadratureRule{getrefshape(interpolation)}(qr_order) + fv = FacetValues(fqr, interpolation, geometric_interpolation(CT)) + local_facet_dofs, local_facet_dofs_offset = + _local_facet_dofs_for_bc(interpolation, 1, 1, field_offset(sdh, field_idx), dirichlet_facetdof_indices) + facet_dofs = ArrayOfVectorViews(local_facet_dofs, local_facet_dofs_offset, LinearIndices(1:(length(local_facet_dofs_offset) - 1))) + + filtered_dbc = IntegrateableDirichlet(dbc.f, filtered_set, dbc.field_name, fv, facet_dofs) + + _add!(ch, filtered_dbc, facet_dofs) + + dbc_added = true + end + dbc_added || error("No overlap between dbc::Dirichlet and fields in the ConstraintHandler's DofHandler") + return ch +end + +function _add!(ch::ConstraintHandler, dbc::IntegrateableDirichlet, facet_dofs) + # loop over all the faces in the set and add the global dofs to `constrained_dofs` + constrained_dofs = Int[] + cc = CellCache(ch.dh, UpdateFlags(; nodes = false, coords = false, dofs = true)) + for (cellidx, facetidx) in dbc.facets + reinit!(cc, cellidx) + local_dofs = facet_dofs[facetidx] + for d in local_dofs + push!(constrained_dofs, cc.dofs[d]) + end + end + + # save it to the ConstraintHandler + push!(ch.idbcs, dbc) + for d in constrained_dofs + add_prescribed_dof!(ch, d, NaN, nothing) + end + return ch +end + +_update!(ch.inhomogeneities, dbc.f, dbc.facets, dbc.ip, dbc.facet_dofs, ch.dh, ch.dofmapping, ch.dofcoefficients, time) + +function _update!( + inhomogeneities::Vector{T}, f::Function, facets::AbstractVecOrSet{FacetIndex}, fv::FacetValues, facet_dofs::ArrayOfVectorViews, + dh::AbstractDofHandler, dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real + ) where {T} + + for fc in FacetIterator(dh, facets) + reinit!(fv, fc) + + local_dofs = facet_dofs[getcurrentfacet(fv)] + # local dof-range for this facet + r = local_facet_dofs_offset[entityidx]:(local_facet_dofs_offset[entityidx + 1] - 1) + counter = 1 + for location in 1:getnquadpoints(boundaryvalues) + sign = if mapping_type(ip) isa IdentityMapping + 1 + else + cell = getcells(cc.grid, cellidx) + shape_number = local_facet_dofs[r[counter]] + get_direction(ip, shape_number, cell) + end + x = spatial_coordinate(boundaryvalues, location, cc.coords) + bc_value = f(x, time) + @assert length(bc_value) == length(components) + + for i in 1:length(components) + # find the global dof + globaldof = cc.dofs[local_facet_dofs[r[counter]]] + counter += 1 + + dbc_index = dofmapping[globaldof] + # Only DBC dofs are currently update!-able so don't modify inhomogeneities + # for affine constraints + if dofcoefficients[dbc_index] === nothing + inhomogeneities[dbc_index] = sign * bc_value[i] + @debug println("prescribing value $(bc_value[i]) on global dof $(globaldof)") + end + end + end + end + return +end From 72706943ada12d9cf6e85b539527748a36ad2c2e Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Fri, 17 Jan 2025 18:59:06 +0100 Subject: [PATCH 162/172] Correct spelling of integrable --- docs/src/reference/boundary_conditions.md | 2 +- src/Dofs/ConstraintHandler.jl | 6 +- src/Dofs/IntegrableDirichlet.jl | 210 ++++++++++++++++++++++ src/Dofs/IntegrateableDirichlet.jl | 145 --------------- src/exports.jl | 2 +- 5 files changed, 215 insertions(+), 150 deletions(-) create mode 100644 src/Dofs/IntegrableDirichlet.jl delete mode 100644 src/Dofs/IntegrateableDirichlet.jl diff --git a/docs/src/reference/boundary_conditions.md b/docs/src/reference/boundary_conditions.md index 969682c892..13b46db75f 100644 --- a/docs/src/reference/boundary_conditions.md +++ b/docs/src/reference/boundary_conditions.md @@ -11,7 +11,7 @@ Pages = ["boundary_conditions.md"] ```@docs ConstraintHandler Dirichlet -IntegrateableDirichlet +IntegrableDirichlet PeriodicDirichlet collect_periodic_facets collect_periodic_facets! diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index c03d8697bf..4cf9f9f52a 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -61,7 +61,7 @@ function __to_components(c) return components end -include(joinpath(@__DIR__, "IntegrateableDirichlet.jl")) +include(joinpath(@__DIR__, "IntegrableDirichlet.jl")) const DofCoefficients{T} = Vector{Pair{Int, T}} """ @@ -85,7 +85,7 @@ A collection of constraints associated with the dof handler `dh`. """ mutable struct ConstraintHandler{DH <: AbstractDofHandler, T} const dbcs::Vector{Dirichlet} - const idbcs::Vector{IntegrateableDirichlet} + const idbcs::Vector{IntegrableDirichlet} const prescribed_dofs::Vector{Int} const free_dofs::Vector{Int} const inhomogeneities::Vector{T} @@ -106,7 +106,7 @@ ConstraintHandler(dh::AbstractDofHandler) = ConstraintHandler(Float64, dh) function ConstraintHandler(::Type{T}, dh::AbstractDofHandler) where {T <: Number} @assert isclosed(dh) return ConstraintHandler( - Dirichlet[], IntegrateableDirichlet[], Int[], Int[], T[], Union{Nothing, T}[], + Dirichlet[], IntegrableDirichlet[], Int[], Int[], T[], Union{Nothing, T}[], Union{Nothing, DofCoefficients{T}}[], Dict{Int, Int}(), BCValues{T}[], dh, false, ) end diff --git a/src/Dofs/IntegrableDirichlet.jl b/src/Dofs/IntegrableDirichlet.jl new file mode 100644 index 0000000000..2061c25d4d --- /dev/null +++ b/src/Dofs/IntegrableDirichlet.jl @@ -0,0 +1,210 @@ +@doc raw""" + IntegrableDirichlet(field_name::Symbol, facets, f::Function; qr_order = -1) + +An `IntegrableDirichlet` conditions enforces conditions for `field_name` on the boundary for +non-nodal interpolations (e.g. ``H(\mathrm{div})`` or ``H(\mathrm{curl})``) in an integral sense. + +For ``H(\mathrm{div})`` interpolations, we have conditions on the form +```math +\boldsymbol{N}^f_i a^f_i \cdot \boldsymbol{n}^f = q_n = f(\boldsymbol{x}, t) +``` +These are enforced as +```math +\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \cdot \boldsymbol{n}^f\ \mathrm{d}\Gamma += \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) q_n\ \mathrm{d}\Gamma +``` +(no sum on ``\alpha``) where the weighting functions, ``g_\alpha(\boldsymbol{x})``, are defined such that +``\sum_{\alpha} g_\alpha(\boldsymbol{x}) = 1`` at all points on the facet. +These weighting functions are defined by each interpolation. + +For ``H(\mathrm{curl})`` interpolations, the conditions are on the form +```math +\boldsymbol{N}^f_i a^f_i \times \boldsymbol{n}^f = \boldsymbol{q}_t = \boldsymbol{f}(\boldsymbol{x}, t) +``` +These are similarly enforced as +```math +a^f_\alpha = +\frac{\int_{\Gamma^f} g_\alpha(\boldsymbol{x}) \boldsymbol{q}_t\ \mathrm{d}\Gamma}{ +\int_{\Gamma^f} \boldsymbol{N}^f_\alpha \times \boldsymbol{n}^f\ \mathrm{d}\Gamma} +``` +(no sum on ``\alpha``) with equivalent weighting functions as for ``H(\mathrm{div})`` interpolations. +""" +mutable struct IntegrableDirichlet + const f::Function + const facets::OrderedSet{FacetIndex} + const field_name::Symbol + const qr_order::Int + # Created during `add!` + fv::Union{Nothing, FacetValues} + facet_dofs::Union{Nothing, ArrayOfVectorViews{Int, 1}} + ip::Union{Nothing, Interpolation} +end +function IntegrableDirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function; qr_order = -1) + return IntegrableDirichlet(f, convert_to_orderedset(facets), field_name, qr_order, nothing, nothing) +end + +function _default_bc_qr_order(user_provided::Int, ip::Interpolation) + user_provided > 0 && return user_provided + return _default_bc_qr_order(ip) +end +# Q&D default, should be more elaborated +_default_bc_qr_order(::Interpolation{<:Any, order}) where {order} = order + +function add!(ch::ConstraintHandler, dbc::IntegrableDirichlet) + # Duplicate the Dirichlet constraint for every SubDofHandler + dbc_added = false + for sdh in ch.dh.subdofhandlers + # Skip if the constrained field does not live on this sub domain + dbc.field_name in sdh.field_names || continue + # Compute the intersection between dbc.set and the cellset of this + # SubDofHandler and skip if the set is empty + filtered_set = filter_dbc_set(get_grid(ch.dh), sdh.cellset, dbc.facets) + isempty(filtered_set) && continue + # Fetch information about the field on this SubDofHandler + field_idx = find_field(sdh, dbc.field_name) + interpolation = getfieldinterpolation(sdh, field_idx) + CT = getcelltype(sdh) # Same celltype enforced in SubDofHandler constructor + qr_order = _default_qr_order(dbc.qr_order, interpolation) + fqr = FacetQuadratureRule{getrefshape(interpolation)}(qr_order) + fv = FacetValues(fqr, interpolation, geometric_interpolation(CT)) + local_facet_dofs, local_facet_dofs_offset = + _local_facet_dofs_for_bc(interpolation, 1, 1, field_offset(sdh, field_idx), dirichlet_facetdof_indices) + facet_dofs = ArrayOfVectorViews(local_facet_dofs, local_facet_dofs_offset, LinearIndices(1:(length(local_facet_dofs_offset) - 1))) + + filtered_dbc = IntegrableDirichlet(dbc.f, filtered_set, dbc.field_name, fv, facet_dofs) + + _add!(ch, filtered_dbc, facet_dofs) + + dbc_added = true + end + dbc_added || error("No overlap between dbc::Dirichlet and fields in the ConstraintHandler's DofHandler") + return ch +end + +function _add!(ch::ConstraintHandler, dbc::IntegrableDirichlet, facet_dofs) + # loop over all the faces in the set and add the global dofs to `constrained_dofs` + constrained_dofs = Int[] + cc = CellCache(ch.dh, UpdateFlags(; nodes = false, coords = false, dofs = true)) + for (cellidx, facetidx) in dbc.facets + reinit!(cc, cellidx) + local_dofs = facet_dofs[facetidx] + for d in local_dofs + push!(constrained_dofs, cc.dofs[d]) + end + end + + # save it to the ConstraintHandler + push!(ch.idbcs, dbc) + for d in constrained_dofs + add_prescribed_dof!(ch, d, NaN, nothing) + end + return ch +end + +_update!(ch.inhomogeneities, dbc.f, dbc.facets, dbc.ip, dbc.facet_dofs, ch.dh, ch.dofmapping, ch.dofcoefficients, time) + +function _update!( + inhomogeneities::Vector{T}, f::Function, facets::AbstractVecOrSet{FacetIndex}, fv::FacetValues, facet_dofs::ArrayOfVectorViews, + dh::AbstractDofHandler, dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real + ) where {T} + ip = function_interpolation(fv) + for fc in FacetIterator(dh, facets) + reinit!(fv, fc) + local_dofs = facet_dofs[getcurrentfacet(fv)] + for (idof, shape_nr) in enumerate(dirichlet_facetdof_indices(ip)[getcurrentfacet(fv)]) + bc_value = _integrate_dbc(f, fv, shape_nr, idof, ip, getcoordinates(fc), time) + globaldof = celldofs(fc)[shape_nr] + dbc_index = dofmapping[globaldof] + # Only DBC dofs are currently update!-able so don't modify inhomogeneities + # for affine constraints + if dofcoefficients[dbc_index] === nothing + inhomogeneities[dbc_index] = bc_value + end + end + end + return nothing +end + +# Temp, put in interpolations.jl +""" +edge_parameterization(::Type{<:AbstractRefShape}, ξ, edge_id) + +An edge is parameterized by the normalized curve coordinate `s [0, 1]`, +increasing in the positive edge direction. +""" +function edge_parameterization(::Type{RefShape}, ξ, edge_id) where {RefShape <: Ferrite.AbstractRefShape} + ipg = Lagrange{RefShape, 1}() # Reference shape always described by 1st order Lagrange ip. + refcoords = Ferrite.reference_coordinates(ipg) + i1, i2 = Ferrite.edgedof_indices(ipg)[edge_id] + ξ1, ξ2 = (refcoords[i1], refcoords[i2]) + Δξ = ξ2 - ξ1 + L = norm(Δξ) + s = (ξ - ξ1) ⋅ normalize(Δξ) / L + @assert norm(ξ - ξ1) ≈ L * s # Ensure ξ is on the line ξ1 - ξ2 + @assert -eps(L) ≤ s ≤ (1 + eps(L)) # Ensure ξ is between ξ1 and ξ2 + return s +end + +function facet_parameterization(::Type{<:Ferrite.AbstractRefShape{3}}, ξ, facet_id) + # Not implemented (not yet defined in Ferrite what this should be), + # but to support testing interpolations with a single facedof interior index, + # we return `nothing` just to allow running the code as long as the output isn't used. + return nothing +end + +function facet_moment(ip::Interpolation{RS}, idof::Int, ξ::Vec, facet_nr) + s = facet_parameterization(RS, ξ, facet_nr) + return facet_moment(ip, idof, s) +end + +facet_moment(::RaviartThomas{2, RefTriangle, 1}, idof::Int, s::Real) = one(s) +facet_moment(::RaviartThomas{2, RefTriangle, 2}, idof::Int, s::Real) = (idof == 1 ? one(s) - s : s) +facet_moment(::BrezziDouglasMarini{2, RefTriangle, 1}, idof::Int, s::Real) = (idof == 1 ? one(s) - s : s) +facet_moment(::Nedelec{2, RefTriangle, 1}, idof::Int, s::Real) = one(s) +facet_moment(::Nedelec{2, RefTriangle, 2}, idof::Int, s::Real) = (idof == 1 ? one(s) - s : s) + +function_space(::Interpolation) = Val(:H1) # Default fallback, should perhaps be explicit... +function_space(::RaviartThomas) = Val(:Hdiv) +function_space(::BrezziDouglasMarini) = Val(:Hdiv) +function_space(::Nedelec) = Val(:Hcurl) +# End of temp that should go in interpolations.jl + +function _integrate_dbc(f::Function, args...) + return _integrate_dbc(function_space(ip), args...) +end + +function _integrate_dbc(::Val{:Hdiv}, f::Function, fv::FacetValues, shape_nr, idof, ip, cellcoords, time) + # Could speed up by having _integrate_facet take two functions... + f1(N, n, x, t, ξ) = facet_moment(ip, idof, ξ, getcurrentfacet(fv)) * f(x, t) + top = _integrate_facet(f1, fv, shape_nr, cellcoords, time) + f2(N, n, x, t, ξ) = N × n + bot = _integrate_facet(f2, fv, shape_nr, cellcoords, time) + return top / bot +end + +function _integrate_dbc(::Val{:Hcurl}, f::Function, fv::FacetValues, shape_nr, idof, ip, cellcoords, time) + # Could speed up by having _integrate_facet take two functions... + f1(N, n, x, t, ξ) = facet_moment(ip, idof, ξ, getcurrentfacet(fv)) * f(x, t) + top = _integrate_facet(f1, fv, shape_nr, cellcoords, time) + f2(N, n, x, t, ξ) = N ⋅ n + bot = _integrate_facet(f2, fv, shape_nr, cellcoords, time) + return top / bot +end + +_integrate_dbc(::Val{:H1}, args...) = ArgumentError("Dirichlet BC for H1 interpolations are not integrable") + +function _integrate_facet(f::F, fv::FacetValues, shape_nr, cellcoords, time) where {F} + function qp_contribution(q_point::Int) + x = spatial_coordinate(fv, q_point, cellcoords) + n = getnormal(fv, q_point) + dΓ = getdetJdV(fv, q_point) + N = shape_value(fv, q_point, shape_nr) + ξ = getpoints(fv.fqr, getcurrentfacet(fv))[q_point] + return f(N, n, x, time, ξ) * dΓ + end + retval = qp_contribution(1) + for q_point in 2:getnquadpoints(fv) + retval += qp_contribution(q_point) + end + return retval +end diff --git a/src/Dofs/IntegrateableDirichlet.jl b/src/Dofs/IntegrateableDirichlet.jl deleted file mode 100644 index ade41dc007..0000000000 --- a/src/Dofs/IntegrateableDirichlet.jl +++ /dev/null @@ -1,145 +0,0 @@ -@doc raw""" - IntegrateableDirichlet(field_name::Symbol, facets, f::Function; qr_order = -1) - -An `IntegrateableDirichlet` conditions enforces conditions for `field_name` on the boundary for -non-nodal interpolations (e.g. ``H(\mathrm{div})`` or ``H(\mathrm{curl})``) in an integral sense. - -For ``H(\mathrm{div})`` interpolations, we have conditions on the form -```math -\boldsymbol{N}^f_i a^f_i \cdot \boldsymbol{n}^f = q_n = f(\boldsymbol{x}, t) -``` -These are enforced as -```math -\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \cdot \boldsymbol{n}^f\ \mathrm{d}\Gamma -= \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) q_n\ \mathrm{d}\Gamma -``` -(no sum on ``\alpha``) where the weighting functions, ``g_\alpha(\boldsymbol{x})``, are defined such that -``\sum_{\alpha} g_\alpha(\boldsymbol{x}) = 1`` at all points on the facet. -These weighting functions are defined by each interpolation. - -For ``H(\mathrm{curl})`` interpolations, the conditions are on the form -```math -\boldsymbol{N}^f_i a^f_i \times \boldsymbol{n}^f = \boldsymbol{q}_t = \boldsymbol{f}(\boldsymbol{x}, t) -``` -These are similarly enforced as -```math -\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \times \boldsymbol{n}^f\ \mathrm{d}\Gamma -= \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) \boldsymbol{q}_t\ \mathrm{d}\Gamma -``` -(no sum on ``\alpha``) with equivalent weighting functions as for ``H(\mathrm{div})`` interpolations. -""" -mutable struct IntegrateableDirichlet - const f::Function - const facets::OrderedSet{FacetIndex} - const field_name::Symbol - const qr_order::Int - # Created during `add!` - fv::Union{Nothing, FacetValues} - facet_dofs::Union{Nothing, ArrayOfVectorViews{Int, 1}} - ip::Union{Nothing, Interpolation} -end -function IntegrateableDirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function; qr_order = -1) - return IntegrateableDirichlet(f, convert_to_orderedset(facets), field_name, qr_order, nothing, nothing) -end - -function _default_bc_qr_order(user_provided::Int, ip::Interpolation) - user_provided > 0 && return user_provided - return _default_bc_qr_order(ip) -end -# Q&D default, should be more elaborated -_default_bc_qr_order(::Interpolation{<:Any, order}) where {order} = order - -function add!(ch::ConstraintHandler, dbc::IntegrateableDirichlet) - # Duplicate the Dirichlet constraint for every SubDofHandler - dbc_added = false - for sdh in ch.dh.subdofhandlers - # Skip if the constrained field does not live on this sub domain - dbc.field_name in sdh.field_names || continue - # Compute the intersection between dbc.set and the cellset of this - # SubDofHandler and skip if the set is empty - filtered_set = filter_dbc_set(get_grid(ch.dh), sdh.cellset, dbc.facets) - isempty(filtered_set) && continue - # Fetch information about the field on this SubDofHandler - field_idx = find_field(sdh, dbc.field_name) - interpolation = getfieldinterpolation(sdh, field_idx) - CT = getcelltype(sdh) # Same celltype enforced in SubDofHandler constructor - qr_order = _default_qr_order(dbc.qr_order, interpolation) - fqr = FacetQuadratureRule{getrefshape(interpolation)}(qr_order) - fv = FacetValues(fqr, interpolation, geometric_interpolation(CT)) - local_facet_dofs, local_facet_dofs_offset = - _local_facet_dofs_for_bc(interpolation, 1, 1, field_offset(sdh, field_idx), dirichlet_facetdof_indices) - facet_dofs = ArrayOfVectorViews(local_facet_dofs, local_facet_dofs_offset, LinearIndices(1:(length(local_facet_dofs_offset) - 1))) - - filtered_dbc = IntegrateableDirichlet(dbc.f, filtered_set, dbc.field_name, fv, facet_dofs) - - _add!(ch, filtered_dbc, facet_dofs) - - dbc_added = true - end - dbc_added || error("No overlap between dbc::Dirichlet and fields in the ConstraintHandler's DofHandler") - return ch -end - -function _add!(ch::ConstraintHandler, dbc::IntegrateableDirichlet, facet_dofs) - # loop over all the faces in the set and add the global dofs to `constrained_dofs` - constrained_dofs = Int[] - cc = CellCache(ch.dh, UpdateFlags(; nodes = false, coords = false, dofs = true)) - for (cellidx, facetidx) in dbc.facets - reinit!(cc, cellidx) - local_dofs = facet_dofs[facetidx] - for d in local_dofs - push!(constrained_dofs, cc.dofs[d]) - end - end - - # save it to the ConstraintHandler - push!(ch.idbcs, dbc) - for d in constrained_dofs - add_prescribed_dof!(ch, d, NaN, nothing) - end - return ch -end - -_update!(ch.inhomogeneities, dbc.f, dbc.facets, dbc.ip, dbc.facet_dofs, ch.dh, ch.dofmapping, ch.dofcoefficients, time) - -function _update!( - inhomogeneities::Vector{T}, f::Function, facets::AbstractVecOrSet{FacetIndex}, fv::FacetValues, facet_dofs::ArrayOfVectorViews, - dh::AbstractDofHandler, dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real - ) where {T} - - for fc in FacetIterator(dh, facets) - reinit!(fv, fc) - - local_dofs = facet_dofs[getcurrentfacet(fv)] - # local dof-range for this facet - r = local_facet_dofs_offset[entityidx]:(local_facet_dofs_offset[entityidx + 1] - 1) - counter = 1 - for location in 1:getnquadpoints(boundaryvalues) - sign = if mapping_type(ip) isa IdentityMapping - 1 - else - cell = getcells(cc.grid, cellidx) - shape_number = local_facet_dofs[r[counter]] - get_direction(ip, shape_number, cell) - end - x = spatial_coordinate(boundaryvalues, location, cc.coords) - bc_value = f(x, time) - @assert length(bc_value) == length(components) - - for i in 1:length(components) - # find the global dof - globaldof = cc.dofs[local_facet_dofs[r[counter]]] - counter += 1 - - dbc_index = dofmapping[globaldof] - # Only DBC dofs are currently update!-able so don't modify inhomogeneities - # for affine constraints - if dofcoefficients[dbc_index] === nothing - inhomogeneities[dbc_index] = sign * bc_value[i] - @debug println("prescribing value $(bc_value[i]) on global dof $(globaldof)") - end - end - end - end - return -end diff --git a/src/exports.jl b/src/exports.jl index 48c685bd2c..60a7a7cdbb 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -143,7 +143,7 @@ export # Constraints ConstraintHandler, Dirichlet, - IntegrateableDirichlet, + IntegrableDirichlet, PeriodicDirichlet, collect_periodic_facets, collect_periodic_facets!, From 4423214daefe7af308ae474325af043e3e236fb5 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sat, 18 Jan 2025 15:14:25 +0100 Subject: [PATCH 163/172] Redefine and rename to WeakDirichlet, tests passing locally --- docs/src/reference/boundary_conditions.md | 2 +- src/Dofs/ConstraintHandler.jl | 240 +++++++++++++++++++++- src/Dofs/IntegrableDirichlet.jl | 210 ------------------- src/FEValues/CellValues.jl | 2 +- src/FEValues/FacetValues.jl | 2 +- src/FEValues/common_values.jl | 7 + src/Ferrite.jl | 2 +- src/exports.jl | 2 +- src/iterators.jl | 16 +- test/test_interpolations.jl | 150 +++++++------- 10 files changed, 336 insertions(+), 297 deletions(-) delete mode 100644 src/Dofs/IntegrableDirichlet.jl diff --git a/docs/src/reference/boundary_conditions.md b/docs/src/reference/boundary_conditions.md index 13b46db75f..2d8a8353c7 100644 --- a/docs/src/reference/boundary_conditions.md +++ b/docs/src/reference/boundary_conditions.md @@ -11,7 +11,7 @@ Pages = ["boundary_conditions.md"] ```@docs ConstraintHandler Dirichlet -IntegrableDirichlet +WeakDirichlet PeriodicDirichlet collect_periodic_facets collect_periodic_facets! diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index 4cf9f9f52a..33414f5ac3 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -61,7 +61,89 @@ function __to_components(c) return components end -include(joinpath(@__DIR__, "IntegrableDirichlet.jl")) +@doc raw""" + WeakDirichlet(field_name::Symbol, facets, f::Function; qr_order = -1) + +An `WeakDirichlet` conditions enforces conditions for `field_name` on the boundary for +non-nodal interpolations (e.g. ``H(\mathrm{div})`` or ``H(\mathrm{curl})``) in an weak sense. + +# ``H(\mathrm{div})`` interpolations + +For ``H(\mathrm{div})`` interpolations, we have conditions on the form +```math +\boldsymbol{N}^f_i a^f_i \cdot \boldsymbol{n}^f = q_n = f(\boldsymbol{x}, t, \boldsymbol{n}) +``` + +To satisfy this condition in a weak sense, we multiply with the (arbitrary) test +function ``\delta q_n \approx \delta\boldsymbol{N}^f_j c^f_j \cdot \boldsymbol{n}^f``, +and integrate over the facet, ``\Gamma^f``, resulting in +```math +\underbrace{\int_{\Gamma^f} \left[\delta\boldsymbol{N}^f_i \cdot \boldsymbol{n}^f\right]\left[\boldsymbol{N}^f_j \cdot \boldsymbol{n}^f\right]\ \mathrm{d}\Gamma}_{K^f_{ij}}\ a_j^f += \underbrace{\int_{\Gamma^f} \left[\delta\boldsymbol{N}^f_i \cdot \boldsymbol{n}^f\right] q_n\ \mathrm{d}\Gamma}_{f^f_i} +``` + +By solving the equation ``\underbar{\underbar K}^f \underbar{a}^f = \underbar{f}^f``, we can +determine the coefficients ``\underbar{a}^f`` for the current facet. + +# ``H(\mathrm{curl})`` interpolations + +For ``H(\mathrm{curl})`` interpolations, the conditions are on the form +```math +\boldsymbol{N}^f_j a^f_j \times \boldsymbol{n}^f = \boldsymbol{q}_t = \boldsymbol{f}(\boldsymbol{x}, t, \boldsymbol{n}) +``` +To satisfy this condition in a weak sense, we take the single contraction +with the (arbitrary) test function +``\delta \boldsymbol{q}_t \approx \delta\boldsymbol{N}^f_i c^f_i \times \boldsymbol{n}^f``, +and integrate over the facet, ``\Gamma^f``, resulting in, +```math +\underbrace{\int_{\Gamma^f} \left[\delta\boldsymbol{N}^f_i \times \boldsymbol{n}^f\right]\cdot\left[\boldsymbol{N}^f_j \times \boldsymbol{n}^f\right]\ \mathrm{d}\Gamma}_{K^f_{ij}}\ a_j^f += \underbrace{\int_{\Gamma^f} \left[\delta\boldsymbol{N}^f_i \times \boldsymbol{n}^f\right]\cdot \boldsymbol{q}_t\ \mathrm{d}\Gamma}_{f^f_i} +``` + +## Old notes +For ``H(\mathrm{div})`` interpolations, we have conditions on the form +```math +\boldsymbol{N}^f_i a^f_i \cdot \boldsymbol{n}^f = q_n = f(\boldsymbol{x}, t, \boldsymbol{n}) +``` +where ``\boldsymbol{x}`` is the spatial coordinate, ``t`` the current time, and ``\boldsymbol{n}`` +the facet normal. These conditions are enforced as an integral over the facet ``\Gamma^f``. +```math +\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \cdot \boldsymbol{n}^f\ \mathrm{d}\Gamma += \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) q_n\ \mathrm{d}\Gamma +``` +(no sum on ``\alpha``) where the weighting functions, ``g_\alpha(\boldsymbol{x})``, are defined such that +``\sum_{\alpha} g_\alpha(\boldsymbol{x}) = 1`` at all points on the facet. +These weighting functions are defined by each interpolation. + +For ``H(\mathrm{curl})`` interpolations, the conditions are on the form +```math +\boldsymbol{N}^f_i a^f_i \times \boldsymbol{n}^f = \boldsymbol{q}_t = +\left[\boldsymbol{I} - \boldsymbol{n}\otimes\boldsymbol{n}\right] \cdot \boldsymbol{f}(\boldsymbol{x}, t, \boldsymbol{n}) +``` +where the multiplication with ``\boldsymbol{I} - \boldsymbol{n}\otimes\boldsymbol{n}`` projects +``\boldsymbol{f}(\boldsymbol{x}, t, \boldsymbol{n})`` onto the plane normal to ``\boldsymbol{n}``. + +Currently, these constraints are not implemented for 3d cases. But the current implementation +enforces this condition by considering the projection along ``\boldsymbol{q}_t``, i.e. +```math +a^f_\alpha = +\frac{\int_{\Gamma^f} g_\alpha(\boldsymbol{x}) \boldsymbol{q}_t \cdot \boldsymbol{q}_t\ \mathrm{d}\Gamma}{ +\int_{\Gamma^f} \left[\boldsymbol{N}^f_\alpha \times \boldsymbol{n}^f\right]\cdot \boldsymbol{q}_t\ \mathrm{d}\Gamma} +``` +(no sum on ``\alpha``) with equivalent weighting functions as for ``H(\mathrm{div})`` interpolations. +""" +mutable struct WeakDirichlet + const f::Function + const facets::OrderedSet{FacetIndex} + const field_name::Symbol + const qr_order::Int + # Created during `add!` + fv::Union{Nothing, FacetValues} + facet_dofs::Union{Nothing, ArrayOfVectorViews{Int, 1}} +end +function WeakDirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function; qr_order = -1) + return WeakDirichlet(f, convert_to_orderedset(facets), field_name, qr_order, nothing, nothing) +end const DofCoefficients{T} = Vector{Pair{Int, T}} """ @@ -85,7 +167,7 @@ A collection of constraints associated with the dof handler `dh`. """ mutable struct ConstraintHandler{DH <: AbstractDofHandler, T} const dbcs::Vector{Dirichlet} - const idbcs::Vector{IntegrableDirichlet} + const idbcs::Vector{WeakDirichlet} const prescribed_dofs::Vector{Int} const free_dofs::Vector{Int} const inhomogeneities::Vector{T} @@ -106,7 +188,7 @@ ConstraintHandler(dh::AbstractDofHandler) = ConstraintHandler(Float64, dh) function ConstraintHandler(::Type{T}, dh::AbstractDofHandler) where {T <: Number} @assert isclosed(dh) return ConstraintHandler( - Dirichlet[], IntegrableDirichlet[], Int[], Int[], T[], Union{Nothing, T}[], + Dirichlet[], WeakDirichlet[], Int[], Int[], T[], Union{Nothing, T}[], Union{Nothing, DofCoefficients{T}}[], Dict{Int, Int}(), BCValues{T}[], dh, false, ) end @@ -1778,3 +1860,155 @@ function _condense_local!( end return end + + +function _default_bc_qr_order(user_provided::Int, ip::Interpolation) + user_provided > 0 && return user_provided + return _default_bc_qr_order(ip) +end +# Q&D default, should be more elaborated +_default_bc_qr_order(::Interpolation{<:Any, order}) where {order} = 2 * order + +function add!(ch::ConstraintHandler, dbc::WeakDirichlet) + # Duplicate the Dirichlet constraint for every SubDofHandler + dbc_added = false + for sdh in ch.dh.subdofhandlers + # Skip if the constrained field does not live on this sub domain + dbc.field_name in sdh.field_names || continue + # Compute the intersection between dbc.set and the cellset of this + # SubDofHandler and skip if the set is empty + filtered_set = filter_dbc_set(get_grid(ch.dh), sdh.cellset, dbc.facets) + isempty(filtered_set) && continue + # Fetch information about the field on this SubDofHandler + field_idx = find_field(sdh, dbc.field_name) + interpolation = getfieldinterpolation(sdh, field_idx) + CT = getcelltype(sdh) # Same celltype enforced in SubDofHandler constructor + qr_order = _default_bc_qr_order(dbc.qr_order, interpolation) + fqr = FacetQuadratureRule{getrefshape(interpolation)}(qr_order) + fv = FacetValues(fqr, interpolation, geometric_interpolation(CT)) + local_facet_dofs, local_facet_dofs_offset = + _local_facet_dofs_for_bc(interpolation, 1, 1, field_offset(sdh, field_idx), dirichlet_facetdof_indices) + facet_dofs = ArrayOfVectorViews(local_facet_dofs_offset, local_facet_dofs, LinearIndices(1:(length(local_facet_dofs_offset) - 1))) + + filtered_dbc = WeakDirichlet(dbc.f, filtered_set, dbc.field_name, qr_order, fv, facet_dofs) + + _add!(ch, filtered_dbc, facet_dofs) + + dbc_added = true + end + dbc_added || error("No overlap between dbc::Dirichlet and fields in the ConstraintHandler's DofHandler") + return ch +end + +function _add!(ch::ConstraintHandler, dbc::WeakDirichlet, facet_dofs) + # loop over all the faces in the set and add the global dofs to `constrained_dofs` + constrained_dofs = Int[] + cc = CellCache(ch.dh, UpdateFlags(; nodes = false, coords = false, dofs = true)) + for (cellidx, facetidx) in dbc.facets + reinit!(cc, cellidx) + local_dofs = facet_dofs[facetidx] + for d in local_dofs + push!(constrained_dofs, cc.dofs[d]) + end + end + + # save it to the ConstraintHandler + push!(ch.idbcs, dbc) + for d in constrained_dofs + add_prescribed_dof!(ch, d, NaN, nothing) + end + return ch +end + + +function _update!( + inhomogeneities::Vector{T}, f::Function, facets::AbstractVecOrSet{FacetIndex}, fv::FacetValues, facet_dofs::ArrayOfVectorViews, + dh::AbstractDofHandler, dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real + ) where {T} + ip = function_interpolation(fv) + n = maximum(length, dirichlet_facetdof_indices(ip)) + Kᶠ = zeros(n, n) + aᶠ = zeros(n) + fᶠ = zeros(n) + for fc in FacetIterator(dh, facets) + reinit!(fv, fc) + shape_nrs = dirichlet_facetdof_indices(ip)[getcurrentfacet(fv)] + solve_weak_dbc!(aᶠ, Kᶠ, fᶠ, f, fv, shape_nrs, getcoordinates(fc), time) + for (idof, shape_nr) in enumerate(shape_nrs) + globaldof = celldofs(fc)[shape_nr] + dbc_index = dofmapping[globaldof] + # Only DBC dofs are currently update!-able so don't modify inhomogeneities + # for affine constraints + if dofcoefficients[dbc_index] === nothing + inhomogeneities[dbc_index] = aᶠ[idof] + end + end + end + return nothing +end + +function solve_weak_dbc!(aᶠ, Kᶠ, fᶠ, bc_fun, fv, shape_nrs, cell_coords, time) + fill!(Kᶠ, 0) + fill!(fᶠ, 0) + # Support varying number of facetdofs (for ref shapes with different facet types) + for i in (length(shape_nrs) + 1):size(Kᶠ, 1) + Kᶠ[i, i] = 1 + end + integrate_weak_dbc!(Kᶠ, fᶠ, bc_fun, fv, shape_nrs, cell_coords, time) + aᶠ .= Kᶠ \ fᶠ # Could be done non-allocating if required, using e.g. SMatrix + return aᶠ +end + +function integrate_weak_dbc!(Kᶠ, fᶠ, bc_fun, fv, shape_nrs, cell_coords, time) + return integrate_weak_dbc!(function_space(fv), Kᶠ, fᶠ, bc_fun, fv, shape_nrs, cell_coords, time) +end + +function integrate_weak_dbc!(::Val{:Hdiv}, Kᶠ, fᶠ, bc_fun, fv, shape_nrs, cell_coords, time) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + x = spatial_coordinate(fv, q_point, cell_coords) + qn = bc_fun(x, time, n) + for (i, I) in enumerate(shape_nrs) + δN_dot_n = shape_value(fv, q_point, I) ⋅ n + fᶠ[i] += qn * δN_dot_n * dΓ + for (j, J) in enumerate(shape_nrs) + N_dot_n = shape_value(fv, q_point, J) ⋅ n + Kᶠ[i, j] += (δN_dot_n * N_dot_n) * dΓ + end + end + end + return +end + +function integrate_weak_dbc!(::Val{:Hcurl}, Kᶠ, fᶠ, bc_fun, fv, shape_nrs, cell_coords, time) + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + n = getnormal(fv, q_point) + x = spatial_coordinate(fv, q_point, cell_coords) + qt = bc_fun(x, time, n) + for (i, I) in enumerate(shape_nrs) + δN_cross_n = shape_value(fv, q_point, I) × n + fᶠ[i] += (δN_cross_n ⋅ qt) * dΓ + for (j, J) in enumerate(shape_nrs) + N_cross_n = shape_value(fv, q_point, J) × n + Kᶠ[i, j] += (δN_cross_n ⋅ N_cross_n) * dΓ + end + end + end + return +end + +function integrate_weak_dbc!(::Val{:H1}, Kᶠ, fᶠ, bc_fun, fv, shape_nrs, cell_coords, time) + ip_str = sprint(show, function_interpolation(fv)) + throw(ArgumentError("WeakDirichlet is not defined for H¹ function spaces ($ip_str)")) +end + +# Temp, put in interpolations.jl +function_space(::Interpolation) = Val(:H1) # Default fallback, should perhaps be explicit, but ok for now... +function_space(::RaviartThomas) = Val(:Hdiv) +function_space(::BrezziDouglasMarini) = Val(:Hdiv) +function_space(::Nedelec) = Val(:Hcurl) +# End of temp that should go in interpolations.jl +# Shortcut for fe values +function_space(fe_values::AbstractValues) = function_space(function_interpolation(fe_values)) diff --git a/src/Dofs/IntegrableDirichlet.jl b/src/Dofs/IntegrableDirichlet.jl deleted file mode 100644 index 2061c25d4d..0000000000 --- a/src/Dofs/IntegrableDirichlet.jl +++ /dev/null @@ -1,210 +0,0 @@ -@doc raw""" - IntegrableDirichlet(field_name::Symbol, facets, f::Function; qr_order = -1) - -An `IntegrableDirichlet` conditions enforces conditions for `field_name` on the boundary for -non-nodal interpolations (e.g. ``H(\mathrm{div})`` or ``H(\mathrm{curl})``) in an integral sense. - -For ``H(\mathrm{div})`` interpolations, we have conditions on the form -```math -\boldsymbol{N}^f_i a^f_i \cdot \boldsymbol{n}^f = q_n = f(\boldsymbol{x}, t) -``` -These are enforced as -```math -\int_{\Gamma^f} \boldsymbol{N}^f_\alpha a^f_\alpha \cdot \boldsymbol{n}^f\ \mathrm{d}\Gamma -= \int_{\Gamma^f} g_\alpha(\boldsymbol{x}) q_n\ \mathrm{d}\Gamma -``` -(no sum on ``\alpha``) where the weighting functions, ``g_\alpha(\boldsymbol{x})``, are defined such that -``\sum_{\alpha} g_\alpha(\boldsymbol{x}) = 1`` at all points on the facet. -These weighting functions are defined by each interpolation. - -For ``H(\mathrm{curl})`` interpolations, the conditions are on the form -```math -\boldsymbol{N}^f_i a^f_i \times \boldsymbol{n}^f = \boldsymbol{q}_t = \boldsymbol{f}(\boldsymbol{x}, t) -``` -These are similarly enforced as -```math -a^f_\alpha = -\frac{\int_{\Gamma^f} g_\alpha(\boldsymbol{x}) \boldsymbol{q}_t\ \mathrm{d}\Gamma}{ -\int_{\Gamma^f} \boldsymbol{N}^f_\alpha \times \boldsymbol{n}^f\ \mathrm{d}\Gamma} -``` -(no sum on ``\alpha``) with equivalent weighting functions as for ``H(\mathrm{div})`` interpolations. -""" -mutable struct IntegrableDirichlet - const f::Function - const facets::OrderedSet{FacetIndex} - const field_name::Symbol - const qr_order::Int - # Created during `add!` - fv::Union{Nothing, FacetValues} - facet_dofs::Union{Nothing, ArrayOfVectorViews{Int, 1}} - ip::Union{Nothing, Interpolation} -end -function IntegrableDirichlet(field_name::Symbol, facets::AbstractVecOrSet, f::Function; qr_order = -1) - return IntegrableDirichlet(f, convert_to_orderedset(facets), field_name, qr_order, nothing, nothing) -end - -function _default_bc_qr_order(user_provided::Int, ip::Interpolation) - user_provided > 0 && return user_provided - return _default_bc_qr_order(ip) -end -# Q&D default, should be more elaborated -_default_bc_qr_order(::Interpolation{<:Any, order}) where {order} = order - -function add!(ch::ConstraintHandler, dbc::IntegrableDirichlet) - # Duplicate the Dirichlet constraint for every SubDofHandler - dbc_added = false - for sdh in ch.dh.subdofhandlers - # Skip if the constrained field does not live on this sub domain - dbc.field_name in sdh.field_names || continue - # Compute the intersection between dbc.set and the cellset of this - # SubDofHandler and skip if the set is empty - filtered_set = filter_dbc_set(get_grid(ch.dh), sdh.cellset, dbc.facets) - isempty(filtered_set) && continue - # Fetch information about the field on this SubDofHandler - field_idx = find_field(sdh, dbc.field_name) - interpolation = getfieldinterpolation(sdh, field_idx) - CT = getcelltype(sdh) # Same celltype enforced in SubDofHandler constructor - qr_order = _default_qr_order(dbc.qr_order, interpolation) - fqr = FacetQuadratureRule{getrefshape(interpolation)}(qr_order) - fv = FacetValues(fqr, interpolation, geometric_interpolation(CT)) - local_facet_dofs, local_facet_dofs_offset = - _local_facet_dofs_for_bc(interpolation, 1, 1, field_offset(sdh, field_idx), dirichlet_facetdof_indices) - facet_dofs = ArrayOfVectorViews(local_facet_dofs, local_facet_dofs_offset, LinearIndices(1:(length(local_facet_dofs_offset) - 1))) - - filtered_dbc = IntegrableDirichlet(dbc.f, filtered_set, dbc.field_name, fv, facet_dofs) - - _add!(ch, filtered_dbc, facet_dofs) - - dbc_added = true - end - dbc_added || error("No overlap between dbc::Dirichlet and fields in the ConstraintHandler's DofHandler") - return ch -end - -function _add!(ch::ConstraintHandler, dbc::IntegrableDirichlet, facet_dofs) - # loop over all the faces in the set and add the global dofs to `constrained_dofs` - constrained_dofs = Int[] - cc = CellCache(ch.dh, UpdateFlags(; nodes = false, coords = false, dofs = true)) - for (cellidx, facetidx) in dbc.facets - reinit!(cc, cellidx) - local_dofs = facet_dofs[facetidx] - for d in local_dofs - push!(constrained_dofs, cc.dofs[d]) - end - end - - # save it to the ConstraintHandler - push!(ch.idbcs, dbc) - for d in constrained_dofs - add_prescribed_dof!(ch, d, NaN, nothing) - end - return ch -end - -_update!(ch.inhomogeneities, dbc.f, dbc.facets, dbc.ip, dbc.facet_dofs, ch.dh, ch.dofmapping, ch.dofcoefficients, time) - -function _update!( - inhomogeneities::Vector{T}, f::Function, facets::AbstractVecOrSet{FacetIndex}, fv::FacetValues, facet_dofs::ArrayOfVectorViews, - dh::AbstractDofHandler, dofmapping::Dict{Int, Int}, dofcoefficients::Vector{Union{Nothing, DofCoefficients{T}}}, time::Real - ) where {T} - ip = function_interpolation(fv) - for fc in FacetIterator(dh, facets) - reinit!(fv, fc) - local_dofs = facet_dofs[getcurrentfacet(fv)] - for (idof, shape_nr) in enumerate(dirichlet_facetdof_indices(ip)[getcurrentfacet(fv)]) - bc_value = _integrate_dbc(f, fv, shape_nr, idof, ip, getcoordinates(fc), time) - globaldof = celldofs(fc)[shape_nr] - dbc_index = dofmapping[globaldof] - # Only DBC dofs are currently update!-able so don't modify inhomogeneities - # for affine constraints - if dofcoefficients[dbc_index] === nothing - inhomogeneities[dbc_index] = bc_value - end - end - end - return nothing -end - -# Temp, put in interpolations.jl -""" -edge_parameterization(::Type{<:AbstractRefShape}, ξ, edge_id) - -An edge is parameterized by the normalized curve coordinate `s [0, 1]`, -increasing in the positive edge direction. -""" -function edge_parameterization(::Type{RefShape}, ξ, edge_id) where {RefShape <: Ferrite.AbstractRefShape} - ipg = Lagrange{RefShape, 1}() # Reference shape always described by 1st order Lagrange ip. - refcoords = Ferrite.reference_coordinates(ipg) - i1, i2 = Ferrite.edgedof_indices(ipg)[edge_id] - ξ1, ξ2 = (refcoords[i1], refcoords[i2]) - Δξ = ξ2 - ξ1 - L = norm(Δξ) - s = (ξ - ξ1) ⋅ normalize(Δξ) / L - @assert norm(ξ - ξ1) ≈ L * s # Ensure ξ is on the line ξ1 - ξ2 - @assert -eps(L) ≤ s ≤ (1 + eps(L)) # Ensure ξ is between ξ1 and ξ2 - return s -end - -function facet_parameterization(::Type{<:Ferrite.AbstractRefShape{3}}, ξ, facet_id) - # Not implemented (not yet defined in Ferrite what this should be), - # but to support testing interpolations with a single facedof interior index, - # we return `nothing` just to allow running the code as long as the output isn't used. - return nothing -end - -function facet_moment(ip::Interpolation{RS}, idof::Int, ξ::Vec, facet_nr) - s = facet_parameterization(RS, ξ, facet_nr) - return facet_moment(ip, idof, s) -end - -facet_moment(::RaviartThomas{2, RefTriangle, 1}, idof::Int, s::Real) = one(s) -facet_moment(::RaviartThomas{2, RefTriangle, 2}, idof::Int, s::Real) = (idof == 1 ? one(s) - s : s) -facet_moment(::BrezziDouglasMarini{2, RefTriangle, 1}, idof::Int, s::Real) = (idof == 1 ? one(s) - s : s) -facet_moment(::Nedelec{2, RefTriangle, 1}, idof::Int, s::Real) = one(s) -facet_moment(::Nedelec{2, RefTriangle, 2}, idof::Int, s::Real) = (idof == 1 ? one(s) - s : s) - -function_space(::Interpolation) = Val(:H1) # Default fallback, should perhaps be explicit... -function_space(::RaviartThomas) = Val(:Hdiv) -function_space(::BrezziDouglasMarini) = Val(:Hdiv) -function_space(::Nedelec) = Val(:Hcurl) -# End of temp that should go in interpolations.jl - -function _integrate_dbc(f::Function, args...) - return _integrate_dbc(function_space(ip), args...) -end - -function _integrate_dbc(::Val{:Hdiv}, f::Function, fv::FacetValues, shape_nr, idof, ip, cellcoords, time) - # Could speed up by having _integrate_facet take two functions... - f1(N, n, x, t, ξ) = facet_moment(ip, idof, ξ, getcurrentfacet(fv)) * f(x, t) - top = _integrate_facet(f1, fv, shape_nr, cellcoords, time) - f2(N, n, x, t, ξ) = N × n - bot = _integrate_facet(f2, fv, shape_nr, cellcoords, time) - return top / bot -end - -function _integrate_dbc(::Val{:Hcurl}, f::Function, fv::FacetValues, shape_nr, idof, ip, cellcoords, time) - # Could speed up by having _integrate_facet take two functions... - f1(N, n, x, t, ξ) = facet_moment(ip, idof, ξ, getcurrentfacet(fv)) * f(x, t) - top = _integrate_facet(f1, fv, shape_nr, cellcoords, time) - f2(N, n, x, t, ξ) = N ⋅ n - bot = _integrate_facet(f2, fv, shape_nr, cellcoords, time) - return top / bot -end - -_integrate_dbc(::Val{:H1}, args...) = ArgumentError("Dirichlet BC for H1 interpolations are not integrable") - -function _integrate_facet(f::F, fv::FacetValues, shape_nr, cellcoords, time) where {F} - function qp_contribution(q_point::Int) - x = spatial_coordinate(fv, q_point, cellcoords) - n = getnormal(fv, q_point) - dΓ = getdetJdV(fv, q_point) - N = shape_value(fv, q_point, shape_nr) - ξ = getpoints(fv.fqr, getcurrentfacet(fv))[q_point] - return f(N, n, x, time, ξ) * dΓ - end - retval = qp_contribution(1) - for q_point in 2:getnquadpoints(fv) - retval += qp_contribution(q_point) - end - return retval -end diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index 9f7583c1ab..b0ab88a933 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -111,7 +111,7 @@ function reinit!(cv::CellValues, cell::Union{AbstractCell, Nothing}, x::Abstract n_geom_basefuncs = getngeobasefunctions(geo_mapping) check_reinit_sdim_consistency(:CellValues, shape_gradient_type(cv), eltype(x)) - if cell === nothing && !isa(mapping_type(fun_values), IdentityMapping) + if cell === nothing && reinit_needs_cell(cv) throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) end if !checkbounds(Bool, x, 1:n_geom_basefuncs) || length(x) != n_geom_basefuncs diff --git a/src/FEValues/FacetValues.jl b/src/FEValues/FacetValues.jl index b6512c3b61..805f42273b 100644 --- a/src/FEValues/FacetValues.jl +++ b/src/FEValues/FacetValues.jl @@ -135,7 +135,7 @@ function reinit!(fv::FacetValues, cell::Union{AbstractCell, Nothing}, x::Abstrac geo_mapping = get_geo_mapping(fv) fun_values = get_fun_values(fv) - if cell === nothing && !isa(mapping_type(fun_values), IdentityMapping) + if cell === nothing && reinit_needs_cell(fv) throw(ArgumentError("The cell::AbstractCell input is required to reinit! non-identity function mappings")) end diff --git a/src/FEValues/common_values.jl b/src/FEValues/common_values.jl index 402f51cf41..08e8c82b8b 100644 --- a/src/FEValues/common_values.jl +++ b/src/FEValues/common_values.jl @@ -9,6 +9,13 @@ function checkquadpoint(fe_v::AbstractValues, qp::Int) return nothing end +@inline function reinit_needs_cell(fe_values::AbstractValues) + # TODO: Might need better logic for this, but for current implementations this + # is ok. If someone implements a non-identity mapping that doesn't require the cell + # as input, this is only a slight performance issue in some cases. + return !isa(mapping_type(get_fun_values(fe_values)), IdentityMapping) +end + @noinline function throw_incompatible_dof_length(length_ue, n_base_funcs) msg = "the number of base functions ($(n_base_funcs)) does not match the length " * "of the vector ($(length_ue)). Perhaps you passed the global vector, " * diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 9dd051c642..ef5f11c4fb 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -8,7 +8,7 @@ using Base: using EnumX: EnumX, @enumx using LinearAlgebra: - LinearAlgebra, Symmetric, cholesky, det, norm, pinv, tr + LinearAlgebra, Symmetric, cholesky, det, norm, normalize, pinv, tr using NearestNeighbors: NearestNeighbors, KDTree, knn using OrderedCollections: diff --git a/src/exports.jl b/src/exports.jl index 60a7a7cdbb..3684582c81 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -143,7 +143,7 @@ export # Constraints ConstraintHandler, Dirichlet, - IntegrableDirichlet, + WeakDirichlet, PeriodicDirichlet, collect_periodic_facets, collect_periodic_facets!, diff --git a/src/iterators.jl b/src/iterators.jl index 64b38f2e0e..9b0705bcd9 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -84,8 +84,20 @@ function reinit!(cc::CellCache, i::Int) end # reinit! FEValues with CellCache -reinit!(cv::CellValues, cc::CellCache) = reinit!(cv, getcells(cc.grid, cellid(cc)), cc.coords) -reinit!(fv::FacetValues, cc::CellCache, f::Int) = reinit!(fv, cc.coords, f) # TODO: Deprecate? +function reinit!(cv::CellValues, cc::CellCache) + return if reinit_needs_cell(cv) + reinit!(cv, getcells(cc.grid, cellid(cc)), cc.coords) + else + reinit!(cv, cc.coords) + end +end +function reinit!(fv::FacetValues, cc::CellCache, f::Int) + return if reinit_needs_cell(fv) + reinit!(fv, getcells(cc.grid, cellid(cc)), cc.coords, f) + else + reinit!(fv, cc.coords, f) + end +end getnodes(cc::CellCache) = cc.nodes getcoordinates(cc::CellCache) = cc.coords diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 65047ad375..776c6e7559 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,6 +1,6 @@ using Ferrite: reference_shape_value, reference_shape_gradient -@testset "interpolations" begin #= +@testset "interpolations" begin @testset "Value Type $value_type" for value_type in (Float32, Float64) @testset "Correctness of $interpolation" for interpolation in ( Lagrange{RefLine, 1}(), @@ -171,22 +171,22 @@ using Ferrite: reference_shape_value, reference_shape_gradient end # regression for https://github.com/Ferrite-FEM/Ferrite.jl/issues/520 - #=interpolation_type = typeof(interpolation).name.wrapper - if func_order > 1 && interpolation_type != Ferrite.Serendipity - first_order = interpolation_type{ref_shape,1}() - for (highorderface, firstorderface) in zip(Ferrite.facedof_indices(interpolation), Ferrite.facedof_indices(first_order)) - for (h_node, f_node) in zip(highorderface, firstorderface) - @test h_node == f_node - end - end - if ref_dim > 2 - for (highorderedge, firstorderedge) in zip(Ferrite.edgedof_indices(interpolation), Ferrite.edgedof_indices(first_order)) - for (h_node, f_node) in zip(highorderedge, firstorderedge) - @test h_node == f_node + interpolation_type = typeof(interpolation).name.wrapper + if func_order > 1 && interpolation_type != Ferrite.Serendipity + first_order = interpolation_type{ref_shape, 1}() + for (highorderface, firstorderface) in zip(Ferrite.facedof_indices(interpolation), Ferrite.facedof_indices(first_order)) + for (h_node, f_node) in zip(highorderface, firstorderface) + @test h_node == f_node + end + end + if ref_dim > 2 + for (highorderedge, firstorderedge) in zip(Ferrite.edgedof_indices(interpolation), Ferrite.edgedof_indices(first_order)) + for (h_node, f_node) in zip(highorderedge, firstorderedge) + @test h_node == f_node + end + end end end - end - end # VectorizedInterpolation v_interpolation_1 = interpolation^2 @@ -203,7 +203,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test @inferred(reference_shape_value(v_interpolation_1, x, dof)) isa Vec{2, value_type} @test @inferred(reference_shape_gradient(v_interpolation_3, x, dof)) isa Tensor{2, ref_dim, value_type} end - end # correctness testset =# + end # correctness testset @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefTriangle, 0}()) ≈ [Vec{2, Float64}((1 / 3, 1 / 3))] @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefQuadrilateral, 0}()) ≈ [Vec{2, Float64}((0, 0))] @@ -221,7 +221,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test Ferrite.is_discontinuous(ip_t) == false @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true - end # =# + end @testset "Correctness of AD of embedded interpolations" begin ip = Lagrange{RefHexahedron, 2}()^3 ξ = rand(Vec{3, Float64}) @@ -247,7 +247,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test g ≈ G[v_ind, :] @test v ≈ V[v_ind] end - end + end # reference_cell(::Type{RefTriangle}) = Triangle((1, 2, 3)) reference_cell(::Type{RefQuadrilateral}) = Quadrilateral((1, 2, 3, 4)) @@ -413,7 +413,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient # s is the path parameter ∈[0,1] along the positive direction of the path. # 2) Zero along other edges: Nⱼ ⋅ v = 0 if j∉𝔇 @testset "H(curl) on RefCell" begin - for ip in Hcurl_interpolations[1:1] + for ip in Hcurl_interpolations cell = reference_cell(getrefshape(ip)) geo_ip = geometric_interpolation(cell) ev = EdgeValues(EdgeQuadratureRule{getrefshape(ip)}(20), ip, geo_ip) @@ -584,76 +584,72 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end - @testset "Hdiv BC" begin + function _setup_dh_fv(ip; nel = 2, qr_order = 4) + RefShape = Ferrite.getrefshape(ip) + CT = typeof(reference_cell(RefShape)) + dim = Ferrite.getrefdim(CT) # dim=sdim=vdim + #grid = generate_grid(CT, ntuple(Returns(nel), dim), -0.25 * ones(Vec{dim}), 0.2 * ones(Vec{dim})) + grid = generate_grid(CT, ntuple(Returns(nel), dim)) + qr = FacetQuadratureRule{RefShape}(qr_order) + fv = FacetValues(qr, ip, geometric_interpolation(CT)) + dh = close!(add!(DofHandler(grid), :u, ip)) + return dh, fv + end + + function test_bc_integral(f_bc::Function, check_fun::Function, dh, facetset, fv; tol = 1.0e-6) + grid = Ferrite.get_grid(dh) + dbc = WeakDirichlet(:u, facetset, f_bc) + ch = close!(add!(ConstraintHandler(dh), dbc)) + a = zeros(ndofs(dh)) + apply!(a, ch) + test_val = zero(f_bc(get_node_coordinate(grid, 1), 0.0, getnormal(fv, 1))) + check_val = zero(check_fun(zero(Ferrite.shape_value_type(fv)), getnormal(fv, 1))) + @assert typeof(test_val) === typeof(check_val) + for (cellidx, facetidx) in facetset + cell_coords = getcoordinates(grid, cellidx) + reinit!(fv, getcells(grid, cellidx), cell_coords, facetidx) + ae = a[celldofs(dh, cellidx)] + for q_point in 1:getnquadpoints(fv) + dΓ = getdetJdV(fv, q_point) + u = function_value(fv, q_point, ae) + n = getnormal(fv, q_point) + x = spatial_coordinate(fv, q_point, cell_coords) + check_val += check_fun(u, n) * dΓ + test_val += f_bc(x, 0.0, n) * dΓ + end + end + @test norm(test_val - check_val) < tol + end + + @testset "H(div) BC" begin for ip in Hdiv_interpolations @testset "$ip" begin - RefShape = Ferrite.getrefshape(ip) - CT = typeof(reference_cell(RefShape)) - dim = Ferrite.getrefdim(CT) # dim=sdim=vdim - grid = generate_grid(CT, ntuple(Returns(2), dim), -0.25 * ones(Vec{dim}), 0.2 * ones(Vec{dim})) - qr = FacetQuadratureRule{RefShape}(4) - fv = FacetValues(qr, ip, geometric_interpolation(CT)) - dh = close!(add!(DofHandler(grid), :u, ip)) - for bval in (0.0,) # TODO: Currently only zero-valued BC supported - for (side, facetset) in grid.facetsets - a = zeros(ndofs(dh)) - ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, facetset, Returns(bval))) - close!(ch) - apply!(a, ch) - test_val = 0.0 - test_area = 0.0 - for (cellidx, facetidx) in facetset - reinit!(fv, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) - ae = a[celldofs(dh, cellidx)] - val = 0.0 - for q_point in 1:getnquadpoints(fv) - dΓ = getdetJdV(fv, q_point) - val += (function_value(fv, q_point, ae) ⋅ getnormal(fv, q_point)) * dΓ - test_area += dΓ - end - test_val += val + dh, fv = _setup_dh_fv(ip) + linear_x1(x, _, _) = x[1] + for f_bc in (Returns(0.0), Returns(1.0), linear_x1) + @testset "f_bc = $f_bc" begin + for facetset in values(dh.grid.facetsets) + test_bc_integral(f_bc, ⋅, dh, facetset, fv) end - @test abs(test_val - test_area * bval) < 1.0e-6 end end end end end - @testset "Hcurl BC" begin + @testset "H(curl) BC" begin for ip in Hcurl_interpolations @testset "$ip" begin - RefShape = Ferrite.getrefshape(ip) - CT = typeof(reference_cell(RefShape)) - dim = Ferrite.getrefdim(CT) # dim=sdim=vdim - grid = generate_grid(CT, ntuple(Returns(2), dim), -0.25 * ones(Vec{dim}), 0.2 * ones(Vec{dim})) - qr = EdgeQuadratureRule{RefShape}(4) - ev = EdgeValues(qr, ip, geometric_interpolation(CT)) - dh = close!(add!(DofHandler(grid), :u, ip)) - @assert dim == 2 # Only 2d supported right now... - for bval in (0.0,) # TODO: Currently only zero-valued BC supported - for (side, facetset) in grid.facetsets - set = OrderedSet(EdgeIndex(a, b) for (a, b) in facetset) - a = zeros(ndofs(dh)) - ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:u, getfacetset(grid, side), Returns(bval))) - close!(ch) - apply!(a, ch) - test_val = 0.0 - test_area = 0.0 - for (cellidx, facetidx) in getfacetset(grid, side) - reinit!(ev, getcells(grid, cellidx), getcoordinates(grid, cellidx), facetidx) - ae = a[celldofs(dh, cellidx)] - val = 0.0 - for q_point in 1:getnquadpoints(ev) - dΓ = getdetJdV(ev, q_point) - val += (function_value(ev, q_point, ae) ⋅ Ferrite.gettangent(ev, q_point)) * dΓ - test_area += dΓ - end - test_val += val + dh, fv = _setup_dh_fv(ip) + dim = Ferrite.getrefdim(getrefshape(ip)) + @assert dim == 2 # 3d not supported yet + v3 = rand() + linear_x1(x, _, _) = x[1] * Vec((0.0, 0.0, v3)) + for f_bc in (Returns(zero(Vec{3})), Returns(Vec((0.0, 0.0, rand()))), linear_x1) + @testset "f_bc = $f_bc" begin + for facetset in values(dh.grid.facetsets) + test_bc_integral(f_bc, ×, dh, facetset, fv) end - @test abs(test_val - test_area * bval) < 1.0e-6 end end end From 856774b4ebb7179b528a5007520adcf5f10c52cf Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sat, 18 Jan 2025 15:25:32 +0100 Subject: [PATCH 164/172] Add get_fun_values to cellvalues --- src/FEValues/CellValues.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/FEValues/CellValues.jl b/src/FEValues/CellValues.jl index b0ab88a933..11c9d0f2aa 100644 --- a/src/FEValues/CellValues.jl +++ b/src/FEValues/CellValues.jl @@ -78,6 +78,7 @@ getdetJdV(cv::CellValues, q_point::Int) = cv.detJdV[q_point] getdetJdV(::CellValues{<:Any, <:Any, <:Any, Nothing}, ::Int) = throw(ArgumentError("detJdV is not saved in CellValues")) # Accessors for function values +get_fun_values(cv::CellValues) = cv.fun_values getnbasefunctions(cv::CellValues) = getnbasefunctions(cv.fun_values) function_interpolation(cv::CellValues) = function_interpolation(cv.fun_values) function_difforder(cv::CellValues) = function_difforder(cv.fun_values) From f6d6c48f2c0a3a2c51f216f539c22a5d36eb7090 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sat, 18 Jan 2025 15:29:42 +0100 Subject: [PATCH 165/172] Remove unused explicit import of LinearAlgebra.normalize --- src/Ferrite.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ferrite.jl b/src/Ferrite.jl index ef5f11c4fb..9dd051c642 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -8,7 +8,7 @@ using Base: using EnumX: EnumX, @enumx using LinearAlgebra: - LinearAlgebra, Symmetric, cholesky, det, norm, normalize, pinv, tr + LinearAlgebra, Symmetric, cholesky, det, norm, pinv, tr using NearestNeighbors: NearestNeighbors, KDTree, knn using OrderedCollections: From 900b1d08d92573dfdb3abc34ac4e6b2486745029 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Sat, 18 Jan 2025 15:38:34 +0100 Subject: [PATCH 166/172] Remove explicit check for error msg --- test/test_quadrules.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_quadrules.jl b/test/test_quadrules.jl index c6bc68ea00..1bc9e40ece 100644 --- a/test/test_quadrules.jl +++ b/test/test_quadrules.jl @@ -131,7 +131,7 @@ using Ferrite: reference_shape_value @testset "$ref_cell unknown facet error path" begin for face in (-1, 0, 100) - err = ArgumentError("unknown facet number") + err = ArgumentError # remove string due to generalization ("unknown facet number") @test_throws err Ferrite.weighted_normal(Tensor{2, dim}(zeros(dim^2)), refshape, face) pt = Vec{dim - 1, Float64}(ntuple(i -> 0.0, dim - 1)) @test_throws err Ferrite.facet_to_element_transformation(pt, refshape, face) From b9dd06c7292e243cf11a36eee58968ece069c12a Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Mon, 20 Jan 2025 08:27:09 +0100 Subject: [PATCH 167/172] Progress on good, bad, and ugly --- docs/Manifest.toml | 5 +- .../maxwell_good_bad_ugly.jl | 224 +++++++++++++----- 2 files changed, 168 insertions(+), 61 deletions(-) diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 2a597842a1..e1c528def9 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -779,8 +779,7 @@ version = "1.0.0" [[deps.FerriteAssembly]] deps = ["Ferrite", "ForwardDiff", "MaterialModelsBase"] -git-tree-sha1 = "0a1071e3b1cb7dc08e10cb2ed4e3171fd500894a" -repo-rev = "main" +git-tree-sha1 = "98055718e3b739e6a1307bb1fe1854f1bf74c475" repo-url = "https://github.com/KnutAM/FerriteAssembly.jl" uuid = "fd21fc07-c509-4fe1-9468-19963fd5935d" version = "0.3.4" @@ -800,7 +799,6 @@ version = "0.2.0" [[deps.FerriteTriangulation]] deps = ["Ferrite", "Tensors"] git-tree-sha1 = "3b7f694d319bdbe4a98d7028a3758444a24eb868" -repo-rev = "main" repo-url = "https://github.com/KnutAM/FerriteTriangulation.jl" uuid = "b200c708-61b9-46d9-917a-515144ac7aac" version = "0.1.0" @@ -1678,7 +1676,6 @@ version = "0.1.2" [[deps.MaterialModelsBase]] deps = ["StaticArrays", "Tensors"] git-tree-sha1 = "1a3591c7472ec7690b5a6d3ced4504f4a7a314d3" -repo-rev = "main" repo-url = "https://github.com/KnutAM/MaterialModelsBase.jl" uuid = "af893363-701d-44dc-8b1e-d9a2c129bfc9" version = "0.2.2" diff --git a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl index f3437c3016..1a4d231872 100644 --- a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl +++ b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl @@ -1,4 +1,9 @@ #= + +# TODO +1) Evaluate triangulation for 2-field problem +2) + # Maxwell Discretizations: The Good, The Bad, and The Ugly This tutorial is based on Jay Gopalakrishnan (Portland State University) [Maxwell Discretizations: The Good, The Bad & The Ugly](https://web.pdx.edu/~gjay/pub/MaxwellGoodBadUgly.html) @@ -153,28 +158,32 @@ function setup_grid(h = 0.2; origin_refinement = 1) Gmsh.finalize() ## Add boundary parts - addfacetset!(grid, "vertical_facets", x -> abs((x[1] - 1) * x[1] * (x[1] + 1)) ≤ 1.0e-6) - addfacetset!(grid, "horizontal_facets", x -> abs((x[2] - 1) * x[2] * (x[2] + 1)) ≤ 1.0e-6) - + top = ExclusiveTopology(grid) + addboundaryfacetset!(grid, top, "vertical_facets", x -> abs((x[1] - 1) * x[1] * (x[1] + 1)) ≤ 1.0e-6) + addboundaryfacetset!(grid, top, "horizontal_facets", x -> abs((x[2] - 1) * x[2] * (x[2] + 1)) ≤ 1.0e-6) + bfacets = union(getfacetset(grid, "vertical_facets"), getfacetset(grid, "horizontal_facets")) + addfacetset!(grid, "boundary_facets", bfacets) return grid end # And prepare some functions to process and plot the data on the grid # by using `FerriteTriangulation.jl` -function _create_data!(f, data, grid, a, cvs, subtria::SubTriangulation) +function _create_data!(f, data, a, cvs, subtria::SubTriangulation, dr::UnitRange) + sdh = subtria.sdh + grid = sdh.dh.grid c1 = first(subtria.faces)[1] x = copy(getcoordinates(grid, c1)) - dofs = copy(celldofs(dh, c1)) + dofs = copy(celldofs(sdh, c1)) ae = zeros(eltype(a), length(dofs)) for (i, (cellnr, facenr)) in enumerate(subtria.faces) cv = cvs[facenr] getcoordinates!(x, grid, cellnr) reinit!(cv, getcells(grid, cellnr), x) - celldofs!(dofs, dh, cellnr) + celldofs!(dofs, sdh, cellnr) copyto!(ae, view(a, dofs)) node_idxs = subtria.face_nodes[i]:(subtria.face_nodes[i + 1] - 1) for q_point in 1:getnquadpoints(cv) - data[node_idxs[q_point]] = f(function_value(cv, q_point, ae)) + data[node_idxs[q_point]] = f(function_value(cv, q_point, ae, dr)) end end return @@ -186,19 +195,28 @@ end Create scalar data by evaluating `f(function_value(...))` at each triangulation node in the `grid`. """ -function create_data(tr::Triangulation, grid, a, ips; f = identity) +function create_data(tr::Triangulation, fieldname::Symbol, a; f = identity) data = zeros(length(tr.nodes)) - for (ip, subtria) in zip(ips, tr.sub_triangulation) + dh = first(tr.sub_triangulation).sdh.dh + if length(a) != ndofs(dh) + display(dh) + println((dh = ndofs(dh), a = length(a))) + error("dof vector length not matching number of dofs in triangulation dh") + end + + for subtria in tr.sub_triangulation + sdh = subtria.sdh + ip = Ferrite.getfieldinterpolation(sdh, fieldname) cvs = [CellValues(cr, ip, geometric_interpolation(getcelltype(subtria.sdh))) for cr in subtria.rules] - _create_data!(f, data, grid, a, cvs, subtria) + _create_data!(f, data, a, cvs, subtria, dof_range(sdh, fieldname)) end return data end -grid = setup_grid(0.00390625; origin_refinement = 1) +#grid = setup_grid(0.00390625; origin_refinement = 1) +grid = setup_grid(0.01; origin_refinement = 1) -ip = DiscontinuousLagrange{RefTriangle, 1}()^2 -dh = close!(add!(DofHandler(grid), :u, ip)) +dh_ana = close!(add!(DofHandler(grid), :u, DiscontinuousLagrange{RefTriangle, 1}()^2)) function analytical_potential(x::Vec{2}) # Analytical potential to be differentiated Δθ = -3π / 4 # Rotate discontinuous line to 4th quadrant @@ -207,25 +225,12 @@ function analytical_potential(x::Vec{2}) # Analytical potential to be differenti θ = r ≤ 1.0e-6 ? zero(eltype(x)) : (atan(xp[2], xp[1]) - Δθ) return r^(2 // 3) * sin(2θ / 3) end +analytical_solution(x::Vec{2}) = gradient(analytical_potential, x) -a = zeros(ndofs(dh)) - -apply_analytical!(a, dh, :u, x -> gradient(analytical_potential, x)) +a_ana = zeros(ndofs(dh_ana)) -tr = Triangulation(dh, 2) -data = create_data(tr, grid, a, (ip,); f = first) +apply_analytical!(a_ana, dh_ana, :u, analytical_solution) -fig = Plt.Figure() -ax = Plt.Axis(fig[1, 1]; aspect = Plt.DataAspect()) - -nodes = [GB.Point(x.data) for x in tr.nodes] -m = Plt.mesh!( - ax, nodes, reshape(tr.triangles, :); color = data, - colormap = Plt.Makie.wong_colors(), - interpolate = true, - colorrange = (-2.0, 0.0), -) -Plt.Colorbar(fig[1, 2], m) #= ```julia for i in 2:length(tr.tri_edges) @@ -241,7 +246,7 @@ mutable struct L2Error{F} const exact_fun::F end -function FerriteAssembly.integrate_cell!(vals::L2Error{F}, state, ae, material, cv, cellbuffer) where {F} +function FerriteAssembly.integrate_cell!(vals::L2Error{F}, state, ae, material, cv::CellValues, cellbuffer) where {F} for q_point in 1:getnquadpoints(cv) Eh = function_value(cv, q_point, ae) x = spatial_coordinate(cv, q_point, getcoordinates(cellbuffer)) @@ -251,6 +256,16 @@ function FerriteAssembly.integrate_cell!(vals::L2Error{F}, state, ae, material, end return end +function FerriteAssembly.integrate_cell!(vals::L2Error{F}, state, ae, material, cv::NamedTuple, cellbuffer) where {F} + for q_point in 1:getnquadpoints(cv.E) + Eh = function_value(cv.E, q_point, ae, dof_range(cellbuffer, :E)) + x = spatial_coordinate(cv.E, q_point, getcoordinates(cellbuffer)) + dΩ = getdetJdV(cv.E, q_point) + vals.l2error += norm(Eh - vals.exact_fun(x))^2 * dΩ + vals.volume += dΩ + end + return +end # ## Lagrange solution struct LagrangeMaterial end @@ -270,11 +285,15 @@ function FerriteAssembly.element_routine!(Ke, re, s, ae, ::LagrangeMaterial, cv, return end -function solve_lagrange(dh, ip) +function solve_lagrange(dh) + ip = Ferrite.getfieldinterpolation(dh, Ferrite.find_field(dh, :E)) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:E, getfacetset(grid, "horizontal_facets"), (x, _) -> gradient(analytical_potential, x)[2], [2])) add!(ch, Dirichlet(:E, getfacetset(grid, "vertical_facets"), (x, _) -> gradient(analytical_potential, x)[1], [1])) close!(ch) + VTKGridFile("dbc", dh) do vtk + Ferrite.write_constraints(vtk, ch) + end cv = CellValues(QuadratureRule{RefTriangle}(1), ip) K = allocate_matrix(dh) @@ -284,46 +303,137 @@ function solve_lagrange(dh, ip) work!(as, db) apply!(K, f, ch) a = K \ f - l2_vals = L2Error(0.0, 0.0, x -> gradient(analytical_potential, x)) + l2_vals = L2Error(0.0, 0.0, analytical_solution) work!(Integrator(l2_vals), db; a) return a, sqrt(l2_vals.l2error) / l2_vals.volume end -ip = Lagrange{RefTriangle, 1}()^2 -dh = close!(add!(DofHandler(grid), :E, ip)) -a_lagrange, e_lagrange = solve_lagrange(dh, ip) +dh_lagrange = close!(add!(DofHandler(grid), :E, Lagrange{RefTriangle, 1}()^2)) +a_lagrange, e_lagrange = solve_lagrange(dh_lagrange) function lagrange_error(h::Float64) grid = setup_grid(h; origin_refinement = 1) ip = Lagrange{RefTriangle, 1}()^2 dh = close!(add!(DofHandler(grid), :E, ip)) - _, e = solve_lagrange(dh, ip) + _, e = solve_lagrange(dh) return e end -#= -```julia -mesh_sizes = (1/2) .^(3:8) -lagrange_errors = Float64[] -for h in mesh_sizes - println("h = $h") - e = @time lagrange_error(h) - push!(lagrange_errors, e) +# ## Nedelec solution +struct NedelecMaterial end +function FerriteAssembly.element_residual!(re, s, ae, ::NedelecMaterial, cv, cellbuffer) + for q_point in 1:getnquadpoints(cv.E) + dΩ = getdetJdV(cv.E, q_point) + E = function_value(cv.E, q_point, ae, dof_range(cellbuffer, :E)) + curlE = function_curl(cv.E, q_point, ae, dof_range(cellbuffer, :E)) + ∇ϕ = function_gradient(cv.ϕ, q_point, ae, dof_range(cellbuffer, :ϕ)) + for (i, I) in pairs(dof_range(cellbuffer, :E)) + δNE = shape_value(cv.E, q_point, i) + curl_δNE = shape_curl(cv.E, q_point, i) + re[I] += (curl_δNE ⋅ curlE + δNE ⋅ ∇ϕ) * dΩ + end + for (i, I) in pairs(dof_range(cellbuffer, :ϕ)) + gradδNϕ = shape_gradient(cv.ϕ, q_point, i) + re[I] += (gradδNϕ ⋅ E) * dΩ + end + end + return +end + +function solve_nedelec(dh) + ipE = Ferrite.getfieldinterpolation(dh, Ferrite.find_field(dh, :E)) + ipϕ = Ferrite.getfieldinterpolation(dh, Ferrite.find_field(dh, :ϕ)) + CT = getcelltype(dh.grid) + + ch = ConstraintHandler(dh) + add!(ch, WeakDirichlet(:E, getfacetset(grid, "boundary_facets"), (x, _, n) -> analytical_solution(x) × n)) + add!(ch, Dirichlet(:ϕ, getfacetset(grid, "boundary_facets"), Returns(0.0))) + close!(ch) + + qr = QuadratureRule{RefTriangle}(1) + ipg = geometric_interpolation(CT) + cv = (E = CellValues(qr, ipE, ipg), ϕ = CellValues(qr, ipϕ, ipg)) + K = allocate_matrix(dh) + f = zeros(ndofs(dh)) + db = setup_domainbuffer(DomainSpec(dh, NedelecMaterial(), cv)) + as = start_assemble(K, f) + a = zeros(ndofs(dh)) + work!(as, db; a) + apply!(K, f, ch) + a .= K \ f + l2_vals = L2Error(0.0, 0.0, analytical_solution) + work!(Integrator(l2_vals), db; a) + return a, sqrt(l2_vals.l2error) / l2_vals.volume +end + +ipE = Nedelec{2, RefTriangle, 1}() +ipϕ = Lagrange{RefTriangle, 1}() +dh_nedelec = DofHandler(grid) +add!(dh_nedelec, :E, ipE) +add!(dh_nedelec, :ϕ, ipϕ) +close!(dh_nedelec) + +a_nedelec, e_nedelec = solve_nedelec(dh_nedelec) + +function nedelec_error(h::Float64) + grid = setup_grid(h; origin_refinement = 1) + ipE = Nedelec{2, RefTriangle, 1}() + ipϕ = Lagrange{RefTriangle, 1}() + dh = DofHandler(grid) + add!(dh, :E, ipE) + add!(dh, :ϕ, ipϕ) + close!(dh) + _, e = solve_nedelec(dh) + return e +end + +function calculate_errors(mesh_sizes) + lagrange_errors = zeros(length(mesh_sizes)) + nedelec_errors = similar(lagrange_errors) + for (i, h) in enumerate(mesh_sizes) + println("h = $h") + lagrange_errors[i] = @time lagrange_error(h) + nedelec_errors[i] = @time nedelec_error(h) + end + return lagrange_errors, nedelec_error +end + + +mesh_sizes = (1 / 2) .^ (3:3) +lagrange_errors, nedelec_errors = calculate_errors(mesh_sizes) + + +function plot_field(fig_part, dh, fieldname, dofvec; plot_edges = false, meshkwargs...) + tr = Triangulation(dh, 2) + data = create_data(tr, fieldname, dofvec; f = (x -> x[1])) + + ax = Plt.Axis(fig_part; aspect = Plt.DataAspect()) + + nodes = [GB.Point(x.data) for x in tr.nodes] + m = Plt.mesh!( + ax, nodes, reshape(tr.triangles, :); color = data, + colormap = Plt.Makie.wong_colors(), + interpolate = true, + meshkwargs... + ) + if plot_edges + for i in 2:length(tr.tri_edges) + Plt.lines!(ax, view(nodes, view(tr.edges, tr.tri_edges[i - 1]:(tr.tri_edges[i] - 1))); color = :black) + end + end + return m end -``` -=# -tr = Triangulation(dh, 2) -data = create_data(tr, grid, a_lagrange, (ip,); f = first) +fig = Plt.Figure(size = (1600, 500)) +m_ana = plot_field(fig[1, 1], dh_ana, :u, a_ana; plot_edges = false, colorrange = (-2, 0)) +Plt.Colorbar(fig[1, 2], m_ana) +m_lag = plot_field(fig[1, 3], dh_lagrange, :E, a_lagrange; plot_edges = false, colorrange = (-2, 0)) +Plt.Colorbar(fig[1, 4], m_lag) +m_ned = plot_field(fig[1, 5], dh_nedelec, :E, a_nedelec; plot_edges = false, colorrange = (-2, 0)) +Plt.Colorbar(fig[1, 6], m_ned) -ax = Plt.Axis(fig[2, 1]; aspect = Plt.DataAspect()) +ax = Plt.Axis(fig[2, 1]) +Plt.lines!(ax, mesh_sizes, lagrange_errors; label = "Lagrange") +Plt.lines!(ax, mesh_sizes, nedelec_errors; label = "Nedelec") -nodes = [GB.Point(x.data) for x in tr.nodes] -m = Plt.mesh!( - ax, nodes, reshape(tr.triangles, :); color = data, - colormap = Plt.Makie.wong_colors(), - interpolate = true, - colorrange = (-2.0, 0.0), -) -Plt.Colorbar(fig[2, 2], m) fig From 28229458a3e6ba90b9f47871e6051ed33f199f55 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 21 Jan 2025 21:48:23 +0100 Subject: [PATCH 168/172] =?UTF-8?q?Working=20maxwell=20tutorial=20=20?= =?UTF-8?q?=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Documentation.yml | 2 +- docs/Manifest.toml | 3 +- docs/Project.toml | 1 - docs/make.jl | 1 - docs/src/literate-tutorials/maxwell.jl | 97 --------------- docs/src/literate-tutorials/maxwell.png | Bin 0 -> 326414 bytes .../maxwell_good_bad_ugly.jl | 112 ++++++++++-------- 7 files changed, 67 insertions(+), 149 deletions(-) delete mode 100644 docs/src/literate-tutorials/maxwell.jl create mode 100644 docs/src/literate-tutorials/maxwell.png diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index b29323df48..bfdf1de2cd 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -16,7 +16,7 @@ jobs: version: '1.11' - uses: julia-actions/cache@v2 - name: Install dependencies - run: julia --project=docs -e 'using Pkg; Pkg.instantiate(); Pkg.precompile()' + run: julia --project=docs -e 'using Pkg; Pkg.dev(;url = "https://github.com/KnutAM/FerriteAssembly.jl"); Pkg.instantiate(); Pkg.precompile()' - name: Build and deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/Manifest.toml b/docs/Manifest.toml index e1c528def9..41f20cab09 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -779,8 +779,7 @@ version = "1.0.0" [[deps.FerriteAssembly]] deps = ["Ferrite", "ForwardDiff", "MaterialModelsBase"] -git-tree-sha1 = "98055718e3b739e6a1307bb1fe1854f1bf74c475" -repo-url = "https://github.com/KnutAM/FerriteAssembly.jl" +path = "/Users/knutan/.julia/dev/FerriteAssembly" uuid = "fd21fc07-c509-4fe1-9468-19963fd5935d" version = "0.3.4" diff --git a/docs/Project.toml b/docs/Project.toml index 74b05da123..26c9b46207 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -34,5 +34,4 @@ WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" [sources] MaterialModelsBase = {url = "https://github.com/KnutAM/MaterialModelsBase.jl"} -FerriteAssembly = {url = "https://github.com/KnutAM/FerriteAssembly.jl"} FerriteTriangulation = {url = "https://github.com/KnutAM/FerriteTriangulation.jl"} diff --git a/docs/make.jl b/docs/make.jl index b9ec9c3f8b..2642e4fc38 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -68,7 +68,6 @@ bibtex_plugin = CitationBibliography( "tutorials/reactive_surface.md", "tutorials/linear_shell.md", "tutorials/dg_heat_equation.md", - "tutorials/maxwell.md", "tutorials/maxwell_good_bad_ugly.md", ], "Topic guides" => [ diff --git a/docs/src/literate-tutorials/maxwell.jl b/docs/src/literate-tutorials/maxwell.jl deleted file mode 100644 index c429e146d9..0000000000 --- a/docs/src/literate-tutorials/maxwell.jl +++ /dev/null @@ -1,97 +0,0 @@ -#= -# Solving Maxwell's equations -```math -\begin{align*} -\nabla \cdot \boldsymbol{D} &= \rho_\mathrm{f} \\ -\nabla \times \boldsymbol{H} &= \boldsymbol{J}_\mathrm{f} + \frac{\partial \boldsymbol{D}}{\partial t} \\ -\nabla \cdot \boldsymbol{B} &= 0 \\ -\nabla \times \boldsymbol{E} &= -\frac{\partial \boldsymbol{B}}{\partial t} -\end{align*} -``` -=# - -#= -**Not working:** Maybe try the standard laplace eigenvalue problem -[https://www.cambridge.org/core/services/aop-cambridge-core/content/view/4BD87CC520C7E11CF402981AA58D77E2/S0962492910000012a.pdf/finite-element-approximation-of-eigenvalue-problems.pdf](https://www.cambridge.org/core/services/aop-cambridge-core/content/view/4BD87CC520C7E11CF402981AA58D77E2/S0962492910000012a.pdf/finite-element-approximation-of-eigenvalue-problems.pdf) - -# Maxwell eigenvalue problem -Strong form -```math -\begin{align*} -\mathrm{curl}(\mathrm{curl}(\boldsymbol{u})) &= \lambda \boldsymbol{u} \text{ in } \Omega \\ -\boldsymbol{u} \times \boldsymbol{n} &= \boldsymbol{0} \text{ on } \partial\Omega -\end{align*} -``` -Weak form -```math -\int_\Omega \mathrm{curl}(\boldsymbol{\delta u}) \cdot \mathrm{curl}(\boldsymbol{u})\ \mathrm{d}\Omega = \lambda \int_\Omega \boldsymbol{\delta u} \cdot \boldsymbol{u}\ \mathrm{d}\Omega \quad \forall\ \boldsymbol{\delta u} \in H_0(\text{curl}) -``` -Finite element formulation -```math -\underbrace{\int_\Omega \mathrm{curl}(\boldsymbol{\delta N}_i) \cdot \mathrm{curl}(\boldsymbol{N_j})\ \mathrm{d}\Omega}_{A_{ij}}\ x_j = \lambda \underbrace{\int_\Omega \boldsymbol{\delta N_i} \cdot \boldsymbol{N_j}\ \mathrm{d}\Omega}_{B_{ij}}\ x_j -``` -=# - -# ## Implementation -using Ferrite, Tensors, KrylovKit -using Arpack: Arpack -# ### Element routine -function element_routine!(Ae, Be, cv::CellValues) - for q_point in 1:getnquadpoints(cv) - dΩ = getdetJdV(cv, q_point) - for i in 1:getnbasefunctions(cv) - δN = shape_value(cv, q_point, i) - curl_δN = shape_curl(cv, q_point, i) - for j in 1:getnbasefunctions(cv) - N = shape_value(cv, q_point, j) - curl_N = shape_curl(cv, q_point, j) - Ae[i, j] = (curl_δN ⋅ curl_N) * dΩ - Be[i, j] = (δN ⋅ N) * dΩ - end - end - end - return -end - -# ### FE setup -function doassemble!(A, B, dh, cv) - n = ndofs_per_cell(dh) - Ae = zeros(n, n) - Be = zeros(n, n) - a_assem, b_assem = start_assemble.((A, B)) - for cc in CellIterator(dh) - cell = getcells(dh.grid, cellid(cc)) - reinit!(cv, cell, getcoordinates(cc)) - element_routine!(Ae, Be, cv) - assemble!(a_assem, celldofs(cc), Ae) - assemble!(b_assem, celldofs(cc), Be) - end - return A, B -end - -function setup_and_assemble(ip::VectorInterpolation{2, RefTriangle}) - grid = generate_grid(Triangle, (40, 40), zero(Vec{2}), π * ones(Vec{2})) - cv = CellValues(QuadratureRule{RefTriangle}(2), ip, geometric_interpolation(Triangle)) - dh = close!(add!(DofHandler(grid), :u, ip)) - ∂Ω = union((getfacetset(grid, k) for k in ("left", "top", "right", "bottom"))...) - dbc = Dirichlet(:u, ∂Ω, ip isa VectorizedInterpolation ? Returns([0.0, 0.0]) : Returns(0.0)) - ch = close!(add!(ConstraintHandler(dh), dbc)) - sp = init_sparsity_pattern(dh) - add_sparsity_entries!(sp, dh) - A = allocate_matrix(sp) - B = allocate_matrix(sp) - doassemble!(A, B, dh, cv) - #Ferrite.zero_out_rows!(B, ch.dofmapping) - #Ferrite.zero_out_columns!(B, ch.prescribed_dofs) - fdofs = ch.free_dofs - return A, B, dh, fdofs -end - -ip = Nedelec{2, RefTriangle, 1}() -#ip = Lagrange{RefTriangle, 1}()^2 - -A, B, dh, fdofs = setup_and_assemble(ip) -Aff = A[fdofs, fdofs] -Bff = B[fdofs, fdofs] -# vals, vecs, info = geneigsolve((Aff, Bff), 10, EigSorter(x -> abs(x - 5.0)); maxiter = 1000); -# λ, ϕ = Arpack.eigs(Aff, Bff, nev = 2, sigma=5.5); diff --git a/docs/src/literate-tutorials/maxwell.png b/docs/src/literate-tutorials/maxwell.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0a8df8b1a2eabe3189b4100c905a9639d75310 GIT binary patch literal 326414 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%0A)ib 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

T)sIz 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 Biy;62 literal 0 HcmV?d00001 diff --git a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl index 1a4d231872..19638198cd 100644 --- a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl +++ b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl @@ -1,9 +1,4 @@ #= - -# TODO -1) Evaluate triangulation for 2-field problem -2) - # Maxwell Discretizations: The Good, The Bad, and The Ugly This tutorial is based on Jay Gopalakrishnan (Portland State University) [Maxwell Discretizations: The Good, The Bad & The Ugly](https://web.pdx.edu/~gjay/pub/MaxwellGoodBadUgly.html) @@ -12,6 +7,12 @@ from the graduate course MTH 653: Advanced Numerical Analysis (Spring 2019) The purpose of the tutorial is to demonstrate how `Nedelec` vector interpolations will converge to the correct solution for a Maxwell problem, when vectorized `Lagrange` interpolations converge to an incorrect solution. +![Results for different discretizations](maxwell.png) + +**Figure 1**: The results of this tutorial, showing how the analytical solution is not found when discretizing +the problem using `Lagrange` interpolations, but when using a `Nedelec` interpolation, we converge to the +analytical solution as the mesh size, ``h``, decreases. + ## Loading packages We start by adding the required packages for this tutorial =# @@ -92,6 +93,13 @@ Finally, due to the singularity, the components of ``\boldsymbol{E}_\mathrm{exac ``H^1(\Omega)``. **TODO:** *Explain why ``\mathrm{div}(\boldsymbol{E})=0`` is fullfilled, use divergence theorem?* +To evaluate the accuracy of the different discretizations, we will use the analytical solution to +evaluate the boundary condition, +``g = \boldsymbol{E}_\mathrm{exact}\cdot \boldsymbol{t} \quad \text{on }\Gamma``. A correct +discretization should then reproduce +``\boldsymbol{E}_\mathrm{exact}(\boldsymbol{x})\text{ in }\Omega``, +as ``\boldsymbol{E}_\mathrm{exact}`` fulfills the PDE exactly, as well as its boundary conditions. + ## Lagrange interpolation Following the notes in the linked example, the lagrange problem becomes to solve ```math @@ -213,8 +221,9 @@ function create_data(tr::Triangulation, fieldname::Symbol, a; f = identity) return data end -#grid = setup_grid(0.00390625; origin_refinement = 1) -grid = setup_grid(0.01; origin_refinement = 1) +# ## Analytical implementation +mesh_size = 0.01 +grid = setup_grid(mesh_size; origin_refinement = 1) dh_ana = close!(add!(DofHandler(grid), :u, DiscontinuousLagrange{RefTriangle, 1}()^2)) @@ -230,16 +239,17 @@ analytical_solution(x::Vec{2}) = gradient(analytical_potential, x) a_ana = zeros(ndofs(dh_ana)) apply_analytical!(a_ana, dh_ana, :u, analytical_solution) - #= -```julia -for i in 2:length(tr.tri_edges) - Plt.lines!(view(nodes, view(tr.edges, tr.tri_edges[i-1]:(tr.tri_edges[i]-1))); color=:black) -end +## Error calculation +We will calculate the error, ``e(h)``, between the analytical, +``\boldsymbol{E}_\mathrm{exact}``, and numerical, +``\boldsymbol{E}_\mathrm{h}(h)``, solutions as integral norm, +```math +e(h) = \frac{1}{V}\int_\Omega ||\boldsymbol{E}_\mathrm{h}(h) - \boldsymbol{E}_\mathrm{exact}||^2\ \mathrm{d}\Omega, +\quad V = \int_\Omega \mathrm{d}\Omega ``` +where ``h`` is a measure of the element size. =# - -# Error calculator mutable struct L2Error{F} l2error::Float64 volume::Float64 @@ -288,12 +298,9 @@ end function solve_lagrange(dh) ip = Ferrite.getfieldinterpolation(dh, Ferrite.find_field(dh, :E)) ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:E, getfacetset(grid, "horizontal_facets"), (x, _) -> gradient(analytical_potential, x)[2], [2])) - add!(ch, Dirichlet(:E, getfacetset(grid, "vertical_facets"), (x, _) -> gradient(analytical_potential, x)[1], [1])) + add!(ch, Dirichlet(:E, getfacetset(dh.grid, "horizontal_facets"), (x, _) -> gradient(analytical_potential, x)[2], [2])) + add!(ch, Dirichlet(:E, getfacetset(dh.grid, "vertical_facets"), (x, _) -> gradient(analytical_potential, x)[1], [1])) close!(ch) - VTKGridFile("dbc", dh) do vtk - Ferrite.write_constraints(vtk, ch) - end cv = CellValues(QuadratureRule{RefTriangle}(1), ip) K = allocate_matrix(dh) @@ -311,8 +318,7 @@ end dh_lagrange = close!(add!(DofHandler(grid), :E, Lagrange{RefTriangle, 1}()^2)) a_lagrange, e_lagrange = solve_lagrange(dh_lagrange) -function lagrange_error(h::Float64) - grid = setup_grid(h; origin_refinement = 1) +function lagrange_error(grid) ip = Lagrange{RefTriangle, 1}()^2 dh = close!(add!(DofHandler(grid), :E, ip)) _, e = solve_lagrange(dh) @@ -346,8 +352,8 @@ function solve_nedelec(dh) CT = getcelltype(dh.grid) ch = ConstraintHandler(dh) - add!(ch, WeakDirichlet(:E, getfacetset(grid, "boundary_facets"), (x, _, n) -> analytical_solution(x) × n)) - add!(ch, Dirichlet(:ϕ, getfacetset(grid, "boundary_facets"), Returns(0.0))) + add!(ch, WeakDirichlet(:E, getfacetset(dh.grid, "boundary_facets"), (x, _, n) -> analytical_solution(x) × n)) + add!(ch, Dirichlet(:ϕ, getfacetset(dh.grid, "boundary_facets"), Returns(0.0))) close!(ch) qr = QuadratureRule{RefTriangle}(1) @@ -355,7 +361,7 @@ function solve_nedelec(dh) cv = (E = CellValues(qr, ipE, ipg), ϕ = CellValues(qr, ipϕ, ipg)) K = allocate_matrix(dh) f = zeros(ndofs(dh)) - db = setup_domainbuffer(DomainSpec(dh, NedelecMaterial(), cv)) + db = setup_domainbuffer(DomainSpec(dh, NedelecMaterial(), cv); autodiffbuffer = true) as = start_assemble(K, f) a = zeros(ndofs(dh)) work!(as, db; a) @@ -375,8 +381,7 @@ close!(dh_nedelec) a_nedelec, e_nedelec = solve_nedelec(dh_nedelec) -function nedelec_error(h::Float64) - grid = setup_grid(h; origin_refinement = 1) +function nedelec_error(grid) ipE = Nedelec{2, RefTriangle, 1}() ipϕ = Lagrange{RefTriangle, 1}() dh = DofHandler(grid) @@ -391,23 +396,31 @@ function calculate_errors(mesh_sizes) lagrange_errors = zeros(length(mesh_sizes)) nedelec_errors = similar(lagrange_errors) for (i, h) in enumerate(mesh_sizes) - println("h = $h") - lagrange_errors[i] = @time lagrange_error(h) - nedelec_errors[i] = @time nedelec_error(h) + grid = setup_grid(h; origin_refinement = 1) + lagrange_errors[i] = lagrange_error(grid) + nedelec_errors[i] = nedelec_error(grid) end - return lagrange_errors, nedelec_error + return lagrange_errors, nedelec_errors end - -mesh_sizes = (1 / 2) .^ (3:3) +mesh_sizes = 0.1 * ((1 / 2) .^ (0:5)) +lagrange_errors = [0.10849541129807588, 0.09262531863237256, 0.08372918040381809, 0.07939314534131932, 0.07639600032795617, 0.07579072269391056] #hide +nedelec_errors = [0.02246208564658041, 0.014303181582571767, 0.00906062850047998, 0.0057408487615066535, 0.003616459776120822, 0.0022876386764458592] #hide +#= +```julia lagrange_errors, nedelec_errors = calculate_errors(mesh_sizes) +``` +=# +using Test #src +for i in 1:2 #src + @test [lagrange_errors[i], nedelec_errors[i]] ≈ collect(map(first, calculate_errors(mesh_sizes[i]))) #src +end #src - -function plot_field(fig_part, dh, fieldname, dofvec; plot_edges = false, meshkwargs...) +function plot_field(fig_part, dh, fieldname, dofvec, name::String; plot_edges = false, meshkwargs...) tr = Triangulation(dh, 2) data = create_data(tr, fieldname, dofvec; f = (x -> x[1])) - ax = Plt.Axis(fig_part; aspect = Plt.DataAspect()) + ax = Plt.Axis(fig_part; aspect = Plt.DataAspect(), xlabel = "x₁", ylabel = "x₂", title = name) nodes = [GB.Point(x.data) for x in tr.nodes] m = Plt.mesh!( @@ -424,16 +437,21 @@ function plot_field(fig_part, dh, fieldname, dofvec; plot_edges = false, meshkwa return m end -fig = Plt.Figure(size = (1600, 500)) -m_ana = plot_field(fig[1, 1], dh_ana, :u, a_ana; plot_edges = false, colorrange = (-2, 0)) -Plt.Colorbar(fig[1, 2], m_ana) -m_lag = plot_field(fig[1, 3], dh_lagrange, :E, a_lagrange; plot_edges = false, colorrange = (-2, 0)) -Plt.Colorbar(fig[1, 4], m_lag) -m_ned = plot_field(fig[1, 5], dh_nedelec, :E, a_nedelec; plot_edges = false, colorrange = (-2, 0)) -Plt.Colorbar(fig[1, 6], m_ned) - -ax = Plt.Axis(fig[2, 1]) -Plt.lines!(ax, mesh_sizes, lagrange_errors; label = "Lagrange") -Plt.lines!(ax, mesh_sizes, nedelec_errors; label = "Nedelec") +fig = let + fig = Plt.Figure(size = (1000, 600)) + m_ana = plot_field(fig[1, 1], dh_ana, :u, a_ana, "Analytical"; plot_edges = false, colorrange = (-2, 0)) + m_lag = plot_field(fig[1, 2], dh_lagrange, :E, a_lagrange, "Lagrange (h = $mesh_size)"; plot_edges = false, colorrange = (-2, 0)) + m_ned = plot_field(fig[1, 3], dh_nedelec, :E, a_nedelec, "Nedelec (h = $mesh_size)"; plot_edges = false, colorrange = (-2, 0)) + Plt.Colorbar(fig[1, 4], m_ned; label = "E₁") + + ax = Plt.Axis( + fig[2, 1:2]; + xscale = log10, yscale = log10, + xlabel = "mesh size, h", ylabel = "error" + ) + Plt.lines!(ax, mesh_sizes, lagrange_errors; label = "Lagrange") + Plt.lines!(ax, mesh_sizes, nedelec_errors; label = "Nedelec") + Plt.axislegend(ax; position = :rb) -fig + fig +end From cae07dd0de07509ed9806cd45d366c865153c267 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 21 Jan 2025 21:51:30 +0100 Subject: [PATCH 169/172] Use Pkg.develop instead of dev --- .github/workflows/Documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index bfdf1de2cd..a2c518c537 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -16,7 +16,7 @@ jobs: version: '1.11' - uses: julia-actions/cache@v2 - name: Install dependencies - run: julia --project=docs -e 'using Pkg; Pkg.dev(;url = "https://github.com/KnutAM/FerriteAssembly.jl"); Pkg.instantiate(); Pkg.precompile()' + run: julia --project=docs -e 'using Pkg; Pkg.develop(;url = "https://github.com/KnutAM/FerriteAssembly.jl"); Pkg.instantiate(); Pkg.precompile()' - name: Build and deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d45b3c7404b6d4987ca3859e7341b8c1b54c1ad0 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Tue, 21 Jan 2025 22:07:29 +0100 Subject: [PATCH 170/172] doc workflow try 2 --- .github/workflows/Documentation.yml | 13 ++++++++++++- docs/Manifest.toml | 6 ++---- docs/Project.toml | 4 ---- .../literate-tutorials/maxwell_good_bad_ugly.jl | 17 ++++++++--------- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml index a2c518c537..2d069173d6 100644 --- a/.github/workflows/Documentation.yml +++ b/.github/workflows/Documentation.yml @@ -16,7 +16,18 @@ jobs: version: '1.11' - uses: julia-actions/cache@v2 - name: Install dependencies - run: julia --project=docs -e 'using Pkg; Pkg.develop(;url = "https://github.com/KnutAM/FerriteAssembly.jl"); Pkg.instantiate(); Pkg.precompile()' + run: | + julia --color=yes --project=docs -e ' + using Pkg + packages = [ + PackageSpec(;url="https://github.com/KnutAM/MaterialModelsBase.jl.git"), + PackageSpec(;url="https://github.com/KnutAM/FerriteAssembly.jl.git"), + PackageSpec(;url="https://github.com/KnutAM/FerriteTriangulation.jl.git"), + PackageSpec(path=pwd()) + ] + Pkg.develop(packages) + Pkg.instantiate()' + shell: bash - name: Build and deploy env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/Manifest.toml b/docs/Manifest.toml index 41f20cab09..2c88bfd124 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -797,8 +797,7 @@ version = "0.2.0" [[deps.FerriteTriangulation]] deps = ["Ferrite", "Tensors"] -git-tree-sha1 = "3b7f694d319bdbe4a98d7028a3758444a24eb868" -repo-url = "https://github.com/KnutAM/FerriteTriangulation.jl" +path = "/Users/knutan/.julia/dev/FerriteTriangulation" uuid = "b200c708-61b9-46d9-917a-515144ac7aac" version = "0.1.0" @@ -1674,8 +1673,7 @@ version = "0.1.2" [[deps.MaterialModelsBase]] deps = ["StaticArrays", "Tensors"] -git-tree-sha1 = "1a3591c7472ec7690b5a6d3ced4504f4a7a314d3" -repo-url = "https://github.com/KnutAM/MaterialModelsBase.jl" +path = "/Users/knutan/.julia/dev/MaterialModelsBase" uuid = "af893363-701d-44dc-8b1e-d9a2c129bfc9" version = "0.2.2" diff --git a/docs/Project.toml b/docs/Project.toml index 26c9b46207..abf2abb9ca 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -31,7 +31,3 @@ Tensors = "48a634ad-e948-5137-8d70-aa71f2a747f4" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" - -[sources] -MaterialModelsBase = {url = "https://github.com/KnutAM/MaterialModelsBase.jl"} -FerriteTriangulation = {url = "https://github.com/KnutAM/FerriteTriangulation.jl"} diff --git a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl index 19638198cd..16e57bd3c6 100644 --- a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl +++ b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl @@ -16,8 +16,6 @@ analytical solution as the mesh size, ``h``, decreases. ## Loading packages We start by adding the required packages for this tutorial =# -import Pkg -# `Pkg.add(;url = ...)` using Ferrite, Tensors, ForwardDiff using Gmsh, FerriteGmsh using FerriteTriangulation: Triangulation, SubTriangulation @@ -238,7 +236,8 @@ analytical_solution(x::Vec{2}) = gradient(analytical_potential, x) a_ana = zeros(ndofs(dh_ana)) -apply_analytical!(a_ana, dh_ana, :u, analytical_solution) +apply_analytical!(a_ana, dh_ana, :u, analytical_solution); + #= ## Error calculation We will calculate the error, ``e(h)``, between the analytical, @@ -275,7 +274,7 @@ function FerriteAssembly.integrate_cell!(vals::L2Error{F}, state, ae, material, vals.volume += dΩ end return -end +end; # ## Lagrange solution struct LagrangeMaterial end @@ -323,7 +322,7 @@ function lagrange_error(grid) dh = close!(add!(DofHandler(grid), :E, ip)) _, e = solve_lagrange(dh) return e -end +end; # ## Nedelec solution struct NedelecMaterial end @@ -403,9 +402,9 @@ function calculate_errors(mesh_sizes) return lagrange_errors, nedelec_errors end -mesh_sizes = 0.1 * ((1 / 2) .^ (0:5)) -lagrange_errors = [0.10849541129807588, 0.09262531863237256, 0.08372918040381809, 0.07939314534131932, 0.07639600032795617, 0.07579072269391056] #hide -nedelec_errors = [0.02246208564658041, 0.014303181582571767, 0.00906062850047998, 0.0057408487615066535, 0.003616459776120822, 0.0022876386764458592] #hide +mesh_sizes = 0.1 * ((1 / 2) .^ (0:5)); +lagrange_errors = [0.10849541129807588, 0.09262531863237256, 0.08372918040381809, 0.07939314534131932, 0.07639600032795617, 0.07579072269391056]; #hide +nedelec_errors = [0.02246208564658041, 0.014303181582571767, 0.00906062850047998, 0.0057408487615066535, 0.003616459776120822, 0.0022876386764458592]; #hide #= ```julia lagrange_errors, nedelec_errors = calculate_errors(mesh_sizes) @@ -454,4 +453,4 @@ fig = let Plt.axislegend(ax; position = :rb) fig -end +end; From c8e6659e0d1135a0571e86f45dc4112ae641cd20 Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Fri, 24 Jan 2025 19:57:08 +0100 Subject: [PATCH 171/172] Minor improvements to maxwell tutorial --- docs/src/literate-tutorials/maxwell.png | Bin 326414 -> 325687 bytes .../maxwell_good_bad_ugly.jl | 40 ++++++++---------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/docs/src/literate-tutorials/maxwell.png b/docs/src/literate-tutorials/maxwell.png index 3a0a8df8b1a2eabe3189b4100c905a9639d75310..35b1d28ec88846962ecb19bee85f97ec23451fb1 100644 GIT binary patch delta 310062 zcmce;Wmr~i*ENbFDxe^xg0zHybPEU~NOuVmN{7;23y~I3Lb|(45F`Zzq!B4;knTo$ z&+C4lcfa5L?R^~k*Z%keTyU+m&hwme%rVBC5PCpRxkd0%1aVgrKfXx*ntG^6NJWUj z;I04Ee!Z+Z2xG0v(?PAo>-=GUL0TDOihglQQFTzzO(mt_j-p71H!1yMipy#r!+m^c z=6W_i@N+K4^3PAkYfD~EK4hBZS4oa_+Z5~-H0cTcsEm*HuMb9vv^Y{LdPNfr$LU`$P-1_Z!THxKhL`xpFa6Ie zaDT6dX#N8K^9p?R@&DY@Apd@x|Km^dpZ)*O^8dHZ`9JV9|8xKU&!hVPLm^Yim+Sr5>@SvFY| z*L^YSyzNkTaehMKG8fj!_1UENe5aFj&>^1ZO%LCn;DYztzfF?Qj#rCUj)OUSOn+s` zVPI)iSyvP2Q?2$big}RN64N*`V11l>sjl8{fFzIGx ziBIY{@+yK(t|KR*J%XOX^Y2XUM;x+8!Ni=#CkN}>#0@7KHAOl#j_aci1360a1H4o2 z=|PXq_I`0%5TU(v%7h0rCLA^RAjD%eNh-Vh4IMXztBu@kE%er(wa>jgYVPMpi$qLw zzISxle8=5)yETbQW^4b>zDGhO2UX-5=EuL<^$PBGjaFFP+lHOsF#PfH<~^PaJxQbT zxnByxg@ohu$?mga`>Vqr0`LRBS5A4xYv!Y&sa^RJc?oUJ{01>c#%JT90!=k$6x@9D zMjcUkd1QcP&H9(TDB94SogKK0$4JR5nW%>|GgI&ga6m$~$lMRE&aa1)ZYPJE7jW9! z-(ce>Y&)5=lU&U;H8o`?!|qG4Xv4`h`C1GYIi$HBZg|WF(&PU;*=)F&@u$dQxPgZD z!1H9ivSyT?+uz53G;gAY1iZH-jhk-yCpp+X1GsgEHsP|uZhAGv{+V*VBi%dK{ZwIQ+H z4yIz&3NKilNcS^oGxzVv$=H<7Ej1>E8_FFve_C@_#i+O8cjgvRA5(U{LUfG;3W8x} z3GhSO2q_1T35Q`+;*^wZrGI$LVR=|;&=z-vmX_A4{%8T>({VX78kIYzf48@NDV&gT z8*_naJwS(}#pK@FPiS5fab;y?tHllVaF*iFzuL}^lo|v+2$LMQ|0So!EKf>6FMqmT zIW@2!CPDtyd@!%@MdkV5KObQCim?&^IM#lD{)64FDpRCYvGPen*Jb+cwd4KO%HGE( z?uQdj+w)TpTr0Di$c5W!{!28(YXuEQ4g*cBAEIWc@I_np?|KgbB)dimk}-~py%GRr zmI)(aw=W|Sj$gCdetBguKc3erDLQ%>miE`q3|{3#&QH7Jc`G48UsPJk#q)HRE5Q~r zieNljYk7aCH(6Mio__J;Q=H$hVS%O-)eh!C&-2xp)Af)6dsUsqg$01Wj{yJ_Tvn5{ zD)Ri9o@d8d-(Vw%nA9J>`11NsYY42fr?b;ys@|jT>6_aD^K)}+6~nq>s)~wRupO3@ zwJymX2g^f+_w*Fj6_u5DAO%hj#;n#yD^@3JDsQD#RsB7jLNrR@ITvLnWqZ3~IZcjs zka;5`qc>w;g#J#yJ31(4IGT%47%=b1l8fIFa(~Le!g3Cw!fQDmQ${7=UEFX!no#97 z58LWI<140`EuT2k?B8%OYGJq7Lz@O$T#%8G0g zqq)X+*M#8?VtbIh$kBZCN-gphvNRbvKO$~-`E3;Kakf7!wg<(g)-eF#ve|L%^z4zLcHpnWxv_`GH)B z(b4#|5!kWCA0tZdE zP3AQIO8N2jcLIp3Wn`+Mp<1U@-&a~iQPH|r=&=0t@BXkn}OpH^0kh z6YD=IpD!yQ%EZN0vsm;U{9LHSo%s+8cRzyq!Dyj;jz_tjjn?H9Sbcg3&I(9s-i{-);o$tOO7 z<#lHS3UhJa7gv1#-1(>Yo$WWzaQLRZet$M;BaPEiS9gRuk)Qq354J;iw~I5B1$i$V z%%$SHe}1y*v^El!-P_(yEaqj%Rpb~9XKgsO-Kxos;j_^nvWFRzK5P=A-q<_M+YOgqS%%!>CFeGeM83}B9EOL7$B^m3wY~zs zi1es&KeFYFNM<+iBj_aCSn2yp8G6USUq9*Qt)H_k?_CI|6ddP%gt7aMyKUCd%?n=~ zDUeq6|V_Fb@lj6W>O^dLu{dJrvN#<1?O(7SJhF3%W%%CsMF|i%x{HPBqeupei47aWeJc{`6uU6y$fwo;;9xuqDhdzHCYD zzr%fOs$Qc2WuPmDoh}d92}HbbW%=WaQ#(ulGk~gLUAOfwZ^j1RP=_t`XTH~**U;7V z98}Xd141e`05zhIXk+jwfEEicXv7oVOj{Y-x1u(Dau)fB`VcZonn6D8(HmrxB zAaMb6KxNX~+Z%WglPc=IacAFGhl>$lO9!jNy-Qgxy9>UqTVz?XvDBF~X;*KOkb3?_ zX4n}vYc}iq{Kl=DZwkD*KMbG~YavmZ$NY{E%3O~=)&9!hy>EPbeNTVkRai~cugvcD z3bV4ZD$rx2p(WU~64NKzhga7xCb?C`U8hwMXLH_~=C@mj83SIQOQr+ZgQ`z{>Nefn z!@&A@S6n{`9V|^XT$HwvTiix|G(d$}PDqZ;O#n<09A1NZusKv1|6A4=vK;*yj;ssg zzDcr27E37K?Dcwqgk~Amx{TxBKP}5d9D-)8Nua|aYJgu;3H?TRa$2C6D}98Of) z{CP&yUumUR-)h~TA*Ea_f+A+ag)ewbdJ@#~)os$H*X#4Dwp;HF9w1`&K&zo*y8t^~ z9V+Bx;>Na&9}ctiV?l>5flMdO}?Wl7jBN;wX)QWV(%)~KYzYd&C#z&(zR zj^NGrcwXPdi)|DPg1U_Vlf8|U6F4~4Q@;Snq zL>RTDiRbf7A;tf0|A9jN{P}afS4~%T9}huH29JzYS}o?U4aTqe`ucJjcP;g$KF#TZ zb0rsYEzg~;*+lA3phRBm7bB=r38CG9g{)T$R~gVoqHeY_+xHZ=P>pHBG!x<_m`vaR zI0Wm12hJe+WdFkEdcp?$)hhF~p}kI((0!AH*Yb7IFQ=+qZLxurC+K#4GVW9i{Idb~ z2U?<-Ul@1Csl}$Dq16z5czm2g5`vA5jil=`D;wrrM-_Pnm8_gqh?)>G0Y?yQP#|1D znx*qRO9rt9Sd(1@it2~>``dMJA|QCqrY_D+uFil!h%=JX4Evmt^74BC9^DBPl3K`9 zD7g=T3a7loQGN^hFp0}dSvFJ5Fv=wM5`WO255fTX4Xg+=b0UzrSS5U=txKmk)X&(J zdi;XFp;RZ3ANgQ10g95 zTmnYHHH?y9nkI>AK&sRWwI+~@vuGVh&5jkHTM_gMNkD9&BD99wi;jw**Ep;m485=d zAq{nf)L}sB;t1(QL~$eC09U)7&cCp#tANVC0_Qpe`%_gNb#TCtiqgj`)W&USEg)?K z?3el$fI>o612J55Tid$R_0(mv?l3O%NtYiw)*tEX2bj20Q#0f$^0a4oP;T|opM?xm zEE}SsNlG1VOcd82n>z?Wgx3EKc$yW2OsD#jbGS%^kZ{A5pM{VN7VBMrdcpoMD|oz= zzHq$VM!{>*k1Mdo_L=@~$0w%lmHmPr{AT?bQ=TVsI>}H;c*m{Zk;ER3TCj39q^m|0 z!_mj0cl{yKhJ=}futCQaEP)*ra^0J@iq9Od6x#z{!Ayp+*!7I8A5oQ!{NkN2OU1DK zEBiyWmm_*g96@Cy5UKt}5Ye^sQ7;BAV_8<9ggC5nfuWmW%MZmfl7*9N%(}3r-`{5D zcnkZ^qlX6Kw%MQDRnkS)=T>HBraO}hXn;@j>3B5-1#7@Al>tR69Vc0;*T0M@*B?E8 z`OS+H>EV0l53+3Nk?W+!ciORmuDFHCS~6s;5``ioY(9ct%Y; zK3TJav}*ujAm5%9D^?C$!K_m~FupK9KbWhMjndsf6#Yxoj|((EKhxp6_8WtLwcvAt zU(6fFs-Byf<>mIBQOw(B0j8_SP~kRaR}D`1o5*4|1$|YAWkP&Cc}Q9*8hqGWlUJ{( z@G{2Q9|u! zx!ErWlwC&9Ai{QJ#TFc&A{6SoTivESjb5%r%#5k3gD@QbS2DmP1gAq(KRQ|-E{aJ}yMS6a3Vf5$M}*ve z7VNy3=(NzAY$Y1{_}KKb$B?Cj=cWXpDuxG@*&dW;CD9EO5;SG2S72cO{*ez@*!-Y)L$GnTy!T2IDvp$ z4w%T0B3OL#dg*9v{Oh;u;~=1d50|VLu~!uM=OZ6nih8ISE=zaB6%WMvr8e5vuV0mX zA837f-EMV{%k)-q(ytt)`<{%*OG1XkbNC@RXaaZ`P9cjM=o^nPnZIkdMH1*qK8gt% zD%R6${|7X?3GPia|nmUf(j&74jL!pp$_{I>&+9b=nv|f};NC zZM$JVDQz8iWhR#h=>I;iZn6dcA*Mb07WI7m3#`XmtqAO{DK9nXXthUJp~)xsG^j0q zP}O149%*ehT&#x+-tgdUl>ZU7w=&pS>kxy3k`%y^Z}N`66fJ^>SwthHW(1`OUdwS8 zXD=V_1&B1Va`~>YfXEJ22%V1(93-XDfd55L>ncI-Y~vjE*?9ggGm6$mY}*HNRT8%p zi5W#|M;5+s<~(GNjEpSMs>lPkE49>pJ?W2oI}4ak98M{!ch8pzZ{N0!zN8mJJdVbj zcWZI)>+G!lZkDJ!)$+>&-GGaZOK2h=Q8E+i$rN%Sgf{_(iiWn0IGfzFATL~}(^J`} ziYEm+H9Yf;89~neh+7sG6H`QSV%9XsD;!TOnU(-0Syd$rjaSUE_~{b?rU1Ijq!n0b zIru1x-Y^9C$rSJeK|`oDz-+C_Ya#vC6@<^qtBW*>bwQt>>@C~%J-Oxp2@EwrK6d>0 zJ2f{N+DN-0mwu)BV25@**-QRYvRy7Ix(Eb#QxQKw1uzTxBCyVC7<)Kkfcx2a3(&g|G^*;1lEh4y(7gQDL>ULK;ymC6Zx(wJyP1(iN@HT< zl3V00R)4UiuKmgs7#K!J8=-}420;J&Q-VTH48=7B9AE#lAp(fh)4?E4a(pEK1Up;X z0fQexhvU0(7Da)K!dWY_^BLdf^7Ye8)2Bg_D!mnTBw-HE(@{@(3Lwh0;RXV}B5+%rq7Fd|!_i;*;HXqzIJN)epWCDCDdHA|T+x7!Q+F1bu(| zf^h-y5@`0uz#e$-CT0(iB`|geFf-@IC*sn@?sy>5J%>;A>fK#1lH%oo4m*zlaaaPT z_MH!#9E@LC8EoJ8C=ql&l+U}ZCxM?c2GX2mp`;8r>V+3NPL4<&WKF(We$=QBePW_H zQy1W{D-fJ4*@JB1vp_54J;faOGDkqc2li`ScAXm_zlXIC*M4)>-?9_3niM4ehv48>)#eD2*)v_?}knQaAfDVdD&tiZ3( zae}jf8nxm&US)GicmfVoJQ*73V6=LPQ!pXP9!3?Xps4s)UnN9U)gH7WsRx)f6drp( z8BCnVE}>D@fKmZAIr8va3(N{|b6X80LP<3*5&umef(YNqbO8npp7@O>qHdJ#tjyJ{ zb>4>Z;H_EhxNct5J(KBoakhy7O-lr$2Fj=Y3Xy0`ZrN_tqZ%*|S!Lfr$hSlzzZww4 zt}sC{2p(82Nl;T~xJc&&7y0|HnuUdBGKJmh7jWfmWAVwk_J|`tOt7G;zdnXkWgOS_ zUG5h;7)cKozl7$fKn^@UhPeeCqE;atEv*h*a{4qBYE(sF7V86>4832KlVh6aCt%0K zNCgOm8&VVLDgF*tk(2(ZLXx%*0RTQg# zWS@iCuvvcsm6Of1kCHlE8Ha?+6r#1`v zqvkFyN5bdZxaqf8tEMkk+W9w0E8H+p7zEDPo*uG*g*b30G_jZ=n^Q!k*%5LK?gw9Y}r^*F~})?Fc6`AAV;kQ zUM%?QPQW-&B6ELOk5|Fe*3Rx?FI!kGBR3Ak1Mf_u%$I*O2Q$EN4FFR5m+QT%csK6x zbUj4KIT2%7dHEjNXl-R6_?QHA!FSoI%Y*WZs#oCP)rX(@-EaX8J7_Qp4+|WB4^CBC z2}RXGJx~k=$<7NdCfNDmQWQ-}o&fWPa@g@RT>?C;XI z0>&tW2TWPI4*8fHRdBcUB}$evsov%pS74*z2TT3{o^8xn(p@Eg!XOd+zU>QDp#%xjUSn zqxzl8Xd)F0Kkk2!wez_EATwIHe*k*!uf>j1`J1sJ!2+Kr2~o>|J*T7!qU-s$G31 ze@}ON+IX0EIT6 z-KK$lw0*V$yd=BE<^I>26J4*|^e4)K+6N|b2&cByf(?3KxkeTA_~}Bt)lT9iAf<$M zb>PiI6qx|^(ln(x`mn-!I(0NfJ6BPpsL3k=nD99j`pAK2TY>PEXI=VQYy2wv)C;9xQ2~UDF%y#}U zK$=p6rw4ZhCYVYnvFVQ!4k9~X6!#2}jvG4`&h{9bC6?zUA`JS}AVP+LNurqN6r?37 z5LECwjv|!KA8P|OqL)vIDN|0LuKzoWec!qw6I=uoj~+nukDzkLpwInj4~hzY1;pOC z=7Q{S63&$gH9C<)c#qeTl*2IKDE#H;HZ$Jy-1II@!9dd z8_J{`wJ2WuLI}3NQ${v@K!bL$dFEFCf|>pZtRT>*8m~Y9MD;}>Kmp6uDxskc->v|n z00LR+;$I9moM#>w=iY@Ai{Ucshd2SdAYT+eP^?(D?lH8oWhTa?kWCNMy(>=2T=+O!6o6! z(XDf(E3rbR>cPF-2SG-l)dFh$41C*3hhd$b$24#jDyy;mu{(y0Ce&ef!8-wKbQ7$g zEt4oWNaPrH0|>abLhlD)YYS#pfmpFR=+Es*aGnj+0~QK-ScPLgUbPJ_TK}3!M^aSq zEOeOFI(r^DuEi{16QFtJi6W8*v&@hPTpgXAkn7c8QGo}}Uu@p`89vj>M3q|%v&>7k zgRgpedI~K;z88H^ZGQv=g^Zy3fn|p6&}ILbt3uCx-D$ah9yBLi8VJZm(DU7rD5u~I zP8pI>r`kSu?mU|vH9!Cr0div<(C`n^8WOk!PsNRmuIIX(sd#N;XTDRTPz%UWAv7sA z0i=k{61M-e>Dji^iVeW_fjhoyAeqOh+yT8o&`HwQf2Kb5#}b~?1(E=7e*gYGL~1Xb zJug#P9e{{QqQh-GD81$_K|~8mU^}hxaVTGbM_QRLsYAfh>E7myl|6^Y1WWM+P$lpT zry!;hDYNsO+QVtVL*DGqlzH@`)rO^RuOEt9Sin^r(xrrS-9&~551xZ>555#+{t(pr zNQ9E+>X7wATl^aJV266K&+le@`QoHG>?9k)<0%9Dwf~aaE+GQ1bF> z)M-pMAQw(k9+l=|H^5D~>hPTyZ+>ARuld_en0VL)_q(_Vl=2CL8e2_8bX3$AXbM1} zi0y$-q_I60Ss{i&KLF)MQPM4#JJ;89WEVh$d9cr@37cF)x)ki z!z!RN&A*iL4wSyhfw62dY(2`PhDK*kd;2oDkP!MD9*0JUmze%*)=R|Ec`4Q3vU-`$ zf!T*|#)lx3e<21&ZBUyqm0(c~w?<7+44M>yeOv|K#ffh~=n4VVLV1Qjh2&w5r@UO% z_XB_cn8*U6UN9CzVc+)zK>vOk^!Br~ghFy`v>{6Ww0)pY11+@8X$;%A=8lp^P)>;M z+(FSIcsxL-9P>bPMj(C`2poTBXD8T-(B{0Ha1Dn8)!ch`4HxWMeB7?i&Um(0-uw=% zw8|ae^M;ae2T{D!1~7x2*@9kRPgYe8)_}J4BE#F~98`bWWd$p%f?pl*ZuffGVn)00KTVU=r@b*(>-tujLYVAhhjaB*JF2 z+)Qi$7!}kCA#Cl`c|A69@%1#)A;TR;zZA0lR3}pZ-NdxLuR!|UJ(m{3k zKSO#VJVy|LU;+_t7u)~;I{*s)WB~+77*z*(! zel6qD?@bRZ;Qqyy$WU|S4f#>xCHi1Lh4!_B<> zBH$bej~HEG`Oq0sHZxm(a*b?n!f7h=p&?X2s2}0VsE#!(KrLzofr+CJe6sMFux{Qf|DMYnafo^BQC$t0h!7;?Kvl^> z_mPczu+WoOWA=-VdIb=e?EvQ1DAT})%;oy_zYS=vJpvs07;peE55_iz0m>bxK8Dku z<-MojdrJufT{;$38gLvn)N&=|MZ2I{NdvDH?;`u{L$idweisw%~<4(3aUfv=ug3Zb`>2EGCVHUkqA zc=t6_{~m-D;mIAnHfBx`1F_`S8ppk?nv9f{xuowRS+Ke=`8;WxTCab z0Q^2lrq!lDsi{|Bz6BJn%570G>^m&Mj6MO?PMf;KN=`!mo-Fn%pk_MLzywf`q?QK% zQO|wbmnk}uQ86=c1~S2Pyow#KJBI!K-Tcb}4lCt=ZtBuQI{ys7{U;#c6J@^YGS(qb zv2x3axBwUi!k{wC zWEGBSWCE4=Xdj6On`dwwA6r+MgrJ-yFfmc02cxSPh@n$o$`epzTvTxRW*ZLMIKB&jR1 zlnNk`^vJ>7)ARgu@8cTOT(JlnTuafz3jhhNq~_8DOy*M>Oj}LqNU2t!)m#>a%E`Z& zusd~eUU}IC;sbV6RabWsI5Xe_YJ?5eItla@oU>2j_oXrXpT=Ss8+) z7+>~xYte+V7)YNq0#f-M&ZC^BXZ>KM0=8$eKo%EWMh(4zI$Vbatk@p3B@Ja>!u3*` z{&hWKLuK_H=mnrLLd%+nURW4`lmv~ED@z58js*`SBdT{GSgVx&4APnf&jYF<0_?t6 zmI$sUGLHh_|6Xs3@=M>74J^1&HWX@^yD+|*D?$Ym1e9rT^jFhi$od8fKZx8M@Zilq zsQX;ulOu1+34Bt;;v09;fHAa|Tu6hqP_phf7&y^-AlN~$1?GS*XdHn#IBcqnNCYgR zJf9fU{cr!T`<+0&VeF4ZX)T*FP0|~ULS_<#+B10>Oxf-vD zn@ivqj3hRaZ`3e9pV9pUU821A&`tKGGabnJ39}OFP_Xpmfm*5zRlxkYY|H&LNP(zm zBwB_6`W}oQJu1^+1Q@y?P~{d3FxYbn*$6NNcV*-}Jw2a1c>-YzU#N8|Pr#s`9<*EJ zUZE6Zpx)JLEWA0{9TTisdQAsM3!H_%U7OS?$_u>CSJGJwD1FO1aUK9NSWOsQVYPk zqs~lqzl`vJxdBhzRUjyXY+6c9#Fjf>M$9< zMACs(>UU1~r_fq|*58PUjw=%(Bck<|YT~?#&qZY(&L}4~qZFu*NQKcQQah3=W8bpe z7^3co=yM%Rj7vSCyE&F(+3)lc^NF(SMHyuh4d0)Lw_J|4#iaKGOPcQg%8uLS*H<-d zrT6LgAdK0@=+Gd}bg;m8j!@;u+}1jcq^r3l8s!!yeZe6W@Zfy3iz!f+KktE1=dbYV zJSX?}rP?#hqUDPC-vOw z%6w^&uZs1^Ypx90e4^tEZp&7G+Lan+>naUeV}frW{`yc;*~mXrynk(ZEXVH>r`jD4%OnWv53!xq7>s-jtF1Ma8ay&zt3Nie4~dW-nPYs;)%jg$m3xY@tNk+rYtCr`l4-XM@ZNrYHrP)VgciR*p^3Gsda?LM!% zYlJd)30CTUz)v*em_c*~iqIpkRzD-oIR6{du6KvtMHA{~8oL{F<7>PUB`NhCsCW1@ z@g7f|4-be59d`wi9fPbHR1o;L`RTZo=1$Yas7Mr-O_BWX7(p*7JG$}Bae z#gwB^3~-E=Sgi}l`*n-)q4Vb$BGAw~HlW9RA|lG{DCxhVX|x#LaW?i zw>Waoxsl4G&^ws@kDWN#zpVD3k#kIQx>524UN?OeX5C+dd0HRni|lHjG%9Z>-#zgu zf3iqVV9Bp-I|M>7=wl?EM2S<=T8P(JoKDUj^~Z-c#}c+%9>EYxu6=tzg?RlYipJ~6JB?!A{Av9CAoc8mII#1r;^ z;Eu3u97_|t6-qOnx)Cen$r>v$+d=TETIs8(kM7BIfcXvPc8|UK?Kvh^t7FT#NNJ@L zswAXR%ybcZljiKn&Kk}0;qZ%}ZVOpQ3GODf7sRDSee7M=C<}4Lts}ZDKN%DWOkHVv z@h#+?sS1^dk9TF()zmm4Q>m8kqf)ctl%9-3FN*}&Pt9^~yG&JM797ZBCU-WFlHs zv~^|MX!M!!}VF{Y8B*EH=h& zs8i`mipKHKNzz1w3EoC9f33e(5)3`t$vxipiRip5UCpAe zduq3uf94Fopw;iOU^X1+6(0-(&OA{?jehi6O&%b|AcAKbjns*pw?(jmrcyYTCM3x{Kf|0;9gy41jr`LkAxZ zB@P|)p7VV9X>j?df&HD$-AOdd;QVjp%^dhYE_xO2?}c#MGGKRo`c>S~x=Wsalu{f+ z`^)H{+SG{1h5S;g!FuMgY~zdkhQzYgtD!WFP6t3x8g$iyp-`cqm#phf$d+cp{UhZTzZXn+MP4MS_QJzx`Lq zWkbAiQ!Mu=(Q2jEk~uxh&(s{Zh#c>gPzn00uU)?Bxft0V%&B+xELCjmhCkZ?2apdgB(fh@VbM@E8|Kk0@a8+H6LDhJK!mP zT2|xdSBRy9L&jMH|Dx8Mazxp`S3mX9@gm z-^(@dKGS$IS0?LDdZN0h?O)$M&{K_)q`};km~7BZQOiSKI$&zx>q^E<=`9i^T|D)@ z64mQ=Ip>P6n>Ge(T;bd@UxVc$I3hd)ar{=m7AE< zuP@8!)8HH8D@yX4zw%q|Dz5l;q1KhsRC(J$x~CG_!i7f~qqAk%xBIbh5W{Ho_JtwapC!7}6u#vcTY_|1 zh_m#ITt1QxwdegERIxtFS7|z~OvxB|lkzuz*3_JBYvo%vYwp|@j+YLuTV6`cfgLcD zEK==AmyUe@5@G0nyCQ${m&Xk$GgN{VtidT5>@V6^Gns%!(8rDP&RiIk4e-ON{85igVkvMuYi<&W$SIF~X-axMExe zt-&uNF;&g-2w&k^rKT5fa^5OMYGpZJ8Tws5OvPTf;>6;k_WIj9t9+cs0@7Ecqz~V2 z9v&rF4QW`E<#oq04yhOz|)o(7h`L}>rwN__4Ai5|0cP4<;QqYM9#fmZ!VY~LgA zy_6M;r%}(N%{=Yu(I@&bR;0XW3io|5>2D8H^PSQ*2r?KJmyRoZF)tG%bJh~af(YWWhIm8BuCvDW^ZTK=u5I*67e+$t{ zJ$EdF(a=v-{O+~ysp>d5L*%gW?CvDu+f^08qVjd*`usat{?EWt@82q`QTAvD z>Gbm-3Je94AM1TWzZj`7<4NZ3^KXGw)B#yvkc@Q9s!> zHc5kMWjv01)L5~Xs)aEx&INpRZ$5fgwNDx7wqB?8+_B6+j^A6H z{gwUuHTea)xZx{<_c$KD|07*_OC9T(j|1)cNaXF9b;e4&-<3Al!*AkQ&?{n+h*Pm- z&%V6u^DZs!$CrJ6B)YzsbBp}$sT#j#Kh}+M8m>DzR0G!wv;DR4`tT;7B8r~^SUC@M z#wL9|C;UG&9}q|*Gy{J=7eDiucry7{cBpWlbGpbKFH-J=Z*A(sCf`ee81v+sDa>kS z@0jDygg%lFF452yNY(#YF|xFHY%kN^dTL$MsE#8aS+VZ*Cy|~hSH(Hl@w$WZ$z}iB z(vImP7YU3BFH%+fXdR70@naBb!M~bxHRzTd{rUs}1px#vq#g5q@OYoSB@qwl^XS20 zUT!p&YD%8)aq%ZOxHGK5BLDjPtdnj1;ei-yDpCICd*0FRE)je243+AZ;|^Wn?JG!6-HcS#up$e2izT ze2Z2br^e~AibI>mJpE}xl8AC;w9$698PzVIOflAr+}Plzu~9BD@>!XJ1plQd4}HR7 zwM-MUj6d}sM9QQ!^L;(;sjhlIn9GUD$rB$5R?y0y?m8&73{mI*$t`^%?IYw(+@Q|l@zCbBR5%FMW+`VQ_XI-@^2-1bedivX2^lO&C%UZ6S&$A+CgLy=F zIu3(sV_)8I&Y)(D)LSg%nTXeh@H}i0EES`YP$v(;RrQPE;IeFony}_bzeB_`tv^Y-G=nZ;9R@nAU)GU^FDhD?_K}6JGOgJel(fxFkVY4 zZqbMVLVhP<^YRUB`S%6OFYHse&+sbj;->V`y zN4K3H*_o}^XB>`(d^tT9LU&3`n5#vo0^Yo56{p@hF8MH!bnK-%%459{WXSNix8-iV z;bdV@u8S>e!QYa|QXjzsy0cGq1C@PB?ngYm%p-D-DzNbu4G@P0p100rg~D{#W$;+N ztkUBO9!Xehk(u*g*w-2G#5G->Z_&i$R@r{#dt!-=BRApV4kN`dT}Yn>y$n>xnh_jv zKh$5@!C(3dgP#n;90-g(fTJ770so$XK_y@b24hhFYb0OU6xSrG>#1|3UAIGqkjwk+ zuQG_#vDcmXhqpGa_xPLe)*MF{V(i^uN@|h$6Pdo)*U{EU+Uj|m+M^>OPrCPjHd^X8 zHDhe?FJH?L)yYeaNqICLf)tw%HVgO|St9Vt%J8*i)$Er z4=>OP#(o6+crZyAL6fQL6i2S2@XWcj&>~=Wfyhu}SHyz~I}K!2!}{w8jh> z_h_i4uc}FR54yg-ad=G%xl27m_E*YVvJ8`+;_>qU^7zc7vXjv#3^$`RZtb4Lamv({ z4ctXuUsdZ*&lOG@yOa0atUOQsvTlMNx6K4|#a&S*?h74y)+bG4>s{}|s`Y0m%(KpF zw65VM|gNnIjAWe+DnN)=Rfw@E&S5v_i*TP z9x=DZAx5ctMt>`AX`jBT14e)Zdl#2vN<1o%kCD5+m$n{dDW zzPQ@>^|67kn}1x{z7*je-RJEQd*6{M4bDmUZ;v!J42h`0&83dF+2#|V!n>dN`}YYOv0p+q8Z`Oky11sXgny+#cZ!w9d5Rt1*vi(6*&Xf`@;vi7yma&OZ2zU%Jz#TeS2^P0g;UvBiCz8kwcg}>6Laa^Qn zERF2Yuz-mnCPF-$MmJa^+GNiq>l2~wErM@0-E11AeMQ&)yuH3xI89Ef{NaJSRkhXb ziu#zuAe;=m-{JyhCVWWJ-t^fuY-$qELOij4gC*&k7wcMJs%)y(Q~ zb{7ZxO=&pmwFqXv?W$-bJL^0=bC*96#7L#^uX*P#hM}FjGNQQpwyE>RJ#ryhYJ~aT zH-~;~rr_1*ZZWnV8==jRl!a7d{gCwf^4buGwNys*(p`aGLFe0;+>_Z@#d{9?{W9oZPeMD0)SGTe}$J(@X1Aq=V$NB4GK4 z@-MGu4<`(q!-DOl^bgR7{|aLvN|>mB-~o+Kd?jd!CFZ~wYog<*8T|K#*3+Y1m>@)r z0Kz05H0d^<2mX53dyGz?(r<6_y48VMPUOe*!vcZFI!-GQi3%hL)g-6xI#$WNkzb^y zxk;DM!nJJ;LK1=~AFFJsB=KU}@-Do@Njvkq-KID0^ve8S0IWb$zoD}7)=J)+d_kaB zpznv!ZS+=!X!DeO|Go@un(7wMlwnIDjOC~uNhAX6;w{tI`K*m8%fBTtHiStTTpI7! z5Ru<8p(Ha2Lvq;86ln{_5{G}gb>gJY2C=oKRfn;@g%uLWA7XB$UtfBy4Wtqg;h7f= zvcfK`^^7jyK6lbiXMbiR5_;yz$5r(MN?JZ{A~R_>vs^Z|2{lx*SuuZ6$x~ZqNY{(^ z5=2re|2bDkp+hn0<L{UnyKd4eB6}Y;{j_t<*s~R5wL$J3Z{41IBZ5; zCX4TL=UIZ+`jfH8I&b9npQnbr4CSTtCaqqNg;e8DmaRyuM&mUKv4)Hg-opn+PNv=B z(am%_CBg&?(^ugd!AcpU9Nh}t|EEjF!*Y0r6dE8jJR~qmvt*8lxUMixA()$2S9W3D z=h%EwU+5Ve-}GFvG+uuod&7yCkxy)_R|ymLDB@3-Oo(;ph*E&Qk3Pgd?rUD+=P?Sc z`vfuBGznjw>&coF>f}!~GOA5cq&IWqeqmn#V>c_!f+-F*2WmHT(6$d0kec?OWe&37Q$?s#I@6YdkuWSeq_KexOZl<-Q}T#(HF}l@oxZywWYq8nb``wGp%wa| zSFKt#>+y>NB?f;D03u?6B21;F#-Bun%_U8m)Ck|~2$t^$S_x#xm9TG#$X-(rR+J0| z@>)W&^H=_*lu=qX%{6WZ-q7@rLS3~Uz&~$))kCOdIqW%K2V((#lo#x zSaz#?;<}j|CDygJ1^lUjIjuz2q)^P6n)CkvDocORKCtURPIF`ixs*|MzyUQBNNng zSq2`o2SU|us0R1t4C-0IGrHxl8bcGXM)A1tjQok3A#_!weq#jl`MZJ?}A?l`nm>!749Gv{y6C0>#@!@wSpsN z*t_5E^H^>z=pd6qntwvg!ji7@{ljM&0k{V4tJBDTRI6U@pJu+MEHoOP=86_de?Has>uv9*9beOO|5 zVTAQN$h2HuPQaSscVR0p+`MrKg4KnF`iaBi9iu8j*w`c@E>(!k741&|g81VV=~2oI zT*Qy!@WwxHOIY>WwicrczXUSM!rp(KhRo|nW{{UgM+jmAzIly_y#u9B9FfAjha~kT zUtTV@NS}mAkQPod+4Vb8tX5%^htpW|gg|Qne_U2NNO-l9RK5xkvS}DM3A928DbF4- zb;^*pnCzj+xqmUt8|7^!FIZwVH2ZIkMWTh_ zJ)&w#nC#N`R_aA#dL|>JG);eFyK(xPy!$T*vtIOM_7;*Syp{=8gFmU#n!~)v#y>R* zS*`3XiYF3MCWSgtHivoe$umYC5Y0Y9o zwW6M&`qi4*Kbfsif{u6j*F==6G_;e9TE67xMBs3TGylu$Pq4>V3nHndFh?q}bdEC`b0(#STKCt`G7<3k2O49qIN|N5Y>U`IDqY1qOW+gd; z6+b#o_=7WXk9vP_PD|S7&O6mEg!ClWZVLa1+2P^g=7&`si)8Kr^A<()y33l zu=HTYaG639`zah#D44YZ%C8dasDe|K>&D0GzDm?%64{-e+W*6M76@3>IxN0UOx{6O z@$GT{|7yTWXrG}on0f=BN?;E@%=Q=dAnOg+dQTO5uNZ%=!lbmfud!#NNlCMQPK;RBcqe;Cr}R zC$Ewq65f9#*53%;CK+gU!q$>zyrj^Nl&jhh#qWt37UO?!15+3?sSVeaGO+tO_dcin zC?I5C8cQ(0y1?Yf#{8=I;x%V}7d}U07cwN7>X?AV652))T1h%snr%ngCRG3&?z<~+ zxPz2f(?@0&1Si4dT(b?#q%j~^nT`)aup6zc90h;imS9{S!miP{F04r~-(7{-A10-- zB#bra=OyVkM_6TgA{mE*saNs|wI>@ljyyBSHXqc33%=+>_+A~;M!n;Sl#=v%Mv}(J zlX?-Q=}hIpk-n|wFJ@OM{ZERy+J%-t_nGjU>n~Cdta+4{A*rSJJw9yhB*S;0ShiJI zVZ?trHr)QP4g7EeiCkZP=(@tteDQ=P33bF&*o+7mkl_(g>G-t8N|2dA)~69_|5+EI z$)fWHQiUXlH#z~R5CKQ2o@}w&kpE_p^9qrF(5N0o{b7`Ja=rAA zsUD%{f7WtAGioO!`FcU>L+f145_s;b>-2w%bWYtc4{n(Qo$GfJd~7!w%Jhdz=G)gzA2-D;6=l7=kK@_J3W zOWR^HMlFGNWR$_~jc!g>=Com>ab^XlOe#XI(yJg+Wo9mqaBf1|m`)8b>dTnHG8lgx zS_EeCAUlY_&_H?xxBsu%B}QNPQMw3rErhQny=Sor^4}^BSe|@IiXS5MEQ)wt0pUr{ zN-VvmRiwW3!~Js1vu$%EDWA76%^1;FzLw!Dkyy#3^(i~W%NIjApsiYbt;!zr`iGY$ zDTi%MZ|wyM@E&@It;zgkW88tGd;`a+O>yp2c%PC*MB zuvA#WzK>DF!I9T}S3!5CV<-vz*|I_+Rh1PXSxBr73VmuG^B6WAg`sBWPRg`0l5TZa z&G8~Bp#x2^o6jw-XsLomk+A+}OO?iI_ZkRRxbLCxnuG7g9nwNjP@4~533ZXJeA=nl)mKf5nQ5P-*@jwzp2``IRj@3V2 zO0TYjTPFFn6K}Xk1S%qZgndXKN^P$3RygGlalNVpCg1MVP4fzulD%z-sG~|CGCG5|44t-?9w#{?NDQ2JB6nxlo4$xh9rwikgnAbxrbeRjUuX+ z2un?b+*wx<7nvFkB4r+Ke!1dDnRJ0>)_j``R2RJOxgPh}Z;G$B3meb*JeB2Q8j5Qx zUB1>P#|E&gcquik^ zL_7Fmgb*4kV`D?ov0D)lnP$~HhR{(wU4143LFo%FB5p!07bmTVN+x&%d^<0`b`iLB z0>k7Hc0zqCBISeD16hBAf0+`}o8y?MpayZX>nsUU%FCWL@Vs2DIQBk~kRl<4i17%r z?0jn*;YwxZZbDc~>A7k)vU%P^KP8`tkx=bYMUhj2r(~7cAh>MquibB^KRskh->bz~ z1=bKIy$P%*n@-0A6Ispb}P2O+7` z=8E*L!;(1;RQ`X!+No67c_jJe`A?aRKf)iW`5JL8wtjY>2y9d0scZHQJeSa3HQ+rJ z7ur#6tvBNvw)lZ=UP~lZd$2~cy2&KtukU%y^LO9X;*fq-UKjC@rDY zl6PZNk^F!E>|N`c-8iZR?|%O$?wIpKELb4Pb|sma)QS1JE8en%kZf}kLd5(Xz+FvL z5P@!@DyPtC9+7s&L+ZrLOywAMKx}*}aq1FrejF)QGM? z=yMuZ)ARGdyuD-8McJbHvrbB7o$l?>er1U3y7_{BZ6K>h)US79E1&xs2j2Rr#tSd} z4zT{RyBbeFD?_9&Zf_Vum`t1&Z!(AM1akfwT+qaN^QZ^|h*NfmouuK<|-gLO{b1S<@bn0qPgmp|xSd4!* zG6hBk_JRC}BXJxjv^r9cQmHkh*`+ZKg+Qmtg}dFC^_|j4O&}tgiDklF9=dWk`cE0z z*~?qOVrIM{c}BgvUnte@m8CxWU)o=2x=+IeQb*l1*!gi5%G0viW#OKr>65dfWt!!S zxTw9RvHz?jl}|=%{Ygok^u!%Z6|R3;>?_X~qEF}Zg%=(J`^5c=h@(8P;26>(J{jhC zkRaNQ!*K4K>(Fp~J4+{F6in59@)B-yIvqqPRc)?rig=Q9!C7s*FwEY65i-LuQty`~ zs|bh;Iq1&xf=9}7uZWl&s;3yq2}T;D%()R$-PTvs^*lheat<|f(Dd<6rmyPQ7`+#1ehY+P(f{iPHJX3qj!=Ay0t+ab zN~`b4q_woTu4OMP`eixBYiY<9U%U^C#e-7e5@R7rcLcCr`vnm`^*9T%YuYYq?sS#s zRs9>pM2@Y-`|>y z6{bSMqWQAHLy}&2;TlLF37AxN{s5Hx;GCsc=5-u>Vp0kQ%UKqreb^QCQ3w1+A7w$YlmS@;E5Fq8?Y(tt`8tw=~t z$_UOTsT_D|DwBVtm9z#iuExA8hPJplRk*m7W6yj1(5BX302YRUc5!!ZmCvQryrE>e zSA}7{ze2=3{d&ml?kuB!7n|SW**h@)`x1twvn}q-A%ZWw@Uw8m3&;^XYj1^*tlZgw zq~j#%l>2bQWTJqi^(m=y5hR@&5YkR80UnJU=p6RGngM?@?`YIGQ?wBoZ6fi@=sO+I zCI_Wo2ihV-ju5Y|!AJIXq<}XRimJ)MT^9```}GttN{QfkLBdpX8AQ@KZhj)V*NonU z)R;05DU(2P?jL$Sfkf)2gh_D2T3@e8FGO%X_0)2!fj{Kw)h>6R!&fAP+2+m?NptJF zBFBD=op^t3&G=_WYjwdwmK2s*q+OOf$QO2dckX3Tw{e%nhyE?$jd1^K;dQvba0@eo z)Fv|Ny8V@v(za}S(H(Jol_nD+E`CH^Ywx{S+vAUh;wA7JU~yic>v3R(=usT^q>a^p z_d_I7#(Gd8giq*3k`2e5;Sn*FqLg^8baHzh0Fi&L8^Igk142in@3v_4q~0fD5Q8{) z_>a>Qpi(^bKCSNbu^5Cz2qXIB0C^FK+yY8_4KT%ZlVsJpOxhOyd=~uU%DHe+bmcI3 z1V3Zvt~y$2nl~!Tq@&+cb~V=9IKJ{_q(;?0-%6DN^R+uh>7PEVTh*+T|3A{rUie>u z@8W+!VipPh%zo`SM%B5VN1ua*>R_E$`a)_g# zJWhWrtW{$Tx>~uB7{}|G?_x4ZvfYP{!^UZ*68{kt2Rg&)-Pkad_$7CNd$+|6qbWZf zTMKLtOF#94NjHJus1fK~yULf@MD5B8J^z2O>K8JE1y+VXE&|gog44ei#{M&M=t|;U z<6Eg|_i#_X)}Mb7lV0{py%&B5X#H!qFBiv3i%X?+i&wcR zB7(HwC-cpkf1KDfnJh@L!b{xD@m1usWV^6#oCruvicjYu1B;*b$#L`)x^$?Vz5st7 z8COg(x*_tSc~CfrA0s0@AoT?Ag|}#Hi~zGYyxtQ@!64a&@=?cTXps_Hj-N4Af&~EO zq%nal!so{rOq8SM{gHU^Rv{y>Nrs3?gwHR0w_!mVUV|q>%A^a&_On}+b9p0!r0rax zi~Bo>V75c=Y>Z zxG-Lx!>Ak;EETfUDUC=HjpJPuYXd%AZ7PU|;PKJnc+-Q+3!0f2aa>au`43AeTm3U# zFTs+MX|p4m-ymElE~|c4H=r#~ST12-b$h2Niwl)e*X7x2R@b2Z?)smHwS5tm9c%rw zdUsZp7v(R!@JqlP8=Oj+QOke)rWJD(c!J?u^3pVtS(*g%x8TNc;%HhFns`*GzCz-N z$$^Go@+37(gN^4Y-=HvM(?}Z7W-Lc)BpM@vGw5xx+d#1i<^`-e) z$gk@X-IU*UQ-xDiQvOi*^C^7c`v9zoH04AjUNW-O7T1KKAY2)Al81td+LbS&h#?$R z3gkN6_X^YRuwp*6eqrT?9wP4uJFF3-0DczuKBaIyTb zYZACZ0lWb#+L10hng)NdbkQ2!Qaz*kcCv5sh<*9L7rrOF5$=C4+`#JV9zw>Wn9G9g zwW(<#-b^TVFr)FPyQ;JeT>z1QoI53v_FhLgd}MPeLF*|eLq`;0*%3Ps8xd%pI-h4a zPc#&9;I!f=Nc9l!Bp~wqqUWqk*#j_YpDhxq>igYI1hMLl+=L^_sUz$SG#+;eWX~M~ z=CkjVITn(-p(cNsh*AV7J*~(UGiC^Z8)HByN00?IH(5Nl^0(EHsX=Yx)3ACSHcG+` zEZ-5~_l@r4>lSpcx@lz-ZTC&S3eR()2hrEc6a6Tam$t(aReVN^yR$M&Rll7E_QD?q z^ueV?1sUNi1u3vpGCdh*<$~Z5WrlBpkZ*|!)NkfVN1uP3v^@_QNjjzPNdiusqA}Gu zMMExze1Tx-2cJR;`2Xe`xj=|IGYy&O2YjSxsrvw`49yce+O4Rg^AyW!?%&nGNaDJ0-i8WZU0`& zrhey($k%^deu+mHS2sOQg8Pfqeg(9=l?hhw{$dv|yzm=9Td+vc=DiAeTCex$6g&_* zA~NALIofB?xK%N%lCA~)MEDNW5vqCiP&cPEPcUyo8M)U-TsDL}ZfVHeF_0D!&;5oC zOLkAH0eqgSxTWM7V`vk$h}fy0<;=slQiyju_2Yj6O;kLGJ8>LUlTRfwhMaj=4Dso) zGNFBXF&q^gdGjMuUJ%lr=17gk?1U)A7po6pf!_<}{_ePjk&Sd}694E_q4qYd5LJb`wVlP^4GlL% ze>i`wCiyz+YCJnI#T$nL5~YmlHQ~1lHbPFJ3Fg~7`k2fAS@jT?U=qX zGET*tE)~}jBF}(C+Ljg{k7lHL5w?8)r2zR5$&lVM|kXCyL-JxaDeFOiNeA zd@)yE7R_C3DZAv-Plj|2&t%sZTIqk{$ExfX;Nk2&(Hvg${DuD}1b+`klQ|$)tY;nK zO8=UPXB|1y%iP}UaMqF|Fg)QRCHuL!sDp?Ly9X}&lBA_1PMP`jNTtb@YSNcd)mna0 zmQ@^tWCaC=qaX6o=juWVSuf-?ED|pJ#V!F-Kpde(e7!e@86@Kon1zp9#=d{Fu>dbN zI{J|={+yqnA1BE>!3P%E1zN-cFL8QDXT}aJfFI+uIPGiRMTJ-rBmFVX z)XHTACT3BN*Z83*S?U{``Rwj&D`|9HSd*-_V957cmv)L-Bw7KlM#WGKx2U z>Ts0W#a+3*$Mip|wrO{*aV9N~s_EjYX+crA(GGR>Q>IZ~m`tmNH;Uw}R~7mv^-Gs+ zF8Y~q4!mDca9?=g5d=H(-9v64pSnl5L-eGIR@CjsGgP|z?>BI0(<=Pwwy=>mjlp1U2rUyH<1tf)p8b_gsgkzn1IV%3q zL{Irx$v9susqU#yvr&h3acY}~*+1ijJ4z9+mXWnQ=ig?KUil(oQFG+bU^7YgtTbs9zCw3Q}0V{4&*8}8VhM~y?;1|yFO zN|gkX3 zuc}j5{oLN+!hFFOI+@B(Oz;aYtO0y4caeBKKw zqdI-7=v38QXbg@z{0G5)gS#dN$K6O-Gmwxh)Ik#1WJrO36bB|Ua+`^`Od;8X{40|3 zLPaFg==emr$sIf5RYc?zH{A{LxT5${1m`prB1lv-bDbR30R7Skwg84wM;1=HD%-ULx@RW8p(}#-z`GPwwct5FnNwB8dVru1T2Menqjw6p=P#AQ!?w~1v z_|R2Y)@71C2^?h6r^h5Z6-o!}Xj&p&AuoxrWy}70PJLVI^vi^w z$6f98P$D^R%(n@$;PS}a=>vaJEG?kBO!7N_>7hY)xfb{y|9s?mByV~^?AHYIko{}o%Uf$Yw+@7-i@ytHYSoHiSz%)Dt6F!c zFXYHo>j&aq%atz5A5(R6@ArzM`SEOjs=QYG3%?EMi*vqxME-97f?Ald9#QByzKfY~ z%uQO4v6adX=hP$Z!PP5HtGU{enb0wmikibd^E~Di&!HkBC!44kDbpOeahS1=15~DY zqYD^r=YlhgP;@&XE{`!_m`E@<2iaK|n#CAaKbKV`yy19$dPFicl^<}6W3GySg?!*t z(xDZo=XsZKudkj3C7rM3%1!|9pI#)ia@z6;_bPQssbK=HIj!|eJiQEMo7)T;OUQoL zLds&zv8O@z1pY*7iChyQ{HsnE#Vh0SHwdl?n}%Tsm&v2HL19O@EEIlOza_j5_rDch zhx-dR&_t%q;%6|xrO6jzW+W(oY8tylI zdr6=oWLI#1WW=In^AYQ4sWCv4R2++!J!U8&`L$KtJt3?QNrtG4q)*$2=PH`dln0H6 zDkT&pL8Cwq;s^A_kohQA75JEv>e4tJCLV3+lVD}WM{5H)lE?amM}}&Dw&$v~Wc{=G zW-g(-Y<`DGU*h17t~&WO0=iBUe>G`{qFq;d@oOrbRpj|rZ#6lLmQD7Bi!g`ptBSqw zhe3py^Zr|6KrkYUGg_nFz=3#kMy$NVD75DZZha3Em*nOwO@b|B={A_NOGo&66~~rR zaQtv^gNXQ8&E&{%w_>h;f>?Ezy^DciIEQ?6As7a`$Hfm}!e^^moWJdO3_f?rm6%%pV4Cg!^9$ zufzR?dzk#@jo-DO)t$Pw5Oi~~tR_uvFd5{a55Iy+kBlgPNV+Z=(dukGXQCCRBoAdr z2!G)7&P5MTTI(v^sBj=7K`09$G~jS?y#l@p$CK}EHEU;j(jo@FBtgX_mb?){d-6LXk__un zm+PFwfgJ%oF?#YD)62? z)<2xCU)5Ls_wYL0|5kVpO?}}OW*OD~6De{$6f6%ku>Ri>7ig=)MUB5+NSNnN7ylW5 zZAlyq)n0*nR6k9g$8Mr=E|4gnZWrgv9g>7$n0a2S zhMH^YCySaWPW35;6uxm}@7d$%E)VPTyMeQur&mKo#?YJ$lq*ky#L$G!WkuzBKx?U) zg;k-i6T(j|icHG8)_%>`#Xwl!olp{g*_X|8kLIdveY_8~1a7`+qH~XTxF4uJ3m1Jp zlevAZur475cuUWWVo_cua52-3+Q&2f!V8yRN_;QnQL>g%nIhhs6(=lSdk(QVt`6#r9xIB_iJtLA#6M$d9obZ4q-%pr>jYr zOWA3Ys+7$>ybkn(uiZTo53E-+#$XkLHy4vhnlMUIM`jp7cAMVks)5Qx$l0<%UJ@TH zQ!(6GZCd|4Z^-S-#aHaa760F{<`t3GP4g{M{&(MszLsklq00)ZHu>=_1=BA>6KcAn zO)hS{@WR&t%(*!PMOge<3KSN9LkGWyVWi-{JWzC)NMtL?@{CYAN0dYf8#@!Vb*r;Z zA8HzO0&$r#y%=0fQb7{o@L@EW9vR5&vhT>D<*l@N*}_W6u1Dl0HT?`Bds3^@lf}!a zpE?O;AOkbZH)179qv*PgoX4lnr_OeqncHpxpKEmV=}zQSBxv{*CqbcqMdOm0*~%O z3f+s|6+shb91K*+{<#VHU%})N`|^J;d{1~I++TPCvF%HwvP`3bwb>}a=(!RJbrx^V zl#6{TZpdo>N?c0%)lP(ePk|W2k6}u!9AR;$_zhY-&$RDdDEZz=E9DvOh~b*4M4@;H zq#!+_U@$(_ra0C{(RegJhkHeRYLvp~BLSj{2nAyJ=x`AO^LX>p{RHo0170-WOe zu|YTN8k*{iuvSV%f4b8zbQ^VBVYR~=pVo~CS z7k&le!vqHiSr^A$494Ls56QkfP@LcR+|A?sEh50tkul>RZf*})i=9W})uLyJ2<)KRw2zsUFGb*NJ!pc89U~R$wE6j}Dof!LO%N_G1 zPA5{|*1B8-+-iKD9)k9cyvBQdKqkwOBOOY#I)>-zA? zC9B-=TFta}GX*q)$EAH~b5{Do$HPf!BLrR+Y1YSIRh1J;g(4;WkeJbtS)2_^vtgnp zpUA`_b_@Z3U?X5$L?Sax#;~8JAGcvmZv4o!gs_;yoTW(dKXj6D42+x{(GQxnPCL|z z6V{S0bHR@W69M(ES%jRM)jaGnG3p{ihXE}JEnuW8{!Sew9^FK^QxyCox3JO2*A#=L z0a%pDCAQV;O+U)a))8mLq60uJxE~qmGu&Yzt#yfid(!7Vc5_}1=NrJAQs%FP*Wv!c z4Hy}+UQ&%>kz1={vS!n4tsK0t^2&PCoDYWW5}@rtXZ%{!pd3@t1szL0?`k*+;ZiDZ7;^y7|@fmCSRXcigj)!{nKP(>W=0sR8*9a4BpGK8^Z5(k-z zgx7w5_&@UOI`3AJm=Tx|ISzv0cpg4r9Uc#Ll`obYIAd?|Vd$iKrUs_j2ORM1zUOBf zt%Y47i=$1iz+D^p0H+S7+B@Wvzhepy+T(lzw^~K}hBV819VvAdHL_Z*F?(K~Tr3Hg zYbgEGk*vqwZ{9q<7hZS@MNkz^E{O4}u37khjuSyz8zPc9YMha3u1GpkljQu#KMPN; z?DrUQp`3gsNMi2pav3Z?MT7XJdBqBG9W|J|CIhY~uaant^UX$fTKEfC0AXkYXQYc! z@OHS(F(Fy#w~@HJebehvd1(UA(mG!Q8nu4TC>7` zQrbS)pU&_zJg*I>3b=al6#cf|Zy$@ce>WIH9zXAKU-b_ZEb%74@WSr^EC>@%<>;67 z=Y$)<`r~}jfNRZAAYH8XDES3DVCAo1z4j_t5CuMe2TMhB&vyR$Wa^=~u7VtbrtE8ew1aD!6z(Mvu7!w{tC9yv2? z|FT)GlCo5(iA}AdW@0Ef^&?^?AGOn;nuX5bR`ym5hOBQ-y98^on&uRa$h2&d&09~; zB;R`ddl4Yz8Xfz}V~|_qm4Ky{RBo-{M)uWj?V*g*2a~g`6I#Y%T==mcT1j?)!x+C2 zybkxj6<&w?3wKbg+>oi6nRaf`9S;+(SQM^;I`2xBYuxCarJ?Ilfz;uU=5Yp_1&)=| z)YT4wsV~yKCuO&zON%hSSr@VNhU4I!>LTa!@pxFQv?*x{h$NRI0~rL-jAD7-dL~~_ zzX^$b#Lg8}u*6y-B$Ab=>U?2;3VpK86jR}_Y8$ES$HJQ32K4^m?YC2SLu z8EsP&tgltSlH!S(+!sw1+?Wpwh*#;iz*QLIwPh05*Z8l0uSPN>RKB?EZ`QAt3S!aQ z8S4`H+!j|4HPIJd_&SiW+c;hGLzV(jU5uBK^!RtS-q<~{rn zBw~`hw$L5pi!k`bjwm&=!+AG_z!z_LEa>9GxvEDvVo4w*JI+uooh2w0k_J&3xzE3` z0)U0&^@mSq^RZr{G4&g_IAqxBZk)R+iGp5`m3(2bT*c0g)fcqQs$W|98w|bhl0!bX zUbofsk%U}u@0EmEuj1#6lx(X07J(99;9UOe2KX!TUif_g9(-iMfJsk_8~g=Z z)*0X5iEQbEYqRHCAU!n@<=3G7Yfh(Vhrw74y~wiSXQ-y| z!cH&o@0FL*k3jCrujIVWEosG{tF{YF4msTi?dBiC z-c995NpTVd9kT zFFNYF-lnR~bxZSq{|A!pb-4en@H*UIxP`{)(nt)hX;XbGLbR$@**r`4*P}`35G-n< zgCbWunXXz5yWQm)(c0Ghy;Z1aA|i6MfCy6NLNo~pOf7O5!HGOB)o3#mqEG}e3iy|^ zhX`eKebG`66j40{8VZ6V6Io1U3_>H61aoAPN-Lw*#7K{S#c`0@M+palaFH?VK~UeB zXhe*rY8Za2;eR<&Z%c>8Qa9QvOv=-r^L=k%KX7bxweC(Q*bv!utZO89&KgqWHZ7;0 zm*dp>2N&~rpOmb~m9x-)p z`DeNqiN(~lTU(N;2QpPTbHPEF?Ne)GLK~V+l z;!(b~`3wJL$c?@t$rOTBCFifTAV#KF7Ae}5xHG%63xjC8u0lAr?oxa%9D>RMA?cx1 zHtGGZE77#a$|{~i^@LqCq~?zrHV))tNg2Xvv_@FcY2t}N(x9^9{YNqx%YGxRsM~iL z(WN4PVy90!61kWhDnG=f{}89+Sl1D-bvPM=*xYtkpMthRER*OVc48G_jN*X2Rxzy5 zL=YHTe0hwjLbYgSHCq&2ST>5RTOu z-S*QZ(hPFUyQOQ6`3?sJDDl01mhDL65ea7oC)n))kLdzo`Y@gdb&r(00uQ)l0n?X% z(~_#(C>TMK-L!{B9SLV5`rh@kcr!A=u3xwh$vDWRM5NFgg_M8DzN73H2^UX)Mwgjn zSZmr?!t3@=JSS1_Msw2jwU-PuIzDlj5(iZ=m!P6x0t(lq#-HO;41q_O)c?djClQ3B zrR^WP^n>MB20r8KhJxya-`Rm3uV;@pvcJ`d&B zc;2eLZ79*@MP&MSKnKrsoJn9y2qqFp#& z%29fus)QnW2O^|6+y&Lx)P~^;cJXVeJ27Mojkt0_OC!U> z358}aR+gM7pKv07h?%$-ShLq4CGUpu-MH^BMZ#Xj+{03KTw)=?LDRd#dD-Jt#l=0n ztMDAvliIt7^}q<9+r5_i3%?tz@U=8;04!y^1gXr)*yBx3q?#2g(N#egJFo)56u}K5 zU|i%V@|V}0nKlyN?ApV!C~8LQ=s(sK)aBGT3zVIdeXbOL-f&tyj#c;w8=8UUH#CQV z&{MdmoT-x=jn`X6iqXk?o}~7kDOif@(36S%$UI(+n<^p)cbBw{w(!DwSiw*cJ|Q$s z#}hm?=1RmP*vs5Z^fyX58eZnda$Ve4dDacOINnF5KUpBBx1%FB(E~AW^FurG>sXM% z#O!1NQ#}WNo=EU&HHg|6Ad8xWDiOs$88^ zE@R=BO;T||HQ20u+qGF~o$LD^AziHWCJakz}l4p<{CGY`%q-sf?3q8nzl5UWo9S8N8h$k|L z=n}+VoL)DA%~fp_(lI-(#X1@|hTyVCkM8344U;+879on$e)1tG#ZCBzMDkipv5MWF zQeLy!AtSY?ert4;rw0LML6Kx|)q?025WMk<$ND zwMm(O-r@On`qm5I0m$bL4wj548BK(5GZ*2rK;6!QLtB%%h%?8KJ>glXT@1+c4>*H{u+jZKGLcK3&7O*`W*|*MX#0<% zC=w1xL(&0nJ;}rBFk-gWh%(FfRFqWo{+mR4ob)3$w+RQtkE)GCNjN+#IZ*`&1ydRa z%k4>!Oq-}fWIk~uG}RB3KcE|9UB~4_S-%YU58_rr!^Hz${T~(&>d?-dv!YayFv69b zVa;3N2p2ISLp1za3!)vYS%G$pjY+S+7pivLdoeh7c9Qg}Q-%4Jk`|Sz%UhL8Q6^<9J*QEHF|}y^%^|({U&AWY?chKZ zMT;i)ZZHYg${MbmTk;AvsRrKJ?K}%gGEGPI_(s#Zw23JiP$Se=tVBLCcD1ON)OHc;s~lfBQ;~-Dah`U_}B<26+xp@FBcjm@@3n1X`4Xx+r_G$KBcA3 zCMjTRHej*_*1-0{V-g+k$NVZ4FzwMGLUVs|smWksB5Yk(7)g%d7N0$E`jU707=R-c zrRVd7Or*HR+7z79DQxX5NkuV56uTvWp+2!VOp;_=sbXa8Rv~8CcTqak%z(YRvc^-_h&bgo%N9hElnyfXo&XXXL-L|t27 zP_*}WzN4el^abrca=U3+D~zgFC+0F@hN^pKGn?Y>H4fEsW%UTFi`1dkmyauUI3Y1*^^g-dp#3GiuV|qoqZ_=I9_mm&L+7Q?h62 zK)7SoW|~!?(RnS4T8E0Rq@8OLlfQyYi(6!=!d;t6b}2N~nL96L#PD`#J>|&62s4od z+*=H*OYOi**TMg+Hvc8;so+~8o266xCEgZQ@UNud=07;c{`4 z3Rx82uTg#$(6i~>{b#D~&lprGZ%WTN72AojU~&sojef9h(D{ochKa+QSfKjLIYxv4 zEdOloltp<#Ov0@~g@%aTJxI#AdaY3Lr?tC6DV-NC8ec~Yj-V1-i?j@MpAwxs($6s} zjG#NvkPjVp&pCUwNST?U`-90fwhl93T78;VZcr6jUmU;nvIUkwjn1wj-sQt=wdP1%21(r77%+V3g@ z+g!)wb3tivMez=aI{E-xM>=;y%vX9P`Cn$is^M`OUOQG7xlAh!mlfK@uESg$6^os0 zfA0jZJGV#-oxA!RW3{j}CljQBkopJPGDddo2_jz%J)gg&$6X1JN4{s|jlQ(Z*aMqg90Nz`NIAJ!RUv==%N~-FE!#yA_)8IiHs5CNU_Jz3W0PC}Ar;i-P$pN;y)>v?F)(PCU-g zzre0ZY3Q2i$ZAo0bN!LKgL`4aS8-9#p*_WZF&Qs7q3-%`_jIRWWKpnL66atBrJ$2=DMmZhJw3Jp z$O`d_=n$IyboQwH@6KWgZGSsb#;KQv9sUfg?mn-P_}}J^^v{G9VMAwJCVGFd`a{4v zVL-pKL}LpR!>~?Q$oPi}iFpvepdlztXeEigTkt_1;(>}WD&w(fo{k8_MNBhLxT+}* zz9~+A`BB6V*dl-hvh^*sMa1~TgVVFTb<`-2=HG%P_PYMtu)ZQmv;gkUZp+-Je2$q~ zKUBgcgC4ti`tZ+2QD6DxifTI`xoEb`G3J-C-D~jx!u>ZKr%E9ovlgs%nOK#IkVQP}gK(`xlbfx?imuDaJfdXkQXe5^Dm+&ull1m41S$J z^U!_N!-ra%WW8||sA8#?@wQ79H`Sq<;T-!-XM&v?#-JN`x5 z8ghN?da}f)-C#X~#o|EYD zf?<^q4d~THr@GO)*Q`p%DwP26pG($ zeCCI)jz&kDmwuzZBqXYyV&oysytt{%Eme6m{99(kun4X6Fy^@O{5A&=lOhQ?o2<6N z5CZLV(pJe4za`kUacdYSnSaO*4HYF?EoIS`*8s_}B*GR3YR%n~ZjBFlIcr| zUQRw2?;_HLyhM%W)3z=z8D;4y?+E=XBfb&)gf$q^svLHPA3w$qN79fA`g7JE?FpukjAsEm*I^@v#w5E_;%o$o$tr&{^#IQ zPfg(9x)XI~LeMnss$<-@2Uc6XUdxA>Kqc%N96BKl#;@R(nlc54$c>OHz<&e=S{&<0BZfIOG17Oh#$qRj*&EmC#mnDeh#gE^8UTsjtnnUFvsfKXTk9vz zabuYe7bJ9cGsIN|-K3#tva|WCxet8<|$Gj_=kiZ6v7wEwhib{zwT%5479CB5D z-E9VAvs1w-=aqpfMoe2`!1JGe5fI-NnK{CVE&1NWS#R<@8@e17`46DcFRL#Putv-V zYmaL3B-hN)`zyfCe=(Ci*z)QX15j;#Y90}BiE8~sQMwR^3)`?+1(P5I7o{Iv7I#_& z$QJ}#3Q&1v0+k$+eC?}JF4b0_-g=?f#uuPFb#C61q5=Q?PVArAclR6f&NF}6xJwDP z5*1W0s6ScdbTa9)m75421o5se9~?<-Z`q;PJ`Sb-#(qMKtY}xVfc(4Z(NcOKlh0j> zgru}9jEXu4b(q(+OZ$wT+AcMU+;TewSW|L)Gp^*e6Iw+J4k8a>Xq;oGfL>d9RpZ}N z$>lSh=R-udWmjtQOYo~XLsLh-Vh^i{YUwFEGu?YHI5*IkWgfN=n3-<7RSDH7--y&> z!bFWf+S|4%6%?&g^;jErO#An+(8vSd)W1=Fn|C=7xUm zwT0$cuhINr6%;tecW9)UWrxefc05W)4#~D#BwT5wPE789v{w3eNoQH5o?p;8L-c!9 zJ-13NwmFiMnZ*IlNUY!StiTzIrg?$+zSZV9xJJFnI-(zao@-fFw6Yuu@FKv2Zr7kK@n4ct&5kli9Jzab7HDs%+gz`a@Qy4C z8r+36TzFjBogGw6JLg~Mt}LZwTkIj8a4Fut4CMYu+8!boZAa+R2;4I(h>Ln1d8sx> zh3PTnCB?hj9uh3@0&-_yoVQz_5nN`Zf~39C0sKU${W42?*SHFU)#+8boj`TQE21k0RnfKy78Y&&`nm8kKz% zaS%?sw;6>t(?me=sS`#rRNl+t+(Y-wo_1^pKa!(2QtP7J0dFN4bliGMt-@bqGW1fe zLwmdhHPslB^)4U~#;aZp;=hf#y`br^_%|De0;PTB#%p^P|LDg^O*pn}J#ES_3cFuz zt7xnn?kMKlv$uXBrT$A|24uQ2Rj zMwp{M7Qr!C9=FCqL49i!9>E!73a6=DF|9l**6t`%ZruDwX4b;aVAbt~t+2GLcb?7U zTDAOMPL_v4!-YQ%f9Vp<3u55$UVpy-SHW+5ET9Ah_R>`8^TGPFS$;rgNWui~<)%)l ziPkLCGKNMFFX^D`vSOypwDeYS8=w^i`Zh|8#aF|qp%QR7h8Hm?7lnHEMx;`pH#Ix= z;#>4;8wh!;Pk&d3H8k$FoY_S~9?*0Rl&h2+TO{TF!&!8KGJK8P{ZBjb}7!>n2IRo-wcAFKLMAb}=V z19K7lriIlXj%VbXKj_@(?wEb5b|dI}3#v$`m2YjBvW$0if@uB)lYvA%B+R*<<|tjO zf;TgEbG8}tHyEo2eykcJD{#Kc{2o1jL9$S1Z*X8KG!=(mDWTdVbawZdv@Mmu|EwN3 zR@bbUdnl<(@WKE9YwX`)BCYTw#m#vxN=+N-*Y#mxcLk92zY`CL%SA=ExS4fzg%Mpx zM}r>7{Z8aI;rB|)Qq;j1Q{P)Vj^csK+1B{(YD)?E7VVm8a4~BhLh4}2^vFdlSwY}R zbeZ^NJID_}Oq-d4_EQ^XQd2oz3ao+S8#lbaTU(~F8lH@>H0A9|o`p!9J1|9#G%C7| zKj8l1{5TnL0i$EeYp~(K=_ZdA8NeLKyR0}&SXWL%J#34O%IKpfMGjpmhA`~XWSNUU z) zd|U22h{&1K??EE_b4hXV{EKSVoW*b+{NuyXgZ+3*LX;n&>%EPArAzQYA#yiY{Z;w1 z@9Yx0y)zF+0!toj3wLi`_hk}y3oE9Vstei}93U_73ySv<;cE1f~M2ucttSwrYL!B__UP`C+sGo;X?6*{q`%(A=}_m%-j-@?@T;Z^}ThZ%H5 z=L`0oM+4IRXV=9w3AsTSISTAk%Lu!+a7zpA(anG+Z+(GUTARI#zGv2{MLjAhumVDn z{u7qJ%Uqaj1mxpp@K1b>o7p9!<)B{44+p?QaBEkb9GZI4$n8j5Z4m$KNH&=}zqotb zfV}wT5la^U>bpiyF{>n_d&my+s;obLaDN${BwGmNk-RCL{M3@HoE}yQ=RRud$SLR`Q^#;q zp`_2rWkQa}5!m9;Nip2#o8a`!fP>PrP|*3O#n_B+LhFAzjpMBypg_Zg*s!oHtKz|+ zhlyLe-1l%NZJ#7g(Mz+mIAO1_N@yyN5&2Ddpz%37Wi;7dNQv{gkag**bQa8d%$Ucg zQ`W!tIkO5%B5`?o(X49e&u{H0j)D@~n?F&T42|I(>QbMn+~_7}*&vwkBcu~^^j$*A zevswdq@o>TPPMuH7m$7L=#BC*f{OgkvsG5NnK;+XLLCOP;1`z}g6_W(I9v2mEt|PY zPayOEce(Vr!r<`G$%1RGWWj13i~3k98pSx*)&5EAt-hl z21(Q_a65;jxPlCdwOcGx*9TSil_b7e)Wyd>h`PT+!!rMRb5gDic?b7w) zJmMtUwicO=IG&Ob7PTR=ZYS&?z%~b$UFCqL1fSteI*@6_wb4LPo=|OdqD{X$H)oe> z$#tSnG3~H+(QEI;Pjs;s@I8@L{b)n}`*Y}5Huw) zP=-z*p%rW0ug}$m(`rtSuK-ue1sXOgvqmiHD`Xf^Z%Hc4$@AW%u8bGww0=O>b}K() z6BdSzc_i_34nBFsJb~G;T>gseI|Y6fx^}cJA8`MxW}{&*5TgnL6`&y$q!?)V$i#-6s2Uv_>rGY39dz+~AIl+u$|i|GHyHoleDehO0}h&7#GQ=Zlse26EYed2y&RZD31e zWaH~!_RE8PjjU=2f21^NRgKLI3@4W0N}I zqSZvx0l%SnhAhQ)rkmI+XmdQ71N7iK%H!Bzco5>Yg$Fs5)LZT20q1^d0n(8r=-m-% zZkEgKJs_la&gYf~qeap{dF@#U#L5k&{_?r)fEyX`6}_i|OxUOQ2tj?4T%FSdg>0b0 zY9&L_q;*FdZSOmTK;F@+J1&-!cJ7TPdh53x@W;6I=@R0|V9$LWgT3m0SM)kRcqAoj z|1v2^sAh!*PZyOa5~pCC-<6S(+U8f@FkHabg@~H_pjl8yD2gjkls|qWRpLbXc2EVM zLw#Byx#&j~A6s`(-R$5OTYZ4A@?S>oa*mEfEfevq&OfOmpF>f@@2+F_ERl!0C%XWC z2~WLCS)S8z4(bBm9{u0+2V;z{@0e8IafAm5Et7P>*r(GBgX@N(*%{xbtTwOa)^l4; zorsj`o44S-DXM* z#Qc=!xSNRiJqJ4kh<*uT%8tZa8wC%mh#DR&#Kw5k9vXyEZg<1o`xF# zoFyZ9FzmTCSjARWaY1qc0OEqv5L@d7xZ&|3L;fCcmbcaGxA;*Nekp%EN&*l* zODj!CV#?#sAfk$skN8+)?!Ch)(!uNsikl(^`bmlL^&~O6nk8_UeAzC=1Z94U5_04H zcVcVTaf_2jLiHU-6@+CYxJ!XNct3t6B@3OLgzrMMr@^Hm^k_}dkW61QCE8Xqp$nOn zYO>1N@6iIF2WoHMljPu8q-!tezwK{v<4@rKcvypAn0)I4w2s9o=fS}Y%8V37-YOBh;Crq`Yf&%VrENe*=8di{9&pa%y$P-c9 zAkBIp7`c9WPCmB7FQU*=?}5Iwy)S}>7Vtba+5?3KPXhLWd%}(#T!~QdbOucQBTaSw7*aMAs1Rm?{oIq6uB zDi24(Q5$@)9X#Yss>RsBD6VSA73SK?M?cOv(ZXoimy+6OY< zVp9j9KwfF2iAq(JHE{L%N=A=3Jr_$W=&HwnN$FT zgs8?^O<%tU{$ZJaVDECvRj8j_3vQdbF8+WyeVmvS zH$#sPdHn}> zM5V~wrvA|wbZm_^Hknr|)V&yBYNmrdRxMEKlF+HFTpiJr)|l57T1Sb?nMSj4krd(+ zn(;9YUL7yAP=-+JhOKtrGwnRwER=Y>S$2{eZOFyk^az_hz3SY`BFbTCVadV1k~mNV zD0CYnpQ%yw2_(yH9eX4k`f^t#f_vGT)a|I?J>3711;IFPe)?P8!@8Vq+=h3#WAeTx zAnA&ISSl?-`%7mwuiI2wu3rf41wsA$P-{W^l97_=>^j6O(h4jJ)YP|z!w)`Mg$BWj z>~flt3v<0+BJ7=OIm!$DZPP`B#Q6$bN5vc7XYNsv1;%zTCB*E~iKQ8sEt0f$T__{- zCGRgZr4_7(b9ABQ;E!ySLo@LU&k&Z;oZ@zk);&bHX@tY1M6=b)AvUb%mSeKt1!r`M z=D2joYMW8-ArfASy4p5O?i&4>%J?mG;MmIfFXEyVR##)ENX%rNSMwmj<*dQHcIIn4 zk0z+v45l{ZG=S5j~L~j`Mm29K~TBR z_@!~!J!}~9s?;JdHV|d5=87otwb`7i7(4?=H9!pA!*S&|BT`525%*-r$uPkB`z74u zgS*^dI%DV=NUtkpM0h=tV5Bn(P6LLbSy8|qMvVQ!hf(Rl+IYTW3lB(j%o;%cDa`)^ zrc%h5P>%XeL0XS?3QzShTdthmsG(gBFp?AMy^wpi)V#P2V?4aGDChhlL$5=WFLMp) z3MBbwQQloHZitalHKe5Vo2ATaGx2cR!j&~0s6=HLA)kvS!QQ> zBzLv=!CX{59*<}J`5OZqH$T(eVaw5ennJW0cr?}Qe0Ch3gm!eqWVVO<*#T*X3@vWq zTfZLikgxRCPo%ddufKXzxijTa*sIH7Sc%CA>lKfzbk>mu_sM!e7>7?1bb~KI4!X+nZMxLWij*?FmjSHAR-c;o{O^2B4|x5ejQ{GcrB!*Nnv8`9c#3fz!}w}i%ZNv& zDe;b|K{MxD+=}Oa^D%7^R^bcUq4w#(aSHt(dB=TdOrJMF z33S;#{)^qDx0>QLuMUeex}!n?edC0OWfw^=Km8C&);4N+r_cVgmOlC(nZYo;E>v8N zJ|YUwa7>g(XupY&)}}JB=2LM}C(t9d%~!5uuO|ECE_NL$LSI&sC1H=={&-4m%6&@J zAos~`(Xex1yc`+$hyMkuV&%y5d3~@4wQ0JBe}d{DNsoJ`dE7#O)B1*We3~5&13kot zq1gN)#ZDmJ^~H_RC}E|gA*~PVZv%-GV#0RL@_A{R zkEAIc1A*cV_Oe%VO&m4tL*w zI0p$So&8O<1U$Zn2KKQ2@I5*=!sD~P5dA-?yL|Yxj5G;AKo$d?SuoWeiPR*bTVK+H zfd23*)sViQ_F(<5IB83cXNq9qPz!x}EeE9Y(J2IW=Ky`%1zu)cED1)(fy8Sk^l9cU zz20H-e|lw7&Hnbs^O7u$qw3)kXqdVxiQR0ikE^~jrFB)MdJT{|ozzCRB<1@@?yy`0 zpqQ7f=K{0T2a!=dI(rnE3Fl;58iI!#TCA9a*;*aq=yxpo&DRht?U20O8t>g$5>{t4+DhoJ0T96C19NC|YiRF!8*fP) z%F+2mXF;l6R{YTFC<`7sA-@b6qqwz6! zOXYFiw@jC>dIWT}TXF9!0p$Ln;4s?aDIel~FKwIx_|A!Ag^0X%7d7#=tNbXPn^{4IzBD5*tx7(PTIOPxyRAK>V3+{*@mbnoNA zHvoJ=djaB$+P}XLT;q*;5LLC1O!GGJUBY|FP~w*kb3u~a**3Z&BM}dQiy$J>RnTzD zDeli1W*=@94l2&uw264P_yZ(lmghK$-C(_< zo4f{QOvhCkqkq^Cw#pM8?*1vn=FWPeDY%D3C5zb!LwPA4geK!b=4VY&_E??Q2}g$- zij()m-e0z|o%skXti*nn?YGL_0-BMs5%rXBR~R9P$B$jmX`R%PImxOpxkKCJ4X-Gj~!$hYR+{G@B~9P+WDjO^?-A40ax za0<2A>pXnO?4a3AfyT^STv2&2{`+2Ce*RCRp=mo>HaYT}Wl1$+E-Yki1_tibt^R-; z;W!MJyt0G_Jq<8gh|gc?iU%8ne#fM@JI?qOHf712z$PrE`LC*Wl2>6ue5Mc{>I)fA zs{F9$i8wf}HWY)xJmkOnBuo#n6Qat;#S6w(u=SzhAE}}EoNzIz;_N~nY8q>AB41wzI&(M(+2x z2;99Bn&fGMkoC&k??#xnz$WeC8&)sHzt#hBy#vpdTtOS9(_b)OKwzN%t-=}EXm{>*wkeF-McMG0>1sviqKnd$imC1 zXy?C-%tdOMKm|&IO!$oIW2F~^c~t+?=K%&M+diz-&clE(5`;e@LmG#9>K(R}Ps(FN z^6em@U~4Lo#2xeEgRiU$aMmS8g5IPQ*ITWhWcY%&mUruomBUJ8r2Fa(qsUR7W!d>A z2-8TufzT?Cl4f^6OVUzi11vzZ)K4xxWcB=_o7>4^&_AJEpaR(2hYPTtyx0QPHx`aL zmbJYSXc^0zEuTy_Gsj&u_6-l+Hi=ciwsL7YM&e+sJOu93H+ zp1(IE2Sm8L)@m84*FFDy=x=sEh-dng$Z@iloLFVF3f4dZ`Wnh5M?DsnaqV~~8A{w6 zmmd?83p-lJY!W>USo|JZ3Y}``@WdJEQWnid=IxqS&RJ2-`JMYZcpQ{$csa)<`xO{u;+DJVqz=&Y?>Ax^o24bV& zepSq#dt(4OfyLW!y%4KZg0H&Vv-$I>buR-SQ#|eW7j{P7ZDYcK1dq=)VN?ww_GSvL z?3?8MWGgz3K>Q`Nk?i9XntqRJ)F5|wzE#Fc$pIo@Y5RG9{~2>O;w&dQs`rp@g2wz| zM##(kT#n=sbG%qYsQzXS4^RDH=tCkl>G4xCbHifKecD(1ueHg;|pV&aE)7- zy6U2P;H8iJl%1@`a^tx`DTcB#C9r90L`qm`VfV-4DIjWS2mr7+9|EAF&BM$6kz*$j z&H&+^#imk_isBUxh$b%pGa2v$9Q3iL&5~k=^$j_Pl~`SVTp#_cw)j$8^6kCZ_F~M6 z?WBA0wdifWuu#A1UiqV1sWJSjZ(^Ik2ChSmx!xJY8Z$EGE;B|5f*WF_4ix>8)mInp z6?h4>u*vV&>6B?IyLyCjVe}L!@GKc+rwrSpok0-{w13LHa{>)_oPPSj`dJgCpaQ%n z0nEDHtSsGd%l&D+iFJ;v1r=&~$aj?D$GS3GaWkrwV)^c$4AcAituvANwG5x77q4=F&TgAysj8%OLqcTbJzV&=dcA=x@(Ut0i zD#KOTUOYS#fv!2VDO;`}uhy;UdxINs-m)`^MJGjtmq;1Kvkev`qaIkwEtebSwSJs| z=)W)WUm(Mn!(AoPzag$zhb6B_`|pf>Sd!%RWvTflG0+Fq<$|=L27#iE1yy`pGm*%* zn2~ZY_=Y%JRjfd{Z#_*GSnzD>%cmJLTOdkyx;1bl9lT;Mhou>0 zJ!ix)+xMzvM+V}!C^Qj!f4NQ=4EL*9Zmc-kSQ%-$UHN=%88n8L_bO!eJSPbyzfEno zvg@76{ANvG43pZlOapQku{96WgE%|UXeo6-sNb%yX*aa|qDA>FDxr8@wIiU?Tx^Rb z5-GQTY>G93{>u0ZJcHQ95Y=yQ5n^p|)y&MoH|1wewc9wwQGAjVoY~;BiSJf*PjkA6 zVo??T5Pzn)hW4Pj@y9(3eT`=jYD4F!yE>xrlQaXPl1L$)><=KRoP2h@$)>R&)y3a9 z6gU)$485pe$qG^IX>&S**L(bf$gc`>~LVaj%2KDsz#f8%bEw%#MHdt;y|5z4H{Bd-=%8q=uKUPyIV{(F(vt8 z7_UW!Ndb6j-lk1QTS`F~H#NiJP=LX$pQ?*8lOTb=X4>}2ec4CjDCGx@M5ndQ#5as= zGom;(1X?;z`?a*+Ev!$%l+qQ?q@Q{I&iqGWFBC~BTM6{l*29IlCL;x1u?WNbsOPr@ ztd+VT366}Tkr-HQeTYnj$<9s7G`qg|XWS)mI&aUd9XaV_%gB6DJp5_y+Y}p@rtrYp z`YpD;?#o#>zS(47OxNA1Hc&;Zczqb#*9KDT)fS{oa2a+{`CM2-@7Z}rCD5`4+=8!( zb52E~JOp;}7ONYoxZLgU&5)>w!?{07T)xg6fe%q9O{dW8j#GyFc%@E1b&;HfHrQNcq-o++j5WeI>27 zJR*q)Y6nJwk=P>}Bx$cI{e6vFv(9B-)+ZZW$OB!7Ru41z?$tmp+g=L7eieg-XWEUI z91iMR>fRNdWDDBu8SN!~DssFz2O?#(6>TSP1LogJlpxi>Xektu4ye)vCGA>QH8E`Y z*Ua8BU2)ofUvwH+BqiYbG6-+u`mzm?;3;cZ@LloC=lmI0RzlS>3?4#Nq+E_88;%HI z%LVRbhd`v&^+?XJTypQ%r)|V`#!2qY>rd^m*cDhml4CQRiMb|WcupRc`Ge1LlqcjX zy@;Y!Bi`_MVoitaZg)$jRQ(Q{xWe@7j79Vz~&T}@5z@VALS`+f^uR(M=*X)_c(Ud>N)Xo})MJxMttTHN#>YEz+5z49W z_mP|s?st?Wc{g4t~)Ru+Ou?*Iuh z3~cGPe`*!{D4tW^GhzM7n6VQ8zsldk);QC!3Dn&0?b@;tTN|ucUeUn|Ry_XPx>beJ z?agUxJQwR!*qAP{s=m5e;cx|ue?CzjhkDcOnD0!4THOS}OFUe)2Z>2kU&{e$O?+Hy z0)9vSQwJj8QQEh&=Th`bTAgZ^er3|GBWRqf+B|KrVjN-V^+uPLn`HDGSa zx%`_qqiuIA{7S2JE`CXfsva&NY^3YUw#BF}iiDj2HM3c^6CJSxm7RpchlFqhGp3qG#}T#ssk&u#+`*&|0@OFP7bOQ*z5a}tzH{gyApJ2D)ZUq-EFAHm z_248KM{w=_sGAh%SbcdipCnU%FH*IAvKfk;<^?f(cHS!(;i#{_tC_Re%}HbOGNB+2 zg4<>Yt3V(?13_4tp6SA#ucqx8}8-RXe1hx)BM+)Pu8sChCmk97)Y{0d#vg zXdmdE9{u#AdTaL}s_Wz8aB?dsY~m^>xo7)@eQnjVr7ksXc|Wr!4T8_` z`JkN~rb?Da4vScOHE1nWF%eA(&)O&@9fq%+-H`SLwR$BmLQ;R`@GVBYhPXpAnULc^ z13=?r>u6(`z`on6ianIj=a<m*`+XTr*yWoF#FA`Pl_ z?lceSi9ZP+mox>iWIxALA?#B%m*xy?4%CB+qYM!Rrxy$%7~;NCycDGK?fLj}9_o{G zuH0AOhLdN0tyItF>2`3_GB;;e3@!2in_f9}d1vHGJ1z;7{?bAWCL63X1Di3USVDLg zExqG37FeXtcM5`PW{Gkibv)|z4O4@6x2O#iIz%VDEf*l#4A0JrvT4?P)swyrA@L^o ztyv-+hJM-bN$Ju|t_q#UI`06F0JzCKN?A*#8FRmkF4@`M8iY2nu9gI~tug{sb0wiZ z`2q*eK)e_tf;Y>u0_`_=pyWw1>Bx_4f=ju~M;0a)5MmGu-2<=~NqZ{m+inh)DeYC)GS9np!jiJMqT_RC*?fo4%!+?6LPYaBD>;<7(LXn$@nF zM}TRft92^5?)#Nb7p-nqHSPrn_nlHQ#8U~|KEk9(O->47o;m0zL)9cm4?|YY>s2xF zT7et|(ywgLv#xtGdx9aJpwhsKO<6CT%AG>2lsa`WTsvCoy+H)l6&aFvcGY&ASlOcx z&q7V|H@(7l452Flb$Gyk>b8Me{?O7e3%T*2hSmT}6Z@0C;$76O)eaqx)&7g8cq|PA zT4!O=UC)-E%MBx!zNLgRjP~SEsDZ|iH#tEFV~J9vWI{KdEN$*tVQPK~tm%tKW!EFY z_{2T73-{L|F3(sxq(w6s`UUPwg4pe`btTp(k?) zxlw$p2(N<^g*D|{k~d2tJ;U&pGL-@|>1ix~rWds&?p+lu4!iaSj)NH+Tn zUf(LLSKZgpp`ufXNplsYzKo}V$F8ed93 zkujJw8r_mk8Z=sPiWY;dL$~}P9pAU1YXFZ&eqv@xeN4?d4woS#As6MdS$8D~QNbFe z;OybKt9k;m$F%X88g*)`&pd`8X=I0{UmTsBJ+QTK($^6E>*a5K<{yEH5Nn%Igs7*9L}v$JNK>f zCvdvl^%I+m(KKf42#nv3lYYTuN@@?8Ib#J;O`VKb7{j)D_s9_t-5Xb|d+c-#IRY6r z!Bx6>_CT^>gr0%F-BOJk{urp}2Pd=2c6*|*%adk3k=lAv-bUrNQl@lK5p1|IOd7*NqL2F}B>HL5`sPud@(;gVIS%iGC!@w_Dg2Mc z_%7uaKdRBfiNkWE05(<>qpu_~=6rqKBDxH9X*y`#YJdCBzT`FMZ3|`j0ppAa2!)_R zWxUWe>r+sI)+!zGbaw;|U^_^U>{vt6xwxPlsA)VEU*2CD$yXYIz4;cTr3C8?_R3CYySSnVEuF;5MgSt>77%#-P{goiS*mNJjG=c4zNgti8vV;RkNVU^Iv;7t$W zXPsh}Vkt7JDiQsljAU$TVe1s}JFfc&ap-APCj=f%z*Npb37eM~aGVXav>n!;MK|I` z6(Gf-Nz_OX+jG0i5V`M1oEm8|KQIEr%ObP zm9yw#b`j}U$B!2}fq2X&A51a3YA!ap3h)(;`s1m$vJT~KU}|~Faj!V)ALo!7HY+@% zqLnG+S{~lM`VIQj9&GZlNBCcfM#uVoqv52gISo8?o6nse1ODLniXsHt8k=#{lrsYk zid~U*uey^^pmw*o2|6Ynp<<$%Y|Q}4w)ux2?V?hiwPf4hO$0VT=cgpnZj>bfY7$*& zs5ekzGc9~KAl6;NTUnV1jo654_IkzyT|~18E9s~tC)Al(dA)($(oQmM-P58LLwE)m z1>JVjuPrL-_%lt-KSfwm*X9g%Umn}T(#jsUe@ez!HGktsI2Na-o?0NhHy6GxGt0;+U3CfKSTT-3 zoXd0KeaYOLix4ljE9zp925x|!i);=r)Ky^(q1uG7KIq?`FS!6dS;mD;1>4b8BoQlk z%|*#w0l{^-%c^*CLFaBG_dcZt3s^1X6*zQgCsTnk6UTq&^}AdARGs{&@BK!pmxpET51*mKexZc3}*#aCCM0wz7{M=vbUfyrpcrV7as&d=u?| z6QxeVKuR0uPNYD9UeY%e``2{wGOIHBmTc8Ea=|hnYoBWgv}4(S8@3H9AD=-R=wKI6 zd(6w5%^B6M$_4O-zn>wP;AENq;Tneg@r-+!bE3lT6y9TJln54Xt!rZx#W89rt()C+ z=usq{-GJ1hIyn8nt|SA;xS#L|JvDH%^wl4{+-WJrP(%jw8CdrDN_TI5Pou#CD`YB^ z8s8UyWK^DkAz39-!4}pYr|tyd294Dxb9DEK5k2&)WjjQDipV+Hx0mCuU=f0gd7dhm zzU@jno^Ag{sR&iSp=-`pJ&@y%XEAa$#CcR@d(z8#lIwV|El!sWegjzTG(L#L-#~jN zU&{>nbF>QtTPGBe`(tA?!INnBXLQGq_|d+&l`~%P!Qu|2<`YNi(9JB^Nj3?4iKfT% zE${YW{Ws}~p07DkVF-(VScOR&_)z8eG5cd0ZMg&yg*lvfDRY_}ss!`FG7Z(>R70|~O~E8q&tb1OaT zU$6Nfr&=1JO=PdvzvwbXxxPA|kWhZQ{+l*YmgCUn^gxDnAm!J{u5O z#{y+D8KDaN+f{!pWD>@xDB~h9uys)ZZNGN($F>x+S{<3br07RCpo}oo;i+BX9!-dj z$UE}#_%JG%JEF3g5* z(lbczRYT;!!7R!|yZ#@h-YLHB?u+^j8{3WTq_J(=W@B5wB#rGfYHZt18XJvm+cX!ssawa^TwT7V{7}^;ADGz$s=&^}bm$h6n0e(o!@z~>Zs*FLz-63O zq6idtb~#@ZP|>V&xK!2pEU--uZk!_WN2p~9<*6^{w{bVuuh69f3+G^r5T$pf?;5LO)xunKi^bB>N|GOy%u zq$*0#i(jUB{5>6p)2x>_J}%UKJHJZ>40g8{FPVXJ)`l1M!a@M<> zb5TE3lEMDb$s>TtoGN}Q@6;@DbU!k;gV{FjAi~~NoNmqVZIv?4EP+16%Ym2UM_njn zt@AVYeB>t|*#A3^louob zuZec$hx^QKoJL?jh1h=bR+tb_JC*}{dr>1Zo=+mxTxblrJDS4EU#Vp`*?|K$`AGuq zKWK|(3Nmv6C=%?a5PtiqC+57nw?mKY2^i4L54<2Vyw_g~POUz~ltG=pAWTH&a8V%L zvFy48_wSCCfv2(~RjrA63Kf^K9i7TObHQ4zTcY2h>16oCbSatc&u#&ctJ6N97lbP= zpcfim+{YoU9IU)g*;O5k`GJSpFEr$?J2{?$Tzxhb(b{8b4RaH@IoLcuJ&)yV#}s+f ze>iGkH9*V@f}ViDLh4arS~vx!tGN3iNgimP92hEBr6DsI-Kwp%ewdpb7zg|Lh3qD#`QW9FFnt0}XmPS!T3BB7dJJZ(#THfcEt&6E#=rLQvnGfaZ>8H;SB+xz zh&GZzc0$%wi;Pw7e;yQ8?uPDrBie=w4~yIOlm+b)>~hv5h633D){0*tM>4BiLnL=~(FgI?^DD3yMZ!V39t(MWbh8Kdr)?v&JzgzV|$jLt06*R0YWZj{;A&rRX zu&2<^oyylao?BOm|9;Om)J;_m#_e!YCPI!}zzt@qR4DL=OvRJ7zz@hyJCG<|3DPcU zpmFqBP3bHAE|}^A>>QgFuC60m&l7_98nNd?91BI~6F6w^obcSAvvD{2O(llR&YWa! zA4JM>QX``?hCJ@Fk4?!dP4EppP%hV^kv~thivE7(%e`tStxWQJqxeVb=fH|Fw8t;ufg?^>GRfZWCzV*k#W#w!t8(neXDr+lAovzJT>JzIjmb2xYqp`` zgSz<4RZEYJHK?D)kASAr9ioIAMLi$#t@V0~ew%G4L?&6s4Cff*_ z6EPpr;q;dp0KDDgqt3QkXRuc|aA-tB2cSN+6A20PlB&C$LRHu&u#|2BKBW}f*AhIW zdIMCv@)vF?`&ce%bevA79uI4yyc>3gcHY1M&uf(0t(Mji?dEb4O0o0wZ}SWKJM(~j z5!3S6u+_+Wlz*y9=558?x0DBr5sE+8uyjhGlds^90HA#iqyJqbgQ4eLTA|Qvq|<*3 zg9l?iyKu?)EBG0~xy=-_(z>+n-*ZGzX_oj|>ueonN3Yz#vIv)Z|Df0gg^h4ZHceI> zD)~IfU85@$DOI+WsDa}YipOo0xWyJVh2c7z|1~J}j!Aw;@ZTB5-4)Q0IO}P|7TB=ICqCI!r=M|0#{2iNk|&2k zs;)t?>6bEc5iv&0b{k&UV)tYCS&yS2{fzjJTROE2&q@+KrU+}J;L3}7Tgn7+xcAr0 zSG_Vj{>>uDKQZjb$0z@eAx4WH^%k`Zl&h*;fWpU&n-17G>cnlHO*cou{%TP%cQpPV z?&`MX?Y6gUG6{2>;0jAwh<4~yh~f5wq6|oq4VkY;eCoev49_;I%PG5u2uMWN=E7lXus$i5W7Crpea`=7i1WY`7rR$|iEhcF z$TqMJ7ks`3M&9%P*p0J7spk`~O~p|&fbr24JvuIM?3G0?8G~bSQO86~1NMc!uzN@O z28>Zp+p2xcwYAd{o?#hvh@PJ76ttA|-%si}1Nt1->}?tz$jRRYn|A9es4m^aPd$QB zL)ZPC{&+D9Z-H!i^~gCC$9<3M_sX6WqJKq29xDH)3=CtlQ7CW`-p6TxA$0t~0OSI2 z)Hy^NB7alzIH;_JY&@eyiNcP$8VD)j(3s#|D#Fkht#RGg@|j=)0W~@wNA52R=DEBB zRY@L<_v#CTlgd;AKP{76S1EAax)TLYWFW=1xg8%_8ZMiPL9nHtfF&`!ZKPd7eiCsUigX__P1&uF)|T%BKUJva=-l{kymzAFTNxOvCGDS`yC@1{<7?d$3^tOM`E*)g4lF^=y} zOO}tx?4d+)sXk03%g8u0o}F~7T+|7g$#yN1txO|aW3F1MZMYdb)Ui)KZPDnCDm{Nt zA=sB3(~*AAp1nL9(f6{k1-b+$PNnVbcdV=ehJ`S-Ioz>=R?4`XCNz>SHug1ojWfzU zhPw)dM(h0BjcV1Mbda8X6kUm0_(O8qk0G-lWkd6~8G3P?>E0GspGnFXI$HL=KTnq6 zyzrs87mmh(PB}fN4yA=h;KxW`7HZC7Fa+$ui^#IF8hHGq_|AVT3V0gNKPZITWgj$} z9Ca;{Jg-$ukq8&A&G{DJR9^t!(5&4g(wl z*mvw}kLWN)q9*i@Yar)^xcV6KhKy>NP&#!?ot0W=ieJUMb7$qhHPrUxI?kW$G!O`J zrVi2M{Hi38&QrM19W(C5D$oaYwqZZ$5`Ywt~PIj|gKaJ8|eWAuJD%%--n20qb{aSx(uVIRVvM212RH6b}5WXT>1?wm7 zh4iyiM6OjS)|Po7hhjY@IQsck?0Yx%Oxf8REQ_imi1GVl4T&=YS;X;o(Srn^Y<9q}jafSt7W!Kb4J>{Q!V@BZ4R4(LZO%8Y z?V$HJH19$v?)ML-lYH!xb@qz_(;Dm0+WP8H$)J2=wH$$P|GcIkc2&*OkOMM5C5o)=}mBH*~ zspAs&l`{ET=3`wI9*g|Vh9*TPr$o zLyD8sYB6IGODeq1%Kj($Y!16UNX$TJBXE-E+cuTU6$`_^8wYkU%vOF|NtZ-LbS6GU z*d!_xAO@u&hwqux=ExK#~4_!r@?0fv0p1u-YMi0y6o;EN3aazARA8 z(I|)tDi8Sd*!cmx_(;@(Eco*Qz@E>S!yD=Q>3|&a55*PL58nc<6#8#~Ti4lsM0T-p z;qbQF8ZU8F%3n&D1ZJs}KFS@#)iKKtlEL(?m5~Poj+s;0=ejun#Y2K?THz+mtMt#7spm<51m;2;O9nYCIC=FaH!tWQvRp`0b zd#z5mSf?L0?y;zXrOE!oB7d#I{=Z$mIO+gqg;=ls-%JW9fPE-b<(DNXI{VlHYG|xG zM8~mbR@4LOWHlqyUwYYeOrC17Lc^o_7aWoC4cB<&jdpd3w&@l4Z3ns7ILU8nwemY# zMCgWKEv=!=yWjckRV_q`XtwhZH{*WuC%d7qZRP0t66tWW=;pEJTi&S4sGU{@k-9^JVx(y>+-r{B(Ukse5KN`K~)t)36l& za0_gH*O<}p5e~fj01K67HM_A@$+b4^g0EW~CPR80IOpc(mY<53F3RbgZJ6(7I!^xfO6CxPh3J zvO3txNFRjgN}z)tIbbN2BNeu$rykN7sYwQM|Fu zKU93$q0;q7NFVb{fb!k-73U%?`(E5H#VN27Pj;W5UB@Un)%**JNQ_k-n^T zsfSMLL3r@B^^-q8D;Vz@63aZd%aSVdm9KuNKxesaNuk49QX7}}?C`74I$rObP2P}9 z7&VFj+E5iz`yQx~WSqui*wZ3W(wI!sBe~Op0UbhowPu}A#ye>YPa(h+$*Lgf;TVm* zx4jkw8L$k)X`D4KD}ee<5Vg-7Cgd*QvOrCx8T&AaOb$!e`gHSa zHC$Qyn?sa_GHH(>#Hh0hXzU9ZDB@q)YPA?%Ao+me4x1wuzW>m!-A%(_%AP92^atUM_vZWgG0UFU>8f z%6aRY@ERV-VVcvetu!Xgi8NuK3*Qqhz?V#Qxbx_;`a#23I@HkcRAc>Yt-e9`%X@IX zHcvf5U!|{2ceWJ;O~^H%rOeo7vH4G^Q@)x~el`&~{1i>H4O+u*3F!`I#Ra^iG2H-> zqd(O|<8e*L`G1OeO)cfa34T| z3%9fqfyfjJ&t&|bQKCyCab*zebZ|P|#Lv$>$*4eaX?Ka5r?p3P|GabtAQGlNCBi5R zPpWB1)fG`zoEk@$zhJW2j6M>!{kh4y9Ne!tT;0@GwAz?eTo>n`Q8%6L z8+51BBe>&RSd~K4vg0L&D#-mdMcYDpuwFbDPIJt~DSVSEv-cmIgtcHZWUkhG;8Gx+ zESaRj?O5gCj*tO!`MwA!W83@?XuV=(_^EzWL)MAwIw@06&_FdpEu9G&eONoqg>iNc2O`Jv=!+UtEb1 zW2i=H4%A>jd8vgLn#W|t22m8l4nK@IwmmAKU69t(VUA;M&`t;_J2DDb33hm5(q}1> zOJtzoNZLSZU1ye*KcbEnI}r+hE(pf3zAZ^tXK&dQ5YHkJ0CVmCM}k4G+vzc zCWi9C|CxA!0%RvN6BTqJW?X3=s0Z_<-{urSN~Jw=!753=frhbOf9CWxxp>dN{?-@{ z-o~ooM&j`9Fe9>JwId$?s<7>SzcQkQDw#>-r1$>{B{r?aX@+F|K+sV^`IE;&)l?b( z%Pt8PuM9K)AS_XWl)~S0qC6|x#0YUbxOXJRl4A`YILOq@ClN16bD?QaDtG$xrrPEb z@d*8N;ZbsMxWw)Y%OL=(T2kjNX(TKXLsSSywF^14i`?msYc!itGHn{*L9KpoRjI!b zQ`&14;z6;UahK#H&3L7p>#R;Mva;T4YUx8@r({DxV-D%KJWXk#(RP<%=Eh~sb33Rq z8P#|Vu+UqJoR7`*RNXZdUh#lKbEz1&=qe%*faVyN9BT*lar8H#p$zRFKO+($2}SoW zX7+mT%EzeR1^)IB>kn2Z!X36`ZY-|Jk(Ng$3J}U zv@X>9F2K*oZ%0aM#u6&WkMzT>Qr!zgz-nO-$Y%Sy9XUYwyPA(Y-CLA7M5iiX0FV3- z$?s2)+8|l*zwzh6pE51%Q-_R|D7GAwRjyV??2dDDDL4<=>6ar_6OC=1;ir1Of;(%H zyH|QN$&r?CI?|&H7b!NElL96nzeCxRXFqOP7mA-mXA)dWTddhOq?S{?u<-SdrKe;d z0lJjE@ZJfM;dCsE&2ZxeSxnc<`$7M_`&6YBwy%87(P$BV5?;6?xP`z~$d2`oCh?LPIM^@(p*$CSL9z0KoN*BxG)AII|j^ilPNhpyvp!) zF`D1XxRRZF_2@At9B!wFfMHwHF%dI708hr(DhqAoE($E`aPE=n){OW~|6AS%e0e^1 zh;$iA97=KVMcm7DAXq8KzJyg;jvNfa6_aCMlN0+ajmxZp&jc@@TQk@{XP&0$*TtZ@ z`ZM&>qMwzMQ&tHb&&mp_nInc{$MKwBjzv3DBy7WBn_$%1hi;<2s?NJ;2(Q9(fWsu@ z&T3Xw9P|H?K+RFp`xY!JY~tJxwG%rjw@37e4iFL-gV{3f+uS4nFZLZ~)GIial7$cx zIAI<4WFvxgZP7K7f7br7FIMN}dz}%Jpc*yEBPrnGwxzASMRnKyKpe(?O&+2?ox2(# zh|{Yckeg@h4{wJGD4De&WGAg!i1%FNQ4O6cA?@+_wb@r7PQITK-F|LF9J>KejOv6HLM83DVr}Zj24U*b~v)N9AUGL~W zFK=r={TCVKlTdMPv|U&16QUVS=ViS$JCEze7ca~^+5%sy$E%tJ)&-%-NOC)yA0iHy3SeUf|*H9`oU=uJNtcnrboAWT1FNj z;)GtiI>E>tvAF(EB;;I=-V8|?;{Dt`loGyPXtaW@2-XN+vOX;YRbD&tAW%dD&#Umi zY2^vD%M6gf*6TGnlyUw+;yj)bUf@pdGTwH!s3;l<`IeqBBvSjf*S-95gJmd2 z^u;f^sc)_lu^zG*SjvjODaXVML?f>~-IpoBLSW>cd>?!N5WWmrgWU-wn9=YGLq#)R zruvD*4dx4I3N8@(oH{&^gCMeQbzMO{MmC>vtRFUC+<8KjNEuU-!#S`G-~zw^z4EO?tnEjXB&Ft zD_?_R@%A45M}tiLoo&C+*xd*RUrweL?g`03$fCR4q`sgExGCvvV=!qt95lQBgLAvw zZ^{Wrl{ou-8Y)7`w#=WSM-T5>8Ol6sl*a&hdQbL2d5b0Q%+YG{mk9cQ?u8;)qp|U# z3Yi%F&3LYF4_+5p9amc=*Z%DG_)fR8kOPkf6P3MWXr;^Lmg7EHWKC zZUZ&)ex`oOycVjIHt|qpT$iVzfppTdqJ`G}7N^+mTv8OIVj`@VcdDaS3sAraaZNN9 zFTL3;aZ6j{n`F*uTLDz|Ha4%-0w0{*H6HR8>EPR7WCsnyw5{jO*)gKhwRSKxV_ldN zr*o^b@*d#&g~DlG?O~ha0qDx%Q$GwATu6Ez(2pqBSs@qXSi?yEg6}4!9|sT&Ky_dz9fyv405T`f1IKOj z4DdNX5tGz|TCz)${6iUA>v|&Cj%dBUO+sAq*eV$UPh_wW%TcKYq9v$ zUD36xjL=Cn56I$L|veS^yZ z&vOgYwKET;IUA4tl{g|%dcM53ixkNqj*Vnw&S~wZHHv`Jfc>%WzbNN@Kbk$RwGvs` zvHo=HEULe?^)M>CrUlUIzcFx*KT~ixZc|mwbvHBespz z2HEs?+Fql}(m6$Mp46B(ZVG~c0tT)OZ=;n#oaWB32+Yt0#gkEjsrFJFPiXR!!K*{M z^5c-}6iF8v0(f5f)dhO*vp!@c-m+#Pg0&6PN|#?f*zO1%oz%^7%9-7!PYne#!q`>v z(rW$$zMgLdy!eh%9H)(HjXrNAc3Gz%ws!cg83_DEz2-6?N`+e0_F3RRwAmc zl#y?+w<@XJOu;kuw~eTDd>#?dC1?TnELq>H(f`bLPgzZAB{Wt3&^a63I7m!*qL*RB za`@ag_}B;Dt3U6j-q)tq?V77!+p1s3fM-TvU&C+zwj8*QG_A9lst5Ed5&`A{cO_-78GGX*B(edN`ak-2S>_@r|6WI%z(Kx7GTn`gP zk06A~A6PnQ2(zK>Ax0K^B8AVr4aRalL?j($)pph+JeffJ%3KH9deFKkg$HUbay_nv zJd&-9%YsosC+Li=k_RN6#iwWeSJ8J}~q2W;Og!B(*Yr=Bq zzsEI>bg8rJPbHOOHQFUIJ7=b9^G82Y)M&^5;p%;F_m%f%?-E-rOx3dF-85jN6iB&Q zJzO+MaMDrmJ4t9IE$$Lxi9#TcyQR(H)ykI zQi#w)j8E}+;ZuX0NLq_5y&#(Vfgc8}E zrblM<+c?L??r*fOKWZzSR-FaPs=Ydx=XyD~af9C2?k{V1SDjSxk=^W7-20(^(3i-d zlhhgD7Q1U<5;m@cTZGZz_SZ=7GIY7Cn|@lj`GSf$@)Mg&2+j~J_VLVoIwg#b_3NqK zdw201dUri~cR%_~DZD0TzwK>)z5<{BePF?T4$poH7x+BM+lGJ%pL7v{^CMR*t;_3yjYIAr!P;J%dmCK%$Mcw&aKokP8m7>IFlU;gZb7H zSHmDlCrBRXBRHq3-8tTRA*utF9l7@a+~s9617j!_g1=u^elfFG!qZZGlRD@uQ}-7$ zXRAvQ)|9c$lVRI`r!geZOjOG7oE?U}>we_S`2FgJY03W;iT8ai;eAhVqv#HmZ|(D9 z^SwR$QngGQ}FxmoT|Efsbc!;?sw?UUmXAW@%2kU$sVkx z4x)lSgSw@5eEge(!o^kbz zqWp&}W&D->kA*l%E--)S^-zWwT~;dGwbB_9cLS0*^q#l7J_CO;s^HduEhkZE50bls z1SJd#@N4iJ=t0E9azjJ1gkDddvF@X7hD-vBSSa#jK0tJ7;xJD(cqr`OjqHBhmlcHi z*bJDRREdk{ofU7;3lJ!aq?s}#&UE_;Mc}wqGAS03N$oRl; z1l_ZwYKZfJ#+x!DsH^p8kmD-;an%I_L45x!g$YlDcD+cc3|L!(>`);MZh<%zceLEg zkB|`i>z=U!ZZB_iT4?Ff3ZPYgViTIIfa>L4c)SKyjOfa+|NE7C$-Cozl5QEMza z>zLZo?;Br@0=L}uyuo|Tt*Zo)baAbMtA6@!;kGADJURi5wCVKx5}0S{MllucnGq7s z4HkiKJ%JLe#-q*;#pR7F56PUl9c!5-b1AM;owS|ts@*}+fh%Bcx0MXUS`F)WROtf@ z;J4@Gpj>aHMq3jkj53^(xongu@f9=QuJy6=5}bbG1BG}CdR$X$1dUPO(26xwG9c{W zcuWN$T4shu>G_eM40gk?-t>qakEjYm;oJkpx~`;x2_!ZHu_TxXo6s#|r@5Cs#P^tL;;Iw(f7&a33!v7x ze7(X#0eD0BPPok!5&g*35Lg42tC`oh+maL*KdQiG=5h`%=+i6ZYUQ3H*yod*^uF$H z4b(|{=Kb5p4Xmz_n-s8^>Vq@xh}lMLnufK zHP2pc8gp$cChmy3+u>aUwxIk3)w*|RyLKc1;7$S@?ej=N;Klx9qWk%;{o7y0uM+-z z`YOT8*RD(Nx~$J@fe$Eu`>PPSR9Az-dpzLHZPji_7{Gld{LWF3BAXBx4ot`zAC%KB_Y8SwxYvBIr3G z7-X@68-J-Y)XRMD>+znv(RHZhB?SrerAMlXyvVX~oqFvGFUj1tZ~G{|@7#+Qm)ZWi zdC&7;Ou_9W%grp^+gq-os-6?e&1G%4x6L*p)nX}ps|Sjv?&UjXljT*GB%W6_F(Vq7 zLE2&XIqrk-7`5;q$#1;nJ-^#zPL$ykQVS`El+|FFG%1H2SH_j9Z;Z2(@6-*Ol-Fx})t$xh+x5RL)@bgcRXt z;iCTQ%7yYC#^PQ%71AJKjYuDA<`l5?!@<;Y-VeBo4eSmCm3&5+867K=%9Q6;>{RFK z<%*EK=~~M^`~hf2D<>RuUGD(;hfSTJws|taY~f#UEuv0;zB^^T#lBVDaP3^#2c>GP z`@=i;PlIh3=0-o^0-L)3_TLg@1ug0+`jmBM`&eg@%QKg4@l;Kqw3ui3FPX^aiI8HA zje-jyRWU5F7)E9xMVnn$Nkf)-mU$$Y#4w5xTz5zEME1YMi`b!EA^;}_d1hfj7V<(k2R z1q6E#dws#srm!O2xF%yr@%TX);S`N)b~Dx_2?z@6l%Meo!2xOWsc}iXdwiddxKiiu zexJ33uS_7odG&i^tnZo0{URm*b-2LC&?l$=l+OXnl;!AGF?L^^c3prI-I^7Q^?$9+ z6^KnFxC|dJ5{%v25f#jyV73_9)i)=(@x!^0au=GsDsTj zggfzu{57+K;ZiZPJ?lmK3~ZCy#8t-n_g&2c?+=O!9$!b<(uMG#Qec%JE4dY4Y!=e! zUkd*$Z%RjL1)xn~6oFGTv@N3xVdB@ellWRcei_O<;~zQB^cLCxg|M2GA1&h!6)7GVS%Ba-{U z3*fDt;>>vACR=|SgZ*svlS$&dN45zqF`V`${m%7-w+Z%sF$DCMn;0p^&k9G|#HH7H z9a*OZ_KTzxYGk2MZo^-g)UV9$p-Lj}BNi$ngh+Rb1p5apl6rXIZS|I%NtlEk=gV`h z@a;!(06+*O_>`|}o#;)6oi_yk`*$aMQ?g8tMQu|JZ!)Io1RRq!C;7o0J}h4tbcFag zTeNo8jA?050>jMEml|-J0kt~Uv3b-J&YC!8{`9^ zsdMtm-+c~#6P;Tc`>`E<=5^YeP|WbD0lko=8PDm3dwL%KF@sHcpPL&k+tme*>M9F0)0O@T>%w-fd~k5qdB~ z$J?DZ|D(h`W67LS>ldsisdU`XG@$4^NP}QI)%OT7}qnQTMpImLi zlo2D0&RbFMd|Qwq|8rV^Z7R53@7EZp4VfB9F#KOUhx>(t;%Cx|%gcntcF4_9jU3Dc zJ&V>4{z>aMeS@DtCYOpe6E^OL)r!-v7w2Qj_Es!qloWL!B)1X)p^)r_CyyB>TyvdG zFN+^s{L?sJ$F+ObY)#2e%@!qCbgs~wYVMzEhqL-{(2A{EWE)n12o8-mOUz+$W;*K( zY9xi2yaDPYx-zM3Ls))7N5)kUQO=>UISC^ALfENT`&i!~ksxMp*cCPsPXp{yo)u%2wrTVlYf|B+xwA53PVQr= zatl^B7X!`sV^c#uAD~ROPp^N?402L^D!zgqU0TB$4z>#R#e&yd&;i`+89Ao2hQqI? z37y5~ujg)(5vWcPf_r^nG*_%?^#eR|?3a)CajV}I^E+n%-ARV%t-U!^kA_;ZKgBhy z)4b$a^vCIMqer)XV~gO@BbtdR7jwJuENY^TCVOaZr)qXa@G1G~6VR}+HMO6DQ2qhxEes3re5#`RuF+hHL_9uiZ%ot)KVBbyUtObJFv6d8 zNrQTaU;y?Q-oHXe<@{muQ;j=qUfHm988ZJ`MNIWMyp1^0&~cRoNLkTyBINqwC44IA z$zW~r$4G5qQm=M}u!nTEzjR$aWNgr|*LK|X&5lLrZ(Gtnp8a1I_rW4?%i?w&uHa+b z`AYdQkX;iw<$K`_+$emEK%Kw!F~0Rxf4)?MKdn^1uMocV622jQZVSAveQ8!h{13JE zPa*bCaKJXB&n*XVn?dnrTkJs!3JUm=Oo8vrH^c}!yt8fHy1xQ#(|z(FNX#)QG;@ zm4p(|{EG22YO_YT&7du;HzVi*4JLebrd1wUN$cFRF-a@>7${7NZb{ENDb9u z?6aGxlcE-cOt@Ur@HX2weo?$}G6BQc!vKXp|J!sz_+~!!%%06jq3m%S#)CI;Jr>t9 zuCz0sQoE$wabK)c^vefB@dglKn=-hoIRm4MIaMdK$)o>4AO+Fj;&BDV+@wkE+LUIt zJfYi~fI_?WqpPACDzi^+GmYy(Vi zbf*k#3Um-#1ChMQVRaAe&#bj@LR^7PJ?-Ow-qVv=KG>kxH{HlG+58(i+Y--)Z4D2D z{0EiSUxjWYX}9Aig}Xp_`*@o?D-SgYB$Fpm=;qB`-B;stjRnjw_?Moo{}eADq}7hx#3bcD=TDzqWUsw)4LqD*(@_z;g!ii|zkw5HlO7+YURM zF01xBNoIS5(Mk1#eZ>IiADY8Pi?oaMeP7S z?}IuagP%)#N}MarO~-&rSvuKuRV5|_f=}aOj#LfHrHa6cgS=V={mT!Y7E7YvxlMK( zKP1B$40;>T)WfY(T%V`Ixf{q3Y2YLLybCMFjb$zNGYRCK9o$;8?L=(YfQCF3Y7R(t znG&)zJLpDjn`aG#`W~MX+u%a^PMMNDg34Ygmx}xDI@{@%hI?Yx<>eUhFL`67;JHbz z#yeneeGX4j!5IwU>R#v-hK1~X5Ya-X?z%6TwHp@L4M=t^RPfrh0G&zjfL@JSzkuQV zZJJ{^XCd60L4^Xqzz-aMAhGreGto)U0l zqoD=Nd8~`DKLiagJ^N;(lUs^m$FK5Ww4GWcu$dt&3E3?t!xwcWaO_Kd)?=Luhuo?_?W$Fu5A=F2yW0TM^dMfUk$~0hq<{9V?JUKv{QOgDj@4bpsT&} zv7qagy^sSV=>08|5Tw=PnSbu{z55gR$IxWqk(v7fd`x|Mu1!vToK39^PJO;j0b|WR zW7!{bz{@V-=M(U1|9Kt?Jca%bIS{_*19vjrcQOKZA_8~f7JkfO4A5CA&l#n{*0XE4u#~s%OpbApn`J_WM*$FFAZu~UCLhdg zg}A%nj#su_!LC6a(fGxO>7ewK`$?08kFxFJz+sr6ecvm{v#VX;UEFwCryW99qHBJY zmA4Di*ah}0)T7XrOydUFt29Ny-6O@&3bvr;-WYkhJtJD;J%W=n?^MoQbuHK zO^!d1fTBo0g=skd`H=tKp_%t`b5urB=Q+b|oEJm%bt~_IP0tF^9#0|`-nuq+D@Moz zmYQy#vi(|$Py5vnwqsbBSb^anS`ktyqGMV*t83Yp{E9UUn-8?Usa&+6XTU4jdGNd+ z?bG5JynKjZj0*2@fjJ5PFV6nCbXmbn568RI1(h^)^9>SHECB~411N-EZXXQwS&h=R zDVf*5qST}0$Fd)UTV2GL5-_@ny~{27*8FFdC1H3@xI=pT$>#rNu8m|M7yG8$UsTJV z`y)J)Qt@rYbp%&ysVgUOj{%&ftH}q99wz;{Y2Lmmt}^^x^WWYDBG|3y8@qb?(Ytp2 z*k08rtJIrNzt+rraGMTioIr^BGW+gh>V%2%E|WBbL&U>yZ!dtuOffM38OVlcGiCS6 z{q`pRNimwj)%JNENSx+J4`}rnf;P$6hc4=6uT(dby@&mR@RLQb6ricM%rau$LIsDOPdcr(!`z?=7;^T=Ac=EqdBAC-=+Q6v z&As1bsnlr=z7t%$8Yv+_XCI6-Coa0)&_#3a80ryA6~WZ@1a~Y4DL5~97uF$&&{OP+ zvRWvwfJE-ALCnteTE^cMJ)NLZVmz}FVUCuDaScoFpk~G{XaCnNogT?ACDP1X+rYW9 z6@Tu7Jv!eRz@lzlD6cy(IOZUGS5NUm$4sk#%XBuKoH3k#qiM$$JJg%LQIbWKD_ju_ zg7|Vx?V(q;(foy@jU|WpZLE@ydv3CwSek?dA`uRsyhtuSg5>&L090{uGamvE9hN~( zaS$_yBuNp@I6bh7AI<=2o~#G1_yc)IO<0x8u2PBxu&MXsMba<%#r4v2l`rP&R2-nK zQi|}%4Ql#GePNje(mnUH5F_=tT6X{EhqjD7M1zj)DsU=a)JIoKU!Mx6YyfK6IO@L( zshdc*ClNW@wuE*?bnkdyP3#odgn&QBo9W)*39Ip9IA^c6tWl_M()!cKBinR#4-}7i zz9O~(s8N)E8t(7sE_P@{Hx>01YT?29SF(8jh$F?bDXe<>vHUBtM`;vqjq~L-Q znxqV`6@ASgjT^TKPlkJmcF6JEUb7@YxdkZyx#YbpN;!ezkE5(yZX_HHN96 z_?~sEAHmoh@}g&fRouUHDxao^SFidA<97ZcdPUr&mQ9<@6X z#QM7srFlGWebi+t!b7emUS+vHLHkUn(*`bznV0O`d=5X1Dx9X9)1y=-E#uA_u<&L z;s4>YvkML2UgNU|33%(lC3)y9u-f}4JE9X{7NYP=w!-%>HQB@0kZ6&d4pCsQp{xAw zaCT48mYGg)6~=kJf<2f(q?qd^Y$%5{oY5U}i{L$neyS)koLe}qg)r-fct8(6(gd=5 z5PARIUW(7-*smac3{Vhb9se(nT7c(zu&19#Yyx~R@F==Y zs@pDEBLi@iJGt^(&qg7TONV9I=kgr@gpF za&u6z+rE{QCBRi#DP_%@HN8GB$2NIWeb-xn^lLuSehu#sB(8P^f7dglBZW~2xof5u zV|^h@mvBV;&CW(R;voyX>=4=vT5pAXr89aq#Ak4NE%JGE7~v(pXX}CeQXQ5Am%cEN`3yeu9U4p6CJA;rark)ls2Z zxfJaGL(@42$N7G3Ja&^bZp@}>Y}>Y-G`5{5Hk&jy8a1|UHnwdW@AmtfcjlR$+1dSP zcjnv&*XLY!AqJ3ou6G0}*STjx1Quk|A1rv5eI7NGjpEa@+A2uN^5eBhF^eFrNaDer zm7=K)E8^$YvI$Pe3^qntl@>&e^jn3>wv38!{k8Y3+>-*hltMBYn7QgaE=E5jM0akJ zp!LHgzJ8+7)j2{?HYvACseGdc*&36G8DClxoiYjgF(nZAS=^Y=7NI;eeqgP&?f8jZ z0EK;9riU`iH%i^L^w_73aBwa8%YK5znSEj_t??oqODlH#hL4;RU*w8E2H-D5+5g0= z*8wNk%Qlhf-Zra>kvDWV;ylQ(t+SOT(;Un@*?xfmHn^G{9pTx?`Kh0T_xbmCc4`!@ zXDLmZ@_K-E_-XuiW8KnFJrpekIL~JE-`N6pYpc_A55u4GfHI2g8%Xs8fI&C!!G!*f z9qx{um5#HOE(xa6=U`BrxxL10Fk&JO-iOgTC3Wmu{qJdL$}qEv(ov9{jvRT z1eOrgeh6|DxS)RfM}vF}Y&_ysKRN;Uga2j8fAQY=*7$KR?z=0l*X}ug`~CuaT>LBI z9#7ASUW|d$@u}x^WmYRJ*+}*XkU}kut?y)|glKG(Y#5ryeM?eLnR;e)r12oe>U_f6 zo0O6KpjEI9ciN0Dk3)*58E`zyz%BMtZixp?_ZJ769cFnkBy7I+8F1%`QBkCKtub#! zjPKokD-qyvjD&SzjAp6&WSJ1CXu*0^FKy^2H2GP*^HqkY@O<#?oixwwd>&g!lASPD zSkm?{>4wL6+!9~xGO8|1u2rLf}R z6FEcCGiydMwsr!|1`uBoTIxMJuPzGPeqL&(R<7h(T^>Q!`V%uSL#1VU^xjd}(#}w? ztJr1I`41?G|HS_7)82MV67Ly|7*~R|kMABtEaxxwHjVGO_j&8Rw#pCpJ^0XbkDG80 zs!CMzT-7wO%F@(J7;~I*Ls{QGDb5^?*tgG)KE&DJ+%F8@0D!|&$e0`A5DcNOcOvW) z&S=&#PFD~sm|Jc{xc{(&>A-vvIJeeJiy!^y&mpYrE$j2rg7k%MqSnq=t zJQYSsHdC~n?u=Lzra*wSWz;`r{bdji+8`!_Z-!>u3^4KVz7JH1h7)1F&AIRhQowG- zv{~C@1?zT*QqOaXJU$xC#!XOD4Vzx@!oPr1+57GHB*d7L5sS>hrJX3OH&?|Lu12p> zrM>3}X^&B$#~5?Pl?0`tC#lzZG3(<^j(~rC{QCoi*Fwfv*H&dWjhDG;y=z?t!2 zdiJmI1O@M&c1J%Q-}_bUNM^$-&A`L zcpU^z2YpXLL+#E7(DCs$X)$Po=kp5c%>sc-lnS?xDWYd#yQI^HpXLC;fZaPqBC*r^ z_B3(}xY{I?{AcY|AgQ%qh>E4}d*dIKCl`a0LX(BR4X#SHgMt?OS-#&?pbSa47g(YF z(acLwnGGm=+?bfMXd;kF7}u8*G6vpati(y}D0Ra>NrukQ(|NZQf=H7=q) z_&ZmkoGp1c;OxYjSR!C`=5!9Z;J{Xl*({4#2JiIubW3U`cA*Qc zT!NU;9+j8j4$P??+N^xJYsqimi_~bI6S%Mq)!z-%Wbpx_Uz|cOjX7KX1Xg+LPqbF7 zCh)GHK9 zLjwQMX19eP={?E;IB(?cjO|)QA%GMQyGs1rkwl}Dzb9G#E)rVo=$|ET-6Nw+N*hA` z)j5lK58P$JWJxE>1B;s5r6iLJvqQLmIb+3i{}0-O5(15M+t0JH>pa#bn;%roF=Wye z(;isLH|6!oCWXey%>^^5(A$_AY(0iP*k5HtJgH~y(0|M}0Irr7!EHd-kucfIwsNUJ zbI_*1GSyQ&+sv>d4v#XH>lZyfJ=W@rpiZw^X?idQ3|L2A;wQ)Ymk5;Bi(4st{TD84 z3w{_4#UtU6CGjKTNn#7Epmr9f8#OECD=B+?Lf^ION8>g$)g#{%-cNl}U?yjbwjIV} zyVX&U+eJ|=K)G3hb^SS)EyuZ_0;kv>jKLxI(QeGjCw--;RuE=icoYuiIP_^Mw?=tvp3 zfD1&bvUr)W;hs^r`mhVBQSr2_R=3>oQl$>_BFo?W~Eyzea zLzds}2Zp91T8v?Y&()53&LCc!5RhQdB8+2P=4!m4~q)% zJheL3zL!JOLePoGlPTlUl8Q2)l+&RRQaRfcUl!}`UWu0Yos{<$Iem)dL~QB^@8*8GJ;B;F zyIKQd<}!H2($+uGeXYui0Mr#s!EGw~@ao^1tbGyY?T>n^TR$ODYEHlNkEzrWQS#YJ z>ZthyS~#Fmy>6jjO~dTP6hbX{)%M-_G+{WRZU_F<>&-`zhr3Vps?Kj5^$+}(pYpkW zSkP^TqG~*FwQ_WoU@3#TBb<8pF?mrjsd{BdiFT>%;x1)LF{En97?3Wx<+c1n2_&5s z>t{U9I-aql!&+R7pT;%V(FC>#TxphS8GV7*>&pRivc|}LY4l$neM$3w@W5O`HpOo$ zykHh?63<>r^`yhzvwqiTi*@Sm#a}Dv#frYt{cDO%kCQ-``yi0f_INk{lYIIu$@F zYi%mMB0yZ)rNYD?dBTD5P=$X;Psf95 zd!4|4Z_E#~nR(%D=Hqj**7~si(1G1$_r7n}ap7~}Wxz?_@wg2V*BBFB3Kq03_nwcz zDhO<`fMIeLybntMDD!YdIYyUY8tc+X)!_BHM4Sq!s<3$RpYz5-(y1zU&=s&a+*|dH zEKkhxcb}W!kL0jzT5y_aZ56A?l31%_-BKpV;)=@#P!mZtS5TJyc?Torj>WeKRZ;Q-R`8De?Jb*_GXEOs)`d?PMAaTKjwKM&XA}$f-Ir z51?3_4Cx}C0O6rwKmO&wFABj7$@@x*B{e_yAfR0|jW4|;e8One`#lkwT>9jJ?V2x9qO?KvcD zcr}AfYu*j|n5Z(?MN(Aem+mf%7>D&FF(C3QrNgEAwcD0xS${^I&?bD0UN_dgaoU$B z?DYrKT+;m8lW#YqsE-1p-(`D1H+5j9rZ=UdQ{buUn&%r3of6n_!G@s49a2 zB&#FjbY|#`bETgmBRS>f7&8OtR=10PX&(Ib-x$k`Z{}6d=Q<%< z<$?u-4sW=-kxcU3jJ10`_<|J4D_8)uXXpl+aRI8MPoU;GmY1nnKVeI%iT{WD{gT;T z5Qvl9x6`u)-n#i8VO_Wf{tesssxo4|cgdXO+UDozm1R|$H8EnuNL9?yB(^$U-0^FTmi|6ZN4=xqt|j0y?N^k(cK53*Po{=wf=Aj0Pa8Ry^P*>>2=y!NmTqli=? zyfRp=n?hcb)DAR^QU7)bk6@5kO?f2Zh-=W7bqMChlQL9|thoyJ$_ zc+(UPra1?E#y`RZPgIo&BpC`IgZ|=D72X0k))UVN3fYK2Ebj~kr%>}`;Aw4nvLb(T^J)&_PDG_ z7OmmS+%=1MN50z8maFNSXq?ZZmLDZY>HNyv2wdn5j`7jkBHcyaIj_jno4~x+d%6U6 z4L0<==-J)Eu$t%LF%(e2!IsCYsK`;xC{EVE+7f4iOUjxF2`-^_4i_dT`%f^NGiTJ0 zJYSusdfvJJ5=GAQB@3qG&D*B#uw-)4t8f?OkSB)YrT?aBjELKx=-=hPNrdw8!Lrue z)T3F(eR9}b%{*LG``pDupnUF|F*poxt3%=WY0ks5WDK?sD_1j&qWJ}5b;T{En9F`8 z-ArXS%5oyVhh^y^+il|_Oopg%xzjnh(xR5BC5HyHCL1^wGMM%E8aG06qT+F)+5se# zu&LS|GAsGTSb9~;+@0adi1}R=@KibIl8xTTw2!GtB^`R`_xL2~|v-pmK6G^SsFzi;e( zJG1+U{kY;6+XV?e0A|)53Cf}796Q${G>$4KSt$jZk1y*%(dW9QzBXZe(!uyw4o zPtqI8cS{0R>9ux(9NXjptgNix%YPj4zddDtJi#XApTC}ilBXANap7~-j}06p`yTyk z(C>b;w;x4zK1LEXkD=fN@CnQ{Cb1C5%;Nr5p4HkEq)`0J6u<-;uO7OR3-3zUx5SCD z5@O|7Mh=rk-?Bl`cwzQL$qTkaAV3QB4vbGgAXK&*8Y)^UJ^I>G3Od@Jx8{?ahU;n(ex-IPho4_}qOJXA zFz8spPlv<$XRiF3#>ZX#)52wOFM6;ipZiOab-H^~C$(3gsDPQ zhU3IWjv6squ3`jh&o3J~O99kE*$-b+7O{Lyw>~}nf#w8God4S7IA2iP%Y!^mWP8x9 zlykTP$5sBclrC)PiMuj}Iovjo=*maTL^&WIq2fqb0vwohaa&CVFXy5t!H^iveTpcF z77AN~R^nd7y%T<_NYgY-7MXUw76v&M2VOH2hC<=I!rXe~o|;LD;;Z?eNb|o9H4mzm zpjMgM#UcZ7y~E8BjmUv6#g$ike=iU$H_@?ekj@zt1uA29v&t$0)n$ec-yQ)*(^#JJ zS06I^I#32A^2790f0j>_NHSATknD6fz*LP?sIQ#uE!96CtzNwXN;F4g|k9lTiYqTDF%Fx~8X8A09bzy`b(VAnb?S}g7bjpS=)$w{<;i6oDl zIc{dpq*!Vj9d>cRSa;SL0B^?Sx1KSFTEp^!t25LeAn^d?o%Vnc!U>tElKVSX-ze>G{Wg-@dho;|Xx&(HUh8w%hocI$HT! zAOXN6@mG0i?XPlMVq(Ds0z>xV=#3gp;QX|%j~r`>xZP?ozdyr)GWXDzap>snMzF!t zh6@S-ug-v<3(XdV^hum60m~x=3s=n>HtxC=C<4}*_~+ys%ojS>E*S=v`K{1_DK&U> zl;<^GqgQG}fhIh&%rOGqdj>e{drC>LiNK2%O3UY6Mg(9t_BGz_rNQncm*_c{=+U3( zaTAvR@zciTr+*tlA8Va2ZV}-Zo(rv69{Z_j?+3o;2hOz%`j3bJN!0wt(}deUwQ9F^ zlJCW5W==pf-AaKG@{cnM)|+p67YnFLJT#|U4o zD2tbG$CxLrSGpZ%KoOq|@tFN_1HAux@qGc&3~q^_gTeQolKk;+N64-7Wo?r8A!C65 z?RIA4xqRdK;p2Uyll#StgvJzF^^P+P3bjQgIEjgVIF&m2v^_5XRbsj`;jS=gD!Tx) zut%d|ZKV59sXikYm?D`gah!A(g6Cy=LdnLEeE;QFImPkDmGnjCDL85v7HmaYPSkAJ*NdWBJn&7SNr&E?bdBX9Qe8`gK2ytN&&1`-s%q^T+YDCvW z8w!8f{2}zRGTHViw?3Xk#VUTE)xgxy8fWFzSG2MCYCiHGKws+Jt(hSA*nz$6G0y^I z(NeU?IX&zS{H^wAa`&s)y|q`Xfu1vN*3-vxc#zzHp8Lm&M6?zLsXse8b4`&Z*Krty z9cPlM-bnq$hu%fS0A&4baW-8ts2wKekt}W`)rS!IvTt~i7Tp*Eckik;d8!zc=+RNp zlP2%*P^qd1Y)QqNiAKQ2r}RPL1bgXm3|>%WehRw-A15HQP&7obMNWy)Z~P$yVgUe6{d;a{IOo1f`=aE$GH^P zFezjR38$aYXPT!O9>ZLvV&{I{I_U~UWgA~*@ zkaUoaKbb9j&iKmH4O6yM80qSsAW)@x@jpNj|I3y>}iyc8x@*{r;PdfCqRg^%aXC-#s1fANVS z&+6T;?%n^NnEbB@c$boRPeV}ec_Zolk1TcH!P{_mSKkrc1+m-<7xUS-^)LTc056`h zF2FCJ7dm}c8V<6&&VlBGtQQb}+D-IQP2>ft%sY3bJ9cdiJ3d}IKl~v=jUKcSK)ZrP zw|Ux?l!LmYteON^Hsnc30|KC{3YF5M%ftV8MVCUR;B||pMS7i-@_J=@wd{#9^memC zJyRfIwKI#v94@z*yP@I=V^;SxG&vYv(D==`A_jRHa^=-B0nT_%m==Dfkp{3cWnIgp z-@gbRT(kf7Ffho~LU<%8tAK)0lEQ%xi2IBpz`-&H74D>ErX&)g0At|tB{^5k-^lfs z>09x9q#4xFF3in<5uC0SaR98L`(k}ypgC3~n)BFoPJ4H5`E7b+>1U+yl=VoTX;@3j zzlp4}!)tfn%Ym7CMtSTIu4YBz@( zuBPuuR(zM-Yga-42j87N**fd$=Pa7p8^$4UnoTmk_$P;#wtKka=sja|OuoYivaZi}Qp!V@R$mCZI05@>3vukD$DxJ#L&1 z;48D;;R2EwTi}+WN9hFya8m{R(DW&mW7lHfdaw`U%oWA(^)`W4&~Eu{(!8t1qee2w z?Qh^$@1#J+Z~FQpsnV^I7BvN#>f;e3zqM$!M<7M}K^@#)oc$Ymd(W(}b{z@UZvw;~ z1zzj+9R=lnw_c}$Gmq0hOew!z!Uhg+Woe70D2w^!zeonhe$S=CZzw8bl?D5P=V!C7 z=629CPJ(veU^hhDWOKvk$0=txMz{LfG|Tv7inkPzVGw31WHn@~%_D^BXZGMY+lAH1 zv;d&D@3A_Q-Q%!7^N6b?k3ziZs^|%JE1nm?R=f0LGqGm64~X~LxViXv z>--@4*a5sj({ayH?e*(_qk7;P#GNm+pSxriCbgCRuL*yDIsn)4lm>iP>|ShGmk3Da zJ>Ez3pQtyUGB-+!Zr`uzKd%2ZAs_#m{eQ$+=l#O_vi#eu{F?62e;dgPoBzgcC^EGu zpi%%I*ZBPAAz!k09jil53XI$hJkcn*jd2yNLwTJ(eISXn972MiHj-_SA@UFdL6=<% zs!B~{xiA{gdY-7xnL$9iZXA%p6zA@D=|)wh9a#&=K}e_=>s6^bJ&DZ>vQ#KeP7gZ@ zI5#sbT$2n#%Dvt>9y!-xfmzU)YtTjIvN}Cdre&B_6KexmQ1VNtrv(xOcyxx#!flFW zh_Y?Zm#H(ooj%J`_?Zu2?4~5VIUJ~vJSGzhKBEI=@;vm>Hn9>7D!;P{jz0gwZ#GCj ztkgq2yiDX~y#45IsJz1{!5XLkNP09am~yEsylm2zF2=-Awf`iYRD5c%*kn+qH-;w_ zAZ`7E`?2aY*$zX~&$rN%5aelclVBHX^aYh-!_48Q<}0h2QYmM8twg70s^=rNQ60CV zCHgmj0jGAT2kzv8Dfu^eggaL#jK+P=nrgt-xdTgUUJJUWH&$Yg2a2}1599C~)R^$6N!%dKRvYw6C{H$63su3&XSWT> zacFA#QS%^k`j^O}ao_NF>7XT)qSi~l6~KvGMDD+;);8vMfeyp=TtRmF721U<~6}I z^V57`wb+%Ei)gz#0}tn7W0kGpoC=&XEC*erfN}N}#H0or)I(+27scucUg>HVJuD=E zn@tqHlY&(fuln!UqE)p5gl>S|IGLdn%)#OU&D(h1H?ALH53#$=IkQ9lPK?!R^S)E2 zUi&=Cl0PU%JP0XAoJl;}U2|>Jvw70byaF-vXmOYcM*T2sQCqn=c2XxCnDf$VTbc-G;q^u;pGbIL1cie%bLaeo@gk_>ri0 zxzeuj0*qc~%=WV;Fc+@rQJ5JlN#IhXze@-9C_LP3)rg69c;CAquB*x*K;`i*M9xi zhQ{`@*!REu&kyhG8<9iwPe19OKsAE?znYx?wU__(pIZK39{_R!kafej1}Wtm%{vuZ z!aSgFxJf(WWC8EMQwNV$iR+x#w)*$?=mI*K=hET5Iv&K^DCbs^%6^(?M6psZYB-v0 zOpjHj9(4!}b9?%}rHji>sgn7t`ulH&KCr8ff+NpQqy+-m;i3o~4( zFLZS(r5)asHQduhU?NM43GF4;U zlyum3`afY8M{%x^wYe@;8A}j1c<94rZE^OOg>bo|(B>EgyEV&}DY}J)wtSW}UMyd= zY9q^1llp$!a?W8f-rA;nahFfw*p2;th@HjKWH70+H@DS5q1+kPD3lD4bq%*f>Hco8 zL>(vd1W5PCjg;$4yym>W(*}KSu4;o!wi-P5y)7@Hm8LS{l$h1H9R|EtmNRl#`% zS!t`QK0_6mWa$Bs6Jq}Q-1M>9p)GLBa&6-)))XFQisz`OQ)lG{?<&UcMkW(njzg zcf*_@JOJOtR)+T=n5sAtcT*E(?bD-kh|14DmPNcb#<YF@^ks+uILo`EuWH+)_2<$U2VFARk_GTGPY?9}tJi;RdT zZqgpmld=u&6Q4TQV`zN%@h8*u%i&f7V(QwCn+GA;sTV%&YUNgZ5cSr3+}2$s!0U&t z=e~7$9!cM!k+AmpeTo0YEZMgGMEBx38Th!?@3`ziBzo+EJy+}O_~#W6(^~5LHnH)< zeUY@w!1M8XGgRv^&uD9wgiIW+9xdnE3Z{{}tlYY6rR&9{dr%7j7Mba#vPPENoSN{i zH0^AtqwI(%vhf5G)_+BgEFEM{Nfkm0ua20w589Bjbmnb+0-qg>IvabC)tBQ1WcpNQ zs4>6%WjeqXKo%GiiG{-rME&d}MYf2u3uk)0?}+7sZv3Z_?+-kD9ey5L3X`A=w}`JC z@|qc(9J9p&cF&#l7Tal)%3m@SpL6^-6JHNvq!;m^A_(qC%)2=V%^~bA8^m)J5+_Gl$!xM*y8*ZTaa%MNVuK_8`&YI2w` z6UZo={_0c(dnkX3Tdh?WbqGzSC=lspV2N$dxPoO=@Zl?9jL9AAx4pvm z&6{#BTG-7!KYhBWzLj0VN5*E zztfRa;ySjCN8W$h2&2{k8u;VYCbVT}21v(wg#1O*eOEsbFi)o#xX7Ii=~_U2orre|hN(C%3VDJsJjqv)`|&|UckE4AgVQ)6d+JJtTv$;^NZjIi**EZC^J z;1=IrG4!w{Cmaok1g#89Fqc8xR!yPxAsYj7uYwMjav364z2tS95+|0% zOT_WVaWP(Zf7fzmVj|={DGtGKqI2B9^u=lGt``s1l_CjFl_3>5d1GTZZCA2>mFDC8 zda;(FFTsARgQ7eKAU-fKZ?dU!$wRsCx05`i!n%R)rm~;ci+y7@t1gh?*DDG+?rle( z$=OgHFH`J=Zbb~k3+LzBW~TjCn&n>iXY=>fG+A;GA`L_t7~HQamgv@o#(VS9h!hHG z!6G$~9l2VA&u6dl>Q|{!ek=lH1g%!1%4Ht8#RraO`_2$?0IeeEMHuF-VXkt6&wCp^ zyLgz8&bu4iyBoA4m;7IG4ZMH`V7Z7MDuTy{>0DT(H8+3lUm%Dj{Z?OKs^an{xL%^F zK=lbG(5}Vx+_&)o3ae_JgR9)S2j)QlBX_?hiX_nWcBE5vn(V zB)EK;9v~y9aTo8$^EOo5dv5DpAXlgpWKJ=A7dexFBLZ7}FgtCk9}-EZjDU(XBoz+C z%B!>_>jZKk#9VW zs@UvaWQ}Z!94S{5sk>p==_Nk&qxCLRSIFe+A&~azI*%Y^2fHMA_UF;`-sC$BJ+3@< z;TP`NSsk+bU*~D{$sTJk?se++L^Grhr25u>bFth$Rm!;6C`>90!F-XFs2TPUht4Nl z4VzfJn`q{)`*kE0pj+{mRGb!4WN-LL26hw4{qntgv$vo2>lPn*`r5;k|Mb)CCT`nb zUrHeSP6xpSyiLOa{my8#avIagCKNAULS?>>xSEVp#2Q``;(U%j{g9T{e%bNDzT{5f zA{W-bX{mKSR~(S1aH$*b*gZA##nF3&!W-ZS>;9G*uTGcs9Au;pVT5)zxz(j7{d|%T zf3>~J>DZ5FC|``+oD60VTqC2YXgs;J-~*hzkxJea{)WCwxzaOa_tNh!gzLn6!22yx zBBVtzh=?OQG2wiguyB0=2g~4$QQN73&{?3M_Ds^hj9EV!EQYApONJtkb)RZ7T^LN& zYCi?vNzDcPwGg7qIqp1Gqo`T2U7L!iJ!TW6R>HhzQRDk^;IXXr2ZQJMeLe)G&kC3+ zt)Ew%Q#IAav?u#!KbceVq{upQ&tNpJk-?&FmPIm6GYZ!6ZnKx|AuJO?&}70lKN#-# zI}Aze!;=}DTkj5^&$7;%ly|I{+a-;=3Dm}FL&(`mwK`>ehAs1akjY9L^|Ecq>Ctf1 z;F&le_16q3iFNON&b0H4y1@~)O$G$%&mfgPMT+KRDbcQuQ|h;O`(`eW(y3&)Mq4xc z`XPPFIkqW8Xo1Cp+K))jbD8g(qm6{~54G|N^-nBZx^jULXtdxeg)M}A` z5_Sk-&lhhTy|Xyq<%YSPGyNzezPDn~p{Op2-kmnl|xq z1^LRN+d0bEPGp_vMzGJPB>|cG-{+7iDcRpjuEL?YSY3&$I z{>zCi{Mu~hNcY#FUJ0Hd=CCOu1?*HMHPQq5br^JGr+A2C|2L08A9}zGOY>UkCJ{@O zm8CDNsW2lgN2JD7aJcn**zK*JJ=Rc?6}_b7Vp(&DIDXc**@S`ypUm3MFmOwP6k<*Z z7|WcFR<(j2i;9yC^Af{rbSc|HB0KjA-Uet&|L>_OpTEEMNlxBE-p1=9YyGzE#lABKa65 z+fSsMA(-`6H{yDD8JhdvWaLJ#ak8)p%&B&!zUU<`YEbYm-gL$XdeJNr{cti>&KD5bG=ArJ?W^b=rz@@ATU2G#~8WI8-~d7xL_6gCXf?^zL7QLY z+SS-LDAgWWrQuWR*U;kxnfFoLJIr~>fHFf)9~(Qb2WAvh3#eH>Z@>q3#3JG|~zA_%zh>RV~h!tGdmv_iMgjqOVC;3*=)pv-iwl>IM zkG34J&76$6Z;rdZ(}8AyI_e@q4F93`3o<5Rg$Nkx#NEj0V+=g-q8Wa@XEfdesNMPN zzWR5CituV->e!y$va{IS3~g2qUMjGiF4xaGCnLRlrGZDO)#=TLmq$CcFwhRKW#%k2 z$lRGl?%I5p3nMpWMp^BasSW>)^OrHM3(K~TruOVo@)2w^!wEdSnCge}mgv>IPGiEp zJd!{YpL|Na?hzj~`l3`(In^!|P-dHld_oLqCngTYFbF|XrGZD~83Px@cgkH)ySdL{ zqIxm_?n6fknPM6wC-XP|V09}6oMe_8|*wOw}gm{95gBXB<;RclSC694bumcA`Uea~LYut08$HRzW zodeQbmIGhz*62B(`I6p(DJ)zQUVr}Mm$T?<;RemPkpum`C*AM}J%j{FB&LVHi8(*h z&vkF!N7u!Ll#yOJUK?mS4xa*bRE&lRMw{FUmfIn@Bn%Q{m8iA4bMTb+H00_r*H7|V zO?;1`NOnFa+JiWN(*$T?kVt>FG8TB<`FP_0FarbbC*PM^5uY9h`QMX4{UOXHwEiVD z@C?E<_Rjx#U;P{Z1eJMK8fmm`a?(9XWa6_V{ndJwpv8ooA7V>&sL$85hyMw+Cd++n zU4(t;!4%2$e&bCdU~&V=T2|iO5iNLF*4x145o3G{fDF8YiMCr;eZ2A;Blo$~PG@J$ zb%hcY@d~)1RMcyPL9Kx5?CZ3pUPRwkrIGgs6q^9?0mrPI<|iluJ3A-F+NSkzGJ1uOgwuw ze*|~~=I5LAwSCItgLYJf*%s*XS8!)v3bAQ_Y|&^0@w=y6j+E|JnJ(RY_dSaEQp>0~ zQVKzM%#tiZ>I$t}uyZpQf9_VmSxvzy(=5?RojORW+e?NF3e%@ceHcS=9M^ej95ene zy`)YeRfcgLQ}F(ou?Tn%U`~(QL<|P7K4Ku>w5wx@(>Sp*XdAd28kx_`?IJI5ixajt zdFR943JsrmuVJ}M@|&Vp{xV{?yl(rhly+%_Q)K?i`rVX{E!wN`N{bpNH@%Cr1C5az zQaPFyghsHtMp)GK*=%nk`Y@D(z7oj1-GbpP)93-u;C0-OFICL1{`eF!GV}dHDMMZx z=r-@hKJ_IGhSV$L%S6!jx8ufb*@R}Yqb*YVYY25p=0+bh=Vr61nEWJwdv268nu#=c zRBiNvi$~-(>KJwsjbN^X&$uQl8%BQGFX6#O?c|oyDZMkhBA8_5-rtiD>=R72VwZPy zrpc-VtEaL$t6k=W^}PSRSCNWuM*I&p@PaeUH@m;e%qBFK7j5pkz8W7lF+43_#)Zop zh_v^0D3E>4zLojTxhUryp50b=4fL~RSM+3v)dHm1h2DBKA_s7eUf0N)!Bqy&gU8iz zb-xK^NbkbSDJ7GMQ3cljvJ1u=TB}dp!dB zOYSdkDwKzUf$WAqUEN#?h+Q_sW|`p~gFl0}8y@{h+(a%XK1*Hat;DGi0-5UtVB)gOiY`S3lF+SU zR+-HJm254dv;}Jm&U%Dw6TH}C=oPTPEc!rhNxM%MvVxz^Duu40hpvRFQ*9H~48DA$+eiw>K`g!rHDW? zMiJT=GI|#P*iW{kk^!kx1yy{ zRJE}a4QBVjKP$?qmzJb24;;;T;#rfl5%2VT9c(0kJ!`GqESuPg5&b20=@~-myAwtP zdDLUWE6n`(BMge@U(?UAY>@I&dAoO8)YqmK2=U;kRH6>mX!=I`j!h1e2GxO$DlAf% znBGnofVa?>qpqMoyt=8uh)iUyh~8$HG6!JbVRU#t5t`LXjAcfjpvJ{hW1kxG+ghMC zrr1`29rNujg0QakKB(;=p7%|*g)g%^(fnZro*}=;rTiys($K8b*iWUcM z{ZXwZR*cCQhE8N`^6R_gz6x!1bX;-|^@)!%w%`BWUynk~ zR~&&&@I~7$j*6+a00Vxp|J;a96zhu?5y)c-0Npo$-Ds)-al+8UI?CF{v<2V82ZInb zw>~uJDt$Ej^lp4p-O?ZGlu|5FNohQW;&w^KlXVtxH(v9WE-wQGcBo_F8f(S|`Q2nW z196M5|E!YH84JdZJ88nOjarKOIE8RRt{2k`Nyj+b#F0BT;W_kkmX1(h=S|ZU-EzzCLVLp*K{tpQfa!=*FWp}7Z&LB)drb#3KaB?M1JuBV}A6Hw`j zSg}RY`^z49ylmfiX)l^N`e$(b-#*blfC6C?ROx=aLGoWhUidz=f_tw3pcP@}Ngm~y zf2sGN^P2j94?k#2(F$p}^Tq9h&JUD=-$xgG))qkG=i4mE+12jfpy+W4WFp_#aaaCv ztp7$0_K(a;a@31f*dHr7TmUueZFutWukLao=|29~*K^9z)Ow61VEA(hN_HZUQo6Zu z-I?RfH%q{;Wk2-sZUU!L^`*^iA5IdDi>Y<;bhQsvQ-9TJ1*zrP%5OY6EzFiIzJ`un zy!TSn(fbc{79um`*#kS2MZ@U%62ZxC4k9i{e-!Sc!zwO5MJ%H8tBk zpSBD%q#UG_S6!z2o#*?VXND|6m6=}{FKkCs2vPv0-wc+pHLX$V6vr#hBrGnu$BozX zx=+y>JIRr6b91=zFiq?v4oO${G+26rb-LuDr43!n~a%vaglRxVy4C#$-Nec4zB4b&X-uOwiG8?l{Kn5zP2gJuIVn+si zz5@VwM$O8F6n$xJHz-8CK1&CsSLiCVNMkLl02L}$5RcKpXz zt3|Hnq>_nGa^-84(wWPU)D$P~deYM|8onmb^y8@a>plK6Q{MvUwC?0?xr^^aehs~p zLjSY17GVM_(1+H{=E|?g?}zhUx8@^C)9I|sM1LcbzZEA^LT4xTWOe4;jn!r&#LC^I z7d^md?-3Q#^BWe;t=rzXlL^aj31lL3=KNwK!|Bgh{CWYRc}wcxF@in1{(=`+ObY{m z&19b9%vg9sgL~YmJ+ZsvIah1=yF*DH!bzwR877pEZ~6!oclNsE!TXkc7KlRx8e4t{ za;Q$y*<}5&WQyjD^%BC(A7^Z)%H{Vc)thxGhY*lKYtQA0#TzGdgwVi0g;O;&&&@)| z+v3S8WdwzABFXu8%8@#?hKOaadRzcGQlNlw=dxT~BKZW2A#8JAxc+Z8MQX2vek&dGgrby8I|u&hOJSn>AoQGqm)GyWv*6H1)GA*v`K&O)tPI zejEI_9$aoC@FRq~a(r)JrfmudMOvVIxZFZ{%jLh#dT2ZNhc~=ze7rw^JeA%+*m3p; zUgs5_=LyKc_2fVBs1V~C#1xhjJx_(cgV?6Qk0buKqJOb=H;=Pp_g3C{2jZYMyl?Y9 z<^zPZXxK^-f=%{o9bt9 zvc^eB35>*BAWHLHk>SPbzOBF+V=ePG{g zJ?bZRi%_rN-Ztl3Ya}@aAJC}|giLYvTyC5+d>)YWSNQ#NZxcqw>46kK<^O2<=HNcQ zsPEXe+1PfPCXH<;ZERZ`J86u@wr$(C8oM#RN%Q3Qyzeu!b9ZKE|Ju1{&pqdSQ11L| zwFfo)H@A||y|!kBBJutv*K+2Yj1ez*UJ4OGfR;Or`xSrAqlV32M!-sDF~Uc2e1Ud` zV)Bn>jMtNxH=iqrmT0Fti|)j)O>g2bsY|Nvk*w0q z50+}&u{rM_bPW<$`2qUZj_y$tSpxN+Wf?{`(IRB7aae4^d&m;otf3dFdXaf7spA`z z?I^9(x7s!+cMb-^TEJhW`qw)*t!&w1j}+;Vj>g!p&MbJhEN6a zj1|Ib#Jg`K&jnQBURBCHCTIIYPkTWskN>;{h@i202S0(u4^C!GG*B7FvtT7|1)5}` z1YpR*gKok@-)vVyGsOve9Uc;5AG##K&VEV=7+`wzglNQX%m9cdN#+_P&&;FD&1Eq2 z#c7qayB{Sh!g}d^jI(h?s3opQ*Fl1*kycAw8z|*YaWiTr6Pn<~fG%TeBUmXQ3?k7A zfk!iZ-Nhr5se8A``SD>@wSVEte*TL%gcqXTOM`(;0k_0h(H zm3ELv6Px(FHNNicXcl#~_NKF}o>mFhz9YK=^PJDojSp8BP9bodh7`J=sz;b+49Pv7 zest$M$rhSP$F6Uq|1qm7Eozfv>~w23+0gNYYLO5Lm`N<*56l|rB0pxcFY%Ing8$vZ zaM(kvSa#{j5ABpfiWbR_%e1{$F_JM|rJ0_mu*U~hDoaF1)l1a3V>Zm5ZpnQ?!5ee3 z;({CWxS$!o;yI?j5Ge*n#+FubF?gG@+`lj+56yMeOQoE_00;YfP!L^f$Vn=77KZRQ zt|y8zKz(kN*xf9;%{f;zsq|MayqJkmooJ{eR2B1D;KLD4GnHM@rd6l4r}8h)zu}$# zxI3S;{GQ*1X9oY#eLA)usz0On{po5;7vtko!ZNJq(fa>gsk;C1?*5m-tN0f2^=463 z(Q=Y-=Xv-OwqX0euL94gAHW`7%`JzM4*A6>{hIz;QSV|gWpkH&=VqbMhq|sH4^Ksk z>EH769V1J-Vce;=O@zF)5l^N%Iuc7!DO>$I&!?o(eE&&PDqMz8^4nckm)$a0y z`m4%&yBnuTde$oO+>G;q@R`cRD9;YA!Hl&@xzN-jf%AnZ1-rkQcEF24)koyexLG{h z46B){{Si|1W#YhAn@Fwr3`jLxVfnazh$AK@XJNZh0?*QG1q#Q{vtFdDu)mRDE@^tZ z=TKamcit(IdSNN@(A1E&bz1X$!$smh1B@TMQ*MLxeee&o ziE6NQ{~6hw_*%TDI8X}zC}b)ufFGA&7>V7}&26^u#$uj32Efg~utqB6Z=2tNhoq?N zdOuT0o?#eJhtxLntlbAhe9V}ya8DWFJ&D3!(rial#fBn37@2DS+Vd|eAL6eh{G~bP zbny7~!ZAAC_Qn2dCu}Bh^jnvm+e8O_V#DR5bEP_`s0k@4g=SROYt5LZlXzV!t^Yu6 zPY2csOZhLbB7jZsdBcmU!}0Wxc)JTrxn@r}mM^6jaY>yWVZGjGr#{ zn6`i5%yw7JH&Se%hZFU{F8`Re$=3z{$-m=%AmoCh*EqM*$sFEkb55fe)5Gr{u7p6Q2hEn%+r07 zhln0;2x>AE$WmY_iSw;p8Vd88{V$4NKi+|!4<0uxuyVH8uwrk%PM78`{!DEfirps8 z`y#ag;&vgWigt`On6S!x97MroDT?WungCg2!Zxicg7?Ph(d|`@y3_3M_Q9DC{PRTB z$rb$ljC%Rv{^?Ss$`V@GIBF}Hu9@}B*kz6Z@6cfQPSuvQ3)c$rq~e}3`0N9CKSzP8 zZ2#}+V$)vKsN_}(7HfxstmMvGd-)RhNgQlk?`bVod0!9Y(uMp)8-1ckohqEXWa{uL z9NE9*S$}QM_|+Z7dLIow>)z>Y6q_kXxYupQNEffZN+_y$&0-JDQ zOxEP9_NN3B=()xas{toP*PlGv>6z-EQvgBESW3sCIUSD0={^laEq(oSyLDwxKDj=GhXM2TXGj#27kbc>&rRj{q44 zRJk0T8)znF@S$THUbt(UEF~0v5qqzGl`!cD=SXMA#~YeIR_T_oqlsd^;5Jc9l9O>o5 zfgf(fSDHeR{3)cZ=354d5I(zx<4=A(R;M21C_-bX^UVDfbF_O3w4JPKI~lf#(86{N zxq5VY(8|}7E5vc+D}SNOv^Nd} z7PcAdIn71vO2%OUn+~A-F2ED2^D`!4D_^Kv2#>Bajo~>;?k03@(wZlO+gG;N-D!IaTC;118ka95?qxO| zq!FXk5uXk=a2gmG8AwaEO~Kf#O=)&5?AWpV86t!y-&AT`I5RpI_3|IS{}|AQ1t z3~ndgjF5?I8?aUmp_A*6>1SKl56&DPps~{Zg}ec>hnN~e9*lE#)GqjCMtI~q;}JQ= z#!8+>^kqa4Q+2Xp5-Zly&loKIfK@FQO*Po1p^v;Je-&922Ch`$0>kw=4=$O#Dhq>ztb!5-AB1WzVzjJlo?l}`a z_}pIX$4Q=kFqpk?dlLI0ZnHi+bN8>aDieKmLUW#+2Nx9il2`3;2G#E>w|@jr`h6dz z>u-O2DX_y#3^nxKrCThFoQ6&mZ-_DM`|)b}9e^98&??0SL)z^?x^3ZP9?!y5VaQG+ ze;?Zxg->07Ws3@H0Uij(yXA^>(H0j6&Np!RCxRomUDr_R#{wQX7*}f$-te61UrDoi zyQGby@ev)JP*K+?>NTe7WHO8&5oOmsQcRjbB%&2l}?WO5o#ZtJo zhHR(&)7zG0`z3Q%+fu_73l@H}DrlK_9k;ALFKiZ#HurFWWage=a(Cki;vt!MpDFcv zX=!CpLxyFFVp*z3S>%7+-(${4^6jmAu>|Kjks`>J^#@%H)g^c1l(I)zH}Rm)Qt3+= zFdbPQ1>KbvRBv(sYN8<|jFiQkST#|sa0AEoFTXf~Eyk05L7JN_Z=N0uKQ~G$hqXv9 zmbKThzzwcn?Wk&(sX0HZambwdHnkQ-SgzGt)7-A0WNW;{?u#-xwvg6xb^If^UoamMee%bl|Mx(R!B!= zlf-|%;d?+~)+$+_|IkP&t&h}NPgP!MgRgzCHO1_FSvHUkHGR#>iQYhYHRlomD93Z# z_AF3m^zVMrDM-;El3Uf`lixm}J%m1?e~KShrSBrlFpS#sIy?Pr3_-NGbc->)Yo|j`8NSJW6YoUj9OR z%}`N>h|r!VIi*a9UDYfTlHnc%+@Nh!n0t*QGSf_2&B>ia@534e4Sur zCg&g@C*IF>7GtZ4F1UA}a!ArSCExThB@Yz(U$>hzG#=)@Zh0u{>R7ryxUqy>F<8;> zcSj4UH~QGA4{R)T_ox{-H2fcyp#8HO@})1`be*LjqljeTJA6f}O;y=t4*+FMNm@Q(Nu$O?#-lcY&aBi=QkK9tgU+opYkPyL39T~YN z_s&XtC)xU%%608_d|!ulskx!J;VcsLu&)H1WkOuW&GoX~VxsqDarD-i zrpzk#!qJE=VH`VN?1&{Wjm}U>m)5|Gm_IeGyX+I!^X7$dmBZd4P4wzljC>Tj!}V^j z;*8-XX^*xpaif#JD5_q-20U)1GFsU8QW)T+%kkxhwdQHljMrpH&u_({G=qCrX)eIE$(o`#w-)V_e%ZFS=Dm^n26biuQt zXWX-(g8BT0r-?jtrYLiADfQWs^lj6&%U3~E^Y(P#zFMbW27;$9LHmRwe-1YbmQM9e z2Q04^>HKDC`d>P{Y_PfJzxKH&3|Vz3p0o0W%KZSqoSlAT?k*ofOxMN06UB|MVIN-_ z93D0~kDel@jdxX%Y|j_ld>_>BF&P%DsflNGS|>}yJY^_uWTkxg!qaLUXOqiAE{Qx{5j28 z)oqD>inbKUEMTdC;6tpOm!YHW9cCJg##jL;*T=t!ti|aRBi4Z4lObEg4w9}@D4I`4 ztlA~h74XwvEz+*>n+CdeY-1vSeb#iNv-0*QM5wW{_mN z)Xq+XP7_Lp(sFXY<-5LW)bJ>$h$E6;K=pjvj68+Ht3GmqogDjJ6%8X}!dG72~*&mNFgtT&q>P;9{KI1BTePZhd zsB4(^{;hQ55G~brbbrR0cHc5a$DR|?A)9yY$90?i6Nw$AI=cyUkUx>VIBulkRj z_+sQ_ItO|}$oqvh@%d|**>}2P%if>I z5izu`J*kUWp-Mm=FWGPQi_hM`+}Y0zJ2$aRF4}f?nYc48`AJEDTz8y#$x%!JwW8#= z#4;k!5Y@vNJgxzl5y1jeLnSGac>uNl#^v_u8-B8hUCF}B(5#42lQ9sR(G}57{QIn0 z=6pXkCvgL09@*hWckoyu|8PV|7}Z+osc|=Tpp3e?V<4N86@IiQ zIIO0cm(5E$^Sae2T-#9E-CZ& z$vI7LwdWJMAvme|)pg;)U>R0MERyPs+!tLbMN2{4$%=aD19A=B%zxuKtoe{-B{0M9mdTFOXO^c5r zcCep@B{$d4^d}AA;9IorY}xq z+S@#Ev6LgGH1!0Cxcl}nngvbiIID1lAULXgrtc>~XT8mAU@4yZM}$t#SzpGd)RlPq zzm&!qaHUB7?*u&k^+xt%*vM9;hmDS5DZ#h{nRrw7Cd1)8?eF z9oBC81T&Qhz7BDxq|$piPG=k>g;ms1h6NJD=L!}uWK`@(e5m*rM)1zn|)Lz+*k{T)Ffly0{5kYhSsfTcM zkh??1?g)%0E?wBHF`TI@(T=TmzUQV8*dvdjR*b)>guZ*!f}T~JOJD+duH(5FzrWlz z(6@IxL1CG7a(~2S9%sMmTF=N?|FLcj)nq>C*tiIWfF3a90~1JF>|VWng!S#5y(tO~ zBDhO&S{}TJxQxEB>pB27Xl70q*dCgU;G@ZkIeE}O^ka6Gv}3-W z_X4aPB+bd%GZ{iLp0RD0o|}YD3|=Oh;Vd^KSifqVZ`J%D#xl$YQE0i`e$HzfoUi&P zm)csQTr@G1UEvWgxG?aQml`#7Tp9#kkl>&&xwxtHks5Vy@`O8P{SqVkh;yNS9(w8H*aJTGDytkuy7>yY+RY~$ zGLm1MDHIxN+5SV{|SLS-u1%WVBJbO@@pzcR~3B1s!5Vm)M(U`B6Fm5le zT;Lm^o07$+Rn+Yj?4yR@AGewTLz~s)w!*b@TdIGfK7KSWD$~_xpBpL+(K7HDvK960 zVFHQgTzWC3MFX}XF!g%hmz7^4bfo|M7fR*DQ=(Gv9Bf#$xl`3C4QA5V=5`zP=bvFr|P7C1Mv?{zRHDeh9qwfc;#Lu&|SbDXmpEQfLwoquudoO){PB=hvL5|s!dOo5=( z6zd-5aL1#keD%w7fz|{3n@(WASp@~{ny*q=8=Y!jU|Kk;s0|s@Z@Vr*;^)8J`f^Ga z7AG#Z>lk9rZ>ZZ#mmMQ`t^EB&**FrEKQK#s-$bc{Zb@i-^)Q|*W~Ss8QR@Y$QsI377hl)>K9>d1)BXN$` zxQst)mts$uiDuU+2@v5`;JlN{M+eeuNseEnTtdvrzvBiq?SxC?E7VvEMk9w!-BGtJ zyojItt2g2^$QdG{v7J7M5W_&Wgw5EoQ(VhCyyF0~Ygx&fW`S$rqptnDpT+Ae$2oF* zvNGQKrI)+ko``qQD#=DkME3c+S(os|-7p)N<$`X} zS`$#a-u8Rbu*QEo^wg7lnGrrkZ!J+v9WdtF?|5|pjDSzDKu;(vOjgozxQuPs?|GPi zCL0SP-9)Ah1?NzsZDsP(PJ(u?k@*OT@BaH@EIr8ohRWalQ^U?v?V!koR;nM@c^r-0 z@iZE&?C0FH2{F`B)vKZ(o2R_*J%zy@2uta6A8JmHzO`JB+BH%e1vhoXqcn4~Z4yS4 zh*|0NtOMneUqzm-D?(HK!khZeiuA!uH)fnOOV8rz;@M3Jc{kMNEuWlZ5{w!WsH}?m zr+68*$jYGNvXki(|Ik$04ce+d3>c88a6bD)by`|%sbUbpnooOZj+f_tp1`i7G10a< zg{~rc`sMiqDxbo?tkM`Zwa{?&(N<;)9FpQYE{iw^402+GkYk2OL#cL|hRll;=imQ~wQ|uf+>S0`auvm%2-V4>e7({5) zZ9`U7RA`-z2u6|pWN@O-sF3LB1$FMxIx1I)Oh@=x z#Vy?v+4xPQUTxx(7LVqpH~AC27*mV38V);au#5NtEXF%O2S^#Rh5CtrPstK^Z6#cSM;U+yM_{4nI!mRn2WC%|}3+ni^Y7#u`mREbq*Ub$k-96xShr>5xR1whurt@#{)#5jOU~3sbJYKMRmpS zjNBNUc@d%H9L12No8K#CY+YhEP7x0;I#u4St1TMan!{8H=I(oIofhv$pzlO&9uaSZ zHMV30i)~v<3E)}&>HWdQR}mQFWWe4t?2}1txzIPavXo(?)wTl0Q!etUyqhyWjky}v zM^zkg&%(9VaO*Yq4Q?@eLakdl28pCb6pi6TN@RR%Z2nF#nsn4dJ|rV+a_nVwc9(Nk zvh1YXTr=hv8LqmS321v0wzcm^;-#iJrG5}9$fB4mTvS-zJ0nOl2vy$o|J%BC62)`E z@39^6_%736<=zXRto-~O$XjfCl-Qw>m9S{o9Bnc_My^o|uCYz`Du|=H6g8#F0C2oy zS8PAZr90rLN5U!ct|qW5V|H+nb}LKrEjDO)s)StNywBK-D+k*>Mr2nEQpkk=b%c1= zZ=k2q$(oSPVpjc;A9dYfgyNnnGVE2wpe=UuY?0rO*CqhWetv5SB%X*fV7PS3Gcowd zAQQ(WaN_Gx33)MH6a`bwra%2~+_zt`vkkF0M!wjTRzyziqb@5nq0*6*#7qB|pn5M) zR3&F*2c9dYr)r*PREuCBot2{{;hq0^?TiV^a&)PIkrV^$&KaZ9w?nt(o1#FQnoa)V zh7l%{@njf4M(fdw`f*R65U<(IsOT^RX1mVT_99Vy+g7oIx2i_Xr%e+Aong24K&kaj zDl@OB<%m}s0>w3td6(!ZG?RScav}L?B<=XF6;|pwJ)IYQ0e7+hwFUgRm~C-Uf0Qr# zmQq^V!>vqg^766WcANRi&MHwkyS^q6OvKST@#iGqd@;0ecj@ zb|7f?2T8qbD`Tq3HN-VjO-T=jt;Od9-)|$+&0Nv|;@f!5qz4z8=Bpp;_llZNoJ{FAkZNU4oCenvo6b>jES-y{~)=v}t3wir#OglP~c6 zIE&yRJdhJbo~JQMd)fON#uY|}HR8c7ssh71RWCLT*8#J_SCA716=P)mbYvpI6WJc* z5nNm$H={7ntosN6-49G=ihl6V)+Sx}-Wb09JxMeA%9Ys2-uLpA9@q36-G9m44Ono1 zP$v0~&AH9@(bI{4D9$ zqPhXE3Y-rL4n=xho_%$WX@n-?Lb6Uj;C5Ic^eFONbIh0ll`n+ppS_(I+-jiIPw^=e zFwd0nZMAktrytJ;{RE?)8S30;d)X|^+xX5{EI5&HNt)mrQan6l)2YN2jyBpat~TiwlgGVGBiqYR;na#U4qvf+EXlO&J|6n^U! zW*@I{pZL&Fstv6jOYB>Xu^zW*y&8p0cPHohpDfQYzabqWd-BwzzK^7CJi?Kfl zNsaTC8?Q>Hk4zE*{nye&Wb15G8O>X;g_XGSK-8+Q*R3su#|)HeurI%y)TB zYq4v!qSrh{l@4}^c%p6lvQ_4QTP#zBk}x5sW%i&G8oMejJ27En{H3%ktli~S5N}6T zzpWABokn8Th-F60Y3eY27k;ZDCC`AJoLzm_m5blc(M-;a?Ng|BFkOJi(`sTh&CAAy z4*~lVekb+TUFN$e} zxaruWrEtsax=akpr|l+o2$oI}^6Na)p=PTKUqQC7J^>r2{482i%^69(R`6l`H^+7m^ri@)f$pc#)TBpUw7=i0wlFJcI?{y9G}BNLxm zR?@iVUAFi|8^K%?0Yox9q_u5bclr`wlu9g`<}MPi)W+W)4Vi|Z``oTFiBe4wl1A3Q z6(z=Fs<1&v$9e51!gn%!PJgXo{~ZIj!MB%gcpOpQmSo z;$Uv#I|bjS4H>VKOIx}1B%E7|g_Qb{Y-^Ywxx**7Oy`w zeItMHW^B)s>h|P3ke#-3%WdlO_bGkIm!H5#f!f$qX{5Zn>of;h5gOi5w8n+DA>u*% z>}r6=#Qy6e)dOP&>)q%~~3Ck9HoG3?>X_ zf%2fEc~sn5fA=Cfl`7I3o#~sJ}k>~1-lw%lT1qt;*k(@?jV3td=u{Ji+5aIJJ_NDNs@|c%0 ziB?@x+&b8N6Tb^L_V&hBz3`X|wg;vS;2#Wnkg1SHfm*PZ#{JvjY`9`cXNy_OtfpGR zWNwE=evtB?xE5}#WqiZJvePF;zlDAt-%*5NrW0P0pFa1DzF##8Hh80uwxIH#$ z#DF)%;)6GVy4yJgiYZ0|r#&8nkaNPZgZk-bTwEy#+R78XOJEsaTixL$&3S@y&%VaE zPVK2J*J(3+9{p**S=5F>WB@U1n@D5@e-1NIKytz%JhPxuAHGQ8^=(Upi2gCgz`%r@C5Q zTDOQ1KyEVkqkg_ML)5FYC8Uo9&1&q(>`0u{73^oZ21eEWDZjr<>-^THF}Uv6nUSgw zqjFLrxV&}rTO@T+AKDx&g$8@IKe~iW)8HQZ^NpMQm#SfH13sfbxP3cRj4chTdPlaP z^0buQK2A90MW_a^0|OD}qVwfIsB&d;iGfa(2BowpAiUrx^) zN`z-*W!+L7w#rbGs$aIlzZV8>Qg))!<4na0Ka!DSa}`)TM2fuSZ9odQ!j|4buW1Cu z!8=iOrkpl6=!LT0`8c1j+Yo=}bAP69QW!kEplPMQu(LF@M_jVf@mT(c`xU+DK8t&T zH2nE)k{R@|23lsGuq7IUQ%W~|n*8|4>M%Di(f6zgfA@z4mq|ag^PX{xVfm$BCVRsa z0T%Rw>>ddBFnVURn4IK12#7V3Ui;I9+*w)V{Akv;F?nDec#9$v9{3wf9L`|e9uwqx zd;B`af&o@ksUsWHVRv^_(jc8E%brV45TRs~Crk%~A&P7lTGg1s?#Sk^mY0ii(4s{N zhpX5x6u0yn-d8=URVA^!I-pnge<$2B?b>wYSIE_fn-^h+t2?`l=eh^Cu{O$dWh zCGk}{Aqo3m@YFxK;4~=R!?8`H+F%8v-Juu2zf{~ja4z7Zc<*7?a1S=mT52@?SyIWn zzJwIgQ>Ultx85Iz(Nzwgn*XF84gV1!;& zK(KE$#TbRdc#1i!Ma7>wR|>dLR+RXdy7sMOal${*UT+ zYLYvF!Bp?Yv+PpMUxW{h*~b{u%4S_z3p|7$pd|4Gt@2*YpY_w`vN;RBl3XhNriMC& zETuD)^?G-`=*@@V%WJ_Z@@GqQ+MI}E7{=5!`Qd8|4!35@SJNX2rq1ii%)gYM6-thn z>Gqfbk-$>lG_ZQ|u|lw=TwAFNaYa|iXD(Jwo)WN4;uc$YLJ`(HR@r73U0h<#3VK@i z$-=F!N{V1|US#=}5sQO1t*}O$1AOGVJh7_|jywW4@m`_dEb+*%+OCR!HjuyePT8AC ziqD_R>3&2pGMiFZ;_`2pL?-tlGA43thzI)s=pO$!Y!}d7gkFC2DF4Bf_|c3`L^_16 z+k-B#9maq<z7zcx zXL~~d3Tx7kJLi(;z>SINixE0H=TWznZ$a|q(EQ@?VtLMG73XfEx*}bf5@iQ?pm0HL zeu-~sMh7K99p&)FR1&D>0EyeJk$ztnA$${@u)yq^jsuoaUEC`~1d zcM#OhX=O{p;c3)h%OMGbZE{vy*>lO{2kjsaAKxYH~MjXZNM$ddfg}= zrJCAu*jqe?h`oVN;TWvC_sP4egGPh3ghb^wYyc65kJ6hAfMZ6Xl#7d@^|Fq+| zcr2Crq5Qr;VVo7SjB&;S$b&yUAGDnR8h>FjtE>JJW!n=dBkz?OU08U&JhNLJ~M18Gex9!FDmKP zt~9NzYBdn0FAje$#B>C&689(EMeS3?I27eTM%OTt!#}M&r-}0s7+<8W(mn9Pf5V=( zPHH~fDi3Nz_CFWb4EG9Dk$Rg`7vDZkDdvV@GVK8n$Df|hw+IJ6Fg_4!{?b5K)%Yo} zg_5-@}o;sn(EXA*9F6McGa|SD((%3mR{$EqQBGA^Ek$vUDrkH$dc7uqPUYHl_cy|+P%s*=b+BW`LGui8BEk((VInCDd zq*h^z%_L#8Xxu+c{54QZuN~LpYZ*;W@HQDV7_lX#*dAp0;6SBD@Os}C^Md#oLTXum2At~-a>%Rbyw1jW)k_MK99veJ9FU-=|H!PlR(BLLJ7$CW@3Ff?NbGHm z8={@>iWO|0X4%t_cav*oPJ_iyKf6eV?5$QCYOD_drRnoclV|TMw9H%PJIjjxI*(Oj zWU1Jf6KWS*31$we5g)vp%pC+0w}RI{5P{s^O}a2&3$sadRf0(L{8%W753Z*j^Rn=54h_ z>cT`;U#3Cr5tuVDS?V8*UxTS>YNufmO#vD*((zOA;-jpkm0tMZtvZY<|fj!auJd2X#Od^!=h zs3X`x`~lN})zm`}NDJd&BG)qydg#RRi%XePg%7h99PEBs(foqJYG7ON`C@o%i(?be z&{C902y4&;y1Nh9BYkGtLR5sHJnp&SetO2z$k39BO8Ml8Ea7XDu{r;!< zA0f$JjpTb5X2n?_^EONAc0gul^=&#Uj-%y<%feKxPYbeHaAOnFa6Cr0T$OdRNd<|# zD?MsVXWjbzse%Ul36jP=^);OicLGCDX9o@WUi5J{6`I7oxmRTj3jLlSphv zd)@>3M?C9b?sb%$tN8$_KYi1nXPV9^CgXuT$c8{~Zw-pItO$UYbZF2?Vh_TFb z?CBUQpmuPvu=r>37_3%Su^n7TL>0M$WfM)? z(ffCLy}`q*CBA^8WguC3*U#;+9laZSX!{3Km#I`Mz2_YRN=6K6^M^S?j!=^3bIyN} z$?!&|7dP~~-It5B%q4Wf*Ni8W@e z#SAP{q4!vO`Q>$q4kjr+1{Z=rBT^GMXedST6P&E|3pK+BB=azDa`G$)e#*kBtA~OJ zWWa>TLDd4AKT)r+u&EfwYk8wRr zY-VlN7B8+io(4+MGf8k8QoPt^_O!x%M-!#0vr_A3-zglTx|xl=nA5M#5fxSb0UGtY z$@Wd4V5_Q`^Pk>zQJf)Rbp{fmr%RYijkdb~L}AZj4w`S#+o-|HrTWPtUur4Oo0+X5 zx;E2Z`zjT??m-sQ#-&!C%!`4zS;SNAg=HgZz0dxOiA)NMXw^9kB`saEQwslFhE;03 zR?A}*$=34I=cxfUs3H_>rLndfn}9Ytjg|>GkI}YwP`8$CiX130YR7#+z`IqY${@VaCaV4wolfcB0rlWYSo`OK=UsOuI5L zrK{iPwkTWZO)tCc zF-oPK3zapQitw_Vj9B^^L9@a;XnR1c>*hF$_1jjKz#2$=bbR?&etxCdjyihW%!>Gj z%bvOI6T@5IUx-b^y{BN&l4-U6@vZ6~$(Or|bAJQ3t}Vf^5aAd;FxGzDSg4>!bdwlx znfY9$Yp@e(xR&>09(I~Mj%JwT*Ta3FQ<2xgQ}p`cbVLZVzi`|SHI~i%Q}G9iz+U=V z>d)mlzjA1ehS1e4M0m4-kQig6X{r52xUkAg^xBf)CNd@y%&~zi;#aHWV{<+J7QNmZRdS(e}>~}WBY;ZK7qcgT? zO1wlAHQ&Vu^(f}k780U6YYDShUk@ zyJltAX&3K^kx<b7iq*5Fs-^ig6Z+iiTtEu7M&P=+c)etITVrMFlef_LOI=eL={uo6 zOpjc6zxdL8J=s&YuMlIbPOyFpeH@O_5%m`Z*g-4%x<$OhC`8KMW|uAg97wNf2`hpj z#jg#QjP1?ttj0C#C4O@YC|esSbw3aHn!lKqa7<(<&51|AWUMV6bW(J{C)351^@Um2 zb{8RPdOZC2mO6(0p}h!v`vj;Xp!jzi>SDAztWlp)dF8L`&D4Bqj|*AJ@&92QL9A{> z<0G_JsjU zB<(n*;B12(sz;r-U14}bddh==5dMCKU|3KGOkH}p7Tw{OKwm)J_1%JVaz2kIS3ki7 zGFIR^6N{kSsdz0#R(Y-opDhMt;JBJ`&Kb(t+UnHInWU&1BsZ+{nRc6MTnL7fLs;9xZiY={T2~gnWt9+tnPeZP z(C&a`UO8i5@dI2In~Ho1(^Qj%rR=l*xn5zW%mK2fX}muFz@)t7$u22rzmzV7 z*-L)EX4j&S2|K9133xQGUv6#c?RR5Y(KO!U^n6cg2At==I?8Xfw{xg`>VMrJd@Kwg zIshT0p~1fB6%tssW%NQhBA1ogGVp`0FUi#!h|S&an_BzngXaPcJI0Nbhzkse1Zw(f zB9gjwr$FG64+*$RYBDKO6RPP%h@wNsH3Uaj;8D1%)(%Gc*p3vYHa7TcJgKrPNKAnJS|s{~0HFe^{5pDJdQ?6zHlkECbXwd1amAf) zNKFFTOpCXA|D%s)Ke|hLAeV@plng7hAxIjx6tHr~MHDMdZ~=U$$5kbSSX3C=bU&Nm zNM+EKN!SgvzbKaw>D=##f1ZfI4>A~~mN2ri&vUiOzx0>4)9Jdy%k20PStWX>ME&$$ zv7xsKWJeKt!C8sygm_8?zxXP#N@LwNCW%PRCcmqo=3*XETMxXmuE8tuf z*gNmk#A~PFi~{Ta8D0-S{e~!l@Ch|=7O#@2JBwuDZ);m;GjxwV!_MdJ)16l|CiLLq zcuMw7csihtLIS}(-rFE(`@~uz$lno|PiPMiqnue9Clx1A2dziP1(86p?7^APgJV1M z(Cq!29?C@IN)~iEq<+Ejr3gQjA6Cu*z--ko%|ewb&C!L`a2s_}wp`(>&-Vl(-V+#Y zf6GFD;hEO_qAec%(IcM?r)~U=!ZO^SDztk33$!+hJ;I!u4ue4nf8DXGDE14my_8Zv zyv0&-zTNAZXD7Z!XkBn0u`Bhvr=~yEeV^?<=bq)~<+x{kc6CZ6!#=WBP6W>?AoTZJ z%lgJ3>wp?}ce~9`9w?5;x@4RedVar&y?OL{NyRO!XglVD`Tn=9j#RCW2qDjj5%IJn$=3wi^mr1>!x$|FpK; z=$IPn$5AG0Q-De1@wB3}1fJR40z??pJ_uH6g!U24f*wkUFU;^UZ5)&P8I8i2vVU=* zjUYQUn-7Jn!MK-GI}(|-9SYsHC%eQ)N9R}>>Wg2vSE4&sz1D3X$6UmiN=yD~l8rI1 zSG-Z6D3z6TGmVgPVufUtePLqiB7H%0^QgmfV0Ldt^8#Gdz~u#ti8~d%-RhXqE_#sN z{9^N{LHPQ-ftdZ+Z(GZ0CNnL0e3FMr;*&}v$jo|awy|v2) z^NwI}u`9_n`jP0-cvRrMeGl}RV#7aM+*q4;uB}-r+O(v^UgHq7(sJ+EYLUr^V7FA* z2i$vYQ=gt+F3-SqQ_?xv)66Pe6MwW7e~zsuQqNWwe#1FSF4HuFZ@U@WHOjqKL2qgnoIfCPLS^>5Bd z6SEtVLo$t&12U)nLwGb0;V=$27nH&;{=px{enUP7{tDqwsAyf(m!F+ecoKF89|!}| z%(BLaQq;B2Ty%2$5GXpvHljY=g7Af*7V%be%u61~%g;CP7xc@v1MT1`(f&4#S4Z%I zbCSh{twWn~R*UR9RT*ndh^tL){vR@LzF(^H2r)}1?J~G)BNKNYUE6zT-^BjAK?bEh zf!q=h0<1i~Hi2G(Y9}JlVS(>%3JWvB1ta+Gat!4|^Kv_Lza(r%mKWr|oz)SS?VMTZ zpL^8Xo~6%fIe9PUOQAFV-CtxZ5Y%M1Or1+3OYIAQE8bpeQSTP5F|lyZ-+ zWDR2(bQs(8EwuO{c|oj1BT$Zwx5Efr!kHCf%wZVk?$s$>CuKKnTTdr3q2~Fky7ltZ6M1sA6ja-q zT-vPi9Qx}Dglhq}ZlD7gF_j1Xl&J6w>P!DGEr~vHQMhUI`pqOL32xY+5#BtvKPM!N9(>(ODQu@g)9E5& z&0sO!%B%6KBN4Eh;`YjpYYC}^#~TvKtWufYkySrtp2B=d+oQ#NajoiRb{FO_KlK!fU z3^v!ol5cQ5rU9|~Q&HwPj&kj2L>cD8Zh5c{b(Bg>wHR@qbK-KFLAB~$90gaRIT)hw zx7c&x*oEI%3|{QZ9#XDKiGijzM8wE|TdgsM5ufj?b*Yzi%0wjMZ}-Xky;Kwdio+`} zd~(cnYJky2Ae)}Ai0E5};lsv^O=$Jut8i|Gj8s%{@mY|@b4t`#g`ZGbpCx{%3E4$r zu{sc^wYcUKg#$g|wDptC+1SqKs)pB5Dju^^qMNCC*P`!~uIDlQmFPl`|713o;c$dQc2(ER57+LJ>nM=((X6%Hc-tKqL-` zBg`B7nFAZI(?fVBOHq~QwznDg;`2pxy*kTFiv!_bt}ZQACd%irR5WAeMx7^^yyf7k7FOrpD*2l4%YQc! zzeu}3IBAJ_V=Ii(-{hx0luu&*kqOEH*q{FMp5Ve_tZpSo-dtq9zeu+duvtoCfJsuw zH<7tFm1;51U-GKor@uA}_HUjqMh{x&_%J+WC!5me%evAP`0U!jAj9N&iWbjl6WvBB zi)7iHuX4XAWR z_8~RKNEl5umgrm#dPP-*EDW<9e!-uJgOH_`kNo}9&F%J6V@H0ZAN#0?5iknKt<&9C zMVTbtM93aDdB|0}pQm@khztOKzTe-zcJBd99j#y=Di>M7-Jd2o6(`T$S1 zo?su0^a6@izh4%G!J{@z(0?#iwz;IlK`3hR`JaEh<(bW z>)ORR=znt>&UR*4Mh|T?-P%!zfzXT`k)x3^jcrOWA4%?NGwJS6$Odl2Ez+wfRilOG zuVaZzWpMQ%<5yNpdE*oc2K)o;Z2#?F1|<*;gUz54pF@$>{L|f z{iERNOp-C@Dw+|9zZmsvoifJQk9R+%>9+IS;BLxvo8MDow^H-=$#o2cOW|8W&2;S2 z04EpSjOf+fx4GWWZv=cJ^#^lZ&}Xw!f&j$9C&r5V+<3avmbn)9DtQ6A5;R^#RoX&!}3pz;TDC;k?=ve(#Cc7djNN2pE{f^O4r%>z^N>E2FQ?CqTH?i7vfuX*i1aYtvt^XJ`%~1(y`- z{>B`>aU`w!lyN0jBvtR$pq0)E*O2v1n*9GPl2)Pf5<)V+z&34t55$Z1$H&WlT;$63 zX7@1Ehk|q<`ujRYj7Hl`Ot&ua{mYg=TEOPt7Druc(`_r$z;x|!zZ+IL50(Cjnxk)J z0cz*)&vi0j94khoOw8!8AtQB9e!=hB z&M#QRk56VX{xaXyeQH2Q6t@Ip<2HgeujabZ!R*Qi>bpK+nwAd9MQz(~XaX4>!uHj6 z?5`aMKg0?3n#}=)>pJ)*i^_HI-V<5pv)z!(-`)?^H?!12{--C#DspIils1!Klt*?y z$M;ZB3+(jEbu=a6DZx(42#f!wfoN`-#&=PntL9GpYrYty7fXJ!xG${8c&>Gco+F=# zx6dp~b`~*vArUlVf$$=1xea!&DCRe6$J}kkg^_eQG7cc>yM&*o@lJ68ol7eS4X2-W z`F%m#N3)&IcAw)6FLm;KyEqzI7_R>}dXS`2bbAWnipZx@Fc{)t|v$R&lXsvq(;2c7Ywl z51AcaP^J<2plF^E{-6jn&MfM_kUv1S7?Gp9aCIf7(z2LZI8p+S^Bib=%=FuO3q&V` z$5q9T+@>?FNypn&K|Gm-#>j@3Ckz-}E3J|1iU@cWV1Nmg$G3w!=ij zMaX(4-hgSI22CE5)Uei~m&Z@K)WPL&~D_Ugvb=0@S+%nD+x!>t+H0rJD< z*@A7PW0S&c`OTIri+C%^ii|d?;}(~DwB)7gbWv9&PV{Grb&E?o!VzAJL6b>x$(08U z6zj{4o2;#}G+_A+O3oZeKu(}A{?2oq#|ehajP_cLmcWreK&ObSizLOBy~ zC$mRa8MQnqU6Gr+F%d-UkDTH4`qme7;BbIwH^h&$iwwBpR+|z_Wl^H^Ou@%y+Pw{M zT6&;fXsEhy+w)j7|Mtj81StoPDf^nh$_AKulHPKWeuqWlO-h6Vx%v*H!M`lrlMOJ)DCyZ9bgow!^6~YpW?%2biq6Ym zZ=j+$8p5caAJUwMC*hEMyYbq#@oWbLI3^D9U8r^cmC&{qlOe zBGBqxc?Heg){}z=I0=ilFS$ptCAj);7&nvp8j^p>c=$~=P^Sm2IAH?{HxgMCP}P5< z#jv}A!0>lywP!@hrXCwuD@ncU!`k}W#CyIOA-d2|gQH!KKN}`o<|Yd-#7WShn|}YEXhNh4@YDVf&;wPF%CsMIoOul4mJZhgE(VSoH){r_Hrs1jT~#{W|m< zW-P`zu|$$jd47J&VXE@fCSuM54sCN1^H6Tj&M7D7a{XS!swQzb%Rda7@MynWzc7gF zBoCvCpR*f1HrYX@4F3~{sa)Uuyfkjj(r3Gy0ii@b`c-pJy=BAB)BI9&8N_jE$!bqf ze=>Aox8~+SZ8SLe%Hfsq$Wo)!?U-n!{!FykT!3~J1IJXUV$}T$mbJg9g#kxIZZDTUTB)d|Q12YzXjjADdQ{x;b#fs*?-tywf=8~K{u_5M zH?tfmHe^i>6e{HYuFWpqx8quMBr*;d$t*C3o2(T1G4K#TrnDW)L3=TIXJ+XA2D+iM zCkJOPQ;_LE4}}`~VFx+j4K9uhvzuE}e zv>zP)oEx+Q=zT5{l#E91b%Ki;f;k)Py@faguTV^$igjf$`>UALC&kyj+>HC59O4~} z;(lOw*io31iJU2VqZr?{S(|zI+A6FXULZ|gKca2aU5DL0<&X2c*KhYYT_rB}zcrco zq%VR3)s*8dFs050Ta=K7O+A?+^y8@jvhVWpyA@(ULs^kBzafMKv4PdEdo}+KJu} zT-;Tj)kXig4o+S|NSe~Hj!YrpuOeZGuBWNPUTY`?Pi^|JgCJ*>VCa|M{ae(0>G(KD zgV!;Shk$t}O(DbVSWEn*E}So6XNSPQ%e|E832+0z<{j(i9meFtCa5_|L-K-v+FjT6 z194mI1~0ZUE#fKy%WR;S z>zfj_IV&1`hTL|tZJOA^G0>tpmudp2SWQjE0@5fD5>@x56}4&^pL!M+SyYn9rXNDh zX}^l0eg@<-9W$#uN>|&^^@r8t!#Q!Im9u<8iK*=XSBI! zJm1C@b%@hUn+^^_t0|d!zoMSKH>5?zEL(@sej9wE>@sFiu%BmT}BZbb#I+ewZu=lJpMJcg$_tJgc42@6cuN&Y$e-Yh~vDkC=V-o%puCCP{dZ-Ym*=a=p4^CobHkO_oJwZUjTfo-lZ1r5DYiHTj3ia%? zr~Q)faAjOS1s}P`uNB)kCj=zx$S^rpXD1tyS{`dQ%8ZCAzCm!|Nc1w5w8KL_Q9J2jK82Iq@O^ayBKJ(JO5RH*Ip5NnT|5p763GE3$q$Ab|2yKlRS! z$9M2ZJveuZd7A-Jj&{rjt2iNX<`AD{*^FY!foC%Qcl5-&znZ#vTQfik1J1eM^@hCl zALUgBv48H+kmZ^_QdWXN0)M={TJCwEeyYOVNBVhpVF}Z@#<1T>!P)WL66(u`E!t7W zAobO;3T_ zKOCrbchVMPxMIGR4kd-JL43F|v*BeF#5^snMFq+*xh&J70RFXLvsSQw3XS>j35~j8GHy_r?l7wO-cS6?sM&icBP{1rlfeTn1T7nT%S&TZg7Mz{sf)GcE74Hx|BQc+{cG^d82b3 zIW+l;2`M|GZdfVcv*hY*rKAh|_1^(H>ls}a zsq8{+qaZ}7CKxhy^a|8+2W^k4(+JtJndOF|2qBHvwb7>|U7w8|wo&A>OW@iU{Uxkt zwCCNP;J>wiYxsqL(7DpCcVUjH{U97d1OlPeVJ*s;30quOcz8T5N@R+b&6voYjtp%H ztop;r2IcnJIj_OQzt^GIIGH$cfav`X?HsL?k?05w1}GL{#{!Q56dO*x@#63J*P}-z zdTWyCfP7wluna$ss2}jO$vLss3d-BKO*=eQ1&>bn?FBE7w0MRA--C>m9+ndi`prG~ zrice3Zw?UJV`#y4_ub3)9X>Sq*bY6;DPEERhH zNbis9y1+O9nYhq*lZ05EK3eV&fhV4&^^hUN*=E^U+nHdF;)OwfCxqNAcwsF4sTmPu9Vl!}WRF)<$v; z&l5L%CYnP1{@kQc1GWGB~Vc9#bY$L0LMZt(8wOROc?hd0gv_c*UZnHxY zqfN)7_gbuY2EgA{Y3r(!_V4JU$z6Z(Q_Ue0$5i`fi%tNRlm!P=%)?JO2Xq%?(Ho(k0tT2oXX#Kwq26IUYFc zILhReMJyV$Uj@6WnWR?U-PYJcuKtpWYyeTPdFPnKKmjzPI`?jpUmQfrG>)%pG)W9F zh(e^G)V6oL>jXi!eis&eTiR83E&plyVPCC^HE7G5KjSAFh$H3Fiu{j{NgGZs?Qeqn z`p0_be0qfuo+Zyu0*8K*Dl^{?iuLvN+Ro(bKUd~~;TigBQrMwHQWAdfT6ns=W_ZH= zE%WJr$j*Ryt@1>{7C#dBFtNUY)z1{_9TC-qFj}x<#5R^O&TeibFT3XFm!`bu1AY@4 zIH#TJ6ZsOg&Bw14o8vN|MT;@|DjC@>#PN3V+fG$>u)RbP7x{2Z&W~Y^6H5XZDJXl9 zcf!t8YE}3KF^&;qr4qtz)_Yx{f&i}l@vr(%URl8ITPP_vRE+Wl7}0IsULps+_t+cf z#OCMd=M?G8#hJxi9BrCt>l?Q#`_=RJB6UJO;o$QsSBEKF7U8C+ab&q~CNW(G!{1UJ z@Ki_jQrVU$bZasUD{%9Yv3WD6ufHN^4XC}8*iLsU(0U39u^A-3VJ8tX%ediG_+Zc! zz0Ct(ts|==XkLoF$NkO~dCc8r*WdLhp4?v^LFK56 z#NzKKLoM^iRnSCS+!Y$F<&RLEgMcHda@Ntq_k%M2tDidp^#VZy+z>7R?( ziR_)RFQmb{DrxpNerPQ&*4=^%X%cn z)>DT&?@L$cQc`vo%03l2O~50k;pt7$`DjuFtE!V6S$aAje|&hqd-Y_pez4{2znEin ze0F=zw%`A@l|PT8J%v(WX*B!JR(Lhg+Sb2WC~LGNl!lz%nupRik!km!9gED{Y=@QM zA9f`Ok1GUwCyXUWvSw%K)T9-?IsEv}IBn4NkxAGMQvdDL^rKdN{PH)d}@&KYxIXV3d>9p zR+sc*hn15Kzf7HgB^MPxtMcYjOr! zr@vkf2;ep|=Z#2Mvid-r7{aYRgUr^+fjK7Ak!iB?<2C7HtM)_6mUocA50_n<2W`pn z;s_18WjIXnsrov-7W97PmqFg1eSy({X}OA#gT|10cReIzVRl-fba3oN(1>l5VeuxW zU}8eQxh2`zG-GktUj!ax)*eEZdUJS*-n#@>-y|tGsJ?0--eimi?lnKxCj#(uIX zyzAi9h`OW$D!pZs*Qbsj$tjNO;8Fku0~r$swDJ7XK2B5X{sG%%{Be<-_QrhWrenZ4 zCE}+U?0*^_Cu<_%P#8O-U1w56I5)97Okclny{B>_ENcw$8K-lyRSKmbo}1rlItNu5;ZP zPUy0c_;n}DdU1ov$7V9;h~uz`gvN7oA=i~`k#Y_vZ1oAr(_m&M8g4wuj+5urcch|Y z$_B8ra&rY|tH_m>hj|0~PVV%-&JUCvKP@UCM5c=M_aOSlle9>JpR#=xQU?Fj#|v~~ zuT=`jCHu$_#$sR|uTzkdSyOV>F7)b)ZL}t{7u!S^blgq#Rz0;RtTx0p|I!$oCCB_{ zCt#$@#j_AbCA!qR4k!P@+9fp@y}G?2ITs*>k^(Yq%>Pzj1Oob=?hlkT9K-%;I%VJ{ zIS^MmvUJCtJsGi#hRGx#iN`ZS4?Ptc#W>#H~d~IY=_d)Nu5Jw@ObR;RTSvg}yqUlx`mM2#z&XrK-S9 z?n$vIk1x+G&Q~`fZ@4uS3sAv-96K7MA*(q-57KjLD|bu_yM&TBmLwo=)Gn_l+pr@! z9^L5r>Vg#z?PVgiLQrQF`N*ME8EujVN3N51tECmRE&~J%u=L7o{0CoR2_(E-Y|u!2 zO+NzVVbxezqrEQn;VmJqB3QO2bID|p7msLiuXWmtX@5fqF}F3bLqXAvyZGVa4i?lgs`JG9C!Zg}mf+-4!)bBO6dU5Y|{DA2SHJ!NrI(JTT;OO@x z7)53(&vuk1l8gz%NQrV?H?@WShZ=-hSH;p60*J=fBFbj!qOTmR+_~b}GBfSvrtF9f zU{=YXs_StEwm@GLKQeqvacp;UBrJ2wlh*58A-4?Gtv=CBuS`xZ+&j+wSJJYOmGR)c zX!VeT5e@ttdHbIYDiGPW;FNDaSuC0QeIofSc{&EE;K%VS4Jl@Ws9c2nffeDx9WePl z)3B@v6dY|mNH}rU)3d3I_(L4$aVeoUo*lVV(DzHQl{grv(2n@*D$J9+c7E8w*DZ(15WR z=j29{-olk~E_Ds({Wj(=mF63k0{D_{2rrHKXUajs{ZboIReTlWuJX}&EMC@;x7(3^ zk8GmGNldN|ww}3dXs*w;D57i{SN`=s6rqC_+k_>lXTMl)U*<8=xBZqS8cKLZ#pxdk zBY#|M1gT=($U|3&nPnH%J9eth=vj?176q8ng)kSxRZ98|XcOC^&HVlclz-jWqQl7_PB{} z3jY5%atM|A3Ge0Qdu0oWGrTcNmu{>Bd&Ui71B%wPoM70+>g(Dx>KC&?+bvhS$6~TL zvH_#-H_Q&scVR1c_>IrJbNNcu|8Ry8O|;owXlo|hN#y`uH|+{!2SSwU7L#6x~%4aZ{|LeG)nF&VJbkJ(Ple4(?$URr5+U1|C5*Td>Drdji z>W+QZm+J)8emqY9gAW(_M8!pEgH5Izsg~=3LRYQ@^dHcO-2weLB1Jd!_o{^vvZ^H! zLf2ssbZr37*oO9oFhzo3+bZMpK!fHtp?0W@mx8^xFZ~r=$7GiHs6T2t>dZ=8wy=5p zO{3+IFPa#9!h-DddAm#)Oxy+Lb~z^ppP(V_5AB_}&#DOXW2i+Y zLOMw?i=<}ERQL}V_;IOW9SXG-*hV}-yh7>oNEuSCS-#F|`8}@A6_~bU9 z4Y7HUc=w%xr0EXua7yr{Wr^zv(|DIWo&ZJBc(P9x2c$kYRoT@=m%mqr%rPa0_WnH= z#xmg7dZyW0$?OlG8V)I0LLzI-Fu<|^5jF`Fnn5n$Tx(leMSjqwz+}M`-Z*oQ9x>B(3pI0M> zo3=OUHwo|IIJaVbgE|-PVrJZBq))P)dN(BGLY-5T8;lk4>cJv z1l6xj`_DHhP5iZslY@Lwac!k7S)<`=8Ex>>mt6s*p+EW8bkah^>e#!Ntg3f;O3~@s zwU$rwXFcy}6d6>(Pz6;S7riGfDnM+_oS*SxVM)xg`FCKPGC`O=J<-;LRR*-AiOKN{ zI8hn&Ub#$dLV(k2NlNJbeYDg#taZ{e_27<1RY8)?Yhh1Q*#BNilTRt@1#jwy321(ul>R|m zIR}jOw6}j@8oMSLH_iafum|t*nM_=rrlsJuhJqADn(gAy6#CqL(9(Uy;4B~B_@rH? z6KpTyS|gwGsJc_yn6x9!Tul{dFF4A@E6%A8A8ODp~lKVheGNwL_rp}gUnf_MZ@3~a>Q?s9GPj;%@DWAf~;fobwU(tFXsgbs^L01&CX z6T7%Ch4QQN9>B+D803`l#-LB4^^D2vO1(z4|BmKnjgyzaG5c4L51rL;eRi&r5g)}LOX2)mP7Eyj9_FA7! z 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 DJjuHe delta 311372 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%0DdDz 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

(ed*}0U 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�-)p7+uN_$Y2Q^v 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 diff --git a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl index 16e57bd3c6..b69b23b588 100644 --- a/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl +++ b/docs/src/literate-tutorials/maxwell_good_bad_ugly.jl @@ -9,9 +9,9 @@ solution for a Maxwell problem, when vectorized `Lagrange` interpolations conver ![Results for different discretizations](maxwell.png) -**Figure 1**: The results of this tutorial, showing how the analytical solution is not found when discretizing +**Figure 1**: The results of this tutorial, showing how the exact solution is not found when discretizing the problem using `Lagrange` interpolations, but when using a `Nedelec` interpolation, we converge to the -analytical solution as the mesh size, ``h``, decreases. +exact solution as the mesh size, ``h``, decreases. ## Loading packages We start by adding the required packages for this tutorial @@ -38,15 +38,8 @@ Specifically, we will consider the problem to determine the vector-valued field where ``\boldsymbol{t}`` is the normalized tangential vector along the boundary, ``\Gamma``, of the domain, ``\Omega``. The rotated L-shaped domain is located such that the sharp internal corner -is at the origin. -=# -fig = Plt.Figure() -ax = Plt.Axis(fig[1, 1]; xlabel = "x", ylabel = "y") -points = [(0, 0), (1, 0), (1, 1), (-1, 1), (-1, -1), (0, -1), (0, 0)] -Plt.lines!(ax, first.(points), last.(points)) -fig #hide +is at the origin, see Figure 1. -#= ## Theoretical background We have the following partial integration rules, where ``\boldsymbol{n}`` is the outward pointing normal vector, following Monk (2003) [Monk2003; Eqs. (3.47) and (3.51)](@cite), @@ -89,9 +82,10 @@ along the lines, ``\theta = 0`` and ``\theta = 3\pi/2``, ``\sin(2\theta/3) = 0`` singularity at ``\boldsymbol{x} = \boldsymbol{0}``, this doesn't enter the boundary conditions. Finally, due to the singularity, the components of ``\boldsymbol{E}_\mathrm{exact}`` are not in ``H^1(\Omega)``. -**TODO:** *Explain why ``\mathrm{div}(\boldsymbol{E})=0`` is fullfilled, use divergence theorem?* -To evaluate the accuracy of the different discretizations, we will use the analytical solution to +**TODO:** Explain why ``\mathrm{div}(\boldsymbol{E})=0`` is fullfilled, use divergence theorem? + +To evaluate the accuracy of the different discretizations, we will use the exact solution to evaluate the boundary condition, ``g = \boldsymbol{E}_\mathrm{exact}\cdot \boldsymbol{t} \quad \text{on }\Gamma``. A correct discretization should then reproduce @@ -219,28 +213,28 @@ function create_data(tr::Triangulation, fieldname::Symbol, a; f = identity) return data end -# ## Analytical implementation +# ## Exact implementation mesh_size = 0.01 grid = setup_grid(mesh_size; origin_refinement = 1) dh_ana = close!(add!(DofHandler(grid), :u, DiscontinuousLagrange{RefTriangle, 1}()^2)) -function analytical_potential(x::Vec{2}) # Analytical potential to be differentiated +function exact_potential(x::Vec{2}) # Exact potential to be differentiated Δθ = -3π / 4 # Rotate discontinuous line to 4th quadrant xp = rotate(x, Δθ) r = sqrt(x ⋅ x + eps()) θ = r ≤ 1.0e-6 ? zero(eltype(x)) : (atan(xp[2], xp[1]) - Δθ) return r^(2 // 3) * sin(2θ / 3) end -analytical_solution(x::Vec{2}) = gradient(analytical_potential, x) +exact_solution(x::Vec{2}) = gradient(exact_potential, x) a_ana = zeros(ndofs(dh_ana)) -apply_analytical!(a_ana, dh_ana, :u, analytical_solution); +apply_analytical!(a_ana, dh_ana, :u, exact_solution); #= ## Error calculation -We will calculate the error, ``e(h)``, between the analytical, +We will calculate the error, ``e(h)``, between the exact, ``\boldsymbol{E}_\mathrm{exact}``, and numerical, ``\boldsymbol{E}_\mathrm{h}(h)``, solutions as integral norm, ```math @@ -297,8 +291,8 @@ end function solve_lagrange(dh) ip = Ferrite.getfieldinterpolation(dh, Ferrite.find_field(dh, :E)) ch = ConstraintHandler(dh) - add!(ch, Dirichlet(:E, getfacetset(dh.grid, "horizontal_facets"), (x, _) -> gradient(analytical_potential, x)[2], [2])) - add!(ch, Dirichlet(:E, getfacetset(dh.grid, "vertical_facets"), (x, _) -> gradient(analytical_potential, x)[1], [1])) + add!(ch, Dirichlet(:E, getfacetset(dh.grid, "horizontal_facets"), (x, _) -> gradient(exact_potential, x)[2], [2])) + add!(ch, Dirichlet(:E, getfacetset(dh.grid, "vertical_facets"), (x, _) -> gradient(exact_potential, x)[1], [1])) close!(ch) cv = CellValues(QuadratureRule{RefTriangle}(1), ip) @@ -309,7 +303,7 @@ function solve_lagrange(dh) work!(as, db) apply!(K, f, ch) a = K \ f - l2_vals = L2Error(0.0, 0.0, analytical_solution) + l2_vals = L2Error(0.0, 0.0, exact_solution) work!(Integrator(l2_vals), db; a) return a, sqrt(l2_vals.l2error) / l2_vals.volume end @@ -351,7 +345,7 @@ function solve_nedelec(dh) CT = getcelltype(dh.grid) ch = ConstraintHandler(dh) - add!(ch, WeakDirichlet(:E, getfacetset(dh.grid, "boundary_facets"), (x, _, n) -> analytical_solution(x) × n)) + add!(ch, WeakDirichlet(:E, getfacetset(dh.grid, "boundary_facets"), (x, _, n) -> exact_solution(x) × n)) add!(ch, Dirichlet(:ϕ, getfacetset(dh.grid, "boundary_facets"), Returns(0.0))) close!(ch) @@ -366,7 +360,7 @@ function solve_nedelec(dh) work!(as, db; a) apply!(K, f, ch) a .= K \ f - l2_vals = L2Error(0.0, 0.0, analytical_solution) + l2_vals = L2Error(0.0, 0.0, exact_solution) work!(Integrator(l2_vals), db; a) return a, sqrt(l2_vals.l2error) / l2_vals.volume end @@ -438,7 +432,7 @@ end fig = let fig = Plt.Figure(size = (1000, 600)) - m_ana = plot_field(fig[1, 1], dh_ana, :u, a_ana, "Analytical"; plot_edges = false, colorrange = (-2, 0)) + m_ana = plot_field(fig[1, 1], dh_ana, :u, a_ana, "Exact"; plot_edges = false, colorrange = (-2, 0)) m_lag = plot_field(fig[1, 2], dh_lagrange, :E, a_lagrange, "Lagrange (h = $mesh_size)"; plot_edges = false, colorrange = (-2, 0)) m_ned = plot_field(fig[1, 3], dh_nedelec, :E, a_nedelec, "Nedelec (h = $mesh_size)"; plot_edges = false, colorrange = (-2, 0)) Plt.Colorbar(fig[1, 4], m_ned; label = "E₁") From dfaf1d3350ea8e974b41ae41b5cd8b37985cfa7b Mon Sep 17 00:00:00 2001 From: Knut Andreas Date: Wed, 5 Feb 2025 21:01:31 +0100 Subject: [PATCH 172/172] Include changes from #1136 --- src/interpolations.jl | 4 +- test/test_interpolations.jl | 542 ++++++++++++++++++------------------ test/test_refshapes.jl | 20 ++ test/test_utils.jl | 32 +-- 4 files changed, 309 insertions(+), 289 deletions(-) create mode 100644 test/test_refshapes.jl diff --git a/src/interpolations.jl b/src/interpolations.jl index e41681e36d..3e5e598f75 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -498,8 +498,8 @@ function reference_coordinates(ip::DiscontinuousLagrange{RefTetrahedron, 0}) end function reference_shape_value(ip::DiscontinuousLagrange{shape, 0}, ::Vec{dim, T}, i::Int) where {dim, shape <: AbstractRefShape{dim}, T} - i > 1 && throw(ArgumentError("no shape function $i for interpolation $ip")) - return one(T) + i == 1 && return one(T) + throw(ArgumentError("no shape function $i for interpolation $ip")) end is_discontinuous(::Type{<:DiscontinuousLagrange}) = true diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index 776c6e7559..3eb67c94fd 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -1,88 +1,200 @@ using Ferrite: reference_shape_value, reference_shape_gradient -@testset "interpolations" begin - @testset "Value Type $value_type" for value_type in (Float32, Float64) - @testset "Correctness of $interpolation" for interpolation in ( - Lagrange{RefLine, 1}(), - Lagrange{RefLine, 2}(), - Lagrange{RefQuadrilateral, 1}(), - Lagrange{RefQuadrilateral, 2}(), - Lagrange{RefQuadrilateral, 3}(), - Lagrange{RefTriangle, 1}(), - Lagrange{RefTriangle, 2}(), - Lagrange{RefTriangle, 3}(), - Lagrange{RefTriangle, 4}(), - Lagrange{RefTriangle, 5}(), - Lagrange{RefHexahedron, 1}(), - Lagrange{RefHexahedron, 2}(), - Serendipity{RefQuadrilateral, 2}(), - Serendipity{RefHexahedron, 2}(), - Lagrange{RefTetrahedron, 1}(), - Lagrange{RefTetrahedron, 2}(), - Lagrange{RefPrism, 1}(), - Lagrange{RefPrism, 2}(), - Lagrange{RefPyramid, 1}(), - Lagrange{RefPyramid, 2}(), - # - DiscontinuousLagrange{RefLine, 0}(), - DiscontinuousLagrange{RefQuadrilateral, 0}(), - DiscontinuousLagrange{RefHexahedron, 0}(), - DiscontinuousLagrange{RefTriangle, 0}(), - DiscontinuousLagrange{RefTetrahedron, 0}(), - DiscontinuousLagrange{RefLine, 1}(), - DiscontinuousLagrange{RefQuadrilateral, 1}(), - DiscontinuousLagrange{RefHexahedron, 1}(), - DiscontinuousLagrange{RefTriangle, 1}(), - DiscontinuousLagrange{RefTetrahedron, 1}(), - DiscontinuousLagrange{RefPrism, 1}(), - DiscontinuousLagrange{RefPyramid, 1}(), - # - BubbleEnrichedLagrange{RefTriangle, 1}(), - # - CrouzeixRaviart{RefTriangle, 1}(), - CrouzeixRaviart{RefTetrahedron, 1}(), - RannacherTurek{RefQuadrilateral, 1}(), - RannacherTurek{RefHexahedron, 1}(), - ) - # Test of utility functions - ref_dim = Ferrite.getrefdim(interpolation) - ref_shape = Ferrite.getrefshape(interpolation) - func_order = Ferrite.getorder(interpolation) - @test typeof(interpolation) <: Interpolation{ref_shape, func_order} - - # Note that not every element formulation exists for every order and dimension. - applicable(Ferrite.getlowerorder, interpolation) && @test typeof(Ferrite.getlowerorder(interpolation)) <: Interpolation{ref_shape, func_order - 1} - @testset "transform face points" begin - # Test both center point and random points on the face - ref_coord = Ferrite.reference_coordinates(Lagrange{ref_shape, 1}()) - for face in 1:nfacets(interpolation) - face_nodes = Ferrite.reference_facets(ref_shape)[face] - center_coord = [0.0 for _ in 1:ref_dim] - rand_coord = [0.0 for _ in 1:ref_dim] - rand_weights = rand(length(face_nodes)) - rand_weights /= sum(rand_weights) - for (i, node) in pairs(face_nodes) - center_coord += ref_coord[node] / length(face_nodes) - rand_coord += rand_weights[i] .* ref_coord[node] - end - for point in (center_coord, rand_coord) - vec_point = Vec{ref_dim}(point) - cell_to_face = Ferrite.element_to_facet_transformation(vec_point, ref_shape, face) - face_to_cell = Ferrite.facet_to_element_transformation(cell_to_face, ref_shape, face) - @test vec_point ≈ face_to_cell - end - end +""" + test_interpolation_properties(ip::Interpolation) + +This function tests the following implementation details for an +interpolation. All base interpolations should pass this test, but +`VectorizedInterpolation`s do not which is ok as these are +special-cased in the code base. + +A) Length of `dof_indices` and `dof_interior_indices` + matches number of reference shape entities (e.g. `edge`) +B) Numbering convention + - vertices -> edges -> faces -> volume + - Numbered in entity order (e.g. edge 1 has lower indices than edge 2) + - Continuous and increasing numbering within entity (e.g. `edgedof_interior_indices(ip)[edgenr]`) + can be `(4, 5)`, but not `(4, 6)` or `(5, 4)`. +C) Lower-dimensional entities' dof indices + current interior dof indices + matches current dof indices (e.g. vertexdof + edge_interior => edgedofs) for each entity. +D) The dof indices values matches `1:N` without duplication (follows from B, but also checked separately) +E) All `N` base functions are implemented + `ArgumentError` if `i=0` or `i=N+1` +F) Interpolation accessor functions versus type parameters (e.g. same refshape) +""" +function test_interpolation_properties(ip::Interpolation{RefShape, FunOrder}) where {RefShape, FunOrder} + return @testset "Interpolation properties: $ip" begin + # test accessor functions and type parameters + @test RefShape == getrefshape(ip) + @test Ferrite.getrefdim(RefShape) == Ferrite.getrefdim(ip) + @test Ferrite.getorder(ip) == FunOrder + + rdim = Ferrite.getrefdim(ip) + + as_vector(t::Tuple) = collect(as_vector.(t)) + as_vector(i::Int) = i + dof_data = ( + vert = as_vector(Ferrite.vertexdof_indices(ip)), + edge = as_vector(Ferrite.edgedof_indices(ip)), + face = as_vector(Ferrite.facedof_indices(ip)), + edge_i = as_vector(Ferrite.edgedof_interior_indices(ip)), + face_i = as_vector(Ferrite.facedof_interior_indices(ip)), + vol_i = as_vector(Ferrite.volumedof_interior_indices(ip)), + n = getnbasefunctions(ip), + ) + + refshape_data = ( + nvertices = as_vector(Ferrite.nvertices(RefShape)), + edges = as_vector(Ferrite.reference_edges(RefShape)), + faces = as_vector(Ferrite.reference_faces(RefShape)), + rdim = rdim, + ) + + # Test A-D + _test_interpolation_properties(dof_data, refshape_data) + + # Test E: All base functions implemented. + # Argument errors for 0th and n+1 indices. + ξ = zero(Vec{Ferrite.getrefdim(ip)}) + @test_throws ArgumentError Ferrite.reference_shape_value(ip, ξ, 0) + for i in 1:getnbasefunctions(ip) + @test Ferrite.reference_shape_value(ip, ξ, i) isa Ferrite.shape_value_type(ip, Float64) + end + @test_throws ArgumentError Ferrite.reference_shape_value(ip, ξ, getnbasefunctions(ip) + 1) + end +end + +# Brake out to avoid compiling new function for each interpolation +function _test_interpolation_properties(dofs::NamedTuple, rs::NamedTuple) + collect_all_dofs(t::Union{Tuple, Vector}) = vcat(Int[], collect.(t)...) + # Check match to reference shape (A) + @test length(dofs.vert) == rs.nvertices + @test length(dofs.edge) == length(rs.edges) + @test length(dofs.edge_i) == length(rs.edges) + @test length(dofs.face) == length(rs.faces) + @test length(dofs.face_i) == length(rs.faces) + + # Check numbering convention (B) and entity matching + all_dofs = Int[] + # Vertices numbered first + append!(all_dofs, collect_all_dofs(dofs.vert)) + @test all(all_dofs .== 1:length(all_dofs)) + if rs.rdim ≥ 1 # Test edges + # Edges numbered next, no gaps or missing numbers. Sorted by edge number. + all_edofs_i = collect_all_dofs(dofs.edge_i) + @test all(all_edofs_i .== length(all_dofs) .+ (1:length(all_edofs_i))) + # - all edge dofs include both vertexdofs and interior edegdofs, and nothing more. + append!(all_dofs, all_edofs_i) + @test all(all_dofs .== 1:length(all_dofs)) + @test length(all_dofs) == length(collect_all_dofs(dofs.vert)) + length(all_edofs_i) + # Coarse check for C + @test Set(collect_all_dofs(dofs.edge)) == Set(1:length(all_dofs)) + # - test each edge individually (Detailed check for C) + for (edge_nr, edge_vertices) in enumerate(rs.edges) + vdofs_e = Int[] # dofs.vert for vertices belonging to the current edge + for j in edge_vertices # vertices in edge i + isempty(dofs.vert[j]) || append!(vdofs_e, collect(dofs.vert[j])) + end + @test Set(dofs.edge[edge_nr]) == Set(vcat(vdofs_e, collect(dofs.edge_i[edge_nr]))) + end + end + if rs.rdim ≥ 2 # Test faces + # Face numbered next, no gaps or missing numbers. Sorted by face number. + all_fdofs_i = collect_all_dofs(dofs.face_i) + @test all(all_fdofs_i .== length(all_dofs) .+ (1:length(all_fdofs_i))) + # - all dofs now include vertex dofs, edge dofs and face dofs, but not volume dofs. + append!(all_dofs, all_fdofs_i) + @test all(all_dofs .== 1:length(all_dofs)) + # Coarse check for C + @test Set(collect_all_dofs(dofs.face)) == Set(1:length(all_dofs)) + # - test each face individually (Detailed check for C) + for (facenr, face_verts) in enumerate(rs.faces) + vdofs_f = Int[] + for j in face_verts # vertices in face i + vdof_indices = dofs.vert[j] + isempty(vdof_indices) || append!(vdofs_f, collect(vdof_indices)) + end + edofs_f = Int[] # Interior edgedofs for edges belong to current face + for (edgenr, edge_verts) in enumerate(rs.edges) + # Both edge vertices belong to face => edge belongs to face + (edge_verts[1] ∈ face_verts && edge_verts[2] ∈ face_verts) || continue + append!(edofs_f, collect(dofs.edge_i[edgenr])) end - n_basefuncs = getnbasefunctions(interpolation) - coords = Ferrite.reference_coordinates(interpolation) - @test length(coords) == n_basefuncs - f(x) = [reference_shape_value(interpolation, Tensor{1, ref_dim}(x), i) for i in 1:n_basefuncs] - - #TODO prefer this test style after 1.6 is removed from CI - # @testset let x = sample_random_point(ref_shape) # not compatible with Julia 1.6 - x = Vec{ref_dim, value_type}(sample_random_point(ref_shape)) - random_point_testset = @testset "Random point test" begin + @test Set(dofs.face[facenr]) == Set(vcat(vdofs_f, edofs_f, collect(dofs.face_i[facenr]))) + end + end + # Test volume + # We always test this, since volumedofs are also used by lower-dimensional + # discontinuous inteprolations to make them internal to the cell, e.g. DiscontinuousLagrange + # Volumedofs numbered last + append!(all_dofs, collect(dofs.vol_i)) + @test all(all_dofs .== 1:length(all_dofs)) # Numbering convention + + # Test D: getnbasefunctions matching number of dof indices + return @test length(all_dofs) == dofs.n +end +@testset "interpolations" begin + @testset "Correctness of $interpolation" for interpolation in ( + Lagrange{RefLine, 1}(), + Lagrange{RefLine, 2}(), + Lagrange{RefQuadrilateral, 1}(), + Lagrange{RefQuadrilateral, 2}(), + Lagrange{RefQuadrilateral, 3}(), + Lagrange{RefTriangle, 1}(), + Lagrange{RefTriangle, 2}(), + Lagrange{RefTriangle, 3}(), + Lagrange{RefTriangle, 4}(), + Lagrange{RefTriangle, 5}(), + Lagrange{RefHexahedron, 1}(), + Lagrange{RefHexahedron, 2}(), + Serendipity{RefQuadrilateral, 2}(), + Serendipity{RefHexahedron, 2}(), + Lagrange{RefTetrahedron, 1}(), + Lagrange{RefTetrahedron, 2}(), + Lagrange{RefPrism, 1}(), + Lagrange{RefPrism, 2}(), + Lagrange{RefPyramid, 1}(), + Lagrange{RefPyramid, 2}(), + # + DiscontinuousLagrange{RefLine, 0}(), + DiscontinuousLagrange{RefQuadrilateral, 0}(), + DiscontinuousLagrange{RefHexahedron, 0}(), + DiscontinuousLagrange{RefTriangle, 0}(), + DiscontinuousLagrange{RefTetrahedron, 0}(), + DiscontinuousLagrange{RefLine, 1}(), + DiscontinuousLagrange{RefQuadrilateral, 1}(), + DiscontinuousLagrange{RefHexahedron, 1}(), + DiscontinuousLagrange{RefTriangle, 1}(), + DiscontinuousLagrange{RefTetrahedron, 1}(), + DiscontinuousLagrange{RefPrism, 1}(), + DiscontinuousLagrange{RefPyramid, 1}(), + # + BubbleEnrichedLagrange{RefTriangle, 1}(), + # + CrouzeixRaviart{RefTriangle, 1}(), + CrouzeixRaviart{RefTetrahedron, 1}(), + RannacherTurek{RefQuadrilateral, 1}(), + RannacherTurek{RefHexahedron, 1}(), + ) + # Standard test all base interpolations must fullfill + test_interpolation_properties(interpolation) + + ref_dim = Ferrite.getrefdim(interpolation) + ref_shape = Ferrite.getrefshape(interpolation) + func_order = Ferrite.getorder(interpolation) + + # Note that not every element formulation exists for every order and dimension. + if applicable(Ferrite.getlowerorder, interpolation) + @test isa(Ferrite.getlowerorder(interpolation), Interpolation{ref_shape, func_order - 1}) + end + + n_basefuncs = getnbasefunctions(interpolation) + coords = Ferrite.reference_coordinates(interpolation) + @test length(coords) == n_basefuncs + + @testset "Value Type $value_type" for value_type in (Float32, Float64) + @testset let x = Vec{ref_dim, value_type}(sample_random_point(ref_shape)) # Check gradient evaluation + f(ξ) = [reference_shape_value(interpolation, Vec{ref_dim}(ξ), i) for i in 1:n_basefuncs] @test vec(ForwardDiff.jacobian(f, Array(x))') ≈ reinterpret(value_type, [reference_shape_gradient(interpolation, x, i) for i in 1:n_basefuncs]) # Check partition of unity at random point. @@ -91,104 +203,72 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test_throws ArgumentError reference_shape_value(interpolation, x, n_basefuncs + 1) # Idempotency test @test reference_shape_value(interpolation, x, n_basefuncs) == reference_shape_value(interpolation, x, n_basefuncs) - end - # Remove after 1.6 is removed from CI (see above) - # Show coordinate in case failure (see issue #811) - !isempty(random_point_testset.results) && println("^^^^^Random point test failed at $x for $interpolation !^^^^^") - - # Test whether we have for each entity corresponding dof indices (possibly empty) - @test length(Ferrite.vertexdof_indices(interpolation)) == Ferrite.nvertices(interpolation) - @test length(Ferrite.facedof_indices(interpolation)) == Ferrite.nfaces(interpolation) - @test length(Ferrite.facedof_interior_indices(interpolation)) == Ferrite.nfaces(interpolation) - @test length(Ferrite.edgedof_indices(interpolation)) == Ferrite.nedges(interpolation) - @test length(Ferrite.edgedof_interior_indices(interpolation)) == Ferrite.nedges(interpolation) - # We have at least as many edge/face dofs as we have edge/face interior dofs - @test all(length.(Ferrite.facedof_interior_indices(interpolation)) .<= length.(Ferrite.facedof_indices(interpolation))) - @test all(length.(Ferrite.edgedof_interior_indices(interpolation)) .<= length.(Ferrite.edgedof_indices(interpolation))) - # The total number of dofs must match the number of base functions - totaldofs = sum(length.(Ferrite.vertexdof_indices(interpolation)); init = 0) - totaldofs += sum(length.(Ferrite.facedof_interior_indices(interpolation)); init = 0) - totaldofs += sum(length.(Ferrite.edgedof_interior_indices(interpolation)); init = 0) - totaldofs += length(Ferrite.volumedof_interior_indices(interpolation)) - @test totaldofs == n_basefuncs - - # The dof indices are valid. - @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.vertexdof_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.facedof_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.facedof_interior_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.edgedof_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.edgedof_interior_indices(interpolation)]) - @test all([all(0 .< i .<= n_basefuncs) for i in Ferrite.volumedof_interior_indices(interpolation)]) - - # Check for evaluation type correctness of interpolation - @testset "return type correctness dof $dof" for dof in 1:n_basefuncs - @test (@inferred reference_shape_value(interpolation, x, dof)) isa value_type - @test (@inferred reference_shape_gradient(interpolation, x, dof)) isa Vec{ref_dim, value_type} - end - # Check for dirac delta property of interpolation - @testset "dirac delta property of dof $dof" for dof in 1:n_basefuncs - for k in 1:n_basefuncs - N_dof = reference_shape_value(interpolation, coords[dof], k) - if k == dof - @test N_dof ≈ 1.0 - else - factor = interpolation isa Lagrange{RefQuadrilateral, 3} ? 200 : 4 - @test N_dof ≈ 0.0 atol = factor * eps(value_type) - end + # Check for evaluation type correctness of interpolation + for dof in 1:n_basefuncs + @test (@inferred reference_shape_value(interpolation, x, dof)) isa value_type + @test (@inferred reference_shape_gradient(interpolation, x, dof)) isa Vec{ref_dim, value_type} end end + end - # Test that facedof_indices(...) return in counter clockwise order (viewing from the outside) - if interpolation isa Lagrange - function __outward_normal(coords::Vector{<:Vec{1}}, nodes) - n = coords[nodes[1]] - return n / norm(n) - end - function __outward_normal(coords::Vector{<:Vec{2}}, nodes) - p1 = coords[nodes[1]] - p2 = coords[nodes[2]] - n = Vec{2}((p2[2] - p1[2], - p2[1] + p1[1])) - return n / norm(n) - end - function __outward_normal(coords::Vector{<:Vec{3}}, nodes) - p1 = coords[nodes[1]] - p2 = coords[nodes[2]] - p3 = coords[nodes[3]] - n = (p3 - p2) × (p1 - p2) - return n / norm(n) - end - _bfunc = if ref_dim == 3 - Ferrite.facedof_indices(interpolation) - elseif ref_dim == 2 - Ferrite.edgedof_indices(interpolation) - elseif ref_dim == 1 - Ferrite.vertexdof_indices(interpolation) - end - for (facenodes, normal) in zip(_bfunc, reference_normals(interpolation)) - @test __outward_normal(coords, facenodes) ≈ normal + # Check for dirac delta property of interpolation + @testset "dirac delta property of dof $dof" for dof in 1:n_basefuncs + for k in 1:n_basefuncs + N_dof = reference_shape_value(interpolation, coords[dof], k) + if k == dof + @test N_dof ≈ 1.0 + else + factor = interpolation isa Lagrange{RefQuadrilateral, 3} ? 200 : 4 + @test N_dof ≈ 0.0 atol = factor * eps(typeof(N_dof)) end end + end - # regression for https://github.com/Ferrite-FEM/Ferrite.jl/issues/520 - interpolation_type = typeof(interpolation).name.wrapper - if func_order > 1 && interpolation_type != Ferrite.Serendipity - first_order = interpolation_type{ref_shape, 1}() - for (highorderface, firstorderface) in zip(Ferrite.facedof_indices(interpolation), Ferrite.facedof_indices(first_order)) - for (h_node, f_node) in zip(highorderface, firstorderface) - @test h_node == f_node - end + # Test that facedof_indices(...) return in counter clockwise order (viewing from the outside) + if interpolation isa Lagrange + function __outward_normal(coords::Vector{<:Vec{1}}, nodes) + n = coords[nodes[1]] + return n / norm(n) + end + function __outward_normal(coords::Vector{<:Vec{2}}, nodes) + p1 = coords[nodes[1]] + p2 = coords[nodes[2]] + n = Vec{2}((p2[2] - p1[2], - p2[1] + p1[1])) + return n / norm(n) + end + function __outward_normal(coords::Vector{<:Vec{3}}, nodes) + p1 = coords[nodes[1]] + p2 = coords[nodes[2]] + p3 = coords[nodes[3]] + n = (p3 - p2) × (p1 - p2) + return n / norm(n) + end + normals = reference_normals(getrefshape(interpolation)) + for (facetnodes, normal) in zip(Ferrite.facetdof_indices(interpolation), normals) + @test __outward_normal(coords, facetnodes) ≈ normal + end + end + + # regression for https://github.com/Ferrite-FEM/Ferrite.jl/issues/520 + interpolation_type = typeof(interpolation).name.wrapper + if func_order > 1 && interpolation_type != Ferrite.Serendipity + first_order = interpolation_type{ref_shape, 1}() + for (highorderface, firstorderface) in zip(Ferrite.facedof_indices(interpolation), Ferrite.facedof_indices(first_order)) + for (h_node, f_node) in zip(highorderface, firstorderface) + @test h_node == f_node end - if ref_dim > 2 - for (highorderedge, firstorderedge) in zip(Ferrite.edgedof_indices(interpolation), Ferrite.edgedof_indices(first_order)) - for (h_node, f_node) in zip(highorderedge, firstorderedge) - @test h_node == f_node - end + end + if ref_dim > 2 + for (highorderedge, firstorderedge) in zip(Ferrite.edgedof_indices(interpolation), Ferrite.edgedof_indices(first_order)) + for (h_node, f_node) in zip(highorderedge, firstorderedge) + @test h_node == f_node end end end + end - # VectorizedInterpolation + @testset "VectorizedInterpolation" begin v_interpolation_1 = interpolation^2 v_interpolation_2 = (d = 2; interpolation^d) @test getnbasefunctions(v_interpolation_1) == @@ -199,12 +279,18 @@ using Ferrite: reference_shape_value, reference_shape_gradient # Check for evaluation type correctness of vectorized interpolation v_interpolation_3 = interpolation^ref_dim - @testset "vectorized case of return type correctness of dof $dof" for dof in 1:n_basefuncs - @test @inferred(reference_shape_value(v_interpolation_1, x, dof)) isa Vec{2, value_type} - @test @inferred(reference_shape_gradient(v_interpolation_3, x, dof)) isa Tensor{2, ref_dim, value_type} + + @testset "Value Type $value_type" for value_type in (Float32, Float64) + x = Vec{ref_dim, value_type}(sample_random_point(getrefshape(v_interpolation_1))) + @testset "vectorized case of return type correctness of dof $dof" for dof in 1:n_basefuncs + @test @inferred(reference_shape_value(v_interpolation_1, x, dof)) isa Vec{2, value_type} + @test @inferred(reference_shape_gradient(v_interpolation_3, x, dof)) isa Tensor{2, ref_dim, value_type} + end end - end # correctness testset + end + end + @testset "Discontinuous interpolations" begin @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefTriangle, 0}()) ≈ [Vec{2, Float64}((1 / 3, 1 / 3))] @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefQuadrilateral, 0}()) ≈ [Vec{2, Float64}((0, 0))] @test Ferrite.reference_coordinates(DiscontinuousLagrange{RefTetrahedron, 0}()) ≈ [Vec{3, Float64}((1 / 4, 1 / 4, 1 / 4))] @@ -222,6 +308,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test Ferrite.is_discontinuous(d_ip) == true @test Ferrite.is_discontinuous(d_ip_t) == true end + @testset "Correctness of AD of embedded interpolations" begin ip = Lagrange{RefHexahedron, 2}()^3 ξ = rand(Vec{3, Float64}) @@ -235,6 +322,7 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test G ≈ G_sa @test H ≈ H_sa end + ips = Lagrange{RefQuadrilateral, 2}() vdim = 3 ipv = ips^vdim @@ -247,7 +335,20 @@ using Ferrite: reference_shape_value, reference_shape_gradient @test g ≈ G[v_ind, :] @test v ≈ V[v_ind] end - end # + end + + @testset "Errors for entitydof_indices on VectorizedInterpolations" begin + ip = Lagrange{RefQuadrilateral, 2}()^2 + @test_throws ArgumentError Ferrite.vertexdof_indices(ip) + @test_throws ArgumentError Ferrite.edgedof_indices(ip) + @test_throws ArgumentError Ferrite.facedof_indices(ip) + @test_throws ArgumentError Ferrite.facetdof_indices(ip) + + @test_throws ArgumentError Ferrite.edgedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.facedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.volumedof_interior_indices(ip) + @test_throws ArgumentError Ferrite.facetdof_interior_indices(ip) + end reference_cell(::Type{RefTriangle}) = Triangle((1, 2, 3)) reference_cell(::Type{RefQuadrilateral}) = Quadrilateral((1, 2, 3, 4)) @@ -319,89 +420,11 @@ using Ferrite: reference_shape_value, reference_shape_gradient return val end - # There is some overlap with tests above, this tests some properties more carefully, - # and does not assume nodal base functions. - function test_interpolation_numbering(ip::Interpolation) - RefShape = getrefshape(ip) - collect_all_dofs(t::Tuple) = vcat(Int[], collect.(t)...) - vdofs = collect_all_dofs(Ferrite.vertexdof_indices(ip)) - edofs = collect_all_dofs(Ferrite.edgedof_indices(ip)) - fdofs = collect_all_dofs(Ferrite.facedof_indices(ip)) - edofs_i = collect_all_dofs(Ferrite.edgedof_interior_indices(ip)) - fdofs_i = collect_all_dofs(Ferrite.facedof_interior_indices(ip)) - voldofs_i = Ferrite.volumedof_interior_indices(ip) - - # Check match to reference shape - @test length(Ferrite.vertexdof_indices(ip)) == Ferrite.nvertices(RefShape) - @test length(Ferrite.edgedof_indices(ip)) == Ferrite.nedges(RefShape) - @test length(Ferrite.edgedof_interior_indices(ip)) == Ferrite.nedges(RefShape) - @test length(Ferrite.facedof_indices(ip)) == Ferrite.nfaces(RefShape) - @test length(Ferrite.facedof_interior_indices(ip)) == Ferrite.nfaces(RefShape) - - # Check numbering convention - # Vertices numbered first - @test all(vdofs .== 1:length(vdofs)) - # Edges numbered next, no gaps or missing numbers. Sorted by edge number. - all_edofs_i = vcat(collect.(edofs_i)...) - @test all(all_edofs_i .== length(vdofs) .+ (1:length(all_edofs_i))) - # - all edge dofs include both vertexdofs and interior edegdofs, and nothing more. - all_edofs = vcat(collect.(edofs)...) - @test all(all_edofs .== 1:length(all_edofs)) - @test length(all_edofs) == length(vdofs) + length(all_edofs_i) - # - test each edge invidividually - for i in 1:Ferrite.nedges(RefShape) - vdofs_e = Int[] - for j in Ferrite.reference_edges(RefShape)[i] - vdof_indices = Ferrite.vertexdof_indices(ip)[j] - isempty(vdof_indices) || append!(vdofs_e, collect(vdof_indices)) - end - edofs_e = collect(Ferrite.edgedof_interior_indices(ip)[i]) - @test Set(Ferrite.edgedof_indices(ip)[i]) == Set(vcat(vdofs_e, edofs_e)) - end - - # Face numbered next, no gaps or missing numbers. Sorted by face number. - all_fdofs_i = vcat(collect.(fdofs_i)...) - @test all(all_fdofs_i .== length(all_edofs) .+ (1:length(all_fdofs_i))) - # - all face dofs include edgedofs and interior facedofs, and nothing more. - all_fdofs = vcat(collect.(fdofs)...) - @test all(all_fdofs .== 1:length(all_fdofs)) - # - test each face individually - for i in 1:Ferrite.nfaces(RefShape) - face_verts = Ferrite.reference_faces(RefShape)[i] - vdofs_f = Int[] - for j in face_verts - vdof_indices = Ferrite.vertexdof_indices(ip)[j] - isempty(vdof_indices) || append!(vdofs_f, collect(vdof_indices)) - end - edofs_f = Int[] - for (edgenr, edge) in enumerate(Ferrite.reference_edges(RefShape)) - (edge[1] ∈ face_verts && edge[2] ∈ face_verts) || continue - append!(edofs_f, collect(Ferrite.edgedof_interior_indices(ip)[edgenr])) - end - fdofs_f = collect(Ferrite.facedof_interior_indices(ip)[i]) - @test Set(Ferrite.facedof_indices(ip)[i]) == Set(vcat(vdofs_f, edofs_f, fdofs_f)) - end - - # Volumedofs numbered last - voldofs = vcat(all_fdofs, collect(voldofs_i)) - @test length(voldofs) == getnbasefunctions(ip) # Correct total number of dofs - @test all(voldofs .== 1:length(voldofs)) # Numbering convention - - # All base functions implemented. Argument errors for 0th and n+1 indices. - ξ = zero(Vec{Ferrite.getrefdim(ip)}) - @test_throws ArgumentError Ferrite.reference_shape_value(ip, ξ, 0) - for i in 1:getnbasefunctions(ip) - @test Ferrite.reference_shape_value(ip, ξ, i) isa Ferrite.shape_value_type(ip, Float64) - end - @test_throws ArgumentError Ferrite.reference_shape_value(ip, ξ, getnbasefunctions(ip) + 1) - - end - Hcurl_interpolations = [Nedelec{2, RefTriangle, 1}(), Nedelec{2, RefTriangle, 2}()] # Nedelec{3, RefTetrahedron, 1}(), Nedelec{3, RefHexahedron, 1}()] Hdiv_interpolations = [RaviartThomas{2, RefTriangle, 1}(), RaviartThomas{2, RefTriangle, 2}(), BrezziDouglasMarini{2, RefTriangle, 1}()] - test_interpolation_numbering.(Hcurl_interpolations) - test_interpolation_numbering.(Hdiv_interpolations) + test_interpolation_properties.(Hcurl_interpolations) + test_interpolation_properties.(Hdiv_interpolations) # Required properties of shape value Nⱼ of an edge-elements (Hcurl) on an edge with direction v, length L, and dofs ∈ 𝔇 # 1) Unit property: ∫(Nⱼ ⋅ v f(s) dS) = 1 ∀ ∈ 𝔇 @@ -655,19 +678,4 @@ using Ferrite: reference_shape_value, reference_shape_gradient end end end - - @testset "Errors for entitydof_indices on VectorizedInterpolations" begin - ip = Lagrange{RefQuadrilateral, 2}()^2 - @test_throws ArgumentError Ferrite.vertexdof_indices(ip) - @test_throws ArgumentError Ferrite.edgedof_indices(ip) - @test_throws ArgumentError Ferrite.facedof_indices(ip) - @test_throws ArgumentError Ferrite.facetdof_indices(ip) - - @test_throws ArgumentError Ferrite.edgedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.facedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.volumedof_interior_indices(ip) - @test_throws ArgumentError Ferrite.facetdof_interior_indices(ip) - end # =# - - end # testset diff --git a/test/test_refshapes.jl b/test/test_refshapes.jl new file mode 100644 index 0000000000..e0d4126d2f --- /dev/null +++ b/test/test_refshapes.jl @@ -0,0 +1,20 @@ +@testset "ReferenceShape" begin + for RefShape in (RefLine, RefTriangle, RefQuadrilateral, RefTetrahedron, RefHexahedron, RefPyramid, RefPrism) + @testset "transform facet points" begin + # Test both center point and random points on the facet + ref_coords = Ferrite.reference_coordinates(Lagrange{RefShape, 1}()) + for facet in 1:nfacets(RefShape) + facet_nodes = collect(Ferrite.reference_facets(RefShape)[facet]) + facet_coords = ref_coords[facet_nodes] + center_coord = sum(facet_coords) / length(facet_nodes) + rand_weights = rand(length(facet_nodes)) + rand_coord = sum(rand_weights .* facet_coords) / sum(rand_weights) + for point in (center_coord, rand_coord) + cell_to_facet = Ferrite.element_to_facet_transformation(point, RefShape, facet) + facet_to_cell = Ferrite.facet_to_element_transformation(cell_to_facet, RefShape, facet) + @test point ≈ facet_to_cell + end + end + end + end +end diff --git a/test/test_utils.jl b/test/test_utils.jl index 98ff5f53ed..0a3b092742 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -28,21 +28,21 @@ function reference_face_area(fs::Interpolation{RefPyramid}, face::Int) end ###################################################### -# Coordinates and normals for the reference elements # +# Coordinates and normals for the reference shapes # ###################################################### -reference_normals(ip::VectorizedInterpolation) = reference_normals(ip.ip) +function reference_normals(::Interpolation{RefShape}) where {RefShape} + @warn "Using reference normals of Interpolation, use of RefShape directly instead" + return reference_normals(RefShape) +end -# Lagrange{1, RefLine} -function reference_normals(::Lagrange{RefLine}) +function reference_normals(::Type{RefLine}) return [ Vec{1, Float64}((-1.0,)), Vec{1, Float64}((1.0,)), ] end - -# Lagrange{2, RefQuadrilateral} -function reference_normals(::Lagrange{RefQuadrilateral}) +function reference_normals(::Type{RefQuadrilateral}) return [ Vec{2, Float64}((0.0, -1.0)), Vec{2, Float64}((1.0, 0.0)), @@ -51,8 +51,7 @@ function reference_normals(::Lagrange{RefQuadrilateral}) ] end -# Lagrange{2, RefTriangle} -function reference_normals(::Lagrange{RefTriangle}) +function reference_normals(::Type{RefTriangle}) return [ Vec{2, Float64}((1 / √2, 1 / √2)), Vec{2, Float64}((-1.0, 0.0)), @@ -60,8 +59,7 @@ function reference_normals(::Lagrange{RefTriangle}) ] end -# Lagrange{3, RefTetrahedron} -function reference_normals(::Lagrange{RefTetrahedron}) +function reference_normals(::Type{RefTetrahedron}) return [ Vec{3, Float64}((0.0, 0.0, -1.0)), Vec{3, Float64}((0.0, -1.0, 0.0)), @@ -70,8 +68,7 @@ function reference_normals(::Lagrange{RefTetrahedron}) ] end -# Lagrange{3, Cube} -function reference_normals(::Lagrange{RefHexahedron}) +function reference_normals(::Type{RefHexahedron}) return [ Vec{3, Float64}((0.0, 0.0, -1.0)), Vec{3, Float64}((0.0, -1.0, 0.0)), @@ -82,8 +79,7 @@ function reference_normals(::Lagrange{RefHexahedron}) ] end -# Lagrange{3, Wedge} -function reference_normals(::Lagrange{RefPrism}) +function reference_normals(::Type{RefPrism}) return [ Vec{3, Float64}((0.0, 0.0, -1.0)), Vec{3, Float64}((0.0, -1.0, 0.0)), @@ -93,8 +89,7 @@ function reference_normals(::Lagrange{RefPrism}) ] end -# Lagrange{3, RefPyramid} -function reference_normals(::Lagrange{RefPyramid}) +function reference_normals(::Type{RefPyramid}) return [ Vec{3, Float64}((0.0, 0.0, -1.0)), Vec{3, Float64}((0.0, -1.0, 0.0)), @@ -104,9 +99,6 @@ function reference_normals(::Lagrange{RefPyramid}) ] end -# Serendipity{2, RefQuadrilateral} -reference_normals(::Serendipity{RefQuadrilateral, 2}) = reference_normals(Lagrange{RefQuadrilateral, 1}()) - ################################## # Valid coordinates by expanding # # and rotating reference shape #