PolyFEM
Loading...
Searching...
No Matches
StateDiff.cpp
Go to the documentation of this file.
2
3#include <polyfem/State.hpp>
4
8
11
12#include <polysolve/linear/FEMSolver.hpp>
13
18// Below types in SolverData are forward declared, include them explicitly.
24
26
27#include <ipc/ipc.hpp>
28#include <ipc/potentials/friction_potential.hpp>
29
30#include <Eigen/Dense>
31
32#include <algorithm>
33#include <vector>
34#include <cassert>
35#include <vector>
36
37using namespace polyfem::basis;
38
39namespace polyfem
40{
41 namespace
42 {
43 void replace_rows_by_identity(StiffnessMatrix &reduced_mat, const StiffnessMatrix &mat, const std::vector<int> &rows)
44 {
45 reduced_mat.resize(mat.rows(), mat.cols());
46
47 std::vector<bool> mask(mat.rows(), false);
48 for (int i : rows)
49 mask[i] = true;
50
51 std::vector<Eigen::Triplet<double>> coeffs;
52 for (int k = 0; k < mat.outerSize(); ++k)
53 {
54 for (StiffnessMatrix::InnerIterator it(mat, k); it; ++it)
55 {
56 if (mask[it.row()])
57 {
58 if (it.row() == it.col())
59 coeffs.emplace_back(it.row(), it.col(), 1.0);
60 }
61 else
62 coeffs.emplace_back(it.row(), it.col(), it.value());
63 }
64 }
65 reduced_mat.setFromTriplets(coeffs.begin(), coeffs.end());
66 }
67
68 void compute_force_jacobian_prev(const State &state, const DiffCache &diff_cache, const int force_step, const int sol_step, StiffnessMatrix &hessian_prev)
69 {
70 assert(force_step > 0);
71 assert(force_step > sol_step);
72
73 auto &s = state;
74
75 if (s.assembler->is_linear() && !s.is_contact_enabled())
76 {
77 hessian_prev = StiffnessMatrix(s.ndof(), s.ndof());
78 }
79 else
80 {
81 const Eigen::MatrixXd u = diff_cache.u(force_step);
82 const Eigen::MatrixXd u_prev = diff_cache.u(sol_step);
83 const double beta = time_integrator::BDF::betas(diff_cache.bdf_order(force_step) - 1);
84 const double dt = s.solve_data.time_integrator->dt();
85
86 hessian_prev = StiffnessMatrix(u.size(), u.size());
87 if (s.problem->is_time_dependent())
88 {
89 if (s.solve_data.friction_form)
90 {
91 if (sol_step == force_step - 1)
92 {
93 Eigen::MatrixXd surface_solution_prev = s.collision_mesh.vertices(utils::unflatten(u_prev, s.mesh->dimension()));
94 Eigen::MatrixXd surface_solution = s.collision_mesh.vertices(utils::unflatten(u, s.mesh->dimension()));
95
96 // TODO: use the time integration to compute the velocity
97 const Eigen::MatrixXd surface_velocities = (surface_solution - surface_solution_prev) / dt;
98 const double dv_dut = -1 / dt;
99
100 if (const auto barrier_contact = dynamic_cast<const solver::BarrierContactForm *>(s.solve_data.contact_form.get()))
101 {
102 hessian_prev =
103 s.solve_data.friction_form->friction_potential().force_jacobian(
104 diff_cache.friction_collision_set(force_step),
105 s.collision_mesh,
106 s.collision_mesh.rest_positions(),
107 /*lagged_displacements=*/surface_solution_prev,
108 surface_velocities,
109 barrier_contact->barrier_potential(),
110 barrier_contact->barrier_stiffness(),
111 ipc::FrictionPotential::DiffWRT::LAGGED_DISPLACEMENTS)
112 + s.solve_data.friction_form->friction_potential().force_jacobian(
113 diff_cache.friction_collision_set(force_step),
114 s.collision_mesh,
115 s.collision_mesh.rest_positions(),
116 /*lagged_displacements=*/surface_solution_prev,
117 surface_velocities,
118 barrier_contact->barrier_potential(),
119 barrier_contact->barrier_stiffness(),
120 ipc::FrictionPotential::DiffWRT::VELOCITIES)
121 * dv_dut;
122 }
123
124 hessian_prev *= -1;
125
126 // {
127 // Eigen::MatrixXd X = collision_mesh.rest_positions();
128 // Eigen::VectorXd x = utils::flatten(surface_solution_prev);
129 // const double barrier_stiffness = solve_data.contact_form->barrier_stiffness();
130 // const double dhat = solve_data.contact_form->dhat();
131 // const double mu = solve_data.friction_form->mu();
132 // const double epsv = solve_data.friction_form->epsv();
133
134 // Eigen::MatrixXd fgrad;
135 // fd::finite_jacobian(
136 // x, [&](const Eigen::VectorXd &y) -> Eigen::VectorXd
137 // {
138 // Eigen::MatrixXd fd_Ut = utils::unflatten(y, surface_solution_prev.cols());
139
140 // ipc::TangentialCollisions fd_friction_constraints;
141 // ipc::NormalCollisions fd_constraints;
142 // fd_constraints.set_use_convergent_formulation(solve_data.contact_form->use_convergent_formulation());
143 // fd_constraints.set_enable_shape_derivatives(true);
144 // fd_constraints.build(collision_mesh, X + fd_Ut, dhat);
145
146 // fd_friction_constraints.build(
147 // collision_mesh, X + fd_Ut, fd_constraints, dhat, barrier_stiffness,
148 // mu);
149
150 // return fd_friction_constraints.compute_potential_gradient(collision_mesh, (surface_solution - fd_Ut) / dt, epsv);
151
152 // }, fgrad, fd::AccuracyOrder::SECOND, 1e-8);
153
154 // logger().trace("force Ut derivative error {} {}", (fgrad - hessian_prev).norm(), hessian_prev.norm());
155 // }
156
157 hessian_prev = s.collision_mesh.to_full_dof(hessian_prev); // / (beta * dt) / (beta * dt);
158 }
159 else
160 {
161 // const double alpha = time_integrator::BDF::alphas(std::min(diff_cached.bdf_order(force_step), force_step) - 1)[force_step - sol_step - 1];
162 // Eigen::MatrixXd velocity = collision_mesh.map_displacements(utils::unflatten(diff_cached.v(force_step), collision_mesh.dim()));
163 // hessian_prev = diff_cached.friction_collision_set(force_step).compute_potential_hessian( //
164 // collision_mesh, velocity, solve_data.friction_form->epsv(), false) * (-alpha / beta / dt);
165
166 // hessian_prev = collision_mesh.to_full_dof(hessian_prev);
167 }
168 }
169
170 if (s.solve_data.tangential_adhesion_form)
171 {
172
173 if (sol_step == force_step - 1)
174 {
175 StiffnessMatrix adhesion_hessian_prev(u.size(), u.size());
176
177 Eigen::MatrixXd surface_solution_prev = s.collision_mesh.vertices(utils::unflatten(u_prev, s.mesh->dimension()));
178 Eigen::MatrixXd surface_solution = s.collision_mesh.vertices(utils::unflatten(u, s.mesh->dimension()));
179
180 // TODO: use the time integration to compute the velocity
181 const Eigen::MatrixXd surface_velocities = (surface_solution - surface_solution_prev) / dt;
182 const double dv_dut = -1 / dt;
183
184 adhesion_hessian_prev =
185 s.solve_data.tangential_adhesion_form->tangential_adhesion_potential().force_jacobian(
186 diff_cache.tangential_adhesion_collision_set(force_step),
187 s.collision_mesh,
188 s.collision_mesh.rest_positions(),
189 /*lagged_displacements=*/surface_solution_prev,
190 surface_velocities,
191 s.solve_data.normal_adhesion_form->normal_adhesion_potential(),
192 1,
193 ipc::TangentialPotential::DiffWRT::LAGGED_DISPLACEMENTS)
194 + s.solve_data.tangential_adhesion_form->tangential_adhesion_potential().force_jacobian(
195 diff_cache.tangential_adhesion_collision_set(force_step),
196 s.collision_mesh,
197 s.collision_mesh.rest_positions(),
198 /*lagged_displacements=*/surface_solution_prev,
199 surface_velocities,
200 s.solve_data.normal_adhesion_form->normal_adhesion_potential(),
201 1,
202 ipc::TangentialPotential::DiffWRT::VELOCITIES)
203 * dv_dut;
204
205 adhesion_hessian_prev *= -1;
206
207 adhesion_hessian_prev = s.collision_mesh.to_full_dof(adhesion_hessian_prev); // / (beta * dt) / (beta * dt);
208
209 hessian_prev += adhesion_hessian_prev;
210 }
211 }
212
213 if (s.damping_assembler->is_valid() && sol_step == force_step - 1) // velocity in damping uses BDF1
214 {
215 utils::SparseMatrixCache mat_cache;
216 StiffnessMatrix damping_hessian_prev(u.size(), u.size());
217 s.damping_prev_assembler->assemble_hessian(s.mesh->is_volume(), s.n_bases, false, s.bases, s.geom_bases(), s.ass_vals_cache, force_step * s.args["time"]["dt"].get<double>() + s.args["time"]["t0"].get<double>(), dt, u, u_prev, mat_cache, damping_hessian_prev);
218
219 hessian_prev += damping_hessian_prev;
220 }
221
222 if (sol_step == force_step - 1)
223 {
224 StiffnessMatrix body_force_hessian(u.size(), u.size());
225 s.solve_data.body_form->hessian_wrt_u_prev(u_prev, force_step * dt, body_force_hessian);
226 hessian_prev += body_force_hessian;
227 }
228 }
229 }
230 }
231
232 Eigen::MatrixXd solve_static_adjoint(const State &state, const DiffCache &diff_cache, const Eigen::MatrixXd &adjoint_rhs)
233 {
234 auto &s = state;
235
236 Eigen::MatrixXd b = adjoint_rhs;
237
238 Eigen::MatrixXd adjoint;
239 if (s.static_linear_solver_cache)
240 {
241 b(s.boundary_nodes, Eigen::all).setZero();
242
243 StiffnessMatrix A = diff_cache.gradu_h(0);
244 const int full_size = A.rows();
245 const int problem_dim = s.problem->is_scalar() ? 1 : s.mesh->dimension();
246 int precond_num = problem_dim * s.n_bases;
247
248 b.conservativeResizeLike(Eigen::MatrixXd::Zero(A.rows(), b.cols()));
249
250 std::vector<int> boundary_nodes_tmp;
251 if (s.has_periodic_bc())
252 {
253 boundary_nodes_tmp = s.periodic_bc->full_to_periodic(s.boundary_nodes);
254 precond_num = s.periodic_bc->full_to_periodic(A);
255 b = s.periodic_bc->full_to_periodic(b, true);
256 }
257 else
258 boundary_nodes_tmp = s.boundary_nodes;
259
260 adjoint.setZero(s.ndof(), adjoint_rhs.cols());
261 for (int i = 0; i < b.cols(); i++)
262 {
263 Eigen::VectorXd x, tmp;
264 tmp = b.col(i);
265 dirichlet_solve_prefactorized(*s.static_linear_solver_cache, A, tmp, boundary_nodes_tmp, x);
266
267 if (s.has_periodic_bc())
268 adjoint.col(i) = s.periodic_bc->periodic_to_full(full_size, x);
269 else
270 adjoint.col(i) = x;
271 }
272 }
273 else
274 {
275 auto solver = polysolve::linear::Solver::create(s.args["solver"]["adjoint_linear"], adjoint_logger());
276
277 StiffnessMatrix A = diff_cache.gradu_h(0); // This should be transposed, but A is symmetric in hyper-elastic and diffusion problems
278
279 /*
280 For non-periodic problems, the adjoint solution p's size is the full size in NLProblem
281 For periodic problems, the adjoint solution p's size is the reduced size in NLProblem
282 */
283 if (!s.is_homogenization())
284 {
285 adjoint.setZero(s.ndof(), adjoint_rhs.cols());
286 for (int i = 0; i < b.cols(); i++)
287 {
288 Eigen::VectorXd tmp = b.col(i);
289 tmp(s.boundary_nodes).setZero();
290
291 Eigen::VectorXd x;
292 x.setZero(tmp.size());
293 dirichlet_solve(*solver, A, tmp, s.boundary_nodes, x, A.rows(), "", false, false, false);
294
295 adjoint.col(i) = x;
296 adjoint(s.boundary_nodes, i) = -b(s.boundary_nodes, i);
297 }
298 }
299 else
300 {
301 solver->analyze_pattern(A, A.rows());
302 solver->factorize(A);
303
304 adjoint.setZero(adjoint_rhs.rows(), adjoint_rhs.cols());
305 for (int i = 0; i < b.cols(); i++)
306 {
307 Eigen::MatrixXd tmp = b.col(i);
308
309 Eigen::VectorXd x;
310 x.setZero(tmp.size());
311 solver->solve(tmp, x);
312 x.conservativeResize(adjoint.rows());
313
314 adjoint.col(i) = x;
315 }
316 }
317 }
318
319 return adjoint;
320 }
321
322 Eigen::MatrixXd solve_transient_adjoint(const State &state, const DiffCache &diff_cache, const Eigen::MatrixXd &adjoint_rhs)
323 {
324 auto &s = state;
325
326 const double dt = s.args["time"]["dt"];
327 const int time_steps = s.args["time"]["time_steps"];
328
329 int bdf_order = 1;
330 if (s.args["time"]["integrator"].is_string())
331 bdf_order = 1;
332 else if (s.args["time"]["integrator"]["type"] == "ImplicitEuler")
333 bdf_order = 1;
334 else if (s.args["time"]["integrator"]["type"] == "BDF")
335 bdf_order = s.args["time"]["integrator"]["steps"].get<int>();
336 else
337 log_and_throw_adjoint_error("Integrator type not supported for differentiability.");
338
339 assert(adjoint_rhs.cols() == time_steps + 1);
340
341 const int cols_per_adjoint = time_steps + 1;
342 Eigen::MatrixXd adjoints;
343 adjoints.setZero(s.ndof(), cols_per_adjoint * 2);
344
345 // set dirichlet rows of mass to identity
346 StiffnessMatrix reduced_mass;
347 replace_rows_by_identity(reduced_mass, s.mass, s.boundary_nodes);
348
349 Eigen::MatrixXd sum_alpha_p, sum_alpha_nu;
350 for (int i = time_steps; i >= 0; --i)
351 {
352 {
353 sum_alpha_p.setZero(s.ndof(), 1);
354 sum_alpha_nu.setZero(s.ndof(), 1);
355
356 const int num = std::min(bdf_order, time_steps - i);
357
358 Eigen::VectorXd bdf_coeffs = Eigen::VectorXd::Zero(num);
359 for (int j = 0; j < bdf_order && i + j < time_steps; ++j)
360 bdf_coeffs(j) = -time_integrator::BDF::alphas(std::min(bdf_order - 1, i + j))[j];
361
362 sum_alpha_p = adjoints.middleCols(i + 1, num) * bdf_coeffs;
363 sum_alpha_nu = adjoints.middleCols(cols_per_adjoint + i + 1, num) * bdf_coeffs;
364 }
365
366 Eigen::VectorXd rhs_ = -reduced_mass.transpose() * sum_alpha_nu - adjoint_rhs.col(i);
367 for (int j = 1; j <= bdf_order; j++)
368 {
369 if (i + j > time_steps)
370 break;
371
372 StiffnessMatrix gradu_h_prev;
373 compute_force_jacobian_prev(state, diff_cache, i + j, i, gradu_h_prev);
374 Eigen::VectorXd tmp = adjoints.col(i + j) * (time_integrator::BDF::betas(diff_cache.bdf_order(i + j) - 1) * dt);
375 tmp(s.boundary_nodes).setZero();
376 rhs_ += -gradu_h_prev.transpose() * tmp;
377 }
378
379 if (i > 0)
380 {
381 double beta_dt = time_integrator::BDF::betas(diff_cache.bdf_order(i) - 1) * dt;
382
383 rhs_ += (1. / beta_dt) * (diff_cache.gradu_h(i) - reduced_mass).transpose() * sum_alpha_p;
384
385 {
386 StiffnessMatrix A = diff_cache.gradu_h(i).transpose();
387 Eigen::VectorXd b_ = rhs_;
388 b_(s.boundary_nodes).setZero();
389
390 auto solver = polysolve::linear::Solver::create(s.args["solver"]["adjoint_linear"], adjoint_logger());
391
392 Eigen::VectorXd x;
393 dirichlet_solve(*solver, A, b_, s.boundary_nodes, x, A.rows(), "", false, false, false);
394 adjoints.col(i + cols_per_adjoint) = x;
395 }
396
397 // TODO: generalize to BDFn
398 Eigen::VectorXd tmp = rhs_(s.boundary_nodes);
399 if (i + 1 < cols_per_adjoint)
400 tmp += (-2. / beta_dt) * adjoints(s.boundary_nodes, i + 1);
401 if (i + 2 < cols_per_adjoint)
402 tmp += (1. / beta_dt) * adjoints(s.boundary_nodes, i + 2);
403
404 tmp -= (diff_cache.gradu_h(i).transpose() * adjoints.col(i + cols_per_adjoint))(s.boundary_nodes);
405 adjoints(s.boundary_nodes, i + cols_per_adjoint) = tmp;
406 adjoints.col(i) = beta_dt * adjoints.col(i + cols_per_adjoint) - sum_alpha_p;
407 }
408 else
409 {
410 adjoints.col(i) = -reduced_mass.transpose() * sum_alpha_p;
411 adjoints.col(i + cols_per_adjoint) = rhs_; // adjoint_nu[0] actually stores adjoint_mu[0]
412 }
413 }
414 return adjoints;
415 }
416
417 Eigen::MatrixXd solve_adjoint(const State &state, const DiffCache &diff_cache, const Eigen::MatrixXd &rhs)
418 {
419 if (state.problem->is_time_dependent())
420 return solve_transient_adjoint(state, diff_cache, rhs);
421 else
422 return solve_static_adjoint(state, diff_cache, rhs);
423 }
424 } // namespace
425
426 void solve_adjoint_cached(const State &state, DiffCache &diff_cache, const Eigen::MatrixXd &rhs)
427 {
428 diff_cache.cache_adjoints(solve_adjoint(state, diff_cache, rhs));
429 }
430
438 Eigen::MatrixXd get_adjoint_mat(const State &state, const DiffCache &diff_cache, int type)
439 {
440 assert(diff_cache.adjoint_mat().size() > 0);
441
442 auto &s = state;
443
444 if (s.problem->is_time_dependent())
445 {
446 if (type == 0)
447 return diff_cache.adjoint_mat().leftCols(diff_cache.adjoint_mat().cols() / 2);
448 else if (type == 1)
449 return diff_cache.adjoint_mat().middleCols(diff_cache.adjoint_mat().cols() / 2, diff_cache.adjoint_mat().cols() / 2);
450 else
451 log_and_throw_adjoint_error("Invalid adjoint type!");
452 }
453
454 return diff_cache.adjoint_mat();
455 }
456
457 void compute_surface_node_ids(const State &state, const int surface_selection, std::vector<int> &node_ids)
458 {
459 auto &s = state;
460
461 node_ids = {};
462
463 const auto &gbases = s.geom_bases();
464 for (const auto &lb : s.total_local_boundary)
465 {
466 const int e = lb.element_id();
467 for (int i = 0; i < lb.size(); ++i)
468 {
469 const int primitive_global_id = lb.global_primitive_id(i);
470 const int boundary_id = s.mesh->get_boundary_id(primitive_global_id);
471 const auto nodes = gbases[e].local_nodes_for_primitive(primitive_global_id, *s.mesh);
472
473 if (boundary_id == surface_selection)
474 {
475 for (long n = 0; n < nodes.size(); ++n)
476 {
477 const int g_id = gbases[e].bases[nodes(n)].global()[0].index;
478
479 if (std::count(node_ids.begin(), node_ids.end(), g_id) == 0)
480 node_ids.push_back(g_id);
481 }
482 }
483 }
484 }
485 }
486
487 void compute_total_surface_node_ids(const State &state, std::vector<int> &node_ids)
488 {
489 auto &s = state;
490
491 node_ids = {};
492
493 const auto &gbases = s.geom_bases();
494 for (const auto &lb : s.total_local_boundary)
495 {
496 const int e = lb.element_id();
497 for (int i = 0; i < lb.size(); ++i)
498 {
499 const int primitive_global_id = lb.global_primitive_id(i);
500 const auto nodes = gbases[e].local_nodes_for_primitive(primitive_global_id, *s.mesh);
501
502 for (long n = 0; n < nodes.size(); ++n)
503 {
504 const int g_id = gbases[e].bases[nodes(n)].global()[0].index;
505
506 if (std::count(node_ids.begin(), node_ids.end(), g_id) == 0)
507 node_ids.push_back(g_id);
508 }
509 }
510 }
511 }
512
513 void compute_volume_node_ids(const State &state, const int volume_selection, std::vector<int> &node_ids)
514 {
515 auto &s = state;
516
517 node_ids = {};
518
519 const auto &gbases = s.geom_bases();
520 for (int e = 0; e < gbases.size(); e++)
521 {
522 const int body_id = s.mesh->get_body_id(e);
523 if (body_id == volume_selection)
524 for (const auto &gbs : gbases[e].bases)
525 for (const auto &g : gbs.global())
526 node_ids.push_back(g.index);
527 }
528 }
529
530} // namespace polyfem
int x
Storage for additional data required by differntial code.
Definition DiffCache.hpp:21
int bdf_order(int step) const
Definition DiffCache.hpp:48
const ipc::TangentialCollisions & friction_collision_set(int step) const
const Eigen::MatrixXd & adjoint_mat() const
Definition DiffCache.hpp:43
Eigen::VectorXd u(int step) const
Definition DiffCache.hpp:64
void cache_adjoints(const Eigen::MatrixXd &adjoint_mat)
const StiffnessMatrix & gradu_h(int step) const
Definition DiffCache.hpp:86
const ipc::TangentialCollisions & tangential_adhesion_collision_set(int step) const
main class that contains the polyfem solver and all its state
Definition State.hpp:113
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
Definition State.hpp:257
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition State.hpp:202
json args
main input arguments containing all defaults
Definition State.hpp:135
static double betas(const int i)
Retrieve the value of beta used for BDF with i steps.
Definition BDF.cpp:35
static const std::vector< double > & alphas(const int i)
Retrieve the alphas used for BDF with i steps.
Definition BDF.cpp:21
list tmp
Definition p_bases.py:365
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
void compute_volume_node_ids(const State &state, const int volume_selection, std::vector< int > &node_ids)
void compute_surface_node_ids(const State &state, const int surface_selection, std::vector< int > &node_ids)
spdlog::logger & adjoint_logger()
Retrieves the current logger for adjoint.
Definition Logger.cpp:30
void log_and_throw_adjoint_error(const std::string &msg)
Definition Logger.cpp:79
Eigen::MatrixXd get_adjoint_mat(const State &state, const DiffCache &diff_cache, int type)
Get adjoint parameter nu or p.
void compute_total_surface_node_ids(const State &state, std::vector< int > &node_ids)
void solve_adjoint_cached(const State &state, DiffCache &diff_cache, const Eigen::MatrixXd &rhs)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24