17#include <spdlog/fmt/fmt.h>
37 class LocalThreadScalarStorage
43 LocalThreadScalarStorage()
58 int e_ref = params.elem;
60 e_ref = search->second;
62 Eigen::MatrixXd pts_ref;
63 target_varform_->primary_space().geometry_basis_list()[e_ref].eval_geom_mapping(local_pts, pts_ref);
65 Eigen::MatrixXd u_ref, grad_u_ref;
69 val = (u_ref + pts_ref - u - pts).rowwise().squaredNorm();
73 int e_ref = params.elem;
75 e_ref = search->second;
77 Eigen::MatrixXd pts_ref;
78 target_varform_->primary_space().geometry_basis_list()[e_ref].eval_geom_mapping(local_pts, pts_ref);
80 Eigen::MatrixXd u_ref, grad_u_ref;
84 val = 2 * (u + pts - u_ref - pts_ref);
94 val.setZero(u.rows(), 1);
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);
102 val.setZero(u.rows(), u.cols());
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);
119 val.setZero(u.rows(), 1);
121 for (
int q = 0; q < u.rows(); q++)
123 Eigen::VectorXd err = u.row(q) - this->
target_disp.transpose();
127 val(q) = err.squaredNorm();
131 val.setZero(u.rows(), u.cols());
133 for (
int q = 0; q < u.rows(); q++)
135 Eigen::VectorXd err = u.row(q) - this->
target_disp.transpose();
139 val.row(q) = 2 * err;
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)
158 std::map<int, std::vector<int>> ref_interested_body_id_to_e;
160 for (
int e = 0; e <
target_varform_->primary_space().basis_list().size(); ++e)
163 if (reference_cached_body_ids.size() > 0 && reference_cached_body_ids.count(body_id) == 0)
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);
168 ref_interested_body_id_to_e[body_id] = {e};
172 std::map<int, std::vector<int>> interested_body_id_to_e;
174 for (
int e = 0; e <
varform_->primary_space().basis_list().size(); ++e)
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)
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);
182 interested_body_id_to_e[body_id] = {e};
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);
191 for (
const auto &kv : interested_body_id_to_e)
193 for (
int i = 0; i < kv.second.size(); ++i)
195 e_to_ref_e_[kv.second[i]] = ref_interested_body_id_to_e[kv.first][i];
203 for (
size_t k = 0; k < grad_func.size(); k++)
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;
216 LocalThreadScalarStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
218 Eigen::MatrixXd uv, samples, gtmp;
219 Eigen::MatrixXd points, normal;
220 Eigen::VectorXd weights;
222 Eigen::MatrixXd u, grad_u;
224 for (int lb_id = start; lb_id < end; ++lb_id)
226 const auto &lb = varform_->boundary_state().total_local_boundary[lb_id];
227 const int e = lb.element_id();
229 for (int i = 0; i < lb.size(); i++)
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())
235 utils::BoundarySampler::boundary_quadrature(lb, varform_->n_boundary_samples(), varform_->get_mesh(), i, false, uv, points, normal, weights);
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);
241 normal = normal * vals.jac_it[0];
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));
250 void SDFTargetForm::set_bspline_target(
const Eigen::MatrixXd &control_points,
const Eigen::VectorXd &knots,
const double delta)
252 dim = control_points.cols();
254 if ((dim != 2) || (varform_->get_mesh().dimension() != 2))
259 nanospline::BSpline<double, 2, 3> curve;
260 curve.set_control_points(control_points);
261 curve.set_knots(knots);
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));
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);
275 interpolation_fn = std::make_unique<LazyCubicInterpolator>(dim, delta_);
278 void SDFTargetForm::set_bspline_target(
const Eigen::MatrixXd &control_points,
const Eigen::VectorXd &knots_u,
const Eigen::VectorXd &knots_v,
const double delta)
281 dim = control_points.cols();
283 if ((dim != 3) || (varform_->get_mesh().dimension() != 3))
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);
294 t_or_uv_sampling.resize(samples * samples, 2);
295 for (
int i = 0; i < samples; ++i)
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);
300 point_sampling.setZero(samples * samples, 3);
301 for (
int i = 0; i < t_or_uv_sampling.rows(); ++i)
303 point_sampling.row(i) = patch.evaluate(t_or_uv_sampling(i, 0), t_or_uv_sampling(i, 1));
307 Eigen::MatrixXi
F(2 * ((samples - 1) * (samples - 1)), 3);
309 for (
int i = 0; i < samples - 1; ++i)
310 for (
int j = 0; j < samples - 1; ++j)
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;
321 interpolation_fn = std::make_unique<LazyCubicInterpolator>(dim, delta_);
324 void SDFTargetForm::compute_distance(
const Eigen::MatrixXd &
point,
double &distance)
const
327 Eigen::MatrixXd p =
point.transpose();
330 for (
int i = 0; i < t_or_uv_sampling.size() - 1; ++i)
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;
342 for (
int i = 0; i < samples - 1; ++i)
343 for (
int j = 0; j < samples - 1; ++j)
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));
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));
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;
367 val.setZero(u.rows(), 1);
369 for (
int q = 0; q < u.rows(); q++)
372 Eigen::MatrixXd unused_grad;
373 interpolation_fn->
evaluate(u.row(q) + pts.row(q), distance, unused_grad);
374 val(q) = pow(distance, 2);
379 val.setZero(u.rows(), u.cols());
381 for (
int q = 0; q < u.rows(); q++)
384 Eigen::MatrixXd grad;
385 interpolation_fn->evaluate(u.row(q) + pts.row(q), distance, grad);
386 val.row(q) = 2 * distance * grad.transpose();
397 void MeshTargetForm::set_surface_mesh_target(
const Eigen::MatrixXd &
V,
const Eigen::MatrixXi &
F,
const double delta)
401 if ((dim != 3) || (varform_->get_mesh().dimension() != 3))
408 interpolation_fn = std::make_unique<LazyCubicInterpolator>(dim, delta_);
411 void MeshTargetForm::solution_changed_step(
const int time_step,
const Eigen::VectorXd &
x)
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;
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);
421 Eigen::MatrixXd uv, samples, gtmp;
422 Eigen::MatrixXd points, normal;
423 Eigen::VectorXd weights;
425 Eigen::MatrixXd u, grad_u;
427 for (int lb_id = start; lb_id < end; ++lb_id)
429 const auto &lb = varform_->boundary_state().total_local_boundary[lb_id];
430 const int e = lb.element_id();
432 for (int i = 0; i < lb.size(); i++)
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())
438 utils::BoundarySampler::boundary_quadrature(lb, varform_->n_boundary_samples(), varform_->get_mesh(), i, false, uv, points, normal, weights);
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);
444 normal = normal * vals.jac_it[0];
446 for (int q = 0; q < u.rows(); q++)
447 interpolation_fn->cache_grid([this](const Eigen::MatrixXd &point, double &distance) {
449 Eigen::Matrix<double, 1, 3> closest;
450 distance = pow(tree_.squared_distance(V_, F_, point.col(0), idx, closest), 0.5);
452 vals.val.row(q) + u.row(q));
462 val.setZero(u.rows(), 1);
464 for (
int q = 0; q < u.rows(); q++)
467 Eigen::MatrixXd unused_grad;
468 interpolation_fn->
evaluate(u.row(q) + pts.row(q), distance, unused_grad);
469 val(q) = pow(distance, 2);
474 val.setZero(u.rows(), u.cols());
476 for (
int q = 0; q < u.rows(); q++)
479 Eigen::MatrixXd grad;
480 interpolation_fn->evaluate(u.row(q) + pts.row(q), distance, grad);
481 val.row(q) = 2 * distance * grad.transpose();
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))
495 const int dim =
varform_->get_mesh().dimension();
498 const Eigen::MatrixXd data = resources.
read_matrix(args[
"target_data_path"].get<std::string>());
503 for (
int s = 0; s < data.rows(); s++)
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++)
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"])
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_)
534 rhs.setZero(diff_cache.
u(0).size());
536 const int dim =
varform_->get_mesh().dimension();
541 const Eigen::VectorXd disp =
diff_cache_->u(time_step);
544 const RowVectorNd cur_pos =
varform_->primary_space().mesh_nodes->node_position(v) + disp.segment(v * dim, dim).transpose();
555 const int dim =
varform_->get_mesh().dimension();
558 const Eigen::VectorXd disp =
diff_cache_->u(time_step);
561 const RowVectorNd cur_pos =
varform_->primary_space().mesh_nodes->node_position(v) + disp.segment(v * dim, dim).transpose();
569 gradv.setZero(
x.size());
572 return Eigen::VectorXd::Zero(0).eval();
579 dim = varform1->get_mesh().dimension();
580 json tmp_args = args;
581 for (
int d = 0; d <
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));
591 Eigen::VectorXd term;
593 for (
int d = 0; d <
dim; d++)
595 double value =
center1[d]->value_unweighted_step(time_step,
x) -
center2[d]->value_unweighted_step(time_step,
x);
602 gradv.setZero(
x.size());
603 Eigen::VectorXd tmp1, tmp2;
604 for (
int d = 0; d <
dim; d++)
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);
616 for (
int d = 0; d <
dim; d++)
623 :
AdjointForm(variable_to_simulations), steps_(steps), target_(target)
625 dim = varform->get_mesh().dimension();
626 json tmp_args = args;
627 for (
int d = 0; d <
dim; d++)
630 objs.push_back(std::make_unique<PositionForm>(variable_to_simulations, varform, diff_cache, tmp_args));
632 objs.push_back(std::make_unique<VolumeForm>(variable_to_simulations, varform, diff_cache, args));
636 Eigen::VectorXd values(
steps_.size());
638 Eigen::MatrixXd g2(
steps_.size(),
objs.size());
642 Eigen::VectorXd input(
objs.size());
644 for (
int d = 0; d <
objs.size(); d++)
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);
649 values[i] =
eval2(input);
658 terms.col(s) += g1(i) * grads[i] * g2.row(i).transpose();
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());
672 Eigen::VectorXd input(
objs.size());
674 for (
int d = 0; d <
objs.size(); d++)
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;
680 values[i] =
eval2(input);
685 gradv.setZero(
x.size());
689 gradv += g1(i) * grads[i] * g2.row(i).transpose();
696 Eigen::VectorXd values(
steps_.size());
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);
706 return eval1(values);
ElementAssemblyValues vals
Storage for additional data required by differntial code.
Eigen::VectorXd u(int step) const
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 ¶ms, 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)
Read-only interface to a hierarchical collection of simulation resources.
virtual Eigen::MatrixXd read_matrix(const std::string &path) const =0
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)
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.
void log_and_throw_adjoint_error(const std::string &msg)
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
void log_and_throw_error(const std::string &msg)
Parameters for the functional evaluation.