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