136 template <
class InputIterator,
class T>
137 int find_index(InputIterator first, InputIterator last,
const T &
val)
139 return std::distance(first, std::find(first, last,
val));
144 std::array<int, 4> v = {{v1, v2, v3, v4}};
145 std::sort(v.begin(), v.end());
152 std::array<int, 4> u;
158 std::sort(u.begin(), u.end());
168 std::array<int, 4> tet_vertices_local_to_global(
const Mesh3D &mesh,
int c)
172 std::array<int, 4> l2g;
174 for (
int vi : mesh.get_ordered_vertices_from_tet(c))
182 std::array<int, 8> hex_vertices_local_to_global(
const Mesh3D &mesh,
int c)
187 std::array<int, 8> l2g;
189 for (
int vi : mesh.get_ordered_vertices_from_hex(c))
197 std::array<int, 6> prism_vertices_local_to_global(
const Mesh3D &mesh,
int c)
202 std::array<int, 6> l2g;
204 for (
int vi : mesh.get_ordered_vertices_from_prism(c))
212 std::array<int, 5> pyramid_vertices_local_to_global(
const Mesh3D &mesh,
int c)
217 std::array<int, 5> l2g;
219 for (
int vi : mesh.get_ordered_vertices_from_pyramid(c))
227 int prism_edge_order(
int cid,
int edge_id,
228 const Eigen::VectorXi &discr_ordersp,
const Eigen::VectorXi &discr_ordersq,
232 return discr_ordersp(cid);
234 const auto pv = prism_vertices_local_to_global(mesh, cid);
236 Eigen::Matrix<int, 9, 2> pev;
237 pev.row(0) << pv[0], pv[1];
238 pev.row(1) << pv[1], pv[2];
239 pev.row(2) << pv[2], pv[0];
240 pev.row(3) << pv[3], pv[4];
241 pev.row(4) << pv[4], pv[5];
242 pev.row(5) << pv[5], pv[3];
243 pev.row(6) << pv[0], pv[3];
244 pev.row(7) << pv[1], pv[4];
245 pev.row(8) << pv[2], pv[5];
247 for (
int le = 0; le < 9; ++le)
251 return le < 6 ? discr_ordersp(cid) : discr_ordersq(cid);
255 return discr_ordersp(cid);
258 int lowest_order_elem_on_edge(
const polyfem::mesh::NCMesh3D &mesh,
const Eigen::VectorXi &discr_orders,
const int eid)
261 int min = std::numeric_limits<int>::max();
263 for (
const auto e : elem_list)
264 if (discr_orders[
e] < min)
269 void tet_local_to_global(
const bool is_geom_bases,
const int p,
const Mesh3D &mesh,
int c,
const Eigen::VectorXi &discr_order,
const Eigen::VectorXi &edge_orders,
const Eigen::VectorXi &face_orders, std::vector<int> &res,
polyfem::mesh::MeshNodes &nodes, std::vector<std::vector<int>> &edge_virtual_nodes, std::vector<std::vector<int>> &face_virtual_nodes)
271 const int n_edge_nodes = p > 1 ? ((p - 1) * 6) : 0;
272 const int nn = p > 2 ? (p - 2) : 0;
273 const int n_loc_f = (
nn * (
nn + 1) / 2);
274 const int n_face_nodes = n_loc_f * 4;
275 int n_cell_nodes = 0;
276 for (
int pp = 4; pp <= p; ++pp)
277 n_cell_nodes += ((pp - 3) * ((pp - 3) + 1) / 2);
281 res.push_back(
nodes.node_id_from_cell(c));
286 res.reserve(4 + n_edge_nodes + n_face_nodes + n_cell_nodes);
289 Eigen::Matrix<Navigation3D::Index, 4, 1>
f;
291 auto v = tet_vertices_local_to_global(mesh, c);
292 Eigen::Matrix<int, 4, 3> fv;
293 fv.row(0) << v[0], v[1], v[2];
294 fv.row(1) << v[0], v[1], v[3];
295 fv.row(2) << v[1], v[2], v[3];
296 fv.row(3) << v[2], v[0], v[3];
298 for (
long lf = 0; lf < fv.rows(); ++lf)
304 Eigen::Matrix<Navigation3D::Index, 6, 1>
e;
305 Eigen::Matrix<int, 6, 2> ev;
306 ev.row(0) << v[0], v[1];
307 ev.row(1) << v[1], v[2];
308 ev.row(2) << v[2], v[0];
310 ev.row(3) << v[0], v[3];
311 ev.row(4) << v[1], v[3];
312 ev.row(5) << v[2], v[3];
314 for (
int le = 0; le <
e.rows(); ++le)
322 for (
size_t lv = 0; lv < v.size(); ++lv)
326 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
328 if (ncmesh.leader_edge_of_vertex(v[lv]) >= 0 || ncmesh.leader_face_of_vertex(v[lv]) >= 0)
329 res.push_back(-lv - 1);
331 res.push_back(
nodes.node_id_from_primitive(v[lv]));
334 res.push_back(
nodes.node_id_from_primitive(v[lv]));
338 for (
int le = 0; le <
e.rows(); ++le)
340 const auto index =
e[le];
342 int min_p = discr_order.size() > 0 ? discr_order(c) : 0;
346 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
347 res.insert(res.end(), node_ids.begin(), node_ids.end());
353 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
355 if (ncmesh.leader_edge_of_edge(index.edge) >= 0 || ncmesh.leader_face_of_edge(index.edge) >= 0)
357 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
358 res.push_back(-le - 1);
361 else if (edge_orders[index.edge] < discr_order(c))
363 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
364 res.push_back(-le - 1);
366 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_order, index.edge);
368 if (min_order_elem == c)
369 edge_virtual_nodes[index.edge] =
nodes.node_ids_from_edge(index, edge_orders[index.edge] - 1);
373 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
374 res.insert(res.end(), node_ids.begin(), node_ids.end());
379 for (
auto cid : neighs)
381 min_p = std::min(min_p, discr_order.size() > 0 ? discr_order(cid) : 0);
384 if (discr_order.size() > 0 && discr_order(c) > min_p)
386 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
387 res.push_back(-le - 10);
391 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
392 res.insert(res.end(), node_ids.begin(), node_ids.end());
399 for (
int lf = 0; lf <
f.rows(); ++lf)
401 const auto index =
f[lf];
404 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
408 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
409 res.insert(res.end(), node_ids.begin(), node_ids.end());
415 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
417 if (ncmesh.leader_face_of_face(index.face) >= 0)
419 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
420 res.push_back(-lf - 1);
423 else if (face_orders[index.face] < discr_order[c])
425 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
426 res.push_back(-lf - 1);
428 if (ncmesh.n_follower_faces(index.face) > 0 && face_orders[index.face] > 2)
429 face_virtual_nodes[index.face] =
nodes.node_ids_from_face(index, face_orders[index.face] - 2);
433 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
434 res.insert(res.end(), node_ids.begin(), node_ids.end());
441 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
442 res.push_back(-lf - 1);
446 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
447 res.insert(res.end(), node_ids.begin(), node_ids.end());
454 if (n_cell_nodes > 0)
456 const auto index =
f[0];
458 auto node_ids =
nodes.node_ids_from_cell(index, p - 3);
459 res.insert(res.end(), node_ids.begin(), node_ids.end());
462 assert(res.size() ==
size_t(4 + n_edge_nodes + n_face_nodes + n_cell_nodes));
465 void hex_local_to_global(
const bool serendipity,
const int q,
const Mesh3D &mesh,
int c,
const Eigen::VectorXi &discr_order, std::vector<int> &res,
MeshNodes &nodes)
469 const int n_edge_nodes = ((q - 1) * 12);
470 const int nn = (q - 1);
471 const int n_loc_f = serendipity ? 0 : (
nn *
nn);
472 const int n_face_nodes = serendipity ? 0 : (n_loc_f * 6);
473 const int n_cell_nodes = serendipity ? 0 : (
nn *
nn *
nn);
477 res.push_back(
nodes.node_id_from_cell(c));
482 res.reserve(8 + n_edge_nodes + n_face_nodes + n_cell_nodes);
485 auto v = hex_vertices_local_to_global(mesh, c);
488 Eigen::Matrix<Navigation3D::Index, 12, 1>
e;
489 Eigen::Matrix<int, 12, 2> ev;
490 ev.row(0) << v[0], v[1];
491 ev.row(1) << v[1], v[2];
492 ev.row(2) << v[2], v[3];
493 ev.row(3) << v[3], v[0];
494 ev.row(4) << v[0], v[4];
495 ev.row(5) << v[1], v[5];
496 ev.row(6) << v[2], v[6];
497 ev.row(7) << v[3], v[7];
498 ev.row(8) << v[4], v[5];
499 ev.row(9) << v[5], v[6];
500 ev.row(10) << v[6], v[7];
501 ev.row(11) << v[7], v[4];
502 for (
int le = 0; le <
e.rows(); ++le)
509 Eigen::Matrix<Navigation3D::Index, 6, 1>
f;
510 Eigen::Matrix<int, 6, 4> fv;
511 fv.row(0) << v[0], v[3], v[4], v[7];
512 fv.row(1) << v[1], v[2], v[5], v[6];
513 fv.row(2) << v[0], v[1], v[5], v[4];
514 fv.row(3) << v[3], v[2], v[6], v[7];
515 fv.row(4) << v[0], v[1], v[2], v[3];
516 fv.row(5) << v[4], v[5], v[6], v[7];
517 for (
int lf = 0; lf <
f.rows(); ++lf)
519 const auto index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
524 for (
size_t lv = 0; lv < v.size(); ++lv)
526 res.push_back(
nodes.node_id_from_primitive(v[lv]));
528 assert(res.size() ==
size_t(8));
531 for (
int le = 0; le <
e.rows(); ++le)
533 const auto index =
e[le];
535 int min_q = discr_order.size() > 0 ? discr_order(c) : 0;
537 for (
auto cid : neighs)
539 min_q = std::min(min_q, discr_order.size() > 0 ? discr_order(cid) : 0);
542 if (discr_order.size() > 0 && discr_order(c) > min_q)
544 for (
int tmp = 0;
tmp < q - 1; ++
tmp)
545 res.push_back(-le - 10);
549 auto node_ids =
nodes.node_ids_from_edge(index, q - 1);
550 res.insert(res.end(), node_ids.begin(), node_ids.end());
553 assert(res.size() ==
size_t(8 + n_edge_nodes));
556 for (
int lf = 0; lf <
f.rows(); ++lf)
558 const auto index =
f[lf];
561 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
565 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
566 res.push_back(-lf - 1);
570 auto node_ids =
nodes.node_ids_from_face(index, serendipity ? 0 : (q - 1));
571 assert(node_ids.size() == n_loc_f);
572 res.insert(res.end(), node_ids.begin(), node_ids.end());
575 assert(res.size() ==
size_t(8 + n_edge_nodes + n_face_nodes));
578 if (n_cell_nodes > 0)
580 const auto index =
f[0];
582 auto node_ids =
nodes.node_ids_from_cell(index, q - 1);
583 res.insert(res.end(), node_ids.begin(), node_ids.end());
586 assert(res.size() ==
size_t(8 + n_edge_nodes + n_face_nodes + n_cell_nodes));
589 void prism_local_to_global(
const int p,
const int q,
const Mesh3D &mesh,
int c,
const Eigen::VectorXi &discr_order, std::vector<int> &res,
MeshNodes &nodes)
593 const int n_edge_nodest = p > 1 ? ((p - 1) * 3) : 0;
594 const int nnt = p > 2 ? (p - 2) : 0;
595 const int n_face_nodest = nnt * (nnt + 1) / 2;
597 const int nnq = q > 1 ? (q - 1) : 0;
598 const int n_face_nodesq = nnq * n_edge_nodest / 3;
600 const int n_edge_nodes = n_edge_nodest * 2 + nnq * 3;
601 const int n_face_nodes = n_face_nodest * 2 + n_face_nodesq * 3;
602 const int n_cell_nodes = n_face_nodest * nnq;
604 if (p == 0 && q == 0)
606 res.push_back(
nodes.node_id_from_cell(c));
610 res.reserve(6 + n_edge_nodes + n_face_nodes + n_cell_nodes);
613 auto v = prism_vertices_local_to_global(mesh, c);
616 Eigen::Matrix<Navigation3D::Index, 9, 1>
e;
617 Eigen::Matrix<int, 9, 2> ev;
618 ev.row(0) << v[0], v[1];
619 ev.row(1) << v[1], v[2];
620 ev.row(2) << v[2], v[0];
621 ev.row(3) << v[3], v[4];
622 ev.row(4) << v[4], v[5];
623 ev.row(5) << v[5], v[3];
624 ev.row(6) << v[0], v[3];
625 ev.row(7) << v[1], v[4];
626 ev.row(8) << v[2], v[5];
628 for (
int le = 0; le <
e.rows(); ++le)
634 Eigen::Matrix<Navigation3D::Index, 5, 1>
f;
635 Eigen::Matrix<int, 2, 3> fvt;
636 fvt.row(0) << v[0], v[1], v[2];
637 fvt.row(1) << v[3], v[4], v[5];
638 for (
int lf = 0; lf < fvt.rows(); ++lf)
644 Eigen::Matrix<int, 3, 4> fvq;
645 fvq.row(0) << v[0], v[1], v[4], v[3];
646 fvq.row(1) << v[1], v[2], v[5], v[4];
647 fvq.row(2) << v[2], v[0], v[3], v[5];
648 for (
int lf = 0; lf < fvq.rows(); ++lf)
650 const auto index = find_quad_face(mesh, c, fvq(lf, 0), fvq(lf, 1), fvq(lf, 2), fvq(lf, 3));
655 for (
size_t lv = 0; lv < v.size(); ++lv)
657 res.push_back(
nodes.node_id_from_primitive(v[lv]));
659 assert(res.size() ==
size_t(6));
662 for (
int le = 0; le <
e.rows(); ++le)
664 const auto index =
e[le];
667 auto node_ids =
nodes.node_ids_from_edge(index, (le < 6 ? p : q) - 1);
668 res.insert(res.end(), node_ids.begin(), node_ids.end());
670 assert(res.size() ==
size_t(6 + n_edge_nodes));
673 for (
int lf = 0; lf <
f.rows(); ++lf)
675 const auto index =
f[lf];
678 auto node_ids =
nodes.node_ids_from_face(index, lf < 2 ? (p - 2) : (p - 1), lf < 2 ? -1 : (q - 1));
679 assert((lf < 2 && node_ids.size() == n_face_nodest) || (lf >= 2 && node_ids.size() == n_face_nodesq));
681 res.insert(res.end(), node_ids.begin(), node_ids.end());
683 assert(res.size() ==
size_t(6 + n_edge_nodes + n_face_nodes));
686 if (n_cell_nodes > 0)
688 const auto index =
f[0];
690 auto node_ids =
nodes.node_ids_from_cell(index, q - 1);
691 res.insert(res.end(), node_ids.begin(), node_ids.end());
698 Eigen::MatrixXd local_nodes;
700 assert(local_nodes.rows() == (
int)res.size());
702 auto map_ref_to_phys = [&mesh, &v](
const Eigen::RowVector3d &uvw) -> Eigen::RowVector3d {
703 const double u = uvw(0);
704 const double vv = uvw(1);
705 const double w = uvw(2);
707 const double N0 = (1.0 - u -
vv) * (1.0 - w);
708 const double N1 = u * (1.0 - w);
709 const double N2 =
vv * (1.0 - w);
710 const double N3 = (1.0 - u -
vv) * w;
711 const double N4 = u * w;
712 const double N5 =
vv * w;
714 return N0 * mesh.
point(v[0]) + N1 * mesh.
point(v[1]) + N2 * mesh.
point(v[2]) + N3 * mesh.
point(v[3]) + N4 * mesh.
point(v[4]) + N5 * mesh.
point(v[5]);
717 std::vector<int> reordered(res.size(), -1);
718 std::vector<bool> used(res.size(),
false);
720 for (
int i = 0; i < local_nodes.rows(); ++i)
722 const Eigen::RowVector3d target = map_ref_to_phys(local_nodes.row(i));
723 double best = std::numeric_limits<double>::infinity();
725 for (
int j = 0; j < (int)res.size(); ++j)
729 const double d2 = (
nodes.node_position(res[j]) - target).squaredNorm();
737 reordered[i] = res[best_j];
744 assert(res.size() ==
size_t(6 + n_edge_nodes + n_face_nodes + n_cell_nodes));
747 void pyramid_local_to_global(
const bool is_geom_bases,
const int p,
const Mesh3D &mesh,
int c,
const Eigen::VectorXi &discr_order,
const Eigen::VectorXi &discr_ordersq, std::vector<int> &res,
MeshNodes &nodes)
755 res.push_back(
nodes.node_id_from_cell(c));
760 const int n_edge_nodes = 8 * (p - 1);
762 const int n_tri_face_nodes = 4 * (p - 1) * (p - 2) / 2;
764 const int n_quad_face_nodes = (p - 1) * (p - 1);
765 const int n_face_nodes = n_tri_face_nodes + n_quad_face_nodes;
767 const int total = (p + 1) * (p + 2) * (2 * p + 3) / 6;
768 const int n_cell_nodes = (p - 1) * (p - 2) * (2 * p - 3) / 6;
770 assert(total == 5 + n_edge_nodes + n_face_nodes + n_cell_nodes);
772 res.reserve(5 + n_edge_nodes + n_face_nodes + n_cell_nodes);
775 auto v = pyramid_vertices_local_to_global(mesh, c);
778 Eigen::Matrix<Navigation3D::Index, 8, 1>
e;
779 Eigen::Matrix<int, 8, 2> ev;
780 ev.row(0) << v[0], v[1];
781 ev.row(1) << v[1], v[2];
782 ev.row(2) << v[2], v[3];
783 ev.row(3) << v[3], v[0];
784 ev.row(4) << v[0], v[4];
785 ev.row(5) << v[1], v[4];
786 ev.row(6) << v[2], v[4];
787 ev.row(7) << v[3], v[4];
789 for (
int le = 0; le <
e.rows(); ++le)
795 Eigen::Matrix<Navigation3D::Index, 5, 1>
f;
796 Eigen::Matrix<int, 4, 3> fvt;
797 fvt.row(0) << v[0], v[1], v[4];
798 fvt.row(1) << v[1], v[2], v[4];
799 fvt.row(2) << v[2], v[3], v[4];
800 fvt.row(3) << v[3], v[0], v[4];
801 for (
int lf = 0; lf < fvt.rows(); ++lf)
818 auto base_idx = find_quad_face(mesh, c, v[0], v[1], v[2], v[3]);
819 for (
int rv = 0; rv < 4; ++rv)
821 if (base_idx.vertex == v[0])
825 assert(base_idx.vertex == v[0]);
830 for (
size_t lv = 0; lv < v.size(); ++lv)
832 res.push_back(
nodes.node_id_from_primitive(v[lv]));
834 assert(res.size() ==
size_t(5));
838 for (
int le = 0; le <
e.rows(); ++le)
840 const auto index =
e[le];
843 int min_p = discr_order.size() > 0 ? discr_order(c) : 0;
845 for (
auto cid : neighs)
846 min_p = std::min(min_p, prism_edge_order(cid, index.edge, discr_order, discr_ordersq, mesh));
848 if (!is_geom_bases && discr_order.size() > 0 && discr_order(c) > min_p)
850 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
851 res.push_back(-le - 10);
855 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
856 res.insert(res.end(), node_ids.begin(), node_ids.end());
859 assert(res.size() ==
size_t(5 + n_edge_nodes));
862 for (
int lf = 0; lf <
f.rows(); ++lf)
864 const auto index =
f[lf];
866 const bool is_tri_face = lf < 4;
868 const bool skip_other =
869 other_cell >= 0 && (discr_order(c) > discr_order(other_cell) || (!is_tri_face && mesh.
is_prism(other_cell) && discr_order(c) > discr_ordersq(other_cell)));
871 if (!is_geom_bases && skip_other)
873 const int nn = is_tri_face ? (p > 2 ? (p - 2) : 0) : (p - 1);
874 const int n_loc_face = is_tri_face ? (
nn * (
nn + 1) / 2) : (
nn *
nn);
876 for (
int tmp = 0;
tmp < n_loc_face; ++
tmp)
877 res.push_back(-lf - 1);
882 const int n_loc_face = is_tri_face ? (p > 2 ? (p - 2) : 0) : (p - 1);
884 auto node_ids =
nodes.node_ids_from_face(index, n_loc_face);
885 res.insert(res.end(), node_ids.begin(), node_ids.end());
888 assert(res.size() ==
size_t(5 + n_edge_nodes + n_face_nodes));
891 if (n_cell_nodes > 0)
893 auto node_ids =
nodes.node_ids_from_cell(f[0], p - 1);
894 res.insert(res.end(), node_ids.begin(), node_ids.end());
896 assert(res.size() ==
size_t(5 + n_edge_nodes + n_face_nodes + n_cell_nodes));
915 const Eigen::VectorXi &discr_ordersp,
916 const Eigen::VectorXi &discr_ordersq,
917 const Eigen::VectorXi &edge_orders,
918 const Eigen::VectorXi &face_orders,
919 const bool serendipity,
920 const bool has_polys,
921 const bool is_geom_bases,
923 std::vector<std::vector<int>> &edge_virtual_nodes,
924 std::vector<std::vector<int>> &face_virtual_nodes,
925 std::vector<std::vector<int>> &element_nodes_id,
926 std::vector<LocalBoundary> &local_boundary,
927 std::map<int, InterfaceData> &poly_face_to_data)
930 local_boundary.clear();
932 element_nodes_id.resize(mesh.
n_faces());
936 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
937 edge_virtual_nodes.resize(ncmesh.n_edges());
938 face_virtual_nodes.resize(ncmesh.n_faces());
941 for (
int c = 0; c < mesh.
n_cells(); ++c)
943 const int discr_order = discr_ordersp(c);
944 const int discr_orderq = discr_ordersq(c);
948 hex_local_to_global(serendipity, discr_order, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
950 auto v = hex_vertices_local_to_global(mesh, c);
951 Eigen::Matrix<int, 6, 4> fv;
952 fv.row(0) << v[0], v[3], v[4], v[7];
953 fv.row(1) << v[1], v[2], v[5], v[6];
954 fv.row(2) << v[0], v[1], v[5], v[4];
955 fv.row(3) << v[3], v[2], v[6], v[7];
956 fv.row(4) << v[0], v[1], v[2], v[3];
957 fv.row(5) << v[4], v[5], v[6], v[7];
960 for (
int i = 0; i < fv.rows(); ++i)
962 const int f = find_quad_face(mesh, c, fv(i, 0), fv(i, 1), fv(i, 2), fv(i, 3)).face;
966 lb.add_boundary_primitive(f, i);
971 local_boundary.emplace_back(lb);
976 tet_local_to_global(is_geom_bases, discr_order, mesh, c, discr_ordersp, edge_orders, face_orders, element_nodes_id[c], nodes, edge_virtual_nodes, face_virtual_nodes);
978 auto v = tet_vertices_local_to_global(mesh, c);
979 Eigen::Matrix<int, 4, 3> fv;
980 fv.row(0) << v[0], v[1], v[2];
981 fv.row(1) << v[0], v[1], v[3];
982 fv.row(2) << v[1], v[2], v[3];
983 fv.row(3) << v[2], v[0], v[3];
986 for (
long i = 0; i < fv.rows(); ++i)
992 lb.add_boundary_primitive(f, i);
997 local_boundary.emplace_back(lb);
1002 prism_local_to_global(discr_order, discr_orderq, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
1004 auto v = prism_vertices_local_to_global(mesh, c);
1005 Eigen::Matrix<int, 2, 3> fvt;
1006 fvt.row(0) << v[0], v[1], v[2];
1007 fvt.row(1) << v[3], v[4], v[5];
1010 for (
long i = 0; i < fvt.rows(); ++i)
1016 lb.add_boundary_primitive(f, i);
1020 Eigen::Matrix<int, 3, 4> fvq;
1021 fvq.row(0) << v[0], v[1], v[4], v[3];
1022 fvq.row(1) << v[1], v[2], v[5], v[4];
1023 fvq.row(2) << v[2], v[0], v[3], v[5];
1025 for (
long i = 0; i < fvq.rows(); ++i)
1027 const int f = find_quad_face(mesh, c, fvq(i, 0), fvq(i, 1), fvq(i, 2), fvq(i, 3)).face;
1031 lb.add_boundary_primitive(f, i + 2);
1036 local_boundary.emplace_back(lb);
1042 pyramid_local_to_global(is_geom_bases, discr_order, mesh, c, discr_ordersp, discr_ordersq, element_nodes_id[c], nodes);
1044 auto v = pyramid_vertices_local_to_global(mesh, c);
1045 Eigen::Matrix<int, 4, 3> fvt;
1046 fvt.row(0) << v[0], v[1], v[4];
1047 fvt.row(1) << v[1], v[2], v[4];
1048 fvt.row(2) << v[2], v[3], v[4];
1049 fvt.row(3) << v[3], v[0], v[4];
1052 for (
long i = 0; i < fvt.rows(); ++i)
1058 lb.add_boundary_primitive(f, i + 1);
1062 Eigen::Matrix<int, 1, 4> fvq;
1063 fvq.row(0) << v[0], v[1], v[2], v[3];
1065 for (
long i = 0; i < fvq.rows(); ++i)
1067 const int f = find_quad_face(mesh, c, fvq(i, 0), fvq(i, 1), fvq(i, 2), fvq(i, 3)).face;
1071 lb.add_boundary_primitive(f, 0);
1076 local_boundary.emplace_back(lb);
1085 for (
int c = 0; c < mesh.
n_cells(); ++c)
1097 int c2 = index2.element;
1100 const int discr_order = discr_ordersp(c2);
1101 const int discr_orderq = discr_ordersq(c2);
1124 poly_face_to_data[index2.face] = data;
1134 void local_to_global(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
1136 const int dim = verts.cols();
1137 const int N = uv.rows();
1139 assert(uv.cols() == dim);
1140 assert(verts.rows() == dim + 1);
1142 pts.setZero(N, dim);
1143 for (
int i = 0; i <
N; i++)
1144 pts.row(i) = uv(i, 0) * verts.row(1) + uv(i, 1) * verts.row(2) + uv(i, 2) * verts.row(3) + (1.0 - uv(i, 0) - uv(i, 1) - uv(i, 2)) * verts.row(0);
1147 void local_to_global_face(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
1149 const int dim = verts.cols();
1150 const int N = uv.rows();
1152 assert(uv.cols() == 2);
1153 assert(verts.rows() == 3);
1155 pts.setZero(N, dim);
1156 for (
int i = 0; i <
N; i++)
1157 pts.row(i) = uv(i, 0) * verts.row(1) + uv(i, 1) * verts.row(2) + (1.0 - uv(i, 0) - uv(i, 1)) * verts.row(0);
1166 void global_to_local(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
1168 const int dim = verts.cols();
1169 const int N = pts.rows();
1171 assert(verts.rows() == dim + 1);
1172 assert(pts.cols() == dim);
1175 for (
int i = 0; i <
dim; i++)
1176 J.col(i) = verts.row(i + 1) - verts.row(0);
1178 Eigen::Matrix3d Jinv =
J.inverse();
1182 for (
int i = start; i < end; i++)
1184 auto point = pts.row(i) - verts.row(0);
1185 uv.row(i) = Jinv *
point.transpose();
1190 void global_to_local_face(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
1192 const int dim = verts.cols();
1193 const int N = pts.rows();
1195 assert(verts.rows() == 3);
1196 assert(pts.cols() == dim);
1199 for (
int i = 0; i < 2; i++)
1200 J.col(i) = verts.row(i + 1) - verts.row(0);
1202 Eigen::Vector3d a =
J.col(0);
1203 Eigen::Vector3d
b =
J.col(1);
1204 Eigen::Vector3d virtual_vert = a.cross(b);
1205 J.col(2) = virtual_vert;
1209 for (
int i = start; i < end; i++)
1211 auto point = pts.row(i) - verts.row(0);
1212 Eigen::Vector3d
x =
J.colPivHouseholderQr().solve(
point.transpose());
1213 uv.row(i) =
x.block(0, 0, 2, 1);
1214 assert(std::abs(
x(2)) < 1e-8);
1219 void global_to_local_edge(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::VectorXd &uv)
1221 const int dim = verts.cols();
1222 const int N = pts.rows();
1224 assert(verts.rows() == 2);
1225 assert(pts.cols() == dim);
1227 auto edge = verts.row(1) - verts.row(0);
1228 double squared_length = edge.squaredNorm();
1232 for (
int i = start; i < end; i++)
1234 auto vec = pts.row(i) - verts.row(0);
1235 uv(i) = (
vec.dot(edge)) / squared_length;
1240 bool check_edge_face_orders(
const polyfem::mesh::NCMesh3D &mesh,
const Eigen::VectorXi &elem_orders,
const Eigen::VectorXi &edge_orders,
const Eigen::VectorXi &face_orders)
1243 for (
int i = 0; i < mesh.
n_faces(); i++)
1249 for (
int i = 0; i < mesh.
n_faces(); i++)
1256 if (edge_orders[e_id] > face_orders[i])
1262 for (
int i = 0; i < mesh.
n_edges(); i++)
1268 for (
int i = 0; i < mesh.
n_edges(); i++)
1286 void compute_edge_face_orders(
const polyfem::mesh::NCMesh3D &mesh,
const Eigen::VectorXi &elem_orders, Eigen::VectorXi &edge_orders, Eigen::VectorXi &face_orders)
1288 const int max_order = elem_orders.maxCoeff();
1289 edge_orders.setConstant(mesh.
n_edges(), max_order);
1290 face_orders.setConstant(mesh.
n_faces(), max_order);
1292 for (
int i = 0; i < mesh.
n_cells(); i++)
1294 face_orders[mesh.
cell_face(i, j)] = std::min(face_orders[mesh.
cell_face(i, j)], elem_orders[i]);
1296 for (
int i = 0; i < mesh.
n_cells(); i++)
1298 edge_orders[mesh.
cell_edge(i, j)] = std::min(edge_orders[mesh.
cell_edge(i, j)], elem_orders[i]);
1300 while (!check_edge_face_orders(mesh, elem_orders, edge_orders, face_orders))
1303 for (
int i = 0; i < mesh.
n_faces(); i++)
1307 for (
int i = 0; i < mesh.
n_faces(); i++)
1312 for (
int i = 0; i < mesh.
n_faces(); i++)
1319 edge_orders[e_id] = std::min(edge_orders[e_id], face_orders[i]);
1324 for (
int i = 0; i < mesh.
n_edges(); i++)
1328 for (
int i = 0; i < mesh.
n_edges(); i++)
1333 for (
int i = 0; i < mesh.
n_edges(); i++)
1346 const int nn = p > 2 ? (p - 2) : 0;
1347 const int n_edge_nodes = (p - 1) * 6;
1348 const int n_face_nodes = nn * (nn + 1) / 2;
1354 const auto l2g = tet_vertices_local_to_global(mesh, c);
1357 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
1358 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1362 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
1363 Eigen::Matrix<int, 6, 2> ev;
1364 ev.row(0) << l2g[0], l2g[1];
1365 ev.row(1) << l2g[1], l2g[2];
1366 ev.row(2) << l2g[2], l2g[0];
1368 ev.row(3) << l2g[0], l2g[3];
1369 ev.row(4) << l2g[1], l2g[3];
1370 ev.row(5) << l2g[2], l2g[3];
1374 for (
int le = 0; le < e.rows(); ++le)
1382 for (
int k = 0; k < 3; ++k)
1384 bool reverse =
false;
1386 for (; le < ev.rows(); ++le)
1390 const auto l_index = e[le];
1391 if (l_index.edge == tmp.edge)
1393 if (l_index.vertex == tmp.vertex)
1410 for (
int i = 0; i < p - 1; ++i)
1412 result[ii++] = 4 + le * (p - 1) + i;
1417 for (
int i = 0; i < p - 1; ++i)
1419 result[ii++] = 4 + (le + 1) * (p - 1) - i - 1;
1428 Eigen::Matrix<int, 4, 3> fv;
1429 fv.row(0) << l2g[0], l2g[1], l2g[2];
1430 fv.row(1) << l2g[0], l2g[1], l2g[3];
1431 fv.row(2) << l2g[1], l2g[2], l2g[3];
1432 fv.row(3) << l2g[2], l2g[0], l2g[3];
1435 for (; lf < fv.rows(); ++lf)
1438 if (l_index.face == index.
face)
1442 assert(lf < fv.rows());
1444 if (n_face_nodes == 0)
1447 else if (n_face_nodes == 1)
1448 result[ii++] = 4 + n_edge_nodes + lf;
1452 const auto get_order = [&p, &nn, &n_face_nodes](
const std::array<int, 3> &corners) {
1457 std::vector<int> order1(n_face_nodes);
1458 for (
int k = 0; k < n_face_nodes; ++k)
1461 std::vector<int> order2(n_face_nodes);
1464 for (
int k = 0; k < nn; ++k)
1466 for (
int l = 0; l < nn - k; ++l)
1468 order2[index] = start - l;
1471 start += (nn - 1) - k;
1474 std::vector<int> order3(n_face_nodes);
1476 for (
int k = 0; k < nn; ++k)
1479 for (
int l = 0; l < nn - k; ++l)
1481 order3[index] = offset;
1488 std::vector<int> order4(n_face_nodes);
1490 start = n_face_nodes - 1;
1491 for (
int k = 0; k < nn; ++k)
1494 for (
int l = 0; l < nn - k; ++l)
1496 order4[index] = start - offset;
1497 offset += k + 2 + l;
1504 std::vector<int> order5(n_face_nodes);
1507 for (
int k = 0; k < nn; ++k)
1510 for (
int l = 0; l < nn - k; ++l)
1512 order5[index] = start + offset;
1513 offset += nn - 1 - l;
1520 std::vector<int> order6(n_face_nodes);
1522 start = n_face_nodes;
1523 for (
int k = 0; k < nn; ++k)
1526 start = start - k - 1;
1527 for (
int l = 0; l < nn - k; ++l)
1529 order6[index] = start - offset;
1530 offset += l + 1 + k;
1535 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
1537 assert(corners[2] == order1[n_face_nodes - 1]);
1541 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
1543 assert(corners[2] == order2[n_face_nodes - 1]);
1547 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
1549 assert(corners[2] == order3[n_face_nodes - 1]);
1553 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
1555 assert(corners[2] == order4[n_face_nodes - 1]);
1559 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
1561 assert(corners[2] == order5[n_face_nodes - 1]);
1565 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
1567 assert(corners[2] == order6[n_face_nodes - 1]);
1575 Eigen::MatrixXd nodes;
1581 std::array<int, 3> idx;
1582 for (
int lv = 0; lv < 3; ++lv)
1584 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1587 Eigen::Matrix3d pos(3, 3);
1591 pos.row(cnt++) = nodes.row(i);
1594 const Eigen::RowVector3d bary = pos.colwise().mean();
1596 const int offset = 4 + n_edge_nodes;
1598 for (
int lff = 0; lff < 4; ++lff)
1600 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
1601 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1603 if ((node_bary - bary).norm() < 1e-10)
1605 std::array<int, 3> corners;
1607 for (
int m = 0; m < 3; ++m)
1609 auto t = pos.row(m);
1611 double min_dis = 10000;
1613 for (
int n = 0; n < n_face_nodes; ++n)
1615 double dis = (loc_nodes.row(n) - t).squaredNorm();
1624 assert(min_n < n_face_nodes);
1628 const auto indices = get_order(corners);
1629 for (
int min_n : indices)
1632 result[ii++] = 4 + n_edge_nodes + min_n + lf * n_face_nodes;
1635 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
1651 assert(ii == result.size());
1657 const int nn = q - 1;
1658 const int n_edge_nodes = nn * 12;
1659 const int n_face_nodes = serendipity ? 0 : nn * nn;
1665 const auto l2g = hex_vertices_local_to_global(mesh, c);
1668 Eigen::VectorXi result(4 + nn * 4 + n_face_nodes);
1669 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1674 Eigen::Matrix<Navigation3D::Index, 12, 1> e;
1675 Eigen::Matrix<int, 12, 2> ev;
1676 ev.row(0) << l2g[0], l2g[1];
1677 ev.row(1) << l2g[1], l2g[2];
1678 ev.row(2) << l2g[2], l2g[3];
1679 ev.row(3) << l2g[3], l2g[0];
1680 ev.row(4) << l2g[0], l2g[4];
1681 ev.row(5) << l2g[1], l2g[5];
1682 ev.row(6) << l2g[2], l2g[6];
1683 ev.row(7) << l2g[3], l2g[7];
1684 ev.row(8) << l2g[4], l2g[5];
1685 ev.row(9) << l2g[5], l2g[6];
1686 ev.row(10) << l2g[6], l2g[7];
1687 ev.row(11) << l2g[7], l2g[4];
1691 for (
int le = 0; le < e.rows(); ++le)
1698 for (
int k = 0; k < 4; ++k)
1700 bool reverse =
false;
1702 for (; le < ev.rows(); ++le)
1706 const auto l_index = e[le];
1707 if (l_index.edge == tmp.edge)
1709 if (l_index.vertex == tmp.vertex)
1725 for (
int i = 0; i < q - 1; ++i)
1727 result[ii++] = 8 + le * (q - 1) + i;
1732 for (
int i = 0; i < q - 1; ++i)
1734 result[ii++] = 8 + (le + 1) * (q - 1) - i - 1;
1743 Eigen::Matrix<int, 6, 4> fv;
1744 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[7];
1745 fv.row(1) << l2g[1], l2g[2], l2g[5], l2g[6];
1746 fv.row(2) << l2g[0], l2g[1], l2g[5], l2g[4];
1747 fv.row(3) << l2g[3], l2g[2], l2g[6], l2g[7];
1748 fv.row(4) << l2g[0], l2g[1], l2g[2], l2g[3];
1749 fv.row(5) << l2g[4], l2g[5], l2g[6], l2g[7];
1752 for (; lf < fv.rows(); ++lf)
1754 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
1755 if (l_index.face == index.
face)
1759 assert(lf < fv.rows());
1761 if (n_face_nodes == 1)
1762 result[ii++] = 8 + n_edge_nodes + lf;
1763 else if (n_face_nodes != 0)
1765 Eigen::MatrixXd nodes;
1771 std::array<int, 4> idx;
1772 for (
int lv = 0; lv < 4; ++lv)
1774 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1777 Eigen::Matrix<double, 4, 3> pos(4, 3);
1781 pos.row(cnt++) = nodes.row(i);
1784 const Eigen::RowVector3d bary = pos.colwise().mean();
1786 const int offset = 8 + n_edge_nodes;
1788 for (
int lff = 0; lff < 6; ++lff)
1790 Eigen::Matrix<double, 4, 3> loc_nodes = nodes.block<4, 3>(offset + lff * n_face_nodes, 0);
1791 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1793 if ((node_bary - bary).norm() < 1e-10)
1796 for (
int m = 0; m < 4; ++m)
1798 auto t = pos.row(m);
1800 double min_dis = 10000;
1802 for (
int n = 0; n < 4; ++n)
1804 double dis = (loc_nodes.row(n) - t).squaredNorm();
1817 result[ii++] = 8 + n_edge_nodes + min_n + lf * n_face_nodes;
1833 assert(ii == result.size());
1843 const auto l2g = prism_vertices_local_to_global(mesh, c);
1844 const int global_n_edges_nodes = (p - 1) * 6 + (q - 1) * 3;
1848 const int nn = p > 2 ? (p - 2) : 0;
1849 const int n_face_nodes = nn * (nn + 1) / 2;
1852 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
1853 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1857 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
1858 Eigen::Matrix<int, 6, 2> ev;
1859 ev.row(0) << l2g[0], l2g[1];
1860 ev.row(1) << l2g[1], l2g[2];
1861 ev.row(2) << l2g[2], l2g[0];
1863 ev.row(3) << l2g[3], l2g[4];
1864 ev.row(4) << l2g[4], l2g[5];
1865 ev.row(5) << l2g[5], l2g[3];
1869 for (
int le = 0; le < e.rows(); ++le)
1877 for (
int k = 0; k < 3; ++k)
1879 bool reverse =
false;
1881 for (; le < ev.rows(); ++le)
1885 const auto l_index = e[le];
1886 if (l_index.edge == tmp.edge)
1888 if (l_index.vertex == tmp.vertex)
1905 for (
int i = 0; i < p - 1; ++i)
1907 result[ii++] = 6 + le * (p - 1) + i;
1912 for (
int i = 0; i < p - 1; ++i)
1914 result[ii++] = 6 + (le + 1) * (p - 1) - i - 1;
1923 Eigen::Matrix<int, 2, 3> fv;
1924 fv.row(0) << l2g[0], l2g[1], l2g[2];
1925 fv.row(1) << l2g[3], l2g[4], l2g[5];
1928 for (; lf < fv.rows(); ++lf)
1931 if (l_index.face == index.
face)
1935 assert(lf < fv.rows());
1937 if (n_face_nodes == 0)
1940 else if (n_face_nodes == 1)
1941 result[ii++] = 6 + global_n_edges_nodes + lf;
1945 const auto get_order = [&p, &q, &nn, &n_face_nodes](
const std::array<int, 3> &corners) {
1950 std::vector<int> order1(n_face_nodes);
1951 for (
int k = 0; k < n_face_nodes; ++k)
1954 std::vector<int> order2(n_face_nodes);
1957 for (
int k = 0; k < nn; ++k)
1959 for (
int l = 0; l < nn - k; ++l)
1961 order2[index] = start - l;
1964 start += (nn - 1) - k;
1967 std::vector<int> order3(n_face_nodes);
1969 for (
int k = 0; k < nn; ++k)
1972 for (
int l = 0; l < nn - k; ++l)
1974 order3[index] = offset;
1981 std::vector<int> order4(n_face_nodes);
1983 start = n_face_nodes - 1;
1984 for (
int k = 0; k < nn; ++k)
1987 for (
int l = 0; l < nn - k; ++l)
1989 order4[index] = start - offset;
1990 offset += k + 2 + l;
1997 std::vector<int> order5(n_face_nodes);
2000 for (
int k = 0; k < nn; ++k)
2003 for (
int l = 0; l < nn - k; ++l)
2005 order5[index] = start + offset;
2006 offset += nn - 1 - l;
2013 std::vector<int> order6(n_face_nodes);
2015 start = n_face_nodes;
2016 for (
int k = 0; k < nn; ++k)
2019 start = start - k - 1;
2020 for (
int l = 0; l < nn - k; ++l)
2022 order6[index] = start - offset;
2023 offset += l + 1 + k;
2028 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
2030 assert(corners[2] == order1[n_face_nodes - 1]);
2034 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
2036 assert(corners[2] == order2[n_face_nodes - 1]);
2040 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
2042 assert(corners[2] == order3[n_face_nodes - 1]);
2046 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
2048 assert(corners[2] == order4[n_face_nodes - 1]);
2052 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
2054 assert(corners[2] == order5[n_face_nodes - 1]);
2058 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
2060 assert(corners[2] == order6[n_face_nodes - 1]);
2068 Eigen::MatrixXd nodes;
2074 std::array<int, 3> idx;
2075 for (
int lv = 0; lv < 3; ++lv)
2077 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2080 Eigen::Matrix3d pos(3, 3);
2084 pos.row(cnt++) = nodes.row(i);
2087 const Eigen::RowVector3d bary = pos.colwise().mean();
2089 const int offset = 6 + global_n_edges_nodes;
2091 for (
int lff = 0; lff < 2; ++lff)
2093 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
2094 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
2096 if ((node_bary - bary).norm() < 1e-10)
2098 std::array<int, 3> corners;
2100 for (
int m = 0; m < 3; ++m)
2102 auto t = pos.row(m);
2104 double min_dis = 10000;
2106 for (
int n = 0; n < n_face_nodes; ++n)
2108 double dis = (loc_nodes.row(n) - t).squaredNorm();
2117 assert(min_n < n_face_nodes);
2121 const auto indices = get_order(corners);
2122 for (
int min_n : indices)
2125 result[ii++] = 6 + global_n_edges_nodes + min_n + lf * n_face_nodes;
2128 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
2144 assert(ii == result.size());
2149 const int n_edge_nodes = 2 * (p - 1) + 2 * (q - 1);
2150 const int n_face_nodes = (p - 1) * (q - 1);
2151 const int n_tri_face_nodes = p > 2 ? (p - 2) : 0;
2154 Eigen::VectorXi result(4 + n_edge_nodes + n_face_nodes);
2156 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2162 Eigen::Matrix<Navigation3D::Index, 9, 1> e;
2163 Eigen::Matrix<int, 9, 2> ev;
2164 ev.row(0) << l2g[0], l2g[1];
2165 ev.row(1) << l2g[1], l2g[2];
2166 ev.row(2) << l2g[2], l2g[0];
2168 ev.row(3) << l2g[3], l2g[4];
2169 ev.row(4) << l2g[4], l2g[5];
2170 ev.row(5) << l2g[5], l2g[3];
2172 ev.row(6) << l2g[0], l2g[3];
2173 ev.row(7) << l2g[1], l2g[4];
2174 ev.row(8) << l2g[2], l2g[5];
2178 for (
int le = 0; le < e.rows(); ++le)
2185 for (
int k = 0; k < 4; ++k)
2187 bool reverse =
false;
2189 for (; le < ev.rows(); ++le)
2193 const auto l_index = e[le];
2194 if (l_index.edge == tmp.edge)
2196 if (l_index.vertex == tmp.vertex)
2216 for (
int i = 0; i < p - 1; ++i)
2218 result[ii++] = 6 + le * (p - 1) + i;
2223 for (
int i = 0; i < p - 1; ++i)
2225 result[ii++] = 6 + (le + 1) * (p - 1) - i - 1;
2234 for (
int i = 0; i < q - 1; ++i)
2236 result[ii++] = 6 + 6 * (p - 1) + (le - 6) * (q - 1) + i;
2241 for (
int i = 0; i < q - 1; ++i)
2243 result[ii++] = 6 + 6 * (p - 1) + (le - 5) * (q - 1) - i - 1;
2252 Eigen::Matrix<int, 3, 4> fv;
2253 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[1];
2254 fv.row(1) << l2g[1], l2g[4], l2g[5], l2g[2];
2255 fv.row(2) << l2g[2], l2g[5], l2g[3], l2g[0];
2258 for (; lf < fv.rows(); ++lf)
2260 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
2261 if (l_index.face == index.
face)
2265 assert(lf < fv.rows());
2267 if (n_face_nodes == 1)
2269 result[ii++] = 6 + global_n_edges_nodes + lf;
2271 else if (n_face_nodes == 2)
2273 Eigen::MatrixXd nodes;
2276 std::array<int, 4> idx;
2277 for (
int lv = 0; lv < 4; ++lv)
2279 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2283 Eigen::Matrix<double, 4, 3> pos(4, 3);
2287 pos.row(cnt++) = nodes.row(i);
2290 const Eigen::RowVector3d bary = pos.colwise().mean();
2292 const int offset = 6 + global_n_edges_nodes;
2295 for (
int lff = 0; lff < 3; ++lff)
2297 int start_row = offset + lff * n_face_nodes + 2 * n_tri_face_nodes;
2299 Eigen::MatrixXd loc_nodes = nodes.block(start_row, 0, n_face_nodes, 3);
2300 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
2302 double dist = (node_bary - bary).norm();
2306 auto t = pos.row(0);
2308 double min_dis = 10000;
2309 for (
int n = 0; n < n_face_nodes; ++n)
2311 double dis = (loc_nodes.row(n) - t).squaredNorm();
2320 assert(min_n < n_face_nodes);
2322 int final_idx = 6 + global_n_edges_nodes + min_n + lf * n_face_nodes + 2 * n_tri_face_nodes;
2323 result[ii++] = final_idx;
2325 final_idx = 6 + global_n_edges_nodes + (min_n + 1) % 2 + lf * n_face_nodes + 2 * n_tri_face_nodes;
2326 result[ii++] = final_idx;
2338 else if (n_face_nodes == 4)
2340 assert(p == 3 && q == 3);
2342 Eigen::MatrixXd nodes;
2345 std::array<int, 4> idx;
2347 for (
int lv = 0; lv < 4; ++lv)
2349 idx[lv] = find_index(l2g.begin(), l2g.end(), idx_it.
vertex);
2353 Eigen::Matrix<double, 4, 3> pos;
2354 for (
int lv = 0; lv < 4; ++lv)
2355 pos.row(lv) = nodes.row(idx[lv]);
2357 const int start_row = 6 + global_n_edges_nodes + lf * n_face_nodes + 2 * n_tri_face_nodes;
2358 Eigen::MatrixXd loc_nodes = nodes.block(start_row, 0, n_face_nodes, 3);
2360 const std::array<Eigen::Vector2d, 4> uv = {{
2361 Eigen::Vector2d(1.0 / 3.0, 1.0 / 3.0),
2362 Eigen::Vector2d(1.0 / 3.0, 2.0 / 3.0),
2363 Eigen::Vector2d(2.0 / 3.0, 1.0 / 3.0),
2364 Eigen::Vector2d(2.0 / 3.0, 2.0 / 3.0),
2367 std::array<bool, 4> used = {{
false,
false,
false,
false}};
2369 for (
const auto &st : uv)
2371 const double s = st(0);
2372 const double t = st(1);
2374 const Eigen::RowVector3d target =
2375 (1.0 - s) * (1.0 - t) * pos.row(0)
2376 + s * (1.0 - t) * pos.row(1)
2377 + s * t * pos.row(2)
2378 + (1.0 - s) * t * pos.row(3);
2381 double best = std::numeric_limits<double>::infinity();
2383 for (
int n = 0; n < n_face_nodes; ++n)
2388 const double d = (loc_nodes.row(n) - target).squaredNorm();
2396 assert(best_n >= 0);
2397 assert(best < 1e-12);
2399 used[best_n] =
true;
2400 result[ii++] = start_row + best_n;
2405 assert(n_face_nodes == 0);
2407 assert(ii == result.size());
2418 const auto l2g = pyramid_vertices_local_to_global(mesh, c);
2419 const auto &v = l2g;
2422 Eigen::Matrix<int, 8, 2> ev;
2423 ev.row(0) << v[0], v[1];
2424 ev.row(1) << v[1], v[2];
2425 ev.row(2) << v[2], v[3];
2426 ev.row(3) << v[3], v[0];
2427 ev.row(4) << v[0], v[4];
2428 ev.row(5) << v[1], v[4];
2429 ev.row(6) << v[2], v[4];
2430 ev.row(7) << v[3], v[4];
2432 Eigen::Matrix<Navigation3D::Index, 8, 1> e;
2433 for (
int le = 0; le < 8; ++le)
2436 const int nei = p - 1;
2437 const int nfi_tri = (p - 1) * (p - 2) / 2;
2438 const int nfi_quad = (p - 1) * (p - 1);
2445 const int edge_start = 5;
2446 const int tri_face_start = edge_start + 8 * nei;
2447 const int quad_face_start = tri_face_start + 4 * nfi_tri;
2451 auto append_edge_dofs = [&](Eigen::VectorXi &result,
int &ii,
const Navigation3D::Index &edge_idx) {
2455 for (; le < 8; ++le)
2457 if (e[le].edge == edge_idx.edge)
2461 const bool forward = (edge_idx.vertex == ev(le, 0));
2462 for (
int q = 0; q < nei; ++q)
2464 const int local_q = forward ? q : (nei - 1 - q);
2465 result[ii++] = edge_start + le * nei + local_q;
2474 Eigen::VectorXi result(3 + 3 * nei + nfi_tri);
2478 result[ii++] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2484 for (
int k = 0; k < 3; ++k)
2486 append_edge_dofs(result, ii, tmp);
2493 static const int tri_fv[4][3] = {{0, 1, 4}, {1, 2, 4}, {2, 3, 4}, {3, 0, 4}};
2494 const int fv0 = index.
vertex;
2498 for (
int f = 0; f < 4; ++f)
2500 const int gv0 = v[tri_fv[f][0]], gv1 = v[tri_fv[f][1]], gv2 = v[tri_fv[f][2]];
2501 if ((fv0 == gv0 || fv0 == gv1 || fv0 == gv2) && (fv1 == gv0 || fv1 == gv1 || fv1 == gv2) && (fv2 == gv0 || fv2 == gv1 || fv2 == gv2))
2508 for (
int q = 0; q < nfi_tri; ++q)
2509 result[ii++] = tri_face_start + lf * nfi_tri + q;
2512 assert(ii == result.size());
2519 Eigen::VectorXi result(4 + 4 * nei + nfi_quad);
2523 result[ii++] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2530 for (
int k = 0; k < 4; ++k)
2532 append_edge_dofs(result, ii, tmp);
2537 for (
int q = 0; q < nfi_quad; ++q)
2538 result[ii++] = quad_face_start + q;
2540 assert(ii == result.size());
2547 const std::string &assembler,
2548 const int quadrature_order,
2549 const int mass_quadrature_order,
2550 const int discr_orderp,
2551 const int discr_orderq,
2552 const bool bernstein,
2553 const bool serendipity,
2554 const bool has_polys,
2555 const bool is_geom_bases,
2556 const bool use_corner_quadrature,
2557 std::vector<ElementBases> &bases,
2558 std::vector<LocalBoundary> &local_boundary,
2559 std::map<int, InterfaceData> &poly_face_to_data,
2560 std::shared_ptr<MeshNodes> &mesh_nodes)
2562 Eigen::VectorXi discr_ordersp(mesh.
n_cells());
2563 discr_ordersp.setConstant(discr_orderp);
2565 Eigen::VectorXi discr_ordersq(mesh.
n_cells());
2566 discr_ordersq.setConstant(discr_orderq);
2568 return build_bases(mesh, assembler, quadrature_order, mass_quadrature_order, discr_ordersp, discr_ordersq, bernstein, serendipity, has_polys, is_geom_bases, use_corner_quadrature, bases, local_boundary, poly_face_to_data, mesh_nodes);
2573 const std::string &assembler,
2574 const int quadrature_order,
2575 const int mass_quadrature_order,
2576 const Eigen::VectorXi &discr_ordersp,
2577 const Eigen::VectorXi &discr_ordersq,
2578 const bool bernstein,
2579 const bool serendipity,
2580 const bool has_polys,
2581 const bool is_geom_bases,
2582 const bool use_corner_quadrature,
2583 std::vector<ElementBases> &bases,
2584 std::vector<LocalBoundary> &local_boundary,
2585 std::map<int, InterfaceData> &poly_face_to_data,
2586 std::shared_ptr<MeshNodes> &mesh_nodes)
2589 assert(discr_ordersp.size() == mesh.
n_cells());
2590 assert(discr_ordersq.size() == mesh.
n_cells());
2598 const int max_p = discr_ordersp.maxCoeff();
2599 const int max_q = discr_ordersq.maxCoeff();
2600 const int mmax = std::max(max_p, max_q);
2603 const int nn = mmax > 1 ? (mmax - 1) : 0;
2604 const int n_face_nodes = nn * nn;
2605 const int n_cells_nodes = nn * nn * nn;
2607 Eigen::VectorXi edge_orders, face_orders;
2610 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
2611 compute_edge_face_orders(ncmesh, discr_ordersp, edge_orders, face_orders);
2614 mesh_nodes = std::make_shared<MeshNodes>(mesh, has_polys, !is_geom_bases, nn, n_face_nodes * (is_geom_bases ? 2 : 1), mmax == 0 ? 1 : n_cells_nodes);
2616 std::vector<std::vector<int>> element_nodes_id, edge_virtual_nodes, face_virtual_nodes;
2617 compute_nodes(mesh, discr_ordersp, discr_ordersq, edge_orders, face_orders, serendipity, has_polys, is_geom_bases, nodes, edge_virtual_nodes, face_virtual_nodes, element_nodes_id, local_boundary, poly_face_to_data);
2621 std::vector<int> interface_elements;
2622 interface_elements.reserve(mesh.
n_faces());
2624 for (
int e = 0; e < mesh.
n_cells(); ++e)
2627 const int discr_order = discr_ordersp(e);
2628 const int discr_orderq = discr_ordersq(e);
2629 const int n_el_bases = (int)element_nodes_id[e].size();
2630 b.bases.resize(n_el_bases);
2632 bool skip_interface_element =
false;
2634 for (
int j = 0; j < n_el_bases; ++j)
2636 const int global_index = element_nodes_id[e][j];
2637 if (global_index < 0)
2639 skip_interface_element =
true;
2644 if (skip_interface_element)
2646 interface_elements.push_back(e);
2651 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
2652 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
2657 b.set_mass_quadrature([real_mass_order](
Quadrature &quad) {
2662 b.set_local_node_from_primitive_func([serendipity, discr_order, e](
const int primitive_id,
const Mesh &mesh) {
2663 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
2666 for (
int lf = 0; lf < 6; ++lf)
2668 index = mesh3d.get_index_from_element(e, lf, 0);
2669 if (index.
face == primitive_id)
2672 assert(index.
face == primitive_id);
2676 for (
int j = 0; j < n_el_bases; ++j)
2678 const int global_index = element_nodes_id[
e][j];
2680 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2682 const int dtmp = serendipity ? -2 : discr_order;
2690 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
2691 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
2693 b.set_quadrature([real_order, use_corner_quadrature](
Quadrature &quad) {
2695 tet_quadrature.get_quadrature(real_order, quad);
2697 b.set_mass_quadrature([real_mass_order, use_corner_quadrature](
Quadrature &quad) {
2699 tet_quadrature.get_quadrature(real_mass_order, quad);
2702 b.set_local_node_from_primitive_func([discr_order, e](
const int primitive_id,
const Mesh &mesh) {
2703 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
2706 for (
int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2708 index = mesh3d.get_index_from_element(e, lf, 0);
2709 if (index.
face == primitive_id)
2712 assert(index.
face == primitive_id);
2719 for (
int j = 0; j < n_el_bases; ++j)
2721 const int global_index = element_nodes_id[
e][j];
2722 if (!skip_interface_element)
2724 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2727 b.bases[j].set_basis([bernstein, discr_order, j](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &
val) {
autogen::p_basis_value_3d(bernstein, discr_order, j, uv,
val); });
2733 const int orderp = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::PRISM_LAGRANGE, 2);
2734 const int orderq = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_orderq, AssemblerUtils::BasisType::PRISM_LAGRANGE, 1);
2736 const int mass_orderp = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::PRISM_LAGRANGE, 2);
2737 const int mass_orderq = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_orderq, AssemblerUtils::BasisType::PRISM_LAGRANGE, 1);
2739 b.set_quadrature([orderp, orderq](
Quadrature &quad) {
2743 b.set_mass_quadrature([mass_orderp, mass_orderq](
Quadrature &quad) {
2748 b.set_local_node_from_primitive_func([discr_order, discr_orderq, e](
const int primitive_id,
const Mesh &mesh) {
2749 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
2752 for (
int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2754 index = mesh3d.get_index_from_element(e, lf, 0);
2755 if (index.
face == primitive_id)
2758 assert(index.
face == primitive_id);
2762 for (
int j = 0; j < n_el_bases; ++j)
2764 const int global_index = element_nodes_id[
e][j];
2765 if (!skip_interface_element)
2767 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2770 b.bases[j].set_basis([discr_order, discr_orderq, j](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &
val) {
autogen::prism_basis_value_3d(discr_order, discr_orderq, j, uv,
val); });
2776 const int orderp = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::PYRAMID_LAGRANGE, 2);
2777 const int mass_orderp = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::PYRAMID_LAGRANGE, 2);
2783 b.set_mass_quadrature([mass_orderp](
Quadrature &quad) {
2788 b.set_local_node_from_primitive_func([discr_order, e](
const int primitive_id,
const Mesh &mesh) {
2789 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
2792 for (
int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2794 index = mesh3d.get_index_from_element(e, lf, 0);
2795 if (index.
face == primitive_id)
2798 assert(index.
face == primitive_id);
2802 for (
int j = 0; j < n_el_bases; ++j)
2804 const int global_index = element_nodes_id[
e][j];
2805 if (!skip_interface_element)
2807 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2825 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
2827 std::vector<std::vector<int>> elementOrder;
2829 const int max_order = discr_ordersp.maxCoeff(), min_order = discr_ordersp.minCoeff();
2831 for (
int e = 0;
e < ncmesh.n_cells();
e++)
2832 if (max_level < ncmesh.cell_ref_level(e))
2833 max_level = ncmesh.cell_ref_level(e);
2835 elementOrder.resize((max_level + 1) * (
max_order - min_order + 1));
2838 while (cur_level <= max_level)
2840 int order = min_order;
2841 while (order <= max_order)
2843 int cur_bucket = (
max_order - min_order + 1) * cur_level + (order - min_order);
2844 for (
int i = 0; i < ncmesh.n_cells(); i++)
2846 if (ncmesh.cell_ref_level(i) != cur_level || discr_ordersp[i] != order)
2850 elementOrder[cur_bucket].push_back(i);
2858 for (
const auto &bucket : elementOrder)
2860 if (bucket.size() == 0)
2863 for (int e_aux = start; e_aux < end; e_aux++)
2865 const int e = bucket[e_aux];
2866 ElementBases &b = bases[e];
2867 const int discr_order = discr_ordersp(e);
2868 const int n_edge_nodes = discr_order - 1;
2869 const int n_face_nodes = (discr_order - 1) * (discr_order - 2) / 2;
2870 const int n_el_bases = element_nodes_id[e].size();
2872 auto v = tet_vertices_local_to_global(mesh, e);
2874 Eigen::Matrix<Navigation3D::Index, 4, 1> cell_faces;
2875 Eigen::Matrix<int, 4, 3> fv;
2876 fv.row(0) << v[0], v[1], v[2];
2877 fv.row(1) << v[0], v[1], v[3];
2878 fv.row(2) << v[1], v[2], v[3];
2879 fv.row(3) << v[2], v[0], v[3];
2881 for (long lf = 0; lf < fv.rows(); ++lf)
2883 const auto index = mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2));
2884 cell_faces[lf] = index;
2887 Eigen::Matrix<Navigation3D::Index, 6, 1> cell_edges;
2888 Eigen::Matrix<int, 6, 2> ev;
2889 ev.row(0) << v[0], v[1];
2890 ev.row(1) << v[1], v[2];
2891 ev.row(2) << v[2], v[0];
2893 ev.row(3) << v[0], v[3];
2894 ev.row(4) << v[1], v[3];
2895 ev.row(5) << v[2], v[3];
2897 for (int le = 0; le < ev.rows(); ++le)
2900 const auto index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
2901 cell_edges[le] = index;
2904 Eigen::MatrixXd verts(4, 3);
2905 for (int i = 0; i < ncmesh.n_cell_vertices(e); i++)
2906 verts.row(i) = ncmesh.point(v[i]);
2908 for (int j = 0; j < n_el_bases; ++j)
2910 const int global_index = element_nodes_id[e][j];
2912 if (global_index >= 0)
2914 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2921 int large_elem = -1;
2922 if (ncmesh.leader_edge_of_vertex(v[j]) >= 0)
2924 large_elem = lowest_order_elem_on_edge(ncmesh, discr_ordersp, ncmesh.leader_edge_of_vertex(v[j]));
2926 else if (ncmesh.leader_face_of_vertex(v[j]) >= 0)
2928 std::vector<int> ids;
2929 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_vertex(v[j]), ids);
2930 assert(ids.size() == 1);
2931 large_elem = ids[0];
2936 Eigen::MatrixXd large_elem_verts(4, 3);
2937 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
2938 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
2939 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
2941 Eigen::MatrixXd node_position;
2942 global_to_local(large_elem_verts, verts.row(j), node_position);
2945 const auto &other_bases = bases[large_elem];
2946 std::vector<AssemblyValues> w;
2947 other_bases.evaluate_bases(node_position, w);
2950 for (long i = 0; i < w.size(); ++i)
2952 assert(w[i].val.size() == 1);
2953 if (std::abs(w[i].val(0)) < 1e-12)
2956 assert(other_bases.bases[i].global().size() > 0);
2957 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
2959 const auto &other_global = other_bases.bases[i].global()[ii];
2960 assert(other_global.index >= 0);
2961 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
2966 else if (j < 4 + 6 * n_edge_nodes)
2968 const int local_edge_id = (j - 4) / n_edge_nodes;
2969 const int edge_id = cell_edges[local_edge_id].edge;
2970 bool need_extra_fake_nodes = false;
2971 int large_elem = -1;
2974 if (ncmesh.leader_edge_of_edge(edge_id) >= 0)
2976 std::vector<int> ids;
2977 ncmesh.get_edge_elements_neighs(ncmesh.leader_edge_of_edge(edge_id), ids);
2978 large_elem = ids[0];
2981 else if (ncmesh.leader_face_of_edge(edge_id) >= 0)
2983 std::vector<int> ids;
2984 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_edge(edge_id), ids);
2985 assert(ids.size() == 1);
2986 large_elem = ids[0];
2989 else if (discr_order > edge_orders[edge_id])
2991 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_ordersp, edge_id);
2993 if (discr_ordersp[min_order_elem] < discr_order)
2994 large_elem = min_order_elem;
3000 need_extra_fake_nodes = true;
3006 assert(large_elem >= 0 || need_extra_fake_nodes);
3007 Eigen::MatrixXd lnodes;
3008 autogen::p_nodes_3d(discr_order, lnodes);
3009 Eigen::MatrixXd local_position = lnodes.row(j);
3010 if (need_extra_fake_nodes)
3012 Eigen::MatrixXd global_position, edge_verts(2, 3);
3013 Eigen::VectorXd point_weight;
3015 edge_verts.row(0) = ncmesh.point(ncmesh.edge_vertex(edge_id, 0));
3016 edge_verts.row(1) = ncmesh.point(ncmesh.edge_vertex(edge_id, 1));
3018 local_to_global(verts, local_position, global_position);
3019 global_to_local_edge(edge_verts, global_position, point_weight);
3021 std::function<double(const int, const int, const double)> basis_1d = [](const int order, const int id, const double x) -> double {
3022 assert(id <= order && id >= 0);
3024 for (int o = 0; o <= order; o++)
3027 y *= (x * order - o) / (id - o);
3033 for (int i = 0; i < edge_virtual_nodes[edge_id].size(); i++)
3035 const int global_index = edge_virtual_nodes[edge_id][i];
3037 Eigen::VectorXd node_weight;
3038 global_to_local_edge(edge_verts, nodes.node_position(global_index), node_weight);
3039 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
3040 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
3041 if (std::abs(weight) < 1e-12)
3043 b.bases[j].global().emplace_back(global_index, nodes.node_position(global_index), weight);
3047 for (int i = 0; i < 2; i++)
3049 const int lv = ev(local_edge_id, i);
3050 const auto &global_ = b.bases[lv].global();
3051 Eigen::VectorXd node_weight;
3052 global_to_local_edge(edge_verts, verts.row(lv), node_weight);
3053 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
3054 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
3055 if (std::abs(weight) > 1e-12)
3057 assert(global_.size() > 0);
3058 for (size_t ii = 0; ii < global_.size(); ++ii)
3059 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
3065 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
3066 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
3067 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
3068 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
3069 local_to_global(verts, local_position, global_position);
3070 global_to_local(large_elem_verts, global_position, local_position);
3073 const auto &other_bases = bases[large_elem];
3074 std::vector<AssemblyValues> w;
3075 other_bases.evaluate_bases(local_position, w);
3078 for (long i = 0; i < w.size(); ++i)
3080 assert(w[i].val.size() == 1);
3081 if (std::abs(w[i].val(0)) < 1e-12)
3084 assert(other_bases.bases[i].global().size() > 0);
3085 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3087 const auto &other_global = other_bases.bases[i].global()[ii];
3088 assert(other_global.index >= 0);
3089 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3095 else if (j < 4 + 6 * n_edge_nodes + 4 * n_face_nodes)
3097 const int local_face_id = (j - (4 + 6 * n_edge_nodes)) / n_face_nodes;
3098 const int face_id = cell_faces(local_face_id).face;
3099 int large_elem = -1;
3100 bool need_extra_fake_nodes = false;
3102 std::vector<int> ids;
3103 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_face(face_id), ids);
3105 Eigen::MatrixXd face_verts(3, 3);
3106 for (int i = 0; i < ncmesh.n_face_vertices(face_id); i++)
3107 face_verts.row(i) = ncmesh.point(ncmesh.face_vertex(face_id, i));
3110 if (ncmesh.leader_face_of_face(face_id) >= 0)
3112 assert(ids.size() == 1);
3113 large_elem = ids[0];
3117 else if (face_orders[face_id] < discr_order && ids.size() == 2)
3119 large_elem = ids[0] == e ? ids[1] : ids[0];
3122 else if (face_orders[face_id] < discr_order && ncmesh.n_follower_faces(face_id) > 0)
3125 need_extra_fake_nodes = true;
3130 assert(large_elem >= 0 || need_extra_fake_nodes);
3131 Eigen::MatrixXd lnodes;
3132 autogen::p_nodes_3d(discr_order, lnodes);
3133 Eigen::MatrixXd local_position = lnodes.row(j);
3134 if (need_extra_fake_nodes)
3136 Eigen::MatrixXd global_position;
3137 local_to_global(verts, local_position, global_position);
3139 Eigen::MatrixXd tmp;
3140 global_to_local_face(face_verts, global_position, tmp);
3141 Eigen::VectorXd face_weight = tmp.transpose();
3143 std::function<double(const int, const int, const double)> basis_aux = [](const int order, const int id, const double x) -> double {
3144 assert(id <= order && id >= 0);
3146 for (int o = 0; o < id; o++)
3147 y *= (x * order - o) / (id - o);
3151 std::function<double(const int, const int, const int, const Eigen::Vector2d)> basis_2d = [&basis_aux](const int order, const int i, const int j, const Eigen::Vector2d uv) -> double {
3152 assert(i + j <= order && i >= 0 && j >= 0);
3153 double u = uv(0), v = uv(1);
3154 return basis_aux(order, i, u) * basis_aux(order, j, v) * basis_aux(order, order - i - j, 1 - u - v);
3158 for (int global_ : face_virtual_nodes[face_id])
3160 auto low_order_node = nodes.node_position(global_);
3161 Eigen::MatrixXd low_order_node_face_weight;
3162 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
3163 int x = round(low_order_node_face_weight(0) * face_orders[face_id]), y = round(low_order_node_face_weight(1) * face_orders[face_id]);
3164 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3165 if (std::abs(weight) < 1e-12)
3167 b.bases[j].global().emplace_back(global_, nodes.node_position(global_), weight);
3171 for (int i = 0; i < 3; i++)
3173 const auto &global_ = b.bases[fv(local_face_id, i)].global();
3174 auto low_order_node = ncmesh.point(fv(local_face_id, i));
3175 Eigen::MatrixXd low_order_node_face_weight;
3176 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
3177 int x = round(low_order_node_face_weight(0) * face_orders[face_id]), y = round(low_order_node_face_weight(1) * face_orders[face_id]);
3178 double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3179 if (std::abs(weight) > 1e-12)
3181 assert(global_.size() > 0);
3182 for (size_t ii = 0; ii < global_.size(); ++ii)
3183 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
3188 for (int x = 0, idx = 0; x <= face_orders[face_id]; x++)
3190 for (int y = 0; x + y <= face_orders[face_id]; y++)
3192 const int z = face_orders[face_id] - x - y;
3193 int flag = (int)(x == 0) + (int)(y == 0) + (int)(z == 0);
3198 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3199 if (std::abs(weight) < 1e-12)
3201 Eigen::MatrixXd face_weight(1, 2);
3202 face_weight << (double)x / face_orders[face_id], (double)y / face_orders[face_id];
3203 Eigen::MatrixXd pos, local_pos;
3204 local_to_global_face(face_verts, face_weight, pos);
3205 global_to_local(verts, pos, local_pos);
3206 Local2Global step1(idx, local_pos, weight);
3211 const auto &other_bases = bases[e];
3212 std::vector<AssemblyValues> w;
3213 other_bases.evaluate_bases(local_pos, w);
3216 for (long i = 0; i < w.size(); ++i)
3218 assert(w[i].val.size() == 1);
3219 if (std::abs(w[i].val(0)) < 1e-12)
3222 assert(other_bases.bases[i].global().size() > 0);
3223 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3225 const auto &other_global = other_bases.bases[i].global()[ii];
3226 assert(other_global.index >= 0);
3227 b.bases[j].global().emplace_back(other_global.index, other_global.node, step1.val * w[i].val(0) * other_global.val);
3236 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
3237 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
3238 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
3239 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
3240 local_to_global(verts, local_position, global_position);
3241 global_to_local(large_elem_verts, global_position, local_position);
3244 const auto &other_bases = bases[large_elem];
3245 std::vector<AssemblyValues> w;
3246 other_bases.evaluate_bases(local_position, w);
3249 for (long i = 0; i < w.size(); ++i)
3251 assert(w[i].val.size() == 1);
3252 if (std::abs(w[i].val(0)) < 1e-12)
3255 assert(other_bases.bases[i].global().size() > 0);
3256 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3258 const auto &other_global = other_bases.bases[i].global()[ii];
3259 assert(other_global.index >= 0);
3260 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3268 auto &global_ = b.bases[j].global();
3269 if (global_.size() <= 1)
3272 std::map<int, Local2Global> list;
3273 for (size_t ii = 0; ii < global_.size(); ii++)
3275 auto pair = list.insert({global_[ii].index, global_[ii]});
3276 if (!pair.second && pair.first != list.end())
3278 assert((pair.first->second.node - global_[ii].node).norm() < 1e-12);
3279 pair.first->second.val += global_[ii].val;
3284 for (auto it = list.begin(); it != list.end(); ++it)
3286 if (std::abs(it->second.val) > 1e-12)
3288 global_.push_back(it->second);
3299 for (
int pp = 2; pp <= autogen::MAX_P_BASES; ++pp)
3301 for (
int e : interface_elements)
3305 const int discr_order = discr_ordersp(e);
3306 const int n_el_bases = element_nodes_id[
e].size();
3307 assert(discr_order > 1);
3308 if (discr_order != pp)
3318 for (
int j = 0; j < n_el_bases; ++j)
3320 const int global_index = element_nodes_id[
e][j];
3322 if (global_index >= 0)
3324 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
3328 const int lnn =
max_p > 2 ? (discr_order - 2) : 0;
3329 const int ln_edge_nodes = discr_order - 1;
3330 const int ln_face_nodes = lnn * (lnn + 1) / 2;
3332 const auto v = tet_vertices_local_to_global(mesh, e);
3334 if (global_index <= -30)
3358 else if (global_index <= -10)
3360 const auto le = -(global_index + 10);
3361 assert(le >= 0 && le < 6);
3362 assert(j >= 4 && j < 4 + 6 * ln_edge_nodes);
3364 Eigen::Matrix<int, 6, 2> ev;
3365 ev.row(0) << v[0], v[1];
3366 ev.row(1) << v[1], v[2];
3367 ev.row(2) << v[2], v[0];
3369 ev.row(3) << v[0], v[3];
3370 ev.row(4) << v[1], v[3];
3371 ev.row(5) << v[2], v[3];
3374 const auto edge_index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
3375 auto neighs = mesh.edge_neighs(edge_index.edge);
3376 int min_p = discr_order;
3377 int min_cell = edge_index.element;
3379 for (
auto cid : neighs)
3381 if (discr_ordersp[cid] < min_p)
3383 min_p = discr_ordersp[cid];
3393 for (
int lf = 0; lf < 4; ++lf)
3395 for (
int lv = 0; lv < 4; ++lv)
3397 index = mesh.get_index_from_element(min_cell, lf, lv);
3399 if (index.
vertex == edge_index.vertex)
3401 if (index.
edge != edge_index.edge)
3404 index = mesh.switch_edge(tmp);
3406 if (index.
edge != edge_index.edge)
3408 index = mesh.switch_edge(mesh.switch_face(tmp));
3422 for (
int lf = 0; lf < 5; ++lf)
3424 for (
int lv = 0; lv < 5; ++lv)
3426 index = mesh.get_index_from_element(min_cell, lf, lv);
3427 if (index.
vertex == edge_index.vertex)
3429 if (index.
edge != edge_index.edge)
3432 index = mesh.switch_edge(tmp);
3434 if (index.
edge != edge_index.edge)
3436 index = mesh.switch_edge(mesh.switch_face(tmp));
3446 index = mesh.switch_face(index);
3450 assert(index.
vertex == edge_index.vertex && index.
edge == edge_index.edge);
3451 assert(index.
element != edge_index.element);
3460 const auto lf = -(global_index + 1);
3461 assert(lf >= 0 && lf < 4);
3462 assert(j >= 4 + 6 * ln_edge_nodes && j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes);
3464 Eigen::Matrix<int, 4, 3> fv;
3465 fv.row(0) << v[0], v[1], v[2];
3466 fv.row(1) << v[0], v[1], v[3];
3467 fv.row(2) << v[1], v[2], v[3];
3468 fv.row(3) << v[2], v[0], v[3];
3470 index = mesh.switch_element(mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2)));
3473 const auto other_cell = index.
element;
3474 assert(other_cell >= 0);
3477 Eigen::MatrixXd lnodes;
3478 Eigen::RowVector3d node_position;
3482 assert(discr_order > discr_ordersp(other_cell));
3486 else if (mesh.
is_prism(other_cell))
3488 assert(discr_order > discr_ordersp(other_cell));
3498 node_position = lnodes.row(
indices(0));
3499 else if (j < 4 + 6 * ln_edge_nodes)
3511 static const int tet_edge_v[6][2] =
3512 {{0, 1}, {1, 2}, {2, 0}, {0, 3}, {1, 3}, {2, 3}};
3513 const int le2 = -(global_index + 10);
3514 const auto pl2g = prism_vertices_local_to_global(mesh, other_cell);
3515 const int i0 = find_index(pl2g.begin(), pl2g.end(), v[tet_edge_v[le2][0]]);
3516 const int i1 = find_index(pl2g.begin(), pl2g.end(), v[tet_edge_v[le2][1]]);
3517 const int k = (j - 4) % ln_edge_nodes;
3518 const double t = double(k + 1) / discr_order;
3519 node_position = (1.0 - t) * lnodes.row(i0) + t * lnodes.row(i1);
3522 node_position = lnodes.row(
indices(((j - 4) % ln_edge_nodes) + 3));
3524 else if (j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes)
3529 for (ii = 0; ii < me_indices.size(); ++ii)
3531 if (me_indices(ii) == j)
3535 assert(ii >= 3 + 3 * ln_edge_nodes);
3536 assert(ii < me_indices.size());
3538 node_position = lnodes.row(
indices(ii));
3543 const auto &other_bases = bases[other_cell];
3545 std::vector<AssemblyValues> w;
3546 other_bases.evaluate_bases(node_position, w);
3548 assert(
b.bases[j].global().size() == 0);
3550 for (
long i = 0; i < w.size(); ++i)
3552 assert(w[i].
val.size() == 1);
3553 if (std::abs(w[i].
val(0)) < 1e-8)
3557 for (
size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3559 const auto &other_global = other_bases.bases[i].global()[ii];
3561 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3569 for (
int j = 0; j < n_el_bases; ++j)
3571 const int global_index = element_nodes_id[
e][j];
3573 if (global_index >= 0)
3575 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
3579 const int lnn = discr_order - 1;
3580 const int ln_edge_nodes = discr_order - 1;
3581 const int ln_face_nodes = lnn * lnn;
3583 const auto v = pyramid_vertices_local_to_global(mesh, e);
3585 if (global_index <= -30)
3589 else if (global_index <= -10)
3591 const int le = -(global_index + 10);
3592 assert(le >= 0 && le < 4);
3593 assert(j >= 5 && j < 5 + 8 * ln_edge_nodes);
3595 Eigen::Matrix<int, 4, 2> ev;
3596 ev.row(0) << v[0], v[1];
3597 ev.row(1) << v[1], v[2];
3598 ev.row(2) << v[2], v[3];
3599 ev.row(3) << v[3], v[0];
3601 const auto edge_index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
3602 auto neighs = mesh.edge_neighs(edge_index.edge);
3603 int min_p = discr_order;
3604 int min_cell = edge_index.element;
3606 for (
auto cid : neighs)
3608 const int cid_order =
3609 prism_edge_order(cid, edge_index.edge, discr_ordersp, discr_ordersq, mesh);
3611 if (cid_order < min_p)
3621 for (
int lf = 0; lf < 5; ++lf)
3623 for (
int lv = 0; lv < 5; ++lv)
3625 index = mesh.get_index_from_element(min_cell, lf, lv);
3626 if (index.
vertex == edge_index.vertex)
3628 if (index.
edge != edge_index.edge)
3631 index = mesh.switch_edge(tmp);
3633 if (index.
edge != edge_index.edge)
3635 index = mesh.switch_edge(mesh.switch_face(tmp));
3647 index = mesh.switch_face(index);
3651 assert(index.
vertex == edge_index.vertex && index.
edge == edge_index.edge);
3652 assert(index.
element != edge_index.element);
3661 const auto lf = -(global_index + 1);
3664 index = mesh.switch_element(find_quad_face(mesh, e, v[0], v[1], v[2], v[3]));
3667 const auto other_cell = index.
element;
3668 assert(other_cell >= 0);
3671 Eigen::MatrixXd lnodes;
3672 Eigen::RowVector3d node_position;
3677 (global_index <= -10 && discr_order > prism_edge_order(other_cell, index.
edge, discr_ordersp, discr_ordersq, mesh))
3678 || (global_index > -10 && (discr_order > discr_ordersp(other_cell) || discr_order > discr_ordersq(other_cell))));
3688 const int tri_face_nodes = (discr_order - 1) * (discr_order - 2) / 2;
3689 const int quad_face_start = 5 + 8 * ln_edge_nodes + 4 * tri_face_nodes;
3692 node_position = lnodes.row(
indices(0));
3693 else if (j < 5 + 8 * ln_edge_nodes)
3695 const int le = -(global_index + 10);
3696 const int edge_offset = j - (5 + le * ln_edge_nodes);
3697 assert(j >= 5 + le * ln_edge_nodes);
3698 assert(j < 5 + (le + 1) * ln_edge_nodes);
3708 static const int pyr_edge_v[4][2] =
3709 {{0, 1}, {1, 2}, {2, 3}, {3, 0}};
3710 const auto pl2g = prism_vertices_local_to_global(mesh, other_cell);
3711 const int i0 = find_index(pl2g.begin(), pl2g.end(), v[pyr_edge_v[le][0]]);
3712 const int i1 = find_index(pl2g.begin(), pl2g.end(), v[pyr_edge_v[le][1]]);
3713 const double t = double(edge_offset + 1) / discr_order;
3714 node_position = (1.0 - t) * lnodes.row(i0) + t * lnodes.row(i1);
3718 node_position = lnodes.row(
indices(4 + edge_offset));
3720 else if (j >= quad_face_start && j < quad_face_start + ln_face_nodes)
3724 for (ii = 0; ii < me_indices.size(); ++ii)
3726 if (me_indices(ii) == j)
3730 assert(ii >= 4 + 4 * ln_edge_nodes);
3731 assert(ii < me_indices.size());
3733 node_position = lnodes.row(
indices(ii));
3738 const auto &other_bases = bases[other_cell];
3740 std::vector<AssemblyValues> w;
3741 other_bases.evaluate_bases(node_position, w);
3743 assert(
b.bases[j].global().size() == 0);
3745 for (
long i = 0; i < w.size(); ++i)
3747 assert(w[i].
val.size() == 1);
3748 if (std::abs(w[i].
val(0)) < 1e-8)
3752 for (
size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3754 const auto &other_global = other_bases.bases[i].global()[ii];
3756 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3771 return nodes.n_nodes();
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).
static Eigen::VectorXi pyramid_face_local_nodes(const int p, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
static Eigen::VectorXi hex_face_local_nodes(const bool serendipity, const int q, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
static Eigen::VectorXi prism_face_local_nodes(const int p, const int q, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
static int build_bases(const mesh::Mesh3D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, const int discr_orderp, const int discr_orderq, const bool bernstein, const bool serendipity, const bool has_polys, const bool is_geom_bases, const bool use_corner_quadrature, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_face_to_data, std::shared_ptr< mesh::MeshNodes > &mesh_nodes)
Builds FE basis functions over the entire mesh (P1, P2 over tets, Q1, Q2 over hes).
static Eigen::VectorXi tet_face_local_nodes(const int p, const mesh::Mesh3D &mesh, mesh::Navigation3D::Index index)
Boundary primitive IDs for a single element.
virtual Navigation3D::Index get_index_from_element(int hi, int lf, int lv) const =0
virtual Navigation3D::Index switch_element(Navigation3D::Index idx) const =0
virtual Navigation3D::Index get_index_from_element_edge(int hi, int v0, int v1) const =0
virtual int n_cell_faces(const int c_id) const =0
virtual std::vector< uint32_t > edge_neighs(const int e_gid) const =0
bool is_volume() const override
checks if mesh is volume
virtual Navigation3D::Index next_around_face(Navigation3D::Index idx) const =0
virtual Navigation3D::Index get_index_from_element_face(int hi, int v0, int v1, int v2) const =0
virtual Navigation3D::Index switch_vertex(Navigation3D::Index idx) const =0
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
bool is_polytope(const int el_id) const
checks if element is polygon compatible
bool is_rational() const
check if curved mesh has rational polynomials elements
virtual bool is_conforming() const =0
if the mesh is conforming
bool is_cube(const int el_id) const
checks if element is cube compatible
virtual RowVectorNd point(const int global_index) const =0
point coordinates
virtual bool is_boundary_face(const int face_global_id) const =0
is face boundary
virtual int get_boundary_id(const int primitive) const
Get the boundary selection of an element (face in 3d, edge in 2d)
bool is_simplex(const int el_id) const
checks if element is simplex
bool is_prism(const int el_id) const
checks if element is a prism
const std::vector< double > & cell_weights(const int cell_index) const
weights for rational polynomial meshes
virtual int n_cells() const =0
number of cells
virtual int n_faces() const =0
number of faces
bool is_pyramid(const int el_id) const
checks if element is a pyramid
virtual int n_face_vertices(const int f_id) const =0
number of vertices of a face
int n_faces() const override
number of faces
int face_edge(const int f_id, const int le_id) const
int n_cell_faces(const int c_id) const override
int n_edge_cells(const int e_id) const
int n_cells() const override
number of cells
int n_edges() const override
number of edges
int n_face_vertices(const int f_id) const override
number of vertices of a face
std::vector< uint32_t > edge_neighs(const int e_gid) const override
int leader_face_of_edge(const int e_id) const
int cell_face(const int c_id, const int lf_id) const override
int leader_edge_of_edge(const int e_id) const
int n_cell_edges(const int c_id) const override
int cell_edge(const int c_id, const int le_id) const override
int leader_face_of_face(const int f_id) const
int n_face_cells(const int f_id) const
void get_quadrature(const int order, Quadrature &quad)
void get_quadrature(const int order, const int order_h, Quadrature &quad)
void get_quadrature(const int order, Quadrature &quad)
void q_grad_basis_value_3d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void prism_basis_value_3d(const int p, const int q, const int li, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void pyramid_grad_basis_value_3d(const int pyramid, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void pyramid_basis_value_3d(const int pyramid, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void prism_grad_basis_value_3d(const int p, const int q, const int li, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void prism_nodes_3d(const int p, const int q, Eigen::MatrixXd &val)
void p_grad_basis_value_3d(const bool bernstein, const int p, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void p_nodes_3d(const int p, Eigen::MatrixXd &val)
void p_basis_value_3d(const bool bernstein, const int p, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void q_nodes_3d(const int q, Eigen::MatrixXd &val)
void q_basis_value_3d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void maybe_parallel_for(int size, const std::function< void(int, int, int)> &partial_for)
std::vector< int > local_indices