PolyFEM
Loading...
Searching...
No Matches
Swap.cpp
Go to the documentation of this file.
4
5namespace polyfem::mesh
6{
7 template <class WMTKMesh>
9 {
10 if constexpr (std::is_same_v<WMTKMesh, wmtk::TriMesh>)
11 op_cache = TriOperationCache::swap_edge(*this, e);
12 else
13 op_cache = TetOperationCache::swap_32(*this, e);
14 }
15
16 template <class WMTKMesh>
18 {
19 if (!WMTKMesh::swap_edge_before(e))
20 return false;
21
22 const int v0i = e.vid(*this);
23 const int v1i = e.switch_vertex(*this).vid(*this);
24
25 if (is_body_boundary_edge(e))
26 {
27 executor.m_cnt_fail--; // do not count this as a failed swap
28 return false;
29 }
30
31 if constexpr (std::is_same_v<WMTKMesh, wmtk::TriMesh>)
32 {
33 const int v2i = opposite_vertex_on_face(e).vid(*this);
34 const int v3i = opposite_vertex_on_face(*e.switch_face(*this)).vid(*this);
35
36 const double f0_area = std::abs(utils::triangle_area_2D(vertex_attrs[v0i].rest_position, vertex_attrs[v1i].rest_position, vertex_attrs[v2i].rest_position));
37 const double f1_area = std::abs(utils::triangle_area_2D(vertex_attrs[v0i].rest_position, vertex_attrs[v1i].rest_position, vertex_attrs[v3i].rest_position));
38 const double f2_area = std::abs(utils::triangle_area_2D(vertex_attrs[v0i].rest_position, vertex_attrs[v2i].rest_position, vertex_attrs[v3i].rest_position));
39 const double f3_area = std::abs(utils::triangle_area_2D(vertex_attrs[v1i].rest_position, vertex_attrs[v2i].rest_position, vertex_attrs[v3i].rest_position));
40
41 const double total_area = f0_area + f1_area;
42 if (f2_area < 1e-1 * total_area || f3_area < 1e-1 * total_area)
43 {
44 executor.m_cnt_fail--; // do not count this as a failed swap
45 return false;
46 }
47
48 // const double current_area_ratio = f0_area < f1_area ? (f1_area / f0_area) : (f0_area / f1_area);
49 // const double future_area_ratio = f2_area < f3_area ? (f3_area / f2_area) : (f2_area / f3_area);
50
51 // if (future_area_ratio > 100 * current_area_ratio)
52 // {
53 // executor.m_cnt_fail--; // do not count this as a failed swap
54 // return false;
55 // }
56 }
57 // TODO: ratio of elements
58
59 cache_swap_edge(e);
60
61 return true;
62 }
63
65 {
66 if (!Super::swap_edge_before(e)) // NOTE: also calls cache_swap_edge
67 return false;
68
69 if (this->edge_attr(e.eid(*this)).op_attempts++ >= this->max_op_attempts
70 || this->edge_attr(e.eid(*this)).op_depth >= args["swap"]["max_depth"].template get<int>())
71 {
72 this->executor.m_cnt_fail--; // do not count this as a failed swap
73 return false;
74 }
75
76 const VectorNd &v0 = vertex_attrs[e.vid(*this)].rest_position;
77 const VectorNd &v1 = vertex_attrs[e.switch_vertex(*this).vid(*this)].rest_position;
78 this->op_cache->local_energy = local_mesh_energy((v0 + v1) / 2);
79
80 return true;
81 }
82
83 template <>
85 {
86 const auto &old_edges = op_cache->edges();
87 for (const Tuple &e : get_edges_for_elements({{e, e.switch_face(*this).value()}}))
88 {
89 size_t v0_id = e.vid(*this);
90 size_t v1_id = e.switch_vertex(*this).vid(*this);
91
92 const auto iter = old_edges.find({{v0_id, v1_id}});
93 if (iter != old_edges.end())
94 {
95 boundary_attrs[e.eid(*this)] = iter->second;
96 }
97 else
98 {
99 assert(e.switch_face(*this).has_value());
100 // swapped interior edge
101 boundary_attrs[e.eid(*this)] =
102 old_edges.find({{op_cache->v0().first, op_cache->v1().first}})->second;
103 }
104 }
105 }
106
107 template <>
108 void WildTriRemesher::map_edge_swap_element_attributes(const Tuple &e)
109 {
110 assert(op_cache->faces()[0].body_id == op_cache->faces()[1].body_id);
111 element_attrs[e.fid(*this)] = op_cache->faces()[0];
112 element_attrs[e.switch_face(*this)->fid(*this)] = op_cache->faces()[1];
113 }
114
115 template <>
116 bool WildTriRemesher::swap_edge_after(const Tuple &e)
117 {
118 if (!wmtk::TriMesh::swap_edge_after(e))
119 return false;
120
121 // 0) perform operation (done before this function)
122
123 // 1a) Update rest position of new vertex
124 // Nothing to do because there is no new vertex.
125
126 // 1b) Assign edge attributes to the new edge
127 map_edge_swap_edge_attributes(e);
128
129 // 1c) Assign boundary attributes to the new edges/faces
130 // Nothing to do because we disallow boundary edge swaps.
131
132 // 1c) Assign face attributes to the new faces
133 map_edge_swap_element_attributes(e);
134
135 // 2) Interpolate quantaties for a good initialization to the local relaxation
136 // Nothing to do because there is no new vertex.
137
138 // Check the new faces are not inverted
139 assert(e.switch_face(*this).has_value());
140 if (is_inverted(e) || element_volume(e) < 1e-16
141 || is_inverted(e.switch_face(*this).value())
142 || element_volume(e.switch_face(*this).value()) < 1e-16)
143 {
144 return false;
145 }
146
147 // No need to check for intersections because we disallow boundary edge swaps.
148
149 // ~2) Project quantities so to minimize the L2 error~ (done after all operations)
150 // project_quantities(); // also projects positions
151
152 return true;
153 }
154
155 template <>
156 bool WildTetRemesher::swap_edge_after(const Tuple &e)
157 {
158 log_and_throw_error("WildTetRemesher::swap_edge_after not implemented!");
159 }
160
161 bool PhysicsTriRemesher::swap_edge_after(const Tuple &e)
162 {
163 utils::Timer timer(this->timings["Swap edges after"]);
164 timer.start();
165 if (!Super::swap_edge_after(e))
166 return false;
167 // local relaxation has its own timers
168 timer.stop();
169
170 // 3) Perform a local relaxation of the n-ring to get an estimate of the
171 // energy decrease/increase.
172 const VectorNd edge_midpoint =
173 (vertex_attrs[e.vid(*this)].rest_position
174 + vertex_attrs[e.switch_vertex(*this).vid(*this)].rest_position)
175 / 2;
176 return local_relaxation(edge_midpoint, args["swap"]["acceptance_tolerance"])
177 && invariants(std::vector<Tuple>());
178 }
179
180 // -------------------------------------------------------------------------
181
183 const WildTriRemesher &m, std::string op, const WildTriRemesher::Tuple &t)
184 {
186 std::array<std::pair<Tuple, int>, 4> valences;
187 valences[0].first = t;
188 valences[1].first = t.switch_vertex(m);
189 valences[2].first = t.switch_edge(m).switch_vertex(m);
190 assert(t.switch_face(m).has_value());
191 valences[3].first = t.switch_face(m)->switch_edge(m).switch_vertex(m);
192 for (auto &[t, v] : valences)
193 v = m.get_valence_for_vertex(t);
194
195 double cost_before_swap = 0.0, cost_after_swap = 0.0;
196 for (int i = 0; i < valences.size(); i++)
197 {
198 const auto &[vert, valence] = valences[i];
199 const int ideal_val = m.is_boundary_vertex(vert) ? 4 : 6;
200 cost_before_swap += std::pow(double(valence - ideal_val), 2);
201 cost_after_swap +=
202 std::pow(double(i < 2 ? (valence - ideal_val - 1) : (valence - ideal_val + 1)), 2);
203 }
204 return cost_before_swap - cost_after_swap;
205 }
206
208 const WildTriRemesher &m, std::string op, const WildTriRemesher::Tuple &t)
209 {
210 const double f0_area = m.element_volume(t);
211 assert(t.switch_face(m).has_value());
212 const double f1_area = m.element_volume(*t.switch_face(m));
213 return std::max(f0_area, f1_area) / std::min(f0_area, f1_area);
214 }
215
216 void PhysicsTriRemesher::swap_edges()
217 {
218 executor.priority = compute_valence_energy;
219 executor.should_renew = [](auto val) { return (val > 0); };
220 executor.renew_neighbor_tuples = [&](const WildRemesher &m, std::string op, const std::vector<Tuple> &tris) -> Operations {
221 assert(tris.size() == 1);
222 const Tuple &t = tris[0];
223 Operations new_ops;
224 for (const Tuple &t : this->get_edges_for_elements({{t, t.switch_face(*this).value()}}))
225 if (!is_body_boundary_edge(t))
226 new_ops.emplace_back(op, t);
227 return new_ops;
228 };
229
230 // for (int i = 0; i < 20 && (i > 0 || executor.cnt_success() != 0); i++)
231 {
232 std::vector<Tuple> included_edges;
233 {
234 const std::vector<Tuple> edges = this->get_edges();
235 std::copy_if(edges.begin(), edges.end(), std::back_inserter(included_edges), [this](const Tuple &e) {
236 // return this->edge_attr(e.eid(*this)).energy_rank == Super::EdgeAttributes::EnergyRank::BOTTOM;
237 return !is_body_boundary_edge(e);
238 });
239 }
240
241 if (included_edges.empty())
242 return;
243
244 Operations swaps;
245 swaps.reserve(included_edges.size());
246 for (const Tuple &e : included_edges)
247 swaps.emplace_back("edge_swap", e);
248
249 executor(*this, swaps);
250 }
251 }
252
253 // =========================================================================
254 // Map attributes
255 // =========================================================================
256
257 // ------------------------------------------------------------------------
258 // Template specializations
259
260 template class WildRemesher<wmtk::TriMesh>;
261 template class WildRemesher<wmtk::TetMesh>;
262 template class PhysicsRemesher<wmtk::TriMesh>;
263 template class PhysicsRemesher<wmtk::TetMesh>;
264
265} // namespace polyfem::mesh
double val
Definition Assembler.cpp:86
double local_mesh_energy(const VectorNd &local_mesh_center) const
Compute the energy of a local n-ring around a vertex.
wmtk::ExecutePass< WildRemesher, EXECUTION_POLICY > executor
wmtk::AttributeCollection< VertexAttributes > vertex_attrs
bool swap_edge_before(const Tuple &t) override
Definition Swap.cpp:64
const json args
Copy of remesh args.
Definition Remesher.hpp:213
static std::shared_ptr< TetOperationCache > swap_32(WildTetRemesher &m, const Tuple &t)
static std::shared_ptr< TriOperationCache > swap_edge(WildTriRemesher &m, const Tuple &t)
std::conditional< std::is_same< WMTKMesh, wmtk::TriMesh >::value, std::shared_ptr< TriOperationCache >, std::shared_ptr< TetOperationCache > >::type op_cache
bool is_boundary_vertex(const Tuple &v) const
Is the given vertex tuple on the boundary of the mesh?
typename WMTKMesh::Tuple Tuple
EdgeAttributes & edge_attr(const size_t e_id)
Get a reference to an edge's attributes.
virtual bool swap_edge_before(const Tuple &t) override
Definition Swap.cpp:17
void map_edge_swap_edge_attributes(const Tuple &t)
double element_volume(const Tuple &e) const
Compute the volume (area) of an tetrahedron (triangle) element.
void cache_swap_edge(const Tuple &e)
Cache the edge swap operation.
Definition Swap.cpp:8
WildTetRemesher::Tuple Tuple
double compute_area_ratio_energy(const WildTriRemesher &m, std::string op, const WildTriRemesher::Tuple &t)
Definition Swap.cpp:207
double compute_valence_energy(const WildTriRemesher &m, std::string op, const WildTriRemesher::Tuple &t)
Definition Swap.cpp:182
double triangle_area_2D(const Eigen::Vector2d &a, const Eigen::Vector2d &b, const Eigen::Vector2d &c)
Compute the signed area of a 2D triangle defined by three points.
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:71