PolyFEM
Loading...
Searching...
No Matches
PhysicsRemesher.cpp
Go to the documentation of this file.
1#include "PhysicsRemesher.hpp"
2
5
6#include <paraviewo/VTUWriter.hpp>
7
8namespace polyfem::mesh
9{
10 template <class WMTKMesh>
11 std::vector<typename PhysicsRemesher<WMTKMesh>::Tuple>
13 {
14 const double rel_area = args["local_relaxation"]["local_mesh_rel_area"];
15 const double n_ring = args["local_relaxation"]["local_mesh_n_ring"];
17 *this, center, rel_area * this->total_volume, n_ring);
18 }
19
20 template <class WMTKMesh>
22 {
23 using namespace polyfem::solver;
24 using namespace polyfem::basis;
25
26 const std::vector<Tuple> local_mesh_tuples = this->local_mesh_tuples(center);
27
28 const bool include_global_boundary =
29 state.is_contact_enabled() && std::any_of(local_mesh_tuples.begin(), local_mesh_tuples.end(), [&](const Tuple &t) {
30 const size_t tid = this->element_id(t);
31 for (int i = 0; i < Super::FACETS_PER_ELEMENT; ++i)
32 if (this->is_boundary_facet(this->tuple_from_facet(tid, i)))
33 return true;
34 return false;
35 });
36
37 LocalMesh<Super> local_mesh(*this, local_mesh_tuples, include_global_boundary);
38 LocalRelaxationData data(this->state, local_mesh, this->current_time, include_global_boundary);
39 return data.solve_data.nl_problem->value(data.sol());
40 }
41
42 template <class WMTKMesh>
45 const std::string &op,
46 const std::vector<Tuple> &elements) const
47 {
48 // POLYFEM_REMESHER_SCOPED_TIMER("Renew neighbor tuples");
49 assert(elements.size() == 1);
50
51 VectorNd center;
52 if constexpr (std::is_same_v<wmtk::TriMesh, WMTKMesh>)
53 {
54 if (op == "edge_split")
55 center = vertex_attrs[elements[0].switch_vertex(*this).vid(*this)].rest_position;
56 else if (op == "edge_swap")
57 center = (vertex_attrs[elements[0].vid(*this)].rest_position
58 + vertex_attrs[elements[0].switch_vertex(*this).vid(*this)].rest_position)
59 / 2.0;
60 else // if (op == "edge_collapse" || op == "vertex_smooth")
61 center = vertex_attrs[elements[0].vid(*this)].rest_position;
62 }
63 else
64 {
65 assert(op == "edge_split" || op == "edge_collapse");
66 center = vertex_attrs[elements[0].vid(*this)].rest_position;
67 }
68
69 // return all edges affected by local relaxation
70 std::vector<Tuple> local_mesh_tuples = this->local_mesh_tuples(center);
71 this->extend_local_patch(local_mesh_tuples);
72
73 Operations new_ops;
74 for (const Tuple &e : this->get_edges_for_elements(local_mesh_tuples))
75 {
76 const auto energy_rank = this->edge_attr(e.eid(*this)).energy_rank;
77
78 if (op == "edge_split" && energy_rank != Super::EdgeAttributes::EnergyRank::TOP)
79 continue;
80 else if (op == "edge_collapse" && energy_rank != Super::EdgeAttributes::EnergyRank::BOTTOM)
81 continue;
82
83 new_ops.emplace_back(op, e);
84 }
85
86 return new_ops;
87 }
88
89 template <class WMTKMesh>
91 {
92 using namespace polyfem::solver;
93 using namespace polyfem::basis;
94
95 const std::vector<Tuple> elements = this->get_incident_elements_for_edge(e);
96
97 double volume = 0;
98 for (const auto &t : elements)
99 volume += this->element_volume(t);
100 assert(volume > 0);
101
102 LocalMesh<Super> local_mesh(*this, elements, false);
103 LocalRelaxationData data(this->state, local_mesh, this->current_time, false);
104 return data.solve_data.nl_problem->value(data.sol()) / volume; // average energy
105 }
106
107 template <class WMTKMesh>
108 void PhysicsRemesher<WMTKMesh>::write_priority_queue_mesh(const std::string &path, const Tuple &e) const
109 {
110 constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
111 constexpr double tol = 1e-14; // tolerance allowed in recomputed values
113 // Save the edge energy and its position in the priority queue
114 std::unordered_map<size_t, std::tuple<double, double, int>> edge_to_fields;
115
116 // The current tuple was popped from the queue, so we need to recompute its energy
117 const double current_edge_energy = edge_elastic_energy(e);
118 edge_to_fields[e.eid(*this)] = std::make_tuple(current_edge_energy, 0, 0);
119
120 // NOTE: this is not thread-safe
121 auto queue = executor.serial_queue();
122
123 // Also check that the energy is consistent with the priority queue values
124 bool energies_match = true;
125
126 for (int i = 1; !queue.empty(); ++i)
127 {
128 std::tuple<double, std::string, Tuple, size_t> tmp;
129 bool pop_success = queue.try_pop(tmp);
130 assert(pop_success);
131 const auto &[energy, op, t, _] = tmp;
132
133 // Some tuple in the queue might not be valid anymore
134 if (!t.is_valid(*this))
135 {
136 --i; // don't count this tuple
137 continue;
138 }
139
140 // assert(t.eid(*this) != e.eid(*this)); // this should have been popped
141
142 // Check that the energy is consistent with the priority queue values
143 const double recomputed_energy = edge_elastic_energy(t);
144 const double diff = energy - recomputed_energy;
145 if (abs(diff) >= tol)
146 {
147 logger().error(
148 "Energy mismatch: {} vs {}; diff={:g}",
149 energy, recomputed_energy, diff);
150 energies_match = false;
151 }
152
153 // Check that the current edge has the highes priority
154 assert(current_edge_energy - energy >= -tol); // account for numerical error
155
156 // Save the edge energy and its position in the priority queue
157 edge_to_fields[t.eid(*this)] = std::make_tuple(energy, abs(diff), i);
158 }
159 assert(energies_match);
160
161 const std::vector<Tuple> edges = WMTKMesh::get_edges();
162
163 // Create two vertices per edge to get per edge values.
164 const int n_vertices = 2 * edges.size();
165
166 std::vector<std::vector<int>> elements(edges.size(), std::vector<int>(2));
167 Eigen::MatrixXd rest_positions(n_vertices, this->dim());
168 Eigen::MatrixXd displacements(n_vertices, this->dim());
169 Eigen::VectorXd edge_energies(n_vertices);
170 Eigen::VectorXd edge_energy_diffs(n_vertices);
171 Eigen::VectorXd edge_orders(n_vertices);
172
173 for (int ei = 0; ei < edges.size(); ei++)
174 {
175 const std::array<size_t, 2> vids = {{
176 edges[ei].vid(*this),
177 edges[ei].switch_vertex(*this).vid(*this),
178 }};
179
180 double edge_energy, edge_energy_diff, edge_order;
181 const auto &itr = edge_to_fields.find(edges[ei].eid(*this));
182 if (itr != edge_to_fields.end())
183 std::tie(edge_energy, edge_energy_diff, edge_order) = itr->second;
184 else
185 edge_energy = edge_energy_diff = edge_order = NaN;
186
187 for (int vi = 0; vi < vids.size(); ++vi)
188 {
189 elements[ei][vi] = 2 * ei + vi;
190 rest_positions.row(elements[ei][vi]) = vertex_attrs[vids[vi]].rest_position;
191 displacements.row(elements[ei][vi]) = vertex_attrs[vids[vi]].displacement();
192 edge_energies(elements[ei][vi]) = edge_energy;
193 edge_energy_diffs(elements[ei][vi]) = edge_energy_diff;
194 edge_orders(elements[ei][vi]) = edge_order;
195 }
196 }
197
198 paraviewo::VTUWriter writer;
199 writer.add_field("displacement", displacements);
200 writer.add_field("edge_energy", edge_energies);
201 writer.add_field("edge_energy_diff", edge_energy_diffs);
202 writer.add_field("operation_order", edge_orders);
203 writer.write_mesh(path, rest_positions, elements, /*is_simplicial=*/true, /*has_poly=*/false);
204 }
205
206 // -------------------------------------------------------------------------
207 // Template specializations
208
209 template class PhysicsRemesher<wmtk::TriMesh>;
210 template class PhysicsRemesher<wmtk::TetMesh>;
211
212} // namespace polyfem::mesh
static std::vector< Tuple > ball_selection(const M &m, const VectorNd &center, const double rel_radius, const int n_ring_size)
double local_mesh_energy(const VectorNd &local_mesh_center) const
Compute the energy of a local n-ring around a vertex.
void write_priority_queue_mesh(const std::string &path, const Tuple &e) const
Write a visualization mesh of the priority queue.
typename Super::Operations Operations
typename Super::VectorNd VectorNd
double edge_elastic_energy(const Tuple &e) const
Compute the average elastic energy of the faces containing an edge.
Operations renew_neighbor_tuples(const std::string &op, const std::vector< Tuple > &tris) const override
Renew the neighbor tuples of an operation.
std::vector< Tuple > local_mesh_tuples(const VectorNd &center) const
Get the local n-ring around a vertex.
std::shared_ptr< solver::NLProblem > nl_problem
WildTetRemesher::Tuple Tuple
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42