Loading [MathJax]/extensions/tex2jax.js
PolyFEM
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
NormalAdhesionForm.cpp
Go to the documentation of this file.
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 NormalAdhesionForm::NormalAdhesionForm(const ipc::CollisionMesh &collision_mesh,
21 const double dhat_p,
22 const double dhat_a,
23 const double Y,
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 : collision_mesh_(collision_mesh),
30 dhat_p_(dhat_p),
31 dhat_a_(dhat_a),
32 Y_(Y),
33 is_time_dependent_(is_time_dependent),
34 enable_shape_derivatives_(enable_shape_derivatives),
35 broad_phase_method_(broad_phase_method),
36 tight_inclusion_ccd_(ccd_tolerance, ccd_max_iterations),
37 normal_adhesion_potential_(dhat_p, dhat_a, Y, 1)
38 {
39 assert(dhat_p > 0);
40 assert(dhat_a > dhat_p);
41 assert(ccd_tolerance > 0);
42
43 prev_distance_ = -1;
44 collision_set_.set_enable_shape_derivatives(enable_shape_derivatives);
45 }
46
47 void NormalAdhesionForm::init(const Eigen::VectorXd &x)
48 {
50 }
51
52 void NormalAdhesionForm::force_shape_derivative(const ipc::NormalCollisions &collision_set, const Eigen::MatrixXd &solution, const Eigen::VectorXd &adjoint_sol, Eigen::VectorXd &term)
53 {
54 // Eigen::MatrixXd U = collision_mesh_.vertices(utils::unflatten(solution, collision_mesh_.dim()));
55 // Eigen::MatrixXd X = collision_mesh_.vertices(boundary_nodes_pos_);
56 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(solution);
57
58 StiffnessMatrix dq_h = collision_mesh_.to_full_dof(normal_adhesion_potential_.shape_derivative(collision_set, collision_mesh_, displaced_surface));
59 term = dq_h.transpose() * adjoint_sol;
60 }
61
62 void NormalAdhesionForm::update_quantities(const double t, const Eigen::VectorXd &x)
63 {
65 }
66
67 Eigen::MatrixXd NormalAdhesionForm::compute_displaced_surface(const Eigen::VectorXd &x) const
68 {
69 return collision_mesh_.displace_vertices(utils::unflatten(x, collision_mesh_.dim()));
70 }
71
72 void NormalAdhesionForm::update_collision_set(const Eigen::MatrixXd &displaced_surface)
73 {
74 // Store the previous value used to compute the constraint set to avoid duplicate computation.
75 static Eigen::MatrixXd cached_displaced_surface;
76 if (cached_displaced_surface.size() == displaced_surface.size() && cached_displaced_surface == displaced_surface)
77 return;
78
80 collision_set_.build(
81 candidates_, collision_mesh_, displaced_surface, dhat_a_);
82 else
83 collision_set_.build(
84 collision_mesh_, displaced_surface, dhat_a_, dmin_, broad_phase_method_);
85 cached_displaced_surface = displaced_surface;
86 }
87
92
93 Eigen::VectorXd NormalAdhesionForm::value_per_element_unweighted(const Eigen::VectorXd &x) const
94 {
95 const Eigen::MatrixXd V = compute_displaced_surface(x);
96 assert(V.rows() == collision_mesh_.num_vertices());
97
98 const size_t num_vertices = collision_mesh_.num_vertices();
99
100 if (collision_set_.empty())
101 {
102 return Eigen::VectorXd::Zero(collision_mesh_.full_num_vertices());
103 }
104
105 const Eigen::MatrixXi &E = collision_mesh_.edges();
106 const Eigen::MatrixXi &F = collision_mesh_.faces();
107
108 auto storage = utils::create_thread_storage<Eigen::VectorXd>(Eigen::VectorXd::Zero(num_vertices));
109
110 utils::maybe_parallel_for(collision_set_.size(), [&](int start, int end, int thread_id) {
111 Eigen::VectorXd &local_storage = utils::get_local_thread_storage(storage, thread_id);
112
113 for (size_t i = start; i < end; i++)
114 {
115 // Quadrature weight is premultiplied by compute_potential
116 const double potential = normal_adhesion_potential_(collision_set_[i], collision_set_[i].dof(V, E, F));
117
118 const int n_v = collision_set_[i].num_vertices();
119 const std::array<long, 4> vis = collision_set_[i].vertex_ids(E, F);
120 for (int j = 0; j < n_v; j++)
121 {
122 assert(0 <= vis[j] && vis[j] < num_vertices);
123 local_storage[vis[j]] += potential / n_v;
124 }
125 }
126 });
127
128 Eigen::VectorXd out = Eigen::VectorXd::Zero(num_vertices);
129 for (const auto &local_potential : storage)
130 {
131 out += local_potential;
132 }
133
134 Eigen::VectorXd out_full = Eigen::VectorXd::Zero(collision_mesh_.full_num_vertices());
135 for (int i = 0; i < out.size(); i++)
136 out_full[collision_mesh_.to_full_vertex_id(i)] = out[i];
137
138 assert(std::abs(value_unweighted(x) - out_full.sum()) < std::max(1e-10 * out_full.sum(), 1e-10));
139
140 return out_full;
141 }
142
143 void NormalAdhesionForm::first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
144 {
146 gradv = collision_mesh_.to_full_dof(gradv);
147 }
148
149 void NormalAdhesionForm::second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const
150 {
151 POLYFEM_SCOPED_TIMER("normal adhesion hessian");
152
153 ipc::PSDProjectionMethod psd_projection_method;
154
155 if (project_to_psd_) {
156 psd_projection_method = ipc::PSDProjectionMethod::CLAMP;
157 } else {
158 psd_projection_method = ipc::PSDProjectionMethod::NONE;
159 }
160
161 hessian = normal_adhesion_potential_.hessian(collision_set_, collision_mesh_, compute_displaced_surface(x), psd_projection_method);
162 hessian = collision_mesh_.to_full_dof(hessian);
163 }
164
165 void NormalAdhesionForm::solution_changed(const Eigen::VectorXd &new_x)
166 {
168 }
169
170 void NormalAdhesionForm::line_search_begin(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1)
171 {
172 candidates_.build(
176 /*inflation_radius=*/dhat_a_ / 2,
178
180 }
181
183 {
184 candidates_.clear();
186 }
187
188 void NormalAdhesionForm::post_step(const polysolve::nonlinear::PostStepData &data)
189 {
190 if (data.iter_num == 0)
191 return;
192
193 const Eigen::MatrixXd displaced_surface = compute_displaced_surface(data.x);
194
195 const double curr_distance = collision_set_.compute_minimum_distance(collision_mesh_, displaced_surface);
196
197 prev_distance_ = curr_distance;
198 }
199
200
201} // namespace polyfem::solver
int V
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
bool project_to_psd_
If true, the form's second derivative is projected to be positive semidefinite.
Definition Form.hpp:147
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.
const ipc::CollisionMesh & collision_mesh_
Collision mesh.
ipc::NormalCollisions collision_set_
Cached constraint set for the current solution.
virtual void second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const override
Compute the second derivative of the value wrt x.
const ipc::NormalAdhesionPotential normal_adhesion_potential_
double prev_distance_
Previous minimum distance between all elements.
ipc::Candidates candidates_
Cached candidate set for the current solution.
const double dhat_a_
Adhesion activation distance.
virtual void first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
Compute the first derivative of the value wrt x.
const double dmin_
Minimum distance between elements.
NormalAdhesionForm(const ipc::CollisionMesh &collision_mesh, const double dhat_p, const double dhat_a, const double Y, 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)
Construct a new NormalAdhesion Form object.
void update_quantities(const double t, const Eigen::VectorXd &x) override
Update time-dependent fields.
virtual double value_unweighted(const Eigen::VectorXd &x) const override
Compute the normal adhesion potential value.
virtual void force_shape_derivative(const ipc::NormalCollisions &collision_set, const Eigen::MatrixXd &solution, const Eigen::VectorXd &adjoint_sol, Eigen::VectorXd &term)
void line_search_begin(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) override
Initialize variables used during the line search.
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
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.
Eigen::MatrixXd compute_displaced_surface(const Eigen::VectorXd &x) const
Compute the displaced positions of the surface nodes.
void solution_changed(const Eigen::VectorXd &new_x) override
Update cached fields upon a change in the solution.
void update_collision_set(const Eigen::MatrixXd &displaced_surface)
Update the cached candidate set for the current solution.
void post_step(const polysolve::nonlinear::PostStepData &data) override
Update fields after a step in the optimization.
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
void maybe_parallel_for(int size, const std::function< void(int, int, int)> &partial_for)
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:22