Skip to content
This repository was archived by the owner on Oct 22, 2025. It is now read-only.

Commit 0c287e1

Browse files
committed
friction force jacobian
1 parent beb56c1 commit 0c287e1

File tree

5 files changed

+459
-222
lines changed

5 files changed

+459
-222
lines changed

src/ipc/friction/friction_collisions.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ void FrictionCollisions::build_for_smooth_contact(
5353
else if (const auto cev = dynamic_cast<const SmoothCollisionTemplate<max_vert_3d, Edge3, Point3> *>(&cc))
5454
{
5555
Eigen::VectorXd collision_points = cev->core_dof(vertices, edges, faces);
56-
collision_points = collision_points({6,7,8,0,1,2,3,4,5}).eval();
56+
collision_points = collision_points({6,7,8,0,1,2,3,4,5}).eval(); // {edge, point} -> {point, edge}
5757
FC_ev.emplace_back(
5858
EdgeVertexCollision(cc[0], cc[1], 1., Eigen::SparseVector<double>()), collision_points, contact_force);
5959
const auto& [vi, e0i, e1i, _] = FC_ev.back().vertex_ids(edges, faces);
@@ -90,7 +90,7 @@ void FrictionCollisions::build_for_smooth_contact(
9090
else if (const auto cfv = dynamic_cast<const SmoothCollisionTemplate<max_vert_3d, Face, Point3> *>(&cc))
9191
{
9292
Eigen::VectorXd collision_points = cfv->core_dof(vertices, edges, faces);
93-
collision_points = collision_points({9,10,11,0,1,2,3,4,5,6,7,8}).eval();
93+
collision_points = collision_points({9,10,11,0,1,2,3,4,5,6,7,8}).eval(); // {face, point} -> {point, face}
9494
FC_fv.emplace_back(
9595
FaceVertexCollision(cc[0], cc[1], 1., Eigen::SparseVector<double>()), collision_points, contact_force);
9696
const auto& [vi, f0i, f1i, f2i] = FC_fv.back().vertex_ids(edges, faces);
@@ -120,7 +120,7 @@ void FrictionCollisions::build_for_smooth_contact(
120120
else if (const auto cev = dynamic_cast<const SmoothCollisionTemplate<max_vert_2d, Edge2, Point2> *>(&cc))
121121
{
122122
Eigen::VectorXd collision_points = cev->core_dof(vertices, edges, faces);
123-
collision_points = collision_points({4,5,0,1,2,3}).eval();
123+
collision_points = collision_points({4,5,0,1,2,3}).eval(); // {edge, point} -> {point, edge}
124124
FC_ev.emplace_back(
125125
EdgeVertexCollision(cc[0], cc[1], 1., Eigen::SparseVector<double>()), collision_points, contact_force);
126126
const auto& [vi, e0i, e1i, _] = FC_ev.back().vertex_ids(edges, faces);

src/ipc/potentials/friction_potential.cpp

Lines changed: 146 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ Eigen::VectorXd FrictionPotential::smooth_contact_force(
164164

165165
tbb::enumerable_thread_specific<Eigen::VectorXd> storage(
166166
Eigen::VectorXd::Zero(velocities.size()));
167-
167+
168168
tbb::parallel_for(
169169
tbb::blocked_range<size_t>(size_t(0), collisions.size()),
170170
[&](const tbb::blocked_range<size_t>& r) {
@@ -176,8 +176,8 @@ Eigen::VectorXd FrictionPotential::smooth_contact_force(
176176
local_force = smooth_contact_force(
177177
collision, collision.dof(rest_positions, edges, faces),
178178
collision.dof(lagged_displacements, edges, faces),
179-
collision.dof(velocities, edges, faces), //
180-
dmin, no_mu);
179+
collision.dof(velocities, edges, faces),
180+
no_mu);
181181

182182
const std::array<long, 4> vis =
183183
collision.vertex_ids(mesh.edges(), mesh.faces());
@@ -197,7 +197,7 @@ Eigen::SparseMatrix<double> FrictionPotential::smooth_contact_force_jacobian(
197197
const Eigen::MatrixXd& rest_positions,
198198
const Eigen::MatrixXd& lagged_displacements,
199199
const Eigen::MatrixXd& velocities,
200-
const ParameterType &params,
200+
const ParameterType& params,
201201
const DiffWRT wrt,
202202
const double dmin) const
203203
{
@@ -213,6 +213,8 @@ Eigen::SparseMatrix<double> FrictionPotential::smooth_contact_force_jacobian(
213213
tbb::enumerable_thread_specific<std::vector<Eigen::Triplet<double>>>
214214
storage;
215215

216+
const Eigen::MatrixXd lagged_positions = rest_positions + lagged_displacements;
217+
216218
tbb::parallel_for(
217219
tbb::blocked_range<size_t>(size_t(0), collisions.size()),
218220
[&](const tbb::blocked_range<size_t>& r) {
@@ -221,15 +223,22 @@ Eigen::SparseMatrix<double> FrictionPotential::smooth_contact_force_jacobian(
221223
for (size_t i = r.begin(); i < r.end(); i++) {
222224
const FrictionCollision& collision = collisions[i];
223225

224-
// This jacobian doesn't include the derivatives of normal contact force
226+
// This jacobian doesn't include the derivatives of normal
227+
// contact force
225228
const MatrixMax<
226229
double, FrictionPotential::element_size,
227230
FrictionPotential::element_size>
228-
local_force_jacobian = smooth_contact_force_jacobian(
229-
collision, collision.dof(rest_positions, edges, faces),
230-
collision.dof(lagged_displacements, edges, faces),
231-
collision.dof(velocities, edges, faces), //
232-
wrt, dmin);
231+
local_force_jacobian =
232+
collision.normal_force_magnitude
233+
* force_jacobian_unit(
234+
collision,
235+
collision.dof(lagged_positions, edges, faces),
236+
collision.dof(velocities, edges, faces), wrt);
237+
// smooth_contact_force_jacobian(
238+
// collision, collision.dof(rest_positions, edges, faces),
239+
// collision.dof(lagged_displacements, edges, faces),
240+
// collision.dof(velocities, edges, faces), //
241+
// wrt);
233242

234243
const std::array<long, 4> vis =
235244
collision.vertex_ids(mesh.edges(), mesh.faces());
@@ -245,35 +254,42 @@ Eigen::SparseMatrix<double> FrictionPotential::smooth_contact_force_jacobian(
245254
local_force = smooth_contact_force(
246255
collision, collision.dof(rest_positions, edges, faces),
247256
collision.dof(lagged_displacements, edges, faces),
248-
collision.dof(velocities, edges, faces), //
249-
dmin, false);
250-
251-
std::cout << "local fric force " << local_force.transpose() << "\n\n";
257+
collision.dof(velocities, edges, faces),
258+
false, true);
252259

260+
// normal_force_grad is the gradient of contact force norm
253261
Eigen::VectorXd normal_force_grad;
254262
std::vector<long> cc_vert_ids;
255-
Eigen::MatrixXd Xt = rest_positions + lagged_displacements;
256-
if (dim == 2)
257-
{
258-
auto cc = collision.smooth_collision_2d;
259-
const Eigen::VectorXd contact_grad = cc->gradient(cc->dof(Xt, edges, faces), params);
260-
const Eigen::MatrixXd contact_hess = cc->hessian(cc->dof(Xt, edges, faces), params);
261-
normal_force_grad = (1 / contact_grad.squaredNorm()) * (contact_hess * contact_grad);
262-
auto tmp_ids = cc->vertex_ids(edges, faces);
263-
cc_vert_ids = std::vector(tmp_ids.begin(), tmp_ids.end());
264-
}
265-
else if (dim == 3)
266263
{
267-
auto cc = collision.smooth_collision_3d;
268-
const Eigen::VectorXd contact_grad = cc->gradient(cc->dof(Xt, edges, faces), params);
269-
const Eigen::MatrixXd contact_hess = cc->hessian(cc->dof(Xt, edges, faces), params);
270-
normal_force_grad = (1 / contact_grad.squaredNorm()) * (contact_hess * contact_grad);
271-
auto tmp_ids = cc->vertex_ids(edges, faces);
272-
cc_vert_ids = std::vector(tmp_ids.begin(), tmp_ids.end());
264+
Eigen::MatrixXd Xt = rest_positions + lagged_displacements;
265+
if (dim == 2) {
266+
auto cc = collision.smooth_collision_2d;
267+
const Eigen::VectorXd contact_grad =
268+
cc->gradient(cc->dof(Xt, edges, faces), params);
269+
const Eigen::MatrixXd contact_hess =
270+
cc->hessian(cc->dof(Xt, edges, faces), params);
271+
normal_force_grad = (1 / contact_grad.norm())
272+
* (contact_hess * contact_grad);
273+
auto tmp_ids = cc->vertex_ids(edges, faces);
274+
cc_vert_ids =
275+
std::vector(tmp_ids.begin(), tmp_ids.end());
276+
} else if (dim == 3) {
277+
auto cc = collision.smooth_collision_3d;
278+
const Eigen::VectorXd contact_grad =
279+
cc->gradient(cc->dof(Xt, edges, faces), params);
280+
const Eigen::MatrixXd contact_hess =
281+
cc->hessian(cc->dof(Xt, edges, faces), params);
282+
normal_force_grad = (1 / contact_grad.norm())
283+
* (contact_hess * contact_grad);
284+
auto tmp_ids = cc->vertex_ids(edges, faces);
285+
cc_vert_ids =
286+
std::vector(tmp_ids.begin(), tmp_ids.end());
287+
}
273288
}
274289

275290
local_jacobian_to_global_triplets(
276-
local_force * normal_force_grad.transpose(), vis, cc_vert_ids, dim, jac_triplets);
291+
local_force * normal_force_grad.transpose(), vis,
292+
cc_vert_ids, dim, jac_triplets);
277293
}
278294
});
279295

@@ -291,10 +307,12 @@ Eigen::SparseMatrix<double> FrictionPotential::smooth_contact_force_jacobian(
291307
// if (wrt == DiffWRT::REST_POSITIONS) {
292308
// for (int i = 0; i < collisions.size(); i++) {
293309
// const FrictionCollision& collision = collisions[i];
294-
// assert(collision.weight_gradient.size() == rest_positions.size());
295-
// if (collision.weight_gradient.size() != rest_positions.size()) {
310+
// assert(collision.weight_gradient.size() ==
311+
// rest_positions.size()); if (collision.weight_gradient.size() !=
312+
// rest_positions.size()) {
296313
// throw std::runtime_error(
297-
// "Shape derivative is not computed for friction collision!");
314+
// "Shape derivative is not computed for friction
315+
// collision!");
298316
// }
299317

300318
// Vector<double, -1, FrictionPotential::element_size> local_force =
@@ -564,6 +582,87 @@ FrictionPotential::force_jacobian(
564582
// Compute β
565583
const VectorMax2d beta = collision.compute_closest_point(lagged_positions);
566584

585+
// Compute Γ
586+
const MatrixMax<double, 3, FrictionPotential::element_size> Gamma =
587+
collision.relative_velocity_matrix(beta);
588+
589+
// Compute T = ΓᵀP
590+
const MatrixMax<double, FrictionPotential::element_size, 2> T =
591+
Gamma.transpose() * P;
592+
593+
// Compute τ = PᵀΓv
594+
const VectorMax2d tau = P.transpose() * Gamma * velocities;
595+
596+
// Compute f₁(‖τ‖)/‖τ‖
597+
const double tau_norm = tau.norm();
598+
const double f1_over_norm_tau = f1_SF_over_x(tau_norm, epsv());
599+
600+
// Premultiplied values
601+
const Vector<double, -1, FrictionPotential::element_size> T_times_tau =
602+
T * tau;
603+
604+
// ------------------------------------------------------------------------
605+
// Compute J = ∇F = ∇(-μ N f₁(‖τ‖)/‖τ‖ T τ)
606+
MatrixMax<
607+
double, FrictionPotential::element_size,
608+
FrictionPotential::element_size>
609+
J = MatrixMax<
610+
double, FrictionPotential::element_size,
611+
FrictionPotential::element_size>::Zero(n, n);
612+
613+
// = -μ f₁(‖τ‖)/‖τ‖ (T τ) [∇N]ᵀ
614+
if (need_jac_N_or_T) {
615+
J = f1_over_norm_tau * T_times_tau * grad_N.transpose();
616+
}
617+
618+
// NOTE: ∇ₓw(x) is not local to the collision pair (i.e., it involves more
619+
// than the 4 collisioning vertices), so we do not have enough information
620+
// here to compute the gradient. Instead this should be handled outside of
621+
// the function. For a simple multiplicitive model (∑ᵢ wᵢ Fᵢ) this can be
622+
// done easily.
623+
J *= -collision.weight * collision.mu;
624+
625+
J += N
626+
* force_jacobian_unit(
627+
collision, lagged_positions, velocities, wrt);
628+
629+
return J;
630+
}
631+
632+
MatrixMax<
633+
double,
634+
FrictionPotential::element_size,
635+
FrictionPotential::element_size>
636+
FrictionPotential::force_jacobian_unit(
637+
const FrictionCollision& collision,
638+
const Vector<double, -1, FrictionPotential::element_size>&
639+
lagged_positions, // = x + u^t
640+
const Vector<double, -1, FrictionPotential::element_size>&
641+
velocities, // = v
642+
const DiffWRT wrt) const
643+
{
644+
// x is the rest position
645+
// u is the displacment at the begginging of the lagged solve
646+
// v is the current velocity
647+
//
648+
// τ = T(x + u)ᵀv is the tangential sliding velocity
649+
// F(x, u, v) = -μ f₁(‖τ‖)/‖τ‖ T(x + u) τ
650+
//
651+
// Compute ∇F
652+
assert(lagged_positions.size() == velocities.size());
653+
const int n = lagged_positions.size();
654+
const int dim = n / collision.num_vertices();
655+
assert(n % collision.num_vertices() == 0);
656+
657+
const bool need_jac_N_or_T = wrt != DiffWRT::VELOCITIES;
658+
659+
// Compute P
660+
const MatrixMax<double, 3, 2> P =
661+
collision.compute_tangent_basis(lagged_positions);
662+
663+
// Compute β
664+
const VectorMax2d beta = collision.compute_closest_point(lagged_positions);
665+
567666
// Compute Γ
568667
const MatrixMax<double, 3, FrictionPotential::element_size> Gamma =
569668
collision.relative_velocity_matrix(beta);
@@ -653,25 +752,20 @@ FrictionPotential::force_jacobian(
653752
double, FrictionPotential::element_size,
654753
FrictionPotential::element_size>::Zero(n, n);
655754

656-
// = -μ f₁(‖τ‖)/‖τ‖ (T τ) [∇N]ᵀ
657-
if (need_jac_N_or_T) {
658-
J = f1_over_norm_tau * T_times_tau * grad_N.transpose();
659-
}
660-
661755
// + -μ N T τ [∇(f₁(‖τ‖)/‖τ‖)]
662-
J += N * T_times_tau * grad_f1_over_norm_tau.transpose();
756+
J += T_times_tau * grad_f1_over_norm_tau.transpose();
663757

664758
// + -μ N f₁(‖τ‖)/‖τ‖ [∇T] τ
665759
if (need_jac_N_or_T) {
666-
const VectorMax2d scaled_tau = N * f1_over_norm_tau * tau;
760+
const VectorMax2d scaled_tau = f1_over_norm_tau * tau;
667761
for (int i = 0; i < n; i++) {
668762
// ∂J/∂xᵢ = ∂T/∂xᵢ * τ
669763
J.col(i) += jac_T.middleRows(i * n, n) * scaled_tau;
670764
}
671765
}
672766

673767
// + -μ N f₁(‖τ‖)/‖τ‖ T [∇τ]
674-
J += N * f1_over_norm_tau * T * jac_tau;
768+
J += f1_over_norm_tau * T * jac_tau;
675769

676770
// NOTE: ∇ₓw(x) is not local to the collision pair (i.e., it involves more
677771
// than the 4 collisioning vertices), so we do not have enough information
@@ -683,16 +777,17 @@ FrictionPotential::force_jacobian(
683777
return J;
684778
}
685779

686-
Vector<double, -1, FrictionPotential::element_size> FrictionPotential::smooth_contact_force(
780+
Vector<double, -1, FrictionPotential::element_size>
781+
FrictionPotential::smooth_contact_force(
687782
const FrictionCollision& collision,
688783
const Vector<double, -1, FrictionPotential::element_size>&
689784
rest_positions, // = x
690785
const Vector<double, -1, FrictionPotential::element_size>&
691786
lagged_displacements, // = u
692787
const Vector<double, -1, FrictionPotential::element_size>&
693788
velocities, // = v
694-
const double dmin,
695-
const bool no_mu) const
789+
const bool no_mu,
790+
const bool no_contact_force_multiplier) const
696791
{
697792
// x is the rest position
698793
// u is the displacment at the begginging of the lagged solve
@@ -739,21 +834,19 @@ Vector<double, -1, FrictionPotential::element_size> FrictionPotential::smooth_co
739834

740835
// F = -μ N f₁(‖τ‖)/‖τ‖ T τ
741836
// NOTE: no_mu -> leave mu out of this function (i.e., assuming mu = 1)
742-
return -collision.weight * (no_mu ? 1.0 : collision.mu) * N
743-
* f1_over_norm_tau * T * tau;
837+
return -collision.weight * (no_mu ? 1.0 : collision.mu)
838+
* (no_contact_force_multiplier ? 1.0 : N) * f1_over_norm_tau * T * tau;
744839
}
745840

746-
Eigen::MatrixXd
747-
FrictionPotential::smooth_contact_force_jacobian(
841+
Eigen::MatrixXd FrictionPotential::smooth_contact_force_jacobian(
748842
const FrictionCollision& collision,
749843
const Vector<double, -1, FrictionPotential::element_size>&
750844
rest_positions, // = x
751845
const Vector<double, -1, FrictionPotential::element_size>&
752846
lagged_displacements, // = u
753847
const Vector<double, -1, FrictionPotential::element_size>&
754848
velocities, // = v
755-
const DiffWRT wrt,
756-
const double dmin) const
849+
const DiffWRT wrt) const
757850
{
758851
// x is the rest position
759852
// u is the displacment at the begginging of the lagged solve
@@ -779,7 +872,7 @@ FrictionPotential::smooth_contact_force_jacobian(
779872
rest_positions + lagged_displacements; // = x + u
780873
const bool need_jac_N_or_T = wrt != DiffWRT::VELOCITIES;
781874

782-
// Compute ∇N
875+
// Compute ∇N -- commented, compute it outside
783876
// Vector<double, -1, FrictionPotential::element_size> grad_N;
784877
// if (need_jac_N_or_T) {
785878
// // ∇ₓN = ∇ᵤN

src/ipc/potentials/friction_potential.hpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,23 @@ class FrictionPotential : public Potential<FrictionCollisions> {
172172
const double dmin = 0) const;
173173

174174

175+
/// @brief Compute the friction force Jacobian assuming the contact force magnitude being 1.
176+
/// @param collision The collision
177+
/// @param rest_positions Rest positions of the vertices (rowwise).
178+
/// @param lagged_displacements Previous displacements of the vertices (rowwise).
179+
/// @param velocities Current displacements of the vertices (rowwise).
180+
/// @param barrier_potential Barrier potential (used for normal force magnitude).
181+
/// @param barrier_stiffness Barrier stiffness (used for normal force magnitude).
182+
/// @param wrt Variable to differentiate the friction force with respect to.
183+
/// @param dmin Minimum distance (used for normal force magnitude).
184+
/// @return Friction force Jacobian
185+
MatrixMax<double, element_size, element_size> force_jacobian_unit(
186+
const FrictionCollision& collision,
187+
const Vector<double, -1, element_size>& lagged_positions,
188+
const Vector<double, -1, element_size>& velocities,
189+
const DiffWRT wrt) const;
190+
191+
175192
/// @brief Compute the friction force.
176193
/// @param collision The collision
177194
/// @param rest_positions Rest positions of the vertices (rowwise).
@@ -186,8 +203,8 @@ class FrictionPotential : public Potential<FrictionCollisions> {
186203
const Vector<double, -1, element_size>& rest_positions,
187204
const Vector<double, -1, element_size>& lagged_displacements,
188205
const Vector<double, -1, element_size>& velocities,
189-
const double dmin = 0,
190-
const bool no_mu = false) const; //< whether to not multiply by mu
206+
const bool no_mu = false,
207+
const bool no_contact_force_multiplier = false) const; //< whether to not multiply by mu
191208

192209
/// @brief Compute the friction force Jacobian.
193210
/// @param collision The collision
@@ -203,8 +220,7 @@ class FrictionPotential : public Potential<FrictionCollisions> {
203220
const Vector<double, -1, element_size>& rest_positions,
204221
const Vector<double, -1, element_size>& lagged_displacements,
205222
const Vector<double, -1, element_size>& velocities,
206-
const DiffWRT wrt,
207-
const double dmin = 0) const;
223+
const DiffWRT wrt) const;
208224

209225
protected:
210226
/// @brief The smooth friction mollifier parameter \f$\epsilon_v\f$.

0 commit comments

Comments
 (0)