DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
SerialAdjReordering.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Geom/Geometric.hpp"
4
5#include "CorrectRCM.hpp"
6
7#include "Geom/Metis.hpp"
8
9#include <array>
10#include <boost/graph/adjacency_list.hpp>
11#include <boost/graph/graph_utility.hpp>
12#include <boost/graph/minimum_degree_ordering.hpp>
13#include <boost/graph/cuthill_mckee_ordering.hpp>
14#include <boost/graph/properties.hpp>
15#include <boost/graph/bandwidth.hpp>
16
17namespace DNDS::Geom
18{
19
20 /**
21 * @brief Partition a (sub-)graph using Metis.
22 *
23 * Adjacency entries in [mat_begin, mat_end) are absolute indices offset by ind_offset.
24 * This function internally subtracts ind_offset and filters out cross-sub-graph
25 * references (entries outside [ind_offset, ind_offset + n_elem)) before building
26 * the 0-based CSR that Metis requires.
27 *
28 * @param ind_offset Absolute index of the first row in this sub-graph. For a full
29 * graph this is 0 and no filtering occurs.
30 */
32 tLocalMatStruct::const_iterator mat_begin, tLocalMatStruct::const_iterator mat_end, int nPart,
33 index ind_offset = 0,
34 std::string metisType = "KWAY", int metisNcuts = 3, int metisUfactor = 5, int metisSeed = 0)
35 {
36 idx_t nCell = METIS::indexToIdx(size_t_to_signed<index>(mat_end - mat_begin));
37 idx_t nCon{1};
38 std::array<idx_t, METIS_NOPTIONS> options{};
39 METIS_SetDefaultOptions(options.data());
40 {
41 options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT;
42 options[METIS_OPTION_CTYPE] = METIS_CTYPE_SHEM; //? could try shem?
43 options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_GROW;
44 options[METIS_OPTION_RTYPE] = METIS_RTYPE_FM;
45 // options[METIS_OPTION_NO2HOP] = 0; // only available in metis 5.1.0
46 options[METIS_OPTION_NCUTS] = std::max(metisNcuts, 1);
47 options[METIS_OPTION_NITER] = 10;
48 // options[METIS_OPTION_UFACTOR] = 30; // load imbalance factor, fow k-way
49 options[METIS_OPTION_UFACTOR] = metisUfactor;
50 options[METIS_OPTION_MINCONN] = 1;
51 options[METIS_OPTION_CONTIG] = 1; // ! forcing contigious partition now ? necessary?
52 options[METIS_OPTION_SEED] = metisSeed; // ! seeding 0 for determined result
53 options[METIS_OPTION_NUMBERING] = 0;
54 // options[METIS_OPTION_DBGLVL] = METIS_DBG_TIME | METIS_DBG_IPART;
55 // options[METIS_OPTION_DBGLVL] = METIS_DBG_TIME;
56 }
57 auto &cell2cellFaceV = mat_begin;
58
59 //! Build CSR with 0-based local indices: subtract ind_offset and skip
60 //! entries outside [ind_offset, ind_offset + nCell) (cross-sub-graph edges).
61 std::vector<idx_t> adjncy, xadj;
62 xadj.resize(nCell + 1);
63 xadj[0] = 0;
64 for (idx_t iC = 0; iC < nCell; iC++)
65 {
66 idx_t count = 0;
67 for (auto iCOther : cell2cellFaceV[iC])
68 {
69 index iLocal = iCOther - ind_offset;
70 if (iLocal >= 0 && iLocal < nCell)
71 count++;
72 }
73 xadj[iC + 1] = xadj[iC] + count;
74 }
75 adjncy.resize(xadj.back());
76 for (idx_t iC = 0; iC < nCell; iC++)
77 {
78 idx_t pos = xadj[iC];
79 for (auto iCOther : cell2cellFaceV[iC])
80 {
81 index iLocal = iCOther - ind_offset;
82 if (iLocal >= 0 && iLocal < nCell)
83 adjncy[pos++] = METIS::indexToIdx(iLocal);
84 }
85 }
86
87 idx_t objval;
88 std::vector<idx_t> partOut(nCell);
89
90 int ret{0};
91 if (metisType == "RB")
92 ret = METIS_PartGraphRecursive(
93 &nCell, &nCon, xadj.data(), adjncy.data(), nullptr, nullptr, nullptr,
94 &nPart, nullptr, nullptr, options.data(), &objval, partOut.data());
95 else if (metisType == "KWAY")
96 ret = METIS_PartGraphKway(
97 &nCell, &nCon, xadj.data(), adjncy.data(), nullptr, nullptr, nullptr,
98 &nPart, nullptr, nullptr, options.data(), &objval, partOut.data());
99
100 DNDS_assert_info(ret == METIS_OK, fmt::format("Metis return not ok, [{}]", ret));
101
102 return partOut;
103 }
104
105 inline std::pair<std::vector<index>, std::vector<index>> ReorderSerialAdj_Metis(const tLocalMatStruct &mat)
106 {
107 idx_t nCell = METIS::indexToIdx(size_t_to_signed<index>(mat.size()));
108 idx_t nCon{1};
109 std::array<idx_t, METIS_NOPTIONS> options{};
110 METIS_SetDefaultOptions(options.data());
111 {
112 options[METIS_OPTION_CTYPE] = METIS_CTYPE_SHEM;
113 options[METIS_OPTION_RTYPE] = METIS_RTYPE_FM;
114 options[METIS_OPTION_IPTYPE] = METIS_IPTYPE_EDGE;
115 options[METIS_OPTION_RTYPE] = METIS_RTYPE_SEP1SIDED;
116 options[METIS_OPTION_NSEPS] = 1;
117 options[METIS_OPTION_NITER] = 10;
118 options[METIS_OPTION_UFACTOR] = 30;
119 options[METIS_OPTION_COMPRESS] = 0; // do not compress
120 // options[METIS_OPTION_CCORDER] = 0; //use default?
121 options[METIS_OPTION_SEED] = 0; // ! seeding 0 for determined result
122 options[METIS_OPTION_PFACTOR] = 0; // not removing large vertices
123 options[METIS_OPTION_NUMBERING] = 0;
124 // options[METIS_OPTION_DBGLVL] = METIS_DBG_TIME | METIS_DBG_IPART;
125 }
126 const std::vector<std::vector<index>> &cell2cellFaceV = mat;
127 std::vector<idx_t> adjncy, xadj, perm, iPerm;
128 xadj.resize(nCell + 1);
129 xadj[0] = 0;
130 for (idx_t iC = 0; iC < nCell; iC++)
131 xadj[iC + 1] = signedIntSafeAdd<idx_t>(xadj[iC], size_t_to_signed<idx_t>(cell2cellFaceV[iC].size())); //! check overflow!
132 adjncy.resize(xadj.back());
133 for (idx_t iC = 0; iC < nCell; iC++)
134 std::copy(cell2cellFaceV[iC].begin(), cell2cellFaceV[iC].end(), adjncy.begin() + xadj[iC]);
135 perm.resize(nCell);
136 iPerm.resize(nCell);
137
138 int ret = METIS_NodeND(&nCell, xadj.data(), adjncy.data(), nullptr, options.data(), perm.data(), iPerm.data());
139 DNDS_assert_info(ret == METIS_OK, fmt::format("Metis return not ok, [{}]", ret));
140
141 std::vector<index> localFillOrderingNew2Old, localFillOrderingOld2New;
142
143 localFillOrderingNew2Old.resize(nCell);
144 localFillOrderingOld2New.resize(nCell);
145 for (index i = 0; i < nCell; i++)
146 {
147 localFillOrderingNew2Old[i] = perm[i];
148 localFillOrderingOld2New[i] = iPerm[i];
149 }
150
151 return {localFillOrderingNew2Old, localFillOrderingOld2New};
152 }
153
154 inline std::pair<std::vector<index>, std::vector<index>> ReorderSerialAdj_BoostMMD(const tLocalMatStruct &mat)
155 {
156 std::vector<index> localFillOrderingNew2Old, localFillOrderingOld2New;
157 using namespace boost;
158 using Graph = adjacency_list<vecS, vecS, directedS>;
159 Graph cell2cellG(mat.size());
160 const std::vector<std::vector<index>> &cell2cellFaceV = mat;
161 for (index iCell = 0; iCell < size_t_to_signed<index>(mat.size()); iCell++)
162 for (auto iCOther : cell2cellFaceV[iCell])
163 add_edge(iCell, iCOther, cell2cellG);
164 std::vector<index> supernodeSizes(mat.size(), 1), degree(mat.size(), 0);
165 localFillOrderingNew2Old.resize(mat.size(), 0);
166 localFillOrderingOld2New.resize(mat.size(), 0);
167 boost::property_map<Graph, vertex_index_t>::type id = get(vertex_index, cell2cellG);
168 minimum_degree_ordering(
169 cell2cellG,
170 make_iterator_property_map(degree.data(), id, degree[0]),
171 localFillOrderingOld2New.data(),
172 localFillOrderingNew2Old.data(),
173 make_iterator_property_map(supernodeSizes.data(), id, supernodeSizes[0]),
174 0,
175 id);
176 return {localFillOrderingNew2Old, localFillOrderingOld2New};
177 }
178
179 inline std::pair<std::vector<index>, std::vector<index>> ReorderSerialAdj_BoostRCM(const tLocalMatStruct &mat, index &bandWidthOld, index &bandWidthNew)
180 {
181 std::vector<index> localFillOrderingNew2Old, localFillOrderingOld2New;
182 using namespace boost;
183 using Graph = adjacency_list<vecS, vecS, undirectedS, property<vertex_color_t, default_color_type, property<vertex_degree_t, int>>>;
184 using Vertex = graph_traits<Graph>::vertex_descriptor;
185 // typedef graph_traits<Graph>::vertices_size_type size_type;
186 Graph cell2cellG(mat.size());
187 const std::vector<std::vector<index>> &cell2cellFaceV = mat;
188 bandWidthOld = 0;
189 for (index iCell = 0; iCell < size_t_to_signed<index>(mat.size()); iCell++)
190 for (auto iCOther : cell2cellFaceV[iCell])
191 add_edge(iCell, iCOther, cell2cellG), bandWidthOld = std::max(bandWidthOld, std::abs(iCell - iCOther));
192 localFillOrderingNew2Old.resize(mat.size(), 0);
193 localFillOrderingOld2New.resize(mat.size(), 0);
194 Vertex startVert = vertex(0, cell2cellG);
195 cuthill_mckee_ordering(cell2cellG, startVert, localFillOrderingNew2Old.rbegin(),
196 get(vertex_color, cell2cellG), get(vertex_degree, cell2cellG));
197 std::unordered_set<index> _checkOrder;
198 for (auto v : localFillOrderingNew2Old)
199 DNDS_assert(v < mat.size() && v >= 0), _checkOrder.insert(v);
200 DNDS_assert_info(_checkOrder.size() == localFillOrderingNew2Old.size(), "The output of boost::cuthill_mckee_ordering is invalid!");
201
202 for (index iCell = 0; iCell < size_t_to_signed<index>(mat.size()); iCell++)
203 localFillOrderingOld2New[localFillOrderingNew2Old[iCell]] = iCell;
204 for (auto v : localFillOrderingOld2New)
205 DNDS_assert(v < size_t_to_signed<index>(mat.size()) && v >= 0);
206 bandWidthNew = 0;
207 for (index iCell = 0; iCell < size_t_to_signed<index>(mat.size()); iCell++)
208 for (auto iCOther : cell2cellFaceV[iCell])
209 bandWidthNew = std::max(bandWidthNew, std::abs(localFillOrderingOld2New[iCell] - localFillOrderingOld2New[iCOther]));
210
211 return {localFillOrderingNew2Old, localFillOrderingOld2New};
212 }
213
214 template <class T>
216 {
217 const T *ptr_;
218 T offset_;
219
220 public:
221 using iterator_category = std::input_iterator_tag;
222 using value_type = T;
223 using difference_type = std::ptrdiff_t;
224 using pointer = const T *;
225 using reference = T; // NOT a real reference! It's a temporary.
226
227 OffsetIterator(const T *p, T off) : ptr_(p), offset_(off) {}
228
229 // Dereference returns a transformed VALUE
230 T operator*() const
231 {
232 return *ptr_ + offset_;
233 }
234
235 // Iterator movement
237 {
238 ++ptr_;
239 return *this;
240 }
241
243 {
244 OffsetIterator tmp = *this;
245 ++(*this);
246 return tmp;
247 }
248
249 // Comparison
250 bool operator==(const OffsetIterator &other) const
251 {
252 return ptr_ == other.ptr_;
253 }
254
255 bool operator!=(const OffsetIterator &other) const
256 {
257 return !(*this == other);
258 }
259 };
260 // Proxy wrapper class
261 template <class T>
263 {
264 const T *begin_;
265 const T *end_;
266 T offset_;
267
268 public:
269 // Constructor from a sub-range of a vector
270 OffsetRange(typename std::vector<T>::const_iterator b, typename std::vector<T>::const_iterator e, T offset)
271 : begin_(std::addressof(*b)), end_(std::addressof(*e)), offset_(offset) {}
272
273 // Support for full vector
274 explicit OffsetRange(const std::vector<const T> &vec, T offset)
275 : begin_(vec.data()), end_(vec.data() + vec.size()), offset_(offset) {}
276
277 [[nodiscard]] OffsetIterator<T> begin() const { return {begin_, offset_}; }
278 [[nodiscard]] OffsetIterator<T> end() const { return {end_, offset_}; }
279 [[nodiscard]] ptrdiff_t size() const { return end_ - begin_; }
280 };
281
282 inline std::pair<std::vector<index>, std::vector<index>> ReorderSerialAdj_CorrectRCM(
283 tLocalMatStruct::const_iterator mat_begin, tLocalMatStruct::const_iterator mat_end,
284 index &bandWidthOld, index &bandWidthNew,
285 index offset = 0)
286 {
287 std::vector<index> localFillOrderingNew2Old, localFillOrderingOld2New;
288 bandWidthOld = 0;
289
290 // for (index iCell = 0; iCell < this->NumCell(); ++iCell)
291 // {
292 // cell2cellFaceVLocal[iCell].resize(4);
293 // int x = iCell % 20, y = iCell / 20;
294 // cell2cellFaceVLocal[iCell][0] = mod(x - 1, 20) + y * 20;
295 // cell2cellFaceVLocal[iCell][1] = mod(x + 1, 20) + y * 20;
296 // cell2cellFaceVLocal[iCell][2] = x + mod(y - 1, 20) * 20;
297 // cell2cellFaceVLocal[iCell][3] = x + mod(y + 1, 20) * 20;
298 // }
299
300 index mat_size = mat_end - mat_begin;
301 // std::cout << "HHH " << offset << "," << mat_size << std::endl;
302 for (index iCell = 0; iCell < size_t_to_signed<index>(mat_size); iCell++)
303 for (auto iCOther : mat_begin[iCell])
304 bandWidthOld = std::max(bandWidthOld, std::abs(iCell + offset - iCOther));
305
306 localFillOrderingNew2Old.resize(mat_size, 0);
307 localFillOrderingOld2New.resize(mat_size, 0);
308 auto graphFunctor = [&](index i)
309 {
310 DNDS_assert(i >= 0 && i < mat_size);
311 return OffsetRange<index>(mat_begin[i].cbegin(), mat_begin[i].cend(), -offset); // mind that OffsetRange adds the offset
312 }; // todo: need improvement in CorrectRCM: can pass a temporary functor and store
313 auto graph = CorrectRCM::UndirectedGraphProxy(graphFunctor, size_t_to_signed<int64_t>(mat_size));
314 int ret = graph.CheckAdj();
316 graph,
317 [&](index i) -> index &
318 {
319 return localFillOrderingOld2New.at(i);
320 },
321 0);
322 for (auto &v : localFillOrderingOld2New)
323 v = localFillOrderingOld2New.size() - 1 - v;
324
325 std::unordered_set<index>
326 _checkOrder;
327 for (auto v : localFillOrderingOld2New)
328 DNDS_assert(v < size_t_to_signed<index>(mat_size) && v >= 0), _checkOrder.insert(v);
329 DNDS_check_throw_info(_checkOrder.size() == localFillOrderingOld2New.size(), "The output of CorrectRCM::CuthillMcKeeOrdering is invalid!");
330
331 for (index iCell = 0; iCell < size_t_to_signed<index>(mat_size); iCell++)
332 localFillOrderingNew2Old[localFillOrderingOld2New[iCell]] = iCell;
333 for (auto v : localFillOrderingNew2Old)
334 DNDS_assert(v < size_t_to_signed<index>(mat_size) && v >= 0);
335 bandWidthNew = 0;
336 for (index iCell = 0; iCell < size_t_to_signed<index>(mat_size); iCell++)
337 for (auto iCOther : mat_begin[iCell])
338 bandWidthNew = std::max(bandWidthNew, std::abs(localFillOrderingOld2New[iCell] - localFillOrderingOld2New.at(iCOther - offset)));
339
340 return {localFillOrderingNew2Old, localFillOrderingOld2New};
341 }
342
343 /**
344 * @brief Partition a sub-graph and optionally RCM-reorder within each partition.
345 *
346 * When used for second-level (inner) partitioning, pass full_mat_begin / full_n_elem
347 * so that cross-sub-graph references in the full graph are updated after permutation.
348 * See implementation in SerialAdjReordering.cpp for detailed documentation.
349 */
350 std::vector<index> ReorderSerialAdj_PartitionMetisC(
351 tLocalMatStruct::iterator mat_begin, tLocalMatStruct::iterator mat_end,
352 std::vector<index>::iterator i_new2old_begin, std::vector<index>::iterator i_new2old_end,
353 int nParts,
354 index ind_offset,
355 bool do_rcm,
356 index &bwOldM, index &bwNewM,
357 tLocalMatStruct::iterator full_mat_begin = tLocalMatStruct::iterator{},
358 index full_n_elem = 0);
359
360}
#define DNDS_assert_info(expr, info)
Debug-only assertion with an extra std::string info message.
Definition Errors.hpp:117
#define DNDS_assert(expr)
Debug-only assertion (compiled out when DNDS_NDEBUG is defined). Prints the expression + file/line + ...
Definition Errors.hpp:112
#define DNDS_check_throw_info(expr, info)
Same as DNDS_check_throw but attaches a user-supplied info message to the thrown std::runtime_error.
Definition Errors.hpp:100
bool operator==(const OffsetIterator &other) const
bool operator!=(const OffsetIterator &other) const
std::input_iterator_tag iterator_category
OffsetRange(typename std::vector< T >::const_iterator b, typename std::vector< T >::const_iterator e, T offset)
OffsetIterator< T > begin() const
OffsetRange(const std::vector< const T > &vec, T offset)
OffsetIterator< T > end() const
void CuthillMcKeeOrdering(TGraph &&G, TFFiller &&FFiller, TIndex &&root, std::optional< std::reference_wrapper< std::ostream > > os={})
std::pair< std::vector< index >, std::vector< index > > ReorderSerialAdj_BoostRCM(const tLocalMatStruct &mat, index &bandWidthOld, index &bandWidthNew)
std::pair< std::vector< index >, std::vector< index > > ReorderSerialAdj_Metis(const tLocalMatStruct &mat)
std::pair< std::vector< index >, std::vector< index > > ReorderSerialAdj_BoostMMD(const tLocalMatStruct &mat)
std::pair< std::vector< index >, std::vector< index > > ReorderSerialAdj_CorrectRCM(tLocalMatStruct::const_iterator mat_begin, tLocalMatStruct::const_iterator mat_end, index &bandWidthOld, index &bandWidthNew, index offset=0)
std::vector< index > ReorderSerialAdj_PartitionMetisC(tLocalMatStruct::iterator mat_begin, tLocalMatStruct::iterator mat_end, std::vector< index >::iterator i_new2old_begin, std::vector< index >::iterator i_new2old_end, int nParts, index ind_offset, bool do_rcm, index &bwOldM, index &bwNewM, tLocalMatStruct::iterator full_mat_begin, index full_n_elem)
Partition a sub-graph into nParts and optionally apply RCM reordering within each part.
auto PartitionSerialAdj_Metis(tLocalMatStruct::const_iterator mat_begin, tLocalMatStruct::const_iterator mat_end, int nPart, index ind_offset=0, std::string metisType="KWAY", int metisNcuts=3, int metisUfactor=5, int metisSeed=0)
Partition a (sub-)graph using Metis.
std::vector< std::vector< index > > tLocalMatStruct
Definition Geometric.hpp:66
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
Definition Defines.hpp:112
Eigen::Matrix< real, 5, 1 > v
tVec b(NCells)
const DNDS::index nCell
const tPoint const tPoint const tPoint & p