PolyFEM
Loading...
Searching...
No Matches
StateHomogenization.cpp
Go to the documentation of this file.
1#include <polyfem/State.hpp>
7#include <polysolve/linear/FEMSolver.hpp>
8#include <polysolve/nonlinear/Solver.hpp>
9
14
15#include <unsupported/Eigen/SparseExtra>
16
19
20#include <ipc/ipc.hpp>
21
22namespace polyfem
23{
24
25 using namespace assembler;
26 using namespace mesh;
27 using namespace solver;
28 using namespace utils;
29 using namespace quadrature;
30
32 {
33 const int dim = mesh->dimension();
34 const int ndof = n_bases * dim;
35
36 const std::vector<std::shared_ptr<Form>> forms = solve_data.init_forms(
37 // General
38 units,
39 mesh->dimension(), t,
40 // Elastic form
42 // Body form
44 n_boundary_samples(), rhs, Eigen::VectorXd::Zero(ndof) /* only to set neumann BC, not used*/, mass_matrix_assembler->density(),
45 // Pressure form
47 // Inertia form
48 args.value("/time/quasistatic"_json_pointer, true), mass,
49 nullptr,
50 // Lagged regularization form
51 args["solver"]["advanced"]["lagged_regularization_weight"],
52 args["solver"]["advanced"]["lagged_regularization_iterations"],
53 // Augmented lagrangian form
54 obstacle.ndof(),
55 // Contact form
56 args["contact"]["enabled"], args["contact"]["periodic"].get<bool>() ? periodic_collision_mesh : collision_mesh, args["contact"]["dhat"],
57 avg_mass, args["contact"]["use_convergent_formulation"],
58 args["solver"]["contact"]["barrier_stiffness"],
59 args["solver"]["contact"]["CCD"]["broad_phase"],
60 args["solver"]["contact"]["CCD"]["tolerance"],
61 args["solver"]["contact"]["CCD"]["max_iterations"],
63 // Homogenization
65 // Periodic contact
66 args["contact"]["periodic"], periodic_collision_mesh_to_basis,
67 // Friction form
68 args["contact"]["friction_coefficient"],
69 args["contact"]["epsv"],
70 args["solver"]["contact"]["friction_iterations"],
71 // Rayleigh damping form
72 args["solver"]["rayleigh_damping"]);
73
74 for (const auto &[name, form] : solve_data.named_forms())
75 {
76 if (name == "augmented_lagrangian_lagr" || name == "augmented_lagrangian_penalty")
77 {
78 form->set_weight(0);
79 form->disable();
80 }
81 }
82
83 bool solve_symmetric_flag = false;
84 {
85 const auto &fixed_entry = macro_strain_constraint.get_fixed_entry();
86 for (int i = 0; i < dim; i++)
87 {
88 for (int j = 0; j < i; j++)
89 {
90 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())
91 {
92 logger().info("Strain entry [{},{}] and [{},{}] are not fixed, solve for symmetric strain...", i, j, j, i);
93 solve_symmetric_flag = true;
94 break;
95 }
96 }
97 if (solve_symmetric_flag)
98 break;
99 }
100 }
101
102 std::shared_ptr<NLHomoProblem> homo_problem = std::make_shared<NLHomoProblem>(
103 ndof,
108 *this, t, forms, solve_symmetric_flag);
110 homo_problem->add_form(solve_data.periodic_contact_form);
112 homo_problem->add_form(solve_data.strain_al_lagr_form);
114 homo_problem->add_form(solve_data.strain_al_pen_form);
115
116 solve_data.nl_problem = homo_problem;
117 solve_data.nl_problem->init(Eigen::VectorXd::Zero(homo_problem->reduced_size() + homo_problem->macro_reduced_size()));
118 solve_data.nl_problem->update_quantities(t, Eigen::VectorXd::Zero(homo_problem->reduced_size() + homo_problem->macro_reduced_size()));
119 }
120
121 void State::solve_homogenization_step(Eigen::MatrixXd &sol, const int t, bool adaptive_initial_weight)
122 {
123 const int dim = mesh->dimension();
124 const int ndof = n_bases * dim;
125
126 auto homo_problem = std::dynamic_pointer_cast<NLHomoProblem>(solve_data.nl_problem);
127
128 Eigen::VectorXd extended_sol;
129 extended_sol.setZero(ndof + dim * dim);
130
131 if (sol.size() == extended_sol.size())
132 extended_sol = sol;
133
134 const auto &fixed_entry = macro_strain_constraint.get_fixed_entry();
135 homo_problem->set_fixed_entry({});
136 {
137 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver = make_nl_solver(true);
138
139 Eigen::VectorXi al_indices = fixed_entry.array() + homo_problem->full_size();
140 Eigen::VectorXd al_values = utils::flatten(macro_strain_constraint.eval(t))(fixed_entry);
141
142 std::shared_ptr<MacroStrainALForm> al_form = solve_data.strain_al_pen_form;
143 std::shared_ptr<MacroStrainLagrangianForm> lagr_form = solve_data.strain_al_lagr_form;
144 al_form->enable();
145 lagr_form->enable();
146
147 const double initial_weight = args["solver"]["augmented_lagrangian"]["initial_weight"];
148 const double max_weight = args["solver"]["augmented_lagrangian"]["max_weight"];
149 const double eta_tol = args["solver"]["augmented_lagrangian"]["eta"];
150 const double scaling = args["solver"]["augmented_lagrangian"]["scaling"];
151 double al_weight = initial_weight;
152
153 Eigen::VectorXd tmp_sol = homo_problem->extended_to_reduced(extended_sol);
154 const Eigen::VectorXd initial_sol = tmp_sol;
155 const double initial_error = al_form->compute_error(extended_sol);
156 double current_error = initial_error;
157
158 // try to enforce fixed values on macro strain
159 extended_sol(al_indices) = al_values;
160 Eigen::VectorXd reduced_sol = homo_problem->extended_to_reduced(extended_sol);
161
162 homo_problem->line_search_begin(tmp_sol, reduced_sol);
163 int al_steps = 0;
164 bool force_al = true;
165 while (force_al
166 || !std::isfinite(homo_problem->value(reduced_sol))
167 || !homo_problem->is_step_valid(tmp_sol, reduced_sol)
168 || !homo_problem->is_step_collision_free(tmp_sol, reduced_sol))
169 {
170 force_al = false;
171 homo_problem->line_search_end();
172
173 al_form->set_weight(al_weight);
174 logger().info("Solving AL Problem with weight {}", al_weight);
175
176 homo_problem->init(tmp_sol);
177 try
178 {
179 nl_solver->minimize(*homo_problem, tmp_sol);
180 }
181 catch (const std::runtime_error &e)
182 {
183 logger().error("AL solve failed!");
184 export_data(homo_problem->reduced_to_full(tmp_sol), Eigen::MatrixXd());
185 }
186
187 extended_sol = homo_problem->reduced_to_extended(tmp_sol);
188 logger().debug("Current macro strain: {}", extended_sol.tail(dim * dim));
189
190 current_error = al_form->compute_error(extended_sol);
191 const double eta = 1 - sqrt(current_error / initial_error);
192
193 logger().info("Current eta = {}, current error = {}, initial error = {}", eta, current_error, initial_error);
194
195 if (eta < eta_tol && al_weight < max_weight)
196 al_weight *= scaling;
197 else
198 lagr_form->update_lagrangian(extended_sol, al_weight);
199
200 if (eta <= 0)
201 {
202 if (adaptive_initial_weight)
203 {
204 args["solver"]["augmented_lagrangian"]["initial_weight"] = args["solver"]["augmented_lagrangian"]["initial_weight"].get<double>() * scaling;
205 {
206 json tmp = json::object();
207 tmp["/solver/augmented_lagrangian/initial_weight"_json_pointer] = args["solver"]["augmented_lagrangian"]["initial_weight"];
208 }
209 logger().warn("AL weight too small, increase weight and revert solution, new initial weight is {}", args["solver"]["augmented_lagrangian"]["initial_weight"].get<double>());
210 }
211 tmp_sol = initial_sol;
212 }
213
214 // try to enforce fixed values on macro strain
215 extended_sol(al_indices) = al_values;
216 reduced_sol = homo_problem->extended_to_reduced(extended_sol);
217
218 homo_problem->line_search_begin(tmp_sol, reduced_sol);
219 }
220 homo_problem->line_search_end();
221 al_form->disable();
222 lagr_form->disable();
223 }
224
225 homo_problem->set_fixed_entry(fixed_entry);
226
227 Eigen::VectorXd reduced_sol = homo_problem->extended_to_reduced(extended_sol);
228
229 homo_problem->init(reduced_sol);
230 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver = make_nl_solver(false);
231 nl_solver->minimize(*homo_problem, reduced_sol);
232
233 logger().info("Macro Strain: {}", extended_sol.tail(dim * dim).transpose());
234
235 // check saddle point
236 {
237 json linear_args = args["solver"]["linear"];
238 std::string solver_name = linear_args["solver"];
239 if (solver_name.find("Pardiso") != std::string::npos)
240 {
241 linear_args["solver"] = "Eigen::PardisoLLT";
242 std::unique_ptr<polysolve::linear::Solver> solver =
243 polysolve::linear::Solver::create(linear_args, logger());
244
246 homo_problem->hessian(reduced_sol, A);
247 Eigen::VectorXd x, b = Eigen::VectorXd::Zero(A.rows());
248 try
249 {
250 dirichlet_solve(
251 *solver, A, b, {}, x, A.rows(), args["output"]["data"]["stiffness_mat"], false, false, false);
252 }
253 catch (const std::runtime_error &error)
254 {
255 logger().error("The solution is a saddle point!");
256 }
257 }
258 }
259
260 sol = homo_problem->reduced_to_extended(reduced_sol);
261 if (args["/boundary_conditions/periodic_boundary/force_zero_mean"_json_pointer].get<bool>())
262 {
263 Eigen::VectorXd integral = io::Evaluator::integrate_function(bases, geom_bases(), sol, dim, dim);
264 double area = io::Evaluator::integrate_function(bases, geom_bases(), Eigen::VectorXd::Ones(n_bases), dim, 1)(0);
265 for (int d = 0; d < dim; d++)
266 sol(Eigen::seqN(d, n_bases, dim), 0).array() -= integral(d) / area;
267
268 reduced_sol = homo_problem->extended_to_reduced(sol);
269 }
270
272 cache_transient_adjoint_quantities(t, homo_problem->reduced_to_full(reduced_sol), utils::unflatten(sol.bottomRows(dim * dim), dim));
273 }
274
275 void State::solve_homogenization(const int time_steps, const double t0, const double dt, Eigen::MatrixXd &sol)
276 {
277 bool is_static = !is_param_valid(args, "time");
278 if (!is_static && !args["time"]["quasistatic"])
279 log_and_throw_error("Transient homogenization can only do quasi-static!");
280
282
283 const int dim = mesh->dimension();
284 Eigen::MatrixXd extended_sol;
285 for (int t = 0; t <= time_steps; ++t)
286 {
287 double forward_solve_time = 0, remeshing_time = 0, global_relaxation_time = 0;
288
289 {
290 POLYFEM_SCOPED_TIMER(forward_solve_time);
291 solve_homogenization_step(extended_sol, t, false);
292 }
293 sol = extended_sol.topRows(extended_sol.size() - dim * dim) + io::Evaluator::generate_linear_field(n_bases, mesh_nodes, utils::unflatten(extended_sol.bottomRows(dim * dim), dim));
294
295 if (is_static)
296 return;
297
298 // Always save the solution for consistency
299 save_timestep(t0 + dt * t, t, t0, dt, sol, Eigen::MatrixXd()); // no pressure
300
301 {
302 POLYFEM_SCOPED_TIMER("Update quantities");
303
304 // solve_data.time_integrator->update_quantities(sol);
305
306 solve_data.nl_problem->update_quantities(t0 + (t + 1) * dt, sol);
307
310 }
311
312 logger().info("{}/{} t={}", t, time_steps, t0 + dt * t);
313
314 // const std::string rest_mesh_path = args["output"]["data"]["rest_mesh"].get<std::string>();
315 // if (!rest_mesh_path.empty())
316 // {
317 // Eigen::MatrixXd V;
318 // Eigen::MatrixXi F;
319 // build_mesh_matrices(V, F);
320 // io::MshWriter::write(
321 // resolve_output_path(fmt::format(args["output"]["data"]["rest_mesh"], t)),
322 // V, F, mesh->get_body_ids(), mesh->is_volume(), /*binary=*/true);
323 // }
324
325 // const std::string &state_path = resolve_output_path(fmt::format(args["output"]["data"]["state"], t));
326 // if (!state_path.empty())
327 // solve_data.time_integrator->save_state(state_path);
328
329 // save restart file
330 save_restart_json(t0, dt, t);
331 // stats_csv.write(t, forward_solve_time, remeshing_time, global_relaxation_time, sol);
332 }
333 }
334
335} // namespace polyfem
Quadrature quadrature
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
assembler::MacroStrainValue macro_strain_constraint
Definition State.hpp:705
int n_bases
number of bases
Definition State.hpp:178
void cache_transient_adjoint_quantities(const int current_step, const Eigen::MatrixXd &sol, const Eigen::MatrixXd &disp_grad)
int n_pressure_bases
number of pressure bases
Definition State.hpp:180
assembler::AssemblyValsCache ass_vals_cache
used to store assembly values for small problems
Definition State.hpp:196
int n_boundary_samples() const
quadrature used for projecting boundary conditions
Definition State.hpp:263
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
Definition State.hpp:223
std::vector< mesh::LocalBoundary > local_pressure_boundary
mapping from elements to nodes for pressure boundary conditions
Definition State.hpp:437
mesh::Obstacle obstacle
Obstacles used in collisions.
Definition State.hpp:468
std::shared_ptr< assembler::Assembler > assembler
assemblers
Definition State.hpp:155
ipc::CollisionMesh periodic_collision_mesh
IPC collision mesh under periodic BC.
Definition State.hpp:518
ipc::CollisionMesh collision_mesh
IPC collision mesh.
Definition State.hpp:515
solver::CacheLevel optimization_enabled
Definition State.hpp:647
void solve_homogenization_step(Eigen::MatrixXd &sol, const int t=0, bool adaptive_initial_weight=false)
In Elasticity PDE, solve for "min W(disp_grad + \grad u)" instead of "min W(\grad u)".
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::shared_ptr< polysolve::nonlinear::Solver > make_nl_solver(bool for_al) const
factory to create the nl solver depending on input
std::shared_ptr< assembler::Mass > mass_matrix_assembler
Definition State.hpp:157
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Definition State.hpp:466
std::shared_ptr< polyfem::mesh::MeshNodes > mesh_nodes
Mapping from input nodes to FE nodes.
Definition State.hpp:193
StiffnessMatrix mass
Mass matrix, it is computed only for time dependent problems.
Definition State.hpp:202
void solve_homogenization(const int time_steps, const double t0, const double dt, Eigen::MatrixXd &sol)
json args
main input arguments containing all defaults
Definition State.hpp:101
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.
Eigen::VectorXi periodic_collision_mesh_to_basis
index mapping from periodic 2x2 collision mesh to FE periodic mesh
Definition State.hpp:520
std::vector< basis::ElementBases > bases
FE bases, the size is #elements.
Definition State.hpp:171
std::vector< mesh::LocalBoundary > local_boundary
mapping from elements to nodes for dirichlet boundary conditions
Definition State.hpp:433
double avg_mass
average system mass, used for contact with IPC
Definition State.hpp:204
int ndof() const
Definition State.hpp:653
void export_data(const Eigen::MatrixXd &sol, const Eigen::MatrixXd &pressure)
saves all data on the disk according to the input params
assembler::AssemblyValsCache mass_ass_vals_cache
Definition State.hpp:197
std::vector< int > boundary_nodes
list of boundary nodes
Definition State.hpp:427
solver::SolveData solve_data
timedependent stuff cached
Definition State.hpp:324
std::vector< mesh::LocalBoundary > local_neumann_boundary
mapping from elements to nodes for neumann boundary conditions
Definition State.hpp:435
std::shared_ptr< assembler::PressureAssembler > elasticity_pressure_assembler
Definition State.hpp:162
Eigen::MatrixXd rhs
System right-hand side.
Definition State.hpp:207
std::unordered_map< int, std::vector< mesh::LocalBoundary > > local_pressure_cavity
mapping from elements to nodes for pressure boundary conditions
Definition State.hpp:439
void init_homogenization_solve(const double t)
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)
static Eigen::VectorXd integrate_function(const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::MatrixXd &fun, const int dim, const int actual_dim)
std::shared_ptr< solver::PeriodicContactForm > periodic_contact_form
void update_dt()
updates the dt inside the different forms
std::shared_ptr< solver::NLProblem > nl_problem
std::shared_ptr< solver::MacroStrainLagrangianForm > strain_al_lagr_form
std::shared_ptr< solver::MacroStrainALForm > strain_al_pen_form
std::vector< std::pair< std::string, std::shared_ptr< solver::Form > > > named_forms() const
std::vector< std::shared_ptr< Form > > init_forms(const Units &units, const int dim, const double t, const int n_bases, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &geom_bases, const assembler::Assembler &assembler, const assembler::AssemblyValsCache &ass_vals_cache, const assembler::AssemblyValsCache &mass_ass_vals_cache, 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 int 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 bool contact_enabled, const ipc::CollisionMesh &collision_mesh, const double dhat, const double avg_mass, const bool use_convergent_contact_formulation, const json &barrier_stiffness, const ipc::BroadPhaseMethod broad_phase, const double ccd_tolerance, const long ccd_max_iterations, const bool enable_shape_derivatives, 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)
Initialize the forms and return a vector of pointers to them.
Definition SolveData.cpp:29
void update_barrier_stiffness(const Eigen::VectorXd &x)
update the barrier stiffness for the forms
std::shared_ptr< assembler::RhsAssembler > rhs_assembler
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:42
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:71
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:22