PolyFEM
Loading...
Searching...
No Matches
Parametrizations.cpp
Go to the documentation of this file.
5#include <polyfem/Common.hpp>
7
8namespace polyfem::solver
9{
10 ExponentialMap::ExponentialMap(const int from, const int to)
11 : from_(from), to_(to)
12 {
13 assert(from_ < to_ || from_ < 0);
14 }
15
16 Eigen::VectorXd ExponentialMap::inverse_eval(const Eigen::VectorXd &y)
17 {
18 if (from_ >= 0)
19 {
20 Eigen::VectorXd res = y;
21 res.segment(from_, to_ - from_) = y.segment(from_, to_ - from_).array().log();
22 return res;
23 }
24 else
25 return y.array().log();
26 }
27
28 Eigen::VectorXd ExponentialMap::eval(const Eigen::VectorXd &x) const
29 {
30 if (from_ >= 0)
31 {
32 Eigen::VectorXd y = x;
33 y.segment(from_, to_ - from_) = x.segment(from_, to_ - from_).array().exp();
34 return y;
35 }
36 else
37 return x.array().exp();
38 }
39
40 Eigen::VectorXd ExponentialMap::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
41 {
42 if (from_ >= 0)
43 {
44 Eigen::VectorXd res = grad.array();
45 res.segment(from_, to_ - from_) = x.segment(from_, to_ - from_).array().exp() * grad.segment(from_, to_ - from_).array();
46 return res;
47 }
48 else
49 return x.array().exp() * grad.array();
50 }
51
52 Scaling::Scaling(const double scale, const int from, const int to)
53 : from_(from), to_(to), scale_(scale)
54 {
55 assert(from_ < to_ || from_ < 0);
56 assert(scale_ != 0);
57 }
58
59 Eigen::VectorXd Scaling::inverse_eval(const Eigen::VectorXd &y)
60 {
61 if (from_ >= 0)
62 {
63 Eigen::VectorXd res = y;
64 res.segment(from_, to_ - from_) = y.segment(from_, to_ - from_).array() / scale_;
65 return res;
66 }
67 else
68 return y.array() / scale_;
69 }
70
71 Eigen::VectorXd Scaling::eval(const Eigen::VectorXd &x) const
72 {
73 if (from_ >= 0)
74 {
75 Eigen::VectorXd y = x;
76 y.segment(from_, to_ - from_) = x.segment(from_, to_ - from_).array() * scale_;
77 return y;
78 }
79 else
80 return x.array() * scale_;
81 }
82
83 Eigen::VectorXd Scaling::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
84 {
85 if (from_ >= 0)
86 {
87 Eigen::VectorXd res = grad.array();
88 res.segment(from_, to_ - from_) = scale_ * grad.segment(from_, to_ - from_).array();
89 return res;
90 }
91 else
92 return scale_ * grad.array();
93 }
94
95 Eigen::VectorXd PowerMap::inverse_eval(const Eigen::VectorXd &y)
96 {
97 if (from_ >= 0)
98 {
99 Eigen::VectorXd res = y;
100 res.segment(from_, to_ - from_) = y.segment(from_, to_ - from_).array().pow(1. / power_);
101 return res;
102 }
103 else
104 return y.array().pow(1. / power_);
105 }
106
107 Eigen::VectorXd PowerMap::eval(const Eigen::VectorXd &x) const
108 {
109 if (from_ >= 0)
110 {
111 Eigen::VectorXd y = x;
112 y.segment(from_, to_ - from_) = x.segment(from_, to_ - from_).array().pow(power_);
113 return y;
114 }
115 else
116 return x.array().pow(power_);
117 }
118
119 Eigen::VectorXd PowerMap::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
120 {
121 if (from_ >= 0)
122 {
123 Eigen::VectorXd res = grad;
124 res.segment(from_, to_ - from_) = grad.segment(from_, to_ - from_).array() * x.segment(from_, to_ - from_).array().pow(power_ - 1) * power_;
125 return res;
126 }
127 else
128 return grad.array() * x.array().pow(power_ - 1) * power_;
129 }
130
131 ENu2LambdaMu::ENu2LambdaMu(const bool is_volume)
132 : is_volume_(is_volume)
133 {
134 }
135
136 Eigen::VectorXd ENu2LambdaMu::inverse_eval(const Eigen::VectorXd &y)
137 {
138 const int size = y.size() / 2;
139 assert(size * 2 == y.size());
140
141 Eigen::VectorXd x(y.size());
142 for (int i = 0; i < size; i++)
143 {
144 x(i) = convert_to_E(is_volume_, y(i), y(i + size));
145 x(i + size) = convert_to_nu(is_volume_, y(i), y(i + size));
146 }
147
148 return x;
149 }
150
151 Eigen::VectorXd ENu2LambdaMu::eval(const Eigen::VectorXd &x) const
152 {
153 const int size = x.size() / 2;
154 assert(size * 2 == x.size());
155
156 Eigen::VectorXd y;
157 y.setZero(x.size());
158 for (int i = 0; i < size; i++)
159 {
160 y(i) = convert_to_lambda(is_volume_, x(i), x(i + size));
161 y(i + size) = convert_to_mu(x(i), x(i + size));
162 }
163
164 return y;
165 }
166
167 Eigen::VectorXd ENu2LambdaMu::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
168 {
169 const int size = grad.size() / 2;
170 assert(size * 2 == grad.size());
171 assert(size * 2 == x.size());
172
173 Eigen::VectorXd grad_E_nu;
174 grad_E_nu.setZero(grad.size());
175 for (int i = 0; i < size; i++)
176 {
177 const Eigen::Matrix2d jacobian = d_lambda_mu_d_E_nu(is_volume_, x(i), x(i + size));
178 grad_E_nu(i) = grad(i) * jacobian(0, 0) + grad(i + size) * jacobian(1, 0);
179 grad_E_nu(i + size) = grad(i) * jacobian(0, 1) + grad(i + size) * jacobian(1, 1);
180 }
181
182 return grad_E_nu;
183 }
184
185 PerBody2PerNode::PerBody2PerNode(const mesh::Mesh &mesh, const std::vector<basis::ElementBases> &bases, const int n_bases) : mesh_(mesh), bases_(bases), full_size_(n_bases)
186 {
187 reduced_size_ = 0;
188 std::map<int, int> body_id_map;
189 for (int e = 0; e < mesh.n_elements(); e++)
190 {
191 const int body_id = mesh.get_body_id(e);
192 if (!body_id_map.count(body_id))
193 {
194 body_id_map[body_id] = reduced_size_;
196 }
197 }
198 logger().info("{} objects found!", reduced_size_);
199
201 node_id_to_body_id_.setConstant(-1);
202 for (int e = 0; e < bases.size(); e++)
203 {
204 const int body_id = mesh_.get_body_id(e);
205 const int id = body_id_map.at(body_id);
206 for (const auto &bs : bases[e].bases)
207 {
208 for (const auto &g : bs.global())
209 {
210 if (node_id_to_body_id_(g.index) < 0)
211 node_id_to_body_id_(g.index) = id;
212 else if (node_id_to_body_id_(g.index) != id)
213 log_and_throw_adjoint_error("Same node on different bodies!");
214 }
215 }
216 }
217 }
218
219 Eigen::VectorXd PerBody2PerNode::eval(const Eigen::VectorXd &x) const
220 {
221 Eigen::VectorXd y;
222 y.setZero(size(x.size()));
223 const int dim = x.size() / reduced_size_;
224
225 for (int i = 0; i < full_size_; i++)
226 {
227 for (int d = 0; d < dim; d++)
228 {
229 y(i * dim + d) = x(node_id_to_body_id_(i) * dim + d);
230 }
231 }
232
233 return y;
234 }
235
236 int PerBody2PerNode::size(const int x_size) const
237 {
238 assert(x_size % reduced_size_ == 0);
239 return (x_size / reduced_size_) * full_size_;
240 }
241
242 Eigen::VectorXd PerBody2PerNode::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
243 {
244 assert(grad.size() == size(x.size()));
245 Eigen::VectorXd grad_body;
246 grad_body.setZero(x.size());
247 const int dim = x.size() / reduced_size_;
248
249 for (int i = 0; i < full_size_; i++)
250 {
251 for (int d = 0; d < dim; d++)
252 {
253 grad_body(node_id_to_body_id_(i) * dim + d) += grad(i * dim + d);
254 }
255 }
256
257 return grad_body;
258 }
259
260 PerBody2PerElem::PerBody2PerElem(const mesh::Mesh &mesh) : mesh_(mesh), full_size_(mesh_.n_elements())
261 {
262 reduced_size_ = 0;
263 for (int e = 0; e < mesh.n_elements(); e++)
264 {
265 const int body_id = mesh.get_body_id(e);
266 if (!body_id_map_.count(body_id))
267 {
268 body_id_map_[body_id] = {{e, reduced_size_}};
270 }
271 }
272 logger().info("{} objects found!", reduced_size_);
273 }
274
275 Eigen::VectorXd PerBody2PerElem::eval(const Eigen::VectorXd &x) const
276 {
277 Eigen::VectorXd y;
278 y.setZero(size(x.size()));
279
280 for (int e = 0; e < mesh_.n_elements(); e++)
281 {
282 const int body_id = mesh_.get_body_id(e);
283 const auto &entry = body_id_map_.at(body_id);
284 // y(e) = x(entry[1]);
285 y(Eigen::seq(e, y.size() - 1, full_size_)) = x(Eigen::seq(entry[1], x.size() - 1, reduced_size_));
286 }
287
288 return y;
289 }
290
291 int PerBody2PerElem::size(const int x_size) const
292 {
293 assert(x_size % reduced_size_ == 0);
294 return (x_size / reduced_size_) * full_size_;
295 }
296
297 Eigen::VectorXd PerBody2PerElem::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
298 {
299 assert(grad.size() == size(x.size()));
300 Eigen::VectorXd grad_body;
301 grad_body.setZero(x.size());
302
303 for (int e = 0; e < mesh_.n_elements(); e++)
304 {
305 const int body_id = mesh_.get_body_id(e);
306 const auto &entry = body_id_map_.at(body_id);
307 // grad_body(entry[1]) += grad(e);
308 grad_body(Eigen::seq(entry[1], x.size() - 1, reduced_size_)) += grad(Eigen::seq(e, grad.size() - 1, full_size_));
309 }
310
311 return grad_body;
312 }
313
314 SliceMap::SliceMap(const int from, const int to, const int total) : from_(from), to_(to), total_(total)
315 {
316 if (to_ - from_ < 0)
317 log_and_throw_adjoint_error("Invalid Slice Map input!");
318 }
319
320 Eigen::VectorXd SliceMap::inverse_eval(const Eigen::VectorXd &y)
321 {
322 if (total_ == -1)
323 return y;
324 else
325 {
326 if (y.size() != size(0))
327 log_and_throw_adjoint_error("Inverse eval on SliceMap is inconsistent in size!");
328 Eigen::VectorXd y_;
329 y_.setZero(total_);
330 y_.segment(from_, to_ - from_) = y;
331 return y_;
332 }
333 }
334
335 Eigen::VectorXd SliceMap::eval(const Eigen::VectorXd &x) const
336 {
337 return x.segment(from_, to_ - from_);
338 }
339 Eigen::VectorXd SliceMap::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
340 {
341 Eigen::VectorXd grad_full;
342 grad_full.setZero(x.size());
343 grad_full.segment(from_, to_ - from_) = grad;
344 return grad_full;
345 }
346
347 InsertConstantMap::InsertConstantMap(const int size, const double val, const int start_index) : start_index_(start_index)
348 {
349 if (size <= 0)
350 log_and_throw_adjoint_error("Invalid InsertConstantMap input!");
351 values_.setConstant(size, val);
352 }
353
354 InsertConstantMap::InsertConstantMap(const Eigen::VectorXd &values, const int start_index) : start_index_(start_index), values_(values)
355 {
356 }
357
358 int InsertConstantMap::size(const int x_size) const
359 {
360 return x_size + values_.size();
361 }
362
363 Eigen::VectorXd InsertConstantMap::inverse_eval(const Eigen::VectorXd &y)
364 {
365 if (start_index_ >= 0)
366 {
367 Eigen::VectorXd x(y.size() - values_.size());
368 if (start_index_ > 0)
369 x.head(start_index_) = y.head(start_index_);
370 x.tail(x.size() - start_index_) = y.tail(y.size() - start_index_ - values_.size());
371 return x;
372 }
373 else
374 return y.head(y.size() - values_.size());
375 }
376
377 Eigen::VectorXd InsertConstantMap::eval(const Eigen::VectorXd &x) const
378 {
379 Eigen::VectorXd y;
380 y.setZero(size(x.size()));
381 if (start_index_ >= 0)
382 {
383 if (start_index_ > 0)
384 y.head(start_index_) = x.head(start_index_);
385 y.segment(start_index_, values_.size()) = values_;
386 y.tail(y.size() - start_index_ - values_.size()) = x.tail(x.size() - start_index_);
387 }
388 else
389 y << x, values_;
390
391 return y;
392 }
393 Eigen::VectorXd InsertConstantMap::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
394 {
395 assert(x.size() == grad.size() - values_.size());
396 Eigen::VectorXd reduced_grad(grad.size() - values_.size());
397 if (start_index_ >= 0)
398 {
399 if (start_index_ > 0)
400 reduced_grad.head(start_index_) = grad.head(start_index_);
401 reduced_grad.tail(reduced_grad.size() - start_index_) = grad.tail(grad.size() - start_index_ - values_.size());
402 }
403 else
404 reduced_grad = grad.head(grad.size() - values_.size());
405
406 return reduced_grad;
407 }
408
409 LinearFilter::LinearFilter(const mesh::Mesh &mesh, const double radius)
410 {
411 std::vector<Eigen::Triplet<double>> tt_adjacency_list;
412
413 Eigen::MatrixXd barycenters;
414 if (mesh.is_volume())
415 mesh.cell_barycenters(barycenters);
416 else
417 mesh.face_barycenters(barycenters);
418
419 RowVectorNd min, max;
420 mesh.bounding_box(min, max);
421 // TODO: more efficient way
422 for (int i = 0; i < barycenters.rows(); i++)
423 {
424 auto center_i = barycenters.row(i);
425 for (int j = 0; j <= i; j++)
426 {
427 auto center_j = barycenters.row(j);
428 double dist = 0;
429 dist = (center_i - center_j).norm();
430 if (dist < radius)
431 {
432 tt_adjacency_list.emplace_back(i, j, radius - dist);
433 if (i != j)
434 tt_adjacency_list.emplace_back(j, i, radius - dist);
435 }
436 }
437 }
438 tt_radius_adjacency.resize(barycenters.rows(), barycenters.rows());
439 tt_radius_adjacency.setFromTriplets(tt_adjacency_list.begin(), tt_adjacency_list.end());
440
442 for (int i = 0; i < tt_radius_adjacency.rows(); i++)
444 }
445
446 Eigen::VectorXd LinearFilter::eval(const Eigen::VectorXd &x) const
447 {
448 assert(x.size() == tt_radius_adjacency.rows());
449 return (tt_radius_adjacency * x).array() / tt_radius_adjacency_row_sum.array();
450 }
451
452 Eigen::VectorXd LinearFilter::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
453 {
454 assert(x.size() == tt_radius_adjacency.rows());
455 return (tt_radius_adjacency * grad).array() / tt_radius_adjacency_row_sum.array();
456 }
457
458 Eigen::VectorXd ScalarVelocityParametrization::inverse_eval(const Eigen::VectorXd &y)
459 {
460 Eigen::VectorXd x;
461 x.setZero(size(y.size()));
462 x(0) = (y(0) - start_val_) / dt_;
463 for (int i = 1; i < x.size(); ++i)
464 x(i) = (y(i) - y(i - 1)) / dt_;
465 return x;
466 }
467
468 Eigen::VectorXd ScalarVelocityParametrization::eval(const Eigen::VectorXd &x) const
469 {
470 Eigen::VectorXd y;
471 y.setZero(size(x.size()));
472 y(0) = start_val_ + dt_ * x(0);
473 for (int i = 1; i < x.size(); ++i)
474 y(i) = y(i - 1) + dt_ * x(i);
475 return y;
476 }
477
478 Eigen::VectorXd ScalarVelocityParametrization::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
479 {
480 assert(x.size() == grad.size());
481
482 Eigen::MatrixXd hess;
483 hess.setZero(x.size(), size(x.size()));
484 for (int i = 0; i < hess.rows(); ++i)
485 for (int j = 0; j <= i; ++j)
486 hess(i, j) = dt_;
487
488 return hess.transpose() * grad;
489 }
490
491} // namespace polyfem::solver
double val
Definition Assembler.cpp:86
int y
int x
Abstract mesh class to capture 2d/3d conforming and non-conforming meshes.
Definition Mesh.hpp:40
int n_elements() const
utitlity to return the number of elements, cells or faces in 3d and 2d
Definition Mesh.hpp:162
virtual int get_body_id(const int primitive) const
Get the volume selection of an element (cell in 3d, face in 2d)
Definition Mesh.hpp:507
void cell_barycenters(Eigen::MatrixXd &barycenters) const
all cells barycenters
Definition Mesh.cpp:320
virtual void bounding_box(RowVectorNd &min, RowVectorNd &max) const =0
computes the bbox of the mesh
void face_barycenters(Eigen::MatrixXd &barycenters) const
all face barycenters
Definition Mesh.cpp:311
virtual bool is_volume() const =0
checks if mesh is volume
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
int size(const int x_size) const override
Compute DOF of y given DOF of x.
ENu2LambdaMu(const bool is_volume)
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) override
Eval x = f^-1 (y).
ExponentialMap(const int from=-1, const int to=-1)
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) override
Eval x = f^-1 (y).
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
int size(const int x_size) const override
Compute DOF of y given DOF of x.
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
InsertConstantMap(const int size=-1, const double val=0, const int start_index=-1)
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) override
Eval x = f^-1 (y).
Eigen::SparseMatrix< double > tt_radius_adjacency
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
LinearFilter(const mesh::Mesh &mesh, const double radius)
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
PerBody2PerElem(const mesh::Mesh &mesh)
int size(const int x_size) const override
Compute DOF of y given DOF of x.
std::map< int, std::array< int, 2 > > body_id_map_
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
int size(const int x_size) const override
Compute DOF of y given DOF of x.
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
PerBody2PerNode(const mesh::Mesh &mesh, const std::vector< basis::ElementBases > &bases, const int n_bases)
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) override
Eval x = f^-1 (y).
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
int size(const int x_size) const override
Compute DOF of y given DOF of x.
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) override
Eval x = f^-1 (y).
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
Scaling(const double scale, const int from=-1, const int to=-1)
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) override
Eval x = f^-1 (y).
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
Apply jacobian for chain rule.
SliceMap(const int from=-1, const int to=-1, const int total=-1)
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) override
Eval x = f^-1 (y).
int size(const int x_size) const override
Compute DOF of y given DOF of x.
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eval y = f(x).
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:44
double convert_to_E(const bool is_volume, const double lambda, const double mu)
double convert_to_mu(const double E, const double nu)
void log_and_throw_adjoint_error(const std::string &msg)
Definition Logger.cpp:79
double convert_to_nu(const bool is_volume, const double lambda, const double mu)
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
double convert_to_lambda(const bool is_volume, const double E, const double nu)
Eigen::Matrix2d d_lambda_mu_d_E_nu(const bool is_volume, const double E, const double nu)