PolyFEM
Loading...
Searching...
No Matches
L2Projection.cpp
Go to the documentation of this file.
1#include "L2Projection.hpp"
2
10
11#include <ipc/ipc.hpp>
12
13#include <polysolve/linear/Solver.hpp>
14
15namespace polyfem::mesh
16{
18 const Eigen::SparseMatrix<double> &M,
19 const Eigen::SparseMatrix<double> &A,
20 const Eigen::Ref<const Eigen::MatrixXd> &y)
21 {
22 // Construct a linear solver for M
23 std::unique_ptr<polysolve::linear::Solver> solver;
24#ifdef POLYSOLVE_WITH_MKL
25 solver = polysolve::linear::Solver::create("Eigen::PardisoLDLT", "");
26#elif defined(POLYSOLVE_WITH_CHOLMOD)
27 solver = polysolve::linear::Solver::create("Eigen::CholmodSimplicialLDLT", "");
28#else
29 solver = polysolve::linear::Solver::create("Eigen::SimplicialLDLT", "");
30#endif
31
32 solver->analyze_pattern(M, 0);
33 solver->factorize(M);
34
35 const Eigen::MatrixXd rhs = A * y;
36 Eigen::MatrixXd x(rhs.rows(), rhs.cols());
37 for (int i = 0; i < x.cols(); ++i)
38 solver->solve(rhs.col(i), x.col(i));
39
40 double residual_error = (M * x - rhs).norm();
41 logger().debug("residual error in L2 projection: {}", residual_error);
42 assert(residual_error < std::max(1e-12 * rhs.norm(), 1e-12));
43
44 return x;
45 }
46
48 const Eigen::MatrixXd &M,
49 const Eigen::MatrixXd &A,
50 const Eigen::Ref<const Eigen::MatrixXd> &y,
51 const std::vector<int> &boundary_nodes,
52 Eigen::Ref<Eigen::MatrixXd> x)
53 {
54 std::vector<int> free_nodes;
55 for (int i = 0, j = 0; i < y.rows(); ++i)
56 {
57 if (boundary_nodes[j] == i)
58 ++j;
59 else
60 free_nodes.push_back(i);
61 }
62
63 const Eigen::MatrixXd H = M(free_nodes, free_nodes);
64
65 const Eigen::MatrixXd g = -((M * x - A * y)(free_nodes, Eigen::all));
66 const Eigen::MatrixXd sol = H.llt().solve(g);
67 x(free_nodes, Eigen::all) += sol;
68 }
69
71 // Nonlinear solver
72 std::shared_ptr<polysolve::nonlinear::Solver> nl_solver,
73 // L2 projection form
74 const Eigen::SparseMatrix<double> &M,
75 const Eigen::SparseMatrix<double> &A,
76 const Eigen::VectorXd &y,
77 // Inversion-free form
78 const Eigen::MatrixXd &rest_positions,
79 const Eigen::MatrixXi &elements,
80 const int dim,
81 // Contact form
82 const ipc::CollisionMesh &collision_mesh,
83 const double dhat,
84 const double barrier_stiffness,
85 const bool use_convergent_formulation,
86 const ipc::BroadPhaseMethod broad_phase_method,
87 const double ccd_tolerance,
88 const int ccd_max_iterations,
89 // Augmented lagrangian form
90 const std::vector<int> &boundary_nodes,
91 const size_t obstacle_ndof,
92 const Eigen::VectorXd &target_x,
93 // Initial guess
94 const Eigen::VectorXd &x0)
95 {
96 using namespace polyfem::solver;
97
98 assert(M.rows() == M.cols());
99 assert(A.rows() == M.rows());
100 assert(A.cols() == y.size());
101
102 std::vector<std::shared_ptr<Form>> forms;
103
104 forms.push_back(std::make_shared<L2ProjectionForm>(M, A, y));
105
106 forms.push_back(std::make_shared<InversionBarrierForm>(
107 rest_positions, elements, dim, /*vhat=*/1e-12));
108 forms.back()->set_weight(barrier_stiffness); // use same weight as barrier stiffness
109 assert(forms.back()->is_step_valid(x0, x0));
110
111 if (collision_mesh.num_vertices() != 0)
112 {
113 forms.push_back(std::make_shared<ContactForm>(
114 collision_mesh, dhat, /*avg_mass=*/1.0, use_convergent_formulation,
115 /*use_adaptive_barrier_stiffness=*/false, /*is_time_dependent=*/true,
116 /*enable_shape_derivatives=*/false, broad_phase_method, ccd_tolerance,
117 ccd_max_iterations));
118 forms.back()->set_weight(barrier_stiffness);
119 assert(!ipc::has_intersections(collision_mesh, collision_mesh.displace_vertices(utils::unflatten(x0, dim))));
120 }
121
122 const int ndof = x0.size();
123
124 std::shared_ptr<BCLagrangianForm> bc_lagrangian_form = std::make_shared<BCLagrangianForm>(
125 ndof, boundary_nodes, M, obstacle_ndof, target_x);
126 forms.push_back(bc_lagrangian_form);
127
128 // --------------------------------------------------------------------
129
130 std::vector<std::shared_ptr<AugmentedLagrangianForm>> bc_forms;
131 bc_forms.push_back(bc_lagrangian_form);
132
133 StaticBoundaryNLProblem problem(ndof, target_x, forms, bc_forms);
134
135 // --------------------------------------------------------------------
136
137 // Create augmented Lagrangian solver
138 // AL parameters
139 constexpr double al_initial_weight = 1e6;
140 constexpr double al_scaling = 2.0;
141 constexpr int al_max_weight = 100 * al_initial_weight;
142 constexpr double al_eta_tol = 0.99;
143 constexpr size_t al_max_solver_iter = 1000;
144 ALSolver al_solver(
145 bc_forms, al_initial_weight,
146 al_scaling, al_max_weight, al_eta_tol,
147 /*update_barrier_stiffness=*/[&](const Eigen::MatrixXd &x) {});
148
149 Eigen::MatrixXd sol = x0;
150
151 const size_t default_max_iterations = nl_solver->stop_criteria().iterations;
152 nl_solver->stop_criteria().iterations = al_max_solver_iter;
153 al_solver.solve_al(nl_solver, problem, sol);
154
155 nl_solver->stop_criteria().iterations = default_max_iterations;
156 al_solver.solve_reduced(nl_solver, problem, sol);
157
158#ifndef NDEBUG
159 assert(forms[1]->is_step_valid(sol, sol)); // inversion-free
160 if (collision_mesh.num_vertices() != 0)
161 assert(!ipc::has_intersections(collision_mesh, collision_mesh.displace_vertices(utils::unflatten(sol, dim))));
162#endif
163
164 return sol;
165 }
166} // namespace polyfem::mesh
int y
int x
void solve_al(std::shared_ptr< NLSolver > nl_solver, NLProblem &nl_problem, Eigen::MatrixXd &sol)
Definition ALSolver.cpp:23
Eigen::MatrixXd unconstrained_L2_projection(const Eigen::SparseMatrix< double > &M, const Eigen::SparseMatrix< double > &A, const Eigen::Ref< const Eigen::MatrixXd > &y)
Eigen::VectorXd constrained_L2_projection(std::shared_ptr< polysolve::nonlinear::Solver > nl_solver, const Eigen::SparseMatrix< double > &M, const Eigen::SparseMatrix< double > &A, const Eigen::VectorXd &y, const Eigen::MatrixXd &rest_positions, const Eigen::MatrixXi &elements, const int dim, const ipc::CollisionMesh &collision_mesh, const double dhat, const double barrier_stiffness, const bool use_convergent_formulation, const ipc::BroadPhaseMethod broad_phase_method, const double ccd_tolerance, const int ccd_max_iterations, const std::vector< int > &boundary_nodes, const size_t obstacle_ndof, const Eigen::VectorXd &target_x, const Eigen::VectorXd &x0)
void reduced_L2_projection(const Eigen::MatrixXd &M, const Eigen::MatrixXd &A, const Eigen::Ref< const Eigen::MatrixXd > &y, const std::vector< int > &boundary_nodes, Eigen::Ref< Eigen::MatrixXd > x)
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42