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 <cctype>
40 #include <cstdio>
41 #include <cstring>
42 #include <string>
43 #include <cmath>
44 #include <stdlib.h>
45 #include <limits>
46 #include <algorithm>
47 #include <sys/wait.h>
48 #include <unistd.h>
49 #include <vector>
50 
51 #include "vect.h"
52 #include <mesher_p.h>
53 #include <mesher_d.h>
54 #include "mesher_schema.hpp"
55 #include "runtime.hpp"
56 #include "snapshot_file_io.hpp"
57 
58 
59 using namespace opencarp;
60 
61 typedef enum {Myocardium=1, Isobath, Anisobath } region_t;
62 
63 #define BOX_CENTERS_GRID false
64 #define NODE_GRID true
65 
66 Point p_assign_array( float *p )
67 {
68  Point a;
69  a.x = p[0]; a.y=p[1]; a.z=p[2];
70  return a;
71 }
72 
73 const Point e_circ = {1,0,0};
74 const Point e_long = {0,1,0};
75 const Point e_rad = {0,1,0};
76 
77 
78 
79 const float CM2UM=1.e4;
80 
81 class TisAxes {
82  public:
83  void set_xi(float xi_) { xi=xi_; }
84  void set_axes(float alpha_, float beta_pr_, float gamma_);
85  void set_bath_axes(bool);
86  Point fiber(void) {return f;}
87  Point sheet(void) {return s;}
88  private:
89  float xi;
90  float alpha;
91  float beta_pr;
92  float gamma;
93  Point f;
94  Point s;
95  Point sn;
96 };
97 
98 void
99 TisAxes::set_axes(float b, float c, float d)
100 {
101  alpha = b/180*M_PI;
102  beta_pr = c/180*M_PI;
103  gamma = d/180*M_PI;
104 
105  Point p(cos(alpha), sin(alpha), sin(gamma));
106  f = normalize(p);
107 
108  Point sp(0.0, sin(double(beta_pr)), cos(double(beta_pr)));
109  float lambda = -dot(f, sp)/dot(f, e_circ);
110  s = normalize(sp + scal_X(e_circ,lambda));
111 }
112 
113 void
114 TisAxes::set_bath_axes(bool aniso_bath)
115 {
116  f.y = f.z = 0.0;
117  s.x = s.y = s.z = 0.0;
118 
119  f.x = aniso_bath?1.0:0.0;
120 }
121 
122 class Region {
123  public:
124  Region(Point p, int b): p0_(p), bath_(b), tag_(-1) {}
125  virtual ~Region() {}
126  virtual bool inside(Point) = 0;
127  bool isbath() { return bath_; }
128  int tag() { return tag_; }
129  void tag(int t) { tag_ = t; }
130  protected:
132  int bath_;
133  int tag_;
134 };
135 
136 class BlockRegion: public Region {
137  public:
138  BlockRegion(Point p0, Point p, int bth): Region(p0, bth), p1_(p) {}
139  virtual bool inside(Point p);
140  private:
141  Point p1_;
142 };
143 
144 class SphericalRegion: public Region {
145  public:
146  SphericalRegion(Point ctr, float r, int bth):
147  Region(ctr, bth), radius2_(r*r) {}
148  virtual bool inside(Point p) { return dist_2(p, p0_)<= radius2_; }
149  private:
150  float radius2_; //square of radius
151 };
152 
153 bool
155 {
156  return p.x >= p0_.x && p.x <= p1_.x && p.y >= p0_.y && p.y <= p1_.y &&
157  p.z >= p0_.z && p.z <= p1_.z;
158 }
159 
160 
161 class CylindricalRegion: public Region {
162  public:
163  CylindricalRegion(Point origin, Point dir, float r, float l, int bth):
164  Region(origin, bth), radius2_(r*r)
165  {axis_=normalize(dir);len_=(l==0.)?1.e36:l;}
166  virtual bool inside( Point p );
167  private:
168  Point axis_;
169  float radius2_;
170  float len_;
171 };
172 
173 bool
175 {
176  Point R = p - p0_;
177  float d = dot(R, axis_);
178 
179  return d>=0 && d<=len_ && mag2(R)-d*d <= radius2_;
180 }
181 
182 
183 class Element {
184  public:
185  Element(int N, const char *t):n_(N),p_(new int[N]),type_(t){}
186  ~Element() { delete[] p_; }
187  Point centre(Point *ctr);
188  int num(){ return n_; };
189  friend std::ostream& operator<<(std::ostream &, Element& );
191  protected:
192  int n_;
193  int *p_;
194  std::string type_;
195 };
196 
197 
199  Point result = ctr[p_[0]];
200  for( int i=1; i<n_; i++ ) result = result + ctr[p_[i]];
201  return scal_X( result, 1./(float)n_);
202 }
203 
204 
205 class Tetrahedron:public Element {
206  public:
207  Tetrahedron( int A, int B, int C, int D ): Element(4,"Tt") {
208  p_[0]=A; p_[1]=B; p_[2]=C; p_[3]=D; }
209  void chkNegVolume(Point *pts);
210 };
211 
212 
214  Point P01 = pts[p_[1]] - pts[p_[0]];
215  Point P02 = pts[p_[2]] - pts[p_[0]];
216  Point P03 = pts[p_[3]] - pts[p_[0]];
217  double vol = det3(P01,P02,P03); // 6x volume
218  if( vol < 0. )
219  std::swap(p_[0],p_[1]);
220 }
221 
222 class Hexahedron:public Element {
223  public:
224  Hexahedron( int A, int B, int C, int D, int E, int F, int G, int H ): Element(8,"Hx") {
225  p_[0]=A; p_[1]=B; p_[2]=C; p_[3]=D; p_[4]=E; p_[5]=F; p_[6]=G; p_[7]=H; }
226 };
227 
228 
229 class Quadrilateral:public Element {
230  public:
231  Quadrilateral( int A, int B, int C, int D ): Element(4,"Qd") {
232  p_[0] = A; p_[1] = B; p_[2] = C; p_[3] = D;
233  }
234 };
235 
236 
237 class Triangle:public Element {
238  public:
239  Triangle( int A, int B, int C ): Element(3,"Tr") {
240  p_[0] = A; p_[1] = B; p_[2] = C;
241  }
242 };
243 
244 
245 class Line:public Element {
246  public:
247  Line( int A, int B ): Element(2,"Ln"){p_[0]=A; p_[1]=B;}
248 };
249 
250 
251 std::ostream& operator<<( std::ostream& out, Element &e ) {
252  out << e.type_ << " ";
253  for( int i=0; i<e.num()-1; i++ )
254  out << e.p_[i] << " ";
255  out << e.p_[e.num()-1];
256  return out;
257 }
258 
259 std::ostream& operator<<( std::ostream& out, Point p ) {
260  out << p.x << " " << p.y << " " << p.z;
261  return out;
262 }
263 
264 
265 class tmProfile {
266  public:
267  ~tmProfile() { free(xi); free(ang); }
268  int read(char *fname);
269  float lookup(float xi);
270  int linear(float ang_endo, float ang_epi, float z_endo, float z_epi );
271  private:
272  int N;
273  float *xi;
274  float *ang;
275 };
276 
277 
278 int
279 tmProfile::linear (float ang_endo,float ang_epi, float z_endo, float z_epi)
280 {
281  float dz = 10; // sample the profile at a 10 um resolution
282  if( z_epi==z_endo ) // 2D case
283  N = 0;
284  else
285  N = (z_epi-z_endo)/dz;
286 
287  xi = (float *)malloc(sizeof(float)*(N+1));
288  ang = (float *)malloc(sizeof(float)*(N+1));
289 
290  if(xi==NULL || ang==NULL) {
291  std::cerr << "Memory allocation failed." << std::endl;
292  return -1;
293  }
294  else if( !N ) { // 2D
295  xi[0] = 0;
296  ang[0] = ang_endo;
297  } else {
298  float K = (ang_epi-ang_endo)/(z_epi-z_endo);
299  for(int i=0;i<=N;i++) {
300  float z = z_endo+i*dz;
301  xi[i] = (z-z_endo)/(z_epi-z_endo)-0.5;
302  ang[i] = ang_endo + K*(z-z_endo);
303  }
304  }
305  return 0;
306 }
307 
308 float
310 {
311  if( !N ) return ang[0];
312 
313  int i=0;
314  while(xi[i]<xi_ && i<N) i++;
315 
316  if((i==0)|| (i==N))
317  return ang[i];
318  else
319  return ang[i-1]+(ang[i]-ang[i-1])/(xi[i]-xi[i-1])*(xi_-xi[i-1]);
320 }
321 
322 int
323 tmProfile::read(char *fname)
324 {
325  FILE *profile = fopen(fname,"rt");
326  if(profile==NULL) {
327  fprintf( stderr, "Can't open transmural profile data file %s.\n", fname);
328  return -1;
329  }
330 
331  int err = fscanf(profile,"%d",&N);
332  xi = (float *)malloc(sizeof(float)*N);
333  ang = (float *)malloc(sizeof(float)*N);
334  for(int i=0; i<N; i++) {
335  xi[i] = (float)i/(float)(N-1)-0.5;
336  err = fscanf(profile,"%f",ang+i);
337  }
338  fclose(profile);
339 
340  return err;
341 }
342 
343 class BBoxDef {
344  public:
345  void update( Point p );
346 // private:
347  float xd = 0;
350  float yd = 0;
353  float zd = 0;
356 };
357 
358 void
360 {
361  if(p.x<x_mn) x_mn = p.x;
362  if(p.y<y_mn) y_mn = p.y;
363  if(p.z<z_mn) z_mn = p.z;
364  if(p.x>x_mx) x_mx = p.x;
365  if(p.y>y_mx) y_mx = p.y;
366  if(p.z>z_mx) z_mx = p.z;
367 }
368 
369 
370 class BoundingBox {
371  public:
372  virtual ~BoundingBox() { delete[] bx; delete[] res; }
373  void init( int *_bx, float *_res, Point p0);
374  void dims(void);
375  float z2xi(float z, bool nodeGrid);
376  float mn_z(bool nodeGrid){ return nodeGrid?nodes.z_mn:bctrs.z_mn; };
377  float mx_z(bool nodeGrid){ return nodeGrid?nodes.z_mx:bctrs.z_mx; };
378  public:
379  int *bx;
380  int bx_inds[3][2];
381  protected:
382  float *res;
385 };
386 
387 float
388 BoundingBox::z2xi (float z, bool nodeGrid)
389 {
390  float zd = nodeGrid?nodes.zd:bctrs.zd;
391  float z_mn = nodeGrid?nodes.z_mn:bctrs.z_mn;
392 
393  if(zd==0.)
394  return 0.;
395  else
396  return (z-z_mn)/zd-0.5;
397 }
398 
399 void
400 BoundingBox::init(int *_bx, float *_res, Point p0)
401 {
402  bx = new int [3];
403  res = new float [3];
404  for(int i=0;i<3;i++) {
405  bx [i] = _bx [i];
406  res[i] = _res[i];
407  bx_inds[i][0] = 0;
408  bx_inds[i][1] = bx[i]-1;
409  }
410 
411  // min corner of nodal bbx
412  nodes.update(p0);
413 
414  // min corner of center bbx
415  Point pc0 = p0;
416  pc0.x = p0.x + res[0]/2;
417  pc0.y = p0.y + res[1]/2;
418  pc0.z = p0.z + res[2]/2;
419  bctrs.update(pc0);
420 
421  // max corner of nodal bbx
422  Point p1 = p0;
423  p1.x = p0.x+bx[0]*res[0];
424  p1.y = p0.y+bx[1]*res[1];
425  p1.z = p0.z+bx[2]*res[2];
426  nodes.update(p1);
427 
428  // max corner of center bbx
429  Point pc1 = p1;
430  pc1.x = p1.x - res[0]/2;
431  pc1.y = p1.y - res[1]/2;
432  pc1.z = p1.z - res[2]/2;
433  bctrs.update(pc1);
434 
435  dims();
436 }
437 
438 void
440 {
441  // figure out dims in terms of nodes
442  nodes.xd = nodes.x_mx - nodes.x_mn;
443  nodes.yd = nodes.y_mx - nodes.y_mn;
444  nodes.zd = nodes.z_mx - nodes.z_mn;
445 
446  // and in terms of box centers
447  bctrs.xd = bctrs.x_mx - bctrs.x_mn;
448  bctrs.yd = bctrs.y_mx - bctrs.y_mn;
449  bctrs.zd = bctrs.z_mx - bctrs.z_mn;
450 }
451 
452 class fibDef {
453  public:
454  void setFiberDefs(char *f_prof, float fEndo, float fEpi, float imbr,
455  char *s_prof, float sEndo, float sEpi);
456  bool withSheets(void);
457  char *f_name(void) { return f_Prof; };
458  char *s_name(void) { return s_Prof; };
459  float imbrication() { return f_imbr; };
460  float rotEndo() { return f_Endo; };
461  float rotEpi() { return f_Epi; };
462  float sheetEndo() { return s_Endo; };
463  float sheetEpi() { return s_Epi; };
464 
465  private:
466  char *f_Prof;
467  float f_Endo;
468  float f_Epi;
469  float f_imbr;
470  char *s_Prof;
471  float s_Endo;
472  float s_Epi;
473 };
474 
475 void fibDef::setFiberDefs(char *f_prof, float fEndo, float fEpi, float imbr,
476  char *s_prof, float sEndo, float sEpi)
477 {
478  f_Prof = strdup(f_prof);
479  f_Endo = fEndo;
480  f_Epi = fEpi;
481  f_imbr = imbr;
482  s_Prof = strdup(s_prof);
483  s_Endo = sEndo;
484  s_Epi = sEpi;
485 }
486 
487 bool
489 {
490  if((f_imbr!=0.0) || (s_Endo!=0.0) || (s_Epi!=0.0) || (strcmp(s_Prof,"")))
491  return true;
492  else
493  return false;
494 
495 }
496 
497 
498 class Grid {
499  public:
500  Grid( char *, Region **, int );
501  virtual ~Grid() {}
502  void unPrMFiberDefs(void);
503  virtual void build_mesh(float*,float*,float*,bool *,float*,float,bool,int)=0;
504  virtual void output_boundary( char * )=0;
505  void add_element( Element &, region_t );
506  void set_indx_bounds(bool *sym);
507  bool chk_bath( int i, int j, int k );
508  bool os_good();
509  protected:
511  int npt;
514  std::ofstream pt_os, elem_os, lon_os, elemc_os, vec_os;
516  int num_axes;
520  int dim;
521 };
522 
523 class Grid2D : public Grid {
524  public:
525  virtual void build_mesh(float*, float*, float*, bool *, float*, float, bool, int);
526  virtual void output_boundary( char *fn ){};
527  Grid2D( char *m, Region **r ):Grid(m,r,2){}
528  private:
529  void add_tri( int, int, int );
530 };
531 
532 class Grid3D : public Grid {
533  public:
534  virtual void build_mesh(float*, float*, float*, bool *, float*, float, bool, int);
535  virtual void output_boundary( char *fn ){}
536  Grid3D( char *m, Region **r ):Grid(m,r,3){}
537  private:
538  void add_tet( int, int, int, int );
539 };
540 
541 class Grid1D : public Grid {
542  public:
543  virtual void build_mesh(float*, float*, float*, bool *, float*, float, bool, int);
544  virtual void output_boundary( char *fn ){}
545  Grid1D( char *m, Region **r ):Grid(m,r,1){}
546  private:
547  void add_line( int, int, int, int );
548 };
549 
550 
552 {
553  return pt_os.good() && lon_os.good() && elem_os.good();
554 }
555 
556 
557 Grid::Grid( char *msh, Region **r, int d ): region(r),dim(d)
558 {
559  std::string fname(msh);
560  fname = msh;
561  fname += ".lon";
562  lon_os.open(fname.c_str());
563 
564  fname = msh;
565  fname += ".pts";
566  pt_os.open(fname.c_str());
567 
568  fname = msh;
569  fname += ".elem";
570  elem_os.open(fname.c_str());
571 
572  fname = msh;
573  fname += ".vpts";
574  elemc_os.open(fname.c_str());
575 
576  fname = msh;
577  fname += ".vec";
578  vec_os.open(fname.c_str());
579 }
580 
581 
588 void
590 {
591  for(int i=0;i<3;i++) {
592  if(b_bbx.bx[i]>t_bbx.bx[i]) {
593  t_bbx.bx_inds[i][0] = 0;
594  if(sym[i])
595  t_bbx.bx_inds[i][0] = (b_bbx.bx[i]-t_bbx.bx[i])/2;
596  t_bbx.bx_inds[i][1] = t_bbx.bx_inds[i][0]+t_bbx.bx[i]-1;
597  }
598  }
599 }
600 
601 
610 bool
611 Grid::chk_bath(int i, int j, int k)
612 {
613  bool bth[] = { false, false, false };
614  int idx[] = { i, j, k };
615 
616  for(int cnt=0; cnt<dim; cnt++ )
617  if( idx[cnt]<t_bbx.bx_inds[cnt][0] || idx[cnt]>t_bbx.bx_inds[cnt][1] )
618  return true;
619 
620  return false;
621 }
622 
623 
624 void
626 {
627  f_def.setFiberDefs(param_globals::fibers.tm_fiber_profile,
628  param_globals::fibers.rotEndo,
629  param_globals::fibers.rotEpi,
630  param_globals::fibers.imbrication,
631  param_globals::fibers.tm_sheet_profile,
632  param_globals::fibers.sheetEndo,
633  param_globals::fibers.sheetEpi);
634 }
635 
641 void
643 {
644  elem_os << elem;
645  Point c = elem.centre(pt);
646  elemc_os << c << std::endl;
647  int r=-1;
648  int regid = regtype==Myocardium?1:0;
649  while( region[++r] != NULL ) {
650  if( region[r]->inside( c ) ) {
651  if( region[r]->isbath() ) { // myo or bath becomes bath
652  regid = region[r]->tag()<0 ? 0 : region[r]->tag();
653  regtype = region[r]->isbath()==1?Isobath:Anisobath;
654  } else if( regtype == Myocardium ) { // only myo becomes other myo
655  regid = region[r]->tag()<0 ? r+2 : region[r]->tag();
656  }
657  if(param_globals::first_reg)
658  break;
659  }
660  }
661 
662  if(regtype==Myocardium) {
663  float xi = t_bbx.z2xi(c.z,BOX_CENTERS_GRID);
664  elem.ax.set_xi(xi);
665  elem.ax.set_axes( f_xi.lookup(xi),s_xi.lookup(xi),f_def.imbrication());
666  }
667  else {
668  elem.ax.set_bath_axes(regtype==Anisobath);
669  if(regtype==Anisobath)
670  regid *= -1;
671  }
672 
673  elem_os << " " << regid << std::endl;
674  if(num_axes>1)
675  lon_os << elem.ax.fiber() << " " << elem.ax.sheet() << std::endl;
676  else
677  lon_os << elem.ax.fiber() << std::endl;
678 
679  // write fiber to vector file
680  vec_os << elem.ax.fiber() << std::endl;
681 }
682 
683 
696 void
697 Grid1D::build_mesh(float* x0, float* x, float* tissue, bool *sym, float *res,
698  float pert, bool aniso_bath, int periodic_bc)
699 {
700  bool eletype;
701  int p1, p2, p3, p4;
702  int bnx[3]; // number of cubes in each direction
703  int tnx[3]; // number of tissue cubes in each direction
704 
705  // determine number of boxes
706  for( int i=0; i<3; i++ ) {
707  bnx[i] = (int)(x[i]/res[i]);
708  tnx[i] = (int)(tissue[i]/res[i]);
709  }
710 
711  // determine origins for tissue and bath bounding box
712  Point pt0, pb0;
713  pt0.x = -tnx[0]*res[0]/2 + x0[0];
714  pt0.y = 0;
715  pt0.z = 0;
716 
717  pb0.x = sym[0]?pt0.x-(bnx[0]-tnx[0])*res[0]/2:pt0.x+x0[0];
718  pb0.y = 0.0;
719  pb0.z = 0.0;
720 
721  // determine bounding box of bath and tissue grids
722  b_bbx.init(bnx,res,pb0);
723  t_bbx.init(tnx,res,pt0);
724 
725  // set tissue index boundaries relative to bath grid
726  set_indx_bounds(sym);
727 
728  f_xi.linear( f_def.rotEndo(), f_def.rotEpi(), 0, 0 );
729  s_xi.linear( f_def.sheetEndo(), f_def.sheetEpi(), 0, 0 );
730 
731  // output the points file
732  npt = (bnx[0]+1);
733  std::cout << "Number of points: " << npt << std::endl;
734  pt = new Point[npt];
735  pt_os << npt << std::endl;
736  npt = 0;
737  for( int i=0; i<=bnx[0]; i++ ) {
738  pt[npt].assign<double>(x0[0]+i*res[0], 0, 0);
739 
740  if( i!=bnx[0] ) {
741  pt[npt].x += ((double)random()/(double)RAND_MAX)*pert*res[0];
742  }
743  pt_os << pt[npt++] << std::endl;
744  }
745  pt_os.close();
746 
747  int num_in_layer = (bnx[0]+1);
748  elem_os << bnx[0] << std::endl;
749  std::cout << "Number of linear elements: " << bnx[0] << std::endl;
750 
751  for( int i=0; i<bnx[0]; i++ ) {
752  p1 = i;
753  p2 = p1+1;
754 
755  region_t regtype = Myocardium;
756  if( chk_bath(i,0,0) )
757  regtype = aniso_bath?Anisobath:Isobath;
758 
759  Line l(p1, p2 );
760  add_element( l, regtype );
761  }
762  elem_os.close();
763  lon_os.close();
764  delete[] pt;
765 }
766 
767 
780 void
781 Grid2D::build_mesh(float* x0, float* x, float* tissue, bool *sym, float *res,
782  float pert, bool aniso_bath, int periodic_bc)
783 {
784  bool eletype;
785  int p1, p2, p3, p4;
786  int bnx[3]; // number of cubes in each direction
787  int tnx[3]; // number of tissue cubes in each direction
788 
789  // determine number of boxes
790  for( int i=0; i<3; i++ ) {
791  bnx[i] = (int)(x[i]/res[i]);
792  tnx[i] = (int)(tissue[i]/res[i]);
793  }
794 
795  // determine origins for tissue and bath bounding box
796  Point pt0, pb0;
797  pt0.x = -tnx[0]*res[0]/2 + x0[0];
798  pt0.y = -tnx[1]*res[1]/2 + x0[1];
799  pt0.z = 0.0;
800 
801  pb0.x = sym[0]?pt0.x-(bnx[0]-tnx[0])*res[0]/2:pt0.x+ x0[0];
802  pb0.y = sym[0]?pt0.y-(bnx[0]-tnx[0])*res[0]/2:pt0.y+ x0[1];
803  pb0.z = 0.0;
804 
805  // determine bounding box of bath and tissue grids
806  b_bbx.init(bnx,res,pb0);
807  t_bbx.init(tnx,res,pt0);
808 
809  // set tissue index boundaries relative to bath grid
810  set_indx_bounds(sym);
811 
812  // output the points file
813  npt = (bnx[0]+1)*(bnx[1]+1);
814  std::cout << "Number of points: " << npt << std::endl;
815  pt = new Point[npt];
816  pt_os << npt << std::endl;
817  npt = 0;
818  for( int j=0; j<=bnx[1]; j++ )
819  for( int i=0; i<=bnx[0]; i++ ) {
820  pt[npt].assign<double>(x0[0]+i*res[0], x0[1]+j*res[1], 0);
821 
822  if( j && j!=bnx[1] && i && i!=bnx[0] ) {
823  pt[npt].x += ((double)random()/(double)RAND_MAX)*pert*res[0];
824  pt[npt].y += ((double)random()/(double)RAND_MAX)*pert*res[1];
825  }
826  pt_os << pt[npt++] << std::endl;
827  }
828  pt_os.close();
829 
830  // determine periodic b.c. connections
831  int nper_cnnx = 0;
832  if( (periodic_bc&1) == 1 )
833  nper_cnnx += bnx[1]+1;
834  if( (periodic_bc&2) == 2 )
835  nper_cnnx += bnx[0]+1;
836 
837  int num_in_layer = (bnx[0]+1)*(bnx[1]+1);
838 
839  if(param_globals::tri2D) {
840  elem_os << 2*bnx[0]*bnx[1]+nper_cnnx << std::endl;
841  elemc_os << 2*bnx[0]*bnx[1]+nper_cnnx << std::endl;
842  std::cout << "Number of triangles: " << 2*bnx[0]*bnx[1] << std::endl;
843  } else {
844  elem_os << bnx[0]*bnx[1]+nper_cnnx << std::endl;
845  elemc_os << bnx[0]*bnx[1]+nper_cnnx << std::endl;
846  std::cout << "Number of quadrilaterals: " << bnx[0]*bnx[1] << std::endl;
847  }
848  if( nper_cnnx )
849  std::cout << "Number of periodic connections: " << nper_cnnx << std::endl;
850 
851  f_xi.linear( f_def.rotEndo(), f_def.rotEpi(), 0, 0 );
852  s_xi.linear( f_def.sheetEndo(), f_def.sheetEpi(), 0, 0 );
853 
854  num_axes = f_def.withSheets()?2:1;
855  lon_os << num_axes << std::endl;
856  if(num_axes>1)
857  std::cout << "Using orthotropic fiber setup." << std::endl;
858  else
859  std::cout << "Using transversely isotropic fiber setup." << std::endl;
860 
861  for( int j=0; j<bnx[1]; j++ ) {
862  eletype = j%2;
863  for( int i=0; i<bnx[0]; i++ ) {
864  p1 = j*(bnx[0]+1) + i;
865  p2 = p1+1;
866  p3 = (j+1)*(bnx[0]+1) + i;
867  p4 = p3+1;
868 
869  region_t regtype = Myocardium;
870  if( chk_bath(i,j,0) )
871  regtype = aniso_bath?Anisobath:Isobath;
872 
873  if( param_globals::tri2D ) {
874  if( eletype ) {
875  Triangle t1(p1, p2, p3), t2(p2, p4, p3);
876  add_element( t1, regtype );
877  add_element( t2, regtype );
878  } else {
879  Triangle t1(p1, p2, p4), t2(p1, p4, p3);
880  add_element( t1, regtype );
881  add_element( t2, regtype );
882  }
883  eletype = !eletype;
884  } else {
885  Quadrilateral q(p1,p2,p4,p3);
886  add_element( q, regtype );
887  }
888  }
889  }
890 
891  if( (periodic_bc&1) == 1 ) {
892  for( int i=0; i<=bnx[1]; i++ ) {
893  Line l( i*(bnx[0]+1), (i+1)*(bnx[0]+1)-1 );
894  elem_os << l << " " << param_globals::periodic_tag<<std::endl;
895  lon_os << "1 0 0" << std::endl;
896  }
897  }
898  if( (periodic_bc&2) == 2 ) {
899  for( int i=0; i<=bnx[0]; i++ ) {
900  Line l( i, (bnx[0]+1)*(bnx[1])+i );
901  elem_os << l << " " << param_globals::periodic_tag+1 << std::endl;
902  lon_os << "0 1 0" << std::endl;
903  }
904  }
905 
906  elem_os.close();
907  lon_os.close();
908  delete[] pt;
909 }
910 
911 
922 void
923 Grid3D::build_mesh( float* x0, float* x, float* tissue, bool *sym, float *res,
924  float pert, bool aniso_bath, int periodic_bc)
925 {
926  bool eletype;
927  int p1, p2, p3, p4, p5, p6, p7,p8;
928  int bnx[3]; // number of cubes in each direction
929  int tnx[3]; // number of tissue cubes in each direction
930 
931  // determine number of boxes
932  for( int i=0; i<3; i++ ) {
933  bnx[i] = (int)(x[i]/res[i]);
934  tnx[i] = (int)(tissue[i]/res[i]);
935  }
936 
937  // determine origins for tissue and bath bounding box
938  Point pt0, pb0;
939  pt0 = {x0[0],x0[1],x0[2]};
940 
941  pb0.x = sym[0]?pt0.x-(bnx[0]-tnx[0])*res[0]/2:pt0.x + x0[0];
942  pb0.y = sym[1]?pt0.y-(bnx[1]-tnx[1])*res[1]/2:pt0.y + x0[1];
943  pb0.z = sym[2]?pt0.z-(bnx[2]-tnx[2])*res[2]/2:pt0.z + x0[2];
944 
945  // determine bounding box of bath and tissue grids
946  b_bbx.init(bnx,res,pb0);
947  t_bbx.init(tnx,res,pt0);
948 
949  // set tissue index boundaries relative to bath grid
950  set_indx_bounds(sym);
951 
952 
953  // define fiber and sheet arrangements
954  if(strcmp(f_def.f_name(),"")) {
955  std::cout << "Reading transmural fiber rotation profile from " << f_def.f_name() << std::endl;
956  f_xi.read(f_def.f_name());
957  }
958  else
961 
962  if(strcmp(f_def.s_name(),"")) {
963  std::cout << "Reading transmural sheet profile from " << f_def.s_name() << std::endl;
964  s_xi.read(f_def.s_name());
965  }
966  else
969 
970  // output the points file
971  npt = (bnx[0]+1)*(bnx[1]+1)*(bnx[2]+1);
972  std::cout << "Number of points: " << npt << std::endl;
973  pt = new Point[npt];
974  pt_os << npt << std::endl;
975  npt = 0;
976  for( int k=0; k<=bnx[2]; k++ )
977  for( int j=0; j<=bnx[1]; j++ )
978  for( int i=0; i<=bnx[0]; i++ ) {
979  pt[npt] = {x0[0]+i*res[0], x0[1]+j*res[1], x0[2]+k*res[2]};
980  if( k && k!=bnx[2] && j && j!=bnx[1] && i && i!=bnx[0] ) {
981  pt[npt].x += ((double)random()/(double)RAND_MAX)*pert*res[0];
982  pt[npt].y += ((double)random()/(double)RAND_MAX)*pert*res[1];
983  pt[npt].z += ((double)random()/(double)RAND_MAX)*pert*res[2];
984  }
985  pt_os << pt[npt++] << std::endl;
986  }
987  pt_os.close();
988 
989  int num_in_layer = (bnx[0]+1)*(bnx[1]+1);
990  if(!param_globals::Elem3D) {
991  elem_os << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
992  elemc_os << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
993  std::cout << "Number of Tetrahedra: " << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
994  }
995  else {
996  elem_os << bnx[0]*bnx[1]*bnx[2] << std::endl;
997  elemc_os << bnx[0]*bnx[1]*bnx[2] << std::endl;
998  std::cout << "Number of Hexahedra: " << bnx[0]*bnx[1]*bnx[2] << std::endl;
999  }
1000 
1001  num_axes = f_def.withSheets()?2:1;
1002  lon_os << num_axes << std::endl;
1003  if(num_axes>1)
1004  std::cout << "Using orthotropic fiber setup." << std::endl;
1005  else
1006  std::cout << "Using transversely isotropic fiber setup." << std::endl;
1007 
1008 
1009  for( int k=0; k<bnx[2]; k++ ) {
1010  eletype = k%2;
1011  for( int j=0; j<bnx[1]; j++ ) {
1012  // if bnx[0] is even we need to toggle
1013  // otherwise we break the chessboard pattern
1014  if(j && !(bnx[0]%2) )
1015  eletype = !eletype;
1016 
1017  for( int i=0; i<bnx[0]; i++ ) {
1018  p1 = k*num_in_layer + j*(bnx[0]+1) + i;
1019  p2 = p1+1;
1020  p3 = k*num_in_layer + (j+1)*(bnx[0]+1) + i;
1021  p4 = p3+1;
1022  p5 = (k+1)*num_in_layer + j*(bnx[0]+1) + i;
1023  p6 = p5+1;
1024  p7 = (k+1)*num_in_layer + (j+1)*(bnx[0]+1) + i;
1025  p8 = p7+1;
1026 
1027  region_t regtype = Myocardium;
1028  if(chk_bath(i,j,k))
1029  regtype = aniso_bath?Anisobath:Isobath;
1030 
1031  if(!param_globals::Elem3D) {
1032  // generate tet mesh
1033  if( eletype ) {
1034  Tetrahedron t1(p1, p2, p4, p6),
1035  t2(p1, p3, p4, p7),
1036  t3(p1, p4, p6, p7),
1037  t4(p1, p5, p6, p7),
1038  t5(p4, p6, p7, p8);
1039  t1.chkNegVolume( pt ); add_element( t1, regtype );
1040  t2.chkNegVolume( pt ); add_element( t2, regtype );
1041  t3.chkNegVolume( pt ); add_element( t3, regtype );
1042  t4.chkNegVolume( pt ); add_element( t4, regtype );
1043  t5.chkNegVolume( pt ); add_element( t5, regtype );
1044  } else {
1045  Tetrahedron t1(p1, p2, p3, p5),
1046  t2(p2, p3, p4, p8),
1047  t3(p2, p5, p6, p8),
1048  t4(p2, p3, p5, p8),
1049  t5(p3, p5, p7, p8);
1050  t1.chkNegVolume( pt ); add_element( t1, regtype );
1051  t2.chkNegVolume( pt ); add_element( t2, regtype );
1052  t3.chkNegVolume( pt ); add_element( t3, regtype );
1053  t4.chkNegVolume( pt ); add_element( t4, regtype );
1054  t5.chkNegVolume( pt ); add_element( t5, regtype );
1055  }
1056  eletype = !eletype;
1057  }
1058  else {
1059  // Hex
1060 
1061  // this is the original hex ordering. not compatible with the SF ansatzfunc scheme..
1062  // Hexahedron h1(p4, p3, p1, p2, p8, p6, p5, p7);
1063 
1064  Hexahedron h1(p5, p7, p8, p6, p1, p2, p4, p3);
1065  add_element( h1, regtype );
1066  }
1067  }
1068  }
1069  }
1070  elem_os.close();
1071  lon_os.close();
1072  elemc_os.close();
1073  vec_os.close();
1074  delete[] pt;
1075 }
1076 
1077 namespace {
1078 
1079 enum class ParserCompareMode {
1080  Off,
1081  Warn,
1082  Strict,
1083 };
1084 
1085 enum class ParserFallbackMode {
1086  Off,
1087  Legacy,
1088 };
1089 
1090 struct RuntimeCompatOptions {
1091  ParserFallbackMode fallback_mode = ParserFallbackMode::Off;
1092 };
1093 
1094 struct LegacyCompareInput {
1095  bool available = false;
1096  std::vector<std::string> runtime_args;
1097  std::string unavailable_reason;
1098 };
1099 
1100 std::string trim_copy(const std::string& value)
1101 {
1102  std::string::size_type first = 0;
1103  while (first < value.size() && std::isspace(static_cast<unsigned char>(value[first]))) {
1104  ++first;
1105  }
1106 
1107  std::string::size_type last = value.size();
1108  while (last > first && std::isspace(static_cast<unsigned char>(value[last - 1]))) {
1109  --last;
1110  }
1111 
1112  return value.substr(first, last - first);
1113 }
1114 
1115 std::string to_lower_ascii(std::string value)
1116 {
1117  for (std::string::size_type i = 0; i < value.size(); ++i) {
1118  value[i] = static_cast<char>(std::tolower(static_cast<unsigned char>(value[i])));
1119  }
1120  return value;
1121 }
1122 
1123 bool parse_option_argument(int* index,
1124  int argc,
1125  char** argv,
1126  const std::string& attached_value,
1127  const char* option_name,
1128  std::string* value,
1129  std::string* error)
1130 {
1131  if (!attached_value.empty()) {
1132  *value = attached_value;
1133  return true;
1134  }
1135  if (*index + 1 >= argc) {
1136  *error = "Missing argument after " + std::string(option_name);
1137  return false;
1138  }
1139  *value = argv[++(*index)];
1140  return true;
1141 }
1142 
1143 bool filename_has_suffix(const std::string& filename, const char* suffix)
1144 {
1145  const std::string normalized_filename = to_lower_ascii(trim_copy(filename));
1146  const std::string normalized_suffix = to_lower_ascii(std::string(suffix == NULL ? "" : suffix));
1147  return normalized_filename.size() >= normalized_suffix.size() &&
1148  normalized_filename.compare(normalized_filename.size() - normalized_suffix.size(),
1149  normalized_suffix.size(),
1150  normalized_suffix) == 0;
1151 }
1152 
1153 bool match_long_option(const std::string& token, const char* option, std::string* attached_value)
1154 {
1155  *attached_value = std::string();
1156  if (token == option) {
1157  return true;
1158  }
1159 
1160  const std::string prefix = std::string(option) + "=";
1161  if (token.size() > prefix.size() && token.compare(0, prefix.size(), prefix) == 0) {
1162  *attached_value = token.substr(prefix.size());
1163  return true;
1164  }
1165 
1166  return false;
1167 }
1168 
1169 bool is_help_topic_candidate(const char* token)
1170 {
1171  return token != NULL && token[0] != '\0' && token[0] != '-' && token[0] != '+';
1172 }
1173 
1174 bool is_long_option_argument_error(const std::string& token,
1175  const char* option,
1176  const std::string& attached_value,
1177  std::string* error)
1178 {
1179  if (attached_value.empty()) {
1180  return false;
1181  }
1182 
1183  *error = "Unexpected argument for " + std::string(option) + " in '" + token + "'";
1184  return true;
1185 }
1186 
1187 bool normalize_runtime_args(int argc,
1188  char** argv,
1189  std::vector<std::string>* normalized,
1190  RuntimeCompatOptions* compat,
1191  std::string* error)
1192 {
1193  normalized->clear();
1194  compat->fallback_mode = ParserFallbackMode::Off;
1195 
1196  if (argc <= 0 || argv == NULL || argv[0] == NULL) {
1197  *error = "Missing program name";
1198  return false;
1199  }
1200 
1201  normalized->push_back(argv[0]);
1202 
1203  for (int i = 1; i < argc; ++i) {
1204  const std::string token = argv[i];
1205  if (token == "+") {
1206  break;
1207  }
1208 
1209  std::string attached_value;
1210 
1211  if (token == "+Help" || match_long_option(token, "--help", &attached_value)) {
1212  std::string topic = "PrM";
1213  if (!attached_value.empty()) {
1214  topic = attached_value;
1215  } else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
1216  topic = argv[++i];
1217  }
1218  normalized->push_back("+Help");
1219  normalized->push_back(topic);
1220  break;
1221  }
1222 
1223  if (token == "+Doc" || match_long_option(token, "--doc", &attached_value)) {
1224  std::string topic = "ALL";
1225  if (!attached_value.empty()) {
1226  topic = attached_value;
1227  } else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
1228  topic = argv[++i];
1229  }
1230  normalized->push_back("+Doc");
1231  normalized->push_back(topic);
1232  break;
1233  }
1234 
1235  if (token == "+Default" || match_long_option(token, "--default", &attached_value)) {
1236  if (token != "+Default" && is_long_option_argument_error(token, "--default", attached_value, error)) {
1237  return false;
1238  }
1239  normalized->push_back("+Default");
1240  break;
1241  }
1242 
1243  if (token == "+Run" || match_long_option(token, "--run", &attached_value)) {
1244  if (token != "+Run" && is_long_option_argument_error(token, "--run", attached_value, error)) {
1245  return false;
1246  }
1247  normalized->push_back("+Run");
1248  continue;
1249  }
1250 
1251  if (token == "+I" || match_long_option(token, "--interactive", &attached_value)) {
1252  if (!attached_value.empty()) {
1253  *error = "Unexpected argument for --interactive in '" + token + "'";
1254  } else {
1255  *error = "Unsupported option " + token + " (interactive mode is not available)";
1256  }
1257  return false;
1258  }
1259 
1260  if (match_long_option(token, "--param-fallback", &attached_value)) {
1261  std::string mode = attached_value;
1262  if (mode.empty()) {
1263  if (i + 1 >= argc) {
1264  *error = "Missing argument after --param-fallback";
1265  return false;
1266  }
1267  mode = argv[++i];
1268  }
1269 
1270  if (to_lower_ascii(trim_copy(mode)) != "legacy") {
1271  *error = "Unsupported value '" + mode + "' for --param-fallback (expected legacy)";
1272  return false;
1273  }
1274 
1275  compat->fallback_mode = ParserFallbackMode::Legacy;
1276  continue;
1277  }
1278 
1279  if (token == "+F" || match_long_option(token, "--file", &attached_value)) {
1280  std::string filename = attached_value;
1281  if (filename.empty()) {
1282  if (i + 1 >= argc) {
1283  *error = "Missing filename after " + token;
1284  return false;
1285  }
1286  filename = argv[++i];
1287  }
1288  normalized->push_back("+F");
1289  normalized->push_back(filename);
1290  continue;
1291  }
1292 
1293  if (token == "+Save" || match_long_option(token, "--save", &attached_value)) {
1294  std::string filename = attached_value;
1295  if (filename.empty()) {
1296  if (i + 1 >= argc) {
1297  *error = "Missing argument after " + token;
1298  return false;
1299  }
1300  filename = argv[++i];
1301  }
1302  normalized->push_back("+Save");
1303  normalized->push_back(filename);
1304  continue;
1305  }
1306 
1307  normalized->push_back(token);
1308  }
1309 
1310  return true;
1311 }
1312 
1313 bool build_legacy_compare_input(int argc, char** argv, LegacyCompareInput* input, std::string* error)
1314 {
1315  input->available = false;
1316  input->runtime_args.clear();
1317  input->unavailable_reason.clear();
1318 
1319  if (argc <= 0 || argv == NULL || argv[0] == NULL) {
1320  *error = "Missing program name";
1321  return false;
1322  }
1323 
1324  input->runtime_args.push_back(argv[0]);
1325  bool saw_passthrough_token_before_file = false;
1326 
1327  for (int i = 1; i < argc; ++i) {
1328  const std::string token = argv[i];
1329  if (token == "+") {
1330  break;
1331  }
1332 
1333  std::string attached_value;
1334 
1335  if (match_long_option(token, "--param-fallback", &attached_value)) {
1336  std::string ignored;
1337  if (!parse_option_argument(&i, argc, argv, attached_value, "--param-fallback", &ignored, error)) {
1338  return false;
1339  }
1340  continue;
1341  }
1342 
1343  if (token == "+Save" || match_long_option(token, "--save", &attached_value)) {
1344  std::string ignored;
1345  if (!parse_option_argument(&i, argc, argv, attached_value, token.c_str(), &ignored, error)) {
1346  return false;
1347  }
1348  continue;
1349  }
1350 
1351  if (token == "+Help" || match_long_option(token, "--help", &attached_value)) {
1352  std::string topic = "PrM";
1353  if (!attached_value.empty()) {
1354  topic = attached_value;
1355  } else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
1356  topic = argv[++i];
1357  }
1358  input->runtime_args.push_back("+Help");
1359  input->runtime_args.push_back(topic);
1360  input->available = true;
1361  return true;
1362  }
1363 
1364  if (token == "+Doc" || match_long_option(token, "--doc", &attached_value)) {
1365  std::string topic = "ALL";
1366  if (!attached_value.empty()) {
1367  topic = attached_value;
1368  } else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
1369  topic = argv[++i];
1370  }
1371  input->runtime_args.push_back("+Doc");
1372  input->runtime_args.push_back(topic);
1373  input->available = true;
1374  return true;
1375  }
1376 
1377  if (token == "+Default" || match_long_option(token, "--default", &attached_value)) {
1378  if (token != "+Default" && is_long_option_argument_error(token, "--default", attached_value, error)) {
1379  return false;
1380  }
1381  input->runtime_args.push_back("+Default");
1382  continue;
1383  }
1384 
1385  if (token == "+Run" || match_long_option(token, "--run", &attached_value)) {
1386  if (token != "+Run" && is_long_option_argument_error(token, "--run", attached_value, error)) {
1387  return false;
1388  }
1389  input->runtime_args.push_back("+Run");
1390  continue;
1391  }
1392 
1393  if (token == "+F" || match_long_option(token, "--file", &attached_value)) {
1394  std::string filename;
1395  if (!parse_option_argument(&i, argc, argv, attached_value, token.c_str(), &filename, error)) {
1396  return false;
1397  }
1398 
1399  if (saw_passthrough_token_before_file) {
1400  input->unavailable_reason =
1401  "legacy compare is unavailable when direct parameter arguments precede a .par input file";
1402  input->runtime_args.clear();
1403  input->runtime_args.push_back(argv[0]);
1404  return true;
1405  }
1406 
1407  if (!filename_has_suffix(filename, ".par")) {
1408  input->unavailable_reason =
1409  "legacy compare is unavailable for non-.par input file '" + filename + "'";
1410  input->runtime_args.clear();
1411  input->runtime_args.push_back(argv[0]);
1412  return true;
1413  }
1414 
1415  input->runtime_args.push_back("+F");
1416  input->runtime_args.push_back(filename);
1417  continue;
1418  }
1419 
1420  input->runtime_args.push_back(token);
1421  saw_passthrough_token_before_file = true;
1422  }
1423 
1424  input->available = input->runtime_args.size() > 1;
1425  if (!input->available && input->unavailable_reason.empty()) {
1426  input->unavailable_reason = "unable to reconstruct a legacy-compatible parameter input";
1427  }
1428  return true;
1429 }
1430 
1431 void populate_arg_pointers(const std::vector<std::string>& values, std::vector<char*>* argv)
1432 {
1433  argv->assign(values.size(), NULL);
1434  for (std::size_t i = 0; i < values.size(); ++i) {
1435  (*argv)[i] = const_cast<char*>(values[i].c_str());
1436  }
1437 }
1438 
1439 void print_lines(FILE* stream, const char* label, const std::vector<std::string>& lines)
1440 {
1441  for (std::size_t i = 0; i < lines.size(); ++i) {
1442  std::fprintf(stream, "%s%s\n", label, lines[i].c_str());
1443  }
1444 }
1445 
1446 ParserCompareMode parser_compare_mode()
1447 {
1448  const char* raw = std::getenv("OPENCARP_PARAM_COMPARE");
1449  if (raw == NULL) {
1450  return ParserCompareMode::Strict;
1451  }
1452 
1453  const std::string normalized = to_lower_ascii(trim_copy(raw));
1454  if (normalized.empty() || normalized == "1" || normalized == "on" || normalized == "true" ||
1455  normalized == "yes" || normalized == "strict" || normalized == "fail" || normalized == "error") {
1456  return ParserCompareMode::Strict;
1457  }
1458  if (normalized == "warn") {
1459  return ParserCompareMode::Warn;
1460  }
1461  if (normalized == "0" || normalized == "off" || normalized == "false" || normalized == "no") {
1462  return ParserCompareMode::Off;
1463  }
1464 
1465  return ParserCompareMode::Strict;
1466 }
1467 
1468 ParserFallbackMode parser_fallback_mode()
1469 {
1470  const char* raw = std::getenv("OPENCARP_PARAM_FALLBACK");
1471  if (raw == NULL) {
1472  return ParserFallbackMode::Off;
1473  }
1474 
1475  const std::string normalized = to_lower_ascii(trim_copy(raw));
1476  if (normalized.empty() || normalized == "0" || normalized == "off" || normalized == "false" ||
1477  normalized == "no") {
1478  return ParserFallbackMode::Off;
1479  }
1480  if (normalized == "legacy") {
1481  return ParserFallbackMode::Legacy;
1482  }
1483 
1484  return ParserFallbackMode::Off;
1485 }
1486 
1487 paramschema::ExecutionResult execute_parser_runtime_args(const std::vector<std::string>& runtime_args,
1488  const bool allow_save)
1489 {
1490  std::vector<char*> argv;
1491  populate_arg_pointers(runtime_args, &argv);
1492 
1493  paramschema::ExecutionOptions options;
1494  options.allow_save = allow_save;
1495  return paramschema::execute_legacy_cli(paramschema::mesher_schema(),
1496  static_cast<int>(argv.size()),
1497  argv.data(),
1498  options);
1499 }
1500 
1501 bool apply_parser_runtime_args(const std::vector<std::string>& runtime_args,
1502  const bool allow_save,
1503  paramschema::ExecutionResult* executed_out)
1504 {
1505  const paramschema::ExecutionResult executed = execute_parser_runtime_args(runtime_args, allow_save);
1506  if (executed_out != NULL) {
1507  *executed_out = executed;
1508  }
1509  print_lines(stderr, "parameter parser warning: ", executed.warnings);
1510  if (!executed.rendered_output.empty()) {
1511  std::fputs(executed.rendered_output.c_str(), stdout);
1512  }
1513  if (!executed.errors.empty() || executed.status == paramschema::ExecutionStatus::Fatal) {
1514  print_lines(stderr, "parameter parser error: ", executed.errors);
1515  return false;
1516  }
1517 
1518  if (executed.status == paramschema::ExecutionStatus::Help) {
1519  exit(EXIT_SUCCESS);
1520  }
1521 
1522  return true;
1523 }
1524 
1525 std::string parent_directory(const std::string& path)
1526 {
1527  const std::string::size_type slash = path.rfind('/');
1528  if (slash == std::string::npos) {
1529  return ".";
1530  }
1531  if (slash == 0) {
1532  return "/";
1533  }
1534  return path.substr(0, slash);
1535 }
1536 
1537 std::string join_path(const std::string& left, const std::string& right)
1538 {
1539  if (left.empty() || left == ".") {
1540  return right;
1541  }
1542  if (!left.empty() && left[left.size() - 1] == '/') {
1543  return left + right;
1544  }
1545  return left + "/" + right;
1546 }
1547 
1548 bool find_executable_on_path(const std::string& name, std::string* resolved_path)
1549 {
1550  if (name.empty() || name.find('/') != std::string::npos) {
1551  return false;
1552  }
1553 
1554  const char* path_env = std::getenv("PATH");
1555  if (path_env == NULL || path_env[0] == '\0') {
1556  return false;
1557  }
1558 
1559  const std::string path_list = path_env;
1560  std::string::size_type start = 0;
1561  while (start <= path_list.size()) {
1562  std::string::size_type end = path_list.find(':', start);
1563  if (end == std::string::npos) {
1564  end = path_list.size();
1565  }
1566 
1567  const std::string directory = path_list.substr(start, end - start);
1568  const std::string candidate = join_path(directory.empty() ? "." : directory, name);
1569  if (access(candidate.c_str(), X_OK) == 0) {
1570  *resolved_path = candidate;
1571  return true;
1572  }
1573 
1574  if (end == path_list.size()) {
1575  break;
1576  }
1577  start = end + 1;
1578  }
1579 
1580  return false;
1581 }
1582 
1583 bool resolve_legacy_snapshot_helper(const std::string& program_path, std::string* helper_path)
1584 {
1585  std::vector<std::string> resolved_program_paths;
1586  if (!program_path.empty()) {
1587  resolved_program_paths.push_back(program_path);
1588  }
1589 
1590  if (program_path.find('/') == std::string::npos) {
1591  std::string resolved_program_path;
1592  if (find_executable_on_path(program_path, &resolved_program_path)) {
1593  resolved_program_paths.push_back(resolved_program_path);
1594  }
1595  }
1596 
1597  for (std::size_t i = 0; i < resolved_program_paths.size(); ++i) {
1598  const std::string program_dir = parent_directory(resolved_program_paths[i]);
1599  const std::string parent_dir = parent_directory(program_dir);
1600 
1601  const std::vector<std::string> candidates = {
1602  join_path(program_dir, "mesher-param-parser-legacy-snapshot"),
1603  join_path(join_path(parent_dir, "tools/mesher"), "mesher-param-parser-legacy-snapshot"),
1604  };
1605 
1606  for (std::size_t j = 0; j < candidates.size(); ++j) {
1607  if (access(candidates[j].c_str(), X_OK) == 0) {
1608  *helper_path = candidates[j];
1609  return true;
1610  }
1611  }
1612  }
1613 
1614  return find_executable_on_path("mesher-param-parser-legacy-snapshot", helper_path);
1615 }
1616 
1617 bool create_temp_output_path(const char* suffix, std::string* path)
1618 {
1619  char temp_path[128];
1620  if (suffix != NULL && suffix[0] != '\0') {
1621  std::snprintf(temp_path, sizeof temp_path, "/tmp/mesher-parser-compare-XXXXXX%s", suffix);
1622  } else {
1623  std::snprintf(temp_path, sizeof temp_path, "/tmp/mesher-parser-compare-XXXXXX");
1624  }
1625 
1626  const int fd = suffix != NULL && suffix[0] != '\0' ? mkstemps(temp_path, static_cast<int>(std::strlen(suffix))) :
1627  mkstemp(temp_path);
1628  if (fd < 0) {
1629  return false;
1630  }
1631  close(fd);
1632  *path = temp_path;
1633  return true;
1634 }
1635 
1636 bool capture_current_parser_snapshot(paramschema::SnapshotResult* snapshot)
1637 {
1638  *snapshot = paramschema::snapshot_schema_state(paramschema::mesher_schema());
1639  print_lines(stderr, "parameter compare warning: ", snapshot->warnings);
1640  if (!snapshot->errors.empty()) {
1641  print_lines(stderr, "parameter compare error: ", snapshot->errors);
1642  return false;
1643  }
1644  return true;
1645 }
1646 
1647 void maybe_force_test_mismatch(paramschema::SnapshotResult* snapshot)
1648 {
1649  const char* raw = std::getenv("OPENCARP_PARAM_TEST_FORCE_MISMATCH");
1650  if (raw == NULL) {
1651  return;
1652  }
1653 
1654  const std::string normalized = to_lower_ascii(trim_copy(raw));
1655  if (normalized.empty() || normalized == "0" || normalized == "off" || normalized == "false" ||
1656  normalized == "no") {
1657  return;
1658  }
1659 
1660  if (!snapshot->entries.empty()) {
1661  snapshot->entries[0].value += "__forced_parser_compare_mismatch__";
1662  return;
1663  }
1664 
1665  paramschema::SnapshotEntry entry;
1666  entry.path = "mesh";
1667  entry.value = "__forced_parser_compare_mismatch__";
1668  snapshot->entries.push_back(entry);
1669 }
1670 
1671 bool run_legacy_snapshot_helper(const std::string& helper_path,
1672  const std::vector<std::string>& runtime_args,
1673  const std::string& output_path)
1674 {
1675  std::vector<std::string> child_values;
1676  child_values.reserve(runtime_args.size() + 4);
1677  child_values.push_back(helper_path);
1678  child_values.push_back("--snapshot-out");
1679  child_values.push_back(output_path);
1680  child_values.push_back("--");
1681  child_values.insert(child_values.end(), runtime_args.begin(), runtime_args.end());
1682 
1683  std::vector<char*> child_argv;
1684  child_argv.reserve(child_values.size() + 1);
1685  for (std::size_t i = 0; i < child_values.size(); ++i) {
1686  child_argv.push_back(const_cast<char*>(child_values[i].c_str()));
1687  }
1688  child_argv.push_back(NULL);
1689 
1690  const pid_t pid = fork();
1691  if (pid < 0) {
1692  std::perror("fork");
1693  return false;
1694  }
1695 
1696  if (pid == 0) {
1697  execv(helper_path.c_str(), child_argv.data());
1698  std::perror("execv");
1699  _exit(127);
1700  }
1701 
1702  int status = 0;
1703  if (waitpid(pid, &status, 0) < 0) {
1704  std::perror("waitpid");
1705  return false;
1706  }
1707 
1708  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1709  if (WIFSIGNALED(status)) {
1710  std::fprintf(stderr, "parameter compare error: legacy snapshot helper terminated with signal %d\n",
1711  WTERMSIG(status));
1712  } else {
1713  std::fprintf(stderr, "parameter compare error: legacy snapshot helper exited with status %d\n",
1714  WEXITSTATUS(status));
1715  }
1716  return false;
1717  }
1718 
1719  return true;
1720 }
1721 
1722 bool legacy_fallback_requested(const RuntimeCompatOptions& compat)
1723 {
1724  return compat.fallback_mode == ParserFallbackMode::Legacy ||
1725  parser_fallback_mode() == ParserFallbackMode::Legacy;
1726 }
1727 
1728 void print_legacy_fallback_workaround()
1729 {
1730  std::fprintf(stderr,
1731  "parameter compare error: rerun with OPENCARP_PARAM_FALLBACK=legacy to continue with the legacy parameter state\n");
1732  std::fprintf(stderr,
1733  "parameter compare error: or add --param-fallback=legacy to the mesher command line\n");
1734 }
1735 
1736 bool restore_legacy_snapshot_state(const paramschema::SnapshotResult& legacy_snapshot)
1737 {
1738  const paramschema::SnapshotRestoreResult restored =
1739  paramschema::restore_snapshot_state(paramschema::mesher_schema(), legacy_snapshot);
1740  print_lines(stderr, "parameter compare warning: ", restored.warnings);
1741  if (!restored.errors.empty()) {
1742  print_lines(stderr, "parameter compare error: ", restored.errors);
1743  return false;
1744  }
1745  return true;
1746 }
1747 
1748 bool run_parser_legacy_compare(const std::vector<std::string>& runtime_args,
1749  const LegacyCompareInput& legacy_input,
1750  const RuntimeCompatOptions& compat)
1751 {
1752  const ParserCompareMode mode = parser_compare_mode();
1753  if (mode == ParserCompareMode::Off) {
1754  return true;
1755  }
1756  const bool fallback_to_legacy = legacy_fallback_requested(compat);
1757 
1758  if (runtime_args.empty()) {
1759  std::fprintf(stderr, "parameter compare error: unable to reconstruct mesher argv\n");
1760  if (fallback_to_legacy) {
1761  print_legacy_fallback_workaround();
1762  }
1763  return mode != ParserCompareMode::Strict;
1764  }
1765 
1766  paramschema::SnapshotResult parser_snapshot;
1767  if (!capture_current_parser_snapshot(&parser_snapshot)) {
1768  std::fprintf(stderr, "parameter compare error: unable to snapshot the parser runtime state\n");
1769  if (fallback_to_legacy) {
1770  print_legacy_fallback_workaround();
1771  }
1772  return mode != ParserCompareMode::Strict;
1773  }
1774  maybe_force_test_mismatch(&parser_snapshot);
1775 
1776  std::string helper_path;
1777  if (!resolve_legacy_snapshot_helper(runtime_args[0], &helper_path)) {
1778  std::fprintf(stderr, "parameter compare error: unable to locate mesher-param-parser-legacy-snapshot\n");
1779  if (fallback_to_legacy) {
1780  print_legacy_fallback_workaround();
1781  } else {
1782  std::fprintf(stderr,
1783  "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1784  }
1785  return mode != ParserCompareMode::Strict;
1786  }
1787 
1788  if (!legacy_input.available) {
1789  std::fprintf(stderr, "parameter compare warning: %s\n", legacy_input.unavailable_reason.c_str());
1790  if (fallback_to_legacy) {
1791  std::fprintf(stderr,
1792  "parameter compare warning: legacy fallback is unavailable because no legacy baseline exists for this input\n");
1793  }
1794  return true;
1795  }
1796 
1797  std::string snapshot_path;
1798  if (!create_temp_output_path("", &snapshot_path)) {
1799  std::fprintf(stderr, "parameter compare error: unable to create temporary snapshot file\n");
1800  if (fallback_to_legacy) {
1801  print_legacy_fallback_workaround();
1802  } else {
1803  std::fprintf(stderr,
1804  "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1805  }
1806  return mode != ParserCompareMode::Strict;
1807  }
1808 
1809  const bool helper_ok = run_legacy_snapshot_helper(helper_path, legacy_input.runtime_args, snapshot_path);
1810  paramschema::SnapshotResult legacy_snapshot;
1811  std::string read_error;
1812  const bool loaded = helper_ok &&
1813  paramschema::snapshotio::read_snapshot_file(snapshot_path, &legacy_snapshot, &read_error);
1814  unlink(snapshot_path.c_str());
1815 
1816  if (!loaded) {
1817  if (!read_error.empty()) {
1818  std::fprintf(stderr, "parameter compare error: %s\n", read_error.c_str());
1819  } else {
1820  std::fprintf(stderr, "parameter compare error: unable to capture the legacy parameter state\n");
1821  }
1822  if (fallback_to_legacy) {
1823  print_legacy_fallback_workaround();
1824  } else {
1825  std::fprintf(stderr,
1826  "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1827  }
1828  return mode != ParserCompareMode::Strict;
1829  }
1830 
1831  const paramschema::SnapshotComparisonResult comparison =
1832  paramschema::compare_snapshot_results(paramschema::mesher_schema(), parser_snapshot, legacy_snapshot);
1833  if (!comparison.errors.empty() || !comparison.mismatches.empty()) {
1834  print_lines(stderr, "", paramschema::format_snapshot_comparison_report(comparison, "parser compare"));
1835  std::fprintf(stderr,
1836  "parameter compare error: the parser runtime and legacy param() produced different parameter states\n");
1837  std::fprintf(stderr,
1838  "parameter compare error: please open an issue and include the triggering command line and parameter files\n");
1839  if (fallback_to_legacy) {
1840  if (!restore_legacy_snapshot_state(legacy_snapshot)) {
1841  print_legacy_fallback_workaround();
1842  return false;
1843  }
1844  std::fprintf(stderr, "parameter compare warning: continuing with the legacy parameter state\n");
1845  return true;
1846  }
1847 
1848  print_legacy_fallback_workaround();
1849  return mode != ParserCompareMode::Strict;
1850  }
1851 
1852  return true;
1853 }
1854 
1855 } // namespace
1856 
1857 
1858 int main( int argc, char *argv[] )
1859 {
1860  LegacyCompareInput legacy_input;
1861  std::vector<std::string> normalized_args;
1862  RuntimeCompatOptions compat;
1863  std::string normalize_error;
1864  if (!build_legacy_compare_input(argc, argv, &legacy_input, &normalize_error) ||
1865  !normalize_runtime_args(argc, argv, &normalized_args, &compat, &normalize_error)) {
1866  std::fprintf(stderr, "\n*** %s\n\n", normalize_error.c_str());
1867  exit(EXIT_FAILURE);
1868  }
1869 
1870  paramschema::ExecutionResult executed;
1871  if (!apply_parser_runtime_args(normalized_args, true, &executed)) {
1872  exit(EXIT_FAILURE);
1873  }
1874 
1875  if (legacy_input.available) {
1876  for (std::size_t i = 0; i < executed.validation.assignments.size(); ++i) {
1877  if (!executed.validation.assignments[i].synthesized) {
1878  continue;
1879  }
1880  legacy_input.available = false;
1881  legacy_input.runtime_args.clear();
1882  legacy_input.runtime_args.push_back(normalized_args[0]);
1883  legacy_input.unavailable_reason =
1884  "legacy compare is unavailable because the parser inferred optional controller counts from the original input";
1885  break;
1886  }
1887  }
1888 
1889  if (!run_parser_legacy_compare(normalized_args, legacy_input, compat)) {
1890  exit(EXIT_FAILURE);
1891  }
1892 
1893  float tsize[3], x0[3];
1894  bool symbath[3];
1895 
1896 
1897  for( int i=0; i<3; i++ ) {
1898  param_globals::size[i] *= CM2UM;
1899  param_globals::bath[i] *= CM2UM;
1900  param_globals::center[i] *= CM2UM;
1901  // in 2D case we don't allow a bath attached in the direction
1902  // perpendicular to the 2D surface
1903  if(param_globals::size[i]==0.) param_globals::bath[i] = 0.0;
1904  if(param_globals::bath[i]>=0) {
1905  tsize[i] = param_globals::size[i]+param_globals::bath[i];
1906  symbath[i] = false;
1907  x0[i] = -0.5*param_globals::size[i]+param_globals::center[i];
1908  }
1909  else {
1910  tsize[i] = param_globals::size[i]-2*param_globals::bath[i];
1911  symbath[i] = true;
1912  x0[i] = -0.5*tsize[i]+param_globals::center[i];
1913  }
1914  }
1915 
1916  Region **region = (Region**)calloc(param_globals::numRegions+1,sizeof(Region*));
1917  Point ctr;
1918  ctr.x = param_globals::center[0];
1919  ctr.y = param_globals::center[1];
1920  ctr.z = param_globals::center[2];
1921 
1922  RegionDef* regdef = param_globals::regdef;
1923 
1924  for (int r = 0; r < param_globals::numRegions; r++ ) {
1925  Point p0 = scal_X( p_assign_array(regdef[r].p0), CM2UM );
1926  if( !param_globals::size[2]) p0.z = 0;
1927  Point p1;
1928  switch(regdef[r].type) {
1929  case 0:
1930  if( !param_globals::size[2] ) p1.z = 0;
1931  p1 = scal_X( p_assign_array( regdef[r].p1 ), CM2UM );
1932  p1 = p1 + ctr;
1933  if( p0.x>p1.x ) std::swap( p0.x, p1.x );
1934  if( p0.y>p1.y ) std::swap( p0.y, p1.y );
1935  if( p0.z>p1.z ) std::swap( p0.z, p1.z );
1936  region[r] = new BlockRegion( p0, p1, regdef[r].bath );
1937  break;
1938  case 1:
1939  regdef[r].rad *= CM2UM;
1940  region[r] = new SphericalRegion( p0,regdef[r].rad, regdef[r].bath );
1941  break;
1942  case 2:
1943  Point axis;
1944  if( !param_globals::size[2] )
1945  axis = {0,0,1};
1946  else
1947  axis = p_assign_array( regdef[r].p1 );
1948  regdef[r].rad *= CM2UM;
1949  regdef[r].cyllen *= CM2UM;
1950  region[r] = new CylindricalRegion( p0, axis, regdef[r].rad,
1951  regdef[r].cyllen, regdef[r].bath );
1952  break;
1953  }
1954  region[r]->tag(regdef[r].tag);
1955  }
1956  region[param_globals::numRegions] = NULL;
1957 
1958 
1959  Grid *grid;
1960  if( !param_globals::size[1] && !param_globals::size[2] )
1961  grid = new Grid1D( param_globals::mesh, region );
1962  else if( !param_globals::size[2] )
1963  grid = new Grid2D( param_globals::mesh, region );
1964  else {
1965  grid = new Grid3D( param_globals::mesh, region );
1966  }
1967 
1968  if( grid->os_good() ) {
1969  grid->unPrMFiberDefs();
1970  grid->build_mesh(x0, tsize, param_globals::size, symbath, param_globals::resolution,
1971  param_globals::perturb, param_globals::anisoBath, param_globals::periodic);
1972  delete grid;
1973  }
1974  else {
1975  std::cerr << "ERROR:" << std::endl;
1976  std::cerr << "Could not open mesh files " << param_globals::mesh << ".* for writing!" << std::endl;
1977  std::cerr << "Aborting!" << std::endl;
1978  delete grid;
1979  return 1;
1980  }
1981 
1982  return 0;
1983 }
#define M_PI
Definition: ION_IF.h:52
void update(Point p)
Definition: mesher.cc:359
virtual bool inside(Point p)
Definition: mesher.cc:154
BlockRegion(Point p0, Point p, int bth)
Definition: mesher.cc:138
int * bx
Definition: mesher.cc:377
float z2xi(float z, bool nodeGrid)
Definition: mesher.cc:388
BBoxDef bctrs
Definition: mesher.cc:383
float mx_z(bool nodeGrid)
Definition: mesher.cc:377
float * res
Definition: mesher.cc:382
BBoxDef nodes
Definition: mesher.cc:384
void dims(void)
Definition: mesher.cc:439
int bx_inds[3][2]
Definition: mesher.cc:380
virtual ~BoundingBox()
Definition: mesher.cc:372
void init(int *_bx, float *_res, Point p0)
Definition: mesher.cc:400
float mn_z(bool nodeGrid)
Definition: mesher.cc:376
virtual bool inside(Point p)
Definition: mesher.cc:174
CylindricalRegion(Point origin, Point dir, float r, float l, int bth)
Definition: mesher.cc:163
~Element()
Definition: mesher.cc:186
int n_
Definition: mesher.cc:192
int num()
Definition: mesher.cc:188
TisAxes ax
Definition: mesher.cc:190
Element(int N, const char *t)
Definition: mesher.cc:185
Point centre(Point *ctr)
Definition: mesher.cc:198
std::string type_
Definition: mesher.cc:194
int * p_
Definition: mesher.cc:193
virtual void output_boundary(char *fn)
Definition: mesher.cc:544
Grid1D(char *m, Region **r)
Definition: mesher.cc:545
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
Definition: mesher.cc:697
Grid2D(char *m, Region **r)
Definition: mesher.cc:527
virtual void output_boundary(char *fn)
Definition: mesher.cc:526
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
Definition: mesher.cc:781
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
Definition: mesher.cc:923
virtual void output_boundary(char *fn)
Definition: mesher.cc:535
Grid3D(char *m, Region **r)
Definition: mesher.cc:536
Definition: mesher.cc:498
virtual void output_boundary(char *)=0
fibDef f_def
Definition: mesher.cc:517
std::ofstream pt_os
Definition: mesher.cc:514
virtual ~Grid()
Definition: mesher.cc:501
BoundingBox t_bbx
Definition: mesher.cc:513
std::ofstream elem_os
Definition: mesher.cc:514
bool os_good()
Definition: mesher.cc:551
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)=0
int dim
Definition: mesher.cc:520
void set_indx_bounds(bool *sym)
Definition: mesher.cc:589
std::ofstream lon_os
Definition: mesher.cc:514
std::ofstream vec_os
Definition: mesher.cc:514
Point * pt
Definition: mesher.cc:510
Grid(char *, Region **, int)
Definition: mesher.cc:557
int num_axes
Definition: mesher.cc:516
void add_element(Element &, region_t)
Definition: mesher.cc:642
std::ofstream elemc_os
Definition: mesher.cc:514
int npt
Definition: mesher.cc:511
void unPrMFiberDefs(void)
Definition: mesher.cc:625
tmProfile f_xi
Definition: mesher.cc:518
bool chk_bath(int i, int j, int k)
Definition: mesher.cc:611
BoundingBox b_bbx
Definition: mesher.cc:512
tmProfile s_xi
Definition: mesher.cc:519
Region ** region
Definition: mesher.cc:515
Hexahedron(int A, int B, int C, int D, int E, int F, int G, int H)
Definition: mesher.cc:224
Definition: mesher.cc:245
Line(int A, int B)
Definition: mesher.cc:247
Quadrilateral(int A, int B, int C, int D)
Definition: mesher.cc:231
virtual bool inside(Point)=0
bool isbath()
Definition: mesher.cc:127
int tag_
Definition: mesher.cc:133
Region(Point p, int b)
Definition: mesher.cc:124
Point p0_
Definition: mesher.cc:131
virtual ~Region()
Definition: mesher.cc:125
int tag()
Definition: mesher.cc:128
void tag(int t)
Definition: mesher.cc:129
int bath_
Definition: mesher.cc:132
virtual bool inside(Point p)
Definition: mesher.cc:148
SphericalRegion(Point ctr, float r, int bth)
Definition: mesher.cc:146
Tetrahedron(int A, int B, int C, int D)
Definition: mesher.cc:207
void chkNegVolume(Point *pts)
Definition: mesher.cc:213
void set_axes(float alpha_, float beta_pr_, float gamma_)
Definition: mesher.cc:99
void set_xi(float xi_)
Definition: mesher.cc:83
Point fiber(void)
Definition: mesher.cc:86
void set_bath_axes(bool)
Definition: mesher.cc:114
Point sheet(void)
Definition: mesher.cc:87
Triangle(int A, int B, int C)
Definition: mesher.cc:239
char * s_name(void)
Definition: mesher.cc:458
float sheetEpi()
Definition: mesher.cc:463
float imbrication()
Definition: mesher.cc:459
float rotEpi()
Definition: mesher.cc:461
float rotEndo()
Definition: mesher.cc:460
void setFiberDefs(char *f_prof, float fEndo, float fEpi, float imbr, char *s_prof, float sEndo, float sEpi)
Definition: mesher.cc:475
float sheetEndo()
Definition: mesher.cc:462
bool withSheets(void)
Definition: mesher.cc:488
char * f_name(void)
Definition: mesher.cc:457
int read(char *fname)
Definition: mesher.cc:323
int linear(float ang_endo, float ang_epi, float z_endo, float z_epi)
Definition: mesher.cc:279
float lookup(float xi)
Definition: mesher.cc:309
~tmProfile()
Definition: mesher.cc:267
int main(int argc, char *argv[])
Definition: mesher.cc:1858
const float CM2UM
Definition: mesher.cc:79
const Point e_circ
Definition: mesher.cc:73
const Point e_rad
Definition: mesher.cc:75
region_t
Definition: mesher.cc:61
@ Anisobath
Definition: mesher.cc:61
@ Isobath
Definition: mesher.cc:61
@ Myocardium
Definition: mesher.cc:61
const Point e_long
Definition: mesher.cc:74
#define BOX_CENTERS_GRID
Definition: mesher.cc:63
std::ostream & operator<<(std::ostream &out, Element &e)
Definition: mesher.cc:251
Point p_assign_array(float *p)
Definition: mesher.cc:66
axis
split axis
Definition: kdpart.hpp:63
constexpr T min(T a, T b)
Definition: ion_type.h:33
constexpr T max(T a, T b)
Definition: ion_type.h:31
V det3(const vec3< V > &a, const vec3< V > &b, const vec3< V > &c)
Definition: vect.h:96
vec3< V > scal_X(const vec3< V > &a, S k)
Definition: vect.h:150
V dot(const vec3< V > &p1, const vec3< V > &p2)
Definition: vect.h:125
vec3< V > normalize(vec3< V > a)
Definition: vect.h:198
V dist_2(const vec3< V > &p1, const vec3< V > &p2)
Definition: vect.h:102
V mag2(const vec3< V > &vect)
Definition: vect.h:138
std::vector< std::string > runtime_args
Definition: sim_utils.cc:78
ParserFallbackMode fallback_mode
Definition: sim_utils.cc:73
bool available
Definition: sim_utils.cc:77
std::string unavailable_reason
Definition: sim_utils.cc:79
void assign(S ix, S iy, S iz)
Definition: vect.h:45