PolyFEM
Loading...
Searching...
No Matches
InversionBarrier.cpp
Go to the documentation of this file.
2
4
5#include <cmath>
6
7namespace polyfem::assembler
8{
9 namespace
10 {
11 // Zero for J >= JBarrierThreshold, smoothly diverges to +inf as J -> 0.
12 // Scaled by mu at the call sites.
13 template <class T, int p = 3>
14 class barrier
15 {
16 constexpr static double C = 1e2;
17
18 public:
19 static_assert(p % 2 == 1);
20 static T value(T J, double JBarrierThreshold)
21 {
22 if (J >= JBarrierThreshold)
23 return T(0.);
24 const T tmp1 = J / JBarrierThreshold - 1;
25 const T tmp2 = pow(tmp1, p);
26 return C * (1 / (tmp2 + 1) - 1);
27 }
28
29 static T first_derivatives(T J, double JBarrierThreshold)
30 {
31 if (J >= JBarrierThreshold)
32 return T(0);
33 const T tmp1 = J / JBarrierThreshold - 1;
34 const T tmp2 = pow(tmp1, p);
35 return C * (-1 / JBarrierThreshold) * p * tmp2 / tmp1 / pow(1 + tmp2, 2);
36 }
37
38 static T second_derivatives(T J, double JBarrierThreshold)
39 {
40 if (J >= JBarrierThreshold)
41 return T(0);
42 const T tmp1 = J / JBarrierThreshold - 1;
43 const T tmp2 = pow(tmp1, p);
44 return C * (1 / (JBarrierThreshold * JBarrierThreshold)) * p * tmp2 / pow(tmp1, 2) * ((1 - p) + (1 + p) * tmp2) / pow(1 + tmp2, 3);
45 }
46 };
47
48 // Cofactor matrix dJ/dF, and its derivative d2J/dF2 (flattened column-major).
49 void jacobian_cofactor_derivatives(const Eigen::MatrixXd &def_grad, const int dim,
50 Eigen::MatrixXd &delJ_delF, Eigen::MatrixXd &del2J_delF2)
51 {
52 delJ_delF.setZero(dim, dim);
53 del2J_delF2.setZero(dim * dim, dim * dim);
54
55 if (dim == 2)
56 {
57 delJ_delF(0, 0) = def_grad(1, 1);
58 delJ_delF(0, 1) = -def_grad(1, 0);
59 delJ_delF(1, 0) = -def_grad(0, 1);
60 delJ_delF(1, 1) = def_grad(0, 0);
61
62 del2J_delF2(0, 3) = 1;
63 del2J_delF2(1, 2) = -1;
64 del2J_delF2(2, 1) = -1;
65 del2J_delF2(3, 0) = 1;
66 }
67 else // dim == 3
68 {
69 const Eigen::Vector3d u = def_grad.col(0), v = def_grad.col(1), w = def_grad.col(2);
70 delJ_delF.col(0) = v.cross(w);
71 delJ_delF.col(1) = w.cross(u);
72 delJ_delF.col(2) = u.cross(v);
73
74 auto hat = [](const Eigen::Vector3d &x) {
75 Eigen::Matrix3d prod;
76 prod << 0, -x(2), x(1),
77 x(2), 0, -x(0),
78 -x(1), x(0), 0;
79 return prod;
80 };
81
82 del2J_delF2.block<3, 3>(0, 6) = hat(v);
83 del2J_delF2.block<3, 3>(6, 0) = -hat(v);
84 del2J_delF2.block<3, 3>(0, 3) = -hat(w);
85 del2J_delF2.block<3, 3>(3, 0) = hat(w);
86 del2J_delF2.block<3, 3>(3, 6) = -hat(u);
87 del2J_delF2.block<3, 3>(6, 3) = hat(u);
88 }
89 }
90 } // namespace
91
93 : JBarrierThreshold_("JBarrierThreshold")
94 {
95 }
96
97 void InversionBarrier::add_multimaterial(const int index, const json &params, const Units &units, const std::string &root_path)
98 {
99 assert(size() == 2 || size() == 3);
100
101 params_.add_multimaterial(index, params, size() == 3, units.stress(), root_path);
102
103 // Default activation threshold matches the original hardcoded barrier (J >= 0.5 is inactive).
104 if (params.count("JBarrierThreshold"))
105 {
106 JBarrierThreshold_.add_multimaterial(index, params, "", root_path);
107 }
108 else
109 {
110 json params_with_default = params;
111 params_with_default["JBarrierThreshold"] = 0.5;
112 JBarrierThreshold_.add_multimaterial(index, params_with_default, "", root_path);
113 }
114 }
115
116 Eigen::Matrix<double, Eigen::Dynamic, 1, 0, 3, 1>
118 {
119 assert(pt.size() == size());
120 Eigen::Matrix<double, Eigen::Dynamic, 1, 0, 3, 1> res;
121 assert(false);
122
123 return res;
124 }
125
127 const OutputData &data,
128 const int all_size,
129 const ElasticityTensorType &type,
130 Eigen::MatrixXd &all,
131 const std::function<Eigen::MatrixXd(const Eigen::MatrixXd &)> &fun) const
132 {
133 const auto &displacement = data.fun;
134 const auto &local_pts = data.local_pts;
135 const auto &bs = data.bs;
136 const auto &gbs = data.gbs;
137 const auto el_id = data.el_id;
138 const auto t = data.t;
139
140 Eigen::MatrixXd displacement_grad(size(), size());
141
142 assert(displacement.cols() == 1);
143
144 all.resize(local_pts.rows(), all_size);
145
147 vals.compute(el_id, size() == 3, local_pts, bs, gbs);
148 const auto I = Eigen::MatrixXd::Identity(size(), size());
149
150 for (long p = 0; p < local_pts.rows(); ++p)
151 {
152 compute_diplacement_grad(size(), bs, vals, local_pts, p, displacement, displacement_grad);
153
154 const Eigen::MatrixXd def_grad = I + displacement_grad;
155 if (type == ElasticityTensorType::F)
156 {
157 all.row(p) = fun(def_grad);
158 continue;
159 }
160
161 const double J = polyfem::utils::determinant(def_grad);
162 const double JBarrierThreshold = JBarrierThreshold_(local_pts.row(p), t, vals.element_id);
163
164 Eigen::MatrixXd delJ_delF, del2J_delF2;
165 jacobian_cofactor_derivatives(def_grad, size(), delJ_delF, del2J_delF2);
166
167 double lambda, mu;
168 params_.lambda_mu(local_pts.row(p), vals.val.row(p), t, vals.element_id, lambda, mu);
169
170 // PK1-like stress dW/dF; convert to the requested tensor type via def_grad.
171 Eigen::MatrixXd stress_tensor = mu * barrier<double>::first_derivatives(J, JBarrierThreshold) * delJ_delF;
173 stress_tensor = (stress_tensor * def_grad.transpose()) / J;
174 else if (type == ElasticityTensorType::PK2)
175 stress_tensor = def_grad.inverse() * stress_tensor;
176 // else: PK1, already in the right form.
177
178 all.row(p) = fun(stress_tensor);
179 }
180 }
181
183 {
184 const int dim = size();
185 const int n_basis = data.vals.basis_values.size();
186
187 Eigen::VectorXd jacs;
189 jacs = data.vals.eval_deformed_jacobian_determinant(data.x);
190
191 Eigen::MatrixXd local_disp(n_basis, dim);
192 local_disp.setZero();
193 for (int i = 0; i < n_basis; ++i)
194 for (const auto &g : data.vals.basis_values[i].global)
195 for (int d = 0; d < dim; ++d)
196 local_disp(i, d) += g.val * data.x(g.index * dim + d);
197
198 double energy = 0.;
199 const int n_pts = data.da.size();
200 for (long p = 0; p < n_pts; ++p)
201 {
202 Eigen::MatrixXd grad(n_basis, dim);
203 for (int i = 0; i < n_basis; ++i)
204 grad.row(i) = data.vals.basis_values[i].grad.row(p);
205
206 const Eigen::MatrixXd jac_it = data.vals.jac_it[p];
207 const Eigen::MatrixXd def_grad = (local_disp.transpose() * grad) * jac_it + Eigen::MatrixXd::Identity(dim, dim);
208
209 const double J = use_robust_jacobian ? jacs(p) * jac_it.determinant() : def_grad.determinant();
210 const double JBarrierThreshold = JBarrierThreshold_(data.vals.quadrature.points.row(p), data.t, data.vals.element_id);
211
212 double lambda, mu;
213 params_.lambda_mu(data.vals.quadrature.points.row(p), data.vals.val.row(p), data.t, data.vals.element_id, lambda, mu);
214
215 energy += mu * barrier<double>::value(J, JBarrierThreshold) * data.da(p);
216 }
217 return energy;
218 }
219
221 {
222 const int dim = size();
223 const int n_basis = data.vals.basis_values.size();
224
225 Eigen::VectorXd jacs;
227 jacs = data.vals.eval_deformed_jacobian_determinant(data.x);
228
229 Eigen::MatrixXd local_disp(n_basis, dim);
230 local_disp.setZero();
231 for (int i = 0; i < n_basis; ++i)
232 for (const auto &g : data.vals.basis_values[i].global)
233 for (int d = 0; d < dim; ++d)
234 local_disp(i, d) += g.val * data.x(g.index * dim + d);
235
236 Eigen::MatrixXd G = Eigen::MatrixXd::Zero(n_basis, dim);
237
238 const int n_pts = data.da.size();
239 for (long p = 0; p < n_pts; ++p)
240 {
241 Eigen::MatrixXd grad(n_basis, dim);
242 for (int i = 0; i < n_basis; ++i)
243 grad.row(i) = data.vals.basis_values[i].grad.row(p);
244
245 const Eigen::MatrixXd jac_it = data.vals.jac_it[p];
246 const Eigen::MatrixXd delF_delU = grad * jac_it;
247
248 const Eigen::MatrixXd def_grad = local_disp.transpose() * delF_delU + Eigen::MatrixXd::Identity(dim, dim);
249
250 const double J = use_robust_jacobian ? jacs(p) * jac_it.determinant() : def_grad.determinant();
251 const double JBarrierThreshold = JBarrierThreshold_(data.vals.quadrature.points.row(p), data.t, data.vals.element_id);
252
253 Eigen::MatrixXd delJ_delF, del2J_delF2;
254 jacobian_cofactor_derivatives(def_grad, dim, delJ_delF, del2J_delF2);
255
256 double lambda, mu;
257 params_.lambda_mu(data.vals.quadrature.points.row(p), data.vals.val.row(p), data.t, data.vals.element_id, lambda, mu);
258
259 const Eigen::MatrixXd gradient_temp = mu * barrier<double>::first_derivatives(J, JBarrierThreshold) * delJ_delF;
260 const Eigen::MatrixXd gradient = delF_delU * gradient_temp.transpose();
261
262 G.noalias() += gradient * data.da(p);
263 }
264
265 const Eigen::MatrixXd G_T = G.transpose();
266 return Eigen::Map<const Eigen::VectorXd>(G_T.data(), G_T.size());
267 }
268
270 {
271 const int dim = size();
272 const int n_basis = data.vals.basis_values.size();
273 const int N = n_basis * dim;
274
275 Eigen::MatrixXd H = Eigen::MatrixXd::Zero(N, N);
276
277 Eigen::VectorXd jacs;
279 jacs = data.vals.eval_deformed_jacobian_determinant(data.x);
280
281 Eigen::MatrixXd local_disp(n_basis, dim);
282 local_disp.setZero();
283 for (int i = 0; i < n_basis; ++i)
284 for (const auto &g : data.vals.basis_values[i].global)
285 for (int d = 0; d < dim; ++d)
286 local_disp(i, d) += g.val * data.x(g.index * dim + d);
287
288 const int n_pts = data.da.size();
289 for (long p = 0; p < n_pts; ++p)
290 {
291 Eigen::MatrixXd grad(n_basis, dim);
292 for (int i = 0; i < n_basis; ++i)
293 grad.row(i) = data.vals.basis_values[i].grad.row(p);
294
295 const Eigen::MatrixXd jac_it = data.vals.jac_it[p];
296 const Eigen::MatrixXd delF_delU = grad * jac_it;
297
298 const Eigen::MatrixXd def_grad = local_disp.transpose() * delF_delU + Eigen::MatrixXd::Identity(dim, dim);
299
300 const double J = use_robust_jacobian ? jacs(p) * jac_it.determinant() : def_grad.determinant();
301 const double JBarrierThreshold = JBarrierThreshold_(data.vals.quadrature.points.row(p), data.t, data.vals.element_id);
302
303 Eigen::MatrixXd delJ_delF, del2J_delF2;
304 jacobian_cofactor_derivatives(def_grad, dim, delJ_delF, del2J_delF2);
305
306 double lambda, mu;
307 params_.lambda_mu(data.vals.quadrature.points.row(p), data.vals.val.row(p), data.t, data.vals.element_id, lambda, mu);
308
309 const Eigen::Map<const Eigen::VectorXd> g_j(delJ_delF.data(), delJ_delF.size());
310
311 const Eigen::MatrixXd hessian_temp = mu * barrier<double>::first_derivatives(J, JBarrierThreshold) * del2J_delF2
312 + mu * barrier<double>::second_derivatives(J, JBarrierThreshold) * (g_j * g_j.transpose());
313
314 Eigen::MatrixXd delF_delU_tensor = Eigen::MatrixXd::Zero(dim * dim, N);
315 for (int i = 0; i < n_basis; ++i)
316 {
317 for (int j = 0; j < dim; ++j)
318 {
319 Eigen::MatrixXd temp = Eigen::MatrixXd::Zero(dim, dim);
320 temp.row(j) = delF_delU.row(i);
321 delF_delU_tensor.col(i * dim + j) = Eigen::Map<const Eigen::VectorXd>(temp.data(), temp.size());
322 }
323 }
324
325 H += (delF_delU_tensor.transpose() * hessian_temp * delF_delU_tensor) * data.da(p);
326 }
327 return H;
328 }
329
330 std::map<std::string, Assembler::ParamFunc> InversionBarrier::parameters() const
331 {
332 std::map<std::string, ParamFunc> res;
333 const auto &params = params_;
334 const auto &JBarrierThreshold = JBarrierThreshold_;
335
336 res["mu"] = [&params](const RowVectorNd &uv, const RowVectorNd &p, double t, int e) {
337 double lambda, mu;
338 params.lambda_mu(uv, p, t, e, lambda, mu);
339 return mu;
340 };
341
342 res["JBarrierThreshold"] = [&JBarrierThreshold](const RowVectorNd &uv, const RowVectorNd &p, double t, int e) {
343 return JBarrierThreshold(p, t, e);
344 };
345
346 return res;
347 }
348} // namespace polyfem::assembler
ElementAssemblyValues vals
Definition Assembler.cpp:25
int x
std::string stress() const
Definition Units.hpp:27
stores per element basis values at given quadrature points and geometric mapping
void compute(const int el_index, const bool is_volume, const Eigen::MatrixXd &pts, const basis::ElementBases &basis, const basis::ElementBases &gbasis)
computes the per element values at the local (ref el) points (pts) sets basis_values,...
std::vector< Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3 > > jac_it
Eigen::VectorXd eval_deformed_jacobian_determinant(const Eigen::VectorXd &disp) const
void add_multimaterial(const int index, const json &params, const std::string &unit_type, const std::string &root_path)
Definition MatParams.cpp:30
VectorNd compute_rhs(const AutodiffHessianPt &pt) const override
void add_multimaterial(const int index, const json &params, const Units &units, const std::string &root_path) override
void assign_stress_tensor(const OutputData &data, const int all_size, const ElasticityTensorType &type, Eigen::MatrixXd &all, const std::function< Eigen::MatrixXd(const Eigen::MatrixXd &)> &fun) const override
Eigen::MatrixXd assemble_hessian(const NonLinearAssemblerData &data) const override
std::map< std::string, ParamFunc > parameters() const override
double compute_energy(const NonLinearAssemblerData &data) const override
Eigen::VectorXd assemble_gradient(const NonLinearAssemblerData &data) const override
void lambda_mu(double px, double py, double pz, double x, double y, double z, double t, int el_id, double &lambda, double &mu) const
void add_multimaterial(const int index, const json &params, const bool is_volume, const std::string &stress_unit, const std::string &root_path)
const basis::ElementBases & bs
const Eigen::MatrixXd & fun
const Eigen::MatrixXd & local_pts
const basis::ElementBases & gbs
Used for test only.
Eigen::Matrix< double, dim, dim > hat(const Eigen::Matrix< double, dim, 1 > &x)
T determinant(const Eigen::Matrix< T, rows, cols, option, maxRow, maxCol > &mat)
Eigen::Matrix< AutodiffScalarHessian, Eigen::Dynamic, 1, 0, 3, 1 > AutodiffHessianPt
nlohmann::json json
Definition Common.hpp:9
void compute_diplacement_grad(const int size, const ElementAssemblyValues &vals, const Eigen::MatrixXd &local_pts, const int p, const Eigen::MatrixXd &displacement, Eigen::MatrixXd &displacement_grad)
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13