openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
mesher.cc
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 
37 #include <iostream>
38 #include <fstream>
39 #include <cstring>
40 #include <string>
41 #include <cmath>
42 #include <stdlib.h>
43 #include <limits>
44 #include <algorithm>
45 
46 #include "vect.h"
47 #include "mesher_p.h"
48 #include "mesher_d.h"
49 
50 
51 using namespace opencarp;
52 
53 typedef enum {Myocardium=1, Isobath, Anisobath } region_t;
54 
55 #define BOX_CENTERS_GRID false
56 #define NODE_GRID true
57 
58 Point p_assign_array( float *p )
59 {
60  Point a;
61  a.x = p[0]; a.y=p[1]; a.z=p[2];
62  return a;
63 }
64 
65 const Point e_circ = {1,0,0};
66 const Point e_long = {0,1,0};
67 const Point e_rad = {0,1,0};
68 
69 
70 
71 const float CM2UM=1.e4;
72 
73 class TisAxes {
74  public:
75  void set_xi(float xi_) { xi=xi_; }
76  void set_axes(float alpha_, float beta_pr_, float gamma_);
77  void set_bath_axes(bool);
78  Point fiber(void) {return f;}
79  Point sheet(void) {return s;}
80  private:
81  float xi;
82  float alpha;
83  float beta_pr;
84  float gamma;
85  Point f;
86  Point s;
87  Point sn;
88 };
89 
90 void
91 TisAxes::set_axes(float b, float c, float d)
92 {
93  alpha = b/180*M_PI;
94  beta_pr = c/180*M_PI;
95  gamma = d/180*M_PI;
96 
97  Point p(cos(alpha), sin(alpha), sin(gamma));
98  f = normalize(p);
99 
100  Point sp(0.0, sin(double(beta_pr)), cos(double(beta_pr)));
101  float lambda = -dot(f, sp)/dot(f, e_circ);
102  s = normalize(sp + scal_X(e_circ,lambda));
103 }
104 
105 void
106 TisAxes::set_bath_axes(bool aniso_bath)
107 {
108  f.y = f.z = 0.0;
109  s.x = s.y = s.z = 0.0;
110 
111  f.x = aniso_bath?1.0:0.0;
112 }
113 
114 class Region {
115  public:
116  Region(Point p, int b): p0_(p), bath_(b), tag_(-1) {}
117  virtual ~Region() {}
118  virtual bool inside(Point) = 0;
119  bool isbath() { return bath_; }
120  int tag() { return tag_; }
121  void tag(int t) { tag_ = t; }
122  protected:
124  int bath_;
125  int tag_;
126 };
127 
128 class BlockRegion: public Region {
129  public:
130  BlockRegion(Point p0, Point p, int bth): Region(p0, bth), p1_(p) {}
131  virtual bool inside(Point p);
132  private:
133  Point p1_;
134 };
135 
136 class SphericalRegion: public Region {
137  public:
138  SphericalRegion(Point ctr, float r, int bth):
139  Region(ctr, bth), radius2_(r*r) {}
140  virtual bool inside(Point p) { return dist_2(p, p0_)<= radius2_; }
141  private:
142  float radius2_; //square of radius
143 };
144 
145 bool
147 {
148  return p.x >= p0_.x && p.x <= p1_.x && p.y >= p0_.y && p.y <= p1_.y &&
149  p.z >= p0_.z && p.z <= p1_.z;
150 }
151 
152 
153 class CylindricalRegion: public Region {
154  public:
155  CylindricalRegion(Point origin, Point dir, float r, float l, int bth):
156  Region(origin, bth), radius2_(r*r)
157  {axis_=normalize(dir);len_=(l==0.)?1.e36:l;}
158  virtual bool inside( Point p );
159  private:
160  Point axis_;
161  float radius2_;
162  float len_;
163 };
164 
165 bool
167 {
168  Point R = p - p0_;
169  float d = dot(R, axis_);
170 
171  return d>=0 && d<=len_ && mag2(R)-d*d <= radius2_;
172 }
173 
174 
175 class Element {
176  public:
177  Element(int N, const char *t):n_(N),p_(new int[N]),type_(t){}
178  ~Element() { delete[] p_; }
179  Point centre(Point *ctr);
180  int num(){ return n_; };
181  friend std::ostream& operator<<(std::ostream &, Element& );
183  protected:
184  int n_;
185  int *p_;
186  std::string type_;
187 };
188 
189 
191  Point result = ctr[p_[0]];
192  for( int i=1; i<n_; i++ ) result = result + ctr[p_[i]];
193  return scal_X( result, 1./(float)n_);
194 }
195 
196 
197 class Tetrahedron:public Element {
198  public:
199  Tetrahedron( int A, int B, int C, int D ): Element(4,"Tt") {
200  p_[0]=A; p_[1]=B; p_[2]=C; p_[3]=D; }
201  void chkNegVolume(Point *pts);
202 };
203 
204 
206  Point P01 = pts[p_[1]] - pts[p_[0]];
207  Point P02 = pts[p_[2]] - pts[p_[0]];
208  Point P03 = pts[p_[3]] - pts[p_[0]];
209  double vol = det3(P01,P02,P03); // 6x volume
210  if( vol < 0. )
211  std::swap(p_[0],p_[1]);
212 }
213 
214 class Hexahedron:public Element {
215  public:
216  Hexahedron( int A, int B, int C, int D, int E, int F, int G, int H ): Element(8,"Hx") {
217  p_[0]=A; p_[1]=B; p_[2]=C; p_[3]=D; p_[4]=E; p_[5]=F; p_[6]=G; p_[7]=H; }
218 };
219 
220 
221 class Quadrilateral:public Element {
222  public:
223  Quadrilateral( int A, int B, int C, int D ): Element(4,"Qd") {
224  p_[0] = A; p_[1] = B; p_[2] = C; p_[3] = D;
225  }
226 };
227 
228 
229 class Triangle:public Element {
230  public:
231  Triangle( int A, int B, int C ): Element(3,"Tr") {
232  p_[0] = A; p_[1] = B; p_[2] = C;
233  }
234 };
235 
236 
237 class Line:public Element {
238  public:
239  Line( int A, int B ): Element(2,"Ln"){p_[0]=A; p_[1]=B;}
240 };
241 
242 
243 std::ostream& operator<<( std::ostream& out, Element &e ) {
244  out << e.type_ << " ";
245  for( int i=0; i<e.num()-1; i++ )
246  out << e.p_[i] << " ";
247  out << e.p_[e.num()-1];
248  return out;
249 }
250 
251 std::ostream& operator<<( std::ostream& out, Point p ) {
252  out << p.x << " " << p.y << " " << p.z;
253  return out;
254 }
255 
256 
257 class tmProfile {
258  public:
259  ~tmProfile() { free(xi); free(ang); }
260  int read(char *fname);
261  float lookup(float xi);
262  int linear(float ang_endo, float ang_epi, float z_endo, float z_epi );
263  private:
264  int N;
265  float *xi;
266  float *ang;
267 };
268 
269 
270 int
271 tmProfile::linear (float ang_endo,float ang_epi, float z_endo, float z_epi)
272 {
273  float dz = 10; // sample the profile at a 10 um resolution
274  if( z_epi==z_endo ) // 2D case
275  N = 0;
276  else
277  N = (z_epi-z_endo)/dz;
278 
279  xi = (float *)malloc(sizeof(float)*(N+1));
280  ang = (float *)malloc(sizeof(float)*(N+1));
281 
282  if(xi==NULL || ang==NULL) {
283  std::cerr << "Memory allocation failed." << std::endl;
284  return -1;
285  }
286  else if( !N ) { // 2D
287  xi[0] = 0;
288  ang[0] = ang_endo;
289  } else {
290  float K = (ang_epi-ang_endo)/(z_epi-z_endo);
291  for(int i=0;i<=N;i++) {
292  float z = z_endo+i*dz;
293  xi[i] = (z-z_endo)/(z_epi-z_endo)-0.5;
294  ang[i] = ang_endo + K*(z-z_endo);
295  }
296  }
297  return 0;
298 }
299 
300 float
302 {
303  if( !N ) return ang[0];
304 
305  int i=0;
306  while(xi[i]<xi_ && i<N) i++;
307 
308  if((i==0)|| (i==N))
309  return ang[i];
310  else
311  return ang[i-1]+(ang[i]-ang[i-1])/(xi[i]-xi[i-1])*(xi_-xi[i-1]);
312 }
313 
314 int
315 tmProfile::read(char *fname)
316 {
317  FILE *profile = fopen(fname,"rt");
318  if(profile==NULL) {
319  fprintf( stderr, "Can't open transmural profile data file %s.\n", fname);
320  return -1;
321  }
322 
323  int err = fscanf(profile,"%d",&N);
324  xi = (float *)malloc(sizeof(float)*N);
325  ang = (float *)malloc(sizeof(float)*N);
326  for(int i=0; i<N; i++) {
327  xi[i] = (float)i/(float)(N-1)-0.5;
328  err = fscanf(profile,"%f",ang+i);
329  }
330  fclose(profile);
331 
332  return err;
333 }
334 
335 class BBoxDef {
336  public:
337  void update( Point p );
338 // private:
339  float xd = 0;
342  float yd = 0;
345  float zd = 0;
348 };
349 
350 void
352 {
353  if(p.x<x_mn) x_mn = p.x;
354  if(p.y<y_mn) y_mn = p.y;
355  if(p.z<z_mn) z_mn = p.z;
356  if(p.x>x_mx) x_mx = p.x;
357  if(p.y>y_mx) y_mx = p.y;
358  if(p.z>z_mx) z_mx = p.z;
359 }
360 
361 
362 class BoundingBox {
363  public:
364  virtual ~BoundingBox() { delete[] bx; delete[] res; }
365  void init( int *_bx, float *_res, Point p0);
366  void dims(void);
367  float z2xi(float z, bool nodeGrid);
368  float mn_z(bool nodeGrid){ return nodeGrid?nodes.z_mn:bctrs.z_mn; };
369  float mx_z(bool nodeGrid){ return nodeGrid?nodes.z_mx:bctrs.z_mx; };
370  public:
371  int *bx;
372  int bx_inds[3][2];
373  protected:
374  float *res;
377 };
378 
379 float
380 BoundingBox::z2xi (float z, bool nodeGrid)
381 {
382  float zd = nodeGrid?nodes.zd:bctrs.zd;
383  float z_mn = nodeGrid?nodes.z_mn:bctrs.z_mn;
384 
385  if(zd==0.)
386  return 0.;
387  else
388  return (z-z_mn)/zd-0.5;
389 }
390 
391 void
392 BoundingBox::init(int *_bx, float *_res, Point p0)
393 {
394  bx = new int [3];
395  res = new float [3];
396  for(int i=0;i<3;i++) {
397  bx [i] = _bx [i];
398  res[i] = _res[i];
399  bx_inds[i][0] = 0;
400  bx_inds[i][1] = bx[i]-1;
401  }
402 
403  // min corner of nodal bbx
404  nodes.update(p0);
405 
406  // min corner of center bbx
407  Point pc0 = p0;
408  pc0.x = p0.x + res[0]/2;
409  pc0.y = p0.y + res[1]/2;
410  pc0.z = p0.z + res[2]/2;
411  bctrs.update(pc0);
412 
413  // max corner of nodal bbx
414  Point p1 = p0;
415  p1.x = p0.x+bx[0]*res[0];
416  p1.y = p0.y+bx[1]*res[1];
417  p1.z = p0.z+bx[2]*res[2];
418  nodes.update(p1);
419 
420  // max corner of center bbx
421  Point pc1 = p1;
422  pc1.x = p1.x - res[0]/2;
423  pc1.y = p1.y - res[1]/2;
424  pc1.z = p1.z - res[2]/2;
425  bctrs.update(pc1);
426 
427  dims();
428 }
429 
430 void
432 {
433  // figure out dims in terms of nodes
434  nodes.xd = nodes.x_mx - nodes.x_mn;
435  nodes.yd = nodes.y_mx - nodes.y_mn;
436  nodes.zd = nodes.z_mx - nodes.z_mn;
437 
438  // and in terms of box centers
439  bctrs.xd = bctrs.x_mx - bctrs.x_mn;
440  bctrs.yd = bctrs.y_mx - bctrs.y_mn;
441  bctrs.zd = bctrs.z_mx - bctrs.z_mn;
442 }
443 
444 class fibDef {
445  public:
446  void setFiberDefs(char *f_prof, float fEndo, float fEpi, float imbr,
447  char *s_prof, float sEndo, float sEpi);
448  bool withSheets(void);
449  char *f_name(void) { return f_Prof; };
450  char *s_name(void) { return s_Prof; };
451  float imbrication() { return f_imbr; };
452  float rotEndo() { return f_Endo; };
453  float rotEpi() { return f_Epi; };
454  float sheetEndo() { return s_Endo; };
455  float sheetEpi() { return s_Epi; };
456 
457  private:
458  char *f_Prof;
459  float f_Endo;
460  float f_Epi;
461  float f_imbr;
462  char *s_Prof;
463  float s_Endo;
464  float s_Epi;
465 };
466 
467 void fibDef::setFiberDefs(char *f_prof, float fEndo, float fEpi, float imbr,
468  char *s_prof, float sEndo, float sEpi)
469 {
470  f_Prof = strdup(f_prof);
471  f_Endo = fEndo;
472  f_Epi = fEpi;
473  f_imbr = imbr;
474  s_Prof = strdup(s_prof);
475  s_Endo = sEndo;
476  s_Epi = sEpi;
477 }
478 
479 bool
481 {
482  if((f_imbr!=0.0) || (s_Endo!=0.0) || (s_Epi!=0.0) || (strcmp(s_Prof,"")))
483  return true;
484  else
485  return false;
486 
487 }
488 
489 
490 class Grid {
491  public:
492  Grid( char *, Region **, int );
493  virtual ~Grid() {}
494  void unPrMFiberDefs(void);
495  virtual void build_mesh(float*,float*,float*,bool *,float*,float,bool,int)=0;
496  virtual void output_boundary( char * )=0;
497  void add_element( Element &, region_t );
498  void set_indx_bounds(bool *sym);
499  bool chk_bath( int i, int j, int k );
500  bool os_good();
501  protected:
503  int npt;
506  std::ofstream pt_os, elem_os, lon_os, elemc_os, vec_os;
508  int num_axes;
512  int dim;
513 };
514 
515 class Grid2D : public Grid {
516  public:
517  virtual void build_mesh(float*, float*, float*, bool *, float*, float, bool, int);
518  virtual void output_boundary( char *fn ){};
519  Grid2D( char *m, Region **r ):Grid(m,r,2){}
520  private:
521  void add_tri( int, int, int );
522 };
523 
524 class Grid3D : public Grid {
525  public:
526  virtual void build_mesh(float*, float*, float*, bool *, float*, float, bool, int);
527  virtual void output_boundary( char *fn ){}
528  Grid3D( char *m, Region **r ):Grid(m,r,3){}
529  private:
530  void add_tet( int, int, int, int );
531 };
532 
533 class Grid1D : public Grid {
534  public:
535  virtual void build_mesh(float*, float*, float*, bool *, float*, float, bool, int);
536  virtual void output_boundary( char *fn ){}
537  Grid1D( char *m, Region **r ):Grid(m,r,1){}
538  private:
539  void add_line( int, int, int, int );
540 };
541 
542 
544 {
545  return pt_os.good() && lon_os.good() && elem_os.good();
546 }
547 
548 
549 Grid::Grid( char *msh, Region **r, int d ): region(r),dim(d)
550 {
551  std::string fname(msh);
552  fname = msh;
553  fname += ".lon";
554  lon_os.open(fname.c_str());
555 
556  fname = msh;
557  fname += ".pts";
558  pt_os.open(fname.c_str());
559 
560  fname = msh;
561  fname += ".elem";
562  elem_os.open(fname.c_str());
563 
564  fname = msh;
565  fname += ".vpts";
566  elemc_os.open(fname.c_str());
567 
568  fname = msh;
569  fname += ".vec";
570  vec_os.open(fname.c_str());
571 }
572 
573 
580 void
582 {
583  for(int i=0;i<3;i++) {
584  if(b_bbx.bx[i]>t_bbx.bx[i]) {
585  t_bbx.bx_inds[i][0] = 0;
586  if(sym[i])
587  t_bbx.bx_inds[i][0] = (b_bbx.bx[i]-t_bbx.bx[i])/2;
588  t_bbx.bx_inds[i][1] = t_bbx.bx_inds[i][0]+t_bbx.bx[i]-1;
589  }
590  }
591 }
592 
593 
602 bool
603 Grid::chk_bath(int i, int j, int k)
604 {
605  bool bth[] = { false, false, false };
606  int idx[] = { i, j, k };
607 
608  for(int cnt=0; cnt<dim; cnt++ )
609  if( idx[cnt]<t_bbx.bx_inds[cnt][0] || idx[cnt]>t_bbx.bx_inds[cnt][1] )
610  return true;
611 
612  return false;
613 }
614 
615 
616 void
618 {
619  f_def.setFiberDefs(param_globals::fibers.tm_fiber_profile,
620  param_globals::fibers.rotEndo,
621  param_globals::fibers.rotEpi,
622  param_globals::fibers.imbrication,
623  param_globals::fibers.tm_sheet_profile,
624  param_globals::fibers.sheetEndo,
625  param_globals::fibers.sheetEpi);
626 }
627 
633 void
635 {
636  elem_os << elem;
637  Point c = elem.centre(pt);
638  elemc_os << c << std::endl;
639  int r=-1;
640  int regid = regtype==Myocardium?1:0;
641  while( region[++r] != NULL ) {
642  if( region[r]->inside( c ) ) {
643  if( region[r]->isbath() ) { // myo or bath becomes bath
644  regid = region[r]->tag()<0 ? 0 : region[r]->tag();
645  regtype = region[r]->isbath()==1?Isobath:Anisobath;
646  } else if( regtype == Myocardium ) { // only myo becomes other myo
647  regid = region[r]->tag()<0 ? r+2 : region[r]->tag();
648  }
649  if(param_globals::first_reg)
650  break;
651  }
652  }
653 
654  if(regtype==Myocardium) {
655  float xi = t_bbx.z2xi(c.z,BOX_CENTERS_GRID);
656  elem.ax.set_xi(xi);
657  elem.ax.set_axes( f_xi.lookup(xi),s_xi.lookup(xi),f_def.imbrication());
658  }
659  else {
660  elem.ax.set_bath_axes(regtype==Anisobath);
661  if(regtype==Anisobath)
662  regid *= -1;
663  }
664 
665  elem_os << " " << regid << std::endl;
666  if(num_axes>1)
667  lon_os << elem.ax.fiber() << " " << elem.ax.sheet() << std::endl;
668  else
669  lon_os << elem.ax.fiber() << std::endl;
670 
671  // write fiber to vector file
672  vec_os << elem.ax.fiber() << std::endl;
673 }
674 
675 
688 void
689 Grid1D::build_mesh(float* x0, float* x, float* tissue, bool *sym, float *res,
690  float pert, bool aniso_bath, int periodic_bc)
691 {
692  bool eletype;
693  int p1, p2, p3, p4;
694  int bnx[3]; // number of cubes in each direction
695  int tnx[3]; // number of tissue cubes in each direction
696 
697  // determine number of boxes
698  for( int i=0; i<3; i++ ) {
699  bnx[i] = (int)(x[i]/res[i]);
700  tnx[i] = (int)(tissue[i]/res[i]);
701  }
702 
703  // determine origins for tissue and bath bounding box
704  Point pt0, pb0;
705  pt0.x = -tnx[0]*res[0]/2 + x0[0];
706  pt0.y = 0;
707  pt0.z = 0;
708 
709  pb0.x = sym[0]?pt0.x-(bnx[0]-tnx[0])*res[0]/2:pt0.x+x0[0];
710  pb0.y = 0.0;
711  pb0.z = 0.0;
712 
713  // determine bounding box of bath and tissue grids
714  b_bbx.init(bnx,res,pb0);
715  t_bbx.init(tnx,res,pt0);
716 
717  // set tissue index boundaries relative to bath grid
718  set_indx_bounds(sym);
719 
720  f_xi.linear( f_def.rotEndo(), f_def.rotEpi(), 0, 0 );
721  s_xi.linear( f_def.sheetEndo(), f_def.sheetEpi(), 0, 0 );
722 
723  // output the points file
724  npt = (bnx[0]+1);
725  std::cout << "Number of points: " << npt << std::endl;
726  pt = new Point[npt];
727  pt_os << npt << std::endl;
728  npt = 0;
729  for( int i=0; i<=bnx[0]; i++ ) {
730  pt[npt].assign<double>(x0[0]+i*res[0], 0, 0);
731 
732  if( i!=bnx[0] ) {
733  pt[npt].x += ((double)random()/(double)RAND_MAX)*pert*res[0];
734  }
735  pt_os << pt[npt++] << std::endl;
736  }
737  pt_os.close();
738 
739  int num_in_layer = (bnx[0]+1);
740  elem_os << bnx[0] << std::endl;
741  std::cout << "Number of linear elements: " << bnx[0] << std::endl;
742 
743  for( int i=0; i<bnx[0]; i++ ) {
744  p1 = i;
745  p2 = p1+1;
746 
747  region_t regtype = Myocardium;
748  if( chk_bath(i,0,0) )
749  regtype = aniso_bath?Anisobath:Isobath;
750 
751  Line l(p1, p2 );
752  add_element( l, regtype );
753  }
754  elem_os.close();
755  lon_os.close();
756  delete[] pt;
757 }
758 
759 
772 void
773 Grid2D::build_mesh(float* x0, float* x, float* tissue, bool *sym, float *res,
774  float pert, bool aniso_bath, int periodic_bc)
775 {
776  bool eletype;
777  int p1, p2, p3, p4;
778  int bnx[3]; // number of cubes in each direction
779  int tnx[3]; // number of tissue cubes in each direction
780 
781  // determine number of boxes
782  for( int i=0; i<3; i++ ) {
783  bnx[i] = (int)(x[i]/res[i]);
784  tnx[i] = (int)(tissue[i]/res[i]);
785  }
786 
787  // determine origins for tissue and bath bounding box
788  Point pt0, pb0;
789  pt0.x = -tnx[0]*res[0]/2 + x0[0];
790  pt0.y = -tnx[1]*res[1]/2 + x0[1];
791  pt0.z = 0.0;
792 
793  pb0.x = sym[0]?pt0.x-(bnx[0]-tnx[0])*res[0]/2:pt0.x+ x0[0];
794  pb0.y = sym[0]?pt0.y-(bnx[0]-tnx[0])*res[0]/2:pt0.y+ x0[1];
795  pb0.z = 0.0;
796 
797  // determine bounding box of bath and tissue grids
798  b_bbx.init(bnx,res,pb0);
799  t_bbx.init(tnx,res,pt0);
800 
801  // set tissue index boundaries relative to bath grid
802  set_indx_bounds(sym);
803 
804  // output the points file
805  npt = (bnx[0]+1)*(bnx[1]+1);
806  std::cout << "Number of points: " << npt << std::endl;
807  pt = new Point[npt];
808  pt_os << npt << std::endl;
809  npt = 0;
810  for( int j=0; j<=bnx[1]; j++ )
811  for( int i=0; i<=bnx[0]; i++ ) {
812  pt[npt].assign<double>(x0[0]+i*res[0], x0[1]+j*res[1], 0);
813 
814  if( j && j!=bnx[1] && i && i!=bnx[0] ) {
815  pt[npt].x += ((double)random()/(double)RAND_MAX)*pert*res[0];
816  pt[npt].y += ((double)random()/(double)RAND_MAX)*pert*res[1];
817  }
818  pt_os << pt[npt++] << std::endl;
819  }
820  pt_os.close();
821 
822  // determine periodic b.c. connections
823  int nper_cnnx = 0;
824  if( (periodic_bc&1) == 1 )
825  nper_cnnx += bnx[1]+1;
826  if( (periodic_bc&2) == 2 )
827  nper_cnnx += bnx[0]+1;
828 
829  int num_in_layer = (bnx[0]+1)*(bnx[1]+1);
830 
831  if(param_globals::tri2D) {
832  elem_os << 2*bnx[0]*bnx[1]+nper_cnnx << std::endl;
833  elemc_os << 2*bnx[0]*bnx[1]+nper_cnnx << std::endl;
834  std::cout << "Number of triangles: " << 2*bnx[0]*bnx[1] << std::endl;
835  } else {
836  elem_os << bnx[0]*bnx[1]+nper_cnnx << std::endl;
837  elemc_os << bnx[0]*bnx[1]+nper_cnnx << std::endl;
838  std::cout << "Number of quadrilaterals: " << bnx[0]*bnx[1] << std::endl;
839  }
840  if( nper_cnnx )
841  std::cout << "Number of periodic connections: " << nper_cnnx << std::endl;
842 
843  f_xi.linear( f_def.rotEndo(), f_def.rotEpi(), 0, 0 );
844  s_xi.linear( f_def.sheetEndo(), f_def.sheetEpi(), 0, 0 );
845 
846  num_axes = f_def.withSheets()?2:1;
847  lon_os << num_axes << std::endl;
848  if(num_axes>1)
849  std::cout << "Using orthotropic fiber setup." << std::endl;
850  else
851  std::cout << "Using transversely isotropic fiber setup." << std::endl;
852 
853  for( int j=0; j<bnx[1]; j++ ) {
854  eletype = j%2;
855  for( int i=0; i<bnx[0]; i++ ) {
856  p1 = j*(bnx[0]+1) + i;
857  p2 = p1+1;
858  p3 = (j+1)*(bnx[0]+1) + i;
859  p4 = p3+1;
860 
861  region_t regtype = Myocardium;
862  if( chk_bath(i,j,0) )
863  regtype = aniso_bath?Anisobath:Isobath;
864 
865  if( param_globals::tri2D ) {
866  if( eletype ) {
867  Triangle t1(p1, p2, p3), t2(p2, p4, p3);
868  add_element( t1, regtype );
869  add_element( t2, regtype );
870  } else {
871  Triangle t1(p1, p2, p4), t2(p1, p4, p3);
872  add_element( t1, regtype );
873  add_element( t2, regtype );
874  }
875  eletype = !eletype;
876  } else {
877  Quadrilateral q(p1,p2,p4,p3);
878  add_element( q, regtype );
879  }
880  }
881  }
882 
883  if( (periodic_bc&1) == 1 ) {
884  for( int i=0; i<=bnx[1]; i++ ) {
885  Line l( i*(bnx[0]+1), (i+1)*(bnx[0]+1)-1 );
886  elem_os << l << " " << param_globals::periodic_tag<<std::endl;
887  lon_os << "1 0 0" << std::endl;
888  }
889  }
890  if( (periodic_bc&2) == 2 ) {
891  for( int i=0; i<=bnx[0]; i++ ) {
892  Line l( i, (bnx[0]+1)*(bnx[1])+i );
893  elem_os << l << " " << param_globals::periodic_tag+1 << std::endl;
894  lon_os << "0 1 0" << std::endl;
895  }
896  }
897 
898  elem_os.close();
899  lon_os.close();
900  delete[] pt;
901 }
902 
903 
914 void
915 Grid3D::build_mesh( float* x0, float* x, float* tissue, bool *sym, float *res,
916  float pert, bool aniso_bath, int periodic_bc)
917 {
918  bool eletype;
919  int p1, p2, p3, p4, p5, p6, p7,p8;
920  int bnx[3]; // number of cubes in each direction
921  int tnx[3]; // number of tissue cubes in each direction
922 
923  // determine number of boxes
924  for( int i=0; i<3; i++ ) {
925  bnx[i] = (int)(x[i]/res[i]);
926  tnx[i] = (int)(tissue[i]/res[i]);
927  }
928 
929  // determine origins for tissue and bath bounding box
930  Point pt0, pb0;
931  pt0 = {x0[0],x0[1],x0[2]};
932 
933  pb0.x = sym[0]?pt0.x-(bnx[0]-tnx[0])*res[0]/2:pt0.x + x0[0];
934  pb0.y = sym[1]?pt0.y-(bnx[1]-tnx[1])*res[1]/2:pt0.y + x0[1];
935  pb0.z = sym[2]?pt0.z-(bnx[2]-tnx[2])*res[2]/2:pt0.z + x0[2];
936 
937  // determine bounding box of bath and tissue grids
938  b_bbx.init(bnx,res,pb0);
939  t_bbx.init(tnx,res,pt0);
940 
941  // set tissue index boundaries relative to bath grid
942  set_indx_bounds(sym);
943 
944 
945  // define fiber and sheet arrangements
946  if(strcmp(f_def.f_name(),"")) {
947  std::cout << "Reading transmural fiber rotation profile from " << f_def.f_name() << std::endl;
948  f_xi.read(f_def.f_name());
949  }
950  else
953 
954  if(strcmp(f_def.s_name(),"")) {
955  std::cout << "Reading transmural sheet profile from " << f_def.s_name() << std::endl;
956  s_xi.read(f_def.s_name());
957  }
958  else
961 
962  // output the points file
963  npt = (bnx[0]+1)*(bnx[1]+1)*(bnx[2]+1);
964  std::cout << "Number of points: " << npt << std::endl;
965  pt = new Point[npt];
966  pt_os << npt << std::endl;
967  npt = 0;
968  for( int k=0; k<=bnx[2]; k++ )
969  for( int j=0; j<=bnx[1]; j++ )
970  for( int i=0; i<=bnx[0]; i++ ) {
971  pt[npt] = {x0[0]+i*res[0], x0[1]+j*res[1], x0[2]+k*res[2]};
972  if( k && k!=bnx[2] && j && j!=bnx[1] && i && i!=bnx[0] ) {
973  pt[npt].x += ((double)random()/(double)RAND_MAX)*pert*res[0];
974  pt[npt].y += ((double)random()/(double)RAND_MAX)*pert*res[1];
975  pt[npt].z += ((double)random()/(double)RAND_MAX)*pert*res[2];
976  }
977  pt_os << pt[npt++] << std::endl;
978  }
979  pt_os.close();
980 
981  int num_in_layer = (bnx[0]+1)*(bnx[1]+1);
982  if(!param_globals::Elem3D) {
983  elem_os << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
984  elemc_os << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
985  std::cout << "Number of Tetrahedra: " << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
986  }
987  else {
988  elem_os << bnx[0]*bnx[1]*bnx[2] << std::endl;
989  elemc_os << bnx[0]*bnx[1]*bnx[2] << std::endl;
990  std::cout << "Number of Hexahedra: " << bnx[0]*bnx[1]*bnx[2] << std::endl;
991  }
992 
993  num_axes = f_def.withSheets()?2:1;
994  lon_os << num_axes << std::endl;
995  if(num_axes>1)
996  std::cout << "Using orthotropic fiber setup." << std::endl;
997  else
998  std::cout << "Using transversely isotropic fiber setup." << std::endl;
999 
1000 
1001  for( int k=0; k<bnx[2]; k++ ) {
1002  eletype = k%2;
1003  for( int j=0; j<bnx[1]; j++ ) {
1004  // if bnx[0] is even we need to toggle
1005  // otherwise we break the chessboard pattern
1006  if(j && !(bnx[0]%2) )
1007  eletype = !eletype;
1008 
1009  for( int i=0; i<bnx[0]; i++ ) {
1010  p1 = k*num_in_layer + j*(bnx[0]+1) + i;
1011  p2 = p1+1;
1012  p3 = k*num_in_layer + (j+1)*(bnx[0]+1) + i;
1013  p4 = p3+1;
1014  p5 = (k+1)*num_in_layer + j*(bnx[0]+1) + i;
1015  p6 = p5+1;
1016  p7 = (k+1)*num_in_layer + (j+1)*(bnx[0]+1) + i;
1017  p8 = p7+1;
1018 
1019  region_t regtype = Myocardium;
1020  if(chk_bath(i,j,k))
1021  regtype = aniso_bath?Anisobath:Isobath;
1022 
1023  if(!param_globals::Elem3D) {
1024  // generate tet mesh
1025  if( eletype ) {
1026  Tetrahedron t1(p1, p2, p4, p6),
1027  t2(p1, p3, p4, p7),
1028  t3(p1, p4, p6, p7),
1029  t4(p1, p5, p6, p7),
1030  t5(p4, p6, p7, p8);
1031  t1.chkNegVolume( pt ); add_element( t1, regtype );
1032  t2.chkNegVolume( pt ); add_element( t2, regtype );
1033  t3.chkNegVolume( pt ); add_element( t3, regtype );
1034  t4.chkNegVolume( pt ); add_element( t4, regtype );
1035  t5.chkNegVolume( pt ); add_element( t5, regtype );
1036  } else {
1037  Tetrahedron t1(p1, p2, p3, p5),
1038  t2(p2, p3, p4, p8),
1039  t3(p2, p5, p6, p8),
1040  t4(p2, p3, p5, p8),
1041  t5(p3, p5, p7, p8);
1042  t1.chkNegVolume( pt ); add_element( t1, regtype );
1043  t2.chkNegVolume( pt ); add_element( t2, regtype );
1044  t3.chkNegVolume( pt ); add_element( t3, regtype );
1045  t4.chkNegVolume( pt ); add_element( t4, regtype );
1046  t5.chkNegVolume( pt ); add_element( t5, regtype );
1047  }
1048  eletype = !eletype;
1049  }
1050  else {
1051  // Hex
1052 
1053  // this is the original hex ordering. not compatible with the SF ansatzfunc scheme..
1054  // Hexahedron h1(p4, p3, p1, p2, p8, p6, p5, p7);
1055 
1056  Hexahedron h1(p5, p7, p8, p6, p1, p2, p4, p3);
1057  add_element( h1, regtype );
1058  }
1059  }
1060  }
1061  }
1062  elem_os.close();
1063  lon_os.close();
1064  elemc_os.close();
1065  vec_os.close();
1066  delete[] pt;
1067 }
1068 
1069 
1070 int main( int argc, char *argv[] )
1071 {
1072  int status;
1073  float tsize[3], x0[3], t0[3];
1074  bool symbath[3];
1075 
1076  do{
1077  status = param(PARAMETERS, &argc, argv );
1078  if( status==PrMERROR||status==PrMFATAL )
1079  fprintf( stderr, "\n*** Error reading parameters\n\n");
1080  else if( status==PrMQUIT )
1081  fprintf( stderr, "\n*** Quitting by user's request\n\n");
1082  }while (status==PrMERROR);
1083  if(status&&PrMERROR) exit( status );
1084 
1085 
1086  for( int i=0; i<3; i++ ) {
1087  param_globals::size[i] *= CM2UM;
1088  param_globals::bath[i] *= CM2UM;
1089  param_globals::center[i] *= CM2UM;
1090  // in 2D case we don't allow a bath attached in the direction
1091  // perpendicular to the 2D surface
1092  if(param_globals::size[i]==0.) param_globals::bath[i] = 0.0;
1093  if(param_globals::bath[i]>=0) {
1094  tsize[i] = param_globals::size[i]+param_globals::bath[i];
1095  symbath[i] = false;
1096  x0[i] = -0.5*param_globals::size[i]+param_globals::center[i];
1097  }
1098  else {
1099  tsize[i] = param_globals::size[i]-2*param_globals::bath[i];
1100  symbath[i] = true;
1101  x0[i] = -0.5*tsize[i]+param_globals::center[i];
1102  }
1103  }
1104 
1105  Region **region = (Region**)calloc(param_globals::numRegions+1,sizeof(Region*));
1106  Point ctr;
1107  ctr.x = param_globals::center[0];
1108  ctr.y = param_globals::center[1];
1109  ctr.z = param_globals::center[2];
1110 
1111  RegionDef* regdef = param_globals::regdef;
1112 
1113  for (int r = 0; r < param_globals::numRegions; r++ ) {
1114  Point p0 = scal_X( p_assign_array(regdef[r].p0), CM2UM );
1115  if( !param_globals::size[2]) p0.z = 0;
1116  Point p1;
1117  switch(regdef[r].type) {
1118  case 0:
1119  if( !param_globals::size[2] ) p1.z = 0;
1120  p1 = scal_X( p_assign_array( regdef[r].p1 ), CM2UM );
1121  p1 = p1 + ctr;
1122  if( p0.x>p1.x ) std::swap( p0.x, p1.x );
1123  if( p0.y>p1.y ) std::swap( p0.y, p1.y );
1124  if( p0.z>p1.z ) std::swap( p0.z, p1.z );
1125  region[r] = new BlockRegion( p0, p1, regdef[r].bath );
1126  break;
1127  case 1:
1128  regdef[r].rad *= CM2UM;
1129  region[r] = new SphericalRegion( p0,regdef[r].rad, regdef[r].bath );
1130  break;
1131  case 2:
1132  Point axis;
1133  if( !param_globals::size[2] )
1134  axis = {0,0,1};
1135  else
1136  axis = p_assign_array( regdef[r].p1 );
1137  regdef[r].rad *= CM2UM;
1138  regdef[r].cyllen *= CM2UM;
1139  region[r] = new CylindricalRegion( p0, axis, regdef[r].rad,
1140  regdef[r].cyllen, regdef[r].bath );
1141  break;
1142  }
1143  region[r]->tag(regdef[r].tag);
1144  }
1145  region[param_globals::numRegions] = NULL;
1146 
1147 
1148  Grid *grid;
1149  if( !param_globals::size[1] && !param_globals::size[2] )
1150  grid = new Grid1D( param_globals::mesh, region );
1151  else if( !param_globals::size[2] )
1152  grid = new Grid2D( param_globals::mesh, region );
1153  else {
1154  grid = new Grid3D( param_globals::mesh, region );
1155  }
1156 
1157  if( grid->os_good() ) {
1158  grid->unPrMFiberDefs();
1159  grid->build_mesh(x0, tsize, param_globals::size, symbath, param_globals::resolution,
1160  param_globals::perturb, param_globals::anisoBath, param_globals::periodic);
1161  delete grid;
1162  }
1163  else {
1164  std::cerr << "ERROR:" << std::endl;
1165  std::cerr << "Could not open mesh files " << param_globals::mesh << ".* for writing!" << std::endl;
1166  std::cerr << "Aborting!" << std::endl;
1167  delete grid;
1168  return 1;
1169  }
1170 
1171  return 0;
1172 }
1173 
constexpr T min(T a, T b)
Definition: ion_type.h:33
CylindricalRegion(Point origin, Point dir, float r, float l, int bth)
Definition: mesher.cc:155
tmProfile f_xi
Definition: mesher.cc:510
char * f_name(void)
Definition: mesher.cc:449
Point p0_
Definition: mesher.cc:123
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
Definition: mesher.cc:915
int num()
Definition: mesher.cc:180
void dims(void)
Definition: mesher.cc:431
void set_indx_bounds(bool *sym)
Definition: mesher.cc:581
Point p_assign_array(float *p)
Definition: mesher.cc:58
int bath_
Definition: mesher.cc:124
virtual void output_boundary(char *fn)
Definition: mesher.cc:518
BBoxDef nodes
Definition: mesher.cc:376
float * res
Definition: mesher.cc:374
const float CM2UM
Definition: mesher.cc:71
std::string type_
Definition: mesher.cc:186
bool isbath()
Definition: mesher.cc:119
Definition: mesher.cc:237
void update(Point p)
Definition: mesher.cc:351
Grid1D(char *m, Region **r)
Definition: mesher.cc:537
constexpr T max(T a, T b)
Definition: ion_type.h:31
float imbrication()
Definition: mesher.cc:451
Point sheet(void)
Definition: mesher.cc:79
float mx_z(bool nodeGrid)
Definition: mesher.cc:369
std::ofstream vec_os
Definition: mesher.cc:506
Grid2D(char *m, Region **r)
Definition: mesher.cc:519
virtual bool inside(Point p)
Definition: mesher.cc:166
float rotEpi()
Definition: mesher.cc:453
virtual ~Region()
Definition: mesher.cc:117
int read(char *fname)
Definition: mesher.cc:315
3D vector algebra.
std::ofstream elem_os
Definition: mesher.cc:506
int bx_inds[3][2]
Definition: mesher.cc:372
Definition: mesher.cc:490
void set_xi(float xi_)
Definition: mesher.cc:75
V det3(const vec3< V > &a, const vec3< V > &b, const vec3< V > &c)
Definition: vect.h:96
vec3< V > normalize(vec3< V > a)
Definition: vect.h:198
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
Definition: mesher.cc:773
int dim
Definition: mesher.cc:512
BoundingBox b_bbx
Definition: mesher.cc:504
int n_
Definition: mesher.cc:184
void add_element(Element &, region_t)
Definition: mesher.cc:634
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)=0
int * bx
Definition: mesher.cc:369
bool withSheets(void)
Definition: mesher.cc:480
std::ostream & operator<<(std::ostream &out, Element &e)
Definition: mesher.cc:243
float rotEndo()
Definition: mesher.cc:452
float sheetEpi()
Definition: mesher.cc:455
std::ofstream elemc_os
Definition: mesher.cc:506
std::ofstream lon_os
Definition: mesher.cc:506
Grid3D(char *m, Region **r)
Definition: mesher.cc:528
V mag2(const vec3< V > &vect)
Definition: vect.h:138
int main(int argc, char *argv[])
Definition: mesher.cc:1070
void chkNegVolume(Point *pts)
Definition: mesher.cc:205
float sheetEndo()
Definition: mesher.cc:454
~Element()
Definition: mesher.cc:178
bool chk_bath(int i, int j, int k)
Definition: mesher.cc:603
int linear(float ang_endo, float ang_epi, float z_endo, float z_epi)
Definition: mesher.cc:271
float z2xi(float z, bool nodeGrid)
Definition: mesher.cc:380
virtual bool inside(Point p)
Definition: mesher.cc:146
virtual ~BoundingBox()
Definition: mesher.cc:364
void setFiberDefs(char *f_prof, float fEndo, float fEpi, float imbr, char *s_prof, float sEndo, float sEpi)
Definition: mesher.cc:467
Region ** region
Definition: mesher.cc:507
Quadrilateral(int A, int B, int C, int D)
Definition: mesher.cc:223
void set_bath_axes(bool)
Definition: mesher.cc:106
TisAxes ax
Definition: mesher.cc:182
Line(int A, int B)
Definition: mesher.cc:239
Hexahedron(int A, int B, int C, int D, int E, int F, int G, int H)
Definition: mesher.cc:216
#define BOX_CENTERS_GRID
Definition: mesher.cc:55
region_t
Definition: mesher.cc:53
Point * pt
Definition: mesher.cc:502
int tag_
Definition: mesher.cc:125
float lookup(float xi)
Definition: mesher.cc:301
virtual bool inside(Point p)
Definition: mesher.cc:140
virtual void output_boundary(char *fn)
Definition: mesher.cc:536
void assign(S ix, S iy, S iz)
Definition: vect.h:45
char * s_name(void)
Definition: mesher.cc:450
SphericalRegion(Point ctr, float r, int bth)
Definition: mesher.cc:138
int num_axes
Definition: mesher.cc:508
BoundingBox t_bbx
Definition: mesher.cc:505
void init(int *_bx, float *_res, Point p0)
Definition: mesher.cc:392
V dot(const vec3< V > &p1, const vec3< V > &p2)
Definition: vect.h:125
BBoxDef bctrs
Definition: mesher.cc:375
void tag(int t)
Definition: mesher.cc:121
Point fiber(void)
Definition: mesher.cc:78
~tmProfile()
Definition: mesher.cc:259
const Point e_circ
Definition: mesher.cc:65
std::ofstream pt_os
Definition: mesher.cc:506
const Point e_long
Definition: mesher.cc:66
vec3< V > scal_X(const vec3< V > &a, S k)
Definition: vect.h:150
virtual ~Grid()
Definition: mesher.cc:493
bool os_good()
Definition: mesher.cc:543
#define M_PI
Definition: ION_IF.h:51
void set_axes(float alpha_, float beta_pr_, float gamma_)
Definition: mesher.cc:91
fibDef f_def
Definition: mesher.cc:509
const Point e_rad
Definition: mesher.cc:67
int * p_
Definition: mesher.cc:185
Region(Point p, int b)
Definition: mesher.cc:116
virtual void output_boundary(char *fn)
Definition: mesher.cc:527
Triangle(int A, int B, int C)
Definition: mesher.cc:231
Tetrahedron(int A, int B, int C, int D)
Definition: mesher.cc:199
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
Definition: mesher.cc:689
Point centre(Point *ctr)
Definition: mesher.cc:190
Element(int N, const char *t)
Definition: mesher.cc:177
V dist_2(const vec3< V > &p1, const vec3< V > &p2)
Definition: vect.h:102
tmProfile s_xi
Definition: mesher.cc:511
Grid(char *, Region **, int)
Definition: mesher.cc:549
int tag()
Definition: mesher.cc:120
BlockRegion(Point p0, Point p, int bth)
Definition: mesher.cc:130
axis
split axis
Definition: kdpart.hpp:63
int npt
Definition: mesher.cc:503
void unPrMFiberDefs(void)
Definition: mesher.cc:617
float mn_z(bool nodeGrid)
Definition: mesher.cc:368