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