From 6d564ed129242bebc0986ee48684d8a2c4fcfdbc Mon Sep 17 00:00:00 2001 From: lijas Date: Thu, 7 Sep 2023 07:55:35 +0200 Subject: [PATCH 01/50] wip --- Project.toml | 3 +- src/IGA.jl | 57 +++++++++++--------------------- src/bezier_grid.jl | 55 ++++++++++++------------------- src/splines/bezier.jl | 75 +++++++++++++++++++++---------------------- 4 files changed, 77 insertions(+), 113 deletions(-) diff --git a/Project.toml b/Project.toml index 4abdb4b..8a5db2b 100644 --- a/Project.toml +++ b/Project.toml @@ -13,5 +13,4 @@ Tensors = "48a634ad-e948-5137-8d70-aa71f2a747f4" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" [compat] -WriteVTK = "1.13" -Ferrite = "< 0.3.13" \ No newline at end of file +WriteVTK = "1.13" \ No newline at end of file diff --git a/src/IGA.jl b/src/IGA.jl index b164f27..d820fd9 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -22,32 +22,30 @@ struct BezierCoords{dim_s,T} wb ::Vector{T} x ::Vector{Vec{dim_s,T}} w ::Vector{T} - beo ::BezierExtractionOperator{T} + beo ::Base.RefValue{ BezierExtractionOperator{T} } end #Base.zero(Type{BezierCoords{dim,T}}) where {dim,T} = BezierCoords """ - BezierCell{dim,N,order,M} <: Ferrite.AbstractCell{dim,N,M} + BezierCell{order,refshape,N} <: Ferrite.AbstractCell{refshape} -`dim` = spacial dimension `N` = number of nodes/controlpoints `order` = tuple with order in each parametric dimension (does not need to be equal to `dim`) -`M` = number of faces (used by Ferrite) (4 in 2d, 6 in 3d) """ -struct BezierCell{dim,N,order,M} <: Ferrite.AbstractCell{dim,N,M} +struct BezierCell{order,refshape,N} <: Ferrite.AbstractCell{refshape} nodes::NTuple{N,Int} - function BezierCell{dim,N,order,M}(nodes::NTuple{N,Int}) where {dim,N,order,M} + function BezierCell{order}(nodes::NTuple{N,Int}) where {order,N} @assert(order isa Tuple) @assert(prod(order.+1)==N) - return new{dim,N,order,M}(nodes) + refdim = length(order) + refshape = Ferrite.RefHypercube{refdim} + + return new{order,refshape,N}(nodes) end end -BezierCell{dim,N,order}(nodes::NTuple{N,Int}) where {dim,N,order} = - BezierCell{dim,N,order,(2,4,6)[dim]}(nodes) - -getorders(::BezierCell{dim,N,orders}) where {dim,N,orders} = orders +getorders(::BezierCell{orders,refshape,N}) where {orders,refshape,N} = orders include("bezier_extraction.jl") include("nurbsmesh.jl") @@ -59,16 +57,15 @@ include("splines/bsplines.jl") include("VTK.jl") include("L2_projection.jl") -Ferrite._mass_qr(::BernsteinBasis{2, (2, 2)}) = QuadratureRule{2,RefCube}(2+1) +Ferrite._mass_qr(::BernsteinBasis{2, (2, 2)}) = QuadratureRule{RefQuadrilatiral}(2+1) #Normaly the verices function should only return the 8 corner nodes of the hexa (or 4 in 2d), #but since the cell connectivity in IGA is different compared to normal FE elements, #we can only distribute cells on the nodes/controlpoints Ferrite.vertices(c::BezierCell) = c.nodes -_bernstein_ordering(::Type{<:BezierCell{dim,N,orders}}) where {dim,N,orders} = _bernstein_ordering(BernsteinBasis{length(orders),orders}()) +_bernstein_ordering(::Type{<:BezierCell{order}}) where {order} = _bernstein_ordering(BernsteinBasis{length(order),order}()) -#Dim 2 function Ferrite.faces(c::BezierCell{dim,N,order}) where {dim,N,order} return getindex.(Ref(c.nodes), collect.(Ferrite.faces(BernsteinBasis{length(order),order}() ))) end @@ -77,33 +74,17 @@ function Ferrite.edges(c::BezierCell{dim,N,order}) where {dim,N,order} return getindex.(Ref(c.nodes), collect.(Ferrite.edges(BernsteinBasis{length(order),order}() ))) end -Ferrite.default_interpolation(::Type{BezierCell{dim,N,order,M}}) where {dim,N,order,M} = BernsteinBasis{length(order),order}() +Ferrite.default_interpolation(::Type{BezierCell{order}}) where {order} = BernsteinBasis{length(order),order}() -# -function Ferrite.cell_to_vtkcell(::Type{BezierCell{2,N,order,M}}) where {N,order,M} - if length(order) == 2 - return Ferrite.VTKCellTypes.VTK_BEZIER_QUADRILATERAL - else - return Ferrite.VTKCellTypes.VTK_BEZIER_CURVE - end -end -function Ferrite.cell_to_vtkcell(::Type{BezierCell{3,N,order,M}}) where {N,order,M} - if length(order) == 3 - return Ferrite.VTKCellTypes.VTK_BEZIER_HEXAHEDRON - elseif length(order) == 2 - return Ferrite.VTKCellTypes.VTK_BEZIER_QUADRILATERAL - else - return Ferrite.VTKCellTypes.VTK_BEZIER_CURVE - end +function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefHexahedron}}) where {order} + return Ferrite.VTKCellTypes.VTK_BEZIER_HEXAHEDRON end - -function Ferrite.cell_to_vtkcell(::Type{BezierCell{1,N,order,M}}) where {N,order,M} - if length(order) == 1 - return Ferrite.VTKCellTypes.VTK_BEZIER_CURVE - else - error("adsf") - end +function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefQuadrilatiral}}) where {order} + return Ferrite.VTKCellTypes.VTK_BEZIER_QUADRILATERAL +end +function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefLine}}) where {order} + return Ferrite.VTKCellTypes.VTK_BEZIER_CURVE end end #end module \ No newline at end of file diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 5b38cc3..6186686 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -5,8 +5,6 @@ struct BezierGrid{dim,C<:Ferrite.AbstractCell,T<:Real} <: Ferrite.AbstractGrid{d weights ::Vector{Float64} beo ::Vector{BezierExtractionOperator{Float64}} end -Ferrite.getdim(g::BezierGrid{dim}) where {dim} = dim -getT(g::BezierGrid) = eltype(first(g.nodes).x) function BezierGrid(cells::Vector{C}, nodes::Vector{Ferrite.Node{dim,T}}, @@ -20,9 +18,7 @@ function BezierGrid(cells::Vector{C}, boundary_matrix::SparseArrays.SparseMatrixCSC{Bool,Int} = SparseArrays.spzeros(Bool, 0, 0)) where {dim,C,T} - grid = Ferrite.Grid(cells, nodes; - cellsets=cellsets, nodesets=nodesets, facesets=facesets, - edgesets, vertexsets, boundary_matrix=boundary_matrix) + grid = Ferrite.Grid(cells, nodes; cellsets, nodesets, facesets, cellsets, vertexsets, boundary_matrix) return BezierGrid{dim,C,T}(grid, weights, extraction_operator) end @@ -97,28 +93,28 @@ end end function Ferrite.getweights(grid::BezierGrid, ic::Int) - #TODO, this can be optimzed, but this function should not be called in performance critical code nodeids = collect(grid.cells[ic].nodes) return grid.weights[nodeids] end -function Ferrite.getcoordinates(grid::BezierGrid{dim,C,T}, ic::Int) where {dim,C,T} - n = Ferrite.nnodes_per_cell(grid, ic) - w = zeros(T, n) - wb = zeros(T, n) - xb = zeros(Vec{dim,T}, n) - x = zeros(Vec{dim,T}, n) - - # copy to avoid reference the extraction operator saved in the grid - bc = BezierCoords(xb, wb, x, w, copy(grid.beo[ic])) - - return getcoordinates!(bc,grid,ic) +function Ferrite.getcoordinates!(bc::BezierCoords{dim,T}, grid::BezierGrid, ic::Int) where {dim,T} + get_bezier_coordinates!(bc.xb, bc.wb, bc.x, bc.w, grid, ic) + bc.beo[] = grid.beo[ic] + return bc end -function Ferrite.getcoordinates!(bc::BezierCoords, grid::BezierGrid, ic::Int) - get_bezier_coordinates!(bc.xb, bc.wb, bc.x, bc.w, grid, ic) - copyto!(bc.beo, get_extraction_operator(grid, ic)) - return bc +function Ferrite.getcoordinates(grid::BezierGrid{dim,C,T}, ic::Int) where {dim,C,T} + + n = Ferrite.nnodes_per_cell(grid, ic) + w = zeros(T, n) + wb = zeros(T, n) + xb = zeros(Vec{dim,T}, n) + x = zeros(Vec{dim,T}, n) + + bc = BezierCoords(xb, wb, x, w, Ref(grid.beo[ic])) + getcoordinates!(bc,grid,ic) + + return bc end function get_bezier_coordinates!(xb::AbstractVector{Vec{dim,T}}, @@ -149,12 +145,10 @@ function get_bezier_coordinates!(xb::AbstractVector{Vec{dim,T}}, end xb ./= wb - return xb, wb + return nothing end -function get_bezier_coordinates(grid::BezierGrid, ic::Int) - - dim = Ferrite.getdim(grid); T = getT(grid) +function get_bezier_coordinates(grid::BezierGrid{dim,C,T}, ic::Int) where {dim,C,T} n = Ferrite.nnodes_per_cell(grid, ic) w = zeros(T, n) @@ -166,8 +160,7 @@ function get_bezier_coordinates(grid::BezierGrid, ic::Int) return xb, wb end -function get_nurbs_coordinates(grid::BezierGrid, cell::Int) - dim = Ferrite.getdim(grid); T = getT(grid) +function get_nurbs_coordinates(grid::BezierGrid{dim,C,T}, cell::Int) where {dim,C,T} nodeidx = grid.cells[cell].nodes return [grid.nodes[i].x for i in nodeidx]::Vector{Vec{dim,T}} end @@ -176,7 +169,7 @@ function get_extraction_operator(grid::BezierGrid, cellid::Int) return grid.beo[cellid] end -Ferrite_to_vtk_order(::Type{<:Ferrite.AbstractCell{dim,N,M}}) where {dim,N,M} = 1:N +#Ferrite_to_vtk_order(::Type{<:Ferrite.AbstractCell{dim,N,M}}) where {dim,N,M} = 1:N # Store the Ferrite to vtk order in a cache for specific cell type let cache = Dict{Type{<:BezierCell}, Vector{Int}}() @@ -192,10 +185,4 @@ let cache = Dict{Type{<:BezierCell}, Vector{Int}}() end end end -end - -function Ferrite.MixedDofHandler(grid::BezierGrid{dim,C,T}) where {dim,C,T} - #TODO: Remove this function and add method Ferrite.MixedDofHandler(::AbstractGrid) in Ferrite - ncells = getncells(grid) - Ferrite.MixedDofHandler{dim,T,typeof(grid)}(FieldHandler[], Ferrite.CellVector(Int[],zeros(Int,ncells),zeros(Int,ncells)), Ferrite.CellVector(Int[],Int[],Int[]), Ferrite.CellVector(Vec{dim,T}[],Int[],Int[]), Ferrite.ScalarWrapper(false), grid, Ferrite.ScalarWrapper(-1)) end \ No newline at end of file diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index e380ddc..82f8997 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -1,7 +1,7 @@ -export BernsteinBasis +export Bernstein """ - BernsteinBasis{dim,order}() + Bernstein{dim,order}() The Bertnstein polynominal spline basis. Usually used as the cell interpolation in IGA, together with bezier extraction + BezierValues. @@ -9,28 +9,25 @@ IGA, together with bezier extraction + BezierValues. `dim` - The spacial dimentsion of the interpolation `order` - A tuple with the order in each parametric direction. """ -struct BernsteinBasis{dim,order} <: Ferrite.Interpolation{dim,Ferrite.RefCube,order} - function BernsteinBasis{dim,order}() where {dim,order} - @assert(length(order) == dim) - # Make order into tuple for 1d case - return new{dim,Tuple(order)}() +struct Bernstein{shape, order} <: Ferrite.ScalarInterpolation{shape, order} + function Bernstein{shape,order}() where {shape,order} + refdim = length(order) + @assert(Ferrite.RefHypercube{refdim} == shape) + @assert(order isa Tuple) + return new{shape,order}() end end #= This is a bit of a hack to get Ferrites Dofhandler to distribute dofs correctly: There are actually dofs on the faces/edges, but define all dofs on the verices instead =# -Ferrite.getnbasefunctions(::BernsteinBasis{dim,order}) where {dim,order} = prod(order .+ 1)::Int -Ferrite.nvertexdofs(::BernsteinBasis{dim,order}) where {dim,order} = 1 -Ferrite.nedgedofs(::BernsteinBasis{dim,order}) where {dim,order} = 0 -Ferrite.nfacedofs(::BernsteinBasis{dim,order}) where {dim,order} = 0 -Ferrite.ncelldofs(::BernsteinBasis{dim,order}) where {dim,order} = 0 - -#Fallback method for any order and dim of BernsteinBasis -function Ferrite.value(ip::BernsteinBasis{dim,order}, i::Int, xi::Vec{dim}) where {dim,order} - _berstein_value(ip, i, xi) -end - -function _berstein_value(ip::BernsteinBasis{dim,order}, i::Int, xi::Vec{dim}) where {dim,order} +Ferrite.getnbasefunctions(::Bernstein{shape,order}) where {shape,order} = prod(order .+ 1)::Int +Ferrite.nvertexdofs(::Bernstein{shape,order}) where {shape,order} = 1 +Ferrite.nedgedofs(::Bernstein{shape,order}) where {shape,order} = 0 +Ferrite.nfacedofs(::Bernstein{shape,order}) where {shape,order} = 0 +Ferrite.ncelldofs(::Bernstein{shape,order}) where {shape,order} = 0 + +#Fallback method for any order and dim of Bernstein +function Ferrite.shape_value(ip::Bernstein{shape,order}, i::Int, xi::Vec{dim}) where {shape,dim,order} _n = order .+ 1 #= Get the order of the bernstein basis (NOTE: not the same as VTK) @@ -48,7 +45,7 @@ function _berstein_value(ip::BernsteinBasis{dim,order}, i::Int, xi::Vec{dim}) wh return val end -function Ferrite.value(ip::BernsteinBasis{1,(2,)}, i::Int, _ξ::Vec{1}) +function Ferrite.shape_value(ip::Bernstein{RefLine,(2,)}, i::Int, _ξ::Vec{1}) ξ = 0.5*(_ξ[1] + 1.0) i == 1 && return (1-ξ)^2 i == 2 && return ξ^2 @@ -56,7 +53,7 @@ function Ferrite.value(ip::BernsteinBasis{1,(2,)}, i::Int, _ξ::Vec{1}) throw(ArgumentError("no shape function $i for interpolation $ip")) end -function Ferrite.value(ip::BernsteinBasis{2,(2,2)}, i::Int, _ξ::Vec{2}) +function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,(2,2)}, i::Int, _ξ::Vec{2}) ξ, η = _ξ i == 1 && return 0.0625((1 - η)^2)*((1 - ξ)^2) i == 2 && return 0.0625((1 + ξ)^2)*((1 - η)^2) @@ -70,7 +67,7 @@ function Ferrite.value(ip::BernsteinBasis{2,(2,2)}, i::Int, _ξ::Vec{2}) throw(ArgumentError("no shape function $i for interpolation $ip")) end -function Ferrite.value(ip::BernsteinBasis{3,(2,2,2)}, i::Int, _ξ::Vec{3}) +function Ferrite.value(ip::Bernstein{Bernstein,(2,2,2)}, i::Int, _ξ::Vec{3}) ξ, η, ζ = _ξ i == 1 && return 0.015625((1 - ζ)^2)*((1 - η)^2)*((1 - ξ)^2) i == 2 && return 0.015625((1 + ξ)^2)*((1 - ζ)^2)*((1 - η)^2) @@ -110,11 +107,11 @@ using Symbolics @variables ξ η ζ -ip1d = BernsteinBasis{1,(2,)}() +ip1d = Bernstein{1,(2,)}() dim = 3 order = (2,2,2)#,2) -ip = BernsteinBasis{dim,order}() +ip = Bernstein{dim,order}() ordering = IGA._bernstein_ordering(ip) cindex = CartesianIndices(order.+1) @@ -139,16 +136,16 @@ end =# -Ferrite.vertices(ip::BernsteinBasis{dim,orders}) where {dim,orders} = ntuple(i -> i, Ferrite.getnbasefunctions(ip)) +Ferrite.vertices(ip::Bernstein{dim,orders}) where {dim,orders} = ntuple(i -> i, Ferrite.getnbasefunctions(ip)) # 2D -function Ferrite.faces(ip::BernsteinBasis{2,orders}) where {orders} +function Ferrite.faces(ip::Bernstein{2,orders}) where {orders} faces = Tuple[] ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) #Order for 1d interpolation. - order_1d1 = _bernstein_ordering(BernsteinBasis{1,orders[1:1]}()) - order_1d2 = _bernstein_ordering(BernsteinBasis{1,orders[2:2]}()) + order_1d1 = _bernstein_ordering(Bernstein{1,orders[1:1]}()) + order_1d2 = _bernstein_ordering(Bernstein{1,orders[2:2]}()) push!(faces, Tuple(ind[:,1][order_1d1])) #bot push!(faces, Tuple(ind[end,:][order_1d2]))# right @@ -158,12 +155,12 @@ function Ferrite.faces(ip::BernsteinBasis{2,orders}) where {orders} return Tuple(faces) end -function Ferrite.faces(ip::BernsteinBasis{3,orders}) where {orders} +function Ferrite.faces(ip::Bernstein{3,orders}) where {orders} faces = Tuple[] ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) #Order for 2d interpolation. - order_2d = _bernstein_ordering(BernsteinBasis{2,orders[1:2]}()) + order_2d = _bernstein_ordering(Bernstein{2,orders[1:2]}()) push!(faces, Tuple(ind[:,:,1][order_2d])) # bottom push!(faces, Tuple(ind[:,1,:][order_2d])) # front @@ -175,12 +172,12 @@ function Ferrite.faces(ip::BernsteinBasis{3,orders}) where {orders} return Tuple(faces) end -function Ferrite.edges(ip::BernsteinBasis{3,orders}) where {orders} +function Ferrite.edges(ip::Bernstein{3,orders}) where {orders} edges = Tuple[] ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) #Order for 1d interpolation. - order_1d = _bernstein_ordering(BernsteinBasis{1,orders[1:1]}()) + order_1d = _bernstein_ordering(Bernstein{1,orders[1:1]}()) # bottom push!(edges, Tuple(ind[:,1,1][order_1d])) @@ -217,7 +214,7 @@ function _bernstein_basis_derivative_recursive(p::Int, i::Int, xi::T) where T return p * (_bernstein_basis_recursive(p - 1, i - 1, xi) - _bernstein_basis_recursive(p - 1, i, xi)) end -function Ferrite.reference_coordinates(ip::BernsteinBasis{dim_s,order}) where {dim_s,order} +function Ferrite.reference_coordinates(ip::Bernstein{dim_s,order}) where {dim_s,order} T = Float64 nbasefunks_dim = order .+ 1 @@ -245,12 +242,12 @@ function Ferrite.reference_coordinates(ip::BernsteinBasis{dim_s,order}) where {d end """ - _bernstein_ordering(::BernsteinBasis) + _bernstein_ordering(::Bernstein) Return the ordering of the bernstein basis base-functions, from a "CartesianIndices-ordering". The ordering is the same as in VTK: https://blog.kitware.com/wp-content/uploads/2020/03/Implementation-of-rational-Be%CC%81zier-cells-into-VTK-Report.pdf. """ -function _bernstein_ordering(::BernsteinBasis{1,orders}) where {orders} +function _bernstein_ordering(::Bernstein{1,orders}) where {orders} @assert(length(orders) == 1) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) @@ -266,7 +263,7 @@ function _bernstein_ordering(::BernsteinBasis{1,orders}) where {orders} return ordering end -function _bernstein_ordering(::BernsteinBasis{2,orders}) where {orders} +function _bernstein_ordering(::Bernstein{2,orders}) where {orders} @assert(length(orders) == 2) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) @@ -289,7 +286,7 @@ function _bernstein_ordering(::BernsteinBasis{2,orders}) where {orders} return ordering end -function _bernstein_ordering(::BernsteinBasis{3,orders}) where {orders} +function _bernstein_ordering(::Bernstein{3,orders}) where {orders} @assert(length(orders) == 3) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) @@ -339,8 +336,8 @@ function _bernstein_ordering(::BernsteinBasis{3,orders}) where {orders} end #Almost the same orderign as _bernstein_ordering, but some changes for faces and edges -_vtk_ordering(::Type{BezierCell{dim,N,orders,M}}) where {dim,N,orders,M} = _vtk_ordering(BernsteinBasis{dim,orders}()) -function _vtk_ordering(::BernsteinBasis{3,orders}) where {orders} +_vtk_ordering(::Type{BezierCell{dim,N,orders,M}}) where {dim,N,orders,M} = _vtk_ordering(Bernstein{dim,orders}()) +function _vtk_ordering(::Bernstein{3,orders}) where {orders} @assert(length(orders) == 3) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) From b692040abd3923463c29fd016863bb46ec01731f Mon Sep 17 00:00:00 2001 From: lijas Date: Mon, 11 Sep 2023 13:19:31 +0200 Subject: [PATCH 02/50] wip --- docs/old_examples/curved_ex.jl | 2 +- docs/old_examples/half_circle.jl | 2 +- docs/old_examples/half_circle2.jl | 2 +- docs/old_examples/heat_example.jl | 2 +- docs/old_examples/heat_example2.jl | 2 +- docs/src/bezier_extraction.md | 2 +- docs/src/examples/example1.jl | 2 +- docs/src/examples/example1.md | 2 +- docs/src/literate/plate_with_hole.jl | 2 +- src/IGA.jl | 42 ++++--- src/bezier_extraction.jl | 2 +- src/bezier_grid.jl | 34 +++--- src/splines/bezier.jl | 161 ++++++++++++++------------- test/test_bertstein.jl | 22 ++-- test/test_bezier_extraction.jl | 4 +- test/test_bsplines.jl | 4 +- test/test_meshes.jl | 2 +- test/test_values.jl | 4 +- 18 files changed, 149 insertions(+), 144 deletions(-) diff --git a/docs/old_examples/curved_ex.jl b/docs/old_examples/curved_ex.jl index ccdfdf6..f951101 100644 --- a/docs/old_examples/curved_ex.jl +++ b/docs/old_examples/curved_ex.jl @@ -231,7 +231,7 @@ function goiga() addfaceset!(grid, "left", (x)->x[1] ≈ -4.0) addfaceset!(grid, "bottom", (x)->x[2] ≈ 0.0) - ip = IGA.BernsteinBasis{dim,(order, order)}() + ip = IGA.Bernstein{dim,(order, order)}() qr = QuadratureRule{dim,RefCube}(4) cellvalues = IGA.BezierValues(CellVectorValues(qr, ip)) diff --git a/docs/old_examples/half_circle.jl b/docs/old_examples/half_circle.jl index 72f4484..d0c74b1 100644 --- a/docs/old_examples/half_circle.jl +++ b/docs/old_examples/half_circle.jl @@ -92,7 +92,7 @@ function solve_2d() Cb, nbe = IGA.compute_bezier_extraction_operators(mesh.orders, mesh.knot_vectors) - ip = BernsteinBasis{2,2}() + ip = Bernstein{2,2}() qr = QuadratureRule{2,RefCube}(3) #ecp = mesh.control_points[globaldofs] diff --git a/docs/old_examples/half_circle2.jl b/docs/old_examples/half_circle2.jl index 0db09a6..efba0c8 100644 --- a/docs/old_examples/half_circle2.jl +++ b/docs/old_examples/half_circle2.jl @@ -92,7 +92,7 @@ function solve_2d() Cb, nbe = IGA.compute_bezier_extraction_operators(mesh.orders, mesh.knot_vectors) - ip = BernsteinBasis{2,2}() + ip = Bernstein{2,2}() qr = QuadratureRule{2,RefCube}(3) #ecp = mesh.control_points[globaldofs] diff --git a/docs/old_examples/heat_example.jl b/docs/old_examples/heat_example.jl index a9fe023..0ce6498 100644 --- a/docs/old_examples/heat_example.jl +++ b/docs/old_examples/heat_example.jl @@ -63,7 +63,7 @@ function goiga(nelx,nely, order, multiplicity) addfaceset!(grid, "bottom", (x)->x[2]<0.001) addfaceset!(grid, "top", (x)->x[2]>Ly*0.9999) - ip = IGA.BernsteinBasis{dim, (order,order)}() + ip = IGA.Bernstein{dim, (order,order)}() qr = QuadratureRule{dim, RefCube}(100) cellvalues = IGA.BezierValues(CellScalarValues(qr, ip)) diff --git a/docs/old_examples/heat_example2.jl b/docs/old_examples/heat_example2.jl index 45d57f1..213d7bf 100644 --- a/docs/old_examples/heat_example2.jl +++ b/docs/old_examples/heat_example2.jl @@ -63,7 +63,7 @@ function goiga(nelx, nely, order, multiplicity) addfaceset!(grid, "bottom", (x)->x[2]<0.001) addfaceset!(grid, "top", (x)->x[2]>Ly*0.9999) - ip = IGA.BernsteinBasis{dim, (order,order)}() + ip = IGA.Bernstein{dim, (order,order)}() qr = QuadratureRule{dim, RefCube}(100) cellvalues = IGA.BezierCellValues(CellScalarValues(qr, ip)) diff --git a/docs/src/bezier_extraction.md b/docs/src/bezier_extraction.md index ea66f57..a8490ad 100644 --- a/docs/src/bezier_extraction.md +++ b/docs/src/bezier_extraction.md @@ -35,7 +35,7 @@ where $\boldsymbol X^e$ are the control points for the NURBS surface, and $\bold The equations above show that we can pre-compute the bernstein basis values at some gauss points (similar to how we pre compute the shape values for Lagrange basis functions), and then using the bezier extraction operator, calculate the NURBS values. In the code, the reinitilzation of the shape functions is performed like this. ```@example -ip = BernsteinBasis{2,orders}() +ip = Bernstein{2,orders}() qr_cell = QuadratureRule{2,RefCube}(4) cv = BezierCellValues( CellVectorValues(qr_cell, ip) ) diff --git a/docs/src/examples/example1.jl b/docs/src/examples/example1.jl index ccf8242..63e7aa1 100644 --- a/docs/src/examples/example1.jl +++ b/docs/src/examples/example1.jl @@ -133,7 +133,7 @@ function solve() addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0) addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0) - ip = BernsteinBasis{2,orders}() + ip = Bernstein{2,orders}() qr_cell = QuadratureRule{2,RefCube}(3) qr_face = QuadratureRule{1,RefCube}(3) diff --git a/docs/src/examples/example1.md b/docs/src/examples/example1.md index f224803..1372fe9 100644 --- a/docs/src/examples/example1.md +++ b/docs/src/examples/example1.md @@ -212,7 +212,7 @@ Create the cellvalues storing the shape function values. Note that the `CellVect reinit-function of the `BezierValues` that the actual bezier transformation of the shape values is performed. ```@example example1 - ip = BernsteinBasis{2,orders}() + ip = Bernstein{2,orders}() qr_cell = QuadratureRule{2,RefCube}(3) qr_face = QuadratureRule{1,RefCube}(3) diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl index e63038c..8315105 100644 --- a/docs/src/literate/plate_with_hole.jl +++ b/docs/src/literate/plate_with_hole.jl @@ -167,7 +167,7 @@ function solve() # Create the cellvalues storing the shape function values. Note that the `CellVectorValues`/`FaceVectorValues` are wrapped in a `BezierValues`. It is in the # reinit-function of the `BezierValues` that the actual bezier transformation of the shape values is performed. - ip = BernsteinBasis{2,orders}() + ip = Bernstein{2,orders}() qr_cell = QuadratureRule{2,RefCube}(4) qr_face = QuadratureRule{1,RefCube}(3) diff --git a/src/IGA.jl b/src/IGA.jl index d820fd9..a495373 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -4,8 +4,9 @@ using Reexport @reexport using Tensors @reexport using Ferrite -@reexport using WriteVTK +using Ferrite: AbstractRefShape, RefHypercube, RefLine, RefQuadrilateral, RefHexahedron, getnbasefunctions +using WriteVTK using LinearAlgebra using StaticArrays import SparseArrays @@ -35,52 +36,49 @@ end """ struct BezierCell{order,refshape,N} <: Ferrite.AbstractCell{refshape} nodes::NTuple{N,Int} - function BezierCell{order}(nodes::NTuple{N,Int}) where {order,N} - @assert(order isa Tuple) - @assert(prod(order.+1)==N) - refdim = length(order) - refshape = Ferrite.RefHypercube{refdim} - - return new{order,refshape,N}(nodes) + function BezierCell{orders,shape}(nodes::NTuple{N,Int}) where {rdim, orders, shape<:RefHypercube{rdim}, N} + #@assert(order isa Integer) + @assert prod(orders.+1) == N + @assert length(orders) == rdim + return new{orders,shape,N}(nodes) end end -getorders(::BezierCell{orders,refshape,N}) where {orders,refshape,N} = orders -include("bezier_extraction.jl") +Ferrite.default_interpolation(::Type{<:BezierCell{order, shape}}) where {order, shape} = Bernstein{shape, order}() +#getorders(::BezierCell{orders,refshape,N}) where {orders,refshape,N} = orders + include("nurbsmesh.jl") include("mesh_generation.jl") include("bezier_grid.jl") include("splines/bezier.jl") -include("splines/bezier_values.jl") -include("splines/bsplines.jl") -include("VTK.jl") -include("L2_projection.jl") +include("bezier_extraction.jl") +#include("splines/bezier_values.jl") +#include("splines/bsplines.jl") +#include("VTK.jl") +#include("L2_projection.jl") -Ferrite._mass_qr(::BernsteinBasis{2, (2, 2)}) = QuadratureRule{RefQuadrilatiral}(2+1) +Ferrite._mass_qr(::Bernstein{2, (2, 2)}) = QuadratureRule{RefQuadrilateral}(2+1) #Normaly the verices function should only return the 8 corner nodes of the hexa (or 4 in 2d), #but since the cell connectivity in IGA is different compared to normal FE elements, #we can only distribute cells on the nodes/controlpoints Ferrite.vertices(c::BezierCell) = c.nodes -_bernstein_ordering(::Type{<:BezierCell{order}}) where {order} = _bernstein_ordering(BernsteinBasis{length(order),order}()) +_bernstein_ordering(::Type{<:BezierCell{order,shape}}) where {order,shape} = _bernstein_ordering(Bernstein{shape,order}()) function Ferrite.faces(c::BezierCell{dim,N,order}) where {dim,N,order} - return getindex.(Ref(c.nodes), collect.(Ferrite.faces(BernsteinBasis{length(order),order}() ))) + return getindex.(Ref(c.nodes), collect.(Ferrite.faces(Bernstein{length(order),order}() ))) end function Ferrite.edges(c::BezierCell{dim,N,order}) where {dim,N,order} - return getindex.(Ref(c.nodes), collect.(Ferrite.edges(BernsteinBasis{length(order),order}() ))) + return getindex.(Ref(c.nodes), collect.(Ferrite.edges(Bernstein{length(order),order}() ))) end -Ferrite.default_interpolation(::Type{BezierCell{order}}) where {order} = BernsteinBasis{length(order),order}() - - function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefHexahedron}}) where {order} return Ferrite.VTKCellTypes.VTK_BEZIER_HEXAHEDRON end -function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefQuadrilatiral}}) where {order} +function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefQuadrilateral}}) where {order} return Ferrite.VTKCellTypes.VTK_BEZIER_QUADRILATERAL end function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefLine}}) where {order} diff --git a/src/bezier_extraction.jl b/src/bezier_extraction.jl index 02d328a..2cf18bb 100644 --- a/src/bezier_extraction.jl +++ b/src/bezier_extraction.jl @@ -113,7 +113,7 @@ Computes the bezier extraction operator in each parametric direction, and uses t #Reorder # - ordering = _bernstein_ordering(BernsteinBasis{pdim,orders}()) + ordering = _bernstein_ordering(Bernstein{RefHypercube{pdim}, orders}()) nel = prod(nels) C_reorder = Vector{eltype(first(Ce))}() for i in 1:nel diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 6186686..5245924 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -18,16 +18,15 @@ function BezierGrid(cells::Vector{C}, boundary_matrix::SparseArrays.SparseMatrixCSC{Bool,Int} = SparseArrays.spzeros(Bool, 0, 0)) where {dim,C,T} - grid = Ferrite.Grid(cells, nodes; cellsets, nodesets, facesets, cellsets, vertexsets, boundary_matrix) + grid = Ferrite.Grid(cells, nodes; nodesets, cellsets, facesets, vertexsets, boundary_matrix) return BezierGrid{dim,C,T}(grid, weights, extraction_operator) end function BezierGrid(mesh::NURBSMesh{pdim,sdim}) where {pdim,sdim} - N = length(mesh.IEN[:,1]) - - CellType = BezierCell{sdim,N,mesh.orders} + N = size(mesh.IEN, 1) + CellType = BezierCell{mesh.orders, RefHypercube{pdim}} ordering = _bernstein_ordering(CellType) cells = [CellType(Tuple(mesh.IEN[ordering,ie])) for ie in 1:getncells(mesh)] @@ -75,6 +74,17 @@ function Base.getproperty(m::BezierGrid, s::Symbol) end end +function Base.show(io::IO, ::MIME"text/plain", grid::BezierGrid) + print(io, "$(typeof(grid)) with $(getncells(grid)) ") + if isconcretetype(eltype(grid.cells)) + typestrs = [repr(eltype(grid.cells))] + else + typestrs = sort!(repr.(Set(typeof(x) for x in grid.cells))) + end + join(io, typestrs, '/') + print(io, " cells and $(getnnodes(grid)) nodes/constrol points") +end + """ getweights!(w::Vector{T}, grid::BezierGrid, cellid::Int) where {T} @@ -169,20 +179,14 @@ function get_extraction_operator(grid::BezierGrid, cellid::Int) return grid.beo[cellid] end -#Ferrite_to_vtk_order(::Type{<:Ferrite.AbstractCell{dim,N,M}}) where {dim,N,M} = 1:N - # Store the Ferrite to vtk order in a cache for specific cell type let cache = Dict{Type{<:BezierCell}, Vector{Int}}() - global function Ferrite_to_vtk_order(celltype::Type{BezierCell{3,N,order,M}}) where {N,order,M} + global function Ferrite.nodes_to_vtkorder(celltype::Type{<:BezierCell}) get!(cache, celltype) do - if length(order) == 3 - igaorder = _bernstein_ordering(celltype) - vtkorder = _vtk_ordering(celltype) - - return [findfirst(ivtk-> ivtk == iiga, vtkorder) for iiga in igaorder] - else - return 1:N - end + igaorder = _bernstein_ordering(celltype) + vtkorder = _vtk_ordering(celltype) + + return [findfirst(ivtk-> ivtk == iiga, vtkorder) for iiga in igaorder] end end end \ No newline at end of file diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index 82f8997..1d80d39 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -10,50 +10,50 @@ IGA, together with bezier extraction + BezierValues. `order` - A tuple with the order in each parametric direction. """ struct Bernstein{shape, order} <: Ferrite.ScalarInterpolation{shape, order} - function Bernstein{shape,order}() where {shape,order} - refdim = length(order) - @assert(Ferrite.RefHypercube{refdim} == shape) - @assert(order isa Tuple) - return new{shape,order}() + function Bernstein{shape,order}() where {rdim, shape<:RefHypercube{rdim}, order} + orders = order + if order isa Integer + orders = ntuple(i->order, rdim) + end + @assert length(orders) == rdim + return new{shape,orders}() end end -#= This is a bit of a hack to get Ferrites Dofhandler to distribute dofs correctly: -There are actually dofs on the faces/edges, but define all dofs on the verices instead =# -Ferrite.getnbasefunctions(::Bernstein{shape,order}) where {shape,order} = prod(order .+ 1)::Int -Ferrite.nvertexdofs(::Bernstein{shape,order}) where {shape,order} = 1 -Ferrite.nedgedofs(::Bernstein{shape,order}) where {shape,order} = 0 -Ferrite.nfacedofs(::Bernstein{shape,order}) where {shape,order} = 0 -Ferrite.ncelldofs(::Bernstein{shape,order}) where {shape,order} = 0 - -#Fallback method for any order and dim of Bernstein -function Ferrite.shape_value(ip::Bernstein{shape,order}, i::Int, xi::Vec{dim}) where {shape,dim,order} - _n = order .+ 1 - #= - Get the order of the bernstein basis (NOTE: not the same as VTK) - The order gets recalculated each time the function is called, so - one should not calculate the values in performance critical parts, but rather - cache the basis values someway (for example in BezierValues). - =# - ordering = _bernstein_ordering(ip) - coord = Tuple(CartesianIndices(_n)[ordering[i]]) +Ferrite.getnbasefunctions(ip::Bernstein{shape,order}) where {shape <: RefHypercube, order} = prod(order) - val = 1.0 - for i in 1:dim - val *= IGA._bernstein_basis_recursive(order[i], coord[i], xi[i]) - end - return val -end +Ferrite.vertexdof_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = + Ferrite.vertexdof_indices(Lagrange{shape,order}()) + +Ferrite.edgedof_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = + Ferrite.edgedof_indices(Lagrange{shape,order}()) -function Ferrite.shape_value(ip::Bernstein{RefLine,(2,)}, i::Int, _ξ::Vec{1}) +Ferrite.edgedof_interior_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = + Ferrite.edgedof_interior_indices(Lagrange{shape,order}()) + +Ferrite.facedof_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = + Ferrite.facedof_indices(Lagrange{shape,order}()) + +Ferrite.facedof_interior_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = + Ferrite.facedof_interior_indices(Lagrange{shape,order}()) + +# # # +# Bernstein line, order 2 +# # # + +function Ferrite.shape_value(ip::Bernstein{RefLine,(2,)}, _ξ::Vec{1}, i::Int) ξ = 0.5*(_ξ[1] + 1.0) i == 1 && return (1-ξ)^2 i == 2 && return ξ^2 - i == 3 && return 2ξ*(1 - ξ) + i == 3 && return 2ξ*(1 - ξ) throw(ArgumentError("no shape function $i for interpolation $ip")) end -function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,(2,2)}, i::Int, _ξ::Vec{2}) +# # # +# Bernstein Quadrilateral, order 2 +# # # + +function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,(2,2)}, _ξ::Vec{2}, i::Int) ξ, η = _ξ i == 1 && return 0.0625((1 - η)^2)*((1 - ξ)^2) i == 2 && return 0.0625((1 + ξ)^2)*((1 - η)^2) @@ -67,7 +67,11 @@ function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,(2,2)}, i::Int, _ξ: throw(ArgumentError("no shape function $i for interpolation $ip")) end -function Ferrite.value(ip::Bernstein{Bernstein,(2,2,2)}, i::Int, _ξ::Vec{3}) +# # # +# Bernstein Hexahedron, order 2 +# # # + +function Ferrite.shape_value(ip::Bernstein{RefHexahedron,(2,2,2)}, _ξ::Vec{3}, i::Int) ξ, η, ζ = _ξ i == 1 && return 0.015625((1 - ζ)^2)*((1 - η)^2)*((1 - ξ)^2) i == 2 && return 0.015625((1 + ξ)^2)*((1 - ζ)^2)*((1 - η)^2) @@ -136,16 +140,46 @@ end =# -Ferrite.vertices(ip::Bernstein{dim,orders}) where {dim,orders} = ntuple(i -> i, Ferrite.getnbasefunctions(ip)) +function Ferrite.shape_value(ip::Bernstein{shape,orders}, i::Int, ξ::Vec{dim,T}) where {shape,dim,orders,T} + _n = orders .+ 1 + ordering = _bernstein_ordering(ip) + basefunction_indeces = CartesianIndices(_n)[ordering[i]] + + val = one(T) + for i in 1:dim + val *= IGA._bernstein_basis_recursive(orders[i], basefunction_indeces[i], ξ[i]) + end + return val +end + +function Ferrite.reference_coordinates(ip::Bernstein{shape,order}) where {rdim, shape <: AbstractRefShape{rdim}, order <: Integer} + + T = Float64 + nbasefunks_dim = ntuple(i->order, rdim) + nbasefuncs = prod(nbasefunks_dim) + + coords = Vec{rdim,T}[] -# 2D -function Ferrite.faces(ip::Bernstein{2,orders}) where {orders} + ordering = _bernstein_ordering(ip) + ranges = [range(T(-1.0), stop=T(1.0), length=nbasefunks_dim[i]) for i in 1:rdim] + + inds = CartesianIndices(nbasefunks_dim) + for i in 1:nbasefuncs + ind = inds[ordering[i]] + x = Vec{rdim,T}(d -> ranges[d][ind[d]]) + push!(coords, x) + end + + return coords +end + +function Ferrite.facedof_indices(ip::Bernstein{RefQuadrilateral,orders}) where {orders} faces = Tuple[] ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) #Order for 1d interpolation. - order_1d1 = _bernstein_ordering(Bernstein{1,orders[1:1]}()) - order_1d2 = _bernstein_ordering(Bernstein{1,orders[2:2]}()) + order_1d1 = _bernstein_ordering(Bernstein{RefLine,orders[1:1]}()) + order_1d2 = _bernstein_ordering(Bernstein{RefLine,orders[2:2]}()) push!(faces, Tuple(ind[:,1][order_1d1])) #bot push!(faces, Tuple(ind[end,:][order_1d2]))# right @@ -155,7 +189,7 @@ function Ferrite.faces(ip::Bernstein{2,orders}) where {orders} return Tuple(faces) end -function Ferrite.faces(ip::Bernstein{3,orders}) where {orders} +function Ferrite.facedof_indices(ip::Bernstein{3,orders}) where {orders} faces = Tuple[] ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) @@ -202,9 +236,9 @@ end function _bernstein_basis_recursive(p::Int, i::Int, xi::T) where T if i == 1 && p == 0 - return 1 + return one(T) elseif i < 1 || i > p + 1 - return 0 + return zero(T) else return 0.5 * (1 - xi) * _bernstein_basis_recursive(p - 1, i, xi) + 0.5 * (1 + xi) * _bernstein_basis_recursive(p - 1, i - 1, xi) end @@ -214,32 +248,6 @@ function _bernstein_basis_derivative_recursive(p::Int, i::Int, xi::T) where T return p * (_bernstein_basis_recursive(p - 1, i - 1, xi) - _bernstein_basis_recursive(p - 1, i, xi)) end -function Ferrite.reference_coordinates(ip::Bernstein{dim_s,order}) where {dim_s,order} - - T = Float64 - nbasefunks_dim = order .+ 1 - nbasefuncs = prod(nbasefunks_dim) - - coords = Vec{dim_s,T}[] - - ordering = _bernstein_ordering(ip) - ranges = [range(-1.0, stop=1.0, length=nbasefunks_dim[i]) for i in 1:dim_s] - - inds = CartesianIndices(nbasefunks_dim) - for i in 1:nbasefuncs - ind = inds[ordering[i]] - - _vec = T[] - for d in 1:dim_s - j = ranges[d][ind[d]] - push!(_vec, j) - end - - push!(coords, Vec(Tuple(_vec))) - end - - return coords -end """ _bernstein_ordering(::Bernstein) @@ -247,9 +255,7 @@ end Return the ordering of the bernstein basis base-functions, from a "CartesianIndices-ordering". The ordering is the same as in VTK: https://blog.kitware.com/wp-content/uploads/2020/03/Implementation-of-rational-Be%CC%81zier-cells-into-VTK-Report.pdf. """ -function _bernstein_ordering(::Bernstein{1,orders}) where {orders} - @assert(length(orders) == 1) - +function _bernstein_ordering(::Bernstein{RefLine, orders}) where {orders} ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) ordering = Int[] @@ -257,15 +263,13 @@ function _bernstein_ordering(::Bernstein{1,orders}) where {orders} push!(ordering, ind[1]) push!(ordering, ind[end]) - # Volume + # Interior append!(ordering, ind[2:end-1]) return ordering end -function _bernstein_ordering(::Bernstein{2,orders}) where {orders} - @assert(length(orders) == 2) - +function _bernstein_ordering(::Bernstein{RefQuadrilateral,orders}) where {orders} ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) ordering = Int[] @@ -283,11 +287,11 @@ function _bernstein_ordering(::Bernstein{2,orders}) where {orders} # inner dofs append!(ordering, ind[2:end-1, 2:end-1]) + return ordering end -function _bernstein_ordering(::Bernstein{3,orders}) where {orders} - @assert(length(orders) == 3) +function _bernstein_ordering(::Bernstein{RefHexahedron, orders}) where {orders} ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) ordering = Int[] @@ -336,8 +340,7 @@ function _bernstein_ordering(::Bernstein{3,orders}) where {orders} end #Almost the same orderign as _bernstein_ordering, but some changes for faces and edges -_vtk_ordering(::Type{BezierCell{dim,N,orders,M}}) where {dim,N,orders,M} = _vtk_ordering(Bernstein{dim,orders}()) -function _vtk_ordering(::Bernstein{3,orders}) where {orders} +function _vtk_ordering(::BezierCell{orders}) where {orders} @assert(length(orders) == 3) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) diff --git a/test/test_bertstein.jl b/test/test_bertstein.jl index 0a4b44d..daccac1 100644 --- a/test/test_bertstein.jl +++ b/test/test_bertstein.jl @@ -2,19 +2,19 @@ @testset "bernstein" begin #1d basis function should equal to 2d basis function on the boundary (-1.0) - b1 = BernsteinBasis{2,(2,2)}() - b2 = BernsteinBasis{1,2}() + b1 = Bernstein{2,(2,2)}() + b2 = Bernstein{1,2}() @test Ferrite.value(b1, 5, Vec((0.0,-1.0))) == Ferrite.value(b2, 3, Vec((0.0))) #Sum of shape function should equal 1 - for bip in (BernsteinBasis{1,(1,)}(), - BernsteinBasis{1,(2,)}(), - BernsteinBasis{1,(3,)}(), - BernsteinBasis{1,(4,)}(), - BernsteinBasis{2,(2,2,)}(), - BernsteinBasis{2,(3,3,)}(), - BernsteinBasis{3,(2,2,2)}(), - BernsteinBasis{3,(3,3,3)}() + for bip in (Bernstein{1,(1,)}(), + Bernstein{1,(2,)}(), + Bernstein{1,(3,)}(), + Bernstein{1,(4,)}(), + Bernstein{2,(2,2,)}(), + Bernstein{2,(3,3,)}(), + Bernstein{3,(2,2,2)}(), + Bernstein{3,(3,3,3)}() ) dim = Ferrite.getdim(bip) @@ -29,7 +29,7 @@ #Test generated Bernstein values with hardcoded ones # ip_list contains interpolation which explicitly been inputed in IGA.jl - ip_list = ( BernsteinBasis{1,(2,)}(), BernsteinBasis{2,(2,2,)}(), BernsteinBasis{3,(2,2,2)}()) + ip_list = ( Bernstein{1,(2,)}(), Bernstein{2,(2,2,)}(), Bernstein{3,(2,2,2)}()) for bip in ip_list dim = Ferrite.getdim(bip) for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})] diff --git a/test/test_bezier_extraction.jl b/test/test_bezier_extraction.jl index ec7ef4f..06db000 100644 --- a/test/test_bezier_extraction.jl +++ b/test/test_bezier_extraction.jl @@ -8,7 +8,7 @@ #Test univariate bspline extraction @test nbe == 4 @test length(C) == 4 - ordering = IGA._bernstein_ordering(BernsteinBasis{1,order}()) + ordering = IGA._bernstein_ordering(Bernstein{1,order}()) @test all(C[1] .≈ Float64[1 0 0 0; 0 1 0.5 1/4; 0 0 1/2 7/12; 0 0 0 1/6][ordering,ordering]) @test all(C[2] .≈ Float64[1/4 0 0 0; 7/12 2/3 1/3 1/6; 1/6 1/3 2/3 2/3; 0 0 0 1/6][ordering,ordering]) @test all(C[3] .≈ Float64[1/6 0 0 0; 2/3 2/3 1/3 1/6; 1/6 1/3 2/3 7/12; 0 0 0 1/4][ordering,ordering]) @@ -18,7 +18,7 @@ orders = (2,2) knots = (Float64[0,0,0,1/3,2/3,1,1,1], Float64[0,0,0,1/3,2/3,1,1,1]) C,nbe = IGA.compute_bezier_extraction_operators(orders, knots) - ordering = IGA._bernstein_ordering(BernsteinBasis{2,orders}()) + ordering = IGA._bernstein_ordering(Bernstein{2,orders}()) @test nbe == 9 @test length(C) == 9 @test all(C[1] .≈ kron( [1 0 0; 0 1 1/2; 0 0 1/2], [1 0 0; 0 1 1/2; 0 0 1/2])[ordering,ordering]) diff --git a/test/test_bsplines.jl b/test/test_bsplines.jl index f1c2f3d..b5f5be9 100644 --- a/test/test_bsplines.jl +++ b/test/test_bsplines.jl @@ -54,13 +54,13 @@ end=# end @testset "bsplines vs. bernstein" begin - #BsplineBasis is the same as BernsteinBasis in interval -1 to 1 + #BsplineBasis is the same as Bernstein in interval -1 to 1 T = Float64 for p in (2,4) knot_vector = [(ones(T, p+1)*-1)..., ones(T, p+1)...] ip1 = BSplineBasis((knot_vector,knot_vector), (p,p)) - ip2 = BernsteinBasis{2,(p,p)}() + ip2 = Bernstein{2,(p,p)}() reorder = IGA._bernstein_ordering(ip2) diff --git a/test/test_meshes.jl b/test/test_meshes.jl index 1e43cc8..c12d52c 100644 --- a/test/test_meshes.jl +++ b/test/test_meshes.jl @@ -30,7 +30,7 @@ function _get_problem_data(meshsymbol::Symbol, nels::NTuple{sdim,Int}, orders; m mesh = generate_nurbs_patch(meshsymbol, nels, orders; meshkwargs...) grid = BezierGrid(mesh) - bern_ip = BernsteinBasis{sdim, mesh.orders}() + bern_ip = Bernstein{sdim, mesh.orders}() #Cell values qr = Ferrite.QuadratureRule{sdim,Ferrite.RefCube}(5) diff --git a/test/test_values.jl b/test/test_values.jl index 2cdc313..f110dcc 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -51,7 +51,7 @@ end grid = BezierGrid(nurbsmesh) - ip = BernsteinBasis{dim, orders}() + ip = Bernstein{dim, orders}() reorder = IGA._bernstein_ordering(ip) @@ -188,7 +188,7 @@ end nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) grid = BezierGrid(nurbsmesh) - ip = BernsteinBasis{dim, orders}() + ip = Bernstein{dim, orders}() qr = QuadratureRule{dim, RefCube}(3) cv = BezierCellValues( CellScalarValues(qr, ip) ) From 930495b3267b9f1aa380715cdc9db51b2f4f36b8 Mon Sep 17 00:00:00 2001 From: lijas Date: Wed, 13 Sep 2023 11:50:02 +0200 Subject: [PATCH 03/50] some work on values.jl --- src/splines/bezier_values.jl | 260 +++++++++++++++-------------------- 1 file changed, 114 insertions(+), 146 deletions(-) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index e99304b..c10e5ed 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -4,91 +4,110 @@ export BezierCellValues, BezierFaceValues, set_bezier_operator! Wraps a standard `Ferrite.CellValues`, but multiplies the shape values with the bezier extraction operator each time the `reinit!` function is called. """ -struct BezierCellValues{dim_s,T<:Real,CV<:Ferrite.CellValues} <: Ferrite.CellValues{dim_s,T,Ferrite.RefCube} - cv_bezier::CV - cv_store::CV +struct BezierCellValues{T<:Real,CV<:Ferrite.CellValues} <: Ferrite.AbstractCellValues + # cv_bezier stores the bernstein basis. These are the same for all elements, and does not change + cv_bezier::CV + # cv_nurbs sotres the nurbs/b-spline basis. These will change for each element + cv_nurbs::CV + # cv_tmp is just and intermidiate state need when converting from cv_bezier to cv_nurbs + cv_tmp::CV current_beo::Base.RefValue{BezierExtractionOperator{T}} current_w::Vector{T} end -struct BezierFaceValues{dim_s,T<:Real,CV<:Ferrite.FaceValues} <: Ferrite.FaceValues{dim_s,T,Ferrite.RefCube} +struct BezierFaceValues{T<:Real,CV<:Ferrite.FaceValues} <: Ferrite.AbstractFaceValues cv_bezier::CV - cv_store::CV + cv_tmp::CV + cv_nurbs::CV current_beo::Base.RefValue{BezierExtractionOperator{T}} current_w::Vector{T} end -BezierValues{dim_s,T,CV} = Union{BezierCellValues{dim_s,T,CV}, BezierFaceValues{dim_s,T,CV}} +BezierCellAndFaceValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFaceValues{T,CV}} -Ferrite.nfaces(fv::BezierFaceValues) = size(fv.cv_store.N, 3) +Ferrite.nfaces(fv::BezierFaceValues) = size(fv.cv_nurbs.N, 3) -function BezierCellValues(cv::Ferrite.CellValues{dim_s,T,Ferrite.RefCube}) where {dim_s,T} +function BezierCellValues(cv::Ferrite.CellValues) + T = eltype(cv.M) undef_beo = Ref(Vector{SparseArrays.SparseVector{T,Int}}(undef,0)) undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(cv)) - return BezierCellValues{dim_s,T,typeof(cv)}(cv, deepcopy(cv), undef_beo, undef_w) + return BezierCellValues(cv, deepcopy(cv), deepcopy(cv), undef_beo, undef_w) end -function BezierFaceValues(cv::Ferrite.FaceValues{dim_s,T,Ferrite.RefCube}) where {dim_s,T} +function BezierFaceValues(cv::Ferrite.FaceValues) + T = eltype(cv.M) undef_beo = Ref(Vector{SparseArrays.SparseVector{T,Int}}(undef,0)) undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(cv)) - return BezierFaceValues{dim_s,T,typeof(cv)}(cv, deepcopy(cv), undef_beo, undef_w) + return BezierFaceValues(cv, deepcopy(cv), deepcopy(cv), undef_beo, undef_w) end -function BezierCellValues{dim_s,T,CV}(qr::QuadratureRule, ip::Interpolation, ip_geo::Interpolation=ip) where {dim_s,T<:Real,CV<:CellValues} - return BezierCellValues(CV(qr,ip,ip_geo)) +function BezierCellValues(qr::QuadratureRule, ip::Interpolation, + gip::Interpolation = Ferrite.default_geometric_interpolation(ip)) + return BezierCellValues(Float64, qr, ip, gip) end -Ferrite.getnbasefunctions(bcv::BezierValues) = size(bcv.cv_bezier.N, 1) -Ferrite.getngeobasefunctions(bcv::BezierValues) = size(bcv.cv_bezier.M, 1) -Ferrite.getnquadpoints(bcv::BezierValues) = Ferrite.getnquadpoints(bcv.cv_bezier) -Ferrite.getdetJdV(bv::BezierValues, q_point::Int) = Ferrite.getdetJdV(bv.cv_bezier, q_point) -Ferrite.shape_value(bcv::BezierValues, qp::Int, i::Int) = Ferrite.shape_value(bcv.cv_store,qp,i) -Ferrite.getn_scalarbasefunctions(bcv::BezierValues) = Ferrite.getn_scalarbasefunctions(bcv.cv_store) -Ferrite._gradienttype(::BezierValues{dim}, ::AbstractVector{T}) where {dim,T} = Tensor{2,dim,T} - -function Ferrite.function_gradient(fe_v::BezierValues{dim}, q_point::Int, u::AbstractVector{T}) where {dim,T} - return Ferrite.function_gradient(fe_v.cv_store, q_point, u) -end -function Ferrite.function_value(fe_v::BezierValues{dim}, q_point::Int, u::AbstractVector{T}, dof_range::AbstractVector{Int} = collect(1:length(u))) where {dim,T} - return Ferrite.function_value(fe_v.cv_store, q_point, u, dof_range) +function BezierCellValues(::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} + cv = CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) + + return BezierCellValues(cv) end function Ferrite.function_symmetric_gradient(bv::IGA.BezierValues, q_point::Int, u::AbstractVector, args...) return function_symmetric_gradient(bv.cv_store, q_point, u, args...) end -Ferrite.geometric_value(cv::BezierValues{dim}, q_point::Int, i::Int) where {dim} = Ferrite.geometric_value(cv.cv_bezier, q_point, i); - -Ferrite.shape_gradient(bcv::BezierValues, q_point::Int, base_func::Int) = Ferrite.shape_gradient(bcv.cv_store, q_point, base_func)#bcv.cv_store.dNdx[base_func, q_point] -function Ferrite.shape_symmetric_gradient(bv::IGA.BezierValues, q_point::Int, i::Int) - return shape_symmetric_gradient(bv.cv_store, q_point, i) +Ferrite.getnbasefunctions(bcv::BezierCellAndFaceValues) = size(bcv.cv_bezier.N, 1) +Ferrite.getngeobasefunctions(bcv::BezierCellAndFaceValues) = size(bcv.cv_bezier.M, 1) +Ferrite.getnquadpoints(bcv::BezierCellAndFaceValues) = Ferrite.getnquadpoints(bcv.cv_bezier) +Ferrite.getdetJdV(bv::BezierCellAndFaceValues, q_point::Int) = Ferrite.getdetJdV(bv.cv_bezier, q_point) +Ferrite.shape_value(bcv::BezierCellAndFaceValues, qp::Int, i::Int) = Ferrite.shape_value(bcv.cv_nurbs,qp,i) +Ferrite.geometric_value(cv::BezierCellAndFaceValues, q_point::Int, i::Int) = Ferrite.geometric_value(cv.cv_bezier, q_point, i) +Ferrite.shape_gradient(bcv::BezierCellAndFaceValues, q_point::Int, i::Int) = Ferrite.shape_gradient(bcv.cv_nurbs, q_point, i) +Ferrite.geometric_value(cv::BezierCellValues, q_point::Int, base_func::Int) = cv.cv_bezier.M[base_func, q_point] +Ferrite.geometric_value(cv::BezierFaceValues, q_point::Int, base_func::Int) = cv.cv_bezier.M[base_func, q_point, cv.cv_bezier.current_face[]] + +function Ferrite.function_symmetric_gradient(bv::BezierCellAndFaceValues, q_point::Int, u::AbstractVector) + return function_symmetric_gradient(bv.cv_nurbs, q_point, u) +end +function Ferrite.shape_symmetric_gradient(bv::BezierCellAndFaceValues, q_point::Int, i::Int) + return shape_symmetric_gradient(bv.cv_nurbs, q_point, i) +end +function Ferrite.function_gradient(fe_v::BezierCellAndFaceValues, q_point::Int, u::AbstractVector) + return function_gradient(fe_v.cv_nurbs, q_point, u) +end +function Ferrite.function_value(fe_v::BezierCellAndFaceValues, q_point::Int, u::AbstractVector) + return function_value(fe_v.cv_nurbs, q_point, u) end -function set_bezier_operator!(bcv::BezierValues, beo::BezierExtractionOperator{T}) where T +function set_bezier_operator!(bcv::BezierCellAndFaceValues, beo::BezierExtractionOperator{T}) where T bcv.current_beo[]=beo end -function set_bezier_operator!(bcv::BezierValues, beo::BezierExtractionOperator{T}, w::Vector{T}) where T +function set_bezier_operator!(bcv::BezierCellAndFaceValues, beo::BezierExtractionOperator{T}, w::Vector{T}) where T bcv.current_w .= w bcv.current_beo[]=beo end -_cellvaluestype(::BezierValues{dim_s,T,CV}) where {dim_s,T,CV} = CV - -function Ferrite.spatial_coordinate(bv::IGA.BezierValues, q_point::Int, bc::BezierCoords) - return spatial_coordinate(bv, q_point, (bc.xb, bc.wb)) -end - -function Ferrite.spatial_coordinate(cv::BezierValues, iqp::Int, (xb, wb)::Tuple{<:AbstractVector{Vec{sdim,T}}, <:AbstractVector{T}}) where {sdim,T} - nbasefunks = Ferrite.getn_scalarbasefunctions(cv) - faceid::Int = hasfield(typeof(cv.cv_bezier), :current_face) ? cv.cv_bezier.current_face[] : 1 - @assert nbasefunks == length(xb) - +function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, (xb, wb)::CoordsAndWeight{sdim,T}) where {sdim,T} + nbasefunks = Ferrite.getngeobasefunctions(cv) + @boundscheck Ferrite.checkquadpoint(cv.cv_bezier, iqp) W = 0.0 x = zero(Vec{sdim,T}) for i in 1:nbasefunks - N = cv.cv_bezier.M[i, iqp, faceid] + N = Ferrite.geometric_value(cv, iqp, i) x += N * wb[i] * xb[i] W += N * wb[i] end @@ -99,24 +118,27 @@ end Ferrite.getnormal(fv::BezierFaceValues, i::Int)= fv.cv_bezier.normals[i] #Function that computs basefunction values from bezier function values and the extraction operator, N = C*B -function _cellvalues_bezier_extraction!(cv_store::Ferrite.Values{dim_s}, cv_bezier::Ferrite.Values{dim_s}, Cbe::BezierExtractionOperator{T}, w::Optional{Vector{T}}, faceid::Int) where {dim_s,T} +function _cellvalues_bezier_extraction!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.AbstractValues, Cbe::BezierExtractionOperator{T}, w::Optional{Vector{T}}, faceid::Int) where {T} dBdx = cv_bezier.dNdx # The derivatives of the bezier element dBdξ = cv_bezier.dNdξ B = cv_bezier.N - for iq in 1:Ferrite.getnquadpoints(cv_store) - for ib in 1:Ferrite.getn_scalarbasefunctions(cv_store) + is_scalar_valued = !(eltype(cv_nurbs.N) isa Tensor) + dim_s = length(first(cv_nurbs.N)) + + for iq in 1:Ferrite.getnquadpoints(cv_nurbs) + for ib in 1:Ferrite.getngeobasefunctions(cv_nurbs) - if Ferrite.FieldTrait(typeof(cv_bezier)) === Ferrite.ScalarValued() - cv_store.N[ib, iq, faceid] = zero(eltype(cv_store.N)) - cv_store.dNdξ[ib, iq, faceid] = zero(eltype(cv_store.dNdξ)) - cv_store.dNdx[ib, iq, faceid] = zero(eltype(cv_store.dNdx)) - else #if FieldTrait(cv_store) == Ferrite.VectorValued() + if is_scalar_valued + cv_nurbs.N[ib, iq, faceid] = zero(eltype(cv_nurbs.N)) + cv_nurbs.dNdξ[ib, iq, faceid] = zero(eltype(cv_nurbs.dNdξ)) + cv_nurbs.dNdx[ib, iq, faceid] = zero(eltype(cv_nurbs.dNdx)) + else #if FieldTrait(cv_nurbs) == Ferrite.VectorValued() for d in 1:dim_s - cv_store.N[(ib-1)*dim_s+d, iq, faceid] = zero(eltype(cv_store.N)) - cv_store.dNdξ[(ib-1)*dim_s+d, iq, faceid] = zero(eltype(cv_store.dNdξ)) - cv_store.dNdx[(ib-1)*dim_s+d, iq, faceid] = zero(eltype(cv_store.dNdx)) + cv_nurbs.N[(ib-1)*dim_s+d, iq, faceid] = zero(eltype(cv_nurbs.N)) + cv_nurbs.dNdξ[(ib-1)*dim_s+d, iq, faceid] = zero(eltype(cv_nurbs.dNdξ)) + cv_nurbs.dNdx[(ib-1)*dim_s+d, iq, faceid] = zero(eltype(cv_nurbs.dNdx)) end end @@ -127,15 +149,15 @@ function _cellvalues_bezier_extraction!(cv_store::Ferrite.Values{dim_s}, cv_bezi if (w !== nothing) val*=w[ib] end - if Ferrite.FieldTrait(typeof(cv_bezier)) === Ferrite.ScalarValued() - cv_store.N[ib, iq, faceid] += val* B[nz_ind, iq, faceid] - cv_store.dNdξ[ib, iq, faceid] += val*dBdξ[nz_ind, iq, faceid] - cv_store.dNdx[ib, iq, faceid] += val*dBdx[nz_ind, iq, faceid] - else #if FieldTrait(cv_store) == Ferrite.VectorValued() + if is_scalar_valued + cv_nurbs.N[ib, iq, faceid] += val* B[nz_ind, iq, faceid] + cv_nurbs.dNdξ[ib, iq, faceid] += val*dBdξ[nz_ind, iq, faceid] + cv_nurbs.dNdx[ib, iq, faceid] += val*dBdx[nz_ind, iq, faceid] + else #if FieldTrait(cv_nurbs) == Ferrite.VectorValued() for d in 1:dim_s - cv_store.N[(ib-1)*dim_s + d, iq, faceid] += val* B[(nz_ind-1)*dim_s + d, iq, faceid] - cv_store.dNdξ[(ib-1)*dim_s + d, iq, faceid] += val*dBdξ[(nz_ind-1)*dim_s + d, iq, faceid] - cv_store.dNdx[(ib-1)*dim_s + d, iq, faceid] += val*dBdx[(nz_ind-1)*dim_s + d, iq, faceid] + cv_nurbs.N[(ib-1)*dim_s + d, iq, faceid] += val* B[(nz_ind-1)*dim_s + d, iq, faceid] + cv_nurbs.dNdξ[(ib-1)*dim_s + d, iq, faceid] += val*dBdξ[(nz_ind-1)*dim_s + d, iq, faceid] + cv_nurbs.dNdx[(ib-1)*dim_s + d, iq, faceid] += val*dBdx[(nz_ind-1)*dim_s + d, iq, faceid] end end end @@ -144,51 +166,46 @@ function _cellvalues_bezier_extraction!(cv_store::Ferrite.Values{dim_s}, cv_bezi end -function Ferrite.reinit!(bcv::BezierCellValues{dim_s,T,CV}, xb::AbstractVector{Vec{dim_s,T}}) where {dim_s,T,CV<:Ferrite.CellValues} +function Ferrite.reinit!(bcv::BezierCellValues, xb::AbstractVector{<:Vec}) Ferrite.reinit!(bcv.cv_bezier, xb) #call the normal reinit function first - - _cellvalues_bezier_extraction!(bcv.cv_store, bcv.cv_bezier, bcv.current_beo[], nothing, 1) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_bezier, bcv.current_beo[], nothing, 1) end -function Ferrite.reinit!(bcv::BezierFaceValues{dim_s,T,CV}, xb::AbstractVector{Vec{dim_s,T}}, faceid::Int) where {dim_s,T,CV<:Ferrite.FaceValues} +function Ferrite.reinit!(bcv::BezierFaceValues, xb::AbstractVector{<:Vec}, faceid::Int) Ferrite.reinit!(bcv.cv_bezier, xb, faceid) #call the normal reinit function first - bcv.cv_store.current_face[] = faceid + bcv.cv_nurbs.current_face[] = faceid + bcv.cv_bezier.current_face[] = faceid - _cellvalues_bezier_extraction!(bcv.cv_store, bcv.cv_bezier, bcv.current_beo[], nothing, faceid) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_bezier, bcv.current_beo[], nothing, faceid) end - -# -function Ferrite.reinit!(bcv::BezierCellValues{dim_s,T,CV}, (xb, wb)::Tuple{<:AbstractVector{Vec{dim_s,T}}, <:AbstractVector{T}}) where {dim_s,T,CV<:Ferrite.CellValues} - _reinit_nurbs!(bcv.cv_store, bcv.cv_bezier, xb, wb) - _cellvalues_bezier_extraction!(bcv.cv_store, copy(bcv.cv_store), bcv.current_beo[], bcv.current_w, 1) +function Ferrite.reinit!(bcv::BezierCellValues, (xb, wb)::CoordsAndWeight) + _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, xb, wb) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], bcv.current_w, 1) end -function Ferrite.reinit!(bcv::BezierFaceValues{dim_s,T,CV}, (xb, wb)::Tuple{<:AbstractVector{Vec{dim_s,T}}, <:AbstractVector{T}}, faceid::Int) where {dim_s,T,CV<:Ferrite.FaceValues} - _reinit_nurbs!(bcv.cv_store, bcv.cv_bezier, xb, wb, faceid) - bcv.cv_store.current_face[] = faceid +function Ferrite.reinit!(bcv::BezierFaceValues, (xb, wb)::CoordsAndWeight, faceid::Int) + _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, xb, wb, faceid) + bcv.cv_nurbs.current_face[] = faceid bcv.cv_bezier.current_face[] = faceid - _cellvalues_bezier_extraction!(bcv.cv_store, copy(bcv.cv_store), bcv.current_beo[], bcv.current_w, faceid) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], bcv.current_w, faceid) end -# -# NURBS Function values needs to be treated differently than other basis values, since they have weights-factors aswell -# -function Ferrite.reinit!(bcv::BezierValues, bc::BezierCoords{dim_s,T}) where {dim_s,T} +function Ferrite.reinit!(bcv::BezierCellValues, bc::BezierCoords) set_bezier_operator!(bcv, bc.beo, bc.w) - _reinit_nurbs!(bcv.cv_store, bcv.cv_bezier, bc.xb, bc.wb) - _cellvalues_bezier_extraction!(bcv.cv_store, copy(bcv.cv_store), bc.beo, bc.w, 1) + _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, bc.xb, bc.wb) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo, bc.w, 1) end -function Ferrite.reinit!(bcv::BezierFaceValues, bc::BezierCoords{dim_s,T}, faceid::Int) where {dim_s,T} +function Ferrite.reinit!(bcv::BezierFaceValues, bc::BezierCoords, faceid::Int) set_bezier_operator!(bcv, bc.beo, bc.w) bcv.cv_bezier.current_face[] = faceid - bcv.cv_store.current_face[] = faceid + bcv.cv_nurbs.current_face[] = faceid - _reinit_nurbs!(bcv.cv_store, bcv.cv_bezier, bc.xb, bc.wb, faceid) - _cellvalues_bezier_extraction!(bcv.cv_store, copy(bcv.cv_store), bc.beo, bc.w, faceid) + _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, bc.xb, bc.wb, faceid) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo, bc.w, faceid) end @@ -199,12 +216,14 @@ Similar to Ferrite's reinit method, but in IGA with NURBS, the weights is also n `xᴮ` - Bezier coordinates `w` - weights for nurbs mesh (not bezier weights) """ -function _reinit_nurbs!(cv_nurbs::Ferrite.Values{dim}, cv_bezier::Ferrite.Values{dim}, xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}, cb::Int = 1) where {dim,T} +function _reinit_nurbs!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.AbstractValues, xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}, cb::Int = 1) where {dim,T} n_geom_basefuncs = Ferrite.getngeobasefunctions(cv_bezier) n_func_basefuncs = Ferrite.getnbasefunctions(cv_bezier) @assert length(xᴮ) == n_geom_basefuncs == length(w) @assert typeof(cv_nurbs) == typeof(cv_bezier) - + + is_vector_valued = eltype(cv_nurbs.N) isa Tensor + B = cv_bezier.M dBdξ = cv_bezier.dMdξ @@ -221,18 +240,17 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.Values{dim}, cv_bezier::Ferrite.Values fecv_J = zero(Tensor{2,dim}) for j in 1:n_geom_basefuncs - #Copute nurbs values + # R = B[j, i, cb]./W dRdξ = inv(W)*dBdξ[j, i, cb] - inv(W^2)* dWdξ * B[j, i, cb] #Jacobian fecv_J += xᴮ[j] ⊗ (w[j]*dRdξ) - end #Store nurbs for j in 1:n_func_basefuncs - if Ferrite.FieldTrait(typeof(cv_bezier)) === Ferrite.VectorValued() + if is_vector_valued cv_nurbs.dNdξ[j, i, cb] = inv(W)*cv_bezier.dNdξ[j, i, cb] - inv(W^2) * cv_bezier.N[j, i, cb] ⊗ dWdξ else cv_nurbs.dNdξ[j, i, cb] = inv(W)*cv_bezier.dNdξ[j, i, cb] - inv(W^2) * cv_bezier.N[j, i, cb] * dWdξ @@ -240,7 +258,7 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.Values{dim}, cv_bezier::Ferrite.Values cv_nurbs.N[j,i,cb] = cv_bezier.N[j, i, cb]/W end - if isa(cv_bezier, Ferrite.FaceValues) + if isa(cv_bezier, Ferrite.AbstractFaceValues) weight_norm = Ferrite.weighted_normal(fecv_J, cv_bezier, cb) cv_bezier.normals[i] = weight_norm / norm(weight_norm) detJ = norm(weight_norm) @@ -255,54 +273,4 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.Values{dim}, cv_bezier::Ferrite.Values cv_nurbs.dNdx[j, i, cb] = cv_nurbs.dNdξ[j, i, cb] ⋅ Jinv end end -end - - - -#= -""" -Ferrite.reinit!(cv::Ferrite.CellVectorValues{dim}, xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}) where {dim,T} - -Similar to Ferrite's reinit method, but in IGA with NURBS, the weights is also needed. - `xᴮ` - Bezier coordinates - `w` - weights for nurbs mesh (not bezier weights) -""" - -function Ferrite.reinit!(fv::FaceValues{dim}, xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}, face::Int) where {dim,T} - n_geom_basefuncs = Ferrite.getngeobasefunctions(fv) - n_func_basefuncs = Ferrite.getn_scalarbasefunctions(fv) - @assert length(xᴮ) == n_geom_basefuncs - isa(fv, FaceVectorValues) && (n_func_basefuncs *= dim) - - fv.current_face[] = face - cb = Ferrite.getcurrentface(fv) - - @inbounds for i in 1:length(fv.qr_weights) - weight = fv.qr_weights[i] - - W = zero(T) - dWdξ = zero(Vec{dim,T}) - for j in 1:n_geom_basefuncs - W += w[j]*fv.M[j, i] - dWdξ += w[j]*fv.dMdξ[j, i] - end - - fefv_J = zero(Tensor{2,dim}) - for j in 1:n_geom_basefuncs - dRdξ = w[j]*(inv(W)*fv.dMdξ[j, i] - dWdξ*inv(W^2)*fv.M[j,i]) - fefv_J += xᴮ[j] ⊗ dRdξ - end - - weight_norm = Ferrite.weighted_normal(fefv_J, fv, cb) - fv.normals[i] = weight_norm / norm(weight_norm) - detJ = norm(weight_norm) - - detJ > 0.0 || throw(ArgumentError("det(J) is not positive: det(J) = $(detJ)")) - fv.detJdV[i, cb] = detJ * weight - Jinv = inv(fefv_J) - for j in 1:n_func_basefuncs - fv.dNdx[j, i, cb] = fv.dNdξ[j, i, cb] ⋅ Jinv - end - end -end -=# +end \ No newline at end of file From 16169055f53ec493c508cfe1133ad8afa745f5f2 Mon Sep 17 00:00:00 2001 From: lijas Date: Wed, 13 Sep 2023 11:50:10 +0200 Subject: [PATCH 04/50] forgot this --- src/IGA.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/IGA.jl b/src/IGA.jl index a495373..ee570cd 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -17,6 +17,7 @@ export BezierCoords const Optional{T} = Union{T, Nothing} const BezierExtractionOperator{T} = Vector{SparseArrays.SparseVector{T,Int}} +const CoordsAndWeight{sdim,T} = Tuple{ <: AbstractVector{Vec{sdim,T}}, <: AbstractVector{T}} struct BezierCoords{dim_s,T} xb ::Vector{Vec{dim_s,T}} @@ -53,7 +54,7 @@ include("mesh_generation.jl") include("bezier_grid.jl") include("splines/bezier.jl") include("bezier_extraction.jl") -#include("splines/bezier_values.jl") +include("splines/bezier_values.jl") #include("splines/bsplines.jl") #include("VTK.jl") #include("L2_projection.jl") From d1c524add230b1e8b6a48e25816c60d457157e3f Mon Sep 17 00:00:00 2001 From: lijas Date: Thu, 14 Sep 2023 09:02:52 +0200 Subject: [PATCH 05/50] igainterpolation --- src/IGA.jl | 44 +++++++++++++++-- src/bezier_grid.jl | 2 +- src/splines/bezier.jl | 107 +++++++++++++++++++++++++++--------------- 3 files changed, 110 insertions(+), 43 deletions(-) diff --git a/src/IGA.jl b/src/IGA.jl index ee570cd..33e5249 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -29,6 +29,40 @@ end #Base.zero(Type{BezierCoords{dim,T}}) where {dim,T} = BezierCoords +""" + IGAInterpolation{shape, order} <: Ferrite.ScalarInterpolation{shape, order} +""" + +struct IGAInterpolation{shape, order} <: Ferrite.ScalarInterpolation{shape, order} + function IGAInterpolation{shape,order}() where {rdim, shape<:RefHypercube{rdim}, order} + #Check if you can construct a Bernstein basis + try Bernstein{shape,order}() end + return new{shape,order}() + end +end + +Ferrite.adjust_dofs_during_distribution(::IGAInterpolation) = false +Ferrite.shape_value(::IGAInterpolation{shape, order}, ξ::Vec, i::Int) where {shape, order} = Ferrite.shape_value(Bernstein{shape, order}(), ξ, i) +Ferrite.reference_coordinates(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.reference_coordinates(Bernstein{shape, order}()) +Ferrite.getnbasefunctions(::IGAInterpolation{shape, order}) where {shape, order} = getnbasefunctions(Bernstein{shape, order}()) + +#Ferrite.nvertices(ip::IGAInterpolation) = getnbasefunctions(ip) +Ferrite.vertexdof_indices(ip::IGAInterpolation) = ntuple(i->i, getnbasefunctions(ip)) + +#Remove dofs on edges and faces such that dofhandler can distribute dofs correctly +Ferrite.edgedof_indices(ip::IGAInterpolation{RefHexahedron}) = ntuple(_ -> (), Ferrite.nedges(ip)) +Ferrite.edgedof_interior_indices(ip::IGAInterpolation) = ntuple(_ -> (), Ferrite.nedges(ip)) +Ferrite.facedof_indices(ip::IGAInterpolation{RefHexahedron}) = ntuple(_ -> (), Ferrite.nfaces(ip)) +Ferrite.facedof_interior_indices(ip::IGAInterpolation{RefHexahedron}) = ntuple(_ -> (), Ferrite.nfaces(ip)) +Ferrite.facedof_indices(ip::IGAInterpolation{RefQuadrilateral}) = ntuple(_ -> (), Ferrite.nfaces(ip)) +Ferrite.facedof_interior_indices(ip::IGAInterpolation{RefQuadrilateral}) = ntuple(_ -> (), Ferrite.nfaces(ip)) + +Ferrite.dirichlet_facedof_indices(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.dirichlet_facedof_indices(Bernstein{shape, order}()) +Ferrite.dirichlet_edgedof_indices(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.dirichlet_edgedof_indices(Bernstein{shape, order}()) +Ferrite.dirichlet_vertexdof_indices(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.dirichlet_vertexdof_indices(Bernstein{shape, order}()) + + + """ BezierCell{order,refshape,N} <: Ferrite.AbstractCell{refshape} @@ -46,7 +80,7 @@ struct BezierCell{order,refshape,N} <: Ferrite.AbstractCell{refshape} end -Ferrite.default_interpolation(::Type{<:BezierCell{order, shape}}) where {order, shape} = Bernstein{shape, order}() +Ferrite.default_interpolation(::Type{<:BezierCell{order, shape}}) where {order, shape} = IGAInterpolation{shape, order}() #getorders(::BezierCell{orders,refshape,N}) where {orders,refshape,N} = orders include("nurbsmesh.jl") @@ -68,12 +102,12 @@ Ferrite.vertices(c::BezierCell) = c.nodes _bernstein_ordering(::Type{<:BezierCell{order,shape}}) where {order,shape} = _bernstein_ordering(Bernstein{shape,order}()) -function Ferrite.faces(c::BezierCell{dim,N,order}) where {dim,N,order} - return getindex.(Ref(c.nodes), collect.(Ferrite.faces(Bernstein{length(order),order}() ))) +function Ferrite.faces(c::BezierCell{order,shape}) where {shape,order} + return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_facedof_indices(IGAInterpolation{shape,order}() ))) end -function Ferrite.edges(c::BezierCell{dim,N,order}) where {dim,N,order} - return getindex.(Ref(c.nodes), collect.(Ferrite.edges(Bernstein{length(order),order}() ))) +function Ferrite.edges(c::BezierCell{order,RefHexahedron}) where {order} + return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_edgedof_indices(IGAInterpolation{RefHexahedron,order}() ))) end function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefHexahedron}}) where {order} diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 5245924..d8d765d 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -82,7 +82,7 @@ function Base.show(io::IO, ::MIME"text/plain", grid::BezierGrid) typestrs = sort!(repr.(Set(typeof(x) for x in grid.cells))) end join(io, typestrs, '/') - print(io, " cells and $(getnnodes(grid)) nodes/constrol points") + print(io, " cells and $(getnnodes(grid)) nodes (contorl points)") end """ diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index 1d80d39..f33a406 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -20,26 +20,18 @@ struct Bernstein{shape, order} <: Ferrite.ScalarInterpolation{shape, order} end end -Ferrite.getnbasefunctions(ip::Bernstein{shape,order}) where {shape <: RefHypercube, order} = prod(order) - -Ferrite.vertexdof_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = - Ferrite.vertexdof_indices(Lagrange{shape,order}()) - -Ferrite.edgedof_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = - Ferrite.edgedof_indices(Lagrange{shape,order}()) - -Ferrite.edgedof_interior_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = - Ferrite.edgedof_interior_indices(Lagrange{shape,order}()) - -Ferrite.facedof_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = - Ferrite.facedof_indices(Lagrange{shape,order}()) - -Ferrite.facedof_interior_indices(::Bernstein{shape,order}) where {shape <: RefHypercube, order} = - Ferrite.facedof_interior_indices(Lagrange{shape,order}()) +Ferrite.adjust_dofs_during_distribution(::Bernstein) = true +Ferrite.adjust_dofs_during_distribution(::Bernstein{<:Any, 2}) = false +Ferrite.adjust_dofs_during_distribution(::Bernstein{<:Any, 1}) = false # # # # Bernstein line, order 2 # # # +Ferrite.getnbasefunctions(::Bernstein{RefLine,(2,)}) = 3 + +Ferrite.vertexdof_indices(::Bernstein{RefLine,(2,)}) = ((1,),(2,)) +Ferrite.facedof_indices(::Bernstein{RefLine,(2,)}) = ((1,), (2,)) +Ferrite.celldof_interior_indices(::Bernstein{RefLine,(2,)}) = (3,) function Ferrite.shape_value(ip::Bernstein{RefLine,(2,)}, _ξ::Vec{1}, i::Int) ξ = 0.5*(_ξ[1] + 1.0) @@ -49,9 +41,15 @@ function Ferrite.shape_value(ip::Bernstein{RefLine,(2,)}, _ξ::Vec{1}, i::Int) throw(ArgumentError("no shape function $i for interpolation $ip")) end + # # # # Bernstein Quadrilateral, order 2 # # # +Ferrite.getnbasefunctions(::Bernstein{RefQuadrilateral,(2,2)}) = 9 + +Ferrite.facedof_indices(::Bernstein{RefQuadrilateral,(2,2)}) = ((1,2, 5), (2,3, 6), (3,4, 7), (4,1, 8)) +Ferrite.facedof_interior_indices(::Bernstein{RefQuadrilateral,(2,2)}) = ((5,), (6,), (7,), (8,)) +Ferrite.celldof_interior_indices(::Bernstein{RefQuadrilateral,(2,2)}) = (9,) function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,(2,2)}, _ξ::Vec{2}, i::Int) ξ, η = _ξ @@ -70,6 +68,36 @@ end # # # # Bernstein Hexahedron, order 2 # # # +Ferrite.getnbasefunctions(::Bernstein{RefHexahedron,(2,2,2)}) = 27 + +Ferrite.facedof_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( + (1,4,3,2, 12,11,10,9, 21), + (1,2,6,5, 9,18,13,17, 22), + (2,3,7,6, 10,19,14,18, 23), + (3,4,8,7, 11,20,15,19, 24), + (1,5,8,4, 17,16,20,12, 25), + (5,6,7,8, 13,14,15,16, 26), +) +Ferrite.facedof_interior_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( + (21,), (22,), (23,), (24,), (25,), (26,), +) +Ferrite.edgedof_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( + (1,2, 9), + (2,3, 10), + (3,4, 11), + (4,1, 12), + (5,6, 13), + (6,7, 14), + (7,8, 15), + (8,5, 16), + (1,5, 17), + (2,6, 18), + (3,7, 19), + (4,8, 20), +) +Ferrite.edgedof_interior_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( + (9,), (10,), (11,), (12,), (13,), (14,), (15,), (16,), (17), (18,), (19,), (20,) +) function Ferrite.shape_value(ip::Bernstein{RefHexahedron,(2,2,2)}, _ξ::Vec{3}, i::Int) ξ, η, ζ = _ξ @@ -151,27 +179,6 @@ function Ferrite.shape_value(ip::Bernstein{shape,orders}, i::Int, ξ::Vec{dim,T} end return val end - -function Ferrite.reference_coordinates(ip::Bernstein{shape,order}) where {rdim, shape <: AbstractRefShape{rdim}, order <: Integer} - - T = Float64 - nbasefunks_dim = ntuple(i->order, rdim) - nbasefuncs = prod(nbasefunks_dim) - - coords = Vec{rdim,T}[] - - ordering = _bernstein_ordering(ip) - ranges = [range(T(-1.0), stop=T(1.0), length=nbasefunks_dim[i]) for i in 1:rdim] - - inds = CartesianIndices(nbasefunks_dim) - for i in 1:nbasefuncs - ind = inds[ordering[i]] - x = Vec{rdim,T}(d -> ranges[d][ind[d]]) - push!(coords, x) - end - - return coords -end function Ferrite.facedof_indices(ip::Bernstein{RefQuadrilateral,orders}) where {orders} faces = Tuple[] @@ -249,6 +256,32 @@ function _bernstein_basis_derivative_recursive(p::Int, i::Int, xi::T) where T end +# # # +# Bernstein generation any order +# # # + + +function Ferrite.reference_coordinates(ip::Bernstein{shape,order}) where {rdim, shape <: AbstractRefShape{rdim}, order <: Integer} + + T = Float64 + nbasefunks_dim = ntuple(i->order, rdim) + nbasefuncs = prod(nbasefunks_dim) + + coords = Vec{rdim,T}[] + + ordering = _bernstein_ordering(ip) + ranges = [range(T(-1.0), stop=T(1.0), length=nbasefunks_dim[i]) for i in 1:rdim] + + inds = CartesianIndices(nbasefunks_dim) + for i in 1:nbasefuncs + ind = inds[ordering[i]] + x = Vec{rdim,T}(d -> ranges[d][ind[d]]) + push!(coords, x) + end + + return coords +end + """ _bernstein_ordering(::Bernstein) From ff7053425bf9b0dc701502d63a7d7c24dff0f3b4 Mon Sep 17 00:00:00 2001 From: lijas Date: Sun, 17 Sep 2023 15:17:15 +0200 Subject: [PATCH 06/50] get tests to work --- docs/src/literate/plate_with_hole.jl | 23 +++-- src/IGA.jl | 4 +- src/bezier_grid.jl | 2 +- src/mesh_generation.jl | 2 +- src/nurbsmesh.jl | 4 + src/splines/bezier.jl | 52 ++++++------ src/splines/bezier_values.jl | 56 ++++++------ src/splines/bsplines.jl | 12 +-- test/runtests.jl | 2 +- test/test_bertstein.jl | 30 +++---- test/test_bezier_extraction.jl | 4 +- test/test_bsplines.jl | 10 ++- test/test_meshes.jl | 22 ++--- test/test_nurbsmesh.jl | 2 +- test/test_values.jl | 122 +++++++++++++++++++-------- 15 files changed, 201 insertions(+), 146 deletions(-) diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl index 8315105..415a523 100644 --- a/docs/src/literate/plate_with_hole.jl +++ b/docs/src/literate/plate_with_hole.jl @@ -77,8 +77,6 @@ function assemble_problem(dh::MixedDofHandler, grid, cv, fv, stiffmat, traction) # we also require the cell weights, and we need to transform them to the bezier mesh. extr = get_extraction_operator(grid, cellid) # Extraction operator get_bezier_coordinates!(xb,wb,x,w,grid,cellid) #Nurbs coords - - # Furthermore, we pass the bezier extraction operator and weigts to the CellValues/Beziervalues. set_bezier_operator!(cv, extr, w) reinit!(cv, (xb,wb)) ## Reinit cellvalues by passsing both bezier coords and weights integrate_element!(ke, stiffmat, cv) @@ -93,8 +91,6 @@ function assemble_problem(dh::MixedDofHandler, grid, cv, fv, stiffmat, traction) celldofs!(celldofs, dh, cellid) beziercoords = getcoordinates(grid, cellid) - - #set_bezier_operator!(fv, ) reinit!(fv, beziercoords, faceid) integrate_traction_force!(fe, traction, fv) @@ -152,7 +148,7 @@ function solve() nels = (20,10) # Number of elements nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) - # Performing the computation on a NURBS-patch is possible, but it is much easier to use the bezier-extraction technique. For this + # Performing the computation on a NURBS-patch is possible, but it is much easier to using "bezier-extraction". For this # we transform the NURBS-patch into a `BezierGrid`. The `BezierGrid` is identical to the standard `Ferrite.Grid`, but includes the NURBS-weights and # bezier extraction operators. grid = BezierGrid(nurbsmesh) @@ -160,23 +156,24 @@ function solve() # Next, create some facesets. This is done in the same way as in normal Ferrite-code. One thing to note however, is that the nodes/controlpoints, # does not necessary lay exactly on the geometry due to the non-interlapotry nature of NURBS spline functions. However, in most cases they will be close enough to # use the Ferrite functions below. - addnodeset!(grid,"right", (x) -> x[1] ≈ -0.0) + addnodeset!(grid, "right", (x) -> x[1] ≈ -0.0) addfaceset!(grid, "left", (x) -> x[1] ≈ -4.0) addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0) addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0) # Create the cellvalues storing the shape function values. Note that the `CellVectorValues`/`FaceVectorValues` are wrapped in a `BezierValues`. It is in the # reinit-function of the `BezierValues` that the actual bezier transformation of the shape values is performed. - ip = Bernstein{2,orders}() - qr_cell = QuadratureRule{2,RefCube}(4) - qr_face = QuadratureRule{1,RefCube}(3) + ip_geo = Bernstein{RefQuadrilateral,2}() + ip_u = ip_geo^2 + qr_cell = QuadratureRule{RefQuadrilateral}(4) + qr_face = FaceQuadratureRule{RefQuadrilateral}(3) - cv = BezierCellValues( CellVectorValues(qr_cell, ip) ) - fv = BezierFaceValues( FaceVectorValues(qr_face, ip) ) + cv = BezierCellValues(qr_cell, ip_u, ip_geo) + fv = BezierFaceValues(qr_face, ip_u, ip_geo) # Distribute dofs as normal - dh = MixedDofHandler(grid) - push!(dh, :u, 2, ip) + dh = DofHandler(grid) + push!(dh, :u, ip_u) close!(dh) # Add two symmetry boundary condintions. Bottom face should only be able to move in x-direction, and the right boundary should only be able to move in y-direction diff --git a/src/IGA.jl b/src/IGA.jl index 33e5249..7302022 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -36,7 +36,7 @@ end struct IGAInterpolation{shape, order} <: Ferrite.ScalarInterpolation{shape, order} function IGAInterpolation{shape,order}() where {rdim, shape<:RefHypercube{rdim}, order} #Check if you can construct a Bernstein basis - try Bernstein{shape,order}() end + Bernstein{shape,order}() return new{shape,order}() end end @@ -89,7 +89,7 @@ include("bezier_grid.jl") include("splines/bezier.jl") include("bezier_extraction.jl") include("splines/bezier_values.jl") -#include("splines/bsplines.jl") +include("splines/bsplines.jl") #include("VTK.jl") #include("L2_projection.jl") diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index d8d765d..929b0b1 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -167,7 +167,7 @@ function get_bezier_coordinates(grid::BezierGrid{dim,C,T}, ic::Int) where {dim,C xb = zeros(Vec{dim,T}, n) get_bezier_coordinates!(xb, wb, x, w, grid, ic) - return xb, wb + return xb, wb, x, w end function get_nurbs_coordinates(grid::BezierGrid{dim,C,T}, cell::Int) where {dim,C,T} diff --git a/src/mesh_generation.jl b/src/mesh_generation.jl index d8552db..871c4fd 100644 --- a/src/mesh_generation.jl +++ b/src/mesh_generation.jl @@ -43,7 +43,7 @@ function generate_nurbs_patch(s::Symbol, args...; kwargs...) generate_nurbs_patch(Val{s}(), args...; kwargs...) end -function generate_nurbs_patch(::Val{:cube}, nel::NTuple{3,Int}, orders::NTuple{3,Int}; cornerpos::NTuple{3,T} = (0.0,0.0,0.0), size::NTuple{3,T}, multiplicity::NTuple{3,Int}=(1,1,1)) where T +function generate_nurbs_patch(::Val{:cube}, nel::NTuple{3,Int}, orders::NTuple{3,Int}; cornerpos::NTuple{3,T} = (-1.0,-1.0,-1.0), size::NTuple{3,T} = (2.0,2.0,2.0), multiplicity::NTuple{3,Int}=(1,1,1)) where T pdim = 3 sdim = 3 diff --git a/src/nurbsmesh.jl b/src/nurbsmesh.jl index a775cd8..3569a88 100644 --- a/src/nurbsmesh.jl +++ b/src/nurbsmesh.jl @@ -41,6 +41,10 @@ struct NURBSMesh{pdim,sdim,T} #<: Ferrite.AbstractGrid end +function Base.show(io::IO, ::MIME"text/plain", grid::NURBSMesh) + print(io, "$(typeof(NURBSMesh)) and with order $(grid.orders) and $(getncells(grid)) cells and $(getnnodes(grid)) nodes (contorl points) ") +end + Ferrite.getncells(mesh::NURBSMesh) = size(mesh.IEN, 2) Ferrite.getnnodes(mesh::NURBSMesh) = maximum(mesh.IEN) const getncontrolponits = Ferrite.getnnodes diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index f33a406..db804ca 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -119,8 +119,8 @@ function Ferrite.shape_value(ip::Bernstein{RefHexahedron,(2,2,2)}, _ξ::Vec{3}, i == 16 && return 0.03125(1 + η)*((1 + ζ)^2)*((1 - ξ)^2)*(1 - η) i == 17 && return 0.03125(1 + ζ)*((1 - η)^2)*((1 - ξ)^2)*(1 - ζ) i == 18 && return 0.03125(1 + ζ)*((1 + ξ)^2)*((1 - η)^2)*(1 - ζ) - i == 19 && return 0.03125(1 + ζ)*((1 + η)^2)*((1 - ξ)^2)*(1 - ζ) - i == 20 && return 0.03125(1 + ζ)*((1 + η)^2)*((1 + ξ)^2)*(1 - ζ) + i == 19 && return 0.03125(1 + ζ)*((1 + η)^2)*((1 + ξ)^2)*(1 - ζ) + i == 20 && return 0.03125(1 + ζ)*((1 + η)^2)*((1 - ξ)^2)*(1 - ζ) i == 21 && return 0.0625(1 + η)*(1 + ξ)*((1 - ζ)^2)*(1 - η)*(1 - ξ) i == 22 && return 0.0625(1 + ζ)*(1 + ξ)*((1 - η)^2)*(1 - ζ)*(1 - ξ) i == 23 && return 0.0625(1 + ζ)*(1 + η)*((1 + ξ)^2)*(1 - ζ)*(1 - η) @@ -133,13 +133,13 @@ end #= # Code for computing higher dim and order Bernstein - polynomials, e.g see function value(::Bernstein{2,(2,2)}, ::Int, ξ) + polynomials, e.g see function value(::Bernstein{RefQuadrilateral, 2}, ::Int, ξ) using Symbolics @variables ξ η ζ -ip1d = Bernstein{1,(2,)}() +ip1d = Bernstein{RefLine, 2}() dim = 3 order = (2,2,2)#,2) @@ -167,8 +167,11 @@ end =# +function Ferrite.shape_value(ip::Bernstein, _ξ::Vec, i::Int) + _compute_bezier_shape_value(ip,_ξ, i) +end -function Ferrite.shape_value(ip::Bernstein{shape,orders}, i::Int, ξ::Vec{dim,T}) where {shape,dim,orders,T} +function _compute_bezier_shape_value(ip::Bernstein{shape,orders}, ξ::Vec{dim,T}, i::Int) where {shape,dim,orders,T} _n = orders .+ 1 ordering = _bernstein_ordering(ip) basefunction_indeces = CartesianIndices(_n)[ordering[i]] @@ -180,7 +183,7 @@ function Ferrite.shape_value(ip::Bernstein{shape,orders}, i::Int, ξ::Vec{dim,T} return val end -function Ferrite.facedof_indices(ip::Bernstein{RefQuadrilateral,orders}) where {orders} +function _compute_facedof_indices(ip::Bernstein{RefQuadrilateral,orders}) where {orders} faces = Tuple[] ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) @@ -196,12 +199,12 @@ function Ferrite.facedof_indices(ip::Bernstein{RefQuadrilateral,orders}) where { return Tuple(faces) end -function Ferrite.facedof_indices(ip::Bernstein{3,orders}) where {orders} +function _compute_facedof_indices(ip::Bernstein{RefHexahedron,orders}) where {orders} faces = Tuple[] ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) #Order for 2d interpolation. - order_2d = _bernstein_ordering(Bernstein{2,orders[1:2]}()) + order_2d = _bernstein_ordering(Bernstein{RefQuadrilateral,first(orders)}()) push!(faces, Tuple(ind[:,:,1][order_2d])) # bottom push!(faces, Tuple(ind[:,1,:][order_2d])) # front @@ -213,7 +216,7 @@ function Ferrite.facedof_indices(ip::Bernstein{3,orders}) where {orders} return Tuple(faces) end -function Ferrite.edges(ip::Bernstein{3,orders}) where {orders} +function _compute_edgedof_indices(ip::Bernstein{RefHexahedron,orders}) where {orders} edges = Tuple[] ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) @@ -259,7 +262,7 @@ end # # # # Bernstein generation any order # # # - +Ferrite.getnbasefunctions(::Bernstein{shape,orders}) where {shape, orders}= prod(orders.+1) function Ferrite.reference_coordinates(ip::Bernstein{shape,order}) where {rdim, shape <: AbstractRefShape{rdim}, order <: Integer} @@ -330,21 +333,22 @@ function _bernstein_ordering(::Bernstein{RefHexahedron, orders}) where {orders} ordering = Int[] # Corners, bottom - push!(ordering, ind[1,1,1]) - push!(ordering, ind[end,1,1]) + push!(ordering, ind[1, 1, 1]) + push!(ordering, ind[end,1, 1]) push!(ordering, ind[end,end,1]) - push!(ordering, ind[1,end,1]) + push!(ordering, ind[1 ,end,1]) + # Corners, top - push!(ordering, ind[1,1,end]) - push!(ordering, ind[end,1,end]) + push!(ordering, ind[1, 1 ,end]) + push!(ordering, ind[end,1, end]) push!(ordering, ind[end,end,end]) - push!(ordering, ind[1,end,end]) + push!(ordering, ind[1, end,end]) - # edges, bottom - append!(ordering, ind[2:end-1,1,1]) - append!(ordering, ind[end, 2:end-1,1]) - append!(ordering, ind[2:end-1,end,1]) - append!(ordering, ind[1, 2:end-1,1]) + # edges, bottom + append!(ordering, ind[2:end-1, 1,1]) + append!(ordering, ind[end, 2:end-1,1]) + append!(ordering, ind[2:end-1,end, 1]) + append!(ordering, ind[1, 2:end-1,1]) # edges, top append!(ordering, ind[2:end-1,1,end]) @@ -353,10 +357,10 @@ function _bernstein_ordering(::Bernstein{RefHexahedron, orders}) where {orders} append!(ordering, ind[1, 2:end-1,end]) # edges, mid - append!(ordering, ind[1,1, 2:end-1]) - append!(ordering, ind[end,1, 2:end-1]) - append!(ordering, ind[1, end, 2:end-1]) + append!(ordering, ind[1, 1, 2:end-1]) + append!(ordering, ind[end, 1, 2:end-1]) append!(ordering, ind[end, end, 2:end-1]) + append!(ordering, ind[1, end, 2:end-1]) # Faces (vtk orders left face first, but Ferrite orders bottom first) append!(ordering, ind[2:end-1, 2:end-1, 1][:]) # bottom diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index c10e5ed..745dbed 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -4,6 +4,13 @@ export BezierCellValues, BezierFaceValues, set_bezier_operator! Wraps a standard `Ferrite.CellValues`, but multiplies the shape values with the bezier extraction operator each time the `reinit!` function is called. """ +function Ferrite.default_geometric_interpolation(::IGAInterpolation{shape, order}) where {order, dim, shape <: AbstractRefShape{dim}} + return VectorizedInterpolation{dim}(IGAInterpolation{shape, order}()) +end +function Ferrite.default_geometric_interpolation(::Bernstein{shape, order}) where {order, dim, shape <: AbstractRefShape{dim}} + return VectorizedInterpolation{dim}(Bernstein{shape, order}()) +end + struct BezierCellValues{T<:Real,CV<:Ferrite.CellValues} <: Ferrite.AbstractCellValues # cv_bezier stores the bernstein basis. These are the same for all elements, and does not change cv_bezier::CV @@ -43,32 +50,27 @@ function BezierFaceValues(cv::Ferrite.FaceValues) return BezierFaceValues(cv, deepcopy(cv), deepcopy(cv), undef_beo, undef_w) end -function BezierCellValues(qr::QuadratureRule, ip::Interpolation, - gip::Interpolation = Ferrite.default_geometric_interpolation(ip)) +function BezierCellValues(qr::QuadratureRule, ip::Interpolation, gip::Interpolation) return BezierCellValues(Float64, qr, ip, gip) end -function BezierCellValues(::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} - cv = CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) - +function BezierCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where {T, QR, IP, VGIP} + cv = CellValues(T, qr, ip, gip) return BezierCellValues(cv) end function Ferrite.function_symmetric_gradient(bv::IGA.BezierValues, q_point::Int, u::AbstractVector, args...) return function_symmetric_gradient(bv.cv_store, q_point, u, args...) end +function BezierFaceValues(qr::FaceQuadratureRule, ip::Interpolation, gip::Interpolation) + return BezierFaceValues(Float64, qr, ip, gip) +end + +function BezierFaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where {T, QR, IP, VGIP} + cv = FaceValues(T, qr, ip, gip) + return BezierFaceValues(cv) +end + Ferrite.getnbasefunctions(bcv::BezierCellAndFaceValues) = size(bcv.cv_bezier.N, 1) Ferrite.getngeobasefunctions(bcv::BezierCellAndFaceValues) = size(bcv.cv_bezier.M, 1) Ferrite.getnquadpoints(bcv::BezierCellAndFaceValues) = Ferrite.getnquadpoints(bcv.cv_bezier) @@ -124,7 +126,7 @@ function _cellvalues_bezier_extraction!(cv_nurbs::Ferrite.AbstractValues, cv_bez dBdξ = cv_bezier.dNdξ B = cv_bezier.N - is_scalar_valued = !(eltype(cv_nurbs.N) isa Tensor) + is_scalar_valued = !(first(cv_nurbs.N) isa Tensor) dim_s = length(first(cv_nurbs.N)) for iq in 1:Ferrite.getnquadpoints(cv_nurbs) @@ -193,19 +195,19 @@ function Ferrite.reinit!(bcv::BezierFaceValues, (xb, wb)::CoordsAndWeight, facei end function Ferrite.reinit!(bcv::BezierCellValues, bc::BezierCoords) - set_bezier_operator!(bcv, bc.beo, bc.w) + set_bezier_operator!(bcv, bc.beo[], bc.w) _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, bc.xb, bc.wb) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo, bc.w, 1) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bc.w, 1) end function Ferrite.reinit!(bcv::BezierFaceValues, bc::BezierCoords, faceid::Int) - set_bezier_operator!(bcv, bc.beo, bc.w) + set_bezier_operator!(bcv, bc.beo[], bc.w) bcv.cv_bezier.current_face[] = faceid bcv.cv_nurbs.current_face[] = faceid _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, bc.xb, bc.wb, faceid) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo, bc.w, faceid) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bc.w, faceid) end @@ -222,13 +224,12 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.Abs @assert length(xᴮ) == n_geom_basefuncs == length(w) @assert typeof(cv_nurbs) == typeof(cv_bezier) - is_vector_valued = eltype(cv_nurbs.N) isa Tensor - + is_vector_valued = first(cv_nurbs.N) isa Tensor B = cv_bezier.M dBdξ = cv_bezier.dMdξ - @inbounds for i in 1:length(cv_bezier.qr.weights) - weight = cv_bezier.qr.weights[i] + qrweights = cv_bezier isa Ferrite.FaceValues ? Ferrite.getweights(cv_bezier.qr, cb) : Ferrite.getweights(cv_bezier.qr) + @inbounds for (i,qr_w) in pairs(qrweights) W = zero(T) dWdξ = zero(Vec{dim,T}) @@ -239,7 +240,6 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.Abs fecv_J = zero(Tensor{2,dim}) for j in 1:n_geom_basefuncs - # R = B[j, i, cb]./W dRdξ = inv(W)*dBdξ[j, i, cb] - inv(W^2)* dWdξ * B[j, i, cb] @@ -267,7 +267,7 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.Abs end detJ > 0.0 || throw(ArgumentError("det(J) is not positive: det(J) = $(detJ)")) - cv_bezier.detJdV[i,cb] = detJ * weight + cv_bezier.detJdV[i,cb] = detJ * qr_w Jinv = inv(fecv_J) for j in 1:n_func_basefuncs cv_nurbs.dNdx[j, i, cb] = cv_nurbs.dNdξ[j, i, cb] ⋅ Jinv diff --git a/src/splines/bsplines.jl b/src/splines/bsplines.jl index 3c6ae0e..d77ddaf 100644 --- a/src/splines/bsplines.jl +++ b/src/splines/bsplines.jl @@ -1,10 +1,11 @@ export BSplineBasis """ -Interpolation for bsplines. - Not really used, since bezier-interpolation + bezier-extraction is prefered. +BSplineBasis{dim,T,order} <: Ferrite.ScalarInterpolation{RefHypercube{dim}, order} + + Not really used in FE-codes. We use it for testing in IGA.jl """ -struct BSplineBasis{dim,T,order} <: Ferrite.Interpolation{dim,Ferrite.RefCube,order} +struct BSplineBasis{dim,T,order} <: Ferrite.ScalarInterpolation{RefHypercube{dim}, order} knot_vector::NTuple{dim,Vector{T}} function BSplineBasis(knots::NTuple{dim,Vector{T}}, order::NTuple{dim,Int}) where {dim,T} @@ -21,10 +22,9 @@ struct BSplineBasis{dim,T,order} <: Ferrite.Interpolation{dim,Ferrite.RefCube,or end getnbasefunctions_dim(basis::BSplineBasis{dim,T,order}) where {dim,T,order} = ntuple(i -> length(basis.knot_vector[i]) - order[i] - 1, dim) -Ferrite.getnbasefunctions(basis::BSplineBasis{dim,T,order}) where {dim,T,order} = prod(getnbasefunctions_dim(basis))::Int - -function Ferrite.value(b::BSplineBasis{dim,T,order}, i, xi::Vec{dim}) where {dim,T,order} +Ferrite.getnbasefunctions(basis::BSplineBasis{dim,T,order}) where {dim,T,order} = prod(getnbasefunctions_dim(basis)) +function Ferrite.shape_value(b::BSplineBasis{dim,T,order}, xi::Vec{dim}, i::Int) where {dim,T,order} @assert( i <= Ferrite.getnbasefunctions(b)) _n = getnbasefunctions_dim(b) diff --git a/test/runtests.jl b/test/runtests.jl index c9764be..342bbb9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,5 +8,5 @@ include("test_bsplines.jl") include("test_bertstein.jl") include("test_bezier_extraction.jl") include("test_nurbsmesh.jl") -include("test_examples.jl") +#include("test_examples.jl") include("test_utility_functions.jl") \ No newline at end of file diff --git a/test/test_bertstein.jl b/test/test_bertstein.jl index daccac1..7b94411 100644 --- a/test/test_bertstein.jl +++ b/test/test_bertstein.jl @@ -2,26 +2,26 @@ @testset "bernstein" begin #1d basis function should equal to 2d basis function on the boundary (-1.0) - b1 = Bernstein{2,(2,2)}() - b2 = Bernstein{1,2}() + b1 = Bernstein{RefQuadrilateral, 2}() + b2 = Bernstein{RefLine, 2}() @test Ferrite.value(b1, 5, Vec((0.0,-1.0))) == Ferrite.value(b2, 3, Vec((0.0))) #Sum of shape function should equal 1 - for bip in (Bernstein{1,(1,)}(), - Bernstein{1,(2,)}(), - Bernstein{1,(3,)}(), - Bernstein{1,(4,)}(), - Bernstein{2,(2,2,)}(), - Bernstein{2,(3,3,)}(), - Bernstein{3,(2,2,2)}(), - Bernstein{3,(3,3,3)}() + for bip in (Bernstein{RefLine, 1}(), + Bernstein{RefLine, 2}(), + Bernstein{RefLine, 3}(), + Bernstein{RefLine, 4}(), + Bernstein{RefQuadrilateral, 2}(), + #Bernstein{RefQuadrilateral, 3}(), + Bernstein{RefHexahedron, 2}(), + #Bernstein{RefHexahedron, 3}() ) dim = Ferrite.getdim(bip) for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})] sum = 0.0 - for i in 1:Ferrite.getnbasefunctions(bip) - sum += Ferrite.value(bip, i, xi) + for i in 1:Ferrite.getnbasefunctions(bip) + sum += Ferrite.shape_value(bip, xi, i) end @test sum ≈ 1.0 end @@ -29,13 +29,13 @@ #Test generated Bernstein values with hardcoded ones # ip_list contains interpolation which explicitly been inputed in IGA.jl - ip_list = ( Bernstein{1,(2,)}(), Bernstein{2,(2,2,)}(), Bernstein{3,(2,2,2)}()) + ip_list = ( Bernstein{RefLine, 2}(), Bernstein{RefQuadrilateral, 2}(), Bernstein{RefHexahedron, 2}()) for bip in ip_list dim = Ferrite.getdim(bip) for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})] for i in 1:Ferrite.getnbasefunctions(bip) - N_hardcoded = Ferrite.value(bip, i, xi) - M_generated = IGA._berstein_value(bip, i, xi) + N_hardcoded = Ferrite.shape_value(bip, xi, i) + M_generated = IGA._compute_bezier_shape_value(bip, xi, i) @test M_generated ≈ N_hardcoded end end diff --git a/test/test_bezier_extraction.jl b/test/test_bezier_extraction.jl index 06db000..62bd7e2 100644 --- a/test/test_bezier_extraction.jl +++ b/test/test_bezier_extraction.jl @@ -8,7 +8,7 @@ #Test univariate bspline extraction @test nbe == 4 @test length(C) == 4 - ordering = IGA._bernstein_ordering(Bernstein{1,order}()) + ordering = IGA._bernstein_ordering(Bernstein{RefLine, order}()) @test all(C[1] .≈ Float64[1 0 0 0; 0 1 0.5 1/4; 0 0 1/2 7/12; 0 0 0 1/6][ordering,ordering]) @test all(C[2] .≈ Float64[1/4 0 0 0; 7/12 2/3 1/3 1/6; 1/6 1/3 2/3 2/3; 0 0 0 1/6][ordering,ordering]) @test all(C[3] .≈ Float64[1/6 0 0 0; 2/3 2/3 1/3 1/6; 1/6 1/3 2/3 7/12; 0 0 0 1/4][ordering,ordering]) @@ -18,7 +18,7 @@ orders = (2,2) knots = (Float64[0,0,0,1/3,2/3,1,1,1], Float64[0,0,0,1/3,2/3,1,1,1]) C,nbe = IGA.compute_bezier_extraction_operators(orders, knots) - ordering = IGA._bernstein_ordering(Bernstein{2,orders}()) + ordering = IGA._bernstein_ordering(Bernstein{RefQuadrilateral, orders}()) @test nbe == 9 @test length(C) == 9 @test all(C[1] .≈ kron( [1 0 0; 0 1 1/2; 0 0 1/2], [1 0 0; 0 1 1/2; 0 0 1/2])[ordering,ordering]) diff --git a/test/test_bsplines.jl b/test/test_bsplines.jl index b5f5be9..8284f30 100644 --- a/test/test_bsplines.jl +++ b/test/test_bsplines.jl @@ -60,14 +60,16 @@ end knot_vector = [(ones(T, p+1)*-1)..., ones(T, p+1)...] ip1 = BSplineBasis((knot_vector,knot_vector), (p,p)) - ip2 = Bernstein{2,(p,p)}() + ip2 = Bernstein{RefQuadrilateral, p}() reorder = IGA._bernstein_ordering(ip2) ξ = Vec((rand(),rand())) - N1 = Ferrite.value(ip1, ξ)[reorder] - N2 = Ferrite.value(ip2, ξ) + for i in 1:getnbasefunctions(ip2) + N1 = Ferrite.shape_value(ip1, ξ, reorder[i]) + N2 = Ferrite.shape_value(ip2, ξ, i) + @test N1 ≈ N2 + end - @test N1 ≈ N2 end end \ No newline at end of file diff --git a/test/test_meshes.jl b/test/test_meshes.jl index c12d52c..58d1610 100644 --- a/test/test_meshes.jl +++ b/test/test_meshes.jl @@ -1,6 +1,7 @@ function _calculate_volume(cv, grid, cellset) V = 0.0 + for cellid in cellset bc = getcoordinates(grid, cellid) reinit!(cv, bc) @@ -30,16 +31,15 @@ function _get_problem_data(meshsymbol::Symbol, nels::NTuple{sdim,Int}, orders; m mesh = generate_nurbs_patch(meshsymbol, nels, orders; meshkwargs...) grid = BezierGrid(mesh) - bern_ip = Bernstein{sdim, mesh.orders}() + bern_ip = Bernstein{Ferrite.RefHypercube{sdim}, mesh.orders}() #Cell values - qr = Ferrite.QuadratureRule{sdim,Ferrite.RefCube}(5) - cv = IGA.BezierCellValues(Ferrite.CellVectorValues(qr, bern_ip)) + qr = Ferrite.QuadratureRule{Ferrite.RefHypercube{sdim}}(5) + cv = IGA.BezierCellValues(qr, bern_ip, bern_ip) #Face values - qr = Ferrite.QuadratureRule{sdim-1,Ferrite.RefCube}(5) - fv = IGA.BezierFaceValues(Ferrite.FaceVectorValues(qr, bern_ip)) - + qr = FaceQuadratureRule{Ferrite.RefHypercube{sdim}}(5) + fv = IGA.BezierFaceValues(qr, bern_ip, bern_ip) return grid, cv, fv end @@ -47,7 +47,7 @@ end function test_cube() grid, cv, fv = _get_problem_data(:cube, (2,2,2), (2,2,2), cornerpos=(-1.0,-2.0,0.0), size=(2.0,3.0,4.0)) addcellset!(grid, "all", (x)->true) - addfaceset!(grid, "left", (x)->x[1]≈-1.0) + addfaceset!(grid, "left", (x)-> x[1]≈-1.0) addfaceset!(grid, "right", (x)->x[1]≈1.0) addfaceset!(grid, "top", (x)->x[3]≈4.0) @@ -70,7 +70,7 @@ end function test_square() grid, cv, fv = _get_problem_data(:cube, (1,1,), (2,2,), cornerpos=(-1.0,-1.0), size=(2.0,3.0,)) addcellset!(grid, "all", (x)->true) - addfaceset!(grid, "left", (x)->x[1]≈-1.0) + addfaceset!(grid, "left", (x)-> x[1] ≈ -1.0) addfaceset!(grid, "right", (x)->x[1]≈1.0) addfaceset!(grid, "top", (x)->x[2]≈2.0) @@ -129,10 +129,6 @@ function test_singly_curved_2d() A = _calculate_area(fv, grid, getfaceset(grid, "top")) @test isapprox(A, (100+5.0/2)*pi/2, atol = 2.0) - - vtk_grid("singly_curved.vtu", grid) do vtk - # - end end @@ -172,8 +168,6 @@ function test_ring() A = _calculate_area(fv, grid, getfaceset(grid, "outer")) @test isapprox(A, 2pi*ro, atol = 0.01) - - end diff --git a/test/test_nurbsmesh.jl b/test/test_nurbsmesh.jl index 31f95f6..6784a09 100644 --- a/test/test_nurbsmesh.jl +++ b/test/test_nurbsmesh.jl @@ -15,7 +15,7 @@ end -@testset "Test BezierGrid(grid)" begin +@testset "Test grid to BezierGrid convertion" begin grid = Ferrite.generate_grid(QuadraticQuadrilateral, (4,4)) bgrid = BezierGrid(grid) diff --git a/test/test_values.jl b/test/test_values.jl index f110dcc..af7d787 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -37,6 +37,30 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pd return B[reorder], dBdξ[reorder] end +@testset "bezier values construction" begin + + sdim = 3 + shape = Ferrite.RefHypercube{sdim} + ip = Bernstein{shape, 2}() + + qr = QuadratureRule{shape}(1) + qr_face = FaceQuadratureRule{shape}(1) + + cv = BezierCellValues( qr, ip, ip) + cv2 = BezierCellValues( CellValues(qr, ip, ip) ) + @test cv.cv_bezier.M == cv2.cv_bezier.M + + cv_vector = BezierCellValues( qr, ip^sdim, ip ) + cv_vector2 = BezierCellValues( CellValues(qr, ip^sdim, ip) ) + @test cv_vector.cv_bezier.M == cv_vector2.cv_bezier.M + + @test Ferrite.getngeobasefunctions(cv_vector) == getnbasefunctions(ip) + @test Ferrite.getngeobasefunctions(cv) == getnbasefunctions(ip) + + @test Ferrite.getnbasefunctions(cv_vector) == getnbasefunctions(ip)*sdim + @test Ferrite.getnbasefunctions(cv) == getnbasefunctions(ip) +end + @testset "bezier values nurbs" begin dim = 2 @@ -50,29 +74,29 @@ end nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) grid = BezierGrid(nurbsmesh) + shape = Ferrite.RefHypercube{dim} - ip = Bernstein{dim, orders}() + ip = Bernstein{shape, orders}() reorder = IGA._bernstein_ordering(ip) - qr = QuadratureRule{dim,RefCube}(3) - qr_face = QuadratureRule{dim-1,RefCube}(3) + qr = QuadratureRule{shape}(1) + qr_face = FaceQuadratureRule{shape}(3) - fv = BezierFaceValues( FaceScalarValues(qr_face, ip) ) - cv = BezierCellValues( CellScalarValues(qr, ip) ) - cv_vector = BezierCellValues( CellVectorValues(qr, ip) ) + fv = BezierFaceValues( qr_face, ip, ip ) + fv_vector = BezierFaceValues( qr_face, ip^dim, ip ) + cv = BezierCellValues( qr, ip, ip ) + cv_vector = BezierCellValues( qr, ip^dim, ip) #Try some different cells for cellnum in [1,4,5] - Xb, wb = get_bezier_coordinates(grid, cellnum) - C = get_extraction_operator(grid, cellnum) - X = get_nurbs_coordinates(grid, cellnum) - w = getweights(grid, cellnum) + Xb, wb, X, w = get_bezier_coordinates(grid, cellnum) + #C = get_extraction_operator(grid, cellnum) + #X = get_nurbs_coordinates(grid, cellnum) + #w = Ferrite.getweights(grid, cellnum) #set_bezier_operator!(cv, w.*C) - bc = BezierCoords(Xb, wb, X, w, C)#getcoordinates(grid, cellnum) + bc = getcoordinates(grid, cellnum) reinit!(cv, bc) - - #set_bezier_operator!(cv_vector, w.*C) reinit!(cv_vector, bc) for (iqp, ξ) in enumerate(qr.points) @@ -98,31 +122,31 @@ end end @test dV_patch ≈ getdetJdV(cv, iqp) - @test sum(cv.cv_store.N[:,iqp]) ≈ 1 - @test all(cv.cv_store.dNdξ[:,iqp] .≈ dRdξ_patch) - @test all(cv.cv_store.dNdx[:,iqp] .≈ dRdX_patch) + @test sum(cv.cv_nurbs.N[:,iqp]) ≈ 1 + @test all(cv.cv_nurbs.dNdξ[:,iqp] .≈ dRdξ_patch) + @test cv.cv_nurbs.dNdx[:,iqp] ≈ dRdX_patch #Check if VectorValues is same as ScalarValues basefunc_count = 1 for i in 1:nb_per_cell for comp in 1:dim N_comp = zeros(Float64, dim) - N_comp[comp] = cv.cv_store.N[i, iqp] + N_comp[comp] = cv.cv_nurbs.N[i, iqp] _N = Vec{dim,Float64}((N_comp...,)) - @test all(cv_vector.cv_store.N[basefunc_count, iqp] .≈ _N) + @test cv_vector.cv_nurbs.N[basefunc_count, iqp] ≈ _N dN_comp = zeros(Float64, dim, dim) - dN_comp[comp, :] = cv.cv_store.dNdξ[i, iqp] + dN_comp[comp, :] = cv.cv_nurbs.dNdξ[i, iqp] _dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) - @test all(cv_vector.cv_store.dNdξ[basefunc_count, iqp] .≈ _dNdξ) + @test cv_vector.cv_nurbs.dNdξ[basefunc_count, iqp] ≈ _dNdξ dN_comp = zeros(Float64, dim, dim) - dN_comp[comp, :] = cv.cv_store.dNdx[i, iqp] + dN_comp[comp, :] = cv.cv_nurbs.dNdx[i, iqp] _dNdx = Tensor{2,dim,Float64}((dN_comp...,)) - @test all(cv_vector.cv_store.dNdx[basefunc_count, iqp] .≈ _dNdx) + @test cv_vector.cv_nurbs.dNdx[basefunc_count, iqp] ≈ _dNdx basefunc_count += 1 end @@ -137,13 +161,16 @@ end Xb, wb = get_bezier_coordinates(grid, cellnum) C = get_extraction_operator(grid, cellnum) X = get_nurbs_coordinates(grid, cellnum) - w = getweights(grid, cellnum) + w = Ferrite.getweights(grid, cellnum) - bc = BezierCoords(Xb, wb, X, w, C.*w) # getcoordinates(grid, cellnum) + bc = getcoordinates(grid, cellnum) reinit!(fv, bc, faceidx) + reinit!(fv_vector, bc, faceidx) + + for iqp in 1:getnquadpoints(fv) - qr_face_side = Ferrite.create_face_quad_rule(qr_face, ip)[faceidx] - for (iqp, ξ) in enumerate(qr_face_side.points) + ξ = qr_face.face_rules[faceidx].points[iqp] + qrw = qr_face.face_rules[faceidx].weights[iqp] #Calculate the value of the NURBS from the nurbs patch N, dNdξ = bspline_values(nurbsmesh, cellnum, ξ, reorder) @@ -158,7 +185,7 @@ end end J = sum(X .⊗ dRdξ_patch) - dV_patch = norm(Ferrite.weighted_normal(J, fv, faceidx))*qr_face_side.weights[iqp] + dV_patch = norm(Ferrite.weighted_normal(J, shape, faceidx))*qrw dRdX_patch = similar(dNdξ) for i in 1:nb_per_cell @@ -166,9 +193,36 @@ end end @test dV_patch ≈ getdetJdV(fv, iqp) - @test sum(fv.cv_store.N[:,iqp, faceidx]) ≈ 1 - @test all(fv.cv_store.dNdξ[:,iqp, faceidx] .≈ dRdξ_patch) - @test all(fv.cv_store.dNdx[:,iqp, faceidx] .≈ dRdX_patch) + @test sum(fv.cv_nurbs.N[:,iqp, faceidx]) ≈ 1 + @test all(fv.cv_nurbs.dNdξ[:,iqp, faceidx] .≈ dRdξ_patch) + @test all(fv.cv_nurbs.dNdx[:,iqp, faceidx] .≈ dRdX_patch) + + #Check if VectorValues is same as ScalarValues + basefunc_count = 1 + for i in 1:nb_per_cell + for comp in 1:dim + N_comp = zeros(Float64, dim) + N_comp[comp] = fv.cv_nurbs.N[i, iqp, faceidx] + _N = Vec{dim,Float64}((N_comp...,)) + + @show fv_vector.cv_nurbs.N[basefunc_count, iqp, faceidx] _N + @test fv_vector.cv_nurbs.N[basefunc_count, iqp, faceidx] ≈ _N + + dN_comp = zeros(Float64, dim, dim) + dN_comp[comp, :] = fv.cv_nurbs.dNdξ[i, iqp, faceidx] + _dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) + + @test fv_vector.cv_nurbs.dNdξ[basefunc_count, iqp, faceidx] ≈ _dNdξ + + dN_comp = zeros(Float64, dim, dim) + dN_comp[comp, :] = fv.cv_nurbs.dNdx[i, iqp, faceidx] + _dNdx = Tensor{2,dim,Float64}((dN_comp...,)) + + @test fv_vector.cv_nurbs.dNdx[basefunc_count, iqp, faceidx] ≈ _dNdx + + basefunc_count += 1 + end + end end end @@ -188,12 +242,12 @@ end nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) grid = BezierGrid(nurbsmesh) - ip = Bernstein{dim, orders}() - qr = QuadratureRule{dim, RefCube}(3) - cv = BezierCellValues( CellScalarValues(qr, ip) ) + ip = Bernstein{Ferrite.RefHypercube{dim}, orders}() + qr = QuadratureRule{Ferrite.RefHypercube{dim}}(3) + cv = BezierCellValues(qr, ip, ip) Xb, wb = get_bezier_coordinates(grid, 1) - w = getweights(grid, 1) + w = Ferrite.getweights(grid, 1) C = get_extraction_operator(grid, 1) set_bezier_operator!(cv, C) From 11722fa04bfa3c4e7877d31aeb740ca164ae6044 Mon Sep 17 00:00:00 2001 From: lijas Date: Mon, 18 Sep 2023 13:39:37 +0200 Subject: [PATCH 07/50] update plate with hole example (wip) --- docs/src/literate/plate_with_hole.jl | 2 +- src/splines/bezier.jl | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl index 415a523..2d2b284 100644 --- a/docs/src/literate/plate_with_hole.jl +++ b/docs/src/literate/plate_with_hole.jl @@ -50,7 +50,7 @@ function integrate_traction_force!(fe::AbstractVector, t::Vec{2}, fv) end; # The assembly loop is also written in almost the same way as in a standard finite element code. The key differences will be described in the next paragraph, -function assemble_problem(dh::MixedDofHandler, grid, cv, fv, stiffmat, traction) +function assemble_problem(dh::DofHandler, grid, cv, fv, stiffmat, traction) f = zeros(ndofs(dh)) K = create_sparsity_pattern(dh) diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index db804ca..cdb5f2f 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -139,11 +139,8 @@ using Symbolics @variables ξ η ζ -ip1d = Bernstein{RefLine, 2}() - -dim = 3 -order = (2,2,2)#,2) -ip = Bernstein{dim,order}() +order = (2,2,2) +ip = Bernstein{RefHexahedron,order}() ordering = IGA._bernstein_ordering(ip) cindex = CartesianIndices(order.+1) From d6f5521f8473ac4453f047183eb953e757db868e Mon Sep 17 00:00:00 2001 From: lijas Date: Tue, 26 Sep 2023 12:34:28 +0200 Subject: [PATCH 08/50] wip new VTK output --- src/IGA.jl | 5 +- src/VTK.jl | 249 +++++++++++++++++++++++++++++++-------------- src/bezier_grid.jl | 2 +- 3 files changed, 176 insertions(+), 80 deletions(-) diff --git a/src/IGA.jl b/src/IGA.jl index 7302022..3976191 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -4,7 +4,8 @@ using Reexport @reexport using Tensors @reexport using Ferrite -using Ferrite: AbstractRefShape, RefHypercube, RefLine, RefQuadrilateral, RefHexahedron, getnbasefunctions +using Ferrite: AbstractRefShape, RefHypercube, RefLine, RefQuadrilateral, RefHexahedron, getnbasefunctions, + VectorInterpolation, VectorizedInterpolation using WriteVTK using LinearAlgebra @@ -90,7 +91,7 @@ include("splines/bezier.jl") include("bezier_extraction.jl") include("splines/bezier_values.jl") include("splines/bsplines.jl") -#include("VTK.jl") +include("VTK.jl") #include("L2_projection.jl") Ferrite._mass_qr(::Bernstein{2, (2, 2)}) = QuadratureRule{RefQuadrilateral}(2+1) diff --git a/src/VTK.jl b/src/VTK.jl index 6e396fb..6dab408 100644 --- a/src/VTK.jl +++ b/src/VTK.jl @@ -2,101 +2,108 @@ function WriteVTK.vtk_grid(filename::AbstractString, grid::BezierGrid{dim,C,T}) where {dim,C,T} - cls = MeshCell[] - beziercoords = Vec{dim,T}[] - weights = T[] + if !isconcretetype(C) + @warn "It appears that you are using a BezierGrid with mixed elements (both IGA elements and FE-elements). + It is not possible to output the IGA elements like normal FE-element, and they will there for not be shown correctly in the output. + Read the docs on how to handle export of IGA-elements to VTK." + end + + vtkfile = vtk_grid(filename, grid.grid) + + #= Can be used for anistropic orders + vtkfile["RationalWeights", WriteVTK.VTKPointData()] = grid.weights cellorders = Int[] - offset = 0 - for (cellid, cell) in enumerate(grid.cells) - reorder = Ferrite_to_vtk_order(typeof(first(grid.cells))) - - vtktype = Ferrite.cell_to_vtkcell(typeof(cell)) + for (cellid, cell) in enumerate(grid.cells) for p in getorders(cell) push!(cellorders, p) end + end + #vtkfile["HigherOrderDegrees", VTKCellData()] = reshape(cellorders, 1, length(grid.cells)) + =# + + return vtkfile +end - xb, wb = get_bezier_coordinates(grid, cellid) - - append!(beziercoords, xb) - append!(weights, wb) - - cellnodes = (1:length(cell.nodes)) .+ offset - - offset += length(cell.nodes) - push!(cls, MeshCell(vtktype, collect(cellnodes[reorder]))) - end +function WriteVTK.vtk_grid(filename::AbstractString, grid::BezierGrid{dim,<:BezierCell,T}) where {dim,T} - coords = reshape(reinterpret(T, beziercoords), (dim, length(beziercoords))) - vtkfile = WriteVTK.vtk_grid(filename, coords, cls, compress = 0)#, append = false) - - vtkfile["RationalWeights", VTKPointData()] = weights - #vtkfile["HigherOrderDegrees", VTKCellData()] = reshape(cellorders, 1, length(grid.cells)) + return vtkfile end -function WriteVTK.vtk_point_data(vtkfile, dh::MixedDofHandler{dim,T,G}, u::Vector, suffix="") where {dim,T,G<:BezierGrid} - C = getcelltype(dh.grid) - @assert isconcretetype(C) - N = Ferrite.nnodes(C) - - fieldnames = Ferrite.getfieldnames(dh) # all primary fields - - for name in fieldnames - @debug println("exporting field $(name)") - field_dim = Ferrite.getfielddim(dh, name) - space_dim = field_dim == 2 ? 3 : field_dim - data = fill(NaN, space_dim, N*getncells(dh.grid)) # set default value - - for fh in dh.fieldhandlers - field_pos = findfirst(i->i == name, Ferrite.getfieldnames(fh)) - field_pos === nothing && continue - - cellnumbers = sort(collect(fh.cellset)) # TODO necessary to have them ordered? - offset = dof_range(fh, name) - - nodecount = 0 - for cellnum in cellnumbers - cell = dh.grid.cells[cellnum] - n = ndofs_per_cell(dh, cellnum) - eldofs = zeros(Int, n) - _celldofs = celldofs!(eldofs, dh, cellnum) - - ub = reinterpret(SVector{field_dim,T}, u[_celldofs[offset]]) - - _distribute_vtk_point_data!(dh.grid.beo[cellnum], data, ub, nodecount) - nodecount += length(cell.nodes) - end - end - vtk_point_data(vtkfile, data, string(name, suffix)) - end +struct VTKIGAFile{VTK<:WriteVTK.DatasetFile} + vtk::VTK + cellset::Vector{Int} +end +function VTKIGAFile(filename::String, grid::BezierGrid, cellset; kwargs...) + vtk = _create_iga_vtk_grid(filename, grid, cellset; kwargs...) + return VTKIGAFile(vtk, cellset) +end - return vtkfile + +# Makes it possible to use the `do`-block syntax +function VTKIGAFile(f::Function, args...; kwargs...) + vtk = VTKIGAFile(args...; kwargs...) + try + f(vtk) + finally + close(vtk) + end end -function _distribute_vtk_point_data!(bezier_extraction, - data::Matrix, - nodevalues::AbstractVector{ <: Union{SymmetricTensor{order,dim,T,M}, - Tensor{order,dim,T,M}, - SVector{M,T}}}, - nodecount::Int) where {order,dim,M,T} - #Transform into values on bezier mesh - ub = compute_bezier_points(bezier_extraction, nodevalues) - - for i in 1:length(nodevalues) - for d in 1:M - data[d, nodecount+i] = ub[i][d] - end - if M == 2 - # paraview requires 3D-data so pad with zero - data[3, nodecount+i] = 0 + +function _create_iga_vtk_grid(filename, grid::BezierGrid{sdim,C,T}, cellset; kwargs...) where {sdim,C,T} + + Ferrite._check_same_cellset(grid, cellset) + + cellset = collect(cellset) + sort!(cellset) + + CT = typeof(grid.cells[first(cellset)]) + nnodes_per_cell = Ferrite.nnodes(CT) + reorder = Ferrite.nodes_to_vtkorder(CT) + + #Variables for the vtk file + cls = MeshCell[] + beziercoords = Vec{dim,T}[] + weights = T[] + cellorders = Int[] + + #Variables for the iterations + bcoords = zeros(Vec{sdim,T}, nnodes_per_cell) + coords = zeros(Vec{sdim,T}, nnodes_per_cell) + wb = zeros(T, nnodes_per_cell) + w = zeros(T, nnodes_per_cell) + + offset = 0 + for (cellid, cell) in enumerate(cellset) + vtktype = Ferrite.cell_to_vtkcell(typeof(cell)) + for p in getorders(cell) + push!(cellorders, p) end - end + get_bezier_coordinates!(bcoords, wb, coords, w, grid, cellid) + + append!(beziercoords, bcoords) + append!(weights, wb) + + cellnodes = (1:length(cell.nodes)) .+ offset + + push!(cls, WriteVTK.MeshCell(vtktype, collect(cellnodes[reorder]))) + offset += length(cell.nodes) + end + + coords = reshape(reinterpret(T, beziercoords), (sdim, length(beziercoords))) + vtkfile = WriteVTK.vtk_grid(filename, coords, cls; kwargs...) + vtkfile["RationalWeights", WriteVTK.VTKPointData()] = weights + vtkfile["HigherOrderDegrees", WriteVTK.VTKCellData()] = reshape(cellorders, 1, length(grid.cells)) + + return vtkfile end + function WriteVTK.vtk_point_data( vtkfile::WriteVTK.DatasetFile, cpvalues::Vector{<:Union{SymmetricTensor{order,dim,T,M}, @@ -121,4 +128,92 @@ function WriteVTK.vtk_point_data( vtk_point_data(vtkfile, data, name) return vtkfile -end \ No newline at end of file +end + + +function _evaluate_at_geometry_nodes!( + vtk ::VTKIGAFile, + dh ::Ferrite.AbstractDofHandler, + a ::Vector, + fieldname ::Symbol) + + Ferrite._check_same_cellset(dh.grid, vtk.cellset) + fieldname ∈ Ferrite.getfieldnames(dh) || error("Field $fieldname not found in the dofhandler.") + + # + local sdh + for _sdh in dh.subdofhandlers + if first(vtk.cellset) in _sdh.cellset + sdh = _sdh + @assert Set{Int}(vtk.cellset) == _sdh.cellset + break + end + end + + # + CT = getcelltype(dh.grid, first(sdh.cellset)) + + field_dim = Ferrite.getfielddim(sdh, fieldname) + ncomponents = field_dim == 2 ? 3 : field_dim + + nviznodes = vtk.vtk.Npts + data = fill(Float64(NaN), ncomponents, nviznodes) + + field_idx = Ferrite.find_field(sdh, fieldname) + field_idx === nothing && error("The field $fieldname does not exist in the subdofhandler") + ip_geo = Ferrite.default_interpolation(CT) + ip = Ferrite.getfieldinterpolation(sdh, field_idx) + drange = Ferrite.dof_range(sdh, fieldname) + shape = Ferrite.getrefshape(ip_geo) + + # + local_node_coords = Ferrite.reference_coordinates(ip_geo) + qr = QuadratureRule{shape}(zeros(length(local_node_coords)), local_node_coords) + ip = Ferrite.getfieldinterpolation(sdh, field_idx) + if ip isa VectorizedInterpolation + # TODO: Remove this hack when embedding works... + cv = BezierCellValues(qr, ip.ip, ip_geo) + else + cv = BezierCellValues(qr, ip, ip_geo) + end + + _evaluate_at_geometry_nodes!(data, sdh, a, cv, drange, vtk.cellset) + + return data +end + + +function _evaluate_at_geometry_nodes!(data, dh, a, cv, drange, cellset) + + n_eval_points = Ferrite.getngeobasefunctions(cv) + ae = zeros(eltype(a), n_eval_points) + bcoords = getcoordinates(dh.grid, first(cellset)) + dofs = zeros(Int, getnbasefunctions(cv)) + offset = 0 + for cellid in cellset + + getcoordinates!(bcoords, dh.grid, cellid) + reinit!(cv, bcoords) + + celldofs!(dofs, dh, cellid) + for (i, I) in pairs(drange) + ae[i] = a[dofs[I]] + end + + cellnodes = (1:n_eval_points) .+ offset + + for (iqp, nodeid) in pairs(cellnodes) + val = function_value(cv, iqp, ae) + if data isa Matrix # VTK + data[1:length(val), nodeid] .= val + data[(length(val)+1):end, nodeid] .= 0 # purge the NaN + else + data[nodeid] = val + end + end + + offset += n_eval_points + end + + return data +end diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 929b0b1..11da433 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -51,7 +51,7 @@ function BezierGrid(grid::Ferrite.Grid{dim,C,T}) where {dim,C,T} push!(extraction_operator, beo) end - return BezierGrid{dim,C,T}(grid, weights, extraction_operator) + return BezierGrid{dim,C,T}(grid, weights, extraction_operator, facesets = grid.facesets, nodesets = grid.nodesets, edgesets = grid.edgesets, vertexsets = grid.vertexsets) end function Base.getproperty(m::BezierGrid, s::Symbol) From e685768073bb464a8f1452adc696bce721bbbc99 Mon Sep 17 00:00:00 2001 From: lijas Date: Fri, 13 Oct 2023 17:19:47 +0200 Subject: [PATCH 09/50] wip --- docs/src/literate/plate_with_hole.jl | 9 +- src/IGA.jl | 51 +++++++++-- src/L2_projection.jl | 3 +- src/VTK.jl | 118 ++++++++++++++----------- src/bezier_grid.jl | 12 --- src/mesh_generation.jl | 27 ++++-- src/splines/bezier.jl | 18 ++-- src/splines/bezier_values.jl | 39 +++++++++ test/test_l2proj.jl | 123 +++++++++++++++++++++++++++ test/test_values.jl | 21 +++-- test_script.jl | 80 +++++++++++++++++ 11 files changed, 409 insertions(+), 92 deletions(-) create mode 100644 test/test_l2proj.jl create mode 100644 test_script.jl diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl index 2d2b284..2b6966e 100644 --- a/docs/src/literate/plate_with_hole.jl +++ b/docs/src/literate/plate_with_hole.jl @@ -163,19 +163,22 @@ function solve() # Create the cellvalues storing the shape function values. Note that the `CellVectorValues`/`FaceVectorValues` are wrapped in a `BezierValues`. It is in the # reinit-function of the `BezierValues` that the actual bezier transformation of the shape values is performed. - ip_geo = Bernstein{RefQuadrilateral,2}() + ip_geo = IGAInterpolation{RefQuadrilateral,2}() ip_u = ip_geo^2 qr_cell = QuadratureRule{RefQuadrilateral}(4) qr_face = FaceQuadratureRule{RefQuadrilateral}(3) - cv = BezierCellValues(qr_cell, ip_u, ip_geo) - fv = BezierFaceValues(qr_face, ip_u, ip_geo) + cv = CellValues(qr_cell, ip_u, ip_geo) + fv = FaceValues(qr_face, ip_u, ip_geo) # Distribute dofs as normal dh = DofHandler(grid) push!(dh, :u, ip_u) close!(dh) + ae = zeros(ndofs(dh)) + apply_analytical!(ae, dh, :u, x->x) + # Add two symmetry boundary condintions. Bottom face should only be able to move in x-direction, and the right boundary should only be able to move in y-direction ch = ConstraintHandler(dh) dbc1 = Dirichlet(:u, getfaceset(grid, "bot"), (x, t) -> 0.0, 2) diff --git a/src/IGA.jl b/src/IGA.jl index 3976191..9ab551b 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -12,9 +12,11 @@ using LinearAlgebra using StaticArrays import SparseArrays +export IGAInterpolation export BezierExtractionOperator export BezierCell export BezierCoords +export VTKIGAFile const Optional{T} = Union{T, Nothing} const BezierExtractionOperator{T} = Vector{SparseArrays.SparseVector{T,Int}} @@ -82,7 +84,8 @@ end Ferrite.default_interpolation(::Type{<:BezierCell{order, shape}}) where {order, shape} = IGAInterpolation{shape, order}() -#getorders(::BezierCell{orders,refshape,N}) where {orders,refshape,N} = orders +Ferrite.nnodes(::BezierCell{order, shape, N}) where {order, shape, N} = N +getorders(::BezierCell{orders,refshape,N}) where {orders,refshape,N} = orders include("nurbsmesh.jl") include("mesh_generation.jl") @@ -94,7 +97,9 @@ include("splines/bsplines.jl") include("VTK.jl") #include("L2_projection.jl") -Ferrite._mass_qr(::Bernstein{2, (2, 2)}) = QuadratureRule{RefQuadrilateral}(2+1) +Ferrite._mass_qr(::IGAInterpolation{shape,order}) where {shape,order}= Ferrite._mass_qr(Bernstein{shape, order}()) +Ferrite._mass_qr(::Bernstein{RefQuadrilateral, (2,2)}) = QuadratureRule{RefQuadrilateral}(2+1) +Ferrite._mass_qr(::Bernstein{RefCube, (2,2,2)}) = QuadratureRule{RefCube}(2+1) #Normaly the verices function should only return the 8 corner nodes of the hexa (or 4 in 2d), #but since the cell connectivity in IGA is different compared to normal FE elements, @@ -111,14 +116,42 @@ function Ferrite.edges(c::BezierCell{order,RefHexahedron}) where {order} return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_edgedof_indices(IGAInterpolation{RefHexahedron,order}() ))) end -function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefHexahedron}}) where {order} - return Ferrite.VTKCellTypes.VTK_BEZIER_HEXAHEDRON -end -function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefQuadrilateral}}) where {order} - return Ferrite.VTKCellTypes.VTK_BEZIER_QUADRILATERAL + +function Ferrite._apply_analytical!( + a::AbstractVector, dh::Ferrite.AbstractDofHandler, celldofinds, field_dim, + ip_fun::Interpolation{RefShape}, ip_geo::Interpolation, f::Function, cellset) where {dim, RefShape<:AbstractRefShape{dim}} + + coords = getcoordinates(Ferrite.get_grid(dh), first(cellset)) + ref_points = Ferrite.reference_coordinates(ip_fun) + dummy_weights = zeros(length(ref_points)) + qr = QuadratureRule{RefShape}(dummy_weights, ref_points) + # Note: Passing ip_geo as the function interpolation here, it is just a dummy. + cv = CellValues(qr, ip_geo, ip_geo) + c_dofs = celldofs(dh, first(cellset)) + f_dofs = zeros(Int, length(celldofinds)) + + # Check f before looping + #length(f(first(coords))) == field_dim || error("length(f(x)) must be equal to dimension of the field ($field_dim)") + + for cellnr in cellset + getcoordinates!(coords, Ferrite.get_grid(dh), cellnr) + celldofs!(c_dofs, dh, cellnr) + for (i, celldofind) in enumerate(celldofinds) + f_dofs[i] = c_dofs[celldofind] + end + _apply_analytical2!(a, f_dofs, coords, field_dim, cv, f) + end + return a end -function Ferrite.cell_to_vtkcell(::Type{BezierCell{order,RefLine}}) where {order} - return Ferrite.VTKCellTypes.VTK_BEZIER_CURVE + +function _apply_analytical2!(a::AbstractVector, dofs::Vector{Int}, coords, field_dim, cv::Ferrite.AbstractCellValues, f) + for i_dof in 1:getnquadpoints(cv) + x_dof = spatial_coordinate(cv, i_dof, coords) + for (idim, icval) in enumerate(f(x_dof)) + a[dofs[field_dim*(i_dof-1)+idim]] = icval + end + end + return a end end #end module \ No newline at end of file diff --git a/src/L2_projection.jl b/src/L2_projection.jl index 4a925a4..fe9ebe7 100644 --- a/src/L2_projection.jl +++ b/src/L2_projection.jl @@ -1,5 +1,6 @@ +#= function Ferrite.L2Projector( func_ip::Interpolation, grid::BezierGrid; @@ -57,4 +58,4 @@ function igaproject(proj::L2Projector, else return projected_vals end -end \ No newline at end of file +end=# \ No newline at end of file diff --git a/src/VTK.jl b/src/VTK.jl index 6dab408..bc7e38b 100644 --- a/src/VTK.jl +++ b/src/VTK.jl @@ -1,34 +1,28 @@ -# Use the vtk bezier cells for the outputs. -function WriteVTK.vtk_grid(filename::AbstractString, grid::BezierGrid{dim,C,T}) where {dim,C,T} - - if !isconcretetype(C) - @warn "It appears that you are using a BezierGrid with mixed elements (both IGA elements and FE-elements). - It is not possible to output the IGA elements like normal FE-element, and they will there for not be shown correctly in the output. - Read the docs on how to handle export of IGA-elements to VTK." - end +function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{order,RefHexahedron}}) where {order} + return Ferrite.VTKCellTypes.VTK_BEZIER_HEXAHEDRON +end +function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{order,RefQuadrilateral}}) where {order} + return Ferrite.VTKCellTypes.VTK_BEZIER_QUADRILATERAL +end +function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{order,RefLine}}) where {order} + return Ferrite.VTKCellTypes.VTK_BEZIER_CURVE +end - vtkfile = vtk_grid(filename, grid.grid) - - #= Can be used for anistropic orders - vtkfile["RationalWeights", WriteVTK.VTKPointData()] = grid.weights - cellorders = Int[] - for (cellid, cell) in enumerate(grid.cells) - for p in getorders(cell) - push!(cellorders, p) +# Store the Ferrite to vtk order in a cache for specific cell type +let cache = Dict{Type{<:BezierCell}, Vector{Int}}() + global function _iga_to_vtkorder(celltype::Type{<:BezierCell{orders,shape,N}}) where {orders,shape,N} + get!(cache, celltype) do + if shape == RefHexahedron + igaorder = _bernstein_ordering(celltype) + vtkorder = _vtk_ordering(celltype) + + return [findfirst(ivtk-> ivtk == iiga, vtkorder) for iiga in igaorder] + else + return collect(1:N) + end end end - #vtkfile["HigherOrderDegrees", VTKCellData()] = reshape(cellorders, 1, length(grid.cells)) - =# - - return vtkfile -end - -function WriteVTK.vtk_grid(filename::AbstractString, grid::BezierGrid{dim,<:BezierCell,T}) where {dim,T} - - - - return vtkfile end struct VTKIGAFile{VTK<:WriteVTK.DatasetFile} @@ -38,9 +32,17 @@ end function VTKIGAFile(filename::String, grid::BezierGrid, cellset; kwargs...) vtk = _create_iga_vtk_grid(filename, grid, cellset; kwargs...) + cellset = sort(collect(copy(cellset))) return VTKIGAFile(vtk, cellset) end +Base.close(vtk::VTKIGAFile) = WriteVTK.vtk_save(vtk.vtk) + +function Base.show(io::IO, ::MIME"text/plain", vtk::VTKIGAFile) + open_str = WriteVTK.isopen(vtk.vtk) ? "open" : "closed" + filename = vtk.vtk.path + print(io, "VTKFile for the $open_str file \"$(filename)\".") +end # Makes it possible to use the `do`-block syntax function VTKIGAFile(f::Function, args...; kwargs...) @@ -52,22 +54,19 @@ function VTKIGAFile(f::Function, args...; kwargs...) end end - - function _create_iga_vtk_grid(filename, grid::BezierGrid{sdim,C,T}, cellset; kwargs...) where {sdim,C,T} - - Ferrite._check_same_cellset(grid, cellset) + Ferrite._check_same_celltype(grid, cellset) cellset = collect(cellset) sort!(cellset) - CT = typeof(grid.cells[first(cellset)]) - nnodes_per_cell = Ferrite.nnodes(CT) - reorder = Ferrite.nodes_to_vtkorder(CT) + cell = grid.cells[first(cellset)] + reorder = _iga_to_vtkorder(typeof(cell)) + nnodes_per_cell = Ferrite.nnodes(cell) #Variables for the vtk file cls = MeshCell[] - beziercoords = Vec{dim,T}[] + beziercoords = Vec{sdim,T}[] weights = T[] cellorders = Int[] @@ -78,7 +77,9 @@ function _create_iga_vtk_grid(filename, grid::BezierGrid{sdim,C,T}, cellset; kwa w = zeros(T, nnodes_per_cell) offset = 0 - for (cellid, cell) in enumerate(cellset) + for cellid in cellset + cell = grid.cells[cellid] + vtktype = Ferrite.cell_to_vtkcell(typeof(cell)) for p in getorders(cell) push!(cellorders, p) @@ -98,7 +99,7 @@ function _create_iga_vtk_grid(filename, grid::BezierGrid{sdim,C,T}, cellset; kwa coords = reshape(reinterpret(T, beziercoords), (sdim, length(beziercoords))) vtkfile = WriteVTK.vtk_grid(filename, coords, cls; kwargs...) vtkfile["RationalWeights", WriteVTK.VTKPointData()] = weights - vtkfile["HigherOrderDegrees", WriteVTK.VTKCellData()] = reshape(cellorders, 1, length(grid.cells)) + #vtkfile["HigherOrderDegrees", WriteVTK.VTKCellData()] = reshape(cellorders, 1, length(grid.cells)) return vtkfile end @@ -130,14 +131,21 @@ function WriteVTK.vtk_point_data( return vtkfile end +function write_solution(vtk, dh::DofHandler, a, suffix="") + for fieldname in Ferrite.getfieldnames(dh) + data = _evaluate_at_geometry_nodes!(vtk, dh, a, fieldname) + vtk_point_data(vtk.vtk, data, string(fieldname, suffix)) + end +end + function _evaluate_at_geometry_nodes!( vtk ::VTKIGAFile, dh ::Ferrite.AbstractDofHandler, - a ::Vector, - fieldname ::Symbol) + a ::Vector{T}, + fieldname ::Symbol) where T - Ferrite._check_same_cellset(dh.grid, vtk.cellset) + Ferrite._check_same_celltype(dh.grid, vtk.cellset) fieldname ∈ Ferrite.getfieldnames(dh) || error("Field $fieldname not found in the dofhandler.") # @@ -170,40 +178,54 @@ function _evaluate_at_geometry_nodes!( local_node_coords = Ferrite.reference_coordinates(ip_geo) qr = QuadratureRule{shape}(zeros(length(local_node_coords)), local_node_coords) ip = Ferrite.getfieldinterpolation(sdh, field_idx) + + # TODO: Remove this hack when embedding works... + RT = ip isa ScalarInterpolation ? T : Vec{Ferrite.n_components(ip),T} if ip isa VectorizedInterpolation - # TODO: Remove this hack when embedding works... cv = BezierCellValues(qr, ip.ip, ip_geo) else cv = BezierCellValues(qr, ip, ip_geo) end - _evaluate_at_geometry_nodes!(data, sdh, a, cv, drange, vtk.cellset) + _evaluate_at_geometry_nodes!(data, sdh, a, cv, drange, vtk.cellset, RT) return data end -function _evaluate_at_geometry_nodes!(data, dh, a, cv, drange, cellset) +function _evaluate_at_geometry_nodes!(data, sdh, a::Vector{T}, cv, drange, cellset, ::Type{RT}) where {T, RT} + + dh = sdh.dh + grid = dh.grid n_eval_points = Ferrite.getngeobasefunctions(cv) - ae = zeros(eltype(a), n_eval_points) + ncelldofs = length(drange) + ue = zeros(eltype(a), ncelldofs) + + # TODO: Remove this hack when embedding works... + if RT <: Vec && cv isa BezierCellValues{T, <:CellValues{<:ScalarInterpolation}} + uer = reinterpret(RT, ue) + else + uer = ue + end + + dofs = zeros(Int, ncelldofs) bcoords = getcoordinates(dh.grid, first(cellset)) - dofs = zeros(Int, getnbasefunctions(cv)) offset = 0 for cellid in cellset getcoordinates!(bcoords, dh.grid, cellid) reinit!(cv, bcoords) - celldofs!(dofs, dh, cellid) + celldofs!(dofs, sdh, cellid) for (i, I) in pairs(drange) - ae[i] = a[dofs[I]] + ue[i] = a[dofs[I]] end cellnodes = (1:n_eval_points) .+ offset for (iqp, nodeid) in pairs(cellnodes) - val = function_value(cv, iqp, ae) + val = function_value(cv, iqp, uer) if data isa Matrix # VTK data[1:length(val), nodeid] .= val data[(length(val)+1):end, nodeid] .= 0 # purge the NaN diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 11da433..1623b57 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -178,15 +178,3 @@ end function get_extraction_operator(grid::BezierGrid, cellid::Int) return grid.beo[cellid] end - -# Store the Ferrite to vtk order in a cache for specific cell type -let cache = Dict{Type{<:BezierCell}, Vector{Int}}() - global function Ferrite.nodes_to_vtkorder(celltype::Type{<:BezierCell}) - get!(cache, celltype) do - igaorder = _bernstein_ordering(celltype) - vtkorder = _vtk_ordering(celltype) - - return [findfirst(ivtk-> ivtk == iiga, vtkorder) for iiga in igaorder] - end - end -end \ No newline at end of file diff --git a/src/mesh_generation.jl b/src/mesh_generation.jl index 871c4fd..f7c2a28 100644 --- a/src/mesh_generation.jl +++ b/src/mesh_generation.jl @@ -39,11 +39,28 @@ function _generate_equidistant_parametrization(knot_vector::Vector{T}, order::In return range(from, stop=to, length=length(knot_vector)-1-order) end +function Ferrite.generate_grid(::Type{<:BezierCell{order,refshape}}, nels::Tuple{Int,Int}, left::Vec{sdim,T}=zero(Vec{sdim}), right::Vec{sdim,T}=one(Vec{sdim})) where {T,order,sdim,refshape<:RefHypercube{sdim}} + patch = generate_nurbs_patch(:rectangle, nels, ntuple(i->order,sdim); cornerpos = tuple(left), size = tuple(right-left)) + return BezierGrid(patch) +end + function generate_nurbs_patch(s::Symbol, args...; kwargs...) generate_nurbs_patch(Val{s}(), args...; kwargs...) end +function generate_nurbs_patch(::Val{:line}, nel::NTuple{1,Int}, orders::NTuple{1,Int}, size::NTuple{1,T}; cornerpos::NTuple{1,T} = (0.0,), multiplicity::NTuple{1,Int}=(1,), sdim::Int=1) where T + generate_nurbs_patch(:hypercube, nel, orders; cornerpos, size, multiplicity, sdim) +end + +function generate_nurbs_patch(::Val{:rectangle}, nel::NTuple{2,Int}, orders::NTuple{2,Int}; cornerpos::NTuple{2,T} = (0.0,0.0), size::NTuple{2,T}, multiplicity::NTuple{2,Int}=(1,1), sdim::Int=2) where T + generate_nurbs_patch(:hypercube, nel, orders; cornerpos, size, multiplicity, sdim=sdim) +end + function generate_nurbs_patch(::Val{:cube}, nel::NTuple{3,Int}, orders::NTuple{3,Int}; cornerpos::NTuple{3,T} = (-1.0,-1.0,-1.0), size::NTuple{3,T} = (2.0,2.0,2.0), multiplicity::NTuple{3,Int}=(1,1,1)) where T + generate_nurbs_patch(:hypercube, nel, orders; cornerpos, size, multiplicity) +end + +function generate_nurbs_patch(::Val{:hypercube}, nel::NTuple{3,Int}, orders::NTuple{3,Int}; cornerpos::NTuple{3,T} = (-1.0,-1.0,-1.0), size::NTuple{3,T} = (2.0,2.0,2.0), multiplicity::NTuple{3,Int}=(1,1,1)) where T pdim = 3 sdim = 3 @@ -70,10 +87,7 @@ function generate_nurbs_patch(::Val{:cube}, nel::NTuple{3,Int}, orders::NTuple{3 return IGA.NURBSMesh(Tuple(knot_vectors), orders, control_points) end -function generate_nurbs_patch(::Val{:rectangle}, nel::NTuple{2,Int}, orders::NTuple{2,Int}; cornerpos::NTuple{2,T} = (0.0,0.0), size::NTuple{2,T}, multiplicity::NTuple{2,Int}=(1,1), sdim::Int=2) where T - generate_nurbs_patch(:cube, nel, orders; cornerpos=cornerpos, size=size, multiplicity=multiplicity, sdim=sdim) -end -function generate_nurbs_patch(::Val{:cube}, nel::NTuple{2,Int}, orders::NTuple{2,Int}; cornerpos::NTuple{2,T} = (0.0,0.0), size::NTuple{2,T}, multiplicity::NTuple{2,Int}=(1,1), sdim::Int=2) where T +function generate_nurbs_patch(::Val{:hypercube}, nel::NTuple{2,Int}, orders::NTuple{2,Int}; cornerpos::NTuple{2,T} = (0.0,0.0), size::NTuple{2,T}, multiplicity::NTuple{2,Int}=(1,1), sdim::Int=2) where T @assert( all(orders .>= multiplicity) ) @@ -105,10 +119,7 @@ function generate_nurbs_patch(::Val{:cube}, nel::NTuple{2,Int}, orders::NTuple{2 end -function generate_nurbs_patch(::Val{:line}, nel::NTuple{1,Int}, orders::NTuple{1,Int}, size::NTuple{1,T}; cornerpos::NTuple{1,T} = (0.0,), multiplicity::NTuple{1,Int}=(1,), sdim::Int=1) where T - generate_nurbs_patch(:cube, nel, orders; cornerpos=cornerpos, size=size, multiplicity=multiplicity, sdim=sdim) -end -function generate_nurbs_patch(::Val{:cube}, nel::NTuple{1,Int}, orders::NTuple{1,Int}; cornerpos::NTuple{1,T} = (0.0,), size::NTuple{1,T}, multiplicity::NTuple{1,Int}=(1,), sdim::Int=1) where T +function generate_nurbs_patch(::Val{:hypercube}, nel::NTuple{1,Int}, orders::NTuple{1,Int}; cornerpos::NTuple{1,T} = (0.0,), size::NTuple{1,T}, multiplicity::NTuple{1,Int}=(1,), sdim::Int=1) where T @assert( all(orders .>= multiplicity) ) diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index cdb5f2f..d833218 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -46,7 +46,7 @@ end # Bernstein Quadrilateral, order 2 # # # Ferrite.getnbasefunctions(::Bernstein{RefQuadrilateral,(2,2)}) = 9 - +Ferrite.vertexdof_indices(::Bernstein{RefQuadrilateral,(2,2)}) = ((1,),(2,),(3,),(4,)) Ferrite.facedof_indices(::Bernstein{RefQuadrilateral,(2,2)}) = ((1,2, 5), (2,3, 6), (3,4, 7), (4,1, 8)) Ferrite.facedof_interior_indices(::Bernstein{RefQuadrilateral,(2,2)}) = ((5,), (6,), (7,), (8,)) Ferrite.celldof_interior_indices(::Bernstein{RefQuadrilateral,(2,2)}) = (9,) @@ -261,10 +261,10 @@ end # # # Ferrite.getnbasefunctions(::Bernstein{shape,orders}) where {shape, orders}= prod(orders.+1) -function Ferrite.reference_coordinates(ip::Bernstein{shape,order}) where {rdim, shape <: AbstractRefShape{rdim}, order <: Integer} +function Ferrite.reference_coordinates(ip::Bernstein{shape,orders}) where {rdim, shape <: AbstractRefShape{rdim}, orders} T = Float64 - nbasefunks_dim = ntuple(i->order, rdim) + nbasefunks_dim = orders .+ 1 nbasefuncs = prod(nbasefunks_dim) coords = Vec{rdim,T}[] @@ -374,7 +374,15 @@ function _bernstein_ordering(::Bernstein{RefHexahedron, orders}) where {orders} end #Almost the same orderign as _bernstein_ordering, but some changes for faces and edges -function _vtk_ordering(::BezierCell{orders}) where {orders} +function _vtk_ordering(c::BezierCell{RefLine}) + _bernstein_ordering(c) +end + +function _vtk_ordering(c::BezierCell{RefQuadrilateral}) + _bernstein_ordering(c) +end + +function _vtk_ordering(::BezierCell{RefHexahedron, orders}) where {orders} @assert(length(orders) == 3) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) @@ -419,4 +427,4 @@ function _vtk_ordering(::BezierCell{orders}) where {orders} append!(ordering, ind[2:end-1, 2:end-1, 2:end-1][:]) return ordering -end \ No newline at end of file +end diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 745dbed..47a7693 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -62,6 +62,21 @@ function Ferrite.function_symmetric_gradient(bv::IGA.BezierValues, q_point::Int, return function_symmetric_gradient(bv.cv_store, q_point, u, args...) end +#Entrypoints for vector valued IGAInterpolation, which creates BezierCellValues +function Ferrite.CellValues(qr::QuadratureRule, ::IP, ::IGAInterpolation{shape,order}) where {shape,order,vdim,IP<:VectorizedInterpolation{vdim, shape, <:Any, <:IGAInterpolation{shape,order}}} + _ip = Bernstein{shape,order}()^vdim + _ip_geo = Bernstein{shape,order}() + cv = CellValues(qr, _ip, _ip_geo) + return BezierCellValues(cv) +end + +function Ferrite.CellValues(qr::QuadratureRule, ::IGAInterpolation{shape,order}, ::IGAInterpolation{shape,order}) where {shape,order} + _ip = Bernstein{shape,order}() + _ip_geo = Bernstein{shape,order}() + cv = CellValues(qr, _ip, _ip_geo) + return BezierCellValues(cv) +end + function BezierFaceValues(qr::FaceQuadratureRule, ip::Interpolation, gip::Interpolation) return BezierFaceValues(Float64, qr, ip, gip) end @@ -103,6 +118,17 @@ function set_bezier_operator!(bcv::BezierCellAndFaceValues, beo::BezierExtractio bcv.current_beo[]=beo end +#This function can be called when we know that the weights are all equal to one. +function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, xb::Vector{<:Vec}) + x = spatial_coordinate(cv.cv_bezier, iqp, xb) + return x +end + +function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, bcoords::BezierCoords) + x = spatial_coordinate(cv, iqp, (bcoords.xb, bcoords.wb)) + return x +end + function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, (xb, wb)::CoordsAndWeight{sdim,T}) where {sdim,T} nbasefunks = Ferrite.getngeobasefunctions(cv) @boundscheck Ferrite.checkquadpoint(cv.cv_bezier, iqp) @@ -273,4 +299,17 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.Abs cv_nurbs.dNdx[j, i, cb] = cv_nurbs.dNdξ[j, i, cb] ⋅ Jinv end end +end + +function Base.show(io::IO, m::MIME"text/plain", fv::BezierFaceValues) + println(io, "FaceValues with") + nqp = getnquadpoints.(fv.cv_bezier.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.cv_bezier.func_interp) + println(io) + print(io, "- Geometric interpolation: "); show(io, m, fv.cv_bezier.geo_interp) end \ No newline at end of file diff --git a/test/test_l2proj.jl b/test/test_l2proj.jl new file mode 100644 index 0000000..923bed1 --- /dev/null +++ b/test/test_l2proj.jl @@ -0,0 +1,123 @@ + +@testset "bezier values construction" begin + + + +end + + +function test_projection() + element = BezierCell{2,RefQuadrilateral} + grid = generate_grid(Quadrilateral, (1,1), Vec((0.,0.)), Vec((1.,1.))) + + # grid = BezierGrid( generate_nurbs_patch(:rectangle, (1,1), (2,2); size=(1.0,1.0)) ) + + ip = Bernstein{RefQuadrilateral, 2}() + ip_geom = Lagrange{RefQuadrilateral, 1}() + qr = Ferrite._mass_qr(ip) + cv = CellValues(qr, ip, ip_geom) + + # Create node values for the cell + f(x) = 1 + x[1]^2 + (2x[2])^2 + + # Nodal approximations for this simple grid when using linear interpolation + f_approx(i) = [0.1666666666666664, 1.166666666666667, 5.166666666666667, 4.166666666666666][i] + + # analytical values + function analytical(f) + qp_values = [] + for cellid in 1:getncells(grid) + coords = getcoordinates(grid, cellid) + reinit!(cv, coords) + r = [f(spatial_coordinate(cv, qp, coords)) for qp in 1:getnquadpoints(cv)] + push!(qp_values, r) + end + return identity.(qp_values) # Tighten the type + end + + qp_values = analytical(f) + + # Now recover the nodal values using a L2 projection. + proj = L2Projector(ip, grid; geom_ip=ip_geom) + point_vars = project(proj, qp_values, qr) + qp_values_matrix = reduce(hcat, qp_values) + point_vars_2 = project(proj, qp_values_matrix, qr) + if order == 1 + # A linear approximation can not recover a quadratic solution, + # so projected values will be different from the analytical ones + ae = [f_approx(i) for i in 1:4] + elseif order == 2 + # For a quadratic approximation the analytical solution is recovered + ae = zeros(length(point_vars)) + apply_analytical!(ae, proj.dh, :_, f) + end + + + for cellid in 1:getncells(grid) + coords = getcoordinates(grid, cellid) + reinit!(cv, coords) + for qp in 1:getnquadpoints(cv) + x = spatial_coordinate(cv, qp, coords) + u_exact = f(x) + u_proj = function_value(cv, qp, point_vars) + @show u_proj ≈ u_exact + end + end + + @test point_vars ≈ point_vars_2 ≈ ae + + # Vec + f_vector(x) = Vec{1,Float64}((f(x),)) + qp_values = analytical(f_vector) + point_vars = project(proj, qp_values, qr) + if order == 1 + ae = [Vec{1,Float64}((f_approx(j),)) for j in 1:4] + elseif order == 2 + ae = zeros(length(point_vars)) + apply_analytical!(ae, proj.dh, :_, x -> f_vector(x)[1]) + ae = reinterpret(Vec{1,Float64}, ae) + end + @test point_vars ≈ ae + + # Tensor + f_tensor(x) = Tensor{2,2,Float64}((f(x),2*f(x),3*f(x),4*f(x))) + qp_values = analytical(f_tensor) + qp_values_matrix = reduce(hcat, qp_values)::Matrix + point_vars = project(proj, qp_values, qr) + point_vars_2 = project(proj, qp_values_matrix, qr) + if order == 1 + ae = [Tensor{2,2,Float64}((f_approx(i),2*f_approx(i),3*f_approx(i),4*f_approx(i))) for i in 1:4] + elseif order == 2 + ae = zeros(4, length(point_vars)) + for i in 1:4 + apply_analytical!(@view(ae[i, :]), proj.dh, :_, x -> f_tensor(x)[i]) + end + ae = reinterpret(reshape, Tensor{2,2,Float64,4}, ae) + end + @test point_vars ≈ point_vars_2 ≈ ae + + # SymmetricTensor + f_stensor(x) = SymmetricTensor{2,2,Float64}((f(x),2*f(x),3*f(x))) + qp_values = analytical(f_stensor) + qp_values_matrix = reduce(hcat, qp_values) + point_vars = project(proj, qp_values, qr) + point_vars_2 = project(proj, qp_values_matrix, qr) + if order == 1 + ae = [SymmetricTensor{2,2,Float64}((f_approx(i),2*f_approx(i),3*f_approx(i))) for i in 1:4] + elseif order == 2 + ae = zeros(3, length(point_vars)) + for i in 1:3 + apply_analytical!(@view(ae[i, :]), proj.dh, :_, x -> f_stensor(x).data[i]) + end + ae = reinterpret(reshape, SymmetricTensor{2,2,Float64,3}, ae) + end + @test point_vars ≈ point_vars_2 ≈ ae + + # Test error-path with bad qr + if refshape == RefTriangle && order == 2 + bad_order = 2 + else + bad_order = 1 + end + @test_throws LinearAlgebra.PosDefException L2Projector(ip, grid; qr_lhs=QuadratureRule{refshape}(bad_order), geom_ip=ip_geom) +end \ No newline at end of file diff --git a/test/test_values.jl b/test/test_values.jl index af7d787..c0129df 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -41,23 +41,32 @@ end sdim = 3 shape = Ferrite.RefHypercube{sdim} - ip = Bernstein{shape, 2}() + ip = IGAInterpolation{shape, 2}() + bip = Bernstein{shape, 2}() qr = QuadratureRule{shape}(1) qr_face = FaceQuadratureRule{shape}(1) cv = BezierCellValues( qr, ip, ip) - cv2 = BezierCellValues( CellValues(qr, ip, ip) ) + cv2 = BezierCellValues( CellValues(qr, bip, bip) ) + cv3 = CellValues( qr, ip, ip) + @test cv.cv_bezier.M == cv2.cv_bezier.M + @test cv.cv_bezier.M == cv3.cv_bezier.M + @test cv3 isa BezierCellValues + + cv_vector1 = BezierCellValues( qr, ip^sdim, ip ) + cv_vector2 = BezierCellValues( CellValues(qr, bip^sdim, bip) ) + cv_vector3 = CellValues( qr, ip^sdim, ip ) - cv_vector = BezierCellValues( qr, ip^sdim, ip ) - cv_vector2 = BezierCellValues( CellValues(qr, ip^sdim, ip) ) @test cv_vector.cv_bezier.M == cv_vector2.cv_bezier.M + @test cv_vector.cv_bezier.M == cv_vector3.cv_bezier.M + @test cv_vector3 isa BezierCellValues - @test Ferrite.getngeobasefunctions(cv_vector) == getnbasefunctions(ip) + @test Ferrite.getngeobasefunctions(cv_vector1) == getnbasefunctions(ip) @test Ferrite.getngeobasefunctions(cv) == getnbasefunctions(ip) - @test Ferrite.getnbasefunctions(cv_vector) == getnbasefunctions(ip)*sdim + @test Ferrite.getnbasefunctions(cv_vector1) == getnbasefunctions(ip)*sdim @test Ferrite.getnbasefunctions(cv) == getnbasefunctions(ip) end diff --git a/test_script.jl b/test_script.jl new file mode 100644 index 0000000..5655d16 --- /dev/null +++ b/test_script.jl @@ -0,0 +1,80 @@ + + +using Ferrite +using IGA + +function _get_face_bezier_coordinates!(xb::Vector{<:Vec{dim}}, x::Vector{<:Vec{dim}}, grid::BezierGrid, cell::Ferrite.AbstractCell, lfaceid::Int, cellid::Int) where dim + ip = Ferrite.default_interpolation(typeof(cell)) + + facedofs = Ferrite.dirichlet_facedof_indices(ip)[lfaceid] + @assert length(x) == length(facedofs) + @assert length(x) == length(xb) + + i = 0 + for d in facedofs + i += 1 + x[i] = grid.nodes[cell.nodes[d]].x + end + + n = length(xb) + C = grid.beo[cellid] + + for i in 1:n + xb[i] = zero(Vec{dim}) + end + + for i in 1:n + k = facedofs[i] + c_row = C[k] + _x = x[i] + for j in 1:n + dof = facedofs[j] + val = c_row[dof] + xb[j] += val * _x + end + end + +end + +ip = IGA.IGAInterpolation{RefQuadrilateral,2}() +ipface = IGA.IGAInterpolation{RefLine,2}() + +nurbsmesh = generate_nurbs_patch(:cube, (3,3), (2,2); size = (1.0, 1.0)) +grid = BezierGrid(nurbsmesh) + +dh = DofHandler(grid) +add!(dh, :u, ip^2) +close!(dh) + +cellid = 3 +lfaceid = 2 +cell = grid.cells[cellid] + +a = zeros(ndofs(dh)) .+1 + +vtk = VTKIGAFile("newoutput", grid, 1:getncells(grid)) +IGA.write_solution(vtk, dh, a) +close(vtk) + +n = 3 +xb = zeros(Vec{2}, n) +x = zeros(Vec{2}, n) + +N = zeros(n) +dNdξ = zeros(Vec{1}, n) +ξ = zero(Vec{1}) +for i in 1:n + dNdξ[i], N[i] = Ferrite.shape_gradient_and_value(ipface, ξ, i) +end + +_get_face_bezier_coordinates!(xb, x, grid, cell, lfaceid, cellid) + +dxdxi = zero(Vec{2}) +for i in 1:n + dxdxi += dNdξ[i][1] * xb[i] +end + +_normal(dxdxi) = Vec((dxdxi[2], -dxdxi[1]))/norm( Vec((dxdxi[2], -dxdxi[1]))) + +@show _normal(dxdxi) + From c6d1d088fea38b5eba369d6fbd78b40d79ceccf7 Mon Sep 17 00:00:00 2001 From: lijas Date: Tue, 31 Oct 2023 17:08:44 +0100 Subject: [PATCH 10/50] change order of shape and order beziercell --- src/IGA.jl | 26 ++++++++++++++------------ src/VTK.jl | 8 ++++---- src/bezier_grid.jl | 4 +++- src/mesh_generation.jl | 18 +++++++++++++----- src/splines/bezier_values.jl | 28 +++++++++++++++++++++++----- test/test_l2proj.jl | 2 +- 6 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/IGA.jl b/src/IGA.jl index 9ab551b..3fd4a8b 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -67,25 +67,27 @@ Ferrite.dirichlet_vertexdof_indices(::IGAInterpolation{shape, order}) where {sha """ - BezierCell{order,refshape,N} <: Ferrite.AbstractCell{refshape} + BezierCell{refshape,order,N} <: Ferrite.AbstractCell{refshape} `N` = number of nodes/controlpoints `order` = tuple with order in each parametric dimension (does not need to be equal to `dim`) """ -struct BezierCell{order,refshape,N} <: Ferrite.AbstractCell{refshape} +struct BezierCell{refshape,order,N} <: Ferrite.AbstractCell{refshape} nodes::NTuple{N,Int} - function BezierCell{orders,shape}(nodes::NTuple{N,Int}) where {rdim, orders, shape<:RefHypercube{rdim}, N} - #@assert(order isa Integer) - @assert prod(orders.+1) == N - @assert length(orders) == rdim - return new{orders,shape,N}(nodes) + function BezierCell{refshape,order}(nodes::NTuple{N,Int}) where {rdim, order, refshape<:RefHypercube{rdim}, N} + @assert(order isa Integer) + @assert (order+1)^rdim == N + return new{refshape,order,N}(nodes) end end +function BezierCell{refshape,order,N}(nodes::NTuple{N,Int}) where {rdim, order, refshape<:RefHypercube{rdim}, N} + return BezierCell{refshape,order}(nodes) +end -Ferrite.default_interpolation(::Type{<:BezierCell{order, shape}}) where {order, shape} = IGAInterpolation{shape, order}() -Ferrite.nnodes(::BezierCell{order, shape, N}) where {order, shape, N} = N -getorders(::BezierCell{orders,refshape,N}) where {orders,refshape,N} = orders +Ferrite.default_interpolation(::Type{<:BezierCell{shape,order}}) where {order, shape} = IGAInterpolation{shape, order}() +Ferrite.nnodes(::BezierCell{shape, order, N}) where {order, shape, N} = N +getorders(::BezierCell{refshape, orders, N}) where {orders,refshape,N} = orders include("nurbsmesh.jl") include("mesh_generation.jl") @@ -106,9 +108,9 @@ Ferrite._mass_qr(::Bernstein{RefCube, (2,2,2)}) = QuadratureRule{RefCube}(2+1) #we can only distribute cells on the nodes/controlpoints Ferrite.vertices(c::BezierCell) = c.nodes -_bernstein_ordering(::Type{<:BezierCell{order,shape}}) where {order,shape} = _bernstein_ordering(Bernstein{shape,order}()) +_bernstein_ordering(::Type{<:BezierCell{shape,order}}) where {order,shape} = _bernstein_ordering(Bernstein{shape,order}()) -function Ferrite.faces(c::BezierCell{order,shape}) where {shape,order} +function Ferrite.faces(c::BezierCell{shape,order}) where {shape,order} return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_facedof_indices(IGAInterpolation{shape,order}() ))) end diff --git a/src/VTK.jl b/src/VTK.jl index bc7e38b..6f05811 100644 --- a/src/VTK.jl +++ b/src/VTK.jl @@ -1,17 +1,17 @@ -function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{order,RefHexahedron}}) where {order} +function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{RefHexahedron,order}}) where {order} return Ferrite.VTKCellTypes.VTK_BEZIER_HEXAHEDRON end -function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{order,RefQuadrilateral}}) where {order} +function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{RefQuadrilateral,order}}) where {order} return Ferrite.VTKCellTypes.VTK_BEZIER_QUADRILATERAL end -function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{order,RefLine}}) where {order} +function Ferrite.cell_to_vtkcell(::Type{<:BezierCell{RefLine,order}}) where {order} return Ferrite.VTKCellTypes.VTK_BEZIER_CURVE end # Store the Ferrite to vtk order in a cache for specific cell type let cache = Dict{Type{<:BezierCell}, Vector{Int}}() - global function _iga_to_vtkorder(celltype::Type{<:BezierCell{orders,shape,N}}) where {orders,shape,N} + global function _iga_to_vtkorder(celltype::Type{<:BezierCell{shape,order,N}}) where {order,shape,N} get!(cache, celltype) do if shape == RefHexahedron igaorder = _bernstein_ordering(celltype) diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 1623b57..78c6f1b 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -25,8 +25,10 @@ end function BezierGrid(mesh::NURBSMesh{pdim,sdim}) where {pdim,sdim} + @assert allequal(mesh.orders) + order = first(mesh.orders) N = size(mesh.IEN, 1) - CellType = BezierCell{mesh.orders, RefHypercube{pdim}} + CellType = BezierCell{RefHypercube{pdim},order} ordering = _bernstein_ordering(CellType) cells = [CellType(Tuple(mesh.IEN[ordering,ie])) for ie in 1:getncells(mesh)] diff --git a/src/mesh_generation.jl b/src/mesh_generation.jl index f7c2a28..9b8b789 100644 --- a/src/mesh_generation.jl +++ b/src/mesh_generation.jl @@ -39,16 +39,21 @@ function _generate_equidistant_parametrization(knot_vector::Vector{T}, order::In return range(from, stop=to, length=length(knot_vector)-1-order) end -function Ferrite.generate_grid(::Type{<:BezierCell{order,refshape}}, nels::Tuple{Int,Int}, left::Vec{sdim,T}=zero(Vec{sdim}), right::Vec{sdim,T}=one(Vec{sdim})) where {T,order,sdim,refshape<:RefHypercube{sdim}} - patch = generate_nurbs_patch(:rectangle, nels, ntuple(i->order,sdim); cornerpos = tuple(left), size = tuple(right-left)) - return BezierGrid(patch) +function Ferrite.generate_grid(::Type{<:BezierCell{RefQuadrilateral,order}}, nels::NTuple{2,Int}, LL::Vec{2,T}, LR::Vec{2,T}, UR::Vec{2,T}, UL::Vec{2,T}) where {T,order} + #IGnore LR and UL for now + patch = generate_nurbs_patch(:rectangle, nels, ntuple(i->order,2); cornerpos = Tuple(LL), size = Tuple(UR-LL)) + grid = BezierGrid(patch) + addfaceset!(grid, "left", x->x[1]≈LL[1]) + addfaceset!(grid, "right", x->x[1]≈UR[1]) + addfaceset!(grid, "top", x->x[2]≈UR[2]) + addfaceset!(grid, "bottom", x->x[2]≈LL[2]) end function generate_nurbs_patch(s::Symbol, args...; kwargs...) generate_nurbs_patch(Val{s}(), args...; kwargs...) end -function generate_nurbs_patch(::Val{:line}, nel::NTuple{1,Int}, orders::NTuple{1,Int}, size::NTuple{1,T}; cornerpos::NTuple{1,T} = (0.0,), multiplicity::NTuple{1,Int}=(1,), sdim::Int=1) where T +function generate_nurbs_patch(::Val{:line}, nel::NTuple{1,Int}, orders::NTuple{1,Int}; size::NTuple{1,T}, cornerpos::NTuple{1,T} = (0.0,), multiplicity::NTuple{1,Int}=(1,), sdim::Int=1) where T generate_nurbs_patch(:hypercube, nel, orders; cornerpos, size, multiplicity, sdim) end @@ -711,7 +716,10 @@ function get_nurbs_griddata(orders::NTuple{pdim,Int}, knot_vectors::NTuple{pdim, ncontrolpoints = length(IEN[:,1]) nodes = [Ferrite.Node(x) for x in control_points] - _BezierCell = BezierCell{sdim,ncontrolpoints,orders} + @assert allequal(orders) + order = first(orders) + N = (order+1)^pdim + _BezierCell = BezierCell{RefHypercube{pdim},order,N} cells = [_BezierCell(Tuple(reverse(IEN[:,ie]))) for ie in 1:nel] return cells, nodes diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 47a7693..7e625ac 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -24,9 +24,12 @@ struct BezierCellValues{T<:Real,CV<:Ferrite.CellValues} <: Ferrite.AbstractCellV end struct BezierFaceValues{T<:Real,CV<:Ferrite.FaceValues} <: Ferrite.AbstractFaceValues - cv_bezier::CV - cv_tmp::CV + # cv_bezier stores the bernstein basis. These are the same for all elements, and does not change + cv_bezier::CV + # cv_nurbs sotres the nurbs/b-spline basis. These will change for each element cv_nurbs::CV + # cv_tmp is just and intermidiate state need when converting from cv_bezier to cv_nurbs + cv_tmp::CV current_beo::Base.RefValue{BezierExtractionOperator{T}} current_w::Vector{T} @@ -77,6 +80,21 @@ function Ferrite.CellValues(qr::QuadratureRule, ::IGAInterpolation{shape,order}, return BezierCellValues(cv) end +#Entrypoints for vector valued IGAInterpolation, which creates BezierCellValues +function Ferrite.FaceValues(qr::FaceQuadratureRule, ::IP, ::IGAInterpolation{shape,order}) where {shape,order,vdim,IP<:VectorizedInterpolation{vdim, shape, <:Any, <:IGAInterpolation{shape,order}}} + _ip = Bernstein{shape,order}()^vdim + _ip_geo = Bernstein{shape,order}() + cv = FaceValues(qr, _ip, _ip_geo) + return BezierFaceValues(cv) +end + +function Ferrite.FaceValues(qr::FaceQuadratureRule, ::IGAInterpolation{shape,order}, ::IGAInterpolation{shape,order}) where {shape,order} + _ip = Bernstein{shape,order}() + _ip_geo = Bernstein{shape,order}() + cv = FaceValues(qr, _ip, _ip_geo) + return BezierFaceValues(cv) +end + function BezierFaceValues(qr::FaceQuadratureRule, ip::Interpolation, gip::Interpolation) return BezierFaceValues(Float64, qr, ip, gip) end @@ -124,14 +142,14 @@ function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, xb::V return x end -function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, bcoords::BezierCoords) +function Ferrite.spatial_coordinate(cv::Ferrite.AbstractCellValues, iqp::Int, bcoords::BezierCoords) x = spatial_coordinate(cv, iqp, (bcoords.xb, bcoords.wb)) return x end -function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, (xb, wb)::CoordsAndWeight{sdim,T}) where {sdim,T} +function Ferrite.spatial_coordinate(cv::Ferrite.AbstractCellValues, iqp::Int, (xb, wb)::CoordsAndWeight{sdim,T}) where {sdim,T} nbasefunks = Ferrite.getngeobasefunctions(cv) - @boundscheck Ferrite.checkquadpoint(cv.cv_bezier, iqp) + @boundscheck Ferrite.checkquadpoint(cv, iqp) W = 0.0 x = zero(Vec{sdim,T}) for i in 1:nbasefunks diff --git a/test/test_l2proj.jl b/test/test_l2proj.jl index 923bed1..4600f62 100644 --- a/test/test_l2proj.jl +++ b/test/test_l2proj.jl @@ -7,7 +7,7 @@ end function test_projection() - element = BezierCell{2,RefQuadrilateral} + element = BezierCell{RefQuadrilateral,2} grid = generate_grid(Quadrilateral, (1,1), Vec((0.,0.)), Vec((1.,1.))) # grid = BezierGrid( generate_nurbs_patch(:rectangle, (1,1), (2,2); size=(1.0,1.0)) ) From 72e7198e5dd7042e8c779000ae8f110d3e68c8c6 Mon Sep 17 00:00:00 2001 From: lijas Date: Fri, 10 Nov 2023 17:16:49 +0100 Subject: [PATCH 11/50] som wip --- src/IGA.jl | 2 ++ src/mesh_generation.jl | 20 ++++++++++++++++++++ src/splines/bezier_values.jl | 15 ++++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/IGA.jl b/src/IGA.jl index 3fd4a8b..4d591b0 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -30,6 +30,8 @@ struct BezierCoords{dim_s,T} beo ::Base.RefValue{ BezierExtractionOperator{T} } end +zero_bezier_coord(dim, T, nnodes) = BezierCoords{dim,T}(zeros(Vec{dim,T}, nnodes), zeros(T, nnodes), zeros(Vec{dim,T}, nnodes), zeros(T, nnodes), Base.RefValue(diagonal_beo(1))) + #Base.zero(Type{BezierCoords{dim,T}}) where {dim,T} = BezierCoords """ diff --git a/src/mesh_generation.jl b/src/mesh_generation.jl index 9b8b789..3179fd1 100644 --- a/src/mesh_generation.jl +++ b/src/mesh_generation.jl @@ -43,10 +43,30 @@ function Ferrite.generate_grid(::Type{<:BezierCell{RefQuadrilateral,order}}, nel #IGnore LR and UL for now patch = generate_nurbs_patch(:rectangle, nels, ntuple(i->order,2); cornerpos = Tuple(LL), size = Tuple(UR-LL)) grid = BezierGrid(patch) + addfaceset!(grid, "left", x->x[1]≈LL[1]) addfaceset!(grid, "right", x->x[1]≈UR[1]) + addfaceset!(grid, "top", x->x[2]≈UR[2]) addfaceset!(grid, "bottom", x->x[2]≈LL[2]) + + return grid +end + +function Ferrite.generate_grid(::Type{<:BezierCell{RefHexahedron,order}}, nels::NTuple{3,Int}, left::Vec{3,T}=Vec{3}((-1.0,-1.0,-1.0)), right::Vec{3,T}=Vec{3}((1.0,1.0,1.0))) where {order,T} + #IGnore LR and UL for now + patch = generate_nurbs_patch(:cube, nels, ntuple(i->order,3); cornerpos = Tuple(left), size = Tuple(right-left)) + grid = BezierGrid(patch) + + addfaceset!(grid, "left", x->x[1]≈left[1]) + addfaceset!(grid, "right", x->x[1]≈right[1]) + + addfaceset!(grid, "front", x->x[2]≈left[2]) + addfaceset!(grid, "back", x->x[2]≈right[2]) + + addfaceset!(grid, "top", x->x[3]≈right[3]) + addfaceset!(grid, "bottom", x->x[3]≈left[3]) + return grid end function generate_nurbs_patch(s::Symbol, args...; kwargs...) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 7e625ac..c696a7c 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -37,6 +37,11 @@ end BezierCellAndFaceValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFaceValues{T,CV}} +function Ferrite.checkface(fv::BezierFaceValues, face::Int) + 0 < face <= nfaces(fv) || error("Face index out of range.") + return nothing +end + Ferrite.nfaces(fv::BezierFaceValues) = size(fv.cv_nurbs.N, 3) function BezierCellValues(cv::Ferrite.CellValues) @@ -67,14 +72,14 @@ end #Entrypoints for vector valued IGAInterpolation, which creates BezierCellValues function Ferrite.CellValues(qr::QuadratureRule, ::IP, ::IGAInterpolation{shape,order}) where {shape,order,vdim,IP<:VectorizedInterpolation{vdim, shape, <:Any, <:IGAInterpolation{shape,order}}} - _ip = Bernstein{shape,order}()^vdim + _ip = IGAInterpolation{shape,order}()^vdim _ip_geo = Bernstein{shape,order}() cv = CellValues(qr, _ip, _ip_geo) return BezierCellValues(cv) end function Ferrite.CellValues(qr::QuadratureRule, ::IGAInterpolation{shape,order}, ::IGAInterpolation{shape,order}) where {shape,order} - _ip = Bernstein{shape,order}() + _ip = IGAInterpolation{shape,order}() _ip_geo = Bernstein{shape,order}() cv = CellValues(qr, _ip, _ip_geo) return BezierCellValues(cv) @@ -82,14 +87,14 @@ end #Entrypoints for vector valued IGAInterpolation, which creates BezierCellValues function Ferrite.FaceValues(qr::FaceQuadratureRule, ::IP, ::IGAInterpolation{shape,order}) where {shape,order,vdim,IP<:VectorizedInterpolation{vdim, shape, <:Any, <:IGAInterpolation{shape,order}}} - _ip = Bernstein{shape,order}()^vdim + _ip = IGAInterpolation{shape,order}()^vdim _ip_geo = Bernstein{shape,order}() cv = FaceValues(qr, _ip, _ip_geo) return BezierFaceValues(cv) end function Ferrite.FaceValues(qr::FaceQuadratureRule, ::IGAInterpolation{shape,order}, ::IGAInterpolation{shape,order}) where {shape,order} - _ip = Bernstein{shape,order}() + _ip = IGAInterpolation{shape,order}() _ip_geo = Bernstein{shape,order}() cv = FaceValues(qr, _ip, _ip_geo) return BezierFaceValues(cv) @@ -320,7 +325,7 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.Abs end function Base.show(io::IO, m::MIME"text/plain", fv::BezierFaceValues) - println(io, "FaceValues with") + println(io, "BezierFaceValues with") nqp = getnquadpoints.(fv.cv_bezier.qr.face_rules) if all(n==first(nqp) for n in nqp) println(io, "- Quadrature rule with ", first(nqp), " points per face") From 8dcc44d1cd090e972db92d339a1f163548a2157c Mon Sep 17 00:00:00 2001 From: lijas Date: Sun, 12 Nov 2023 21:03:46 +0100 Subject: [PATCH 12/50] neverending update --- src/bezier_extraction.jl | 3 +- src/splines/bezier.jl | 89 ++++++------ src/splines/bezier_values.jl | 270 +++++++++++++++++++++++++++++------ test/test_meshes.jl | 5 +- test/test_values.jl | 42 +++--- 5 files changed, 305 insertions(+), 104 deletions(-) diff --git a/src/bezier_extraction.jl b/src/bezier_extraction.jl index 2cf18bb..8dc199a 100644 --- a/src/bezier_extraction.jl +++ b/src/bezier_extraction.jl @@ -113,7 +113,8 @@ Computes the bezier extraction operator in each parametric direction, and uses t #Reorder # - ordering = _bernstein_ordering(Bernstein{RefHypercube{pdim}, orders}()) + @assert allequal(orders) + ordering = _bernstein_ordering(Bernstein{RefHypercube{pdim}, orders[1]}()) nel = prod(nels) C_reorder = Vector{eltype(first(Ce))}() for i in 1:nel diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index d833218..616f614 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -11,12 +11,8 @@ IGA, together with bezier extraction + BezierValues. """ struct Bernstein{shape, order} <: Ferrite.ScalarInterpolation{shape, order} function Bernstein{shape,order}() where {rdim, shape<:RefHypercube{rdim}, order} - orders = order - if order isa Integer - orders = ntuple(i->order, rdim) - end - @assert length(orders) == rdim - return new{shape,orders}() + @assert order isa Int + return new{shape,order}() end end @@ -27,13 +23,13 @@ Ferrite.adjust_dofs_during_distribution(::Bernstein{<:Any, 1}) = false # # # # Bernstein line, order 2 # # # -Ferrite.getnbasefunctions(::Bernstein{RefLine,(2,)}) = 3 +Ferrite.getnbasefunctions(::Bernstein{RefLine,2}) = 3 -Ferrite.vertexdof_indices(::Bernstein{RefLine,(2,)}) = ((1,),(2,)) -Ferrite.facedof_indices(::Bernstein{RefLine,(2,)}) = ((1,), (2,)) -Ferrite.celldof_interior_indices(::Bernstein{RefLine,(2,)}) = (3,) +Ferrite.vertexdof_indices(::Bernstein{RefLine,2}) = ((1,),(2,)) +Ferrite.facedof_indices(::Bernstein{RefLine,2}) = ((1,), (2,)) +Ferrite.celldof_interior_indices(::Bernstein{RefLine,2}) = (3,) -function Ferrite.shape_value(ip::Bernstein{RefLine,(2,)}, _ξ::Vec{1}, i::Int) +function Ferrite.shape_value(ip::Bernstein{RefLine,2}, _ξ::Vec{1}, i::Int) ξ = 0.5*(_ξ[1] + 1.0) i == 1 && return (1-ξ)^2 i == 2 && return ξ^2 @@ -45,13 +41,13 @@ end # # # # Bernstein Quadrilateral, order 2 # # # -Ferrite.getnbasefunctions(::Bernstein{RefQuadrilateral,(2,2)}) = 9 -Ferrite.vertexdof_indices(::Bernstein{RefQuadrilateral,(2,2)}) = ((1,),(2,),(3,),(4,)) -Ferrite.facedof_indices(::Bernstein{RefQuadrilateral,(2,2)}) = ((1,2, 5), (2,3, 6), (3,4, 7), (4,1, 8)) -Ferrite.facedof_interior_indices(::Bernstein{RefQuadrilateral,(2,2)}) = ((5,), (6,), (7,), (8,)) -Ferrite.celldof_interior_indices(::Bernstein{RefQuadrilateral,(2,2)}) = (9,) +Ferrite.getnbasefunctions(::Bernstein{RefQuadrilateral,2}) = 9 +Ferrite.vertexdof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,),(2,),(3,),(4,)) +Ferrite.facedof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,2, 5), (2,3, 6), (3,4, 7), (4,1, 8)) +Ferrite.facedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = ((5,), (6,), (7,), (8,)) +Ferrite.celldof_interior_indices(::Bernstein{RefQuadrilateral,2}) = (9,) -function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,(2,2)}, _ξ::Vec{2}, i::Int) +function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,2}, _ξ::Vec{2}, i::Int) ξ, η = _ξ i == 1 && return 0.0625((1 - η)^2)*((1 - ξ)^2) i == 2 && return 0.0625((1 + ξ)^2)*((1 - η)^2) @@ -68,9 +64,9 @@ end # # # # Bernstein Hexahedron, order 2 # # # -Ferrite.getnbasefunctions(::Bernstein{RefHexahedron,(2,2,2)}) = 27 +Ferrite.getnbasefunctions(::Bernstein{RefHexahedron,2}) = 27 -Ferrite.facedof_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( +Ferrite.facedof_indices(::Bernstein{RefHexahedron,2}) = ( (1,4,3,2, 12,11,10,9, 21), (1,2,6,5, 9,18,13,17, 22), (2,3,7,6, 10,19,14,18, 23), @@ -78,10 +74,10 @@ Ferrite.facedof_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( (1,5,8,4, 17,16,20,12, 25), (5,6,7,8, 13,14,15,16, 26), ) -Ferrite.facedof_interior_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( +Ferrite.facedof_interior_indices(::Bernstein{RefHexahedron,2}) = ( (21,), (22,), (23,), (24,), (25,), (26,), ) -Ferrite.edgedof_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( +Ferrite.edgedof_indices(::Bernstein{RefHexahedron,2}) = ( (1,2, 9), (2,3, 10), (3,4, 11), @@ -95,11 +91,11 @@ Ferrite.edgedof_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( (3,7, 19), (4,8, 20), ) -Ferrite.edgedof_interior_indices(::Bernstein{RefHexahedron,(2,2,2)}) = ( +Ferrite.edgedof_interior_indices(::Bernstein{RefHexahedron,2}) = ( (9,), (10,), (11,), (12,), (13,), (14,), (15,), (16,), (17), (18,), (19,), (20,) ) -function Ferrite.shape_value(ip::Bernstein{RefHexahedron,(2,2,2)}, _ξ::Vec{3}, i::Int) +function Ferrite.shape_value(ip::Bernstein{RefHexahedron,2}, _ξ::Vec{3}, i::Int) ξ, η, ζ = _ξ i == 1 && return 0.015625((1 - ζ)^2)*((1 - η)^2)*((1 - ξ)^2) i == 2 && return 0.015625((1 + ξ)^2)*((1 - ζ)^2)*((1 - η)^2) @@ -168,25 +164,27 @@ function Ferrite.shape_value(ip::Bernstein, _ξ::Vec, i::Int) _compute_bezier_shape_value(ip,_ξ, i) end -function _compute_bezier_shape_value(ip::Bernstein{shape,orders}, ξ::Vec{dim,T}, i::Int) where {shape,dim,orders,T} - _n = orders .+ 1 +function _compute_bezier_shape_value(ip::Bernstein{shape,order}, ξ::Vec{dim,T}, i::Int) where {shape,dim,order,T} + _n = ntuple(i->order+1, dim) ordering = _bernstein_ordering(ip) basefunction_indeces = CartesianIndices(_n)[ordering[i]] val = one(T) for i in 1:dim - val *= IGA._bernstein_basis_recursive(orders[i], basefunction_indeces[i], ξ[i]) + val *= IGA._bernstein_basis_recursive(order, basefunction_indeces[i], ξ[i]) end return val end -function _compute_facedof_indices(ip::Bernstein{RefQuadrilateral,orders}) where {orders} +function _compute_facedof_indices(ip::Bernstein{RefQuadrilateral,order}) where {order} faces = Tuple[] + orders = (order, order) + ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) #Order for 1d interpolation. - order_1d1 = _bernstein_ordering(Bernstein{RefLine,orders[1:1]}()) - order_1d2 = _bernstein_ordering(Bernstein{RefLine,orders[2:2]}()) + order_1d1 = _bernstein_ordering(Bernstein{RefLine,order}()) + order_1d2 = _bernstein_ordering(Bernstein{RefLine,order}()) push!(faces, Tuple(ind[:,1][order_1d1])) #bot push!(faces, Tuple(ind[end,:][order_1d2]))# right @@ -196,12 +194,14 @@ function _compute_facedof_indices(ip::Bernstein{RefQuadrilateral,orders}) where return Tuple(faces) end -function _compute_facedof_indices(ip::Bernstein{RefHexahedron,orders}) where {orders} +function _compute_facedof_indices(ip::Bernstein{RefHexahedron,order}) where {order} faces = Tuple[] + orders = (order, order, order) + ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) #Order for 2d interpolation. - order_2d = _bernstein_ordering(Bernstein{RefQuadrilateral,first(orders)}()) + order_2d = _bernstein_ordering(Bernstein{RefQuadrilateral,order}()) push!(faces, Tuple(ind[:,:,1][order_2d])) # bottom push!(faces, Tuple(ind[:,1,:][order_2d])) # front @@ -213,12 +213,14 @@ function _compute_facedof_indices(ip::Bernstein{RefHexahedron,orders}) where {or return Tuple(faces) end -function _compute_edgedof_indices(ip::Bernstein{RefHexahedron,orders}) where {orders} +function _compute_edgedof_indices(ip::Bernstein{RefHexahedron,order}) where {order} edges = Tuple[] + orders = (order, order, order) + ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...) #Order for 1d interpolation. - order_1d = _bernstein_ordering(Bernstein{1,orders[1:1]}()) + order_1d = _bernstein_ordering(Bernstein{1,order}()) # bottom push!(edges, Tuple(ind[:,1,1][order_1d])) @@ -259,12 +261,12 @@ end # # # # Bernstein generation any order # # # -Ferrite.getnbasefunctions(::Bernstein{shape,orders}) where {shape, orders}= prod(orders.+1) +Ferrite.getnbasefunctions(::Bernstein{shape,order}) where {rdim, shape <: AbstractRefShape{rdim}, order}= (order+1)^rdim -function Ferrite.reference_coordinates(ip::Bernstein{shape,orders}) where {rdim, shape <: AbstractRefShape{rdim}, orders} +function Ferrite.reference_coordinates(ip::Bernstein{shape,order}) where {rdim, shape <: AbstractRefShape{rdim}, order} T = Float64 - nbasefunks_dim = orders .+ 1 + nbasefunks_dim = ntuple(i->order+1, rdim) nbasefuncs = prod(nbasefunks_dim) coords = Vec{rdim,T}[] @@ -288,8 +290,8 @@ end Return the ordering of the bernstein basis base-functions, from a "CartesianIndices-ordering". The ordering is the same as in VTK: https://blog.kitware.com/wp-content/uploads/2020/03/Implementation-of-rational-Be%CC%81zier-cells-into-VTK-Report.pdf. """ -function _bernstein_ordering(::Bernstein{RefLine, orders}) where {orders} - ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) +function _bernstein_ordering(::Bernstein{RefLine, order}) where {order} + ind = reshape(1:prod(order+1), order+1) ordering = Int[] # Corners @@ -302,7 +304,8 @@ function _bernstein_ordering(::Bernstein{RefLine, orders}) where {orders} return ordering end -function _bernstein_ordering(::Bernstein{RefQuadrilateral,orders}) where {orders} +function _bernstein_ordering(::Bernstein{RefQuadrilateral,order}) where {order} + orders = (order, order) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) ordering = Int[] @@ -324,8 +327,8 @@ function _bernstein_ordering(::Bernstein{RefQuadrilateral,orders}) where {orders return ordering end -function _bernstein_ordering(::Bernstein{RefHexahedron, orders}) where {orders} - +function _bernstein_ordering(::Bernstein{RefHexahedron, order}) where {order} + orders = (order, order, order) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) ordering = Int[] @@ -382,8 +385,8 @@ function _vtk_ordering(c::BezierCell{RefQuadrilateral}) _bernstein_ordering(c) end -function _vtk_ordering(::BezierCell{RefHexahedron, orders}) where {orders} - @assert(length(orders) == 3) +function _vtk_ordering(::BezierCell{RefHexahedron, order}) where {order} + orders = (order, order, order) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index c696a7c..44bf696 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -11,25 +11,41 @@ function Ferrite.default_geometric_interpolation(::Bernstein{shape, order}) wher return VectorizedInterpolation{dim}(Bernstein{shape, order}()) end -struct BezierCellValues{T<:Real,CV<:Ferrite.CellValues} <: Ferrite.AbstractCellValues - # cv_bezier stores the bernstein basis. These are the same for all elements, and does not change +struct BezierCellValues{T<:Real,CV<:Ferrite.CellValues, d²MdX²_t, d²NdX²_t} <: Ferrite.AbstractCellValues + # cv_bezier stores the shape values from the bernstein basis. These are the same for all elements, and does not change cv_bezier::CV - # cv_nurbs sotres the nurbs/b-spline basis. These will change for each element + d²Bdξ²_geom::Matrix{d²MdX²_t} + d²Bdξ²_func::Matrix{d²NdX²_t} + + # cv_nurbs stores the nurbs/b-spline basis. These will change for each element cv_nurbs::CV - # cv_tmp is just and intermidiate state need when converting from cv_bezier to cv_nurbs + d²Ndξ²::Matrix{d²NdX²_t} + d²NdX²::Matrix{d²NdX²_t} + + # cv_tmp is just an intermidiate state needed for converting from cv_bezier to cv_nurbs cv_tmp::CV + d²Ndξ²_tmp::Matrix{d²NdX²_t} + d²NdX²_tmp::Matrix{d²NdX²_t} current_beo::Base.RefValue{BezierExtractionOperator{T}} current_w::Vector{T} end -struct BezierFaceValues{T<:Real,CV<:Ferrite.FaceValues} <: Ferrite.AbstractFaceValues - # cv_bezier stores the bernstein basis. These are the same for all elements, and does not change +struct BezierFaceValues{T<:Real,CV<:Ferrite.FaceValues, d²MdX²_t, d²NdX²_t} <: Ferrite.AbstractFaceValues + # cv_bezier stores the shape values from the bernstein basis. These are the same for all elements, and does not change cv_bezier::CV - # cv_nurbs sotres the nurbs/b-spline basis. These will change for each element + d²Bdξ²_geom::Array{d²MdX²_t,3} + d²Bdξ²_func::Array{d²NdX²_t,3} + + # cv_nurbs stores the nurbs/b-spline basis. These will change for each element cv_nurbs::CV - # cv_tmp is just and intermidiate state need when converting from cv_bezier to cv_nurbs + d²Ndξ²::Array{d²NdX²_t,3} + d²NdX²::Array{d²NdX²_t,3} + + # cv_tmp is just an intermidiate state needed for converting from cv_bezier to cv_nurbs cv_tmp::CV + d²Ndξ²_tmp::Array{d²NdX²_t,3} + d²NdX²_tmp::Array{d²NdX²_t,3} current_beo::Base.RefValue{BezierExtractionOperator{T}} current_w::Vector{T} @@ -46,42 +62,132 @@ Ferrite.nfaces(fv::BezierFaceValues) = size(fv.cv_nurbs.N, 3) function BezierCellValues(cv::Ferrite.CellValues) T = eltype(cv.M) + dim = Ferrite.getdim(cv.ip) + + is_vector_valued = cv.ip isa Ferrite.VectorInterpolation + undef_beo = Ref(Vector{SparseArrays.SparseVector{T,Int}}(undef,0)) undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(cv)) - return BezierCellValues(cv, deepcopy(cv), deepcopy(cv), undef_beo, undef_w) + + # + # Higher order functions + # + d²MdX²_t = Tensor{2,dim,Float64,Tensors.n_components(Tensor{2,dim})} + d²NdX²_t = if is_vector_valued + Tensor{3,dim,Float64,Tensors.n_components(Tensor{3,dim})} + else + Tensor{2,dim,Float64,Tensors.n_components(Tensor{2,dim})} + end + + n_geom_basefuncs = getnbasefunctions(cv.gip) + n_func_basefuncs = getnbasefunctions(cv.gip) + n_qpoints = getnquadpoints(cv) + d²MdX² = fill(zero(d²MdX²_t) * T(NaN), n_geom_basefuncs, n_qpoints) + d²NdX² = fill(zero(d²NdX²_t) * T(NaN), n_func_basefuncs, n_qpoints) + + for (qp, ξ) in pairs(Ferrite.getpoints(cv.qr)) + for ib in 1:n_func_basefuncs + d²MdX²[ib, qp] = Tensors.hessian(ξ -> shape_value(cv.gip, ξ, ib), ξ) + end + for ib in 1:n_func_basefuncs + d²NdX²[ib, qp] = Tensors.hessian(ξ -> shape_value(cv.ip, ξ, ib), ξ) + end + end + + return BezierCellValues( + cv, d²MdX², d²NdX², + deepcopy(cv), copy(d²NdX²), copy(d²NdX²) , + deepcopy(cv), copy(d²NdX²), copy(d²NdX²) , + undef_beo, undef_w) end + function BezierFaceValues(cv::Ferrite.FaceValues) T = eltype(cv.M) + dim = Ferrite.getdim(cv.func_interp) + nfaces = dim*2 + + is_vector_valued = cv.func_interp isa Ferrite.VectorInterpolation + undef_beo = Ref(Vector{SparseArrays.SparseVector{T,Int}}(undef,0)) undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(cv)) - return BezierFaceValues(cv, deepcopy(cv), deepcopy(cv), undef_beo, undef_w) + + # + # Higher order functions + # + d²MdX²_t = Tensor{2,dim,Float64,Tensors.n_components(Tensor{2,dim})} + d²NdX²_t = if is_vector_valued + Tensor{3,dim,Float64,Tensors.n_components(Tensor{3,dim})} + else + Tensor{2,dim,Float64,Tensors.n_components(Tensor{2,dim})} + end + + n_geom_basefuncs = getnbasefunctions(cv.geo_interp) + n_func_basefuncs = getnbasefunctions(cv.geo_interp) + n_qpoints = getnquadpoints(cv.qr, 1) + d²MdX² = fill(zero(d²MdX²_t) * T(NaN), n_geom_basefuncs, n_qpoints, nfaces) + d²NdX² = fill(zero(d²NdX²_t) * T(NaN), n_func_basefuncs, n_qpoints, nfaces) + + for (qp, ξ) in pairs(Ferrite.getpoints(cv.qr, 1)), iface in 1:nfaces + for ib in 1:n_func_basefuncs + d²MdX²[ib, qp, iface] = Tensors.hessian(ξ -> shape_value(cv.geo_interp, ξ, ib), ξ) + end + for ib in 1:n_func_basefuncs + d²NdX²[ib, qp, iface] = Tensors.hessian(ξ -> shape_value(cv.func_interp, ξ, ib), ξ) + end + end + + return BezierFaceValues( + cv, d²MdX², d²NdX², + deepcopy(cv), copy(d²NdX²), copy(d²NdX²) , + deepcopy(cv), copy(d²NdX²), copy(d²NdX²) , + undef_beo, undef_w) end -function BezierCellValues(qr::QuadratureRule, ip::Interpolation, gip::Interpolation) +#=function BezierCellValues(qr::QuadratureRule, ip::Interpolation, gip::Interpolation) return BezierCellValues(Float64, qr, ip, gip) -end +end=# -function BezierCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where {T, QR, IP, VGIP} +#=function BezierCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where {T, QR, IP, VGIP} cv = CellValues(T, qr, ip, gip) return BezierCellValues(cv) -end -function Ferrite.function_symmetric_gradient(bv::IGA.BezierValues, q_point::Int, u::AbstractVector, args...) - return function_symmetric_gradient(bv.cv_store, q_point, u, args...) -end +end=# + +# Entrypoint for `VectorInterpolation`s (vdim == rdim == sdim) with IGAInterpolation +function Ferrite.CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { + order, dim, shape <: AbstractRefShape{dim}, T, + QR <: QuadratureRule{shape}, + IP <: VectorizedInterpolation{dim, shape, order, <: IGAInterpolation{shape,order}}, + GIP <: IGAInterpolation{shape,order}, + VGIP <: VectorizedInterpolation{dim, shape, order, 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} + cv = CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) -#Entrypoints for vector valued IGAInterpolation, which creates BezierCellValues -function Ferrite.CellValues(qr::QuadratureRule, ::IP, ::IGAInterpolation{shape,order}) where {shape,order,vdim,IP<:VectorizedInterpolation{vdim, shape, <:Any, <:IGAInterpolation{shape,order}}} - _ip = IGAInterpolation{shape,order}()^vdim - _ip_geo = Bernstein{shape,order}() - cv = CellValues(qr, _ip, _ip_geo) return BezierCellValues(cv) end -function Ferrite.CellValues(qr::QuadratureRule, ::IGAInterpolation{shape,order}, ::IGAInterpolation{shape,order}) where {shape,order} - _ip = IGAInterpolation{shape,order}() - _ip_geo = Bernstein{shape,order}() - cv = CellValues(qr, _ip, _ip_geo) +# Entrypoint for `ScalarInterpolation`s (rdim == sdim) +function Ferrite.CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { + order, dim, shape <: AbstractRefShape{dim}, T, + QR <: QuadratureRule{shape}, + IP <: IGAInterpolation{shape,order}, + GIP <: IGAInterpolation{shape,order}, + 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} + cv = CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) return BezierCellValues(cv) end @@ -147,12 +253,12 @@ function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, xb::V return x end -function Ferrite.spatial_coordinate(cv::Ferrite.AbstractCellValues, iqp::Int, bcoords::BezierCoords) +function Ferrite.spatial_coordinate(cv::Ferrite.AbstractValues, iqp::Int, bcoords::BezierCoords) x = spatial_coordinate(cv, iqp, (bcoords.xb, bcoords.wb)) return x end -function Ferrite.spatial_coordinate(cv::Ferrite.AbstractCellValues, iqp::Int, (xb, wb)::CoordsAndWeight{sdim,T}) where {sdim,T} +function Ferrite.spatial_coordinate(cv::Ferrite.AbstractValues, iqp::Int, (xb, wb)::CoordsAndWeight{sdim,T}) where {sdim,T} nbasefunks = Ferrite.getngeobasefunctions(cv) @boundscheck Ferrite.checkquadpoint(cv, iqp) W = 0.0 @@ -217,9 +323,60 @@ function _cellvalues_bezier_extraction!(cv_nurbs::Ferrite.AbstractValues, cv_bez end -function Ferrite.reinit!(bcv::BezierCellValues, xb::AbstractVector{<:Vec}) - Ferrite.reinit!(bcv.cv_bezier, xb) #call the normal reinit function first + +function _cellvalues_bezier_extraction_higher_order!( + d²Ndξ²_nurbs::Array{d²Ndξ²_t}, d²NdX²_nurbs::Array{d²Ndξ²_t}, + d²Ndξ²_tmp::Array{d²Ndξ²_t}, d²NdX²_tmp::Array{d²Ndξ²_t}, + Cbe::BezierExtractionOperator{T}, w::Optional{Vector{T}}, faceid::Int) where {T, dim_s, d²Ndξ²_t <: Tensor{<:Any,dim_s}} + + is_scalar_valued = !(eltype(d²Ndξ²_nurbs) <: Tensor{3}) + + ngeobasefunctions = size(d²Ndξ²_nurbs, 1) ÷ dim_s + n_quad_ponts = size(d²Ndξ²_nurbs, 2) + + for iq in 1:n_quad_ponts + for ib in 1:ngeobasefunctions + + if is_scalar_valued + d²Ndξ²_nurbs[ib, iq, faceid] = zero(eltype(d²Ndξ²_nurbs)) + d²NdX²_nurbs[ib, iq, faceid] = zero(eltype(d²NdX²_nurbs)) + else #if FieldTrait(cv_nurbs) == Ferrite.VectorValued() + for d in 1:dim_s + d²Ndξ²_nurbs[(ib-1)*dim_s+d, iq, faceid] = zero(eltype(d²Ndξ²_nurbs)) + d²NdX²_nurbs[(ib-1)*dim_s+d, iq, faceid] = zero(eltype(d²NdX²_nurbs)) + end + end + + Cbe_ib = Cbe[ib] + + for (i, nz_ind) in enumerate(Cbe_ib.nzind) + val = Cbe_ib.nzval[i] + if (w !== nothing) + val*=w[ib] + end + if is_scalar_valued + d²Ndξ²_nurbs[ib, iq, faceid] += val*d²Ndξ²_tmp[nz_ind, iq, faceid] + d²NdX²_nurbs[ib, iq, faceid] += val*d²NdX²_tmp[nz_ind, iq, faceid] + else #if FieldTrait(cv_nurbs) == Ferrite.VectorValued() + for d in 1:dim_s + d²Ndξ²_nurbs[(ib-1)*dim_s + d, iq, faceid] += val*d²Ndξ²_tmp[(nz_ind-1)*dim_s + d, iq, faceid] + d²NdX²_nurbs[(ib-1)*dim_s + d, iq, faceid] += val*d²NdX²_tmp[(nz_ind-1)*dim_s + d, iq, faceid] + end + end + end + end + end + +end + +function Ferrite.reinit!(bcv::BezierCellValues, xb::AbstractVector{<:Vec})#; updateflags = CellValuesFlags()) + #Ferrite.reinit!(bcv.cv_bezier, xb) #call the normal reinit function first + _reinit_nurbs!( + bcv.cv_tmp, bcv.cv_bezier, + bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, + xb, bcv.current_w, 1) _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_bezier, bcv.current_beo[], nothing, 1) + _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) end function Ferrite.reinit!(bcv::BezierFaceValues, xb::AbstractVector{<:Vec}, faceid::Int) @@ -246,8 +403,12 @@ end function Ferrite.reinit!(bcv::BezierCellValues, bc::BezierCoords) set_bezier_operator!(bcv, bc.beo[], bc.w) - _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, bc.xb, bc.wb) + _reinit_nurbs!( + bcv.cv_tmp, bcv.cv_bezier, + bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, + bc.xb, bcv.current_w, 1) _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bc.w, 1) + _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) end function Ferrite.reinit!(bcv::BezierFaceValues, bc::BezierCoords, faceid::Int) @@ -255,8 +416,12 @@ function Ferrite.reinit!(bcv::BezierFaceValues, bc::BezierCoords, faceid::Int) bcv.cv_bezier.current_face[] = faceid bcv.cv_nurbs.current_face[] = faceid - _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, bc.xb, bc.wb, faceid) + _reinit_nurbs!( + bcv.cv_tmp, bcv.cv_bezier, + bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, + bc.xb, bcv.current_w, faceid) _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bc.w, faceid) + _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) end @@ -267,16 +432,23 @@ Similar to Ferrite's reinit method, but in IGA with NURBS, the weights is also n `xᴮ` - Bezier coordinates `w` - weights for nurbs mesh (not bezier weights) """ -function _reinit_nurbs!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.AbstractValues, xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}, cb::Int = 1) where {dim,T} +function _reinit_nurbs!( + cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.AbstractValues, + d²Bdξ²_geom, d²Bdξ²_func, d²Ndξ²_nurbs, d²NdX²_nurbs, + xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}, cb::Int = 1) where {dim,T} + n_geom_basefuncs = Ferrite.getngeobasefunctions(cv_bezier) n_func_basefuncs = Ferrite.getnbasefunctions(cv_bezier) @assert length(xᴮ) == n_geom_basefuncs == length(w) @assert typeof(cv_nurbs) == typeof(cv_bezier) - is_vector_valued = first(cv_nurbs.N) isa Tensor B = cv_bezier.M dBdξ = cv_bezier.dMdξ + is_vector_valued = first(cv_nurbs.N) isa Tensor + is_vector_valued && @assert eltype(d²Bdξ²_func) <: Tensor{3} + !is_vector_valued && @assert eltype(d²Bdξ²_func) <: Tensor{2} + qrweights = cv_bezier isa Ferrite.FaceValues ? Ferrite.getweights(cv_bezier.qr, cb) : Ferrite.getweights(cv_bezier.qr) @inbounds for (i,qr_w) in pairs(qrweights) @@ -287,39 +459,49 @@ function _reinit_nurbs!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.Abs dWdξ += w[j]*dBdξ[j, i, cb] end - fecv_J = zero(Tensor{2,dim}) + J = zero(Tensor{2,dim}) + H = zero(Tensor{3,dim}) for j in 1:n_geom_basefuncs # R = B[j, i, cb]./W dRdξ = inv(W)*dBdξ[j, i, cb] - inv(W^2)* dWdξ * B[j, i, cb] #Jacobian - fecv_J += xᴮ[j] ⊗ (w[j]*dRdξ) + J += xᴮ[j] ⊗ (w[j]*dRdξ) + + #Hessian + #NOTE: assume weights == 1.0 + d²Rdξ² = d²Bdξ²_geom[j, i, cb] + H += xᴮ[j] ⊗ (d²Rdξ²) end #Store nurbs for j in 1:n_func_basefuncs if is_vector_valued cv_nurbs.dNdξ[j, i, cb] = inv(W)*cv_bezier.dNdξ[j, i, cb] - inv(W^2) * cv_bezier.N[j, i, cb] ⊗ dWdξ + d²Ndξ²_nurbs[j, i, cb] = d²Bdξ²_func[j, i, cb] else cv_nurbs.dNdξ[j, i, cb] = inv(W)*cv_bezier.dNdξ[j, i, cb] - inv(W^2) * cv_bezier.N[j, i, cb] * dWdξ + d²Ndξ²_nurbs[j, i, cb] = d²Bdξ²_func[j, i, cb] end cv_nurbs.N[j,i,cb] = cv_bezier.N[j, i, cb]/W end if isa(cv_bezier, Ferrite.AbstractFaceValues) - weight_norm = Ferrite.weighted_normal(fecv_J, cv_bezier, cb) + weight_norm = Ferrite.weighted_normal(J, cv_bezier, cb) cv_bezier.normals[i] = weight_norm / norm(weight_norm) detJ = norm(weight_norm) else - detJ = det(fecv_J) + detJ = det(J) end detJ > 0.0 || throw(ArgumentError("det(J) is not positive: det(J) = $(detJ)")) cv_bezier.detJdV[i,cb] = detJ * qr_w - Jinv = inv(fecv_J) + Jinv = inv(J) for j in 1:n_func_basefuncs cv_nurbs.dNdx[j, i, cb] = cv_nurbs.dNdξ[j, i, cb] ⋅ Jinv + #@assert isapprox(norm(H), 0.0; atol = 1e-15) + d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv end end end @@ -335,4 +517,12 @@ function Base.show(io::IO, m::MIME"text/plain", fv::BezierFaceValues) print(io, "- Function interpolation: "); show(io, m, fv.cv_bezier.func_interp) println(io) print(io, "- Geometric interpolation: "); show(io, m, fv.cv_bezier.geo_interp) -end \ No newline at end of file +end + +function Base.show(io::IO, m::MIME"text/plain", cv::BezierCellValues) + println(io, "BezierCellValues with") + println(io, "- Quadrature rule with ", getnquadpoints(cv), " points") + print(io, "- Function interpolation: "); show(io, m, cv.cv_bezier.ip) + println(io) + print(io, "- Geometric interpolation: "); show(io, m, cv.cv_bezier.gip) +end diff --git a/test/test_meshes.jl b/test/test_meshes.jl index 58d1610..40aa867 100644 --- a/test/test_meshes.jl +++ b/test/test_meshes.jl @@ -31,7 +31,8 @@ function _get_problem_data(meshsymbol::Symbol, nels::NTuple{sdim,Int}, orders; m mesh = generate_nurbs_patch(meshsymbol, nels, orders; meshkwargs...) grid = BezierGrid(mesh) - bern_ip = Bernstein{Ferrite.RefHypercube{sdim}, mesh.orders}() + @assert allequal(orders) + bern_ip = Bernstein{Ferrite.RefHypercube{sdim}, orders[1]}() #Cell values qr = Ferrite.QuadratureRule{Ferrite.RefHypercube{sdim}}(5) @@ -68,7 +69,7 @@ function test_cube() end function test_square() - grid, cv, fv = _get_problem_data(:cube, (1,1,), (2,2,), cornerpos=(-1.0,-1.0), size=(2.0,3.0,)) + grid, cv, fv = _get_problem_data(:hypercube, (1,1,), (2,2,), cornerpos=(-1.0,-1.0), size=(2.0,3.0,)) addcellset!(grid, "all", (x)->true) addfaceset!(grid, "left", (x)-> x[1] ≈ -1.0) addfaceset!(grid, "right", (x)->x[1]≈1.0) diff --git a/test/test_values.jl b/test/test_values.jl index c0129df..0ac1564 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -3,10 +3,13 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pdim}, reorder) where {pdim,sdim} Ξ = nurbsmesh.knot_vectors - + bspline = BSplineBasis(Ξ, nurbsmesh.orders) + nbasefuncs = length(nurbsmesh.IEN[:, cellid]) # number of basefunctions per cell B = zeros(Float64, nbasefuncs) - dBdξ = zeros(Vec{pdim,Float64}, nbasefuncs) + dBdξ = zeros(Tensor{1,pdim,Float64}, nbasefuncs) + dBdξ = zeros(Tensor{2,pdim,Float64}, nbasefuncs) + for i in 1:nbasefuncs global_basefunk = nurbsmesh.IEN[i, cellid] @@ -21,6 +24,7 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pd value = 1.0 deriv = ones(Float64, pdim) + #ddN, dN, N = Tensors.hessian(x -> Ferrite.shape_value(bspline, x, iqp), Vec(ξηζ...), :all) for d in 1:pdim value *= IGA._bspline_basis_value_alg1(nurbsmesh.orders[d], Ξ[d], ni[d], ξηζ[d]) for d2 in 1:pdim @@ -33,8 +37,9 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pd end B[i] = value dBdξ[i] = Vec(Tuple(deriv)) ⋅ dξdξᴾ + #d²Bdξ²[i] = dξdξᴾ' ⋅ ddN ⋅ dξdξᴾ end - return B[reorder], dBdξ[reorder] + return B[reorder], dBdξ[reorder], d²Bdξ²[reorder] end @testset "bezier values construction" begin @@ -47,7 +52,7 @@ end qr = QuadratureRule{shape}(1) qr_face = FaceQuadratureRule{shape}(1) - cv = BezierCellValues( qr, ip, ip) + cv = CellValues( qr, ip, ip) cv2 = BezierCellValues( CellValues(qr, bip, bip) ) cv3 = CellValues( qr, ip, ip) @@ -55,12 +60,12 @@ end @test cv.cv_bezier.M == cv3.cv_bezier.M @test cv3 isa BezierCellValues - cv_vector1 = BezierCellValues( qr, ip^sdim, ip ) + cv_vector1 = CellValues( qr, ip^sdim, ip ) cv_vector2 = BezierCellValues( CellValues(qr, bip^sdim, bip) ) cv_vector3 = CellValues( qr, ip^sdim, ip ) - @test cv_vector.cv_bezier.M == cv_vector2.cv_bezier.M - @test cv_vector.cv_bezier.M == cv_vector3.cv_bezier.M + @test cv_vector1.cv_bezier.M == cv_vector2.cv_bezier.M + @test cv_vector1.cv_bezier.M == cv_vector3.cv_bezier.M @test cv_vector3 isa BezierCellValues @test Ferrite.getngeobasefunctions(cv_vector1) == getnbasefunctions(ip) @@ -73,9 +78,9 @@ end @testset "bezier values nurbs" begin dim = 2 - orders = (2,2) + order = 2 nels = (4,3) - nb_per_cell = prod(orders.+1) + nb_per_cell = (2+1)^dim ## # Build the problem @@ -85,21 +90,22 @@ end grid = BezierGrid(nurbsmesh) shape = Ferrite.RefHypercube{dim} - ip = Bernstein{shape, orders}() + ip = IGAInterpolation{shape, order}() + bip = Bernstein{shape, order}() - reorder = IGA._bernstein_ordering(ip) + reorder = IGA._bernstein_ordering(bip) qr = QuadratureRule{shape}(1) qr_face = FaceQuadratureRule{shape}(3) - fv = BezierFaceValues( qr_face, ip, ip ) - fv_vector = BezierFaceValues( qr_face, ip^dim, ip ) - cv = BezierCellValues( qr, ip, ip ) - cv_vector = BezierCellValues( qr, ip^dim, ip) + fv = FaceValues( qr_face, ip, ip ) + fv_vector = FaceValues( qr_face, ip^dim, ip ) + cv = CellValues( qr, ip, ip ) + cv_vector = CellValues( qr, ip^dim, ip) #Try some different cells for cellnum in [1,4,5] - Xb, wb, X, w = get_bezier_coordinates(grid, cellnum) + #Xb, wb, X, w = get_bezier_coordinates(grid, cellnum) #C = get_extraction_operator(grid, cellnum) #X = get_nurbs_coordinates(grid, cellnum) #w = Ferrite.getweights(grid, cellnum) @@ -111,7 +117,7 @@ end for (iqp, ξ) in enumerate(qr.points) #Calculate the value of the NURBS from the nurbs patch - N, dNdξ = bspline_values(nurbsmesh, cellnum, ξ, reorder) + N, dNdξ, d²Ndξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) Wb = sum(N.*w) dWbdξ = sum(dNdξ.*w) @@ -182,7 +188,7 @@ end qrw = qr_face.face_rules[faceidx].weights[iqp] #Calculate the value of the NURBS from the nurbs patch - N, dNdξ = bspline_values(nurbsmesh, cellnum, ξ, reorder) + N, dNdξ, d²Ndξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) Wb = sum(N.*w) dWbdξ = sum(dNdξ.*w) From fe555dc2a42a7cf048e6f896efcf72889a7d218a Mon Sep 17 00:00:00 2001 From: lijas Date: Mon, 13 Nov 2023 08:46:14 +0100 Subject: [PATCH 13/50] wip --- src/bezier_grid.jl | 2 +- src/splines/bezier_values.jl | 19 ++++++++++--------- test/test_values.jl | 19 ++++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 78c6f1b..48ce192 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -98,7 +98,7 @@ Returns the weights (for the nurbs interpolation) for cell with id `cellid`. end @inline function getweights!(w::Vector{T}, grid::BezierGrid, cell::Ferrite.AbstractCell) where {T} - @inbounds for i in 1:length(w) + for i in 1:length(w) w[i] = grid.weights[cell.nodes[i]] end return w diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 44bf696..2adc732 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -80,13 +80,13 @@ function BezierCellValues(cv::Ferrite.CellValues) end n_geom_basefuncs = getnbasefunctions(cv.gip) - n_func_basefuncs = getnbasefunctions(cv.gip) + n_func_basefuncs = getnbasefunctions(cv.ip) n_qpoints = getnquadpoints(cv) d²MdX² = fill(zero(d²MdX²_t) * T(NaN), n_geom_basefuncs, n_qpoints) d²NdX² = fill(zero(d²NdX²_t) * T(NaN), n_func_basefuncs, n_qpoints) for (qp, ξ) in pairs(Ferrite.getpoints(cv.qr)) - for ib in 1:n_func_basefuncs + for ib in 1:n_geom_basefuncs d²MdX²[ib, qp] = Tensors.hessian(ξ -> shape_value(cv.gip, ξ, ib), ξ) end for ib in 1:n_func_basefuncs @@ -96,8 +96,8 @@ function BezierCellValues(cv::Ferrite.CellValues) return BezierCellValues( cv, d²MdX², d²NdX², - deepcopy(cv), copy(d²NdX²), copy(d²NdX²) , - deepcopy(cv), copy(d²NdX²), copy(d²NdX²) , + deepcopy(cv), deepcopy(d²NdX²), deepcopy(d²NdX²) , + deepcopy(cv), deepcopy(d²NdX²), deepcopy(d²NdX²) , undef_beo, undef_w) end @@ -123,13 +123,13 @@ function BezierFaceValues(cv::Ferrite.FaceValues) end n_geom_basefuncs = getnbasefunctions(cv.geo_interp) - n_func_basefuncs = getnbasefunctions(cv.geo_interp) + n_func_basefuncs = getnbasefunctions(cv.func_interp) n_qpoints = getnquadpoints(cv.qr, 1) d²MdX² = fill(zero(d²MdX²_t) * T(NaN), n_geom_basefuncs, n_qpoints, nfaces) d²NdX² = fill(zero(d²NdX²_t) * T(NaN), n_func_basefuncs, n_qpoints, nfaces) for (qp, ξ) in pairs(Ferrite.getpoints(cv.qr, 1)), iface in 1:nfaces - for ib in 1:n_func_basefuncs + for ib in 1:n_geom_basefuncs d²MdX²[ib, qp, iface] = Tensors.hessian(ξ -> shape_value(cv.geo_interp, ξ, ib), ξ) end for ib in 1:n_func_basefuncs @@ -371,6 +371,7 @@ end function Ferrite.reinit!(bcv::BezierCellValues, xb::AbstractVector{<:Vec})#; updateflags = CellValuesFlags()) #Ferrite.reinit!(bcv.cv_bezier, xb) #call the normal reinit function first + @assert bcv.current_w .== 1.0 |> all _reinit_nurbs!( bcv.cv_tmp, bcv.cv_bezier, bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, @@ -406,8 +407,8 @@ function Ferrite.reinit!(bcv::BezierCellValues, bc::BezierCoords) _reinit_nurbs!( bcv.cv_tmp, bcv.cv_bezier, bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - bc.xb, bcv.current_w, 1) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bc.w, 1) + bc.xb, bc.wb, 1) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bcv.current_w, 1) _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) end @@ -450,7 +451,7 @@ function _reinit_nurbs!( !is_vector_valued && @assert eltype(d²Bdξ²_func) <: Tensor{2} qrweights = cv_bezier isa Ferrite.FaceValues ? Ferrite.getweights(cv_bezier.qr, cb) : Ferrite.getweights(cv_bezier.qr) - @inbounds for (i,qr_w) in pairs(qrweights) + for (i,qr_w) in pairs(qrweights) W = zero(T) dWdξ = zero(Vec{dim,T}) diff --git a/test/test_values.jl b/test/test_values.jl index 0ac1564..d6b59d3 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -8,7 +8,7 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pd nbasefuncs = length(nurbsmesh.IEN[:, cellid]) # number of basefunctions per cell B = zeros(Float64, nbasefuncs) dBdξ = zeros(Tensor{1,pdim,Float64}, nbasefuncs) - dBdξ = zeros(Tensor{2,pdim,Float64}, nbasefuncs) + d²Bdξ² = zeros(Tensor{2,pdim,Float64}, nbasefuncs) for i in 1:nbasefuncs global_basefunk = nurbsmesh.IEN[i, cellid] @@ -24,7 +24,7 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pd value = 1.0 deriv = ones(Float64, pdim) - #ddN, dN, N = Tensors.hessian(x -> Ferrite.shape_value(bspline, x, iqp), Vec(ξηζ...), :all) + #ddN, dN, N = Tensors.hessian(x -> Ferrite.shape_value(bspline, x, i), Vec(ξηζ...), :all) for d in 1:pdim value *= IGA._bspline_basis_value_alg1(nurbsmesh.orders[d], Ξ[d], ni[d], ξηζ[d]) for d2 in 1:pdim @@ -35,8 +35,8 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pd end end end - B[i] = value - dBdξ[i] = Vec(Tuple(deriv)) ⋅ dξdξᴾ + B[i] = N + dBdξ[i] = vec(Tuple(deriv)) ⋅ dξdξᴾ #d²Bdξ²[i] = dξdξᴾ' ⋅ ddN ⋅ dξdξᴾ end return B[reorder], dBdξ[reorder], d²Bdξ²[reorder] @@ -105,7 +105,7 @@ end #Try some different cells for cellnum in [1,4,5] - #Xb, wb, X, w = get_bezier_coordinates(grid, cellnum) + Xb, wb, X, w = get_bezier_coordinates(grid, cellnum) #C = get_extraction_operator(grid, cellnum) #X = get_nurbs_coordinates(grid, cellnum) #w = Ferrite.getweights(grid, cellnum) @@ -113,6 +113,7 @@ end bc = getcoordinates(grid, cellnum) reinit!(cv, bc) reinit!(cv_vector, bc) + # (; xb, wb, x, w) = bc for (iqp, ξ) in enumerate(qr.points) @@ -247,9 +248,9 @@ end @testset "bezier values give NaN" begin dim = 2 - orders = (2,2) + order = 2 nels = (4,3) - nb_per_cell = prod(orders.+1) + nb_per_cell = (order+1)^dim ## # Build the problem @@ -257,9 +258,9 @@ end nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) grid = BezierGrid(nurbsmesh) - ip = Bernstein{Ferrite.RefHypercube{dim}, orders}() + ip = Bernstein{Ferrite.RefHypercube{dim}, order}() qr = QuadratureRule{Ferrite.RefHypercube{dim}}(3) - cv = BezierCellValues(qr, ip, ip) + cv = BezierCellValues(CellValues(qr, ip, ip)) Xb, wb = get_bezier_coordinates(grid, 1) w = Ferrite.getweights(grid, 1) From 0e1fa5a6f66222b22c17dd5467dbc87eb558b6e6 Mon Sep 17 00:00:00 2001 From: lijas Date: Mon, 13 Nov 2023 17:33:53 +0100 Subject: [PATCH 14/50] wip higher order --- src/nurbsmesh.jl | 2 +- src/splines/bezier_values.jl | 12 +++- src/splines/bsplines.jl | 4 +- test/test_values.jl | 117 +++++++++++++++++++++++------------ 4 files changed, 89 insertions(+), 46 deletions(-) diff --git a/src/nurbsmesh.jl b/src/nurbsmesh.jl index 3569a88..23d61bb 100644 --- a/src/nurbsmesh.jl +++ b/src/nurbsmesh.jl @@ -42,7 +42,7 @@ struct NURBSMesh{pdim,sdim,T} #<: Ferrite.AbstractGrid end function Base.show(io::IO, ::MIME"text/plain", grid::NURBSMesh) - print(io, "$(typeof(NURBSMesh)) and with order $(grid.orders) and $(getncells(grid)) cells and $(getnnodes(grid)) nodes (contorl points) ") + print(io, "NURBSMesh and with order $(grid.orders) and $(getncells(grid)) cells and $(getnnodes(grid)) nodes (control points) ") end Ferrite.getncells(mesh::NURBSMesh) = size(mesh.IEN, 2) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 2adc732..36e3c3d 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -330,8 +330,11 @@ function _cellvalues_bezier_extraction_higher_order!( Cbe::BezierExtractionOperator{T}, w::Optional{Vector{T}}, faceid::Int) where {T, dim_s, d²Ndξ²_t <: Tensor{<:Any,dim_s}} is_scalar_valued = !(eltype(d²Ndξ²_nurbs) <: Tensor{3}) + ngeobasefunctions = size(d²Ndξ²_nurbs, 1) - ngeobasefunctions = size(d²Ndξ²_nurbs, 1) ÷ dim_s + if !is_scalar_valued + ngeobasefunctions ÷= dim_s + end n_quad_ponts = size(d²Ndξ²_nurbs, 2) for iq in 1:n_quad_ponts @@ -458,6 +461,7 @@ function _reinit_nurbs!( for j in 1:n_geom_basefuncs W += w[j]*B[j, i, cb] dWdξ += w[j]*dBdξ[j, i, cb] + #d²Wdξ² += w[j]*d²Bdξ²_geom[j, i, cb] end J = zero(Tensor{2,dim}) @@ -501,8 +505,10 @@ function _reinit_nurbs!( Jinv = inv(J) for j in 1:n_func_basefuncs cv_nurbs.dNdx[j, i, cb] = cv_nurbs.dNdξ[j, i, cb] ⋅ Jinv - #@assert isapprox(norm(H), 0.0; atol = 1e-15) - d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv + #@assert isapprox(norm(H), 0.0; atol = 1e-14) + #d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv + FF = cv_nurbs.dNdx[j, i, cb] ⋅ H + d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv - Jinv'⋅FF⋅Jinv end end end diff --git a/src/splines/bsplines.jl b/src/splines/bsplines.jl index d77ddaf..b24ab0a 100644 --- a/src/splines/bsplines.jl +++ b/src/splines/bsplines.jl @@ -24,13 +24,13 @@ end getnbasefunctions_dim(basis::BSplineBasis{dim,T,order}) where {dim,T,order} = ntuple(i -> length(basis.knot_vector[i]) - order[i] - 1, dim) Ferrite.getnbasefunctions(basis::BSplineBasis{dim,T,order}) where {dim,T,order} = prod(getnbasefunctions_dim(basis)) -function Ferrite.shape_value(b::BSplineBasis{dim,T,order}, xi::Vec{dim}, i::Int) where {dim,T,order} +function Ferrite.shape_value(b::BSplineBasis{dim,T,order}, xi::Vec{dim,T2}, i::Int) where {dim,T,T2,order} @assert( i <= Ferrite.getnbasefunctions(b)) _n = getnbasefunctions_dim(b) indecies = CartesianIndices(_n)[i] - val = 1.0 + val = one(T2) for i in 1:dim val *= IGA._bspline_basis_value_alg1(order[i], b.knot_vector[i], indecies[i], xi[i]) end diff --git a/test/test_values.jl b/test/test_values.jl index d6b59d3..01bd85f 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -6,40 +6,57 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pd bspline = BSplineBasis(Ξ, nurbsmesh.orders) nbasefuncs = length(nurbsmesh.IEN[:, cellid]) # number of basefunctions per cell - B = zeros(Float64, nbasefuncs) - dBdξ = zeros(Tensor{1,pdim,Float64}, nbasefuncs) - d²Bdξ² = zeros(Tensor{2,pdim,Float64}, nbasefuncs) + N = zeros(Float64, nbasefuncs) + dNdξ = zeros(Tensor{1,pdim,Float64}, nbasefuncs) + d²Ndξ² = zeros(Tensor{2,pdim,Float64}, nbasefuncs) + + R = zeros(Float64, nbasefuncs) + dRdξ = zeros(Tensor{1,pdim,Float64}, nbasefuncs) + d²Rdξ² = zeros(Tensor{2,pdim,Float64}, nbasefuncs) for i in 1:nbasefuncs global_basefunk = nurbsmesh.IEN[i, cellid] _ni = nurbsmesh.INN[nurbsmesh.IEN[end,cellid],1:pdim] - ni = nurbsmesh.INN[global_basefunk,:] # Defines the basis functions nurbs coord - - #Map to parametric domain from parent domain - ξηζ = [0.5*((Ξ[d][_ni[d]+1] - Ξ[d][_ni[d]])*xi[d] + (Ξ[d][_ni[d]+1] + Ξ[d][_ni[d]])) for d in 1:pdim] - _dξdξᴾ = [0.5*(Ξ[d][_ni[d]+1] - Ξ[d][_ni[d]]) for d in 1:pdim] - dξdξᴾ = Tensor{2,pdim}(Tuple((_dξdξᴾ[1], 0.0, 0.0, _dξdξᴾ[2]))) - - value = 1.0 - deriv = ones(Float64, pdim) - #ddN, dN, N = Tensors.hessian(x -> Ferrite.shape_value(bspline, x, i), Vec(ξηζ...), :all) - for d in 1:pdim - value *= IGA._bspline_basis_value_alg1(nurbsmesh.orders[d], Ξ[d], ni[d], ξηζ[d]) - for d2 in 1:pdim - if d == d2 - deriv[d2] *= gradient( (xi) -> IGA._bspline_basis_value_alg1(nurbsmesh.orders[d], Ξ[d], ni[d], xi), ξηζ[d]) - else - deriv[d2] *= IGA._bspline_basis_value_alg1(nurbsmesh.orders[d], Ξ[d], ni[d], ξηζ[d]) - end + + function __bspline_shape_value__(xi::Vec{pdim,T}, _ni, ni, Ξ, nurbsmesh) where T + ξηζ = Vec{pdim}(d->0.5*((Ξ[d][_ni[d]+1] - Ξ[d][_ni[d]])*xi[d] + (Ξ[d][_ni[d]+1] + Ξ[d][_ni[d]]))) + value = one(T) + for d in 1:pdim + value *= IGA._bspline_basis_value_alg1(nurbsmesh.orders[d], Ξ[d], ni[d], ξηζ[d]) end + return value end - B[i] = N - dBdξ[i] = vec(Tuple(deriv)) ⋅ dξdξᴾ - #d²Bdξ²[i] = dξdξᴾ' ⋅ ddN ⋅ dξdξᴾ + + function __nurbs_shape_value__(xi::Vec{pdim,T}) where T + W = zero(T) + _Nvec = zeros(T, nbasefuncs) + _wvec = zeros(Float64, nbasefuncs) + for j in 1:nbasefuncs + global_base = nurbsmesh.IEN[j, cellid] + local_ni = nurbsmesh.INN[global_base,:] + + Nj = __bspline_shape_value__(xi, _ni, local_ni, Ξ, nurbsmesh) + W += (nurbsmesh.weights[global_base] * Nj) + _Nvec[j] = Nj + _wvec[j] = nurbsmesh.weights[global_base] + end + return _Nvec[i]*_wvec[i]/W + end + + _ddN, _dN, _N = Tensors.hessian(x->__bspline_shape_value__(x, _ni, ni, Ξ, nurbsmesh), xi, :all) + _ddR, _dR, _R = Tensors.hessian(x->__nurbs_shape_value__(x), xi, :all) + + d²Ndξ²[i] = _ddN + dNdξ[i] = _dN + N[i] = _N + + d²Rdξ²[i] = _ddR + dRdξ[i] = _dR + R[i] = _R end - return B[reorder], dBdξ[reorder], d²Bdξ²[reorder] + return N[reorder], dNdξ[reorder], d²Ndξ²[reorder], R[reorder], dRdξ[reorder], d²Rdξ²[reorder] end @testset "bezier values construction" begin @@ -86,6 +103,8 @@ end # Build the problem ## nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) + #nurbsmesh = generate_nurbs_patch(:rectangle, nels, (order,order), size = (20.0,20.0)) + nurbsmesh.weights .= 1.0 grid = BezierGrid(nurbsmesh) shape = Ferrite.RefHypercube{dim} @@ -104,8 +123,7 @@ end cv_vector = CellValues( qr, ip^dim, ip) #Try some different cells - for cellnum in [1,4,5] - Xb, wb, X, w = get_bezier_coordinates(grid, cellnum) + for cellnum in 1:getncells(grid)#[1,4,5] #C = get_extraction_operator(grid, cellnum) #X = get_nurbs_coordinates(grid, cellnum) #w = Ferrite.getweights(grid, cellnum) @@ -113,12 +131,12 @@ end bc = getcoordinates(grid, cellnum) reinit!(cv, bc) reinit!(cv_vector, bc) - # (; xb, wb, x, w) = bc + (; xb, wb, x, w) = bc for (iqp, ξ) in enumerate(qr.points) #Calculate the value of the NURBS from the nurbs patch - N, dNdξ, d²Ndξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) + N, dNdξ, d²Ndξ², R2, dR2dξ, d²R2dξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) Wb = sum(N.*w) dWbdξ = sum(dNdξ.*w) @@ -129,18 +147,31 @@ end dRdξ_patch[i] = w[i]*(1/Wb * dNdξ[i] - inv(Wb^2)*dWbdξ * N[i]) end - J = sum(X .⊗ dRdξ_patch) + J = sum(x .⊗ dRdξ_patch) + H = sum(x .⊗ d²R2dξ²) dV_patch = det(J)*qr.weights[iqp] dRdX_patch = similar(dNdξ) for i in 1:nb_per_cell dRdX_patch[i] = dRdξ_patch[i] ⋅ inv(J) end + + d²RdX² = similar(d²R2dξ²) + for i in 1:nb_per_cell + FF = dRdX_patch[i] ⋅ H + d²RdX²[i] = inv(J)' ⋅ d²R2dξ²[i] ⋅ inv(J) - inv(J)' ⋅ FF ⋅ inv(J) + end @test dV_patch ≈ getdetJdV(cv, iqp) @test sum(cv.cv_nurbs.N[:,iqp]) ≈ 1 - @test all(cv.cv_nurbs.dNdξ[:,iqp] .≈ dRdξ_patch) + @test cv.cv_nurbs.N[:,iqp] ≈ R_patch + @test cv.cv_nurbs.dNdξ[:,iqp] ≈ dRdξ_patch + @test R2 ≈ R_patch + @test dR2dξ ≈ dRdξ_patch @test cv.cv_nurbs.dNdx[:,iqp] ≈ dRdX_patch + @test d²R2dξ² ≈ d²Ndξ² atol=1e-14 + @test cv.d²Ndξ²[:,iqp] ≈ d²Ndξ² atol=1e-14 + @test cv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 #Check if VectorValues is same as ScalarValues basefunc_count = 1 @@ -171,7 +202,7 @@ end end end - addfaceset!(grid, "face1", (x)-> x[1] == -4.0) + addfaceset!(grid, "face1", (x)-> x[1] == -40.0) for (cellnum, faceidx) in getfaceset(grid, "face1") Xb, wb = get_bezier_coordinates(grid, cellnum) @@ -189,7 +220,7 @@ end qrw = qr_face.face_rules[faceidx].weights[iqp] #Calculate the value of the NURBS from the nurbs patch - N, dNdξ, d²Ndξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) + N, dNdξ, d²Ndξ², R2, dR2dξ, d²R2dξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) Wb = sum(N.*w) dWbdξ = sum(dNdξ.*w) @@ -208,10 +239,16 @@ end dRdX_patch[i] = dRdξ_patch[i] ⋅ inv(J) end - @test dV_patch ≈ getdetJdV(fv, iqp) - @test sum(fv.cv_nurbs.N[:,iqp, faceidx]) ≈ 1 - @test all(fv.cv_nurbs.dNdξ[:,iqp, faceidx] .≈ dRdξ_patch) - @test all(fv.cv_nurbs.dNdx[:,iqp, faceidx] .≈ dRdX_patch) + @test dV_patch ≈ getdetJdV(cv, iqp) + @test sum(cv.cv_nurbs.N[:,iqp]) ≈ 1 + @test cv.cv_nurbs.N[:,iqp] ≈ R_patch + @test cv.cv_nurbs.dNdξ[:,iqp] ≈ dRdξ_patch + @test R2 ≈ R_patch + @test dR2dξ ≈ dRdξ_patch + @test cv.cv_nurbs.dNdx[:,iqp] ≈ dRdX_patch + @test d²R2dξ² ≈ d²Ndξ² atol=1e-14 + @test cv.d²Ndξ²[:,iqp] ≈ d²Ndξ² atol=1e-14 + @test cv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 #Check if VectorValues is same as ScalarValues basefunc_count = 1 @@ -221,7 +258,6 @@ end N_comp[comp] = fv.cv_nurbs.N[i, iqp, faceidx] _N = Vec{dim,Float64}((N_comp...,)) - @show fv_vector.cv_nurbs.N[basefunc_count, iqp, faceidx] _N @test fv_vector.cv_nurbs.N[basefunc_count, iqp, faceidx] ≈ _N dN_comp = zeros(Float64, dim, dim) @@ -244,7 +280,7 @@ end end - +#= @testset "bezier values give NaN" begin dim = 2 @@ -319,4 +355,5 @@ end @test norm(x) ≈ ro end end -end \ No newline at end of file +end +=# \ No newline at end of file From 8a07d6d3904223bd05fb175a85fa0a6eaa3333b2 Mon Sep 17 00:00:00 2001 From: lijas Date: Tue, 14 Nov 2023 10:38:57 +0100 Subject: [PATCH 15/50] cellvalues with hessian and weights working --- src/splines/bezier_values.jl | 63 ++++++++++++++++++++++++++++-------- test/test_meshes.jl | 6 ++-- test/test_values.jl | 58 ++++++++++++--------------------- 3 files changed, 73 insertions(+), 54 deletions(-) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 36e3c3d..4b26578 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -412,7 +412,7 @@ function Ferrite.reinit!(bcv::BezierCellValues, bc::BezierCoords) bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bc.xb, bc.wb, 1) _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bcv.current_w, 1) - _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) + _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], bcv.current_w, 1) end function Ferrite.reinit!(bcv::BezierFaceValues, bc::BezierCoords, faceid::Int) @@ -423,7 +423,7 @@ function Ferrite.reinit!(bcv::BezierFaceValues, bc::BezierCoords, faceid::Int) _reinit_nurbs!( bcv.cv_tmp, bcv.cv_bezier, bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - bc.xb, bcv.current_w, faceid) + bc.xb, bc.wb, faceid) _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bc.w, faceid) _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) end @@ -446,6 +446,7 @@ function _reinit_nurbs!( @assert length(xᴮ) == n_geom_basefuncs == length(w) @assert typeof(cv_nurbs) == typeof(cv_bezier) + hessian = true B = cv_bezier.M dBdξ = cv_bezier.dMdξ @@ -458,36 +459,70 @@ function _reinit_nurbs!( W = zero(T) dWdξ = zero(Vec{dim,T}) + d²Wdξ² = zero(Tensor{2,dim,T}) for j in 1:n_geom_basefuncs - W += w[j]*B[j, i, cb] - dWdξ += w[j]*dBdξ[j, i, cb] - #d²Wdξ² += w[j]*d²Bdξ²_geom[j, i, cb] + W += w[j]*B[j, i, cb] + dWdξ += w[j]*dBdξ[j, i, cb] + d²Wdξ² += w[j]*d²Bdξ²_geom[j, i, cb] end J = zero(Tensor{2,dim}) H = zero(Tensor{3,dim}) for j in 1:n_geom_basefuncs - # - R = B[j, i, cb]./W - dRdξ = inv(W)*dBdξ[j, i, cb] - inv(W^2)* dWdξ * B[j, i, cb] + S = W^2 + Fi = dBdξ[j, i, cb]*W - B[j, i, cb]*dWdξ + dRdξ = Fi/S #Jacobian J += xᴮ[j] ⊗ (w[j]*dRdξ) #Hessian - #NOTE: assume weights == 1.0 - d²Rdξ² = d²Bdξ²_geom[j, i, cb] - H += xᴮ[j] ⊗ (d²Rdξ²) + if hessian + Fi_j = (d²Bdξ²_geom[j, i, cb]*W + dBdξ[j, i, cb]⊗dWdξ) - (dWdξ⊗dBdξ[j, i, cb] + B[j, i, cb]*d²Wdξ²) + S_j = 2*W*dWdξ + + d²Rdξ² = (Fi_j*S - Fi⊗S_j)/S^2 + H += xᴮ[j] ⊗ (w[j]*d²Rdξ²) + end end #Store nurbs for j in 1:n_func_basefuncs if is_vector_valued cv_nurbs.dNdξ[j, i, cb] = inv(W)*cv_bezier.dNdξ[j, i, cb] - inv(W^2) * cv_bezier.N[j, i, cb] ⊗ dWdξ - d²Ndξ²_nurbs[j, i, cb] = d²Bdξ²_func[j, i, cb] + + if hessian + _B = cv_bezier.N[j, i, cb] + _dBdξ = cv_bezier.dNdξ[j, i, cb] + _d²Bdξ² = d²Bdξ²_func[j, i, cb] + tmp = _dBdξ⊗dWdξ + tmp = permutedims(tmp, (1,3,2)) + tmp = Tensor{3,dim}(tmp) + + Fij = _dBdξ*W - _B⊗dWdξ + S = W^2 + Fij_k = (_d²Bdξ²*W + _dBdξ⊗dWdξ) - (tmp + _B⊗d²Wdξ²) + S_k = 2*W*dWdξ + + d²Ndξ²_nurbs[j, i, cb] = (Fij_k*S - Fij⊗S_k)/S^2 + end else - cv_nurbs.dNdξ[j, i, cb] = inv(W)*cv_bezier.dNdξ[j, i, cb] - inv(W^2) * cv_bezier.N[j, i, cb] * dWdξ - d²Ndξ²_nurbs[j, i, cb] = d²Bdξ²_func[j, i, cb] + _B = cv_bezier.N[j, i, cb] + _dBdξ = cv_bezier.dNdξ[j, i, cb] + S = W^2 + Fi = _dBdξ*W - _B⊗dWdξ + #cv_nurbs.dNdξ[j, i, cb] = inv(W)*cv_bezier.dNdξ[j, i, cb] - inv(W^2) * cv_bezier.N[j, i, cb] * dWdξ + cv_nurbs.dNdξ[j, i, cb] = Fi/S + + if hessian + _d²Bdξ² = d²Bdξ²_func[j, i, cb] + S = W^2 + Fi = _dBdξ*W - _B⊗dWdξ + Fi_j = (_d²Bdξ²*W + _dBdξ⊗dWdξ) - (dWdξ⊗_dBdξ + _B⊗d²Wdξ²) + S_j = 2*W*dWdξ + d²Ndξ²_nurbs[j, i, cb] = (Fi_j*S - Fi⊗S_j)/S^2 + end + end cv_nurbs.N[j,i,cb] = cv_bezier.N[j, i, cb]/W end diff --git a/test/test_meshes.jl b/test/test_meshes.jl index 40aa867..6df53b6 100644 --- a/test/test_meshes.jl +++ b/test/test_meshes.jl @@ -32,15 +32,15 @@ function _get_problem_data(meshsymbol::Symbol, nels::NTuple{sdim,Int}, orders; m grid = BezierGrid(mesh) @assert allequal(orders) - bern_ip = Bernstein{Ferrite.RefHypercube{sdim}, orders[1]}() + bern_ip = IGAInterpolation{Ferrite.RefHypercube{sdim}, orders[1]}() #Cell values qr = Ferrite.QuadratureRule{Ferrite.RefHypercube{sdim}}(5) - cv = IGA.BezierCellValues(qr, bern_ip, bern_ip) + cv = CellValues(qr, bern_ip, bern_ip) #Face values qr = FaceQuadratureRule{Ferrite.RefHypercube{sdim}}(5) - fv = IGA.BezierFaceValues(qr, bern_ip, bern_ip) + fv = FaceValues(qr, bern_ip, bern_ip) return grid, cv, fv end diff --git a/test/test_values.jl b/test/test_values.jl index 01bd85f..5391acb 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -44,19 +44,14 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pd end return _Nvec[i]*_wvec[i]/W end - - _ddN, _dN, _N = Tensors.hessian(x->__bspline_shape_value__(x, _ni, ni, Ξ, nurbsmesh), xi, :all) + _ddR, _dR, _R = Tensors.hessian(x->__nurbs_shape_value__(x), xi, :all) - d²Ndξ²[i] = _ddN - dNdξ[i] = _dN - N[i] = _N - d²Rdξ²[i] = _ddR dRdξ[i] = _dR R[i] = _R end - return N[reorder], dNdξ[reorder], d²Ndξ²[reorder], R[reorder], dRdξ[reorder], d²Rdξ²[reorder] + return R[reorder], dRdξ[reorder], d²Rdξ²[reorder] end @testset "bezier values construction" begin @@ -104,7 +99,7 @@ end ## nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) #nurbsmesh = generate_nurbs_patch(:rectangle, nels, (order,order), size = (20.0,20.0)) - nurbsmesh.weights .= 1.0 + #nurbsmesh.weights .= 1.0 grid = BezierGrid(nurbsmesh) shape = Ferrite.RefHypercube{dim} @@ -131,47 +126,36 @@ end bc = getcoordinates(grid, cellnum) reinit!(cv, bc) reinit!(cv_vector, bc) + @show cv.current_w (; xb, wb, x, w) = bc for (iqp, ξ) in enumerate(qr.points) #Calculate the value of the NURBS from the nurbs patch - N, dNdξ, d²Ndξ², R2, dR2dξ, d²R2dξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) - - Wb = sum(N.*w) - dWbdξ = sum(dNdξ.*w) - R_patch = w.*N/Wb - - dRdξ_patch = similar(dNdξ) - for i in 1:nb_per_cell - dRdξ_patch[i] = w[i]*(1/Wb * dNdξ[i] - inv(Wb^2)*dWbdξ * N[i]) - end + R, dRdξ, d²Rdξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) - J = sum(x .⊗ dRdξ_patch) - H = sum(x .⊗ d²R2dξ²) + J = sum(x .⊗ dRdξ) + H = sum(x .⊗ d²Rdξ²) dV_patch = det(J)*qr.weights[iqp] - dRdX_patch = similar(dNdξ) + dRdX = similar(dRdξ) for i in 1:nb_per_cell - dRdX_patch[i] = dRdξ_patch[i] ⋅ inv(J) + dRdX[i] = dRdξ[i] ⋅ inv(J) end - - d²RdX² = similar(d²R2dξ²) + + d²RdX² = similar(d²Rdξ²) for i in 1:nb_per_cell - FF = dRdX_patch[i] ⋅ H - d²RdX²[i] = inv(J)' ⋅ d²R2dξ²[i] ⋅ inv(J) - inv(J)' ⋅ FF ⋅ inv(J) + FF = dRdX[i] ⋅ H + d²RdX²[i] = inv(J)' ⋅ d²Rdξ²[i] ⋅ inv(J) - inv(J)' ⋅ FF ⋅ inv(J) end @test dV_patch ≈ getdetJdV(cv, iqp) @test sum(cv.cv_nurbs.N[:,iqp]) ≈ 1 - @test cv.cv_nurbs.N[:,iqp] ≈ R_patch - @test cv.cv_nurbs.dNdξ[:,iqp] ≈ dRdξ_patch - @test R2 ≈ R_patch - @test dR2dξ ≈ dRdξ_patch - @test cv.cv_nurbs.dNdx[:,iqp] ≈ dRdX_patch - @test d²R2dξ² ≈ d²Ndξ² atol=1e-14 - @test cv.d²Ndξ²[:,iqp] ≈ d²Ndξ² atol=1e-14 - @test cv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 + @test cv.cv_nurbs.N[:,iqp] ≈ R + @test cv.cv_nurbs.dNdξ[:,iqp] ≈ dRdξ + @test cv.cv_nurbs.dNdx[:,iqp] ≈ dRdX + @test cv.d²Ndξ²[:,iqp] ≈ d²Rdξ² atol=1e-14 + @test cv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 #Check if VectorValues is same as ScalarValues basefunc_count = 1 @@ -181,19 +165,19 @@ end N_comp[comp] = cv.cv_nurbs.N[i, iqp] _N = Vec{dim,Float64}((N_comp...,)) - @test cv_vector.cv_nurbs.N[basefunc_count, iqp] ≈ _N + @test cv_vector.cv_nurbs.N[basefunc_count, iqp] ≈ _N atol = 1e-15 dN_comp = zeros(Float64, dim, dim) dN_comp[comp, :] = cv.cv_nurbs.dNdξ[i, iqp] _dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) - @test cv_vector.cv_nurbs.dNdξ[basefunc_count, iqp] ≈ _dNdξ + @test cv_vector.cv_nurbs.dNdξ[basefunc_count, iqp] ≈ _dNdξ atol = 1e-15 dN_comp = zeros(Float64, dim, dim) dN_comp[comp, :] = cv.cv_nurbs.dNdx[i, iqp] _dNdx = Tensor{2,dim,Float64}((dN_comp...,)) - @test cv_vector.cv_nurbs.dNdx[basefunc_count, iqp] ≈ _dNdx + @test cv_vector.cv_nurbs.dNdx[basefunc_count, iqp] ≈ _dNdx atol = 1e-15 basefunc_count += 1 end From 48768cf9a58d81254f2abef8eb28a79c4f2b790c Mon Sep 17 00:00:00 2001 From: lijas Date: Tue, 14 Nov 2023 10:41:05 +0100 Subject: [PATCH 16/50] WIP face test --- test/test_values.jl | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/test/test_values.jl b/test/test_values.jl index 5391acb..06faec0 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -186,7 +186,7 @@ end end end - addfaceset!(grid, "face1", (x)-> x[1] == -40.0) + addfaceset!(grid, "face1", (x)-> x[1] == -4.0) for (cellnum, faceidx) in getfaceset(grid, "face1") Xb, wb = get_bezier_coordinates(grid, cellnum) @@ -204,35 +204,33 @@ end qrw = qr_face.face_rules[faceidx].weights[iqp] #Calculate the value of the NURBS from the nurbs patch - N, dNdξ, d²Ndξ², R2, dR2dξ, d²R2dξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) + R, dRdξ, d²Rdξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) - Wb = sum(N.*w) - dWbdξ = sum(dNdξ.*w) - R_patch = w.*N/Wb - - dRdξ_patch = similar(dNdξ) - for i in 1:nb_per_cell - dRdξ_patch[i] = w[i]*(1/Wb * dNdξ[i] - inv(Wb^2)*dWbdξ * N[i]) - end + #Calculate the value of the NURBS from the nurbs patch + R, dRdξ, d²Rdξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) - J = sum(X .⊗ dRdξ_patch) + J = sum(x .⊗ dRdξ) + H = sum(x .⊗ d²Rdξ²) dV_patch = norm(Ferrite.weighted_normal(J, shape, faceidx))*qrw - dRdX_patch = similar(dNdξ) + dRdX = similar(dRdξ) + for i in 1:nb_per_cell + dRdX[i] = dRdξ[i] ⋅ inv(J) + end + + d²RdX² = similar(d²Rdξ²) for i in 1:nb_per_cell - dRdX_patch[i] = dRdξ_patch[i] ⋅ inv(J) + FF = dRdX[i] ⋅ H + d²RdX²[i] = inv(J)' ⋅ d²Rdξ²[i] ⋅ inv(J) - inv(J)' ⋅ FF ⋅ inv(J) end @test dV_patch ≈ getdetJdV(cv, iqp) - @test sum(cv.cv_nurbs.N[:,iqp]) ≈ 1 - @test cv.cv_nurbs.N[:,iqp] ≈ R_patch - @test cv.cv_nurbs.dNdξ[:,iqp] ≈ dRdξ_patch - @test R2 ≈ R_patch - @test dR2dξ ≈ dRdξ_patch - @test cv.cv_nurbs.dNdx[:,iqp] ≈ dRdX_patch - @test d²R2dξ² ≈ d²Ndξ² atol=1e-14 - @test cv.d²Ndξ²[:,iqp] ≈ d²Ndξ² atol=1e-14 - @test cv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 + @test sum(fv.cv_nurbs.N[:,iqp]) ≈ 1 + @test fv.cv_nurbs.N[:,iqp] ≈ R + @test fv.cv_nurbs.dNdξ[:,iqp] ≈ dRdξ + @test fv.cv_nurbs.dNdx[:,iqp] ≈ dRdX + @test fv.d²Ndξ²[:,iqp] ≈ d²Rdξ² atol=1e-14 + @test fv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 #Check if VectorValues is same as ScalarValues basefunc_count = 1 From 021b502946c599b5e9a0c1e07a32b1a51d6dd1e8 Mon Sep 17 00:00:00 2001 From: lijas Date: Wed, 15 Nov 2023 17:31:31 +0100 Subject: [PATCH 17/50] hessian getters --- src/splines/bezier_values.jl | 50 ++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 4b26578..e38abf3 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -51,6 +51,9 @@ struct BezierFaceValues{T<:Real,CV<:Ferrite.FaceValues, d²MdX²_t, d²NdX²_t} current_w::Vector{T} end +Ferrite.shape_value_type(cv::Union{BezierCellValues, BezierFaceValues}) = Ferrite.shape_value_type(cv.cv_bezier) +Ferrite.shape_gradient_type(cv::Union{BezierCellValues, BezierFaceValues}) = Ferrite.shape_gradient_type(cv.cv_bezier) + BezierCellAndFaceValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFaceValues{T,CV}} function Ferrite.checkface(fv::BezierFaceValues, face::Int) @@ -59,6 +62,7 @@ function Ferrite.checkface(fv::BezierFaceValues, face::Int) end Ferrite.nfaces(fv::BezierFaceValues) = size(fv.cv_nurbs.N, 3) +Ferrite.getnormal(fv::BezierFaceValues, iqp::Int) = getnormal(fv.cv_bezier, iqp) function BezierCellValues(cv::Ferrite.CellValues) T = eltype(cv.M) @@ -224,6 +228,8 @@ Ferrite.geometric_value(cv::BezierCellAndFaceValues, q_point::Int, i::Int) = Fer Ferrite.shape_gradient(bcv::BezierCellAndFaceValues, q_point::Int, i::Int) = Ferrite.shape_gradient(bcv.cv_nurbs, q_point, i) Ferrite.geometric_value(cv::BezierCellValues, q_point::Int, base_func::Int) = cv.cv_bezier.M[base_func, q_point] Ferrite.geometric_value(cv::BezierFaceValues, q_point::Int, base_func::Int) = cv.cv_bezier.M[base_func, q_point, cv.cv_bezier.current_face[]] +shape_hessian(bv::BezierFaceValues, q_point::Int, base_func::Int) = bv.d²NdX²[base_func, q_point, bv.cv_bezier.current_face[]] +shape_hessian(bv::BezierCellValues, q_point::Int, base_func::Int) = bv.d²NdX²[base_func, q_point] function Ferrite.function_symmetric_gradient(bv::BezierCellAndFaceValues, q_point::Int, u::AbstractVector) return function_symmetric_gradient(bv.cv_nurbs, q_point, u) @@ -238,6 +244,22 @@ function Ferrite.function_value(fe_v::BezierCellAndFaceValues, q_point::Int, u:: return function_value(fe_v.cv_nurbs, q_point, u) end +# TODO: Deprecate this, nobody is using this in practice... +function function_hessian(fe_v::Ferrite.AbstractValues, q_point::Int, u::AbstractVector{<:Vec}) + n_base_funcs = getnbasefunctions(fe_v) + length(u) == n_base_funcs || Ferrite.throw_incompatible_dof_length(length(u), n_base_funcs) + @boundscheck Ferrite.checkquadpoint(fe_v, q_point) + hess = function_hessian_init(fe_v, u) + @inbounds for i in 1:n_base_funcs + hess += u[i] ⊗ shape_hessian(fe_v, q_point, i) + end + return hess +end +shape_hessian_type(::Union{BezierCellValues{<:Any, <:Any, <:Any, d²NdX²_t}, BezierFaceValues{<:Any, <:Any, <:Any, d²NdX²_t}}) where d²NdX²_t = d²NdX²_t +function function_hessian_init(cv::Ferrite.AbstractValues, ::AbstractVector{T}) where {T} + return zero(shape_hessian_type(cv)) * zero(T) +end + function set_bezier_operator!(bcv::BezierCellAndFaceValues, beo::BezierExtractionOperator{T}) where T bcv.current_beo[]=beo end @@ -374,21 +396,29 @@ end function Ferrite.reinit!(bcv::BezierCellValues, xb::AbstractVector{<:Vec})#; updateflags = CellValuesFlags()) #Ferrite.reinit!(bcv.cv_bezier, xb) #call the normal reinit function first - @assert bcv.current_w .== 1.0 |> all + @assert all(bcv.current_w .== 1.0) + wb = bcv.current_w _reinit_nurbs!( bcv.cv_tmp, bcv.cv_bezier, bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - xb, bcv.current_w, 1) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_bezier, bcv.current_beo[], nothing, 1) + xb, wb, 1) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], nothing, 1) _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) end function Ferrite.reinit!(bcv::BezierFaceValues, xb::AbstractVector{<:Vec}, faceid::Int) - Ferrite.reinit!(bcv.cv_bezier, xb, faceid) #call the normal reinit function first + @assert all(bcv.current_w .== 1.0) bcv.cv_nurbs.current_face[] = faceid bcv.cv_bezier.current_face[] = faceid - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_bezier, bcv.current_beo[], nothing, faceid) + wb = bcv.current_w # borrow + _reinit_nurbs!( + bcv.cv_tmp, bcv.cv_bezier, + bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, + xb, wb, faceid) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], nothing, faceid) + _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, faceid) + end function Ferrite.reinit!(bcv::BezierCellValues, (xb, wb)::CoordsAndWeight) @@ -463,7 +493,9 @@ function _reinit_nurbs!( for j in 1:n_geom_basefuncs W += w[j]*B[j, i, cb] dWdξ += w[j]*dBdξ[j, i, cb] - d²Wdξ² += w[j]*d²Bdξ²_geom[j, i, cb] + if hessian + d²Wdξ² += w[j]*d²Bdξ²_geom[j, i, cb] + end end J = zero(Tensor{2,dim}) @@ -542,8 +574,10 @@ function _reinit_nurbs!( cv_nurbs.dNdx[j, i, cb] = cv_nurbs.dNdξ[j, i, cb] ⋅ Jinv #@assert isapprox(norm(H), 0.0; atol = 1e-14) #d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv - FF = cv_nurbs.dNdx[j, i, cb] ⋅ H - d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv - Jinv'⋅FF⋅Jinv + if hessian + FF = cv_nurbs.dNdx[j, i, cb] ⋅ H + d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv - Jinv'⋅FF⋅Jinv + end end end end From 00ae12dba920e29b45682e2c5c8e59c76327a700 Mon Sep 17 00:00:00 2001 From: lijas Date: Wed, 15 Nov 2023 17:31:37 +0100 Subject: [PATCH 18/50] cell cache for iga --- src/IGA.jl | 10 +++++++++ src/iterators.jl | 47 ++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 3 ++- test/test_iterators.jl | 32 ++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/iterators.jl create mode 100644 test/test_iterators.jl diff --git a/src/IGA.jl b/src/IGA.jl index 4d591b0..6bb5f1a 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -17,6 +17,7 @@ export BezierExtractionOperator export BezierCell export BezierCoords export VTKIGAFile +export IGACellCache const Optional{T} = Union{T, Nothing} const BezierExtractionOperator{T} = Vector{SparseArrays.SparseVector{T,Int}} @@ -30,6 +31,14 @@ struct BezierCoords{dim_s,T} beo ::Base.RefValue{ BezierExtractionOperator{T} } end +function resize_bezier_coord!(X::BezierCoords, N::Int) + (; xb, wb, x, w) = X + resize!(xb, N) + resize!(wb, N) + resize!(x, N) + resize!(w, N) +end + zero_bezier_coord(dim, T, nnodes) = BezierCoords{dim,T}(zeros(Vec{dim,T}, nnodes), zeros(T, nnodes), zeros(Vec{dim,T}, nnodes), zeros(T, nnodes), Base.RefValue(diagonal_beo(1))) #Base.zero(Type{BezierCoords{dim,T}}) where {dim,T} = BezierCoords @@ -99,6 +108,7 @@ include("bezier_extraction.jl") include("splines/bezier_values.jl") include("splines/bsplines.jl") include("VTK.jl") +include("iterators.jl") #include("L2_projection.jl") Ferrite._mass_qr(::IGAInterpolation{shape,order}) where {shape,order}= Ferrite._mass_qr(Bernstein{shape, order}()) diff --git a/src/iterators.jl b/src/iterators.jl new file mode 100644 index 0000000..488b68d --- /dev/null +++ b/src/iterators.jl @@ -0,0 +1,47 @@ +struct IGACellCache{X,G<:Ferrite.AbstractGrid,DH<:Union{Ferrite.AbstractDofHandler,Nothing}} + flags::UpdateFlags + grid::G + # Pretty useless to store this since you have it already for the reinit! call, but + # needed for the CellIterator(...) workflow since the user doesn't necessarily control + # the loop order in the cell subset. + cellid::Ferrite.ScalarWrapper{Int} + nodes::Vector{Int} + bezier_cell_data::X + dh::DH + dofs::Vector{Int} +end + +function IGACellCache(dh::DofHandler{dim,G}, flags::UpdateFlags=UpdateFlags()) where {dim,G} + grid = Ferrite.get_grid(dh) + T = Ferrite.get_coordinate_eltype(grid) + N = Ferrite.nnodes_per_cell(grid) + nodes = zeros(Int, N) + coords = zero_bezier_coord(dim,T,N) + n = ndofs_per_cell(dh) + celldofs = zeros(Int, n) + + return IGACellCache(flags, grid, Ferrite.ScalarWrapper(-1), nodes, coords, dh, celldofs) +end + +function reinit!(cc::IGACellCache, i::Int) + cc.cellid[] = i + if cc.flags.nodes + resize!(cc.nodes, Ferrite.nnodes_per_cell(cc.grid, i)) + Ferrite.cellnodes!(cc.nodes, cc.grid, i) + end + if cc.flags.coords + resize_bezier_coord!(cc.bezier_cell_data, Ferrite.nnodes_per_cell(cc.grid, i)) + getcoordinates!(cc.bezier_cell_data, cc.grid, i) + end + if cc.dh !== nothing && cc.flags.dofs + resize!(cc.dofs, ndofs_per_cell(cc.dh, i)) + celldofs!(cc.dofs, cc.dh, i) + end + return cc +end + +# Accessor functions (TODO: Deprecate? We are so inconsistent with `getxx` vs `xx`...) +Ferrite.getnodes(cc::IGACellCache) = cc.nodes +Ferrite.getcoordinates(cc::IGACellCache) = cc.bezier_cell_data +Ferrite.celldofs(cc::IGACellCache) = cc.dofs +Ferrite.cellid(cc::IGACellCache) = cc.cellid[] \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 342bbb9..c4fd9ee 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,4 +9,5 @@ include("test_bertstein.jl") include("test_bezier_extraction.jl") include("test_nurbsmesh.jl") #include("test_examples.jl") -include("test_utility_functions.jl") \ No newline at end of file +include("test_utility_functions.jl") +include("test_iterators.jl") \ No newline at end of file diff --git a/test/test_iterators.jl b/test/test_iterators.jl new file mode 100644 index 0000000..540ff2d --- /dev/null +++ b/test/test_iterators.jl @@ -0,0 +1,32 @@ + + +@testset "iga cell cache" begin + + grid = generate_grid(BezierCell{RefQuadrilateral,2}, (3,3)) + + ip = IGAInterpolation{RefQuadrilateral,2}() + + dh = DofHandler(grid) + add!(dh, :u, ip) + close!(dh) + + cc = IGACellCache(dh) + + cell_id = 5 + reinit!(cc, cell_id) + + cell = getcells(grid, cell_id) + nodes = collect(Ferrite.get_node_ids(cell)) + dofs = zeros(Int, Ferrite.ndofs_per_cell(dh, cell_id)) + celldofs!(dofs, dh, cell_id) + coords = getcoordinates(grid, cell_id) + + @test Ferrite.getnodes(cc) == nodes + @test getcoordinates(cc).xb == coords.xb + @test getcoordinates(cc).x == coords.x + @test getcoordinates(cc).wb == coords.wb + @test getcoordinates(cc).w == coords.w + @test celldofs(cc) == dofs + @test cellid(cc) == cell_id + +end \ No newline at end of file From 9c618c7061b9b46e2a819d99b9a72eb09f8838b7 Mon Sep 17 00:00:00 2001 From: lijas Date: Fri, 1 Dec 2023 15:35:42 +0100 Subject: [PATCH 19/50] some more test and work on outputing --- docs/src/literate/plate_with_hole.jl | 27 +++++++++++--------- src/IGA.jl | 4 +-- src/VTK.jl | 12 ++++++--- src/iterators.jl | 2 +- src/mesh_generation.jl | 15 ++++++++--- src/splines/bezier_values.jl | 17 +++++++++---- test/runtests.jl | 2 +- test/test_bertstein.jl | 1 + test/test_meshes.jl | 37 ++++++++++++++++++++++++++++ test/test_values.jl | 5 ---- 10 files changed, 90 insertions(+), 32 deletions(-) diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl index 2b6966e..a6a0ba9 100644 --- a/docs/src/literate/plate_with_hole.jl +++ b/docs/src/literate/plate_with_hole.jl @@ -113,7 +113,7 @@ function get_material(; E, ν) end; # We also create a function that calculates the stress in each quadrature point, given the cell displacement and such... -function calculate_stress(dh, cv::Ferrite.Values, C::SymmetricTensor{4,2}, u::Vector{Float64}) +function calculate_stress(dh, cv::BezierCellValues, C::SymmetricTensor{4,2}, u::Vector{Float64}) celldofs = zeros(Int, ndofs_per_cell(dh)) @@ -144,9 +144,9 @@ end; # We begin by generating the mesh. IGA.jl includes a couple of different functions that can generate different nurbs patches. # In this example, we will generate the patch called "plate with hole". Note, currently this function can only generate the patch with second order basefunctions. function solve() - orders = (2,2) # Order in the ξ and η directions . + order = 2 # order of the NURBS nels = (20,10) # Number of elements - nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) + nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels, order) # Performing the computation on a NURBS-patch is possible, but it is much easier to using "bezier-extraction". For this # we transform the NURBS-patch into a `BezierGrid`. The `BezierGrid` is identical to the standard `Ferrite.Grid`, but includes the NURBS-weights and @@ -163,7 +163,7 @@ function solve() # Create the cellvalues storing the shape function values. Note that the `CellVectorValues`/`FaceVectorValues` are wrapped in a `BezierValues`. It is in the # reinit-function of the `BezierValues` that the actual bezier transformation of the shape values is performed. - ip_geo = IGAInterpolation{RefQuadrilateral,2}() + ip_geo = IGAInterpolation{RefQuadrilateral,order}() ip_u = ip_geo^2 qr_cell = QuadratureRule{RefQuadrilateral}(4) qr_face = FaceQuadratureRule{RefQuadrilateral}(3) @@ -204,14 +204,19 @@ function solve() cellstresses = calculate_stress(dh, cv, stiffmat, u) #csv = BezierCellValues( CellScalarValues(qr_cell, ip) ) - projector = L2Projector(ip, grid) - σ_nodes = IGA.igaproject(projector, cellstresses, qr_cell; project_to_nodes=true) - + projector = L2Projector(ip_u, grid) + σ_nodes = project(projector, cellstresses, qr_cell) + @show σ_nodes # Output results to VTK - vtkgrid = vtk_grid("plate_with_hole.vtu", grid) - vtk_point_data(vtkgrid, dh, u, :u) - vtk_point_data(vtkgrid, σ_nodes, "sigma", grid) - vtk_save(vtkgrid) + #vtkgrid = vtk_grid("plate_with_hole.vtu", grid) + #vtk_point_data(vtkgrid, dh, u, :u) + # vtk_point_data(vtkgrid, σ_nodes, "sigma", grid) + # vtk_save(vtkgrid) + + IGA.VTKIGAFile("plate_with_hole.vtu", grid, collect(1:getncells(grid))) do vtk + IGA.write_solution(vtk, dh, u) + IGA.write_projected(vtk, dh, u) + end end; diff --git a/src/IGA.jl b/src/IGA.jl index 6bb5f1a..d89f873 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -112,8 +112,8 @@ include("iterators.jl") #include("L2_projection.jl") Ferrite._mass_qr(::IGAInterpolation{shape,order}) where {shape,order}= Ferrite._mass_qr(Bernstein{shape, order}()) -Ferrite._mass_qr(::Bernstein{RefQuadrilateral, (2,2)}) = QuadratureRule{RefQuadrilateral}(2+1) -Ferrite._mass_qr(::Bernstein{RefCube, (2,2,2)}) = QuadratureRule{RefCube}(2+1) +Ferrite._mass_qr(::Bernstein{RefQuadrilateral, 2}) = QuadratureRule{RefQuadrilateral}(2+1) +Ferrite._mass_qr(::Bernstein{RefHexahedron, 2}) = QuadratureRule{RefCube}(2+1) #Normaly the verices function should only return the 8 corner nodes of the hexa (or 4 in 2d), #but since the cell connectivity in IGA is different compared to normal FE elements, diff --git a/src/VTK.jl b/src/VTK.jl index 6f05811..3263ac3 100644 --- a/src/VTK.jl +++ b/src/VTK.jl @@ -131,13 +131,19 @@ function WriteVTK.vtk_point_data( return vtkfile end -function write_solution(vtk, dh::DofHandler, a, suffix="") +function #=Ferrite.=#write_solution(vtk::VTKIGAFile, dh::DofHandler, a, suffix="") for fieldname in Ferrite.getfieldnames(dh) data = _evaluate_at_geometry_nodes!(vtk, dh, a, fieldname) vtk_point_data(vtk.vtk, data, string(fieldname, suffix)) end end +function #=Ferrite.=#write_projection(vtk::VTKIGAFile, proj::L2Projector, vals, name) + data = _evaluate_at_grid_nodes(proj, vals, #=vtk=# Val(true))::Matrix + @assert size(data, 2) == getnnodes(get_grid(proj.dh)) + vtk_node_data(vtk.vtk, data, name; component_names=Ferrite.component_names(eltype(vals))) + return vtk +end function _evaluate_at_geometry_nodes!( vtk ::VTKIGAFile, @@ -182,9 +188,9 @@ function _evaluate_at_geometry_nodes!( # TODO: Remove this hack when embedding works... RT = ip isa ScalarInterpolation ? T : Vec{Ferrite.n_components(ip),T} if ip isa VectorizedInterpolation - cv = BezierCellValues(qr, ip.ip, ip_geo) + cv = CellValues(qr, ip.ip, ip_geo) else - cv = BezierCellValues(qr, ip, ip_geo) + cv = CellValues(qr, ip, ip_geo) end _evaluate_at_geometry_nodes!(data, sdh, a, cv, drange, vtk.cellset, RT) diff --git a/src/iterators.jl b/src/iterators.jl index 488b68d..0bb3a9b 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -23,7 +23,7 @@ function IGACellCache(dh::DofHandler{dim,G}, flags::UpdateFlags=UpdateFlags()) w return IGACellCache(flags, grid, Ferrite.ScalarWrapper(-1), nodes, coords, dh, celldofs) end -function reinit!(cc::IGACellCache, i::Int) +function Ferrite.reinit!(cc::IGACellCache, i::Int) cc.cellid[] = i if cc.flags.nodes resize!(cc.nodes, Ferrite.nnodes_per_cell(cc.grid, i)) diff --git a/src/mesh_generation.jl b/src/mesh_generation.jl index 3179fd1..b6bbb1b 100644 --- a/src/mesh_generation.jl +++ b/src/mesh_generation.jl @@ -40,6 +40,7 @@ function _generate_equidistant_parametrization(knot_vector::Vector{T}, order::In end function Ferrite.generate_grid(::Type{<:BezierCell{RefQuadrilateral,order}}, nels::NTuple{2,Int}, LL::Vec{2,T}, LR::Vec{2,T}, UR::Vec{2,T}, UL::Vec{2,T}) where {T,order} + #IGnore LR and UL for now patch = generate_nurbs_patch(:rectangle, nels, ntuple(i->order,2); cornerpos = Tuple(LL), size = Tuple(UR-LL)) grid = BezierGrid(patch) @@ -69,8 +70,14 @@ function Ferrite.generate_grid(::Type{<:BezierCell{RefHexahedron,order}}, nels:: return grid end -function generate_nurbs_patch(s::Symbol, args...; kwargs...) - generate_nurbs_patch(Val{s}(), args...; kwargs...) +function generate_nurbs_patch(s::Symbol, nel::NTuple{N,Int}, order::Int; kwargs...) where N + orders = ntuple(i->order, N) + generate_nurbs_patch(Val{s}(), nel, orders; kwargs...) +end + +function generate_nurbs_patch(s::Symbol, nel::NTuple{N1,Int}, orders::NTuple{N2,Int}; kwargs...) where {N1,N2} + @assert N1 == N2 + generate_nurbs_patch(Val{s}(), nel, orders; kwargs...) end function generate_nurbs_patch(::Val{:line}, nel::NTuple{1,Int}, orders::NTuple{1,Int}; size::NTuple{1,T}, cornerpos::NTuple{1,T} = (0.0,), multiplicity::NTuple{1,Int}=(1,), sdim::Int=1) where T @@ -549,8 +556,8 @@ end mesh = IGA.NURBSMesh((kvxi, kveta), orders, control_points) end=# -function generate_nurbs_patch(::Val{:plate_with_hole}, nel::NTuple{2,Int}) - +function generate_nurbs_patch(::Val{:plate_with_hole}, nel::NTuple{2,Int}, orders::NTuple{2,Int}) + @assert orders[1]==2 && orders[2]==2 @assert(nel[1]>=2 && nel[2]>=1) @assert(iseven(nel[1])) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index e38abf3..f5bf27c 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -422,16 +422,25 @@ function Ferrite.reinit!(bcv::BezierFaceValues, xb::AbstractVector{<:Vec}, facei end function Ferrite.reinit!(bcv::BezierCellValues, (xb, wb)::CoordsAndWeight) - _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, xb, wb) + _reinit_nurbs!( + bcv.cv_tmp, bcv.cv_bezier, + bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, + xb, wb, 1) + _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], bcv.current_w, 1) + _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) end function Ferrite.reinit!(bcv::BezierFaceValues, (xb, wb)::CoordsAndWeight, faceid::Int) - _reinit_nurbs!(bcv.cv_tmp, bcv.cv_bezier, xb, wb, faceid) bcv.cv_nurbs.current_face[] = faceid bcv.cv_bezier.current_face[] = faceid + _reinit_nurbs!( + bcv.cv_tmp, bcv.cv_bezier, + bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, + xb, wb, faceid) _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], bcv.current_w, faceid) + _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, faceid) end function Ferrite.reinit!(bcv::BezierCellValues, bc::BezierCoords) @@ -567,13 +576,11 @@ function _reinit_nurbs!( detJ = det(J) end - detJ > 0.0 || throw(ArgumentError("det(J) is not positive: det(J) = $(detJ)")) +# detJ > 0.0 || throw(ArgumentError("det(J) is not positive: det(J) = $(detJ)")) cv_bezier.detJdV[i,cb] = detJ * qr_w Jinv = inv(J) for j in 1:n_func_basefuncs cv_nurbs.dNdx[j, i, cb] = cv_nurbs.dNdξ[j, i, cb] ⋅ Jinv - #@assert isapprox(norm(H), 0.0; atol = 1e-14) - #d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv if hessian FF = cv_nurbs.dNdx[j, i, cb] ⋅ H d²NdX²_nurbs[j, i, cb] = Jinv' ⋅ d²Ndξ²_nurbs[j, i, cb] ⋅ Jinv - Jinv'⋅FF⋅Jinv diff --git a/test/runtests.jl b/test/runtests.jl index c4fd9ee..f9467a6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,6 +8,6 @@ include("test_bsplines.jl") include("test_bertstein.jl") include("test_bezier_extraction.jl") include("test_nurbsmesh.jl") -#include("test_examples.jl") +include("test_examples.jl") include("test_utility_functions.jl") include("test_iterators.jl") \ No newline at end of file diff --git a/test/test_bertstein.jl b/test/test_bertstein.jl index 7b94411..8581dc5 100644 --- a/test/test_bertstein.jl +++ b/test/test_bertstein.jl @@ -14,6 +14,7 @@ Bernstein{RefQuadrilateral, 2}(), #Bernstein{RefQuadrilateral, 3}(), Bernstein{RefHexahedron, 2}(), + IGAInterpolation{RefHexahedron, 2}(), #Bernstein{RefHexahedron, 3}() ) diff --git a/test/test_meshes.jl b/test/test_meshes.jl index 6df53b6..7e3bc12 100644 --- a/test/test_meshes.jl +++ b/test/test_meshes.jl @@ -91,6 +91,31 @@ function test_square() end +function test_plate_with_hole() + grid, cv, fv = _get_problem_data(:plate_with_hole, (4,4,), (2,2,)) + L = 4.0 + r = 1.0 + addcellset!(grid, "all", (x)->true) + addfaceset!(grid, "right", (x)->x[1]≈-4.0) + addfaceset!(grid, "top", (x)->x[2]≈4.0) + addfaceset!(grid, "circle", (x)-> r*0.9 < norm(x) < r*1.1) + + #Volume + V = _calculate_volume(cv, grid, getcellset(grid, "all")) + @test V ≈ L*L - 0.25*pi*r^2 + + #Area + A = _calculate_area(fv, grid, getfaceset(grid, "right")) + @test A ≈ L + + A = _calculate_area(fv, grid, getfaceset(grid, "top")) + @test A ≈ L + + A = _calculate_area(fv, grid, getfaceset(grid, "circle")) + @test A ≈ 2r*pi/4 #forth of circumfrence + +end + function test_singly_curved_3d() grid, cv, fv = _get_problem_data(:singly_curved, (20,2,1), (2,2,2), α = pi/2, R = 100.0, width = 5.0, thickness = 3.0) addcellset!(grid, "all", (x)->true) @@ -194,11 +219,23 @@ function test_cylinder_sector_3d() end + + @testset "Geometries, vtk-outputing and integration" begin test_cube() test_square() + test_plate_with_hole() test_singly_curved_2d() test_singly_curved_3d() test_ring() test_cylinder_sector_3d() end + +@testset "test grid_generator" + #Check that it is possible to generate gird with ferrite-api: + #TODO: What to test? + generate_grid(BezierCell{RefQuadrilateral, 2}, (2,2)) + generate_grid(BezierCell{RefQuadrilateral, 2}, (2,2), left = Vec((0.0,0.0)), right = Vec((1.0,1.0))) + generate_grid(BezierCell{RefHexahedron, 2}, (2,2,2)) + generate_grid(BezierCell{RefHexahedron, 2}, (2,2,2), left = Vec((0.0,0.0)), right = Vec((1.0,1.0))) +end \ No newline at end of file diff --git a/test/test_values.jl b/test/test_values.jl index 06faec0..32f659a 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -3,12 +3,7 @@ function bspline_values(nurbsmesh::NURBSMesh{pdim,sdim}, cellid::Int, xi::Vec{pdim}, reorder) where {pdim,sdim} Ξ = nurbsmesh.knot_vectors - bspline = BSplineBasis(Ξ, nurbsmesh.orders) - nbasefuncs = length(nurbsmesh.IEN[:, cellid]) # number of basefunctions per cell - N = zeros(Float64, nbasefuncs) - dNdξ = zeros(Tensor{1,pdim,Float64}, nbasefuncs) - d²Ndξ² = zeros(Tensor{2,pdim,Float64}, nbasefuncs) R = zeros(Float64, nbasefuncs) dRdξ = zeros(Tensor{1,pdim,Float64}, nbasefuncs) From 50c3ef894581b26b4adb8a95c1dc30410838b81d Mon Sep 17 00:00:00 2001 From: lijas Date: Wed, 13 Dec 2023 09:28:23 +0100 Subject: [PATCH 20/50] update bezier values with functionvalues and geometrymapping --- src/IGA.jl | 6 +- src/splines/bezier_values.jl | 438 ++++++++++++++--------------------- test/test_values.jl | 15 +- 3 files changed, 194 insertions(+), 265 deletions(-) diff --git a/src/IGA.jl b/src/IGA.jl index d89f873..37b03d7 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -4,8 +4,10 @@ using Reexport @reexport using Tensors @reexport using Ferrite -using Ferrite: AbstractRefShape, RefHypercube, RefLine, RefQuadrilateral, RefHexahedron, getnbasefunctions, - VectorInterpolation, VectorizedInterpolation +using Ferrite: + AbstractRefShape, RefHypercube, RefLine, RefQuadrilateral, RefHexahedron, getnbasefunctions, + VectorInterpolation, VectorizedInterpolation, + FunctionValues, GeometryMapping, MappingValues using WriteVTK using LinearAlgebra diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index f5bf27c..e5eecd4 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -1,9 +1,5 @@ export BezierCellValues, BezierFaceValues, set_bezier_operator! -""" -Wraps a standard `Ferrite.CellValues`, but multiplies the shape values with the bezier extraction operator each time the -`reinit!` function is called. -""" function Ferrite.default_geometric_interpolation(::IGAInterpolation{shape, order}) where {order, dim, shape <: AbstractRefShape{dim}} return VectorizedInterpolation{dim}(IGAInterpolation{shape, order}()) end @@ -11,240 +7,122 @@ function Ferrite.default_geometric_interpolation(::Bernstein{shape, order}) wher return VectorizedInterpolation{dim}(Bernstein{shape, order}()) end -struct BezierCellValues{T<:Real,CV<:Ferrite.CellValues, d²MdX²_t, d²NdX²_t} <: Ferrite.AbstractCellValues - # cv_bezier stores the shape values from the bernstein basis. These are the same for all elements, and does not change - cv_bezier::CV - d²Bdξ²_geom::Matrix{d²MdX²_t} - d²Bdξ²_func::Matrix{d²NdX²_t} - - # cv_nurbs stores the nurbs/b-spline basis. These will change for each element - cv_nurbs::CV - d²Ndξ²::Matrix{d²NdX²_t} - d²NdX²::Matrix{d²NdX²_t} - - # cv_tmp is just an intermidiate state needed for converting from cv_bezier to cv_nurbs - cv_tmp::CV - d²Ndξ²_tmp::Matrix{d²NdX²_t} - d²NdX²_tmp::Matrix{d²NdX²_t} +struct BezierCellValues{FV, GM, QR, T} <: Ferrite.AbstractCellValues + bezier_values::FV # FunctionValues + tmp_values::FV # FunctionValues + nurbs_values::FV # FunctionValues + geo_mapping::GM # GeometryMapping + qr::QR # QuadratureRule + detJdV::Vector{T} current_beo::Base.RefValue{BezierExtractionOperator{T}} current_w::Vector{T} end -struct BezierFaceValues{T<:Real,CV<:Ferrite.FaceValues, d²MdX²_t, d²NdX²_t} <: Ferrite.AbstractFaceValues - # cv_bezier stores the shape values from the bernstein basis. These are the same for all elements, and does not change - cv_bezier::CV - d²Bdξ²_geom::Array{d²MdX²_t,3} - d²Bdξ²_func::Array{d²NdX²_t,3} - - # cv_nurbs stores the nurbs/b-spline basis. These will change for each element - cv_nurbs::CV - d²Ndξ²::Array{d²NdX²_t,3} - d²NdX²::Array{d²NdX²_t,3} - - # cv_tmp is just an intermidiate state needed for converting from cv_bezier to cv_nurbs - cv_tmp::CV - d²Ndξ²_tmp::Array{d²NdX²_t,3} - d²NdX²_tmp::Array{d²NdX²_t,3} +struct BezierFaceValues{FV, GM, FQR, dim, T, V_FV<:AbstractVector{FV}, V_GM<:AbstractVector{GM}} <: Ferrite.AbstractFaceValues + bezier_values::V_FV # FunctionValues + tmp_values::V_FV # FunctionValues + nurbs_values::V_FV # FunctionValues + geo_mapping::V_GM # GeometryMapping + qr::QR # QuadratureRule + detJdV::Vector{T} + normals::Vector{Vec{dim,T}} current_beo::Base.RefValue{BezierExtractionOperator{T}} current_w::Vector{T} + current_face::Ferrite.ScalarWrapper{Int} end -Ferrite.shape_value_type(cv::Union{BezierCellValues, BezierFaceValues}) = Ferrite.shape_value_type(cv.cv_bezier) -Ferrite.shape_gradient_type(cv::Union{BezierCellValues, BezierFaceValues}) = Ferrite.shape_gradient_type(cv.cv_bezier) +Ferrite.shape_value_type(cv::Union{BezierCellValues, BezierFaceValues}) = Ferrite.shape_value_type(cv.bezier_values) +Ferrite.shape_gradient_type(cv::Union{BezierCellValues, BezierFaceValues}) = Ferrite.shape_gradient_type(cv.bezier_values) BezierCellAndFaceValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFaceValues{T,CV}} -function Ferrite.checkface(fv::BezierFaceValues, face::Int) - 0 < face <= nfaces(fv) || error("Face index out of range.") - return nothing -end +Ferrite.nfaces(fv::BezierFaceValues) = length(fv.geo_mapping) +Ferrite.getnormal(fv::BezierFaceValues, iqp::Int) = fv.normals[iqp] +Ferrite.function_interpolation(cv::BezierCellAndFaceValues) = Ferrite.function_interpolation(cv.bezier_values) +Ferrite.geometric_interpolation(cv::BezierCellAndFaceValues) = Ferrite.geometric_interpolation(cv.bezier_values) -Ferrite.nfaces(fv::BezierFaceValues) = size(fv.cv_nurbs.N, 3) -Ferrite.getnormal(fv::BezierFaceValues, iqp::Int) = getnormal(fv.cv_bezier, iqp) +function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; + update_gradients::Bool = true, update_detJdV::Bool = true) where T -function BezierCellValues(cv::Ferrite.CellValues) - T = eltype(cv.M) - dim = Ferrite.getdim(cv.ip) + @assert update_detJdV == true - is_vector_valued = cv.ip isa Ferrite.VectorInterpolation + FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs + GeoDiffOrder = max(Ferrite.required_geo_diff_order(Ferrite.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 = fill(T(NaN), getnquadpoints(qr)) undef_beo = Ref(Vector{SparseArrays.SparseVector{T,Int}}(undef,0)) - undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(cv)) - - # - # Higher order functions - # - d²MdX²_t = Tensor{2,dim,Float64,Tensors.n_components(Tensor{2,dim})} - d²NdX²_t = if is_vector_valued - Tensor{3,dim,Float64,Tensors.n_components(Tensor{3,dim})} - else - Tensor{2,dim,Float64,Tensors.n_components(Tensor{2,dim})} - end - - n_geom_basefuncs = getnbasefunctions(cv.gip) - n_func_basefuncs = getnbasefunctions(cv.ip) - n_qpoints = getnquadpoints(cv) - d²MdX² = fill(zero(d²MdX²_t) * T(NaN), n_geom_basefuncs, n_qpoints) - d²NdX² = fill(zero(d²NdX²_t) * T(NaN), n_func_basefuncs, n_qpoints) - - for (qp, ξ) in pairs(Ferrite.getpoints(cv.qr)) - for ib in 1:n_geom_basefuncs - d²MdX²[ib, qp] = Tensors.hessian(ξ -> shape_value(cv.gip, ξ, ib), ξ) - end - for ib in 1:n_func_basefuncs - d²NdX²[ib, qp] = Tensors.hessian(ξ -> shape_value(cv.ip, ξ, ib), ξ) - end - end + undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(geo_mapping)) return BezierCellValues( - cv, d²MdX², d²NdX², - deepcopy(cv), deepcopy(d²NdX²), deepcopy(d²NdX²) , - deepcopy(cv), deepcopy(d²NdX²), deepcopy(d²NdX²) , - undef_beo, undef_w) + fun_values, + deepcopy(fun_values), + deepcopy(fun_values), + geo_mapping, qr, detJdV, undef_beo, undef_w) end +BezierCellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) = BezierCellValues(Float64, qr, ip, args...; kwargs...) -function BezierFaceValues(cv::Ferrite.FaceValues) - T = eltype(cv.M) - dim = Ferrite.getdim(cv.func_interp) - nfaces = dim*2 - - is_vector_valued = cv.func_interp isa Ferrite.VectorInterpolation +function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = default_geometric_interpolation(ip_fun); + update_gradients::Bool = true) where {T,sdim} + FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs + GeoDiffOrder = max(Ferrite.required_geo_diff_order(Ferrite.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(Ferrite.getweights(qr)), fqr.face_rules) + detJdV = fill(T(NaN), max_nquadpoints) + normals = fill(zero(Vec{sdim, T}) * T(NaN), max_nquadpoints) undef_beo = Ref(Vector{SparseArrays.SparseVector{T,Int}}(undef,0)) - undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(cv)) - - # - # Higher order functions - # - d²MdX²_t = Tensor{2,dim,Float64,Tensors.n_components(Tensor{2,dim})} - d²NdX²_t = if is_vector_valued - Tensor{3,dim,Float64,Tensors.n_components(Tensor{3,dim})} - else - Tensor{2,dim,Float64,Tensors.n_components(Tensor{2,dim})} - end - - n_geom_basefuncs = getnbasefunctions(cv.geo_interp) - n_func_basefuncs = getnbasefunctions(cv.func_interp) - n_qpoints = getnquadpoints(cv.qr, 1) - d²MdX² = fill(zero(d²MdX²_t) * T(NaN), n_geom_basefuncs, n_qpoints, nfaces) - d²NdX² = fill(zero(d²NdX²_t) * T(NaN), n_func_basefuncs, n_qpoints, nfaces) - - for (qp, ξ) in pairs(Ferrite.getpoints(cv.qr, 1)), iface in 1:nfaces - for ib in 1:n_geom_basefuncs - d²MdX²[ib, qp, iface] = Tensors.hessian(ξ -> shape_value(cv.geo_interp, ξ, ib), ξ) - end - for ib in 1:n_func_basefuncs - d²NdX²[ib, qp, iface] = Tensors.hessian(ξ -> shape_value(cv.func_interp, ξ, ib), ξ) - end - end - + undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(geo_mapping)) return BezierFaceValues( - cv, d²MdX², d²NdX², - deepcopy(cv), copy(d²NdX²), copy(d²NdX²) , - deepcopy(cv), copy(d²NdX²), copy(d²NdX²) , - undef_beo, undef_w) + fun_values, + deepcopy(fun_values), + deepcopy(fun_values), + geo_mapping, qr, detJdV, normals, undef_beo, undef_w, Ferrite.ScalarWrapper(1)) end -#=function BezierCellValues(qr::QuadratureRule, ip::Interpolation, gip::Interpolation) - return BezierCellValues(Float64, qr, ip, gip) -end=# - -#=function BezierCellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where {T, QR, IP, VGIP} - cv = CellValues(T, qr, ip, gip) - return BezierCellValues(cv) -end=# - -# Entrypoint for `VectorInterpolation`s (vdim == rdim == sdim) with IGAInterpolation -function Ferrite.CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - order, dim, shape <: AbstractRefShape{dim}, T, - QR <: QuadratureRule{shape}, - IP <: VectorizedInterpolation{dim, shape, order, <: IGAInterpolation{shape,order}}, - GIP <: IGAInterpolation{shape,order}, - VGIP <: VectorizedInterpolation{dim, shape, order, 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} - cv = CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) +#Intercept construction of CellValues called with IGAInterpolation +function Ferrite.CellValues( + ::Type{T}, + qr::QuadratureRule, + ip_fun::Union{IGAInterpolation, VectorizedInterpolation{<:Any, <:Any, <:Any, <: IGAInterpolation}}, + ip_geo::VectorizedInterpolation; + update_gradients::Bool = true, update_detJdV::Bool = true) where T - return BezierCellValues(cv) -end + cv = BezierCellValues(T, qr, ip_fun, ip_geo; update_gradients, update_detJdV) -# Entrypoint for `ScalarInterpolation`s (rdim == sdim) -function Ferrite.CellValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where { - order, dim, shape <: AbstractRefShape{dim}, T, - QR <: QuadratureRule{shape}, - IP <: IGAInterpolation{shape,order}, - GIP <: IGAInterpolation{shape,order}, - 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} - cv = CellValues{IP, N_t, dNdx_t, dNdξ_t, M_t, dMdξ_t, QR, GIP}(qr, ip, gip.ip) - return BezierCellValues(cv) + return cv end -#Entrypoints for vector valued IGAInterpolation, which creates BezierCellValues -function Ferrite.FaceValues(qr::FaceQuadratureRule, ::IP, ::IGAInterpolation{shape,order}) where {shape,order,vdim,IP<:VectorizedInterpolation{vdim, shape, <:Any, <:IGAInterpolation{shape,order}}} - _ip = IGAInterpolation{shape,order}()^vdim - _ip_geo = Bernstein{shape,order}() - cv = FaceValues(qr, _ip, _ip_geo) - return BezierFaceValues(cv) -end +#Intercept construction of FaceValues called with IGAInterpolation +function Ferrite.FaceValues( + ::Type{T}, + qr::FaceQuadratureRule, + ip_fun::Union{IGAInterpolation, VectorizedInterpolation{<:Any, <:Any, <:Any, <: IGAInterpolation}}, + ip_geo::VectorizedInterpolation; + update_gradients::Bool = true) where T -function Ferrite.FaceValues(qr::FaceQuadratureRule, ::IGAInterpolation{shape,order}, ::IGAInterpolation{shape,order}) where {shape,order} - _ip = IGAInterpolation{shape,order}() - _ip_geo = Bernstein{shape,order}() - cv = FaceValues(qr, _ip, _ip_geo) - return BezierFaceValues(cv) -end + cv = BezierFaceValues(T, qr, ip_fun, ip_geo; update_gradients) -function BezierFaceValues(qr::FaceQuadratureRule, ip::Interpolation, gip::Interpolation) - return BezierFaceValues(Float64, qr, ip, gip) + return cv end -function BezierFaceValues(::Type{T}, qr::QR, ip::IP, gip::VGIP) where {T, QR, IP, VGIP} - cv = FaceValues(T, qr, ip, gip) - return BezierFaceValues(cv) -end +Ferrite.getnbasefunctions(bcv::BezierCellAndFaceValues) = getnbasefunctions(bcv.nurbs_values) +Ferrite.getngeobasefunctions(bcv::BezierCellAndFaceValues) = Ferrite.getngeobasefunctions(bcv.geo_mapping) +Ferrite.getnquadpoints(bcv::BezierCellAndFaceValues) = Ferrite.getnquadpoints(bcv.qr) +Ferrite.getdetJdV(bv::BezierCellAndFaceValues, q_point::Int) = bv.detJdV[q_point] +Ferrite.shape_value(bcv::BezierCellAndFaceValues, qp::Int, i::Int) = Ferrite.shape_value(bcv.nurbs_values, qp, i) +Ferrite.shape_gradient(bcv::BezierCellAndFaceValues, q_point::Int, i::Int) = Ferrite.shape_gradient(bcv.nurbs_values, q_point, i) +Ferrite.geometric_value(cv::BezierCellValues, q_point::Int, i::Int) = Ferrite.geometric_value(cv.geo_mapping, q_point, i) -Ferrite.getnbasefunctions(bcv::BezierCellAndFaceValues) = size(bcv.cv_bezier.N, 1) -Ferrite.getngeobasefunctions(bcv::BezierCellAndFaceValues) = size(bcv.cv_bezier.M, 1) -Ferrite.getnquadpoints(bcv::BezierCellAndFaceValues) = Ferrite.getnquadpoints(bcv.cv_bezier) -Ferrite.getdetJdV(bv::BezierCellAndFaceValues, q_point::Int) = Ferrite.getdetJdV(bv.cv_bezier, q_point) -Ferrite.shape_value(bcv::BezierCellAndFaceValues, qp::Int, i::Int) = Ferrite.shape_value(bcv.cv_nurbs,qp,i) -Ferrite.geometric_value(cv::BezierCellAndFaceValues, q_point::Int, i::Int) = Ferrite.geometric_value(cv.cv_bezier, q_point, i) -Ferrite.shape_gradient(bcv::BezierCellAndFaceValues, q_point::Int, i::Int) = Ferrite.shape_gradient(bcv.cv_nurbs, q_point, i) -Ferrite.geometric_value(cv::BezierCellValues, q_point::Int, base_func::Int) = cv.cv_bezier.M[base_func, q_point] -Ferrite.geometric_value(cv::BezierFaceValues, q_point::Int, base_func::Int) = cv.cv_bezier.M[base_func, q_point, cv.cv_bezier.current_face[]] shape_hessian(bv::BezierFaceValues, q_point::Int, base_func::Int) = bv.d²NdX²[base_func, q_point, bv.cv_bezier.current_face[]] shape_hessian(bv::BezierCellValues, q_point::Int, base_func::Int) = bv.d²NdX²[base_func, q_point] -function Ferrite.function_symmetric_gradient(bv::BezierCellAndFaceValues, q_point::Int, u::AbstractVector) - return function_symmetric_gradient(bv.cv_nurbs, q_point, u) -end -function Ferrite.shape_symmetric_gradient(bv::BezierCellAndFaceValues, q_point::Int, i::Int) - return shape_symmetric_gradient(bv.cv_nurbs, q_point, i) -end -function Ferrite.function_gradient(fe_v::BezierCellAndFaceValues, q_point::Int, u::AbstractVector) - return function_gradient(fe_v.cv_nurbs, q_point, u) -end -function Ferrite.function_value(fe_v::BezierCellAndFaceValues, q_point::Int, u::AbstractVector) - return function_value(fe_v.cv_nurbs, q_point, u) -end - -# TODO: Deprecate this, nobody is using this in practice... +#= function function_hessian(fe_v::Ferrite.AbstractValues, q_point::Int, u::AbstractVector{<:Vec}) n_base_funcs = getnbasefunctions(fe_v) length(u) == n_base_funcs || Ferrite.throw_incompatible_dof_length(length(u), n_base_funcs) @@ -259,6 +137,7 @@ shape_hessian_type(::Union{BezierCellValues{<:Any, <:Any, <:Any, d²NdX²_t}, Be function function_hessian_init(cv::Ferrite.AbstractValues, ::AbstractVector{T}) where {T} return zero(shape_hessian_type(cv)) * zero(T) end +=# function set_bezier_operator!(bcv::BezierCellAndFaceValues, beo::BezierExtractionOperator{T}) where T bcv.current_beo[]=beo @@ -394,79 +273,119 @@ function _cellvalues_bezier_extraction_higher_order!( end -function Ferrite.reinit!(bcv::BezierCellValues, xb::AbstractVector{<:Vec})#; updateflags = CellValuesFlags()) - #Ferrite.reinit!(bcv.cv_bezier, xb) #call the normal reinit function first - @assert all(bcv.current_w .== 1.0) - wb = bcv.current_w - _reinit_nurbs!( - bcv.cv_tmp, bcv.cv_bezier, - bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - xb, wb, 1) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], nothing, 1) - _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) +function Ferrite.reinit!(cv::BezierCellValues, bc::BezierCoords) + set_bezier_operator!(cv, bc.beo[], bc.w) + return reinit!(cv, (bc.xb, bc.wb)) end -function Ferrite.reinit!(bcv::BezierFaceValues, xb::AbstractVector{<:Vec}, faceid::Int) - @assert all(bcv.current_w .== 1.0) - bcv.cv_nurbs.current_face[] = faceid - bcv.cv_bezier.current_face[] = faceid +function Ferrite.reinit!(cv::BezierCellValues, (x,w)::CoordsAndWeight) + n_geom_basefuncs = Ferrite.getngeobasefunctions(cv.geo_mapping) + @assert isa(Ferrite.mapping_type(cv.bezier_values), Ferrite.IdentityMapping) + @assert checkbounds(Bool, x, 1:n_geom_basefuncs) + @assert checkbounds(Bool, w, 1:n_geom_basefuncs) - wb = bcv.current_w # borrow - _reinit_nurbs!( - bcv.cv_tmp, bcv.cv_bezier, - bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - xb, wb, faceid) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], nothing, faceid) - _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, faceid) + for (q_point, gauss_w) in enumerate(Ferrite.getweights(cv.qr)) + mapping = Ferrite.calculate_mapping(cv.geo_mapping, q_point, x, w) + + detJ = Ferrite.calculate_detJ(Ferrite.getjacobian(mapping)) + detJ > 0.0 || Ferrite.throw_detJ_not_pos(detJ) + cv.detJdV[q_point] = detJ * gauss_w + _compute_intermidiate!(cv.tmp_values, cv.bezier_values, cv.geo_mapping, q_point, w) + Ferrite.apply_mapping!(cv.tmp_values, q_point, mapping) + _bezier_transform(cv.nurbs_values, cv.tmp_values, q_point, cv.current_beo[], cv.current_w) + end + return nothing end -function Ferrite.reinit!(bcv::BezierCellValues, (xb, wb)::CoordsAndWeight) - _reinit_nurbs!( - bcv.cv_tmp, bcv.cv_bezier, - bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - xb, wb, 1) +function _bezier_transform(nurbs::FunctionValues{1}, bezier::FunctionValues{1}, iq::Int, Cbe::BezierExtractionOperator{T}, w::Optional{Vector{T}}) where {T} + + N = length(Cbe) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], bcv.current_w, 1) - _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) -end + dBdx = bezier.dNdx + dBdξ = bezier.dNdξ + B = bezier.Nξ + + is_scalar_valued = !(Ferrite.shape_value_type(nurbs) <: Tensor) + dim_s = Ferrite.sdim_from_gradtype(Ferrite.shape_gradient_type(nurbs)) -function Ferrite.reinit!(bcv::BezierFaceValues, (xb, wb)::CoordsAndWeight, faceid::Int) - bcv.cv_nurbs.current_face[] = faceid - bcv.cv_bezier.current_face[] = faceid - _reinit_nurbs!( - bcv.cv_tmp, bcv.cv_bezier, - bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - xb, wb, faceid) + for ib in 1:N + if is_scalar_valued + nurbs.Nξ[ib, iq] = zero(eltype(nurbs.Nξ)) + nurbs.dNdξ[ib, iq] = zero(eltype(nurbs.dNdξ)) + nurbs.dNdx[ib, iq] = zero(eltype(nurbs.dNdx)) + else #if FieldTrait(cv_nurbs) == Ferrite.VectorValued() + for d in 1:dim_s + nurbs.Nξ[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.Nξ)) + nurbs.dNdξ[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.dNdξ)) + nurbs.dNdx[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.dNdx)) + end + end + + Cbe_ib = Cbe[ib] + + for (i, nz_ind) in enumerate(Cbe_ib.nzind) + val = Cbe_ib.nzval[i] + if (w !== nothing) + val*=w[ib] + end + if is_scalar_valued + nurbs.Nξ[ib, iq] += val* B[nz_ind, iq] + nurbs.dNdξ[ib, iq] += val*dBdξ[nz_ind, iq] + nurbs.dNdx[ib, iq] += val*dBdx[nz_ind, iq] + else + for d in 1:dim_s + nurbs.Nξ[(ib-1)*dim_s + d, iq] += val* B[(nz_ind-1)*dim_s + d, iq] + nurbs.dNdξ[(ib-1)*dim_s + d, iq] += val*dBdξ[(nz_ind-1)*dim_s + d, iq] + nurbs.dNdx[(ib-1)*dim_s + d, iq] += val*dBdx[(nz_ind-1)*dim_s + d, iq] + end + end + end + end - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bcv.current_beo[], bcv.current_w, faceid) - _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, faceid) end -function Ferrite.reinit!(bcv::BezierCellValues, bc::BezierCoords) - set_bezier_operator!(bcv, bc.beo[], bc.w) +Ferrite.otimes_helper(x::Number, dMdξ::Vec{dim}) where dim = x * dMdξ + +function _compute_intermidiate!(tmp_values::FunctionValues{1}, bezier_values::FunctionValues{1}, geom_values::GeometryMapping, q_point::Int, w::Vector{T}) where {T} + dim = Ferrite.sdim_from_gradtype(Ferrite.shape_gradient_type(tmp_values)) - _reinit_nurbs!( - bcv.cv_tmp, bcv.cv_bezier, - bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - bc.xb, bc.wb, 1) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bcv.current_w, 1) - _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], bcv.current_w, 1) + W = zero(T) + dWdξ = zero(Vec{dim,T}) + for j in 1:Ferrite.getngeobasefunctions(geom_values) + W += w[j]*geom_values.M[j, q_point] + dWdξ += w[j]*geom_values.dMdξ[j, q_point] + end + for j in 1:getnbasefunctions(tmp_values) + tmp_values.dNdξ[j, q_point] = ( bezier_values.dNdξ[j, q_point]*W - Ferrite.otimes_helper(bezier_values.Nξ[j, q_point], dWdξ) ) / W^2 + tmp_values.Nξ[j,q_point] = bezier_values.Nξ[j, q_point]/W + end end -function Ferrite.reinit!(bcv::BezierFaceValues, bc::BezierCoords, faceid::Int) - set_bezier_operator!(bcv, bc.beo[], bc.w) - bcv.cv_bezier.current_face[] = faceid - bcv.cv_nurbs.current_face[] = faceid - - _reinit_nurbs!( - bcv.cv_tmp, bcv.cv_bezier, - bcv.d²Bdξ²_geom, bcv.d²Bdξ²_func, bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, - bc.xb, bc.wb, faceid) - _cellvalues_bezier_extraction!(bcv.cv_nurbs, bcv.cv_tmp, bc.beo[], bc.w, faceid) - _cellvalues_bezier_extraction_higher_order!(bcv.d²Ndξ², bcv.d²NdX², bcv.d²Ndξ²_tmp, bcv.d²NdX²_tmp, bcv.current_beo[], nothing, 1) +function Ferrite.apply_mapping!(tmp_values::FunctionValues{1}, q_point::Int, mapping::MappingValues) + Jinv = Ferrite.calculate_Jinv(Ferrite.getjacobian(mapping)) + for j in 1:getnbasefunctions(tmp_values) + tmp_values.dNdx[j, q_point] = Ferrite.dothelper(tmp_values.dNdξ[j, q_point], Jinv) + end end +function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{1}, q_point, x::Vector{Vec{dim,T}}, w::Vector{T}) where {dim,T} + + W = zero(T) + dWdξ = zero(Vec{dim,T}) + #d²Wdξ² = zero(Tensor{2,dim,T}) + for j in 1:Ferrite.getngeobasefunctions(geo_mapping) + W += w[j]*geo_mapping.M[j, q_point] + dWdξ += w[j]*geo_mapping.dMdξ[j, q_point] + end + + fecv_J = zero(Tensor{2,dim,T}) + for j in 1:Ferrite.getngeobasefunctions(geo_mapping) + dRdξ = (geo_mapping.dMdξ[j, q_point]*W - geo_mapping.M[j, q_point]*dWdξ)/W^2 + fecv_J += x[j] ⊗ (w[j]*dRdξ) + end + return Ferrite.MappingValues(fecv_J, nothing) +end """ Ferrite.reinit!(cv::Ferrite.CellVectorValues{dim}, xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}) where {dim,T} @@ -475,7 +394,7 @@ Similar to Ferrite's reinit method, but in IGA with NURBS, the weights is also n `xᴮ` - Bezier coordinates `w` - weights for nurbs mesh (not bezier weights) """ -function _reinit_nurbs!( +#=function _reinit_nurbs!( cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.AbstractValues, d²Bdξ²_geom, d²Bdξ²_func, d²Ndξ²_nurbs, d²NdX²_nurbs, xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}, cb::Int = 1) where {dim,T} @@ -588,6 +507,7 @@ function _reinit_nurbs!( end end end +=# function Base.show(io::IO, m::MIME"text/plain", fv::BezierFaceValues) println(io, "BezierFaceValues with") @@ -603,9 +523,11 @@ function Base.show(io::IO, m::MIME"text/plain", fv::BezierFaceValues) end function Base.show(io::IO, m::MIME"text/plain", cv::BezierCellValues) + fip = Ferrite.function_interpolation(cv) + gip = Ferrite.function_interpolation(cv) println(io, "BezierCellValues with") println(io, "- Quadrature rule with ", getnquadpoints(cv), " points") - print(io, "- Function interpolation: "); show(io, m, cv.cv_bezier.ip) + print(io, "- Function interpolation: "); show(io, m, fip) println(io) - print(io, "- Geometric interpolation: "); show(io, m, cv.cv_bezier.gip) + print(io, "- Geometric interpolation: "); show(io, m, gip) end diff --git a/test/test_values.jl b/test/test_values.jl index 32f659a..0313156 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -92,7 +92,7 @@ end ## # Build the problem ## - nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels) + nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels, (2,2)) #nurbsmesh = generate_nurbs_patch(:rectangle, nels, (order,order), size = (20.0,20.0)) #nurbsmesh.weights .= 1.0 @@ -113,6 +113,7 @@ end cv_vector = CellValues( qr, ip^dim, ip) #Try some different cells + cellnum = 1 for cellnum in 1:getncells(grid)#[1,4,5] #C = get_extraction_operator(grid, cellnum) #X = get_nurbs_coordinates(grid, cellnum) @@ -124,6 +125,8 @@ end @show cv.current_w (; xb, wb, x, w) = bc + iqp = 1 + ξ = qr.points[iqp] for (iqp, ξ) in enumerate(qr.points) #Calculate the value of the NURBS from the nurbs patch @@ -145,10 +148,12 @@ end end @test dV_patch ≈ getdetJdV(cv, iqp) - @test sum(cv.cv_nurbs.N[:,iqp]) ≈ 1 - @test cv.cv_nurbs.N[:,iqp] ≈ R - @test cv.cv_nurbs.dNdξ[:,iqp] ≈ dRdξ - @test cv.cv_nurbs.dNdx[:,iqp] ≈ dRdX + @test sum(cv.nurbs_values.Nξ[:,iqp]) ≈ 1 + for i in 1:getnbasefunctions(cv) + @test shape_value(cv,iqp,i) ≈ R[i] + @test shape_gradient(cv, iqp, i) ≈ dRdX[i] + @test cv.nurbs_values.dNdξ[i,iqp] ≈ dRdξ[i] + end @test cv.d²Ndξ²[:,iqp] ≈ d²Rdξ² atol=1e-14 @test cv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 From 0035ebc5008fc10b7ed15129c4677ac1ab363424 Mon Sep 17 00:00:00 2001 From: lijas Date: Fri, 19 Jan 2024 09:53:08 +0100 Subject: [PATCH 21/50] stash --- src/IGA.jl | 1 + src/bezier_grid.jl | 5 ++++- src/iterators_future.jl | 42 ++++++++++++++++++++++++++++++++++++ src/splines/bezier_values.jl | 6 +++--- 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 src/iterators_future.jl diff --git a/src/IGA.jl b/src/IGA.jl index 37b03d7..3f64c52 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -111,6 +111,7 @@ include("splines/bezier_values.jl") include("splines/bsplines.jl") include("VTK.jl") include("iterators.jl") +include("iterators_future.jl") #include("L2_projection.jl") Ferrite._mass_qr(::IGAInterpolation{shape,order}) where {shape,order}= Ferrite._mass_qr(Bernstein{shape, order}()) diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 48ce192..57ebaf0 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -53,7 +53,7 @@ function BezierGrid(grid::Ferrite.Grid{dim,C,T}) where {dim,C,T} push!(extraction_operator, beo) end - return BezierGrid{dim,C,T}(grid, weights, extraction_operator, facesets = grid.facesets, nodesets = grid.nodesets, edgesets = grid.edgesets, vertexsets = grid.vertexsets) + return BezierGrid{dim,C,T}(grid, weights, extraction_operator) end function Base.getproperty(m::BezierGrid, s::Symbol) @@ -109,6 +109,9 @@ function Ferrite.getweights(grid::BezierGrid, ic::Int) return grid.weights[nodeids] end +#Note: This is needed in order to get some stuff to work nicely in Ferrite. +Ferrite.get_coordinate_type(::BezierGrid{dim,C,T}) where {dim,C,T} = Vec{dim,T} + function Ferrite.getcoordinates!(bc::BezierCoords{dim,T}, grid::BezierGrid, ic::Int) where {dim,T} get_bezier_coordinates!(bc.xb, bc.wb, bc.x, bc.w, grid, ic) bc.beo[] = grid.beo[ic] diff --git a/src/iterators_future.jl b/src/iterators_future.jl new file mode 100644 index 0000000..bfd501f --- /dev/null +++ b/src/iterators_future.jl @@ -0,0 +1,42 @@ +struct IGACellCache_Future{X<:BezierCoords} + coords::X + nodes::Vector{Int} + dofs::Vector{Int} +end + +IGACellCache_Future(grid::BezierGrid) = IGACellCache_Future(getcoordinates(grid, 1), Ferrite.nnodes_per_cell(grid), 0) +IGACellCache_Future(dh::DofHandler) = IGACellCache_Future(getcoordinates(Ferrite.get_grid(dh), 1), Ferrite.nnodes_per_cell(Ferrite.get_grid(dh)), ndofs_per_cell(dh)) + +function IGACellCache_Future(coords, nnodes::Int, ndofs::Int) + nodes = zeros(Int, nnodes) + #coords = zeros(Vec{dim,T}, nnodes) + celldofs = zeros(Int, ndofs) + return IGACellCache_Future(coords, nodes, celldofs) +end + +function Ferrite.reinit!(cc::IGACellCache_Future, dh::DofHandler, i::Int, flags) + reinit!(cc, dh.grid, i, flags) + if flags.dofs + resize!(cc.dofs, ndofs_per_cell(dh, i)) + celldofs!(cc.dofs, dh, i) + end + return cc +end + +function Ferrite.reinit!(cc::IGACellCache_Future, grid::BezierGrid, i::Int, flags) + if flags.nodes + resize!(cc.nodes, Ferrite.nnodes_per_cell(grid, i)) + Ferrite.cellnodes!(cc.nodes, grid, i) + end + if flags.coords + resize_bezier_coord!(cc.coords, Ferrite.nnodes_per_cell(grid, i)) + getcoordinates!(cc.coords, grid, i) + end + return cc +end + +# Accessor functions (TODO: Deprecate? We are so inconsistent with `getxx` vs `xx`...) +Ferrite.getnodes(cc::IGACellCache_Future) = cc.nodes +Ferrite.getcoordinates(cc::IGACellCache_Future) = cc.coords +Ferrite.celldofs(cc::IGACellCache_Future) = cc.dofs +#Ferrite.cellid(cc::IGACellCache_Future) = cc.cellid[] \ No newline at end of file diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index e5eecd4..b2eb0a7 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -24,7 +24,7 @@ struct BezierFaceValues{FV, GM, FQR, dim, T, V_FV<:AbstractVector{FV}, V_GM<:Abs tmp_values::V_FV # FunctionValues nurbs_values::V_FV # FunctionValues geo_mapping::V_GM # GeometryMapping - qr::QR # QuadratureRule + qr::FQR # QuadratureRule detJdV::Vector{T} normals::Vector{Vec{dim,T}} @@ -41,7 +41,7 @@ BezierCellAndFaceValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFaceValues{T Ferrite.nfaces(fv::BezierFaceValues) = length(fv.geo_mapping) Ferrite.getnormal(fv::BezierFaceValues, iqp::Int) = fv.normals[iqp] Ferrite.function_interpolation(cv::BezierCellAndFaceValues) = Ferrite.function_interpolation(cv.bezier_values) -Ferrite.geometric_interpolation(cv::BezierCellAndFaceValues) = Ferrite.geometric_interpolation(cv.bezier_values) +Ferrite.geometric_interpolation(cv::BezierCellAndFaceValues) = Ferrite.geometric_interpolation(cv.geo_mapping) function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; update_gradients::Bool = true, update_detJdV::Bool = true) where T @@ -77,7 +77,7 @@ function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolat detJdV = fill(T(NaN), max_nquadpoints) normals = fill(zero(Vec{sdim, T}) * T(NaN), max_nquadpoints) undef_beo = Ref(Vector{SparseArrays.SparseVector{T,Int}}(undef,0)) - undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(geo_mapping)) + undef_w = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(first(geo_mapping))) return BezierFaceValues( fun_values, deepcopy(fun_values), From 766f5c0f005dec66412a21198414bb29a183e177 Mon Sep 17 00:00:00 2001 From: lijas Date: Fri, 19 Jan 2024 16:18:21 +0100 Subject: [PATCH 22/50] small changes --- src/splines/bezier.jl | 4 ++-- src/splines/bezier_values.jl | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index 616f614..e2503bf 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -160,11 +160,11 @@ end =# -function Ferrite.shape_value(ip::Bernstein, _ξ::Vec, i::Int) +function Ferrite.shape_value(ip::Bernstein{refshape,order}, _ξ::Vec{dim}, i::Int) where {dim,refshape<:Ferrite.AbstractRefShape{dim},order} _compute_bezier_shape_value(ip,_ξ, i) end -function _compute_bezier_shape_value(ip::Bernstein{shape,order}, ξ::Vec{dim,T}, i::Int) where {shape,dim,order,T} +function _compute_bezier_shape_value(ip::Bernstein{shape,order}, ξ::Vec{dim,T}, i::Int) where {dim,shape<:Ferrite.AbstractRefShape{dim},order,T} _n = ntuple(i->order+1, dim) ordering = _bernstein_ordering(ip) basefunction_indeces = CartesianIndices(_n)[ordering[i]] diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index b2eb0a7..4a42169 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -40,8 +40,10 @@ BezierCellAndFaceValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFaceValues{T Ferrite.nfaces(fv::BezierFaceValues) = length(fv.geo_mapping) Ferrite.getnormal(fv::BezierFaceValues, iqp::Int) = fv.normals[iqp] -Ferrite.function_interpolation(cv::BezierCellAndFaceValues) = Ferrite.function_interpolation(cv.bezier_values) -Ferrite.geometric_interpolation(cv::BezierCellAndFaceValues) = Ferrite.geometric_interpolation(cv.geo_mapping) +Ferrite.function_interpolation(cv::BezierCellValues) = Ferrite.function_interpolation(cv.bezier_values) +Ferrite.geometric_interpolation(cv::BezierCellValues) = Ferrite.geometric_interpolation(cv.geo_mapping) +Ferrite.function_interpolation(cv::BezierFaceValues) = Ferrite.function_interpolation(cv.bezier_values[1]) +Ferrite.geometric_interpolation(cv::BezierFaceValues) = Ferrite.geometric_interpolation(cv.geo_mapping[1]) function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; update_gradients::Bool = true, update_detJdV::Bool = true) where T @@ -82,7 +84,7 @@ function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolat fun_values, deepcopy(fun_values), deepcopy(fun_values), - geo_mapping, qr, detJdV, normals, undef_beo, undef_w, Ferrite.ScalarWrapper(1)) + geo_mapping, fqr, detJdV, normals, undef_beo, undef_w, Ferrite.ScalarWrapper(1)) end #Intercept construction of CellValues called with IGAInterpolation @@ -511,20 +513,22 @@ end function Base.show(io::IO, m::MIME"text/plain", fv::BezierFaceValues) println(io, "BezierFaceValues with") - nqp = getnquadpoints.(fv.cv_bezier.qr.face_rules) + nqp = getnquadpoints.(fv.qr.face_rules) + fip = Ferrite.function_interpolation(fv) + gip = Ferrite.geometric_interpolation(fv) 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.cv_bezier.func_interp) + print(io, "- Function interpolation: "); show(io, m, fip) println(io) - print(io, "- Geometric interpolation: "); show(io, m, fv.cv_bezier.geo_interp) + print(io, "- Geometric interpolation: "); show(io, m, gip) end function Base.show(io::IO, m::MIME"text/plain", cv::BezierCellValues) fip = Ferrite.function_interpolation(cv) - gip = Ferrite.function_interpolation(cv) + gip = Ferrite.geometric_interpolation(cv) println(io, "BezierCellValues with") println(io, "- Quadrature rule with ", getnquadpoints(cv), " points") print(io, "- Function interpolation: "); show(io, m, fip) From 55d3da96b369974f7bd4b6258bf699fd8433a7ab Mon Sep 17 00:00:00 2001 From: lijas Date: Wed, 14 Feb 2024 15:15:30 +0100 Subject: [PATCH 23/50] work on cell and face values hessian --- src/IGA.jl | 2 +- src/iterators.jl | 34 +++++- src/splines/bezier_values.jl | 198 +++++++++++++++++++++++++++++------ test/test_values.jl | 90 ++++++++-------- 4 files changed, 244 insertions(+), 80 deletions(-) diff --git a/src/IGA.jl b/src/IGA.jl index 3f64c52..e62e907 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -19,7 +19,7 @@ export BezierExtractionOperator export BezierCell export BezierCoords export VTKIGAFile -export IGACellCache +export IGACellCache, IGAFaceCache const Optional{T} = Union{T, Nothing} const BezierExtractionOperator{T} = Vector{SparseArrays.SparseVector{T,Int}} diff --git a/src/iterators.jl b/src/iterators.jl index 0bb3a9b..518aa8d 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -44,4 +44,36 @@ end Ferrite.getnodes(cc::IGACellCache) = cc.nodes Ferrite.getcoordinates(cc::IGACellCache) = cc.bezier_cell_data Ferrite.celldofs(cc::IGACellCache) = cc.dofs -Ferrite.cellid(cc::IGACellCache) = cc.cellid[] \ No newline at end of file +Ferrite.cellid(cc::IGACellCache) = cc.cellid[] + + +# +# Copy FaceCache from ferrite +struct IGAFaceCache{CC} + cc::CC # const for julia > 1.8 + dofs::Vector{Int} # aliasing cc.dofs + current_faceid::Ferrite.ScalarWrapper{Int} +end + +function IGAFaceCache(args...) + cc = IGACellCache(args...) + IGAFaceCache(cc, cc.dofs, Ferrite.ScalarWrapper(0)) +end + +function Ferrite.reinit!(fc::IGAFaceCache, face::FaceIndex) + cellid, faceid = face + reinit!(fc.cc, cellid) + fc.current_faceid[] = faceid + return nothing +end + +# Delegate methods to the cell cache +for op = (:getnodes, :getcoordinates, :cellid, :celldofs) + @eval begin + function Ferrite.$op(fc::IGAFaceCache, args...) + return Ferrite.$op(fc.cc, args...) + end + end +end +# @inline faceid(fc::FaceCache) = fc.current_faceid[] +@inline Ferrite.celldofs!(v::Vector, fc::IGAFaceCache) = celldofs!(v, fc.cc) \ No newline at end of file diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 4a42169..73b3711 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -33,8 +33,11 @@ struct BezierFaceValues{FV, GM, FQR, dim, T, V_FV<:AbstractVector{FV}, V_GM<:Abs current_face::Ferrite.ScalarWrapper{Int} end -Ferrite.shape_value_type(cv::Union{BezierCellValues, BezierFaceValues}) = Ferrite.shape_value_type(cv.bezier_values) -Ferrite.shape_gradient_type(cv::Union{BezierCellValues, BezierFaceValues}) = Ferrite.shape_gradient_type(cv.bezier_values) +Ferrite.shape_value_type(cv::BezierCellValues) = Ferrite.shape_value_type(cv.bezier_values) +Ferrite.shape_value_type(cv::BezierFaceValues) = Ferrite.shape_value_type(cv.bezier_values[Ferrite.getcurrentface(cv)]) + +Ferrite.shape_gradient_type(cv::BezierFaceValues) = Ferrite.shape_gradient_type(cv.bezier_values[Ferrite.getcurrentface(cv)]) +Ferrite.shape_gradient_type(cv::BezierCellValues) = Ferrite.shape_gradient_type(cv.bezier_values) BezierCellAndFaceValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFaceValues{T,CV}} @@ -46,11 +49,12 @@ Ferrite.function_interpolation(cv::BezierFaceValues) = Ferrite.function_interpol Ferrite.geometric_interpolation(cv::BezierFaceValues) = Ferrite.geometric_interpolation(cv.geo_mapping[1]) function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; - update_gradients::Bool = true, update_detJdV::Bool = true) where T + update_gradients::Bool = true, update_hessians = false, update_detJdV::Bool = true) where T @assert update_detJdV == true - FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs + FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs + FunDiffOrder += convert(Int, update_hessians) # Logic must change when supporting update_hessian kwargs GeoDiffOrder = max(Ferrite.required_geo_diff_order(Ferrite.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) @@ -69,9 +73,10 @@ end BezierCellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) = BezierCellValues(Float64, qr, ip, args...; kwargs...) function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = default_geometric_interpolation(ip_fun); - update_gradients::Bool = true) where {T,sdim} + update_hessians::Bool = false, update_gradients::Bool = true) where {T,sdim} - FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs + FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs + FunDiffOrder += convert(Int, update_hessians) # Logic must change when supporting update_hessian kwargs GeoDiffOrder = max(Ferrite.required_geo_diff_order(Ferrite.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] @@ -84,7 +89,7 @@ function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolat fun_values, deepcopy(fun_values), deepcopy(fun_values), - geo_mapping, fqr, detJdV, normals, undef_beo, undef_w, Ferrite.ScalarWrapper(1)) + geo_mapping, fqr, detJdV, normals, undef_beo, undef_w, Ferrite.ScalarWrapper(-1)) end #Intercept construction of CellValues called with IGAInterpolation @@ -93,9 +98,9 @@ function Ferrite.CellValues( qr::QuadratureRule, ip_fun::Union{IGAInterpolation, VectorizedInterpolation{<:Any, <:Any, <:Any, <: IGAInterpolation}}, ip_geo::VectorizedInterpolation; - update_gradients::Bool = true, update_detJdV::Bool = true) where T + update_gradients::Bool = true, update_hessians::Bool = false, update_detJdV::Bool = true) where T - cv = BezierCellValues(T, qr, ip_fun, ip_geo; update_gradients, update_detJdV) + cv = BezierCellValues(T, qr, ip_fun, ip_geo; update_gradients, update_hessians, update_detJdV) return cv end @@ -106,23 +111,41 @@ function Ferrite.FaceValues( qr::FaceQuadratureRule, ip_fun::Union{IGAInterpolation, VectorizedInterpolation{<:Any, <:Any, <:Any, <: IGAInterpolation}}, ip_geo::VectorizedInterpolation; - update_gradients::Bool = true) where T + update_gradients::Bool = true, + update_hessians::Bool = false) where T - cv = BezierFaceValues(T, qr, ip_fun, ip_geo; update_gradients) + cv = BezierFaceValues(T, qr, ip_fun, ip_geo; update_gradients, update_hessians) return cv end -Ferrite.getnbasefunctions(bcv::BezierCellAndFaceValues) = getnbasefunctions(bcv.nurbs_values) -Ferrite.getngeobasefunctions(bcv::BezierCellAndFaceValues) = Ferrite.getngeobasefunctions(bcv.geo_mapping) -Ferrite.getnquadpoints(bcv::BezierCellAndFaceValues) = Ferrite.getnquadpoints(bcv.qr) +Ferrite.getnbasefunctions(fv::BezierFaceValues) = getnbasefunctions(fv.nurbs_values[Ferrite.getcurrentface(fv)]) +Ferrite.getnbasefunctions(cv::BezierCellValues) = getnbasefunctions(cv.nurbs_values) +Ferrite.getngeobasefunctions(fv::BezierFaceValues) = Ferrite.getngeobasefunctions(fv.geo_mapping[Ferrite.getcurrentface(fv)]) +Ferrite.getngeobasefunctions(cv::BezierCellValues) = Ferrite.getngeobasefunctions(cv.geo_mapping) +Ferrite.getnquadpoints(bcv::BezierCellValues) = Ferrite.getnquadpoints(bcv.qr) +Ferrite.getnquadpoints(bcv::BezierFaceValues) = Ferrite.getnquadpoints(bcv.qr, Ferrite.getcurrentface(bcv)) Ferrite.getdetJdV(bv::BezierCellAndFaceValues, q_point::Int) = bv.detJdV[q_point] -Ferrite.shape_value(bcv::BezierCellAndFaceValues, qp::Int, i::Int) = Ferrite.shape_value(bcv.nurbs_values, qp, i) -Ferrite.shape_gradient(bcv::BezierCellAndFaceValues, q_point::Int, i::Int) = Ferrite.shape_gradient(bcv.nurbs_values, q_point, i) +Ferrite.shape_value(bcv::BezierCellValues, qp::Int, i::Int) = Ferrite.shape_value(bcv.nurbs_values, qp, i) +Ferrite.shape_gradient(bcv::BezierCellValues, q_point::Int, i::Int) = Ferrite.shape_gradient(bcv.nurbs_values, q_point, i) Ferrite.geometric_value(cv::BezierCellValues, q_point::Int, i::Int) = Ferrite.geometric_value(cv.geo_mapping, q_point, i) -shape_hessian(bv::BezierFaceValues, q_point::Int, base_func::Int) = bv.d²NdX²[base_func, q_point, bv.cv_bezier.current_face[]] -shape_hessian(bv::BezierCellValues, q_point::Int, base_func::Int) = bv.d²NdX²[base_func, q_point] +Ferrite.shape_value(fv::BezierFaceValues, qp::Int, i::Int) = shape_value(fv.nurbs_values[Ferrite.getcurrentface(fv)], qp, i) +Ferrite.shape_gradient(fv::BezierFaceValues, q_point::Int, i::Int) = shape_gradient(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i) +Ferrite.geometric_value(fv::BezierFaceValues, q_point::Int, i::Int) = Ferrite.geometric_value(fv.geo_mapping[Ferrite.getcurrentface(fv)], q_point, i) + +#Ferrite.get_fun_values(fv::BezierFaceValues) = @inbounds fv.nurbs_values[Ferrite.getcurrentface(fv)] +#Ferrite.get_fun_values(cv::BezierCellValues) = @inbounds cv.nurbs_values + +Ferrite.shape_hessian(fv::BezierFaceValues, q_point::Int, i::Int) = shape_hessian(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i) +Ferrite.shape_hessian(cv::BezierCellValues, q_point::Int, i::Int) = shape_hessian(cv.nurbs_values, q_point, i) + +Ferrite.nfaces(fv::BezierFaceValues) = length(fv.geo_mapping) +Ferrite.getcurrentface(fv::BezierFaceValues) = fv.current_face[] +function Ferrite.set_current_face!(fv::BezierFaceValues, face_nr::Int) + checkbounds(Bool, 1:Ferrite.nfaces(fv), face_nr) || throw(ArgumentError("Face index out of range.")) + fv.current_face[] = face_nr +end #= function function_hessian(fe_v::Ferrite.AbstractValues, q_point::Int, u::AbstractVector{<:Vec}) @@ -151,8 +174,9 @@ function set_bezier_operator!(bcv::BezierCellAndFaceValues, beo::BezierExtractio end #This function can be called when we know that the weights are all equal to one. -function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, xb::Vector{<:Vec}) - x = spatial_coordinate(cv.cv_bezier, iqp, xb) +function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, xb::Vector{<:Vec{dim,T}}) where {dim,T} + wb = ones(T, length(xb)) + x = spatial_coordinate(cv, iqp, (xb, wb)) return x end @@ -292,7 +316,6 @@ function Ferrite.reinit!(cv::BezierCellValues, (x,w)::CoordsAndWeight) detJ = Ferrite.calculate_detJ(Ferrite.getjacobian(mapping)) detJ > 0.0 || Ferrite.throw_detJ_not_pos(detJ) cv.detJdV[q_point] = detJ * gauss_w - _compute_intermidiate!(cv.tmp_values, cv.bezier_values, cv.geo_mapping, q_point, w) Ferrite.apply_mapping!(cv.tmp_values, q_point, mapping) _bezier_transform(cv.nurbs_values, cv.tmp_values, q_point, cv.current_beo[], cv.current_w) @@ -300,10 +323,47 @@ function Ferrite.reinit!(cv::BezierCellValues, (x,w)::CoordsAndWeight) return nothing end -function _bezier_transform(nurbs::FunctionValues{1}, bezier::FunctionValues{1}, iq::Int, Cbe::BezierExtractionOperator{T}, w::Optional{Vector{T}}) where {T} - +function Ferrite.reinit!(cv::BezierFaceValues, bc::BezierCoords, face_nr::Int) + set_bezier_operator!(cv, bc.beo[], bc.w) + return reinit!(cv, (bc.xb, bc.wb), face_nr) +end + +function Ferrite.reinit!(fv::BezierFaceValues, (x,w)::CoordsAndWeight, face_nr::Int) + Ferrite.set_current_face!(fv, face_nr) + geo_mapping = fv.geo_mapping[face_nr] + bezier_values = fv.bezier_values[face_nr] + tmp_values = fv.tmp_values[face_nr] + nurbs_values = fv.nurbs_values[face_nr] + + n_geom_basefuncs = Ferrite.getngeobasefunctions(geo_mapping) + @assert isa(Ferrite.mapping_type(bezier_values), Ferrite.IdentityMapping) + @assert checkbounds(Bool, x, 1:n_geom_basefuncs) + @assert checkbounds(Bool, w, 1:n_geom_basefuncs) + + for (q_point, gauss_w) in enumerate(Ferrite.getweights(fv.qr, face_nr)) + mapping = Ferrite.calculate_mapping(geo_mapping, q_point, x, w) + + J = Ferrite.getjacobian(mapping) + weight_norm = Ferrite.weighted_normal(J, Ferrite.getrefshape(geo_mapping.ip), face_nr) + detJ = norm(weight_norm) + detJ > 0.0 || Ferrite.throw_detJ_not_pos(detJ) + @inbounds fv.detJdV[q_point] = detJ * gauss_w + @inbounds fv.normals[q_point] = weight_norm / detJ + + _compute_intermidiate!(tmp_values, bezier_values, geo_mapping, q_point, w) + Ferrite.apply_mapping!(tmp_values, q_point, mapping) + _bezier_transform(nurbs_values, tmp_values, q_point, fv.current_beo[], fv.current_w) + end + return nothing +end + + +function _bezier_transform(nurbs::FunctionValues{DIFFORDER}, bezier::FunctionValues{DIFFORDER}, iq::Int, Cbe::BezierExtractionOperator{T}, w::Optional{Vector{T}}) where {T,DIFFORDER} + @assert DIFFORDER < 3 N = length(Cbe) + d2Bdx2 = bezier.d2Ndx2 + d2Bdξ2 = bezier.d2Ndξ2 dBdx = bezier.dNdx dBdξ = bezier.dNdξ B = bezier.Nξ @@ -316,11 +376,19 @@ function _bezier_transform(nurbs::FunctionValues{1}, bezier::FunctionValues{1}, nurbs.Nξ[ib, iq] = zero(eltype(nurbs.Nξ)) nurbs.dNdξ[ib, iq] = zero(eltype(nurbs.dNdξ)) nurbs.dNdx[ib, iq] = zero(eltype(nurbs.dNdx)) - else #if FieldTrait(cv_nurbs) == Ferrite.VectorValued() + if DIFFORDER == 2 + nurbs.d2Ndx2[ib, iq] = zero(eltype(nurbs.d2Ndx2)) + nurbs.d2Ndξ2[ib, iq] = zero(eltype(nurbs.d2Ndξ2)) + end + else for d in 1:dim_s nurbs.Nξ[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.Nξ)) nurbs.dNdξ[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.dNdξ)) nurbs.dNdx[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.dNdx)) + if DIFFORDER == 2 + nurbs.d2Ndx2[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.d2Ndx2)) + nurbs.d2Ndξ2[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.d2Ndξ2)) + end end end @@ -332,47 +400,87 @@ function _bezier_transform(nurbs::FunctionValues{1}, bezier::FunctionValues{1}, val*=w[ib] end if is_scalar_valued - nurbs.Nξ[ib, iq] += val* B[nz_ind, iq] + nurbs.Nξ[ib, iq] += val* B[nz_ind, iq] nurbs.dNdξ[ib, iq] += val*dBdξ[nz_ind, iq] nurbs.dNdx[ib, iq] += val*dBdx[nz_ind, iq] + if DIFFORDER == 2 + nurbs.d2Ndξ2[ib, iq] += val*d2Bdξ2[nz_ind, iq] + nurbs.d2Ndx2[ib, iq] += val*d2Bdx2[nz_ind, iq] + end else for d in 1:dim_s - nurbs.Nξ[(ib-1)*dim_s + d, iq] += val* B[(nz_ind-1)*dim_s + d, iq] + nurbs.Nξ[(ib-1)*dim_s + d, iq] += val* B[(nz_ind-1)*dim_s + d, iq] nurbs.dNdξ[(ib-1)*dim_s + d, iq] += val*dBdξ[(nz_ind-1)*dim_s + d, iq] nurbs.dNdx[(ib-1)*dim_s + d, iq] += val*dBdx[(nz_ind-1)*dim_s + d, iq] + if DIFFORDER == 2 + nurbs.d2Ndξ2[(ib-1)*dim_s + d, iq] += val*d2Bdξ2[(nz_ind-1)*dim_s + d, iq] + nurbs.d2Ndx2[(ib-1)*dim_s + d, iq] += val*d2Bdx2[(nz_ind-1)*dim_s + d, iq] + end end end end end - end Ferrite.otimes_helper(x::Number, dMdξ::Vec{dim}) where dim = x * dMdξ -function _compute_intermidiate!(tmp_values::FunctionValues{1}, bezier_values::FunctionValues{1}, geom_values::GeometryMapping, q_point::Int, w::Vector{T}) where {T} +function _compute_intermidiate!(tmp_values::FunctionValues{DIFFORDER}, bezier_values::FunctionValues{DIFFORDER}, geom_values::GeometryMapping, q_point::Int, w::Vector{T}) where {T,DIFFORDER} dim = Ferrite.sdim_from_gradtype(Ferrite.shape_gradient_type(tmp_values)) + @assert DIFFORDER < 3 "Diff order > 2 not supported" W = zero(T) dWdξ = zero(Vec{dim,T}) + d2Wdξ2 = zero(Tensor{2,dim,T}) for j in 1:Ferrite.getngeobasefunctions(geom_values) W += w[j]*geom_values.M[j, q_point] dWdξ += w[j]*geom_values.dMdξ[j, q_point] + if DIFFORDER == 2 + d2Wdξ2 += w[j]*geom_values.d2Mdξ2[j, q_point] + end end for j in 1:getnbasefunctions(tmp_values) tmp_values.dNdξ[j, q_point] = ( bezier_values.dNdξ[j, q_point]*W - Ferrite.otimes_helper(bezier_values.Nξ[j, q_point], dWdξ) ) / W^2 tmp_values.Nξ[j,q_point] = bezier_values.Nξ[j, q_point]/W + + if DIFFORDER == 2 + is_vector_valued = (first(tmp_values.Nξ) isa Vec) + if is_vector_valued + _B = bezier_values.Nξ[j, q_point] + _dBdξ = bezier_values.dNdξ[j, q_point] + _d²Bdξ² = bezier_values.d2Ndξ2[j, q_point] + tmp = _dBdξ⊗dWdξ + tmp = permutedims(tmp, (1,3,2)) + tmp = Tensor{3,dim}(tmp) + + Fij = _dBdξ*W - _B⊗dWdξ + S = W^2 + Fij_k = (_d²Bdξ²*W + _dBdξ⊗dWdξ) - (tmp + _B⊗d2Wdξ2) + S_k = 2*W*dWdξ + + tmp_values.d2Ndξ2[j, q_point] = (Fij_k*S - Fij⊗S_k)/S^2 + else + _B = bezier_values.Nξ[j, q_point] + _dBdξ = bezier_values.dNdξ[j, q_point] + _d²Bdξ² = bezier_values.d2Ndξ2[j, q_point] + + S = W^2 + Fi = _dBdξ*W - _B⊗dWdξ + Fi_j = (_d²Bdξ²*W + _dBdξ⊗dWdξ) - (dWdξ⊗_dBdξ + _B⊗d2Wdξ2) + S_j = 2*W*dWdξ + tmp_values.d2Ndξ2[j, q_point] = (Fi_j*S - Fi⊗S_j)/S^2 + end + end end end -function Ferrite.apply_mapping!(tmp_values::FunctionValues{1}, q_point::Int, mapping::MappingValues) +#=function Ferrite.apply_mapping!(tmp_values::FunctionValues{1}, q_point::Int, mapping::MappingValues) Jinv = Ferrite.calculate_Jinv(Ferrite.getjacobian(mapping)) for j in 1:getnbasefunctions(tmp_values) tmp_values.dNdx[j, q_point] = Ferrite.dothelper(tmp_values.dNdξ[j, q_point], Jinv) end -end +end=# function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{1}, q_point, x::Vector{Vec{dim,T}}, w::Vector{T}) where {dim,T} - W = zero(T) dWdξ = zero(Vec{dim,T}) #d²Wdξ² = zero(Tensor{2,dim,T}) @@ -389,6 +497,34 @@ function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{1}, q_po return Ferrite.MappingValues(fecv_J, nothing) end +function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{2}, q_point, x::Vector{Vec{dim,T}}, w::Vector{T}) where {dim,T} + W = zero(T) + dWdξ = zero(Vec{dim,T}) + d²Wdξ² = zero(Tensor{2,dim,T}) + for j in 1:Ferrite.getngeobasefunctions(geo_mapping) + W += w[j]*geo_mapping.M[ j, q_point] + dWdξ += w[j]*geo_mapping.dMdξ[ j, q_point] + d²Wdξ² += w[j]*geo_mapping.d2Mdξ2[j, q_point] + end + + J = zero(Tensor{2,dim,T}) + H = zero(Tensor{3,dim,T}) + for j in 1:Ferrite.getngeobasefunctions(geo_mapping) + dRdξ = (geo_mapping.dMdξ[j, q_point]*W - geo_mapping.M[j, q_point]*dWdξ)/W^2 + J += x[j] ⊗ (w[j]*dRdξ) + + Fi_j = (geo_mapping.d2Mdξ2[j, q_point]*W +geo_mapping.dMdξ[j, q_point]⊗dWdξ) - (dWdξ⊗geo_mapping.dMdξ[j, q_point] + geo_mapping.M[j, q_point]*d²Wdξ²) + S_j = 2*W*dWdξ + S = W^2 + Fi = geo_mapping.dMdξ[j, q_point]*W - geo_mapping.M[j, q_point]*dWdξ + + d²Rdξ² = (Fi_j*S - Fi⊗S_j)/S^2 + H += x[j] ⊗ (w[j]*d²Rdξ²) + + end + return Ferrite.MappingValues(J, H) +end + """ Ferrite.reinit!(cv::Ferrite.CellVectorValues{dim}, xᴮ::AbstractVector{Vec{dim,T}}, w::AbstractVector{T}) where {dim,T} diff --git a/test/test_values.jl b/test/test_values.jl index 0313156..4bf71eb 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -104,13 +104,13 @@ end reorder = IGA._bernstein_ordering(bip) - qr = QuadratureRule{shape}(1) + qr = QuadratureRule{shape}(2) qr_face = FaceQuadratureRule{shape}(3) - fv = FaceValues( qr_face, ip, ip ) - fv_vector = FaceValues( qr_face, ip^dim, ip ) - cv = CellValues( qr, ip, ip ) - cv_vector = CellValues( qr, ip^dim, ip) + fv = FaceValues( qr_face, ip, ip; update_hessians = true ) + fv_vector = FaceValues( qr_face, ip^dim, ip; update_hessians = true ) + cv = CellValues( qr, ip, ip; update_hessians = true) + cv_vector = CellValues( qr, ip^dim, ip; update_hessians = true) #Try some different cells cellnum = 1 @@ -122,7 +122,7 @@ end bc = getcoordinates(grid, cellnum) reinit!(cv, bc) reinit!(cv_vector, bc) - @show cv.current_w + (; xb, wb, x, w) = bc iqp = 1 @@ -150,54 +150,51 @@ end @test dV_patch ≈ getdetJdV(cv, iqp) @test sum(cv.nurbs_values.Nξ[:,iqp]) ≈ 1 for i in 1:getnbasefunctions(cv) - @test shape_value(cv,iqp,i) ≈ R[i] + @test shape_value(cv, iqp,i) ≈ R[i] @test shape_gradient(cv, iqp, i) ≈ dRdX[i] - @test cv.nurbs_values.dNdξ[i,iqp] ≈ dRdξ[i] + @test shape_hessian(cv, iqp, i) ≈ d²RdX²[i] atol=1e-12 + + @test cv.nurbs_values.dNdξ[ i,iqp] ≈ dRdξ[i] atol=1e-12 + @test cv.nurbs_values.d2Ndξ2[i,iqp] ≈ d²Rdξ²[i] atol=1e-12 end - @test cv.d²Ndξ²[:,iqp] ≈ d²Rdξ² atol=1e-14 - @test cv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 #Check if VectorValues is same as ScalarValues basefunc_count = 1 + i = 1 + comp = 1 for i in 1:nb_per_cell for comp in 1:dim N_comp = zeros(Float64, dim) - N_comp[comp] = cv.cv_nurbs.N[i, iqp] + N_comp[comp] = shape_value(cv, iqp, i) _N = Vec{dim,Float64}((N_comp...,)) - @test cv_vector.cv_nurbs.N[basefunc_count, iqp] ≈ _N atol = 1e-15 + @test shape_value(cv_vector,iqp,basefunc_count) ≈ _N atol=1e-15 - dN_comp = zeros(Float64, dim, dim) - dN_comp[comp, :] = cv.cv_nurbs.dNdξ[i, iqp] - _dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) - - @test cv_vector.cv_nurbs.dNdξ[basefunc_count, iqp] ≈ _dNdξ atol = 1e-15 + #dN_comp = zeros(Float64, dim, dim) + #dN_comp[comp, :] = cv.cv_nurbs.dNdξ[i, iqp] + #_dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) + #@test cv_vector.cv_nurbs.dNdξ[basefunc_count, iqp] ≈ _dNdξ atol = 1e-15 dN_comp = zeros(Float64, dim, dim) - dN_comp[comp, :] = cv.cv_nurbs.dNdx[i, iqp] + dN_comp[comp, :] = shape_gradient(cv,iqp,i) _dNdx = Tensor{2,dim,Float64}((dN_comp...,)) - - @test cv_vector.cv_nurbs.dNdx[basefunc_count, iqp] ≈ _dNdx atol = 1e-15 + @test shape_gradient(cv_vector,iqp,basefunc_count) ≈ _dNdx atol = 1e-15 basefunc_count += 1 end end - end end addfaceset!(grid, "face1", (x)-> x[1] == -4.0) for (cellnum, faceidx) in getfaceset(grid, "face1") - Xb, wb = get_bezier_coordinates(grid, cellnum) - C = get_extraction_operator(grid, cellnum) - X = get_nurbs_coordinates(grid, cellnum) - w = Ferrite.getweights(grid, cellnum) - bc = getcoordinates(grid, cellnum) reinit!(fv, bc, faceidx) reinit!(fv_vector, bc, faceidx) + (; xb, wb, x, w) = bc + for iqp in 1:getnquadpoints(fv) ξ = qr_face.face_rules[faceidx].points[iqp] @@ -206,11 +203,9 @@ end #Calculate the value of the NURBS from the nurbs patch R, dRdξ, d²Rdξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) - #Calculate the value of the NURBS from the nurbs patch - R, dRdξ, d²Rdξ² = bspline_values(nurbsmesh, cellnum, ξ, reorder) - J = sum(x .⊗ dRdξ) H = sum(x .⊗ d²Rdξ²) + wn = Ferrite.weighted_normal(J, shape, faceidx) dV_patch = norm(Ferrite.weighted_normal(J, shape, faceidx))*qrw dRdX = similar(dRdξ) @@ -224,35 +219,36 @@ end d²RdX²[i] = inv(J)' ⋅ d²Rdξ²[i] ⋅ inv(J) - inv(J)' ⋅ FF ⋅ inv(J) end - @test dV_patch ≈ getdetJdV(cv, iqp) - @test sum(fv.cv_nurbs.N[:,iqp]) ≈ 1 - @test fv.cv_nurbs.N[:,iqp] ≈ R - @test fv.cv_nurbs.dNdξ[:,iqp] ≈ dRdξ - @test fv.cv_nurbs.dNdx[:,iqp] ≈ dRdX - @test fv.d²Ndξ²[:,iqp] ≈ d²Rdξ² atol=1e-14 - @test fv.d²NdX²[:,iqp] ≈ d²RdX² atol=1e-14 + @test getnormal(fv, iqp) ≈ Vec((-1.0, 0.0)) + @test dV_patch ≈ getdetJdV(fv, iqp) + @test sum(fv.nurbs_values[faceidx].Nξ[:,iqp]) ≈ 1 + for i in 1:getnbasefunctions(fv) + @test shape_value(fv, iqp,i) ≈ R[i] + @test shape_gradient(fv, iqp, i) ≈ dRdX[i] + @test shape_hessian(fv, iqp, i) ≈ d²RdX²[i] atol=1e-11 + + @test fv.nurbs_values[faceidx].dNdξ[ i,iqp] ≈ dRdξ[i] atol=1e-11 + @test fv.nurbs_values[faceidx].d2Ndξ2[i,iqp] ≈ d²Rdξ²[i] atol=1e-11 + end #Check if VectorValues is same as ScalarValues basefunc_count = 1 for i in 1:nb_per_cell for comp in 1:dim N_comp = zeros(Float64, dim) - N_comp[comp] = fv.cv_nurbs.N[i, iqp, faceidx] + N_comp[comp] = shape_value(fv,iqp,i) _N = Vec{dim,Float64}((N_comp...,)) + @test shape_value(fv_vector,iqp,basefunc_count) ≈ _N atol = 1e-15 - @test fv_vector.cv_nurbs.N[basefunc_count, iqp, faceidx] ≈ _N - - dN_comp = zeros(Float64, dim, dim) - dN_comp[comp, :] = fv.cv_nurbs.dNdξ[i, iqp, faceidx] - _dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) - - @test fv_vector.cv_nurbs.dNdξ[basefunc_count, iqp, faceidx] ≈ _dNdξ + #dN_comp = zeros(Float64, dim, dim) + #dN_comp[comp, :] = fv.cv_nurbs.dNdξ[i, iqp, faceidx] + #_dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) + #@test fv_vector.cv_nurbs.dNdξ[basefunc_count, iqp, faceidx] ≈ _dNdξ dN_comp = zeros(Float64, dim, dim) - dN_comp[comp, :] = fv.cv_nurbs.dNdx[i, iqp, faceidx] + dN_comp[comp, :] = shape_gradient(fv,iqp,i) _dNdx = Tensor{2,dim,Float64}((dN_comp...,)) - - @test fv_vector.cv_nurbs.dNdx[basefunc_count, iqp, faceidx] ≈ _dNdx + @test shape_gradient(fv_vector,iqp,basefunc_count) ≈ _dNdx atol = 1e-15 basefunc_count += 1 end From bc7197575bc104c969342e3c133eeeccb5698956 Mon Sep 17 00:00:00 2001 From: lijas Date: Fri, 16 Feb 2024 17:19:48 +0100 Subject: [PATCH 24/50] asdf --- src/iterators.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iterators.jl b/src/iterators.jl index 518aa8d..16da3ff 100644 --- a/src/iterators.jl +++ b/src/iterators.jl @@ -14,10 +14,10 @@ end function IGACellCache(dh::DofHandler{dim,G}, flags::UpdateFlags=UpdateFlags()) where {dim,G} grid = Ferrite.get_grid(dh) T = Ferrite.get_coordinate_eltype(grid) - N = Ferrite.nnodes_per_cell(grid) + N = Ferrite.nnodes_per_cell(grid, 1) nodes = zeros(Int, N) coords = zero_bezier_coord(dim,T,N) - n = ndofs_per_cell(dh) + n = ndofs_per_cell(dh, 1) celldofs = zeros(Int, n) return IGACellCache(flags, grid, Ferrite.ScalarWrapper(-1), nodes, coords, dh, celldofs) From 1cfd1d59a190e1549dfb3dfd078d28531543cc9e Mon Sep 17 00:00:00 2001 From: lijas Date: Sun, 7 Apr 2024 14:02:09 +0200 Subject: [PATCH 25/50] fix allocation when getting weights (bug in julia?) --- src/bezier_grid.jl | 53 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index 57ebaf0..b63fe19 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -4,6 +4,23 @@ struct BezierGrid{dim,C<:Ferrite.AbstractCell,T<:Real} <: Ferrite.AbstractGrid{d grid ::Ferrite.Grid{dim,C,T} weights ::Vector{Float64} beo ::Vector{BezierExtractionOperator{Float64}} + + #Alias from grid + cells::Vector{C} + nodes::Vector{Node{dim,T}} + cellsets::Dict{String,Set{Int}} + nodesets::Dict{String,Set{Int}} + facesets::Dict{String,Set{FaceIndex}} + edgesets::Dict{String,Set{EdgeIndex}} + vertexsets::Dict{String,Set{VertexIndex}} + boundary_matrix::Ferrite.SparseMatrixCSC{Bool,Int} + + function BezierGrid(grid::Ferrite.Grid{dim,C,T}, weights::Vector{T}, beo::Vector{BezierExtractionOperator{T}}) where {dim,C,T} + return new{dim,C,T}( + grid, weights, beo, + grid.cells, grid.nodes, grid.cellsets, grid.nodesets, grid.facesets, grid.edgesets, grid.vertexsets, grid.boundary_matrix + ) + end end function BezierGrid(cells::Vector{C}, @@ -18,9 +35,9 @@ function BezierGrid(cells::Vector{C}, boundary_matrix::SparseArrays.SparseMatrixCSC{Bool,Int} = SparseArrays.spzeros(Bool, 0, 0)) where {dim,C,T} - grid = Ferrite.Grid(cells, nodes; nodesets, cellsets, facesets, vertexsets, boundary_matrix) + grid = Ferrite.Grid(cells, nodes; nodesets, cellsets, facesets, edgesets, vertexsets, boundary_matrix) - return BezierGrid{dim,C,T}(grid, weights, extraction_operator) + return BezierGrid(grid, weights, extraction_operator) end function BezierGrid(mesh::NURBSMesh{pdim,sdim}) where {pdim,sdim} @@ -56,26 +73,6 @@ function BezierGrid(grid::Ferrite.Grid{dim,C,T}) where {dim,C,T} return BezierGrid{dim,C,T}(grid, weights, extraction_operator) end -function Base.getproperty(m::BezierGrid, s::Symbol) - if s === :nodes - return getfield(m.grid, :nodes) - elseif s === :cells - return getfield(m.grid, :cells) - elseif s === :cellsets - return getfield(m.grid, :cellsets) - elseif s === :nodesets - return getfield(m.grid, :nodesets) - elseif s === :facesets - return getfield(m.grid, :facesets) - elseif s === :edgesets - return getfield(m.grid, :edgesets) - elseif s === :vertexsets - return getfield(m.grid, :vertexsets) - else - return getfield(m, s) - end -end - function Base.show(io::IO, ::MIME"text/plain", grid::BezierGrid) print(io, "$(typeof(grid)) with $(getncells(grid)) ") if isconcretetype(eltype(grid.cells)) @@ -92,16 +89,18 @@ end Returns the weights (for the nurbs interpolation) for cell with id `cellid`. """ -@inline function getweights!(w::Vector{T}, grid::BezierGrid, cellid::Int) where {T} +Base.@propagate_inbounds function getweights!(w::Vector, grid::BezierGrid, cellid::Int) cell = grid.cells[cellid] getweights!(w, grid, cell) end -@inline function getweights!(w::Vector{T}, grid::BezierGrid, cell::Ferrite.AbstractCell) where {T} - for i in 1:length(w) - w[i] = grid.weights[cell.nodes[i]] +Base.@propagate_inbounds function getweights!(w::Vector, grid::BezierGrid, cell::Ferrite.AbstractCell) + node_ids = Ferrite.get_node_ids(cell) + nnodes = length(node_ids) + @boundscheck checkbounds(Bool, w, 1:nnodes) + @inbounds for i in 1:nnodes + w[i] = grid.weights[i] end - return w end function Ferrite.getweights(grid::BezierGrid, ic::Int) From 3dbc9fae454fd72fc3149b5d46dd15bb762fc54e Mon Sep 17 00:00:00 2001 From: lijas Date: Sun, 7 Apr 2024 14:37:53 +0200 Subject: [PATCH 26/50] move apply_analytical --- src/apply_analytical_iga.jl | 87 +++++++++++++++++++++++++++++++++++++ test/test_l2proj.jl | 8 ++-- 2 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 src/apply_analytical_iga.jl diff --git a/src/apply_analytical_iga.jl b/src/apply_analytical_iga.jl new file mode 100644 index 0000000..7765811 --- /dev/null +++ b/src/apply_analytical_iga.jl @@ -0,0 +1,87 @@ +function _default_interpolations(dh::DofHandler) + sdhs = dh.subdofhandlers + getcelltype(i) = typeof(getcells(Ferrite.get_grid(dh), first(sdhs[i].cellset))) + ntuple(i -> Ferrite.default_interpolation(getcelltype(i)), length(sdhs)) +end + +""" + apply_analytical!( + a::AbstractVector, dh::AbstractDofHandler, fieldname::Symbol, + f::Function, cellset=1:getncells(get_grid(dh))) + +Apply a solution `f(x)` by modifying the values in the degree of freedom vector `a` +pertaining to the field `fieldname` for all cells in `cellset`. +The function `f(x)` are given the spatial coordinate +of the degree of freedom. For scalar fields, `f(x)::Number`, +and for vector fields with dimension `dim`, `f(x)::Vec{dim}`. + +This function can be used to apply initial conditions for time dependent problems. + +!!! note + + This function only works for standard nodal finite element interpolations + when the function value at the (algebraic) node is equal to the corresponding + degree of freedom value. + This holds for e.g. Lagrange and Serendipity interpolations, including + sub- and superparametric elements. +""" +function apply_analytical_iga!( + a::AbstractVector, dh::DofHandler, fieldname::Symbol, f::Function, + cellset = 1:getncells(Ferrite.get_grid(dh))) + + fieldname ∉ Ferrite.getfieldnames(dh) && error("The fieldname $fieldname was not found in the dof handler") + ip_geos = _default_interpolations(dh) + + for (sdh, ip_geo) in zip(dh.subdofhandlers, ip_geos) + isnothing(Ferrite._find_field(sdh, fieldname)) && continue + field_idx = Ferrite.find_field(sdh, fieldname) + ip_fun = Ferrite.getfieldinterpolation(sdh, field_idx) + field_dim = Ferrite.getfielddim(sdh, field_idx) + celldofinds = Ferrite.dof_range(sdh, fieldname) + set_intersection = if length(cellset) == length(sdh.cellset) == getncells(Ferrite.get_grid(dh)) + BitSet(1:getncells(Ferrite.get_grid(dh))) + else + intersect(BitSet(sdh.cellset), BitSet(cellset)) + end + _apply_analytical2!(a, dh, celldofinds, field_dim, ip_fun, ip_geo, f, set_intersection) + end + return a +end + + +function _apply_analytical2!( + a::AbstractVector, dh::Ferrite.AbstractDofHandler, celldofinds, field_dim, + ip_fun::Interpolation{RefShape}, ip_geo::Interpolation, f::Function, cellset) where {dim, RefShape<:AbstractRefShape{dim}} + + coords = getcoordinates(Ferrite.get_grid(dh), first(cellset)) + ref_points = Ferrite.reference_coordinates(ip_fun) + dummy_weights = zeros(length(ref_points)) + qr = QuadratureRule{RefShape}(dummy_weights, ref_points) + # Note: Passing ip_geo as the function interpolation here, it is just a dummy. + cv = CellValues(qr, ip_geo, ip_geo) + c_dofs = celldofs(dh, first(cellset)) + f_dofs = zeros(Int, length(celldofinds)) + + # Check f before looping + #length(f(first(coords))) == field_dim || error("length(f(x)) must be equal to dimension of the field ($field_dim)") + + for cellnr in cellset + getcoordinates!(coords, Ferrite.get_grid(dh), cellnr) + celldofs!(c_dofs, dh, cellnr) + for (i, celldofind) in enumerate(celldofinds) + f_dofs[i] = c_dofs[celldofind] + end + _apply_analytical2!(a, f_dofs, coords, field_dim, cv, f) + end + return a +end + +function _apply_analytical2!(a::AbstractVector, dofs::Vector{Int}, coords, field_dim, cv::Ferrite.AbstractCellValues, f) + for i_dof in 1:getnquadpoints(cv) + x_dof = spatial_coordinate(cv, i_dof, coords) + for (idim, icval) in enumerate(f(x_dof)) + a[dofs[field_dim*(i_dof-1)+idim]] = icval + end + end + return a +end diff --git a/test/test_l2proj.jl b/test/test_l2proj.jl index 4600f62..6b9494c 100644 --- a/test/test_l2proj.jl +++ b/test/test_l2proj.jl @@ -49,7 +49,7 @@ function test_projection() elseif order == 2 # For a quadratic approximation the analytical solution is recovered ae = zeros(length(point_vars)) - apply_analytical!(ae, proj.dh, :_, f) + apply_analytical_iga!(ae, proj.dh, :_, f) end @@ -74,7 +74,7 @@ function test_projection() ae = [Vec{1,Float64}((f_approx(j),)) for j in 1:4] elseif order == 2 ae = zeros(length(point_vars)) - apply_analytical!(ae, proj.dh, :_, x -> f_vector(x)[1]) + apply_analytical_iga!(ae, proj.dh, :_, x -> f_vector(x)[1]) ae = reinterpret(Vec{1,Float64}, ae) end @test point_vars ≈ ae @@ -90,7 +90,7 @@ function test_projection() elseif order == 2 ae = zeros(4, length(point_vars)) for i in 1:4 - apply_analytical!(@view(ae[i, :]), proj.dh, :_, x -> f_tensor(x)[i]) + apply_analytical_iga!(@view(ae[i, :]), proj.dh, :_, x -> f_tensor(x)[i]) end ae = reinterpret(reshape, Tensor{2,2,Float64,4}, ae) end @@ -107,7 +107,7 @@ function test_projection() elseif order == 2 ae = zeros(3, length(point_vars)) for i in 1:3 - apply_analytical!(@view(ae[i, :]), proj.dh, :_, x -> f_stensor(x).data[i]) + apply_analytical_iga!(@view(ae[i, :]), proj.dh, :_, x -> f_stensor(x).data[i]) end ae = reinterpret(reshape, SymmetricTensor{2,2,Float64,3}, ae) end From f14b4efbe8cac8615a70ea504646973cb52498bc Mon Sep 17 00:00:00 2001 From: lijas Date: Sun, 7 Apr 2024 14:38:13 +0200 Subject: [PATCH 27/50] fix getter weights --- src/bezier_grid.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl index b63fe19..d4462b3 100644 --- a/src/bezier_grid.jl +++ b/src/bezier_grid.jl @@ -70,7 +70,7 @@ function BezierGrid(grid::Ferrite.Grid{dim,C,T}) where {dim,C,T} push!(extraction_operator, beo) end - return BezierGrid{dim,C,T}(grid, weights, extraction_operator) + return BezierGrid(grid, weights, extraction_operator) end function Base.show(io::IO, ::MIME"text/plain", grid::BezierGrid) @@ -99,7 +99,7 @@ Base.@propagate_inbounds function getweights!(w::Vector, grid::BezierGrid, cell: nnodes = length(node_ids) @boundscheck checkbounds(Bool, w, 1:nnodes) @inbounds for i in 1:nnodes - w[i] = grid.weights[i] + w[i] = grid.weights[node_ids[i]] end end From 13056dac9da1ec58fbaeb98c3d20493f9350c603 Mon Sep 17 00:00:00 2001 From: lijas Date: Sun, 7 Apr 2024 14:38:32 +0200 Subject: [PATCH 28/50] various changes and fixes --- Project.toml | 3 +- docs/src/literate/plate_with_hole.jl | 10 +-- src/IGA.jl | 45 ++---------- src/VTK.jl | 15 ++-- src/bezier_extraction.jl | 1 + src/nurbsmesh.jl | 2 +- src/splines/bezier.jl | 6 +- src/splines/bezier_values.jl | 100 +++++++++++++++++++-------- test/test_bertstein.jl | 2 +- test/test_bezier_extraction.jl | 6 +- test/test_meshes.jl | 6 +- test/test_nurbsmesh.jl | 2 +- test/test_values.jl | 58 ++++++++-------- 13 files changed, 132 insertions(+), 124 deletions(-) diff --git a/Project.toml b/Project.toml index 8a5db2b..d7ee3e3 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.2.6" [deps] Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" @@ -13,4 +14,4 @@ Tensors = "48a634ad-e948-5137-8d70-aa71f2a747f4" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" [compat] -WriteVTK = "1.13" \ No newline at end of file +WriteVTK = "1.13" diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl index a6a0ba9..6bdfe58 100644 --- a/docs/src/literate/plate_with_hole.jl +++ b/docs/src/literate/plate_with_hole.jl @@ -53,7 +53,7 @@ end; function assemble_problem(dh::DofHandler, grid, cv, fv, stiffmat, traction) f = zeros(ndofs(dh)) - K = create_sparsity_pattern(dh) + K = create_matrix(dh) assembler = start_assemble(K, f) n = getnbasefunctions(cv) @@ -130,7 +130,7 @@ function calculate_stress(dh, cv::BezierCellValues, C::SymmetricTensor{4,2}, u:: ue = u[celldofs] qp_stresses = SymmetricTensor{2,2,Float64,3}[] for qp in 1:getnquadpoints(cv) - ɛ = symmetric(function_gradient(cv, qp, ue)) + ε = symmetric(function_gradient(cv, qp, ue)) σ = C ⊡ ε push!(qp_stresses, σ) end @@ -173,11 +173,11 @@ function solve() # Distribute dofs as normal dh = DofHandler(grid) - push!(dh, :u, ip_u) + add!(dh, :u, ip_u) close!(dh) ae = zeros(ndofs(dh)) - apply_analytical!(ae, dh, :u, x->x) + IGA.apply_analytical_iga!(ae, dh, :u, x->x) # Add two symmetry boundary condintions. Bottom face should only be able to move in x-direction, and the right boundary should only be able to move in y-direction ch = ConstraintHandler(dh) @@ -215,7 +215,7 @@ function solve() IGA.VTKIGAFile("plate_with_hole.vtu", grid, collect(1:getncells(grid))) do vtk IGA.write_solution(vtk, dh, u) - IGA.write_projected(vtk, dh, u) + #IGA.write_projected(vtk, projector, σ_nodes, "σ") end end; diff --git a/src/IGA.jl b/src/IGA.jl index e62e907..522b5d7 100644 --- a/src/IGA.jl +++ b/src/IGA.jl @@ -62,7 +62,7 @@ Ferrite.shape_value(::IGAInterpolation{shape, order}, ξ::Vec, i::Int) where {sh Ferrite.reference_coordinates(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.reference_coordinates(Bernstein{shape, order}()) Ferrite.getnbasefunctions(::IGAInterpolation{shape, order}) where {shape, order} = getnbasefunctions(Bernstein{shape, order}()) -#Ferrite.nvertices(ip::IGAInterpolation) = getnbasefunctions(ip) +Ferrite.nvertices(ip::IGAInterpolation) = getnbasefunctions(ip) Ferrite.vertexdof_indices(ip::IGAInterpolation) = ntuple(i->i, getnbasefunctions(ip)) #Remove dofs on edges and faces such that dofhandler can distribute dofs correctly @@ -75,7 +75,7 @@ Ferrite.facedof_interior_indices(ip::IGAInterpolation{RefQuadrilateral}) = ntup Ferrite.dirichlet_facedof_indices(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.dirichlet_facedof_indices(Bernstein{shape, order}()) Ferrite.dirichlet_edgedof_indices(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.dirichlet_edgedof_indices(Bernstein{shape, order}()) -Ferrite.dirichlet_vertexdof_indices(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.dirichlet_vertexdof_indices(Bernstein{shape, order}()) +#Ferrite.dirichlet_vertexdof_indices(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.dirichlet_vertexdof_indices(Bernstein{shape, order}()) @@ -112,6 +112,7 @@ include("splines/bsplines.jl") include("VTK.jl") include("iterators.jl") include("iterators_future.jl") +include("apply_analytical_iga.jl") #include("L2_projection.jl") Ferrite._mass_qr(::IGAInterpolation{shape,order}) where {shape,order}= Ferrite._mass_qr(Bernstein{shape, order}()) @@ -129,46 +130,12 @@ function Ferrite.faces(c::BezierCell{shape,order}) where {shape,order} return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_facedof_indices(IGAInterpolation{shape,order}() ))) end -function Ferrite.edges(c::BezierCell{order,RefHexahedron}) where {order} +function Ferrite.edges(c::BezierCell{RefHexahedron, order}) where {order} return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_edgedof_indices(IGAInterpolation{RefHexahedron,order}() ))) end - -function Ferrite._apply_analytical!( - a::AbstractVector, dh::Ferrite.AbstractDofHandler, celldofinds, field_dim, - ip_fun::Interpolation{RefShape}, ip_geo::Interpolation, f::Function, cellset) where {dim, RefShape<:AbstractRefShape{dim}} - - coords = getcoordinates(Ferrite.get_grid(dh), first(cellset)) - ref_points = Ferrite.reference_coordinates(ip_fun) - dummy_weights = zeros(length(ref_points)) - qr = QuadratureRule{RefShape}(dummy_weights, ref_points) - # Note: Passing ip_geo as the function interpolation here, it is just a dummy. - cv = CellValues(qr, ip_geo, ip_geo) - c_dofs = celldofs(dh, first(cellset)) - f_dofs = zeros(Int, length(celldofinds)) - - # Check f before looping - #length(f(first(coords))) == field_dim || error("length(f(x)) must be equal to dimension of the field ($field_dim)") - - for cellnr in cellset - getcoordinates!(coords, Ferrite.get_grid(dh), cellnr) - celldofs!(c_dofs, dh, cellnr) - for (i, celldofind) in enumerate(celldofinds) - f_dofs[i] = c_dofs[celldofind] - end - _apply_analytical2!(a, f_dofs, coords, field_dim, cv, f) - end - return a -end - -function _apply_analytical2!(a::AbstractVector, dofs::Vector{Int}, coords, field_dim, cv::Ferrite.AbstractCellValues, f) - for i_dof in 1:getnquadpoints(cv) - x_dof = spatial_coordinate(cv, i_dof, coords) - for (idim, icval) in enumerate(f(x_dof)) - a[dofs[field_dim*(i_dof-1)+idim]] = icval - end - end - return a +function Ferrite.edges(c::BezierCell{RefQuadrilateral, order}) where {order} + return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_edgedof_indices(IGAInterpolation{RefQuadrilateral,order}() ))) end end #end module \ No newline at end of file diff --git a/src/VTK.jl b/src/VTK.jl index 3263ac3..2886f8f 100644 --- a/src/VTK.jl +++ b/src/VTK.jl @@ -138,9 +138,9 @@ function #=Ferrite.=#write_solution(vtk::VTKIGAFile, dh::DofHandler, a, suffix=" end end -function #=Ferrite.=#write_projection(vtk::VTKIGAFile, proj::L2Projector, vals, name) - data = _evaluate_at_grid_nodes(proj, vals, #=vtk=# Val(true))::Matrix - @assert size(data, 2) == getnnodes(get_grid(proj.dh)) +function #=Ferrite.=#write_projected(vtk::VTKIGAFile, proj::L2Projector, vals, name) + data = Ferrite._evaluate_at_grid_nodes(proj, vals, #=vtk=# Val(true))::Matrix + @assert size(data, 2) == getnnodes(Ferrite.get_grid(proj.dh)) vtk_node_data(vtk.vtk, data, name; component_names=Ferrite.component_names(eltype(vals))) return vtk end @@ -198,7 +198,8 @@ function _evaluate_at_geometry_nodes!( return data end - +is_scalar_interpolaiton(::BezierCellValues{<:Ferrite.FunctionValues{DiffOrder, <:Ferrite.VectorInterpolation}}) where DiffOrder = false +is_scalar_interpolaiton(::BezierCellValues{<:Ferrite.FunctionValues{DiffOrder, <:Ferrite.ScalarInterpolation}}) where DiffOrder = true function _evaluate_at_geometry_nodes!(data, sdh, a::Vector{T}, cv, drange, cellset, ::Type{RT}) where {T, RT} dh = sdh.dh @@ -209,7 +210,7 @@ function _evaluate_at_geometry_nodes!(data, sdh, a::Vector{T}, cv, drange, cells ue = zeros(eltype(a), ncelldofs) # TODO: Remove this hack when embedding works... - if RT <: Vec && cv isa BezierCellValues{T, <:CellValues{<:ScalarInterpolation}} + if RT <: Vec && is_scalar_interpolaiton(cv) uer = reinterpret(RT, ue) else uer = ue @@ -219,9 +220,9 @@ function _evaluate_at_geometry_nodes!(data, sdh, a::Vector{T}, cv, drange, cells bcoords = getcoordinates(dh.grid, first(cellset)) offset = 0 for cellid in cellset - getcoordinates!(bcoords, dh.grid, cellid) - reinit!(cv, bcoords) + + reinit_values!(cv, bcoords) celldofs!(dofs, sdh, cellid) for (i, I) in pairs(drange) diff --git a/src/bezier_extraction.jl b/src/bezier_extraction.jl index 8dc199a..152cc6e 100644 --- a/src/bezier_extraction.jl +++ b/src/bezier_extraction.jl @@ -65,6 +65,7 @@ function compute_bezier_points!(bezier_points::Vector{T2}, Ce::BezierExtractionO return nothing end +#const bezier_extraction! = compute_bezier_points! """ compute_bezier_points(Ce::BezierExtractionOperator{T}, control_points::AbstractVector{T2}; dim::Int=1) diff --git a/src/nurbsmesh.jl b/src/nurbsmesh.jl index 23d61bb..0820636 100644 --- a/src/nurbsmesh.jl +++ b/src/nurbsmesh.jl @@ -69,7 +69,7 @@ function eval_parametric_coordinate(mesh::NURBSMesh{pdim,sdim}, ξ::Vec{pdim}) w x = zero(Vec{sdim,Float64}) for i in 1:getnbasefunctions(bspline) - N = Ferrite.value(bspline, i, ξ) + N = Ferrite.shape_value(bspline, ξ, i) x += N*mesh.control_points[i] end diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl index e2503bf..cd861ec 100644 --- a/src/splines/bezier.jl +++ b/src/splines/bezier.jl @@ -377,15 +377,15 @@ function _bernstein_ordering(::Bernstein{RefHexahedron, order}) where {order} end #Almost the same orderign as _bernstein_ordering, but some changes for faces and edges -function _vtk_ordering(c::BezierCell{RefLine}) +function _vtk_ordering(c::Type{<:BezierCell{RefLine}}) _bernstein_ordering(c) end -function _vtk_ordering(c::BezierCell{RefQuadrilateral}) +function _vtk_ordering(c::Type{<:BezierCell{RefQuadrilateral}}) _bernstein_ordering(c) end -function _vtk_ordering(::BezierCell{RefHexahedron, order}) where {order} +function _vtk_ordering(::Type{<:BezierCell{RefHexahedron, order}}) where {order} orders = (order, order, order) ind = reshape(1:prod(orders .+ 1), (orders .+ 1)...) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index 73b3711..ef97ba8 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -72,7 +72,7 @@ end BezierCellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) = BezierCellValues(Float64, qr, ip, args...; kwargs...) -function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = default_geometric_interpolation(ip_fun); +function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = Ferrite.default_geometric_interpolation(ip_fun); update_hessians::Bool = false, update_gradients::Bool = true) where {T,sdim} FunDiffOrder = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs @@ -140,7 +140,6 @@ Ferrite.geometric_value(fv::BezierFaceValues, q_point::Int, i::Int) = Ferrite.ge Ferrite.shape_hessian(fv::BezierFaceValues, q_point::Int, i::Int) = shape_hessian(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i) Ferrite.shape_hessian(cv::BezierCellValues, q_point::Int, i::Int) = shape_hessian(cv.nurbs_values, q_point, i) -Ferrite.nfaces(fv::BezierFaceValues) = length(fv.geo_mapping) Ferrite.getcurrentface(fv::BezierFaceValues) = fv.current_face[] function Ferrite.set_current_face!(fv::BezierFaceValues, face_nr::Int) checkbounds(Bool, 1:Ferrite.nfaces(fv), face_nr) || throw(ArgumentError("Face index out of range.")) @@ -323,6 +322,27 @@ function Ferrite.reinit!(cv::BezierCellValues, (x,w)::CoordsAndWeight) return nothing end +function reinit_values!(cv::BezierCellValues, bc::BezierCoords) + set_bezier_operator!(cv, bc.beo[], bc.w) + x, w = (bc.xb, bc.wb) + + n_geom_basefuncs = Ferrite.getngeobasefunctions(cv.geo_mapping) + @assert isa(Ferrite.mapping_type(cv.bezier_values), Ferrite.IdentityMapping) + @assert checkbounds(Bool, x, 1:n_geom_basefuncs) + @assert checkbounds(Bool, w, 1:n_geom_basefuncs) + + for (q_point, gauss_w) in enumerate(Ferrite.getweights(cv.qr)) + #mapping = Ferrite.calculate_mapping(cv.geo_mapping, q_point, x, w) + #detJ = Ferrite.calculate_detJ(Ferrite.getjacobian(mapping)) + #cv.detJdV[q_point] = detJ * gauss_w + _compute_intermidiate!(cv.tmp_values, cv.bezier_values, cv.geo_mapping, q_point, w) + #Ferrite.apply_mapping!(cv.tmp_values, q_point, mapping) + _bezier_transform(cv.nurbs_values, cv.tmp_values, q_point, cv.current_beo[], cv.current_w) + #detJ > 0.0 || Ferrite.throw_detJ_not_pos(detJ) + end + return nothing +end + function Ferrite.reinit!(cv::BezierFaceValues, bc::BezierCoords, face_nr::Int) set_bezier_operator!(cv, bc.beo[], bc.w) return reinit!(cv, (bc.xb, bc.wb), face_nr) @@ -368,26 +388,30 @@ function _bezier_transform(nurbs::FunctionValues{DIFFORDER}, bezier::FunctionVal dBdξ = bezier.dNdξ B = bezier.Nξ - is_scalar_valued = !(Ferrite.shape_value_type(nurbs) <: Tensor) - dim_s = Ferrite.sdim_from_gradtype(Ferrite.shape_gradient_type(nurbs)) + is_scalar_valued = Ferrite.shape_value_type(nurbs) <: AbstractFloat + vdim = length(first(B))#Ferrite.sdim_from_gradtype(Ferrite.shape_gradient_type(nurbs)) for ib in 1:N if is_scalar_valued nurbs.Nξ[ib, iq] = zero(eltype(nurbs.Nξ)) - nurbs.dNdξ[ib, iq] = zero(eltype(nurbs.dNdξ)) - nurbs.dNdx[ib, iq] = zero(eltype(nurbs.dNdx)) - if DIFFORDER == 2 + if DIFFORDER > 0 + nurbs.dNdξ[ib, iq] = zero(eltype(nurbs.dNdξ)) + nurbs.dNdx[ib, iq] = zero(eltype(nurbs.dNdx)) + end + if DIFFORDER > 1 nurbs.d2Ndx2[ib, iq] = zero(eltype(nurbs.d2Ndx2)) nurbs.d2Ndξ2[ib, iq] = zero(eltype(nurbs.d2Ndξ2)) end else - for d in 1:dim_s - nurbs.Nξ[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.Nξ)) - nurbs.dNdξ[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.dNdξ)) - nurbs.dNdx[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.dNdx)) - if DIFFORDER == 2 - nurbs.d2Ndx2[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.d2Ndx2)) - nurbs.d2Ndξ2[(ib-1)*dim_s+d, iq] = zero(eltype(nurbs.d2Ndξ2)) + for d in 1:vdim + nurbs.Nξ[(ib-1)*vdim+d, iq] = zero(eltype(nurbs.Nξ)) + if DIFFORDER > 0 + nurbs.dNdξ[(ib-1)*vdim+d, iq] = zero(eltype(nurbs.dNdξ)) + nurbs.dNdx[(ib-1)*vdim+d, iq] = zero(eltype(nurbs.dNdx)) + end + if DIFFORDER > 1 + nurbs.d2Ndx2[(ib-1)*vdim+d, iq] = zero(eltype(nurbs.d2Ndx2)) + nurbs.d2Ndξ2[(ib-1)*vdim+d, iq] = zero(eltype(nurbs.d2Ndξ2)) end end end @@ -401,20 +425,24 @@ function _bezier_transform(nurbs::FunctionValues{DIFFORDER}, bezier::FunctionVal end if is_scalar_valued nurbs.Nξ[ib, iq] += val* B[nz_ind, iq] - nurbs.dNdξ[ib, iq] += val*dBdξ[nz_ind, iq] - nurbs.dNdx[ib, iq] += val*dBdx[nz_ind, iq] - if DIFFORDER == 2 + if DIFFORDER > 0 + nurbs.dNdξ[ib, iq] += val*dBdξ[nz_ind, iq] + nurbs.dNdx[ib, iq] += val*dBdx[nz_ind, iq] + end + if DIFFORDER > 1 nurbs.d2Ndξ2[ib, iq] += val*d2Bdξ2[nz_ind, iq] nurbs.d2Ndx2[ib, iq] += val*d2Bdx2[nz_ind, iq] end else - for d in 1:dim_s - nurbs.Nξ[(ib-1)*dim_s + d, iq] += val* B[(nz_ind-1)*dim_s + d, iq] - nurbs.dNdξ[(ib-1)*dim_s + d, iq] += val*dBdξ[(nz_ind-1)*dim_s + d, iq] - nurbs.dNdx[(ib-1)*dim_s + d, iq] += val*dBdx[(nz_ind-1)*dim_s + d, iq] - if DIFFORDER == 2 - nurbs.d2Ndξ2[(ib-1)*dim_s + d, iq] += val*d2Bdξ2[(nz_ind-1)*dim_s + d, iq] - nurbs.d2Ndx2[(ib-1)*dim_s + d, iq] += val*d2Bdx2[(nz_ind-1)*dim_s + d, iq] + for d in 1:vdim + nurbs.Nξ[(ib-1)*vdim + d, iq] += val* B[(nz_ind-1)*vdim + d, iq] + if DIFFORDER > 0 + nurbs.dNdξ[(ib-1)*vdim + d, iq] += val*dBdξ[(nz_ind-1)*vdim + d, iq] + nurbs.dNdx[(ib-1)*vdim + d, iq] += val*dBdx[(nz_ind-1)*vdim + d, iq] + end + if DIFFORDER > 1 + nurbs.d2Ndξ2[(ib-1)*vdim + d, iq] += val*d2Bdξ2[(nz_ind-1)*vdim + d, iq] + nurbs.d2Ndx2[(ib-1)*vdim + d, iq] += val*d2Bdx2[(nz_ind-1)*vdim + d, iq] end end end @@ -424,6 +452,17 @@ end Ferrite.otimes_helper(x::Number, dMdξ::Vec{dim}) where dim = x * dMdξ + +function _compute_intermidiate!(tmp_values::FunctionValues{0}, bezier_values::FunctionValues{0}, geom_values::GeometryMapping, q_point::Int, w::Vector{T}) where {T} + W = zero(T) + for j in 1:Ferrite.getngeobasefunctions(geom_values) + W += w[j]*geom_values.M[j, q_point] + end + for j in 1:getnbasefunctions(tmp_values) + tmp_values.Nξ[j,q_point] = bezier_values.Nξ[j, q_point]/W + end +end + function _compute_intermidiate!(tmp_values::FunctionValues{DIFFORDER}, bezier_values::FunctionValues{DIFFORDER}, geom_values::GeometryMapping, q_point::Int, w::Vector{T}) where {T,DIFFORDER} dim = Ferrite.sdim_from_gradtype(Ferrite.shape_gradient_type(tmp_values)) @assert DIFFORDER < 3 "Diff order > 2 not supported" @@ -433,16 +472,20 @@ function _compute_intermidiate!(tmp_values::FunctionValues{DIFFORDER}, bezier_va d2Wdξ2 = zero(Tensor{2,dim,T}) for j in 1:Ferrite.getngeobasefunctions(geom_values) W += w[j]*geom_values.M[j, q_point] - dWdξ += w[j]*geom_values.dMdξ[j, q_point] - if DIFFORDER == 2 + if DIFFORDER > 0 + dWdξ += w[j]*geom_values.dMdξ[j, q_point] + end + if DIFFORDER > 1 d2Wdξ2 += w[j]*geom_values.d2Mdξ2[j, q_point] end end for j in 1:getnbasefunctions(tmp_values) - tmp_values.dNdξ[j, q_point] = ( bezier_values.dNdξ[j, q_point]*W - Ferrite.otimes_helper(bezier_values.Nξ[j, q_point], dWdξ) ) / W^2 tmp_values.Nξ[j,q_point] = bezier_values.Nξ[j, q_point]/W + if DIFFORDER > 0 + tmp_values.dNdξ[j, q_point] = ( bezier_values.dNdξ[j, q_point]*W - Ferrite.otimes_helper(bezier_values.Nξ[j, q_point], dWdξ) ) / W^2 + end - if DIFFORDER == 2 + if DIFFORDER > 1 is_vector_valued = (first(tmp_values.Nξ) isa Vec) if is_vector_valued _B = bezier_values.Nξ[j, q_point] @@ -483,7 +526,6 @@ end=# function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{1}, q_point, x::Vector{Vec{dim,T}}, w::Vector{T}) where {dim,T} W = zero(T) dWdξ = zero(Vec{dim,T}) - #d²Wdξ² = zero(Tensor{2,dim,T}) for j in 1:Ferrite.getngeobasefunctions(geo_mapping) W += w[j]*geo_mapping.M[j, q_point] dWdξ += w[j]*geo_mapping.dMdξ[j, q_point] diff --git a/test/test_bertstein.jl b/test/test_bertstein.jl index 8581dc5..df5b9db 100644 --- a/test/test_bertstein.jl +++ b/test/test_bertstein.jl @@ -4,7 +4,7 @@ #1d basis function should equal to 2d basis function on the boundary (-1.0) b1 = Bernstein{RefQuadrilateral, 2}() b2 = Bernstein{RefLine, 2}() - @test Ferrite.value(b1, 5, Vec((0.0,-1.0))) == Ferrite.value(b2, 3, Vec((0.0))) + @test Ferrite.shape_value(b1, Vec((0.0,-1.0)), 5) == Ferrite.shape_value(b2, Vec((0.0)), 3) #Sum of shape function should equal 1 for bip in (Bernstein{RefLine, 1}(), diff --git a/test/test_bezier_extraction.jl b/test/test_bezier_extraction.jl index 62bd7e2..9d0badd 100644 --- a/test/test_bezier_extraction.jl +++ b/test/test_bezier_extraction.jl @@ -15,10 +15,10 @@ @test all(C[4] .≈ Float64[1/6 0 0 0; 7/12 1/2 0 0; 1/4 1/2 1 0; 0 0 0 1][ordering,ordering]) #Test multivariate extraction operator - orders = (2,2) + order = 2 knots = (Float64[0,0,0,1/3,2/3,1,1,1], Float64[0,0,0,1/3,2/3,1,1,1]) - C,nbe = IGA.compute_bezier_extraction_operators(orders, knots) - ordering = IGA._bernstein_ordering(Bernstein{RefQuadrilateral, orders}()) + C,nbe = IGA.compute_bezier_extraction_operators((order,order), knots) + ordering = IGA._bernstein_ordering(Bernstein{RefQuadrilateral, order}()) @test nbe == 9 @test length(C) == 9 @test all(C[1] .≈ kron( [1 0 0; 0 1 1/2; 0 0 1/2], [1 0 0; 0 1 1/2; 0 0 1/2])[ordering,ordering]) diff --git a/test/test_meshes.jl b/test/test_meshes.jl index 7e3bc12..9df0797 100644 --- a/test/test_meshes.jl +++ b/test/test_meshes.jl @@ -231,11 +231,11 @@ end test_cylinder_sector_3d() end -@testset "test grid_generator" +@testset "test grid_generator" begin #Check that it is possible to generate gird with ferrite-api: #TODO: What to test? generate_grid(BezierCell{RefQuadrilateral, 2}, (2,2)) - generate_grid(BezierCell{RefQuadrilateral, 2}, (2,2), left = Vec((0.0,0.0)), right = Vec((1.0,1.0))) + generate_grid(BezierCell{RefQuadrilateral, 2}, (2,2), Vec((0.0,0.0)), Vec((1.0,1.0))) generate_grid(BezierCell{RefHexahedron, 2}, (2,2,2)) - generate_grid(BezierCell{RefHexahedron, 2}, (2,2,2), left = Vec((0.0,0.0)), right = Vec((1.0,1.0))) + generate_grid(BezierCell{RefHexahedron, 2}, (2,2,2), Vec((0.0,0.0,0.0)), Vec((1.0,1.0,1.0))) end \ No newline at end of file diff --git a/test/test_nurbsmesh.jl b/test/test_nurbsmesh.jl index 6784a09..4e44789 100644 --- a/test/test_nurbsmesh.jl +++ b/test/test_nurbsmesh.jl @@ -2,7 +2,7 @@ @testset "test nurbsmesh" begin - mesh = generate_nurbs_patch(:plate_with_hole, (4,4)) + mesh = generate_nurbs_patch(:plate_with_hole, (4,4), (2,2)) x = eval_parametric_coordinate(mesh, Vec((-1.0,-1.0))) @test x == Vec((-1.0, 0.0)) diff --git a/test/test_values.jl b/test/test_values.jl index 4bf71eb..9f6da93 100644 --- a/test/test_values.jl +++ b/test/test_values.jl @@ -60,21 +60,13 @@ end qr_face = FaceQuadratureRule{shape}(1) cv = CellValues( qr, ip, ip) - cv2 = BezierCellValues( CellValues(qr, bip, bip) ) + #cv2 = BezierCellValues( CellValues(qr, bip, bip) ) cv3 = CellValues( qr, ip, ip) - @test cv.cv_bezier.M == cv2.cv_bezier.M - @test cv.cv_bezier.M == cv3.cv_bezier.M - @test cv3 isa BezierCellValues - cv_vector1 = CellValues( qr, ip^sdim, ip ) - cv_vector2 = BezierCellValues( CellValues(qr, bip^sdim, bip) ) + #cv_vector2 = BezierCellValues( CellValues(qr, bip^sdim, bip) ) cv_vector3 = CellValues( qr, ip^sdim, ip ) - @test cv_vector1.cv_bezier.M == cv_vector2.cv_bezier.M - @test cv_vector1.cv_bezier.M == cv_vector3.cv_bezier.M - @test cv_vector3 isa BezierCellValues - @test Ferrite.getngeobasefunctions(cv_vector1) == getnbasefunctions(ip) @test Ferrite.getngeobasefunctions(cv) == getnbasefunctions(ip) @@ -93,6 +85,8 @@ end # Build the problem ## nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels, (2,2)) + #nurbsmesh.knot_vectors[1][4] = -0.5 + #nurbsmesh.knot_vectors[2][4] = -0.5 #nurbsmesh = generate_nurbs_patch(:rectangle, nels, (order,order), size = (20.0,20.0)) #nurbsmesh.weights .= 1.0 @@ -152,10 +146,10 @@ end for i in 1:getnbasefunctions(cv) @test shape_value(cv, iqp,i) ≈ R[i] @test shape_gradient(cv, iqp, i) ≈ dRdX[i] - @test shape_hessian(cv, iqp, i) ≈ d²RdX²[i] atol=1e-12 + @test shape_hessian(cv, iqp, i) ≈ d²RdX²[i] # atol=1e-12 - @test cv.nurbs_values.dNdξ[ i,iqp] ≈ dRdξ[i] atol=1e-12 - @test cv.nurbs_values.d2Ndξ2[i,iqp] ≈ d²Rdξ²[i] atol=1e-12 + @test cv.nurbs_values.dNdξ[ i,iqp] ≈ dRdξ[i] # atol=1e-12 + @test cv.nurbs_values.d2Ndξ2[i,iqp] ≈ d²Rdξ²[i] # atol=1e-12 end #Check if VectorValues is same as ScalarValues @@ -167,18 +161,17 @@ end N_comp = zeros(Float64, dim) N_comp[comp] = shape_value(cv, iqp, i) _N = Vec{dim,Float64}((N_comp...,)) - - @test shape_value(cv_vector,iqp,basefunc_count) ≈ _N atol=1e-15 - - #dN_comp = zeros(Float64, dim, dim) - #dN_comp[comp, :] = cv.cv_nurbs.dNdξ[i, iqp] - #_dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) - #@test cv_vector.cv_nurbs.dNdξ[basefunc_count, iqp] ≈ _dNdξ atol = 1e-15 + @test shape_value(cv_vector,iqp,basefunc_count) ≈ _N #atol=1e-15 dN_comp = zeros(Float64, dim, dim) dN_comp[comp, :] = shape_gradient(cv,iqp,i) _dNdx = Tensor{2,dim,Float64}((dN_comp...,)) - @test shape_gradient(cv_vector,iqp,basefunc_count) ≈ _dNdx atol = 1e-15 + @test shape_gradient(cv_vector,iqp,basefunc_count) ≈ _dNdx #atol = 1e-15 + + ddN_comp = zeros(Float64, dim, dim, dim) + ddN_comp[comp, :, :] = shape_hessian(cv,iqp,i) + _d2Ndx2 = Tensor{3,dim,Float64}((ddN_comp...,)) + @test shape_hessian(cv_vector,iqp,basefunc_count) ≈ _d2Ndx2 #atol = 1e-15 basefunc_count += 1 end @@ -186,6 +179,7 @@ end end end + cellnum, faceidx = (9, 3) addfaceset!(grid, "face1", (x)-> x[1] == -4.0) for (cellnum, faceidx) in getfaceset(grid, "face1") @@ -225,30 +219,32 @@ end for i in 1:getnbasefunctions(fv) @test shape_value(fv, iqp,i) ≈ R[i] @test shape_gradient(fv, iqp, i) ≈ dRdX[i] - @test shape_hessian(fv, iqp, i) ≈ d²RdX²[i] atol=1e-11 + @test shape_hessian(fv, iqp, i) ≈ d²RdX²[i] #atol=1e-11 - @test fv.nurbs_values[faceidx].dNdξ[ i,iqp] ≈ dRdξ[i] atol=1e-11 - @test fv.nurbs_values[faceidx].d2Ndξ2[i,iqp] ≈ d²Rdξ²[i] atol=1e-11 + @test fv.nurbs_values[faceidx].dNdξ[ i,iqp] ≈ dRdξ[i] #atol=1e-11 + @test fv.nurbs_values[faceidx].d2Ndξ2[i,iqp] ≈ d²Rdξ²[i] #atol=1e-11 end #Check if VectorValues is same as ScalarValues + i = 1 basefunc_count = 1 + comp = 1 for i in 1:nb_per_cell for comp in 1:dim N_comp = zeros(Float64, dim) N_comp[comp] = shape_value(fv,iqp,i) _N = Vec{dim,Float64}((N_comp...,)) - @test shape_value(fv_vector,iqp,basefunc_count) ≈ _N atol = 1e-15 - - #dN_comp = zeros(Float64, dim, dim) - #dN_comp[comp, :] = fv.cv_nurbs.dNdξ[i, iqp, faceidx] - #_dNdξ = Tensor{2,dim,Float64}((dN_comp...,)) - #@test fv_vector.cv_nurbs.dNdξ[basefunc_count, iqp, faceidx] ≈ _dNdξ + @test shape_value(fv_vector,iqp,basefunc_count) ≈ _N #atol = 1e-15 dN_comp = zeros(Float64, dim, dim) dN_comp[comp, :] = shape_gradient(fv,iqp,i) _dNdx = Tensor{2,dim,Float64}((dN_comp...,)) - @test shape_gradient(fv_vector,iqp,basefunc_count) ≈ _dNdx atol = 1e-15 + @test shape_gradient(fv_vector,iqp,basefunc_count) ≈ _dNdx #atol = 1e-15 + + ddN_comp = zeros(Float64, dim, dim, dim) + ddN_comp[comp, :, :] = shape_hessian(fv,iqp,i) + _d2Ndx2 = Tensor{3,dim,Float64}((ddN_comp...,)) + @test shape_hessian(fv_vector,iqp,basefunc_count) ≈ _d2Ndx2 #atol = 1e-15 basefunc_count += 1 end From 6e2e05fe92307a10ac84345d4a5c0c8af243b8cd Mon Sep 17 00:00:00 2001 From: lijas Date: Tue, 21 May 2024 16:27:48 +0200 Subject: [PATCH 29/50] relax constraint that knots should be within -1 and 1 for BSplines --- src/splines/bsplines.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/splines/bsplines.jl b/src/splines/bsplines.jl index b24ab0a..aa0c0ef 100644 --- a/src/splines/bsplines.jl +++ b/src/splines/bsplines.jl @@ -10,8 +10,7 @@ struct BSplineBasis{dim,T,order} <: Ferrite.ScalarInterpolation{RefHypercube{dim function BSplineBasis(knots::NTuple{dim,Vector{T}}, order::NTuple{dim,Int}) where {dim,T} @assert(length(order)==dim) - @assert( all( first.(knots) .== T(-1.0)) ) - @assert( all( last.(knots) .== T(1.0)) ) + @assert all( issorted.(knots) ) return new{dim,T,Tuple(order)}(knots) end From cc2c116bfee79cd028d529d1d4f0a83b9547e6f6 Mon Sep 17 00:00:00 2001 From: lijas Date: Tue, 21 May 2024 16:28:06 +0200 Subject: [PATCH 30/50] Fix output of projected data --- src/VTK.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VTK.jl b/src/VTK.jl index 2886f8f..de66f85 100644 --- a/src/VTK.jl +++ b/src/VTK.jl @@ -141,7 +141,7 @@ end function #=Ferrite.=#write_projected(vtk::VTKIGAFile, proj::L2Projector, vals, name) data = Ferrite._evaluate_at_grid_nodes(proj, vals, #=vtk=# Val(true))::Matrix @assert size(data, 2) == getnnodes(Ferrite.get_grid(proj.dh)) - vtk_node_data(vtk.vtk, data, name; component_names=Ferrite.component_names(eltype(vals))) + vtk_point_data(vtk.vtk, data, name; component_names=Ferrite.component_names(eltype(vals))) return vtk end From 5d6caad63dd474e7ad0bd1fc25c8013f779d5571 Mon Sep 17 00:00:00 2001 From: lijas Date: Tue, 21 May 2024 16:28:56 +0200 Subject: [PATCH 31/50] Fix mapping for shells --- src/splines/bezier_values.jl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl index ef97ba8..c26862d 100644 --- a/src/splines/bezier_values.jl +++ b/src/splines/bezier_values.jl @@ -523,23 +523,30 @@ end end end=# -function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{1}, q_point, x::Vector{Vec{dim,T}}, w::Vector{T}) where {dim,T} +@inline _getrdim(geomapping::Ferrite.GeometryMapping) = length(first(geomapping.dMdξ)) +function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{1}, q_point, x::Vector{Vec{sdim,T}}, w::Vector{T}) where {sdim,T} + rdim = _getrdim(geo_mapping) + W = zero(T) - dWdξ = zero(Vec{dim,T}) + dWdξ = zero(Vec{rdim,T}) for j in 1:Ferrite.getngeobasefunctions(geo_mapping) W += w[j]*geo_mapping.M[j, q_point] dWdξ += w[j]*geo_mapping.dMdξ[j, q_point] end - fecv_J = zero(Tensor{2,dim,T}) + fecv_J = Ferrite.otimes_helper(first(x), first(geo_mapping.dMdξ)) |> typeof |> zero for j in 1:Ferrite.getngeobasefunctions(geo_mapping) dRdξ = (geo_mapping.dMdξ[j, q_point]*W - geo_mapping.M[j, q_point]*dWdξ)/W^2 - fecv_J += x[j] ⊗ (w[j]*dRdξ) + #fecv_J += x[j] ⊗ (w[j]*dRdξ) + fecv_J += Ferrite.otimes_helper(x[j], w[j]*dRdξ) end return Ferrite.MappingValues(fecv_J, nothing) end -function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{2}, q_point, x::Vector{Vec{dim,T}}, w::Vector{T}) where {dim,T} +function Ferrite.calculate_mapping(geo_mapping::Ferrite.GeometryMapping{2}, q_point, x::Vector{Vec{sdim,T}}, w::Vector{T}) where {sdim,T} + dim = rdim = _getrdim(geo_mapping) + @assert rdim == sdim + W = zero(T) dWdξ = zero(Vec{dim,T}) d²Wdξ² = zero(Tensor{2,dim,T}) From 8ec24249a1e030988b0a5734aaa312473fbf717c Mon Sep 17 00:00:00 2001 From: lijas Date: Fri, 24 May 2024 09:10:08 +0200 Subject: [PATCH 32/50] start work on update to ferrite master --- Project.toml | 1 + docs/build/examples/example1.jl | 6 +- docs/build/examples/example1/index.html | 6 +- docs/build/literate/example1.jl | 6 +- docs/build/search_index.js | 2 +- docs/old_examples/curved_ex.jl | 14 ++--- docs/old_examples/dawn_gen.jl | 2 +- docs/old_examples/heat_example.jl | 8 +-- docs/old_examples/heat_example2.jl | 10 ++-- docs/old_examples/quarter_hole.jl | 6 +- docs/src/examples/example1.jl | 6 +- docs/src/examples/example1.md | 6 +- docs/src/literate/plate_with_hole.jl | 10 ++-- src/IGA.jl | 3 + src/bezier_grid.jl | 24 ++++---- src/mesh_generation.jl | 20 +++---- src/splines/bezier.jl | 4 +- src/splines/bezier_values.jl | 78 ++++++++++++------------- test/test_meshes.jl | 40 ++++++------- test/test_values.jl | 10 ++-- 20 files changed, 131 insertions(+), 131 deletions(-) diff --git a/Project.toml b/Project.toml index d7ee3e3..5e2bed4 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.2.6" Ferrite = "c061ca5d-56c9-439f-9c0e-210fe06d3992" InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" diff --git a/docs/build/examples/example1.jl b/docs/build/examples/example1.jl index 5eab419..0b4fa8b 100644 --- a/docs/build/examples/example1.jl +++ b/docs/build/examples/example1.jl @@ -129,9 +129,9 @@ function solve() grid = BezierGrid(nurbsmesh) addnodeset!(grid,"right", (x) -> x[1] ≈ -0.0) - addfaceset!(grid, "left", (x) -> x[1] ≈ -4.0) - addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0) - addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0) + addfacetset!(grid, "left", (x) -> x[1] ≈ -4.0) + addfacetset!(grid, "bot", (x) -> x[2] ≈ 0.0) + addfacetset!(grid, "right", (x) -> x[1] ≈ 0.0) ip = BernsteinBasis{2,orders}() qr_cell = QuadratureRule{2,RefCube}(3) diff --git a/docs/build/examples/example1/index.html b/docs/build/examples/example1/index.html index c50fac8..dcb4f7f 100644 --- a/docs/build/examples/example1/index.html +++ b/docs/build/examples/example1/index.html @@ -113,9 +113,9 @@ orders = (2,2) # Order in the ξ and η directions . nels = (10,10) # Number of elements nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels)

Performing the computation on a NURBS-patch is possible, but it is much easier to use the bezier-extraction technique. For this we transform the NURBS-patch into a BezierGrid. The BezierGrid is identical to the standard JuAFEM.Grid, but includes the NURBS-weights and bezier extraction operators.

    grid = BezierGrid(nurbsmesh)

Next, create some facesets. This is done in the same way as in normal JuAFEM-code. One thing to note however, is that the nodes/controlpoints, does not necessary lay exactly on the geometry due to the non-interlapotry nature of NURBS spline functions. However, in most cases they will be close enough to use the JuAFEM functions below.

    addnodeset!(grid,"right", (x) -> x[1] ≈ -0.0)
-    addfaceset!(grid, "left", (x) -> x[1] ≈ -4.0)
-    addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0)
-    addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0)

Create the cellvalues storing the shape function values. Note that the CellVectorValues/FaceVectorValues are wrapped in a BezierValues. It is in the reinit-function of the BezierValues that the actual bezier transformation of the shape values is performed.

    ip = BernsteinBasis{2,orders}()
+    addfacetset!(grid, "left", (x) -> x[1] ≈ -4.0)
+    addfacetset!(grid, "bot", (x) -> x[2] ≈ 0.0)
+    addfacetset!(grid, "right", (x) -> x[1] ≈ 0.0)

Create the cellvalues storing the shape function values. Note that the CellVectorValues/FaceVectorValues are wrapped in a BezierValues. It is in the reinit-function of the BezierValues that the actual bezier transformation of the shape values is performed.

    ip = BernsteinBasis{2,orders}()
     qr_cell = QuadratureRule{2,RefCube}(3)
     qr_face = QuadratureRule{1,RefCube}(3)
 
diff --git a/docs/build/literate/example1.jl b/docs/build/literate/example1.jl
index e050faf..7b5f8ff 100644
--- a/docs/build/literate/example1.jl
+++ b/docs/build/literate/example1.jl
@@ -167,9 +167,9 @@ function solve()
     # does not necessary lay exactly on the geometry due to the non-interlapotry nature of NURBS spline functions. However, in most cases they will be close enough to 
     # use the JuAFEM functions below.
     addnodeset!(grid,"right", (x) -> x[1] ≈ -0.0)
-    addfaceset!(grid, "left", (x) -> x[1] ≈ -4.0)
-    addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0)
-    addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0)
+    addfacetset!(grid, "left", (x) -> x[1] ≈ -4.0)
+    addfacetset!(grid, "bot", (x) -> x[2] ≈ 0.0)
+    addfacetset!(grid, "right", (x) -> x[1] ≈ 0.0)
 
     # Create the cellvalues storing the shape function values. Note that the `CellVectorValues`/`FaceVectorValues` are wrapped in a `BezierValues`. It is in the 
     # reinit-function of the `BezierValues` that the actual bezier transformation of the shape values is performed. 
diff --git a/docs/build/search_index.js b/docs/build/search_index.js
index c03ce59..b420b21 100644
--- a/docs/build/search_index.js
+++ b/docs/build/search_index.js
@@ -1,3 +1,3 @@
 var documenterSearchIndex = {"docs":
-[{"location":"bezier_values/#BezierValues.jl","page":"BezierValues.jl","title":"BezierValues.jl","text":"","category":"section"},{"location":"bezier_values/","page":"BezierValues.jl","title":"BezierValues.jl","text":"Documentation for IGA.jl","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"EditURL = \"/docs/src/literate/example1.jl\"","category":"page"},{"location":"examples/example1/#Infinite-plate-with-hole","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"","category":"section"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"(Image: )","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"In this example we will solve a simple elasticity problem; an infinite plate with a hole. The main goal of the tutorial is to show how one can solve the problem using Isogeometric Analysis (IGA), or in other words, solving a FE-problem with splines as the basis/shape functions. By using so called bezier extraction, we will see that most of the structure of the code will be the same as in standard FE-codes (however many differences are happening \"under the hood\").","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"note: Note\nIt is expected that the reader already be familiar with IGA and the concept of \"bezier extraction\". It is also expected that the reader is familiar with the JuAFEM package. In particular JuAFEM.DofHandler and JuAFEM.CellValues.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Start by loading the necessary packages","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"using JuAFEM, IGA, LinearAlgebra","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Next we define the functions for the integration of the element stiffness matrix and traction force. These functions will be the same as for a normal finite elment problem, but with the difference that we need the cell coorinates AND cell weights (the weights from the NURBS shape functions), to reinitilize the shape values, dNdx. Read this page, to see how the shape values are reinitilized.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function integrate_element!(ke::AbstractMatrix, Xᴮ::Vector{Vec{2,Float64}}, w::Vector{Float64}, C::SymmetricTensor{4,2}, cv)\n    n_basefuncs = getnbasefunctions(cv)\n\n    reinit!(cv, (Xᴮ, w)) ## Reinit cellvalues by passsing both bezier coords and weights\n\n    δɛ = [zero(SymmetricTensor{2,2,Float64}) for i in 1:n_basefuncs]\n\n    for q_point in 1:getnquadpoints(cv)\n\n        for i in 1:n_basefuncs\n            δɛ[i] = symmetric(shape_gradient(cv, q_point, i))\n        end\n\n        dΩ = getdetJdV(cv, q_point)\n        for i in 1:n_basefuncs\n            for j in 1:n_basefuncs\n                ke[i, j] += (δɛ[i] ⊡ C ⊡ δɛ[j]) * dΩ\n            end\n        end\n    end\nend;\n\nfunction integrate_traction_force!(fe::AbstractVector, Xᴮ::Vector{Vec{2,Float64}}, w::Vector{Float64}, t::Vec{2}, fv, faceid::Int)\n    n_basefuncs = getnbasefunctions(fv)\n\n    reinit!(fv, Xᴮ, w, faceid) ## Reinit cellvalues by passsing both bezier coords and weights\n\n    for q_point in 1:getnquadpoints(fv)\n        dA = getdetJdV(fv, q_point)\n        for i in 1:n_basefuncs\n            δu = shape_value(fv, q_point, i)\n            fe[i] += t ⋅ δu * dA\n        end\n    end\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"The assembly loop is also written in almost the same way as in a standard finite element code. The key differences will be described in the next paragraph,","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function assemble_problem(dh::MixedDofHandler, grid, cv, fv, stiffmat, traction)\n\n    f = zeros(ndofs(dh))\n    K = create_sparsity_pattern(dh)\n    assembler = start_assemble(K, f)\n\n    n = getnbasefunctions(cv)\n    celldofs = zeros(Int, n)\n    fe = zeros(n)     # element force vector\n    ke = zeros(n, n)  # element stiffness matrix\n\n    # Assemble internal forces\n    for cellid in 1:getncells(grid)\n        fill!(fe, 0.0)\n        fill!(ke, 0.0)\n        celldofs!(celldofs, dh, cellid)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"In a normal finite elment code, this is the point where we usually get the coordinates of the element X = getcoordinates(grid, cellid). In this case, however, we also require the cell weights, and we need to transform them to the bezier mesh.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"        extr = grid.beo[cellid] # Extraction operator\n        X = getcoordinates(grid.grid, cellid) #Nurbs coords\n        w = IGA.getweights(grid, cellid)       #Nurbs weights\n        wᴮ = compute_bezier_points(extr, w)\n        Xᴮ = inv.(wᴮ) .* compute_bezier_points(extr, w.*X)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"tip: Tip\nSince the operations above are quite common in IGA, there is a helper-function called get_bezier_coordinates(grid, cellid), which return the bezier coorinates and weights directly.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Furthermore, we pass the bezier extraction operator to the CellValues/Beziervalues.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"        set_bezier_operator!(cv, extr)\n\n        integrate_element!(ke, Xᴮ, w, stiffmat, cv)\n        assemble!(assembler, celldofs, ke, fe)\n    end\n\n    # Assamble external forces\n    for (cellid, faceid) in getfaceset(grid, \"left\")\n        fill!(fe, 0.0)\n        fill!(ke, 0.0)\n\n        celldofs!(celldofs, dh, cellid)\n\n        extr = grid.beo[cellid]\n        Xᴮ, w = get_bezier_coordinates(grid, cellid)\n        set_bezier_operator!(fv, extr)\n\n        integrate_traction_force!(fe, Xᴮ, w, traction, fv, faceid)\n        assemble!(assembler, celldofs, ke, fe)\n    end\n\n    return K, f\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"This is a function that returns the elastic stiffness matrix","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function get_material(; E, ν)\n    λ = E*ν / ((1 + ν) * (1 - 2ν))\n    μ = E / (2(1 + ν))\n    δ(i,j) = i == j ? 1.0 : 0.0\n    g(i,j,k,l) = λ*δ(i,j)*δ(k,l) + μ*(δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k))\n\n    return SymmetricTensor{4, 2}(g)\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"We also create a function that calculates the stress in each quadrature point, given the cell displacement and such...","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function calculate_stress(dh, cv::JuAFEM.Values, C::SymmetricTensor{4,2}, u::Vector{Float64})\n\n    celldofs = zeros(Int, ndofs_per_cell(dh))\n\n    #Store the stresses in each qp for all cells\n    cellstresses = Vector{SymmetricTensor{2,2,Float64,3}}[]\n\n    for cellid in 1:getncells(dh.grid)\n\n        extr = dh.grid.beo[cellid]\n        Xᴮ, w = get_bezier_coordinates(dh.grid, cellid)\n\n        set_bezier_operator!(cv, extr)\n        reinit!(cv, (Xᴮ, w))\n        celldofs!(celldofs, dh, cellid)\n\n        ue = u[celldofs]\n        qp_stresses = SymmetricTensor{2,2,Float64,3}[]\n        for qp in 1:getnquadpoints(cv)\n            ɛ = symmetric(function_gradient(cv, qp, ue))\n            σ = C ⊡ ε\n            push!(qp_stresses, σ)\n        end\n        push!(cellstresses, qp_stresses)\n    end\n\n    return cellstresses\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Now we have all the parts needed to solve the problem. We begin by generating the mesh. IGA.jl includes a couple of different functions that can generate different nurbs patches. In this example, we will generate the patch called \"plate with hole\". Note, currently this function can only generate the patch with second order basefunctions.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function solve()\n    orders = (2,2) # Order in the ξ and η directions .\n    nels = (10,10) # Number of elements\n    nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Performing the computation on a NURBS-patch is possible, but it is much easier to use the bezier-extraction technique. For this we transform the NURBS-patch into a BezierGrid. The BezierGrid is identical to the standard JuAFEM.Grid, but includes the NURBS-weights and bezier extraction operators.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    grid = BezierGrid(nurbsmesh)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Next, create some facesets. This is done in the same way as in normal JuAFEM-code. One thing to note however, is that the nodes/controlpoints, does not necessary lay exactly on the geometry due to the non-interlapotry nature of NURBS spline functions. However, in most cases they will be close enough to use the JuAFEM functions below.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    addnodeset!(grid,\"right\", (x) -> x[1] ≈ -0.0)\n    addfaceset!(grid, \"left\", (x) -> x[1] ≈ -4.0)\n    addfaceset!(grid, \"bot\", (x) -> x[2] ≈ 0.0)\n    addfaceset!(grid, \"right\", (x) -> x[1] ≈ 0.0)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Create the cellvalues storing the shape function values. Note that the CellVectorValues/FaceVectorValues are wrapped in a BezierValues. It is in the reinit-function of the BezierValues that the actual bezier transformation of the shape values is performed.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    ip = BernsteinBasis{2,orders}()\n    qr_cell = QuadratureRule{2,RefCube}(3)\n    qr_face = QuadratureRule{1,RefCube}(3)\n\n    cv = BezierValues( CellVectorValues(qr_cell, ip) )\n    fv = BezierValues( FaceVectorValues(qr_face, ip) )","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Distribute dofs as normal","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    dh = MixedDofHandler(grid)\n    push!(dh, :u, 2, ip)\n    close!(dh)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Add two symmetry boundary condintions. Bottom face should only be able to move in x-direction, and the right boundary should only be able to move in y-direction","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    ch = ConstraintHandler(dh)\n    dbc1 = Dirichlet(:u, getfaceset(grid, \"bot\"), (x, t) -> 0.0, 2)\n    dbc2 = Dirichlet(:u, getfaceset(grid, \"right\"), (x, t) -> 0.0, 1)\n    add!(ch, dbc1)\n    add!(ch, dbc2)\n    close!(ch)\n    update!(ch, 0.0)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Define stiffness matrix and traction force","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    stiffmat = get_material(E = 100, ν = 0.3)\n    traction = Vec((-10.0, 0.0))\n    K,f = assemble_problem(dh, grid, cv, fv, stiffmat, traction)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Solve","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    apply!(K, f, ch)\n    u = K\\f","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Now we want to export the results to VTK. So we calculate the stresses in each gauss-point, and project them to the nodes using the L2Projector from JuAFEM. Node that we need to create new CellValues of type CellScalarValues, since the L2Projector only works with scalar fields.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    cellstresses = calculate_stress(dh, cv, stiffmat, u)\n\n    csv = BezierValues( CellScalarValues(qr_cell, ip) )\n    projector = L2Projector(csv, ip, grid)\n    σ_nodes = project(cellstresses, projector)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Output results to VTK","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    vtkgrid = vtk_grid(\"plate_with_hole.vtu\", grid)\n    vtk_point_data(vtkgrid, dh, u, :u)\n    vtk_point_data(vtkgrid, σ_nodes, \"sigma\", grid)\n    vtk_save(vtkgrid)\n\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Call the function","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"solve()","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"This page was generated using Literate.jl.","category":"page"},{"location":"#IGA.jl","page":"Home","title":"IGA.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Documentation for IGA.jl","category":"page"},{"location":"","page":"Home","title":"Home","text":"BezierCell","category":"page"},{"location":"#IGA.BezierCell","page":"Home","title":"IGA.BezierCell","text":"BezierCell{dim,N,order,M} <: JuAFEM.AbstractCell{dim,N,M}\n\ndim = spacial dimension N = number of nodes/controlpoints order = tuple with order in each parametric dimension (does not need to be equal to dim) M = number of faces (used by JuAFEM) (4 in 2d, 6 in 3d)\n\n\n\n\n\n","category":"type"}]
+[{"location":"bezier_values/#BezierValues.jl","page":"BezierValues.jl","title":"BezierValues.jl","text":"","category":"section"},{"location":"bezier_values/","page":"BezierValues.jl","title":"BezierValues.jl","text":"Documentation for IGA.jl","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"EditURL = \"/docs/src/literate/example1.jl\"","category":"page"},{"location":"examples/example1/#Infinite-plate-with-hole","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"","category":"section"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"(Image: )","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"In this example we will solve a simple elasticity problem; an infinite plate with a hole. The main goal of the tutorial is to show how one can solve the problem using Isogeometric Analysis (IGA), or in other words, solving a FE-problem with splines as the basis/shape functions. By using so called bezier extraction, we will see that most of the structure of the code will be the same as in standard FE-codes (however many differences are happening \"under the hood\").","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"note: Note\nIt is expected that the reader already be familiar with IGA and the concept of \"bezier extraction\". It is also expected that the reader is familiar with the JuAFEM package. In particular JuAFEM.DofHandler and JuAFEM.CellValues.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Start by loading the necessary packages","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"using JuAFEM, IGA, LinearAlgebra","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Next we define the functions for the integration of the element stiffness matrix and traction force. These functions will be the same as for a normal finite elment problem, but with the difference that we need the cell coorinates AND cell weights (the weights from the NURBS shape functions), to reinitilize the shape values, dNdx. Read this page, to see how the shape values are reinitilized.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function integrate_element!(ke::AbstractMatrix, Xᴮ::Vector{Vec{2,Float64}}, w::Vector{Float64}, C::SymmetricTensor{4,2}, cv)\n    n_basefuncs = getnbasefunctions(cv)\n\n    reinit!(cv, (Xᴮ, w)) ## Reinit cellvalues by passsing both bezier coords and weights\n\n    δɛ = [zero(SymmetricTensor{2,2,Float64}) for i in 1:n_basefuncs]\n\n    for q_point in 1:getnquadpoints(cv)\n\n        for i in 1:n_basefuncs\n            δɛ[i] = symmetric(shape_gradient(cv, q_point, i))\n        end\n\n        dΩ = getdetJdV(cv, q_point)\n        for i in 1:n_basefuncs\n            for j in 1:n_basefuncs\n                ke[i, j] += (δɛ[i] ⊡ C ⊡ δɛ[j]) * dΩ\n            end\n        end\n    end\nend;\n\nfunction integrate_traction_force!(fe::AbstractVector, Xᴮ::Vector{Vec{2,Float64}}, w::Vector{Float64}, t::Vec{2}, fv, faceid::Int)\n    n_basefuncs = getnbasefunctions(fv)\n\n    reinit!(fv, Xᴮ, w, faceid) ## Reinit cellvalues by passsing both bezier coords and weights\n\n    for q_point in 1:getnquadpoints(fv)\n        dA = getdetJdV(fv, q_point)\n        for i in 1:n_basefuncs\n            δu = shape_value(fv, q_point, i)\n            fe[i] += t ⋅ δu * dA\n        end\n    end\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"The assembly loop is also written in almost the same way as in a standard finite element code. The key differences will be described in the next paragraph,","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function assemble_problem(dh::MixedDofHandler, grid, cv, fv, stiffmat, traction)\n\n    f = zeros(ndofs(dh))\n    K = create_sparsity_pattern(dh)\n    assembler = start_assemble(K, f)\n\n    n = getnbasefunctions(cv)\n    celldofs = zeros(Int, n)\n    fe = zeros(n)     # element force vector\n    ke = zeros(n, n)  # element stiffness matrix\n\n    # Assemble internal forces\n    for cellid in 1:getncells(grid)\n        fill!(fe, 0.0)\n        fill!(ke, 0.0)\n        celldofs!(celldofs, dh, cellid)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"In a normal finite elment code, this is the point where we usually get the coordinates of the element X = getcoordinates(grid, cellid). In this case, however, we also require the cell weights, and we need to transform them to the bezier mesh.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"        extr = grid.beo[cellid] # Extraction operator\n        X = getcoordinates(grid.grid, cellid) #Nurbs coords\n        w = IGA.getweights(grid, cellid)       #Nurbs weights\n        wᴮ = compute_bezier_points(extr, w)\n        Xᴮ = inv.(wᴮ) .* compute_bezier_points(extr, w.*X)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"tip: Tip\nSince the operations above are quite common in IGA, there is a helper-function called get_bezier_coordinates(grid, cellid), which return the bezier coorinates and weights directly.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Furthermore, we pass the bezier extraction operator to the CellValues/Beziervalues.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"        set_bezier_operator!(cv, extr)\n\n        integrate_element!(ke, Xᴮ, w, stiffmat, cv)\n        assemble!(assembler, celldofs, ke, fe)\n    end\n\n    # Assamble external forces\n    for (cellid, faceid) in getfaceset(grid, \"left\")\n        fill!(fe, 0.0)\n        fill!(ke, 0.0)\n\n        celldofs!(celldofs, dh, cellid)\n\n        extr = grid.beo[cellid]\n        Xᴮ, w = get_bezier_coordinates(grid, cellid)\n        set_bezier_operator!(fv, extr)\n\n        integrate_traction_force!(fe, Xᴮ, w, traction, fv, faceid)\n        assemble!(assembler, celldofs, ke, fe)\n    end\n\n    return K, f\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"This is a function that returns the elastic stiffness matrix","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function get_material(; E, ν)\n    λ = E*ν / ((1 + ν) * (1 - 2ν))\n    μ = E / (2(1 + ν))\n    δ(i,j) = i == j ? 1.0 : 0.0\n    g(i,j,k,l) = λ*δ(i,j)*δ(k,l) + μ*(δ(i,k)*δ(j,l) + δ(i,l)*δ(j,k))\n\n    return SymmetricTensor{4, 2}(g)\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"We also create a function that calculates the stress in each quadrature point, given the cell displacement and such...","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function calculate_stress(dh, cv::JuAFEM.Values, C::SymmetricTensor{4,2}, u::Vector{Float64})\n\n    celldofs = zeros(Int, ndofs_per_cell(dh))\n\n    #Store the stresses in each qp for all cells\n    cellstresses = Vector{SymmetricTensor{2,2,Float64,3}}[]\n\n    for cellid in 1:getncells(dh.grid)\n\n        extr = dh.grid.beo[cellid]\n        Xᴮ, w = get_bezier_coordinates(dh.grid, cellid)\n\n        set_bezier_operator!(cv, extr)\n        reinit!(cv, (Xᴮ, w))\n        celldofs!(celldofs, dh, cellid)\n\n        ue = u[celldofs]\n        qp_stresses = SymmetricTensor{2,2,Float64,3}[]\n        for qp in 1:getnquadpoints(cv)\n            ɛ = symmetric(function_gradient(cv, qp, ue))\n            σ = C ⊡ ε\n            push!(qp_stresses, σ)\n        end\n        push!(cellstresses, qp_stresses)\n    end\n\n    return cellstresses\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Now we have all the parts needed to solve the problem. We begin by generating the mesh. IGA.jl includes a couple of different functions that can generate different nurbs patches. In this example, we will generate the patch called \"plate with hole\". Note, currently this function can only generate the patch with second order basefunctions.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"function solve()\n    orders = (2,2) # Order in the ξ and η directions .\n    nels = (10,10) # Number of elements\n    nurbsmesh = generate_nurbs_patch(:plate_with_hole, nels)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Performing the computation on a NURBS-patch is possible, but it is much easier to use the bezier-extraction technique. For this we transform the NURBS-patch into a BezierGrid. The BezierGrid is identical to the standard JuAFEM.Grid, but includes the NURBS-weights and bezier extraction operators.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    grid = BezierGrid(nurbsmesh)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Next, create some facesets. This is done in the same way as in normal JuAFEM-code. One thing to note however, is that the nodes/controlpoints, does not necessary lay exactly on the geometry due to the non-interlapotry nature of NURBS spline functions. However, in most cases they will be close enough to use the JuAFEM functions below.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    addnodeset!(grid,\"right\", (x) -> x[1] ≈ -0.0)\n    addfacetset!(grid, \"left\", (x) -> x[1] ≈ -4.0)\n    addfacetset!(grid, \"bot\", (x) -> x[2] ≈ 0.0)\n    addfacetset!(grid, \"right\", (x) -> x[1] ≈ 0.0)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Create the cellvalues storing the shape function values. Note that the CellVectorValues/FaceVectorValues are wrapped in a BezierValues. It is in the reinit-function of the BezierValues that the actual bezier transformation of the shape values is performed.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    ip = BernsteinBasis{2,orders}()\n    qr_cell = QuadratureRule{2,RefCube}(3)\n    qr_face = QuadratureRule{1,RefCube}(3)\n\n    cv = BezierValues( CellVectorValues(qr_cell, ip) )\n    fv = BezierValues( FaceVectorValues(qr_face, ip) )","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Distribute dofs as normal","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    dh = MixedDofHandler(grid)\n    push!(dh, :u, 2, ip)\n    close!(dh)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Add two symmetry boundary condintions. Bottom face should only be able to move in x-direction, and the right boundary should only be able to move in y-direction","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    ch = ConstraintHandler(dh)\n    dbc1 = Dirichlet(:u, getfaceset(grid, \"bot\"), (x, t) -> 0.0, 2)\n    dbc2 = Dirichlet(:u, getfaceset(grid, \"right\"), (x, t) -> 0.0, 1)\n    add!(ch, dbc1)\n    add!(ch, dbc2)\n    close!(ch)\n    update!(ch, 0.0)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Define stiffness matrix and traction force","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    stiffmat = get_material(E = 100, ν = 0.3)\n    traction = Vec((-10.0, 0.0))\n    K,f = assemble_problem(dh, grid, cv, fv, stiffmat, traction)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Solve","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    apply!(K, f, ch)\n    u = K\\f","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Now we want to export the results to VTK. So we calculate the stresses in each gauss-point, and project them to the nodes using the L2Projector from JuAFEM. Node that we need to create new CellValues of type CellScalarValues, since the L2Projector only works with scalar fields.","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    cellstresses = calculate_stress(dh, cv, stiffmat, u)\n\n    csv = BezierValues( CellScalarValues(qr_cell, ip) )\n    projector = L2Projector(csv, ip, grid)\n    σ_nodes = project(cellstresses, projector)","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Output results to VTK","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"    vtkgrid = vtk_grid(\"plate_with_hole.vtu\", grid)\n    vtk_point_data(vtkgrid, dh, u, :u)\n    vtk_point_data(vtkgrid, σ_nodes, \"sigma\", grid)\n    vtk_save(vtkgrid)\n\nend;\nnothing #hide","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"Call the function","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"solve()","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"","category":"page"},{"location":"examples/example1/","page":"Infinite plate with hole","title":"Infinite plate with hole","text":"This page was generated using Literate.jl.","category":"page"},{"location":"#IGA.jl","page":"Home","title":"IGA.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Documentation for IGA.jl","category":"page"},{"location":"","page":"Home","title":"Home","text":"BezierCell","category":"page"},{"location":"#IGA.BezierCell","page":"Home","title":"IGA.BezierCell","text":"BezierCell{dim,N,order,M} <: JuAFEM.AbstractCell{dim,N,M}\n\ndim = spacial dimension N = number of nodes/controlpoints order = tuple with order in each parametric dimension (does not need to be equal to dim) M = number of faces (used by JuAFEM) (4 in 2d, 6 in 3d)\n\n\n\n\n\n","category":"type"}]
 }
diff --git a/docs/old_examples/curved_ex.jl b/docs/old_examples/curved_ex.jl
index f951101..2a1b5d6 100644
--- a/docs/old_examples/curved_ex.jl
+++ b/docs/old_examples/curved_ex.jl
@@ -227,9 +227,9 @@ function goiga()
     order = 2
     dim = 2
 
-    addfaceset!(grid, "right",   (x)->x[1] ≈ 0.0)
-    addfaceset!(grid, "left",   (x)->x[1] ≈ -4.0)
-    addfaceset!(grid, "bottom", (x)->x[2] ≈ 0.0)
+    addfacetset!(grid, "right",   (x)->x[1] ≈ 0.0)
+    addfacetset!(grid, "left",   (x)->x[1] ≈ -4.0)
+    addfacetset!(grid, "bottom", (x)->x[2] ≈ 0.0)
 
     ip = IGA.Bernstein{dim,(order, order)}()
 
@@ -237,7 +237,7 @@ function goiga()
     cellvalues = IGA.BezierValues(CellVectorValues(qr, ip))
 
     qr = QuadratureRule{dim-1,RefCube}(4)
-    facevalues = IGA.BezierFaceValues(FaceVectorValues(qr, ip))
+    facevalues = IGA.BezierFacetValues(FaceVectorValues(qr, ip))
 
     go(grid, ip, cellvalues, facevalues)
 end
@@ -248,9 +248,9 @@ function gofem()
     order = 2
     dim = 2
 
-    addfaceset!(grid, "right",   (x)->x[1] ≈ 0.0)
-    addfaceset!(grid, "left",   (x)->x[1] ≈ -4.0)
-    addfaceset!(grid, "bottom", (x)->x[2] ≈ 0.0)
+    addfacetset!(grid, "right",   (x)->x[1] ≈ 0.0)
+    addfacetset!(grid, "left",   (x)->x[1] ≈ -4.0)
+    addfacetset!(grid, "bottom", (x)->x[2] ≈ 0.0)
 
     ip_geom = Lagrange{dim,RefCube,1}()
     ip = Lagrange{dim,RefCube,order}()
diff --git a/docs/old_examples/dawn_gen.jl b/docs/old_examples/dawn_gen.jl
index 42454db..3b05064 100644
--- a/docs/old_examples/dawn_gen.jl
+++ b/docs/old_examples/dawn_gen.jl
@@ -14,7 +14,7 @@ function blabla()
     grid = IGA.BezierGrid(mesh)
     addnodeset!(grid, "left", (x) -> x[1]≈0.0)
     addnodeset!(grid, "top", (x) -> x[2]≈h)
-    addfaceset!(grid, "top", (x) -> x[2]≈h)
+    addfacetset!(grid, "top", (x) -> x[2]≈h)
 
     file = open("beam_order$(order)_nelx$(nelx)_nely$(nely)", "w")
 
diff --git a/docs/old_examples/heat_example.jl b/docs/old_examples/heat_example.jl
index 0ce6498..9c517f8 100644
--- a/docs/old_examples/heat_example.jl
+++ b/docs/old_examples/heat_example.jl
@@ -58,10 +58,10 @@ function goiga(nelx,nely, order, multiplicity)
     
     #@show nurbsmesh.knot_vectors
 
-    addfaceset!(grid, "left",   (x)->x[1]<0.001)
-    addfaceset!(grid, "right",  (x)->x[1]>Lx*0.9999)
-    addfaceset!(grid, "bottom", (x)->x[2]<0.001)
-    addfaceset!(grid, "top",    (x)->x[2]>Ly*0.9999)
+    addfacetset!(grid, "left",   (x)->x[1]<0.001)
+    addfacetset!(grid, "right",  (x)->x[1]>Lx*0.9999)
+    addfacetset!(grid, "bottom", (x)->x[2]<0.001)
+    addfacetset!(grid, "top",    (x)->x[2]>Ly*0.9999)
 
     ip = IGA.Bernstein{dim, (order,order)}()
     qr = QuadratureRule{dim, RefCube}(100)
diff --git a/docs/old_examples/heat_example2.jl b/docs/old_examples/heat_example2.jl
index 213d7bf..6e9edb4 100644
--- a/docs/old_examples/heat_example2.jl
+++ b/docs/old_examples/heat_example2.jl
@@ -58,10 +58,10 @@ function goiga(nelx, nely, order, multiplicity)
 
     #@show nurbsmesh.knot_vectors
 
-    addfaceset!(grid, "left",   (x)->x[1]<0.001)
-    addfaceset!(grid, "right",  (x)->x[1]>Lx*0.9999)
-    addfaceset!(grid, "bottom", (x)->x[2]<0.001)
-    addfaceset!(grid, "top",    (x)->x[2]>Ly*0.9999)
+    addfacetset!(grid, "left",   (x)->x[1]<0.001)
+    addfacetset!(grid, "right",  (x)->x[1]>Lx*0.9999)
+    addfacetset!(grid, "bottom", (x)->x[2]<0.001)
+    addfacetset!(grid, "top",    (x)->x[2]>Ly*0.9999)
 
     ip = IGA.Bernstein{dim, (order,order)}()
     qr = QuadratureRule{dim, RefCube}(100)
@@ -104,7 +104,7 @@ function goiga(nelx, nely, order, multiplicity)
     @assert(isodd(nely))
     midcell = ceil(Int, nelx*nely*0.5)
     qr = QuadratureRule{dim, RefCube}(1)
-    cellvalues = IGA.BezierFaceValues(CellScalarValues(qr, ip))
+    cellvalues = IGA.BezierFacetValues(CellScalarValues(qr, ip))
     coords = getcoordinates(grid, midcell)
     bcoords = IGA.compute_bezier_points(Cvec[midcell], coords)
     IGA.set_bezier_operator!(cellvalues, Cvec[midcell])
diff --git a/docs/old_examples/quarter_hole.jl b/docs/old_examples/quarter_hole.jl
index 6a14e7c..d6475b8 100644
--- a/docs/old_examples/quarter_hole.jl
+++ b/docs/old_examples/quarter_hole.jl
@@ -187,9 +187,9 @@ function solve()
     # does not necessary lay exactly on the geometry due to the non-interlapotry nature of NURBS spline functions. However, in most cases they will be close enough to 
     # use the Ferrite functions below.
     addnodeset!(grid,"right", (x) -> x[1] ≈ -0.0)
-    addfaceset!(grid, "left", (x) -> x[1] ≈ -4.0)
-    addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0)
-    addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0)
+    addfacetset!(grid, "left", (x) -> x[1] ≈ -4.0)
+    addfacetset!(grid, "bot", (x) -> x[2] ≈ 0.0)
+    addfacetset!(grid, "right", (x) -> x[1] ≈ 0.0)
 
     # Create the cellvalues storing the shape function values. Note that the `CellVectorValues`/`FaceVectorValues` are wrapped in a `BezierValues`. It is in the 
     # reinit-function of the `BezierValues` that the actual bezier transformation of the shape values is performed. 
diff --git a/docs/src/examples/example1.jl b/docs/src/examples/example1.jl
index 63e7aa1..6d0b444 100644
--- a/docs/src/examples/example1.jl
+++ b/docs/src/examples/example1.jl
@@ -129,9 +129,9 @@ function solve()
     grid = BezierGrid(nurbsmesh)
 
     addnodeset!(grid,"right", (x) -> x[1] ≈ -0.0)
-    addfaceset!(grid, "left", (x) -> x[1] ≈ -4.0)
-    addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0)
-    addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0)
+    addfacetset!(grid, "left", (x) -> x[1] ≈ -4.0)
+    addfacetset!(grid, "bot", (x) -> x[2] ≈ 0.0)
+    addfacetset!(grid, "right", (x) -> x[1] ≈ 0.0)
 
     ip = Bernstein{2,orders}()
     qr_cell = QuadratureRule{2,RefCube}(3)
diff --git a/docs/src/examples/example1.md b/docs/src/examples/example1.md
index 1372fe9..2787fc0 100644
--- a/docs/src/examples/example1.md
+++ b/docs/src/examples/example1.md
@@ -203,9 +203,9 @@ use the Ferrite functions below.
 
 ```@example example1
     addnodeset!(grid,"right", (x) -> x[1] ≈ -0.0)
-    addfaceset!(grid, "left", (x) -> x[1] ≈ -4.0)
-    addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0)
-    addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0)
+    addfacetset!(grid, "left", (x) -> x[1] ≈ -4.0)
+    addfacetset!(grid, "bot", (x) -> x[2] ≈ 0.0)
+    addfacetset!(grid, "right", (x) -> x[1] ≈ 0.0)
 ```
 
 Create the cellvalues storing the shape function values. Note that the `CellVectorValues`/`FaceVectorValues` are wrapped in a `BezierValues`. It is in the
diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl
index 6bdfe58..d64b6d7 100644
--- a/docs/src/literate/plate_with_hole.jl
+++ b/docs/src/literate/plate_with_hole.jl
@@ -157,19 +157,19 @@ function solve()
     # does not necessary lay exactly on the geometry due to the non-interlapotry nature of NURBS spline functions. However, in most cases they will be close enough to 
     # use the Ferrite functions below.
     addnodeset!(grid, "right", (x) -> x[1] ≈ -0.0)
-    addfaceset!(grid, "left", (x) -> x[1] ≈ -4.0)
-    addfaceset!(grid, "bot", (x) -> x[2] ≈ 0.0)
-    addfaceset!(grid, "right", (x) -> x[1] ≈ 0.0)
+    addfacetset!(grid, "left", (x) -> x[1] ≈ -4.0)
+    addfacetset!(grid, "bot", (x) -> x[2] ≈ 0.0)
+    addfacetset!(grid, "right", (x) -> x[1] ≈ 0.0)
 
     # Create the cellvalues storing the shape function values. Note that the `CellVectorValues`/`FaceVectorValues` are wrapped in a `BezierValues`. It is in the 
     # reinit-function of the `BezierValues` that the actual bezier transformation of the shape values is performed. 
     ip_geo = IGAInterpolation{RefQuadrilateral,order}()
     ip_u = ip_geo^2
     qr_cell = QuadratureRule{RefQuadrilateral}(4)
-    qr_face = FaceQuadratureRule{RefQuadrilateral}(3)
+    qr_face = FacetQuadratureRule{RefQuadrilateral}(3)
 
     cv = CellValues(qr_cell, ip_u, ip_geo)
-    fv = FaceValues(qr_face, ip_u, ip_geo)
+    fv = FacetValues(qr_face, ip_u, ip_geo)
 
     # Distribute dofs as normal
     dh = DofHandler(grid)
diff --git a/src/IGA.jl b/src/IGA.jl
index 522b5d7..1568e02 100644
--- a/src/IGA.jl
+++ b/src/IGA.jl
@@ -9,6 +9,9 @@ using Ferrite:
     VectorInterpolation, VectorizedInterpolation,
     FunctionValues, GeometryMapping, MappingValues
 
+using OrderedCollections:
+    OrderedSet
+
 using WriteVTK
 using LinearAlgebra
 using StaticArrays
diff --git a/src/bezier_grid.jl b/src/bezier_grid.jl
index d4462b3..a8e1ff1 100644
--- a/src/bezier_grid.jl
+++ b/src/bezier_grid.jl
@@ -8,17 +8,15 @@ struct BezierGrid{dim,C<:Ferrite.AbstractCell,T<:Real} <: Ferrite.AbstractGrid{d
 	#Alias from grid
 	cells::Vector{C}
     nodes::Vector{Node{dim,T}}
-    cellsets::Dict{String,Set{Int}}
-    nodesets::Dict{String,Set{Int}}
-    facesets::Dict{String,Set{FaceIndex}}
-    edgesets::Dict{String,Set{EdgeIndex}}
-    vertexsets::Dict{String,Set{VertexIndex}}
-    boundary_matrix::Ferrite.SparseMatrixCSC{Bool,Int}
+    cellsets::Dict{String,OrderedSet{Int}}
+    nodesets::Dict{String,OrderedSet{Int}}
+    facetsets::Dict{String,OrderedSet{FacetIndex}}
+    vertexsets::Dict{String,OrderedSet{VertexIndex}}
 
 	function BezierGrid(grid::Ferrite.Grid{dim,C,T}, weights::Vector{T}, beo::Vector{BezierExtractionOperator{T}}) where {dim,C,T}
 		return new{dim,C,T}(
 			grid, weights, beo,
-			grid.cells, grid.nodes, grid.cellsets, grid.nodesets, grid.facesets, grid.edgesets, grid.vertexsets, grid.boundary_matrix
+			grid.cells, grid.nodes, grid.cellsets, grid.nodesets, grid.facetsets, grid.vertexsets, 
 		)
 	end
 end
@@ -27,15 +25,13 @@ function BezierGrid(cells::Vector{C},
 		nodes::Vector{Ferrite.Node{dim,T}},
 		weights::AbstractVector{T},
 		extraction_operator::AbstractVector{BezierExtractionOperator{T}}; 
-		cellsets::Dict{String,Set{Int}}             =Dict{String,Set{Int}}(),
-		nodesets::Dict{String,Set{Int}}             =Dict{String,Set{Int}}(),
-		facesets::Dict{String,Set{FaceIndex}}       =Dict{String,Set{FaceIndex}}(),
-		edgesets::Dict{String,Set{EdgeIndex}}       =Dict{String,Set{EdgeIndex}}(),
-		vertexsets::Dict{String,Set{VertexIndex}}   =Dict{String,Set{VertexIndex}}(),
-		boundary_matrix::SparseArrays.SparseMatrixCSC{Bool,Int}  = SparseArrays.spzeros(Bool, 0, 0)) where {dim,C,T}
+		cellsets::Dict{String,OrderedSet{Int}}             = Dict{String,OrderedSet{Int}}(),
+		nodesets::Dict{String,OrderedSet{Int}}             = Dict{String,OrderedSet{Int}}(),
+		facetsets::Dict{String,OrderedSet{FacetIndex}}      = Dict{String,OrderedSet{FacetIndex}}(),
+		vertexsets::Dict{String,OrderedSet{VertexIndex}}   = Dict{String,OrderedSet{VertexIndex}}()) where {dim,C,T}
 
 	
-	grid = Ferrite.Grid(cells, nodes; nodesets, cellsets, facesets, edgesets, vertexsets, boundary_matrix)
+	grid = Ferrite.Grid(cells, nodes; nodesets, cellsets, facetsets, vertexsets)
 
 	return BezierGrid(grid, weights, extraction_operator)
 end
diff --git a/src/mesh_generation.jl b/src/mesh_generation.jl
index b6bbb1b..c88644e 100644
--- a/src/mesh_generation.jl
+++ b/src/mesh_generation.jl
@@ -45,11 +45,11 @@ function Ferrite.generate_grid(::Type{<:BezierCell{RefQuadrilateral,order}}, nel
 	patch = generate_nurbs_patch(:rectangle, nels, ntuple(i->order,2); cornerpos = Tuple(LL), size = Tuple(UR-LL))
 	grid = BezierGrid(patch)
 	
-	addfaceset!(grid, "left", x->x[1]≈LL[1])
-	addfaceset!(grid, "right", x->x[1]≈UR[1])
+	addfacetset!(grid, "left", x->x[1]≈LL[1])
+	addfacetset!(grid, "right", x->x[1]≈UR[1])
 	
-	addfaceset!(grid, "top", x->x[2]≈UR[2])
-	addfaceset!(grid, "bottom", x->x[2]≈LL[2])
+	addfacetset!(grid, "top", x->x[2]≈UR[2])
+	addfacetset!(grid, "bottom", x->x[2]≈LL[2])
 
 	return grid
 end
@@ -59,14 +59,14 @@ function Ferrite.generate_grid(::Type{<:BezierCell{RefHexahedron,order}}, nels::
 	patch = generate_nurbs_patch(:cube, nels, ntuple(i->order,3); cornerpos = Tuple(left), size = Tuple(right-left))
 	grid = BezierGrid(patch)
 	
-	addfaceset!(grid, "left", x->x[1]≈left[1])
-	addfaceset!(grid, "right", x->x[1]≈right[1])
+	addfacetset!(grid, "left", x->x[1]≈left[1])
+	addfacetset!(grid, "right", x->x[1]≈right[1])
 	
-	addfaceset!(grid, "front", x->x[2]≈left[2])
-	addfaceset!(grid, "back", x->x[2]≈right[2])
+	addfacetset!(grid, "front", x->x[2]≈left[2])
+	addfacetset!(grid, "back", x->x[2]≈right[2])
 
-	addfaceset!(grid, "top", x->x[3]≈right[3])
-	addfaceset!(grid, "bottom", x->x[3]≈left[3])
+	addfacetset!(grid, "top", x->x[3]≈right[3])
+	addfacetset!(grid, "bottom", x->x[3]≈left[3])
 	return grid
 end
 
diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl
index cd861ec..3b1c483 100644
--- a/src/splines/bezier.jl
+++ b/src/splines/bezier.jl
@@ -27,7 +27,7 @@ Ferrite.getnbasefunctions(::Bernstein{RefLine,2}) = 3
 
 Ferrite.vertexdof_indices(::Bernstein{RefLine,2}) = ((1,),(2,))
 Ferrite.facedof_indices(::Bernstein{RefLine,2}) = ((1,), (2,))
-Ferrite.celldof_interior_indices(::Bernstein{RefLine,2}) = (3,)
+Ferrite.volumedof_interior_indices(::Bernstein{RefLine,2}) = (3,)
 
 function Ferrite.shape_value(ip::Bernstein{RefLine,2}, _ξ::Vec{1}, i::Int)
     ξ = 0.5*(_ξ[1] + 1.0)
@@ -45,7 +45,7 @@ Ferrite.getnbasefunctions(::Bernstein{RefQuadrilateral,2}) = 9
 Ferrite.vertexdof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,),(2,),(3,),(4,))
 Ferrite.facedof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,2, 5), (2,3, 6), (3,4, 7), (4,1, 8))
 Ferrite.facedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = ((5,), (6,), (7,), (8,))
-Ferrite.celldof_interior_indices(::Bernstein{RefQuadrilateral,2}) = (9,)
+Ferrite.volumedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = (9,)
 
 function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,2}, _ξ::Vec{2}, i::Int)
     ξ, η = _ξ
diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl
index c26862d..328b5ce 100644
--- a/src/splines/bezier_values.jl
+++ b/src/splines/bezier_values.jl
@@ -1,4 +1,4 @@
-export BezierCellValues, BezierFaceValues, set_bezier_operator!
+export BezierCellValues, BezierFacetValues, set_bezier_operator!
 
 function Ferrite.default_geometric_interpolation(::IGAInterpolation{shape, order}) where {order, dim, shape <: AbstractRefShape{dim}}
     return VectorizedInterpolation{dim}(IGAInterpolation{shape, order}())
@@ -19,7 +19,7 @@ struct BezierCellValues{FV, GM, QR, T} <: Ferrite.AbstractCellValues
     current_w::Vector{T}
 end
 
-struct BezierFaceValues{FV, GM, FQR, dim, T, V_FV<:AbstractVector{FV}, V_GM<:AbstractVector{GM}} <: Ferrite.AbstractFaceValues
+struct BezierFacetValues{FV, GM, FQR, dim, T, V_FV<:AbstractVector{FV}, V_GM<:AbstractVector{GM}} <: Ferrite.AbstractFacetValues
     bezier_values::V_FV # FunctionValues
     tmp_values::V_FV    # FunctionValues
     nurbs_values::V_FV  # FunctionValues
@@ -30,23 +30,23 @@ struct BezierFaceValues{FV, GM, FQR, dim, T, V_FV<:AbstractVector{FV}, V_GM<:Abs
 
     current_beo::Base.RefValue{BezierExtractionOperator{T}}
     current_w::Vector{T}
-    current_face::Ferrite.ScalarWrapper{Int}
+    current_facet::Ferrite.ScalarWrapper{Int}
 end
 
 Ferrite.shape_value_type(cv::BezierCellValues) = Ferrite.shape_value_type(cv.bezier_values)
-Ferrite.shape_value_type(cv::BezierFaceValues) = Ferrite.shape_value_type(cv.bezier_values[Ferrite.getcurrentface(cv)])
+Ferrite.shape_value_type(cv::BezierFacetValues) = Ferrite.shape_value_type(cv.bezier_values[Ferrite.getcurrentface(cv)])
 
-Ferrite.shape_gradient_type(cv::BezierFaceValues) = Ferrite.shape_gradient_type(cv.bezier_values[Ferrite.getcurrentface(cv)])
+Ferrite.shape_gradient_type(cv::BezierFacetValues) = Ferrite.shape_gradient_type(cv.bezier_values[Ferrite.getcurrentface(cv)])
 Ferrite.shape_gradient_type(cv::BezierCellValues) = Ferrite.shape_gradient_type(cv.bezier_values)
 
-BezierCellAndFaceValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFaceValues{T,CV}}
+BezierCellAndFacetValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFacetValues{T,CV}}
 
-Ferrite.nfaces(fv::BezierFaceValues) = length(fv.geo_mapping)
-Ferrite.getnormal(fv::BezierFaceValues, iqp::Int) = fv.normals[iqp]
+Ferrite.nfaces(fv::BezierFacetValues) = length(fv.geo_mapping)
+Ferrite.getnormal(fv::BezierFacetValues, iqp::Int) = fv.normals[iqp]
 Ferrite.function_interpolation(cv::BezierCellValues) = Ferrite.function_interpolation(cv.bezier_values)
 Ferrite.geometric_interpolation(cv::BezierCellValues) = Ferrite.geometric_interpolation(cv.geo_mapping)
-Ferrite.function_interpolation(cv::BezierFaceValues) = Ferrite.function_interpolation(cv.bezier_values[1])
-Ferrite.geometric_interpolation(cv::BezierFaceValues) = Ferrite.geometric_interpolation(cv.geo_mapping[1])
+Ferrite.function_interpolation(cv::BezierFacetValues) = Ferrite.function_interpolation(cv.bezier_values[1])
+Ferrite.geometric_interpolation(cv::BezierFacetValues) = Ferrite.geometric_interpolation(cv.geo_mapping[1])
 
 function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; 
     update_gradients::Bool = true, update_hessians = false, update_detJdV::Bool = true) where T 
@@ -72,7 +72,7 @@ end
 
 BezierCellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) = BezierCellValues(Float64, qr, ip, args...; kwargs...)
 
-function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = Ferrite.default_geometric_interpolation(ip_fun); 
+function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = Ferrite.default_geometric_interpolation(ip_fun); 
     update_hessians::Bool = false, update_gradients::Bool = true) where {T,sdim} 
 
     FunDiffOrder  = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs
@@ -85,7 +85,7 @@ function BezierFaceValues(::Type{T}, fqr::FaceQuadratureRule, ip_fun::Interpolat
     normals = fill(zero(Vec{sdim, T}) * T(NaN), max_nquadpoints)
     undef_beo = Ref(Vector{SparseArrays.SparseVector{T,Int}}(undef,0))
     undef_w   = NaN .* zeros(Float64, Ferrite.getngeobasefunctions(first(geo_mapping)))
-    return BezierFaceValues(
+    return BezierFacetValues(
         fun_values, 
         deepcopy(fun_values), 
         deepcopy(fun_values), 
@@ -105,45 +105,45 @@ function Ferrite.CellValues(
     return cv
 end
 
-#Intercept construction of FaceValues called with IGAInterpolation
-function Ferrite.FaceValues(
+#Intercept construction of FacetValues called with IGAInterpolation
+function Ferrite.FacetValues(
     ::Type{T}, 
-    qr::FaceQuadratureRule, 
+    qr::FacetQuadratureRule, 
     ip_fun::Union{IGAInterpolation, VectorizedInterpolation{<:Any, <:Any, <:Any, <: IGAInterpolation}}, 
     ip_geo::VectorizedInterpolation; 
     update_gradients::Bool = true,
     update_hessians::Bool  = false) where T 
 
-    cv = BezierFaceValues(T, qr, ip_fun, ip_geo; update_gradients, update_hessians)
+    cv = BezierFacetValues(T, qr, ip_fun, ip_geo; update_gradients, update_hessians)
 
     return cv
 end
 
-Ferrite.getnbasefunctions(fv::BezierFaceValues)            = getnbasefunctions(fv.nurbs_values[Ferrite.getcurrentface(fv)])
+Ferrite.getnbasefunctions(fv::BezierFacetValues)            = getnbasefunctions(fv.nurbs_values[Ferrite.getcurrentface(fv)])
 Ferrite.getnbasefunctions(cv::BezierCellValues)            = getnbasefunctions(cv.nurbs_values)
-Ferrite.getngeobasefunctions(fv::BezierFaceValues)         = Ferrite.getngeobasefunctions(fv.geo_mapping[Ferrite.getcurrentface(fv)])
+Ferrite.getngeobasefunctions(fv::BezierFacetValues)         = Ferrite.getngeobasefunctions(fv.geo_mapping[Ferrite.getcurrentface(fv)])
 Ferrite.getngeobasefunctions(cv::BezierCellValues)         = Ferrite.getngeobasefunctions(cv.geo_mapping)
 Ferrite.getnquadpoints(bcv::BezierCellValues)                      = Ferrite.getnquadpoints(bcv.qr)
-Ferrite.getnquadpoints(bcv::BezierFaceValues)                      = Ferrite.getnquadpoints(bcv.qr, Ferrite.getcurrentface(bcv))
-Ferrite.getdetJdV(bv::BezierCellAndFaceValues, q_point::Int)       = bv.detJdV[q_point]
+Ferrite.getnquadpoints(bcv::BezierFacetValues)                      = Ferrite.getnquadpoints(bcv.qr, Ferrite.getcurrentface(bcv))
+Ferrite.getdetJdV(bv::BezierCellAndFacetValues, q_point::Int)       = bv.detJdV[q_point]
 Ferrite.shape_value(bcv::BezierCellValues, qp::Int, i::Int) = Ferrite.shape_value(bcv.nurbs_values, qp, i)
 Ferrite.shape_gradient(bcv::BezierCellValues, q_point::Int, i::Int) = Ferrite.shape_gradient(bcv.nurbs_values, q_point, i)
 Ferrite.geometric_value(cv::BezierCellValues, q_point::Int, i::Int) = Ferrite.geometric_value(cv.geo_mapping, q_point, i)
 
-Ferrite.shape_value(fv::BezierFaceValues, qp::Int, i::Int)          = shape_value(fv.nurbs_values[Ferrite.getcurrentface(fv)], qp, i)
-Ferrite.shape_gradient(fv::BezierFaceValues, q_point::Int, i::Int)  = shape_gradient(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i)
-Ferrite.geometric_value(fv::BezierFaceValues, q_point::Int, i::Int) = Ferrite.geometric_value(fv.geo_mapping[Ferrite.getcurrentface(fv)], q_point, i)
+Ferrite.shape_value(fv::BezierFacetValues, qp::Int, i::Int)          = shape_value(fv.nurbs_values[Ferrite.getcurrentface(fv)], qp, i)
+Ferrite.shape_gradient(fv::BezierFacetValues, q_point::Int, i::Int)  = shape_gradient(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i)
+Ferrite.geometric_value(fv::BezierFacetValues, q_point::Int, i::Int) = Ferrite.geometric_value(fv.geo_mapping[Ferrite.getcurrentface(fv)], q_point, i)
 
-#Ferrite.get_fun_values(fv::BezierFaceValues) = @inbounds fv.nurbs_values[Ferrite.getcurrentface(fv)]
-#Ferrite.get_fun_values(cv::BezierCellValues) = @inbounds cv.nurbs_values
+Ferrite.get_fun_values(fv::BezierFacetValues) = @inbounds fv.nurbs_values[Ferrite.getcurrentface(fv)]
+Ferrite.get_fun_values(cv::BezierCellValues) = @inbounds cv.nurbs_values
 
-Ferrite.shape_hessian(fv::BezierFaceValues, q_point::Int, i::Int) = shape_hessian(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i)
+Ferrite.shape_hessian(fv::BezierFacetValues, q_point::Int, i::Int) = shape_hessian(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i)
 Ferrite.shape_hessian(cv::BezierCellValues, q_point::Int, i::Int) = shape_hessian(cv.nurbs_values, q_point, i)
 
-Ferrite.getcurrentface(fv::BezierFaceValues) = fv.current_face[]
-function Ferrite.set_current_face!(fv::BezierFaceValues, face_nr::Int)
-    checkbounds(Bool, 1:Ferrite.nfaces(fv), face_nr) || throw(ArgumentError("Face index out of range."))
-    fv.current_face[] = face_nr
+Ferrite.getcurrentfacet(fv::BezierFacetValues) = fv.current_face[]
+function Ferrite.set_current_facet!(fv::BezierFacetValues, face_nr::Int)
+    checkbounds(Bool, 1:Ferrite.nfacets(fv), face_nr) || throw(ArgumentError("Face index out of range."))
+    fv.current_facet[] = face_nr
 end
 
 #=
@@ -157,23 +157,23 @@ function function_hessian(fe_v::Ferrite.AbstractValues, q_point::Int, u::Abstrac
     end
     return hess
 end
-shape_hessian_type(::Union{BezierCellValues{<:Any, <:Any, <:Any, d²NdX²_t}, BezierFaceValues{<:Any, <:Any, <:Any, d²NdX²_t}}) where d²NdX²_t = d²NdX²_t
+shape_hessian_type(::Union{BezierCellValues{<:Any, <:Any, <:Any, d²NdX²_t}, BezierFacetValues{<:Any, <:Any, <:Any, d²NdX²_t}}) where d²NdX²_t = d²NdX²_t
 function function_hessian_init(cv::Ferrite.AbstractValues, ::AbstractVector{T}) where {T}
     return zero(shape_hessian_type(cv)) * zero(T)
 end
 =#
 
-function set_bezier_operator!(bcv::BezierCellAndFaceValues, beo::BezierExtractionOperator{T}) where T 
+function set_bezier_operator!(bcv::BezierCellAndFacetValues, beo::BezierExtractionOperator{T}) where T 
     bcv.current_beo[]=beo
 end
 
-function set_bezier_operator!(bcv::BezierCellAndFaceValues, beo::BezierExtractionOperator{T}, w::Vector{T}) where T 
+function set_bezier_operator!(bcv::BezierCellAndFacetValues, beo::BezierExtractionOperator{T}, w::Vector{T}) where T 
     bcv.current_w   .= w
     bcv.current_beo[]=beo
 end
 
 #This function can be called when we know that the weights are all equal to one.
-function Ferrite.spatial_coordinate(cv::BezierCellAndFaceValues, iqp::Int, xb::Vector{<:Vec{dim,T}}) where {dim,T}
+function Ferrite.spatial_coordinate(cv::BezierCellAndFacetValues, iqp::Int, xb::Vector{<:Vec{dim,T}}) where {dim,T}
     wb = ones(T, length(xb))
     x = spatial_coordinate(cv, iqp, (xb, wb))
     return x
@@ -343,12 +343,12 @@ function reinit_values!(cv::BezierCellValues, bc::BezierCoords)
     return nothing
 end
 
-function Ferrite.reinit!(cv::BezierFaceValues, bc::BezierCoords, face_nr::Int)
+function Ferrite.reinit!(cv::BezierFacetValues, bc::BezierCoords, face_nr::Int)
     set_bezier_operator!(cv, bc.beo[], bc.w)
     return reinit!(cv, (bc.xb, bc.wb), face_nr)
 end
 
-function Ferrite.reinit!(fv::BezierFaceValues, (x,w)::CoordsAndWeight, face_nr::Int)
+function Ferrite.reinit!(fv::BezierFacetValues, (x,w)::CoordsAndWeight, face_nr::Int)
     Ferrite.set_current_face!(fv, face_nr) 
     geo_mapping   = fv.geo_mapping[face_nr]
     bezier_values = fv.bezier_values[face_nr]
@@ -599,7 +599,7 @@ Similar to Ferrite's reinit method, but in IGA with NURBS, the weights is also n
     is_vector_valued  && @assert eltype(d²Bdξ²_func) <: Tensor{3}
     !is_vector_valued && @assert eltype(d²Bdξ²_func) <: Tensor{2}
 
-    qrweights = cv_bezier isa Ferrite.FaceValues ? Ferrite.getweights(cv_bezier.qr, cb) : Ferrite.getweights(cv_bezier.qr)
+    qrweights = cv_bezier isa Ferrite.FacetValues ? Ferrite.getweights(cv_bezier.qr, cb) : Ferrite.getweights(cv_bezier.qr)
     for (i,qr_w) in pairs(qrweights)
 
         W = zero(T)
@@ -696,8 +696,8 @@ Similar to Ferrite's reinit method, but in IGA with NURBS, the weights is also n
 end
 =#
 
-function Base.show(io::IO, m::MIME"text/plain", fv::BezierFaceValues)
-    println(io, "BezierFaceValues with")
+function Base.show(io::IO, m::MIME"text/plain", fv::BezierFacetValues)
+    println(io, "BezierFacetValues with")
     nqp = getnquadpoints.(fv.qr.face_rules)
     fip = Ferrite.function_interpolation(fv)
     gip = Ferrite.geometric_interpolation(fv)
diff --git a/test/test_meshes.jl b/test/test_meshes.jl
index 9df0797..9cc4e10 100644
--- a/test/test_meshes.jl
+++ b/test/test_meshes.jl
@@ -39,8 +39,8 @@ function _get_problem_data(meshsymbol::Symbol, nels::NTuple{sdim,Int}, orders; m
     cv = CellValues(qr, bern_ip, bern_ip)
 
     #Face values
-    qr = FaceQuadratureRule{Ferrite.RefHypercube{sdim}}(5)
-    fv = FaceValues(qr, bern_ip, bern_ip)
+    qr = FacetQuadratureRule{Ferrite.RefHypercube{sdim}}(5)
+    fv = FacetValues(qr, bern_ip, bern_ip^sdim)
 
     return grid, cv, fv
 end
@@ -48,9 +48,9 @@ end
 function test_cube()
     grid, cv, fv = _get_problem_data(:cube, (2,2,2), (2,2,2), cornerpos=(-1.0,-2.0,0.0), size=(2.0,3.0,4.0))
     addcellset!(grid, "all", (x)->true)
-    addfaceset!(grid, "left", (x)-> x[1]≈-1.0)
-    addfaceset!(grid, "right", (x)->x[1]≈1.0)
-    addfaceset!(grid, "top", (x)->x[3]≈4.0)
+    addfacetset!(grid, "left", (x)-> x[1]≈-1.0)
+    addfacetset!(grid, "right", (x)->x[1]≈1.0)
+    addfacetset!(grid, "top", (x)->x[3]≈4.0)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
@@ -71,9 +71,9 @@ end
 function test_square()
     grid, cv, fv = _get_problem_data(:hypercube, (1,1,), (2,2,), cornerpos=(-1.0,-1.0), size=(2.0,3.0,))
     addcellset!(grid, "all", (x)->true)
-    addfaceset!(grid, "left", (x)-> x[1] ≈ -1.0)
-    addfaceset!(grid, "right", (x)->x[1]≈1.0)
-    addfaceset!(grid, "top", (x)->x[2]≈2.0)
+    addfacetset!(grid, "left", (x)-> x[1] ≈ -1.0)
+    addfacetset!(grid, "right", (x)->x[1]≈1.0)
+    addfacetset!(grid, "top", (x)->x[2]≈2.0)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
@@ -96,9 +96,9 @@ function test_plate_with_hole()
     L = 4.0
     r = 1.0
     addcellset!(grid, "all", (x)->true)
-    addfaceset!(grid, "right", (x)->x[1]≈-4.0)
-    addfaceset!(grid, "top", (x)->x[2]≈4.0)
-    addfaceset!(grid, "circle", (x)-> r*0.9 < norm(x) < r*1.1)
+    addfacetset!(grid, "right", (x)->x[1]≈-4.0)
+    addfacetset!(grid, "top", (x)->x[2]≈4.0)
+    addfacetset!(grid, "circle", (x)-> r*0.9 < norm(x) < r*1.1)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
@@ -119,9 +119,9 @@ end
 function test_singly_curved_3d()
     grid, cv, fv = _get_problem_data(:singly_curved, (20,2,1), (2,2,2), α = pi/2, R = 100.0, width = 5.0, thickness = 3.0)
     addcellset!(grid, "all", (x)->true)
-    addfaceset!(grid, "left", (x)->x[3]≈0.0)
-    addfaceset!(grid, "front", (x)->x[2]≈5.0/2)
-    addfaceset!(grid, "top", (x)-> sqrt(x[1]^2 + x[3]^2) > 100.0 + 3.0/3, all=true)
+    addfacetset!(grid, "left", (x)->x[3]≈0.0)
+    addfacetset!(grid, "front", (x)->x[2]≈5.0/2)
+    addfacetset!(grid, "top", (x)-> sqrt(x[1]^2 + x[3]^2) > 100.0 + 3.0/3, all=true)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
@@ -142,8 +142,8 @@ end
 function test_singly_curved_2d()
     grid, cv, fv = _get_problem_data(:singly_curved, (20,2), (2,2), α = pi/2, R = 100.0, thickness = 3.0)
     addcellset!(grid, "all", (x)->true)
-    addfaceset!(grid, "left", (x)->x[2]≈0.0)
-    addfaceset!(grid, "top", (x)-> sqrt(x[1]^2 + x[2]^2) > 100.0 + 3.0/3, all=true)
+    addfacetset!(grid, "left", (x)->x[2]≈0.0)
+    addfacetset!(grid, "top", (x)-> sqrt(x[1]^2 + x[2]^2) > 100.0 + 3.0/3, all=true)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
@@ -181,8 +181,8 @@ function test_ring()
         
     end
         
-    grid.facesets["inner"] = Set(inner)#addfaceset!(grid, "inner", Set(inner))
-    grid.facesets["outer"] = Set(outer)#addfaceset!(grid, "outer", Set(outer))
+    grid.facetsets["inner"] = Set(inner)#addfacetset!(grid, "inner", Set(inner))
+    grid.facetsets["outer"] = Set(outer)#addfacetset!(grid, "outer", Set(outer))
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
@@ -203,8 +203,8 @@ function test_cylinder_sector_3d()
     e1 = basevec(Vec{3}, 1)
     grid, cv, fv = _get_problem_data(:cylinder_sector, (8,10,3), (2,2,2); r, L)
     addcellset!(grid, "all", (x)->true)
-    addfaceset!(grid, "left", (x)->x[1]≈-L/2)
-    addfaceset!(grid, "topsurface", (x) -> (0.95r) <= norm(x - (e1⋅x)*e1) <= (1.05r), all=true)
+    addfacetset!(grid, "left", (x)->x[1]≈-L/2)
+    addfacetset!(grid, "topsurface", (x) -> (0.95r) <= norm(x - (e1⋅x)*e1) <= (1.05r), all=true)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
diff --git a/test/test_values.jl b/test/test_values.jl
index 9f6da93..9d91ce3 100644
--- a/test/test_values.jl
+++ b/test/test_values.jl
@@ -57,7 +57,7 @@ end
     bip = Bernstein{shape, 2}()
 
     qr = QuadratureRule{shape}(1)
-    qr_face = FaceQuadratureRule{shape}(1)
+    qr_face = FacetQuadratureRule{shape}(1)
 
     cv  = CellValues( qr, ip, ip)
     #cv2 = BezierCellValues( CellValues(qr, bip, bip) )
@@ -99,10 +99,10 @@ end
     reorder = IGA._bernstein_ordering(bip)
 
     qr = QuadratureRule{shape}(2)
-    qr_face = FaceQuadratureRule{shape}(3)
+    qr_face = FacetQuadratureRule{shape}(3)
 
-    fv = FaceValues( qr_face, ip, ip; update_hessians = true )
-    fv_vector = FaceValues( qr_face, ip^dim, ip; update_hessians = true )
+    fv = FacetValues( qr_face, ip, ip; update_hessians = true )
+    fv_vector = FacetValues( qr_face, ip^dim, ip; update_hessians = true )
     cv  = CellValues( qr, ip, ip; update_hessians = true)
     cv_vector = CellValues( qr, ip^dim, ip; update_hessians = true)
 
@@ -180,7 +180,7 @@ end
     end
     
     cellnum, faceidx = (9, 3)
-    addfaceset!(grid, "face1", (x)-> x[1] == -4.0)
+    addfacetset!(grid, "face1", (x)-> x[1] == -4.0)
     for (cellnum, faceidx) in getfaceset(grid, "face1")
 
         bc = getcoordinates(grid, cellnum)

From 4714b53cf5c8e2870080622981c7c0a4042a7ec3 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Sat, 8 Jun 2024 22:20:42 +0200
Subject: [PATCH 33/50] Update default_ip to geometric_ip

---
 src/IGA.jl                  | 2 +-
 src/VTK.jl                  | 2 +-
 src/apply_analytical_iga.jl | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/IGA.jl b/src/IGA.jl
index 1568e02..0319114 100644
--- a/src/IGA.jl
+++ b/src/IGA.jl
@@ -101,7 +101,7 @@ function BezierCell{refshape,order,N}(nodes::NTuple{N,Int}) where {rdim, order,
     return BezierCell{refshape,order}(nodes)
 end
 
-Ferrite.default_interpolation(::Type{<:BezierCell{shape,order}}) where {order, shape} = IGAInterpolation{shape, order}()
+Ferrite.geometric_interpolation(::Type{<:BezierCell{shape,order}}) where {order, shape} = IGAInterpolation{shape, order}()
 Ferrite.nnodes(::BezierCell{shape, order, N}) where {order, shape, N} = N
 getorders(::BezierCell{refshape, orders, N}) where {orders,refshape,N} = orders
 
diff --git a/src/VTK.jl b/src/VTK.jl
index de66f85..77e42b9 100644
--- a/src/VTK.jl
+++ b/src/VTK.jl
@@ -175,7 +175,7 @@ function _evaluate_at_geometry_nodes!(
 
     field_idx = Ferrite.find_field(sdh, fieldname)
     field_idx === nothing && error("The field $fieldname does not exist in the subdofhandler")
-	ip_geo = Ferrite.default_interpolation(CT)
+	ip_geo = Ferrite.geometric_interpolation(CT)
     ip     = Ferrite.getfieldinterpolation(sdh, field_idx)
     drange = Ferrite.dof_range(sdh, fieldname)
 	shape  = Ferrite.getrefshape(ip_geo)
diff --git a/src/apply_analytical_iga.jl b/src/apply_analytical_iga.jl
index 7765811..810200a 100644
--- a/src/apply_analytical_iga.jl
+++ b/src/apply_analytical_iga.jl
@@ -1,7 +1,7 @@
 function _default_interpolations(dh::DofHandler)
     sdhs = dh.subdofhandlers
     getcelltype(i) = typeof(getcells(Ferrite.get_grid(dh), first(sdhs[i].cellset)))
-    ntuple(i -> Ferrite.default_interpolation(getcelltype(i)), length(sdhs))
+    ntuple(i -> Ferrite.geometric_interpolation(getcelltype(i)), length(sdhs))
 end
 
 """

From 1cfde1f03c6665a61067bbc093f1a9f367bbfbed Mon Sep 17 00:00:00 2001
From: lijas 
Date: Sat, 8 Jun 2024 23:07:13 +0200
Subject: [PATCH 34/50] update facets and Bernstein ip. Fix tests

---
 docs/src/literate/plate_with_hole.jl |  8 ++---
 src/IGA.jl                           |  8 ++---
 src/VTK.jl                           |  2 +-
 src/apply_analytical_iga.jl          |  2 +-
 src/splines/bezier.jl                | 13 ++++----
 src/splines/bezier_values.jl         | 26 +++++++--------
 test/test_bertstein.jl               |  4 +--
 test/test_bsplines.jl                |  6 ----
 test/test_meshes.jl                  | 48 ++++++++++++++--------------
 test/test_values.jl                  |  2 +-
 10 files changed, 55 insertions(+), 64 deletions(-)

diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl
index d64b6d7..1af9a0d 100644
--- a/docs/src/literate/plate_with_hole.jl
+++ b/docs/src/literate/plate_with_hole.jl
@@ -53,7 +53,7 @@ end;
 function assemble_problem(dh::DofHandler, grid, cv, fv, stiffmat, traction)
 
     f = zeros(ndofs(dh))
-    K = create_matrix(dh)
+    K = create_sparsity_pattern(dh)##create_matrix(dh)
     assembler = start_assemble(K, f)
 
     n = getnbasefunctions(cv)
@@ -85,7 +85,7 @@ function assemble_problem(dh::DofHandler, grid, cv, fv, stiffmat, traction)
     end
 
     ## Assamble external forces
-    for (cellid, faceid) in getfaceset(grid, "left")
+    for (cellid, faceid) in getfacetset(grid, "left")
         fill!(fe, 0.0)
 
         celldofs!(celldofs, dh, cellid)
@@ -181,8 +181,8 @@ function solve()
 
     # Add two symmetry boundary condintions. Bottom face should only be able to move in x-direction, and the right boundary should only be able to move in y-direction
     ch = ConstraintHandler(dh)
-    dbc1 = Dirichlet(:u, getfaceset(grid, "bot"), (x, t) -> 0.0, 2)
-    dbc2 = Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.0, 1)
+    dbc1 = Dirichlet(:u, getfacetset(grid, "bot"), (x, t) -> 0.0, 2)
+    dbc2 = Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 0.0, 1)
     add!(ch, dbc1)
     add!(ch, dbc2)
     close!(ch)
diff --git a/src/IGA.jl b/src/IGA.jl
index 0319114..045bf15 100644
--- a/src/IGA.jl
+++ b/src/IGA.jl
@@ -133,12 +133,8 @@ function Ferrite.faces(c::BezierCell{shape,order}) where {shape,order}
     return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_facedof_indices(IGAInterpolation{shape,order}() )))
 end
 
-function Ferrite.edges(c::BezierCell{RefHexahedron, order}) where {order}
-    return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_edgedof_indices(IGAInterpolation{RefHexahedron,order}() )))
-end
-
-function Ferrite.edges(c::BezierCell{RefQuadrilateral, order}) where {order}
-    return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_edgedof_indices(IGAInterpolation{RefQuadrilateral,order}() )))
+function Ferrite.edges(c::BezierCell{shape, order}) where {shape,order}
+    return getindex.(Ref(c.nodes), collect.(Ferrite.dirichlet_edgedof_indices(IGAInterpolation{shape,order}() )))
 end
 
 end #end module
\ No newline at end of file
diff --git a/src/VTK.jl b/src/VTK.jl
index 77e42b9..c3412e3 100644
--- a/src/VTK.jl
+++ b/src/VTK.jl
@@ -167,7 +167,7 @@ function _evaluate_at_geometry_nodes!(
 	#
 	CT = getcelltype(dh.grid, first(sdh.cellset))
 
-    field_dim = Ferrite.getfielddim(sdh, fieldname)
+    field_dim = Ferrite.n_components(sdh, fieldname)
     ncomponents = field_dim == 2 ? 3 : field_dim
     
     nviznodes = vtk.vtk.Npts
diff --git a/src/apply_analytical_iga.jl b/src/apply_analytical_iga.jl
index 810200a..4471725 100644
--- a/src/apply_analytical_iga.jl
+++ b/src/apply_analytical_iga.jl
@@ -36,7 +36,7 @@ function apply_analytical_iga!(
         isnothing(Ferrite._find_field(sdh, fieldname)) && continue
         field_idx = Ferrite.find_field(sdh, fieldname)
         ip_fun = Ferrite.getfieldinterpolation(sdh, field_idx)
-        field_dim = Ferrite.getfielddim(sdh, field_idx)
+        field_dim = Ferrite.n_components(sdh, field_idx)
         celldofinds = Ferrite.dof_range(sdh, fieldname)
         set_intersection = if length(cellset) == length(sdh.cellset) == getncells(Ferrite.get_grid(dh))
             BitSet(1:getncells(Ferrite.get_grid(dh)))
diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl
index 3b1c483..a398dd3 100644
--- a/src/splines/bezier.jl
+++ b/src/splines/bezier.jl
@@ -26,8 +26,7 @@ Ferrite.adjust_dofs_during_distribution(::Bernstein{<:Any, 1}) = false
 Ferrite.getnbasefunctions(::Bernstein{RefLine,2}) = 3
 
 Ferrite.vertexdof_indices(::Bernstein{RefLine,2}) = ((1,),(2,))
-Ferrite.facedof_indices(::Bernstein{RefLine,2}) = ((1,), (2,))
-Ferrite.volumedof_interior_indices(::Bernstein{RefLine,2}) = (3,)
+Ferrite.edgedof_indices(::Bernstein{RefLine,2}) = ((1,2,3),)
 
 function Ferrite.shape_value(ip::Bernstein{RefLine,2}, _ξ::Vec{1}, i::Int)
     ξ = 0.5*(_ξ[1] + 1.0)
@@ -43,9 +42,10 @@ end
 # # #
 Ferrite.getnbasefunctions(::Bernstein{RefQuadrilateral,2}) = 9
 Ferrite.vertexdof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,),(2,),(3,),(4,))
-Ferrite.facedof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,2, 5), (2,3, 6), (3,4, 7), (4,1, 8))
-Ferrite.facedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = ((5,), (6,), (7,), (8,))
-Ferrite.volumedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = (9,)
+Ferrite.edgedof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,2, 5), (2,3, 6), (3,4, 7), (4,1, 8))
+Ferrite.edgedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = ((5,), (6,), (7,), (8,))
+Ferrite.facedof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,2,3,4,5,6,7,8,9),)
+Ferrite.facedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = (9,)
 
 function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,2}, _ξ::Vec{2}, i::Int)
     ξ, η = _ξ
@@ -94,6 +94,7 @@ Ferrite.edgedof_indices(::Bernstein{RefHexahedron,2}) = (
 Ferrite.edgedof_interior_indices(::Bernstein{RefHexahedron,2}) = (
     (9,), (10,), (11,), (12,), (13,), (14,), (15,), (16,), (17), (18,), (19,), (20,)
 )
+Ferrite.volumedof_interior_indices(::Bernstein{RefHexahedron,2}) = (27,)
 
 function Ferrite.shape_value(ip::Bernstein{RefHexahedron,2}, _ξ::Vec{3}, i::Int)
     ξ, η, ζ = _ξ
@@ -176,7 +177,7 @@ function _compute_bezier_shape_value(ip::Bernstein{shape,order}, ξ::Vec{dim,T},
     return val
 end
 
-function _compute_facedof_indices(ip::Bernstein{RefQuadrilateral,order}) where {order}
+function _compute_edgedof_indices(ip::Bernstein{RefQuadrilateral,order}) where {order}
     faces = Tuple[]
     orders = (order, order)
 
diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl
index 328b5ce..7b858f2 100644
--- a/src/splines/bezier_values.jl
+++ b/src/splines/bezier_values.jl
@@ -34,14 +34,14 @@ struct BezierFacetValues{FV, GM, FQR, dim, T, V_FV<:AbstractVector{FV}, V_GM<:Ab
 end
 
 Ferrite.shape_value_type(cv::BezierCellValues) = Ferrite.shape_value_type(cv.bezier_values)
-Ferrite.shape_value_type(cv::BezierFacetValues) = Ferrite.shape_value_type(cv.bezier_values[Ferrite.getcurrentface(cv)])
+Ferrite.shape_value_type(cv::BezierFacetValues) = Ferrite.shape_value_type(cv.bezier_values[Ferrite.getcurrentfacet(cv)])
 
-Ferrite.shape_gradient_type(cv::BezierFacetValues) = Ferrite.shape_gradient_type(cv.bezier_values[Ferrite.getcurrentface(cv)])
+Ferrite.shape_gradient_type(cv::BezierFacetValues) = Ferrite.shape_gradient_type(cv.bezier_values[Ferrite.getcurrentfacet(cv)])
 Ferrite.shape_gradient_type(cv::BezierCellValues) = Ferrite.shape_gradient_type(cv.bezier_values)
 
 BezierCellAndFacetValues{T,CV} = Union{BezierCellValues{T,CV}, BezierFacetValues{T,CV}}
 
-Ferrite.nfaces(fv::BezierFacetValues) = length(fv.geo_mapping)
+Ferrite.nfacets(fv::BezierFacetValues) = length(fv.geo_mapping)
 Ferrite.getnormal(fv::BezierFacetValues, iqp::Int) = fv.normals[iqp]
 Ferrite.function_interpolation(cv::BezierCellValues) = Ferrite.function_interpolation(cv.bezier_values)
 Ferrite.geometric_interpolation(cv::BezierCellValues) = Ferrite.geometric_interpolation(cv.geo_mapping)
@@ -119,28 +119,28 @@ function Ferrite.FacetValues(
     return cv
 end
 
-Ferrite.getnbasefunctions(fv::BezierFacetValues)            = getnbasefunctions(fv.nurbs_values[Ferrite.getcurrentface(fv)])
+Ferrite.getnbasefunctions(fv::BezierFacetValues)            = getnbasefunctions(fv.nurbs_values[Ferrite.getcurrentfacet(fv)])
 Ferrite.getnbasefunctions(cv::BezierCellValues)            = getnbasefunctions(cv.nurbs_values)
-Ferrite.getngeobasefunctions(fv::BezierFacetValues)         = Ferrite.getngeobasefunctions(fv.geo_mapping[Ferrite.getcurrentface(fv)])
+Ferrite.getngeobasefunctions(fv::BezierFacetValues)         = Ferrite.getngeobasefunctions(fv.geo_mapping[Ferrite.getcurrentfacet(fv)])
 Ferrite.getngeobasefunctions(cv::BezierCellValues)         = Ferrite.getngeobasefunctions(cv.geo_mapping)
 Ferrite.getnquadpoints(bcv::BezierCellValues)                      = Ferrite.getnquadpoints(bcv.qr)
-Ferrite.getnquadpoints(bcv::BezierFacetValues)                      = Ferrite.getnquadpoints(bcv.qr, Ferrite.getcurrentface(bcv))
+Ferrite.getnquadpoints(bcv::BezierFacetValues)                      = Ferrite.getnquadpoints(bcv.qr, Ferrite.getcurrentfacet(bcv))
 Ferrite.getdetJdV(bv::BezierCellAndFacetValues, q_point::Int)       = bv.detJdV[q_point]
 Ferrite.shape_value(bcv::BezierCellValues, qp::Int, i::Int) = Ferrite.shape_value(bcv.nurbs_values, qp, i)
 Ferrite.shape_gradient(bcv::BezierCellValues, q_point::Int, i::Int) = Ferrite.shape_gradient(bcv.nurbs_values, q_point, i)
 Ferrite.geometric_value(cv::BezierCellValues, q_point::Int, i::Int) = Ferrite.geometric_value(cv.geo_mapping, q_point, i)
 
-Ferrite.shape_value(fv::BezierFacetValues, qp::Int, i::Int)          = shape_value(fv.nurbs_values[Ferrite.getcurrentface(fv)], qp, i)
-Ferrite.shape_gradient(fv::BezierFacetValues, q_point::Int, i::Int)  = shape_gradient(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i)
-Ferrite.geometric_value(fv::BezierFacetValues, q_point::Int, i::Int) = Ferrite.geometric_value(fv.geo_mapping[Ferrite.getcurrentface(fv)], q_point, i)
+Ferrite.shape_value(fv::BezierFacetValues, qp::Int, i::Int)          = shape_value(fv.nurbs_values[Ferrite.getcurrentfacet(fv)], qp, i)
+Ferrite.shape_gradient(fv::BezierFacetValues, q_point::Int, i::Int)  = shape_gradient(fv.nurbs_values[Ferrite.getcurrentfacet(fv)], q_point, i)
+Ferrite.geometric_value(fv::BezierFacetValues, q_point::Int, i::Int) = Ferrite.geometric_value(fv.geo_mapping[Ferrite.getcurrentfacet(fv)], q_point, i)
 
-Ferrite.get_fun_values(fv::BezierFacetValues) = @inbounds fv.nurbs_values[Ferrite.getcurrentface(fv)]
+Ferrite.get_fun_values(fv::BezierFacetValues) = @inbounds fv.nurbs_values[Ferrite.getcurrentfacet(fv)]
 Ferrite.get_fun_values(cv::BezierCellValues) = @inbounds cv.nurbs_values
 
-Ferrite.shape_hessian(fv::BezierFacetValues, q_point::Int, i::Int) = shape_hessian(fv.nurbs_values[Ferrite.getcurrentface(fv)], q_point, i)
+Ferrite.shape_hessian(fv::BezierFacetValues, q_point::Int, i::Int) = shape_hessian(fv.nurbs_values[Ferrite.getcurrentfacet(fv)], q_point, i)
 Ferrite.shape_hessian(cv::BezierCellValues, q_point::Int, i::Int) = shape_hessian(cv.nurbs_values, q_point, i)
 
-Ferrite.getcurrentfacet(fv::BezierFacetValues) = fv.current_face[]
+Ferrite.getcurrentfacet(fv::BezierFacetValues) = fv.current_facet[]
 function Ferrite.set_current_facet!(fv::BezierFacetValues, face_nr::Int)
     checkbounds(Bool, 1:Ferrite.nfacets(fv), face_nr) || throw(ArgumentError("Face index out of range."))
     fv.current_facet[] = face_nr
@@ -349,7 +349,7 @@ function Ferrite.reinit!(cv::BezierFacetValues, bc::BezierCoords, face_nr::Int)
 end
 
 function Ferrite.reinit!(fv::BezierFacetValues, (x,w)::CoordsAndWeight, face_nr::Int)
-    Ferrite.set_current_face!(fv, face_nr) 
+    Ferrite.set_current_facet!(fv, face_nr) 
     geo_mapping   = fv.geo_mapping[face_nr]
     bezier_values = fv.bezier_values[face_nr]
     tmp_values    = fv.tmp_values[face_nr]
diff --git a/test/test_bertstein.jl b/test/test_bertstein.jl
index df5b9db..60eebc7 100644
--- a/test/test_bertstein.jl
+++ b/test/test_bertstein.jl
@@ -18,7 +18,7 @@
                 #Bernstein{RefHexahedron, 3}()
                 )
 
-        dim = Ferrite.getdim(bip)
+        dim = Ferrite.getrefdim(bip)
         for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})]
             sum = 0.0
             for i in 1:Ferrite.getnbasefunctions(bip)
@@ -32,7 +32,7 @@
     # ip_list contains interpolation which explicitly been inputed in IGA.jl
     ip_list = ( Bernstein{RefLine, 2}(), Bernstein{RefQuadrilateral, 2}(), Bernstein{RefHexahedron, 2}())
     for bip in ip_list
-        dim = Ferrite.getdim(bip)
+        dim = Ferrite.getrefdim(bip)
         for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})]
             for i in  1:Ferrite.getnbasefunctions(bip)
                 N_hardcoded = Ferrite.shape_value(bip, xi, i)
diff --git a/test/test_bsplines.jl b/test/test_bsplines.jl
index 8284f30..6756db2 100644
--- a/test/test_bsplines.jl
+++ b/test/test_bsplines.jl
@@ -45,12 +45,6 @@ end=#
     @test IGA.getnbasefunctions_dim(basis) == (7,8)
     @test IGA.getnbasefunctions(basis) == 7*8
 
-    #Knot vector needs to be in range [-1 ... 1]
-    order = 2
-    knots = Float64[-1, -1, -1, 0, 1, 1, 1]
-    @test_throws AssertionError IGA.BSplineBasis( [0.0, 1.0], 2)
-    @test_throws AssertionError IGA.BSplineBasis( ([-1.0, 0.0],), (2,))
-    @test_throws AssertionError IGA.BSplineBasis( ([-1.0, 0.0],[0.0, 1.0]), (2,2))
 end
 
 @testset "bsplines vs. bernstein" begin
diff --git a/test/test_meshes.jl b/test/test_meshes.jl
index 9cc4e10..85e21cd 100644
--- a/test/test_meshes.jl
+++ b/test/test_meshes.jl
@@ -57,13 +57,13 @@ function test_cube()
     @test V ≈ prod((2.0,3.0,4.0))
 
     #Area
-    A = _calculate_area(fv, grid, getfaceset(grid, "left"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "left"))
     @test A ≈ prod((3.0,4.0))
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "right"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "right"))
     @test A ≈ prod((3.0,4.0))
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "top"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "top"))
     @test A ≈ prod((3.0,2.0))
 
 end
@@ -80,13 +80,13 @@ function test_square()
     @test V ≈ prod((2.0,3.0))
 
     #Area
-    A = _calculate_area(fv, grid, getfaceset(grid, "left"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "left"))
     @test A ≈ 3.0
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "right"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "right"))
     @test A ≈ 3.0
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "top"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "top"))
     @test A ≈ 2.0
 
 end
@@ -105,13 +105,13 @@ function test_plate_with_hole()
     @test V ≈ L*L - 0.25*pi*r^2
 
     #Area
-    A = _calculate_area(fv, grid, getfaceset(grid, "right"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "right"))
     @test A ≈ L
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "top"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "top"))
     @test A ≈ L
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "circle"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "circle"))
     @test A ≈ 2r*pi/4 #forth of circumfrence
 
 end
@@ -128,13 +128,13 @@ function test_singly_curved_3d()
     @test isapprox(V, pi/2 * 100.0 * 5.0 * 3.0, atol = 10.0)
 
     #Area
-    A = _calculate_area(fv, grid, getfaceset(grid, "left"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "left"))
     @test A ≈ 5.0*3.0
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "front"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "front"))
     @test isapprox(A, pi/2 * 100.0 * 3.0, atol = 2.0)
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "top"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "top"))
     @test isapprox(A, (100+5.0/2)*pi/2 * 5.0, atol = 10.0) 
 
 end
@@ -150,10 +150,10 @@ function test_singly_curved_2d()
     @test isapprox(V, pi/2 * 100.0 * 3.0, atol = 1.0)
 
     #Area
-    A = _calculate_area(fv, grid, getfaceset(grid, "left"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "left"))
     @test A ≈ 3.0
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "top"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "top"))
     @test isapprox(A, (100+5.0/2)*pi/2, atol = 2.0)
     
 end
@@ -165,8 +165,8 @@ function test_ring()
     grid, cv, fv = _get_problem_data(:ring, (4,1), (2,2); ri=ri, ro=ro)
     addcellset!(grid, "all", (x)->true)
 
-    inner = FaceIndex[]
-    outer = FaceIndex[]
+    inner = FacetIndex[]
+    outer = FacetIndex[]
     for cellid in 1:getncells(grid), fid in 1:4
         beziercoords = getcoordinates(grid, cellid)
         reinit!(fv, beziercoords, fid)
@@ -174,25 +174,25 @@ function test_ring()
         x = spatial_coordinate(fv, 1, (xb, wb))
         
         if norm(x) ≈ ri
-            push!(inner, FaceIndex(cellid, fid))
+            push!(inner, FacetIndex(cellid, fid))
         elseif norm(x) ≈ ro
-            push!(outer, FaceIndex(cellid, fid))
+            push!(outer, FacetIndex(cellid, fid))
         end
         
     end
         
-    grid.facetsets["inner"] = Set(inner)#addfacetset!(grid, "inner", Set(inner))
-    grid.facetsets["outer"] = Set(outer)#addfacetset!(grid, "outer", Set(outer))
+    addfacetset!(grid, "inner", inner)
+    addfacetset!(grid, "outer", outer)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
     @test isapprox(V, pi*(ro^2 - ri^2), atol = 0.01)
 
     #Area
-    A = _calculate_area(fv, grid, getfaceset(grid, "inner"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "inner"))
     @test isapprox(A, 2pi*ri, atol = 0.01)
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "outer"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "outer"))
     @test isapprox(A, 2pi*ro, atol = 0.01)
 end
 
@@ -211,10 +211,10 @@ function test_cylinder_sector_3d()
     @test isapprox(V, pi * r^2 * L / 2.0, atol = 0.0001)
 
     #Area
-    A = _calculate_area(fv, grid, getfaceset(grid, "left"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "left"))
     @test isapprox(A, pi * r^2 / 2, atol = 0.0001)
 
-    A = _calculate_area(fv, grid, getfaceset(grid, "topsurface"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "topsurface"))
     @test isapprox(A, pi*2r/2 * L, atol = 0.0001)
 
 end
diff --git a/test/test_values.jl b/test/test_values.jl
index 9d91ce3..7fb5067 100644
--- a/test/test_values.jl
+++ b/test/test_values.jl
@@ -181,7 +181,7 @@ end
     
     cellnum, faceidx = (9, 3)
     addfacetset!(grid, "face1", (x)-> x[1] == -4.0)
-    for (cellnum, faceidx) in getfaceset(grid, "face1")
+    for (cellnum, faceidx) in getfacetset(grid, "face1")
 
         bc = getcoordinates(grid, cellnum)
         reinit!(fv, bc, faceidx)

From 50a463739612804a34be9386bf8e642b70ca18c1 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Sun, 16 Jun 2024 16:51:24 +0200
Subject: [PATCH 35/50] some work on bezier values construction

---
 src/splines/bezier_values.jl | 51 ++++++++++++++++++------------------
 test/test_meshes.jl          |  4 +--
 test/test_values.jl          | 27 +++++++++----------
 3 files changed, 39 insertions(+), 43 deletions(-)

diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl
index 7b858f2..d2b8306 100644
--- a/src/splines/bezier_values.jl
+++ b/src/splines/bezier_values.jl
@@ -3,6 +3,9 @@ export BezierCellValues, BezierFacetValues, set_bezier_operator!
 function Ferrite.default_geometric_interpolation(::IGAInterpolation{shape, order}) where {order, dim, shape <: AbstractRefShape{dim}}
     return VectorizedInterpolation{dim}(IGAInterpolation{shape, order}())
 end
+function Ferrite.default_geometric_interpolation(::VectorizedInterpolation{vdim,shape,order,IGAInterpolation{shape, order}}) where {vdim,order, dim, shape <: AbstractRefShape{dim}}
+    return VectorizedInterpolation{dim}(IGAInterpolation{shape, order}())
+end
 function Ferrite.default_geometric_interpolation(::Bernstein{shape, order}) where {order, dim, shape <: AbstractRefShape{dim}}
     return VectorizedInterpolation{dim}(Bernstein{shape, order}())
 end
@@ -71,8 +74,11 @@ function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation,
 end
 
 BezierCellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) = BezierCellValues(Float64, qr, ip, args...; kwargs...)
+function BezierCellValues(::Type{T}, qr, ip::Interpolation; kwargs...) where T
+    return BezierCellValues(T, qr, ip, Ferrite.default_geometric_interpolation(ip); kwargs...)
+end
 
-function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim} = Ferrite.default_geometric_interpolation(ip_fun); 
+function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}; 
     update_hessians::Bool = false, update_gradients::Bool = true) where {T,sdim} 
 
     FunDiffOrder  = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs
@@ -92,6 +98,11 @@ function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpol
         geo_mapping, fqr, detJdV, normals, undef_beo, undef_w, Ferrite.ScalarWrapper(-1))
 end
 
+BezierFacetValues(qr::FacetQuadratureRule, ip::Interpolation, args...; kwargs...) = BezierFacetValues(Float64, qr, ip, args...; kwargs...)
+function BezierFacetValues(::Type{T}, qr, ip::Interpolation; kwargs...) where T
+    return BezierFacetValues(T, qr, ip, Ferrite.default_geometric_interpolation(ip); kwargs...)
+end
+
 #Intercept construction of CellValues called with IGAInterpolation
 function Ferrite.CellValues(
     ::Type{T}, 
@@ -146,23 +157,6 @@ function Ferrite.set_current_facet!(fv::BezierFacetValues, face_nr::Int)
     fv.current_facet[] = face_nr
 end
 
-#=
-function function_hessian(fe_v::Ferrite.AbstractValues, q_point::Int, u::AbstractVector{<:Vec})
-    n_base_funcs = getnbasefunctions(fe_v)
-    length(u) == n_base_funcs || Ferrite.throw_incompatible_dof_length(length(u), n_base_funcs)
-    @boundscheck Ferrite.checkquadpoint(fe_v, q_point)
-    hess = function_hessian_init(fe_v, u)
-    @inbounds for i in 1:n_base_funcs
-        hess += u[i] ⊗ shape_hessian(fe_v, q_point, i)
-    end
-    return hess
-end
-shape_hessian_type(::Union{BezierCellValues{<:Any, <:Any, <:Any, d²NdX²_t}, BezierFacetValues{<:Any, <:Any, <:Any, d²NdX²_t}}) where d²NdX²_t = d²NdX²_t
-function function_hessian_init(cv::Ferrite.AbstractValues, ::AbstractVector{T}) where {T}
-    return zero(shape_hessian_type(cv)) * zero(T)
-end
-=#
-
 function set_bezier_operator!(bcv::BezierCellAndFacetValues, beo::BezierExtractionOperator{T}) where T 
     bcv.current_beo[]=beo
 end
@@ -711,12 +705,17 @@ function Base.show(io::IO, m::MIME"text/plain", fv::BezierFacetValues)
     print(io, "- Geometric interpolation: "); show(io, m, gip)
 end
 
-function Base.show(io::IO, m::MIME"text/plain", cv::BezierCellValues)
-    fip = Ferrite.function_interpolation(cv)
-    gip = Ferrite.geometric_interpolation(cv)
-    println(io, "BezierCellValues with")
-    println(io, "- Quadrature rule with ", getnquadpoints(cv), " points")
-    print(io, "- Function interpolation: "); show(io, m, fip)
-    println(io)
-    print(io, "- Geometric interpolation: "); show(io, m, gip)
+function Base.show(io::IO, d::MIME"text/plain", cv::BezierCellValues)
+    ip_geo = geometric_interpolation(cv)
+    ip_fun = Ferrite.function_interpolation(cv)
+    rdim = Ferrite.getrefdim(ip_geo)
+    vdim = isa(shape_value(cv, 1, 1), Vec) ? length(shape_value(cv, 1, 1)) : 0
+    GradT = Ferrite.shape_gradient_type(cv)
+    sdim = GradT === nothing ? nothing : Ferrite.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: ");
+    sdim === nothing ? show(io, d, ip_geo) : show(io, d, ip_geo^sdim)
 end
diff --git a/test/test_meshes.jl b/test/test_meshes.jl
index 85e21cd..fe60d84 100644
--- a/test/test_meshes.jl
+++ b/test/test_meshes.jl
@@ -36,11 +36,11 @@ function _get_problem_data(meshsymbol::Symbol, nels::NTuple{sdim,Int}, orders; m
 
     #Cell values
     qr = Ferrite.QuadratureRule{Ferrite.RefHypercube{sdim}}(5)
-    cv = CellValues(qr, bern_ip, bern_ip)
+    cv = BezierCellValues(qr, bern_ip)
 
     #Face values
     qr = FacetQuadratureRule{Ferrite.RefHypercube{sdim}}(5)
-    fv = FacetValues(qr, bern_ip, bern_ip^sdim)
+    fv = BezierFacetValues(qr, bern_ip)
 
     return grid, cv, fv
 end
diff --git a/test/test_values.jl b/test/test_values.jl
index 7fb5067..3bacb7b 100644
--- a/test/test_values.jl
+++ b/test/test_values.jl
@@ -59,19 +59,16 @@ end
     qr = QuadratureRule{shape}(1)
     qr_face = FacetQuadratureRule{shape}(1)
 
-    cv  = CellValues( qr, ip, ip)
-    #cv2 = BezierCellValues( CellValues(qr, bip, bip) )
-    cv3 = CellValues( qr, ip, ip)
+    cv  = BezierCellValues(qr, ip)
+    cv  = BezierCellValues(qr, ip^2)
+    cv  = BezierCellValues(qr, ip, ip^3)
+    cv  = BezierCellValues(qr, ip^2, ip^3)
 
-    cv_vector1 = CellValues( qr, ip^sdim, ip )
-    #cv_vector2 = BezierCellValues( CellValues(qr, bip^sdim, bip) )
-    cv_vector3 = CellValues( qr, ip^sdim, ip )
+    #embedded
+    ip = IGAInterpolation{RefQuadrilateral, 2}()
+    qr = QuadratureRule{RefQuadrilateral}(1)
+    cv  = BezierCellValues(qr, ip^3, ip^3)
 
-    @test Ferrite.getngeobasefunctions(cv_vector1) == getnbasefunctions(ip)
-    @test Ferrite.getngeobasefunctions(cv) == getnbasefunctions(ip)
-
-    @test Ferrite.getnbasefunctions(cv_vector1) == getnbasefunctions(ip)*sdim
-    @test Ferrite.getnbasefunctions(cv) == getnbasefunctions(ip)
 end
 
 @testset "bezier values nurbs" begin
@@ -101,10 +98,10 @@ end
     qr = QuadratureRule{shape}(2)
     qr_face = FacetQuadratureRule{shape}(3)
 
-    fv = FacetValues( qr_face, ip, ip; update_hessians = true )
-    fv_vector = FacetValues( qr_face, ip^dim, ip; update_hessians = true )
-    cv  = CellValues( qr, ip, ip; update_hessians = true)
-    cv_vector = CellValues( qr, ip^dim, ip; update_hessians = true)
+    fv = BezierFacetValues( qr_face, ip, ip^dim; update_hessians = true )
+    fv_vector = BezierFacetValues( qr_face, ip^dim, ip^dim; update_hessians = true )
+    cv  = BezierCellValues( qr, ip, ip^dim; update_hessians = true)
+    cv_vector = BezierCellValues( qr, ip^dim, ip^dim; update_hessians = true)
 
     #Try some different cells
     cellnum = 1

From 7e35b0667bf3159979a4904c356a915228c1790e Mon Sep 17 00:00:00 2001
From: lijas 
Date: Thu, 27 Jun 2024 10:17:49 +0200
Subject: [PATCH 36/50] expand functionallity and tests for arbitrary order

---
 src/splines/bezier.jl  | 32 ++++++++++++++++--
 test/test_bertstein.jl |  9 +++--
 test/test_meshes.jl    | 75 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 101 insertions(+), 15 deletions(-)

diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl
index a398dd3..e6d36ed 100644
--- a/src/splines/bezier.jl
+++ b/src/splines/bezier.jl
@@ -20,6 +20,10 @@ Ferrite.adjust_dofs_during_distribution(::Bernstein) = true
 Ferrite.adjust_dofs_during_distribution(::Bernstein{<:Any, 2}) = false
 Ferrite.adjust_dofs_during_distribution(::Bernstein{<:Any, 1}) = false
 
+Ferrite.vertexdof_indices(ip::Bernstein{refshape,order}) where {refshape,order} = _compute_vertexdof_indices(ip)
+Ferrite.edgedof_indices(ip::Bernstein{refshape,order}) where {refshape,order} = _compute_edgedof_indices(ip)
+Ferrite.facedof_indices(ip::Bernstein{refshape,order}) where {refshape,order} = _compute_facedof_indices(ip)
+
 # # #
 #   Bernstein line, order 2
 # # #
@@ -65,7 +69,9 @@ end
 #   Bernstein Hexahedron, order 2
 # # #
 Ferrite.getnbasefunctions(::Bernstein{RefHexahedron,2}) = 27
-
+Ferrite.vertexdof_indices(::Bernstein{RefHexahedron,2}) = (
+    (1,),(2,),(3,),(4,),(5,),(6,),(7,),(8,)
+)
 Ferrite.facedof_indices(::Bernstein{RefHexahedron,2}) = (
     (1,4,3,2, 12,11,10,9, 21),
     (1,2,6,5, 9,18,13,17, 22),
@@ -177,6 +183,10 @@ function _compute_bezier_shape_value(ip::Bernstein{shape,order}, ξ::Vec{dim,T},
     return val
 end
 
+function _compute_vertexdof_indices(::Bernstein{RefQuadrilateral,order}) where order
+    ((1,),(2,),(3,),(4,),)
+end
+
 function _compute_edgedof_indices(ip::Bernstein{RefQuadrilateral,order}) where {order}
     faces = Tuple[]
     orders = (order, order)
@@ -195,6 +205,17 @@ function _compute_edgedof_indices(ip::Bernstein{RefQuadrilateral,order}) where {
     return Tuple(faces) 
 end
 
+function _compute_volumedof_indices(ip::Bernstein{RefQuadrilateral,order}) where {order}
+    orders = (order, order)
+    ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...)
+    volumedofs = ind[2:end-1, 2:end-1]
+    return Tuple(volumedofs) 
+end
+
+function _compute_vertexdof_indices(::Bernstein{RefHexahedron,order}) where order
+    ((1,),(2,),(3,),(4,),(5,),(6,),(7,),(8,))
+end
+
 function _compute_facedof_indices(ip::Bernstein{RefHexahedron,order}) where {order}
     faces = Tuple[]
     orders = (order, order, order)
@@ -244,6 +265,13 @@ function _compute_edgedof_indices(ip::Bernstein{RefHexahedron,order}) where {ord
     return Tuple(edges)
 end
 
+function _compute_volumedof_indices(ip::Bernstein{RefHexahedron,order}) where {order}
+    orders = (order, order, order)
+    ind = reshape([findfirst(i->i==j, _bernstein_ordering(ip)) for j in 1:prod(orders.+1)], (orders.+1)...)
+    volumedofs = ind[2:end-1, 2:end-1, 2:end-1]
+    return Tuple(volumedofs) 
+end
+
 function _bernstein_basis_recursive(p::Int, i::Int, xi::T) where T
 	if i == 1 && p == 0
 		return one(T)
@@ -289,7 +317,6 @@ end
     _bernstein_ordering(::Bernstein)
 
 Return the ordering of the bernstein basis base-functions, from a "CartesianIndices-ordering".
-The ordering is the same as in VTK: https://blog.kitware.com/wp-content/uploads/2020/03/Implementation-of-rational-Be%CC%81zier-cells-into-VTK-Report.pdf.
 """
 function _bernstein_ordering(::Bernstein{RefLine, order}) where {order}
     ind = reshape(1:prod(order+1), order+1)
@@ -378,6 +405,7 @@ function _bernstein_ordering(::Bernstein{RefHexahedron, order}) where {order}
 end
 
 #Almost the same orderign as _bernstein_ordering, but some changes for faces and edges
+#The ordering is the same as in VTK: https://blog.kitware.com/wp-content/uploads/2020/03/Implementation-of-rational-Be%CC%81zier-cells-into-VTK-Report.pdf.
 function _vtk_ordering(c::Type{<:BezierCell{RefLine}})
     _bernstein_ordering(c)
 end
diff --git a/test/test_bertstein.jl b/test/test_bertstein.jl
index 60eebc7..5bf239b 100644
--- a/test/test_bertstein.jl
+++ b/test/test_bertstein.jl
@@ -12,10 +12,10 @@
                 Bernstein{RefLine, 3}(),
                 Bernstein{RefLine, 4}(),
                 Bernstein{RefQuadrilateral, 2}(),
-                #Bernstein{RefQuadrilateral, 3}(),
                 Bernstein{RefHexahedron, 2}(),
+                IGAInterpolation{RefQuadrilateral, 3}(),
                 IGAInterpolation{RefHexahedron, 2}(),
-                #Bernstein{RefHexahedron, 3}()
+                IGAInterpolation{RefHexahedron, 3}(),
                 )
 
         dim = Ferrite.getrefdim(bip)
@@ -28,9 +28,8 @@
         end
     end
 
-    #Test generated Bernstein values with hardcoded ones
-    # ip_list contains interpolation which explicitly been inputed in IGA.jl
-    ip_list = ( Bernstein{RefLine, 2}(), Bernstein{RefQuadrilateral, 2}(), Bernstein{RefHexahedron, 2}())
+    #Test if generated Bernstein values matches with the hardcoded ones
+    ip_list = (Bernstein{RefLine, 2}(), Bernstein{RefQuadrilateral, 2}(), Bernstein{RefHexahedron, 2}())
     for bip in ip_list
         dim = Ferrite.getrefdim(bip)
         for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})]
diff --git a/test/test_meshes.jl b/test/test_meshes.jl
index fe60d84..a48ec3e 100644
--- a/test/test_meshes.jl
+++ b/test/test_meshes.jl
@@ -45,12 +45,15 @@ function _get_problem_data(meshsymbol::Symbol, nels::NTuple{sdim,Int}, orders; m
     return grid, cv, fv
 end
 
-function test_cube()
-    grid, cv, fv = _get_problem_data(:cube, (2,2,2), (2,2,2), cornerpos=(-1.0,-2.0,0.0), size=(2.0,3.0,4.0))
+function test_cube(order::Int)
+    grid, cv, fv = _get_problem_data(:cube, (2,2,2), (order,order,order), cornerpos=(-1.0,-2.0,0.0), size=(2.0,3.0,4.0))
     addcellset!(grid, "all", (x)->true)
     addfacetset!(grid, "left", (x)-> x[1]≈-1.0)
     addfacetset!(grid, "right", (x)->x[1]≈1.0)
     addfacetset!(grid, "top", (x)->x[3]≈4.0)
+    addfacetset!(grid, "bottom", (x)->x[3]≈0.0)
+    addfacetset!(grid, "front", (x)->x[2]≈-2.0)
+    addfacetset!(grid, "back", (x)->x[2]≈1.0)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
@@ -59,21 +62,55 @@ function test_cube()
     #Area
     A = _calculate_area(fv, grid, getfacetset(grid, "left"))
     @test A ≈ prod((3.0,4.0))
+    @test getnormal(fv, 1) ≈ Vec((-1.0, 0.0, 0.0))
 
     A = _calculate_area(fv, grid, getfacetset(grid, "right"))
     @test A ≈ prod((3.0,4.0))
+    @test getnormal(fv, 1) ≈ Vec((01.0, 0.0, 0.0))
 
     A = _calculate_area(fv, grid, getfacetset(grid, "top"))
     @test A ≈ prod((3.0,2.0))
+    @test getnormal(fv, 1) ≈ Vec((0.0, 0.0, 1.0))
+
+    A = _calculate_area(fv, grid, getfacetset(grid, "bottom"))
+    @test A ≈ prod((3.0,2.0))
+    @test getnormal(fv, 1) ≈ Vec((0.0, 0.0, -1.0))
+
+    A = _calculate_area(fv, grid, getfacetset(grid, "front"))
+    @test A ≈ prod((4.0,2.0))
+    @test getnormal(fv, 1) ≈ Vec((0.0, -1.0, 0.0))
+
+    A = _calculate_area(fv, grid, getfacetset(grid, "back"))
+    @test A ≈ prod((4.0,2.0))
+    @test getnormal(fv, 1) ≈ Vec((0.0, 1.0, 0.0))
 
 end
 
-function test_square()
-    grid, cv, fv = _get_problem_data(:hypercube, (1,1,), (2,2,), cornerpos=(-1.0,-1.0), size=(2.0,3.0,))
+function _create_set2(f::Function, grid::Ferrite.AbstractGrid, ::Type{BI}; all=true) where {BI <: Ferrite.BoundaryIndex}
+    set = Ferrite.OrderedSet{BI}()
+    # Since we loop over the cells in order the resulting set will be sorted
+    # lexicographically based on the (cell_idx, entity_idx) tuple
+    for (cell_idx, cell) in enumerate(getcells(grid))
+        for (entity_idx, entity) in enumerate(Ferrite.boundaryfunction(BI)(cell))
+            @show 
+            pass = all
+            for node_idx in entity
+                v = f(Ferrite.get_node_coordinate(grid, node_idx))
+                all ? (!v && (pass = false; break)) : (v && (pass = true; break))
+            end
+            pass && push!(set, BI(cell_idx, entity_idx))
+        end
+    end
+    return set
+end
+
+function test_square(order::Int)
+    grid, cv, fv = _get_problem_data(:hypercube, (1,1,), (order, order,), cornerpos=(-1.0,-1.0), size=(2.0,3.0,))
     addcellset!(grid, "all", (x)->true)
     addfacetset!(grid, "left", (x)-> x[1] ≈ -1.0)
     addfacetset!(grid, "right", (x)->x[1]≈1.0)
     addfacetset!(grid, "top", (x)->x[2]≈2.0)
+    addfacetset!(grid, "bottom", (x)->x[2]≈-1.0)
 
     #Volume
     V = _calculate_volume(cv, grid, getcellset(grid, "all"))
@@ -82,13 +119,19 @@ function test_square()
     #Area
     A = _calculate_area(fv, grid, getfacetset(grid, "left"))
     @test A ≈ 3.0
+    @test getnormal(fv, 1) ≈ Vec((-1.0, 0.0))
 
     A = _calculate_area(fv, grid, getfacetset(grid, "right"))
     @test A ≈ 3.0
+    @test getnormal(fv, 1) ≈ Vec((1.0, 0.0))
 
     A = _calculate_area(fv, grid, getfacetset(grid, "top"))
     @test A ≈ 2.0
+    @test getnormal(fv, 1) ≈ Vec((0.0, 1.0))
 
+    A = _calculate_area(fv, grid, getfacetset(grid, "bottom"))
+    @test A ≈ 2.0
+    @test getnormal(fv, 1) ≈ Vec((0.0, -1.0))
 end
 
 function test_plate_with_hole()
@@ -96,8 +139,10 @@ function test_plate_with_hole()
     L = 4.0
     r = 1.0
     addcellset!(grid, "all", (x)->true)
-    addfacetset!(grid, "right", (x)->x[1]≈-4.0)
+    addfacetset!(grid, "left", (x)->x[1]≈-4.0)
     addfacetset!(grid, "top", (x)->x[2]≈4.0)
+    addfacetset!(grid, "right", (x)->x[1]≈0.0)
+    addfacetset!(grid, "bottom", (x)->x[2]≈0.0)
     addfacetset!(grid, "circle", (x)-> r*0.9 < norm(x) < r*1.1)
 
     #Volume
@@ -105,11 +150,21 @@ function test_plate_with_hole()
     @test V ≈ L*L - 0.25*pi*r^2
 
     #Area
-    A = _calculate_area(fv, grid, getfacetset(grid, "right"))
+    A = _calculate_area(fv, grid, getfacetset(grid, "left"))
     @test A ≈ L
+    @test getnormal(fv, 1) ≈ Vec((-1.0, 0.0))
 
     A = _calculate_area(fv, grid, getfacetset(grid, "top"))
     @test A ≈ L
+    @test getnormal(fv, 1) ≈ Vec((0.0, 1.0))
+
+    A = _calculate_area(fv, grid, getfacetset(grid, "bottom"))
+    @test A ≈ L-r
+    @test getnormal(fv, 1) ≈ Vec((0.0, -1.0))
+
+    A = _calculate_area(fv, grid, getfacetset(grid, "right"))
+    @test A ≈ L-r
+    @test getnormal(fv, 1) ≈ Vec((1.0, 0.0))
 
     A = _calculate_area(fv, grid, getfacetset(grid, "circle"))
     @test A ≈ 2r*pi/4 #forth of circumfrence
@@ -222,8 +277,12 @@ end
 
 
 @testset "Geometries, vtk-outputing and integration" begin
-    test_cube()
-    test_square()
+    test_cube(1)
+    test_cube(2)
+    test_cube(3)
+    test_square(1)
+    test_square(2)
+    test_square(3)
     test_plate_with_hole()
     test_singly_curved_2d()
     test_singly_curved_3d()

From c7549925ee20d666540e8b0e09185afa8395fd33 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Thu, 27 Jun 2024 10:32:52 +0200
Subject: [PATCH 37/50] promote usage of BezierCellValues Instead of CellValues

---
 docs/src/literate/plate_with_hole.jl | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl
index 1af9a0d..7c6c607 100644
--- a/docs/src/literate/plate_with_hole.jl
+++ b/docs/src/literate/plate_with_hole.jl
@@ -168,8 +168,8 @@ function solve()
     qr_cell = QuadratureRule{RefQuadrilateral}(4)
     qr_face = FacetQuadratureRule{RefQuadrilateral}(3)
 
-    cv = CellValues(qr_cell, ip_u, ip_geo)
-    fv = FacetValues(qr_face, ip_u, ip_geo)
+    cv = BezierCellValues(qr_cell, ip_u, ip_geo^2)
+    fv = BezierFacetValues(qr_face, ip_u, ip_geo^2)
 
     # Distribute dofs as normal
     dh = DofHandler(grid)
@@ -206,7 +206,6 @@ function solve()
     #csv = BezierCellValues( CellScalarValues(qr_cell, ip) )
     projector = L2Projector(ip_u, grid)
     σ_nodes = project(projector, cellstresses, qr_cell)
-    @show σ_nodes
     # Output results to VTK
     #vtkgrid = vtk_grid("plate_with_hole.vtu", grid)
     #vtk_point_data(vtkgrid, dh, u, :u)

From dc9514378445996c4f2229f5567140def1abb0fa Mon Sep 17 00:00:00 2001
From: lijas 
Date: Thu, 27 Jun 2024 16:05:50 +0200
Subject: [PATCH 38/50] fix show for BezierCellValues

---
 src/splines/bezier_values.jl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl
index d2b8306..6e84ab1 100644
--- a/src/splines/bezier_values.jl
+++ b/src/splines/bezier_values.jl
@@ -713,7 +713,7 @@ function Base.show(io::IO, d::MIME"text/plain", cv::BezierCellValues)
     GradT = Ferrite.shape_gradient_type(cv)
     sdim = GradT === nothing ? nothing : Ferrite.sdim_from_gradtype(GradT)
     vstr = vdim==0 ? "scalar" : "vdim=$vdim"
-    print(io, "CellValues(", vstr, ", rdim=$rdim, and sdim=$sdim): ")
+    print(io, "BezierCellValues(", 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: ");

From b27b57ab5a6506c7c7c5b39977a99d7f183aa468 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 08:50:27 +0200
Subject: [PATCH 39/50] Fix boundaryindex/faceindex

---
 src/iterators.jl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/iterators.jl b/src/iterators.jl
index 16da3ff..4ea191d 100644
--- a/src/iterators.jl
+++ b/src/iterators.jl
@@ -60,7 +60,7 @@ function IGAFaceCache(args...)
     IGAFaceCache(cc, cc.dofs, Ferrite.ScalarWrapper(0))
 end
 
-function Ferrite.reinit!(fc::IGAFaceCache, face::FaceIndex)
+function Ferrite.reinit!(fc::IGAFaceCache, face::Ferrite.BoundaryIndex)
     cellid, faceid = face
     reinit!(fc.cc, cellid)
     fc.current_faceid[] = faceid

From a6a2a9f5be83327ea288e11765c58974047cd363 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 09:00:29 +0200
Subject: [PATCH 40/50] update name change in ferrite from shape_value to
 reference_shape_value

---
 src/IGA.jl              |  2 +-
 src/nurbsmesh.jl        |  2 +-
 src/splines/bezier.jl   | 12 ++++++------
 src/splines/bsplines.jl |  2 +-
 test/test_bertstein.jl  |  6 +++---
 test/test_bsplines.jl   |  4 ++--
 6 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/IGA.jl b/src/IGA.jl
index 045bf15..ab53b69 100644
--- a/src/IGA.jl
+++ b/src/IGA.jl
@@ -61,7 +61,7 @@ struct IGAInterpolation{shape, order} <: Ferrite.ScalarInterpolation{shape, orde
 end
 
 Ferrite.adjust_dofs_during_distribution(::IGAInterpolation) = false
-Ferrite.shape_value(::IGAInterpolation{shape, order}, ξ::Vec, i::Int) where {shape, order} = Ferrite.shape_value(Bernstein{shape, order}(), ξ, i)
+Ferrite.reference_shape_value(::IGAInterpolation{shape, order}, ξ::Vec, i::Int) where {shape, order} = Ferrite.reference_shape_value(Bernstein{shape, order}(), ξ, i)
 Ferrite.reference_coordinates(::IGAInterpolation{shape, order}) where {shape, order} = Ferrite.reference_coordinates(Bernstein{shape, order}())
 Ferrite.getnbasefunctions(::IGAInterpolation{shape, order}) where {shape, order} = getnbasefunctions(Bernstein{shape, order}())
 
diff --git a/src/nurbsmesh.jl b/src/nurbsmesh.jl
index 0820636..798cb7c 100644
--- a/src/nurbsmesh.jl
+++ b/src/nurbsmesh.jl
@@ -69,7 +69,7 @@ function eval_parametric_coordinate(mesh::NURBSMesh{pdim,sdim}, ξ::Vec{pdim}) w
 
 	x = zero(Vec{sdim,Float64})
 	for i in 1:getnbasefunctions(bspline)
-		N = Ferrite.shape_value(bspline, ξ, i)
+		N = Ferrite.reference_shape_value(bspline, ξ, i)
 		x += N*mesh.control_points[i]
 	end
 
diff --git a/src/splines/bezier.jl b/src/splines/bezier.jl
index e6d36ed..d1480d7 100644
--- a/src/splines/bezier.jl
+++ b/src/splines/bezier.jl
@@ -32,7 +32,7 @@ Ferrite.getnbasefunctions(::Bernstein{RefLine,2}) = 3
 Ferrite.vertexdof_indices(::Bernstein{RefLine,2}) = ((1,),(2,))
 Ferrite.edgedof_indices(::Bernstein{RefLine,2}) = ((1,2,3),)
 
-function Ferrite.shape_value(ip::Bernstein{RefLine,2}, _ξ::Vec{1}, i::Int)
+function Ferrite.reference_shape_value(ip::Bernstein{RefLine,2}, _ξ::Vec{1}, i::Int)
     ξ = 0.5*(_ξ[1] + 1.0)
     i == 1 && return (1-ξ)^2
     i == 2 && return ξ^2
@@ -51,7 +51,7 @@ Ferrite.edgedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = ((5,), (6,),
 Ferrite.facedof_indices(::Bernstein{RefQuadrilateral,2}) = ((1,2,3,4,5,6,7,8,9),)
 Ferrite.facedof_interior_indices(::Bernstein{RefQuadrilateral,2}) = (9,)
 
-function Ferrite.shape_value(ip::Bernstein{RefQuadrilateral,2}, _ξ::Vec{2}, i::Int)
+function Ferrite.reference_shape_value(ip::Bernstein{RefQuadrilateral,2}, _ξ::Vec{2}, i::Int)
     ξ, η = _ξ
     i == 1 && return 0.0625((1 - η)^2)*((1 - ξ)^2)
     i == 2 && return 0.0625((1 + ξ)^2)*((1 - η)^2)
@@ -102,7 +102,7 @@ Ferrite.edgedof_interior_indices(::Bernstein{RefHexahedron,2}) = (
 )
 Ferrite.volumedof_interior_indices(::Bernstein{RefHexahedron,2}) = (27,)
 
-function Ferrite.shape_value(ip::Bernstein{RefHexahedron,2}, _ξ::Vec{3}, i::Int)
+function Ferrite.reference_shape_value(ip::Bernstein{RefHexahedron,2}, _ξ::Vec{3}, i::Int)
     ξ, η, ζ = _ξ
     i == 1 && return 0.015625((1 - ζ)^2)*((1 - η)^2)*((1 - ξ)^2)
     i == 2 && return 0.015625((1 + ξ)^2)*((1 - ζ)^2)*((1 - η)^2)
@@ -167,11 +167,11 @@ end
 
 =#
 
-function Ferrite.shape_value(ip::Bernstein{refshape,order}, _ξ::Vec{dim}, i::Int) where {dim,refshape<:Ferrite.AbstractRefShape{dim},order}
-    _compute_bezier_shape_value(ip,_ξ, i)
+function Ferrite.reference_shape_value(ip::Bernstein{refshape,order}, _ξ::Vec{dim}, i::Int) where {dim,refshape<:Ferrite.AbstractRefShape{dim},order}
+    _compute_bezier_reference_shape_value(ip,_ξ, i)
 end
 
-function _compute_bezier_shape_value(ip::Bernstein{shape,order}, ξ::Vec{dim,T}, i::Int) where {dim,shape<:Ferrite.AbstractRefShape{dim},order,T} 
+function _compute_bezier_reference_shape_value(ip::Bernstein{shape,order}, ξ::Vec{dim,T}, i::Int) where {dim,shape<:Ferrite.AbstractRefShape{dim},order,T} 
     _n = ntuple(i->order+1, dim)
     ordering = _bernstein_ordering(ip)
     basefunction_indeces = CartesianIndices(_n)[ordering[i]]
diff --git a/src/splines/bsplines.jl b/src/splines/bsplines.jl
index aa0c0ef..7c25a02 100644
--- a/src/splines/bsplines.jl
+++ b/src/splines/bsplines.jl
@@ -23,7 +23,7 @@ end
 getnbasefunctions_dim(basis::BSplineBasis{dim,T,order}) where {dim,T,order} = ntuple(i -> length(basis.knot_vector[i]) - order[i] - 1, dim)
 Ferrite.getnbasefunctions(basis::BSplineBasis{dim,T,order}) where {dim,T,order} = prod(getnbasefunctions_dim(basis))
 
-function Ferrite.shape_value(b::BSplineBasis{dim,T,order}, xi::Vec{dim,T2}, i::Int) where {dim,T,T2,order}
+function Ferrite.reference_shape_value(b::BSplineBasis{dim,T,order}, xi::Vec{dim,T2}, i::Int) where {dim,T,T2,order}
     @assert( i <= Ferrite.getnbasefunctions(b))
 
     _n = getnbasefunctions_dim(b)
diff --git a/test/test_bertstein.jl b/test/test_bertstein.jl
index 5bf239b..4ff1a7f 100644
--- a/test/test_bertstein.jl
+++ b/test/test_bertstein.jl
@@ -4,7 +4,7 @@
     #1d basis function should equal to 2d basis function on the boundary (-1.0)
     b1 = Bernstein{RefQuadrilateral, 2}()
     b2 = Bernstein{RefLine, 2}()
-    @test Ferrite.shape_value(b1, Vec((0.0,-1.0)), 5) == Ferrite.shape_value(b2, Vec((0.0)), 3)
+    @test Ferrite.reference_shape_value(b1, Vec((0.0,-1.0)), 5) == Ferrite.reference_shape_value(b2, Vec((0.0)), 3)
 
     #Sum of shape function should equal 1     
     for bip in (Bernstein{RefLine, 1}(),
@@ -22,7 +22,7 @@
         for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})]
             sum = 0.0
             for i in 1:Ferrite.getnbasefunctions(bip)
-                sum += Ferrite.shape_value(bip, xi, i)
+                sum += Ferrite.reference_shape_value(bip, xi, i)
             end
             @test sum ≈ 1.0
         end
@@ -34,7 +34,7 @@
         dim = Ferrite.getrefdim(bip)
         for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})]
             for i in  1:Ferrite.getnbasefunctions(bip)
-                N_hardcoded = Ferrite.shape_value(bip, xi, i)
+                N_hardcoded = Ferrite.reference_shape_value(bip, xi, i)
                 M_generated = IGA._compute_bezier_shape_value(bip, xi, i)
                 @test M_generated ≈ N_hardcoded
             end
diff --git a/test/test_bsplines.jl b/test/test_bsplines.jl
index 6756db2..18f47f5 100644
--- a/test/test_bsplines.jl
+++ b/test/test_bsplines.jl
@@ -60,8 +60,8 @@ end
 
         ξ = Vec((rand(),rand()))
         for i in 1:getnbasefunctions(ip2)
-            N1 = Ferrite.shape_value(ip1, ξ, reorder[i])
-            N2 = Ferrite.shape_value(ip2, ξ, i)
+            N1 = Ferrite.reference_shape_value(ip1, ξ, reorder[i])
+            N2 = Ferrite.reference_shape_value(ip2, ξ, i)
             @test N1 ≈ N2
         end
 

From b7f580e69706dc4f29768b23b9f2a8fe032ebc0c Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 08:51:04 +0200
Subject: [PATCH 41/50] wip

---
 src/IGA.jl                   |  2 +-
 src/splines/bezier_values.jl | 17 +++++++++--------
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/IGA.jl b/src/IGA.jl
index ab53b69..0658261 100644
--- a/src/IGA.jl
+++ b/src/IGA.jl
@@ -7,7 +7,7 @@ using Reexport
 using Ferrite: 
     AbstractRefShape, RefHypercube, RefLine, RefQuadrilateral, RefHexahedron, getnbasefunctions,
     VectorInterpolation, VectorizedInterpolation,
-    FunctionValues, GeometryMapping, MappingValues
+    FunctionValues, GeometryMapping, MappingValues, ValuesUpdateFlags
 
 using OrderedCollections:
     OrderedSet
diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl
index 6e84ab1..ea4b125 100644
--- a/src/splines/bezier_values.jl
+++ b/src/splines/bezier_values.jl
@@ -51,14 +51,10 @@ Ferrite.geometric_interpolation(cv::BezierCellValues) = Ferrite.geometric_interp
 Ferrite.function_interpolation(cv::BezierFacetValues) = Ferrite.function_interpolation(cv.bezier_values[1])
 Ferrite.geometric_interpolation(cv::BezierFacetValues) = Ferrite.geometric_interpolation(cv.geo_mapping[1])
 
-function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation; 
-    update_gradients::Bool = true, update_hessians = false, update_detJdV::Bool = true) where T 
+function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation, ::ValuesUpdateFlags{FunDiffOrder, GeoDiffOrder, DetJdV}) where {T, FunDiffOrder, GeoDiffOrder, DetJdV}
 
-    @assert update_detJdV == true
+    @assert DetJdV
 
-    FunDiffOrder  = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs
-    FunDiffOrder += convert(Int, update_hessians) # Logic must change when supporting update_hessian kwargs
-    GeoDiffOrder = max(Ferrite.required_geo_diff_order(Ferrite.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 = fill(T(NaN), getnquadpoints(qr))
@@ -73,10 +69,15 @@ function BezierCellValues(::Type{T}, qr::QuadratureRule, ip_fun::Interpolation,
         geo_mapping, qr, detJdV, undef_beo, undef_w)
 end
 
-BezierCellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) = BezierCellValues(Float64, qr, ip, args...; kwargs...)
-function BezierCellValues(::Type{T}, qr, ip::Interpolation; kwargs...) where T
+function BezierCellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) 
+    return BezierCellValues(Float64, qr, ip, args...; kwargs...)
+end
+function BezierCellValues(::Type{T}, qr, ip::ScalarInterpolation; kwargs...) where T
     return BezierCellValues(T, qr, ip, Ferrite.default_geometric_interpolation(ip); kwargs...)
 end
+function BezierCellValues(::Type{T}, qr, ip, ip_geo::VectorizedInterpolation = Ferrite.default_geometric_interpolation(ip); kwargs...) where T
+    return BezierCellValues(T, qr, ip, ip_geo, Ferrite.ValuesUpdateFlags(ip; kwargs...))
+end
 
 function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}; 
     update_hessians::Bool = false, update_gradients::Bool = true) where {T,sdim} 

From 98867cd2229e21d48572aa8f0c9990f06bb7200f Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 09:15:04 +0200
Subject: [PATCH 42/50] upgrade to the new way to use hessians in cellvalues

---
 src/splines/bezier_values.jl | 20 +++++++++++---------
 test/test_values.jl          | 13 +++++++++++++
 2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl
index ea4b125..56c5750 100644
--- a/src/splines/bezier_values.jl
+++ b/src/splines/bezier_values.jl
@@ -72,19 +72,15 @@ end
 function BezierCellValues(qr::QuadratureRule, ip::Interpolation, args...; kwargs...) 
     return BezierCellValues(Float64, qr, ip, args...; kwargs...)
 end
-function BezierCellValues(::Type{T}, qr, ip::ScalarInterpolation; kwargs...) where T
+function BezierCellValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation; kwargs...) where T
     return BezierCellValues(T, qr, ip, Ferrite.default_geometric_interpolation(ip); kwargs...)
 end
 function BezierCellValues(::Type{T}, qr, ip, ip_geo::VectorizedInterpolation = Ferrite.default_geometric_interpolation(ip); kwargs...) where T
     return BezierCellValues(T, qr, ip, ip_geo, Ferrite.ValuesUpdateFlags(ip; kwargs...))
 end
 
-function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}; 
-    update_hessians::Bool = false, update_gradients::Bool = true) where {T,sdim} 
-
-    FunDiffOrder  = convert(Int, update_gradients) # Logic must change when supporting update_hessian kwargs
-    FunDiffOrder += convert(Int, update_hessians) # Logic must change when supporting update_hessian kwargs
-    GeoDiffOrder = max(Ferrite.required_geo_diff_order(Ferrite.mapping_type(ip_fun), FunDiffOrder), 1)
+function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpolation, ip_geo::VectorizedInterpolation{sdim}, ::ValuesUpdateFlags{FunDiffOrder, GeoDiffOrder, DetJdV}) where {T, sdim, FunDiffOrder, GeoDiffOrder, DetJdV}
+    @assert DetJdV
     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(Ferrite.getweights(qr)), fqr.face_rules)
@@ -99,10 +95,16 @@ function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpol
         geo_mapping, fqr, detJdV, normals, undef_beo, undef_w, Ferrite.ScalarWrapper(-1))
 end
 
-BezierFacetValues(qr::FacetQuadratureRule, ip::Interpolation, args...; kwargs...) = BezierFacetValues(Float64, qr, ip, args...; kwargs...)
-function BezierFacetValues(::Type{T}, qr, ip::Interpolation; kwargs...) where T
+function BezierFacetValues(qr::FacetQuadratureRule, ip::Interpolation, args...; kwargs...) 
+    return BezierFacetValues(Float64, qr, ip, args...; kwargs...)
+end
+function BezierFacetValues(::Type{T}, qr, ip::Interpolation, ip_geo::ScalarInterpolation; kwargs...) where T
     return BezierFacetValues(T, qr, ip, Ferrite.default_geometric_interpolation(ip); kwargs...)
 end
+function BezierFacetValues(::Type{T}, qr, ip, ip_geo::VectorizedInterpolation = Ferrite.default_geometric_interpolation(ip); kwargs...) where T
+    return BezierFacetValues(T, qr, ip, ip_geo, Ferrite.ValuesUpdateFlags(ip; kwargs...))
+end
+
 
 #Intercept construction of CellValues called with IGAInterpolation
 function Ferrite.CellValues(
diff --git a/test/test_values.jl b/test/test_values.jl
index 3bacb7b..a573a8d 100644
--- a/test/test_values.jl
+++ b/test/test_values.jl
@@ -59,10 +59,23 @@ end
     qr = QuadratureRule{shape}(1)
     qr_face = FacetQuadratureRule{shape}(1)
 
+    #Cells
     cv  = BezierCellValues(qr, ip)
     cv  = BezierCellValues(qr, ip^2)
+    cv  = BezierCellValues(qr, ip^2, ip)
     cv  = BezierCellValues(qr, ip, ip^3)
     cv  = BezierCellValues(qr, ip^2, ip^3)
+    cv  = BezierCellValues(qr, ip; update_hessians=true)
+    cv  = BezierCellValues(qr, ip^3; update_hessians=true)
+
+    #Facets
+    cv  = BezierFacetValues(qr_face, ip)
+    cv  = BezierFacetValues(qr_face, ip^2)
+    cv  = BezierFacetValues(qr_face, ip^2, ip)
+    cv  = BezierFacetValues(qr_face, ip, ip^3)
+    cv  = BezierFacetValues(qr_face, ip^2, ip^3)
+    cv  = BezierFacetValues(qr_face, ip; update_hessians=true)
+    cv  = BezierFacetValues(qr_face, ip^3; update_hessians=true)
 
     #embedded
     ip = IGAInterpolation{RefQuadrilateral, 2}()

From 4efb605682f03031c06f72038586f7158b0002b1 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 09:29:40 +0200
Subject: [PATCH 43/50] fix name change that was missed

---
 test/test_bertstein.jl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/test_bertstein.jl b/test/test_bertstein.jl
index 4ff1a7f..7b72590 100644
--- a/test/test_bertstein.jl
+++ b/test/test_bertstein.jl
@@ -35,7 +35,7 @@
         for xi in [rand(Vec{dim,Float64}), rand(Vec{dim,Float64})]
             for i in  1:Ferrite.getnbasefunctions(bip)
                 N_hardcoded = Ferrite.reference_shape_value(bip, xi, i)
-                M_generated = IGA._compute_bezier_shape_value(bip, xi, i)
+                M_generated = IGA._compute_bezier_reference_shape_value(bip, xi, i)
                 @test M_generated ≈ N_hardcoded
             end
         end

From 034eae95483473cc39e397b65ed3f83105171521 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 09:30:07 +0200
Subject: [PATCH 44/50] devault vtk output to all cells

---
 src/VTK.jl | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/VTK.jl b/src/VTK.jl
index c3412e3..829a006 100644
--- a/src/VTK.jl
+++ b/src/VTK.jl
@@ -30,7 +30,7 @@ struct VTKIGAFile{VTK<:WriteVTK.DatasetFile}
 	cellset::Vector{Int}
 end
 
-function VTKIGAFile(filename::String, grid::BezierGrid, cellset; kwargs...)
+function VTKIGAFile(filename::String, grid::BezierGrid, cellset=1:getncells(grid); kwargs...)
     vtk = _create_iga_vtk_grid(filename, grid, cellset; kwargs...)
 	cellset = sort(collect(copy(cellset)))
     return VTKIGAFile(vtk, cellset)
@@ -131,14 +131,14 @@ function WriteVTK.vtk_point_data(
     return vtkfile
 end
 
-function #=Ferrite.=#write_solution(vtk::VTKIGAFile, dh::DofHandler, a, suffix="")
+function Ferrite.write_solution(vtk::VTKIGAFile, dh::DofHandler, a, suffix="")
 	for fieldname in Ferrite.getfieldnames(dh)
 		data = _evaluate_at_geometry_nodes!(vtk, dh, a, fieldname)
 		vtk_point_data(vtk.vtk, data, string(fieldname, suffix))
 	end
 end
 
-function #=Ferrite.=#write_projected(vtk::VTKIGAFile, proj::L2Projector, vals, name)
+function Ferrite.write_projection(vtk::VTKIGAFile, proj::L2Projector, vals, name)
     data = Ferrite._evaluate_at_grid_nodes(proj, vals, #=vtk=# Val(true))::Matrix
     @assert size(data, 2) == getnnodes(Ferrite.get_grid(proj.dh))
     vtk_point_data(vtk.vtk, data, name; component_names=Ferrite.component_names(eltype(vals)))

From cec9b538a40dba83a75208a0effa93296ad8fa37 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 09:30:30 +0200
Subject: [PATCH 45/50] update pate_with_hole example with ferrite master
 changes

---
 docs/src/literate/plate_with_hole.jl | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/docs/src/literate/plate_with_hole.jl b/docs/src/literate/plate_with_hole.jl
index 7c6c607..c841b09 100644
--- a/docs/src/literate/plate_with_hole.jl
+++ b/docs/src/literate/plate_with_hole.jl
@@ -53,7 +53,7 @@ end;
 function assemble_problem(dh::DofHandler, grid, cv, fv, stiffmat, traction)
 
     f = zeros(ndofs(dh))
-    K = create_sparsity_pattern(dh)##create_matrix(dh)
+    K = allocate_matrix(dh)
     assembler = start_assemble(K, f)
 
     n = getnbasefunctions(cv)
@@ -168,8 +168,8 @@ function solve()
     qr_cell = QuadratureRule{RefQuadrilateral}(4)
     qr_face = FacetQuadratureRule{RefQuadrilateral}(3)
 
-    cv = BezierCellValues(qr_cell, ip_u, ip_geo^2)
-    fv = BezierFacetValues(qr_face, ip_u, ip_geo^2)
+    cv = BezierCellValues(qr_cell, ip_u)
+    fv = BezierFacetValues(qr_face, ip_u)
 
     # Distribute dofs as normal
     dh = DofHandler(grid)
@@ -203,18 +203,13 @@ function solve()
 
     cellstresses = calculate_stress(dh, cv, stiffmat, u)
 
-    #csv = BezierCellValues( CellScalarValues(qr_cell, ip) )
-    projector = L2Projector(ip_u, grid)
-    σ_nodes = project(projector, cellstresses, qr_cell)
-    # Output results to VTK
-    #vtkgrid = vtk_grid("plate_with_hole.vtu", grid)
-    #vtk_point_data(vtkgrid, dh, u, :u)
-   # vtk_point_data(vtkgrid, σ_nodes, "sigma", grid)
-   # vtk_save(vtkgrid)
-
-    IGA.VTKIGAFile("plate_with_hole.vtu", grid, collect(1:getncells(grid))) do vtk
-        IGA.write_solution(vtk, dh, u)
-        #IGA.write_projected(vtk, projector, σ_nodes, "σ")
+    # L2 projections currently broken for IGA
+    # projector = L2Projector(ip_u, grid)
+    # σ_nodes = project(projector, cellstresses, qr_cell)
+
+    IGA.VTKIGAFile("plate_with_hole.vtu", grid) do vtk
+        write_solution(vtk, dh, u)
+        #IGA.write_projections(vtk, projector, σ_nodes, "σ")
     end
 
 end;

From 4cd4b452225bd47ea99d2bb15a0cf57b55f1a344 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 09:42:47 +0200
Subject: [PATCH 46/50] update readme with quick start

---
 README.md | 32 ++++++++++++++++++++++++++++----
 1 file changed, 28 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index e5c94a3..0d62f44 100644
--- a/README.md
+++ b/README.md
@@ -8,14 +8,38 @@ Small toolbox for Isogeometric analysis. Built on top of [Ferrite](https://githu
 
 ## Installation
 
+Currently only works on Ferrite master branch
+
 ```
-pkg> add https://github.com/lijas/IGA.jl.git
+Pkg.add(url="https://github.com/Ferrite-FEM/Ferrite.jl",rev="master")
+Pkg.add(url="https://github.com/lijas/IGA.jl",rev="master")
 ```
 
 [docs-dev-img]: https://img.shields.io/badge/docs-dev-blue.svg
 [docs-dev-url]: https://lijas.github.io/IGA.jl/dev/
 
-## About IGA
+## Quick start
+The API is similar to Ferrite.jl:
+
+```
+using Ferrite, IGA
+
+order = 2 # second order NURBS
+nels = (20,10) # Number of elements
+patch = generate_nurbs_patch(:plate_with_hole, nels, order) 
+
+#Convert nurbs patch to a Grid structure with bezier-extraction operators
+grid = BezierGrid(patch)
+
+#Create interpolation and shape values
+ip = IGAInterpolation{RefQuadrilateral,order}() #Bernstein polynomials
+qr_cell = QuadratureRule{RefQuadrilateral}(4)
+
+cv = BezierCellValues(qr_cell, ip, update_hessians=true)
+
+#...
+#update cell values
+coords::BezierCoords = getcoordinates(grid, 1)
+reinit!(cv, coords)
 
-From wikipedia: 
-*Isogeometric analysis is a computational approach that offers the possibility of integrating finite element analysis (FEA) into conventional NURBS-based CAD design tools. Currently, it is necessary to convert data between CAD and FEA packages to analyse new designs during development, a difficult task since the two computational geometric approaches are different. Isogeometric analysis employs complex NURBS geometry (the basis of most CAD packages) in the FEA application directly. This allows models to be designed, tested and adjusted in one go, using a common data set.*
+```
\ No newline at end of file

From 4bf03ce5925ab77c45deadf43a3257d51c8a7ae7 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 10:39:36 +0200
Subject: [PATCH 47/50] fix residual rebase error

---
 src/splines/bezier_values.jl | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl
index 56c5750..024351a 100644
--- a/src/splines/bezier_values.jl
+++ b/src/splines/bezier_values.jl
@@ -195,8 +195,6 @@ function Ferrite.spatial_coordinate(cv::Ferrite.AbstractValues, iqp::Int, (xb, w
     return x
 end
 
-Ferrite.getnormal(fv::BezierFaceValues, i::Int)= fv.cv_bezier.normals[i]
-
 #Function that computs basefunction values from bezier function values and the extraction operator, N = C*B
 function _cellvalues_bezier_extraction!(cv_nurbs::Ferrite.AbstractValues, cv_bezier::Ferrite.AbstractValues, Cbe::BezierExtractionOperator{T}, w::Optional{Vector{T}}, faceid::Int) where {T}
 

From a58b49f751c3e2440d3e7e606c73793f4dbb479d Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 10:46:26 +0200
Subject: [PATCH 48/50] Remove Ferrite.scalarwrapper

---
 src/iterators.jl             | 8 ++++----
 src/splines/bezier_values.jl | 4 ++--
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/iterators.jl b/src/iterators.jl
index 4ea191d..9067958 100644
--- a/src/iterators.jl
+++ b/src/iterators.jl
@@ -4,7 +4,7 @@ struct IGACellCache{X,G<:Ferrite.AbstractGrid,DH<:Union{Ferrite.AbstractDofHandl
     # Pretty useless to store this since you have it already for the reinit! call, but
     # needed for the CellIterator(...) workflow since the user doesn't necessarily control
     # the loop order in the cell subset.
-    cellid::Ferrite.ScalarWrapper{Int}
+    cellid::Base.RefValue{Int}
     nodes::Vector{Int}
     bezier_cell_data::X
     dh::DH
@@ -20,7 +20,7 @@ function IGACellCache(dh::DofHandler{dim,G}, flags::UpdateFlags=UpdateFlags()) w
     n        = ndofs_per_cell(dh, 1)
     celldofs = zeros(Int, n)
 
-    return IGACellCache(flags, grid, Ferrite.ScalarWrapper(-1), nodes, coords, dh, celldofs)
+    return IGACellCache(flags, grid, Ref(-1), nodes, coords, dh, celldofs)
 end
 
 function Ferrite.reinit!(cc::IGACellCache, i::Int)
@@ -52,12 +52,12 @@ Ferrite.cellid(cc::IGACellCache) = cc.cellid[]
 struct IGAFaceCache{CC}
     cc::CC  # const for julia > 1.8
     dofs::Vector{Int} # aliasing cc.dofs
-    current_faceid::Ferrite.ScalarWrapper{Int}
+    current_faceid::Base.RefValue{Int}
 end
 
 function IGAFaceCache(args...)
     cc = IGACellCache(args...)
-    IGAFaceCache(cc, cc.dofs, Ferrite.ScalarWrapper(0))
+    IGAFaceCache(cc, cc.dofs, Ref(-1))
 end
 
 function Ferrite.reinit!(fc::IGAFaceCache, face::Ferrite.BoundaryIndex)
diff --git a/src/splines/bezier_values.jl b/src/splines/bezier_values.jl
index 024351a..917cdf3 100644
--- a/src/splines/bezier_values.jl
+++ b/src/splines/bezier_values.jl
@@ -33,7 +33,7 @@ struct BezierFacetValues{FV, GM, FQR, dim, T, V_FV<:AbstractVector{FV}, V_GM<:Ab
 
     current_beo::Base.RefValue{BezierExtractionOperator{T}}
     current_w::Vector{T}
-    current_facet::Ferrite.ScalarWrapper{Int}
+    current_facet::Base.RefValue{Int}
 end
 
 Ferrite.shape_value_type(cv::BezierCellValues) = Ferrite.shape_value_type(cv.bezier_values)
@@ -92,7 +92,7 @@ function BezierFacetValues(::Type{T}, fqr::FacetQuadratureRule, ip_fun::Interpol
         fun_values, 
         deepcopy(fun_values), 
         deepcopy(fun_values), 
-        geo_mapping, fqr, detJdV, normals, undef_beo, undef_w, Ferrite.ScalarWrapper(-1))
+        geo_mapping, fqr, detJdV, normals, undef_beo, undef_w, Ref(-1))
 end
 
 function BezierFacetValues(qr::FacetQuadratureRule, ip::Interpolation, args...; kwargs...) 

From 42e23d70f04a65fcf4c72b157476594746760441 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 10:51:49 +0200
Subject: [PATCH 49/50] fix link in readme and remove trash file

---
 README.md      |  2 +-
 test_script.jl | 80 --------------------------------------------------
 2 files changed, 1 insertion(+), 81 deletions(-)
 delete mode 100644 test_script.jl

diff --git a/README.md b/README.md
index 0d62f44..083b118 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # IGA.jl
 
-Small toolbox for Isogeometric analysis. Built on top of [Ferrite](https://github.com/KristofferC/Ferrite.jl)
+Small toolbox for Isogeometric analysis. Built on top of [Ferrite](https://github.com/Ferrite-FEM/Ferrite.jl)
 
 ## Documentation
 
diff --git a/test_script.jl b/test_script.jl
deleted file mode 100644
index 5655d16..0000000
--- a/test_script.jl
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-using Ferrite
-using IGA
-
-function _get_face_bezier_coordinates!(xb::Vector{<:Vec{dim}}, x::Vector{<:Vec{dim}}, grid::BezierGrid, cell::Ferrite.AbstractCell, lfaceid::Int, cellid::Int) where dim
-    ip = Ferrite.default_interpolation(typeof(cell))
-    
-    facedofs = Ferrite.dirichlet_facedof_indices(ip)[lfaceid]
-    @assert length(x) == length(facedofs)
-    @assert length(x) == length(xb)
-
-    i = 0
-    for d in facedofs
-        i += 1
-        x[i] = grid.nodes[cell.nodes[d]].x
-    end
-
-    n = length(xb)
-	C = grid.beo[cellid]
-
-    for i in 1:n
-        xb[i] = zero(Vec{dim})
-    end
-
-    for i in 1:n
-        k = facedofs[i]
-		c_row = C[k]
-		_x = x[i]
-		for j in 1:n
-            dof = facedofs[j]
-            val = c_row[dof]              
-			xb[j] += val * _x
-		end
-	end
-
-end
-
-ip = IGA.IGAInterpolation{RefQuadrilateral,2}()
-ipface = IGA.IGAInterpolation{RefLine,2}()
-
-nurbsmesh = generate_nurbs_patch(:cube, (3,3), (2,2); size = (1.0, 1.0))
-grid = BezierGrid(nurbsmesh)
-
-dh = DofHandler(grid)
-add!(dh, :u, ip^2)
-close!(dh)
-
-cellid = 3
-lfaceid = 2
-cell = grid.cells[cellid]
-
-a = zeros(ndofs(dh)) .+1
-
-vtk = VTKIGAFile("newoutput", grid, 1:getncells(grid))
-IGA.write_solution(vtk, dh, a)
-close(vtk)
-
-n = 3
-xb = zeros(Vec{2}, n)
-x = zeros(Vec{2}, n)
-
-N = zeros(n)
-dNdξ = zeros(Vec{1}, n)
-ξ = zero(Vec{1})
-for i in 1:n
-    dNdξ[i], N[i] = Ferrite.shape_gradient_and_value(ipface, ξ, i)
-end
-
-_get_face_bezier_coordinates!(xb, x, grid, cell, lfaceid, cellid)
-
-dxdxi = zero(Vec{2})
-for i in 1:n
-    dxdxi += dNdξ[i][1] * xb[i]
-end
-
-_normal(dxdxi) = Vec((dxdxi[2], -dxdxi[1]))/norm( Vec((dxdxi[2], -dxdxi[1])))
-
-@show _normal(dxdxi)
-

From 816d1c170c41d2fab7d7d6028ec295bf183341a9 Mon Sep 17 00:00:00 2001
From: lijas 
Date: Mon, 1 Jul 2024 11:39:45 +0200
Subject: [PATCH 50/50] fix docs build (use ferrite master and julia 1.9)

---
 .github/workflows/docs.yml |   2 +-
 docs/Manifest.toml         | 297 ++++++++++++++++++++++---------------
 2 files changed, 178 insertions(+), 121 deletions(-)

diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index bd7f653..7d896d9 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -14,7 +14,7 @@ jobs:
       - uses: actions/checkout@v2
       - uses: julia-actions/setup-julia@v1
         with:
-          version: 1.7
+          version: 1.9
       - name: Install dependencies
         run: julia --project=docs -e 'using Pkg; Pkg.instantiate()'
       - name: Build and deploy
diff --git a/docs/Manifest.toml b/docs/Manifest.toml
index 329e0aa..ddb371f 100644
--- a/docs/Manifest.toml
+++ b/docs/Manifest.toml
@@ -10,23 +10,11 @@ uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
 [[Base64]]
 uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
 
-[[ChainRulesCore]]
-deps = ["Compat", "LinearAlgebra", "SparseArrays"]
-git-tree-sha1 = "c6d890a52d2c4d55d326439580c3b8d0875a77d9"
-uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
-version = "1.15.7"
-
-[[ChangesOfVariables]]
-deps = ["ChainRulesCore", "LinearAlgebra", "Test"]
-git-tree-sha1 = "844b061c104c408b24537482469400af6075aae4"
-uuid = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
-version = "0.1.5"
-
 [[CodecZlib]]
 deps = ["TranscodingStreams", "Zlib_jll"]
-git-tree-sha1 = "9c209fb7536406834aa938fb149964b985de6c83"
+git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73"
 uuid = "944b1d66-785c-5afd-91f1-9de20f533193"
-version = "0.7.1"
+version = "0.7.4"
 
 [[CommonSubexpressions]]
 deps = ["MacroTools", "Test"]
@@ -34,16 +22,10 @@ git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7"
 uuid = "bbf7d656-a473-5ed7-a52c-81e309532950"
 version = "0.3.0"
 
-[[Compat]]
-deps = ["Dates", "LinearAlgebra", "UUIDs"]
-git-tree-sha1 = "00a2cccc7f098ff3b66806862d275ca3db9e6e5a"
-uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
-version = "4.5.0"
-
 [[CompilerSupportLibraries_jll]]
 deps = ["Artifacts", "Libdl"]
 uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
-version = "0.5.2+0"
+version = "1.1.0+0"
 
 [[Dates]]
 deps = ["Printf"]
@@ -57,15 +39,23 @@ version = "1.1.0"
 
 [[DiffRules]]
 deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"]
-git-tree-sha1 = "c5b6685d53f933c11404a3ae9822afe30d522494"
+git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272"
 uuid = "b552c78f-8df3-52c6-915a-8e097449b14b"
-version = "1.12.2"
+version = "1.15.1"
 
 [[Distances]]
-deps = ["LinearAlgebra", "SparseArrays", "Statistics", "StatsAPI"]
-git-tree-sha1 = "3258d0659f812acde79e8a74b11f17ac06d0ca04"
+deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
+git-tree-sha1 = "66c4c81f259586e8f002eacebc177e1fb06363b0"
 uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
-version = "0.10.7"
+version = "0.10.11"
+
+    [Distances.extensions]
+    DistancesChainRulesCoreExt = "ChainRulesCore"
+    DistancesSparseArraysExt = "SparseArrays"
+
+    [Distances.weakdeps]
+    ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
+    SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
 
 [[DocStringExtensions]]
 deps = ["LibGit2"]
@@ -90,31 +80,55 @@ uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
 version = "1.0.4"
 
 [[Ferrite]]
-deps = ["EnumX", "LinearAlgebra", "NearestNeighbors", "Preferences", "Reexport", "SparseArrays", "Tensors", "WriteVTK"]
-git-tree-sha1 = "19787ed1e790736e6bffc34c07289f63fbb19e1a"
+deps = ["EnumX", "ForwardDiff", "LinearAlgebra", "NearestNeighbors", "OrderedCollections", "Preferences", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"]
+git-tree-sha1 = "d1320a09bd35eee5e8af12d084891c7696044ff9"
+repo-rev = "master"
+repo-url = "https://github.com/Ferrite-FEM/Ferrite.jl.git"
 uuid = "c061ca5d-56c9-439f-9c0e-210fe06d3992"
-version = "0.3.11"
+version = "0.3.14"
+
+    [Ferrite.extensions]
+    FerriteBlockArrays = "BlockArrays"
+    FerriteMetis = "Metis"
+
+    [Ferrite.weakdeps]
+    BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e"
+    Metis = "2679e427-3c69-5b7f-982b-ece356f1e94b"
 
 [[FileWatching]]
 uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
 
 [[FillArrays]]
-deps = ["LinearAlgebra", "Random", "SparseArrays", "Statistics"]
-git-tree-sha1 = "d3ba08ab64bdfd27234d3f61956c966266757fe6"
+deps = ["LinearAlgebra"]
+git-tree-sha1 = "0653c0a2396a6da5bc4766c43041ef5fd3efbe57"
 uuid = "1a297f60-69ca-5386-bcde-b61e274b549b"
-version = "0.13.7"
+version = "1.11.0"
+
+    [FillArrays.extensions]
+    FillArraysPDMatsExt = "PDMats"
+    FillArraysSparseArraysExt = "SparseArrays"
+    FillArraysStatisticsExt = "Statistics"
+
+    [FillArrays.weakdeps]
+    PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150"
+    SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
+    Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
 
 [[ForwardDiff]]
-deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions", "StaticArrays"]
-git-tree-sha1 = "a69dd6db8a809f78846ff259298678f0d6212180"
+deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"]
+git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad"
 uuid = "f6369f11-7733-5829-9624-2563aa707210"
-version = "0.10.34"
+version = "0.10.36"
+weakdeps = ["StaticArrays"]
+
+    [ForwardDiff.extensions]
+    ForwardDiffStaticArraysExt = "StaticArrays"
 
 [[IGA]]
-deps = ["Ferrite", "LinearAlgebra", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"]
+deps = ["Ferrite", "InteractiveUtils", "LinearAlgebra", "OrderedCollections", "Reexport", "SparseArrays", "StaticArrays", "Tensors", "WriteVTK"]
 path = ".."
 uuid = "e7b8d123-e02a-40ba-ad18-d3943ed54f1c"
-version = "0.2.3"
+version = "0.2.6"
 
 [[IOCapture]]
 deps = ["Logging"]
@@ -126,65 +140,64 @@ version = "0.1.1"
 deps = ["Markdown"]
 uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
 
-[[InverseFunctions]]
-deps = ["Test"]
-git-tree-sha1 = "49510dfcb407e572524ba94aeae2fced1f3feb0f"
-uuid = "3587e190-3f89-42d0-90ee-14403ec27112"
-version = "0.1.8"
-
 [[IrrationalConstants]]
-git-tree-sha1 = "7fd44fd4ff43fc60815f8e764c0f352b83c49151"
+git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2"
 uuid = "92d709cd-6900-40b7-9082-c6be49f344b6"
-version = "0.1.1"
+version = "0.2.2"
 
 [[JLLWrappers]]
-deps = ["Preferences"]
-git-tree-sha1 = "abc9885a7ca2052a736a600f7fa66209f96506e1"
+deps = ["Artifacts", "Preferences"]
+git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca"
 uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
-version = "1.4.1"
+version = "1.5.0"
 
 [[JSON]]
 deps = ["Dates", "Mmap", "Parsers", "Unicode"]
-git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e"
+git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a"
 uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
-version = "0.21.3"
+version = "0.21.4"
 
 [[LibCURL]]
 deps = ["LibCURL_jll", "MozillaCACerts_jll"]
 uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
-version = "0.6.3"
+version = "0.6.4"
 
 [[LibCURL_jll]]
 deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
 uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
-version = "7.84.0+0"
+version = "8.4.0+0"
 
 [[LibGit2]]
-deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
+deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"]
 uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
 
+[[LibGit2_jll]]
+deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"]
+uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5"
+version = "1.6.4+0"
+
 [[LibSSH2_jll]]
 deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
 uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
-version = "1.10.2+0"
+version = "1.11.0+1"
 
 [[Libdl]]
 uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
 
 [[Libiconv_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
-git-tree-sha1 = "c7cb1f5d892775ba13767a87c7ada0b980ea0a71"
+deps = ["Artifacts", "JLLWrappers", "Libdl"]
+git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175"
 uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
-version = "1.16.1+2"
+version = "1.17.0+0"
 
 [[LightXML]]
 deps = ["Libdl", "XML2_jll"]
-git-tree-sha1 = "e129d9391168c677cd4800f5c0abb1ed8cb3794f"
+git-tree-sha1 = "3a994404d3f6709610701c7dabfc03fed87a81f8"
 uuid = "9c8b4983-aa76-5018-a973-4c85ecc9e179"
-version = "0.9.0"
+version = "0.9.1"
 
 [[LinearAlgebra]]
-deps = ["Libdl", "libblastrampoline_jll"]
+deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"]
 uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
 
 [[Literate]]
@@ -194,19 +207,29 @@ uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
 version = "2.7.0"
 
 [[LogExpFunctions]]
-deps = ["ChainRulesCore", "ChangesOfVariables", "DocStringExtensions", "InverseFunctions", "IrrationalConstants", "LinearAlgebra"]
-git-tree-sha1 = "45b288af6956e67e621c5cbb2d75a261ab58300b"
+deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"]
+git-tree-sha1 = "a2d09619db4e765091ee5c6ffe8872849de0feea"
 uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
-version = "0.3.20"
+version = "0.3.28"
+
+    [LogExpFunctions.extensions]
+    LogExpFunctionsChainRulesCoreExt = "ChainRulesCore"
+    LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables"
+    LogExpFunctionsInverseFunctionsExt = "InverseFunctions"
+
+    [LogExpFunctions.weakdeps]
+    ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
+    ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0"
+    InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112"
 
 [[Logging]]
 uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
 
 [[MacroTools]]
 deps = ["Markdown", "Random"]
-git-tree-sha1 = "42324d08725e200c23d4dfb549e0d5d89dede2d2"
+git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df"
 uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
-version = "0.5.10"
+version = "0.5.13"
 
 [[Markdown]]
 deps = ["Base64"]
@@ -215,26 +238,26 @@ uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
 [[MbedTLS_jll]]
 deps = ["Artifacts", "Libdl"]
 uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
-version = "2.28.0+0"
+version = "2.28.2+1"
 
 [[Mmap]]
 uuid = "a63ad114-7e13-5084-954f-fe012c677804"
 
 [[MozillaCACerts_jll]]
 uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
-version = "2022.2.1"
+version = "2023.1.10"
 
 [[NaNMath]]
 deps = ["OpenLibm_jll"]
-git-tree-sha1 = "a7c3d1da1189a1c2fe843a3bfa04d18d20eb3211"
+git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4"
 uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
-version = "1.0.1"
+version = "1.0.2"
 
 [[NearestNeighbors]]
 deps = ["Distances", "StaticArrays"]
-git-tree-sha1 = "2c3726ceb3388917602169bed973dbc97f1b51a8"
+git-tree-sha1 = "91a67b4d73842da90b526011fa85c5c4c9343fe0"
 uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
-version = "0.4.13"
+version = "0.4.18"
 
 [[NetworkOptions]]
 uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
@@ -243,12 +266,12 @@ version = "1.2.0"
 [[OpenBLAS_jll]]
 deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
 uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"
-version = "0.3.20+0"
+version = "0.3.23+4"
 
 [[OpenLibm_jll]]
 deps = ["Artifacts", "Libdl"]
 uuid = "05823500-19ac-5b8b-9628-191a04bc5112"
-version = "0.8.1+0"
+version = "0.8.1+2"
 
 [[OpenSpecFun_jll]]
 deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
@@ -256,22 +279,33 @@ git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1"
 uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
 version = "0.5.5+0"
 
+[[OrderedCollections]]
+git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5"
+uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
+version = "1.6.3"
+
 [[Parsers]]
-deps = ["Dates", "SnoopPrecompile"]
-git-tree-sha1 = "8175fc2b118a3755113c8e68084dc1a9e63c61ee"
+deps = ["Dates", "PrecompileTools", "UUIDs"]
+git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821"
 uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
-version = "2.5.3"
+version = "2.8.1"
 
 [[Pkg]]
-deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
+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.8.0"
+version = "1.10.0"
+
+[[PrecompileTools]]
+deps = ["Preferences"]
+git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f"
+uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
+version = "1.2.1"
 
 [[Preferences]]
 deps = ["TOML"]
-git-tree-sha1 = "47e5f437cc0e7ef2ce8406ce1e7e24d44915f88d"
+git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6"
 uuid = "21216c6a-2e73-6563-6e65-726566657250"
-version = "1.3.0"
+version = "1.4.3"
 
 [[Printf]]
 deps = ["Unicode"]
@@ -282,7 +316,7 @@ deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
 uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
 
 [[Random]]
-deps = ["SHA", "Serialization"]
+deps = ["SHA"]
 uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
 
 [[Reexport]]
@@ -295,79 +329,97 @@ uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
 version = "0.7.0"
 
 [[SIMD]]
-deps = ["SnoopPrecompile"]
-git-tree-sha1 = "8b20084a97b004588125caebf418d8cab9e393d1"
+deps = ["PrecompileTools"]
+git-tree-sha1 = "2803cab51702db743f3fda07dd1745aadfbf43bd"
 uuid = "fdea26ae-647d-5447-a871-4b548cad5224"
-version = "3.4.4"
+version = "3.5.0"
 
 [[Serialization]]
 uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
 
-[[SnoopPrecompile]]
-deps = ["Preferences"]
-git-tree-sha1 = "e760a70afdcd461cf01a575947738d359234665c"
-uuid = "66db9d55-30c0-4569-8b51-7e840670fc0c"
-version = "1.0.3"
-
 [[Sockets]]
 uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
 
 [[SparseArrays]]
-deps = ["LinearAlgebra", "Random"]
+deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"]
 uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
+version = "1.10.0"
 
 [[SpecialFunctions]]
-deps = ["ChainRulesCore", "IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"]
-git-tree-sha1 = "d75bda01f8c31ebb72df80a46c88b25d1c79c56d"
+deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"]
+git-tree-sha1 = "2f5d4697f21388cbe1ff299430dd169ef97d7e14"
 uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
-version = "2.1.7"
+version = "2.4.0"
+
+    [SpecialFunctions.extensions]
+    SpecialFunctionsChainRulesCoreExt = "ChainRulesCore"
+
+    [SpecialFunctions.weakdeps]
+    ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
 
 [[StaticArrays]]
-deps = ["LinearAlgebra", "Random", "StaticArraysCore", "Statistics"]
-git-tree-sha1 = "6954a456979f23d05085727adb17c4551c19ecd1"
+deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"]
+git-tree-sha1 = "20833c5b7f7edf0e5026f23db7f268e4f23ec577"
 uuid = "90137ffa-7385-5640-81b9-e52037218182"
-version = "1.5.12"
+version = "1.9.6"
+
+    [StaticArrays.extensions]
+    StaticArraysChainRulesCoreExt = "ChainRulesCore"
+    StaticArraysStatisticsExt = "Statistics"
+
+    [StaticArrays.weakdeps]
+    ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
+    Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
 
 [[StaticArraysCore]]
-git-tree-sha1 = "6b7ba252635a5eff6a0b0664a41ee140a1c9e72a"
+git-tree-sha1 = "192954ef1208c7019899fbf8049e717f92959682"
 uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
-version = "1.4.0"
+version = "1.4.3"
 
 [[Statistics]]
 deps = ["LinearAlgebra", "SparseArrays"]
 uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
+version = "1.10.0"
 
 [[StatsAPI]]
 deps = ["LinearAlgebra"]
-git-tree-sha1 = "f9af7f195fb13589dd2e2d57fdb401717d2eb1f6"
+git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed"
 uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
-version = "1.5.0"
+version = "1.7.0"
+
+[[SuiteSparse_jll]]
+deps = ["Artifacts", "Libdl", "libblastrampoline_jll"]
+uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c"
+version = "7.2.1+1"
 
 [[TOML]]
 deps = ["Dates"]
 uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
-version = "1.0.0"
+version = "1.0.3"
 
 [[Tar]]
 deps = ["ArgTools", "SHA"]
 uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
-version = "1.10.1"
+version = "1.10.0"
 
 [[Tensors]]
-deps = ["ForwardDiff", "LinearAlgebra", "SIMD", "SnoopPrecompile", "StaticArrays", "Statistics"]
-git-tree-sha1 = "71f054343e85ab1eab12bf8336004309002ff82d"
+deps = ["ForwardDiff", "LinearAlgebra", "PrecompileTools", "SIMD", "StaticArrays", "Statistics"]
+git-tree-sha1 = "957f256fb380cad64cae4da39e562ecfb5c3fec9"
 uuid = "48a634ad-e948-5137-8d70-aa71f2a747f4"
-version = "1.13.1"
+version = "1.16.1"
 
 [[Test]]
 deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
 uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
 
 [[TranscodingStreams]]
-deps = ["Random", "Test"]
-git-tree-sha1 = "94f38103c984f89cf77c402f2a68dbd870f8165f"
+git-tree-sha1 = "d73336d81cafdc277ff45558bb7eaa2b04a8e472"
 uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa"
-version = "0.9.11"
+version = "0.10.10"
+weakdeps = ["Random", "Test"]
+
+    [TranscodingStreams.extensions]
+    TestExt = ["Test", "Random"]
 
 [[UUIDs]]
 deps = ["Random", "SHA"]
@@ -376,34 +428,39 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
 [[Unicode]]
 uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
 
+[[VTKBase]]
+git-tree-sha1 = "c2d0db3ef09f1942d08ea455a9e252594be5f3b6"
+uuid = "4004b06d-e244-455f-a6ce-a5f9919cc534"
+version = "1.0.1"
+
 [[WriteVTK]]
-deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams"]
-git-tree-sha1 = "f1b5fce1c4849bc49212c5f471284abf11c57eec"
+deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"]
+git-tree-sha1 = "48b9e8e9c83865e99e57f027d4edfa94e0acddae"
 uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192"
-version = "1.17.0"
+version = "1.19.1"
 
 [[XML2_jll]]
-deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"]
-git-tree-sha1 = "93c41695bc1c08c46c5899f4fe06d6ead504bb73"
+deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"]
+git-tree-sha1 = "d9717ce3518dc68a99e6b96300813760d887a01d"
 uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
-version = "2.10.3+0"
+version = "2.13.1+0"
 
 [[Zlib_jll]]
 deps = ["Libdl"]
 uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
-version = "1.2.12+3"
+version = "1.2.13+1"
 
 [[libblastrampoline_jll]]
-deps = ["Artifacts", "Libdl", "OpenBLAS_jll"]
+deps = ["Artifacts", "Libdl"]
 uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
-version = "5.1.1+0"
+version = "5.8.0+1"
 
 [[nghttp2_jll]]
 deps = ["Artifacts", "Libdl"]
 uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
-version = "1.48.0+0"
+version = "1.52.0+1"
 
 [[p7zip_jll]]
 deps = ["Artifacts", "Libdl"]
 uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
-version = "17.4.0+0"
+version = "17.4.0+2"