PolyFEM
Loading...
Searching...
No Matches
State.cpp
Go to the documentation of this file.
2#include <polyfem/Common.hpp>
3
8// the non-legacy OutData carries the hybrid (prism/pyramid) collision-proxy
9// construction; the collision mesh is built through it in both architectures
12
16
25
28
30
33
36
39
42
47
51
52#include <polysolve/linear/FEMSolver.hpp>
53
54#include <igl/edges.h>
55#include <igl/Timer.h>
56
57#include <Eigen/Core>
58
59#include <spdlog/fmt/fmt.h>
60
61#include <algorithm>
62#include <memory>
63#include <stdexcept>
64#include <string>
65#include <utility>
66#include <vector>
67#include <cassert>
68#include <cmath>
69#include <cstddef>
70
71using namespace Eigen;
72
73namespace polyfem::legacy
74{
75 using namespace assembler;
76 using namespace mesh;
77 using namespace io;
78 using namespace utils;
79
80 namespace
81 {
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)
86 {
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();
91
92 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
93
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();
97
98 in_node_to_in_primitive.resize(num_nodes);
99 in_node_offset.resize(num_nodes);
100
101 // Only one node per vertex, so this is an identity map.
102 in_node_to_in_primitive.head(num_vertex_nodes).setLinSpaced(num_vertex_nodes, 0, num_vertex_nodes - 1); // vertex nodes
103 in_node_offset.head(num_vertex_nodes).setZero();
104
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)
109 return;
110 const Eigen::VectorXi range = Eigen::VectorXi::LinSpaced(num_prim_nodes, 0, num_prim_nodes - 1);
111 // TODO: This assumes isotropic degree of element.
112 const int node_per_prim = num_prim_nodes / num_prims;
113
114 in_node_to_in_primitive.segment(node_offset, num_prim_nodes) =
115 range.array() / node_per_prim + prim_offset;
116
117 in_node_offset.segment(node_offset, num_prim_nodes) =
118 range.unaryExpr([&](const int x) { return x % node_per_prim; });
119
120 prim_offset += num_prims;
121 node_offset += num_prim_nodes;
122 };
123
124 foo(mesh.n_edges(), num_edge_nodes);
125 foo(mesh.n_faces(), num_face_nodes);
126 foo(mesh.n_cells(), num_cell_nodes);
127 }
128
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)
135 {
136 // NOTE: Assume in_cells_to_cells is identity
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;
142
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();
146
147 in_primitive_to_primitive.setLinSpaced(num_in_primitives, 0, num_in_primitives - 1);
148
149 igl::Timer timer;
150
151 // ------------
152 // Map vertices
153 // ------------
154
155 if (in_ordered_vertices.rows() != n_vertices)
156 {
157 logger().warn("Node ordering disabled, in_ordered_vertices != n_vertices, {} != {}", in_ordered_vertices.rows(), n_vertices);
158 return false;
159 }
160
161 in_primitive_to_primitive.head(n_vertices) = in_ordered_vertices;
162
163 int in_offset = n_vertices;
164 int offset = mesh.n_vertices();
165
166 // ---------
167 // Map edges
168 // ---------
169
170 logger().trace("Building Mesh edges to IDs...");
171 timer.start();
172 const auto edges_to_ids = mesh.edges_to_ids();
173 if (in_ordered_edges.rows() != edges_to_ids.size())
174 {
175 logger().warn("Node ordering disabled, in_ordered_edges != edges_to_ids, {} != {}", in_ordered_edges.rows(), edges_to_ids.size());
176 return false;
177 }
178 timer.stop();
179 logger().trace("Done (took {}s)", timer.getElapsedTime());
180
181 logger().trace("Building in-edge to edge mapping...");
182 timer.start();
183 for (int in_ei = 0; in_ei < in_ordered_edges.rows(); in_ei++)
184 {
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); // offset edge ids
190 }
191 timer.stop();
192 logger().trace("Done (took {}s)", timer.getElapsedTime());
193
194 in_offset += mesh.n_edges();
195 offset += mesh.n_edges();
196
197 // ---------
198 // Map faces
199 // ---------
200
201 if (mesh.is_volume())
202 {
203 logger().trace("Building Mesh faces to IDs...");
204 timer.start();
205 const auto faces_to_ids = mesh.faces_to_ids();
206 if (in_ordered_faces.rows() != faces_to_ids.size())
207 {
208 logger().warn("Node ordering disabled, in_ordered_faces != faces_to_ids, {} != {}", in_ordered_faces.rows(), faces_to_ids.size());
209 return false;
210 }
211 timer.stop();
212 logger().trace("Done (took {}s)", timer.getElapsedTime());
213
214 logger().trace("Building in-face to face mapping...");
215 timer.start();
216 for (int in_fi = 0; in_fi < in_ordered_faces.rows(); in_fi++)
217 {
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());
222
223 in_primitive_to_primitive[in_offset + in_fi] =
224 offset + faces_to_ids.at(in_face); // offset face ids
225 }
226 timer.stop();
227 logger().trace("Done (took {}s)", timer.getElapsedTime());
228
229 in_offset += mesh.n_faces();
230 offset += mesh.n_faces();
231 }
232
233 return true;
234 }
235 } // namespace
236
237 std::vector<int> State::primitive_to_node() const
238 {
239 auto indices = iso_parametric() ? mesh_nodes->primitive_to_node() : geom_mesh_nodes->primitive_to_node();
240 indices.resize(mesh->n_vertices());
241 return indices;
242 }
243
244 std::vector<int> State::node_to_primitive() const
245 {
246 auto p2n = primitive_to_node();
247 std::vector<int> indices;
248 indices.resize(n_geom_bases);
249 for (int i = 0; i < p2n.size(); i++)
250 indices[p2n[i]] = i;
251 return indices;
252 }
253
255 {
256 if (args["space"]["basis_type"] == "Spline")
257 {
258 logger().warn("Node ordering disabled, it dosent work for splines!");
259 return;
260 }
261
262 if (disc_orders.maxCoeff() >= 4 || disc_orders.maxCoeff() != disc_orders.minCoeff())
263 {
264 logger().warn("Node ordering disabled, it works only for p < 4 and uniform order!");
265 return;
266 }
267
268 if (!mesh->is_conforming())
269 {
270 logger().warn("Node ordering disabled, not supported for non-conforming meshes!");
271 return;
272 }
273
274 if (mesh->has_poly())
275 {
276 logger().warn("Node ordering disabled, not supported for polygonal meshes!");
277 return;
278 }
279
280 if (mesh->in_ordered_vertices().size() <= 0 || mesh->in_ordered_edges().size() <= 0 || (mesh->is_volume() && mesh->in_ordered_faces().size() <= 0))
281 {
282 logger().warn("Node ordering disabled, input vertices/edges/faces not computed!");
283 return;
284 }
285
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();
290
291 const int num_nodes = num_vertex_nodes + num_edge_nodes + num_face_nodes + num_cell_nodes;
292
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();
296
297 igl::Timer timer;
298
299 logger().trace("Building in-node to in-primitive mapping...");
300 timer.start();
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);
304 timer.stop();
305 logger().trace("Done (took {}s)", timer.getElapsedTime());
306
307 logger().trace("Building in-primitive to primitive mapping...");
308 timer.start();
309 bool ok = build_in_primitive_to_primitive(
310 *mesh, *mesh_nodes,
311 mesh->in_ordered_vertices(),
312 mesh->in_ordered_edges(),
313 mesh->in_ordered_faces(),
315 timer.stop();
316 logger().trace("Done (took {}s)", timer.getElapsedTime());
317
318 if (!ok)
319 {
320 in_node_to_node.resize(0);
322 return;
323 }
324 const auto &tmp = mesh_nodes->in_ordered_vertices();
325 int max_tmp = -1;
326 for (auto v : tmp)
327 max_tmp = std::max(max_tmp, v);
328
329 in_node_to_node.resize(max_tmp + 1);
330 for (int i = 0; i < tmp.size(); ++i)
331 {
332 if (tmp[i] >= 0)
333 in_node_to_node[tmp[i]] = i;
334 }
335 }
336
337 std::string State::formulation() const
338 {
339 if (args["materials"].is_null())
340 {
341 logger().error("specify some 'materials'");
342 assert(!args["materials"].is_null());
343 throw std::runtime_error("invalid input");
344 }
345
346 if (args["materials"].is_array())
347 {
348 std::string current = "";
349 for (const auto &m : args["materials"])
350 {
351 const std::string tmp = m["type"];
352 if (current.empty())
353 current = tmp;
354 else if (current != tmp)
355 {
357 {
359 current = "MultiModels";
360 else
361 {
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");
364 }
365 }
366 else
367 {
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");
370 }
371 }
372 }
373
374 return current;
375 }
376 else
377 return args["materials"]["type"];
378 }
379
380 void State::sol_to_pressure(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure)
381 {
382 if (n_pressure_bases <= 0)
383 {
384 logger().error("No pressure bases defined!");
385 return;
386 }
387
388 assert(mixed_assembler != nullptr);
389 Eigen::MatrixXd tmp = sol;
390
391 int fluid_offset = use_avg_pressure ? (assembler->is_fluid() ? 1 : 0) : 0;
392 sol = tmp.topRows(tmp.rows() - n_pressure_bases - fluid_offset);
393 assert(sol.size() == n_bases * (problem->is_scalar() ? 1 : mesh->dimension()));
394 pressure = tmp.middleRows(tmp.rows() - n_pressure_bases - fluid_offset, n_pressure_bases);
395 assert(pressure.size() == n_pressure_bases);
396 }
397
399 const Mesh3D &mesh,
400 const int n_bases,
401 const std::vector<basis::ElementBases> &bases,
402 const std::vector<basis::ElementBases> &gbases,
403 Eigen::MatrixXd &basis_integrals)
404 {
405 if (!mesh.is_volume())
406 {
407 logger().error("Works only on volumetric meshes!");
408 return;
409 }
410 assert(mesh.is_volume());
411
412 basis_integrals.resize(n_bases, 9);
413 basis_integrals.setZero();
414 Eigen::MatrixXd rhs(n_bases, 9);
415 rhs.setZero();
416
417 const int n_elements = mesh.n_elements();
418 for (int e = 0; e < n_elements; ++e)
419 {
420 // if (mesh.is_polytope(e)) {
421 // continue;
422 // }
423 // ElementAssemblyValues vals = values[e];
424 // const ElementAssemblyValues &gvals = gvalues[e];
426 vals.compute(e, mesh.is_volume(), bases[e], gbases[e]);
427
428 // Computes the discretized integral of the PDE over the element
429 const int n_local_bases = int(vals.basis_values.size());
430 for (int j = 0; j < n_local_bases; ++j)
431 {
432 const AssemblyValues &v = vals.basis_values[j];
433 const double integral_100 = (v.grad_t_m.col(0).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
434 const double integral_010 = (v.grad_t_m.col(1).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
435 const double integral_001 = (v.grad_t_m.col(2).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
436
437 const double integral_110 = ((vals.val.col(1).array() * v.grad_t_m.col(0).array() + vals.val.col(0).array() * v.grad_t_m.col(1).array()) * vals.det.array() * vals.quadrature.weights.array()).sum();
438 const double integral_011 = ((vals.val.col(2).array() * v.grad_t_m.col(1).array() + vals.val.col(1).array() * v.grad_t_m.col(2).array()) * vals.det.array() * vals.quadrature.weights.array()).sum();
439 const double integral_101 = ((vals.val.col(0).array() * v.grad_t_m.col(2).array() + vals.val.col(2).array() * v.grad_t_m.col(0).array()) * vals.det.array() * vals.quadrature.weights.array()).sum();
440
441 const double integral_200 = 2 * (vals.val.col(0).array() * v.grad_t_m.col(0).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
442 const double integral_020 = 2 * (vals.val.col(1).array() * v.grad_t_m.col(1).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
443 const double integral_002 = 2 * (vals.val.col(2).array() * v.grad_t_m.col(2).array() * vals.det.array() * vals.quadrature.weights.array()).sum();
444
445 const double area = (v.val.array() * vals.det.array() * vals.quadrature.weights.array()).sum();
446
447 for (size_t ii = 0; ii < v.global.size(); ++ii)
448 {
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;
452
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;
456
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;
460
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;
464 }
465 }
466 }
467
468 basis_integrals -= rhs;
469 }
470
472 {
473 if (mesh->has_poly())
474 return true;
475
476 if (args["space"]["basis_type"] == "Bernstein")
477 return false;
478
479 if (args["space"]["basis_type"] == "Spline")
480 return true;
481
482 if (mesh->is_rational())
483 return false;
484
485 if (args["space"]["use_p_ref"])
486 return false;
487
488 if (mesh->orders().size() <= 0)
489 {
490 if (args["space"]["discr_order"] == 1)
491 return true;
492 else
493 return args["space"]["advanced"]["isoparametric"];
494 }
495
496 if (mesh->orders().minCoeff() != mesh->orders().maxCoeff())
497 return false;
498
499 if (args["space"]["discr_order"] == mesh->orders().minCoeff())
500 return true;
501
502 // TODO:
503 // if (args["space"]["discr_order"] == 1 && args["force_linear_geometry"])
504 // return true;
505
506 return args["space"]["advanced"]["isoparametric"];
507 }
508
510 {
511 if (!mesh)
512 {
513 logger().error("Load the mesh first!");
514 return;
515 }
516
517 mesh->prepare_mesh();
518
519 bases.clear();
520 pressure_bases.clear();
521 geom_bases_.clear();
522 boundary_nodes.clear();
523 dirichlet_nodes.clear();
524 neumann_nodes.clear();
525 local_boundary.clear();
526 total_local_boundary.clear();
529 local_pressure_cavity.clear();
530 polys.clear();
531 poly_edge_to_data.clear();
532 rhs.resize(0, 0);
533
534 if (assembler::MultiModel *mm = dynamic_cast<assembler::MultiModel *>(assembler.get()))
535 {
536 assert(args["materials"].is_array());
537
538 std::vector<std::string> materials(mesh->n_elements());
539
540 std::map<int, std::string> mats;
541
542 for (const auto &m : args["materials"])
543 mats[m["id"].get<int>()] = m["type"];
544
545 for (int i = 0; i < materials.size(); ++i)
546 materials[i] = mats.at(mesh->get_body_id(i));
547
548 mm->init_multimodels(materials);
549 }
550
551 n_bases = 0;
552 n_geom_bases = 0;
554
555 stats.reset();
556
557 disc_orders.resize(mesh->n_elements());
558 disc_ordersq.resize(mesh->n_elements());
559
560 problem->init(*mesh);
561 logger().info("Building {} basis...", (iso_parametric() ? "isoparametric" : "not isoparametric"));
562 const bool has_polys = mesh->has_poly();
563
564 local_boundary.clear();
567 local_pressure_cavity.clear();
568 std::map<int, basis::InterfaceData> poly_edge_to_data_geom; // temp dummy variable
569
570 const auto &tmp_json = args["space"]["discr_order"];
571 if (tmp_json.is_number_integer())
572 {
573 disc_orders.setConstant(tmp_json);
574 }
575 else if (tmp_json.is_string())
576 {
577 const std::string discr_orders_path = utils::resolve_path(tmp_json, root_path());
578 Eigen::MatrixXi tmp;
579 polyfem::io::read_matrix(discr_orders_path, tmp);
580 assert(tmp.size() == disc_orders.size());
581 assert(tmp.cols() == 1);
582 disc_orders = tmp;
583 }
584 else if (tmp_json.is_array())
585 {
586 const auto b_discr_orders = tmp_json;
587
588 std::map<int, int> b_orders;
589 for (size_t i = 0; i < b_discr_orders.size(); ++i)
590 {
591 assert(b_discr_orders[i]["id"].is_array() || b_discr_orders[i]["id"].is_number_integer());
592
593 const int order = b_discr_orders[i]["order"];
594 for (const int id : json_as_array<int>(b_discr_orders[i]["id"]))
595 {
596 b_orders[id] = order;
597 logger().trace("bid {}, discr {}", id, order);
598 }
599 }
600
601 for (int e = 0; e < mesh->n_elements(); ++e)
602 {
603 const int bid = mesh->get_body_id(e);
604 const auto order = b_orders.find(bid);
605 if (order == b_orders.end())
606 {
607 logger().debug("Missing discretization order for body {}; using 1", bid);
608 b_orders[bid] = 1;
609 disc_orders[e] = 1;
610 }
611 else
612 {
613 disc_orders[e] = order->second;
614 }
615 }
616 }
617 else
618 {
619 logger().error("space/discr_order must be either a number a path or an array");
620 throw std::runtime_error("invalid json");
621 }
622 // TODO: same for pressure!
623
624#ifdef POLYFEM_WITH_MISO
625 if (!mesh->is_simplicial())
626#else
627 if constexpr (true)
628#endif
629 {
630 args["space"]["advanced"]["count_flipped_els_continuous"] = false;
631 args["output"]["paraview"]["options"]["jacobian_validity"] = false;
632 args["solver"]["advanced"]["check_inversion"] = "Discrete";
633 }
634 else if (args["solver"]["advanced"]["check_inversion"] != "Discrete")
635 {
636 args["space"]["advanced"]["use_corner_quadrature"] = true;
637 }
638
639 Eigen::MatrixXi geom_disc_orders;
640 if (!iso_parametric())
641 {
642 if (mesh->orders().size() <= 0)
643 {
644 geom_disc_orders.resizeLike(disc_orders);
645 geom_disc_orders.setConstant(1);
646 }
647 else
648 geom_disc_orders = mesh->orders();
649 }
650
651 // TODO: implement prism geometric order
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())
655 {
656 // tmp fix for n-m order prism
657 disc_ordersq.setConstant(tmp_json2);
658 }
659
660 igl::Timer timer;
661 timer.start();
662 if (args["space"]["use_p_ref"])
663 {
665 *mesh,
666 args["space"]["advanced"]["B"],
667 args["space"]["advanced"]["h1_formula"],
668 args["space"]["discr_order"],
669 args["space"]["advanced"]["discr_order_max"],
670 stats,
672
673 logger().info("min p: {} max p: {}", disc_orders.minCoeff(), disc_orders.maxCoeff());
674 }
675
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>();
678 if (mixed_assembler != nullptr)
679 {
680 const int disc_order = disc_orders.maxCoeff();
681 if (disc_order - disc_orders.minCoeff() != 0)
682 {
683 logger().error("p refinement not supported in mixed formulation!");
684 return;
685 }
686 }
687
688 // shape optimization needs continuous geometric basis
689 const bool use_continuous_gbasis = true;
690 const bool use_corner_quadrature = args["space"]["advanced"]["use_corner_quadrature"];
691
692 if (mesh->is_volume())
693 {
694 const Mesh3D &tmp_mesh = *dynamic_cast<Mesh3D *>(mesh.get());
695 if (args["space"]["basis_type"] == "Spline")
696 {
697 // if (!iso_parametric())
698 // {
699 // logger().error("Splines must be isoparametric, ignoring...");
700 // // LagrangeBasis3d::build_bases(tmp_mesh, quadrature_order, geom_disc_orders, has_polys, geom_bases_, local_boundary, poly_edge_to_data_geom, mesh_nodes);
701 // SplineBasis3d::build_bases(tmp_mesh, quadrature_order, geom_bases_, local_boundary, poly_edge_to_data);
702 // }
703
704 n_bases = basis::SplineBasis3d::build_bases(tmp_mesh, assembler->name(), quadrature_order, mass_quadrature_order, bases, local_boundary, poly_edge_to_data);
705
706 // if (iso_parametric() && args["fit_nodes"])
707 // SplineBasis3d::fit_nodes(tmp_mesh, n_bases, bases);
708 }
709 else
710 {
711 if (!iso_parametric())
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);
713
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);
715 }
716
717 // if(problem->is_mixed())
718 if (mixed_assembler != nullptr)
719 {
720 log_and_throw_error("Mixed formulation is not supported anymore for legacy!");
721 const int order = args["space"]["pressure_discr_order"];
722 // todo prism
723 const int orderq = order;
724
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);
726 }
727 }
728 else
729 {
730 const Mesh2D &tmp_mesh = *dynamic_cast<Mesh2D *>(mesh.get());
731 if (args["space"]["basis_type"] == "Spline")
732 {
733 // TODO:
734 // if (!iso_parametric())
735 // {
736 // logger().error("Splines must be isoparametric, ignoring...");
737 // // LagrangeBasis2d::build_bases(tmp_mesh, quadrature_order, disc_orders, has_polys, geom_bases_, local_boundary, poly_edge_to_data_geom, mesh_nodes);
738 // n_bases = SplineBasis2d::build_bases(tmp_mesh, quadrature_order, geom_bases_, local_boundary, poly_edge_to_data);
739 // }
740
741 n_bases = basis::SplineBasis2d::build_bases(tmp_mesh, assembler->name(), quadrature_order, mass_quadrature_order, bases, local_boundary, poly_edge_to_data);
742
743 // if (iso_parametric() && args["fit_nodes"])
744 // SplineBasis2d::fit_nodes(tmp_mesh, n_bases, bases);
745 }
746 else
747 {
748 if (!iso_parametric())
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);
750
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);
752 }
753
754 // if(problem->is_mixed())
755 if (mixed_assembler != nullptr)
756 {
757 log_and_throw_error("Mixed formulation is not supported anymore for legacy!");
758
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);
760 }
761 }
762
763 if (mixed_assembler != nullptr)
764 {
765 assert(bases.size() == pressure_bases.size());
766 for (int i = 0; i < pressure_bases.size(); ++i)
767 {
769 bases[i].compute_quadrature(b_quad);
770 pressure_bases[i].set_quadrature([b_quad](quadrature::Quadrature &quad) { quad = b_quad; });
771 }
772 }
773
774 timer.stop();
775
777
778 if (n_geom_bases == 0)
780
781 for (const auto &lb : local_boundary)
782 total_local_boundary.emplace_back(lb);
783
784 const int dim = mesh->dimension();
785 const int problem_dim = problem->is_scalar() ? 1 : dim;
786
787 // Build the explicit periodic pair mapping once for consumers such as
788 // periodic contact. The constraint forms use the same centroid matcher.
789 if (has_periodic_bc())
790 {
791 const json &conditions = args["boundary_conditions"]["periodic"];
792 periodic_dof_mask.setZero(n_bases);
793 periodic_tile_offsets.resize(dim, conditions.size());
794 for (int i = 0; i < int(conditions.size()); ++i)
795 {
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>()}};
800 n_bases * problem_dim, problem_dim, *mesh, bases, total_local_boundary,
801 boundary_ids, condition.value("tolerance", 1e-5));
802 periodic_tile_offsets.col(i) = mapping.translation.transpose();
803 for (const int dof : mapping.boundary_dofs)
804 periodic_dof_mask(dof) = 1;
805 }
806 }
807 else
808 {
809 periodic_dof_mask.resize(0);
810 periodic_tile_offsets.resize(0, 0);
811 }
812
813 if (args["constraints"].contains("macro_displacement_gradient"))
814 {
815 const polyfem::io::FileSystemIO resources(root_path());
816 macro_strain_constraint.init(dim, args["constraints"]["macro_displacement_gradient"], resources);
817 }
818
819 if (args["space"]["advanced"]["count_flipped_els"])
821
822 const int prev_bases = n_bases;
824
825 {
826 igl::Timer timer2;
827 logger().debug("Building node mapping...");
828 timer2.start();
830 problem->update_nodes(in_node_to_node);
831 mesh->update_nodes(in_node_to_node);
832 timer2.stop();
833 logger().debug("Done (took {}s)", timer2.getElapsedTime());
834 }
835
836 logger().info("Building collision mesh...");
838 if (has_periodic_bc() && args["contact"]["periodic"])
840 logger().info("Done!");
841
842 const int prev_b_size = local_boundary.size();
843 problem->setup_bc(*mesh, n_bases - obstacle.n_vertices(),
852
853 // setp nodal values
854 {
856 for (int n = 0; n < dirichlet_nodes.size(); ++n)
857 {
858 const int n_id = dirichlet_nodes[n];
859 bool found = false;
860 for (const auto &bs : bases)
861 {
862 for (const auto &b : bs.bases)
863 {
864 for (const auto &lg : b.global())
865 {
866 if (lg.index == n_id)
867 {
868 dirichlet_nodes_position[n] = lg.node;
869 found = true;
870 break;
871 }
872 }
873
874 if (found)
875 break;
876 }
877
878 if (found)
879 break;
880 }
881
882 assert(found);
883 }
884
886 for (int n = 0; n < neumann_nodes.size(); ++n)
887 {
888 const int n_id = neumann_nodes[n];
889 bool found = false;
890 for (const auto &bs : bases)
891 {
892 for (const auto &b : bs.bases)
893 {
894 for (const auto &lg : b.global())
895 {
896 if (lg.index == n_id)
897 {
898 neumann_nodes_position[n] = lg.node;
899 found = true;
900 break;
901 }
902 }
903
904 if (found)
905 break;
906 }
907
908 if (found)
909 break;
910 }
911
912 assert(found);
913 }
914 }
915
916 const bool has_neumann = local_neumann_boundary.size() > 0 || local_boundary.size() < prev_b_size;
917 use_avg_pressure = !has_neumann;
918
919 for (int i = prev_bases; i < n_bases; ++i)
920 {
921 for (int d = 0; d < problem_dim; ++d)
922 boundary_nodes.push_back(i * problem_dim + d);
923 }
924
925 std::sort(boundary_nodes.begin(), boundary_nodes.end());
926 auto it = std::unique(boundary_nodes.begin(), boundary_nodes.end());
927 boundary_nodes.resize(std::distance(boundary_nodes.begin(), it));
928
929 const auto &curret_bases = geom_bases();
930 const int n_samples = 10;
931 stats.compute_mesh_size(*mesh, curret_bases, n_samples, args["output"]["advanced"]["curved_mesh_size"]);
933 {
935 }
937 {
939 }
940
941 if (is_contact_enabled())
942 {
943 min_boundary_edge_length = std::numeric_limits<double>::max();
944 for (const auto &edge : collision_mesh.edges().rowwise())
945 {
946 const VectorNd v0 = collision_mesh.rest_positions().row(edge(0));
947 const VectorNd v1 = collision_mesh.rest_positions().row(edge(1));
948 min_boundary_edge_length = std::min(min_boundary_edge_length, (v1 - v0).norm());
949 }
950
951 double dhat = Units::convert(args["contact"]["dhat"], units.length());
952 args["contact"]["epsv"] = Units::convert(args["contact"]["epsv"], units.velocity());
953 args["contact"]["dhat"] = dhat;
954
955 if (!has_dhat && dhat > min_boundary_edge_length)
956 {
957 args["contact"]["dhat"] = double(args["contact"]["dhat_percentage"]) * min_boundary_edge_length;
958 logger().info("dhat set to {}", double(args["contact"]["dhat"]));
959 }
960 else
961 {
962 if (dhat > min_boundary_edge_length)
963 logger().warn("dhat larger than min boundary edge, {} > {}", dhat, min_boundary_edge_length);
964 }
965 }
966
967 logger().info("n_bases {}", n_bases);
968
969 timings.building_basis_time = timer.getElapsedTime();
970 logger().info(" took {}s", timings.building_basis_time);
971
972 logger().info("flipped elements {}", stats.n_flipped);
973 logger().info("h: {}", stats.mesh_size);
974 logger().info("n bases: {}", n_bases);
975 logger().info("n pressure bases: {}", n_pressure_bases);
976
977 if (n_bases <= args["solver"]["advanced"]["cache_size"])
978 {
979 timer.start();
980 logger().info("Building cache...");
981 ass_vals_cache.init(mesh->is_volume(), bases, curret_bases);
982 mass_ass_vals_cache.init(mesh->is_volume(), bases, curret_bases, true);
983 pure_mass_ass_vals_cache.init(mesh->is_volume(), bases, curret_bases, true);
984 if (mixed_assembler != nullptr)
985 pressure_ass_vals_cache.init(mesh->is_volume(), pressure_bases, curret_bases);
986
987 logger().info(" took {}s", timer.getElapsedTime());
988 }
989 else
990 {
993 if (mixed_assembler != nullptr)
995 }
996
997 out_geom.build_grid(*mesh, args["output"]["advanced"]["sol_on_grid"]);
998
999 if ((!problem->is_time_dependent() || args["time"]["quasistatic"]) && boundary_nodes.empty())
1000 {
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())
1005 || has_periodic_bc();
1006 if (!has_global_constraints)
1007 {
1008 log_and_throw_error("Static problem need to have some Dirichlet nodes or linear constraints!");
1009 }
1010 logger().warn("Static problem has no Dirichlet nodes; relying on linear constraints for uniqueness");
1011 }
1012 }
1013
1015 {
1016 if (!mesh)
1017 {
1018 logger().error("Load the mesh first!");
1019 return;
1020 }
1021
1022 rhs.resize(0, 0);
1023
1024 if (poly_edge_to_data.empty() && polys.empty())
1025 {
1027 return;
1028 }
1029
1030 igl::Timer timer;
1031 timer.start();
1032 logger().info("Computing polygonal basis...");
1033
1034 // std::sort(boundary_nodes.begin(), boundary_nodes.end());
1035
1036 // mixed not supports polygonal bases
1037 assert(n_pressure_bases == 0 || poly_edge_to_data.size() == 0);
1038
1039 int new_bases = 0;
1040
1041 if (iso_parametric())
1042 {
1043 if (mesh->is_volume())
1044 {
1045 if (args["space"]["poly_basis_type"] == "MeanValue" || args["space"]["poly_basis_type"] == "Wachspress")
1046 logger().error("Barycentric bases not supported in 3D");
1047 assert(assembler->is_linear());
1049 *dynamic_cast<LinearAssembler *>(assembler.get()),
1050 args["space"]["advanced"]["n_harmonic_samples"],
1051 *dynamic_cast<Mesh3D *>(mesh.get()),
1052 n_bases,
1053 args["space"]["advanced"]["quadrature_order"],
1054 args["space"]["advanced"]["mass_quadrature_order"],
1055 args["space"]["advanced"]["integral_constraints"],
1056 bases,
1057 bases,
1059 polys_3d);
1060 }
1061 else
1062 {
1063 if (args["space"]["poly_basis_type"] == "MeanValue")
1064 {
1066 assembler->name(),
1067 assembler->is_tensor() ? 2 : 1,
1068 *dynamic_cast<Mesh2D *>(mesh.get()),
1069 n_bases,
1070 args["space"]["advanced"]["quadrature_order"],
1071 args["space"]["advanced"]["mass_quadrature_order"],
1073 }
1074 else if (args["space"]["poly_basis_type"] == "Wachspress")
1075 {
1077 assembler->name(),
1078 assembler->is_tensor() ? 2 : 1,
1079 *dynamic_cast<Mesh2D *>(mesh.get()),
1080 n_bases,
1081 args["space"]["advanced"]["quadrature_order"],
1082 args["space"]["advanced"]["mass_quadrature_order"],
1084 }
1085 else
1086 {
1087 assert(assembler->is_linear());
1089 *dynamic_cast<LinearAssembler *>(assembler.get()),
1090 args["space"]["advanced"]["n_harmonic_samples"],
1091 *dynamic_cast<Mesh2D *>(mesh.get()),
1092 n_bases,
1093 args["space"]["advanced"]["quadrature_order"],
1094 args["space"]["advanced"]["mass_quadrature_order"],
1095 args["space"]["advanced"]["integral_constraints"],
1096 bases,
1097 bases,
1099 polys);
1100 }
1101 }
1102 }
1103 else
1104 {
1105 if (mesh->is_volume())
1106 {
1107 if (args["space"]["poly_basis_type"] == "MeanValue" || args["space"]["poly_basis_type"] == "Wachspress")
1108 {
1109 logger().error("Barycentric bases not supported in 3D");
1110 throw std::runtime_error("not implemented");
1111 }
1112 else
1113 {
1114 assert(assembler->is_linear());
1116 *dynamic_cast<LinearAssembler *>(assembler.get()),
1117 args["space"]["advanced"]["n_harmonic_samples"],
1118 *dynamic_cast<Mesh3D *>(mesh.get()),
1119 n_bases,
1120 args["space"]["advanced"]["quadrature_order"],
1121 args["space"]["advanced"]["mass_quadrature_order"],
1122 args["space"]["advanced"]["integral_constraints"],
1123 bases,
1126 polys_3d);
1127 }
1128 }
1129 else
1130 {
1131 if (args["space"]["poly_basis_type"] == "MeanValue")
1132 {
1134 assembler->name(),
1135 assembler->is_tensor() ? 2 : 1,
1136 *dynamic_cast<Mesh2D *>(mesh.get()),
1137 n_bases, args["space"]["advanced"]["quadrature_order"],
1138 args["space"]["advanced"]["mass_quadrature_order"],
1140 }
1141 else if (args["space"]["poly_basis_type"] == "Wachspress")
1142 {
1144 assembler->name(),
1145 assembler->is_tensor() ? 2 : 1,
1146 *dynamic_cast<Mesh2D *>(mesh.get()),
1147 n_bases, args["space"]["advanced"]["quadrature_order"],
1148 args["space"]["advanced"]["mass_quadrature_order"],
1150 }
1151 else
1152 {
1153 assert(assembler->is_linear());
1155 *dynamic_cast<LinearAssembler *>(assembler.get()),
1156 args["space"]["advanced"]["n_harmonic_samples"],
1157 *dynamic_cast<Mesh2D *>(mesh.get()),
1158 n_bases,
1159 args["space"]["advanced"]["quadrature_order"],
1160 args["space"]["advanced"]["mass_quadrature_order"],
1161 args["space"]["advanced"]["integral_constraints"],
1162 bases,
1165 polys);
1166 }
1167 }
1168 }
1169
1170 timer.stop();
1171 timings.computing_poly_basis_time = timer.getElapsedTime();
1172 logger().info(" took {}s", timings.computing_poly_basis_time);
1173
1174 n_bases += new_bases;
1175 }
1176
1178 {
1179 assert(!mesh->is_volume());
1180 const int dim = mesh->dimension();
1181 const int n_tiles = 2;
1182
1183 if (mesh->dimension() != 2)
1184 log_and_throw_error("Periodic collision mesh is only implemented in 2D!");
1185
1186 Eigen::MatrixXd V(n_bases, dim);
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;
1191
1192 Eigen::MatrixXi E = collision_mesh.edges();
1193 for (int i = 0; i < E.size(); i++)
1194 E(i) = collision_mesh.to_full_vertex_id(E(i));
1195
1196 Eigen::MatrixXd bbox(V.cols(), 2);
1197 bbox.col(0) = V.colwise().minCoeff();
1198 bbox.col(1) = V.colwise().maxCoeff();
1199
1200 // remove boundary edges on periodic BC, buggy
1201 {
1202 std::vector<int> ind;
1203 for (int i = 0; i < E.rows(); i++)
1204 {
1205 if (!periodic_dof_mask(E(i, 0)) || !periodic_dof_mask(E(i, 1)))
1206 ind.push_back(i);
1207 }
1208
1209 E = E(ind, Eigen::all).eval();
1210 }
1211
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());
1216
1217 if (periodic_tile_offsets.rows() != dim || periodic_tile_offsets.cols() != dim
1218 || Eigen::FullPivLU<Eigen::MatrixXd>(periodic_tile_offsets).rank() != dim)
1219 log_and_throw_error("Periodic contact requires {} linearly independent periodic boundary pairs", dim);
1220 const Eigen::MatrixXd &tile_offset = periodic_tile_offsets;
1221
1222 for (int i = 0, idx = 0; i < n_tiles; i++)
1223 {
1224 for (int j = 0; j < n_tiles; j++)
1225 {
1226 Eigen::Vector2d block_id;
1227 block_id << i, j;
1228
1229 Vtmp.middleRows(idx * V.rows(), V.rows()) = V;
1230 // Vtmp.block(idx * V.rows(), 0, V.rows(), 1).array() += tile_offset(0) * i;
1231 // Vtmp.block(idx * V.rows(), 1, V.rows(), 1).array() += tile_offset(1) * j;
1232 for (int vid = 0; vid < V.rows(); vid++)
1233 Vtmp.block(idx * V.rows() + vid, 0, 1, 2) += (tile_offset * block_id).transpose();
1234
1235 Etmp.middleRows(idx * E.rows(), E.rows()) = E.array() + idx * V.rows();
1236 idx += 1;
1237 }
1238 }
1239
1240 // clean duplicated vertices
1241 Eigen::VectorXi indices;
1242 {
1243 std::vector<int> tmp;
1244 for (int i = 0; i < V.rows(); i++)
1245 {
1246 if (periodic_dof_mask(i))
1247 tmp.push_back(i);
1248 }
1249
1250 indices.resize(tmp.size() * n_tiles * n_tiles);
1251 for (int i = 0; i < n_tiles * n_tiles; i++)
1252 {
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();
1255 }
1256 }
1257
1258 Eigen::VectorXi potentially_duplicate_mask(Vtmp.rows());
1259 potentially_duplicate_mask.setZero();
1260 potentially_duplicate_mask(indices).array() = 1;
1261
1262 Eigen::MatrixXd candidates = Vtmp(indices, Eigen::all);
1263
1264 Eigen::VectorXi SVI;
1265 std::vector<int> SVJ;
1266 SVI.setConstant(Vtmp.rows(), -1);
1267 int id = 0;
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++)
1273 {
1274 if (SVI[i] < 0)
1275 {
1276 SVI[i] = id;
1277 SVJ.push_back(i);
1278 if (potentially_duplicate_mask(i))
1279 {
1280 Eigen::VectorXd diffs = (candidates.rowwise() - Vtmp.row(i)).rowwise().norm();
1281 for (int j = 0; j < diffs.size(); j++)
1282 if (diffs(j) < eps)
1283 SVI[indices[j]] = id;
1284 }
1285 id++;
1286 }
1287 }
1288
1289 Vnew = Vtmp(SVJ, Eigen::all);
1290
1291 Enew.resizeLike(Etmp);
1292 for (int d = 0; d < Etmp.cols(); d++)
1293 Enew.col(d) = SVI(Etmp.col(d));
1294
1295 std::vector<bool> is_on_surface = ipc::CollisionMesh::construct_is_on_surface(Vnew.rows(), Enew);
1296
1297 Eigen::MatrixXi boundary_triangles;
1298 Eigen::SparseMatrix<double> displacement_map;
1299 periodic_collision_mesh = ipc::CollisionMesh(is_on_surface,
1300 std::vector<bool>(Vnew.rows(), false),
1301 Vnew,
1302 Enew,
1303 boundary_triangles,
1304 displacement_map);
1305
1306 periodic_collision_mesh.init_area_jacobians();
1307
1308 periodic_collision_mesh_to_basis.setConstant(Vnew.rows(), -1);
1309 for (int i = 0; i < V.rows(); i++)
1310 for (int j = 0; j < n_tiles * n_tiles; j++)
1311 periodic_collision_mesh_to_basis(SVI[j * V.rows() + i]) = i;
1312
1313 if (periodic_collision_mesh_to_basis.maxCoeff() + 1 != V.rows())
1314 log_and_throw_error("Failed to tile mesh!");
1315 }
1316
1318 {
1321 args, [this](const std::string &p) { return resolve_input_path(p); },
1323 }
1324
1326 const mesh::Mesh &mesh,
1327 const int n_bases,
1328 const std::vector<basis::ElementBases> &bases,
1329 const std::vector<basis::ElementBases> &geom_bases,
1330 const std::vector<mesh::LocalBoundary> &total_local_boundary,
1331 const mesh::Obstacle &obstacle,
1332 const json &args,
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)
1336 {
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;
1341
1342 if (args.contains("/contact/collision_mesh"_json_pointer)
1343 && args.at("/contact/collision_mesh/enabled"_json_pointer).get<bool>())
1344 {
1345 const json collision_mesh_args = args.at("/contact/collision_mesh"_json_pointer);
1346 if (collision_mesh_args.contains("linear_map"))
1347 {
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", "");
1351 const polyfem::io::FileSystemIO resources(root_path);
1352 // TODO: handle transformation per geometry
1353 const json transformation = json_as_array(args["geometry"])[0]["transformation"];
1355 resources,
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);
1360 }
1361 else if (collision_mesh_args.contains("tessellation_type")
1362 && collision_mesh_args["tessellation_type"] == "max_order")
1363 {
1366 collision_vertices, collision_edges, collision_triangles, displacement_map_entries,
1367 utils::json_value<int>(collision_mesh_args, "sampling_order", 0));
1368 }
1369 else if (collision_mesh_args.contains("max_edge_length"))
1370 {
1371 logger().debug(
1372 "Building collision proxy with max edge length={} ...",
1373 collision_mesh_args["max_edge_length"].get<double>());
1374 igl::Timer timer;
1375 timer.start();
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);
1383 timer.stop();
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()));
1389 }
1390 else
1391 {
1394 collision_vertices, collision_edges, collision_triangles, displacement_map_entries);
1395 }
1396 }
1397 else
1398 {
1401 collision_vertices, collision_edges, collision_triangles, displacement_map_entries);
1402 }
1403
1404 std::vector<bool> is_orientable_vertex(collision_vertices.rows(), true);
1405
1406 // n_bases already contains the obstacle vertices
1407 const int num_fe_nodes = n_bases - obstacle.n_vertices();
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);
1411
1412 // Append the obstacles to the collision mesh
1413 if (obstacle.n_vertices() > 0)
1414 {
1415 append_rows(collision_vertices, obstacle.v());
1416 append_rows(collision_codim_vids, obstacle.codim_v().array() + num_fe_collision_vertices);
1417 append_rows(collision_edges, obstacle.e().array() + num_fe_collision_vertices);
1418 append_rows(collision_triangles, obstacle.f().array() + num_fe_collision_vertices);
1419
1420 for (int i = 0; i < obstacle.n_vertices(); i++)
1421 {
1422 is_orientable_vertex.push_back(false);
1423 }
1424
1425 if (!displacement_map_entries.empty())
1426 {
1427 displacement_map_entries.reserve(displacement_map_entries.size() + obstacle.n_vertices());
1428 for (int i = 0; i < obstacle.n_vertices(); i++)
1429 {
1430 displacement_map_entries.emplace_back(num_fe_collision_vertices + i, num_fe_nodes + i, 1.0);
1431 }
1432 }
1433 }
1434
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)
1438 {
1439 is_on_surface[vid] = true;
1440 }
1441
1442 Eigen::SparseMatrix<double> displacement_map;
1443 if (!displacement_map_entries.empty())
1444 {
1445 displacement_map.resize(collision_vertices.rows(), n_bases);
1446 displacement_map.setFromTriplets(displacement_map_entries.begin(), displacement_map_entries.end());
1447 }
1448
1449 collision_mesh = ipc::CollisionMesh(
1450 is_on_surface, is_orientable_vertex, collision_vertices, collision_edges, collision_triangles,
1451 displacement_map);
1452
1453 collision_mesh.can_collide = [&collision_mesh, num_fe_collision_vertices](size_t vi, size_t vj) {
1454 // obstacles do not collide with other obstacles
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;
1457 };
1458
1459 collision_mesh.init_area_jacobians();
1460 }
1461
1463 {
1464 if (!mesh)
1465 {
1466 logger().error("Load the mesh first!");
1467 return;
1468 }
1469 if (n_bases <= 0)
1470 {
1471 logger().error("Build the bases first!");
1472 return;
1473 }
1474 if (assembler->name() == "OperatorSplitting")
1475 {
1477 avg_mass = 1;
1478 return;
1479 }
1480
1481 if (!problem->is_time_dependent())
1482 {
1483 avg_mass = 1;
1485 if (!is_problem_linear())
1487
1488 return;
1489 }
1490
1491 mass.resize(0, 0);
1492
1493 igl::Timer timer;
1494 timer.start();
1495 logger().info("Assembling mass mat...");
1496
1497 if (mixed_assembler != nullptr)
1498 {
1499 StiffnessMatrix velocity_mass;
1500 mass_matrix_assembler->assemble(mesh->is_volume(), n_bases, bases, geom_bases(), mass_ass_vals_cache, 0, velocity_mass, true);
1501 if (!is_problem_linear())
1503
1504 std::vector<Eigen::Triplet<double>> mass_blocks;
1505 mass_blocks.reserve(velocity_mass.nonZeros());
1506
1507 for (int k = 0; k < velocity_mass.outerSize(); ++k)
1508 {
1509 for (StiffnessMatrix::InnerIterator it(velocity_mass, k); it; ++it)
1510 {
1511 mass_blocks.emplace_back(it.row(), it.col(), it.value());
1512 }
1513 }
1514
1515 mass.resize(n_bases * assembler->size(), n_bases * assembler->size());
1516 mass.setFromTriplets(mass_blocks.begin(), mass_blocks.end());
1517 mass.makeCompressed();
1518 }
1519 else
1520 {
1521 mass_matrix_assembler->assemble(mesh->is_volume(), n_bases, bases, geom_bases(), mass_ass_vals_cache, 0, mass, true);
1522 if (!is_problem_linear())
1524 }
1525
1526 assert(mass.size() > 0);
1527
1528 avg_mass = 0;
1529 for (int k = 0; k < mass.outerSize(); ++k)
1530 {
1531
1532 for (StiffnessMatrix::InnerIterator it(mass, k); it; ++it)
1533 {
1534 assert(it.col() == k);
1535 avg_mass += it.value();
1536 }
1537 }
1538
1539 avg_mass /= mass.rows();
1540 logger().info("average mass {}", avg_mass);
1541
1542 if (args["solver"]["advanced"]["lump_mass_matrix"])
1543 {
1545 }
1546
1547 timer.stop();
1548 timings.assembling_mass_mat_time = timer.getElapsedTime();
1549 logger().info(" took {}s", timings.assembling_mass_mat_time);
1550
1551 stats.nn_zero = mass.nonZeros();
1552 stats.num_dofs = mass.rows();
1553 stats.mat_size = (long long)mass.rows() * (long long)mass.cols();
1554 logger().info("sparsity: {}/{}", stats.nn_zero, stats.mat_size);
1555 }
1556
1557 std::shared_ptr<RhsAssembler> State::build_rhs_assembler(
1558 const int n_bases_,
1559 const std::vector<basis::ElementBases> &bases_,
1560 const assembler::AssemblyValsCache &ass_vals_cache_) const
1561 {
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; // matrix type for Pardiso (2 = SPD)
1566
1567 const int size = problem->is_scalar() ? 1 : mesh->dimension();
1568
1569 return std::make_shared<RhsAssembler>(
1570 *assembler, *mesh, &obstacle,
1573 n_bases_, size, bases_, geom_bases(), ass_vals_cache_, *problem, rhs_resources_,
1574 args["space"]["advanced"]["bc_method"],
1575 rhs_solver_params);
1576 }
1577
1578 std::shared_ptr<PressureAssembler> State::build_pressure_assembler(
1579 const int n_bases_,
1580 const std::vector<basis::ElementBases> &bases_) const
1581 {
1582 const int size = problem->is_scalar() ? 1 : mesh->dimension();
1583
1584 return std::make_shared<PressureAssembler>(
1590 n_bases_, size, bases_, geom_bases(), *problem);
1591 }
1592
1594 {
1595 if (!mesh)
1596 {
1597 logger().error("Load the mesh first!");
1598 return;
1599 }
1600 if (n_bases <= 0)
1601 {
1602 logger().error("Build the bases first!");
1603 return;
1604 }
1605
1606 igl::Timer timer;
1607
1608 json p_params = {};
1609 p_params["formulation"] = assembler->name();
1610 p_params["root_path"] = root_path();
1611 {
1612 RowVectorNd min, max, delta;
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)};
1617 else
1618 p_params["bbox_center"] = {delta(0), delta(1)};
1619 }
1620 const polyfem::io::FileSystemIO resources(root_path());
1621 problem->set_parameters(p_params, resources);
1622
1623 rhs.resize(0, 0);
1624
1625 timer.start();
1626 logger().info("Assigning rhs...");
1627
1629 solve_data.rhs_assembler->assemble(mass_matrix_assembler->density(), rhs);
1630 rhs *= -1;
1631
1632 // if(problem->is_mixed())
1633 if (mixed_assembler != nullptr)
1634 {
1635 const int prev_size = rhs.size();
1636 const int n_larger = n_pressure_bases + (use_avg_pressure ? (assembler->is_fluid() ? 1 : 0) : 0);
1637 rhs.conservativeResize(prev_size + n_larger, rhs.cols());
1638 if (assembler->name() == "OperatorSplitting")
1639 {
1641 return;
1642 }
1643 // Divergence free rhs
1644 if (assembler->name() != "Bilaplacian" || local_neumann_boundary.empty())
1645 {
1646 rhs.block(prev_size, 0, n_larger, rhs.cols()).setZero();
1647 }
1648 else
1649 {
1650 Eigen::MatrixXd tmp(n_pressure_bases, 1);
1651 tmp.setZero();
1652
1653 std::shared_ptr<RhsAssembler> tmp_rhs_assembler = build_rhs_assembler(
1655
1656 tmp_rhs_assembler->set_bc(std::vector<LocalBoundary>(), std::vector<int>(), n_boundary_samples(), local_neumann_boundary, tmp);
1657 rhs.block(prev_size, 0, n_larger, rhs.cols()) = tmp;
1658 }
1659 }
1660
1661 timer.stop();
1662 timings.assigning_rhs_time = timer.getElapsedTime();
1663 logger().info(" took {}s", timings.assigning_rhs_time);
1664 }
1665
1666 void State::solve_problem(Eigen::MatrixXd &sol,
1667 Eigen::MatrixXd &pressure,
1668 UserPostStepCallback user_post_step,
1669 const InitialConditionOverride *ic_override)
1670 {
1671 if (!mesh)
1672 {
1673 logger().error("Load the mesh first!");
1674 return;
1675 }
1676 if (n_bases <= 0)
1677 {
1678 logger().error("Build the bases first!");
1679 return;
1680 }
1681
1682 // if (rhs.size() <= 0)
1683 // {
1684 // logger().error("Assemble the rhs first!");
1685 // return;
1686 // }
1687
1688 // sol.resize(0, 0);
1689 // pressure.resize(0, 0);
1690 stats.spectrum.setZero();
1691
1692 igl::Timer timer;
1693 timer.start();
1694 logger().info("Solving {}", assembler->name());
1695
1696 init_solve(sol, pressure, ic_override);
1697
1698 if (problem->is_time_dependent())
1699 {
1700 const double t0 = args["time"]["t0"];
1701 const int time_steps = args["time"]["time_steps"];
1702 const double dt = args["time"]["dt"];
1703
1704 // Pre log the output path for easier watching
1705 if (args["output"]["advanced"]["save_time_sequence"])
1706 {
1707 logger().info("Time sequence of simulation will be written to: \"{}\"",
1708 resolve_output_path(args["output"]["paraview"]["file_name"]));
1709 }
1710
1711 if (assembler->name() == "NavierStokes")
1712 log_and_throw_error("NavierStokes is only supported through VarFormFactory");
1713 else if (assembler->name() == "OperatorSplitting")
1714 solve_transient_navier_stokes_split(time_steps, dt, sol, pressure, user_post_step);
1715 else if (is_homogenization())
1716 solve_homogenization(time_steps, t0, dt, sol, user_post_step);
1717 else if (is_problem_linear())
1718 solve_transient_linear(time_steps, t0, dt, sol, pressure, user_post_step, ic_override);
1719 else if (!assembler->is_linear() && problem->is_scalar())
1720 throw std::runtime_error("Nonlinear scalar problems are not supported yet!");
1721 else
1722 solve_transient_tensor_nonlinear(time_steps, t0, dt, sol, user_post_step, ic_override);
1723 }
1724 else
1725 {
1726 if (assembler->name() == "NavierStokes")
1727 log_and_throw_error("NavierStokes is only supported through VarFormFactory");
1728 else if (is_homogenization())
1729 solve_homogenization(/* time steps */ 0, /* t0 */ 0, /* dt */ 0, sol, user_post_step);
1730 else if (is_problem_linear())
1731 {
1732 init_linear_solve(sol, 1.0, ic_override);
1733 solve_linear(0, sol, pressure, user_post_step);
1734 }
1735 else if (!assembler->is_linear() && problem->is_scalar())
1736 throw std::runtime_error("Nonlinear scalar problems are not supported yet!");
1737 else
1738 {
1739 init_nonlinear_tensor_solve(sol, 1.0, true, ic_override);
1740 solve_tensor_nonlinear(0, sol, true, user_post_step);
1741
1742 const std::string state_path = resolve_output_path(args["output"]["data"]["state"]);
1743 if (!state_path.empty())
1744 polyfem::io::write_matrix(state_path, "u", sol);
1745 }
1746 }
1747
1748 timer.stop();
1749 timings.solving_time = timer.getElapsedTime();
1750 logger().info(" took {}s", timings.solving_time);
1751 }
1752
1753} // namespace polyfem::legacy
int V
ElementAssemblyValues vals
Definition Assembler.cpp:26
int x
std::string velocity() const
Definition Units.hpp:29
static double convert(const json &val, const std::string &unit_type)
Definition Units.cpp:35
const std::string & length() const
Definition Units.hpp:19
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
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,...
assemble matrix based on the local assembler local assembler is eg Laplace, LinearElasticity etc
void init(const int dim, const json &param, 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 ...
Definition OutData.cpp:434
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
Definition OutData.cpp:737
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
Definition OutData.cpp:2815
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...
Definition OutData.cpp:2732
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
Definition OutData.cpp:2810
double min_edge_length
min edge lenght
Runtime override for initial-condition histories.
Definition State.hpp:91
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition State.hpp:208
bool iso_parametric() const
check if using iso parametric bases
Definition State.cpp:471
StiffnessMatrix pure_mass
Definition State.hpp:244
Eigen::VectorXi periodic_dof_mask
Explicit periodic boundary-pair data used by periodic contact.
Definition State.hpp:489
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.
Definition State.hpp:268
ipc::CollisionMesh collision_mesh
IPC collision mesh.
Definition State.hpp:656
StiffnessMatrix mass
Mass matrix, it is computed only for time dependent problems.
Definition State.hpp:243
bool has_dhat
stores if input json contains dhat
Definition State.hpp:715
std::vector< RowVectorNd > dirichlet_nodes_position
Definition State.hpp:556
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
Definition State.cpp:1317
std::vector< mesh::LocalBoundary > local_boundary
mapping from elements to nodes for dirichlet boundary conditions
Definition State.hpp:545
std::vector< mesh::LocalBoundary > local_pressure_boundary
mapping from elements to nodes for pressure boundary conditions
Definition State.hpp:549
std::vector< int > dirichlet_nodes
per node dirichlet
Definition State.hpp:555
std::shared_ptr< polyfem::mesh::MeshNodes > mesh_nodes
Mapping from input nodes to FE nodes.
Definition State.hpp:233
Eigen::VectorXi in_primitive_to_primitive
maps in vertices/edges/faces/cells to polyfem vertices/edges/faces/cells
Definition State.hpp:564
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
Definition State.hpp:196
std::vector< int > pressure_boundary_nodes
list of neumann boundary nodes
Definition State.hpp:541
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
Definition State.hpp:551
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Definition State.hpp:578
Eigen::MatrixXd rhs
System right-hand side.
Definition State.hpp:252
void build_node_mapping()
build the mapping from input nodes to polyfem nodes
Definition State.cpp:254
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.
Definition State.hpp:728
json args
main input arguments containing all defaults
Definition State.hpp:140
void build_polygonal_basis()
builds bases for polygons, called inside build_basis
Definition State.cpp:1014
assembler::AssemblyValsCache pure_mass_ass_vals_cache
Definition State.hpp:238
std::vector< basis::ElementBases > geom_bases_
Geometric mapping bases, if the elements are isoparametric, this list is empty.
Definition State.hpp:215
std::vector< RowVectorNd > neumann_nodes_position
Definition State.hpp:559
Eigen::VectorXi disc_ordersq
Definition State.hpp:230
std::map< int, Eigen::MatrixXd > polys
polygons, used since poly have no geom mapping
Definition State.hpp:225
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
Definition State.cpp:380
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
Definition State.hpp:220
assembler::AssemblyValsCache pressure_ass_vals_cache
used to store assembly values for pressure for small problems
Definition State.hpp:240
int n_bases
number of bases
Definition State.hpp:218
void assemble_mass_mat()
assemble mass, step 4 of solve build mass matrix based on defined basis modifies mass (and maybe more...
Definition State.cpp:1462
double starting_min_edge_length
Definition State.hpp:729
std::shared_ptr< polyfem::mesh::MeshNodes > geom_mesh_nodes
Definition State.hpp:233
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
Definition State.hpp:731
io::OutRuntimeData timings
runtime statistics
Definition State.hpp:726
std::vector< basis::ElementBases > pressure_bases
FE pressure bases for mixed elements, the size is #elements.
Definition State.hpp:213
mesh::Obstacle obstacle
Obstacles used in collisions.
Definition State.hpp:580
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)
Definition State.cpp:337
Eigen::VectorXi disc_orders
vector of discretization orders, used when not all elements have the same degree, one per element
Definition State.hpp:230
assembler::AssemblyValsCache ass_vals_cache
used to store assembly values for small problems
Definition State.hpp:236
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
Definition State.hpp:724
assembler::AssemblyValsCache mass_ass_vals_cache
Definition State.hpp:237
QuadratureOrders n_boundary_samples() const
quadrature used for projecting boundary conditions
Definition State.hpp:308
bool use_avg_pressure
use average pressure for stokes problem to fix the additional dofs, true by default if false,...
Definition State.hpp:256
std::shared_ptr< assembler::Assembler > assembler
assemblers
Definition State.hpp:194
void build_basis()
builds the bases step 2 of solve modifies bases, pressure_bases, geom_bases_, boundary_nodes,...
Definition State.cpp:509
std::map< int, basis::InterfaceData > poly_edge_to_data
nodes on the boundary of polygonal elements, used for harmonic bases
Definition State.hpp:553
double avg_mass
average system mass, used for contact with IPC
Definition State.hpp:246
std::vector< basis::ElementBases > bases
FE bases, the size is #elements.
Definition State.hpp:211
void solve_linear(int step, Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={})
solves a linear problem
Eigen::MatrixXd periodic_tile_offsets
Definition State.hpp:490
polyfem::io::FileSystemIO rhs_resources_
Definition State.hpp:117
void build_periodic_collision_mesh()
Definition State.cpp:1177
std::map< int, std::pair< Eigen::MatrixXd, Eigen::MatrixXi > > polys_3d
polyhedra, used since poly have no geom mapping
Definition State.hpp:227
bool has_periodic_bc() const
Definition State.hpp:491
ipc::CollisionMesh periodic_collision_mesh
IPC collision mesh under periodic BC.
Definition State.hpp:659
std::vector< int > neumann_nodes
per node neumann
Definition State.hpp:558
Eigen::VectorXi periodic_collision_mesh_to_basis
index mapping from periodic 2x2 collision mesh to FE periodic mesh
Definition State.hpp:661
std::shared_ptr< polyfem::mesh::MeshNodes > pressure_mesh_nodes
Definition State.hpp:233
assembler::MacroStrainValue macro_strain_constraint
Definition State.hpp:798
std::shared_ptr< assembler::RhsAssembler > build_rhs_assembler() const
build a RhsAssembler for the problem
Definition State.hpp:293
bool is_problem_linear() const
Returns whether the system is linear. Collisions and pressure add nonlinearity to the problem.
Definition State.hpp:514
std::shared_ptr< assembler::PressureAssembler > build_pressure_assembler() const
Definition State.hpp:301
std::vector< mesh::LocalBoundary > total_local_boundary
mapping from elements to nodes for all mesh
Definition State.hpp:543
int n_geom_bases
number of geometric bases
Definition State.hpp:222
std::shared_ptr< assembler::HRZMass > pure_mass_matrix_assembler
Definition State.hpp:197
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...
Definition State.cpp:1593
void solve_problem(Eigen::MatrixXd &sol, Eigen::MatrixXd &pressure, UserPostStepCallback user_post_step={}, const InitialConditionOverride *ic_override=nullptr)
solves the problems
Definition State.cpp:1666
double starting_max_edge_length
Definition State.hpp:730
std::vector< mesh::LocalBoundary > local_neumann_boundary
mapping from elements to nodes for neumann boundary conditions
Definition State.hpp:547
std::vector< int > primitive_to_node() const
Definition State.cpp:237
std::vector< int > boundary_nodes
list of boundary nodes
Definition State.hpp:539
solver::SolveData solve_data
timedependent stuff cached
Definition State.hpp:385
std::vector< int > node_to_primitive() const
Definition State.cpp:244
Eigen::VectorXi in_node_to_node
Inpute nodes (including high-order) to polyfem nodes, only for isoparametric.
Definition State.hpp:562
bool is_homogenization() const
Definition State.hpp:804
bool is_contact_enabled() const
does the simulation have contact
Definition State.hpp:692
std::shared_ptr< assembler::MixedAssembler > mixed_assembler
Definition State.hpp:199
void build_grid(const polyfem::mesh::Mesh &mesh, const double spacing)
builds the grid to export the solution
Definition OutData.cpp:2782
bool is_volume() const override
checks if mesh is volume
Definition Mesh3D.hpp:28
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:50
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:148
const Eigen::MatrixXi & e() const
Definition Obstacle.hpp:45
const Eigen::MatrixXi & f() const
Definition Obstacle.hpp:44
const Eigen::MatrixXd & v() const
Definition Obstacle.hpp:42
const Eigen::VectorXi & codim_v() const
Definition Obstacle.hpp:43
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
Definition APriori.cpp:242
static Mapping build_mapping(int ndof, int value_dim, const mesh::Mesh &mesh, const std::vector< basis::ElementBases > &bases, const std::vector< mesh::LocalBoundary > &local_boundary, const std::array< int, 2 > &boundary_ids, double relative_tolerance)
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.
Definition MatrixIO.cpp:19
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.
Definition MatrixIO.cpp:43
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.
Definition State.hpp:87
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)
Definition State.cpp:398
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.
Definition JSONUtils.hpp:49
void append_rows(DstMat &dst, const SrcMat &src)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Definition Types.hpp:11
nlohmann::json json
Definition Common.hpp:9
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24