PolyFEM
Loading...
Searching...
No Matches
NonlinearElasticVarForm.cpp
Go to the documentation of this file.
2
5
10
17
21
30
31#include <igl/Timer.h>
32#include <igl/edges.h>
33
34#include <ipc/ipc.hpp>
35
36#include <polysolve/linear/Solver.hpp>
37#include <polysolve/nonlinear/Solver.hpp>
38
39#include <algorithm>
40#include <cmath>
41#include <limits>
42
43namespace polyfem::varform
44{
45 using namespace solver;
46 using namespace time_integrator;
47
48 void NonlinearElasticVarForm::init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path)
49 {
50 json clean_args = args;
51 const bool contact_dhat_was_explicit = clean_args["contact"].value("_dhat_was_explicit", false);
52 clean_args["contact"].erase("_dhat_was_explicit");
53 ElasticVarForm::init(formulation, units, clean_args, out_path);
54 contact_dhat_was_explicit_ = contact_dhat_was_explicit;
55 }
56
58 {
60 collision_mesh_ = ipc::CollisionMesh();
61 periodic_collision_mesh_ = ipc::CollisionMesh();
65 forms.clear();
67 damping_assembler_ = nullptr;
70 }
71
72 void NonlinearElasticVarForm::load_mesh(const mesh::Mesh &mesh, const json &args)
73 {
75
76 logger().info("Loading obstacles...");
78 units,
79 args["geometry"],
80 utils::json_as_array(args["boundary_conditions"]["obstacle_displacements"]),
81 utils::json_as_array(args["boundary_conditions"]["dirichlet_boundary"]),
82 root_path, mesh.dimension());
83 }
84
86 {
87 auto space = ElasticVarForm::output_space();
88 space.collision_mesh = is_contact_enabled() ? &collision_mesh_ : nullptr;
89 space.obstacle = &obstacle;
90 return space;
91 }
92
93 std::vector<io::OutputField> NonlinearElasticVarForm::output_fields(
94 const io::OutputSample &sample,
95 const Eigen::MatrixXd &solution,
96 const io::OutputFieldOptions &options) const
97 {
98 std::vector<io::OutputField> fields = elastic_output_fields(
99 sample, solution, options, &obstacle, solve_data_.time_integrator.get(),
101 if (!mesh_ || !problem || solution.size() <= 0)
102 return fields;
104 return fields;
105
106 const int actual_dim = problem->is_scalar() ? 1 : mesh_->dimension();
107 const auto &paraview_options = args["output"]["paraview"]["options"];
108 const bool explicit_fields = !options.fields.empty();
109
110 const auto has_field = [&](const std::string &name) {
111 return std::any_of(fields.begin(), fields.end(), [&](const io::OutputField &field) {
112 return field.association == io::OutputField::Association::Point && field.name == name;
113 });
114 };
115
116 const auto append_collision_dof_field = [&](const std::string &name, const Eigen::MatrixXd &dof_values) {
117 if (has_field(name) || dof_values.size() <= 0)
118 return;
119
120 Eigen::MatrixXd values = collision_mesh_.map_displacements(utils::unflatten(dof_values, actual_dim));
121 if (values.rows() == sample.points.rows())
122 fields.push_back({name, values, io::OutputField::Association::Point});
123 };
124
125 const auto append_collision_form_force = [&](const std::string &name, const std::shared_ptr<solver::Form> &form) {
126 if (!form || !form->enabled() || sample.points.rows() != collision_mesh_.rest_positions().rows())
127 return;
128
129 Eigen::VectorXd force;
130 form->first_derivative(solution.col(0), force);
131 const double acceleration_scaling =
132 solve_data_.time_integrator ? solve_data_.time_integrator->acceleration_scaling() : 1;
133 force *= -1.0 / acceleration_scaling;
134 append_collision_dof_field(name, force);
135 };
136
137 if (paraview_options["forces"] && !problem->is_scalar())
138 {
139 const double s = solve_data_.time_integrator ? solve_data_.time_integrator->acceleration_scaling() : 1;
140 for (const auto &[name, form] : solve_data_.named_forms())
141 {
142 const std::string field_name = name + "_forces";
143 if (!options.export_field(field_name))
144 continue;
145
146 Eigen::VectorXd force;
147 if (form && form->enabled())
148 {
149 form->first_derivative(solution, force);
150 force *= -1.0 / s;
151 }
152 else
153 {
154 force.setZero(solution.size());
155 }
156 append_collision_dof_field(field_name, force);
157 }
158 }
159
160 if (options.export_field("gradient_of_elastic_potential") && solve_data_.elastic_form)
161 {
162 Eigen::VectorXd potential_grad;
163 solve_data_.elastic_form->first_derivative(solution, potential_grad);
164 append_collision_dof_field("gradient_of_elastic_potential", potential_grad);
165 }
166
167 if (options.export_field("gradient_of_contact_potential") && solve_data_.contact_form && solve_data_.contact_form->weight() > 0)
168 {
169 Eigen::VectorXd potential_grad;
170 solve_data_.contact_form->first_derivative(solution, potential_grad);
171 potential_grad *= -solve_data_.contact_form->barrier_stiffness() / solve_data_.contact_form->weight();
172 append_collision_dof_field("gradient_of_contact_potential", potential_grad);
173 }
174
175 if (options.export_field("displacement"))
176 append_collision_dof_field("displacement", solution);
177 if (options.export_field("solution"))
178 append_collision_dof_field("solution", solution);
179
180 if ((paraview_options["contact_forces"] || explicit_fields) && options.export_field("contact_forces"))
181 append_collision_form_force("contact_forces", solve_data_.contact_form);
182 if ((paraview_options["friction_forces"] || explicit_fields) && options.export_field("friction_forces"))
183 append_collision_form_force("friction_forces", solve_data_.friction_form);
184 if ((paraview_options["normal_adhesion_forces"] || explicit_fields) && options.export_field("normal_adhesion_forces"))
185 append_collision_form_force("normal_adhesion_forces", solve_data_.normal_adhesion_form);
186 if ((paraview_options["tangential_adhesion_forces"] || explicit_fields) && options.export_field("tangential_adhesion_forces"))
187 append_collision_form_force("tangential_adhesion_forces", solve_data_.tangential_adhesion_form);
188
189 if (explicit_fields
190 && options.export_field("adaptive_dhat")
191 && args["contact"]["use_gcp_formulation"]
192 && args["contact"]["use_adaptive_dhat"])
193 {
194 const auto smooth_contact = std::dynamic_pointer_cast<solver::SmoothContactForm>(solve_data_.contact_form);
195 if (smooth_contact)
196 {
197 const auto &set = smooth_contact->collision_set();
198 if (actual_dim == 2)
199 {
200 Eigen::VectorXd dhats(collision_mesh_.num_edges());
201 for (int e = 0; e < dhats.size(); ++e)
202 dhats(e) = set.get_edge_dhat(e);
203 fields.push_back({"dhat", dhats, io::OutputField::Association::Cell});
204 }
205 else
206 {
207 Eigen::VectorXd dhats(collision_mesh_.num_faces());
208 for (int f = 0; f < dhats.size(); ++f)
209 dhats(f) = set.get_face_dhat(f);
210 fields.push_back({"dhat_face", dhats, io::OutputField::Association::Cell});
211
212 Eigen::VectorXd vertex_dhats(collision_mesh_.num_vertices());
213 for (int v = 0; v < vertex_dhats.size(); ++v)
214 vertex_dhats(v) = set.get_vert_dhat(v);
215 fields.push_back({"dhat_vert", vertex_dhats, io::OutputField::Association::Point});
216 }
217 }
218 }
219
220 return fields;
221 }
222
223 void NonlinearElasticVarForm::build_basis(mesh::Mesh &mesh, const bool iso_parametric, const json &args)
224 {
225 ElasticVarForm::build_basis(mesh, iso_parametric, args);
226
227 // Legacy nonlinear/contact code assumes the displacement space includes obstacle vertices.
228 // The shared build path only counts FE bases, so extend it here
229 // before constructing collision/contact state.
230 const int n_fe_bases = space_.n_bases;
231 space_.n_bases += obstacle.n_vertices();
232
233 if (is_contact_enabled())
234 {
235 logger().info("Building collision mesh...");
236 build_collision_mesh(mesh, args);
237 preprocess_contact_parameters();
238
239 if (args["contact"]["periodic"])
240 build_periodic_collision_mesh();
241 }
242
243 logger().info("Done!");
244
245 for (int i = n_fe_bases; i < space_.n_bases; ++i)
246 {
247 for (int d = 0; d < mesh.dimension(); ++d)
248 boundary_.boundary_nodes.push_back(i * mesh.dimension() + d);
249 }
250
251 boundary_.normalize_boundary_nodes();
252 }
253
254 void NonlinearElasticVarForm::preprocess_contact_parameters()
255 {
256 if (!is_contact_enabled())
257 return;
258
259 double min_boundary_edge_length = std::numeric_limits<double>::max();
260 for (const auto &edge : collision_mesh_.edges().rowwise())
261 {
262 const VectorNd v0 = collision_mesh_.rest_positions().row(edge(0));
263 const VectorNd v1 = collision_mesh_.rest_positions().row(edge(1));
264 min_boundary_edge_length = std::min(min_boundary_edge_length, (v1 - v0).norm());
265 }
266
267 double dhat = Units::convert(args["contact"]["dhat"], units.length());
268 args["contact"]["epsv"] = Units::convert(args["contact"]["epsv"], units.velocity());
269
270 if (!contact_dhat_was_explicit_
271 && std::isfinite(min_boundary_edge_length)
272 && dhat > min_boundary_edge_length)
273 {
274 dhat = args["contact"]["dhat_percentage"].get<double>() * min_boundary_edge_length;
275 logger().info("dhat set to {}", dhat);
276 }
277 else if (std::isfinite(min_boundary_edge_length) && dhat > min_boundary_edge_length)
278 {
279 logger().warn("dhat larger than min boundary edge, {} > {}", dhat, min_boundary_edge_length);
280 }
281
282 args["contact"]["dhat"] = dhat;
283 }
284
285 void NonlinearElasticVarForm::build_rhs_assembler()
286 {
287 json rhs_solver_params = args["solver"]["linear"];
288 if (!rhs_solver_params.contains("Pardiso"))
289 rhs_solver_params["Pardiso"] = {};
290 rhs_solver_params["Pardiso"]["mtype"] = -2;
291
292 const int size = problem->is_scalar() ? 1 : mesh_->dimension();
293
294 solve_data_.rhs_assembler = std::make_shared<assembler::RhsAssembler>(
295 *primary_assembler_, *mesh_, &obstacle,
296 boundary_.dirichlet_nodes, boundary_.neumann_nodes,
297 boundary_.dirichlet_nodes_position, boundary_.neumann_nodes_position,
298 space_.n_bases, size, space_.basis_list(), space_.geometry_basis_list(), mass_ass_vals_cache_, *problem,
299 args["space"]["advanced"]["bc_method"],
300 rhs_solver_params,
301 /*fe_space_id=*/-1);
302 rhs_assembler_ = solve_data_.rhs_assembler;
303 }
304
305 void NonlinearElasticVarForm::build_collision_mesh(
306 const mesh::Mesh &mesh,
307 const json &args)
308 {
309 build_collision_mesh(
310 mesh, space_.n_bases, space_.basis_list(), space_.geometry_basis_list(), boundary_.total_local_boundary, obstacle,
311 args, [this](const std::string &p) { return utils::resolve_path(p, root_path, false); },
312 space_.space_in_node_to_node, collision_mesh_);
313 }
314
315 void NonlinearElasticVarForm::build_collision_mesh(
316 const mesh::Mesh &mesh,
317 const int n_bases,
318 const std::vector<basis::ElementBases> &bases,
319 const std::vector<basis::ElementBases> &geom_bases,
320 const std::vector<mesh::LocalBoundary> &total_local_boundary,
321 const mesh::Obstacle &obstacle,
322 const json &args,
323 const std::function<std::string(const std::string &)> &resolve_input_path,
324 const Eigen::VectorXi &in_node_to_node,
325 ipc::CollisionMesh &collision_mesh_)
326 {
327 Eigen::MatrixXd collision_vertices;
328 Eigen::VectorXi collision_codim_vids;
329 Eigen::MatrixXi collision_edges, collision_triangles;
330 std::vector<Eigen::Triplet<double>> displacement_map_entries;
331
332 const auto extract_default_collision_mesh = [&]() {
333 if (args.at("/space/basis_type"_json_pointer) == "Spline")
334 {
336 mesh, n_bases - obstacle.n_vertices(), bases, total_local_boundary,
337 collision_vertices, collision_edges, collision_triangles, displacement_map_entries);
338 }
339 else
340 {
342 mesh, n_bases - obstacle.n_vertices(), bases, total_local_boundary,
343 collision_vertices, collision_edges, collision_triangles, displacement_map_entries);
344 }
345 };
346
347 if (args.contains("/contact/collision_mesh"_json_pointer)
348 && args.at("/contact/collision_mesh/enabled"_json_pointer).get<bool>())
349 {
350 const json collision_mesh_args = args.at("/contact/collision_mesh"_json_pointer);
351 if (collision_mesh_args.contains("linear_map"))
352 {
353 assert(displacement_map_entries.empty());
354 assert(collision_mesh_args.contains("mesh"));
355 const std::string root_path = utils::json_value<std::string>(args, "root_path", "");
356 // TODO: handle transformation per geometry
357 const json transformation = utils::json_as_array(args["geometry"])[0]["transformation"];
359 utils::resolve_path(collision_mesh_args["mesh"], root_path),
360 utils::resolve_path(collision_mesh_args["linear_map"], root_path),
361 in_node_to_node, transformation, collision_vertices, collision_codim_vids,
362 collision_edges, collision_triangles, displacement_map_entries);
363 }
364 else if (collision_mesh_args.contains("tessellation_type")
365 && collision_mesh_args["tessellation_type"] == "max_order")
366 {
368 mesh, n_bases - obstacle.n_vertices(), bases, total_local_boundary,
369 collision_vertices, collision_edges, collision_triangles, displacement_map_entries,
370 utils::json_value<int>(collision_mesh_args, "sampling_order", 0));
371 }
372 else if (collision_mesh_args.contains("max_edge_length"))
373 {
374 logger().debug(
375 "Building collision proxy with max edge length={} ...",
376 collision_mesh_args["max_edge_length"].get<double>());
377 igl::Timer timer;
378 timer.start();
380 bases, geom_bases, total_local_boundary, n_bases, mesh.dimension(),
381 collision_mesh_args["max_edge_length"], collision_vertices,
382 collision_triangles, displacement_map_entries,
383 collision_mesh_args["tessellation_type"]);
384 if (collision_triangles.size())
385 igl::edges(collision_triangles, collision_edges);
386 timer.stop();
387 logger().debug(fmt::format(
388 std::locale("en_US.UTF-8"),
389 "Done (took {:g}s, {:L} vertices, {:L} triangles)",
390 timer.getElapsedTime(),
391 collision_vertices.rows(), collision_triangles.rows()));
392 }
393 else
394 {
395 extract_default_collision_mesh();
396 }
397 }
398 else
399 {
400 extract_default_collision_mesh();
401 }
402
403 std::vector<bool> is_orientable_vertex(collision_vertices.rows(), true);
404
405 // n_bases already contains the obstacle vertices
406 const int num_fe_nodes = n_bases - obstacle.n_vertices();
407 const int num_fe_collision_vertices = collision_vertices.rows();
408 assert(collision_edges.size() == 0 || collision_edges.maxCoeff() < num_fe_collision_vertices);
409 assert(collision_triangles.size() == 0 || collision_triangles.maxCoeff() < num_fe_collision_vertices);
410
411 // Append the obstacles to the collision mesh
412 if (obstacle.n_vertices() > 0)
413 {
414 utils::append_rows(collision_vertices, obstacle.v());
415 utils::append_rows(collision_codim_vids, obstacle.codim_v().array() + num_fe_collision_vertices);
416 utils::append_rows(collision_edges, obstacle.e().array() + num_fe_collision_vertices);
417 utils::append_rows(collision_triangles, obstacle.f().array() + num_fe_collision_vertices);
418
419 for (int i = 0; i < obstacle.n_vertices(); i++)
420 {
421 is_orientable_vertex.push_back(false);
422 }
423
424 if (!displacement_map_entries.empty())
425 {
426 displacement_map_entries.reserve(displacement_map_entries.size() + obstacle.n_vertices());
427 for (int i = 0; i < obstacle.n_vertices(); i++)
428 {
429 displacement_map_entries.emplace_back(num_fe_collision_vertices + i, num_fe_nodes + i, 1.0);
430 }
431 }
432 }
433
434 std::vector<bool> is_on_surface = ipc::CollisionMesh::construct_is_on_surface(
435 collision_vertices.rows(), collision_edges);
436 for (const int vid : collision_codim_vids)
437 {
438 is_on_surface[vid] = true;
439 }
440
441 Eigen::SparseMatrix<double> displacement_map;
442 if (!displacement_map_entries.empty())
443 {
444 displacement_map.resize(collision_vertices.rows(), n_bases);
445 displacement_map.setFromTriplets(displacement_map_entries.begin(), displacement_map_entries.end());
446 }
447
448 collision_mesh_ = ipc::CollisionMesh(
449 is_on_surface, is_orientable_vertex, collision_vertices, collision_edges, collision_triangles,
450 displacement_map);
451
452 collision_mesh_.can_collide = [&collision_mesh_, num_fe_collision_vertices](size_t vi, size_t vj) {
453 // obstacles do not collide with other obstacles
454 return collision_mesh_.to_full_vertex_id(vi) < num_fe_collision_vertices
455 || collision_mesh_.to_full_vertex_id(vj) < num_fe_collision_vertices;
456 };
457
458 collision_mesh_.init_area_jacobians();
459 }
460
461 void NonlinearElasticVarForm::build_periodic_collision_mesh()
462 {
463 assert(!mesh_->is_volume());
464 const int dim = mesh_->dimension();
465 const int n_tiles = 2;
466
467 if (mesh_->dimension() != 2)
468 log_and_throw_error("Periodic collision mesh is only implemented in 2D!");
469 if (obstacle.n_vertices() != 0)
470 log_and_throw_error("Periodic contact does not support obstacles.");
471
472 const int n_bases = space_.n_bases;
473 const json &conditions = args["boundary_conditions"]["periodic"];
474 Eigen::VectorXi periodic_dof_mask = Eigen::VectorXi::Zero(n_bases);
475 Eigen::MatrixXd periodic_tile_offsets(dim, conditions.size());
476 for (int i = 0; i < int(conditions.size()); ++i)
477 {
478 const json &condition = conditions[i];
479 const std::array<int, 2> boundary_ids = {{condition["boundary_ids"][0].get<int>(),
480 condition["boundary_ids"][1].get<int>()}};
482 n_bases * dim, dim, *mesh_, space_.basis_list(), boundary_.total_local_boundary,
483 boundary_ids, condition.value("tolerance", 1e-5));
484 periodic_tile_offsets.col(i) = mapping.translation.transpose();
485 for (const int dof : mapping.boundary_dofs)
486 periodic_dof_mask(dof) = 1;
487 }
488
489 Eigen::MatrixXd V(n_bases, dim);
490 for (const auto &bs : space_.basis_list())
491 for (const auto &b : bs.bases)
492 for (const auto &g : b.global())
493 V.row(g.index) = g.node;
494
495 Eigen::MatrixXi E = collision_mesh_.edges();
496 for (int i = 0; i < E.size(); i++)
497 {
498 E(i) = collision_mesh_.to_full_vertex_id(E(i));
499 if (E(i) < 0 || E(i) >= n_bases)
500 log_and_throw_error("Periodic contact requires collision vertices to map to FE basis nodes.");
501 }
502
503 Eigen::MatrixXd bbox(V.cols(), 2);
504 bbox.col(0) = V.colwise().minCoeff();
505 bbox.col(1) = V.colwise().maxCoeff();
506
507 // remove boundary edges on periodic BC, buggy
508 {
509 std::vector<int> ind;
510 for (int i = 0; i < E.rows(); i++)
511 {
512 if (!periodic_dof_mask(E(i, 0)) || !periodic_dof_mask(E(i, 1)))
513 ind.push_back(i);
514 }
515
516 E = E(ind, Eigen::all).eval();
517 }
518
519 Eigen::MatrixXd Vtmp, Vnew;
520 Eigen::MatrixXi Etmp, Enew;
521 Vtmp.setZero(V.rows() * n_tiles * n_tiles, V.cols());
522 Etmp.setZero(E.rows() * n_tiles * n_tiles, E.cols());
523
524 if (periodic_tile_offsets.rows() != dim || periodic_tile_offsets.cols() != dim
525 || Eigen::FullPivLU<Eigen::MatrixXd>(periodic_tile_offsets).rank() != dim)
526 log_and_throw_error("Periodic contact requires {} linearly independent periodic boundary pairs", dim);
527 const Eigen::MatrixXd &tile_offset = periodic_tile_offsets;
528
529 for (int i = 0, idx = 0; i < n_tiles; i++)
530 {
531 for (int j = 0; j < n_tiles; j++)
532 {
533 Eigen::Vector2d block_id;
534 block_id << i, j;
535
536 Vtmp.middleRows(idx * V.rows(), V.rows()) = V;
537 for (int vid = 0; vid < V.rows(); vid++)
538 Vtmp.block(idx * V.rows() + vid, 0, 1, 2) += (tile_offset * block_id).transpose();
539
540 Etmp.middleRows(idx * E.rows(), E.rows()) = E.array() + idx * V.rows();
541 idx += 1;
542 }
543 }
544
545 // clean duplicated vertices
546 Eigen::VectorXi indices;
547 {
548 std::vector<int> tmp;
549 for (int i = 0; i < V.rows(); i++)
550 {
551 if (periodic_dof_mask(i))
552 tmp.push_back(i);
553 }
554
555 indices.resize(tmp.size() * n_tiles * n_tiles);
556 for (int i = 0; i < n_tiles * n_tiles; i++)
557 {
558 indices.segment(i * tmp.size(), tmp.size()) = Eigen::Map<Eigen::VectorXi, Eigen::Unaligned>(tmp.data(), tmp.size());
559 indices.segment(i * tmp.size(), tmp.size()).array() += i * V.rows();
560 }
561 }
562
563 Eigen::VectorXi potentially_duplicate_mask(Vtmp.rows());
564 potentially_duplicate_mask.setZero();
565 potentially_duplicate_mask(indices).array() = 1;
566
567 Eigen::MatrixXd candidates = Vtmp(indices, Eigen::all);
568
569 Eigen::VectorXi SVI;
570 std::vector<int> SVJ;
571 SVI.setConstant(Vtmp.rows(), -1);
572 int id = 0;
573 double relative_tolerance = 1e-5;
574 for (const json &condition : conditions)
575 relative_tolerance = std::max(relative_tolerance, condition.value("tolerance", 1e-5));
576 const double eps = (bbox.col(1) - bbox.col(0)).maxCoeff() * relative_tolerance;
577 for (int i = 0; i < Vtmp.rows(); i++)
578 {
579 if (SVI[i] < 0)
580 {
581 SVI[i] = id;
582 SVJ.push_back(i);
583 if (potentially_duplicate_mask(i))
584 {
585 Eigen::VectorXd diffs = (candidates.rowwise() - Vtmp.row(i)).rowwise().norm();
586 for (int j = 0; j < diffs.size(); j++)
587 if (diffs(j) < eps)
588 SVI[indices[j]] = id;
589 }
590 id++;
591 }
592 }
593
594 Vnew = Vtmp(SVJ, Eigen::all);
595
596 Enew.resizeLike(Etmp);
597 for (int d = 0; d < Etmp.cols(); d++)
598 Enew.col(d) = SVI(Etmp.col(d));
599
600 std::vector<bool> is_on_surface = ipc::CollisionMesh::construct_is_on_surface(Vnew.rows(), Enew);
601
602 Eigen::MatrixXi boundary_triangles;
603 Eigen::SparseMatrix<double> displacement_map;
604 periodic_collision_mesh_ = ipc::CollisionMesh(is_on_surface,
605 std::vector<bool>(Vnew.rows(), false),
606 Vnew,
607 Enew,
608 boundary_triangles,
609 displacement_map);
610
611 periodic_collision_mesh_.init_area_jacobians();
612
613 periodic_collision_mesh_to_basis_.setConstant(Vnew.rows(), -1);
614 for (int i = 0; i < V.rows(); i++)
615 for (int j = 0; j < n_tiles * n_tiles; j++)
616 periodic_collision_mesh_to_basis_(SVI[j * V.rows() + i]) = i;
617
618 if (periodic_collision_mesh_to_basis_.maxCoeff() + 1 != V.rows())
619 log_and_throw_error("Failed to tile mesh!");
620 }
621
622 std::shared_ptr<assembler::PressureAssembler> NonlinearElasticVarForm::build_pressure_assembler() const
623 {
624 const int size = problem->is_scalar() ? 1 : mesh_->dimension();
625
626 return std::make_shared<assembler::PressureAssembler>(
627 *primary_assembler_, *mesh_, obstacle,
628 boundary_.local_pressure_boundary,
629 boundary_.local_pressure_cavity,
630 boundary_.boundary_nodes,
631 elastic_primitive_to_node(), elastic_node_to_primitive(),
632 space_.n_bases, size, space_.basis_list(), space_.geometry_basis_list(), *problem);
633 }
634
635 void NonlinearElasticStaticVarForm::solve_problem(
636 Eigen::MatrixXd &sol,
637 const InitialConditionOverride *initial_condition_override,
638 const ForwardStepCallback &post_step)
639 {
640 assert((!initial_condition_override || (initial_condition_override->velocity.size() == 0 && initial_condition_override->acceleration.size() == 0))
641 && "Static elasticity does not accept initial velocity or acceleration overrides");
642
643 stats.spectrum.setZero();
644
645 igl::Timer timer;
646 timer.start();
647 logger().info("Solving {}", primary_assembler_->name());
648
649 {
650 POLYFEM_SCOPED_TIMER("Setup RHS");
651
652 // FIXME
653 // read_initial_x_from_file(
654 // resolve_input_path(args["input"]["data"]["state"]), "u",
655 // args["input"]["data"]["reorder"], in_node_to_node,
656 // mesh->dimension(), solution);
657
658 if (initial_condition_override && initial_condition_override->solution.size() != 0)
659 initial_solution(sol, initial_condition_override);
660 else if (sol.size() <= 0)
661 initial_solution(sol, initial_condition_override);
662
663 if (initial_condition_override && initial_condition_override->solution.size() != 0)
664 assert(sol.cols() == 1 && "Static initial solution override must have exactly one column");
665 else if (sol.cols() != 1)
666 log_and_throw_error("Static elasticity requires exactly one initial solution column.");
667 }
668 init_solve(sol, 1.0, initial_condition_override);
669
670 solve_tensor_nonlinear(0, sol, true);
671 if (post_step)
672 post_step(0, sol);
673
674 const std::string state_path = resolve_output_path(args["output"]["data"]["state"]);
675 if (!state_path.empty())
676 io::write_matrix(state_path, "u", sol);
677
678 timer.stop();
679 timings.solving_time = timer.getElapsedTime();
680 logger().info(" took {}s", timings.solving_time);
681 }
682
683 void NonlinearElasticTransientVarForm::solve_problem(
684 Eigen::MatrixXd &sol,
685 const InitialConditionOverride *initial_condition_override,
686 const ForwardStepCallback &post_step)
687 {
688 const bool save_stats = args["output"]["stats"];
689 stats.spectrum.setZero();
690
691 igl::Timer timer;
692 timer.start();
693 logger().info("Solving {}", primary_assembler_->name());
694
695 {
696 POLYFEM_SCOPED_TIMER("Setup RHS");
697
698 // FIXME
699 // read_initial_x_from_file(
700 // resolve_input_path(args["input"]["data"]["state"]), "u",
701 // args["input"]["data"]["reorder"], in_node_to_node,
702 // mesh->dimension(), solution);
703
704 if (initial_condition_override && initial_condition_override->solution.size() != 0)
705 initial_solution(sol, initial_condition_override);
706 else if (sol.size() <= 0)
707 initial_solution(sol, initial_condition_override);
708
709 if (sol.cols() > 1) // ignore previous solutions
710 sol.conservativeResize(Eigen::NoChange, 1);
711 }
712 init_solve(sol, t0 + dt, initial_condition_override);
713 if (post_step)
714 post_step(0, sol);
715
716 // Write the total energy to a CSV file
717 int save_i = 0;
718
719 std::unique_ptr<io::EnergyCSVWriter> energy_csv = nullptr;
720 std::unique_ptr<io::RuntimeStatsCSVWriter> stats_csv = nullptr;
721
722 if (save_stats)
723 {
724 logger().debug("Saving nl stats to {} and {}", resolve_output_path("energy.csv"), resolve_output_path("stats.csv"));
725 energy_csv = std::make_unique<io::EnergyCSVWriter>(resolve_output_path("energy.csv"), solve_data_);
726 const io::OutputSpace space = output_space();
727 stats_csv = std::make_unique<io::RuntimeStatsCSVWriter>(
728 resolve_output_path("stats.csv"),
729 space_.n_bases,
730 space.mesh ? space.mesh->n_elements() : 0,
731 t0, dt);
732 }
733
734 // Save the initial solution
735 if (energy_csv)
736 energy_csv->write(save_i, sol);
737 save_timestep(t0, 0, t0, dt, sol);
738
739 save_i++;
740
741 for (int t = 1; t <= time_steps; ++t)
742 {
743 double forward_solve_time = 0, remeshing_time = 0, global_relaxation_time = 0;
744
745 {
746 POLYFEM_SCOPED_TIMER(forward_solve_time);
747 solve_tensor_nonlinear(t, sol, true);
748 }
749 if (post_step)
750 post_step(t, sol);
751
752 // Always save the solution for consistency
753 if (energy_csv)
754 energy_csv->write(save_i, sol);
755 save_timestep(t0 + dt * t, t, t0, dt, sol);
756 save_i++;
757
758 {
759 POLYFEM_SCOPED_TIMER("Update quantities");
760
761 if (solve_data_.time_integrator)
762 solve_data_.time_integrator->update_quantities(sol);
763
764 solve_data_.nl_problem->update_quantities(t0 + (t + 1) * dt, sol);
765
766 solve_data_.update_dt();
767 solve_data_.update_barrier_stiffness(sol);
768 }
769
770 logger().info("{}/{} t={}", t, time_steps, t0 + dt * t);
771 notify_time_step(t, time_steps, t0, dt);
772
773 save_elastic_step_state(t0, dt, t, solve_data_.time_integrator.get());
774 if (stats_csv)
775 stats_csv->write(t, forward_solve_time, remeshing_time, global_relaxation_time);
776 }
777
778 timer.stop();
779 timings.solving_time = timer.getElapsedTime();
780 logger().info(" took {}s", timings.solving_time);
781 }
782
783 void NonlinearElasticVarForm::init_forms(const json &args, const int dim, Eigen::MatrixXd &sol, const double t)
784 {
785 damping_assembler_ = std::make_shared<assembler::ViscousDamping>();
786 set_materials(*damping_assembler_, mesh_->dimension());
787
788 elasticity_pressure_assembler = build_pressure_assembler();
789
790 // for backward solve
791 damping_prev_assembler_ = std::make_shared<assembler::ViscousDampingPrev>();
792 set_materials(*damping_prev_assembler_, mesh_->dimension());
793
794 const ElementInversionCheck check_inversion = args["solver"]["advanced"]["check_inversion"];
795
796 // NOTE: some stuff are legacy and hardcoded to be off
797 forms = solve_data_.init_forms(
798 // General
799 units,
800 dim, t, space_.space_in_node_to_node,
801 // Elastic form
802 space_.n_bases, *space_.bases, space_.geometry_basis_list(), *primary_assembler_, ass_vals_cache_, mass_ass_vals_cache_, args["solver"]["advanced"]["jacobian_threshold"], check_inversion,
803 args["solver"]["advanced"]["conservative_max_iter"],
804 // Body form
805 0, boundary_.boundary_nodes, boundary_.local_boundary,
806 boundary_.local_neumann_boundary,
807 elastic_boundary_samples(), rhs_, sol, mass_assembler_->density(),
808 // Pressure form
809 boundary_.local_pressure_boundary, boundary_.local_pressure_cavity, elasticity_pressure_assembler,
810 // Inertia form
811 args.value("/time/quasistatic"_json_pointer, true), mass_,
812 damping_assembler_->is_valid() ? damping_assembler_ : nullptr,
813 // Lagged regularization form
814 args["solver"]["advanced"]["lagged_regularization_weight"],
815 args["solver"]["advanced"]["lagged_regularization_iterations"],
816 // Augmented lagrangian form
817 obstacle.ndof(), args["constraints"]["hard"], args["constraints"]["soft"], args["constraints"]["zero_mean"],
818 // Contact form
819 args["contact"]["enabled"], collision_mesh_, args["contact"]["dhat"],
820 avg_mass_, args["contact"]["use_convergent_formulation"] ? bool(args["contact"]["use_area_weighting"]) : false,
821 args["contact"]["use_convergent_formulation"] ? bool(args["contact"]["use_improved_max_operator"]) : false,
822 args["contact"]["use_convergent_formulation"] ? bool(args["contact"]["use_physical_barrier"]) : false,
823 args["solver"]["contact"]["barrier_stiffness"],
824 args["solver"]["contact"]["initial_barrier_stiffness"],
825 args["solver"]["contact"]["CCD"]["broad_phase"],
826 args["solver"]["contact"]["CCD"]["tolerance"],
827 args["solver"]["contact"]["CCD"]["max_iterations"],
828 false,
829 // Smooth Contact Form
830 args["contact"]["use_gcp_formulation"],
831 args["contact"]["alpha_t"],
832 args["contact"]["alpha_n"],
833 args["contact"]["use_adaptive_dhat"],
834 args["contact"]["min_distance_ratio"],
835 // Normal Adhesion Form
836 args["contact"]["adhesion"]["adhesion_enabled"],
837 args["contact"]["adhesion"]["dhat_p"],
838 args["contact"]["adhesion"]["dhat_a"],
839 args["contact"]["adhesion"]["adhesion_strength"],
840 // Tangential Adhesion Form
841 args["contact"]["adhesion"]["tangential_adhesion_coefficient"],
842 args["contact"]["adhesion"]["epsa"],
843 args["solver"]["contact"]["tangential_adhesion_iterations"],
844 // Homogenization
846 // Periodic contact
847 false, Eigen::VectorXi(),
848 // Friction form
849 args["contact"]["friction_coefficient"],
850 args["contact"]["epsv"],
851 args["solver"]["contact"]["friction_iterations"],
852 // Rayleigh damping form
853 args["solver"]["rayleigh_damping"],
854
855 // BC AL lumping
856 args["solver"]["augmented_lagrangian"]["lumping"],
857
858 // Boundary-ID periodic constraints
859 mesh_.get(), &boundary_.total_local_boundary,
860 args["boundary_conditions"]["periodic"], /*fe_space_id=*/-1);
861
862 for (const auto &form : forms)
863 form->set_output_dir(output_path);
864
865 if (solve_data_.contact_form != nullptr)
866 solve_data_.contact_form->save_ccd_debug_meshes = args["output"]["advanced"]["save_ccd_debug_meshes"];
867 }
868
869 void NonlinearElasticVarForm::init_solve(
870 Eigen::MatrixXd &sol,
871 const double t,
872 const InitialConditionOverride *initial_condition_override)
873 {
874 init_solve_data(sol, t, "", initial_condition_override);
875
876 double characteristic_length = 0;
877 if (args["solver"]["advanced"]["characteristic_length"] > 0)
878 {
879 characteristic_length = args["solver"]["advanced"]["characteristic_length"];
880 }
881 else
882 {
883 RowVectorNd min, max;
884 mesh_->bounding_box(min, max);
885 characteristic_length = (max - min).norm();
886 }
887
888 double characteristic_force_density = 0;
889 if (args["solver"]["advanced"]["characteristic_force_density"] <= 0)
890 {
891 logger().warn("No user-specified force density was provided, defaulting to 10000.");
892 characteristic_force_density = 10000;
893 }
894 else
895 {
896 characteristic_force_density = args["solver"]["advanced"]["characteristic_force_density"];
897 }
898
899 const int ndof = space_.n_bases * mesh_->dimension();
900 solve_data_.nl_problem = std::make_shared<solver::NLProblem>(
901 ndof, t, forms, solve_data_.al_form,
902 polysolve::linear::Solver::create(args["solver"]["linear"], logger()),
903 characteristic_length, characteristic_force_density, pure_mass_, mesh_->dimension());
904 solve_data_.nl_problem->init(sol);
905 solve_data_.nl_problem->update_quantities(t, sol);
906
907 stats.solver_info = json::array();
908 }
909
910 void NonlinearElasticVarForm::init_solve_data(
911 Eigen::MatrixXd &sol,
912 const double t,
913 const std::string &state_prefix,
914 const InitialConditionOverride *initial_condition_override)
915 {
916 assert(sol.cols() == 1);
917 assert(!problem->is_scalar()); // tensor
918
919 // FIXME
920 // if (optimization_enabled != solver::CacheLevel::None)
921 // {
922 // if (initial_sol_update.size() == ndof())
923 // sol = initial_sol_update;
924 // else
925 // initial_sol_update = sol;
926 // }
927
928 // --------------------------------------------------------------------
929 // Check for initial intersections
930 if (args["contact"]["enabled"])
931 {
932 POLYFEM_SCOPED_TIMER("Check for initial intersections");
933
934 const Eigen::MatrixXd displaced = collision_mesh_.displace_vertices(
935 utils::unflatten(sol, mesh_->dimension()));
936
937 if (ipc::has_intersections(collision_mesh_, displaced, ipc::create_broad_phase(args["solver"]["contact"]["CCD"]["broad_phase"]).get()))
938 {
940 resolve_output_path("intersection.obj"), displaced,
941 collision_mesh_.edges(), collision_mesh_.faces());
942 log_and_throw_error("Unable to solve, initial solution has intersections!");
943 }
944 }
945
946 // --------------------------------------------------------------------
947
948 if (problem->is_time_dependent())
949 {
950 POLYFEM_SCOPED_TIMER("Initialize time integrator");
951 solve_data_.time_integrator = ImplicitTimeIntegrator::construct_time_integrator(args["time"]["integrator"]);
952
953 Eigen::MatrixXd solution, velocity, acceleration;
954 initial_solution(solution, initial_condition_override, state_prefix); // Reload this because we need all previous solutions
955 solution.col(0) = sol; // Make sure the current solution is the same as `sol`
956 assert(solution.rows() == sol.size());
957 initial_velocity(velocity, initial_condition_override, state_prefix);
958 assert(velocity.rows() == sol.size());
959 initial_acceleration(acceleration, initial_condition_override, state_prefix);
960 assert(acceleration.rows() == sol.size());
961 if (solution.cols() != velocity.cols() || solution.cols() != acceleration.cols())
962 {
964 "Incompatible initial-condition history for transient solve: "
965 "solution has {} columns, velocity has {}, acceleration has {}.",
966 solution.cols(), velocity.cols(), acceleration.cols());
967 }
968
969 solve_data_.time_integrator->init(solution, velocity, acceleration, dt);
970 assert(solve_data_.time_integrator != nullptr && "Transient nonlinear elasticity requires an initialized time integrator");
971 }
972 else
973 {
974 solve_data_.time_integrator = nullptr;
975 }
976
977 // --------------------------------------------------------------------
978 // Initialize forms
979
980 // --------------------------------------------------------------------
981 // Initialize nonlinear problems
982
983 init_forms(args, mesh_->dimension(), sol, t);
984
985 if (pure_mass_.size() == 0)
986 pure_mass_assembler_->assemble(mesh_->is_volume(), space_.n_bases, space_.basis_list(), space_.geometry_basis_list(), pure_mass_ass_vals_cache_, 0, pure_mass_, true);
987 }
988
989 void NonlinearElasticVarForm::prepare_for_embedding()
990 {
991 prepare();
992 }
993
994 void NonlinearElasticVarForm::initial_solution_for_embedding(
995 Eigen::MatrixXd &solution, const std::string &state_prefix) const
996 {
997 initial_solution(solution, nullptr, state_prefix);
998 if (solution.cols() > 1)
999 solution.conservativeResize(Eigen::NoChange, 1);
1000 }
1001
1002 void NonlinearElasticVarForm::init_forms_for_embedding(
1003 Eigen::MatrixXd &solution, const double t, const std::string &state_prefix)
1004 {
1005 prepare();
1006 init_solve_data(solution, t, state_prefix);
1007 }
1008
1009 void NonlinearElasticVarForm::advance_for_embedding(const Eigen::VectorXd &solution)
1010 {
1011 assert(solve_data_.time_integrator);
1012 solve_data_.time_integrator->update_quantities(solution);
1013 solve_data_.update_dt();
1014 }
1015
1016 void NonlinearElasticVarForm::update_barrier_stiffness_for_embedding(
1017 const Eigen::VectorXd &solution)
1018 {
1019 solve_data_.update_barrier_stiffness(solution);
1020 }
1021
1022 bool NonlinearElasticVarForm::save_timestep_for_embedding(
1023 const double time, const int step, const double dt,
1024 const Eigen::MatrixXd &solution, paraviewo::VTMWriter &vtm,
1025 const std::string &block_prefix) const
1026 {
1027 return save_timestep_to_vtm(time, step, dt, solution, vtm, block_prefix);
1028 }
1029
1030 int NonlinearElasticVarForm::embedding_ndof() const
1031 {
1032 return mesh_ ? space_.n_bases * mesh_->dimension() : 0;
1033 }
1034
1035 void NonlinearElasticVarForm::solve_tensor_nonlinear(
1036 const int step,
1037 Eigen::MatrixXd &sol,
1038 const bool init_lagging)
1039 {
1040 assert(solve_data_.nl_problem != nullptr && "Nonlinear forms must initialize the nonlinear problem before solving");
1041 solver::NLProblem &nl_problem = *(solve_data_.nl_problem);
1042
1043 assert(sol.size() == rhs_.size());
1044
1045 if (nl_problem.uses_lagging())
1046 {
1047 if (init_lagging)
1048 {
1049 POLYFEM_SCOPED_TIMER("Initializing lagging");
1050 nl_problem.init_lagging(sol);
1051 }
1052 logger().info("Lagging iteration 1:");
1053 }
1054
1055 save_subsolve(0, step, sol);
1056
1057 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
1058 polysolve::nonlinear::Solver::create(args["solver"]["augmented_lagrangian"]["nonlinear"], args["solver"]["linear"], units.characteristic_length(), logger());
1059
1060 ALSolver al_solver(
1061 solve_data_.al_form,
1062 args["solver"]["augmented_lagrangian"]["initial_weight"],
1063 args["solver"]["augmented_lagrangian"]["scaling"],
1064 args["solver"]["augmented_lagrangian"]["max_weight"],
1065 args["solver"]["augmented_lagrangian"]["eta"],
1066 [&](const Eigen::VectorXd &x) {
1067 this->solve_data_.update_barrier_stiffness(sol);
1068 });
1069
1070 al_solver.post_subsolve = [&](const double al_weight) {
1071 stats.solver_info.push_back(
1072 {{"type", al_weight > 0 ? "al" : "rc"},
1073 {"t", step},
1074 {"info", nl_solver->info()}});
1075 if (al_weight > 0)
1076 stats.solver_info.back()["weight"] = al_weight;
1077 save_subsolve(stats.solver_info.size(), step, sol);
1078 };
1079
1080 Eigen::MatrixXd prev_sol = sol;
1081 al_solver.solve_al(nl_problem, sol,
1082 args["solver"]["augmented_lagrangian"]["nonlinear"], args["solver"]["linear"], units.characteristic_length());
1083
1084 al_solver.solve_reduced(nl_problem, sol,
1085 args["solver"]["nonlinear"], args["solver"]["linear"], units.characteristic_length());
1086
1087 if (args["space"]["advanced"]["count_flipped_els_continuous"])
1088 {
1089 const auto invalidList = utils::count_invalid(mesh_->dimension(), space_.basis_list(), space_.geometry_basis_list(), sol);
1090 logger().debug("Flipped elements (cnt {}) : {}", invalidList.size(), invalidList);
1091 }
1092
1093 const double lagging_tol = args["solver"]["contact"].value("friction_convergence_tol", 1e-2) * units.characteristic_length();
1094
1095 bool lagging_converged = !nl_problem.uses_lagging();
1096 for (int lag_i = 1; !lagging_converged; lag_i++)
1097 {
1098 Eigen::VectorXd tmp_sol = nl_problem.full_to_reduced(sol);
1099
1100 nl_problem.update_lagging(tmp_sol, lag_i);
1101
1102 Eigen::VectorXd grad;
1103 nl_problem.gradient(tmp_sol, grad);
1104 const double delta_x_norm = (prev_sol - sol).lpNorm<Eigen::Infinity>();
1105 logger().debug("Lagging convergence grad_norm={:g} tol={:g} (||Δx||={:g})", grad.norm(), lagging_tol, delta_x_norm);
1106 if (grad.norm() <= lagging_tol)
1107 {
1108 logger().info(
1109 "Lagging converged in {:d} iteration(s) (grad_norm={:g} tol={:g})",
1110 lag_i, grad.norm(), lagging_tol);
1111 lagging_converged = true;
1112 break;
1113 }
1114
1115 if (delta_x_norm <= 1e-12)
1116 {
1117 logger().warn(
1118 "Lagging produced tiny update between iterations {:d} and {:d} (grad_norm={:g} grad_tol={:g} ||Δx||={:g} Δx_tol={:g}); stopping early",
1119 lag_i - 1, lag_i, grad.norm(), lagging_tol, delta_x_norm, 1e-6);
1120 lagging_converged = false;
1121 break;
1122 }
1123
1124 if (lag_i >= nl_problem.max_lagging_iterations())
1125 {
1126 logger().warn(
1127 "Lagging failed to converge with {:d} iteration(s) (grad_norm={:g} tol={:g})",
1128 lag_i, grad.norm(), lagging_tol);
1129 lagging_converged = false;
1130 break;
1131 }
1132
1133 logger().info("Lagging iteration {:d}:", lag_i + 1);
1134 nl_problem.init(sol);
1135 solve_data_.update_barrier_stiffness(sol);
1136 nl_problem.normalize_forms();
1137 nl_solver->minimize(nl_problem, tmp_sol);
1138 nl_problem.finish();
1139 prev_sol = sol;
1140 sol = nl_problem.reduced_to_full(tmp_sol);
1141
1142 stats.solver_info.push_back(
1143 {{"type", "rc"},
1144 {"t", step},
1145 {"lag_i", lag_i},
1146 {"info", nl_solver->info()}});
1147 save_subsolve(stats.solver_info.size(), step, sol);
1148 }
1149 }
1150
1151} // namespace polyfem::varform
int V
int x
std::array< Matrix< int, 3, 3 >, 3 > space_
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
static double convert(const json &val, const std::string &unit_type)
Definition Units.cpp:35
static bool write(const std::string &path, const Eigen::MatrixXd &v, const Eigen::MatrixXi &e, const Eigen::MatrixXi &f)
Definition OBJWriter.cpp:18
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
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:49
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:174
int dimension() const
utily for dimension
Definition Mesh.hpp:164
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
void solve_reduced(NLProblem &nl_problem, Eigen::MatrixXd &sol, std::shared_ptr< polysolve::nonlinear::Solver > nl_solver)
Definition ALSolver.hpp:41
std::function< void(const double)> post_subsolve
Definition ALSolver.hpp:53
void solve_al(NLProblem &nl_problem, Eigen::MatrixXd &sol, std::shared_ptr< polysolve::nonlinear::Solver > nl_solver)
Definition ALSolver.hpp:29
virtual void init(const TVector &x0) override
double normalize_forms() override
TVector full_to_reduced(const TVector &full) const
virtual void gradient(const TVector &x, TVector &gradv) override
void init_lagging(const TVector &x) override
TVector reduced_to_full(const TVector &reduced) const
void update_lagging(const TVector &x, const int iter_num) override
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)
class to store time stepping data
Definition SolveData.hpp:55
std::shared_ptr< solver::ContactForm > contact_form
std::vector< std::pair< std::string, std::shared_ptr< solver::Form > > > named_forms() const
std::shared_ptr< solver::ElasticForm > elastic_form
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > time_integrator
io::OutputSpace output_space() const override
Get the output space of the variational formulation, for output purposes.
void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path) override
Initialize the variational formulation with the given parameters.
std::vector< io::OutputField > elastic_output_fields(const io::OutputSample &sample, const Eigen::MatrixXd &solution, const io::OutputFieldOptions &options, const mesh::Obstacle *obstacle, const time_integrator::ImplicitTimeIntegrator *time_integrator, const std::vector< std::pair< std::string, std::shared_ptr< solver::Form > > > &named_forms, const solver::Form *elastic_form, const solver::ContactForm *contact_form=nullptr) const
void load_mesh(const mesh::Mesh &mesh, const json &args) override
std::shared_ptr< assembler::PressureAssembler > elasticity_pressure_assembler
std::vector< io::OutputField > output_fields(const io::OutputSample &sample, const Eigen::MatrixXd &solution, const io::OutputFieldOptions &options) const override
Get the output fields of the variational formulation, for output purposes.
std::shared_ptr< assembler::ViscousDampingPrev > damping_prev_assembler_
void init(const std::string &formulation, const Units &units, const json &args, const std::string &out_path) override
Initialize the variational formulation with the given parameters.
std::shared_ptr< assembler::ViscousDamping > damping_assembler_
io::OutputSpace output_space() const override
Get the output space of the variational formulation, for output purposes.
void load_mesh(const mesh::Mesh &mesh, const json &args) override
bool is_contact_enabled() const override
Check if contact is enabled for the variational formulation, for output purposes.
std::vector< std::shared_ptr< solver::Form > > forms
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition VarForm.hpp:214
virtual std::string name() const =0
Get the name of the variational formulation.
std::unique_ptr< mesh::Mesh > mesh_
Definition VarForm.hpp:226
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
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)
Obstacle read_obstacle_geometry(const Units &units, const json &geometry, const std::vector< json > &displacements, const std::vector< json > &dirichlets, const std::string &root_path, const int dim, const std::vector< std::string > &_names, const std::vector< Eigen::MatrixXd > &_vertices, const std::vector< Eigen::MatrixXi > &_cells, const bool non_conforming)
read a FEM mesh from a geometry JSON
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:41
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
void append_rows(DstMat &dst, const SrcMat &src)
std::vector< int > count_invalid(const int dim, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXd &u, const unsigned max_iter)
Definition Jacobian.cpp:122
std::function< void(int step, const Eigen::MatrixXd &solution)> ForwardStepCallback
Definition VarForm.hpp:49
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
std::vector< std::string > fields
const mesh::Mesh * mesh