PolyFEM
Loading...
Searching...
No Matches
BodyForm.cpp
Go to the documentation of this file.
1#include "BodyForm.hpp"
2
6
10
12
14
16
17namespace polyfem::solver
18{
19 namespace
20 {
21 class LocalThreadVecStorage
22 {
23 public:
24 Eigen::MatrixXd vec;
25 assembler::ElementAssemblyValues vals, gvals;
27
28 LocalThreadVecStorage(const int size)
29 {
30 vec.resize(size, 1);
31 vec.setZero();
32 }
33 };
34 } // namespace
35 BodyForm::BodyForm(const int ndof,
36 const int n_pressure_bases,
37 const std::vector<int> &boundary_nodes,
38 const std::vector<mesh::LocalBoundary> &local_boundary,
39 const std::vector<mesh::LocalBoundary> &local_neumann_boundary,
40 const int n_boundary_samples,
41 const Eigen::MatrixXd &rhs,
42 const assembler::RhsAssembler &rhs_assembler,
43 const assembler::Density &density,
44 const bool apply_DBC,
45 const bool is_formulation_mixed,
46 const bool is_time_dependent)
47 : ndof_(ndof),
48 n_pressure_bases_(n_pressure_bases),
49 boundary_nodes_(boundary_nodes),
50 local_boundary_(local_boundary),
51 local_neumann_boundary_(local_neumann_boundary),
52 n_boundary_samples_(n_boundary_samples),
53 rhs_(rhs),
54 rhs_assembler_(rhs_assembler),
55 density_(density),
56 apply_DBC_(apply_DBC),
57 is_formulation_mixed_(is_formulation_mixed)
58 {
59 t_ = 0;
60 if (!is_time_dependent)
61 update_current_rhs(Eigen::VectorXd());
62 }
63
68
69 void BodyForm::first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
70 {
71 // REMEMBER -!!!!!
72 gradv = -current_rhs_;
73 }
74
75 void BodyForm::second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const
76 {
77 hessian.resize(x.size(), x.size());
78 }
79
80 void BodyForm::update_quantities(const double t, const Eigen::VectorXd &x)
81 {
82 this->t_ = t;
83 this->x_prev_ = x;
85 }
86
87 void BodyForm::update_current_rhs(const Eigen::VectorXd &x)
88 {
93
95 {
96 current_rhs_.conservativeResize(
98 current_rhs_.bottomRows(n_pressure_bases_).setZero();
99 }
100
101 // Apply Neumann boundary conditions
103 std::vector<mesh::LocalBoundary>(), std::vector<int>(),
105 current_rhs_, x, t_);
106
107 // Apply Dirichlet boundary conditions
108 if (apply_DBC_)
111 n_boundary_samples_, std::vector<mesh::LocalBoundary>(),
112 current_rhs_, x, t_);
113 }
114
115 void BodyForm::force_shape_derivative(const int n_verts, const double t, const Eigen::MatrixXd &x, const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
116 {
117 const auto &bases = rhs_assembler_.bases();
118 const auto &gbases = rhs_assembler_.gbases();
119 const int dim = rhs_assembler_.mesh().dimension();
120 const int actual_dim = rhs_assembler_.problem().is_scalar() ? 1 : dim;
121
122 const int n_elements = int(bases.size());
123 term.setZero(n_verts * dim, 1);
124
125 auto storage = utils::create_thread_storage(LocalThreadVecStorage(term.size()));
126
127 utils::maybe_parallel_for(n_elements, [&](int start, int end, int thread_id) {
128 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
129
130 for (int e = start; e < end; ++e)
131 {
132 assembler::ElementAssemblyValues &vals = local_storage.vals;
133 rhs_assembler_.ass_vals_cache().compute(e, rhs_assembler_.mesh().is_volume(), bases[e], gbases[e], vals);
134 assembler::ElementAssemblyValues &gvals = local_storage.gvals;
135 gvals.compute(e, rhs_assembler_.mesh().is_volume(), vals.quadrature.points, gbases[e], gbases[e]);
136
137 const quadrature::Quadrature &quadrature = vals.quadrature;
138 local_storage.da = vals.det.array() * quadrature.weights.array();
139
140 Eigen::MatrixXd p, grad_p;
141 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim, vals, adjoint, p, grad_p);
142
143 Eigen::MatrixXd rhs_function;
144 rhs_assembler_.problem().rhs(rhs_assembler_.assembler(), vals.val, t, rhs_function);
145 rhs_function *= -1;
146 for (int q = 0; q < vals.val.rows(); q++)
147 {
148 const double rho = density_(quadrature.points.row(q), vals.val.row(q), t, e);
149 rhs_function.row(q) *= rho;
150 }
151
152 for (int q = 0; q < local_storage.da.size(); ++q)
153 {
154 const double value = p.row(q).dot(rhs_function.row(q)) * local_storage.da(q);
155 for (auto &v : gvals.basis_values)
156 {
157 local_storage.vec.block(v.global[0].index * dim, 0, dim, 1) += value * v.grad_t_m.row(q).transpose();
158 }
159 }
160 }
161 });
162
163 // Zero entries in p that correspond to nodes lying between dirichlet and neumann surfaces
164 // DO NOT PARALLELIZE since they both write to the same location
165 Eigen::MatrixXd adjoint_zeroed = adjoint;
166 adjoint_zeroed(boundary_nodes_, Eigen::all).setZero();
167
168 utils::maybe_parallel_for(local_neumann_boundary_.size(), [&](int start, int end, int thread_id) {
169 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
170
171 Eigen::MatrixXd uv, points, normals;
172 Eigen::VectorXd weights;
173 Eigen::VectorXi global_ids;
174 assembler::ElementAssemblyValues vals, gvals;
175
176 for (int lb_id = start; lb_id < end; ++lb_id)
177 {
178 const auto &lb = local_neumann_boundary_[lb_id];
179 const int e = lb.element_id();
180
181 for (int i = 0; i < lb.size(); i++)
182 {
183 const int global_primitive_id = lb.global_primitive_id(i);
184
185 utils::BoundarySampler::boundary_quadrature(lb, n_boundary_samples_, rhs_assembler_.mesh(), i, false, uv, points, normals, weights);
186 global_ids.setConstant(points.rows(), global_primitive_id);
187
188 Eigen::MatrixXd reference_normals = normals;
189
190 vals.compute(e, rhs_assembler_.mesh().is_volume(), points, bases[e], gbases[e]);
191 gvals.compute(e, rhs_assembler_.mesh().is_volume(), points, gbases[e], gbases[e]);
192
193 std::vector<std::vector<Eigen::MatrixXd>> grad_normal;
194 for (int n = 0; n < gvals.jac_it.size(); ++n)
195 {
196 Eigen::MatrixXd trafo = gvals.jac_it[n].inverse();
197
198 Eigen::MatrixXd deform_mat(dim, dim);
199 deform_mat.setZero();
200 Eigen::MatrixXd jac_mat(dim, gvals.basis_values.size());
201 int b_idx = 0;
202 for (const auto &b : gvals.basis_values)
203 {
204 jac_mat.col(b_idx++) = b.grad.row(n);
205
206 for (const auto &g : b.global)
207 for (int d = 0; d < dim; ++d)
208 deform_mat.row(d) += x(g.index * dim + d) * b.grad.row(n);
209 }
210
211 trafo += deform_mat;
212 trafo = trafo.inverse();
213
214 Eigen::VectorXd displaced_normal = normals.row(n) * trafo;
215 normals.row(n) = displaced_normal / displaced_normal.norm();
216
217 std::vector<Eigen::MatrixXd> grad;
218 {
219 Eigen::MatrixXd vec = -(jac_mat.transpose() * trafo * reference_normals.row(n).transpose());
220 // Gradient of the displaced normal computation
221 for (int k = 0; k < dim; ++k)
222 {
223 Eigen::MatrixXd grad_i(jac_mat.rows(), jac_mat.cols());
224 grad_i.setZero();
225 for (int m = 0; m < jac_mat.rows(); ++m)
226 for (int l = 0; l < jac_mat.cols(); ++l)
227 grad_i(m, l) = -(reference_normals.row(n) * trafo)(m) * (jac_mat.transpose() * trafo)(l, k);
228 grad.push_back(grad_i);
229 }
230 }
231
232 {
233 Eigen::MatrixXd normalization_chain_rule = (normals.row(n).transpose() * normals.row(n));
234 normalization_chain_rule = Eigen::MatrixXd::Identity(dim, dim) - normalization_chain_rule;
235 normalization_chain_rule /= displaced_normal.norm();
236
237 Eigen::VectorXd vec(dim);
238 b_idx = 0;
239 for (const auto &b : gvals.basis_values)
240 {
241 for (int d = 0; d < dim; ++d)
242 {
243 for (int k = 0; k < dim; ++k)
244 vec(k) = grad[k](d, b_idx);
245 vec = normalization_chain_rule * vec;
246 for (int k = 0; k < dim; ++k)
247 grad[k](d, b_idx) = vec(k);
248 }
249 b_idx++;
250 }
251 }
252
253 grad_normal.push_back(grad);
254 }
255
256 Eigen::MatrixXd neumann_val;
257 rhs_assembler_.problem().neumann_bc(rhs_assembler_.mesh(), global_ids, uv, vals.val, normals, t, neumann_val);
258
259 Eigen::MatrixXd p, grad_p;
260 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim, vals, adjoint_zeroed, p, grad_p);
261
262 const Eigen::VectorXi geom_nodes = gbases[e].local_nodes_for_primitive(global_primitive_id, rhs_assembler_.mesh());
263
264 if (geom_nodes.size() != dim)
265 log_and_throw_error("Only linear geometry is supported in shape derivative!");
266
267 Eigen::VectorXd value = (p.array() * neumann_val.array()).rowwise().sum() * weights.array();
268
269 Eigen::VectorXd pressure_bc;
270 pressure_bc = (neumann_val.array() * normals.array()).rowwise().sum();
271
272 for (long n = 0; n < geom_nodes.size(); ++n)
273 {
274 const assembler::AssemblyValues &v = gvals.basis_values[geom_nodes(n)];
275
276 Eigen::MatrixXd velocity_div_mat;
277 {
278 if (rhs_assembler_.mesh().is_volume())
279 {
280 Eigen::MatrixXd V(3, 3);
281 V.row(0) = gbases[e].bases[geom_nodes(0)].global()[0].node;
282 V.row(1) = gbases[e].bases[geom_nodes(1)].global()[0].node;
283 V.row(2) = gbases[e].bases[geom_nodes(2)].global()[0].node;
284
285 velocity_div_mat = AdjointTools::face_velocity_divergence(V);
286 }
287 else
288 {
289 Eigen::MatrixXd V(2, 2);
290 V.row(0) = gbases[e].bases[geom_nodes(0)].global()[0].node;
291 V.row(1) = gbases[e].bases[geom_nodes(1)].global()[0].node;
292
293 velocity_div_mat = AdjointTools::edge_velocity_divergence(V);
294 }
295 }
296
297 // integrate j * div(gbases) over the whole boundary
298 for (int d = 0; d < dim; d++)
299 {
300 assert(v.global.size() == 1);
301 const int g_index = v.global[0].index * dim + d;
302
303 Eigen::MatrixXd gvals(v.val.size(), dim);
304 gvals.setZero();
305 gvals.col(d) = v.val;
306
307 local_storage.vec(g_index) += value.sum() * velocity_div_mat(n, d);
308 const bool is_pressure = rhs_assembler_.problem().is_boundary_pressure(rhs_assembler_.mesh().get_boundary_id(global_primitive_id));
309 // if (is_pressure)
310 // {
311 // for (int q = 0; q < vals.jac_it.size(); ++q)
312 // {
313 // Eigen::MatrixXd grad_x_f(dim, dim);
314 // for (int ki = 0; ki < dim; ++ki)
315 // for (int kj = 0; kj < dim; ++kj)
316 // grad_x_f(ki, kj) = grad_normal[q][ki](kj, geom_nodes(n));
317 // local_storage.vec(g_index) += pressure_bc(q) * (grad_x_f * gvals.row(q).transpose()).dot(p.row(q)) * weights(q);
318 // }
319 // }
320 }
321 }
322 }
323 }
324 });
325
326 for (const LocalThreadVecStorage &local_storage : storage)
327 term += local_storage.vec;
328
329 term *= -1;
330
331 if (actual_dim == dim)
332 {
333 StiffnessMatrix hess;
334 hessian_wrt_u_prev(x, t, hess);
335 if (hess.cols() == term.size())
336 term += hess.transpose() * adjoint_zeroed;
337 // TODO: fix me for P2 basis
338 }
339 }
340
341 void BodyForm::hessian_wrt_u_prev(const Eigen::VectorXd &u_prev, const double t, StiffnessMatrix &hessian) const
342 {
344 hessian *= -1;
345 }
346} // 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
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
virtual bool is_scalar() const =0
virtual void rhs(const assembler::Assembler &assembler, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val) const =0
void set_bc(const std::vector< mesh::LocalBoundary > &local_boundary, const std::vector< int > &bounday_nodes, const int resolution, const std::vector< mesh::LocalBoundary > &local_neumann_boundary, Eigen::MatrixXd &rhs, const Eigen::MatrixXd &displacement=Eigen::MatrixXd(), const double t=1) const
const AssemblyValsCache & ass_vals_cache() const
const mesh::Mesh & mesh() const
const Problem & problem() const
void compute_energy_grad(const std::vector< mesh::LocalBoundary > &local_boundary, const std::vector< int > &bounday_nodes, const Density &density, const int resolution, const std::vector< mesh::LocalBoundary > &local_neumann_boundary, const Eigen::MatrixXd &final_rhs, const double t, Eigen::MatrixXd &rhs) const
double compute_energy(const Eigen::MatrixXd &displacement, const Eigen::MatrixXd &displacement_prev, const std::vector< mesh::LocalBoundary > &local_neumann_boundary, const Density &density, const int resolution, const double t) const
void compute_energy_hess(const std::vector< int > &bounday_nodes, const int resolution, const std::vector< mesh::LocalBoundary > &local_neumann_boundary, const Eigen::MatrixXd &displacement, const double t, const bool project_to_psd, StiffnessMatrix &hess) const
const Assembler & assembler() const
const std::vector< basis::ElementBases > & gbases() const
const std::vector< basis::ElementBases > & bases() const
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)
virtual bool is_volume() const =0
checks if mesh is volume
int dimension() const
utily for dimension
Definition Mesh.hpp:148
const assembler::RhsAssembler & rhs_assembler_
Reference to the RHS assembler.
Definition BodyForm.hpp:98
void second_derivative_unweighted(const Eigen::VectorXd &x, StiffnessMatrix &hessian) const override
Compute the second derivative of the value wrt x.
Definition BodyForm.cpp:75
bool is_formulation_mixed_
True if the formulation is mixed.
Definition BodyForm.hpp:102
const std::vector< int > & boundary_nodes_
Definition BodyForm.hpp:92
double t_
Current time.
Definition BodyForm.hpp:86
void update_quantities(const double t, const Eigen::VectorXd &x) override
Update time dependent quantities.
Definition BodyForm.cpp:80
void force_shape_derivative(const int n_verts, const double t, const Eigen::MatrixXd &x, const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
Compute the derivative of the force wrt vertex positions, then multiply the resulting matrix with adj...
Definition BodyForm.cpp:115
void first_derivative_unweighted(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
Compute the first derivative of the value wrt x.
Definition BodyForm.cpp:69
const std::vector< mesh::LocalBoundary > & local_neumann_boundary_
Definition BodyForm.hpp:94
const int ndof_
Number of degrees of freedom.
Definition BodyForm.hpp:87
const assembler::Density & density_
Definition BodyForm.hpp:99
Eigen::MatrixXd current_rhs_
Cached RHS for the current time.
Definition BodyForm.hpp:104
const std::vector< mesh::LocalBoundary > & local_boundary_
Definition BodyForm.hpp:93
void update_current_rhs(const Eigen::VectorXd &x)
Update current_rhs.
Definition BodyForm.cpp:87
bool apply_DBC_
If true, set the Dirichlet boundary conditions in the RHS.
Definition BodyForm.hpp:101
const Eigen::MatrixXd & rhs_
static RHS for the current time
Definition BodyForm.hpp:97
Eigen::MatrixXd x_prev_
Cached previous solution.
Definition BodyForm.hpp:90
void hessian_wrt_u_prev(const Eigen::VectorXd &u_prev, const double t, StiffnessMatrix &hessian) const
Definition BodyForm.cpp:341
BodyForm(const int ndof, const int n_pressure_bases, const std::vector< int > &boundary_nodes, const std::vector< mesh::LocalBoundary > &local_boundary, const std::vector< mesh::LocalBoundary > &local_neumann_boundary, const int n_boundary_samples, const Eigen::MatrixXd &rhs, const assembler::RhsAssembler &rhs_assembler, const assembler::Density &density, const bool apply_DBC, const bool is_formulation_mixed, const bool is_time_dependent)
Construct a new Body Form object.
Definition BodyForm.cpp:35
double value_unweighted(const Eigen::VectorXd &x) const override
Compute the value of the body force form.
Definition BodyForm.cpp:64
virtual double value(const Eigen::VectorXd &x) const
Compute the value of the form multiplied with the weigth.
Definition Form.hpp:24
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