34#include <paraviewo/VTMWriter.hpp>
35#include <paraviewo/PVDWriter.hpp>
37#include <ipc/potentials/normal_adhesion_potential.hpp>
38#include <ipc/potentials/tangential_adhesion_potential.hpp>
40#include <SimpleBVH/BVH.hpp>
42#include <igl/write_triangle_mesh.h>
44#include <igl/facet_adjacency_matrix.h>
45#include <igl/connected_components.h>
66 void add_output_fields(
67 paraviewo::ParaviewWriter &writer,
74 for (
const OutputField &field : output_fields(sample))
76 if (field.values.rows() <= 0)
79 const int expected_rows =
83 if (field.values.rows() != expected_rows)
86 "Skipping output field '{}' with {} rows; expected {} {} rows",
87 field.name, field.values.rows(), expected_rows,
93 writer.add_cell_field(field.name, field.values);
95 writer.add_field(field.name, field.values);
99 void avoid_pyramid_apex(Eigen::MatrixXd &points)
101 assert(points.cols() == 3);
102 constexpr double eps = 1e-8;
103 for (
int i = 0; i < points.rows(); ++i)
105 if (std::abs(points(i, 2) - 1.0) < eps)
106 points(i, 2) = 1.0 - eps;
110 void pyramid_nodes_for_output(
const int order, Eigen::MatrixXd &points)
113 avoid_pyramid_apex(points);
122 constexpr long PROXY_PARAM_SCALE = 840;
126 const int p = b.bases.empty() ? 1 : b.bases.front().order();
127 const int n_tri = (p + 1) * (p + 2) / 2;
128 const int q = (n_tri > 0 && b.bases.size() % n_tri == 0) ?
int(b.bases.size()) / n_tri - 1 : p;
129 return std::max(1, q);
135 const int p = b.bases.empty() ? 1 : b.bases.front().order();
146 return ref_nodes.rows() == long(b.bases.size());
149 int element_n_vertices(
const mesh::Mesh &mesh,
const int el)
163 std::vector<std::pair<int, int>> element_ref_edges(
const mesh::Mesh &mesh,
const int el,
const Eigen::MatrixXd &ref_nodes)
165 const int nv = element_n_vertices(mesh, el);
166 const auto n_coord_diffs = [&](
int a,
int b) {
168 for (
int c = 0; c < 3; ++c)
169 if (std::abs(ref_nodes(a, c) - ref_nodes(b, c)) > 1e-12)
175 for (
int a = 0; a < nv; ++a)
176 if (std::abs(ref_nodes(a, 2) - 1.0) < 1e-12)
179 std::vector<std::pair<int, int>> edges;
180 for (
int a = 0; a < nv; ++a)
182 for (
int b = a + 1; b < nv; ++b)
188 is_edge = std::abs(ref_nodes(a, 2) - ref_nodes(b, 2)) < 1e-12
189 || (std::abs(ref_nodes(a, 0) - ref_nodes(b, 0)) < 1e-12
190 && std::abs(ref_nodes(a, 1) - ref_nodes(b, 1)) < 1e-12);
192 is_edge = (a == apex || b == apex) || n_coord_diffs(a, b) == 1;
194 is_edge = n_coord_diffs(a, b) == 1;
196 edges.emplace_back(a, b);
209 using EdgeDofs = std::vector<std::tuple<long, int, Eigen::Vector3d>>;
210 std::map<std::pair<int, int>, EdgeDofs> build_edge_dofs(
const mesh::Mesh &mesh,
const std::vector<basis::ElementBases> &bases)
212 std::map<std::pair<int, int>, EdgeDofs> edge_dofs;
213 Eigen::MatrixXd ref_nodes;
214 for (
int el = 0; el < int(bases.size()); ++el)
217 if (b.bases.empty() || !element_ref_nodes(mesh, el, b, ref_nodes))
220 const int nv = element_n_vertices(mesh, el);
221 std::vector<int> vd(nv, -1);
223 for (
int i = 0; i < nv; ++i)
225 const auto &glob = b.bases[i].global();
226 assert(glob.size() == 1);
227 if (glob.size() != 1)
232 vd[i] = glob.front().index;
237 const auto edges = element_ref_edges(mesh, el, ref_nodes);
238 for (
int j = nv; j < int(b.bases.size()); ++j)
240 const auto &glob = b.bases[j].global();
241 if (glob.size() != 1)
243 const Eigen::RowVector3d r = ref_nodes.row(j);
244 for (
const auto &e : edges)
246 const Eigen::RowVector3d pa = ref_nodes.row(e.first);
247 const Eigen::RowVector3d d = ref_nodes.row(e.second) - pa;
248 const double t = (r - pa).dot(d) / d.squaredNorm();
249 if (t < 1e-9 || t > 1 - 1e-9 || ((r - pa) - t * d).norm() > 1e-9)
251 long tl = std::lround(t * PROXY_PARAM_SCALE);
252 assert(std::abs(t * PROXY_PARAM_SCALE - tl) < 1e-6);
253 int va = vd[e.first], vb = vd[e.second];
257 tl = PROXY_PARAM_SCALE - tl;
259 EdgeDofs &ed = edge_dofs[{va, vb}];
260 const int dof = glob.front().index;
261 bool present =
false;
262 for (
const auto &existing : ed)
263 if (std::get<1>(existing) == dof)
265 assert(std::get<0>(existing) == tl);
270 ed.emplace_back(tl, dof, glob.front().node.transpose());
275 for (
auto &kv : edge_dofs)
276 std::sort(kv.second.begin(), kv.second.end(),
277 [](
const EdgeDofs::value_type &
x,
const EdgeDofs::value_type &
y) {
return std::get<0>(
x) < std::get<0>(
y); });
288 bool triangulate_lattice(
const std::vector<std::array<long, 2>> &pts,
const int nfv, std::vector<std::array<int, 3>> &tris)
290 constexpr long S = PROXY_PARAM_SCALE;
291 const int n = int(pts.size());
292 std::map<std::array<long, 2>,
int> id;
293 for (
int i = 0; i < n; ++i)
294 if (!
id.emplace(pts[i], i).second)
301 const int k = int(std::lround((std::sqrt(8.0 * n + 1.0) - 3.0) / 2.0));
302 if ((k + 1) * (k + 2) / 2 != n || k < 1 || S % k != 0)
304 const long h = S / k;
305 const auto at = [&](
int i,
int j) ->
int {
306 const auto it =
id.find({{i * h, j * h}});
307 return it ==
id.end() ? -1 : it->second;
309 for (
int j = 0; j <= k; ++j)
310 for (
int i = 0; i <= k - j; ++i)
313 for (
int j = 0; j < k; ++j)
315 for (
int i = 0; i < k - j; ++i)
317 tris.push_back({{at(i, j), at(i + 1, j), at(i, j + 1)}});
319 tris.push_back({{at(i + 1, j), at(i + 1, j + 1), at(i, j + 1)}});
326 std::set<long> us, vs;
327 for (
const auto &p : pts)
332 const int k1 = int(us.size()) - 1, k2 = int(vs.size()) - 1;
333 if (k1 < 1 || k2 < 1 || (k1 + 1) * (k2 + 1) != n || S % k1 != 0 || S % k2 != 0)
335 const long h1 = S / k1, h2 = S / k2;
336 const auto at = [&](
int i,
int j) ->
int {
337 const auto it =
id.find({{i * h1, j * h2}});
338 return it ==
id.end() ? -1 : it->second;
340 for (
int j = 0; j <= k2; ++j)
341 for (
int i = 0; i <= k1; ++i)
344 for (
int j = 0; j < k2; ++j)
346 for (
int i = 0; i < k1; ++i)
348 tris.push_back({{at(i, j), at(i + 1, j), at(i, j + 1)}});
349 tris.push_back({{at(i + 1, j + 1), at(i, j + 1), at(i + 1, j)}});
361 void triangulate_convex_pointset(
const std::vector<std::array<long, 2>> &pts, std::vector<std::array<int, 3>> &tris)
363 const int n = int(pts.size());
365 std::vector<int> order(n);
366 for (
int i = 0; i < n; ++i)
368 std::sort(order.begin(), order.end(), [&](
int a,
int b) { return pts[a] < pts[b]; });
369 const auto orient = [&](
int a,
int b,
int c) ->
long long {
370 return (
long long)(pts[b][0] - pts[a][0]) * (pts[c][1] - pts[a][1])
371 - (
long long)(pts[b][1] - pts[a][1]) * (pts[c][0] - pts[a][0]);
375 std::vector<int> hull;
379 if (hull.size() >= 2 && orient(hull[0], hull[1], order[k]) != 0)
381 hull.push_back(order[k]);
388 const int p = order[k];
389 const bool left = orient(hull[0], hull[1], p) > 0;
390 for (
int i = 0; i + 1 < int(hull.size()); ++i)
393 tris.push_back({{hull[i], hull[i + 1], p}});
395 tris.push_back({{hull[i + 1], hull[i], p}});
398 std::reverse(hull.begin(), hull.end());
405 const int p = order[k];
406 const int m = int(hull.size());
408 std::vector<bool> vis(m);
409 for (
int i = 0; i < m; ++i)
410 vis[i] = orient(hull[i], hull[(i + 1) % m], p) < 0;
412 while (s < m && !(vis[s] && !vis[(s + m - 1) % m]))
419 tris.push_back({{hull[e % m], p, hull[(e + 1) % m]}});
423 std::vector<int> new_hull;
424 new_hull.push_back(p);
425 for (
int i = e % m; i != s; i = (i + 1) % m)
426 new_hull.push_back(hull[i]);
427 new_hull.push_back(hull[s]);
437 const std::vector<basis::ElementBases> &bases,
438 const std::vector<mesh::LocalBoundary> &total_local_boundary,
439 Eigen::MatrixXd &node_positions,
440 Eigen::MatrixXi &boundary_edges,
441 Eigen::MatrixXi &boundary_triangles,
442 std::vector<Eigen::Triplet<double>> &displacement_map_entries,
443 const int sampling_order)
457 logger().warn(
"max_order collision-proxy sampling requires a conforming volume mesh without polytopes; falling back to the standard boundary extraction");
459 node_positions, boundary_edges, boundary_triangles, displacement_map_entries);
463 displacement_map_entries.clear();
464 const Mesh3D &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
467 if (sampling_order > 0)
475 int o = b.bases.front().order();
477 o = std::max(o, prism_q_order(b));
487 std::map<std::array<int, 4>,
int> vertex_id;
488 std::vector<Eigen::Vector3d> vertices;
489 std::vector<std::tuple<int, int, int>> proxy_tris;
493 const int el = lb.element_id();
495 Eigen::MatrixXd ref_nodes;
496 if (b.bases.empty() || !element_ref_nodes(mesh, el, b, ref_nodes))
504 for (
int i = 0; i < ref_nodes.rows(); ++i)
505 if (std::abs(ref_nodes(i, 2) - 1.0) < 1e-12)
511 for (
int j = 0; j < lb.size(); ++j)
513 const int eid = lb.global_primitive_id(j);
514 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh3d);
516 assert(nfv == 3 || nfv == 4);
517 assert(nodes.size() >= nfv);
523 std::vector<Eigen::RowVector3d> c(nfv);
524 for (
int k = 0; k < nfv; ++k)
525 c[k] = ref_nodes.row(nodes(k));
534 assert(nav.
face == eid);
535 std::vector<int> gv(nfv);
538 for (
int k = 0; k < nfv; ++k)
546 std::vector<std::array<int, 2>> coords;
547 std::vector<std::tuple<int, int, int>> local_tris;
550 pts.resize((M + 1) * (M + 2) / 2, 3);
551 coords.resize(pts.rows());
552 std::vector<int> off(M + 2, 0);
553 for (
int r = 0; r <= M; ++r)
554 off[r + 1] = off[r] + (M + 1 - r);
555 for (
int r = 0; r <= M; ++r)
556 for (
int i = 0; i <= M - r; ++i)
558 pts.row(off[r] + i) = c[0] + (double(i) / M) * (c[1] - c[0]) + (double(r) / M) * (c[2] - c[0]);
559 coords[off[r] + i] = {{i, r}};
561 for (
int r = 0; r < M; ++r)
563 for (
int i = 0; i < M - r; ++i)
565 local_tris.emplace_back(off[r] + i, off[r] + i + 1, off[r + 1] + i);
567 local_tris.emplace_back(off[r] + i + 1, off[r + 1] + i + 1, off[r + 1] + i);
573 pts.resize((M + 1) * (M + 1), 3);
574 coords.resize(pts.rows());
575 const auto gid = [M](
const int i,
const int r) {
return r * (M + 1) + i; };
576 for (
int r = 0; r <= M; ++r)
578 for (
int i = 0; i <= M; ++i)
580 const double u = double(i) / M, v = double(r) / M;
581 pts.row(gid(i, r)) = (1 - u) * (1 - v) * c[0] + u * (1 - v) * c[1] + u * v * c[2] + (1 - u) * v * c[3];
582 coords[gid(i, r)] = {{i, r}};
585 for (
int r = 0; r < M; ++r)
587 for (
int i = 0; i < M; ++i)
589 local_tris.emplace_back(gid(i, r), gid(i + 1, r), gid(i, r + 1));
590 local_tris.emplace_back(gid(i + 1, r + 1), gid(i, r + 1), gid(i + 1, r));
595 std::vector<polyfem::assembler::AssemblyValues>
vals;
596 b.evaluate_bases(pts,
vals);
598 const auto edge_key = [M](
const int va,
const int vb,
const int step) {
599 return va < vb ? std::array<int, 4>{{1, va, vb, step}}
600 : std::array<int, 4>{{1, vb, va, M - step}};
603 std::vector<int> ids(pts.rows());
604 for (
int s = 0; s < pts.rows(); ++s)
606 const int i = coords[s][0], r = coords[s][1];
607 std::array<int, 4> key;
610 if (r == 0 && i == 0)
611 key = {{0, gv[0], 0, 0}};
612 else if (r == 0 && i == M)
613 key = {{0, gv[1], 0, 0}};
615 key = {{0, gv[2], 0, 0}};
617 key = edge_key(gv[0], gv[1], i);
619 key = edge_key(gv[0], gv[2], r);
621 key = edge_key(gv[1], gv[2], r);
623 key = {{2, eid, i, r}};
627 if (i == 0 && r == 0)
628 key = {{0, gv[0], 0, 0}};
629 else if (i == M && r == 0)
630 key = {{0, gv[1], 0, 0}};
631 else if (i == M && r == M)
632 key = {{0, gv[2], 0, 0}};
633 else if (i == 0 && r == M)
634 key = {{0, gv[3], 0, 0}};
636 key = edge_key(gv[0], gv[1], i);
638 key = edge_key(gv[1], gv[2], r);
640 key = edge_key(gv[3], gv[2], i);
642 key = edge_key(gv[0], gv[3], r);
644 key = {{2, eid, i, r}};
647 const auto it = vertex_id.find(key);
648 if (it != vertex_id.end())
654 Eigen::Vector3d pos = Eigen::Vector3d::Zero();
656 if (apex_node >= 0 && std::abs(pts(s, 2) - 1.0) < 1e-12)
658 for (
const auto &g : b.bases[apex_node].global())
660 pos += g.val * g.node.transpose();
665 for (
size_t i2 = 0; i2 <
vals.size(); ++i2)
667 const double Ni =
vals[i2].val(s);
668 if (std::abs(Ni) < 1e-12)
670 for (
const auto &g : b.bases[i2].global())
672 pos += Ni * g.val * g.node.transpose();
673 weights[g.index] += Ni * g.val;
676 assert(pos.allFinite());
678 const int vid = int(vertices.size());
679 vertex_id[key] = vid;
680 vertices.push_back(pos);
682 if (std::abs(kv.second) > 1e-10)
683 displacement_map_entries.emplace_back(vid, kv.first, kv.second);
687 for (
const auto &t : local_tris)
688 proxy_tris.emplace_back(ids[std::get<0>(t)], ids[std::get<1>(t)], ids[std::get<2>(t)]);
692 node_positions.resize(vertices.size(), 3);
693 for (
int i = 0; i < int(vertices.size()); ++i)
694 node_positions.row(i) = vertices[i];
696 boundary_triangles.resize(proxy_tris.size(), 3);
697 for (
int i = 0; i < int(proxy_tris.size()); ++i)
698 boundary_triangles.row(i) << std::get<0>(proxy_tris[i]), std::get<2>(proxy_tris[i]), std::get<1>(proxy_tris[i]);
700 if (boundary_triangles.rows() > 0)
701 igl::edges(boundary_triangles, boundary_edges);
703 if (
const char *dump = getenv(
"POLYFEM_DUMP_COLLISION_PROXY"))
704 igl::write_triangle_mesh(dump, node_positions, boundary_triangles);
710 const std::vector<basis::ElementBases> &bases,
711 const std::vector<mesh::LocalBoundary> &total_local_boundary,
712 Eigen::MatrixXd &node_positions,
713 Eigen::MatrixXi &boundary_edges,
714 Eigen::MatrixXi &boundary_triangles,
715 std::vector<Eigen::Triplet<double>> &displacement_map_entries)
719 displacement_map_entries.clear();
725 logger().warn(
"Skipping as the mesh has polygons");
731 std::vector<Eigen::Vector3d> node_positions_vec;
732 node_positions_vec.reserve(n_bases + (is_simplicial ? 0 : mesh.
n_faces()));
736 const Mesh3D &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
738 std::vector<std::tuple<int, int, int>> tris;
740 std::vector<bool> visited_node(n_bases,
false);
742 std::stringstream print_warning;
756 bool has_prism_or_pyramid =
false;
761 has_prism_or_pyramid =
true;
768 const auto edge_dofs = build_edge_dofs(mesh, bases);
769 constexpr long S = PROXY_PARAM_SCALE;
771 const auto emit_dof = [&](
const int gindex,
const Eigen::Vector3d &pos) {
772 if (gindex >=
int(node_positions_vec.size()))
773 node_positions_vec.resize(gindex + 1, Eigen::Vector3d::Zero());
774 node_positions_vec[gindex] = pos;
775 if (!visited_node[gindex])
776 displacement_map_entries.emplace_back(gindex, gindex, 1);
777 visited_node[gindex] =
true;
780 Eigen::MatrixXd ref_nodes;
783 const int el = lb.element_id();
785 if (b.bases.empty() || !element_ref_nodes(mesh, el, b, ref_nodes))
788 for (
int j = 0; j < lb.size(); ++j)
790 const int eid = lb.global_primitive_id(j);
791 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh3d);
793 assert(nfv == 3 || nfv == 4);
794 assert(nodes.size() >= nfv);
797 std::vector<std::array<long, 2>> pts;
798 std::vector<int> dof;
801 std::array<std::array<long, 2>, 4> cp;
803 cp = {{{{0, 0}}, {{S, 0}}, {{0, S}}, {{0, 0}}}};
805 cp = {{{{0, 0}}, {{S, 0}}, {{S, S}}, {{0, S}}}};
806 std::array<int, 4> cd{{-1, -1, -1, -1}};
808 for (
int k = 0; k < nfv; ++k)
810 const auto &glob = b.bases[nodes(k)].global();
811 assert(glob.size() == 1);
812 if (glob.size() != 1)
817 cd[k] = glob.front().index;
818 pts.push_back(cp[k]);
819 dof.push_back(cd[k]);
820 emit_dof(cd[k], glob.front().node.transpose());
826 for (
int k = 0; k < nfv; ++k)
828 const int va = cd[k], vb = cd[(k + 1) % nfv];
829 const auto it = edge_dofs.find({std::min(va, vb), std::max(va, vb)});
830 if (it == edge_dofs.end())
832 for (
const auto &ed : it->second)
834 const long s = va < vb ? std::get<0>(ed) : S - std::get<0>(ed);
835 const auto &A = cp[k];
836 const auto &B = cp[(k + 1) % nfv];
837 pts.push_back({{(A[0] * (S - s) + B[0] * s) / S, (A[1] * (S - s) + B[1] * s) / S}});
838 dof.push_back(std::get<1>(ed));
839 emit_dof(std::get<1>(ed), std::get<2>(ed));
845 const Eigen::RowVector3d c0 = ref_nodes.row(nodes(0));
846 const Eigen::RowVector3d A3 = ref_nodes.row(nodes(1)) - c0;
847 const Eigen::RowVector3d B3 = ref_nodes.row(nodes(nfv - 1)) - c0;
848 const double aa = A3.squaredNorm(), bb = B3.squaredNorm(), ab = A3.dot(B3);
849 const double det = aa * bb - ab * ab;
850 for (
long n = nfv; n < nodes.size(); ++n)
852 const auto &glob = b.bases[nodes(n)].global();
853 if (glob.size() != 1)
855 const Eigen::RowVector3d d3 = ref_nodes.row(nodes(n)) - c0;
856 const double du = d3.dot(A3), dv = d3.dot(B3);
857 const double u = (du * bb - dv * ab) / det;
858 const double v = (dv * aa - du * ab) / det;
859 const long lu = std::lround(u * S), lv = std::lround(v * S);
860 assert(std::abs(u * S - lu) < 1e-6 && std::abs(v * S - lv) < 1e-6);
862 const bool on_edge = nfv == 3
863 ? (lu == 0 || lv == 0 || lu + lv == S)
864 : (lu == 0 || lu == S || lv == 0 || lv == S);
867 pts.push_back({{lu, lv}});
868 dof.push_back(glob.front().index);
869 emit_dof(glob.front().index, glob.front().node.transpose());
872 std::vector<std::array<int, 3>> local_tris;
873 if (!triangulate_lattice(pts, nfv, local_tris))
874 triangulate_convex_pointset(pts, local_tris);
875 for (
const auto &t : local_tris)
876 tris.emplace_back(dof[t[0]], dof[t[1]], dof[t[2]]);
882 node_positions_vec.resize(
883 std::max(node_positions_vec.size(),
size_t(n_bases)), Eigen::Vector3d::Zero());
885 node_positions.resize(node_positions_vec.size(), 3);
886 for (
int i = 0; i < int(node_positions_vec.size()); ++i)
887 node_positions.row(i) = node_positions_vec[i];
889 boundary_triangles.resize(tris.size(), 3);
890 for (
int i = 0; i < int(tris.size()); ++i)
891 boundary_triangles.row(i) << std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]);
893 if (boundary_triangles.rows() > 0)
894 igl::edges(boundary_triangles, boundary_edges);
896 if (
const char *dump = getenv(
"POLYFEM_DUMP_COLLISION_PROXY"))
897 igl::write_triangle_mesh(dump, node_positions, boundary_triangles);
906 for (
int j = 0; j < lb.size(); ++j)
908 const int eid = lb.global_primitive_id(j);
909 const int lid = lb[j];
910 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh3d);
912 if (mesh.
is_cube(lb.element_id()))
914 assert(!is_simplicial);
916 std::vector<int> loc_nodes;
919 for (
long n = 0; n < nodes.size(); ++n)
921 auto &bs = b.bases[nodes(n)];
922 const auto &glob = bs.global();
923 if (glob.size() != 1)
926 int gindex = glob.front().index;
927 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), gindex + 1));
928 node_positions_vec[gindex] = glob.front().node;
929 bary += glob.front().node;
930 loc_nodes.push_back(gindex);
933 if (loc_nodes.size() != 4)
935 logger().trace(
"skipping element {} since it is not Q1", eid);
941 const int new_node = n_bases + eid;
942 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), new_node + 1));
943 node_positions_vec[new_node] = bary;
944 tris.emplace_back(loc_nodes[1], loc_nodes[0], new_node);
945 tris.emplace_back(loc_nodes[2], loc_nodes[1], new_node);
946 tris.emplace_back(loc_nodes[3], loc_nodes[2], new_node);
947 tris.emplace_back(loc_nodes[0], loc_nodes[3], new_node);
949 for (
int q = 0; q < 4; ++q)
951 if (!visited_node[loc_nodes[q]])
952 displacement_map_entries.emplace_back(loc_nodes[q], loc_nodes[q], 1);
954 visited_node[loc_nodes[q]] =
true;
955 displacement_map_entries.emplace_back(new_node, loc_nodes[q], 0.25);
960 else if (mesh.
is_prism(lb.element_id()))
962 assert(!is_simplicial);
964 std::vector<int> loc_nodes;
965 std::vector<int> loc_local_nodes;
967 for (
long n = 0; n < nodes.size(); ++n)
969 auto &bs = b.bases[nodes(n)];
970 const auto &glob = bs.global();
971 if (glob.size() != 1)
974 int gindex = glob.front().index;
975 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), gindex + 1));
976 node_positions_vec[gindex] = glob.front().node;
977 loc_nodes.push_back(gindex);
978 loc_local_nodes.push_back(nodes(n));
981 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
982 for (
int k = 0; k < loc_nodes.size(); ++k)
984 if (!visited_node[loc_nodes[k]])
985 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
987 visited_node[loc_nodes[k]] =
true;
994 if (loc_nodes.size() == 3)
996 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
998 update_mapping(loc_nodes);
1000 else if (loc_nodes.size() == 6)
1002 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
1003 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
1004 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
1005 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
1007 update_mapping(loc_nodes);
1009 else if (loc_nodes.size() == 10)
1011 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
1012 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
1013 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
1014 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
1015 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
1016 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
1017 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
1018 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
1019 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
1020 update_mapping(loc_nodes);
1024 logger().trace(
"skipping element {} since it is not linear, it has {} nodes", eid, loc_nodes.size());
1029 if (loc_nodes.size() < 4 || loc_local_nodes.size() < 4)
1031 logger().trace(
"skipping prism quad face {} since it has only {} complete nodes", eid, loc_nodes.size());
1035 const int p = b.bases.empty() ? -1 : b.bases.front().order();
1036 const int n_tri_nodes = (p + 1) * (p + 2) / 2;
1037 const int q = n_tri_nodes > 0 && b.bases.size() % n_tri_nodes == 0 ? int(b.bases.size()) / n_tri_nodes - 1 : -1;
1039 if (p < 1 || p > 3 || q < 1 || q > 3 || (p == 3 && q == 3))
1041 logger().trace(
"skipping prism quad face {} with unsupported p={}, q={}", eid, p, q);
1045 auto is_vertical_prism_edge = [](
const int a,
const int b) {
1046 return (a >= 0 && a < 3 && b == a + 3) || (b >= 0 && b < 3 && a == b + 3);
1049 std::vector<int> edge_orders(4);
1050 for (
int k = 0; k < 4; ++k)
1051 edge_orders[k] = is_vertical_prism_edge(loc_local_nodes[k], loc_local_nodes[(k + 1) % 4]) ? q : p;
1053 const int u_order = edge_orders[0];
1054 const int v_order = edge_orders[1];
1055 const int expected_nodes = (u_order + 1) * (v_order + 1);
1056 if (loc_nodes.size() != expected_nodes || edge_orders[0] != edge_orders[2] || edge_orders[1] != edge_orders[3])
1058 logger().trace(
"skipping prism quad face {} with p={}, q={} and {} nodes", eid, p, q, loc_nodes.size());
1062 std::vector<int> grid(expected_nodes, -1);
1063 auto grid_index = [u_order](
const int i,
const int j) {
1064 return j * (u_order + 1) + i;
1067 grid[grid_index(0, 0)] = loc_nodes[0];
1068 grid[grid_index(u_order, 0)] = loc_nodes[1];
1069 grid[grid_index(u_order, v_order)] = loc_nodes[2];
1070 grid[grid_index(0, v_order)] = loc_nodes[3];
1073 for (
int i = 1; i < u_order; ++i)
1074 grid[grid_index(i, 0)] = loc_nodes[node_index++];
1075 for (
int j = 1; j < v_order; ++j)
1076 grid[grid_index(u_order, j)] = loc_nodes[node_index++];
1077 for (
int i = u_order - 1; i > 0; --i)
1078 grid[grid_index(i, v_order)] = loc_nodes[node_index++];
1079 for (
int j = v_order - 1; j > 0; --j)
1080 grid[grid_index(0, j)] = loc_nodes[node_index++];
1082 for (
int j = 1; j < v_order; ++j)
1083 for (
int i = 1; i < u_order; ++i)
1084 grid[grid_index(i, j)] = loc_nodes[node_index++];
1086 assert(node_index == loc_nodes.size());
1087 assert(std::all_of(grid.begin(), grid.end(), [](
const int n) { return n >= 0; }));
1089 for (
int j = 0; j < v_order; ++j)
1091 for (
int i = 0; i < u_order; ++i)
1093 tris.emplace_back(grid[grid_index(i, j)], grid[grid_index(i + 1, j)], grid[grid_index(i, j + 1)]);
1094 tris.emplace_back(grid[grid_index(i + 1, j + 1)], grid[grid_index(i, j + 1)], grid[grid_index(i + 1, j)]);
1098 update_mapping(loc_nodes);
1105 assert(!is_simplicial);
1107 std::vector<int> loc_nodes;
1108 std::vector<int> loc_local_nodes;
1110 for (
long n = 0; n < nodes.size(); ++n)
1112 auto &bs = b.bases[nodes(n)];
1113 const auto &glob = bs.global();
1114 if (glob.size() != 1)
1117 int gindex = glob.front().index;
1118 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), gindex + 1));
1119 node_positions_vec[gindex] = glob.front().node;
1120 loc_nodes.push_back(gindex);
1121 loc_local_nodes.push_back(nodes(n));
1124 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
1125 for (
int k = 0; k < loc_nodes.size(); ++k)
1127 if (!visited_node[loc_nodes[k]])
1128 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
1130 visited_node[loc_nodes[k]] =
true;
1134 const int p = b.bases.empty() ? -1 : b.bases.front().order();
1137 logger().trace(
"skipping pyramid face {} with unsupported p={}", eid, p);
1143 const int expected_nodes = (p + 1) * (p + 1);
1144 if (loc_nodes.size() != expected_nodes || loc_local_nodes.size() != expected_nodes)
1146 logger().trace(
"skipping pyramid quad face {} with p={} and {} nodes", eid, p, loc_nodes.size());
1150 Eigen::MatrixXd pyramid_nodes;
1153 const Eigen::RowVector3d origin = pyramid_nodes.row(loc_local_nodes[0]);
1154 const Eigen::RowVector3d u_axis = pyramid_nodes.row(loc_local_nodes[1]) - origin;
1155 const Eigen::RowVector3d v_axis = pyramid_nodes.row(loc_local_nodes[3]) - origin;
1157 std::vector<int> grid(expected_nodes, -1);
1158 auto grid_index = [p](
const int i,
const int j) {
1159 return j * (p + 1) + i;
1162 bool valid_grid =
true;
1163 for (
int n = 0; n < loc_nodes.size(); ++n)
1165 const Eigen::RowVector3d rel = pyramid_nodes.row(loc_local_nodes[n]) - origin;
1166 const int i = int(std::lround(p * rel.dot(u_axis) / u_axis.squaredNorm()));
1167 const int j = int(std::lround(p * rel.dot(v_axis) / v_axis.squaredNorm()));
1168 if (i < 0 || i > p || j < 0 || j > p)
1170 logger().trace(
"skipping pyramid quad face {} with invalid local grid coordinate ({}, {})", eid, i, j);
1174 if (grid[grid_index(i, j)] >= 0)
1176 logger().trace(
"skipping pyramid quad face {} with duplicate local grid coordinate ({}, {})", eid, i, j);
1180 grid[grid_index(i, j)] = loc_nodes[n];
1183 if (!valid_grid || !std::all_of(grid.begin(), grid.end(), [](
const int n) { return n >= 0; }))
1186 for (
int j = 0; j < p; ++j)
1188 for (
int i = 0; i < p; ++i)
1190 tris.emplace_back(grid[grid_index(i, j)], grid[grid_index(i + 1, j)], grid[grid_index(i, j + 1)]);
1191 tris.emplace_back(grid[grid_index(i + 1, j + 1)], grid[grid_index(i, j + 1)], grid[grid_index(i + 1, j)]);
1195 update_mapping(loc_nodes);
1197 else if (loc_nodes.size() == 3)
1199 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
1200 update_mapping(loc_nodes);
1202 else if (loc_nodes.size() == 6)
1204 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
1205 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
1206 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
1207 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
1208 update_mapping(loc_nodes);
1210 else if (loc_nodes.size() == 10)
1212 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
1213 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
1214 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
1215 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
1216 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
1217 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
1218 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
1219 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
1220 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
1221 update_mapping(loc_nodes);
1225 logger().trace(
"skipping pyramid tri face {} with p={} and {} nodes", eid, p, loc_nodes.size());
1234 logger().trace(
"skipping element {} since it is not a simplex or hex", eid);
1240 std::vector<int> loc_nodes;
1242 bool is_follower =
false;
1245 for (
long n = 0; n < nodes.size(); ++n)
1247 auto &bs = b.bases[nodes(n)];
1248 const auto &glob = bs.global();
1249 if (glob.size() != 1)
1260 for (
long n = 0; n < nodes.size(); ++n)
1263 const std::vector<basis::Local2Global> &glob = bs.
global();
1264 if (glob.size() != 1)
1267 int gindex = glob.front().index;
1268 node_positions_vec.resize(std::max(
int(node_positions_vec.size()), gindex + 1));
1269 node_positions_vec[gindex] = glob.front().node;
1270 loc_nodes.push_back(gindex);
1273 if (loc_nodes.size() == 3)
1275 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
1277 else if (loc_nodes.size() == 6)
1279 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
1280 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
1281 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
1282 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
1284 else if (loc_nodes.size() == 10)
1286 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
1287 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
1288 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
1289 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
1290 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
1291 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
1292 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
1293 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
1294 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
1296 else if (loc_nodes.size() == 15)
1298 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[11]);
1299 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[12]);
1300 tris.emplace_back(loc_nodes[3], loc_nodes[12], loc_nodes[11]);
1301 tris.emplace_back(loc_nodes[12], loc_nodes[10], loc_nodes[11]);
1302 tris.emplace_back(loc_nodes[4], loc_nodes[5], loc_nodes[13]);
1303 tris.emplace_back(loc_nodes[4], loc_nodes[13], loc_nodes[12]);
1304 tris.emplace_back(loc_nodes[12], loc_nodes[13], loc_nodes[14]);
1305 tris.emplace_back(loc_nodes[12], loc_nodes[14], loc_nodes[10]);
1306 tris.emplace_back(loc_nodes[14], loc_nodes[9], loc_nodes[10]);
1307 tris.emplace_back(loc_nodes[5], loc_nodes[1], loc_nodes[6]);
1308 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[13]);
1309 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[13]);
1310 tris.emplace_back(loc_nodes[13], loc_nodes[7], loc_nodes[14]);
1311 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[14]);
1312 tris.emplace_back(loc_nodes[14], loc_nodes[8], loc_nodes[9]);
1313 tris.emplace_back(loc_nodes[8], loc_nodes[2], loc_nodes[9]);
1317 print_warning << loc_nodes.size() <<
" ";
1323 for (
int k = 0; k < loc_nodes.size(); ++k)
1325 if (!visited_node[loc_nodes[k]])
1326 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
1328 visited_node[loc_nodes[k]] =
true;
1334 if (print_warning.str().size() > 0)
1335 logger().warn(
"Skipping faces as theys have {} nodes, boundary export supported up to p4", print_warning.str());
1339 node_positions_vec.resize(
1340 std::max(node_positions_vec.size(),
size_t(n_bases + (is_simplicial ? 0 : mesh.
n_faces()))),
1341 Eigen::Vector3d::Zero());
1343 node_positions.resize(node_positions_vec.size(), 3);
1344 for (
int i = 0; i < node_positions_vec.size(); ++i)
1345 node_positions.row(i) = node_positions_vec[i];
1347 boundary_triangles.resize(tris.size(), 3);
1348 for (
int i = 0; i < tris.size(); ++i)
1350 boundary_triangles.row(i) << std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]);
1353 if (boundary_triangles.rows() > 0)
1355 igl::edges(boundary_triangles, boundary_edges);
1358 if (
const char *dump = getenv(
"POLYFEM_DUMP_COLLISION_PROXY"))
1359 igl::write_triangle_mesh(dump, node_positions, boundary_triangles);
1363 node_positions.resize(n_bases, 2);
1364 node_positions.setZero();
1365 const Mesh2D &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
1367 std::vector<std::pair<int, int>> edges;
1373 for (
int j = 0; j < lb.size(); ++j)
1375 const int eid = lb.global_primitive_id(j);
1376 const int lid = lb[j];
1377 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh2d);
1381 for (
long n = 0; n < nodes.size(); ++n)
1384 const std::vector<basis::Local2Global> &glob = bs.
global();
1385 if (glob.size() != 1)
1388 int gindex = glob.front().index;
1389 node_positions.row(gindex) = glob.front().node.head<2>();
1392 edges.emplace_back(prev_node, gindex);
1399 boundary_triangles.resize(0, 0);
1400 boundary_edges.resize(edges.size(), 2);
1401 for (
int i = 0; i < edges.size(); ++i)
1403 boundary_edges.row(i) << edges[i].first, edges[i].second;
1410 const std::vector<basis::ElementBases> &gbases,
1411 const std::vector<mesh::LocalBoundary> &total_local_boundary,
1412 Eigen::MatrixXd &boundary_vis_vertices,
1413 Eigen::MatrixXd &boundary_vis_local_vertices,
1414 Eigen::MatrixXi &boundary_vis_elements,
1415 Eigen::MatrixXi &boundary_vis_elements_ids,
1416 Eigen::MatrixXi &boundary_vis_primitive_ids,
1417 Eigen::MatrixXd &boundary_vis_normals)
const
1421 std::vector<Eigen::MatrixXd> lv, vertices, allnormals;
1422 std::vector<int> el_ids, global_primitive_ids;
1423 Eigen::MatrixXd uv, local_pts, tmp_n, normals;
1429 std::vector<std::pair<int, int>> edges;
1430 std::vector<std::tuple<int, int, int>> tris;
1432 for (
auto it = total_local_boundary.begin(); it != total_local_boundary.end(); ++it)
1434 const auto &lb = *it;
1435 const auto &gbs = gbases[lb.element_id()];
1437 for (
int k = 0; k < lb.size(); ++k)
1441 case BoundaryType::TRI_LINE:
1445 case BoundaryType::QUAD_LINE:
1449 case BoundaryType::QUAD:
1453 case BoundaryType::TRI:
1457 case BoundaryType::PRISM:
1461 case BoundaryType::PYRAMID:
1465 case BoundaryType::POLYGON:
1469 case BoundaryType::POLYHEDRON:
1472 case BoundaryType::INVALID:
1479 vertices.emplace_back();
1480 lv.emplace_back(local_pts);
1481 el_ids.push_back(lb.element_id());
1482 global_primitive_ids.push_back(lb.global_primitive_id(k));
1483 gbs.eval_geom_mapping(local_pts, vertices.back());
1484 vals.compute(lb.element_id(), mesh.
is_volume(), local_pts, gbs, gbs);
1485 const int tris_start = tris.size();
1489 const bool prism_quad = lb.type() == BoundaryType::PRISM && lb[k] >= 2;
1490 const bool prism_tri = lb.type() == BoundaryType::PRISM && lb[k] < 2;
1492 const bool pyramid_quad = lb.type() == BoundaryType::PYRAMID && lb[k] == 0;
1493 const bool pyramid_tri = lb.type() == BoundaryType::PYRAMID && lb[k] > 0;
1495 if (lb.type() == BoundaryType::QUAD || prism_quad || pyramid_quad)
1497 const auto map = [n_samples, size](
int i,
int j) {
return j * n_samples + i + size; };
1499 for (
int j = 0; j < n_samples - 1; ++j)
1501 for (
int i = 0; i < n_samples - 1; ++i)
1503 tris.emplace_back(map(i, j), map(i + 1, j), map(i, j + 1));
1504 tris.emplace_back(map(i + 1, j + 1), map(i, j + 1), map(i + 1, j));
1508 else if (lb.type() == BoundaryType::TRI || prism_tri || pyramid_tri)
1511 std::vector<int> mapp(n_samples * n_samples, -1);
1512 for (
int j = 0; j < n_samples; ++j)
1514 for (
int i = 0; i < n_samples - j; ++i)
1516 mapp[j * n_samples + i] = index;
1520 const auto map = [mapp, n_samples](
int i,
int j) {
1521 if (j * n_samples + i >= mapp.size())
1523 return mapp[j * n_samples + i];
1526 for (
int j = 0; j < n_samples - 1; ++j)
1528 for (
int i = 0; i < n_samples - j; ++i)
1530 if (map(i, j) >= 0 && map(i + 1, j) >= 0 && map(i, j + 1) >= 0)
1531 tris.emplace_back(map(i, j) + size, map(i + 1, j) + size, map(i, j + 1) + size);
1533 if (map(i + 1, j + 1) >= 0 && map(i, j + 1) >= 0 && map(i + 1, j) >= 0)
1534 tris.emplace_back(map(i + 1, j + 1) + size, map(i, j + 1) + size, map(i + 1, j) + size);
1545 for (
int i = 0; i < vertices.back().rows() - 1; ++i)
1546 edges.emplace_back(i + size, i + size + 1);
1549 normals.resize(
vals.jac_it.size(), tmp_n.cols());
1551 for (
int n = 0; n <
vals.jac_it.size(); ++n)
1553 normals.row(n) = tmp_n *
vals.jac_it[n];
1554 normals.row(n).normalize();
1557 allnormals.push_back(normals);
1560 for (
int n = 0; n <
vals.jac_it.size(); ++n)
1562 tmp_n += normals.row(n);
1567 Eigen::Vector3d e1 = vertices.back().row(std::get<1>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
1568 Eigen::Vector3d e2 = vertices.back().row(std::get<2>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
1570 Eigen::Vector3d n = e1.cross(e2);
1571 Eigen::Vector3d nn = tmp_n.transpose();
1575 for (
int i = tris_start; i < tris.size(); ++i)
1577 tris[i] = std::tuple<int, int, int>(std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]));
1582 size += vertices.back().rows();
1586 boundary_vis_vertices.resize(size, vertices.front().cols());
1587 boundary_vis_local_vertices.resize(size, vertices.front().cols());
1588 boundary_vis_elements_ids.resize(size, 1);
1589 boundary_vis_primitive_ids.resize(size, 1);
1590 boundary_vis_normals.resize(size, vertices.front().cols());
1593 boundary_vis_elements.resize(tris.size(), 3);
1595 boundary_vis_elements.resize(edges.size(), 2);
1599 for (
const auto &v : vertices)
1601 boundary_vis_vertices.block(index, 0, v.rows(), v.cols()) = v;
1602 boundary_vis_local_vertices.block(index, 0, v.rows(), v.cols()) = lv[ii];
1603 boundary_vis_elements_ids.block(index, 0, v.rows(), 1).setConstant(el_ids[ii]);
1604 boundary_vis_primitive_ids.block(index, 0, v.rows(), 1).setConstant(global_primitive_ids[ii++]);
1609 for (
const auto &n : allnormals)
1611 boundary_vis_normals.block(index, 0, n.rows(), n.cols()) = n;
1618 for (
const auto &t : tris)
1620 boundary_vis_elements.row(index) << std::get<0>(t), std::get<1>(t), std::get<2>(t);
1626 for (
const auto &e : edges)
1628 boundary_vis_elements.row(index) << e.first, e.second;
1636 const Eigen::VectorXi &disc_orders,
1637 const std::vector<basis::ElementBases> &gbases,
1638 const std::map<int, Eigen::MatrixXd> &polys,
1639 const std::map<
int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d,
1640 const bool boundary_only,
1641 Eigen::MatrixXd &points,
1642 Eigen::MatrixXi &tets,
1643 Eigen::MatrixXi &el_id,
1644 Eigen::MatrixXd &discr,
1645 Eigen::MatrixXd &local_points)
const
1649 const auto ¤t_bases = gbases;
1650 int tet_total_size = 0;
1651 int pts_total_size = 0;
1653 Eigen::MatrixXd vis_pts_poly;
1654 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
1656 for (
size_t i = 0; i < current_bases.size(); ++i)
1658 const auto &bs = current_bases[i];
1666 pts_total_size += sampler.simplex_points().rows();
1670 tet_total_size += sampler.cube_volume().rows();
1671 pts_total_size += sampler.cube_points().rows();
1675 tet_total_size += sampler.prism_volume().rows();
1676 pts_total_size += sampler.prism_points().rows();
1680 tet_total_size += sampler.pyramid_volume().rows();
1681 pts_total_size += sampler.pyramid_points().rows();
1687 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
1689 tet_total_size += vis_faces_poly.rows();
1690 pts_total_size += vis_pts_poly.rows();
1694 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
1696 tet_total_size += vis_faces_poly.rows();
1697 pts_total_size += vis_pts_poly.rows();
1702 points.resize(pts_total_size, mesh.
dimension());
1703 local_points.resize(pts_total_size, mesh.
dimension());
1704 local_points.setZero();
1705 tets.resize(tet_total_size, mesh.
is_volume() ? 4 : 3);
1707 el_id.resize(pts_total_size, 1);
1708 discr.resize(pts_total_size, 1);
1710 Eigen::MatrixXd mapped, tmp;
1711 int tet_index = 0, pts_index = 0;
1713 for (
size_t i = 0; i < current_bases.size(); ++i)
1715 const auto &bs = current_bases[i];
1722 bs.eval_geom_mapping(sampler.simplex_points(), mapped);
1724 tets.block(tet_index, 0, sampler.simplex_volume().rows(), tets.cols()) = sampler.simplex_volume().array() + pts_index;
1725 tet_index += sampler.simplex_volume().rows();
1727 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1728 local_points.block(pts_index, 0, sampler.simplex_points().rows(), sampler.simplex_points().cols()) = sampler.simplex_points();
1729 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
1730 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1731 pts_index += mapped.rows();
1735 bs.eval_geom_mapping(sampler.cube_points(), mapped);
1737 tets.block(tet_index, 0, sampler.cube_volume().rows(), tets.cols()) = sampler.cube_volume().array() + pts_index;
1738 tet_index += sampler.cube_volume().rows();
1740 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1741 local_points.block(pts_index, 0, sampler.cube_points().rows(), sampler.cube_points().cols()) = sampler.cube_points();
1742 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
1743 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1744 pts_index += mapped.rows();
1748 bs.eval_geom_mapping(sampler.prism_points(), mapped);
1750 tets.block(tet_index, 0, sampler.prism_volume().rows(), tets.cols()) = sampler.prism_volume().array() + pts_index;
1751 tet_index += sampler.prism_volume().rows();
1753 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1754 local_points.block(pts_index, 0, sampler.prism_points().rows(), sampler.prism_points().cols()) = sampler.prism_points();
1755 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
1756 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1757 pts_index += mapped.rows();
1761 bs.eval_geom_mapping(sampler.pyramid_points(), mapped);
1763 tets.block(tet_index, 0, sampler.pyramid_volume().rows(), tets.cols()) = sampler.pyramid_volume().array() + pts_index;
1764 tet_index += sampler.pyramid_volume().rows();
1766 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1767 local_points.block(pts_index, 0, sampler.pyramid_points().rows(), sampler.pyramid_points().cols()) = sampler.pyramid_points();
1768 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
1769 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1770 pts_index += mapped.rows();
1776 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
1777 bs.eval_geom_mapping(vis_pts_poly, mapped);
1779 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
1780 tet_index += vis_faces_poly.rows();
1782 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1783 local_points.block(pts_index, 0, vis_pts_poly.rows(), vis_pts_poly.cols()) = vis_pts_poly;
1784 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
1785 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1786 pts_index += mapped.rows();
1790 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
1791 bs.eval_geom_mapping(vis_pts_poly, mapped);
1793 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
1794 tet_index += vis_faces_poly.rows();
1796 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
1797 local_points.block(pts_index, 0, vis_pts_poly.rows(), vis_pts_poly.cols()) = vis_pts_poly;
1798 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
1799 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
1800 pts_index += mapped.rows();
1805 assert(pts_index == points.rows());
1806 assert(tet_index == tets.rows());
1811 const Eigen::VectorXi &output_orders,
1812 const std::vector<basis::ElementBases> &bases,
1813 Eigen::MatrixXd &points,
1814 std::vector<CellElement> &elements,
1815 Eigen::MatrixXi &el_id,
1816 Eigen::MatrixXd &discr,
1817 Eigen::MatrixXd &local_points)
const
1831 std::vector<RowVectorNd> nodes;
1832 int pts_total_size = 0;
1833 elements.resize(bases.size());
1834 Eigen::MatrixXd ref_pts;
1836 for (
size_t i = 0; i < bases.size(); ++i)
1838 const auto &bs = bases[i];
1851 if (output_orders(i) == 1)
1852 pyramid_nodes_for_output(1, ref_pts);
1867 const int n_v =
static_cast<const mesh::Mesh2D &
>(mesh).n_face_vertices(i);
1868 ref_pts.resize(n_v, 2);
1872 pts_total_size += ref_pts.rows();
1876 local_points.resize(pts_total_size, mesh.
dimension());
1877 local_points.setZero();
1879 el_id.resize(pts_total_size, 1);
1880 discr.resize(pts_total_size, 1);
1882 Eigen::MatrixXd mapped;
1885 std::string error_msg =
"";
1887 for (
size_t i = 0; i < bases.size(); ++i)
1889 const auto &bs = bases[i];
1902 if (output_orders(i) == 1)
1903 pyramid_nodes_for_output(1, ref_pts);
1920 bs.eval_geom_mapping(ref_pts, mapped);
1922 for (
int j = 0; j < mapped.rows(); ++j)
1924 points.row(pts_index) = mapped.row(j);
1925 local_points.row(pts_index).leftCols(ref_pts.cols()) = ref_pts.row(j);
1926 el_id(pts_index) = i;
1927 discr(pts_index) = output_orders(i);
1928 elements[i].vertices.push_back(pts_index);
1937 const int n_nodes = elements[i].vertices.size();
1938 if (output_orders(i) >= 3)
1940 std::swap(elements[i].vertices[16], elements[i].vertices[17]);
1941 std::swap(elements[i].vertices[17], elements[i].vertices[18]);
1942 std::swap(elements[i].vertices[18], elements[i].vertices[19]);
1944 if (output_orders(i) > 4)
1945 error_msg =
"Saving high-order meshes not implemented for P5+ elements!";
1949 if (output_orders(i) == 4)
1951 const int n_nodes = elements[i].vertices.size();
1952 std::swap(elements[i].vertices[n_nodes - 1], elements[i].vertices[n_nodes - 2]);
1954 if (output_orders(i) > 4)
1955 error_msg =
"Saving high-order meshes not implemented for P5+ elements!";
1960 const int n_nodes = elements[i].vertices.size();
1961 if (output_orders(i) == 2)
1963 std::swap(elements[i].vertices[12], elements[i].vertices[16]);
1964 std::swap(elements[i].vertices[13], elements[i].vertices[17]);
1965 std::swap(elements[i].vertices[14], elements[i].vertices[18]);
1966 std::swap(elements[i].vertices[15], elements[i].vertices[19]);
1967 std::swap(elements[i].vertices[18], elements[i].vertices[19]);
1982 if (output_orders(i) > 2)
1983 error_msg =
"Saving high-order meshes not implemented for Q2+ elements!";
1985 else if (output_orders(i) > 1)
1988 error_msg =
"Saving high-order meshes not implemented for Q2+ elements!";
1992 if (!error_msg.empty())
1993 logger().warn(error_msg);
1995 for (
size_t i = 0; i < bases.size(); ++i)
2000 const auto &mesh2d =
static_cast<const mesh::Mesh2D &
>(mesh);
2003 for (
int j = 0; j < n_v; ++j)
2005 points.row(pts_index) = mesh2d.point(mesh2d.face_vertex(i, j));
2006 local_points.row(pts_index) = mesh2d.point(mesh2d.face_vertex(i, j));
2007 el_id(pts_index) = i;
2008 discr(pts_index) = output_orders(i);
2009 elements[i].vertices.push_back(pts_index);
2015 for (
size_t i = 0; i < bases.size(); ++i)
2019 if (elements[i].
vertices.size() == 1)
2020 elements[i].ctype = CellType::Vertex;
2021 else if (elements[i].
vertices.size() == 2)
2022 elements[i].ctype = CellType::Line;
2024 elements[i].ctype = CellType::Triangle;
2026 elements[i].ctype = CellType::Quadrilateral;
2028 elements[i].ctype = CellType::Polygon;
2033 elements[i].ctype = CellType::Tetrahedron;
2035 elements[i].ctype = CellType::Hexahedron;
2037 elements[i].ctype = CellType::Wedge;
2039 elements[i].ctype = CellType::Pyramid;
2048 std::vector<CellElement> expanded_elements;
2049 expanded_elements.reserve(elements.size());
2050 for (
size_t i = 0; i < bases.size(); ++i)
2052 if (!mesh.
is_pyramid(i) || output_orders(i) == 1)
2054 expanded_elements.push_back(std::move(elements[i]));
2061 tet.ctype = CellType::Tetrahedron;
2064 expanded_elements.push_back(std::move(tet));
2067 elements.swap(expanded_elements);
2070 assert(pts_index ==
points.rows());
2076 const bool is_time_dependent,
2077 const double tend_in,
2080 const std::string &vis_mesh_path)
const
2084 logger().error(
"Load the mesh first!");
2088 double tend = tend_in;
2092 if (!vis_mesh_path.empty() && !is_time_dependent)
2095 vis_mesh_path, space, output_fields,
2107 fields = args[
"output"][
"paraview"][
"fields"];
2109 volume = args[
"output"][
"paraview"][
"volume"];
2110 surface = args[
"output"][
"paraview"][
"surface"];
2111 wire = args[
"output"][
"paraview"][
"wireframe"];
2112 points = args[
"output"][
"paraview"][
"points"];
2113 contact_forces = args[
"output"][
"paraview"][
"options"][
"contact_forces"] && !is_problem_scalar;
2114 friction_forces = args[
"output"][
"paraview"][
"options"][
"friction_forces"] && !is_problem_scalar;
2115 normal_adhesion_forces = args[
"output"][
"paraview"][
"options"][
"normal_adhesion_forces"] && !is_problem_scalar;
2116 tangential_adhesion_forces = args[
"output"][
"paraview"][
"options"][
"tangential_adhesion_forces"] && !is_problem_scalar;
2118 if (args[
"output"][
"paraview"][
"options"][
"force_high_order"])
2119 use_sampler =
false;
2121 use_sampler = !(is_mesh_linear && args[
"output"][
"paraview"][
"high_order_mesh"]);
2122 boundary_only = use_sampler && args[
"output"][
"advanced"][
"vis_boundary_only"];
2123 sol_on_grid = args[
"output"][
"advanced"][
"sol_on_grid"] > 0;
2125 discretization_order = args[
"output"][
"paraview"][
"options"][
"discretization_order"];
2127 reorder_output = args[
"output"][
"data"][
"advanced"][
"reorder_nodes"];
2129 use_hdf5 = args[
"output"][
"paraview"][
"options"][
"use_hdf5"];
2133 const std::string &path,
2142 logger().error(
"Load the mesh first!");
2146 const std::filesystem::path fs_path(path);
2147 const std::string path_stem = fs_path.stem().string();
2148 const std::string base_path = (fs_path.parent_path() / path_stem).
string();
2149 paraviewo::VTMWriter vtm(t);
2150 save_vtu(path, space, output_fields, t, dt, opts, vtm,
"");
2151 vtm.save(base_path +
".vtm");
2155 const std::string &path,
2161 paraviewo::VTMWriter &vtm,
2162 const std::string &block_prefix)
const
2166 logger().error(
"Load the mesh first!");
2170 const bool save_contact =
2175 logger().info(
"Saving vtu to {}; volume={}, surface={}, contact={}, points={}, wireframe={}",
2178 const std::filesystem::path fs_path(path);
2179 const std::string path_stem = fs_path.stem().string();
2180 const std::string base_path = (fs_path.parent_path() / path_stem).
string();
2207 const auto block_name = [&block_prefix](
const std::string &name) {
2208 return block_prefix.empty() ? name : block_prefix +
" " + name;
2211 vtm.add_dataset(block_name(
"Volume"),
"data", path_stem + opts.
file_extension());
2213 vtm.add_dataset(block_name(
"Surface"),
"data", path_stem +
"_surf" + opts.
file_extension());
2215 vtm.add_dataset(block_name(
"Contact"),
"data", path_stem +
"_surf_contact" + opts.
file_extension());
2217 vtm.add_dataset(block_name(
"Wireframe"),
"data", path_stem +
"_wire" + opts.
file_extension());
2219 vtm.add_dataset(block_name(
"Points"),
"data", path_stem +
"_points" + opts.
file_extension());
2223 const std::string &path,
2233 static const std::map<int, Eigen::MatrixXd> empty_polys;
2234 static const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> empty_polys_3d;
2237 const std::vector<basis::ElementBases> &gbases = *space.
geometry_bases;
2238 const std::map<int, Eigen::MatrixXd> &polys = space.
polys ? *space.
polys : empty_polys;
2239 const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d = space.
polys_3d ? *space.
polys_3d : empty_polys_3d;
2240 const Eigen::VectorXi output_orders =
2246 Eigen::MatrixXd points;
2247 Eigen::MatrixXi tets;
2248 Eigen::MatrixXi el_id;
2249 Eigen::MatrixXd discr;
2250 Eigen::MatrixXd local_points;
2251 std::vector<CellElement> elements;
2256 points, tets, el_id, discr, local_points);
2260 points, elements, el_id, discr, local_points);
2263 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2265 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2267 tmpw = std::make_shared<paraviewo::VTUWriter>();
2268 paraviewo::ParaviewWriter &writer = *tmpw;
2272 discr.conservativeResize(discr.size() + obstacle->
n_vertices(), 1);
2273 discr.bottomRows(obstacle->
n_vertices()).setZero();
2277 writer.add_field(
"discr", discr);
2281 const int orig_p = points.rows();
2282 points.conservativeResize(points.rows() + obstacle->
n_vertices(), points.cols());
2283 points.bottomRows(obstacle->
n_vertices()) = obstacle->
v();
2285 if (elements.empty())
2287 for (
int i = 0; i < tets.rows(); ++i)
2289 elements.emplace_back();
2290 elements.back().ctype = CellType::Tetrahedron;
2291 for (
int j = 0; j < tets.cols(); ++j)
2292 elements.back().vertices.push_back(tets(i, j));
2298 elements.emplace_back();
2299 elements.back().ctype = CellType::Triangle;
2306 elements.emplace_back();
2307 elements.back().ctype = CellType::Line;
2314 elements.emplace_back();
2315 elements.back().ctype = CellType::Vertex;
2326 sample.
cell_count = elements.empty() ? tets.rows() :
static_cast<int>(elements.size());
2329 add_output_fields(writer, sample, output_fields);
2344 grid_sample.
time = t;
2345 grid_sample.
dt = dt;
2348 "solution_gradient",
2350 "pressure_gradient",
2354 for (
const OutputField &field : output_fields(grid_sample))
2358 if (field.name ==
"solution")
2360 else if (field.name ==
"solution_gradient")
2362 else if (field.name ==
"pressure")
2364 else if (field.name ==
"pressure_gradient")
2369 if (elements.empty())
2370 writer.write_mesh(path, points, tets, mesh.
is_volume() ? CellType::Tetrahedron : CellType::Triangle);
2372 writer.write_mesh(path, points, elements);
2376 const std::string &export_surface,
2387 const std::vector<basis::ElementBases> &gbases = *space.
geometry_bases;
2389 Eigen::MatrixXd boundary_vis_vertices;
2390 Eigen::MatrixXd boundary_vis_local_vertices;
2391 Eigen::MatrixXi boundary_vis_elements;
2392 Eigen::MatrixXi boundary_vis_elements_ids;
2393 Eigen::MatrixXi boundary_vis_primitive_ids;
2394 Eigen::MatrixXd boundary_vis_normals;
2397 boundary_vis_vertices, boundary_vis_local_vertices, boundary_vis_elements,
2398 boundary_vis_elements_ids, boundary_vis_primitive_ids, boundary_vis_normals);
2400 Eigen::MatrixXd discr, b_sidesets;
2401 discr.resize(boundary_vis_vertices.rows(), 1);
2402 b_sidesets.resize(boundary_vis_vertices.rows(), 1);
2403 b_sidesets.setZero();
2405 for (
int i = 0; i < boundary_vis_vertices.rows(); ++i)
2407 const auto s_id = mesh.
get_boundary_id(boundary_vis_primitive_ids(i));
2410 b_sidesets(i) = s_id;
2413 const int el_index = boundary_vis_elements_ids(i);
2417 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2419 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2421 tmpw = std::make_shared<paraviewo::VTUWriter>();
2422 paraviewo::ParaviewWriter &writer = *tmpw;
2425 writer.add_field(
"normals", boundary_vis_normals);
2427 writer.add_field(
"discr", discr);
2429 writer.add_field(
"sidesets", b_sidesets);
2433 sample.
points = boundary_vis_vertices;
2435 sample.
element_ids = boundary_vis_elements_ids.col(0);
2437 sample.
normals = boundary_vis_normals;
2439 sample.
cell_count = boundary_vis_elements.rows();
2442 add_output_fields(writer, sample, output_fields);
2443 writer.write_mesh(export_surface, boundary_vis_vertices, boundary_vis_elements, mesh.
is_volume() ? CellType::Triangle : CellType::Line);
2447 const std::string &export_surface,
2457 const ipc::CollisionMesh &collision_mesh = *space.
collision_mesh;
2459 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2461 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2463 tmpw = std::make_shared<paraviewo::VTUWriter>();
2464 paraviewo::ParaviewWriter &writer = *tmpw;
2468 sample.
points = collision_mesh.rest_positions();
2471 collision_mesh.dim() == 3 ? collision_mesh.num_faces() : collision_mesh.num_edges());
2474 add_output_fields(writer, sample, output_fields);
2476 const std::filesystem::path surface_path(export_surface);
2477 const std::string contact_path =
2478 (surface_path.parent_path() / (surface_path.stem().string() +
"_contact" + surface_path.extension().string())).string();
2481 collision_mesh.rest_positions(),
2482 collision_mesh.dim() == 3 ? collision_mesh.faces() : collision_mesh.edges(),
2483 collision_mesh.dim() == 3 ? CellType::Triangle : CellType::Line);
2487 const std::string &name,
2496 static const std::map<int, Eigen::MatrixXd> empty_polys;
2497 static const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> empty_polys_3d;
2499 const std::vector<basis::ElementBases> &gbases = *space.
geometry_bases;
2501 const std::map<int, Eigen::MatrixXd> &polys = space.
polys ? *space.
polys : empty_polys;
2502 const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d = space.
polys_3d ? *space.
polys_3d : empty_polys_3d;
2503 const Eigen::VectorXi output_orders =
2508 Eigen::MatrixXd points, discr, local_points;
2509 Eigen::MatrixXi cells, element_ids, edges;
2510 build_vis_mesh(mesh, output_orders, gbases, polys, polys_3d,
false, points, cells, element_ids, discr, local_points);
2511 if (cells.size() > 0)
2512 igl::edges(cells, edges);
2516 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2518 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2520 tmpw = std::make_shared<paraviewo::VTUWriter>();
2521 paraviewo::ParaviewWriter &writer = *tmpw;
2527 if (element_ids.cols() > 0)
2532 add_output_fields(writer, sample, output_fields);
2534 writer.write_mesh(name, points, edges, CellType::Line);
2538 const std::string &path,
2550 Eigen::MatrixXd b_sidesets(dirichlet_nodes_position.size(), 1);
2551 b_sidesets.setZero();
2552 Eigen::MatrixXd points(dirichlet_nodes_position.size(), mesh.
dimension());
2553 std::vector<CellElement> cells(dirichlet_nodes_position.size());
2555 for (
int i = 0; i < dirichlet_nodes_position.size(); ++i)
2557 const int n_id = dirichlet_nodes[i];
2561 b_sidesets(i) = s_id;
2564 points.row(i) = dirichlet_nodes_position[i];
2565 cells[i].vertices.push_back(i);
2566 cells[i].ctype = CellType::Vertex;
2569 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2571 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2573 tmpw = std::make_shared<paraviewo::VTUWriter>();
2574 paraviewo::ParaviewWriter &writer = *tmpw;
2577 writer.add_field(
"sidesets", b_sidesets);
2582 sample.
node_ids.resize(dirichlet_nodes.size());
2583 for (
int i = 0; i < dirichlet_nodes.size(); ++i)
2584 sample.
node_ids(i) = dirichlet_nodes[i];
2586 sample.
cell_count =
static_cast<int>(cells.size());
2587 add_output_fields(writer, sample, output_fields);
2588 writer.write_mesh(path, points, cells);
2592 const std::string &name,
2593 const std::function<std::string(
int)> &vtu_names,
2594 int time_steps,
double t0,
double dt,
int skip_frame)
const
2596 paraviewo::PVDWriter::save_pvd(name, vtu_names, time_steps, t0, dt, skip_frame);
2612 const int nx = delta[0] / spacing + 1;
2613 const int ny = delta[1] / spacing + 1;
2614 const int nz = delta.cols() >= 3 ? (delta[2] / spacing + 1) : 1;
2615 const int n = nx * ny * nz;
2619 for (
int i = 0; i < nx; ++i)
2621 const double x = (delta[0] / (nx - 1)) * i + min[0];
2623 for (
int j = 0; j < ny; ++j)
2625 const double y = (delta[1] / (ny - 1)) * j + min[1];
2627 if (delta.cols() <= 2)
2633 for (
int k = 0; k < nz; ++k)
2635 const double z = (delta[2] / (nz - 1)) * k + min[2];
2644 std::vector<std::array<Eigen::Vector3d, 2>> boxes;
2650 const double eps = 1e-6;
2659 const Eigen::Vector3d min(
2664 const Eigen::Vector3d max(
2669 std::vector<unsigned int> candidates;
2671 bvh.intersect_box(min, max, candidates);
2673 for (
const auto cand : candidates)
2677 logger().warn(
"Element {} is not simplex, skipping", cand);
2681 Eigen::MatrixXd coords;
2684 for (
int d = 0; d < coords.size(); ++d)
2686 if (fabs(coords(d)) < 1e-8)
2688 else if (fabs(coords(d) - 1) < 1e-8)
2692 if (coords.array().minCoeff() >= 0 && coords.array().maxCoeff() <= 1)
2704 Eigen::MatrixXd samples_simplex, samples_cube, mapped, p0, p1, p;
2707 average_edge_length = 0;
2708 min_edge_length = std::numeric_limits<double>::max();
2710 if (!use_curved_mesh_size)
2714 min_edge_length = p.rowwise().norm().minCoeff();
2715 average_edge_length = p.rowwise().norm().mean();
2716 mesh_size = p.rowwise().norm().maxCoeff();
2718 logger().info(
"hmin: {}", min_edge_length);
2719 logger().info(
"hmax: {}", mesh_size);
2720 logger().info(
"havg: {}", average_edge_length);
2737 for (
size_t i = 0; i < bases_in.size(); ++i)
2746 bases_in[i].eval_geom_mapping(samples_simplex, mapped);
2751 bases_in[i].eval_geom_mapping(samples_cube, mapped);
2754 for (
int j = 0; j < n_edges; ++j)
2756 double current_edge = 0;
2757 for (
int k = 0; k < n_samples - 1; ++k)
2759 p0 = mapped.row(j * n_samples + k);
2760 p1 = mapped.row(j * n_samples + k + 1);
2763 current_edge += p.norm();
2766 mesh_size = std::max(current_edge, mesh_size);
2767 min_edge_length = std::min(current_edge, min_edge_length);
2768 average_edge_length += current_edge;
2773 average_edge_length /= n;
2775 logger().info(
"hmin: {}", min_edge_length);
2776 logger().info(
"hmax: {}", mesh_size);
2777 logger().info(
"havg: {}", average_edge_length);
2787 using namespace mesh;
2789 logger().info(
"Counting flipped elements...");
2793 for (
size_t i = 0; i < gbases.size(); ++i)
2799 if (!
vals.is_geom_mapping_positive(mesh.
is_volume(), gbases[i]))
2803 static const std::vector<std::string> element_type_names{{
2805 "RegularInteriorCube",
2806 "RegularBoundaryCube",
2807 "SimpleSingularInteriorCube",
2808 "MultiSingularInteriorCube",
2809 "SimpleSingularBoundaryCube",
2811 "MultiSingularBoundaryCube",
2817 log_and_throw_error(
"element {} is flipped, type {}", i, element_type_names[
static_cast<int>(els_tag[i])]);
2832 const std::vector<polyfem::basis::ElementBases> &bases,
2833 const std::vector<polyfem::basis::ElementBases> &gbases,
2837 const Eigen::MatrixXd &sol)
2841 logger().error(
"Build the bases first!");
2844 if (sol.size() <= 0)
2846 logger().error(
"Solve the problem first!");
2856 logger().info(
"Computing errors...");
2859 const int n_el = int(bases.size());
2861 Eigen::MatrixXd v_exact, v_approx;
2862 Eigen::MatrixXd v_exact_grad(0, 0), v_approx_grad;
2872 static const int p = 8;
2877 for (
int e = 0; e < n_el; ++e)
2887 v_approx.resize(
vals.val.rows(), actual_dim);
2890 v_approx_grad.resize(
vals.val.rows(), mesh.
dimension() * actual_dim);
2891 v_approx_grad.setZero();
2893 const int n_loc_bases = int(
vals.basis_values.size());
2895 for (
int i = 0; i < n_loc_bases; ++i)
2897 const auto &
val =
vals.basis_values[i];
2899 for (
size_t ii = 0; ii <
val.global.size(); ++ii)
2901 for (
int d = 0; d < actual_dim; ++d)
2903 v_approx.col(d) +=
val.global[ii].val * sol(
val.global[ii].index * actual_dim + d) *
val.val;
2904 v_approx_grad.block(0, d *
val.grad_t_m.cols(), v_approx_grad.rows(),
val.grad_t_m.cols()) +=
val.global[ii].val * sol(
val.global[ii].index * actual_dim + d) *
val.grad_t_m;
2909 const auto err = problem.
has_exact_sol() ? (v_exact - v_approx).eval().rowwise().norm().eval() : (v_approx).eval().rowwise().norm().eval();
2910 const auto err_grad = problem.
has_exact_sol() ? (v_exact_grad - v_approx_grad).eval().rowwise().norm().eval() : (v_approx_grad).eval().rowwise().norm().eval();
2915 linf_err = std::max(linf_err, err.maxCoeff());
2916 grad_max_err = std::max(linf_err, err_grad.maxCoeff());
2958 l2_err += (err.array() * err.array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
2959 h1_err += (err_grad.array() * err_grad.array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
2960 lp_err += (err.array().pow(p) *
vals.det.array() *
vals.quadrature.weights.array()).sum();
2963 h1_semi_err = sqrt(fabs(h1_err));
2964 h1_err = sqrt(fabs(l2_err) + fabs(h1_err));
2965 l2_err = sqrt(fabs(l2_err));
2967 lp_err = pow(fabs(lp_err), 1. / p);
2972 const double computing_errors_time = timer.getElapsedTime();
2973 logger().info(
" took {}s", computing_errors_time);
2975 logger().info(
"-- L2 error: {}", l2_err);
2976 logger().info(
"-- Lp error: {}", lp_err);
2977 logger().info(
"-- H1 error: {}", h1_err);
2978 logger().info(
"-- H1 semi error: {}", h1_semi_err);
2981 logger().info(
"-- Linf error: {}", linf_err);
2982 logger().info(
"-- grad max error: {}", grad_max_err);
2999 regular_boundary_count = 0;
3000 simple_singular_count = 0;
3001 multi_singular_count = 0;
3003 non_regular_boundary_count = 0;
3004 non_regular_count = 0;
3005 undefined_count = 0;
3006 multi_singular_boundary_count = 0;
3010 for (
size_t i = 0; i < els_tag.size(); ++i)
3016 case ElementType::SIMPLEX:
3019 case ElementType::PRISM:
3022 case ElementType::PYRAMID:
3025 case ElementType::REGULAR_INTERIOR_CUBE:
3028 case ElementType::REGULAR_BOUNDARY_CUBE:
3029 regular_boundary_count++;
3031 case ElementType::SIMPLE_SINGULAR_INTERIOR_CUBE:
3032 simple_singular_count++;
3034 case ElementType::MULTI_SINGULAR_INTERIOR_CUBE:
3035 multi_singular_count++;
3037 case ElementType::SIMPLE_SINGULAR_BOUNDARY_CUBE:
3040 case ElementType::INTERFACE_CUBE:
3041 case ElementType::MULTI_SINGULAR_BOUNDARY_CUBE:
3042 multi_singular_boundary_count++;
3044 case ElementType::BOUNDARY_POLYTOPE:
3045 non_regular_boundary_count++;
3047 case ElementType::INTERIOR_POLYTOPE:
3048 non_regular_count++;
3050 case ElementType::UNDEFINED:
3054 throw std::runtime_error(
"Unknown element type");
3058 logger().info(
"simplex_count: \t{}", simplex_count);
3059 logger().info(
"prism_count: \t{}", prism_count);
3060 logger().info(
"pyramid_count: \t{}", pyramid_count);
3061 logger().info(
"regular_count: \t{}", regular_count);
3062 logger().info(
"regular_boundary_count: \t{}", regular_boundary_count);
3063 logger().info(
"simple_singular_count: \t{}", simple_singular_count);
3064 logger().info(
"multi_singular_count: \t{}", multi_singular_count);
3065 logger().info(
"boundary_count: \t{}", boundary_count);
3066 logger().info(
"multi_singular_boundary_count: \t{}", multi_singular_boundary_count);
3067 logger().info(
"non_regular_count: \t{}", non_regular_count);
3068 logger().info(
"non_regular_boundary_count: \t{}", non_regular_boundary_count);
3069 logger().info(
"undefined_count: \t{}", undefined_count);
3074 const nlohmann::json &args,
3075 const int n_bases,
const int n_pressure_bases,
3076 const Eigen::MatrixXd &sol,
3078 const Eigen::VectorXi &disc_orders,
3079 const Eigen::VectorXi &disc_ordersq,
3082 const std::string &formulation,
3083 const bool isoparametric,
3084 const int sol_at_node_id,
3085 nlohmann::json &j)
const
3090 j[
"geom_order"] = mesh.
orders().size() > 0 ? mesh.
orders().maxCoeff() : 1;
3091 j[
"geom_order_min"] = mesh.
orders().size() > 0 ? mesh.
orders().minCoeff() : 1;
3092 j[
"discr_order_min"] = disc_orders.minCoeff();
3093 j[
"discr_order_max"] = disc_orders.maxCoeff();
3094 j[
"discr_orderq_min"] = disc_ordersq.minCoeff();
3095 j[
"discr_orderq_max"] = disc_ordersq.maxCoeff();
3096 j[
"iso_parametric"] = isoparametric;
3097 j[
"problem"] = problem.
name();
3098 j[
"mat_size"] = mat_size;
3099 j[
"num_bases"] = n_bases;
3100 j[
"num_pressure_bases"] = n_pressure_bases;
3101 j[
"num_non_zero"] = nn_zero;
3102 j[
"num_flipped"] = n_flipped;
3103 j[
"num_dofs"] = num_dofs;
3107 j[
"num_p1"] = (disc_orders.array() == 1).count();
3108 j[
"num_p2"] = (disc_orders.array() == 2).count();
3109 j[
"num_p3"] = (disc_orders.array() == 3).count();
3110 j[
"num_p4"] = (disc_orders.array() == 4).count();
3111 j[
"num_p5"] = (disc_orders.array() == 5).count();
3113 j[
"mesh_size"] = mesh_size;
3114 j[
"max_angle"] = max_angle;
3116 j[
"sigma_max"] = sigma_max;
3117 j[
"sigma_min"] = sigma_min;
3118 j[
"sigma_avg"] = sigma_avg;
3120 j[
"min_edge_length"] = min_edge_length;
3121 j[
"average_edge_length"] = average_edge_length;
3123 j[
"err_l2"] = l2_err;
3124 j[
"err_h1"] = h1_err;
3125 j[
"err_h1_semi"] = h1_semi_err;
3126 j[
"err_linf"] = linf_err;
3127 j[
"err_linf_grad"] = grad_max_err;
3128 j[
"err_lp"] = lp_err;
3130 j[
"spectrum"] = {spectrum(0), spectrum(1), spectrum(2), spectrum(3)};
3131 j[
"spectrum_condest"] = std::abs(spectrum(3)) / std::abs(spectrum(0));
3144 j[
"solver_info"] = solver_info;
3146 j[
"count_simplex"] = simplex_count;
3147 j[
"count_prism"] = prism_count;
3148 j[
"count_pyramid"] = pyramid_count;
3149 j[
"count_regular"] = regular_count;
3150 j[
"count_regular_boundary"] = regular_boundary_count;
3151 j[
"count_simple_singular"] = simple_singular_count;
3152 j[
"count_multi_singular"] = multi_singular_count;
3153 j[
"count_boundary"] = boundary_count;
3154 j[
"count_non_regular_boundary"] = non_regular_boundary_count;
3155 j[
"count_non_regular"] = non_regular_count;
3156 j[
"count_undefined"] = undefined_count;
3157 j[
"count_multi_singular_boundary"] = multi_singular_boundary_count;
3159 j[
"is_simplicial"] = mesh.
n_elements() == simplex_count;
3161 j[
"peak_memory"] =
getPeakRSS() / (1024 * 1024);
3165 std::vector<double> mmin(actual_dim);
3166 std::vector<double> mmax(actual_dim);
3168 for (
int d = 0; d < actual_dim; ++d)
3170 mmin[d] = std::numeric_limits<double>::max();
3171 mmax[d] = -std::numeric_limits<double>::max();
3174 for (
int i = 0; i < sol.size(); i += actual_dim)
3176 for (
int d = 0; d < actual_dim; ++d)
3178 mmin[d] = std::min(mmin[d], sol(i + d));
3179 mmax[d] = std::max(mmax[d], sol(i + d));
3183 std::vector<double> sol_at_node(actual_dim);
3185 if (sol_at_node_id >= 0)
3187 const int node_id = sol_at_node_id;
3189 for (
int d = 0; d < actual_dim; ++d)
3191 sol_at_node[d] = sol(node_id * actual_dim + d);
3195 j[
"sol_at_node"] = sol_at_node;
3196 j[
"sol_min"] = mmin;
3197 j[
"sol_max"] = mmax;
3199#if defined(POLYFEM_WITH_CPP_THREADS)
3201#elif defined(POLYFEM_WITH_TBB)
3204 j[
"num_threads"] = 1;
3207 j[
"formulation"] = formulation;
ElementAssemblyValues vals
stores per element basis values at given quadrature points and geometric mapping
void compute(const int el_index, const bool is_volume, const Eigen::MatrixXd &pts, const basis::ElementBases &basis, const basis::ElementBases &gbasis)
computes the per element values at the local (ref el) points (pts) sets basis_values,...
const std::string & name() const
virtual void exact_grad(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const
virtual bool is_scalar() const =0
virtual bool has_exact_sol() const =0
virtual void exact(const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const
Represents one basis function and its gradient.
const std::vector< Local2Global > & global() const
Stores the basis functions for a given element in a mesh (facet in 2d, cell in 3d).
void build_vis_boundary_mesh(const mesh::Mesh &mesh, const std::vector< basis::ElementBases > &gbases, const std::vector< mesh::LocalBoundary > &total_local_boundary, Eigen::MatrixXd &boundary_vis_vertices, Eigen::MatrixXd &boundary_vis_local_vertices, Eigen::MatrixXi &boundary_vis_elements, Eigen::MatrixXi &boundary_vis_elements_ids, Eigen::MatrixXi &boundary_vis_primitive_ids, Eigen::MatrixXd &boundary_vis_normals) const
builds the boundary mesh for visualization
Eigen::MatrixXd grid_points_bc
grid mesh boundaries
void build_high_order_vis_mesh(const mesh::Mesh &mesh, const Eigen::VectorXi &output_orders, const std::vector< basis::ElementBases > &bases, Eigen::MatrixXd &points, std::vector< paraviewo::CellElement > &elements, Eigen::MatrixXi &el_id, Eigen::MatrixXd &discr, Eigen::MatrixXd &local_points) const
builds high-der visualzation mesh per element all disconnected it also retuns the mapping to element ...
Eigen::MatrixXd grid_points
grid mesh points to export solution sampled on a grid
static void extract_boundary_mesh_sampled(const mesh::Mesh &mesh, const int n_bases, const std::vector< basis::ElementBases > &bases, const std::vector< mesh::LocalBoundary > &total_local_boundary, Eigen::MatrixXd &node_positions, Eigen::MatrixXi &boundary_edges, Eigen::MatrixXi &boundary_triangles, std::vector< Eigen::Triplet< double > > &displacement_map_entries, const int sampling_order=0)
extracts a collision proxy sampling every boundary face on a uniform lattice of the globally maximal ...
void save_volume(const std::string &path, const OutputSpace &space, const OutputFieldFunction &output_fields, const double t, const double dt, const ExportOptions &opts) const
saves the volume vtu file
void build_vis_mesh(const mesh::Mesh &mesh, const Eigen::VectorXi &disc_orders, const std::vector< basis::ElementBases > &gbases, const std::map< int, Eigen::MatrixXd > &polys, const std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &polys_3d, const bool boundary_only, Eigen::MatrixXd &points, Eigen::MatrixXi &tets, Eigen::MatrixXi &el_id, Eigen::MatrixXd &discr, Eigen::MatrixXd &local_points) const
builds visualzation mesh, upsampled mesh used for visualization the visualization mesh is a dense mes...
void build_grid(const polyfem::mesh::Mesh &mesh, const double spacing)
builds the grid to export the solution
void save_wire(const std::string &name, const OutputSpace &space, const OutputFieldFunction &output_fields, const double t, const ExportOptions &opts) const
saves the wireframe
static void extract_boundary_mesh(const mesh::Mesh &mesh, const int n_bases, const std::vector< basis::ElementBases > &bases, const std::vector< mesh::LocalBoundary > &total_local_boundary, Eigen::MatrixXd &node_positions, Eigen::MatrixXi &boundary_edges, Eigen::MatrixXi &boundary_triangles, std::vector< Eigen::Triplet< double > > &displacement_map_entries)
extracts the boundary mesh
void save_pvd(const std::string &name, const std::function< std::string(int)> &vtu_names, int time_steps, double t0, double dt, int skip_frame=1) const
save a PVD of a time dependent simulation
void save_contact_surface(const std::string &export_surface, const OutputSpace &space, const OutputFieldFunction &output_fields, const double t, const double dt_in, const ExportOptions &opts) const
saves the surface vtu file for for constact quantites, eg contact or friction forces
void export_data(const OutputSpace &space, const OutputFieldFunction &output_fields, const bool is_time_dependent, const double tend_in, const double dt, const ExportOptions &opts, const std::string &vis_mesh_path) const
exports everytihng, txt, vtu, etc
void save_points(const std::string &path, const OutputSpace &space, const OutputFieldFunction &output_fields, const ExportOptions &opts) const
saves the nodal values
void save_vtu(const std::string &path, const OutputSpace &space, const OutputFieldFunction &output_fields, const double t, const double dt, const ExportOptions &opts) const
saves the vtu file for time t
void init_sampler(const polyfem::mesh::Mesh &mesh, const double vismesh_rel_area)
unitalize the ref element sampler
void save_surface(const std::string &export_surface, const OutputSpace &space, const OutputFieldFunction &output_fields, const double t, const double dt_in, const ExportOptions &opts) const
saves the surface vtu file for for surface quantites, eg traction forces
Eigen::MatrixXi grid_points_to_elements
grid mesh mapping to fe elements
utils::RefElementSampler ref_element_sampler
used to sample the solution
double loading_mesh_time
time to load the mesh
double assembling_stiffness_mat_time
time to assembly
double assigning_rhs_time
time to computing the rhs
double assembling_mass_mat_time
time to assembly mass
double building_basis_time
time to construct the basis
double solving_time
time to solve
double computing_poly_basis_time
time to build the polygonal/polyhedral bases
void count_flipped_elements(const polyfem::mesh::Mesh &mesh, const std::vector< polyfem::basis::ElementBases > &gbases)
counts the number of flipped elements
void compute_errors(const int n_bases, const std::vector< polyfem::basis::ElementBases > &bases, const std::vector< polyfem::basis::ElementBases > &gbases, const polyfem::mesh::Mesh &mesh, const assembler::Problem &problem, const double tend, const Eigen::MatrixXd &sol)
compute errors
void compute_mesh_size(const polyfem::mesh::Mesh &mesh_in, const std::vector< polyfem::basis::ElementBases > &bases_in, const int n_samples, const bool use_curved_mesh_size)
computes the mesh size, it samples every edges n_samples times uses curved_mesh_size (false by defaul...
void reset()
clears all stats
void save_json(const nlohmann::json &args, const int n_bases, const int n_pressure_bases, const Eigen::MatrixXd &sol, const mesh::Mesh &mesh, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const assembler::Problem &problem, const OutRuntimeData &runtime, const std::string &formulation, const bool isoparametric, const int sol_at_node_id, nlohmann::json &j) const
saves the output statistic to a json object
void compute_mesh_stats(const polyfem::mesh::Mesh &mesh)
compute stats (counts els type, mesh lenght, etc), step 1 of solve
Boundary primitive IDs for a single element.
virtual Navigation3D::Index get_index_from_element(int hi, int lf, int lv) const =0
virtual int n_cell_faces(const int c_id) const =0
virtual Navigation3D::Index next_around_face(Navigation3D::Index idx) const =0
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
virtual int n_vertices() const =0
number of vertices
bool is_polytope(const int el_id) const
checks if element is polygon compatible
virtual void get_edges(Eigen::MatrixXd &p0, Eigen::MatrixXd &p1) const =0
Get all the edges.
bool is_simplicial() const
checks if the mesh is simplicial
virtual bool is_conforming() const =0
if the mesh is conforming
virtual void bounding_box(RowVectorNd &min, RowVectorNd &max) const =0
computes the bbox of the mesh
virtual void barycentric_coords(const RowVectorNd &p, const int el_id, Eigen::MatrixXd &coord) const =0
constructs barycentric coodiantes for a point p.
bool is_cube(const int el_id) const
checks if element is cube compatible
const Eigen::MatrixXi & orders() const
order of each element
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
virtual bool is_volume() const =0
checks if mesh is volume
bool has_poly() const
checks if the mesh has polytopes
int dimension() const
utily for dimension
virtual int n_faces() const =0
number of faces
const std::vector< ElementType > & elements_tag() const
Returns the elements types.
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
virtual void elements_boxes(std::vector< std::array< Eigen::Vector3d, 2 > > &boxes) const =0
constructs a box around every element (3d cell, 2d face)
virtual bool is_boundary_element(const int element_global_id) const =0
is cell boundary
virtual int get_node_id(const int node_id) const
Get the boundary selection of a node.
const Eigen::MatrixXi & get_edge_connectivity() const
const Eigen::MatrixXi & get_face_connectivity() const
const Eigen::MatrixXd & v() const
const Eigen::VectorXi & get_vertex_connectivity() const
static void sample_parametric_prism_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void normal_for_quad_edge(int index, Eigen::MatrixXd &normal)
static void normal_for_tri_edge(int index, Eigen::MatrixXd &normal)
static void normal_for_quad_face(int index, Eigen::MatrixXd &normal)
static void sample_parametric_pyramid_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void sample_parametric_tri_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void normal_for_prism_face(int index, Eigen::MatrixXd &normal)
static void normal_for_tri_face(int index, Eigen::MatrixXd &normal)
static void sample_parametric_quad_face(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void normal_for_polygon_edge(int face_id, int edge_id, const mesh::Mesh &mesh, Eigen::MatrixXd &normal)
static void normal_for_pyramid_face(int index, Eigen::MatrixXd &normal)
static void sample_parametric_quad_edge(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void sample_polygon_edge(int face_id, int edge_id, int n_samples, const mesh::Mesh &mesh, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void sample_parametric_tri_edge(int index, int n_samples, Eigen::MatrixXd &uv, Eigen::MatrixXd &samples)
static void sample_3d_simplex(const int resolution, Eigen::MatrixXd &samples)
static void sample_3d_cube(const int resolution, Eigen::MatrixXd &samples)
static void sample_2d_cube(const int resolution, Eigen::MatrixXd &samples)
static void sample_2d_simplex(const int resolution, Eigen::MatrixXd &samples)
void init(const bool is_volume, const int n_elements, const double target_rel_area)
const Eigen::MatrixXi & pyramid_volume() const
const Eigen::MatrixXd & pyramid_points() const
const Eigen::MatrixXi & simplex_volume() const
size_t getPeakRSS(void)
Returns the peak (maximum so far) resident set size (physical memory use) measured in bytes,...
void q_nodes_2d(const int q, Eigen::MatrixXd &val)
void pyramid_nodes_3d(const int pyramid, Eigen::MatrixXd &val)
void prism_nodes_3d(const int p, const int q, Eigen::MatrixXd &val)
void p_nodes_2d(const int p, Eigen::MatrixXd &val)
void p_nodes_3d(const int p, Eigen::MatrixXd &val)
void q_nodes_3d(const int q, Eigen::MatrixXd &val)
std::function< std::vector< OutputField >(const OutputSample &)> OutputFieldFunction
paraviewo::CellElement CellElement
paraviewo::CellType CellType
bool write_matrix(const std::string &path, const Mat &mat)
Writes a matrix to a file. Determines the file format based on the path's extension.
ElementType
Type of Element, check [Poly-Spline Finite Element Method] for a complete description.
spdlog::logger & logger()
Retrieves the current logger.
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
void log_and_throw_error(const std::string &msg)
bool tangential_adhesion_forces
std::string file_extension() const
return the extension of the output paraview files depending on use_hdf5
std::vector< std::string > fields
ExportOptions(const json &args, const bool is_mesh_linear, const bool mesh_has_prisms, const bool is_problem_scalar)
initialize the flags based on the input args
bool discretization_order
bool normal_adhesion_forces
bool export_field(const std::string &field) const
bool export_field(const std::string &field) const
std::vector< std::string > fields
Eigen::VectorXi primitive_ids
std::vector< std::string > requested_fields
Eigen::VectorXi element_ids
Eigen::MatrixXd local_points
Eigen::VectorXi output_orders
const std::vector< mesh::LocalBoundary > * total_local_boundary
const std::vector< basis::ElementBases > * geometry_bases
const std::vector< RowVectorNd > * dirichlet_nodes_position
const std::vector< int > * dirichlet_nodes
const std::map< int, Eigen::MatrixXd > * polys
const mesh::Obstacle * obstacle
const ipc::CollisionMesh * collision_mesh
const std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > * polys_3d