52#include <polysolve/linear/FEMSolver.hpp>
59#include <spdlog/fmt/fmt.h>
75 using namespace assembler;
78 using namespace utils;
83 void build_in_node_to_in_primitive(
const Mesh &mesh,
const MeshNodes &mesh_nodes,
84 Eigen::VectorXi &in_node_to_in_primitive,
85 Eigen::VectorXi &in_node_offset)
87 const int num_vertex_nodes = mesh_nodes.num_vertex_nodes();
88 const int num_edge_nodes = mesh_nodes.num_edge_nodes();
89 const int num_face_nodes = mesh_nodes.num_face_nodes();
90 const int num_cell_nodes = mesh_nodes.num_cell_nodes();
92 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
94 const long n_vertices = num_vertex_nodes;
95 const int num_in_primitives = n_vertices + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
96 const int num_primitives = mesh.n_vertices() + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
98 in_node_to_in_primitive.resize(num_nodes);
99 in_node_offset.resize(num_nodes);
102 in_node_to_in_primitive.head(num_vertex_nodes).setLinSpaced(num_vertex_nodes, 0, num_vertex_nodes - 1);
103 in_node_offset.head(num_vertex_nodes).setZero();
105 int prim_offset = n_vertices;
106 int node_offset = num_vertex_nodes;
107 auto foo = [&](
const int num_prims,
const int num_prim_nodes) {
108 if (num_prims <= 0 || num_prim_nodes <= 0)
110 const Eigen::VectorXi range = Eigen::VectorXi::LinSpaced(num_prim_nodes, 0, num_prim_nodes - 1);
112 const int node_per_prim = num_prim_nodes / num_prims;
114 in_node_to_in_primitive.segment(node_offset, num_prim_nodes) =
115 range.array() / node_per_prim + prim_offset;
117 in_node_offset.segment(node_offset, num_prim_nodes) =
118 range.unaryExpr([&](
const int x) {
return x % node_per_prim; });
120 prim_offset += num_prims;
121 node_offset += num_prim_nodes;
124 foo(mesh.n_edges(), num_edge_nodes);
125 foo(mesh.n_faces(), num_face_nodes);
126 foo(mesh.n_cells(), num_cell_nodes);
129 bool build_in_primitive_to_primitive(
130 const Mesh &mesh,
const MeshNodes &mesh_nodes,
131 const Eigen::VectorXi &in_ordered_vertices,
132 const Eigen::MatrixXi &in_ordered_edges,
133 const Eigen::MatrixXi &in_ordered_faces,
134 Eigen::VectorXi &in_primitive_to_primitive)
137 const int num_vertex_nodes = mesh_nodes.num_vertex_nodes();
138 const int num_edge_nodes = mesh_nodes.num_edge_nodes();
139 const int num_face_nodes = mesh_nodes.num_face_nodes();
140 const int num_cell_nodes = mesh_nodes.num_cell_nodes();
141 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
143 const long n_vertices = num_vertex_nodes;
144 const int num_in_primitives = n_vertices + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
145 const int num_primitives = mesh.n_vertices() + mesh.n_edges() + mesh.n_faces() + mesh.n_cells();
147 in_primitive_to_primitive.setLinSpaced(num_in_primitives, 0, num_in_primitives - 1);
155 if (in_ordered_vertices.rows() != n_vertices)
157 logger().warn(
"Node ordering disabled, in_ordered_vertices != n_vertices, {} != {}", in_ordered_vertices.rows(), n_vertices);
161 in_primitive_to_primitive.head(n_vertices) = in_ordered_vertices;
163 int in_offset = n_vertices;
164 int offset = mesh.n_vertices();
170 logger().trace(
"Building Mesh edges to IDs...");
172 const auto edges_to_ids = mesh.edges_to_ids();
173 if (in_ordered_edges.rows() != edges_to_ids.size())
175 logger().warn(
"Node ordering disabled, in_ordered_edges != edges_to_ids, {} != {}", in_ordered_edges.rows(), edges_to_ids.size());
179 logger().trace(
"Done (took {}s)", timer.getElapsedTime());
181 logger().trace(
"Building in-edge to edge mapping...");
183 for (
int in_ei = 0; in_ei < in_ordered_edges.rows(); in_ei++)
185 const std::pair<int, int> in_edge(
186 in_ordered_edges.row(in_ei).minCoeff(),
187 in_ordered_edges.row(in_ei).maxCoeff());
188 in_primitive_to_primitive[in_offset + in_ei] =
189 offset + edges_to_ids.at(in_edge);
192 logger().trace(
"Done (took {}s)", timer.getElapsedTime());
194 in_offset += mesh.n_edges();
195 offset += mesh.n_edges();
201 if (mesh.is_volume())
203 logger().trace(
"Building Mesh faces to IDs...");
205 const auto faces_to_ids = mesh.faces_to_ids();
206 if (in_ordered_faces.rows() != faces_to_ids.size())
208 logger().warn(
"Node ordering disabled, in_ordered_faces != faces_to_ids, {} != {}", in_ordered_faces.rows(), faces_to_ids.size());
212 logger().trace(
"Done (took {}s)", timer.getElapsedTime());
214 logger().trace(
"Building in-face to face mapping...");
216 for (
int in_fi = 0; in_fi < in_ordered_faces.rows(); in_fi++)
218 std::vector<int> in_face(in_ordered_faces.cols());
219 for (
int i = 0; i < in_face.size(); i++)
220 in_face[i] = in_ordered_faces(in_fi, i);
221 std::sort(in_face.begin(), in_face.end());
223 in_primitive_to_primitive[in_offset + in_fi] =
224 offset + faces_to_ids.at(in_face);
227 logger().trace(
"Done (took {}s)", timer.getElapsedTime());
229 in_offset += mesh.n_faces();
230 offset += mesh.n_faces();
240 indices.resize(
mesh->n_vertices());
247 std::vector<int> indices;
249 for (
int i = 0; i < p2n.size(); i++)
256 if (
args[
"space"][
"basis_type"] ==
"Spline")
258 logger().warn(
"Node ordering disabled, it dosent work for splines!");
264 logger().warn(
"Node ordering disabled, it works only for p < 4 and uniform order!");
268 if (!
mesh->is_conforming())
270 logger().warn(
"Node ordering disabled, not supported for non-conforming meshes!");
274 if (
mesh->has_poly())
276 logger().warn(
"Node ordering disabled, not supported for polygonal meshes!");
280 if (
mesh->in_ordered_vertices().size() <= 0 ||
mesh->in_ordered_edges().size() <= 0 || (
mesh->is_volume() &&
mesh->in_ordered_faces().size() <= 0))
282 logger().warn(
"Node ordering disabled, input vertices/edges/faces not computed!");
286 const int num_vertex_nodes =
mesh_nodes->num_vertex_nodes();
287 const int num_edge_nodes =
mesh_nodes->num_edge_nodes();
288 const int num_face_nodes =
mesh_nodes->num_face_nodes();
289 const int num_cell_nodes =
mesh_nodes->num_cell_nodes();
291 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
293 const long n_vertices = num_vertex_nodes;
294 const int num_in_primitives = n_vertices +
mesh->n_edges() +
mesh->n_faces() +
mesh->n_cells();
295 const int num_primitives =
mesh->n_vertices() +
mesh->n_edges() +
mesh->n_faces() +
mesh->n_cells();
299 logger().trace(
"Building in-node to in-primitive mapping...");
301 Eigen::VectorXi in_node_to_in_primitive;
302 Eigen::VectorXi in_node_offset;
303 build_in_node_to_in_primitive(*
mesh, *
mesh_nodes, in_node_to_in_primitive, in_node_offset);
305 logger().trace(
"Done (took {}s)", timer.getElapsedTime());
307 logger().trace(
"Building in-primitive to primitive mapping...");
309 bool ok = build_in_primitive_to_primitive(
311 mesh->in_ordered_vertices(),
312 mesh->in_ordered_edges(),
313 mesh->in_ordered_faces(),
316 logger().trace(
"Done (took {}s)", timer.getElapsedTime());
324 const auto &tmp =
mesh_nodes->in_ordered_vertices();
327 max_tmp = std::max(max_tmp, v);
330 for (
int i = 0; i < tmp.size(); ++i)
339 if (
args[
"materials"].is_null())
341 logger().error(
"specify some 'materials'");
342 assert(!
args[
"materials"].is_null());
343 throw std::runtime_error(
"invalid input");
346 if (
args[
"materials"].is_array())
348 std::string current =
"";
349 for (
const auto &m :
args[
"materials"])
351 const std::string tmp = m[
"type"];
354 else if (current != tmp)
359 current =
"MultiModels";
362 logger().error(
"Current material is {}, new material is {}, multimaterial supported only for LinearElasticity and NeoHookean", current, tmp);
363 throw std::runtime_error(
"invalid input");
368 logger().error(
"Current material is {}, new material is {}, multimaterial supported only for LinearElasticity and NeoHookean", current, tmp);
369 throw std::runtime_error(
"invalid input");
377 return args[
"materials"][
"type"];
384 logger().error(
"No pressure bases defined!");
389 Eigen::MatrixXd tmp = sol;
401 const std::vector<basis::ElementBases> &bases,
402 const std::vector<basis::ElementBases> &gbases,
403 Eigen::MatrixXd &basis_integrals)
407 logger().error(
"Works only on volumetric meshes!");
412 basis_integrals.resize(n_bases, 9);
413 basis_integrals.setZero();
414 Eigen::MatrixXd rhs(n_bases, 9);
418 for (
int e = 0; e < n_elements; ++e)
430 for (
int j = 0; j < n_local_bases; ++j)
447 for (
size_t ii = 0; ii < v.
global.size(); ++ii)
449 basis_integrals(v.
global[ii].index, 0) += integral_100 * v.
global[ii].val;
450 basis_integrals(v.
global[ii].index, 1) += integral_010 * v.
global[ii].val;
451 basis_integrals(v.
global[ii].index, 2) += integral_001 * v.
global[ii].val;
453 basis_integrals(v.
global[ii].index, 3) += integral_110 * v.
global[ii].val;
454 basis_integrals(v.
global[ii].index, 4) += integral_011 * v.
global[ii].val;
455 basis_integrals(v.
global[ii].index, 5) += integral_101 * v.
global[ii].val;
457 basis_integrals(v.
global[ii].index, 6) += integral_200 * v.
global[ii].val;
458 basis_integrals(v.
global[ii].index, 7) += integral_020 * v.
global[ii].val;
459 basis_integrals(v.
global[ii].index, 8) += integral_002 * v.
global[ii].val;
461 rhs(v.
global[ii].index, 6) += -2.0 * area * v.
global[ii].val;
462 rhs(v.
global[ii].index, 7) += -2.0 * area * v.
global[ii].val;
463 rhs(v.
global[ii].index, 8) += -2.0 * area * v.
global[ii].val;
468 basis_integrals -= rhs;
473 if (
mesh->has_poly())
476 if (
args[
"space"][
"basis_type"] ==
"Bernstein")
479 if (
args[
"space"][
"basis_type"] ==
"Spline")
482 if (
mesh->is_rational())
485 if (
args[
"space"][
"use_p_ref"])
488 if (
mesh->orders().size() <= 0)
490 if (
args[
"space"][
"discr_order"] == 1)
493 return args[
"space"][
"advanced"][
"isoparametric"];
496 if (
mesh->orders().minCoeff() !=
mesh->orders().maxCoeff())
499 if (
args[
"space"][
"discr_order"] ==
mesh->orders().minCoeff())
506 return args[
"space"][
"advanced"][
"isoparametric"];
513 logger().error(
"Load the mesh first!");
517 mesh->prepare_mesh();
536 assert(
args[
"materials"].is_array());
538 std::vector<std::string> materials(
mesh->n_elements());
540 std::map<int, std::string> mats;
542 for (
const auto &m :
args[
"materials"])
543 mats[m[
"id"].get<int>()] = m[
"type"];
545 for (
int i = 0; i < materials.size(); ++i)
546 materials[i] = mats.at(
mesh->get_body_id(i));
548 mm->init_multimodels(materials);
561 logger().info(
"Building {} basis...", (
iso_parametric() ?
"isoparametric" :
"not isoparametric"));
562 const bool has_polys =
mesh->has_poly();
568 std::map<int, basis::InterfaceData> poly_edge_to_data_geom;
570 const auto &tmp_json =
args[
"space"][
"discr_order"];
571 if (tmp_json.is_number_integer())
575 else if (tmp_json.is_string())
581 assert(tmp.cols() == 1);
584 else if (tmp_json.is_array())
586 const auto b_discr_orders = tmp_json;
588 std::map<int, int> b_orders;
589 for (
size_t i = 0; i < b_discr_orders.size(); ++i)
591 assert(b_discr_orders[i][
"id"].is_array() || b_discr_orders[i][
"id"].is_number_integer());
593 const int order = b_discr_orders[i][
"order"];
594 for (
const int id : json_as_array<int>(b_discr_orders[i][
"id"]))
596 b_orders[id] = order;
597 logger().trace(
"bid {}, discr {}",
id, order);
601 for (
int e = 0; e <
mesh->n_elements(); ++e)
603 const int bid =
mesh->get_body_id(e);
604 const auto order = b_orders.find(bid);
605 if (order == b_orders.end())
607 logger().debug(
"Missing discretization order for body {}; using 1", bid);
619 logger().error(
"space/discr_order must be either a number a path or an array");
620 throw std::runtime_error(
"invalid json");
624#ifdef POLYFEM_WITH_MISO
625 if (!
mesh->is_simplicial())
630 args[
"space"][
"advanced"][
"count_flipped_els_continuous"] =
false;
631 args[
"output"][
"paraview"][
"options"][
"jacobian_validity"] =
false;
632 args[
"solver"][
"advanced"][
"check_inversion"] =
"Discrete";
634 else if (
args[
"solver"][
"advanced"][
"check_inversion"] !=
"Discrete")
636 args[
"space"][
"advanced"][
"use_corner_quadrature"] =
true;
639 Eigen::MatrixXi geom_disc_orders;
642 if (
mesh->orders().size() <= 0)
645 geom_disc_orders.setConstant(1);
648 geom_disc_orders =
mesh->orders();
652 Eigen::MatrixXi geom_disc_ordersq = geom_disc_orders;
653 const auto &tmp_json2 =
args[
"space"][
"discr_orderq"];
654 if (tmp_json2.is_number_integer())
662 if (
args[
"space"][
"use_p_ref"])
666 args[
"space"][
"advanced"][
"B"],
667 args[
"space"][
"advanced"][
"h1_formula"],
668 args[
"space"][
"discr_order"],
669 args[
"space"][
"advanced"][
"discr_order_max"],
676 int quadrature_order =
args[
"space"][
"advanced"][
"quadrature_order"].get<
int>();
677 const int mass_quadrature_order =
args[
"space"][
"advanced"][
"mass_quadrature_order"].get<
int>();
683 logger().error(
"p refinement not supported in mixed formulation!");
689 const bool use_continuous_gbasis =
true;
690 const bool use_corner_quadrature =
args[
"space"][
"advanced"][
"use_corner_quadrature"];
692 if (
mesh->is_volume())
695 if (
args[
"space"][
"basis_type"] ==
"Spline")
712 n_geom_bases =
basis::LagrangeBasis3d::build_bases(tmp_mesh,
assembler->name(), quadrature_order, mass_quadrature_order, geom_disc_orders, geom_disc_ordersq,
false,
false, has_polys, !use_continuous_gbasis, use_corner_quadrature,
geom_bases_,
local_boundary, poly_edge_to_data_geom,
geom_mesh_nodes);
714 n_bases =
basis::LagrangeBasis3d::build_bases(tmp_mesh,
assembler->name(), quadrature_order, mass_quadrature_order,
disc_orders,
disc_ordersq,
args[
"space"][
"basis_type"] ==
"Bernstein",
args[
"space"][
"basis_type"] ==
"Serendipity", has_polys,
false, use_corner_quadrature,
bases,
local_boundary,
poly_edge_to_data,
mesh_nodes);
721 const int order =
args[
"space"][
"pressure_discr_order"];
723 const int orderq = order;
725 n_pressure_bases =
basis::LagrangeBasis3d::build_bases(tmp_mesh,
assembler->name(), quadrature_order, mass_quadrature_order, order, orderq,
args[
"space"][
"basis_type"] ==
"Bernstein",
false, has_polys,
false, use_corner_quadrature,
pressure_bases,
local_boundary, poly_edge_to_data_geom,
pressure_mesh_nodes);
731 if (
args[
"space"][
"basis_type"] ==
"Spline")
749 n_geom_bases =
basis::LagrangeBasis2d::build_bases(tmp_mesh,
assembler->name(), quadrature_order, mass_quadrature_order, geom_disc_orders,
false,
false, has_polys, !use_continuous_gbasis, use_corner_quadrature,
geom_bases_,
local_boundary, poly_edge_to_data_geom,
geom_mesh_nodes);
751 n_bases =
basis::LagrangeBasis2d::build_bases(tmp_mesh,
assembler->name(), quadrature_order, mass_quadrature_order,
disc_orders,
args[
"space"][
"basis_type"] ==
"Bernstein",
args[
"space"][
"basis_type"] ==
"Serendipity", has_polys,
false, use_corner_quadrature,
bases,
local_boundary,
poly_edge_to_data,
mesh_nodes);
759 n_pressure_bases =
basis::LagrangeBasis2d::build_bases(tmp_mesh,
assembler->name(), quadrature_order, mass_quadrature_order,
int(
args[
"space"][
"pressure_discr_order"]),
args[
"space"][
"basis_type"] ==
"Bernstein",
false, has_polys,
false, use_corner_quadrature,
pressure_bases,
local_boundary, poly_edge_to_data_geom,
pressure_mesh_nodes);
769 bases[i].compute_quadrature(b_quad);
784 const int dim =
mesh->dimension();
785 const int problem_dim =
problem->is_scalar() ? 1 : dim;
791 const json &conditions =
args[
"boundary_conditions"][
"periodic"];
794 for (
int i = 0; i < int(conditions.size()); ++i)
796 const json &condition = conditions[i];
797 const std::array<int, 2> boundary_ids = {{condition[
"boundary_ids"][0].get<
int>(),
798 condition[
"boundary_ids"][1].get<int>()}};
801 boundary_ids, condition.value(
"tolerance", 1e-5));
803 for (
const int dof : mapping.boundary_dofs)
813 if (
args[
"constraints"].contains(
"macro_displacement_gradient"))
819 if (
args[
"space"][
"advanced"][
"count_flipped_els"])
822 const int prev_bases =
n_bases;
827 logger().debug(
"Building node mapping...");
833 logger().debug(
"Done (took {}s)", timer2.getElapsedTime());
836 logger().info(
"Building collision mesh...");
860 for (
const auto &bs :
bases)
862 for (
const auto &b : bs.bases)
864 for (
const auto &lg : b.global())
866 if (lg.index == n_id)
890 for (
const auto &bs :
bases)
892 for (
const auto &b : bs.bases)
894 for (
const auto &lg : b.global())
896 if (lg.index == n_id)
919 for (
int i = prev_bases; i <
n_bases; ++i)
921 for (
int d = 0; d < problem_dim; ++d)
930 const int n_samples = 10;
953 args[
"contact"][
"dhat"] = dhat;
958 logger().info(
"dhat set to {}",
double(
args[
"contact"][
"dhat"]));
977 if (
n_bases <=
args[
"solver"][
"advanced"][
"cache_size"])
980 logger().info(
"Building cache...");
987 logger().info(
" took {}s", timer.getElapsedTime());
1001 const bool has_global_constraints =
1002 !
args[
"constraints"][
"hard"].empty()
1003 || (
args[
"constraints"][
"zero_mean"].is_boolean() &&
args[
"constraints"][
"zero_mean"].get<
bool>())
1004 || (
args[
"constraints"][
"zero_mean"].is_array() && !
args[
"constraints"][
"zero_mean"].empty())
1006 if (!has_global_constraints)
1008 log_and_throw_error(
"Static problem need to have some Dirichlet nodes or linear constraints!");
1010 logger().warn(
"Static problem has no Dirichlet nodes; relying on linear constraints for uniqueness");
1018 logger().error(
"Load the mesh first!");
1032 logger().info(
"Computing polygonal basis...");
1043 if (
mesh->is_volume())
1045 if (
args[
"space"][
"poly_basis_type"] ==
"MeanValue" ||
args[
"space"][
"poly_basis_type"] ==
"Wachspress")
1046 logger().error(
"Barycentric bases not supported in 3D");
1050 args[
"space"][
"advanced"][
"n_harmonic_samples"],
1053 args[
"space"][
"advanced"][
"quadrature_order"],
1054 args[
"space"][
"advanced"][
"mass_quadrature_order"],
1055 args[
"space"][
"advanced"][
"integral_constraints"],
1063 if (
args[
"space"][
"poly_basis_type"] ==
"MeanValue")
1070 args[
"space"][
"advanced"][
"quadrature_order"],
1071 args[
"space"][
"advanced"][
"mass_quadrature_order"],
1074 else if (
args[
"space"][
"poly_basis_type"] ==
"Wachspress")
1081 args[
"space"][
"advanced"][
"quadrature_order"],
1082 args[
"space"][
"advanced"][
"mass_quadrature_order"],
1090 args[
"space"][
"advanced"][
"n_harmonic_samples"],
1093 args[
"space"][
"advanced"][
"quadrature_order"],
1094 args[
"space"][
"advanced"][
"mass_quadrature_order"],
1095 args[
"space"][
"advanced"][
"integral_constraints"],
1105 if (
mesh->is_volume())
1107 if (
args[
"space"][
"poly_basis_type"] ==
"MeanValue" ||
args[
"space"][
"poly_basis_type"] ==
"Wachspress")
1109 logger().error(
"Barycentric bases not supported in 3D");
1110 throw std::runtime_error(
"not implemented");
1117 args[
"space"][
"advanced"][
"n_harmonic_samples"],
1120 args[
"space"][
"advanced"][
"quadrature_order"],
1121 args[
"space"][
"advanced"][
"mass_quadrature_order"],
1122 args[
"space"][
"advanced"][
"integral_constraints"],
1131 if (
args[
"space"][
"poly_basis_type"] ==
"MeanValue")
1137 n_bases,
args[
"space"][
"advanced"][
"quadrature_order"],
1138 args[
"space"][
"advanced"][
"mass_quadrature_order"],
1141 else if (
args[
"space"][
"poly_basis_type"] ==
"Wachspress")
1147 n_bases,
args[
"space"][
"advanced"][
"quadrature_order"],
1148 args[
"space"][
"advanced"][
"mass_quadrature_order"],
1156 args[
"space"][
"advanced"][
"n_harmonic_samples"],
1159 args[
"space"][
"advanced"][
"quadrature_order"],
1160 args[
"space"][
"advanced"][
"mass_quadrature_order"],
1161 args[
"space"][
"advanced"][
"integral_constraints"],
1179 assert(!
mesh->is_volume());
1180 const int dim =
mesh->dimension();
1181 const int n_tiles = 2;
1183 if (
mesh->dimension() != 2)
1187 for (
const auto &bs :
bases)
1188 for (
const auto &b : bs.bases)
1189 for (
const auto &g : b.global())
1190 V.row(g.index) = g.node;
1193 for (
int i = 0; i < E.size(); i++)
1196 Eigen::MatrixXd bbox(
V.cols(), 2);
1197 bbox.col(0) =
V.colwise().minCoeff();
1198 bbox.col(1) =
V.colwise().maxCoeff();
1202 std::vector<int> ind;
1203 for (
int i = 0; i < E.rows(); i++)
1209 E = E(ind, Eigen::all).eval();
1212 Eigen::MatrixXd Vtmp, Vnew;
1213 Eigen::MatrixXi Etmp, Enew;
1214 Vtmp.setZero(
V.rows() * n_tiles * n_tiles,
V.cols());
1215 Etmp.setZero(E.rows() * n_tiles * n_tiles, E.cols());
1219 log_and_throw_error(
"Periodic contact requires {} linearly independent periodic boundary pairs", dim);
1222 for (
int i = 0, idx = 0; i < n_tiles; i++)
1224 for (
int j = 0; j < n_tiles; j++)
1226 Eigen::Vector2d block_id;
1229 Vtmp.middleRows(idx *
V.rows(),
V.rows()) =
V;
1232 for (
int vid = 0; vid <
V.rows(); vid++)
1233 Vtmp.block(idx *
V.rows() + vid, 0, 1, 2) += (tile_offset * block_id).transpose();
1235 Etmp.middleRows(idx * E.rows(), E.rows()) = E.array() + idx *
V.rows();
1241 Eigen::VectorXi indices;
1243 std::vector<int> tmp;
1244 for (
int i = 0; i <
V.rows(); i++)
1250 indices.resize(tmp.size() * n_tiles * n_tiles);
1251 for (
int i = 0; i < n_tiles * n_tiles; i++)
1253 indices.segment(i * tmp.size(), tmp.size()) = Eigen::Map<Eigen::VectorXi, Eigen::Unaligned>(tmp.data(), tmp.size());
1254 indices.segment(i * tmp.size(), tmp.size()).array() += i *
V.rows();
1258 Eigen::VectorXi potentially_duplicate_mask(Vtmp.rows());
1259 potentially_duplicate_mask.setZero();
1260 potentially_duplicate_mask(indices).array() = 1;
1262 Eigen::MatrixXd candidates = Vtmp(indices, Eigen::all);
1264 Eigen::VectorXi SVI;
1265 std::vector<int> SVJ;
1266 SVI.setConstant(Vtmp.rows(), -1);
1268 double relative_tolerance = 1e-5;
1269 for (
const json &condition :
args[
"boundary_conditions"][
"periodic"])
1270 relative_tolerance = std::max(relative_tolerance, condition.value(
"tolerance", 1e-5));
1271 const double eps = (bbox.col(1) - bbox.col(0)).maxCoeff() * relative_tolerance;
1272 for (
int i = 0; i < Vtmp.rows(); i++)
1278 if (potentially_duplicate_mask(i))
1280 Eigen::VectorXd diffs = (candidates.rowwise() - Vtmp.row(i)).rowwise().norm();
1281 for (
int j = 0; j < diffs.size(); j++)
1283 SVI[indices[j]] =
id;
1289 Vnew = Vtmp(SVJ, Eigen::all);
1291 Enew.resizeLike(Etmp);
1292 for (
int d = 0; d < Etmp.cols(); d++)
1293 Enew.col(d) = SVI(Etmp.col(d));
1295 std::vector<bool> is_on_surface = ipc::CollisionMesh::construct_is_on_surface(Vnew.rows(), Enew);
1297 Eigen::MatrixXi boundary_triangles;
1298 Eigen::SparseMatrix<double> displacement_map;
1300 std::vector<bool>(Vnew.rows(),
false),
1309 for (
int i = 0; i <
V.rows(); i++)
1310 for (
int j = 0; j < n_tiles * n_tiles; j++)
1328 const std::vector<basis::ElementBases> &bases,
1329 const std::vector<basis::ElementBases> &geom_bases,
1330 const std::vector<mesh::LocalBoundary> &total_local_boundary,
1333 const std::function<std::string(
const std::string &)> &resolve_input_path,
1334 const Eigen::VectorXi &in_node_to_node,
1335 ipc::CollisionMesh &collision_mesh)
1337 Eigen::MatrixXd collision_vertices;
1338 Eigen::VectorXi collision_codim_vids;
1339 Eigen::MatrixXi collision_edges, collision_triangles;
1340 std::vector<Eigen::Triplet<double>> displacement_map_entries;
1342 if (
args.contains(
"/contact/collision_mesh"_json_pointer)
1343 &&
args.at(
"/contact/collision_mesh/enabled"_json_pointer).get<
bool>())
1345 const json collision_mesh_args =
args.at(
"/contact/collision_mesh"_json_pointer);
1346 if (collision_mesh_args.contains(
"linear_map"))
1348 assert(displacement_map_entries.empty());
1349 assert(collision_mesh_args.contains(
"mesh"));
1350 const std::string
root_path = utils::json_value<std::string>(
args,
"root_path",
"");
1356 collision_mesh_args[
"mesh"].get<std::string>(),
1357 collision_mesh_args[
"linear_map"].get<std::string>(),
1358 in_node_to_node, transformation, collision_vertices, collision_codim_vids,
1359 collision_edges, collision_triangles, displacement_map_entries);
1361 else if (collision_mesh_args.contains(
"tessellation_type")
1362 && collision_mesh_args[
"tessellation_type"] ==
"max_order")
1366 collision_vertices, collision_edges, collision_triangles, displacement_map_entries,
1367 utils::json_value<int>(collision_mesh_args,
"sampling_order", 0));
1369 else if (collision_mesh_args.contains(
"max_edge_length"))
1372 "Building collision proxy with max edge length={} ...",
1373 collision_mesh_args[
"max_edge_length"].get<double>());
1378 collision_mesh_args[
"max_edge_length"], collision_vertices,
1379 collision_triangles, displacement_map_entries,
1380 collision_mesh_args[
"tessellation_type"]);
1381 if (collision_triangles.size())
1382 igl::edges(collision_triangles, collision_edges);
1384 logger().debug(fmt::format(
1385 std::locale(
"en_US.UTF-8"),
1386 "Done (took {:g}s, {:L} vertices, {:L} triangles)",
1387 timer.getElapsedTime(),
1388 collision_vertices.rows(), collision_triangles.rows()));
1394 collision_vertices, collision_edges, collision_triangles, displacement_map_entries);
1401 collision_vertices, collision_edges, collision_triangles, displacement_map_entries);
1404 std::vector<bool> is_orientable_vertex(collision_vertices.rows(),
true);
1408 const int num_fe_collision_vertices = collision_vertices.rows();
1409 assert(collision_edges.size() == 0 || collision_edges.maxCoeff() < num_fe_collision_vertices);
1410 assert(collision_triangles.size() == 0 || collision_triangles.maxCoeff() < num_fe_collision_vertices);
1422 is_orientable_vertex.push_back(
false);
1425 if (!displacement_map_entries.empty())
1427 displacement_map_entries.reserve(displacement_map_entries.size() +
obstacle.
n_vertices());
1430 displacement_map_entries.emplace_back(num_fe_collision_vertices + i, num_fe_nodes + i, 1.0);
1435 std::vector<bool> is_on_surface = ipc::CollisionMesh::construct_is_on_surface(
1436 collision_vertices.rows(), collision_edges);
1437 for (
const int vid : collision_codim_vids)
1439 is_on_surface[vid] =
true;
1442 Eigen::SparseMatrix<double> displacement_map;
1443 if (!displacement_map_entries.empty())
1445 displacement_map.resize(collision_vertices.rows(),
n_bases);
1446 displacement_map.setFromTriplets(displacement_map_entries.begin(), displacement_map_entries.end());
1450 is_on_surface, is_orientable_vertex, collision_vertices, collision_edges, collision_triangles,
1455 return collision_mesh.to_full_vertex_id(vi) < num_fe_collision_vertices
1456 ||
collision_mesh.to_full_vertex_id(vj) < num_fe_collision_vertices;
1466 logger().error(
"Load the mesh first!");
1471 logger().error(
"Build the bases first!");
1474 if (
assembler->name() ==
"OperatorSplitting")
1481 if (!
problem->is_time_dependent())
1495 logger().info(
"Assembling mass mat...");
1504 std::vector<Eigen::Triplet<double>> mass_blocks;
1505 mass_blocks.reserve(velocity_mass.nonZeros());
1507 for (
int k = 0; k < velocity_mass.outerSize(); ++k)
1509 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
1511 mass_blocks.emplace_back(it.row(), it.col(), it.value());
1516 mass.setFromTriplets(mass_blocks.begin(), mass_blocks.end());
1517 mass.makeCompressed();
1526 assert(
mass.size() > 0);
1529 for (
int k = 0; k <
mass.outerSize(); ++k)
1532 for (StiffnessMatrix::InnerIterator it(
mass, k); it; ++it)
1534 assert(it.col() == k);
1542 if (
args[
"solver"][
"advanced"][
"lump_mass_matrix"])
1559 const std::vector<basis::ElementBases> &bases_,
1562 json rhs_solver_params =
args[
"solver"][
"linear"];
1563 if (!rhs_solver_params.contains(
"Pardiso"))
1564 rhs_solver_params[
"Pardiso"] = {};
1565 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
1567 const int size =
problem->is_scalar() ? 1 :
mesh->dimension();
1569 return std::make_shared<RhsAssembler>(
1574 args[
"space"][
"advanced"][
"bc_method"],
1580 const std::vector<basis::ElementBases> &bases_)
const
1582 const int size =
problem->is_scalar() ? 1 :
mesh->dimension();
1584 return std::make_shared<PressureAssembler>(
1597 logger().error(
"Load the mesh first!");
1602 logger().error(
"Build the bases first!");
1609 p_params[
"formulation"] =
assembler->name();
1613 mesh->bounding_box(min, max);
1614 delta = (max - min) / 2. + min;
1615 if (
mesh->is_volume())
1616 p_params[
"bbox_center"] = {delta(0), delta(1), delta(2)};
1618 p_params[
"bbox_center"] = {delta(0), delta(1)};
1621 problem->set_parameters(p_params, resources);
1626 logger().info(
"Assigning rhs...");
1635 const int prev_size =
rhs.size();
1637 rhs.conservativeResize(prev_size + n_larger,
rhs.cols());
1638 if (
assembler->name() ==
"OperatorSplitting")
1646 rhs.block(prev_size, 0, n_larger,
rhs.cols()).setZero();
1657 rhs.block(prev_size, 0, n_larger,
rhs.cols()) = tmp;
1667 Eigen::MatrixXd &pressure,
1673 logger().error(
"Load the mesh first!");
1678 logger().error(
"Build the bases first!");
1698 if (
problem->is_time_dependent())
1700 const double t0 =
args[
"time"][
"t0"];
1701 const int time_steps =
args[
"time"][
"time_steps"];
1702 const double dt =
args[
"time"][
"dt"];
1705 if (
args[
"output"][
"advanced"][
"save_time_sequence"])
1707 logger().info(
"Time sequence of simulation will be written to: \"{}\"",
1711 if (
assembler->name() ==
"NavierStokes")
1713 else if (
assembler->name() ==
"OperatorSplitting")
1720 throw std::runtime_error(
"Nonlinear scalar problems are not supported yet!");
1726 if (
assembler->name() ==
"NavierStokes")
1736 throw std::runtime_error(
"Nonlinear scalar problems are not supported yet!");
1743 if (!state_path.empty())
ElementAssemblyValues vals
std::string velocity() const
static double convert(const json &val, const std::string &unit_type)
const std::string & length() const
static bool is_elastic_material(const std::string &material)
utility to check if material is one of the elastic materials
Caches basis evaluation and geometric mapping at every element.
void init(const bool is_volume, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const bool is_mass=false)
computes the basis evaluation and geometric mapping for each of the given ElementBases in bases initi...
void init_empty(const bool is_mass=false)
initialize an empty cache.
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,...
quadrature::Quadrature quadrature
assemble matrix based on the local assembler local assembler is eg Laplace, LinearElasticity etc
void init(const int dim, const json ¶m, const io::ResourceIO &resources)
static int build_bases(const mesh::Mesh2D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, const int discr_order, const bool bernstein, const bool serendipity, const bool has_polys, const bool is_geom_bases, const bool use_corner_quadrature, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_edge_to_data, std::shared_ptr< mesh::MeshNodes > &mesh_nodes)
Builds FE basis functions over the entire mesh (P1, P2 over triangles, Q1, Q2 over quads).
static int build_bases(const mesh::Mesh3D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, const int discr_orderp, const int discr_orderq, const bool bernstein, const bool serendipity, const bool has_polys, const bool is_geom_bases, const bool use_corner_quadrature, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_face_to_data, std::shared_ptr< mesh::MeshNodes > &mesh_nodes)
Builds FE basis functions over the entire mesh (P1, P2 over tets, Q1, Q2 over hes).
static int build_bases(const std::string &assembler_name, const int dim, const mesh::Mesh2D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, Eigen::MatrixXd > &mapped_boundary)
static int build_bases(const assembler::LinearAssembler &assembler, const int n_samples_per_edge, const mesh::Mesh2D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, const int integral_constraints, std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, const std::map< int, InterfaceData > &poly_edge_to_data, std::map< int, Eigen::MatrixXd > &mapped_boundary)
Build bases over the remaining polygons of a mesh.
static int build_bases(const assembler::LinearAssembler &assembler, const int n_samples_per_edge, const mesh::Mesh3D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, const int integral_constraints, std::vector< ElementBases > &bases, const std::vector< ElementBases > &gbases, const std::map< int, InterfaceData > &poly_face_to_data, std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > &mapped_boundary)
Build bases over the remaining polygons of a mesh.
static int build_bases(const mesh::Mesh2D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_edge_to_data)
static int build_bases(const mesh::Mesh3D &mesh, const std::string &assembler, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, InterfaceData > &poly_face_to_data)
static int build_bases(const std::string &assembler_name, const int dim, const mesh::Mesh2D &mesh, const int n_bases, const int quadrature_order, const int mass_quadrature_order, std::vector< ElementBases > &bases, std::vector< mesh::LocalBoundary > &local_boundary, std::map< int, Eigen::MatrixXd > &mapped_boundary)
static void extract_boundary_mesh_sampled(const mesh::Mesh &mesh, const int n_bases, const std::vector< basis::ElementBases > &bases, const std::vector< mesh::LocalBoundary > &total_local_boundary, Eigen::MatrixXd &node_positions, Eigen::MatrixXi &boundary_edges, Eigen::MatrixXi &boundary_triangles, std::vector< Eigen::Triplet< double > > &displacement_map_entries, const int sampling_order=0)
extracts a collision proxy sampling every boundary face on a uniform lattice of the globally maximal ...
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
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
int n_flipped
number of flipped elements, compute only when using count_flipped_els (false by default)
Eigen::Vector4d spectrum
spectrum of the stiffness matrix, enable only if POLYSOLVE_WITH_SPECTRA is ON (off by default)
void count_flipped_elements(const polyfem::mesh::Mesh &mesh, const std::vector< polyfem::basis::ElementBases > &gbases)
counts the number of flipped elements
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...
long long nn_zero
non zeros and sytem matrix size num dof is the total dof in the system
double mesh_size
max edge lenght
void reset()
clears all stats
double min_edge_length
min edge lenght
Runtime override for initial-condition histories.
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
bool iso_parametric() const
check if using iso parametric bases
StiffnessMatrix pure_mass
Eigen::VectorXi periodic_dof_mask
Explicit periodic boundary-pair data used by periodic contact.
void solve_transient_tensor_nonlinear(const int time_steps, const double t0, const double dt, Eigen::MatrixXd &sol, UserPostStepCallback user_post_step={}, const InitialConditionOverride *ic_override=nullptr)
solves transient tensor nonlinear problem
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
ipc::CollisionMesh collision_mesh
IPC collision mesh.
StiffnessMatrix mass
Mass matrix, it is computed only for time dependent problems.
bool has_dhat
stores if input json contains dhat
std::vector< RowVectorNd > dirichlet_nodes_position
std::string resolve_input_path(const std::string &path, const bool only_if_exists=false) const
Resolve input path relative to root_path() if the path is not absolute.
void build_collision_mesh()
extracts the boundary mesh for collision, called in build_basis
std::vector< mesh::LocalBoundary > local_boundary
mapping from elements to nodes for dirichlet boundary conditions
std::vector< mesh::LocalBoundary > local_pressure_boundary
mapping from elements to nodes for pressure boundary conditions
std::vector< int > dirichlet_nodes
per node dirichlet
std::shared_ptr< polyfem::mesh::MeshNodes > mesh_nodes
Mapping from input nodes to FE nodes.
Eigen::VectorXi in_primitive_to_primitive
maps in vertices/edges/faces/cells to polyfem vertices/edges/faces/cells
void solve_tensor_nonlinear(int step, Eigen::MatrixXd &sol, const bool init_lagging=true, UserPostStepCallback user_post_step={})
solves nonlinear problems
std::shared_ptr< assembler::Mass > mass_matrix_assembler
std::vector< int > pressure_boundary_nodes
list of neumann boundary nodes
void init_solve(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, const InitialConditionOverride *ic_override=nullptr)
initialize solver
std::unordered_map< int, std::vector< mesh::LocalBoundary > > local_pressure_cavity
mapping from elements to nodes for pressure boundary conditions
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Eigen::MatrixXd rhs
System right-hand side.
void build_node_mapping()
build the mapping from input nodes to polyfem nodes
void init_linear_solve(Eigen::MatrixXd &sol, const double t=1.0, const InitialConditionOverride *ic_override=nullptr)
initialize the linear solve
io::OutStatsData stats
Other statistics.
json args
main input arguments containing all defaults
void build_polygonal_basis()
builds bases for polygons, called inside build_basis
assembler::AssemblyValsCache pure_mass_ass_vals_cache
std::vector< basis::ElementBases > geom_bases_
Geometric mapping bases, if the elements are isoparametric, this list is empty.
std::vector< RowVectorNd > neumann_nodes_position
Eigen::VectorXi disc_ordersq
std::map< int, Eigen::MatrixXd > polys
polygons, used since poly have no geom mapping
void solve_transient_navier_stokes_split(const int time_steps, const double dt, Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={})
solves transient navier stokes with operator splitting
void sol_to_pressure(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure)
splits the solution in solution and pressure for mixed problems
void solve_homogenization(const int time_steps, const double t0, const double dt, Eigen::MatrixXd &sol, UserPostStepCallback user_post_step={})
int n_pressure_bases
number of pressure bases
assembler::AssemblyValsCache pressure_ass_vals_cache
used to store assembly values for pressure for small problems
int n_bases
number of bases
void assemble_mass_mat()
assemble mass, step 4 of solve build mass matrix based on defined basis modifies mass (and maybe more...
double starting_min_edge_length
std::shared_ptr< polyfem::mesh::MeshNodes > geom_mesh_nodes
std::string resolve_output_path(const std::string &path) const
Resolve output path relative to output_dir if the path is not absolute.
double min_boundary_edge_length
io::OutRuntimeData timings
runtime statistics
std::vector< basis::ElementBases > pressure_bases
FE pressure bases for mixed elements, the size is #elements.
mesh::Obstacle obstacle
Obstacles used in collisions.
std::string root_path() const
Get the root path for the state (e.g., args["root_path"] or ".")
std::string formulation() const
return the formulation (checks if the problem is scalar or not and deals with multiphysics)
Eigen::VectorXi disc_orders
vector of discretization orders, used when not all elements have the same degree, one per element
assembler::AssemblyValsCache ass_vals_cache
used to store assembly values for small problems
void solve_transient_linear(const int time_steps, const double t0, const double dt, Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={}, const InitialConditionOverride *ic_override=nullptr)
solves transient linear problem
io::OutGeometryData out_geom
visualization stuff
assembler::AssemblyValsCache mass_ass_vals_cache
QuadratureOrders n_boundary_samples() const
quadrature used for projecting boundary conditions
bool use_avg_pressure
use average pressure for stokes problem to fix the additional dofs, true by default if false,...
std::shared_ptr< assembler::Assembler > assembler
assemblers
void build_basis()
builds the bases step 2 of solve modifies bases, pressure_bases, geom_bases_, boundary_nodes,...
std::map< int, basis::InterfaceData > poly_edge_to_data
nodes on the boundary of polygonal elements, used for harmonic bases
double avg_mass
average system mass, used for contact with IPC
std::vector< basis::ElementBases > bases
FE bases, the size is #elements.
void solve_linear(int step, Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={})
solves a linear problem
Eigen::MatrixXd periodic_tile_offsets
polyfem::io::FileSystemIO rhs_resources_
void build_periodic_collision_mesh()
std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > polys_3d
polyhedra, used since poly have no geom mapping
bool has_periodic_bc() const
ipc::CollisionMesh periodic_collision_mesh
IPC collision mesh under periodic BC.
std::vector< int > neumann_nodes
per node neumann
Eigen::VectorXi periodic_collision_mesh_to_basis
index mapping from periodic 2x2 collision mesh to FE periodic mesh
std::shared_ptr< polyfem::mesh::MeshNodes > pressure_mesh_nodes
assembler::MacroStrainValue macro_strain_constraint
std::shared_ptr< assembler::RhsAssembler > build_rhs_assembler() const
build a RhsAssembler for the problem
bool is_problem_linear() const
Returns whether the system is linear. Collisions and pressure add nonlinearity to the problem.
std::shared_ptr< assembler::PressureAssembler > build_pressure_assembler() const
std::vector< mesh::LocalBoundary > total_local_boundary
mapping from elements to nodes for all mesh
int n_geom_bases
number of geometric bases
std::shared_ptr< assembler::HRZMass > pure_mass_matrix_assembler
void init_nonlinear_tensor_solve(Eigen::MatrixXd &sol, const double t=1.0, const bool init_time_integrator=true, const InitialConditionOverride *ic_override=nullptr)
initialize the nonlinear solver
void assemble_rhs()
compute rhs, step 3 of solve build rhs vector based on defined basis and given rhs of the problem mod...
void solve_problem(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={}, const InitialConditionOverride *ic_override=nullptr)
solves the problems
double starting_max_edge_length
std::vector< mesh::LocalBoundary > local_neumann_boundary
mapping from elements to nodes for neumann boundary conditions
std::vector< int > primitive_to_node() const
std::vector< int > boundary_nodes
list of boundary nodes
solver::SolveData solve_data
timedependent stuff cached
std::vector< int > node_to_primitive() const
Eigen::VectorXi in_node_to_node
Inpute nodes (including high-order) to polyfem nodes, only for isoparametric.
bool is_homogenization() const
bool is_contact_enabled() const
does the simulation have contact
std::shared_ptr< assembler::MixedAssembler > mixed_assembler
void build_grid(const polyfem::mesh::Mesh &mesh, const double spacing)
builds the grid to export the solution
bool is_volume() const override
checks if mesh is volume
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
const Eigen::MatrixXi & e() const
const Eigen::MatrixXi & f() const
const Eigen::MatrixXd & v() const
const Eigen::VectorXi & codim_v() const
static void p_refine(const mesh::Mesh &mesh, const double B, const bool h1_formula, const int base_p, const int discr_order_max, io::OutStatsData &stats, Eigen::VectorXi &disc_orders)
compute a priori prefinement
std::shared_ptr< assembler::RhsAssembler > rhs_assembler
bool read_matrix(const std::string &path, Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &mat)
Reads a matrix from a file. Determines the file format based on the path's extension.
bool write_matrix(const std::string &path, const Mat &mat)
Writes a matrix to a file. Determines the file format based on the path's extension.
std::function< void(int step, State &state, const Eigen::MatrixXd &sol, const Eigen::MatrixXd *disp_grad, const Eigen::MatrixXd *pressure)> UserPostStepCallback
User callback at the end of every solver step.
void compute_integral_constraints(const Mesh3D &mesh, const int n_bases, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, Eigen::MatrixXd &basis_integrals)
void build_collision_proxy(const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &geom_bases, const std::vector< LocalBoundary > &total_local_boundary, const int n_bases, const int dim, const double max_edge_length, Eigen::MatrixXd &proxy_vertices, Eigen::MatrixXi &proxy_faces, std::vector< Eigen::Triplet< double > > &displacement_map_entries, const CollisionProxyTessellation tessellation)
void load_collision_proxy(const io::ResourceIO &resources, const std::string &mesh_filename, const std::string &weights_filename, const Eigen::VectorXi &in_node_to_node, const json &transformation, Eigen::MatrixXd &vertices, Eigen::VectorXi &codim_vertices, Eigen::MatrixXi &edges, Eigen::MatrixXi &faces, std::vector< Eigen::Triplet< double > > &displacement_map_entries)
Load a collision proxy mesh and displacement map from resources.
Eigen::SparseMatrix< double > lump_matrix(const Eigen::SparseMatrix< double > &M)
Lump each row of a matrix into the diagonal.
std::string resolve_path(const std::string &path, const std::string &input_file_path, const bool only_if_exists=false)
std::vector< T > json_as_array(const json &j)
Return the value of a json object as an array.
void append_rows(DstMat &dst, const SrcMat &src)
spdlog::logger & logger()
Retrieves the current logger.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
void log_and_throw_error(const std::string &msg)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix