Hyperiso 1.0.3
Modular flavour-physics calculations, Wilson coefficients and statistical inference
Loading...
Searching...
No Matches
contour.cpp
Go to the documentation of this file.
1#include "contour.h"
2
3MarchingSquaresExtractor::MarchingSquaresExtractor(const ScalarField2D& f, std::array<double, 4> bounds, size_t max_depth) :
4 f(f), max_depth(max_depth)
5{
6 this->xmin = bounds[0];
7 this->xmax = bounds[1];
8 this->ymin = bounds[2];
9 this->ymax = bounds[3];
10 this->N = 1 << this->max_depth;
11 this->root = nullptr;
12}
13
15 this->set_iso_value(lvl);
16 MSGraph first_pass_graph = this->build_adjacency(this->compute_segments());
17 std::set<Path> first_pass_paths = this->extract_paths(first_pass_graph);
18 std::set<Path> second_pass_paths = this->correct_topology(first_pass_paths);
19 return second_pass_paths;
20}
21
22std::unique_ptr<Cell> MarchingSquaresExtractor::build(int i, int j, int depth, int force_refine) {
23 std::array<Index, 4> verts = this->cell_vertices({i, j}, depth);
24 std::array<double, 4> vals;
25 for (size_t i = 0; i < 4; i++)
26 vals[i] = this->vertex_value(verts[i]);
27
28 std::cout << "Vertex values: ";
29 for (auto &&v : vals) {
30 std::cout << v << ",";
31 }
32 std::cout << std::endl;
33
34
35 auto positive = [] (double a) { return a > 0; };
36 bool topology = !std::all_of(vals.begin(), vals.end(), positive) && std::any_of(vals.begin(), vals.end(), positive);
37
38 std::cout << std::boolalpha << "Topology = " << topology << std::endl;
39
40 if (!topology && depth == this->max_depth)
41 return std::make_unique<Cell>(verts, vals, depth, CellStatus::EMPTY_LEAF);
42
43 bool refine = topology || depth < force_refine;
44 if (!refine && depth < this->max_depth - 1) {
45 int I0 = verts[0].first, I1 = verts[1].first, J0 = verts[0].second, J1 = verts[2].second;
46 int Im = (I0 + I1) / 2, Jm = (J0 + J1) / 2;
47 double fc = this->vertex_value({Im, Jm});
48 bool inside_loop = fc * vals[0] < 0;
49
50 if (!inside_loop) {
51 std::set<Index> edge_midpoints {{Im, J0}, {I1, Jm}, {Im, J1}, {I0, Jm}};
52 auto opposite = [this, vals] (Index ijm) { return this->vertex_value(ijm) * vals[0] < 0; };
53 inside_loop = std::any_of(edge_midpoints.begin(), edge_midpoints.end(), opposite);
54
55 if (!inside_loop) {
56 int Iq1 = (3 * I0 + I1) / 4, Iq2 = (I0 + 3 * I1) / 4;
57 int Jq1 = (3 * J0 + J1) / 4, Jq2 = (J0 + 3 * J1) / 4;
58 std::set<Index> quarter_points {{Iq1, Jq1}, {Iq2, Jq1}, {Iq2, Jq2}, {Iq1, Jq2}};
59 inside_loop = std::any_of(quarter_points.begin(), quarter_points.end(), opposite);
60 }
61 }
62
63 refine = refine || inside_loop;
64 }
65
66 if (!refine || depth == this->max_depth) {
67 std::cout << (topology ? "Active" : "Empty") << " leaf" << std::endl;
68 return std::make_unique<Cell>(verts, vals, depth, topology ? CellStatus::ACTIVE_LEAF : CellStatus::EMPTY_LEAF);
69 }
70
71 std::array<std::unique_ptr<Cell>, 4> children;
72 std::array<Index, 4> deltas {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}};
73 for (size_t k = 0; k < 4; k++)
74 children[k] = this->build(2 * i + deltas[k].first, 2 * j + deltas[k].second, depth + 1, force_refine);
75
76 return std::make_unique<Cell>(verts, vals, depth, CellStatus::INTERNAL, std::move(children));
77}
78
79Index MarchingSquaresExtractor::point_to_idx(Point xy) const {
80 double u = (xy.first - this->xmin) / (this->xmax - this->xmin);
81 double v = (xy.second - this->ymin) / (this->ymax - this->ymin);
82
83 int I = static_cast<int>(u * this->N);
84 int J = static_cast<int>(v * this->N);
85
86 return {I, J};
87}
88
89Point MarchingSquaresExtractor::idx_to_point(Index ij) const {
90 double x = this->xmin + (this->xmax - this->xmin) * ij.first / this->N;
91 double y = this->ymin + (this->ymax - this->ymin) * ij.second / this->N;
92
93 return {x, y};
94}
95
96std::array<Index, 4> MarchingSquaresExtractor::cell_vertices(Index ij, std::size_t depth) const {
97 int scale = 1 << (this->max_depth - depth);
98
99 int I0 = ij.first * scale;
100 int I1 = (ij.first + 1) * scale;
101 int J0 = ij.second * scale;
102 int J1 = (ij.second + 1) * scale;
103
104 return {{{I0, J0}, {I1, J0}, {I1, J1}, {I0, J1}}};
105}
106
107double MarchingSquaresExtractor::vertex_value(Index ij) {
108 if (!this->vertices.contains(ij)) {
109 Point xy = idx_to_point(ij);
110 this->vertices.emplace(ij, this->f(xy.first, xy.second) - this->k);
111 }
112
113 return this->vertices.at(ij);
114}
115
116Cell* MarchingSquaresExtractor::locate_leaf(Point xy) const {
117 Cell* cell = this->root.get();
118 Index IJ = point_to_idx(xy);
119
120 while (cell->children.has_value()) {
121 auto verts = cell->vertices;
122 int Im = (verts[0].first + verts[2].first) / 2;
123 int Jm = (verts[0].second + verts[2].second) / 2;
124
125 if (IJ.first < Im)
126 cell = IJ.second < Jm ? cell->children.value()[0].get() : cell->children.value()[3].get();
127 else
128 cell = IJ.second < Jm ? cell->children.value()[1].get() : cell->children.value()[2].get();
129 }
130
131 return cell;
132}
133
134Point MarchingSquaresExtractor::edge_point(Cell *cell, int edge) {
135 auto verts = cell->vertices;
136 auto vals = cell->values;
137
138 Index ab = Cell::EDGES[edge];
139 Index IJa = verts[ab.first];
140 Index IJb = verts[ab.second];
141
142 double fa = vals[ab.first];
143 double fb = vals[ab.second];
144 double t = -fa / (fb - fa);
145
146 Point xy_a = idx_to_point(IJa);
147 Point xy_b = idx_to_point(IJb);
148
149 return {
150 xy_a.first + t * (xy_b.first - xy_a.first),
151 xy_a.second + t * (xy_b.second - xy_a.second)
152 };
153}
154
155std::set<std::pair<int, int>> MarchingSquaresExtractor::get_crossed_edges(Cell *cell) {
156 int mask = 0;
157 for (size_t i = 0; i < 4; i++)
158 if (cell->values[i] > 0) mask |= (1 << i);
159
160 if (mask == 5 || mask == 10) {
161 auto verts = cell->vertices;
162 int Im = (verts[0].first + verts[2].first) / 2;
163 int Jm = (verts[0].second + verts[2].second) / 2;
164 double fc = vertex_value({Im, Jm});
165 mask = fc * cell->values[0] > 0 ? 5 : 10;
166 }
167
168 return MASK_LOOKUP[mask];
169}
170
171bool MarchingSquaresExtractor::point_on_boundary(Point xy, double tol) const {
172 double x = xy.first, y = xy.second;
173 return (
174 std::abs(x - this->xmin) < tol ||
175 std::abs(x - this->xmax) < tol ||
176 std::abs(y - this->ymin) < tol ||
177 std::abs(y - this->ymax) < tol
178 );
179}
180
181void MarchingSquaresExtractor::set_iso_value(double lvl) {
182 this->k = lvl;
183 this->vertices.clear();
184 this->root = this->build(0, 0, 0);
185 this->active_cells.clear();
186 this->update_active_cells(this->root.get());
187
188 std::cout << "Active cells: " << this->active_cells.size() << std::endl;
189}
190
191void MarchingSquaresExtractor::update_active_cells(Cell *cell) {
192 if (cell->status == CellStatus::EMPTY_LEAF)
193 return;
194
195 if (cell->status == CellStatus::ACTIVE_LEAF) {
196 this->active_cells.emplace(cell);
197 return;
198 }
199
200 for (std::unique_ptr<Cell>& child : cell->children.value()) {
201 this->update_active_cells(child.get());
202 }
203}
204
205std::vector<Segment> MarchingSquaresExtractor::compute_segments() {
206 std::vector<Segment> segments;
207
208 for (Cell* cell : this->active_cells) {
209 auto crossed_edges = this->get_crossed_edges(cell);
210 for (auto edge_pair : crossed_edges) {
211 Point p0 = this->edge_point(cell, edge_pair.first);
212 Point p1 = this->edge_point(cell, edge_pair.second);
213 segments.emplace_back(p0, p1);
214 }
215 }
216
217 return segments;
218}
219
220MSGraph MarchingSquaresExtractor::build_adjacency(std::vector<Segment> segments) const {
221 double coord_max = std::max({std::abs(this->xmin), std::abs(this->xmax), std::abs(this->ymin), std::abs(this->ymax)});
222 double epsilon = 2 * coord_max / INT32_MAX;
223 auto quantize = [epsilon] (Point p) -> std::pair<int, int> {
224 return {static_cast<int>(p.first / epsilon), static_cast<int>(p.second / epsilon)};
225 };
226
227 std::map<Index, Point> vertices;
228 std::map<Index, std::vector<Index>> adjacency;
229
230 for (auto& s : segments) {
231 Index k0 = quantize(s.first);
232 Index k1 = quantize(s.second);
233 vertices.emplace(k0, s.first);
234 vertices.emplace(k1, s.second);
235
236 if (adjacency.contains(k0))
237 adjacency.at(k0).emplace_back(k1);
238 else
239 adjacency.emplace(k0, std::vector {k1});
240
241 if (adjacency.contains(k1))
242 adjacency.at(k1).emplace_back(k0);
243 else
244 adjacency.emplace(k1, std::vector {k0});
245 }
246
247 return MSGraph {std::move(vertices), std::move(adjacency)};
248}
249
250std::set<Path> MarchingSquaresExtractor::extract_paths(MSGraph graph) const {
251 auto unused = get_keys(graph.adjacency);
252 std::set<Path> paths;
253 std::set<Index> path_ends;
254
255 while (!unused.empty()) {
256 auto start = *unused.begin();
257 unused.erase(start);
258
259 if (graph.adjacency.at(start).size() == 2) {
260 std::stack<Index> stack;
261 stack.push(start);
262 std::set<Index> seen = {start};
263 Index open_start;
264 bool has_open_start {false};
265 while (!stack.empty()) {
266 Index u = stack.top();
267 stack.pop();
268
269 if (graph.adjacency.at(u).size() == 1) {
270 open_start = u;
271 has_open_start = true;
272 break;
273 }
274
275 for (auto v : graph.adjacency.at(u)) {
276 if (!seen.contains(v)) {
277 seen.emplace(v);
278 stack.push(v);
279 }
280 }
281 }
282
283 if (has_open_start)
284 start = open_start;
285
286 std::vector<Index> path = {start};
287 Index prev;
288 Index cur = start;
289
290 if (path_ends.contains(start)) continue;
291
292 while (true) {
293 auto neigh = graph.adjacency.at(cur);
294 Index next;
295 bool has_next {false};
296 for (Index v : neigh) {
297 if (v != prev) {
298 next = v;
299 has_next = true;
300 break;
301 }
302 }
303 if (!has_next) break;
304
305 path.emplace_back(next);
306 unused.erase(next);
307 prev = cur;
308 cur = next;
309
310 if (cur == start) break;
311 }
312
313 path_ends.emplace(start);
314 path_ends.emplace(cur);
315
316 Path point_path;
317 for (Index idx : path)
318 point_path.emplace_back(graph.vertices.at(idx));
319
320 paths.emplace(point_path);
321 }
322 }
323
324 return paths;
325}
326
327std::set<Point> MarchingSquaresExtractor::detect_dangling_ends(std::set<Path> paths) const {
328 std::set<Point> dangling;
329 for (Path p : paths) {
330 if (p.front() != p.back() && !(point_on_boundary(p.front()) && point_on_boundary(p.back()))) {
331 dangling.emplace(p.front());
332 dangling.emplace(p.back());
333 }
334 }
335 return dangling;
336}
337
338void MarchingSquaresExtractor::refine_leaf(Cell *leaf) {
339 if (leaf->status == CellStatus::INTERNAL || leaf->depth == this->max_depth)
340 return;
341
342 size_t d = leaf->depth;
343 int scale = 1 << (this->max_depth - d);
344 Index IJ0 = leaf->vertices[0];
345 double i = IJ0.first / scale, j = IJ0.second / scale;
346 std::unique_ptr<Cell> refined = this->build(i, j, d, std::min(d + 2, this->max_depth));
347 *leaf = std::move(*refined);
348}
349
350std::set<Path> MarchingSquaresExtractor::correct_topology(std::set<Path> raw_paths) {
351 std::set<Point> dangling = this->detect_dangling_ends(raw_paths);
352 std::set<Cell*> to_refine;
353
354 for (Point p : dangling)
355 to_refine.emplace(this->locate_leaf(p));
356
357 for (Cell* cell : to_refine)
358 this->refine_leaf(cell);
359
360 this->update_active_cells(this->root.get());
361 MSGraph updated_graph = this->build_adjacency(this->compute_segments());
362 return this->extract_paths(updated_graph);
363}
std::unordered_set< T > get_keys(const std::map< T, U > &map)
Extracts the key set from a std::map into an unordered_set.
Definition Utils.h:108
std::set< Path > find_iso_contour(double lvl)
Definition contour.cpp:14
MarchingSquaresExtractor(const ScalarField2D &f, std::array< double, 4 > bounds, size_t max_depth=7)
Definition contour.cpp:3
constexpr std::complex< double > I
Definition constants.h:20
std::pair< int, int > Index
Definition contour.h:15
const std::vector< std::set< Index > > MASK_LOOKUP
Definition contour.h:19
std::vector< Point > Path
Definition contour.h:16
std::function< double(double, double)> ScalarField2D
Definition contour.h:17
std::pair< double, double > Point
Definition contour.h:13
CellStatus
Definition contour.h:38
constexpr double epsilon
csl::Expr v
Definition sm.h:110
double f(double x)
Wilson special function f depending on x.
double I1(double x)
double I0(double x)
Definition contour.h:44
std::array< Index, 4 > vertices
Definition contour.h:47
std::size_t depth
Definition contour.h:49
std::array< double, 4 > values
Definition contour.h:48
CellStatus status
Definition contour.h:50
static const std::vector< Index > EDGES
Definition contour.h:45
std::optional< std::array< std::unique_ptr< Cell >, 4 > > children
Definition contour.h:51
std::map< Index, std::vector< Index > > adjacency
Definition contour.h:56