PolyFEM
Loading...
Searching...
No Matches
AMIPSForm.cpp
Go to the documentation of this file.
1#include "AMIPSForm.hpp"
2
3#include <polyfem/State.hpp>
4
7
13
14namespace polyfem::solver
15{
16 namespace
17 {
18 double triangle_jacobian(const Eigen::VectorXd &v1, const Eigen::VectorXd &v2, const Eigen::VectorXd &v3)
19 {
20 Eigen::VectorXd a = v2 - v1, b = v3 - v1;
21 return a(0) * b(1) - b(0) * a(1);
22 }
23
24 double tet_determinant(const Eigen::VectorXd &v1, const Eigen::VectorXd &v2, const Eigen::VectorXd &v3, const Eigen::VectorXd &v4)
25 {
26 Eigen::Matrix3d mat;
27 mat.col(0) << v2 - v1;
28 mat.col(1) << v3 - v1;
29 mat.col(2) << v4 - v1;
30 return mat.determinant();
31 }
32
33 void scaled_jacobian(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, Eigen::VectorXd &quality)
34 {
35 const int dim = F.cols() - 1;
36
37 quality.setZero(F.rows());
38 if (dim == 2)
39 {
40 for (int i = 0; i < F.rows(); i++)
41 {
42 Eigen::RowVector3d e0;
43 e0(2) = 0;
44 e0.head(2) = V.row(F(i, 2)) - V.row(F(i, 1));
45 Eigen::RowVector3d e1;
46 e1(2) = 0;
47 e1.head(2) = V.row(F(i, 0)) - V.row(F(i, 2));
48 Eigen::RowVector3d e2;
49 e2(2) = 0;
50 e2.head(2) = V.row(F(i, 1)) - V.row(F(i, 0));
51
52 double l0 = e0.norm();
53 double l1 = e1.norm();
54 double l2 = e2.norm();
55
56 double A = 0.5 * (e0.cross(e1)).norm();
57 double Lmax = std::max(l0 * l1, std::max(l1 * l2, l0 * l2));
58
59 quality(i) = 2 * A * (2 / sqrt(3)) / Lmax;
60 }
61 }
62 else
63 {
64 for (int i = 0; i < F.rows(); i++)
65 {
66 Eigen::RowVector3d e0 = V.row(F(i, 1)) - V.row(F(i, 0));
67 Eigen::RowVector3d e1 = V.row(F(i, 2)) - V.row(F(i, 1));
68 Eigen::RowVector3d e2 = V.row(F(i, 0)) - V.row(F(i, 2));
69 Eigen::RowVector3d e3 = V.row(F(i, 3)) - V.row(F(i, 0));
70 Eigen::RowVector3d e4 = V.row(F(i, 3)) - V.row(F(i, 1));
71 Eigen::RowVector3d e5 = V.row(F(i, 3)) - V.row(F(i, 2));
72
73 double l0 = e0.norm();
74 double l1 = e1.norm();
75 double l2 = e2.norm();
76 double l3 = e3.norm();
77 double l4 = e4.norm();
78 double l5 = e5.norm();
79
80 double J = std::abs((e0.cross(e3)).dot(e2));
81
82 double a1 = l0 * l2 * l3;
83 double a2 = l0 * l1 * l4;
84 double a3 = l1 * l2 * l5;
85 double a4 = l3 * l4 * l5;
86
87 double a = std::max({a1, a2, a3, a4, J});
88 quality(i) = J * sqrt(2) / a;
89 }
90 }
91 }
92
93 bool is_flipped(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
94 {
95 if (F.cols() == 3)
96 {
97 for (int i = 0; i < F.rows(); i++)
98 if (triangle_jacobian(V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2))) <= 0)
99 return true;
100 }
101 else if (F.cols() == 4)
102 {
103 for (int i = 0; i < F.rows(); i++)
104 if (tet_determinant(V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2)), V.row(F(i, 3))) <= 0)
105 return true;
106 }
107 else
108 {
109 return true;
110 }
111
112 return false;
113 }
114 } // namespace
115
116 double MinJacobianForm::value_unweighted(const Eigen::VectorXd &x) const
117 {
118 const bool is_volume = state_.mesh->is_volume();
119 double min_jacs = std::numeric_limits<double>::max();
120 for (size_t e = 0; e < state_.geom_bases().size(); ++e)
121 {
122 if (state_.mesh->is_polytope(e))
123 continue;
124
125 const auto &gbasis = state_.geom_bases()[e];
126 const int n_local_bases = int(gbasis.bases.size());
127
129 gbasis.compute_quadrature(quad);
130
131 std::vector<assembler::AssemblyValues> tmp;
132
133 Eigen::MatrixXd dx = Eigen::MatrixXd::Zero(quad.points.rows(), quad.points.cols());
134 Eigen::MatrixXd dy = Eigen::MatrixXd::Zero(quad.points.rows(), quad.points.cols());
135 Eigen::MatrixXd dz;
136 if (is_volume)
137 dz = Eigen::MatrixXd::Zero(quad.points.rows(), quad.points.cols());
138
139 gbasis.evaluate_grads(quad.points, tmp);
140
141 for (int j = 0; j < n_local_bases; ++j)
142 {
143 const basis::Basis &b = gbasis.bases[j];
144
145 for (std::size_t ii = 0; ii < b.global().size(); ++ii)
146 {
147 dx += tmp[j].grad.col(0) * b.global()[ii].node * b.global()[ii].val;
148 dy += tmp[j].grad.col(1) * b.global()[ii].node * b.global()[ii].val;
149 if (is_volume)
150 dz += tmp[j].grad.col(2) * b.global()[ii].node * b.global()[ii].val;
151 }
152 }
153
154 for (long i = 0; i < dx.rows(); ++i)
155 {
156 if (is_volume)
157 {
158 Eigen::Matrix3d tmp;
159 tmp << dx.row(i), dy.row(i), dz.row(i);
160 min_jacs = std::min(min_jacs, tmp.determinant());
161 }
162 else
163 {
164 Eigen::Matrix2d tmp;
165 tmp << dx.row(i), dy.row(i);
166 min_jacs = std::min(min_jacs, tmp.determinant());
167 }
168 }
169 }
170
171 return min_jacs;
172 }
173
174 void MinJacobianForm::compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
175 {
176 log_and_throw_adjoint_error("{} is not differentiable!", name());
177 }
178
179 AMIPSForm::AMIPSForm(const VariableToSimulationGroup& variable_to_simulation, const State &state)
180 : AdjointForm(variable_to_simulation),
181 state_(state)
182 {
184 amips_energy_->set_size(state.mesh->dimension());
185
186 json transform_params = {};
187 transform_params["canonical_transformation"] = json::array();
188 if (!state.mesh->is_volume())
189 {
190 Eigen::MatrixXd regular_tri(3, 3);
191 regular_tri << 0, 0, 1,
192 1, 0, 1,
193 1. / 2., std::sqrt(3) / 2., 1;
194 regular_tri.transposeInPlace();
195 Eigen::MatrixXd regular_tri_inv = regular_tri.inverse();
196
197 const auto &mesh2d = *dynamic_cast<mesh::Mesh2D *>(state.mesh.get());
198 for (int e = 0; e < state.mesh->n_elements(); e++)
199 {
200 Eigen::MatrixXd transform;
201 mesh2d.compute_face_jacobian(e, regular_tri_inv, transform);
202 transform_params["canonical_transformation"].push_back(json({
203 {
204 transform(0, 0),
205 transform(0, 1),
206 },
207 {
208 transform(1, 0),
209 transform(1, 1),
210 },
211 }));
212 }
213 }
214 else
215 {
216 Eigen::MatrixXd regular_tet(4, 4);
217 regular_tet << 0, 0, 0, 1,
218 1, 0, 0, 1,
219 1. / 2., std::sqrt(3) / 2., 0, 1,
220 1. / 2., 1. / 2. / std::sqrt(3), std::sqrt(3) / 2., 1;
221 regular_tet.transposeInPlace();
222 Eigen::MatrixXd regular_tet_inv = regular_tet.inverse();
223
224 const auto &mesh3d = *dynamic_cast<mesh::Mesh3D *>(state.mesh.get());
225 for (int e = 0; e < state.mesh->n_elements(); e++)
226 {
227 Eigen::MatrixXd transform;
228 mesh3d.compute_cell_jacobian(e, regular_tet_inv, transform);
229 transform_params["canonical_transformation"].push_back(json({
230 {
231 transform(0, 0),
232 transform(0, 1),
233 transform(0, 2),
234 },
235 {
236 transform(1, 0),
237 transform(1, 1),
238 transform(1, 2),
239 },
240 {
241 transform(2, 0),
242 transform(2, 1),
243 transform(2, 2),
244 },
245 }));
246 }
247 }
248 transform_params["solve_displacement"] = true;
249 amips_energy_->add_multimaterial(0, transform_params, state.units);
250
251 Eigen::MatrixXd V;
256 }
257
258 double AMIPSForm::value_unweighted(const Eigen::VectorXd &x) const
259 {
260 Eigen::VectorXd X = get_updated_mesh_nodes(x);
261
262 return amips_energy_->assemble_energy(state_.mesh->is_volume(), init_geom_bases_, init_geom_bases_, init_ass_vals_cache_, 0, 0, AdjointTools::map_primitive_to_node_order(state_, X - X_rest), Eigen::VectorXd());
263 }
264
265 void AMIPSForm::compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const
266 {
268 const Eigen::VectorXd X = get_updated_mesh_nodes(x);
269 Eigen::MatrixXd grad;
270 amips_energy_->assemble_gradient(state_.mesh->is_volume(), state_.n_geom_bases, init_geom_bases_, init_geom_bases_, init_ass_vals_cache_, 0, 0, AdjointTools::map_primitive_to_node_order(state_, X - X_rest), Eigen::VectorXd(), grad); // grad wrt. gbases
272 });
273 }
274
275 bool AMIPSForm::is_step_valid(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) const
276 {
277 Eigen::VectorXd X = get_updated_mesh_nodes(x1);
278 Eigen::MatrixXd V1 = utils::unflatten(X, state_.mesh->dimension());
279 bool flipped = is_flipped(V1, F);
280
281 if (flipped)
282 adjoint_logger().trace("[{}] Step flips elements.", name());
283
284 return !flipped;
285 }
286}
int V
int x
main class that contains the polyfem solver and all its state
Definition State.hpp:79
void get_vertices(Eigen::MatrixXd &vertices) const
Definition StateDiff.cpp:69
const std::vector< basis::ElementBases > & geom_bases() const
Get a constant reference to the geometry mapping bases.
Definition State.hpp:223
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Definition State.hpp:466
void get_elements(Eigen::MatrixXi &elements) const
Definition StateDiff.cpp:77
int n_geom_bases
number of geometric bases
Definition State.hpp:182
static std::shared_ptr< Assembler > make_assembler(const std::string &formulation)
Represents one basis function and its gradient.
Definition Basis.hpp:44
const std::vector< Local2Global > & global() const
Definition Basis.hpp:104
void compute_face_jacobian(const int el_id, const Eigen::MatrixXd &reference_map, Eigen::MatrixXd &jacobian) const
Definition Mesh2D.cpp:101
void compute_cell_jacobian(const int el_id, const Eigen::MatrixXd &reference_map, Eigen::MatrixXd &jacobian) const
Definition Mesh3D.cpp:424
double value_unweighted(const Eigen::VectorXd &x) const override
Compute the value of the form.
virtual std::string name() const override
Definition AMIPSForm.hpp:46
bool is_step_valid(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) const override
Determine if a step from solution x0 to solution x1 is allowed.
Eigen::VectorXd get_updated_mesh_nodes(const Eigen::VectorXd &x) const
Definition AMIPSForm.hpp:53
std::vector< polyfem::basis::ElementBases > init_geom_bases_
Definition AMIPSForm.hpp:64
void compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
std::shared_ptr< assembler::Assembler > amips_energy_
Definition AMIPSForm.hpp:66
assembler::AssemblyValsCache init_ass_vals_cache_
Definition AMIPSForm.hpp:65
AMIPSForm(const VariableToSimulationGroup &variable_to_simulation, const State &state)
const VariableToSimulationGroup variable_to_simulations_
virtual double weight() const
Get the form's multiplicative constant weight.
Definition Form.hpp:128
virtual std::string name() const override
Definition AMIPSForm.hpp:32
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.
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.
int norm
Definition p_bases.py:238
bool scaled_jacobian(Mesh3DStorage &hmi, Mesh_Quality &mq)
double tet_determinant(const Eigen::VectorXd &v1, const Eigen::VectorXd &v2, const Eigen::VectorXd &v3, const Eigen::VectorXd &v4)
Eigen::VectorXd map_primitive_to_node_order(const State &state, const Eigen::VectorXd &primitives)
double triangle_jacobian(const Eigen::VectorXd &v1, const Eigen::VectorXd &v2, const Eigen::VectorXd &v3)
bool is_flipped(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
Eigen::VectorXd map_node_to_primitive_order(const State &state, const Eigen::VectorXd &nodes)
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.
spdlog::logger & adjoint_logger()
Retrieves the current logger for adjoint.
Definition Logger.cpp:28
nlohmann::json json
Definition Common.hpp:9
void log_and_throw_adjoint_error(const std::string &msg)
Definition Logger.cpp:77