PolyFEM
Loading...
Searching...
No Matches
TargetForms.cpp
Go to the documentation of this file.
2
4#include <polyfem/Common.hpp>
13
15
16#include <Eigen/Core>
17#include <spdlog/fmt/fmt.h>
18
19#include <algorithm>
20#include <cassert>
21#include <cmath>
22#include <cstddef>
23#include <map>
24#include <memory>
25#include <set>
26#include <stdexcept>
27#include <string>
28#include <utility>
29#include <vector>
30
31using namespace polyfem::utils;
32
33namespace polyfem::solver
34{
35 namespace
36 {
37 class LocalThreadScalarStorage
38 {
39 public:
40 double val;
42
43 LocalThreadScalarStorage()
44 {
45 val = 0;
46 }
47 };
48 } // namespace
49
51 {
54 {
55 assert(target_diff_cache_->size() > 0);
56
57 auto j_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
58 int e_ref = params.elem;
59 if (auto search = e_to_ref_e_.find(params.elem); search != e_to_ref_e_.end())
60 e_ref = search->second;
61
62 Eigen::MatrixXd pts_ref;
63 target_varform_->primary_space().geometry_basis_list()[e_ref].eval_geom_mapping(local_pts, pts_ref);
64
65 Eigen::MatrixXd u_ref, grad_u_ref;
66 const Eigen::VectorXd &sol_ref = target_diff_cache_->u(target_varform_->get_problem().is_time_dependent() ? params.step : 0);
67 io::Evaluator::interpolate_at_local_vals(target_varform_->get_mesh(), target_varform_->get_problem().is_scalar(), target_varform_->primary_space().basis_list(), target_varform_->primary_space().geometry_basis_list(), e_ref, local_pts, sol_ref, u_ref, grad_u_ref);
68
69 val = (u_ref + pts_ref - u - pts).rowwise().squaredNorm();
70 };
71
72 auto djdu_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
73 int e_ref = params.elem;
74 if (auto search = e_to_ref_e_.find(params.elem); search != e_to_ref_e_.end())
75 e_ref = search->second;
76
77 Eigen::MatrixXd pts_ref;
78 target_varform_->primary_space().geometry_basis_list()[e_ref].eval_geom_mapping(local_pts, pts_ref);
79
80 Eigen::MatrixXd u_ref, grad_u_ref;
81 const Eigen::VectorXd &sol_ref = target_diff_cache_->u(target_varform_->get_problem().is_time_dependent() ? params.step : 0);
82 io::Evaluator::interpolate_at_local_vals(target_varform_->get_mesh(), target_varform_->get_problem().is_scalar(), target_varform_->primary_space().basis_list(), target_varform_->primary_space().geometry_basis_list(), e_ref, local_pts, sol_ref, u_ref, grad_u_ref);
83
84 val = 2 * (u + pts - u_ref - pts_ref);
85 };
86
87 j.set_j(j_func);
88 j.set_dj_du(djdu_func);
89 j.set_dj_dx(djdu_func); // only used for shape derivative
90 }
91 else if (have_target_func)
92 {
93 auto j_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
94 val.setZero(u.rows(), 1);
95
96 const Eigen::MatrixXd X = u + pts;
97 for (int q = 0; q < u.rows(); q++)
98 val(q) = target_func(X(q, 0), X(q, 1), X.cols() == 2 ? 0 : X(q, 2), 0, params.elem);
99 };
100
101 auto djdu_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
102 val.setZero(u.rows(), u.cols());
103
104 const Eigen::MatrixXd X = u + pts;
105 for (int q = 0; q < u.rows(); q++)
106 for (int d = 0; d < val.cols(); d++)
107 val(q, d) = target_func_grad[d](X(q, 0), X(q, 1), X.cols() == 2 ? 0 : X(q, 2), 0, params.elem);
108 };
109
110 j.set_j(j_func);
111 j.set_dj_du(djdu_func);
112 j.set_dj_dx(djdu_func); // only used for shape derivative
113 }
114 else // error wrt. a constant displacement
115 {
116 if (target_disp.size() == varform_->get_mesh().dimension())
117 {
118 auto j_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
119 val.setZero(u.rows(), 1);
120
121 for (int q = 0; q < u.rows(); q++)
122 {
123 Eigen::VectorXd err = u.row(q) - this->target_disp.transpose();
124 for (int d = 0; d < active_dimension_mask.size(); d++)
125 if (!active_dimension_mask[d])
126 err(d) = 0;
127 val(q) = err.squaredNorm();
128 }
129 };
130 auto djdu_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
131 val.setZero(u.rows(), u.cols());
132
133 for (int q = 0; q < u.rows(); q++)
134 {
135 Eigen::VectorXd err = u.row(q) - this->target_disp.transpose();
136 for (int d = 0; d < active_dimension_mask.size(); d++)
137 if (!active_dimension_mask[d])
138 err(d) = 0;
139 val.row(q) = 2 * err;
140 }
141 };
142
143 j.set_j(j_func);
144 j.set_dj_du(djdu_func);
145 }
146 else
147 log_and_throw_adjoint_error("[{}] Only constant target displacement is supported!", name());
148 }
149
150 return j;
151 }
152
153 void TargetForm::set_reference(const std::shared_ptr<const varform::DifferentiableVarForm> &target_varform, std::shared_ptr<const DiffCache> target_diff_cache, const std::set<int> &reference_cached_body_ids)
154 {
155 target_varform_ = target_varform;
156 target_diff_cache_ = std::move(target_diff_cache);
157
158 std::map<int, std::vector<int>> ref_interested_body_id_to_e;
159 int ref_count = 0;
160 for (int e = 0; e < target_varform_->primary_space().basis_list().size(); ++e)
161 {
162 int body_id = target_varform_->get_mesh().get_body_id(e);
163 if (reference_cached_body_ids.size() > 0 && reference_cached_body_ids.count(body_id) == 0)
164 continue;
165 if (ref_interested_body_id_to_e.find(body_id) != ref_interested_body_id_to_e.end())
166 ref_interested_body_id_to_e[body_id].push_back(e);
167 else
168 ref_interested_body_id_to_e[body_id] = {e};
169 ref_count++;
170 }
171
172 std::map<int, std::vector<int>> interested_body_id_to_e;
173 int count = 0;
174 for (int e = 0; e < varform_->primary_space().basis_list().size(); ++e)
175 {
176 int body_id = varform_->get_mesh().get_body_id(e);
177 if (reference_cached_body_ids.size() > 0 && reference_cached_body_ids.count(body_id) == 0)
178 continue;
179 if (interested_body_id_to_e.find(body_id) != interested_body_id_to_e.end())
180 interested_body_id_to_e[body_id].push_back(e);
181 else
182 interested_body_id_to_e[body_id] = {e};
183 count++;
184 }
185
186 if (count != ref_count)
187 adjoint_logger().error("[{}] Number of interested elements in the reference and optimization examples do not match! {} {}", name(), count, ref_count);
188 else
189 adjoint_logger().trace("[{}] Found {} matching elements.", name(), count);
190
191 for (const auto &kv : interested_body_id_to_e)
192 {
193 for (int i = 0; i < kv.second.size(); ++i)
194 {
195 e_to_ref_e_[kv.second[i]] = ref_interested_body_id_to_e[kv.first][i];
196 }
197 }
198 }
199
200 void TargetForm::set_reference(const json &func, const json &grad_func)
201 {
203 for (size_t k = 0; k < grad_func.size(); k++)
204 target_func_grad[k].init(grad_func[k], resources_);
205 have_target_func = true;
206 }
207
208 void SDFTargetForm::solution_changed_step(const int time_step, const Eigen::VectorXd &x)
209 {
210 const auto &bases = varform_->primary_space().basis_list();
211 const auto &gbases = varform_->primary_space().geometry_basis_list();
212 const int actual_dim = varform_->get_problem().is_scalar() ? 1 : dim;
213
214 auto storage = utils::create_thread_storage(LocalThreadScalarStorage());
215 utils::maybe_parallel_for(varform_->boundary_state().total_local_boundary.size(), [&](int start, int end, int thread_id) {
216 LocalThreadScalarStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
217
218 Eigen::MatrixXd uv, samples, gtmp;
219 Eigen::MatrixXd points, normal;
220 Eigen::VectorXd weights;
221
222 Eigen::MatrixXd u, grad_u;
223
224 for (int lb_id = start; lb_id < end; ++lb_id)
225 {
226 const auto &lb = varform_->boundary_state().total_local_boundary[lb_id];
227 const int e = lb.element_id();
228
229 for (int i = 0; i < lb.size(); i++)
230 {
231 const int global_primitive_id = lb.global_primitive_id(i);
232 if (ids_.size() != 0 && ids_.find(varform_->get_mesh().get_boundary_id(global_primitive_id)) == ids_.end())
233 continue;
234
235 utils::BoundarySampler::boundary_quadrature(lb, varform_->n_boundary_samples(), varform_->get_mesh(), i, false, uv, points, normal, weights);
236
237 assembler::ElementAssemblyValues &vals = local_storage.vals;
238 vals.compute(e, varform_->get_mesh().is_volume(), points, bases[e], gbases[e]);
239 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim, vals, diff_cache_->u(time_step), u, grad_u);
240
241 normal = normal * vals.jac_it[0]; // assuming linear geometry
242
243 for (int q = 0; q < u.rows(); q++)
244 interpolation_fn->cache_grid([this](const Eigen::MatrixXd &point, double &distance) { compute_distance(point, distance); }, vals.val.row(q) + u.row(q));
245 }
246 }
247 });
248 }
249
250 void SDFTargetForm::set_bspline_target(const Eigen::MatrixXd &control_points, const Eigen::VectorXd &knots, const double delta)
251 {
252 dim = control_points.cols();
253 delta_ = delta;
254 if ((dim != 2) || (varform_->get_mesh().dimension() != 2))
255 log_and_throw_error("SDFTargetForm specified for 2d.");
256
257 samples = 100;
258
259 nanospline::BSpline<double, 2, 3> curve;
260 curve.set_control_points(control_points);
261 curve.set_knots(knots);
262
263 t_or_uv_sampling = Eigen::VectorXd::LinSpaced(samples, 0, 1);
264 point_sampling.setZero(samples, 2);
265 for (int i = 0; i < t_or_uv_sampling.size(); ++i)
266 point_sampling.row(i) = curve.evaluate(t_or_uv_sampling(i));
267
268 {
269 Eigen::MatrixXi edges(samples - 1, 2);
270 edges.col(0) = Eigen::VectorXi::LinSpaced(samples - 1, 0, samples - 2);
271 edges.col(1) = Eigen::VectorXi::LinSpaced(samples - 1, 1, samples - 1);
272 io::OBJWriter::write(fmt::format("spline_target_{:d}.obj", rand() % 100), point_sampling, edges);
273 }
274
275 interpolation_fn = std::make_unique<LazyCubicInterpolator>(dim, delta_);
276 }
277
278 void SDFTargetForm::set_bspline_target(const Eigen::MatrixXd &control_points, const Eigen::VectorXd &knots_u, const Eigen::VectorXd &knots_v, const double delta)
279 {
280
281 dim = control_points.cols();
282 delta_ = delta;
283 if ((dim != 3) || (varform_->get_mesh().dimension() != 3))
284 log_and_throw_error("SDFTargetForm specified for 3d.");
285
286 samples = 100;
287
288 nanospline::BSplinePatch<double, 3, 3, 3> patch;
289 patch.set_control_grid(control_points);
290 patch.set_knots_u(knots_u);
291 patch.set_knots_v(knots_v);
292 patch.initialize();
293
294 t_or_uv_sampling.resize(samples * samples, 2);
295 for (int i = 0; i < samples; ++i)
296 {
297 t_or_uv_sampling.block(i * samples, 0, samples, 1) = Eigen::VectorXd::LinSpaced(samples, 0, 1);
298 t_or_uv_sampling.block(i * samples, 1, samples, 1) = (double)i / (samples - 1) * Eigen::VectorXd::Ones(samples);
299 }
300 point_sampling.setZero(samples * samples, 3);
301 for (int i = 0; i < t_or_uv_sampling.rows(); ++i)
302 {
303 point_sampling.row(i) = patch.evaluate(t_or_uv_sampling(i, 0), t_or_uv_sampling(i, 1));
304 }
305
306 {
307 Eigen::MatrixXi F(2 * ((samples - 1) * (samples - 1)), 3);
308 int f = 0;
309 for (int i = 0; i < samples - 1; ++i)
310 for (int j = 0; j < samples - 1; ++j)
311 {
312 Eigen::MatrixXi F_local(2, 3);
313 F_local << (i * samples + j), ((i + 1) * samples + j), (i * samples + j + 1),
314 (i * samples + j + 1), ((i + 1) * samples + j), ((i + 1) * samples + j + 1);
315 F.block(f, 0, 2, 3) = F_local;
316 f += 2;
317 }
318 io::OBJWriter::write(fmt::format("spline_target_{:d}.obj", rand() % 100), point_sampling, F);
319 }
320
321 interpolation_fn = std::make_unique<LazyCubicInterpolator>(dim, delta_);
322 }
323
324 void SDFTargetForm::compute_distance(const Eigen::MatrixXd &point, double &distance) const
325 {
326 distance = DBL_MAX;
327 Eigen::MatrixXd p = point.transpose();
328
329 if (dim == 2)
330 for (int i = 0; i < t_or_uv_sampling.size() - 1; ++i)
331 {
332 const double l = (point_sampling.row(i + 1) - point_sampling.row(i)).squaredNorm();
333 double distance_to_perpendicular = ((p - point_sampling.row(i)) * (point_sampling.row(i + 1) - point_sampling.row(i)).transpose())(0) / l;
334 const double t = std::max(0., std::min(1., distance_to_perpendicular));
335 const auto project = point_sampling.row(i) * (1 - t) + point_sampling.row(i + 1) * t;
336 const double project_distance = (p - project).norm();
337 if (project_distance < distance)
338 distance = project_distance;
339 }
340 else if (dim == 3)
341 {
342 for (int i = 0; i < samples - 1; ++i)
343 for (int j = 0; j < samples - 1; ++j)
344 {
345 int loc = samples * i + j;
346 const double l1 = (point_sampling.row(loc + 1) - point_sampling.row(loc)).squaredNorm();
347 double distance_to_perpendicular = ((p - point_sampling.row(loc)) * (point_sampling.row(loc + 1) - point_sampling.row(loc)).transpose())(0) / l1;
348 const double u = std::max(0., std::min(1., distance_to_perpendicular));
349
350 const double l2 = (point_sampling.row(loc + samples) - point_sampling.row(loc)).squaredNorm();
351 distance_to_perpendicular = ((p - point_sampling.row(loc)) * (point_sampling.row(loc + samples) - point_sampling.row(loc)).transpose())(0) / l2;
352 const double v = std::max(0., std::min(1., distance_to_perpendicular));
353
354 Eigen::MatrixXd project = point_sampling.row(loc) * (1 - u) + point_sampling.row(loc + 1) * u;
355 project += v * (point_sampling.row(loc + samples) - point_sampling.row(loc));
356 const double project_distance = (p - project).norm();
357 if (project_distance < distance)
358 distance = project_distance;
359 }
360 }
361 }
362
363 IntegrableFunctional SDFTargetForm::get_integral_functional() const
364 {
366 auto j_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
367 val.setZero(u.rows(), 1);
368
369 for (int q = 0; q < u.rows(); q++)
370 {
371 double distance;
372 Eigen::MatrixXd unused_grad;
373 interpolation_fn->evaluate(u.row(q) + pts.row(q), distance, unused_grad);
374 val(q) = pow(distance, 2);
375 }
376 };
377
378 auto djdu_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
379 val.setZero(u.rows(), u.cols());
380
381 for (int q = 0; q < u.rows(); q++)
382 {
383 double distance;
384 Eigen::MatrixXd grad;
385 interpolation_fn->evaluate(u.row(q) + pts.row(q), distance, grad);
386 val.row(q) = 2 * distance * grad.transpose();
387 }
388 };
389
390 j.set_j(j_func);
391 j.set_dj_du(djdu_func);
392 j.set_dj_dx(djdu_func);
393
394 return j;
395 }
396
397 void MeshTargetForm::set_surface_mesh_target(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, const double delta)
398 {
399 dim = V.cols();
400 delta_ = delta;
401 if ((dim != 3) || (varform_->get_mesh().dimension() != 3))
402 log_and_throw_error("MeshTargetForm is only available for 3d scenes.");
403
404 tree_.init(V, F);
405 V_ = V;
406 F_ = F;
407
408 interpolation_fn = std::make_unique<LazyCubicInterpolator>(dim, delta_);
409 }
410
411 void MeshTargetForm::solution_changed_step(const int time_step, const Eigen::VectorXd &x)
412 {
413 const auto &bases = varform_->primary_space().basis_list();
414 const auto &gbases = varform_->primary_space().geometry_basis_list();
415 const int actual_dim = varform_->get_problem().is_scalar() ? 1 : dim;
416
417 auto storage = utils::create_thread_storage(LocalThreadScalarStorage());
418 utils::maybe_parallel_for(varform_->boundary_state().total_local_boundary.size(), [&](int start, int end, int thread_id) {
419 LocalThreadScalarStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
420
421 Eigen::MatrixXd uv, samples, gtmp;
422 Eigen::MatrixXd points, normal;
423 Eigen::VectorXd weights;
424
425 Eigen::MatrixXd u, grad_u;
426
427 for (int lb_id = start; lb_id < end; ++lb_id)
428 {
429 const auto &lb = varform_->boundary_state().total_local_boundary[lb_id];
430 const int e = lb.element_id();
431
432 for (int i = 0; i < lb.size(); i++)
433 {
434 const int global_primitive_id = lb.global_primitive_id(i);
435 if (ids_.size() != 0 && ids_.find(varform_->get_mesh().get_boundary_id(global_primitive_id)) == ids_.end())
436 continue;
437
438 utils::BoundarySampler::boundary_quadrature(lb, varform_->n_boundary_samples(), varform_->get_mesh(), i, false, uv, points, normal, weights);
439
440 assembler::ElementAssemblyValues &vals = local_storage.vals;
441 vals.compute(e, varform_->get_mesh().is_volume(), points, bases[e], gbases[e]);
442 io::Evaluator::interpolate_at_local_vals(e, dim, actual_dim, vals, diff_cache_->u(time_step), u, grad_u);
443
444 normal = normal * vals.jac_it[0]; // assuming linear geometry
445
446 for (int q = 0; q < u.rows(); q++)
447 interpolation_fn->cache_grid([this](const Eigen::MatrixXd &point, double &distance) {
448 int idx;
449 Eigen::Matrix<double, 1, 3> closest;
450 distance = pow(tree_.squared_distance(V_, F_, point.col(0), idx, closest), 0.5);
451 },
452 vals.val.row(q) + u.row(q));
453 }
454 }
455 });
456 }
457
458 IntegrableFunctional MeshTargetForm::get_integral_functional() const
459 {
461 auto j_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
462 val.setZero(u.rows(), 1);
463
464 for (int q = 0; q < u.rows(); q++)
465 {
466 double distance;
467 Eigen::MatrixXd unused_grad;
468 interpolation_fn->evaluate(u.row(q) + pts.row(q), distance, unused_grad);
469 val(q) = pow(distance, 2);
470 }
471 };
472
473 auto djdu_func = [this](const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::VectorXd &lambda, const Eigen::VectorXd &mu, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, const IntegrableFunctional::ParameterType &params, Eigen::MatrixXd &val) {
474 val.setZero(u.rows(), u.cols());
475
476 for (int q = 0; q < u.rows(); q++)
477 {
478 double distance;
479 Eigen::MatrixXd grad;
480 interpolation_fn->evaluate(u.row(q) + pts.row(q), distance, grad);
481 val.row(q) = 2 * distance * grad.transpose();
482 }
483 };
484
485 j.set_j(j_func);
486 j.set_dj_du(djdu_func);
487 j.set_dj_dx(djdu_func);
488
489 return j;
490 }
491
492 NodeTargetForm::NodeTargetForm(std::shared_ptr<const varform::DifferentiableVarForm> varform, std::shared_ptr<const DiffCache> diff_cache, const VariableToSimulationGroup &variable_to_simulations, const json &args, const io::ResourceIO &resources)
493 : StaticForm(variable_to_simulations), varform_(std::move(varform)), diff_cache_(std::move(diff_cache))
494 {
495 const int dim = varform_->get_mesh().dimension();
496
497 // N x (dim * 2), each row is [rest_x, rest_y, rest_z, deform_x, deform_y, deform_z]
498 const Eigen::MatrixXd data = resources.read_matrix(args["target_data_path"].get<std::string>());
499
500 // markers to nodes
501 target_vertex_positions.setZero(data.rows(), dim);
502 active_nodes.reserve(data.rows());
503 for (int s = 0; s < data.rows(); s++)
504 {
505 target_vertex_positions.row(s) = data.block(s, dim, 1, dim);
506
507 const RowVectorNd node = data.block(s, 0, 1, dim);
508 bool not_found = true;
509 double min_dist = std::numeric_limits<double>::max();
510 for (int v = 0; v < varform_->primary_space().mesh_nodes->n_nodes(); v++)
511 {
512 min_dist = std::min(min_dist, (varform_->primary_space().mesh_nodes->node_position(v) - node).norm());
513 if ((varform_->primary_space().mesh_nodes->node_position(v) - node).norm() < args["tolerance"])
514 {
515 active_nodes.push_back(v);
516 not_found = false;
517 break;
518 }
519 }
520 if (not_found)
521 log_and_throw_adjoint_error("Failed to find corresponding node for {}! Minimum distance {}", node, min_dist);
522 }
523 }
524
525 NodeTargetForm::NodeTargetForm(std::shared_ptr<const varform::DifferentiableVarForm> varform, std::shared_ptr<const DiffCache> diff_cache, const VariableToSimulationGroup &variable_to_simulations, const std::vector<int> &active_nodes_, const Eigen::MatrixXd &target_vertex_positions_)
526 : StaticForm(variable_to_simulations), varform_(std::move(varform)), diff_cache_(std::move(diff_cache)), target_vertex_positions(target_vertex_positions_), active_nodes(active_nodes_)
527 {
528 // log_and_throw_adjoint_error("[{}] Constructor not implemented!", name());
529 }
530
531 Eigen::VectorXd NodeTargetForm::compute_adjoint_rhs_step(const int time_step, const Eigen::VectorXd &x, const varform::DifferentiableVarForm &varform, const DiffCache &diff_cache) const
532 {
533 Eigen::VectorXd rhs;
534 rhs.setZero(diff_cache.u(0).size());
535
536 const int dim = varform_->get_mesh().dimension();
537
538 if (&varform == varform_.get())
539 {
540 int i = 0;
541 const Eigen::VectorXd disp = diff_cache_->u(time_step);
542 for (int v : active_nodes)
543 {
544 const RowVectorNd cur_pos = varform_->primary_space().mesh_nodes->node_position(v) + disp.segment(v * dim, dim).transpose();
545
546 rhs.segment(v * dim, dim) = 2 * (cur_pos - target_vertex_positions.row(i++));
547 }
548 }
549
550 return rhs * weight();
551 }
552
553 double NodeTargetForm::value_unweighted_step(const int time_step, const Eigen::VectorXd &x) const
554 {
555 const int dim = varform_->get_mesh().dimension();
556 double val = 0;
557 int i = 0;
558 const Eigen::VectorXd disp = diff_cache_->u(time_step);
559 for (int v : active_nodes)
560 {
561 const RowVectorNd cur_pos = varform_->primary_space().mesh_nodes->node_position(v) + disp.segment(v * dim, dim).transpose();
562 val += (cur_pos - target_vertex_positions.row(i++)).squaredNorm();
563 }
564 return val;
565 }
566
567 void NodeTargetForm::compute_partial_gradient_step(const int time_step, const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
568 {
569 gradv.setZero(x.size());
571 log_and_throw_adjoint_error("[{}] Doesn't support derivatives wrt. shape!", name());
572 return Eigen::VectorXd::Zero(0).eval();
573 });
574 }
575
576 BarycenterTargetForm::BarycenterTargetForm(const VariableToSimulationGroup &variable_to_simulations, const json &args, const std::shared_ptr<varform::DifferentiableVarForm> &varform1, std::shared_ptr<const DiffCache> diff_cache1, const std::shared_ptr<varform::DifferentiableVarForm> &varform2, std::shared_ptr<const DiffCache> diff_cache2)
577 : StaticForm(variable_to_simulations)
578 {
579 dim = varform1->get_mesh().dimension();
580 json tmp_args = args;
581 for (int d = 0; d < dim; d++)
582 {
583 tmp_args["dim"] = d;
584 center1.push_back(std::make_unique<PositionForm>(variable_to_simulations, varform1, diff_cache1, tmp_args));
585 center2.push_back(std::make_unique<PositionForm>(variable_to_simulations, varform2, diff_cache2, tmp_args));
586 }
587 }
588
589 Eigen::VectorXd BarycenterTargetForm::compute_adjoint_rhs_step(const int time_step, const Eigen::VectorXd &x, const varform::DifferentiableVarForm &varform, const DiffCache &diff_cache) const
590 {
591 Eigen::VectorXd term;
592 term.setZero(varform.primary_space().ndof());
593 for (int d = 0; d < dim; d++)
594 {
595 double value = center1[d]->value_unweighted_step(time_step, x) - center2[d]->value_unweighted_step(time_step, x);
596 term += (2 * value) * (center1[d]->compute_adjoint_rhs_step(time_step, x, varform, diff_cache) - center2[d]->compute_adjoint_rhs_step(time_step, x, varform, diff_cache));
597 }
598 return term * weight();
599 }
600 void BarycenterTargetForm::compute_partial_gradient_step(const int time_step, const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
601 {
602 gradv.setZero(x.size());
603 Eigen::VectorXd tmp1, tmp2;
604 for (int d = 0; d < dim; d++)
605 {
606 double value = center1[d]->value_unweighted_step(time_step, x) - center2[d]->value_unweighted_step(time_step, x);
607 center1[d]->compute_partial_gradient_step(time_step, x, tmp1);
608 center2[d]->compute_partial_gradient_step(time_step, x, tmp2);
609 gradv += (2 * value) * (tmp1 - tmp2);
610 }
611 gradv *= weight();
612 }
613 double BarycenterTargetForm::value_unweighted_step(const int time_step, const Eigen::VectorXd &x) const
614 {
615 double dist = 0;
616 for (int d = 0; d < dim; d++)
617 dist += std::pow(center1[d]->value_unweighted_step(time_step, x) - center2[d]->value_unweighted_step(time_step, x), 2);
618
619 return dist;
620 }
621
622 MinTargetDistForm::MinTargetDistForm(const VariableToSimulationGroup &variable_to_simulations, const std::vector<int> &steps, const Eigen::VectorXd &target, const json &args, const std::shared_ptr<varform::DifferentiableVarForm> &varform, std::shared_ptr<const DiffCache> diff_cache)
623 : AdjointForm(variable_to_simulations), steps_(steps), target_(target)
624 {
625 dim = varform->get_mesh().dimension();
626 json tmp_args = args;
627 for (int d = 0; d < dim; d++)
628 {
629 tmp_args["dim"] = d;
630 objs.push_back(std::make_unique<PositionForm>(variable_to_simulations, varform, diff_cache, tmp_args));
631 }
632 objs.push_back(std::make_unique<VolumeForm>(variable_to_simulations, varform, diff_cache, args));
633 }
634 Eigen::MatrixXd MinTargetDistForm::compute_adjoint_rhs(const Eigen::VectorXd &x, const varform::DifferentiableVarForm &varform, const DiffCache &diff_cache) const
635 {
636 Eigen::VectorXd values(steps_.size());
637 std::vector<Eigen::MatrixXd> grads(steps_.size(), Eigen::MatrixXd::Zero(varform.primary_space().ndof(), objs.size()));
638 Eigen::MatrixXd g2(steps_.size(), objs.size());
639 int i = 0;
640 for (int s : steps_)
641 {
642 Eigen::VectorXd input(objs.size());
643 Eigen::VectorXd tmp;
644 for (int d = 0; d < objs.size(); d++)
645 {
646 input(d) = objs[d]->value_unweighted_step(s, x);
647 grads[i].col(d) = objs[d]->compute_adjoint_rhs_step(s, x, varform, diff_cache);
648 }
649 values[i] = eval2(input);
650 g2.row(i++) = eval2_grad(input);
651 }
652
653 Eigen::VectorXd g1 = eval1_grad(values);
654 Eigen::MatrixXd terms = Eigen::MatrixXd::Zero(varform.primary_space().ndof(), diff_cache.size());
655 i = 0;
656 for (int s : steps_)
657 {
658 terms.col(s) += g1(i) * grads[i] * g2.row(i).transpose();
659 i++;
660 }
661
662 return terms * weight();
663 }
664 void MinTargetDistForm::compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
665 {
666 Eigen::VectorXd values(steps_.size());
667 std::vector<Eigen::MatrixXd> grads(steps_.size(), Eigen::MatrixXd::Zero(x.size(), objs.size()));
668 Eigen::MatrixXd g2(steps_.size(), objs.size());
669 int i = 0;
670 for (int s : steps_)
671 {
672 Eigen::VectorXd input(objs.size());
673 Eigen::VectorXd tmp;
674 for (int d = 0; d < objs.size(); d++)
675 {
676 input(d) = objs[d]->value_unweighted_step(s, x);
677 objs[d]->compute_partial_gradient_step(s, x, tmp);
678 grads[i].col(d) = tmp;
679 }
680 values[i] = eval2(input);
681 g2.row(i++) = eval2_grad(input);
682 }
683
684 Eigen::VectorXd g1 = eval1_grad(values);
685 gradv.setZero(x.size());
686 i = 0;
687 for (int s : steps_)
688 {
689 gradv += g1(i) * grads[i] * g2.row(i).transpose();
690 i++;
691 }
692 gradv *= weight();
693 }
694 double MinTargetDistForm::value_unweighted(const Eigen::VectorXd &x) const
695 {
696 Eigen::VectorXd values(steps_.size());
697 int i = 0;
698 for (int s : steps_)
699 {
700 Eigen::VectorXd input(objs.size());
701 for (int d = 0; d < objs.size(); d++)
702 input(d) = objs[d]->value_unweighted_step(s, x);
703 values[i++] = eval2(input);
704 }
705
706 return eval1(values);
707 }
708} // namespace polyfem::solver
int V
double val
Definition Assembler.cpp:90
ElementAssemblyValues vals
Definition Assembler.cpp:26
Eigen::RowVectorXd point
int x
Storage for additional data required by differntial code.
Definition DiffCache.hpp:22
int size() const
Definition DiffCache.hpp:46
Eigen::VectorXd u(int step) const
Definition DiffCache.hpp:63
void set_dj_du(const functionalType &dj_du)
void evaluate(const Eigen::MatrixXd &elastic_params, const Eigen::MatrixXd &local_pts, const Eigen::MatrixXd &pts, const Eigen::MatrixXd &u, const Eigen::MatrixXd &grad_u, const Eigen::MatrixXd &reference_normals, const assembler::ElementAssemblyValues &vals, ParameterType &params, Eigen::MatrixXd &val) const
void set_j(const functionalType &j)
void set_dj_dx(const functionalType &dj_dx)
stores per element basis values at given quadrature points and geometric mapping
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)
static bool write(const std::string &path, const Eigen::MatrixXd &v, const Eigen::MatrixXi &e, const Eigen::MatrixXi &f)
Definition OBJWriter.cpp:18
Read-only interface to a hierarchical collection of simulation resources.
virtual Eigen::MatrixXd read_matrix(const std::string &path) const =0
double value(const Eigen::VectorXd &x) const override
Compute the value of the form multiplied with the weigth.
const VariableToSimulationGroup variable_to_simulations_
std::vector< std::unique_ptr< PositionForm > > center1
BarycenterTargetForm(const VariableToSimulationGroup &variable_to_simulations, const json &args, const std::shared_ptr< varform::DifferentiableVarForm > &varform1, std::shared_ptr< const DiffCache > diff_cache1, const std::shared_ptr< varform::DifferentiableVarForm > &varform2, std::shared_ptr< const DiffCache > diff_cache2)
std::vector< std::unique_ptr< PositionForm > > center2
void compute_partial_gradient_step(const int time_step, const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
double value_unweighted_step(const int time_step, const Eigen::VectorXd &x) const override
Eigen::VectorXd compute_adjoint_rhs_step(const int time_step, const Eigen::VectorXd &x, const varform::DifferentiableVarForm &varform, const DiffCache &diff_cache) const override
virtual void init(const Eigen::VectorXd &x)
Initialize the form.
Definition Form.hpp:19
virtual double weight() const
Get the form's multiplicative constant weight.
Definition Form.hpp:128
static double eval1(Eigen::VectorXd &x)
double eval2(Eigen::VectorXd &x) const
std::vector< std::unique_ptr< StaticForm > > objs
void compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
double value_unweighted(const Eigen::VectorXd &x) const override
Compute the value of the form.
const std::vector< int > steps_
static Eigen::VectorXd eval1_grad(Eigen::VectorXd &x)
Eigen::MatrixXd compute_adjoint_rhs(const Eigen::VectorXd &x, const varform::DifferentiableVarForm &varform, const DiffCache &diff_cache) const override
Eigen::VectorXd eval2_grad(Eigen::VectorXd &x) const
MinTargetDistForm(const VariableToSimulationGroup &variable_to_simulations, const std::vector< int > &steps, const Eigen::VectorXd &target, const json &args, const std::shared_ptr< varform::DifferentiableVarForm > &varform, std::shared_ptr< const DiffCache > diff_cache)
std::string name() const override
std::shared_ptr< const DiffCache > diff_cache_
double value_unweighted_step(const int time_step, const Eigen::VectorXd &x) const override
NodeTargetForm(std::shared_ptr< const varform::DifferentiableVarForm > varform, std::shared_ptr< const DiffCache > diff_cache, const VariableToSimulationGroup &variable_to_simulations, const json &args, const io::ResourceIO &resources)
void compute_partial_gradient_step(const int time_step, const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
Eigen::MatrixXd target_vertex_positions
std::shared_ptr< const varform::DifferentiableVarForm > varform_
Eigen::VectorXd compute_adjoint_rhs_step(const int time_step, const Eigen::VectorXd &x, const varform::DifferentiableVarForm &varform, const DiffCache &diff_cache) const override
void solution_changed_step(const int time_step, const Eigen::VectorXd &new_x) override
std::shared_ptr< const varform::DifferentiableVarForm > varform_
void set_reference(const std::shared_ptr< const varform::DifferentiableVarForm > &target_varform, std::shared_ptr< const DiffCache > target_diff_cache, const std::set< int > &reference_cached_body_ids)
std::shared_ptr< const varform::DifferentiableVarForm > target_varform_
IntegrableFunctional get_integral_functional() const override
std::shared_ptr< const DiffCache > target_diff_cache_
std::array< utils::ExpressionValue, 3 > target_func_grad
const io::ResourceIO & resources_
utils::ExpressionValue target_func
std::map< int, int > e_to_ref_e_
std::vector< bool > active_dimension_mask
virtual std::string name() const override
Eigen::VectorXd apply_parametrization_jacobian(ParameterType type, const varform::DifferentiableVarForm &target, const Eigen::VectorXd &x, const std::function< Eigen::VectorXd()> &grad) const
Compute parametrization jacobian for all var2sim matching parameter type and output to target varform...
void init(const json &vals, const io::ResourceIO &resources)
Optimization-facing interface implemented by differentiated VarForm adapters.
virtual const FESpace & primary_space() const =0
auto create_thread_storage(const LocalStorage &initial_local_storage)
void maybe_parallel_for(int size, const std::function< void(int, int, int)> &partial_for)
spdlog::logger & adjoint_logger()
Retrieves the current logger for adjoint.
Definition Logger.cpp:30
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_adjoint_error(const std::string &msg)
Definition Logger.cpp:79
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:73
Parameters for the functional evaluation.