Loading [MathJax]/extensions/tex2jax.js
PolyFEM
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
BCLagrangianForm.cpp
Go to the documentation of this file.
2
4#include <igl/slice.h>
5
6namespace polyfem::solver
7{
9 const std::vector<int> &boundary_nodes,
10 const std::vector<mesh::LocalBoundary> &local_boundary,
11 const std::vector<mesh::LocalBoundary> &local_neumann_boundary,
12 const int n_boundary_samples,
13 const StiffnessMatrix &mass,
14 const assembler::RhsAssembler &rhs_assembler,
15 const size_t obstacle_ndof,
16 const bool is_time_dependent,
17 const double t)
18 : boundary_nodes_(boundary_nodes),
19 local_boundary_(&local_boundary),
20 local_neumann_boundary_(&local_neumann_boundary),
21 n_boundary_samples_(n_boundary_samples),
22 rhs_assembler_(&rhs_assembler),
23 is_time_dependent_(is_time_dependent),
24 n_dofs_(ndof)
25 {
26 init_masked_lumped_mass(mass, obstacle_ndof);
27 update_target(t); // initialize b_
28 }
29
31 const std::vector<int> &boundary_nodes,
32 const StiffnessMatrix &mass,
33 const size_t obstacle_ndof,
34 const Eigen::MatrixXd &target_x)
35 : boundary_nodes_(boundary_nodes),
36 local_boundary_(nullptr),
37 local_neumann_boundary_(nullptr),
38 n_boundary_samples_(0),
39 rhs_assembler_(nullptr),
40 is_time_dependent_(false),
41 n_dofs_(ndof)
42 {
43 init_masked_lumped_mass(mass, obstacle_ndof);
44
45 b_ = target_x;
46 b_proj_ = b_;
47 igl::slice(b_, constraints_, 1, b_);
48 }
49
51 const StiffnessMatrix &mass,
52 const size_t obstacle_ndof)
53 {
54 std::vector<Eigen::Triplet<double>> A_triplets;
55
56 constraints_.resize(boundary_nodes_.size());
57 for (int i = 0; i < boundary_nodes_.size(); ++i)
58 {
59 const int bn = boundary_nodes_[i];
60
61 constraints_[i] = bn;
62 A_triplets.emplace_back(i, bn, 1.0);
63 }
64 A_.resize(boundary_nodes_.size(), n_dofs_);
65 A_.setFromTriplets(A_triplets.begin(), A_triplets.end());
66 A_.makeCompressed();
67
69 {
70 double min_diag = std::numeric_limits<double>::max();
71 double max_diag = 0;
72 for (int k = 0; k < masked_lumped_mass_.outerSize(); ++k)
73 {
74 for (StiffnessMatrix::InnerIterator it(masked_lumped_mass_, k); it; ++it)
75 {
76 if (it.col() == it.row())
77 {
78 min_diag = std::min(min_diag, it.value());
79 max_diag = std::max(max_diag, it.value());
80 }
81 }
82 }
83 if (max_diag <= 0 || min_diag <= 0 || min_diag / max_diag < 1e-16)
84 {
85 logger().warn("Lumped mass matrix ill-conditioned. Setting lumped mass matrix to identity.");
87 }
88 }
89
90 assert(n_dofs_ == masked_lumped_mass_.rows() && n_dofs_ == masked_lumped_mass_.cols());
91 // Give the collision obstacles a entry in the lumped mass matrix
92 if (obstacle_ndof != 0)
93 {
94 const int n_fe_dof = n_dofs_ - obstacle_ndof;
95 const double avg_mass = masked_lumped_mass_.diagonal().head(n_fe_dof).mean();
96 for (int i = n_fe_dof; i < n_dofs_; ++i)
97 {
98 masked_lumped_mass_.coeffRef(i, i) = avg_mass;
99 }
100 }
101
104 assert(boundary_nodes_.size() == masked_lumped_mass_.rows() && boundary_nodes_.size() == masked_lumped_mass_.cols());
105
107 std::vector<Eigen::Triplet<double>> tmp_triplets;
108 tmp_triplets.reserve(masked_lumped_mass_.nonZeros());
109 for (int k = 0; k < masked_lumped_mass_.outerSize(); ++k)
110 {
111 for (StiffnessMatrix::InnerIterator it(masked_lumped_mass_, k); it; ++it)
112 {
113 assert(it.col() == k);
114 tmp_triplets.emplace_back(it.row(), it.col(), sqrt(it.value()));
115 }
116 }
117
118 masked_lumped_mass_sqrt_.setFromTriplets(tmp_triplets.begin(), tmp_triplets.end());
119 masked_lumped_mass_sqrt_.makeCompressed();
120
121 std::vector<bool> is_contraints(n_dofs_, false);
122 for (int i = 0; i < boundary_nodes_.size(); ++i)
123 is_contraints[boundary_nodes_[i]] = true;
124
125 int index = 0;
126 A_triplets.clear();
128 for (int i = 0; i < n_dofs_; ++i)
129 {
130 if (is_contraints[i])
131 continue;
132
133 A_triplets.emplace_back(i, index, 1.0);
134 not_constraints_[index] = i;
135 index++;
136 }
137 A_proj_.resize(n_dofs_, index);
138 A_proj_.setFromTriplets(A_triplets.begin(), A_triplets.end());
139 A_proj_.makeCompressed();
140
141 lagr_mults_.resize(boundary_nodes_.size());
142 lagr_mults_.setZero();
143 }
144
145 bool BCLagrangianForm::can_project() const { return true; }
146
147 void BCLagrangianForm::project_gradient(Eigen::VectorXd &grad) const
148 {
149 // Assumes not_constraints_ is sorted
150 for (int i = 0; i < not_constraints_.size(); ++i)
151 grad[i] = grad[not_constraints_[i]];
152 grad.conservativeResize(not_constraints_.size());
153 }
154
156 {
157 igl::slice(hessian, not_constraints_, 1, hessian);
158 igl::slice(hessian, not_constraints_, 2, hessian);
159 }
160
161 double BCLagrangianForm::value_unweighted(const Eigen::VectorXd &x) const
162 {
163 const Eigen::VectorXd dist = A_ * x - b_;
164 const double L_penalty = -lagr_mults_.transpose() * masked_lumped_mass_sqrt_ * dist;
165 const double A_penalty = 0.5 * dist.transpose() * masked_lumped_mass_ * dist;
166
167 return L_weight() * L_penalty + A_weight() * A_penalty;
168 }
169
170 void BCLagrangianForm::first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
171 {
172 gradv = L_weight() * A_.transpose() * (-(masked_lumped_mass_sqrt_ * lagr_mults_) + A_weight() * (masked_lumped_mass_ * (A_ * x - b_)));
173 }
174
175 void BCLagrangianForm::second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const
176 {
177 hessian = A_weight() * A_.transpose() * masked_lumped_mass_ * A_;
178 }
179
180 void BCLagrangianForm::update_quantities(const double t, const Eigen::VectorXd &)
181 {
183 update_target(t);
184 }
185
186 double BCLagrangianForm::compute_error(const Eigen::VectorXd &x) const
187 {
188 // return (b_ - x).transpose() * A_ * (b_ - x);
189 const Eigen::VectorXd res = A_ * x - b_;
190 return res.squaredNorm();
191 }
192
194 {
195 assert(rhs_assembler_ != nullptr);
196 assert(local_boundary_ != nullptr);
197 assert(local_neumann_boundary_ != nullptr);
198 b_.setZero(n_dofs_, 1);
201 *local_neumann_boundary_, b_, Eigen::MatrixXd(), t);
202 b_proj_ = b_;
203 b_ = igl::slice(b_, constraints_, 1);
204 }
205
206 void BCLagrangianForm::update_lagrangian(const Eigen::VectorXd &x, const double k_al)
207 {
208 k_al_ = k_al;
209 lagr_mults_ -= k_al * masked_lumped_mass_sqrt_ * (A_ * x - b_);
210 }
211} // namespace polyfem::solver
int x
void set_bc(const std::vector< mesh::LocalBoundary > &local_boundary, const std::vector< int > &bounday_nodes, const int resolution, const std::vector< mesh::LocalBoundary > &local_neumann_boundary, Eigen::MatrixXd &rhs, const Eigen::MatrixXd &displacement=Eigen::MatrixXd(), const double t=1) const
Eigen::VectorXd lagr_mults_
vector of lagrange multipliers
StiffnessMatrix A_proj_
Constraints projection matrix.
Eigen::MatrixXd b_proj_
Constraints projection value.
Eigen::VectorXi not_constraints_
Not Constraints.
void first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
Compute the first derivative of the value wrt x.
void update_quantities(const double t, const Eigen::VectorXd &x) override
Update time dependent quantities.
void update_target(const double t)
Update target x to the Dirichlet boundary values at time t.
virtual bool can_project() const override
void update_lagrangian(const Eigen::VectorXd &x, const double k_al) override
double compute_error(const Eigen::VectorXd &x) const override
const assembler::RhsAssembler * rhs_assembler_
Reference to the RHS assembler.
void second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const override
Compute the second derivative of the value wrt x.
virtual void project_gradient(Eigen::VectorXd &grad) const override
void init_masked_lumped_mass(const StiffnessMatrix &mass, const size_t obstacle_ndof)
Initialize the masked lumped mass matrix.
BCLagrangianForm(const int ndof, 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 StiffnessMatrix &mass, const assembler::RhsAssembler &rhs_assembler, const size_t obstacle_ndof, const bool is_time_dependent, const double t)
Construct a new BCLagrangianForm object with a time dependent Dirichlet boundary.
const std::vector< mesh::LocalBoundary > * local_neumann_boundary_
const std::vector< mesh::LocalBoundary > * local_boundary_
StiffnessMatrix masked_lumped_mass_sqrt_
sqrt mass matrix masked by the AL dofs
double value_unweighted(const Eigen::VectorXd &x) const override
Compute the value of the form.
virtual void project_hessian(StiffnessMatrix &hessian) const override
StiffnessMatrix masked_lumped_mass_
mass matrix masked by the AL dofs
const std::vector< int > & boundary_nodes_
Eigen::VectorXi constraints_
Constraints.
Eigen::SparseMatrix< double > lump_matrix(const Eigen::SparseMatrix< double > &M)
Lump each row of a matrix into the diagonal.
Eigen::SparseMatrix< double > sparse_identity(int rows, int cols)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:22