openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
SF_mesh_utils.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_MESH_UTILS_H
13 #define _SF_MESH_UTILS_H
14 
15 #include <algorithm>
16 #include <iostream>
17 #include <string>
18 
19 #include "asciiPlotter.hpp"
20 
21 #include "SF_container.h"
22 #include "SF_network.h"
23 #include "SF_parallel_layout.h"
24 #include "SF_numbering.h"
25 #include "SF_sort.h"
26 #include "SF_mesh_io.h"
27 #include "SF_vector.h"
28 
29 namespace SF {
30 
31 
40 template<class T, class S>
41 inline void permute_mesh(const meshdata<T, S> & inmesh, meshdata<T, S> & outmesh, const vector<T> & perm)
42 {
43  assert(perm.size() == inmesh.l_numelem);
44 
45  outmesh.comm = inmesh.comm;
46 
47  outmesh.g_numelem = inmesh.g_numelem;
48  outmesh.l_numelem = inmesh.l_numelem;
49 
50  // copy numberings
51  outmesh.nbr = inmesh.nbr;
52 
53  const vector<T> & ref_eidx_in = inmesh.get_numbering(NBR_ELEM_REF);
54  vector<T> & ref_eidx_out = outmesh.get_numbering(NBR_ELEM_REF);
55 
56  size_t numelem = outmesh.l_numelem, numcon = inmesh.con.size();
57  int nFib = 0;
58  if(inmesh.fib.size() > 0) {
59  nFib = 1;
60  if(inmesh.she.size() == inmesh.fib.size())
61  nFib = 2;
62  }
63 
64  outmesh.dsp .resize(numelem+1);
65  outmesh.tag .resize(numelem);
66  outmesh.type.resize(numelem);
67  if(nFib>=1)
68  outmesh.fib .resize(numelem*3);
69  if(nFib==2)
70  outmesh.she .resize(numelem*3);
71 
72  outmesh.con.resize(numcon);
73 
74  vector<T> cnt(numelem);
75  T* elem = outmesh.con.data();
76 
77  for(size_t i=0; i<numelem; i++) {
78  outmesh.tag[i] = inmesh.tag[perm[i]];
79  ref_eidx_out[i] = ref_eidx_in[perm[i]];
80  outmesh.type[i] = inmesh.type[perm[i]];
81 
82  if(nFib>=1) {
83  outmesh.fib[i*3+0] = inmesh.fib[perm[i]*3+0];
84  outmesh.fib[i*3+1] = inmesh.fib[perm[i]*3+1];
85  outmesh.fib[i*3+2] = inmesh.fib[perm[i]*3+2];
86  }
87 
88  if(nFib==2) {
89  outmesh.she[i*3+0] = inmesh.she[perm[i]*3+0];
90  outmesh.she[i*3+1] = inmesh.she[perm[i]*3+1];
91  outmesh.she[i*3+2] = inmesh.she[perm[i]*3+2];
92  }
93 
94  int esize = inmesh.dsp[perm[i]+1] - inmesh.dsp[perm[i]];
95  cnt[i] = esize;
96 
97  T estart = inmesh.dsp[perm[i]];
98  for(int j=0; j<esize; j++) elem[j] = inmesh.con[estart+j];
99  elem += esize;
100  }
101 
102  dsp_from_cnt(cnt, outmesh.dsp);
103 
104  // check consistency
105  assert(inmesh.dsp[inmesh.l_numelem] == outmesh.dsp[outmesh.l_numelem]);
106 }
107 
117 template<class T, class S>
118 inline void redistribute_elements(meshdata<T, S> & mesh, meshdata<T, S> & sendbuff, vector<T> & part)
119 {
120  MPI_Comm comm = mesh.comm;
121 
122  int size, rank;
123  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
124 
125  // first, we reorder the elements into contiguous blocks so we can
126  // communicate them more easily
127  vector<T> perm;
128  interval(perm, 0, part.size());
129  binary_sort_copy(part, perm);
130 
131  permute_mesh(mesh, sendbuff, perm);
132 
133  // now we set up the communication graphs (i.e. cnt and dsp for sending and receiving)
134  commgraph<size_t> elem_grph, con_grph;
135  // configure element exchange with the partitioning information.
136  elem_grph.configure(part, comm);
137 
138  // configure connectivity exchange based on element exchange
139  con_grph.resize(size);
140  con_grph.sdsp[0] = 0;
141  for(int i=0; i<size; i++) con_grph.sdsp[i+1] = sendbuff.dsp[elem_grph.sdsp[i+1]];
142  cnt_from_dsp(con_grph.sdsp, con_grph.scnt);
143  MPI_Alltoall(con_grph.scnt.data(), sizeof(size_t), MPI_BYTE, con_grph.rcnt.data(), sizeof(size_t), MPI_BYTE, comm);
144  dsp_from_cnt(con_grph.rcnt, con_grph.rdsp);
145 
146  int nFib=0;
147  if((sendbuff.l_numelem > 0) && (sendbuff.fib.size() == sendbuff.l_numelem*3))
148  nFib = 1;
149  if((sendbuff.l_numelem > 0) && (sendbuff.she.size() == sendbuff.l_numelem*3))
150  nFib = 2;
151  MPI_Allreduce(MPI_IN_PLACE, &nFib, 1, MPI_INT, MPI_MAX, comm);
152 
153  // resize meshdata to fit received data
154  vector<T> & ref_eidx = mesh.get_numbering(NBR_ELEM_REF);
155  vector<T> & ref_eidx_sbuff = sendbuff.get_numbering(NBR_ELEM_REF);
156 
157  size_t recv_size = sum(elem_grph.rcnt);
158  mesh.l_numelem = recv_size;
159  mesh.dsp .resize(recv_size+1);
160  mesh.tag .resize(recv_size);
161  ref_eidx.resize(recv_size);
162  mesh.type.resize(recv_size);
163  if(nFib>=1)
164  mesh.fib .resize(recv_size*3);
165  if(nFib==2)
166  mesh.she .resize(recv_size*3);
167 
168  // we need auxiliary mesh counts
169  vector<T> sendmesh_cnt(sendbuff.l_numelem), mesh_cnt(mesh.l_numelem);
170  cnt_from_dsp(sendbuff.dsp, sendmesh_cnt);
171 
172  MPI_Exchange(elem_grph, sendmesh_cnt, mesh_cnt, comm);
173  dsp_from_cnt(mesh_cnt, mesh.dsp);
174 
175  MPI_Exchange(elem_grph, sendbuff.tag, mesh.tag, comm);
176  MPI_Exchange(elem_grph, ref_eidx_sbuff, ref_eidx, comm);
177  MPI_Exchange(elem_grph, sendbuff.type, mesh.type, comm);
178 
179  elem_grph.scale(3); // fiber data has three values per element
180  if(nFib >= 1)
181  MPI_Exchange(elem_grph, sendbuff.fib, mesh.fib, comm);
182  if(nFib >= 2)
183  MPI_Exchange(elem_grph, sendbuff.she, mesh.she, comm);
184 
185  // the only thing left is to communicate connectivity
186  {
187  // we map the sendbuff connectivity to global reference indexing
188  vector<T> & rnod = sendbuff.get_numbering(NBR_REF);
189  for(size_t i=0; i<sendbuff.con.size(); i++) sendbuff.con[i] = rnod[sendbuff.con[i]];
190  }
191  recv_size = sum(con_grph.rcnt);
192  mesh.con.resize(recv_size);
193  MPI_Exchange(con_grph, sendbuff.con, mesh.con, comm);
194 
195  mesh.localize(NBR_REF);
196 }
204 template<class T, class S>
205 inline void redistribute_elements(meshdata<T, S> & mesh, vector<T> & part)
206 {
207  meshdata<T, S> sendbuff;
208  redistribute_elements(mesh, sendbuff, part);
209 }
210 
220 template<class T, class S>
221 inline void redistribute_mesh(meshdata<T, S> & mesh, vector<T> & part)
222 {
223  // the main idea is to compute the unique node set of the element block we send to
224  // each process. Therefore we know which coordinates to communicate.
225 
226  meshdata<T, S> sendmesh;
227  redistribute_elements(mesh, sendmesh, part);
228  sendmesh.xyz.assign(mesh.xyz.begin(), mesh.xyz.end());
229  // part and sendmesh have been sorted in redistribute_elements.
230 
231  MPI_Comm comm = mesh.comm;
232  int size, rank;
233  MPI_Comm_size(comm, &size);
234  MPI_Comm_rank(comm, &rank);
235 
236  // we use commgraphs to store the layouts of the elements, connectivities and nodes
237  commgraph<T> elem_layout, con_layout, nod_layout;
238  elem_layout.resize(size);
239  con_layout.resize(size);
240  nod_layout.resize(size);
241 
242  // compute layout of the elements
243  count(part, elem_layout.scnt);
244  dsp_from_cnt(elem_layout.scnt, elem_layout.sdsp);
245 
246  // compute layout of the connectivities using the element layout
247  con_layout.scnt.zero();
248  for(int pid=0; pid<size; pid++)
249  for(T i=elem_layout.sdsp[pid]; i<elem_layout.sdsp[pid+1]; i++)
250  con_layout.scnt[pid] += sendmesh.dsp[i+1] - sendmesh.dsp[i];
251  dsp_from_cnt(con_layout.scnt, con_layout.sdsp);
252 
253  // finally compute the nodes we need to send to each rank and their layout
254  vector<T> nod_sbuff(sendmesh.con.size());
255  nod_layout.sdsp[0] = 0; // initialize first entry of displacements
256 
257  for(int pid=0; pid<size; pid++)
258  {
259  T con_start = con_layout.sdsp[pid], con_end = con_layout.sdsp[pid+1];
260  vector<T> nod(con_end - con_start);
261  // copy connectivity block
262  vec_assign(nod.data(), sendmesh.con.data() + con_start, nod.size());
263  // get unique node set for block
264  binary_sort(nod); unique_resize(nod);
265 
266  // add block size to layout
267  nod_layout.scnt[pid] = nod.size();
268  nod_layout.sdsp[pid+1] = nod_layout.sdsp[pid] + nod_layout.scnt[pid];
269  // copy node set to send buffer
270  vec_assign(nod_sbuff.data() + nod_layout.sdsp[pid], nod.data(), nod.size());
271  }
272  nod_sbuff.resize(nod_layout.sdsp[nod_layout.sdsp.size()-1]);
273  // finish up nod_layout
274  MPI_Alltoall(nod_layout.scnt.data(), sizeof(T), MPI_BYTE, nod_layout.rcnt.data(), sizeof(T), MPI_BYTE, comm);
275  dsp_from_cnt(nod_layout.rcnt, nod_layout.rdsp);
276 
277  vector<T> nod_lidx(nod_sbuff);
278  global_to_local(sendmesh.get_numbering(NBR_REF), nod_lidx, false, true);
279 
280  // fill coordinates send buffer
281  vector<S> xyz_sbuff(nod_sbuff.size() * 3);
282  for(size_t i=0; i<nod_lidx.size(); i++)
283  {
284  T lidx = nod_lidx[i];
285  xyz_sbuff[i*3+0] = sendmesh.xyz[lidx*3+0];
286  xyz_sbuff[i*3+1] = sendmesh.xyz[lidx*3+1];
287  xyz_sbuff[i*3+2] = sendmesh.xyz[lidx*3+2];
288  }
289 
290  size_t rsize = sum(nod_layout.rcnt);
291  vector<T> nod_rbuff(rsize);
292  vector<S> xyz_rbuff(rsize*3);
293 
294  MPI_Exchange(nod_layout, nod_sbuff, nod_rbuff, comm);
295  nod_layout.scale(3);
296  MPI_Exchange(nod_layout, xyz_sbuff, xyz_rbuff, comm);
297 
298  // map received indices to local indexing
299  global_to_local(mesh.get_numbering(NBR_REF), nod_rbuff, false, true);
300 
301  // find unique coordinates
302  vector<T> acc_cnt(nod_rbuff.size(), 1), acc_dsp,
303  acc_col(nod_rbuff.size());
304 
305  interval(acc_col, 0, nod_rbuff.size());
306  binary_sort_copy(nod_rbuff, acc_col);
307  unique_accumulate(nod_rbuff, acc_cnt);
308  acc_dsp.resize(acc_cnt.size()+1);
309  dsp_from_cnt(acc_cnt, acc_dsp);
310 
311  // generate new coordinates vector
312  mesh.xyz.resize(acc_cnt.size()*3);
313  for(size_t i=0; i<acc_cnt.size(); i++)
314  {
315  T pidx = acc_col[acc_dsp[i]];
316  mesh.xyz[i*3+0] = xyz_rbuff[pidx*3+0];
317  mesh.xyz[i*3+1] = xyz_rbuff[pidx*3+1];
318  mesh.xyz[i*3+2] = xyz_rbuff[pidx*3+2];
319  }
320 }
321 
322 
341 template<class T, class S>
342 inline void write_points_parallel(const meshdata<T, S> & mesh, bool binary, std::string basename)
343 {
344  const MPI_Comm comm = mesh.comm;
345 
346  int size, rank;
347  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
348 
349  // the global vertex numbering of the mesh
350  const vector<T> & nbr_orig = mesh.nbr.count(NBR_SUBMESH) ? mesh.get_numbering(NBR_SUBMESH)
351  : mesh.get_numbering(NBR_REF);
352 
353  // redistribute the vertex coordinates
354  const vector<T> & alg_nod = mesh.pl.algebraic_nodes();
355  vector<T> xyz_cnt(alg_nod.size(), 3), xyz_idx(alg_nod.size()), srt_cnt, srt_idx;
356  vector<S> xyz(alg_nod.size()*3), srt_xyz;
357 
358  for(size_t i=0; i<alg_nod.size(); i++) {
359  T loc = alg_nod[i];
360 
361  xyz[i*3+0] = mesh.xyz[loc*3+0];
362  xyz[i*3+1] = mesh.xyz[loc*3+1];
363  xyz[i*3+2] = mesh.xyz[loc*3+2];
364 
365  xyz_idx[i] = nbr_orig[loc];
366  }
367 
368  sort_parallel(comm, xyz_idx, xyz_cnt, xyz, srt_idx, srt_cnt, srt_xyz);
369 
370  // write header with root
371  FILE* pts_fd;
372  std::string pts_file = binary ? basename + ".bpts" : basename + ".pts";
373 
374  if(rank == 0) {
375  pts_fd = fopen(pts_file.c_str(), "w");
376  if(!pts_fd) {
377  fprintf(stderr, "Error: could not open file: %s. Aborting!", pts_file.c_str());
378  exit(1);
379  }
380  write_pts_header(pts_fd, binary, mesh.g_numpts);
381  fclose(pts_fd);
382  }
383 
384  // write vertices sequentially rank after rank
385  for(int pid=0; pid < size; pid++) {
386  if(pid == rank) {
387  pts_fd = fopen(pts_file.c_str(), "a");
388  if(!pts_fd) {
389  fprintf(stderr, "Error: could not open file: %s. Aborting!", pts_file.c_str());
390  exit(1);
391  }
392  write_pts_block(pts_fd, binary, srt_xyz);
393  fclose(pts_fd);
394  }
395  MPI_Barrier(comm);
396  }
397 }
398 
399 template<class T, class S>
400 inline void write_mesh_parallel(const meshdata<T, S> & mesh, bool binary, std::string basename)
401 {
402  const MPI_Comm comm = mesh.comm;
403 
404  int size, rank;
405  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
406 
407  // we duplicate the mesh so that we can safely redistribute it
408  meshdata<T, S> wmesh = mesh;
409  vector<T> & ref_eidx = wmesh.get_numbering(NBR_ELEM_REF);
410 
411  T elem_gmin = global_min(ref_eidx, comm);
412  T elem_gmax = global_max(ref_eidx, comm);
413 
414  // block size
415  T elem_bsize = (elem_gmax - elem_gmin) / size + 1;
416 
417  // redistribute elements linearly ascending after their reference element index --------------------
418  vector<T> dest(wmesh.l_numelem);
419  for(size_t i=0; i<dest.size(); i++)
420  dest[i] = (ref_eidx[i] - elem_gmin) / elem_bsize;
421 
422  // redistribute elements of write mesh.
423  // WARNING: vertex coordinates are not redistributed. therefore the
424  // vertices will be redistributed based on the original mesh.
425  redistribute_elements(wmesh, dest);
426 
427  {
428  // temporary datastructs
429  vector<T> perm, eidx;
430  meshdata<T, S> tmesh = wmesh;
431 
432  // generate permutation
433  eidx = wmesh.get_numbering(NBR_ELEM_REF);
434  interval(perm, 0, eidx.size());
435  binary_sort_copy(eidx, perm);
436 
437  // permute local mesh to locally ascending element indices
438  permute_mesh(tmesh, wmesh, perm);
439  }
440 
441  if(mesh.nbr.count(NBR_SUBMESH)) {
442  // mesh is a submesh. generate submesh numbering for the redistributed
443  // mesh and map connectivity to it
444  submesh_numbering<T, S> submesh_nbr;
445  submesh_nbr(wmesh);
446  const vector<T> & nbr = wmesh.get_numbering(NBR_SUBMESH);
447  for(size_t i=0; i<wmesh.con.size(); i++) wmesh.con[i] = nbr[wmesh.con[i]];
448  }
449  else {
450  // mesh is a reference mesh. just map connectivity.
451  const vector<T> & nbr = wmesh.get_numbering(NBR_REF);
452  for(size_t i=0; i<wmesh.con.size(); i++) wmesh.con[i] = nbr[wmesh.con[i]];
453  }
454 
455  // write .elem and .lon files
456  write_elements(wmesh, binary, basename);
457 
458  write_points_parallel(mesh, binary, basename);
459 }
460 
461 
468 template<class T, class S>
469 inline void gather_mesh(const meshdata<T, S> & locmesh, meshdata<T, S> & globmesh)
470 {
471  MPI_Comm comm = locmesh.comm;
472 
473  int size, rank;
474  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
475 
476  // gather mesh data using the redistribute_mesh function
477  vector<T> part(locmesh.l_numelem, 0); // all elements to rank 0
478  globmesh = locmesh;
479  redistribute_mesh(globmesh, part);
480 }
481 
485 template<class T, class S>
486 inline void print_DD_info(const meshdata<T, S> & mesh)
487 {
488  MPI_Comm comm = mesh.comm;
489 
490  int size, rank;
491  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
492 
493  vector<size_t> npoint(size), nelem(size), ninterf(size), nidx(size);
494 
495  size_t intf_size = mesh.pl.interface().size(),
496  idx_size = mesh.pl.num_algebraic_idx();
497 
498  MPI_Gather(&mesh.l_numpts, sizeof(size_t), MPI_BYTE, npoint.data(), sizeof(size_t), MPI_BYTE, 0, comm);
499  MPI_Gather(&mesh.l_numelem, sizeof(size_t), MPI_BYTE, nelem.data(), sizeof(size_t), MPI_BYTE, 0, comm);
500  MPI_Gather(&intf_size, sizeof(size_t), MPI_BYTE, ninterf.data(), sizeof(size_t), MPI_BYTE, 0, comm);
501  MPI_Gather(&idx_size, sizeof(size_t), MPI_BYTE, nidx.data(), sizeof(size_t), MPI_BYTE, 0, comm);
502 
503  vector<int> mult(mesh.l_numpts, 1);
504  mesh.pl.reduce(mult, "sum");
505 
506  int hist_size = 16;
507  vector<int> mult_hist(hist_size, 0), global_mult_hist(hist_size, 0);
508  for(auto m : mult) mult_hist[m]++;
509 
510  MPI_Reduce(mult_hist.data(), global_mult_hist.data(), hist_size, MPI_INT, MPI_SUM, 0, comm);
511 
512  if(!rank) {
513  printf("===== Parallel mesh statistics =====\n");
514 
515  printf("#pid\t#nodes\t#elems\t#interf\t#alg\n");
516  for(int pid = 0; pid < size; pid++)
517  printf("%d\t%ld\t%ld\t%ld\t%ld\n", pid, (long int)npoint[pid], (long int)nelem[pid],
518  (long int)ninterf[pid], (long int)nidx[pid]);
519  printf("\n");
520 
521  std::cout << "Multiplicities :" << std::endl;
522  for(int i = 2; i < hist_size && global_mult_hist[i] > 0; i++)
523  std::cout << i << ": " << global_mult_hist[i] << std::endl;
524  }
525 }
526 
527 
536 template<class T, class S>
537 inline void extract_mesh(const vector<bool> & keep,
538  const meshdata<T, S> & mesh,
539  meshdata<T, S> & submesh)
540 {
541  assert(keep.size() == mesh.l_numelem);
542 
543  size_t num_extr_elem = 0, num_extr_entr = 0;
544  int nFib = 0;
545  if( mesh.fib.size() == (mesh.l_numelem*3) ) {
546  nFib = 1;
547  if( mesh.she.size() == (mesh.l_numelem*3) )
548  nFib = 2;
549  }
550 
551  submesh.l_numpts = submesh.g_numpts = 0;
552  submesh.comm = mesh.comm;
553 
554  const vector<T> & mesh_ref_eidx = mesh.get_numbering(NBR_ELEM_REF);
555  vector<T> & sub_ref_eidx = submesh.register_numbering(NBR_ELEM_REF);
556 
557  for(size_t i=0; i<mesh.l_numelem; i++) {
558  if(keep[i]) {
559  num_extr_elem++;
560  num_extr_entr += mesh.dsp[i+1] - mesh.dsp[i];
561  }
562  }
563 
564  submesh.l_numelem = num_extr_elem;
565 
566  vector<T> cnt(num_extr_elem);
567  submesh.dsp.resize(num_extr_elem+1);
568  submesh.tag.resize(num_extr_elem);
569  sub_ref_eidx.resize(num_extr_elem);
570  submesh.type.resize(num_extr_elem);
571  if(nFib>=1)
572  submesh.fib.resize(num_extr_elem*3);
573  if(nFib==2)
574  submesh.she.resize(num_extr_elem*3);
575 
576  submesh.con.resize(num_extr_entr);
577  // reference numbering
578  const vector<T> & rnod = mesh.get_numbering(NBR_REF);
579 
580  for(size_t ridx_ele=0, ridx_con=0, widx_ele=0, widx_con=0; ridx_ele<mesh.l_numelem; ridx_ele++)
581  {
582  ridx_con = mesh.dsp[ridx_ele];
583 
584  if(keep[ridx_ele]) {
585  // element data
586  cnt[widx_ele] = mesh.dsp[ridx_ele + 1] - mesh.dsp[ridx_ele];
587  submesh.tag [widx_ele] = mesh.tag [ridx_ele];
588  sub_ref_eidx[widx_ele] = mesh_ref_eidx[ridx_ele];
589  submesh.type[widx_ele] = mesh.type [ridx_ele];
590 
591  // fibers
592  if(nFib>=1) {
593  submesh.fib[widx_ele*3+0] = mesh.fib[ridx_ele*3+0];
594  submesh.fib[widx_ele*3+1] = mesh.fib[ridx_ele*3+1];
595  submesh.fib[widx_ele*3+2] = mesh.fib[ridx_ele*3+2];
596  }
597  if(nFib==2) {
598  submesh.she[widx_ele*3+0] = mesh.she[ridx_ele*3+0];
599  submesh.she[widx_ele*3+1] = mesh.she[ridx_ele*3+1];
600  submesh.she[widx_ele*3+2] = mesh.she[ridx_ele*3+2];
601  }
602 
603  // connectivity
604  for(int j=0; j<cnt[widx_ele]; j++)
605  submesh.con[widx_con++] = rnod[mesh.con[ridx_con++]];
606 
607  widx_ele++;
608  }
609  }
610  dsp_from_cnt(cnt, submesh.dsp);
611 
612  unsigned long int gnumele = submesh.l_numelem;
613  MPI_Allreduce(MPI_IN_PLACE, &gnumele, 1, MPI_UNSIGNED_LONG, MPI_SUM, submesh.comm);
614  submesh.g_numelem = gnumele;
615 
616  submesh.localize(NBR_REF);
617 }
618 
619 
625 template<class T, class S>
626 inline void rebalance_mesh(meshdata<T, S> & mesh)
627 {
628  MPI_Comm comm = mesh.comm;
629 
630  // we check if some ranks have 0 local elems
631  short errflag = 0;
632  if(mesh.l_numelem == 0) errflag = 1;
633  MPI_Allreduce(MPI_IN_PLACE, &errflag, 1, MPI_SHORT, MPI_SUM, comm);
634 
635  // we redistribute the elements if necessary
636  if(errflag) {
637  int size, rank;
638  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
639 
640  vector<T> part(mesh.l_numelem);
641  vector<size_t> elem_counts(size), layout;
642  MPI_Allgather(&mesh.l_numelem, sizeof(size_t), MPI_BYTE, elem_counts.data(),
643  sizeof(size_t), MPI_BYTE, comm);
644 
645  dsp_from_cnt(elem_counts, layout);
646 
647  for(size_t i=0; i<mesh.l_numelem; i++)
648  part[i] = (layout[rank] + i) % size;
649 
650  redistribute_elements(mesh, part);
651  }
652 }
653 
654 
663 template<class T, class S>
664 inline void extract_myocardium(const meshdata<T, S> & mesh, meshdata<T, S> & submesh, bool require_fibers=true)
665 {
666  MPI_Comm comm = mesh.comm;
667  vector<bool> keep(mesh.l_numelem);
668 
669  // fill keep based on empty fiber definitions
670  for(size_t i=0; i<mesh.l_numelem; i++) {
671  S l1 = 1,l2=0,l3=0;
672  if(require_fibers){
673  l1 = mesh.fib[i*3+0];
674  l2 = mesh.fib[i*3+1];
675  l3 = mesh.fib[i*3+2];
676  }
677 
678  if( l1*l1 + l2*l2 + l3*l3 )
679  keep[i] = true;
680  else
681  keep[i] = false;
682  }
683 
684  extract_mesh(keep, mesh, submesh);
685  rebalance_mesh(submesh);
686 
687  // get unique tag set
688  submesh.extr_tag.insert(submesh.tag.begin(), submesh.tag.end());
689  {
690  // we have to collect the global list of tags of this submesh
691  vector<T> tags, glob_tags;
692  tags.assign(submesh.extr_tag.begin(), submesh.extr_tag.end()); // local list
693  make_global(tags, glob_tags, mesh.comm); // now globalized
694 
695  submesh.extr_tag.insert(glob_tags.begin(), glob_tags.end());
696  }
697 }
698 
699 
709 template<class T, class S>
710 inline void extract_tagbased(const meshdata<T, S> & mesh, meshdata<T, S> & submesh)
711 {
712  if(submesh.extr_tag.size())
713  {
714  vector<bool> keep(mesh.l_numelem, false);
715 
716  // fill keep based on empty fiber definitions
717  for(size_t i=0; i<mesh.l_numelem; i++) {
718  if( submesh.extr_tag.count(mesh.tag[i]) )
719  keep[i] = true;
720  }
721 
722  extract_mesh(keep, mesh, submesh);
723  }
724  else {
725  // if the tags are not provided, we assume that the whole mesh is copied into the
726  // submesh
727  std::string oldname = submesh.name;
728  // copy reference mesh
729  submesh = mesh;
730  // revert name to old name
731  submesh.name = oldname;
732  // compute tags
733  submesh.extr_tag.insert(submesh.tag.begin(), submesh.tag.end());
734  {
735  // we have to collect the global list of tags of this submesh
736  vector<T> tags, glob_tags;
737  tags.assign(submesh.extr_tag.begin(), submesh.extr_tag.end()); // local list
738  make_global(tags, glob_tags, mesh.comm); // now globalized
739 
740  submesh.extr_tag.insert(glob_tags.begin(), glob_tags.end());
741  }
742  }
743 
744  rebalance_mesh(submesh);
745 }
746 
752 template<class T, class S>
753 inline void print_mesh_graph(meshdata<T,S> & mesh)
754 {
755  int rank, size;
756  MPI_Comm_size(mesh.comm, &size); MPI_Comm_rank(mesh.comm, &rank);
757 
758  vector<T> n2n_cnt, n2n_con;
759  nodal_connectivity_graph(mesh, n2n_cnt, n2n_con);
760  matrixgraph_plotter<vector<T> > plotter(30, 60);
761 
762  for(int pid=0; pid<size; pid++)
763  {
764  if(pid == rank) {
765  std::cout << "\n\n Rank " << rank << ": \n" << std::endl;
766  plotter.print(n2n_cnt, n2n_con, '*');
767  }
768  MPI_Barrier(mesh.comm);
769  }
770 }
771 
785 template<class T, class S> inline
786 void inter_domain_mapping(const meshdata<T,S> & mesh_a, const meshdata<T,S> & mesh_b,
787  const SF_nbr snbr, index_mapping<T> & a_to_b)
788 {
789  assert(mesh_a.comm == mesh_b.comm);
790  MPI_Comm comm = mesh_a.comm;
791 
792  int size, rank;
793  MPI_Comm_size(comm, &size);
794  MPI_Comm_rank(comm, &rank);
795 
796  // reference and submesh numberings of both meshes
797  const vector<T> & mesh_a_rnbr = mesh_a.get_numbering(NBR_REF);
798  const vector<T> & mesh_a_snbr = mesh_a.get_numbering(snbr);
799  const vector<T> & mesh_b_rnbr = mesh_b.get_numbering(NBR_REF);
800  const vector<T> & mesh_b_snbr = mesh_b.get_numbering(snbr);
801  // send- and receive-buffer for the reference and submesh numberings
802  vector<T> mesh_a_rnbr_sbuff, mesh_a_rnbr_rbuff, mesh_a_snbr_sbuff, mesh_a_snbr_rbuff;
803  vector<T> mesh_b_rnbr_sbuff, mesh_b_rnbr_rbuff, mesh_b_snbr_sbuff, mesh_b_snbr_rbuff;
804 
805  // communicate reference- and submesh numbering of mesh a ----------------------------
806  vector<T> dest(mesh_a_rnbr.size()), perm_a, perm_b;
807  for(size_t i=0; i<dest.size(); i++) dest[i] = mesh_a_rnbr[i] % size;
808 
809  interval(perm_a, 0, mesh_a_rnbr.size());
810  binary_sort_copy(dest, perm_a);
811 
812  mesh_a_rnbr_sbuff.resize(dest.size()); mesh_a_snbr_sbuff.resize(dest.size());
813  for(size_t i=0; i<dest.size(); i++) {
814  mesh_a_rnbr_sbuff[i] = mesh_a_rnbr[perm_a[i]];
815  //mesh_a_snbr_sbuff[i] = mesh_a_snbr[perm_a[i]];
816  }
817 
818  commgraph<size_t> grph_a;
819  grph_a.configure(dest, comm);
820  size_t rcv_size = sum(grph_a.rcnt);
821 
822  mesh_a_rnbr_rbuff.resize(rcv_size);
823  mesh_a_snbr_rbuff.resize(rcv_size);
824 
825  MPI_Exchange(grph_a, mesh_a_rnbr_sbuff, mesh_a_rnbr_rbuff, comm);
826  //MPI_Exchange(grph_a, mesh_a_snbr_sbuff, mesh_a_snbr_rbuff, comm);
827 
828  // communicate reference- and submesh numbering of mesh b ----------------------------
829  dest.resize(mesh_b_rnbr.size());
830  for(size_t i=0; i<dest.size(); i++) dest[i] = mesh_b_rnbr[i] % size;
831 
832  interval(perm_b, 0, mesh_b_rnbr.size());
833  binary_sort_copy(dest, perm_b);
834 
835  mesh_b_rnbr_sbuff.resize(dest.size()); mesh_b_snbr_sbuff.resize(dest.size());
836  for(size_t i=0; i<dest.size(); i++) {
837  mesh_b_rnbr_sbuff[i] = mesh_b_rnbr[perm_b[i]];
838  mesh_b_snbr_sbuff[i] = mesh_b_snbr[perm_b[i]];
839  }
840 
841  commgraph<size_t> grph_b;
842  grph_b.configure(dest, comm);
843  rcv_size = sum(grph_b.rcnt);
844 
845  mesh_b_rnbr_rbuff.resize(rcv_size);
846  mesh_b_snbr_rbuff.resize(rcv_size);
847 
848  MPI_Exchange(grph_b, mesh_b_rnbr_sbuff, mesh_b_rnbr_rbuff, comm);
849  MPI_Exchange(grph_b, mesh_b_snbr_sbuff, mesh_b_snbr_rbuff, comm);
850 
851  // generate mapping ------------------------------------------------------------------
852  // We do this by first setting up a map between reference index and submesh index for
853  // mesh b. This allows us to check fast if a reference index is in b and do the mapping.
854  hashmap::unordered_map<T,T> ref_to_sub_b;
855  for(size_t i=0; i<mesh_b_rnbr_rbuff.size(); i++) {
856  T ref_idx = mesh_b_rnbr_rbuff[i];
857  T sub_idx = mesh_b_snbr_rbuff[i];
858 
859  if(ref_to_sub_b.count(ref_idx) && ref_to_sub_b[ref_idx] != sub_idx)
860  fprintf(stderr, "inter_domain_mapping error: Missmatching multiple mappings: %lld : %lld \n",
861  static_cast<long long>(ref_to_sub_b[ref_idx]),
862  static_cast<long long>(sub_idx));
863  ref_to_sub_b[ref_idx] = sub_idx;
864  }
865 
866  // Now we either map the reference indices of mesh a to the submesh index of b, or, if
867  // the reference index is not in b, set it to -1
868  for(size_t i=0; i<mesh_a_rnbr_rbuff.size(); i++) {
869  auto it = ref_to_sub_b.find(mesh_a_rnbr_rbuff[i]);
870  if(it != ref_to_sub_b.end())
871  mesh_a_snbr_rbuff[i] = it->second;
872  else
873  mesh_a_snbr_rbuff[i] = -1;
874  }
875 
876  // now we simply communicate back by transposing grph_a
877  grph_a.transpose();
878  MPI_Exchange(grph_a, mesh_a_snbr_rbuff, mesh_a_snbr_sbuff, comm);
879 
880  size_t num_mapped = 0;
881  for(size_t i=0; i<mesh_a_snbr_sbuff.size(); i++)
882  if(mesh_a_snbr_sbuff[i] > -1) num_mapped++;
883 
884  // the submesh indices that could be mapped
885  vector<T> snbr_a(num_mapped), snbr_b(num_mapped);
886  binary_sort_copy(perm_a, mesh_a_snbr_sbuff);
887 
888  for(size_t i=0, idx=0; i<mesh_a_snbr_sbuff.size(); i++) {
889  if(mesh_a_snbr_sbuff[i] > -1) {
890  snbr_a[idx] = mesh_a_snbr[i];
891  snbr_b[idx] = mesh_a_snbr_sbuff[i];
892  idx++;
893  }
894  }
895 
896  // now we can set up the a-to-b mapping
897  a_to_b.assign(snbr_a, snbr_b);
898 }
899 
900 template<class T> inline
901 void insert_surf_tri(T n1, T n2, T n3, size_t eidx,
902  triple<T> & surf,
903  tri_sele<T> & sele,
905 {
906  sele.v1 = n1, sele.v2 = n2, sele.v3 = n3; sele.eidx = eidx;
907  sort_triple(n1, n2, n3, surf.v1, surf.v2, surf.v3);
908 
909  auto it = surfmap.find(surf);
910  if(it != surfmap.end()) surfmap.erase(it);
911  else surfmap[surf] = sele;
912 }
913 
914 template<class T> inline
915 void insert_surf_quad(T n1, T n2, T n3, T n4, size_t eidx,
916  vector<T> & buff,
917  quadruple<T> & surf,
918  quad_sele<T> & sele,
920 {
921  buff[0] = n1, buff[1] = n2, buff[2] = n3, buff[3] = n4;
922  binary_sort(buff);
923 
924  sele.v1 = n1, sele.v2 = n2, sele.v3 = n3, sele.v4 = n4, sele.eidx = eidx;
925  surf.v1 = buff[0], surf.v2 = buff[1], surf.v3 = buff[2], surf.v4 = buff[3];
926 
927  auto it = surfmap.find(surf);
928  if(it != surfmap.end()) surfmap.erase(it);
929  else surfmap[surf] = sele;
930 }
931 
932 template<class T> inline
933 void insert_surf_tet(const T* nod,
934  const size_t eidx,
936 {
937  // surfaces are (2,3,1) , (1,4,2) , (2,4,3) , (1,3,4)
938  struct triple<T> surf;
939  struct tri_sele<T> sele;
940 
941  T n1 = nod[0], n2 = nod[1], n3 = nod[2], n4 = nod[3];
942 
943  insert_surf_tri(n2, n3, n1, eidx, surf, sele, surfmap);
944  insert_surf_tri(n1, n4, n2, eidx, surf, sele, surfmap);
945  insert_surf_tri(n2, n4, n3, eidx, surf, sele, surfmap);
946  insert_surf_tri(n1, n3, n4, eidx, surf, sele, surfmap);
947 }
948 
949 template<class T> inline
950 void insert_surf_pyr(const T* nod, const size_t eidx, vector<T> & buff,
953 {
954  // surfaces are (1,5,2) , (2,5,3) , (3,5,4) , (4,5,1) , (1,2,3,4)
955  triple<T> surf; quadruple<T> qsurf;
956  tri_sele<T> sele; quad_sele<T> qsele;
957 
958  T n1 = nod[0], n2 = nod[1], n3 = nod[2], n4 = nod[3], n5 = nod[4];
959 
960  insert_surf_tri(n1, n5, n2, eidx, surf, sele, surfmap);
961  insert_surf_tri(n2, n5, n3, eidx, surf, sele, surfmap);
962  insert_surf_tri(n3, n5, n4, eidx, surf, sele, surfmap);
963  insert_surf_tri(n4, n5, n1, eidx, surf, sele, surfmap);
964  insert_surf_quad(n1, n2, n3, n4, eidx, buff, qsurf, qsele, qsurfmap);
965 }
966 
967 template<class T> inline
968 void insert_surf_pri(const T* nod, const size_t eidx, vector<T> & buff,
971 {
972  struct triple<T> surf;
973  struct quadruple<T> qsurf;
974  struct tri_sele<T> sele;
975  struct quad_sele<T> qsele;
976 
977  T n1 = nod[0], n2 = nod[1], n3 = nod[2], n4 = nod[3], n5 = nod[4], n6 = nod[5];
978 
979  // surfaces are (1,2,3) , (4,5,6) , (1,2,6,4) , (2,3,5,6) , (3,1,4,5)
980  insert_surf_tri(n1, n2, n3, eidx, surf, sele, surfmap);
981  insert_surf_tri(n4, n5, n6, eidx, surf, sele, surfmap);
982  insert_surf_quad(n1, n2, n6, n4, eidx, buff, qsurf, qsele, qsurfmap);
983  insert_surf_quad(n2, n3, n5, n6, eidx, buff, qsurf, qsele, qsurfmap);
984  insert_surf_quad(n3, n1, n4, n5, eidx, buff, qsurf, qsele, qsurfmap);
985 }
986 
987 template<class T> inline
988 void insert_surf_hex(const T* nod, const size_t eidx, vector<T> & buff,
990 {
991  // surfaces are (1,2,3,4) , (3,2,8,7) , (4,3,7,6) , (1,4,6,5) , (2,1,5,8), (6,7,8,5)
992  struct quadruple<T> qsurf;
993  struct quad_sele<T> qsele;
994 
995  T n1 = nod[0], n2 = nod[1], n3 = nod[2], n4 = nod[3],
996  n5 = nod[4], n6 = nod[5], n7 = nod[6], n8 = nod[7];
997 
998  insert_surf_quad(n1, n2, n3, n4, eidx, buff, qsurf, qsele, surfmap);
999  insert_surf_quad(n3, n2, n8, n7, eidx, buff, qsurf, qsele, surfmap);
1000  insert_surf_quad(n4, n3, n7, n6, eidx, buff, qsurf, qsele, surfmap);
1001  insert_surf_quad(n1, n4, n6, n5, eidx, buff, qsurf, qsele, surfmap);
1002  insert_surf_quad(n2, n1, n5, n8, eidx, buff, qsurf, qsele, surfmap);
1003  insert_surf_quad(n6, n7, n8, n5, eidx, buff, qsurf, qsele, surfmap);
1004 }
1005 
1006 template<class T, class S> inline
1008  const hashmap::unordered_map<triple<T>, tri_sele<T> > & search_tri,
1009  const hashmap::unordered_map<quadruple<T>, quad_sele<T> > & search_quad,
1012 {
1013  const T* con = mesh.con.data();
1014  const T* nbr = mesh.get_numbering(numbering).data();
1015 
1018 
1019  vector<T> nodvec(mesh.con.size()); T* nod = nodvec.data();
1020  for(size_t i=0; i<nodvec.size(); i++)
1021  nod[i] = nbr[con[i]];
1022 
1023  const vector<T> & ref_eidx = mesh.get_numbering(NBR_ELEM_REF);
1024  vector<T> qbuff(4); // buffer if quad nodes need to be sorted
1025 
1026  for(size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
1027  tri_surf.clear();
1028  quad_surf.clear();
1029 
1030  switch(mesh.type[eidx]) {
1031  case Tri: {
1032  triple<T> tri;
1033  tri_sele<T> trie;
1034  insert_surf_tri(nod[0], nod[1], nod[2], ref_eidx[eidx], tri, trie, tri_surf);
1035  break;
1036  }
1037 
1038  case Quad: {
1039  quadruple<T> qd;
1040  quad_sele<T> qde;
1041  insert_surf_quad(nod[0], nod[1], nod[2], nod[3],
1042  ref_eidx[eidx], qbuff, qd, qde, quad_surf);
1043  break;
1044  }
1045 
1046  case Tetra:
1047  insert_surf_tet(nod, ref_eidx[eidx], tri_surf);
1048  break;
1049 
1050  case Pyramid:
1051  insert_surf_pyr(nod, ref_eidx[eidx], qbuff, tri_surf, quad_surf);
1052  break;
1053 
1054  case Prism:
1055  insert_surf_pri(nod, ref_eidx[eidx], qbuff, tri_surf, quad_surf);
1056  break;
1057 
1058  case Hexa:
1059  insert_surf_hex(nod, ref_eidx[eidx], qbuff, quad_surf);
1060  break;
1061 
1062  default: break;
1063  }
1064 
1065  for(auto it = tri_surf.begin(); it != tri_surf.end(); ++it) {
1066  auto ft = search_tri.find(it->first);
1067  if(ft != search_tri.end() && !found_tri.count(ft->first)) {
1068  // now we also look for the same orientation
1069  T iv1 = mesh.pl.localize(it->second.v1);
1070  T iv2 = mesh.pl.localize(it->second.v2);
1071  T iv3 = mesh.pl.localize(it->second.v3);
1072  SF::Point ip1 = {mesh.xyz[iv1*3+0], mesh.xyz[iv1*3+1], mesh.xyz[iv1*3+2]};
1073  SF::Point ip2 = {mesh.xyz[iv2*3+0], mesh.xyz[iv2*3+1], mesh.xyz[iv2*3+2]};
1074  SF::Point ip3 = {mesh.xyz[iv3*3+0], mesh.xyz[iv3*3+1], mesh.xyz[iv3*3+2]};
1075  SF::Point e12 = normalize(ip2 - ip1);
1076  SF::Point e13 = normalize(ip3 - ip1);
1077  SF::Point in = normalize(cross(e12, e13));
1078 
1079  T fv1 = mesh.pl.localize(ft->second.v1);
1080  T fv2 = mesh.pl.localize(ft->second.v2);
1081  T fv3 = mesh.pl.localize(ft->second.v3);
1082  SF::Point fp1 = {mesh.xyz[fv1*3+0], mesh.xyz[fv1*3+1], mesh.xyz[fv1*3+2]};
1083  SF::Point fp2 = {mesh.xyz[fv2*3+0], mesh.xyz[fv2*3+1], mesh.xyz[fv2*3+2]};
1084  SF::Point fp3 = {mesh.xyz[fv3*3+0], mesh.xyz[fv3*3+1], mesh.xyz[fv3*3+2]};
1085  e12 = normalize(fp2 - fp1);
1086  e13 = normalize(fp3 - fp1);
1087  SF::Point fn = normalize(cross(e12, e13));
1088 
1089  if (inner_prod(in, fn) > 0.9) {
1090  tri_sele<T> found = ft->second; found.eidx = eidx;
1091  found_tri[ft->first] = found;
1092  }
1093  }
1094  }
1095 
1096  for(auto it = quad_surf.begin(); it != quad_surf.end(); ++it) {
1097  auto ft = search_quad.find(it->first);
1098  if(ft != search_quad.end() && !found_quad.count(ft->first)) {
1099  // now we also look for the same orientation
1100  T iv1 = mesh.pl.localize(it->second.v1);
1101  T iv2 = mesh.pl.localize(it->second.v2);
1102  T iv3 = mesh.pl.localize(it->second.v3);
1103  SF::Point ip1 = {mesh.xyz[iv1*3+0], mesh.xyz[iv1*3+1], mesh.xyz[iv1*3+2]};
1104  SF::Point ip2 = {mesh.xyz[iv2*3+0], mesh.xyz[iv2*3+1], mesh.xyz[iv2*3+2]};
1105  SF::Point ip3 = {mesh.xyz[iv3*3+0], mesh.xyz[iv3*3+1], mesh.xyz[iv3*3+2]};
1106  SF::Point e12 = normalize(ip2 - ip1);
1107  SF::Point e13 = normalize(ip3 - ip1);
1108  SF::Point in = normalize(cross(e12, e13));
1109 
1110  T fv1 = mesh.pl.localize(ft->second.v1);
1111  T fv2 = mesh.pl.localize(ft->second.v2);
1112  T fv3 = mesh.pl.localize(ft->second.v3);
1113  SF::Point fp1 = {mesh.xyz[fv1*3+0], mesh.xyz[fv1*3+1], mesh.xyz[fv1*3+2]};
1114  SF::Point fp2 = {mesh.xyz[fv2*3+0], mesh.xyz[fv2*3+1], mesh.xyz[fv2*3+2]};
1115  SF::Point fp3 = {mesh.xyz[fv3*3+0], mesh.xyz[fv3*3+1], mesh.xyz[fv3*3+2]};
1116  e12 = normalize(fp2 - fp1);
1117  e13 = normalize(fp3 - fp1);
1118  SF::Point fn = normalize(cross(e12, e13));
1119 
1120  if (inner_prod(in, fn) > 0.9) {
1121  quad_sele<T> found = ft->second; found.eidx = eidx;
1122  found_quad[ft->first] = found;
1123  }
1124  }
1125  }
1126 
1127  nod += mesh.dsp[eidx+1] - mesh.dsp[eidx];
1128  }
1129 }
1130 
1131 template<class T, class S> inline
1133  const hashmap::unordered_set<T> & tags,
1136 {
1137  const T* con = mesh.con.data();
1138  const T* nbr = mesh.get_numbering(numbering).data();
1139 
1140  bool have_tags = tags.size() > 0;
1141 
1142  vector<T> nodvec(mesh.con.size()); T* nod = nodvec.data();
1143  for(size_t i=0; i<nodvec.size(); i++)
1144  nod[i] = nbr[con[i]];
1145 
1146  vector<T> qbuff(4); // buffer if quad nodes need to be sorted
1147 
1148  for(size_t eidx = 0; eidx < mesh.l_numelem; eidx++) {
1149  if(have_tags == false || tags.count(mesh.tag[eidx])) {
1150  switch(mesh.type[eidx]) {
1151  case Tri: {
1152  triple<T> tri;
1153  tri_sele<T> trie;
1154  insert_surf_tri(nod[0], nod[1], nod[2], eidx, tri, trie, tri_surf);
1155  break;
1156  }
1157 
1158  case Quad: {
1159  quadruple<T> qd;
1160  quad_sele<T> qde;
1161  insert_surf_quad(nod[0], nod[1], nod[2], nod[3],
1162  eidx, qbuff, qd, qde, quad_surf);
1163  break;
1164  }
1165 
1166  case Tetra:
1167  insert_surf_tet(nod, eidx, tri_surf);
1168  break;
1169 
1170  case Pyramid:
1171  insert_surf_pyr(nod, eidx, qbuff, tri_surf, quad_surf);
1172  break;
1173 
1174  case Prism:
1175  insert_surf_pri(nod, eidx, qbuff, tri_surf, quad_surf);
1176  break;
1177 
1178  case Hexa:
1179  insert_surf_hex(nod, eidx, qbuff, quad_surf);
1180  break;
1181 
1182  default: break;
1183  }
1184  }
1185 
1186  nod += mesh.dsp[eidx+1] - mesh.dsp[eidx];
1187  }
1188 }
1189 
1190 template<class T, class S> inline
1194 {
1196  compute_surface(mesh, numbering, tags, tri_surf, quad_surf);
1197 }
1198 
1199 template<class V> inline
1200 void get_hashmap_duplicates(const vector<V> & data, const MPI_Comm comm,
1201  vector<bool> & is_dup)
1202 {
1203  int size, rank;
1204  MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank);
1205 
1206  size_t dsize = data.size();
1207  is_dup.assign(dsize, false);
1208 
1209  vector<int> perm, dest(dsize);
1210 
1211  // set up destinations
1212  for(size_t i=0; i<dsize; i++)
1213  dest[i] = hashmap::hash_ops<V>::hash(data[i]) % size;
1214 
1215  commgraph<size_t> grph;
1216  grph.configure(dest, comm);
1217  size_t nrecv = sum(grph.rcnt);
1218 
1219  interval(perm, 0, dsize);
1220  binary_sort_copy(dest, perm);
1221 
1222  // fill send buffer and communicate
1223  vector<V> sbuff(dsize), rbuff(nrecv);
1224  for(size_t i=0; i<dsize; i++)
1225  sbuff[i] = data[perm[i]];
1226  MPI_Exchange(grph, sbuff, rbuff, comm);
1227 
1228  // now check duplicates. values occurring multiple times should go into dup, those occurring
1229  // only once go into not_dup
1230  hashmap::unordered_set<V> not_dup, dup;
1231  vector<bool> cdup(rbuff.size());
1232 
1233  for(const V & val : rbuff) {
1234  if(not_dup.count(val) == 0 && dup.count(val) == 0)
1235  not_dup.insert(val);
1236  else {
1237  not_dup.erase(val);
1238  dup.insert(val);
1239  }
1240  }
1241 
1242  for(size_t i=0; i<nrecv; i++) {
1243  V val = rbuff[i];
1244  bool d = dup.count(val);
1245 
1246  // just a debug check
1247  assert(d == (not_dup.count(val) == 0));
1248 
1249  cdup[i] = d;
1250  }
1251 
1252  // now we communicate cdup back
1253  grph.transpose();
1254  MPI_Exchange(grph, cdup, is_dup, comm);
1255 
1256  // permute is_dup back to input permutation
1257  binary_sort_copy(perm, is_dup);
1258 }
1259 
1261 template<class K, class V> inline
1262 void remove_duplicates(hashmap::unordered_map<K,V> & map, const MPI_Comm comm)
1263 {
1264  vector<K> check_vec (map.size());
1265  vector<bool> is_dup (map.size());
1266 
1267  size_t idx=0;
1268  for(const auto & v : map) check_vec[idx++] = v.first;
1269 
1270  get_hashmap_duplicates(check_vec, comm, is_dup);
1271 
1272  for(size_t i=0; i<is_dup.size(); i++)
1273  if(is_dup[i])
1274  map.erase(check_vec[i]);
1275 }
1277 template<class K> inline
1278 void remove_duplicates(hashmap::unordered_set<K> & set, const MPI_Comm comm)
1279 {
1280  vector<K> check_vec (set.size());
1281  vector<bool> is_dup (set.size());
1282 
1283  check_vec.assign(set.begin(), set.end());
1284  get_hashmap_duplicates(check_vec, comm, is_dup);
1285 
1286  for(size_t i=0; i<is_dup.size(); i++)
1287  if(is_dup[i])
1288  set.erase(check_vec[i]);
1289 }
1290 
1291 template<class T> inline
1294  MPI_Comm comm)
1295 {
1296  long int g_num_tri = tri_surf.size();
1297  long int g_num_quad = quad_surf.size();
1298  MPI_Allreduce(MPI_IN_PLACE, &g_num_tri, 1, MPI_LONG, MPI_SUM, comm);
1299  MPI_Allreduce(MPI_IN_PLACE, &g_num_quad, 1, MPI_LONG, MPI_SUM, comm);
1300 
1301  if(g_num_tri)
1302  remove_duplicates(tri_surf, comm);
1303  if(g_num_quad)
1304  remove_duplicates(quad_surf, comm);
1305 }
1306 
1307 
1308 template<class T, class S> inline
1311  meshdata<T,S> & surfmesh,
1312  vector<T> & elem_orig)
1313 {
1314  MPI_Comm comm = surfmesh.comm;
1315 
1316  long int g_num_tri = tri_surf.size();
1317  long int g_num_quad = quad_surf.size();
1318  MPI_Allreduce(MPI_IN_PLACE, &g_num_tri, 1, MPI_LONG, MPI_SUM, comm);
1319  MPI_Allreduce(MPI_IN_PLACE, &g_num_quad, 1, MPI_LONG, MPI_SUM, comm);
1320 
1321  surfmesh.g_numelem = g_num_tri + g_num_quad;
1322  surfmesh.l_numelem = tri_surf.size() + quad_surf.size();
1323 
1324  vector<T> cnt(surfmesh.l_numelem);
1325  surfmesh.type.resize(surfmesh.l_numelem);
1326  surfmesh.con.resize(tri_surf.size() * 3 + quad_surf.size() * 4);
1327  elem_orig.resize(surfmesh.l_numelem);
1328 
1329  size_t idx = 0, cidx = 0;
1330  for(const auto & v : tri_surf) {
1331  cnt[idx] = 3;
1332  surfmesh.type[idx] = Tri;
1333  surfmesh.con[cidx + 0] = v.second.v1;
1334  surfmesh.con[cidx + 1] = v.second.v2;
1335  surfmesh.con[cidx + 2] = v.second.v3;
1336 
1337  elem_orig[idx] = v.second.eidx;
1338 
1339  idx += 1;
1340  cidx += 3;
1341  }
1342 
1343  for(const auto & v : quad_surf) {
1344  cnt[idx] = 4;
1345  surfmesh.type[idx] = Quad;
1346  surfmesh.con[cidx + 0] = v.second.v1;
1347  surfmesh.con[cidx + 1] = v.second.v2;
1348  surfmesh.con[cidx + 2] = v.second.v3;
1349  surfmesh.con[cidx + 3] = v.second.v4;
1350 
1351  elem_orig[idx] = v.second.eidx;
1352 
1353  idx += 1;
1354  cidx += 4;
1355  }
1356 
1357  dsp_from_cnt(cnt, surfmesh.dsp);
1358 }
1359 
1364 template<class T, class S> inline
1365 void convert_mesh_surface(const meshdata<T,S> & surfmesh,
1368 {
1369  vector<T> qbuff(4); // buffer if quad nodes need to be sorted
1370  quadruple<T> qd; quad_sele<T> qde;
1371  triple<T> tri; tri_sele<T> trie;
1372 
1373  for(size_t eidx=0; eidx<surfmesh.l_numelem; eidx++) {
1374  int esize = surfmesh.dsp[eidx+1] - surfmesh.dsp[eidx];
1375 
1376  const T* nod = surfmesh.con.data() + surfmesh.dsp[eidx];
1377  if(esize == 4) {
1378  insert_surf_quad(nod[0], nod[1], nod[2], nod[3], eidx, qbuff, qd, qde, quad_surf);
1379  }
1380  else {
1381  insert_surf_tri(nod[0], nod[1], nod[2], eidx, tri, trie, tri_surf);
1382  }
1383  }
1384 }
1385 
1396 template<class T, class S> inline
1398  const hashmap::unordered_set<T> & tags,
1399  meshdata<T,S> & surfmesh)
1400 {
1403  vector<T> elem_orig; // local element indices of "mesh"
1404 
1405  compute_surface(mesh, numbering, tags, tri_surf, quad_surf);
1406 
1407  surfmesh.comm = mesh.comm;
1408  remove_parallel_duplicates(tri_surf, quad_surf, surfmesh.comm);
1409  convert_surface_mesh(tri_surf, quad_surf, surfmesh, elem_orig);
1410 
1411  surfmesh.tag.resize(size_t(surfmesh.l_numelem));
1412  surfmesh.fib.resize(size_t(surfmesh.l_numelem * 3));
1413 
1414  if(mesh.she.size())
1415  surfmesh.she.resize(surfmesh.l_numelem * 3);
1416 
1417  for(size_t eidx = 0; eidx < elem_orig.size(); eidx++) {
1418  T orig = elem_orig[eidx];
1419 
1420  surfmesh.tag[eidx] = mesh.tag[orig];
1421  surfmesh.fib[eidx*3+0] = mesh.fib[orig*3+0];
1422  surfmesh.fib[eidx*3+1] = mesh.fib[orig*3+1];
1423  surfmesh.fib[eidx*3+2] = mesh.fib[orig*3+2];
1424 
1425  if(mesh.she.size()) {
1426  surfmesh.she[eidx*3+0] = mesh.she[orig*3+0];
1427  surfmesh.she[eidx*3+1] = mesh.she[orig*3+1];
1428  surfmesh.she[eidx*3+2] = mesh.she[orig*3+2];
1429  }
1430  }
1431 }
1432 
1442 template<class T, class S> inline
1444  meshdata<T,S> & surfmesh)
1445 {
1447  compute_surface_mesh(mesh, numbering, tags, surfmesh);
1448 }
1449 
1450 template<class T, class S> inline
1452  meshdata<T,S> & surface,
1453  std::string filename)
1454 {
1455  int rank; MPI_Comm_rank(mesh.comm, &rank);
1456  FILE *fd = NULL;
1457 
1458  size_t numele = 0;
1459  size_t numcon = 0;
1460  int nFib = 0;
1461  bool read_bin = false;
1462 
1463  if(rank == 0)
1464  {
1465  fd = fopen(filename.c_str(), "r");
1466  if(fd)
1467  SF::read_headers(fd, NULL, read_bin, numele, nFib);
1468  }
1469  MPI_Bcast(&numele, sizeof(size_t), MPI_BYTE, 0, mesh.comm);
1470  surface.g_numelem = numele;
1471 
1472  // We read the whole surface at once, this could be rewritten into multiple reads to
1473  // save memory
1474  if(rank == 0) {
1475  SF::read_elem_block(fd, read_bin, 0, numele, surface);
1476 
1477  numcon = surface.con.size();
1478 
1479  if(numele != surface.l_numelem) {
1480  std::cerr << "Error: Incomplete surface file! Aborting!" << std::endl;
1481  fclose(fd);
1482  exit(1);
1483  }
1484  }
1485 
1486  surface.l_numelem = numele;
1487 
1488  surface.tag.resize(surface.l_numelem);
1489  MPI_Bcast(surface.tag.data(), surface.tag.size()*sizeof(T), MPI_BYTE, 0, mesh.comm);
1490 
1491  surface.dsp.resize(surface.l_numelem + 1);
1492  MPI_Bcast(surface.dsp.data(), surface.dsp.size()*sizeof(T), MPI_BYTE, 0, mesh.comm);
1493 
1494  surface.type.resize(surface.l_numelem);
1495  MPI_Bcast(surface.type.data(), surface.type.size()*sizeof(elem_t), MPI_BYTE, 0, mesh.comm);
1496 
1497  vector<T> & ref_eidx = surface.register_numbering(NBR_ELEM_REF);
1498  ref_eidx.resize(surface.l_numelem);
1499 
1500  MPI_Bcast(ref_eidx.data(), surface.l_numelem*sizeof(T), MPI_BYTE, 0, mesh.comm);
1501 
1502  MPI_Bcast(&numcon, sizeof(size_t), MPI_BYTE, 0, mesh.comm);
1503  surface.con.resize(numcon);
1504  MPI_Bcast(surface.con.data(), surface.con.size()*sizeof(T), MPI_BYTE, 0, mesh.comm);
1505 
1506  // now we have to restrict the surface elements to those in the local partition
1507  {
1508  size_t initial_gnumelem = surface.g_numelem;
1509 
1510  hashmap::unordered_map<triple<T>, tri_sele<T>> search_tri, found_tri;
1511  hashmap::unordered_map<quadruple<T>, quad_sele<T>> search_quad, found_quad;
1512 
1513  convert_mesh_surface(surface, search_tri, search_quad);
1514  search_for_surface (mesh, SF::NBR_REF, search_tri, search_quad, found_tri, found_quad);
1515 
1516  vector<T> elem_orig;
1517  convert_surface_mesh(found_tri, found_quad, surface, elem_orig);
1518  mesh.pl.localize(surface.con);
1519 
1520  surface.tag.resize(size_t(surface.l_numelem));
1521  surface.fib.resize(size_t(surface.l_numelem * 3));
1522 
1523  if(mesh.she.size())
1524  surface.she.resize(surface.l_numelem * 3);
1525 
1526  for(size_t eidx = 0; eidx < elem_orig.size(); eidx++) {
1527  T orig = elem_orig[eidx];
1528 
1529  surface.tag[eidx] = mesh.tag[orig];
1530  surface.fib[eidx*3+0] = mesh.fib[orig*3+0];
1531  surface.fib[eidx*3+1] = mesh.fib[orig*3+1];
1532  surface.fib[eidx*3+2] = mesh.fib[orig*3+2];
1533 
1534  if(mesh.she.size()) {
1535  surface.she[eidx*3+0] = mesh.she[orig*3+0];
1536  surface.she[eidx*3+1] = mesh.she[orig*3+1];
1537  surface.she[eidx*3+2] = mesh.she[orig*3+2];
1538  }
1539  }
1540 
1541  // we want to remember to original gnumelems, so that the next consistency check is
1542  // actually useful.
1543  surface.g_numelem = initial_gnumelem;
1544  }
1545 
1546  {
1547  long int numele_check = surface.l_numelem;
1548  MPI_Allreduce(MPI_IN_PLACE, &numele_check, 1, MPI_LONG, MPI_SUM, mesh.comm);
1549  if(numele_check != surface.g_numelem) {
1550  if(rank == 0)
1551  fprintf(stderr, "ERROR: Bad partitioning of surface %s!"
1552  " Global elem sum should be %ld, but is %ld!\n\n",
1553  filename.c_str(), surface.g_numelem, numele_check);
1554  }
1555  }
1556 
1557  // copy indexing from parent mesh
1558  SF::vector<T> & nod_ref = surface.register_numbering(SF::NBR_REF);
1559  SF::vector<T> & nod_petsc = surface.register_numbering(SF::NBR_PETSC);
1560  nod_ref = mesh.get_numbering(SF::NBR_REF);
1561  nod_petsc = mesh.get_numbering(SF::NBR_PETSC);
1562 
1563  // copy coords from parent mesh
1564  surface.l_numpts = mesh.l_numpts;
1565  surface.g_numpts = mesh.g_numpts;
1566  surface.xyz = mesh.xyz;
1567 }
1568 
1569 template<class T>
1571 {
1572  size_t widx = 0;
1573 
1574  for(size_t i=0; i<v.size(); i++)
1575  if(set.count(v[i]))
1576  v[widx++] = v[i];
1577 
1578  v.resize(widx);
1579 }
1580 template<class T>
1581 inline void restrict_to_set(vector<T> & v, const vector<T> & vset)
1582 {
1584  set.insert(vset.begin(), vset.end());
1585 
1586  restrict_to_set(v, set);
1587 }
1588 
1589 }
1590 
1591 #endif
Basic containers.
void sort_triple(const T in1, const T in2, const T in3, T &out1, T &out2, T &out3)
sort the "in" triple into the "out" triple
Definition: SF_container.h:878
Functions related to mesh IO.
Functions related to network communication.
Classes related to mesh node renumbering.
Classes and algorithms related to the layout of distributed meshes.
Various sorting algorithms.
The vector class and related algorithms.
Class used to plot functions on the terminal.
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 transpose()
transpose comm graph (receive becomes send, and vice versa)
Definition: SF_container.h:648
void scale(V fac)
scale comm graph layout data
Definition: SF_container.h:638
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
size_t g_numpts
global number of points
Definition: SF_container.h:385
vector< T > dsp
connectivity starting index of each element
Definition: SF_container.h:401
vector< S > she
sheet direction
Definition: SF_container.h:406
vector< S > fib
fiber direction
Definition: SF_container.h:405
size_t l_numelem
local number of elements
Definition: SF_container.h:384
vector< elem_t > type
element type
Definition: SF_container.h:403
std::map< SF_nbr, vector< T > > nbr
container for different numberings
Definition: SF_container.h:411
std::string name
the mesh name
Definition: SF_container.h:392
vector< T > & register_numbering(SF_nbr nbr_type)
Register a new numbering to the mesh and return the associated index vector.
Definition: SF_container.h:429
vector< T > con
Definition: SF_container.h:397
vector< S > xyz
node cooridnates
Definition: SF_container.h:412
size_t l_numpts
local number of points
Definition: SF_container.h:386
size_t g_numelem
global number of elements
Definition: SF_container.h:383
void localize(SF_nbr nbr_type)
Localize the connectivity data w.r.t. a given numbering.
Definition: SF_container.h:481
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
vector< T > tag
element tag
Definition: SF_container.h:402
hashmap::unordered_set< int > extr_tag
the element tags based on which the mesh has been extracted
Definition: SF_container.h:409
The abstract numbering class.
Definition: SF_numbering.h:35
Functor class applying a submesh renumbering.
Definition: SF_numbering.h:55
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 assign(InputIterator s, InputIterator e)
Assign a memory range.
Definition: SF_vector.h:146
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
hm_int count(const K &key) const
Check if key exists.
Definition: hashmap.hpp:612
size_t size() const
Definition: hashmap.hpp:720
hm_int erase(const K &key)
Erase by key.
Definition: hashmap.hpp:598
Custom unordered_set implementation.
Definition: hashmap.hpp:739
size_t size() const
Definition: hashmap.hpp:1141
hm_int erase(const K &key)
Definition: hashmap.hpp:1053
hm_int count(const K &key) const
Definition: hashmap.hpp:1067
void insert(InputIterator first, InputIterator last)
Definition: hashmap.hpp:1037
Ascii matrix graph plotter.
void print(const VEC &cnt, const VEC &col, char s)
Print a matrix graph to stdout.
Definition: dense_mat.hpp:19
void extract_tagbased(const meshdata< T, S > &mesh, meshdata< T, S > &submesh)
Extract a submesh based on element tags.
void cnt_from_dsp(const vector< T > &dsp, vector< T > &cnt)
Compute counts from displacements.
Definition: SF_vector.h:304
void dsp_from_cnt(const vector< T > &cnt, vector< T > &dsp)
Compute displacements from counts.
Definition: SF_vector.h:295
void compute_surface_mesh(const meshdata< T, S > &mesh, const SF_nbr numbering, const hashmap::unordered_set< T > &tags, meshdata< T, S > &surfmesh)
Compute the surface of a given mesh.
void print_DD_info(const meshdata< T, S > &mesh)
Print some basic information on the domain decomposition of a mesh.
void get_hashmap_duplicates(const vector< V > &data, const MPI_Comm comm, vector< bool > &is_dup)
void read_headers(FILE *ele_fd, FILE *fib_fd, bool read_binary, size_t &numelem, int &nFib)
Read the header from the element and fiber files.
Definition: SF_mesh_io.h:75
void write_pts_block(FILE *&fd, bool write_binary, const vector< S > &xyz)
Write a chunk of points to a file.
Definition: SF_mesh_io.h:911
void read_surface_mesh(const meshdata< T, S > &mesh, meshdata< T, S > &surface, std::string filename)
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 make_global(const vector< T > &vec, vector< T > &out, MPI_Comm comm)
make a parallel vector global
Definition: SF_network.h:210
void unique_accumulate(vector< T > &_P, vector< S > &_A)
Definition: SF_sort.h:399
void convert_surface_mesh(hashmap::unordered_map< triple< T >, tri_sele< T >> &tri_surf, hashmap::unordered_map< quadruple< T >, quad_sele< T > > &quad_surf, meshdata< T, S > &surfmesh, vector< T > &elem_orig)
void rebalance_mesh(meshdata< T, S > &mesh)
Rebalance the parallel distribution of a mesh, if a local size is 0.
void extract_mesh(const vector< bool > &keep, const meshdata< T, S > &mesh, meshdata< T, S > &submesh)
Extract a submesh from a given mesh.
void search_for_surface(const meshdata< T, S > &mesh, const SF_nbr numbering, const hashmap::unordered_map< triple< T >, tri_sele< T > > &search_tri, const hashmap::unordered_map< quadruple< T >, quad_sele< T > > &search_quad, hashmap::unordered_map< triple< T >, tri_sele< T > > &found_tri, hashmap::unordered_map< quadruple< T >, quad_sele< T > > &found_quad)
void permute_mesh(const meshdata< T, S > &inmesh, meshdata< T, S > &outmesh, const vector< T > &perm)
Permute the element data of a mesh based on a given permutation.
Definition: SF_mesh_utils.h:41
double inner_prod(const Point &a, const Point &b)
Definition: SF_container.h:75
void sort_parallel(MPI_Comm comm, const vector< T > &idx, vector< T > &out_idx)
Sort index values parallel ascending across the ranks.
void write_elements(const meshdata< T, S > &mesh, bool binary, std::string basename)
Read the element data (elements and fibers) of a CARP mesh.
Definition: SF_mesh_io.h:760
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 insert_surf_pyr(const T *nod, const size_t eidx, vector< T > &buff, hashmap::unordered_map< triple< T >, tri_sele< T > > &surfmap, hashmap::unordered_map< quadruple< T >, quad_sele< T > > &qsurfmap)
void unique_resize(vector< T > &_P)
Definition: SF_sort.h:338
void count(const vector< T > &data, vector< S > &cnt)
Count number of occurrences of indices.
Definition: SF_vector.h:317
void write_pts_header(FILE *&pts_fd, bool binary, size_t numpts)
Write the header of the points file.
Definition: SF_mesh_io.h:167
T global_min(const vector< T > &vec, MPI_Comm comm)
Compute the global minimum of a distributed vector.
Definition: SF_network.h:111
void read_elem_block(FILE *&fd, bool read_binary, size_t bstart, size_t bsize, meshdata< T, S > &mesh)
Read a block of size bsize from an CARP element file.
Definition: SF_mesh_io.h:194
void insert_surf_hex(const T *nod, const size_t eidx, vector< T > &buff, hashmap::unordered_map< quadruple< T >, quad_sele< T > > &surfmap)
void redistribute_elements(meshdata< T, S > &mesh, meshdata< T, S > &sendbuff, vector< T > &part)
Redistribute the element data of a parallel mesh among the ranks based on a partitioning.
void write_points_parallel(const meshdata< T, S > &mesh, bool binary, std::string basename)
Write a parallel mesh to harddisk without gathering it on one rank.
Point normalize(const Point &vect)
Definition: SF_container.h:101
void gather_mesh(const meshdata< T, S > &locmesh, meshdata< T, S > &globmesh)
Gather a mesh on rank 0.
void restrict_to_set(vector< T > &v, const hashmap::unordered_set< T > &set)
void compute_surface(const meshdata< T, S > &mesh, const SF_nbr numbering, const hashmap::unordered_set< T > &tags, hashmap::unordered_map< triple< T >, tri_sele< T > > &tri_surf, hashmap::unordered_map< quadruple< T >, quad_sele< T > > &quad_surf)
void global_to_local(const vector< T > &glob, vector< T > &data, bool sortedData, bool doWarn)
Definition: SF_sort.h:530
void insert_surf_pri(const T *nod, const size_t eidx, vector< T > &buff, hashmap::unordered_map< triple< T >, tri_sele< T > > &surfmap, hashmap::unordered_map< quadruple< T >, quad_sele< T > > &qsurfmap)
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 insert_surf_tri(T n1, T n2, T n3, size_t eidx, triple< T > &surf, tri_sele< T > &sele, hashmap::unordered_map< triple< T >, tri_sele< T > > &surfmap)
void remove_parallel_duplicates(hashmap::unordered_map< triple< T >, tri_sele< T >> &tri_surf, hashmap::unordered_map< quadruple< T >, quad_sele< T > > &quad_surf, MPI_Comm comm)
void convert_mesh_surface(const meshdata< T, S > &surfmesh, hashmap::unordered_map< triple< T >, tri_sele< T >> &tri_surf, hashmap::unordered_map< quadruple< T >, quad_sele< T >> &quad_surf)
void binary_sort(vector< T > &_V)
Definition: SF_sort.h:274
T global_max(const vector< T > &vec, MPI_Comm comm)
Compute the global maximum of a distributed vector.
Definition: SF_network.h:141
void extract_myocardium(const meshdata< T, S > &mesh, meshdata< T, S > &submesh, bool require_fibers=true)
Extract the myocardium submesh.
Point cross(const Point &a, const Point &b)
cross product
Definition: SF_container.h:69
void write_mesh_parallel(const meshdata< T, S > &mesh, bool binary, std::string basename)
elem_t
element type enum
Definition: SF_container.h:38
@ Tri
Definition: SF_container.h:45
@ Prism
Definition: SF_container.h:43
@ Pyramid
Definition: SF_container.h:42
@ Tetra
Definition: SF_container.h:39
@ Quad
Definition: SF_container.h:44
@ Hexa
Definition: SF_container.h:40
void insert_surf_quad(T n1, T n2, T n3, T n4, size_t eidx, vector< T > &buff, quadruple< T > &surf, quad_sele< T > &sele, hashmap::unordered_map< quadruple< T >, quad_sele< T > > &surfmap)
void redistribute_mesh(meshdata< T, S > &mesh, vector< T > &part)
Redistribute both element and vertex data of a mesh.
void print_mesh_graph(meshdata< T, S > &mesh)
One-by-one each process prints the graph of a given mesh.
void inter_domain_mapping(const meshdata< T, S > &mesh_a, const meshdata< T, S > &mesh_b, const SF_nbr snbr, index_mapping< T > &a_to_b)
Submesh index mapping between different domains/meshes.
void nodal_connectivity_graph(const meshdata< T, S > &mesh, vector< T > &n2n_cnt, vector< T > &n2n_con)
Compute the node-to-node connectivity.
Definition: SF_container.h:568
SF_nbr
Enumeration encoding the different supported numberings.
Definition: SF_container.h:185
@ NBR_PETSC
PETSc numbering of nodes.
Definition: SF_container.h:188
@ NBR_ELEM_REF
The element numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:189
@ NBR_REF
The nodal numbering of the reference mesh (the one stored on HD).
Definition: SF_container.h:186
@ NBR_SUBMESH
Submesh nodal numbering: The globally ascending sorted reference indices are reindexed.
Definition: SF_container.h:187
void remove_duplicates(hashmap::unordered_map< K, V > &map, const MPI_Comm comm)
remove parallel duplicates from a hashmap::unordered_map
void insert_surf_tet(const T *nod, const size_t eidx, hashmap::unordered_map< triple< T >, tri_sele< T > > &surfmap)
Point and vector struct.
Definition: SF_container.h:50
Base hashing class.
Definition: hashmap.hpp:73