45#include <paraviewo/VTMWriter.hpp>
46#include <paraviewo/PVDWriter.hpp>
48#include <SimpleBVH/BVH.hpp>
50#include <igl/write_triangle_mesh.h>
52#include <igl/facet_adjacency_matrix.h>
53#include <igl/connected_components.h>
63 void compute_traction_forces(
const State &state,
const Eigen::MatrixXd &solution,
const double t, Eigen::MatrixXd &traction_forces,
bool skip_dirichlet =
true)
66 if (!state.problem->is_scalar())
67 actual_dim = state.mesh->dimension();
71 const std::vector<basis::ElementBases> &bases = state.bases;
72 const std::vector<basis::ElementBases> &gbases = state.geom_bases();
74 Eigen::MatrixXd uv, samples, gtmp, rhs_fun, deform_mat, trafo;
75 Eigen::VectorXi global_primitive_ids;
76 Eigen::MatrixXd
points, normals;
77 Eigen::VectorXd weights;
80 traction_forces.setZero(state.n_bases * actual_dim, 1);
82 for (
const auto &lb : state.total_local_boundary)
84 const int e = lb.element_id();
90 const basis::ElementBases &gbs = gbases[
e];
91 const basis::ElementBases &bs = bases[
e];
93 vals.
compute(e, state.mesh->is_volume(), points, bs, gbs);
95 for (
int n = 0; n < normals.rows(); ++n)
99 if (solution.size() > 0)
101 assert(actual_dim == 2 || actual_dim == 3);
102 deform_mat.resize(actual_dim, actual_dim);
103 deform_mat.setZero();
104 for (
const auto &b :
vals.basis_values)
106 for (
const auto &g : b.global)
108 for (
int d = 0; d < actual_dim; ++d)
110 deform_mat.row(d) += solution(
g.index * actual_dim + d) * b.grad.row(n);
118 normals.row(n) = normals.row(n) * trafo.inverse();
119 normals.row(n).normalize();
122 std::vector<assembler::Assembler::NamedMatrix> tensor_flat;
123 state.assembler->compute_tensor_value(assembler::OutputData(t, e, bs, gbs, points, solution), tensor_flat);
129 const int g_index = v.
global[0].index * actual_dim;
131 for (
int q = 0; q <
points.rows(); ++q)
134 assert(tensor_flat[0].first ==
"cauchy_stess");
135 assert(tensor_flat[0].second.row(q).size() == actual_dim * actual_dim);
137 Eigen::MatrixXd stress_tensor =
utils::unflatten(tensor_flat[0].second.row(q), actual_dim);
139 traction_forces.block(g_index, 0, actual_dim, 1) += stress_tensor * normals.row(q).transpose() * v.
val(q) * weights(q);
149 const std::vector<basis::ElementBases> &bases,
150 const std::vector<mesh::LocalBoundary> &total_local_boundary,
151 Eigen::MatrixXd &node_positions,
152 Eigen::MatrixXi &boundary_edges,
153 Eigen::MatrixXi &boundary_triangles,
154 std::vector<Eigen::Triplet<double>> &displacement_map_entries)
158 displacement_map_entries.clear();
164 logger().warn(
"Skipping as the mesh has polygons");
170 node_positions.resize(n_bases + (is_simplicial ? 0 : mesh.
n_faces()), 3);
171 node_positions.setZero();
172 const Mesh3D &mesh3d =
dynamic_cast<const Mesh3D &
>(mesh);
174 std::vector<std::tuple<int, int, int>> tris;
176 std::vector<bool> visited_node(n_bases,
false);
178 std::stringstream print_warning;
184 for (
int j = 0; j < lb.size(); ++j)
186 const int eid = lb.global_primitive_id(j);
187 const int lid = lb[j];
190 if (mesh.
is_cube(lb.element_id()))
192 assert(!is_simplicial);
194 std::vector<int> loc_nodes;
197 for (
long n = 0; n < nodes.size(); ++n)
199 auto &bs = b.
bases[nodes(n)];
200 const auto &glob = bs.global();
201 if (glob.size() != 1)
204 int gindex = glob.front().index;
205 node_positions.row(gindex) = glob.front().node;
206 bary += glob.front().node;
207 loc_nodes.push_back(gindex);
210 if (loc_nodes.size() != 4)
212 logger().trace(
"skipping element {} since it is not Q1", eid);
218 const int new_node = n_bases + eid;
219 node_positions.row(new_node) = bary;
220 tris.emplace_back(loc_nodes[1], loc_nodes[0], new_node);
221 tris.emplace_back(loc_nodes[2], loc_nodes[1], new_node);
222 tris.emplace_back(loc_nodes[3], loc_nodes[2], new_node);
223 tris.emplace_back(loc_nodes[0], loc_nodes[3], new_node);
225 for (
int q = 0; q < 4; ++q)
227 if (!visited_node[loc_nodes[q]])
228 displacement_map_entries.emplace_back(loc_nodes[q], loc_nodes[q], 1);
230 visited_node[loc_nodes[q]] =
true;
231 displacement_map_entries.emplace_back(new_node, loc_nodes[q], 0.25);
236 else if (mesh.
is_prism(lb.element_id()))
238 assert(!is_simplicial);
240 std::vector<int> loc_nodes;
243 for (
long n = 0; n < nodes.size(); ++n)
245 auto &bs = b.
bases[nodes(n)];
246 const auto &glob = bs.global();
247 if (glob.size() != 1)
250 int gindex = glob.front().index;
251 node_positions.row(gindex) = glob.front().node;
252 bary += glob.front().node;
253 loc_nodes.push_back(gindex);
256 auto update_mapping = [&displacement_map_entries, &visited_node](
const std::vector<int> &loc_nodes) {
257 for (
int k = 0; k < loc_nodes.size(); ++k)
259 if (!visited_node[loc_nodes[k]])
260 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
262 visited_node[loc_nodes[k]] =
true;
266 if (loc_nodes.size() == 3)
268 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
270 update_mapping(loc_nodes);
272 else if (loc_nodes.size() == 6)
274 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
275 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
276 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
277 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
279 update_mapping(loc_nodes);
281 else if (loc_nodes.size() == 10)
283 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
284 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
285 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
286 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
287 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
288 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
289 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
290 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
291 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
292 update_mapping(loc_nodes);
294 else if (loc_nodes.size() == 15)
296 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[11]);
297 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[12]);
298 tris.emplace_back(loc_nodes[3], loc_nodes[12], loc_nodes[11]);
299 tris.emplace_back(loc_nodes[12], loc_nodes[10], loc_nodes[11]);
300 tris.emplace_back(loc_nodes[4], loc_nodes[5], loc_nodes[13]);
301 tris.emplace_back(loc_nodes[4], loc_nodes[13], loc_nodes[12]);
302 tris.emplace_back(loc_nodes[12], loc_nodes[13], loc_nodes[14]);
303 tris.emplace_back(loc_nodes[12], loc_nodes[14], loc_nodes[10]);
304 tris.emplace_back(loc_nodes[14], loc_nodes[9], loc_nodes[10]);
305 tris.emplace_back(loc_nodes[5], loc_nodes[1], loc_nodes[6]);
306 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[13]);
307 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[13]);
308 tris.emplace_back(loc_nodes[13], loc_nodes[7], loc_nodes[14]);
309 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[14]);
310 tris.emplace_back(loc_nodes[14], loc_nodes[8], loc_nodes[9]);
311 tris.emplace_back(loc_nodes[8], loc_nodes[2], loc_nodes[9]);
312 update_mapping(loc_nodes);
314 else if (loc_nodes.size() == 4)
318 const int new_node = n_bases + eid;
319 node_positions.row(new_node) = bary;
320 tris.emplace_back(loc_nodes[1], loc_nodes[0], new_node);
321 tris.emplace_back(loc_nodes[2], loc_nodes[1], new_node);
322 tris.emplace_back(loc_nodes[3], loc_nodes[2], new_node);
323 tris.emplace_back(loc_nodes[0], loc_nodes[3], new_node);
325 update_mapping(loc_nodes);
329 logger().trace(
"skipping element {} since it is not linear, it has {} nodes", eid, loc_nodes.size());
338 logger().trace(
"skipping element {} since it is not a simplex or hex", eid);
344 std::vector<int> loc_nodes;
346 bool is_follower =
false;
349 for (
long n = 0; n < nodes.size(); ++n)
351 auto &bs = b.
bases[nodes(n)];
352 const auto &glob = bs.global();
353 if (glob.size() != 1)
364 for (
long n = 0; n < nodes.size(); ++n)
367 const std::vector<basis::Local2Global> &glob = bs.
global();
368 if (glob.size() != 1)
371 int gindex = glob.front().index;
372 node_positions.row(gindex) = glob.front().node;
373 loc_nodes.push_back(gindex);
376 if (loc_nodes.size() == 3)
378 tris.emplace_back(loc_nodes[0], loc_nodes[1], loc_nodes[2]);
380 else if (loc_nodes.size() == 6)
382 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[5]);
383 tris.emplace_back(loc_nodes[3], loc_nodes[1], loc_nodes[4]);
384 tris.emplace_back(loc_nodes[4], loc_nodes[2], loc_nodes[5]);
385 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[5]);
387 else if (loc_nodes.size() == 10)
389 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[8]);
390 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[9]);
391 tris.emplace_back(loc_nodes[4], loc_nodes[1], loc_nodes[5]);
392 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[9]);
393 tris.emplace_back(loc_nodes[6], loc_nodes[2], loc_nodes[7]);
394 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[9]);
395 tris.emplace_back(loc_nodes[8], loc_nodes[3], loc_nodes[9]);
396 tris.emplace_back(loc_nodes[9], loc_nodes[4], loc_nodes[5]);
397 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[9]);
399 else if (loc_nodes.size() == 15)
401 tris.emplace_back(loc_nodes[0], loc_nodes[3], loc_nodes[11]);
402 tris.emplace_back(loc_nodes[3], loc_nodes[4], loc_nodes[12]);
403 tris.emplace_back(loc_nodes[3], loc_nodes[12], loc_nodes[11]);
404 tris.emplace_back(loc_nodes[12], loc_nodes[10], loc_nodes[11]);
405 tris.emplace_back(loc_nodes[4], loc_nodes[5], loc_nodes[13]);
406 tris.emplace_back(loc_nodes[4], loc_nodes[13], loc_nodes[12]);
407 tris.emplace_back(loc_nodes[12], loc_nodes[13], loc_nodes[14]);
408 tris.emplace_back(loc_nodes[12], loc_nodes[14], loc_nodes[10]);
409 tris.emplace_back(loc_nodes[14], loc_nodes[9], loc_nodes[10]);
410 tris.emplace_back(loc_nodes[5], loc_nodes[1], loc_nodes[6]);
411 tris.emplace_back(loc_nodes[5], loc_nodes[6], loc_nodes[13]);
412 tris.emplace_back(loc_nodes[6], loc_nodes[7], loc_nodes[13]);
413 tris.emplace_back(loc_nodes[13], loc_nodes[7], loc_nodes[14]);
414 tris.emplace_back(loc_nodes[7], loc_nodes[8], loc_nodes[14]);
415 tris.emplace_back(loc_nodes[14], loc_nodes[8], loc_nodes[9]);
416 tris.emplace_back(loc_nodes[8], loc_nodes[2], loc_nodes[9]);
420 print_warning << loc_nodes.size() <<
" ";
426 for (
int k = 0; k < loc_nodes.size(); ++k)
428 if (!visited_node[loc_nodes[k]])
429 displacement_map_entries.emplace_back(loc_nodes[k], loc_nodes[k], 1);
431 visited_node[loc_nodes[k]] =
true;
437 if (print_warning.str().size() > 0)
438 logger().warn(
"Skipping faces as theys have {} nodes, boundary export supported up to p4", print_warning.str());
440 boundary_triangles.resize(tris.size(), 3);
441 for (
int i = 0; i < tris.size(); ++i)
443 boundary_triangles.row(i) << std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]);
446 if (boundary_triangles.rows() > 0)
448 igl::edges(boundary_triangles, boundary_edges);
453 node_positions.resize(n_bases, 2);
454 node_positions.setZero();
455 const Mesh2D &mesh2d =
dynamic_cast<const Mesh2D &
>(mesh);
457 std::vector<std::pair<int, int>> edges;
463 for (
int j = 0; j < lb.size(); ++j)
465 const int eid = lb.global_primitive_id(j);
466 const int lid = lb[j];
471 for (
long n = 0; n < nodes.size(); ++n)
474 const std::vector<basis::Local2Global> &glob = bs.
global();
475 if (glob.size() != 1)
478 int gindex = glob.front().index;
479 node_positions.row(gindex) = glob.front().node.head<2>();
482 edges.emplace_back(prev_node, gindex);
489 boundary_triangles.resize(0, 0);
490 boundary_edges.resize(edges.size(), 2);
491 for (
int i = 0; i < edges.size(); ++i)
493 boundary_edges.row(i) << edges[i].first, edges[i].second;
500 const std::vector<basis::ElementBases> &bases,
501 const std::vector<basis::ElementBases> &gbases,
502 const std::vector<mesh::LocalBoundary> &total_local_boundary,
503 const Eigen::MatrixXd &solution,
504 const int problem_dim,
505 Eigen::MatrixXd &boundary_vis_vertices,
506 Eigen::MatrixXd &boundary_vis_local_vertices,
507 Eigen::MatrixXi &boundary_vis_elements,
508 Eigen::MatrixXi &boundary_vis_elements_ids,
509 Eigen::MatrixXi &boundary_vis_primitive_ids,
510 Eigen::MatrixXd &boundary_vis_normals,
511 Eigen::MatrixXd &displaced_boundary_vis_normals)
const
515 std::vector<Eigen::MatrixXd> lv, vertices, allnormals, displaced_allnormals;
516 std::vector<int> el_ids, global_primitive_ids;
517 Eigen::MatrixXd uv, local_pts, tmp_n, normals, displaced_normals, trafo, deform_mat;
523 std::vector<std::pair<int, int>> edges;
524 std::vector<std::tuple<int, int, int>> tris;
526 for (
auto it = total_local_boundary.begin(); it != total_local_boundary.end(); ++it)
528 const auto &lb = *it;
529 const auto &gbs = gbases[lb.element_id()];
530 const auto &bs = bases[lb.element_id()];
532 for (
int k = 0; k < lb.size(); ++k)
536 case BoundaryType::TRI_LINE:
540 case BoundaryType::QUAD_LINE:
544 case BoundaryType::QUAD:
548 case BoundaryType::TRI:
552 case BoundaryType::PRISM:
556 case BoundaryType::POLYGON:
560 case BoundaryType::POLYHEDRON:
563 case BoundaryType::INVALID:
570 vertices.emplace_back();
571 lv.emplace_back(local_pts);
572 el_ids.push_back(lb.element_id());
573 global_primitive_ids.push_back(lb.global_primitive_id(k));
574 gbs.eval_geom_mapping(local_pts, vertices.back());
575 vals.compute(lb.element_id(), mesh.
is_volume(), local_pts, bs, gbs);
576 const int tris_start = tris.size();
580 const bool prism_quad = lb.type() == BoundaryType::PRISM && lb[k] >= 2;
581 const bool prism_tri = lb.type() == BoundaryType::PRISM && lb[k] < 2;
583 if (lb.type() == BoundaryType::QUAD || prism_quad)
585 const auto map = [n_samples, size](
int i,
int j) {
return j * n_samples + i + size; };
587 for (
int j = 0; j < n_samples - 1; ++j)
589 for (
int i = 0; i < n_samples - 1; ++i)
591 tris.emplace_back(map(i, j), map(i + 1, j), map(i, j + 1));
592 tris.emplace_back(map(i + 1, j + 1), map(i, j + 1), map(i + 1, j));
596 else if (lb.type() == BoundaryType::TRI || prism_tri)
599 std::vector<int> mapp(n_samples * n_samples, -1);
600 for (
int j = 0; j < n_samples; ++j)
602 for (
int i = 0; i < n_samples - j; ++i)
604 mapp[j * n_samples + i] = index;
608 const auto map = [mapp, n_samples](
int i,
int j) {
609 if (j * n_samples + i >= mapp.size())
611 return mapp[j * n_samples + i];
614 for (
int j = 0; j < n_samples - 1; ++j)
616 for (
int i = 0; i < n_samples - j; ++i)
618 if (map(i, j) >= 0 && map(i + 1, j) >= 0 && map(i, j + 1) >= 0)
619 tris.emplace_back(map(i, j) + size, map(i + 1, j) + size, map(i, j + 1) + size);
621 if (map(i + 1, j + 1) >= 0 && map(i, j + 1) >= 0 && map(i + 1, j) >= 0)
622 tris.emplace_back(map(i + 1, j + 1) + size, map(i, j + 1) + size, map(i + 1, j) + size);
633 for (
int i = 0; i < vertices.back().rows() - 1; ++i)
634 edges.emplace_back(i + size, i + size + 1);
637 normals.resize(
vals.jac_it.size(), tmp_n.cols());
638 displaced_normals.resize(
vals.jac_it.size(), tmp_n.cols());
640 for (
int n = 0; n <
vals.jac_it.size(); ++n)
642 trafo =
vals.jac_it[n].inverse();
644 if (problem_dim == 2 || problem_dim == 3)
647 if (solution.size() > 0)
649 deform_mat.resize(problem_dim, problem_dim);
650 deform_mat.setZero();
651 for (
const auto &b :
vals.basis_values)
652 for (
const auto &g : b.global)
653 for (
int d = 0; d < problem_dim; ++d)
654 deform_mat.row(d) += solution(g.index * problem_dim + d) * b.grad.row(n);
660 normals.row(n) = tmp_n *
vals.jac_it[n];
661 normals.row(n).normalize();
663 displaced_normals.row(n) = tmp_n * trafo.inverse();
664 displaced_normals.row(n).normalize();
667 allnormals.push_back(normals);
668 displaced_allnormals.push_back(displaced_normals);
671 for (
int n = 0; n <
vals.jac_it.size(); ++n)
673 tmp_n += normals.row(n);
678 Eigen::Vector3d e1 = vertices.back().row(std::get<1>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
679 Eigen::Vector3d e2 = vertices.back().row(std::get<2>(tris.back()) - size) - vertices.back().row(std::get<0>(tris.back()) - size);
681 Eigen::Vector3d n = e1.cross(e2);
682 Eigen::Vector3d nn = tmp_n.transpose();
686 for (
int i = tris_start; i < tris.size(); ++i)
688 tris[i] = std::tuple<int, int, int>(std::get<0>(tris[i]), std::get<2>(tris[i]), std::get<1>(tris[i]));
693 size += vertices.back().rows();
697 boundary_vis_vertices.resize(size, vertices.front().cols());
698 boundary_vis_local_vertices.resize(size, vertices.front().cols());
699 boundary_vis_elements_ids.resize(size, 1);
700 boundary_vis_primitive_ids.resize(size, 1);
701 boundary_vis_normals.resize(size, vertices.front().cols());
702 displaced_boundary_vis_normals.resize(size, vertices.front().cols());
705 boundary_vis_elements.resize(tris.size(), 3);
707 boundary_vis_elements.resize(edges.size(), 2);
711 for (
const auto &v : vertices)
713 boundary_vis_vertices.block(index, 0, v.rows(), v.cols()) = v;
714 boundary_vis_local_vertices.block(index, 0, v.rows(), v.cols()) = lv[ii];
715 boundary_vis_elements_ids.block(index, 0, v.rows(), 1).setConstant(el_ids[ii]);
716 boundary_vis_primitive_ids.block(index, 0, v.rows(), 1).setConstant(global_primitive_ids[ii++]);
721 for (
const auto &n : allnormals)
723 boundary_vis_normals.block(index, 0, n.rows(), n.cols()) = n;
728 for (
const auto &n : displaced_allnormals)
730 displaced_boundary_vis_normals.block(index, 0, n.rows(), n.cols()) = n;
737 for (
const auto &t : tris)
739 boundary_vis_elements.row(index) << std::get<0>(t), std::get<1>(t), std::get<2>(t);
745 for (
const auto &e : edges)
747 boundary_vis_elements.row(index) << e.first, e.second;
755 const Eigen::VectorXi &disc_orders,
756 const std::vector<basis::ElementBases> &gbases,
757 const std::map<int, Eigen::MatrixXd> &polys,
758 const std::map<
int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d,
759 const bool boundary_only,
760 Eigen::MatrixXd &points,
761 Eigen::MatrixXi &tets,
762 Eigen::MatrixXi &el_id,
763 Eigen::MatrixXd &discr)
const
767 const auto ¤t_bases = gbases;
768 int tet_total_size = 0;
769 int pts_total_size = 0;
771 Eigen::MatrixXd vis_pts_poly;
772 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
774 for (
size_t i = 0; i < current_bases.size(); ++i)
776 const auto &bs = current_bases[i];
784 pts_total_size += sampler.simplex_points().rows();
788 tet_total_size += sampler.cube_volume().rows();
789 pts_total_size += sampler.cube_points().rows();
793 tet_total_size += sampler.prism_volume().rows();
794 pts_total_size += sampler.prism_points().rows();
800 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
802 tet_total_size += vis_faces_poly.rows();
803 pts_total_size += vis_pts_poly.rows();
807 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
809 tet_total_size += vis_faces_poly.rows();
810 pts_total_size += vis_pts_poly.rows();
815 points.resize(pts_total_size, mesh.
dimension());
816 tets.resize(tet_total_size, mesh.
is_volume() ? 4 : 3);
818 el_id.resize(pts_total_size, 1);
819 discr.resize(pts_total_size, 1);
821 Eigen::MatrixXd mapped, tmp;
822 int tet_index = 0, pts_index = 0;
824 for (
size_t i = 0; i < current_bases.size(); ++i)
826 const auto &bs = current_bases[i];
833 bs.eval_geom_mapping(sampler.simplex_points(), mapped);
835 tets.block(tet_index, 0, sampler.simplex_volume().rows(), tets.cols()) = sampler.simplex_volume().array() + pts_index;
836 tet_index += sampler.simplex_volume().rows();
838 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
839 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
840 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
841 pts_index += mapped.rows();
845 bs.eval_geom_mapping(sampler.cube_points(), mapped);
847 tets.block(tet_index, 0, sampler.cube_volume().rows(), tets.cols()) = sampler.cube_volume().array() + pts_index;
848 tet_index += sampler.cube_volume().rows();
850 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
851 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
852 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
853 pts_index += mapped.rows();
857 bs.eval_geom_mapping(sampler.prism_points(), mapped);
859 tets.block(tet_index, 0, sampler.prism_volume().rows(), tets.cols()) = sampler.prism_volume().array() + pts_index;
860 tet_index += sampler.prism_volume().rows();
862 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
863 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(disc_orders(i));
864 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
865 pts_index += mapped.rows();
871 sampler.sample_polyhedron(polys_3d.at(i).first, polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
872 bs.eval_geom_mapping(vis_pts_poly, mapped);
874 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
875 tet_index += vis_faces_poly.rows();
877 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
878 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
879 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
880 pts_index += mapped.rows();
884 sampler.sample_polygon(polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
885 bs.eval_geom_mapping(vis_pts_poly, mapped);
887 tets.block(tet_index, 0, vis_faces_poly.rows(), tets.cols()) = vis_faces_poly.array() + pts_index;
888 tet_index += vis_faces_poly.rows();
890 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
891 discr.block(pts_index, 0, mapped.rows(), 1).setConstant(-1);
892 el_id.block(pts_index, 0, mapped.rows(), 1).setConstant(i);
893 pts_index += mapped.rows();
898 assert(pts_index == points.rows());
899 assert(tet_index == tets.rows());
904 const Eigen::VectorXi &disc_orders,
905 const Eigen::VectorXi &disc_ordersq,
906 const std::vector<basis::ElementBases> &bases,
907 Eigen::MatrixXd &points,
908 std::vector<std::vector<int>> &elements,
909 Eigen::MatrixXi &el_id,
910 Eigen::MatrixXd &discr)
const
924 std::vector<RowVectorNd> nodes;
925 int pts_total_size = 0;
926 elements.resize(bases.size());
927 Eigen::MatrixXd ref_pts;
929 for (
size_t i = 0; i < bases.size(); ++i)
931 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();
959 points.resize(pts_total_size, mesh.
dimension());
961 el_id.resize(pts_total_size, 1);
962 discr.resize(pts_total_size, 1);
964 Eigen::MatrixXd mapped;
967 std::string error_msg =
"";
969 for (
size_t i = 0; i < bases.size(); ++i)
971 const auto &bs = bases[i];
993 bs.eval_geom_mapping(ref_pts, mapped);
995 for (
int j = 0; j < mapped.rows(); ++j)
997 points.row(pts_index) = mapped.row(j);
998 el_id(pts_index) = i;
999 discr(pts_index) = disc_orders(i);
1000 elements[i].push_back(pts_index);
1009 const int n_nodes = elements[i].size();
1010 if (disc_orders(i) >= 3)
1012 std::swap(elements[i][16], elements[i][17]);
1013 std::swap(elements[i][17], elements[i][18]);
1014 std::swap(elements[i][18], elements[i][19]);
1016 if (disc_orders(i) > 4)
1017 error_msg =
"Saving high-order meshes not implemented for P5+ elements!";
1021 if (disc_orders(i) == 4)
1023 const int n_nodes = elements[i].size();
1024 std::swap(elements[i][n_nodes - 1], elements[i][n_nodes - 2]);
1026 if (disc_orders(i) > 4)
1027 error_msg =
"Saving high-order meshes not implemented for P5+ elements!";
1030 else if (disc_orders(i) > 1)
1031 error_msg =
"Saving high-order meshes not implemented for Q2+ elements!";
1034 if (!error_msg.empty())
1035 logger().warn(error_msg);
1037 for (
size_t i = 0; i < bases.size(); ++i)
1042 const auto &mesh2d =
static_cast<const mesh::Mesh2D &
>(mesh);
1045 for (
int j = 0; j < n_v; ++j)
1047 points.row(pts_index) = mesh2d.point(mesh2d.face_vertex(i, j));
1048 el_id(pts_index) = i;
1049 discr(pts_index) = disc_orders(i);
1050 elements[i].push_back(pts_index);
1056 assert(pts_index == points.rows());
1061 const Eigen::MatrixXd &sol,
1062 const Eigen::MatrixXd &pressure,
1063 const bool is_time_dependent,
1064 const double tend_in,
1067 const std::string &vis_mesh_path,
1068 const std::string &nodes_path,
1069 const std::string &solution_path,
1070 const std::string &stress_path,
1071 const std::string &mises_path,
1072 const bool is_contact_enabled)
const
1076 logger().error(
"Load the mesh first!");
1079 const int n_bases = state.
n_bases;
1080 const std::vector<basis::ElementBases> &bases = state.
bases;
1081 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
1084 const Eigen::MatrixXd &rhs = state.
rhs;
1089 logger().error(
"Build the bases first!");
1097 if (sol.size() <= 0)
1099 logger().error(
"Solve the problem first!");
1103 if (!solution_path.empty())
1105 logger().info(
"Saving solution to {}", solution_path);
1107 std::ofstream out(solution_path);
1109 out << std::scientific;
1113 Eigen::VectorXi reordering(n_bases);
1114 reordering.setConstant(-1);
1116 for (
int i = 0; i < in_node_to_node.size(); ++i)
1118 reordering[in_node_to_node[i]] = i;
1121 Eigen::MatrixXd tmp(tmp_sol.rows(), tmp_sol.cols());
1123 for (
int i = 0; i < reordering.size(); ++i)
1125 if (reordering[i] < 0)
1128 tmp.row(reordering[i]) = tmp_sol.row(i);
1131 for (
int i = 0; i < tmp.rows(); ++i)
1133 for (
int j = 0; j < tmp.cols(); ++j)
1134 out << tmp(i, j) <<
" ";
1140 out << sol << std::endl;
1144 double tend = tend_in;
1148 if (!vis_mesh_path.empty() && !is_time_dependent)
1151 vis_mesh_path, state, sol, pressure,
1153 is_contact_enabled);
1155 if (!nodes_path.empty())
1157 Eigen::MatrixXd nodes(n_bases, mesh.
dimension());
1163 for (
size_t ii = 0; ii < b.global().size(); ++ii)
1165 const auto &lg = b.global()[ii];
1166 nodes.row(lg.index) = lg.node;
1170 std::ofstream out(nodes_path);
1175 if (!stress_path.empty())
1177 Eigen::MatrixXd result;
1178 Eigen::VectorXd mises;
1182 sol, tend, result, mises);
1183 std::ofstream out(stress_path);
1187 if (!mises_path.empty())
1189 Eigen::MatrixXd result;
1190 Eigen::VectorXd mises;
1194 sol, tend, result, mises);
1195 std::ofstream out(mises_path);
1208 fields = args[
"output"][
"paraview"][
"fields"];
1210 volume = args[
"output"][
"paraview"][
"volume"];
1211 surface = args[
"output"][
"paraview"][
"surface"];
1212 wire = args[
"output"][
"paraview"][
"wireframe"];
1213 points = args[
"output"][
"paraview"][
"points"];
1214 contact_forces = args[
"output"][
"paraview"][
"options"][
"contact_forces"] && !is_problem_scalar;
1215 friction_forces = args[
"output"][
"paraview"][
"options"][
"friction_forces"] && !is_problem_scalar;
1216 normal_adhesion_forces = args[
"output"][
"paraview"][
"options"][
"normal_adhesion_forces"] && !is_problem_scalar;
1217 tangential_adhesion_forces = args[
"output"][
"paraview"][
"options"][
"tangential_adhesion_forces"] && !is_problem_scalar;
1219 if (args[
"output"][
"paraview"][
"options"][
"force_high_order"])
1220 use_sampler =
false;
1222 use_sampler = !(is_mesh_linear && args[
"output"][
"paraview"][
"high_order_mesh"]);
1223 if (mesh_has_prisms)
1226 boundary_only = use_sampler && args[
"output"][
"advanced"][
"vis_boundary_only"];
1227 material_params = args[
"output"][
"paraview"][
"options"][
"material"];
1228 body_ids = args[
"output"][
"paraview"][
"options"][
"body_ids"];
1229 sol_on_grid = args[
"output"][
"advanced"][
"sol_on_grid"] > 0;
1230 velocity = args[
"output"][
"paraview"][
"options"][
"velocity"];
1231 acceleration = args[
"output"][
"paraview"][
"options"][
"acceleration"];
1232 forces = args[
"output"][
"paraview"][
"options"][
"forces"] && !is_problem_scalar;
1233 jacobian_validity = args[
"output"][
"paraview"][
"options"][
"jacobian_validity"] && !is_problem_scalar;
1235 scalar_values = args[
"output"][
"paraview"][
"options"][
"scalar_values"];
1236 tensor_values = args[
"output"][
"paraview"][
"options"][
"tensor_values"] && !is_problem_scalar;
1237 discretization_order = args[
"output"][
"paraview"][
"options"][
"discretization_order"];
1238 nodes = args[
"output"][
"paraview"][
"options"][
"nodes"] && !is_problem_scalar;
1240 use_spline = args[
"space"][
"basis_type"] ==
"Spline";
1242 reorder_output = args[
"output"][
"data"][
"advanced"][
"reorder_nodes"];
1244 use_hdf5 = args[
"output"][
"paraview"][
"options"][
"use_hdf5"];
1248 const std::string &path,
1250 const Eigen::MatrixXd &sol,
1251 const Eigen::MatrixXd &pressure,
1255 const bool is_contact_enabled)
const
1259 logger().error(
"Load the mesh first!");
1263 const Eigen::MatrixXd &rhs = state.
rhs;
1267 logger().error(
"Build the bases first!");
1275 if (sol.size() <= 0)
1277 logger().error(
"Solve the problem first!");
1283 logger().info(
"Saving vtu to {}; volume={}, surface={}, contact={}, points={}, wireframe={}",
1286 const std::filesystem::path fs_path(path);
1287 const std::string path_stem = fs_path.stem().string();
1288 const std::string base_path = (fs_path.parent_path() / path_stem).
string();
1298 is_contact_enabled);
1304 is_contact_enabled);
1317 paraviewo::VTMWriter vtm(t);
1319 vtm.add_dataset(
"Volume",
"data", path_stem + opts.
file_extension());
1321 vtm.add_dataset(
"Surface",
"data", path_stem +
"_surf" + opts.
file_extension());
1323 vtm.add_dataset(
"Contact",
"data", path_stem +
"_surf_contact" + opts.
file_extension());
1325 vtm.add_dataset(
"Wireframe",
"data", path_stem +
"_wire" + opts.
file_extension());
1327 vtm.add_dataset(
"Points",
"data", path_stem +
"_points" + opts.
file_extension());
1328 vtm.save(base_path +
".vtm");
1332 const std::string &path,
1334 const Eigen::MatrixXd &sol,
1335 const Eigen::MatrixXd &pressure,
1340 const Eigen::VectorXi &disc_orders = state.
disc_orders;
1341 const Eigen::VectorXi &disc_ordersq = state.
disc_ordersq;
1343 const std::vector<basis::ElementBases> &bases = state.
bases;
1344 const std::vector<basis::ElementBases> &pressure_bases = state.
pressure_bases;
1345 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
1346 const std::map<int, Eigen::MatrixXd> &polys = state.
polys;
1347 const std::map<int, std::pair<Eigen::MatrixXd, Eigen::MatrixXi>> &polys_3d = state.
polys_3d;
1354 Eigen::MatrixXd points;
1355 Eigen::MatrixXi tets;
1356 Eigen::MatrixXi el_id;
1357 Eigen::MatrixXd discr;
1358 std::vector<std::vector<int>> elements;
1363 points, tets, el_id, discr);
1366 points, elements, el_id, discr);
1368 Eigen::MatrixXd fun, exact_fun, err, node_fun;
1373 Eigen::MatrixXd tmp, tmp_grad;
1374 Eigen::MatrixXd tmp_p, tmp_grad_p;
1376 res.setConstant(std::numeric_limits<double>::quiet_NaN());
1378 res_grad.setConstant(std::numeric_limits<double>::quiet_NaN());
1381 res_p.setConstant(std::numeric_limits<double>::quiet_NaN());
1383 res_grad_p.setConstant(std::numeric_limits<double>::quiet_NaN());
1392 Eigen::MatrixXd pt(1, bc.cols() - 1);
1393 for (
int d = 1; d < bc.cols(); ++d)
1396 mesh, problem.
is_scalar(), bases, gbases,
1397 el_id, pt, sol, tmp, tmp_grad);
1400 res_grad.row(i) = tmp_grad;
1405 mesh, 1, pressure_bases, gbases,
1406 el_id, pt, pressure, tmp_p, tmp_grad_p);
1407 res_p.row(i) = tmp_p;
1408 res_grad_p.row(i) = tmp_grad_p;
1412 std::ofstream os(path +
"_sol.txt");
1415 std::ofstream osg(path +
"_grad.txt");
1418 std::ofstream osgg(path +
"_grid.txt");
1423 std::ofstream osp(path +
"_p_sol.txt");
1426 std::ofstream osgp(path +
"_p_grad.txt");
1431 Eigen::Vector<bool, -1> validity;
1439 mesh, problem.
is_scalar(), bases, disc_orders, disc_ordersq,
1444 Eigen::MatrixXd tmp = Eigen::VectorXd::LinSpaced(sol.size(), 0, sol.size() - 1);
1447 mesh, problem.
is_scalar(), bases, disc_orders, disc_ordersq,
1454 fun.conservativeResize(fun.rows() + obstacle.
n_vertices(), fun.cols());
1455 node_fun.conservativeResize(node_fun.rows() + obstacle.
n_vertices(), node_fun.cols());
1456 node_fun.bottomRows(obstacle.
n_vertices()).setZero();
1464 problem.
exact(points, t, exact_fun);
1465 err = (fun - exact_fun).eval().rowwise().norm();
1469 exact_fun.conservativeResize(exact_fun.rows() + obstacle.
n_vertices(), exact_fun.cols());
1473 err.conservativeResize(err.rows() + obstacle.
n_vertices(), 1);
1474 err.bottomRows(obstacle.
n_vertices()).setZero();
1478 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
1480 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
1482 tmpw = std::make_shared<paraviewo::VTUWriter>();
1483 paraviewo::ParaviewWriter &writer = *tmpw;
1486 writer.add_field(
"validity", validity.cast<
double>());
1489 writer.add_field(
"nodes", node_fun);
1493 bool is_time_integrator_valid = time_integrator !=
nullptr;
1497 const Eigen::VectorXd velocity =
1498 is_time_integrator_valid ? (time_integrator->v_prev()) : Eigen::VectorXd::Zero(sol.size());
1504 const Eigen::VectorXd acceleration =
1505 is_time_integrator_valid ? (time_integrator->a_prev()) : Eigen::VectorXd::Zero(sol.size());
1519 if (form ==
nullptr)
1522 Eigen::VectorXd force;
1523 if (form->enabled())
1525 form->first_derivative(sol, force);
1530 force.setZero(sol.size());
1540 Eigen::MatrixXd interp_p;
1548 interp_p.conservativeResize(interp_p.size() + obstacle.
n_vertices(), 1);
1549 interp_p.bottomRows(obstacle.
n_vertices()).setZero();
1552 writer.add_field(
"pressure", interp_p);
1557 discr.conservativeResize(discr.size() + obstacle.
n_vertices(), 1);
1558 discr.bottomRows(obstacle.
n_vertices()).setZero();
1562 writer.add_field(
"discr", discr);
1567 writer.add_field(
"exact", exact_fun);
1569 writer.add_field(
"error", err);
1572 if (fun.cols() != 1)
1574 std::vector<assembler::Assembler::NamedMatrix>
vals, tvals;
1576 mesh, problem.
is_scalar(), bases, gbases,
1581 for (
auto &[_, v] :
vals)
1586 for (
const auto &[name, v] :
vals)
1589 writer.add_field(name, v);
1596 mesh, problem.
is_scalar(), bases, gbases, disc_orders, disc_ordersq,
1600 for (
auto &[_, v] : tvals)
1603 for (
const auto &[name, v] : tvals)
1606 assert(v.cols() % stride == 0);
1611 for (
int i = 0; i < v.cols(); i += stride)
1613 const Eigen::MatrixXd tmp = v.middleCols(i, stride);
1614 assert(tmp.cols() == stride);
1616 const int ii = (i / stride) + 1;
1617 writer.add_field(fmt::format(
"{:s}_{:d}", name, ii), tmp);
1632 for (
auto &v :
vals)
1634 v.second.conservativeResize(v.second.size() + obstacle.
n_vertices(), 1);
1635 v.second.bottomRows(obstacle.
n_vertices()).setZero();
1641 for (
const auto &v :
vals)
1643 if (opts.
export_field(fmt::format(
"{:s}_avg", v.first)))
1644 writer.add_field(fmt::format(
"{:s}_avg", v.first), v.second);
1654 std::map<std::string, Eigen::MatrixXd> param_val;
1655 for (
const auto &[p, _] : params)
1656 param_val[p] = Eigen::MatrixXd(points.rows(), 1);
1657 Eigen::MatrixXd rhos(points.rows(), 1);
1659 Eigen::MatrixXd local_pts;
1660 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
1664 for (
int e = 0; e < int(bases.size()); ++e)
1672 local_pts = sampler.simplex_points();
1674 local_pts = sampler.cube_points();
1676 local_pts = sampler.prism_points();
1680 sampler.sample_polyhedron(polys_3d.at(e).first, polys_3d.at(e).second, local_pts, vis_faces_poly, vis_edges_poly);
1682 sampler.sample_polygon(polys.at(e), local_pts, vis_faces_poly, vis_edges_poly);
1706 const auto &mesh2d =
static_cast<const mesh::Mesh2D &
>(mesh);
1708 local_pts.resize(n_v, 2);
1710 for (
int j = 0; j < n_v; ++j)
1712 local_pts.row(j) = mesh2d.point(mesh2d.face_vertex(e, j));
1721 for (
int j = 0; j <
vals.val.rows(); ++j)
1723 for (
const auto &[p, func] : params)
1724 param_val.at(p)(index) = func(local_pts.row(j),
vals.val.row(j), t, e);
1726 rhos(index) = density(local_pts.row(j),
vals.val.row(j), t, e);
1732 assert(index == points.rows());
1736 for (
auto &[_, tmp] : param_val)
1738 tmp.conservativeResize(tmp.size() + obstacle.
n_vertices(), 1);
1739 tmp.bottomRows(obstacle.
n_vertices()).setZero();
1742 rhos.conservativeResize(rhos.size() + obstacle.
n_vertices(), 1);
1743 rhos.bottomRows(obstacle.
n_vertices()).setZero();
1745 for (
const auto &[p, tmp] : param_val)
1748 writer.add_field(p, tmp);
1751 writer.add_field(
"rho", rhos);
1757 Eigen::MatrixXd ids(points.rows(), 1);
1759 for (
int i = 0; i < points.rows(); ++i)
1766 ids.conservativeResize(ids.size() + obstacle.
n_vertices(), 1);
1767 ids.bottomRows(obstacle.
n_vertices()).setZero();
1770 writer.add_field(
"body_ids", ids);
1781 Eigen::MatrixXd traction_forces, traction_forces_fun;
1782 compute_traction_forces(state, sol, t, traction_forces,
false);
1785 mesh, problem.
is_scalar(), bases, disc_orders, disc_ordersq,
1791 traction_forces_fun.conservativeResize(traction_forces_fun.rows() + obstacle.
n_vertices(), traction_forces_fun.cols());
1792 traction_forces_fun.bottomRows(obstacle.
n_vertices()).setZero();
1795 writer.add_field(
"traction_force", traction_forces_fun);
1802 Eigen::VectorXd potential_grad;
1803 Eigen::MatrixXd potential_grad_fun;
1808 mesh, problem.
is_scalar(), bases, disc_orders, disc_ordersq,
1814 potential_grad_fun.conservativeResize(potential_grad_fun.rows() + obstacle.
n_vertices(), potential_grad_fun.cols());
1815 potential_grad_fun.bottomRows(obstacle.
n_vertices()).setZero();
1818 writer.add_field(
"gradient_of_elastic_potential", potential_grad_fun);
1820 catch (std::exception &)
1829 Eigen::VectorXd potential_grad;
1830 Eigen::MatrixXd potential_grad_fun;
1843 potential_grad_fun.conservativeResize(potential_grad_fun.rows() + obstacle.
n_vertices(), potential_grad_fun.cols());
1844 potential_grad_fun.bottomRows(obstacle.
n_vertices()).setZero();
1847 writer.add_field(
"gradient_of_contact_potential", potential_grad_fun);
1850 catch (std::exception &)
1856 writer.add_field(
"solution", fun);
1860 const int orig_p = points.rows();
1861 points.conservativeResize(points.rows() + obstacle.
n_vertices(), points.cols());
1862 points.bottomRows(obstacle.
n_vertices()) = obstacle.
v();
1864 if (elements.empty())
1866 for (
int i = 0; i < tets.rows(); ++i)
1868 elements.emplace_back();
1869 for (
int j = 0; j < tets.cols(); ++j)
1870 elements.back().push_back(tets(i, j));
1876 elements.emplace_back();
1883 elements.emplace_back();
1890 elements.emplace_back();
1895 if (elements.empty())
1896 writer.write_mesh(path, points, tets);
1898 writer.write_mesh(path, points, elements,
true, disc_orders.maxCoeff() == 1);
1903 const Eigen::MatrixXd &points,
1905 const std::string &name,
1906 const Eigen::VectorXd &field,
1907 paraviewo::ParaviewWriter &writer)
const
1909 Eigen::MatrixXd inerpolated_field;
1917 inerpolated_field.conservativeResize(
1923 writer.add_field(name, inerpolated_field);
1927 const std::string &export_surface,
1929 const Eigen::MatrixXd &sol,
1930 const Eigen::MatrixXd &pressure,
1934 const bool is_contact_enabled)
const
1937 const Eigen::VectorXi &disc_orders = state.
disc_orders;
1939 const std::vector<basis::ElementBases> &bases = state.
bases;
1940 const std::vector<basis::ElementBases> &pressure_bases = state.
pressure_bases;
1941 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
1947 Eigen::MatrixXd boundary_vis_vertices;
1948 Eigen::MatrixXd boundary_vis_local_vertices;
1949 Eigen::MatrixXi boundary_vis_elements;
1950 Eigen::MatrixXi boundary_vis_elements_ids;
1951 Eigen::MatrixXi boundary_vis_primitive_ids;
1952 Eigen::MatrixXd boundary_vis_normals;
1953 Eigen::MatrixXd displaced_boundary_vis_normals;
1956 boundary_vis_vertices, boundary_vis_local_vertices, boundary_vis_elements,
1957 boundary_vis_elements_ids, boundary_vis_primitive_ids, boundary_vis_normals,
1958 displaced_boundary_vis_normals);
1960 Eigen::MatrixXd fun, interp_p, discr, vect, b_sidesets;
1962 Eigen::MatrixXd lsol, lp, lgrad, lpgrad;
1968 discr.resize(boundary_vis_vertices.rows(), 1);
1969 fun.resize(boundary_vis_vertices.rows(), actual_dim);
1970 interp_p.resize(boundary_vis_vertices.rows(), 1);
1971 vect.resize(boundary_vis_vertices.rows(), mesh.
dimension());
1973 b_sidesets.resize(boundary_vis_vertices.rows(), 1);
1974 b_sidesets.setZero();
1976 for (
int i = 0; i < boundary_vis_vertices.rows(); ++i)
1978 const auto s_id = mesh.
get_boundary_id(boundary_vis_primitive_ids(i));
1981 b_sidesets(i) = s_id;
1984 const int el_index = boundary_vis_elements_ids(i);
1986 mesh, problem.
is_scalar(), bases, gbases,
1987 el_index, boundary_vis_local_vertices.row(i), sol, lsol, lgrad);
1988 assert(lsol.size() == actual_dim);
1992 mesh, 1, pressure_bases, gbases,
1993 el_index, boundary_vis_local_vertices.row(i), pressure, lp, lpgrad);
1994 assert(lp.size() == 1);
1995 interp_p(i) = lp(0);
1998 discr(i) = disc_orders(el_index);
1999 for (
int j = 0; j < actual_dim; ++j)
2001 fun(i, j) = lsol(j);
2004 if (actual_dim == 1)
2006 assert(lgrad.size() == mesh.
dimension());
2007 for (
int j = 0; j < mesh.
dimension(); ++j)
2009 vect(i, j) = lgrad(j);
2014 assert(lgrad.size() == actual_dim * actual_dim);
2015 std::vector<assembler::Assembler::NamedMatrix> tensor_flat;
2020 assert(tensor_flat[0].first ==
"cauchy_stess");
2021 assert(tensor_flat[0].second.size() == actual_dim * actual_dim);
2023 Eigen::Map<Eigen::MatrixXd> tensor(tensor_flat[0].second.data(), actual_dim, actual_dim);
2024 vect.row(i) = displaced_boundary_vis_normals.row(i) * tensor;
2030 area = mesh.
tri_area(boundary_vis_primitive_ids(i));
2031 else if (mesh.
is_cube(el_index))
2032 area = mesh.
quad_area(boundary_vis_primitive_ids(i));
2035 const int tmp = boundary_vis_primitive_ids(i);
2040 area = mesh.
edge_length(boundary_vis_primitive_ids(i));
2042 vect.row(i) *= area;
2046 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2048 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2050 tmpw = std::make_shared<paraviewo::VTUWriter>();
2051 paraviewo::ParaviewWriter &writer = *tmpw;
2054 writer.add_field(
"normals", boundary_vis_normals);
2056 writer.add_field(
"displaced_normals", displaced_boundary_vis_normals);
2058 writer.add_field(
"pressure", interp_p);
2060 writer.add_field(
"discr", discr);
2062 writer.add_field(
"sidesets", b_sidesets);
2064 if (actual_dim == 1 && opts.
export_field(
"solution_grad"))
2065 writer.add_field(
"solution_grad", vect);
2068 writer.add_field(
"traction_force", vect);
2075 std::map<std::string, Eigen::MatrixXd> param_val;
2076 for (
const auto &[p, _] : params)
2077 param_val[p] = Eigen::MatrixXd(boundary_vis_vertices.rows(), 1);
2078 Eigen::MatrixXd rhos(boundary_vis_vertices.rows(), 1);
2080 for (
int i = 0; i < boundary_vis_vertices.rows(); ++i)
2084 for (
const auto &[p, func] : params)
2085 param_val.at(p)(i) = func(boundary_vis_local_vertices.row(i), boundary_vis_vertices.row(i), t, boundary_vis_elements_ids(i));
2087 rhos(i) = density(boundary_vis_local_vertices.row(i), boundary_vis_vertices.row(i), t, boundary_vis_elements_ids(i));
2090 for (
const auto &[p, tmp] : param_val)
2093 writer.add_field(p, tmp);
2096 writer.add_field(
"rho", rhos);
2102 Eigen::MatrixXd ids(boundary_vis_vertices.rows(), 1);
2104 for (
int i = 0; i < boundary_vis_vertices.rows(); ++i)
2106 ids(i) = mesh.
get_body_id(boundary_vis_elements_ids(i));
2109 writer.add_field(
"body_ids", ids);
2113 writer.add_field(
"solution", fun);
2114 writer.write_mesh(export_surface, boundary_vis_vertices, boundary_vis_elements);
2118 const std::string &export_surface,
2120 const Eigen::MatrixXd &sol,
2121 const Eigen::MatrixXd &pressure,
2125 const bool is_contact_enabled)
const
2129 const double dhat = state.
args[
"contact"][
"dhat"];
2130 const double friction_coefficient = state.
args[
"contact"][
"friction_coefficient"];
2131 const double epsv = state.
args[
"contact"][
"epsv"];
2132 const double dhat_a = state.
args[
"contact"][
"adhesion"][
"dhat_a"];
2133 const double dhat_p = state.
args[
"contact"][
"adhesion"][
"dhat_p"];
2134 const double Y = state.
args[
"contact"][
"adhesion"][
"adhesion_strength"];
2135 const double epsa = state.
args[
"contact"][
"adhesion"][
"epsa"];
2136 const double tangential_adhesion_coefficient = state.
args[
"contact"][
"adhesion"][
"tangential_adhesion_coefficient"];
2142 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2144 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2146 tmpw = std::make_shared<paraviewo::VTUWriter>();
2147 paraviewo::ParaviewWriter &writer = *tmpw;
2149 const int problem_dim = mesh.
dimension();
2150 const Eigen::MatrixXd full_displacements =
utils::unflatten(sol, problem_dim);
2151 const Eigen::MatrixXd surface_displacements = collision_mesh.map_displacements(full_displacements);
2153 const Eigen::MatrixXd displaced_surface = collision_mesh.displace_vertices(full_displacements);
2155 ipc::NormalCollisions collision_set;
2157 if (state.
args[
"contact"][
"use_convergent_formulation"])
2159 collision_set.set_use_area_weighting(state.
args[
"contact"][
"use_area_weighting"]);
2160 collision_set.set_use_improved_max_approximator(state.
args[
"contact"][
"use_improved_max_operator"]);
2163 collision_set.build(
2164 collision_mesh, displaced_surface, dhat,
2165 0, ipc::create_broad_phase(state.
args[
"solver"][
"contact"][
"CCD"][
"broad_phase"]));
2167 ipc::BarrierPotential barrier_potential(dhat);
2168 if (state.
args[
"contact"][
"use_convergent_formulation"])
2170 barrier_potential.set_use_physical_barrier(state.
args[
"contact"][
"use_physical_barrier"]);
2173 const double barrier_stiffness = contact_form !=
nullptr ? contact_form->barrier_stiffness() : 1;
2177 Eigen::MatrixXd forces = -barrier_stiffness * barrier_potential.gradient(collision_set, collision_mesh, displaced_surface);
2181 assert(forces_reshaped.rows() == surface_displacements.rows());
2182 assert(forces_reshaped.cols() == surface_displacements.cols());
2183 writer.add_field(
"contact_forces", forces_reshaped);
2186 if (contact_form && state.
args[
"contact"][
"use_gcp_formulation"] && state.
args[
"contact"][
"use_adaptive_dhat"] && opts.
export_field(
"adaptive_dhat"))
2188 const auto form = std::dynamic_pointer_cast<solver::SmoothContactForm>(contact_form);
2190 const auto &set = form->collision_set();
2192 if (problem_dim == 2)
2194 Eigen::VectorXd dhats(collision_mesh.num_edges());
2195 dhats.setConstant(dhat);
2196 for (
int e = 0; e < dhats.size(); e++)
2197 dhats(e) = set.get_edge_dhat(e);
2199 writer.add_cell_field(
"dhat", dhats);
2203 Eigen::VectorXd fdhats(collision_mesh.num_faces());
2204 fdhats.setConstant(dhat);
2205 for (
int e = 0; e < fdhats.size(); e++)
2206 fdhats(e) = set.get_face_dhat(e);
2208 writer.add_cell_field(
"dhat_face", fdhats);
2210 Eigen::VectorXd vdhats(collision_mesh.num_vertices());
2211 vdhats.setConstant(dhat);
2212 for (
int i = 0; i < vdhats.size(); i++)
2213 vdhats(i) = set.get_vert_dhat(i);
2215 writer.add_field(
"dhat_vert", vdhats);
2221 ipc::TangentialCollisions friction_collision_set;
2222 friction_collision_set.build(
2223 collision_mesh, displaced_surface, collision_set,
2224 barrier_potential, barrier_stiffness, friction_coefficient);
2226 ipc::FrictionPotential friction_potential(epsv);
2228 Eigen::MatrixXd velocities;
2233 velocities = collision_mesh.map_displacements(
utils::unflatten(velocities, collision_mesh.dim()));
2235 Eigen::MatrixXd forces = -friction_potential.gradient(
2236 friction_collision_set, collision_mesh, velocities);
2240 assert(forces_reshaped.rows() == surface_displacements.rows());
2241 assert(forces_reshaped.cols() == surface_displacements.cols());
2242 writer.add_field(
"friction_forces", forces_reshaped);
2245 ipc::NormalCollisions adhesion_collision_set;
2246 adhesion_collision_set.build(
2247 collision_mesh, displaced_surface, dhat_a,
2248 0, ipc::create_broad_phase(state.
args[
"solver"][
"contact"][
"CCD"][
"broad_phase"]));
2250 ipc::NormalAdhesionPotential normal_adhesion_potential(dhat_p, dhat_a, Y, 1);
2254 Eigen::MatrixXd forces = -1 * normal_adhesion_potential.gradient(adhesion_collision_set, collision_mesh, displaced_surface);
2258 assert(forces_reshaped.rows() == surface_displacements.rows());
2259 assert(forces_reshaped.cols() == surface_displacements.cols());
2260 writer.add_field(
"normal_adhesion_forces", forces_reshaped);
2265 ipc::TangentialCollisions tangential_collision_set;
2266 tangential_collision_set.build(
2267 collision_mesh, displaced_surface, adhesion_collision_set,
2268 normal_adhesion_potential, 1, tangential_adhesion_coefficient);
2270 ipc::TangentialAdhesionPotential tangential_adhesion_potential(epsa);
2272 Eigen::MatrixXd velocities;
2277 velocities = collision_mesh.map_displacements(
utils::unflatten(velocities, collision_mesh.dim()));
2279 Eigen::MatrixXd forces = -tangential_adhesion_potential.gradient(
2280 tangential_collision_set, collision_mesh, velocities);
2284 assert(forces_reshaped.rows() == surface_displacements.rows());
2285 assert(forces_reshaped.cols() == surface_displacements.cols());
2286 writer.add_field(
"tangential_adhesion_forces", forces_reshaped);
2289 assert(collision_mesh.rest_positions().rows() == surface_displacements.rows());
2290 assert(collision_mesh.rest_positions().cols() == surface_displacements.cols());
2293 writer.add_field(
"solution", surface_displacements);
2296 export_surface.substr(0, export_surface.length() - 4) +
"_contact.vtu",
2297 collision_mesh.rest_positions(),
2298 problem_dim == 3 ? collision_mesh.faces() : collision_mesh.edges());
2302 const std::string &name,
2304 const Eigen::MatrixXd &sol,
2308 const std::vector<basis::ElementBases> &gbases = state.
geom_bases();
2314 Eigen::MatrixXi vis_faces_poly, vis_edges_poly;
2315 Eigen::MatrixXd vis_pts_poly;
2317 const auto ¤t_bases = gbases;
2318 int seg_total_size = 0;
2319 int pts_total_size = 0;
2320 int faces_total_size = 0;
2322 for (
size_t i = 0; i < current_bases.size(); ++i)
2324 const auto &bs = current_bases[i];
2329 seg_total_size += sampler.simplex_edges().rows();
2330 faces_total_size += sampler.simplex_faces().rows();
2334 pts_total_size += sampler.cube_points().rows();
2335 seg_total_size += sampler.cube_edges().rows();
2336 faces_total_size += sampler.cube_faces().rows();
2340 pts_total_size += sampler.prism_points().rows();
2341 seg_total_size += sampler.prism_edges().rows();
2342 faces_total_size += sampler.prism_faces().rows();
2347 sampler.sample_polyhedron(state.
polys_3d.at(i).first, state.
polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
2349 sampler.sample_polygon(state.
polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
2351 pts_total_size += vis_pts_poly.rows();
2352 seg_total_size += vis_edges_poly.rows();
2353 faces_total_size += vis_faces_poly.rows();
2357 Eigen::MatrixXd points(pts_total_size, mesh.
dimension());
2358 Eigen::MatrixXi edges(seg_total_size, 2);
2359 Eigen::MatrixXi
faces(faces_total_size, 3);
2362 Eigen::MatrixXd mapped, tmp;
2363 int seg_index = 0, pts_index = 0, face_index = 0;
2364 for (
size_t i = 0; i < current_bases.size(); ++i)
2366 const auto &bs = current_bases[i];
2370 bs.eval_geom_mapping(sampler.simplex_points(), mapped);
2371 edges.block(seg_index, 0, sampler.simplex_edges().rows(), edges.cols()) = sampler.simplex_edges().array() + pts_index;
2372 seg_index += sampler.simplex_edges().rows();
2374 faces.block(face_index, 0, sampler.simplex_faces().rows(), 3) = sampler.simplex_faces().array() + pts_index;
2375 face_index += sampler.simplex_faces().rows();
2377 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
2378 pts_index += mapped.rows();
2382 bs.eval_geom_mapping(sampler.cube_points(), mapped);
2383 edges.block(seg_index, 0, sampler.cube_edges().rows(), edges.cols()) = sampler.cube_edges().array() + pts_index;
2384 seg_index += sampler.cube_edges().rows();
2386 faces.block(face_index, 0, sampler.cube_faces().rows(), 3) = sampler.cube_faces().array() + pts_index;
2387 face_index += sampler.cube_faces().rows();
2389 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
2390 pts_index += mapped.rows();
2394 bs.eval_geom_mapping(sampler.prism_points(), mapped);
2395 edges.block(seg_index, 0, sampler.prism_edges().rows(), edges.cols()) = sampler.prism_edges().array() + pts_index;
2396 seg_index += sampler.prism_edges().rows();
2398 faces.block(face_index, 0, sampler.prism_faces().rows(), 3) = sampler.prism_faces().array() + pts_index;
2399 face_index += sampler.prism_faces().rows();
2401 points.block(pts_index, 0, mapped.rows(), points.cols()) = mapped;
2402 pts_index += mapped.rows();
2407 sampler.sample_polyhedron(state.
polys_3d.at(i).first, state.
polys_3d.at(i).second, vis_pts_poly, vis_faces_poly, vis_edges_poly);
2409 sampler.sample_polygon(state.
polys.at(i), vis_pts_poly, vis_faces_poly, vis_edges_poly);
2411 edges.block(seg_index, 0, vis_edges_poly.rows(), edges.cols()) = vis_edges_poly.array() + pts_index;
2412 seg_index += vis_edges_poly.rows();
2414 faces.block(face_index, 0, vis_faces_poly.rows(), 3) = vis_faces_poly.array() + pts_index;
2415 face_index += vis_faces_poly.rows();
2417 points.block(pts_index, 0, vis_pts_poly.rows(), points.cols()) = vis_pts_poly;
2418 pts_index += vis_pts_poly.rows();
2422 assert(pts_index == points.rows());
2423 assert(face_index ==
faces.rows());
2428 for (
long i = 0; i <
faces.rows(); ++i)
2430 const int v0 =
faces(i, 0);
2431 const int v1 =
faces(i, 1);
2432 const int v2 =
faces(i, 2);
2434 int tmpc =
faces(i, 2);
2441 Eigen::Matrix2d mmat;
2442 for (
long i = 0; i <
faces.rows(); ++i)
2444 const int v0 =
faces(i, 0);
2445 const int v1 =
faces(i, 1);
2446 const int v2 =
faces(i, 2);
2448 mmat.row(0) = points.row(v2) - points.row(v0);
2449 mmat.row(1) = points.row(v1) - points.row(v0);
2451 if (mmat.determinant() > 0)
2453 int tmpc =
faces(i, 2);
2460 Eigen::MatrixXd fun;
2464 pts_index, sol, fun,
true,
false);
2466 Eigen::MatrixXd exact_fun, err;
2470 problem.
exact(points, t, exact_fun);
2471 err = (fun - exact_fun).eval().rowwise().norm();
2474 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2476 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2478 tmpw = std::make_shared<paraviewo::VTUWriter>();
2479 paraviewo::ParaviewWriter &writer = *tmpw;
2484 writer.add_field(
"exact", exact_fun);
2486 writer.add_field(
"error", err);
2489 if (fun.cols() != 1)
2491 std::vector<assembler::Assembler::NamedMatrix> scalar_val;
2497 for (
const auto &v : scalar_val)
2500 writer.add_field(v.first, v.second);
2504 writer.add_field(
"solution", fun);
2506 writer.write_mesh(name, points, edges);
2510 const std::string &path,
2512 const Eigen::MatrixXd &sol,
2524 Eigen::MatrixXd fun(dirichlet_nodes_position.size(), actual_dim);
2525 Eigen::MatrixXd b_sidesets(dirichlet_nodes_position.size(), 1);
2526 b_sidesets.setZero();
2527 Eigen::MatrixXd points(dirichlet_nodes_position.size(), mesh.
dimension());
2528 std::vector<std::vector<int>> cells(dirichlet_nodes_position.size());
2530 for (
int i = 0; i < dirichlet_nodes_position.size(); ++i)
2532 const int n_id = dirichlet_nodes[i];
2536 b_sidesets(i) = s_id;
2539 for (
int j = 0; j < actual_dim; ++j)
2541 fun(i, j) = sol(n_id * actual_dim + j);
2544 points.row(i) = dirichlet_nodes_position[i];
2545 cells[i].push_back(i);
2548 std::shared_ptr<paraviewo::ParaviewWriter> tmpw;
2550 tmpw = std::make_shared<paraviewo::HDF5VTUWriter>();
2552 tmpw = std::make_shared<paraviewo::VTUWriter>();
2553 paraviewo::ParaviewWriter &writer = *tmpw;
2556 writer.add_field(
"sidesets", b_sidesets);
2558 writer.add_field(
"solution", fun);
2559 writer.write_mesh(path, points, cells,
false,
false);
2563 const std::string &name,
2564 const std::function<std::string(
int)> &vtu_names,
2565 int time_steps,
double t0,
double dt,
int skip_frame)
const
2567 paraviewo::PVDWriter::save_pvd(name, vtu_names, time_steps, t0, dt, skip_frame);
2583 const int nx = delta[0] / spacing + 1;
2584 const int ny = delta[1] / spacing + 1;
2585 const int nz = delta.cols() >= 3 ? (delta[2] / spacing + 1) : 1;
2586 const int n = nx * ny * nz;
2590 for (
int i = 0; i < nx; ++i)
2592 const double x = (delta[0] / (nx - 1)) * i + min[0];
2594 for (
int j = 0; j < ny; ++j)
2596 const double y = (delta[1] / (ny - 1)) * j + min[1];
2598 if (delta.cols() <= 2)
2604 for (
int k = 0; k < nz; ++k)
2606 const double z = (delta[2] / (nz - 1)) * k + min[2];
2615 std::vector<std::array<Eigen::Vector3d, 2>> boxes;
2621 const double eps = 1e-6;
2630 const Eigen::Vector3d min(
2635 const Eigen::Vector3d max(
2640 std::vector<unsigned int> candidates;
2642 bvh.intersect_box(min, max, candidates);
2644 for (
const auto cand : candidates)
2648 logger().warn(
"Element {} is not simplex, skipping", cand);
2652 Eigen::MatrixXd coords;
2655 for (
int d = 0; d < coords.size(); ++d)
2657 if (fabs(coords(d)) < 1e-8)
2659 else if (fabs(coords(d) - 1) < 1e-8)
2663 if (coords.array().minCoeff() >= 0 && coords.array().maxCoeff() <= 1)
2675 Eigen::MatrixXd samples_simplex, samples_cube, mapped, p0, p1, p;
2678 average_edge_length = 0;
2679 min_edge_length = std::numeric_limits<double>::max();
2681 if (!use_curved_mesh_size)
2685 min_edge_length = p.rowwise().norm().minCoeff();
2686 average_edge_length = p.rowwise().norm().mean();
2687 mesh_size = p.rowwise().norm().maxCoeff();
2689 logger().info(
"hmin: {}", min_edge_length);
2690 logger().info(
"hmax: {}", mesh_size);
2691 logger().info(
"havg: {}", average_edge_length);
2708 for (
size_t i = 0; i < bases_in.size(); ++i)
2717 bases_in[i].eval_geom_mapping(samples_simplex, mapped);
2722 bases_in[i].eval_geom_mapping(samples_cube, mapped);
2725 for (
int j = 0; j < n_edges; ++j)
2727 double current_edge = 0;
2728 for (
int k = 0; k < n_samples - 1; ++k)
2730 p0 = mapped.row(j * n_samples + k);
2731 p1 = mapped.row(j * n_samples + k + 1);
2734 current_edge += p.norm();
2737 mesh_size = std::max(current_edge, mesh_size);
2738 min_edge_length = std::min(current_edge, min_edge_length);
2739 average_edge_length += current_edge;
2744 average_edge_length /= n;
2746 logger().info(
"hmin: {}", min_edge_length);
2747 logger().info(
"hmax: {}", mesh_size);
2748 logger().info(
"havg: {}", average_edge_length);
2762 using namespace mesh;
2764 logger().info(
"Counting flipped elements...");
2768 for (
size_t i = 0; i < gbases.size(); ++i)
2774 if (!
vals.is_geom_mapping_positive(mesh.
is_volume(), gbases[i]))
2778 static const std::vector<std::string> element_type_names{{
2780 "RegularInteriorCube",
2781 "RegularBoundaryCube",
2782 "SimpleSingularInteriorCube",
2783 "MultiSingularInteriorCube",
2784 "SimpleSingularBoundaryCube",
2786 "MultiSingularBoundaryCube",
2792 log_and_throw_error(
"element {} is flipped, type {}", i, element_type_names[
static_cast<int>(els_tag[i])]);
2807 const std::vector<polyfem::basis::ElementBases> &bases,
2808 const std::vector<polyfem::basis::ElementBases> &gbases,
2812 const Eigen::MatrixXd &sol)
2816 logger().error(
"Build the bases first!");
2819 if (sol.size() <= 0)
2821 logger().error(
"Solve the problem first!");
2831 logger().info(
"Computing errors...");
2834 const int n_el = int(bases.size());
2836 Eigen::MatrixXd v_exact, v_approx;
2837 Eigen::MatrixXd v_exact_grad(0, 0), v_approx_grad;
2847 static const int p = 8;
2852 for (
int e = 0; e < n_el; ++e)
2862 v_approx.resize(
vals.val.rows(), actual_dim);
2865 v_approx_grad.resize(
vals.val.rows(), mesh.
dimension() * actual_dim);
2866 v_approx_grad.setZero();
2868 const int n_loc_bases = int(
vals.basis_values.size());
2870 for (
int i = 0; i < n_loc_bases; ++i)
2872 const auto &
val =
vals.basis_values[i];
2874 for (
size_t ii = 0; ii <
val.global.size(); ++ii)
2876 for (
int d = 0; d < actual_dim; ++d)
2878 v_approx.col(d) +=
val.global[ii].val * sol(
val.global[ii].index * actual_dim + d) *
val.val;
2879 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;
2884 const auto err = problem.
has_exact_sol() ? (v_exact - v_approx).eval().rowwise().norm().eval() : (v_approx).eval().rowwise().norm().eval();
2885 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();
2890 linf_err = std::max(linf_err, err.maxCoeff());
2891 grad_max_err = std::max(linf_err, err_grad.maxCoeff());
2933 l2_err += (err.array() * err.array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
2934 h1_err += (err_grad.array() * err_grad.array() *
vals.det.array() *
vals.quadrature.weights.array()).sum();
2935 lp_err += (err.array().pow(p) *
vals.det.array() *
vals.quadrature.weights.array()).sum();
2938 h1_semi_err = sqrt(fabs(h1_err));
2939 h1_err = sqrt(fabs(l2_err) + fabs(h1_err));
2940 l2_err = sqrt(fabs(l2_err));
2942 lp_err = pow(fabs(lp_err), 1. / p);
2947 const double computing_errors_time = timer.getElapsedTime();
2948 logger().info(
" took {}s", computing_errors_time);
2950 logger().info(
"-- L2 error: {}", l2_err);
2951 logger().info(
"-- Lp error: {}", lp_err);
2952 logger().info(
"-- H1 error: {}", h1_err);
2953 logger().info(
"-- H1 semi error: {}", h1_semi_err);
2956 logger().info(
"-- Linf error: {}", linf_err);
2957 logger().info(
"-- grad max error: {}", grad_max_err);
2973 regular_boundary_count = 0;
2974 simple_singular_count = 0;
2975 multi_singular_count = 0;
2977 non_regular_boundary_count = 0;
2978 non_regular_count = 0;
2979 undefined_count = 0;
2980 multi_singular_boundary_count = 0;
2984 for (
size_t i = 0; i < els_tag.size(); ++i)
2990 case ElementType::SIMPLEX:
2993 case ElementType::PRISM:
2996 case ElementType::REGULAR_INTERIOR_CUBE:
2999 case ElementType::REGULAR_BOUNDARY_CUBE:
3000 regular_boundary_count++;
3002 case ElementType::SIMPLE_SINGULAR_INTERIOR_CUBE:
3003 simple_singular_count++;
3005 case ElementType::MULTI_SINGULAR_INTERIOR_CUBE:
3006 multi_singular_count++;
3008 case ElementType::SIMPLE_SINGULAR_BOUNDARY_CUBE:
3011 case ElementType::INTERFACE_CUBE:
3012 case ElementType::MULTI_SINGULAR_BOUNDARY_CUBE:
3013 multi_singular_boundary_count++;
3015 case ElementType::BOUNDARY_POLYTOPE:
3016 non_regular_boundary_count++;
3018 case ElementType::INTERIOR_POLYTOPE:
3019 non_regular_count++;
3021 case ElementType::UNDEFINED:
3025 throw std::runtime_error(
"Unknown element type");
3029 logger().info(
"simplex_count: \t{}", simplex_count);
3030 logger().info(
"prism_count: \t{}", prism_count);
3031 logger().info(
"regular_count: \t{}", regular_count);
3032 logger().info(
"regular_boundary_count: \t{}", regular_boundary_count);
3033 logger().info(
"simple_singular_count: \t{}", simple_singular_count);
3034 logger().info(
"multi_singular_count: \t{}", multi_singular_count);
3035 logger().info(
"boundary_count: \t{}", boundary_count);
3036 logger().info(
"multi_singular_boundary_count: \t{}", multi_singular_boundary_count);
3037 logger().info(
"non_regular_count: \t{}", non_regular_count);
3038 logger().info(
"non_regular_boundary_count: \t{}", non_regular_boundary_count);
3039 logger().info(
"undefined_count: \t{}", undefined_count);
3044 const nlohmann::json &args,
3045 const int n_bases,
const int n_pressure_bases,
3046 const Eigen::MatrixXd &sol,
3048 const Eigen::VectorXi &disc_orders,
3049 const Eigen::VectorXi &disc_ordersq,
3052 const std::string &formulation,
3053 const bool isoparametric,
3054 const int sol_at_node_id,
3060 j[
"geom_order"] = mesh.
orders().size() > 0 ? mesh.
orders().maxCoeff() : 1;
3061 j[
"geom_order_min"] = mesh.
orders().size() > 0 ? mesh.
orders().minCoeff() : 1;
3062 j[
"discr_order_min"] = disc_orders.minCoeff();
3063 j[
"discr_order_max"] = disc_orders.maxCoeff();
3064 j[
"discr_orderq_min"] = disc_ordersq.minCoeff();
3065 j[
"discr_orderq_max"] = disc_ordersq.maxCoeff();
3066 j[
"iso_parametric"] = isoparametric;
3067 j[
"problem"] = problem.
name();
3068 j[
"mat_size"] = mat_size;
3069 j[
"num_bases"] = n_bases;
3070 j[
"num_pressure_bases"] = n_pressure_bases;
3071 j[
"num_non_zero"] = nn_zero;
3072 j[
"num_flipped"] = n_flipped;
3073 j[
"num_dofs"] = num_dofs;
3077 j[
"num_p1"] = (disc_orders.array() == 1).count();
3078 j[
"num_p2"] = (disc_orders.array() == 2).count();
3079 j[
"num_p3"] = (disc_orders.array() == 3).count();
3080 j[
"num_p4"] = (disc_orders.array() == 4).count();
3081 j[
"num_p5"] = (disc_orders.array() == 5).count();
3083 j[
"mesh_size"] = mesh_size;
3084 j[
"max_angle"] = max_angle;
3086 j[
"sigma_max"] = sigma_max;
3087 j[
"sigma_min"] = sigma_min;
3088 j[
"sigma_avg"] = sigma_avg;
3090 j[
"min_edge_length"] = min_edge_length;
3091 j[
"average_edge_length"] = average_edge_length;
3093 j[
"err_l2"] = l2_err;
3094 j[
"err_h1"] = h1_err;
3095 j[
"err_h1_semi"] = h1_semi_err;
3096 j[
"err_linf"] = linf_err;
3097 j[
"err_linf_grad"] = grad_max_err;
3098 j[
"err_lp"] = lp_err;
3100 j[
"spectrum"] = {spectrum(0), spectrum(1), spectrum(2), spectrum(3)};
3101 j[
"spectrum_condest"] = std::abs(spectrum(3)) / std::abs(spectrum(0));
3114 j[
"solver_info"] = solver_info;
3116 j[
"count_simplex"] = simplex_count;
3117 j[
"count_prism"] = prism_count;
3118 j[
"count_regular"] = regular_count;
3119 j[
"count_regular_boundary"] = regular_boundary_count;
3120 j[
"count_simple_singular"] = simple_singular_count;
3121 j[
"count_multi_singular"] = multi_singular_count;
3122 j[
"count_boundary"] = boundary_count;
3123 j[
"count_non_regular_boundary"] = non_regular_boundary_count;
3124 j[
"count_non_regular"] = non_regular_count;
3125 j[
"count_undefined"] = undefined_count;
3126 j[
"count_multi_singular_boundary"] = multi_singular_boundary_count;
3128 j[
"is_simplicial"] = mesh.
n_elements() == simplex_count;
3130 j[
"peak_memory"] =
getPeakRSS() / (1024 * 1024);
3134 std::vector<double> mmin(actual_dim);
3135 std::vector<double> mmax(actual_dim);
3137 for (
int d = 0; d < actual_dim; ++d)
3139 mmin[d] = std::numeric_limits<double>::max();
3140 mmax[d] = -std::numeric_limits<double>::max();
3143 for (
int i = 0; i < sol.size(); i += actual_dim)
3145 for (
int d = 0; d < actual_dim; ++d)
3147 mmin[d] = std::min(mmin[d], sol(i + d));
3148 mmax[d] = std::max(mmax[d], sol(i + d));
3152 std::vector<double> sol_at_node(actual_dim);
3154 if (sol_at_node_id >= 0)
3156 const int node_id = sol_at_node_id;
3158 for (
int d = 0; d < actual_dim; ++d)
3160 sol_at_node[d] = sol(node_id * actual_dim + d);
3164 j[
"sol_at_node"] = sol_at_node;
3165 j[
"sol_min"] = mmin;
3166 j[
"sol_max"] = mmax;
3168#if defined(POLYFEM_WITH_CPP_THREADS)
3170#elif defined(POLYFEM_WITH_TBB)
3173 j[
"num_threads"] = 1;
3176 j[
"formulation"] = formulation;
3182 : file(path), solve_data(solve_data)
3187 file << name <<
",";
3189 file <<
"total_energy" << std::endl;
3206 file << ((form && form->enabled()) ? form->value(sol) : 0) / s <<
",";
3213 : file(path), state(state), t0(t0), dt(dt)
3215 file <<
"step,time,forward,remeshing,global_relaxation,peak_mem,#V,#T" << std::endl;
3239 const double peak_mem =
getPeakRSS() / double(1 << 30);
3242 file << fmt::format(
3243 "{},{},{},{},{},{},{},{}\n",
3244 t,
t0 +
dt * t, forward, remeshing, global_relaxation, peak_mem,
ElementAssemblyValues vals
std::vector< Eigen::VectorXi > faces
main class that contains the polyfem solver and all its state
int n_bases
number of bases
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
Eigen::VectorXi in_node_to_node
Inpute nodes (including high-order) to polyfem nodes, only for isoparametric.
mesh::Obstacle obstacle
Obstacles used in collisions.
std::shared_ptr< assembler::Assembler > assembler
assemblers
ipc::CollisionMesh collision_mesh
IPC collision mesh.
std::vector< basis::ElementBases > pressure_bases
FE pressure bases for mixed elements, the size is #elements.
std::shared_ptr< assembler::Mass > mass_matrix_assembler
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
std::vector< int > dirichlet_nodes
per node dirichlet
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
std::vector< RowVectorNd > dirichlet_nodes_position
std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > polys_3d
polyhedra, used since poly have no geom mapping
json args
main input arguments containing all defaults
std::vector< basis::ElementBases > bases
FE bases, the size is #elements.
Eigen::VectorXi disc_ordersq
std::vector< mesh::LocalBoundary > total_local_boundary
mapping from elements to nodes for all mesh
solver::SolveData solve_data
timedependent stuff cached
Eigen::VectorXi disc_orders
vector of discretization orders, used when not all elements have the same degree, one per element
std::shared_ptr< assembler::MixedAssembler > mixed_assembler
std::map< int, Eigen::MatrixXd > polys
polygons, used since poly have no geom mapping
Eigen::MatrixXd rhs
System right-hand side.
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
std::vector< AssemblyValues > basis_values
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,...
std::vector< Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3 > > jac_it
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
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).
Eigen::VectorXi local_nodes_for_primitive(const int local_index, const mesh::Mesh &mesh) const
std::vector< Basis > bases
one basis function per node in the element
EnergyCSVWriter(const std::string &path, const solver::SolveData &solve_data)
const solver::SolveData & solve_data
void write(const int i, const Eigen::MatrixXd &sol)
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)
computes scalar quantity of funtion (ie von mises for elasticity and norm of velocity for fluid) the ...
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 velocy)
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...
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_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
Eigen::MatrixXd grid_points_bc
grid mesh boundaries
Eigen::MatrixXd grid_points
grid mesh points to export solution sampled on a grid
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 save_points(const std::string &path, const State &state, const Eigen::MatrixXd &sol, const ExportOptions &opts) const
saves the nodal values
void build_grid(const polyfem::mesh::Mesh &mesh, const double spacing)
builds the grid to export the solution
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
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_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 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 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< std::vector< int > > &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_wire(const std::string &name, const State &state, const Eigen::MatrixXd &sol, const double t, const ExportOptions &opts) const
saves the wireframe
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 init_sampler(const polyfem::mesh::Mesh &mesh, const double vismesh_rel_area)
unitalize the ref element sampler
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_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
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 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)
saves the output statistic to a json object
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 compute_mesh_stats(const polyfem::mesh::Mesh &mesh)
compute stats (counts els type, mesh lenght, etc), step 1 of solve
double total_forward_solve_time
void write(const int t, const double forward, const double remeshing, const double global_relaxation, const Eigen::MatrixXd &sol)
double total_remeshing_time
RuntimeStatsCSVWriter(const std::string &path, const State &state, const double t0, const double dt)
double total_global_relaxation_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 n_vertices() const =0
number of vertices
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 void get_edges(Eigen::MatrixXd &p0, Eigen::MatrixXd &p1) const =0
Get all the edges.
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
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
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
const std::vector< ElementType > & elements_tag() const
Returns the elements types.
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_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 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::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 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)
ElementType
Type of Element, check [Poly-Spline Finite Element Method] for a complete description.
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
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