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,
29 const double dhat_epsilon_scale,
30 const ipc::NormalCollisions::CollisionSetType collision_set_type,
31 const bool skip_obstacles,
32 std::shared_ptr<ipc::Barrier> barrier) : ContactForm(collision_mesh, dhat, avg_mass, use_adaptive_barrier_stiffness, is_time_dependent, enable_shape_derivatives, broad_phase_method, ccd_tolerance, ccd_max_iterations, dhat_epsilon_scale),
33 barrier_potential_(barrier
34 ? ipc::BarrierPotential(barrier, dhat, /*stiffness=*/1.0, /*use_physical_barrier=*/false, /*use_squared_distance=*/false)
35 : ipc::BarrierPotential(dhat, /*stiffness=*/1.0, use_physical_barrier))
36 {
37 // collision_set_.set_use_convergent_formulation(use_convergent_formulation);
38 collision_set_.set_use_area_weighting(use_area_weighting);
39 collision_set_.set_enable_shape_derivatives(enable_shape_derivatives);
40
41 // collision_set_type and use_improved_max_operator both write the
42 // collision set type, so only one of them may be applied. The explicit
43 // collision_set_type wins when it asks for something other than the
44 // default IPC; otherwise use_improved_max_operator decides, which is
45 // what "use_convergent_formulation" drives.
46 if (collision_set_type != ipc::NormalCollisions::CollisionSetType::IPC)
47 collision_set_.set_collision_set_type(collision_set_type);
48 else
49 collision_set_.set_use_improved_max_approximator(use_improved_max_operator);
50
51 collision_set_.set_skip_obstacles(skip_obstacles);
52 }
53
54 void BarrierContactForm::update_barrier_stiffness(const Eigen::VectorXd &x, const Eigen::MatrixXd &grad_energy)
55 {
57 return;
58
59 if (!barrier_potential_.use_squared_distance())
60 log_and_throw_error("Adaptive barrier stiffness is not supported for the selected barrier.");
61
62 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(x);
63
64 // The adative stiffness is designed for the non-convergent formulation,
65 // so we need to compute the gradient of the non-convergent barrier.
66 // After we can map it to a good value for the convergent formulation.
67 ipc::NormalCollisions nonconvergent_constraints;
68 // nonconvergent_constraints.set_use_convergent_formulation(false);
69 nonconvergent_constraints.set_use_area_weighting(false);
70 nonconvergent_constraints.set_use_improved_max_approximator(false);
71 nonconvergent_constraints.build(
72 collision_mesh_, displaced_surface, dhat_, dmin_, broad_phase_.get());
73 Eigen::VectorXd grad_barrier = barrier_potential_.gradient(
74 nonconvergent_constraints, collision_mesh_, displaced_surface);
75 grad_barrier = collision_mesh_.to_full_dof(grad_barrier);
76
77 barrier_stiffness_ = ipc::initial_barrier_stiffness(
78 ipc::world_bbox_diagonal_length(displaced_surface), barrier_potential_.barrier(), dhat_, avg_mass_,
79 grad_energy, grad_barrier, max_barrier_stiffness_);
80
82 {
83 double scaling_factor = 0;
84 if (!nonconvergent_constraints.empty())
85 {
86 const double nonconvergent_potential = barrier_potential_(
87 nonconvergent_constraints, collision_mesh_, displaced_surface);
88
89 update_collision_set(displaced_surface);
90 const double convergent_potential = barrier_potential_(
91 collision_set_, collision_mesh_, displaced_surface);
92
93 scaling_factor = nonconvergent_potential / convergent_potential;
94 }
95 else
96 {
97 // Hardcoded difference between the non-convergent and convergent barrier
98 scaling_factor = dhat_ * std::pow(dhat_ + 2 * dmin_, 2);
99 }
100 barrier_stiffness_ *= scaling_factor;
101 max_barrier_stiffness_ *= scaling_factor;
102 }
103
104 // The barrier stiffness is choosen based on including the acceleration scaling,
105 // but the acceleration scaling will be applied later. Therefore, we need to remove it.
108
109 logger().debug(
110 "Setting adaptive barrier stiffness to {} (max barrier stiffness: {})",
112 }
113
114 void BarrierContactForm::update_collision_set(const Eigen::MatrixXd &displaced_surface)
115 {
116 // Store the previous value used to compute the constraint set to avoid duplicate computation.
117 if (cached_displaced_surface_.size() == displaced_surface.size() && cached_displaced_surface_ == displaced_surface)
118 return;
119
121 collision_set_.build(
122 candidates_, collision_mesh_, displaced_surface, dhat_);
123 else
124 collision_set_.build(
125 collision_mesh_, displaced_surface, dhat_, dmin_, broad_phase_.get());
126 cached_displaced_surface_ = displaced_surface;
127 }
128
133
134 Eigen::VectorXd BarrierContactForm::value_per_element_unweighted(const Eigen::VectorXd &x) const
135 {
136 const Eigen::MatrixXd V = compute_displaced_surface(x);
137 assert(V.rows() == collision_mesh_.num_vertices());
138
139 const size_t num_vertices = collision_mesh_.num_vertices();
140
141 if (collision_set_.empty())
142 {
143 return Eigen::VectorXd::Zero(collision_mesh_.full_num_vertices());
144 }
145
146 const Eigen::MatrixXi &E = collision_mesh_.edges();
147 const Eigen::MatrixXi &F = collision_mesh_.faces();
148
149 auto storage = utils::create_thread_storage<Eigen::VectorXd>(Eigen::VectorXd::Zero(num_vertices));
150
151 utils::maybe_parallel_for(collision_set_.size(), [&](int start, int end, int thread_id) {
152 Eigen::VectorXd &local_storage = utils::get_local_thread_storage(storage, thread_id);
153
154 for (size_t i = start; i < end; i++)
155 {
156 // Quadrature weight is premultiplied by compute_potential
157 const double potential = barrier_potential_(collision_set_[i], collision_set_[i].dof(V, E, F));
158
159 const int n_v = collision_set_[i].num_vertices();
160 const auto vis = collision_set_[i].vertex_ids(E, F);
161 for (int j = 0; j < n_v; j++)
162 {
163 assert(0 <= vis[j] && vis[j] < num_vertices);
164 local_storage[vis[j]] += potential / n_v;
165 }
166 }
167 });
168
169 Eigen::VectorXd out = Eigen::VectorXd::Zero(num_vertices);
170 for (const auto &local_potential : storage)
171 {
172 out += local_potential;
173 }
174
175 Eigen::VectorXd out_full = Eigen::VectorXd::Zero(collision_mesh_.full_num_vertices());
176 for (int i = 0; i < out.size(); i++)
177 out_full[collision_mesh_.to_full_vertex_id(i)] = out[i];
178
179 assert(std::abs(value_unweighted(x) - out_full.sum()) < std::max(1e-10 * out_full.sum(), 1e-10));
180
181 return out_full;
182 }
183
184 void BarrierContactForm::first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
185 {
186 gradv = barrier_potential_.gradient(collision_set_, collision_mesh_, compute_displaced_surface(x));
187 gradv = collision_mesh_.to_full_dof(gradv);
188 }
189
190 void BarrierContactForm::second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const
191 {
192 POLYFEM_SCOPED_TIMER("barrier hessian");
193
194 ipc::PSDProjectionMethod psd_projection_method;
195
196 if (project_to_psd_)
197 {
198 psd_projection_method = ipc::PSDProjectionMethod::CLAMP;
199 }
200 else
201 {
202 psd_projection_method = ipc::PSDProjectionMethod::NONE;
203 }
204
205 hessian = barrier_potential_.hessian(collision_set_, collision_mesh_, compute_displaced_surface(x), psd_projection_method);
206 hessian = collision_mesh_.to_full_dof(hessian);
207 }
208
209 void BarrierContactForm::post_step(const polysolve::nonlinear::PostStepData &data)
210 {
211 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(data.x);
212
213 const double curr_distance = collision_set_.compute_minimum_distance(collision_mesh_, displaced_surface);
214 if (!std::isinf(curr_distance))
215 {
216 const double ratio = sqrt(curr_distance) / dhat();
217 const auto log_level = (ratio < 1e-6) ? spdlog::level::err : ((ratio < 1e-4) ? spdlog::level::warn : spdlog::level::debug);
218 polyfem::logger().log(log_level, "Minimum distance during solve: {}, dhat: {}", sqrt(curr_distance), dhat());
219 }
220
221 if (data.iter_num == 0)
222 return;
223
224 if (use_adaptive_barrier_stiffness_)
225 {
226 if (is_time_dependent_)
227 {
228 const double prev_barrier_stiffness = barrier_stiffness();
229
230 barrier_stiffness_ = ipc::update_barrier_stiffness(
231 prev_distance_, curr_distance, max_barrier_stiffness_,
232 barrier_stiffness(), ipc::world_bbox_diagonal_length(displaced_surface),
233 dhat_epsilon_scale_);
234
235 if (barrier_stiffness() != prev_barrier_stiffness)
236 {
237 polyfem::logger().debug(
238 "updated barrier stiffness from {:g} to {:g} (max barrier stiffness: )",
239 prev_barrier_stiffness, barrier_stiffness(), max_barrier_stiffness_);
240 }
241 }
242 else
243 {
244 // TODO: missing feature
245 // update_barrier_stiffness(data.x);
246 }
247 }
248
249 prev_distance_ = curr_distance;
250 }
251} // namespace polyfem::solver
int V
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
bool use_improved_max_operator() const
Get use_improved_max_operator.
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, const double dhat_epsilon_scale, const ipc::NormalCollisions::CollisionSetType collision_set_type=ipc::NormalCollisions::CollisionSetType::IPC, const bool skip_obstacles=false, std::shared_ptr< ipc::Barrier > barrier=nullptr)
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::MatrixXd cached_displaced_surface_
Per-form cache: identical positions in another state do not imply that this collision set is built.
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.
Form representing the contact potential and forces.
const double avg_mass_
Average mass of the mesh (used for 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.
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_
double weight_
weight of the form (e.g., AL penalty weight or Δt²)
Definition Form.hpp:149
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
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24