PolyFEM
Loading...
Searching...
No Matches
PeriodicMeshToMesh.cpp
Go to the documentation of this file.
4
5namespace polyfem::solver
6{
8 {
9 dim_ = V.cols();
10
11 assert(dim_ == V.cols());
12 const int n_verts = V.rows();
13
14 Eigen::VectorXd min = V.colwise().minCoeff();
15 Eigen::VectorXd max = V.colwise().maxCoeff();
16 Eigen::VectorXd scale_ = max - min;
17
19 dependent_map.resize(n_verts);
20 dependent_map.setConstant(-1);
21
22 const double eps = 1e-4 * scale_.maxCoeff();
23 Eigen::VectorXi boundary_indices;
24 {
25 Eigen::VectorXi boundary_mask1 = ((V.rowwise() - min.transpose()).rowwise().minCoeff().array() < eps).select(Eigen::VectorXi::Ones(V.rows()), Eigen::VectorXi::Zero(V.rows()));
26 Eigen::VectorXi boundary_mask2 = ((V.rowwise() - max.transpose()).rowwise().maxCoeff().array() > -eps).select(Eigen::VectorXi::Ones(V.rows()), Eigen::VectorXi::Zero(V.rows()));
27 Eigen::VectorXi boundary_mask = boundary_mask1.array() + boundary_mask2.array();
28
29 boundary_indices.setZero(boundary_mask.sum());
30 for (int i = 0, j = 0; i < boundary_mask.size(); i++)
31 if (boundary_mask[i])
32 boundary_indices[j++] = i;
33 }
34
35 // find corresponding periodic boundary nodes
36 Eigen::MatrixXd V_boundary = V(boundary_indices, Eigen::all);
37 for (int d = 0; d < dim_; d++)
38 {
39 Eigen::VectorXi mask1 = (V_boundary.col(d).array() < min(d) + eps).select(Eigen::VectorXi::Ones(V_boundary.rows()), Eigen::VectorXi::Zero(V_boundary.rows()));
40 Eigen::VectorXi mask2 = (V_boundary.col(d).array() > max(d) - eps).select(Eigen::VectorXi::Ones(V_boundary.rows()), Eigen::VectorXi::Zero(V_boundary.rows()));
41
42 for (int i = 0; i < mask1.size(); i++)
43 {
44 if (!mask1(i))
45 continue;
46
47 bool found_target = false;
48 for (int j = 0; j < mask2.size(); j++)
49 {
50 if (!mask2(j))
51 continue;
52
53 RowVectorNd projected_diff = V_boundary.row(j) - V_boundary.row(i);
54 projected_diff(d) = 0;
55 if (projected_diff.norm() < eps)
56 {
57 dependent_map(boundary_indices[j]) = boundary_indices[i];
58 std::array<int, 2> pair = {{boundary_indices[i], boundary_indices[j]}};
59 periodic_dependence[d].insert(pair);
60 found_target = true;
61 break;
62 }
63 }
64 if (!found_target)
65 log_and_throw_error("Periodic mesh failed to find corresponding node for {} in {} direction!", V_boundary.row(i), (std::vector<char>{'X','Y','Z'})[d]);
66 }
67 }
68
69 // break dependency chains into direct dependency
70 for (int d = 0; d < dim_; d++)
71 for (int i = 0; i < dependent_map.size(); i++)
72 if (dependent_map(i) >= 0 && dependent_map(dependent_map(i)) >= 0)
74
75 Eigen::VectorXi reduce_map;
76 reduce_map.setZero(dependent_map.size());
77 for (int i = 0; i < dependent_map.size(); i++)
78 if (dependent_map(i) < 0)
79 reduce_map(i) = n_periodic_dof_++;
80 for (int i = 0; i < dependent_map.size(); i++)
81 if (dependent_map(i) >= 0)
82 reduce_map(i) = reduce_map(dependent_map(i));
83
84 dependent_map = std::move(reduce_map);
85 }
86
87 Eigen::VectorXd PeriodicMeshToMesh::eval(const Eigen::VectorXd &x) const
88 {
89 assert(x.size() == input_size());
90
91 Eigen::MatrixXd affine = utils::unflatten(x.tail(dim_ * dim_), dim_).transpose();
92 Eigen::VectorXd y;
93 y.setZero(size(x.size()));
94 for (int i = 0; i < dependent_map.size(); i++)
95 y.segment(i * dim_, dim_) = affine * x.segment(dependent_map(i) * dim_, dim_);
96
97 for (int d = 0; d < dim_; d++)
98 {
99 const auto &dependence_list = periodic_dependence[d];
100 for (const auto &pair : dependence_list)
101 y.segment(pair[1] * dim_, dim_) += affine.col(d);
102 }
103
104 return y;
105 }
106
107 Eigen::VectorXd PeriodicMeshToMesh::inverse_eval(const Eigen::VectorXd &y)
108 {
109 assert(y.size() == dim_ * dependent_map.size());
110 Eigen::VectorXd x;
111 x.setZero(input_size());
112
113 Eigen::MatrixXd V = utils::unflatten(y, dim_);
114 Eigen::VectorXd min = V.colwise().minCoeff();
115 Eigen::VectorXd max = V.colwise().maxCoeff();
116 Eigen::VectorXd scale = max - min;
117 Eigen::MatrixXd affine = scale.asDiagonal();
118 x.tail(dim_ * dim_) = Eigen::Map<Eigen::VectorXd>(affine.data(), dim_ * dim_, 1);
119
120 Eigen::VectorXd z = y;
121 for (int d = 0; d < dim_; d++)
122 {
123 const auto &dependence_list = periodic_dependence[d];
124 for (const auto &pair : dependence_list)
125 z(pair[1] * dim_ + d) -= scale[d];
126 }
127
128 for (int i = 0; i < dependent_map.size(); i++)
129 x.segment(dependent_map(i) * dim_, dim_) = z.segment(i * dim_, dim_).array() / scale.array();
130
131 if ((y - eval(x)).norm() > 1e-5)
132 log_and_throw_adjoint_error("Non-periodic mesh detected!");
133
134 return x;
135 }
136
137 Eigen::VectorXd PeriodicMeshToMesh::apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const
138 {
139 assert(x.size() == input_size());
140 Eigen::VectorXd reduced_grad;
141 reduced_grad.setZero(x.size());
142
143 Eigen::MatrixXd affine = utils::unflatten(x.tail(dim_ * dim_), dim_).transpose();
144
145 for (int i = 0; i < dependent_map.size(); i++)
146 reduced_grad.segment(dependent_map(i) * dim_, dim_) += affine.transpose() * grad.segment(i * dim_, dim_);
147
148 for (int i = 0; i < dependent_map.size(); i++)
149 {
150 Eigen::MatrixXd tmp = grad.segment(i * dim_, dim_) * x.segment(dependent_map(i) * dim_, dim_).transpose();
151 reduced_grad.tail(dim_ * dim_) += utils::flatten(tmp.transpose());
152 }
153
154 for (int d = 0; d < dim_; d++)
155 {
156 const auto &dependence_list = periodic_dependence[d];
157 for (const auto &pair : dependence_list)
158 reduced_grad.segment(dim_ * n_periodic_dof_ + d * dim_, dim_) += grad.segment(pair[1] * dim_, dim_);
159 }
160
161 return reduced_grad;
162 }
163}
int V
int y
int z
int x
Eigen::VectorXd inverse_eval(const Eigen::VectorXd &y) override
PeriodicMeshToMesh(const Eigen::MatrixXd &V)
Eigen::VectorXd eval(const Eigen::VectorXd &x) const override
Eigen::VectorXd apply_jacobian(const Eigen::VectorXd &grad, const Eigen::VectorXd &x) const override
int size(const int x_size) const override
std::array< std::set< std::array< int, 2 > >, 3 > periodic_dependence
Eigen::MatrixXd unflatten(const Eigen::VectorXd &x, int dim)
Unflatten rowwises, so every dim elements in x become a row.
Eigen::VectorXd flatten(const Eigen::MatrixXd &X)
Flatten rowwises.
void log_and_throw_adjoint_error(const std::string &msg)
Definition Logger.cpp:77
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:71