PolyFEM
Loading...
Searching...
No Matches
StateHomogenization.cpp
Go to the documentation of this file.
2#include <polyfem/Common.hpp>
13
17
18#include <Eigen/Core>
19#include <unsupported/Eigen/SparseExtra>
20#include <polysolve/linear/FEMSolver.hpp>
21#include <polysolve/nonlinear/Solver.hpp>
22#include <ipc/ipc.hpp>
23#include <spdlog/fmt/fmt.h>
24
25#include <algorithm>
26#include <memory>
27#include <stdexcept>
28#include <string>
29#include <vector>
30
31namespace polyfem::legacy
32{
33
34 using namespace assembler;
35 using namespace mesh;
36 using namespace solver;
37 using namespace utils;
38 using namespace quadrature;
39
41 {
42 const int dim = mesh->dimension();
43 const int ndof = n_bases * dim;
44 const polyfem::io::FileSystemIO resources(root_path());
45
46 const std::vector<std::shared_ptr<Form>> forms = solve_data.init_forms(
47 // General
48 units,
49 resources,
50 mesh->dimension(), t, in_node_to_node,
51 // Elastic form
53 args["solver"]["advanced"]["jacobian_threshold"], args["solver"]["advanced"]["check_inversion"],
54 args["solver"]["advanced"]["conservative_max_iter"],
55 // Body form
57 n_boundary_samples(), rhs, Eigen::VectorXd::Zero(ndof) /* only to set neumann BC, not used*/, mass_matrix_assembler->density(),
58 // Pressure form
60 // Inertia form
61 args.value("/time/quasistatic"_json_pointer, true), mass,
62 nullptr,
63 // Lagged regularization form
64 args["solver"]["advanced"]["lagged_regularization_weight"],
65 args["solver"]["advanced"]["lagged_regularization_iterations"],
66 // Augmented lagrangian form
67 obstacle.ndof(), args["constraints"]["hard"], args["constraints"]["soft"], args["constraints"]["zero_mean"],
68 // Contact form
69 args["contact"]["enabled"], args["contact"]["periodic"].get<bool>() ? periodic_collision_mesh : collision_mesh, args["contact"]["dhat"],
70 avg_mass, args["contact"]["use_convergent_formulation"] ? bool(args["contact"]["use_area_weighting"]) : false,
71 args["contact"]["use_convergent_formulation"] ? bool(args["contact"]["use_improved_max_operator"]) : false,
72 args["contact"]["use_convergent_formulation"] ? bool(args["contact"]["use_physical_barrier"]) : false,
73 args["solver"]["contact"]["barrier_stiffness"],
74 args["solver"]["contact"]["initial_barrier_stiffness"],
75 args["solver"]["contact"]["CCD"]["broad_phase"],
76 args["solver"]["contact"]["CCD"]["tolerance"],
77 args["solver"]["contact"]["CCD"]["max_iterations"],
79 // Smooth Contact Form
80 args["contact"]["use_gcp_formulation"],
81 args["contact"]["alpha_t"],
82 args["contact"]["alpha_n"],
83 args["contact"]["use_adaptive_dhat"],
84 args["contact"]["min_distance_ratio"],
85 // Normal Adhesion Form
87 args["contact"]["adhesion"]["dhat_p"],
88 args["contact"]["adhesion"]["dhat_a"],
89 args["contact"]["adhesion"]["adhesion_strength"],
90 // Tangential Adhesion Form
91 args["contact"]["adhesion"]["tangential_adhesion_coefficient"],
92 args["contact"]["adhesion"]["epsa"],
93 args["solver"]["contact"]["tangential_adhesion_iterations"],
94 // Homogenization
96 // Periodic contact
97 args["contact"]["periodic"], periodic_collision_mesh_to_basis,
98 // Friction form
99 args["contact"]["friction_coefficient"],
100 args["contact"]["epsv"],
101 args["solver"]["contact"]["friction_iterations"],
102 // Rayleigh damping form
103 args["solver"]["rayleigh_damping"],
104 // BC AL lumping
105 args["solver"]["augmented_lagrangian"]["lumping"],
106 // Boundary-ID periodic constraints
108 args["boundary_conditions"]["periodic"], /*fe_space_id=*/-1);
109
110 for (const auto &[name, form] : solve_data.named_forms())
111 {
112 if (name == "augmented_lagrangian")
113 {
114 form->set_weight(0);
115 form->disable();
116 }
117 }
118
119 bool solve_symmetric_flag = false;
120 {
121 const auto &fixed_entry = macro_strain_constraint.get_fixed_entry();
122 for (int i = 0; i < dim; i++)
123 {
124 for (int j = 0; j < i; j++)
125 {
126 if (std::find(fixed_entry.data(), fixed_entry.data() + fixed_entry.size(), i + j * dim) == fixed_entry.data() + fixed_entry.size() && std::find(fixed_entry.data(), fixed_entry.data() + fixed_entry.size(), j + i * dim) == fixed_entry.data() + fixed_entry.size())
127 {
128 logger().info("Strain entry [{},{}] and [{},{}] are not fixed, solve for symmetric strain...", i, j, j, i);
129 solve_symmetric_flag = true;
130 break;
131 }
132 }
133 if (solve_symmetric_flag)
134 break;
135 }
136 }
137
138 std::shared_ptr<solver::NLHomoProblem> homo_problem = std::make_shared<solver::NLHomoProblem>(
139 ndof,
141 n_bases, mesh_nodes, t, forms, solve_data.al_form, solve_symmetric_flag,
142 polysolve::linear::Solver::create(args["solver"]["linear"], logger()),
145 homo_problem->add_form(solve_data.periodic_contact_form);
147 homo_problem->add_form(solve_data.strain_al_lagr_form);
148
149 solve_data.nl_problem = homo_problem;
150 solve_data.nl_problem->init(Eigen::VectorXd::Zero(homo_problem->reduced_size() + homo_problem->macro_reduced_size()));
151 solve_data.nl_problem->update_quantities(t, Eigen::VectorXd::Zero(homo_problem->reduced_size() + homo_problem->macro_reduced_size()));
152 }
153
154 void State::solve_homogenization_step(int step, Eigen::MatrixXd &sol, bool adaptive_initial_weight, UserPostStepCallback user_post_step)
155 {
156 const int dim = mesh->dimension();
157 const int ndof = n_bases * dim;
158
159 auto homo_problem = std::dynamic_pointer_cast<solver::NLHomoProblem>(solve_data.nl_problem);
160
161 Eigen::VectorXd extended_sol;
162 extended_sol.setZero(ndof + dim * dim);
163
164 if (sol.size() == extended_sol.size())
165 extended_sol = sol;
166
167 const auto &fixed_entry = macro_strain_constraint.get_fixed_entry();
168 homo_problem->set_fixed_entry({});
169 {
170 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver = make_nl_solver(true);
171
172 Eigen::VectorXi al_indices = fixed_entry.array() + homo_problem->full_size();
173 Eigen::VectorXd al_values = utils::flatten(macro_strain_constraint.eval(step))(fixed_entry);
174
175 std::shared_ptr<MacroStrainLagrangianForm> lagr_form = solve_data.strain_al_lagr_form;
176 lagr_form->enable();
177
178 const double initial_weight = args["solver"]["augmented_lagrangian"]["initial_weight"];
179 const double max_weight = args["solver"]["augmented_lagrangian"]["max_weight"];
180 const double eta_tol = args["solver"]["augmented_lagrangian"]["eta"];
181 const double scaling = args["solver"]["augmented_lagrangian"]["scaling"];
182 double al_weight = initial_weight;
183
184 Eigen::VectorXd tmp_sol = homo_problem->extended_to_reduced(extended_sol);
185 const Eigen::VectorXd initial_sol = tmp_sol;
186 const double initial_error = lagr_form->compute_error(extended_sol);
187 double current_error = initial_error;
188
189 // try to enforce fixed values on macro strain
190 extended_sol(al_indices) = al_values;
191 Eigen::VectorXd reduced_sol = homo_problem->extended_to_reduced(extended_sol);
192
193 homo_problem->line_search_begin(tmp_sol, reduced_sol);
194 int al_steps = 0;
195 bool force_al = true;
196
197 lagr_form->set_initial_weight(al_weight);
198
199 while (force_al
200 || !std::isfinite(homo_problem->value(reduced_sol))
201 || !homo_problem->is_step_valid(tmp_sol, reduced_sol)
202 || !homo_problem->is_step_collision_free(tmp_sol, reduced_sol))
203 {
204 force_al = false;
205 homo_problem->line_search_end();
206
207 logger().info("Solving AL Problem with weight {}", al_weight);
208
209 homo_problem->init(tmp_sol);
210 try
211 {
212 homo_problem->normalize_forms();
213 nl_solver->minimize(*homo_problem, tmp_sol);
214 }
215 catch (const std::runtime_error &e)
216 {
217 logger().error("AL solve failed!");
218 }
219
220 extended_sol = homo_problem->reduced_to_extended(tmp_sol);
221 logger().debug("Current macro strain: {}", extended_sol.tail(dim * dim));
222
223 current_error = lagr_form->compute_error(extended_sol);
224 const double eta = 1 - sqrt(current_error / initial_error);
225
226 logger().info("Current eta = {}, current error = {}, initial error = {}", eta, current_error, initial_error);
227
228 if (eta < eta_tol && al_weight < max_weight)
229 al_weight *= scaling;
230 else
231 lagr_form->update_lagrangian(extended_sol, al_weight);
232
233 if (eta <= 0)
234 {
235 if (adaptive_initial_weight)
236 {
237 args["solver"]["augmented_lagrangian"]["initial_weight"] = args["solver"]["augmented_lagrangian"]["initial_weight"].get<double>() * scaling;
238 {
239 json tmp = json::object();
240 tmp["/solver/augmented_lagrangian/initial_weight"_json_pointer] = args["solver"]["augmented_lagrangian"]["initial_weight"];
241 }
242 logger().warn("AL weight too small, increase weight and revert solution, new initial weight is {}", args["solver"]["augmented_lagrangian"]["initial_weight"].get<double>());
243 }
244 tmp_sol = initial_sol;
245 }
246
247 // try to enforce fixed values on macro strain
248 extended_sol(al_indices) = al_values;
249 reduced_sol = homo_problem->extended_to_reduced(extended_sol);
250
251 homo_problem->line_search_begin(tmp_sol, reduced_sol);
252 }
253 homo_problem->line_search_end();
254 lagr_form->disable();
255 }
256
257 homo_problem->set_fixed_entry(fixed_entry);
258
259 Eigen::VectorXd reduced_sol = homo_problem->extended_to_reduced(extended_sol);
260
261 homo_problem->init(reduced_sol);
262 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver = make_nl_solver(false);
263 homo_problem->normalize_forms();
264 nl_solver->minimize(*homo_problem, reduced_sol);
265
266 logger().info("Macro Strain: {}", extended_sol.tail(dim * dim).transpose());
267
268 // check saddle point
269 {
270 json linear_args = args["solver"]["linear"];
271 std::string solver_name = linear_args["solver"];
272 if (solver_name.find("Pardiso") != std::string::npos)
273 {
274 linear_args["solver"] = "Eigen::PardisoLLT";
275 std::unique_ptr<polysolve::linear::Solver> solver =
276 polysolve::linear::Solver::create(linear_args, logger());
277
279 homo_problem->hessian(reduced_sol, A);
280 Eigen::VectorXd x, b = Eigen::VectorXd::Zero(A.rows());
281 try
282 {
283 dirichlet_solve(
284 *solver, A, b, {}, x, A.rows(), args["output"]["data"]["stiffness_mat"], false, false, false);
285 }
286 catch (const std::runtime_error &error)
287 {
288 logger().error("The solution is a saddle point!");
289 }
290 }
291 }
292
293 sol = homo_problem->reduced_to_extended(reduced_sol);
294
295 if (user_post_step)
296 {
297 Eigen::MatrixXd disp_grad = utils::unflatten(sol.bottomRows(dim * dim), dim);
298 user_post_step(step, *this, homo_problem->reduced_to_full(reduced_sol), &disp_grad, nullptr);
299 }
300 }
301
302 void State::solve_homogenization(const int time_steps, const double t0, const double dt, Eigen::MatrixXd &sol, UserPostStepCallback user_post_step)
303 {
304 bool is_static = !is_param_valid(args, "time");
305 if (!is_static && !args["time"]["quasistatic"])
306 log_and_throw_error("Transient homogenization can only do quasi-static!");
307
309
310 const int t_offset = args["output"]["data"]["file_index_offset"].get<int>();
311 const int dim = mesh->dimension();
312 Eigen::MatrixXd extended_sol;
313 for (int t = 0; t <= time_steps; ++t)
314 {
315 double forward_solve_time = 0, remeshing_time = 0, global_relaxation_time = 0;
316
317 {
318 POLYFEM_SCOPED_TIMER(forward_solve_time);
319 solve_homogenization_step(t, extended_sol, false, user_post_step);
320 }
321 sol = extended_sol.topRows(extended_sol.size() - dim * dim) + polyfem::io::Evaluator::generate_linear_field(n_bases, mesh_nodes, utils::unflatten(extended_sol.bottomRows(dim * dim), dim));
322
323 if (is_static)
324 return;
325
326 // Always save the solution for consistency
327 save_timestep(t0 + dt * t, t + t_offset, t0, dt, sol, Eigen::MatrixXd()); // no pressure
328
329 {
330 POLYFEM_SCOPED_TIMER("Update quantities");
331
332 // solve_data.time_integrator->update_quantities(sol);
333
334 solve_data.nl_problem->update_quantities(t0 + (t + 1) * dt, sol);
335
338 }
339
340 logger().info("{}/{} t={}", t, time_steps, t0 + dt * t);
341
342 // const std::string rest_mesh_path = args["output"]["data"]["rest_mesh"].get<std::string>();
343 // if (!rest_mesh_path.empty())
344 // {
345 // Eigen::MatrixXd V;
346 // Eigen::MatrixXi F;
347 // build_mesh_matrices(V, F);
348 // polyfem::io::MshWriter::write(
349 // resolve_output_path(fmt::format(args["output"]["data"]["rest_mesh"], t)),
350 // V, F, mesh->get_body_ids(), mesh->is_volume(), /*binary=*/true);
351 // }
352
353 // const std::string &state_path = resolve_output_path(fmt::format(args["output"]["data"]["state"], t));
354 // if (!state_path.empty())
355 // solve_data.time_integrator->save_state(state_path);
356
357 // save restart file
358 save_restart_json(t0, dt, t);
359 // stats_csv.write(t, forward_solve_time, remeshing_time, global_relaxation_time, sol);
360 }
361 }
362
363} // namespace polyfem::legacy
Quadrature quadrature
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
Eigen::MatrixXd eval(const double t) const
const Eigen::VectorXi & get_fixed_entry() const
static Eigen::MatrixXd generate_linear_field(const int n_bases, const std::shared_ptr< mesh::MeshNodes > mesh_nodes, const Eigen::MatrixXd &grad)
StiffnessMatrix pure_mass
Definition State.hpp:244
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
Definition State.hpp:268
ipc::CollisionMesh collision_mesh
IPC collision mesh.
Definition State.hpp:656
StiffnessMatrix mass
Mass matrix, it is computed only for time dependent problems.
Definition State.hpp:243
std::vector< mesh::LocalBoundary > local_boundary
mapping from elements to nodes for dirichlet boundary conditions
Definition State.hpp:545
std::vector< mesh::LocalBoundary > local_pressure_boundary
mapping from elements to nodes for pressure boundary conditions
Definition State.hpp:549
std::shared_ptr< polyfem::mesh::MeshNodes > mesh_nodes
Mapping from input nodes to FE nodes.
Definition State.hpp:233
std::shared_ptr< assembler::Mass > mass_matrix_assembler
Definition State.hpp:196
std::unordered_map< int, std::vector< mesh::LocalBoundary > > local_pressure_cavity
mapping from elements to nodes for pressure boundary conditions
Definition State.hpp:551
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Definition State.hpp:578
Eigen::MatrixXd rhs
System right-hand side.
Definition State.hpp:252
json args
main input arguments containing all defaults
Definition State.hpp:140
void solve_homogenization(const int time_steps, const double t0, const double dt, Eigen::MatrixXd &sol, UserPostStepCallback user_post_step={})
int n_pressure_bases
number of pressure bases
Definition State.hpp:220
int n_bases
number of bases
Definition State.hpp:218
void solve_homogenization_step(int step, Eigen::MatrixXd &sol, bool adaptive_initial_weight=false, UserPostStepCallback user_post_step={})
In Elasticity PDE, solve for "min W(disp_grad + \grad u)" instead of "min W(\grad u)".
void save_restart_json(const double t0, const double dt, const int t) const
Save a JSON sim file for restarting the simulation at time t.
mesh::Obstacle obstacle
Obstacles used in collisions.
Definition State.hpp:580
std::string root_path() const
Get the root path for the state (e.g., args["root_path"] or ".")
assembler::AssemblyValsCache ass_vals_cache
used to store assembly values for small problems
Definition State.hpp:236
void init_homogenization_solve(const double t)
assembler::AssemblyValsCache mass_ass_vals_cache
Definition State.hpp:237
QuadratureOrders n_boundary_samples() const
quadrature used for projecting boundary conditions
Definition State.hpp:308
double characteristic_length
Definition State.hpp:248
std::shared_ptr< assembler::Assembler > assembler
assemblers
Definition State.hpp:194
double avg_mass
average system mass, used for contact with IPC
Definition State.hpp:246
std::vector< basis::ElementBases > bases
FE bases, the size is #elements.
Definition State.hpp:211
std::shared_ptr< assembler::PressureAssembler > elasticity_pressure_assembler
Definition State.hpp:202
bool is_adhesion_enabled() const
does the simulation have adhesion
Definition State.hpp:700
ipc::CollisionMesh periodic_collision_mesh
IPC collision mesh under periodic BC.
Definition State.hpp:659
Eigen::VectorXi periodic_collision_mesh_to_basis
index mapping from periodic 2x2 collision mesh to FE periodic mesh
Definition State.hpp:661
assembler::MacroStrainValue macro_strain_constraint
Definition State.hpp:798
std::vector< mesh::LocalBoundary > total_local_boundary
mapping from elements to nodes for all mesh
Definition State.hpp:543
std::shared_ptr< polysolve::nonlinear::Solver > make_nl_solver(bool for_al) const
factory to create the nl solver depending on input
void save_timestep(const double time, const int t, const double t0, const double dt, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure)
saves a timestep
std::vector< mesh::LocalBoundary > local_neumann_boundary
mapping from elements to nodes for neumann boundary conditions
Definition State.hpp:547
std::vector< int > boundary_nodes
list of boundary nodes
Definition State.hpp:539
solver::SolveData solve_data
timedependent stuff cached
Definition State.hpp:385
Eigen::VectorXi in_node_to_node
Inpute nodes (including high-order) to polyfem nodes, only for isoparametric.
Definition State.hpp:562
double characteristic_force_density
Definition State.hpp:249
std::shared_ptr< solver::PeriodicContactForm > periodic_contact_form
void update_dt()
updates the dt inside the different forms
std::vector< std::shared_ptr< Form > > init_forms(const Units &units, const io::ResourceIO &resources, const int dim, const double t, const Eigen::VectorXi &in_node_to_node, const int n_bases, std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &geom_bases, const assembler::Assembler &assembler, assembler::AssemblyValsCache &ass_vals_cache, const assembler::AssemblyValsCache &mass_ass_vals_cache, const double jacobian_threshold, const solver::ElementInversionCheck check_inversion, const unsigned conservative_max_iter, const int n_pressure_bases, const std::vector< int > &boundary_nodes, const std::vector< mesh::LocalBoundary > &local_boundary, const std::vector< mesh::LocalBoundary > &local_neumann_boundary, const QuadratureOrders &n_boundary_samples, const Eigen::MatrixXd &rhs, const Eigen::MatrixXd &sol, const assembler::Density &density, const std::vector< mesh::LocalBoundary > &local_pressure_boundary, const std::unordered_map< int, std::vector< mesh::LocalBoundary > > &local_pressure_cavity, const std::shared_ptr< assembler::PressureAssembler > pressure_assembler, const bool ignore_inertia, const StiffnessMatrix &mass, const std::shared_ptr< assembler::ViscousDamping > damping_assembler, const double lagged_regularization_weight, const int lagged_regularization_iterations, const size_t obstacle_ndof, const std::vector< std::string > &hard_constraint_files, const std::vector< json > &soft_constraint_files, const json &zero_mean, const bool contact_enabled, const ipc::CollisionMesh &collision_mesh, const double dhat, const double avg_mass, const bool use_area_weighting, const bool use_improved_max_operator, const bool use_physical_barrier, const json &barrier_stiffness, const double initial_barrier_stiffness, const ipc::BroadPhaseMethod broad_phase, const double ccd_tolerance, const long ccd_max_iterations, const bool enable_shape_derivatives, const bool use_gcp_formulation, const double alpha_t, const double alpha_n, const bool use_adaptive_dhat, const double min_distance_ratio, const bool adhesion_enabled, const double dhat_p, const double dhat_a, const double Y, const double tangential_adhesion_coefficient, const double epsa, const int tangential_adhesion_iterations, const assembler::MacroStrainValue &macro_strain_constraint, const bool periodic_contact, const Eigen::VectorXi &tiled_to_single, const double friction_coefficient, const double epsv, const int friction_iterations, const json &rayleigh_damping, const BCLumpingMode al_lumping=BCLumpingMode::ROW_SUM, const mesh::Mesh *periodic_mesh=nullptr, const std::vector< mesh::LocalBoundary > *periodic_local_boundary=nullptr, const json &periodic_conditions=json::array(), const int fe_space_id=-1)
Initialize the forms and return a vector of pointers to them.
Definition SolveData.cpp:57
std::shared_ptr< solver::NLProblem > nl_problem
std::shared_ptr< solver::MacroStrainLagrangianForm > strain_al_lagr_form
std::vector< std::pair< std::string, std::shared_ptr< solver::Form > > > named_forms() const
std::vector< std::shared_ptr< solver::AugmentedLagrangianForm > > al_form
void update_barrier_stiffness(const Eigen::VectorXd &x)
update the barrier stiffness for the forms
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
bool is_param_valid(const json &params, const std::string &key)
Determine if a key exists and is non-null in a json object.
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
Eigen::VectorXd flatten(const Eigen::MatrixXd &X)
Flatten rowwises.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24