31#include <paraviewo/VTMWriter.hpp>
32#include <paraviewo/PVDWriter.hpp>
34#include <ipc/potentials/normal_adhesion_potential.hpp>
35#include <ipc/potentials/tangential_adhesion_potential.hpp>
37#include <SimpleBVH/BVH.hpp>
39#include <igl/write_triangle_mesh.h>
41#include <igl/facet_adjacency_matrix.h>
42#include <igl/connected_components.h>
61 void add_output_fields(
62 paraviewo::ParaviewWriter &writer,
69 for (
const OutputField &field : output_fields(sample))
71 if (field.values.rows() <= 0)
74 const int expected_rows =
78 if (field.values.rows() != expected_rows)
81 "Skipping output field '{}' with {} rows; expected {} {} rows",
82 field.name, field.values.rows(), expected_rows,
88 writer.add_cell_field(field.name, field.values);
90 writer.add_field(field.name, field.values);
98 const std::vector<basis::ElementBases> &bases,
99 const std::vector<mesh::LocalBoundary> &total_local_boundary,
100 Eigen::MatrixXd &node_positions,
101 Eigen::MatrixXi &boundary_edges,
102 Eigen::MatrixXi &boundary_triangles,
103 std::vector<Eigen::Triplet<double>> &displacement_map_entries)
107 displacement_map_entries.clear();
113 logger().warn(
"Skipping as the mesh has polygons");
119 node_positions.resize(n_bases + (is_simplicial ? 0 : mesh.
n_faces()), 3);
120 node_positions.setZero();
121 const Mesh3D &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
123 std::vector<std::tuple<int, int, int>> tris;
125 std::vector<bool> visited_node(n_bases,
false);
127 std::stringstream print_warning;
133 for (
int j = 0; j < lb.size(); ++j)
135 const int eid = lb.global_primitive_id(j);
136 const int lid = lb[j];
137 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh3d);
139 if (mesh.
is_cube(lb.element_id()))
141 assert(!is_simplicial);
143 std::vector<int> loc_nodes;
146 for (
long n = 0; n < nodes.size(); ++n)
148 auto &bs = b.bases[nodes(n)];
149 const auto &glob = bs.global();
150 if (glob.size() != 1)
153 int gindex = glob.front().index;
154 node_positions.row(gindex) = glob.front().node;
155 bary += glob.front().node;
156 loc_nodes.push_back(gindex);
159 if (loc_nodes.size() != 4)
161 logger().trace(
"skipping element {} since it is not Q1", eid);
167 const int new_node = n_bases + eid;
168 node_positions.row(new_node) = bary;
169 tris.emplace_back(loc_nodes[1], loc_nodes[0], new_node);
170 tris.emplace_back(loc_nodes[2], loc_nodes[1], new_node);
171 tris.emplace_back(loc_nodes[3], loc_nodes[2], new_node);
172 tris.emplace_back(loc_nodes[0], loc_nodes[3], new_node);
174 for (
int q = 0; q < 4; ++q)
176 if (!visited_node[loc_nodes[q]])
177 displacement_map_entries.emplace_back(loc_nodes[q], loc_nodes[q], 1);
179 visited_node[loc_nodes[q]] =
true;
180 displacement_map_entries.emplace_back(new_node, loc_nodes[q], 0.25);
185 else if (mesh.
is_prism(lb.element_id()))
187 assert(!is_simplicial);
189 std::vector<int> loc_nodes;
192 for (
long n = 0; n < nodes.size(); ++n)
194 auto &bs = b.bases[nodes(n)];
195 const auto &glob = bs.global();
196 if (glob.size() != 1)
199 int gindex = glob.front().index;
200 node_positions.row(gindex) = glob.front().node;
201 bary += glob.front().node;
202 loc_nodes.push_back(gindex);
205 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
206 for (
int k = 0; k < loc_nodes.size(); ++k)
208 if (!visited_node[loc_nodes[k]])
209 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
211 visited_node[loc_nodes[k]] =
true;
215 if (loc_nodes.size() == 3)
217 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
219 update_mapping(loc_nodes);
221 else if (loc_nodes.size() == 6)
223 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
224 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
225 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
226 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
228 update_mapping(loc_nodes);
230 else if (loc_nodes.size() == 10)
232 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
233 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
234 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
235 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
236 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
237 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
238 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
239 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
240 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
241 update_mapping(loc_nodes);
243 else if (loc_nodes.size() == 15)
245 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[11]);
246 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[12]);
247 tris.emplace_back(loc_nodes[3], loc_nodes[12], loc_nodes[11]);
248 tris.emplace_back(loc_nodes[12], loc_nodes[10], loc_nodes[11]);
249 tris.emplace_back(loc_nodes[4], loc_nodes[5], loc_nodes[13]);
250 tris.emplace_back(loc_nodes[4], loc_nodes[13], loc_nodes[12]);
251 tris.emplace_back(loc_nodes[12], loc_nodes[13], loc_nodes[14]);
252 tris.emplace_back(loc_nodes[12], loc_nodes[14], loc_nodes[10]);
253 tris.emplace_back(loc_nodes[14], loc_nodes[9], loc_nodes[10]);
254 tris.emplace_back(loc_nodes[5], loc_nodes[1], loc_nodes[6]);
255 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[13]);
256 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[13]);
257 tris.emplace_back(loc_nodes[13], loc_nodes[7], loc_nodes[14]);
258 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[14]);
259 tris.emplace_back(loc_nodes[14], loc_nodes[8], loc_nodes[9]);
260 tris.emplace_back(loc_nodes[8], loc_nodes[2], loc_nodes[9]);
261 update_mapping(loc_nodes);
263 else if (loc_nodes.size() == 4)
267 const int new_node = n_bases + eid;
268 node_positions.row(new_node) = bary;
269 tris.emplace_back(loc_nodes[1], loc_nodes[0], new_node);
270 tris.emplace_back(loc_nodes[2], loc_nodes[1], new_node);
271 tris.emplace_back(loc_nodes[3], loc_nodes[2], new_node);
272 tris.emplace_back(loc_nodes[0], loc_nodes[3], new_node);
274 update_mapping(loc_nodes);
278 logger().trace(
"skipping element {} since it is not linear, it has {} nodes", eid, loc_nodes.size());
287 assert(!is_simplicial);
289 std::vector<int> loc_nodes;
291 for (
long n = 0; n < nodes.size(); ++n)
293 auto &bs = b.bases[nodes(n)];
294 const auto &glob = bs.global();
295 if (glob.size() != 1)
298 int gindex = glob.front().index;
299 node_positions.row(gindex) = glob.front().node;
300 loc_nodes.push_back(gindex);
303 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
304 for (
int k = 0; k < loc_nodes.size(); ++k)
306 if (!visited_node[loc_nodes[k]])
307 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
309 visited_node[loc_nodes[k]] =
true;
313 if (loc_nodes.size() == 3)
315 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
316 update_mapping(loc_nodes);
318 else if (loc_nodes.size() == 4)
320 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
321 tris.emplace_back(loc_nodes[0], loc_nodes[2], loc_nodes[3]);
322 update_mapping(loc_nodes);
326 logger().trace(
"skipping element {} since it is not linear, it has {} nodes", eid, loc_nodes.size());
335 logger().trace(
"skipping element {} since it is not a simplex or hex", eid);
341 std::vector<int> loc_nodes;
343 bool is_follower =
false;
346 for (
long n = 0; n < nodes.size(); ++n)
348 auto &bs = b.bases[nodes(n)];
349 const auto &glob = bs.global();
350 if (glob.size() != 1)
361 for (
long n = 0; n < nodes.size(); ++n)
364 const std::vector<basis::Local2Global> &glob = bs.
global();
365 if (glob.size() != 1)
368 int gindex = glob.front().index;
369 node_positions.row(gindex) = glob.front().node;
370 loc_nodes.push_back(gindex);
373 if (loc_nodes.size() == 3)
375 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
377 else if (loc_nodes.size() == 6)
379 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
380 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
381 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
382 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
384 else if (loc_nodes.size() == 10)
386 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
387 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
388 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
389 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
390 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
391 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
392 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
393 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
394 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
396 else if (loc_nodes.size() == 15)
398 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[11]);
399 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[12]);
400 tris.emplace_back(loc_nodes[3], loc_nodes[12], loc_nodes[11]);
401 tris.emplace_back(loc_nodes[12], loc_nodes[10], loc_nodes[11]);
402 tris.emplace_back(loc_nodes[4], loc_nodes[5], loc_nodes[13]);
403 tris.emplace_back(loc_nodes[4], loc_nodes[13], loc_nodes[12]);
404 tris.emplace_back(loc_nodes[12], loc_nodes[13], loc_nodes[14]);
405 tris.emplace_back(loc_nodes[12], loc_nodes[14], loc_nodes[10]);
406 tris.emplace_back(loc_nodes[14], loc_nodes[9], loc_nodes[10]);
407 tris.emplace_back(loc_nodes[5], loc_nodes[1], loc_nodes[6]);
408 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[13]);
409 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[13]);
410 tris.emplace_back(loc_nodes[13], loc_nodes[7], loc_nodes[14]);
411 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[14]);
412 tris.emplace_back(loc_nodes[14], loc_nodes[8], loc_nodes[9]);
413 tris.emplace_back(loc_nodes[8], loc_nodes[2], loc_nodes[9]);
417 print_warning << loc_nodes.size() <<
" ";
423 for (
int k = 0; k < loc_nodes.size(); ++k)
425 if (!visited_node[loc_nodes[k]])
426 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
428 visited_node[loc_nodes[k]] =
true;
434 if (print_warning.str().size() > 0)
435 logger().warn(
"Skipping faces as theys have {} nodes, boundary export supported up to p4", print_warning.str());
437 boundary_triangles.resize(tris.size(), 3);
438 for (
int i = 0; i < tris.size(); ++i)
440 boundary_triangles.row(i) << std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]);
443 if (boundary_triangles.rows() > 0)
445 igl::edges(boundary_triangles, boundary_edges);
450 node_positions.resize(n_bases, 2);
451 node_positions.setZero();
452 const Mesh2D &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
454 std::vector<std::pair<int, int>> edges;
460 for (
int j = 0; j < lb.size(); ++j)
462 const int eid = lb.global_primitive_id(j);
463 const int lid = lb[j];
464 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh2d);
468 for (
long n = 0; n < nodes.size(); ++n)
471 const std::vector<basis::Local2Global> &glob = bs.
global();
472 if (glob.size() != 1)
475 int gindex = glob.front().index;
476 node_positions.row(gindex) = glob.front().node.head<2>();
479 edges.emplace_back(prev_node, gindex);
486 boundary_triangles.resize(0, 0);
487 boundary_edges.resize(edges.size(), 2);
488 for (
int i = 0; i < edges.size(); ++i)
490 boundary_edges.row(i) << edges[i].first, edges[i].second;
497 const std::vector<basis::ElementBases> &gbases,
498 const std::vector<mesh::LocalBoundary> &total_local_boundary,
499 Eigen::MatrixXd &boundary_vis_vertices,
500 Eigen::MatrixXd &boundary_vis_local_vertices,
501 Eigen::MatrixXi &boundary_vis_elements,
502 Eigen::MatrixXi &boundary_vis_elements_ids,
503 Eigen::MatrixXi &boundary_vis_primitive_ids,
504 Eigen::MatrixXd &boundary_vis_normals)
const
508 std::vector<Eigen::MatrixXd> lv, vertices, allnormals;
509 std::vector<int> el_ids, global_primitive_ids;
510 Eigen::MatrixXd uv, local_pts, tmp_n, normals;
516 std::vector<std::pair<int, int>> edges;
517 std::vector<std::tuple<int, int, int>> tris;
519 for (
auto it = total_local_boundary.begin(); it != total_local_boundary.end(); ++it)
521 const auto &lb = *it;
522 const auto &gbs = gbases[lb.element_id()];
524 for (
int k = 0; k < lb.size(); ++k)
528 case BoundaryType::TRI_LINE:
532 case BoundaryType::QUAD_LINE:
536 case BoundaryType::QUAD:
540 case BoundaryType::TRI:
544 case BoundaryType::PRISM:
548 case BoundaryType::PYRAMID:
552 case BoundaryType::POLYGON:
556 case BoundaryType::POLYHEDRON:
559 case BoundaryType::INVALID:
566 vertices.emplace_back();
567 lv.emplace_back(local_pts);
568 el_ids.push_back(lb.element_id());
569 global_primitive_ids.push_back(lb.global_primitive_id(k));
570 gbs.eval_geom_mapping(local_pts, vertices.back());
571 vals.compute(lb.element_id(), mesh.
is_volume(), local_pts, gbs, gbs);
572 const int tris_start = tris.size();
576 const bool prism_quad = lb.type() == BoundaryType::PRISM && lb[k] >= 2;
577 const bool prism_tri = lb.type() == BoundaryType::PRISM && lb[k] < 2;
579 const bool pyramid_quad = lb.type() == BoundaryType::PYRAMID && lb[k] == 0;
580 const bool pyramid_tri = lb.type() == BoundaryType::PYRAMID && lb[k] > 0;
582 if (lb.type() == BoundaryType::QUAD || prism_quad || pyramid_quad)
584 const auto map = [n_samples, size](
int i,
int j) {
return j * n_samples + i + size; };
586 for (
int j = 0; j < n_samples - 1; ++j)
588 for (
int i = 0; i < n_samples - 1; ++i)
590 tris.emplace_back(map(i, j), map(i + 1, j), map(i, j + 1));
591 tris.emplace_back(map(i + 1, j + 1), map(i, j + 1), map(i + 1, j));
595 else if (lb.type() == BoundaryType::TRI || prism_tri || pyramid_tri)
598 std::vector<int> mapp(n_samples * n_samples, -1);
599 for (
int j = 0; j < n_samples; ++j)
601 for (
int i = 0; i < n_samples - j; ++i)
603 mapp[j * n_samples + i] = index;
607 const auto map = [mapp, n_samples](
int i,
int j) {
608 if (j * n_samples + i >= mapp.size())
610 return mapp[j * n_samples + i];
613 for (
int j = 0; j < n_samples - 1; ++j)
615 for (
int i = 0; i < n_samples - j; ++i)
617 if (map(i, j) >= 0 && map(i + 1, j) >= 0 && map(i, j + 1) >= 0)
618 tris.emplace_back(map(i, j) + size, map(i + 1, j) + size, map(i, j + 1) + size);
620 if (map(i + 1, j + 1) >= 0 && map(i, j + 1) >= 0 && map(i + 1, j) >= 0)
621 tris.emplace_back(map(i + 1, j + 1) + size, map(i, j + 1) + size, map(i + 1, j) + size);
632 for (
int i = 0; i < vertices.back().rows() - 1; ++i)
633 edges.emplace_back(i + size, i + size + 1);
636 normals.resize(
vals.jac_it.size(), tmp_n.cols());
638 for (
int n = 0; n <
vals.jac_it.size(); ++n)
640 normals.row(n) = tmp_n *
vals.jac_it[n];
641 normals.row(n).normalize();
644 allnormals.push_back(normals);
647 for (
int n = 0; n <
vals.jac_it.size(); ++n)
649 tmp_n += normals.row(n);
654 Eigen::Vector3d e1 = vertices.back().row(std::get<1>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
655 Eigen::Vector3d e2 = vertices.back().row(std::get<2>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
657 Eigen::Vector3d n = e1.cross(e2);
658 Eigen::Vector3d nn = tmp_n.transpose();
662 for (
int i = tris_start; i < tris.size(); ++i)
664 tris[i] = std::tuple<int, int, int>(std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]));
669 size += vertices.back().rows();
673 boundary_vis_vertices.resize(size, vertices.front().cols());
674 boundary_vis_local_vertices.resize(size, vertices.front().cols());
675 boundary_vis_elements_ids.resize(size, 1);
676 boundary_vis_primitive_ids.resize(size, 1);
677 boundary_vis_normals.resize(size, vertices.front().cols());
680 boundary_vis_elements.resize(tris.size(), 3);
682 boundary_vis_elements.resize(edges.size(), 2);
686 for (
const auto &v : vertices)
688 boundary_vis_vertices.block(index, 0, v.rows(), v.cols()) = v;
689 boundary_vis_local_vertices.block(index, 0, v.rows(), v.cols()) = lv[ii];
690 boundary_vis_elements_ids.block(index, 0, v.rows(), 1).setConstant(el_ids[ii]);
691 boundary_vis_primitive_ids.block(index, 0, v.rows(), 1).setConstant(global_primitive_ids[ii++]);
696 for (
const auto &n : allnormals)
698 boundary_vis_normals.block(index, 0, n.rows(), n.cols()) = n;
705 for (
const auto &t : tris)
707 boundary_vis_elements.row(index) << std::get<0>(t), std::get<1>(t), std::get<2>(t);
713 for (
const auto &e : edges)
715 boundary_vis_elements.row(index) << e.first, e.second;
723 const Eigen::VectorXi &disc_orders,
724 const std::vector<basis::ElementBases> &gbases,
725 const std::map<int, Eigen::MatrixXd> &polys,
726 const std::map<
int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d,
727 const bool boundary_only,
728 Eigen::MatrixXd &points,
729 Eigen::MatrixXi &tets,
730 Eigen::MatrixXi &el_id,
731 Eigen::MatrixXd &discr,
732 Eigen::MatrixXd &local_points)
const
736 const auto ¤t_bases = gbases;
737 int tet_total_size = 0;
738 int pts_total_size = 0;
740 Eigen::MatrixXd vis_pts_poly;
741 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
743 for (
size_t i = 0; i < current_bases.size(); ++i)
745 const auto &bs = current_bases[i];
753 pts_total_size += sampler.simplex_points().rows();
757 tet_total_size += sampler.cube_volume().rows();
758 pts_total_size += sampler.cube_points().rows();
762 tet_total_size += sampler.prism_volume().rows();
763 pts_total_size += sampler.prism_points().rows();
767 tet_total_size += sampler.pyramid_volume().rows();
768 pts_total_size += sampler.pyramid_points().rows();
774 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
776 tet_total_size += vis_faces_poly.rows();
777 pts_total_size += vis_pts_poly.rows();
781 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
783 tet_total_size += vis_faces_poly.rows();
784 pts_total_size += vis_pts_poly.rows();
789 points.resize(pts_total_size, mesh.
dimension());
790 local_points.resize(pts_total_size, mesh.
dimension());
791 local_points.setZero();
792 tets.resize(tet_total_size, mesh.
is_volume() ? 4 : 3);
794 el_id.resize(pts_total_size, 1);
795 discr.resize(pts_total_size, 1);
797 Eigen::MatrixXd mapped, tmp;
798 int tet_index = 0, pts_index = 0;
800 for (
size_t i = 0; i < current_bases.size(); ++i)
802 const auto &bs = current_bases[i];
809 bs.eval_geom_mapping(sampler.simplex_points(), mapped);
811 tets.block(tet_index, 0, sampler.simplex_volume().rows(), tets.cols()) = sampler.simplex_volume().array() + pts_index;
812 tet_index += sampler.simplex_volume().rows();
814 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
815 local_points.block(pts_index, 0, sampler.simplex_points().rows(), sampler.simplex_points().cols()) = sampler.simplex_points();
816 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
817 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
818 pts_index += mapped.rows();
822 bs.eval_geom_mapping(sampler.cube_points(), mapped);
824 tets.block(tet_index, 0, sampler.cube_volume().rows(), tets.cols()) = sampler.cube_volume().array() + pts_index;
825 tet_index += sampler.cube_volume().rows();
827 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
828 local_points.block(pts_index, 0, sampler.cube_points().rows(), sampler.cube_points().cols()) = sampler.cube_points();
829 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
830 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
831 pts_index += mapped.rows();
835 bs.eval_geom_mapping(sampler.prism_points(), mapped);
837 tets.block(tet_index, 0, sampler.prism_volume().rows(), tets.cols()) = sampler.prism_volume().array() + pts_index;
838 tet_index += sampler.prism_volume().rows();
840 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
841 local_points.block(pts_index, 0, sampler.prism_points().rows(), sampler.prism_points().cols()) = sampler.prism_points();
842 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
843 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
844 pts_index += mapped.rows();
848 bs.eval_geom_mapping(sampler.pyramid_points(), mapped);
850 tets.block(tet_index, 0, sampler.pyramid_volume().rows(), tets.cols()) = sampler.pyramid_volume().array() + pts_index;
851 tet_index += sampler.pyramid_volume().rows();
853 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
854 local_points.block(pts_index, 0, sampler.pyramid_points().rows(), sampler.pyramid_points().cols()) = sampler.pyramid_points();
855 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
856 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
857 pts_index += mapped.rows();
863 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
864 bs.eval_geom_mapping(vis_pts_poly, mapped);
866 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
867 tet_index += vis_faces_poly.rows();
869 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
870 local_points.block(pts_index, 0, vis_pts_poly.rows(), vis_pts_poly.cols()) = vis_pts_poly;
871 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
872 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
873 pts_index += mapped.rows();
877 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
878 bs.eval_geom_mapping(vis_pts_poly, mapped);
880 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
881 tet_index += vis_faces_poly.rows();
883 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
884 local_points.block(pts_index, 0, vis_pts_poly.rows(), vis_pts_poly.cols()) = vis_pts_poly;
885 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
886 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
887 pts_index += mapped.rows();
892 assert(pts_index == points.rows());
893 assert(tet_index == tets.rows());
898 const Eigen::VectorXi &output_orders,
899 const std::vector<basis::ElementBases> &bases,
900 Eigen::MatrixXd &points,
901 std::vector<CellElement> &elements,
902 Eigen::MatrixXi &el_id,
903 Eigen::MatrixXd &discr,
904 Eigen::MatrixXd &local_points)
const
918 std::vector<RowVectorNd> nodes;
919 int pts_total_size = 0;
920 elements.resize(bases.size());
921 Eigen::MatrixXd ref_pts;
923 for (
size_t i = 0; i < bases.size(); ++i)
925 const auto &bs = bases[i];
951 const int n_v =
static_cast<const mesh::Mesh2D &
>(mesh).n_face_vertices(i);
952 ref_pts.resize(n_v, 2);
956 pts_total_size += ref_pts.rows();
960 local_points.resize(pts_total_size, mesh.
dimension());
961 local_points.setZero();
963 el_id.resize(pts_total_size, 1);
964 discr.resize(pts_total_size, 1);
966 Eigen::MatrixXd mapped;
969 std::string error_msg =
"";
971 for (
size_t i = 0; i < bases.size(); ++i)
973 const auto &bs = bases[i];
1001 bs.eval_geom_mapping(ref_pts, mapped);
1003 for (
int j = 0; j < mapped.rows(); ++j)
1005 points.row(pts_index) = mapped.row(j);
1006 local_points.row(pts_index).leftCols(ref_pts.cols()) = ref_pts.row(j);
1007 el_id(pts_index) = i;
1008 discr(pts_index) = output_orders(i);
1009 elements[i].vertices.push_back(pts_index);
1018 const int n_nodes = elements[i].vertices.size();
1019 if (output_orders(i) >= 3)
1021 std::swap(elements[i].vertices[16], elements[i].vertices[17]);
1022 std::swap(elements[i].vertices[17], elements[i].vertices[18]);
1023 std::swap(elements[i].vertices[18], elements[i].vertices[19]);
1025 if (output_orders(i) > 4)
1026 error_msg =
"Saving high-order meshes not implemented for P5+ elements!";
1030 if (output_orders(i) == 4)
1032 const int n_nodes = elements[i].vertices.size();
1033 std::swap(elements[i].vertices[n_nodes - 1], elements[i].vertices[n_nodes - 2]);
1035 if (output_orders(i) > 4)
1036 error_msg =
"Saving high-order meshes not implemented for P5+ elements!";
1041 const int n_nodes = elements[i].vertices.size();
1042 if (output_orders(i) == 2)
1044 std::swap(elements[i].vertices[12], elements[i].vertices[16]);
1045 std::swap(elements[i].vertices[13], elements[i].vertices[17]);
1046 std::swap(elements[i].vertices[14], elements[i].vertices[18]);
1047 std::swap(elements[i].vertices[15], elements[i].vertices[19]);
1048 std::swap(elements[i].vertices[18], elements[i].vertices[19]);
1063 if (output_orders(i) > 2)
1064 error_msg =
"Saving high-order meshes not implemented for P2+ elements!";
1066 else if (output_orders(i) > 1)
1067 error_msg =
"Saving high-order meshes not implemented for Q2+ elements!";
1070 if (!error_msg.empty())
1071 logger().warn(error_msg);
1073 for (
size_t i = 0; i < bases.size(); ++i)
1078 const auto &mesh2d =
static_cast<const mesh::Mesh2D &
>(mesh);
1081 for (
int j = 0; j < n_v; ++j)
1083 points.row(pts_index) = mesh2d.point(mesh2d.face_vertex(i, j));
1084 local_points.row(pts_index) = mesh2d.point(mesh2d.face_vertex(i, j));
1085 el_id(pts_index) = i;
1086 discr(pts_index) = output_orders(i);
1087 elements[i].vertices.push_back(pts_index);
1093 for (
size_t i = 0; i < bases.size(); ++i)
1097 if (elements[i].
vertices.size() == 1)
1098 elements[i].ctype = CellType::Vertex;
1099 else if (elements[i].
vertices.size() == 2)
1100 elements[i].ctype = CellType::Line;
1102 elements[i].ctype = CellType::Triangle;
1104 elements[i].ctype = CellType::Quadrilateral;
1106 elements[i].ctype = CellType::Polygon;
1111 elements[i].ctype = CellType::Tetrahedron;
1113 elements[i].ctype = CellType::Hexahedron;
1115 elements[i].ctype = CellType::Wedge;
1117 elements[i].ctype = CellType::Pyramid;
1121 assert(pts_index ==
points.rows());
1127 const bool is_time_dependent,
1128 const double tend_in,
1131 const std::string &vis_mesh_path)
const
1135 logger().error(
"Load the mesh first!");
1139 double tend = tend_in;
1143 if (!vis_mesh_path.empty() && !is_time_dependent)
1146 vis_mesh_path, space, output_fields,
1158 fields = args[
"output"][
"paraview"][
"fields"];
1160 volume = args[
"output"][
"paraview"][
"volume"];
1161 surface = args[
"output"][
"paraview"][
"surface"];
1162 wire = args[
"output"][
"paraview"][
"wireframe"];
1163 points = args[
"output"][
"paraview"][
"points"];
1164 contact_forces = args[
"output"][
"paraview"][
"options"][
"contact_forces"] && !is_problem_scalar;
1165 friction_forces = args[
"output"][
"paraview"][
"options"][
"friction_forces"] && !is_problem_scalar;
1166 normal_adhesion_forces = args[
"output"][
"paraview"][
"options"][
"normal_adhesion_forces"] && !is_problem_scalar;
1167 tangential_adhesion_forces = args[
"output"][
"paraview"][
"options"][
"tangential_adhesion_forces"] && !is_problem_scalar;
1169 if (args[
"output"][
"paraview"][
"options"][
"force_high_order"])
1170 use_sampler =
false;
1172 use_sampler = !(is_mesh_linear && args[
"output"][
"paraview"][
"high_order_mesh"]);
1173 boundary_only = use_sampler && args[
"output"][
"advanced"][
"vis_boundary_only"];
1174 sol_on_grid = args[
"output"][
"advanced"][
"sol_on_grid"] > 0;
1176 discretization_order = args[
"output"][
"paraview"][
"options"][
"discretization_order"];
1178 reorder_output = args[
"output"][
"data"][
"advanced"][
"reorder_nodes"];
1180 use_hdf5 = args[
"output"][
"paraview"][
"options"][
"use_hdf5"];
1184 const std::string &path,
1193 logger().error(
"Load the mesh first!");
1197 const bool save_contact =
1202 logger().info(
"Saving vtu to {}; volume={}, surface={}, contact={}, points={}, wireframe={}",
1205 const std::filesystem::path fs_path(path);
1206 const std::string path_stem = fs_path.stem().string();
1207 const std::string base_path = (fs_path.parent_path() / path_stem).
string();
1234 paraviewo::VTMWriter vtm(t);
1236 vtm.add_dataset(
"Volume",
"data", path_stem + opts.
file_extension());
1238 vtm.add_dataset(
"Surface",
"data", path_stem +
"_surf" + opts.
file_extension());
1240 vtm.add_dataset(
"Contact",
"data", path_stem +
"_surf_contact" + opts.
file_extension());
1242 vtm.add_dataset(
"Wireframe",
"data", path_stem +
"_wire" + opts.
file_extension());
1244 vtm.add_dataset(
"Points",
"data", path_stem +
"_points" + opts.
file_extension());
1245 vtm.save(base_path +
".vtm");
1249 const std::string &path,
1259 static const std::map<int, Eigen::MatrixXd> empty_polys;
1260 static const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> empty_polys_3d;
1263 const std::vector<basis::ElementBases> &gbases = *space.
geometry_bases;
1264 const std::map<int, Eigen::MatrixXd> &polys = space.
polys ? *space.
polys : empty_polys;
1265 const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d = space.
polys_3d ? *space.
polys_3d : empty_polys_3d;
1266 const Eigen::VectorXi output_orders =
1272 Eigen::MatrixXd points;
1273 Eigen::MatrixXi tets;
1274 Eigen::MatrixXi el_id;
1275 Eigen::MatrixXd discr;
1276 Eigen::MatrixXd local_points;
1277 std::vector<CellElement> elements;
1282 points, tets, el_id, discr, local_points);
1286 points, elements, el_id, discr, local_points);
1289 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
1291 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
1293 tmpw = std::make_shared<paraviewo::VTUWriter>();
1294 paraviewo::ParaviewWriter &writer = *tmpw;
1298 discr.conservativeResize(discr.size() + obstacle->
n_vertices(), 1);
1299 discr.bottomRows(obstacle->
n_vertices()).setZero();
1303 writer.add_field(
"discr", discr);
1307 const int orig_p = points.rows();
1308 points.conservativeResize(points.rows() + obstacle->
n_vertices(), points.cols());
1309 points.bottomRows(obstacle->
n_vertices()) = obstacle->
v();
1311 if (elements.empty())
1313 for (
int i = 0; i < tets.rows(); ++i)
1315 elements.emplace_back();
1316 elements.back().ctype = CellType::Tetrahedron;
1317 for (
int j = 0; j < tets.cols(); ++j)
1318 elements.back().vertices.push_back(tets(i, j));
1324 elements.emplace_back();
1325 elements.back().ctype = CellType::Triangle;
1332 elements.emplace_back();
1333 elements.back().ctype = CellType::Line;
1340 elements.emplace_back();
1341 elements.back().ctype = CellType::Vertex;
1352 sample.
cell_count = elements.empty() ? tets.rows() :
static_cast<int>(elements.size());
1355 add_output_fields(writer, sample, output_fields);
1370 grid_sample.
time = t;
1371 grid_sample.
dt = dt;
1374 "solution_gradient",
1376 "pressure_gradient",
1380 for (
const OutputField &field : output_fields(grid_sample))
1384 if (field.name ==
"solution")
1386 else if (field.name ==
"solution_gradient")
1388 else if (field.name ==
"pressure")
1390 else if (field.name ==
"pressure_gradient")
1395 if (elements.empty())
1396 writer.write_mesh(path, points, tets, mesh.
is_volume() ? CellType::Tetrahedron : CellType::Triangle);
1398 writer.write_mesh(path, points, elements);
1402 const std::string &export_surface,
1413 const std::vector<basis::ElementBases> &gbases = *space.
geometry_bases;
1415 Eigen::MatrixXd boundary_vis_vertices;
1416 Eigen::MatrixXd boundary_vis_local_vertices;
1417 Eigen::MatrixXi boundary_vis_elements;
1418 Eigen::MatrixXi boundary_vis_elements_ids;
1419 Eigen::MatrixXi boundary_vis_primitive_ids;
1420 Eigen::MatrixXd boundary_vis_normals;
1423 boundary_vis_vertices, boundary_vis_local_vertices, boundary_vis_elements,
1424 boundary_vis_elements_ids, boundary_vis_primitive_ids, boundary_vis_normals);
1426 Eigen::MatrixXd discr, b_sidesets;
1427 discr.resize(boundary_vis_vertices.rows(), 1);
1428 b_sidesets.resize(boundary_vis_vertices.rows(), 1);
1429 b_sidesets.setZero();
1431 for (
int i = 0; i < boundary_vis_vertices.rows(); ++i)
1433 const auto s_id = mesh.
get_boundary_id(boundary_vis_primitive_ids(i));
1436 b_sidesets(i) = s_id;
1439 const int el_index = boundary_vis_elements_ids(i);
1443 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
1445 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
1447 tmpw = std::make_shared<paraviewo::VTUWriter>();
1448 paraviewo::ParaviewWriter &writer = *tmpw;
1451 writer.add_field(
"normals", boundary_vis_normals);
1453 writer.add_field(
"discr", discr);
1455 writer.add_field(
"sidesets", b_sidesets);
1459 sample.
points = boundary_vis_vertices;
1461 sample.
element_ids = boundary_vis_elements_ids.col(0);
1463 sample.
normals = boundary_vis_normals;
1465 sample.
cell_count = boundary_vis_elements.rows();
1468 add_output_fields(writer, sample, output_fields);
1469 writer.write_mesh(export_surface, boundary_vis_vertices, boundary_vis_elements, mesh.
is_volume() ? CellType::Triangle : CellType::Line);
1473 const std::string &export_surface,
1483 const ipc::CollisionMesh &collision_mesh = *space.
collision_mesh;
1485 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
1487 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
1489 tmpw = std::make_shared<paraviewo::VTUWriter>();
1490 paraviewo::ParaviewWriter &writer = *tmpw;
1494 sample.
points = collision_mesh.rest_positions();
1497 collision_mesh.dim() == 3 ? collision_mesh.num_faces() : collision_mesh.num_edges());
1500 add_output_fields(writer, sample, output_fields);
1502 const std::filesystem::path surface_path(export_surface);
1503 const std::string contact_path =
1504 (surface_path.parent_path() / (surface_path.stem().string() +
"_contact" + surface_path.extension().string())).string();
1507 collision_mesh.rest_positions(),
1508 collision_mesh.dim() == 3 ? collision_mesh.faces() : collision_mesh.edges(),
1509 collision_mesh.dim() == 3 ? CellType::Triangle : CellType::Line);
1513 const std::string &name,
1522 static const std::map<int, Eigen::MatrixXd> empty_polys;
1523 static const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> empty_polys_3d;
1525 const std::vector<basis::ElementBases> &gbases = *space.
geometry_bases;
1527 const std::map<int, Eigen::MatrixXd> &polys = space.
polys ? *space.
polys : empty_polys;
1528 const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d = space.
polys_3d ? *space.
polys_3d : empty_polys_3d;
1529 const Eigen::VectorXi output_orders =
1534 Eigen::MatrixXd points, discr, local_points;
1535 Eigen::MatrixXi cells, element_ids, edges;
1536 build_vis_mesh(mesh, output_orders, gbases, polys, polys_3d,
false, points, cells, element_ids, discr, local_points);
1537 if (cells.size() > 0)
1538 igl::edges(cells, edges);
1542 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
1544 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
1546 tmpw = std::make_shared<paraviewo::VTUWriter>();
1547 paraviewo::ParaviewWriter &writer = *tmpw;
1553 if (element_ids.cols() > 0)
1558 add_output_fields(writer, sample, output_fields);
1560 writer.write_mesh(name, points, edges, CellType::Line);
1564 const std::string &path,
1576 Eigen::MatrixXd b_sidesets(dirichlet_nodes_position.size(), 1);
1577 b_sidesets.setZero();
1578 Eigen::MatrixXd points(dirichlet_nodes_position.size(), mesh.
dimension());
1579 std::vector<CellElement> cells(dirichlet_nodes_position.size());
1581 for (
int i = 0; i < dirichlet_nodes_position.size(); ++i)
1583 const int n_id = dirichlet_nodes[i];
1587 b_sidesets(i) = s_id;
1590 points.row(i) = dirichlet_nodes_position[i];
1591 cells[i].vertices.push_back(i);
1592 cells[i].ctype = CellType::Vertex;
1595 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
1597 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
1599 tmpw = std::make_shared<paraviewo::VTUWriter>();
1600 paraviewo::ParaviewWriter &writer = *tmpw;
1603 writer.add_field(
"sidesets", b_sidesets);
1608 sample.
node_ids.resize(dirichlet_nodes.size());
1609 for (
int i = 0; i < dirichlet_nodes.size(); ++i)
1610 sample.
node_ids(i) = dirichlet_nodes[i];
1612 sample.
cell_count =
static_cast<int>(cells.size());
1613 add_output_fields(writer, sample, output_fields);
1614 writer.write_mesh(path, points, cells);
1618 const std::string &name,
1619 const std::function<std::string(
int)> &vtu_names,
1620 int time_steps,
double t0,
double dt,
int skip_frame)
const
1622 paraviewo::PVDWriter::save_pvd(name, vtu_names, time_steps, t0, dt, skip_frame);
1638 const int nx = delta[0] / spacing + 1;
1639 const int ny = delta[1] / spacing + 1;
1640 const int nz = delta.cols() >= 3 ? (delta[2] / spacing + 1) : 1;
1641 const int n = nx * ny * nz;
1645 for (
int i = 0; i < nx; ++i)
1647 const double x = (delta[0] / (nx - 1)) * i + min[0];
1649 for (
int j = 0; j < ny; ++j)
1651 const double y = (delta[1] / (ny - 1)) * j + min[1];
1653 if (delta.cols() <= 2)
1659 for (
int k = 0; k < nz; ++k)
1661 const double z = (delta[2] / (nz - 1)) * k + min[2];
1670 std::vector<std::array<Eigen::Vector3d, 2>> boxes;
1676 const double eps = 1e-6;
1685 const Eigen::Vector3d min(
1690 const Eigen::Vector3d max(
1695 std::vector<unsigned int> candidates;
1697 bvh.intersect_box(min, max, candidates);
1699 for (
const auto cand : candidates)
1703 logger().warn(
"Element {} is not simplex, skipping", cand);
1707 Eigen::MatrixXd coords;
1710 for (
int d = 0; d < coords.size(); ++d)
1712 if (fabs(coords(d)) < 1e-8)
1714 else if (fabs(coords(d) - 1) < 1e-8)
1718 if (coords.array().minCoeff() >= 0 && coords.array().maxCoeff() <= 1)
1730 Eigen::MatrixXd samples_simplex, samples_cube, mapped, p0, p1, p;
1733 average_edge_length = 0;
1734 min_edge_length = std::numeric_limits<double>::max();
1736 if (!use_curved_mesh_size)
1740 min_edge_length = p.rowwise().norm().minCoeff();
1741 average_edge_length = p.rowwise().norm().mean();
1742 mesh_size = p.rowwise().norm().maxCoeff();
1744 logger().info(
"hmin: {}", min_edge_length);
1745 logger().info(
"hmax: {}", mesh_size);
1746 logger().info(
"havg: {}", average_edge_length);
1763 for (
size_t i = 0; i < bases_in.size(); ++i)
1772 bases_in[i].eval_geom_mapping(samples_simplex, mapped);
1777 bases_in[i].eval_geom_mapping(samples_cube, mapped);
1780 for (
int j = 0; j < n_edges; ++j)
1782 double current_edge = 0;
1783 for (
int k = 0; k < n_samples - 1; ++k)
1785 p0 = mapped.row(j * n_samples + k);
1786 p1 = mapped.row(j * n_samples + k + 1);
1789 current_edge += p.norm();
1792 mesh_size = std::max(current_edge, mesh_size);
1793 min_edge_length = std::min(current_edge, min_edge_length);
1794 average_edge_length += current_edge;
1799 average_edge_length /= n;
1801 logger().info(
"hmin: {}", min_edge_length);
1802 logger().info(
"hmax: {}", mesh_size);
1803 logger().info(
"havg: {}", average_edge_length);
1813 using namespace mesh;
1815 logger().info(
"Counting flipped elements...");
1819 for (
size_t i = 0; i < gbases.size(); ++i)
1825 if (!
vals.is_geom_mapping_positive(mesh.
is_volume(), gbases[i]))
1829 static const std::vector<std::string> element_type_names{{
1831 "RegularInteriorCube",
1832 "RegularBoundaryCube",
1833 "SimpleSingularInteriorCube",
1834 "MultiSingularInteriorCube",
1835 "SimpleSingularBoundaryCube",
1837 "MultiSingularBoundaryCube",
1843 log_and_throw_error(
"element {} is flipped, type {}", i, element_type_names[
static_cast<int>(els_tag[i])]);
1858 const std::vector<polyfem::basis::ElementBases> &bases,
1859 const std::vector<polyfem::basis::ElementBases> &gbases,
1863 const Eigen::MatrixXd &sol)
1867 logger().error(
"Build the bases first!");
1870 if (sol.size() <= 0)
1872 logger().error(
"Solve the problem first!");
1882 logger().info(
"Computing errors...");
1885 const int n_el = int(bases.size());
1887 Eigen::MatrixXd v_exact, v_approx;
1888 Eigen::MatrixXd v_exact_grad(0, 0), v_approx_grad;
1898 static const int p = 8;
1903 for (
int e = 0; e < n_el; ++e)
1913 v_approx.resize(
vals.val.rows(), actual_dim);
1916 v_approx_grad.resize(
vals.val.rows(), mesh.
dimension() * actual_dim);
1917 v_approx_grad.setZero();
1919 const int n_loc_bases = int(
vals.basis_values.size());
1921 for (
int i = 0; i < n_loc_bases; ++i)
1923 const auto &
val =
vals.basis_values[i];
1925 for (
size_t ii = 0; ii <
val.global.size(); ++ii)
1927 for (
int d = 0; d < actual_dim; ++d)
1929 v_approx.col(d) +=
val.global[ii].val * sol(
val.global[ii].index * actual_dim + d) *
val.val;
1930 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;
1935 const auto err = problem.
has_exact_sol() ? (v_exact - v_approx).eval().rowwise().norm().eval() : (v_approx).eval().rowwise().norm().eval();
1936 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();
1941 linf_err = std::max(linf_err, err.maxCoeff());
1942 grad_max_err = std::max(linf_err, err_grad.maxCoeff());
1984 l2_err += (err.array() * err.array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
1985 h1_err += (err_grad.array() * err_grad.array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
1986 lp_err += (err.array().pow(p) *
vals.det.array() *
vals.quadrature.weights.array()).sum();
1989 h1_semi_err = sqrt(fabs(h1_err));
1990 h1_err = sqrt(fabs(l2_err) + fabs(h1_err));
1991 l2_err = sqrt(fabs(l2_err));
1993 lp_err = pow(fabs(lp_err), 1. / p);
1998 const double computing_errors_time = timer.getElapsedTime();
1999 logger().info(
" took {}s", computing_errors_time);
2001 logger().info(
"-- L2 error: {}", l2_err);
2002 logger().info(
"-- Lp error: {}", lp_err);
2003 logger().info(
"-- H1 error: {}", h1_err);
2004 logger().info(
"-- H1 semi error: {}", h1_semi_err);
2007 logger().info(
"-- Linf error: {}", linf_err);
2008 logger().info(
"-- grad max error: {}", grad_max_err);
2025 regular_boundary_count = 0;
2026 simple_singular_count = 0;
2027 multi_singular_count = 0;
2029 non_regular_boundary_count = 0;
2030 non_regular_count = 0;
2031 undefined_count = 0;
2032 multi_singular_boundary_count = 0;
2036 for (
size_t i = 0; i < els_tag.size(); ++i)
2042 case ElementType::SIMPLEX:
2045 case ElementType::PRISM:
2048 case ElementType::PYRAMID:
2051 case ElementType::REGULAR_INTERIOR_CUBE:
2054 case ElementType::REGULAR_BOUNDARY_CUBE:
2055 regular_boundary_count++;
2057 case ElementType::SIMPLE_SINGULAR_INTERIOR_CUBE:
2058 simple_singular_count++;
2060 case ElementType::MULTI_SINGULAR_INTERIOR_CUBE:
2061 multi_singular_count++;
2063 case ElementType::SIMPLE_SINGULAR_BOUNDARY_CUBE:
2066 case ElementType::INTERFACE_CUBE:
2067 case ElementType::MULTI_SINGULAR_BOUNDARY_CUBE:
2068 multi_singular_boundary_count++;
2070 case ElementType::BOUNDARY_POLYTOPE:
2071 non_regular_boundary_count++;
2073 case ElementType::INTERIOR_POLYTOPE:
2074 non_regular_count++;
2076 case ElementType::UNDEFINED:
2080 throw std::runtime_error(
"Unknown element type");
2084 logger().info(
"simplex_count: \t{}", simplex_count);
2085 logger().info(
"prism_count: \t{}", prism_count);
2086 logger().info(
"pyramid_count: \t{}", pyramid_count);
2087 logger().info(
"regular_count: \t{}", regular_count);
2088 logger().info(
"regular_boundary_count: \t{}", regular_boundary_count);
2089 logger().info(
"simple_singular_count: \t{}", simple_singular_count);
2090 logger().info(
"multi_singular_count: \t{}", multi_singular_count);
2091 logger().info(
"boundary_count: \t{}", boundary_count);
2092 logger().info(
"multi_singular_boundary_count: \t{}", multi_singular_boundary_count);
2093 logger().info(
"non_regular_count: \t{}", non_regular_count);
2094 logger().info(
"non_regular_boundary_count: \t{}", non_regular_boundary_count);
2095 logger().info(
"undefined_count: \t{}", undefined_count);
2100 const nlohmann::json &args,
2101 const int n_bases,
const int n_pressure_bases,
2102 const Eigen::MatrixXd &sol,
2104 const Eigen::VectorXi &disc_orders,
2105 const Eigen::VectorXi &disc_ordersq,
2108 const std::string &formulation,
2109 const bool isoparametric,
2110 const int sol_at_node_id,
2111 nlohmann::json &j)
const
2116 j[
"geom_order"] = mesh.
orders().size() > 0 ? mesh.
orders().maxCoeff() : 1;
2117 j[
"geom_order_min"] = mesh.
orders().size() > 0 ? mesh.
orders().minCoeff() : 1;
2118 j[
"discr_order_min"] = disc_orders.minCoeff();
2119 j[
"discr_order_max"] = disc_orders.maxCoeff();
2120 j[
"discr_orderq_min"] = disc_ordersq.minCoeff();
2121 j[
"discr_orderq_max"] = disc_ordersq.maxCoeff();
2122 j[
"iso_parametric"] = isoparametric;
2123 j[
"problem"] = problem.
name();
2124 j[
"mat_size"] = mat_size;
2125 j[
"num_bases"] = n_bases;
2126 j[
"num_pressure_bases"] = n_pressure_bases;
2127 j[
"num_non_zero"] = nn_zero;
2128 j[
"num_flipped"] = n_flipped;
2129 j[
"num_dofs"] = num_dofs;
2133 j[
"num_p1"] = (disc_orders.array() == 1).count();
2134 j[
"num_p2"] = (disc_orders.array() == 2).count();
2135 j[
"num_p3"] = (disc_orders.array() == 3).count();
2136 j[
"num_p4"] = (disc_orders.array() == 4).count();
2137 j[
"num_p5"] = (disc_orders.array() == 5).count();
2139 j[
"mesh_size"] = mesh_size;
2140 j[
"max_angle"] = max_angle;
2142 j[
"sigma_max"] = sigma_max;
2143 j[
"sigma_min"] = sigma_min;
2144 j[
"sigma_avg"] = sigma_avg;
2146 j[
"min_edge_length"] = min_edge_length;
2147 j[
"average_edge_length"] = average_edge_length;
2149 j[
"err_l2"] = l2_err;
2150 j[
"err_h1"] = h1_err;
2151 j[
"err_h1_semi"] = h1_semi_err;
2152 j[
"err_linf"] = linf_err;
2153 j[
"err_linf_grad"] = grad_max_err;
2154 j[
"err_lp"] = lp_err;
2156 j[
"spectrum"] = {spectrum(0), spectrum(1), spectrum(2), spectrum(3)};
2157 j[
"spectrum_condest"] = std::abs(spectrum(3)) / std::abs(spectrum(0));
2170 j[
"solver_info"] = solver_info;
2172 j[
"count_simplex"] = simplex_count;
2173 j[
"count_prism"] = prism_count;
2174 j[
"count_pyramid"] = pyramid_count;
2175 j[
"count_regular"] = regular_count;
2176 j[
"count_regular_boundary"] = regular_boundary_count;
2177 j[
"count_simple_singular"] = simple_singular_count;
2178 j[
"count_multi_singular"] = multi_singular_count;
2179 j[
"count_boundary"] = boundary_count;
2180 j[
"count_non_regular_boundary"] = non_regular_boundary_count;
2181 j[
"count_non_regular"] = non_regular_count;
2182 j[
"count_undefined"] = undefined_count;
2183 j[
"count_multi_singular_boundary"] = multi_singular_boundary_count;
2185 j[
"is_simplicial"] = mesh.
n_elements() == simplex_count;
2187 j[
"peak_memory"] =
getPeakRSS() / (1024 * 1024);
2191 std::vector<double> mmin(actual_dim);
2192 std::vector<double> mmax(actual_dim);
2194 for (
int d = 0; d < actual_dim; ++d)
2196 mmin[d] = std::numeric_limits<double>::max();
2197 mmax[d] = -std::numeric_limits<double>::max();
2200 for (
int i = 0; i < sol.size(); i += actual_dim)
2202 for (
int d = 0; d < actual_dim; ++d)
2204 mmin[d] = std::min(mmin[d], sol(i + d));
2205 mmax[d] = std::max(mmax[d], sol(i + d));
2209 std::vector<double> sol_at_node(actual_dim);
2211 if (sol_at_node_id >= 0)
2213 const int node_id = sol_at_node_id;
2215 for (
int d = 0; d < actual_dim; ++d)
2217 sol_at_node[d] = sol(node_id * actual_dim + d);
2221 j[
"sol_at_node"] = sol_at_node;
2222 j[
"sol_min"] = mmin;
2223 j[
"sol_max"] = mmax;
2225#if defined(POLYFEM_WITH_CPP_THREADS)
2227#elif defined(POLYFEM_WITH_TBB)
2230 j[
"num_threads"] = 1;
2233 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
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.
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 & 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