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