Wildmeshing Toolkit
Loading...
Searching...
No Matches
ParallelCollect.hpp
1#pragma once
2
3#include <wmtk/ExecutionScheduler.hpp> // for wmtk::Op
4#include <wmtk/threading/collector.hpp>
5#include <wmtk/threading/parallel_for.hpp>
6
7#include <utility>
8#include <vector>
9
10namespace wmtk {
11
12// Build an operation list ("prepare") in parallel by reconstructing the canonical
13// simplices over the cell range and appending per chunk (a single merge lock per chunk,
14// not per element). This replaces the serial `for (auto& t : get_edges()/get_faces())
15// collect.emplace_back(...)` loops. The resulting order differs from serial, but the
16// executor's priority queue reorders it, so the processed set is identical.
17//
18// "Cell" is the top-dimensional element: a tet for TetMesh, a triangle for TriMesh. Both
19// meshes expose cell_capacity() / tuple_from_cell() / EDGES_PER_CELL, so the edge version
20// below is dimension-generic. `parallel_collect_face_ops` is tet-only (a TriMesh has no
21// faces below its cells).
22//
23// `emit(mesh, simplex_tuple, local_out)` appends the desired op(s) for one simplex.
24
25template <class Mesh, class Emit>
26std::vector<std::pair<Op, typename Mesh::Tuple>>
27parallel_collect_edge_ops(Mesh& m, int num_threads, Emit&& emit)
28{
29 using Tuple = typename Mesh::Tuple;
30 constexpr size_t n_edges = Mesh::EDGES_PER_CELL;
31 threading::collector<std::pair<Op, Tuple>> collect;
32
33 threading::parallel_for(
34 threading::range(0, m.cell_capacity()),
35 [&](const threading::range& r) {
36 std::vector<std::pair<Op, Tuple>> local;
37 for (size_t i = r.begin(); i < r.end(); i++) {
38 if (!m.tuple_from_cell(i).is_valid(m)) {
39 continue;
40 }
41 for (size_t j = 0; j < n_edges; j++) {
42 const Tuple e = m.tuple_from_edge(i, j);
43 if (e.eid(m) == n_edges * i + j) {
44 emit(m, e, local); // canonical edge only
45 }
46 }
47 }
48 if (local.empty()) {
49 return;
50 }
51 collect.append(local);
52 },
53 num_threads);
54
55 return collect.data();
56}
57
58template <class Mesh, class Emit>
59std::vector<std::pair<Op, typename Mesh::Tuple>>
60parallel_collect_face_ops(Mesh& m, int num_threads, Emit&& emit)
61{
62 using Tuple = typename Mesh::Tuple;
63 constexpr size_t n_faces = Mesh::FACES_PER_CELL;
64 static_assert(
65 n_faces > 0,
66 "parallel_collect_face_ops requires a mesh with faces below its cells");
67 threading::collector<std::pair<Op, Tuple>> collect;
68
69 threading::parallel_for(
70 threading::range(0, m.cell_capacity()),
71 [&](const threading::range& r) {
72 std::vector<std::pair<Op, Tuple>> local;
73 for (size_t i = r.begin(); i < r.end(); i++) {
74 if (!m.tuple_from_cell(i).is_valid(m)) {
75 continue;
76 }
77 for (size_t j = 0; j < n_faces; j++) {
78 const Tuple f = m.tuple_from_face(i, j);
79 if (f.fid(m) == n_faces * i + j) {
80 emit(m, f, local); // canonical face only
81 }
82 }
83 }
84 if (local.empty()) {
85 return;
86 }
87 collect.append(local);
88 },
89 num_threads);
90
91 return collect.data();
92}
93
94} // namespace wmtk