11#include <polysolve/linear/Solver.hpp>
18#include <Eigen/Sparse>
31 using namespace Eigen;
32 using namespace assembler;
40 typedef Matrix<std::vector<int>, 3, 3> SpaceMatrix;
41 typedef Matrix<RowVectorNd, 3, 3> NodeMatrix;
43 void print_local_space(
const SpaceMatrix &space)
45 std::cout << std::endl;
46 for (
int j = 2; j >= 0; --j)
48 for (
int i = 0; i < 3; ++i)
50 if (space(i, j).size() > 0)
52 for (std::size_t l = 0; l < space(i, j).size(); ++l)
53 std::cout << space(i, j)[l] <<
",";
60 std::cout << std::endl;
64 int node_id_from_edge_index(
const Mesh2D &mesh, MeshNodes &mesh_nodes,
const Navigation::Index &index)
66 const int face_id = mesh.switch_face(index).face;
67 if (face_id >= 0 && mesh.is_cube(face_id))
68 return mesh_nodes.node_id_from_face(face_id);
70 return mesh_nodes.node_id_from_edge(index.edge);
73 void explore_direction(
const Navigation::Index &index,
const Mesh2D &mesh, MeshNodes &mesh_nodes,
const int x,
const int y,
const bool is_x,
const bool invert, LocalBoundary &lb, SpaceMatrix &space, NodeMatrix &node, std::map<int, InterfaceData> &poly_edge_to_data)
75 int node_id = node_id_from_edge_index(mesh, mesh_nodes, index);
78 assert(std::find(space(
x,
y).begin(), space(
x,
y).end(), node_id) == space(
x,
y).end());
79 space(
x,
y).push_back(node_id);
80 node(
x,
y) = mesh_nodes.node_position(node_id);
81 assert(node(
x,
y).size() == 2);
83 const int x1 = is_x ?
x : (invert ? 2 : 0);
84 const int y1 = !is_x ?
y : (invert ? 2 : 0);
86 const int x2 = is_x ?
x : (invert ? 0 : 2);
87 const int y2 = !is_x ?
y : (invert ? 0 : 2);
89 const bool is_boundary = mesh_nodes.is_boundary(node_id);
90 const bool is_interface = mesh_nodes.is_interface(node_id);
98 else if (is_interface)
100 InterfaceData &data = poly_edge_to_data[index.edge];
101 data.local_indices.push_back(
y * 3 +
x);
105 assert(!is_boundary && !is_interface);
107 Navigation::Index start_index = mesh.switch_face(index);
108 assert(start_index.vertex == index.vertex);
109 assert(start_index.face >= 0);
111 Navigation::Index edge1 = mesh.switch_edge(start_index);
112 node_id = node_id_from_edge_index(mesh, mesh_nodes, edge1);
116 if (std::find(space(x1, y1).begin(), space(x1, y1).end(), node_id) == space(x1, y1).end())
118 space(x1, y1).push_back(node_id);
119 node(x1, y1) = mesh_nodes.node_position(node_id);
122 Navigation::Index edge2 = mesh.switch_edge(mesh.switch_vertex(start_index));
123 node_id = node_id_from_edge_index(mesh, mesh_nodes, edge2);
126 if (std::find(space(x2, y2).begin(), space(x2, y2).end(), node_id) == space(x2, y2).end())
128 space(x2, y2).push_back(node_id);
129 node(x2, y2) = mesh_nodes.node_position(node_id);
135 void add_id_for_poly(
const Navigation::Index &index,
const int x1,
const int y1,
const int x2,
const int y2,
const SpaceMatrix &space, std::map<int, InterfaceData> &poly_edge_to_data)
137 auto it = poly_edge_to_data.find(index.edge);
138 if (it != poly_edge_to_data.end())
140 InterfaceData &data = it->second;
142 assert(space(x1, y1).size() == 1);
143 data.local_indices.push_back(y1 * 3 + x1);
145 assert(space(x2, y2).size() == 1);
146 data.local_indices.push_back(y2 * 3 + x2);
150 void build_local_space(
const Mesh2D &mesh, MeshNodes &mesh_nodes,
const int el_index, SpaceMatrix &space, NodeMatrix &node, std::vector<LocalBoundary> &local_boundary, std::map<int, InterfaceData> &poly_edge_to_data)
152 assert(!mesh.is_volume());
154 Navigation::Index index;
157 const int el_node_id = mesh_nodes.node_id_from_face(el_index);
158 space(1, 1).push_back(el_node_id);
159 node(1, 1) = mesh_nodes.node_position(el_node_id);
162 LocalBoundary lb(el_index, BoundaryType::QUAD_LINE);
165 index = mesh.get_index_from_face(el_index);
166 explore_direction(index, mesh, mesh_nodes, 1, 0,
false,
false, lb, space, node, poly_edge_to_data);
169 index = mesh.next_around_face(index);
170 explore_direction(index, mesh, mesh_nodes, 2, 1,
true,
false, lb, space, node, poly_edge_to_data);
173 index = mesh.next_around_face(index);
174 explore_direction(index, mesh, mesh_nodes, 1, 2,
false,
true, lb, space, node, poly_edge_to_data);
177 index = mesh.next_around_face(index);
178 explore_direction(index, mesh, mesh_nodes, 0, 1,
true,
true, lb, space, node, poly_edge_to_data);
181 if (mesh_nodes.is_boundary_or_interface(space(1, 2).front()) && mesh_nodes.is_boundary_or_interface(space(2, 1).front()))
183 assert(space(2, 2).empty());
185 Navigation::Index start_index = mesh.get_index_from_face(el_index);
186 start_index = mesh.next_around_face(start_index);
187 start_index = mesh.next_around_face(start_index);
189 const int node_id = mesh_nodes.node_id_from_vertex(start_index.vertex);
191 space(2, 2).push_back(node_id);
192 node(2, 2) = mesh_nodes.node_position(node_id);
198 if (mesh_nodes.is_boundary_or_interface(space(1, 0).front()) && mesh_nodes.is_boundary_or_interface(space(2, 1).front()))
200 assert(space(2, 0).empty());
202 Navigation::Index start_index = mesh.get_index_from_face(el_index);
203 start_index = mesh.next_around_face(start_index);
205 const int node_id = mesh_nodes.node_id_from_vertex(start_index.vertex);
207 space(2, 0).push_back(node_id);
208 node(2, 0) = mesh_nodes.node_position(node_id);
214 if (mesh_nodes.is_boundary_or_interface(space(1, 2).front()) && mesh_nodes.is_boundary_or_interface(space(0, 1).front()))
216 assert(space(0, 2).empty());
218 Navigation::Index start_index = mesh.get_index_from_face(el_index);
219 start_index = mesh.next_around_face(start_index);
220 start_index = mesh.next_around_face(start_index);
221 start_index = mesh.next_around_face(start_index);
224 const int node_id = mesh_nodes.node_id_from_vertex(start_index.vertex);
225 space(0, 2).push_back(node_id);
226 node(0, 2) = mesh_nodes.node_position(node_id);
232 if (mesh_nodes.is_boundary_or_interface(space(1, 0).front()) && mesh_nodes.is_boundary_or_interface(space(0, 1).front()))
234 Navigation::Index start_index = mesh.get_index_from_face(el_index);
237 const int node_id = mesh_nodes.node_id_from_vertex(start_index.vertex);
238 space(0, 0).push_back(node_id);
239 node(0, 0) = mesh_nodes.node_position(node_id);
249 index = mesh.get_index_from_face(el_index);
250 add_id_for_poly(index, 0, 0, 2, 0, space, poly_edge_to_data);
252 index = mesh.next_around_face(index);
253 add_id_for_poly(index, 2, 0, 2, 2, space, poly_edge_to_data);
255 index = mesh.next_around_face(index);
256 add_id_for_poly(index, 2, 2, 0, 2, space, poly_edge_to_data);
258 index = mesh.next_around_face(index);
259 add_id_for_poly(index, 0, 2, 0, 0, space, poly_edge_to_data);
262 local_boundary.emplace_back(lb);
265 void setup_knots_vectors(MeshNodes &mesh_nodes,
const SpaceMatrix &space, std::array<std::array<double, 4>, 3> &h_knots, std::array<std::array<double, 4>, 3> &v_knots)
268 if (mesh_nodes.is_boundary_or_interface(space(0, 1).front()) && mesh_nodes.is_boundary_or_interface(space(2, 1).front()))
270 h_knots[0] = {{0, 0, 0, 1}};
271 h_knots[1] = {{0, 0, 1, 1}};
272 h_knots[2] = {{0, 1, 1, 1}};
275 else if (mesh_nodes.is_boundary_or_interface(space(0, 1).front()))
277 h_knots[0] = {{0, 0, 0, 1}};
278 h_knots[1] = {{0, 0, 1, 2}};
279 h_knots[2] = {{0, 1, 2, 3}};
282 else if (mesh_nodes.is_boundary_or_interface(space(2, 1).front()))
284 h_knots[0] = {{-2, -1, 0, 1}};
285 h_knots[1] = {{-1, 0, 1, 1}};
286 h_knots[2] = {{0, 1, 1, 1}};
290 h_knots[0] = {{-2, -1, 0, 1}};
291 h_knots[1] = {{-1, 0, 1, 2}};
292 h_knots[2] = {{0, 1, 2, 3}};
296 if (mesh_nodes.is_boundary_or_interface(space(1, 0).front()) && mesh_nodes.is_boundary_or_interface(space(1, 2).front()))
298 v_knots[0] = {{0, 0, 0, 1}};
299 v_knots[1] = {{0, 0, 1, 1}};
300 v_knots[2] = {{0, 1, 1, 1}};
303 else if (mesh_nodes.is_boundary_or_interface(space(1, 0).front()))
305 v_knots[0] = {{0, 0, 0, 1}};
306 v_knots[1] = {{0, 0, 1, 2}};
307 v_knots[2] = {{0, 1, 2, 3}};
310 else if (mesh_nodes.is_boundary_or_interface(space(1, 2).front()))
312 v_knots[0] = {{-2, -1, 0, 1}};
313 v_knots[1] = {{-1, 0, 1, 1}};
314 v_knots[2] = {{0, 1, 1, 1}};
318 v_knots[0] = {{-2, -1, 0, 1}};
319 v_knots[1] = {{-1, 0, 1, 2}};
320 v_knots[2] = {{0, 1, 2, 3}};
324 void basis_for_regular_quad(
const SpaceMatrix &space,
const NodeMatrix &loc_nodes,
const std::array<std::array<double, 4>, 3> &h_knots,
const std::array<std::array<double, 4>, 3> &v_knots, ElementBases &b)
326 for (
int y = 0;
y < 3; ++
y)
328 for (
int x = 0;
x < 3; ++
x)
330 if (space(
x,
y).size() == 1)
332 const int global_index = space(
x,
y).front();
333 const Eigen::MatrixXd &node = loc_nodes(
x,
y);
334 assert(node.size() == 2);
336 const int local_index =
y * 3 +
x;
337 b.bases[local_index].init(2, global_index, local_index, node);
339 const QuadraticBSpline2d spline(h_knots[
x], v_knots[
y]);
340 b.bases[local_index].set_basis([spline](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &
val) { spline.interpolate(uv,
val); });
341 b.bases[local_index].set_grad([spline](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &
val) { spline.derivative(uv,
val); });
347 void basis_for_irregulard_quad(
const int el_id,
const Mesh2D &mesh, MeshNodes &mesh_nodes,
const SpaceMatrix &space,
const NodeMatrix &loc_nodes,
const std::array<std::array<double, 4>, 3> &h_knots,
const std::array<std::array<double, 4>, 3> &v_knots, ElementBases &b)
349 for (
int y = 0;
y < 3; ++
y)
351 for (
int x = 0;
x < 3; ++
x)
353 if (space(
x,
y).size() > 1)
361 std::vector<int> other_indices;
362 const auto ¢er = b.bases[1 * 3 + 1].global().front();
364 const auto &el1 = b.bases[mpy * 3 + mpx].global().front();
365 const auto &el2 = b.bases[mmy * 3 + mmx].global().front();
367 Navigation::Index start_index = mesh.get_index_from_face(el_id);
369 for (
int i = 0; i < 4; ++i)
371 other_indices.clear();
373 Navigation::Index index = start_index;
376 const int f_index = mesh_nodes.node_id_from_face(index.face);
377 if (f_index != el1.index && f_index != el2.index && f_index != center.index)
378 other_indices.push_back(f_index);
381 index = mesh.next_around_vertex(index);
382 }
while (index.face != start_index.face);
389 start_index = mesh.next_around_face(start_index);
393 const int local_index =
y * 3 +
x;
394 auto &
base = b.bases[local_index];
396 const int k = int(other_indices.size()) + 3;
398 base.global().resize(k);
400 base.global()[0].index = center.index;
401 base.global()[0].val = (4. - k) / k;
402 base.global()[0].node = center.node;
404 base.global()[1].index = el1.index;
405 base.global()[1].val = (4. - k) / k;
406 base.global()[1].node = el1.node;
408 base.global()[2].index = el2.index;
409 base.global()[2].val = (4. - k) / k;
410 base.global()[2].node = el2.node;
412 for (std::size_t n = 0; n < other_indices.size(); ++n)
414 base.global()[3 + n].index = other_indices[n];
415 base.global()[3 + n].val = 4. / k;
416 base.global()[3 + n].node = mesh_nodes.node_position(other_indices[n]);
419 const QuadraticBSpline2d spline(h_knots[
x], v_knots[
y]);
420 b.bases[local_index].set_basis([spline](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &
val) { spline.interpolate(uv,
val); });
421 b.bases[local_index].set_grad([spline](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &
val) { spline.derivative(uv,
val); });
427 void create_q2_nodes(
const Mesh2D &mesh,
const int el_index, std::set<int> &vertex_id, std::set<int> &
edge_id, ElementBases &b, std::vector<LocalBoundary> &local_boundary,
int &n_bases)
431 LocalBoundary lb(el_index, BoundaryType::QUAD_LINE);
433 Navigation::Index index = mesh.get_index_from_face(el_index);
434 for (
int j = 0; j < 4; ++j)
436 int current_vertex_node_id = -1;
437 int current_edge_node_id = -1;
438 Eigen::Matrix<double, 1, 2> current_edge_node;
439 Eigen::MatrixXd current_vertex_node;
444 int vertex_basis_id = e2l[0];
445 int edge_basis_id = e2l[1];
447 const int opposite_face = mesh.switch_face(index).face;
450 bool is_vertex_q2 =
true;
451 bool is_vertex_boundary =
false;
453 Navigation::Index vindex = index;
459 is_vertex_boundary =
true;
462 if (mesh.is_spline_compatible(vindex.face))
464 is_vertex_q2 =
false;
467 vindex = mesh.next_around_vertex(vindex);
468 }
while (vindex.edge != index.edge);
472 vindex = mesh.switch_face(index);
477 is_vertex_boundary =
true;
481 if (mesh.is_spline_compatible(vindex.face))
483 is_vertex_q2 =
false;
486 vindex = mesh.next_around_vertex(vindex);
487 }
while (vindex.edge != index.edge);
490 const bool is_edge_q2 = opposite_face < 0 || !mesh.is_spline_compatible(opposite_face);
494 const bool is_new_edge =
edge_id.insert(index.edge).second;
498 current_edge_node_id = n_bases++;
499 current_edge_node = mesh.edge_barycenter(index.edge);
501 if (opposite_face < 0)
504 lb.add_boundary_primitive(index.edge, edge_basis_id - 4);
512 const bool is_new_vertex = vertex_id.insert(index.vertex).second;
516 current_vertex_node_id = n_bases++;
517 current_vertex_node = mesh.point(index.vertex);
525 if (current_vertex_node_id >= 0)
526 b.bases[vertex_basis_id].init(2, current_vertex_node_id, vertex_basis_id, current_vertex_node);
528 if (current_edge_node_id >= 0)
529 b.bases[edge_basis_id].init(2, current_edge_node_id, edge_basis_id, current_edge_node);
538 index = mesh.next_around_face(index);
542 const int face_basis_id = 8;
543 b.bases[face_basis_id].init(2, n_bases++, face_basis_id, mesh.face_barycenter(el_index));
548 local_boundary.emplace_back(lb);
551 void insert_into_global(
const Local2Global &data, std::vector<Local2Global> &
vec)
554 if (fabs(data.val) < 1e-10)
559 for (std::size_t i = 0; i <
vec.size(); ++i)
561 if (
vec[i].index == data.index)
564 assert(fabs(
vec[i].
val - data.val) < 1e-10);
565 assert((
vec[i].node - data.node).norm() < 1e-10);
575 void assign_q2_weights(
const Mesh2D &mesh,
const int el_index, std::vector<ElementBases> &bases)
578 std::vector<AssemblyValues> eval_p;
579 Navigation::Index index = mesh.get_index_from_face(el_index);
580 ElementBases &b = bases[el_index];
582 for (
int j = 0; j < 4; ++j)
584 const int opposite_face = mesh.switch_face(index).face;
586 if (opposite_face < 0 || !mesh.is_cube(opposite_face))
588 index = mesh.next_around_face(index);
596 Eigen::Matrix<double, 3, 2> param_p;
599 Eigen::MatrixXd quad_loc_nodes;
602 for (
int k = 0; k < 3; ++k)
603 param_p.row(k) = quad_loc_nodes.row(opposite_indices[k]);
612 const auto &other_bases = bases[opposite_face];
613 other_bases.evaluate_bases(param_p, eval_p);
615 for (std::size_t i = 0; i < other_bases.bases.size(); ++i)
617 const auto &other_b = other_bases.bases[i];
619 if (other_b.global().empty())
622 assert(eval_p[i].
val.size() == 3);
625 if (eval_p[i].
val.cwiseAbs().maxCoeff() <= 1e-10)
628 for (std::size_t k = 0; k < other_b.global().size(); ++k)
634 auto glob0 = other_b.global()[k];
635 glob0.val *= eval_p[i].val(0);
636 auto glob1 = other_b.global()[k];
637 glob1.val *= eval_p[i].val(1);
638 auto glob2 = other_b.global()[k];
639 glob2.val *= eval_p[i].val(2);
641 insert_into_global(glob0, b.bases[i0].global());
642 insert_into_global(glob1, b.bases[i1].global());
643 insert_into_global(glob2, b.bases[i2].global());
647 index = mesh.next_around_face(index);
651 void setup_data_for_polygons(
const Mesh2D &mesh,
const int el_index,
const ElementBases &b, std::map<int, InterfaceData> &poly_edge_to_data)
653 Navigation::Index index = mesh.get_index_from_face(el_index);
654 for (
int j = 0; j < 4; ++j)
656 const int opposite_face = mesh.switch_face(index).face;
657 const bool is_neigh_poly = opposite_face >= 0 && mesh.is_polytope(opposite_face);
663 const int vertex_basis_id = e2l[0];
664 const int edge_basis_id = e2l[1];
665 const int vertex_basis_id2 = e2l[2];
667 InterfaceData &data = poly_edge_to_data[index.edge];
669 data.local_indices.push_back(edge_basis_id);
670 data.local_indices.push_back(vertex_basis_id);
671 data.local_indices.push_back(vertex_basis_id2);
674 index = mesh.next_around_face(index);
680 const std::string &assembler,
681 const int quadrature_order,
const int mass_quadrature_order, std::vector<ElementBases> &bases, std::vector<LocalBoundary> &local_boundary, std::map<int, InterfaceData> &poly_edge_to_data)
686 MeshNodes mesh_nodes(mesh,
true,
true, 1, 1);
691 local_boundary.clear();
695 for (
int e = 0; e < n_els; ++e)
701 NodeMatrix loc_nodes;
704 build_local_space(mesh, mesh_nodes, e, space, loc_nodes, local_boundary, poly_edge_to_data);
710 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", 2, AssemblerUtils::BasisType::SPLINE, 2);
723 Eigen::VectorXi res(3);
724 const auto &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
727 for (le = 0; le < mesh2d.n_face_vertices(e); ++le)
729 if (index.edge == primitive_id)
731 index = mesh2d.next_around_face(index);
733 assert(index.edge == primitive_id);
738 res << (3 * 0 + 0), (3 * 1 + 0), (3 * 2 + 0);
741 res << (3 * 0 + 0), (3 * 0 + 1), (3 * 0 + 2);
744 res << (3 * 0 + 2), (3 * 1 + 2), (3 * 2 + 2);
747 res << (3 * 2 + 0), (3 * 2 + 1), (3 * 2 + 2);
756 std::array<std::array<double, 4>, 3> h_knots;
757 std::array<std::array<double, 4>, 3> v_knots;
759 setup_knots_vectors(mesh_nodes, space, h_knots, v_knots);
763 basis_for_regular_quad(space, loc_nodes, h_knots, v_knots, b);
764 basis_for_irregulard_quad(e, mesh, mesh_nodes, space, loc_nodes, h_knots, v_knots, b);
768 std::set<int> vertex_id;
770 int n_bases = mesh_nodes.
n_nodes();
772 for (
int e = 0; e < n_els; ++e)
779 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, 2, AssemblerUtils::BasisType::CUBE_LAGRANGE, 2);
780 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", 2, AssemblerUtils::BasisType::CUBE_LAGRANGE, 2);
792 const auto &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
795 for (
int le = 0; le < mesh2d.n_face_vertices(e); ++le)
797 if (index.edge == primitive_id)
799 index = mesh2d.next_around_face(index);
801 assert(index.edge == primitive_id);
805 Eigen::VectorXi res(indices.size());
807 for (
size_t i = 0; i < indices.size(); ++i)
813 create_q2_nodes(mesh, e, vertex_id,
edge_id, b, local_boundary, n_bases);
816 bool missing_bases =
false;
819 missing_bases =
false;
820 for (
int e = 0; e < n_els; ++e)
829 assign_q2_weights(mesh, e, bases);
831 missing_bases = missing_bases || b.is_complete();
833 }
while (missing_bases);
835 for (
int e = 0; e < n_els; ++e)
840 setup_data_for_polygons(mesh, e, b, poly_edge_to_data);
static int quadrature_order(const std::string &assembler, const int basis_degree, const BasisType &b_type, const int dim)
utility for retrieving the needed quadrature order to precisely integrate the given form on the given...
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
void set_quadrature(const QuadratureFunction &fun)
void set_local_node_from_primitive_func(LocalNodeFromPrimitiveFunc fun)
sets mapping from local nodes to global nodes
std::vector< Basis > bases
one basis function per node in the element
void set_mass_quadrature(const QuadratureFunction &fun)
static Eigen::VectorXi quad_edge_local_nodes(const int q, const mesh::Mesh2D &mesh, mesh::Navigation::Index index)
static void fit_nodes(const mesh::Mesh2D &mesh, const int n_bases, std::vector< ElementBases > &gbases)
static int build_bases(const mesh::Mesh2D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_edge_to_data)
bool is_volume() const override
checks if mesh is volume
virtual Navigation::Index get_index_from_face(int f, int lv=0) const =0
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
bool is_polytope(const int el_id) const
checks if element is polygon compatible
bool is_spline_compatible(const int el_id) const
checks if element is spline compatible
void get_quadrature(const int order, Quadrature &quad)
void q_nodes_2d(const int q, Eigen::MatrixXd &val)
void q_grad_basis_value_2d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void q_basis_value_2d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)