PolyFEM
Loading...
Searching...
No Matches
ContactForm.cpp
Go to the documentation of this file.
1#include "ContactForm.hpp"
2
10
12
13#include <ipc/barrier/adaptive_stiffness.hpp>
14#include <ipc/utils/world_bbox_diagonal_length.hpp>
15
16#include <igl/writePLY.h>
17
18namespace polyfem::solver
19{
20 ContactForm::ContactForm(const ipc::CollisionMesh &collision_mesh,
21 const double dhat,
22 const double avg_mass,
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 : collision_mesh_(collision_mesh),
31 dhat_(dhat),
32 use_adaptive_barrier_stiffness_(use_adaptive_barrier_stiffness),
33 barrier_stiffness_(1.0),
34 dhat_epsilon_scale_(dhat_epsilon_scale),
35 avg_mass_(avg_mass),
36 is_time_dependent_(is_time_dependent),
37 enable_shape_derivatives_(enable_shape_derivatives),
38 broad_phase_method_(broad_phase_method),
39 broad_phase_(ipc::create_broad_phase(broad_phase_method)),
40 tight_inclusion_ccd_(ccd_tolerance, ccd_max_iterations)
41 {
42 assert(dhat_ > 0);
43 assert(ccd_tolerance > 0);
44
45 prev_distance_ = -1;
46 }
47
48 void ContactForm::init(const Eigen::VectorXd &x)
49 {
51 }
52
53 void ContactForm::update_quantities(const double t, const Eigen::VectorXd &x)
54 {
56 }
57
58 Eigen::MatrixXd ContactForm::compute_displaced_surface(const Eigen::VectorXd &x) const
59 {
60 return collision_mesh_.displace_vertices(utils::unflatten(x, collision_mesh_.dim()));
61 }
62
63 void ContactForm::solution_changed(const Eigen::VectorXd &new_x)
64 {
66 }
67
68 double ContactForm::max_step_size(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) const
69 {
70 // Extract surface only
71 const Eigen::MatrixXd V0 = compute_displaced_surface(x0);
72 const Eigen::MatrixXd V1 = compute_displaced_surface(x1);
73
75 {
76 const Eigen::MatrixXi E = collision_mesh_.dim() == 2 ? Eigen::MatrixXi() : collision_mesh_.edges();
77 const Eigen::MatrixXi &F = collision_mesh_.faces();
78 igl::writePLY(resolve_output_path("debug_ccd_0.ply"), V0, F, E);
79 igl::writePLY(resolve_output_path("debug_ccd_1.ply"), V1, F, E);
80 }
81
82 double max_step;
83 if (use_cached_candidates_ && broad_phase_method_ != ipc::BroadPhaseMethod::SWEEP_AND_TINIEST_QUEUE)
84 max_step = candidates_.compute_collision_free_stepsize(
86 else
87 max_step = ipc::compute_collision_free_stepsize(
89
90 if (save_ccd_debug_meshes && ipc::has_intersections(collision_mesh_, (V1 - V0) * max_step + V0, broad_phase_.get()))
91 {
92 log_and_throw_error("Taking max_step results in intersections (max_step={})", max_step);
93 }
94
95#ifndef NDEBUG
96 // This will check for static intersections as a failsafe. Not needed if we use our conservative CCD.
97 Eigen::MatrixXd V_toi = (V1 - V0) * max_step + V0;
98
99 while (ipc::has_intersections(collision_mesh_, V_toi, broad_phase_.get()))
100 {
101 logger().error("Taking max_step results in intersections (max_step={:g})", max_step);
102 max_step /= 2.0;
103
104 const double Linf = (V_toi - V0).lpNorm<Eigen::Infinity>();
105 if (max_step <= 0 || Linf == 0)
106 log_and_throw_error("Unable to find an intersection free step size (max_step={:g} L∞={:g})", max_step, Linf);
107
108 V_toi = (V1 - V0) * max_step + V0;
109 }
110#endif
111
112 return max_step;
113 }
114
115 void ContactForm::line_search_begin(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1)
116 {
117 candidates_.build(
121 /*inflation_radius=*/barrier_support_size() / 2,
122 broad_phase_.get());
123
125 }
126
128 {
129 candidates_.clear();
131 }
132
133 bool ContactForm::is_step_collision_free(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) const
134 {
135 const auto displaced0 = compute_displaced_surface(x0);
136 const auto displaced1 = compute_displaced_surface(x1);
137
138 // Skip CCD if the displacement is zero.
139 if ((displaced1 - displaced0).lpNorm<Eigen::Infinity>() == 0.0)
140 {
141 // Assumes initially intersection-free
142 return true;
143 }
144
145 bool is_valid;
147 is_valid = candidates_.is_step_collision_free(
148 collision_mesh_, displaced0, displaced1, dmin_,
150 else
151 is_valid = ipc::is_step_collision_free(
152 collision_mesh_, displaced0, displaced1, dmin_, broad_phase_.get(),
154
155 return is_valid;
156 }
157} // namespace polyfem::solver
int x
virtual void update_collision_set(const Eigen::MatrixXd &displaced_surface)=0
Update the cached candidate set for the current solution.
const double dmin_
Minimum distance between elements.
virtual void init(const Eigen::VectorXd &x) override
Initialize the form.
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.
ContactForm(const ipc::CollisionMesh &collision_mesh, const double dhat, const double avg_mass, 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)
Construct a new Contact Form object.
virtual double max_step_size(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) const override
Determine the maximum step size allowable between the current and next solution.
virtual void solution_changed(const Eigen::VectorXd &new_x) override
Update cached fields upon a change in the solution.
double prev_distance_
Previous minimum distance between all elements.
const ipc::BroadPhaseMethod broad_phase_method_
Broad phase method to use for distance and CCD evaluations.
void line_search_end() override
Clear variables used during the line search.
const ipc::CollisionMesh & collision_mesh_
Collision mesh.
const ipc::TightInclusionCCD tight_inclusion_ccd_
Continuous collision detection specification object.
virtual void line_search_begin(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) override
Initialize variables used during the line search.
ipc::Candidates candidates_
Cached candidate set for the current solution.
virtual double barrier_support_size() const
virtual void update_quantities(const double t, const Eigen::VectorXd &x) override
Update time-dependent fields.
bool save_ccd_debug_meshes
If true, output debug files.
const double dhat_
Barrier activation distance.
const std::shared_ptr< ipc::BroadPhase > broad_phase_
virtual bool is_step_collision_free(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) const override
Checks if the step is collision free.
std::string resolve_output_path(const std::string &path) const
Definition Form.hpp:155
std::tuple< bool, int, Tree > is_valid(const int dim, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const Eigen::VectorXd &u, const double threshold, const unsigned max_iter)
Definition Jacobian.cpp:201
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73