From c971a9880ad12ad64c43668e35bb25be36652b4d Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Tue, 23 Jan 2024 18:05:28 +0100 Subject: [PATCH 01/34] Starting to get rid of Object --- src/GalerkinToolkit.jl | 316 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 290 insertions(+), 26 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 15fc6f89..f346bc83 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -10,6 +10,270 @@ using PartitionedArrays using Combinatorics using SparseArrays +struct Geometry{A,B,C,D} + num_dims::A + is_axis_aligned::Bool + is_n_cube::Bool + is_simplex::Bool + bounding_box::B + boundary::C + vertex_permutations::D +end +function Geometry(; + num_dims, + is_axis_aligned, + is_n_cube, + is_simplex, + bounding_box, + boundary=nothing, + vertex_permutations=nothing) + + Geometry( + num_dims, + is_axis_aligned, + is_n_cube, + is_simplex, + bounding_box, + boundary, + vertex_permutations) +end +function setproperties( + geom::Geometry; + boundary=nothing, + vertex_permutations=nothing) + + Geometry( + geom.num_dims, + geom.is_axis_aligned, + geom.is_n_cube, + geom.is_simplex, + geom.bounding_box, + boundary === nothing ? geom.boundary : boundary, + vertex_permutations == nothing ? geom.vertex_permutations : vertex_permutations) +end +function Base.show(io::IO,data::Geometry) + print(io,"GalerkinToolkit.Geometry(…)") +end + +struct Mesh + num_dims + node_coordinates + face_nodes + face_reference_id + reference_faces + physical_groups + periodic_nodes + topology + outwards_normals +end +function Mesh(; + num_dims, + node_coordinates, + face_nodes, + face_reference_id, + reference_faces, + physical_groups = nothing, + periodic_nodes = nothing, + topology = nothing, + outwards_normals = nothing, + ) + Mesh( + num_dims, + node_coordinates, + face_nodes, + face_reference_id, + reference_faces, + physical_groups, + periodic_nodes, + topology, + outwards_normals, + ) +end +function setproperties(m::Mesh; + physical_groups = nothing, + periodic_nodes = nothing, + topology = nothing, + outwards_normals = nothing, + ) + Mesh( + m.num_dims, + m.node_coordinates, + m.face_nodes, + m.face_reference_id, + m.reference_faces, + physical_groups === nothing ? m.physical_groups : physical_groups, + periodic_nodes === nothing ? m.periodic_nodes : periodic_nodes, + topology === nothing ? m.topology : topology, + outwards_normals === nothing ? m.outwards_normals : outwards_normals, + ) +end +function Base.show(io::IO,data::Mesh) + print(io,"GalerkinToolkit.Mesh(…)") +end + +struct Chain + num_dims + node_coordinates + face_nodes + face_reference_id + reference_faces + physical_groups + periodic_nodes +end +function Chain(; + num_dims, + node_coordinates, + face_nodes, + face_reference_id, + reference_faces, + physical_groups = nothing, + periodic_nodes = nothing, + ) + Chain( + num_dims, + node_coordinates, + face_nodes, + face_reference_id, + reference_faces, + physical_groups, + periodic_nodes, + ) +end +function setproperties(c::Chain; + physical_groups = nothing, + periodic_nodes = nothing) + Chain( + c.num_dims, + c.node_coordinates, + c.face_nodes, + c.face_reference_id, + c.reference_faces, + physical_groups === nothing ? c.physical_groups : physical_groups, + periodic_nodes === nothing ? c.periodic_nodes : periodic_nodes, + ) +end +function Base.show(io::IO,data::Chain) + print(io,"GalerkinToolkit.Chain(…)") +end + +struct Topology + face_incidence + face_reference_id + reference_faces + face_permutation_ids +end +function Topology(; + face_incidence, + face_reference_id, + reference_faces, + face_permutation_ids) + Topology( + face_incidence, + face_reference_id, + reference_faces, + face_permutation_ids) +end +function setproperties(t::Topology) + t +end +function Base.show(io::IO,data::Topology) + print(io,"GalerkinToolkit.Topology(…)") +end + +# TODO this is growing too big +struct Face + geometry + shape_functions + dofs + node_coordinates + order + order_per_dir + monomial_exponents + lib_to_user_nodes + boundary + interior_nodes + vtk_mesh_cell + num_dofs + node_to_dofs + dof_to_node + dof_to_index + face_own_dofs + face_own_dof_permutations +end +function Face(; + geometry, + shape_functions, + dofs, + node_coordinates, + order, + order_per_dir, + monomial_exponents, + lib_to_user_nodes, + boundary=nothing, + interior_nodes=nothing, + vtk_mesh_cell=nothing, + num_dofs=nothing, + node_to_dofs=nothing, + dof_to_node=nothing, + dof_to_index=nothing, + face_own_dofs=nothing, + face_own_dof_permutations=nothing) + Face( + geometry, + shape_functions, + dofs, + node_coordinates, + order, + order_per_dir, + monomial_exponents, + lib_to_user_nodes, + boundary, + interior_nodes, + vtk_mesh_cell, + num_dofs, + node_to_dofs, + dof_to_node, + dof_to_index, + face_own_dofs, + face_own_dof_permutations,) +end +function setproperties(f::Face; + shape_functions=nothing, + dofs=nothing, + boundary=nothing, + interior_nodes=nothing, + vtk_mesh_cell=nothing, + num_dofs=nothing, + node_to_dofs=nothing, + dof_to_node=nothing, + dof_to_index=nothing, + face_own_dofs=nothing, + face_own_dof_permutations=nothing + ) + Face( + f.geometry, + shape_functions === nothing ? f.shape_functions : shape_functions, + dofs === nothing ? f.dofs : dofs, + f.node_coordinates, + f.order, + f.order_per_dir, + f.monomial_exponents, + f.lib_to_user_nodes, + boundary === nothing ? f.boundary : boundary, + interior_nodes === nothing ? f.interior_nodes : interior_nodes, + vtk_mesh_cell === nothing ? f.vtk_mesh_cell : vtk_mesh_cell, + num_dofs === nothing ? f.num_dofs : num_dofs, + node_to_dofs === nothing ? f.node_to_dofs : node_to_dofs, + dof_to_node === nothing ? f.dof_to_node : dof_to_node, + dof_to_index === nothing ? f.dof_to_index : dof_to_index, + face_own_dofs === nothing ? f.face_own_dofs : face_own_dofs, + face_own_dof_permutations === nothing ? f.face_own_dof_permutations : face_own_dof_permutations + ) +end +function Base.show(io::IO,data::Face) + print(io,"GalerkinToolkit.Face(…)") +end + # Inspited by PropertyUtils.jl # Use a dict directly instead? (provably not, we would not be type stable even if we wanted,really?) struct Object @@ -57,9 +321,9 @@ face_incidence(a) = a.face_incidence face_reference_id(a) = a.face_reference_id vtk_mesh_cell(a) = a.vtk_mesh_cell physical_groups(a) = a.physical_groups -has_physical_groups(a) = hasproperty(a,:physical_groups) +has_physical_groups(a) = hasproperty(a,:physical_groups) && a.physical_groups !== nothing periodic_nodes(a) = a.periodic_nodes -has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) +has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing geometry(a) = a.geometry topology(a) = a.topology boundary(a) = a.boundary @@ -147,7 +411,7 @@ function mesh_from_reference_face(ref_face;physical_groups=Val(true)) face_to_refid = push(face_reference_id(boundary_mesh),[1]) refid_refface = push(reference_faces(boundary_mesh),[ref_face]) node_to_coords = node_coordinates(ref_face) - mesh = Object(; + mesh = Mesh(; num_dims=Val(D), node_coordinates=node_to_coords, face_nodes=face_to_nodes, @@ -537,7 +801,7 @@ function reference_face_boundary_from_reference_face(refface) end face_nodes_inter[d+1] = face_nodes_inter_d end - mesh_inter = Object(; + mesh_inter = Mesh(; num_dims = Val(D-1), node_coordinates=node_coordinates_inter, face_nodes=face_nodes_inter, @@ -594,7 +858,7 @@ function unit_n_cube(D;kwargs...) bounding_box=SVector{d,Float64}[ntuple(i->0,Val(d)),ntuple(i->1,Val(d))] is_simplex = d in (0,1) boundary = unit_n_cube_boundary(D;kwargs...) - geometry = Object(;num_dims,is_n_cube,is_simplex,is_axis_aligned,bounding_box,boundary) + geometry = Geometry(;num_dims,is_n_cube,is_simplex,is_axis_aligned,bounding_box,boundary) vertex_permutations = vertex_permutations_from_geometry(geometry) setproperties(geometry;vertex_permutations) end @@ -607,7 +871,7 @@ function unit_simplex(D;kwargs...) is_n_cube = d in (0,1) bounding_box=SVector{d,Float64}[ntuple(i->0,Val(d)),ntuple(i->1,Val(d))] boundary = unit_simplex_boundary(D;kwargs...) - geometry = Object(;num_dims,is_n_cube,is_simplex,is_axis_aligned,bounding_box,boundary) + geometry = Geometry(;num_dims,is_n_cube,is_simplex,is_axis_aligned,bounding_box,boundary) vertex_permutations = vertex_permutations_from_geometry(geometry) setproperties(geometry;vertex_permutations) end @@ -632,7 +896,7 @@ function unit_n_cube_boundary( face_reference_id = [[1,1]] vertex = reference_face my_reference_faces = ([vertex],) - Object(;num_dims=Val(0),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces) + Mesh(;num_dims=Val(0),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces) elseif d == 2 node_coordinates = SVector{2,Float64}[(0,0),(1,0),(0,1),(1,1)] face_nodes = [[[1],[2],[3],[4]],[[1,2],[3,4],[1,3],[2,4]]] @@ -641,7 +905,7 @@ function unit_n_cube_boundary( vertex = first(reference_faces(boundary(geometry(segment)),0)) my_reference_faces = ([vertex],[segment]) outwards_normals = SVector{2,Float64}[(0,-1),(0,1),(-1,0),(1,0)] - Object(;num_dims=Val(1),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) + Mesh(;num_dims=Val(1),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) elseif d == 3 node_coordinates = SVector{3,Float64}[(0,0,0),(1,0,0),(0,1,0),(1,1,0),(0,0,1),(1,0,1),(0,1,1),(1,1,1)] face_nodes = [ @@ -655,7 +919,7 @@ function unit_n_cube_boundary( vertex = first(reference_faces(boundary(geometry(segment)),0)) my_reference_faces = ([vertex],[segment],[quad]) outwards_normals = SVector{3,Float64}[(0,0,-1),(0,0,1),(0,-1,0),(0,1,0),(-1,0,0),(1,0,0)] - Object(;num_dims=Val(2),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) + Mesh(;num_dims=Val(2),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) else @error "Case not implemented" end @@ -682,7 +946,7 @@ function unit_simplex_boundary( face_reference_id = [[1,1]] vertex = reference_face my_reference_faces = ([vertex],) - Object(;num_dims=Val(0),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces) + Mesh(;num_dims=Val(0),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces) elseif d == 2 node_coordinates = SVector{2,Float64}[(0,0),(1,0),(0,1)] face_nodes = [[[1],[2],[3]],[[1,2],[1,3],[2,3]]] @@ -692,7 +956,7 @@ function unit_simplex_boundary( my_reference_faces = ([vertex],[segment]) n1 = sqrt(2)/2 outwards_normals = SVector{2,Float64}[(0,-1),(-1,0),(n1,n1)] - Object(;num_dims=Val(1),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) + Mesh(;num_dims=Val(1),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) elseif d == 3 node_coordinates = SVector{3,Float64}[(0,0,0),(1,0,0),(0,1,0),(0,0,1)] face_nodes = [ @@ -707,7 +971,7 @@ function unit_simplex_boundary( my_reference_faces = ([vertex],[segment],[tri]) n1 = sqrt(3)/3 outwards_normals = SVector{3,Float64}[(0,0,-1),(0,-1,0),(-1,0,0),(n1,n1,n1)] - Object(;num_dims=Val(2),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) + Mesh(;num_dims=Val(2),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) else error("case not implemented") end @@ -858,7 +1122,7 @@ function simplexify_unit_n_cube(geo) ncells = length(cell_nodes) cell_reference_id = fill(Int8(1),ncells) reference_cells = [ref_cell] - chain = (; + chain = Chain(; num_dims=Val(D), node_coordinates=node_coords, face_nodes=cell_nodes, @@ -958,7 +1222,7 @@ function simplexify_reference_face(ref_face) face_nodes_inter[face] = my_nodes end ref_inter = - chain = Object(; + chain = Chain(; num_dims=Val(D), node_coordinates=node_coordinates_inter, face_nodes = face_nodes_inter, @@ -1178,7 +1442,7 @@ function mesh_from_gmsh_module(;complexify=true,topology=true) my_groups[d+1][groupname] = dfaces_in_physical_group end end - mesh = Object(; + mesh = Mesh(; num_dims=Val(D), node_coordinates = my_node_to_coords, face_nodes = my_face_nodes, @@ -1365,7 +1629,7 @@ function complexify_mesh(mesh) old_to_new[d+1] = old_dface_to_new_dface end node_to_coords = node_coordinates(mesh) - new_mesh = Object(; + new_mesh = Mesh(; num_dims = Val(D), node_coordinates=node_to_coords, face_nodes=newface_nodes, @@ -1735,7 +1999,7 @@ function topology_from_mesh(mesh) my_face_reference_id = [ face_reference_id(mesh,d) for d in 0:D ] my_reference_faces = Tuple([ map(reference_topology_from_reference_face,reference_faces(mesh,d)) for d in 0:D ]) my_face_permutation_ids = Matrix{T}(undef,D+1,D+1) - topo = Object(; + topo = Topology(; face_incidence=my_face_incidence, face_reference_id=my_face_reference_id, reference_faces=my_reference_faces, @@ -2142,7 +2406,7 @@ function cartesian_mesh_with_boundary(domain,cells_per_dir) mesh_face_reference_id = push(face_to_refid,face_reference_id(interior_mesh,D)) mesh_reference_faces = push(refid_to_refface,reference_faces(interior_mesh,D)) mesh_groups = push(groups,physical_groups(interior_mesh,D)) - Object(; + Mesh(; num_dims=Val(D), node_coordinates=node_coords, face_nodes=mesh_face_nodes, @@ -2193,7 +2457,7 @@ function cartesian_chain(domain,cells_per_dir) reference_cells = [ref_cell] interior_cells = collect(Int32,1:length(cell_nodes)) groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) - chain = Object(; + chain = Chain(; num_dims=Val(D), node_coordinates=node_coords, face_nodes=cell_nodes, @@ -2259,7 +2523,7 @@ function structured_simplex_chain(domain,cells_per_dir) reference_cells = reference_faces(ref_simplex_mesh,D) interior_cells = collect(Int32,1:length(cell_nodes)) groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) - chain = Object(; + chain = Chain(; num_dims=Val(D), node_coordinates=node_coords, face_nodes=cell_nodes, @@ -2390,7 +2654,7 @@ function structured_simplex_mesh_with_boundary(domain,cells_per_dir) mesh_face_reference_id = push(face_to_refid,face_reference_id(simplex_chain)) mesh_reference_faces = reference_faces(ref_simplex_mesh) mesh_groups = push(groups,physical_groups(simplex_chain)) - Object(; + Mesh(; num_dims=Val(D), node_coordinates=node_coords, face_nodes=mesh_face_nodes, @@ -2417,7 +2681,7 @@ function mesh_from_chain(chain) ref_cell = first(reference_cells) ref_faces = reference_faces(boundary(ref_cell)) refid_to_refface = push(ref_faces,reference_cells) - mesh = Object(; + mesh = Mesh(; num_dims=Val(D), node_coordinates=node_coords, face_nodes=face_to_nodes, @@ -2533,7 +2797,7 @@ function visualization_mesh_from_mesh(mesh,dim=num_dims(mesh);order=nothing,reso vnode_prev = vnode + 1 end vcell_to_vnodes = JaggedArray(vcell_to_vnodes_data,vcell_to_vnodes_ptrs) - vchain = Object(; + vchain = Chain(; num_dims=Val(dim), node_coordinates=vnode_to_coords, face_nodes=vcell_to_vnodes, @@ -2630,7 +2894,7 @@ function refine_reference_geometry(geo,resolution) end end refface = lagrangian_reference_face(geo) - chain = Object(; + chain = Chain(; num_dims=Val(2), node_coordinates = X, face_nodes = T, @@ -2683,7 +2947,7 @@ function refine_reference_geometry(geo,resolution) end end refface = lagrangian_reference_face(geo) - chain = Object(; + chain = Chain(; num_dims=Val(3), node_coordinates = X, face_nodes = T, @@ -2740,7 +3004,7 @@ function lagrangian_reference_face( monomials = map(e->(x-> prod(x.^e)),monomial_exponents) dofs = map(x->(f->f(x)),node_coordinates) shape_functions, dofs = shape_functions_and_dofs_from_bases(monomials,dofs) - refface = Object(; + refface = Face(; geometry, shape_functions, dofs, From 2773bd573a3600edd0733234d5b897192765427f Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Wed, 24 Jan 2024 16:48:08 +0100 Subject: [PATCH 02/34] Starting a refactoring --- src/GalerkinToolkit.jl | 3522 +--------------------------------------- test/runtests.jl | 28 +- 2 files changed, 116 insertions(+), 3434 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index f346bc83..497b1e5d 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -10,617 +10,81 @@ using PartitionedArrays using Combinatorics using SparseArrays -struct Geometry{A,B,C,D} - num_dims::A - is_axis_aligned::Bool - is_n_cube::Bool - is_simplex::Bool - bounding_box::B - boundary::C - vertex_permutations::D -end -function Geometry(; - num_dims, - is_axis_aligned, - is_n_cube, - is_simplex, - bounding_box, - boundary=nothing, - vertex_permutations=nothing) - - Geometry( - num_dims, - is_axis_aligned, - is_n_cube, - is_simplex, - bounding_box, - boundary, - vertex_permutations) -end -function setproperties( - geom::Geometry; - boundary=nothing, - vertex_permutations=nothing) - - Geometry( - geom.num_dims, - geom.is_axis_aligned, - geom.is_n_cube, - geom.is_simplex, - geom.bounding_box, - boundary === nothing ? geom.boundary : boundary, - vertex_permutations == nothing ? geom.vertex_permutations : vertex_permutations) -end -function Base.show(io::IO,data::Geometry) - print(io,"GalerkinToolkit.Geometry(…)") -end - -struct Mesh - num_dims - node_coordinates - face_nodes - face_reference_id - reference_faces - physical_groups - periodic_nodes - topology - outwards_normals -end -function Mesh(; - num_dims, - node_coordinates, - face_nodes, - face_reference_id, - reference_faces, - physical_groups = nothing, - periodic_nodes = nothing, - topology = nothing, - outwards_normals = nothing, - ) - Mesh( - num_dims, - node_coordinates, - face_nodes, - face_reference_id, - reference_faces, - physical_groups, - periodic_nodes, - topology, - outwards_normals, - ) -end -function setproperties(m::Mesh; - physical_groups = nothing, - periodic_nodes = nothing, - topology = nothing, - outwards_normals = nothing, - ) - Mesh( - m.num_dims, - m.node_coordinates, - m.face_nodes, - m.face_reference_id, - m.reference_faces, - physical_groups === nothing ? m.physical_groups : physical_groups, - periodic_nodes === nothing ? m.periodic_nodes : periodic_nodes, - topology === nothing ? m.topology : topology, - outwards_normals === nothing ? m.outwards_normals : outwards_normals, - ) -end -function Base.show(io::IO,data::Mesh) - print(io,"GalerkinToolkit.Mesh(…)") -end - -struct Chain - num_dims - node_coordinates - face_nodes - face_reference_id - reference_faces - physical_groups - periodic_nodes -end -function Chain(; - num_dims, - node_coordinates, - face_nodes, - face_reference_id, - reference_faces, - physical_groups = nothing, - periodic_nodes = nothing, - ) - Chain( - num_dims, - node_coordinates, - face_nodes, - face_reference_id, - reference_faces, - physical_groups, - periodic_nodes, - ) -end -function setproperties(c::Chain; - physical_groups = nothing, - periodic_nodes = nothing) - Chain( - c.num_dims, - c.node_coordinates, - c.face_nodes, - c.face_reference_id, - c.reference_faces, - physical_groups === nothing ? c.physical_groups : physical_groups, - periodic_nodes === nothing ? c.periodic_nodes : periodic_nodes, - ) -end -function Base.show(io::IO,data::Chain) - print(io,"GalerkinToolkit.Chain(…)") -end - -struct Topology - face_incidence - face_reference_id - reference_faces - face_permutation_ids -end -function Topology(; - face_incidence, - face_reference_id, - reference_faces, - face_permutation_ids) - Topology( - face_incidence, - face_reference_id, - reference_faces, - face_permutation_ids) -end -function setproperties(t::Topology) - t -end -function Base.show(io::IO,data::Topology) - print(io,"GalerkinToolkit.Topology(…)") -end - -# TODO this is growing too big -struct Face - geometry - shape_functions - dofs - node_coordinates - order - order_per_dir - monomial_exponents - lib_to_user_nodes - boundary - interior_nodes - vtk_mesh_cell - num_dofs - node_to_dofs - dof_to_node - dof_to_index - face_own_dofs - face_own_dof_permutations -end -function Face(; - geometry, - shape_functions, - dofs, - node_coordinates, - order, - order_per_dir, - monomial_exponents, - lib_to_user_nodes, - boundary=nothing, - interior_nodes=nothing, - vtk_mesh_cell=nothing, - num_dofs=nothing, - node_to_dofs=nothing, - dof_to_node=nothing, - dof_to_index=nothing, - face_own_dofs=nothing, - face_own_dof_permutations=nothing) - Face( - geometry, - shape_functions, - dofs, - node_coordinates, - order, - order_per_dir, - monomial_exponents, - lib_to_user_nodes, - boundary, - interior_nodes, - vtk_mesh_cell, - num_dofs, - node_to_dofs, - dof_to_node, - dof_to_index, - face_own_dofs, - face_own_dof_permutations,) -end -function setproperties(f::Face; - shape_functions=nothing, - dofs=nothing, - boundary=nothing, - interior_nodes=nothing, - vtk_mesh_cell=nothing, - num_dofs=nothing, - node_to_dofs=nothing, - dof_to_node=nothing, - dof_to_index=nothing, - face_own_dofs=nothing, - face_own_dof_permutations=nothing - ) - Face( - f.geometry, - shape_functions === nothing ? f.shape_functions : shape_functions, - dofs === nothing ? f.dofs : dofs, - f.node_coordinates, - f.order, - f.order_per_dir, - f.monomial_exponents, - f.lib_to_user_nodes, - boundary === nothing ? f.boundary : boundary, - interior_nodes === nothing ? f.interior_nodes : interior_nodes, - vtk_mesh_cell === nothing ? f.vtk_mesh_cell : vtk_mesh_cell, - num_dofs === nothing ? f.num_dofs : num_dofs, - node_to_dofs === nothing ? f.node_to_dofs : node_to_dofs, - dof_to_node === nothing ? f.dof_to_node : dof_to_node, - dof_to_index === nothing ? f.dof_to_index : dof_to_index, - face_own_dofs === nothing ? f.face_own_dofs : face_own_dofs, - face_own_dof_permutations === nothing ? f.face_own_dof_permutations : face_own_dof_permutations - ) -end -function Base.show(io::IO,data::Face) - print(io,"GalerkinToolkit.Face(…)") -end - -# Inspited by PropertyUtils.jl -# Use a dict directly instead? (provably not, we would not be type stable even if we wanted,really?) -struct Object - item::Dict{Symbol,Any} -end -Object(;kwargs...) = Object(Dict(kwargs)) -Base.propertynames(i::Object) = collect(keys(getfield(i, :item))) -Base.getproperty(i::Object, x::Symbol) = getindex(getfield(i, :item), x) -Base.setproperty!(i::Object, name::Symbol, x) = setindex!(getfield(i, :item), x, name) - -# TODO setproperty instead? -function setproperties(a::Object;kwargs...) - dict = getfield(a, :item) - item = merge(dict,kwargs) - Object(item) -end - -function Base.show(io::IO,a::Object) - print(io,"GalerkinToolkit.Object(") - dict = getfield(a, :item) - for (i,(k,v)) in enumerate(dict) - if i != 1 - print(io,", ") - end - print(io,k," = ") - if isa(v,Object) - print(io,"GalerkinToolkit.Object(...)") - elseif isa(v,AbstractArray) && length(v) > 10 - print(io,"[...]") - else - print(io,v) - end - end - print(io,")") +abstract type GalerkinToolkitDataType end +function Base.show(io::IO,data::GalerkinToolkitDataType) + print(io,"GalerkinToolkit.$(nameof(typeof(data)))(…)") end val_parameter(a) = a val_parameter(::Val{a}) where a = a - num_dims(a) = val_parameter(a.num_dims) -node_coordinates(a) = a.node_coordinates -reference_faces(a) = a.reference_faces -face_nodes(a) = a.face_nodes -face_incidence(a) = a.face_incidence -face_reference_id(a) = a.face_reference_id -vtk_mesh_cell(a) = a.vtk_mesh_cell -physical_groups(a) = a.physical_groups -has_physical_groups(a) = hasproperty(a,:physical_groups) && a.physical_groups !== nothing -periodic_nodes(a) = a.periodic_nodes -has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing -geometry(a) = a.geometry -topology(a) = a.topology -boundary(a) = a.boundary -is_n_cube(a) = hasproperty(a,:is_n_cube) ? val_parameter(a.is_n_cube) : false -is_simplex(a) = hasproperty(a,:is_simplex) ? val_parameter(a.is_simplex) : false -is_axis_aligned(a) = a.is_axis_aligned -bounding_box(a) = a.bounding_box -vertex_permutations(a) = a.vertex_permutations -face_own_dofs(a) = a.face_own_dofs -face_own_dof_permutations(a) = a.face_own_dof_permutations -node_to_dofs(a) = a.node_to_dofs -dof_to_node(a) = a.dof_to_node -dof_to_index(a) = a.dof_to_index -num_dofs(a) = a.num_dofs coordinates(a) = a.coordinates weights(a) = a.weights -shape_functions(a) = a.shape_functions -tabulation_matrix!(a) = a.tabulation_matrix! -tabulation_matrix(a) = a.tabulation_matrix -order(a) = a.order -monomial_exponents(a) = a.monomial_exponents -lib_to_user_nodes(a) = a.lib_to_user_nodes -interior_nodes(a) = a.interior_nodes -face_permutation_ids(a) = a.face_permutation_ids -face_permutation_ids(a,m,n) = face_permutation_ids(a)[m+1,n+1] -local_nodes(a) = a.local_nodes -local_node_colors(a) = a.local_node_colors - - -reference_faces(a,d) = reference_faces(a)[val_parameter(d)+1] -face_nodes(a,d) = face_nodes(a)[val_parameter(d)+1] -face_incidence(a,d1,d2) = face_incidence(a)[val_parameter(d1)+1,val_parameter(d2)+1] -face_reference_id(a,d) = face_reference_id(a)[val_parameter(d)+1] -num_faces(a) = map(length,face_reference_id(a)) -num_faces(a,d) = length(face_reference_id(a,d)) -physical_groups(a,d) = physical_groups(a)[val_parameter(d)+1] -num_nodes(a) = length(node_coordinates(a)) -num_ambient_dims(a) = length(eltype(node_coordinates(a))) -function face_offsets(a) - D = num_dims(a) - offsets = zeros(Int,D+1) - for d in 1:D - offsets[d+1] = offsets[d] + num_faces(a,d-1) - end - offsets -end - -function face_dim(a,d) - n = num_faces(a,d) - fill(d,n) -end - -function face_dim(a) - D = num_dims(a) - reduce(vcat,map(d->face_dim(a,d),0:D)) -end -function is_unit_n_cube(geom) - !(is_axis_aligned(geom) && is_n_cube(geom)) && return false - my_bounding_box = bounding_box(geom) - all(i->i==0,first(my_bounding_box)) && all(i->i==1,last(my_bounding_box)) +struct UnitSimplex{D} <: GalerkinToolkitDataType + num_dims::Val{D} end - -function is_unit_simplex(geom) - !(is_axis_aligned(geom) && is_simplex(geom)) && return false - my_bounding_box = bounding_box(geom) - all(i->i==0,first(my_bounding_box)) && all(i->i==1,last(my_bounding_box)) -end - -function push(a::AbstractVector,x) - b = copy(a) - push!(b,x) - b -end - -function push(a::Tuple,x) - (a...,x) -end - -function mesh_from_reference_face(ref_face;physical_groups=Val(true)) - boundary_mesh = boundary(ref_face) - D = num_dims(geometry(ref_face)) - nnodes = num_nodes(ref_face) - face_to_nodes = push(face_nodes(boundary_mesh),[collect(1:nnodes)]) - face_to_refid = push(face_reference_id(boundary_mesh),[1]) - refid_refface = push(reference_faces(boundary_mesh),[ref_face]) - node_to_coords = node_coordinates(ref_face) - mesh = Mesh(; - num_dims=Val(D), - node_coordinates=node_to_coords, - face_nodes=face_to_nodes, - face_reference_id=face_to_refid, - reference_faces=refid_refface) - if val_parameter(physical_groups) - groups = [ Dict{String,Vector{Int32}}() for d in 0:D] - for d in 0:D - for face in 1:num_faces(mesh,d) - groups[d+1]["$d-face-$face"] = [face] - end - end - groups[end-1]["boundary"] = 1:num_faces(mesh,D-1) - groups[end]["interior"] = [1] - mesh = setproperties(mesh,physical_groups=groups) - end - mesh -end - -function vtk_points(mesh) - function barrirer(coords) - nnodes = length(coords) - points = zeros(3,nnodes) - for node in 1:nnodes - coord = coords[node] - for i in 1:length(coord) - points[i,node] = coord[i] - end - end - points - end - coords = node_coordinates(mesh) - barrirer(coords) +function Base.show(io::IO,k::MIME"text/plain",data::UnitSimplex) + D = num_dims(data) + print(io,"Unit simplex of dimension $D") end -function vtk_cells(mesh,d) - function barrirer(face_to_refid,face_to_nodes,refid_mesh_cell) - cells = map(face_to_refid,face_to_nodes) do refid, nodes - mesh_cell = refid_mesh_cell[refid] - if mesh_cell === nothing - msg = """ - Not enough information to visualize this mesh via vtk: - vtk_mesh_cell returns nothing for the reference face in position $refid in dimension $d. - """ - error(msg) - end - mesh_cell(nodes) - end - cells - end - face_to_nodes = face_nodes(mesh,d) - face_to_refid = face_reference_id(mesh,d) - refid_refface = reference_faces(mesh,d) - refid_mesh_cell = map(vtk_mesh_cell,refid_refface) - barrirer(face_to_refid,face_to_nodes,refid_mesh_cell) +struct UnitNCube{D} <: GalerkinToolkitDataType + num_dims::Val{D} end - -""" - args = vtk_args(mesh,d) - -Return the arguments `args` to be passed in final position -to functions like `WriteVTK.vtk_grid`. -""" -function vtk_args(mesh,d) - points = vtk_points(mesh) - cells = vtk_cells(mesh,d) - points, cells +function Base.show(io::IO,k::MIME"text/plain",data::UnitNCube) + D = num_dims(data) + print(io,"Unit cube of dimension $D") end -function vtk_args(mesh) - points = vtk_points(mesh) - D = num_dims(mesh) - allcells = [vtk_cells(mesh,d) for d in 0:D if num_faces(mesh,d) != 0] - cells = reduce(vcat,allcells) - points, cells +function unit_simplex(num_dims) + D = val_parameter(num_dims) + UnitSimplex(Val(D)) end -function vtk_physical_groups!(vtk,mesh,d;physical_groups=physical_groups(mesh,d)) - ndfaces = num_faces(mesh,d) - for group in physical_groups - name,faces = group - face_mask = zeros(Int,ndfaces) - face_mask[faces] .= 1 - vtk[name,WriteVTK.VTKCellData()] = face_mask - end - vtk +function unit_n_cube(num_dims) + D = val_parameter(num_dims) + UnitNCube(Val(D)) end -function vtk_physical_groups!(vtk,mesh;physical_groups=physical_groups(mesh)) - nfaces = sum(num_faces(mesh)) - offsets = face_offsets(mesh) - D = num_dims(mesh) - data = Dict{String,Vector{Int}}() - for d in 0:D - for group in physical_groups[d+1] - name, = group - if !haskey(data,name) - face_mask = zeros(Int,nfaces) - data[name] = face_mask - end - end - end - for d in 0:D - for group in physical_groups[d+1] - offset = offsets[d+1] - name,faces = group - face_mask = data[name] - face_mask[faces.+offset] .= 1 - end - end - for (name,face_mask) in data - vtk[name,WriteVTK.VTKCellData()] = face_mask - end - vtk +struct GenericCuadrature{A,B} <: GalerkinToolkitDataType + coordinates::A + weights::B end - -function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) - fill!(node_to_tag,zero(eltype(node_to_tag))) - for d in dmax:-1:0 - face_to_nodes = face_nodes(mesh,d) - face_groups = physical_groups(mesh,d) - for (tag,name) in enumerate(tag_to_name) - for (name2,faces) in face_groups - if name != name2 - continue - end - for face in faces - nodes = face_to_nodes[face] - node_to_tag[nodes] .= tag - end - end - end - end - node_to_tag +struct Cuadrature{D,T} <: GalerkinToolkitDataType + coordinates::Vector{SVector{D,T}} + weights::Vector{T} end - -partition_from_mask(a) = partition_from_mask(identity,a) - -function partition_from_mask(f,node_to_mask) - T = Vector{Int32} - free_nodes = convert(T,findall(f,node_to_mask)) - dirichlet_nodes = convert(T,findall(i->!f(i),node_to_mask)) - nfree = length(free_nodes) - ndiri = length(dirichlet_nodes) - permutation = T(undef,nfree+ndiri) - permutation[free_nodes] = 1:nfree - permutation[dirichlet_nodes] = (1:ndiri) .+ nfree - TwoPartPartition(free_nodes,dirichlet_nodes,permutation) +function quadrature(coordinates,weights) + GenericCuadrature(coordinates,weights) end - -struct TwoPartPartition{A} <: AbstractVector{A} - first::A - last::A - permutation::A +function quadrature(coordinates::Vector{SVector{D,T}},weights::Vector{T}) where {D,T} + Cuadrature(coordinates,weights) end -permutation(a::TwoPartPartition) = a.permutation -Base.size(a::TwoPartPartition) = (2,) -Base.IndexStyle(::Type{<:TwoPartPartition}) = IndexLinear() -function Base.getindex(a::TwoPartPartition,i::Int) - @boundscheck @assert i in (1,2) - if i == 1 - a.first - else - a.last - end +function default_quadrature(geo::UnitSimplex; + degree, + real_type=Float64) + duffy_quadrature(geo;degree,real_type) end -function tensor_product_quadrature( - degree_per_dir, - limits_per_dir; - weight_type=Float64, - coordinate_type=SVector{length(limits_per_dir),weight_type}, - allocator=zeros) - D = length(degree_per_dir) - n_per_dir = map(d->ceil(Int,(d+1)/2),degree_per_dir) - quad_per_dir = map(n_per_dir,limits_per_dir) do n,limits - x,w = FastGaussQuadrature.gausslegendre(n) - a,b = limits - x .= (0.5*(b-a)) .*x .+ (0.5*(b+a)) - w .*= 0.5*(b-a) - (;coordinates=x,weights=w) - end - coords_per_dir = map(coordinates,quad_per_dir) - weights_per_dir = map(weights,quad_per_dir) - m = prod(map(length,weights_per_dir)) - w = allocator(weight_type,m) - x = allocator(coordinate_type,m) - tensor_product!(identity,x,coords_per_dir) - tensor_product!(prod,w,weights_per_dir) - (;coordinates=x,weights=w) +function default_quadrature(geo::UnitNCube; + degree, + real_type=Float64) + degree_per_dir = ntuple(i->degree,Val(num_dims(geo))) + tensor_product_quadrature(geo;degree_per_dir,real_type) end -# Duffy map from the n-cube in [0,1]^d to the n-simplex in [0,1]^d -function duffy_map(q) - D = length(q) - a = 1.0 - m = ntuple(Val(D)) do i - if i == 1 - q[i] - else - a *= (1-q[i-1]) - a*q[i] - end +function duffy_quadrature(geo::UnitSimplex; + degree, + real_type = Float64, + ) + D = num_dims(geo) + if D == 0 + x = zeros(SVector{0,real_type},1) + w = ones(real_type,1) + return quadrature(x,w) end - typeof(q)(m) -end - -function duffy_quadrature(order,Dval) function map_to(a,b,(points,weights)) points_ab = similar(points) weights_ab = similar(weights) @@ -628,8 +92,20 @@ function duffy_quadrature(order,Dval) weights_ab .= 0.5*(b-a)*weights (points_ab, weights_ab) end - n = ceil(Int, (order + 1.0) / 2.0 ) - D = val_parameter(Dval) + function duffy_map(q) + D = length(q) + a = 1.0 + m = ntuple(Val(D)) do i + if i == 1 + q[i] + else + a *= (1-q[i-1]) + a*q[i] + end + end + typeof(q)(m) + end + n = ceil(Int, (degree + 1.0) / 2.0 ) beta = 0 dim_to_quad_1d = map(1:(D-1)) do d alpha = (D-1)-(d-1) @@ -646,12 +122,38 @@ function duffy_quadrature(order,Dval) a *= 0.5 end m = prod(map(length,weights_per_dir)) - w = zeros(Float64,m) - x = zeros(SVector{D,Float64},m) + w = zeros(real_type,m) + x = zeros(SVector{D,real_type},m) tensor_product!(identity,x,coords_per_dir) tensor_product!(prod,w,weights_per_dir) x .= duffy_map.(x) - (;coordinates=x,weights=w) + quadrature(x,w) +end + +function tensor_product_quadrature(geo::UnitNCube; + degree_per_dir, + real_type = Float64, + ) + + D = num_dims(geo) + limits_per_dir = ntuple(i->(0,1),Val(D)) + n_per_dir = map(d->ceil(Int,(d+1)/2),degree_per_dir) + function quadrature_1d(n,limits) + x,w = FastGaussQuadrature.gausslegendre(n) + a,b = limits + x .= (0.5*(b-a)) .*x .+ (0.5*(b+a)) + w .*= 0.5*(b-a) + quadrature(x,w) + end + quad_per_dir = map(quadrature_1d,n_per_dir,limits_per_dir) + coords_per_dir = map(coordinates,quad_per_dir) + weights_per_dir = map(weights,quad_per_dir) + m = prod(map(length,weights_per_dir)) + w = zeros(real_type,m) + x = zeros(SVector{D,real_type},m) + tensor_product!(identity,x,coords_per_dir) + tensor_product!(prod,w,weights_per_dir) + quadrature(x,w) end function tensor_product!(f,result,values_per_dir) @@ -665,2848 +167,4 @@ function tensor_product!(f,result,values_per_dir) result end -function quadrature(geom,degree;kwargs...) - if is_n_cube(geom) - if is_axis_aligned(geom) - my_bounding_box = bounding_box(geom) - D = length(first(my_bounding_box)) - limits_per_dir = ntuple(i->(my_bounding_box[1][i],my_bounding_box[2][i]),Val(D)) - degree_per_dir = ntuple(i->degree,Val(D)) - tensor_product_quadrature(degree_per_dir,limits_per_dir;kwargs...) - else - error("Not implemented") - end - elseif is_unit_simplex(geom) - duffy_quadrature(degree,num_dims(geom)) - else - error("Not implemented") - end -end - -function monomial_exponents_from_filter( - f,order_per_dir; monomial_type=SVector{length(order_per_dir),Int}, allocator=zeros) - terms_per_dir = Tuple(map(d->d+1,order_per_dir)) - D = length(terms_per_dir) - cis = CartesianIndices(terms_per_dir) - m = count(ci->f(Tuple(ci) .- 1,order_per_dir),cis) - li = 0 - result = zeros(monomial_type,m) - for ci in cis - t = Tuple(ci) .- 1 - if f(t,order_per_dir) - li += 1 - result[li] = t - end - end - result -end - -function monomial_exponents_from_space(space,order_per_dir;kwargs...) - filter = if space == :Q - (e,o)->true - elseif space == :P - (e,o)->sum(e)<=maximum(o) - else - error("Case not implemented (yet)") - end - monomial_exponents_from_filter(filter,order_per_dir;kwargs...) -end - -function shape_functions_and_dofs_from_bases(primal,dual) - primal_t = permutedims(primal) - function dofs_from_dual_basis(dual) - function tabulation_matrix(primal) - A = value.(dual,primal_t) - end - (;tabulation_matrix) - end - dofs = dofs_from_dual_basis(dual) - A = dofs.tabulation_matrix(primal) - B = A\I - function tabulation_matrix!(f,r,x;scratch=nothing) - C = if scratch !== nothing - broadcast!(f,scratch,primal_t,x) - else - broadcast(f,primal_t,x) - end - mul!(r,C,B) - r - end - function tabulation_matrix(f,x) - C = broadcast(f,primal_t,x) - C*B - end - # We want this to be type stable for - # performance reasons - shape_functions = (;tabulation_matrix!,tabulation_matrix) - shape_functions, dofs -end - -value(f,x) = f(x) - -function default_polynomial_space(geo) - if is_n_cube(geo) - :Q - elseif is_simplex(geo) - :P - else - error("Case not implemented") - end -end - -function reference_face_boundary_from_reference_face(refface) - geom = geometry(refface) - node_coordinates_inter = node_coordinates(refface) - order_inter = order(refface) - D = num_dims(geom) - if D == 0 - return nothing, collect(1:length(node_coordinates_inter)) - end - mesh_geom = boundary(geom) - node_coordinates_geom = node_coordinates(mesh_geom) - face_nodes_inter = Vector{Vector{Vector{Int}}}(undef,D) - node_coordinates_aux = map(xi->map(xii->round(Int,order_inter*xii),xi),node_coordinates_inter) - ref_faces_geom = reference_faces(mesh_geom) - ref_faces = map(ref_faces_geom) do ref_faces_geom_d - map(r->lagrangian_reference_face(geometry(r);order=order_inter),ref_faces_geom_d) - end - face_ref_id_geom = face_reference_id(mesh_geom) - node_is_touched = fill(true,length(node_coordinates_inter)) - for d in 0:(D-1) - s_ref = map(ref_faces_geom[d+1],ref_faces[d+1]) do r_geom,r - m = num_nodes(r) - n = num_nodes(r_geom) - f = shape_functions(r_geom) - x = node_coordinates(r) - tabulation_matrix(f)(value,x) - end - face_nodes_geom = face_nodes(mesh_geom,d) - nfaces = length(face_ref_id_geom[d+1]) - face_nodes_inter_d = Vector{Vector{Int}}(undef,nfaces) - for face in 1:nfaces - ref_id_geom = face_ref_id_geom[d+1][face] - s = s_ref[ref_id_geom] - nodes_geom = face_nodes_geom[face] - nnodes, nnodes_geom = size(s) - x_mapped = map(1:nnodes) do i - x = zero(eltype(node_coordinates_inter)) - for k in 1:nnodes_geom - x += node_coordinates_geom[nodes_geom[k]]*s[i,k] - end - map(xi->round(Int,order_inter*xi),x) - end - my_nodes = indexin(x_mapped,node_coordinates_aux) - node_is_touched[my_nodes] .= false - face_nodes_inter_d[face] = my_nodes - end - face_nodes_inter[d+1] = face_nodes_inter_d - end - mesh_inter = Mesh(; - num_dims = Val(D-1), - node_coordinates=node_coordinates_inter, - face_nodes=face_nodes_inter, - face_reference_id=face_ref_id_geom, - reference_faces=ref_faces - ) - interior_nodes = findall(node_is_touched) - mesh_inter, interior_nodes -end - -function vtk_mesh_cell_from_reference_face(ref_face) - geom = geometry(ref_face) - d = num_dims(geom) - nnodes = num_nodes(ref_face) - lib_to_user = lib_to_user_nodes(ref_face) - if d == 0 && nnodes == 1 - cell_type = WriteVTK.VTKCellTypes.VTK_VERTEX - vtk_to_lib = [1] - elseif d == 1 && (is_simplex(geom) || is_n_cube(geom)) && nnodes == 2 - cell_type = WriteVTK.VTKCellTypes.VTK_LINE - vtk_to_lib = [1,2] - elseif d == 1 && (is_simplex(geom) || is_n_cube(geom)) && nnodes == 3 - cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_EDGE - vtk_to_lib = [1,3,2] - elseif d == 2 && is_n_cube(geom) && nnodes == 4 - cell_type = WriteVTK.VTKCellTypes.VTK_QUAD - vtk_to_lib = [1,2,4,3] - elseif d == 2 && is_simplex(geom) && nnodes == 3 - cell_type = WriteVTK.VTKCellTypes.VTK_TRIANGLE - vtk_to_lib = [1,2,3] - elseif d == 2 && is_simplex(geom) && nnodes == 6 - cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_TRIANGLE - vtk_to_lib = [1,3,6,2,5,4] - elseif d == 3 && is_n_cube(geom) && nnodes == 8 - cell_type = WriteVTK.VTKCellTypes.VTK_HEXAHEDRON - vtk_to_lib = [1,2,4,3,5,6,8,7] - elseif d == 3 && is_simplex(geom) && nnodes == 4 - cell_type = WriteVTK.VTKCellTypes.VTK_TETRA - vtk_to_lib = [1,2,3,4] - elseif d == 3 && is_simplex(geom) && nnodes == 10 - cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_TETRA - vtk_to_lib = [1,3,6,10,2,5,4,7,8,9] - else - return nothing - end - nodes -> WriteVTK.MeshCell(cell_type,(nodes[lib_to_user])[vtk_to_lib]) -end - -function unit_n_cube(D;kwargs...) - d = val_parameter(D) - num_dims = Val(d) - is_n_cube=true - is_axis_aligned=true - bounding_box=SVector{d,Float64}[ntuple(i->0,Val(d)),ntuple(i->1,Val(d))] - is_simplex = d in (0,1) - boundary = unit_n_cube_boundary(D;kwargs...) - geometry = Geometry(;num_dims,is_n_cube,is_simplex,is_axis_aligned,bounding_box,boundary) - vertex_permutations = vertex_permutations_from_geometry(geometry) - setproperties(geometry;vertex_permutations) -end - -function unit_simplex(D;kwargs...) - d = val_parameter(D) - num_dims = Val(d) - is_simplex=true - is_axis_aligned=true - is_n_cube = d in (0,1) - bounding_box=SVector{d,Float64}[ntuple(i->0,Val(d)),ntuple(i->1,Val(d))] - boundary = unit_simplex_boundary(D;kwargs...) - geometry = Geometry(;num_dims,is_n_cube,is_simplex,is_axis_aligned,bounding_box,boundary) - vertex_permutations = vertex_permutations_from_geometry(geometry) - setproperties(geometry;vertex_permutations) -end - -function unit_n_cube_boundary( - D; - reference_face=nothing) - - # TODO use the same convention than in Gridap - # allow the user define an alternative ordering - - d = val_parameter(D) - if d == 0 - return nothing - end - if reference_face === nothing - reference_face = lagrangian_reference_face(unit_n_cube(d-1)) - end - my_boundary = if d == 1 - node_coordinates = SVector{1,Float64}[(0,),(1,)] - face_nodes = [[[1],[2]]] - face_reference_id = [[1,1]] - vertex = reference_face - my_reference_faces = ([vertex],) - Mesh(;num_dims=Val(0),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces) - elseif d == 2 - node_coordinates = SVector{2,Float64}[(0,0),(1,0),(0,1),(1,1)] - face_nodes = [[[1],[2],[3],[4]],[[1,2],[3,4],[1,3],[2,4]]] - face_reference_id = [[1,1,1,1],[1,1,1,1]] - segment = reference_face - vertex = first(reference_faces(boundary(geometry(segment)),0)) - my_reference_faces = ([vertex],[segment]) - outwards_normals = SVector{2,Float64}[(0,-1),(0,1),(-1,0),(1,0)] - Mesh(;num_dims=Val(1),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) - elseif d == 3 - node_coordinates = SVector{3,Float64}[(0,0,0),(1,0,0),(0,1,0),(1,1,0),(0,0,1),(1,0,1),(0,1,1),(1,1,1)] - face_nodes = [ - [[1],[2],[3],[4],[5],[6],[7],[8]], - [[1,2],[3,4],[1,3],[2,4],[5,6],[7,8],[5,7],[6,8],[1,5],[3,7],[2,6],[4,8]], - [[1,2,3,4],[5,6,7,8],[1,2,5,6],[3,4,7,8],[1,3,5,7],[2,4,6,8]], - ] - face_reference_id = [ones(Int,8),ones(Int,12),ones(Int,6)] - quad = reference_face - segment = first(reference_faces(boundary(geometry(quad)),1)) - vertex = first(reference_faces(boundary(geometry(segment)),0)) - my_reference_faces = ([vertex],[segment],[quad]) - outwards_normals = SVector{3,Float64}[(0,0,-1),(0,0,1),(0,-1,0),(0,1,0),(-1,0,0),(1,0,0)] - Mesh(;num_dims=Val(2),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) - else - @error "Case not implemented" - end - topology = topology_from_mesh(my_boundary) - setproperties(my_boundary;topology) -end - -function unit_simplex_boundary( - # TODO use the same convention than in Gridap - # allow the user to customize the boundary object ids - D; - reference_face=nothing) - - d = val_parameter(D) - if d == 0 - return nothing - end - if reference_face === nothing - reference_face = lagrangian_reference_face(unit_simplex(Val(d-1))) - end - my_boundary = if d == 1 - node_coordinates = SVector{1,Float64}[(0,),(1,)] - face_nodes = [[[1],[2]]] - face_reference_id = [[1,1]] - vertex = reference_face - my_reference_faces = ([vertex],) - Mesh(;num_dims=Val(0),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces) - elseif d == 2 - node_coordinates = SVector{2,Float64}[(0,0),(1,0),(0,1)] - face_nodes = [[[1],[2],[3]],[[1,2],[1,3],[2,3]]] - face_reference_id = [[1,1,1],[1,1,1]] - segment = reference_face - vertex = first(reference_faces(boundary(geometry(segment)),0)) - my_reference_faces = ([vertex],[segment]) - n1 = sqrt(2)/2 - outwards_normals = SVector{2,Float64}[(0,-1),(-1,0),(n1,n1)] - Mesh(;num_dims=Val(1),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) - elseif d == 3 - node_coordinates = SVector{3,Float64}[(0,0,0),(1,0,0),(0,1,0),(0,0,1)] - face_nodes = [ - [[1],[2],[3],[4]], - [[1,2],[1,3],[2,3],[1,4],[2,4],[3,4]], - [[1,2,3],[1,2,4],[1,3,4],[2,3,4]], - ] - face_reference_id = [ones(Int,4),ones(Int,6),ones(Int,4)] - tri = reference_face - segment = first(reference_faces(boundary(geometry(tri)),1)) - vertex = first(reference_faces(boundary(geometry(segment)),0)) - my_reference_faces = ([vertex],[segment],[tri]) - n1 = sqrt(3)/3 - outwards_normals = SVector{3,Float64}[(0,0,-1),(0,-1,0),(-1,0,0),(n1,n1,n1)] - Mesh(;num_dims=Val(2),node_coordinates,face_nodes,face_reference_id,reference_faces=my_reference_faces,outwards_normals) - else - error("case not implemented") - end - topology = topology_from_mesh(my_boundary) - setproperties(my_boundary;topology) -end - -## Admissible if the following map is admissible -# phi_i(x) = sum_i x_perm[i] * fun_i(x) -# This map sends vertex i to vertex perm[i] -function vertex_permutations_from_geometry(geo) - D = num_dims(geo) - if D == 0 - return [[1]] - end - geo_mesh = boundary(geo) - vertex_to_geo_nodes = face_nodes(geo_mesh,0) - vertex_to_geo_node = map(first,vertex_to_geo_nodes) - nvertices = length(vertex_to_geo_node) - # TODO compute this more lazily - # so that we never compute it for 3d - # since it is not needed - if D > 2 - return [collect(1:nvertices)] - end - permutations = Combinatorics.permutations(1:nvertices) - if is_simplex(geo) - return collect(permutations) - end - admissible_permutations = Vector{Int}[] - order = 1 - ref_face = lagrangian_reference_face(geo) - fun_mesh = boundary(ref_face) - geo_node_coords = node_coordinates(geo_mesh) - fun_node_coords = node_coordinates(fun_mesh) - vertex_coords = geo_node_coords[vertex_to_geo_node] - shape_funs = shape_functions(ref_face) - degree = 1 - quad = quadrature(geo,degree) - q = coordinates(quad) - Tx = eltype(vertex_coords) - TJ = typeof(zero(Tx)*zero(Tx)') - A = tabulation_matrix(shape_funs)(ForwardDiff.gradient,q) - function compute_volume(vertex_coords) - vol = zero(eltype(TJ)) - for iq in 1:size(A,1) - J = zero(TJ) - for fun_node in 1:size(A,2) - vertex = fun_node # TODO we are assuming that the vertices and nodes match - g = A[iq,fun_node] - x = vertex_coords[vertex] - J += g*x' - end - vol += abs(det(J)) - end - vol - end - refvol = compute_volume(vertex_coords) - perm_vertex_coords = similar(vertex_coords) - for permutation in permutations - for (j,cj) in enumerate(permutation) - perm_vertex_coords[j] = vertex_coords[cj] - end - vol2 = compute_volume(perm_vertex_coords) - if (refvol + vol2) ≈ (2*refvol) - push!(admissible_permutations,permutation) - end - end - admissible_permutations -end - -function interior_node_permutations_from_reference_face(refface) - interior_ho_nodes = interior_nodes(refface) - ho_nodes_coordinates = node_coordinates(refface) - geo = geometry(refface) - vertex_perms = vertex_permutations(geo) - if length(interior_ho_nodes) == 0 - return map(i->Int[],vertex_perms) - end - if length(vertex_perms) == 1 - return map(i->collect(1:length(interior_ho_nodes)),vertex_perms) - end - geo_mesh = boundary(geo) - vertex_to_geo_nodes = face_nodes(geo_mesh,0) - vertex_to_geo_node = map(first,vertex_to_geo_nodes) - ref_face = lagrangian_reference_face(geo) - fun_mesh = boundary(ref_face) - geo_node_coords = node_coordinates(geo_mesh) - fun_node_coords = node_coordinates(fun_mesh) - vertex_coords = geo_node_coords[vertex_to_geo_node] - shape_funs = shape_functions(ref_face) - q = ho_nodes_coordinates[interior_ho_nodes] - Tx = eltype(vertex_coords) - A = zeros(Float64,length(q),length(fun_node_coords)) - A = tabulation_matrix(shape_funs)(value,q) - perm_vertex_coords = similar(vertex_coords) - node_perms = similar(vertex_perms) - for (iperm,permutation) in enumerate(vertex_perms) - for (j,cj) in enumerate(permutation) - perm_vertex_coords[j] = vertex_coords[cj] - end - node_to_pnode = fill(INVALID_ID,length(interior_ho_nodes)) - for iq in 1:size(A,1) - y = zero(Tx) - for fun_node in 1:size(A,2) - vertex = fun_node # TODO we are assuming that the vertices and nodes match - g = A[iq,fun_node] - x = perm_vertex_coords[vertex] - y += g*x - end - pnode = findfirst(i->(norm(i-y)+1)≈1,q) - if pnode != nothing - node_to_pnode[iq] = pnode - end - end - node_perms[iperm] = node_to_pnode - end - node_perms -end - -function simplexify_reference_geometry(geo) - if is_unit_simplex(geo) - simplexify_unit_simplex(geo) - elseif is_unit_n_cube(geo) - simplexify_unit_n_cube(geo) - else - error("case not implemented") - end -end - -function simplexify_unit_simplex(geo) - @assert is_unit_simplex(geo) - refface = lagrangian_reference_face(geo) - mesh_from_reference_face(refface) -end - -function simplexify_unit_n_cube(geo) - @assert is_unit_n_cube(geo) - D = num_dims(geo) - if D in (0,1) - return simplexify_unit_simplex(geo) - end - simplex = unit_simplex(Val(D)) - order = 1 - ref_cell = lagrangian_reference_face(simplex) - node_coords = node_coordinates(boundary(geo)) - cell_nodes = simplex_node_ids_n_cube(geo) - ncells = length(cell_nodes) - cell_reference_id = fill(Int8(1),ncells) - reference_cells = [ref_cell] - chain = Chain(; - num_dims=Val(D), - node_coordinates=node_coords, - face_nodes=cell_nodes, - face_reference_id=cell_reference_id, - reference_faces=reference_cells, - ) - mesh = mesh_from_chain(chain) - mesh_complex, = complexify_mesh(mesh) - groups = [Dict{String,Vector{Int32}}() for d in 0:D] - for d in 0:(D-1) - sface_to_nodes = face_nodes(mesh_complex,d) - cface_to_nodes = face_nodes(boundary(geo),d) - nsfaces = length(sface_to_nodes) - ncfaces = length(cface_to_nodes) - sface_touched = fill(false,nsfaces) - for cface in 1:ncfaces - fill!(sface_touched,false) - nodes_c = cface_to_nodes[cface] - for sface in 1:nsfaces - nodes_s = sface_to_nodes[sface] - if all(map(n->n in nodes_c,nodes_s)) - sface_touched[sface] = true - end - end - sfaces_in_group = findall(sface_touched) - group_name = "$d-face-$cface" - groups[d+1][group_name] = sfaces_in_group - end - end - groups[end]["interior"] = 1:(num_faces(mesh_complex,D)) - sface_is_boundary = fill(false,num_faces(mesh_complex,D-1)) - for (_,sfaces) in groups[end-1] - sface_is_boundary[sfaces] .= true - end - groups[end-1]["boundary"] = findall(sface_is_boundary) - mesh_complex = setproperties(mesh_complex,physical_groups=groups) - mesh_complex -end - -function simplex_node_ids_n_cube(geo) - D = num_dims(geo) - # TODO check orientation of nodes - # this assumes lexicographic ordering - # for 3d nodes ids are carefully selected - # such that opposite faces match. - # This allows one to complexify meshes - # of ncubes with oriented faces - if D == 0 - [[1]] - elseif D == 1 - [[1,2]] - elseif D == 2 - [[1,2,3],[2,3,4]] - elseif D ==3 - [[1,2,3,7], [1,2,5,7], [2,3,4,7], - [2,4,7,8], [2,5,6,7], [2,6,7,8]] - else - error("case not implemented") - end -end - -function simplexify_reference_face(ref_face) - mesh_geom = simplexify_reference_geometry(geometry(ref_face)) - D = num_dims(mesh_geom) - node_coordinates_geom = node_coordinates(mesh_geom) - ref_faces_geom = reference_faces(mesh_geom,D) - face_nodes_geom = face_nodes(mesh_geom,D) - face_ref_id_geom = face_reference_id(mesh_geom,D) - nfaces = length(face_ref_id_geom) - # We need the same order in all directions - # for this to make sense - my_order = order(ref_face) - ref_faces_inter = map(r_geom->lagrangian_reference_face(geometry(r_geom),order=my_order),ref_faces_geom) - s_ref = map(ref_faces_geom,ref_faces_inter) do r_geom,r - m = num_nodes(r) - n = num_nodes(r_geom) - f = shape_functions(r_geom) - x = node_coordinates(r) - tabulation_matrix(f)(value,x) - end - node_coordinates_inter = node_coordinates(ref_face) - node_coordinates_aux = map(xi->map(xii->round(Int,my_order*xii),xi),node_coordinates_inter) - face_nodes_inter = Vector{Vector{Int}}(undef,nfaces) - for face in 1:nfaces - ref_id_geom = face_ref_id_geom[face] - s = s_ref[ref_id_geom] - nodes_geom = face_nodes_geom[face] - nnodes, nnodes_geom = size(s) - x_mapped = map(1:nnodes) do i - x = zero(eltype(node_coordinates_inter)) - for k in 1:nnodes_geom - x += node_coordinates_geom[nodes_geom[k]]*s[i,k] - end - map(xi->round(Int,my_order*xi),x) - end - my_nodes = indexin(x_mapped,node_coordinates_aux) - face_nodes_inter[face] = my_nodes - end - ref_inter = - chain = Chain(; - num_dims=Val(D), - node_coordinates=node_coordinates_inter, - face_nodes = face_nodes_inter, - face_reference_id = face_ref_id_geom, - reference_faces = ref_faces_inter, - ) - mesh = mesh_from_chain(chain) - mesh_complex, = complexify_mesh(mesh) - if has_physical_groups(mesh_geom) - mesh_complex = setproperties(mesh_complex,physical_groups=physical_groups(mesh_geom)) - end - mesh_complex -end - -const INVALID_ID = 0 - -function default_gmsh_options() - [ - "General.Terminal"=>1, - "Mesh.SaveAll"=>1, - "Mesh.MedImportGroupsOfNodes"=>1 - ] -end - -function with_gmsh(f;options=default_gmsh_options()) - gmsh.initialize() - for (k,v) in options - gmsh.option.setNumber(k,v) - end - try - return f() - finally - gmsh.finalize() - end -end - -function mesh_from_gmsh(file;complexify=true,topology=true,renumber=true,kwargs...) - @assert ispath(file) "File not found: $(file)" - with_gmsh(;kwargs...) do - gmsh.open(file) - renumber && gmsh.model.mesh.renumberNodes() - renumber && gmsh.model.mesh.renumberElements() - mesh_from_gmsh_module(;complexify,topology) - end -end - -function mesh_from_gmsh_module(;complexify=true,topology=true) - entities = gmsh.model.getEntities() - nodeTags, coord, parametricCoord = gmsh.model.mesh.getNodes() - - # find num_dims - ddim = -1 - for e in entities - ddim = max(ddim,e[1]) - end - if ddim == -1 - error("No entities in the msh file.") - end - D = ddim - - # find embedded_dimension - dtouched = [false,false,false] - for node in nodeTags - if !(coord[(node-1)*3+1] + 1 ≈ 1) - dtouched[1] = true - end - if !(coord[(node-1)*3+2] + 1 ≈ 1) - dtouched[2] = true - end - if !(coord[(node-1)*3+3] + 1 ≈ 1) - dtouched[3] = true - end - end - if dtouched[3] - adim = 3 - elseif dtouched[2] - adim = 2 - elseif dtouched[1] - adim = 1 - else - adim = 0 - end - - # Setup node coords - nmin = minimum(nodeTags) - nmax = maximum(nodeTags) - nnodes = length(nodeTags) - if !(nmax == nnodes && nmin == 1) - error("Only consecutive node tags allowed.") - end - my_node_to_coords = zeros(SVector{adim,Float64},nnodes) - m = zero(MVector{adim,Float64}) - for node in nodeTags - for j in 1:adim - k = (node-1)*3 + j - xj = coord[k] - m[j] = xj - end - my_node_to_coords[node] = m - end - - # Setup face nodes - offsets = zeros(Int32,D+1) - my_face_nodes = Vector{JaggedArray{Int32,Int32}}(undef,D+1) - for d in 0:D - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) - ndfaces = 0 - for t in 1:length(elemTypes) - ndfaces += length(elemTags[t]) - end - if ndfaces != 0 - nmin::Int = minimum( minimum, elemTags ) - nmax::Int = maximum( maximum, elemTags ) - if !( (nmax-nmin+1) == ndfaces) - error("Only consecutive elem tags allowed.") - end - offsets[d+1] = nmin-1 - end - ptrs = zeros(Int32,ndfaces+1) - dface = 0 - for t in 1:length(elemTypes) - elementName, dim, order, numNodes, nodeCoord = - gmsh.model.mesh.getElementProperties(elemTypes[t]) - for e in 1:length(elemTags[t]) - dface += 1 - ptrs[dface+1] = numNodes - end - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = zeros(Int32,ndata) - dface = 1 - for t in 1:length(elemTypes) - p = ptrs[dface]-Int32(1) - for (i,node) in enumerate(nodeTags[t]) - data[p+i] = node - end - dface += length(elemTags[t]) - end - my_face_nodes[d+1] = JaggedArray(data,ptrs) - end - - # Setup face_reference_id - my_face_reference_id = Vector{Vector{Int32}}(undef,D+1) - for d in 0:D - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) - ndfaces = length(my_face_nodes[d+1]) - dface_to_refid = zeros(Int8,ndfaces) - refid = 0 - dface = 0 - for t in 1:length(elemTypes) - refid += 1 - for e in 1:length(elemTags[t]) - dface += 1 - dface_to_refid[dface] = refid - end - end - my_face_reference_id[d+1] = dface_to_refid - end - - # Setup reference faces - my_reference_faces = () - for d in D:-1:0 - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) - refdfaces = () - for t in 1:length(elemTypes) - refface = reference_face_from_gmsh_eltype(elemTypes[t]) - refdfaces = (refdfaces...,refface) - end - if refdfaces == () - refdfaces = reference_faces(boundary(first(first(my_reference_faces))),d) - end - my_reference_faces = (refdfaces,my_reference_faces...) - end - - ## Setup periodic nodes - node_to_master_node = fill(Int32(INVALID_ID),nnodes) - for (dim,tag) in entities - tagMaster, nodeTags, nodeTagsMaster, = gmsh.model.mesh.getPeriodicNodes(dim,tag) - for i in 1:length(nodeTags) - node = nodeTags[i] - master_node = nodeTagsMaster[i] - node_to_master_node[node] = master_node - end - end - pnode_to_node = Int32.(findall(i->i!=INVALID_ID,node_to_master_node)) - pnode_to_master = node_to_master_node[pnode_to_node] - periodic_nodes = pnode_to_node => pnode_to_master - - # Setup physical groups - my_groups = [ Dict{String,Vector{Int32}}() for d in 0:D] - for d in 0:D - offset = Int32(offsets[d+1]) - dimTags = gmsh.model.getPhysicalGroups(d) - for (dim,tag) in dimTags - @boundscheck @assert dim == d - g_entities = gmsh.model.getEntitiesForPhysicalGroup(dim,tag) - ndfaces_in_physical_group = 0 - for entity in g_entities - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(dim,entity) - for t in 1:length(elemTypes) - ndfaces_in_physical_group += length(elemTags[t]) - end - end - dfaces_in_physical_group = zeros(Int32,ndfaces_in_physical_group) - ndfaces_in_physical_group = 0 - for entity in g_entities - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(dim,entity) - for t in 1:length(elemTypes) - for etag in elemTags[t] - ndfaces_in_physical_group += 1 - dfaces_in_physical_group[ndfaces_in_physical_group] = Int32(etag)-offset - end - end - end - groupname = gmsh.model.getPhysicalName(dim,tag) - my_groups[d+1][groupname] = dfaces_in_physical_group - end - end - mesh = Mesh(; - num_dims=Val(D), - node_coordinates = my_node_to_coords, - face_nodes = my_face_nodes, - face_reference_id = my_face_reference_id, - reference_faces = my_reference_faces, - physical_groups = my_groups, - periodic_nodes, - ) - - if complexify - mesh, _ = complexify_mesh(mesh) - if topology - topo = topology_from_mesh(mesh) - mesh = setproperties(mesh,topology=topo) - end - end - mesh -end - -function reference_face_from_gmsh_eltype(eltype) - if eltype == 1 - order = 1 - geom = unit_n_cube(Val(1)) - lib_to_gmsh = [1,2] - elseif eltype == 2 - order = 1 - geom = unit_simplex(Val(2)) - lib_to_gmsh = [1,2,3] - elseif eltype == 3 - order = 1 - geom = unit_n_cube(Val(2)) - lib_to_gmsh = [1,2,4,3] - elseif eltype == 4 - order = 1 - geom = unit_simplex(Val(3)) - lib_to_gmsh = [1,2,3,4] - elseif eltype == 5 - order = 1 - lib_to_gmsh = [1,2,4,3,5,6,8,7] - elseif eltype == 15 - order = 1 - geom = unit_n_cube(Val(0)) - lib_to_gmsh = [1] - elseif eltype == 8 - order = 2 - geom = unit_n_cube(Val(1)) - lib_to_gmsh = [1,3,2] - elseif eltype == 9 - order = 2 - geom = unit_simplex(Val(2)) - lib_to_gmsh = [1,4,2,6,5,3] - else - en, = gmsh.model.mesh.getElementProperties(eltype) - error("Unsupported element type. elemType: $eltype ($en)") - end - lagrangian_reference_face(geom;order,lib_to_user_nodes=lib_to_gmsh) -end - -function intersection!(a,b,na,nb) - function findeq!(i,a,b,nb) - for j in 1:nb - if a[i] == b[j] - return - end - end - a[i] = INVALID_ID - return - end - for i in 1:na - if a[i] == INVALID_ID - continue - end - findeq!(i,a,b,nb) - end -end - -function same_valid_ids(a,b) - function is_subset(a,b) - for i in 1:length(a) - v = a[i] - if v == INVALID_ID - continue - end - c = find_eq(v,b) - if c == false; return false; end - end - return true - end - function find_eq(v,b) - for vs in b - if v == vs - return true - end - end - return false - end - c = is_subset(a,b) - if c == false; return false; end - c = is_subset(b,a) - if c == false; return false; end - return true -end - -function complexify_mesh(mesh) - Ti = Int32 - T = JaggedArray{Ti,Ti} - D = num_dims(mesh) - oldface_to_newvertices = Vector{T}(undef,D+1) - newvertex_to_oldfaces = Vector{T}(undef,D+1) - newface_incidence = Matrix{T}(undef,D+1,D+1) - nnewfaces = zeros(Int,D+1) - newface_refid = Vector{Vector{Ti}}(undef,D+1) - newreffaces = Vector{Any}(undef,D+1) - newface_nodes = Vector{T}(undef,D+1) - old_to_new = Vector{Vector{Ti}}(undef,D+1) - node_to_newvertex, n_new_vertices = find_node_to_vertex(mesh) # Optimizable for linear meshes - for d in 0:D - oldface_to_newvertices[d+1] = fill_face_vertices(mesh,d,node_to_newvertex) # Optimizable for linear meshes - newvertex_to_oldfaces[d+1] = generate_face_coboundary(oldface_to_newvertices[d+1],n_new_vertices) # Optimizable for linear meshes - end - newface_incidence[D+1,0+1] = oldface_to_newvertices[D+1] - newface_incidence[0+1,D+1] = newvertex_to_oldfaces[D+1] - nnewfaces[D+1] = length(oldface_to_newvertices[D+1]) - newface_refid[D+1] = face_reference_id(mesh,D) - newreffaces[D+1] = reference_faces(mesh,D) - newface_nodes[D+1] = face_nodes(mesh,D) - old_to_new[D+1] = collect(Ti,1:length(newface_nodes[D+1])) - # TODO optimize for d==0 - for d in (D-1):-1:0 - n = d+1 - new_nface_to_new_vertices = newface_incidence[n+1,0+1] - new_vertex_to_new_nfaces = newface_incidence[0+1,n+1] - old_dface_to_new_vertices = oldface_to_newvertices[d+1] - new_vertex_to_old_dfaces = newvertex_to_oldfaces[d+1] - new_nface_to_nrefid = newface_refid[n+1] - old_dface_to_drefid = face_reference_id(mesh,d) - drefid_to_ref_dface = reference_faces(mesh,d) - old_dface_to_nodes = face_nodes(mesh,d) - new_nface_to_nodes = newface_nodes[n+1] - nrefid_to_ldface_to_lvertices = map(a->face_incidence(topology(boundary(geometry(a))),d,0),newreffaces[n+1]) - nrefid_to_ldface_to_lnodes = map(a->face_nodes(boundary(a),d),newreffaces[n+1]) - nrefid_to_ldface_to_drefrefid = map(a->face_reference_id(boundary(a),d),newreffaces[n+1]) - nrefid_to_drefrefid_to_ref_dface = map(a->reference_faces(boundary(a),d),newreffaces[n+1]) - new_nface_to_new_dfaces, n_new_dfaces, old_dface_to_new_dface = generate_face_boundary( - new_nface_to_new_vertices, - new_vertex_to_new_nfaces, - old_dface_to_new_vertices, - new_vertex_to_old_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_lvertices) - new_dface_to_new_vertices = generate_face_vertices( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_new_vertices, - new_nface_to_new_vertices, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_lvertices) - new_vertex_to_new_dfaces = generate_face_coboundary(new_dface_to_new_vertices,n_new_vertices) - new_dface_to_nodes = generate_face_vertices( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_nodes, - new_nface_to_nodes, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_lnodes) - new_dface_to_new_drefid, new_refid_to_ref_dface = generate_reference_faces( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_drefid, - drefid_to_ref_dface, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_drefrefid, - nrefid_to_drefrefid_to_ref_dface) - newface_incidence[n+1,d+1] = new_nface_to_new_dfaces - newface_incidence[d+1,0+1] = new_dface_to_new_vertices - newface_incidence[0+1,d+1] = new_vertex_to_new_dfaces - newface_refid[d+1] = new_dface_to_new_drefid - newreffaces[d+1] = new_refid_to_ref_dface - nnewfaces[d+1] = n_new_dfaces - newface_nodes[d+1] = new_dface_to_nodes - old_to_new[d+1] = old_dface_to_new_dface - end - node_to_coords = node_coordinates(mesh) - new_mesh = Mesh(; - num_dims = Val(D), - node_coordinates=node_to_coords, - face_nodes=newface_nodes, - face_reference_id=newface_refid, - reference_faces=Tuple(newreffaces) - ) - if has_physical_groups(mesh) - old_physical_groups = physical_groups(mesh) - new_physical_groups = [ Dict{String,Vector{Int32}}() for d in 0:D] # TODO hardcoded - for d in 0:D - old_groups = old_physical_groups[d+1] - for (group_name,old_group_faces) in old_groups - new_group_faces = similar(old_group_faces) - new_group_faces .= old_to_new[d+1][old_group_faces] - new_physical_groups[d+1][group_name] = new_group_faces - end - end - new_mesh = setproperties(new_mesh,physical_groups=new_physical_groups) - end - if has_periodic_nodes(mesh) - new_mesh = setproperties(new_mesh;periodic_nodes=periodic_nodes(mesh)) - end - new_mesh, old_to_new -end - -function generate_face_vertices( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_new_vertices, - new_nface_to_new_vertices, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_lvertices - ) - - Ti = eltype(eltype(old_dface_to_new_vertices)) - new_dface_to_touched = fill(false,n_new_dfaces) - new_dface_to_new_vertices_ptrs = zeros(Ti,n_new_dfaces+1) - n_old_dfaces = length(old_dface_to_new_dface) - for old_dface in 1:n_old_dfaces - new_dface = old_dface_to_new_dface[old_dface] - new_vertices = old_dface_to_new_vertices[old_dface] - new_dface_to_new_vertices_ptrs[new_dface+1] = length(new_vertices) - new_dface_to_touched[new_dface] = true - end - n_new_nfaces = length(new_nface_to_new_vertices) - for new_nface in 1:n_new_nfaces - nrefid = new_nface_to_nrefid[new_nface] - ldface_to_lvertices = nrefid_to_ldface_to_lvertices[nrefid] - ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] - n_ldfaces = length(ldface_to_new_dface) - for ldface in 1:n_ldfaces - new_dface = ldface_to_new_dface[ldface] - if new_dface_to_touched[new_dface] - continue - end - lvertices = ldface_to_lvertices[ldface] - new_dface_to_new_vertices_ptrs[new_dface+1] = length(lvertices) - new_dface_to_touched[new_dface] = true - end - - end - length_to_ptrs!(new_dface_to_new_vertices_ptrs) - ndata = new_dface_to_new_vertices_ptrs[end]-1 - new_dface_to_new_vertices_data = zeros(Ti,ndata) - new_dface_to_new_vertices = JaggedArray(new_dface_to_new_vertices_data,new_dface_to_new_vertices_ptrs) - fill!(new_dface_to_touched,false) - for old_dface in 1:n_old_dfaces - new_dface = old_dface_to_new_dface[old_dface] - new_vertices_in = old_dface_to_new_vertices[old_dface] - new_vertices_out = new_dface_to_new_vertices[new_dface] - for i in 1:length(new_vertices_in) - new_vertices_out[i] = new_vertices_in[i] - end - new_dface_to_touched[new_dface] = true - end - for new_nface in 1:n_new_nfaces - nrefid = new_nface_to_nrefid[new_nface] - ldface_to_lvertices = nrefid_to_ldface_to_lvertices[nrefid] - ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] - n_ldfaces = length(ldface_to_new_dface) - new_vertices_in = new_nface_to_new_vertices[new_nface] - for ldface in 1:n_ldfaces - new_dface = ldface_to_new_dface[ldface] - if new_dface_to_touched[new_dface] - continue - end - new_vertices_out = new_dface_to_new_vertices[new_dface] - lvertices = ldface_to_lvertices[ldface] - for i in 1:length(lvertices) - new_vertices_out[i] = new_vertices_in[lvertices[i]] - end - new_dface_to_touched[new_dface] = true - end - - end - new_dface_to_new_vertices -end - -function generate_reference_faces( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_drefid, - drefid_to_ref_dface, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_drefrefid, - nrefid_to_drefrefid_to_ref_dface) - - i_to_ref_dface = collect(Any,drefid_to_ref_dface) - drefid_to_i = collect(1:length(drefid_to_ref_dface)) - i = length(i_to_ref_dface) - Ti = Int32 - nrefid_to_drefrefid_to_i = map(a->zeros(Ti,length(a)),nrefid_to_drefrefid_to_ref_dface) - for (nrefid,drefrefid_to_ref_dface) in enumerate(nrefid_to_drefrefid_to_ref_dface) - for (drefrefid, ref_dface) in enumerate(drefrefid_to_ref_dface) - push!(i_to_ref_dface,ref_dface) - i += 1 - nrefid_to_drefrefid_to_i[nrefid][drefrefid] = i - end - end - u_to_ref_dface = unique(i_to_ref_dface) - i_to_u = indexin(i_to_ref_dface,u_to_ref_dface) - new_dface_to_u = zeros(Ti,n_new_dfaces) - new_dface_to_touched = fill(false,n_new_dfaces) - for (old_dface,new_dface) in enumerate(old_dface_to_new_dface) - drefid = old_dface_to_drefid[old_dface] - i = drefid_to_i[drefid] - u = i_to_u[i] - new_dface_to_u[new_dface] = u - new_dface_to_touched[new_dface] = true - end - for (new_nface,nrefid) in enumerate(new_nface_to_nrefid) - ldface_to_drefrefid = nrefid_to_ldface_to_drefrefid[nrefid] - ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] - drefrefid_to_i = nrefid_to_drefrefid_to_i[nrefid] - for (ldface,new_dface) in enumerate(ldface_to_new_dface) - new_dface = ldface_to_new_dface[ldface] - if new_dface_to_touched[new_dface] - continue - end - drefrefid = ldface_to_drefrefid[ldface] - i = drefrefid_to_i[drefrefid] - u = i_to_u[i] - new_dface_to_u[new_dface] = u - new_dface_to_touched[new_dface] = true - end - end - new_dface_to_u, Tuple(u_to_ref_dface) -end - -function generate_face_boundary( - Dface_to_vertices, - vertex_to_Dfaces, - dface_to_vertices, - vertex_to_dfaces, - Dface_to_refid, - Drefid_to_ldface_to_lvertices) - - # Count - ndfaces = length(dface_to_vertices) - nDfaces = length(Dface_to_vertices) - nvertices = length(vertex_to_Dfaces) - maxldfaces = 0 - for ldface_to_lvertices in Drefid_to_ldface_to_lvertices - maxldfaces = max(maxldfaces,length(ldface_to_lvertices)) - end - maxDfaces = 0 - for vertex in 1:length(vertex_to_Dfaces) - Dfaces = vertex_to_Dfaces[vertex] - maxDfaces = max(maxDfaces,length(Dfaces)) - end - # Allocate output - ptrs = zeros(Int32,nDfaces+1) - for Dface in 1:nDfaces - Drefid = Dface_to_refid[Dface] - ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] - ptrs[Dface+1] = length(ldface_to_lvertices) - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = fill(Int32(INVALID_ID),ndata) - Dface_to_dfaces = GenericJaggedArray(data,ptrs) - # Main loop - Dfaces1 = fill(Int32(INVALID_ID),maxDfaces) - Dfaces2 = fill(Int32(INVALID_ID),maxDfaces) - ldfaces1 = fill(Int32(INVALID_ID),maxDfaces) - nDfaces1 = 0 - nDfaces2 = 0 - newdface = Int32(ndfaces) - old_to_new = collect(Int32,1:ndfaces) - for Dface in 1:nDfaces - Drefid = Dface_to_refid[Dface] - ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] - lvertex_to_vertex = Dface_to_vertices[Dface] - ldface_to_dface = Dface_to_dfaces[Dface] - for (ldface,lvertices) in enumerate(ldface_to_lvertices) - # Do nothing if this local face has already been processed by - # a neighbor - if ldface_to_dface[ldface] != Int32(INVALID_ID) - continue - end - # Find if there is already a global d-face for this local d-face - # if yes, then use the global id of this d-face - # if not, create a new one - dface2 = Int32(INVALID_ID) - fill!(Dfaces1,Int32(INVALID_ID)) - fill!(Dfaces2,Int32(INVALID_ID)) - vertices = view(lvertex_to_vertex,lvertices) - for (i,lvertex) in enumerate(lvertices) - vertex = lvertex_to_vertex[lvertex] - dfaces = vertex_to_dfaces[vertex] - for dface1 in dfaces - vertices1 = dface_to_vertices[dface1] - if same_valid_ids(vertices,vertices1) - dface2 = dface1 - break - end - end - if dface2 != Int32(INVALID_ID) - break - end - end - if dface2 == Int32(INVALID_ID) - newdface += Int32(1) - dface2 = newdface - end - # Find all D-faces around this local d-face - for (i,lvertex) in enumerate(lvertices) - vertex = lvertex_to_vertex[lvertex] - Dfaces = vertex_to_Dfaces[vertex] - if i == 1 - copyto!(Dfaces1,Dfaces) - nDfaces1 = length(Dfaces) - else - copyto!(Dfaces2,Dfaces) - nDfaces2 = length(Dfaces) - intersection!(Dfaces1,Dfaces2,nDfaces1,nDfaces2) - end - end - # Find their correspondent local d-face and set the d-face - for Dface1 in Dfaces1 - if Dface1 != INVALID_ID - Drefid1 = Dface_to_refid[Dface1] - lvertex1_to_vertex1 = Dface_to_vertices[Dface1] - ldface1_to_lvertices1 = Drefid_to_ldface_to_lvertices[Drefid1] - ldface2 = Int32(INVALID_ID) - for (ldface1,lvertices1) in enumerate(ldface1_to_lvertices1) - vertices1 = view(lvertex1_to_vertex1,lvertices1) - if same_valid_ids(vertices,vertices1) - ldface2 = ldface1 - break - end - end - @boundscheck @assert ldface2 != INVALID_ID - Dface_to_dfaces[Dface1][ldface2] = dface2 - end - end - end # (ldface,lvertices) - end # Dface - Dface_to_dfaces, newdface, old_to_new -end - -function fill_face_vertices(mesh,d,node_to_vertex) - function barrier(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) - Ti = eltype(node_to_vertex) - nfaces = length(face_to_nodes) - face_to_vertices_ptrs = zeros(Ti,nfaces+1) - for face in 1:nfaces - nodes = face_to_nodes[face] - refid = face_to_refid[face] - lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] - nlvertices = length(lvertex_to_lnodes) - face_to_vertices_ptrs[face+1] = nlvertices - end - length_to_ptrs!(face_to_vertices_ptrs) - ndata = face_to_vertices_ptrs[end]-1 - face_to_vertices_data = zeros(Ti,ndata) - face_to_vertices = JaggedArray(face_to_vertices_data,face_to_vertices_ptrs) - for face in 1:nfaces - vertices = face_to_vertices[face] - nodes = face_to_nodes[face] - refid = face_to_refid[face] - lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] - for (lvertex,lnodes) in enumerate(lvertex_to_lnodes) - lnode = first(lnodes) - vertex = node_to_vertex[nodes[lnode]] - @boundscheck @assert vertex != INVALID_ID - vertices[lvertex] = vertex - end - end - face_to_vertices - end - face_to_nodes = face_nodes(mesh,d) - face_to_refid = face_reference_id(mesh,d) - refid_to_lvertex_to_lnodes = map(reference_faces(mesh,d)) do a - if num_dims(geometry(a)) != 0 - face_nodes(boundary(a),0) - else - [interior_nodes(a)] - end - end - barrier(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) -end - -function find_node_to_vertex(mesh) - function barrier!(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) - valid_id = one(eltype(node_to_vertex)) - for face in eachindex(face_to_nodes) - nodes = face_to_nodes[face] - refid = face_to_refid[face] - lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] - for lnodes in lvertex_to_lnodes - lnode = first(lnodes) - node = nodes[lnode] - node_to_vertex[node] = valid_id - end - end - end - Ti = Int32 - nnodes = num_nodes(mesh) - node_to_vertex = zeros(Ti,nnodes) - fill!(node_to_vertex,Ti(INVALID_ID)) - D = num_dims(mesh) - for d in 0:D - face_to_nodes = face_nodes(mesh,d) - if length(face_to_nodes) == 0 - continue - end - face_to_refid = face_reference_id(mesh,d) - refid_to_lvertex_to_lnodes = map(reference_faces(mesh,d)) do a - if num_dims(geometry(a)) != 0 - face_nodes(boundary(a),0) - else - [interior_nodes(a)] - end - end - barrier!(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) - end - vertex = Ti(0) - for node in eachindex(node_to_vertex) - if node_to_vertex[node] != Ti(INVALID_ID) - vertex += Ti(1) - node_to_vertex[node] = vertex - end - end - node_to_vertex, vertex -end - -function reference_topology_from_reference_face(refface) - geom = geometry(refface) - D = num_dims(geom) - if D != 0 - myboundary = geom |> boundary |> topology - else - myboundary = nothing - end - myperms = vertex_permutations(geom) - Object(;num_dims=Val(D),boundary=myboundary,vertex_permutations=myperms) -end - -function topology_from_mesh(mesh) - # Assumes that the input is a cell complex - T = JaggedArray{Int32,Int32} - D = num_dims(mesh) - my_face_incidence = Matrix{T}(undef,D+1,D+1) - my_face_reference_id = [ face_reference_id(mesh,d) for d in 0:D ] - my_reference_faces = Tuple([ map(reference_topology_from_reference_face,reference_faces(mesh,d)) for d in 0:D ]) - my_face_permutation_ids = Matrix{T}(undef,D+1,D+1) - topo = Topology(; - face_incidence=my_face_incidence, - face_reference_id=my_face_reference_id, - reference_faces=my_reference_faces, - face_permutation_ids=my_face_permutation_ids, - ) - for d in 0:D - fill_face_interior_mesh_topology!(topo,mesh,d) - end - for d in 1:D - fill_face_vertices_mesh_topology!(topo,mesh,d) - fill_face_coboundary_mesh_topology!(topo,mesh,d,0) - end - for d in 1:(D-1) - for n in (D-d):-1:1 - m = n+d - fill_face_boundary_mesh_topology!(topo,mesh,m,n) - fill_face_coboundary_mesh_topology!(topo,mesh,m,n) - end - end - for d in 0:D - for n in 0:d - fill_face_permutation_ids!(topo,d,n) - end - end - topo -end - -function fill_face_interior_mesh_topology!(mesh_topology,mesh,d) - n = num_faces(mesh,d) - ptrs = collect(Int32,1:(n+1)) - data = collect(Int32,1:n) - mesh_topology.face_incidence[d+1,d+1] = JaggedArray(data,ptrs) -end - -function generate_face_coboundary(nface_to_mfaces,nmfaces) - ptrs = zeros(Int32,nmfaces+1) - nnfaces = length(nface_to_mfaces) - for nface in 1:nnfaces - mfaces = nface_to_mfaces[nface] - for mface in mfaces - ptrs[mface+1] += Int32(1) - end - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = zeros(Int32,ndata) - for nface in 1:nnfaces - mfaces = nface_to_mfaces[nface] - for mface in mfaces - p = ptrs[mface] - data[p] = nface - ptrs[mface] += Int32(1) - end - end - rewind_ptrs!(ptrs) - mface_to_nfaces = JaggedArray(data,ptrs) - mface_to_nfaces -end - -function fill_face_coboundary_mesh_topology!(topology,mesh,n,m) - nmfaces = num_faces(mesh,m) - nface_to_mfaces = face_incidence(topology,n,m) - face_incidence(topology)[m+1,n+1] = generate_face_coboundary(nface_to_mfaces,nmfaces) -end - -function fill_face_vertices_mesh_topology!(topo,mesh,d) - function barrier(nnodes,vertex_to_nodes,dface_to_nodes,dface_to_refid,refid_to_lvertex_to_lnodes) - vertex_to_node = JaggedArray(vertex_to_nodes).data - #if vertex_to_node == 1:nnodes - # return dface_to_nodes - #end - node_to_vertex = zeros(Int32,nnodes) - nvertices = length(vertex_to_nodes) - node_to_vertex[vertex_to_node] = 1:nvertices - ndfaces = length(dface_to_nodes) - dface_to_vertices_ptrs = zeros(Int32,ndfaces+1) - for dface in 1:ndfaces - refid = dface_to_refid[dface] - nlvertices = length(refid_to_lvertex_to_lnodes[refid]) - dface_to_vertices_ptrs[dface+1] = nlvertices - end - length_to_ptrs!(dface_to_vertices_ptrs) - ndata = dface_to_vertices_ptrs[end]-1 - dface_to_vertices_data = zeros(Int32,ndata) - for dface in 1:ndfaces - refid = dface_to_refid[dface] - lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] - nlvertices = length(lvertex_to_lnodes) - lnode_to_node = dface_to_nodes[dface] - offset = dface_to_vertices_ptrs[dface]-1 - for lvertex in 1:nlvertices - lnode = first(lvertex_to_lnodes[lvertex]) - node = lnode_to_node[lnode] - vertex = node_to_vertex[node] - dface_to_vertices_data[offset+lvertex] = vertex - end - end - dface_to_vertices = JaggedArray(dface_to_vertices_data,dface_to_vertices_ptrs) - end - nnodes = num_nodes(mesh) - vertex_to_nodes = face_nodes(mesh,0) - dface_to_nodes = face_nodes(mesh,d) - dface_to_refid = face_reference_id(mesh,d) - refid_refface = reference_faces(mesh,d) - refid_to_lvertex_to_lnodes = map(refface->face_nodes(boundary(refface),0),refid_refface) - face_incidence(topo)[d+1,0+1] = barrier(nnodes,vertex_to_nodes,dface_to_nodes,dface_to_refid,refid_to_lvertex_to_lnodes) -end - -function fill_face_boundary_mesh_topology!(topo,mesh,D,d) - function barrier( - Dface_to_vertices, - vertex_to_Dfaces, - dface_to_vertices, - vertex_to_dfaces, - Dface_to_refid, - Drefid_to_ldface_to_lvertices) - - # Count - ndfaces = length(dface_to_vertices) - nDfaces = length(Dface_to_vertices) - # Allocate output - ptrs = zeros(Int32,nDfaces+1) - for Dface in 1:nDfaces - Drefid = Dface_to_refid[Dface] - ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] - ptrs[Dface+1] = length(ldface_to_lvertices) - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = fill(Int32(INVALID_ID),ndata) - Dface_to_dfaces = JaggedArray(data,ptrs) - for Dface in 1:nDfaces - Drefid = Dface_to_refid[Dface] - ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] - lvertex_to_vertex = Dface_to_vertices[Dface] - ldface_to_dface = Dface_to_dfaces[Dface] - for (ldface,lvertices) in enumerate(ldface_to_lvertices) - # Find the global d-face for this local d-face - dface2 = Int32(INVALID_ID) - vertices = view(lvertex_to_vertex,lvertices) - for (i,lvertex) in enumerate(lvertices) - vertex = lvertex_to_vertex[lvertex] - dfaces = vertex_to_dfaces[vertex] - for dface1 in dfaces - vertices1 = dface_to_vertices[dface1] - if same_valid_ids(vertices,vertices1) - dface2 = dface1 - break - end - end - if dface2 != Int32(INVALID_ID) - break - end - end - @boundscheck begin - msg = """ - - - Error in: topology_from_mesh - - The given mesh is provably not a cell complex. - """ - @assert dface2 != Int32(INVALID_ID) msg - end - ldface_to_dface[ldface] = dface2 - end # (ldface,lvertices) - end # Dface - Dface_to_dfaces - end - Dface_to_vertices = face_incidence(topo,D,0) - vertex_to_Dfaces = face_incidence(topo,0,D) - dface_to_vertices = face_incidence(topo,d,0) - vertex_to_dfaces = face_incidence(topo,0,d) - Dface_to_refid = face_reference_id(topo,D) - refid_refface = reference_faces(topo,D) - Drefid_to_ldface_to_lvertices = map(refface->face_incidence(boundary(refface),d,0),refid_refface) - Dface_to_dfaces = barrier( - Dface_to_vertices, - vertex_to_Dfaces, - dface_to_vertices, - vertex_to_dfaces, - Dface_to_refid, - Drefid_to_ldface_to_lvertices) - topo.face_incidence[D+1,d+1] = Dface_to_dfaces -end - -function fill_face_permutation_ids!(top,D,d) - function barrier!( - cell_to_lface_to_pindex, - cell_to_lface_to_face, - cell_to_cvertex_to_vertex, - cell_to_ctype, - ctype_to_lface_to_cvertices, - face_to_fvertex_to_vertex, - face_to_ftype, - ftype_to_pindex_to_cfvertex_to_fvertex) - - ncells = length(cell_to_lface_to_face) - for cell in 1:ncells - ctype = cell_to_ctype[cell] - lface_to_cvertices = ctype_to_lface_to_cvertices[ctype] - a = cell_to_lface_to_face.ptrs[cell]-1 - c = cell_to_cvertex_to_vertex.ptrs[cell]-1 - for (lface,cfvertex_to_cvertex) in enumerate(lface_to_cvertices) - face = cell_to_lface_to_face.data[a+lface] - ftype = face_to_ftype[face] - b = face_to_fvertex_to_vertex.ptrs[face]-1 - pindex_to_cfvertex_to_fvertex = ftype_to_pindex_to_cfvertex_to_fvertex[ftype] - pindexfound = false - for (pindex, cfvertex_to_fvertex) in enumerate(pindex_to_cfvertex_to_fvertex) - found = true - for (cfvertex,fvertex) in enumerate(cfvertex_to_fvertex) - vertex1 = face_to_fvertex_to_vertex.data[b+fvertex] - cvertex = cfvertex_to_cvertex[cfvertex] - vertex2 = cell_to_cvertex_to_vertex.data[c+cvertex] - if vertex1 != vertex2 - found = false - break - end - end - if found - cell_to_lface_to_pindex.data[a+lface] = pindex - pindexfound = true - break - end - end - @assert pindexfound "Valid pindex not found" - end - end - end - @assert D >= d - cell_to_lface_to_face = JaggedArray(face_incidence(top,D,d)) - data = similar(cell_to_lface_to_face.data,Int8) - ptrs = cell_to_lface_to_face.ptrs - cell_to_lface_to_pindex = JaggedArray(data,ptrs) - if d == D || d == 0 - fill!(cell_to_lface_to_pindex.data,Int8(1)) - top.face_permutation_ids[D+1,d+1] = cell_to_lface_to_pindex - return top - end - face_to_fvertex_to_vertex = JaggedArray(face_incidence(top,d,0)) - face_to_ftype = face_reference_id(top,d) - ref_dfaces = reference_faces(top,d) - ftype_to_pindex_to_cfvertex_to_fvertex = map(vertex_permutations,ref_dfaces) - cell_to_cvertex_to_vertex = JaggedArray(face_incidence(top,D,0)) - cell_to_ctype = face_reference_id(top,D) - ref_Dfaces = reference_faces(top,D) - ctype_to_lface_to_cvertices = map(p->face_incidence(boundary(p),d,0),ref_Dfaces) - barrier!( - cell_to_lface_to_pindex, - cell_to_lface_to_face, - cell_to_cvertex_to_vertex, - cell_to_ctype, - ctype_to_lface_to_cvertices, - face_to_fvertex_to_vertex, - face_to_ftype, - ftype_to_pindex_to_cfvertex_to_fvertex) - top.face_permutation_ids[D+1,d+1] = cell_to_lface_to_pindex - top -end - -function bounding_box_from_domain(domain) - l = length(domain) - D = div(l,2) - pmin = SVector(ntuple(d->domain[2*(d-1)+1],Val(D))) - pmax = SVector(ntuple(d->domain[2*d],Val(D))) - (pmin,pmax) -end - -function domain_from_bounding_box(box) - l = sum(length,box) - ntuple(Val(l)) do i - vector = mod(i-1,2)+1 - component = div(i-1,2)+1 - box[vector][component] - end -end - -function cartesian_mesh(domain,cells_per_dir;boundary=true,complexify=true,simplexify=false,topology=true) - mesh = if boundary - if simplexify - structured_simplex_mesh_with_boundary(domain,cells_per_dir) - else - cartesian_mesh_with_boundary(domain,cells_per_dir) - end - else - if simplexify - chain = structured_simplex_chain(domain,cells_per_dir) - else - chain = cartesian_chain(domain,cells_per_dir) - end - mesh_from_chain(chain) - end - if complexify - mesh, = complexify_mesh(mesh) - if topology - topo = topology_from_mesh(mesh) - mesh = setproperties(mesh,topology=topo) - end - end - mesh -end - -function cartesian_mesh_with_boundary(domain,cells_per_dir) - function barrier( - D, - cell_to_nodes, - nnodes, - d_to_ldface_to_lnodes) - - node_to_n = zeros(Int32,nnodes) - for nodes in cell_to_nodes - for node in nodes - node_to_n[node] += Int32(1) - end - end - J = typeof(JaggedArray(Vector{Int32}[])) - face_to_nodes = Vector{J}(undef,D) - groups = [ Dict{String,Vector{Int32}}() for d in 0:(D-1) ] - ngroups = 0 - for d in 0:(D-1) - nmax = 2^d - ldface_to_lnodes = d_to_ldface_to_lnodes[d+1] - ndfaces = 0 - for nodes in cell_to_nodes - for (ldface,lnodes) in enumerate(ldface_to_lnodes) - isboundary = true - for lnode in lnodes - node = nodes[lnode] - if node_to_n[node] > nmax - isboundary = false - break - end - end - if isboundary - ndfaces += 1 - end - end - end - ptrs = zeros(Int32,ndfaces+1) - for dface in 1:ndfaces - ptrs[dface+1] += Int32(nmax) - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = zeros(Int32,ndata) - dface_to_physical_group = zeros(Int32,ndfaces) - ndfaces = 0 - for nodes in cell_to_nodes - for (ldface,lnodes) in enumerate(ldface_to_lnodes) - isboundary = true - for lnode in lnodes - node = nodes[lnode] - if node_to_n[node] > nmax - isboundary = false - break - end - end - if isboundary - ndfaces += 1 - group = ngroups + ldface - dface_to_physical_group[ndfaces] = group - p = ptrs[ndfaces]-Int32(1) - for (i,lnode) in enumerate(lnodes) - node = nodes[lnode] - data[p+i] = node - end - end - end - end - nldfaces = length(ldface_to_lnodes) - face_to_nodes[d+1] = JaggedArray(data,ptrs) - for ldface in 1:nldfaces - group = ngroups + ldface - group_name = "$(d)-face-$ldface" - faces_in_physical_group = findall(g->g==group,dface_to_physical_group) - groups[d+1][group_name] = faces_in_physical_group - end - ngroups += nldfaces - if d == (D-1) - groups[d+1]["boundary"] = 1:length(dface_to_physical_group) - end - end # d - ngroups += 1 - groups, face_to_nodes - end # barrier - chain = cartesian_chain(domain,cells_per_dir) - interior_mesh = mesh_from_chain(chain) - D = num_dims(interior_mesh) - cell_to_nodes = face_nodes(interior_mesh,D) - reference_cells = reference_faces(interior_mesh,D) - node_coords = node_coordinates(interior_mesh) - ref_cell = first(reference_cells) - refid_to_refface = reference_faces(boundary(ref_cell)) - nnodes = num_nodes(interior_mesh) - d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] - groups, face_to_nodes = barrier( - D, - cell_to_nodes, - nnodes, - d_to_ldface_to_lnodes) - face_to_refid = [ ones(Int8,length(face_to_nodes[d+1])) for d in 0:(D-1)] - mesh_face_nodes = push(face_to_nodes,face_nodes(interior_mesh,D)) - mesh_face_reference_id = push(face_to_refid,face_reference_id(interior_mesh,D)) - mesh_reference_faces = push(refid_to_refface,reference_faces(interior_mesh,D)) - mesh_groups = push(groups,physical_groups(interior_mesh,D)) - Mesh(; - num_dims=Val(D), - node_coordinates=node_coords, - face_nodes=mesh_face_nodes, - face_reference_id=mesh_face_reference_id, - reference_faces=mesh_reference_faces, - physical_groups=mesh_groups, - ) -end - -function cartesian_chain(domain,cells_per_dir) - box = bounding_box_from_domain(domain) - D = length(cells_per_dir) - nodes_per_dir = cells_per_dir .+ 1 - pmin = first(box) - pmax = last(box) - extent_per_dir = pmax .- pmin - h_per_dir = SVector(extent_per_dir ./ cells_per_dir) - nnodes = prod(nodes_per_dir) - ncells = prod(cells_per_dir) - nlnodes = 2^D - cell_nodes_ptrs = fill(Int32(nlnodes),ncells+1) - cell_nodes_ptrs[1] = 0 - length_to_ptrs!(cell_nodes_ptrs) - cell_nodes_data = zeros(Int32,ncells*nlnodes) - cell_nodes = JaggedArray(cell_nodes_data,cell_nodes_ptrs) - cell_cis = CartesianIndices(cells_per_dir) - cell_lis = LinearIndices(cells_per_dir) - node_cis = CartesianIndices(nodes_per_dir) - node_lis = LinearIndices(nodes_per_dir) - lnode_cis = CartesianIndices(ntuple(i->0:1,Val(D))) - for (cell_li,cell_ci) in enumerate(cell_cis) - nodes = cell_nodes[cell_li] - for (lnode_li,lnode_ci) in enumerate(lnode_cis) - node_ci = CartesianIndex(Tuple(cell_ci) .+ Tuple(lnode_ci)) - node_li = node_lis[node_ci] - nodes[lnode_li] = node_li - end - end - node_coords = zeros(SVector{D,Float64},nnodes) - for (node_li,node_ci) in enumerate(node_cis) - anchor = SVector(Tuple(node_ci) .- 1) - x = pmin .+ h_per_dir .* anchor - node_coords[node_li] = x - end - cell_reference_id = fill(Int32(1),ncells) - cell_geometry = unit_n_cube(Val(D)) - ref_cell = lagrangian_reference_face(cell_geometry) - reference_cells = [ref_cell] - interior_cells = collect(Int32,1:length(cell_nodes)) - groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) - chain = Chain(; - num_dims=Val(D), - node_coordinates=node_coords, - face_nodes=cell_nodes, - face_reference_id=cell_reference_id, - reference_faces=reference_cells, - physical_groups=groups, - ) - chain -end - -function structured_simplex_chain(domain,cells_per_dir) - box = bounding_box_from_domain(domain) - D = length(cells_per_dir) - nodes_per_dir = cells_per_dir .+ 1 - pmin = first(box) - pmax = last(box) - extent_per_dir = pmax .- pmin - h_per_dir = SVector(extent_per_dir ./ cells_per_dir) - nnodes = prod(nodes_per_dir) - nlnodes = 2^D - cell_geometry = unit_n_cube(Val(D)) - ref_simplex_mesh = simplexify_reference_geometry(cell_geometry) - rscell_to_lnodes = face_nodes(ref_simplex_mesh,D) - nrscells = length(rscell_to_lnodes) - nslnodes = length(first(rscell_to_lnodes)) - ncells = prod(cells_per_dir)*nrscells - cell_nodes_ptrs = fill(Int32(nslnodes),ncells+1) - cell_nodes_ptrs[1] = 0 - length_to_ptrs!(cell_nodes_ptrs) - ndata = cell_nodes_ptrs[end]-1 - cell_nodes_data = zeros(Int32,ndata) - cell_nodes = JaggedArray(cell_nodes_data,cell_nodes_ptrs) - cell_cis = CartesianIndices(cells_per_dir) - cell_lis = LinearIndices(cells_per_dir) - node_cis = CartesianIndices(nodes_per_dir) - node_lis = LinearIndices(nodes_per_dir) - lnode_cis = CartesianIndices(ntuple(i->0:1,Val(D))) - clnodes = zeros(Int,nlnodes) - scell = 0 - for (cell_li,cell_ci) in enumerate(cell_cis) - for (lnode_li,lnode_ci) in enumerate(lnode_cis) - node_ci = CartesianIndex(Tuple(cell_ci) .+ Tuple(lnode_ci)) - node_li = node_lis[node_ci] - clnodes[lnode_li] = node_li - end - for srcell in 1:nrscells - scell += 1 - nodes = cell_nodes[scell] - lnodes = rscell_to_lnodes[srcell] - for (i,lnode) in enumerate(lnodes) - nodes[i] = clnodes[lnode] - end - end - end - node_coords = zeros(SVector{D,Float64},nnodes) - for (node_li,node_ci) in enumerate(node_cis) - anchor = SVector(Tuple(node_ci) .- 1) - x = pmin .+ h_per_dir .* anchor - node_coords[node_li] = x - end - cell_reference_id = fill(Int32(1),ncells) - order = 1 - reference_cells = reference_faces(ref_simplex_mesh,D) - interior_cells = collect(Int32,1:length(cell_nodes)) - groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) - chain = Chain(; - num_dims=Val(D), - node_coordinates=node_coords, - face_nodes=cell_nodes, - face_reference_id=cell_reference_id, - reference_faces=reference_cells, - physical_groups=groups, - ) - chain -end - -function structured_simplex_mesh_with_boundary(domain,cells_per_dir) - function barrier( - D, - cell_to_nodes, - nnodes, - d_to_ldface_to_lnodes, - d_to_ldface_to_sldface_to_lnodes) - - node_to_n = zeros(Int32,nnodes) - for nodes in cell_to_nodes - for node in nodes - node_to_n[node] += Int32(1) - end - end - J = typeof(JaggedArray(Vector{Int32}[])) - face_to_nodes = Vector{J}(undef,D) - groups = [ Dict{String,Vector{Int32}}() for d in 0:(D-1) ] - ngroups = 0 - for d in 0:(D-1) - nmax = 2^d - ldface_to_lnodes = d_to_ldface_to_lnodes[d+1] - ldface_to_sldface_to_lnodes = d_to_ldface_to_sldface_to_lnodes[d+1] - ndfaces = 0 - for nodes in cell_to_nodes - for (ldface,lnodes) in enumerate(ldface_to_lnodes) - isboundary = true - for lnode in lnodes - node = nodes[lnode] - if node_to_n[node] > nmax - isboundary = false - break - end - end - if isboundary - ndfaces += length(ldface_to_sldface_to_lnodes[ldface]) - end - end - end - nslnodes = length(ldface_to_sldface_to_lnodes[begin][begin]) - ptrs = zeros(Int32,ndfaces+1) - for dface in 1:ndfaces - ptrs[dface+1] += Int32(nslnodes) - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = zeros(Int32,ndata) - dface_to_physical_group = zeros(Int32,ndfaces) - ndfaces = 0 - for nodes in cell_to_nodes - for (ldface,lnodes) in enumerate(ldface_to_lnodes) - isboundary = true - for lnode in lnodes - node = nodes[lnode] - if node_to_n[node] > nmax - isboundary = false - break - end - end - if isboundary - group = ngroups + ldface - sldface_to_lnodes = ldface_to_sldface_to_lnodes[ldface] - nsldfaces = length(sldface_to_lnodes) - for sldface in 1:nsldfaces - ndfaces += 1 - dface_to_physical_group[ndfaces] = group - p = ptrs[ndfaces]-Int32(1) - mylnodes = sldface_to_lnodes[sldface] - for (i,lnode) in enumerate(mylnodes) - node = nodes[lnode] - data[p+i] = node - end - end - end - end - end - nldfaces = length(ldface_to_lnodes) - face_to_nodes[d+1] = JaggedArray(data,ptrs) - for ldface in 1:nldfaces - group = ngroups + ldface - group_name = "$(d)-face-$ldface" - faces_in_physical_group = findall(g->g==group,dface_to_physical_group) - groups[d+1][group_name] = faces_in_physical_group - end - ngroups += nldfaces - if d == (D-1) - groups[d+1]["boundary"] = 1:length(dface_to_physical_group) - end - end # d - ngroups += 1 - groups, face_to_nodes - end # barrier - chain = cartesian_chain(domain,cells_per_dir) - interior_mesh = mesh_from_chain(chain) - D = num_dims(interior_mesh) - cell_to_nodes = face_nodes(interior_mesh,D) - reference_cells = reference_faces(interior_mesh,D) - ref_cell = first(reference_cells) - refid_to_refface = reference_faces(boundary(ref_cell)) - nnodes = num_nodes(interior_mesh) - - cell_geometry = unit_n_cube(Val(D)) - ref_simplex_mesh = simplexify_reference_geometry(cell_geometry) - d_to_ldface_to_sldface_to_lnodes = [ - [ face_nodes(ref_simplex_mesh,d)[physical_groups(ref_simplex_mesh,d)["$d-face-$ldface"]] - for ldface in 1:num_faces(boundary(ref_cell),d) ] for d in 0:(D-1)] - d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] - groups, face_to_nodes = barrier( - D, - cell_to_nodes, - nnodes, - d_to_ldface_to_lnodes, - d_to_ldface_to_sldface_to_lnodes - ) - simplex_chain = structured_simplex_chain(domain,cells_per_dir) - node_coords = node_coordinates(simplex_chain) - face_to_refid = [ ones(Int8,length(face_to_nodes[d+1])) for d in 0:(D-1)] - mesh_face_nodes = push(face_to_nodes,face_nodes(simplex_chain)) - mesh_face_reference_id = push(face_to_refid,face_reference_id(simplex_chain)) - mesh_reference_faces = reference_faces(ref_simplex_mesh) - mesh_groups = push(groups,physical_groups(simplex_chain)) - Mesh(; - num_dims=Val(D), - node_coordinates=node_coords, - face_nodes=mesh_face_nodes, - face_reference_id=mesh_face_reference_id, - reference_faces=mesh_reference_faces, - physical_groups=mesh_groups, - ) -end - -function mesh_from_chain(chain) - D = num_dims(chain) - cell_nodes = face_nodes(chain) - cell_reference_id = face_reference_id(chain) - reference_cells = reference_faces(chain) - node_coords = node_coordinates(chain) - face_to_nodes = Vector{typeof(cell_nodes)}(undef,D+1) - face_to_refid = Vector{typeof(cell_reference_id)}(undef,D+1) - for d in 0:D-1 - face_to_nodes[d+1] = Vector{Int}[] - face_to_refid[d+1] = Int[] - end - face_to_nodes[end] = cell_nodes - face_to_refid[end] = cell_reference_id - ref_cell = first(reference_cells) - ref_faces = reference_faces(boundary(ref_cell)) - refid_to_refface = push(ref_faces,reference_cells) - mesh = Mesh(; - num_dims=Val(D), - node_coordinates=node_coords, - face_nodes=face_to_nodes, - face_reference_id=face_to_refid, - reference_faces=refid_to_refface, - ) - if has_physical_groups(chain) - cell_groups = physical_groups(chain) - groups = [ typeof(cell_groups)() for d in 0:D] - groups[end] = cell_groups - mesh = setproperties(mesh,physical_groups=groups) - end - mesh -end - -function visualization_mesh_from_mesh(mesh,dim=num_dims(mesh);order=nothing,resolution=nothing) - function barrier( - refid_to_tabulation, - refid_to_scell_to_snodes, - refid_to_scell_to_srefid, - refid_to_srefid_to_oid, - refid_to_srefid_to_vrefface, - refid_to_snode_to_coords, - node_to_coords, - cell_to_nodes, - cell_to_refid, - ::Val{Dn}) where Dn - - ncells = length(cell_to_refid) - nvnodes = 0 - nvcells = 0 - for cell in 1:ncells - refid = cell_to_refid[cell] - nvnodes += length(refid_to_snode_to_coords[refid]) - nvcells += length(refid_to_scell_to_srefid[refid]) - - end - nrefids = length(refid_to_srefid_to_oid) - i_to_oid = reduce(vcat,refid_to_srefid_to_oid) - i_to_vrefface = reduce(vcat,refid_to_srefid_to_vrefface) - refid_to_srefid_to_i = Vector{Vector{Int}}(undef,nrefids) - i = 0 - for refid in 1:nrefids - srefid_to_oid = refid_to_srefid_to_oid[refid] - nsrefids = length(srefid_to_oid) - srefid_to_i = zeros(Int,nsrefids) - for srefid in 1:nsrefids - i += 1 - srefid_to_i[srefid] = i - end - refid_to_srefid_to_i[refid] = srefid_to_i - end - vrefid_to_oid = unique(i_to_oid) - i_to_vrefid = indexin(i_to_oid,vrefid_to_oid) - vrefid_to_i = indexin(vrefid_to_oid,i_to_oid) - vrefid_to_vrefface = i_to_vrefface[vrefid_to_i] - Tx = SVector{Dn,Float64} - vnode_to_coords = zeros(Tx,nvnodes) - vcell_to_vnodes_ptrs = zeros(Int32,nvcells+1) - vcell_to_vrefid = zeros(Int32,nvcells) - vcell_to_cell = zeros(Int32,nvcells) - cell_to_vnodes = fill(0:1,ncells) - vcell = 0 - for cell in 1:ncells - refid = cell_to_refid[cell] - scell_to_snodes = refid_to_scell_to_snodes[refid] - nscells = length(scell_to_snodes) - scell_to_srefid = refid_to_scell_to_srefid[refid] - srefid_to_i = refid_to_srefid_to_i[refid] - for scell in 1:nscells - srefid = scell_to_srefid[scell] - i = srefid_to_i[srefid] - vrefid = i_to_vrefid[i] - snodes = scell_to_snodes[scell] - vcell += 1 - vcell_to_vnodes_ptrs[vcell+1] = length(snodes) - vcell_to_vrefid[vcell] = vrefid - vcell_to_cell[vcell] = cell - end - end - length_to_ptrs!(vcell_to_vnodes_ptrs) - ndata = vcell_to_vnodes_ptrs[end]-1 - vcell_to_vnodes_data = zeros(Int32,ndata) - vcell = 0 - vnode = 0 - vnode_prev = 1 - for cell in 1:ncells - refid = cell_to_refid[cell] - scell_to_snodes = refid_to_scell_to_snodes[refid] - nscells = length(scell_to_snodes) - for scell in 1:nscells - snodes = scell_to_snodes[scell] - vcell += 1 - p = vcell_to_vnodes_ptrs[vcell] - for (i,snode) in enumerate(snodes) - vcell_to_vnodes_data[p-1+i] = snode + vnode - end - end - tabulation = refid_to_tabulation[refid] - nsnodes = size(tabulation,1) - nodes = cell_to_nodes[cell] - for snode in 1:nsnodes - y = zero(Tx) - for (i,node) in enumerate(nodes) - coeff = tabulation[snode,i] - x = node_to_coords[node] - y += coeff*x - end - vnode += 1 - vnode_to_coords[vnode] = y - end - cell_to_vnodes[cell] = vnode_prev:vnode - vnode_prev = vnode + 1 - end - vcell_to_vnodes = JaggedArray(vcell_to_vnodes_data,vcell_to_vnodes_ptrs) - vchain = Chain(; - num_dims=Val(dim), - node_coordinates=vnode_to_coords, - face_nodes=vcell_to_vnodes, - face_reference_id=vcell_to_vrefid, - reference_faces=vrefid_to_vrefface) - vmesh = mesh_from_chain(vchain) - vglue = (;parent_face=vcell_to_cell, - reference_coordinates=refid_to_snode_to_coords, - face_fine_nodes = cell_to_vnodes, - num_dims=Val(dim)) - vmesh, vglue - end # barrier - refid_to_refface = reference_faces(mesh,dim) - refid_to_refmesh = map(refid_to_refface) do ref_face - if order === nothing && resolution === nothing - # Use the given cells as visualization cells - mesh_from_reference_face(ref_face) - elseif order !== nothing && resolution === nothing - # Use cells of given order as visualization cells - geo = geometry(ref_face) - ref_face_ho = lagrangian_reference_face(geo;order) - mesh_from_reference_face(ref_face_ho) - elseif order === nothing && resolution !== nothing - # Use linear sub-cells with $resolution per direction per direction - geom = geometry(ref_face) - refine_reference_geometry(geom,resolution) - else - error("order and resolution kw-arguments can not be given at the same time") - end - end - refid_to_tabulation = map(refid_to_refface,refid_to_refmesh) do refface,refmesh - shape_funs = shape_functions(refface) - x = node_coordinates(refmesh) - tabulation_matrix(shape_funs)(value,x) - end - refid_to_scell_to_snodes = map(refmesh->face_nodes(refmesh,dim),refid_to_refmesh) - refid_to_scell_to_srefid = map(refmesh->face_reference_id(refmesh,dim),refid_to_refmesh) - refid_to_srefid_to_oid = map(refmesh->map(objectid,reference_faces(refmesh,dim)),refid_to_refmesh) - refid_to_srefid_to_vrefface = map(refmesh->reference_faces(refmesh,dim),refid_to_refmesh) - refid_to_snode_to_coords = map(node_coordinates,refid_to_refmesh) - node_to_coords = node_coordinates(mesh) - cell_to_nodes = face_nodes(mesh,dim) - cell_to_refid = face_reference_id(mesh,dim) - Dn = num_ambient_dims(mesh) - barrier( - refid_to_tabulation, - refid_to_scell_to_snodes, - refid_to_scell_to_srefid, - refid_to_srefid_to_oid, - refid_to_srefid_to_vrefface, - refid_to_snode_to_coords, - node_to_coords, - cell_to_nodes, - cell_to_refid, - Val(Dn)) -end - -function refine_reference_geometry(geo,resolution) - function refine_n_cube_aligned(geo,n) - box = bounding_box(geo) - domain = domain_from_bounding_box(box) - D = num_dims(geo) - cells = ntuple(i->n,Val(D)) - cartesian_mesh(domain,cells) - end - function refine_unit_triangle(geo,n) - # Copyed + adapted from Gridap - tri_num(n) = n*(n+1)÷2 - v(n,i,j) = tri_num(n) - tri_num(n-i+1) + j - D = 2 - quad_to_tris = ((1,2,3),(2,4,3)) - quad = CartesianIndices( (0:1,0:1) ) - Tp = SVector{2,Float64} - n_verts = tri_num(n+1) - n_cells = tri_num(n)+tri_num(n-1) - n_verts_x_cell = 3 - X = zeros(Tp,n_verts) - T = [ zeros(Int,n_verts_x_cell) for i in 1:n_cells ] - for i in 1:n+1 - for j in 1:n+1-i+1 - vert = v(n+1,i,j) - X[vert] = SVector((i-1)/n,(j-1)/n) - end - end - for i in 1:n - for j in 1:n-(i-1) - verts = ntuple( lv-> v(n+1, (i,j).+quad[lv].I ...), Val{2^D}() ) - cell = v(n,i,j) - T[cell] .= map(i->verts[i],quad_to_tris[1]) - if (i-1)+(j-1) < n-1 - cell = tri_num(n) + v(n-1,i,j) - T[cell] .= map(i->verts[i],quad_to_tris[2]) - end - end - end - refface = lagrangian_reference_face(geo) - chain = Chain(; - num_dims=Val(2), - node_coordinates = X, - face_nodes = T, - face_reference_id = fill(1,length(T)), - reference_faces = [refface] - ) - mesh_from_chain(chain) - end - function refine_unit_tet(geo,n) - # Copyed + adapted from Gridap - tri_num(n) = n*(n+1)÷2 - tet_num(n) = n*(n+1)*(n+2)÷6 - v(n,i,j) = tri_num(n) - tri_num(n-i+1) + j - v(n,i,j,k) = tet_num(n) - tet_num(n-i+1) + v(n-i+1,j,k) - D = 3 - cube_to_tets = ((1,2,3,5),(2,4,3,6),(3,5,7,6),(2,3,5,6),(3,4,7,6),(4,6,7,8)) - cube = CartesianIndices( (0:1,0:1,0:1) ) - n_core_tets = length(cube_to_tets)-2 - Tp = SVector{3,Float64} - n_verts = tet_num(n+1) - n_cells = tet_num(n)+n_core_tets*tet_num(n-1)+tet_num(n-2) - n_verts_x_cell = 4 - X = zeros(Tp,n_verts) - T = [ zeros(Int,n_verts_x_cell) for i in 1:n_cells ] - for i in 1:n+1 - for j in 1:n+1-(i-1) - for k in 1:n+1-(i-1)-(j-1) - vert = v(n+1,i,j,k) - X[vert] = SVector((i-1)/n,(j-1)/n,(k-1)/n) - end - end - end - for i in 1:n - for j in 1:n-(i-1) - for k in 1:n-(i-1)-(j-1) - verts = ntuple( lv-> v(n+1, (i,j,k).+cube[lv].I ...), Val{2^D}() ) - cell = v(n,i,j,k) - T[cell] .= map(i->verts[i],cube_to_tets[1]) - if (i-1)+(j-1)+(k-1) < n-1 - cell = tet_num(n) + (v(n-1,i,j,k)-1)*n_core_tets - for t in 1:n_core_tets - T[cell+t] .= map(i->verts[i],cube_to_tets[t+1]) - end - end - if (i-1)+(j-1)+(k-1) < n-2 - cell = tet_num(n) + n_core_tets*tet_num(n-1) + v(n-2,i,j,k) - T[cell] .= map(i->verts[i],cube_to_tets[end]) - end - end - end - end - refface = lagrangian_reference_face(geo) - chain = Chain(; - num_dims=Val(3), - node_coordinates = X, - face_nodes = T, - face_reference_id = fill(1,length(T)), - reference_faces = [refface], - ) - mesh_from_chain(chain) - end - if is_n_cube(geo) && is_axis_aligned(geo) - refine_n_cube_aligned(geo,resolution) - elseif is_unit_simplex(geo) && num_dims(geo) == 2 - refine_unit_triangle(geo,resolution) - elseif is_unit_simplex(geo) && num_dims(geo) == 3 - refine_unit_tet(geo,resolution) - else - error("Case not implemented (yet)") - end -end - -function lagrangian_reference_face( - geometry; - space=:default, - order=1, - lib_to_user_nodes = nothing, - init_coordinate=SVector{num_dims(geometry),Float64}, - order_per_dir=ntuple(i->order,Val(num_dims(geometry))), - kwargs... - ) - - D = num_dims(geometry) - if space === :default - space = default_polynomial_space(geometry) - end - monomial_exponents = - monomial_exponents_from_space(space,order_per_dir,kwargs...) - T = Float64 - node_coordinates = map(monomial_exponents) do exponent - c = map(exponent) do e - if order != 0 - T(e/order) - else - T(e) - end - end - init_coordinate(c) - end - nnodes = length(node_coordinates) - lib_to_user_nodes = if lib_to_user_nodes !== nothing - lib_to_user_nodes - else - collect(1:nnodes) - end - node_coordinates[lib_to_user_nodes] = node_coordinates - monomials = map(e->(x-> prod(x.^e)),monomial_exponents) - dofs = map(x->(f->f(x)),node_coordinates) - shape_functions, dofs = shape_functions_and_dofs_from_bases(monomials,dofs) - refface = Face(; - geometry, - shape_functions, - dofs, - node_coordinates, - order= D==0 ? order : maximum(order_per_dir), - order_per_dir, - monomial_exponents, - lib_to_user_nodes) - boundary, interior_nodes = reference_face_boundary_from_reference_face(refface) - vtk_mesh_cell = vtk_mesh_cell_from_reference_face(refface) - refface1 = setproperties(refface;boundary,interior_nodes,vtk_mesh_cell) - refface1 -end - -function lagrangian_reference_element(geom;shape=:scalar,kwargs...) - if shape === :scalar - lagrangian_reference_element_scalar(geom;kwargs...) - else - lagrangian_reference_element_tensor(geom;shape,kwargs...) - end -end - -function lagrangian_reference_element_scalar(geom;kwargs...) - D = num_dims(geom) - refface = lagrangian_reference_face(geom;kwargs...) - nnodes = num_nodes(refface) - dof_to_node = 1:nnodes - dof_to_index = fill(1,nnodes) - node_to_dofs = map(i->[1],1:nnodes) - face_interior_nodes = Vector{Vector{Vector{Int}}}(undef,D+1) - face_interior_nodes[end] = [interior_nodes(refface)] - for d in 0:(D-1) - face_to_refid = face_reference_id(boundary(refface),d) - face_to_nodes = face_nodes(boundary(refface),d) - refid_to_refface = reference_faces(boundary(refface),d) - refid_to_interior_nodes = map(interior_nodes,refid_to_refface) - ndfaces = num_faces(boundary(refface),d) - face_interior_nodes[d+1] = Vector{Vector{Int}}(undef,ndfaces) - for face in 1:ndfaces - refid = face_to_refid[face] - my_interior_nodes = refid_to_interior_nodes[refid] - face_interior_nodes[d+1][face] = face_to_nodes[face][my_interior_nodes] - end - end - face_interior_node_permutations = Vector{Vector{Vector{Vector{Int}}}}(undef,D+1) - face_interior_node_permutations[end] = [interior_node_permutations_from_reference_face(refface)] - for d in 0:(D-1) - face_to_refid = face_reference_id(boundary(refface),d) - refid_to_refface = reference_faces(boundary(refface),d) - refid_to_permutations = map(interior_node_permutations_from_reference_face,refid_to_refface) - face_interior_node_permutations[d+1] = refid_to_permutations[face_to_refid] - end - face_own_dofs = face_interior_nodes - face_own_dof_permutations = face_interior_node_permutations - num_dofs = nnodes - reffe = setproperties(refface; - num_dofs, - node_to_dofs, - dof_to_node, - dof_to_index, - face_own_dofs, - face_own_dof_permutations) - reffe -end - -function lagrangian_reference_element_tensor( - geom; - shape=Val(()), - init_tensor=SArray{Tuple{val_parameter(shape)...}}, - major=:component, - inner=(a,b)->sum(map(*,a,b)), - kwargs... - ) - - reffe_scalar = lagrangian_reference_element_scalar(geom;kwargs...) - nnodes = num_nodes(reffe_scalar) - monomials = map(e->(x-> prod(x.^e)),monomial_exponents(reffe_scalar)) - node_coordinates_reffe = node_coordinates(reffe_scalar) - cis = CartesianIndices(val_parameter(shape)) - l = prod(val_parameter(shape)) - cis_flat = cis[:] - tensor_basis = map(cis_flat) do ci - init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) - end - primal_nested = map(monomials) do monomial - map(tensor_basis) do e - x -> monomial(x)*e - end - end - primal = reduce(vcat,primal_nested) - if major === :component - dual_nested = map(node_coordinates_reffe) do x - map(tensor_basis) do e - f->inner(e,f(x)) - end - end - dof_to_node_nested = map(1:nnodes) do inode - fill(inode,length(cis)) - end - dof_to_index_nested = map(1:nnodes) do inode - cis[:] - end - elseif major === :node - dual_nested = map(tensor_basis) do e - map(node_coordinates_reffe) do x - f->inner(e,f(x)) - end - end - dof_to_node_nested = map(cis_flat) do ci - collect(1:nnodes) - end - dof_to_index_nested = map(cis_flat) do ci - fill(ci,nnodes) - end - end - dual = reduce(vcat,dual_nested) - dof_to_node = reduce(vcat,dof_to_node_nested) - dof_to_index = reduce(vcat,dof_to_index_nested) - node_to_dofs = zeros(eltype(tensor_basis),nnodes) - ndofs = length(dof_to_node) - lis = LinearIndices(cis) - for dof in 1:ndofs - node = dof_to_node[dof] - ci = dof_to_index[dof] - node_to_dofs[node] += dof*tensor_basis[lis[ci]] - end - shape_functions, dofs = shape_functions_and_dofs_from_bases(primal,dual) - face_interior_nodes = face_own_dofs(reffe_scalar) - my_face_own_dofs = map(face_interior_nodes) do face_to_interior_nodes - map(face_to_interior_nodes) do own_nodes - if length(own_nodes) != 0 - own_dofs_nested = map(own_nodes) do node - collect(node_to_dofs[node][:]) - end - reduce(vcat,own_dofs_nested) - else - copy(own_nodes) - end - end - end - face_interior_node_permutations = face_own_dof_permutations(reffe_scalar) - my_face_own_dof_permutations = map(face_interior_node_permutations,face_interior_nodes,my_face_own_dofs) do face_to_perms, face_to_interior_nodes, face_to_own_dofs - map(face_to_perms,face_to_interior_nodes,face_to_own_dofs) do perms,interior_nodes,own_dofs - map(perms) do permutation - if length(interior_nodes) != 0 - permuted_interior_nodes = interior_nodes[permutation] - permuted_own_dofs_nested = map(permuted_interior_nodes) do pnode - collect(node_to_dofs[pnode][:]) - end - permuted_own_dofs = reduce(vcat,permuted_own_dofs_nested) - odof_to_podof = Vector{Int}(indexin(own_dofs,permuted_own_dofs)) - else - odof_to_podof = copy(permutation) - end - end - end - end - reffe = setproperties(reffe_scalar; - num_dofs=ndofs, - shape_functions, - dofs, - node_to_dofs, - dof_to_node, - dof_to_index, - face_own_dofs=my_face_own_dofs, - face_own_dof_permutations=my_face_own_dof_permutations) - reffe -end - - -function dof_glue_from_mesh_and_local_dofs( - mesh, - local_dofs; - face_glue=map(n->1:length(face_reference_id(local_dofs,n)),0:num_dims(local_dofs))) - # TODO periodic boundary conditions (just find and return slave->master mapping) - # First we need to find slave->master faces while building the mesh topology - - D = num_dims(local_dofs) - n_nface_refid = face_reference_id(local_dofs) - n_refid_reffe = reference_faces(local_dofs) - n_refid_nldofs = map(refid_reffe->map(num_dofs,refid_reffe),n_refid_reffe) - n_refid_j_ljface_odof_ldof = map(refid_reffe->map(reffe->face_own_dofs(reffe),refid_reffe),n_refid_reffe) - n_refid_j_ljface_p_odof_podof = map(refid_reffe->map(reffe->face_own_dof_permutations(reffe),refid_reffe),n_refid_reffe) - @assert length(face_glue) == D+1 - n_nface_Nface = face_glue - @assert num_dims(mesh) >= D - mesh_topology = topology(mesh) - nd_Nface_ldface_dface = face_incidence(mesh_topology) - nd_Nface_ldface_p = face_permutation_ids(mesh_topology) - d_to_ndfaces = map(d->num_faces(mesh,d),0:D) - dof = 0 - d_dface_podof_dof = map(0:D) do d - ndfaces = d_to_ndfaces[d+1] - dface_podof_dof_ptrs = zeros(Int32,ndfaces+1) - dface_istouched = fill(false,ndfaces) - for n in d:D - Nface_ldface_dface = nd_Nface_ldface_dface[n+1,d+1] - nface_Nface = n_nface_Nface[n+1] - nface_refid = n_nface_refid[n+1] - refid_nldofs = n_refid_nldofs[n+1] - refid_j_ljface_odof_ldof = n_refid_j_ljface_odof_ldof[n+1] - refid_j_ljface_p_odof_podof = n_refid_j_ljface_p_odof_podof[n+1] - nnfaces = length(nface_refid) - for nface in 1:nnfaces - refid = nface_refid[nface] - j_ljface_odof_ldof = refid_j_ljface_odof_ldof[refid] - ldface_odof_ldof = j_ljface_odof_ldof[d+1] - Nface = nface_Nface[nface] - ldface_dface = Nface_ldface_dface[Nface] - for ldface in 1:length(ldface_dface) - dface = ldface_dface[ldface] - if dface_istouched[dface]# This one is not really needed - continue - end - odof_ldof = ldface_odof_ldof[ldface] - dface_podof_dof_ptrs[dface+1] = length(odof_ldof) - dface_istouched[dface] = true - end - end - end - length_to_ptrs!(dface_podof_dof_ptrs) - ndata = dface_podof_dof_ptrs[end]-1 - dface_podof_dof_data = zeros(Int32,ndata) - fill!(dface_istouched,false) - for n in d:D - Nface_ldface_dface = nd_Nface_ldface_dface[n+1,d+1] - nface_Nface = n_nface_Nface[n+1] - nface_refid = n_nface_refid[n+1] - refid_nldofs = n_refid_nldofs[n+1] - refid_j_ljface_odof_ldof = n_refid_j_ljface_odof_ldof[n+1] - refid_j_ljface_p_odof_podof = n_refid_j_ljface_p_odof_podof[n+1] - nnfaces = length(nface_refid) - for nface in 1:nnfaces - refid = nface_refid[nface] - j_ljface_odof_ldof = refid_j_ljface_odof_ldof[refid] - dlface_odof_ldof = j_ljface_odof_ldof[d+1] - Nface = nface_Nface[nface] - ldface_dface = Nface_ldface_dface[Nface] - for ldface in 1:length(ldface_dface) - dface = ldface_dface[ldface] - if dface_istouched[dface] - continue - end - odof_ldof = dlface_odof_ldof[ldface] - offset = dface_podof_dof_ptrs[dface]-1 - for podof in 1:length(odof_ldof) - dof += 1 - dface_podof_dof_data[offset+podof] = dof - end - dface_istouched[dface] = true - end - end - end - JaggedArray(dface_podof_dof_data,dface_podof_dof_ptrs) - end - ndofs = dof - n_nface_ldof_dof = map(0:D) do n - nface_Nface = n_nface_Nface[n+1] - nface_refid = n_nface_refid[n+1] - refid_nldofs = n_refid_nldofs[n+1] - refid_j_ljface_odof_ldof = n_refid_j_ljface_odof_ldof[n+1] - refid_j_ljface_p_odof_podof = n_refid_j_ljface_p_odof_podof[n+1] - nnfaces = length(nface_refid) - nface_ldof_dof_ptrs = zeros(Int32,nnfaces+1) - for nface in 1:nnfaces - refid = nface_refid[nface] - nldofs = refid_nldofs[refid] - nface_ldof_dof_ptrs[nface+1] = nldofs - end - length_to_ptrs!(nface_ldof_dof_ptrs) - ndata = nface_ldof_dof_ptrs[end]-1 - nface_ldof_dof_data = zeros(Int32,ndata) - nface_ldof_dof = JaggedArray(nface_ldof_dof_data,nface_ldof_dof_ptrs) - for d in 0:n - Nface_ldface_dface = nd_Nface_ldface_dface[n+1,d+1] - Nface_ldface_p = nd_Nface_ldface_p[n+1,d+1] - dface_podof_dof = d_dface_podof_dof[d+1] - for nface in 1:nnfaces - Nface_ldface_dface = nd_Nface_ldface_dface[n+1,d+1] - Nface = nface_Nface[nface] - refid = nface_refid[nface] - j_ljface_odof_ldof = refid_j_ljface_odof_ldof[refid] - j_ljface_p_odof_podof = refid_j_ljface_p_odof_podof[refid] - dlface_odof_ldof = j_ljface_odof_ldof[d+1] - ldface_p_odof_podof = j_ljface_p_odof_podof[d+1] - ldface_dface = Nface_ldface_dface[Nface] - ldface_p = Nface_ldface_p[Nface] - ldof_dof = nface_ldof_dof[nface] - for ldface in 1:length(ldface_dface) - dface = ldface_dface[ldface] - p_odof_podof = ldface_p_odof_podof[ldface] - p = ldface_p[ldface] - odof_podof = p_odof_podof[p] - odof_ldof = dlface_odof_ldof[ldface] - podof_dof = dface_podof_dof[dface] - for odof in 1:length(odof_podof) - podof = odof_podof[odof] - dof = podof_dof[podof] - ldof = odof_ldof[odof] - ldof_dof[ldof] = dof - end - end - end - end - nface_ldof_dof - end - Object(; - num_dims=Val(D), - face_dofs=n_nface_ldof_dof, - num_dofs=ndofs, - ) -end - -function assemble_graph(nnodes,d_to_cell_to_nodes) - ndata = 0 - for cell_to_nodes in d_to_cell_to_nodes - ncells = length(cell_to_nodes) - for cell in 1:ncells - nodes = cell_to_nodes[cell] - nlnodes = length(nodes) - ndata += nlnodes*nlnodes - end - end - I = zeros(Int32,ndata) - J = zeros(Int32,ndata) - p = 0 - for cell_to_nodes in d_to_cell_to_nodes - ncells = length(cell_to_nodes) - for cell in 1:ncells - nodes = cell_to_nodes[cell] - nlnodes = length(nodes) - for j in 1:nlnodes - for i in 1:nlnodes - p += 1 - I[p] = nodes[i] - J[p] = nodes[j] - end - end - end - end - V = ones(Int8,ndata) - g = sparse(I,J,V,nnodes,nnodes) - fill!(g.nzval,Int8(1)) - g -end - -function restrict_mesh(mesh,lnode_to_node,lface_to_face_mesh) - nnodes = num_nodes(mesh) - node_to_lnode = zeros(Int32,nnodes) - node_to_lnode[lnode_to_node] = 1:length(lnode_to_node) - lnode_to_coords = node_coordinates(mesh)[lnode_to_node] - lface_to_lnodes_mesh = map(lface_to_face_mesh,face_nodes(mesh)) do lface_to_face,face_to_nodes - lface_to_nodes = view(face_to_nodes,lface_to_face) - lface_to_lnodes = JaggedArray(lface_to_nodes) - f = node->node_to_lnode[node] - lface_to_lnodes.data .= f.(lface_to_lnodes.data) - lface_to_lnodes - end - lface_to_refid_mesh = map((a,b)->b[a],lface_to_face_mesh,face_reference_id(mesh)) - D = num_dims(mesh) - lmesh = Object(; - num_dims=Val(D), - node_coordinates=lnode_to_coords, - face_nodes=lface_to_lnodes_mesh, - face_reference_id=lface_to_refid_mesh, - reference_faces=reference_faces(mesh)) - if has_physical_groups(mesh) - lgroups_mesh = map(lface_to_face_mesh,num_faces(mesh),physical_groups(mesh)) do lface_to_face, nfaces, groups - lgroups = Dict{String,Vector{Int32}}() - face_to_lface = zeros(Int32,nfaces) - face_to_lface[lface_to_face] = 1:length(lface_to_face) - for (k,faces) in groups - lgroups[k] = filter(i->i!=0,face_to_lface[faces]) - end - lgroups - end - lmesh = setproperties(lmesh;physical_groups=lgroups_mesh) - end - if has_periodic_nodes(mesh) - pnode_to_node,pnode_to_master = periodic_nodes(mesh) - plnode_to_lnode = filter(i->i!=0,node_to_lnode[pnode_to_node]) - plnode_to_lmaster = filter(i->i!=0,node_to_lnode[pnode_to_master]) - lmesh = setproperties(lmesh,periodic_nodes=(plnode_to_lnode=>plnode_to_lmaster)) - end - lmesh -end - -function partition_mesh(colorize,ranks,mesh;via=:nodes) - if via === :nodes - partition_mesh_via_nodes(colorize,ranks,mesh) - elseif via === :cells - partition_mesh_via_cells(colorize,ranks,mesh) - else - error("case not implemented") - end -end - -function partition_mesh_via_nodes(colorize,ranks,mesh) - face_nodes_mesh = face_nodes(mesh) - nnodes = num_nodes(mesh) - root = 1 - colors_root = map(ranks) do rank - if rank == root - g = assemble_graph(nnodes,face_nodes_mesh) - colors::Vector{Int32}=colorize(g,length(ranks)) - else - colors = Int32[] - end - colors - end - node_to_color = emit(colors_root;source=root) - node_faces_mesh = map(face_nodes_mesh) do face_to_nodes - generate_face_coboundary(face_to_nodes,nnodes) - end - map(ranks,node_to_color) do rank,node_to_color - onode_to_node = findall(color->color==rank,node_to_color) - node_to_mask = fill(false,nnodes) - local_faces = map(face_nodes_mesh,node_faces_mesh) do face_to_nodes, node_to_faces - nfaces = length(face_to_nodes) - face_to_mask = fill(false,nfaces) - for node in onode_to_node - for face in node_to_faces[node] - face_to_mask[face] = true - end - end - lface_to_face = findall(face_to_mask) - nlfaces = length(lface_to_face) - lface_to_color = zeros(Int32,nlfaces) - for (lface,face) in enumerate(lface_to_face) - nodes = face_to_nodes[face] - color = maximum(node->node_to_color[node],nodes) - lface_to_color[lface] = color - node_to_mask[nodes] .= true - end - LocalIndices(nfaces,rank,lface_to_face,lface_to_color) - end - lnode_to_node = findall(node_to_mask) - lnode_to_color = node_to_color[lnode_to_node] - local_nodes = LocalIndices(nnodes,rank,lnode_to_node,lnode_to_color) - lface_to_face_mesh = map(local_to_global,local_faces) - lmesh = restrict_mesh(mesh,lnode_to_node,lface_to_face_mesh) - setproperties(lmesh;local_nodes,local_faces) - end -end - -function partition_mesh_via_cells(colorize,ranks,mesh) - D = num_dims(mesh) - cell_to_nodes = face_nodes(mesh,D) - nnodes = num_nodes(mesh) - node_to_cells = generate_face_coboundary(cell_to_nodes,nnodes) - ncells = length(cell_to_nodes) - root = 1 - colors_root = map(ranks) do rank - if rank == root - g = assemble_graph(ncells,[node_to_cells]) - colors::Vector{Int32} = colorize(g,length(ranks)) - else - colors = Int32[] - end - colors - end - cell_to_color = emit(colors_root;source=root) - pmesh = map(ranks,cell_to_color) do rank,cell_to_color - ocell_to_cell = findall(color->color==rank,cell_to_color) - node_to_mask = fill(false,nnodes) - for cell in ocell_to_cell - nodes = cell_to_nodes[cell] - node_to_mask[nodes] .= true - end - lnode_to_node = findall(node_to_mask) - nlnodes = length(lnode_to_node) - lnode_to_colors = JaggedArray(view(node_to_cells,lnode_to_node)) - f = cell->cell_to_color[cell] - lnode_to_colors.data .= f.(lnode_to_colors.data) - lnode_to_colors = JaggedArray(map(colors->sort(unique(colors)),lnode_to_colors)) - lnode_to_owner = zeros(Int32,nlnodes) - for (lnode, node) in enumerate(lnode_to_node) - cells = node_to_cells[node] - color = maximum(cell->cell_to_color[cell],cells) - lnode_to_owner[lnode] = color - end - lface_to_face_mesh = map(face_nodes(mesh)[1:end-1]) do face_to_nodes - nfaces = length(face_to_nodes) - face_to_mask = fill(false,nfaces) - for face in 1:nfaces - nodes = face_to_nodes[face] - if all(node->node_to_mask[node],nodes) - face_to_mask[face] = true - end - end - lface_to_face = findall(face_to_mask) - lface_to_face - end - lmesh = restrict_mesh(mesh,lnode_to_node,[lface_to_face_mesh...,ocell_to_cell]) - # TODO we know the neighbors (in lnode_to_colors), use them - local_nodes = LocalIndices(nnodes,rank,lnode_to_node,lnode_to_owner) - setproperties(lmesh;local_nodes,local_node_colors=lnode_to_colors) - end - pmesh -end - -include("bddc.jl") - end # module - diff --git a/test/runtests.jl b/test/runtests.jl index 9046d74f..7f249844 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,33 @@ module GalerkinToolkitTests using Test +import GalerkinToolkit as gt + +spx0 = gt.unit_simplex(0) +spx1 = gt.unit_simplex(1) +spx2 = gt.unit_simplex(2) +spx3 = gt.unit_simplex(3) +display(spx3) + +cube0 = gt.unit_n_cube(0) +cube1 = gt.unit_n_cube(1) +cube2 = gt.unit_n_cube(2) +cube3 = gt.unit_n_cube(3) +display(cube3) + +quad = gt.default_quadrature(spx0;degree=2) +quad = gt.default_quadrature(spx1;degree=2) +quad = gt.default_quadrature(spx2;degree=2) +quad = gt.default_quadrature(spx3;degree=2) + +quad = gt.default_quadrature(cube0;degree=2) +quad = gt.default_quadrature(cube1;degree=2) +quad = gt.default_quadrature(cube2;degree=2) +quad = gt.default_quadrature(cube3;degree=2) + +quad = gt.default_quadrature(cube1;degree=4,real_type=Float32) +quad = gt.default_quadrature(spx1;degree=4,real_type=Float32) -@time @testset "unittests.jl" begin include("unittests.jl") end -@time @testset "poisson_test.jl" begin include("poisson_test.jl") end end # module From 678538e772f672e9d79db6a5463da3dd20f82cfa Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Wed, 24 Jan 2024 18:13:06 +0100 Subject: [PATCH 03/34] Adding LagrangieFE --- src/GalerkinToolkit.jl | 193 +++++++++++++++++++++++++++++++++++++---- test/runtests.jl | 21 +++++ 2 files changed, 197 insertions(+), 17 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 497b1e5d..ee8827da 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -18,33 +18,92 @@ end val_parameter(a) = a val_parameter(::Val{a}) where a = a num_dims(a) = val_parameter(a.num_dims) +node_coordinates(a) = a.node_coordinates +reference_faces(a) = a.reference_faces +face_nodes(a) = a.face_nodes +face_incidence(a) = a.face_incidence +face_reference_id(a) = a.face_reference_id +vtk_mesh_cell(a) = a.vtk_mesh_cell +physical_groups(a) = a.physical_groups +has_physical_groups(a) = hasproperty(a,:physical_groups) && a.physical_groups !== nothing +periodic_nodes(a) = a.periodic_nodes +has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing +geometry(a) = a.geometry +topology(a) = a.topology +boundary(a) = a.boundary +is_n_cube(a) = hasproperty(a,:is_n_cube) ? val_parameter(a.is_n_cube) : false +is_simplex(a) = hasproperty(a,:is_simplex) ? val_parameter(a.is_simplex) : false +is_axis_aligned(a) = a.is_axis_aligned +bounding_box(a) = a.bounding_box +vertex_permutations(a) = a.vertex_permutations +face_own_dofs(a) = a.face_own_dofs +face_own_dof_permutations(a) = a.face_own_dof_permutations +node_to_dofs(a) = a.node_to_dofs +dof_to_node(a) = a.dof_to_node +dof_to_index(a) = a.dof_to_index +num_dofs(a) = a.num_dofs coordinates(a) = a.coordinates weights(a) = a.weights +shape_functions(a) = a.shape_functions +tabulation_matrix!(a) = a.tabulation_matrix! +tabulation_matrix(a) = a.tabulation_matrix +order(a) = a.order +monomial_exponents(a) = a.monomial_exponents +lib_to_user_nodes(a) = a.lib_to_user_nodes +interior_nodes(a) = a.interior_nodes +face_permutation_ids(a) = a.face_permutation_ids +face_permutation_ids(a,m,n) = face_permutation_ids(a)[m+1,n+1] +local_nodes(a) = a.local_nodes +local_node_colors(a) = a.local_node_colors +real_type(a) = a.real_type +int_type(a) = a.int_type -struct UnitSimplex{D} <: GalerkinToolkitDataType - num_dims::Val{D} +reference_faces(a,d) = reference_faces(a)[val_parameter(d)+1] +face_nodes(a,d) = face_nodes(a)[val_parameter(d)+1] +face_incidence(a,d1,d2) = face_incidence(a)[val_parameter(d1)+1,val_parameter(d2)+1] +face_reference_id(a,d) = face_reference_id(a)[val_parameter(d)+1] +num_faces(a) = map(length,face_reference_id(a)) +num_faces(a,d) = length(face_reference_id(a,d)) +physical_groups(a,d) = physical_groups(a)[val_parameter(d)+1] +num_nodes(a) = length(node_coordinates(a)) +num_ambient_dims(a) = length(eltype(node_coordinates(a))) +function face_offsets(a) + D = num_dims(a) + offsets = zeros(Int,D+1) + for d in 1:D + offsets[d+1] = offsets[d] + num_faces(a,d-1) + end + offsets +end +function face_dim(a,d) + n = num_faces(a,d) + fill(d,n) end -function Base.show(io::IO,k::MIME"text/plain",data::UnitSimplex) - D = num_dims(data) - print(io,"Unit simplex of dimension $D") +function face_dim(a) + D = num_dims(a) + reduce(vcat,map(d->face_dim(a,d),0:D)) end -struct UnitNCube{D} <: GalerkinToolkitDataType +struct UnitSimplex{D,Tv,Ti} <: GalerkinToolkitDataType num_dims::Val{D} + real_type::Type{Tv} + int_type::Type{Ti} end -function Base.show(io::IO,k::MIME"text/plain",data::UnitNCube) - D = num_dims(data) - print(io,"Unit cube of dimension $D") + +struct UnitNCube{D,Tv,Ti} <: GalerkinToolkitDataType + num_dims::Val{D} + real_type::Type{Tv} + int_type::Type{Ti} end -function unit_simplex(num_dims) +function unit_simplex(num_dims;real_type=Float64,int_type=Int) D = val_parameter(num_dims) - UnitSimplex(Val(D)) + UnitSimplex(Val(D),real_type,int_type) end -function unit_n_cube(num_dims) +function unit_n_cube(num_dims;real_type=Float64,int_type=Int) D = val_parameter(num_dims) - UnitNCube(Val(D)) + UnitNCube(Val(D),real_type,int_type) end struct GenericCuadrature{A,B} <: GalerkinToolkitDataType @@ -64,20 +123,20 @@ end function default_quadrature(geo::UnitSimplex; degree, - real_type=Float64) + real_type=geo.real_type) duffy_quadrature(geo;degree,real_type) end function default_quadrature(geo::UnitNCube; degree, - real_type=Float64) + real_type=geo.real_type) degree_per_dir = ntuple(i->degree,Val(num_dims(geo))) tensor_product_quadrature(geo;degree_per_dir,real_type) end function duffy_quadrature(geo::UnitSimplex; degree, - real_type = Float64, + real_type = geo.real_type, ) D = num_dims(geo) if D == 0 @@ -132,7 +191,7 @@ end function tensor_product_quadrature(geo::UnitNCube; degree_per_dir, - real_type = Float64, + real_type = geo.real_type, ) D = num_dims(geo) @@ -167,4 +226,104 @@ function tensor_product!(f,result,values_per_dir) result end +abstract type AbstractLagrangeFE <: GalerkinToolkitDataType end + +struct GenericLagrangeFE{A,B} <: AbstractLagrangeFE + geometry::A + order_per_dir::B + space::Symbol +end +struct LagrangianUnitSimplex{D,Tv,Ti} <: AbstractLagrangeFE + geometry::UnitSimplex{D,Tv,Ti} + order_per_dir::NTuple{D,Ti} + space::Symbol +end +struct LagrangianUnitNCube{D,Tv,Ti} <: AbstractLagrangeFE + geometry::UnitNCube{D,Tv,Ti} + order_per_dir::NTuple{D,Ti} + space::Symbol +end + +function lagrangian_fe(geometry,order_per_dir,space) + GenericLagrangeFE(geometry,order_per_dir,space) +end +function lagrangian_fe(geometry::UnitSimplex,order_per_dir,space) + LagrangianUnitSimplex(geometry,order_per_dir,space) +end +function lagrangian_fe(geometry::UnitNCube,order_per_dir,space) + LagrangianUnitNCube(geometry,order_per_dir,space) +end + +function lagrangian_fe(geometry;order,space=default_space(geometry)) + D = num_dims(geometry) + order_per_dir = ntuple(i->order,Val(D)) + lagrangian_fe(geometry,order_per_dir,space) +end + +default_space(::UnitSimplex) = :P +default_space(::UnitNCube) = :Q + +function tensor_product_lagrangian_fe(geometry::UnitNCube;order_per_dir,space=:Q) + lagrangian_fe(geometry,order_per_dir,space) +end + +function monomial_exponents(fe::AbstractLagrangeFE) + monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> real_type) +end + +function node_coordinates(fe::AbstractLagrangeFE) + mexps = monomial_exponents(fe) + node_coordinates_from_monomials_exponents(mexps,fe.order_per_dir,fe.geometry |> real_type) +end + +function node_coordinates_from_monomials_exponents(monomial_exponents,order_per_dir,real_type) + node_coordinates = map(monomial_exponents) do exponent + map(exponent,order_per_dir) do e,order + if order != 0 + real_type(e/order) + else + real_type(e) + end + end + end +end + +function monomial_exponents_from_space(space,args...) + filter = if space == :Q + (e,o)->true + elseif space == :P + (e,o)->sum(e)<=maximum(o) + else + error("Case not implemented (yet)") + end + monomial_exponents_from_filter(filter,args...) +end + +function monomial_exponents_from_filter(f,order_per_dir,int_type) + terms_per_dir = Tuple(map(d->d+1,order_per_dir)) + D = length(terms_per_dir) + cis = CartesianIndices(terms_per_dir) + m = count(ci->f(Tuple(ci) .- 1,order_per_dir),cis) + li = 0 + result = zeros(SVector{D,int_type},m) + for ci in cis + t = Tuple(ci) .- 1 + if f(t,order_per_dir) + li += 1 + result[li] = t + end + end + result +end + +#function boundary(::UnitSimplex{0}) +# nothing +#end +# +#function boundary(::UnitNCube{0}) +# nothing +#end + + + end # module diff --git a/test/runtests.jl b/test/runtests.jl index 7f249844..96e31715 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -28,6 +28,27 @@ quad = gt.default_quadrature(cube3;degree=2) quad = gt.default_quadrature(cube1;degree=4,real_type=Float32) quad = gt.default_quadrature(spx1;degree=4,real_type=Float32) +@show typeof(spx0) + +fe = gt.lagrangian_fe(spx0,order=1) +fe = gt.lagrangian_fe(spx1,order=1) +fe = gt.lagrangian_fe(spx2,order=1) +fe = gt.lagrangian_fe(spx3,order=1) +display(fe) + +fe = gt.lagrangian_fe(cube0,order=1) +fe = gt.lagrangian_fe(cube1,order=1) +fe = gt.lagrangian_fe(cube2,order=1) +fe = gt.lagrangian_fe(cube3,order=1) +display(fe) + +fe = gt.lagrangian_fe(cube2,order=3) +@show gt.node_coordinates(fe) + +#∂spx0 = gt.boundary(spx0) +#∂spx0 = gt.boundary(spx1) +#∂cube0 = gt.boundary(cube0) + end # module From e0c837e83e037e4bb672c59eea2f7d26fe3c8b7c Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Thu, 25 Jan 2024 13:37:06 +0100 Subject: [PATCH 04/34] Implemented general LagrangianFEs --- src/GalerkinToolkit.jl | 320 +++++++++++++++++++++++++++++++---------- test/runtests.jl | 71 ++++++--- 2 files changed, 295 insertions(+), 96 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index ee8827da..b57df38b 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -84,26 +84,51 @@ function face_dim(a) reduce(vcat,map(d->face_dim(a,d),0:D)) end -struct UnitSimplex{D,Tv,Ti} <: GalerkinToolkitDataType - num_dims::Val{D} - real_type::Type{Tv} - int_type::Type{Ti} -end - -struct UnitNCube{D,Tv,Ti} <: GalerkinToolkitDataType - num_dims::Val{D} - real_type::Type{Tv} +struct ExtrusionPolytope{D,Tv,Ti} <: GalerkinToolkitDataType + extrusion::NTuple{D,Bool} + bounding_box::NTuple{2,SVector{D,Tv}} int_type::Type{Ti} end function unit_simplex(num_dims;real_type=Float64,int_type=Int) D = val_parameter(num_dims) - UnitSimplex(Val(D),real_type,int_type) + extrusion = ntuple(i->false,Val(D)) + Tv = real_type + p0 = ntuple(i->zero(real_type),Val(D)) |> SVector{D,Tv} + p1 = ntuple(i->one(real_type),Val(D)) |> SVector{D,Tv} + bounding_box = (p0,p1) + ExtrusionPolytope(extrusion,bounding_box,int_type) end function unit_n_cube(num_dims;real_type=Float64,int_type=Int) D = val_parameter(num_dims) - UnitNCube(Val(D),real_type,int_type) + extrusion = ntuple(i->true,Val(D)) + Tv = real_type + p0 = ntuple(i->zero(real_type),Val(D)) |> SVector{D,Tv} + p1 = ntuple(i->one(real_type),Val(D)) |> SVector{D,Tv} + bounding_box = (p0,p1) + ExtrusionPolytope(extrusion,bounding_box,int_type) +end + +num_dims(p::ExtrusionPolytope{D}) where D = D +is_axis_aligned(p::ExtrusionPolytope) = true +is_simplex(geom::ExtrusionPolytope) = all(i->i==false,geom.extrusion) +is_n_cube(geom::ExtrusionPolytope) = all(geom.extrusion) +real_type(p::ExtrusionPolytope{D,Tv}) where {D,Tv} = Tv +int_type(p::ExtrusionPolytope{D,Tv,Ti}) where {D,Tv,Ti} = Ti + +function is_unit_n_cube(geo) + is_n_cube(geo) && is_unitary(geo) +end + +function is_unit_simplex(geo) + is_simplex(geo) && is_unitary(geo) +end + +function is_unitary(geom) + ! is_axis_aligned(geom) && return false + my_bounding_box = bounding_box(geom) + all(i->i==0,first(my_bounding_box)) && all(i->i==1,last(my_bounding_box)) end struct GenericCuadrature{A,B} <: GalerkinToolkitDataType @@ -121,23 +146,8 @@ function quadrature(coordinates::Vector{SVector{D,T}},weights::Vector{T}) where Cuadrature(coordinates,weights) end -function default_quadrature(geo::UnitSimplex; - degree, - real_type=geo.real_type) - duffy_quadrature(geo;degree,real_type) -end - -function default_quadrature(geo::UnitNCube; - degree, - real_type=geo.real_type) - degree_per_dir = ntuple(i->degree,Val(num_dims(geo))) - tensor_product_quadrature(geo;degree_per_dir,real_type) -end - -function duffy_quadrature(geo::UnitSimplex; - degree, - real_type = geo.real_type, - ) +function duffy_quadrature(geo,degree) + @assert is_unit_simplex(geo) D = num_dims(geo) if D == 0 x = zeros(SVector{0,real_type},1) @@ -181,21 +191,21 @@ function duffy_quadrature(geo::UnitSimplex; a *= 0.5 end m = prod(map(length,weights_per_dir)) - w = zeros(real_type,m) - x = zeros(SVector{D,real_type},m) + Tv = real_type(geo) + w = zeros(Tv,m) + x = zeros(SVector{D,Tv},m) tensor_product!(identity,x,coords_per_dir) tensor_product!(prod,w,weights_per_dir) x .= duffy_map.(x) quadrature(x,w) end -function tensor_product_quadrature(geo::UnitNCube; - degree_per_dir, - real_type = geo.real_type, - ) - +function tensor_product_quadrature(geo,degree_per_dir) + @assert is_n_cube(geo) + @assert is_axis_aligned(geo) + my_bounding_box = bounding_box(geo) D = num_dims(geo) - limits_per_dir = ntuple(i->(0,1),Val(D)) + limits_per_dir = ntuple(i->(my_bounding_box[1][i],my_bounding_box[2][i]),Val(D)) n_per_dir = map(d->ceil(Int,(d+1)/2),degree_per_dir) function quadrature_1d(n,limits) x,w = FastGaussQuadrature.gausslegendre(n) @@ -208,8 +218,9 @@ function tensor_product_quadrature(geo::UnitNCube; coords_per_dir = map(coordinates,quad_per_dir) weights_per_dir = map(weights,quad_per_dir) m = prod(map(length,weights_per_dir)) - w = zeros(real_type,m) - x = zeros(SVector{D,real_type},m) + Tv = real_type(geo) + w = zeros(Tv,m) + x = zeros(SVector{D,Tv},m) tensor_product!(identity,x,coords_per_dir) tensor_product!(prod,w,weights_per_dir) quadrature(x,w) @@ -226,45 +237,126 @@ function tensor_product!(f,result,values_per_dir) result end +function repeat_per_dir(geo,a) + D = num_dims(geo) + ntuple(i->a,Val(D)) +end +repeat_per_dir(geo,a::NTuple) = a + +function default_quadrature(geo,degree) + if is_n_cube(geo) && is_axis_aligned(geo) + D = num_dims(geo) + degree_per_dir = repeat_per_dir(geo,degree) + tensor_product_quadrature(geo,degree_per_dir) + elseif is_unit_simplex(geo) + duffy_quadrature(geo,degree) + else + error("Not implemented") + end +end + abstract type AbstractLagrangeFE <: GalerkinToolkitDataType end -struct GenericLagrangeFE{A,B} <: AbstractLagrangeFE +struct GenericLagrangeFE{A,B,C,D} <: AbstractLagrangeFE geometry::A order_per_dir::B space::Symbol -end -struct LagrangianUnitSimplex{D,Tv,Ti} <: AbstractLagrangeFE - geometry::UnitSimplex{D,Tv,Ti} - order_per_dir::NTuple{D,Ti} - space::Symbol -end -struct LagrangianUnitNCube{D,Tv,Ti} <: AbstractLagrangeFE - geometry::UnitNCube{D,Tv,Ti} - order_per_dir::NTuple{D,Ti} - space::Symbol + lib_to_user_nodes::C + major::Symbol + shape::D end -function lagrangian_fe(geometry,order_per_dir,space) - GenericLagrangeFE(geometry,order_per_dir,space) -end -function lagrangian_fe(geometry::UnitSimplex,order_per_dir,space) - LagrangianUnitSimplex(geometry,order_per_dir,space) -end -function lagrangian_fe(geometry::UnitNCube,order_per_dir,space) - LagrangianUnitNCube(geometry,order_per_dir,space) -end +struct ScalarShape end +const SCALAR_SHAPE = ScalarShape() + +#struct LagrangeFE{D,Tv,Ti} <: AbstractLagrangeFE +# geometry::ExtrusionPolytope{D,Tv,Ti} +# order_per_dir::NTuple{D,Ti} +# space::Symbol +# lib_to_user_nodes::Vector{Ti} +# major::Symbol +# shape::ScalarShape +#end +# +#struct TensorValuedLagrangeFE{D,S,Tv,Ti} <: AbstractLagrangeFE +# geometry::ExtrusionPolytope{D,Tv,Ti} +# order_per_dir::NTuple{D,Ti} +# space::Symbol +# lib_to_user_nodes::Vector{Ti} +# major::Symbol +# shape::NTuple{S,Ti} +#end + +lagrangian_fe(args...) = GenericLagrangeFE(args...) + +#function lagrangian_fe( +# geometry::ExtrusionPolytope{D,Tv,Ti}, +# order_per_dir::NTuple{D,Ti}, +# space::Symbol, +# lib_to_user_nodes::Vector{Ti}, +# major::Symbol, +# shape::ScalarShape) where {D,Tv,Ti} +# +# LagrangeFE( +# geometry, +# order_per_dir, +# space, +# lib_to_user_nodes, +# major, +# shape) +#end +# +#function lagrangian_fe( +# geometry::ExtrusionPolytope{D,Tv,Ti}, +# order_per_dir::NTuple{D,Ti}, +# space::Symbol, +# lib_to_user_nodes::Vector{Ti}, +# major::Symbol, +# shape::NTuple{S,Ti}) where {D,S,Tv,Ti} +# +# TensorValuedLagrangeFE( +# geometry, +# order_per_dir, +# space, +# lib_to_user_nodes, +# major, +# shape) +#end -function lagrangian_fe(geometry;order,space=default_space(geometry)) +function lagrangian_fe(geometry,order; + space = default_space(geometry), + lib_to_user_nodes = int_type(geometry)[], + major = :component, + shape = SCALAR_SHAPE) D = num_dims(geometry) - order_per_dir = ntuple(i->order,Val(D)) - lagrangian_fe(geometry,order_per_dir,space) + order_per_dir = repeat_per_dir(geometry,order) + lagrangian_fe( + geometry, + order_per_dir, + space, + lib_to_user_nodes, + major, + shape) end -default_space(::UnitSimplex) = :P -default_space(::UnitNCube) = :Q +function default_space(geom) + if is_simplex(geom) + :P + elseif is_n_cube(geom) + :Q + else + error("Not implemented") + end +end -function tensor_product_lagrangian_fe(geometry::UnitNCube;order_per_dir,space=:Q) - lagrangian_fe(geometry,order_per_dir,space) +function lib_to_user_nodes(fe::AbstractLagrangeFE) + if length(fe.lib_to_user_nodes) == 0 + nnodes = num_nodes(fe) + Ti = int_type(geometry(fe)) + collect(Ti.(1:nnodes)) + else + fe.lib_to_user_nodes + end end function monomial_exponents(fe::AbstractLagrangeFE) @@ -272,6 +364,7 @@ function monomial_exponents(fe::AbstractLagrangeFE) end function node_coordinates(fe::AbstractLagrangeFE) + @assert fe |> geometry |> is_unitary mexps = monomial_exponents(fe) node_coordinates_from_monomials_exponents(mexps,fe.order_per_dir,fe.geometry |> real_type) end @@ -289,14 +382,13 @@ function node_coordinates_from_monomials_exponents(monomial_exponents,order_per_ end function monomial_exponents_from_space(space,args...) - filter = if space == :Q - (e,o)->true + if space == :Q + monomial_exponents_from_filter((e,o)->true,args...) elseif space == :P - (e,o)->sum(e)<=maximum(o) + monomial_exponents_from_filter((e,o)->sum(e)<=maximum(o),args...) else error("Case not implemented (yet)") end - monomial_exponents_from_filter(filter,args...) end function monomial_exponents_from_filter(f,order_per_dir,int_type) @@ -316,14 +408,90 @@ function monomial_exponents_from_filter(f,order_per_dir,int_type) result end -#function boundary(::UnitSimplex{0}) -# nothing -#end -# -#function boundary(::UnitNCube{0}) -# nothing -#end +function tensor_basis(fe::AbstractLagrangeFE) + Ti = fe |> geometry |> int_type + if fe.shape == SCALAR_SHAPE + return Ti(1) + else + cis = CartesianIndices(val_parameter(fe.shape)) + l = prod(val_parameter(fe.shape)) + init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Ti} + cis_flat = cis[:] + return map(cis_flat) do ci + init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) + end + end +end + +function primal_basis(fe::AbstractLagrangeFE) + scalar_basis = map(e->(x-> prod(x.^e)),fe|>monomial_exponents) + if fe.shape == SCALAR_SHAPE + return scalar_basis + else + primal_nested = map(scalar_basis) do monomial + map(tensor_basis(fe)) do e + x -> monomial(x)*e + end + end + return reduce(vcat,primal_nested) + end +end + +function dual_basis(fe::AbstractLagrangeFE) + node_coordinates_reffe = fe|>node_coordinates + scalar_basis = map(x->(f->f(x)),node_coordinates_reffe) + if fe.shape == SCALAR_SHAPE + return scalar_basis + else + if fe.major === :component + dual_nested = map(node_coordinates_reffe) do x + map(tensor_basis(fe)) do e + f->inner(e,f(x)) + end + end + elseif fe.major === :node + dual_nested = map(tensor_basis(fe)) do e + map(node_coordinates_reffe) do x + f->inner(e,f(x)) + end + end + else + error("Not Implemented") + end + return reduce(vcat,dual_nested) + end +end + +inner(a,b) = sum(map(*,a,b)) +value(f,x) = f(x) +function shape_functions(fe) + primal = primal_basis(fe) + dual = dual_basis(fe) + primal_t = permutedims(primal) + A = value.(dual,primal_t) + B = A\I + n = length(primal) + map(1:n) do i + Bi = view(B,:,i) + x->begin + primal_t_x = map(f->f(x),primal_t) + (primal_t_x*Bi)[1,1]#TODO + end + end +end + +function tabulator(fe) + primal = primal_basis(fe) + dual = dual_basis(fe) + primal_t = permutedims(primal) + A = value.(dual,primal_t) + B = A\I + (f,x) -> begin + C = broadcast(f,primal_t,x) + C*B + end +end end # module diff --git a/test/runtests.jl b/test/runtests.jl index 96e31715..0ca418ae 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,36 +15,67 @@ cube2 = gt.unit_n_cube(2) cube3 = gt.unit_n_cube(3) display(cube3) -quad = gt.default_quadrature(spx0;degree=2) -quad = gt.default_quadrature(spx1;degree=2) -quad = gt.default_quadrature(spx2;degree=2) -quad = gt.default_quadrature(spx3;degree=2) +@show typeof(spx0) +@show typeof(cube0) +@show typeof(spx1) +@show typeof(cube1) -quad = gt.default_quadrature(cube0;degree=2) -quad = gt.default_quadrature(cube1;degree=2) -quad = gt.default_quadrature(cube2;degree=2) -quad = gt.default_quadrature(cube3;degree=2) +degree = 4 +quad = gt.default_quadrature(spx0,degree) +quad = gt.default_quadrature(spx1,degree) +quad = gt.default_quadrature(spx2,degree) +quad = gt.default_quadrature(spx3,degree) -quad = gt.default_quadrature(cube1;degree=4,real_type=Float32) -quad = gt.default_quadrature(spx1;degree=4,real_type=Float32) +quad = gt.default_quadrature(cube0,degree) +quad = gt.default_quadrature(cube1,degree) +quad = gt.default_quadrature(cube2,degree) +quad = gt.default_quadrature(cube3,degree) -@show typeof(spx0) -fe = gt.lagrangian_fe(spx0,order=1) -fe = gt.lagrangian_fe(spx1,order=1) -fe = gt.lagrangian_fe(spx2,order=1) -fe = gt.lagrangian_fe(spx3,order=1) +order = 1 +fe = gt.lagrangian_fe(spx0,order) +fe = gt.lagrangian_fe(spx1,order) +fe = gt.lagrangian_fe(spx2,order) +fe = gt.lagrangian_fe(spx3,order) display(fe) -fe = gt.lagrangian_fe(cube0,order=1) -fe = gt.lagrangian_fe(cube1,order=1) -fe = gt.lagrangian_fe(cube2,order=1) -fe = gt.lagrangian_fe(cube3,order=1) +fe = gt.lagrangian_fe(cube0,order) +fe = gt.lagrangian_fe(cube1,order) +fe = gt.lagrangian_fe(cube2,order) +fe = gt.lagrangian_fe(cube3,order) display(fe) -fe = gt.lagrangian_fe(cube2,order=3) +fe = gt.lagrangian_fe(cube0,order) +fe = gt.lagrangian_fe(cube2,order) @show gt.node_coordinates(fe) +spx2 = gt.unit_simplex(2) +quad = gt.default_quadrature(spx2,degree) +fe = gt.lagrangian_fe(spx2,order) +funs = gt.shape_functions(fe) +x = gt.coordinates(quad) +B = broadcast(gt.value,permutedims(funs),x) +display(B) +tabulator = gt.tabulator(fe) +A = tabulator(gt.value,x) +@test A≈B +x = gt.node_coordinates(fe) +A = tabulator(gt.value,x) +@show A + +fe = gt.lagrangian_fe(spx2,order;shape=(3,)) +funs = gt.shape_functions(fe) +x = gt.coordinates(quad) +B = broadcast(gt.value,permutedims(funs),x) +display(B) +tabulator = gt.tabulator(fe) +A = tabulator(gt.value,x) +@test A≈B +x = gt.node_coordinates(fe) +A = tabulator(gt.value,x) +@show A + + #∂spx0 = gt.boundary(spx0) #∂spx0 = gt.boundary(spx1) #∂cube0 = gt.boundary(cube0) From 828687523038ea81b73eb5050caf3407b2a74e1a Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Thu, 25 Jan 2024 14:38:05 +0100 Subject: [PATCH 05/34] mesh working --- src/GalerkinToolkit.jl | 509 ++++++++++++++++++++++++++++++++++++----- test/runtests.jl | 24 +- 2 files changed, 468 insertions(+), 65 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index b57df38b..9aed3f4b 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -23,7 +23,6 @@ reference_faces(a) = a.reference_faces face_nodes(a) = a.face_nodes face_incidence(a) = a.face_incidence face_reference_id(a) = a.face_reference_id -vtk_mesh_cell(a) = a.vtk_mesh_cell physical_groups(a) = a.physical_groups has_physical_groups(a) = hasproperty(a,:physical_groups) && a.physical_groups !== nothing periodic_nodes(a) = a.periodic_nodes @@ -44,7 +43,6 @@ dof_to_index(a) = a.dof_to_index num_dofs(a) = a.num_dofs coordinates(a) = a.coordinates weights(a) = a.weights -shape_functions(a) = a.shape_functions tabulation_matrix!(a) = a.tabulation_matrix! tabulation_matrix(a) = a.tabulation_matrix order(a) = a.order @@ -269,60 +267,8 @@ end struct ScalarShape end const SCALAR_SHAPE = ScalarShape() -#struct LagrangeFE{D,Tv,Ti} <: AbstractLagrangeFE -# geometry::ExtrusionPolytope{D,Tv,Ti} -# order_per_dir::NTuple{D,Ti} -# space::Symbol -# lib_to_user_nodes::Vector{Ti} -# major::Symbol -# shape::ScalarShape -#end -# -#struct TensorValuedLagrangeFE{D,S,Tv,Ti} <: AbstractLagrangeFE -# geometry::ExtrusionPolytope{D,Tv,Ti} -# order_per_dir::NTuple{D,Ti} -# space::Symbol -# lib_to_user_nodes::Vector{Ti} -# major::Symbol -# shape::NTuple{S,Ti} -#end - lagrangian_fe(args...) = GenericLagrangeFE(args...) -#function lagrangian_fe( -# geometry::ExtrusionPolytope{D,Tv,Ti}, -# order_per_dir::NTuple{D,Ti}, -# space::Symbol, -# lib_to_user_nodes::Vector{Ti}, -# major::Symbol, -# shape::ScalarShape) where {D,Tv,Ti} -# -# LagrangeFE( -# geometry, -# order_per_dir, -# space, -# lib_to_user_nodes, -# major, -# shape) -#end -# -#function lagrangian_fe( -# geometry::ExtrusionPolytope{D,Tv,Ti}, -# order_per_dir::NTuple{D,Ti}, -# space::Symbol, -# lib_to_user_nodes::Vector{Ti}, -# major::Symbol, -# shape::NTuple{S,Ti}) where {D,S,Tv,Ti} -# -# TensorValuedLagrangeFE( -# geometry, -# order_per_dir, -# space, -# lib_to_user_nodes, -# major, -# shape) -#end - function lagrangian_fe(geometry,order; space = default_space(geometry), lib_to_user_nodes = int_type(geometry)[], @@ -360,7 +306,7 @@ function lib_to_user_nodes(fe::AbstractLagrangeFE) end function monomial_exponents(fe::AbstractLagrangeFE) - monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> real_type) + monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> int_type) end function node_coordinates(fe::AbstractLagrangeFE) @@ -370,6 +316,11 @@ function node_coordinates(fe::AbstractLagrangeFE) end function node_coordinates_from_monomials_exponents(monomial_exponents,order_per_dir,real_type) + D = length(order_per_dir) + Tv = real_type + if length(monomial_exponents) == 0 + return + end node_coordinates = map(monomial_exponents) do exponent map(exponent,order_per_dir) do e,order if order != 0 @@ -377,7 +328,7 @@ function node_coordinates_from_monomials_exponents(monomial_exponents,order_per_ else real_type(e) end - end + end |> SVector{D,Tv} end end @@ -385,21 +336,22 @@ function monomial_exponents_from_space(space,args...) if space == :Q monomial_exponents_from_filter((e,o)->true,args...) elseif space == :P - monomial_exponents_from_filter((e,o)->sum(e)<=maximum(o),args...) + monomial_exponents_from_filter((e,o)->sum(e)<=maximum(o,init=0),args...) else error("Case not implemented (yet)") end end function monomial_exponents_from_filter(f,order_per_dir,int_type) + Ti = int_type terms_per_dir = Tuple(map(d->d+1,order_per_dir)) D = length(terms_per_dir) cis = CartesianIndices(terms_per_dir) - m = count(ci->f(Tuple(ci) .- 1,order_per_dir),cis) - li = 0 + m = count(ci->f(SVector{D,Ti}(Tuple(ci) .- 1),order_per_dir),cis) result = zeros(SVector{D,int_type},m) + li = 0 for ci in cis - t = Tuple(ci) .- 1 + t = SVector{D,Ti}(Tuple(ci) .- 1) if f(t,order_per_dir) li += 1 result[li] = t @@ -409,13 +361,13 @@ function monomial_exponents_from_filter(f,order_per_dir,int_type) end function tensor_basis(fe::AbstractLagrangeFE) - Ti = fe |> geometry |> int_type + Tv = fe |> geometry |> real_type if fe.shape == SCALAR_SHAPE - return Ti(1) + return Tv(1) else cis = CartesianIndices(val_parameter(fe.shape)) l = prod(val_parameter(fe.shape)) - init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Ti} + init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Tv} cis_flat = cis[:] return map(cis_flat) do ci init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) @@ -493,5 +445,436 @@ function tabulator(fe) end end +abstract type AbstractFEMesh <: GalerkinToolkitDataType end + +struct GenericFEMesh{A,B,C,D,E,F,G} <: AbstractFEMesh + node_coordinates::A + face_nodes::B + face_reference_id::C + reference_faces::D + periodic_nodes::E + physical_groups::F + outwards_normals::G +end + +function fe_mesh(args...) + GenericFEMesh(args...) +end + +function fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + periodic_nodes = eltype(eltype(face_reference_id))[], + physical_groups = map(i->Dict{String,Vector{eltype(eltype(face_reference_id))}}(),face_reference_id), + outwards_normals = nothing + ) + fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces, + periodic_nodes, + physical_groups, + outwards_normals) +end + +num_dims(mesh::AbstractFEMesh) = length(reference_faces(mesh))-1 + +const INVALID_ID = 0 + +function default_gmsh_options() + [ + "General.Terminal"=>1, + "Mesh.SaveAll"=>1, + "Mesh.MedImportGroupsOfNodes"=>1 + ] +end + +function with_gmsh(f;options=default_gmsh_options()) + gmsh.initialize() + for (k,v) in options + gmsh.option.setNumber(k,v) + end + try + return f() + finally + gmsh.finalize() + end +end + +function mesh_from_gmsh(file;complexify=true,topology=true,renumber=true,kwargs...) + @assert ispath(file) "File not found: $(file)" + with_gmsh(;kwargs...) do + gmsh.open(file) + renumber && gmsh.model.mesh.renumberNodes() + renumber && gmsh.model.mesh.renumberElements() + mesh_from_gmsh_module(;complexify,topology) + end +end + +function mesh_from_gmsh_module(;complexify=true,topology=true) + entities = gmsh.model.getEntities() + nodeTags, coord, parametricCoord = gmsh.model.mesh.getNodes() + + # find num_dims + ddim = -1 + for e in entities + ddim = max(ddim,e[1]) + end + if ddim == -1 + error("No entities in the msh file.") + end + D = ddim + + # find embedded_dimension + dtouched = [false,false,false] + for node in nodeTags + if !(coord[(node-1)*3+1] + 1 ≈ 1) + dtouched[1] = true + end + if !(coord[(node-1)*3+2] + 1 ≈ 1) + dtouched[2] = true + end + if !(coord[(node-1)*3+3] + 1 ≈ 1) + dtouched[3] = true + end + end + if dtouched[3] + adim = 3 + elseif dtouched[2] + adim = 2 + elseif dtouched[1] + adim = 1 + else + adim = 0 + end + + # Setup node coords + nmin = minimum(nodeTags) + nmax = maximum(nodeTags) + nnodes = length(nodeTags) + if !(nmax == nnodes && nmin == 1) + error("Only consecutive node tags allowed.") + end + my_node_to_coords = zeros(SVector{adim,Float64},nnodes) + m = zero(MVector{adim,Float64}) + for node in nodeTags + for j in 1:adim + k = (node-1)*3 + j + xj = coord[k] + m[j] = xj + end + my_node_to_coords[node] = m + end + + # Setup face nodes + offsets = zeros(Int32,D+1) + my_face_nodes = Vector{JaggedArray{Int32,Int32}}(undef,D+1) + for d in 0:D + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) + ndfaces = 0 + for t in 1:length(elemTypes) + ndfaces += length(elemTags[t]) + end + if ndfaces != 0 + nmin::Int = minimum( minimum, elemTags ) + nmax::Int = maximum( maximum, elemTags ) + if !( (nmax-nmin+1) == ndfaces) + error("Only consecutive elem tags allowed.") + end + offsets[d+1] = nmin-1 + end + ptrs = zeros(Int32,ndfaces+1) + dface = 0 + for t in 1:length(elemTypes) + elementName, dim, order, numNodes, nodeCoord = + gmsh.model.mesh.getElementProperties(elemTypes[t]) + for e in 1:length(elemTags[t]) + dface += 1 + ptrs[dface+1] = numNodes + end + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = zeros(Int32,ndata) + dface = 1 + for t in 1:length(elemTypes) + p = ptrs[dface]-Int32(1) + for (i,node) in enumerate(nodeTags[t]) + data[p+i] = node + end + dface += length(elemTags[t]) + end + my_face_nodes[d+1] = JaggedArray(data,ptrs) + end + + # Setup face_reference_id + my_face_reference_id = Vector{Vector{Int32}}(undef,D+1) + for d in 0:D + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) + ndfaces = length(my_face_nodes[d+1]) + dface_to_refid = zeros(Int8,ndfaces) + refid = 0 + dface = 0 + for t in 1:length(elemTypes) + refid += 1 + for e in 1:length(elemTags[t]) + dface += 1 + dface_to_refid[dface] = refid + end + end + my_face_reference_id[d+1] = dface_to_refid + end + + # Setup reference faces + my_reference_faces = () + for d in D:-1:0 + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) + refdfaces = () + for t in 1:length(elemTypes) + refface = reference_face_from_gmsh_eltype(elemTypes[t]) + refdfaces = (refdfaces...,refface) + end + if refdfaces == () + refdfaces = reference_faces(boundary(first(first(my_reference_faces))),d) + end + my_reference_faces = (refdfaces,my_reference_faces...) + end + + ## Setup periodic nodes + node_to_master_node = fill(Int32(INVALID_ID),nnodes) + for (dim,tag) in entities + tagMaster, nodeTags, nodeTagsMaster, = gmsh.model.mesh.getPeriodicNodes(dim,tag) + for i in 1:length(nodeTags) + node = nodeTags[i] + master_node = nodeTagsMaster[i] + node_to_master_node[node] = master_node + end + end + pnode_to_node = Int32.(findall(i->i!=INVALID_ID,node_to_master_node)) + pnode_to_master = node_to_master_node[pnode_to_node] + periodic_nodes = pnode_to_node => pnode_to_master + + # Setup physical groups + my_groups = [ Dict{String,Vector{Int32}}() for d in 0:D] + for d in 0:D + offset = Int32(offsets[d+1]) + dimTags = gmsh.model.getPhysicalGroups(d) + for (dim,tag) in dimTags + @boundscheck @assert dim == d + g_entities = gmsh.model.getEntitiesForPhysicalGroup(dim,tag) + ndfaces_in_physical_group = 0 + for entity in g_entities + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(dim,entity) + for t in 1:length(elemTypes) + ndfaces_in_physical_group += length(elemTags[t]) + end + end + dfaces_in_physical_group = zeros(Int32,ndfaces_in_physical_group) + ndfaces_in_physical_group = 0 + for entity in g_entities + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(dim,entity) + for t in 1:length(elemTypes) + for etag in elemTags[t] + ndfaces_in_physical_group += 1 + dfaces_in_physical_group[ndfaces_in_physical_group] = Int32(etag)-offset + end + end + end + groupname = gmsh.model.getPhysicalName(dim,tag) + my_groups[d+1][groupname] = dfaces_in_physical_group + end + end + mesh = fe_mesh( + my_node_to_coords, + my_face_nodes, + my_face_reference_id, + my_reference_faces; + physical_groups = my_groups, + periodic_nodes,) + + if complexify + error("Not Implemented") + mesh, _ = complexify_mesh(mesh) + end + mesh +end + +function reference_face_from_gmsh_eltype(eltype) + if eltype == 1 + order = 1 + geom = unit_n_cube(Val(1)) + lib_to_gmsh = [1,2] + elseif eltype == 2 + order = 1 + geom = unit_simplex(Val(2)) + lib_to_gmsh = [1,2,3] + elseif eltype == 3 + order = 1 + geom = unit_n_cube(Val(2)) + lib_to_gmsh = [1,2,4,3] + elseif eltype == 4 + order = 1 + geom = unit_simplex(Val(3)) + lib_to_gmsh = [1,2,3,4] + elseif eltype == 5 + order = 1 + lib_to_gmsh = [1,2,4,3,5,6,8,7] + elseif eltype == 15 + order = 1 + geom = unit_n_cube(Val(0)) + lib_to_gmsh = [1] + elseif eltype == 8 + order = 2 + geom = unit_n_cube(Val(1)) + lib_to_gmsh = [1,3,2] + elseif eltype == 9 + order = 2 + geom = unit_simplex(Val(2)) + lib_to_gmsh = [1,4,2,6,5,3] + else + en, = gmsh.model.mesh.getElementProperties(eltype) + error("Unsupported element type. elemType: $eltype ($en)") + end + lagrangian_fe(geom,order;lib_to_user_nodes=lib_to_gmsh) +end + +function vtk_points(mesh) + function barrirer(coords) + nnodes = length(coords) + points = zeros(3,nnodes) + for node in 1:nnodes + coord = coords[node] + for i in 1:length(coord) + points[i,node] = coord[i] + end + end + points + end + coords = node_coordinates(mesh) + barrirer(coords) +end + +function vtk_cells(mesh,d) + function barrirer(face_to_refid,face_to_nodes,refid_mesh_cell) + cells = map(face_to_refid,face_to_nodes) do refid, nodes + mesh_cell = refid_mesh_cell[refid] + if mesh_cell === nothing + msg = """ + Not enough information to visualize this mesh via vtk: + vtk_mesh_cell returns nothing for the reference face in position $refid in dimension $d. + """ + error(msg) + end + mesh_cell(nodes) + end + cells + end + face_to_nodes = face_nodes(mesh,d) + face_to_refid = face_reference_id(mesh,d) + refid_refface = reference_faces(mesh,d) + refid_mesh_cell = map(vtk_mesh_cell,refid_refface) + barrirer(face_to_refid,face_to_nodes,refid_mesh_cell) +end + +""" + args = vtk_args(mesh,d) + +Return the arguments `args` to be passed in final position +to functions like `WriteVTK.vtk_grid`. +""" +function vtk_args(mesh,d) + points = vtk_points(mesh) + cells = vtk_cells(mesh,d) + points, cells +end + +function vtk_args(mesh) + points = vtk_points(mesh) + D = num_dims(mesh) + allcells = [vtk_cells(mesh,d) for d in 0:D if num_faces(mesh,d) != 0] + cells = reduce(vcat,allcells) + points, cells +end + +function vtk_physical_groups!(vtk,mesh,d;physical_groups=physical_groups(mesh,d)) + ndfaces = num_faces(mesh,d) + for group in physical_groups + name,faces = group + face_mask = zeros(Int,ndfaces) + face_mask[faces] .= 1 + vtk[name,WriteVTK.VTKCellData()] = face_mask + end + vtk +end + +function vtk_physical_groups!(vtk,mesh;physical_groups=physical_groups(mesh)) + nfaces = sum(num_faces(mesh)) + offsets = face_offsets(mesh) + D = num_dims(mesh) + data = Dict{String,Vector{Int}}() + for d in 0:D + for group in physical_groups[d+1] + name, = group + if !haskey(data,name) + face_mask = zeros(Int,nfaces) + data[name] = face_mask + end + end + end + for d in 0:D + for group in physical_groups[d+1] + offset = offsets[d+1] + name,faces = group + face_mask = data[name] + face_mask[faces.+offset] .= 1 + end + end + for (name,face_mask) in data + vtk[name,WriteVTK.VTKCellData()] = face_mask + end + vtk +end + +function vtk_mesh_cell(ref_face) + geom = geometry(ref_face) + d = num_dims(geom) + nnodes = num_nodes(ref_face) + lib_to_user = lib_to_user_nodes(ref_face) + if d == 0 && nnodes == 1 + cell_type = WriteVTK.VTKCellTypes.VTK_VERTEX + vtk_to_lib = [1] + elseif d == 1 && (is_simplex(geom) || is_n_cube(geom)) && nnodes == 2 + cell_type = WriteVTK.VTKCellTypes.VTK_LINE + vtk_to_lib = [1,2] + elseif d == 1 && (is_simplex(geom) || is_n_cube(geom)) && nnodes == 3 + cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_EDGE + vtk_to_lib = [1,3,2] + elseif d == 2 && is_n_cube(geom) && nnodes == 4 + cell_type = WriteVTK.VTKCellTypes.VTK_QUAD + vtk_to_lib = [1,2,4,3] + elseif d == 2 && is_simplex(geom) && nnodes == 3 + cell_type = WriteVTK.VTKCellTypes.VTK_TRIANGLE + vtk_to_lib = [1,2,3] + elseif d == 2 && is_simplex(geom) && nnodes == 6 + cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_TRIANGLE + vtk_to_lib = [1,3,6,2,5,4] + elseif d == 3 && is_n_cube(geom) && nnodes == 8 + cell_type = WriteVTK.VTKCellTypes.VTK_HEXAHEDRON + vtk_to_lib = [1,2,4,3,5,6,8,7] + elseif d == 3 && is_simplex(geom) && nnodes == 4 + cell_type = WriteVTK.VTKCellTypes.VTK_TETRA + vtk_to_lib = [1,2,3,4] + elseif d == 3 && is_simplex(geom) && nnodes == 10 + cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_TETRA + vtk_to_lib = [1,3,6,10,2,5,4,7,8,9] + else + return nothing + end + nodes -> WriteVTK.MeshCell(cell_type,(nodes[lib_to_user])[vtk_to_lib]) +end + end # module diff --git a/test/runtests.jl b/test/runtests.jl index 0ca418ae..198308f6 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,7 @@ module GalerkinToolkitTests using Test import GalerkinToolkit as gt +using WriteVTK spx0 = gt.unit_simplex(0) spx1 = gt.unit_simplex(1) @@ -46,6 +47,8 @@ fe = gt.lagrangian_fe(cube3,order) display(fe) fe = gt.lagrangian_fe(cube0,order) +@show gt.monomial_exponents(fe) +@show gt.node_coordinates(fe) fe = gt.lagrangian_fe(cube2,order) @show gt.node_coordinates(fe) @@ -61,7 +64,6 @@ A = tabulator(gt.value,x) @test A≈B x = gt.node_coordinates(fe) A = tabulator(gt.value,x) -@show A fe = gt.lagrangian_fe(spx2,order;shape=(3,)) funs = gt.shape_functions(fe) @@ -73,7 +75,25 @@ A = tabulator(gt.value,x) @test A≈B x = gt.node_coordinates(fe) A = tabulator(gt.value,x) -@show A + +fe = gt.lagrangian_fe(spx2,order;shape=()) +funs = gt.shape_functions(fe) +x = gt.coordinates(quad) +B = broadcast(gt.value,permutedims(funs),x) +display(B) +tabulator = gt.tabulator(fe) +A = tabulator(gt.value,x) +@test A≈B +x = gt.node_coordinates(fe) +A = tabulator(gt.value,x) + +outdir = mkpath(joinpath(@__DIR__,"..","output")) + +msh = joinpath(@__DIR__,"..","assets","demo.msh") +mesh = gt.mesh_from_gmsh(msh;complexify=false) +vtk_grid(joinpath(outdir,"demo"),gt.vtk_args(mesh)...) do vtk + gt.vtk_physical_groups!(vtk,mesh) +end #∂spx0 = gt.boundary(spx0) From 8e5af542557b3a3aa949b932db77ed3cbf42118b Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Fri, 26 Jan 2024 12:48:13 +0100 Subject: [PATCH 06/34] More refactoring --- src/GalerkinToolkit.jl | 1751 +++++++++++++++++++++++++++++++++++++++- test/runtests.jl | 23 + 2 files changed, 1763 insertions(+), 11 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 9aed3f4b..9673e634 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -15,6 +15,16 @@ function Base.show(io::IO,data::GalerkinToolkitDataType) print(io,"GalerkinToolkit.$(nameof(typeof(data)))(…)") end +function push(a::AbstractVector,x) + b = copy(a) + push!(b,x) + b +end + +function push(a::Tuple,x) + (a...,x) +end + val_parameter(a) = a val_parameter(::Val{a}) where a = a num_dims(a) = val_parameter(a.num_dims) @@ -28,7 +38,6 @@ has_physical_groups(a) = hasproperty(a,:physical_groups) && a.physical_groups != periodic_nodes(a) = a.periodic_nodes has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing geometry(a) = a.geometry -topology(a) = a.topology boundary(a) = a.boundary is_n_cube(a) = hasproperty(a,:is_n_cube) ? val_parameter(a.is_n_cube) : false is_simplex(a) = hasproperty(a,:is_simplex) ? val_parameter(a.is_simplex) : false @@ -43,9 +52,7 @@ dof_to_index(a) = a.dof_to_index num_dofs(a) = a.num_dofs coordinates(a) = a.coordinates weights(a) = a.weights -tabulation_matrix!(a) = a.tabulation_matrix! -tabulation_matrix(a) = a.tabulation_matrix -order(a) = a.order +order_per_dir(a) = a.order_per_dir monomial_exponents(a) = a.monomial_exponents lib_to_user_nodes(a) = a.lib_to_user_nodes interior_nodes(a) = a.interior_nodes @@ -55,6 +62,7 @@ local_nodes(a) = a.local_nodes local_node_colors(a) = a.local_node_colors real_type(a) = a.real_type int_type(a) = a.int_type +outwards_normals(a) = a.outwards_normals reference_faces(a,d) = reference_faces(a)[val_parameter(d)+1] face_nodes(a,d) = face_nodes(a)[val_parameter(d)+1] @@ -82,7 +90,9 @@ function face_dim(a) reduce(vcat,map(d->face_dim(a,d),0:D)) end -struct ExtrusionPolytope{D,Tv,Ti} <: GalerkinToolkitDataType +abstract type AbstractFaceGeometry <: GalerkinToolkitDataType end + +struct ExtrusionPolytope{D,Tv,Ti} <: AbstractFaceGeometry extrusion::NTuple{D,Bool} bounding_box::NTuple{2,SVector{D,Tv}} int_type::Type{Ti} @@ -253,7 +263,11 @@ function default_quadrature(geo,degree) end end -abstract type AbstractLagrangeFE <: GalerkinToolkitDataType end +abstract type AbstractMeshFace <: GalerkinToolkitDataType end + +num_dims(f::AbstractMeshFace) = num_dims(geometry(f)) + +abstract type AbstractLagrangeFE <: AbstractMeshFace end struct GenericLagrangeFE{A,B,C,D} <: AbstractLagrangeFE geometry::A @@ -295,6 +309,8 @@ function default_space(geom) end end +order(fe::AbstractLagrangeFE) = maximum(order_per_dir(fe),init=0) + function lib_to_user_nodes(fe::AbstractLagrangeFE) if length(fe.lib_to_user_nodes) == 0 nnodes = num_nodes(fe) @@ -504,17 +520,17 @@ function with_gmsh(f;options=default_gmsh_options()) end end -function mesh_from_gmsh(file;complexify=true,topology=true,renumber=true,kwargs...) +function mesh_from_gmsh(file;complexify=true,renumber=true,kwargs...) @assert ispath(file) "File not found: $(file)" with_gmsh(;kwargs...) do gmsh.open(file) renumber && gmsh.model.mesh.renumberNodes() renumber && gmsh.model.mesh.renumberElements() - mesh_from_gmsh_module(;complexify,topology) + mesh_from_gmsh_module(;complexify) end end -function mesh_from_gmsh_module(;complexify=true,topology=true) +function mesh_from_gmsh_module(;complexify=true) entities = gmsh.model.getEntities() nodeTags, coord, parametricCoord = gmsh.model.mesh.getNodes() @@ -696,8 +712,7 @@ function mesh_from_gmsh_module(;complexify=true,topology=true) periodic_nodes,) if complexify - error("Not Implemented") - mesh, _ = complexify_mesh(mesh) + mesh, _ = GalerkinToolkit.complexify(mesh) end mesh end @@ -876,5 +891,1719 @@ function vtk_mesh_cell(ref_face) nodes -> WriteVTK.MeshCell(cell_type,(nodes[lib_to_user])[vtk_to_lib]) end +# TODO use the same convention than in Gridap +# allow the user to customize the boundary object ids +function boundary(geom::ExtrusionPolytope{0}) + nothing +end +function boundary(geom::ExtrusionPolytope{1}) + Tv = real_type(geom) + Ti = int_type(geom) + if is_unit_simplex(geom) + vertex = unit_simplex(0;real_type=Tv,int_type=Ti) + elseif is_unit_n_cube(geom) + vertex = unit_n_cube(0;real_type=Tv,int_type=Ti) + else + error("Not implemented") + end + order = 1 + fe = lagrangian_fe(vertex,order) + node_coordinates = SVector{1,Tv}[(0,),(1,)] + face_nodes = [Vector{Ti}[[1],[2]]] + face_reference_id = [Ti[1,1]] + reference_faces = ([fe],) + outwards_normals = SVector{1,Tv}[(-1,),(1,)] + fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + outwards_normals + ) +end +function boundary(geom::ExtrusionPolytope{2}) + Tv = real_type(geom) + Ti = int_type(geom) + if is_unit_simplex(geom) + n1 = sqrt(2)/2 + g0 = unit_simplex(0;real_type=Tv,int_type=Ti) + g1 = unit_simplex(1;real_type=Tv,int_type=Ti) + node_coordinates = SVector{2,Tv}[(0,0),(1,0),(0,1)] + face_nodes = [Vector{Ti}[[1],[2],[3]],Vector{Ti}[[1,2],[1,3],[2,3]]] + face_reference_id = [Ti[1,1,1],Ti[1,1,1]] + outwards_normals = SVector{2,Tv}[(0,-1),(-1,0),(n1,n1)] + elseif is_unit_n_cube(geom) + g0 = unit_n_cube(0;real_type=Tv,int_type=Ti) + g1 = unit_n_cube(1;real_type=Tv,int_type=Ti) + node_coordinates = SVector{2,Tv}[(0,0),(1,0),(0,1),(1,1)] + face_nodes = [Vector{Ti}[[1],[2],[3],[4]],Vector{Ti}[[1,2],[3,4],[1,3],[2,4]]] + face_reference_id = [Ti[1,1,1,1],Ti[1,1,1,1]] + outwards_normals = SVector{2,Tv}[(0,-1),(0,1),(-1,0),(1,0)] + else + error("Not implemented") + end + order = 1 + fe0 = lagrangian_fe(g0,order) + fe1 = lagrangian_fe(g1,order) + reference_faces = ([fe0],[fe1]) + fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + outwards_normals + ) +end +function boundary(geom::ExtrusionPolytope{3}) + Tv = real_type(geom) + Ti = int_type(geom) + if is_unit_simplex(geom) + g0 = unit_simplex(0;real_type=Tv,int_type=Ti) + g1 = unit_simplex(1;real_type=Tv,int_type=Ti) + g2 = unit_simplex(2;real_type=Tv,int_type=Ti) + node_coordinates = SVector{3,Tv}[(0,0,0),(1,0,0),(0,1,0),(0,0,1)] + face_nodes = [ + Vector{Ti}[[1],[2],[3],[4]], + Vector{Ti}[[1,2],[1,3],[2,3],[1,4],[2,4],[3,4]], + Vector{Ti}[[1,2,3],[1,2,4],[1,3,4],[2,3,4]], + ] + face_reference_id = [ones(Ti,4),ones(Ti,6),ones(Ti,4)] + n1 = sqrt(3)/3 + outwards_normals = SVector{3,Tv}[(0,0,-1),(0,-1,0),(-1,0,0),(n1,n1,n1)] + elseif is_unit_n_cube(geom) + g0 = unit_n_cube(0;real_type=Tv,int_type=Ti) + g1 = unit_n_cube(1;real_type=Tv,int_type=Ti) + g2 = unit_n_cube(2;real_type=Tv,int_type=Ti) + node_coordinates = SVector{3,Tv}[(0,0,0),(1,0,0),(0,1,0),(1,1,0),(0,0,1),(1,0,1),(0,1,1),(1,1,1)] + face_nodes = [ + Vector{Ti}[[1],[2],[3],[4],[5],[6],[7],[8]], + Vector{Ti}[[1,2],[3,4],[1,3],[2,4],[5,6],[7,8],[5,7],[6,8],[1,5],[3,7],[2,6],[4,8]], + Vector{Ti}[[1,2,3,4],[5,6,7,8],[1,2,5,6],[3,4,7,8],[1,3,5,7],[2,4,6,8]], + ] + face_reference_id = [ones(Ti,8),ones(Ti,12),ones(Ti,6)] + outwards_normals = SVector{3,Tv}[(0,0,-1),(0,0,1),(0,-1,0),(0,1,0),(-1,0,0),(1,0,0)] + else + error("Not implemented") + end + order = 1 + fe0 = lagrangian_fe(g0,order) + fe1 = lagrangian_fe(g1,order) + fe2 = lagrangian_fe(g2,order) + reference_faces = ([fe0,],[fe1],[fe2]) + fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + outwards_normals + ) +end + +function boundary(refface::AbstractMeshFace) + boundary_from_mesh_face(refface) +end + +function boundary_from_mesh_face(refface) + geom = geometry(refface) + node_coordinates_inter = node_coordinates(refface) + order_inter = order(refface) + D = num_dims(geom) + if D == 0 + return nothing + end + mesh_geom = boundary(geom) + node_coordinates_geom = node_coordinates(mesh_geom) + face_nodes_inter = Vector{Vector{Vector{Int}}}(undef,D) + node_coordinates_aux = map(xi->map(xii->round(Int,order_inter*xii),xi),node_coordinates_inter) + ref_faces_geom = reference_faces(mesh_geom) + ref_faces = map(ref_faces_geom) do ref_faces_geom_d + map(r->lagrangian_fe(geometry(r),order_inter),ref_faces_geom_d) + end + face_ref_id_geom = face_reference_id(mesh_geom) + for d in 0:(D-1) + s_ref = map(ref_faces_geom[d+1],ref_faces[d+1]) do r_geom,r + m = num_nodes(r) + n = num_nodes(r_geom) + x = node_coordinates(r) + tabulator(r_geom)(value,x) + end + face_nodes_geom = face_nodes(mesh_geom,d) + nfaces = length(face_ref_id_geom[d+1]) + face_nodes_inter_d = Vector{Vector{Int}}(undef,nfaces) + for face in 1:nfaces + ref_id_geom = face_ref_id_geom[d+1][face] + s = s_ref[ref_id_geom] + nodes_geom = face_nodes_geom[face] + nnodes, nnodes_geom = size(s) + x_mapped = map(1:nnodes) do i + x = zero(eltype(node_coordinates_inter)) + for k in 1:nnodes_geom + x += node_coordinates_geom[nodes_geom[k]]*s[i,k] + end + map(xi->round(Int,order_inter*xi),x) + end + my_nodes = indexin(x_mapped,node_coordinates_aux) + face_nodes_inter_d[face] = my_nodes + end + face_nodes_inter[d+1] = face_nodes_inter_d + end + fe_mesh( + node_coordinates_inter, + face_nodes_inter, + face_ref_id_geom, + ref_faces) +end + +function interior_nodes(fe::AbstractMeshFace) + interior_nodes_from_mesh_face(fe) +end + +function interior_nodes_from_mesh_face(fe) + nnodes = num_nodes(fe) + D = num_dims(fe|>geometry) + if D == 0 + return collect(1:nnodes) + else + node_is_touched = fill(true,nnodes) + mesh = boundary(fe) + for d in 0:D + face_to_nodes = face_nodes(fe,d) + for nodes in face_to_nodes + node_is_touched[nodes] .= false + end + end + return findall(node_is_touched) + end +end + +function vertex_permutations(geom::AbstractFaceGeometry) + vertex_permutations_from_face_geometry(geom) +end + +## Admissible if the following map is admissible +# phi_i(x) = sum_i x_perm[i] * fun_i(x) +# This map sends vertex i to vertex perm[i] +function vertex_permutations_from_face_geometry(geo) + D = num_dims(geo) + if D == 0 + return [[1]] + end + geo_mesh = boundary(geo) + vertex_to_geo_nodes = face_nodes(geo_mesh,0) + vertex_to_geo_node = map(first,vertex_to_geo_nodes) + nvertices = length(vertex_to_geo_node) + # TODO compute this more lazily + # so that we never compute it for 3d + # since it is not needed + if D > 2 + return [collect(1:nvertices)] + end + permutations = Combinatorics.permutations(1:nvertices) + if is_simplex(geo) + return collect(permutations) + end + admissible_permutations = Vector{Int}[] + order = 1 + ref_face = lagrangian_fe(geo,order) + fun_mesh = boundary(ref_face) + geo_node_coords = node_coordinates(geo_mesh) + fun_node_coords = node_coordinates(fun_mesh) + vertex_coords = geo_node_coords[vertex_to_geo_node] + degree = 1 + quad = default_quadrature(geo,degree) + q = coordinates(quad) + Tx = eltype(vertex_coords) + TJ = typeof(zero(Tx)*zero(Tx)') + A = tabulator(ref_face)(ForwardDiff.gradient,q) + function compute_volume(vertex_coords) + vol = zero(eltype(TJ)) + for iq in 1:size(A,1) + J = zero(TJ) + for fun_node in 1:size(A,2) + vertex = fun_node # TODO we are assuming that the vertices and nodes match + g = A[iq,fun_node] + x = vertex_coords[vertex] + J += g*x' + end + vol += abs(det(J)) + end + vol + end + refvol = compute_volume(vertex_coords) + perm_vertex_coords = similar(vertex_coords) + for permutation in permutations + for (j,cj) in enumerate(permutation) + perm_vertex_coords[j] = vertex_coords[cj] + end + vol2 = compute_volume(perm_vertex_coords) + if (refvol + vol2) ≈ (2*refvol) + push!(admissible_permutations,permutation) + end + end + admissible_permutations +end + +function interior_node_permutations(fe::AbstractMeshFace) + interior_node_permutations_from_mesh_face(fe) +end + +function interior_node_permutations_from_mesh_face(refface) + interior_ho_nodes = interior_nodes(refface) + ho_nodes_coordinates = node_coordinates(refface) + geo = geometry(refface) + vertex_perms = vertex_permutations(geo) + if length(interior_ho_nodes) == 0 + return map(i->Int[],vertex_perms) + end + if length(vertex_perms) == 1 + return map(i->collect(1:length(interior_ho_nodes)),vertex_perms) + end + geo_mesh = boundary(geo) + vertex_to_geo_nodes = face_nodes(geo_mesh,0) + vertex_to_geo_node = map(first,vertex_to_geo_nodes) + ref_face = lagrangian_reference_face(geo) + fun_mesh = boundary(ref_face) + geo_node_coords = node_coordinates(geo_mesh) + fun_node_coords = node_coordinates(fun_mesh) + vertex_coords = geo_node_coords[vertex_to_geo_node] + q = ho_nodes_coordinates[interior_ho_nodes] + Tx = eltype(vertex_coords) + A = zeros(Float64,length(q),length(fun_node_coords)) + A = tabulator(ref_face)(value,q) + perm_vertex_coords = similar(vertex_coords) + node_perms = similar(vertex_perms) + for (iperm,permutation) in enumerate(vertex_perms) + for (j,cj) in enumerate(permutation) + perm_vertex_coords[j] = vertex_coords[cj] + end + node_to_pnode = fill(INVALID_ID,length(interior_ho_nodes)) + for iq in 1:size(A,1) + y = zero(Tx) + for fun_node in 1:size(A,2) + vertex = fun_node # TODO we are assuming that the vertices and nodes match + g = A[iq,fun_node] + x = perm_vertex_coords[vertex] + y += g*x + end + pnode = findfirst(i->(norm(i-y)+1)≈1,q) + if pnode != nothing + node_to_pnode[iq] = pnode + end + end + node_perms[iperm] = node_to_pnode + end + node_perms +end + +abstract type AbstractMeshTopology <: GalerkinToolkitDataType end + +struct GenericMeshTopology{A,B,C,D} <: AbstractMeshTopology + face_incidence::A + face_reference_id::B + face_permutation_ids::C + reference_faces::D +end + +function mesh_topology(args...) + GenericMeshTopology(args...) +end + +function topology(mesh::AbstractFEMesh) + topology_from_mesh(mesh) +end + +function topology_from_mesh(mesh) + # Assumes that the input is a cell complex + T = JaggedArray{Int32,Int32} + D = num_dims(mesh) + my_face_incidence = Matrix{T}(undef,D+1,D+1) + my_face_reference_id = [ face_reference_id(mesh,d) for d in 0:D ] + my_reference_faces = Tuple([ map(topology,reference_faces(mesh,d)) for d in 0:D ]) + my_face_permutation_ids = Matrix{T}(undef,D+1,D+1) + topo = mesh_topology( + my_face_incidence, + my_face_reference_id, + my_face_permutation_ids, + my_reference_faces, + ) + for d in 0:D + fill_face_interior_mesh_topology!(topo,mesh,d) + end + for d in 1:D + fill_face_vertices_mesh_topology!(topo,mesh,d) + fill_face_coboundary_mesh_topology!(topo,mesh,d,0) + end + for d in 1:(D-1) + for n in (D-d):-1:1 + m = n+d + fill_face_boundary_mesh_topology!(topo,mesh,m,n) + fill_face_coboundary_mesh_topology!(topo,mesh,m,n) + end + end + for d in 0:D + for n in 0:d + fill_face_permutation_ids!(topo,d,n) + end + end + topo +end + +function fill_face_interior_mesh_topology!(mesh_topology,mesh,d) + n = num_faces(mesh,d) + ptrs = collect(Int32,1:(n+1)) + data = collect(Int32,1:n) + mesh_topology.face_incidence[d+1,d+1] = JaggedArray(data,ptrs) +end + +function generate_face_coboundary(nface_to_mfaces,nmfaces) + ptrs = zeros(Int32,nmfaces+1) + nnfaces = length(nface_to_mfaces) + for nface in 1:nnfaces + mfaces = nface_to_mfaces[nface] + for mface in mfaces + ptrs[mface+1] += Int32(1) + end + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = zeros(Int32,ndata) + for nface in 1:nnfaces + mfaces = nface_to_mfaces[nface] + for mface in mfaces + p = ptrs[mface] + data[p] = nface + ptrs[mface] += Int32(1) + end + end + rewind_ptrs!(ptrs) + mface_to_nfaces = JaggedArray(data,ptrs) + mface_to_nfaces +end + +function fill_face_coboundary_mesh_topology!(topology,mesh,n,m) + nmfaces = num_faces(mesh,m) + nface_to_mfaces = face_incidence(topology,n,m) + face_incidence(topology)[m+1,n+1] = generate_face_coboundary(nface_to_mfaces,nmfaces) +end + +function fill_face_vertices_mesh_topology!(topo,mesh,d) + function barrier(nnodes,vertex_to_nodes,dface_to_nodes,dface_to_refid,refid_to_lvertex_to_lnodes) + vertex_to_node = JaggedArray(vertex_to_nodes).data + #if vertex_to_node == 1:nnodes + # return dface_to_nodes + #end + node_to_vertex = zeros(Int32,nnodes) + nvertices = length(vertex_to_nodes) + node_to_vertex[vertex_to_node] = 1:nvertices + ndfaces = length(dface_to_nodes) + dface_to_vertices_ptrs = zeros(Int32,ndfaces+1) + for dface in 1:ndfaces + refid = dface_to_refid[dface] + nlvertices = length(refid_to_lvertex_to_lnodes[refid]) + dface_to_vertices_ptrs[dface+1] = nlvertices + end + length_to_ptrs!(dface_to_vertices_ptrs) + ndata = dface_to_vertices_ptrs[end]-1 + dface_to_vertices_data = zeros(Int32,ndata) + for dface in 1:ndfaces + refid = dface_to_refid[dface] + lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] + nlvertices = length(lvertex_to_lnodes) + lnode_to_node = dface_to_nodes[dface] + offset = dface_to_vertices_ptrs[dface]-1 + for lvertex in 1:nlvertices + lnode = first(lvertex_to_lnodes[lvertex]) + node = lnode_to_node[lnode] + vertex = node_to_vertex[node] + dface_to_vertices_data[offset+lvertex] = vertex + end + end + dface_to_vertices = JaggedArray(dface_to_vertices_data,dface_to_vertices_ptrs) + end + nnodes = num_nodes(mesh) + vertex_to_nodes = face_nodes(mesh,0) + dface_to_nodes = face_nodes(mesh,d) + dface_to_refid = face_reference_id(mesh,d) + refid_refface = reference_faces(mesh,d) + refid_to_lvertex_to_lnodes = map(refface->face_nodes(boundary(refface),0),refid_refface) + face_incidence(topo)[d+1,0+1] = barrier(nnodes,vertex_to_nodes,dface_to_nodes,dface_to_refid,refid_to_lvertex_to_lnodes) +end + +function fill_face_boundary_mesh_topology!(topo,mesh,D,d) + function barrier( + Dface_to_vertices, + vertex_to_Dfaces, + dface_to_vertices, + vertex_to_dfaces, + Dface_to_refid, + Drefid_to_ldface_to_lvertices) + + # Count + ndfaces = length(dface_to_vertices) + nDfaces = length(Dface_to_vertices) + # Allocate output + ptrs = zeros(Int32,nDfaces+1) + for Dface in 1:nDfaces + Drefid = Dface_to_refid[Dface] + ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] + ptrs[Dface+1] = length(ldface_to_lvertices) + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = fill(Int32(INVALID_ID),ndata) + Dface_to_dfaces = JaggedArray(data,ptrs) + for Dface in 1:nDfaces + Drefid = Dface_to_refid[Dface] + ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] + lvertex_to_vertex = Dface_to_vertices[Dface] + ldface_to_dface = Dface_to_dfaces[Dface] + for (ldface,lvertices) in enumerate(ldface_to_lvertices) + # Find the global d-face for this local d-face + dface2 = Int32(INVALID_ID) + vertices = view(lvertex_to_vertex,lvertices) + for (i,lvertex) in enumerate(lvertices) + vertex = lvertex_to_vertex[lvertex] + dfaces = vertex_to_dfaces[vertex] + for dface1 in dfaces + vertices1 = dface_to_vertices[dface1] + if same_valid_ids(vertices,vertices1) + dface2 = dface1 + break + end + end + if dface2 != Int32(INVALID_ID) + break + end + end + @boundscheck begin + msg = """ + + + Error in: topology_from_mesh + + The given mesh is provably not a cell complex. + """ + @assert dface2 != Int32(INVALID_ID) msg + end + ldface_to_dface[ldface] = dface2 + end # (ldface,lvertices) + end # Dface + Dface_to_dfaces + end + Dface_to_vertices = face_incidence(topo,D,0) + vertex_to_Dfaces = face_incidence(topo,0,D) + dface_to_vertices = face_incidence(topo,d,0) + vertex_to_dfaces = face_incidence(topo,0,d) + Dface_to_refid = face_reference_id(topo,D) + refid_refface = reference_faces(topo,D) + Drefid_to_ldface_to_lvertices = map(refface->face_incidence(boundary(refface),d,0),refid_refface) + Dface_to_dfaces = barrier( + Dface_to_vertices, + vertex_to_Dfaces, + dface_to_vertices, + vertex_to_dfaces, + Dface_to_refid, + Drefid_to_ldface_to_lvertices) + topo.face_incidence[D+1,d+1] = Dface_to_dfaces +end + +function fill_face_permutation_ids!(top,D,d) + function barrier!( + cell_to_lface_to_pindex, + cell_to_lface_to_face, + cell_to_cvertex_to_vertex, + cell_to_ctype, + ctype_to_lface_to_cvertices, + face_to_fvertex_to_vertex, + face_to_ftype, + ftype_to_pindex_to_cfvertex_to_fvertex) + + ncells = length(cell_to_lface_to_face) + for cell in 1:ncells + ctype = cell_to_ctype[cell] + lface_to_cvertices = ctype_to_lface_to_cvertices[ctype] + a = cell_to_lface_to_face.ptrs[cell]-1 + c = cell_to_cvertex_to_vertex.ptrs[cell]-1 + for (lface,cfvertex_to_cvertex) in enumerate(lface_to_cvertices) + face = cell_to_lface_to_face.data[a+lface] + ftype = face_to_ftype[face] + b = face_to_fvertex_to_vertex.ptrs[face]-1 + pindex_to_cfvertex_to_fvertex = ftype_to_pindex_to_cfvertex_to_fvertex[ftype] + pindexfound = false + for (pindex, cfvertex_to_fvertex) in enumerate(pindex_to_cfvertex_to_fvertex) + found = true + for (cfvertex,fvertex) in enumerate(cfvertex_to_fvertex) + vertex1 = face_to_fvertex_to_vertex.data[b+fvertex] + cvertex = cfvertex_to_cvertex[cfvertex] + vertex2 = cell_to_cvertex_to_vertex.data[c+cvertex] + if vertex1 != vertex2 + found = false + break + end + end + if found + cell_to_lface_to_pindex.data[a+lface] = pindex + pindexfound = true + break + end + end + @assert pindexfound "Valid pindex not found" + end + end + end + @assert D >= d + cell_to_lface_to_face = JaggedArray(face_incidence(top,D,d)) + data = similar(cell_to_lface_to_face.data,Int8) + ptrs = cell_to_lface_to_face.ptrs + cell_to_lface_to_pindex = JaggedArray(data,ptrs) + if d == D || d == 0 + fill!(cell_to_lface_to_pindex.data,Int8(1)) + top.face_permutation_ids[D+1,d+1] = cell_to_lface_to_pindex + return top + end + face_to_fvertex_to_vertex = JaggedArray(face_incidence(top,d,0)) + face_to_ftype = face_reference_id(top,d) + ref_dfaces = reference_faces(top,d) + ftype_to_pindex_to_cfvertex_to_fvertex = map(vertex_permutations,ref_dfaces) + cell_to_cvertex_to_vertex = JaggedArray(face_incidence(top,D,0)) + cell_to_ctype = face_reference_id(top,D) + ref_Dfaces = reference_faces(top,D) + ctype_to_lface_to_cvertices = map(p->face_incidence(boundary(p),d,0),ref_Dfaces) + barrier!( + cell_to_lface_to_pindex, + cell_to_lface_to_face, + cell_to_cvertex_to_vertex, + cell_to_ctype, + ctype_to_lface_to_cvertices, + face_to_fvertex_to_vertex, + face_to_ftype, + ftype_to_pindex_to_cfvertex_to_fvertex) + top.face_permutation_ids[D+1,d+1] = cell_to_lface_to_pindex + top +end + +function intersection!(a,b,na,nb) + function findeq!(i,a,b,nb) + for j in 1:nb + if a[i] == b[j] + return + end + end + a[i] = INVALID_ID + return + end + for i in 1:na + if a[i] == INVALID_ID + continue + end + findeq!(i,a,b,nb) + end +end + +function same_valid_ids(a,b) + function is_subset(a,b) + for i in 1:length(a) + v = a[i] + if v == INVALID_ID + continue + end + c = find_eq(v,b) + if c == false; return false; end + end + return true + end + function find_eq(v,b) + for vs in b + if v == vs + return true + end + end + return false + end + c = is_subset(a,b) + if c == false; return false; end + c = is_subset(b,a) + if c == false; return false; end + return true +end + +## TODO AbstractFaceTopology <: AbstractMeshTopology +abstract type AbstractFaceTopology <: GalerkinToolkitDataType end + +struct GenericFaceTopology{A,B} <: AbstractFaceTopology + boundary::A + vertex_permutations::B +end + +function face_topology(args...) + GenericFaceTopology(args...) +end + +num_dims(a::AbstractFaceTopology) = num_dims(boundary(a))+1 + +function topology(fe::AbstractMeshFace) + topology_from_mesh_face(fe) +end + +function topology_from_mesh_face(refface) + geom = geometry(refface) + D = num_dims(geom) + if D != 0 + myboundary = geom |> boundary |> topology + else + myboundary = nothing + end + myperms = vertex_permutations(geom) + face_topology(myboundary,myperms) +end + +function complexify(mesh::AbstractFEMesh) + complexify_mesh(mesh) +end + +function complexify_mesh(mesh) + Ti = Int32 + T = JaggedArray{Ti,Ti} + D = num_dims(mesh) + oldface_to_newvertices = Vector{T}(undef,D+1) + newvertex_to_oldfaces = Vector{T}(undef,D+1) + newface_incidence = Matrix{T}(undef,D+1,D+1) + nnewfaces = zeros(Int,D+1) + newface_refid = Vector{Vector{Ti}}(undef,D+1) + newreffaces = Vector{Any}(undef,D+1) + newface_nodes = Vector{T}(undef,D+1) + old_to_new = Vector{Vector{Ti}}(undef,D+1) + node_to_newvertex, n_new_vertices = find_node_to_vertex(mesh) # Optimizable for linear meshes + for d in 0:D + oldface_to_newvertices[d+1] = fill_face_vertices(mesh,d,node_to_newvertex) # Optimizable for linear meshes + newvertex_to_oldfaces[d+1] = generate_face_coboundary(oldface_to_newvertices[d+1],n_new_vertices) # Optimizable for linear meshes + end + newface_incidence[D+1,0+1] = oldface_to_newvertices[D+1] + newface_incidence[0+1,D+1] = newvertex_to_oldfaces[D+1] + nnewfaces[D+1] = length(oldface_to_newvertices[D+1]) + newface_refid[D+1] = face_reference_id(mesh,D) + newreffaces[D+1] = reference_faces(mesh,D) + newface_nodes[D+1] = face_nodes(mesh,D) + old_to_new[D+1] = collect(Ti,1:length(newface_nodes[D+1])) + # TODO optimize for d==0 + for d in (D-1):-1:0 + n = d+1 + new_nface_to_new_vertices = newface_incidence[n+1,0+1] + new_vertex_to_new_nfaces = newface_incidence[0+1,n+1] + old_dface_to_new_vertices = oldface_to_newvertices[d+1] + new_vertex_to_old_dfaces = newvertex_to_oldfaces[d+1] + new_nface_to_nrefid = newface_refid[n+1] + old_dface_to_drefid = face_reference_id(mesh,d) + drefid_to_ref_dface = reference_faces(mesh,d) + old_dface_to_nodes = face_nodes(mesh,d) + new_nface_to_nodes = newface_nodes[n+1] + nrefid_to_ldface_to_lvertices = map(a->face_incidence(topology(boundary(geometry(a))),d,0),newreffaces[n+1]) + nrefid_to_ldface_to_lnodes = map(a->face_nodes(boundary(a),d),newreffaces[n+1]) + nrefid_to_ldface_to_drefrefid = map(a->face_reference_id(boundary(a),d),newreffaces[n+1]) + nrefid_to_drefrefid_to_ref_dface = map(a->reference_faces(boundary(a),d),newreffaces[n+1]) + new_nface_to_new_dfaces, n_new_dfaces, old_dface_to_new_dface = generate_face_boundary( + new_nface_to_new_vertices, + new_vertex_to_new_nfaces, + old_dface_to_new_vertices, + new_vertex_to_old_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_lvertices) + new_dface_to_new_vertices = generate_face_vertices( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_new_vertices, + new_nface_to_new_vertices, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_lvertices) + new_vertex_to_new_dfaces = generate_face_coboundary(new_dface_to_new_vertices,n_new_vertices) + new_dface_to_nodes = generate_face_vertices( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_nodes, + new_nface_to_nodes, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_lnodes) + new_dface_to_new_drefid, new_refid_to_ref_dface = generate_reference_faces( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_drefid, + drefid_to_ref_dface, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_drefrefid, + nrefid_to_drefrefid_to_ref_dface) + newface_incidence[n+1,d+1] = new_nface_to_new_dfaces + newface_incidence[d+1,0+1] = new_dface_to_new_vertices + newface_incidence[0+1,d+1] = new_vertex_to_new_dfaces + newface_refid[d+1] = new_dface_to_new_drefid + newreffaces[d+1] = new_refid_to_ref_dface + nnewfaces[d+1] = n_new_dfaces + newface_nodes[d+1] = new_dface_to_nodes + old_to_new[d+1] = old_dface_to_new_dface + end + node_to_coords = node_coordinates(mesh) + old_physical_groups = physical_groups(mesh) + new_physical_groups = [ Dict{String,Vector{Int32}}() for d in 0:D] # TODO hardcoded + for d in 0:D + old_groups = old_physical_groups[d+1] + for (group_name,old_group_faces) in old_groups + new_group_faces = similar(old_group_faces) + new_group_faces .= old_to_new[d+1][old_group_faces] + new_physical_groups[d+1][group_name] = new_group_faces + end + end + new_mesh = fe_mesh( + node_to_coords, + newface_nodes, + newface_refid, + Tuple(newreffaces); + physical_groups = new_physical_groups, + periodic_nodes = periodic_nodes(mesh), + outwards_normals = outwards_normals(mesh) + ) + new_mesh, old_to_new +end + +function generate_face_vertices( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_new_vertices, + new_nface_to_new_vertices, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_lvertices + ) + + Ti = eltype(eltype(old_dface_to_new_vertices)) + new_dface_to_touched = fill(false,n_new_dfaces) + new_dface_to_new_vertices_ptrs = zeros(Ti,n_new_dfaces+1) + n_old_dfaces = length(old_dface_to_new_dface) + for old_dface in 1:n_old_dfaces + new_dface = old_dface_to_new_dface[old_dface] + new_vertices = old_dface_to_new_vertices[old_dface] + new_dface_to_new_vertices_ptrs[new_dface+1] = length(new_vertices) + new_dface_to_touched[new_dface] = true + end + n_new_nfaces = length(new_nface_to_new_vertices) + for new_nface in 1:n_new_nfaces + nrefid = new_nface_to_nrefid[new_nface] + ldface_to_lvertices = nrefid_to_ldface_to_lvertices[nrefid] + ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] + n_ldfaces = length(ldface_to_new_dface) + for ldface in 1:n_ldfaces + new_dface = ldface_to_new_dface[ldface] + if new_dface_to_touched[new_dface] + continue + end + lvertices = ldface_to_lvertices[ldface] + new_dface_to_new_vertices_ptrs[new_dface+1] = length(lvertices) + new_dface_to_touched[new_dface] = true + end + + end + length_to_ptrs!(new_dface_to_new_vertices_ptrs) + ndata = new_dface_to_new_vertices_ptrs[end]-1 + new_dface_to_new_vertices_data = zeros(Ti,ndata) + new_dface_to_new_vertices = JaggedArray(new_dface_to_new_vertices_data,new_dface_to_new_vertices_ptrs) + fill!(new_dface_to_touched,false) + for old_dface in 1:n_old_dfaces + new_dface = old_dface_to_new_dface[old_dface] + new_vertices_in = old_dface_to_new_vertices[old_dface] + new_vertices_out = new_dface_to_new_vertices[new_dface] + for i in 1:length(new_vertices_in) + new_vertices_out[i] = new_vertices_in[i] + end + new_dface_to_touched[new_dface] = true + end + for new_nface in 1:n_new_nfaces + nrefid = new_nface_to_nrefid[new_nface] + ldface_to_lvertices = nrefid_to_ldface_to_lvertices[nrefid] + ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] + n_ldfaces = length(ldface_to_new_dface) + new_vertices_in = new_nface_to_new_vertices[new_nface] + for ldface in 1:n_ldfaces + new_dface = ldface_to_new_dface[ldface] + if new_dface_to_touched[new_dface] + continue + end + new_vertices_out = new_dface_to_new_vertices[new_dface] + lvertices = ldface_to_lvertices[ldface] + for i in 1:length(lvertices) + new_vertices_out[i] = new_vertices_in[lvertices[i]] + end + new_dface_to_touched[new_dface] = true + end + + end + new_dface_to_new_vertices +end + +function generate_reference_faces( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_drefid, + drefid_to_ref_dface, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_drefrefid, + nrefid_to_drefrefid_to_ref_dface) + + i_to_ref_dface = collect(Any,drefid_to_ref_dface) + drefid_to_i = collect(1:length(drefid_to_ref_dface)) + i = length(i_to_ref_dface) + Ti = Int32 + nrefid_to_drefrefid_to_i = map(a->zeros(Ti,length(a)),nrefid_to_drefrefid_to_ref_dface) + for (nrefid,drefrefid_to_ref_dface) in enumerate(nrefid_to_drefrefid_to_ref_dface) + for (drefrefid, ref_dface) in enumerate(drefrefid_to_ref_dface) + push!(i_to_ref_dface,ref_dface) + i += 1 + nrefid_to_drefrefid_to_i[nrefid][drefrefid] = i + end + end + u_to_ref_dface = unique(i_to_ref_dface) + i_to_u = indexin(i_to_ref_dface,u_to_ref_dface) + new_dface_to_u = zeros(Ti,n_new_dfaces) + new_dface_to_touched = fill(false,n_new_dfaces) + for (old_dface,new_dface) in enumerate(old_dface_to_new_dface) + drefid = old_dface_to_drefid[old_dface] + i = drefid_to_i[drefid] + u = i_to_u[i] + new_dface_to_u[new_dface] = u + new_dface_to_touched[new_dface] = true + end + for (new_nface,nrefid) in enumerate(new_nface_to_nrefid) + ldface_to_drefrefid = nrefid_to_ldface_to_drefrefid[nrefid] + ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] + drefrefid_to_i = nrefid_to_drefrefid_to_i[nrefid] + for (ldface,new_dface) in enumerate(ldface_to_new_dface) + new_dface = ldface_to_new_dface[ldface] + if new_dface_to_touched[new_dface] + continue + end + drefrefid = ldface_to_drefrefid[ldface] + i = drefrefid_to_i[drefrefid] + u = i_to_u[i] + new_dface_to_u[new_dface] = u + new_dface_to_touched[new_dface] = true + end + end + new_dface_to_u, Tuple(u_to_ref_dface) +end + +function generate_face_boundary( + Dface_to_vertices, + vertex_to_Dfaces, + dface_to_vertices, + vertex_to_dfaces, + Dface_to_refid, + Drefid_to_ldface_to_lvertices) + + # Count + ndfaces = length(dface_to_vertices) + nDfaces = length(Dface_to_vertices) + nvertices = length(vertex_to_Dfaces) + maxldfaces = 0 + for ldface_to_lvertices in Drefid_to_ldface_to_lvertices + maxldfaces = max(maxldfaces,length(ldface_to_lvertices)) + end + maxDfaces = 0 + for vertex in 1:length(vertex_to_Dfaces) + Dfaces = vertex_to_Dfaces[vertex] + maxDfaces = max(maxDfaces,length(Dfaces)) + end + # Allocate output + ptrs = zeros(Int32,nDfaces+1) + for Dface in 1:nDfaces + Drefid = Dface_to_refid[Dface] + ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] + ptrs[Dface+1] = length(ldface_to_lvertices) + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = fill(Int32(INVALID_ID),ndata) + Dface_to_dfaces = GenericJaggedArray(data,ptrs) + # Main loop + Dfaces1 = fill(Int32(INVALID_ID),maxDfaces) + Dfaces2 = fill(Int32(INVALID_ID),maxDfaces) + ldfaces1 = fill(Int32(INVALID_ID),maxDfaces) + nDfaces1 = 0 + nDfaces2 = 0 + newdface = Int32(ndfaces) + old_to_new = collect(Int32,1:ndfaces) + for Dface in 1:nDfaces + Drefid = Dface_to_refid[Dface] + ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] + lvertex_to_vertex = Dface_to_vertices[Dface] + ldface_to_dface = Dface_to_dfaces[Dface] + for (ldface,lvertices) in enumerate(ldface_to_lvertices) + # Do nothing if this local face has already been processed by + # a neighbor + if ldface_to_dface[ldface] != Int32(INVALID_ID) + continue + end + # Find if there is already a global d-face for this local d-face + # if yes, then use the global id of this d-face + # if not, create a new one + dface2 = Int32(INVALID_ID) + fill!(Dfaces1,Int32(INVALID_ID)) + fill!(Dfaces2,Int32(INVALID_ID)) + vertices = view(lvertex_to_vertex,lvertices) + for (i,lvertex) in enumerate(lvertices) + vertex = lvertex_to_vertex[lvertex] + dfaces = vertex_to_dfaces[vertex] + for dface1 in dfaces + vertices1 = dface_to_vertices[dface1] + if same_valid_ids(vertices,vertices1) + dface2 = dface1 + break + end + end + if dface2 != Int32(INVALID_ID) + break + end + end + if dface2 == Int32(INVALID_ID) + newdface += Int32(1) + dface2 = newdface + end + # Find all D-faces around this local d-face + for (i,lvertex) in enumerate(lvertices) + vertex = lvertex_to_vertex[lvertex] + Dfaces = vertex_to_Dfaces[vertex] + if i == 1 + copyto!(Dfaces1,Dfaces) + nDfaces1 = length(Dfaces) + else + copyto!(Dfaces2,Dfaces) + nDfaces2 = length(Dfaces) + intersection!(Dfaces1,Dfaces2,nDfaces1,nDfaces2) + end + end + # Find their correspondent local d-face and set the d-face + for Dface1 in Dfaces1 + if Dface1 != INVALID_ID + Drefid1 = Dface_to_refid[Dface1] + lvertex1_to_vertex1 = Dface_to_vertices[Dface1] + ldface1_to_lvertices1 = Drefid_to_ldface_to_lvertices[Drefid1] + ldface2 = Int32(INVALID_ID) + for (ldface1,lvertices1) in enumerate(ldface1_to_lvertices1) + vertices1 = view(lvertex1_to_vertex1,lvertices1) + if same_valid_ids(vertices,vertices1) + ldface2 = ldface1 + break + end + end + @boundscheck @assert ldface2 != INVALID_ID + Dface_to_dfaces[Dface1][ldface2] = dface2 + end + end + end # (ldface,lvertices) + end # Dface + Dface_to_dfaces, newdface, old_to_new +end + +function fill_face_vertices(mesh,d,node_to_vertex) + function barrier(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) + Ti = eltype(node_to_vertex) + nfaces = length(face_to_nodes) + face_to_vertices_ptrs = zeros(Ti,nfaces+1) + for face in 1:nfaces + nodes = face_to_nodes[face] + refid = face_to_refid[face] + lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] + nlvertices = length(lvertex_to_lnodes) + face_to_vertices_ptrs[face+1] = nlvertices + end + length_to_ptrs!(face_to_vertices_ptrs) + ndata = face_to_vertices_ptrs[end]-1 + face_to_vertices_data = zeros(Ti,ndata) + face_to_vertices = JaggedArray(face_to_vertices_data,face_to_vertices_ptrs) + for face in 1:nfaces + vertices = face_to_vertices[face] + nodes = face_to_nodes[face] + refid = face_to_refid[face] + lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] + for (lvertex,lnodes) in enumerate(lvertex_to_lnodes) + lnode = first(lnodes) + vertex = node_to_vertex[nodes[lnode]] + @boundscheck @assert vertex != INVALID_ID + vertices[lvertex] = vertex + end + end + face_to_vertices + end + face_to_nodes = face_nodes(mesh,d) + face_to_refid = face_reference_id(mesh,d) + refid_to_lvertex_to_lnodes = map(reference_faces(mesh,d)) do a + if num_dims(geometry(a)) != 0 + face_nodes(boundary(a),0) + else + [interior_nodes(a)] + end + end + barrier(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) +end + +function find_node_to_vertex(mesh) + function barrier!(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) + valid_id = one(eltype(node_to_vertex)) + for face in eachindex(face_to_nodes) + nodes = face_to_nodes[face] + refid = face_to_refid[face] + lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] + for lnodes in lvertex_to_lnodes + lnode = first(lnodes) + node = nodes[lnode] + node_to_vertex[node] = valid_id + end + end + end + Ti = Int32 + nnodes = num_nodes(mesh) + node_to_vertex = zeros(Ti,nnodes) + fill!(node_to_vertex,Ti(INVALID_ID)) + D = num_dims(mesh) + for d in 0:D + face_to_nodes = face_nodes(mesh,d) + if length(face_to_nodes) == 0 + continue + end + face_to_refid = face_reference_id(mesh,d) + refid_to_lvertex_to_lnodes = map(reference_faces(mesh,d)) do a + if num_dims(geometry(a)) != 0 + face_nodes(boundary(a),0) + else + [interior_nodes(a)] + end + end + barrier!(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) + end + vertex = Ti(0) + for node in eachindex(node_to_vertex) + if node_to_vertex[node] != Ti(INVALID_ID) + vertex += Ti(1) + node_to_vertex[node] = vertex + end + end + node_to_vertex, vertex +end + +abstract type AbstractFEChain <: GalerkinToolkitDataType end + +struct GenericFEChain{A,B,C,D,E,F,G} <: AbstractFEChain + node_coordinates::A + face_nodes::B + face_reference_id::C + reference_faces::D + periodic_nodes::E + physical_groups::F + outwards_normals::G +end + +function fe_chain(args...) + GenericFEChain(args...) +end + +function fe_chain( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + periodic_nodes = eltype(eltype(face_reference_id))[], + physical_groups = map(i->Dict{String,Vector{eltype(eltype(face_reference_id))}}(),face_reference_id), + outwards_normals = nothing + ) + fe_chain( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces, + periodic_nodes, + physical_groups, + outwards_normals) +end + +num_dims(mesh::AbstractFEChain) = num_dims(first(reference_faces(mesh))) + +function fe_mesh(chain::AbstractFEChain) + mesh_from_chain(chain) +end + +function mesh_from_chain(chain) + D = num_dims(chain) + cell_nodes = face_nodes(chain) + cell_reference_id = face_reference_id(chain) + reference_cells = reference_faces(chain) + node_coords = node_coordinates(chain) + face_to_nodes = Vector{typeof(cell_nodes)}(undef,D+1) + face_to_refid = Vector{typeof(cell_reference_id)}(undef,D+1) + for d in 0:D-1 + face_to_nodes[d+1] = Vector{Int}[] + face_to_refid[d+1] = Int[] + end + face_to_nodes[end] = cell_nodes + face_to_refid[end] = cell_reference_id + ref_cell = first(reference_cells) + ref_faces = reference_faces(boundary(ref_cell)) + refid_to_refface = push(ref_faces,reference_cells) + cell_groups = physical_groups(chain) + groups = [ typeof(cell_groups)() for d in 0:D] + groups[end] = cell_groups + pnodes = periodic_nodes(chain) + onormals = outwards_normals(chain) + fe_mesh( + node_coords, + face_to_nodes, + face_to_refid, + refid_to_refface, + pnodes, + groups, + onormals) +end + +function simplexify(geo::AbstractFaceGeometry) + simplexify_face_geometry(geo) +end + +function simplexify_face_geometry(geo) + if is_unit_simplex(geo) + simplexify_unit_simplex(geo) + elseif is_unit_n_cube(geo) + simplexify_unit_n_cube(geo) + else + error("case not implemented") + end +end + +function simplexify_unit_simplex(geo) + @assert is_unit_simplex(geo) + refface = lagrangian_reference_face(geo) + mesh_from_reference_face(refface) +end + +function simplexify_unit_n_cube(geo) + @assert is_unit_n_cube(geo) + D = num_dims(geo) + if D in (0,1) + return simplexify_unit_simplex(geo) + end + simplex = unit_simplex(Val(D)) + order = 1 + ref_cell = lagrangian_fe(simplex,order) + node_coords = node_coordinates(boundary(geo)) + cell_nodes = simplex_node_ids_n_cube(geo) + ncells = length(cell_nodes) + cell_reference_id = fill(Int8(1),ncells) + reference_cells = [ref_cell] + chain = fe_chain( + node_coords, + cell_nodes, + cell_reference_id, + reference_cells, + ) + mesh = mesh_from_chain(chain) + mesh_complex, = complexify_mesh(mesh) + groups = [Dict{String,Vector{Int32}}() for d in 0:D] + for d in 0:(D-1) + sface_to_nodes = face_nodes(mesh_complex,d) + cface_to_nodes = face_nodes(boundary(geo),d) + nsfaces = length(sface_to_nodes) + ncfaces = length(cface_to_nodes) + sface_touched = fill(false,nsfaces) + for cface in 1:ncfaces + fill!(sface_touched,false) + nodes_c = cface_to_nodes[cface] + for sface in 1:nsfaces + nodes_s = sface_to_nodes[sface] + if all(map(n->n in nodes_c,nodes_s)) + sface_touched[sface] = true + end + end + sfaces_in_group = findall(sface_touched) + group_name = "$d-face-$cface" + groups[d+1][group_name] = sfaces_in_group + end + end + groups[end]["interior"] = 1:(num_faces(mesh_complex,D)) + sface_is_boundary = fill(false,num_faces(mesh_complex,D-1)) + for (_,sfaces) in groups[end-1] + sface_is_boundary[sfaces] .= true + end + groups[end-1]["boundary"] = findall(sface_is_boundary) + physical_groups(mesh_complex) .= groups + mesh_complex +end + +function simplex_node_ids_n_cube(geo) + D = num_dims(geo) + # TODO check orientation of nodes + # this assumes lexicographic ordering + # for 3d nodes ids are carefully selected + # such that opposite faces match. + # This allows one to complexify meshes + # of ncubes with oriented faces + if D == 0 + [[1]] + elseif D == 1 + [[1,2]] + elseif D == 2 + [[1,2,3],[2,3,4]] + elseif D ==3 + [[1,2,3,7], [1,2,5,7], [2,3,4,7], + [2,4,7,8], [2,5,6,7], [2,6,7,8]] + else + error("case not implemented") + end +end + +function simplexify_reference_face(ref_face) + mesh_geom = simplexify(geometry(ref_face)) + D = num_dims(mesh_geom) + node_coordinates_geom = node_coordinates(mesh_geom) + ref_faces_geom = reference_faces(mesh_geom,D) + face_nodes_geom = face_nodes(mesh_geom,D) + face_ref_id_geom = face_reference_id(mesh_geom,D) + nfaces = length(face_ref_id_geom) + # We need the same order in all directions + # for this to make sense + my_order = order(ref_face) + ref_faces_inter = map(r_geom->lagrangian_reference_face(geometry(r_geom),order=my_order),ref_faces_geom) + s_ref = map(ref_faces_geom,ref_faces_inter) do r_geom,r + m = num_nodes(r) + n = num_nodes(r_geom) + x = node_coordinates(r) + tabulator(r_geom)(value,x) + end + node_coordinates_inter = node_coordinates(ref_face) + node_coordinates_aux = map(xi->map(xii->round(Int,my_order*xii),xi),node_coordinates_inter) + face_nodes_inter = Vector{Vector{Int}}(undef,nfaces) + for face in 1:nfaces + ref_id_geom = face_ref_id_geom[face] + s = s_ref[ref_id_geom] + nodes_geom = face_nodes_geom[face] + nnodes, nnodes_geom = size(s) + x_mapped = map(1:nnodes) do i + x = zero(eltype(node_coordinates_inter)) + for k in 1:nnodes_geom + x += node_coordinates_geom[nodes_geom[k]]*s[i,k] + end + map(xi->round(Int,my_order*xi),x) + end + my_nodes = indexin(x_mapped,node_coordinates_aux) + face_nodes_inter[face] = my_nodes + end + ref_inter = + chain = Chain(; + num_dims=Val(D), + node_coordinates=node_coordinates_inter, + face_nodes = face_nodes_inter, + face_reference_id = face_ref_id_geom, + reference_faces = ref_faces_inter, + ) + mesh = mesh_from_chain(chain) + mesh_complex, = complexify_mesh(mesh) + pg = physical_groups(mesh_complex) + pg .= physical_groups(mesh_geom) + mesh_complex +end + +function cartesian_mesh(domain,cells_per_dir;boundary=true,complexify=true,simplexify=false) + mesh = if boundary + if simplexify + structured_simplex_mesh_with_boundary(domain,cells_per_dir) + else + cartesian_mesh_with_boundary(domain,cells_per_dir) + end + else + if simplexify + chain = structured_simplex_chain(domain,cells_per_dir) + else + chain = cartesian_chain(domain,cells_per_dir) + end + mesh_from_chain(chain) + end + if complexify + mesh, = complexify_mesh(mesh) + end + mesh +end + +function bounding_box_from_domain(domain) + l = length(domain) + D = div(l,2) + pmin = SVector(ntuple(d->domain[2*(d-1)+1],Val(D))) + pmax = SVector(ntuple(d->domain[2*d],Val(D))) + (pmin,pmax) +end + +function domain_from_bounding_box(box) + l = sum(length,box) + ntuple(Val(l)) do i + vector = mod(i-1,2)+1 + component = div(i-1,2)+1 + box[vector][component] + end +end + +function cartesian_mesh_with_boundary(domain,cells_per_dir) + function barrier( + D, + cell_to_nodes, + nnodes, + d_to_ldface_to_lnodes) + + node_to_n = zeros(Int32,nnodes) + for nodes in cell_to_nodes + for node in nodes + node_to_n[node] += Int32(1) + end + end + J = typeof(JaggedArray(Vector{Int32}[])) + face_to_nodes = Vector{J}(undef,D) + groups = [ Dict{String,Vector{Int32}}() for d in 0:(D-1) ] + ngroups = 0 + for d in 0:(D-1) + nmax = 2^d + ldface_to_lnodes = d_to_ldface_to_lnodes[d+1] + ndfaces = 0 + for nodes in cell_to_nodes + for (ldface,lnodes) in enumerate(ldface_to_lnodes) + isboundary = true + for lnode in lnodes + node = nodes[lnode] + if node_to_n[node] > nmax + isboundary = false + break + end + end + if isboundary + ndfaces += 1 + end + end + end + ptrs = zeros(Int32,ndfaces+1) + for dface in 1:ndfaces + ptrs[dface+1] += Int32(nmax) + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = zeros(Int32,ndata) + dface_to_physical_group = zeros(Int32,ndfaces) + ndfaces = 0 + for nodes in cell_to_nodes + for (ldface,lnodes) in enumerate(ldface_to_lnodes) + isboundary = true + for lnode in lnodes + node = nodes[lnode] + if node_to_n[node] > nmax + isboundary = false + break + end + end + if isboundary + ndfaces += 1 + group = ngroups + ldface + dface_to_physical_group[ndfaces] = group + p = ptrs[ndfaces]-Int32(1) + for (i,lnode) in enumerate(lnodes) + node = nodes[lnode] + data[p+i] = node + end + end + end + end + nldfaces = length(ldface_to_lnodes) + face_to_nodes[d+1] = JaggedArray(data,ptrs) + for ldface in 1:nldfaces + group = ngroups + ldface + group_name = "$(d)-face-$ldface" + faces_in_physical_group = findall(g->g==group,dface_to_physical_group) + groups[d+1][group_name] = faces_in_physical_group + end + ngroups += nldfaces + if d == (D-1) + groups[d+1]["boundary"] = 1:length(dface_to_physical_group) + end + end # d + ngroups += 1 + groups, face_to_nodes + end # barrier + chain = cartesian_chain(domain,cells_per_dir) + interior_mesh = mesh_from_chain(chain) + D = num_dims(interior_mesh) + cell_to_nodes = face_nodes(interior_mesh,D) + reference_cells = reference_faces(interior_mesh,D) + node_coords = node_coordinates(interior_mesh) + ref_cell = first(reference_cells) + refid_to_refface = reference_faces(boundary(ref_cell)) + nnodes = num_nodes(interior_mesh) + d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] + groups, face_to_nodes = barrier( + D, + cell_to_nodes, + nnodes, + d_to_ldface_to_lnodes) + face_to_refid = [ ones(Int8,length(face_to_nodes[d+1])) for d in 0:(D-1)] + mesh_face_nodes = push(face_to_nodes,face_nodes(interior_mesh,D)) + mesh_face_reference_id = push(face_to_refid,face_reference_id(interior_mesh,D)) + mesh_reference_faces = push(refid_to_refface,reference_faces(interior_mesh,D)) + mesh_groups = push(groups,physical_groups(interior_mesh,D)) + fe_mesh( + node_coords, + mesh_face_nodes, + mesh_face_reference_id, + mesh_reference_faces; + physical_groups=mesh_groups, + ) +end + +function cartesian_chain(domain,cells_per_dir) + box = bounding_box_from_domain(domain) + D = length(cells_per_dir) + nodes_per_dir = cells_per_dir .+ 1 + pmin = first(box) + pmax = last(box) + extent_per_dir = pmax .- pmin + h_per_dir = SVector(extent_per_dir ./ cells_per_dir) + nnodes = prod(nodes_per_dir) + ncells = prod(cells_per_dir) + nlnodes = 2^D + cell_nodes_ptrs = fill(Int32(nlnodes),ncells+1) + cell_nodes_ptrs[1] = 0 + length_to_ptrs!(cell_nodes_ptrs) + cell_nodes_data = zeros(Int32,ncells*nlnodes) + cell_nodes = JaggedArray(cell_nodes_data,cell_nodes_ptrs) + cell_cis = CartesianIndices(cells_per_dir) + cell_lis = LinearIndices(cells_per_dir) + node_cis = CartesianIndices(nodes_per_dir) + node_lis = LinearIndices(nodes_per_dir) + lnode_cis = CartesianIndices(ntuple(i->0:1,Val(D))) + for (cell_li,cell_ci) in enumerate(cell_cis) + nodes = cell_nodes[cell_li] + for (lnode_li,lnode_ci) in enumerate(lnode_cis) + node_ci = CartesianIndex(Tuple(cell_ci) .+ Tuple(lnode_ci)) + node_li = node_lis[node_ci] + nodes[lnode_li] = node_li + end + end + node_coords = zeros(SVector{D,Float64},nnodes) + for (node_li,node_ci) in enumerate(node_cis) + anchor = SVector(Tuple(node_ci) .- 1) + x = pmin .+ h_per_dir .* anchor + node_coords[node_li] = x + end + cell_reference_id = fill(Int32(1),ncells) + cell_geometry = unit_n_cube(Val(D)) + order = 1 + ref_cell = lagrangian_fe(cell_geometry,order) + reference_cells = [ref_cell] + interior_cells = collect(Int32,1:length(cell_nodes)) + groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) + chain = fe_chain( + node_coords, + cell_nodes, + cell_reference_id, + reference_cells; + physical_groups=groups, + ) + chain +end + +function structured_simplex_chain(domain,cells_per_dir) + box = bounding_box_from_domain(domain) + D = length(cells_per_dir) + nodes_per_dir = cells_per_dir .+ 1 + pmin = first(box) + pmax = last(box) + extent_per_dir = pmax .- pmin + h_per_dir = SVector(extent_per_dir ./ cells_per_dir) + nnodes = prod(nodes_per_dir) + nlnodes = 2^D + cell_geometry = unit_n_cube(Val(D)) + ref_simplex_mesh = simplexify(cell_geometry) + rscell_to_lnodes = face_nodes(ref_simplex_mesh,D) + nrscells = length(rscell_to_lnodes) + nslnodes = length(first(rscell_to_lnodes)) + ncells = prod(cells_per_dir)*nrscells + cell_nodes_ptrs = fill(Int32(nslnodes),ncells+1) + cell_nodes_ptrs[1] = 0 + length_to_ptrs!(cell_nodes_ptrs) + ndata = cell_nodes_ptrs[end]-1 + cell_nodes_data = zeros(Int32,ndata) + cell_nodes = JaggedArray(cell_nodes_data,cell_nodes_ptrs) + cell_cis = CartesianIndices(cells_per_dir) + cell_lis = LinearIndices(cells_per_dir) + node_cis = CartesianIndices(nodes_per_dir) + node_lis = LinearIndices(nodes_per_dir) + lnode_cis = CartesianIndices(ntuple(i->0:1,Val(D))) + clnodes = zeros(Int,nlnodes) + scell = 0 + for (cell_li,cell_ci) in enumerate(cell_cis) + for (lnode_li,lnode_ci) in enumerate(lnode_cis) + node_ci = CartesianIndex(Tuple(cell_ci) .+ Tuple(lnode_ci)) + node_li = node_lis[node_ci] + clnodes[lnode_li] = node_li + end + for srcell in 1:nrscells + scell += 1 + nodes = cell_nodes[scell] + lnodes = rscell_to_lnodes[srcell] + for (i,lnode) in enumerate(lnodes) + nodes[i] = clnodes[lnode] + end + end + end + node_coords = zeros(SVector{D,Float64},nnodes) + for (node_li,node_ci) in enumerate(node_cis) + anchor = SVector(Tuple(node_ci) .- 1) + x = pmin .+ h_per_dir .* anchor + node_coords[node_li] = x + end + cell_reference_id = fill(Int32(1),ncells) + order = 1 + reference_cells = reference_faces(ref_simplex_mesh,D) + interior_cells = collect(Int32,1:length(cell_nodes)) + groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) + chain = fe_chain( + node_coords, + cell_nodes, + cell_reference_id, + reference_cells; + physical_groups=groups, + ) + chain +end + +function structured_simplex_mesh_with_boundary(domain,cells_per_dir) + function barrier( + D, + cell_to_nodes, + nnodes, + d_to_ldface_to_lnodes, + d_to_ldface_to_sldface_to_lnodes) + + node_to_n = zeros(Int32,nnodes) + for nodes in cell_to_nodes + for node in nodes + node_to_n[node] += Int32(1) + end + end + J = typeof(JaggedArray(Vector{Int32}[])) + face_to_nodes = Vector{J}(undef,D) + groups = [ Dict{String,Vector{Int32}}() for d in 0:(D-1) ] + ngroups = 0 + for d in 0:(D-1) + nmax = 2^d + ldface_to_lnodes = d_to_ldface_to_lnodes[d+1] + ldface_to_sldface_to_lnodes = d_to_ldface_to_sldface_to_lnodes[d+1] + ndfaces = 0 + for nodes in cell_to_nodes + for (ldface,lnodes) in enumerate(ldface_to_lnodes) + isboundary = true + for lnode in lnodes + node = nodes[lnode] + if node_to_n[node] > nmax + isboundary = false + break + end + end + if isboundary + ndfaces += length(ldface_to_sldface_to_lnodes[ldface]) + end + end + end + nslnodes = length(ldface_to_sldface_to_lnodes[begin][begin]) + ptrs = zeros(Int32,ndfaces+1) + for dface in 1:ndfaces + ptrs[dface+1] += Int32(nslnodes) + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = zeros(Int32,ndata) + dface_to_physical_group = zeros(Int32,ndfaces) + ndfaces = 0 + for nodes in cell_to_nodes + for (ldface,lnodes) in enumerate(ldface_to_lnodes) + isboundary = true + for lnode in lnodes + node = nodes[lnode] + if node_to_n[node] > nmax + isboundary = false + break + end + end + if isboundary + group = ngroups + ldface + sldface_to_lnodes = ldface_to_sldface_to_lnodes[ldface] + nsldfaces = length(sldface_to_lnodes) + for sldface in 1:nsldfaces + ndfaces += 1 + dface_to_physical_group[ndfaces] = group + p = ptrs[ndfaces]-Int32(1) + mylnodes = sldface_to_lnodes[sldface] + for (i,lnode) in enumerate(mylnodes) + node = nodes[lnode] + data[p+i] = node + end + end + end + end + end + nldfaces = length(ldface_to_lnodes) + face_to_nodes[d+1] = JaggedArray(data,ptrs) + for ldface in 1:nldfaces + group = ngroups + ldface + group_name = "$(d)-face-$ldface" + faces_in_physical_group = findall(g->g==group,dface_to_physical_group) + groups[d+1][group_name] = faces_in_physical_group + end + ngroups += nldfaces + if d == (D-1) + groups[d+1]["boundary"] = 1:length(dface_to_physical_group) + end + end # d + ngroups += 1 + groups, face_to_nodes + end # barrier + chain = cartesian_chain(domain,cells_per_dir) + interior_mesh = mesh_from_chain(chain) + D = num_dims(interior_mesh) + cell_to_nodes = face_nodes(interior_mesh,D) + reference_cells = reference_faces(interior_mesh,D) + ref_cell = first(reference_cells) + refid_to_refface = reference_faces(boundary(ref_cell)) + nnodes = num_nodes(interior_mesh) + + cell_geometry = unit_n_cube(Val(D)) + ref_simplex_mesh = simplexify(cell_geometry) + d_to_ldface_to_sldface_to_lnodes = [ + [ face_nodes(ref_simplex_mesh,d)[physical_groups(ref_simplex_mesh,d)["$d-face-$ldface"]] + for ldface in 1:num_faces(boundary(ref_cell),d) ] for d in 0:(D-1)] + d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] + groups, face_to_nodes = barrier( + D, + cell_to_nodes, + nnodes, + d_to_ldface_to_lnodes, + d_to_ldface_to_sldface_to_lnodes + ) + simplex_chain = structured_simplex_chain(domain,cells_per_dir) + node_coords = node_coordinates(simplex_chain) + face_to_refid = [ ones(Int8,length(face_to_nodes[d+1])) for d in 0:(D-1)] + mesh_face_nodes = push(face_to_nodes,face_nodes(simplex_chain)) + mesh_face_reference_id = push(face_to_refid,face_reference_id(simplex_chain)) + mesh_reference_faces = reference_faces(ref_simplex_mesh) + mesh_groups = push(groups,physical_groups(simplex_chain)) + fe_chain( + node_coords, + mesh_face_nodes, + mesh_face_reference_id, + mesh_reference_faces; + physical_groups=mesh_groups, + ) +end + end # module diff --git a/test/runtests.jl b/test/runtests.jl index 198308f6..d8321402 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -95,6 +95,29 @@ vtk_grid(joinpath(outdir,"demo"),gt.vtk_args(mesh)...) do vtk gt.vtk_physical_groups!(vtk,mesh) end +@show gt.unit_simplex(0) |> gt.boundary +@show gt.unit_simplex(1) |> gt.boundary +@show gt.unit_simplex(2) |> gt.boundary +@show gt.unit_simplex(3) |> gt.boundary + +@show gt.unit_n_cube(0) |> gt.boundary +@show gt.unit_n_cube(1) |> gt.boundary +@show gt.unit_n_cube(2) |> gt.boundary +@show gt.unit_n_cube(3) |> gt.boundary + +order = 2 +gt.lagrangian_fe(spx1,order) |> gt.boundary |> gt.topology + +mesh = gt.mesh_from_gmsh(msh;complexify=false) + +new_mesh, old_to_new = gt.complexify(mesh) + +mesh = gt.mesh_from_gmsh(msh) + +domain = (0,1,0,1) +cells = (2,2) +mesh = gt.cartesian_mesh(domain,cells) +mesh = gt.cartesian_mesh(domain,cells,simplexify=true) #∂spx0 = gt.boundary(spx0) #∂spx0 = gt.boundary(spx1) From 46e26d672a6235620cd2473881fb5a6d0b866fa5 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Fri, 26 Jan 2024 17:52:25 +0100 Subject: [PATCH 07/34] Cartesian grid working --- src/GalerkinToolkit.jl | 18 +++++++++--------- test/runtests.jl | 2 ++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 9673e634..4c388939 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -2012,7 +2012,7 @@ function fe_chain( face_reference_id, reference_faces; periodic_nodes = eltype(eltype(face_reference_id))[], - physical_groups = map(i->Dict{String,Vector{eltype(eltype(face_reference_id))}}(),face_reference_id), + physical_groups = Dict{String,Vector{eltype(eltype(face_reference_id))}}(), outwards_normals = nothing ) fe_chain( @@ -2057,10 +2057,10 @@ function mesh_from_chain(chain) node_coords, face_to_nodes, face_to_refid, - refid_to_refface, - pnodes, - groups, - onormals) + refid_to_refface; + periodic_nodes = pnodes, + physical_groups = groups, + outwards_normals = onormals) end function simplexify(geo::AbstractFaceGeometry) @@ -2104,7 +2104,7 @@ function simplexify_unit_n_cube(geo) reference_cells, ) mesh = mesh_from_chain(chain) - mesh_complex, = complexify_mesh(mesh) + mesh_complex, = complexify(mesh) groups = [Dict{String,Vector{Int32}}() for d in 0:D] for d in 0:(D-1) sface_to_nodes = face_nodes(mesh_complex,d) @@ -2203,7 +2203,7 @@ function simplexify_reference_face(ref_face) reference_faces = ref_faces_inter, ) mesh = mesh_from_chain(chain) - mesh_complex, = complexify_mesh(mesh) + mesh_complex, = complexify(mesh) pg = physical_groups(mesh_complex) pg .= physical_groups(mesh_geom) mesh_complex @@ -2225,7 +2225,7 @@ function cartesian_mesh(domain,cells_per_dir;boundary=true,complexify=true,simpl mesh_from_chain(chain) end if complexify - mesh, = complexify_mesh(mesh) + mesh, = GalerkinToolkit.complexify(mesh) end mesh end @@ -2596,7 +2596,7 @@ function structured_simplex_mesh_with_boundary(domain,cells_per_dir) mesh_face_reference_id = push(face_to_refid,face_reference_id(simplex_chain)) mesh_reference_faces = reference_faces(ref_simplex_mesh) mesh_groups = push(groups,physical_groups(simplex_chain)) - fe_chain( + fe_mesh( node_coords, mesh_face_nodes, mesh_face_reference_id, diff --git a/test/runtests.jl b/test/runtests.jl index d8321402..dfcddbc0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -117,7 +117,9 @@ mesh = gt.mesh_from_gmsh(msh) domain = (0,1,0,1) cells = (2,2) mesh = gt.cartesian_mesh(domain,cells) +mesh = gt.cartesian_mesh(domain,cells,boundary=false) mesh = gt.cartesian_mesh(domain,cells,simplexify=true) +mesh = gt.cartesian_mesh(domain,cells,boundary=false,simplexify=true) #∂spx0 = gt.boundary(spx0) #∂spx0 = gt.boundary(spx1) From 38ad76a08bf815a504c2e55b97661dae5438d88e Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Fri, 26 Jan 2024 18:06:57 +0100 Subject: [PATCH 08/34] Saving --- src/GalerkinToolkit.jl | 293 +++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 2 + 2 files changed, 295 insertions(+) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 4c388939..343773b2 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -2605,5 +2605,298 @@ function structured_simplex_mesh_with_boundary(domain,cells_per_dir) ) end +function visualization_mesh(mesh::AbstractFEMesh,args...;kwargs...) + visualization_mesh_from_mesh(mesh,args...;kwargs...) +end + +function visualization_mesh_from_mesh(mesh,dim=num_dims(mesh);order=nothing,resolution=nothing) + function barrier( + refid_to_tabulation, + refid_to_scell_to_snodes, + refid_to_scell_to_srefid, + refid_to_srefid_to_oid, + refid_to_srefid_to_vrefface, + refid_to_snode_to_coords, + node_to_coords, + cell_to_nodes, + cell_to_refid, + ::Val{Dn}) where Dn + + ncells = length(cell_to_refid) + nvnodes = 0 + nvcells = 0 + for cell in 1:ncells + refid = cell_to_refid[cell] + nvnodes += length(refid_to_snode_to_coords[refid]) + nvcells += length(refid_to_scell_to_srefid[refid]) + + end + nrefids = length(refid_to_srefid_to_oid) + i_to_oid = reduce(vcat,refid_to_srefid_to_oid) + i_to_vrefface = reduce(vcat,refid_to_srefid_to_vrefface) + refid_to_srefid_to_i = Vector{Vector{Int}}(undef,nrefids) + i = 0 + for refid in 1:nrefids + srefid_to_oid = refid_to_srefid_to_oid[refid] + nsrefids = length(srefid_to_oid) + srefid_to_i = zeros(Int,nsrefids) + for srefid in 1:nsrefids + i += 1 + srefid_to_i[srefid] = i + end + refid_to_srefid_to_i[refid] = srefid_to_i + end + vrefid_to_oid = unique(i_to_oid) + i_to_vrefid = indexin(i_to_oid,vrefid_to_oid) + vrefid_to_i = indexin(vrefid_to_oid,i_to_oid) + vrefid_to_vrefface = i_to_vrefface[vrefid_to_i] + Tx = SVector{Dn,Float64} + vnode_to_coords = zeros(Tx,nvnodes) + vcell_to_vnodes_ptrs = zeros(Int32,nvcells+1) + vcell_to_vrefid = zeros(Int32,nvcells) + vcell_to_cell = zeros(Int32,nvcells) + cell_to_vnodes = fill(0:1,ncells) + vcell = 0 + for cell in 1:ncells + refid = cell_to_refid[cell] + scell_to_snodes = refid_to_scell_to_snodes[refid] + nscells = length(scell_to_snodes) + scell_to_srefid = refid_to_scell_to_srefid[refid] + srefid_to_i = refid_to_srefid_to_i[refid] + for scell in 1:nscells + srefid = scell_to_srefid[scell] + i = srefid_to_i[srefid] + vrefid = i_to_vrefid[i] + snodes = scell_to_snodes[scell] + vcell += 1 + vcell_to_vnodes_ptrs[vcell+1] = length(snodes) + vcell_to_vrefid[vcell] = vrefid + vcell_to_cell[vcell] = cell + end + end + length_to_ptrs!(vcell_to_vnodes_ptrs) + ndata = vcell_to_vnodes_ptrs[end]-1 + vcell_to_vnodes_data = zeros(Int32,ndata) + vcell = 0 + vnode = 0 + vnode_prev = 1 + for cell in 1:ncells + refid = cell_to_refid[cell] + scell_to_snodes = refid_to_scell_to_snodes[refid] + nscells = length(scell_to_snodes) + for scell in 1:nscells + snodes = scell_to_snodes[scell] + vcell += 1 + p = vcell_to_vnodes_ptrs[vcell] + for (i,snode) in enumerate(snodes) + vcell_to_vnodes_data[p-1+i] = snode + vnode + end + end + tabulation = refid_to_tabulation[refid] + nsnodes = size(tabulation,1) + nodes = cell_to_nodes[cell] + for snode in 1:nsnodes + y = zero(Tx) + for (i,node) in enumerate(nodes) + coeff = tabulation[snode,i] + x = node_to_coords[node] + y += coeff*x + end + vnode += 1 + vnode_to_coords[vnode] = y + end + cell_to_vnodes[cell] = vnode_prev:vnode + vnode_prev = vnode + 1 + end + vcell_to_vnodes = JaggedArray(vcell_to_vnodes_data,vcell_to_vnodes_ptrs) + vchain = fe_chain( + vnode_to_coords, + vcell_to_vnodes, + vcell_to_vrefid, + vrefid_to_vrefface) + vmesh = mesh_from_chain(vchain) + vglue = (;parent_face=vcell_to_cell, + reference_coordinates=refid_to_snode_to_coords, + face_fine_nodes = cell_to_vnodes, + num_dims=Val(dim)) + vmesh, vglue + end # barrier + refid_to_refface = reference_faces(mesh,dim) + refid_to_refmesh = map(refid_to_refface) do ref_face + if order === nothing && resolution === nothing + # Use the given cells as visualization cells + mesh_from_reference_face(ref_face) + elseif order !== nothing && resolution === nothing + # Use cells of given order as visualization cells + geo = geometry(ref_face) + ref_face_ho = lagrangian_reference_face(geo;order) + mesh_from_reference_face(ref_face_ho) + elseif order === nothing && resolution !== nothing + # Use linear sub-cells with $resolution per direction per direction + geom = geometry(ref_face) + refine_reference_geometry(geom,resolution) + else + error("order and resolution kw-arguments can not be given at the same time") + end + end + refid_to_tabulation = map(refid_to_refface,refid_to_refmesh) do refface,refmesh + x = node_coordinates(refmesh) + tabulator(refface)(value,x) + end + refid_to_scell_to_snodes = map(refmesh->face_nodes(refmesh,dim),refid_to_refmesh) + refid_to_scell_to_srefid = map(refmesh->face_reference_id(refmesh,dim),refid_to_refmesh) + refid_to_srefid_to_oid = map(refmesh->map(objectid,reference_faces(refmesh,dim)),refid_to_refmesh) + refid_to_srefid_to_vrefface = map(refmesh->reference_faces(refmesh,dim),refid_to_refmesh) + refid_to_snode_to_coords = map(node_coordinates,refid_to_refmesh) + node_to_coords = node_coordinates(mesh) + cell_to_nodes = face_nodes(mesh,dim) + cell_to_refid = face_reference_id(mesh,dim) + Dn = num_ambient_dims(mesh) + barrier( + refid_to_tabulation, + refid_to_scell_to_snodes, + refid_to_scell_to_srefid, + refid_to_srefid_to_oid, + refid_to_srefid_to_vrefface, + refid_to_snode_to_coords, + node_to_coords, + cell_to_nodes, + cell_to_refid, + Val(Dn)) +end + +function refine_reference_geometry(geo,resolution) + function refine_n_cube_aligned(geo,n) + box = bounding_box(geo) + domain = domain_from_bounding_box(box) + D = num_dims(geo) + cells = ntuple(i->n,Val(D)) + cartesian_mesh(domain,cells) + end + function refine_unit_triangle(geo,n) + # Copyed + adapted from Gridap + tri_num(n) = n*(n+1)÷2 + v(n,i,j) = tri_num(n) - tri_num(n-i+1) + j + D = 2 + quad_to_tris = ((1,2,3),(2,4,3)) + quad = CartesianIndices( (0:1,0:1) ) + Tp = SVector{2,Float64} + n_verts = tri_num(n+1) + n_cells = tri_num(n)+tri_num(n-1) + n_verts_x_cell = 3 + X = zeros(Tp,n_verts) + T = [ zeros(Int,n_verts_x_cell) for i in 1:n_cells ] + for i in 1:n+1 + for j in 1:n+1-i+1 + vert = v(n+1,i,j) + X[vert] = SVector((i-1)/n,(j-1)/n) + end + end + for i in 1:n + for j in 1:n-(i-1) + verts = ntuple( lv-> v(n+1, (i,j).+quad[lv].I ...), Val{2^D}() ) + cell = v(n,i,j) + T[cell] .= map(i->verts[i],quad_to_tris[1]) + if (i-1)+(j-1) < n-1 + cell = tri_num(n) + v(n-1,i,j) + T[cell] .= map(i->verts[i],quad_to_tris[2]) + end + end + end + refface = lagrangian_reference_face(geo) + chain = Chain(; + num_dims=Val(2), + node_coordinates = X, + face_nodes = T, + face_reference_id = fill(1,length(T)), + reference_faces = [refface] + ) + mesh_from_chain(chain) + end + function refine_unit_tet(geo,n) + # Copyed + adapted from Gridap + tri_num(n) = n*(n+1)÷2 + tet_num(n) = n*(n+1)*(n+2)÷6 + v(n,i,j) = tri_num(n) - tri_num(n-i+1) + j + v(n,i,j,k) = tet_num(n) - tet_num(n-i+1) + v(n-i+1,j,k) + D = 3 + cube_to_tets = ((1,2,3,5),(2,4,3,6),(3,5,7,6),(2,3,5,6),(3,4,7,6),(4,6,7,8)) + cube = CartesianIndices( (0:1,0:1,0:1) ) + n_core_tets = length(cube_to_tets)-2 + Tp = SVector{3,Float64} + n_verts = tet_num(n+1) + n_cells = tet_num(n)+n_core_tets*tet_num(n-1)+tet_num(n-2) + n_verts_x_cell = 4 + X = zeros(Tp,n_verts) + T = [ zeros(Int,n_verts_x_cell) for i in 1:n_cells ] + for i in 1:n+1 + for j in 1:n+1-(i-1) + for k in 1:n+1-(i-1)-(j-1) + vert = v(n+1,i,j,k) + X[vert] = SVector((i-1)/n,(j-1)/n,(k-1)/n) + end + end + end + for i in 1:n + for j in 1:n-(i-1) + for k in 1:n-(i-1)-(j-1) + verts = ntuple( lv-> v(n+1, (i,j,k).+cube[lv].I ...), Val{2^D}() ) + cell = v(n,i,j,k) + T[cell] .= map(i->verts[i],cube_to_tets[1]) + if (i-1)+(j-1)+(k-1) < n-1 + cell = tet_num(n) + (v(n-1,i,j,k)-1)*n_core_tets + for t in 1:n_core_tets + T[cell+t] .= map(i->verts[i],cube_to_tets[t+1]) + end + end + if (i-1)+(j-1)+(k-1) < n-2 + cell = tet_num(n) + n_core_tets*tet_num(n-1) + v(n-2,i,j,k) + T[cell] .= map(i->verts[i],cube_to_tets[end]) + end + end + end + end + refface = lagrangian_fe(geo,1) + chain = fe_chain( + X, + T, + fill(1,length(T)), + [refface], + ) + mesh_from_chain(chain) + end + if is_n_cube(geo) && is_axis_aligned(geo) + refine_n_cube_aligned(geo,resolution) + elseif is_unit_simplex(geo) && num_dims(geo) == 2 + refine_unit_triangle(geo,resolution) + elseif is_unit_simplex(geo) && num_dims(geo) == 3 + refine_unit_tet(geo,resolution) + else + error("Case not implemented (yet)") + end +end + +function mesh_from_reference_face(ref_face) + boundary_mesh = boundary(ref_face) + D = num_dims(geometry(ref_face)) + nnodes = num_nodes(ref_face) + face_to_nodes = push(face_nodes(boundary_mesh),[collect(1:nnodes)]) + face_to_refid = push(face_reference_id(boundary_mesh),[1]) + refid_refface = push(reference_faces(boundary_mesh),[ref_face]) + node_to_coords = node_coordinates(ref_face) + groups = [ Dict{String,Vector{Int32}}() for d in 0:D] + for d in 0:D + for face in 1:length(face_to_refid[d+1]) + groups[d+1]["$d-face-$face"] = [face] + end + end + groups[end-1]["boundary"] = 1:length(face_to_refid[D-1+1]) + groups[end]["interior"] = [1] + mesh = fe_mesh( + node_to_coords, + face_to_nodes, + face_to_refid, + refid_refface) +end end # module diff --git a/test/runtests.jl b/test/runtests.jl index dfcddbc0..2ca09d03 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -121,6 +121,8 @@ mesh = gt.cartesian_mesh(domain,cells,boundary=false) mesh = gt.cartesian_mesh(domain,cells,simplexify=true) mesh = gt.cartesian_mesh(domain,cells,boundary=false,simplexify=true) +vmesh, vglue = gt.visualization_mesh(mesh) + #∂spx0 = gt.boundary(spx0) #∂spx0 = gt.boundary(spx1) #∂cube0 = gt.boundary(cube0) From 5413491586a953d333dd5e37a32e754a013c599f Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 10:44:06 +0100 Subject: [PATCH 09/34] added physical_nodes --- src/GalerkinToolkit.jl | 79 ++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 7 ++++ 2 files changed, 86 insertions(+) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 343773b2..1f029057 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -1990,6 +1990,84 @@ function find_node_to_vertex(mesh) node_to_vertex, vertex end +function physical_nodes(mesh,d) + nnodes = num_nodes(mesh) + node_to_touched = fill(false,nnodes) + node_groups = Dict{String,Vector{Int32}}() + face_to_nodes = face_nodes(mesh,d) + for (name,faces) in physical_groups(mesh,d) + fill!(node_to_touched,false) + for face in faces + nodes = face_to_nodes[face] + node_to_touched[nodes] .= true + end + node_groups[name] = findall(node_to_touched) + end + node_groups +end + +function physical_nodes(mesh;merge_dims=Val(true),disjoint=Val(false)) + D = num_dims(mesh) + d_to_groups = [ physical_nodes(mesh,d) for d in 0:D ] + if val_parameter(merge_dims) == false && val_parameter(disjoint) == false + return d_to_groups + end + names = Set{String}() + for groups in d_to_groups + for name in keys(groups) + push!(names,name) + end + end + if val_parameter(disjoint) == false + nnodes = num_nodes(mesh) + node_to_touched = fill(false,nnodes) + node_groups = Dict{String,Vector{Int32}}() + for name in names + fill!(node_to_touched,false) + for groups in d_to_groups + for (name2,nodes) in groups + if name != name2 + continue + end + node_to_touched[nodes] .= true + end + end + node_groups[name] = findall(node_to_touched) + end + return node_groups + else + tag_to_name = sort(collect(names)) + nnodes = num_nodes(mesh) + node_to_tag = zeros(Int32,nnodes) + classify_mesh_nodes!(node_to_tag,mesh,tag_to_name) + disjoint_groups = Dict{String,Vector{Int32}}() + for (tag,name) in enumerate(tag_to_name) + disjoint_groups[name] = findall(tag2->tag2==tag,node_to_tag) + end + return disjoint_groups + end +end + +function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) + fill!(node_to_tag,zero(eltype(node_to_tag))) + for d in dmax:-1:0 + face_to_nodes = face_nodes(mesh,d) + face_groups = physical_groups(mesh,d) + for (tag,name) in enumerate(tag_to_name) + for (name2,faces) in face_groups + if name != name2 + continue + end + for face in faces + nodes = face_to_nodes[face] + node_to_tag[nodes] .= tag + end + end + end + end + node_to_tag +end + abstract type AbstractFEChain <: GalerkinToolkitDataType end struct GenericFEChain{A,B,C,D,E,F,G} <: AbstractFEChain @@ -2899,4 +2977,5 @@ function mesh_from_reference_face(ref_face) refid_refface) end + end # module diff --git a/test/runtests.jl b/test/runtests.jl index 2ca09d03..97fc4b1d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -121,6 +121,13 @@ mesh = gt.cartesian_mesh(domain,cells,boundary=false) mesh = gt.cartesian_mesh(domain,cells,simplexify=true) mesh = gt.cartesian_mesh(domain,cells,boundary=false,simplexify=true) +mesh = gt.mesh_from_gmsh(msh) +node_groups = gt.physical_nodes(mesh) +node_groups = gt.physical_nodes(mesh;disjoint=true) +node_groups = gt.physical_nodes(mesh;merge_dims=true) +node_groups = gt.physical_nodes(mesh;merge_dims=true,disjoint=false) +node_groups = gt.physical_nodes(mesh;merge_dims=true,disjoint=true) + vmesh, vglue = gt.visualization_mesh(mesh) #∂spx0 = gt.boundary(spx0) From a89e88bafa90a674a8a99d7c439b087c5cfad203 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 10:50:28 +0100 Subject: [PATCH 10/34] Renamed physical_groups -> physical_faces --- src/GalerkinToolkit.jl | 68 +++++++++++++++++++++--------------------- test/runtests.jl | 3 +- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 1f029057..9eaea0b9 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -33,8 +33,8 @@ reference_faces(a) = a.reference_faces face_nodes(a) = a.face_nodes face_incidence(a) = a.face_incidence face_reference_id(a) = a.face_reference_id -physical_groups(a) = a.physical_groups -has_physical_groups(a) = hasproperty(a,:physical_groups) && a.physical_groups !== nothing +physical_faces(a) = a.physical_faces +has_physical_faces(a) = hasproperty(a,:physical_faces) && a.physical_faces !== nothing periodic_nodes(a) = a.periodic_nodes has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing geometry(a) = a.geometry @@ -70,7 +70,7 @@ face_incidence(a,d1,d2) = face_incidence(a)[val_parameter(d1)+1,val_parameter(d2 face_reference_id(a,d) = face_reference_id(a)[val_parameter(d)+1] num_faces(a) = map(length,face_reference_id(a)) num_faces(a,d) = length(face_reference_id(a,d)) -physical_groups(a,d) = physical_groups(a)[val_parameter(d)+1] +physical_faces(a,d) = physical_faces(a)[val_parameter(d)+1] num_nodes(a) = length(node_coordinates(a)) num_ambient_dims(a) = length(eltype(node_coordinates(a))) function face_offsets(a) @@ -469,7 +469,7 @@ struct GenericFEMesh{A,B,C,D,E,F,G} <: AbstractFEMesh face_reference_id::C reference_faces::D periodic_nodes::E - physical_groups::F + physical_faces::F outwards_normals::G end @@ -483,7 +483,7 @@ function fe_mesh( face_reference_id, reference_faces; periodic_nodes = eltype(eltype(face_reference_id))[], - physical_groups = map(i->Dict{String,Vector{eltype(eltype(face_reference_id))}}(),face_reference_id), + physical_faces = map(i->Dict{String,Vector{eltype(eltype(face_reference_id))}}(),face_reference_id), outwards_normals = nothing ) fe_mesh( @@ -492,7 +492,7 @@ function fe_mesh( face_reference_id, reference_faces, periodic_nodes, - physical_groups, + physical_faces, outwards_normals) end @@ -708,7 +708,7 @@ function mesh_from_gmsh_module(;complexify=true) my_face_nodes, my_face_reference_id, my_reference_faces; - physical_groups = my_groups, + physical_faces = my_groups, periodic_nodes,) if complexify @@ -814,9 +814,9 @@ function vtk_args(mesh) points, cells end -function vtk_physical_groups!(vtk,mesh,d;physical_groups=physical_groups(mesh,d)) +function vtk_physical_faces!(vtk,mesh,d;physical_faces=physical_faces(mesh,d)) ndfaces = num_faces(mesh,d) - for group in physical_groups + for group in physical_faces name,faces = group face_mask = zeros(Int,ndfaces) face_mask[faces] .= 1 @@ -825,13 +825,13 @@ function vtk_physical_groups!(vtk,mesh,d;physical_groups=physical_groups(mesh,d) vtk end -function vtk_physical_groups!(vtk,mesh;physical_groups=physical_groups(mesh)) +function vtk_physical_faces!(vtk,mesh;physical_faces=physical_faces(mesh)) nfaces = sum(num_faces(mesh)) offsets = face_offsets(mesh) D = num_dims(mesh) data = Dict{String,Vector{Int}}() for d in 0:D - for group in physical_groups[d+1] + for group in physical_faces[d+1] name, = group if !haskey(data,name) face_mask = zeros(Int,nfaces) @@ -840,7 +840,7 @@ function vtk_physical_groups!(vtk,mesh;physical_groups=physical_groups(mesh)) end end for d in 0:D - for group in physical_groups[d+1] + for group in physical_faces[d+1] offset = offsets[d+1] name,faces = group face_mask = data[name] @@ -1644,14 +1644,14 @@ function complexify_mesh(mesh) old_to_new[d+1] = old_dface_to_new_dface end node_to_coords = node_coordinates(mesh) - old_physical_groups = physical_groups(mesh) - new_physical_groups = [ Dict{String,Vector{Int32}}() for d in 0:D] # TODO hardcoded + old_physical_faces = physical_faces(mesh) + new_physical_faces = [ Dict{String,Vector{Int32}}() for d in 0:D] # TODO hardcoded for d in 0:D - old_groups = old_physical_groups[d+1] + old_groups = old_physical_faces[d+1] for (group_name,old_group_faces) in old_groups new_group_faces = similar(old_group_faces) new_group_faces .= old_to_new[d+1][old_group_faces] - new_physical_groups[d+1][group_name] = new_group_faces + new_physical_faces[d+1][group_name] = new_group_faces end end new_mesh = fe_mesh( @@ -1659,7 +1659,7 @@ function complexify_mesh(mesh) newface_nodes, newface_refid, Tuple(newreffaces); - physical_groups = new_physical_groups, + physical_faces = new_physical_faces, periodic_nodes = periodic_nodes(mesh), outwards_normals = outwards_normals(mesh) ) @@ -1995,7 +1995,7 @@ function physical_nodes(mesh,d) node_to_touched = fill(false,nnodes) node_groups = Dict{String,Vector{Int32}}() face_to_nodes = face_nodes(mesh,d) - for (name,faces) in physical_groups(mesh,d) + for (name,faces) in physical_faces(mesh,d) fill!(node_to_touched,false) for face in faces nodes = face_to_nodes[face] @@ -2052,7 +2052,7 @@ function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) fill!(node_to_tag,zero(eltype(node_to_tag))) for d in dmax:-1:0 face_to_nodes = face_nodes(mesh,d) - face_groups = physical_groups(mesh,d) + face_groups = physical_faces(mesh,d) for (tag,name) in enumerate(tag_to_name) for (name2,faces) in face_groups if name != name2 @@ -2076,7 +2076,7 @@ struct GenericFEChain{A,B,C,D,E,F,G} <: AbstractFEChain face_reference_id::C reference_faces::D periodic_nodes::E - physical_groups::F + physical_faces::F outwards_normals::G end @@ -2090,7 +2090,7 @@ function fe_chain( face_reference_id, reference_faces; periodic_nodes = eltype(eltype(face_reference_id))[], - physical_groups = Dict{String,Vector{eltype(eltype(face_reference_id))}}(), + physical_faces = Dict{String,Vector{eltype(eltype(face_reference_id))}}(), outwards_normals = nothing ) fe_chain( @@ -2099,7 +2099,7 @@ function fe_chain( face_reference_id, reference_faces, periodic_nodes, - physical_groups, + physical_faces, outwards_normals) end @@ -2126,7 +2126,7 @@ function mesh_from_chain(chain) ref_cell = first(reference_cells) ref_faces = reference_faces(boundary(ref_cell)) refid_to_refface = push(ref_faces,reference_cells) - cell_groups = physical_groups(chain) + cell_groups = physical_faces(chain) groups = [ typeof(cell_groups)() for d in 0:D] groups[end] = cell_groups pnodes = periodic_nodes(chain) @@ -2137,7 +2137,7 @@ function mesh_from_chain(chain) face_to_refid, refid_to_refface; periodic_nodes = pnodes, - physical_groups = groups, + physical_faces = groups, outwards_normals = onormals) end @@ -2210,7 +2210,7 @@ function simplexify_unit_n_cube(geo) sface_is_boundary[sfaces] .= true end groups[end-1]["boundary"] = findall(sface_is_boundary) - physical_groups(mesh_complex) .= groups + physical_faces(mesh_complex) .= groups mesh_complex end @@ -2282,8 +2282,8 @@ function simplexify_reference_face(ref_face) ) mesh = mesh_from_chain(chain) mesh_complex, = complexify(mesh) - pg = physical_groups(mesh_complex) - pg .= physical_groups(mesh_geom) + pg = physical_faces(mesh_complex) + pg .= physical_faces(mesh_geom) mesh_complex end @@ -2427,13 +2427,13 @@ function cartesian_mesh_with_boundary(domain,cells_per_dir) mesh_face_nodes = push(face_to_nodes,face_nodes(interior_mesh,D)) mesh_face_reference_id = push(face_to_refid,face_reference_id(interior_mesh,D)) mesh_reference_faces = push(refid_to_refface,reference_faces(interior_mesh,D)) - mesh_groups = push(groups,physical_groups(interior_mesh,D)) + mesh_groups = push(groups,physical_faces(interior_mesh,D)) fe_mesh( node_coords, mesh_face_nodes, mesh_face_reference_id, mesh_reference_faces; - physical_groups=mesh_groups, + physical_faces=mesh_groups, ) end @@ -2484,7 +2484,7 @@ function cartesian_chain(domain,cells_per_dir) cell_nodes, cell_reference_id, reference_cells; - physical_groups=groups, + physical_faces=groups, ) chain end @@ -2549,7 +2549,7 @@ function structured_simplex_chain(domain,cells_per_dir) cell_nodes, cell_reference_id, reference_cells; - physical_groups=groups, + physical_faces=groups, ) chain end @@ -2657,7 +2657,7 @@ function structured_simplex_mesh_with_boundary(domain,cells_per_dir) cell_geometry = unit_n_cube(Val(D)) ref_simplex_mesh = simplexify(cell_geometry) d_to_ldface_to_sldface_to_lnodes = [ - [ face_nodes(ref_simplex_mesh,d)[physical_groups(ref_simplex_mesh,d)["$d-face-$ldface"]] + [ face_nodes(ref_simplex_mesh,d)[physical_faces(ref_simplex_mesh,d)["$d-face-$ldface"]] for ldface in 1:num_faces(boundary(ref_cell),d) ] for d in 0:(D-1)] d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] groups, face_to_nodes = barrier( @@ -2673,13 +2673,13 @@ function structured_simplex_mesh_with_boundary(domain,cells_per_dir) mesh_face_nodes = push(face_to_nodes,face_nodes(simplex_chain)) mesh_face_reference_id = push(face_to_refid,face_reference_id(simplex_chain)) mesh_reference_faces = reference_faces(ref_simplex_mesh) - mesh_groups = push(groups,physical_groups(simplex_chain)) + mesh_groups = push(groups,physical_faces(simplex_chain)) fe_mesh( node_coords, mesh_face_nodes, mesh_face_reference_id, mesh_reference_faces; - physical_groups=mesh_groups, + physical_faces=mesh_groups, ) end diff --git a/test/runtests.jl b/test/runtests.jl index 97fc4b1d..a760ffd5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -92,7 +92,7 @@ outdir = mkpath(joinpath(@__DIR__,"..","output")) msh = joinpath(@__DIR__,"..","assets","demo.msh") mesh = gt.mesh_from_gmsh(msh;complexify=false) vtk_grid(joinpath(outdir,"demo"),gt.vtk_args(mesh)...) do vtk - gt.vtk_physical_groups!(vtk,mesh) + gt.vtk_physical_faces!(vtk,mesh) end @show gt.unit_simplex(0) |> gt.boundary @@ -122,6 +122,7 @@ mesh = gt.cartesian_mesh(domain,cells,simplexify=true) mesh = gt.cartesian_mesh(domain,cells,boundary=false,simplexify=true) mesh = gt.mesh_from_gmsh(msh) +face_groups = gt.physical_faces(mesh) node_groups = gt.physical_nodes(mesh) node_groups = gt.physical_nodes(mesh;disjoint=true) node_groups = gt.physical_nodes(mesh;merge_dims=true) From d6ff3be7642b1f6c31f876bf19fa279f25c2b62a Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 11:47:16 +0100 Subject: [PATCH 11/34] More work on physical_nodes --- src/GalerkinToolkit.jl | 50 ++++++++++++++++++++++++++++-------------- test/runtests.jl | 6 ++--- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 9eaea0b9..bc82f641 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -2006,22 +2006,25 @@ function physical_nodes(mesh,d) node_groups end -function physical_nodes(mesh;merge_dims=Val(true),disjoint=Val(false)) +function physical_nodes(mesh; + merge_dims=Val(false), + disjoint=Val(false), + name_priority=nothing) + + if val_parameter(disjoint) == true && val_parameter(merge_dims) == false + error("disjoint=true requires merge_dims=true") + end D = num_dims(mesh) d_to_groups = [ physical_nodes(mesh,d) for d in 0:D ] - if val_parameter(merge_dims) == false && val_parameter(disjoint) == false + if val_parameter(merge_dims) == false return d_to_groups end - names = Set{String}() - for groups in d_to_groups - for name in keys(groups) - push!(names,name) - end - end + names = physical_names(mesh;merge_dims) + nnodes = num_nodes(mesh) + node_groups = Dict{String,Vector{Int32}}() + if val_parameter(disjoint) == false - nnodes = num_nodes(mesh) node_to_touched = fill(false,nnodes) - node_groups = Dict{String,Vector{Int32}}() for name in names fill!(node_to_touched,false) for groups in d_to_groups @@ -2034,18 +2037,19 @@ function physical_nodes(mesh;merge_dims=Val(true),disjoint=Val(false)) end node_groups[name] = findall(node_to_touched) end - return node_groups else - tag_to_name = sort(collect(names)) - nnodes = num_nodes(mesh) + if name_priority === nothing + tag_to_name = sort(collect(names)) + else + tag_to_name = name_priority + end node_to_tag = zeros(Int32,nnodes) classify_mesh_nodes!(node_to_tag,mesh,tag_to_name) - disjoint_groups = Dict{String,Vector{Int32}}() for (tag,name) in enumerate(tag_to_name) - disjoint_groups[name] = findall(tag2->tag2==tag,node_to_tag) + node_groups[name] = findall(t->t==tag,node_to_tag) end - return disjoint_groups end + node_groups end function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) @@ -2068,6 +2072,20 @@ function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) node_to_tag end +function physical_names(mesh,d) + groups = physical_faces(mesh,d) + Set(keys(groups)) +end + +function physical_names(mesh;merge_dims=Val(false)) + D = num_dims(mesh) + d_to_names = [ physical_names(mesh,d) for d in 0:D] + if val_parameter(merge_dims) == false + return d_to_names + end + reduce(union,d_to_names) +end + abstract type AbstractFEChain <: GalerkinToolkitDataType end struct GenericFEChain{A,B,C,D,E,F,G} <: AbstractFEChain diff --git a/test/runtests.jl b/test/runtests.jl index a760ffd5..bb4aa396 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -123,10 +123,10 @@ mesh = gt.cartesian_mesh(domain,cells,boundary=false,simplexify=true) mesh = gt.mesh_from_gmsh(msh) face_groups = gt.physical_faces(mesh) -node_groups = gt.physical_nodes(mesh) -node_groups = gt.physical_nodes(mesh;disjoint=true) +group_names = gt.physical_names(mesh,2) +group_names = gt.physical_names(mesh) +group_names = gt.physical_names(mesh;merge_dims=true) node_groups = gt.physical_nodes(mesh;merge_dims=true) -node_groups = gt.physical_nodes(mesh;merge_dims=true,disjoint=false) node_groups = gt.physical_nodes(mesh;merge_dims=true,disjoint=true) vmesh, vglue = gt.visualization_mesh(mesh) From 69f7be304fba9db6998f754c87661069da016ebb Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 14:17:12 +0100 Subject: [PATCH 12/34] Adding poisson example --- src/GalerkinToolkit.jl | 31 ++ .../iso_param_poisson_tests.jl | 466 ++++-------------- test/mesh_interface_tests.jl | 140 ++++++ test/runtests.jl | 140 ------ 4 files changed, 254 insertions(+), 523 deletions(-) rename examples/poisson.jl => test/iso_param_poisson_tests.jl (52%) create mode 100644 test/mesh_interface_tests.jl delete mode 100644 test/runtests.jl diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index bc82f641..c15564a2 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -2995,5 +2995,36 @@ function mesh_from_reference_face(ref_face) refid_refface) end +partition_from_mask(a) = partition_from_mask(identity,a) + +function partition_from_mask(f,node_to_mask) + T = Vector{Int32} + free_nodes = convert(T,findall(f,node_to_mask)) + dirichlet_nodes = convert(T,findall(i->!f(i),node_to_mask)) + nfree = length(free_nodes) + ndiri = length(dirichlet_nodes) + permutation = T(undef,nfree+ndiri) + permutation[free_nodes] = 1:nfree + permutation[dirichlet_nodes] = (1:ndiri) .+ nfree + TwoWayPartition(free_nodes,dirichlet_nodes,permutation) +end + +struct TwoWayPartition{A} <: AbstractVector{A} + first::A + last::A + permutation::A +end + +permutation(a::TwoWayPartition) = a.permutation +Base.size(a::TwoWayPartition) = (2,) +Base.IndexStyle(::Type{<:TwoWayPartition}) = IndexLinear() +function Base.getindex(a::TwoWayPartition,i::Int) + @boundscheck @assert i in (1,2) + if i == 1 + a.first + else + a.last + end +end end # module diff --git a/examples/poisson.jl b/test/iso_param_poisson_tests.jl similarity index 52% rename from examples/poisson.jl rename to test/iso_param_poisson_tests.jl index d24404e6..4f1dbae6 100644 --- a/examples/poisson.jl +++ b/test/iso_param_poisson_tests.jl @@ -1,12 +1,15 @@ -module Poisson +module IsoParamPoissonTest -import GalerkinToolkit as glk +import GalerkinToolkit as gk import ForwardDiff using StaticArrays using LinearAlgebra using SparseArrays using WriteVTK +# This one implements a vanilla iso-parametric Poisson solver by only +# using the mesh interface. + function main(params_in) # Process params @@ -45,27 +48,35 @@ end function default_params_poisson() outdir = mkpath(joinpath(@__DIR__,"..","output")) params = Dict{Symbol,Any}() - params[:mesh] = glk.cartesian_mesh((0,1,0,1),(10,10)) + params[:mesh] = gk.cartesian_mesh((0,1,0,1),(10,10)) params[:u] = (x) -> sum(x) params[:f] = (x) -> 0.0 params[:g] = (x) -> 0.0 params[:dirichlet_tags] = ["boundary"] params[:neumann_tags] = String[] params[:integration_degree] = 2 - params[:solve] = \ + params[:solver] = lu_solver() params[:example_path] = joinpath(outdir,"poisson") params end +function lu_solver() + setup(x,A,b) = lu(A) + setup! = lu! + solve! = ldiv! + finalize!(S) = nothing + (;setup,setup!,solve!,finalize!) +end + function setup_dirichlet_bcs(params) mesh = params[:mesh] u = params[:u] dirichlet_tags = params[:dirichlet_tags] - node_to_tag = zeros(glk.num_nodes(mesh)) + node_to_tag = zeros(gk.num_nodes(mesh)) tag_to_name = dirichlet_tags - glk.classify_mesh_nodes!(node_to_tag,mesh,tag_to_name) - free_and_dirichlet_nodes = glk.partition_from_mask(i->i==0,node_to_tag) - node_to_x = glk.node_coordinates(mesh) + gk.classify_mesh_nodes!(node_to_tag,mesh,tag_to_name) + free_and_dirichlet_nodes = gk.partition_from_mask(i->i==0,node_to_tag) + node_to_x = gk.node_coordinates(mesh) dirichlet_nodes = last(free_and_dirichlet_nodes) x_dirichlet = view(node_to_x,dirichlet_nodes) u_dirichlet = u.(x_dirichlet) @@ -75,7 +86,7 @@ end function setup_integration(params,objects) mesh = params[:mesh] degree = params[:integration_degree] - D = glk.num_dims(mesh) + D = gk.num_dims(mesh) if objects === :cells d = D elseif objects === :faces @@ -84,13 +95,13 @@ function setup_integration(params,objects) error("") end # Integration - ref_cells = glk.reference_faces(mesh,d) - face_to_rid = glk.face_reference_id(mesh,d) + ref_cells = gk.reference_faces(mesh,d) + face_to_rid = gk.face_reference_id(mesh,d) integration_rules = map(ref_cells) do ref_cell - glk.quadrature(glk.geometry(ref_cell),degree) + gk.default_quadrature(gk.geometry(ref_cell),degree) end - rid_to_weights = map(glk.weights,integration_rules) - rid_to_coords = map(glk.coordinates,integration_rules) + rid_to_weights = map(gk.weights,integration_rules) + rid_to_coords = map(gk.coordinates,integration_rules) integration = (;rid_to_weights,rid_to_coords,face_to_rid,d) end @@ -99,17 +110,16 @@ function setup_isomap(params,integration) face_to_rid = integration.face_to_rid d = integration.d mesh = params[:mesh] - ref_cells = glk.reference_faces(mesh,d) + ref_cells = gk.reference_faces(mesh,d) shape_funs = map(rid_to_coords,ref_cells) do q,ref_cell - shape_functions = glk.shape_functions(ref_cell) - shape_vals = glk.tabulation_matrix(shape_functions)(glk.value,q) - shape_grads = glk.tabulation_matrix(shape_functions)(ForwardDiff.gradient,q) + shape_vals = gk.tabulator(ref_cell)(gk.value,q) + shape_grads = gk.tabulator(ref_cell)(ForwardDiff.gradient,q) shape_vals, shape_grads end rid_to_shape_vals = map(first,shape_funs) rid_to_shape_grads = map(last,shape_funs) - face_to_nodes = glk.face_nodes(mesh,d) - node_to_coords = glk.node_coordinates(mesh) + face_to_nodes = gk.face_nodes(mesh,d) + node_to_coords = gk.node_coordinates(mesh) isomap = (;face_to_nodes,node_to_coords,face_to_rid,rid_to_shape_vals,rid_to_shape_grads,d) end @@ -118,8 +128,8 @@ function setup_neumann_bcs(params) neumann_tags = params[:neumann_tags] neum_face_to_face = Int[] if length(neumann_tags) != 0 - D = glk.num_dims(mesh) - tag_to_groups = glk.physical_groups(mesh,D-1) + D = gk.num_dims(mesh) + tag_to_groups = gk.physical_faces(mesh,D-1) neum_face_to_face = reduce(union,map(tag->tag_to_groups[tag],neumann_tags)) end (;neum_face_to_face) @@ -140,8 +150,8 @@ function setup(params) cell_isomap = setup_isomap(params,cell_integration) face_isomap = setup_isomap(params,face_integration) user_funs = setup_user_funs(params) - solve = params[:solve] - state = (;solve,dirichlet_bcs,neumann_bcs,cell_integration,cell_isomap,face_integration,face_isomap,user_funs) + solver = params[:solver] + state = (;solver,dirichlet_bcs,neumann_bcs,cell_integration,cell_isomap,face_integration,face_isomap,user_funs) end function assemble_system(state) @@ -160,7 +170,7 @@ function assemble_system(state) # Count coo entries n_coo = 0 ncells = length(cell_to_nodes) - node_to_free_node = glk.permutation(free_and_dirichlet_nodes) + node_to_free_node = gk.permutation(free_and_dirichlet_nodes) nfree = length(first(free_and_dirichlet_nodes)) for cell in 1:ncells nodes = cell_to_nodes[cell] @@ -300,7 +310,7 @@ function add_neumann_bcs!(b,state) fes = map(i->zeros(size(i,2)),s_f) Tx = SVector{d,Float64} Ts = typeof(zero(SVector{d-1,Float64})*zero(Tx)') - node_to_free_node = glk.permutation(free_and_dirichlet_nodes) + node_to_free_node = gk.permutation(free_and_dirichlet_nodes) nfree = length(first(free_and_dirichlet_nodes)) for face in neum_face_to_face @@ -344,13 +354,15 @@ function add_neumann_bcs!(b,state) end function solve_system(A,b,state) - solve = state.solve + solver = state.solver free_and_dirichlet_nodes = state.dirichlet_bcs.free_and_dirichlet_nodes u_dirichlet = state.dirichlet_bcs.u_dirichlet node_to_x = state.cell_isomap.node_to_coords - + u_free = similar(b,axes(A,2)) + setup = solver.setup(u_free,A,b) + solver.solve!(u_free,setup,b) + solver.finalize!(setup) nnodes = length(node_to_x) - u_free = solve(A,b) uh = zeros(nnodes) uh[first(free_and_dirichlet_nodes)] = u_free uh[last(free_and_dirichlet_nodes)] = u_dirichlet @@ -447,365 +459,53 @@ function export_results(uh,params,state) mesh = params[:mesh] example_path = params[:example_path] node_to_tag = state.dirichlet_bcs.node_to_tag - vtk_grid(example_path,glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) + vtk_grid(example_path,gk.vtk_args(mesh)...) do vtk + gk.vtk_physical_faces!(vtk,mesh) vtk["tag"] = node_to_tag vtk["uh"] = uh - vtk["dim"] = glk.face_dim(mesh) + vtk["dim"] = gk.face_dim(mesh) end end - - -function diag!(diagA,A::SparseMatrixCSC) - # TODO improve with binary search - colptr = A.colptr - rowval = A.rowval - nzval = A.nzval - ncols = length(colptr)-1 - for j in 1:ncols - pini = colptr[j] - pend = colptr[j+1]-1 - for p in pini:pend - i = rowval[p] - if j != i - continue - end - diagA[j] = nzval[p] - end - end - diagA -end - -function spmv!(b,A,x) - T = eltype(b) - alpha = zero(T) - beta = one(T) - spmv!(b,A,x,alpha,beta) - b -end - -function spmv!(b,A::SparseMatrixCSC,x,alpha,beta) - colptr = A.colptr - rowval = A.rowval - nzval = A.nzval - nrows,ncols = size(A) - if alpha == zero(eltype(b)) - b .= alpha - else - b .= alpha .* b - end - for j in 1:ncols - xj = x[j] - pini = colptr[j] - pend = colptr[j+1]-1 - for p in pini:pend - i = rowval[p] - aij = nzval[p] - b[i] += beta*aij*xj - end - end - b -end - -function spmm(A::SparseMatrixCSC,B::SparseMatrixCSC) - # TODO implementation - A*B -end - -const SparseMatrixCSCT = Transpose{R,SparseMatrixCSC{R,S}} where {R,S} - -function sprap(R::SparseMatrixCSCT,A::SparseMatrixCSC,P::SparseMatrixCSC) - # TODO implementation - R*A*P -end - -function jacobi!(x,A,b;kwargs...) - setup = jacobi_setup(x,A,b;kwargs...) - jacobi!(x,A,b,setup) -end - -function jacobi_setup(x,A,b;maxiters,omega=1) - options = (;maxiters,omega) - jacobi_setup(x,A,b,options) -end - -function jacobi_setup(x,A::SparseMatrixCSC,b,options) - xnew = similar(x) - diagA = similar(x) - diag!(diagA,A) - (;xnew,diagA,options) -end - -function jacobi_setup!(setup,A::SparseMatrixCSC) - diag!(setup.diagA,A) - setup -end - -function jacobi!(xin,A::SparseMatrixCSC,b,setup) - maxiters = setup.options.maxiters - omega = setup.options.omega - x = xin - xnew = setup.xnew - diagA = setup.diagA - colptr = A.colptr - rowval = A.rowval - nzval = A.nzval - nrows,ncols = size(A) - for iter in 1:maxiters - xnew .= b .+ diagA .* x ./ omega - for j in 1:ncols - xj = x[j] - pini = colptr[j] - pend = colptr[j+1]-1 - for p in pini:pend - i = rowval[p] - aij = nzval[p] - xnew[i] -= aij*xj - end - end - xnew .= omega .* xnew ./ diagA - x,xnew = xnew,x - end - xin .= x - xin -end - -function aggregate(A;epsilon) - options = (;epsilon) - aggregate(A,options) -end - -function aggregate(A::SparseMatrixCSC,options) - - epsi = options.epsilon - typeof_aggregate = Int32 - typeof_strength = eltype(A.nzval) - - nnodes = size(A,1) - pending = typeof_aggregate(0) - isolated = typeof_aggregate(-1) - - node_to_aggregate = fill(pending,nnodes) - node_to_old_aggregate = similar(node_to_aggregate) - - diagA = zeros(eltype(A),nnodes) - diag!(diagA,A) - - node_to_neigs = jagged_array(A.rowval,A.colptr) - node_to_vals = jagged_array(A.nzval,A.colptr) - strongly_connected = (node,ineig) -> begin - neig = node_to_neigs[node][ineig] - aii = diagA[node] - ajj = diagA[neig] - aij = node_to_vals[node][ineig] - abs(aij) > epsi*sqrt(aii*ajj) - end - coupling_strength = (node,ineig) -> begin - abs(node_to_vals[node][ineig]) - end - - # Initialization - for node in 1:nnodes - neigs = node_to_neigs[node] - isolated_node = count(i->i!=node,neigs) == 0 - if isolated_node - node_to_aggregate[node] = isolated - end - end - - # Step 1 - aggregate = typeof_aggregate(0) - for node in 1:nnodes - if node_to_aggregate[node] != pending - continue - end - neigs = node_to_neigs[node] - nneigs = length(neigs) - all_pending = true - for ineig in 1:nneigs - neig = neigs[ineig] - if neig == node || !strongly_connected(node,ineig) - continue - end - all_pending &= (node_to_aggregate[neig] == pending) - end - if !all_pending - continue - end - aggregate += typeof_aggregate(1) - node_to_aggregate[node] = aggregate - for ineig in 1:nneigs - neig = neigs[ineig] - if neig == node || !strongly_connected(node,ineig) - continue - end - node_to_aggregate[neig] = aggregate - end - end - - # Step 2 - copy!(node_to_old_aggregate,node_to_aggregate) - for node in 1:nnodes - if node_to_aggregate[node] != pending - continue - end - strength = zero(typeof_strength) - neigs = node_to_neigs[node] - nneigs = length(neigs) - for ineig in 1:nneigs - neig = neigs[ineig] - if neig == node || !strongly_connected(node,ineig) - continue - end - neig_aggregate = node_to_old_aggregate[neig] - if neig_aggregate != pending && neig_aggregate != isolated - neig_strength = coupling_strength(node,ineig) - if neig_strength > strength - strength = neig_strength - node_to_aggregate[node] = neig_aggregate - end - end - end - end - - # Step 3 - for node in 1:nnodes - if node_to_aggregate[node] != pending - continue - end - aggregate += typeof_aggregate(1) - node_to_aggregate[node] = aggregate - neigs = node_to_neigs[node] - nneigs = length(neigs) - for ineig in 1:nneigs - neig = neigs[ineig] - if neig == node || !strongly_connected(node,ineig) - continue - end - neig_aggregate = node_to_old_aggregate[neig] - if neig_aggregate == pending || neig_aggregate == isolated - node_to_aggregate[neig] = aggregate - end - end - end - naggregates = aggregate - - ## Compression - aggregate_to_nodes_ptrs = zeros(Int,naggregates+1) - for node in 1:nnodes - agg = node_to_aggregate[node] - if agg == pending - continue - end - aggregate_to_nodes_ptrs[agg+1] += 1 - end - length_to_ptrs!(aggregate_to_nodes_ptrs) - ndata = aggregate_to_nodes_ptrs[end]-1 - aggregate_to_nodes_data = zeros(Int,ndata) - for node in 1:nnodes - agg = node_to_aggregate[node] - if agg == pending - continue - end - p = aggregate_to_nodes_ptrs[agg] - aggregate_to_nodes_data[p] = node - aggregate_to_nodes_ptrs[agg] += 1 - end - rewind_ptrs!(aggregate_to_nodes_ptrs) - aggregate_to_nodes = JaggedArray(aggregate_to_nodes_data,aggregate_to_nodes_ptrs) - - aggregate_to_nodes -end - -function prolongator(A,agg_to_nodes;omega=1) - options = (;omega) - prolongator(A,agg_to_nodes,options) -end - -function prolongator(A::SparseMatrixCSC,agg_to_nodes,options) - # TODO Optimize - omega = options.omega - nrows,ncols = size(A) - diagA = zeros(eltype(A.nzval),nrows) - diag!(diagA,A) # TODO We are taking the diagonal to many times - nagg = length(agg_to_nodes) - P0 = SparseMatrixCSC(ncols,nagg,agg_to_nodes.ptrs,agg_to_nodes.data,ones(length(agg_to_nodes.data))) - Dinv = sparse(1:nrows,1:nrows,1 ./ diagA,nrows,nrows) - Id = sparse(1:nrows,1:nrows,ones(nrows),nrows,nrows) - P = (I-omega*Dinv*A)*P0 -end - -function smooth_aggregation(A;epsilon,omega) - aggrs = aggregate(A;epsilon) - P = prolongator(A,aggrs;omega) - R = transpose(P) - Ac = sprap(R,A,P) - Ac,R,P -end - -function amg!(x,A,b;kwargs...) - setup = amg_setup(x,A,b;kwargs...) - amg!(x,A,b,setup) -end - -function amg_setup(x,A,b;fine_params,coarse_solver) - nlevels = length(fine_params) - fine_levels_setup = map(fine_params) do fine_level - (;pre_smoother,pos_smoother,coarsen,cycle) = fine_level - pre_setup = pre_smoother.setup(x,A,b;pre_smoother.params...) - pos_setup = pos_smoother.setup(x,A,b;pos_smoother.params...) - pre_smoother! = (x,b) -> pre_smoother.solve(x,A,b,pre_setup) - pos_smoother! = (x,b) -> pos_smoother.solve(x,A,b,pos_setup) - Ac,R,P = coarsen.setup(A;coarsen.params...) - r = similar(b) - e = similar(x) - rc = similar(r,axes(Ac,1)) - ec = similar(e,axes(Ac,2)) - level_setup = (;R,A,Ac,P,r,e,rc,ec,pre_smoother!,pos_smoother!,cycle) - A = Ac - b = rc - x = ec - level_setup - end - coarse_setup = coarse_solver.setup(x,A,b,coarse_solver.params...) - coarse_solver! = (x,b)->coarse_solver.solve(x,A,b,coarse_setup) - (;nlevels,fine_levels_setup,coarse_solver!) -end - -function amg!(x,A,b,setup) - level = 1 - amg_cycle!(x,b,setup,level) - x -end - -function amg_cycle!(x,b,setup,level) - if level == setup.nlevels+1 - return setup.coarse_solver!(x,b) - end - level_setup = setup.fine_levels_setup[level] - (;R,A,P,r,e,rc,ec,pre_smoother!,pos_smoother!,cycle) = level_setup - pre_smoother!(x,b) - copy!(r,b) - spmv!(r,A,x,-1,1) # TODO mul! better? Provably, yes - mul!(rc,R,r) # TODO spmv! - fill!(ec,zero(eltype(ec))) - cycle(ec,rc,setup,level+1) - spmv!(e,P,ec) - x .-= e - pos_smoother!(x,b) - x -end - -function v_cycle!(args...) - amg_cycle!(args...) -end - -function w_cycle!(args...) - amg_cycle!(args...) - amg_cycle!(args...) -end - +using Test + +tol = 1.0e-10 +params = Dict{Symbol,Any}() +params[:mesh] = gk.cartesian_mesh((0,3,0,2),(10,5),complexify=false) +params[:dirichlet_tags] = ["1-face-1","1-face-3","1-face-4"] +params[:neumann_tags] = ["1-face-2"] +params[:u] = (x) -> sum(x) +params[:f] = (x) -> 0.0 +params[:g] = (x) -> 1.0 +results = main(params) +@test results[:eh1] < tol +@test results[:el2] < tol +@test results[:ncells] == 10*5 + +params = Dict{Symbol,Any}() +params[:mesh] = gk.cartesian_mesh((0,3,0,2,0,1),(5,5,5)) +results = main(params) +@test results[:eh1] < tol +@test results[:el2] < tol +@test results[:ncells] == 5*5*5 + +params = Dict{Symbol,Any}() +params[:mesh] = gk.cartesian_mesh((0,3,0,2),(5,5),simplexify=true) +results = main(params) +@test results[:eh1] < tol +@test results[:el2] < tol + +params = Dict{Symbol,Any}() +params[:mesh] = gk.cartesian_mesh((0,3,0,2,0,1),(5,5,5),simplexify=true) +results = main(params) +@test results[:eh1] < tol +@test results[:el2] < tol + +params = Dict{Symbol,Any}() +params[:hi] = 1 +results = main(params) +@test results[:eh1] < tol +@test results[:el2] < tol end # module diff --git a/test/mesh_interface_tests.jl b/test/mesh_interface_tests.jl new file mode 100644 index 00000000..f2ed7510 --- /dev/null +++ b/test/mesh_interface_tests.jl @@ -0,0 +1,140 @@ +module MeshInterfaceTests + +using Test +import GalerkinToolkit as gk +using WriteVTK + +spx0 = gk.unit_simplex(0) +spx1 = gk.unit_simplex(1) +spx2 = gk.unit_simplex(2) +spx3 = gk.unit_simplex(3) +display(spx3) + +cube0 = gk.unit_n_cube(0) +cube1 = gk.unit_n_cube(1) +cube2 = gk.unit_n_cube(2) +cube3 = gk.unit_n_cube(3) +display(cube3) + +@show typeof(spx0) +@show typeof(cube0) +@show typeof(spx1) +@show typeof(cube1) + +degree = 4 +quad = gk.default_quadrature(spx0,degree) +quad = gk.default_quadrature(spx1,degree) +quad = gk.default_quadrature(spx2,degree) +quad = gk.default_quadrature(spx3,degree) + +quad = gk.default_quadrature(cube0,degree) +quad = gk.default_quadrature(cube1,degree) +quad = gk.default_quadrature(cube2,degree) +quad = gk.default_quadrature(cube3,degree) + + +order = 1 +fe = gk.lagrangian_fe(spx0,order) +fe = gk.lagrangian_fe(spx1,order) +fe = gk.lagrangian_fe(spx2,order) +fe = gk.lagrangian_fe(spx3,order) +display(fe) + +fe = gk.lagrangian_fe(cube0,order) +fe = gk.lagrangian_fe(cube1,order) +fe = gk.lagrangian_fe(cube2,order) +fe = gk.lagrangian_fe(cube3,order) +display(fe) + +fe = gk.lagrangian_fe(cube0,order) +@show gk.monomial_exponents(fe) +@show gk.node_coordinates(fe) +fe = gk.lagrangian_fe(cube2,order) +@show gk.node_coordinates(fe) + +spx2 = gk.unit_simplex(2) +quad = gk.default_quadrature(spx2,degree) +fe = gk.lagrangian_fe(spx2,order) +funs = gk.shape_functions(fe) +x = gk.coordinates(quad) +B = broadcast(gk.value,permutedims(funs),x) +display(B) +tabulator = gk.tabulator(fe) +A = tabulator(gk.value,x) +@test A≈B +x = gk.node_coordinates(fe) +A = tabulator(gk.value,x) + +fe = gk.lagrangian_fe(spx2,order;shape=(3,)) +funs = gk.shape_functions(fe) +x = gk.coordinates(quad) +B = broadcast(gk.value,permutedims(funs),x) +display(B) +tabulator = gk.tabulator(fe) +A = tabulator(gk.value,x) +@test A≈B +x = gk.node_coordinates(fe) +A = tabulator(gk.value,x) + +fe = gk.lagrangian_fe(spx2,order;shape=()) +funs = gk.shape_functions(fe) +x = gk.coordinates(quad) +B = broadcast(gk.value,permutedims(funs),x) +display(B) +tabulator = gk.tabulator(fe) +A = tabulator(gk.value,x) +@test A≈B +x = gk.node_coordinates(fe) +A = tabulator(gk.value,x) + +outdir = mkpath(joinpath(@__DIR__,"..","output")) + +msh = joinpath(@__DIR__,"..","assets","demo.msh") +mesh = gk.mesh_from_gmsh(msh;complexify=false) +vtk_grid(joinpath(outdir,"demo"),gk.vtk_args(mesh)...) do vtk + gk.vtk_physical_faces!(vtk,mesh) +end + +@show gk.unit_simplex(0) |> gk.boundary +@show gk.unit_simplex(1) |> gk.boundary +@show gk.unit_simplex(2) |> gk.boundary +@show gk.unit_simplex(3) |> gk.boundary + +@show gk.unit_n_cube(0) |> gk.boundary +@show gk.unit_n_cube(1) |> gk.boundary +@show gk.unit_n_cube(2) |> gk.boundary +@show gk.unit_n_cube(3) |> gk.boundary + +order = 2 +gk.lagrangian_fe(spx1,order) |> gk.boundary |> gk.topology + +mesh = gk.mesh_from_gmsh(msh;complexify=false) + +new_mesh, old_to_new = gk.complexify(mesh) + +mesh = gk.mesh_from_gmsh(msh) + +domain = (0,1,0,1) +cells = (2,2) +mesh = gk.cartesian_mesh(domain,cells) +mesh = gk.cartesian_mesh(domain,cells,boundary=false) +mesh = gk.cartesian_mesh(domain,cells,simplexify=true) +mesh = gk.cartesian_mesh(domain,cells,boundary=false,simplexify=true) + +mesh = gk.mesh_from_gmsh(msh) +face_groups = gk.physical_faces(mesh) +group_names = gk.physical_names(mesh,2) +group_names = gk.physical_names(mesh) +group_names = gk.physical_names(mesh;merge_dims=true) +node_groups = gk.physical_nodes(mesh;merge_dims=true) +node_groups = gk.physical_nodes(mesh;merge_dims=true,disjoint=true) + +vmesh, vglue = gk.visualization_mesh(mesh) + +#∂spx0 = gk.boundary(spx0) +#∂spx0 = gk.boundary(spx1) +#∂cube0 = gk.boundary(cube0) + + + +end # module diff --git a/test/runtests.jl b/test/runtests.jl deleted file mode 100644 index bb4aa396..00000000 --- a/test/runtests.jl +++ /dev/null @@ -1,140 +0,0 @@ -module GalerkinToolkitTests - -using Test -import GalerkinToolkit as gt -using WriteVTK - -spx0 = gt.unit_simplex(0) -spx1 = gt.unit_simplex(1) -spx2 = gt.unit_simplex(2) -spx3 = gt.unit_simplex(3) -display(spx3) - -cube0 = gt.unit_n_cube(0) -cube1 = gt.unit_n_cube(1) -cube2 = gt.unit_n_cube(2) -cube3 = gt.unit_n_cube(3) -display(cube3) - -@show typeof(spx0) -@show typeof(cube0) -@show typeof(spx1) -@show typeof(cube1) - -degree = 4 -quad = gt.default_quadrature(spx0,degree) -quad = gt.default_quadrature(spx1,degree) -quad = gt.default_quadrature(spx2,degree) -quad = gt.default_quadrature(spx3,degree) - -quad = gt.default_quadrature(cube0,degree) -quad = gt.default_quadrature(cube1,degree) -quad = gt.default_quadrature(cube2,degree) -quad = gt.default_quadrature(cube3,degree) - - -order = 1 -fe = gt.lagrangian_fe(spx0,order) -fe = gt.lagrangian_fe(spx1,order) -fe = gt.lagrangian_fe(spx2,order) -fe = gt.lagrangian_fe(spx3,order) -display(fe) - -fe = gt.lagrangian_fe(cube0,order) -fe = gt.lagrangian_fe(cube1,order) -fe = gt.lagrangian_fe(cube2,order) -fe = gt.lagrangian_fe(cube3,order) -display(fe) - -fe = gt.lagrangian_fe(cube0,order) -@show gt.monomial_exponents(fe) -@show gt.node_coordinates(fe) -fe = gt.lagrangian_fe(cube2,order) -@show gt.node_coordinates(fe) - -spx2 = gt.unit_simplex(2) -quad = gt.default_quadrature(spx2,degree) -fe = gt.lagrangian_fe(spx2,order) -funs = gt.shape_functions(fe) -x = gt.coordinates(quad) -B = broadcast(gt.value,permutedims(funs),x) -display(B) -tabulator = gt.tabulator(fe) -A = tabulator(gt.value,x) -@test A≈B -x = gt.node_coordinates(fe) -A = tabulator(gt.value,x) - -fe = gt.lagrangian_fe(spx2,order;shape=(3,)) -funs = gt.shape_functions(fe) -x = gt.coordinates(quad) -B = broadcast(gt.value,permutedims(funs),x) -display(B) -tabulator = gt.tabulator(fe) -A = tabulator(gt.value,x) -@test A≈B -x = gt.node_coordinates(fe) -A = tabulator(gt.value,x) - -fe = gt.lagrangian_fe(spx2,order;shape=()) -funs = gt.shape_functions(fe) -x = gt.coordinates(quad) -B = broadcast(gt.value,permutedims(funs),x) -display(B) -tabulator = gt.tabulator(fe) -A = tabulator(gt.value,x) -@test A≈B -x = gt.node_coordinates(fe) -A = tabulator(gt.value,x) - -outdir = mkpath(joinpath(@__DIR__,"..","output")) - -msh = joinpath(@__DIR__,"..","assets","demo.msh") -mesh = gt.mesh_from_gmsh(msh;complexify=false) -vtk_grid(joinpath(outdir,"demo"),gt.vtk_args(mesh)...) do vtk - gt.vtk_physical_faces!(vtk,mesh) -end - -@show gt.unit_simplex(0) |> gt.boundary -@show gt.unit_simplex(1) |> gt.boundary -@show gt.unit_simplex(2) |> gt.boundary -@show gt.unit_simplex(3) |> gt.boundary - -@show gt.unit_n_cube(0) |> gt.boundary -@show gt.unit_n_cube(1) |> gt.boundary -@show gt.unit_n_cube(2) |> gt.boundary -@show gt.unit_n_cube(3) |> gt.boundary - -order = 2 -gt.lagrangian_fe(spx1,order) |> gt.boundary |> gt.topology - -mesh = gt.mesh_from_gmsh(msh;complexify=false) - -new_mesh, old_to_new = gt.complexify(mesh) - -mesh = gt.mesh_from_gmsh(msh) - -domain = (0,1,0,1) -cells = (2,2) -mesh = gt.cartesian_mesh(domain,cells) -mesh = gt.cartesian_mesh(domain,cells,boundary=false) -mesh = gt.cartesian_mesh(domain,cells,simplexify=true) -mesh = gt.cartesian_mesh(domain,cells,boundary=false,simplexify=true) - -mesh = gt.mesh_from_gmsh(msh) -face_groups = gt.physical_faces(mesh) -group_names = gt.physical_names(mesh,2) -group_names = gt.physical_names(mesh) -group_names = gt.physical_names(mesh;merge_dims=true) -node_groups = gt.physical_nodes(mesh;merge_dims=true) -node_groups = gt.physical_nodes(mesh;merge_dims=true,disjoint=true) - -vmesh, vglue = gt.visualization_mesh(mesh) - -#∂spx0 = gt.boundary(spx0) -#∂spx0 = gt.boundary(spx1) -#∂cube0 = gt.boundary(cube0) - - - -end # module From f89273b4b6371da128e2e72cde504c0ad12241b6 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 14:18:34 +0100 Subject: [PATCH 13/34] Adding missing file --- test/runtests.jl | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 test/runtests.jl diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 00000000..46028b3e --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,9 @@ +module GalerkinToolkitTests + +using Test + +@testset "Mesh interface" begin include("mesh_interface_tests.jl") end + +@testset "Iso-parametric Poisson" begin include("iso_param_poisson_tests.jl") end + +end # module From 4262999fdf9f911e699c2b20baac938c25f69fe0 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 14:22:22 +0100 Subject: [PATCH 14/34] Deleting old files --- examples/poisson_bddc.jl | 541 -------------------------------------- src/bddc.jl | 344 ------------------------ test/poisson_bddc_test.jl | 38 --- test/poisson_test.jl | 50 ---- test/unittests.jl | 332 ----------------------- 5 files changed, 1305 deletions(-) delete mode 100644 examples/poisson_bddc.jl delete mode 100644 src/bddc.jl delete mode 100644 test/poisson_bddc_test.jl delete mode 100644 test/poisson_test.jl delete mode 100644 test/unittests.jl diff --git a/examples/poisson_bddc.jl b/examples/poisson_bddc.jl deleted file mode 100644 index ead0b30e..00000000 --- a/examples/poisson_bddc.jl +++ /dev/null @@ -1,541 +0,0 @@ -module PoissonBDDC - -import GalerkinToolkit as glk -import ForwardDiff -using StaticArrays -using LinearAlgebra -using SparseArrays -using WriteVTK -using PartitionedArrays -using Metis -using IterativeSolvers - -function main(params_in) - - # Dict to collect results - results = Dict{Symbol,Any}() - - # Process params - params_default = default_params() - params = add_default_params(params_in,params_default) - - # Setup main data structures - state = setup(params) - - # Assemble system and solve it - A,b = assemble_system(state) - uh = solve_system(results,A,b,state,params) - - # Post process - integrate_error_norms!(results,uh,state) - export_results(uh,params,state) - - results -end - -function add_default_params(params_in,params_default) - UmD = setdiff(keys(params_in),keys(params_default)) - for k in UmD - @warn "Parameter with key :$k is unused" - end - UiD = intersect(keys(params_default),keys(params_in)) - DmU = setdiff(keys(params_default),keys(params_in)) - a = [ k=>params_in[k] for k in UiD] - b = [ k=>params_default[k] for k in DmU] - Dict(vcat(a,b)) -end - -function default_params() - mesh = glk.cartesian_mesh((0,1,0,1),(10,10),complexify=false) - np = 4 - ranks = DebugArray(LinearIndices((np,))) - pmesh = glk.partition_mesh(Metis.partition,ranks,mesh,via=:cells) - outdir = mkpath(joinpath(@__DIR__,"..","output")) - params = Dict{Symbol,Any}() - params[:pmesh] = glk.cartesian_mesh((0,1,0,1),(10,10)) - params[:u] = (x) -> sum(x) - params[:f] = (x) -> 0.0 - params[:g] = (x) -> 0.0 - params[:dirichlet_tags] = ["boundary"] - params[:neumann_tags] = String[] - params[:integration_degree] = 2 - params[:example_path] = joinpath(outdir,"poisson_bddc") - params[:bddc_type] = glk.bddc_cef() - params -end - -function setup_dirichlet_bcs(mesh,params) - u = params[:u] - dirichlet_tags = params[:dirichlet_tags] - node_to_tag = zeros(glk.num_nodes(mesh)) - tag_to_name = dirichlet_tags - glk.classify_mesh_nodes!(node_to_tag,mesh,tag_to_name) - free_and_dirichlet_nodes = glk.partition_from_mask(i->i==0,node_to_tag) - node_to_x = glk.node_coordinates(mesh) - dirichlet_nodes = last(free_and_dirichlet_nodes) - x_dirichlet = view(node_to_x,dirichlet_nodes) - u_dirichlet = u.(x_dirichlet) - dirichlet_bcs = (;free_and_dirichlet_nodes,u_dirichlet,node_to_tag) -end - -function setup_integration(mesh,params,objects) - degree = params[:integration_degree] - D = glk.num_dims(mesh) - if objects === :cells - d = D - elseif objects === :faces - d = D-1 - else - error("") - end - # Integration - ref_cells = glk.reference_faces(mesh,d) - face_to_rid = glk.face_reference_id(mesh,d) - integration_rules = map(ref_cells) do ref_cell - glk.quadrature(glk.geometry(ref_cell),degree) - end - rid_to_weights = map(glk.weights,integration_rules) - rid_to_coords = map(glk.coordinates,integration_rules) - integration = (;rid_to_weights,rid_to_coords,face_to_rid,d) -end - -function setup_isomap(mesh,params,integration) - rid_to_coords = integration.rid_to_coords - face_to_rid = integration.face_to_rid - d = integration.d - ref_cells = glk.reference_faces(mesh,d) - shape_funs = map(rid_to_coords,ref_cells) do q,ref_cell - shape_functions = glk.shape_functions(ref_cell) - shape_vals = glk.tabulation_matrix(shape_functions)(glk.value,q) - shape_grads = glk.tabulation_matrix(shape_functions)(ForwardDiff.gradient,q) - shape_vals, shape_grads - end - rid_to_shape_vals = map(first,shape_funs) - rid_to_shape_grads = map(last,shape_funs) - face_to_nodes = glk.face_nodes(mesh,d) - node_to_coords = glk.node_coordinates(mesh) - isomap = (;face_to_nodes,node_to_coords,face_to_rid,rid_to_shape_vals,rid_to_shape_grads,d) -end - -function setup_neumann_bcs(mesh,params) - neumann_tags = params[:neumann_tags] - neum_face_to_face = Int[] - if length(neumann_tags) != 0 - D = glk.num_dims(mesh) - tag_to_groups = glk.physical_groups(mesh,D-1) - neum_face_to_face = reduce(union,map(tag->tag_to_groups[tag],neumann_tags)) - end - (;neum_face_to_face) -end - -function setup_user_funs(params) - u = params[:u] - f = params[:f] - g = params[:g] - user_funs = (;u,f,g) -end - -function setup(params) - pmesh = params[:pmesh] - bddc_type = params[:bddc_type] - ranks = linear_indices(pmesh) - state1 = map(ranks,pmesh) do rank,mesh - dirichlet_bcs = setup_dirichlet_bcs(mesh,params) - neumann_bcs = setup_neumann_bcs(mesh,params) - cell_integration = setup_integration(mesh,params,:cells) - face_integration = setup_integration(mesh,params,:faces) - cell_isomap = setup_isomap(mesh,params,cell_integration) - face_isomap = setup_isomap(mesh,params,face_integration) - user_funs = setup_user_funs(params) - lnodes = glk.local_nodes(mesh) - (; - dirichlet_bcs, - neumann_bcs, - cell_integration, - cell_isomap, - face_integration, - face_isomap, - user_funs, - rank, - bddc_type, - lnodes) - end - state2 = setup_dof_partition(state1) - state2 -end - -function setup_dof_partition(state1) - nodofs = map(state1) do state1 - rank = state1.rank - dirichlet_bcs = state1.dirichlet_bcs - free_and_dirichlet_lnodes = dirichlet_bcs.free_and_dirichlet_nodes - lnodes = state1.lnodes - lnode_to_owner = local_to_owner(lnodes) - ldof_to_lnode = first(free_and_dirichlet_lnodes) - count(lnode->lnode_to_owner[lnode]==rank,ldof_to_lnode) - end - ndofs = sum(nodofs) - dof_partition = variable_partition(nodofs,ndofs) - node_partition = map(i->i.lnodes,state1) - v = PVector{Vector{Int}}(undef,node_partition) - fill!(v,0) - map(local_values(v),dof_partition,state1) do lnode_to_dof, odofs, state1 - rank = state1.rank - lnodes = state1.lnodes - dirichlet_bcs = state1.dirichlet_bcs - ldof_to_lnode = first(dirichlet_bcs.free_and_dirichlet_nodes) - odof_to_dof = own_to_global(odofs) - lnode_to_owner = local_to_owner(lnodes) - nldofs = length(ldof_to_lnode) - odof = 0 - for ldof in 1:nldofs - lnode = ldof_to_lnode[ldof] - if lnode_to_owner[lnode] == rank - odof += 1 - dof = odof_to_dof[odof] - lnode_to_dof[lnode] = dof - end - end - end - consistent!(v) |> wait - state2 = map(local_values(v),state1) do lnode_to_dof,state1 - rank = state1.rank - lnodes = state1.lnodes - dirichlet_bcs = state1.dirichlet_bcs - lnode_to_owner = local_to_owner(lnodes) - ldof_to_lnode = first(dirichlet_bcs.free_and_dirichlet_nodes) - ldof_to_dof = lnode_to_dof[ldof_to_lnode] - ldof_to_owner = lnode_to_owner[ldof_to_lnode] - ldofs = LocalIndices(ndofs,rank,ldof_to_dof,ldof_to_owner) - (;ldofs,state1...) - end - state2 -end - -function assemble_system(state) - Alocal,blocal = map(assemble_system_locally,state) |> tuple_of_arrays - map(add_neumann_bcs_locally!,blocal,state) - dof_partition = map(i->i.ldofs,state) - b = PVector(blocal,dof_partition) - assemble!(b) |> wait # do not forget this - A = glk.DDOperator(Alocal,axes(b,1)) - A,b -end - -function assemble_system_locally(state) - - cell_to_nodes = state.cell_isomap.face_to_nodes - cell_to_rid = state.cell_isomap.face_to_rid - free_and_dirichlet_nodes = state.dirichlet_bcs.free_and_dirichlet_nodes - node_to_x = state.cell_isomap.node_to_coords - ∇s = state.cell_isomap.rid_to_shape_grads - s = state.cell_isomap.rid_to_shape_vals - u_dirichlet = state.dirichlet_bcs.u_dirichlet - f = state.user_funs.f - w = state.cell_integration.rid_to_weights - d = state.cell_integration.d - - # Count coo entries - n_coo = 0 - ncells = length(cell_to_nodes) - node_to_free_node = glk.permutation(free_and_dirichlet_nodes) - nfree = length(first(free_and_dirichlet_nodes)) - for cell in 1:ncells - nodes = cell_to_nodes[cell] - for nj in nodes - if node_to_free_node[nj] > nfree - continue - end - for ni in nodes - if node_to_free_node[ni] > nfree - continue - end - n_coo += 1 - end - end - end - - # Allocate coo values - I_coo = zeros(Int,n_coo) - J_coo = zeros(Int,n_coo) - V_coo = zeros(Float64,n_coo) - b = zeros(nfree) - - # Allocate auxiliary buffers - ∇x = map(i->similar(i,size(i,2)),∇s) - Aes = map(i->zeros(size(i,2),size(i,2)),∇s) - fes = map(i->zeros(size(i,2)),∇s) - ues = map(i->zeros(size(i,2)),∇s) - - Tx = SVector{d,Float64} - TJ = typeof(zero(Tx)*zero(Tx)') - - # Fill coo values - i_coo = 0 - for cell in 1:ncells - rid = cell_to_rid[cell] - nodes = cell_to_nodes[cell] - Ae = Aes[rid] - ue = ues[rid] - fe = fes[rid] - nl = length(nodes) - ∇se = ∇s[rid] - ∇xe = ∇x[rid] - se = s[rid] - we = w[rid] - nq = length(we) - fill!(Ae,zero(eltype(Ae))) - fill!(fe,zero(eltype(Ae))) - fill!(ue,zero(eltype(Ae))) - for k in 1:nl - nk = nodes[k] - gk = node_to_free_node[nk] - if gk <= nfree - continue - end - ue[k] = u_dirichlet[gk-nfree] - end - for iq in 1:nq - Jt = zero(TJ) - xint = zero(Tx) - for k in 1:nl - x = node_to_x[nodes[k]] - ∇sqx = ∇se[iq,k] - sqx = se[iq,k] - Jt += ∇sqx*x' - xint += sqx*x - end - detJt = det(Jt) - invJt = inv(Jt) - dV = abs(detJt)*we[iq] - for k in 1:nl - ∇xe[k] = invJt*∇se[iq,k] - end - for j in 1:nl - for i in 1:nl - Ae[i,j] += ∇xe[i]⋅∇xe[j]*dV - end - end - fx = f(xint) - for k in 1:nl - fe[k] += fx*se[iq,k]*dV - end - end - for i in 1:nl - for j in 1:nl - fe[i] -= Ae[i,j]*ue[j] - end - end - for i in 1:nl - ni = nodes[i] - gi = node_to_free_node[ni] - if gi > nfree - continue - end - b[gi] += fe[i] - end - for j in 1:nl - nj = nodes[j] - gj = node_to_free_node[nj] - if gj > nfree - continue - end - for i in 1:nl - ni = nodes[i] - gi = node_to_free_node[ni] - if gi > nfree - continue - end - i_coo += 1 - I_coo[i_coo] = gi - J_coo[i_coo] = gj - V_coo[i_coo] = Ae[i,j] - end - end - end - - A = sparse(I_coo,J_coo,V_coo,nfree,nfree) - - A,b -end - -function add_neumann_bcs_locally!(b,state) - - neum_face_to_face = state.neumann_bcs.neum_face_to_face - if length(neum_face_to_face) == 0 - return nothing - end - face_to_rid = state.face_isomap.face_to_rid - face_to_nodes = state.face_isomap.face_to_nodes - ∇s_f = state.face_isomap.rid_to_shape_grads - s_f = state.face_isomap.rid_to_shape_vals - node_to_x = state.face_isomap.node_to_coords - free_and_dirichlet_nodes = state.dirichlet_bcs.free_and_dirichlet_nodes - g = state.user_funs.g - w_f = state.face_integration.rid_to_weights - d = state.cell_integration.d - - fes = map(i->zeros(size(i,2)),s_f) - Tx = SVector{d,Float64} - Ts = typeof(zero(SVector{d-1,Float64})*zero(Tx)') - node_to_free_node = glk.permutation(free_and_dirichlet_nodes) - nfree = length(first(free_and_dirichlet_nodes)) - - for face in neum_face_to_face - rid = face_to_rid[face] - nodes = face_to_nodes[face] - ∇se = ∇s_f[rid] - se = s_f[rid] - we = w_f[rid] - nq = length(we) - fe = fes[rid] - fill!(fe,zero(eltype(fe))) - nl = length(nodes) - for iq in 1:nq - Jt = zero(Ts) - xint = zero(Tx) - for k in 1:nl - x = node_to_x[nodes[k]] - ∇sqx = ∇se[iq,k] - sqx = se[iq,k] - Jt += ∇sqx*x' - xint += sqx*x - end - J = transpose(Jt) - dS = sqrt(det(Jt*J))*we[iq] - gx = g(xint) - for k in 1:nl - fe[k] += gx*se[iq,k]*dS - end - end - for i in 1:nl - ni = nodes[i] - gi = node_to_free_node[ni] - if gi > nfree - continue - end - b[gi] += fe[i] - end - end - - nothing -end - -function solve_system(results,A,b,state,params) - M = glk.bddc_preconditioner(A;bddc_type=params[:bddc_type]) - x = similar(b) - fill!(x,zero(eltype(b))) - IterativeSolvers.cg!(copy(x),A,b,Pl=M,verbose=i_am_main(state)) - ranks = linear_indices(state) - t = PTimer(ranks) - tic!(t,barrier=true) - IterativeSolvers.cg!(x,A,b,Pl=M) - toc!(t,"cg!") - display(t) - consistent!(x) |> wait - node_partition = map(i->i.lnodes,state) - uh = pzeros(node_partition) - map(state,local_values(uh),local_values(x)) do state, uh, u_free - free_and_dirichlet_nodes = state.dirichlet_bcs.free_and_dirichlet_nodes - u_dirichlet = state.dirichlet_bcs.u_dirichlet - uh[first(free_and_dirichlet_nodes)] = u_free - uh[last(free_and_dirichlet_nodes)] = u_dirichlet - end - uh -end - -function export_results(uh,params,state) - example_path = params[:example_path] - pmesh = params[:pmesh] - ranks = linear_indices(pmesh) - np = length(ranks) - map(pmesh,ranks,local_values(uh)) do mesh,rank,uh - pvtk_grid(example_path,glk.vtk_args(mesh)...;part=rank,nparts=np) do vtk - glk.vtk_physical_groups!(vtk,mesh) - vtk["piece",VTKCellData()] = fill(rank,sum(glk.num_faces(mesh))) - vtk["owner",VTKPointData()] = local_to_owner(glk.local_nodes(mesh)) - vtk["uh",VTKPointData()] = uh - vtk["interface",VTKPointData()] = map(colors->Int(length(colors)!=1),glk.local_node_colors(mesh)) - end - end -end - -function integrate_error_norms!(results,uh,state) - errs = map(integrate_error_norms_local,local_values(uh),state) - eh1 = sum(map(i->i.eh1,errs)) |> sqrt - el2 = sum(map(i->i.el2,errs)) |> sqrt - results[:eh1] = eh1 - results[:el2] = el2 - results -end - -function integrate_error_norms_local(uh,state) - - cell_to_nodes = state.cell_isomap.face_to_nodes - cell_to_rid = state.cell_isomap.face_to_rid - free_and_dirichlet_nodes = state.dirichlet_bcs.free_and_dirichlet_nodes - node_to_x = state.cell_isomap.node_to_coords - ∇s = state.cell_isomap.rid_to_shape_grads - s = state.cell_isomap.rid_to_shape_vals - u_dirichlet = state.dirichlet_bcs.u_dirichlet - u = state.user_funs.u - w = state.cell_integration.rid_to_weights - d = state.cell_integration.d - - eh1 = 0.0 - el2 = 0.0 - ncells = length(cell_to_rid) - ues = map(i->zeros(size(i,2)),∇s) - ∇x = map(i->similar(i,size(i,2)),∇s) - Tx = SVector{d,Float64} - TJ = typeof(zero(Tx)*zero(Tx)') - for cell in 1:ncells - rid = cell_to_rid[cell] - nodes = cell_to_nodes[cell] - ue = ues[rid] - nl = length(nodes) - ∇se = ∇s[rid] - ∇xe = ∇x[rid] - se = s[rid] - we = w[rid] - nq = length(we) - for k in 1:nl - nk = nodes[k] - ue[k] = uh[nk] - end - for iq in 1:nq - Jt = zero(TJ) - xint = zero(Tx) - for k in 1:nl - x = node_to_x[nodes[k]] - ∇sqx = ∇se[iq,k] - sqx = se[iq,k] - Jt += ∇sqx*x' - xint += sqx*x - end - detJt = det(Jt) - invJt = inv(Jt) - dV = abs(detJt)*we[iq] - for k in 1:nl - ∇xe[k] = invJt*∇se[iq,k] - end - ux = u(xint) - ∇ux = ForwardDiff.gradient(u,xint) - ∇uhx = zero(∇ux) - uhx = zero(ux) - for k in 1:nl - uek = ue[k] - ∇uhx += uek*∇xe[k] - uhx += uek*se[iq,k] - end - ∇ex = ∇ux - ∇uhx - ex = ux - uhx - eh1 += (∇ex⋅∇ex + ex*ex)*dV - el2 += ex*ex*dV - end - end - - (;eh1,el2) -end - -end # module diff --git a/src/bddc.jl b/src/bddc.jl deleted file mode 100644 index 12906ab1..00000000 --- a/src/bddc.jl +++ /dev/null @@ -1,344 +0,0 @@ - -const DD_CORNER = 0 -const DD_EDGE = 1 -const DD_FACE = 2 - -struct DDOperator{A,B<:PRange} - matrices::A - ids::B -end - -Base.eltype(A::DDOperator) = eltype(eltype(A.matrices)) -Base.eltype(::Type{<:DDOperator{A}}) where A = eltype(eltype(A)) -Base.size(A::DDOperator) = (length(A.ids),length(A.ids)) -Base.axes(A::DDOperator) = (A.ids,A.ids) -Base.size(A::DDOperator,i::Integer) = length(A.ids) -Base.axes(A::DDOperator,i::Integer) = A.ids - -function Base.:*(A::DDOperator,x::PVector) - b = similar(x) - mul!(b,A,x) - b -end - -function LinearAlgebra.mul!(b::PVector,A::DDOperator,x::PVector) - fill!(b,zero(eltype(b))) - consistent!(x) |> wait # TODO we can add latency hiding here - map(mul!,local_values(b),A.matrices,local_values(x)) - assemble!(b) |> wait - b -end - -struct BDDC{T<:DDOperator,S} - A::T - workspace::S -end - -function LinearAlgebra.ldiv!(x::PVector,M::BDDC,r::PVector) - consistent!(r) |> wait - workspace = M.workspace - # Coarse correction - rci = map(workspace,local_values(r)) do workspace,ri - Wi = workspace.Wi - Phii = workspace.Phii - PhiiT = transpose(Phii) - PhiiT*(Wi .* ri)# TODO allocations here - end - rank_to_rci = gather(rci,destination=MAIN) # TODO allocations here - rank_to_v1ci = map(rank_to_rci,workspace) do rank_to_rci,workspace - rank = workspace.rank - if rank == MAIN - ncdofs = workspace.ncdofs - Pc = workspace.Pc - rank_to_cdofs = workspace.rank_to_cdofs - rc = zeros(ncdofs)#TODO allocation here - nparts = length(rank_to_cdofs) - for part in 1:nparts - cdofs = rank_to_cdofs[part] - rci = rank_to_rci[part] - rc[cdofs] += rci - end - v1ic = Pc\rc # TODO allocation here - JaggedArray([ v1ic[cdofs] for cdofs in rank_to_cdofs])# TODO allocation here - else - JaggedArray([Float64[]])# TODO allocation here - end - end - v1ci = scatter(rank_to_v1ci) # TODO allocation here - v1 = similar(x) # TODO allocation here - map(workspace,local_values(v1),v1ci) do workspace,v1i,v1ci - Wi = workspace.Wi - Phii = workspace.Phii - v1i .= Wi.*(Phii*v1ci) # TODO allocations here - end - assemble!(v1) |> wait # TODO group communications - - # Neumann correction - v2 = similar(x) # TODO allocation here - map(workspace,local_values(v2),local_values(r)) do workspace,v2i,r - Wi = workspace.Wi - nldofs = length(Wi) - Pn = workspace.Pn - ncldofs = workspace.ncldofs - rhs = vcat((Wi.*r),zeros(ncldofs)) # TODO allocations here - sol = Pn\rhs # TODO allocation here - zi = view(sol,1:nldofs) - v2i .= Wi .* zi - end - assemble!(v2) |> wait # TODO group communications - - # Dirichlet correction - A = M.A - r1 = r - A*(v1+v2) # TODO allocations here - consistent!(r1) |> wait - v3 = similar(x) # TODO allocation here - map(workspace,local_values(v3),local_values(r1)) do workspace, v3i, r1i - RIi = workspace.RIi - Pd = workspace.Pd - fill!(v3i,zero(eltype(v3i))) - v3i[RIi] .= Pd\r1i[RIi] # TODO allocations here - end - #assemble!(v3) |> wait # TODO really needed, boundary is 0? TODO group communications - - # Final correction - x .= v1 .+ v2 .+ v3 - x -end - -bddc_cef() = (DD_CORNER,DD_EDGE,DD_FACE) -bddc_c() = (DD_CORNER,) -bddc_cf() = (DD_CORNER,DD_FACE) -bddc_ce() = (DD_CORNER,DD_EDGE) - -function bddc_preconditioner(A::DDOperator;bddc_type=bddc_cef()) - dof_partition = partition(A.ids) - coarse_dofs = generate_coarse_objects(bddc_type,dof_partition) - setup_BDDC(A,coarse_dofs) -end - -function generate_coarse_objects( - bddc_type, - dof_partition, - ldof_to_colors=collect_parts_around(dof_partition), - ) - ranks = linear_indices(dof_partition) - state = map(ranks,ldof_to_colors) do rank,ldof_to_colors - interface_dof_to_ldof = findall(colors->length(colors)!=1,ldof_to_colors) - interface_dof_to_colors = view(ldof_to_colors,interface_dof_to_ldof) - cldof_to_colors = unique(interface_dof_to_colors) - cldof_to_owner = map(maximum,cldof_to_colors) - ncldofs = length(cldof_to_colors) - interface_dof_to_cldof = indexin(interface_dof_to_colors,cldof_to_colors) - cldof_to_interface_dofs = inverse_index_map(interface_dof_to_cldof,ncldofs) - f = interface_dof->interface_dof_to_ldof[interface_dof] - cldof_to_interface_dofs.data .= f.(cldof_to_interface_dofs.data) - cldof_to_ldofs = cldof_to_interface_dofs - cldof_to_type = fill(DD_EDGE,ncldofs) - cldof_to_type[ map(i->length(i) == 2,cldof_to_colors) ] .= DD_FACE - cldof_to_type[ map(i->length(i) == 1,cldof_to_ldofs) ] .= DD_CORNER - mask = map(i->i in bddc_type,cldof_to_type) - cldof_to_colors = JaggedArray(cldof_to_colors[mask]) - cldof_to_ldofs = JaggedArray(cldof_to_ldofs[mask]) - cldof_to_owner = cldof_to_owner[mask] - ncodofs = count(owner->owner==rank,cldof_to_owner) - (;ldof_to_colors,cldof_to_colors,cldof_to_ldofs,cldof_to_owner,ncodofs,rank) - end - nown = map(i->i.ncodofs,state) - ncdofs = sum(nown) - codof_partition = variable_partition(nown,ncdofs) - v = PVector{Vector{Int}}(undef,dof_partition) - map(state,local_values(v),codof_partition) do state, ldof_to_cdof, codofs - rank = state.rank - cldof_to_owner = state.cldof_to_owner - cldof_to_ldofs = state.cldof_to_ldofs - codof_to_cdof = own_to_global(codofs) - codof = 0 - for (cldof,owner) in enumerate(cldof_to_owner) - if owner != rank - continue - end - codof += 1 - cdof = codof_to_cdof[codof] - ldofs = cldof_to_ldofs[cldof] - ldof_to_cdof[ldofs] .= cdof - end - end - consistent!(v) |> wait - cldof_to_cdof_all = map(state,local_values(v)) do state, ldof_to_cdof - rank = state.rank - cldof_to_ldofs = state.cldof_to_ldofs - ncldofs = length(cldof_to_ldofs) - cldof_to_cdof = zeros(Int,ncldofs) - for cldof in 1:ncldofs - ldofs = cldof_to_ldofs[cldof] - cdof = ldof_to_cdof[first(ldofs)] - cldof_to_cdof[cldof] = cdof - end - cldof_to_cdof - end - rank_to_cdofs = gather(cldof_to_cdof_all,destination=MAIN) - map(state,rank_to_cdofs) do state, rank_to_cdofs - cldof_to_ldofs = state.cldof_to_ldofs - (;cldof_to_ldofs, rank_to_cdofs,ncdofs, state...) - end -end - -function setup_BDDC(A,state) - workspace1 = map(A.matrices,state) do Ai,state - # Setup Neumann problems - cldof_to_ldofs = state.cldof_to_ldofs - nldofs = size(Ai,1) - ncldofs = length(cldof_to_ldofs) - cldof_to_coefs = map(cldof_to_ldofs) do ldofs - fill(1/length(ldofs),length(ldofs)) - end - mycolptr = cldof_to_ldofs.ptrs - myrowval = cldof_to_ldofs.data - mynzval = reduce(vcat,cldof_to_coefs) - CiT = SparseMatrixCSC(nldofs,ncldofs,mycolptr,myrowval,mynzval) - Ci = transpose(CiT) - Zi = spzeros(ncldofs,ncldofs) - Ti = [Ai CiT; Ci Zi] - Pn = lu(Ti)# TODO with some work one could use Cholesky - rhs = [zeros(nldofs,ncldofs);Matrix(I,ncldofs,ncldofs)] - Phii = (Pn\rhs)[1:nldofs,:] - - # Setup Dirichlet problems - ldof_to_colors = state.ldof_to_colors - idof_to_ldof = findall(colors->length(colors)==1,ldof_to_colors) - RIi = idof_to_ldof - AIi = Ai[RIi,RIi] - Pd = cholesky(AIi) - - # Setup weight - ldof_to_w = map(colors->1/length(colors),ldof_to_colors) - Wi = ldof_to_w - - rank_to_cdofs = state.rank_to_cdofs - rank = state.rank - ncdofs = state.ncdofs - (;Pn,Phii,Pd,RIi,Wi,ncldofs,rank_to_cdofs,rank,ncdofs) - end - - # Setup coarse solver - Aci = map(workspace1,A.matrices) do workspace1,Ai - Phii = workspace1.Phii - PhiiT = transpose(Phii) - PhiiT*Ai*Phii - end - rank_to_Aci = gather_matrix(Aci,destination=MAIN) - workspace2 = map(rank_to_Aci,workspace1) do rank_to_Aci,workspace1 - rank_to_cdofs = workspace1.rank_to_cdofs - rank = workspace1.rank - ncdofs = workspace1.ncdofs - if rank == MAIN - nparts = length(rank_to_cdofs) - ncoo = 0 - for part in 1:nparts - cdofs = rank_to_cdofs[part] - ncoo += length(cdofs)*length(cdofs) - end - Icoo = zeros(Int,ncoo) - Jcoo = zeros(Int,ncoo) - Vcoo = zeros(ncoo) - ncoo = 0 - for part in 1:nparts - cdofs = rank_to_cdofs[part] - myAci = rank_to_Aci[part] - for lj in 1:size(myAci,2) - for li in 1:size(myAci,1) - ncoo += 1 - Icoo[ncoo] = cdofs[li] - Jcoo[ncoo] = cdofs[lj] - Vcoo[ncoo] = myAci[li,lj] - end - end - end - Ac = sparse(Icoo,Jcoo,Vcoo,ncdofs,ncdofs) - Ac = 0.5*(Ac+transpose(Ac)) # TODO cholesky requires exactly symmetric matrices (no rounding errors allowed) - Pc = cholesky(Ac) - else - Pc = nothing - end - (;Pc,workspace1...) - end - BDDC(A,workspace2) -end - -# TODO this should be handled by PartitionedArrays -function gather_matrix(Aci;destination=MAIN) - s = gather(map(i->size(i),Aci);destination) - data = gather(map(i->i[:],Aci);destination) - ranks = linear_indices(Aci) - map(ranks,data,s) do rank,data,s - if rank == destination - map(reshape,data,s) - else - nothing - end - end -end - -function collect_parts_around(dof_partition) - v = PVector{Vector{Int32}}(undef,dof_partition) - map(i->fill!(i,Int32(1)),local_values(v)) - assemble!(v) |> wait - consistent!(v) |> wait - ranks = linear_indices(dof_partition) - ldof_to_parts_around = map(ranks,local_values(v)) do rank,ldof_to_n_parts_around - nldofs = length(ldof_to_n_parts_around) - ptrs = zeros(Int32,nldofs+1) - for ldof in 1:nldofs - ptrs[ldof+1] = ldof_to_n_parts_around[ldof] - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = fill(Int32(rank),ndata) - JaggedArray(data,ptrs) - end - map(fill!,local_values(v),ranks) - cache = v.cache - vector_partition = partition(v) - buffer_snd = map(vector_partition,cache) do values,cache - local_indices_snd = cache.local_indices_snd - for (p,lid) in enumerate(local_indices_snd.data) - cache.buffer_snd.data[p] = values[lid] - end - cache.buffer_snd - end - neighbors_snd, neighbors_rcv, buffer_rcv = map(cache) do cache - cache.neighbors_snd, cache.neighbors_rcv, cache.buffer_rcv - end |> tuple_of_arrays - graph = ExchangeGraph(neighbors_snd,neighbors_rcv) - exchange!(buffer_rcv,buffer_snd,graph) |> wait - map(ldof_to_parts_around,vector_partition,cache) do ldof_to_parts_around,values,cache - local_indices_rcv = cache.local_indices_rcv - ldof_to_parts_around_ptrs = copy(ldof_to_parts_around.ptrs) - for (p,lid) in enumerate(local_indices_rcv.data) - q = ldof_to_parts_around_ptrs[lid] - ldof_to_parts_around.data[q] = cache.buffer_rcv.data[p] - ldof_to_parts_around_ptrs[lid] += 1 - end - map(sort!,ldof_to_parts_around) - end - w = PVector(ldof_to_parts_around,dof_partition) - consistent!(w) |> wait - ldof_to_parts_around -end - -function inverse_index_map(a_to_b,nb) - b_to_as_ptrs = zeros(Int32,nb+1) - for b in a_to_b - b_to_as_ptrs[b+1] += 1 - end - length_to_ptrs!(b_to_as_ptrs) - ndata = b_to_as_ptrs[end]-1 - b_to_as_data = zeros(Int32,ndata) - for (a,b) in enumerate(a_to_b) - p = b_to_as_ptrs[b] - b_to_as_data[p] = a - b_to_as_ptrs[b] += 1 - end - rewind_ptrs!(b_to_as_ptrs) - JaggedArray(b_to_as_data,b_to_as_ptrs) -end diff --git a/test/poisson_bddc_test.jl b/test/poisson_bddc_test.jl deleted file mode 100644 index ec0326e9..00000000 --- a/test/poisson_bddc_test.jl +++ /dev/null @@ -1,38 +0,0 @@ -module PoissonParallelTests - -using Test -import GalerkinToolkit as gt -using PartitionedArrays -using Metis - -include("../examples/poisson_bddc.jl") - -with_debug() do distribute - - #tol = 1.0e-7 - #np = 8 - #load = 20 - #n = ceil(Int,load*(np^(1/3))) - - params = Dict{Symbol,Any}() - n = 10 - np = 4 #MPI.Comm_size(MPI.COMM_WORLD) - tol = 1.0e-6 - mesh = gt.cartesian_mesh((0,2,0,2,0,2),(n,n,n),complexify=false) - ranks = distribute(LinearIndices((np,))) - pmesh = gt.partition_mesh(Metis.partition,ranks,mesh,via=:cells) - params[:pmesh] = pmesh - #params[:dirichlet_tags] = ["1-face-1","1-face-3","1-face-4"] - #params[:neumann_tags] = ["1-face-2"] - params[:dirichlet_tags] = ["boundary"] - params[:u] = (x) -> sum(x) - params[:f] = (x) -> 0.0 - params[:g] = (x) -> 1.0 - @time results = PoissonBDDC.main(params) - @test results[:eh1] < tol - @test results[:el2] < tol - -end - - -end # module diff --git a/test/poisson_test.jl b/test/poisson_test.jl deleted file mode 100644 index e3c027ba..00000000 --- a/test/poisson_test.jl +++ /dev/null @@ -1,50 +0,0 @@ -module PoissonTest - -using Test -import GalerkinToolkit as gt -# TODO normal vectors (reference normals plus push) -# Triangular meshes - -include("../examples/poisson.jl") - -tol = 1.0e-10 - -params = Dict{Symbol,Any}() -params[:mesh] = gt.cartesian_mesh((0,3,0,2),(10,5),complexify=false) -params[:dirichlet_tags] = ["1-face-1","1-face-3","1-face-4"] -params[:neumann_tags] = ["1-face-2"] -params[:u] = (x) -> sum(x) -params[:f] = (x) -> 0.0 -params[:g] = (x) -> 1.0 -@time results = Poisson.main(params) -@test results[:eh1] < tol -@test results[:el2] < tol -@test results[:ncells] == 10*5 - -params = Dict{Symbol,Any}() -params[:mesh] = gt.cartesian_mesh((0,3,0,2,0,1),(5,5,5)) -@time results = Poisson.main(params) -@test results[:eh1] < tol -@test results[:el2] < tol -@test results[:ncells] == 5*5*5 - -params = Dict{Symbol,Any}() -params[:mesh] = gt.cartesian_mesh((0,3,0,2),(5,5),simplexify=true) -@time results = Poisson.main(params) -@test results[:eh1] < tol -@test results[:el2] < tol - -params = Dict{Symbol,Any}() -params[:mesh] = gt.cartesian_mesh((0,3,0,2,0,1),(5,5,5),simplexify=true) -@time results = Poisson.main(params) -@test results[:eh1] < tol -@test results[:el2] < tol - -params = Dict{Symbol,Any}() -params[:hi] = 1 -@time results = Poisson.main(params) -@test results[:eh1] < tol -@test results[:el2] < tol - - -end # module diff --git a/test/unittests.jl b/test/unittests.jl deleted file mode 100644 index d5368113..00000000 --- a/test/unittests.jl +++ /dev/null @@ -1,332 +0,0 @@ -module UnitTests - -import GalerkinToolkit as glk -using StaticArrays -using WriteVTK -using SparseArrays -using LinearAlgebra -using ForwardDiff -using Test -using PartitionedArrays -using Metis - - -# TODO -# before more cleanup: -# generate a high order mesh from a linear non oriented mesh -# -# visualization mesh [done] -# -# remove interpolation [done] -# remove gradient! and value! [done] -# swap order in tabulation matrix. Important!! -# topology reference_faces[end][1].boundary -# cleanups -# parametrize with Ti and Tf -# think about order if it needs to be a tuple - -#lagrangian_reference_face(geo) -#lagrangian_reference_element(geo) -#lagrangian_reference_element(geo,shape=:scaler) -#lagrangian_reference_element(geo,shape=()) -#lagrangian_reference_element(geo,shape=(2,)) - -#segment -#num_dims(segment) -#face_reference_id(segment,d) -#face_reference_id(segment.boundary,d) -#face_reference_id(segment.boundary.topology,d) -#shape_functions(segment,d) -#node_coordinates(segment,d) -# -#mesh -#num_dims(mesh) -#face_reference_id(mesh,d) -#face_reference_id(mesh.topology,d) -#physical_groups(mesh) -# -#mesh2 = set_data(mesh;physical_groups,topology) - -domain = (1,2,1,2) -np = 4 -cells = (4,4) -ranks = LinearIndices((np,)) -mesh = glk.cartesian_mesh(domain,cells) -pmesh = glk.partition_mesh(Metis.partition,ranks,mesh,via=:cells) -node_partition = map(mesh->mesh.local_nodes,pmesh) -node_to_parts = glk.collect_parts_around(node_partition) - -outdir = mkpath(joinpath(@__DIR__,"..","output")) - -msh = joinpath(@__DIR__,"..","assets","demo.msh") -mesh = glk.mesh_from_gmsh(msh;complexify=false) - -np = 4 -ranks = DebugArray(LinearIndices((np,))) -# TODO inclure global "glue" outside pmesh? -pmesh = glk.partition_mesh(Metis.partition,ranks,mesh,via=:nodes) -pmesh = glk.partition_mesh(Metis.partition,ranks,mesh,via=:cells) - - - -map(pmesh,ranks) do mesh,rank - pvtk_grid(joinpath(outdir,"pdebug"),glk.vtk_args(mesh)...;part=rank,nparts=np) do vtk - glk.vtk_physical_groups!(vtk,mesh) - vtk["piece"] = fill(rank,sum(glk.num_faces(mesh))) - vtk["owner"] = local_to_owner(mesh.local_nodes) - vtk["interface"] = map(colors->Int(length(colors)!=1),mesh.local_node_colors) - end -end - - -domain = (1,2,1,2) -cells = (2,2) -mesh = glk.cartesian_mesh(domain,cells) - -D = glk.num_dims(mesh) -geo = glk.reference_faces(mesh,D)|>first|>glk.geometry -reffe = glk.lagrangian_reference_element(geo,order=3) - -local_dofs = glk.Object(; - num_dims=Val(2), - face_reference_id = [Int[],Int[],fill(1,glk.num_faces(mesh,2))], - reference_faces = ((),(),[reffe]) -) - -dof_glue = glk.dof_glue_from_mesh_and_local_dofs(mesh,local_dofs) - -geo = glk.unit_n_cube(Val(1)) -reffe = glk.lagrangian_reference_element(geo) -A = reffe.shape_functions.tabulation_matrix(glk.value,reffe.node_coordinates) -B = reffe.shape_functions.tabulation_matrix(ForwardDiff.gradient,reffe.node_coordinates) - -reffe = glk.lagrangian_reference_element(geo,shape=(2,),order=2,major=:node) -A = reffe.shape_functions.tabulation_matrix(glk.value,reffe.node_coordinates) -A = reffe.shape_functions.tabulation_matrix(ForwardDiff.jacobian,reffe.node_coordinates) - -reffe = glk.lagrangian_reference_element(geo,shape=(2,),major=:component) -A = reffe.shape_functions.tabulation_matrix(glk.value,reffe.node_coordinates) -A = reffe.shape_functions.tabulation_matrix(ForwardDiff.jacobian,reffe.node_coordinates) -display(reffe.node_to_dofs) - -reffe = glk.lagrangian_reference_element(geo,shape=(),major=:component) -A = reffe.shape_functions.tabulation_matrix(glk.value,reffe.node_coordinates) -A = reffe.shape_functions.tabulation_matrix(ForwardDiff.jacobian,reffe.node_coordinates) -display(reffe.node_to_dofs) - -geo = glk.unit_n_cube(Val(2)) -reffe = glk.lagrangian_reference_element(geo,order=3,shape=(2,)) -display(reffe.face_own_dofs) -display(reffe.face_own_dof_permutations) - -reffe = glk.lagrangian_reference_element(geo,shape=(2,3)) -A = reffe.shape_functions.tabulation_matrix(glk.value,reffe.node_coordinates) - -geo = glk.unit_simplex(Val(1)) -perms = glk.vertex_permutations_from_geometry(geo) -@test length(perms) == 2 -glk.vertex_permutations(geo) == perms - -geo = glk.unit_simplex(Val(3)) -reffe = glk.lagrangian_reference_face(geo) - -@show reffe -display(reffe) - -perms = glk.vertex_permutations_from_geometry(geo) -glk.vertex_permutations(geo) == perms - -geo = glk.unit_n_cube(Val(2)) -perms = glk.vertex_permutations_from_geometry(geo) -@test length(perms) == 8 -glk.vertex_permutations(geo) == perms - -quad1 = glk.lagrangian_reference_face(geo) -node_perms = glk.interior_node_permutations_from_reference_face(quad1) -@test all(map(i->all(i .!= 0),node_perms)) - -quad2 = glk.lagrangian_reference_face(geo,order=2) -node_perms = glk.interior_node_permutations_from_reference_face(quad2) -@test all(map(i->all(i .!= 0),node_perms)) - -quad3 = glk.lagrangian_reference_face(geo,order=3) -node_perms = glk.interior_node_permutations_from_reference_face(quad3) -@test all(map(i->all(i .!= 0),node_perms)) - -domain = (1,2,1,2,1,2) -cells = (2,2,2) -mesh = glk.structured_simplex_mesh_with_boundary(domain,cells) -vtk_grid(joinpath(outdir,"debug"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -tet = glk.unit_simplex(Val(3)) -refface = glk.lagrangian_reference_face(tet,order=2) -mesh = glk.mesh_from_reference_face(refface) -vtk_grid(joinpath(outdir,"debug"),glk.vtk_args(mesh)...) do vtk - vtk["nodeid"] = 1:6 - glk.vtk_physical_groups!(vtk,mesh) -end - -vtk_grid(joinpath(outdir,"debug_boundary"),glk.vtk_args(glk.boundary(refface))...) do vtk - vtk["nodeid"] = 1:6 -end - -hex = glk.unit_n_cube(Val(3)) -tri_hex = glk.simplexify_reference_geometry(hex) -vtk_grid(joinpath(outdir,"debug"),glk.vtk_args(tri_hex)...) do vtk - glk.vtk_physical_groups!(vtk,tri_hex) -end - -order = 2 -refface = glk.lagrangian_reference_face(hex;order) -mesh = glk.simplexify_reference_face(refface) - -vtk_grid(joinpath(outdir,"debug"),glk.vtk_args(mesh)...) |> vtk_save - -domain = (1,2,1,2,1,2) -cells = (2,2,2) -mesh = glk.cartesian_mesh(domain,cells,boundary=false,complexify=false) - -vtk_grid(joinpath(outdir,"cartesian"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -mesh = glk.cartesian_mesh(domain,cells,complexify=false) - -vtk_grid(joinpath(outdir,"cartesian"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -domain = (1,2,1,2) -cells = (2,2) -mesh = glk.cartesian_mesh(domain,cells,simplexify=false,boundary=false,complexify=false) -vismesh, visglue = glk.visualization_mesh_from_mesh(mesh) -vismesh, visglue = glk.visualization_mesh_from_mesh(mesh,order=2) -vismesh, visglue = glk.visualization_mesh_from_mesh(mesh,resolution=4) - -pointdata = zeros(glk.num_nodes(vismesh)) -for face in 1:glk.num_faces(mesh,2) - fine_nodes = visglue.face_fine_nodes[face] - for fine_node in fine_nodes - pointdata[fine_node] = face - end -end - -vtk_grid(joinpath(outdir,"vismesh"),glk.vtk_args(vismesh,2)...) do vtk - vtk["cellid"] = visglue.parent_face - vtk["pointdata"] = pointdata -end - -mesh = glk.cartesian_mesh(domain,cells,simplexify=true,boundary=false,complexify=false) -vismesh, visglue = glk.visualization_mesh_from_mesh(mesh) -vismesh, visglue = glk.visualization_mesh_from_mesh(mesh,order=2) -vismesh, visglue = glk.visualization_mesh_from_mesh(mesh,resolution=4) - -domain = (1,2,1,2,1,2) -cells = (2,2,2) -mesh = glk.cartesian_mesh(domain,cells,boundary=false,complexify=false) - -mesh,_ = glk.complexify_mesh(mesh) - -vtk_grid(joinpath(outdir,"cartesian"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -mesh = glk.cartesian_mesh(domain,cells) - -vtk_grid(joinpath(outdir,"cartesian"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -mesh = glk.cartesian_mesh(domain,cells,simplexify=true) - -vtk_grid(joinpath(outdir,"cartesian"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -mesh = glk.cartesian_mesh(domain,cells,simplexify=true,boundary=false) - -vtk_grid(joinpath(outdir,"cartesian"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -vertex = glk.unit_n_cube(Val(0)) -vertex1 = glk.lagrangian_reference_face(vertex) - -segment = glk.unit_n_cube(Val(1)) -vtk_grid(joinpath(outdir,"segment"),glk.vtk_args(segment.boundary)...) |> vtk_save - -segment2 = glk.lagrangian_reference_face(segment) -segment3 = glk.lagrangian_reference_face(segment,order=2) -segment4 = glk.lagrangian_reference_face(segment,order=4) - -quad = glk.unit_n_cube(Val(2)) -vtk_grid(joinpath(outdir,"quad"),glk.vtk_args(quad.boundary)...) |> vtk_save - -quad4 = glk.lagrangian_reference_face(quad,order=1) -quad9 = glk.lagrangian_reference_face(quad,order=2) -vtk_grid(joinpath(outdir,"quad9"),glk.vtk_args(quad9.boundary)...) |> vtk_save - -mesh_quad4 = glk.mesh_from_reference_face(quad4) - -vtk_grid(joinpath(outdir,"mesh_quad4"),glk.vtk_args(mesh_quad4)...) |> vtk_save - -order = 1 -segment = glk.unit_n_cube(Val(1)) -segment2 = glk.lagrangian_reference_face(segment;order) -quad = glk.unit_n_cube(Val(2)) -quad4 = glk.lagrangian_reference_face(quad;order) - -node_coordinates = SVector{2,Float64}[(0,0),(1,0),(2,0),(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)] -face_nodes = [ - Vector{Int}[], - [[1,2],[2,3],[1,4],[4,7],[7,8],[8,9],[3,6],[6,9]], - [[1,2,4,5],[2,3,5,6],[4,5,7,8],[5,6,8,9]] - ] -face_reference_id = [Int[],Int[1,1,1,1,1,1,1,1],[1,1,1,1]] -reference_faces = ([],[segment2],[quad4]) -physical_groups = [ - Dict([]), - Dict(["face_1"=>[1,2],"face_2"=>[3,4],"boundary"=>[1,2,3,4,5,6,7,8]]), - Dict(["domain"=>[1,2,3,4]])] -mesh = (; - num_dims=Val(2),node_coordinates, - face_nodes,face_reference_id, - reference_faces,physical_groups) - -d = 2 -vtk_grid(joinpath(outdir,"mesh2"),glk.vtk_args(mesh,d)...) do vtk - glk.vtk_physical_groups!(vtk,mesh,d) -end - -d = 1 -vtk_grid(joinpath(outdir,"mesh1"),glk.vtk_args(mesh,d)...) do vtk - glk.vtk_physical_groups!(vtk,mesh,d) -end - -vtk_grid(joinpath(outdir,"mesh"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -new_mesh, old_to_new = glk.complexify_mesh(mesh) - -vtk_grid(joinpath(outdir,"new_mesh"),glk.vtk_args(new_mesh)...) do vtk - glk.vtk_physical_groups!(vtk,new_mesh) -end - -msh = joinpath(@__DIR__,"..","assets","demo.msh") -mesh = glk.mesh_from_gmsh(msh;complexify=true) -@test glk.periodic_nodes(mesh) == ([] => []) - -vtk_grid(joinpath(outdir,"mesh"),glk.vtk_args(mesh)...) do vtk - glk.vtk_physical_groups!(vtk,mesh) -end - -for d in 1:3 - vtk_grid(joinpath(outdir,"mesh_$d"),glk.vtk_args(mesh,d)...) do vtk - glk.vtk_physical_groups!(vtk,mesh,d) - end -end - -end # module From ffadb9d326cd5478c203cf842f140e9d4f26fa12 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 14:47:49 +0100 Subject: [PATCH 15/34] Minor --- docs/make.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/make.jl b/docs/make.jl index 82a13ed7..e61d8c97 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,7 +6,6 @@ DocMeta.setdocmeta!(GalerkinToolkit, :DocTestSetup, :(using GalerkinToolkit); re makedocs(; modules=[GalerkinToolkit], authors="Francesc Verdugo and contributors", - repo="https://github.com/fverdugo/GalerkinToolkit.jl/blob/{commit}{path}#{line}", sitename="GalerkinToolkit.jl", format=Documenter.HTML(; prettyurls=get(ENV, "CI", "false") == "true", From c6c68268ae67c93f61c400a5a04a1f9b908f061a Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 14:51:13 +0100 Subject: [PATCH 16/34] Some refactoring --- src/GalerkinToolkit.jl | 3017 +-------------------------------------- src/mesh_interface.jl | 3018 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3019 insertions(+), 3016 deletions(-) create mode 100644 src/mesh_interface.jl diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index c15564a2..7a67bf6e 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -10,3021 +10,6 @@ using PartitionedArrays using Combinatorics using SparseArrays -abstract type GalerkinToolkitDataType end -function Base.show(io::IO,data::GalerkinToolkitDataType) - print(io,"GalerkinToolkit.$(nameof(typeof(data)))(…)") -end - -function push(a::AbstractVector,x) - b = copy(a) - push!(b,x) - b -end - -function push(a::Tuple,x) - (a...,x) -end - -val_parameter(a) = a -val_parameter(::Val{a}) where a = a -num_dims(a) = val_parameter(a.num_dims) -node_coordinates(a) = a.node_coordinates -reference_faces(a) = a.reference_faces -face_nodes(a) = a.face_nodes -face_incidence(a) = a.face_incidence -face_reference_id(a) = a.face_reference_id -physical_faces(a) = a.physical_faces -has_physical_faces(a) = hasproperty(a,:physical_faces) && a.physical_faces !== nothing -periodic_nodes(a) = a.periodic_nodes -has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing -geometry(a) = a.geometry -boundary(a) = a.boundary -is_n_cube(a) = hasproperty(a,:is_n_cube) ? val_parameter(a.is_n_cube) : false -is_simplex(a) = hasproperty(a,:is_simplex) ? val_parameter(a.is_simplex) : false -is_axis_aligned(a) = a.is_axis_aligned -bounding_box(a) = a.bounding_box -vertex_permutations(a) = a.vertex_permutations -face_own_dofs(a) = a.face_own_dofs -face_own_dof_permutations(a) = a.face_own_dof_permutations -node_to_dofs(a) = a.node_to_dofs -dof_to_node(a) = a.dof_to_node -dof_to_index(a) = a.dof_to_index -num_dofs(a) = a.num_dofs -coordinates(a) = a.coordinates -weights(a) = a.weights -order_per_dir(a) = a.order_per_dir -monomial_exponents(a) = a.monomial_exponents -lib_to_user_nodes(a) = a.lib_to_user_nodes -interior_nodes(a) = a.interior_nodes -face_permutation_ids(a) = a.face_permutation_ids -face_permutation_ids(a,m,n) = face_permutation_ids(a)[m+1,n+1] -local_nodes(a) = a.local_nodes -local_node_colors(a) = a.local_node_colors -real_type(a) = a.real_type -int_type(a) = a.int_type -outwards_normals(a) = a.outwards_normals - -reference_faces(a,d) = reference_faces(a)[val_parameter(d)+1] -face_nodes(a,d) = face_nodes(a)[val_parameter(d)+1] -face_incidence(a,d1,d2) = face_incidence(a)[val_parameter(d1)+1,val_parameter(d2)+1] -face_reference_id(a,d) = face_reference_id(a)[val_parameter(d)+1] -num_faces(a) = map(length,face_reference_id(a)) -num_faces(a,d) = length(face_reference_id(a,d)) -physical_faces(a,d) = physical_faces(a)[val_parameter(d)+1] -num_nodes(a) = length(node_coordinates(a)) -num_ambient_dims(a) = length(eltype(node_coordinates(a))) -function face_offsets(a) - D = num_dims(a) - offsets = zeros(Int,D+1) - for d in 1:D - offsets[d+1] = offsets[d] + num_faces(a,d-1) - end - offsets -end -function face_dim(a,d) - n = num_faces(a,d) - fill(d,n) -end -function face_dim(a) - D = num_dims(a) - reduce(vcat,map(d->face_dim(a,d),0:D)) -end - -abstract type AbstractFaceGeometry <: GalerkinToolkitDataType end - -struct ExtrusionPolytope{D,Tv,Ti} <: AbstractFaceGeometry - extrusion::NTuple{D,Bool} - bounding_box::NTuple{2,SVector{D,Tv}} - int_type::Type{Ti} -end - -function unit_simplex(num_dims;real_type=Float64,int_type=Int) - D = val_parameter(num_dims) - extrusion = ntuple(i->false,Val(D)) - Tv = real_type - p0 = ntuple(i->zero(real_type),Val(D)) |> SVector{D,Tv} - p1 = ntuple(i->one(real_type),Val(D)) |> SVector{D,Tv} - bounding_box = (p0,p1) - ExtrusionPolytope(extrusion,bounding_box,int_type) -end - -function unit_n_cube(num_dims;real_type=Float64,int_type=Int) - D = val_parameter(num_dims) - extrusion = ntuple(i->true,Val(D)) - Tv = real_type - p0 = ntuple(i->zero(real_type),Val(D)) |> SVector{D,Tv} - p1 = ntuple(i->one(real_type),Val(D)) |> SVector{D,Tv} - bounding_box = (p0,p1) - ExtrusionPolytope(extrusion,bounding_box,int_type) -end - -num_dims(p::ExtrusionPolytope{D}) where D = D -is_axis_aligned(p::ExtrusionPolytope) = true -is_simplex(geom::ExtrusionPolytope) = all(i->i==false,geom.extrusion) -is_n_cube(geom::ExtrusionPolytope) = all(geom.extrusion) -real_type(p::ExtrusionPolytope{D,Tv}) where {D,Tv} = Tv -int_type(p::ExtrusionPolytope{D,Tv,Ti}) where {D,Tv,Ti} = Ti - -function is_unit_n_cube(geo) - is_n_cube(geo) && is_unitary(geo) -end - -function is_unit_simplex(geo) - is_simplex(geo) && is_unitary(geo) -end - -function is_unitary(geom) - ! is_axis_aligned(geom) && return false - my_bounding_box = bounding_box(geom) - all(i->i==0,first(my_bounding_box)) && all(i->i==1,last(my_bounding_box)) -end - -struct GenericCuadrature{A,B} <: GalerkinToolkitDataType - coordinates::A - weights::B -end -struct Cuadrature{D,T} <: GalerkinToolkitDataType - coordinates::Vector{SVector{D,T}} - weights::Vector{T} -end -function quadrature(coordinates,weights) - GenericCuadrature(coordinates,weights) -end -function quadrature(coordinates::Vector{SVector{D,T}},weights::Vector{T}) where {D,T} - Cuadrature(coordinates,weights) -end - -function duffy_quadrature(geo,degree) - @assert is_unit_simplex(geo) - D = num_dims(geo) - if D == 0 - x = zeros(SVector{0,real_type},1) - w = ones(real_type,1) - return quadrature(x,w) - end - function map_to(a,b,(points,weights)) - points_ab = similar(points) - weights_ab = similar(weights) - points_ab .= 0.5*(b-a)*points .+ 0.5*(a+b) - weights_ab .= 0.5*(b-a)*weights - (points_ab, weights_ab) - end - function duffy_map(q) - D = length(q) - a = 1.0 - m = ntuple(Val(D)) do i - if i == 1 - q[i] - else - a *= (1-q[i-1]) - a*q[i] - end - end - typeof(q)(m) - end - n = ceil(Int, (degree + 1.0) / 2.0 ) - beta = 0 - dim_to_quad_1d = map(1:(D-1)) do d - alpha = (D-1)-(d-1) - map_to(0,1,gaussjacobi(n,alpha,beta)) - end - quad_1d = map_to(0,1,gausslegendre(n)) - push!(dim_to_quad_1d,quad_1d) - coords_per_dir = map(first,dim_to_quad_1d) - weights_per_dir = map(last,dim_to_quad_1d) - a = 0.5 - for d in (D-1):-1:1 - ws_1d = weights_per_dir[d] - ws_1d[:] *= a - a *= 0.5 - end - m = prod(map(length,weights_per_dir)) - Tv = real_type(geo) - w = zeros(Tv,m) - x = zeros(SVector{D,Tv},m) - tensor_product!(identity,x,coords_per_dir) - tensor_product!(prod,w,weights_per_dir) - x .= duffy_map.(x) - quadrature(x,w) -end - -function tensor_product_quadrature(geo,degree_per_dir) - @assert is_n_cube(geo) - @assert is_axis_aligned(geo) - my_bounding_box = bounding_box(geo) - D = num_dims(geo) - limits_per_dir = ntuple(i->(my_bounding_box[1][i],my_bounding_box[2][i]),Val(D)) - n_per_dir = map(d->ceil(Int,(d+1)/2),degree_per_dir) - function quadrature_1d(n,limits) - x,w = FastGaussQuadrature.gausslegendre(n) - a,b = limits - x .= (0.5*(b-a)) .*x .+ (0.5*(b+a)) - w .*= 0.5*(b-a) - quadrature(x,w) - end - quad_per_dir = map(quadrature_1d,n_per_dir,limits_per_dir) - coords_per_dir = map(coordinates,quad_per_dir) - weights_per_dir = map(weights,quad_per_dir) - m = prod(map(length,weights_per_dir)) - Tv = real_type(geo) - w = zeros(Tv,m) - x = zeros(SVector{D,Tv},m) - tensor_product!(identity,x,coords_per_dir) - tensor_product!(prod,w,weights_per_dir) - quadrature(x,w) -end - -function tensor_product!(f,result,values_per_dir) - shape = Tuple(map(length,values_per_dir)) - cis = CartesianIndices(shape) - lis = LinearIndices(cis) - for ci in cis - li = lis[ci] - result[li] = f(map((q,i)->q[i],values_per_dir,Tuple(ci))) - end - result -end - -function repeat_per_dir(geo,a) - D = num_dims(geo) - ntuple(i->a,Val(D)) -end -repeat_per_dir(geo,a::NTuple) = a - -function default_quadrature(geo,degree) - if is_n_cube(geo) && is_axis_aligned(geo) - D = num_dims(geo) - degree_per_dir = repeat_per_dir(geo,degree) - tensor_product_quadrature(geo,degree_per_dir) - elseif is_unit_simplex(geo) - duffy_quadrature(geo,degree) - else - error("Not implemented") - end -end - -abstract type AbstractMeshFace <: GalerkinToolkitDataType end - -num_dims(f::AbstractMeshFace) = num_dims(geometry(f)) - -abstract type AbstractLagrangeFE <: AbstractMeshFace end - -struct GenericLagrangeFE{A,B,C,D} <: AbstractLagrangeFE - geometry::A - order_per_dir::B - space::Symbol - lib_to_user_nodes::C - major::Symbol - shape::D -end - -struct ScalarShape end -const SCALAR_SHAPE = ScalarShape() - -lagrangian_fe(args...) = GenericLagrangeFE(args...) - -function lagrangian_fe(geometry,order; - space = default_space(geometry), - lib_to_user_nodes = int_type(geometry)[], - major = :component, - shape = SCALAR_SHAPE) - D = num_dims(geometry) - order_per_dir = repeat_per_dir(geometry,order) - lagrangian_fe( - geometry, - order_per_dir, - space, - lib_to_user_nodes, - major, - shape) -end - -function default_space(geom) - if is_simplex(geom) - :P - elseif is_n_cube(geom) - :Q - else - error("Not implemented") - end -end - -order(fe::AbstractLagrangeFE) = maximum(order_per_dir(fe),init=0) - -function lib_to_user_nodes(fe::AbstractLagrangeFE) - if length(fe.lib_to_user_nodes) == 0 - nnodes = num_nodes(fe) - Ti = int_type(geometry(fe)) - collect(Ti.(1:nnodes)) - else - fe.lib_to_user_nodes - end -end - -function monomial_exponents(fe::AbstractLagrangeFE) - monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> int_type) -end - -function node_coordinates(fe::AbstractLagrangeFE) - @assert fe |> geometry |> is_unitary - mexps = monomial_exponents(fe) - node_coordinates_from_monomials_exponents(mexps,fe.order_per_dir,fe.geometry |> real_type) -end - -function node_coordinates_from_monomials_exponents(monomial_exponents,order_per_dir,real_type) - D = length(order_per_dir) - Tv = real_type - if length(monomial_exponents) == 0 - return - end - node_coordinates = map(monomial_exponents) do exponent - map(exponent,order_per_dir) do e,order - if order != 0 - real_type(e/order) - else - real_type(e) - end - end |> SVector{D,Tv} - end -end - -function monomial_exponents_from_space(space,args...) - if space == :Q - monomial_exponents_from_filter((e,o)->true,args...) - elseif space == :P - monomial_exponents_from_filter((e,o)->sum(e)<=maximum(o,init=0),args...) - else - error("Case not implemented (yet)") - end -end - -function monomial_exponents_from_filter(f,order_per_dir,int_type) - Ti = int_type - terms_per_dir = Tuple(map(d->d+1,order_per_dir)) - D = length(terms_per_dir) - cis = CartesianIndices(terms_per_dir) - m = count(ci->f(SVector{D,Ti}(Tuple(ci) .- 1),order_per_dir),cis) - result = zeros(SVector{D,int_type},m) - li = 0 - for ci in cis - t = SVector{D,Ti}(Tuple(ci) .- 1) - if f(t,order_per_dir) - li += 1 - result[li] = t - end - end - result -end - -function tensor_basis(fe::AbstractLagrangeFE) - Tv = fe |> geometry |> real_type - if fe.shape == SCALAR_SHAPE - return Tv(1) - else - cis = CartesianIndices(val_parameter(fe.shape)) - l = prod(val_parameter(fe.shape)) - init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Tv} - cis_flat = cis[:] - return map(cis_flat) do ci - init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) - end - end -end - -function primal_basis(fe::AbstractLagrangeFE) - scalar_basis = map(e->(x-> prod(x.^e)),fe|>monomial_exponents) - if fe.shape == SCALAR_SHAPE - return scalar_basis - else - primal_nested = map(scalar_basis) do monomial - map(tensor_basis(fe)) do e - x -> monomial(x)*e - end - end - return reduce(vcat,primal_nested) - end -end - -function dual_basis(fe::AbstractLagrangeFE) - node_coordinates_reffe = fe|>node_coordinates - scalar_basis = map(x->(f->f(x)),node_coordinates_reffe) - if fe.shape == SCALAR_SHAPE - return scalar_basis - else - if fe.major === :component - dual_nested = map(node_coordinates_reffe) do x - map(tensor_basis(fe)) do e - f->inner(e,f(x)) - end - end - elseif fe.major === :node - dual_nested = map(tensor_basis(fe)) do e - map(node_coordinates_reffe) do x - f->inner(e,f(x)) - end - end - else - error("Not Implemented") - end - return reduce(vcat,dual_nested) - end -end - -inner(a,b) = sum(map(*,a,b)) -value(f,x) = f(x) - -function shape_functions(fe) - primal = primal_basis(fe) - dual = dual_basis(fe) - primal_t = permutedims(primal) - A = value.(dual,primal_t) - B = A\I - n = length(primal) - map(1:n) do i - Bi = view(B,:,i) - x->begin - primal_t_x = map(f->f(x),primal_t) - (primal_t_x*Bi)[1,1]#TODO - end - end -end - -function tabulator(fe) - primal = primal_basis(fe) - dual = dual_basis(fe) - primal_t = permutedims(primal) - A = value.(dual,primal_t) - B = A\I - (f,x) -> begin - C = broadcast(f,primal_t,x) - C*B - end -end - -abstract type AbstractFEMesh <: GalerkinToolkitDataType end - -struct GenericFEMesh{A,B,C,D,E,F,G} <: AbstractFEMesh - node_coordinates::A - face_nodes::B - face_reference_id::C - reference_faces::D - periodic_nodes::E - physical_faces::F - outwards_normals::G -end - -function fe_mesh(args...) - GenericFEMesh(args...) -end - -function fe_mesh( - node_coordinates, - face_nodes, - face_reference_id, - reference_faces; - periodic_nodes = eltype(eltype(face_reference_id))[], - physical_faces = map(i->Dict{String,Vector{eltype(eltype(face_reference_id))}}(),face_reference_id), - outwards_normals = nothing - ) - fe_mesh( - node_coordinates, - face_nodes, - face_reference_id, - reference_faces, - periodic_nodes, - physical_faces, - outwards_normals) -end - -num_dims(mesh::AbstractFEMesh) = length(reference_faces(mesh))-1 - -const INVALID_ID = 0 - -function default_gmsh_options() - [ - "General.Terminal"=>1, - "Mesh.SaveAll"=>1, - "Mesh.MedImportGroupsOfNodes"=>1 - ] -end - -function with_gmsh(f;options=default_gmsh_options()) - gmsh.initialize() - for (k,v) in options - gmsh.option.setNumber(k,v) - end - try - return f() - finally - gmsh.finalize() - end -end - -function mesh_from_gmsh(file;complexify=true,renumber=true,kwargs...) - @assert ispath(file) "File not found: $(file)" - with_gmsh(;kwargs...) do - gmsh.open(file) - renumber && gmsh.model.mesh.renumberNodes() - renumber && gmsh.model.mesh.renumberElements() - mesh_from_gmsh_module(;complexify) - end -end - -function mesh_from_gmsh_module(;complexify=true) - entities = gmsh.model.getEntities() - nodeTags, coord, parametricCoord = gmsh.model.mesh.getNodes() - - # find num_dims - ddim = -1 - for e in entities - ddim = max(ddim,e[1]) - end - if ddim == -1 - error("No entities in the msh file.") - end - D = ddim - - # find embedded_dimension - dtouched = [false,false,false] - for node in nodeTags - if !(coord[(node-1)*3+1] + 1 ≈ 1) - dtouched[1] = true - end - if !(coord[(node-1)*3+2] + 1 ≈ 1) - dtouched[2] = true - end - if !(coord[(node-1)*3+3] + 1 ≈ 1) - dtouched[3] = true - end - end - if dtouched[3] - adim = 3 - elseif dtouched[2] - adim = 2 - elseif dtouched[1] - adim = 1 - else - adim = 0 - end - - # Setup node coords - nmin = minimum(nodeTags) - nmax = maximum(nodeTags) - nnodes = length(nodeTags) - if !(nmax == nnodes && nmin == 1) - error("Only consecutive node tags allowed.") - end - my_node_to_coords = zeros(SVector{adim,Float64},nnodes) - m = zero(MVector{adim,Float64}) - for node in nodeTags - for j in 1:adim - k = (node-1)*3 + j - xj = coord[k] - m[j] = xj - end - my_node_to_coords[node] = m - end - - # Setup face nodes - offsets = zeros(Int32,D+1) - my_face_nodes = Vector{JaggedArray{Int32,Int32}}(undef,D+1) - for d in 0:D - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) - ndfaces = 0 - for t in 1:length(elemTypes) - ndfaces += length(elemTags[t]) - end - if ndfaces != 0 - nmin::Int = minimum( minimum, elemTags ) - nmax::Int = maximum( maximum, elemTags ) - if !( (nmax-nmin+1) == ndfaces) - error("Only consecutive elem tags allowed.") - end - offsets[d+1] = nmin-1 - end - ptrs = zeros(Int32,ndfaces+1) - dface = 0 - for t in 1:length(elemTypes) - elementName, dim, order, numNodes, nodeCoord = - gmsh.model.mesh.getElementProperties(elemTypes[t]) - for e in 1:length(elemTags[t]) - dface += 1 - ptrs[dface+1] = numNodes - end - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = zeros(Int32,ndata) - dface = 1 - for t in 1:length(elemTypes) - p = ptrs[dface]-Int32(1) - for (i,node) in enumerate(nodeTags[t]) - data[p+i] = node - end - dface += length(elemTags[t]) - end - my_face_nodes[d+1] = JaggedArray(data,ptrs) - end - - # Setup face_reference_id - my_face_reference_id = Vector{Vector{Int32}}(undef,D+1) - for d in 0:D - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) - ndfaces = length(my_face_nodes[d+1]) - dface_to_refid = zeros(Int8,ndfaces) - refid = 0 - dface = 0 - for t in 1:length(elemTypes) - refid += 1 - for e in 1:length(elemTags[t]) - dface += 1 - dface_to_refid[dface] = refid - end - end - my_face_reference_id[d+1] = dface_to_refid - end - - # Setup reference faces - my_reference_faces = () - for d in D:-1:0 - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) - refdfaces = () - for t in 1:length(elemTypes) - refface = reference_face_from_gmsh_eltype(elemTypes[t]) - refdfaces = (refdfaces...,refface) - end - if refdfaces == () - refdfaces = reference_faces(boundary(first(first(my_reference_faces))),d) - end - my_reference_faces = (refdfaces,my_reference_faces...) - end - - ## Setup periodic nodes - node_to_master_node = fill(Int32(INVALID_ID),nnodes) - for (dim,tag) in entities - tagMaster, nodeTags, nodeTagsMaster, = gmsh.model.mesh.getPeriodicNodes(dim,tag) - for i in 1:length(nodeTags) - node = nodeTags[i] - master_node = nodeTagsMaster[i] - node_to_master_node[node] = master_node - end - end - pnode_to_node = Int32.(findall(i->i!=INVALID_ID,node_to_master_node)) - pnode_to_master = node_to_master_node[pnode_to_node] - periodic_nodes = pnode_to_node => pnode_to_master - - # Setup physical groups - my_groups = [ Dict{String,Vector{Int32}}() for d in 0:D] - for d in 0:D - offset = Int32(offsets[d+1]) - dimTags = gmsh.model.getPhysicalGroups(d) - for (dim,tag) in dimTags - @boundscheck @assert dim == d - g_entities = gmsh.model.getEntitiesForPhysicalGroup(dim,tag) - ndfaces_in_physical_group = 0 - for entity in g_entities - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(dim,entity) - for t in 1:length(elemTypes) - ndfaces_in_physical_group += length(elemTags[t]) - end - end - dfaces_in_physical_group = zeros(Int32,ndfaces_in_physical_group) - ndfaces_in_physical_group = 0 - for entity in g_entities - elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(dim,entity) - for t in 1:length(elemTypes) - for etag in elemTags[t] - ndfaces_in_physical_group += 1 - dfaces_in_physical_group[ndfaces_in_physical_group] = Int32(etag)-offset - end - end - end - groupname = gmsh.model.getPhysicalName(dim,tag) - my_groups[d+1][groupname] = dfaces_in_physical_group - end - end - mesh = fe_mesh( - my_node_to_coords, - my_face_nodes, - my_face_reference_id, - my_reference_faces; - physical_faces = my_groups, - periodic_nodes,) - - if complexify - mesh, _ = GalerkinToolkit.complexify(mesh) - end - mesh -end - -function reference_face_from_gmsh_eltype(eltype) - if eltype == 1 - order = 1 - geom = unit_n_cube(Val(1)) - lib_to_gmsh = [1,2] - elseif eltype == 2 - order = 1 - geom = unit_simplex(Val(2)) - lib_to_gmsh = [1,2,3] - elseif eltype == 3 - order = 1 - geom = unit_n_cube(Val(2)) - lib_to_gmsh = [1,2,4,3] - elseif eltype == 4 - order = 1 - geom = unit_simplex(Val(3)) - lib_to_gmsh = [1,2,3,4] - elseif eltype == 5 - order = 1 - lib_to_gmsh = [1,2,4,3,5,6,8,7] - elseif eltype == 15 - order = 1 - geom = unit_n_cube(Val(0)) - lib_to_gmsh = [1] - elseif eltype == 8 - order = 2 - geom = unit_n_cube(Val(1)) - lib_to_gmsh = [1,3,2] - elseif eltype == 9 - order = 2 - geom = unit_simplex(Val(2)) - lib_to_gmsh = [1,4,2,6,5,3] - else - en, = gmsh.model.mesh.getElementProperties(eltype) - error("Unsupported element type. elemType: $eltype ($en)") - end - lagrangian_fe(geom,order;lib_to_user_nodes=lib_to_gmsh) -end - -function vtk_points(mesh) - function barrirer(coords) - nnodes = length(coords) - points = zeros(3,nnodes) - for node in 1:nnodes - coord = coords[node] - for i in 1:length(coord) - points[i,node] = coord[i] - end - end - points - end - coords = node_coordinates(mesh) - barrirer(coords) -end - -function vtk_cells(mesh,d) - function barrirer(face_to_refid,face_to_nodes,refid_mesh_cell) - cells = map(face_to_refid,face_to_nodes) do refid, nodes - mesh_cell = refid_mesh_cell[refid] - if mesh_cell === nothing - msg = """ - Not enough information to visualize this mesh via vtk: - vtk_mesh_cell returns nothing for the reference face in position $refid in dimension $d. - """ - error(msg) - end - mesh_cell(nodes) - end - cells - end - face_to_nodes = face_nodes(mesh,d) - face_to_refid = face_reference_id(mesh,d) - refid_refface = reference_faces(mesh,d) - refid_mesh_cell = map(vtk_mesh_cell,refid_refface) - barrirer(face_to_refid,face_to_nodes,refid_mesh_cell) -end - -""" - args = vtk_args(mesh,d) - -Return the arguments `args` to be passed in final position -to functions like `WriteVTK.vtk_grid`. -""" -function vtk_args(mesh,d) - points = vtk_points(mesh) - cells = vtk_cells(mesh,d) - points, cells -end - -function vtk_args(mesh) - points = vtk_points(mesh) - D = num_dims(mesh) - allcells = [vtk_cells(mesh,d) for d in 0:D if num_faces(mesh,d) != 0] - cells = reduce(vcat,allcells) - points, cells -end - -function vtk_physical_faces!(vtk,mesh,d;physical_faces=physical_faces(mesh,d)) - ndfaces = num_faces(mesh,d) - for group in physical_faces - name,faces = group - face_mask = zeros(Int,ndfaces) - face_mask[faces] .= 1 - vtk[name,WriteVTK.VTKCellData()] = face_mask - end - vtk -end - -function vtk_physical_faces!(vtk,mesh;physical_faces=physical_faces(mesh)) - nfaces = sum(num_faces(mesh)) - offsets = face_offsets(mesh) - D = num_dims(mesh) - data = Dict{String,Vector{Int}}() - for d in 0:D - for group in physical_faces[d+1] - name, = group - if !haskey(data,name) - face_mask = zeros(Int,nfaces) - data[name] = face_mask - end - end - end - for d in 0:D - for group in physical_faces[d+1] - offset = offsets[d+1] - name,faces = group - face_mask = data[name] - face_mask[faces.+offset] .= 1 - end - end - for (name,face_mask) in data - vtk[name,WriteVTK.VTKCellData()] = face_mask - end - vtk -end - -function vtk_mesh_cell(ref_face) - geom = geometry(ref_face) - d = num_dims(geom) - nnodes = num_nodes(ref_face) - lib_to_user = lib_to_user_nodes(ref_face) - if d == 0 && nnodes == 1 - cell_type = WriteVTK.VTKCellTypes.VTK_VERTEX - vtk_to_lib = [1] - elseif d == 1 && (is_simplex(geom) || is_n_cube(geom)) && nnodes == 2 - cell_type = WriteVTK.VTKCellTypes.VTK_LINE - vtk_to_lib = [1,2] - elseif d == 1 && (is_simplex(geom) || is_n_cube(geom)) && nnodes == 3 - cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_EDGE - vtk_to_lib = [1,3,2] - elseif d == 2 && is_n_cube(geom) && nnodes == 4 - cell_type = WriteVTK.VTKCellTypes.VTK_QUAD - vtk_to_lib = [1,2,4,3] - elseif d == 2 && is_simplex(geom) && nnodes == 3 - cell_type = WriteVTK.VTKCellTypes.VTK_TRIANGLE - vtk_to_lib = [1,2,3] - elseif d == 2 && is_simplex(geom) && nnodes == 6 - cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_TRIANGLE - vtk_to_lib = [1,3,6,2,5,4] - elseif d == 3 && is_n_cube(geom) && nnodes == 8 - cell_type = WriteVTK.VTKCellTypes.VTK_HEXAHEDRON - vtk_to_lib = [1,2,4,3,5,6,8,7] - elseif d == 3 && is_simplex(geom) && nnodes == 4 - cell_type = WriteVTK.VTKCellTypes.VTK_TETRA - vtk_to_lib = [1,2,3,4] - elseif d == 3 && is_simplex(geom) && nnodes == 10 - cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_TETRA - vtk_to_lib = [1,3,6,10,2,5,4,7,8,9] - else - return nothing - end - nodes -> WriteVTK.MeshCell(cell_type,(nodes[lib_to_user])[vtk_to_lib]) -end - -# TODO use the same convention than in Gridap -# allow the user to customize the boundary object ids -function boundary(geom::ExtrusionPolytope{0}) - nothing -end -function boundary(geom::ExtrusionPolytope{1}) - Tv = real_type(geom) - Ti = int_type(geom) - if is_unit_simplex(geom) - vertex = unit_simplex(0;real_type=Tv,int_type=Ti) - elseif is_unit_n_cube(geom) - vertex = unit_n_cube(0;real_type=Tv,int_type=Ti) - else - error("Not implemented") - end - order = 1 - fe = lagrangian_fe(vertex,order) - node_coordinates = SVector{1,Tv}[(0,),(1,)] - face_nodes = [Vector{Ti}[[1],[2]]] - face_reference_id = [Ti[1,1]] - reference_faces = ([fe],) - outwards_normals = SVector{1,Tv}[(-1,),(1,)] - fe_mesh( - node_coordinates, - face_nodes, - face_reference_id, - reference_faces; - outwards_normals - ) -end -function boundary(geom::ExtrusionPolytope{2}) - Tv = real_type(geom) - Ti = int_type(geom) - if is_unit_simplex(geom) - n1 = sqrt(2)/2 - g0 = unit_simplex(0;real_type=Tv,int_type=Ti) - g1 = unit_simplex(1;real_type=Tv,int_type=Ti) - node_coordinates = SVector{2,Tv}[(0,0),(1,0),(0,1)] - face_nodes = [Vector{Ti}[[1],[2],[3]],Vector{Ti}[[1,2],[1,3],[2,3]]] - face_reference_id = [Ti[1,1,1],Ti[1,1,1]] - outwards_normals = SVector{2,Tv}[(0,-1),(-1,0),(n1,n1)] - elseif is_unit_n_cube(geom) - g0 = unit_n_cube(0;real_type=Tv,int_type=Ti) - g1 = unit_n_cube(1;real_type=Tv,int_type=Ti) - node_coordinates = SVector{2,Tv}[(0,0),(1,0),(0,1),(1,1)] - face_nodes = [Vector{Ti}[[1],[2],[3],[4]],Vector{Ti}[[1,2],[3,4],[1,3],[2,4]]] - face_reference_id = [Ti[1,1,1,1],Ti[1,1,1,1]] - outwards_normals = SVector{2,Tv}[(0,-1),(0,1),(-1,0),(1,0)] - else - error("Not implemented") - end - order = 1 - fe0 = lagrangian_fe(g0,order) - fe1 = lagrangian_fe(g1,order) - reference_faces = ([fe0],[fe1]) - fe_mesh( - node_coordinates, - face_nodes, - face_reference_id, - reference_faces; - outwards_normals - ) -end -function boundary(geom::ExtrusionPolytope{3}) - Tv = real_type(geom) - Ti = int_type(geom) - if is_unit_simplex(geom) - g0 = unit_simplex(0;real_type=Tv,int_type=Ti) - g1 = unit_simplex(1;real_type=Tv,int_type=Ti) - g2 = unit_simplex(2;real_type=Tv,int_type=Ti) - node_coordinates = SVector{3,Tv}[(0,0,0),(1,0,0),(0,1,0),(0,0,1)] - face_nodes = [ - Vector{Ti}[[1],[2],[3],[4]], - Vector{Ti}[[1,2],[1,3],[2,3],[1,4],[2,4],[3,4]], - Vector{Ti}[[1,2,3],[1,2,4],[1,3,4],[2,3,4]], - ] - face_reference_id = [ones(Ti,4),ones(Ti,6),ones(Ti,4)] - n1 = sqrt(3)/3 - outwards_normals = SVector{3,Tv}[(0,0,-1),(0,-1,0),(-1,0,0),(n1,n1,n1)] - elseif is_unit_n_cube(geom) - g0 = unit_n_cube(0;real_type=Tv,int_type=Ti) - g1 = unit_n_cube(1;real_type=Tv,int_type=Ti) - g2 = unit_n_cube(2;real_type=Tv,int_type=Ti) - node_coordinates = SVector{3,Tv}[(0,0,0),(1,0,0),(0,1,0),(1,1,0),(0,0,1),(1,0,1),(0,1,1),(1,1,1)] - face_nodes = [ - Vector{Ti}[[1],[2],[3],[4],[5],[6],[7],[8]], - Vector{Ti}[[1,2],[3,4],[1,3],[2,4],[5,6],[7,8],[5,7],[6,8],[1,5],[3,7],[2,6],[4,8]], - Vector{Ti}[[1,2,3,4],[5,6,7,8],[1,2,5,6],[3,4,7,8],[1,3,5,7],[2,4,6,8]], - ] - face_reference_id = [ones(Ti,8),ones(Ti,12),ones(Ti,6)] - outwards_normals = SVector{3,Tv}[(0,0,-1),(0,0,1),(0,-1,0),(0,1,0),(-1,0,0),(1,0,0)] - else - error("Not implemented") - end - order = 1 - fe0 = lagrangian_fe(g0,order) - fe1 = lagrangian_fe(g1,order) - fe2 = lagrangian_fe(g2,order) - reference_faces = ([fe0,],[fe1],[fe2]) - fe_mesh( - node_coordinates, - face_nodes, - face_reference_id, - reference_faces; - outwards_normals - ) -end - -function boundary(refface::AbstractMeshFace) - boundary_from_mesh_face(refface) -end - -function boundary_from_mesh_face(refface) - geom = geometry(refface) - node_coordinates_inter = node_coordinates(refface) - order_inter = order(refface) - D = num_dims(geom) - if D == 0 - return nothing - end - mesh_geom = boundary(geom) - node_coordinates_geom = node_coordinates(mesh_geom) - face_nodes_inter = Vector{Vector{Vector{Int}}}(undef,D) - node_coordinates_aux = map(xi->map(xii->round(Int,order_inter*xii),xi),node_coordinates_inter) - ref_faces_geom = reference_faces(mesh_geom) - ref_faces = map(ref_faces_geom) do ref_faces_geom_d - map(r->lagrangian_fe(geometry(r),order_inter),ref_faces_geom_d) - end - face_ref_id_geom = face_reference_id(mesh_geom) - for d in 0:(D-1) - s_ref = map(ref_faces_geom[d+1],ref_faces[d+1]) do r_geom,r - m = num_nodes(r) - n = num_nodes(r_geom) - x = node_coordinates(r) - tabulator(r_geom)(value,x) - end - face_nodes_geom = face_nodes(mesh_geom,d) - nfaces = length(face_ref_id_geom[d+1]) - face_nodes_inter_d = Vector{Vector{Int}}(undef,nfaces) - for face in 1:nfaces - ref_id_geom = face_ref_id_geom[d+1][face] - s = s_ref[ref_id_geom] - nodes_geom = face_nodes_geom[face] - nnodes, nnodes_geom = size(s) - x_mapped = map(1:nnodes) do i - x = zero(eltype(node_coordinates_inter)) - for k in 1:nnodes_geom - x += node_coordinates_geom[nodes_geom[k]]*s[i,k] - end - map(xi->round(Int,order_inter*xi),x) - end - my_nodes = indexin(x_mapped,node_coordinates_aux) - face_nodes_inter_d[face] = my_nodes - end - face_nodes_inter[d+1] = face_nodes_inter_d - end - fe_mesh( - node_coordinates_inter, - face_nodes_inter, - face_ref_id_geom, - ref_faces) -end - -function interior_nodes(fe::AbstractMeshFace) - interior_nodes_from_mesh_face(fe) -end - -function interior_nodes_from_mesh_face(fe) - nnodes = num_nodes(fe) - D = num_dims(fe|>geometry) - if D == 0 - return collect(1:nnodes) - else - node_is_touched = fill(true,nnodes) - mesh = boundary(fe) - for d in 0:D - face_to_nodes = face_nodes(fe,d) - for nodes in face_to_nodes - node_is_touched[nodes] .= false - end - end - return findall(node_is_touched) - end -end - -function vertex_permutations(geom::AbstractFaceGeometry) - vertex_permutations_from_face_geometry(geom) -end - -## Admissible if the following map is admissible -# phi_i(x) = sum_i x_perm[i] * fun_i(x) -# This map sends vertex i to vertex perm[i] -function vertex_permutations_from_face_geometry(geo) - D = num_dims(geo) - if D == 0 - return [[1]] - end - geo_mesh = boundary(geo) - vertex_to_geo_nodes = face_nodes(geo_mesh,0) - vertex_to_geo_node = map(first,vertex_to_geo_nodes) - nvertices = length(vertex_to_geo_node) - # TODO compute this more lazily - # so that we never compute it for 3d - # since it is not needed - if D > 2 - return [collect(1:nvertices)] - end - permutations = Combinatorics.permutations(1:nvertices) - if is_simplex(geo) - return collect(permutations) - end - admissible_permutations = Vector{Int}[] - order = 1 - ref_face = lagrangian_fe(geo,order) - fun_mesh = boundary(ref_face) - geo_node_coords = node_coordinates(geo_mesh) - fun_node_coords = node_coordinates(fun_mesh) - vertex_coords = geo_node_coords[vertex_to_geo_node] - degree = 1 - quad = default_quadrature(geo,degree) - q = coordinates(quad) - Tx = eltype(vertex_coords) - TJ = typeof(zero(Tx)*zero(Tx)') - A = tabulator(ref_face)(ForwardDiff.gradient,q) - function compute_volume(vertex_coords) - vol = zero(eltype(TJ)) - for iq in 1:size(A,1) - J = zero(TJ) - for fun_node in 1:size(A,2) - vertex = fun_node # TODO we are assuming that the vertices and nodes match - g = A[iq,fun_node] - x = vertex_coords[vertex] - J += g*x' - end - vol += abs(det(J)) - end - vol - end - refvol = compute_volume(vertex_coords) - perm_vertex_coords = similar(vertex_coords) - for permutation in permutations - for (j,cj) in enumerate(permutation) - perm_vertex_coords[j] = vertex_coords[cj] - end - vol2 = compute_volume(perm_vertex_coords) - if (refvol + vol2) ≈ (2*refvol) - push!(admissible_permutations,permutation) - end - end - admissible_permutations -end - -function interior_node_permutations(fe::AbstractMeshFace) - interior_node_permutations_from_mesh_face(fe) -end - -function interior_node_permutations_from_mesh_face(refface) - interior_ho_nodes = interior_nodes(refface) - ho_nodes_coordinates = node_coordinates(refface) - geo = geometry(refface) - vertex_perms = vertex_permutations(geo) - if length(interior_ho_nodes) == 0 - return map(i->Int[],vertex_perms) - end - if length(vertex_perms) == 1 - return map(i->collect(1:length(interior_ho_nodes)),vertex_perms) - end - geo_mesh = boundary(geo) - vertex_to_geo_nodes = face_nodes(geo_mesh,0) - vertex_to_geo_node = map(first,vertex_to_geo_nodes) - ref_face = lagrangian_reference_face(geo) - fun_mesh = boundary(ref_face) - geo_node_coords = node_coordinates(geo_mesh) - fun_node_coords = node_coordinates(fun_mesh) - vertex_coords = geo_node_coords[vertex_to_geo_node] - q = ho_nodes_coordinates[interior_ho_nodes] - Tx = eltype(vertex_coords) - A = zeros(Float64,length(q),length(fun_node_coords)) - A = tabulator(ref_face)(value,q) - perm_vertex_coords = similar(vertex_coords) - node_perms = similar(vertex_perms) - for (iperm,permutation) in enumerate(vertex_perms) - for (j,cj) in enumerate(permutation) - perm_vertex_coords[j] = vertex_coords[cj] - end - node_to_pnode = fill(INVALID_ID,length(interior_ho_nodes)) - for iq in 1:size(A,1) - y = zero(Tx) - for fun_node in 1:size(A,2) - vertex = fun_node # TODO we are assuming that the vertices and nodes match - g = A[iq,fun_node] - x = perm_vertex_coords[vertex] - y += g*x - end - pnode = findfirst(i->(norm(i-y)+1)≈1,q) - if pnode != nothing - node_to_pnode[iq] = pnode - end - end - node_perms[iperm] = node_to_pnode - end - node_perms -end - -abstract type AbstractMeshTopology <: GalerkinToolkitDataType end - -struct GenericMeshTopology{A,B,C,D} <: AbstractMeshTopology - face_incidence::A - face_reference_id::B - face_permutation_ids::C - reference_faces::D -end - -function mesh_topology(args...) - GenericMeshTopology(args...) -end - -function topology(mesh::AbstractFEMesh) - topology_from_mesh(mesh) -end - -function topology_from_mesh(mesh) - # Assumes that the input is a cell complex - T = JaggedArray{Int32,Int32} - D = num_dims(mesh) - my_face_incidence = Matrix{T}(undef,D+1,D+1) - my_face_reference_id = [ face_reference_id(mesh,d) for d in 0:D ] - my_reference_faces = Tuple([ map(topology,reference_faces(mesh,d)) for d in 0:D ]) - my_face_permutation_ids = Matrix{T}(undef,D+1,D+1) - topo = mesh_topology( - my_face_incidence, - my_face_reference_id, - my_face_permutation_ids, - my_reference_faces, - ) - for d in 0:D - fill_face_interior_mesh_topology!(topo,mesh,d) - end - for d in 1:D - fill_face_vertices_mesh_topology!(topo,mesh,d) - fill_face_coboundary_mesh_topology!(topo,mesh,d,0) - end - for d in 1:(D-1) - for n in (D-d):-1:1 - m = n+d - fill_face_boundary_mesh_topology!(topo,mesh,m,n) - fill_face_coboundary_mesh_topology!(topo,mesh,m,n) - end - end - for d in 0:D - for n in 0:d - fill_face_permutation_ids!(topo,d,n) - end - end - topo -end - -function fill_face_interior_mesh_topology!(mesh_topology,mesh,d) - n = num_faces(mesh,d) - ptrs = collect(Int32,1:(n+1)) - data = collect(Int32,1:n) - mesh_topology.face_incidence[d+1,d+1] = JaggedArray(data,ptrs) -end - -function generate_face_coboundary(nface_to_mfaces,nmfaces) - ptrs = zeros(Int32,nmfaces+1) - nnfaces = length(nface_to_mfaces) - for nface in 1:nnfaces - mfaces = nface_to_mfaces[nface] - for mface in mfaces - ptrs[mface+1] += Int32(1) - end - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = zeros(Int32,ndata) - for nface in 1:nnfaces - mfaces = nface_to_mfaces[nface] - for mface in mfaces - p = ptrs[mface] - data[p] = nface - ptrs[mface] += Int32(1) - end - end - rewind_ptrs!(ptrs) - mface_to_nfaces = JaggedArray(data,ptrs) - mface_to_nfaces -end - -function fill_face_coboundary_mesh_topology!(topology,mesh,n,m) - nmfaces = num_faces(mesh,m) - nface_to_mfaces = face_incidence(topology,n,m) - face_incidence(topology)[m+1,n+1] = generate_face_coboundary(nface_to_mfaces,nmfaces) -end - -function fill_face_vertices_mesh_topology!(topo,mesh,d) - function barrier(nnodes,vertex_to_nodes,dface_to_nodes,dface_to_refid,refid_to_lvertex_to_lnodes) - vertex_to_node = JaggedArray(vertex_to_nodes).data - #if vertex_to_node == 1:nnodes - # return dface_to_nodes - #end - node_to_vertex = zeros(Int32,nnodes) - nvertices = length(vertex_to_nodes) - node_to_vertex[vertex_to_node] = 1:nvertices - ndfaces = length(dface_to_nodes) - dface_to_vertices_ptrs = zeros(Int32,ndfaces+1) - for dface in 1:ndfaces - refid = dface_to_refid[dface] - nlvertices = length(refid_to_lvertex_to_lnodes[refid]) - dface_to_vertices_ptrs[dface+1] = nlvertices - end - length_to_ptrs!(dface_to_vertices_ptrs) - ndata = dface_to_vertices_ptrs[end]-1 - dface_to_vertices_data = zeros(Int32,ndata) - for dface in 1:ndfaces - refid = dface_to_refid[dface] - lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] - nlvertices = length(lvertex_to_lnodes) - lnode_to_node = dface_to_nodes[dface] - offset = dface_to_vertices_ptrs[dface]-1 - for lvertex in 1:nlvertices - lnode = first(lvertex_to_lnodes[lvertex]) - node = lnode_to_node[lnode] - vertex = node_to_vertex[node] - dface_to_vertices_data[offset+lvertex] = vertex - end - end - dface_to_vertices = JaggedArray(dface_to_vertices_data,dface_to_vertices_ptrs) - end - nnodes = num_nodes(mesh) - vertex_to_nodes = face_nodes(mesh,0) - dface_to_nodes = face_nodes(mesh,d) - dface_to_refid = face_reference_id(mesh,d) - refid_refface = reference_faces(mesh,d) - refid_to_lvertex_to_lnodes = map(refface->face_nodes(boundary(refface),0),refid_refface) - face_incidence(topo)[d+1,0+1] = barrier(nnodes,vertex_to_nodes,dface_to_nodes,dface_to_refid,refid_to_lvertex_to_lnodes) -end - -function fill_face_boundary_mesh_topology!(topo,mesh,D,d) - function barrier( - Dface_to_vertices, - vertex_to_Dfaces, - dface_to_vertices, - vertex_to_dfaces, - Dface_to_refid, - Drefid_to_ldface_to_lvertices) - - # Count - ndfaces = length(dface_to_vertices) - nDfaces = length(Dface_to_vertices) - # Allocate output - ptrs = zeros(Int32,nDfaces+1) - for Dface in 1:nDfaces - Drefid = Dface_to_refid[Dface] - ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] - ptrs[Dface+1] = length(ldface_to_lvertices) - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = fill(Int32(INVALID_ID),ndata) - Dface_to_dfaces = JaggedArray(data,ptrs) - for Dface in 1:nDfaces - Drefid = Dface_to_refid[Dface] - ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] - lvertex_to_vertex = Dface_to_vertices[Dface] - ldface_to_dface = Dface_to_dfaces[Dface] - for (ldface,lvertices) in enumerate(ldface_to_lvertices) - # Find the global d-face for this local d-face - dface2 = Int32(INVALID_ID) - vertices = view(lvertex_to_vertex,lvertices) - for (i,lvertex) in enumerate(lvertices) - vertex = lvertex_to_vertex[lvertex] - dfaces = vertex_to_dfaces[vertex] - for dface1 in dfaces - vertices1 = dface_to_vertices[dface1] - if same_valid_ids(vertices,vertices1) - dface2 = dface1 - break - end - end - if dface2 != Int32(INVALID_ID) - break - end - end - @boundscheck begin - msg = """ - - - Error in: topology_from_mesh - - The given mesh is provably not a cell complex. - """ - @assert dface2 != Int32(INVALID_ID) msg - end - ldface_to_dface[ldface] = dface2 - end # (ldface,lvertices) - end # Dface - Dface_to_dfaces - end - Dface_to_vertices = face_incidence(topo,D,0) - vertex_to_Dfaces = face_incidence(topo,0,D) - dface_to_vertices = face_incidence(topo,d,0) - vertex_to_dfaces = face_incidence(topo,0,d) - Dface_to_refid = face_reference_id(topo,D) - refid_refface = reference_faces(topo,D) - Drefid_to_ldface_to_lvertices = map(refface->face_incidence(boundary(refface),d,0),refid_refface) - Dface_to_dfaces = barrier( - Dface_to_vertices, - vertex_to_Dfaces, - dface_to_vertices, - vertex_to_dfaces, - Dface_to_refid, - Drefid_to_ldface_to_lvertices) - topo.face_incidence[D+1,d+1] = Dface_to_dfaces -end - -function fill_face_permutation_ids!(top,D,d) - function barrier!( - cell_to_lface_to_pindex, - cell_to_lface_to_face, - cell_to_cvertex_to_vertex, - cell_to_ctype, - ctype_to_lface_to_cvertices, - face_to_fvertex_to_vertex, - face_to_ftype, - ftype_to_pindex_to_cfvertex_to_fvertex) - - ncells = length(cell_to_lface_to_face) - for cell in 1:ncells - ctype = cell_to_ctype[cell] - lface_to_cvertices = ctype_to_lface_to_cvertices[ctype] - a = cell_to_lface_to_face.ptrs[cell]-1 - c = cell_to_cvertex_to_vertex.ptrs[cell]-1 - for (lface,cfvertex_to_cvertex) in enumerate(lface_to_cvertices) - face = cell_to_lface_to_face.data[a+lface] - ftype = face_to_ftype[face] - b = face_to_fvertex_to_vertex.ptrs[face]-1 - pindex_to_cfvertex_to_fvertex = ftype_to_pindex_to_cfvertex_to_fvertex[ftype] - pindexfound = false - for (pindex, cfvertex_to_fvertex) in enumerate(pindex_to_cfvertex_to_fvertex) - found = true - for (cfvertex,fvertex) in enumerate(cfvertex_to_fvertex) - vertex1 = face_to_fvertex_to_vertex.data[b+fvertex] - cvertex = cfvertex_to_cvertex[cfvertex] - vertex2 = cell_to_cvertex_to_vertex.data[c+cvertex] - if vertex1 != vertex2 - found = false - break - end - end - if found - cell_to_lface_to_pindex.data[a+lface] = pindex - pindexfound = true - break - end - end - @assert pindexfound "Valid pindex not found" - end - end - end - @assert D >= d - cell_to_lface_to_face = JaggedArray(face_incidence(top,D,d)) - data = similar(cell_to_lface_to_face.data,Int8) - ptrs = cell_to_lface_to_face.ptrs - cell_to_lface_to_pindex = JaggedArray(data,ptrs) - if d == D || d == 0 - fill!(cell_to_lface_to_pindex.data,Int8(1)) - top.face_permutation_ids[D+1,d+1] = cell_to_lface_to_pindex - return top - end - face_to_fvertex_to_vertex = JaggedArray(face_incidence(top,d,0)) - face_to_ftype = face_reference_id(top,d) - ref_dfaces = reference_faces(top,d) - ftype_to_pindex_to_cfvertex_to_fvertex = map(vertex_permutations,ref_dfaces) - cell_to_cvertex_to_vertex = JaggedArray(face_incidence(top,D,0)) - cell_to_ctype = face_reference_id(top,D) - ref_Dfaces = reference_faces(top,D) - ctype_to_lface_to_cvertices = map(p->face_incidence(boundary(p),d,0),ref_Dfaces) - barrier!( - cell_to_lface_to_pindex, - cell_to_lface_to_face, - cell_to_cvertex_to_vertex, - cell_to_ctype, - ctype_to_lface_to_cvertices, - face_to_fvertex_to_vertex, - face_to_ftype, - ftype_to_pindex_to_cfvertex_to_fvertex) - top.face_permutation_ids[D+1,d+1] = cell_to_lface_to_pindex - top -end - -function intersection!(a,b,na,nb) - function findeq!(i,a,b,nb) - for j in 1:nb - if a[i] == b[j] - return - end - end - a[i] = INVALID_ID - return - end - for i in 1:na - if a[i] == INVALID_ID - continue - end - findeq!(i,a,b,nb) - end -end - -function same_valid_ids(a,b) - function is_subset(a,b) - for i in 1:length(a) - v = a[i] - if v == INVALID_ID - continue - end - c = find_eq(v,b) - if c == false; return false; end - end - return true - end - function find_eq(v,b) - for vs in b - if v == vs - return true - end - end - return false - end - c = is_subset(a,b) - if c == false; return false; end - c = is_subset(b,a) - if c == false; return false; end - return true -end - -## TODO AbstractFaceTopology <: AbstractMeshTopology -abstract type AbstractFaceTopology <: GalerkinToolkitDataType end - -struct GenericFaceTopology{A,B} <: AbstractFaceTopology - boundary::A - vertex_permutations::B -end - -function face_topology(args...) - GenericFaceTopology(args...) -end - -num_dims(a::AbstractFaceTopology) = num_dims(boundary(a))+1 - -function topology(fe::AbstractMeshFace) - topology_from_mesh_face(fe) -end - -function topology_from_mesh_face(refface) - geom = geometry(refface) - D = num_dims(geom) - if D != 0 - myboundary = geom |> boundary |> topology - else - myboundary = nothing - end - myperms = vertex_permutations(geom) - face_topology(myboundary,myperms) -end - -function complexify(mesh::AbstractFEMesh) - complexify_mesh(mesh) -end - -function complexify_mesh(mesh) - Ti = Int32 - T = JaggedArray{Ti,Ti} - D = num_dims(mesh) - oldface_to_newvertices = Vector{T}(undef,D+1) - newvertex_to_oldfaces = Vector{T}(undef,D+1) - newface_incidence = Matrix{T}(undef,D+1,D+1) - nnewfaces = zeros(Int,D+1) - newface_refid = Vector{Vector{Ti}}(undef,D+1) - newreffaces = Vector{Any}(undef,D+1) - newface_nodes = Vector{T}(undef,D+1) - old_to_new = Vector{Vector{Ti}}(undef,D+1) - node_to_newvertex, n_new_vertices = find_node_to_vertex(mesh) # Optimizable for linear meshes - for d in 0:D - oldface_to_newvertices[d+1] = fill_face_vertices(mesh,d,node_to_newvertex) # Optimizable for linear meshes - newvertex_to_oldfaces[d+1] = generate_face_coboundary(oldface_to_newvertices[d+1],n_new_vertices) # Optimizable for linear meshes - end - newface_incidence[D+1,0+1] = oldface_to_newvertices[D+1] - newface_incidence[0+1,D+1] = newvertex_to_oldfaces[D+1] - nnewfaces[D+1] = length(oldface_to_newvertices[D+1]) - newface_refid[D+1] = face_reference_id(mesh,D) - newreffaces[D+1] = reference_faces(mesh,D) - newface_nodes[D+1] = face_nodes(mesh,D) - old_to_new[D+1] = collect(Ti,1:length(newface_nodes[D+1])) - # TODO optimize for d==0 - for d in (D-1):-1:0 - n = d+1 - new_nface_to_new_vertices = newface_incidence[n+1,0+1] - new_vertex_to_new_nfaces = newface_incidence[0+1,n+1] - old_dface_to_new_vertices = oldface_to_newvertices[d+1] - new_vertex_to_old_dfaces = newvertex_to_oldfaces[d+1] - new_nface_to_nrefid = newface_refid[n+1] - old_dface_to_drefid = face_reference_id(mesh,d) - drefid_to_ref_dface = reference_faces(mesh,d) - old_dface_to_nodes = face_nodes(mesh,d) - new_nface_to_nodes = newface_nodes[n+1] - nrefid_to_ldface_to_lvertices = map(a->face_incidence(topology(boundary(geometry(a))),d,0),newreffaces[n+1]) - nrefid_to_ldface_to_lnodes = map(a->face_nodes(boundary(a),d),newreffaces[n+1]) - nrefid_to_ldface_to_drefrefid = map(a->face_reference_id(boundary(a),d),newreffaces[n+1]) - nrefid_to_drefrefid_to_ref_dface = map(a->reference_faces(boundary(a),d),newreffaces[n+1]) - new_nface_to_new_dfaces, n_new_dfaces, old_dface_to_new_dface = generate_face_boundary( - new_nface_to_new_vertices, - new_vertex_to_new_nfaces, - old_dface_to_new_vertices, - new_vertex_to_old_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_lvertices) - new_dface_to_new_vertices = generate_face_vertices( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_new_vertices, - new_nface_to_new_vertices, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_lvertices) - new_vertex_to_new_dfaces = generate_face_coboundary(new_dface_to_new_vertices,n_new_vertices) - new_dface_to_nodes = generate_face_vertices( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_nodes, - new_nface_to_nodes, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_lnodes) - new_dface_to_new_drefid, new_refid_to_ref_dface = generate_reference_faces( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_drefid, - drefid_to_ref_dface, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_drefrefid, - nrefid_to_drefrefid_to_ref_dface) - newface_incidence[n+1,d+1] = new_nface_to_new_dfaces - newface_incidence[d+1,0+1] = new_dface_to_new_vertices - newface_incidence[0+1,d+1] = new_vertex_to_new_dfaces - newface_refid[d+1] = new_dface_to_new_drefid - newreffaces[d+1] = new_refid_to_ref_dface - nnewfaces[d+1] = n_new_dfaces - newface_nodes[d+1] = new_dface_to_nodes - old_to_new[d+1] = old_dface_to_new_dface - end - node_to_coords = node_coordinates(mesh) - old_physical_faces = physical_faces(mesh) - new_physical_faces = [ Dict{String,Vector{Int32}}() for d in 0:D] # TODO hardcoded - for d in 0:D - old_groups = old_physical_faces[d+1] - for (group_name,old_group_faces) in old_groups - new_group_faces = similar(old_group_faces) - new_group_faces .= old_to_new[d+1][old_group_faces] - new_physical_faces[d+1][group_name] = new_group_faces - end - end - new_mesh = fe_mesh( - node_to_coords, - newface_nodes, - newface_refid, - Tuple(newreffaces); - physical_faces = new_physical_faces, - periodic_nodes = periodic_nodes(mesh), - outwards_normals = outwards_normals(mesh) - ) - new_mesh, old_to_new -end - -function generate_face_vertices( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_new_vertices, - new_nface_to_new_vertices, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_lvertices - ) - - Ti = eltype(eltype(old_dface_to_new_vertices)) - new_dface_to_touched = fill(false,n_new_dfaces) - new_dface_to_new_vertices_ptrs = zeros(Ti,n_new_dfaces+1) - n_old_dfaces = length(old_dface_to_new_dface) - for old_dface in 1:n_old_dfaces - new_dface = old_dface_to_new_dface[old_dface] - new_vertices = old_dface_to_new_vertices[old_dface] - new_dface_to_new_vertices_ptrs[new_dface+1] = length(new_vertices) - new_dface_to_touched[new_dface] = true - end - n_new_nfaces = length(new_nface_to_new_vertices) - for new_nface in 1:n_new_nfaces - nrefid = new_nface_to_nrefid[new_nface] - ldface_to_lvertices = nrefid_to_ldface_to_lvertices[nrefid] - ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] - n_ldfaces = length(ldface_to_new_dface) - for ldface in 1:n_ldfaces - new_dface = ldface_to_new_dface[ldface] - if new_dface_to_touched[new_dface] - continue - end - lvertices = ldface_to_lvertices[ldface] - new_dface_to_new_vertices_ptrs[new_dface+1] = length(lvertices) - new_dface_to_touched[new_dface] = true - end - - end - length_to_ptrs!(new_dface_to_new_vertices_ptrs) - ndata = new_dface_to_new_vertices_ptrs[end]-1 - new_dface_to_new_vertices_data = zeros(Ti,ndata) - new_dface_to_new_vertices = JaggedArray(new_dface_to_new_vertices_data,new_dface_to_new_vertices_ptrs) - fill!(new_dface_to_touched,false) - for old_dface in 1:n_old_dfaces - new_dface = old_dface_to_new_dface[old_dface] - new_vertices_in = old_dface_to_new_vertices[old_dface] - new_vertices_out = new_dface_to_new_vertices[new_dface] - for i in 1:length(new_vertices_in) - new_vertices_out[i] = new_vertices_in[i] - end - new_dface_to_touched[new_dface] = true - end - for new_nface in 1:n_new_nfaces - nrefid = new_nface_to_nrefid[new_nface] - ldface_to_lvertices = nrefid_to_ldface_to_lvertices[nrefid] - ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] - n_ldfaces = length(ldface_to_new_dface) - new_vertices_in = new_nface_to_new_vertices[new_nface] - for ldface in 1:n_ldfaces - new_dface = ldface_to_new_dface[ldface] - if new_dface_to_touched[new_dface] - continue - end - new_vertices_out = new_dface_to_new_vertices[new_dface] - lvertices = ldface_to_lvertices[ldface] - for i in 1:length(lvertices) - new_vertices_out[i] = new_vertices_in[lvertices[i]] - end - new_dface_to_touched[new_dface] = true - end - - end - new_dface_to_new_vertices -end - -function generate_reference_faces( - n_new_dfaces, - old_dface_to_new_dface, - old_dface_to_drefid, - drefid_to_ref_dface, - new_nface_to_new_dfaces, - new_nface_to_nrefid, - nrefid_to_ldface_to_drefrefid, - nrefid_to_drefrefid_to_ref_dface) - - i_to_ref_dface = collect(Any,drefid_to_ref_dface) - drefid_to_i = collect(1:length(drefid_to_ref_dface)) - i = length(i_to_ref_dface) - Ti = Int32 - nrefid_to_drefrefid_to_i = map(a->zeros(Ti,length(a)),nrefid_to_drefrefid_to_ref_dface) - for (nrefid,drefrefid_to_ref_dface) in enumerate(nrefid_to_drefrefid_to_ref_dface) - for (drefrefid, ref_dface) in enumerate(drefrefid_to_ref_dface) - push!(i_to_ref_dface,ref_dface) - i += 1 - nrefid_to_drefrefid_to_i[nrefid][drefrefid] = i - end - end - u_to_ref_dface = unique(i_to_ref_dface) - i_to_u = indexin(i_to_ref_dface,u_to_ref_dface) - new_dface_to_u = zeros(Ti,n_new_dfaces) - new_dface_to_touched = fill(false,n_new_dfaces) - for (old_dface,new_dface) in enumerate(old_dface_to_new_dface) - drefid = old_dface_to_drefid[old_dface] - i = drefid_to_i[drefid] - u = i_to_u[i] - new_dface_to_u[new_dface] = u - new_dface_to_touched[new_dface] = true - end - for (new_nface,nrefid) in enumerate(new_nface_to_nrefid) - ldface_to_drefrefid = nrefid_to_ldface_to_drefrefid[nrefid] - ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] - drefrefid_to_i = nrefid_to_drefrefid_to_i[nrefid] - for (ldface,new_dface) in enumerate(ldface_to_new_dface) - new_dface = ldface_to_new_dface[ldface] - if new_dface_to_touched[new_dface] - continue - end - drefrefid = ldface_to_drefrefid[ldface] - i = drefrefid_to_i[drefrefid] - u = i_to_u[i] - new_dface_to_u[new_dface] = u - new_dface_to_touched[new_dface] = true - end - end - new_dface_to_u, Tuple(u_to_ref_dface) -end - -function generate_face_boundary( - Dface_to_vertices, - vertex_to_Dfaces, - dface_to_vertices, - vertex_to_dfaces, - Dface_to_refid, - Drefid_to_ldface_to_lvertices) - - # Count - ndfaces = length(dface_to_vertices) - nDfaces = length(Dface_to_vertices) - nvertices = length(vertex_to_Dfaces) - maxldfaces = 0 - for ldface_to_lvertices in Drefid_to_ldface_to_lvertices - maxldfaces = max(maxldfaces,length(ldface_to_lvertices)) - end - maxDfaces = 0 - for vertex in 1:length(vertex_to_Dfaces) - Dfaces = vertex_to_Dfaces[vertex] - maxDfaces = max(maxDfaces,length(Dfaces)) - end - # Allocate output - ptrs = zeros(Int32,nDfaces+1) - for Dface in 1:nDfaces - Drefid = Dface_to_refid[Dface] - ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] - ptrs[Dface+1] = length(ldface_to_lvertices) - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = fill(Int32(INVALID_ID),ndata) - Dface_to_dfaces = GenericJaggedArray(data,ptrs) - # Main loop - Dfaces1 = fill(Int32(INVALID_ID),maxDfaces) - Dfaces2 = fill(Int32(INVALID_ID),maxDfaces) - ldfaces1 = fill(Int32(INVALID_ID),maxDfaces) - nDfaces1 = 0 - nDfaces2 = 0 - newdface = Int32(ndfaces) - old_to_new = collect(Int32,1:ndfaces) - for Dface in 1:nDfaces - Drefid = Dface_to_refid[Dface] - ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] - lvertex_to_vertex = Dface_to_vertices[Dface] - ldface_to_dface = Dface_to_dfaces[Dface] - for (ldface,lvertices) in enumerate(ldface_to_lvertices) - # Do nothing if this local face has already been processed by - # a neighbor - if ldface_to_dface[ldface] != Int32(INVALID_ID) - continue - end - # Find if there is already a global d-face for this local d-face - # if yes, then use the global id of this d-face - # if not, create a new one - dface2 = Int32(INVALID_ID) - fill!(Dfaces1,Int32(INVALID_ID)) - fill!(Dfaces2,Int32(INVALID_ID)) - vertices = view(lvertex_to_vertex,lvertices) - for (i,lvertex) in enumerate(lvertices) - vertex = lvertex_to_vertex[lvertex] - dfaces = vertex_to_dfaces[vertex] - for dface1 in dfaces - vertices1 = dface_to_vertices[dface1] - if same_valid_ids(vertices,vertices1) - dface2 = dface1 - break - end - end - if dface2 != Int32(INVALID_ID) - break - end - end - if dface2 == Int32(INVALID_ID) - newdface += Int32(1) - dface2 = newdface - end - # Find all D-faces around this local d-face - for (i,lvertex) in enumerate(lvertices) - vertex = lvertex_to_vertex[lvertex] - Dfaces = vertex_to_Dfaces[vertex] - if i == 1 - copyto!(Dfaces1,Dfaces) - nDfaces1 = length(Dfaces) - else - copyto!(Dfaces2,Dfaces) - nDfaces2 = length(Dfaces) - intersection!(Dfaces1,Dfaces2,nDfaces1,nDfaces2) - end - end - # Find their correspondent local d-face and set the d-face - for Dface1 in Dfaces1 - if Dface1 != INVALID_ID - Drefid1 = Dface_to_refid[Dface1] - lvertex1_to_vertex1 = Dface_to_vertices[Dface1] - ldface1_to_lvertices1 = Drefid_to_ldface_to_lvertices[Drefid1] - ldface2 = Int32(INVALID_ID) - for (ldface1,lvertices1) in enumerate(ldface1_to_lvertices1) - vertices1 = view(lvertex1_to_vertex1,lvertices1) - if same_valid_ids(vertices,vertices1) - ldface2 = ldface1 - break - end - end - @boundscheck @assert ldface2 != INVALID_ID - Dface_to_dfaces[Dface1][ldface2] = dface2 - end - end - end # (ldface,lvertices) - end # Dface - Dface_to_dfaces, newdface, old_to_new -end - -function fill_face_vertices(mesh,d,node_to_vertex) - function barrier(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) - Ti = eltype(node_to_vertex) - nfaces = length(face_to_nodes) - face_to_vertices_ptrs = zeros(Ti,nfaces+1) - for face in 1:nfaces - nodes = face_to_nodes[face] - refid = face_to_refid[face] - lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] - nlvertices = length(lvertex_to_lnodes) - face_to_vertices_ptrs[face+1] = nlvertices - end - length_to_ptrs!(face_to_vertices_ptrs) - ndata = face_to_vertices_ptrs[end]-1 - face_to_vertices_data = zeros(Ti,ndata) - face_to_vertices = JaggedArray(face_to_vertices_data,face_to_vertices_ptrs) - for face in 1:nfaces - vertices = face_to_vertices[face] - nodes = face_to_nodes[face] - refid = face_to_refid[face] - lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] - for (lvertex,lnodes) in enumerate(lvertex_to_lnodes) - lnode = first(lnodes) - vertex = node_to_vertex[nodes[lnode]] - @boundscheck @assert vertex != INVALID_ID - vertices[lvertex] = vertex - end - end - face_to_vertices - end - face_to_nodes = face_nodes(mesh,d) - face_to_refid = face_reference_id(mesh,d) - refid_to_lvertex_to_lnodes = map(reference_faces(mesh,d)) do a - if num_dims(geometry(a)) != 0 - face_nodes(boundary(a),0) - else - [interior_nodes(a)] - end - end - barrier(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) -end - -function find_node_to_vertex(mesh) - function barrier!(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) - valid_id = one(eltype(node_to_vertex)) - for face in eachindex(face_to_nodes) - nodes = face_to_nodes[face] - refid = face_to_refid[face] - lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] - for lnodes in lvertex_to_lnodes - lnode = first(lnodes) - node = nodes[lnode] - node_to_vertex[node] = valid_id - end - end - end - Ti = Int32 - nnodes = num_nodes(mesh) - node_to_vertex = zeros(Ti,nnodes) - fill!(node_to_vertex,Ti(INVALID_ID)) - D = num_dims(mesh) - for d in 0:D - face_to_nodes = face_nodes(mesh,d) - if length(face_to_nodes) == 0 - continue - end - face_to_refid = face_reference_id(mesh,d) - refid_to_lvertex_to_lnodes = map(reference_faces(mesh,d)) do a - if num_dims(geometry(a)) != 0 - face_nodes(boundary(a),0) - else - [interior_nodes(a)] - end - end - barrier!(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) - end - vertex = Ti(0) - for node in eachindex(node_to_vertex) - if node_to_vertex[node] != Ti(INVALID_ID) - vertex += Ti(1) - node_to_vertex[node] = vertex - end - end - node_to_vertex, vertex -end - -function physical_nodes(mesh,d) - nnodes = num_nodes(mesh) - node_to_touched = fill(false,nnodes) - node_groups = Dict{String,Vector{Int32}}() - face_to_nodes = face_nodes(mesh,d) - for (name,faces) in physical_faces(mesh,d) - fill!(node_to_touched,false) - for face in faces - nodes = face_to_nodes[face] - node_to_touched[nodes] .= true - end - node_groups[name] = findall(node_to_touched) - end - node_groups -end - -function physical_nodes(mesh; - merge_dims=Val(false), - disjoint=Val(false), - name_priority=nothing) - - if val_parameter(disjoint) == true && val_parameter(merge_dims) == false - error("disjoint=true requires merge_dims=true") - end - D = num_dims(mesh) - d_to_groups = [ physical_nodes(mesh,d) for d in 0:D ] - if val_parameter(merge_dims) == false - return d_to_groups - end - names = physical_names(mesh;merge_dims) - nnodes = num_nodes(mesh) - node_groups = Dict{String,Vector{Int32}}() - - if val_parameter(disjoint) == false - node_to_touched = fill(false,nnodes) - for name in names - fill!(node_to_touched,false) - for groups in d_to_groups - for (name2,nodes) in groups - if name != name2 - continue - end - node_to_touched[nodes] .= true - end - end - node_groups[name] = findall(node_to_touched) - end - else - if name_priority === nothing - tag_to_name = sort(collect(names)) - else - tag_to_name = name_priority - end - node_to_tag = zeros(Int32,nnodes) - classify_mesh_nodes!(node_to_tag,mesh,tag_to_name) - for (tag,name) in enumerate(tag_to_name) - node_groups[name] = findall(t->t==tag,node_to_tag) - end - end - node_groups -end - -function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) - fill!(node_to_tag,zero(eltype(node_to_tag))) - for d in dmax:-1:0 - face_to_nodes = face_nodes(mesh,d) - face_groups = physical_faces(mesh,d) - for (tag,name) in enumerate(tag_to_name) - for (name2,faces) in face_groups - if name != name2 - continue - end - for face in faces - nodes = face_to_nodes[face] - node_to_tag[nodes] .= tag - end - end - end - end - node_to_tag -end - -function physical_names(mesh,d) - groups = physical_faces(mesh,d) - Set(keys(groups)) -end - -function physical_names(mesh;merge_dims=Val(false)) - D = num_dims(mesh) - d_to_names = [ physical_names(mesh,d) for d in 0:D] - if val_parameter(merge_dims) == false - return d_to_names - end - reduce(union,d_to_names) -end - -abstract type AbstractFEChain <: GalerkinToolkitDataType end - -struct GenericFEChain{A,B,C,D,E,F,G} <: AbstractFEChain - node_coordinates::A - face_nodes::B - face_reference_id::C - reference_faces::D - periodic_nodes::E - physical_faces::F - outwards_normals::G -end - -function fe_chain(args...) - GenericFEChain(args...) -end - -function fe_chain( - node_coordinates, - face_nodes, - face_reference_id, - reference_faces; - periodic_nodes = eltype(eltype(face_reference_id))[], - physical_faces = Dict{String,Vector{eltype(eltype(face_reference_id))}}(), - outwards_normals = nothing - ) - fe_chain( - node_coordinates, - face_nodes, - face_reference_id, - reference_faces, - periodic_nodes, - physical_faces, - outwards_normals) -end - -num_dims(mesh::AbstractFEChain) = num_dims(first(reference_faces(mesh))) - -function fe_mesh(chain::AbstractFEChain) - mesh_from_chain(chain) -end - -function mesh_from_chain(chain) - D = num_dims(chain) - cell_nodes = face_nodes(chain) - cell_reference_id = face_reference_id(chain) - reference_cells = reference_faces(chain) - node_coords = node_coordinates(chain) - face_to_nodes = Vector{typeof(cell_nodes)}(undef,D+1) - face_to_refid = Vector{typeof(cell_reference_id)}(undef,D+1) - for d in 0:D-1 - face_to_nodes[d+1] = Vector{Int}[] - face_to_refid[d+1] = Int[] - end - face_to_nodes[end] = cell_nodes - face_to_refid[end] = cell_reference_id - ref_cell = first(reference_cells) - ref_faces = reference_faces(boundary(ref_cell)) - refid_to_refface = push(ref_faces,reference_cells) - cell_groups = physical_faces(chain) - groups = [ typeof(cell_groups)() for d in 0:D] - groups[end] = cell_groups - pnodes = periodic_nodes(chain) - onormals = outwards_normals(chain) - fe_mesh( - node_coords, - face_to_nodes, - face_to_refid, - refid_to_refface; - periodic_nodes = pnodes, - physical_faces = groups, - outwards_normals = onormals) -end - -function simplexify(geo::AbstractFaceGeometry) - simplexify_face_geometry(geo) -end - -function simplexify_face_geometry(geo) - if is_unit_simplex(geo) - simplexify_unit_simplex(geo) - elseif is_unit_n_cube(geo) - simplexify_unit_n_cube(geo) - else - error("case not implemented") - end -end - -function simplexify_unit_simplex(geo) - @assert is_unit_simplex(geo) - refface = lagrangian_reference_face(geo) - mesh_from_reference_face(refface) -end - -function simplexify_unit_n_cube(geo) - @assert is_unit_n_cube(geo) - D = num_dims(geo) - if D in (0,1) - return simplexify_unit_simplex(geo) - end - simplex = unit_simplex(Val(D)) - order = 1 - ref_cell = lagrangian_fe(simplex,order) - node_coords = node_coordinates(boundary(geo)) - cell_nodes = simplex_node_ids_n_cube(geo) - ncells = length(cell_nodes) - cell_reference_id = fill(Int8(1),ncells) - reference_cells = [ref_cell] - chain = fe_chain( - node_coords, - cell_nodes, - cell_reference_id, - reference_cells, - ) - mesh = mesh_from_chain(chain) - mesh_complex, = complexify(mesh) - groups = [Dict{String,Vector{Int32}}() for d in 0:D] - for d in 0:(D-1) - sface_to_nodes = face_nodes(mesh_complex,d) - cface_to_nodes = face_nodes(boundary(geo),d) - nsfaces = length(sface_to_nodes) - ncfaces = length(cface_to_nodes) - sface_touched = fill(false,nsfaces) - for cface in 1:ncfaces - fill!(sface_touched,false) - nodes_c = cface_to_nodes[cface] - for sface in 1:nsfaces - nodes_s = sface_to_nodes[sface] - if all(map(n->n in nodes_c,nodes_s)) - sface_touched[sface] = true - end - end - sfaces_in_group = findall(sface_touched) - group_name = "$d-face-$cface" - groups[d+1][group_name] = sfaces_in_group - end - end - groups[end]["interior"] = 1:(num_faces(mesh_complex,D)) - sface_is_boundary = fill(false,num_faces(mesh_complex,D-1)) - for (_,sfaces) in groups[end-1] - sface_is_boundary[sfaces] .= true - end - groups[end-1]["boundary"] = findall(sface_is_boundary) - physical_faces(mesh_complex) .= groups - mesh_complex -end - -function simplex_node_ids_n_cube(geo) - D = num_dims(geo) - # TODO check orientation of nodes - # this assumes lexicographic ordering - # for 3d nodes ids are carefully selected - # such that opposite faces match. - # This allows one to complexify meshes - # of ncubes with oriented faces - if D == 0 - [[1]] - elseif D == 1 - [[1,2]] - elseif D == 2 - [[1,2,3],[2,3,4]] - elseif D ==3 - [[1,2,3,7], [1,2,5,7], [2,3,4,7], - [2,4,7,8], [2,5,6,7], [2,6,7,8]] - else - error("case not implemented") - end -end - -function simplexify_reference_face(ref_face) - mesh_geom = simplexify(geometry(ref_face)) - D = num_dims(mesh_geom) - node_coordinates_geom = node_coordinates(mesh_geom) - ref_faces_geom = reference_faces(mesh_geom,D) - face_nodes_geom = face_nodes(mesh_geom,D) - face_ref_id_geom = face_reference_id(mesh_geom,D) - nfaces = length(face_ref_id_geom) - # We need the same order in all directions - # for this to make sense - my_order = order(ref_face) - ref_faces_inter = map(r_geom->lagrangian_reference_face(geometry(r_geom),order=my_order),ref_faces_geom) - s_ref = map(ref_faces_geom,ref_faces_inter) do r_geom,r - m = num_nodes(r) - n = num_nodes(r_geom) - x = node_coordinates(r) - tabulator(r_geom)(value,x) - end - node_coordinates_inter = node_coordinates(ref_face) - node_coordinates_aux = map(xi->map(xii->round(Int,my_order*xii),xi),node_coordinates_inter) - face_nodes_inter = Vector{Vector{Int}}(undef,nfaces) - for face in 1:nfaces - ref_id_geom = face_ref_id_geom[face] - s = s_ref[ref_id_geom] - nodes_geom = face_nodes_geom[face] - nnodes, nnodes_geom = size(s) - x_mapped = map(1:nnodes) do i - x = zero(eltype(node_coordinates_inter)) - for k in 1:nnodes_geom - x += node_coordinates_geom[nodes_geom[k]]*s[i,k] - end - map(xi->round(Int,my_order*xi),x) - end - my_nodes = indexin(x_mapped,node_coordinates_aux) - face_nodes_inter[face] = my_nodes - end - ref_inter = - chain = Chain(; - num_dims=Val(D), - node_coordinates=node_coordinates_inter, - face_nodes = face_nodes_inter, - face_reference_id = face_ref_id_geom, - reference_faces = ref_faces_inter, - ) - mesh = mesh_from_chain(chain) - mesh_complex, = complexify(mesh) - pg = physical_faces(mesh_complex) - pg .= physical_faces(mesh_geom) - mesh_complex -end - -function cartesian_mesh(domain,cells_per_dir;boundary=true,complexify=true,simplexify=false) - mesh = if boundary - if simplexify - structured_simplex_mesh_with_boundary(domain,cells_per_dir) - else - cartesian_mesh_with_boundary(domain,cells_per_dir) - end - else - if simplexify - chain = structured_simplex_chain(domain,cells_per_dir) - else - chain = cartesian_chain(domain,cells_per_dir) - end - mesh_from_chain(chain) - end - if complexify - mesh, = GalerkinToolkit.complexify(mesh) - end - mesh -end - -function bounding_box_from_domain(domain) - l = length(domain) - D = div(l,2) - pmin = SVector(ntuple(d->domain[2*(d-1)+1],Val(D))) - pmax = SVector(ntuple(d->domain[2*d],Val(D))) - (pmin,pmax) -end - -function domain_from_bounding_box(box) - l = sum(length,box) - ntuple(Val(l)) do i - vector = mod(i-1,2)+1 - component = div(i-1,2)+1 - box[vector][component] - end -end - -function cartesian_mesh_with_boundary(domain,cells_per_dir) - function barrier( - D, - cell_to_nodes, - nnodes, - d_to_ldface_to_lnodes) - - node_to_n = zeros(Int32,nnodes) - for nodes in cell_to_nodes - for node in nodes - node_to_n[node] += Int32(1) - end - end - J = typeof(JaggedArray(Vector{Int32}[])) - face_to_nodes = Vector{J}(undef,D) - groups = [ Dict{String,Vector{Int32}}() for d in 0:(D-1) ] - ngroups = 0 - for d in 0:(D-1) - nmax = 2^d - ldface_to_lnodes = d_to_ldface_to_lnodes[d+1] - ndfaces = 0 - for nodes in cell_to_nodes - for (ldface,lnodes) in enumerate(ldface_to_lnodes) - isboundary = true - for lnode in lnodes - node = nodes[lnode] - if node_to_n[node] > nmax - isboundary = false - break - end - end - if isboundary - ndfaces += 1 - end - end - end - ptrs = zeros(Int32,ndfaces+1) - for dface in 1:ndfaces - ptrs[dface+1] += Int32(nmax) - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = zeros(Int32,ndata) - dface_to_physical_group = zeros(Int32,ndfaces) - ndfaces = 0 - for nodes in cell_to_nodes - for (ldface,lnodes) in enumerate(ldface_to_lnodes) - isboundary = true - for lnode in lnodes - node = nodes[lnode] - if node_to_n[node] > nmax - isboundary = false - break - end - end - if isboundary - ndfaces += 1 - group = ngroups + ldface - dface_to_physical_group[ndfaces] = group - p = ptrs[ndfaces]-Int32(1) - for (i,lnode) in enumerate(lnodes) - node = nodes[lnode] - data[p+i] = node - end - end - end - end - nldfaces = length(ldface_to_lnodes) - face_to_nodes[d+1] = JaggedArray(data,ptrs) - for ldface in 1:nldfaces - group = ngroups + ldface - group_name = "$(d)-face-$ldface" - faces_in_physical_group = findall(g->g==group,dface_to_physical_group) - groups[d+1][group_name] = faces_in_physical_group - end - ngroups += nldfaces - if d == (D-1) - groups[d+1]["boundary"] = 1:length(dface_to_physical_group) - end - end # d - ngroups += 1 - groups, face_to_nodes - end # barrier - chain = cartesian_chain(domain,cells_per_dir) - interior_mesh = mesh_from_chain(chain) - D = num_dims(interior_mesh) - cell_to_nodes = face_nodes(interior_mesh,D) - reference_cells = reference_faces(interior_mesh,D) - node_coords = node_coordinates(interior_mesh) - ref_cell = first(reference_cells) - refid_to_refface = reference_faces(boundary(ref_cell)) - nnodes = num_nodes(interior_mesh) - d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] - groups, face_to_nodes = barrier( - D, - cell_to_nodes, - nnodes, - d_to_ldface_to_lnodes) - face_to_refid = [ ones(Int8,length(face_to_nodes[d+1])) for d in 0:(D-1)] - mesh_face_nodes = push(face_to_nodes,face_nodes(interior_mesh,D)) - mesh_face_reference_id = push(face_to_refid,face_reference_id(interior_mesh,D)) - mesh_reference_faces = push(refid_to_refface,reference_faces(interior_mesh,D)) - mesh_groups = push(groups,physical_faces(interior_mesh,D)) - fe_mesh( - node_coords, - mesh_face_nodes, - mesh_face_reference_id, - mesh_reference_faces; - physical_faces=mesh_groups, - ) -end - -function cartesian_chain(domain,cells_per_dir) - box = bounding_box_from_domain(domain) - D = length(cells_per_dir) - nodes_per_dir = cells_per_dir .+ 1 - pmin = first(box) - pmax = last(box) - extent_per_dir = pmax .- pmin - h_per_dir = SVector(extent_per_dir ./ cells_per_dir) - nnodes = prod(nodes_per_dir) - ncells = prod(cells_per_dir) - nlnodes = 2^D - cell_nodes_ptrs = fill(Int32(nlnodes),ncells+1) - cell_nodes_ptrs[1] = 0 - length_to_ptrs!(cell_nodes_ptrs) - cell_nodes_data = zeros(Int32,ncells*nlnodes) - cell_nodes = JaggedArray(cell_nodes_data,cell_nodes_ptrs) - cell_cis = CartesianIndices(cells_per_dir) - cell_lis = LinearIndices(cells_per_dir) - node_cis = CartesianIndices(nodes_per_dir) - node_lis = LinearIndices(nodes_per_dir) - lnode_cis = CartesianIndices(ntuple(i->0:1,Val(D))) - for (cell_li,cell_ci) in enumerate(cell_cis) - nodes = cell_nodes[cell_li] - for (lnode_li,lnode_ci) in enumerate(lnode_cis) - node_ci = CartesianIndex(Tuple(cell_ci) .+ Tuple(lnode_ci)) - node_li = node_lis[node_ci] - nodes[lnode_li] = node_li - end - end - node_coords = zeros(SVector{D,Float64},nnodes) - for (node_li,node_ci) in enumerate(node_cis) - anchor = SVector(Tuple(node_ci) .- 1) - x = pmin .+ h_per_dir .* anchor - node_coords[node_li] = x - end - cell_reference_id = fill(Int32(1),ncells) - cell_geometry = unit_n_cube(Val(D)) - order = 1 - ref_cell = lagrangian_fe(cell_geometry,order) - reference_cells = [ref_cell] - interior_cells = collect(Int32,1:length(cell_nodes)) - groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) - chain = fe_chain( - node_coords, - cell_nodes, - cell_reference_id, - reference_cells; - physical_faces=groups, - ) - chain -end - -function structured_simplex_chain(domain,cells_per_dir) - box = bounding_box_from_domain(domain) - D = length(cells_per_dir) - nodes_per_dir = cells_per_dir .+ 1 - pmin = first(box) - pmax = last(box) - extent_per_dir = pmax .- pmin - h_per_dir = SVector(extent_per_dir ./ cells_per_dir) - nnodes = prod(nodes_per_dir) - nlnodes = 2^D - cell_geometry = unit_n_cube(Val(D)) - ref_simplex_mesh = simplexify(cell_geometry) - rscell_to_lnodes = face_nodes(ref_simplex_mesh,D) - nrscells = length(rscell_to_lnodes) - nslnodes = length(first(rscell_to_lnodes)) - ncells = prod(cells_per_dir)*nrscells - cell_nodes_ptrs = fill(Int32(nslnodes),ncells+1) - cell_nodes_ptrs[1] = 0 - length_to_ptrs!(cell_nodes_ptrs) - ndata = cell_nodes_ptrs[end]-1 - cell_nodes_data = zeros(Int32,ndata) - cell_nodes = JaggedArray(cell_nodes_data,cell_nodes_ptrs) - cell_cis = CartesianIndices(cells_per_dir) - cell_lis = LinearIndices(cells_per_dir) - node_cis = CartesianIndices(nodes_per_dir) - node_lis = LinearIndices(nodes_per_dir) - lnode_cis = CartesianIndices(ntuple(i->0:1,Val(D))) - clnodes = zeros(Int,nlnodes) - scell = 0 - for (cell_li,cell_ci) in enumerate(cell_cis) - for (lnode_li,lnode_ci) in enumerate(lnode_cis) - node_ci = CartesianIndex(Tuple(cell_ci) .+ Tuple(lnode_ci)) - node_li = node_lis[node_ci] - clnodes[lnode_li] = node_li - end - for srcell in 1:nrscells - scell += 1 - nodes = cell_nodes[scell] - lnodes = rscell_to_lnodes[srcell] - for (i,lnode) in enumerate(lnodes) - nodes[i] = clnodes[lnode] - end - end - end - node_coords = zeros(SVector{D,Float64},nnodes) - for (node_li,node_ci) in enumerate(node_cis) - anchor = SVector(Tuple(node_ci) .- 1) - x = pmin .+ h_per_dir .* anchor - node_coords[node_li] = x - end - cell_reference_id = fill(Int32(1),ncells) - order = 1 - reference_cells = reference_faces(ref_simplex_mesh,D) - interior_cells = collect(Int32,1:length(cell_nodes)) - groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) - chain = fe_chain( - node_coords, - cell_nodes, - cell_reference_id, - reference_cells; - physical_faces=groups, - ) - chain -end - -function structured_simplex_mesh_with_boundary(domain,cells_per_dir) - function barrier( - D, - cell_to_nodes, - nnodes, - d_to_ldface_to_lnodes, - d_to_ldface_to_sldface_to_lnodes) - - node_to_n = zeros(Int32,nnodes) - for nodes in cell_to_nodes - for node in nodes - node_to_n[node] += Int32(1) - end - end - J = typeof(JaggedArray(Vector{Int32}[])) - face_to_nodes = Vector{J}(undef,D) - groups = [ Dict{String,Vector{Int32}}() for d in 0:(D-1) ] - ngroups = 0 - for d in 0:(D-1) - nmax = 2^d - ldface_to_lnodes = d_to_ldface_to_lnodes[d+1] - ldface_to_sldface_to_lnodes = d_to_ldface_to_sldface_to_lnodes[d+1] - ndfaces = 0 - for nodes in cell_to_nodes - for (ldface,lnodes) in enumerate(ldface_to_lnodes) - isboundary = true - for lnode in lnodes - node = nodes[lnode] - if node_to_n[node] > nmax - isboundary = false - break - end - end - if isboundary - ndfaces += length(ldface_to_sldface_to_lnodes[ldface]) - end - end - end - nslnodes = length(ldface_to_sldface_to_lnodes[begin][begin]) - ptrs = zeros(Int32,ndfaces+1) - for dface in 1:ndfaces - ptrs[dface+1] += Int32(nslnodes) - end - length_to_ptrs!(ptrs) - ndata = ptrs[end]-1 - data = zeros(Int32,ndata) - dface_to_physical_group = zeros(Int32,ndfaces) - ndfaces = 0 - for nodes in cell_to_nodes - for (ldface,lnodes) in enumerate(ldface_to_lnodes) - isboundary = true - for lnode in lnodes - node = nodes[lnode] - if node_to_n[node] > nmax - isboundary = false - break - end - end - if isboundary - group = ngroups + ldface - sldface_to_lnodes = ldface_to_sldface_to_lnodes[ldface] - nsldfaces = length(sldface_to_lnodes) - for sldface in 1:nsldfaces - ndfaces += 1 - dface_to_physical_group[ndfaces] = group - p = ptrs[ndfaces]-Int32(1) - mylnodes = sldface_to_lnodes[sldface] - for (i,lnode) in enumerate(mylnodes) - node = nodes[lnode] - data[p+i] = node - end - end - end - end - end - nldfaces = length(ldface_to_lnodes) - face_to_nodes[d+1] = JaggedArray(data,ptrs) - for ldface in 1:nldfaces - group = ngroups + ldface - group_name = "$(d)-face-$ldface" - faces_in_physical_group = findall(g->g==group,dface_to_physical_group) - groups[d+1][group_name] = faces_in_physical_group - end - ngroups += nldfaces - if d == (D-1) - groups[d+1]["boundary"] = 1:length(dface_to_physical_group) - end - end # d - ngroups += 1 - groups, face_to_nodes - end # barrier - chain = cartesian_chain(domain,cells_per_dir) - interior_mesh = mesh_from_chain(chain) - D = num_dims(interior_mesh) - cell_to_nodes = face_nodes(interior_mesh,D) - reference_cells = reference_faces(interior_mesh,D) - ref_cell = first(reference_cells) - refid_to_refface = reference_faces(boundary(ref_cell)) - nnodes = num_nodes(interior_mesh) - - cell_geometry = unit_n_cube(Val(D)) - ref_simplex_mesh = simplexify(cell_geometry) - d_to_ldface_to_sldface_to_lnodes = [ - [ face_nodes(ref_simplex_mesh,d)[physical_faces(ref_simplex_mesh,d)["$d-face-$ldface"]] - for ldface in 1:num_faces(boundary(ref_cell),d) ] for d in 0:(D-1)] - d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] - groups, face_to_nodes = barrier( - D, - cell_to_nodes, - nnodes, - d_to_ldface_to_lnodes, - d_to_ldface_to_sldface_to_lnodes - ) - simplex_chain = structured_simplex_chain(domain,cells_per_dir) - node_coords = node_coordinates(simplex_chain) - face_to_refid = [ ones(Int8,length(face_to_nodes[d+1])) for d in 0:(D-1)] - mesh_face_nodes = push(face_to_nodes,face_nodes(simplex_chain)) - mesh_face_reference_id = push(face_to_refid,face_reference_id(simplex_chain)) - mesh_reference_faces = reference_faces(ref_simplex_mesh) - mesh_groups = push(groups,physical_faces(simplex_chain)) - fe_mesh( - node_coords, - mesh_face_nodes, - mesh_face_reference_id, - mesh_reference_faces; - physical_faces=mesh_groups, - ) -end - -function visualization_mesh(mesh::AbstractFEMesh,args...;kwargs...) - visualization_mesh_from_mesh(mesh,args...;kwargs...) -end - -function visualization_mesh_from_mesh(mesh,dim=num_dims(mesh);order=nothing,resolution=nothing) - function barrier( - refid_to_tabulation, - refid_to_scell_to_snodes, - refid_to_scell_to_srefid, - refid_to_srefid_to_oid, - refid_to_srefid_to_vrefface, - refid_to_snode_to_coords, - node_to_coords, - cell_to_nodes, - cell_to_refid, - ::Val{Dn}) where Dn - - ncells = length(cell_to_refid) - nvnodes = 0 - nvcells = 0 - for cell in 1:ncells - refid = cell_to_refid[cell] - nvnodes += length(refid_to_snode_to_coords[refid]) - nvcells += length(refid_to_scell_to_srefid[refid]) - - end - nrefids = length(refid_to_srefid_to_oid) - i_to_oid = reduce(vcat,refid_to_srefid_to_oid) - i_to_vrefface = reduce(vcat,refid_to_srefid_to_vrefface) - refid_to_srefid_to_i = Vector{Vector{Int}}(undef,nrefids) - i = 0 - for refid in 1:nrefids - srefid_to_oid = refid_to_srefid_to_oid[refid] - nsrefids = length(srefid_to_oid) - srefid_to_i = zeros(Int,nsrefids) - for srefid in 1:nsrefids - i += 1 - srefid_to_i[srefid] = i - end - refid_to_srefid_to_i[refid] = srefid_to_i - end - vrefid_to_oid = unique(i_to_oid) - i_to_vrefid = indexin(i_to_oid,vrefid_to_oid) - vrefid_to_i = indexin(vrefid_to_oid,i_to_oid) - vrefid_to_vrefface = i_to_vrefface[vrefid_to_i] - Tx = SVector{Dn,Float64} - vnode_to_coords = zeros(Tx,nvnodes) - vcell_to_vnodes_ptrs = zeros(Int32,nvcells+1) - vcell_to_vrefid = zeros(Int32,nvcells) - vcell_to_cell = zeros(Int32,nvcells) - cell_to_vnodes = fill(0:1,ncells) - vcell = 0 - for cell in 1:ncells - refid = cell_to_refid[cell] - scell_to_snodes = refid_to_scell_to_snodes[refid] - nscells = length(scell_to_snodes) - scell_to_srefid = refid_to_scell_to_srefid[refid] - srefid_to_i = refid_to_srefid_to_i[refid] - for scell in 1:nscells - srefid = scell_to_srefid[scell] - i = srefid_to_i[srefid] - vrefid = i_to_vrefid[i] - snodes = scell_to_snodes[scell] - vcell += 1 - vcell_to_vnodes_ptrs[vcell+1] = length(snodes) - vcell_to_vrefid[vcell] = vrefid - vcell_to_cell[vcell] = cell - end - end - length_to_ptrs!(vcell_to_vnodes_ptrs) - ndata = vcell_to_vnodes_ptrs[end]-1 - vcell_to_vnodes_data = zeros(Int32,ndata) - vcell = 0 - vnode = 0 - vnode_prev = 1 - for cell in 1:ncells - refid = cell_to_refid[cell] - scell_to_snodes = refid_to_scell_to_snodes[refid] - nscells = length(scell_to_snodes) - for scell in 1:nscells - snodes = scell_to_snodes[scell] - vcell += 1 - p = vcell_to_vnodes_ptrs[vcell] - for (i,snode) in enumerate(snodes) - vcell_to_vnodes_data[p-1+i] = snode + vnode - end - end - tabulation = refid_to_tabulation[refid] - nsnodes = size(tabulation,1) - nodes = cell_to_nodes[cell] - for snode in 1:nsnodes - y = zero(Tx) - for (i,node) in enumerate(nodes) - coeff = tabulation[snode,i] - x = node_to_coords[node] - y += coeff*x - end - vnode += 1 - vnode_to_coords[vnode] = y - end - cell_to_vnodes[cell] = vnode_prev:vnode - vnode_prev = vnode + 1 - end - vcell_to_vnodes = JaggedArray(vcell_to_vnodes_data,vcell_to_vnodes_ptrs) - vchain = fe_chain( - vnode_to_coords, - vcell_to_vnodes, - vcell_to_vrefid, - vrefid_to_vrefface) - vmesh = mesh_from_chain(vchain) - vglue = (;parent_face=vcell_to_cell, - reference_coordinates=refid_to_snode_to_coords, - face_fine_nodes = cell_to_vnodes, - num_dims=Val(dim)) - vmesh, vglue - end # barrier - refid_to_refface = reference_faces(mesh,dim) - refid_to_refmesh = map(refid_to_refface) do ref_face - if order === nothing && resolution === nothing - # Use the given cells as visualization cells - mesh_from_reference_face(ref_face) - elseif order !== nothing && resolution === nothing - # Use cells of given order as visualization cells - geo = geometry(ref_face) - ref_face_ho = lagrangian_reference_face(geo;order) - mesh_from_reference_face(ref_face_ho) - elseif order === nothing && resolution !== nothing - # Use linear sub-cells with $resolution per direction per direction - geom = geometry(ref_face) - refine_reference_geometry(geom,resolution) - else - error("order and resolution kw-arguments can not be given at the same time") - end - end - refid_to_tabulation = map(refid_to_refface,refid_to_refmesh) do refface,refmesh - x = node_coordinates(refmesh) - tabulator(refface)(value,x) - end - refid_to_scell_to_snodes = map(refmesh->face_nodes(refmesh,dim),refid_to_refmesh) - refid_to_scell_to_srefid = map(refmesh->face_reference_id(refmesh,dim),refid_to_refmesh) - refid_to_srefid_to_oid = map(refmesh->map(objectid,reference_faces(refmesh,dim)),refid_to_refmesh) - refid_to_srefid_to_vrefface = map(refmesh->reference_faces(refmesh,dim),refid_to_refmesh) - refid_to_snode_to_coords = map(node_coordinates,refid_to_refmesh) - node_to_coords = node_coordinates(mesh) - cell_to_nodes = face_nodes(mesh,dim) - cell_to_refid = face_reference_id(mesh,dim) - Dn = num_ambient_dims(mesh) - barrier( - refid_to_tabulation, - refid_to_scell_to_snodes, - refid_to_scell_to_srefid, - refid_to_srefid_to_oid, - refid_to_srefid_to_vrefface, - refid_to_snode_to_coords, - node_to_coords, - cell_to_nodes, - cell_to_refid, - Val(Dn)) -end - -function refine_reference_geometry(geo,resolution) - function refine_n_cube_aligned(geo,n) - box = bounding_box(geo) - domain = domain_from_bounding_box(box) - D = num_dims(geo) - cells = ntuple(i->n,Val(D)) - cartesian_mesh(domain,cells) - end - function refine_unit_triangle(geo,n) - # Copyed + adapted from Gridap - tri_num(n) = n*(n+1)÷2 - v(n,i,j) = tri_num(n) - tri_num(n-i+1) + j - D = 2 - quad_to_tris = ((1,2,3),(2,4,3)) - quad = CartesianIndices( (0:1,0:1) ) - Tp = SVector{2,Float64} - n_verts = tri_num(n+1) - n_cells = tri_num(n)+tri_num(n-1) - n_verts_x_cell = 3 - X = zeros(Tp,n_verts) - T = [ zeros(Int,n_verts_x_cell) for i in 1:n_cells ] - for i in 1:n+1 - for j in 1:n+1-i+1 - vert = v(n+1,i,j) - X[vert] = SVector((i-1)/n,(j-1)/n) - end - end - for i in 1:n - for j in 1:n-(i-1) - verts = ntuple( lv-> v(n+1, (i,j).+quad[lv].I ...), Val{2^D}() ) - cell = v(n,i,j) - T[cell] .= map(i->verts[i],quad_to_tris[1]) - if (i-1)+(j-1) < n-1 - cell = tri_num(n) + v(n-1,i,j) - T[cell] .= map(i->verts[i],quad_to_tris[2]) - end - end - end - refface = lagrangian_reference_face(geo) - chain = Chain(; - num_dims=Val(2), - node_coordinates = X, - face_nodes = T, - face_reference_id = fill(1,length(T)), - reference_faces = [refface] - ) - mesh_from_chain(chain) - end - function refine_unit_tet(geo,n) - # Copyed + adapted from Gridap - tri_num(n) = n*(n+1)÷2 - tet_num(n) = n*(n+1)*(n+2)÷6 - v(n,i,j) = tri_num(n) - tri_num(n-i+1) + j - v(n,i,j,k) = tet_num(n) - tet_num(n-i+1) + v(n-i+1,j,k) - D = 3 - cube_to_tets = ((1,2,3,5),(2,4,3,6),(3,5,7,6),(2,3,5,6),(3,4,7,6),(4,6,7,8)) - cube = CartesianIndices( (0:1,0:1,0:1) ) - n_core_tets = length(cube_to_tets)-2 - Tp = SVector{3,Float64} - n_verts = tet_num(n+1) - n_cells = tet_num(n)+n_core_tets*tet_num(n-1)+tet_num(n-2) - n_verts_x_cell = 4 - X = zeros(Tp,n_verts) - T = [ zeros(Int,n_verts_x_cell) for i in 1:n_cells ] - for i in 1:n+1 - for j in 1:n+1-(i-1) - for k in 1:n+1-(i-1)-(j-1) - vert = v(n+1,i,j,k) - X[vert] = SVector((i-1)/n,(j-1)/n,(k-1)/n) - end - end - end - for i in 1:n - for j in 1:n-(i-1) - for k in 1:n-(i-1)-(j-1) - verts = ntuple( lv-> v(n+1, (i,j,k).+cube[lv].I ...), Val{2^D}() ) - cell = v(n,i,j,k) - T[cell] .= map(i->verts[i],cube_to_tets[1]) - if (i-1)+(j-1)+(k-1) < n-1 - cell = tet_num(n) + (v(n-1,i,j,k)-1)*n_core_tets - for t in 1:n_core_tets - T[cell+t] .= map(i->verts[i],cube_to_tets[t+1]) - end - end - if (i-1)+(j-1)+(k-1) < n-2 - cell = tet_num(n) + n_core_tets*tet_num(n-1) + v(n-2,i,j,k) - T[cell] .= map(i->verts[i],cube_to_tets[end]) - end - end - end - end - refface = lagrangian_fe(geo,1) - chain = fe_chain( - X, - T, - fill(1,length(T)), - [refface], - ) - mesh_from_chain(chain) - end - if is_n_cube(geo) && is_axis_aligned(geo) - refine_n_cube_aligned(geo,resolution) - elseif is_unit_simplex(geo) && num_dims(geo) == 2 - refine_unit_triangle(geo,resolution) - elseif is_unit_simplex(geo) && num_dims(geo) == 3 - refine_unit_tet(geo,resolution) - else - error("Case not implemented (yet)") - end -end - -function mesh_from_reference_face(ref_face) - boundary_mesh = boundary(ref_face) - D = num_dims(geometry(ref_face)) - nnodes = num_nodes(ref_face) - face_to_nodes = push(face_nodes(boundary_mesh),[collect(1:nnodes)]) - face_to_refid = push(face_reference_id(boundary_mesh),[1]) - refid_refface = push(reference_faces(boundary_mesh),[ref_face]) - node_to_coords = node_coordinates(ref_face) - groups = [ Dict{String,Vector{Int32}}() for d in 0:D] - for d in 0:D - for face in 1:length(face_to_refid[d+1]) - groups[d+1]["$d-face-$face"] = [face] - end - end - groups[end-1]["boundary"] = 1:length(face_to_refid[D-1+1]) - groups[end]["interior"] = [1] - mesh = fe_mesh( - node_to_coords, - face_to_nodes, - face_to_refid, - refid_refface) -end - -partition_from_mask(a) = partition_from_mask(identity,a) - -function partition_from_mask(f,node_to_mask) - T = Vector{Int32} - free_nodes = convert(T,findall(f,node_to_mask)) - dirichlet_nodes = convert(T,findall(i->!f(i),node_to_mask)) - nfree = length(free_nodes) - ndiri = length(dirichlet_nodes) - permutation = T(undef,nfree+ndiri) - permutation[free_nodes] = 1:nfree - permutation[dirichlet_nodes] = (1:ndiri) .+ nfree - TwoWayPartition(free_nodes,dirichlet_nodes,permutation) -end - -struct TwoWayPartition{A} <: AbstractVector{A} - first::A - last::A - permutation::A -end - -permutation(a::TwoWayPartition) = a.permutation -Base.size(a::TwoWayPartition) = (2,) -Base.IndexStyle(::Type{<:TwoWayPartition}) = IndexLinear() -function Base.getindex(a::TwoWayPartition,i::Int) - @boundscheck @assert i in (1,2) - if i == 1 - a.first - else - a.last - end -end +include("mesh_interface.jl") end # module diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl new file mode 100644 index 00000000..a610b511 --- /dev/null +++ b/src/mesh_interface.jl @@ -0,0 +1,3018 @@ + +abstract type GalerkinToolkitDataType end +function Base.show(io::IO,data::GalerkinToolkitDataType) + print(io,"GalerkinToolkit.$(nameof(typeof(data)))(…)") +end + +function push(a::AbstractVector,x) + b = copy(a) + push!(b,x) + b +end + +function push(a::Tuple,x) + (a...,x) +end + +val_parameter(a) = a +val_parameter(::Val{a}) where a = a +num_dims(a) = val_parameter(a.num_dims) +node_coordinates(a) = a.node_coordinates +reference_faces(a) = a.reference_faces +face_nodes(a) = a.face_nodes +face_incidence(a) = a.face_incidence +face_reference_id(a) = a.face_reference_id +physical_faces(a) = a.physical_faces +has_physical_faces(a) = hasproperty(a,:physical_faces) && a.physical_faces !== nothing +periodic_nodes(a) = a.periodic_nodes +has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing +geometry(a) = a.geometry +boundary(a) = a.boundary +is_n_cube(a) = hasproperty(a,:is_n_cube) ? val_parameter(a.is_n_cube) : false +is_simplex(a) = hasproperty(a,:is_simplex) ? val_parameter(a.is_simplex) : false +is_axis_aligned(a) = a.is_axis_aligned +bounding_box(a) = a.bounding_box +vertex_permutations(a) = a.vertex_permutations +face_own_dofs(a) = a.face_own_dofs +face_own_dof_permutations(a) = a.face_own_dof_permutations +node_to_dofs(a) = a.node_to_dofs +dof_to_node(a) = a.dof_to_node +dof_to_index(a) = a.dof_to_index +num_dofs(a) = a.num_dofs +coordinates(a) = a.coordinates +weights(a) = a.weights +order_per_dir(a) = a.order_per_dir +monomial_exponents(a) = a.monomial_exponents +lib_to_user_nodes(a) = a.lib_to_user_nodes +interior_nodes(a) = a.interior_nodes +face_permutation_ids(a) = a.face_permutation_ids +face_permutation_ids(a,m,n) = face_permutation_ids(a)[m+1,n+1] +local_nodes(a) = a.local_nodes +local_node_colors(a) = a.local_node_colors +real_type(a) = a.real_type +int_type(a) = a.int_type +outwards_normals(a) = a.outwards_normals + +reference_faces(a,d) = reference_faces(a)[val_parameter(d)+1] +face_nodes(a,d) = face_nodes(a)[val_parameter(d)+1] +face_incidence(a,d1,d2) = face_incidence(a)[val_parameter(d1)+1,val_parameter(d2)+1] +face_reference_id(a,d) = face_reference_id(a)[val_parameter(d)+1] +num_faces(a) = map(length,face_reference_id(a)) +num_faces(a,d) = length(face_reference_id(a,d)) +physical_faces(a,d) = physical_faces(a)[val_parameter(d)+1] +num_nodes(a) = length(node_coordinates(a)) +num_ambient_dims(a) = length(eltype(node_coordinates(a))) +function face_offsets(a) + D = num_dims(a) + offsets = zeros(Int,D+1) + for d in 1:D + offsets[d+1] = offsets[d] + num_faces(a,d-1) + end + offsets +end +function face_dim(a,d) + n = num_faces(a,d) + fill(d,n) +end +function face_dim(a) + D = num_dims(a) + reduce(vcat,map(d->face_dim(a,d),0:D)) +end + +abstract type AbstractFaceGeometry <: GalerkinToolkitDataType end + +struct ExtrusionPolytope{D,Tv,Ti} <: AbstractFaceGeometry + extrusion::NTuple{D,Bool} + bounding_box::NTuple{2,SVector{D,Tv}} + int_type::Type{Ti} +end + +function unit_simplex(num_dims;real_type=Float64,int_type=Int) + D = val_parameter(num_dims) + extrusion = ntuple(i->false,Val(D)) + Tv = real_type + p0 = ntuple(i->zero(real_type),Val(D)) |> SVector{D,Tv} + p1 = ntuple(i->one(real_type),Val(D)) |> SVector{D,Tv} + bounding_box = (p0,p1) + ExtrusionPolytope(extrusion,bounding_box,int_type) +end + +function unit_n_cube(num_dims;real_type=Float64,int_type=Int) + D = val_parameter(num_dims) + extrusion = ntuple(i->true,Val(D)) + Tv = real_type + p0 = ntuple(i->zero(real_type),Val(D)) |> SVector{D,Tv} + p1 = ntuple(i->one(real_type),Val(D)) |> SVector{D,Tv} + bounding_box = (p0,p1) + ExtrusionPolytope(extrusion,bounding_box,int_type) +end + +num_dims(p::ExtrusionPolytope{D}) where D = D +is_axis_aligned(p::ExtrusionPolytope) = true +is_simplex(geom::ExtrusionPolytope) = all(i->i==false,geom.extrusion) +is_n_cube(geom::ExtrusionPolytope) = all(geom.extrusion) +real_type(p::ExtrusionPolytope{D,Tv}) where {D,Tv} = Tv +int_type(p::ExtrusionPolytope{D,Tv,Ti}) where {D,Tv,Ti} = Ti + +function is_unit_n_cube(geo) + is_n_cube(geo) && is_unitary(geo) +end + +function is_unit_simplex(geo) + is_simplex(geo) && is_unitary(geo) +end + +function is_unitary(geom) + ! is_axis_aligned(geom) && return false + my_bounding_box = bounding_box(geom) + all(i->i==0,first(my_bounding_box)) && all(i->i==1,last(my_bounding_box)) +end + +struct GenericCuadrature{A,B} <: GalerkinToolkitDataType + coordinates::A + weights::B +end +struct Cuadrature{D,T} <: GalerkinToolkitDataType + coordinates::Vector{SVector{D,T}} + weights::Vector{T} +end +function quadrature(coordinates,weights) + GenericCuadrature(coordinates,weights) +end +function quadrature(coordinates::Vector{SVector{D,T}},weights::Vector{T}) where {D,T} + Cuadrature(coordinates,weights) +end + +function duffy_quadrature(geo,degree) + @assert is_unit_simplex(geo) + D = num_dims(geo) + if D == 0 + x = zeros(SVector{0,real_type},1) + w = ones(real_type,1) + return quadrature(x,w) + end + function map_to(a,b,(points,weights)) + points_ab = similar(points) + weights_ab = similar(weights) + points_ab .= 0.5*(b-a)*points .+ 0.5*(a+b) + weights_ab .= 0.5*(b-a)*weights + (points_ab, weights_ab) + end + function duffy_map(q) + D = length(q) + a = 1.0 + m = ntuple(Val(D)) do i + if i == 1 + q[i] + else + a *= (1-q[i-1]) + a*q[i] + end + end + typeof(q)(m) + end + n = ceil(Int, (degree + 1.0) / 2.0 ) + beta = 0 + dim_to_quad_1d = map(1:(D-1)) do d + alpha = (D-1)-(d-1) + map_to(0,1,gaussjacobi(n,alpha,beta)) + end + quad_1d = map_to(0,1,gausslegendre(n)) + push!(dim_to_quad_1d,quad_1d) + coords_per_dir = map(first,dim_to_quad_1d) + weights_per_dir = map(last,dim_to_quad_1d) + a = 0.5 + for d in (D-1):-1:1 + ws_1d = weights_per_dir[d] + ws_1d[:] *= a + a *= 0.5 + end + m = prod(map(length,weights_per_dir)) + Tv = real_type(geo) + w = zeros(Tv,m) + x = zeros(SVector{D,Tv},m) + tensor_product!(identity,x,coords_per_dir) + tensor_product!(prod,w,weights_per_dir) + x .= duffy_map.(x) + quadrature(x,w) +end + +function tensor_product_quadrature(geo,degree_per_dir) + @assert is_n_cube(geo) + @assert is_axis_aligned(geo) + my_bounding_box = bounding_box(geo) + D = num_dims(geo) + limits_per_dir = ntuple(i->(my_bounding_box[1][i],my_bounding_box[2][i]),Val(D)) + n_per_dir = map(d->ceil(Int,(d+1)/2),degree_per_dir) + function quadrature_1d(n,limits) + x,w = FastGaussQuadrature.gausslegendre(n) + a,b = limits + x .= (0.5*(b-a)) .*x .+ (0.5*(b+a)) + w .*= 0.5*(b-a) + quadrature(x,w) + end + quad_per_dir = map(quadrature_1d,n_per_dir,limits_per_dir) + coords_per_dir = map(coordinates,quad_per_dir) + weights_per_dir = map(weights,quad_per_dir) + m = prod(map(length,weights_per_dir)) + Tv = real_type(geo) + w = zeros(Tv,m) + x = zeros(SVector{D,Tv},m) + tensor_product!(identity,x,coords_per_dir) + tensor_product!(prod,w,weights_per_dir) + quadrature(x,w) +end + +function tensor_product!(f,result,values_per_dir) + shape = Tuple(map(length,values_per_dir)) + cis = CartesianIndices(shape) + lis = LinearIndices(cis) + for ci in cis + li = lis[ci] + result[li] = f(map((q,i)->q[i],values_per_dir,Tuple(ci))) + end + result +end + +function repeat_per_dir(geo,a) + D = num_dims(geo) + ntuple(i->a,Val(D)) +end +repeat_per_dir(geo,a::NTuple) = a + +function default_quadrature(geo,degree) + if is_n_cube(geo) && is_axis_aligned(geo) + D = num_dims(geo) + degree_per_dir = repeat_per_dir(geo,degree) + tensor_product_quadrature(geo,degree_per_dir) + elseif is_unit_simplex(geo) + duffy_quadrature(geo,degree) + else + error("Not implemented") + end +end + +abstract type AbstractMeshFace <: GalerkinToolkitDataType end + +num_dims(f::AbstractMeshFace) = num_dims(geometry(f)) + +abstract type AbstractLagrangeFE <: AbstractMeshFace end + +struct GenericLagrangeFE{A,B,C,D} <: AbstractLagrangeFE + geometry::A + order_per_dir::B + space::Symbol + lib_to_user_nodes::C + major::Symbol + shape::D +end + +struct ScalarShape end +const SCALAR_SHAPE = ScalarShape() + +lagrangian_fe(args...) = GenericLagrangeFE(args...) + +function lagrangian_fe(geometry,order; + space = default_space(geometry), + lib_to_user_nodes = int_type(geometry)[], + major = :component, + shape = SCALAR_SHAPE) + D = num_dims(geometry) + order_per_dir = repeat_per_dir(geometry,order) + lagrangian_fe( + geometry, + order_per_dir, + space, + lib_to_user_nodes, + major, + shape) +end + +function default_space(geom) + if is_simplex(geom) + :P + elseif is_n_cube(geom) + :Q + else + error("Not implemented") + end +end + +order(fe::AbstractLagrangeFE) = maximum(order_per_dir(fe),init=0) + +function lib_to_user_nodes(fe::AbstractLagrangeFE) + if length(fe.lib_to_user_nodes) == 0 + nnodes = num_nodes(fe) + Ti = int_type(geometry(fe)) + collect(Ti.(1:nnodes)) + else + fe.lib_to_user_nodes + end +end + +function monomial_exponents(fe::AbstractLagrangeFE) + monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> int_type) +end + +function node_coordinates(fe::AbstractLagrangeFE) + @assert fe |> geometry |> is_unitary + mexps = monomial_exponents(fe) + node_coordinates_from_monomials_exponents(mexps,fe.order_per_dir,fe.geometry |> real_type) +end + +function node_coordinates_from_monomials_exponents(monomial_exponents,order_per_dir,real_type) + D = length(order_per_dir) + Tv = real_type + if length(monomial_exponents) == 0 + return + end + node_coordinates = map(monomial_exponents) do exponent + map(exponent,order_per_dir) do e,order + if order != 0 + real_type(e/order) + else + real_type(e) + end + end |> SVector{D,Tv} + end +end + +function monomial_exponents_from_space(space,args...) + if space == :Q + monomial_exponents_from_filter((e,o)->true,args...) + elseif space == :P + monomial_exponents_from_filter((e,o)->sum(e)<=maximum(o,init=0),args...) + else + error("Case not implemented (yet)") + end +end + +function monomial_exponents_from_filter(f,order_per_dir,int_type) + Ti = int_type + terms_per_dir = Tuple(map(d->d+1,order_per_dir)) + D = length(terms_per_dir) + cis = CartesianIndices(terms_per_dir) + m = count(ci->f(SVector{D,Ti}(Tuple(ci) .- 1),order_per_dir),cis) + result = zeros(SVector{D,int_type},m) + li = 0 + for ci in cis + t = SVector{D,Ti}(Tuple(ci) .- 1) + if f(t,order_per_dir) + li += 1 + result[li] = t + end + end + result +end + +function tensor_basis(fe::AbstractLagrangeFE) + Tv = fe |> geometry |> real_type + if fe.shape == SCALAR_SHAPE + return Tv(1) + else + cis = CartesianIndices(val_parameter(fe.shape)) + l = prod(val_parameter(fe.shape)) + init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Tv} + cis_flat = cis[:] + return map(cis_flat) do ci + init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) + end + end +end + +function primal_basis(fe::AbstractLagrangeFE) + scalar_basis = map(e->(x-> prod(x.^e)),fe|>monomial_exponents) + if fe.shape == SCALAR_SHAPE + return scalar_basis + else + primal_nested = map(scalar_basis) do monomial + map(tensor_basis(fe)) do e + x -> monomial(x)*e + end + end + return reduce(vcat,primal_nested) + end +end + +function dual_basis(fe::AbstractLagrangeFE) + node_coordinates_reffe = fe|>node_coordinates + scalar_basis = map(x->(f->f(x)),node_coordinates_reffe) + if fe.shape == SCALAR_SHAPE + return scalar_basis + else + if fe.major === :component + dual_nested = map(node_coordinates_reffe) do x + map(tensor_basis(fe)) do e + f->inner(e,f(x)) + end + end + elseif fe.major === :node + dual_nested = map(tensor_basis(fe)) do e + map(node_coordinates_reffe) do x + f->inner(e,f(x)) + end + end + else + error("Not Implemented") + end + return reduce(vcat,dual_nested) + end +end + +inner(a,b) = sum(map(*,a,b)) +value(f,x) = f(x) + +function shape_functions(fe) + primal = primal_basis(fe) + dual = dual_basis(fe) + primal_t = permutedims(primal) + A = value.(dual,primal_t) + B = A\I + n = length(primal) + map(1:n) do i + Bi = view(B,:,i) + x->begin + primal_t_x = map(f->f(x),primal_t) + (primal_t_x*Bi)[1,1]#TODO + end + end +end + +function tabulator(fe) + primal = primal_basis(fe) + dual = dual_basis(fe) + primal_t = permutedims(primal) + A = value.(dual,primal_t) + B = A\I + (f,x) -> begin + C = broadcast(f,primal_t,x) + C*B + end +end + +abstract type AbstractFEMesh <: GalerkinToolkitDataType end + +struct GenericFEMesh{A,B,C,D,E,F,G} <: AbstractFEMesh + node_coordinates::A + face_nodes::B + face_reference_id::C + reference_faces::D + periodic_nodes::E + physical_faces::F + outwards_normals::G +end + +function fe_mesh(args...) + GenericFEMesh(args...) +end + +function fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + periodic_nodes = eltype(eltype(face_reference_id))[], + physical_faces = map(i->Dict{String,Vector{eltype(eltype(face_reference_id))}}(),face_reference_id), + outwards_normals = nothing + ) + fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces, + periodic_nodes, + physical_faces, + outwards_normals) +end + +num_dims(mesh::AbstractFEMesh) = length(reference_faces(mesh))-1 + +const INVALID_ID = 0 + +function default_gmsh_options() + [ + "General.Terminal"=>1, + "Mesh.SaveAll"=>1, + "Mesh.MedImportGroupsOfNodes"=>1 + ] +end + +function with_gmsh(f;options=default_gmsh_options()) + gmsh.initialize() + for (k,v) in options + gmsh.option.setNumber(k,v) + end + try + return f() + finally + gmsh.finalize() + end +end + +function mesh_from_gmsh(file;complexify=true,renumber=true,kwargs...) + @assert ispath(file) "File not found: $(file)" + with_gmsh(;kwargs...) do + gmsh.open(file) + renumber && gmsh.model.mesh.renumberNodes() + renumber && gmsh.model.mesh.renumberElements() + mesh_from_gmsh_module(;complexify) + end +end + +function mesh_from_gmsh_module(;complexify=true) + entities = gmsh.model.getEntities() + nodeTags, coord, parametricCoord = gmsh.model.mesh.getNodes() + + # find num_dims + ddim = -1 + for e in entities + ddim = max(ddim,e[1]) + end + if ddim == -1 + error("No entities in the msh file.") + end + D = ddim + + # find embedded_dimension + dtouched = [false,false,false] + for node in nodeTags + if !(coord[(node-1)*3+1] + 1 ≈ 1) + dtouched[1] = true + end + if !(coord[(node-1)*3+2] + 1 ≈ 1) + dtouched[2] = true + end + if !(coord[(node-1)*3+3] + 1 ≈ 1) + dtouched[3] = true + end + end + if dtouched[3] + adim = 3 + elseif dtouched[2] + adim = 2 + elseif dtouched[1] + adim = 1 + else + adim = 0 + end + + # Setup node coords + nmin = minimum(nodeTags) + nmax = maximum(nodeTags) + nnodes = length(nodeTags) + if !(nmax == nnodes && nmin == 1) + error("Only consecutive node tags allowed.") + end + my_node_to_coords = zeros(SVector{adim,Float64},nnodes) + m = zero(MVector{adim,Float64}) + for node in nodeTags + for j in 1:adim + k = (node-1)*3 + j + xj = coord[k] + m[j] = xj + end + my_node_to_coords[node] = m + end + + # Setup face nodes + offsets = zeros(Int32,D+1) + my_face_nodes = Vector{JaggedArray{Int32,Int32}}(undef,D+1) + for d in 0:D + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) + ndfaces = 0 + for t in 1:length(elemTypes) + ndfaces += length(elemTags[t]) + end + if ndfaces != 0 + nmin::Int = minimum( minimum, elemTags ) + nmax::Int = maximum( maximum, elemTags ) + if !( (nmax-nmin+1) == ndfaces) + error("Only consecutive elem tags allowed.") + end + offsets[d+1] = nmin-1 + end + ptrs = zeros(Int32,ndfaces+1) + dface = 0 + for t in 1:length(elemTypes) + elementName, dim, order, numNodes, nodeCoord = + gmsh.model.mesh.getElementProperties(elemTypes[t]) + for e in 1:length(elemTags[t]) + dface += 1 + ptrs[dface+1] = numNodes + end + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = zeros(Int32,ndata) + dface = 1 + for t in 1:length(elemTypes) + p = ptrs[dface]-Int32(1) + for (i,node) in enumerate(nodeTags[t]) + data[p+i] = node + end + dface += length(elemTags[t]) + end + my_face_nodes[d+1] = JaggedArray(data,ptrs) + end + + # Setup face_reference_id + my_face_reference_id = Vector{Vector{Int32}}(undef,D+1) + for d in 0:D + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) + ndfaces = length(my_face_nodes[d+1]) + dface_to_refid = zeros(Int8,ndfaces) + refid = 0 + dface = 0 + for t in 1:length(elemTypes) + refid += 1 + for e in 1:length(elemTags[t]) + dface += 1 + dface_to_refid[dface] = refid + end + end + my_face_reference_id[d+1] = dface_to_refid + end + + # Setup reference faces + my_reference_faces = () + for d in D:-1:0 + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(d) + refdfaces = () + for t in 1:length(elemTypes) + refface = reference_face_from_gmsh_eltype(elemTypes[t]) + refdfaces = (refdfaces...,refface) + end + if refdfaces == () + refdfaces = reference_faces(boundary(first(first(my_reference_faces))),d) + end + my_reference_faces = (refdfaces,my_reference_faces...) + end + + ## Setup periodic nodes + node_to_master_node = fill(Int32(INVALID_ID),nnodes) + for (dim,tag) in entities + tagMaster, nodeTags, nodeTagsMaster, = gmsh.model.mesh.getPeriodicNodes(dim,tag) + for i in 1:length(nodeTags) + node = nodeTags[i] + master_node = nodeTagsMaster[i] + node_to_master_node[node] = master_node + end + end + pnode_to_node = Int32.(findall(i->i!=INVALID_ID,node_to_master_node)) + pnode_to_master = node_to_master_node[pnode_to_node] + periodic_nodes = pnode_to_node => pnode_to_master + + # Setup physical groups + my_groups = [ Dict{String,Vector{Int32}}() for d in 0:D] + for d in 0:D + offset = Int32(offsets[d+1]) + dimTags = gmsh.model.getPhysicalGroups(d) + for (dim,tag) in dimTags + @boundscheck @assert dim == d + g_entities = gmsh.model.getEntitiesForPhysicalGroup(dim,tag) + ndfaces_in_physical_group = 0 + for entity in g_entities + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(dim,entity) + for t in 1:length(elemTypes) + ndfaces_in_physical_group += length(elemTags[t]) + end + end + dfaces_in_physical_group = zeros(Int32,ndfaces_in_physical_group) + ndfaces_in_physical_group = 0 + for entity in g_entities + elemTypes, elemTags, nodeTags = gmsh.model.mesh.getElements(dim,entity) + for t in 1:length(elemTypes) + for etag in elemTags[t] + ndfaces_in_physical_group += 1 + dfaces_in_physical_group[ndfaces_in_physical_group] = Int32(etag)-offset + end + end + end + groupname = gmsh.model.getPhysicalName(dim,tag) + my_groups[d+1][groupname] = dfaces_in_physical_group + end + end + mesh = fe_mesh( + my_node_to_coords, + my_face_nodes, + my_face_reference_id, + my_reference_faces; + physical_faces = my_groups, + periodic_nodes,) + + if complexify + mesh, _ = GalerkinToolkit.complexify(mesh) + end + mesh +end + +function reference_face_from_gmsh_eltype(eltype) + if eltype == 1 + order = 1 + geom = unit_n_cube(Val(1)) + lib_to_gmsh = [1,2] + elseif eltype == 2 + order = 1 + geom = unit_simplex(Val(2)) + lib_to_gmsh = [1,2,3] + elseif eltype == 3 + order = 1 + geom = unit_n_cube(Val(2)) + lib_to_gmsh = [1,2,4,3] + elseif eltype == 4 + order = 1 + geom = unit_simplex(Val(3)) + lib_to_gmsh = [1,2,3,4] + elseif eltype == 5 + order = 1 + lib_to_gmsh = [1,2,4,3,5,6,8,7] + elseif eltype == 15 + order = 1 + geom = unit_n_cube(Val(0)) + lib_to_gmsh = [1] + elseif eltype == 8 + order = 2 + geom = unit_n_cube(Val(1)) + lib_to_gmsh = [1,3,2] + elseif eltype == 9 + order = 2 + geom = unit_simplex(Val(2)) + lib_to_gmsh = [1,4,2,6,5,3] + else + en, = gmsh.model.mesh.getElementProperties(eltype) + error("Unsupported element type. elemType: $eltype ($en)") + end + lagrangian_fe(geom,order;lib_to_user_nodes=lib_to_gmsh) +end + +function vtk_points(mesh) + function barrirer(coords) + nnodes = length(coords) + points = zeros(3,nnodes) + for node in 1:nnodes + coord = coords[node] + for i in 1:length(coord) + points[i,node] = coord[i] + end + end + points + end + coords = node_coordinates(mesh) + barrirer(coords) +end + +function vtk_cells(mesh,d) + function barrirer(face_to_refid,face_to_nodes,refid_mesh_cell) + cells = map(face_to_refid,face_to_nodes) do refid, nodes + mesh_cell = refid_mesh_cell[refid] + if mesh_cell === nothing + msg = """ + Not enough information to visualize this mesh via vtk: + vtk_mesh_cell returns nothing for the reference face in position $refid in dimension $d. + """ + error(msg) + end + mesh_cell(nodes) + end + cells + end + face_to_nodes = face_nodes(mesh,d) + face_to_refid = face_reference_id(mesh,d) + refid_refface = reference_faces(mesh,d) + refid_mesh_cell = map(vtk_mesh_cell,refid_refface) + barrirer(face_to_refid,face_to_nodes,refid_mesh_cell) +end + +""" + args = vtk_args(mesh,d) + +Return the arguments `args` to be passed in final position +to functions like `WriteVTK.vtk_grid`. +""" +function vtk_args(mesh,d) + points = vtk_points(mesh) + cells = vtk_cells(mesh,d) + points, cells +end + +function vtk_args(mesh) + points = vtk_points(mesh) + D = num_dims(mesh) + allcells = [vtk_cells(mesh,d) for d in 0:D if num_faces(mesh,d) != 0] + cells = reduce(vcat,allcells) + points, cells +end + +function vtk_physical_faces!(vtk,mesh,d;physical_faces=physical_faces(mesh,d)) + ndfaces = num_faces(mesh,d) + for group in physical_faces + name,faces = group + face_mask = zeros(Int,ndfaces) + face_mask[faces] .= 1 + vtk[name,WriteVTK.VTKCellData()] = face_mask + end + vtk +end + +function vtk_physical_faces!(vtk,mesh;physical_faces=physical_faces(mesh)) + nfaces = sum(num_faces(mesh)) + offsets = face_offsets(mesh) + D = num_dims(mesh) + data = Dict{String,Vector{Int}}() + for d in 0:D + for group in physical_faces[d+1] + name, = group + if !haskey(data,name) + face_mask = zeros(Int,nfaces) + data[name] = face_mask + end + end + end + for d in 0:D + for group in physical_faces[d+1] + offset = offsets[d+1] + name,faces = group + face_mask = data[name] + face_mask[faces.+offset] .= 1 + end + end + for (name,face_mask) in data + vtk[name,WriteVTK.VTKCellData()] = face_mask + end + vtk +end + +function vtk_mesh_cell(ref_face) + geom = geometry(ref_face) + d = num_dims(geom) + nnodes = num_nodes(ref_face) + lib_to_user = lib_to_user_nodes(ref_face) + if d == 0 && nnodes == 1 + cell_type = WriteVTK.VTKCellTypes.VTK_VERTEX + vtk_to_lib = [1] + elseif d == 1 && (is_simplex(geom) || is_n_cube(geom)) && nnodes == 2 + cell_type = WriteVTK.VTKCellTypes.VTK_LINE + vtk_to_lib = [1,2] + elseif d == 1 && (is_simplex(geom) || is_n_cube(geom)) && nnodes == 3 + cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_EDGE + vtk_to_lib = [1,3,2] + elseif d == 2 && is_n_cube(geom) && nnodes == 4 + cell_type = WriteVTK.VTKCellTypes.VTK_QUAD + vtk_to_lib = [1,2,4,3] + elseif d == 2 && is_simplex(geom) && nnodes == 3 + cell_type = WriteVTK.VTKCellTypes.VTK_TRIANGLE + vtk_to_lib = [1,2,3] + elseif d == 2 && is_simplex(geom) && nnodes == 6 + cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_TRIANGLE + vtk_to_lib = [1,3,6,2,5,4] + elseif d == 3 && is_n_cube(geom) && nnodes == 8 + cell_type = WriteVTK.VTKCellTypes.VTK_HEXAHEDRON + vtk_to_lib = [1,2,4,3,5,6,8,7] + elseif d == 3 && is_simplex(geom) && nnodes == 4 + cell_type = WriteVTK.VTKCellTypes.VTK_TETRA + vtk_to_lib = [1,2,3,4] + elseif d == 3 && is_simplex(geom) && nnodes == 10 + cell_type = WriteVTK.VTKCellTypes.VTK_QUADRATIC_TETRA + vtk_to_lib = [1,3,6,10,2,5,4,7,8,9] + else + return nothing + end + nodes -> WriteVTK.MeshCell(cell_type,(nodes[lib_to_user])[vtk_to_lib]) +end + +# TODO use the same convention than in Gridap +# allow the user to customize the boundary object ids +function boundary(geom::ExtrusionPolytope{0}) + nothing +end +function boundary(geom::ExtrusionPolytope{1}) + Tv = real_type(geom) + Ti = int_type(geom) + if is_unit_simplex(geom) + vertex = unit_simplex(0;real_type=Tv,int_type=Ti) + elseif is_unit_n_cube(geom) + vertex = unit_n_cube(0;real_type=Tv,int_type=Ti) + else + error("Not implemented") + end + order = 1 + fe = lagrangian_fe(vertex,order) + node_coordinates = SVector{1,Tv}[(0,),(1,)] + face_nodes = [Vector{Ti}[[1],[2]]] + face_reference_id = [Ti[1,1]] + reference_faces = ([fe],) + outwards_normals = SVector{1,Tv}[(-1,),(1,)] + fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + outwards_normals + ) +end +function boundary(geom::ExtrusionPolytope{2}) + Tv = real_type(geom) + Ti = int_type(geom) + if is_unit_simplex(geom) + n1 = sqrt(2)/2 + g0 = unit_simplex(0;real_type=Tv,int_type=Ti) + g1 = unit_simplex(1;real_type=Tv,int_type=Ti) + node_coordinates = SVector{2,Tv}[(0,0),(1,0),(0,1)] + face_nodes = [Vector{Ti}[[1],[2],[3]],Vector{Ti}[[1,2],[1,3],[2,3]]] + face_reference_id = [Ti[1,1,1],Ti[1,1,1]] + outwards_normals = SVector{2,Tv}[(0,-1),(-1,0),(n1,n1)] + elseif is_unit_n_cube(geom) + g0 = unit_n_cube(0;real_type=Tv,int_type=Ti) + g1 = unit_n_cube(1;real_type=Tv,int_type=Ti) + node_coordinates = SVector{2,Tv}[(0,0),(1,0),(0,1),(1,1)] + face_nodes = [Vector{Ti}[[1],[2],[3],[4]],Vector{Ti}[[1,2],[3,4],[1,3],[2,4]]] + face_reference_id = [Ti[1,1,1,1],Ti[1,1,1,1]] + outwards_normals = SVector{2,Tv}[(0,-1),(0,1),(-1,0),(1,0)] + else + error("Not implemented") + end + order = 1 + fe0 = lagrangian_fe(g0,order) + fe1 = lagrangian_fe(g1,order) + reference_faces = ([fe0],[fe1]) + fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + outwards_normals + ) +end +function boundary(geom::ExtrusionPolytope{3}) + Tv = real_type(geom) + Ti = int_type(geom) + if is_unit_simplex(geom) + g0 = unit_simplex(0;real_type=Tv,int_type=Ti) + g1 = unit_simplex(1;real_type=Tv,int_type=Ti) + g2 = unit_simplex(2;real_type=Tv,int_type=Ti) + node_coordinates = SVector{3,Tv}[(0,0,0),(1,0,0),(0,1,0),(0,0,1)] + face_nodes = [ + Vector{Ti}[[1],[2],[3],[4]], + Vector{Ti}[[1,2],[1,3],[2,3],[1,4],[2,4],[3,4]], + Vector{Ti}[[1,2,3],[1,2,4],[1,3,4],[2,3,4]], + ] + face_reference_id = [ones(Ti,4),ones(Ti,6),ones(Ti,4)] + n1 = sqrt(3)/3 + outwards_normals = SVector{3,Tv}[(0,0,-1),(0,-1,0),(-1,0,0),(n1,n1,n1)] + elseif is_unit_n_cube(geom) + g0 = unit_n_cube(0;real_type=Tv,int_type=Ti) + g1 = unit_n_cube(1;real_type=Tv,int_type=Ti) + g2 = unit_n_cube(2;real_type=Tv,int_type=Ti) + node_coordinates = SVector{3,Tv}[(0,0,0),(1,0,0),(0,1,0),(1,1,0),(0,0,1),(1,0,1),(0,1,1),(1,1,1)] + face_nodes = [ + Vector{Ti}[[1],[2],[3],[4],[5],[6],[7],[8]], + Vector{Ti}[[1,2],[3,4],[1,3],[2,4],[5,6],[7,8],[5,7],[6,8],[1,5],[3,7],[2,6],[4,8]], + Vector{Ti}[[1,2,3,4],[5,6,7,8],[1,2,5,6],[3,4,7,8],[1,3,5,7],[2,4,6,8]], + ] + face_reference_id = [ones(Ti,8),ones(Ti,12),ones(Ti,6)] + outwards_normals = SVector{3,Tv}[(0,0,-1),(0,0,1),(0,-1,0),(0,1,0),(-1,0,0),(1,0,0)] + else + error("Not implemented") + end + order = 1 + fe0 = lagrangian_fe(g0,order) + fe1 = lagrangian_fe(g1,order) + fe2 = lagrangian_fe(g2,order) + reference_faces = ([fe0,],[fe1],[fe2]) + fe_mesh( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + outwards_normals + ) +end + +function boundary(refface::AbstractMeshFace) + boundary_from_mesh_face(refface) +end + +function boundary_from_mesh_face(refface) + geom = geometry(refface) + node_coordinates_inter = node_coordinates(refface) + order_inter = order(refface) + D = num_dims(geom) + if D == 0 + return nothing + end + mesh_geom = boundary(geom) + node_coordinates_geom = node_coordinates(mesh_geom) + face_nodes_inter = Vector{Vector{Vector{Int}}}(undef,D) + node_coordinates_aux = map(xi->map(xii->round(Int,order_inter*xii),xi),node_coordinates_inter) + ref_faces_geom = reference_faces(mesh_geom) + ref_faces = map(ref_faces_geom) do ref_faces_geom_d + map(r->lagrangian_fe(geometry(r),order_inter),ref_faces_geom_d) + end + face_ref_id_geom = face_reference_id(mesh_geom) + for d in 0:(D-1) + s_ref = map(ref_faces_geom[d+1],ref_faces[d+1]) do r_geom,r + m = num_nodes(r) + n = num_nodes(r_geom) + x = node_coordinates(r) + tabulator(r_geom)(value,x) + end + face_nodes_geom = face_nodes(mesh_geom,d) + nfaces = length(face_ref_id_geom[d+1]) + face_nodes_inter_d = Vector{Vector{Int}}(undef,nfaces) + for face in 1:nfaces + ref_id_geom = face_ref_id_geom[d+1][face] + s = s_ref[ref_id_geom] + nodes_geom = face_nodes_geom[face] + nnodes, nnodes_geom = size(s) + x_mapped = map(1:nnodes) do i + x = zero(eltype(node_coordinates_inter)) + for k in 1:nnodes_geom + x += node_coordinates_geom[nodes_geom[k]]*s[i,k] + end + map(xi->round(Int,order_inter*xi),x) + end + my_nodes = indexin(x_mapped,node_coordinates_aux) + face_nodes_inter_d[face] = my_nodes + end + face_nodes_inter[d+1] = face_nodes_inter_d + end + fe_mesh( + node_coordinates_inter, + face_nodes_inter, + face_ref_id_geom, + ref_faces) +end + +function interior_nodes(fe::AbstractMeshFace) + interior_nodes_from_mesh_face(fe) +end + +function interior_nodes_from_mesh_face(fe) + nnodes = num_nodes(fe) + D = num_dims(fe|>geometry) + if D == 0 + return collect(1:nnodes) + else + node_is_touched = fill(true,nnodes) + mesh = boundary(fe) + for d in 0:D + face_to_nodes = face_nodes(fe,d) + for nodes in face_to_nodes + node_is_touched[nodes] .= false + end + end + return findall(node_is_touched) + end +end + +function vertex_permutations(geom::AbstractFaceGeometry) + vertex_permutations_from_face_geometry(geom) +end + +## Admissible if the following map is admissible +# phi_i(x) = sum_i x_perm[i] * fun_i(x) +# This map sends vertex i to vertex perm[i] +function vertex_permutations_from_face_geometry(geo) + D = num_dims(geo) + if D == 0 + return [[1]] + end + geo_mesh = boundary(geo) + vertex_to_geo_nodes = face_nodes(geo_mesh,0) + vertex_to_geo_node = map(first,vertex_to_geo_nodes) + nvertices = length(vertex_to_geo_node) + # TODO compute this more lazily + # so that we never compute it for 3d + # since it is not needed + if D > 2 + return [collect(1:nvertices)] + end + permutations = Combinatorics.permutations(1:nvertices) + if is_simplex(geo) + return collect(permutations) + end + admissible_permutations = Vector{Int}[] + order = 1 + ref_face = lagrangian_fe(geo,order) + fun_mesh = boundary(ref_face) + geo_node_coords = node_coordinates(geo_mesh) + fun_node_coords = node_coordinates(fun_mesh) + vertex_coords = geo_node_coords[vertex_to_geo_node] + degree = 1 + quad = default_quadrature(geo,degree) + q = coordinates(quad) + Tx = eltype(vertex_coords) + TJ = typeof(zero(Tx)*zero(Tx)') + A = tabulator(ref_face)(ForwardDiff.gradient,q) + function compute_volume(vertex_coords) + vol = zero(eltype(TJ)) + for iq in 1:size(A,1) + J = zero(TJ) + for fun_node in 1:size(A,2) + vertex = fun_node # TODO we are assuming that the vertices and nodes match + g = A[iq,fun_node] + x = vertex_coords[vertex] + J += g*x' + end + vol += abs(det(J)) + end + vol + end + refvol = compute_volume(vertex_coords) + perm_vertex_coords = similar(vertex_coords) + for permutation in permutations + for (j,cj) in enumerate(permutation) + perm_vertex_coords[j] = vertex_coords[cj] + end + vol2 = compute_volume(perm_vertex_coords) + if (refvol + vol2) ≈ (2*refvol) + push!(admissible_permutations,permutation) + end + end + admissible_permutations +end + +function interior_node_permutations(fe::AbstractMeshFace) + interior_node_permutations_from_mesh_face(fe) +end + +function interior_node_permutations_from_mesh_face(refface) + interior_ho_nodes = interior_nodes(refface) + ho_nodes_coordinates = node_coordinates(refface) + geo = geometry(refface) + vertex_perms = vertex_permutations(geo) + if length(interior_ho_nodes) == 0 + return map(i->Int[],vertex_perms) + end + if length(vertex_perms) == 1 + return map(i->collect(1:length(interior_ho_nodes)),vertex_perms) + end + geo_mesh = boundary(geo) + vertex_to_geo_nodes = face_nodes(geo_mesh,0) + vertex_to_geo_node = map(first,vertex_to_geo_nodes) + ref_face = lagrangian_reference_face(geo) + fun_mesh = boundary(ref_face) + geo_node_coords = node_coordinates(geo_mesh) + fun_node_coords = node_coordinates(fun_mesh) + vertex_coords = geo_node_coords[vertex_to_geo_node] + q = ho_nodes_coordinates[interior_ho_nodes] + Tx = eltype(vertex_coords) + A = zeros(Float64,length(q),length(fun_node_coords)) + A = tabulator(ref_face)(value,q) + perm_vertex_coords = similar(vertex_coords) + node_perms = similar(vertex_perms) + for (iperm,permutation) in enumerate(vertex_perms) + for (j,cj) in enumerate(permutation) + perm_vertex_coords[j] = vertex_coords[cj] + end + node_to_pnode = fill(INVALID_ID,length(interior_ho_nodes)) + for iq in 1:size(A,1) + y = zero(Tx) + for fun_node in 1:size(A,2) + vertex = fun_node # TODO we are assuming that the vertices and nodes match + g = A[iq,fun_node] + x = perm_vertex_coords[vertex] + y += g*x + end + pnode = findfirst(i->(norm(i-y)+1)≈1,q) + if pnode != nothing + node_to_pnode[iq] = pnode + end + end + node_perms[iperm] = node_to_pnode + end + node_perms +end + +abstract type AbstractMeshTopology <: GalerkinToolkitDataType end + +struct GenericMeshTopology{A,B,C,D} <: AbstractMeshTopology + face_incidence::A + face_reference_id::B + face_permutation_ids::C + reference_faces::D +end + +function mesh_topology(args...) + GenericMeshTopology(args...) +end + +function topology(mesh::AbstractFEMesh) + topology_from_mesh(mesh) +end + +function topology_from_mesh(mesh) + # Assumes that the input is a cell complex + T = JaggedArray{Int32,Int32} + D = num_dims(mesh) + my_face_incidence = Matrix{T}(undef,D+1,D+1) + my_face_reference_id = [ face_reference_id(mesh,d) for d in 0:D ] + my_reference_faces = Tuple([ map(topology,reference_faces(mesh,d)) for d in 0:D ]) + my_face_permutation_ids = Matrix{T}(undef,D+1,D+1) + topo = mesh_topology( + my_face_incidence, + my_face_reference_id, + my_face_permutation_ids, + my_reference_faces, + ) + for d in 0:D + fill_face_interior_mesh_topology!(topo,mesh,d) + end + for d in 1:D + fill_face_vertices_mesh_topology!(topo,mesh,d) + fill_face_coboundary_mesh_topology!(topo,mesh,d,0) + end + for d in 1:(D-1) + for n in (D-d):-1:1 + m = n+d + fill_face_boundary_mesh_topology!(topo,mesh,m,n) + fill_face_coboundary_mesh_topology!(topo,mesh,m,n) + end + end + for d in 0:D + for n in 0:d + fill_face_permutation_ids!(topo,d,n) + end + end + topo +end + +function fill_face_interior_mesh_topology!(mesh_topology,mesh,d) + n = num_faces(mesh,d) + ptrs = collect(Int32,1:(n+1)) + data = collect(Int32,1:n) + mesh_topology.face_incidence[d+1,d+1] = JaggedArray(data,ptrs) +end + +function generate_face_coboundary(nface_to_mfaces,nmfaces) + ptrs = zeros(Int32,nmfaces+1) + nnfaces = length(nface_to_mfaces) + for nface in 1:nnfaces + mfaces = nface_to_mfaces[nface] + for mface in mfaces + ptrs[mface+1] += Int32(1) + end + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = zeros(Int32,ndata) + for nface in 1:nnfaces + mfaces = nface_to_mfaces[nface] + for mface in mfaces + p = ptrs[mface] + data[p] = nface + ptrs[mface] += Int32(1) + end + end + rewind_ptrs!(ptrs) + mface_to_nfaces = JaggedArray(data,ptrs) + mface_to_nfaces +end + +function fill_face_coboundary_mesh_topology!(topology,mesh,n,m) + nmfaces = num_faces(mesh,m) + nface_to_mfaces = face_incidence(topology,n,m) + face_incidence(topology)[m+1,n+1] = generate_face_coboundary(nface_to_mfaces,nmfaces) +end + +function fill_face_vertices_mesh_topology!(topo,mesh,d) + function barrier(nnodes,vertex_to_nodes,dface_to_nodes,dface_to_refid,refid_to_lvertex_to_lnodes) + vertex_to_node = JaggedArray(vertex_to_nodes).data + #if vertex_to_node == 1:nnodes + # return dface_to_nodes + #end + node_to_vertex = zeros(Int32,nnodes) + nvertices = length(vertex_to_nodes) + node_to_vertex[vertex_to_node] = 1:nvertices + ndfaces = length(dface_to_nodes) + dface_to_vertices_ptrs = zeros(Int32,ndfaces+1) + for dface in 1:ndfaces + refid = dface_to_refid[dface] + nlvertices = length(refid_to_lvertex_to_lnodes[refid]) + dface_to_vertices_ptrs[dface+1] = nlvertices + end + length_to_ptrs!(dface_to_vertices_ptrs) + ndata = dface_to_vertices_ptrs[end]-1 + dface_to_vertices_data = zeros(Int32,ndata) + for dface in 1:ndfaces + refid = dface_to_refid[dface] + lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] + nlvertices = length(lvertex_to_lnodes) + lnode_to_node = dface_to_nodes[dface] + offset = dface_to_vertices_ptrs[dface]-1 + for lvertex in 1:nlvertices + lnode = first(lvertex_to_lnodes[lvertex]) + node = lnode_to_node[lnode] + vertex = node_to_vertex[node] + dface_to_vertices_data[offset+lvertex] = vertex + end + end + dface_to_vertices = JaggedArray(dface_to_vertices_data,dface_to_vertices_ptrs) + end + nnodes = num_nodes(mesh) + vertex_to_nodes = face_nodes(mesh,0) + dface_to_nodes = face_nodes(mesh,d) + dface_to_refid = face_reference_id(mesh,d) + refid_refface = reference_faces(mesh,d) + refid_to_lvertex_to_lnodes = map(refface->face_nodes(boundary(refface),0),refid_refface) + face_incidence(topo)[d+1,0+1] = barrier(nnodes,vertex_to_nodes,dface_to_nodes,dface_to_refid,refid_to_lvertex_to_lnodes) +end + +function fill_face_boundary_mesh_topology!(topo,mesh,D,d) + function barrier( + Dface_to_vertices, + vertex_to_Dfaces, + dface_to_vertices, + vertex_to_dfaces, + Dface_to_refid, + Drefid_to_ldface_to_lvertices) + + # Count + ndfaces = length(dface_to_vertices) + nDfaces = length(Dface_to_vertices) + # Allocate output + ptrs = zeros(Int32,nDfaces+1) + for Dface in 1:nDfaces + Drefid = Dface_to_refid[Dface] + ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] + ptrs[Dface+1] = length(ldface_to_lvertices) + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = fill(Int32(INVALID_ID),ndata) + Dface_to_dfaces = JaggedArray(data,ptrs) + for Dface in 1:nDfaces + Drefid = Dface_to_refid[Dface] + ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] + lvertex_to_vertex = Dface_to_vertices[Dface] + ldface_to_dface = Dface_to_dfaces[Dface] + for (ldface,lvertices) in enumerate(ldface_to_lvertices) + # Find the global d-face for this local d-face + dface2 = Int32(INVALID_ID) + vertices = view(lvertex_to_vertex,lvertices) + for (i,lvertex) in enumerate(lvertices) + vertex = lvertex_to_vertex[lvertex] + dfaces = vertex_to_dfaces[vertex] + for dface1 in dfaces + vertices1 = dface_to_vertices[dface1] + if same_valid_ids(vertices,vertices1) + dface2 = dface1 + break + end + end + if dface2 != Int32(INVALID_ID) + break + end + end + @boundscheck begin + msg = """ + + + Error in: topology_from_mesh + + The given mesh is provably not a cell complex. + """ + @assert dface2 != Int32(INVALID_ID) msg + end + ldface_to_dface[ldface] = dface2 + end # (ldface,lvertices) + end # Dface + Dface_to_dfaces + end + Dface_to_vertices = face_incidence(topo,D,0) + vertex_to_Dfaces = face_incidence(topo,0,D) + dface_to_vertices = face_incidence(topo,d,0) + vertex_to_dfaces = face_incidence(topo,0,d) + Dface_to_refid = face_reference_id(topo,D) + refid_refface = reference_faces(topo,D) + Drefid_to_ldface_to_lvertices = map(refface->face_incidence(boundary(refface),d,0),refid_refface) + Dface_to_dfaces = barrier( + Dface_to_vertices, + vertex_to_Dfaces, + dface_to_vertices, + vertex_to_dfaces, + Dface_to_refid, + Drefid_to_ldface_to_lvertices) + topo.face_incidence[D+1,d+1] = Dface_to_dfaces +end + +function fill_face_permutation_ids!(top,D,d) + function barrier!( + cell_to_lface_to_pindex, + cell_to_lface_to_face, + cell_to_cvertex_to_vertex, + cell_to_ctype, + ctype_to_lface_to_cvertices, + face_to_fvertex_to_vertex, + face_to_ftype, + ftype_to_pindex_to_cfvertex_to_fvertex) + + ncells = length(cell_to_lface_to_face) + for cell in 1:ncells + ctype = cell_to_ctype[cell] + lface_to_cvertices = ctype_to_lface_to_cvertices[ctype] + a = cell_to_lface_to_face.ptrs[cell]-1 + c = cell_to_cvertex_to_vertex.ptrs[cell]-1 + for (lface,cfvertex_to_cvertex) in enumerate(lface_to_cvertices) + face = cell_to_lface_to_face.data[a+lface] + ftype = face_to_ftype[face] + b = face_to_fvertex_to_vertex.ptrs[face]-1 + pindex_to_cfvertex_to_fvertex = ftype_to_pindex_to_cfvertex_to_fvertex[ftype] + pindexfound = false + for (pindex, cfvertex_to_fvertex) in enumerate(pindex_to_cfvertex_to_fvertex) + found = true + for (cfvertex,fvertex) in enumerate(cfvertex_to_fvertex) + vertex1 = face_to_fvertex_to_vertex.data[b+fvertex] + cvertex = cfvertex_to_cvertex[cfvertex] + vertex2 = cell_to_cvertex_to_vertex.data[c+cvertex] + if vertex1 != vertex2 + found = false + break + end + end + if found + cell_to_lface_to_pindex.data[a+lface] = pindex + pindexfound = true + break + end + end + @assert pindexfound "Valid pindex not found" + end + end + end + @assert D >= d + cell_to_lface_to_face = JaggedArray(face_incidence(top,D,d)) + data = similar(cell_to_lface_to_face.data,Int8) + ptrs = cell_to_lface_to_face.ptrs + cell_to_lface_to_pindex = JaggedArray(data,ptrs) + if d == D || d == 0 + fill!(cell_to_lface_to_pindex.data,Int8(1)) + top.face_permutation_ids[D+1,d+1] = cell_to_lface_to_pindex + return top + end + face_to_fvertex_to_vertex = JaggedArray(face_incidence(top,d,0)) + face_to_ftype = face_reference_id(top,d) + ref_dfaces = reference_faces(top,d) + ftype_to_pindex_to_cfvertex_to_fvertex = map(vertex_permutations,ref_dfaces) + cell_to_cvertex_to_vertex = JaggedArray(face_incidence(top,D,0)) + cell_to_ctype = face_reference_id(top,D) + ref_Dfaces = reference_faces(top,D) + ctype_to_lface_to_cvertices = map(p->face_incidence(boundary(p),d,0),ref_Dfaces) + barrier!( + cell_to_lface_to_pindex, + cell_to_lface_to_face, + cell_to_cvertex_to_vertex, + cell_to_ctype, + ctype_to_lface_to_cvertices, + face_to_fvertex_to_vertex, + face_to_ftype, + ftype_to_pindex_to_cfvertex_to_fvertex) + top.face_permutation_ids[D+1,d+1] = cell_to_lface_to_pindex + top +end + +function intersection!(a,b,na,nb) + function findeq!(i,a,b,nb) + for j in 1:nb + if a[i] == b[j] + return + end + end + a[i] = INVALID_ID + return + end + for i in 1:na + if a[i] == INVALID_ID + continue + end + findeq!(i,a,b,nb) + end +end + +function same_valid_ids(a,b) + function is_subset(a,b) + for i in 1:length(a) + v = a[i] + if v == INVALID_ID + continue + end + c = find_eq(v,b) + if c == false; return false; end + end + return true + end + function find_eq(v,b) + for vs in b + if v == vs + return true + end + end + return false + end + c = is_subset(a,b) + if c == false; return false; end + c = is_subset(b,a) + if c == false; return false; end + return true +end + +## TODO AbstractFaceTopology <: AbstractMeshTopology +abstract type AbstractFaceTopology <: GalerkinToolkitDataType end + +struct GenericFaceTopology{A,B} <: AbstractFaceTopology + boundary::A + vertex_permutations::B +end + +function face_topology(args...) + GenericFaceTopology(args...) +end + +num_dims(a::AbstractFaceTopology) = num_dims(boundary(a))+1 + +function topology(fe::AbstractMeshFace) + topology_from_mesh_face(fe) +end + +function topology_from_mesh_face(refface) + geom = geometry(refface) + D = num_dims(geom) + if D != 0 + myboundary = geom |> boundary |> topology + else + myboundary = nothing + end + myperms = vertex_permutations(geom) + face_topology(myboundary,myperms) +end + +function complexify(mesh::AbstractFEMesh) + complexify_mesh(mesh) +end + +function complexify_mesh(mesh) + Ti = Int32 + T = JaggedArray{Ti,Ti} + D = num_dims(mesh) + oldface_to_newvertices = Vector{T}(undef,D+1) + newvertex_to_oldfaces = Vector{T}(undef,D+1) + newface_incidence = Matrix{T}(undef,D+1,D+1) + nnewfaces = zeros(Int,D+1) + newface_refid = Vector{Vector{Ti}}(undef,D+1) + newreffaces = Vector{Any}(undef,D+1) + newface_nodes = Vector{T}(undef,D+1) + old_to_new = Vector{Vector{Ti}}(undef,D+1) + node_to_newvertex, n_new_vertices = find_node_to_vertex(mesh) # Optimizable for linear meshes + for d in 0:D + oldface_to_newvertices[d+1] = fill_face_vertices(mesh,d,node_to_newvertex) # Optimizable for linear meshes + newvertex_to_oldfaces[d+1] = generate_face_coboundary(oldface_to_newvertices[d+1],n_new_vertices) # Optimizable for linear meshes + end + newface_incidence[D+1,0+1] = oldface_to_newvertices[D+1] + newface_incidence[0+1,D+1] = newvertex_to_oldfaces[D+1] + nnewfaces[D+1] = length(oldface_to_newvertices[D+1]) + newface_refid[D+1] = face_reference_id(mesh,D) + newreffaces[D+1] = reference_faces(mesh,D) + newface_nodes[D+1] = face_nodes(mesh,D) + old_to_new[D+1] = collect(Ti,1:length(newface_nodes[D+1])) + # TODO optimize for d==0 + for d in (D-1):-1:0 + n = d+1 + new_nface_to_new_vertices = newface_incidence[n+1,0+1] + new_vertex_to_new_nfaces = newface_incidence[0+1,n+1] + old_dface_to_new_vertices = oldface_to_newvertices[d+1] + new_vertex_to_old_dfaces = newvertex_to_oldfaces[d+1] + new_nface_to_nrefid = newface_refid[n+1] + old_dface_to_drefid = face_reference_id(mesh,d) + drefid_to_ref_dface = reference_faces(mesh,d) + old_dface_to_nodes = face_nodes(mesh,d) + new_nface_to_nodes = newface_nodes[n+1] + nrefid_to_ldface_to_lvertices = map(a->face_incidence(topology(boundary(geometry(a))),d,0),newreffaces[n+1]) + nrefid_to_ldface_to_lnodes = map(a->face_nodes(boundary(a),d),newreffaces[n+1]) + nrefid_to_ldface_to_drefrefid = map(a->face_reference_id(boundary(a),d),newreffaces[n+1]) + nrefid_to_drefrefid_to_ref_dface = map(a->reference_faces(boundary(a),d),newreffaces[n+1]) + new_nface_to_new_dfaces, n_new_dfaces, old_dface_to_new_dface = generate_face_boundary( + new_nface_to_new_vertices, + new_vertex_to_new_nfaces, + old_dface_to_new_vertices, + new_vertex_to_old_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_lvertices) + new_dface_to_new_vertices = generate_face_vertices( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_new_vertices, + new_nface_to_new_vertices, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_lvertices) + new_vertex_to_new_dfaces = generate_face_coboundary(new_dface_to_new_vertices,n_new_vertices) + new_dface_to_nodes = generate_face_vertices( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_nodes, + new_nface_to_nodes, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_lnodes) + new_dface_to_new_drefid, new_refid_to_ref_dface = generate_reference_faces( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_drefid, + drefid_to_ref_dface, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_drefrefid, + nrefid_to_drefrefid_to_ref_dface) + newface_incidence[n+1,d+1] = new_nface_to_new_dfaces + newface_incidence[d+1,0+1] = new_dface_to_new_vertices + newface_incidence[0+1,d+1] = new_vertex_to_new_dfaces + newface_refid[d+1] = new_dface_to_new_drefid + newreffaces[d+1] = new_refid_to_ref_dface + nnewfaces[d+1] = n_new_dfaces + newface_nodes[d+1] = new_dface_to_nodes + old_to_new[d+1] = old_dface_to_new_dface + end + node_to_coords = node_coordinates(mesh) + old_physical_faces = physical_faces(mesh) + new_physical_faces = [ Dict{String,Vector{Int32}}() for d in 0:D] # TODO hardcoded + for d in 0:D + old_groups = old_physical_faces[d+1] + for (group_name,old_group_faces) in old_groups + new_group_faces = similar(old_group_faces) + new_group_faces .= old_to_new[d+1][old_group_faces] + new_physical_faces[d+1][group_name] = new_group_faces + end + end + new_mesh = fe_mesh( + node_to_coords, + newface_nodes, + newface_refid, + Tuple(newreffaces); + physical_faces = new_physical_faces, + periodic_nodes = periodic_nodes(mesh), + outwards_normals = outwards_normals(mesh) + ) + new_mesh, old_to_new +end + +function generate_face_vertices( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_new_vertices, + new_nface_to_new_vertices, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_lvertices + ) + + Ti = eltype(eltype(old_dface_to_new_vertices)) + new_dface_to_touched = fill(false,n_new_dfaces) + new_dface_to_new_vertices_ptrs = zeros(Ti,n_new_dfaces+1) + n_old_dfaces = length(old_dface_to_new_dface) + for old_dface in 1:n_old_dfaces + new_dface = old_dface_to_new_dface[old_dface] + new_vertices = old_dface_to_new_vertices[old_dface] + new_dface_to_new_vertices_ptrs[new_dface+1] = length(new_vertices) + new_dface_to_touched[new_dface] = true + end + n_new_nfaces = length(new_nface_to_new_vertices) + for new_nface in 1:n_new_nfaces + nrefid = new_nface_to_nrefid[new_nface] + ldface_to_lvertices = nrefid_to_ldface_to_lvertices[nrefid] + ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] + n_ldfaces = length(ldface_to_new_dface) + for ldface in 1:n_ldfaces + new_dface = ldface_to_new_dface[ldface] + if new_dface_to_touched[new_dface] + continue + end + lvertices = ldface_to_lvertices[ldface] + new_dface_to_new_vertices_ptrs[new_dface+1] = length(lvertices) + new_dface_to_touched[new_dface] = true + end + + end + length_to_ptrs!(new_dface_to_new_vertices_ptrs) + ndata = new_dface_to_new_vertices_ptrs[end]-1 + new_dface_to_new_vertices_data = zeros(Ti,ndata) + new_dface_to_new_vertices = JaggedArray(new_dface_to_new_vertices_data,new_dface_to_new_vertices_ptrs) + fill!(new_dface_to_touched,false) + for old_dface in 1:n_old_dfaces + new_dface = old_dface_to_new_dface[old_dface] + new_vertices_in = old_dface_to_new_vertices[old_dface] + new_vertices_out = new_dface_to_new_vertices[new_dface] + for i in 1:length(new_vertices_in) + new_vertices_out[i] = new_vertices_in[i] + end + new_dface_to_touched[new_dface] = true + end + for new_nface in 1:n_new_nfaces + nrefid = new_nface_to_nrefid[new_nface] + ldface_to_lvertices = nrefid_to_ldface_to_lvertices[nrefid] + ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] + n_ldfaces = length(ldface_to_new_dface) + new_vertices_in = new_nface_to_new_vertices[new_nface] + for ldface in 1:n_ldfaces + new_dface = ldface_to_new_dface[ldface] + if new_dface_to_touched[new_dface] + continue + end + new_vertices_out = new_dface_to_new_vertices[new_dface] + lvertices = ldface_to_lvertices[ldface] + for i in 1:length(lvertices) + new_vertices_out[i] = new_vertices_in[lvertices[i]] + end + new_dface_to_touched[new_dface] = true + end + + end + new_dface_to_new_vertices +end + +function generate_reference_faces( + n_new_dfaces, + old_dface_to_new_dface, + old_dface_to_drefid, + drefid_to_ref_dface, + new_nface_to_new_dfaces, + new_nface_to_nrefid, + nrefid_to_ldface_to_drefrefid, + nrefid_to_drefrefid_to_ref_dface) + + i_to_ref_dface = collect(Any,drefid_to_ref_dface) + drefid_to_i = collect(1:length(drefid_to_ref_dface)) + i = length(i_to_ref_dface) + Ti = Int32 + nrefid_to_drefrefid_to_i = map(a->zeros(Ti,length(a)),nrefid_to_drefrefid_to_ref_dface) + for (nrefid,drefrefid_to_ref_dface) in enumerate(nrefid_to_drefrefid_to_ref_dface) + for (drefrefid, ref_dface) in enumerate(drefrefid_to_ref_dface) + push!(i_to_ref_dface,ref_dface) + i += 1 + nrefid_to_drefrefid_to_i[nrefid][drefrefid] = i + end + end + u_to_ref_dface = unique(i_to_ref_dface) + i_to_u = indexin(i_to_ref_dface,u_to_ref_dface) + new_dface_to_u = zeros(Ti,n_new_dfaces) + new_dface_to_touched = fill(false,n_new_dfaces) + for (old_dface,new_dface) in enumerate(old_dface_to_new_dface) + drefid = old_dface_to_drefid[old_dface] + i = drefid_to_i[drefid] + u = i_to_u[i] + new_dface_to_u[new_dface] = u + new_dface_to_touched[new_dface] = true + end + for (new_nface,nrefid) in enumerate(new_nface_to_nrefid) + ldface_to_drefrefid = nrefid_to_ldface_to_drefrefid[nrefid] + ldface_to_new_dface = new_nface_to_new_dfaces[new_nface] + drefrefid_to_i = nrefid_to_drefrefid_to_i[nrefid] + for (ldface,new_dface) in enumerate(ldface_to_new_dface) + new_dface = ldface_to_new_dface[ldface] + if new_dface_to_touched[new_dface] + continue + end + drefrefid = ldface_to_drefrefid[ldface] + i = drefrefid_to_i[drefrefid] + u = i_to_u[i] + new_dface_to_u[new_dface] = u + new_dface_to_touched[new_dface] = true + end + end + new_dface_to_u, Tuple(u_to_ref_dface) +end + +function generate_face_boundary( + Dface_to_vertices, + vertex_to_Dfaces, + dface_to_vertices, + vertex_to_dfaces, + Dface_to_refid, + Drefid_to_ldface_to_lvertices) + + # Count + ndfaces = length(dface_to_vertices) + nDfaces = length(Dface_to_vertices) + nvertices = length(vertex_to_Dfaces) + maxldfaces = 0 + for ldface_to_lvertices in Drefid_to_ldface_to_lvertices + maxldfaces = max(maxldfaces,length(ldface_to_lvertices)) + end + maxDfaces = 0 + for vertex in 1:length(vertex_to_Dfaces) + Dfaces = vertex_to_Dfaces[vertex] + maxDfaces = max(maxDfaces,length(Dfaces)) + end + # Allocate output + ptrs = zeros(Int32,nDfaces+1) + for Dface in 1:nDfaces + Drefid = Dface_to_refid[Dface] + ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] + ptrs[Dface+1] = length(ldface_to_lvertices) + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = fill(Int32(INVALID_ID),ndata) + Dface_to_dfaces = GenericJaggedArray(data,ptrs) + # Main loop + Dfaces1 = fill(Int32(INVALID_ID),maxDfaces) + Dfaces2 = fill(Int32(INVALID_ID),maxDfaces) + ldfaces1 = fill(Int32(INVALID_ID),maxDfaces) + nDfaces1 = 0 + nDfaces2 = 0 + newdface = Int32(ndfaces) + old_to_new = collect(Int32,1:ndfaces) + for Dface in 1:nDfaces + Drefid = Dface_to_refid[Dface] + ldface_to_lvertices = Drefid_to_ldface_to_lvertices[Drefid] + lvertex_to_vertex = Dface_to_vertices[Dface] + ldface_to_dface = Dface_to_dfaces[Dface] + for (ldface,lvertices) in enumerate(ldface_to_lvertices) + # Do nothing if this local face has already been processed by + # a neighbor + if ldface_to_dface[ldface] != Int32(INVALID_ID) + continue + end + # Find if there is already a global d-face for this local d-face + # if yes, then use the global id of this d-face + # if not, create a new one + dface2 = Int32(INVALID_ID) + fill!(Dfaces1,Int32(INVALID_ID)) + fill!(Dfaces2,Int32(INVALID_ID)) + vertices = view(lvertex_to_vertex,lvertices) + for (i,lvertex) in enumerate(lvertices) + vertex = lvertex_to_vertex[lvertex] + dfaces = vertex_to_dfaces[vertex] + for dface1 in dfaces + vertices1 = dface_to_vertices[dface1] + if same_valid_ids(vertices,vertices1) + dface2 = dface1 + break + end + end + if dface2 != Int32(INVALID_ID) + break + end + end + if dface2 == Int32(INVALID_ID) + newdface += Int32(1) + dface2 = newdface + end + # Find all D-faces around this local d-face + for (i,lvertex) in enumerate(lvertices) + vertex = lvertex_to_vertex[lvertex] + Dfaces = vertex_to_Dfaces[vertex] + if i == 1 + copyto!(Dfaces1,Dfaces) + nDfaces1 = length(Dfaces) + else + copyto!(Dfaces2,Dfaces) + nDfaces2 = length(Dfaces) + intersection!(Dfaces1,Dfaces2,nDfaces1,nDfaces2) + end + end + # Find their correspondent local d-face and set the d-face + for Dface1 in Dfaces1 + if Dface1 != INVALID_ID + Drefid1 = Dface_to_refid[Dface1] + lvertex1_to_vertex1 = Dface_to_vertices[Dface1] + ldface1_to_lvertices1 = Drefid_to_ldface_to_lvertices[Drefid1] + ldface2 = Int32(INVALID_ID) + for (ldface1,lvertices1) in enumerate(ldface1_to_lvertices1) + vertices1 = view(lvertex1_to_vertex1,lvertices1) + if same_valid_ids(vertices,vertices1) + ldface2 = ldface1 + break + end + end + @boundscheck @assert ldface2 != INVALID_ID + Dface_to_dfaces[Dface1][ldface2] = dface2 + end + end + end # (ldface,lvertices) + end # Dface + Dface_to_dfaces, newdface, old_to_new +end + +function fill_face_vertices(mesh,d,node_to_vertex) + function barrier(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) + Ti = eltype(node_to_vertex) + nfaces = length(face_to_nodes) + face_to_vertices_ptrs = zeros(Ti,nfaces+1) + for face in 1:nfaces + nodes = face_to_nodes[face] + refid = face_to_refid[face] + lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] + nlvertices = length(lvertex_to_lnodes) + face_to_vertices_ptrs[face+1] = nlvertices + end + length_to_ptrs!(face_to_vertices_ptrs) + ndata = face_to_vertices_ptrs[end]-1 + face_to_vertices_data = zeros(Ti,ndata) + face_to_vertices = JaggedArray(face_to_vertices_data,face_to_vertices_ptrs) + for face in 1:nfaces + vertices = face_to_vertices[face] + nodes = face_to_nodes[face] + refid = face_to_refid[face] + lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] + for (lvertex,lnodes) in enumerate(lvertex_to_lnodes) + lnode = first(lnodes) + vertex = node_to_vertex[nodes[lnode]] + @boundscheck @assert vertex != INVALID_ID + vertices[lvertex] = vertex + end + end + face_to_vertices + end + face_to_nodes = face_nodes(mesh,d) + face_to_refid = face_reference_id(mesh,d) + refid_to_lvertex_to_lnodes = map(reference_faces(mesh,d)) do a + if num_dims(geometry(a)) != 0 + face_nodes(boundary(a),0) + else + [interior_nodes(a)] + end + end + barrier(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) +end + +function find_node_to_vertex(mesh) + function barrier!(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) + valid_id = one(eltype(node_to_vertex)) + for face in eachindex(face_to_nodes) + nodes = face_to_nodes[face] + refid = face_to_refid[face] + lvertex_to_lnodes = refid_to_lvertex_to_lnodes[refid] + for lnodes in lvertex_to_lnodes + lnode = first(lnodes) + node = nodes[lnode] + node_to_vertex[node] = valid_id + end + end + end + Ti = Int32 + nnodes = num_nodes(mesh) + node_to_vertex = zeros(Ti,nnodes) + fill!(node_to_vertex,Ti(INVALID_ID)) + D = num_dims(mesh) + for d in 0:D + face_to_nodes = face_nodes(mesh,d) + if length(face_to_nodes) == 0 + continue + end + face_to_refid = face_reference_id(mesh,d) + refid_to_lvertex_to_lnodes = map(reference_faces(mesh,d)) do a + if num_dims(geometry(a)) != 0 + face_nodes(boundary(a),0) + else + [interior_nodes(a)] + end + end + barrier!(node_to_vertex,face_to_nodes,face_to_refid,refid_to_lvertex_to_lnodes) + end + vertex = Ti(0) + for node in eachindex(node_to_vertex) + if node_to_vertex[node] != Ti(INVALID_ID) + vertex += Ti(1) + node_to_vertex[node] = vertex + end + end + node_to_vertex, vertex +end + +function physical_nodes(mesh,d) + nnodes = num_nodes(mesh) + node_to_touched = fill(false,nnodes) + node_groups = Dict{String,Vector{Int32}}() + face_to_nodes = face_nodes(mesh,d) + for (name,faces) in physical_faces(mesh,d) + fill!(node_to_touched,false) + for face in faces + nodes = face_to_nodes[face] + node_to_touched[nodes] .= true + end + node_groups[name] = findall(node_to_touched) + end + node_groups +end + +function physical_nodes(mesh; + merge_dims=Val(false), + disjoint=Val(false), + name_priority=nothing) + + if val_parameter(disjoint) == true && val_parameter(merge_dims) == false + error("disjoint=true requires merge_dims=true") + end + D = num_dims(mesh) + d_to_groups = [ physical_nodes(mesh,d) for d in 0:D ] + if val_parameter(merge_dims) == false + return d_to_groups + end + names = physical_names(mesh;merge_dims) + nnodes = num_nodes(mesh) + node_groups = Dict{String,Vector{Int32}}() + + if val_parameter(disjoint) == false + node_to_touched = fill(false,nnodes) + for name in names + fill!(node_to_touched,false) + for groups in d_to_groups + for (name2,nodes) in groups + if name != name2 + continue + end + node_to_touched[nodes] .= true + end + end + node_groups[name] = findall(node_to_touched) + end + else + if name_priority === nothing + tag_to_name = sort(collect(names)) + else + tag_to_name = name_priority + end + node_to_tag = zeros(Int32,nnodes) + classify_mesh_nodes!(node_to_tag,mesh,tag_to_name) + for (tag,name) in enumerate(tag_to_name) + node_groups[name] = findall(t->t==tag,node_to_tag) + end + end + node_groups +end + +function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) + fill!(node_to_tag,zero(eltype(node_to_tag))) + for d in dmax:-1:0 + face_to_nodes = face_nodes(mesh,d) + face_groups = physical_faces(mesh,d) + for (tag,name) in enumerate(tag_to_name) + for (name2,faces) in face_groups + if name != name2 + continue + end + for face in faces + nodes = face_to_nodes[face] + node_to_tag[nodes] .= tag + end + end + end + end + node_to_tag +end + +function physical_names(mesh,d) + groups = physical_faces(mesh,d) + Set(keys(groups)) +end + +function physical_names(mesh;merge_dims=Val(false)) + D = num_dims(mesh) + d_to_names = [ physical_names(mesh,d) for d in 0:D] + if val_parameter(merge_dims) == false + return d_to_names + end + reduce(union,d_to_names) +end + +abstract type AbstractFEChain <: GalerkinToolkitDataType end + +struct GenericFEChain{A,B,C,D,E,F,G} <: AbstractFEChain + node_coordinates::A + face_nodes::B + face_reference_id::C + reference_faces::D + periodic_nodes::E + physical_faces::F + outwards_normals::G +end + +function fe_chain(args...) + GenericFEChain(args...) +end + +function fe_chain( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces; + periodic_nodes = eltype(eltype(face_reference_id))[], + physical_faces = Dict{String,Vector{eltype(eltype(face_reference_id))}}(), + outwards_normals = nothing + ) + fe_chain( + node_coordinates, + face_nodes, + face_reference_id, + reference_faces, + periodic_nodes, + physical_faces, + outwards_normals) +end + +num_dims(mesh::AbstractFEChain) = num_dims(first(reference_faces(mesh))) + +function fe_mesh(chain::AbstractFEChain) + mesh_from_chain(chain) +end + +function mesh_from_chain(chain) + D = num_dims(chain) + cell_nodes = face_nodes(chain) + cell_reference_id = face_reference_id(chain) + reference_cells = reference_faces(chain) + node_coords = node_coordinates(chain) + face_to_nodes = Vector{typeof(cell_nodes)}(undef,D+1) + face_to_refid = Vector{typeof(cell_reference_id)}(undef,D+1) + for d in 0:D-1 + face_to_nodes[d+1] = Vector{Int}[] + face_to_refid[d+1] = Int[] + end + face_to_nodes[end] = cell_nodes + face_to_refid[end] = cell_reference_id + ref_cell = first(reference_cells) + ref_faces = reference_faces(boundary(ref_cell)) + refid_to_refface = push(ref_faces,reference_cells) + cell_groups = physical_faces(chain) + groups = [ typeof(cell_groups)() for d in 0:D] + groups[end] = cell_groups + pnodes = periodic_nodes(chain) + onormals = outwards_normals(chain) + fe_mesh( + node_coords, + face_to_nodes, + face_to_refid, + refid_to_refface; + periodic_nodes = pnodes, + physical_faces = groups, + outwards_normals = onormals) +end + +function simplexify(geo::AbstractFaceGeometry) + simplexify_face_geometry(geo) +end + +function simplexify_face_geometry(geo) + if is_unit_simplex(geo) + simplexify_unit_simplex(geo) + elseif is_unit_n_cube(geo) + simplexify_unit_n_cube(geo) + else + error("case not implemented") + end +end + +function simplexify_unit_simplex(geo) + @assert is_unit_simplex(geo) + refface = lagrangian_reference_face(geo) + mesh_from_reference_face(refface) +end + +function simplexify_unit_n_cube(geo) + @assert is_unit_n_cube(geo) + D = num_dims(geo) + if D in (0,1) + return simplexify_unit_simplex(geo) + end + simplex = unit_simplex(Val(D)) + order = 1 + ref_cell = lagrangian_fe(simplex,order) + node_coords = node_coordinates(boundary(geo)) + cell_nodes = simplex_node_ids_n_cube(geo) + ncells = length(cell_nodes) + cell_reference_id = fill(Int8(1),ncells) + reference_cells = [ref_cell] + chain = fe_chain( + node_coords, + cell_nodes, + cell_reference_id, + reference_cells, + ) + mesh = mesh_from_chain(chain) + mesh_complex, = complexify(mesh) + groups = [Dict{String,Vector{Int32}}() for d in 0:D] + for d in 0:(D-1) + sface_to_nodes = face_nodes(mesh_complex,d) + cface_to_nodes = face_nodes(boundary(geo),d) + nsfaces = length(sface_to_nodes) + ncfaces = length(cface_to_nodes) + sface_touched = fill(false,nsfaces) + for cface in 1:ncfaces + fill!(sface_touched,false) + nodes_c = cface_to_nodes[cface] + for sface in 1:nsfaces + nodes_s = sface_to_nodes[sface] + if all(map(n->n in nodes_c,nodes_s)) + sface_touched[sface] = true + end + end + sfaces_in_group = findall(sface_touched) + group_name = "$d-face-$cface" + groups[d+1][group_name] = sfaces_in_group + end + end + groups[end]["interior"] = 1:(num_faces(mesh_complex,D)) + sface_is_boundary = fill(false,num_faces(mesh_complex,D-1)) + for (_,sfaces) in groups[end-1] + sface_is_boundary[sfaces] .= true + end + groups[end-1]["boundary"] = findall(sface_is_boundary) + physical_faces(mesh_complex) .= groups + mesh_complex +end + +function simplex_node_ids_n_cube(geo) + D = num_dims(geo) + # TODO check orientation of nodes + # this assumes lexicographic ordering + # for 3d nodes ids are carefully selected + # such that opposite faces match. + # This allows one to complexify meshes + # of ncubes with oriented faces + if D == 0 + [[1]] + elseif D == 1 + [[1,2]] + elseif D == 2 + [[1,2,3],[2,3,4]] + elseif D ==3 + [[1,2,3,7], [1,2,5,7], [2,3,4,7], + [2,4,7,8], [2,5,6,7], [2,6,7,8]] + else + error("case not implemented") + end +end + +function simplexify_reference_face(ref_face) + mesh_geom = simplexify(geometry(ref_face)) + D = num_dims(mesh_geom) + node_coordinates_geom = node_coordinates(mesh_geom) + ref_faces_geom = reference_faces(mesh_geom,D) + face_nodes_geom = face_nodes(mesh_geom,D) + face_ref_id_geom = face_reference_id(mesh_geom,D) + nfaces = length(face_ref_id_geom) + # We need the same order in all directions + # for this to make sense + my_order = order(ref_face) + ref_faces_inter = map(r_geom->lagrangian_reference_face(geometry(r_geom),order=my_order),ref_faces_geom) + s_ref = map(ref_faces_geom,ref_faces_inter) do r_geom,r + m = num_nodes(r) + n = num_nodes(r_geom) + x = node_coordinates(r) + tabulator(r_geom)(value,x) + end + node_coordinates_inter = node_coordinates(ref_face) + node_coordinates_aux = map(xi->map(xii->round(Int,my_order*xii),xi),node_coordinates_inter) + face_nodes_inter = Vector{Vector{Int}}(undef,nfaces) + for face in 1:nfaces + ref_id_geom = face_ref_id_geom[face] + s = s_ref[ref_id_geom] + nodes_geom = face_nodes_geom[face] + nnodes, nnodes_geom = size(s) + x_mapped = map(1:nnodes) do i + x = zero(eltype(node_coordinates_inter)) + for k in 1:nnodes_geom + x += node_coordinates_geom[nodes_geom[k]]*s[i,k] + end + map(xi->round(Int,my_order*xi),x) + end + my_nodes = indexin(x_mapped,node_coordinates_aux) + face_nodes_inter[face] = my_nodes + end + ref_inter = + chain = Chain(; + num_dims=Val(D), + node_coordinates=node_coordinates_inter, + face_nodes = face_nodes_inter, + face_reference_id = face_ref_id_geom, + reference_faces = ref_faces_inter, + ) + mesh = mesh_from_chain(chain) + mesh_complex, = complexify(mesh) + pg = physical_faces(mesh_complex) + pg .= physical_faces(mesh_geom) + mesh_complex +end + +function cartesian_mesh(domain,cells_per_dir;boundary=true,complexify=true,simplexify=false) + mesh = if boundary + if simplexify + structured_simplex_mesh_with_boundary(domain,cells_per_dir) + else + cartesian_mesh_with_boundary(domain,cells_per_dir) + end + else + if simplexify + chain = structured_simplex_chain(domain,cells_per_dir) + else + chain = cartesian_chain(domain,cells_per_dir) + end + mesh_from_chain(chain) + end + if complexify + mesh, = GalerkinToolkit.complexify(mesh) + end + mesh +end + +function bounding_box_from_domain(domain) + l = length(domain) + D = div(l,2) + pmin = SVector(ntuple(d->domain[2*(d-1)+1],Val(D))) + pmax = SVector(ntuple(d->domain[2*d],Val(D))) + (pmin,pmax) +end + +function domain_from_bounding_box(box) + l = sum(length,box) + ntuple(Val(l)) do i + vector = mod(i-1,2)+1 + component = div(i-1,2)+1 + box[vector][component] + end +end + +function cartesian_mesh_with_boundary(domain,cells_per_dir) + function barrier( + D, + cell_to_nodes, + nnodes, + d_to_ldface_to_lnodes) + + node_to_n = zeros(Int32,nnodes) + for nodes in cell_to_nodes + for node in nodes + node_to_n[node] += Int32(1) + end + end + J = typeof(JaggedArray(Vector{Int32}[])) + face_to_nodes = Vector{J}(undef,D) + groups = [ Dict{String,Vector{Int32}}() for d in 0:(D-1) ] + ngroups = 0 + for d in 0:(D-1) + nmax = 2^d + ldface_to_lnodes = d_to_ldface_to_lnodes[d+1] + ndfaces = 0 + for nodes in cell_to_nodes + for (ldface,lnodes) in enumerate(ldface_to_lnodes) + isboundary = true + for lnode in lnodes + node = nodes[lnode] + if node_to_n[node] > nmax + isboundary = false + break + end + end + if isboundary + ndfaces += 1 + end + end + end + ptrs = zeros(Int32,ndfaces+1) + for dface in 1:ndfaces + ptrs[dface+1] += Int32(nmax) + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = zeros(Int32,ndata) + dface_to_physical_group = zeros(Int32,ndfaces) + ndfaces = 0 + for nodes in cell_to_nodes + for (ldface,lnodes) in enumerate(ldface_to_lnodes) + isboundary = true + for lnode in lnodes + node = nodes[lnode] + if node_to_n[node] > nmax + isboundary = false + break + end + end + if isboundary + ndfaces += 1 + group = ngroups + ldface + dface_to_physical_group[ndfaces] = group + p = ptrs[ndfaces]-Int32(1) + for (i,lnode) in enumerate(lnodes) + node = nodes[lnode] + data[p+i] = node + end + end + end + end + nldfaces = length(ldface_to_lnodes) + face_to_nodes[d+1] = JaggedArray(data,ptrs) + for ldface in 1:nldfaces + group = ngroups + ldface + group_name = "$(d)-face-$ldface" + faces_in_physical_group = findall(g->g==group,dface_to_physical_group) + groups[d+1][group_name] = faces_in_physical_group + end + ngroups += nldfaces + if d == (D-1) + groups[d+1]["boundary"] = 1:length(dface_to_physical_group) + end + end # d + ngroups += 1 + groups, face_to_nodes + end # barrier + chain = cartesian_chain(domain,cells_per_dir) + interior_mesh = mesh_from_chain(chain) + D = num_dims(interior_mesh) + cell_to_nodes = face_nodes(interior_mesh,D) + reference_cells = reference_faces(interior_mesh,D) + node_coords = node_coordinates(interior_mesh) + ref_cell = first(reference_cells) + refid_to_refface = reference_faces(boundary(ref_cell)) + nnodes = num_nodes(interior_mesh) + d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] + groups, face_to_nodes = barrier( + D, + cell_to_nodes, + nnodes, + d_to_ldface_to_lnodes) + face_to_refid = [ ones(Int8,length(face_to_nodes[d+1])) for d in 0:(D-1)] + mesh_face_nodes = push(face_to_nodes,face_nodes(interior_mesh,D)) + mesh_face_reference_id = push(face_to_refid,face_reference_id(interior_mesh,D)) + mesh_reference_faces = push(refid_to_refface,reference_faces(interior_mesh,D)) + mesh_groups = push(groups,physical_faces(interior_mesh,D)) + fe_mesh( + node_coords, + mesh_face_nodes, + mesh_face_reference_id, + mesh_reference_faces; + physical_faces=mesh_groups, + ) +end + +function cartesian_chain(domain,cells_per_dir) + box = bounding_box_from_domain(domain) + D = length(cells_per_dir) + nodes_per_dir = cells_per_dir .+ 1 + pmin = first(box) + pmax = last(box) + extent_per_dir = pmax .- pmin + h_per_dir = SVector(extent_per_dir ./ cells_per_dir) + nnodes = prod(nodes_per_dir) + ncells = prod(cells_per_dir) + nlnodes = 2^D + cell_nodes_ptrs = fill(Int32(nlnodes),ncells+1) + cell_nodes_ptrs[1] = 0 + length_to_ptrs!(cell_nodes_ptrs) + cell_nodes_data = zeros(Int32,ncells*nlnodes) + cell_nodes = JaggedArray(cell_nodes_data,cell_nodes_ptrs) + cell_cis = CartesianIndices(cells_per_dir) + cell_lis = LinearIndices(cells_per_dir) + node_cis = CartesianIndices(nodes_per_dir) + node_lis = LinearIndices(nodes_per_dir) + lnode_cis = CartesianIndices(ntuple(i->0:1,Val(D))) + for (cell_li,cell_ci) in enumerate(cell_cis) + nodes = cell_nodes[cell_li] + for (lnode_li,lnode_ci) in enumerate(lnode_cis) + node_ci = CartesianIndex(Tuple(cell_ci) .+ Tuple(lnode_ci)) + node_li = node_lis[node_ci] + nodes[lnode_li] = node_li + end + end + node_coords = zeros(SVector{D,Float64},nnodes) + for (node_li,node_ci) in enumerate(node_cis) + anchor = SVector(Tuple(node_ci) .- 1) + x = pmin .+ h_per_dir .* anchor + node_coords[node_li] = x + end + cell_reference_id = fill(Int32(1),ncells) + cell_geometry = unit_n_cube(Val(D)) + order = 1 + ref_cell = lagrangian_fe(cell_geometry,order) + reference_cells = [ref_cell] + interior_cells = collect(Int32,1:length(cell_nodes)) + groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) + chain = fe_chain( + node_coords, + cell_nodes, + cell_reference_id, + reference_cells; + physical_faces=groups, + ) + chain +end + +function structured_simplex_chain(domain,cells_per_dir) + box = bounding_box_from_domain(domain) + D = length(cells_per_dir) + nodes_per_dir = cells_per_dir .+ 1 + pmin = first(box) + pmax = last(box) + extent_per_dir = pmax .- pmin + h_per_dir = SVector(extent_per_dir ./ cells_per_dir) + nnodes = prod(nodes_per_dir) + nlnodes = 2^D + cell_geometry = unit_n_cube(Val(D)) + ref_simplex_mesh = simplexify(cell_geometry) + rscell_to_lnodes = face_nodes(ref_simplex_mesh,D) + nrscells = length(rscell_to_lnodes) + nslnodes = length(first(rscell_to_lnodes)) + ncells = prod(cells_per_dir)*nrscells + cell_nodes_ptrs = fill(Int32(nslnodes),ncells+1) + cell_nodes_ptrs[1] = 0 + length_to_ptrs!(cell_nodes_ptrs) + ndata = cell_nodes_ptrs[end]-1 + cell_nodes_data = zeros(Int32,ndata) + cell_nodes = JaggedArray(cell_nodes_data,cell_nodes_ptrs) + cell_cis = CartesianIndices(cells_per_dir) + cell_lis = LinearIndices(cells_per_dir) + node_cis = CartesianIndices(nodes_per_dir) + node_lis = LinearIndices(nodes_per_dir) + lnode_cis = CartesianIndices(ntuple(i->0:1,Val(D))) + clnodes = zeros(Int,nlnodes) + scell = 0 + for (cell_li,cell_ci) in enumerate(cell_cis) + for (lnode_li,lnode_ci) in enumerate(lnode_cis) + node_ci = CartesianIndex(Tuple(cell_ci) .+ Tuple(lnode_ci)) + node_li = node_lis[node_ci] + clnodes[lnode_li] = node_li + end + for srcell in 1:nrscells + scell += 1 + nodes = cell_nodes[scell] + lnodes = rscell_to_lnodes[srcell] + for (i,lnode) in enumerate(lnodes) + nodes[i] = clnodes[lnode] + end + end + end + node_coords = zeros(SVector{D,Float64},nnodes) + for (node_li,node_ci) in enumerate(node_cis) + anchor = SVector(Tuple(node_ci) .- 1) + x = pmin .+ h_per_dir .* anchor + node_coords[node_li] = x + end + cell_reference_id = fill(Int32(1),ncells) + order = 1 + reference_cells = reference_faces(ref_simplex_mesh,D) + interior_cells = collect(Int32,1:length(cell_nodes)) + groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) + chain = fe_chain( + node_coords, + cell_nodes, + cell_reference_id, + reference_cells; + physical_faces=groups, + ) + chain +end + +function structured_simplex_mesh_with_boundary(domain,cells_per_dir) + function barrier( + D, + cell_to_nodes, + nnodes, + d_to_ldface_to_lnodes, + d_to_ldface_to_sldface_to_lnodes) + + node_to_n = zeros(Int32,nnodes) + for nodes in cell_to_nodes + for node in nodes + node_to_n[node] += Int32(1) + end + end + J = typeof(JaggedArray(Vector{Int32}[])) + face_to_nodes = Vector{J}(undef,D) + groups = [ Dict{String,Vector{Int32}}() for d in 0:(D-1) ] + ngroups = 0 + for d in 0:(D-1) + nmax = 2^d + ldface_to_lnodes = d_to_ldface_to_lnodes[d+1] + ldface_to_sldface_to_lnodes = d_to_ldface_to_sldface_to_lnodes[d+1] + ndfaces = 0 + for nodes in cell_to_nodes + for (ldface,lnodes) in enumerate(ldface_to_lnodes) + isboundary = true + for lnode in lnodes + node = nodes[lnode] + if node_to_n[node] > nmax + isboundary = false + break + end + end + if isboundary + ndfaces += length(ldface_to_sldface_to_lnodes[ldface]) + end + end + end + nslnodes = length(ldface_to_sldface_to_lnodes[begin][begin]) + ptrs = zeros(Int32,ndfaces+1) + for dface in 1:ndfaces + ptrs[dface+1] += Int32(nslnodes) + end + length_to_ptrs!(ptrs) + ndata = ptrs[end]-1 + data = zeros(Int32,ndata) + dface_to_physical_group = zeros(Int32,ndfaces) + ndfaces = 0 + for nodes in cell_to_nodes + for (ldface,lnodes) in enumerate(ldface_to_lnodes) + isboundary = true + for lnode in lnodes + node = nodes[lnode] + if node_to_n[node] > nmax + isboundary = false + break + end + end + if isboundary + group = ngroups + ldface + sldface_to_lnodes = ldface_to_sldface_to_lnodes[ldface] + nsldfaces = length(sldface_to_lnodes) + for sldface in 1:nsldfaces + ndfaces += 1 + dface_to_physical_group[ndfaces] = group + p = ptrs[ndfaces]-Int32(1) + mylnodes = sldface_to_lnodes[sldface] + for (i,lnode) in enumerate(mylnodes) + node = nodes[lnode] + data[p+i] = node + end + end + end + end + end + nldfaces = length(ldface_to_lnodes) + face_to_nodes[d+1] = JaggedArray(data,ptrs) + for ldface in 1:nldfaces + group = ngroups + ldface + group_name = "$(d)-face-$ldface" + faces_in_physical_group = findall(g->g==group,dface_to_physical_group) + groups[d+1][group_name] = faces_in_physical_group + end + ngroups += nldfaces + if d == (D-1) + groups[d+1]["boundary"] = 1:length(dface_to_physical_group) + end + end # d + ngroups += 1 + groups, face_to_nodes + end # barrier + chain = cartesian_chain(domain,cells_per_dir) + interior_mesh = mesh_from_chain(chain) + D = num_dims(interior_mesh) + cell_to_nodes = face_nodes(interior_mesh,D) + reference_cells = reference_faces(interior_mesh,D) + ref_cell = first(reference_cells) + refid_to_refface = reference_faces(boundary(ref_cell)) + nnodes = num_nodes(interior_mesh) + + cell_geometry = unit_n_cube(Val(D)) + ref_simplex_mesh = simplexify(cell_geometry) + d_to_ldface_to_sldface_to_lnodes = [ + [ face_nodes(ref_simplex_mesh,d)[physical_faces(ref_simplex_mesh,d)["$d-face-$ldface"]] + for ldface in 1:num_faces(boundary(ref_cell),d) ] for d in 0:(D-1)] + d_to_ldface_to_lnodes = [face_nodes(boundary(ref_cell),d) for d in 0:(D-1)] + groups, face_to_nodes = barrier( + D, + cell_to_nodes, + nnodes, + d_to_ldface_to_lnodes, + d_to_ldface_to_sldface_to_lnodes + ) + simplex_chain = structured_simplex_chain(domain,cells_per_dir) + node_coords = node_coordinates(simplex_chain) + face_to_refid = [ ones(Int8,length(face_to_nodes[d+1])) for d in 0:(D-1)] + mesh_face_nodes = push(face_to_nodes,face_nodes(simplex_chain)) + mesh_face_reference_id = push(face_to_refid,face_reference_id(simplex_chain)) + mesh_reference_faces = reference_faces(ref_simplex_mesh) + mesh_groups = push(groups,physical_faces(simplex_chain)) + fe_mesh( + node_coords, + mesh_face_nodes, + mesh_face_reference_id, + mesh_reference_faces; + physical_faces=mesh_groups, + ) +end + +function visualization_mesh(mesh::AbstractFEMesh,args...;kwargs...) + visualization_mesh_from_mesh(mesh,args...;kwargs...) +end + +function visualization_mesh_from_mesh(mesh,dim=num_dims(mesh);order=nothing,resolution=nothing) + function barrier( + refid_to_tabulation, + refid_to_scell_to_snodes, + refid_to_scell_to_srefid, + refid_to_srefid_to_oid, + refid_to_srefid_to_vrefface, + refid_to_snode_to_coords, + node_to_coords, + cell_to_nodes, + cell_to_refid, + ::Val{Dn}) where Dn + + ncells = length(cell_to_refid) + nvnodes = 0 + nvcells = 0 + for cell in 1:ncells + refid = cell_to_refid[cell] + nvnodes += length(refid_to_snode_to_coords[refid]) + nvcells += length(refid_to_scell_to_srefid[refid]) + + end + nrefids = length(refid_to_srefid_to_oid) + i_to_oid = reduce(vcat,refid_to_srefid_to_oid) + i_to_vrefface = reduce(vcat,refid_to_srefid_to_vrefface) + refid_to_srefid_to_i = Vector{Vector{Int}}(undef,nrefids) + i = 0 + for refid in 1:nrefids + srefid_to_oid = refid_to_srefid_to_oid[refid] + nsrefids = length(srefid_to_oid) + srefid_to_i = zeros(Int,nsrefids) + for srefid in 1:nsrefids + i += 1 + srefid_to_i[srefid] = i + end + refid_to_srefid_to_i[refid] = srefid_to_i + end + vrefid_to_oid = unique(i_to_oid) + i_to_vrefid = indexin(i_to_oid,vrefid_to_oid) + vrefid_to_i = indexin(vrefid_to_oid,i_to_oid) + vrefid_to_vrefface = i_to_vrefface[vrefid_to_i] + Tx = SVector{Dn,Float64} + vnode_to_coords = zeros(Tx,nvnodes) + vcell_to_vnodes_ptrs = zeros(Int32,nvcells+1) + vcell_to_vrefid = zeros(Int32,nvcells) + vcell_to_cell = zeros(Int32,nvcells) + cell_to_vnodes = fill(0:1,ncells) + vcell = 0 + for cell in 1:ncells + refid = cell_to_refid[cell] + scell_to_snodes = refid_to_scell_to_snodes[refid] + nscells = length(scell_to_snodes) + scell_to_srefid = refid_to_scell_to_srefid[refid] + srefid_to_i = refid_to_srefid_to_i[refid] + for scell in 1:nscells + srefid = scell_to_srefid[scell] + i = srefid_to_i[srefid] + vrefid = i_to_vrefid[i] + snodes = scell_to_snodes[scell] + vcell += 1 + vcell_to_vnodes_ptrs[vcell+1] = length(snodes) + vcell_to_vrefid[vcell] = vrefid + vcell_to_cell[vcell] = cell + end + end + length_to_ptrs!(vcell_to_vnodes_ptrs) + ndata = vcell_to_vnodes_ptrs[end]-1 + vcell_to_vnodes_data = zeros(Int32,ndata) + vcell = 0 + vnode = 0 + vnode_prev = 1 + for cell in 1:ncells + refid = cell_to_refid[cell] + scell_to_snodes = refid_to_scell_to_snodes[refid] + nscells = length(scell_to_snodes) + for scell in 1:nscells + snodes = scell_to_snodes[scell] + vcell += 1 + p = vcell_to_vnodes_ptrs[vcell] + for (i,snode) in enumerate(snodes) + vcell_to_vnodes_data[p-1+i] = snode + vnode + end + end + tabulation = refid_to_tabulation[refid] + nsnodes = size(tabulation,1) + nodes = cell_to_nodes[cell] + for snode in 1:nsnodes + y = zero(Tx) + for (i,node) in enumerate(nodes) + coeff = tabulation[snode,i] + x = node_to_coords[node] + y += coeff*x + end + vnode += 1 + vnode_to_coords[vnode] = y + end + cell_to_vnodes[cell] = vnode_prev:vnode + vnode_prev = vnode + 1 + end + vcell_to_vnodes = JaggedArray(vcell_to_vnodes_data,vcell_to_vnodes_ptrs) + vchain = fe_chain( + vnode_to_coords, + vcell_to_vnodes, + vcell_to_vrefid, + vrefid_to_vrefface) + vmesh = mesh_from_chain(vchain) + vglue = (;parent_face=vcell_to_cell, + reference_coordinates=refid_to_snode_to_coords, + face_fine_nodes = cell_to_vnodes, + num_dims=Val(dim)) + vmesh, vglue + end # barrier + refid_to_refface = reference_faces(mesh,dim) + refid_to_refmesh = map(refid_to_refface) do ref_face + if order === nothing && resolution === nothing + # Use the given cells as visualization cells + mesh_from_reference_face(ref_face) + elseif order !== nothing && resolution === nothing + # Use cells of given order as visualization cells + geo = geometry(ref_face) + ref_face_ho = lagrangian_reference_face(geo;order) + mesh_from_reference_face(ref_face_ho) + elseif order === nothing && resolution !== nothing + # Use linear sub-cells with $resolution per direction per direction + geom = geometry(ref_face) + refine_reference_geometry(geom,resolution) + else + error("order and resolution kw-arguments can not be given at the same time") + end + end + refid_to_tabulation = map(refid_to_refface,refid_to_refmesh) do refface,refmesh + x = node_coordinates(refmesh) + tabulator(refface)(value,x) + end + refid_to_scell_to_snodes = map(refmesh->face_nodes(refmesh,dim),refid_to_refmesh) + refid_to_scell_to_srefid = map(refmesh->face_reference_id(refmesh,dim),refid_to_refmesh) + refid_to_srefid_to_oid = map(refmesh->map(objectid,reference_faces(refmesh,dim)),refid_to_refmesh) + refid_to_srefid_to_vrefface = map(refmesh->reference_faces(refmesh,dim),refid_to_refmesh) + refid_to_snode_to_coords = map(node_coordinates,refid_to_refmesh) + node_to_coords = node_coordinates(mesh) + cell_to_nodes = face_nodes(mesh,dim) + cell_to_refid = face_reference_id(mesh,dim) + Dn = num_ambient_dims(mesh) + barrier( + refid_to_tabulation, + refid_to_scell_to_snodes, + refid_to_scell_to_srefid, + refid_to_srefid_to_oid, + refid_to_srefid_to_vrefface, + refid_to_snode_to_coords, + node_to_coords, + cell_to_nodes, + cell_to_refid, + Val(Dn)) +end + +function refine_reference_geometry(geo,resolution) + function refine_n_cube_aligned(geo,n) + box = bounding_box(geo) + domain = domain_from_bounding_box(box) + D = num_dims(geo) + cells = ntuple(i->n,Val(D)) + cartesian_mesh(domain,cells) + end + function refine_unit_triangle(geo,n) + # Copyed + adapted from Gridap + tri_num(n) = n*(n+1)÷2 + v(n,i,j) = tri_num(n) - tri_num(n-i+1) + j + D = 2 + quad_to_tris = ((1,2,3),(2,4,3)) + quad = CartesianIndices( (0:1,0:1) ) + Tp = SVector{2,Float64} + n_verts = tri_num(n+1) + n_cells = tri_num(n)+tri_num(n-1) + n_verts_x_cell = 3 + X = zeros(Tp,n_verts) + T = [ zeros(Int,n_verts_x_cell) for i in 1:n_cells ] + for i in 1:n+1 + for j in 1:n+1-i+1 + vert = v(n+1,i,j) + X[vert] = SVector((i-1)/n,(j-1)/n) + end + end + for i in 1:n + for j in 1:n-(i-1) + verts = ntuple( lv-> v(n+1, (i,j).+quad[lv].I ...), Val{2^D}() ) + cell = v(n,i,j) + T[cell] .= map(i->verts[i],quad_to_tris[1]) + if (i-1)+(j-1) < n-1 + cell = tri_num(n) + v(n-1,i,j) + T[cell] .= map(i->verts[i],quad_to_tris[2]) + end + end + end + refface = lagrangian_reference_face(geo) + chain = Chain(; + num_dims=Val(2), + node_coordinates = X, + face_nodes = T, + face_reference_id = fill(1,length(T)), + reference_faces = [refface] + ) + mesh_from_chain(chain) + end + function refine_unit_tet(geo,n) + # Copyed + adapted from Gridap + tri_num(n) = n*(n+1)÷2 + tet_num(n) = n*(n+1)*(n+2)÷6 + v(n,i,j) = tri_num(n) - tri_num(n-i+1) + j + v(n,i,j,k) = tet_num(n) - tet_num(n-i+1) + v(n-i+1,j,k) + D = 3 + cube_to_tets = ((1,2,3,5),(2,4,3,6),(3,5,7,6),(2,3,5,6),(3,4,7,6),(4,6,7,8)) + cube = CartesianIndices( (0:1,0:1,0:1) ) + n_core_tets = length(cube_to_tets)-2 + Tp = SVector{3,Float64} + n_verts = tet_num(n+1) + n_cells = tet_num(n)+n_core_tets*tet_num(n-1)+tet_num(n-2) + n_verts_x_cell = 4 + X = zeros(Tp,n_verts) + T = [ zeros(Int,n_verts_x_cell) for i in 1:n_cells ] + for i in 1:n+1 + for j in 1:n+1-(i-1) + for k in 1:n+1-(i-1)-(j-1) + vert = v(n+1,i,j,k) + X[vert] = SVector((i-1)/n,(j-1)/n,(k-1)/n) + end + end + end + for i in 1:n + for j in 1:n-(i-1) + for k in 1:n-(i-1)-(j-1) + verts = ntuple( lv-> v(n+1, (i,j,k).+cube[lv].I ...), Val{2^D}() ) + cell = v(n,i,j,k) + T[cell] .= map(i->verts[i],cube_to_tets[1]) + if (i-1)+(j-1)+(k-1) < n-1 + cell = tet_num(n) + (v(n-1,i,j,k)-1)*n_core_tets + for t in 1:n_core_tets + T[cell+t] .= map(i->verts[i],cube_to_tets[t+1]) + end + end + if (i-1)+(j-1)+(k-1) < n-2 + cell = tet_num(n) + n_core_tets*tet_num(n-1) + v(n-2,i,j,k) + T[cell] .= map(i->verts[i],cube_to_tets[end]) + end + end + end + end + refface = lagrangian_fe(geo,1) + chain = fe_chain( + X, + T, + fill(1,length(T)), + [refface], + ) + mesh_from_chain(chain) + end + if is_n_cube(geo) && is_axis_aligned(geo) + refine_n_cube_aligned(geo,resolution) + elseif is_unit_simplex(geo) && num_dims(geo) == 2 + refine_unit_triangle(geo,resolution) + elseif is_unit_simplex(geo) && num_dims(geo) == 3 + refine_unit_tet(geo,resolution) + else + error("Case not implemented (yet)") + end +end + +function mesh_from_reference_face(ref_face) + boundary_mesh = boundary(ref_face) + D = num_dims(geometry(ref_face)) + nnodes = num_nodes(ref_face) + face_to_nodes = push(face_nodes(boundary_mesh),[collect(1:nnodes)]) + face_to_refid = push(face_reference_id(boundary_mesh),[1]) + refid_refface = push(reference_faces(boundary_mesh),[ref_face]) + node_to_coords = node_coordinates(ref_face) + groups = [ Dict{String,Vector{Int32}}() for d in 0:D] + for d in 0:D + for face in 1:length(face_to_refid[d+1]) + groups[d+1]["$d-face-$face"] = [face] + end + end + groups[end-1]["boundary"] = 1:length(face_to_refid[D-1+1]) + groups[end]["interior"] = [1] + mesh = fe_mesh( + node_to_coords, + face_to_nodes, + face_to_refid, + refid_refface) +end + +partition_from_mask(a) = partition_from_mask(identity,a) + +function partition_from_mask(f,node_to_mask) + T = Vector{Int32} + free_nodes = convert(T,findall(f,node_to_mask)) + dirichlet_nodes = convert(T,findall(i->!f(i),node_to_mask)) + nfree = length(free_nodes) + ndiri = length(dirichlet_nodes) + permutation = T(undef,nfree+ndiri) + permutation[free_nodes] = 1:nfree + permutation[dirichlet_nodes] = (1:ndiri) .+ nfree + TwoWayPartition(free_nodes,dirichlet_nodes,permutation) +end + +struct TwoWayPartition{A} <: AbstractVector{A} + first::A + last::A + permutation::A +end + +permutation(a::TwoWayPartition) = a.permutation +Base.size(a::TwoWayPartition) = (2,) +Base.IndexStyle(::Type{<:TwoWayPartition}) = IndexLinear() +function Base.getindex(a::TwoWayPartition,i::Int) + @boundscheck @assert i in (1,2) + if i == 1 + a.first + else + a.last + end +end + From 810461f6696b406d84416c59dad924885b2c7110 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 16:10:25 +0100 Subject: [PATCH 17/34] More work on documentation --- docs/Manifest.toml | 802 ++++++++++++++++++++++++++- docs/make.jl | 3 + docs/src/index.md | 5 - docs/src/reference/mesh_interface.md | 7 + src/mesh_interface.jl | 92 ++- 5 files changed, 887 insertions(+), 22 deletions(-) create mode 100644 docs/src/reference/mesh_interface.md diff --git a/docs/Manifest.toml b/docs/Manifest.toml index e2760426..1ab8ac2d 100644 --- a/docs/Manifest.toml +++ b/docs/Manifest.toml @@ -1,75 +1,607 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.7.0" +julia_version = "1.10.0" manifest_format = "2.0" +project_hash = "06b9521c1b5466285e95a69a2724b3212ddd7990" [[deps.ANSIColoredPrinters]] git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" version = "0.0.1" +[[deps.AbstractTrees]] +git-tree-sha1 = "faa260e4cb5aba097a73fab382dd4b5819d8ec8c" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.4.4" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.1" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" + [[deps.Base64]] uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +[[deps.Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.8+1" + +[[deps.CEnum]] +git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90" +uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" +version = "0.4.2" + +[[deps.Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "4b859a208b2397a7a623a03449e4636bdb17bcf2" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.16.1+1" + +[[deps.CircularArrays]] +deps = ["OffsetArrays"] +git-tree-sha1 = "3f7b8a37359ae592cfa7aca7f811da045deff222" +uuid = "7a955b69-7140-5f4e-a0ed-f168c5e2e749" +version = "1.3.3" + +[[deps.CodecZlib]] +deps = ["TranscodingStreams", "Zlib_jll"] +git-tree-sha1 = "cd67fc487743b2f0fd4380d4cbd3a24660d0eec8" +uuid = "944b1d66-785c-5afd-91f1-9de20f533193" +version = "0.7.3" + +[[deps.Combinatorics]] +git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" +uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" +version = "1.0.2" + +[[deps.CommonSubexpressions]] +deps = ["MacroTools", "Test"] +git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7" +uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" +version = "0.3.0" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.0.5+1" + [[deps.Dates]] deps = ["Printf"] uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +[[deps.DiffResults]] +deps = ["StaticArraysCore"] +git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" +uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" +version = "1.1.0" + +[[deps.DiffRules]] +deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] +git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" +uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" +version = "1.15.1" + +[[deps.Distances]] +deps = ["LinearAlgebra", "Statistics", "StatsAPI"] +git-tree-sha1 = "66c4c81f259586e8f002eacebc177e1fb06363b0" +uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" +version = "0.10.11" + + [deps.Distances.extensions] + DistancesChainRulesCoreExt = "ChainRulesCore" + DistancesSparseArraysExt = "SparseArrays" + + [deps.Distances.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + [[deps.DocStringExtensions]] deps = ["LibGit2"] -git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b" +git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.6" +version = "0.9.3" [[deps.Documenter]] -deps = ["ANSIColoredPrinters", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"] -git-tree-sha1 = "3eb46f2549b52a79206469cdc03ae2518ded8d31" +deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "Test", "Unicode"] +git-tree-sha1 = "2613dbec8f4748273bbe30ba71fd5cb369966bac" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "0.27.18" +version = "1.2.1" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.5.0+0" + +[[deps.FLTK_jll]] +deps = ["Artifacts", "Fontconfig_jll", "FreeType2_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "72a4842f93e734f378cf381dae2ca4542f019d23" +uuid = "4fce6fc7-ba6a-5f4c-898f-77e99806d6f8" +version = "1.3.8+0" + +[[deps.FastGaussQuadrature]] +deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "fd923962364b645f3719855c88f7074413a6ad92" +uuid = "442a2c76-b920-505d-bb47-c5924d526838" +version = "1.0.2" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" + +[[deps.FillArrays]] +deps = ["LinearAlgebra", "Random"] +git-tree-sha1 = "5b93957f6dcd33fc343044af3d48c215be2562f1" +uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" +version = "1.9.3" + + [deps.FillArrays.extensions] + FillArraysPDMatsExt = "PDMats" + FillArraysSparseArraysExt = "SparseArrays" + FillArraysStatisticsExt = "Statistics" + + [deps.FillArrays.weakdeps] + PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.13.93+0" + +[[deps.ForwardDiff]] +deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] +git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad" +uuid = "f6369f11-7733-5829-9624-2563aa707210" +version = "0.10.36" +weakdeps = ["StaticArrays"] + + [deps.ForwardDiff.extensions] + ForwardDiffStaticArraysExt = "StaticArrays" + +[[deps.FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.13.1+0" + +[[deps.GLU_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg"] +git-tree-sha1 = "65af046f4221e27fb79b28b6ca89dd1d12bc5ec7" +uuid = "bd17208b-e95e-5925-bf81-e2f59b3e5c61" +version = "9.0.1+0" + +[[deps.GMP_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d" +version = "6.2.1+6" [[deps.GalerkinToolkit]] +deps = ["Combinatorics", "FastGaussQuadrature", "ForwardDiff", "Gmsh", "IterativeSolvers", "LinearAlgebra", "MPI", "Metis", "PartitionedArrays", "SparseArrays", "StaticArrays", "WriteVTK"] path = ".." uuid = "5e3ba9c4-dd81-444d-b69a-0e7bd7bf60a4" version = "0.1.0" +[[deps.Gettext_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] +git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" +uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" +version = "0.21.0+0" + +[[deps.Git]] +deps = ["Git_jll"] +git-tree-sha1 = "51764e6c2e84c37055e846c516e9015b4a291c7d" +uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" +version = "1.3.0" + +[[deps.Git_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "d8be4aab0f4e043cc40984e9097417307cce4c03" +uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" +version = "2.36.1+2" + +[[deps.Glib_jll]] +deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "e94c92c7bf4819685eb80186d51c43e71d4afa17" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.76.5+0" + +[[deps.Gmsh]] +deps = ["gmsh_jll"] +git-tree-sha1 = "00228b1b4697f060d39ef883e7b4929d0a537453" +uuid = "705231aa-382f-11e9-3f0c-b7cb4346fdeb" +version = "0.3.0" + +[[deps.HDF5_jll]] +deps = ["Artifacts", "JLLWrappers", "LibCURL_jll", "Libdl", "OpenSSL_jll", "Pkg", "Zlib_jll"] +git-tree-sha1 = "4cc2bb72df6ff40b055295fdef6d92955f9dede8" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "1.12.2+2" + +[[deps.Hwloc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "ca0f6bf568b4bfc807e7537f081c81e35ceca114" +uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" +version = "2.10.0+0" + [[deps.IOCapture]] deps = ["Logging", "Random"] -git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a" +git-tree-sha1 = "8b72179abc660bfab5e28472e019392b97d0985c" uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.2" +version = "0.2.4" [[deps.InteractiveUtils]] deps = ["Markdown"] uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +[[deps.IrrationalConstants]] +git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.2.2" + +[[deps.IterativeSolvers]] +deps = ["LinearAlgebra", "Printf", "Random", "RecipesBase", "SparseArrays"] +git-tree-sha1 = "b435d190ef8369cf4d79cc9dd5fba88ba0165307" +uuid = "42fd0dbc-a981-5370-80f2-aaf504508153" +version = "0.9.3" + +[[deps.JLLWrappers]] +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.5.0" + [[deps.JSON]] deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "3c837543ddb02250ef42f4738347454f95079d4e" +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.3" +version = "0.21.4" + +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "60b1194df0a3298f460063de985eae7b01bc011a" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "3.0.1+0" + +[[deps.LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d986ce2d884d49126836ea94ed5bfb0f12679713" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "15.0.7+0" + +[[deps.LZO_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" +uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" +version = "2.10.1+0" + +[[deps.LazilyInitializedFields]] +git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" +uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" +version = "1.2.2" + +[[deps.LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.4.0+0" [[deps.LibGit2]] -deps = ["Base64", "NetworkOptions", "Printf", "SHA"] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.6.4+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.2.2+1" + +[[deps.Libgcrypt_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] +git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" +uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" +version = "1.8.7+0" + +[[deps.Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.6.0+0" + +[[deps.Libgpg_error_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" +uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" +version = "1.42.0+0" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.17.0+0" + +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.35.0+0" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.36.0+0" + +[[deps.LightXML]] +deps = ["Libdl", "XML2_jll"] +git-tree-sha1 = "3a994404d3f6709610701c7dabfc03fed87a81f8" +uuid = "9c8b4983-aa76-5018-a973-4c85ecc9e179" +version = "0.9.1" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + +[[deps.LinearElasticity_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "71e8ee0f9fe0e86a8f8c7f28361e5118eab2f93f" +uuid = "18c40d15-f7cd-5a6d-bc92-87468d86c5db" +version = "5.0.0+0" + +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "7d6dd4e9212aebaeed356de34ccf262a3cd415aa" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "0.3.26" + + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + [[deps.Logging]] uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +[[deps.METIS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "1fd0a97409e418b78c53fac671cf4622efdf0f21" +uuid = "d00139f3-1899-568f-a2f0-47f597d42d70" +version = "5.1.2+0" + +[[deps.MMG_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "LinearElasticity_jll", "Pkg", "SCOTCH_jll"] +git-tree-sha1 = "70a59df96945782bb0d43b56d0fbfdf1ce2e4729" +uuid = "86086c02-e288-5929-a127-40944b0018b7" +version = "5.6.0+0" + +[[deps.MPI]] +deps = ["Distributed", "DocStringExtensions", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "PkgVersion", "PrecompileTools", "Requires", "Serialization", "Sockets"] +git-tree-sha1 = "b4d8707e42b693720b54f0b3434abee6dd4d947a" +uuid = "da04e1cc-30fd-572f-bb4f-1f8673147195" +version = "0.20.16" + + [deps.MPI.extensions] + AMDGPUExt = "AMDGPU" + CUDAExt = "CUDA" + + [deps.MPI.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + +[[deps.MPICH_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "2ee75365ca243c1a39d467e35ffd3d4d32eef11e" +uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" +version = "4.1.2+1" + +[[deps.MPIPreferences]] +deps = ["Libdl", "Preferences"] +git-tree-sha1 = "8f6af051b9e8ec597fa09d8885ed79fd582f33c9" +uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" +version = "0.1.10" + +[[deps.MPItrampoline_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "8eeb3c73bbc0ca203d0dc8dad4008350bbe5797b" +uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" +version = "5.3.1+1" + +[[deps.MacroTools]] +deps = ["Markdown", "Random"] +git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.13" + [[deps.Markdown]] deps = ["Base64"] uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +[[deps.MarkdownAST]] +deps = ["AbstractTrees", "Markdown"] +git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" +uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" +version = "0.1.2" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.2+1" + +[[deps.Metis]] +deps = ["CEnum", "LinearAlgebra", "METIS_jll", "SparseArrays"] +git-tree-sha1 = "66a4f74edb3ac5f28c74de60f9acc2a541fbbe28" +uuid = "2679e427-3c69-5b7f-982b-ece356f1e94b" +version = "1.4.0" + + [deps.Metis.extensions] + MetisGraphs = "Graphs" + MetisLightGraphs = "LightGraphs" + MetisSimpleWeightedGraphs = ["SimpleWeightedGraphs", "Graphs"] + + [deps.Metis.weakdeps] + Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" + LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d" + SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622" + +[[deps.MicrosoftMPI_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "b01beb91d20b0d1312a9471a36017b5b339d26de" +uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" +version = "10.1.4+1" + [[deps.Mmap]] uuid = "a63ad114-7e13-5084-954f-fe012c677804" +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2023.1.10" + +[[deps.NaNMath]] +deps = ["OpenLibm_jll"] +git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "1.0.2" + [[deps.NetworkOptions]] uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[deps.OCCT_jll]] +deps = ["Artifacts", "FreeType2_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "acc8099ae8ed10226dc8424fb256ec9fe367a1f0" +uuid = "baad4e97-8daa-5946-aac2-2edac59d34e1" +version = "7.6.2+2" + +[[deps.OffsetArrays]] +git-tree-sha1 = "6a731f2b5c03157418a20c12195eb4b74c8f8621" +uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" +version = "1.13.0" + + [deps.OffsetArrays.extensions] + OffsetArraysAdaptExt = "Adapt" + + [deps.OffsetArrays.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.23+2" + +[[deps.OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.1+2" + +[[deps.OpenMPI_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "PMIx_jll", "TOML", "Zlib_jll", "libevent_jll", "prrte_jll"] +git-tree-sha1 = "1d1421618bab0e820bdc7ae1a2b46ce576981273" +uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" +version = "5.0.1+0" + +[[deps.OpenSSL_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a12e56c72edee3ce6b96667745e6cbbe5498f200" +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "1.1.23+0" + +[[deps.OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.5+0" + +[[deps.PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.42.0+1" + +[[deps.PMIx_jll]] +deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "Libdl", "Zlib_jll", "libevent_jll"] +git-tree-sha1 = "8b3b19351fa24791f94d7ae85faf845ca1362541" +uuid = "32165bc3-0280-59bc-8c0b-c33b6203efab" +version = "4.2.7+0" [[deps.Parsers]] -deps = ["Dates"] -git-tree-sha1 = "1285416549ccfcdf0c50d4997a94331e88d68413" +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "2.3.1" +version = "2.8.1" + +[[deps.PartitionedArrays]] +deps = ["CircularArrays", "Distances", "FillArrays", "IterativeSolvers", "LinearAlgebra", "MPI", "Printf", "Random", "SparseArrays", "SparseMatricesCSR"] +git-tree-sha1 = "ef8689c008317753472f0dc4a8160265619b23c4" +uuid = "5a9dfac6-5c52-46f7-8278-5e2210713be9" +version = "0.4.0" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.42.2+0" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.10.0" + +[[deps.PkgVersion]] +deps = ["Pkg"] +git-tree-sha1 = "f9501cc0430a26bc3d156ae1b5b0c1b47af4d6da" +uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688" +version = "0.3.3" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "03b4c25b43cb84cee5c90aa9b5ea0a78fd848d2f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.0" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "00805cd429dcb4870060ff49ef443486c262e38e" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.4.1" [[deps.Printf]] deps = ["Unicode"] @@ -80,11 +612,36 @@ deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" [[deps.Random]] -deps = ["SHA", "Serialization"] +deps = ["SHA"] uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +[[deps.RecipesBase]] +deps = ["PrecompileTools"] +git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "1.3.4" + +[[deps.RegistryInstances]] +deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] +git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" +uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" +version = "0.1.0" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.0" + +[[deps.SCOTCH_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] +git-tree-sha1 = "7110b749766853054ce8a2afaa73325d72d32129" +uuid = "a8d0f55d-b80e-548d-aff6-1a04c175f0f9" +version = "6.1.3+0" + [[deps.SHA]] uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" [[deps.Serialization]] uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" @@ -92,9 +649,224 @@ uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" [[deps.Sockets]] uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.10.0" + +[[deps.SparseMatricesCSR]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] +git-tree-sha1 = "38677ca58e80b5cad2382e5a1848f93b054ad28d" +uuid = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1" +version = "0.6.7" + +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "2.3.1" + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + + [deps.SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] +git-tree-sha1 = "f68dd04d131d9a8a8eb836173ee8f105c360b0c5" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.9.1" + + [deps.StaticArrays.extensions] + StaticArraysChainRulesCoreExt = "ChainRulesCore" + StaticArraysStatisticsExt = "Statistics" + + [deps.StaticArrays.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.2" + +[[deps.Statistics]] +deps = ["LinearAlgebra", "SparseArrays"] +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.10.0" + +[[deps.StatsAPI]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" +uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" +version = "1.7.0" + +[[deps.SuiteSparse]] +deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] +uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" + +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.2.1+1" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + [[deps.Test]] deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +[[deps.TranscodingStreams]] +deps = ["Random", "Test"] +git-tree-sha1 = "9a6ae7ed916312b41236fcef7e0af564ef934769" +uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" +version = "0.9.13" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + [[deps.Unicode]] uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" + +[[deps.VTKBase]] +git-tree-sha1 = "c2d0db3ef09f1942d08ea455a9e252594be5f3b6" +uuid = "4004b06d-e244-455f-a6ce-a5f9919cc534" +version = "1.0.1" + +[[deps.WriteVTK]] +deps = ["Base64", "CodecZlib", "FillArrays", "LightXML", "TranscodingStreams", "VTKBase"] +git-tree-sha1 = "41f0dc2a8f6fd860c266b91fd5cdf4fead65ae69" +uuid = "64499a7a-5c06-52f2-abe2-ccb03c286192" +version = "1.18.1" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] +git-tree-sha1 = "801cbe47eae69adc50f36c3caec4758d2650741b" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.12.2+0" + +[[deps.XSLT_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] +git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" +uuid = "aed1982a-8fda-507f-9586-7b0439959a61" +version = "1.1.34+0" + +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.8.6+0" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.11+0" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.4+0" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.4+4" + +[[deps.Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "5.0.3+4" + +[[deps.Xorg_libXft_jll]] +deps = ["Fontconfig_jll", "Libdl", "Pkg", "Xorg_libXrender_jll"] +git-tree-sha1 = "754b542cdc1057e0a2f1888ec5414ee17a4ca2a1" +uuid = "2c808117-e144-5220-80d1-69d4eaa9352c" +version = "2.3.3+1" + +[[deps.Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] +git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.4+4" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] +git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.10+4" + +[[deps.Xorg_libpthread_stubs_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" +uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" +version = "0.1.1+0" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] +git-tree-sha1 = "b4bfde5d5b652e22b9c790ad00af08b6d042b97d" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.15.0+0" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.5.0+0" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+1" + +[[deps.gmsh_jll]] +deps = ["Artifacts", "Cairo_jll", "CompilerSupportLibraries_jll", "FLTK_jll", "FreeType2_jll", "GLU_jll", "GMP_jll", "HDF5_jll", "JLLWrappers", "JpegTurbo_jll", "LLVMOpenMP_jll", "Libdl", "Libglvnd_jll", "METIS_jll", "MMG_jll", "OCCT_jll", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "Xorg_libXft_jll", "Xorg_libXinerama_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "d15409a4b9f1d14f1e1f9e910cd00f7d6695c261" +uuid = "630162c2-fc9b-58b3-9910-8442a8a132e6" +version = "4.11.1+0" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.8.0+1" + +[[deps.libevent_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Pkg"] +git-tree-sha1 = "31bceb8a838f2e1e6d7e0311f2bea22e3a3fdae3" +uuid = "1080aeaf-3a6a-583e-a51c-c537b09f60ec" +version = "2.1.12+0" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "93284c28274d9e75218a416c65ec49d0e0fcdf3d" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.40+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.52.0+1" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+2" + +[[deps.prrte_jll]] +deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "Libdl", "PMIx_jll", "libevent_jll"] +git-tree-sha1 = "5adb2d7a18a30280feb66cad6f1a1dfdca2dc7b0" +uuid = "eb928a42-fffd-568d-ab9c-3f5d54fc65b9" +version = "3.0.2+0" diff --git a/docs/make.jl b/docs/make.jl index e61d8c97..5403e16f 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -14,6 +14,9 @@ makedocs(; ), pages=[ "Home" => "index.md", + "Reference" =>[ + "Mesh interface" => "reference/mesh_interface.md" + ] ], ) diff --git a/docs/src/index.md b/docs/src/index.md index 469318bd..144de200 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -6,9 +6,4 @@ CurrentModule = GalerkinToolkit Documentation for [GalerkinToolkit](https://github.com/fverdugo/GalerkinToolkit.jl). -```@index -``` -```@autodocs -Modules = [GalerkinToolkit] -``` diff --git a/docs/src/reference/mesh_interface.md b/docs/src/reference/mesh_interface.md new file mode 100644 index 00000000..3fc45d28 --- /dev/null +++ b/docs/src/reference/mesh_interface.md @@ -0,0 +1,7 @@ +# Mesh interface + + +```@autodocs +Modules = [GalerkinToolkit] +Pages = ["mesh_interface.jl"] +``` diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl index a610b511..dfa0b24b 100644 --- a/src/mesh_interface.jl +++ b/src/mesh_interface.jl @@ -14,8 +14,15 @@ function push(a::Tuple,x) (a...,x) end + val_parameter(a) = a val_parameter(::Val{a}) where a = a + +""" + num_dims(a) + +Return the number of space dimensions of `a`. Defaults to `a.num_dims`. +""" num_dims(a) = val_parameter(a.num_dims) node_coordinates(a) = a.node_coordinates reference_faces(a) = a.reference_faces @@ -27,11 +34,24 @@ has_physical_faces(a) = hasproperty(a,:physical_faces) && a.physical_faces !== n periodic_nodes(a) = a.periodic_nodes has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing geometry(a) = a.geometry +""" +""" boundary(a) = a.boundary +""" +""" is_n_cube(a) = hasproperty(a,:is_n_cube) ? val_parameter(a.is_n_cube) : false +""" +""" is_simplex(a) = hasproperty(a,:is_simplex) ? val_parameter(a.is_simplex) : false + +""" +""" is_axis_aligned(a) = a.is_axis_aligned +""" +""" bounding_box(a) = a.bounding_box +""" +""" vertex_permutations(a) = a.vertex_permutations face_own_dofs(a) = a.face_own_dofs face_own_dof_permutations(a) = a.face_own_dof_permutations @@ -39,7 +59,11 @@ node_to_dofs(a) = a.node_to_dofs dof_to_node(a) = a.dof_to_node dof_to_index(a) = a.dof_to_index num_dofs(a) = a.num_dofs +""" +""" coordinates(a) = a.coordinates +""" +""" weights(a) = a.weights order_per_dir(a) = a.order_per_dir monomial_exponents(a) = a.monomial_exponents @@ -49,7 +73,12 @@ face_permutation_ids(a) = a.face_permutation_ids face_permutation_ids(a,m,n) = face_permutation_ids(a)[m+1,n+1] local_nodes(a) = a.local_nodes local_node_colors(a) = a.local_node_colors +""" + +""" real_type(a) = a.real_type +""" +""" int_type(a) = a.int_type outwards_normals(a) = a.outwards_normals @@ -79,6 +108,34 @@ function face_dim(a) reduce(vcat,map(d->face_dim(a,d),0:D)) end +""" + abstract type AbstractFaceGeometry + +# Basic queries + +- [`num_dims`](@ref) +- [`is_axis_aligned`](@ref) +- [`is_simplex`](@ref) +- [`is_n_cube`](@ref) +- [`real_type`](@ref) +- [`int_type`](@ref) +- [`is_unit_n_cube`](@ref) +- [`is_unit_simplex`](@ref) +- [`is_unitary`](@ref) +- [`bounding_box`](@ref) +- [`boundary`](@ref) +- [`vertex_permutations`](@ref) + +# Basic constructors + +- [`unit_simplex`](@ref) +- [`unit_n_cube`](@ref) + +# Supertype hierarchy + + AbstractFaceGeometry <: GalerkinToolkitDataType + +""" abstract type AbstractFaceGeometry <: GalerkinToolkitDataType end struct ExtrusionPolytope{D,Tv,Ti} <: AbstractFaceGeometry @@ -87,6 +144,8 @@ struct ExtrusionPolytope{D,Tv,Ti} <: AbstractFaceGeometry int_type::Type{Ti} end +""" +""" function unit_simplex(num_dims;real_type=Float64,int_type=Int) D = val_parameter(num_dims) extrusion = ntuple(i->false,Val(D)) @@ -97,6 +156,8 @@ function unit_simplex(num_dims;real_type=Float64,int_type=Int) ExtrusionPolytope(extrusion,bounding_box,int_type) end +""" +""" function unit_n_cube(num_dims;real_type=Float64,int_type=Int) D = val_parameter(num_dims) extrusion = ntuple(i->true,Val(D)) @@ -107,6 +168,7 @@ function unit_n_cube(num_dims;real_type=Float64,int_type=Int) ExtrusionPolytope(extrusion,bounding_box,int_type) end + num_dims(p::ExtrusionPolytope{D}) where D = D is_axis_aligned(p::ExtrusionPolytope) = true is_simplex(geom::ExtrusionPolytope) = all(i->i==false,geom.extrusion) @@ -114,21 +176,45 @@ is_n_cube(geom::ExtrusionPolytope) = all(geom.extrusion) real_type(p::ExtrusionPolytope{D,Tv}) where {D,Tv} = Tv int_type(p::ExtrusionPolytope{D,Tv,Ti}) where {D,Tv,Ti} = Ti +""" +""" function is_unit_n_cube(geo) is_n_cube(geo) && is_unitary(geo) end +""" +""" function is_unit_simplex(geo) is_simplex(geo) && is_unitary(geo) end +""" +""" function is_unitary(geom) ! is_axis_aligned(geom) && return false my_bounding_box = bounding_box(geom) all(i->i==0,first(my_bounding_box)) && all(i->i==1,last(my_bounding_box)) end -struct GenericCuadrature{A,B} <: GalerkinToolkitDataType +""" + abstract type AbstractQuadrature + +# Basic queries + +- [`coordinates`](@ref) +- [`weights`](@ref) + +# Basic constructors + +- [`default_quadrature`](@ref) + +# Supertype hierarchy + + AbstractQuadrature <: GalerkinToolkitDataType +""" +abstract type AbstractQuadrature <: GalerkinToolkitDataType end + +struct GenericCuadrature{A,B} <: AbstractQuadrature coordinates::A weights::B end @@ -240,6 +326,8 @@ function repeat_per_dir(geo,a) end repeat_per_dir(geo,a::NTuple) = a +""" +""" function default_quadrature(geo,degree) if is_n_cube(geo) && is_axis_aligned(geo) D = num_dims(geo) @@ -784,7 +872,7 @@ function vtk_cells(mesh,d) end """ - args = vtk_args(mesh,d) + args = vtk_args(mesh[,d]) Return the arguments `args` to be passed in final position to functions like `WriteVTK.vtk_grid`. From f0ea32106e100c938ec4d990c16b7ad4db0d2309 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 16:39:43 +0100 Subject: [PATCH 18/34] Adding lagrange_mesh_face --- src/mesh_interface.jl | 253 ++++++++++++++++++++++------------- test/mesh_interface_tests.jl | 46 ++----- 2 files changed, 169 insertions(+), 130 deletions(-) diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl index dfa0b24b..857ed4cf 100644 --- a/src/mesh_interface.jl +++ b/src/mesh_interface.jl @@ -144,6 +144,12 @@ struct ExtrusionPolytope{D,Tv,Ti} <: AbstractFaceGeometry int_type::Type{Ti} end +""" +""" +function num_dims(a::AbstractFaceGeometry) + val_parameter(a.num_dims) +end + """ """ function unit_simplex(num_dims;real_type=Float64,int_type=Int) @@ -344,51 +350,32 @@ abstract type AbstractMeshFace <: GalerkinToolkitDataType end num_dims(f::AbstractMeshFace) = num_dims(geometry(f)) -abstract type AbstractLagrangeFE <: AbstractMeshFace end +abstract type AbstractLagrangeMeshFace <: AbstractMeshFace end -struct GenericLagrangeFE{A,B,C,D} <: AbstractLagrangeFE +struct GenericLagrangeMeshFace{A,B,C} <: AbstractLagrangeMeshFace geometry::A order_per_dir::B space::Symbol lib_to_user_nodes::C - major::Symbol - shape::D end -struct ScalarShape end -const SCALAR_SHAPE = ScalarShape() +lagrange_mesh_face(args...) = GenericLagrangeMeshFace(args...) -lagrangian_fe(args...) = GenericLagrangeFE(args...) - -function lagrangian_fe(geometry,order; +function lagrange_mesh_face(geometry,order; space = default_space(geometry), - lib_to_user_nodes = int_type(geometry)[], - major = :component, - shape = SCALAR_SHAPE) + lib_to_user_nodes = int_type(geometry)[]) D = num_dims(geometry) order_per_dir = repeat_per_dir(geometry,order) - lagrangian_fe( + lagrange_mesh_face( geometry, order_per_dir, space, - lib_to_user_nodes, - major, - shape) + lib_to_user_nodes) end -function default_space(geom) - if is_simplex(geom) - :P - elseif is_n_cube(geom) - :Q - else - error("Not implemented") - end -end +order(fe::AbstractLagrangeMeshFace) = maximum(order_per_dir(fe),init=0) -order(fe::AbstractLagrangeFE) = maximum(order_per_dir(fe),init=0) - -function lib_to_user_nodes(fe::AbstractLagrangeFE) +function lib_to_user_nodes(fe::AbstractLagrangeMeshFace) if length(fe.lib_to_user_nodes) == 0 nnodes = num_nodes(fe) Ti = int_type(geometry(fe)) @@ -398,16 +385,35 @@ function lib_to_user_nodes(fe::AbstractLagrangeFE) end end -function monomial_exponents(fe::AbstractLagrangeFE) +function monomial_exponents(fe::AbstractLagrangeMeshFace) monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> int_type) end -function node_coordinates(fe::AbstractLagrangeFE) +function node_coordinates(fe::AbstractLagrangeMeshFace) @assert fe |> geometry |> is_unitary mexps = monomial_exponents(fe) node_coordinates_from_monomials_exponents(mexps,fe.order_per_dir,fe.geometry |> real_type) end +function primal_basis(fe::AbstractLagrangeMeshFace) + map(e->(x-> prod(x.^e)),fe|>monomial_exponents) +end + +function dual_basis(fe::AbstractLagrangeMeshFace) + node_coordinates_reffe = fe|>node_coordinates + map(x->(f->f(x)),node_coordinates_reffe) +end + +function default_space(geom) + if is_simplex(geom) + :P + elseif is_n_cube(geom) + :Q + else + error("Not implemented") + end +end + function node_coordinates_from_monomials_exponents(monomial_exponents,order_per_dir,real_type) D = length(order_per_dir) Tv = real_type @@ -453,60 +459,6 @@ function monomial_exponents_from_filter(f,order_per_dir,int_type) result end -function tensor_basis(fe::AbstractLagrangeFE) - Tv = fe |> geometry |> real_type - if fe.shape == SCALAR_SHAPE - return Tv(1) - else - cis = CartesianIndices(val_parameter(fe.shape)) - l = prod(val_parameter(fe.shape)) - init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Tv} - cis_flat = cis[:] - return map(cis_flat) do ci - init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) - end - end -end - -function primal_basis(fe::AbstractLagrangeFE) - scalar_basis = map(e->(x-> prod(x.^e)),fe|>monomial_exponents) - if fe.shape == SCALAR_SHAPE - return scalar_basis - else - primal_nested = map(scalar_basis) do monomial - map(tensor_basis(fe)) do e - x -> monomial(x)*e - end - end - return reduce(vcat,primal_nested) - end -end - -function dual_basis(fe::AbstractLagrangeFE) - node_coordinates_reffe = fe|>node_coordinates - scalar_basis = map(x->(f->f(x)),node_coordinates_reffe) - if fe.shape == SCALAR_SHAPE - return scalar_basis - else - if fe.major === :component - dual_nested = map(node_coordinates_reffe) do x - map(tensor_basis(fe)) do e - f->inner(e,f(x)) - end - end - elseif fe.major === :node - dual_nested = map(tensor_basis(fe)) do e - map(node_coordinates_reffe) do x - f->inner(e,f(x)) - end - end - else - error("Not Implemented") - end - return reduce(vcat,dual_nested) - end -end - inner(a,b) = sum(map(*,a,b)) value(f,x) = f(x) @@ -538,6 +490,115 @@ function tabulator(fe) end end +#abstract type AbstractLagrangeFE <: AbstractMeshFace end +# +#struct GenericLagrangeFE{A,B,C,D} <: AbstractLagrangeFE +# geometry::A +# order_per_dir::B +# space::Symbol +# lib_to_user_nodes::C +# major::Symbol +# shape::D +#end +# +#struct ScalarShape end +#const SCALAR_SHAPE = ScalarShape() +# +#lagrangian_fe(args...) = GenericLagrangeFE(args...) +# +#function lagrangian_fe(geometry,order; +# space = default_space(geometry), +# lib_to_user_nodes = int_type(geometry)[], +# major = :component, +# shape = SCALAR_SHAPE) +# D = num_dims(geometry) +# order_per_dir = repeat_per_dir(geometry,order) +# lagrangian_fe( +# geometry, +# order_per_dir, +# space, +# lib_to_user_nodes, +# major, +# shape) +#end +# +# +#order(fe::AbstractLagrangeFE) = maximum(order_per_dir(fe),init=0) +# +#function lib_to_user_nodes(fe::AbstractLagrangeFE) +# if length(fe.lib_to_user_nodes) == 0 +# nnodes = num_nodes(fe) +# Ti = int_type(geometry(fe)) +# collect(Ti.(1:nnodes)) +# else +# fe.lib_to_user_nodes +# end +#end +# +#function monomial_exponents(fe::AbstractLagrangeFE) +# monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> int_type) +#end +# +#function node_coordinates(fe::AbstractLagrangeFE) +# @assert fe |> geometry |> is_unitary +# mexps = monomial_exponents(fe) +# node_coordinates_from_monomials_exponents(mexps,fe.order_per_dir,fe.geometry |> real_type) +#end +# +#function tensor_basis(fe::AbstractLagrangeFE) +# Tv = fe |> geometry |> real_type +# if fe.shape == SCALAR_SHAPE +# return Tv(1) +# else +# cis = CartesianIndices(val_parameter(fe.shape)) +# l = prod(val_parameter(fe.shape)) +# init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Tv} +# cis_flat = cis[:] +# return map(cis_flat) do ci +# init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) +# end +# end +#end +# +#function primal_basis(fe::AbstractLagrangeFE) +# scalar_basis = map(e->(x-> prod(x.^e)),fe|>monomial_exponents) +# if fe.shape == SCALAR_SHAPE +# return scalar_basis +# else +# primal_nested = map(scalar_basis) do monomial +# map(tensor_basis(fe)) do e +# x -> monomial(x)*e +# end +# end +# return reduce(vcat,primal_nested) +# end +#end +# +#function dual_basis(fe::AbstractLagrangeFE) +# node_coordinates_reffe = fe|>node_coordinates +# scalar_basis = map(x->(f->f(x)),node_coordinates_reffe) +# if fe.shape == SCALAR_SHAPE +# return scalar_basis +# else +# if fe.major === :component +# dual_nested = map(node_coordinates_reffe) do x +# map(tensor_basis(fe)) do e +# f->inner(e,f(x)) +# end +# end +# elseif fe.major === :node +# dual_nested = map(tensor_basis(fe)) do e +# map(node_coordinates_reffe) do x +# f->inner(e,f(x)) +# end +# end +# else +# error("Not Implemented") +# end +# return reduce(vcat,dual_nested) +# end +#end + abstract type AbstractFEMesh <: GalerkinToolkitDataType end struct GenericFEMesh{A,B,C,D,E,F,G} <: AbstractFEMesh @@ -830,7 +891,7 @@ function reference_face_from_gmsh_eltype(eltype) en, = gmsh.model.mesh.getElementProperties(eltype) error("Unsupported element type. elemType: $eltype ($en)") end - lagrangian_fe(geom,order;lib_to_user_nodes=lib_to_gmsh) + lagrange_mesh_face(geom,order;lib_to_user_nodes=lib_to_gmsh) end function vtk_points(mesh) @@ -984,7 +1045,7 @@ function boundary(geom::ExtrusionPolytope{1}) error("Not implemented") end order = 1 - fe = lagrangian_fe(vertex,order) + fe = lagrange_mesh_face(vertex,order) node_coordinates = SVector{1,Tv}[(0,),(1,)] face_nodes = [Vector{Ti}[[1],[2]]] face_reference_id = [Ti[1,1]] @@ -1020,8 +1081,8 @@ function boundary(geom::ExtrusionPolytope{2}) error("Not implemented") end order = 1 - fe0 = lagrangian_fe(g0,order) - fe1 = lagrangian_fe(g1,order) + fe0 = lagrange_mesh_face(g0,order) + fe1 = lagrange_mesh_face(g1,order) reference_faces = ([fe0],[fe1]) fe_mesh( node_coordinates, @@ -1063,9 +1124,9 @@ function boundary(geom::ExtrusionPolytope{3}) error("Not implemented") end order = 1 - fe0 = lagrangian_fe(g0,order) - fe1 = lagrangian_fe(g1,order) - fe2 = lagrangian_fe(g2,order) + fe0 = lagrange_mesh_face(g0,order) + fe1 = lagrange_mesh_face(g1,order) + fe2 = lagrange_mesh_face(g2,order) reference_faces = ([fe0,],[fe1],[fe2]) fe_mesh( node_coordinates, @@ -1094,7 +1155,7 @@ function boundary_from_mesh_face(refface) node_coordinates_aux = map(xi->map(xii->round(Int,order_inter*xii),xi),node_coordinates_inter) ref_faces_geom = reference_faces(mesh_geom) ref_faces = map(ref_faces_geom) do ref_faces_geom_d - map(r->lagrangian_fe(geometry(r),order_inter),ref_faces_geom_d) + map(r->lagrange_mesh_face(geometry(r),order_inter),ref_faces_geom_d) end face_ref_id_geom = face_reference_id(mesh_geom) for d in 0:(D-1) @@ -1181,7 +1242,7 @@ function vertex_permutations_from_face_geometry(geo) end admissible_permutations = Vector{Int}[] order = 1 - ref_face = lagrangian_fe(geo,order) + ref_face = lagrange_mesh_face(geo,order) fun_mesh = boundary(ref_face) geo_node_coords = node_coordinates(geo_mesh) fun_node_coords = node_coordinates(fun_mesh) @@ -2264,7 +2325,7 @@ function simplexify_unit_n_cube(geo) end simplex = unit_simplex(Val(D)) order = 1 - ref_cell = lagrangian_fe(simplex,order) + ref_cell = lagrange_mesh_face(simplex,order) node_coords = node_coordinates(boundary(geo)) cell_nodes = simplex_node_ids_n_cube(geo) ncells = length(cell_nodes) @@ -2570,7 +2631,7 @@ function cartesian_chain(domain,cells_per_dir) cell_reference_id = fill(Int32(1),ncells) cell_geometry = unit_n_cube(Val(D)) order = 1 - ref_cell = lagrangian_fe(cell_geometry,order) + ref_cell = lagrange_mesh_face(cell_geometry,order) reference_cells = [ref_cell] interior_cells = collect(Int32,1:length(cell_nodes)) groups = Dict(["interior"=>interior_cells,"$D-face-1"=>interior_cells]) @@ -3029,7 +3090,7 @@ function refine_reference_geometry(geo,resolution) end end end - refface = lagrangian_fe(geo,1) + refface = lagrange_mesh_face(geo,1) chain = fe_chain( X, T, diff --git a/test/mesh_interface_tests.jl b/test/mesh_interface_tests.jl index f2ed7510..eebb1c8a 100644 --- a/test/mesh_interface_tests.jl +++ b/test/mesh_interface_tests.jl @@ -34,49 +34,27 @@ quad = gk.default_quadrature(cube3,degree) order = 1 -fe = gk.lagrangian_fe(spx0,order) -fe = gk.lagrangian_fe(spx1,order) -fe = gk.lagrangian_fe(spx2,order) -fe = gk.lagrangian_fe(spx3,order) +fe = gk.lagrange_mesh_face(spx0,order) +fe = gk.lagrange_mesh_face(spx1,order) +fe = gk.lagrange_mesh_face(spx2,order) +fe = gk.lagrange_mesh_face(spx3,order) display(fe) -fe = gk.lagrangian_fe(cube0,order) -fe = gk.lagrangian_fe(cube1,order) -fe = gk.lagrangian_fe(cube2,order) -fe = gk.lagrangian_fe(cube3,order) +fe = gk.lagrange_mesh_face(cube0,order) +fe = gk.lagrange_mesh_face(cube1,order) +fe = gk.lagrange_mesh_face(cube2,order) +fe = gk.lagrange_mesh_face(cube3,order) display(fe) -fe = gk.lagrangian_fe(cube0,order) +fe = gk.lagrange_mesh_face(cube0,order) @show gk.monomial_exponents(fe) @show gk.node_coordinates(fe) -fe = gk.lagrangian_fe(cube2,order) +fe = gk.lagrange_mesh_face(cube2,order) @show gk.node_coordinates(fe) spx2 = gk.unit_simplex(2) quad = gk.default_quadrature(spx2,degree) -fe = gk.lagrangian_fe(spx2,order) -funs = gk.shape_functions(fe) -x = gk.coordinates(quad) -B = broadcast(gk.value,permutedims(funs),x) -display(B) -tabulator = gk.tabulator(fe) -A = tabulator(gk.value,x) -@test A≈B -x = gk.node_coordinates(fe) -A = tabulator(gk.value,x) - -fe = gk.lagrangian_fe(spx2,order;shape=(3,)) -funs = gk.shape_functions(fe) -x = gk.coordinates(quad) -B = broadcast(gk.value,permutedims(funs),x) -display(B) -tabulator = gk.tabulator(fe) -A = tabulator(gk.value,x) -@test A≈B -x = gk.node_coordinates(fe) -A = tabulator(gk.value,x) - -fe = gk.lagrangian_fe(spx2,order;shape=()) +fe = gk.lagrange_mesh_face(spx2,order) funs = gk.shape_functions(fe) x = gk.coordinates(quad) B = broadcast(gk.value,permutedims(funs),x) @@ -106,7 +84,7 @@ end @show gk.unit_n_cube(3) |> gk.boundary order = 2 -gk.lagrangian_fe(spx1,order) |> gk.boundary |> gk.topology +gk.lagrange_mesh_face(spx1,order) |> gk.boundary |> gk.topology mesh = gk.mesh_from_gmsh(msh;complexify=false) From 6cc80be442bf61efeb7c81def6d6a8365189ca32 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 29 Jan 2024 17:24:44 +0100 Subject: [PATCH 19/34] More work in docs --- src/mesh_interface.jl | 181 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 174 insertions(+), 7 deletions(-) diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl index 857ed4cf..6b34edd0 100644 --- a/src/mesh_interface.jl +++ b/src/mesh_interface.jl @@ -1,3 +1,9 @@ +# TODO there are functions that depending on the input +# return objects on different interfaces +# topology +# boundary +# reference_faces +# abstract type GalerkinToolkitDataType end function Base.show(io::IO,data::GalerkinToolkitDataType) @@ -24,15 +30,31 @@ val_parameter(::Val{a}) where a = a Return the number of space dimensions of `a`. Defaults to `a.num_dims`. """ num_dims(a) = val_parameter(a.num_dims) +""" +""" node_coordinates(a) = a.node_coordinates +""" +""" reference_faces(a) = a.reference_faces +""" +""" face_nodes(a) = a.face_nodes +""" +""" face_incidence(a) = a.face_incidence +""" +""" face_reference_id(a) = a.face_reference_id +""" +""" physical_faces(a) = a.physical_faces has_physical_faces(a) = hasproperty(a,:physical_faces) && a.physical_faces !== nothing +""" +""" periodic_nodes(a) = a.periodic_nodes has_periodic_nodes(a) = hasproperty(a,:periodic_nodes) && a.periodic_nodes !== nothing +""" +""" geometry(a) = a.geometry """ """ @@ -66,20 +88,35 @@ coordinates(a) = a.coordinates """ weights(a) = a.weights order_per_dir(a) = a.order_per_dir +""" +""" monomial_exponents(a) = a.monomial_exponents +""" +""" +primal_basis(a) = a.primal_basis +""" +""" +dual_basis(a) = a.dual_basis +""" +""" lib_to_user_nodes(a) = a.lib_to_user_nodes +""" +""" interior_nodes(a) = a.interior_nodes +""" +""" face_permutation_ids(a) = a.face_permutation_ids face_permutation_ids(a,m,n) = face_permutation_ids(a)[m+1,n+1] local_nodes(a) = a.local_nodes local_node_colors(a) = a.local_node_colors """ - """ real_type(a) = a.real_type """ """ int_type(a) = a.int_type +""" +""" outwards_normals(a) = a.outwards_normals reference_faces(a,d) = reference_faces(a)[val_parameter(d)+1] @@ -89,6 +126,8 @@ face_reference_id(a,d) = face_reference_id(a)[val_parameter(d)+1] num_faces(a) = map(length,face_reference_id(a)) num_faces(a,d) = length(face_reference_id(a,d)) physical_faces(a,d) = physical_faces(a)[val_parameter(d)+1] +""" +""" num_nodes(a) = length(node_coordinates(a)) num_ambient_dims(a) = length(eltype(node_coordinates(a))) function face_offsets(a) @@ -144,12 +183,6 @@ struct ExtrusionPolytope{D,Tv,Ti} <: AbstractFaceGeometry int_type::Type{Ti} end -""" -""" -function num_dims(a::AbstractFaceGeometry) - val_parameter(a.num_dims) -end - """ """ function unit_simplex(num_dims;real_type=Float64,int_type=Int) @@ -213,6 +246,8 @@ end # Basic constructors - [`default_quadrature`](@ref) +- [`duffy_quadrature`](@ref) +- [`tensor_product_quadrature`](@ref) # Supertype hierarchy @@ -235,6 +270,8 @@ function quadrature(coordinates::Vector{SVector{D,T}},weights::Vector{T}) where Cuadrature(coordinates,weights) end +""" +""" function duffy_quadrature(geo,degree) @assert is_unit_simplex(geo) D = num_dims(geo) @@ -289,6 +326,8 @@ function duffy_quadrature(geo,degree) quadrature(x,w) end +""" +""" function tensor_product_quadrature(geo,degree_per_dir) @assert is_n_cube(geo) @assert is_axis_aligned(geo) @@ -346,6 +385,30 @@ function default_quadrature(geo,degree) end end +""" + abstract type AbstractMeshFace + +# Basic queries + +- [`geometry`](@ref) +- [`num_dims`](@ref) +- [`num_nodes`](@ref) +- [`node_coordinates`](@ref) +- [`monomial_exponents`](@ref) +- [`primal_basis`](@ref) +- [`dual_basis`](@ref) +- [`lib_to_user_nodes`](@ref) +- [`shape_functions`](@ref) +- [`tabulator`](@ref) +- [`boundary`](@ref) +- [`interior_nodes`](@ref) +- [`interior_node_permutations`](@ref) + +# Basic constructors + +- [`lagrange_mesh_face`](@ref) + +""" abstract type AbstractMeshFace <: GalerkinToolkitDataType end num_dims(f::AbstractMeshFace) = num_dims(geometry(f)) @@ -361,6 +424,8 @@ end lagrange_mesh_face(args...) = GenericLagrangeMeshFace(args...) +""" +""" function lagrange_mesh_face(geometry,order; space = default_space(geometry), lib_to_user_nodes = int_type(geometry)[]) @@ -462,6 +527,8 @@ end inner(a,b) = sum(map(*,a,b)) value(f,x) = f(x) +""" +""" function shape_functions(fe) primal = primal_basis(fe) dual = dual_basis(fe) @@ -478,6 +545,8 @@ function shape_functions(fe) end end +""" +""" function tabulator(fe) primal = primal_basis(fe) dual = dual_basis(fe) @@ -599,6 +668,28 @@ end # end #end +""" + abstract type AbstractFEMesh + +# Basic queries + +- [`node_coordinates`](@ref) +- [`face_nodes`](@ref) +- [`face_reference_id`](@ref) +- [`reference_faces`](@ref) +- [`periodic_nodes`](@ref) +- [`physical_faces`](@ref) +- [`physical_nodes`](@ref) +- [`outwards_normals`](@ref) + +# Basic constructors + +- [`fe_mesh`](@ref) +- [`mesh_from_gmsh`](@ref) +- [`cartesian_mesh`](@ref) +- [`mesh_from_chain`](@ref) + +""" abstract type AbstractFEMesh <: GalerkinToolkitDataType end struct GenericFEMesh{A,B,C,D,E,F,G} <: AbstractFEMesh @@ -615,6 +706,8 @@ function fe_mesh(args...) GenericFEMesh(args...) end +""" +""" function fe_mesh( node_coordinates, face_nodes, @@ -658,6 +751,8 @@ function with_gmsh(f;options=default_gmsh_options()) end end +""" +""" function mesh_from_gmsh(file;complexify=true,renumber=true,kwargs...) @assert ispath(file) "File not found: $(file)" with_gmsh(;kwargs...) do @@ -952,6 +1047,8 @@ function vtk_args(mesh) points, cells end +""" +""" function vtk_physical_faces!(vtk,mesh,d;physical_faces=physical_faces(mesh,d)) ndfaces = num_faces(mesh,d) for group in physical_faces @@ -991,6 +1088,8 @@ function vtk_physical_faces!(vtk,mesh;physical_faces=physical_faces(mesh)) vtk end +""" +""" function vtk_mesh_cell(ref_face) geom = geometry(ref_face) d = num_dims(geom) @@ -1281,6 +1380,8 @@ function vertex_permutations_from_face_geometry(geo) admissible_permutations end +""" +""" function interior_node_permutations(fe::AbstractMeshFace) interior_node_permutations_from_mesh_face(fe) end @@ -1333,6 +1434,21 @@ function interior_node_permutations_from_mesh_face(refface) node_perms end +""" + abstract type AbstractMeshTopology + +# Basic queries + +- [`face_incidence`](@ref) +- [`face_reference_id`](@ref) +- [`face_permutation_ids`](@ref) +- [`reference_faces`](@ref) + +# Basic constructors + +- [`topology`](@ref) + +""" abstract type AbstractMeshTopology <: GalerkinToolkitDataType end struct GenericMeshTopology{A,B,C,D} <: AbstractMeshTopology @@ -1346,6 +1462,8 @@ function mesh_topology(args...) GenericMeshTopology(args...) end +""" +""" function topology(mesh::AbstractFEMesh) topology_from_mesh(mesh) end @@ -1666,6 +1784,19 @@ function same_valid_ids(a,b) end ## TODO AbstractFaceTopology <: AbstractMeshTopology +""" + abstract type AbstractFaceTopology + +# Basic queries + +- [`boundary`](@ref) +- [`vertex_permutations`](@ref) + +# Basic constructors + +- [`topology`](@ref) + +""" abstract type AbstractFaceTopology <: GalerkinToolkitDataType end struct GenericFaceTopology{A,B} <: AbstractFaceTopology @@ -1695,6 +1826,8 @@ function topology_from_mesh_face(refface) face_topology(myboundary,myperms) end +""" +""" function complexify(mesh::AbstractFEMesh) complexify_mesh(mesh) end @@ -2144,6 +2277,8 @@ function physical_nodes(mesh,d) node_groups end +""" +""" function physical_nodes(mesh; merge_dims=Val(false), disjoint=Val(false), @@ -2190,6 +2325,8 @@ function physical_nodes(mesh; node_groups end +""" +""" function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) fill!(node_to_tag,zero(eltype(node_to_tag))) for d in dmax:-1:0 @@ -2210,6 +2347,8 @@ function classify_mesh_nodes!(node_to_tag,mesh,tag_to_name,dmax=num_dims(mesh)) node_to_tag end +""" +""" function physical_names(mesh,d) groups = physical_faces(mesh,d) Set(keys(groups)) @@ -2224,6 +2363,25 @@ function physical_names(mesh;merge_dims=Val(false)) reduce(union,d_to_names) end +""" +abstract type AbstractFEChain + +# Basic queries + +- [`node_coordinates`](@ref) +- [`face_nodes`](@ref) +- [`face_reference_id`](@ref) +- [`reference_faces`](@ref) +- [`periodic_nodes`](@ref) +- [`physical_faces`](@ref) +- [`physical_nodes`](@ref) +- [`outwards_normals`](@ref) + +# Basic constructors + +- [`fe_chain`](@ref) + +""" abstract type AbstractFEChain <: GalerkinToolkitDataType end struct GenericFEChain{A,B,C,D,E,F,G} <: AbstractFEChain @@ -2240,6 +2398,8 @@ function fe_chain(args...) GenericFEChain(args...) end +""" +""" function fe_chain( node_coordinates, face_nodes, @@ -2265,6 +2425,8 @@ function fe_mesh(chain::AbstractFEChain) mesh_from_chain(chain) end +""" +""" function mesh_from_chain(chain) D = num_dims(chain) cell_nodes = face_nodes(chain) @@ -2297,6 +2459,9 @@ function mesh_from_chain(chain) outwards_normals = onormals) end +# TODO simplexify mesh +""" +""" function simplexify(geo::AbstractFaceGeometry) simplexify_face_geometry(geo) end @@ -2443,6 +2608,8 @@ function simplexify_reference_face(ref_face) mesh_complex end +""" +""" function cartesian_mesh(domain,cells_per_dir;boundary=true,complexify=true,simplexify=false) mesh = if boundary if simplexify From ca38596924954ac70aa6dfc5893eab2c29431a01 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Wed, 31 Jan 2024 15:12:20 +0100 Subject: [PATCH 20/34] Added mesh partition --- src/mesh_interface.jl | 329 ++++++++++++++++++++++++++++++++++- test/mesh_interface_tests.jl | 46 ++++- 2 files changed, 364 insertions(+), 11 deletions(-) diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl index 6b34edd0..1de27d20 100644 --- a/src/mesh_interface.jl +++ b/src/mesh_interface.jl @@ -130,7 +130,7 @@ physical_faces(a,d) = physical_faces(a)[val_parameter(d)+1] """ num_nodes(a) = length(node_coordinates(a)) num_ambient_dims(a) = length(eltype(node_coordinates(a))) -function face_offsets(a) +function face_offset(a) D = num_dims(a) offsets = zeros(Int,D+1) for d in 1:D @@ -138,6 +138,17 @@ function face_offsets(a) end offsets end +function face_offset(a,d) + face_offset(a)[d+1] +end +function face_range(a,d) + o = face_offset(a,d) + o .+ (1:num_faces(a,d)) +end +function face_range(a) + D = num_dims(a) + map(d->face_range(a,d),0:D) +end function face_dim(a,d) n = num_faces(a,d) fill(d,n) @@ -713,7 +724,7 @@ function fe_mesh( face_nodes, face_reference_id, reference_faces; - periodic_nodes = eltype(eltype(face_reference_id))[], + periodic_nodes = eltype(eltype(face_reference_id))[] => eltype(eltype(face_reference_id))[], physical_faces = map(i->Dict{String,Vector{eltype(eltype(face_reference_id))}}(),face_reference_id), outwards_normals = nothing ) @@ -1062,7 +1073,7 @@ end function vtk_physical_faces!(vtk,mesh;physical_faces=physical_faces(mesh)) nfaces = sum(num_faces(mesh)) - offsets = face_offsets(mesh) + offsets = face_offset(mesh) D = num_dims(mesh) data = Dict{String,Vector{Int}}() for d in 0:D @@ -1088,6 +1099,25 @@ function vtk_physical_faces!(vtk,mesh;physical_faces=physical_faces(mesh)) vtk end +function vtk_physical_nodes!(vtk,mesh,d;physical_nodes=physical_nodes(mesh,d)) + nnodes = num_nodes(mesh) + for group in physical_nodes + name,nodes = group + nodes_mask = zeros(Int,nnodes) + nodes_mask[nodes] .= 1 + vtk[name,WriteVTK.VTKPointData()] = nodes_mask + end + vtk +end + +function vtk_physical_nodes!(vtk,mesh;physical_nodes=physical_nodes(mesh)) + D = num_dims(mesh) + for d in 0:D + vtk_physical_nodes!(vtk,mesh,d,physical_nodes=physical_nodes[d+1]) + end + vtk +end + """ """ function vtk_mesh_cell(ref_face) @@ -2312,7 +2342,7 @@ function physical_nodes(mesh; end else if name_priority === nothing - tag_to_name = sort(collect(names)) + tag_to_name = reverse(sort(collect(names))) else tag_to_name = name_priority end @@ -2649,12 +2679,15 @@ function domain_from_bounding_box(box) end function cartesian_mesh_with_boundary(domain,cells_per_dir) + if any(i->i!=1,cells_per_dir) && any(i->i<2,cells_per_dir) + error("At least 2 cells in any direction (or 1 cell in all directions)") + end function barrier( D, cell_to_nodes, nnodes, d_to_ldface_to_lnodes) - + node_to_n = zeros(Int32,nnodes) for nodes in cell_to_nodes for node in nodes @@ -2878,6 +2911,9 @@ function structured_simplex_chain(domain,cells_per_dir) end function structured_simplex_mesh_with_boundary(domain,cells_per_dir) + if any(i->i!=1,cells_per_dir) && any(i->i<2,cells_per_dir) + error("At least 2 cells in any direction (or 1 cell in all directions)") + end function barrier( D, cell_to_nodes, @@ -3332,3 +3368,286 @@ function Base.getindex(a::TwoWayPartition,i::Int) end end +function restrict(mesh::AbstractFEMesh,args...) + restrict_mesh(mesh,args...) +end + +function restrict_mesh(mesh,lnode_to_node,lface_to_face_mesh) + nnodes = num_nodes(mesh) + node_to_lnode = zeros(Int32,nnodes) + node_to_lnode[lnode_to_node] = 1:length(lnode_to_node) + lnode_to_coords = node_coordinates(mesh)[lnode_to_node] + lface_to_lnodes_mesh = map(lface_to_face_mesh,face_nodes(mesh)) do lface_to_face,face_to_nodes + lface_to_nodes = view(face_to_nodes,lface_to_face) + lface_to_lnodes = JaggedArray(lface_to_nodes) + f = node->node_to_lnode[node] + lface_to_lnodes.data .= f.(lface_to_lnodes.data) + lface_to_lnodes + end + lface_to_refid_mesh = map((a,b)->b[a],lface_to_face_mesh,face_reference_id(mesh)) + D = num_dims(mesh) + lgroups_mesh = map(lface_to_face_mesh,num_faces(mesh),physical_faces(mesh)) do lface_to_face, nfaces, groups + lgroups = Dict{String,Vector{Int32}}() + face_to_lface = zeros(Int32,nfaces) + face_to_lface[lface_to_face] = 1:length(lface_to_face) + for (k,faces) in groups + lgroups[k] = filter(i->i!=0,face_to_lface[faces]) + end + lgroups + end + pnode_to_node,pnode_to_master = periodic_nodes(mesh) + plnode_to_lnode = filter(i->i!=0,node_to_lnode[pnode_to_node]) + plnode_to_lmaster = filter(i->i!=0,node_to_lnode[pnode_to_master]) + if outwards_normals(mesh) !== nothing + lnormals = outwards_normals(mesh)[lface_to_face_mesh[end]] + else + lnormals = nothing + end + + lmesh = fe_mesh( + lnode_to_coords, + lface_to_lnodes_mesh, + lface_to_refid_mesh, + reference_faces(mesh); + physical_faces = lgroups_mesh, + periodic_nodes = (plnode_to_lnode=>plnode_to_lmaster), + outwards_normals = lnormals + ) + + lmesh +end + +function fe_assembly_graph(nnodes,d_to_cell_to_nodes) + ndata = 0 + for cell_to_nodes in d_to_cell_to_nodes + ncells = length(cell_to_nodes) + for cell in 1:ncells + nodes = cell_to_nodes[cell] + nlnodes = length(nodes) + ndata += nlnodes*nlnodes + end + end + I = zeros(Int32,ndata) + J = zeros(Int32,ndata) + p = 0 + for cell_to_nodes in d_to_cell_to_nodes + ncells = length(cell_to_nodes) + for cell in 1:ncells + nodes = cell_to_nodes[cell] + nlnodes = length(nodes) + for j in 1:nlnodes + for i in 1:nlnodes + p += 1 + I[p] = nodes[i] + J[p] = nodes[j] + end + end + end + end + V = ones(Int8,ndata) + g = sparse(I,J,V,nnodes,nnodes) + fill!(g.nzval,Int8(1)) + g +end + +function fe_assembly_graph(mesh::AbstractFEMesh;via=:nodes) + if via === :nodes + face_nodes_mesh = face_nodes(mesh) + nnodes = num_nodes(mesh) + return fe_assembly_graph(nnodes,face_nodes_mesh) + elseif via === :cells + D = num_dims(mesh) + cell_to_nodes = face_nodes(mesh,D) + nnodes = num_nodes(mesh) + node_to_cells = generate_face_coboundary(cell_to_nodes,nnodes) + ncells = length(cell_to_nodes) + return fe_assembly_graph(ncells,[node_to_cells]) + else + error("case not implemented") + end +end +struct PMesh{A,B,C} <: GalerkinToolkitDataType + mesh_partition::A + node_partition::B + face_partition::C +end +PartitionedArrays.partition(m::PMesh) = m.mesh_partition +face_partition(a::PMesh,d) = a.face_partition[d+1] +node_partition(a::PMesh) = a.node_partition +function index_partition(a::PMesh) + function setup(nodes,faces...) + PMeshLocalIds(nodes,faces) + end + map(setup,a.node_partition,a.face_partition...) +end + +struct PMeshLocalIds{A,B} <: GalerkinToolkitDataType + node_indices::A + face_indices::B +end +node_indices(a::PMeshLocalIds) = a.node_indices +face_indices(a::PMeshLocalIds,d) = a.face_indices[d+1] + +function partition_mesh(colorize,ranks,mesh;via=:nodes,renumber=true) + root = 1 + colors_root = map(ranks) do part + if part == root + g = fe_assembly_graph(mesh;via) + colors::Vector{Int32} = colorize(g,length(ranks)) + else + colors = Int32[] + end + colors + end + x_to_colors = PartitionedArrays.getany(multicast(colors_root;source=root)) + if via === :nodes + partition_mesh_via_nodes(x_to_colors,ranks,mesh;renumber) + elseif via === :cells + partition_mesh_via_cells(x_to_colors,ranks,mesh;renumber) + else + error("case not implemented") + end +end + +function partition_mesh_via_nodes(node_to_color,ranks,mesh;renumber) + face_nodes_mesh = face_nodes(mesh) + nnodes = num_nodes(mesh) + node_faces_mesh = map(face_nodes_mesh) do face_to_nodes + generate_face_coboundary(face_to_nodes,nnodes) + end + function setup(part) + onode_to_node = findall(color->color==part,node_to_color) + node_to_mask = fill(false,nnodes) + local_faces = map(face_nodes_mesh,node_faces_mesh) do face_to_nodes, node_to_faces + nfaces = length(face_to_nodes) + face_to_mask = fill(false,nfaces) + for node in onode_to_node + for face in node_to_faces[node] + face_to_mask[face] = true + end + end + lface_to_face = findall(face_to_mask) + nlfaces = length(lface_to_face) + lface_to_color = zeros(Int32,nlfaces) + for (lface,face) in enumerate(lface_to_face) + nodes = face_to_nodes[face] + color = maximum(node->node_to_color[node],nodes) + lface_to_color[lface] = color + node_to_mask[nodes] .= true + end + oface_to_lface = findall(color->color==part,lface_to_color) + hface_to_lface = findall(color->color!=part,lface_to_color) + oface_to_face = lface_to_face[oface_to_lface] + hface_to_face = lface_to_face[hface_to_lface] + hface_to_color = lface_to_color[hface_to_lface] + own = OwnIndices(nfaces,part,oface_to_face) + ghost = GhostIndices(nfaces,hface_to_face,hface_to_color) + OwnAndGhostIndices(own,ghost) + end + lnode_to_node = findall(node_to_mask) + lnode_to_color = node_to_color[lnode_to_node] + onode_to_lnode = findall(color->color==part,lnode_to_color) + hnode_to_lnode = findall(color->color!=part,lnode_to_color) + onode_to_node = lnode_to_node[onode_to_lnode] + hnode_to_node = lnode_to_node[hnode_to_lnode] + hnode_to_color = lnode_to_color[hnode_to_lnode] + own = OwnIndices(nnodes,part,onode_to_node) + ghost = GhostIndices(nnodes,hnode_to_node,hnode_to_color) + local_nodes = OwnAndGhostIndices(own,ghost,node_to_color) + lface_to_face_mesh = map(local_to_global,local_faces) + lmesh = restrict_mesh(mesh,lnode_to_node,lface_to_face_mesh) + lmesh, local_nodes, Tuple(local_faces) + end + mesh_partition, node_partition, face_partition_array = map(setup,ranks) |> tuple_of_arrays + face_partition = face_partition_array |> tuple_of_arrays + if renumber + node_partition = renumber_partition(node_partition) + face_partition = map(renumber_partition,face_partition) + end + # TODO here we have the opportunity to provide the parts rcv + assembly_graph(node_partition) + map(assembly_graph,face_partition) + pmesh = PMesh(mesh_partition,node_partition,face_partition) + pmesh +end + +function partition_mesh_via_cells(cell_to_color,ranks,mesh;renumber) + D = num_dims(mesh) + cell_to_nodes = face_nodes(mesh,D) + nnodes = num_nodes(mesh) + node_to_cells = generate_face_coboundary(cell_to_nodes,nnodes) + ncells = length(cell_to_nodes) + function setup(part) + ocell_to_cell = findall(color->color==part,cell_to_color) + own = OwnIndices(ncells,part,ocell_to_cell) + ghost = GhostIndices(ncells,Int[],Int32[]) + local_cells = OwnAndGhostIndices(own,ghost) + node_to_mask = fill(false,nnodes) + for cell in ocell_to_cell + nodes = cell_to_nodes[cell] + node_to_mask[nodes] .= true + end + lnode_to_node = findall(node_to_mask) + nlnodes = length(lnode_to_node) + lnode_to_color = zeros(Int32,nlnodes) + for (lnode, node) in enumerate(lnode_to_node) + cells = node_to_cells[node] + color = maximum(cell->cell_to_color[cell],cells) + lnode_to_color[lnode] = color + end + onode_to_lnode = findall(color->color==part,lnode_to_color) + hnode_to_lnode = findall(color->color!=part,lnode_to_color) + onode_to_node = lnode_to_node[onode_to_lnode] + hnode_to_node = lnode_to_node[hnode_to_lnode] + hnode_to_color = lnode_to_color[hnode_to_lnode] + own = OwnIndices(nnodes,part,onode_to_node) + ghost = GhostIndices(nnodes,hnode_to_node,hnode_to_color) + local_nodes = OwnAndGhostIndices(own,ghost) + topo = topology(mesh) + D = num_dims(mesh) + local_faces = map(0:(D-1)) do d + face_to_cells = face_incidence(topo,d,D) + nfaces = length(face_to_cells) + face_to_mask = fill(false,nfaces) + for face in 1:nfaces + cells = face_to_cells[face] + if any(cell->cell_to_color[cell]==part,cells) + face_to_mask[face] = true + end + end + lface_to_face = findall(face_to_mask) + nlfaces = length(lface_to_face) + lface_to_color = zeros(Int32,nlfaces) + for (lface,face) in enumerate(lface_to_face) + cells = face_to_cells[face] + color = maximum(cell->cell_to_color[cell],cells) + lface_to_color[lface] = color + end + oface_to_lface = findall(color->color==part,lface_to_color) + hface_to_lface = findall(color->color!=part,lface_to_color) + oface_to_face = lface_to_face[oface_to_lface] + hface_to_face = lface_to_face[hface_to_lface] + hface_to_color = lface_to_color[hface_to_lface] + own = OwnIndices(nfaces,part,oface_to_face) + ghost = GhostIndices(nfaces,hface_to_face,hface_to_color) + OwnAndGhostIndices(own,ghost) + end + push!(local_faces,local_cells) + lnode_to_node_mesh = local_to_global(local_nodes) + lface_to_face_mesh = map(local_to_global,local_faces) + lmesh = restrict_mesh(mesh,lnode_to_node_mesh,lface_to_face_mesh) + lmesh, local_nodes, Tuple(local_faces) + end + mesh_partition, node_partition, face_partition_array = map(setup,ranks) |> tuple_of_arrays + face_partition = face_partition_array |> tuple_of_arrays + if renumber + node_partition = renumber_partition(node_partition) + face_partition = map(renumber_partition,face_partition) + end + # TODO here we have the opportunity to provide the parts rcv + assembly_graph(node_partition) + map(assembly_graph,face_partition) + pmesh = PMesh(mesh_partition,node_partition,face_partition) + pmesh +end + diff --git a/test/mesh_interface_tests.jl b/test/mesh_interface_tests.jl index eebb1c8a..e224779d 100644 --- a/test/mesh_interface_tests.jl +++ b/test/mesh_interface_tests.jl @@ -3,6 +3,8 @@ module MeshInterfaceTests using Test import GalerkinToolkit as gk using WriteVTK +using PartitionedArrays +using Metis spx0 = gk.unit_simplex(0) spx1 = gk.unit_simplex(1) @@ -32,7 +34,6 @@ quad = gk.default_quadrature(cube1,degree) quad = gk.default_quadrature(cube2,degree) quad = gk.default_quadrature(cube3,degree) - order = 1 fe = gk.lagrange_mesh_face(spx0,order) fe = gk.lagrange_mesh_face(spx1,order) @@ -71,6 +72,13 @@ msh = joinpath(@__DIR__,"..","assets","demo.msh") mesh = gk.mesh_from_gmsh(msh;complexify=false) vtk_grid(joinpath(outdir,"demo"),gk.vtk_args(mesh)...) do vtk gk.vtk_physical_faces!(vtk,mesh) + gk.vtk_physical_nodes!(vtk,mesh) +end +for d in 0:gk.num_dims(mesh) + vtk_grid(joinpath(outdir,"demo_$d"),gk.vtk_args(mesh,d)...) do vtk + gk.vtk_physical_faces!(vtk,mesh,d) + gk.vtk_physical_nodes!(vtk,mesh,d) + end end @show gk.unit_simplex(0) |> gk.boundary @@ -99,6 +107,18 @@ mesh = gk.cartesian_mesh(domain,cells,boundary=false) mesh = gk.cartesian_mesh(domain,cells,simplexify=true) mesh = gk.cartesian_mesh(domain,cells,boundary=false,simplexify=true) +mesh = gk.cartesian_mesh(domain,cells) +vtk_grid(joinpath(outdir,"cartesian"),gk.vtk_args(mesh)...) do vtk + gk.vtk_physical_faces!(vtk,mesh) + gk.vtk_physical_nodes!(vtk,mesh) +end +for d in 0:gk.num_dims(mesh) + vtk_grid(joinpath(outdir,"cartesian_$d"),gk.vtk_args(mesh,d)...) do vtk + gk.vtk_physical_faces!(vtk,mesh,d) + gk.vtk_physical_nodes!(vtk,mesh,d) + end +end + mesh = gk.mesh_from_gmsh(msh) face_groups = gk.physical_faces(mesh) group_names = gk.physical_names(mesh,2) @@ -109,10 +129,24 @@ node_groups = gk.physical_nodes(mesh;merge_dims=true,disjoint=true) vmesh, vglue = gk.visualization_mesh(mesh) -#∂spx0 = gk.boundary(spx0) -#∂spx0 = gk.boundary(spx1) -#∂cube0 = gk.boundary(cube0) - - +np = 4 +ranks = DebugArray(LinearIndices((np,))) +mesh = gk.mesh_from_gmsh(msh) +pmesh = gk.partition_mesh(Metis.partition,ranks,mesh,via=:nodes) +function setup(mesh,ids,rank) + face_to_owner = zeros(Int,sum(gk.num_faces(mesh))) + D = gk.num_dims(mesh) + for d in 0:D + face_to_owner[gk.face_range(mesh,d)] = local_to_owner(gk.face_indices(ids,d)) + end + pvtk_grid(joinpath(outdir,"pmesh"),gk.vtk_args(mesh)...;part=rank,nparts=np) do vtk + gk.vtk_physical_faces!(vtk,mesh) + gk.vtk_physical_nodes!(vtk,mesh) + vtk["piece"] = fill(rank,sum(gk.num_faces(mesh))) + vtk["owner"] = local_to_owner(gk.node_indices(ids)) + vtk["owner"] = face_to_owner + end +end +map(setup,partition(pmesh),gk.index_partition(pmesh),ranks) end # module From 001662a2a430600bec2166dd4fac439a30b37b15 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Wed, 31 Jan 2024 17:42:06 +0100 Subject: [PATCH 21/34] Cleanup in partition_mesh --- src/mesh_interface.jl | 272 +++++++++++++++++++++-------------- test/mesh_interface_tests.jl | 39 ++++- 2 files changed, 201 insertions(+), 110 deletions(-) diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl index 1de27d20..b2fa498f 100644 --- a/src/mesh_interface.jl +++ b/src/mesh_interface.jl @@ -3417,55 +3417,78 @@ function restrict_mesh(mesh,lnode_to_node,lface_to_face_mesh) lmesh end -function fe_assembly_graph(nnodes,d_to_cell_to_nodes) - ndata = 0 - for cell_to_nodes in d_to_cell_to_nodes - ncells = length(cell_to_nodes) - for cell in 1:ncells - nodes = cell_to_nodes[cell] - nlnodes = length(nodes) - ndata += nlnodes*nlnodes +function mesh_graph(mesh::AbstractFEMesh; + graph_nodes, + graph_edges= graph_nodes === :nodes ? (:cells) : (:nodes), + d=num_dims(mesh)) + function barrier(nnodes,d_to_cell_to_nodes) + ndata = 0 + for cell_to_nodes in d_to_cell_to_nodes + ncells = length(cell_to_nodes) + for cell in 1:ncells + nodes = cell_to_nodes[cell] + nlnodes = length(nodes) + ndata += nlnodes*nlnodes + end end - end - I = zeros(Int32,ndata) - J = zeros(Int32,ndata) - p = 0 - for cell_to_nodes in d_to_cell_to_nodes - ncells = length(cell_to_nodes) - for cell in 1:ncells - nodes = cell_to_nodes[cell] - nlnodes = length(nodes) - for j in 1:nlnodes - for i in 1:nlnodes - p += 1 - I[p] = nodes[i] - J[p] = nodes[j] + I = zeros(Int32,ndata) + J = zeros(Int32,ndata) + p = 0 + for cell_to_nodes in d_to_cell_to_nodes + ncells = length(cell_to_nodes) + for cell in 1:ncells + nodes = cell_to_nodes[cell] + nlnodes = length(nodes) + for j in 1:nlnodes + for i in 1:nlnodes + p += 1 + I[p] = nodes[i] + J[p] = nodes[j] + end end end end + V = ones(Int8,ndata) + g = sparse(I,J,V,nnodes,nnodes) + fill!(g.nzval,Int8(1)) + g end - V = ones(Int8,ndata) - g = sparse(I,J,V,nnodes,nnodes) - fill!(g.nzval,Int8(1)) - g -end -function fe_assembly_graph(mesh::AbstractFEMesh;via=:nodes) - if via === :nodes - face_nodes_mesh = face_nodes(mesh) + if graph_nodes === :nodes nnodes = num_nodes(mesh) - return fe_assembly_graph(nnodes,face_nodes_mesh) - elseif via === :cells + if graph_edges === :cells + d = num_dims(mesh) + face_nodes_mesh = face_nodes(mesh,d) + return barrier(nnodes,[face_nodes_mesh]) + elseif graph_edges === :faces + face_nodes_mesh = face_nodes(mesh,d) + return barrier(nnodes,[face_nodes_mesh]) + elseif graph_edges === :allfaces + face_nodes_mesh = face_nodes(mesh) + return barrier(nnodes,face_nodes_mesh) + else + error("case not implemented") + end + elseif graph_nodes === :cells D = num_dims(mesh) - cell_to_nodes = face_nodes(mesh,D) - nnodes = num_nodes(mesh) - node_to_cells = generate_face_coboundary(cell_to_nodes,nnodes) - ncells = length(cell_to_nodes) - return fe_assembly_graph(ncells,[node_to_cells]) + ndfaces = num_faces(mesh,D) + if graph_edges === :nodes + dface_to_nodes = face_nodes(mesh,D) + nnodes = num_nodes(mesh) + node_to_dfaces = generate_face_coboundary(dface_to_nodes,nnodes) + return barrier(ndfaces,[node_to_dfaces]) + elseif graph_edges === :faces + topo = topology(mesh) + node_to_dfaces = face_incidence(topo,d,D) + return barrier(ndfaces,[node_to_dfaces]) + else + error("case not implemented") + end else error("case not implemented") end end + struct PMesh{A,B,C} <: GalerkinToolkitDataType mesh_partition::A node_partition::B @@ -3488,41 +3511,66 @@ end node_indices(a::PMeshLocalIds) = a.node_indices face_indices(a::PMeshLocalIds,d) = a.face_indices[d+1] -function partition_mesh(colorize,ranks,mesh;via=:nodes,renumber=true) - root = 1 - colors_root = map(ranks) do part - if part == root - g = fe_assembly_graph(mesh;via) - colors::Vector{Int32} = colorize(g,length(ranks)) - else - colors = Int32[] - end - colors +function partition_mesh( + graph_node_to_color,ranks,mesh; + graph_nodes, + renumber=true, + ghost_layers=1, + graph= ghost_layers == 0 ? nothing : mesh_graph(mesh;graph_nodes), + multicast=false, + source=MAIN) + + if multicast == true + global_to_owner = PartitionedArrays.getany(PartitionedArrays.multicast(graph_node_to_color;source)) + else + global_to_owner = graph_node_to_color end - x_to_colors = PartitionedArrays.getany(multicast(colors_root;source=root)) - if via === :nodes - partition_mesh_via_nodes(x_to_colors,ranks,mesh;renumber) - elseif via === :cells - partition_mesh_via_cells(x_to_colors,ranks,mesh;renumber) + + if graph_nodes === :nodes + partition_mesh_nodes(global_to_owner,ranks,mesh,graph,ghost_layers,renumber) + elseif graph_nodes === :cells + partition_mesh_cells(global_to_owner,ranks,mesh,graph,ghost_layers,renumber) else - error("case not implemented") + error("Case not implemented") end end -function partition_mesh_via_nodes(node_to_color,ranks,mesh;renumber) +function partition_mesh_nodes(node_to_color,ranks,mesh,graph,ghost_layers,renumber) face_nodes_mesh = face_nodes(mesh) nnodes = num_nodes(mesh) - node_faces_mesh = map(face_nodes_mesh) do face_to_nodes - generate_face_coboundary(face_to_nodes,nnodes) - end function setup(part) onode_to_node = findall(color->color==part,node_to_color) node_to_mask = fill(false,nnodes) - local_faces = map(face_nodes_mesh,node_faces_mesh) do face_to_nodes, node_to_faces + if ghost_layers == 0 + node_to_mask[onode_to_node] .= true + elseif ghost_layers == 1 + for node in onode_to_node + pini = graph.colptr[node] + pend = graph.colptr[node+1]-1 + for p in pini:pend + node2 = graph.rowval[p] + node_to_mask[node2] = true + end + end + else + error("case not implemented") + end + lnode_to_node = findall(node_to_mask) + lnode_to_color = node_to_color[lnode_to_node] + onode_to_lnode = findall(color->color==part,lnode_to_color) + hnode_to_lnode = findall(color->color!=part,lnode_to_color) + onode_to_node = lnode_to_node[onode_to_lnode] + hnode_to_node = lnode_to_node[hnode_to_lnode] + hnode_to_color = lnode_to_color[hnode_to_lnode] + own = OwnIndices(nnodes,part,onode_to_node) + ghost = GhostIndices(nnodes,hnode_to_node,hnode_to_color) + local_nodes = OwnAndGhostIndices(own,ghost,node_to_color) + local_faces = map(face_nodes_mesh) do face_to_nodes nfaces = length(face_to_nodes) face_to_mask = fill(false,nfaces) - for node in onode_to_node - for face in node_to_faces[node] + for face in 1:nfaces + nodes = face_to_nodes[face] + if all(node->node_to_mask[node],nodes) face_to_mask[face] = true end end @@ -3533,7 +3581,6 @@ function partition_mesh_via_nodes(node_to_color,ranks,mesh;renumber) nodes = face_to_nodes[face] color = maximum(node->node_to_color[node],nodes) lface_to_color[lface] = color - node_to_mask[nodes] .= true end oface_to_lface = findall(color->color==part,lface_to_color) hface_to_lface = findall(color->color!=part,lface_to_color) @@ -3544,16 +3591,6 @@ function partition_mesh_via_nodes(node_to_color,ranks,mesh;renumber) ghost = GhostIndices(nfaces,hface_to_face,hface_to_color) OwnAndGhostIndices(own,ghost) end - lnode_to_node = findall(node_to_mask) - lnode_to_color = node_to_color[lnode_to_node] - onode_to_lnode = findall(color->color==part,lnode_to_color) - hnode_to_lnode = findall(color->color!=part,lnode_to_color) - onode_to_node = lnode_to_node[onode_to_lnode] - hnode_to_node = lnode_to_node[hnode_to_lnode] - hnode_to_color = lnode_to_color[hnode_to_lnode] - own = OwnIndices(nnodes,part,onode_to_node) - ghost = GhostIndices(nnodes,hnode_to_node,hnode_to_color) - local_nodes = OwnAndGhostIndices(own,ghost,node_to_color) lface_to_face_mesh = map(local_to_global,local_faces) lmesh = restrict_mesh(mesh,lnode_to_node,lface_to_face_mesh) lmesh, local_nodes, Tuple(local_faces) @@ -3571,53 +3608,49 @@ function partition_mesh_via_nodes(node_to_color,ranks,mesh;renumber) pmesh end -function partition_mesh_via_cells(cell_to_color,ranks,mesh;renumber) +function partition_mesh_cells(cell_to_color,ranks,mesh,graph,ghost_layers,renumber) D = num_dims(mesh) - cell_to_nodes = face_nodes(mesh,D) - nnodes = num_nodes(mesh) - node_to_cells = generate_face_coboundary(cell_to_nodes,nnodes) - ncells = length(cell_to_nodes) + ncells = num_faces(mesh,D) + topo = topology(mesh) function setup(part) ocell_to_cell = findall(color->color==part,cell_to_color) + cell_to_mask = fill(false,ncells) + if ghost_layers == 0 + cell_to_mask[ocell_to_cell] .= true + elseif ghost_layers == 1 + for cell in ocell_to_cell + pini = graph.colptr[cell] + pend = graph.colptr[cell+1]-1 + for p in pini:pend + cell2 = graph.rowval[p] + cell_to_mask[cell2] = true + end + end + else + error("case not implemented") + end + lcell_to_cell = findall(cell_to_mask) + lcell_to_color = cell_to_color[lcell_to_cell] + ocell_to_lcell = findall(color->color==part,lcell_to_color) + hcell_to_lcell = findall(color->color!=part,lcell_to_color) + ocell_to_cell = lcell_to_cell[ocell_to_lcell] + hcell_to_cell = lcell_to_cell[hcell_to_lcell] + hcell_to_color = lcell_to_color[hcell_to_lcell] own = OwnIndices(ncells,part,ocell_to_cell) - ghost = GhostIndices(ncells,Int[],Int32[]) + ghost = GhostIndices(ncells,hcell_to_cell,hcell_to_color) local_cells = OwnAndGhostIndices(own,ghost) - node_to_mask = fill(false,nnodes) - for cell in ocell_to_cell - nodes = cell_to_nodes[cell] - node_to_mask[nodes] .= true - end - lnode_to_node = findall(node_to_mask) - nlnodes = length(lnode_to_node) - lnode_to_color = zeros(Int32,nlnodes) - for (lnode, node) in enumerate(lnode_to_node) - cells = node_to_cells[node] - color = maximum(cell->cell_to_color[cell],cells) - lnode_to_color[lnode] = color - end - onode_to_lnode = findall(color->color==part,lnode_to_color) - hnode_to_lnode = findall(color->color!=part,lnode_to_color) - onode_to_node = lnode_to_node[onode_to_lnode] - hnode_to_node = lnode_to_node[hnode_to_lnode] - hnode_to_color = lnode_to_color[hnode_to_lnode] - own = OwnIndices(nnodes,part,onode_to_node) - ghost = GhostIndices(nnodes,hnode_to_node,hnode_to_color) - local_nodes = OwnAndGhostIndices(own,ghost) - topo = topology(mesh) - D = num_dims(mesh) local_faces = map(0:(D-1)) do d - face_to_cells = face_incidence(topo,d,D) - nfaces = length(face_to_cells) + cell_to_faces = face_incidence(topo,D,d) + nfaces = num_faces(mesh,d) face_to_mask = fill(false,nfaces) - for face in 1:nfaces - cells = face_to_cells[face] - if any(cell->cell_to_color[cell]==part,cells) - face_to_mask[face] = true - end + for cell in lcell_to_cell + faces = cell_to_faces[cell] + face_to_mask[faces] .= true end lface_to_face = findall(face_to_mask) nlfaces = length(lface_to_face) lface_to_color = zeros(Int32,nlfaces) + face_to_cells = face_incidence(topo,d,D) for (lface,face) in enumerate(lface_to_face) cells = face_to_cells[face] color = maximum(cell->cell_to_color[cell],cells) @@ -3633,6 +3666,30 @@ function partition_mesh_via_cells(cell_to_color,ranks,mesh;renumber) OwnAndGhostIndices(own,ghost) end push!(local_faces,local_cells) + nnodes = num_nodes(mesh) + cell_to_nodes = face_nodes(mesh,D) + node_to_mask = fill(false,nnodes) + for cell in lcell_to_cell + nodes = cell_to_nodes[cell] + node_to_mask[nodes] .= true + end + lnode_to_node = findall(node_to_mask) + nlnodes = length(lnode_to_node) + lnode_to_color = zeros(Int32,nlnodes) + node_to_cells = generate_face_coboundary(cell_to_nodes,nnodes) + for (lnode,node) in enumerate(lnode_to_node) + cells = node_to_cells[node] + color = maximum(cell->cell_to_color[cell],cells) + lnode_to_color[lnode] = color + end + onode_to_lnode = findall(color->color==part,lnode_to_color) + hnode_to_lnode = findall(color->color!=part,lnode_to_color) + onode_to_node = lnode_to_node[onode_to_lnode] + hnode_to_node = lnode_to_node[hnode_to_lnode] + hnode_to_color = lnode_to_color[hnode_to_lnode] + own = OwnIndices(nnodes,part,onode_to_node) + ghost = GhostIndices(nnodes,hnode_to_node,hnode_to_color) + local_nodes = OwnAndGhostIndices(own,ghost) lnode_to_node_mesh = local_to_global(local_nodes) lface_to_face_mesh = map(local_to_global,local_faces) lmesh = restrict_mesh(mesh,lnode_to_node_mesh,lface_to_face_mesh) @@ -3651,3 +3708,4 @@ function partition_mesh_via_cells(cell_to_color,ranks,mesh;renumber) pmesh end + diff --git a/test/mesh_interface_tests.jl b/test/mesh_interface_tests.jl index e224779d..6c9485ce 100644 --- a/test/mesh_interface_tests.jl +++ b/test/mesh_interface_tests.jl @@ -129,10 +129,24 @@ node_groups = gk.physical_nodes(mesh;merge_dims=true,disjoint=true) vmesh, vglue = gk.visualization_mesh(mesh) -np = 4 +mesh = gk.cartesian_mesh(domain,cells) +gk.mesh_graph(mesh;graph_nodes=:nodes) +gk.mesh_graph(mesh;graph_nodes=:cells) +gk.mesh_graph(mesh;graph_nodes=:nodes,graph_edges=:cells) +gk.mesh_graph(mesh;graph_nodes=:nodes,graph_edges=:faces) +gk.mesh_graph(mesh;graph_nodes=:cells,graph_edges=:nodes) +gk.mesh_graph(mesh;graph_nodes=:cells,graph_edges=:faces) + +np = 2 ranks = DebugArray(LinearIndices((np,))) -mesh = gk.mesh_from_gmsh(msh) -pmesh = gk.partition_mesh(Metis.partition,ranks,mesh,via=:nodes) +mesh = gk.cartesian_mesh((0,1,0,1),(2,2)) +cell_to_color = [1,1,2,2] +pmesh = gk.partition_mesh(cell_to_color,ranks,mesh;graph_nodes=:cells,ghost_layers=0) +pmesh = gk.partition_mesh(cell_to_color,ranks,mesh;graph_nodes=:cells,ghost_layers=1) +node_to_color = [1,1,1,1,2,2,2,2,2] +pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:nodes,ghost_layers=0) +pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:nodes,ghost_layers=1) + function setup(mesh,ids,rank) face_to_owner = zeros(Int,sum(gk.num_faces(mesh))) D = gk.num_dims(mesh) @@ -149,4 +163,23 @@ function setup(mesh,ids,rank) end map(setup,partition(pmesh),gk.index_partition(pmesh),ranks) +np = 4 +ranks = DebugArray(LinearIndices((np,))) + +mesh = gk.mesh_from_gmsh(msh) +graph = gk.mesh_graph(mesh;graph_nodes=:nodes) +node_to_color = map_main(ranks) do ranks + Metis.partition(graph,np) +end +pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:nodes,ghost_layers=0,graph,multicast=true) +pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:nodes,ghost_layers=1,graph,multicast=true) + +mesh = gk.mesh_from_gmsh(msh) +graph = gk.mesh_graph(mesh;graph_nodes=:cells) +node_to_color = map_main(ranks) do ranks + Metis.partition(graph,np) +end +pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:cells,ghost_layers=0,graph,multicast=true) +pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:cells,ghost_layers=1,graph,multicast=true) + end # module From 2f52fabb337361f124c021554d73ae7f5068575a Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Thu, 1 Feb 2024 18:17:14 +0100 Subject: [PATCH 22/34] Better API for the mesh partition --- src/GalerkinToolkit.jl | 1 + src/mesh_interface.jl | 37 ++++++++++++---------- test/mesh_interface_tests.jl | 60 +++++++++++++++++++++++++++--------- 3 files changed, 66 insertions(+), 32 deletions(-) diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 7a67bf6e..74df5682 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -9,6 +9,7 @@ using Gmsh using PartitionedArrays using Combinatorics using SparseArrays +using Metis include("mesh_interface.jl") diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl index b2fa498f..c51e0d96 100644 --- a/src/mesh_interface.jl +++ b/src/mesh_interface.jl @@ -3418,7 +3418,7 @@ function restrict_mesh(mesh,lnode_to_node,lface_to_face_mesh) end function mesh_graph(mesh::AbstractFEMesh; - graph_nodes, + graph_nodes = :cells, graph_edges= graph_nodes === :nodes ? (:cells) : (:nodes), d=num_dims(mesh)) function barrier(nnodes,d_to_cell_to_nodes) @@ -3511,30 +3511,33 @@ end node_indices(a::PMeshLocalIds) = a.node_indices face_indices(a::PMeshLocalIds,d) = a.face_indices[d+1] -function partition_mesh( - graph_node_to_color,ranks,mesh; - graph_nodes, - renumber=true, +function partition_mesh(mesh,np; + ranks = LinearIndices((np,)), + graph_nodes = :cells, + graph = mesh_graph(mesh;graph_nodes), + graph_partition = Metis.partition(graph,np), ghost_layers=1, - graph= ghost_layers == 0 ? nothing : mesh_graph(mesh;graph_nodes), - multicast=false, - source=MAIN) - - if multicast == true - global_to_owner = PartitionedArrays.getany(PartitionedArrays.multicast(graph_node_to_color;source)) - else - global_to_owner = graph_node_to_color - end - + renumber = true, + ) if graph_nodes === :nodes - partition_mesh_nodes(global_to_owner,ranks,mesh,graph,ghost_layers,renumber) + partition_mesh_nodes(graph_partition,ranks,mesh,graph,ghost_layers,renumber) elseif graph_nodes === :cells - partition_mesh_cells(global_to_owner,ranks,mesh,graph,ghost_layers,renumber) + partition_mesh_cells(graph_partition,ranks,mesh,graph,ghost_layers,renumber) else error("Case not implemented") end end +function scatter_mesh(pmeshes_on_main;source=MAIN) + snd = map_main(pmeshes_on_main;main=source) do pmesh + map(tuple,pmesh.mesh_partition,pmesh.node_partition,map(tuple,pmesh.face_partition...)) + end + rcv = scatter(snd;source) + mesh_partition, node_partition, face_partition_array = rcv |> tuple_of_arrays + face_partition = face_partition_array |> tuple_of_arrays + PMesh(mesh_partition,node_partition,face_partition) +end + function partition_mesh_nodes(node_to_color,ranks,mesh,graph,ghost_layers,renumber) face_nodes_mesh = face_nodes(mesh) nnodes = num_nodes(mesh) diff --git a/test/mesh_interface_tests.jl b/test/mesh_interface_tests.jl index 6c9485ce..1b577e69 100644 --- a/test/mesh_interface_tests.jl +++ b/test/mesh_interface_tests.jl @@ -141,11 +141,14 @@ np = 2 ranks = DebugArray(LinearIndices((np,))) mesh = gk.cartesian_mesh((0,1,0,1),(2,2)) cell_to_color = [1,1,2,2] -pmesh = gk.partition_mesh(cell_to_color,ranks,mesh;graph_nodes=:cells,ghost_layers=0) -pmesh = gk.partition_mesh(cell_to_color,ranks,mesh;graph_nodes=:cells,ghost_layers=1) +pmesh = gk.partition_mesh(mesh,np) +pmesh = gk.partition_mesh(mesh,np;ranks) +pmesh = gk.partition_mesh(mesh,np;ranks,graph_partition=cell_to_color,graph_nodes=:cells,ghost_layers=0) +pmesh = gk.partition_mesh(mesh,np;ranks,graph_partition=cell_to_color,graph_nodes=:cells,ghost_layers=1) + node_to_color = [1,1,1,1,2,2,2,2,2] -pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:nodes,ghost_layers=0) -pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:nodes,ghost_layers=1) +pmesh = gk.partition_mesh(mesh,np;ranks,graph_partition=node_to_color,graph_nodes=:nodes,ghost_layers=0) +pmesh = gk.partition_mesh(mesh,np;ranks,graph_partition=node_to_color,graph_nodes=:nodes,ghost_layers=1) function setup(mesh,ids,rank) face_to_owner = zeros(Int,sum(gk.num_faces(mesh))) @@ -163,23 +166,50 @@ function setup(mesh,ids,rank) end map(setup,partition(pmesh),gk.index_partition(pmesh),ranks) +## In this case, we do the hard work only on the main proc np = 4 ranks = DebugArray(LinearIndices((np,))) +pmesh = map_main(ranks) do ranks + mesh = gk.mesh_from_gmsh(msh) + graph_nodes = :nodes + graph = gk.mesh_graph(mesh;graph_nodes=:nodes) + graph_partition = Metis.partition(graph,np) + gk.partition_mesh(mesh,np;graph,graph_nodes,graph_partition) +end |> gk.scatter_mesh -mesh = gk.mesh_from_gmsh(msh) -graph = gk.mesh_graph(mesh;graph_nodes=:nodes) -node_to_color = map_main(ranks) do ranks - Metis.partition(graph,np) +function setup(mesh,ids,rank) + face_to_owner = zeros(Int,sum(gk.num_faces(mesh))) + D = gk.num_dims(mesh) + for d in 0:D + face_to_owner[gk.face_range(mesh,d)] = local_to_owner(gk.face_indices(ids,d)) + end + pvtk_grid(joinpath(outdir,"pmesh"),gk.vtk_args(mesh)...;part=rank,nparts=np) do vtk + gk.vtk_physical_faces!(vtk,mesh) + gk.vtk_physical_nodes!(vtk,mesh) + vtk["piece"] = fill(rank,sum(gk.num_faces(mesh))) + vtk["owner"] = local_to_owner(gk.node_indices(ids)) + vtk["owner"] = face_to_owner + end end -pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:nodes,ghost_layers=0,graph,multicast=true) -pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:nodes,ghost_layers=1,graph,multicast=true) +map(setup,partition(pmesh),gk.index_partition(pmesh),ranks) +# In this one, we do only the graph partition on the main +# but we load the mesh everywhere mesh = gk.mesh_from_gmsh(msh) -graph = gk.mesh_graph(mesh;graph_nodes=:cells) -node_to_color = map_main(ranks) do ranks +graph_nodes = :nodes +graph = gk.mesh_graph(mesh;graph_nodes) +graph_partition = map_main(ranks) do ranks Metis.partition(graph,np) -end -pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:cells,ghost_layers=0,graph,multicast=true) -pmesh = gk.partition_mesh(node_to_color,ranks,mesh;graph_nodes=:cells,ghost_layers=1,graph,multicast=true) +end |> multicast |> PartitionedArrays.getany +pmesh = gk.partition_mesh(mesh,np;ranks,graph,graph_nodes,graph_partition,ghost_layers=0) +pmesh = gk.partition_mesh(mesh,np;ranks,graph,graph_nodes,graph_partition,ghost_layers=1) + +graph_nodes = :cells +graph = gk.mesh_graph(mesh;graph_nodes) +graph_partition = map_main(ranks) do ranks + Metis.partition(graph,np) +end |> multicast |> PartitionedArrays.getany +pmesh = gk.partition_mesh(mesh,np;ranks,graph,graph_nodes,graph_partition,ghost_layers=0) +pmesh = gk.partition_mesh(mesh,np;ranks,graph,graph_nodes,graph_partition,ghost_layers=1) end # module From 83e85631c288228d47fe2c7e6e2e20f93be03e98 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Fri, 2 Feb 2024 15:34:41 +0100 Subject: [PATCH 23/34] parallel cartesian mesh (reference implementation) --- src/mesh_interface.jl | 38 ++++++++++++++++++------ test/mesh_interface_tests.jl | 57 ++++++++++++++++++++++++++---------- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl index c51e0d96..dd3cad83 100644 --- a/src/mesh_interface.jl +++ b/src/mesh_interface.jl @@ -2640,7 +2640,15 @@ end """ """ -function cartesian_mesh(domain,cells_per_dir;boundary=true,complexify=true,simplexify=false) +function cartesian_mesh( + domain,cells_per_dir,parts_per_dir=nothing; + boundary=true, + complexify=true, + simplexify=false, + parts = parts_per_dir === nothing ? nothing : LinearIndices((prod(parts_per_dir),)), + graph_nodes=:cells, + ghost_layers=1, + ) mesh = if boundary if simplexify structured_simplex_mesh_with_boundary(domain,cells_per_dir) @@ -2658,7 +2666,19 @@ function cartesian_mesh(domain,cells_per_dir;boundary=true,complexify=true,simpl if complexify mesh, = GalerkinToolkit.complexify(mesh) end - mesh + if parts_per_dir === nothing + return mesh + end + # TODO this can (and should!) be heavily optimized + @assert graph_nodes === :cells + np = prod(parts_per_dir) + graph = mesh_graph(mesh;graph_nodes) + parts_seq = LinearIndices((np,)) + graph_partition = zeros(Int,prod(cells_per_dir)) + for ids in uniform_partition(LinearIndices((np,)),parts_per_dir,cells_per_dir) + graph_partition[local_to_global(ids)] = local_to_owner(ids) + end + partition_mesh(mesh,np;parts,graph,graph_nodes,graph_partition,ghost_layers) end function bounding_box_from_domain(domain) @@ -3512,7 +3532,7 @@ node_indices(a::PMeshLocalIds) = a.node_indices face_indices(a::PMeshLocalIds,d) = a.face_indices[d+1] function partition_mesh(mesh,np; - ranks = LinearIndices((np,)), + parts = LinearIndices((np,)), graph_nodes = :cells, graph = mesh_graph(mesh;graph_nodes), graph_partition = Metis.partition(graph,np), @@ -3520,9 +3540,9 @@ function partition_mesh(mesh,np; renumber = true, ) if graph_nodes === :nodes - partition_mesh_nodes(graph_partition,ranks,mesh,graph,ghost_layers,renumber) + partition_mesh_nodes(graph_partition,parts,mesh,graph,ghost_layers,renumber) elseif graph_nodes === :cells - partition_mesh_cells(graph_partition,ranks,mesh,graph,ghost_layers,renumber) + partition_mesh_cells(graph_partition,parts,mesh,graph,ghost_layers,renumber) else error("Case not implemented") end @@ -3538,7 +3558,7 @@ function scatter_mesh(pmeshes_on_main;source=MAIN) PMesh(mesh_partition,node_partition,face_partition) end -function partition_mesh_nodes(node_to_color,ranks,mesh,graph,ghost_layers,renumber) +function partition_mesh_nodes(node_to_color,parts,mesh,graph,ghost_layers,renumber) face_nodes_mesh = face_nodes(mesh) nnodes = num_nodes(mesh) function setup(part) @@ -3598,7 +3618,7 @@ function partition_mesh_nodes(node_to_color,ranks,mesh,graph,ghost_layers,renumb lmesh = restrict_mesh(mesh,lnode_to_node,lface_to_face_mesh) lmesh, local_nodes, Tuple(local_faces) end - mesh_partition, node_partition, face_partition_array = map(setup,ranks) |> tuple_of_arrays + mesh_partition, node_partition, face_partition_array = map(setup,parts) |> tuple_of_arrays face_partition = face_partition_array |> tuple_of_arrays if renumber node_partition = renumber_partition(node_partition) @@ -3611,7 +3631,7 @@ function partition_mesh_nodes(node_to_color,ranks,mesh,graph,ghost_layers,renumb pmesh end -function partition_mesh_cells(cell_to_color,ranks,mesh,graph,ghost_layers,renumber) +function partition_mesh_cells(cell_to_color,parts,mesh,graph,ghost_layers,renumber) D = num_dims(mesh) ncells = num_faces(mesh,D) topo = topology(mesh) @@ -3698,7 +3718,7 @@ function partition_mesh_cells(cell_to_color,ranks,mesh,graph,ghost_layers,renumb lmesh = restrict_mesh(mesh,lnode_to_node_mesh,lface_to_face_mesh) lmesh, local_nodes, Tuple(local_faces) end - mesh_partition, node_partition, face_partition_array = map(setup,ranks) |> tuple_of_arrays + mesh_partition, node_partition, face_partition_array = map(setup,parts) |> tuple_of_arrays face_partition = face_partition_array |> tuple_of_arrays if renumber node_partition = renumber_partition(node_partition) diff --git a/test/mesh_interface_tests.jl b/test/mesh_interface_tests.jl index 1b577e69..bd41f9ed 100644 --- a/test/mesh_interface_tests.jl +++ b/test/mesh_interface_tests.jl @@ -138,17 +138,17 @@ gk.mesh_graph(mesh;graph_nodes=:cells,graph_edges=:nodes) gk.mesh_graph(mesh;graph_nodes=:cells,graph_edges=:faces) np = 2 -ranks = DebugArray(LinearIndices((np,))) +parts = DebugArray(LinearIndices((np,))) mesh = gk.cartesian_mesh((0,1,0,1),(2,2)) cell_to_color = [1,1,2,2] pmesh = gk.partition_mesh(mesh,np) -pmesh = gk.partition_mesh(mesh,np;ranks) -pmesh = gk.partition_mesh(mesh,np;ranks,graph_partition=cell_to_color,graph_nodes=:cells,ghost_layers=0) -pmesh = gk.partition_mesh(mesh,np;ranks,graph_partition=cell_to_color,graph_nodes=:cells,ghost_layers=1) +pmesh = gk.partition_mesh(mesh,np;parts) +pmesh = gk.partition_mesh(mesh,np;parts,graph_partition=cell_to_color,graph_nodes=:cells,ghost_layers=0) +pmesh = gk.partition_mesh(mesh,np;parts,graph_partition=cell_to_color,graph_nodes=:cells,ghost_layers=1) node_to_color = [1,1,1,1,2,2,2,2,2] -pmesh = gk.partition_mesh(mesh,np;ranks,graph_partition=node_to_color,graph_nodes=:nodes,ghost_layers=0) -pmesh = gk.partition_mesh(mesh,np;ranks,graph_partition=node_to_color,graph_nodes=:nodes,ghost_layers=1) +pmesh = gk.partition_mesh(mesh,np;parts,graph_partition=node_to_color,graph_nodes=:nodes,ghost_layers=0) +pmesh = gk.partition_mesh(mesh,np;parts,graph_partition=node_to_color,graph_nodes=:nodes,ghost_layers=1) function setup(mesh,ids,rank) face_to_owner = zeros(Int,sum(gk.num_faces(mesh))) @@ -164,12 +164,12 @@ function setup(mesh,ids,rank) vtk["owner"] = face_to_owner end end -map(setup,partition(pmesh),gk.index_partition(pmesh),ranks) +map(setup,partition(pmesh),gk.index_partition(pmesh),parts) ## In this case, we do the hard work only on the main proc np = 4 -ranks = DebugArray(LinearIndices((np,))) -pmesh = map_main(ranks) do ranks +parts = DebugArray(LinearIndices((np,))) +pmesh = map_main(parts) do parts mesh = gk.mesh_from_gmsh(msh) graph_nodes = :nodes graph = gk.mesh_graph(mesh;graph_nodes=:nodes) @@ -191,25 +191,50 @@ function setup(mesh,ids,rank) vtk["owner"] = face_to_owner end end -map(setup,partition(pmesh),gk.index_partition(pmesh),ranks) +map(setup,partition(pmesh),gk.index_partition(pmesh),parts) # In this one, we do only the graph partition on the main # but we load the mesh everywhere mesh = gk.mesh_from_gmsh(msh) graph_nodes = :nodes graph = gk.mesh_graph(mesh;graph_nodes) -graph_partition = map_main(ranks) do ranks +graph_partition = map_main(parts) do parts Metis.partition(graph,np) end |> multicast |> PartitionedArrays.getany -pmesh = gk.partition_mesh(mesh,np;ranks,graph,graph_nodes,graph_partition,ghost_layers=0) -pmesh = gk.partition_mesh(mesh,np;ranks,graph,graph_nodes,graph_partition,ghost_layers=1) +pmesh = gk.partition_mesh(mesh,np;parts,graph,graph_nodes,graph_partition,ghost_layers=0) +pmesh = gk.partition_mesh(mesh,np;parts,graph,graph_nodes,graph_partition,ghost_layers=1) graph_nodes = :cells graph = gk.mesh_graph(mesh;graph_nodes) -graph_partition = map_main(ranks) do ranks +graph_partition = map_main(parts) do parts Metis.partition(graph,np) end |> multicast |> PartitionedArrays.getany -pmesh = gk.partition_mesh(mesh,np;ranks,graph,graph_nodes,graph_partition,ghost_layers=0) -pmesh = gk.partition_mesh(mesh,np;ranks,graph,graph_nodes,graph_partition,ghost_layers=1) +pmesh = gk.partition_mesh(mesh,np;parts,graph,graph_nodes,graph_partition,ghost_layers=0) +pmesh = gk.partition_mesh(mesh,np;parts,graph,graph_nodes,graph_partition,ghost_layers=1) + +domain = (0,1,0,1) +cells_per_dir = (4,4) +parts_per_dir = (2,2) +pmesh = gk.cartesian_mesh(domain,cells_per_dir,parts_per_dir) +pmesh = gk.cartesian_mesh(domain,cells_per_dir,parts_per_dir;ghost_layers=0) +np = prod(parts_per_dir) +parts = DebugArray(LinearIndices((np,))) +pmesh = gk.cartesian_mesh(domain,cells_per_dir,parts_per_dir;parts,ghost_layers=0) + +function setup(mesh,ids,rank) + face_to_owner = zeros(Int,sum(gk.num_faces(mesh))) + D = gk.num_dims(mesh) + for d in 0:D + face_to_owner[gk.face_range(mesh,d)] = local_to_owner(gk.face_indices(ids,d)) + end + pvtk_grid(joinpath(outdir,"pmesh-cartesian"),gk.vtk_args(mesh)...;part=rank,nparts=np) do vtk + gk.vtk_physical_faces!(vtk,mesh) + gk.vtk_physical_nodes!(vtk,mesh) + vtk["piece"] = fill(rank,sum(gk.num_faces(mesh))) + vtk["owner"] = local_to_owner(gk.node_indices(ids)) + vtk["owner"] = face_to_owner + end +end +map(setup,partition(pmesh),gk.index_partition(pmesh),parts) end # module From d03612e76396d58be23ddf5bdd6b55a8ea71e3e7 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Fri, 2 Feb 2024 18:02:16 +0100 Subject: [PATCH 24/34] Added two_level_mesh --- src/mesh_interface.jl | 164 +++++++++++++++++++++++++++++++++++ test/mesh_interface_tests.jl | 18 ++++ 2 files changed, 182 insertions(+) diff --git a/src/mesh_interface.jl b/src/mesh_interface.jl index dd3cad83..76ff0693 100644 --- a/src/mesh_interface.jl +++ b/src/mesh_interface.jl @@ -3731,4 +3731,168 @@ function partition_mesh_cells(cell_to_color,parts,mesh,graph,ghost_layers,renumb pmesh end +function two_level_mesh(coarse_mesh,fine_mesh;boundary_names=nothing) + + D = num_dims(fine_mesh) + n_fine_cells = num_faces(fine_mesh,D) + n_fine_nodes = num_nodes(fine_mesh) + refcell = first(reference_faces(fine_mesh,D)) + n_coarse_cells = num_faces(coarse_mesh,D) + fine_cell_local_node_to_fine_node = face_nodes(fine_mesh,D) + coarse_cell_lnode_to_coarse_node = face_nodes(coarse_mesh,D) + coarse_node_to_x = node_coordinates(coarse_mesh) + topo = topology(coarse_mesh) + + # The user can provide custom names for physical groups on the boundary + # but we give some default value + if boundary_names === nothing + boundary_names = [ + [ "$d-face-$face" for face in 1:num_faces(boundary(refcell),d)] for d in 0:(D-1)] + end + name_priority = reduce(vcat,boundary_names) + fine_node_groups = physical_nodes(fine_mesh;merge_dims=true,disjoint=true,name_priority) + + # Recover boundary info + d_to_local_dface_to_fine_nodes = Vector{Vector{Vector{Int}}}(undef,D+1) + fine_node_mask = fill(true,n_fine_nodes) + for d in 0:(D-1) + n_local_dfaces = num_faces(boundary(refcell),d) + local_dface_to_fine_nodes = Vector{Vector{Int}}(undef,n_local_dfaces) + for local_dface in 1:n_local_dfaces + fine_nodes = fine_node_groups[boundary_names[d+1][local_dface]] + local_dface_to_fine_nodes[local_dface] = fine_nodes + fine_node_mask[fine_nodes] .= false + end + d_to_local_dface_to_fine_nodes[d+1] = local_dface_to_fine_nodes + end + d_to_local_dface_to_fine_nodes[D+1] = [findall(fine_node_mask)] + + # Coordinates + fine_node_to_x = node_coordinates(fine_mesh) + A = tabulator(refcell)(value,fine_node_to_x) + coarse_cell_fine_node_to_x = Vector{Vector{SVector{D,Float64}}}(undef,n_coarse_cells) + for coarse_cell in 1:n_coarse_cells + lnode_to_coarse_node = coarse_cell_lnode_to_coarse_node[coarse_cell] + lnode_to_x = coarse_node_to_x[lnode_to_coarse_node] + coarse_cell_fine_node_to_x[coarse_cell] = A*lnode_to_x + end + + # Glue fine node ids + # TODO ordering in the physical group + # start with a 2x2 unit cell + final_node = 0 + d_coarse_dface_to_offset = Vector{Vector{Int}}(undef,D+1) + for d in 0:D + local_dface_to_fine_nodes = d_to_local_dface_to_fine_nodes[d+1] + coarse_dface_to_coarse_cells = face_incidence(topo,d,D) + coarse_cell_to_coarse_dfaces = face_incidence(topo,D,d) + n_coarse_dfaces = num_faces(coarse_mesh,d) + coarse_dface_to_offset = zeros(Int,n_coarse_dfaces) + for coarse_dface in 1:n_coarse_dfaces + coarse_cells = coarse_dface_to_coarse_cells[coarse_dface] + for coarse_cell in coarse_cells + coarse_dfaces = coarse_cell_to_coarse_dfaces[coarse_cell] + local_dface = findfirst(i->coarse_dface==i,coarse_dfaces) + fine_nodes = local_dface_to_fine_nodes[local_dface] + if coarse_dface_to_offset[coarse_dface] == 0 + coarse_dface_to_offset[coarse_dface] = final_node + final_node += length(fine_nodes) + end + end + end + d_coarse_dface_to_offset[d+1] = coarse_dface_to_offset + end + n_final_nodes = final_node + coarse_cell_fine_node_to_final_node = Vector{Vector{Int}}(undef,n_coarse_cells) + for coarse_cell in 1:n_coarse_cells + coarse_cell_fine_node_to_final_node[coarse_cell] = zeros(Int,n_fine_nodes) + end + n_coarse_nodes = num_nodes(coarse_mesh) + for d in 0:D + local_dface_to_fine_nodes = d_to_local_dface_to_fine_nodes[d+1] + coarse_dface_to_coarse_cells = face_incidence(topo,d,D) + coarse_cell_to_coarse_dfaces = face_incidence(topo,D,d) + n_coarse_dfaces = num_faces(coarse_mesh,d) + for coarse_dface in 1:n_coarse_dfaces + offset = d_coarse_dface_to_offset[d+1][coarse_dface] + coarse_cells = coarse_dface_to_coarse_cells[coarse_dface] + for coarse_cell in coarse_cells + coarse_dfaces = coarse_cell_to_coarse_dfaces[coarse_cell] + local_dface = findfirst(i->coarse_dface==i,coarse_dfaces) + fine_nodes = local_dface_to_fine_nodes[local_dface] + final_nodes = offset .+ (1:length(fine_nodes)) # TODO, we need a permutation here defined by the boundary conditions + coarse_cell_fine_node_to_final_node[coarse_cell][fine_nodes] = final_nodes + end + end + end + + final_node_to_x = zeros(SVector{D,Float64},n_final_nodes) + for coarse_cell in 1:n_coarse_cells + fine_node_to_final_node = coarse_cell_fine_node_to_final_node[coarse_cell] + fine_node_to_x = coarse_cell_fine_node_to_x[coarse_cell] + final_node_to_x[fine_node_to_final_node] = fine_node_to_x + end + + n_final_cells = n_coarse_cells*n_fine_cells + final_cell_local_node_to_final_node = Vector{Vector{Int}}(undef,n_final_cells) + for final_cell in 1:n_final_cells + final_cell_local_node_to_final_node[final_cell] = zeros(Int,n_final_nodes) + end + final_cell = 0 + for coarse_cell in 1:n_coarse_cells + for fine_cell in 1:n_fine_cells + local_node_to_fine_node = fine_cell_local_node_to_fine_node[fine_cell] + local_node_to_final_node = coarse_cell_fine_node_to_final_node[coarse_cell][local_node_to_fine_node] + final_cell += 1 + final_cell_local_node_to_final_node[final_cell] = local_node_to_final_node + end + end + final_cell_to_refid = fill(1,n_final_cells) + refid_to_refcell = [refcell] + + chain = fe_chain( + final_node_to_x, + JaggedArray(final_cell_local_node_to_final_node), + final_cell_to_refid, + refid_to_refcell) + + # TODO we could avoid this call to complexify by using + # the fine faces (just as we did with the fine nodes) + # TODO maybe we don't need to simplexify and only find the faces + # needed for the physical groups + final_mesh, = mesh_from_chain(chain) |> complexify + + # Refine physical groups + final_node_to_mask = fill(false,n_final_nodes) + for d in 0:D + final_physical_dfaces = physical_faces(final_mesh,d) + coarse_phsycial_dfaces = physical_faces(coarse_mesh,d) + final_dfaces_to_final_nodes = face_nodes(final_mesh,d) + for (name,coarse_dfaces_in_group) in coarse_phsycial_dfaces + fill!(final_node_to_mask,false) + for n in 0:d + coarse_dface_to_coarse_nfaces = face_incidence(topo,d,n) + coarse_cell_to_coarse_nfaces = face_incidence(topo,D,n) + coarse_nface_to_coarse_cells = face_incidence(topo,n,D) + local_nface_to_fine_nodes = d_to_local_dface_to_fine_nodes[n+1] + for coarse_dface in coarse_dfaces_in_group + for coarse_nface in coarse_dface_to_coarse_nfaces[coarse_dface] + coarse_cells = coarse_nface_to_coarse_cells[coarse_nface] + coarse_cell = first(coarse_cells) + coarse_nfaces = coarse_cell_to_coarse_nfaces[coarse_cell] + local_nface = findfirst(i->coarse_nface==i,coarse_nfaces) + fine_nodes = local_nface_to_fine_nodes[local_nface] + final_nodes = coarse_cell_fine_node_to_final_node[coarse_cell][fine_nodes] + final_node_to_mask[final_nodes] .=true + end + end + final_faces_in_group = findall(final_nodes->all(final_node->final_node_to_mask[final_node],final_nodes),final_dfaces_to_final_nodes) + final_physical_dfaces[name] = final_faces_in_group + end + end + end + + glue = (;coarse_cell_fine_node_to_final_node,d_to_local_dface_to_fine_nodes) + final_mesh, glue +end diff --git a/test/mesh_interface_tests.jl b/test/mesh_interface_tests.jl index bd41f9ed..bc6f7b8b 100644 --- a/test/mesh_interface_tests.jl +++ b/test/mesh_interface_tests.jl @@ -237,4 +237,22 @@ function setup(mesh,ids,rank) end map(setup,partition(pmesh),gk.index_partition(pmesh),parts) + +domain = (0,1,0,1) +cells = (10,10) +fine_mesh = gk.cartesian_mesh(domain,cells) + +domain = (0,30,0,10) +cells = (2,2) +coarse_mesh = gk.cartesian_mesh(domain,cells) + +final_mesh, glue = gk.two_level_mesh(coarse_mesh,fine_mesh) + +vtk_grid(joinpath(outdir,"two-level-mesh"),gk.vtk_args(final_mesh)...) do vtk + gk.vtk_physical_faces!(vtk,final_mesh) + gk.vtk_physical_nodes!(vtk,final_mesh) + vtk["node_ids"] = 1:gk.num_nodes(final_mesh) +end + + end # module From 3a1b76dd0ec18230c9b48e28f131cfb6c14cbd67 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Sat, 3 Feb 2024 11:22:28 +0100 Subject: [PATCH 25/34] Moved file --- .../GalerkinToolkitExamples/src}/iso_param_poisson_tests.jl | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {test => extensions/GalerkinToolkitExamples/src}/iso_param_poisson_tests.jl (100%) diff --git a/test/iso_param_poisson_tests.jl b/extensions/GalerkinToolkitExamples/src/iso_param_poisson_tests.jl similarity index 100% rename from test/iso_param_poisson_tests.jl rename to extensions/GalerkinToolkitExamples/src/iso_param_poisson_tests.jl From a41c3791a8b398fb2c1d47fba10b1ad788cbb6de Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Sat, 3 Feb 2024 11:24:59 +0100 Subject: [PATCH 26/34] Moved file --- .../{iso_param_poisson_tests.jl => poisson_cg_isoparametric.jl} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename extensions/GalerkinToolkitExamples/src/{iso_param_poisson_tests.jl => poisson_cg_isoparametric.jl} (100%) diff --git a/extensions/GalerkinToolkitExamples/src/iso_param_poisson_tests.jl b/extensions/GalerkinToolkitExamples/src/poisson_cg_isoparametric.jl similarity index 100% rename from extensions/GalerkinToolkitExamples/src/iso_param_poisson_tests.jl rename to extensions/GalerkinToolkitExamples/src/poisson_cg_isoparametric.jl From abc9a7c0b7aa294302d04044e79de806364d366c Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Sat, 3 Feb 2024 11:48:06 +0100 Subject: [PATCH 27/34] Added examples extension package --- .../GalerkinToolkitExamples/Project.toml | 19 ++++++++ .../src/GalerkinToolkitExamples.jl | 5 ++ ...sson_cg_isoparametric.jl => example001.jl} | 46 +------------------ .../test/example001_tests.jl | 46 +++++++++++++++++++ .../GalerkinToolkitExamples/test/runtests.jl | 7 +++ 5 files changed, 79 insertions(+), 44 deletions(-) create mode 100644 extensions/GalerkinToolkitExamples/Project.toml create mode 100644 extensions/GalerkinToolkitExamples/src/GalerkinToolkitExamples.jl rename extensions/GalerkinToolkitExamples/src/{poisson_cg_isoparametric.jl => example001.jl} (92%) create mode 100644 extensions/GalerkinToolkitExamples/test/example001_tests.jl create mode 100644 extensions/GalerkinToolkitExamples/test/runtests.jl diff --git a/extensions/GalerkinToolkitExamples/Project.toml b/extensions/GalerkinToolkitExamples/Project.toml new file mode 100644 index 00000000..30466f76 --- /dev/null +++ b/extensions/GalerkinToolkitExamples/Project.toml @@ -0,0 +1,19 @@ +name = "GalerkinToolkitExamples" +uuid = "c6296ea7-31ea-46a1-ad69-c5bcf9a0231f" +authors = ["Francesc Verdugo "] +version = "0.1.0" + +[deps] +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +GalerkinToolkit = "5e3ba9c4-dd81-444d-b69a-0e7bd7bf60a4" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" +prrte_jll = "eb928a42-fffd-568d-ab9c-3f5d54fc65b9" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/extensions/GalerkinToolkitExamples/src/GalerkinToolkitExamples.jl b/extensions/GalerkinToolkitExamples/src/GalerkinToolkitExamples.jl new file mode 100644 index 00000000..3119650d --- /dev/null +++ b/extensions/GalerkinToolkitExamples/src/GalerkinToolkitExamples.jl @@ -0,0 +1,5 @@ +module GalerkinToolkitExamples + +include("example001.jl") + +end # module diff --git a/extensions/GalerkinToolkitExamples/src/poisson_cg_isoparametric.jl b/extensions/GalerkinToolkitExamples/src/example001.jl similarity index 92% rename from extensions/GalerkinToolkitExamples/src/poisson_cg_isoparametric.jl rename to extensions/GalerkinToolkitExamples/src/example001.jl index 4f1dbae6..e439cf60 100644 --- a/extensions/GalerkinToolkitExamples/src/poisson_cg_isoparametric.jl +++ b/extensions/GalerkinToolkitExamples/src/example001.jl @@ -1,4 +1,4 @@ -module IsoParamPoissonTest +module Example001 import GalerkinToolkit as gk import ForwardDiff @@ -7,7 +7,7 @@ using LinearAlgebra using SparseArrays using WriteVTK -# This one implements a vanilla iso-parametric Poisson solver by only +# This one implements a vanilla sequential iso-parametric Poisson solver by only # using the mesh interface. function main(params_in) @@ -467,46 +467,4 @@ function export_results(uh,params,state) end end -using Test - -tol = 1.0e-10 -params = Dict{Symbol,Any}() -params[:mesh] = gk.cartesian_mesh((0,3,0,2),(10,5),complexify=false) -params[:dirichlet_tags] = ["1-face-1","1-face-3","1-face-4"] -params[:neumann_tags] = ["1-face-2"] -params[:u] = (x) -> sum(x) -params[:f] = (x) -> 0.0 -params[:g] = (x) -> 1.0 -results = main(params) -@test results[:eh1] < tol -@test results[:el2] < tol -@test results[:ncells] == 10*5 - -params = Dict{Symbol,Any}() -params[:mesh] = gk.cartesian_mesh((0,3,0,2,0,1),(5,5,5)) -results = main(params) -@test results[:eh1] < tol -@test results[:el2] < tol -@test results[:ncells] == 5*5*5 - -params = Dict{Symbol,Any}() -params[:mesh] = gk.cartesian_mesh((0,3,0,2),(5,5),simplexify=true) -results = main(params) -@test results[:eh1] < tol -@test results[:el2] < tol - -params = Dict{Symbol,Any}() -params[:mesh] = gk.cartesian_mesh((0,3,0,2,0,1),(5,5,5),simplexify=true) -results = main(params) -@test results[:eh1] < tol -@test results[:el2] < tol - -params = Dict{Symbol,Any}() -params[:hi] = 1 -results = main(params) -@test results[:eh1] < tol -@test results[:el2] < tol - end # module - - diff --git a/extensions/GalerkinToolkitExamples/test/example001_tests.jl b/extensions/GalerkinToolkitExamples/test/example001_tests.jl new file mode 100644 index 00000000..bb476b38 --- /dev/null +++ b/extensions/GalerkinToolkitExamples/test/example001_tests.jl @@ -0,0 +1,46 @@ +module Example001Tests + +import GalerkinToolkit as gk +using GalerkinToolkitExamples: Example001 +using Test + +tol = 1.0e-10 +params = Dict{Symbol,Any}() +params[:mesh] = gk.cartesian_mesh((0,3,0,2),(10,5),complexify=false) +params[:dirichlet_tags] = ["1-face-1","1-face-3","1-face-4"] +params[:neumann_tags] = ["1-face-2"] +params[:u] = (x) -> sum(x) +params[:f] = (x) -> 0.0 +params[:g] = (x) -> 1.0 +results = Example001.main(params) +@test results[:eh1] < tol +@test results[:el2] < tol +@test results[:ncells] == 10*5 + +params = Dict{Symbol,Any}() +params[:mesh] = gk.cartesian_mesh((0,3,0,2,0,1),(5,5,5)) +results = Example001.main(params) +@test results[:eh1] < tol +@test results[:el2] < tol +@test results[:ncells] == 5*5*5 + +params = Dict{Symbol,Any}() +params[:mesh] = gk.cartesian_mesh((0,3,0,2),(5,5),simplexify=true) +results = Example001.main(params) +@test results[:eh1] < tol +@test results[:el2] < tol + +params = Dict{Symbol,Any}() +params[:mesh] = gk.cartesian_mesh((0,3,0,2,0,1),(5,5,5),simplexify=true) +results = Example001.main(params) +@test results[:eh1] < tol +@test results[:el2] < tol + +params = Dict{Symbol,Any}() +params[:hi] = 1 +results = Example001.main(params) +@test results[:eh1] < tol +@test results[:el2] < tol + + +end # module diff --git a/extensions/GalerkinToolkitExamples/test/runtests.jl b/extensions/GalerkinToolkitExamples/test/runtests.jl new file mode 100644 index 00000000..95a1c303 --- /dev/null +++ b/extensions/GalerkinToolkitExamples/test/runtests.jl @@ -0,0 +1,7 @@ +module GalerkinToolkitExamplesTests + +using Test + +@testset "example001" begin include("example001_tests.jl") end + +end # module From 36f860a2afb2f6114834f5aa4f06611464ddfdb6 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Sun, 4 Feb 2024 09:57:14 +0100 Subject: [PATCH 28/34] Moving examples to a separate CI action --- .github/workflows/CI.yml | 25 +++++++++++++++++++++++++ test/runtests.jl | 2 -- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3d543fec..c3f1f3d2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -36,6 +36,31 @@ jobs: - uses: codecov/codecov-action@v2 with: files: lcov.info + examples: + name: Examples with Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.9' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@v1 + - run: | + julia --project=extensions/GalerkinToolkitExamples -e ' + using Pkg + Pkg.dev(".") + Pkg.test("GalerkinToolkitExamples")' docs: name: Documentation runs-on: ubuntu-latest diff --git a/test/runtests.jl b/test/runtests.jl index 46028b3e..a2ed310a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,6 +4,4 @@ using Test @testset "Mesh interface" begin include("mesh_interface_tests.jl") end -@testset "Iso-parametric Poisson" begin include("iso_param_poisson_tests.jl") end - end # module From 5b07283d322574ecab8e8feda01866f9d2ad1390 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Sun, 4 Feb 2024 10:17:50 +0100 Subject: [PATCH 29/34] Refactor file structure of the project --- docs/make.jl | 4 +- docs/src/reference/geometry.md | 6 + .../{mesh_interface.md => integration.md} | 6 +- docs/src/reference/interpolation.md | 7 + src/GalerkinToolkit.jl | 4 +- src/{mesh_interface.jl => geometry.jl} | 264 +----------------- src/integration.jl | 144 ++++++++++ src/interpolation.jl | 111 ++++++++ ...h_interface_tests.jl => geometry_tests.jl} | 0 test/integration_tests.jl | 32 +++ test/interpolation_tests.jl | 0 test/runtests.jl | 6 +- 12 files changed, 320 insertions(+), 264 deletions(-) create mode 100644 docs/src/reference/geometry.md rename docs/src/reference/{mesh_interface.md => integration.md} (50%) create mode 100644 docs/src/reference/interpolation.md rename src/{mesh_interface.jl => geometry.jl} (94%) create mode 100644 src/integration.jl create mode 100644 src/interpolation.jl rename test/{mesh_interface_tests.jl => geometry_tests.jl} (100%) create mode 100644 test/integration_tests.jl create mode 100644 test/interpolation_tests.jl diff --git a/docs/make.jl b/docs/make.jl index 5403e16f..144b9bbd 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -15,7 +15,9 @@ makedocs(; pages=[ "Home" => "index.md", "Reference" =>[ - "Mesh interface" => "reference/mesh_interface.md" + "Geometry" => "reference/geometry.md", + "Integration" => "reference/integration.md", + "Interpolation" => "reference/interpolation.md", ] ], ) diff --git a/docs/src/reference/geometry.md b/docs/src/reference/geometry.md new file mode 100644 index 00000000..454fbd4c --- /dev/null +++ b/docs/src/reference/geometry.md @@ -0,0 +1,6 @@ +# Geometry + +```@autodocs +Modules = [GalerkinToolkit] +Pages = ["geometry.jl"] +``` diff --git a/docs/src/reference/mesh_interface.md b/docs/src/reference/integration.md similarity index 50% rename from docs/src/reference/mesh_interface.md rename to docs/src/reference/integration.md index 3fc45d28..89ef657d 100644 --- a/docs/src/reference/mesh_interface.md +++ b/docs/src/reference/integration.md @@ -1,7 +1,7 @@ -# Mesh interface - +# Integration ```@autodocs Modules = [GalerkinToolkit] -Pages = ["mesh_interface.jl"] +Pages = ["integration.jl"] ``` + diff --git a/docs/src/reference/interpolation.md b/docs/src/reference/interpolation.md new file mode 100644 index 00000000..a329351c --- /dev/null +++ b/docs/src/reference/interpolation.md @@ -0,0 +1,7 @@ +# Interpolation + +```@autodocs +Modules = [GalerkinToolkit] +Pages = ["interpolation.jl"] +``` + diff --git a/src/GalerkinToolkit.jl b/src/GalerkinToolkit.jl index 74df5682..fa5c824d 100644 --- a/src/GalerkinToolkit.jl +++ b/src/GalerkinToolkit.jl @@ -11,6 +11,8 @@ using Combinatorics using SparseArrays using Metis -include("mesh_interface.jl") +include("geometry.jl") +include("integration.jl") +include("interpolation.jl") end # module diff --git a/src/mesh_interface.jl b/src/geometry.jl similarity index 94% rename from src/mesh_interface.jl rename to src/geometry.jl index 76ff0693..64c0de47 100644 --- a/src/mesh_interface.jl +++ b/src/geometry.jl @@ -119,6 +119,12 @@ int_type(a) = a.int_type """ outwards_normals(a) = a.outwards_normals +function repeat_per_dir(geo,a) + D = num_dims(geo) + ntuple(i->a,Val(D)) +end +repeat_per_dir(geo,a::NTuple) = a + reference_faces(a,d) = reference_faces(a)[val_parameter(d)+1] face_nodes(a,d) = face_nodes(a)[val_parameter(d)+1] face_incidence(a,d1,d2) = face_incidence(a)[val_parameter(d1)+1,val_parameter(d2)+1] @@ -246,155 +252,6 @@ function is_unitary(geom) all(i->i==0,first(my_bounding_box)) && all(i->i==1,last(my_bounding_box)) end -""" - abstract type AbstractQuadrature - -# Basic queries - -- [`coordinates`](@ref) -- [`weights`](@ref) - -# Basic constructors - -- [`default_quadrature`](@ref) -- [`duffy_quadrature`](@ref) -- [`tensor_product_quadrature`](@ref) - -# Supertype hierarchy - - AbstractQuadrature <: GalerkinToolkitDataType -""" -abstract type AbstractQuadrature <: GalerkinToolkitDataType end - -struct GenericCuadrature{A,B} <: AbstractQuadrature - coordinates::A - weights::B -end -struct Cuadrature{D,T} <: GalerkinToolkitDataType - coordinates::Vector{SVector{D,T}} - weights::Vector{T} -end -function quadrature(coordinates,weights) - GenericCuadrature(coordinates,weights) -end -function quadrature(coordinates::Vector{SVector{D,T}},weights::Vector{T}) where {D,T} - Cuadrature(coordinates,weights) -end - -""" -""" -function duffy_quadrature(geo,degree) - @assert is_unit_simplex(geo) - D = num_dims(geo) - if D == 0 - x = zeros(SVector{0,real_type},1) - w = ones(real_type,1) - return quadrature(x,w) - end - function map_to(a,b,(points,weights)) - points_ab = similar(points) - weights_ab = similar(weights) - points_ab .= 0.5*(b-a)*points .+ 0.5*(a+b) - weights_ab .= 0.5*(b-a)*weights - (points_ab, weights_ab) - end - function duffy_map(q) - D = length(q) - a = 1.0 - m = ntuple(Val(D)) do i - if i == 1 - q[i] - else - a *= (1-q[i-1]) - a*q[i] - end - end - typeof(q)(m) - end - n = ceil(Int, (degree + 1.0) / 2.0 ) - beta = 0 - dim_to_quad_1d = map(1:(D-1)) do d - alpha = (D-1)-(d-1) - map_to(0,1,gaussjacobi(n,alpha,beta)) - end - quad_1d = map_to(0,1,gausslegendre(n)) - push!(dim_to_quad_1d,quad_1d) - coords_per_dir = map(first,dim_to_quad_1d) - weights_per_dir = map(last,dim_to_quad_1d) - a = 0.5 - for d in (D-1):-1:1 - ws_1d = weights_per_dir[d] - ws_1d[:] *= a - a *= 0.5 - end - m = prod(map(length,weights_per_dir)) - Tv = real_type(geo) - w = zeros(Tv,m) - x = zeros(SVector{D,Tv},m) - tensor_product!(identity,x,coords_per_dir) - tensor_product!(prod,w,weights_per_dir) - x .= duffy_map.(x) - quadrature(x,w) -end - -""" -""" -function tensor_product_quadrature(geo,degree_per_dir) - @assert is_n_cube(geo) - @assert is_axis_aligned(geo) - my_bounding_box = bounding_box(geo) - D = num_dims(geo) - limits_per_dir = ntuple(i->(my_bounding_box[1][i],my_bounding_box[2][i]),Val(D)) - n_per_dir = map(d->ceil(Int,(d+1)/2),degree_per_dir) - function quadrature_1d(n,limits) - x,w = FastGaussQuadrature.gausslegendre(n) - a,b = limits - x .= (0.5*(b-a)) .*x .+ (0.5*(b+a)) - w .*= 0.5*(b-a) - quadrature(x,w) - end - quad_per_dir = map(quadrature_1d,n_per_dir,limits_per_dir) - coords_per_dir = map(coordinates,quad_per_dir) - weights_per_dir = map(weights,quad_per_dir) - m = prod(map(length,weights_per_dir)) - Tv = real_type(geo) - w = zeros(Tv,m) - x = zeros(SVector{D,Tv},m) - tensor_product!(identity,x,coords_per_dir) - tensor_product!(prod,w,weights_per_dir) - quadrature(x,w) -end - -function tensor_product!(f,result,values_per_dir) - shape = Tuple(map(length,values_per_dir)) - cis = CartesianIndices(shape) - lis = LinearIndices(cis) - for ci in cis - li = lis[ci] - result[li] = f(map((q,i)->q[i],values_per_dir,Tuple(ci))) - end - result -end - -function repeat_per_dir(geo,a) - D = num_dims(geo) - ntuple(i->a,Val(D)) -end -repeat_per_dir(geo,a::NTuple) = a - -""" -""" -function default_quadrature(geo,degree) - if is_n_cube(geo) && is_axis_aligned(geo) - D = num_dims(geo) - degree_per_dir = repeat_per_dir(geo,degree) - tensor_product_quadrature(geo,degree_per_dir) - elseif is_unit_simplex(geo) - duffy_quadrature(geo,degree) - else - error("Not implemented") - end -end """ abstract type AbstractMeshFace @@ -570,115 +427,6 @@ function tabulator(fe) end end -#abstract type AbstractLagrangeFE <: AbstractMeshFace end -# -#struct GenericLagrangeFE{A,B,C,D} <: AbstractLagrangeFE -# geometry::A -# order_per_dir::B -# space::Symbol -# lib_to_user_nodes::C -# major::Symbol -# shape::D -#end -# -#struct ScalarShape end -#const SCALAR_SHAPE = ScalarShape() -# -#lagrangian_fe(args...) = GenericLagrangeFE(args...) -# -#function lagrangian_fe(geometry,order; -# space = default_space(geometry), -# lib_to_user_nodes = int_type(geometry)[], -# major = :component, -# shape = SCALAR_SHAPE) -# D = num_dims(geometry) -# order_per_dir = repeat_per_dir(geometry,order) -# lagrangian_fe( -# geometry, -# order_per_dir, -# space, -# lib_to_user_nodes, -# major, -# shape) -#end -# -# -#order(fe::AbstractLagrangeFE) = maximum(order_per_dir(fe),init=0) -# -#function lib_to_user_nodes(fe::AbstractLagrangeFE) -# if length(fe.lib_to_user_nodes) == 0 -# nnodes = num_nodes(fe) -# Ti = int_type(geometry(fe)) -# collect(Ti.(1:nnodes)) -# else -# fe.lib_to_user_nodes -# end -#end -# -#function monomial_exponents(fe::AbstractLagrangeFE) -# monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> int_type) -#end -# -#function node_coordinates(fe::AbstractLagrangeFE) -# @assert fe |> geometry |> is_unitary -# mexps = monomial_exponents(fe) -# node_coordinates_from_monomials_exponents(mexps,fe.order_per_dir,fe.geometry |> real_type) -#end -# -#function tensor_basis(fe::AbstractLagrangeFE) -# Tv = fe |> geometry |> real_type -# if fe.shape == SCALAR_SHAPE -# return Tv(1) -# else -# cis = CartesianIndices(val_parameter(fe.shape)) -# l = prod(val_parameter(fe.shape)) -# init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Tv} -# cis_flat = cis[:] -# return map(cis_flat) do ci -# init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) -# end -# end -#end -# -#function primal_basis(fe::AbstractLagrangeFE) -# scalar_basis = map(e->(x-> prod(x.^e)),fe|>monomial_exponents) -# if fe.shape == SCALAR_SHAPE -# return scalar_basis -# else -# primal_nested = map(scalar_basis) do monomial -# map(tensor_basis(fe)) do e -# x -> monomial(x)*e -# end -# end -# return reduce(vcat,primal_nested) -# end -#end -# -#function dual_basis(fe::AbstractLagrangeFE) -# node_coordinates_reffe = fe|>node_coordinates -# scalar_basis = map(x->(f->f(x)),node_coordinates_reffe) -# if fe.shape == SCALAR_SHAPE -# return scalar_basis -# else -# if fe.major === :component -# dual_nested = map(node_coordinates_reffe) do x -# map(tensor_basis(fe)) do e -# f->inner(e,f(x)) -# end -# end -# elseif fe.major === :node -# dual_nested = map(tensor_basis(fe)) do e -# map(node_coordinates_reffe) do x -# f->inner(e,f(x)) -# end -# end -# else -# error("Not Implemented") -# end -# return reduce(vcat,dual_nested) -# end -#end - """ abstract type AbstractFEMesh diff --git a/src/integration.jl b/src/integration.jl new file mode 100644 index 00000000..f627efbf --- /dev/null +++ b/src/integration.jl @@ -0,0 +1,144 @@ +""" + abstract type AbstractQuadrature + +# Basic queries + +- [`coordinates`](@ref) +- [`weights`](@ref) + +# Basic constructors + +- [`default_quadrature`](@ref) +- [`duffy_quadrature`](@ref) +- [`tensor_product_quadrature`](@ref) + +# Supertype hierarchy + + AbstractQuadrature <: GalerkinToolkitDataType +""" +abstract type AbstractQuadrature <: GalerkinToolkitDataType end + +struct GenericCuadrature{A,B} <: AbstractQuadrature + coordinates::A + weights::B +end +struct Cuadrature{D,T} <: GalerkinToolkitDataType + coordinates::Vector{SVector{D,T}} + weights::Vector{T} +end +function quadrature(coordinates,weights) + GenericCuadrature(coordinates,weights) +end +function quadrature(coordinates::Vector{SVector{D,T}},weights::Vector{T}) where {D,T} + Cuadrature(coordinates,weights) +end + +""" +""" +function duffy_quadrature(geo,degree) + @assert is_unit_simplex(geo) + D = num_dims(geo) + if D == 0 + x = zeros(SVector{0,real_type},1) + w = ones(real_type,1) + return quadrature(x,w) + end + function map_to(a,b,(points,weights)) + points_ab = similar(points) + weights_ab = similar(weights) + points_ab .= 0.5*(b-a)*points .+ 0.5*(a+b) + weights_ab .= 0.5*(b-a)*weights + (points_ab, weights_ab) + end + function duffy_map(q) + D = length(q) + a = 1.0 + m = ntuple(Val(D)) do i + if i == 1 + q[i] + else + a *= (1-q[i-1]) + a*q[i] + end + end + typeof(q)(m) + end + n = ceil(Int, (degree + 1.0) / 2.0 ) + beta = 0 + dim_to_quad_1d = map(1:(D-1)) do d + alpha = (D-1)-(d-1) + map_to(0,1,gaussjacobi(n,alpha,beta)) + end + quad_1d = map_to(0,1,gausslegendre(n)) + push!(dim_to_quad_1d,quad_1d) + coords_per_dir = map(first,dim_to_quad_1d) + weights_per_dir = map(last,dim_to_quad_1d) + a = 0.5 + for d in (D-1):-1:1 + ws_1d = weights_per_dir[d] + ws_1d[:] *= a + a *= 0.5 + end + m = prod(map(length,weights_per_dir)) + Tv = real_type(geo) + w = zeros(Tv,m) + x = zeros(SVector{D,Tv},m) + tensor_product!(identity,x,coords_per_dir) + tensor_product!(prod,w,weights_per_dir) + x .= duffy_map.(x) + quadrature(x,w) +end + +""" +""" +function tensor_product_quadrature(geo,degree_per_dir) + @assert is_n_cube(geo) + @assert is_axis_aligned(geo) + my_bounding_box = bounding_box(geo) + D = num_dims(geo) + limits_per_dir = ntuple(i->(my_bounding_box[1][i],my_bounding_box[2][i]),Val(D)) + n_per_dir = map(d->ceil(Int,(d+1)/2),degree_per_dir) + function quadrature_1d(n,limits) + x,w = FastGaussQuadrature.gausslegendre(n) + a,b = limits + x .= (0.5*(b-a)) .*x .+ (0.5*(b+a)) + w .*= 0.5*(b-a) + quadrature(x,w) + end + quad_per_dir = map(quadrature_1d,n_per_dir,limits_per_dir) + coords_per_dir = map(coordinates,quad_per_dir) + weights_per_dir = map(weights,quad_per_dir) + m = prod(map(length,weights_per_dir)) + Tv = real_type(geo) + w = zeros(Tv,m) + x = zeros(SVector{D,Tv},m) + tensor_product!(identity,x,coords_per_dir) + tensor_product!(prod,w,weights_per_dir) + quadrature(x,w) +end + +function tensor_product!(f,result,values_per_dir) + shape = Tuple(map(length,values_per_dir)) + cis = CartesianIndices(shape) + lis = LinearIndices(cis) + for ci in cis + li = lis[ci] + result[li] = f(map((q,i)->q[i],values_per_dir,Tuple(ci))) + end + result +end + +""" +""" +function default_quadrature(geo,degree) + if is_n_cube(geo) && is_axis_aligned(geo) + D = num_dims(geo) + degree_per_dir = repeat_per_dir(geo,degree) + tensor_product_quadrature(geo,degree_per_dir) + elseif is_unit_simplex(geo) + duffy_quadrature(geo,degree) + else + error("Not implemented") + end +end + diff --git a/src/interpolation.jl b/src/interpolation.jl new file mode 100644 index 00000000..ca9ec696 --- /dev/null +++ b/src/interpolation.jl @@ -0,0 +1,111 @@ + +abstract type AbstractLagrangeFE <: AbstractMeshFace end + +struct GenericLagrangeFE{A,B,C,D} <: AbstractLagrangeFE + geometry::A + order_per_dir::B + space::Symbol + lib_to_user_nodes::C + major::Symbol + shape::D +end + +struct ScalarShape end +const SCALAR_SHAPE = ScalarShape() + +""" +""" +lagrangian_fe(args...) = GenericLagrangeFE(args...) + +function lagrangian_fe(geometry,order; + space = default_space(geometry), + lib_to_user_nodes = int_type(geometry)[], + major = :component, + shape = SCALAR_SHAPE) + D = num_dims(geometry) + order_per_dir = repeat_per_dir(geometry,order) + lagrangian_fe( + geometry, + order_per_dir, + space, + lib_to_user_nodes, + major, + shape) +end + +order(fe::AbstractLagrangeFE) = maximum(order_per_dir(fe),init=0) + +function lib_to_user_nodes(fe::AbstractLagrangeFE) + if length(fe.lib_to_user_nodes) == 0 + nnodes = num_nodes(fe) + Ti = int_type(geometry(fe)) + collect(Ti.(1:nnodes)) + else + fe.lib_to_user_nodes + end +end + +function monomial_exponents(fe::AbstractLagrangeFE) + monomial_exponents_from_space(fe.space,fe.order_per_dir,fe.geometry |> int_type) +end + +function node_coordinates(fe::AbstractLagrangeFE) + @assert fe |> geometry |> is_unitary + mexps = monomial_exponents(fe) + node_coordinates_from_monomials_exponents(mexps,fe.order_per_dir,fe.geometry |> real_type) +end + +function tensor_basis(fe::AbstractLagrangeFE) + Tv = fe |> geometry |> real_type + if fe.shape == SCALAR_SHAPE + return Tv(1) + else + cis = CartesianIndices(val_parameter(fe.shape)) + l = prod(val_parameter(fe.shape)) + init_tensor = SArray{Tuple{val_parameter(fe.shape)...},Tv} + cis_flat = cis[:] + return map(cis_flat) do ci + init_tensor(ntuple(j->cis[j]==ci ? 1 : 0 ,Val(l))) + end + end +end + +function primal_basis(fe::AbstractLagrangeFE) + scalar_basis = map(e->(x-> prod(x.^e)),fe|>monomial_exponents) + if fe.shape == SCALAR_SHAPE + return scalar_basis + else + primal_nested = map(scalar_basis) do monomial + map(tensor_basis(fe)) do e + x -> monomial(x)*e + end + end + return reduce(vcat,primal_nested) + end +end + +function dual_basis(fe::AbstractLagrangeFE) + node_coordinates_reffe = fe|>node_coordinates + scalar_basis = map(x->(f->f(x)),node_coordinates_reffe) + if fe.shape == SCALAR_SHAPE + return scalar_basis + else + if fe.major === :component + dual_nested = map(node_coordinates_reffe) do x + map(tensor_basis(fe)) do e + f->inner(e,f(x)) + end + end + elseif fe.major === :node + dual_nested = map(tensor_basis(fe)) do e + map(node_coordinates_reffe) do x + f->inner(e,f(x)) + end + end + else + error("Not Implemented") + end + return reduce(vcat,dual_nested) + end +end + diff --git a/test/mesh_interface_tests.jl b/test/geometry_tests.jl similarity index 100% rename from test/mesh_interface_tests.jl rename to test/geometry_tests.jl diff --git a/test/integration_tests.jl b/test/integration_tests.jl new file mode 100644 index 00000000..99a25458 --- /dev/null +++ b/test/integration_tests.jl @@ -0,0 +1,32 @@ +module IntegrationTests + +using Test +import GalerkinToolkit as gk + +spx0 = gk.unit_simplex(0) +spx1 = gk.unit_simplex(1) +spx2 = gk.unit_simplex(2) +spx3 = gk.unit_simplex(3) + +cube0 = gk.unit_n_cube(0) +cube1 = gk.unit_n_cube(1) +cube2 = gk.unit_n_cube(2) +cube3 = gk.unit_n_cube(3) + +degree = 4 +quad = gk.default_quadrature(spx0,degree) +quad = gk.default_quadrature(spx1,degree) +quad = gk.default_quadrature(spx2,degree) +quad = gk.default_quadrature(spx3,degree) + +quad = gk.default_quadrature(cube0,degree) +@test sum(gk.weights(quad)) ≈ 1 +quad = gk.default_quadrature(cube1,degree) +@test sum(gk.weights(quad)) ≈ 1 +quad = gk.default_quadrature(cube2,degree) +@test sum(gk.weights(quad)) ≈ 1 +quad = gk.default_quadrature(cube3,degree) +@test sum(gk.weights(quad)) ≈ 1 + + +end # module diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl new file mode 100644 index 00000000..e69de29b diff --git a/test/runtests.jl b/test/runtests.jl index a2ed310a..4596b0e2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,10 @@ module GalerkinToolkitTests using Test -@testset "Mesh interface" begin include("mesh_interface_tests.jl") end +@testset "GalerkinToolkit" begin + @testset "Geometry" begin include("geometry_tests.jl") end + @testset "Integration" begin include("integration_tests.jl") end + @testset "Interpolation" begin include("interpolation_tests.jl") end +end end # module From 69583d398acb4f5e8a90a0f93dffe7c3932addf4 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Sun, 4 Feb 2024 17:57:36 +0100 Subject: [PATCH 30/34] Starting to add example002 --- .../GalerkinToolkitExamples/Project.toml | 1 + .../src/GalerkinToolkitExamples.jl | 1 + .../GalerkinToolkitExamples/src/example001.jl | 86 ++++---- .../GalerkinToolkitExamples/src/example002.jl | 183 ++++++++++++++++++ .../test/example002_tests.jl | 28 +++ .../GalerkinToolkitExamples/test/runtests.jl | 5 +- src/geometry.jl | 12 ++ 7 files changed, 279 insertions(+), 37 deletions(-) create mode 100644 extensions/GalerkinToolkitExamples/src/example002.jl create mode 100644 extensions/GalerkinToolkitExamples/test/example002_tests.jl diff --git a/extensions/GalerkinToolkitExamples/Project.toml b/extensions/GalerkinToolkitExamples/Project.toml index 30466f76..50981091 100644 --- a/extensions/GalerkinToolkitExamples/Project.toml +++ b/extensions/GalerkinToolkitExamples/Project.toml @@ -7,6 +7,7 @@ version = "0.1.0" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" GalerkinToolkit = "5e3ba9c4-dd81-444d-b69a-0e7bd7bf60a4" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" diff --git a/extensions/GalerkinToolkitExamples/src/GalerkinToolkitExamples.jl b/extensions/GalerkinToolkitExamples/src/GalerkinToolkitExamples.jl index 3119650d..03dd517e 100644 --- a/extensions/GalerkinToolkitExamples/src/GalerkinToolkitExamples.jl +++ b/extensions/GalerkinToolkitExamples/src/GalerkinToolkitExamples.jl @@ -1,5 +1,6 @@ module GalerkinToolkitExamples include("example001.jl") +include("example002.jl") end # module diff --git a/extensions/GalerkinToolkitExamples/src/example001.jl b/extensions/GalerkinToolkitExamples/src/example001.jl index e439cf60..957a5256 100644 --- a/extensions/GalerkinToolkitExamples/src/example001.jl +++ b/extensions/GalerkinToolkitExamples/src/example001.jl @@ -13,21 +13,21 @@ using WriteVTK function main(params_in) # Process params - params_default = default_params_poisson() + params_default = default_params() params = add_default_params(params_in,params_default) + results = Dict{Symbol,Any}() # Setup main data structures state = setup(params) + add_basic_info(results,params,state) # Assemble system and solve it A,b = assemble_system(state) - add_neumann_bcs!(b,state) - uh = solve_system(A,b,state) + x = solve_system(A,b,params) # Post process - results = Dict{Symbol,Any}() - add_basic_info!(results,state) - integrate_error_norms!(results,uh,state) + uh = setup_uh(x,state) + integrate_error_norms(results,uh,state) export_results(uh,params,state) results @@ -45,7 +45,7 @@ function add_default_params(params_in,params_default) Dict(vcat(a,b)) end -function default_params_poisson() +function default_params() outdir = mkpath(joinpath(@__DIR__,"..","output")) params = Dict{Symbol,Any}() params[:mesh] = gk.cartesian_mesh((0,1,0,1),(10,10)) @@ -56,7 +56,7 @@ function default_params_poisson() params[:neumann_tags] = String[] params[:integration_degree] = 2 params[:solver] = lu_solver() - params[:example_path] = joinpath(outdir,"poisson") + params[:example_path] = joinpath(outdir,"example001") params end @@ -154,7 +154,20 @@ function setup(params) state = (;solver,dirichlet_bcs,neumann_bcs,cell_integration,cell_isomap,face_integration,face_isomap,user_funs) end -function assemble_system(state) +function add_basic_info(results,params,state) + node_to_x = state.cell_isomap.node_to_coords + free_and_dirichlet_nodes = state.dirichlet_bcs.free_and_dirichlet_nodes + cell_to_rid = state.cell_isomap.face_to_rid + nfree = length(first(free_and_dirichlet_nodes)) + nnodes = length(node_to_x) + ncells = length(cell_to_rid) + results[:nnodes] = nnodes + results[:nfree] = nfree + results[:ncells] = ncells + results +end + +function assemble_system_loop(state) cell_to_nodes = state.cell_isomap.face_to_nodes cell_to_rid = state.cell_isomap.face_to_rid @@ -285,10 +298,8 @@ function assemble_system(state) end end end - - A = sparse(I_coo,J_coo,V_coo,nfree,nfree) - - A,b + add_neumann_bcs!(b,state) + I_coo,J_coo,V_coo,b end function add_neumann_bcs!(b,state) @@ -353,15 +364,27 @@ function add_neumann_bcs!(b,state) nothing end -function solve_system(A,b,state) - solver = state.solver +function assemble_system(state) + I,J,V,b = assemble_system_loop(state) + nfree = length(b) + A = sparse(I,J,V,nfree,nfree) + A,b +end + +function solve_system(A,b,params) + solver = params[:solver] + x = similar(b,axes(A,2)) + setup = solver.setup(x,A,b) + solver.solve!(x,setup,b) + solver.finalize!(setup) + x +end + +function setup_uh(x,state) free_and_dirichlet_nodes = state.dirichlet_bcs.free_and_dirichlet_nodes - u_dirichlet = state.dirichlet_bcs.u_dirichlet node_to_x = state.cell_isomap.node_to_coords - u_free = similar(b,axes(A,2)) - setup = solver.setup(u_free,A,b) - solver.solve!(u_free,setup,b) - solver.finalize!(setup) + u_free = x + u_dirichlet = state.dirichlet_bcs.u_dirichlet nnodes = length(node_to_x) uh = zeros(nnodes) uh[first(free_and_dirichlet_nodes)] = u_free @@ -369,20 +392,7 @@ function solve_system(A,b,state) uh end -function add_basic_info!(results,state) - node_to_x = state.cell_isomap.node_to_coords - free_and_dirichlet_nodes = state.dirichlet_bcs.free_and_dirichlet_nodes - cell_to_rid = state.cell_isomap.face_to_rid - nfree = length(first(free_and_dirichlet_nodes)) - nnodes = length(node_to_x) - ncells = length(cell_to_rid) - results[:nnodes] = nnodes - results[:nfree] = nfree - results[:ncells] = ncells - results -end - -function integrate_error_norms!(results,uh,state) +function integrate_error_norms_loop(uh,state) cell_to_nodes = state.cell_isomap.face_to_nodes cell_to_rid = state.cell_isomap.face_to_rid @@ -447,9 +457,13 @@ function integrate_error_norms!(results,uh,state) el2 += ex*ex*dV end end + eh1, el2 +end - eh1 = sqrt(eh1) - el2 = sqrt(el2) +function integrate_error_norms(results,uh,state) + eh1², el2² = integrate_error_norms_loop(uh,state) + eh1 = sqrt(eh1²) + el2 = sqrt(el2²) results[:eh1] = eh1 results[:el2] = el2 results diff --git a/extensions/GalerkinToolkitExamples/src/example002.jl b/extensions/GalerkinToolkitExamples/src/example002.jl new file mode 100644 index 00000000..fec4eddc --- /dev/null +++ b/extensions/GalerkinToolkitExamples/src/example002.jl @@ -0,0 +1,183 @@ +module Example002 + +import GalerkinToolkit as gk +import GalerkinToolkitExamples: Example001 +import ForwardDiff +using StaticArrays +using LinearAlgebra +using SparseArrays +using WriteVTK +using PartitionedArrays + +function main(params_in) + + # Dict to collect results + results = Dict{Symbol,Any}() + + # Process params + params_default = default_params() + params = add_default_params(params_in,params_default) + + # Setup main data structures + state = setup(params) + add_basic_info(results,params,state) + + # Assemble system and solve it + A,b = assemble_system(state) + x = solve_system(A,b,params) + + # Post process + uh = setup_uh(x,state) + integrate_error_norms(results,uh,state) + export_results(uh,params,state) + + results +end + +function add_default_params(params_in,params_default) + UmD = setdiff(keys(params_in),keys(params_default)) + for k in UmD + @warn "Parameter with key :$k is unused" + end + UiD = intersect(keys(params_default),keys(params_in)) + DmU = setdiff(keys(params_default),keys(params_in)) + a = [ k=>params_in[k] for k in UiD] + b = [ k=>params_default[k] for k in DmU] + Dict(vcat(a,b)) +end + +function default_params() + np = 4 + parts = DebugArray(LinearIndices((np,))) + domain = (0,1,0,1) + cells_per_dir = (10,10) + parts_per_dir = (2,2) + ghost_layers = 0 + mesh = gk.cartesian_mesh(domain,cells_per_dir,parts_per_dir;parts,ghost_layers) + outdir = mkpath(joinpath(@__DIR__,"..","output")) + params = Example001.default_params() + params[:mesh] = mesh + params[:example_path] = joinpath(outdir,"example002") + params +end + +function setup(params) + mesh = params[:mesh] + local_states = map(partition(mesh)) do mesh + local_params = copy(params) + local_params[:mesh] = mesh + Example001.setup(local_params) + end + + node_partition = gk.node_partition(mesh) + global_node_to_mask = pfill(false,node_partition) + function fillmask!(node_to_mask,state) + free_nodes = first(state.dirichlet_bcs.free_and_dirichlet_nodes) + node_to_mask[free_nodes] .= true + end + map(fillmask!,partition(global_node_to_mask),local_states) + global_dof_to_node, global_node_to_dof = find_local_indices(global_node_to_mask) + dof_partition = partition(axes(global_dof_to_node,1)) + state = (;local_states,global_dof_to_node, global_node_to_dof) + state +end + +function add_basic_info(results,params,state) + mesh = params[:mesh] + nfree = length(state.global_dof_to_node) + nnodes = gk.num_nodes(mesh) + ncells = gk.num_faces(mesh,gk.num_dims(mesh)) + results[:nfree] = nfree + results[:nnodes] = nnodes + results[:ncells] = ncells + results +end + +function assemble_system(state) + dof_partition = partition(axes(state.global_dof_to_node,1)) + b = pzeros(Float64,dof_partition) + I,J,V,free_node_to_b = map(Example001.assemble_system_loop,state.local_states) |> tuple_of_arrays + function map_dofs(state,node_to_dof,dofs,I,J,free_node_to_b,dof_to_b) + free_node_to_node = first(state.dirichlet_bcs.free_and_dirichlet_nodes) + dof_to_global_dof = local_to_global(dofs) + function free_node_to_global_dof(free_node) + # TODO this free_node -> dof should not be needed of the order free dofs conveniently + node = free_node_to_node[free_node] + dof = node_to_dof[node] + global_dof = dof_to_global_dof[dof] + global_dof + end + I .= free_node_to_global_dof.(I) + J .= free_node_to_global_dof.(J) + free_node_to_dof = view(node_to_dof,free_node_to_node) + # TODO this one should not be needed of the order free dofs conveniently + dof_to_b[free_node_to_dof] = free_node_to_b + nothing + end + map(map_dofs,state.local_states,partition(state.global_node_to_dof),dof_partition,I,J,free_node_to_b,partition(b)) + A = psparse(I,J,V,dof_partition,dof_partition;assemble=false) |> fetch + # TODO + A = assemble(A) |> fetch + b = assemble(b,partition(axes(A,1))) |> fetch + A,b +end + +function solve_system(A,b,params) + solver = params[:solver] + x = similar(b,axes(A,2)) + setup = solver.setup(x,A,b) + solver.solve!(x,setup,b) + solver.finalize!(setup) + x +end + +function setup_uh(x,state) + dofs = axes(state.global_dof_to_node,1) + local_states = state.local_states + + # TODO this similar can be avoided when using + # a sub-assembled system + global_dof_to_x = similar(x,dofs) + global_dof_to_x .= x + consistent!(global_dof_to_x) |> wait + + # TODO this map can be avoided + # when the local numeration of free nodes coincides + # with local dofs. + function map_dofs(dof_to_x,node_to_dof,state) + free_node_to_node = first(state.dirichlet_bcs.free_and_dirichlet_nodes) + free_node_to_dof = view(node_to_dof,free_node_to_node) + view(dof_to_x,free_node_to_dof) + end + free_node_to_x = map(map_dofs,partition(global_dof_to_x),partition(state.global_node_to_dof),state.local_states) + + uh = map(Example001.setup_uh,free_node_to_x,local_states) + uh +end + +function integrate_error_norms(results,uh,state) + local_states = state.local_states + eh1², el2² = map(Example001.integrate_error_norms_loop,uh,local_states) |> tuple_of_arrays + eh1 = sqrt(sum(eh1²)) + el2 = sqrt(sum(el2²)) + results[:eh1] = eh1 + results[:el2] = el2 + results +end + +function export_results(uh,params,state) + example_path = params[:example_path] + mesh = params[:mesh] + ranks = linear_indices(gk.node_partition(mesh)) + np = length(ranks) + map(partition(mesh),ranks,uh,gk.index_partition(mesh)) do mesh,rank,uh,ids + pvtk_grid(example_path,gk.vtk_args(mesh)...;part=rank,nparts=np) do vtk + gk.vtk_physical_faces!(vtk,mesh) + vtk["piece",VTKCellData()] = fill(rank,sum(gk.num_faces(mesh))) + vtk["owner",VTKPointData()] = local_to_owner(gk.node_indices(ids)) + vtk["uh",VTKPointData()] = uh + end + end +end + +end # module diff --git a/extensions/GalerkinToolkitExamples/test/example002_tests.jl b/extensions/GalerkinToolkitExamples/test/example002_tests.jl new file mode 100644 index 00000000..27149066 --- /dev/null +++ b/extensions/GalerkinToolkitExamples/test/example002_tests.jl @@ -0,0 +1,28 @@ +module Example002Tests + +import GalerkinToolkit as gk +using GalerkinToolkitExamples: Example002 +using Test +using PartitionedArrays + +tol = 1.0e-10 + +params = Dict{Symbol,Any}() +results = Example002.main(params) +@test results[:eh1] < tol +@test results[:el2] < tol + +domain = (0,10,0,10) +cells_per_dir = (20,20) +parts_per_dir = (2,2) +np = prod(parts_per_dir) +parts = DebugArray(LinearIndices((np,))) +ghost_layers = 0 +mesh = gk.cartesian_mesh(domain,cells_per_dir,parts_per_dir;parts,ghost_layers) + +params = Dict{Symbol,Any}() +results = Example002.main(params) +@test results[:eh1] < tol +@test results[:el2] < tol + +end # module diff --git a/extensions/GalerkinToolkitExamples/test/runtests.jl b/extensions/GalerkinToolkitExamples/test/runtests.jl index 95a1c303..b448524c 100644 --- a/extensions/GalerkinToolkitExamples/test/runtests.jl +++ b/extensions/GalerkinToolkitExamples/test/runtests.jl @@ -2,6 +2,9 @@ module GalerkinToolkitExamplesTests using Test -@testset "example001" begin include("example001_tests.jl") end +@testset "GalerkinToolkitExamples" begin + @testset "example001" begin include("example001_tests.jl") end + @testset "example002" begin include("example002_tests.jl") end +end end # module diff --git a/src/geometry.jl b/src/geometry.jl index 64c0de47..d22a3c36 100644 --- a/src/geometry.jl +++ b/src/geometry.jl @@ -3272,6 +3272,18 @@ function index_partition(a::PMesh) map(setup,a.node_partition,a.face_partition...) end +function num_nodes(mesh::PMesh) + length(PRange(mesh.node_partition)) +end + +function num_faces(mesh::PMesh,d) + length(PRange(mesh.face_partition[d+1])) +end + +function num_dims(mesh::PMesh) + length(mesh.face_partition) - 1 +end + struct PMeshLocalIds{A,B} <: GalerkinToolkitDataType node_indices::A face_indices::B From fdc74087e9542b386aa7c8c3b07374342f45a43f Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 5 Feb 2024 13:53:44 +0100 Subject: [PATCH 31/34] Using two_level_mesh in example001 --- extensions/GalerkinToolkitExamples/Project.toml | 1 - .../test/example001_tests.jl | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/extensions/GalerkinToolkitExamples/Project.toml b/extensions/GalerkinToolkitExamples/Project.toml index 50981091..db775e48 100644 --- a/extensions/GalerkinToolkitExamples/Project.toml +++ b/extensions/GalerkinToolkitExamples/Project.toml @@ -11,7 +11,6 @@ PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" WriteVTK = "64499a7a-5c06-52f2-abe2-ccb03c286192" -prrte_jll = "eb928a42-fffd-568d-ab9c-3f5d54fc65b9" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/extensions/GalerkinToolkitExamples/test/example001_tests.jl b/extensions/GalerkinToolkitExamples/test/example001_tests.jl index bb476b38..ceda93e7 100644 --- a/extensions/GalerkinToolkitExamples/test/example001_tests.jl +++ b/extensions/GalerkinToolkitExamples/test/example001_tests.jl @@ -42,5 +42,19 @@ results = Example001.main(params) @test results[:eh1] < tol @test results[:el2] < tol +domain = (0,1,0,1) +cells = (10,10) +fine_mesh = gk.cartesian_mesh(domain,cells) +domain = (0,30,0,10) +cells = (2,2) +coarse_mesh = gk.cartesian_mesh(domain,cells) +mesh, = gk.two_level_mesh(coarse_mesh,fine_mesh) + +params = Dict{Symbol,Any}() +params[:mesh] = mesh +results = Example001.main(params) +@test results[:eh1] < tol +@test results[:el2] < tol + end # module From d1e0aca5e3b515a65ac250e860457cb41a31ee4a Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 5 Feb 2024 14:21:47 +0100 Subject: [PATCH 32/34] Adding opposite_faces --- src/geometry.jl | 23 +++++++++++++++++++++++ test/geometry_tests.jl | 10 ++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/geometry.jl b/src/geometry.jl index d22a3c36..67d2ac64 100644 --- a/src/geometry.jl +++ b/src/geometry.jl @@ -906,11 +906,18 @@ function vtk_mesh_cell(ref_face) nodes -> WriteVTK.MeshCell(cell_type,(nodes[lib_to_user])[vtk_to_lib]) end +opposite_faces(geom,d) = opposite_faces(geom)[d+1] + # TODO use the same convention than in Gridap # allow the user to customize the boundary object ids function boundary(geom::ExtrusionPolytope{0}) nothing end + +function opposite_faces(geom::ExtrusionPolytope{0}) + [[1]] +end + function boundary(geom::ExtrusionPolytope{1}) Tv = real_type(geom) Ti = int_type(geom) @@ -936,6 +943,11 @@ function boundary(geom::ExtrusionPolytope{1}) outwards_normals ) end + +function opposite_faces(geom::ExtrusionPolytope{1}) + [[2,1],[1]] +end + function boundary(geom::ExtrusionPolytope{2}) Tv = real_type(geom) Ti = int_type(geom) @@ -969,6 +981,12 @@ function boundary(geom::ExtrusionPolytope{2}) outwards_normals ) end + +function opposite_faces(geom::ExtrusionPolytope{2}) + @assert is_n_cube(geom) + [[4,3,2,1],[2,1,4,3],[1]] +end + function boundary(geom::ExtrusionPolytope{3}) Tv = real_type(geom) Ti = int_type(geom) @@ -1014,6 +1032,11 @@ function boundary(geom::ExtrusionPolytope{3}) ) end +function opposite_faces(geom::ExtrusionPolytope{3}) + @assert is_n_cube(geom) + [[8,7,6,5,4,3,2,1],[6,5,8,7,2,1,4,3,12,11,10,9],[2,1,4,3,6,5],[1]] +end + function boundary(refface::AbstractMeshFace) boundary_from_mesh_face(refface) end diff --git a/test/geometry_tests.jl b/test/geometry_tests.jl index bc6f7b8b..440017ee 100644 --- a/test/geometry_tests.jl +++ b/test/geometry_tests.jl @@ -91,6 +91,16 @@ end @show gk.unit_n_cube(2) |> gk.boundary @show gk.unit_n_cube(3) |> gk.boundary +gk.unit_n_cube(0) |> gk.opposite_faces +gk.unit_n_cube(1) |> gk.opposite_faces +gk.unit_n_cube(2) |> gk.opposite_faces +gk.unit_n_cube(3) |> gk.opposite_faces + +gk.opposite_faces(cube3,0) +gk.opposite_faces(cube3,1) +gk.opposite_faces(cube3,2) +gk.opposite_faces(cube3,3) + order = 2 gk.lagrange_mesh_face(spx1,order) |> gk.boundary |> gk.topology From 7f549f72a6cf8db577257a7985a1e2380cbda8ef Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 5 Feb 2024 14:31:52 +0100 Subject: [PATCH 33/34] Trying to fix CI --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c3f1f3d2..fb145c0b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -59,7 +59,7 @@ jobs: - run: | julia --project=extensions/GalerkinToolkitExamples -e ' using Pkg - Pkg.dev(".") + Pkg.develop(".") Pkg.test("GalerkinToolkitExamples")' docs: name: Documentation From 711c10da736fca93e679c83836804766d1910843 Mon Sep 17 00:00:00 2001 From: Francesc Verdugo Date: Mon, 5 Feb 2024 14:35:12 +0100 Subject: [PATCH 34/34] 2nd attempt to fix CI --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fb145c0b..172300ed 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -59,7 +59,7 @@ jobs: - run: | julia --project=extensions/GalerkinToolkitExamples -e ' using Pkg - Pkg.develop(".") + Pkg.develop(path=".") Pkg.test("GalerkinToolkitExamples")' docs: name: Documentation