PolyFEM
Loading...
Searching...
No Matches
BodyForceDerivative.cpp
Go to the documentation of this file.
2
3#include <cassert>
4#include <vector>
5
6#include <Eigen/Core>
17
18namespace polyfem::solver
19{
20 namespace
21 {
22 class LocalThreadVecStorage
23 {
24 public:
25 Eigen::MatrixXd vec;
26 assembler::ElementAssemblyValues vals, gvals;
28
29 LocalThreadVecStorage(const int size)
30 {
31 vec.resize(size, 1);
32 vec.setZero();
33 }
34 };
35 } // namespace
36
38 BodyForm &form,
39 const int n_verts,
40 const double t,
41 const Eigen::MatrixXd &x,
42 const Eigen::MatrixXd &adjoint,
43 Eigen::VectorXd &term)
44 {
45 const auto &bases = form.rhs_assembler_.bases();
46 const auto &gbases = form.rhs_assembler_.gbases();
47 const int dim = form.rhs_assembler_.mesh().dimension();
48 const int actual_dim = form.rhs_assembler_.problem().is_scalar() ? 1 : dim;
49
50 const int n_elements = int(bases.size());
51 term.setZero(n_verts * dim, 1);
52
53 auto storage = utils::create_thread_storage(LocalThreadVecStorage(term.size()));
54
55 utils::maybe_parallel_for(n_elements, [&](int start, int end, int thread_id) {
56 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
57
58 for (int e = start; e < end; ++e)
59 {
60 assembler::ElementAssemblyValues &vals = local_storage.vals;
61 form.rhs_assembler_.ass_vals_cache().compute(e, form.rhs_assembler_.mesh().is_volume(), bases[e], gbases[e], vals);
62 assembler::ElementAssemblyValues &gvals = local_storage.gvals;
63 gvals.compute(e, form.rhs_assembler_.mesh().is_volume(), vals.quadrature.points, gbases[e], gbases[e]);
64
65 const quadrature::Quadrature &quadrature = vals.quadrature;
66 local_storage.da = vals.det.array() * quadrature.weights.array();
67
68 Eigen::MatrixXd p, grad_p;
69 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim, vals, adjoint, p, grad_p);
70
71 Eigen::MatrixXd rhs_function;
72 form.rhs_assembler_.problem().rhs(form.rhs_assembler_.assembler(), vals.val, t, rhs_function);
73 rhs_function *= -1;
74 for (int q = 0; q < vals.val.rows(); q++)
75 {
76 const double rho = form.density_(quadrature.points.row(q), vals.val.row(q), t, e);
77 rhs_function.row(q) *= rho;
78 }
79
80 for (int q = 0; q < local_storage.da.size(); ++q)
81 {
82 const double value = p.row(q).dot(rhs_function.row(q)) * local_storage.da(q);
83 for (auto &v : gvals.basis_values)
84 {
85 local_storage.vec.block(v.global[0].index * dim, 0, dim, 1) += value * v.grad_t_m.row(q).transpose();
86 }
87 }
88 }
89 });
90
91 // Zero entries in p that correspond to nodes lying between dirichlet and neumann surfaces
92 // DO NOT PARALLELIZE since they both write to the same location
93 Eigen::MatrixXd adjoint_zeroed = adjoint;
94 adjoint_zeroed(form.boundary_nodes_, Eigen::all).setZero();
95
96 utils::maybe_parallel_for(form.local_neumann_boundary_.size(), [&](int start, int end, int thread_id) {
97 LocalThreadVecStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
98
99 Eigen::MatrixXd uv, points, normals;
100 Eigen::VectorXd weights;
101 Eigen::VectorXi global_ids;
102 assembler::ElementAssemblyValues vals, gvals;
103
104 for (int lb_id = start; lb_id < end; ++lb_id)
105 {
106 const auto &lb = form.local_neumann_boundary_[lb_id];
107 const int e = lb.element_id();
108
109 for (int i = 0; i < lb.size(); i++)
110 {
111 const int global_primitive_id = lb.global_primitive_id(i);
112 utils::BoundarySampler::boundary_quadrature(lb, form.n_boundary_samples_, form.rhs_assembler_.mesh(), i, false, uv, points, normals, weights);
113 global_ids.setConstant(points.rows(), global_primitive_id);
114
115 Eigen::MatrixXd reference_normals = normals;
116
117 vals.compute(e, form.rhs_assembler_.mesh().is_volume(), points, bases[e], gbases[e]);
118 gvals.compute(e, form.rhs_assembler_.mesh().is_volume(), points, gbases[e], gbases[e]);
119
120 std::vector<std::vector<Eigen::MatrixXd>> grad_normal;
121 for (int n = 0; n < gvals.jac_it.size(); ++n)
122 {
123 Eigen::MatrixXd trafo = gvals.jac_it[n].inverse();
124
125 Eigen::MatrixXd deform_mat(dim, dim);
126 deform_mat.setZero();
127 Eigen::MatrixXd jac_mat(dim, gvals.basis_values.size());
128 int b_idx = 0;
129 for (const auto &b : gvals.basis_values)
130 {
131 jac_mat.col(b_idx++) = b.grad.row(n);
132
133 for (const auto &g : b.global)
134 for (int d = 0; d < dim; ++d)
135 deform_mat.row(d) += x(g.index * dim + d) * b.grad.row(n);
136 }
137
138 trafo += deform_mat;
139 trafo = trafo.inverse();
140
141 Eigen::VectorXd displaced_normal = normals.row(n) * trafo;
142 normals.row(n) = displaced_normal / displaced_normal.norm();
143
144 std::vector<Eigen::MatrixXd> grad;
145 {
146 Eigen::MatrixXd vec = -(jac_mat.transpose() * trafo * reference_normals.row(n).transpose());
147 // Gradient of the displaced normal computation
148 for (int k = 0; k < dim; ++k)
149 {
150 Eigen::MatrixXd grad_i(jac_mat.rows(), jac_mat.cols());
151 grad_i.setZero();
152 for (int m = 0; m < jac_mat.rows(); ++m)
153 for (int l = 0; l < jac_mat.cols(); ++l)
154 grad_i(m, l) = -(reference_normals.row(n) * trafo)(m) * (jac_mat.transpose() * trafo)(l, k);
155 grad.push_back(grad_i);
156 }
157 }
158
159 {
160 Eigen::MatrixXd normalization_chain_rule = (normals.row(n).transpose() * normals.row(n));
161 normalization_chain_rule = Eigen::MatrixXd::Identity(dim, dim) - normalization_chain_rule;
162 normalization_chain_rule /= displaced_normal.norm();
163
164 Eigen::VectorXd vec(dim);
165 b_idx = 0;
166 for (const auto &b : gvals.basis_values)
167 {
168 for (int d = 0; d < dim; ++d)
169 {
170 for (int k = 0; k < dim; ++k)
171 vec(k) = grad[k](d, b_idx);
172 vec = normalization_chain_rule * vec;
173 for (int k = 0; k < dim; ++k)
174 grad[k](d, b_idx) = vec(k);
175 }
176 b_idx++;
177 }
178 }
179
180 grad_normal.push_back(grad);
181 }
182
183 Eigen::MatrixXd neumann_val;
184 form.rhs_assembler_.problem().neumann_bc(form.rhs_assembler_.mesh(), global_ids, uv, vals.val, normals, t, neumann_val);
185
186 Eigen::MatrixXd p, grad_p;
187 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim, vals, adjoint_zeroed, p, grad_p);
188
189 const Eigen::VectorXi geom_nodes = gbases[e].local_nodes_for_primitive(global_primitive_id, form.rhs_assembler_.mesh());
190
191 if (geom_nodes.size() != dim)
192 log_and_throw_error("Only linear geometry is supported in shape derivative!");
193
194 Eigen::VectorXd value = (p.array() * neumann_val.array()).rowwise().sum() * weights.array();
195
196 Eigen::VectorXd pressure_bc;
197 pressure_bc = (neumann_val.array() * normals.array()).rowwise().sum();
198
199 for (long n = 0; n < geom_nodes.size(); ++n)
200 {
201 const assembler::AssemblyValues &v = gvals.basis_values[geom_nodes(n)];
202
203 Eigen::MatrixXd velocity_div_mat;
204 {
205 if (form.rhs_assembler_.mesh().is_volume())
206 {
207 Eigen::MatrixXd V(3, 3);
208 V.row(0) = gbases[e].bases[geom_nodes(0)].global()[0].node;
209 V.row(1) = gbases[e].bases[geom_nodes(1)].global()[0].node;
210 V.row(2) = gbases[e].bases[geom_nodes(2)].global()[0].node;
211
212 velocity_div_mat = AdjointTools::face_velocity_divergence(V);
213 }
214 else
215 {
216 Eigen::MatrixXd V(2, 2);
217 V.row(0) = gbases[e].bases[geom_nodes(0)].global()[0].node;
218 V.row(1) = gbases[e].bases[geom_nodes(1)].global()[0].node;
219
220 velocity_div_mat = AdjointTools::edge_velocity_divergence(V);
221 }
222 }
223
224 // integrate j * div(gbases) over the whole boundary
225 for (int d = 0; d < dim; d++)
226 {
227 assert(v.global.size() == 1);
228 const int g_index = v.global[0].index * dim + d;
229
230 Eigen::MatrixXd gvals(v.val.size(), dim);
231 gvals.setZero();
232 gvals.col(d) = v.val;
233
234 local_storage.vec(g_index) += value.sum() * velocity_div_mat(n, d);
235 const bool is_pressure = form.rhs_assembler_.problem().is_boundary_pressure(form.rhs_assembler_.mesh().get_boundary_id(global_primitive_id));
236 // if (is_pressure)
237 // {
238 // for (int q = 0; q < vals.jac_it.size(); ++q)
239 // {
240 // Eigen::MatrixXd grad_x_f(dim, dim);
241 // for (int ki = 0; ki < dim; ++ki)
242 // for (int kj = 0; kj < dim; ++kj)
243 // grad_x_f(ki, kj) = grad_normal[q][ki](kj, geom_nodes(n));
244 // local_storage.vec(g_index) += pressure_bc(q) * (grad_x_f * gvals.row(q).transpose()).dot(p.row(q)) * weights(q);
245 // }
246 // }
247 }
248 }
249 }
250 }
251 });
252
253 for (const LocalThreadVecStorage &local_storage : storage)
254 term += local_storage.vec;
255
256 term *= -1;
257
258 if (actual_dim == dim)
259 {
260 StiffnessMatrix hess;
261 form.hessian_wrt_u_prev(x, t, hess);
262 if (hess.cols() == term.size())
263 term += hess.transpose() * adjoint_zeroed;
264 // TODO: fix me for P2 basis
265 }
266 }
267} // 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
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
const AssemblyValsCache & ass_vals_cache() const
const mesh::Mesh & mesh() const
const Problem & problem() 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:152
static void force_shape_derivative(BodyForm &form, const int n_verts, const double t, const Eigen::MatrixXd &x, const Eigen::MatrixXd &adjoint, Eigen::VectorXd &term)
Form representing body forces.
Definition BodyForm.hpp:17
const assembler::RhsAssembler & rhs_assembler_
Reference to the RHS assembler.
Definition BodyForm.hpp:78
const std::vector< int > & boundary_nodes_
Definition BodyForm.hpp:72
const std::vector< mesh::LocalBoundary > & local_neumann_boundary_
Definition BodyForm.hpp:74
const assembler::Density & density_
Definition BodyForm.hpp:79
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:17
Eigen::SparseMatrix< double, Eigen::ColMajor > StiffnessMatrix
Definition Types.hpp:24