46#include <paraviewo/VTMWriter.hpp>
47#include <paraviewo/PVDWriter.hpp>
49#include <SimpleBVH/BVH.hpp>
51#include <igl/write_triangle_mesh.h>
53#include <igl/facet_adjacency_matrix.h>
54#include <igl/connected_components.h>
68 void compute_traction_forces(
const State &state,
const Eigen::MatrixXd &solution,
const double t, Eigen::MatrixXd &traction_forces,
bool skip_dirichlet =
true)
71 if (!state.
problem->is_scalar())
72 actual_dim = state.
mesh->dimension();
76 const std::vector<basis::ElementBases> &bases = state.
bases;
77 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
79 Eigen::MatrixXd uv, samples, gtmp, rhs_fun, deform_mat, trafo;
80 Eigen::VectorXi global_primitive_ids;
81 Eigen::MatrixXd points, normals;
82 Eigen::VectorXd weights;
85 traction_forces.setZero(state.
n_bases * actual_dim, 1);
89 const int e = lb.element_id();
98 vals.compute(e, state.
mesh->is_volume(), points, bs, gbs);
100 for (
int n = 0; n < normals.rows(); ++n)
102 trafo =
vals.jac_it[n].inverse();
104 if (solution.size() > 0)
106 assert(actual_dim == 2 || actual_dim == 3);
107 deform_mat.resize(actual_dim, actual_dim);
108 deform_mat.setZero();
109 for (
const auto &b :
vals.basis_values)
111 for (
const auto &g : b.global)
113 for (
int d = 0; d < actual_dim; ++d)
115 deform_mat.row(d) += solution(g.index * actual_dim + d) * b.grad.row(n);
123 normals.row(n) = normals.row(n) * trafo.inverse();
124 normals.row(n).normalize();
127 std::vector<assembler::Assembler::NamedMatrix> tensor_flat;
130 for (
long n = 0; n <
vals.basis_values.size(); ++n)
134 const int g_index = v.
global[0].index * actual_dim;
136 for (
int q = 0; q < points.rows(); ++q)
139 assert(tensor_flat[0].first ==
"cauchy_stess");
140 assert(tensor_flat[0].second.row(q).size() == actual_dim * actual_dim);
142 Eigen::MatrixXd stress_tensor =
utils::unflatten(tensor_flat[0].second.row(q), actual_dim);
144 traction_forces.block(g_index, 0, actual_dim, 1) += stress_tensor * normals.row(q).transpose() * v.
val(q) * weights(q);
154 const std::vector<basis::ElementBases> &bases,
155 const std::vector<mesh::LocalBoundary> &total_local_boundary,
156 Eigen::MatrixXd &node_positions,
157 Eigen::MatrixXi &boundary_edges,
158 Eigen::MatrixXi &boundary_triangles,
159 std::vector<Eigen::Triplet<double>> &displacement_map_entries)
163 displacement_map_entries.clear();
169 logger().warn(
"Skipping as the mesh has polygons");
175 node_positions.resize(n_bases + (is_simplicial ? 0 : mesh.
n_faces()), 3);
176 node_positions.setZero();
177 const Mesh3D &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
179 std::vector<std::tuple<int, int, int>> tris;
181 std::vector<bool> visited_node(n_bases,
false);
183 std::stringstream print_warning;
189 for (
int j = 0; j < lb.size(); ++j)
191 const int eid = lb.global_primitive_id(j);
192 const int lid = lb[j];
193 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh3d);
195 if (mesh.
is_cube(lb.element_id()))
197 assert(!is_simplicial);
199 std::vector<int> loc_nodes;
202 for (
long n = 0; n < nodes.size(); ++n)
204 auto &bs = b.bases[nodes(n)];
205 const auto &glob = bs.global();
206 if (glob.size() != 1)
209 int gindex = glob.front().index;
210 node_positions.row(gindex) = glob.front().node;
211 bary += glob.front().node;
212 loc_nodes.push_back(gindex);
215 if (loc_nodes.size() != 4)
217 logger().trace(
"skipping element {} since it is not Q1", eid);
223 const int new_node = n_bases + eid;
224 node_positions.row(new_node) = bary;
225 tris.emplace_back(loc_nodes[1], loc_nodes[0], new_node);
226 tris.emplace_back(loc_nodes[2], loc_nodes[1], new_node);
227 tris.emplace_back(loc_nodes[3], loc_nodes[2], new_node);
228 tris.emplace_back(loc_nodes[0], loc_nodes[3], new_node);
230 for (
int q = 0; q < 4; ++q)
232 if (!visited_node[loc_nodes[q]])
233 displacement_map_entries.emplace_back(loc_nodes[q], loc_nodes[q], 1);
235 visited_node[loc_nodes[q]] =
true;
236 displacement_map_entries.emplace_back(new_node, loc_nodes[q], 0.25);
241 else if (mesh.
is_prism(lb.element_id()))
243 assert(!is_simplicial);
245 std::vector<int> loc_nodes;
248 for (
long n = 0; n < nodes.size(); ++n)
250 auto &bs = b.bases[nodes(n)];
251 const auto &glob = bs.global();
252 if (glob.size() != 1)
255 int gindex = glob.front().index;
256 node_positions.row(gindex) = glob.front().node;
257 bary += glob.front().node;
258 loc_nodes.push_back(gindex);
261 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
262 for (
int k = 0; k < loc_nodes.size(); ++k)
264 if (!visited_node[loc_nodes[k]])
265 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
267 visited_node[loc_nodes[k]] =
true;
271 if (loc_nodes.size() == 3)
273 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
275 update_mapping(loc_nodes);
277 else if (loc_nodes.size() == 6)
279 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
280 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
281 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
282 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
284 update_mapping(loc_nodes);
286 else if (loc_nodes.size() == 10)
288 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
289 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
290 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
291 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
292 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
293 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
294 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
295 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
296 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
297 update_mapping(loc_nodes);
299 else if (loc_nodes.size() == 15)
301 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[11]);
302 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[12]);
303 tris.emplace_back(loc_nodes[3], loc_nodes[12], loc_nodes[11]);
304 tris.emplace_back(loc_nodes[12], loc_nodes[10], loc_nodes[11]);
305 tris.emplace_back(loc_nodes[4], loc_nodes[5], loc_nodes[13]);
306 tris.emplace_back(loc_nodes[4], loc_nodes[13], loc_nodes[12]);
307 tris.emplace_back(loc_nodes[12], loc_nodes[13], loc_nodes[14]);
308 tris.emplace_back(loc_nodes[12], loc_nodes[14], loc_nodes[10]);
309 tris.emplace_back(loc_nodes[14], loc_nodes[9], loc_nodes[10]);
310 tris.emplace_back(loc_nodes[5], loc_nodes[1], loc_nodes[6]);
311 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[13]);
312 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[13]);
313 tris.emplace_back(loc_nodes[13], loc_nodes[7], loc_nodes[14]);
314 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[14]);
315 tris.emplace_back(loc_nodes[14], loc_nodes[8], loc_nodes[9]);
316 tris.emplace_back(loc_nodes[8], loc_nodes[2], loc_nodes[9]);
317 update_mapping(loc_nodes);
319 else if (loc_nodes.size() == 4)
323 const int new_node = n_bases + eid;
324 node_positions.row(new_node) = bary;
325 tris.emplace_back(loc_nodes[1], loc_nodes[0], new_node);
326 tris.emplace_back(loc_nodes[2], loc_nodes[1], new_node);
327 tris.emplace_back(loc_nodes[3], loc_nodes[2], new_node);
328 tris.emplace_back(loc_nodes[0], loc_nodes[3], new_node);
330 update_mapping(loc_nodes);
334 logger().trace(
"skipping element {} since it is not linear, it has {} nodes", eid, loc_nodes.size());
343 assert(!is_simplicial);
345 std::vector<int> loc_nodes;
347 for (
long n = 0; n < nodes.size(); ++n)
349 auto &bs = b.bases[nodes(n)];
350 const auto &glob = bs.global();
351 if (glob.size() != 1)
354 int gindex = glob.front().index;
355 node_positions.row(gindex) = glob.front().node;
356 loc_nodes.push_back(gindex);
359 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
360 for (
int k = 0; k < loc_nodes.size(); ++k)
362 if (!visited_node[loc_nodes[k]])
363 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
365 visited_node[loc_nodes[k]] =
true;
369 if (loc_nodes.size() == 3)
371 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
372 update_mapping(loc_nodes);
374 else if (loc_nodes.size() == 4)
376 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
377 tris.emplace_back(loc_nodes[0], loc_nodes[2], loc_nodes[3]);
378 update_mapping(loc_nodes);
382 logger().trace(
"skipping element {} since it is not linear, it has {} nodes", eid, loc_nodes.size());
391 logger().trace(
"skipping element {} since it is not a simplex or hex", eid);
397 std::vector<int> loc_nodes;
399 bool is_follower =
false;
402 for (
long n = 0; n < nodes.size(); ++n)
404 auto &bs = b.bases[nodes(n)];
405 const auto &glob = bs.global();
406 if (glob.size() != 1)
417 for (
long n = 0; n < nodes.size(); ++n)
420 const std::vector<basis::Local2Global> &glob = bs.
global();
421 if (glob.size() != 1)
424 int gindex = glob.front().index;
425 node_positions.row(gindex) = glob.front().node;
426 loc_nodes.push_back(gindex);
429 if (loc_nodes.size() == 3)
431 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
433 else if (loc_nodes.size() == 6)
435 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
436 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
437 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
438 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
440 else if (loc_nodes.size() == 10)
442 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
443 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
444 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
445 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
446 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
447 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
448 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
449 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
450 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
452 else if (loc_nodes.size() == 15)
454 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[11]);
455 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[12]);
456 tris.emplace_back(loc_nodes[3], loc_nodes[12], loc_nodes[11]);
457 tris.emplace_back(loc_nodes[12], loc_nodes[10], loc_nodes[11]);
458 tris.emplace_back(loc_nodes[4], loc_nodes[5], loc_nodes[13]);
459 tris.emplace_back(loc_nodes[4], loc_nodes[13], loc_nodes[12]);
460 tris.emplace_back(loc_nodes[12], loc_nodes[13], loc_nodes[14]);
461 tris.emplace_back(loc_nodes[12], loc_nodes[14], loc_nodes[10]);
462 tris.emplace_back(loc_nodes[14], loc_nodes[9], loc_nodes[10]);
463 tris.emplace_back(loc_nodes[5], loc_nodes[1], loc_nodes[6]);
464 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[13]);
465 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[13]);
466 tris.emplace_back(loc_nodes[13], loc_nodes[7], loc_nodes[14]);
467 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[14]);
468 tris.emplace_back(loc_nodes[14], loc_nodes[8], loc_nodes[9]);
469 tris.emplace_back(loc_nodes[8], loc_nodes[2], loc_nodes[9]);
473 print_warning << loc_nodes.size() <<
" ";
479 for (
int k = 0; k < loc_nodes.size(); ++k)
481 if (!visited_node[loc_nodes[k]])
482 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
484 visited_node[loc_nodes[k]] =
true;
490 if (print_warning.str().size() > 0)
491 logger().warn(
"Skipping faces as theys have {} nodes, boundary export supported up to p4", print_warning.str());
493 boundary_triangles.resize(tris.size(), 3);
494 for (
int i = 0; i < tris.size(); ++i)
496 boundary_triangles.row(i) << std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]);
499 if (boundary_triangles.rows() > 0)
501 igl::edges(boundary_triangles, boundary_edges);
506 node_positions.resize(n_bases, 2);
507 node_positions.setZero();
508 const Mesh2D &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
510 std::vector<std::pair<int, int>> edges;
516 for (
int j = 0; j < lb.size(); ++j)
518 const int eid = lb.global_primitive_id(j);
519 const int lid = lb[j];
520 const Eigen::VectorXi nodes = b.local_nodes_for_primitive(eid, mesh2d);
524 for (
long n = 0; n < nodes.size(); ++n)
527 const std::vector<basis::Local2Global> &glob = bs.
global();
528 if (glob.size() != 1)
531 int gindex = glob.front().index;
532 node_positions.row(gindex) = glob.front().node.head<2>();
535 edges.emplace_back(prev_node, gindex);
542 boundary_triangles.resize(0, 0);
543 boundary_edges.resize(edges.size(), 2);
544 for (
int i = 0; i < edges.size(); ++i)
546 boundary_edges.row(i) << edges[i].first, edges[i].second;
553 const std::vector<basis::ElementBases> &bases,
554 const std::vector<basis::ElementBases> &gbases,
555 const std::vector<mesh::LocalBoundary> &total_local_boundary,
556 const Eigen::MatrixXd &solution,
557 const int problem_dim,
558 Eigen::MatrixXd &boundary_vis_vertices,
559 Eigen::MatrixXd &boundary_vis_local_vertices,
560 Eigen::MatrixXi &boundary_vis_elements,
561 Eigen::MatrixXi &boundary_vis_elements_ids,
562 Eigen::MatrixXi &boundary_vis_primitive_ids,
563 Eigen::MatrixXd &boundary_vis_normals,
564 Eigen::MatrixXd &displaced_boundary_vis_normals)
const
568 std::vector<Eigen::MatrixXd> lv, vertices, allnormals, displaced_allnormals;
569 std::vector<int> el_ids, global_primitive_ids;
570 Eigen::MatrixXd uv, local_pts, tmp_n, normals, displaced_normals, trafo, deform_mat;
576 std::vector<std::pair<int, int>> edges;
577 std::vector<std::tuple<int, int, int>> tris;
579 for (
auto it = total_local_boundary.begin(); it != total_local_boundary.end(); ++it)
581 const auto &lb = *it;
582 const auto &gbs = gbases[lb.element_id()];
583 const auto &bs = bases[lb.element_id()];
585 for (
int k = 0; k < lb.size(); ++k)
589 case BoundaryType::TRI_LINE:
593 case BoundaryType::QUAD_LINE:
597 case BoundaryType::QUAD:
601 case BoundaryType::TRI:
605 case BoundaryType::PRISM:
609 case BoundaryType::PYRAMID:
613 case BoundaryType::POLYGON:
617 case BoundaryType::POLYHEDRON:
620 case BoundaryType::INVALID:
627 vertices.emplace_back();
628 lv.emplace_back(local_pts);
629 el_ids.push_back(lb.element_id());
630 global_primitive_ids.push_back(lb.global_primitive_id(k));
631 gbs.eval_geom_mapping(local_pts, vertices.back());
632 vals.compute(lb.element_id(), mesh.
is_volume(), local_pts, bs, gbs);
633 const int tris_start = tris.size();
637 const bool prism_quad = lb.type() == BoundaryType::PRISM && lb[k] >= 2;
638 const bool prism_tri = lb.type() == BoundaryType::PRISM && lb[k] < 2;
640 const bool pyramid_quad = lb.type() == BoundaryType::PYRAMID && lb[k] == 0;
641 const bool pyramid_tri = lb.type() == BoundaryType::PYRAMID && lb[k] > 0;
643 if (lb.type() == BoundaryType::QUAD || prism_quad || pyramid_quad)
645 const auto map = [n_samples, size](
int i,
int j) {
return j * n_samples + i + size; };
647 for (
int j = 0; j < n_samples - 1; ++j)
649 for (
int i = 0; i < n_samples - 1; ++i)
651 tris.emplace_back(map(i, j), map(i + 1, j), map(i, j + 1));
652 tris.emplace_back(map(i + 1, j + 1), map(i, j + 1), map(i + 1, j));
656 else if (lb.type() == BoundaryType::TRI || prism_tri || pyramid_tri)
659 std::vector<int> mapp(n_samples * n_samples, -1);
660 for (
int j = 0; j < n_samples; ++j)
662 for (
int i = 0; i < n_samples - j; ++i)
664 mapp[j * n_samples + i] = index;
668 const auto map = [mapp, n_samples](
int i,
int j) {
669 if (j * n_samples + i >= mapp.size())
671 return mapp[j * n_samples + i];
674 for (
int j = 0; j < n_samples - 1; ++j)
676 for (
int i = 0; i < n_samples - j; ++i)
678 if (map(i, j) >= 0 && map(i + 1, j) >= 0 && map(i, j + 1) >= 0)
679 tris.emplace_back(map(i, j) + size, map(i + 1, j) + size, map(i, j + 1) + size);
681 if (map(i + 1, j + 1) >= 0 && map(i, j + 1) >= 0 && map(i + 1, j) >= 0)
682 tris.emplace_back(map(i + 1, j + 1) + size, map(i, j + 1) + size, map(i + 1, j) + size);
693 for (
int i = 0; i < vertices.back().rows() - 1; ++i)
694 edges.emplace_back(i + size, i + size + 1);
697 normals.resize(
vals.jac_it.size(), tmp_n.cols());
698 displaced_normals.resize(
vals.jac_it.size(), tmp_n.cols());
700 for (
int n = 0; n <
vals.jac_it.size(); ++n)
702 trafo =
vals.jac_it[n].inverse();
704 if (problem_dim == 2 || problem_dim == 3)
707 if (solution.size() > 0)
709 deform_mat.resize(problem_dim, problem_dim);
710 deform_mat.setZero();
711 for (
const auto &b :
vals.basis_values)
712 for (
const auto &g : b.global)
713 for (
int d = 0; d < problem_dim; ++d)
714 deform_mat.row(d) += solution(g.index * problem_dim + d) * b.grad.row(n);
720 normals.row(n) = tmp_n *
vals.jac_it[n];
721 normals.row(n).normalize();
723 displaced_normals.row(n) = tmp_n * trafo.inverse();
724 displaced_normals.row(n).normalize();
727 allnormals.push_back(normals);
728 displaced_allnormals.push_back(displaced_normals);
731 for (
int n = 0; n <
vals.jac_it.size(); ++n)
733 tmp_n += normals.row(n);
738 Eigen::Vector3d e1 = vertices.back().row(std::get<1>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
739 Eigen::Vector3d e2 = vertices.back().row(std::get<2>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
741 Eigen::Vector3d n = e1.cross(e2);
742 Eigen::Vector3d nn = tmp_n.transpose();
746 for (
int i = tris_start; i < tris.size(); ++i)
748 tris[i] = std::tuple<int, int, int>(std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]));
753 size += vertices.back().rows();
757 boundary_vis_vertices.resize(size, vertices.front().cols());
758 boundary_vis_local_vertices.resize(size, vertices.front().cols());
759 boundary_vis_elements_ids.resize(size, 1);
760 boundary_vis_primitive_ids.resize(size, 1);
761 boundary_vis_normals.resize(size, vertices.front().cols());
762 displaced_boundary_vis_normals.resize(size, vertices.front().cols());
765 boundary_vis_elements.resize(tris.size(), 3);
767 boundary_vis_elements.resize(edges.size(), 2);
771 for (
const auto &v : vertices)
773 boundary_vis_vertices.block(index, 0, v.rows(), v.cols()) = v;
774 boundary_vis_local_vertices.block(index, 0, v.rows(), v.cols()) = lv[ii];
775 boundary_vis_elements_ids.block(index, 0, v.rows(), 1).setConstant(el_ids[ii]);
776 boundary_vis_primitive_ids.block(index, 0, v.rows(), 1).setConstant(global_primitive_ids[ii++]);
781 for (
const auto &n : allnormals)
783 boundary_vis_normals.block(index, 0, n.rows(), n.cols()) = n;
788 for (
const auto &n : displaced_allnormals)
790 displaced_boundary_vis_normals.block(index, 0, n.rows(), n.cols()) = n;
797 for (
const auto &t : tris)
799 boundary_vis_elements.row(index) << std::get<0>(t), std::get<1>(t), std::get<2>(t);
805 for (
const auto &e : edges)
807 boundary_vis_elements.row(index) << e.first, e.second;
815 const Eigen::VectorXi &disc_orders,
816 const std::vector<basis::ElementBases> &gbases,
817 const std::map<int, Eigen::MatrixXd> &polys,
818 const std::map<
int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d,
819 const bool boundary_only,
820 Eigen::MatrixXd &points,
821 Eigen::MatrixXi &tets,
822 Eigen::MatrixXi &el_id,
823 Eigen::MatrixXd &discr)
const
827 const auto ¤t_bases = gbases;
828 int tet_total_size = 0;
829 int pts_total_size = 0;
831 Eigen::MatrixXd vis_pts_poly;
832 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
834 for (
size_t i = 0; i < current_bases.size(); ++i)
836 const auto &bs = current_bases[i];
844 pts_total_size += sampler.simplex_points().rows();
848 tet_total_size += sampler.cube_volume().rows();
849 pts_total_size += sampler.cube_points().rows();
853 tet_total_size += sampler.prism_volume().rows();
854 pts_total_size += sampler.prism_points().rows();
858 tet_total_size += sampler.pyramid_volume().rows();
859 pts_total_size += sampler.pyramid_points().rows();
865 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
867 tet_total_size += vis_faces_poly.rows();
868 pts_total_size += vis_pts_poly.rows();
872 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
874 tet_total_size += vis_faces_poly.rows();
875 pts_total_size += vis_pts_poly.rows();
880 points.resize(pts_total_size, mesh.
dimension());
881 tets.resize(tet_total_size, mesh.
is_volume() ? 4 : 3);
883 el_id.resize(pts_total_size, 1);
884 discr.resize(pts_total_size, 1);
886 Eigen::MatrixXd mapped, tmp;
887 int tet_index = 0, pts_index = 0;
889 for (
size_t i = 0; i < current_bases.size(); ++i)
891 const auto &bs = current_bases[i];
898 bs.eval_geom_mapping(sampler.simplex_points(), mapped);
900 tets.block(tet_index, 0, sampler.simplex_volume().rows(), tets.cols()) = sampler.simplex_volume().array() + pts_index;
901 tet_index += sampler.simplex_volume().rows();
903 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
904 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
905 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
906 pts_index += mapped.rows();
910 bs.eval_geom_mapping(sampler.cube_points(), mapped);
912 tets.block(tet_index, 0, sampler.cube_volume().rows(), tets.cols()) = sampler.cube_volume().array() + pts_index;
913 tet_index += sampler.cube_volume().rows();
915 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
916 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
917 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
918 pts_index += mapped.rows();
922 bs.eval_geom_mapping(sampler.prism_points(), mapped);
924 tets.block(tet_index, 0, sampler.prism_volume().rows(), tets.cols()) = sampler.prism_volume().array() + pts_index;
925 tet_index += sampler.prism_volume().rows();
927 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
928 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
929 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
930 pts_index += mapped.rows();
934 bs.eval_geom_mapping(sampler.pyramid_points(), mapped);
936 tets.block(tet_index, 0, sampler.pyramid_volume().rows(), tets.cols()) = sampler.pyramid_volume().array() + pts_index;
937 tet_index += sampler.pyramid_volume().rows();
939 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
940 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
941 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
942 pts_index += mapped.rows();
948 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
949 bs.eval_geom_mapping(vis_pts_poly, mapped);
951 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
952 tet_index += vis_faces_poly.rows();
954 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
955 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
956 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
957 pts_index += mapped.rows();
961 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
962 bs.eval_geom_mapping(vis_pts_poly, mapped);
964 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
965 tet_index += vis_faces_poly.rows();
967 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
968 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
969 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
970 pts_index += mapped.rows();
975 assert(pts_index == points.rows());
976 assert(tet_index == tets.rows());
981 const Eigen::VectorXi &disc_orders,
982 const Eigen::VectorXi &disc_ordersq,
983 const std::vector<basis::ElementBases> &bases,
984 Eigen::MatrixXd &points,
985 std::vector<CellElement> &elements,
986 Eigen::MatrixXi &el_id,
987 Eigen::MatrixXd &discr)
const
1001 std::vector<RowVectorNd> nodes;
1002 int pts_total_size = 0;
1003 elements.resize(bases.size());
1004 Eigen::MatrixXd ref_pts;
1006 for (
size_t i = 0; i < bases.size(); ++i)
1008 const auto &bs = bases[i];
1017 int max_order = std::max(disc_orders(i), disc_ordersq(i));
1035 const int n_v =
static_cast<const mesh::Mesh2D &
>(mesh).n_face_vertices(i);
1036 ref_pts.resize(n_v, 2);
1040 pts_total_size += ref_pts.rows();
1045 el_id.resize(pts_total_size, 1);
1046 discr.resize(pts_total_size, 1);
1048 Eigen::MatrixXd mapped;
1051 std::string error_msg =
"";
1053 for (
size_t i = 0; i < bases.size(); ++i)
1055 const auto &bs = bases[i];
1064 int max_order = std::max(disc_orders(i), disc_ordersq(i));
1084 bs.eval_geom_mapping(ref_pts, mapped);
1086 for (
int j = 0; j < mapped.rows(); ++j)
1088 points.row(pts_index) = mapped.row(j);
1089 el_id(pts_index) = i;
1090 discr(pts_index) = disc_orders(i);
1091 elements[i].vertices.push_back(pts_index);
1100 const int n_nodes = elements[i].vertices.size();
1101 if (disc_orders(i) >= 3)
1103 std::swap(elements[i].vertices[16], elements[i].vertices[17]);
1104 std::swap(elements[i].vertices[17], elements[i].vertices[18]);
1105 std::swap(elements[i].vertices[18], elements[i].vertices[19]);
1107 if (disc_orders(i) > 4)
1108 error_msg =
"Saving high-order meshes not implemented for P5+ elements!";
1112 if (disc_orders(i) == 4)
1114 const int n_nodes = elements[i].vertices.size();
1115 std::swap(elements[i].vertices[n_nodes - 1], elements[i].vertices[n_nodes - 2]);
1117 if (disc_orders(i) > 4)
1118 error_msg =
"Saving high-order meshes not implemented for P5+ elements!";
1123 const int n_nodes = elements[i].vertices.size();
1124 if (disc_orders(i) == 2)
1126 std::swap(elements[i].vertices[12], elements[i].vertices[16]);
1127 std::swap(elements[i].vertices[13], elements[i].vertices[17]);
1128 std::swap(elements[i].vertices[14], elements[i].vertices[18]);
1129 std::swap(elements[i].vertices[15], elements[i].vertices[19]);
1130 std::swap(elements[i].vertices[18], elements[i].vertices[19]);
1145 if (disc_orders(i) > 2)
1146 error_msg =
"Saving high-order meshes not implemented for P2+ elements!";
1148 else if (disc_orders(i) > 1)
1149 error_msg =
"Saving high-order meshes not implemented for Q2+ elements!";
1152 if (!error_msg.empty())
1153 logger().warn(error_msg);
1155 for (
size_t i = 0; i < bases.size(); ++i)
1160 const auto &mesh2d =
static_cast<const mesh::Mesh2D &
>(mesh);
1163 for (
int j = 0; j < n_v; ++j)
1165 points.row(pts_index) = mesh2d.point(mesh2d.face_vertex(i, j));
1166 el_id(pts_index) = i;
1167 discr(pts_index) = disc_orders(i);
1168 elements[i].vertices.push_back(pts_index);
1174 for (
size_t i = 0; i < bases.size(); ++i)
1178 if (elements[i].
vertices.size() == 1)
1179 elements[i].ctype = CellType::Vertex;
1180 else if (elements[i].
vertices.size() == 2)
1181 elements[i].ctype = CellType::Line;
1183 elements[i].ctype = CellType::Triangle;
1185 elements[i].ctype = CellType::Quadrilateral;
1187 elements[i].ctype = CellType::Polygon;
1192 elements[i].ctype = CellType::Tetrahedron;
1194 elements[i].ctype = CellType::Hexahedron;
1196 elements[i].ctype = CellType::Wedge;
1198 elements[i].ctype = CellType::Pyramid;
1202 assert(pts_index ==
points.rows());
1207 const Eigen::MatrixXd &sol,
1208 const Eigen::MatrixXd &pressure,
1209 const bool is_time_dependent,
1210 const double tend_in,
1213 const std::string &vis_mesh_path,
1214 const std::string &nodes_path,
1215 const std::string &solution_path,
1216 const std::string &stress_path,
1217 const std::string &mises_path,
1218 const bool is_contact_enabled)
const
1222 logger().error(
"Load the mesh first!");
1225 const int n_bases = state.
n_bases;
1226 const std::vector<basis::ElementBases> &bases = state.
bases;
1227 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
1230 const Eigen::MatrixXd &rhs = state.
rhs;
1235 logger().error(
"Build the bases first!");
1243 if (sol.size() <= 0)
1245 logger().error(
"Solve the problem first!");
1249 if (!solution_path.empty())
1251 logger().info(
"Saving solution to {}", solution_path);
1253 std::ofstream out(solution_path);
1255 out << std::scientific;
1259 Eigen::VectorXi reordering(n_bases);
1260 reordering.setConstant(-1);
1262 for (
int i = 0; i < in_node_to_node.size(); ++i)
1264 reordering[in_node_to_node[i]] = i;
1267 Eigen::MatrixXd tmp(tmp_sol.rows(), tmp_sol.cols());
1269 for (
int i = 0; i < reordering.size(); ++i)
1271 if (reordering[i] < 0)
1274 tmp.row(reordering[i]) = tmp_sol.row(i);
1277 for (
int i = 0; i < tmp.rows(); ++i)
1279 for (
int j = 0; j < tmp.cols(); ++j)
1280 out << tmp(i, j) <<
" ";
1286 out << sol << std::endl;
1290 double tend = tend_in;
1294 if (!vis_mesh_path.empty() && !is_time_dependent)
1297 vis_mesh_path, state, sol, pressure,
1299 is_contact_enabled);
1301 if (!nodes_path.empty())
1303 Eigen::MatrixXd nodes(n_bases, mesh.
dimension());
1309 for (
size_t ii = 0; ii < b.global().size(); ++ii)
1311 const auto &lg = b.global()[ii];
1312 nodes.row(lg.index) = lg.node;
1316 std::ofstream out(nodes_path);
1321 if (!stress_path.empty())
1323 Eigen::MatrixXd result;
1324 Eigen::VectorXd mises;
1328 sol, tend, result, mises);
1329 std::ofstream out(stress_path);
1333 if (!mises_path.empty())
1335 Eigen::MatrixXd result;
1336 Eigen::VectorXd mises;
1340 sol, tend, result, mises);
1341 std::ofstream out(mises_path);
1354 fields = args[
"output"][
"paraview"][
"fields"];
1356 volume = args[
"output"][
"paraview"][
"volume"];
1357 surface = args[
"output"][
"paraview"][
"surface"];
1358 wire = args[
"output"][
"paraview"][
"wireframe"];
1359 points = args[
"output"][
"paraview"][
"points"];
1360 contact_forces = args[
"output"][
"paraview"][
"options"][
"contact_forces"] && !is_problem_scalar;
1361 friction_forces = args[
"output"][
"paraview"][
"options"][
"friction_forces"] && !is_problem_scalar;
1362 normal_adhesion_forces = args[
"output"][
"paraview"][
"options"][
"normal_adhesion_forces"] && !is_problem_scalar;
1363 tangential_adhesion_forces = args[
"output"][
"paraview"][
"options"][
"tangential_adhesion_forces"] && !is_problem_scalar;
1365 if (args[
"output"][
"paraview"][
"options"][
"force_high_order"])
1366 use_sampler =
false;
1368 use_sampler = !(is_mesh_linear && args[
"output"][
"paraview"][
"high_order_mesh"]);
1369 boundary_only = use_sampler && args[
"output"][
"advanced"][
"vis_boundary_only"];
1370 material_params = args[
"output"][
"paraview"][
"options"][
"material"];
1371 body_ids = args[
"output"][
"paraview"][
"options"][
"body_ids"];
1372 sol_on_grid = args[
"output"][
"advanced"][
"sol_on_grid"] > 0;
1373 velocity = args[
"output"][
"paraview"][
"options"][
"velocity"];
1374 acceleration = args[
"output"][
"paraview"][
"options"][
"acceleration"];
1375 forces = args[
"output"][
"paraview"][
"options"][
"forces"] && !is_problem_scalar;
1376 jacobian_validity = args[
"output"][
"paraview"][
"options"][
"jacobian_validity"] && !is_problem_scalar;
1378 scalar_values = args[
"output"][
"paraview"][
"options"][
"scalar_values"];
1379 tensor_values = args[
"output"][
"paraview"][
"options"][
"tensor_values"] && !is_problem_scalar;
1380 discretization_order = args[
"output"][
"paraview"][
"options"][
"discretization_order"];
1381 nodes = args[
"output"][
"paraview"][
"options"][
"nodes"] && !is_problem_scalar;
1383 use_spline = args[
"space"][
"basis_type"] ==
"Spline";
1385 reorder_output = args[
"output"][
"data"][
"advanced"][
"reorder_nodes"];
1387 use_hdf5 = args[
"output"][
"paraview"][
"options"][
"use_hdf5"];
1391 const std::string &path,
1393 const Eigen::MatrixXd &sol,
1394 const Eigen::MatrixXd &pressure,
1398 const bool is_contact_enabled)
const
1402 logger().error(
"Load the mesh first!");
1406 const Eigen::MatrixXd &rhs = state.
rhs;
1410 logger().error(
"Build the bases first!");
1418 if (sol.size() <= 0)
1420 logger().error(
"Solve the problem first!");
1426 logger().info(
"Saving vtu to {}; volume={}, surface={}, contact={}, points={}, wireframe={}",
1429 const std::filesystem::path fs_path(path);
1430 const std::string path_stem = fs_path.stem().string();
1431 const std::string base_path = (fs_path.parent_path() / path_stem).
string();
1441 is_contact_enabled);
1447 is_contact_enabled);
1460 paraviewo::VTMWriter vtm(t);
1462 vtm.add_dataset(
"Volume",
"data", path_stem + opts.
file_extension());
1464 vtm.add_dataset(
"Surface",
"data", path_stem +
"_surf" + opts.
file_extension());
1466 vtm.add_dataset(
"Contact",
"data", path_stem +
"_surf_contact" + opts.
file_extension());
1468 vtm.add_dataset(
"Wireframe",
"data", path_stem +
"_wire" + opts.
file_extension());
1470 vtm.add_dataset(
"Points",
"data", path_stem +
"_points" + opts.
file_extension());
1471 vtm.save(base_path +
".vtm");
1475 const std::string &path,
1477 const Eigen::MatrixXd &sol,
1478 const Eigen::MatrixXd &pressure,
1483 const Eigen::VectorXi &disc_orders = state.
disc_orders;
1484 const Eigen::VectorXi &disc_ordersq = state.
disc_ordersq;
1486 const std::vector<basis::ElementBases> &bases = state.
bases;
1487 const std::vector<basis::ElementBases> &pressure_bases = state.
pressure_bases;
1488 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
1489 const std::map<int, Eigen::MatrixXd> &polys = state.
polys;
1490 const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d = state.
polys_3d;
1497 Eigen::MatrixXd points;
1498 Eigen::MatrixXi tets;
1499 Eigen::MatrixXi el_id;
1500 Eigen::MatrixXd discr;
1501 std::vector<CellElement> elements;
1506 points, tets, el_id, discr);
1509 points, elements, el_id, discr);
1511 Eigen::MatrixXd fun, exact_fun, err, node_fun;
1516 Eigen::MatrixXd tmp, tmp_grad;
1517 Eigen::MatrixXd tmp_p, tmp_grad_p;
1519 res.setConstant(std::numeric_limits<double>::quiet_NaN());
1521 res_grad.setConstant(std::numeric_limits<double>::quiet_NaN());
1524 res_p.setConstant(std::numeric_limits<double>::quiet_NaN());
1526 res_grad_p.setConstant(std::numeric_limits<double>::quiet_NaN());
1535 Eigen::MatrixXd pt(1, bc.cols() - 1);
1536 for (
int d = 1; d < bc.cols(); ++d)
1539 mesh, problem.
is_scalar(), bases, gbases,
1540 el_id, pt, sol, tmp, tmp_grad);
1543 res_grad.row(i) = tmp_grad;
1548 mesh, 1, pressure_bases, gbases,
1549 el_id, pt, pressure, tmp_p, tmp_grad_p);
1550 res_p.row(i) = tmp_p;
1551 res_grad_p.row(i) = tmp_grad_p;
1555 std::ofstream os(path +
"_sol.txt");
1558 std::ofstream osg(path +
"_grad.txt");
1561 std::ofstream osgg(path +
"_grid.txt");
1566 std::ofstream osp(path +
"_p_sol.txt");
1569 std::ofstream osgp(path +
"_p_grad.txt");
1574 Eigen::Vector<bool, -1> validity;
1582 mesh, problem.
is_scalar(), bases, disc_orders, disc_ordersq,
1587 Eigen::MatrixXd tmp = Eigen::VectorXd::LinSpaced(sol.size(), 0, sol.size() - 1);
1590 mesh, problem.
is_scalar(), bases, disc_orders, disc_ordersq,
1597 fun.conservativeResize(fun.rows() + obstacle.
n_vertices(), fun.cols());
1598 node_fun.conservativeResize(node_fun.rows() + obstacle.
n_vertices(), node_fun.cols());
1599 node_fun.bottomRows(obstacle.
n_vertices()).setZero();
1607 problem.
exact(points, t, exact_fun);
1608 err = (fun - exact_fun).eval().rowwise().norm();
1612 exact_fun.conservativeResize(exact_fun.rows() + obstacle.
n_vertices(), exact_fun.cols());
1616 err.conservativeResize(err.rows() + obstacle.
n_vertices(), 1);
1617 err.bottomRows(obstacle.
n_vertices()).setZero();
1621 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
1623 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
1625 tmpw = std::make_shared<paraviewo::VTUWriter>();
1626 paraviewo::ParaviewWriter &writer = *tmpw;
1629 writer.add_field(
"validity", validity.cast<
double>());
1632 writer.add_field(
"nodes", node_fun);
1636 bool is_time_integrator_valid = time_integrator !=
nullptr;
1640 const Eigen::VectorXd velocity =
1641 is_time_integrator_valid ? (time_integrator->v_prev()) : Eigen::VectorXd::Zero(sol.size());
1647 const Eigen::VectorXd acceleration =
1648 is_time_integrator_valid ? (time_integrator->a_prev()) : Eigen::VectorXd::Zero(sol.size());
1662 if (form ==
nullptr)
1665 Eigen::VectorXd force;
1666 if (form->enabled())
1668 form->first_derivative(sol, force);
1673 force.setZero(sol.size());
1683 Eigen::MatrixXd interp_p;
1691 interp_p.conservativeResize(interp_p.size() + obstacle.
n_vertices(), 1);
1692 interp_p.bottomRows(obstacle.
n_vertices()).setZero();
1695 writer.add_field(
"pressure", interp_p);
1700 discr.conservativeResize(discr.size() + obstacle.
n_vertices(), 1);
1701 discr.bottomRows(obstacle.
n_vertices()).setZero();
1705 writer.add_field(
"discr", discr);
1710 writer.add_field(
"exact", exact_fun);
1712 writer.add_field(
"error", err);
1717 std::vector<assembler::Assembler::NamedMatrix>
vals, tvals;
1719 mesh, problem.
is_scalar(), bases, gbases,
1724 for (
auto &[_, v] :
vals)
1729 for (
const auto &[name, v] :
vals)
1732 writer.add_field(name, v);
1739 mesh, problem.
is_scalar(), bases, gbases, disc_orders, disc_ordersq,
1743 for (
auto &[_, v] : tvals)
1746 for (
const auto &[name, v] : tvals)
1749 assert(v.cols() % stride == 0);
1754 for (
int i = 0; i < v.cols(); i += stride)
1756 const Eigen::MatrixXd tmp = v.middleCols(i, stride);
1757 assert(tmp.cols() == stride);
1759 const int ii = (i / stride) + 1;
1760 writer.add_field(fmt::format(
"{:s}_{:d}", name, ii), tmp);
1775 for (
auto &v :
vals)
1777 v.second.conservativeResize(v.second.size() + obstacle.
n_vertices(), 1);
1778 v.second.bottomRows(obstacle.
n_vertices()).setZero();
1784 for (
const auto &v :
vals)
1786 if (opts.
export_field(fmt::format(
"{:s}_avg", v.first)))
1787 writer.add_field(fmt::format(
"{:s}_avg", v.first), v.second);
1792 for (
const auto &v : tvals)
1795 assert(v.second.cols() % stride == 0);
1797 if (!opts.
export_field(fmt::format(
"{:s}_avg", v.first)))
1800 for (
int i = 0; i < v.second.cols(); i += stride)
1802 const Eigen::MatrixXd tmp = v.second.middleCols(i, stride);
1803 assert(tmp.cols() == stride);
1805 const int ii = (i / stride) + 1;
1807 fmt::format(
"{:s}_avg_{:d}", v.first, ii), tmp);
1818 std::map<std::string, Eigen::MatrixXd> param_val;
1819 for (
const auto &[p, _] : params)
1820 param_val[p] = Eigen::MatrixXd(points.rows(), 1);
1821 Eigen::MatrixXd rhos(points.rows(), 1);
1823 Eigen::MatrixXd local_pts;
1824 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
1828 for (
int e = 0; e < int(bases.size()); ++e)
1836 local_pts = sampler.simplex_points();
1838 local_pts = sampler.cube_points();
1840 local_pts = sampler.prism_points();
1842 local_pts = sampler.pyramid_points();
1846 sampler.sample_polyhedron(polys_3d.at(e).first, polys_3d.at(e).second, local_pts, vis_faces_poly, vis_edges_poly);
1848 sampler.sample_polygon(polys.at(e), local_pts, vis_faces_poly, vis_edges_poly);
1861 const auto o = std::max(disc_orders(e), disc_ordersq(e));
1878 const auto &mesh2d =
static_cast<const mesh::Mesh2D &
>(mesh);
1880 local_pts.resize(n_v, 2);
1882 for (
int j = 0; j < n_v; ++j)
1884 local_pts.row(j) = mesh2d.point(mesh2d.face_vertex(e, j));
1893 for (
int j = 0; j <
vals.val.rows(); ++j)
1895 for (
const auto &[p, func] : params)
1896 param_val.at(p)(index) = func(local_pts.row(j),
vals.val.row(j), t, e);
1898 rhos(index) = density(local_pts.row(j),
vals.val.row(j), t, e);
1904 assert(index == points.rows());
1908 for (
auto &[_, tmp] : param_val)
1910 tmp.conservativeResize(tmp.size() + obstacle.
n_vertices(), 1);
1911 tmp.bottomRows(obstacle.
n_vertices()).setZero();
1914 rhos.conservativeResize(rhos.size() + obstacle.
n_vertices(), 1);
1915 rhos.bottomRows(obstacle.
n_vertices()).setZero();
1917 for (
const auto &[p, tmp] : param_val)
1920 writer.add_field(p, tmp);
1923 writer.add_field(
"rho", rhos);
1929 Eigen::MatrixXd ids(points.rows(), 1);
1931 for (
int i = 0; i < points.rows(); ++i)
1938 ids.conservativeResize(ids.size() + obstacle.
n_vertices(), 1);
1939 ids.bottomRows(obstacle.
n_vertices()).setZero();
1942 writer.add_field(
"body_ids", ids);
1953 Eigen::MatrixXd traction_forces, traction_forces_fun;
1954 compute_traction_forces(state, sol, t, traction_forces,
false);
1957 mesh, problem.
is_scalar(), bases, disc_orders, disc_ordersq,
1963 traction_forces_fun.conservativeResize(traction_forces_fun.rows() + obstacle.
n_vertices(), traction_forces_fun.cols());
1964 traction_forces_fun.bottomRows(obstacle.
n_vertices()).setZero();
1967 writer.add_field(
"traction_force", traction_forces_fun);
1974 Eigen::VectorXd potential_grad;
1975 Eigen::MatrixXd potential_grad_fun;
1980 mesh, problem.
is_scalar(), bases, disc_orders, disc_ordersq,
1986 potential_grad_fun.conservativeResize(potential_grad_fun.rows() + obstacle.
n_vertices(), potential_grad_fun.cols());
1987 potential_grad_fun.bottomRows(obstacle.
n_vertices()).setZero();
1990 writer.add_field(
"gradient_of_elastic_potential", potential_grad_fun);
1992 catch (std::exception &)
2001 Eigen::VectorXd potential_grad;
2002 Eigen::MatrixXd potential_grad_fun;
2015 potential_grad_fun.conservativeResize(potential_grad_fun.rows() + obstacle.
n_vertices(), potential_grad_fun.cols());
2016 potential_grad_fun.bottomRows(obstacle.
n_vertices()).setZero();
2019 writer.add_field(
"gradient_of_contact_potential", potential_grad_fun);
2022 catch (std::exception &)
2028 writer.add_field(
"solution", fun);
2032 const int orig_p = points.rows();
2033 points.conservativeResize(points.rows() + obstacle.
n_vertices(), points.cols());
2034 points.bottomRows(obstacle.
n_vertices()) = obstacle.
v();
2036 if (elements.empty())
2038 for (
int i = 0; i < tets.rows(); ++i)
2040 elements.emplace_back();
2041 elements.back().ctype = CellType::Tetrahedron;
2042 for (
int j = 0; j < tets.cols(); ++j)
2043 elements.back().vertices.push_back(tets(i, j));
2049 elements.emplace_back();
2050 elements.back().ctype = CellType::Triangle;
2057 elements.emplace_back();
2058 elements.back().ctype = CellType::Line;
2065 elements.emplace_back();
2066 elements.back().ctype = CellType::Vertex;
2071 if (elements.empty())
2072 writer.write_mesh(path, points, tets, mesh.
is_volume() ? CellType::Tetrahedron : CellType::Triangle);
2074 writer.write_mesh(path, points, elements);
2079 const Eigen::MatrixXd &points,
2081 const std::string &name,
2082 const Eigen::VectorXd &field,
2083 paraviewo::ParaviewWriter &writer)
const
2085 Eigen::MatrixXd inerpolated_field;
2093 inerpolated_field.conservativeResize(
2099 writer.add_field(name, inerpolated_field);
2103 const std::string &export_surface,
2105 const Eigen::MatrixXd &sol,
2106 const Eigen::MatrixXd &pressure,
2110 const bool is_contact_enabled)
const
2113 const Eigen::VectorXi &disc_orders = state.
disc_orders;
2115 const std::vector<basis::ElementBases> &bases = state.
bases;
2116 const std::vector<basis::ElementBases> &pressure_bases = state.
pressure_bases;
2117 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
2123 Eigen::MatrixXd boundary_vis_vertices;
2124 Eigen::MatrixXd boundary_vis_local_vertices;
2125 Eigen::MatrixXi boundary_vis_elements;
2126 Eigen::MatrixXi boundary_vis_elements_ids;
2127 Eigen::MatrixXi boundary_vis_primitive_ids;
2128 Eigen::MatrixXd boundary_vis_normals;
2129 Eigen::MatrixXd displaced_boundary_vis_normals;
2132 boundary_vis_vertices, boundary_vis_local_vertices, boundary_vis_elements,
2133 boundary_vis_elements_ids, boundary_vis_primitive_ids, boundary_vis_normals,
2134 displaced_boundary_vis_normals);
2136 Eigen::MatrixXd fun, interp_p, discr, vect, b_sidesets;
2138 Eigen::MatrixXd lsol, lp, lgrad, lpgrad;
2144 discr.resize(boundary_vis_vertices.rows(), 1);
2145 fun.resize(boundary_vis_vertices.rows(), actual_dim);
2146 interp_p.resize(boundary_vis_vertices.rows(), 1);
2147 vect.resize(boundary_vis_vertices.rows(), mesh.
dimension());
2149 b_sidesets.resize(boundary_vis_vertices.rows(), 1);
2150 b_sidesets.setZero();
2152 for (
int i = 0; i < boundary_vis_vertices.rows(); ++i)
2154 const auto s_id = mesh.
get_boundary_id(boundary_vis_primitive_ids(i));
2157 b_sidesets(i) = s_id;
2160 const int el_index = boundary_vis_elements_ids(i);
2162 mesh, problem.
is_scalar(), bases, gbases,
2163 el_index, boundary_vis_local_vertices.row(i), sol, lsol, lgrad);
2164 assert(lsol.size() == actual_dim);
2168 mesh, 1, pressure_bases, gbases,
2169 el_index, boundary_vis_local_vertices.row(i), pressure, lp, lpgrad);
2170 assert(lp.size() == 1);
2171 interp_p(i) = lp(0);
2174 discr(i) = disc_orders(el_index);
2175 for (
int j = 0; j < actual_dim; ++j)
2177 fun(i, j) = lsol(j);
2180 if (actual_dim == 1)
2182 assert(lgrad.size() == mesh.
dimension());
2183 for (
int j = 0; j < mesh.
dimension(); ++j)
2185 vect(i, j) = lgrad(j);
2190 assert(lgrad.size() == actual_dim * actual_dim);
2191 std::vector<assembler::Assembler::NamedMatrix> tensor_flat;
2196 assert(tensor_flat[0].first ==
"cauchy_stess");
2197 assert(tensor_flat[0].second.size() == actual_dim * actual_dim);
2199 Eigen::Map<Eigen::MatrixXd> tensor(tensor_flat[0].second.data(), actual_dim, actual_dim);
2200 vect.row(i) = displaced_boundary_vis_normals.row(i) * tensor;
2206 area = mesh.
tri_area(boundary_vis_primitive_ids(i));
2207 else if (mesh.
is_cube(el_index))
2208 area = mesh.
quad_area(boundary_vis_primitive_ids(i));
2211 const int tmp = boundary_vis_primitive_ids(i);
2216 const int tmp = boundary_vis_primitive_ids(i);
2221 area = mesh.
edge_length(boundary_vis_primitive_ids(i));
2223 vect.row(i) *= area;
2227 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2229 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2231 tmpw = std::make_shared<paraviewo::VTUWriter>();
2232 paraviewo::ParaviewWriter &writer = *tmpw;
2235 writer.add_field(
"normals", boundary_vis_normals);
2237 writer.add_field(
"displaced_normals", displaced_boundary_vis_normals);
2239 writer.add_field(
"pressure", interp_p);
2241 writer.add_field(
"discr", discr);
2243 writer.add_field(
"sidesets", b_sidesets);
2245 if (actual_dim == 1 && opts.
export_field(
"solution_grad"))
2246 writer.add_field(
"solution_grad", vect);
2249 writer.add_field(
"traction_force", vect);
2256 std::map<std::string, Eigen::MatrixXd> param_val;
2257 for (
const auto &[p, _] : params)
2258 param_val[p] = Eigen::MatrixXd(boundary_vis_vertices.rows(), 1);
2259 Eigen::MatrixXd rhos(boundary_vis_vertices.rows(), 1);
2261 for (
int i = 0; i < boundary_vis_vertices.rows(); ++i)
2265 for (
const auto &[p, func] : params)
2266 param_val.at(p)(i) = func(boundary_vis_local_vertices.row(i), boundary_vis_vertices.row(i), t, boundary_vis_elements_ids(i));
2268 rhos(i) = density(boundary_vis_local_vertices.row(i), boundary_vis_vertices.row(i), t, boundary_vis_elements_ids(i));
2271 for (
const auto &[p, tmp] : param_val)
2274 writer.add_field(p, tmp);
2277 writer.add_field(
"rho", rhos);
2283 Eigen::MatrixXd ids(boundary_vis_vertices.rows(), 1);
2285 for (
int i = 0; i < boundary_vis_vertices.rows(); ++i)
2287 ids(i) = mesh.
get_body_id(boundary_vis_elements_ids(i));
2290 writer.add_field(
"body_ids", ids);
2294 writer.add_field(
"solution", fun);
2295 writer.write_mesh(export_surface, boundary_vis_vertices, boundary_vis_elements, mesh.
is_volume() ? CellType::Triangle : CellType::Line);
2299 const std::string &export_surface,
2301 const Eigen::MatrixXd &sol,
2302 const Eigen::MatrixXd &pressure,
2306 const bool is_contact_enabled)
const
2310 const double dhat = state.
args[
"contact"][
"dhat"];
2311 const double friction_coefficient = state.
args[
"contact"][
"friction_coefficient"];
2312 const double epsv = state.
args[
"contact"][
"epsv"];
2313 const double dhat_a = state.
args[
"contact"][
"adhesion"][
"dhat_a"];
2314 const double dhat_p = state.
args[
"contact"][
"adhesion"][
"dhat_p"];
2315 const double Y = state.
args[
"contact"][
"adhesion"][
"adhesion_strength"];
2316 const double epsa = state.
args[
"contact"][
"adhesion"][
"epsa"];
2317 const double tangential_adhesion_coefficient = state.
args[
"contact"][
"adhesion"][
"tangential_adhesion_coefficient"];
2323 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2325 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2327 tmpw = std::make_shared<paraviewo::VTUWriter>();
2328 paraviewo::ParaviewWriter &writer = *tmpw;
2330 const int problem_dim = mesh.
dimension();
2331 const Eigen::MatrixXd full_displacements =
utils::unflatten(sol, problem_dim);
2332 const Eigen::MatrixXd surface_displacements = collision_mesh.map_displacements(full_displacements);
2334 const Eigen::MatrixXd displaced_surface = collision_mesh.displace_vertices(full_displacements);
2336 ipc::NormalCollisions collision_set;
2338 if (state.
args[
"contact"][
"use_convergent_formulation"])
2340 collision_set.set_use_area_weighting(state.
args[
"contact"][
"use_area_weighting"]);
2341 collision_set.set_use_improved_max_approximator(state.
args[
"contact"][
"use_improved_max_operator"]);
2344 collision_set.build(
2345 collision_mesh, displaced_surface, dhat,
2346 0, ipc::create_broad_phase(state.
args[
"solver"][
"contact"][
"CCD"][
"broad_phase"]).get());
2348 const double barrier_stiffness = contact_form !=
nullptr ? contact_form->barrier_stiffness() : 1;
2349 ipc::BarrierPotential barrier_potential(dhat, barrier_stiffness);
2350 if (state.
args[
"contact"][
"use_convergent_formulation"])
2352 barrier_potential.set_use_physical_barrier(state.
args[
"contact"][
"use_physical_barrier"]);
2357 Eigen::MatrixXd forces = -barrier_potential.gradient(collision_set, collision_mesh, displaced_surface);
2361 assert(forces_reshaped.rows() == surface_displacements.rows());
2362 assert(forces_reshaped.cols() == surface_displacements.cols());
2363 writer.add_field(
"contact_forces", forces_reshaped);
2366 if (contact_form && state.
args[
"contact"][
"use_gcp_formulation"] && state.
args[
"contact"][
"use_adaptive_dhat"] && opts.
export_field(
"adaptive_dhat"))
2368 const auto form = std::dynamic_pointer_cast<solver::SmoothContactForm>(contact_form);
2370 const auto &set = form->collision_set();
2372 if (problem_dim == 2)
2374 Eigen::VectorXd dhats(collision_mesh.num_edges());
2375 dhats.setConstant(dhat);
2376 for (
int e = 0; e < dhats.size(); e++)
2377 dhats(e) = set.get_edge_dhat(e);
2379 writer.add_cell_field(
"dhat", dhats);
2383 Eigen::VectorXd fdhats(collision_mesh.num_faces());
2384 fdhats.setConstant(dhat);
2385 for (
int e = 0; e < fdhats.size(); e++)
2386 fdhats(e) = set.get_face_dhat(e);
2388 writer.add_cell_field(
"dhat_face", fdhats);
2390 Eigen::VectorXd vdhats(collision_mesh.num_vertices());
2391 vdhats.setConstant(dhat);
2392 for (
int i = 0; i < vdhats.size(); i++)
2393 vdhats(i) = set.get_vert_dhat(i);
2395 writer.add_field(
"dhat_vert", vdhats);
2401 ipc::TangentialCollisions friction_collision_set;
2402 friction_collision_set.build(
2403 collision_mesh, displaced_surface, collision_set,
2404 barrier_potential, friction_coefficient);
2406 ipc::FrictionPotential friction_potential(epsv);
2408 Eigen::MatrixXd velocities;
2413 velocities = collision_mesh.map_displacements(
utils::unflatten(velocities, collision_mesh.dim()));
2415 Eigen::MatrixXd forces = -friction_potential.gradient(
2416 friction_collision_set, collision_mesh, velocities);
2420 assert(forces_reshaped.rows() == surface_displacements.rows());
2421 assert(forces_reshaped.cols() == surface_displacements.cols());
2422 writer.add_field(
"friction_forces", forces_reshaped);
2429 ipc::NormalCollisions adhesion_collision_set;
2430 ipc::NormalAdhesionPotential normal_adhesion_potential(dhat_p, dhat_a, Y, 1);
2432 if (need_normal_adhesion || need_tangential_adhesion)
2434 adhesion_collision_set.build(
2435 collision_mesh, displaced_surface, dhat_a,
2436 0, ipc::create_broad_phase(state.
args[
"solver"][
"contact"][
"CCD"][
"broad_phase"]).get());
2439 if (need_normal_adhesion)
2441 Eigen::MatrixXd forces = -1 * normal_adhesion_potential.gradient(adhesion_collision_set, collision_mesh, displaced_surface);
2445 assert(forces_reshaped.rows() == surface_displacements.rows());
2446 assert(forces_reshaped.cols() == surface_displacements.cols());
2447 writer.add_field(
"normal_adhesion_forces", forces_reshaped);
2450 if (need_tangential_adhesion)
2452 ipc::TangentialCollisions tangential_collision_set;
2453 tangential_collision_set.build(
2454 collision_mesh, displaced_surface, adhesion_collision_set,
2455 normal_adhesion_potential, tangential_adhesion_coefficient);
2457 ipc::TangentialAdhesionPotential tangential_adhesion_potential(epsa);
2459 Eigen::MatrixXd velocities;
2464 velocities = collision_mesh.map_displacements(
utils::unflatten(velocities, collision_mesh.dim()));
2466 Eigen::MatrixXd forces = -tangential_adhesion_potential.gradient(
2467 tangential_collision_set, collision_mesh, velocities);
2471 assert(forces_reshaped.rows() == surface_displacements.rows());
2472 assert(forces_reshaped.cols() == surface_displacements.cols());
2473 writer.add_field(
"tangential_adhesion_forces", forces_reshaped);
2476 assert(collision_mesh.rest_positions().rows() == surface_displacements.rows());
2477 assert(collision_mesh.rest_positions().cols() == surface_displacements.cols());
2480 writer.add_field(
"solution", surface_displacements);
2483 export_surface.substr(0, export_surface.length() - 4) +
"_contact.vtu",
2484 collision_mesh.rest_positions(),
2485 problem_dim == 3 ? collision_mesh.faces() : collision_mesh.edges(),
2486 problem_dim == 3 ? CellType::Triangle : CellType::Line);
2490 const std::string &name,
2492 const Eigen::MatrixXd &sol,
2496 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
2502 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
2503 Eigen::MatrixXd vis_pts_poly;
2505 const auto ¤t_bases = gbases;
2506 int seg_total_size = 0;
2507 int pts_total_size = 0;
2508 int faces_total_size = 0;
2510 for (
size_t i = 0; i < current_bases.size(); ++i)
2512 const auto &bs = current_bases[i];
2517 seg_total_size += sampler.simplex_edges().rows();
2518 faces_total_size += sampler.simplex_faces().rows();
2522 pts_total_size += sampler.cube_points().rows();
2523 seg_total_size += sampler.cube_edges().rows();
2524 faces_total_size += sampler.cube_faces().rows();
2528 pts_total_size += sampler.prism_points().rows();
2529 seg_total_size += sampler.prism_edges().rows();
2530 faces_total_size += sampler.prism_faces().rows();
2534 pts_total_size += sampler.pyramid_points().rows();
2535 seg_total_size += sampler.pyramid_edges().rows();
2536 faces_total_size += sampler.pyramid_faces().rows();
2541 sampler.sample_polyhedron(state.
polys_3d.at(i).first, state.
polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
2543 sampler.sample_polygon(state.
polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
2545 pts_total_size += vis_pts_poly.rows();
2546 seg_total_size += vis_edges_poly.rows();
2547 faces_total_size += vis_faces_poly.rows();
2551 Eigen::MatrixXd points(pts_total_size, mesh.
dimension());
2552 Eigen::MatrixXi edges(seg_total_size, 2);
2553 Eigen::MatrixXi
faces(faces_total_size, 3);
2556 Eigen::MatrixXd mapped, tmp;
2557 int seg_index = 0, pts_index = 0, face_index = 0;
2558 for (
size_t i = 0; i < current_bases.size(); ++i)
2560 const auto &bs = current_bases[i];
2564 bs.eval_geom_mapping(sampler.simplex_points(), mapped);
2565 edges.block(seg_index, 0, sampler.simplex_edges().rows(), edges.cols()) = sampler.simplex_edges().array() + pts_index;
2566 seg_index += sampler.simplex_edges().rows();
2568 faces.block(face_index, 0, sampler.simplex_faces().rows(), 3) = sampler.simplex_faces().array() + pts_index;
2569 face_index += sampler.simplex_faces().rows();
2571 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
2572 pts_index += mapped.rows();
2576 bs.eval_geom_mapping(sampler.cube_points(), mapped);
2577 edges.block(seg_index, 0, sampler.cube_edges().rows(), edges.cols()) = sampler.cube_edges().array() + pts_index;
2578 seg_index += sampler.cube_edges().rows();
2580 faces.block(face_index, 0, sampler.cube_faces().rows(), 3) = sampler.cube_faces().array() + pts_index;
2581 face_index += sampler.cube_faces().rows();
2583 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
2584 pts_index += mapped.rows();
2588 bs.eval_geom_mapping(sampler.prism_points(), mapped);
2589 edges.block(seg_index, 0, sampler.prism_edges().rows(), edges.cols()) = sampler.prism_edges().array() + pts_index;
2590 seg_index += sampler.prism_edges().rows();
2592 faces.block(face_index, 0, sampler.prism_faces().rows(), 3) = sampler.prism_faces().array() + pts_index;
2593 face_index += sampler.prism_faces().rows();
2595 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
2596 pts_index += mapped.rows();
2600 bs.eval_geom_mapping(sampler.pyramid_points(), mapped);
2601 edges.block(seg_index, 0, sampler.pyramid_edges().rows(), edges.cols()) = sampler.pyramid_edges().array() + pts_index;
2602 seg_index += sampler.pyramid_edges().rows();
2604 faces.block(face_index, 0, sampler.pyramid_faces().rows(), 3) = sampler.pyramid_faces().array() + pts_index;
2605 face_index += sampler.pyramid_faces().rows();
2607 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
2608 pts_index += mapped.rows();
2613 sampler.sample_polyhedron(state.
polys_3d.at(i).first, state.
polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
2615 sampler.sample_polygon(state.
polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
2617 edges.block(seg_index, 0, vis_edges_poly.rows(), edges.cols()) = vis_edges_poly.array() + pts_index;
2618 seg_index += vis_edges_poly.rows();
2620 faces.block(face_index, 0, vis_faces_poly.rows(), 3) = vis_faces_poly.array() + pts_index;
2621 face_index += vis_faces_poly.rows();
2623 points.block(pts_index, 0, vis_pts_poly.rows(), points.cols()) = vis_pts_poly;
2624 pts_index += vis_pts_poly.rows();
2628 assert(pts_index == points.rows());
2629 assert(face_index ==
faces.rows());
2634 for (
long i = 0; i <
faces.rows(); ++i)
2636 const int v0 =
faces(i, 0);
2637 const int v1 =
faces(i, 1);
2638 const int v2 =
faces(i, 2);
2640 int tmpc =
faces(i, 2);
2647 Eigen::Matrix2d mmat;
2648 for (
long i = 0; i <
faces.rows(); ++i)
2650 const int v0 =
faces(i, 0);
2651 const int v1 =
faces(i, 1);
2652 const int v2 =
faces(i, 2);
2654 mmat.row(0) = points.row(v2) - points.row(v0);
2655 mmat.row(1) = points.row(v1) - points.row(v0);
2657 if (mmat.determinant() > 0)
2659 int tmpc =
faces(i, 2);
2666 Eigen::MatrixXd fun;
2670 pts_index, sol, fun,
true,
false);
2672 Eigen::MatrixXd exact_fun, err;
2676 problem.
exact(points, t, exact_fun);
2677 err = (fun - exact_fun).eval().rowwise().norm();
2680 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2682 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2684 tmpw = std::make_shared<paraviewo::VTUWriter>();
2685 paraviewo::ParaviewWriter &writer = *tmpw;
2690 writer.add_field(
"exact", exact_fun);
2692 writer.add_field(
"error", err);
2695 if (fun.cols() != 1)
2697 std::vector<assembler::Assembler::NamedMatrix> scalar_val;
2703 for (
const auto &v : scalar_val)
2706 writer.add_field(v.first, v.second);
2710 writer.add_field(
"solution", fun);
2712 writer.write_mesh(name, points, edges, CellType::Line);
2716 const std::string &path,
2718 const Eigen::MatrixXd &sol,
2730 Eigen::MatrixXd fun(dirichlet_nodes_position.size(), actual_dim);
2731 Eigen::MatrixXd b_sidesets(dirichlet_nodes_position.size(), 1);
2732 b_sidesets.setZero();
2733 Eigen::MatrixXd points(dirichlet_nodes_position.size(), mesh.
dimension());
2734 std::vector<CellElement> cells(dirichlet_nodes_position.size());
2736 for (
int i = 0; i < dirichlet_nodes_position.size(); ++i)
2738 const int n_id = dirichlet_nodes[i];
2742 b_sidesets(i) = s_id;
2745 for (
int j = 0; j < actual_dim; ++j)
2747 fun(i, j) = sol(n_id * actual_dim + j);
2750 points.row(i) = dirichlet_nodes_position[i];
2751 cells[i].vertices.push_back(i);
2752 cells[i].ctype = CellType::Vertex;
2755 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2757 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2759 tmpw = std::make_shared<paraviewo::VTUWriter>();
2760 paraviewo::ParaviewWriter &writer = *tmpw;
2763 writer.add_field(
"sidesets", b_sidesets);
2765 writer.add_field(
"solution", fun);
2766 writer.write_mesh(path, points, cells);
2770 const std::string &name,
2771 const std::function<std::string(
int)> &vtu_names,
2772 int time_steps,
double t0,
double dt,
int skip_frame)
const
2774 paraviewo::PVDWriter::save_pvd(name, vtu_names, time_steps, t0, dt, skip_frame);
2790 const int nx = delta[0] / spacing + 1;
2791 const int ny = delta[1] / spacing + 1;
2792 const int nz = delta.cols() >= 3 ? (delta[2] / spacing + 1) : 1;
2793 const int n = nx * ny * nz;
2797 for (
int i = 0; i < nx; ++i)
2799 const double x = (delta[0] / (nx - 1)) * i + min[0];
2801 for (
int j = 0; j < ny; ++j)
2803 const double y = (delta[1] / (ny - 1)) * j + min[1];
2805 if (delta.cols() <= 2)
2811 for (
int k = 0; k < nz; ++k)
2813 const double z = (delta[2] / (nz - 1)) * k + min[2];
2822 std::vector<std::array<Eigen::Vector3d, 2>> boxes;
2828 const double eps = 1e-6;
2837 const Eigen::Vector3d min(
2842 const Eigen::Vector3d max(
2847 std::vector<unsigned int> candidates;
2849 bvh.intersect_box(min, max, candidates);
2851 for (
const auto cand : candidates)
2855 logger().warn(
"Element {} is not simplex, skipping", cand);
2859 Eigen::MatrixXd coords;
2862 for (
int d = 0; d < coords.size(); ++d)
2864 if (fabs(coords(d)) < 1e-8)
2866 else if (fabs(coords(d) - 1) < 1e-8)
2870 if (coords.array().minCoeff() >= 0 && coords.array().maxCoeff() <= 1)
2881 : file(path), solve_data(solve_data)
2886 file << name <<
",";
2888 file <<
"total_energy" << std::endl;
2905 file << ((form && form->enabled()) ? form->value(sol) : 0) / s <<
",";
2912 : file(path), state(state), t0(t0), dt(dt)
2914 file <<
"step,time,forward,remeshing,global_relaxation,peak_mem,#V,#T" << std::endl;
2938 const double peak_mem =
getPeakRSS() / double(1 << 30);
2941 file << fmt::format(
2942 "{},{},{},{},{},{},{},{}\n",
2943 t,
t0 +
dt * t, forward, remeshing, global_relaxation, peak_mem,
ElementAssemblyValues vals
std::vector< Eigen::VectorXi > faces
virtual std::map< std::string, ParamFunc > parameters() const =0
virtual void compute_tensor_value(const OutputData &data, std::vector< NamedMatrix > &result) const
stores per local bases evaluations
std::vector< basis::Local2Global > global
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,...
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
virtual bool is_time_dependent() 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).
static void interpolate_at_local_vals(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const int el_index, const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, Eigen::MatrixXd &result_grad)
interpolate solution and gradient at element (calls interpolate_at_local_vals with sol)
static void average_grad_based_function(const mesh::Mesh &mesh, const bool is_problem_scalar, const int n_bases, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::map< int, Eigen::MatrixXd > &polys, const std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &polys_3d, const assembler::Assembler &assembler, const utils::RefElementSampler &sampler, const double t, const int n_points, const Eigen::MatrixXd &fun, std::vector< assembler::Assembler::NamedMatrix > &result_scalar, std::vector< assembler::Assembler::NamedMatrix > &result_tensor, const bool use_sampler, const bool boundary_only)
calls compute_scalar_value (i.e von mises for elasticity and norm of velocity for fluid) and compute_...
static void compute_scalar_value(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::map< int, Eigen::MatrixXd > &polys, const std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &polys_3d, const assembler::Assembler &assembler, const utils::RefElementSampler &sampler, const int n_points, const Eigen::MatrixXd &fun, const double t, std::vector< assembler::Assembler::NamedMatrix > &result, const bool use_sampler, const bool boundary_only)
computes scalar quantity of funtion (ie von mises for elasticity and norm of velocity for fluid)
static void compute_tensor_value(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::map< int, Eigen::MatrixXd > &polys, const std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &polys_3d, const assembler::Assembler &assembler, const utils::RefElementSampler &sampler, const int n_points, const Eigen::MatrixXd &fun, const double t, std::vector< assembler::Assembler::NamedMatrix > &result, const bool use_sampler, const bool boundary_only)
compute tensor quantity (ie stress tensor or velocity)
static void mark_flipped_cells(const mesh::Mesh &mesh, const std::vector< basis::ElementBases > &gbasis, const std::vector< basis::ElementBases > &basis, const Eigen::VectorXi &disc_orders, const std::map< int, Eigen::MatrixXd > &polys, const std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &polys_3d, const utils::RefElementSampler &sampler, const int n_points, const Eigen::MatrixXd &fun, Eigen::Vector< bool, -1 > &result, const bool use_sampler, const bool boundary_only)
static void interpolate_function(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::map< int, Eigen::MatrixXd > &polys, const std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &polys_3d, const utils::RefElementSampler &sampler, const int n_points, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, const bool use_sampler, const bool boundary_only)
interpolate the function fun.
static void compute_stress_at_quadrature_points(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const assembler::Assembler &assembler, const Eigen::MatrixXd &fun, const double t, Eigen::MatrixXd &result, Eigen::VectorXd &von_mises)
compute von mises stress at quadrature points for the function fun, also compute the interpolated fun...
main class that contains the polyfem solver and all its state
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
ipc::CollisionMesh collision_mesh
IPC collision mesh.
std::vector< RowVectorNd > dirichlet_nodes_position
std::vector< int > dirichlet_nodes
per node dirichlet
std::shared_ptr< assembler::Mass > mass_matrix_assembler
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Eigen::MatrixXd rhs
System right-hand side.
json args
main input arguments containing all defaults
Eigen::VectorXi disc_ordersq
std::map< int, Eigen::MatrixXd > polys
polygons, used since poly have no geom mapping
int n_bases
number of bases
std::vector< basis::ElementBases > pressure_bases
FE pressure bases for mixed elements, the size is #elements.
mesh::Obstacle obstacle
Obstacles used in collisions.
Eigen::VectorXi disc_orders
vector of discretization orders, used when not all elements have the same degree, one per element
QuadratureOrders n_boundary_samples() const
quadrature used for projecting boundary conditions
std::shared_ptr< assembler::Assembler > assembler
assemblers
std::vector< basis::ElementBases > bases
FE bases, the size is #elements.
std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > polys_3d
polyhedra, used since poly have no geom mapping
bool is_adhesion_enabled() const
does the simulation have adhesion
std::vector< mesh::LocalBoundary > total_local_boundary
mapping from elements to nodes for all mesh
solver::SolveData solve_data
timedependent stuff cached
Eigen::VectorXi in_node_to_node
Inpute nodes (including high-order) to polyfem nodes, only for isoparametric.
std::shared_ptr< assembler::MixedAssembler > mixed_assembler
EnergyCSVWriter(const std::string &path, const solver::SolveData &solve_data)
void write(const int i, const Eigen::MatrixXd &sol)
const solver::SolveData & solve_data
utils::RefElementSampler ref_element_sampler
used to sample the solution
void build_high_order_vis_mesh(const mesh::Mesh &mesh, const Eigen::VectorXi &disc_orders, const Eigen::VectorXi &disc_ordersq, const std::vector< basis::ElementBases > &bases, Eigen::MatrixXd &points, std::vector< paraviewo::CellElement > &elements, Eigen::MatrixXi &el_id, Eigen::MatrixXd &discr) const
builds high-der visualzation mesh per element all disconnected it also retuns the mapping to element ...
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
Eigen::MatrixXd grid_points_bc
grid mesh boundaries
void export_data(const State &state, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure, const bool is_time_dependent, const double tend_in, const double dt, const ExportOptions &opts, const std::string &vis_mesh_path, const std::string &nodes_path, const std::string &solution_path, const std::string &stress_path, const std::string &mises_path, const bool is_contact_enabled) const
exports everytihng, txt, vtu, etc
void save_vtu(const std::string &path, const State &state, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure, const double t, const double dt, const ExportOptions &opts, const bool is_contact_enabled) const
saves the vtu file for time t
void save_wire(const std::string &name, const State &state, const Eigen::MatrixXd &sol, const double t, const ExportOptions &opts) const
saves the wireframe
void save_points(const std::string &path, const State &state, const Eigen::MatrixXd &sol, const ExportOptions &opts) const
saves the nodal values
void save_volume_vector_field(const State &state, const Eigen::MatrixXd &points, const ExportOptions &opts, const std::string &name, const Eigen::VectorXd &field, paraviewo::ParaviewWriter &writer) const
void save_volume(const std::string &path, const State &state, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure, const double t, const double dt, const ExportOptions &opts) const
saves the volume vtu file
void build_vis_boundary_mesh(const mesh::Mesh &mesh, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const std::vector< mesh::LocalBoundary > &total_local_boundary, const Eigen::MatrixXd &solution, const int problem_dim, 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, Eigen::MatrixXd &displaced_boundary_vis_normals) const
builds the boundary mesh for visualization
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) const
builds visualzation mesh, upsampled mesh used for visualization the visualization mesh is a dense mes...
void save_contact_surface(const std::string &export_surface, const State &state, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure, const double t, const double dt_in, const ExportOptions &opts, const bool is_contact_enabled) const
saves the surface vtu file for for constact quantites, eg contact or friction forces
Eigen::MatrixXi grid_points_to_elements
grid mesh mapping to fe elements
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 build_grid(const polyfem::mesh::Mesh &mesh, const double spacing)
builds the grid to export the solution
void init_sampler(const polyfem::mesh::Mesh &mesh, const double vismesh_rel_area)
unitalize the ref element sampler
Eigen::MatrixXd grid_points
grid mesh points to export solution sampled on a grid
void save_surface(const std::string &export_surface, const State &state, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure, const double t, const double dt_in, const ExportOptions &opts, const bool is_contact_enabled) const
saves the surface vtu file for for surface quantites, eg traction forces
double total_forward_solve_time
double total_global_relaxation_time
RuntimeStatsCSVWriter(const std::string &path, const State &state, const double t0, const double dt)
void write(const int t, const double forward, const double remeshing, const double global_relaxation, const Eigen::MatrixXd &sol)
double total_remeshing_time
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 get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
virtual double edge_length(const int gid) const
edge length
bool is_polytope(const int el_id) const
checks if element is polygon compatible
virtual double tri_area(const int gid) const
area of a tri face of a tet mesh
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
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
virtual double quad_area(const int gid) const
area of a quad face of an hex mesh
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
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
class to store time stepping data
std::shared_ptr< solver::FrictionForm > friction_form
std::shared_ptr< solver::NLProblem > nl_problem
std::shared_ptr< solver::NormalAdhesionForm > normal_adhesion_form
std::shared_ptr< solver::ContactForm > contact_form
std::vector< std::pair< std::string, std::shared_ptr< solver::Form > > > named_forms() const
std::shared_ptr< solver::ElasticForm > elastic_form
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > time_integrator
std::shared_ptr< solver::TangentialAdhesionForm > tangential_adhesion_form
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 bool boundary_quadrature(const mesh::LocalBoundary &local_boundary, const QuadratureOrders &order, const mesh::Mesh &mesh, const bool skip_computation, Eigen::MatrixXd &uv, Eigen::MatrixXd &points, Eigen::MatrixXd &normals, Eigen::VectorXd &weights, Eigen::VectorXi &global_primitive_ids)
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)
void init(const bool is_volume, const int n_elements, const double target_rel_area)
const Eigen::MatrixXd & simplex_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)
paraviewo::CellType CellType
paraviewo::CellElement CellElement
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
void append_rows_of_zeros(DstMat &dst, const size_t n_zero_rows)
spdlog::logger & logger()
Retrieves the current logger.
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
bool normal_adhesion_forces
bool export_field(const std::string &field) const
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
std::vector< std::string > fields
bool tangential_adhesion_forces
bool discretization_order
std::string file_extension() const
return the extension of the output paraview files depending on use_hdf5