133 template <
class InputIterator,
class T>
134 int find_index(InputIterator first, InputIterator last,
const T &
val)
136 return std::distance(first, std::find(first, last,
val));
141 std::array<int, 4> v = {{v1, v2, v3, v4}};
142 std::sort(v.begin(), v.end());
147 std::array<int, 4> u;
153 std::sort(u.begin(), u.end());
163 std::array<int, 4> tet_vertices_local_to_global(
const Mesh3D &mesh,
int c)
167 std::array<int, 4> l2g;
169 for (
int vi : mesh.get_ordered_vertices_from_tet(c))
177 std::array<int, 8> hex_vertices_local_to_global(
const Mesh3D &mesh,
int c)
182 std::array<int, 8> l2g;
184 for (
int vi : mesh.get_ordered_vertices_from_hex(c))
192 int lowest_order_elem_on_edge(
const polyfem::mesh::NCMesh3D &mesh,
const Eigen::VectorXi &discr_orders,
const int eid)
195 int min = std::numeric_limits<int>::max();
197 for (
const auto e : elem_list)
198 if (discr_orders[
e] < min)
203 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)
205 const int n_edge_nodes = p > 1 ? ((p - 1) * 6) : 0;
206 const int nn = p > 2 ? (p - 2) : 0;
207 const int n_loc_f = (
nn * (
nn + 1) / 2);
208 const int n_face_nodes = n_loc_f * 4;
209 int n_cell_nodes = 0;
210 for (
int pp = 4; pp <= p; ++pp)
211 n_cell_nodes += ((pp - 3) * ((pp - 3) + 1) / 2);
215 res.push_back(
nodes.node_id_from_cell(c));
220 res.reserve(4 + n_edge_nodes + n_face_nodes + n_cell_nodes);
223 Eigen::Matrix<Navigation3D::Index, 4, 1>
f;
225 auto v = tet_vertices_local_to_global(mesh, c);
226 Eigen::Matrix<int, 4, 3> fv;
227 fv.row(0) << v[0], v[1], v[2];
228 fv.row(1) << v[0], v[1], v[3];
229 fv.row(2) << v[1], v[2], v[3];
230 fv.row(3) << v[2], v[0], v[3];
232 for (
long lf = 0; lf < fv.rows(); ++lf)
238 Eigen::Matrix<Navigation3D::Index, 6, 1>
e;
239 Eigen::Matrix<int, 6, 2> ev;
240 ev.row(0) << v[0], v[1];
241 ev.row(1) << v[1], v[2];
242 ev.row(2) << v[2], v[0];
244 ev.row(3) << v[0], v[3];
245 ev.row(4) << v[1], v[3];
246 ev.row(5) << v[2], v[3];
248 for (
int le = 0; le <
e.rows(); ++le)
256 for (
size_t lv = 0; lv < v.size(); ++lv)
260 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
262 if (ncmesh.leader_edge_of_vertex(v[lv]) >= 0 || ncmesh.leader_face_of_vertex(v[lv]) >= 0)
263 res.push_back(-lv - 1);
265 res.push_back(
nodes.node_id_from_primitive(v[lv]));
268 res.push_back(
nodes.node_id_from_primitive(v[lv]));
272 for (
int le = 0; le <
e.rows(); ++le)
274 const auto index =
e[le];
276 int min_p = discr_order.size() > 0 ? discr_order(c) : 0;
280 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
281 res.insert(res.end(), node_ids.begin(), node_ids.end());
287 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
289 if (ncmesh.leader_edge_of_edge(index.edge) >= 0 || ncmesh.leader_face_of_edge(index.edge) >= 0)
291 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
292 res.push_back(-le - 1);
295 else if (edge_orders[index.edge] < discr_order(c))
297 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
298 res.push_back(-le - 1);
300 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_order, index.edge);
302 if (min_order_elem == c)
303 edge_virtual_nodes[index.edge] =
nodes.node_ids_from_edge(index, edge_orders[index.edge] - 1);
307 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
308 res.insert(res.end(), node_ids.begin(), node_ids.end());
313 for (
auto cid : neighs)
315 min_p = std::min(min_p, discr_order.size() > 0 ? discr_order(cid) : 0);
318 if (discr_order.size() > 0 && discr_order(c) > min_p)
320 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
321 res.push_back(-le - 10);
325 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
326 res.insert(res.end(), node_ids.begin(), node_ids.end());
333 for (
int lf = 0; lf <
f.rows(); ++lf)
335 const auto index =
f[lf];
338 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
342 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
343 res.insert(res.end(), node_ids.begin(), node_ids.end());
349 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
351 if (ncmesh.leader_face_of_face(index.face) >= 0)
353 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
354 res.push_back(-lf - 1);
357 else if (face_orders[index.face] < discr_order[c])
359 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
360 res.push_back(-lf - 1);
362 if (ncmesh.n_follower_faces(index.face) > 0 && face_orders[index.face] > 2)
363 face_virtual_nodes[index.face] =
nodes.node_ids_from_face(index, face_orders[index.face] - 2);
367 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
368 res.insert(res.end(), node_ids.begin(), node_ids.end());
375 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
376 res.push_back(-lf - 1);
380 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
381 res.insert(res.end(), node_ids.begin(), node_ids.end());
388 if (n_cell_nodes > 0)
390 const auto index =
f[0];
392 auto node_ids =
nodes.node_ids_from_cell(index, p - 3);
393 res.insert(res.end(), node_ids.begin(), node_ids.end());
396 assert(res.size() ==
size_t(4 + n_edge_nodes + n_face_nodes + n_cell_nodes));
399 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)
403 const int n_edge_nodes = ((q - 1) * 12);
404 const int nn = (q - 1);
405 const int n_loc_f = serendipity ? 0 : (
nn *
nn);
406 const int n_face_nodes = serendipity ? 0 : (n_loc_f * 6);
407 const int n_cell_nodes = serendipity ? 0 : (
nn *
nn *
nn);
411 res.push_back(
nodes.node_id_from_cell(c));
416 res.reserve(8 + n_edge_nodes + n_face_nodes + n_cell_nodes);
419 auto v = hex_vertices_local_to_global(mesh, c);
422 Eigen::Matrix<Navigation3D::Index, 12, 1>
e;
423 Eigen::Matrix<int, 12, 2> ev;
424 ev.row(0) << v[0], v[1];
425 ev.row(1) << v[1], v[2];
426 ev.row(2) << v[2], v[3];
427 ev.row(3) << v[3], v[0];
428 ev.row(4) << v[0], v[4];
429 ev.row(5) << v[1], v[5];
430 ev.row(6) << v[2], v[6];
431 ev.row(7) << v[3], v[7];
432 ev.row(8) << v[4], v[5];
433 ev.row(9) << v[5], v[6];
434 ev.row(10) << v[6], v[7];
435 ev.row(11) << v[7], v[4];
436 for (
int le = 0; le <
e.rows(); ++le)
443 Eigen::Matrix<Navigation3D::Index, 6, 1>
f;
444 Eigen::Matrix<int, 6, 4> fv;
445 fv.row(0) << v[0], v[3], v[4], v[7];
446 fv.row(1) << v[1], v[2], v[5], v[6];
447 fv.row(2) << v[0], v[1], v[5], v[4];
448 fv.row(3) << v[3], v[2], v[6], v[7];
449 fv.row(4) << v[0], v[1], v[2], v[3];
450 fv.row(5) << v[4], v[5], v[6], v[7];
451 for (
int lf = 0; lf <
f.rows(); ++lf)
453 const auto index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
458 for (
size_t lv = 0; lv < v.size(); ++lv)
460 res.push_back(
nodes.node_id_from_primitive(v[lv]));
462 assert(res.size() ==
size_t(8));
465 for (
int le = 0; le <
e.rows(); ++le)
467 const auto index =
e[le];
469 int min_q = discr_order.size() > 0 ? discr_order(c) : 0;
471 for (
auto cid : neighs)
473 min_q = std::min(min_q, discr_order.size() > 0 ? discr_order(cid) : 0);
476 if (discr_order.size() > 0 && discr_order(c) > min_q)
478 for (
int tmp = 0;
tmp < q - 1; ++
tmp)
479 res.push_back(-le - 10);
483 auto node_ids =
nodes.node_ids_from_edge(index, q - 1);
484 res.insert(res.end(), node_ids.begin(), node_ids.end());
487 assert(res.size() ==
size_t(8 + n_edge_nodes));
490 for (
int lf = 0; lf <
f.rows(); ++lf)
492 const auto index =
f[lf];
495 const bool skip_other = discr_order.size() > 0 && other_cell >= 0 && discr_order(c) > discr_order(other_cell);
499 for (
int tmp = 0;
tmp < n_loc_f; ++
tmp)
500 res.push_back(-lf - 1);
504 auto node_ids =
nodes.node_ids_from_face(index, serendipity ? 0 : (q - 1));
505 assert(node_ids.size() == n_loc_f);
506 res.insert(res.end(), node_ids.begin(), node_ids.end());
509 assert(res.size() ==
size_t(8 + n_edge_nodes + n_face_nodes));
512 if (n_cell_nodes > 0)
514 const auto index =
f[0];
516 auto node_ids =
nodes.node_ids_from_cell(index, q - 1);
517 res.insert(res.end(), node_ids.begin(), node_ids.end());
520 assert(res.size() ==
size_t(8 + n_edge_nodes + n_face_nodes + n_cell_nodes));
540 const Eigen::VectorXi &discr_orders,
541 const Eigen::VectorXi &edge_orders,
542 const Eigen::VectorXi &face_orders,
543 const bool serendipity,
544 const bool has_polys,
545 const bool is_geom_bases,
547 std::vector<std::vector<int>> &edge_virtual_nodes,
548 std::vector<std::vector<int>> &face_virtual_nodes,
549 std::vector<std::vector<int>> &element_nodes_id,
550 std::vector<LocalBoundary> &local_boundary,
551 std::map<int, InterfaceData> &poly_face_to_data)
554 local_boundary.clear();
556 element_nodes_id.resize(mesh.
n_faces());
560 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
561 edge_virtual_nodes.resize(ncmesh.n_edges());
562 face_virtual_nodes.resize(ncmesh.n_faces());
565 for (
int c = 0; c < mesh.
n_cells(); ++c)
567 const int discr_order = discr_orders(c);
571 hex_local_to_global(serendipity, discr_order, mesh, c, discr_orders, element_nodes_id[c], nodes);
573 auto v = hex_vertices_local_to_global(mesh, c);
574 Eigen::Matrix<int, 6, 4> fv;
575 fv.row(0) << v[0], v[3], v[4], v[7];
576 fv.row(1) << v[1], v[2], v[5], v[6];
577 fv.row(2) << v[0], v[1], v[5], v[4];
578 fv.row(3) << v[3], v[2], v[6], v[7];
579 fv.row(4) << v[0], v[1], v[2], v[3];
580 fv.row(5) << v[4], v[5], v[6], v[7];
583 for (
int i = 0; i < fv.rows(); ++i)
585 const int f = find_quad_face(mesh, c, fv(i, 0), fv(i, 1), fv(i, 2), fv(i, 3)).face;
589 lb.add_boundary_primitive(f, i);
594 local_boundary.emplace_back(lb);
599 tet_local_to_global(is_geom_bases, discr_order, mesh, c, discr_orders, edge_orders, face_orders, element_nodes_id[c], nodes, edge_virtual_nodes, face_virtual_nodes);
601 auto v = tet_vertices_local_to_global(mesh, c);
602 Eigen::Matrix<int, 4, 3> fv;
603 fv.row(0) << v[0], v[1], v[2];
604 fv.row(1) << v[0], v[1], v[3];
605 fv.row(2) << v[1], v[2], v[3];
606 fv.row(3) << v[2], v[0], v[3];
609 for (
long i = 0; i < fv.rows(); ++i)
615 lb.add_boundary_primitive(f, i);
620 local_boundary.emplace_back(lb);
629 for (
int c = 0; c < mesh.
n_cells(); ++c)
641 int c2 = index2.element;
644 const int discr_order = discr_orders(c2);
659 poly_face_to_data[index2.face] = data;
669 void local_to_global(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
671 const int dim = verts.cols();
672 const int N = uv.rows();
674 assert(uv.cols() == dim);
675 assert(verts.rows() == dim + 1);
678 for (
int i = 0; i <
N; i++)
679 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);
682 void local_to_global_face(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
684 const int dim = verts.cols();
685 const int N = uv.rows();
687 assert(uv.cols() == 2);
688 assert(verts.rows() == 3);
691 for (
int i = 0; i <
N; i++)
692 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);
701 void global_to_local(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
703 const int dim = verts.cols();
704 const int N = pts.rows();
706 assert(verts.rows() == dim + 1);
707 assert(pts.cols() == dim);
710 for (
int i = 0; i <
dim; i++)
711 J.col(i) = verts.row(i + 1) - verts.row(0);
713 Eigen::Matrix3d Jinv = J.inverse();
717 for (
int i = start; i < end; i++)
719 auto point = pts.row(i) - verts.row(0);
720 uv.row(i) = Jinv * point.transpose();
725 void global_to_local_face(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
727 const int dim = verts.cols();
728 const int N = pts.rows();
730 assert(verts.rows() == 3);
731 assert(pts.cols() == dim);
734 for (
int i = 0; i < 2; i++)
735 J.col(i) = verts.row(i + 1) - verts.row(0);
737 Eigen::Vector3d a = J.col(0);
738 Eigen::Vector3d b = J.col(1);
739 Eigen::Vector3d virtual_vert = a.cross(b);
740 J.col(2) = virtual_vert;
744 for (
int i = start; i < end; i++)
746 auto point = pts.row(i) - verts.row(0);
747 Eigen::Vector3d
x = J.colPivHouseholderQr().solve(point.transpose());
748 uv.row(i) =
x.block(0, 0, 2, 1);
749 assert(std::abs(
x(2)) < 1e-8);
754 void global_to_local_edge(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::VectorXd &uv)
756 const int dim = verts.cols();
757 const int N = pts.rows();
759 assert(verts.rows() == 2);
760 assert(pts.cols() == dim);
762 auto edge = verts.row(1) - verts.row(0);
763 double squared_length = edge.squaredNorm();
767 for (
int i = start; i < end; i++)
769 auto vec = pts.row(i) - verts.row(0);
770 uv(i) = (
vec.dot(edge)) / squared_length;
775 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)
778 for (
int i = 0; i < mesh.
n_faces(); i++)
784 for (
int i = 0; i < mesh.
n_faces(); i++)
791 if (edge_orders[e_id] > face_orders[i])
797 for (
int i = 0; i < mesh.
n_edges(); i++)
803 for (
int i = 0; i < mesh.
n_edges(); i++)
821 void compute_edge_face_orders(
const polyfem::mesh::NCMesh3D &mesh,
const Eigen::VectorXi &elem_orders, Eigen::VectorXi &edge_orders, Eigen::VectorXi &face_orders)
823 const int max_order = elem_orders.maxCoeff();
824 edge_orders.setConstant(mesh.
n_edges(), max_order);
825 face_orders.setConstant(mesh.
n_faces(), max_order);
827 for (
int i = 0; i < mesh.
n_cells(); i++)
829 face_orders[mesh.
cell_face(i, j)] = std::min(face_orders[mesh.
cell_face(i, j)], elem_orders[i]);
831 for (
int i = 0; i < mesh.
n_cells(); i++)
833 edge_orders[mesh.
cell_edge(i, j)] = std::min(edge_orders[mesh.
cell_edge(i, j)], elem_orders[i]);
835 while (!check_edge_face_orders(mesh, elem_orders, edge_orders, face_orders))
838 for (
int i = 0; i < mesh.
n_faces(); i++)
842 for (
int i = 0; i < mesh.
n_faces(); i++)
847 for (
int i = 0; i < mesh.
n_faces(); i++)
854 edge_orders[e_id] = std::min(edge_orders[e_id], face_orders[i]);
859 for (
int i = 0; i < mesh.
n_edges(); i++)
863 for (
int i = 0; i < mesh.
n_edges(); i++)
868 for (
int i = 0; i < mesh.
n_edges(); i++)
881 const int nn = p > 2 ? (p - 2) : 0;
882 const int n_edge_nodes = (p - 1) * 6;
883 const int n_face_nodes = nn * (nn + 1) / 2;
889 const auto l2g = tet_vertices_local_to_global(mesh, c);
892 Eigen::VectorXi result(3 + (p - 1) * 3 + n_face_nodes);
893 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
897 Eigen::Matrix<Navigation3D::Index, 6, 1> e;
898 Eigen::Matrix<int, 6, 2> ev;
899 ev.row(0) << l2g[0], l2g[1];
900 ev.row(1) << l2g[1], l2g[2];
901 ev.row(2) << l2g[2], l2g[0];
903 ev.row(3) << l2g[0], l2g[3];
904 ev.row(4) << l2g[1], l2g[3];
905 ev.row(5) << l2g[2], l2g[3];
909 for (
int le = 0; le < e.rows(); ++le)
917 for (
int k = 0; k < 3; ++k)
919 bool reverse =
false;
921 for (; le < ev.rows(); ++le)
925 const auto l_index = e[le];
926 if (l_index.edge == tmp.edge)
928 if (l_index.vertex == tmp.vertex)
945 for (
int i = 0; i < p - 1; ++i)
947 result[ii++] = 4 + le * (p - 1) + i;
952 for (
int i = 0; i < p - 1; ++i)
954 result[ii++] = 4 + (le + 1) * (p - 1) - i - 1;
963 Eigen::Matrix<int, 4, 3> fv;
964 fv.row(0) << l2g[0], l2g[1], l2g[2];
965 fv.row(1) << l2g[0], l2g[1], l2g[3];
966 fv.row(2) << l2g[1], l2g[2], l2g[3];
967 fv.row(3) << l2g[2], l2g[0], l2g[3];
970 for (; lf < fv.rows(); ++lf)
973 if (l_index.face == index.
face)
977 assert(lf < fv.rows());
979 if (n_face_nodes == 0)
982 else if (n_face_nodes == 1)
983 result[ii++] = 4 + n_edge_nodes + lf;
987 const auto get_order = [&p, &nn, &n_face_nodes](
const std::array<int, 3> &corners) {
992 std::vector<int> order1(n_face_nodes);
993 for (
int k = 0; k < n_face_nodes; ++k)
996 std::vector<int> order2(n_face_nodes);
999 for (
int k = 0; k < nn; ++k)
1001 for (
int l = 0; l < nn - k; ++l)
1003 order2[index] = start - l;
1006 start += (nn - 1) - k;
1009 std::vector<int> order3(n_face_nodes);
1011 for (
int k = 0; k < nn; ++k)
1014 for (
int l = 0; l < nn - k; ++l)
1016 order3[index] = offset;
1023 std::vector<int> order4(n_face_nodes);
1025 start = n_face_nodes - 1;
1026 for (
int k = 0; k < nn; ++k)
1029 for (
int l = 0; l < nn - k; ++l)
1031 order4[index] = start - offset;
1032 offset += k + 2 + l;
1039 std::vector<int> order5(n_face_nodes);
1042 for (
int k = 0; k < nn; ++k)
1045 for (
int l = 0; l < nn - k; ++l)
1047 order5[index] = start + offset;
1048 offset += nn - 1 - l;
1055 std::vector<int> order6(n_face_nodes);
1057 start = n_face_nodes;
1058 for (
int k = 0; k < nn; ++k)
1061 start = start - k - 1;
1062 for (
int l = 0; l < nn - k; ++l)
1064 order6[index] = start - offset;
1065 offset += l + 1 + k;
1070 if (corners[0] == order1[0] && corners[1] == order1[nn - 1])
1072 assert(corners[2] == order1[n_face_nodes - 1]);
1076 if (corners[0] == order2[0] && corners[1] == order2[nn - 1])
1078 assert(corners[2] == order2[n_face_nodes - 1]);
1082 if (corners[0] == order3[0] && corners[1] == order3[nn - 1])
1084 assert(corners[2] == order3[n_face_nodes - 1]);
1088 if (corners[0] == order4[0] && corners[1] == order4[nn - 1])
1090 assert(corners[2] == order4[n_face_nodes - 1]);
1094 if (corners[0] == order5[0] && corners[1] == order5[nn - 1])
1096 assert(corners[2] == order5[n_face_nodes - 1]);
1100 if (corners[0] == order6[0] && corners[1] == order6[nn - 1])
1102 assert(corners[2] == order6[n_face_nodes - 1]);
1110 Eigen::MatrixXd nodes;
1116 std::array<int, 3> idx;
1117 for (
int lv = 0; lv < 3; ++lv)
1119 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1122 Eigen::Matrix3d pos(3, 3);
1126 pos.row(cnt++) = nodes.row(i);
1129 const Eigen::RowVector3d bary = pos.colwise().mean();
1131 const int offset = 4 + n_edge_nodes;
1133 for (
int lff = 0; lff < 4; ++lff)
1135 Eigen::MatrixXd loc_nodes = nodes.block(offset + lff * n_face_nodes, 0, n_face_nodes, 3);
1136 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1138 if ((node_bary - bary).norm() < 1e-10)
1140 std::array<int, 3> corners;
1142 for (
int m = 0; m < 3; ++m)
1144 auto t = pos.row(m);
1146 double min_dis = 10000;
1148 for (
int n = 0; n < n_face_nodes; ++n)
1150 double dis = (loc_nodes.row(n) - t).squaredNorm();
1159 assert(min_n < n_face_nodes);
1163 const auto indices = get_order(corners);
1164 for (
int min_n : indices)
1167 result[ii++] = 4 + n_edge_nodes + min_n + lf * n_face_nodes;
1170 assert(sum == (n_face_nodes - 1) * n_face_nodes / 2);
1186 assert(ii == result.size());
1192 const int nn = q - 1;
1193 const int n_edge_nodes = nn * 12;
1194 const int n_face_nodes = serendipity ? 0 : nn * nn;
1200 const auto l2g = hex_vertices_local_to_global(mesh, c);
1203 Eigen::VectorXi result(4 + nn * 4 + n_face_nodes);
1204 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1209 Eigen::Matrix<Navigation3D::Index, 12, 1> e;
1210 Eigen::Matrix<int, 12, 2> ev;
1211 ev.row(0) << l2g[0], l2g[1];
1212 ev.row(1) << l2g[1], l2g[2];
1213 ev.row(2) << l2g[2], l2g[3];
1214 ev.row(3) << l2g[3], l2g[0];
1215 ev.row(4) << l2g[0], l2g[4];
1216 ev.row(5) << l2g[1], l2g[5];
1217 ev.row(6) << l2g[2], l2g[6];
1218 ev.row(7) << l2g[3], l2g[7];
1219 ev.row(8) << l2g[4], l2g[5];
1220 ev.row(9) << l2g[5], l2g[6];
1221 ev.row(10) << l2g[6], l2g[7];
1222 ev.row(11) << l2g[7], l2g[4];
1226 for (
int le = 0; le < e.rows(); ++le)
1233 for (
int k = 0; k < 4; ++k)
1235 bool reverse =
false;
1237 for (; le < ev.rows(); ++le)
1241 const auto l_index = e[le];
1242 if (l_index.edge == tmp.edge)
1244 if (l_index.vertex == tmp.vertex)
1260 for (
int i = 0; i < q - 1; ++i)
1262 result[ii++] = 8 + le * (q - 1) + i;
1267 for (
int i = 0; i < q - 1; ++i)
1269 result[ii++] = 8 + (le + 1) * (q - 1) - i - 1;
1278 Eigen::Matrix<int, 6, 4> fv;
1279 fv.row(0) << l2g[0], l2g[3], l2g[4], l2g[7];
1280 fv.row(1) << l2g[1], l2g[2], l2g[5], l2g[6];
1281 fv.row(2) << l2g[0], l2g[1], l2g[5], l2g[4];
1282 fv.row(3) << l2g[3], l2g[2], l2g[6], l2g[7];
1283 fv.row(4) << l2g[0], l2g[1], l2g[2], l2g[3];
1284 fv.row(5) << l2g[4], l2g[5], l2g[6], l2g[7];
1287 for (; lf < fv.rows(); ++lf)
1289 const auto l_index = find_quad_face(mesh, c, fv(lf, 0), fv(lf, 1), fv(lf, 2), fv(lf, 3));
1290 if (l_index.face == index.
face)
1294 assert(lf < fv.rows());
1296 if (n_face_nodes == 1)
1297 result[ii++] = 8 + n_edge_nodes + lf;
1298 else if (n_face_nodes != 0)
1300 Eigen::MatrixXd nodes;
1306 std::array<int, 4> idx;
1307 for (
int lv = 0; lv < 4; ++lv)
1309 idx[lv] = find_index(l2g.begin(), l2g.end(), index.
vertex);
1312 Eigen::Matrix<double, 4, 3> pos(4, 3);
1316 pos.row(cnt++) = nodes.row(i);
1319 const Eigen::RowVector3d bary = pos.colwise().mean();
1321 const int offset = 8 + n_edge_nodes;
1323 for (
int lff = 0; lff < 6; ++lff)
1325 Eigen::Matrix<double, 4, 3> loc_nodes = nodes.block<4, 3>(offset + lff * n_face_nodes, 0);
1326 Eigen::RowVector3d node_bary = loc_nodes.colwise().mean();
1328 if ((node_bary - bary).norm() < 1e-10)
1331 for (
int m = 0; m < 4; ++m)
1333 auto t = pos.row(m);
1335 double min_dis = 10000;
1337 for (
int n = 0; n < 4; ++n)
1339 double dis = (loc_nodes.row(n) - t).squaredNorm();
1352 result[ii++] = 8 + n_edge_nodes + min_n + lf * n_face_nodes;
1368 assert(ii == result.size());
1374 const std::string &assembler,
1375 const int quadrature_order,
1376 const int mass_quadrature_order,
1377 const int discr_order,
1378 const bool serendipity,
1379 const bool has_polys,
1380 const bool is_geom_bases,
1381 std::vector<ElementBases> &bases,
1382 std::vector<LocalBoundary> &local_boundary,
1383 std::map<int, InterfaceData> &poly_face_to_data,
1384 std::shared_ptr<MeshNodes> &mesh_nodes)
1386 Eigen::VectorXi discr_orders(mesh.
n_cells());
1387 discr_orders.setConstant(discr_order);
1389 return build_bases(mesh, assembler, quadrature_order, mass_quadrature_order, discr_orders, serendipity, has_polys, is_geom_bases, bases, local_boundary, poly_face_to_data, mesh_nodes);
1394 const std::string &assembler,
1395 const int quadrature_order,
1396 const int mass_quadrature_order,
1397 const Eigen::VectorXi &discr_orders,
1398 const bool serendipity,
1399 const bool has_polys,
1400 const bool is_geom_bases,
1401 std::vector<ElementBases> &bases,
1402 std::vector<LocalBoundary> &local_boundary,
1403 std::map<int, InterfaceData> &poly_face_to_data,
1404 std::shared_ptr<MeshNodes> &mesh_nodes)
1407 assert(discr_orders.size() == mesh.
n_cells());
1415 const int max_p = discr_orders.maxCoeff();
1418 const int nn = max_p > 1 ? (max_p - 1) : 0;
1419 const int n_face_nodes = nn * nn;
1420 const int n_cells_nodes = nn * nn * nn;
1422 Eigen::VectorXi edge_orders, face_orders;
1425 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
1426 compute_edge_face_orders(ncmesh, discr_orders, edge_orders, face_orders);
1429 mesh_nodes = std::make_shared<MeshNodes>(mesh, has_polys, !is_geom_bases, nn, n_face_nodes * (is_geom_bases ? 2 : 1), max_p == 0 ? 1 : n_cells_nodes);
1431 std::vector<std::vector<int>> element_nodes_id, edge_virtual_nodes, face_virtual_nodes;
1432 compute_nodes(mesh, discr_orders, 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);
1442 std::vector<int> interface_elements;
1443 interface_elements.reserve(mesh.
n_faces());
1445 for (
int e = 0; e < mesh.
n_cells(); ++e)
1448 const int discr_order = discr_orders(e);
1449 const int n_el_bases = (int)element_nodes_id[e].size();
1450 b.
bases.resize(n_el_bases);
1452 bool skip_interface_element =
false;
1454 for (
int j = 0; j < n_el_bases; ++j)
1456 const int global_index = element_nodes_id[e][j];
1457 if (global_index < 0)
1459 skip_interface_element =
true;
1464 if (skip_interface_element)
1466 interface_elements.push_back(e);
1471 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
1472 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 3);
1483 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
1486 for (
int lf = 0; lf < 6; ++lf)
1488 index = mesh3d.get_index_from_element(e, lf, 0);
1489 if (index.
face == primitive_id)
1492 assert(index.
face == primitive_id);
1496 for (
int j = 0; j < n_el_bases; ++j)
1498 const int global_index = element_nodes_id[
e][j];
1500 b.
bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
1502 const int dtmp = serendipity ? -2 : discr_order;
1510 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
1511 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 3);
1523 const auto &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
1526 for (
int lf = 0; lf < mesh3d.n_cell_faces(e); ++lf)
1528 index = mesh3d.get_index_from_element(e, lf, 0);
1529 if (index.
face == primitive_id)
1532 assert(index.
face == primitive_id);
1539 for (
int j = 0; j < n_el_bases; ++j)
1541 const int global_index = element_nodes_id[
e][j];
1542 if (!skip_interface_element)
1544 b.
bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
1562 const auto &ncmesh =
dynamic_cast<const NCMesh3D &
>(mesh);
1564 std::vector<std::vector<int>> elementOrder;
1566 const int max_order = discr_orders.maxCoeff(), min_order = discr_orders.minCoeff();
1568 for (
int e = 0;
e < ncmesh.n_cells();
e++)
1569 if (max_level < ncmesh.cell_ref_level(e))
1572 elementOrder.resize((max_level + 1) * (max_order - min_order + 1));
1575 while (cur_level <= max_level)
1577 int order = min_order;
1578 while (order <= max_order)
1580 int cur_bucket = (max_order - min_order + 1) * cur_level + (order - min_order);
1581 for (
int i = 0; i < ncmesh.n_cells(); i++)
1583 if (ncmesh.cell_ref_level(i) != cur_level || discr_orders[i] != order)
1587 elementOrder[cur_bucket].push_back(i);
1595 for (
const auto &bucket : elementOrder)
1597 if (bucket.size() == 0)
1600 for (int e_aux = start; e_aux < end; e_aux++)
1602 const int e = bucket[e_aux];
1603 ElementBases &b = bases[e];
1604 const int discr_order = discr_orders(e);
1605 const int n_edge_nodes = discr_order - 1;
1606 const int n_face_nodes = (discr_order - 1) * (discr_order - 2) / 2;
1607 const int n_el_bases = element_nodes_id[e].size();
1609 auto v = tet_vertices_local_to_global(mesh, e);
1611 Eigen::Matrix<Navigation3D::Index, 4, 1> cell_faces;
1612 Eigen::Matrix<int, 4, 3> fv;
1613 fv.row(0) << v[0], v[1], v[2];
1614 fv.row(1) << v[0], v[1], v[3];
1615 fv.row(2) << v[1], v[2], v[3];
1616 fv.row(3) << v[2], v[0], v[3];
1618 for (long lf = 0; lf < fv.rows(); ++lf)
1620 const auto index = mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2));
1621 cell_faces[lf] = index;
1624 Eigen::Matrix<Navigation3D::Index, 6, 1> cell_edges;
1625 Eigen::Matrix<int, 6, 2> ev;
1626 ev.row(0) << v[0], v[1];
1627 ev.row(1) << v[1], v[2];
1628 ev.row(2) << v[2], v[0];
1630 ev.row(3) << v[0], v[3];
1631 ev.row(4) << v[1], v[3];
1632 ev.row(5) << v[2], v[3];
1634 for (int le = 0; le < ev.rows(); ++le)
1637 const auto index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
1638 cell_edges[le] = index;
1641 Eigen::MatrixXd verts(4, 3);
1642 for (int i = 0; i < ncmesh.n_cell_vertices(e); i++)
1643 verts.row(i) = ncmesh.point(v[i]);
1645 for (int j = 0; j < n_el_bases; ++j)
1647 const int global_index = element_nodes_id[e][j];
1649 if (global_index >= 0)
1651 b.bases[j].init(discr_order, global_index, j, nodes.node_position(global_index));
1658 int large_elem = -1;
1659 if (ncmesh.leader_edge_of_vertex(v[j]) >= 0)
1661 large_elem = lowest_order_elem_on_edge(ncmesh, discr_orders, ncmesh.leader_edge_of_vertex(v[j]));
1663 else if (ncmesh.leader_face_of_vertex(v[j]) >= 0)
1665 std::vector<int> ids;
1666 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_vertex(v[j]), ids);
1667 assert(ids.size() == 1);
1668 large_elem = ids[0];
1673 Eigen::MatrixXd large_elem_verts(4, 3);
1674 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
1675 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
1676 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
1678 Eigen::MatrixXd node_position;
1679 global_to_local(large_elem_verts, verts.row(j), node_position);
1682 const auto &other_bases = bases[large_elem];
1683 std::vector<AssemblyValues> w;
1684 other_bases.evaluate_bases(node_position, w);
1687 for (long i = 0; i < w.size(); ++i)
1689 assert(w[i].val.size() == 1);
1690 if (std::abs(w[i].val(0)) < 1e-12)
1693 assert(other_bases.bases[i].global().size() > 0);
1694 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
1696 const auto &other_global = other_bases.bases[i].global()[ii];
1697 assert(other_global.index >= 0);
1698 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
1703 else if (j < 4 + 6 * n_edge_nodes)
1705 const int local_edge_id = (j - 4) / n_edge_nodes;
1706 const int edge_id = cell_edges[local_edge_id].edge;
1707 bool need_extra_fake_nodes = false;
1708 int large_elem = -1;
1711 if (ncmesh.leader_edge_of_edge(edge_id) >= 0)
1713 std::vector<int> ids;
1714 ncmesh.get_edge_elements_neighs(ncmesh.leader_edge_of_edge(edge_id), ids);
1715 large_elem = ids[0];
1718 else if (ncmesh.leader_face_of_edge(edge_id) >= 0)
1720 std::vector<int> ids;
1721 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_edge(edge_id), ids);
1722 assert(ids.size() == 1);
1723 large_elem = ids[0];
1726 else if (discr_order > edge_orders[edge_id])
1728 int min_order_elem = lowest_order_elem_on_edge(ncmesh, discr_orders, edge_id);
1730 if (discr_orders[min_order_elem] < discr_order)
1731 large_elem = min_order_elem;
1737 need_extra_fake_nodes = true;
1743 assert(large_elem >= 0 || need_extra_fake_nodes);
1744 Eigen::MatrixXd lnodes;
1745 autogen::p_nodes_3d(discr_order, lnodes);
1746 Eigen::MatrixXd local_position = lnodes.row(j);
1747 if (need_extra_fake_nodes)
1749 Eigen::MatrixXd global_position, edge_verts(2, 3);
1750 Eigen::VectorXd point_weight;
1752 edge_verts.row(0) = ncmesh.point(ncmesh.edge_vertex(edge_id, 0));
1753 edge_verts.row(1) = ncmesh.point(ncmesh.edge_vertex(edge_id, 1));
1755 local_to_global(verts, local_position, global_position);
1756 global_to_local_edge(edge_verts, global_position, point_weight);
1758 std::function<double(const int, const int, const double)> basis_1d = [](const int order, const int id, const double x) -> double {
1759 assert(id <= order && id >= 0);
1761 for (int o = 0; o <= order; o++)
1764 y *= (x * order - o) / (id - o);
1770 for (int i = 0; i < edge_virtual_nodes[edge_id].size(); i++)
1772 const int global_index = edge_virtual_nodes[edge_id][i];
1774 Eigen::VectorXd node_weight;
1775 global_to_local_edge(edge_verts, nodes.node_position(global_index), node_weight);
1776 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
1777 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
1778 if (std::abs(weight) < 1e-12)
1780 b.bases[j].global().emplace_back(global_index, nodes.node_position(global_index), weight);
1784 for (int i = 0; i < 2; i++)
1786 const int lv = ev(local_edge_id, i);
1787 const auto &global_ = b.bases[lv].global();
1788 Eigen::VectorXd node_weight;
1789 global_to_local_edge(edge_verts, verts.row(lv), node_weight);
1790 const int basis_id = std::lround(node_weight(0) * edge_orders[edge_id]);
1791 const double weight = basis_1d(edge_orders[edge_id], basis_id, point_weight(0));
1792 if (std::abs(weight) > 1e-12)
1794 assert(global_.size() > 0);
1795 for (size_t ii = 0; ii < global_.size(); ++ii)
1796 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
1802 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
1803 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
1804 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
1805 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
1806 local_to_global(verts, local_position, global_position);
1807 global_to_local(large_elem_verts, global_position, local_position);
1810 const auto &other_bases = bases[large_elem];
1811 std::vector<AssemblyValues> w;
1812 other_bases.evaluate_bases(local_position, w);
1815 for (long i = 0; i < w.size(); ++i)
1817 assert(w[i].val.size() == 1);
1818 if (std::abs(w[i].val(0)) < 1e-12)
1821 assert(other_bases.bases[i].global().size() > 0);
1822 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
1824 const auto &other_global = other_bases.bases[i].global()[ii];
1825 assert(other_global.index >= 0);
1826 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
1832 else if (j < 4 + 6 * n_edge_nodes + 4 * n_face_nodes)
1834 const int local_face_id = (j - (4 + 6 * n_edge_nodes)) / n_face_nodes;
1835 const int face_id = cell_faces(local_face_id).face;
1836 int large_elem = -1;
1837 bool need_extra_fake_nodes = false;
1839 std::vector<int> ids;
1840 ncmesh.get_face_elements_neighs(ncmesh.leader_face_of_face(face_id), ids);
1842 Eigen::MatrixXd face_verts(3, 3);
1843 for (int i = 0; i < ncmesh.n_face_vertices(face_id); i++)
1844 face_verts.row(i) = ncmesh.point(ncmesh.face_vertex(face_id, i));
1847 if (ncmesh.leader_face_of_face(face_id) >= 0)
1849 assert(ids.size() == 1);
1850 large_elem = ids[0];
1854 else if (face_orders[face_id] < discr_order && ids.size() == 2)
1856 large_elem = ids[0] == e ? ids[1] : ids[0];
1859 else if (face_orders[face_id] < discr_order && ncmesh.n_follower_faces(face_id) > 0)
1862 need_extra_fake_nodes = true;
1867 assert(large_elem >= 0 || need_extra_fake_nodes);
1868 Eigen::MatrixXd lnodes;
1869 autogen::p_nodes_3d(discr_order, lnodes);
1870 Eigen::MatrixXd local_position = lnodes.row(j);
1871 if (need_extra_fake_nodes)
1873 Eigen::MatrixXd global_position;
1874 local_to_global(verts, local_position, global_position);
1876 Eigen::MatrixXd tmp;
1877 global_to_local_face(face_verts, global_position, tmp);
1878 Eigen::VectorXd face_weight = tmp.transpose();
1880 std::function<double(const int, const int, const double)> basis_aux = [](const int order, const int id, const double x) -> double {
1881 assert(id <= order && id >= 0);
1883 for (int o = 0; o < id; o++)
1884 y *= (x * order - o) / (id - o);
1888 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 {
1889 assert(i + j <= order && i >= 0 && j >= 0);
1890 double u = uv(0), v = uv(1);
1891 return basis_aux(order, i, u) * basis_aux(order, j, v) * basis_aux(order, order - i - j, 1 - u - v);
1895 for (int global_ : face_virtual_nodes[face_id])
1897 auto low_order_node = nodes.node_position(global_);
1898 Eigen::MatrixXd low_order_node_face_weight;
1899 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
1900 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]);
1901 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
1902 if (std::abs(weight) < 1e-12)
1904 b.bases[j].global().emplace_back(global_, nodes.node_position(global_), weight);
1908 for (int i = 0; i < 3; i++)
1910 const auto &global_ = b.bases[fv(local_face_id, i)].global();
1911 auto low_order_node = ncmesh.point(fv(local_face_id, i));
1912 Eigen::MatrixXd low_order_node_face_weight;
1913 global_to_local_face(face_verts, low_order_node, low_order_node_face_weight);
1914 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]);
1915 double weight = basis_2d(face_orders[face_id], x, y, face_weight);
1916 if (std::abs(weight) > 1e-12)
1918 assert(global_.size() > 0);
1919 for (size_t ii = 0; ii < global_.size(); ++ii)
1920 b.bases[j].global().emplace_back(global_[ii].index, global_[ii].node, weight * global_[ii].val);
1925 for (int x = 0, idx = 0; x <= face_orders[face_id]; x++)
1927 for (int y = 0; x + y <= face_orders[face_id]; y++)
1929 const int z = face_orders[face_id] - x - y;
1930 int flag = (int)(x == 0) + (int)(y == 0) + (int)(z == 0);
1935 const double weight = basis_2d(face_orders[face_id], x, y, face_weight);
1936 if (std::abs(weight) < 1e-12)
1938 Eigen::MatrixXd face_weight(1, 2);
1939 face_weight << (double)x / face_orders[face_id], (double)y / face_orders[face_id];
1940 Eigen::MatrixXd pos, local_pos;
1941 local_to_global_face(face_verts, face_weight, pos);
1942 global_to_local(verts, pos, local_pos);
1943 Local2Global step1(idx, local_pos, weight);
1948 const auto &other_bases = bases[e];
1949 std::vector<AssemblyValues> w;
1950 other_bases.evaluate_bases(local_pos, w);
1953 for (long i = 0; i < w.size(); ++i)
1955 assert(w[i].val.size() == 1);
1956 if (std::abs(w[i].val(0)) < 1e-12)
1959 assert(other_bases.bases[i].global().size() > 0);
1960 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
1962 const auto &other_global = other_bases.bases[i].global()[ii];
1963 assert(other_global.index >= 0);
1964 b.bases[j].global().emplace_back(other_global.index, other_global.node, step1.val * w[i].val(0) * other_global.val);
1973 Eigen::MatrixXd global_position, large_elem_verts(4, 3);
1974 auto v_large = tet_vertices_local_to_global(mesh, large_elem);
1975 for (int i = 0; i < ncmesh.n_cell_vertices(large_elem); i++)
1976 large_elem_verts.row(i) = ncmesh.point(v_large[i]);
1977 local_to_global(verts, local_position, global_position);
1978 global_to_local(large_elem_verts, global_position, local_position);
1981 const auto &other_bases = bases[large_elem];
1982 std::vector<AssemblyValues> w;
1983 other_bases.evaluate_bases(local_position, w);
1986 for (long i = 0; i < w.size(); ++i)
1988 assert(w[i].val.size() == 1);
1989 if (std::abs(w[i].val(0)) < 1e-12)
1992 assert(other_bases.bases[i].global().size() > 0);
1993 for (size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
1995 const auto &other_global = other_bases.bases[i].global()[ii];
1996 assert(other_global.index >= 0);
1997 b.bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
2005 auto &global_ = b.bases[j].global();
2006 if (global_.size() <= 1)
2009 std::map<int, Local2Global> list;
2010 for (size_t ii = 0; ii < global_.size(); ii++)
2012 auto pair = list.insert({global_[ii].index, global_[ii]});
2013 if (!pair.second && pair.first != list.end())
2015 assert((pair.first->second.node - global_[ii].node).norm() < 1e-12);
2016 pair.first->second.val += global_[ii].val;
2021 for (auto it = list.begin(); it != list.end(); ++it)
2023 if (std::abs(it->second.val) > 1e-12)
2025 global_.push_back(it->second);
2036 for (
int pp = 2; pp <= autogen::MAX_P_BASES; ++pp)
2038 for (
int e : interface_elements)
2041 const int discr_order = discr_orders(e);
2042 const int n_el_bases = element_nodes_id[
e].size();
2043 assert(discr_order > 1);
2044 if (discr_order != pp)
2054 for (
int j = 0; j < n_el_bases; ++j)
2056 const int global_index = element_nodes_id[
e][j];
2058 if (global_index >= 0)
2059 b.
bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
2062 const int lnn = max_p > 2 ? (discr_order - 2) : 0;
2063 const int ln_edge_nodes = discr_order - 1;
2064 const int ln_face_nodes = lnn * (lnn + 1) / 2;
2066 const auto v = tet_vertices_local_to_global(mesh, e);
2068 if (global_index <= -30)
2092 else if (global_index <= -10)
2094 const auto le = -(global_index + 10);
2095 assert(le >= 0 && le < 6);
2096 assert(j >= 4 && j < 4 + 6 * ln_edge_nodes);
2098 Eigen::Matrix<int, 6, 2> ev;
2099 ev.row(0) << v[0], v[1];
2100 ev.row(1) << v[1], v[2];
2101 ev.row(2) << v[2], v[0];
2103 ev.row(3) << v[0], v[3];
2104 ev.row(4) << v[1], v[3];
2105 ev.row(5) << v[2], v[3];
2108 const auto edge_index = mesh.get_index_from_element_edge(e, ev(le, 0), ev(le, 1));
2109 auto neighs = mesh.edge_neighs(edge_index.edge);
2110 int min_p = discr_order;
2113 for (
auto cid : neighs)
2115 if (discr_orders[cid] < min_p)
2117 min_p = discr_orders[cid];
2123 for (
int lf = 0; lf < 4; ++lf)
2125 for (
int lv = 0; lv < 4; ++lv)
2127 index = mesh.get_index_from_element(min_cell, lf, lv);
2129 if (index.
vertex == edge_index.vertex)
2131 if (index.
edge != edge_index.edge)
2134 index = mesh.switch_edge(tmp);
2136 if (index.
edge != edge_index.edge)
2138 index = mesh.switch_edge(mesh.switch_face(tmp));
2151 assert(index.
vertex == edge_index.vertex && index.
edge == edge_index.edge);
2152 assert(index.
element != edge_index.element);
2156 const auto lf = -(global_index + 1);
2157 assert(lf >= 0 && lf < 4);
2158 assert(j >= 4 + 6 * ln_edge_nodes && j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes);
2160 Eigen::Matrix<int, 4, 3> fv;
2161 fv.row(0) << v[0], v[1], v[2];
2162 fv.row(1) << v[0], v[1], v[3];
2163 fv.row(2) << v[1], v[2], v[3];
2164 fv.row(3) << v[2], v[0], v[3];
2166 index = mesh.switch_element(mesh.get_index_from_element_face(e, fv(lf, 0), fv(lf, 1), fv(lf, 2)));
2169 const auto other_cell = index.
element;
2170 assert(other_cell >= 0);
2171 assert(discr_order > discr_orders(other_cell));
2174 Eigen::MatrixXd lnodes;
2176 Eigen::RowVector3d node_position;
2179 node_position = lnodes.row(
indices(0));
2180 else if (j < 4 + 6 * ln_edge_nodes)
2181 node_position = lnodes.row(
indices(((j - 4) % ln_edge_nodes) + 3));
2182 else if (j < 4 + 6 * ln_edge_nodes + 4 * ln_face_nodes)
2187 for (ii = 0; ii < me_indices.size(); ++ii)
2189 if (me_indices(ii) == j)
2193 assert(ii >= 3 + 3 * ln_edge_nodes);
2194 assert(ii < me_indices.size());
2196 node_position = lnodes.row(
indices(ii));
2217 const auto &other_bases = bases[other_cell];
2219 std::vector<AssemblyValues> w;
2220 other_bases.evaluate_bases(node_position, w);
2222 assert(b.
bases[j].global().size() == 0);
2224 for (
long i = 0; i < w.size(); ++i)
2226 assert(w[i].
val.size() == 1);
2227 if (std::abs(w[i].
val(0)) < 1e-8)
2231 for (
size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
2233 const auto &other_global = other_bases.bases[i].global()[ii];
2235 b.
bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
2250 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).
void set_quadrature(const QuadratureFunction &fun)
void set_local_node_from_primitive_func(LocalNodeFromPrimitiveFunc fun)
sets mapping from local nodes to global nodes
std::vector< Basis > bases
one basis function per node in the element
void set_mass_quadrature(const QuadratureFunction &fun)
static Eigen::VectorXi hex_face_local_nodes(const bool serendipity, 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_order, const bool serendipity, const bool has_polys, const bool is_geom_bases, 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 simples compatible
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
virtual int n_face_vertices(const int f_id) const =0
number of vertices of a face
int cell_ref_level(const int c_id) const
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, Quadrature &quad)
void q_grad_basis_value_3d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void p_nodes_3d(const int p, Eigen::MatrixXd &val)
void p_grad_basis_value_3d(const int p, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void p_basis_value_3d(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