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(), form.rhs_assembler_.mesh(), vals.element_id, 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:75
QuadratureVector da
Definition Assembler.cpp:26
ElementAssemblyValues vals
Definition Assembler.cpp:25
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 void rhs(const assembler::Assembler &assembler, const Eigen::MatrixXd &pts, const double t, Eigen::MatrixXd &val, const int fe_space_id=-1) const =0
virtual bool is_scalar() 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:153
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