114 void sort(std::array<int, 2> &array)
116 if (array[0] > array[1])
118 std::swap(array[0], array[1]);
121 assert(array[0] <= array[1]);
124 template <
class InputIterator,
class T>
125 int find_index(InputIterator first, InputIterator last,
const T &
val)
127 return std::distance(first, std::find(first, last,
val));
132 std::array<int, 2> v = {{v1, v2}};
133 std::array<int, 2> u;
145 if (idx.vertex != v1)
147 assert(idx.vertex == v1);
152 throw std::runtime_error(
"Edge not found");
155 std::array<int, 3> tri_vertices_local_to_global(
const Mesh2D &mesh,
int f)
160 std::array<int, 3> l2g;
161 for (
int lv = 0; lv < 3; ++lv)
169 std::array<int, 4> quad_vertices_local_to_global(
const Mesh2D &mesh,
int f)
174 std::array<int, 4> l2g;
175 for (
int lv = 0; lv < 4; ++lv)
183 void tri_local_to_global(
const bool is_geom_bases,
const int p,
const Mesh2D &mesh,
int f,
const Eigen::VectorXi &discr_order,
const Eigen::VectorXi &edge_orders, std::vector<int> &res,
MeshNodes &nodes, std::vector<std::vector<int>> &edge_virtual_nodes)
186 int face_offset = edge_offset + mesh.
n_edges();
188 const int n_edge_nodes = p > 1 ? ((p - 1) * 3) : 0;
189 const int nn = p > 2 ? (p - 2) : 0;
190 const int n_face_nodes =
nn * (
nn + 1) / 2;
194 res.push_back(
nodes.node_id_from_face(f));
199 res.reserve(3 + n_edge_nodes + n_face_nodes);
202 auto v = tri_vertices_local_to_global(mesh, f);
205 Eigen::Matrix<Navigation::Index, 3, 1>
e;
206 Eigen::Matrix<int, 3, 2> ev;
207 ev.row(0) << v[0], v[1];
208 ev.row(1) << v[1], v[2];
209 ev.row(2) << v[2], v[0];
210 for (
int le = 0; le <
e.rows(); ++le)
212 const auto index = find_edge(mesh, f, ev(le, 0), ev(le, 1));
216 for (
size_t lv = 0; lv < v.size(); ++lv)
218 const auto index =
e[lv];
222 res.push_back(
nodes.node_id_from_primitive(v[lv]));
227 const auto &ncmesh =
dynamic_cast<const NCMesh2D &
>(mesh);
229 if (ncmesh.leader_edge_of_vertex(ncmesh.face_vertex(f, lv)) >= 0)
230 res.push_back(-lv - 1);
232 res.push_back(
nodes.node_id_from_primitive(v[lv]));
236 if (discr_order.size() > 0 && other_face >= 0 && discr_order(f) > discr_order(other_face))
237 res.push_back(-lv - 1);
239 res.push_back(
nodes.node_id_from_primitive(v[lv]));
244 for (
int le = 0; le <
e.rows(); ++le)
246 const auto index =
e[le];
251 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
252 res.insert(res.end(), node_ids.begin(), node_ids.end());
258 const auto &ncmesh =
dynamic_cast<const NCMesh2D &
>(mesh);
261 if (ncmesh.leader_edge_of_edge(
edge_id) >= 0)
263 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
264 res.push_back(-le - 1);
267 else if (edge_orders[
edge_id] < discr_order[f])
269 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
270 res.push_back(-le - 1);
272 if (ncmesh.n_follower_edges(
edge_id) > 0)
278 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
279 res.insert(res.end(), node_ids.begin(), node_ids.end());
284 bool skip_other = discr_order.size() > 0 && other_face >= 0 && discr_order(f) > discr_order(other_face);
286 if (discr_order.size() > 0 && other_face >= 0 && discr_order(f) > discr_order(other_face))
288 for (
int tmp = 0;
tmp < p - 1; ++
tmp)
289 res.push_back(-le - 1);
293 auto node_ids =
nodes.node_ids_from_edge(index, p - 1);
294 res.insert(res.end(), node_ids.begin(), node_ids.end());
300 if (n_face_nodes > 0)
302 const auto index =
e[0];
304 auto node_ids =
nodes.node_ids_from_face(index, p - 2);
305 res.insert(res.end(), node_ids.begin(), node_ids.end());
308 assert(res.size() ==
size_t(3 + n_edge_nodes + n_face_nodes));
312 void quad_local_to_global(
const bool serendipity,
const int q,
const Mesh2D &mesh,
int f,
const Eigen::VectorXi &discr_order, std::vector<int> &res,
MeshNodes &nodes)
315 int face_offset = edge_offset + mesh.
n_edges();
317 const int nn = q > 1 ? (q - 1) : 0;
318 const int n_edge_nodes =
nn * 4;
319 const int n_face_nodes = serendipity ? 0 :
nn *
nn;
323 res.push_back(
nodes.node_id_from_face(f));
328 res.reserve(4 + n_edge_nodes + n_face_nodes);
331 auto v = quad_vertices_local_to_global(mesh, f);
334 Eigen::Matrix<Navigation::Index, 4, 1>
e;
335 Eigen::Matrix<int, 4, 2> ev;
336 ev.row(0) << v[0], v[1];
337 ev.row(1) << v[1], v[2];
338 ev.row(2) << v[2], v[3];
339 ev.row(3) << v[3], v[0];
340 for (
int le = 0; le <
e.rows(); ++le)
342 const auto index = find_edge(mesh, f, ev(le, 0), ev(le, 1));
346 for (
size_t lv = 0; lv < v.size(); ++lv)
348 const auto index =
e[lv];
351 if (discr_order.size() > 0 && other_face >= 0 && discr_order(f) > discr_order(other_face))
352 res.push_back(-lv - 1);
354 res.push_back(
nodes.node_id_from_primitive(v[lv]));
357 for (
int le = 0; le <
e.rows(); ++le)
359 const auto index =
e[le];
362 bool skip_other = discr_order.size() > 0 && other_face >= 0 && discr_order(f) > discr_order(other_face);
364 if (discr_order.size() > 0 && other_face >= 0 && discr_order(f) > discr_order(other_face))
366 for (
int tmp = 0;
tmp < q - 1; ++
tmp)
367 res.push_back(-le - 1);
371 auto node_ids =
nodes.node_ids_from_edge(index, q - 1);
372 res.insert(res.end(), node_ids.begin(), node_ids.end());
376 if (n_face_nodes > 0)
378 const auto index =
e[0];
380 auto node_ids =
nodes.node_ids_from_face(index, q - 1);
381 res.insert(res.end(), node_ids.begin(), node_ids.end());
384 assert(res.size() ==
size_t(4 + n_edge_nodes + n_face_nodes));
407 const Eigen::VectorXi &discr_orders,
408 const Eigen::VectorXi &edge_orders,
409 const bool serendipity,
410 const bool has_polys,
411 const bool is_geom_bases,
413 std::vector<std::vector<int>> &edge_virtual_nodes,
414 std::vector<std::vector<int>> &element_nodes_id,
415 std::vector<LocalBoundary> &local_boundary,
416 std::map<int, InterfaceData> &poly_edge_to_data)
419 local_boundary.clear();
421 element_nodes_id.resize(mesh.
n_faces());
425 const auto &ncmesh =
dynamic_cast<const NCMesh2D &
>(mesh);
426 edge_virtual_nodes.resize(ncmesh.n_edges());
431 const int discr_order = discr_orders(f);
434 quad_local_to_global(serendipity, discr_order, mesh, f, discr_orders, element_nodes_id[f], nodes);
438 auto v = quad_vertices_local_to_global(mesh, f);
439 Eigen::Matrix<int, 4, 2> ev;
440 ev.row(0) << v[0], v[1];
441 ev.row(1) << v[1], v[2];
442 ev.row(2) << v[2], v[3];
443 ev.row(3) << v[3], v[0];
445 for (
int i = 0; i < int(ev.rows()); ++i)
447 const auto index = find_edge(mesh, f, ev(i, 0), ev(i, 1));
448 const int edge = index.edge;
452 lb.add_boundary_primitive(edge, i);
457 local_boundary.emplace_back(lb);
461 tri_local_to_global(is_geom_bases, discr_order, mesh, f, discr_orders, edge_orders, element_nodes_id[f], nodes, edge_virtual_nodes);
463 auto v = tri_vertices_local_to_global(mesh, f);
465 Eigen::Matrix<int, 3, 2> ev;
466 ev.row(0) << v[0], v[1];
467 ev.row(1) << v[1], v[2];
468 ev.row(2) << v[2], v[0];
472 for (
int i = 0; i < int(ev.rows()); ++i)
474 const auto index = find_edge(mesh, f, ev(i, 0), ev(i, 1));
475 const int edge = index.edge;
479 lb.add_boundary_primitive(edge, i);
484 local_boundary.emplace_back(lb);
505 if (index2.face >= 0)
508 int f2 = index2.
face;
509 const int discr_order = discr_orders(f2);
525 assert(
indices.size() == 2 + discr_order - 1);
528 poly_edge_to_data[index2.edge] = data;
541 void local_to_global(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &uv, Eigen::MatrixXd &pts)
543 const int dim = verts.cols();
544 const int N = uv.rows();
546 assert(uv.cols() == dim);
547 assert(verts.rows() == dim + 1);
550 for (
int i = 0; i <
N; i++)
551 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);
560 void global_to_local(
const Eigen::MatrixXd &verts,
const Eigen::MatrixXd &pts, Eigen::MatrixXd &uv)
562 const int dim = verts.cols();
563 const int N = pts.rows();
565 assert(verts.rows() == dim + 1);
566 assert(pts.cols() == dim);
569 for (
int i = 0; i <
dim; i++)
570 J.col(i) = verts.row(i + 1) - verts.row(0);
572 double detJ = J(0, 0) * J(1, 1) - J(0, 1) * J(1, 0);
577 for (
int i = start; i < end; i++)
579 auto point = pts.row(i) - verts.row(0);
580 uv(i, 0) = J(1, 1) * point(0) - J(0, 1) * point(1);
581 uv(i, 1) = J(0, 0) * point(1) - J(1, 0) * point(0);
592 void compute_edge_orders(
const NCMesh2D &mesh,
const Eigen::VectorXi &elem_orders, Eigen::VectorXi &edge_orders)
594 const int max_order = elem_orders.maxCoeff();
595 edge_orders.setConstant(mesh.
n_edges(), max_order);
597 for (
int i = 0; i < mesh.
n_faces(); i++)
599 edge_orders[mesh.
face_edge(i, j)] = std::min(edge_orders[mesh.
face_edge(i, j)], elem_orders[i]);
601 for (
int i = 0; i < mesh.
n_edges(); i++)
605 for (
int i = 0; i < mesh.
n_edges(); i++)
617 auto l2g = tri_vertices_local_to_global(mesh, f);
620 Eigen::VectorXi result(2 + (p - 1));
621 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
622 result[result.size() - 1] = find_index(l2g.begin(), l2g.end(), mesh.
switch_vertex(index).
vertex);
624 if ((result[0] == 0 && result[result.size() - 1] == 1) || (result[0] == 1 && result[result.size() - 1] == 2) || (result[0] == 2 && result[result.size() - 1] == 0))
626 for (
int i = 0; i < p - 1; ++i)
628 result[i + 1] = 3 + result[0] * (p - 1) + i;
633 for (
int i = 0; i < p - 1; ++i)
635 result[i + 1] = 3 + (result[0] + (result[0] == 0 ? 3 : 0)) * (p - 1) - i - 1;
648 auto l2g = quad_vertices_local_to_global(mesh, f);
651 Eigen::VectorXi result(2 + (q - 1));
652 result[0] = find_index(l2g.begin(), l2g.end(), index.
vertex);
653 result[result.size() - 1] = find_index(l2g.begin(), l2g.end(), mesh.
switch_vertex(index).
vertex);
655 if ((result[0] == 0 && result[result.size() - 1] == 1) || (result[0] == 1 && result[result.size() - 1] == 2) || (result[0] == 2 && result[result.size() - 1] == 3) || (result[0] == 3 && result[result.size() - 1] == 0))
657 for (
int i = 0; i < q - 1; ++i)
659 result[i + 1] = 4 + result[0] * (q - 1) + i;
664 for (
int i = 0; i < q - 1; ++i)
666 result[i + 1] = 4 + (result[0] + (result[0] == 0 ? 4 : 0)) * (q - 1) - i - 1;
677 const std::string &assembler,
678 const int quadrature_order,
679 const int mass_quadrature_order,
680 const int discr_order,
681 const bool serendipity,
682 const bool has_polys,
683 const bool is_geom_bases,
684 std::vector<ElementBases> &bases,
685 std::vector<LocalBoundary> &local_boundary,
686 std::map<int, InterfaceData> &poly_edge_to_data,
687 std::shared_ptr<MeshNodes> &mesh_nodes)
690 Eigen::VectorXi discr_orders(mesh.
n_faces());
691 discr_orders.setConstant(discr_order);
693 return build_bases(mesh, assembler, quadrature_order, mass_quadrature_order, discr_orders, serendipity, has_polys, is_geom_bases, bases, local_boundary, poly_edge_to_data, mesh_nodes);
698 const std::string &assembler,
699 const int quadrature_order,
700 const int mass_quadrature_order,
701 const Eigen::VectorXi &discr_orders,
702 const bool serendipity,
703 const bool has_polys,
704 const bool is_geom_bases,
705 std::vector<ElementBases> &bases,
706 std::vector<LocalBoundary> &local_boundary,
707 std::map<int, InterfaceData> &poly_edge_to_data,
708 std::shared_ptr<MeshNodes> &mesh_nodes)
711 assert(discr_orders.size() == mesh.
n_faces());
713 const int max_p = discr_orders.maxCoeff();
714 const int nn = max_p > 1 ? (max_p - 1) : 0;
715 const int n_face_nodes = std::max(nn * nn, max_p == 1 ? 1 : 0);
717 Eigen::VectorXi edge_orders;
720 const auto &ncmesh =
dynamic_cast<const NCMesh2D &
>(mesh);
721 compute_edge_orders(ncmesh, discr_orders, edge_orders);
724 mesh_nodes = std::make_shared<MeshNodes>(mesh, has_polys, !is_geom_bases, (max_p > 1 ? (max_p - 1) : 0) * (is_geom_bases ? 2 : 1), max_p == 0 ? 1 : n_face_nodes);
726 std::vector<std::vector<int>> element_nodes_id, edge_virtual_nodes;
727 compute_nodes(mesh, discr_orders, edge_orders, serendipity, has_polys, is_geom_bases, nodes, edge_virtual_nodes, element_nodes_id, local_boundary, poly_edge_to_data);
731 std::vector<int> interface_elements;
732 interface_elements.reserve(mesh.
n_faces());
734 for (
int e = 0; e < mesh.
n_faces(); ++e)
737 const int discr_order = discr_orders(e);
738 const int n_el_bases = element_nodes_id[e].size();
739 b.
bases.resize(n_el_bases);
741 bool skip_interface_element =
false;
743 for (
int j = 0; j < n_el_bases; ++j)
746 const int global_index = element_nodes_id[e][j];
747 if (global_index < 0)
749 skip_interface_element =
true;
754 if (skip_interface_element)
756 interface_elements.push_back(e);
761 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 2);
762 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::CUBE_LAGRANGE, 2);
774 const auto &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
777 for (
int le = 0; le < mesh2d.n_face_vertices(e); ++le)
779 if (index.edge == primitive_id)
781 index = mesh2d.next_around_face(index);
783 assert(index.edge == primitive_id);
787 for (
int j = 0; j < n_el_bases; ++j)
789 const int global_index = element_nodes_id[
e][j];
792 b.
bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
794 const int dtmp = serendipity ? -2 : discr_order;
802 const int real_order = quadrature_order > 0 ? quadrature_order :
AssemblerUtils::quadrature_order(assembler, discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 2);
803 const int real_mass_order = mass_quadrature_order > 0 ? mass_quadrature_order :
AssemblerUtils::quadrature_order(
"Mass", discr_order, AssemblerUtils::BasisType::SIMPLEX_LAGRANGE, 2);
814 const auto &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
817 for (
int le = 0; le < mesh2d.n_face_vertices(e); ++le)
819 if (index.edge == primitive_id)
821 index = mesh2d.next_around_face(index);
823 assert(index.edge == primitive_id);
829 for (
int j = 0; j < n_el_bases; ++j)
831 const int global_index = element_nodes_id[
e][j];
833 if (!skip_interface_element)
835 b.
bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
841 assert(discr_order == 2);
842 assert(w.size() == 6);
844 b.
bases[j].set_basis([discr_order, j, w](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &
val) {
846 Eigen::MatrixXd denom =
val;
850 for (
int k = 0; k < 6; ++k)
856 val = (w[j] *
val.array() / denom.array()).eval();
859 b.
bases[j].set_grad([discr_order, j, w](
const Eigen::MatrixXd &uv, Eigen::MatrixXd &
val) {
863 Eigen::MatrixXd denom = b;
865 Eigen::MatrixXd denom_prime =
val;
866 denom_prime.setZero();
869 for (
int k = 0; k < 6; ++k)
875 denom_prime += w[k] *
tmp;
878 val.col(0) = ((w[j] *
val.col(0).array() * denom.array() - w[j] * b.array() * denom_prime.col(0).array()) / (denom.array() * denom.array())).eval();
879 val.col(1) = ((w[j] *
val.col(1).array() * denom.array() - w[j] * b.array() * denom_prime.col(1).array()) / (denom.array() * denom.array())).eval();
898 Eigen::MatrixXd uv(4, 2);
899 uv << 0.1, 0.1, 0.3, 0.3, 0.9, 0.01, 0.01, 0.9;
900 Eigen::MatrixXd dx(4, 1);
901 dx.setConstant(1e-6);
902 Eigen::MatrixXd uvdx = uv;
904 Eigen::MatrixXd uvdy = uv;
906 Eigen::MatrixXd
grad,
val, vdx, vdy;
908 for (
int j = 0; j < n_el_bases; ++j)
910 b.bases[j].eval_grad(uv, grad);
912 b.bases[j].eval_basis(uv,
val);
913 b.bases[j].eval_basis(uvdx, vdx);
914 b.bases[j].eval_basis(uvdy, vdy);
916 assert((
grad.col(0) - (vdx -
val) / 1e-6).norm() < 1e-4);
917 assert((
grad.col(1) - (vdy -
val) / 1e-6).norm() < 1e-4);
927 const auto &ncmesh =
dynamic_cast<const NCMesh2D &
>(mesh);
929 std::vector<std::vector<int>> elementOrder;
931 const int max_order = discr_orders.maxCoeff(), min_order = discr_orders.minCoeff();
933 for (
int e = 0;
e < ncmesh.n_faces();
e++)
934 if (max_level < ncmesh.face_ref_level(e))
937 elementOrder.resize((max_level + 1) * (max_order - min_order + 1));
940 while (cur_level <= max_level)
942 int order = min_order;
943 while (order <= max_order)
945 int cur_bucket = (max_order - min_order + 1) * cur_level + (order - min_order);
946 for (
int i = 0; i < ncmesh.n_faces(); i++)
948 if (ncmesh.face_ref_level(i) != cur_level || discr_orders[i] != order)
952 elementOrder[cur_bucket].push_back(i);
960 for (
const auto &bucket : elementOrder)
962 if (bucket.size() == 0)
964 for (
const int e : bucket)
967 const int discr_order = discr_orders(e);
968 const int n_el_bases = element_nodes_id[
e].size();
970 for (
int j = 0; j < n_el_bases; ++j)
972 const int global_index = element_nodes_id[
e][j];
974 if (global_index >= 0)
975 b.
bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
978 int large_edge = -1, local_edge = -1;
979 int opposite_element = -1;
980 bool is_on_leader_edge =
false;
982 if (j < 3 + 3 * (discr_order - 1) && j >= 3)
984 local_edge = (j - 3) / (discr_order - 1);
985 large_edge = ncmesh.face_edge(e, local_edge);
986 if (ncmesh.n_follower_edges(large_edge) > 0)
987 is_on_leader_edge =
true;
991 if (is_on_leader_edge)
993 const int edge_order = edge_orders[large_edge];
997 indices.resize(discr_order + 1);
998 for (
int i = 0; i < discr_order - 1; i++)
1000 indices[i + 1] = 3 + local_edge * (discr_order - 1) + i;
1002 int index =
indices[(j - 3) % (discr_order - 1) + 1];
1005 Eigen::MatrixXd lnodes;
1007 Eigen::Vector2d node_position = lnodes.row(index);
1009 std::function<double(
const int,
const int,
const double)> basis_1d = [](
const int order,
const int id,
const double x) ->
double {
1010 assert(id <= order && id >= 0);
1012 for (
int o = 0; o <= order; o++)
1015 y *= (
x * order - o) / (
id - o);
1022 for (
int i = 0; i < edge_virtual_nodes[large_edge].size(); i++)
1024 const int global_index = edge_virtual_nodes[large_edge][i];
1025 const double weight = basis_1d(edge_order, i + 1,
x);
1026 if (std::abs(weight) < 1e-12)
1028 b.
bases[j].global().emplace_back(global_index,
nodes.node_position(global_index), weight);
1031 const auto &global_1 = b.
bases[local_edge].global();
1032 const auto &global_2 = b.
bases[(local_edge + 1) % 3].global();
1033 double weight = basis_1d(edge_order, 0,
x);
1034 if (std::abs(weight) > 1e-12)
1035 for (
size_t ii = 0; ii < global_1.size(); ++ii)
1036 b.
bases[j].global().emplace_back(global_1[ii].index, global_1[ii].node, weight * global_1[ii].
val);
1037 weight = basis_1d(edge_order, edge_order,
x);
1038 if (std::abs(weight) > 1e-12)
1039 for (
size_t ii = 0; ii < global_2.size(); ++ii)
1040 b.
bases[j].global().emplace_back(global_2[ii].index, global_2[ii].node, weight * global_2[ii].
val);
1044 Eigen::MatrixXd global_position;
1048 const int v_id = ncmesh.face_vertex(e, j);
1049 large_edge = ncmesh.leader_edge_of_vertex(v_id);
1050 opposite_element = ncmesh.face_neighbor(large_edge);
1051 global_position = ncmesh.point(v_id);
1057 int small_edge = ncmesh.face_edge(e, local_edge);
1058 large_edge = ncmesh.leader_edge_of_edge(small_edge);
1061 if (ncmesh.n_face_neighbors(small_edge) == 1)
1062 opposite_element = ncmesh.face_neighbor(large_edge);
1067 idx.
edge = small_edge;
1068 idx.
vertex = ncmesh.edge_vertex(small_edge, 0);
1071 opposite_element = ncmesh.switch_face(idx).face;
1075 Eigen::MatrixXd lnodes;
1077 global_position = lnodes.row(j);
1079 Eigen::MatrixXd verts(ncmesh.n_face_vertices(e), 2);
1080 for (
int i = 0; i < verts.rows(); i++)
1081 verts.row(i) = ncmesh.point(ncmesh.face_vertex(e, i));
1083 Eigen::MatrixXd local_position = lnodes.row(j);
1084 local_to_global(verts, local_position, global_position);
1087 Eigen::MatrixXd verts(ncmesh.n_face_vertices(e), 2);
1088 for (
int i = 0; i < verts.rows(); i++)
1089 verts.row(i) = ncmesh.point(ncmesh.face_vertex(opposite_element, i));
1091 Eigen::MatrixXd node_position;
1092 global_to_local(verts, global_position, node_position);
1095 const auto &other_bases = bases[opposite_element];
1096 std::vector<AssemblyValues> w;
1097 other_bases.evaluate_bases(node_position, w);
1100 for (
long i = 0; i < w.size(); ++i)
1102 assert(w[i].
val.size() == 1);
1103 if (std::abs(w[i].
val(0)) < 1e-12)
1106 for (
size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
1108 const auto &other_global = other_bases.bases[i].global()[ii];
1109 b.
bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
1114 auto &global_ = b.
bases[j].global();
1115 if (global_.size() <= 1)
1118 std::map<int, Local2Global> list;
1119 for (
size_t ii = 0; ii < global_.size(); ii++)
1121 auto pair = list.insert({global_[ii].index, global_[ii]});
1122 if (!pair.second && pair.first != list.end())
1124 assert((pair.first->second.node - global_[ii].node).norm() < 1e-12);
1125 pair.first->second.val += global_[ii].val;
1130 for (
auto it = list.begin(); it != list.end(); ++it)
1132 if (std::abs(it->second.val) > 1
e-12)
1133 global_.push_back(it->second);
1143 for (
int pp = 2; pp <= autogen::MAX_P_BASES; ++pp)
1146 for (
int e : interface_elements)
1149 const int discr_order = discr_orders(e);
1150 const int n_el_bases = element_nodes_id[
e].size();
1152 assert(discr_order > 1);
1153 if (discr_order != pp)
1163 for (
int j = 0; j < n_el_bases; ++j)
1165 const int global_index = element_nodes_id[
e][j];
1167 if (global_index >= 0)
1168 b.
bases[j].init(discr_order, global_index, j,
nodes.node_position(global_index));
1171 const auto le = -(global_index + 1);
1173 auto v = tri_vertices_local_to_global(mesh, e);
1174 Eigen::Matrix<int, 3, 2> ev;
1175 ev.row(0) << v[0], v[1];
1176 ev.row(1) << v[1], v[2];
1177 ev.row(2) << v[2], v[0];
1179 const auto index = mesh.switch_face(find_edge(mesh, e, ev(le, 0), ev(le, 1)));
1180 const auto other_face = index.face;
1182 Eigen::RowVector2d node_position;
1183 assert(discr_order > 1);
1186 Eigen::MatrixXd lnodes;
1190 node_position = lnodes.row(
indices(0));
1191 else if (j < 3 + 3 * (discr_order - 1))
1192 node_position = lnodes.row(
indices(((j - 3) % (discr_order - 1)) + 1));
1196 const auto &other_bases = bases[other_face];
1198 std::vector<AssemblyValues> w;
1199 other_bases.evaluate_bases(node_position, w);
1201 for (
long i = 0; i < w.size(); ++i)
1203 assert(w[i].
val.size() == 1);
1204 if (std::abs(w[i].
val(0)) < 1e-8)
1207 for (
size_t ii = 0; ii < other_bases.bases[i].global().size(); ++ii)
1209 const auto &other_global = other_bases.bases[i].global()[ii];
1211 b.
bases[j].global().emplace_back(other_global.index, other_global.node, w[i].val(0) * other_global.val);
1226 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 int build_bases(const mesh::Mesh2D &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_edge_to_data, std::shared_ptr< mesh::MeshNodes > &mesh_nodes)
Builds FE basis functions over the entire mesh (P1, P2 over triangles, Q1, Q2 over quads).
static Eigen::VectorXi tri_edge_local_nodes(const int p, const mesh::Mesh2D &mesh, mesh::Navigation::Index index)
static Eigen::VectorXi quad_edge_local_nodes(const int q, const mesh::Mesh2D &mesh, mesh::Navigation::Index index)
Boundary primitive IDs for a single element.
Navigation::Index next_around_face(Navigation::Index idx) const
bool is_volume() const override
checks if mesh is volume
virtual Navigation::Index switch_vertex(Navigation::Index idx) const =0
virtual Navigation::Index get_index_from_face(int f, int lv=0) const =0
virtual Navigation::Index switch_face(Navigation::Index idx) const =0
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
virtual int n_vertices() const =0
number of vertices
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 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 bool is_boundary_edge(const int edge_global_id) const =0
is edge boundary
virtual int n_faces() const =0
number of faces
virtual int n_edges() const =0
number of edges
virtual int n_face_vertices(const int f_id) const =0
number of vertices of a face
virtual int face_vertex(const int f_id, const int lv_id) const =0
id of the face vertex
int face_edge(const int f_id, const int le_id) const
static double element_weight_to_edge_weight(const int l, const Eigen::Vector2d &pos)
int n_faces() const override
number of faces
int leader_edge_of_edge(const int e_id) const
int face_ref_level(const int f_id) const
int n_face_vertices(const int f_id) const override
number of vertices of a face
int n_edges() const override
number of edges
void get_quadrature(const int order, Quadrature &quad)
void get_quadrature(const int order, Quadrature &quad)
enable_if_t< t==0 > sort(Eigen::Matrix< T, 3, 3 > &U, Eigen::Matrix< T, 3, 1 > &sigma, Eigen::Matrix< T, 3, 3 > &V)
Helper function of 3X3 SVD for sorting singular values.
void p_grad_basis_value_2d(const int p, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void q_grad_basis_value_2d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void p_nodes_2d(const int p, Eigen::MatrixXd &val)
void q_basis_value_2d(const int q, const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val)
void p_basis_value_2d(const int p, 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