18#include <spdlog/fmt/fmt.h>
38 class LocalThreadScalarStorage
44 LocalThreadScalarStorage()
59 int e_ref = params.elem;
61 e_ref = search->second;
63 Eigen::MatrixXd pts_ref;
64 target_state_->geom_bases()[e_ref].eval_geom_mapping(local_pts, pts_ref);
66 Eigen::MatrixXd u_ref, grad_u_ref;
70 val = (u_ref + pts_ref - u - pts).rowwise().squaredNorm();
74 int e_ref = params.elem;
76 e_ref = search->second;
78 Eigen::MatrixXd pts_ref;
79 target_state_->geom_bases()[e_ref].eval_geom_mapping(local_pts, pts_ref);
81 Eigen::MatrixXd u_ref, grad_u_ref;
85 val = 2 * (u + pts - u_ref - pts_ref);
95 val.setZero(u.rows(), 1);
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);
103 val.setZero(u.rows(), u.cols());
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);
120 val.setZero(u.rows(), 1);
122 for (
int q = 0; q < u.rows(); q++)
124 Eigen::VectorXd err = u.row(q) - this->
target_disp.transpose();
128 val(q) = err.squaredNorm();
132 val.setZero(u.rows(), u.cols());
134 for (
int q = 0; q < u.rows(); q++)
136 Eigen::VectorXd err = u.row(q) - this->
target_disp.transpose();
140 val.row(q) = 2 * err;
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)
159 std::map<int, std::vector<int>> ref_interested_body_id_to_e;
164 if (reference_cached_body_ids.size() > 0 && reference_cached_body_ids.count(body_id) == 0)
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);
169 ref_interested_body_id_to_e[body_id] = {e};
173 std::map<int, std::vector<int>> interested_body_id_to_e;
175 for (
int e = 0; e <
state_->bases.size(); ++e)
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)
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);
183 interested_body_id_to_e[body_id] = {e};
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);
192 for (
const auto &kv : interested_body_id_to_e)
194 for (
int i = 0; i < kv.second.size(); ++i)
196 e_to_ref_e_[kv.second[i]] = ref_interested_body_id_to_e[kv.first][i];
204 for (
size_t k = 0; k < grad_func.size(); k++)
211 const auto &bases =
state_->bases;
212 const auto &gbases =
state_->geom_bases();
213 const int actual_dim =
state_->problem->is_scalar() ? 1 :
dim;
217 LocalThreadScalarStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
219 Eigen::MatrixXd uv, samples, gtmp;
220 Eigen::MatrixXd points, normal;
221 Eigen::VectorXd weights;
223 Eigen::MatrixXd u, grad_u;
225 for (int lb_id = start; lb_id < end; ++lb_id)
227 const auto &lb = state_->total_local_boundary[lb_id];
228 const int e = lb.element_id();
230 for (int i = 0; i < lb.size(); i++)
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())
236 utils::BoundarySampler::boundary_quadrature(lb, state_->n_boundary_samples(), *state_->mesh, i, false, uv, points, normal, weights);
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);
242 normal = normal * vals.jac_it[0];
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));
253 dim = control_points.cols();
255 if ((
dim != 2) || (
state_->mesh->dimension() != 2))
260 nanospline::BSpline<double, 2, 3> curve;
261 curve.set_control_points(control_points);
262 curve.set_knots(knots);
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);
282 dim = control_points.cols();
284 if ((
dim != 3) || (
state_->mesh->dimension() != 3))
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);
296 for (
int i = 0; i <
samples; ++i)
310 for (
int i = 0; i <
samples - 1; ++i)
311 for (
int j = 0; j <
samples - 1; ++j)
313 Eigen::MatrixXi F_local(2, 3);
316 F.block(f, 0, 2, 3) = F_local;
328 Eigen::MatrixXd p = point.transpose();
335 const double t = std::max(0., std::min(1., distance_to_perpendicular));
337 const double project_distance = (p - project).norm();
338 if (project_distance < distance)
339 distance = project_distance;
343 for (
int i = 0; i <
samples - 1; ++i)
344 for (
int j = 0; j <
samples - 1; ++j)
349 const double u = std::max(0., std::min(1., distance_to_perpendicular));
353 const double v = std::max(0., std::min(1., distance_to_perpendicular));
357 const double project_distance = (p - project).norm();
358 if (project_distance < distance)
359 distance = project_distance;
368 val.setZero(u.rows(), 1);
370 for (
int q = 0; q < u.rows(); q++)
373 Eigen::MatrixXd unused_grad;
375 val(q) = pow(distance, 2);
380 val.setZero(u.rows(), u.cols());
382 for (
int q = 0; q < u.rows(); q++)
385 Eigen::MatrixXd grad;
387 val.row(q) = 2 * distance * grad.transpose();
402 if ((
dim != 3) || (
state_->mesh->dimension() != 3))
414 const auto &bases =
state_->bases;
415 const auto &gbases =
state_->geom_bases();
416 const int actual_dim =
state_->problem->is_scalar() ? 1 :
dim;
420 LocalThreadScalarStorage &local_storage = utils::get_local_thread_storage(storage, thread_id);
422 Eigen::MatrixXd uv, samples, gtmp;
423 Eigen::MatrixXd points, normal;
424 Eigen::VectorXd weights;
426 Eigen::MatrixXd u, grad_u;
428 for (int lb_id = start; lb_id < end; ++lb_id)
430 const auto &lb = state_->total_local_boundary[lb_id];
431 const int e = lb.element_id();
433 for (int i = 0; i < lb.size(); i++)
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())
439 utils::BoundarySampler::boundary_quadrature(lb, state_->n_boundary_samples(), *state_->mesh, i, false, uv, points, normal, weights);
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);
445 normal = normal * vals.jac_it[0];
447 for (int q = 0; q < u.rows(); q++)
448 interpolation_fn->cache_grid([this](const Eigen::MatrixXd &point, double &distance) {
450 Eigen::Matrix<double, 1, 3> closest;
451 distance = pow(tree_.squared_distance(V_, F_, point.col(0), idx, closest), 0.5);
453 vals.val.row(q) + u.row(q));
463 val.setZero(u.rows(), 1);
465 for (
int q = 0; q < u.rows(); q++)
468 Eigen::MatrixXd unused_grad;
470 val(q) = pow(distance, 2);
475 val.setZero(u.rows(), u.cols());
477 for (
int q = 0; q < u.rows(); q++)
480 Eigen::MatrixXd grad;
482 val.row(q) = 2 * distance * grad.transpose();
494 :
StaticForm(variable_to_simulations), state_(std::move(state)), diff_cache_(std::move(diff_cache))
496 const int dim =
state_->mesh->dimension();
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))
502 throw std::runtime_error(
"Marker path invalid!");
505 Eigen::MatrixXd data;
511 for (
int s = 0; s < data.rows(); s++)
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++)
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"])
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_)
542 rhs.setZero(diff_cache.
u(0).size());
544 const int dim =
state_->mesh->dimension();
546 if (&state ==
state_.get())
549 const Eigen::VectorXd disp =
diff_cache_->u(time_step);
552 const RowVectorNd cur_pos =
state_->mesh_nodes->node_position(v) + disp.segment(v * dim, dim).transpose();
563 const int dim =
state_->mesh->dimension();
566 const Eigen::VectorXd disp =
diff_cache_->u(time_step);
569 const RowVectorNd cur_pos =
state_->mesh_nodes->node_position(v) + disp.segment(v * dim, dim).transpose();
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();
587 dim = state1->mesh->dimension();
588 json tmp_args = args;
589 for (
int d = 0; d <
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));
599 Eigen::VectorXd term;
600 term.setZero(state.
ndof());
601 for (
int d = 0; d <
dim; d++)
603 double value =
center1[d]->value_unweighted_step(time_step,
x) -
center2[d]->value_unweighted_step(time_step,
x);
610 gradv.setZero(
x.size());
611 Eigen::VectorXd tmp1, tmp2;
612 for (
int d = 0; d <
dim; d++)
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);
624 for (
int d = 0; d <
dim; d++)
631 :
AdjointForm(variable_to_simulations), steps_(steps), target_(target)
633 dim = state->mesh->dimension();
634 json tmp_args = args;
635 for (
int d = 0; d <
dim; d++)
638 objs.push_back(std::make_unique<PositionForm>(variable_to_simulations, state, diff_cache, tmp_args));
640 objs.push_back(std::make_unique<VolumeForm>(variable_to_simulations, state, diff_cache, args));
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());
650 Eigen::VectorXd input(
objs.size());
652 for (
int d = 0; d <
objs.size(); d++)
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);
657 values[i] =
eval2(input);
662 Eigen::MatrixXd terms = Eigen::MatrixXd::Zero(state.
ndof(), diff_cache.
size());
666 terms.col(s) += g1(i) * grads[i] * g2.row(i).transpose();
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());
680 Eigen::VectorXd input(
objs.size());
682 for (
int d = 0; d <
objs.size(); d++)
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;
688 values[i] =
eval2(input);
693 gradv.setZero(
x.size());
697 gradv += g1(i) * grads[i] * g2.row(i).transpose();
704 Eigen::VectorXd values(
steps_.size());
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);
714 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)
main class that contains the polyfem solver and all its state
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)
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.
void init(const json &vals)
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.
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.