PolyFEM
Loading...
Searching...
No Matches
SmoothingForms.cpp
Go to the documentation of this file.
2
6
7#include <Eigen/Core>
8
9#include <cassert>
10#include <memory>
11#include <numeric>
12#include <set>
13#include <utility>
14#include <vector>
15
16namespace polyfem::solver
17{
19 const VariableToSimulationGroup &variable_to_simulations,
20 std::shared_ptr<const varform::DifferentiableVarForm> varform,
21 const bool scale_invariant,
22 const int power,
23 const std::vector<int> &surface_selections,
24 const std::vector<int> &active_dims) : AdjointForm(variable_to_simulations), varform_(std::move(varform)), scale_invariant_(scale_invariant), power_(power), active_dims_(active_dims)
25 {
26 const auto &mesh = varform_->get_mesh();
27 const int dim = mesh.dimension();
28 const int n_verts = mesh.n_vertices();
29 assert(mesh.is_simplicial());
30 // empty implies all active.
31 if (active_dims_.empty())
32 {
33 active_dims_.resize(dim);
34 std::iota(active_dims_.begin(), active_dims_.end(), 0);
35 }
36
37 surface_ids_ = std::set(surface_selections.begin(), surface_selections.end());
38
39 // collect active nodes
40 std::vector<bool> active_mask;
41 active_mask.assign(n_verts, false);
42 std::vector<Eigen::Triplet<bool>> T_adj;
43
44 for (int b = 0; b < mesh.n_boundary_elements(); b++)
45 {
46 const int boundary_id = mesh.get_boundary_id(b);
47 if (!surface_ids_.empty() && surface_ids_.find(boundary_id) == surface_ids_.end())
48 continue;
49
50 for (int lv = 0; lv < dim; lv++)
51 {
52 active_mask[mesh.boundary_element_vertex(b, lv)] = true;
53 }
54
55 for (int lv1 = 0; lv1 < dim; lv1++)
56 for (int lv2 = 0; lv2 < lv1; lv2++)
57 {
58 const int v1 = mesh.boundary_element_vertex(b, lv1);
59 const int v2 = mesh.boundary_element_vertex(b, lv2);
60 T_adj.emplace_back(v2, v1, true);
61 T_adj.emplace_back(v1, v2, true);
62 }
63 }
64
65 adj.setZero();
66 adj.resize(n_verts, n_verts);
67 adj.setFromTriplets(T_adj.begin(), T_adj.end());
68
69 std::vector<int> degrees(n_verts, 0);
70 for (int k = 0; k < adj.outerSize(); ++k)
71 for (Eigen::SparseMatrix<bool, Eigen::RowMajor>::InnerIterator it(adj, k); it; ++it)
72 degrees[k]++;
73
74 L.setZero();
75 L.resize(n_verts, n_verts);
77 {
78 std::vector<Eigen::Triplet<double>> T_L;
79 for (int k = 0; k < adj.outerSize(); ++k)
80 {
81 if (!active_mask[k])
82 continue;
83 T_L.emplace_back(k, k, 1);
84 for (Eigen::SparseMatrix<bool, Eigen::RowMajor>::InnerIterator it(adj, k); it; ++it)
85 {
86 assert(it.row() == k);
87 T_L.emplace_back(it.row(), it.col(), -1. / degrees[k]);
88 }
89 }
90 L.setFromTriplets(T_L.begin(), T_L.end());
91 L.prune([](int i, int j, double val) { return abs(val) > 1e-12; });
92 }
93 }
94
95 double BoundarySmoothingForm::value_unweighted(const Eigen::VectorXd &x) const
96 {
97 const auto &mesh = varform_->get_mesh();
98 const int dim = mesh.dimension();
99 const int n_verts = mesh.n_vertices();
100
101 double val = 0;
103 {
104 for (int b = 0; b < adj.rows(); b++)
105 {
107 s.setZero(dim);
108 double sum_norm = 0;
109 int valence = 0;
110 for (Eigen::SparseMatrix<bool, Eigen::RowMajor>::InnerIterator it(adj, b); it; ++it)
111 {
112 assert(it.col() != b);
113 polyfem::RowVectorNd x = mesh.point(b) - mesh.point(it.col());
114 s += x;
115 sum_norm += x.norm();
116 valence += 1;
117 }
118 if (valence)
119 {
120 s = s / sum_norm;
121 val += pow(s.norm(), power_);
122 }
123 }
124 }
125 else
126 {
127 Eigen::MatrixXd V;
128 varform_->get_vertices(V);
129
130 val = (L * V(Eigen::all, active_dims_)).squaredNorm();
131 }
132
133 return val;
134 }
135
136 void BoundarySmoothingForm::compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
137 {
138 const auto &mesh = varform_->get_mesh();
139 const int dim = mesh.dimension();
140 const int n_verts = mesh.n_vertices();
141
142 Eigen::VectorXd grad;
144 {
145 grad.setZero(n_verts * dim);
146 for (int b = 0; b < adj.rows(); b++)
147 {
149 s.setZero(dim);
150 double sum_norm = 0;
151 polyfem::RowVectorNd sum_normalized = s;
152 int valence = 0;
153 for (Eigen::SparseMatrix<bool, Eigen::RowMajor>::InnerIterator it(adj, b); it; ++it)
154 {
155 assert(it.col() != b);
156 polyfem::RowVectorNd x = mesh.point(b) - mesh.point(it.col());
157 s += x;
158 sum_norm += x.norm();
159 sum_normalized += x.normalized();
160 valence += 1;
161 }
162 if (valence)
163 {
164 s = s / sum_norm;
165 const double coeff = power_ * pow(s.norm(), power_ - 2.) / sum_norm;
166
167 grad.segment(b * dim, dim) += (s * valence - s.squaredNorm() * sum_normalized) * coeff;
168 for (Eigen::SparseMatrix<bool, Eigen::RowMajor>::InnerIterator it(adj, b); it; ++it)
169 grad.segment(it.col() * dim, dim) -= (s + s.squaredNorm() * (mesh.point(it.col()) - mesh.point(b)).normalized()) * coeff;
170 }
171 }
172 }
173 else
174 {
175 Eigen::MatrixXd V;
176 varform_->get_vertices(V);
177
178 Eigen::MatrixXd grad_mat = 2 * (L.transpose() * (L * V));
179 for (int d = 0; d < dim; d++)
180 if (std::find(active_dims_.begin(), active_dims_.end(), d) == active_dims_.end())
181 grad_mat.col(d).setZero();
182 grad = utils::flatten(grad_mat);
183 }
184
186 return grad;
187 });
188 }
189} // namespace polyfem::solver
int V
double val
Definition Assembler.cpp:90
int x
const VariableToSimulationGroup variable_to_simulations_
BoundarySmoothingForm(const VariableToSimulationGroup &variable_to_simulations, std::shared_ptr< const varform::DifferentiableVarForm > varform, const bool scale_invariant, const int power, const std::vector< int > &surface_selections, const std::vector< int > &active_dims)
Eigen::SparseMatrix< bool, Eigen::RowMajor > adj
std::shared_ptr< const varform::DifferentiableVarForm > varform_
double value_unweighted(const Eigen::VectorXd &x) const override
Compute the value of the form.
void compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
Eigen::SparseMatrix< double, Eigen::RowMajor > L
virtual double weight() const
Get the form's multiplicative constant weight.
Definition Form.hpp:128
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...
Eigen::VectorXd flatten(const Eigen::MatrixXd &X)
Flatten rowwises.
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13