OpenFPM_pdata  1.1.0
Project that contain the implementation of distributed structures
 All Data Structures Namespaces Functions Variables Typedefs Enumerations Friends Pages
ParMetisDistribution.hpp
1 /*
2  * ParMetisDistribution.hpp
3  *
4  * Created on: Nov 19, 2015
5  * Author: Antonio Leo
6  */
7 
8 
9 #ifndef SRC_DECOMPOSITION_PARMETISDISTRIBUTION_HPP_
10 #define SRC_DECOMPOSITION_PARMETISDISTRIBUTION_HPP_
11 
12 
13 #include "SubdomainGraphNodes.hpp"
14 #include "parmetis_util.hpp"
15 #include "Graph/ids.hpp"
16 #include "Graph/CartesianGraphFactory.hpp"
17 
18 #define PARMETIS_DISTRIBUTION_ERROR 100002
19 
35 template<unsigned int dim, typename T>
37 {
39  bool is_distributed = false;
40 
43 
46 
49 
52 
55 
58 
60  //
61  // vtxdist is a common array across processor, it indicate how
62  // vertex are distributed across processors
63  //
64  // Example we have 3 processors
65  //
66  // processor 0 has 3 vertices
67  // processor 1 has 5 vertices
68  // processor 2 has 4 vertices
69  //
70  // vtxdist contain, 0,3,8,12
71  //
72  // vtx dist is the unique global-id of the vertices
73  //
75 
78 
81 
83  std::unordered_map<rid, gid> m2g;
84 
86  bool verticesGotWeights = false;
87 
91  void updateGraphs()
92  {
93  sub_sub_owner.clear();
94 
95  size_t Np = v_cl.getProcessingUnits();
96 
97  // Init n_vtxdist to gather informations about the new decomposition
98  openfpm::vector<rid> n_vtxdist(Np + 1);
99  for (size_t i = 0; i <= Np; i++)
100  n_vtxdist.get(i).id = 0;
101 
102  // Update the main graph with received data from processor i
103  for (size_t i = 0; i < Np; i++)
104  {
105  size_t ndata = partitions.get(i).size();
106  size_t k = 0;
107 
108  // Update the main graph with the received informations
109  for (rid l = vtxdist.get(i); k < ndata && l < vtxdist.get(i + 1); k++, ++l)
110  {
111  // Create new n_vtxdist (just count processors vertices)
112  ++n_vtxdist.get(partitions.get(i).get(k) + 1);
113 
114  // vertex id from vtx to grobal id
115  auto v_id = m2g.find(l)->second.id;
116 
117  // Update proc id in the vertex (using the old map)
118  gp.template vertex_p<nm_v::proc_id>(v_id) = partitions.get(i).get(k);
119 
120  if (partitions.get(i).get(k) == (long int)v_cl.getProcessUnitID())
121  sub_sub_owner.add(v_id);
122 
123  // Add vertex to temporary structure of distribution (needed to update main graph)
124  v_per_proc.get(partitions.get(i).get(k)).add(getVertexGlobalId(l));
125  }
126  }
127 
128  // Create new n_vtxdist (accumulate the counters)
129  for (size_t i = 2; i <= Np; i++)
130  n_vtxdist.get(i) += n_vtxdist.get(i - 1);
131 
132  // Copy the new decomposition in the main vtxdist
133  for (size_t i = 0; i <= Np; i++)
134  vtxdist.get(i) = n_vtxdist.get(i);
135 
137  cnt.resize(Np);
138 
139  for (size_t i = 0 ; i < gp.getNVertex(); ++i)
140  {
141  size_t pid = gp.template vertex_p<nm_v::proc_id>(i);
142 
143  rid j = rid(vtxdist.get(pid).id + cnt.get(pid));
144  gid gi = gid(i);
145 
146  gp.template vertex_p<nm_v::id>(i) = j.id;
147  cnt.get(pid)++;
148 
149  setMapId(j,gi);
150  }
151  }
152 
160  inline auto vertexByMapId(rid id) -> decltype( gp.vertex(m2g.find(id)->second.id) )
161  {
162  return gp.vertex(m2g.find(id)->second.id);
163  }
164 
171  inline void setMapId(rid n, gid g)
172  {
173  m2g[n] = g;
174  }
175 
183  {
184  return m2g.find(n)->second;
185  }
186 
193  {
194  gid g;
195  rid i;
196  i.id = 0;
197 
198  m2g.clear();
199  for ( ; (size_t)i.id < gp.getNVertex(); ++i)
200  {
201  g.id = i.id;
202 
203  m2g.insert( { i, g });
204  }
205  }
206 
216  static void * message_receive(size_t msg_i, size_t total_msg, size_t total_p, size_t i, size_t ri, void * ptr)
217  {
219 
220  v->get(i).resize(msg_i / sizeof(idx_t));
221 
222  return &(v->get(i).get(0));
223  }
224 
230  {
232  size_t p_id = v_cl.getProcessUnitID();
233 
235  size_t Np = v_cl.getProcessingUnits();
236 
237  // Number of local vertex
238  size_t nl_vertex = vtxdist.get(p_id+1).id - vtxdist.get(p_id).id;
239 
241  idx_t * partition = parmetis_graph.getPartition();
242 
244  partitions.get(p_id).resize(nl_vertex);
245  std::copy(partition, partition + nl_vertex, &partitions.get(p_id).get(0));
246 
247  // Reset data structure to keep trace of new vertices distribution in processors (needed to update main graph)
248  for (size_t i = 0; i < Np; ++i)
249  {
250  v_per_proc.get(i).clear();
251  }
252 
253  // Communicate the local distribution to the other processors
254  // to reconstruct individually the global graph
258 
259  for (size_t i = 0; i < Np; i++)
260  {
261  if (i != v_cl.getProcessUnitID())
262  {
263  partitions.get(i).clear();
264  prc.add(i);
265  sz.add(nl_vertex * sizeof(idx_t));
266  ptr.add(partitions.get(p_id).getPointer());
267  }
268  }
269 
270  if (prc.size() == 0)
271  v_cl.sendrecvMultipleMessagesNBX(0, NULL, NULL, NULL, message_receive, &partitions,NONE);
272  else
273  v_cl.sendrecvMultipleMessagesNBX(prc.size(), &sz.get(0), &prc.get(0), &ptr.get(0), message_receive, &partitions,NONE);
274 
275  // Update graphs with the received data
276  updateGraphs();
277  }
278 
279 
280 public:
281 
287  :is_distributed(false),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())
288  {
289  }
290 
297  :v_cl(pm.v_cl),parmetis_graph(v_cl, v_cl.getProcessingUnits())
298  {
299  this->operator=(pm);
300  }
301 
308  :v_cl(pm.v_cl)
309  {
310  this->operator=(pm);
311  }
312 
319  {
320  size_t bc[dim];
321 
322  for (size_t i = 0 ; i < dim ; i++)
323  bc[i] = NON_PERIODIC;
324 
325  // Set grid and domain
326  gr = grid;
327  domain = dom;
328 
329  // Create a cartesian grid graph
331  gp = g_factory_part.template construct<NO_EDGE, nm_v::id, T, dim - 1, 0>(gr.getSize(), domain, bc);
333 
335  size_t Np = v_cl.getProcessingUnits();
336 
340  size_t mod_v = gr.size() % Np;
341  size_t div_v = gr.size() / Np;
342 
343  for (size_t i = 0; i <= Np; i++)
344  {
345  if (i < mod_v)
346  vtxdist.get(i).id = (div_v + 1) * i;
347  else
348  vtxdist.get(i).id = (div_v) * i + mod_v;
349  }
350 
351  // Init to 0.0 axis z (to fix in graphFactory)
352  if (dim < 3)
353  {
354  for (size_t i = 0; i < gp.getNVertex(); i++)
355  {
356  gp.vertex(i).template get<nm_v::x>()[2] = 0.0;
357  }
358  }
359  for (size_t i = 0; i < gp.getNVertex(); i++)
360  {
361  gp.vertex(i).template get<nm_v::global_id>() = i;
362  }
363 
364  }
365 
370  {
371  return gp;
372  }
373 
377  void decompose()
378  {
379  if (is_distributed == false)
381  else
383 
386 
387  // update after decomposition
389 
390  is_distributed = true;
391  }
392 
399  void refine()
400  {
401  // Reset parmetis graph and reconstruct it
403 
404  // Refine
406 
408  }
409 
416  void redecompose()
417  {
418  // Reset parmetis graph and reconstruct it
420 
421  // Refine
423 
425  }
426 
431  float getUnbalance()
432  {
433  long t_cost = 0;
434 
435  long min, max, sum;
436  float unbalance;
437 
438  t_cost = getProcessorLoad();
439 
440  min = t_cost;
441  max = t_cost;
442  sum = t_cost;
443 
444  v_cl.min(min);
445  v_cl.max(max);
446  v_cl.sum(sum);
447  v_cl.execute();
448 
449  unbalance = ((float) (max - min)) / (float) (sum / v_cl.getProcessingUnits());
450 
451  return unbalance * 100;
452  }
453 
460  void getSubSubDomainPosition(size_t id, T (&pos)[dim])
461  {
462 #ifdef SE_CLASS1
463  if (id >= gp.getNVertex())
464  std::cerr << __FILE__ << ":" << __LINE__ << "Such vertex doesn't exist (id = " << id << ", " << "total size = " << gp.getNVertex() << ")\n";
465 #endif
466 
467  // Copy the geometrical informations inside the pos vector
468  pos[0] = gp.vertex(id).template get<nm_v::x>()[0];
469  pos[1] = gp.vertex(id).template get<nm_v::x>()[1];
470  if (dim == 3)
471  pos[2] = gp.vertex(id).template get<nm_v::x>()[2];
472  }
473 
480  inline void setComputationCost(size_t id, size_t weight)
481  {
482  if (!verticesGotWeights)
483  verticesGotWeights = true;
484 
485 #ifdef SE_CLASS1
486  if (id >= gp.getNVertex())
487  std::cerr << __FILE__ << ":" << __LINE__ << "Such vertex doesn't exist (id = " << id << ", " << "total size = " << gp.getNVertex() << ")\n";
488 #endif
489 
490  // Update vertex in main graph
491  gp.vertex(id).template get<nm_v::computation>() = weight;
492  }
493 
499  {
500  return verticesGotWeights;
501  }
502 
509  {
510 #ifdef SE_CLASS1
511  if (id >= gp.getNVertex())
512  std::cerr << __FILE__ << ":" << __LINE__ << "Such vertex doesn't exist (id = " << id << ", " << "total size = " << gp.getNVertex() << ")\n";
513 #endif
514 
515  return gp.vertex(id).template get<nm_v::computation>();
516  }
517 
523  {
524  size_t load = 0;
525 
526  // Processor id
527  size_t p_id = v_cl.getProcessUnitID();
528 
529 
530  for (rid i = vtxdist.get(p_id); i < vtxdist.get(p_id+1) ; ++i)
531  load += gp.vertex(m2g.find(i)->second.id).template get<nm_v::computation>();
532 
533  //std::cout << v_cl.getProcessUnitID() << " weight " << load << " size " << sub_g.getNVertex() << "\n";
534  return load;
535  }
536 
542  void setMigrationCost(size_t id, size_t migration)
543  {
544 #ifdef SE_CLASS1
545  if (id >= gp.getNVertex())
546  std::cerr << __FILE__ << ":" << __LINE__ << "Such vertex doesn't exist (id = " << id << ", " << "total size = " << gp.getNVertex() << ")\n";
547 #endif
548 
549  gp.vertex(id).template get<nm_v::migration>() = migration;
550  }
551 
558  void setCommunicationCost(size_t v_id, size_t e, size_t communication)
559  {
560 #ifdef SE_CLASS1
561 
562  size_t e_id = v_id + e;
563 
564  if (e_id >= gp.getNEdge())
565  std::cerr << "Such edge doesn't exist (id = " << e_id << ", " << "total size = " << gp.getNEdge() << ")\n";
566 #endif
567 
568  gp.getChildEdge(v_id, e).template get<nm_e::communication>() = communication;
569  }
570 
576  size_t getNSubSubDomains() const
577  {
578  return gp.getNVertex();
579  }
580 
586  size_t getNOwnerSubSubDomains() const
587  {
588  return sub_sub_owner.size();
589  }
590 
598  size_t getOwnerSubSubDomain(size_t id) const
599  {
600  return sub_sub_owner.get(id);
601  }
602 
610  size_t getNSubSubDomainNeighbors(size_t id)
611  {
612 #ifdef SE_CLASS1
613  if (id >= gp.getNVertex())
614  std::cerr << __FILE__ << ":" << __LINE__ << "Such vertex doesn't exist (id = " << id << ", " << "total size = " << gp.getNVertex() << ")\n";
615 #endif
616 
617  return gp.getNChilds(id);
618  }
619 
625  void write(const std::string & file)
626  {
627  VTKWriter<Graph_CSR<nm_v, nm_e>, VTK_GRAPH> gv2(gp);
628  gv2.write(std::to_string(v_cl.getProcessUnitID()) + "_" + file + ".vtk");
629  }
630 
631  const ParMetisDistribution<dim,T> & operator=(const ParMetisDistribution<dim,T> & dist)
632  {
634  gr = dist.gr;
635  domain = dist.domain;
636  gp = dist.gp;
637  vtxdist = dist.vtxdist;
638  partitions = dist.partitions;
639  v_per_proc = dist.v_per_proc;
642  m2g = dist.m2g;
644 
645  return *this;
646  }
647 
649  {
650  is_distributed = dist.is_distributed;
651  v_cl = dist.v_cl;
652  gr = dist.gr;
653  domain = dist.domain;
654  gp.swap(dist.gp);
655  vtxdist.swap(dist.vtxdist);
656  partitions.swap(dist.partitions);
657  v_per_proc.swap(dist.v_per_proc);
658  verticesGotWeights = dist.verticesGotWeights;
659  sub_sub_owner.swap(dist.sub_sub_owner);
660  m2g.swap(dist.m2g);
661  parmetis_graph = dist.parmetis_graph;
662 
663  return *this;
664  }
665 
671  size_t get_ndec()
672  {
673  return parmetis_graph.get_ndec();
674  }
675 
681  void setDistTol(double tol)
682  {
684  }
685 };
686 
687 #endif /* SRC_DECOMPOSITION_PARMETISDISTRIBUTION_HPP_ */
void sum(T &num)
Sum the numbers across all processors and get the result.
void refine(openfpm::vector< rid > &vtxdist)
Refine the graph.
void createCartGraph(grid_sm< dim, void > &grid, Box< dim, T > dom)
Create the Cartesian graph.
void decompose(const openfpm::vector< rid > &vtxdist)
Decompose the graph.
Box< dim, T > domain
rectangular domain to decompose
void refine()
Refine current decomposition.
size_t getNEdge() const
Return the number of edges.
Definition: map_graph.hpp:1040
void setComputationCost(size_t id, size_t weight)
Function that set the weight of the vertex.
void postDecomposition()
It update the full decomposition.
void setMapId(rid n, gid g)
operator to remap vertex to a new position
void decompose()
Create the decomposition.
size_t getProcessUnitID()
Get the process unit id.
size_t getNOwnerSubSubDomains() const
Return the total number of sub-sub-domains this processor own.
static void * message_receive(size_t msg_i, size_t total_msg, size_t total_p, size_t i, size_t ri, void *ptr)
Callback of the sendrecv to set the size of the array received.
void execute()
Execute all the requests.
void setDistTol(real_t tol)
Distribution tolerance.
void initSubGraph(Graph &g, const openfpm::vector< rid > &vtxdist, const std::unordered_map< rid, gid > &m2g, bool w)
Set the Sub-graph.
size_t size() const
Return the size of the grid.
Definition: grid_sm.hpp:572
grid_sm< dim, void > gr
Structure that store the cartesian grid information.
ParMetisDistribution(ParMetisDistribution< dim, T > &&pm)
size_t get_ndec()
Get the decomposition counter.
gid getVertexGlobalId(rid n)
Get the global id of the vertex given the re-mapped one.
void setMigrationCost(size_t id, size_t migration)
Set migration cost of the vertex id.
auto vertexByMapId(rid id) -> decltype(gp.vertex(m2g.find(id) ->second.id))
operator to access the vertex by mapped position
float getUnbalance()
Compute the unbalance of the processor compared to the optimal balance.
void setDistTol(double tol)
Set the tolerance for each partition.
void redecompose()
Redecompose current decomposition.
Graph_CSR< nm_v, nm_e > & getGraph()
Get the current graph (main)
void getSubSubDomainPosition(size_t id, T(&pos)[dim])
function that return the position of the vertex in the space
Helper class to define Metis graph.
void redecompose(openfpm::vector< rid > &vtxdist)
Redecompose the graph.
size_t get_ndec()
Get the decomposition counter.
size_t size()
Stub size.
Definition: map_vector.hpp:70
bool verticesGotWeights
Flag to check if weights are used on vertices.
void max(T &num)
Get the maximum number across all processors (or reduction with infinity norm)
void sendrecvMultipleMessagesNBX(openfpm::vector< size_t > &prc, openfpm::vector< T > &data, openfpm::vector< size_t > prc_recv, openfpm::vector< size_t > &recv_sz, void *(*msg_alloc)(size_t, size_t, size_t, size_t, size_t, void *), void *ptr_arg, long int opt=NONE)
Send and receive multiple messages.
bool is_distributed
Is distributed.
auto getChildEdge(size_t v, size_t v_e) -> decltype(e.get(0))
Get the vertex edge.
Definition: map_graph.hpp:781
void setCommunicationCost(size_t v_id, size_t e, size_t communication)
Set communication cost of the edge id.
Implementation of VCluster class.
Definition: VCluster.hpp:36
Vcluster & v_cl
Vcluster.
openfpm::vector< openfpm::vector< idx_t > > partitions
partitions
This class construct a cartesian graph.
ParMetisDistribution(const ParMetisDistribution< dim, T > &pm)
Class that distribute sub-sub-domains across processors using ParMetis Library.
ParMetisDistribution(Vcluster &v_cl)
size_t getProcessorLoad()
Compute the processor load counting the total weights of its vertices.
void write(const std::string &file)
Print the current distribution and save it to VTK file.
void reset(Graph &g, const openfpm::vector< rid > &vtxdist, const std::unordered_map< rid, gid > &m2g, bool vgw)
Reset graph and reconstruct it.
openfpm::vector< rid > vtxdist
Init vtxdist needed for Parmetis.
size_t getNSubSubDomains() const
Returns total number of sub-sub-domains in the distribution graph.
const size_t(& getSize() const)[N]
Return the size of the grid as an array.
Definition: grid_sm.hpp:677
void initLocalToGlobalMap()
operator to init ids vector
Parmetis< Graph_CSR< nm_v, nm_e > > parmetis_graph
Convert the graph to parmetis format.
This class represent an N-dimensional box.
Definition: Box.hpp:56
size_t getSubSubDomainComputationCost(size_t id)
function that get the weight of the vertex
auto vertex(size_t id) -> decltype(v.get(id))
Function to access the vertex.
Definition: map_graph.hpp:600
std::unordered_map< rid, gid > m2g
Hashmap to access to the global position given the re-mapped one (needed for access the map) ...
size_t getNVertex() const
Return the number of the vertex.
Definition: map_graph.hpp:1029
static const unsigned int id
id property id in boost::fusion::vector
void swap(Graph_CSR< V, E > &g)
swap the memory of g with this graph
Definition: map_graph.hpp:960
size_t getNChilds(size_t c) const
Return the number of childs of a vertex.
Definition: map_graph.hpp:752
void min(T &num)
Get the minimum number across all processors (or reduction with insinity norm)
It model an expression expr1 + ... exprn.
Definition: sum.hpp:92
idx_t id
id
Definition: ids.hpp:21
Definition: ids.hpp:18
size_t getOwnerSubSubDomain(size_t id) const
Return the global id of the owned sub-sub-domain.
Definition: ids.hpp:148
openfpm::vector< size_t > sub_sub_owner
Id of the sub-sub-domain where we set the costs.
size_t getProcessingUnits()
Get the total number of processors.
openfpm::vector< openfpm::vector< gid > > v_per_proc
Init data structure to keep trace of new vertices distribution in processors (needed to update main g...
size_t getNSubSubDomainNeighbors(size_t id)
Returns total number of neighbors of the sub-sub-domain id.
idx_t * getPartition()
Get graph partition vector.
bool weightsAreUsed()
Checks if weights are used on the vertices.
Graph_CSR< nm_v, nm_e > gp
Global sub-sub-domain graph.
void updateGraphs()
Update main graph ad subgraph with the received data of the partitions from the other processors...