35#include <polysolve/linear/Solver.hpp>
36#include <polysolve/nonlinear/Solver.hpp>
44 using namespace solver;
45 using namespace time_integrator;
50 const bool contact_dhat_was_explicit = clean_args[
"contact"].value(
"_dhat_was_explicit",
false);
51 clean_args[
"contact"].erase(
"_dhat_was_explicit");
73 logger().info(
"Loading obstacles...");
92 const Eigen::MatrixXd &solution,
103 const int actual_dim =
problem->is_scalar() ? 1 :
mesh_->dimension();
104 const auto ¶view_options =
args[
"output"][
"paraview"][
"options"];
105 const bool explicit_fields = !options.
fields.empty();
107 const auto has_field = [&](
const std::string &
name) {
108 return std::any_of(fields.begin(), fields.end(), [&](
const io::OutputField &field) {
109 return field.association == io::OutputField::Association::Point && field.name == name;
113 const auto append_collision_dof_field = [&](
const std::string &
name,
const Eigen::MatrixXd &dof_values) {
114 if (has_field(
name) || dof_values.size() <= 0)
118 if (values.rows() == sample.
points.rows())
122 const auto append_collision_form_force = [&](
const std::string &name,
const std::shared_ptr<solver::Form> &form) {
123 if (!form || !form->enabled() || sample.points.rows() != collision_mesh.rest_positions().rows())
126 Eigen::VectorXd force;
127 form->first_derivative(solution.col(0), force);
128 const double acceleration_scaling =
129 solve_data.time_integrator ? solve_data.time_integrator->acceleration_scaling() : 1;
130 force *= -1.0 / acceleration_scaling;
131 append_collision_dof_field(name, force);
134 if (paraview_options[
"forces"] && !problem->is_scalar())
136 const double s = solve_data.time_integrator ? solve_data.time_integrator->acceleration_scaling() : 1;
137 for (
const auto &[name, form] : solve_data.named_forms())
139 const std::string field_name = name +
"_forces";
140 if (!options.export_field(field_name))
143 Eigen::VectorXd force;
144 if (form && form->enabled())
146 form->first_derivative(solution, force);
151 force.setZero(solution.size());
153 append_collision_dof_field(field_name, force);
157 if (options.export_field(
"gradient_of_elastic_potential") && solve_data.elastic_form)
159 Eigen::VectorXd potential_grad;
160 solve_data.elastic_form->first_derivative(solution, potential_grad);
161 append_collision_dof_field(
"gradient_of_elastic_potential", potential_grad);
164 if (options.export_field(
"gradient_of_contact_potential") && solve_data.contact_form && solve_data.contact_form->weight() > 0)
166 Eigen::VectorXd potential_grad;
167 solve_data.contact_form->first_derivative(solution, potential_grad);
168 potential_grad *= -solve_data.contact_form->barrier_stiffness() / solve_data.contact_form->weight();
169 append_collision_dof_field(
"gradient_of_contact_potential", potential_grad);
172 if (options.export_field(
"displacement"))
173 append_collision_dof_field(
"displacement", solution);
174 if (options.export_field(
"solution"))
175 append_collision_dof_field(
"solution", solution);
177 if ((paraview_options[
"contact_forces"] || explicit_fields) && options.export_field(
"contact_forces"))
178 append_collision_form_force(
"contact_forces", solve_data.contact_form);
179 if ((paraview_options[
"friction_forces"] || explicit_fields) && options.export_field(
"friction_forces"))
180 append_collision_form_force(
"friction_forces", solve_data.friction_form);
181 if ((paraview_options[
"normal_adhesion_forces"] || explicit_fields) && options.export_field(
"normal_adhesion_forces"))
182 append_collision_form_force(
"normal_adhesion_forces", solve_data.normal_adhesion_form);
183 if ((paraview_options[
"tangential_adhesion_forces"] || explicit_fields) && options.export_field(
"tangential_adhesion_forces"))
184 append_collision_form_force(
"tangential_adhesion_forces", solve_data.tangential_adhesion_form);
187 && options.export_field(
"adaptive_dhat")
188 && args[
"contact"][
"use_gcp_formulation"]
189 && args[
"contact"][
"use_adaptive_dhat"])
191 const auto smooth_contact = std::dynamic_pointer_cast<solver::SmoothContactForm>(solve_data.contact_form);
194 const auto &set = smooth_contact->collision_set();
197 Eigen::VectorXd dhats(collision_mesh.num_edges());
198 for (
int e = 0;
e < dhats.size(); ++
e)
199 dhats(e) = set.get_edge_dhat(e);
204 Eigen::VectorXd dhats(collision_mesh.num_faces());
205 for (
int f = 0;
f < dhats.size(); ++
f)
206 dhats(f) = set.get_face_dhat(f);
209 Eigen::VectorXd vertex_dhats(collision_mesh.num_vertices());
210 for (
int v = 0; v < vertex_dhats.size(); ++v)
211 vertex_dhats(v) = set.get_vert_dhat(v);
220 void NonlinearElasticVarForm::build_basis(
mesh::Mesh &mesh,
const bool iso_parametric,
const json &args)
222 ElasticVarForm::build_basis(mesh, iso_parametric, args);
227 const int n_fe_bases =
space_.n_bases;
228 space_.n_bases += obstacle.n_vertices();
230 logger().info(
"Building collision mesh...");
231 build_collision_mesh(mesh, args);
232 preprocess_contact_parameters();
238 for (
int i = n_fe_bases; i <
space_.n_bases; ++i)
240 for (
int d = 0; d < mesh.
dimension(); ++d)
241 boundary_.boundary_nodes.push_back(i * mesh.
dimension() + d);
244 boundary_.normalize_boundary_nodes();
247 void NonlinearElasticVarForm::preprocess_contact_parameters()
249 if (!is_contact_enabled())
252 double min_boundary_edge_length = std::numeric_limits<double>::max();
253 for (
const auto &edge : collision_mesh.edges().rowwise())
255 const VectorNd v0 = collision_mesh.rest_positions().row(edge(0));
256 const VectorNd v1 = collision_mesh.rest_positions().row(edge(1));
257 min_boundary_edge_length = std::min(min_boundary_edge_length, (v1 - v0).norm());
260 double dhat =
Units::convert(args[
"contact"][
"dhat"], units.length());
261 args[
"contact"][
"epsv"] =
Units::convert(args[
"contact"][
"epsv"], units.velocity());
263 if (!contact_dhat_was_explicit_
264 && std::isfinite(min_boundary_edge_length)
265 && dhat > min_boundary_edge_length)
267 dhat = args[
"contact"][
"dhat_percentage"].get<
double>() * min_boundary_edge_length;
268 logger().info(
"dhat set to {}", dhat);
270 else if (std::isfinite(min_boundary_edge_length) && dhat > min_boundary_edge_length)
272 logger().warn(
"dhat larger than min boundary edge, {} > {}", dhat, min_boundary_edge_length);
275 args[
"contact"][
"dhat"] = dhat;
278 void NonlinearElasticVarForm::build_rhs_assembler()
280 json rhs_solver_params = args[
"solver"][
"linear"];
281 if (!rhs_solver_params.contains(
"Pardiso"))
282 rhs_solver_params[
"Pardiso"] = {};
283 rhs_solver_params[
"Pardiso"][
"mtype"] = -2;
285 const int size = problem->is_scalar() ? 1 : mesh_->dimension();
287 solve_data.rhs_assembler = std::make_shared<assembler::RhsAssembler>(
288 *primary_assembler_, *mesh_, &obstacle,
289 boundary_.dirichlet_nodes, boundary_.neumann_nodes,
290 boundary_.dirichlet_nodes_position, boundary_.neumann_nodes_position,
291 space_.n_bases, size,
space_.basis_list(),
space_.geometry_basis_list(), mass_ass_vals_cache_, *problem,
292 args[
"space"][
"advanced"][
"bc_method"],
295 rhs_assembler_ = solve_data.rhs_assembler;
298 void NonlinearElasticVarForm::build_collision_mesh(
302 build_collision_mesh(
303 mesh,
space_.n_bases,
space_.basis_list(),
space_.geometry_basis_list(), boundary_.total_local_boundary, obstacle,
304 args, [
this](
const std::string &p) { return utils::resolve_path(p, root_path, false); },
305 space_.space_in_node_to_node, collision_mesh);
308 void NonlinearElasticVarForm::build_collision_mesh(
311 const std::vector<basis::ElementBases> &bases,
312 const std::vector<basis::ElementBases> &geom_bases,
313 const std::vector<mesh::LocalBoundary> &total_local_boundary,
316 const std::function<std::string(
const std::string &)> &resolve_input_path,
317 const Eigen::VectorXi &in_node_to_node,
318 ipc::CollisionMesh &collision_mesh)
320 Eigen::MatrixXd collision_vertices;
321 Eigen::VectorXi collision_codim_vids;
322 Eigen::MatrixXi collision_edges, collision_triangles;
323 std::vector<Eigen::Triplet<double>> displacement_map_entries;
325 if (args.contains(
"/contact/collision_mesh"_json_pointer)
326 && args.at(
"/contact/collision_mesh/enabled"_json_pointer).get<
bool>())
328 const json collision_mesh_args = args.at(
"/contact/collision_mesh"_json_pointer);
329 if (collision_mesh_args.contains(
"linear_map"))
331 assert(displacement_map_entries.empty());
332 assert(collision_mesh_args.contains(
"mesh"));
333 const std::string root_path = utils::json_value<std::string>(args,
"root_path",
"");
339 in_node_to_node, transformation, collision_vertices, collision_codim_vids,
340 collision_edges, collision_triangles, displacement_map_entries);
342 else if (collision_mesh_args.contains(
"max_edge_length"))
345 "Building collision proxy with max edge length={} ...",
346 collision_mesh_args[
"max_edge_length"].get<double>());
350 bases, geom_bases, total_local_boundary, n_bases, mesh.
dimension(),
351 collision_mesh_args[
"max_edge_length"], collision_vertices,
352 collision_triangles, displacement_map_entries,
353 collision_mesh_args[
"tessellation_type"]);
354 if (collision_triangles.size())
355 igl::edges(collision_triangles, collision_edges);
357 logger().debug(fmt::format(
358 std::locale(
"en_US.UTF-8"),
359 "Done (took {:g}s, {:L} vertices, {:L} triangles)",
360 timer.getElapsedTime(),
361 collision_vertices.rows(), collision_triangles.rows()));
366 mesh, n_bases - obstacle.
n_vertices(), bases, total_local_boundary,
367 collision_vertices, collision_edges, collision_triangles, displacement_map_entries);
373 mesh, n_bases - obstacle.
n_vertices(), bases, total_local_boundary,
374 collision_vertices, collision_edges, collision_triangles, displacement_map_entries);
377 std::vector<bool> is_orientable_vertex(collision_vertices.rows(),
true);
380 const int num_fe_nodes = n_bases - obstacle.
n_vertices();
381 const int num_fe_collision_vertices = collision_vertices.rows();
382 assert(collision_edges.size() == 0 || collision_edges.maxCoeff() < num_fe_collision_vertices);
383 assert(collision_triangles.size() == 0 || collision_triangles.maxCoeff() < num_fe_collision_vertices);
393 for (
int i = 0; i < obstacle.
n_vertices(); i++)
395 is_orientable_vertex.push_back(
false);
398 if (!displacement_map_entries.empty())
400 displacement_map_entries.reserve(displacement_map_entries.size() + obstacle.
n_vertices());
401 for (
int i = 0; i < obstacle.
n_vertices(); i++)
403 displacement_map_entries.emplace_back(num_fe_collision_vertices + i, num_fe_nodes + i, 1.0);
408 std::vector<bool> is_on_surface = ipc::CollisionMesh::construct_is_on_surface(
409 collision_vertices.rows(), collision_edges);
410 for (
const int vid : collision_codim_vids)
412 is_on_surface[vid] =
true;
415 Eigen::SparseMatrix<double> displacement_map;
416 if (!displacement_map_entries.empty())
418 displacement_map.resize(collision_vertices.rows(), n_bases);
419 displacement_map.setFromTriplets(displacement_map_entries.begin(), displacement_map_entries.end());
422 collision_mesh = ipc::CollisionMesh(
423 is_on_surface, is_orientable_vertex, collision_vertices, collision_edges, collision_triangles,
426 collision_mesh.can_collide = [&collision_mesh, num_fe_collision_vertices](
size_t vi,
size_t vj) {
428 return collision_mesh.to_full_vertex_id(vi) < num_fe_collision_vertices
429 || collision_mesh.to_full_vertex_id(vj) < num_fe_collision_vertices;
432 collision_mesh.init_area_jacobians();
435 std::shared_ptr<assembler::PressureAssembler> NonlinearElasticVarForm::build_pressure_assembler()
const
437 const int size = problem->is_scalar() ? 1 : mesh_->dimension();
439 return std::make_shared<assembler::PressureAssembler>(
440 *primary_assembler_, *mesh_, obstacle,
441 boundary_.local_pressure_boundary,
442 boundary_.local_pressure_cavity,
443 boundary_.boundary_nodes,
444 elastic_primitive_to_node(), elastic_node_to_primitive(),
448 void NonlinearElasticStaticVarForm::solve_problem(Eigen::MatrixXd &sol)
450 stats.spectrum.setZero();
454 logger().info(
"Solving {}", primary_assembler_->name());
466 initial_elastic_solution(sol);
469 sol.conservativeResize(Eigen::NoChange, 1);
471 init_solve(sol, 1.0);
473 solve_tensor_nonlinear(0, sol,
true);
475 const std::string state_path = resolve_output_path(args[
"output"][
"data"][
"state"]);
476 if (!state_path.empty())
480 timings.solving_time = timer.getElapsedTime();
481 logger().info(
" took {}s", timings.solving_time);
484 void NonlinearElasticTransientVarForm::solve_problem(Eigen::MatrixXd &sol)
486 const bool save_stats = args[
"output"][
"stats"];
487 stats.spectrum.setZero();
491 logger().info(
"Solving {}", primary_assembler_->name());
503 initial_elastic_solution(sol);
506 sol.conservativeResize(Eigen::NoChange, 1);
508 init_solve(sol, t0 + dt);
513 std::unique_ptr<io::EnergyCSVWriter> energy_csv =
nullptr;
514 std::unique_ptr<io::RuntimeStatsCSVWriter> stats_csv =
nullptr;
518 logger().debug(
"Saving nl stats to {} and {}", resolve_output_path(
"energy.csv"), resolve_output_path(
"stats.csv"));
519 energy_csv = std::make_unique<io::EnergyCSVWriter>(resolve_output_path(
"energy.csv"), solve_data);
521 stats_csv = std::make_unique<io::RuntimeStatsCSVWriter>(
522 resolve_output_path(
"stats.csv"),
530 energy_csv->write(save_i, sol);
531 save_timestep(t0, 0, t0, dt, sol);
535 for (
int t = 1; t <= time_steps; ++t)
537 double forward_solve_time = 0, remeshing_time = 0, global_relaxation_time = 0;
541 solve_tensor_nonlinear(t, sol,
true);
546 energy_csv->write(save_i, sol);
547 save_timestep(t0 + dt * t, t, t0, dt, sol);
553 solve_data.time_integrator->update_quantities(sol);
555 solve_data.nl_problem->update_quantities(t0 + (t + 1) * dt, sol);
557 solve_data.update_dt();
558 solve_data.update_barrier_stiffness(sol);
561 logger().info(
"{}/{} t={}", t, time_steps, t0 + dt * t);
562 notify_time_step(t, time_steps, t0, dt);
564 save_elastic_step_state(t0, dt, t, solve_data.time_integrator.get());
566 stats_csv->write(t, forward_solve_time, remeshing_time, global_relaxation_time);
570 timings.solving_time = timer.getElapsedTime();
571 logger().info(
" took {}s", timings.solving_time);
574 void NonlinearElasticVarForm::init_forms(
const json &args,
const int dim, Eigen::MatrixXd &sol,
const double t)
576 damping_assembler = std::make_shared<assembler::ViscousDamping>();
577 set_materials(*damping_assembler, mesh_->dimension());
579 elasticity_pressure_assembler = build_pressure_assembler();
582 damping_prev_assembler = std::make_shared<assembler::ViscousDampingPrev>();
583 set_materials(*damping_prev_assembler, mesh_->dimension());
588 forms = solve_data.init_forms(
591 dim, t,
space_.space_in_node_to_node,
593 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,
594 args[
"solver"][
"advanced"][
"conservative_max_iter"],
596 0, boundary_.boundary_nodes, boundary_.local_boundary,
597 boundary_.local_neumann_boundary,
598 elastic_boundary_samples(), rhs_, sol, mass_assembler_->density(),
600 boundary_.local_pressure_boundary, boundary_.local_pressure_cavity, elasticity_pressure_assembler,
602 args.value(
"/time/quasistatic"_json_pointer,
true), mass_,
603 damping_assembler->is_valid() ? damping_assembler :
nullptr,
605 args[
"solver"][
"advanced"][
"lagged_regularization_weight"],
606 args[
"solver"][
"advanced"][
"lagged_regularization_iterations"],
608 obstacle.
ndof(), args[
"constraints"][
"hard"], args[
"constraints"][
"soft"],
610 args[
"contact"][
"enabled"], collision_mesh, args[
"contact"][
"dhat"],
611 avg_mass_, args[
"contact"][
"use_convergent_formulation"] ? bool(args[
"contact"][
"use_area_weighting"]) :
false,
612 args[
"contact"][
"use_convergent_formulation"] ? bool(args[
"contact"][
"use_improved_max_operator"]) :
false,
613 args[
"contact"][
"use_convergent_formulation"] ? bool(args[
"contact"][
"use_physical_barrier"]) :
false,
614 args[
"solver"][
"contact"][
"barrier_stiffness"],
615 args[
"solver"][
"contact"][
"initial_barrier_stiffness"],
616 args[
"solver"][
"contact"][
"CCD"][
"broad_phase"],
617 args[
"solver"][
"contact"][
"CCD"][
"tolerance"],
618 args[
"solver"][
"contact"][
"CCD"][
"max_iterations"],
621 args[
"contact"][
"use_gcp_formulation"],
622 args[
"contact"][
"alpha_t"],
623 args[
"contact"][
"alpha_n"],
624 args[
"contact"][
"use_adaptive_dhat"],
625 args[
"contact"][
"min_distance_ratio"],
627 args[
"contact"][
"adhesion"][
"adhesion_enabled"],
628 args[
"contact"][
"adhesion"][
"dhat_p"],
629 args[
"contact"][
"adhesion"][
"dhat_a"],
630 args[
"contact"][
"adhesion"][
"adhesion_strength"],
632 args[
"contact"][
"adhesion"][
"tangential_adhesion_coefficient"],
633 args[
"contact"][
"adhesion"][
"epsa"],
634 args[
"solver"][
"contact"][
"tangential_adhesion_iterations"],
638 false, Eigen::VectorXi(),
nullptr,
640 args[
"contact"][
"friction_coefficient"],
641 args[
"contact"][
"epsv"],
642 args[
"solver"][
"contact"][
"friction_iterations"],
644 args[
"solver"][
"rayleigh_damping"]);
646 for (
const auto &form : forms)
647 form->set_output_dir(output_path);
649 if (solve_data.contact_form !=
nullptr)
650 solve_data.contact_form->save_ccd_debug_meshes = args[
"output"][
"advanced"][
"save_ccd_debug_meshes"];
653 void NonlinearElasticVarForm::init_solve(Eigen::MatrixXd &sol,
const double t)
655 assert(sol.cols() == 1);
656 assert(!problem->is_scalar());
669 if (args[
"contact"][
"enabled"])
673 const Eigen::MatrixXd displaced = collision_mesh.displace_vertices(
676 if (ipc::has_intersections(collision_mesh, displaced, ipc::create_broad_phase(args[
"solver"][
"contact"][
"CCD"][
"broad_phase"]).get()))
679 resolve_output_path(
"intersection.obj"), displaced,
680 collision_mesh.edges(), collision_mesh.faces());
687 if (problem->is_time_dependent())
690 solve_data.time_integrator = ImplicitTimeIntegrator::construct_time_integrator(args[
"time"][
"integrator"]);
692 Eigen::MatrixXd solution, velocity, acceleration;
693 initial_elastic_solution(solution);
694 solution.col(0) = sol;
695 assert(solution.rows() == sol.size());
696 initial_velocity(velocity);
697 assert(velocity.rows() == sol.size());
698 initial_acceleration(acceleration);
699 assert(acceleration.rows() == sol.size());
701 solve_data.time_integrator->init(solution, velocity, acceleration, dt);
702 assert(solve_data.time_integrator !=
nullptr);
706 solve_data.time_integrator =
nullptr;
715 init_forms(args, mesh_->dimension(), sol, t);
717 double characteristic_length = 0;
718 if (args[
"solver"][
"advanced"][
"characteristic_length"] > 0)
720 characteristic_length = args[
"solver"][
"advanced"][
"characteristic_length"];
725 mesh_->bounding_box(min, max);
726 characteristic_length = (max - min).norm();
729 double characteristic_force_density = 0;
730 if (args[
"solver"][
"advanced"][
"characteristic_force_density"] <= 0)
732 logger().warn(
"No user-specified force density was provided, defaulting to 10000.");
733 characteristic_force_density = 10000;
737 characteristic_force_density = args[
"solver"][
"advanced"][
"characteristic_force_density"];
740 if (pure_mass_.size() == 0)
741 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);
743 const int ndof =
space_.n_bases * mesh_->dimension();
744 solve_data.nl_problem = std::make_shared<solver::NLProblem>(
745 ndof,
nullptr, t, forms, solve_data.al_form,
746 polysolve::linear::Solver::create(args[
"solver"][
"linear"],
logger()),
747 characteristic_length, characteristic_force_density, pure_mass_, mesh_->dimension());
748 solve_data.nl_problem->init(sol);
749 solve_data.nl_problem->update_quantities(t, sol);
752 stats.solver_info = json::array();
755 void NonlinearElasticVarForm::solve_tensor_nonlinear(
int step, Eigen::MatrixXd &sol,
const bool init_lagging)
757 assert(solve_data.nl_problem !=
nullptr);
760 assert(sol.size() == rhs_.size());
769 logger().info(
"Lagging iteration 1:");
772 save_subsolve(0, step, sol);
774 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver =
775 polysolve::nonlinear::Solver::create(args[
"solver"][
"augmented_lagrangian"][
"nonlinear"], args[
"solver"][
"linear"], units.characteristic_length(),
logger());
779 args[
"solver"][
"augmented_lagrangian"][
"initial_weight"],
780 args[
"solver"][
"augmented_lagrangian"][
"scaling"],
781 args[
"solver"][
"augmented_lagrangian"][
"max_weight"],
782 args[
"solver"][
"augmented_lagrangian"][
"eta"],
783 [&](
const Eigen::VectorXd &
x) {
784 this->solve_data.update_barrier_stiffness(sol);
788 stats.solver_info.push_back(
789 {{
"type", al_weight > 0 ?
"al" :
"rc"},
791 {
"info", nl_solver->info()}});
793 stats.solver_info.back()[
"weight"] = al_weight;
794 save_subsolve(stats.solver_info.size(), step, sol);
797 Eigen::MatrixXd prev_sol = sol;
799 args[
"solver"][
"augmented_lagrangian"][
"nonlinear"], args[
"solver"][
"linear"], units.characteristic_length());
802 args[
"solver"][
"nonlinear"], args[
"solver"][
"linear"], units.characteristic_length());
804 if (args[
"space"][
"advanced"][
"count_flipped_els_continuous"])
807 logger().debug(
"Flipped elements (cnt {}) : {}", invalidList.size(), invalidList);
810 const double lagging_tol = args[
"solver"][
"contact"].value(
"friction_convergence_tol", 1e-2) * units.characteristic_length();
813 for (
int lag_i = 1; !lagging_converged; lag_i++)
819 Eigen::VectorXd grad;
821 const double delta_x_norm = (prev_sol - sol).lpNorm<Eigen::Infinity>();
822 logger().debug(
"Lagging convergence grad_norm={:g} tol={:g} (||Δx||={:g})", grad.norm(), lagging_tol, delta_x_norm);
823 if (grad.norm() <= lagging_tol)
826 "Lagging converged in {:d} iteration(s) (grad_norm={:g} tol={:g})",
827 lag_i, grad.norm(), lagging_tol);
828 lagging_converged =
true;
832 if (delta_x_norm <= 1e-12)
835 "Lagging produced tiny update between iterations {:d} and {:d} (grad_norm={:g} grad_tol={:g} ||Δx||={:g} Δx_tol={:g}); stopping early",
836 lag_i - 1, lag_i, grad.norm(), lagging_tol, delta_x_norm, 1e-6);
837 lagging_converged =
false;
844 "Lagging failed to converge with {:d} iteration(s) (grad_norm={:g} tol={:g})",
845 lag_i, grad.norm(), lagging_tol);
846 lagging_converged =
false;
850 logger().info(
"Lagging iteration {:d}:", lag_i + 1);
851 nl_problem.
init(sol);
852 solve_data.update_barrier_stiffness(sol);
854 nl_solver->minimize(nl_problem, tmp_sol);
859 stats.solver_info.push_back(
863 {
"info", nl_solver->info()}});
864 save_subsolve(stats.solver_info.size(), step, sol);
std::array< Matrix< int, 3, 3 >, 3 > space_
#define POLYFEM_SCOPED_TIMER(...)
static double convert(const json &val, const std::string &unit_type)
static bool write(const std::string &path, const Eigen::MatrixXd &v, const Eigen::MatrixXi &e, const Eigen::MatrixXi &f)
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
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
int dimension() const
utily for dimension
const Eigen::MatrixXi & e() const
const Eigen::MatrixXi & f() const
const Eigen::MatrixXd & v() const
const Eigen::VectorXi & codim_v() const
void solve_reduced(NLProblem &nl_problem, Eigen::MatrixXd &sol, std::shared_ptr< polysolve::nonlinear::Solver > nl_solver)
std::function< void(const double)> post_subsolve
void solve_al(NLProblem &nl_problem, Eigen::MatrixXd &sol, std::shared_ptr< polysolve::nonlinear::Solver > nl_solver)
bool uses_lagging() const
int max_lagging_iterations() const
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
class to store time stepping data
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
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.
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.
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)
spdlog::logger & logger()
Retrieves the current logger.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
void log_and_throw_error(const std::string &msg)
std::vector< std::string > fields