openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
SF_partitioning.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) NumeriCor GmbH
2 // SPDX-License-Identifier: Apache-2.0
3 
12 #ifndef _SF_PARTITIONING_H
13 #define _SF_PARTITIONING_H
14 
15 #define KDPART_MPI
16 #include "kdpart.hpp"
17 #include "SF_container.h"
18 #include "SF_vector.h"
19 
20 namespace SF {
21 
28 template<class T, class S>
30 {
31 public:
32  virtual void operator()(const meshdata<T, S> & mesh, vector<T> & part) = 0;
33 };
34 
35 #ifdef WITH_PARMETIS
36 
37 #include "parmetis.h"
38 
42 template<class T, class S>
43 class parmetis_partitioner : public abstract_partitioner<T, S>
44 {
45 private:
46  // this are the options configurable by the user
47  float _unbalance;
48  short _ncommon;
49 
50 public:
52  parmetis_partitioner() : _unbalance(1.01f), _ncommon(2)
53  {}
55  parmetis_partitioner(float ub, short nc) : _unbalance(ub), _ncommon(nc)
56  {}
57 
65  inline void operator()(const meshdata<T, S> & mesh, vector<T> & part)
66  {
67  MPI_Comm comm = mesh.comm;
68 
69  int rank, size;
70  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
71 
72  part.resize(mesh.l_numelem);
73  // special treatment of sequential case since parmetis is rather slow
74  if(size == 1) {
75  part.zero();
76  return;
77  }
78 
79  // communicate the distribution of the elements
80  vector<idx_t> elemdist_cnt(size), elemdist_dsp(size+1);
81  idx_t numelem = mesh.l_numelem; // we need a type conversion to int
82  MPI_Allgather(&numelem, sizeof(idx_t), MPI_BYTE, elemdist_cnt.data(), sizeof(idx_t), MPI_BYTE, comm);
83  dsp_from_cnt(elemdist_cnt, elemdist_dsp);
84 
85  idx_t nparts = size; // number of partitions
86  idx_t ncommonnod = _ncommon; // number of common nodes defining the neighbourhood between 2 elems
87  idx_t ncon = 1; // number of constraints to satisfy (we always use 1)
88  idx_t wgtflag = 0; // number of weights for graph edges (unused, thus 0)
89  idx_t* elemwgt = NULL;
90  idx_t numflag = 0; // indexing is 0 based
91 
92  vector<real_t> tpwgts(nparts*ncon); // weights for nodes
93  for(size_t i=0; i<tpwgts.size(); i++) tpwgts[i] = (float)1. / (float)(nparts*ncon);
94 
95  vector<real_t> ubvec(ncon);
96  for (int i = 0; i < ncon; i++ ) ubvec[i] = _unbalance;
97 
98  vector<idx_t> opt(4, 0); // options are unused
99 
100  // we need to convert the mesh to idx_t
101  vector<idx_t> eptr(mesh.dsp.size()), eind(mesh.con.size());
102  vec_assign(eptr.data(), mesh.dsp.data(), eptr.size());
103 
104  const vector<T> & rnod = mesh.get_numbering(NBR_REF);
105  for(size_t i=0; i<mesh.con.size(); i++) eind[i] = rnod[mesh.con[i]];
106 
107  vector<idx_t> pm_part(mesh.l_numelem);
108 
109  idx_t n_edgecut;
110  ParMETIS_V3_PartMeshKway(elemdist_dsp.data(),
111  eptr.data(),
112  eind.data(),
113  elemwgt,
114  &wgtflag,
115  &numflag,
116  &ncon,
117  &ncommonnod,
118  &nparts,
119  tpwgts.data(),
120  ubvec.data(),
121  opt.data(),
122  &n_edgecut,
123  pm_part.data(),
124  &comm);
125 
126  vec_assign(part.data(), pm_part.data(), part.size());
127 
128  }
129 };
130 
131 #endif
132 
133 template<class T, class S>
135 {
136  public:
137  void operator() (const meshdata<T,S> & mesh, vector<T> & part_vec)
138  {
139  int size; MPI_Comm_size(mesh.comm, &size);
140 
141  part_vec.resize(mesh.l_numelem);
142  // special treatment of sequential case
143  if(size == 1) {
144  part_vec.zero();
145  return;
146  }
147 
148  std::vector<S> ctr(mesh.l_numelem*3);
149  for(size_t i=0; i<mesh.l_numelem; i++) {
150  kdpart::vec3<S> avrg;
151  T dsp = mesh.dsp[i];
152  T elemsize = mesh.dsp[i+1] - dsp;
153 
154  for(T j=0; j<elemsize; j++) {
155  T v = mesh.con[dsp+j];
156  avrg.x += mesh.xyz[v*3+0];
157  avrg.y += mesh.xyz[v*3+1];
158  avrg.z += mesh.xyz[v*3+2];
159  }
160  avrg.x /= S(elemsize);
161  avrg.y /= S(elemsize);
162  avrg.z /= S(elemsize);
163 
164  ctr[i*3+0] = avrg.x;
165  ctr[i*3+1] = avrg.y;
166  ctr[i*3+2] = avrg.z;
167  }
168 
169  std::vector<T> part;
171  partitioner(mesh.comm, ctr, size, part);
172 
173  part_vec.assign(part.begin(), part.end());
174  }
175 };
176 
177 }
178 
179 #endif
Basic containers.
The vector class and related algorithms.
Abstract base class for a mesh partitioner.
virtual void operator()(const meshdata< T, S > &mesh, vector< T > &part)=0
void operator()(const meshdata< T, S > &mesh, vector< T > &part_vec)
The mesh storage class. It contains both element and vertex data.
Definition: SF_container.h:381
vector< T > dsp
connectivity starting index of each element
Definition: SF_container.h:401
size_t l_numelem
local number of elements
Definition: SF_container.h:384
vector< T > con
Definition: SF_container.h:397
vector< S > xyz
node cooridnates
Definition: SF_container.h:412
MPI_Comm comm
the parallel mesh is defined on a MPI world
Definition: SF_container.h:389
A vector storing arbitrary data.
Definition: SF_vector.h:28
void resize(size_t n)
Resize a vector.
Definition: SF_vector.h:194
void assign(InputIterator s, InputIterator e)
Assign a memory range.
Definition: SF_vector.h:146
void zero()
Definition: SF_vector.h:233
kdtree based partitioning classes.
Definition: dense_mat.hpp:19
void dsp_from_cnt(const vector< T > &cnt, vector< T > &dsp)
Compute displacements from counts.
Definition: SF_vector.h:295
void vec_assign(S *lhs, const V *rhs, size_t size)
Assign the values in rhs to lhs. The data-type of rhs is cast to the type of lhs.
Definition: SF_vector.h:356
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:186
minimalistic internal point struct
Definition: kdpart.hpp:29