openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
SF_parallel_layout.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_PARALLEL_LAYOUT_H
13 #define _SF_PARALLEL_LAYOUT_H
14 
15 
16 #include <cassert>
17 #include <iostream>
18 
19 #include "hashmap.hpp"
20 
21 #include "mpi_utils.h"
22 #include "SF_vector.h"
23 #include "SF_container.h"
24 
25 namespace SF {
26 
43 template<class T>
44 inline void parallel_distribution(const vector<T> & gtarget,
45  const vector<T> & cnt,
46  const vector<T> & dsp,
47  const vector<T> & ranks,
48  const int myrank,
49  vector<T> & owner,
50  vector<T> & counts)
51 {
52  int size = gtarget.size();
53  size_t csize = cnt.size();
54  vector<T> ltarget(size);
55 
56  // this small algorithm computes ltarget[i] = gtarget[i] / size,
57  // but with a even distribution of the divisions remainder
58  // among the processes
59  for(int i=0; i<size; i++) {
60  int idx = (i + myrank) % size;
61  T gt = gtarget[idx];
62  ltarget[idx] = (i * gt + gt) / size - (i * gt) / size;
63  }
64 
65  // set initial distribution
66  vector<T> act_idx(csize);
67  counts.resize(size); counts.zero();
68  owner.resize(csize);
69  for(size_t nidx=0; nidx < csize; nidx++)
70  {
71  act_idx[nidx] = (unsigned int)(2147483647 * nidx) % cnt[nidx];
72 
73  T j = dsp[nidx] + act_idx[nidx];
74  T p = ranks[j];
75  owner[nidx] = p;
76  counts[p]++;
77  }
78 
79  // compute initial functional value
80  unsigned int J = 0;
81  for(int i = 0; i < size; i++) J += (counts[i] - ltarget[i])*(counts[i] - ltarget[i]);
82 
83  unsigned int k = 0, update = 0, osteps = 32;
84 
85  // optimize
86  while(J > 0 && k++ < osteps)
87  {
88  for(size_t nidx = 0; nidx < csize; nidx++)
89  {
90  // round-robin incrementation
91  act_idx[nidx]++;
92  if(act_idx[nidx] == cnt[nidx]) act_idx[nidx] = 0;
93 
94  T j = dsp[nidx] + act_idx[nidx];
95  T op = owner[nidx];
96  T p = ranks[j];
97  T n0 = counts[op], n1 = counts[p];
98  T n0_ = ltarget[op], n1_ = ltarget[p];
99  if( ((n0 - n0_) - (n1 - n1_)) >= 1 )
100  {
101  owner[nidx] = p;
102  counts[op]--; counts[p]++;
103  update++;
104  J += 2*(1 - (n0 - n0_) + (n1 - n1_));
105 
106  if (J == 0) break;
107  }
108  }
109  }
110 }
111 
112 
125 template<class T>
126 inline void refine_distribution(const vector<T> & gtarget,
127  const vector<T> & cnt,
128  const vector<T> & dsp,
129  const vector<T> & ranks,
130  const int myrank,
131  vector<T> & owner,
132  vector<T> & counts)
133 {
134  int size = gtarget.size();
135  size_t csize = cnt.size();
136  vector<T> ltarget(size);
137 
138  // this small algorithm computes ltarget[i] = gtarget[i] / size,
139  // but with a even distribution of the divisions remainder
140  // among the processes
141  for(int i=0; i<size; i++) {
142  int idx = (i + myrank) % size;
143  T gt = gtarget[idx];
144  ltarget[idx] = (i * gt + gt) / size - (i * gt) / size;
145  }
146 
147  vector<T> act_idx(csize);
148  counts.resize(size); counts.zero();
149 
150  // compute initial functional value
151  unsigned int J = 0;
152  for(int i = 0; i < size; i++) J += (counts[i] - ltarget[i])*(counts[i] - ltarget[i]);
153 
154  unsigned int k = 0, update = 0, osteps = 64;
155 
156  // choose random starting index
157  for(size_t nidx=0; nidx < csize; nidx++)
158  act_idx[nidx] = (unsigned int)(2147483647 * nidx) % cnt[nidx];
159 
160  // optimize
161  while(J > 0 && k++ < osteps)
162  {
163  for(size_t nidx = 0; nidx < csize; nidx++)
164  {
165  // round-robin incrementation
166  act_idx[nidx]++;
167  if(act_idx[nidx] == cnt[nidx]) act_idx[nidx] = 0;
168 
169  T j = dsp[nidx] + act_idx[nidx];
170  T op = owner[nidx];
171  T p = ranks[j];
172  T n0 = counts[op], n1 = counts[p];
173  T n0_ = ltarget[op], n1_ = ltarget[p];
174  if( ((n0 - n0_) - (n1 - n1_)) >= 1 )
175  {
176  owner[nidx] = p;
177  counts[op]--; counts[p]++;
178  update++;
179  J += 2*(1 - (n0 - n0_) + (n1 - n1_));
180 
181  if (J == 0) break;
182  }
183  }
184  }
185 }
186 
187 template<class T>
188 inline void parallel_distribution_minrank(const vector<T> & gtarget,
189  const vector<T> & cnt,
190  const vector<T> & dsp,
191  const vector<T> & ranks,
192  vector<T> & owner,
193  vector<T> & counts)
194 {
195  owner.resize(cnt.size());
196  counts.assign(gtarget.size(), 0);
197 
198  for(size_t i = 0; i < owner.size(); i++)
199  {
200  // get the smallest rank index that holds current node
201  int minrank = ranks[dsp[i]];
202  for(T j = dsp[i]; j < dsp[i+1]; j++)
203  if(minrank > ranks[j]) minrank = ranks[j];
204 
205  // this rank gets assigned to this node
206  owner[i] = minrank;
207  counts[minrank]++;
208  }
209 }
210 
211 
212 
221 template<class T>
223 {
224  protected:
229 
230  public:
241  inline void globalize(vector<T> & lvec) const
242  {
243  size_t lsize = _l2g.size(), widx = 0;
244 
245  for(size_t ridx=0; ridx<lvec.size(); ridx++)
246  {
247  T loc = lvec[ridx];
248  if(loc < (T)lsize)
249  lvec[widx++] = _l2g[loc];
250  }
251  lvec.resize(widx);
252  }
263  inline T globalize(const T lidx) const
264  {
265  size_t lsize = _l2g.size();
266 
267  if(lidx < (T)lsize)
268  return _l2g[lidx];
269  else
270  return T(-1);
271  }
272 
273 
284  inline void localize(vector<T> & gvec) const
285  {
286  size_t widx = 0;
288 
289  for(size_t ridx=0; ridx<gvec.size(); ridx++)
290  {
291  T glob = gvec[ridx];
292  it = _g2l.find(glob);
293  if(it != _g2l.end())
294  gvec[widx++] = it->second;
295  }
296  gvec.resize(widx);
297  }
298 
312  template<class V>
313  inline void localize(vector<T> & gidx, vector<V> & gdat) const
314  {
315  size_t widx = 0;
317 
318  for(size_t ridx=0; ridx<gidx.size(); ridx++)
319  {
320  T glob = gidx[ridx];
321  it = _g2l.find(glob);
322  if(it != _g2l.end()) {
323  gidx[widx] = it->second;
324  gdat[widx] = gdat[ridx];
325  widx++;
326  }
327  }
328  gidx.resize(widx);
329  gdat.resize(widx);
330  }
331 
332  inline T localize(T gidx) const
333  {
334  auto it = _g2l.find(gidx);
335  if(it != _g2l.end())
336  return it->second;
337  else
338  return T(-1);
339  }
340 
346  inline void assign(const vector<T> & idx)
347  {
348  _l2g.assign(idx.begin(), idx.end());
349  _g2l.clear();
350 
351  for(size_t i=0; i<_l2g.size(); i++)
352  _g2l[_l2g[i]] = i;
353  }
354 
355 };
356 
357 
362 template<class T>
364 {
365  private:
367  vector<T> _inod;
369  size_t _glob_num_idx;
370 
372  vector<T> _alg_nod;
374  vector<T> _alg_layout;
376  vector<T> _layout;
378  MPI_Comm _comm;
379 
387  inline void find_domain_interfaces()
388  {
389  int size, rank;
390  MPI_Comm_size(_comm, &size); MPI_Comm_rank(_comm, &rank);
391 
392  // compute a destination for each index in local domain
394  for(size_t i=0; i<parallel_layout<T>::_l2g.size(); i++)
395  dest[i] = parallel_layout<T>::_l2g[i] % size;
396  binary_sort_copy(dest, sbuf);
397 
398  // set up a commgraph w.r.t. the destination
399  commgraph<size_t> grph;
400  grph.configure(dest, _comm);
401  size_t numrecv = sum(grph.rcnt);
402 
403  // allocate receiving datastructs
404  vector<T> rnod(numrecv); // holds the received node indices
405  vector<T> rproc(numrecv); // holds the process rank each index was received from
406  vector<T> acc_cnt(numrecv, 1), acc_dsp;
407  grph.source_ranks(rproc);
408 
409  MPI_Exchange(grph, sbuf, rnod, _comm);
410 
411  binary_sort_copy(rnod, rproc);
412  unique_accumulate(rnod, acc_cnt);
413  acc_dsp.resize(acc_cnt.size()+1); dsp_from_cnt(acc_cnt, acc_dsp);
414 
415  // compute the global number of entities
416  unsigned long int num_unique = acc_cnt.size(), gnum_unique;
417  MPI_Allreduce(&num_unique, &gnum_unique, 1, MPI_UNSIGNED_LONG, MPI_SUM, _comm);
418  _glob_num_idx = gnum_unique;
419 
420  // now for each unique node rnod[i] we know the multiplicity acc_cnt[i],
421  // and the process ranks associated to it in rproc[acc_dsp[i]] till rproc[acc_dsp[i+1]]
422 
423  // compute commgraph for those entities with acc_cnt[i] > 1
424  grph.scnt.zero();
425  for(size_t i=0; i<acc_cnt.size(); i++) {
426  if(acc_cnt[i] > 1) {
427  for(T j=acc_dsp[i]; j<acc_dsp[i+1]; j++)
428  grph.scnt[rproc[j]]++;
429  }
430  }
431  dsp_from_cnt(grph.scnt, grph.sdsp);
432  MPI_Alltoall(grph.scnt.data(), sizeof(size_t), MPI_BYTE, grph.rcnt.data(), sizeof(size_t), MPI_BYTE, _comm);
433  dsp_from_cnt(grph.rcnt, grph.rdsp);
434 
435  // fill sbuf with those entities with acc_cnt[i] > 1
436  size_t numsend = sum(grph.scnt);
437  sbuf.resize(numsend);
438 
439  for(size_t i=0; i<acc_cnt.size(); i++) {
440  if(acc_cnt[i] > 1) {
441  for(T j=acc_dsp[i]; j<acc_dsp[i+1]; j++) {
442  T sproc = rproc[j];
443  sbuf[grph.sdsp[sproc]] = rnod[i];
444  grph.sdsp[sproc]++;
445  }
446  }
447  }
448 
449  dsp_from_cnt(grph.scnt, grph.sdsp);
450  numrecv = sum(grph.rcnt);
451  _inod.resize(numrecv);
452  MPI_Exchange(grph, sbuf, _inod, _comm);
453  binary_sort(_inod);
454  }
455 
468  inline void find_algebraic_layout()
469  {
470  // whether we want to be verbose about suboptimal algebraic node distributions. from
471  // a high-level view this is unimportant, so by default this is false.
472  const bool dist_warnings = false;
473 
474  int size, rank;
475  MPI_Comm_size(_comm, &size); MPI_Comm_rank(_comm, &rank);
476 
477  // first compute a unique ownership of a subset of the nodes in the local domain
478  {
479  // compute inner nodes via a set difference of all nodes and the interface nodes
480  vector<T> inner_nodes(parallel_layout<T>::_l2g.size());
481  {
482  vector<T> nodes(parallel_layout<T>::_l2g), intf_nodes(_inod);
483  binary_sort(nodes);
484  T* end = std::set_difference(nodes.begin(), nodes.end(),
485  intf_nodes.begin(), intf_nodes.end(), inner_nodes.begin());
486  inner_nodes.resize(end - inner_nodes.begin());
487  }
488  _alg_nod.assign(inner_nodes.begin(), inner_nodes.end());
489 
490  // compute a destination for each interface index in the local domain
491  vector<T> sbuf(_inod.size()), dest(_inod.size());
492  for(size_t i=0; i < sbuf.size(); i++) {
493  T nod = _inod[i];
494  dest[i] = nod % size;
495  sbuf[i] = nod;
496  }
497  binary_sort_copy(dest, sbuf);
498 
499  // set up a commgraph w.r.t. the destination
500  commgraph<size_t> grph;
501  grph.configure(dest, _comm);
502 
503  // allocate receiving datastructs
504  size_t numrecv = sum(grph.rcnt);
505  vector<T> rnod(numrecv); // holds the received node indices
506  vector<T> rproc(numrecv); // holds the process rank each index was received from
507 
508  grph.source_ranks(rproc);
509  MPI_Exchange(grph, sbuf, rnod, _comm);
510 
511  vector<T> acc_cnt(numrecv, 1), acc_dsp;
512  binary_sort_copy(rnod, rproc);
513  unique_accumulate(rnod, acc_cnt);
514  acc_dsp.resize(acc_cnt.size()+1); dsp_from_cnt(acc_cnt, acc_dsp);
515 
516  const MPI_Datatype mpi_t = opencarp::mpi_datatype<T>();
517 
518  // initialize the target distribution to approx. _glob_num_idx / size
519  vector<T> target;
520  divide(_glob_num_idx, size, target);
521 
522  target[rank] -= inner_nodes.size();
523  MPI_Allreduce(MPI_IN_PLACE, target.data(), target.size(), mpi_t, MPI_MIN, _comm);
524 
525  if(dist_warnings) {
526  // treat negative values in target with warning.
527  bool warn = false;
528  for(int i=0; i<size; i++)
529  if(target[i] < 0) {
530  warn = true;
531  break;
532  }
533  if(warn)
534  if(!rank) std::cerr << "Warning: Domains too unbalanced for balanced re-indexing!" << std::endl;
535  }
536 
537  vector<T> owners, counts;
538 
539  #if 1
540  // use optimization algorithm to find a parallel distribution that fits the specified
541  // target distribution
542  parallel_distribution(target, acc_cnt, acc_dsp, rproc, rank, owners, counts);
543  MPI_Allreduce(MPI_IN_PLACE, counts.data(), counts.size(), mpi_t, MPI_SUM, _comm);
544  for(int i=0; i<size; i++) target[i] -= counts[i];
545 
546  unsigned int k = 0, numref = 10;
547  while( !isEmpty(target) && k++ < numref)
548  {
549  refine_distribution(target, acc_cnt, acc_dsp, rproc, rank, owners, counts);
550  MPI_Allreduce(MPI_IN_PLACE, counts.data(), counts.size(), mpi_t, MPI_SUM, _comm);
551  for(int i=0; i<size; i++) target[i] -= counts[i];
552  }
553  #else
554  parallel_distribution_minrank(target, acc_cnt, acc_dsp, rproc, owners, counts);
555  MPI_Allreduce(MPI_IN_PLACE, counts.data(), counts.size(), mpi_t, MPI_SUM, _comm);
556  for(int i=0; i<size; i++) target[i] -= counts[i];
557  #endif
558 
559  if( dist_warnings && rank == 0 ) {
560  if(!isEmpty(target)) {
561  std::cerr << "Warning: Balanced re-indexing could not be computed." << std::endl;
562  std::cerr << "Final differences to even distribution: " << std::endl;
563  for(int i=0; i<size; i++) std::cerr << target[i] << " ";
564  std::cerr << std::endl;
565  }
566  }
567 
568  // send the assigned nodes back to the respective processes
569  binary_sort_copy(owners, rnod);
570  grph.configure(owners, _comm);
571  numrecv = sum(grph.rcnt);
572  sbuf.resize(numrecv);
573  MPI_Exchange(grph, rnod, sbuf, _comm);
574 
575  _alg_nod.append(sbuf.begin(), sbuf.end());
576  binary_sort(_alg_nod);
577  }
578 
579  // Generate the algebraic layout
580  {
581  commgraph<size_t> owned_layout;
582  owned_layout.resize(size);
583  size_t owned_idx_size = _alg_nod.size();
584  MPI_Allgather(&owned_idx_size, sizeof(size_t), MPI_BYTE,
585  owned_layout.scnt.data(), sizeof(size_t), MPI_BYTE, _comm);
586  dsp_from_cnt(owned_layout.scnt, owned_layout.sdsp);
587 
588  _alg_layout.resize(owned_layout.sdsp.size());
589  vec_assign(_alg_layout.data(), owned_layout.sdsp.data(), owned_layout.sdsp.size());
590  }
591  }
592 
593  inline void compute_layout()
594  {
595  T size = this->algebraic_layout().size()-1;
596  T nlidx = this->num_local_idx();
597 
598  _layout.resize(size + 1);
599  vector<T> cnt(size);
600 
601  MPI_Allgather(&nlidx, sizeof(T), MPI_BYTE, cnt.data(), sizeof(T), MPI_BYTE, _comm);
602  dsp_from_cnt(cnt, _layout);
603  }
604 
605  public:
607  overlapping_layout() : _glob_num_idx(0), _comm(SF_COMM) {}
608 
619  inline void assign(const vector<T> & idx, MPI_Comm comm)
620  {
621  _comm = comm;
622 
624 
625  this->find_domain_interfaces();
626  this->find_algebraic_layout();
627  this->compute_layout();
628 
629  this->localize(_inod);
630  binary_sort(_inod);
631  this->localize(_alg_nod);
632  binary_sort(_alg_nod);
633  }
634 
635 
644  inline const vector<T>& interface() const
645  {
646  return _inod;
647  }
648 
659  inline const vector<T> & algebraic_nodes() const
660  {
661  return _alg_nod;
662  }
663 
674  inline void permute_algebraic_nodes(const vector<T> & perm_nod)
675  {
676  assert(perm_nod.size() == _alg_nod.size());
677  _alg_nod.assign(perm_nod.begin(), perm_nod.end());
678  }
679 
685  inline const vector<T> & algebraic_layout() const
686  {
687  return _alg_layout;
688  }
694  inline const vector<T> & layout() const
695  {
696  return _layout;
697  }
698 
709  inline T localize_algebraic(const T global_idx, const vector<T> & global_alg_nbr, const int rank)
710  {
711  T loc_nodal_idx = parallel_layout<T>::localize(global_idx);
712  if(loc_nodal_idx == -1)
713  return -1;
714 
715  T local_offset = _alg_layout[rank], local_size = _alg_layout[rank+1] - local_offset;
716  T local_alg_idx = global_alg_nbr[loc_nodal_idx] - local_offset;
717 
718  if(local_alg_idx > -1 && local_alg_idx < local_size)
719  return local_alg_idx;
720  else
721  return -1;
722  }
723 
725  inline size_t num_global_idx() const
726  {
727  return _glob_num_idx;
728  }
730  inline size_t num_local_idx() const
731  {
732  return parallel_layout<T>::_l2g.size();
733  }
735  inline size_t num_algebraic_idx() const
736  {
737  return _alg_nod.size();
738  }
739 
748  template<class V>
749  inline void reduce(vector<V> & ndat, const char* op) const
750  {
751  int size, rank;
752  MPI_Comm_size(_comm, &size); MPI_Comm_rank(_comm, &rank);
753 
754  // parallel layout of nodal data must match the one of the
755  // overlapping_layout class
756  assert(ndat.size() == parallel_layout<T>::_l2g.size());
757 
758  size_t isize = _inod.size();
759  vector<T> nod_sbuf(isize), dest(isize), perm;
760  vector<V> dat_sbuf(isize);
761 
762  for(size_t i=0; i<isize; i++) {
763  T lidx = _inod[i];
764  dest[i] = parallel_layout<T>::_l2g[lidx] % size;
765  }
766  interval(perm, 0, isize);
767  binary_sort_copy(dest, perm);
768 
769  for(size_t i=0; i<perm.size(); i++)
770  {
771  T lidx = _inod[perm[i]];
772  nod_sbuf[i] = parallel_layout<T>::_l2g[lidx];
773  dat_sbuf[i] = ndat[lidx];
774  }
775 
776  // set up a commgraph w.r.t. the destination
777  commgraph<size_t> grph;
778  grph.configure(dest, _comm);
779 
780  size_t numrecv = sum(grph.rcnt);
781  vector<T> nod_rbuf(numrecv);
782  vector<V> dat_rbuf(numrecv);
783 
784  MPI_Exchange(grph, nod_sbuf, nod_rbuf, _comm);
785  MPI_Exchange(grph, dat_sbuf, dat_rbuf, _comm);
786 
787  vector<T> acc_cnt(numrecv, 1), acc_dsp, acc_col;
788  interval(acc_col, 0, numrecv);
789 
790  binary_sort_copy(nod_rbuf, acc_col);
791  unique_accumulate(nod_rbuf, acc_cnt);
792 
793  acc_dsp.resize(acc_cnt.size() + 1);
794  dsp_from_cnt(acc_cnt, acc_dsp);
795 
796  short op_sw = 0;
797  if(! strcmp(op, "max"))
798  op_sw = 0;
799  else if(! strcmp(op, "min"))
800  op_sw = 1;
801  else if(! strcmp(op, "sum"))
802  op_sw = 2;
803  else
804  assert(0);
805 
806  for(size_t i=0; i<acc_cnt.size(); i++)
807  {
808  V val = 0.0; // value we will set for all interface nodes
809  // compute value based on selected operation
810  switch(op_sw)
811  {
812  // max
813  case 0:
814  {
815  V max = dat_rbuf[acc_col[acc_dsp[i]]];
816  T idx=1;
817  while(idx < acc_cnt[i])
818  {
819  T p = acc_col[acc_dsp[i]+idx];
820  if(max < dat_rbuf[p]) max = dat_rbuf[p];
821  idx++;
822  }
823  val = max;
824  break;
825  }
826 
827  // min
828  case 1:
829  {
830  V min = dat_rbuf[acc_col[acc_dsp[i]]];
831  T idx=1;
832  while(idx < acc_cnt[i])
833  {
834  T p = acc_col[acc_dsp[i]+idx];
835  if(min > dat_rbuf[p]) min = dat_rbuf[p];
836  idx++;
837  }
838  val = min;
839  break;
840  }
841 
842  // sum
843  case 2:
844  {
845  T idx=0;
846  while(idx < acc_cnt[i])
847  {
848  T p = acc_col[acc_dsp[i]+idx];
849  val += dat_rbuf[p];
850  idx++;
851  }
852  break;
853  }
854  }
855  for(T j = acc_dsp[i]; j < acc_dsp[i] + acc_cnt[i]; j++)
856  dat_rbuf[acc_col[j]] = val;
857  }
858 
859  grph.transpose();
860  MPI_Exchange(grph, dat_rbuf, dat_sbuf, _comm);
861 
862  for(size_t i=0; i<perm.size(); i++)
863  {
864  T lidx = _inod[perm[i]];
865  ndat[lidx] = dat_sbuf[i];
866  }
867  }
868 };
869 
875 template<class T>
877 {
878  private:
879  vector<T> _elem_layout;
880  MPI_Comm _comm;
881 
882  public:
885  {}
886 
895  inline void assign(const vector<T> & ref_eidx, MPI_Comm comm)
896  {
897  _comm = comm;
898 
899  parallel_layout<T>::assign(ref_eidx);
900 
901  int size; MPI_Comm_size(_comm, &size);
902 
903  T lsize = ref_eidx.size();
904  vector<T> layout_cnt(size);
905 
906  MPI_Allgather(&lsize, sizeof(T), MPI_BYTE, layout_cnt.data(), sizeof(T), MPI_BYTE, _comm);
907  dsp_from_cnt(layout_cnt, _elem_layout);
908  }
910  const vector<T> & algebraic_layout() const
911  {
912  return _elem_layout;
913  }
914 };
915 
916 
917 
921 template<class T, class S>
922 T local_nodal_to_local_petsc(const meshdata<T,S> & mesh, int rank, T local_nodal)
923 {
924  const vector<T> & alg_layout = mesh.pl.algebraic_layout();
925  const vector<T> & petsc_nbr = mesh.get_numbering(NBR_PETSC);
926 
927  const T my_offset = alg_layout[rank];
928 
929  return petsc_nbr[local_nodal] - my_offset;
930 }
931 
934 template<class T, class S>
935 T local_petsc_to_local_nodal(const meshdata<T,S> & mesh, int rank, T local_petsc)
936 {
937  const vector<T> & alg_layout = mesh.pl.algebraic_layout();
938  const vector<T> & alg_nod = mesh.pl.algebraic_nodes();
939  const vector<T> & petsc_nbr = mesh.get_numbering(NBR_PETSC);
940 
941  const T my_offset = alg_layout[rank];
942  const size_t num_alg = alg_nod.size();
943 
944  size_t idx = 0;
945  while(idx < num_alg && petsc_nbr[alg_nod[idx]] != local_petsc + my_offset) idx++;
946 
947  if(idx == num_alg) return -1;
948  else return alg_nod[idx];
949 }
950 
951 template<class T, class S>
952 void local_petsc_to_nodal_mapping(const meshdata<T,S> & mesh, index_mapping<T> & petsc_to_nodal)
953 {
954  int rank; MPI_Comm_rank(mesh.comm, &rank);
955  const vector<T> & alg_nod = mesh.pl.algebraic_nodes();
956  vector<T> petsc_idx(alg_nod.size());
957 
958  size_t idx = 0;
959  for(const T & n : alg_nod)
960  petsc_idx[idx++] = local_nodal_to_local_petsc(mesh, rank, n);
961 
962  petsc_to_nodal.assign(petsc_idx, alg_nod);
963 }
964 
965 
966 }
967 
968 #endif
Basic containers.
#define SF_COMM
the default SlimFem MPI communicator
Definition: SF_globals.h:13
The vector class and related algorithms.
The class holds the communication graph for a MPI_Exchange() call.
Definition: SF_container.h:623
vector< T > rcnt
Number of elements received from each rank.
Definition: SF_container.h:627
vector< T > scnt
Number of elements sent to each rank.
Definition: SF_container.h:625
void resize(size_t size)
Resize all vectors to size.
Definition: SF_container.h:631
void configure(const vector< V > &dest, MPI_Comm comm)
Set up the communication graph.
Definition: SF_container.h:664
vector< T > sdsp
Displacements w.r.t. scnt.
Definition: SF_container.h:626
vector< T > rdsp
Displacements w.r.t. rcnt.
Definition: SF_container.h:628
void source_ranks(vector< V > &source)
For every received data element, get the rank indices it was receive from.
Definition: SF_container.h:711
void transpose()
transpose comm graph (receive becomes send, and vice versa)
Definition: SF_container.h:648
Index mapping class. This is a bijective mapping.
Definition: SF_container.h:205
void assign(const vector< T > &a, const vector< T > &b)
Set up the index mapping between a and b.
Definition: SF_container.h:232
The mesh storage class. It contains both element and vertex data.
Definition: SF_container.h:381
overlapping_layout< T > pl
nodal parallel layout
Definition: SF_container.h:414
MPI_Comm comm
the parallel mesh is defined on a MPI world
Definition: SF_container.h:389
vector< T > & get_numbering(SF_nbr nbr_type)
Get the vector defining a certain numbering.
Definition: SF_container.h:449
The parallel layout of non overlapping indices.
const vector< T > & algebraic_layout() const
Getter function for the algebraic layout of the elements.
non_overlapping_layout()
Empty constructor. Use assign() to set up the layout.
void assign(const vector< T > &ref_eidx, MPI_Comm comm)
Generate the layout.
The overlapping_layout class contains the algorithms related to managing overlapping parallel index s...
void permute_algebraic_nodes(const vector< T > &perm_nod)
Reorder the local algebraic node set.
T localize_algebraic(const T global_idx, const vector< T > &global_alg_nbr, const int rank)
map a global (REF_NBR, reference numbering) index to local algebraic (non-overlapping) indexing w....
const vector< T > & algebraic_layout() const
Getter function for the global algebraic node layout.
size_t num_algebraic_idx() const
Retrieve the number of local algebraic indices.
size_t num_local_idx() const
Retrieve the local number of indices.
const vector< T > & layout() const
Return the the overlapping layout.
const vector< T > & algebraic_nodes() const
Getter function for the local indices forming the local algebraic node set.
size_t num_global_idx() const
Retrieve the global number of indices.
void assign(const vector< T > &idx, MPI_Comm comm)
Initialization function.
const vector< T > & interface() const
Retrieve the local indices of the subdomain interfaces.
void reduce(vector< V > &ndat, const char *op) const
Compute a reduction on overlapping data.
overlapping_layout()
Non-parameterized constructor. Use assign() to initialize later.
The base class for parallel layouts.
T localize(T gidx) const
void assign(const vector< T > &idx)
Assign a parallel distributed index set that defines the parallel layout.
hashmap::unordered_map< T, T > _g2l
The global-to-local map for the DD domain.
void localize(vector< T > &gidx, vector< V > &gdat) const
Localize global indices and associated data.
vector< T > _l2g
The global indices of the local DD domain. Also serves as the local-to-global map.
void globalize(vector< T > &lvec) const
Globalize local indices.
T globalize(const T lidx) const
Globalize local indices.
void localize(vector< T > &gvec) const
Localize global indices.
A vector storing arbitrary data.
Definition: SF_vector.h:28
size_t size() const
The current size of the vector.
Definition: SF_vector.h:89
void resize(size_t n)
Resize a vector.
Definition: SF_vector.h:194
const T * end() const
Pointer to the vector's end.
Definition: SF_vector.h:113
void append(InputIterator s, InputIterator e)
Append data to the current data chunk.
Definition: SF_vector.h:253
void assign(InputIterator s, InputIterator e)
Assign a memory range.
Definition: SF_vector.h:146
void zero()
Definition: SF_vector.h:233
const T * begin() const
Pointer to the vector's start.
Definition: SF_vector.h:101
T * data()
Pointer to the vector's start.
Definition: SF_vector.h:76
iterator find(const K &key)
Search for key. Return iterator.
Definition: hashmap.hpp:626
Classes similar to unordered_set and unordered_map, but with better performance.
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
T local_petsc_to_local_nodal(const meshdata< T, S > &mesh, int rank, T local_petsc)
void interval(vector< T > &vec, size_t start, size_t end)
Create an integer interval between start and end.
Definition: SF_vector.h:335
void unique_accumulate(vector< T > &_P, vector< S > &_A)
Definition: SF_sort.h:399
void binary_sort_copy(vector< T > &_V, vector< S > &_W)
Definition: SF_sort.h:286
T sum(const vector< T > &vec)
Compute sum of a vector's entries.
Definition: SF_vector.h:325
void divide(const size_t gsize, const size_t num_parts, vector< T > &loc_sizes)
divide gsize into num_parts local parts with even distribution of the remainder
Definition: SF_vector.h:343
void local_petsc_to_nodal_mapping(const meshdata< T, S > &mesh, index_mapping< T > &petsc_to_nodal)
bool isEmpty(vector< T > &v)
Return whether an vector is empty (all values are 0).
Definition: SF_vector.h:363
void parallel_distribution_minrank(const vector< T > &gtarget, const vector< T > &cnt, const vector< T > &dsp, const vector< T > &ranks, vector< T > &owner, vector< T > &counts)
T local_nodal_to_local_petsc(const meshdata< T, S > &mesh, int rank, T local_nodal)
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
void MPI_Exchange(commgraph< T > &grph, vector< S > &send, vector< S > &recv, MPI_Comm comm)
Exchange data in parallel over MPI.
Definition: SF_network.h:32
void refine_distribution(const vector< T > &gtarget, const vector< T > &cnt, const vector< T > &dsp, const vector< T > &ranks, const int myrank, vector< T > &owner, vector< T > &counts)
Further refine a distribution generated by parallel_distribution().
void binary_sort(vector< T > &_V)
Definition: SF_sort.h:274
void parallel_distribution(const vector< T > &gtarget, const vector< T > &cnt, const vector< T > &dsp, const vector< T > &ranks, const int myrank, vector< T > &owner, vector< T > &counts)
The distribution distributes entities between all ranks.
@ NBR_PETSC
PETSc numbering of nodes.
Definition: SF_container.h:188
constexpr T min(T a, T b)
Definition: ion_type.h:18
constexpr T max(T a, T b)
Definition: ion_type.h:16