PolyFEM
Loading...
Searching...
No Matches
BCLagrangianForm.cpp
Go to the documentation of this file.
2
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 const std::shared_ptr<utils::PeriodicBoundary> &periodic_bc)
19 : AugmentedLagrangianForm(periodic_bc ? periodic_bc->full_to_periodic(boundary_nodes) : boundary_nodes),
20 boundary_nodes_(boundary_nodes),
21 local_boundary_(&local_boundary),
22 local_neumann_boundary_(&local_neumann_boundary),
23 n_boundary_samples_(n_boundary_samples),
24 rhs_assembler_(&rhs_assembler),
25 is_time_dependent_(is_time_dependent)
26 {
27 init_masked_lumped_mass(ndof, mass, obstacle_ndof);
28 update_target(t); // initialize target_x_
29 }
30
32 const std::vector<int> &boundary_nodes,
33 const StiffnessMatrix &mass,
34 const size_t obstacle_ndof,
35 const Eigen::MatrixXd &target_x)
36 : AugmentedLagrangianForm(boundary_nodes),
37 boundary_nodes_(boundary_nodes),
38 local_boundary_(nullptr),
39 local_neumann_boundary_(nullptr),
40 n_boundary_samples_(0),
41 rhs_assembler_(nullptr),
42 is_time_dependent_(false),
43 target_x_(target_x)
44 {
45 init_masked_lumped_mass(ndof, mass, obstacle_ndof);
46 }
47
49 const int ndof,
50 const StiffnessMatrix &mass,
51 const size_t obstacle_ndof)
52 {
53 std::vector<bool> is_boundary_dof(ndof, true);
54 for (const auto bn : boundary_nodes_)
55 is_boundary_dof[bn] = false;
56
58 {
59 double min_diag = std::numeric_limits<double>::max();
60 double max_diag = 0;
61 for (int k = 0; k < masked_lumped_mass_sqrt_.outerSize(); ++k)
62 {
63 for (StiffnessMatrix::InnerIterator it(masked_lumped_mass_sqrt_, k); it; ++it)
64 {
65 if (it.col() == it.row())
66 {
67 min_diag = std::min(min_diag, it.value());
68 max_diag = std::max(max_diag, it.value());
69 }
70 }
71 }
72 if (max_diag <= 0 || min_diag <= 0 || min_diag / max_diag < 1e-16)
73 {
74 logger().warn("Lumped mass matrix ill-conditioned. Setting lumped mass matrix to identity.");
76 }
77 }
78
79 assert(ndof == masked_lumped_mass_sqrt_.rows() && ndof == masked_lumped_mass_sqrt_.cols());
80
81 // Give the collision obstacles a entry in the lumped mass matrix
82 if (obstacle_ndof != 0)
83 {
84 const int n_fe_dof = ndof - obstacle_ndof;
85 const double avg_mass = masked_lumped_mass_sqrt_.diagonal().head(n_fe_dof).mean();
86 for (int i = n_fe_dof; i < ndof; ++i)
87 {
88 masked_lumped_mass_sqrt_.coeffRef(i, i) = avg_mass;
89 }
90 }
91
92 // Remove non-boundary ndof from mass matrix
93 masked_lumped_mass_sqrt_.prune([&](const int &row, const int &col, const double &value) -> bool {
94 assert(row == col); // matrix should be diagonal
95 return !is_boundary_dof[row];
96 });
98 mask_.resize(masked_lumped_mass_.rows(), masked_lumped_mass_.cols());
99 mask_.setIdentity();
100 mask_.prune([&](const int &row, const int &col, const double &value) -> bool {
101 assert(row == col); // matrix should be diagonal
102 return !is_boundary_dof[row];
103 });
104
105 std::vector<Eigen::Triplet<double>> tmp_triplets;
106 tmp_triplets.reserve(masked_lumped_mass_sqrt_.nonZeros());
107 for (int k = 0; k < masked_lumped_mass_sqrt_.outerSize(); ++k)
108 {
109 for (StiffnessMatrix::InnerIterator it(masked_lumped_mass_sqrt_, k); it; ++it)
110 {
111 assert(it.col() == k);
112 tmp_triplets.emplace_back(it.row(), it.col(), sqrt(it.value()));
113 }
114 }
115
116 masked_lumped_mass_sqrt_.setFromTriplets(tmp_triplets.begin(), tmp_triplets.end());
117 masked_lumped_mass_sqrt_.makeCompressed();
118
119 lagr_mults_.resize(ndof);
120 lagr_mults_.setZero();
121 }
122
123 double BCLagrangianForm::value_unweighted(const Eigen::VectorXd &x) const
124 {
125 const Eigen::VectorXd dist = x - target_x_;
126 const double L_penalty = -lagr_mults_.transpose() * masked_lumped_mass_sqrt_ * dist;
127 const double A_penalty = 0.5 * dist.transpose() * masked_lumped_mass_ * dist;
128
129 return L_penalty + k_al_ * A_penalty;
130 }
131
132 void BCLagrangianForm::first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
133 {
135 }
136
137 void BCLagrangianForm::second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const
138 {
139 hessian = k_al_ * masked_lumped_mass_;
140 }
141
142 void BCLagrangianForm::update_quantities(const double t, const Eigen::VectorXd &)
143 {
145 update_target(t);
146 }
147
148 double BCLagrangianForm::compute_error(const Eigen::VectorXd &x) const
149 {
150 return (this->target(x) - x).transpose() * this->mask() * (this->target(x) - x);
151 }
152
154 {
155 assert(rhs_assembler_ != nullptr);
156 assert(local_boundary_ != nullptr);
157 assert(local_neumann_boundary_ != nullptr);
158 target_x_.setZero(masked_lumped_mass_sqrt_.rows(), 1);
161 *local_neumann_boundary_, target_x_, Eigen::MatrixXd(), t);
162 }
163
164 void BCLagrangianForm::update_lagrangian(const Eigen::VectorXd &x, const double k_al)
165 {
166 k_al_ = k_al;
168 }
169} // 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
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.
Eigen::VectorXd target(const Eigen::VectorXd &) const override
void update_target(const double t)
Update target x to the Dirichlet boundary values at time t.
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.
StiffnessMatrix mask_
identity matrix masked by the AL dofs
Eigen::MatrixXd target_x_
actually a vector with the same size as x with target nodal positions
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.
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, const std::shared_ptr< utils::PeriodicBoundary > &periodic_bc=nullptr)
Construct a new BCLagrangianForm object with a time dependent Dirichlet boundary.
void init_masked_lumped_mass(const int ndof, const StiffnessMatrix &mass, const size_t obstacle_ndof)
Initialize the masked lumped mass matrix.
StiffnessMatrix masked_lumped_mass_
mass matrix masked by the AL dofs
const std::vector< int > & boundary_nodes_
virtual double value(const Eigen::VectorXd &x) const
Compute the value of the form multiplied with the weigth.
Definition Form.hpp:26
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:42
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:22