PolyFEM
Loading...
Searching...
No Matches
StateRemesh.cpp
Go to the documentation of this file.
1#include <polyfem/State.hpp>
2
10
11#include <igl/edges.h>
12
13namespace polyfem
14{
15 using namespace mesh;
16
17 namespace
18 {
23 Remesher::BoundaryMap<int> build_boundary_to_id(
24 const std::unique_ptr<mesh::Mesh> &mesh,
25 const Eigen::VectorXi &in_node_to_node)
26 {
27 if (mesh->dimension() == 2)
28 {
29 Remesher::EdgeMap<int> edge_to_boundary_id;
30 for (int i = 0; i < mesh->n_edges(); i++)
31 {
32 const size_t e0 = in_node_to_node[mesh->edge_vertex(i, 0)];
33 const size_t e1 = in_node_to_node[mesh->edge_vertex(i, 1)];
34 edge_to_boundary_id[{{e0, e1}}] = mesh->get_boundary_id(i);
35 }
36 return edge_to_boundary_id;
37 }
38 else
39 {
40 assert(mesh->dimension() == 3);
41 Remesher::FaceMap<int> face_to_boundary_id;
42 for (int i = 0; i < mesh->n_faces(); i++)
43 {
44 std::array<size_t, 3> f =
45 {{(size_t)in_node_to_node[mesh->face_vertex(i, 0)],
46 (size_t)in_node_to_node[mesh->face_vertex(i, 1)],
47 (size_t)in_node_to_node[mesh->face_vertex(i, 2)]}};
48 face_to_boundary_id[f] = mesh->get_boundary_id(i);
49
50#ifndef NDEBUG
51 if (mesh->is_boundary_face(i))
52 assert(face_to_boundary_id[f] >= 0);
53 else
54 assert(face_to_boundary_id[f] == -1);
55#endif
56 }
57 return face_to_boundary_id;
58 }
59 }
60
68 void build_edge_energy_maps(
69 const State &state,
70 const Eigen::MatrixXd &vertices,
71 const Eigen::MatrixXi &elements,
72 const Eigen::MatrixXd &sol,
73 Remesher::EdgeMap<double> &elastic_energy,
74 Remesher::EdgeMap<double> &contact_energy)
75 {
76 Eigen::MatrixXi edges;
77 igl::edges(elements, edges);
78 Remesher::EdgeMap<std::vector<double>> elastic_multienergy;
79 for (const auto &edge : edges.rowwise())
80 {
81 elastic_multienergy[{{(size_t)edge(0), (size_t)edge(1)}}] = std::vector<double>();
82 }
83
84 assert(state.solve_data.elastic_form != nullptr);
85 const Eigen::VectorXd elastic_energy_per_element = state.solve_data.elastic_form->value_per_element(sol);
86 for (int i = 0; i < elements.rows(); ++i)
87 {
88 assert(elements.cols() == 3 || elements.cols() == 4);
89 const auto &element = elements.row(i);
90 const double energy = elastic_energy_per_element[i];
91
92 elastic_multienergy[{{(size_t)element(0), (size_t)element(1)}}].push_back(energy);
93 elastic_multienergy[{{(size_t)element(0), (size_t)element(2)}}].push_back(energy);
94 elastic_multienergy[{{(size_t)element(1), (size_t)element(2)}}].push_back(energy);
95 if (elements.cols() == 4)
96 {
97 elastic_multienergy[{{(size_t)element(0), (size_t)element(3)}}].push_back(energy);
98 elastic_multienergy[{{(size_t)element(1), (size_t)element(3)}}].push_back(energy);
99 elastic_multienergy[{{(size_t)element(2), (size_t)element(3)}}].push_back(energy);
100 }
101 }
102
103 // Average the element energies
104 for (const auto &[edge, energies] : elastic_multienergy)
105 {
106 elastic_energy[edge] =
107 std::reduce(energies.begin(), energies.end()) / energies.size();
108 }
109
110 if (state.solve_data.contact_form != nullptr)
111 {
112 const Eigen::VectorXd contact_energy_per_vertex = state.solve_data.contact_form->value_per_element(sol);
113 for (int i = 0; i < edges.rows(); ++i)
114 {
115 contact_energy[{{(size_t)edges(i, 0), (size_t)edges(i, 1)}}] =
116 (contact_energy_per_vertex[edges(i, 0)] + contact_energy_per_vertex[edges(i, 1)]) / 2.0;
117 }
118 }
119 }
120
121 std::shared_ptr<Remesher> create_wild_remeshing(
122 State &state,
123 const Eigen::VectorXd &obstacle_sol,
124 const Eigen::MatrixXd &obstacle_projection_quantities,
125 const double time,
126 const double current_energy)
127 {
128 const int dim = state.mesh->dimension();
129
130 const std::string type = state.args["space"]["remesh"]["type"];
131 std::shared_ptr<Remesher> remeshing = nullptr;
132 if (type == "physics")
133 {
134 if (dim == 2)
135 remeshing = std::make_shared<PhysicsTriRemesher>(
136 state, utils::unflatten(obstacle_sol, dim), obstacle_projection_quantities,
137 time, current_energy);
138 else
139 remeshing = std::make_shared<PhysicsTetRemesher>(
140 state, utils::unflatten(obstacle_sol, dim), obstacle_projection_quantities,
141 time, current_energy);
142 }
143 else if (type == "sizing_field")
144 {
145 if (dim == 2)
146 remeshing = std::make_shared<SizingFieldTriRemesher>(
147 state, utils::unflatten(obstacle_sol, dim), obstacle_projection_quantities,
148 time, current_energy);
149 else
150 remeshing = std::make_shared<SizingFieldTetRemesher>(
151 state, utils::unflatten(obstacle_sol, dim), obstacle_projection_quantities,
152 time, current_energy);
153 }
154 assert(remeshing != nullptr);
155 return remeshing;
156 }
157 } // namespace
158
159 bool State::remesh(const double time, const double dt, Eigen::MatrixXd &sol)
160 {
161 const int dim = mesh->dimension();
162 int ndof = sol.size();
163 assert(sol.cols() == 1);
164 int ndof_mesh = mesh->n_vertices() * dim;
165 int ndof_obstacle = obstacle.n_vertices() * dim;
166 assert(ndof == ndof_mesh + ndof_obstacle);
167
168 Eigen::MatrixXd rest_positions;
169 Eigen::MatrixXi elements;
170 build_mesh_matrices(rest_positions, elements);
171 assert(rest_positions.size() == ndof_mesh);
172
173 // --------------------------------------------------------------------
174
175 Remesher::EdgeMap<double> elastic_energy, contact_energy;
176 build_edge_energy_maps(
177 *this, rest_positions, elements, sol, elastic_energy, contact_energy);
178
179 // --------------------------------------------------------------------
180
181 const Remesher::BoundaryMap<int> boundary_to_id = build_boundary_to_id(mesh, in_node_to_node);
182
183 const std::vector<int> body_ids = mesh->has_body_ids() ? mesh->get_body_ids() : std::vector<int>(elements.rows(), 0);
184 assert(body_ids.size() == elements.rows());
185
186 // Only remesh the FE mesh
187 assert(sol.size() - rest_positions.size() == obstacle.n_vertices() * dim);
188 const Eigen::MatrixXd mesh_sol = sol.topRows(ndof_mesh);
189 const Eigen::MatrixXd obstacle_sol = sol.bottomRows(ndof_obstacle);
190 const Eigen::MatrixXd positions = rest_positions + utils::unflatten(mesh_sol, dim);
191
192 // not including current displacement as this will be handled as positions
193 Eigen::MatrixXd projection_quantities = Remesher::combine_time_integrator_quantities(
195 assert(projection_quantities.rows() == ndof);
196
197 const Eigen::MatrixXd obstacle_projection_quantities = projection_quantities.bottomRows(ndof_obstacle);
198 projection_quantities.conservativeResize(ndof_mesh, Eigen::NoChange);
199
200 // --------------------------------------------------------------------
201 // remesh
202
203 std::shared_ptr<Remesher> remeshing = create_wild_remeshing(
204 *this, obstacle_sol, obstacle_projection_quantities, time, solve_data.nl_problem->value(sol));
205 remeshing->init(
206 rest_positions, positions, elements, projection_quantities, boundary_to_id, body_ids,
207 elastic_energy, contact_energy);
208
209 const bool made_change = remeshing->execute();
210
211 if (!made_change)
212 return false;
213
214 // --------------------------------------------------------------------
215 // create new mesh
216
217 mesh = mesh::Mesh::create(remeshing->rest_positions(), remeshing->elements(), /*non_conforming=*/false);
218
219 // set body ids
220 mesh->set_body_ids(remeshing->body_ids());
221
222 // set boundary ids
223 std::vector<int> boundary_ids;
224 if (dim == 2)
225 {
226 const auto remesh_boundary_ids = std::get<Remesher::EdgeMap<int>>(remeshing->boundary_ids());
227 boundary_ids = std::vector<int>(mesh->n_edges(), -1);
228 for (int i = 0; i < mesh->n_edges(); i++)
229 {
230 const size_t e0 = mesh->edge_vertex(i, 0);
231 const size_t e1 = mesh->edge_vertex(i, 1);
232 boundary_ids[i] = remesh_boundary_ids.at({{e0, e1}});
233 }
234 }
235 else
236 {
237 const auto remesh_boundary_ids = std::get<Remesher::FaceMap<int>>(remeshing->boundary_ids());
238 boundary_ids = std::vector<int>(mesh->n_faces(), -1);
239 for (int i = 0; i < mesh->n_faces(); i++)
240 {
241 const std::array<size_t, 3> f = {{(size_t)mesh->face_vertex(i, 0),
242 (size_t)mesh->face_vertex(i, 1),
243 (size_t)mesh->face_vertex(i, 2)}};
244 boundary_ids[i] = remesh_boundary_ids.at(f);
245 }
246 }
247 mesh->set_boundary_ids(boundary_ids);
248
249 // load mesh (and set materials) (will also reload obstacles from disk)
250 load_mesh();
251
252 // --------------------------------------------------------------------
253
254 build_basis();
255 assemble_rhs();
257
258 // --------------------------------------------------------------------
259
260 const int old_ndof = ndof;
261 const int old_ndof_mesh = ndof_mesh;
262 const int old_ndof_obstacle = ndof_obstacle;
263
264 ndof_mesh = mesh->n_vertices() * dim;
265 ndof_obstacle = obstacle.n_vertices() * dim;
266 assert(ndof_obstacle == old_ndof_obstacle);
267 ndof = n_bases * dim;
268 assert(ndof == ndof_mesh + ndof_obstacle);
269
270 sol.resize(ndof, 1);
271 sol.topRows(ndof_mesh) = utils::flatten(utils::reorder_matrix(remeshing->displacements(), in_node_to_node));
272 if (ndof_obstacle > 0)
273 sol.bottomRows(ndof_obstacle) = obstacle_sol;
274
276 if (problem->is_time_dependent())
277 {
278 assert(solve_data.time_integrator != nullptr);
279
280 Eigen::MatrixXd projected_quantities = remeshing->projection_quantities();
281 assert(projected_quantities.rows() == ndof_mesh);
282 assert(projected_quantities.cols() == projection_quantities.cols());
283 projected_quantities = utils::reorder_matrix(
284 projected_quantities, in_node_to_node, /*out_blocks=*/-1, dim);
285 projected_quantities.conservativeResize(ndof, Eigen::NoChange);
286 projected_quantities.bottomRows(ndof_obstacle) = obstacle_projection_quantities;
287
288 Eigen::MatrixXd x_prevs, v_prevs, a_prevs;
290 projected_quantities, dim, x_prevs, v_prevs, a_prevs);
291 solve_data.time_integrator->init(x_prevs, v_prevs, a_prevs, dt);
292 }
293 init_nonlinear_tensor_solve(sol, time, /*init_time_integrator=*/false);
294
295 // initialize the problem so contact force show up correctly in the output
296 solve_data.nl_problem->update_quantities(time, solve_data.time_integrator->x_prev());
297 solve_data.nl_problem->init(sol);
298 solve_data.nl_problem->init_lagging(solve_data.time_integrator->x_prev());
299 solve_data.nl_problem->update_lagging(sol, /*iter_num=*/0);
300
301 return true;
302 }
303} // namespace polyfem
int n_bases
number of bases
Definition State.hpp:178
void assemble_mass_mat()
assemble mass, step 4 of solve build mass matrix based on defined basis modifies mass (and maybe more...
Definition State.cpp:1487
Eigen::VectorXi in_node_to_node
Inpute nodes (including high-order) to polyfem nodes, only for isoparametric.
Definition State.hpp:450
mesh::Obstacle obstacle
Obstacles used in collisions.
Definition State.hpp:468
void load_mesh(bool non_conforming=false, const std::vector< std::string > &names=std::vector< std::string >(), const std::vector< Eigen::MatrixXi > &cells=std::vector< Eigen::MatrixXi >(), const std::vector< Eigen::MatrixXd > &vertices=std::vector< Eigen::MatrixXd >())
loads the mesh from the json arguments
Definition StateLoad.cpp:91
bool remesh(const double time, const double dt, Eigen::MatrixXd &sol)
Remesh the FE space and update solution(s).
std::unique_ptr< mesh::Mesh > mesh
current mesh, it can be a Mesh2D or Mesh3D
Definition State.hpp:466
void build_basis()
builds the bases step 2 of solve modifies bases, pressure_bases, geom_bases_, boundary_nodes,...
Definition State.cpp:518
std::shared_ptr< assembler::Problem > problem
current problem, it contains rhs and bc
Definition State.hpp:168
void assemble_rhs()
compute rhs, step 3 of solve build rhs vector based on defined basis and given rhs of the problem mod...
Definition State.cpp:1611
std::shared_ptr< assembler::RhsAssembler > build_rhs_assembler() const
build a RhsAssembler for the problem
Definition State.hpp:248
int ndof() const
Definition State.hpp:653
solver::SolveData solve_data
timedependent stuff cached
Definition State.hpp:324
void init_nonlinear_tensor_solve(Eigen::MatrixXd &sol, const double t=1.0, const bool init_time_integrator=true)
initialize the nonlinear solver
void build_mesh_matrices(Eigen::MatrixXd &V, Eigen::MatrixXi &F)
Build the mesh matrices (vertices and elements) from the mesh using the bases node ordering.
static std::unique_ptr< Mesh > create(const std::string &path, const bool non_conforming=false)
factory to build the proper mesh
Definition Mesh.cpp:173
std::unordered_map< std::array< size_t, 2 >, T, polyfem::utils::HashUnorderedArray< size_t, 2 >, polyfem::utils::EqualUnorderedArray< size_t, 2 > > EdgeMap
Map from a (sorted) edge to an integer (ID)
Definition Remesher.hpp:29
std::variant< EdgeMap< T >, FaceMap< T > > BoundaryMap
Definition Remesher.hpp:42
static Eigen::MatrixXd combine_time_integrator_quantities(const std::shared_ptr< time_integrator::ImplicitTimeIntegrator > &time_integrator)
Combine the quantities of a time integrator into a single matrix (one column per quantity)
Definition Remesher.cpp:375
static void split_time_integrator_quantities(const Eigen::MatrixXd &quantities, const int dim, Eigen::MatrixXd &x_prevs, Eigen::MatrixXd &v_prevs, Eigen::MatrixXd &a_prevs)
Split the quantities of a time integrator into separate vectors.
Definition Remesher.cpp:396
std::shared_ptr< solver::NLProblem > nl_problem
std::shared_ptr< time_integrator::ImplicitTimeIntegrator > time_integrator
std::shared_ptr< assembler::RhsAssembler > rhs_assembler
Eigen::MatrixXd reorder_matrix(const Eigen::MatrixXd &in, const Eigen::VectorXi &in_to_out, int out_blocks=-1, const int block_size=1)
Reorder row blocks in a matrix.
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.