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 lowest_order_elem_on_edge(
const polyfem::mesh::NCMesh3D &mesh,
const Eigen::VectorXi &discr_orders,
const int eid)
230 int min = std::numeric_limits<int>::max();
232 for (
const auto e : elem_list)
233 if (discr_orders[
e] < min)
238 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)
240 const int n_edge_nodes = p > 1 ? ((p - 1) * 6) : 0;
241 const int nn = p > 2 ? (p - 2) : 0;
242 const int n_loc_f = (
nn * (
nn + 1) / 2);
243 const int n_face_nodes = n_loc_f * 4;
244 int n_cell_nodes = 0;
245 for (
int pp = 4; pp <= p; ++pp)
246 n_cell_nodes += ((pp - 3) * ((pp - 3) + 1) / 2);
250 res.push_back(
nodes.node_id_from_cell(c));
255 res.reserve(4 + n_edge_nodes + n_face_nodes + n_cell_nodes);
258 Eigen::Matrix<Navigation3D::Index, 4, 1>
f;
260 auto v = tet_vertices_local_to_global(mesh, c);
261 Eigen::Matrix<int, 4, 3> fv;
262 fv.row(0) << v[0], v[1], v[2];
263 fv.row(1) << v[0], v[1], v[3];
264 fv.row(2) << v[1], v[2], v[3];
265 fv.row(3) << v[2], v[0], v[3];
267 for (
long lf = 0; lf < fv.rows(); ++lf)
273 Eigen::Matrix<Navigation3D::Index, 6, 1>
e;
274 Eigen::Matrix<int, 6, 2> ev;
275 ev.row(0) << v[0], v[1];
276 ev.row(1) << v[1], v[2];
277 ev.row(2) << v[2], v[0];
279 ev.row(3) << v[0], v[3];
280 ev.row(4) << v[1], v[3];
281 ev.row(5) << v[2], v[3];
283 for (
int le = 0; le <
e.rows(); ++le)
291 for (
size_t lv = 0; lv < v.size(); ++lv)
295 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
297 if (ncmesh.leader_edge_of_vertex(v[lv]) >= 0 || ncmesh.leader_face_of_vertex(v[lv]) >= 0)
298 res.push_back(-lv - 1);
300 res.push_back(
nodes.node_id_from_primitive(v[lv]));
303 res.push_back(
nodes.node_id_from_primitive(v[lv]));
307 for (
int le = 0; le <
e.rows(); ++le)
309 const auto index =
e[le];
311 int min_p = discr_order.size() > 0 ? discr_order(c) : 0;
315 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
316 res.insert(res.end(), node_ids.begin(), node_ids.end());
322 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
324 if (ncmesh.leader_edge_of_edge(index.edge) >= 0 || ncmesh.leader_face_of_edge(index.edge) >= 0)
326 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
327 res.push_back(-le - 1);
330 else if (edge_orders[index.edge] < discr_order(c))
332 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
333 res.push_back(-le - 1);
335 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_order, index.edge);
337 if (min_order_elem == c)
338 edge_virtual_nodes[index.edge] =
nodes.node_ids_from_edge(index, edge_orders[index.edge] - 1);
342 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
343 res.insert(res.end(), node_ids.begin(), node_ids.end());
348 for (
auto cid : neighs)
350 min_p = std::min(min_p, discr_order.size() > 0 ? discr_order(cid) : 0);
353 if (discr_order.size() > 0 && discr_order(c) > min_p)
355 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
356 res.push_back(-le - 10);
360 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
361 res.insert(res.end(), node_ids.begin(), node_ids.end());
368 for (
int lf = 0; lf <
f.rows(); ++lf)
370 const auto index =
f[lf];
373 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
377 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
378 res.insert(res.end(), node_ids.begin(), node_ids.end());
384 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
386 if (ncmesh.leader_face_of_face(index.face) >= 0)
388 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
389 res.push_back(-lf - 1);
392 else if (face_orders[index.face] < discr_order[c])
394 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
395 res.push_back(-lf - 1);
397 if (ncmesh.n_follower_faces(index.face) > 0 && face_orders[index.face] > 2)
398 face_virtual_nodes[index.face] =
nodes.node_ids_from_face(index, face_orders[index.face] - 2);
402 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
403 res.insert(res.end(), node_ids.begin(), node_ids.end());
410 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
411 res.push_back(-lf - 1);
415 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
416 res.insert(res.end(), node_ids.begin(), node_ids.end());
423 if (n_cell_nodes > 0)
425 const auto index =
f[0];
427 auto node_ids =
nodes.node_ids_from_cell(index, p - 3);
428 res.insert(res.end(), node_ids.begin(), node_ids.end());
431 assert(res.size() ==
size_t(4 + n_edge_nodes + n_face_nodes + n_cell_nodes));
434 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)
438 const int n_edge_nodes = ((q - 1) * 12);
439 const int nn = (q - 1);
440 const int n_loc_f = serendipity ? 0 : (
nn *
nn);
441 const int n_face_nodes = serendipity ? 0 : (n_loc_f * 6);
442 const int n_cell_nodes = serendipity ? 0 : (
nn *
nn *
nn);
446 res.push_back(
nodes.node_id_from_cell(c));
451 res.reserve(8 + n_edge_nodes + n_face_nodes + n_cell_nodes);
454 auto v = hex_vertices_local_to_global(mesh, c);
457 Eigen::Matrix<Navigation3D::Index, 12, 1>
e;
458 Eigen::Matrix<int, 12, 2> ev;
459 ev.row(0) << v[0], v[1];
460 ev.row(1) << v[1], v[2];
461 ev.row(2) << v[2], v[3];
462 ev.row(3) << v[3], v[0];
463 ev.row(4) << v[0], v[4];
464 ev.row(5) << v[1], v[5];
465 ev.row(6) << v[2], v[6];
466 ev.row(7) << v[3], v[7];
467 ev.row(8) << v[4], v[5];
468 ev.row(9) << v[5], v[6];
469 ev.row(10) << v[6], v[7];
470 ev.row(11) << v[7], v[4];
471 for (
int le = 0; le <
e.rows(); ++le)
478 Eigen::Matrix<Navigation3D::Index, 6, 1>
f;
479 Eigen::Matrix<int, 6, 4> fv;
480 fv.row(0) << v[0], v[3], v[4], v[7];
481 fv.row(1) << v[1], v[2], v[5], v[6];
482 fv.row(2) << v[0], v[1], v[5], v[4];
483 fv.row(3) << v[3], v[2], v[6], v[7];
484 fv.row(4) << v[0], v[1], v[2], v[3];
485 fv.row(5) << v[4], v[5], v[6], v[7];
486 for (
int lf = 0; lf <
f.rows(); ++lf)
488 const auto index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
493 for (
size_t lv = 0; lv < v.size(); ++lv)
495 res.push_back(
nodes.node_id_from_primitive(v[lv]));
497 assert(res.size() ==
size_t(8));
500 for (
int le = 0; le <
e.rows(); ++le)
502 const auto index =
e[le];
504 int min_q = discr_order.size() > 0 ? discr_order(c) : 0;
506 for (
auto cid : neighs)
508 min_q = std::min(min_q, discr_order.size() > 0 ? discr_order(cid) : 0);
511 if (discr_order.size() > 0 && discr_order(c) > min_q)
513 for (
int tmp = 0;
tmp < q - 1; ++
tmp)
514 res.push_back(-le - 10);
518 auto node_ids =
nodes.node_ids_from_edge(index, q - 1);
519 res.insert(res.end(), node_ids.begin(), node_ids.end());
522 assert(res.size() ==
size_t(8 + n_edge_nodes));
525 for (
int lf = 0; lf <
f.rows(); ++lf)
527 const auto index =
f[lf];
530 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
534 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
535 res.push_back(-lf - 1);
539 auto node_ids =
nodes.node_ids_from_face(index, serendipity ? 0 : (q - 1));
540 assert(node_ids.size() == n_loc_f);
541 res.insert(res.end(), node_ids.begin(), node_ids.end());
544 assert(res.size() ==
size_t(8 + n_edge_nodes + n_face_nodes));
547 if (n_cell_nodes > 0)
549 const auto index =
f[0];
551 auto node_ids =
nodes.node_ids_from_cell(index, q - 1);
552 res.insert(res.end(), node_ids.begin(), node_ids.end());
555 assert(res.size() ==
size_t(8 + n_edge_nodes + n_face_nodes + n_cell_nodes));
558 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)
562 const int n_edge_nodest = p > 1 ? ((p - 1) * 3) : 0;
563 const int nnt = p > 2 ? (p - 2) : 0;
564 const int n_face_nodest = nnt * (nnt + 1) / 2;
566 const int nnq = q > 1 ? (q - 1) : 0;
567 const int n_face_nodesq = nnq * n_edge_nodest / 3;
569 const int n_edge_nodes = n_edge_nodest * 2 + nnq * 3;
570 const int n_face_nodes = n_face_nodest * 2 + n_face_nodesq * 3;
571 const int n_cell_nodes = n_face_nodest * nnq;
573 if (p == 0 && q == 0)
575 res.push_back(
nodes.node_id_from_cell(c));
579 res.reserve(6 + n_edge_nodes + n_face_nodes + n_cell_nodes);
582 auto v = prism_vertices_local_to_global(mesh, c);
585 Eigen::Matrix<Navigation3D::Index, 9, 1>
e;
586 Eigen::Matrix<int, 9, 2> ev;
587 ev.row(0) << v[0], v[1];
588 ev.row(1) << v[1], v[2];
589 ev.row(2) << v[2], v[0];
590 ev.row(3) << v[3], v[4];
591 ev.row(4) << v[4], v[5];
592 ev.row(5) << v[5], v[3];
593 ev.row(6) << v[0], v[3];
594 ev.row(7) << v[1], v[4];
595 ev.row(8) << v[2], v[5];
597 for (
int le = 0; le <
e.rows(); ++le)
603 Eigen::Matrix<Navigation3D::Index, 5, 1>
f;
604 Eigen::Matrix<int, 2, 3> fvt;
605 fvt.row(0) << v[0], v[1], v[2];
606 fvt.row(1) << v[3], v[4], v[5];
607 for (
int lf = 0; lf < fvt.rows(); ++lf)
613 Eigen::Matrix<int, 3, 4> fvq;
614 fvq.row(0) << v[0], v[1], v[4], v[3];
615 fvq.row(1) << v[1], v[2], v[5], v[4];
616 fvq.row(2) << v[2], v[0], v[3], v[5];
617 for (
int lf = 0; lf < fvq.rows(); ++lf)
619 const auto index = find_quad_face(mesh, c, fvq(lf, 0), fvq(lf, 1), fvq(lf, 2), fvq(lf, 3));
624 for (
size_t lv = 0; lv < v.size(); ++lv)
626 res.push_back(
nodes.node_id_from_primitive(v[lv]));
628 assert(res.size() ==
size_t(6));
631 for (
int le = 0; le <
e.rows(); ++le)
633 const auto index =
e[le];
650 auto node_ids =
nodes.node_ids_from_edge(index, (le < 6 ? p : q) - 1);
651 res.insert(res.end(), node_ids.begin(), node_ids.end());
654 assert(res.size() ==
size_t(6 + n_edge_nodes));
657 for (
int lf = 0; lf <
f.rows(); ++lf)
659 const auto index =
f[lf];
673 auto node_ids =
nodes.node_ids_from_face(index, lf < 2 ? (p - 2) : ((q - 1) * (p - 1)));
675 res.insert(res.end(), node_ids.begin(), node_ids.end());
678 assert(res.size() ==
size_t(6 + n_edge_nodes + n_face_nodes));
681 if (n_cell_nodes > 0)
686 const auto index =
f[0];
688 auto node_ids =
nodes.node_ids_from_cell(index, q - 1);
689 res.insert(res.end(), node_ids.begin(), node_ids.end());
692 assert(res.size() ==
size_t(6 + n_edge_nodes + n_face_nodes + n_cell_nodes));
695 void pyramid_local_to_global(
const int p,
const Mesh3D &mesh,
int c,
const Eigen::VectorXi &discr_order, std::vector<int> &res,
MeshNodes &nodes)
701 res.push_back(
nodes.node_id_from_cell(c));
706 const int n_edge_nodes = 8 * (p - 1);
708 const int n_tri_face_nodes = 4 * (p - 1) * (p - 2) / 2;
710 const int n_quad_face_nodes = (p - 1) * (p - 1);
711 const int n_face_nodes = n_tri_face_nodes + n_quad_face_nodes;
713 const int total = (p + 1) * (p + 2) * (2 * p + 3) / 6;
714 const int n_cell_nodes = (p - 1) * (p - 2) * (2 * p - 3) / 6;
716 assert(total == 5 + n_edge_nodes + n_face_nodes + n_cell_nodes);
718 res.reserve(5 + n_edge_nodes + n_face_nodes + n_cell_nodes);
721 auto v = pyramid_vertices_local_to_global(mesh, c);
724 Eigen::Matrix<Navigation3D::Index, 8, 1>
e;
725 Eigen::Matrix<int, 8, 2> ev;
726 ev.row(0) << v[0], v[1];
727 ev.row(1) << v[1], v[2];
728 ev.row(2) << v[2], v[3];
729 ev.row(3) << v[3], v[0];
730 ev.row(4) << v[0], v[4];
731 ev.row(5) << v[1], v[4];
732 ev.row(6) << v[2], v[4];
733 ev.row(7) << v[3], v[4];
735 for (
int le = 0; le <
e.rows(); ++le)
741 Eigen::Matrix<Navigation3D::Index, 5, 1>
f;
742 Eigen::Matrix<int, 4, 3> fvt;
743 fvt.row(0) << v[0], v[1], v[4];
744 fvt.row(1) << v[1], v[2], v[4];
745 fvt.row(2) << v[2], v[3], v[4];
746 fvt.row(3) << v[3], v[0], v[4];
747 for (
int lf = 0; lf < fvt.rows(); ++lf)
764 auto base_idx = find_quad_face(mesh, c, v[0], v[1], v[2], v[3]);
765 for (
int rv = 0; rv < 4; ++rv)
767 if (base_idx.vertex == v[0])
771 assert(base_idx.vertex == v[0]);
776 for (
size_t lv = 0; lv < v.size(); ++lv)
778 res.push_back(
nodes.node_id_from_primitive(v[lv]));
780 assert(res.size() ==
size_t(5));
783 for (
int le = 0; le <
e.rows(); ++le)
785 const auto index =
e[le];
786 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
787 res.insert(res.end(), node_ids.begin(), node_ids.end());
789 assert(res.size() ==
size_t(5 + n_edge_nodes));
792 for (
int lf = 0; lf <
f.rows(); ++lf)
794 const auto index =
f[lf];
796 int n_loc_face = (lf < 4) ? (p - 2) : (p - 1);
797 auto node_ids =
nodes.node_ids_from_face(index, n_loc_face);
798 res.insert(res.end(), node_ids.begin(), node_ids.end());
800 assert(res.size() ==
size_t(5 + n_edge_nodes + n_face_nodes));
803 if (n_cell_nodes > 0)
805 auto node_ids =
nodes.node_ids_from_cell(f[0], p - 1);
806 res.insert(res.end(), node_ids.begin(), node_ids.end());
808 assert(res.size() ==
size_t(5 + n_edge_nodes + n_face_nodes + n_cell_nodes));
827 const Eigen::VectorXi &discr_ordersp,
828 const Eigen::VectorXi &discr_ordersq,
829 const Eigen::VectorXi &edge_orders,
830 const Eigen::VectorXi &face_orders,
831 const bool serendipity,
832 const bool has_polys,
833 const bool is_geom_bases,
835 std::vector<std::vector<int>> &edge_virtual_nodes,
836 std::vector<std::vector<int>> &face_virtual_nodes,
837 std::vector<std::vector<int>> &element_nodes_id,
838 std::vector<LocalBoundary> &local_boundary,
839 std::map<int, InterfaceData> &poly_face_to_data)
842 local_boundary.clear();
844 element_nodes_id.resize(mesh.
n_faces());
848 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
849 edge_virtual_nodes.resize(ncmesh.n_edges());
850 face_virtual_nodes.resize(ncmesh.n_faces());
853 for (
int c = 0; c < mesh.
n_cells(); ++c)
855 const int discr_order = discr_ordersp(c);
856 const int discr_orderq = discr_ordersq(c);
860 hex_local_to_global(serendipity, discr_order, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
862 auto v = hex_vertices_local_to_global(mesh, c);
863 Eigen::Matrix<int, 6, 4> fv;
864 fv.row(0) << v[0], v[3], v[4], v[7];
865 fv.row(1) << v[1], v[2], v[5], v[6];
866 fv.row(2) << v[0], v[1], v[5], v[4];
867 fv.row(3) << v[3], v[2], v[6], v[7];
868 fv.row(4) << v[0], v[1], v[2], v[3];
869 fv.row(5) << v[4], v[5], v[6], v[7];
872 for (
int i = 0; i < fv.rows(); ++i)
874 const int f = find_quad_face(mesh, c, fv(i, 0), fv(i, 1), fv(i, 2), fv(i, 3)).face;
878 lb.add_boundary_primitive(f, i);
883 local_boundary.emplace_back(lb);
888 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);
890 auto v = tet_vertices_local_to_global(mesh, c);
891 Eigen::Matrix<int, 4, 3> fv;
892 fv.row(0) << v[0], v[1], v[2];
893 fv.row(1) << v[0], v[1], v[3];
894 fv.row(2) << v[1], v[2], v[3];
895 fv.row(3) << v[2], v[0], v[3];
898 for (
long i = 0; i < fv.rows(); ++i)
904 lb.add_boundary_primitive(f, i);
909 local_boundary.emplace_back(lb);
914 prism_local_to_global(discr_order, discr_orderq, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
916 auto v = prism_vertices_local_to_global(mesh, c);
917 Eigen::Matrix<int, 2, 3> fvt;
918 fvt.row(0) << v[0], v[1], v[2];
919 fvt.row(1) << v[3], v[4], v[5];
922 for (
long i = 0; i < fvt.rows(); ++i)
928 lb.add_boundary_primitive(f, i);
932 Eigen::Matrix<int, 3, 4> fvq;
933 fvq.row(0) << v[0], v[1], v[4], v[3];
934 fvq.row(1) << v[1], v[2], v[5], v[4];
935 fvq.row(2) << v[2], v[0], v[3], v[5];
937 for (
long i = 0; i < fvq.rows(); ++i)
939 const int f = find_quad_face(mesh, c, fvq(i, 0), fvq(i, 1), fvq(i, 2), fvq(i, 3)).face;
943 lb.add_boundary_primitive(f, i + 2);
948 local_boundary.emplace_back(lb);
954 pyramid_local_to_global(discr_order, mesh, c, discr_ordersp, element_nodes_id[c], nodes);
956 auto v = pyramid_vertices_local_to_global(mesh, c);
957 Eigen::Matrix<int, 4, 3> fvt;
958 fvt.row(0) << v[0], v[1], v[4];
959 fvt.row(1) << v[1], v[2], v[4];
960 fvt.row(2) << v[2], v[3], v[4];
961 fvt.row(3) << v[3], v[0], v[4];
964 for (
long i = 0; i < fvt.rows(); ++i)
970 lb.add_boundary_primitive(f, i + 1);
974 Eigen::Matrix<int, 1, 4> fvq;
975 fvq.row(0) << v[0], v[1], v[2], v[3];
977 for (
long i = 0; i < fvq.rows(); ++i)
979 const int f = find_quad_face(mesh, c, fvq(i, 0), fvq(i, 1), fvq(i, 2), fvq(i, 3)).face;
983 lb.add_boundary_primitive(f, 0);
988 local_boundary.emplace_back(lb);
997 for (
int c = 0; c < mesh.
n_cells(); ++c)
1009 int c2 = index2.element;
1012 const int discr_order = discr_ordersp(c2);
1013 const int discr_orderq = discr_ordersq(c2);
1036 poly_face_to_data[index2.face] = data;
1046 void local_to_global(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
1048 const int dim = verts.cols();
1049 const int N = uv.rows();
1051 assert(uv.cols() == dim);
1052 assert(verts.rows() == dim + 1);
1054 pts.setZero(N, dim);
1055 for (
int i = 0; i <
N; i++)
1056 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);
1059 void local_to_global_face(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
1061 const int dim = verts.cols();
1062 const int N = uv.rows();
1064 assert(uv.cols() == 2);
1065 assert(verts.rows() == 3);
1067 pts.setZero(N, dim);
1068 for (
int i = 0; i <
N; i++)
1069 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);
1078 void global_to_local(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
1080 const int dim = verts.cols();
1081 const int N = pts.rows();
1083 assert(verts.rows() == dim + 1);
1084 assert(pts.cols() == dim);
1087 for (
int i = 0; i <
dim; i++)
1088 J.col(i) = verts.row(i + 1) - verts.row(0);
1090 Eigen::Matrix3d Jinv = J.inverse();
1094 for (
int i = start; i < end; i++)
1096 auto point = pts.row(i) - verts.row(0);
1097 uv.row(i) = Jinv * point.transpose();
1102 void global_to_local_face(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
1104 const int dim = verts.cols();
1105 const int N = pts.rows();
1107 assert(verts.rows() == 3);
1108 assert(pts.cols() == dim);
1111 for (
int i = 0; i < 2; i++)
1112 J.col(i) = verts.row(i + 1) - verts.row(0);
1114 Eigen::Vector3d a = J.col(0);
1115 Eigen::Vector3d
b = J.col(1);
1116 Eigen::Vector3d virtual_vert = a.cross(b);
1117 J.col(2) = virtual_vert;
1121 for (
int i = start; i < end; i++)
1123 auto point = pts.row(i) - verts.row(0);
1124 Eigen::Vector3d
x = J.colPivHouseholderQr().solve(point.transpose());
1125 uv.row(i) =
x.block(0, 0, 2, 1);
1126 assert(std::abs(
x(2)) < 1e-8);
1131 void global_to_local_edge(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::VectorXd &uv)
1133 const int dim = verts.cols();
1134 const int N = pts.rows();
1136 assert(verts.rows() == 2);
1137 assert(pts.cols() == dim);
1139 auto edge = verts.row(1) - verts.row(0);
1140 double squared_length = edge.squaredNorm();
1144 for (
int i = start; i < end; i++)
1146 auto vec = pts.row(i) - verts.row(0);
1147 uv(i) = (
vec.dot(edge)) / squared_length;
1152 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)
1155 for (
int i = 0; i < mesh.
n_faces(); i++)
1161 for (
int i = 0; i < mesh.
n_faces(); i++)
1168 if (edge_orders[e_id] > face_orders[i])
1174 for (
int i = 0; i < mesh.
n_edges(); i++)
1180 for (
int i = 0; i < mesh.
n_edges(); i++)
1198 void compute_edge_face_orders(
const polyfem::mesh::NCMesh3D &mesh,
const Eigen::VectorXi &elem_orders, Eigen::VectorXi &edge_orders, Eigen::VectorXi &face_orders)
1200 const int max_order = elem_orders.maxCoeff();
1201 edge_orders.setConstant(mesh.
n_edges(), max_order);
1202 face_orders.setConstant(mesh.
n_faces(), max_order);
1204 for (
int i = 0; i < mesh.
n_cells(); i++)
1206 face_orders[mesh.
cell_face(i, j)] = std::min(face_orders[mesh.
cell_face(i, j)], elem_orders[i]);
1208 for (
int i = 0; i < mesh.
n_cells(); i++)
1210 edge_orders[mesh.
cell_edge(i, j)] = std::min(edge_orders[mesh.
cell_edge(i, j)], elem_orders[i]);
1212 while (!check_edge_face_orders(mesh, elem_orders, edge_orders, face_orders))
1215 for (
int i = 0; i < mesh.
n_faces(); i++)
1219 for (
int i = 0; i < mesh.
n_faces(); i++)
1224 for (
int i = 0; i < mesh.
n_faces(); i++)
1231 edge_orders[e_id] = std::min(edge_orders[e_id], face_orders[i]);
1236 for (
int i = 0; i < mesh.
n_edges(); i++)
1240 for (
int i = 0; i < mesh.
n_edges(); i++)
1245 for (
int i = 0; i < mesh.
n_edges(); i++)
1258 const int nn = p > 2 ? (p - 2) : 0;
1259 const int n_edge_nodes = (p - 1) * 6;
1260 const int n_face_nodes = nn * (nn + 1) / 2;
1266 const auto l2g = tet_vertices_local_to_global(mesh, c);
1269 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
1270 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1274 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
1275 Eigen::Matrix<int, 6, 2> ev;
1276 ev.row(0) << l2g[0], l2g[1];
1277 ev.row(1) << l2g[1], l2g[2];
1278 ev.row(2) << l2g[2], l2g[0];
1280 ev.row(3) << l2g[0], l2g[3];
1281 ev.row(4) << l2g[1], l2g[3];
1282 ev.row(5) << l2g[2], l2g[3];
1286 for (
int le = 0; le < e.rows(); ++le)
1294 for (
int k = 0; k < 3; ++k)
1296 bool reverse =
false;
1298 for (; le < ev.rows(); ++le)
1302 const auto l_index = e[le];
1303 if (l_index.edge == tmp.edge)
1305 if (l_index.vertex == tmp.vertex)
1322 for (
int i = 0; i < p - 1; ++i)
1324 result[ii++] = 4 + le * (p - 1) + i;
1329 for (
int i = 0; i < p - 1; ++i)
1331 result[ii++] = 4 + (le + 1) * (p - 1) - i - 1;
1340 Eigen::Matrix<int, 4, 3> fv;
1341 fv.row(0) << l2g[0], l2g[1], l2g[2];
1342 fv.row(1) << l2g[0], l2g[1], l2g[3];
1343 fv.row(2) << l2g[1], l2g[2], l2g[3];
1344 fv.row(3) << l2g[2], l2g[0], l2g[3];
1347 for (; lf < fv.rows(); ++lf)
1350 if (l_index.face == index.
face)
1354 assert(lf < fv.rows());
1356 if (n_face_nodes == 0)
1359 else if (n_face_nodes == 1)
1360 result[ii++] = 4 + n_edge_nodes + lf;
1364 const auto get_order = [&p, &nn, &n_face_nodes](
const std::array<int, 3> &corners) {
1369 std::vector<int> order1(n_face_nodes);
1370 for (
int k = 0; k < n_face_nodes; ++k)
1373 std::vector<int> order2(n_face_nodes);
1376 for (
int k = 0; k < nn; ++k)
1378 for (
int l = 0; l < nn - k; ++l)
1380 order2[index] = start - l;
1383 start += (nn - 1) - k;
1386 std::vector<int> order3(n_face_nodes);
1388 for (
int k = 0; k < nn; ++k)
1391 for (
int l = 0; l < nn - k; ++l)
1393 order3[index] = offset;
1400 std::vector<int> order4(n_face_nodes);
1402 start = n_face_nodes - 1;
1403 for (
int k = 0; k < nn; ++k)
1406 for (
int l = 0; l < nn - k; ++l)
1408 order4[index] = start - offset;
1409 offset += k + 2 + l;
1416 std::vector<int> order5(n_face_nodes);
1419 for (
int k = 0; k < nn; ++k)
1422 for (
int l = 0; l < nn - k; ++l)
1424 order5[index] = start + offset;
1425 offset += nn - 1 - l;
1432 std::vector<int> order6(n_face_nodes);
1434 start = n_face_nodes;
1435 for (
int k = 0; k < nn; ++k)
1438 start = start - k - 1;
1439 for (
int l = 0; l < nn - k; ++l)
1441 order6[index] = start - offset;
1442 offset += l + 1 + k;
1447 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
1449 assert(corners[2] == order1[n_face_nodes - 1]);
1453 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
1455 assert(corners[2] == order2[n_face_nodes - 1]);
1459 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
1461 assert(corners[2] == order3[n_face_nodes - 1]);
1465 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
1467 assert(corners[2] == order4[n_face_nodes - 1]);
1471 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
1473 assert(corners[2] == order5[n_face_nodes - 1]);
1477 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
1479 assert(corners[2] == order6[n_face_nodes - 1]);
1487 Eigen::MatrixXd nodes;
1493 std::array<int, 3> idx;
1494 for (
int lv = 0; lv < 3; ++lv)
1496 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1499 Eigen::Matrix3d pos(3, 3);
1503 pos.row(cnt++) = nodes.row(i);
1506 const Eigen::RowVector3d bary = pos.colwise().mean();
1508 const int offset = 4 + n_edge_nodes;
1510 for (
int lff = 0; lff < 4; ++lff)
1512 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
1513 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1515 if ((node_bary - bary).norm() < 1e-10)
1517 std::array<int, 3> corners;
1519 for (
int m = 0; m < 3; ++m)
1521 auto t = pos.row(m);
1523 double min_dis = 10000;
1525 for (
int n = 0; n < n_face_nodes; ++n)
1527 double dis = (loc_nodes.row(n) - t).squaredNorm();
1536 assert(min_n < n_face_nodes);
1540 const auto indices = get_order(corners);
1541 for (
int min_n : indices)
1544 result[ii++] = 4 + n_edge_nodes + min_n + lf * n_face_nodes;
1547 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
1563 assert(ii == result.size());
1569 const int nn = q - 1;
1570 const int n_edge_nodes = nn * 12;
1571 const int n_face_nodes = serendipity ? 0 : nn * nn;
1577 const auto l2g = hex_vertices_local_to_global(mesh, c);
1580 Eigen::VectorXi result(4 + nn * 4 + n_face_nodes);
1581 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1586 Eigen::Matrix<Navigation3D::Index, 12, 1> e;
1587 Eigen::Matrix<int, 12, 2> ev;
1588 ev.row(0) << l2g[0], l2g[1];
1589 ev.row(1) << l2g[1], l2g[2];
1590 ev.row(2) << l2g[2], l2g[3];
1591 ev.row(3) << l2g[3], l2g[0];
1592 ev.row(4) << l2g[0], l2g[4];
1593 ev.row(5) << l2g[1], l2g[5];
1594 ev.row(6) << l2g[2], l2g[6];
1595 ev.row(7) << l2g[3], l2g[7];
1596 ev.row(8) << l2g[4], l2g[5];
1597 ev.row(9) << l2g[5], l2g[6];
1598 ev.row(10) << l2g[6], l2g[7];
1599 ev.row(11) << l2g[7], l2g[4];
1603 for (
int le = 0; le < e.rows(); ++le)
1610 for (
int k = 0; k < 4; ++k)
1612 bool reverse =
false;
1614 for (; le < ev.rows(); ++le)
1618 const auto l_index = e[le];
1619 if (l_index.edge == tmp.edge)
1621 if (l_index.vertex == tmp.vertex)
1637 for (
int i = 0; i < q - 1; ++i)
1639 result[ii++] = 8 + le * (q - 1) + i;
1644 for (
int i = 0; i < q - 1; ++i)
1646 result[ii++] = 8 + (le + 1) * (q - 1) - i - 1;
1655 Eigen::Matrix<int, 6, 4> fv;
1656 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[7];
1657 fv.row(1) << l2g[1], l2g[2], l2g[5], l2g[6];
1658 fv.row(2) << l2g[0], l2g[1], l2g[5], l2g[4];
1659 fv.row(3) << l2g[3], l2g[2], l2g[6], l2g[7];
1660 fv.row(4) << l2g[0], l2g[1], l2g[2], l2g[3];
1661 fv.row(5) << l2g[4], l2g[5], l2g[6], l2g[7];
1664 for (; lf < fv.rows(); ++lf)
1666 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
1667 if (l_index.face == index.
face)
1671 assert(lf < fv.rows());
1673 if (n_face_nodes == 1)
1674 result[ii++] = 8 + n_edge_nodes + lf;
1675 else if (n_face_nodes != 0)
1677 Eigen::MatrixXd nodes;
1683 std::array<int, 4> idx;
1684 for (
int lv = 0; lv < 4; ++lv)
1686 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1689 Eigen::Matrix<double, 4, 3> pos(4, 3);
1693 pos.row(cnt++) = nodes.row(i);
1696 const Eigen::RowVector3d bary = pos.colwise().mean();
1698 const int offset = 8 + n_edge_nodes;
1700 for (
int lff = 0; lff < 6; ++lff)
1702 Eigen::Matrix<double, 4, 3> loc_nodes = nodes.block<4, 3>(offset + lff * n_face_nodes, 0);
1703 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1705 if ((node_bary - bary).norm() < 1e-10)
1708 for (
int m = 0; m < 4; ++m)
1710 auto t = pos.row(m);
1712 double min_dis = 10000;
1714 for (
int n = 0; n < 4; ++n)
1716 double dis = (loc_nodes.row(n) - t).squaredNorm();
1729 result[ii++] = 8 + n_edge_nodes + min_n + lf * n_face_nodes;
1745 assert(ii == result.size());
1755 const auto l2g = prism_vertices_local_to_global(mesh, c);
1757 const int global_n_edges_nodes = (p - 1) * 6 + (q - 1) * 3;
1761 const int nn = p > 2 ? (p - 2) : 0;
1762 const int n_edge_nodes = (p - 1) * 6;
1763 const int n_face_nodes = nn * (nn + 1) / 2;
1766 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
1767 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1771 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
1772 Eigen::Matrix<int, 6, 2> ev;
1773 ev.row(0) << l2g[0], l2g[1];
1774 ev.row(1) << l2g[1], l2g[2];
1775 ev.row(2) << l2g[2], l2g[0];
1777 ev.row(3) << l2g[3], l2g[4];
1778 ev.row(4) << l2g[4], l2g[5];
1779 ev.row(5) << l2g[5], l2g[3];
1783 for (
int le = 0; le < e.rows(); ++le)
1791 for (
int k = 0; k < 3; ++k)
1793 bool reverse =
false;
1795 for (; le < ev.rows(); ++le)
1799 const auto l_index = e[le];
1800 if (l_index.edge == tmp.edge)
1802 if (l_index.vertex == tmp.vertex)
1819 for (
int i = 0; i < p - 1; ++i)
1821 result[ii++] = 6 + le * (p - 1) + i;
1826 for (
int i = 0; i < p - 1; ++i)
1828 result[ii++] = 6 + (le + 1) * (p - 1) - i - 1;
1837 Eigen::Matrix<int, 2, 3> fv;
1838 fv.row(0) << l2g[0], l2g[1], l2g[2];
1839 fv.row(1) << l2g[3], l2g[4], l2g[5];
1842 for (; lf < fv.rows(); ++lf)
1845 if (l_index.face == index.
face)
1849 assert(lf < fv.rows());
1851 if (n_face_nodes == 0)
1854 else if (n_face_nodes == 1)
1855 result[ii++] = 6 + global_n_edges_nodes + lf;
1859 const auto get_order = [&p, &q, &nn, &n_face_nodes](
const std::array<int, 3> &corners) {
1864 std::vector<int> order1(n_face_nodes);
1865 for (
int k = 0; k < n_face_nodes; ++k)
1868 std::vector<int> order2(n_face_nodes);
1871 for (
int k = 0; k < nn; ++k)
1873 for (
int l = 0; l < nn - k; ++l)
1875 order2[index] = start - l;
1878 start += (nn - 1) - k;
1881 std::vector<int> order3(n_face_nodes);
1883 for (
int k = 0; k < nn; ++k)
1886 for (
int l = 0; l < nn - k; ++l)
1888 order3[index] = offset;
1895 std::vector<int> order4(n_face_nodes);
1897 start = n_face_nodes - 1;
1898 for (
int k = 0; k < nn; ++k)
1901 for (
int l = 0; l < nn - k; ++l)
1903 order4[index] = start - offset;
1904 offset += k + 2 + l;
1911 std::vector<int> order5(n_face_nodes);
1914 for (
int k = 0; k < nn; ++k)
1917 for (
int l = 0; l < nn - k; ++l)
1919 order5[index] = start + offset;
1920 offset += nn - 1 - l;
1927 std::vector<int> order6(n_face_nodes);
1929 start = n_face_nodes;
1930 for (
int k = 0; k < nn; ++k)
1933 start = start - k - 1;
1934 for (
int l = 0; l < nn - k; ++l)
1936 order6[index] = start - offset;
1937 offset += l + 1 + k;
1942 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
1944 assert(corners[2] == order1[n_face_nodes - 1]);
1948 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
1950 assert(corners[2] == order2[n_face_nodes - 1]);
1954 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
1956 assert(corners[2] == order3[n_face_nodes - 1]);
1960 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
1962 assert(corners[2] == order4[n_face_nodes - 1]);
1966 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
1968 assert(corners[2] == order5[n_face_nodes - 1]);
1972 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
1974 assert(corners[2] == order6[n_face_nodes - 1]);
1982 Eigen::MatrixXd nodes;
1988 std::array<int, 3> idx;
1989 for (
int lv = 0; lv < 3; ++lv)
1991 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1994 Eigen::Matrix3d pos(3, 3);
1998 pos.row(cnt++) = nodes.row(i);
2001 const Eigen::RowVector3d bary = pos.colwise().mean();
2003 const int offset = 6 + global_n_edges_nodes;
2005 for (
int lff = 0; lff < 2; ++lff)
2007 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
2008 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
2010 if ((node_bary - bary).norm() < 1e-10)
2012 std::array<int, 3> corners;
2014 for (
int m = 0; m < 3; ++m)
2016 auto t = pos.row(m);
2018 double min_dis = 10000;
2020 for (
int n = 0; n < n_face_nodes; ++n)
2022 double dis = (loc_nodes.row(n) - t).squaredNorm();
2031 assert(min_n < n_face_nodes);
2035 const auto indices = get_order(corners);
2036 for (
int min_n : indices)
2039 result[ii++] = 6 + global_n_edges_nodes + min_n + lf * n_face_nodes;
2042 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
2058 assert(ii == result.size());
2063 const int nn = q - 1;
2064 const int n_edge_nodes = nn * 12;
2065 const int n_face_nodes = nn * nn;
2068 Eigen::VectorXi result(4 + nn * 4 + n_face_nodes);
2069 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2074 Eigen::Matrix<Navigation3D::Index, 9, 1> e;
2075 Eigen::Matrix<int, 9, 2> ev;
2076 ev.row(0) << l2g[0], l2g[1];
2077 ev.row(1) << l2g[1], l2g[2];
2078 ev.row(2) << l2g[2], l2g[0];
2080 ev.row(3) << l2g[3], l2g[4];
2081 ev.row(4) << l2g[4], l2g[5];
2082 ev.row(5) << l2g[5], l2g[3];
2084 ev.row(6) << l2g[0], l2g[3];
2085 ev.row(7) << l2g[1], l2g[4];
2086 ev.row(8) << l2g[2], l2g[5];
2090 for (
int le = 0; le < e.rows(); ++le)
2097 for (
int k = 0; k < 4; ++k)
2099 bool reverse =
false;
2101 for (; le < ev.rows(); ++le)
2105 const auto l_index = e[le];
2106 if (l_index.edge == tmp.edge)
2108 if (l_index.vertex == tmp.vertex)
2124 for (
int i = 0; i < q - 1; ++i)
2126 result[ii++] = 6 + le * (q - 1) + i;
2131 for (
int i = 0; i < q - 1; ++i)
2133 result[ii++] = 6 + (le + 1) * (q - 1) - i - 1;
2142 Eigen::Matrix<int, 3, 4> fv;
2143 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[1];
2144 fv.row(1) << l2g[1], l2g[4], l2g[5], l2g[2];
2145 fv.row(2) << l2g[2], l2g[5], l2g[3], l2g[0];
2148 for (; lf < fv.rows(); ++lf)
2150 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
2151 if (l_index.face == index.
face)
2155 assert(lf < fv.rows());
2157 if (n_face_nodes == 1)
2158 result[ii++] = 6 + global_n_edges_nodes + lf;
2159 else if (n_face_nodes != 0)
2161 Eigen::MatrixXd nodes;
2167 std::array<int, 4> idx;
2168 for (
int lv = 0; lv < 4; ++lv)
2170 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2173 Eigen::Matrix<double, 4, 3> pos(4, 3);
2177 pos.row(cnt++) = nodes.row(i);
2180 const Eigen::RowVector3d bary = pos.colwise().mean();
2182 const int offset = 8 + n_edge_nodes;
2184 for (
int lff = 0; lff < 6; ++lff)
2186 Eigen::Matrix<double, 4, 3> loc_nodes = nodes.block<4, 3>(offset + lff * n_face_nodes, 0);
2187 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
2189 if ((node_bary - bary).norm() < 1e-10)
2192 for (
int m = 0; m < 4; ++m)
2194 auto t = pos.row(m);
2196 double min_dis = 10000;
2198 for (
int n = 0; n < 4; ++n)
2200 double dis = (loc_nodes.row(n) - t).squaredNorm();
2213 result[ii++] = 8 + n_edge_nodes + min_n + lf * n_face_nodes;
2229 assert(ii == result.size());
2240 const auto l2g = pyramid_vertices_local_to_global(mesh, c);
2241 const auto &v = l2g;
2244 Eigen::Matrix<int, 8, 2> ev;
2245 ev.row(0) << v[0], v[1];
2246 ev.row(1) << v[1], v[2];
2247 ev.row(2) << v[2], v[3];
2248 ev.row(3) << v[3], v[0];
2249 ev.row(4) << v[0], v[4];
2250 ev.row(5) << v[1], v[4];
2251 ev.row(6) << v[2], v[4];
2252 ev.row(7) << v[3], v[4];
2254 Eigen::Matrix<Navigation3D::Index, 8, 1> e;
2255 for (
int le = 0; le < 8; ++le)
2258 const int nei = p - 1;
2259 const int nfi_tri = (p - 1) * (p - 2) / 2;
2260 const int nfi_quad = (p - 1) * (p - 1);
2267 const int edge_start = 5;
2268 const int tri_face_start = edge_start + 8 * nei;
2269 const int quad_face_start = tri_face_start + 4 * nfi_tri;
2273 auto append_edge_dofs = [&](Eigen::VectorXi &result,
int &ii,
const Navigation3D::Index &edge_idx) {
2277 for (; le < 8; ++le)
2279 if (e[le].edge == edge_idx.edge)
2283 const bool forward = (edge_idx.vertex == ev(le, 0));
2284 for (
int q = 0; q < nei; ++q)
2286 const int local_q = forward ? q : (nei - 1 - q);
2287 result[ii++] = edge_start + le * nei + local_q;
2296 Eigen::VectorXi result(3 + 3 * nei + nfi_tri);
2300 result[ii++] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2306 for (
int k = 0; k < 3; ++k)
2308 append_edge_dofs(result, ii, tmp);
2315 static const int tri_fv[4][3] = {{0, 1, 4}, {1, 2, 4}, {2, 3, 4}, {3, 0, 4}};
2316 const int fv0 = index.
vertex;
2320 for (
int f = 0; f < 4; ++f)
2322 const int gv0 = v[tri_fv[f][0]], gv1 = v[tri_fv[f][1]], gv2 = v[tri_fv[f][2]];
2323 if ((fv0 == gv0 || fv0 == gv1 || fv0 == gv2) && (fv1 == gv0 || fv1 == gv1 || fv1 == gv2) && (fv2 == gv0 || fv2 == gv1 || fv2 == gv2))
2330 for (
int q = 0; q < nfi_tri; ++q)
2331 result[ii++] = tri_face_start + lf * nfi_tri + q;
2334 assert(ii == result.size());
2341 Eigen::VectorXi result(4 + 4 * nei + nfi_quad);
2345 result[ii++] = find_index(l2g.begin(), l2g.end(), index.
vertex);
2352 for (
int k = 0; k < 4; ++k)
2354 append_edge_dofs(result, ii, tmp);
2359 for (
int q = 0; q < nfi_quad; ++q)
2360 result[ii++] = quad_face_start + q;
2362 assert(ii == result.size());
2369 const std::string &assembler,
2370 const int quadrature_order,
2371 const int mass_quadrature_order,
2372 const int discr_orderp,
2373 const int discr_orderq,
2374 const bool bernstein,
2375 const bool serendipity,
2376 const bool has_polys,
2377 const bool is_geom_bases,
2378 const bool use_corner_quadrature,
2379 std::vector<ElementBases> &bases,
2380 std::vector<LocalBoundary> &local_boundary,
2381 std::map<int, InterfaceData> &poly_face_to_data,
2382 std::shared_ptr<MeshNodes> &mesh_nodes)
2384 Eigen::VectorXi discr_ordersp(mesh.
n_cells());
2385 discr_ordersp.setConstant(discr_orderp);
2387 Eigen::VectorXi discr_ordersq(mesh.
n_cells());
2388 discr_ordersq.setConstant(discr_orderq);
2390 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);
2395 const std::string &assembler,
2396 const int quadrature_order,
2397 const int mass_quadrature_order,
2398 const Eigen::VectorXi &discr_ordersp,
2399 const Eigen::VectorXi &discr_ordersq,
2400 const bool bernstein,
2401 const bool serendipity,
2402 const bool has_polys,
2403 const bool is_geom_bases,
2404 const bool use_corner_quadrature,
2405 std::vector<ElementBases> &bases,
2406 std::vector<LocalBoundary> &local_boundary,
2407 std::map<int, InterfaceData> &poly_face_to_data,
2408 std::shared_ptr<MeshNodes> &mesh_nodes)
2411 assert(discr_ordersp.size() == mesh.
n_cells());
2412 assert(discr_ordersq.size() == mesh.
n_cells());
2420 const int max_p = discr_ordersp.maxCoeff();
2421 const int max_q = discr_ordersq.maxCoeff();
2422 const int mmax = std::max(max_p, max_q);
2425 const int nn = mmax > 1 ? (mmax - 1) : 0;
2426 const int n_face_nodes = nn * nn;
2427 const int n_cells_nodes = nn * nn * nn;
2429 Eigen::VectorXi edge_orders, face_orders;
2432 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
2433 compute_edge_face_orders(ncmesh, discr_ordersp, edge_orders, face_orders);
2436 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);
2438 std::vector<std::vector<int>> element_nodes_id, edge_virtual_nodes, face_virtual_nodes;
2439 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);
2443 std::vector<int> interface_elements;
2444 interface_elements.reserve(mesh.
n_faces());
2446 for (
int e = 0; e < mesh.
n_cells(); ++e)
2449 const int discr_order = discr_ordersp(e);
2450 const int discr_orderq = discr_ordersq(e);
2451 const int n_el_bases = (int)element_nodes_id[e].size();
2452 b.bases.resize(n_el_bases);
2454 bool skip_interface_element =
false;
2456 for (
int j = 0; j < n_el_bases; ++j)
2458 const int global_index = element_nodes_id[e][j];
2459 if (global_index < 0)
2461 skip_interface_element =
true;
2466 if (skip_interface_element)
2468 interface_elements.push_back(e);
2473 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
2474 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
2479 b.set_mass_quadrature([real_mass_order](
Quadrature &quad) {
2484 b.set_local_node_from_primitive_func([serendipity, discr_order, e](
const int primitive_id,
const Mesh &mesh) {
2485 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
2488 for (
int lf = 0; lf < 6; ++lf)
2490 index = mesh3d.get_index_from_element(e, lf, 0);
2491 if (index.
face == primitive_id)
2494 assert(index.
face == primitive_id);
2498 for (
int j = 0; j < n_el_bases; ++j)
2500 const int global_index = element_nodes_id[
e][j];
2502 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2504 const int dtmp = serendipity ? -2 : discr_order;
2512 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
2513 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
2515 b.set_quadrature([real_order, use_corner_quadrature](
Quadrature &quad) {
2517 tet_quadrature.get_quadrature(real_order, quad);
2519 b.set_mass_quadrature([real_mass_order, use_corner_quadrature](
Quadrature &quad) {
2521 tet_quadrature.get_quadrature(real_mass_order, quad);
2524 b.set_local_node_from_primitive_func([discr_order, e](
const int primitive_id,
const Mesh &mesh) {
2525 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
2528 for (
int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2530 index = mesh3d.get_index_from_element(e, lf, 0);
2531 if (index.
face == primitive_id)
2534 assert(index.
face == primitive_id);
2541 for (
int j = 0; j < n_el_bases; ++j)
2543 const int global_index = element_nodes_id[
e][j];
2544 if (!skip_interface_element)
2546 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2549 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); });
2555 const int orderp = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::PRISM_LAGRANGE, 2);
2556 const int orderq = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_orderq, AssemblerUtils::BasisType::PRISM_LAGRANGE, 1);
2558 const int mass_orderp = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::PRISM_LAGRANGE, 2);
2559 const int mass_orderq = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_orderq, AssemblerUtils::BasisType::PRISM_LAGRANGE, 1);
2561 b.set_quadrature([orderp, orderq](
Quadrature &quad) {
2565 b.set_mass_quadrature([mass_orderp, mass_orderq](
Quadrature &quad) {
2570 b.set_local_node_from_primitive_func([discr_order, discr_orderq, e](
const int primitive_id,
const Mesh &mesh) {
2571 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
2574 for (
int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2576 index = mesh3d.get_index_from_element(e, lf, 0);
2577 if (index.
face == primitive_id)
2580 assert(index.
face == primitive_id);
2584 for (
int j = 0; j < n_el_bases; ++j)
2586 const int global_index = element_nodes_id[
e][j];
2587 if (!skip_interface_element)
2589 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2592 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); });
2598 const int orderp = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::PYRAMID_LAGRANGE, 2);
2599 const int mass_orderp = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::PYRAMID_LAGRANGE, 2);
2605 b.set_mass_quadrature([mass_orderp](
Quadrature &quad) {
2610 b.set_local_node_from_primitive_func([discr_order, e](
const int primitive_id,
const Mesh &mesh) {
2611 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
2614 for (
int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
2616 index = mesh3d.get_index_from_element(e, lf, 0);
2617 if (index.
face == primitive_id)
2620 assert(index.
face == primitive_id);
2624 for (
int j = 0; j < n_el_bases; ++j)
2626 const int global_index = element_nodes_id[
e][j];
2627 if (!skip_interface_element)
2629 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2647 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
2649 std::vector<std::vector<int>> elementOrder;
2651 const int max_order = discr_ordersp.maxCoeff(), min_order = discr_ordersp.minCoeff();
2653 for (
int e = 0;
e < ncmesh.n_cells();
e++)
2654 if (max_level < ncmesh.cell_ref_level(e))
2655 max_level = ncmesh.cell_ref_level(e);
2657 elementOrder.resize((max_level + 1) * (
max_order - min_order + 1));
2660 while (cur_level <= max_level)
2662 int order = min_order;
2663 while (order <= max_order)
2665 int cur_bucket = (
max_order - min_order + 1) * cur_level + (order - min_order);
2666 for (
int i = 0; i < ncmesh.n_cells(); i++)
2668 if (ncmesh.cell_ref_level(i) != cur_level || discr_ordersp[i] != order)
2672 elementOrder[cur_bucket].push_back(i);
2680 for (
const auto &bucket : elementOrder)
2682 if (bucket.size() == 0)
2685 for (int e_aux = start; e_aux < end; e_aux++)
2687 const int e = bucket[e_aux];
2688 ElementBases &b = bases[e];
2689 const int discr_order = discr_ordersp(e);
2690 const int n_edge_nodes = discr_order - 1;
2691 const int n_face_nodes = (discr_order - 1) * (discr_order - 2) / 2;
2692 const int n_el_bases = element_nodes_id[e].size();
2694 auto v = tet_vertices_local_to_global(mesh, e);
2696 Eigen::Matrix<Navigation3D::Index, 4, 1> cell_faces;
2697 Eigen::Matrix<int, 4, 3> fv;
2698 fv.row(0) << v[0], v[1], v[2];
2699 fv.row(1) << v[0], v[1], v[3];
2700 fv.row(2) << v[1], v[2], v[3];
2701 fv.row(3) << v[2], v[0], v[3];
2703 for (long lf = 0; lf < fv.rows(); ++lf)
2705 const auto index = mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2));
2706 cell_faces[lf] = index;
2709 Eigen::Matrix<Navigation3D::Index, 6, 1> cell_edges;
2710 Eigen::Matrix<int, 6, 2> ev;
2711 ev.row(0) << v[0], v[1];
2712 ev.row(1) << v[1], v[2];
2713 ev.row(2) << v[2], v[0];
2715 ev.row(3) << v[0], v[3];
2716 ev.row(4) << v[1], v[3];
2717 ev.row(5) << v[2], v[3];
2719 for (int le = 0; le < ev.rows(); ++le)
2722 const auto index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
2723 cell_edges[le] = index;
2726 Eigen::MatrixXd verts(4, 3);
2727 for (int i = 0; i < ncmesh.n_cell_vertices(e); i++)
2728 verts.row(i) = ncmesh.point(v[i]);
2730 for (int j = 0; j < n_el_bases; ++j)
2732 const int global_index = element_nodes_id[e][j];
2734 if (global_index >= 0)
2736 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
2743 int large_elem = -1;
2744 if (ncmesh.leader_edge_of_vertex(v[j]) >= 0)
2746 large_elem = lowest_order_elem_on_edge(ncmesh, discr_ordersp, ncmesh.leader_edge_of_vertex(v[j]));
2748 else if (ncmesh.leader_face_of_vertex(v[j]) >= 0)
2750 std::vector<int> ids;
2751 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_vertex(v[j]), ids);
2752 assert(ids.size() == 1);
2753 large_elem = ids[0];
2758 Eigen::MatrixXd large_elem_verts(4, 3);
2759 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
2760 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
2761 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
2763 Eigen::MatrixXd node_position;
2764 global_to_local(large_elem_verts, verts.row(j), node_position);
2767 const auto &other_bases = bases[large_elem];
2768 std::vector<AssemblyValues> w;
2769 other_bases.evaluate_bases(node_position, w);
2772 for (long i = 0; i < w.size(); ++i)
2774 assert(w[i].val.size() == 1);
2775 if (std::abs(w[i].val(0)) < 1e-12)
2778 assert(other_bases.bases[i].global().size() > 0);
2779 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
2781 const auto &other_global = other_bases.bases[i].global()[ii];
2782 assert(other_global.index >= 0);
2783 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
2788 else if (j < 4 + 6 * n_edge_nodes)
2790 const int local_edge_id = (j - 4) / n_edge_nodes;
2791 const int edge_id = cell_edges[local_edge_id].edge;
2792 bool need_extra_fake_nodes = false;
2793 int large_elem = -1;
2796 if (ncmesh.leader_edge_of_edge(edge_id) >= 0)
2798 std::vector<int> ids;
2799 ncmesh.get_edge_elements_neighs(ncmesh.leader_edge_of_edge(edge_id), ids);
2800 large_elem = ids[0];
2803 else if (ncmesh.leader_face_of_edge(edge_id) >= 0)
2805 std::vector<int> ids;
2806 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_edge(edge_id), ids);
2807 assert(ids.size() == 1);
2808 large_elem = ids[0];
2811 else if (discr_order > edge_orders[edge_id])
2813 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_ordersp, edge_id);
2815 if (discr_ordersp[min_order_elem] < discr_order)
2816 large_elem = min_order_elem;
2822 need_extra_fake_nodes = true;
2828 assert(large_elem >= 0 || need_extra_fake_nodes);
2829 Eigen::MatrixXd lnodes;
2830 autogen::p_nodes_3d(discr_order, lnodes);
2831 Eigen::MatrixXd local_position = lnodes.row(j);
2832 if (need_extra_fake_nodes)
2834 Eigen::MatrixXd global_position, edge_verts(2, 3);
2835 Eigen::VectorXd point_weight;
2837 edge_verts.row(0) = ncmesh.point(ncmesh.edge_vertex(edge_id, 0));
2838 edge_verts.row(1) = ncmesh.point(ncmesh.edge_vertex(edge_id, 1));
2840 local_to_global(verts, local_position, global_position);
2841 global_to_local_edge(edge_verts, global_position, point_weight);
2843 std::function<double(const int, const int, const double)> basis_1d = [](const int order, const int id, const double x) -> double {
2844 assert(id <= order && id >= 0);
2846 for (int o = 0; o <= order; o++)
2849 y *= (x * order - o) / (id - o);
2855 for (int i = 0; i < edge_virtual_nodes[edge_id].size(); i++)
2857 const int global_index = edge_virtual_nodes[edge_id][i];
2859 Eigen::VectorXd node_weight;
2860 global_to_local_edge(edge_verts, nodes.node_position(global_index), node_weight);
2861 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
2862 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
2863 if (std::abs(weight) < 1e-12)
2865 b.bases[j].global().emplace_back(global_index, nodes.node_position(global_index), weight);
2869 for (int i = 0; i < 2; i++)
2871 const int lv = ev(local_edge_id, i);
2872 const auto &global_ = b.bases[lv].global();
2873 Eigen::VectorXd node_weight;
2874 global_to_local_edge(edge_verts, verts.row(lv), node_weight);
2875 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
2876 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
2877 if (std::abs(weight) > 1e-12)
2879 assert(global_.size() > 0);
2880 for (size_t ii = 0; ii < global_.size(); ++ii)
2881 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
2887 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
2888 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
2889 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
2890 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
2891 local_to_global(verts, local_position, global_position);
2892 global_to_local(large_elem_verts, global_position, local_position);
2895 const auto &other_bases = bases[large_elem];
2896 std::vector<AssemblyValues> w;
2897 other_bases.evaluate_bases(local_position, w);
2900 for (long i = 0; i < w.size(); ++i)
2902 assert(w[i].val.size() == 1);
2903 if (std::abs(w[i].val(0)) < 1e-12)
2906 assert(other_bases.bases[i].global().size() > 0);
2907 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
2909 const auto &other_global = other_bases.bases[i].global()[ii];
2910 assert(other_global.index >= 0);
2911 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
2917 else if (j < 4 + 6 * n_edge_nodes + 4 * n_face_nodes)
2919 const int local_face_id = (j - (4 + 6 * n_edge_nodes)) / n_face_nodes;
2920 const int face_id = cell_faces(local_face_id).face;
2921 int large_elem = -1;
2922 bool need_extra_fake_nodes = false;
2924 std::vector<int> ids;
2925 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_face(face_id), ids);
2927 Eigen::MatrixXd face_verts(3, 3);
2928 for (int i = 0; i < ncmesh.n_face_vertices(face_id); i++)
2929 face_verts.row(i) = ncmesh.point(ncmesh.face_vertex(face_id, i));
2932 if (ncmesh.leader_face_of_face(face_id) >= 0)
2934 assert(ids.size() == 1);
2935 large_elem = ids[0];
2939 else if (face_orders[face_id] < discr_order && ids.size() == 2)
2941 large_elem = ids[0] == e ? ids[1] : ids[0];
2944 else if (face_orders[face_id] < discr_order && ncmesh.n_follower_faces(face_id) > 0)
2947 need_extra_fake_nodes = true;
2952 assert(large_elem >= 0 || need_extra_fake_nodes);
2953 Eigen::MatrixXd lnodes;
2954 autogen::p_nodes_3d(discr_order, lnodes);
2955 Eigen::MatrixXd local_position = lnodes.row(j);
2956 if (need_extra_fake_nodes)
2958 Eigen::MatrixXd global_position;
2959 local_to_global(verts, local_position, global_position);
2961 Eigen::MatrixXd tmp;
2962 global_to_local_face(face_verts, global_position, tmp);
2963 Eigen::VectorXd face_weight = tmp.transpose();
2965 std::function<double(const int, const int, const double)> basis_aux = [](const int order, const int id, const double x) -> double {
2966 assert(id <= order && id >= 0);
2968 for (int o = 0; o < id; o++)
2969 y *= (x * order - o) / (id - o);
2973 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 {
2974 assert(i + j <= order && i >= 0 && j >= 0);
2975 double u = uv(0), v = uv(1);
2976 return basis_aux(order, i, u) * basis_aux(order, j, v) * basis_aux(order, order - i - j, 1 - u - v);
2980 for (int global_ : face_virtual_nodes[face_id])
2982 auto low_order_node = nodes.node_position(global_);
2983 Eigen::MatrixXd low_order_node_face_weight;
2984 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
2985 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]);
2986 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
2987 if (std::abs(weight) < 1e-12)
2989 b.bases[j].global().emplace_back(global_, nodes.node_position(global_), weight);
2993 for (int i = 0; i < 3; i++)
2995 const auto &global_ = b.bases[fv(local_face_id, i)].global();
2996 auto low_order_node = ncmesh.point(fv(local_face_id, i));
2997 Eigen::MatrixXd low_order_node_face_weight;
2998 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
2999 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]);
3000 double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3001 if (std::abs(weight) > 1e-12)
3003 assert(global_.size() > 0);
3004 for (size_t ii = 0; ii < global_.size(); ++ii)
3005 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
3010 for (int x = 0, idx = 0; x <= face_orders[face_id]; x++)
3012 for (int y = 0; x + y <= face_orders[face_id]; y++)
3014 const int z = face_orders[face_id] - x - y;
3015 int flag = (int)(x == 0) + (int)(y == 0) + (int)(z == 0);
3020 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
3021 if (std::abs(weight) < 1e-12)
3023 Eigen::MatrixXd face_weight(1, 2);
3024 face_weight << (double)x / face_orders[face_id], (double)y / face_orders[face_id];
3025 Eigen::MatrixXd pos, local_pos;
3026 local_to_global_face(face_verts, face_weight, pos);
3027 global_to_local(verts, pos, local_pos);
3028 Local2Global step1(idx, local_pos, weight);
3033 const auto &other_bases = bases[e];
3034 std::vector<AssemblyValues> w;
3035 other_bases.evaluate_bases(local_pos, w);
3038 for (long i = 0; i < w.size(); ++i)
3040 assert(w[i].val.size() == 1);
3041 if (std::abs(w[i].val(0)) < 1e-12)
3044 assert(other_bases.bases[i].global().size() > 0);
3045 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3047 const auto &other_global = other_bases.bases[i].global()[ii];
3048 assert(other_global.index >= 0);
3049 b.bases[j].global().emplace_back(other_global.index, other_global.node, step1.val * w[i].val(0) * other_global.val);
3058 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
3059 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
3060 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
3061 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
3062 local_to_global(verts, local_position, global_position);
3063 global_to_local(large_elem_verts, global_position, local_position);
3066 const auto &other_bases = bases[large_elem];
3067 std::vector<AssemblyValues> w;
3068 other_bases.evaluate_bases(local_position, w);
3071 for (long i = 0; i < w.size(); ++i)
3073 assert(w[i].val.size() == 1);
3074 if (std::abs(w[i].val(0)) < 1e-12)
3077 assert(other_bases.bases[i].global().size() > 0);
3078 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3080 const auto &other_global = other_bases.bases[i].global()[ii];
3081 assert(other_global.index >= 0);
3082 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3090 auto &global_ = b.bases[j].global();
3091 if (global_.size() <= 1)
3094 std::map<int, Local2Global> list;
3095 for (size_t ii = 0; ii < global_.size(); ii++)
3097 auto pair = list.insert({global_[ii].index, global_[ii]});
3098 if (!pair.second && pair.first != list.end())
3100 assert((pair.first->second.node - global_[ii].node).norm() < 1e-12);
3101 pair.first->second.val += global_[ii].val;
3106 for (auto it = list.begin(); it != list.end(); ++it)
3108 if (std::abs(it->second.val) > 1e-12)
3110 global_.push_back(it->second);
3121 for (
int pp = 2; pp <= autogen::MAX_P_BASES; ++pp)
3123 for (
int e : interface_elements)
3127 const int discr_order = discr_ordersp(e);
3128 const int n_el_bases = element_nodes_id[
e].size();
3129 assert(discr_order > 1);
3130 if (discr_order != pp)
3140 for (
int j = 0; j < n_el_bases; ++j)
3142 const int global_index = element_nodes_id[
e][j];
3144 if (global_index >= 0)
3145 b.bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
3148 const int lnn =
max_p > 2 ? (discr_order - 2) : 0;
3149 const int ln_edge_nodes = discr_order - 1;
3150 const int ln_face_nodes = lnn * (lnn + 1) / 2;
3152 const auto v = tet_vertices_local_to_global(mesh, e);
3154 if (global_index <= -30)
3178 else if (global_index <= -10)
3180 const auto le = -(global_index + 10);
3181 assert(le >= 0 && le < 6);
3182 assert(j >= 4 && j < 4 + 6 * ln_edge_nodes);
3184 Eigen::Matrix<int, 6, 2> ev;
3185 ev.row(0) << v[0], v[1];
3186 ev.row(1) << v[1], v[2];
3187 ev.row(2) << v[2], v[0];
3189 ev.row(3) << v[0], v[3];
3190 ev.row(4) << v[1], v[3];
3191 ev.row(5) << v[2], v[3];
3194 const auto edge_index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
3195 auto neighs = mesh.edge_neighs(edge_index.edge);
3196 int min_p = discr_order;
3197 int min_cell = edge_index.element;
3199 for (
auto cid : neighs)
3201 if (discr_ordersp[cid] < min_p)
3203 min_p = discr_ordersp[cid];
3209 for (
int lf = 0; lf < 4; ++lf)
3211 for (
int lv = 0; lv < 4; ++lv)
3213 index = mesh.get_index_from_element(min_cell, lf, lv);
3215 if (index.
vertex == edge_index.vertex)
3217 if (index.
edge != edge_index.edge)
3220 index = mesh.switch_edge(tmp);
3222 if (index.
edge != edge_index.edge)
3224 index = mesh.switch_edge(mesh.switch_face(tmp));
3237 assert(index.
vertex == edge_index.vertex && index.
edge == edge_index.edge);
3238 assert(index.
element != edge_index.element);
3242 const auto lf = -(global_index + 1);
3243 assert(lf >= 0 && lf < 4);
3244 assert(j >= 4 + 6 * ln_edge_nodes && j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes);
3246 Eigen::Matrix<int, 4, 3> fv;
3247 fv.row(0) << v[0], v[1], v[2];
3248 fv.row(1) << v[0], v[1], v[3];
3249 fv.row(2) << v[1], v[2], v[3];
3250 fv.row(3) << v[2], v[0], v[3];
3252 index = mesh.switch_element(mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2)));
3255 const auto other_cell = index.
element;
3256 assert(other_cell >= 0);
3257 assert(discr_order > discr_ordersp(other_cell));
3260 Eigen::MatrixXd lnodes;
3262 Eigen::RowVector3d node_position;
3265 node_position = lnodes.row(
indices(0));
3266 else if (j < 4 + 6 * ln_edge_nodes)
3267 node_position = lnodes.row(
indices(((j - 4) % ln_edge_nodes) + 3));
3268 else if (j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes)
3273 for (ii = 0; ii < me_indices.size(); ++ii)
3275 if (me_indices(ii) == j)
3279 assert(ii >= 3 + 3 * ln_edge_nodes);
3280 assert(ii < me_indices.size());
3282 node_position = lnodes.row(
indices(ii));
3287 const auto &other_bases = bases[other_cell];
3289 std::vector<AssemblyValues> w;
3290 other_bases.evaluate_bases(node_position, w);
3292 assert(
b.bases[j].global().size() == 0);
3294 for (
long i = 0; i < w.size(); ++i)
3296 assert(w[i].
val.size() == 1);
3297 if (std::abs(w[i].
val(0)) < 1e-8)
3301 for (
size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
3303 const auto &other_global = other_bases.bases[i].global()[ii];
3305 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
3325 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 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)
void log_and_throw_error(const std::string &msg)
std::vector< int > local_indices