Loading [MathJax]/jax/input/TeX/config.js
PolyFEM
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
BarrierContactForm.cpp
Go to the documentation of this file.
2#include <ipc/potentials/barrier_potential.hpp>
8
9#include <ipc/barrier/adaptive_stiffness.hpp>
10#include <ipc/utils/world_bbox_diagonal_length.hpp>
11
12namespace polyfem::solver
13{
14 BarrierContactForm::BarrierContactForm(const ipc::CollisionMesh &collision_mesh,
15 const double dhat,
16 const double avg_mass,
17 const bool use_area_weighting,
18 const bool use_improved_max_operator,
19 const bool use_physical_barrier,
20 const bool use_adaptive_barrier_stiffness,
21 const bool is_time_dependent,
22 const bool enable_shape_derivatives,
23 const ipc::BroadPhaseMethod broad_phase_method,
24 const double ccd_tolerance,
25 const int ccd_max_iterations): ContactForm(collision_mesh, dhat, avg_mass, use_adaptive_barrier_stiffness, is_time_dependent, enable_shape_derivatives, broad_phase_method, ccd_tolerance, ccd_max_iterations), barrier_potential_(dhat, use_physical_barrier)
26 {
27 // collision_set_.set_use_convergent_formulation(use_convergent_formulation);
28 collision_set_.set_use_area_weighting(use_area_weighting);
29 collision_set_.set_use_improved_max_approximator(use_improved_max_operator);
30 collision_set_.set_enable_shape_derivatives(enable_shape_derivatives);
31 }
32 void BarrierContactForm::force_shape_derivative(const ipc::NormalCollisions &collision_set, const Eigen::MatrixXd &solution, const Eigen::VectorXd &adjoint_sol, Eigen::VectorXd &term) const
33 {
34 // Eigen::MatrixXd U = collision_mesh_.vertices(utils::unflatten(solution, collision_mesh_.dim()));
35 // Eigen::MatrixXd X = collision_mesh_.vertices(boundary_nodes_pos_);
36 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(solution);
37
38 StiffnessMatrix dq_h = collision_mesh_.to_full_dof(barrier_potential_.shape_derivative(collision_set, collision_mesh_, displaced_surface));
39 term = barrier_stiffness() * dq_h.transpose() * adjoint_sol;
40 }
41
42 void BarrierContactForm::update_barrier_stiffness(const Eigen::VectorXd &x, const Eigen::MatrixXd &grad_energy)
43 {
45 return;
46
47 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(x);
48
49 // The adative stiffness is designed for the non-convergent formulation,
50 // so we need to compute the gradient of the non-convergent barrier.
51 // After we can map it to a good value for the convergent formulation.
52 ipc::NormalCollisions nonconvergent_constraints;
53 //nonconvergent_constraints.set_use_convergent_formulation(false);
54 nonconvergent_constraints.set_use_area_weighting(false);
55 nonconvergent_constraints.set_use_improved_max_approximator(false);
56 nonconvergent_constraints.build(
57 collision_mesh_, displaced_surface, dhat_, dmin_, broad_phase_);
58 Eigen::VectorXd grad_barrier = barrier_potential_.gradient(
59 nonconvergent_constraints, collision_mesh_, displaced_surface);
60 grad_barrier = collision_mesh_.to_full_dof(grad_barrier);
61
62 barrier_stiffness_ = ipc::initial_barrier_stiffness(
63 ipc::world_bbox_diagonal_length(displaced_surface), barrier_potential_.barrier(), dhat_, avg_mass_,
64 grad_energy, grad_barrier, max_barrier_stiffness_);
65
67 {
68 double scaling_factor = 0;
69 if (!nonconvergent_constraints.empty())
70 {
71 const double nonconvergent_potential = barrier_potential_(
72 nonconvergent_constraints, collision_mesh_, displaced_surface);
73
74 update_collision_set(displaced_surface);
75 const double convergent_potential = barrier_potential_(
76 collision_set_, collision_mesh_, displaced_surface);
77
78 scaling_factor = nonconvergent_potential / convergent_potential;
79 }
80 else
81 {
82 // Hardcoded difference between the non-convergent and convergent barrier
83 scaling_factor = dhat_ * std::pow(dhat_ + 2 * dmin_, 2);
84 }
85 barrier_stiffness_ *= scaling_factor;
86 max_barrier_stiffness_ *= scaling_factor;
87 }
88
89 // The barrier stiffness is choosen based on including the acceleration scaling,
90 // but the acceleration scaling will be applied later. Therefore, we need to remove it.
93
94 logger().debug(
95 "Setting adaptive barrier stiffness to {} (max barrier stiffness: {})",
97 }
98
99 void BarrierContactForm::update_collision_set(const Eigen::MatrixXd &displaced_surface)
100 {
101 // Store the previous value used to compute the constraint set to avoid duplicate computation.
102 static Eigen::MatrixXd cached_displaced_surface;
103 if (cached_displaced_surface.size() == displaced_surface.size() && cached_displaced_surface == displaced_surface)
104 return;
105
107 collision_set_.build(
108 candidates_, collision_mesh_, displaced_surface, dhat_);
109 else
110 collision_set_.build(
111 collision_mesh_, displaced_surface, dhat_, dmin_, broad_phase_);
112 cached_displaced_surface = displaced_surface;
113 }
114
119
120 Eigen::VectorXd BarrierContactForm::value_per_element_unweighted(const Eigen::VectorXd &x) const
121 {
122 const Eigen::MatrixXd V = compute_displaced_surface(x);
123 assert(V.rows() == collision_mesh_.num_vertices());
124
125 const size_t num_vertices = collision_mesh_.num_vertices();
126
127 if (collision_set_.empty())
128 {
129 return Eigen::VectorXd::Zero(collision_mesh_.full_num_vertices());
130 }
131
132 const Eigen::MatrixXi &E = collision_mesh_.edges();
133 const Eigen::MatrixXi &F = collision_mesh_.faces();
134
135 auto storage = utils::create_thread_storage<Eigen::VectorXd>(Eigen::VectorXd::Zero(num_vertices));
136
137 utils::maybe_parallel_for(collision_set_.size(), [&](int start, int end, int thread_id) {
138 Eigen::VectorXd &local_storage = utils::get_local_thread_storage(storage, thread_id);
139
140 for (size_t i = start; i < end; i++)
141 {
142 // Quadrature weight is premultiplied by compute_potential
143 const double potential = barrier_potential_(collision_set_[i], collision_set_[i].dof(V, E, F));
144
145 const int n_v = collision_set_[i].num_vertices();
146 const auto vis = collision_set_[i].vertex_ids(E, F);
147 for (int j = 0; j < n_v; j++)
148 {
149 assert(0 <= vis[j] && vis[j] < num_vertices);
150 local_storage[vis[j]] += potential / n_v;
151 }
152 }
153 });
154
155 Eigen::VectorXd out = Eigen::VectorXd::Zero(num_vertices);
156 for (const auto &local_potential : storage)
157 {
158 out += local_potential;
159 }
160
161 Eigen::VectorXd out_full = Eigen::VectorXd::Zero(collision_mesh_.full_num_vertices());
162 for (int i = 0; i < out.size(); i++)
163 out_full[collision_mesh_.to_full_vertex_id(i)] = out[i];
164
165 assert(std::abs(value_unweighted(x) - out_full.sum()) < std::max(1e-10 * out_full.sum(), 1e-10));
166
167 return out_full;
168 }
169
170 void BarrierContactForm::first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
171 {
173 gradv = collision_mesh_.to_full_dof(gradv);
174 }
175
176 void BarrierContactForm::second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const
177 {
178 POLYFEM_SCOPED_TIMER("barrier hessian");
179
180 ipc::PSDProjectionMethod psd_projection_method;
181
182 if (project_to_psd_) {
183 psd_projection_method = ipc::PSDProjectionMethod::CLAMP;
184 } else {
185 psd_projection_method = ipc::PSDProjectionMethod::NONE;
186 }
187
188 hessian = barrier_potential_.hessian(collision_set_, collision_mesh_, compute_displaced_surface(x), psd_projection_method);
189 hessian = collision_mesh_.to_full_dof(hessian);
190 }
191
192 void BarrierContactForm::post_step(const polysolve::nonlinear::PostStepData &data)
193 {
194 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(data.x);
195
196 const double curr_distance = collision_set_.compute_minimum_distance(collision_mesh_, displaced_surface);
197 if (!std::isinf(curr_distance))
198 {
199 const double ratio = sqrt(curr_distance) / dhat();
200 const auto log_level = (ratio < 1e-6) ? spdlog::level::err : ((ratio < 1e-4) ? spdlog::level::warn : spdlog::level::debug);
201 polyfem::logger().log(log_level, "Minimum distance during solve: {}, dhat: {}", sqrt(curr_distance), dhat());
202 }
203
204 if (data.iter_num == 0)
205 return;
206
208 {
210 {
211 const double prev_barrier_stiffness = barrier_stiffness();
212
213 barrier_stiffness_ = ipc::update_barrier_stiffness(
215 barrier_stiffness(), ipc::world_bbox_diagonal_length(displaced_surface));
216
217 if (barrier_stiffness() != prev_barrier_stiffness)
218 {
219 polyfem::logger().debug(
220 "updated barrier stiffness from {:g} to {:g} (max barrier stiffness: )",
221 prev_barrier_stiffness, barrier_stiffness(), max_barrier_stiffness_);
222 }
223 }
224 else
225 {
226 // TODO: missing feature
227 // update_barrier_stiffness(data.x);
228 }
229 }
230
231 prev_distance_ = curr_distance;
232 }
233}
int V
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
virtual void first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
Compute the first derivative of the value wrt x.
bool use_improved_max_operator() const
Get use_improved_max_operator.
virtual void second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const override
Compute the second derivative of the value wrt x.
BarrierContactForm(const ipc::CollisionMesh &collision_mesh, const double dhat, const double avg_mass, const bool use_area_weighting, const bool use_improved_max_operator, const bool use_physical_barrier, const bool use_adaptive_barrier_stiffness, const bool is_time_dependent, const bool enable_shape_derivatives, const ipc::BroadPhaseMethod broad_phase_method, const double ccd_tolerance, const int ccd_max_iterations)
void force_shape_derivative(const ipc::NormalCollisions &collision_set, const Eigen::MatrixXd &solution, const Eigen::VectorXd &adjoint_sol, Eigen::VectorXd &term) const
bool use_convergent_formulation() const override
Get use_convergent_formulation.
bool use_area_weighting() const
Get use_area_weighting.
virtual double value_unweighted(const Eigen::VectorXd &x) const override
Compute the contact barrier potential value.
Eigen::VectorXd value_per_element_unweighted(const Eigen::VectorXd &x) const override
Compute the value of the form multiplied per element.
const ipc::NormalCollisions & collision_set() const
virtual void update_barrier_stiffness(const Eigen::VectorXd &x, const Eigen::MatrixXd &grad_energy) override
Update the barrier stiffness based on the current elasticity energy.
void update_collision_set(const Eigen::MatrixXd &displaced_surface) override
Update the cached candidate set for the current solution.
const ipc::BarrierPotential barrier_potential_
Contact potential.
ipc::NormalCollisions collision_set_
Cached constraint set for the current solution.
void post_step(const polysolve::nonlinear::PostStepData &data) override
Update fields after a step in the optimization.
Form representing the contact potential and forces.
const double avg_mass_
Average mass of the mesh (used for adaptive barrier stiffness)
const bool use_adaptive_barrier_stiffness_
If true, use an adaptive barrier stiffness.
const double dmin_
Minimum distance between elements.
bool use_cached_candidates_
If true, use the cached candidate set for the current solution.
Eigen::MatrixXd compute_displaced_surface(const Eigen::VectorXd &x) const
Compute the displaced positions of the surface nodes.
double barrier_stiffness() const
Get the current barrier stiffness.
double barrier_stiffness_
Barrier stiffness.
double max_barrier_stiffness_
Maximum barrier stiffness to use when using adaptive barrier stiffness.
double prev_distance_
Previous minimum distance between all elements.
const ipc::CollisionMesh & collision_mesh_
Collision mesh.
bool use_adaptive_barrier_stiffness() const
Get use_adaptive_barrier_stiffness.
ipc::Candidates candidates_
Cached candidate set for the current solution.
const double dhat_
Barrier activation distance.
const std::shared_ptr< ipc::BroadPhase > broad_phase_
const bool is_time_dependent_
Is the simulation time dependent?
double weight_
weight of the form (e.g., AL penalty weight or Δt²)
Definition Form.hpp:149
bool project_to_psd_
If true, the form's second derivative is projected to be positive semidefinite.
Definition Form.hpp:147
void maybe_parallel_for(int size, const std::function< void(int, int, int)> &partial_for)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:22