PolyFEM
Loading...
Searching...
No Matches
ElasticForm.cpp
Go to the documentation of this file.
1#include "ElasticForm.hpp"
2
9
10using namespace polyfem::assembler;
11using namespace polyfem::utils;
12
13namespace polyfem::solver
14{
15 namespace
16 {
17 class LocalThreadVecStorage
18 {
19 public:
20 Eigen::MatrixXd vec;
23
24 LocalThreadVecStorage(const int size)
25 {
26 vec.resize(size, 1);
27 vec.setZero();
28 }
29 };
30
31 double dot(const Eigen::MatrixXd &A, const Eigen::MatrixXd &B) { return (A.array() * B.array()).sum(); }
32 } // namespace
33
34 ElasticForm::ElasticForm(const int n_bases,
35 const std::vector<basis::ElementBases> &bases,
36 const std::vector<basis::ElementBases> &geom_bases,
37 const assembler::Assembler &assembler,
38 const assembler::AssemblyValsCache &ass_vals_cache,
39 const double t, const double dt,
40 const bool is_volume)
41 : n_bases_(n_bases),
42 bases_(bases),
43 geom_bases_(geom_bases),
44 assembler_(assembler),
45 ass_vals_cache_(ass_vals_cache),
46 t_(t),
47 dt_(dt),
48 is_volume_(is_volume)
49 {
52 // mat_cache_ = std::make_unique<utils::DenseMatrixCache>();
53 mat_cache_ = std::make_unique<utils::SparseMatrixCache>();
54 }
55
56 double ElasticForm::value_unweighted(const Eigen::VectorXd &x) const
57 {
61 }
62
63 Eigen::VectorXd ElasticForm::value_per_element_unweighted(const Eigen::VectorXd &x) const
64 {
65 const Eigen::VectorXd out = assembler_.assemble_energy_per_element(
67 assert(abs(out.sum() - value_unweighted(x)) < std::max(1e-10 * out.sum(), 1e-10));
68 return out;
69 }
70
71 void ElasticForm::first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
72 {
73 Eigen::MatrixXd grad;
75 ass_vals_cache_, t_, dt_, x, x_prev_, grad);
76 gradv = grad;
77 }
78
79 void ElasticForm::second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const
80 {
81 POLYFEM_SCOPED_TIMER("elastic hessian");
82
83 hessian.resize(x.size(), x.size());
84
86 {
87 assert(cached_stiffness_.rows() == x.size() && cached_stiffness_.cols() == x.size());
88 hessian = cached_stiffness_;
89 }
90 else
91 {
92 // NOTE: mat_cache_ is marked as mutable so we can modify it here
96 }
97 }
98
99 bool ElasticForm::is_step_valid(const Eigen::VectorXd &, const Eigen::VectorXd &x1) const
100 {
101 Eigen::VectorXd grad;
102 first_derivative(x1, grad);
103
104 if (grad.array().isNaN().any())
105 return false;
106
107 // Check the scalar field in the output does not contain NANs.
108 // WARNING: Does not work because the energy is not evaluated at the same quadrature points.
109 // This causes small step lengths in the LS.
110 // TVector x1_full;
111 // reduced_to_full(x1, x1_full);
112 // return state_.check_scalar_value(x1_full, true, false);
113 return true;
114 }
115
124
125 void ElasticForm::force_material_derivative(const double t, const Eigen::MatrixXd &x, const Eigen::MatrixXd &x_prev, const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
126 {
127 const int dim = is_volume_ ? 3 : 2;
128
129 const int n_elements = int(bases_.size());
130
131 if (assembler_.name() == "ViscousDamping")
132 {
133 term.setZero(2);
134
135 auto storage = utils::create_thread_storage(LocalThreadVecStorage(term.size()));
136
137 utils::maybe_parallel_for(n_elements, [&](int start, int end, int thread_id) {
138 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
139
140 for (int e = start; e < end; ++e)
141 {
142 assembler::ElementAssemblyValues &vals = local_storage.vals;
144
146 local_storage.da = vals.det.array() * quadrature.weights.array();
147
148 Eigen::MatrixXd u, grad_u, prev_u, prev_grad_u, p, grad_p;
149 io::Evaluator::interpolate_at_local_vals(e, dim, dim, vals, x, u, grad_u);
150 io::Evaluator::interpolate_at_local_vals(e, dim, dim, vals, x_prev, prev_u, prev_grad_u);
151 io::Evaluator::interpolate_at_local_vals(e, dim, dim, vals, adjoint, p, grad_p);
152
153 for (int q = 0; q < local_storage.da.size(); ++q)
154 {
155 Eigen::MatrixXd grad_p_i, grad_u_i, prev_grad_u_i;
156 vector2matrix(grad_p.row(q), grad_p_i);
157 vector2matrix(grad_u.row(q), grad_u_i);
158 vector2matrix(prev_grad_u.row(q), prev_grad_u_i);
159
160 Eigen::MatrixXd f_prime_dpsi, f_prime_dphi;
161 assembler::ViscousDamping::compute_dstress_dpsi_dphi(OptAssemblerData(t, dt_, e, quadrature.points.row(q), vals.val.row(q), grad_u_i), prev_grad_u_i, f_prime_dpsi, f_prime_dphi);
162
163 // This needs to be a sum over material parameter basis.
164 local_storage.vec(0) += -dot(f_prime_dpsi, grad_p_i) * local_storage.da(q);
165 local_storage.vec(1) += -dot(f_prime_dphi, grad_p_i) * local_storage.da(q);
166 }
167 }
168 });
169
170 for (const LocalThreadVecStorage &local_storage : storage)
171 term += local_storage.vec;
172 }
173 else
174 {
175 term.setZero(n_elements * 2, 1);
176
177 auto storage = utils::create_thread_storage(LocalThreadVecStorage(term.size()));
178
179 utils::maybe_parallel_for(n_elements, [&](int start, int end, int thread_id) {
180 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
181
182 for (int e = start; e < end; ++e)
183 {
184 assembler::ElementAssemblyValues &vals = local_storage.vals;
186
188 local_storage.da = vals.det.array() * quadrature.weights.array();
189
190 Eigen::MatrixXd u, grad_u, p, grad_p;
191 io::Evaluator::interpolate_at_local_vals(e, dim, dim, vals, x, u, grad_u);
192 io::Evaluator::interpolate_at_local_vals(e, dim, dim, vals, adjoint, p, grad_p);
193
194 for (int q = 0; q < local_storage.da.size(); ++q)
195 {
196 Eigen::MatrixXd grad_p_i, grad_u_i;
197 vector2matrix(grad_p.row(q), grad_p_i);
198 vector2matrix(grad_u.row(q), grad_u_i);
199
200 Eigen::MatrixXd f_prime_dmu, f_prime_dlambda;
201 assembler_.compute_dstress_dmu_dlambda(OptAssemblerData(t, dt_, e, quadrature.points.row(q), vals.val.row(q), grad_u_i), f_prime_dmu, f_prime_dlambda);
202
203 // This needs to be a sum over material parameter basis.
204 local_storage.vec(e + n_elements) += -dot(f_prime_dmu, grad_p_i) * local_storage.da(q);
205 local_storage.vec(e) += -dot(f_prime_dlambda, grad_p_i) * local_storage.da(q);
206 }
207 }
208 });
209
210 for (const LocalThreadVecStorage &local_storage : storage)
211 term += local_storage.vec;
212 }
213 }
214
215 void ElasticForm::force_shape_derivative(const double t, const int n_verts, const Eigen::MatrixXd &x, const Eigen::MatrixXd &x_prev, const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
216 {
217 const int dim = is_volume_ ? 3 : 2;
218 const int actual_dim = (assembler_.name() == "Laplacian") ? 1 : dim;
219
220 const int n_elements = int(bases_.size());
221 term.setZero(n_verts * dim, 1);
222
223 auto storage = utils::create_thread_storage(LocalThreadVecStorage(term.size()));
224
225 if (assembler_.name() == "ViscousDamping")
226 {
227 utils::maybe_parallel_for(n_elements, [&](int start, int end, int thread_id) {
228 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
229
230 for (int e = start; e < end; ++e)
231 {
232 assembler::ElementAssemblyValues &vals = local_storage.vals;
236
238 local_storage.da = vals.det.array() * quadrature.weights.array();
239
240 Eigen::MatrixXd u, grad_u, prev_u, prev_grad_u, p, grad_p;
241 io::Evaluator::interpolate_at_local_vals(e, dim, dim, vals, x, u, grad_u);
242 io::Evaluator::interpolate_at_local_vals(e, dim, dim, vals, x_prev, prev_u, prev_grad_u);
243 io::Evaluator::interpolate_at_local_vals(e, dim, dim, vals, adjoint, p, grad_p);
244
245 Eigen::MatrixXd grad_u_i, grad_p_i, prev_grad_u_i;
246 Eigen::MatrixXd grad_v_i;
247 Eigen::MatrixXd stress_tensor, f_prime_gradu_gradv;
248 Eigen::MatrixXd f_prev_prime_prev_gradu_gradv;
249
250 for (int q = 0; q < local_storage.da.size(); ++q)
251 {
252 vector2matrix(grad_u.row(q), grad_u_i);
253 vector2matrix(grad_p.row(q), grad_p_i);
254 vector2matrix(prev_grad_u.row(q), prev_grad_u_i);
255
256 for (auto &v : gvals.basis_values)
257 {
258 Eigen::MatrixXd stress_grad, stress_prev_grad;
259 assembler_.compute_stress_grad(OptAssemblerData(t, dt_, e, quadrature.points.row(q), vals.val.row(q), grad_u_i), prev_grad_u_i, stress_tensor, stress_grad);
260 assembler_.compute_stress_prev_grad(OptAssemblerData(t, dt_, e, quadrature.points.row(q), vals.val.row(q), grad_u_i), prev_grad_u_i, stress_prev_grad);
261 for (int d = 0; d < dim; d++)
262 {
263 grad_v_i.setZero(dim, dim);
264 grad_v_i.row(d) = v.grad_t_m.row(q);
265
266 f_prime_gradu_gradv.setZero(dim, dim);
267 Eigen::MatrixXd tmp = grad_u_i * grad_v_i;
268 for (int i = 0; i < f_prime_gradu_gradv.rows(); i++)
269 for (int j = 0; j < f_prime_gradu_gradv.cols(); j++)
270 for (int k = 0; k < tmp.rows(); k++)
271 for (int l = 0; l < tmp.cols(); l++)
272 f_prime_gradu_gradv(i, j) += stress_grad(i * dim + j, k * dim + l) * tmp(k, l);
273
274 f_prev_prime_prev_gradu_gradv.setZero(dim, dim);
275 tmp = prev_grad_u_i * grad_v_i;
276 for (int i = 0; i < f_prev_prime_prev_gradu_gradv.rows(); i++)
277 for (int j = 0; j < f_prev_prime_prev_gradu_gradv.cols(); j++)
278 for (int k = 0; k < tmp.rows(); k++)
279 for (int l = 0; l < tmp.cols(); l++)
280 f_prev_prime_prev_gradu_gradv(i, j) += stress_prev_grad(i * dim + j, k * dim + l) * tmp(k, l);
281
282 tmp = grad_v_i - grad_v_i.trace() * Eigen::MatrixXd::Identity(dim, dim);
283 local_storage.vec(v.global[0].index * dim + d) -= dot(f_prime_gradu_gradv + f_prev_prime_prev_gradu_gradv + stress_tensor * tmp.transpose(), grad_p_i) * local_storage.da(q);
284 }
285 }
286 }
287 }
288 });
289 }
290 else
291 {
292 utils::maybe_parallel_for(n_elements, [&](int start, int end, int thread_id) {
293 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
294
295 for (int e = start; e < end; ++e)
296 {
297 assembler::ElementAssemblyValues &vals = local_storage.vals;
301
303 local_storage.da = vals.det.array() * quadrature.weights.array();
304
305 Eigen::MatrixXd u, grad_u, p, grad_p; //, stiffnesses;
306 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim, vals, x, u, grad_u);
307 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim, vals, adjoint, p, grad_p);
308 // assembler_.compute_stiffness_value(formulation_, vals, quadrature.points, x, stiffnesses);
309
310 for (int q = 0; q < local_storage.da.size(); ++q)
311 {
312 Eigen::MatrixXd grad_u_i, grad_p_i, stiffness_i;
313 if (actual_dim == 1)
314 {
315 grad_u_i = grad_u.row(q);
316 grad_p_i = grad_p.row(q);
317 }
318 else
319 {
320 vector2matrix(grad_u.row(q), grad_u_i);
321 vector2matrix(grad_p.row(q), grad_p_i);
322 }
323
324 // stiffness_i = utils::unflatten(stiffnesses.row(q).transpose(), actual_dim * dim);
325
326 for (auto &v : gvals.basis_values)
327 {
328 for (int d = 0; d < dim; d++)
329 {
330 Eigen::MatrixXd grad_v_i;
331 grad_v_i.setZero(dim, dim);
332 grad_v_i.row(d) = v.grad_t_m.row(q);
333
334 Eigen::MatrixXd stress_tensor, f_prime_gradu_gradv;
335 assembler_.compute_stress_grad_multiply_mat(OptAssemblerData(t, dt_, e, quadrature.points.row(q), vals.val.row(q), grad_u_i), grad_u_i * grad_v_i, stress_tensor, f_prime_gradu_gradv);
336 // f_prime_gradu_gradv = utils::unflatten(stiffness_i * utils::flatten(grad_u_i * grad_v_i), dim);
337
338 Eigen::MatrixXd tmp = grad_v_i - grad_v_i.trace() * Eigen::MatrixXd::Identity(dim, dim);
339 local_storage.vec(v.global[0].index * dim + d) -= dot(f_prime_gradu_gradv + stress_tensor * tmp.transpose(), grad_p_i) * local_storage.da(q);
340 }
341 }
342 }
343 }
344 });
345 }
346
347 for (const LocalThreadVecStorage &local_storage : storage)
348 term += local_storage.vec;
349 }
350} // namespace polyfem::solver
Eigen::MatrixXd vec
Definition Assembler.cpp:72
QuadratureVector da
Definition Assembler.cpp:23
ElementAssemblyValues vals
Definition Assembler.cpp:22
assembler::ElementAssemblyValues gvals
Definition BodyForm.cpp:25
Quadrature quadrature
int x
#define POLYFEM_SCOPED_TIMER(...)
Definition Timer.hpp:10
virtual void assemble(const bool is_volume, const int n_basis, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, StiffnessMatrix &stiffness, const bool is_mass=false) const
Definition Assembler.hpp:68
virtual void compute_dstress_dmu_dlambda(const OptAssemblerData &data, Eigen::MatrixXd &dstress_dmu, Eigen::MatrixXd &dstress_dlambda) const
virtual void compute_stress_grad_multiply_mat(const OptAssemblerData &data, const Eigen::MatrixXd &mat, Eigen::MatrixXd &stress, Eigen::MatrixXd &result) const
virtual std::string name() const =0
virtual Eigen::VectorXd assemble_energy_per_element(const bool is_volume, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, const double dt, const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev) const
Definition Assembler.hpp:89
virtual void assemble_gradient(const bool is_volume, const int n_basis, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, const double dt, const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev, Eigen::MatrixXd &rhs) const
virtual void compute_stress_grad(const OptAssemblerData &data, const Eigen::MatrixXd &prev_grad_u_i, Eigen::MatrixXd &stress, Eigen::MatrixXd &result) const
virtual void assemble_hessian(const bool is_volume, const int n_basis, const bool project_to_psd, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, const double dt, const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev, utils::MatrixCache &mat_cache, StiffnessMatrix &grad) const
virtual double assemble_energy(const bool is_volume, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, const double dt, const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev) const
Definition Assembler.hpp:79
virtual bool is_linear() const =0
virtual void compute_stress_prev_grad(const OptAssemblerData &data, const Eigen::MatrixXd &prev_grad_u_i, Eigen::MatrixXd &result) const
Caches basis evaluation and geometric mapping at every element.
void compute(const int el_index, const bool is_volume, const basis::ElementBases &basis, const basis::ElementBases &gbasis, ElementAssemblyValues &vals) const
retrieves cached basis evaluation and geometric for the given element if it doesn't exist,...
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,...
static void compute_dstress_dpsi_dphi(const OptAssemblerData &data, const Eigen::MatrixXd &prev_grad_u_i, Eigen::MatrixXd &dstress_dpsi, Eigen::MatrixXd &dstress_dphi)
static void interpolate_at_local_vals(const mesh::Mesh &mesh, const bool is_problem_scalar, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const int el_index, const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &fun, Eigen::MatrixXd &result, Eigen::MatrixXd &result_grad)
interpolate solution and gradient at element (calls interpolate_at_local_vals with sol)
const assembler::Assembler & assembler_
Reference to the assembler.
void second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const override
Compute the second derivative of the value wrt x.
void compute_cached_stiffness()
Compute the stiffness matrix (cached)
std::unique_ptr< utils::MatrixCache > mat_cache_
Matrix cache (mutable because it is modified in second_derivative_unweighted)
ElasticForm(const int n_bases, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &geom_bases, const assembler::Assembler &assembler, const assembler::AssemblyValsCache &ass_vals_cache, const double t, const double dt, const bool is_volume)
Construct a new Elastic Form object.
void force_material_derivative(const double t, const Eigen::MatrixXd &x, const Eigen::MatrixXd &x_prev, const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
Compute the derivative of the force wrt lame/damping parameters, then multiply the resulting matrix w...
void force_shape_derivative(const double t, const int n_verts, const Eigen::MatrixXd &x, const Eigen::MatrixXd &x_prev, const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
Compute the derivative of the force wrt vertex positions, then multiply the resulting matrix with adj...
virtual double value_unweighted(const Eigen::VectorXd &x) const override
Compute the elastic potential value.
const std::vector< basis::ElementBases > & geom_bases_
StiffnessMatrix cached_stiffness_
Cached stiffness matrix for linear elasticity.
virtual void first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
Compute the first derivative of the value wrt x.
const assembler::AssemblyValsCache & ass_vals_cache_
Eigen::VectorXd value_per_element_unweighted(const Eigen::VectorXd &x) const override
Compute the value of the form multiplied with the weigth.
const std::vector< basis::ElementBases > & bases_
bool is_step_valid(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) const override
Determine if a step from solution x0 to solution x1 is allowed.
virtual void first_derivative(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
Compute the first derivative of the value wrt x multiplied with the weigth.
Definition Form.hpp:40
bool project_to_psd_
If true, the form's second derivative is projected to be positive semidefinite.
Definition Form.hpp:146
Used for test only.
void vector2matrix(const Eigen::VectorXd &vec, Eigen::MatrixXd &mat)
auto & get_local_thread_storage(Storages &storage, int thread_id)
auto create_thread_storage(const LocalStorage &initial_local_storage)
void maybe_parallel_for(int size, const std::function< void(int, int, int)> &partial_for)
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, MAX_QUAD_POINTS, 1 > QuadratureVector
Definition Types.hpp:15
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:20