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