PolyFEM
Loading...
Searching...
No Matches
AMIPSForm.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <polyfem/State.hpp>
9
12
13namespace polyfem::solver
14{
15 namespace
16 {
17 double triangle_jacobian(const Eigen::VectorXd &v1, const Eigen::VectorXd &v2, const Eigen::VectorXd &v3)
18 {
19 Eigen::VectorXd a = v2 - v1, b = v3 - v1;
20 return a(0) * b(1) - b(0) * a(1);
21 }
22
23 double tet_determinant(const Eigen::VectorXd &v1, const Eigen::VectorXd &v2, const Eigen::VectorXd &v3, const Eigen::VectorXd &v4)
24 {
25 Eigen::Matrix3d mat;
26 mat.col(0) << v2 - v1;
27 mat.col(1) << v3 - v1;
28 mat.col(2) << v4 - v1;
29 return mat.determinant();
30 }
31
32 void scaled_jacobian(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F, Eigen::VectorXd &quality)
33 {
34 const int dim = F.cols() - 1;
35
36 quality.setZero(F.rows());
37 if (dim == 2)
38 {
39 for (int i = 0; i < F.rows(); i++)
40 {
41 Eigen::RowVector3d e0;
42 e0(2) = 0;
43 e0.head(2) = V.row(F(i, 2)) - V.row(F(i, 1));
44 Eigen::RowVector3d e1;
45 e1(2) = 0;
46 e1.head(2) = V.row(F(i, 0)) - V.row(F(i, 2));
47 Eigen::RowVector3d e2;
48 e2(2) = 0;
49 e2.head(2) = V.row(F(i, 1)) - V.row(F(i, 0));
50
51 double l0 = e0.norm();
52 double l1 = e1.norm();
53 double l2 = e2.norm();
54
55 double A = 0.5 * (e0.cross(e1)).norm();
56 double Lmax = std::max(l0 * l1, std::max(l1 * l2, l0 * l2));
57
58 quality(i) = 2 * A * (2 / sqrt(3)) / Lmax;
59 }
60 }
61 else
62 {
63 for (int i = 0; i < F.rows(); i++)
64 {
65 Eigen::RowVector3d e0 = V.row(F(i, 1)) - V.row(F(i, 0));
66 Eigen::RowVector3d e1 = V.row(F(i, 2)) - V.row(F(i, 1));
67 Eigen::RowVector3d e2 = V.row(F(i, 0)) - V.row(F(i, 2));
68 Eigen::RowVector3d e3 = V.row(F(i, 3)) - V.row(F(i, 0));
69 Eigen::RowVector3d e4 = V.row(F(i, 3)) - V.row(F(i, 1));
70 Eigen::RowVector3d e5 = V.row(F(i, 3)) - V.row(F(i, 2));
71
72 double l0 = e0.norm();
73 double l1 = e1.norm();
74 double l2 = e2.norm();
75 double l3 = e3.norm();
76 double l4 = e4.norm();
77 double l5 = e5.norm();
78
79 double J = std::abs((e0.cross(e3)).dot(e2));
80
81 double a1 = l0 * l2 * l3;
82 double a2 = l0 * l1 * l4;
83 double a3 = l1 * l2 * l5;
84 double a4 = l3 * l4 * l5;
85
86 double a = std::max({a1, a2, a3, a4, J});
87 quality(i) = J * sqrt(2) / a;
88 }
89 }
90 }
91
92 bool is_flipped(const Eigen::MatrixXd &V, const Eigen::MatrixXi &F)
93 {
94 if (F.cols() == 3)
95 {
96 for (int i = 0; i < F.rows(); i++)
97 if (triangle_jacobian(V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2))) <= 0)
98 return true;
99 }
100 else if (F.cols() == 4)
101 {
102 for (int i = 0; i < F.rows(); i++)
103 if (tet_determinant(V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2)), V.row(F(i, 3))) <= 0)
104 return true;
105 }
106 else
107 {
108 return true;
109 }
110
111 return false;
112 }
113 } // namespace
114
115 class AMIPSForm : public AdjointForm
116 {
117 public:
118 AMIPSForm(const VariableToSimulationGroup& variable_to_simulation, const State &state)
119 : AdjointForm(variable_to_simulation),
120 state_(state)
121 {
123 amips_energy_->set_size(state.mesh->dimension());
124
125 json transform_params = {};
126 transform_params["canonical_transformation"] = json::array();
127 if (!state.mesh->is_volume())
128 {
129 Eigen::MatrixXd regular_tri(3, 3);
130 regular_tri << 0, 0, 1,
131 1, 0, 1,
132 1. / 2., std::sqrt(3) / 2., 1;
133 regular_tri.transposeInPlace();
134 Eigen::MatrixXd regular_tri_inv = regular_tri.inverse();
135
136 const auto &mesh2d = *dynamic_cast<mesh::Mesh2D *>(state.mesh.get());
137 for (int e = 0; e < state.mesh->n_elements(); e++)
138 {
139 Eigen::MatrixXd transform;
140 mesh2d.compute_face_jacobian(e, regular_tri_inv, transform);
141 transform_params["canonical_transformation"].push_back(json({
142 {
143 transform(0, 0),
144 transform(0, 1),
145 },
146 {
147 transform(1, 0),
148 transform(1, 1),
149 },
150 }));
151 }
152 }
153 else
154 {
155 Eigen::MatrixXd regular_tet(4, 4);
156 regular_tet << 0, 0, 0, 1,
157 1, 0, 0, 1,
158 1. / 2., std::sqrt(3) / 2., 0, 1,
159 1. / 2., 1. / 2. / std::sqrt(3), std::sqrt(3) / 2., 1;
160 regular_tet.transposeInPlace();
161 Eigen::MatrixXd regular_tet_inv = regular_tet.inverse();
162
163 const auto &mesh3d = *dynamic_cast<mesh::Mesh3D *>(state.mesh.get());
164 for (int e = 0; e < state.mesh->n_elements(); e++)
165 {
166 Eigen::MatrixXd transform;
167 mesh3d.compute_cell_jacobian(e, regular_tet_inv, transform);
168 transform_params["canonical_transformation"].push_back(json({
169 {
170 transform(0, 0),
171 transform(0, 1),
172 transform(0, 2),
173 },
174 {
175 transform(1, 0),
176 transform(1, 1),
177 transform(1, 2),
178 },
179 {
180 transform(2, 0),
181 transform(2, 1),
182 transform(2, 2),
183 },
184 }));
185 }
186 }
187 transform_params["solve_displacement"] = true;
188 amips_energy_->add_multimaterial(0, transform_params, state.units);
189
190 Eigen::MatrixXd V;
195 }
196
197 virtual std::string name() const override { return "AMIPS"; }
198
199 double value_unweighted(const Eigen::VectorXd &x) const override
200 {
201 Eigen::VectorXd X = get_updated_mesh_nodes(x);
202
203 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_init), Eigen::VectorXd());
204 }
205
206 void compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
207 {
209 const Eigen::VectorXd X = get_updated_mesh_nodes(x);
210 Eigen::MatrixXd grad;
211 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_init), Eigen::VectorXd(), grad); // grad wrt. gbases
213 });
214 }
215
216 bool is_step_valid(const Eigen::VectorXd &x0, const Eigen::VectorXd &x1) const override
217 {
218 Eigen::VectorXd X = get_updated_mesh_nodes(x1);
219 Eigen::MatrixXd V1 = utils::unflatten(X, state_.mesh->dimension());
220 bool flipped = is_flipped(V1, F);
221
222 if (flipped)
223 adjoint_logger().trace("[{}] Step flips elements.", name());
224
225 return !flipped;
226 }
227
228 private:
229 Eigen::VectorXd get_updated_mesh_nodes(const Eigen::VectorXd &x) const
230 {
231 Eigen::VectorXd X = X_init;
233 return X;
234 }
235
236 const State &state_;
237
238 Eigen::VectorXd X_init;
239 Eigen::MatrixXi F;
240 std::vector<polyfem::basis::ElementBases> init_geom_bases_;
242
243 std::shared_ptr<assembler::Assembler> amips_energy_;
244 };
245} // namespace polyfem::solver
int V
int x
Eigen::MatrixXd mat
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)
Caches basis evaluation and geometric mapping at every element.
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
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
std::vector< polyfem::basis::ElementBases > init_geom_bases_
void compute_partial_gradient(const Eigen::VectorXd &x, Eigen::VectorXd &gradv) const override
std::shared_ptr< assembler::Assembler > amips_energy_
assembler::AssemblyValsCache init_ass_vals_cache_
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:126
A collection of VariableToSimulation.
void compute_state_variable(const ParameterType type, const State *state_ptr, const Eigen::VectorXd &x, Eigen::VectorXd &state_variable) const
Evaluate the variable to simulations and overwrite the state_variable based on x.
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)
Eigen::VectorXd map_primitive_to_node_order(const State &state, const Eigen::VectorXd &primitives)
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