1 | /* |
2 | * DistParMetisDistribution.hpp |
3 | * |
4 | * Created on: Nov 19, 2015 |
5 | * Author: Antonio Leo |
6 | */ |
7 | |
8 | #ifndef SRC_DECOMPOSITION_DISTPARMETISDISTRIBUTION_HPP_ |
9 | #define SRC_DECOMPOSITION_DISTPARMETISDISTRIBUTION_HPP_ |
10 | |
11 | #include "SubdomainGraphNodes.hpp" |
12 | #include "parmetis_dist_util.hpp" |
13 | #include "Graph/dist_map_graph.hpp" |
14 | #include "Graph/DistGraphFactory.hpp" |
15 | |
16 | template<unsigned int dim, typename T> |
17 | class DistParMetisDistribution |
18 | { |
19 | //! Vcluster |
20 | Vcluster<> & v_cl; |
21 | |
22 | //! Structure that store the cartesian grid information |
23 | grid_sm<dim, void> gr; |
24 | |
25 | //! rectangular domain to decompose |
26 | Box<dim, T> domain; |
27 | |
28 | //! Processor sub-sub-domain graph |
29 | DistGraph_CSR<nm_v<dim>, nm_e> g; |
30 | |
31 | //! Convert the graph to parmetis format |
32 | DistParmetis<DistGraph_CSR<nm_v<dim>, nm_e>> parmetis_graph; |
33 | |
34 | //! Init vtxdist needed for Parmetis |
35 | openfpm::vector<idx_t> vtxdist; |
36 | |
37 | //! partitions |
38 | openfpm::vector<openfpm::vector<idx_t>> partitions; |
39 | |
40 | //! Init data structure to keep trace of new vertices distribution in processors (needed to update main graph) |
41 | openfpm::vector<openfpm::vector<size_t>> v_per_proc; |
42 | |
43 | //! Number of moved vertices in all iterations |
44 | size_t g_moved = 0; |
45 | |
46 | //! Max number of moved vertices in all iterations |
47 | size_t m_moved = 0; |
48 | |
49 | //! Flag to check if weights are used on vertices |
50 | bool verticesGotWeights = false; |
51 | |
52 | /*! \brief Callback of the sendrecv to set the size of the array received |
53 | * |
54 | * \param msg_i Index of the message |
55 | * \param total_msg Total numeber of messages |
56 | * \param total_p Total number of processors to comunicate with |
57 | * \param i Processor id |
58 | * \param ri Request id |
59 | * \param ptr Void pointer parameter for additional data to pass to the call-back |
60 | */ |
61 | static void * message_receive(size_t msg_i, size_t total_msg, size_t total_p, size_t i, size_t ri, void * ptr) |
62 | { |
63 | openfpm::vector < openfpm::vector < idx_t >> *v = static_cast<openfpm::vector<openfpm::vector<idx_t>> *>(ptr); |
64 | |
65 | v->get(i).resize(msg_i / sizeof(idx_t)); |
66 | |
67 | return &(v->get(i).get(0)); |
68 | } |
69 | |
70 | public: |
71 | |
72 | /*! Constructor for the ParMetis class |
73 | * |
74 | * @param v_cl Vcluster to use as communication object in this class |
75 | */ |
76 | DistParMetisDistribution(Vcluster<> & v_cl) : |
77 | v_cl(v_cl), parmetis_graph(v_cl, v_cl.getProcessingUnits()), vtxdist(v_cl.getProcessingUnits() + 1), partitions(v_cl.getProcessingUnits()), v_per_proc(v_cl.getProcessingUnits()) |
78 | |
79 | { |
80 | } |
81 | |
82 | /*! \brief Initialize the distribution graph |
83 | * |
84 | * /param grid Grid |
85 | * /param dom Domain |
86 | */ |
87 | void createCartGraph(grid_sm<dim, void> & grid, Box<dim, T> dom) |
88 | { |
89 | //! Set grid and domain |
90 | gr = grid; |
91 | domain = dom; |
92 | |
93 | //! Create sub graph |
94 | DistGraphFactory<dim, DistGraph_CSR<nm_v<dim>, nm_e>> dist_g_factory; |
95 | g = dist_g_factory.template construct<NO_EDGE, T, dim - 1, 0>(gr.getSize(), domain); |
96 | g.getDecompositionVector(vtxdist); |
97 | |
98 | if (dim == 2) |
99 | for (size_t i = 0; i < g.getNVertex(); i++) |
100 | g.vertex(i).template get<nm_v_x>()[2] = 0; |
101 | |
102 | } |
103 | |
104 | /*! \brief Get the current graph (main) |
105 | * |
106 | */ |
107 | DistGraph_CSR<nm_v<dim>, nm_e> & getGraph() |
108 | { |
109 | return g; |
110 | } |
111 | |
112 | /*! \brief Create first decomposition, it divides the graph in slices and give each slice to a processor |
113 | * |
114 | */ |
115 | void decompose() |
116 | { |
117 | //! Init sub graph in parmetis format |
118 | parmetis_graph.initSubGraph(g); |
119 | |
120 | //! Decompose |
121 | parmetis_graph.template decompose<nm_v_proc_id>(g); |
122 | |
123 | //! Get result partition for this processors |
124 | idx_t *partition = parmetis_graph.getPartition(); |
125 | |
126 | for (size_t i = 0, j = g.firstId(); i < g.getNVertex() && j <= g.lastId(); i++, j++) |
127 | { |
128 | if ((size_t)partition[i] != v_cl.getProcessUnitID()) |
129 | g.q_move(g.nodeById(j), partition[i]); |
130 | } |
131 | g.redistribute(); |
132 | } |
133 | |
134 | /*! \brief Refine current decomposition |
135 | * |
136 | * It makes a refinement of the current decomposition using Parmetis function RefineKWay |
137 | * After that it also does the remapping of the graph |
138 | * |
139 | */ |
140 | void refine() |
141 | { |
142 | //! Reset parmetis graph and reconstruct it |
143 | parmetis_graph.reset(g); |
144 | |
145 | //! Refine |
146 | parmetis_graph.template refine<nm_v_proc_id>(g); |
147 | |
148 | //! Get result partition for this processors |
149 | idx_t *partition = parmetis_graph.getPartition(); |
150 | |
151 | for (size_t i = 0, j = g.firstId(); i < g.getNVertex() && j <= g.lastId(); i++, j++) |
152 | { |
153 | if ((size_t)partition[i] != v_cl.getProcessUnitID()) |
154 | g.q_move(g.nodeById(j), partition[i]); |
155 | } |
156 | g.redistribute(); |
157 | } |
158 | |
159 | /*! \brief Compute the unbalance value |
160 | * |
161 | * \return the unbalance value |
162 | */ |
163 | float getUnbalance() |
164 | { |
165 | long t_cost = getProcessorLoad(); |
166 | |
167 | long min, max, sum; |
168 | float unbalance; |
169 | |
170 | min = t_cost; |
171 | max = t_cost; |
172 | sum = t_cost; |
173 | |
174 | v_cl.min(min); |
175 | v_cl.max(max); |
176 | v_cl.sum(sum); |
177 | v_cl.execute(); |
178 | |
179 | unbalance = ((float) (max - min)) / (float) (sum / v_cl.getProcessingUnits()); |
180 | |
181 | return unbalance * 100; |
182 | } |
183 | |
184 | /*! \brief function that return the position of the vertex in the space |
185 | * |
186 | * \param id vertex id |
187 | * \param pos vector that will contain x, y, z |
188 | * |
189 | */ |
190 | void getSubSubDomainPosition(size_t id, T (&pos)[dim]) |
191 | { |
192 | #ifdef SE_CLASS1 |
193 | if (id >= g.getNVertex()) |
194 | std::cerr << __FILE__ << ":" << __LINE__ << " Position - Such vertex doesn't exist (id = " << id << ", " << "total size = " << g.getNVertex() << ")\n" ; |
195 | #endif |
196 | |
197 | pos[0] = g.vertex(id).template get<nm_v_x>()[0]; |
198 | pos[1] = g.vertex(id).template get<nm_v_x>()[1]; |
199 | if (dim == 3) |
200 | pos[2] = g.vertex(id).template get<nm_v_x>()[2]; |
201 | } |
202 | |
203 | /*! \brief Function that set the weight of the vertex |
204 | * |
205 | * \param id vertex id |
206 | * \param weight to give to the vertex |
207 | * |
208 | */ |
209 | inline void setComputationCost(size_t id, size_t weight) |
210 | { |
211 | verticesGotWeights = true; |
212 | #ifdef SE_CLASS1 |
213 | if (id >= g.getNVertex()) |
214 | std::cerr << __FILE__ << ":" << __LINE__ << "Weight - Such vertex doesn't exist (id = " << id << ", " << "total size = " << g.getNVertex() << ")\n" ; |
215 | #endif |
216 | |
217 | // If the vertex is inside this processor update the value |
218 | g.vertex(id).template get<nm_v_computation>() = weight; |
219 | |
220 | } |
221 | |
222 | /*! \brief Checks if weights are used on the vertices |
223 | * |
224 | * \return true if weights are used in the decomposition |
225 | */ |
226 | bool weightsAreUsed() |
227 | { |
228 | return verticesGotWeights; |
229 | } |
230 | |
231 | /*! \brief function that get the weight of the vertex |
232 | * |
233 | * \param id vertex id |
234 | * |
235 | * \return the weight of the vertex |
236 | * |
237 | */ |
238 | size_t getVertexWeight(size_t id) |
239 | { |
240 | #ifdef SE_CLASS1 |
241 | if (id >= g.getNVertex()) |
242 | std::cerr << __FILE__ << ":" << __LINE__ << "Such vertex doesn't exist (id = " << id << ", " << "total size = " << g.getTotNVertex() << ")\n" ; |
243 | #endif |
244 | |
245 | return g.vertex(id).template get<nm_v_computation>(); |
246 | } |
247 | |
248 | /*! \brief Compute the processor load counting the total weights of its vertices |
249 | * |
250 | * \return the computational load of the processor graph |
251 | */ |
252 | size_t getProcessorLoad() |
253 | { |
254 | size_t load = 0; |
255 | |
256 | for (size_t i = 0; i < g.getNVertex(); i++) |
257 | { |
258 | load += g.vertex(i).template get<nm_v_computation>(); |
259 | } |
260 | return load; |
261 | } |
262 | |
263 | /*! \brief return number of moved vertices in all iterations so far |
264 | * |
265 | * \return number of moved vertices |
266 | * |
267 | */ |
268 | size_t getTotalMovedV() |
269 | { |
270 | return g_moved; |
271 | } |
272 | |
273 | /*! \brief return number of moved vertices in all iterations so far |
274 | * |
275 | * \return number of moved vertices |
276 | * |
277 | */ |
278 | size_t getMaxMovedV() |
279 | { |
280 | return m_moved; |
281 | } |
282 | |
283 | /*! \brief Set migration cost of the vertex id |
284 | * |
285 | * \param id of the vertex to update |
286 | * \param migration cost of the migration |
287 | */ |
288 | void setMigrationCost(size_t id, size_t migration) |
289 | { |
290 | #ifdef SE_CLASS1 |
291 | if (id >= g.getNVertex()) |
292 | std::cerr << __FILE__ << ":" << __LINE__ << "Migration - Such vertex doesn't exist (id = " << id << ", " << "total size = " << g.getNVertex() << ")\n" ; |
293 | #endif |
294 | |
295 | g.vertex(id).template get<nm_v_migration>() = migration; |
296 | } |
297 | |
298 | /*! \brief Set communication cost of the edge id |
299 | * |
300 | * \param v_id Id of the source vertex of the edge |
301 | * \param e i child of the vertex |
302 | * \param communication Communication value |
303 | */ |
304 | void setCommunicationCost(size_t v_id, size_t e, size_t communication) |
305 | { |
306 | g.getChildEdge(v_id, e).template get<nm_e::communication>() = communication; |
307 | } |
308 | |
309 | /*! \brief Returns total number of sub-sub-domains in the distribution graph |
310 | * |
311 | * \return number od sub-sub-domain |
312 | * |
313 | */ |
314 | size_t getNSubSubDomains() |
315 | { |
316 | return g.getNVertex(); |
317 | } |
318 | |
319 | /*! \brief Returns total number of neighbors of the sub-sub-domain id |
320 | * |
321 | * \param id id of the sub-sub-domain |
322 | * |
323 | * \return the number of neighborhood sub-sub-domain |
324 | * |
325 | */ |
326 | size_t getNSubSubDomainNeighbors(size_t id) |
327 | { |
328 | if (id >= g.getNVertex()) |
329 | std::cerr << "Neighbors - Such vertex doesn't exist (id = " << id << ", " << "total size = " << g.getNVertex() << ")\n" ; |
330 | |
331 | return g.getNChilds(id); |
332 | } |
333 | |
334 | /*! \brief Print current graph and save it to file |
335 | * |
336 | * \param file file |
337 | * |
338 | */ |
339 | void write(const std::string & file) |
340 | { |
341 | VTKWriter<DistGraph_CSR<nm_v<dim>, nm_e>, DIST_GRAPH> gv2(g); |
342 | gv2.write(std::to_string(file + ".vtk" )); |
343 | } |
344 | |
345 | /*! \brief copy operator |
346 | * |
347 | * \param dist object to copy |
348 | * |
349 | * \return itself |
350 | * |
351 | */ |
352 | const DistParMetisDistribution<dim, T> & operator=(const DistParMetisDistribution<dim, T> & dist) |
353 | { |
354 | gr = dist.gr; |
355 | domain = dist.domain; |
356 | g = dist.g; |
357 | vtxdist = dist.vtxdist; |
358 | partitions = dist.partitions; |
359 | v_per_proc = dist.v_per_proc; |
360 | verticesGotWeights = dist.verticesGotWeights; |
361 | |
362 | return *this; |
363 | } |
364 | |
365 | /*! \brief copy operator |
366 | * |
367 | * \param dist object to copy |
368 | * |
369 | * \return itself |
370 | * |
371 | */ |
372 | const DistParMetisDistribution<dim, T> & operator=(DistParMetisDistribution<dim, T> && dist) |
373 | { |
374 | gr = dist.gr; |
375 | domain = dist.domain; |
376 | g.swap(dist.g); |
377 | vtxdist.swap(dist.vtxdist); |
378 | partitions.swap(dist.partitions); |
379 | v_per_proc.swap(dist.v_per_proc); |
380 | verticesGotWeights = dist.verticesGotWeights; |
381 | |
382 | return *this; |
383 | } |
384 | } |
385 | ; |
386 | |
387 | #endif /* SRC_DECOMPOSITION_PARMETISDISTRIBUTION_HPP_ */ |
388 | |