PolyFEM
Loading...
Searching...
No Matches
BarrierContactForm.cpp
Go to the documentation of this file.
2
7
8#include <ipc/barrier/adaptive_stiffness.hpp>
9#include <ipc/utils/world_bbox_diagonal_length.hpp>
10
11#include <algorithm>
12#include <cassert>
13#include <cmath>
14
15namespace polyfem::solver
16{
17 BarrierContactForm::BarrierContactForm(const ipc::CollisionMesh &collision_mesh,
18 const double dhat,
19 const double avg_mass,
20 const bool use_area_weighting,
21 const bool use_improved_max_operator,
22 const bool use_physical_barrier,
23 const bool use_adaptive_barrier_stiffness,
24 const bool is_time_dependent,
25 const bool enable_shape_derivatives,
26 const ipc::BroadPhaseMethod broad_phase_method,
27 const double ccd_tolerance,
28 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)
29 {
30 // collision_set_.set_use_convergent_formulation(use_convergent_formulation);
31 collision_set_.set_use_area_weighting(use_area_weighting);
32 collision_set_.set_use_improved_max_approximator(use_improved_max_operator);
33 collision_set_.set_enable_shape_derivatives(enable_shape_derivatives);
34 }
35
36 void BarrierContactForm::update_barrier_stiffness(const Eigen::VectorXd &x, const Eigen::MatrixXd &grad_energy)
37 {
39 return;
40
41 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(x);
42
43 // The adative stiffness is designed for the non-convergent formulation,
44 // so we need to compute the gradient of the non-convergent barrier.
45 // After we can map it to a good value for the convergent formulation.
46 ipc::NormalCollisions nonconvergent_constraints;
47 // nonconvergent_constraints.set_use_convergent_formulation(false);
48 nonconvergent_constraints.set_use_area_weighting(false);
49 nonconvergent_constraints.set_use_improved_max_approximator(false);
50 nonconvergent_constraints.build(
51 collision_mesh_, displaced_surface, dhat_, dmin_, broad_phase_);
52 Eigen::VectorXd grad_barrier = barrier_potential_.gradient(
53 nonconvergent_constraints, collision_mesh_, displaced_surface);
54 grad_barrier = collision_mesh_.to_full_dof(grad_barrier);
55
56 barrier_stiffness_ = ipc::initial_barrier_stiffness(
57 ipc::world_bbox_diagonal_length(displaced_surface), barrier_potential_.barrier(), dhat_, avg_mass_,
58 grad_energy, grad_barrier, max_barrier_stiffness_);
59
61 {
62 double scaling_factor = 0;
63 if (!nonconvergent_constraints.empty())
64 {
65 const double nonconvergent_potential = barrier_potential_(
66 nonconvergent_constraints, collision_mesh_, displaced_surface);
67
68 update_collision_set(displaced_surface);
69 const double convergent_potential = barrier_potential_(
70 collision_set_, collision_mesh_, displaced_surface);
71
72 scaling_factor = nonconvergent_potential / convergent_potential;
73 }
74 else
75 {
76 // Hardcoded difference between the non-convergent and convergent barrier
77 scaling_factor = dhat_ * std::pow(dhat_ + 2 * dmin_, 2);
78 }
79 barrier_stiffness_ *= scaling_factor;
80 max_barrier_stiffness_ *= scaling_factor;
81 }
82
83 // The barrier stiffness is choosen based on including the acceleration scaling,
84 // but the acceleration scaling will be applied later. Therefore, we need to remove it.
87
88 logger().debug(
89 "Setting adaptive barrier stiffness to {} (max barrier stiffness: {})",
91 }
92
93 void BarrierContactForm::update_collision_set(const Eigen::MatrixXd &displaced_surface)
94 {
95 // Store the previous value used to compute the constraint set to avoid duplicate computation.
96 static Eigen::MatrixXd cached_displaced_surface;
97 if (cached_displaced_surface.size() == displaced_surface.size() && cached_displaced_surface == displaced_surface)
98 return;
99
101 collision_set_.build(
102 candidates_, collision_mesh_, displaced_surface, dhat_);
103 else
104 collision_set_.build(
105 collision_mesh_, displaced_surface, dhat_, dmin_, broad_phase_);
106 cached_displaced_surface = displaced_surface;
107 }
108
113
114 Eigen::VectorXd BarrierContactForm::value_per_element_unweighted(const Eigen::VectorXd &x) const
115 {
116 const Eigen::MatrixXd V = compute_displaced_surface(x);
117 assert(V.rows() == collision_mesh_.num_vertices());
118
119 const size_t num_vertices = collision_mesh_.num_vertices();
120
121 if (collision_set_.empty())
122 {
123 return Eigen::VectorXd::Zero(collision_mesh_.full_num_vertices());
124 }
125
126 const Eigen::MatrixXi &E = collision_mesh_.edges();
127 const Eigen::MatrixXi &F = collision_mesh_.faces();
128
129 auto storage = utils::create_thread_storage<Eigen::VectorXd>(Eigen::VectorXd::Zero(num_vertices));
130
131 utils::maybe_parallel_for(collision_set_.size(), [&](int start, int end, int thread_id) {
132 Eigen::VectorXd &local_storage = utils::get_local_thread_storage(storage, thread_id);
133
134 for (size_t i = start; i < end; i++)
135 {
136 // Quadrature weight is premultiplied by compute_potential
137 const double potential = barrier_potential_(collision_set_[i], collision_set_[i].dof(V, E, F));
138
139 const int n_v = collision_set_[i].num_vertices();
140 const auto vis = collision_set_[i].vertex_ids(E, F);
141 for (int j = 0; j < n_v; j++)
142 {
143 assert(0 <= vis[j] && vis[j] < num_vertices);
144 local_storage[vis[j]] += potential / n_v;
145 }
146 }
147 });
148
149 Eigen::VectorXd out = Eigen::VectorXd::Zero(num_vertices);
150 for (const auto &local_potential : storage)
151 {
152 out += local_potential;
153 }
154
155 Eigen::VectorXd out_full = Eigen::VectorXd::Zero(collision_mesh_.full_num_vertices());
156 for (int i = 0; i < out.size(); i++)
157 out_full[collision_mesh_.to_full_vertex_id(i)] = out[i];
158
159 assert(std::abs(value_unweighted(x) - out_full.sum()) < std::max(1e-10 * out_full.sum(), 1e-10));
160
161 return out_full;
162 }
163
164 void BarrierContactForm::first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
165 {
167 gradv = collision_mesh_.to_full_dof(gradv);
168 }
169
170 void BarrierContactForm::second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const
171 {
172 POLYFEM_SCOPED_TIMER("barrier hessian");
173
174 ipc::PSDProjectionMethod psd_projection_method;
175
176 if (project_to_psd_)
177 {
178 psd_projection_method = ipc::PSDProjectionMethod::CLAMP;
179 }
180 else
181 {
182 psd_projection_method = ipc::PSDProjectionMethod::NONE;
183 }
184
185 hessian = barrier_potential_.hessian(collision_set_, collision_mesh_, compute_displaced_surface(x), psd_projection_method);
186 hessian = collision_mesh_.to_full_dof(hessian);
187 }
188
189 void BarrierContactForm::post_step(const polysolve::nonlinear::PostStepData &data)
190 {
191 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(data.x);
192
193 const double curr_distance = collision_set_.compute_minimum_distance(collision_mesh_, displaced_surface);
194 if (!std::isinf(curr_distance))
195 {
196 const double ratio = sqrt(curr_distance) / dhat();
197 const auto log_level = (ratio < 1e-6) ? spdlog::level::err : ((ratio < 1e-4) ? spdlog::level::warn : spdlog::level::debug);
198 polyfem::logger().log(log_level, "Minimum distance during solve: {}, dhat: {}", sqrt(curr_distance), dhat());
199 }
200
201 if (data.iter_num == 0)
202 return;
203
205 {
207 {
208 const double prev_barrier_stiffness = barrier_stiffness();
209
210 barrier_stiffness_ = ipc::update_barrier_stiffness(
212 barrier_stiffness(), ipc::world_bbox_diagonal_length(displaced_surface));
213
214 if (barrier_stiffness() != prev_barrier_stiffness)
215 {
216 polyfem::logger().debug(
217 "updated barrier stiffness from {:g} to {:g} (max barrier stiffness: )",
218 prev_barrier_stiffness, barrier_stiffness(), max_barrier_stiffness_);
219 }
220 }
221 else
222 {
223 // TODO: missing feature
224 // update_barrier_stiffness(data.x);
225 }
226 }
227
228 prev_distance_ = curr_distance;
229 }
230} // namespace polyfem::solver
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)
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.
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:24