7#include <wmtk/utils/TupleUtils.hpp>
9#include <unordered_map>
10#include <unordered_set>
12#define VERTEX_ATTRIBUTE_GETTER(name, attribute) \
13 template <class WMTKMesh> \
14 Eigen::MatrixXd WildRemesher<WMTKMesh>::name() const \
16 Eigen::MatrixXd attributes = Eigen::MatrixXd::Constant(WMTKMesh::vert_capacity(), DIM, NaN); \
17 for (const Tuple &t : WMTKMesh::get_vertices()) \
18 attributes.row(t.vid(*this)) = vertex_attrs[t.vid(*this)].attribute; \
22#define VERTEX_ATTRIBUTE_SETTER(name, attribute) \
23 template <class WMTKMesh> \
24 void WildRemesher<WMTKMesh>::name(const Eigen::MatrixXd &attributes) \
26 for (const Tuple &t : WMTKMesh::get_vertices()) \
27 vertex_attrs[t.vid(*this)].attribute = attributes.row(t.vid(*this)); \
32 static constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
34 template <
class WMTKMesh>
37 const Eigen::MatrixXd &obstacle_displacements,
38 const Eigen::MatrixXd &obstacle_vals,
39 const double current_time,
40 const double starting_energy)
41 :
Remesher(state, obstacle_displacements, obstacle_vals, current_time, starting_energy),
46 template <
typename WMTKMesh>
50 if (edge_energy.empty())
53 double min_energy = std::numeric_limits<double>::infinity();
54 double max_energy = -std::numeric_limits<double>::infinity();
55 std::vector<double> sorted_energies;
56 sorted_energies.reserve(edge_energy.size());
57 for (
const auto &[edge, energy] : edge_energy)
59 min_energy = std::min(min_energy, energy);
60 max_energy = std::max(max_energy, energy);
61 sorted_energies.push_back(energy);
63 std::sort(sorted_energies.begin(), sorted_energies.end());
65 const double split_threshold = args[
"split"][
"culling_threshold"];
66 const double split_tolerance = args[
"split"][
"acceptance_tolerance"];
67 const double top_energy_threshold = (max_energy - min_energy) * split_threshold + min_energy;
68 const double top_element_threshold = sorted_energies[int(sorted_energies.size() * split_threshold)];
69 const double top_threshold = std::max(std::min(top_energy_threshold, top_element_threshold), std::min(1e-12, split_tolerance));
71 const double collapse_threshold = args[
"collapse"][
"culling_threshold"];
72 const double bottom_energy_threshold = (max_energy - min_energy) * collapse_threshold + min_energy;
74 const double bottom_threshold = bottom_energy_threshold;
76 logger().info(
"min energy: {}, max energy: {}, thresholds: {}, {}", min_energy, max_energy, bottom_threshold, top_threshold);
79 for (
const auto &[edge, energy] : edge_energy)
81 if (energy >= top_threshold)
83 else if (energy <= bottom_threshold)
92 template <
class WMTKMesh>
94 const Eigen::MatrixXd &rest_positions,
95 const Eigen::MatrixXd &positions,
96 const Eigen::MatrixXi &elements,
97 const Eigen::MatrixXd &projection_quantities,
99 const std::vector<int> &body_ids,
104 rest_positions, positions, elements, projection_quantities,
105 boundary_to_id, body_ids, elastic_energy, contact_energy);
108 for (
const Tuple &t : get_elements())
109 total_volume += element_volume(t);
110 assert(total_volume > 0);
113 assert(get_elements().size() == elements.rows());
114 for (
const Tuple &t : get_elements())
115 assert(!is_inverted(t));
118 const auto edge_elastic_ranks = rank_edges<WMTKMesh>(elastic_energy, args);
119 const auto edge_contact_ranks = rank_edges<WMTKMesh>(contact_energy, args);
122 for (
const auto &[edge, elastic_rank] : edge_elastic_ranks)
124 const auto contact_rank = edge_contact_ranks.empty() ? elastic_rank : edge_contact_ranks.at(edge);
125 if (elastic_rank == EdgeAttributes::EnergyRank::TOP || contact_rank == EdgeAttributes::EnergyRank::TOP)
126 edge_ranks[edge] = EdgeAttributes::EnergyRank::TOP;
127 else if (elastic_rank == EdgeAttributes::EnergyRank::BOTTOM && contact_rank == EdgeAttributes::EnergyRank::BOTTOM)
128 edge_ranks[edge] = EdgeAttributes::EnergyRank::BOTTOM;
130 edge_ranks[edge] = EdgeAttributes::EnergyRank::MIDDLE;
133 for (
const Tuple &edge : WMTKMesh::get_edges())
135 const size_t e0 = edge.vid(*
this);
136 const size_t e1 = edge.switch_vertex(*this).vid(*
this);
137 edge_attr(edge.eid(*
this)).energy_rank = edge_ranks.at({{e0, e1}});
150 template <class WMTKMesh>
153 const std::vector<Tuple> edges = WMTKMesh::get_edges();
154 Eigen::MatrixXi E = Eigen::MatrixXi::Constant(edges.size(), 2, -1);
155 for (
int i = 0; i < edges.size(); ++i)
157 const Tuple &e = edges[i];
158 E(i, 0) = e.vid(*
this);
159 E(i, 1) = e.switch_vertex(*this).vid(*
this);
164 template <
class WMTKMesh>
167 const std::vector<Tuple> elements = get_elements();
168 Eigen::MatrixXi
F = Eigen::MatrixXi::Constant(elements.size(), dim() + 1, -1);
169 for (
size_t i = 0; i < elements.size(); i++)
171 const auto vids = element_vids(elements[i]);
173 for (
int j = 0; j < vids.size(); j++)
181 template <
class WMTKMesh>
184 Eigen::MatrixXd projection_quantities =
185 Eigen::MatrixXd::Constant(dim() * WMTKMesh::vert_capacity(), n_quantities(), NaN);
187 for (
const Tuple &t : WMTKMesh::get_vertices())
189 const int vi = t.vid(*
this);
190 projection_quantities.middleRows(dim() * vi, dim()) = vertex_attrs[vi].projection_quantities;
193 return projection_quantities;
196 template <
class WMTKMesh>
199 const std::vector<Tuple> elements = get_elements();
200 std::vector<int> body_ids(elements.size(), -1);
201 for (
size_t i = 0; i < elements.size(); i++)
203 body_ids[i] = element_attrs[element_id(elements[i])].body_id;
208 template <
class WMTKMesh>
210 const Eigen::VectorXi &vertex_to_basis)
const
212 std::vector<int> boundary_nodes;
214 const std::unordered_map<int, std::array<bool, 3>> bcs =
215 state.boundary_conditions_ids(
"dirichlet_boundary");
216 std::vector<int> boundary_ids;
217 const std::vector<Tuple> boundary_facets = this->boundary_facets(&boundary_ids);
218 for (
int i = 0; i < boundary_facets.size(); ++i)
220 const Tuple &t = boundary_facets[i];
221 const auto bc = bcs.find(boundary_ids[i]);
226 for (
int d = 0; d < this->dim(); ++d)
231 for (
const size_t vid : facet_vids(t))
232 boundary_nodes.push_back(dim() * vertex_to_basis[vid] + d);
237 std::sort(boundary_nodes.begin(), boundary_nodes.end());
238 auto new_end = std::unique(boundary_nodes.begin(), boundary_nodes.end());
239 boundary_nodes.erase(new_end, boundary_nodes.end());
241 return boundary_nodes;
244 template <
class WMTKMesh>
245 std::vector<typename WMTKMesh::Tuple>
250 size_t element_capacity;
251 if constexpr (std::is_same_v<wmtk::TriMesh, WMTKMesh>)
252 element_capacity = WMTKMesh::tri_capacity();
254 element_capacity = WMTKMesh::tet_capacity();
256 const size_t max_tid = std::min(boundary_attrs.size() / FACETS_PER_ELEMENT, element_capacity);
258 std::vector<Tuple> boundary_facets;
259 for (
int tid = 0; tid < max_tid; ++tid)
261 const Tuple t = tuple_from_element(tid);
262 if (!t.is_valid(*
this))
265 for (
int local_fid = 0; local_fid < FACETS_PER_ELEMENT; ++local_fid)
267 const Tuple facet_tuple = tuple_from_facet(tid, local_fid);
268 const int boundary_id = boundary_attrs[facet_id(facet_tuple)].boundary_id;
269 assert((boundary_id >= 0) == is_boundary_facet(facet_tuple));
270 if (boundary_id >= 0)
272 boundary_facets.push_back(facet_tuple);
274 boundary_ids->push_back(boundary_id);
281 std::vector<size_t> boundary_facet_ids;
282 for (
const Tuple &f : boundary_facets)
283 boundary_facet_ids.push_back(facet_id(f));
284 std::sort(boundary_facet_ids.begin(), boundary_facet_ids.end());
286 std::vector<size_t> gt_boundary_facet_ids;
287 for (
const Tuple &f : get_facets())
288 if (is_boundary_facet(f))
289 gt_boundary_facet_ids.push_back(facet_id(f));
290 std::sort(gt_boundary_facet_ids.begin(), gt_boundary_facet_ids.end());
292 assert(boundary_facets.size() == gt_boundary_facet_ids.size());
293 assert(boundary_facet_ids == gt_boundary_facet_ids);
297 return boundary_facets;
306 template <
class WMTKMesh>
308 const Eigen::MatrixXd &projection_quantities)
310 assert(projection_quantities.rows() == dim() * WMTKMesh::vert_capacity());
311 m_n_quantities = projection_quantities.cols();
313 for (
const Tuple &t : WMTKMesh::get_vertices())
315 const int vi = t.vid(*
this);
316 vertex_attrs[vi].projection_quantities =
317 projection_quantities.middleRows(dim() * vi, dim());
321 template <
class WMTKMesh>
323 const std::vector<bool> &fixed)
325 assert(fixed.size() == WMTKMesh::vert_capacity());
326 for (
const Tuple &t : WMTKMesh::get_vertices())
327 vertex_attrs[t.vid(*
this)].fixed = fixed[t.vid(*
this)];
330 template <
class WMTKMesh>
332 const std::vector<int> &body_ids)
334 const std::vector<Tuple> elements = get_elements();
335 for (
int i = 0; i < elements.size(); ++i)
337 element_attrs[element_id(elements[i])].body_id = body_ids.at(i);
341 template <
class WMTKMesh>
344 for (
const Tuple &face : get_facets())
346 if constexpr (std::is_same_v<wmtk::TriMesh, WMTKMesh>)
348 assert(std::holds_alternative<
EdgeMap<int>>(boundary_to_id));
349 const EdgeMap<int> &edge_to_boundary_id = std::get<EdgeMap<int>>(boundary_to_id);
350 boundary_attrs[facet_id(face)].boundary_id = edge_to_boundary_id.at(facet_vids(face));
354 assert(std::holds_alternative<
FaceMap<int>>(boundary_to_id));
355 const FaceMap<int> &face_to_boundary_id = std::get<FaceMap<int>>(boundary_to_id);
356 boundary_attrs[facet_id(face)].boundary_id = face_to_boundary_id.at(facet_vids(face));
360 if (is_boundary_facet(face))
361 assert(boundary_attrs[facet_id(face)].boundary_id >= 0);
363 assert(boundary_attrs[facet_id(face)].boundary_id == -1);
370 template <
class WMTKMesh>
375 for (
auto &t : get_elements())
379 static int inversion_cnt = 0;
380 write_mesh(state.resolve_output_path(fmt::format(
"inversion_{:04d}.vtu", inversion_cnt++)));
391 template <
class WMTKMesh>
394 const auto &e0 = vertex_attrs[e.vid(*
this)].rest_position;
395 const auto &e1 = vertex_attrs[e.switch_vertex(*this).vid(*
this)].rest_position;
396 return (e1 - e0).norm();
399 template <
class WMTKMesh>
402 const auto &e0 = vertex_attrs[e.vid(*
this)].position;
403 const auto &e1 = vertex_attrs[e.switch_vertex(*this).vid(*
this)].position;
404 return (e1 - e0).norm();
407 template <
class WMTKMesh>
411 const VectorNd &e0 = vertex_attrs[e.vid(*
this)].rest_position;
412 const VectorNd &e1 = vertex_attrs[e.switch_vertex(*this).vid(*
this)].rest_position;
413 return (e1 + e0) / 2.0;
416 template <
class WMTKMesh>
420 const VectorNd &e0 = vertex_attrs[e.vid(*
this)].position;
421 const VectorNd &e1 = vertex_attrs[e.switch_vertex(*this).vid(*
this)].position;
422 return (e1 + e0) / 2.0;
425 template <
class WMTKMesh>
427 const std::vector<Tuple> &elements)
const
429 std::vector<Tuple> edges;
430 for (
auto t : elements)
431 for (
int j = 0; j < EDGES_PER_ELEMENT; ++j)
432 edges.push_back(WMTKMesh::tuple_from_edge(element_id(t), j));
433 wmtk::unique_edge_tuples(*
this, edges);
437 template <
class WMTKMesh>
441 std::vector<Tuple> adjacent_elements;
442 if constexpr (std::is_same_v<wmtk::TriMesh, WMTKMesh>)
444 adjacent_elements = {{e}};
445 const std::optional<Tuple> f = e.switch_face(*
this);
447 adjacent_elements.push_back(f.value());
451 adjacent_elements = get_incident_elements_for_edge(e);
454 Eigen::VectorXd adjacent_element_volumes(adjacent_elements.size());
455 for (
int i = 0; i < adjacent_elements.size(); ++i)
456 adjacent_element_volumes[i] = element_volume(adjacent_elements[i]);
458 return adjacent_element_volumes;
461 template <
class WMTKMesh>
464 const size_t starting_size = patch.size();
466 std::unordered_set<size_t> element_ids;
467 for (
const Tuple &t : patch)
468 element_ids.insert(element_id(t));
470 for (
size_t i = 0; i < starting_size; ++i)
472 const size_t id = element_id(patch[i]);
473 for (
int j = 0; j < EDGES_PER_ELEMENT; ++j)
475 const Tuple e = WMTKMesh::tuple_from_edge(
id, j);
477 for (
const Tuple &t : get_incident_elements_for_edge(e))
479 const size_t t_id = element_id(t);
480 if (element_ids.find(t_id) == element_ids.end())
483 element_ids.insert(t_id);
490 template <
class WMTKMesh>
493 min.setConstant(dim(), std::numeric_limits<double>::infinity());
494 max.setConstant(dim(), -std::numeric_limits<double>::infinity());
496 for (
const size_t vid : element_vids(t))
498 min = min.cwiseMin(vertex_attrs[vid].rest_position);
499 max = max.cwiseMax(vertex_attrs[vid].rest_position);
503 template <
class WMTKMesh>
508 const std::vector<Tuple> edges = WMTKMesh::get_edges();
511 const int n_vertices = 2 * edges.size();
513 std::vector<std::vector<int>> elements(edges.size(), std::vector<int>(2));
514 Eigen::MatrixXd rest_positions(n_vertices, dim());
515 Eigen::MatrixXd displacements(n_vertices, dim());
516 Eigen::VectorXd energy_ranks(n_vertices);
517 Eigen::VectorXd elastic_energy_ranks(n_vertices);
518 Eigen::VectorXd contact_energy_ranks(n_vertices);
520 for (
int ei = 0; ei < edges.size(); ei++)
522 const std::array<size_t, 2> vids = {{
523 edges[ei].vid(*
this),
524 edges[ei].switch_vertex(*this).vid(*
this),
527 for (
int vi = 0; vi < vids.size(); ++vi)
529 elements[ei][vi] = 2 * ei + vi;
530 rest_positions.row(elements[ei][vi]) = vertex_attrs[vids[vi]].rest_position;
531 displacements.row(elements[ei][vi]) = vertex_attrs[vids[vi]].displacement();
532 energy_ranks(elements[ei][vi]) = int(edge_attr(edges[ei].eid(*
this)).energy_rank);
533 elastic_energy_ranks(elements[ei][vi]) = int(elastic_ranks.at(vids));
534 if (!contact_ranks.empty())
535 contact_energy_ranks(elements[ei][vi]) = int(contact_ranks.at(vids));
539 const double t0 = state.args[
"time"][
"t0"];
540 const double dt = state.args[
"time"][
"dt"];
541 const double save_dt = dt / 3;
544 const int time_steps = int(round((current_time - t0) / save_dt));
550 const auto vtu_name = [&](
int i) -> std::string {
551 return state.resolve_output_path(fmt::format(
"edge_ranks_{:d}.vtu", i));
554 paraviewo::VTUWriter writer;
555 writer.add_field(
"displacement", displacements);
556 writer.add_field(
"elastic_energy_rank", elastic_energy_ranks);
557 if (!contact_ranks.empty())
558 writer.add_field(
"contact_energy_rank", contact_energy_ranks);
559 writer.add_field(
"energy_rank", energy_ranks);
560 writer.write_mesh(vtu_name(time_steps - 3), rest_positions, elements, state.mesh->is_volume() ? paraviewo::CellType::Tetrahedron : paraviewo::CellType::Triangle);
562 writer.add_field(
"displacement", displacements);
563 writer.add_field(
"elastic_energy_rank", elastic_energy_ranks);
564 if (!contact_ranks.empty())
565 writer.add_field(
"contact_energy_rank", contact_energy_ranks);
566 writer.add_field(
"energy_rank", energy_ranks);
567 writer.write_mesh(vtu_name(time_steps - 2), rest_positions, elements, state.mesh->is_volume() ? paraviewo::CellType::Tetrahedron : paraviewo::CellType::Triangle);
569 writer.add_field(
"displacement", displacements);
570 writer.add_field(
"elastic_energy_rank", elastic_energy_ranks);
571 if (!contact_ranks.empty())
572 writer.add_field(
"contact_energy_rank", contact_energy_ranks);
573 writer.add_field(
"energy_rank", energy_ranks);
574 writer.write_mesh(vtu_name(time_steps - 1), rest_positions, elements, state.mesh->is_volume() ? paraviewo::CellType::Tetrahedron : paraviewo::CellType::Triangle);
576 state.out_geom.save_pvd(
577 state.resolve_output_path(
"edge_ranks.pvd"), vtu_name,
578 time_steps - 1, save_dt, save_dt);
#define POLYFEM_REMESHER_SCOPED_TIMER(name)
#define VERTEX_ATTRIBUTE_SETTER(name, attribute)
#define VERTEX_ATTRIBUTE_GETTER(name, attribute)
main class that contains the polyfem solver and all its state
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)
std::variant< EdgeMap< T >, FaceMap< T > > BoundaryMap
std::unordered_map< std::array< size_t, 3 >, T, polyfem::utils::HashUnorderedArray< size_t, 3 >, polyfem::utils::EqualUnorderedArray< size_t, 3 > > FaceMap
Map from a (sorted) edge to an integer (ID)
virtual void init(const Eigen::MatrixXd &rest_positions, const Eigen::MatrixXd &positions, const Eigen::MatrixXi &elements, const Eigen::MatrixXd &projection_quantities, const BoundaryMap< int > &boundary_to_id, const std::vector< int > &body_ids, const EdgeMap< double > &elastic_energy, const EdgeMap< double > &contact_energy)
Initialize the mesh.
Eigen::MatrixXi elements() const override
Exports elements of the stored mesh.
Eigen::MatrixXd projection_quantities() const override
Exports projected quantities of the stored mesh.
double deformed_edge_length(const Tuple &e) const
void set_projection_quantities(const Eigen::MatrixXd &projection_quantities) override
Set projected quantities of the stored mesh.
void write_edge_ranks_mesh(const EdgeMap< typename EdgeAttributes::EnergyRank > &elastic_ranks, const EdgeMap< typename EdgeAttributes::EnergyRank > &contact_ranks) const
std::vector< Tuple > boundary_facets(std::vector< int > *boundary_ids=nullptr) const
Get the boundary facets of the mesh.
void set_body_ids(const std::vector< int > &body_ids) override
Set the body IDs of all elements.
virtual void init(const Eigen::MatrixXd &rest_positions, const Eigen::MatrixXd &positions, const Eigen::MatrixXi &elements, const Eigen::MatrixXd &projection_quantities, const BoundaryMap< int > &boundary_to_id, const std::vector< int > &body_ids, const EdgeMap< double > &elastic_energy, const EdgeMap< double > &contact_energy) override
Initialize the mesh.
typename WMTKMesh::Tuple Tuple
WildRemesher(const State &state, const Eigen::MatrixXd &obstacle_displacements, const Eigen::MatrixXd &obstacle_vals, const double current_time, const double starting_energy)
Construct a new WildRemesher object.
void set_boundary_ids(const BoundaryMap< int > &boundary_to_id) override
Set the boundary IDs of all edges.
std::vector< Tuple > get_edges_for_elements(const std::vector< Tuple > &elements) const
double rest_edge_length(const Tuple &e) const
Compute the length of an edge.
VectorNd rest_edge_center(const Tuple &e) const
Compute the center of the edge.
std::vector< int > body_ids() const override
Exports body ids of the stored mesh.
void element_aabb(const Tuple &t, polyfem::VectorNd &el_min, polyfem::VectorNd &el_max) const
Get a AABB for an element.
std::vector< int > boundary_nodes(const Eigen::VectorXi &vertex_to_basis) const override
Get the boundary nodes of the stored mesh.
Eigen::VectorXd edge_adjacent_element_volumes(const Tuple &e) const
void extend_local_patch(std::vector< Tuple > &patch) const
Extend the local patch by including neighboring elements.
Eigen::Matrix< double, DIM, 1 > VectorNd
void set_fixed(const std::vector< bool > &fixed) override
Set if a vertex is fixed.
bool invariants(const std::vector< Tuple > &new_tris) override
Check if invariants are satisfied.
VectorNd deformed_edge_center(const Tuple &e) const
WildTetRemesher::Tuple Tuple
Remesher::EdgeMap< typename WildRemesher< WMTKMesh >::EdgeAttributes::EnergyRank > rank_edges(const Remesher::EdgeMap< double > &edge_energy, const json &args)
spdlog::logger & logger()
Retrieves the current logger.
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 > VectorNd
void log_and_throw_error(const std::string &msg)