54 #include "mesher_schema.hpp"
55 #include "runtime.hpp"
56 #include "snapshot_file_io.hpp"
63 #define BOX_CENTERS_GRID false
64 #define NODE_GRID true
69 a.
x = p[0]; a.
y=p[1]; a.
z=p[2];
84 void set_axes(
float alpha_,
float beta_pr_,
float gamma_);
85 void set_bath_axes(
bool);
102 beta_pr = c/180*
M_PI;
105 Point p(cos(alpha), sin(alpha), sin(gamma));
108 Point sp(0.0, sin(
double(beta_pr)), cos(
double(beta_pr)));
117 s.x = s.y = s.z = 0.0;
119 f.x = aniso_bath?1.0:0.0;
128 int tag() {
return tag_; }
129 void tag(
int t) { tag_ = t; }
139 virtual bool inside(
Point p);
147 Region(ctr, bth), radius2_(r*r) {}
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;
164 Region(origin, bth), radius2_(r*r)
165 {axis_=
normalize(dir);len_=(l==0.)?1.e36:l;}
166 virtual bool inside(
Point p );
177 float d =
dot(R, axis_);
179 return d>=0 && d<=len_ &&
mag2(R)-d*d <= radius2_;
185 Element(
int N,
const char *t):n_(N),p_(new int[N]),type_(t){}
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_);
208 p_[0]=A; p_[1]=B; p_[2]=C; p_[3]=D; }
209 void chkNegVolume(
Point *pts);
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);
219 std::swap(p_[0],p_[1]);
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; }
232 p_[0] = A; p_[1] = B; p_[2] = C; p_[3] = D;
240 p_[0] = A; p_[1] = B; p_[2] = C;
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];
260 out << p.
x <<
" " << p.
y <<
" " << p.
z;
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 );
285 N = (z_epi-z_endo)/dz;
287 xi = (
float *)malloc(
sizeof(
float)*(N+1));
288 ang = (
float *)malloc(
sizeof(
float)*(N+1));
290 if(xi==NULL || ang==NULL) {
291 std::cerr <<
"Memory allocation failed." << std::endl;
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);
311 if( !N )
return ang[0];
314 while(xi[i]<xi_ && i<N) i++;
319 return ang[i-1]+(ang[i]-ang[i-1])/(xi[i]-xi[i-1])*(xi_-xi[i-1]);
325 FILE *profile = fopen(fname,
"rt");
327 fprintf( stderr,
"Can't open transmural profile data file %s.\n", fname);
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);
345 void update(
Point p );
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;
373 void init(
int *_bx,
float *_res,
Point p0);
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; };
390 float zd = nodeGrid?nodes.zd:bctrs.zd;
391 float z_mn = nodeGrid?nodes.z_mn:bctrs.z_mn;
396 return (z-z_mn)/zd-0.5;
404 for(
int i=0;i<3;i++) {
408 bx_inds[i][1] = bx[i]-1;
416 pc0.
x = p0.
x + res[0]/2;
417 pc0.
y = p0.
y + res[1]/2;
418 pc0.
z = p0.
z + res[2]/2;
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];
430 pc1.
x = p1.
x - res[0]/2;
431 pc1.
y = p1.
y - res[1]/2;
432 pc1.
z = p1.
z - res[2]/2;
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;
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;
454 void setFiberDefs(
char *f_prof,
float fEndo,
float fEpi,
float imbr,
455 char *s_prof,
float sEndo,
float sEpi);
456 bool withSheets(
void);
476 char *s_prof,
float sEndo,
float sEpi)
478 f_Prof = strdup(f_prof);
482 s_Prof = strdup(s_prof);
490 if((f_imbr!=0.0) || (s_Endo!=0.0) || (s_Epi!=0.0) || (strcmp(s_Prof,
"")))
502 void unPrMFiberDefs(
void);
503 virtual void build_mesh(
float*,
float*,
float*,
bool *,
float*,
float,
bool,
int)=0;
506 void set_indx_bounds(
bool *sym);
507 bool chk_bath(
int i,
int j,
int k );
514 std::ofstream pt_os,
elem_os, lon_os, elemc_os, vec_os;
525 virtual void build_mesh(
float*,
float*,
float*,
bool *,
float*,
float,
bool,
int);
529 void add_tri(
int,
int,
int );
534 virtual void build_mesh(
float*,
float*,
float*,
bool *,
float*,
float,
bool,
int);
538 void add_tet(
int,
int,
int,
int );
543 virtual void build_mesh(
float*,
float*,
float*,
bool *,
float*,
float,
bool,
int);
547 void add_line(
int,
int,
int,
int );
553 return pt_os.good() && lon_os.good() && elem_os.good();
559 std::string fname(msh);
562 lon_os.open(fname.c_str());
566 pt_os.open(fname.c_str());
578 vec_os.open(fname.c_str());
591 for(
int i=0;i<3;i++) {
613 bool bth[] = {
false,
false,
false };
614 int idx[] = { i, j, k };
616 for(
int cnt=0; cnt<
dim; cnt++ )
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);
649 while(
region[++r] != NULL ) {
650 if(
region[r]->inside( c ) ) {
651 if(
region[r]->isbath() ) {
657 if(param_globals::first_reg)
673 elem_os <<
" " << regid << std::endl;
698 float pert,
bool aniso_bath,
int periodic_bc)
706 for(
int i=0; i<3; i++ ) {
707 bnx[i] = (int)(x[i]/res[i]);
708 tnx[i] = (int)(tissue[i]/res[i]);
713 pt0.
x = -tnx[0]*res[0]/2 + x0[0];
717 pb0.
x = sym[0]?pt0.
x-(bnx[0]-tnx[0])*res[0]/2:pt0.
x+x0[0];
733 std::cout <<
"Number of points: " <<
npt << std::endl;
737 for(
int i=0; i<=bnx[0]; i++ ) {
741 pt[
npt].
x += ((double)random()/(double)RAND_MAX)*pert*res[0];
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;
751 for(
int i=0; i<bnx[0]; i++ ) {
782 float pert,
bool aniso_bath,
int periodic_bc)
790 for(
int i=0; i<3; i++ ) {
791 bnx[i] = (int)(x[i]/res[i]);
792 tnx[i] = (int)(tissue[i]/res[i]);
797 pt0.
x = -tnx[0]*res[0]/2 + x0[0];
798 pt0.
y = -tnx[1]*res[1]/2 + x0[1];
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];
813 npt = (bnx[0]+1)*(bnx[1]+1);
814 std::cout <<
"Number of points: " <<
npt << std::endl;
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);
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];
832 if( (periodic_bc&1) == 1 )
833 nper_cnnx += bnx[1]+1;
834 if( (periodic_bc&2) == 2 )
835 nper_cnnx += bnx[0]+1;
837 int num_in_layer = (bnx[0]+1)*(bnx[1]+1);
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;
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;
849 std::cout <<
"Number of periodic connections: " << nper_cnnx << std::endl;
857 std::cout <<
"Using orthotropic fiber setup." << std::endl;
859 std::cout <<
"Using transversely isotropic fiber setup." << std::endl;
861 for(
int j=0; j<bnx[1]; j++ ) {
863 for(
int i=0; i<bnx[0]; i++ ) {
864 p1 = j*(bnx[0]+1) + i;
866 p3 = (j+1)*(bnx[0]+1) + i;
873 if( param_globals::tri2D ) {
875 Triangle t1(p1, p2, p3), t2(p2, p4, p3);
879 Triangle t1(p1, p2, p4), t2(p1, p4, p3);
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;
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;
924 float pert,
bool aniso_bath,
int periodic_bc)
927 int p1, p2, p3, p4, p5, p6, p7,p8;
932 for(
int i=0; i<3; i++ ) {
933 bnx[i] = (int)(x[i]/res[i]);
934 tnx[i] = (int)(tissue[i]/res[i]);
939 pt0 = {x0[0],x0[1],x0[2]};
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];
955 std::cout <<
"Reading transmural fiber rotation profile from " <<
f_def.
f_name() << std::endl;
963 std::cout <<
"Reading transmural sheet profile from " <<
f_def.
s_name() << std::endl;
971 npt = (bnx[0]+1)*(bnx[1]+1)*(bnx[2]+1);
972 std::cout <<
"Number of points: " <<
npt << std::endl;
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];
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;
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;
1004 std::cout <<
"Using orthotropic fiber setup." << std::endl;
1006 std::cout <<
"Using transversely isotropic fiber setup." << std::endl;
1009 for(
int k=0; k<bnx[2]; k++ ) {
1011 for(
int j=0; j<bnx[1]; j++ ) {
1014 if(j && !(bnx[0]%2) )
1017 for(
int i=0; i<bnx[0]; i++ ) {
1018 p1 = k*num_in_layer + j*(bnx[0]+1) + i;
1020 p3 = k*num_in_layer + (j+1)*(bnx[0]+1) + i;
1022 p5 = (k+1)*num_in_layer + j*(bnx[0]+1) + i;
1024 p7 = (k+1)*num_in_layer + (j+1)*(bnx[0]+1) + i;
1031 if(!param_globals::Elem3D) {
1064 Hexahedron h1(p5, p7, p8, p6, p1, p2, p4, p3);
1079 enum class ParserCompareMode {
1085 enum class ParserFallbackMode {
1090 struct RuntimeCompatOptions {
1094 struct LegacyCompareInput {
1100 std::string trim_copy(
const std::string& value)
1102 std::string::size_type first = 0;
1103 while (first < value.size() && std::isspace(
static_cast<unsigned char>(value[first]))) {
1107 std::string::size_type last = value.size();
1108 while (last > first && std::isspace(
static_cast<unsigned char>(value[last - 1]))) {
1112 return value.substr(first, last - first);
1115 std::string to_lower_ascii(std::string value)
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])));
1123 bool parse_option_argument(
int* index,
1126 const std::string& attached_value,
1127 const char* option_name,
1131 if (!attached_value.empty()) {
1132 *value = attached_value;
1135 if (*index + 1 >= argc) {
1136 *error =
"Missing argument after " + std::string(option_name);
1139 *value = argv[++(*index)];
1143 bool filename_has_suffix(
const std::string&
filename,
const char* suffix)
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;
1153 bool match_long_option(
const std::string& token,
const char* option, std::string* attached_value)
1155 *attached_value = std::string();
1156 if (token == option) {
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());
1169 bool is_help_topic_candidate(
const char* token)
1171 return token != NULL && token[0] !=
'\0' && token[0] !=
'-' && token[0] !=
'+';
1174 bool is_long_option_argument_error(
const std::string& token,
1176 const std::string& attached_value,
1179 if (attached_value.empty()) {
1183 *error =
"Unexpected argument for " + std::string(option) +
" in '" + token +
"'";
1187 bool normalize_runtime_args(
int argc,
1189 std::vector<std::string>* normalized,
1190 RuntimeCompatOptions* compat,
1193 normalized->clear();
1194 compat->fallback_mode = ParserFallbackMode::Off;
1196 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
1197 *error =
"Missing program name";
1201 normalized->push_back(argv[0]);
1203 for (
int i = 1; i < argc; ++i) {
1204 const std::string token = argv[i];
1209 std::string attached_value;
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])) {
1218 normalized->push_back(
"+Help");
1219 normalized->push_back(topic);
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])) {
1230 normalized->push_back(
"+Doc");
1231 normalized->push_back(topic);
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)) {
1239 normalized->push_back(
"+Default");
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)) {
1247 normalized->push_back(
"+Run");
1251 if (token ==
"+I" || match_long_option(token,
"--interactive", &attached_value)) {
1252 if (!attached_value.empty()) {
1253 *error =
"Unexpected argument for --interactive in '" + token +
"'";
1255 *error =
"Unsupported option " + token +
" (interactive mode is not available)";
1260 if (match_long_option(token,
"--param-fallback", &attached_value)) {
1261 std::string mode = attached_value;
1263 if (i + 1 >= argc) {
1264 *error =
"Missing argument after --param-fallback";
1270 if (to_lower_ascii(trim_copy(mode)) !=
"legacy") {
1271 *error =
"Unsupported value '" + mode +
"' for --param-fallback (expected legacy)";
1275 compat->fallback_mode = ParserFallbackMode::Legacy;
1279 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
1280 std::string
filename = attached_value;
1282 if (i + 1 >= argc) {
1283 *error =
"Missing filename after " + token;
1288 normalized->push_back(
"+F");
1293 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
1294 std::string
filename = attached_value;
1296 if (i + 1 >= argc) {
1297 *error =
"Missing argument after " + token;
1302 normalized->push_back(
"+Save");
1307 normalized->push_back(token);
1313 bool build_legacy_compare_input(
int argc,
char** argv, LegacyCompareInput* input, std::string* error)
1315 input->available =
false;
1316 input->runtime_args.clear();
1317 input->unavailable_reason.clear();
1319 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
1320 *error =
"Missing program name";
1324 input->runtime_args.push_back(argv[0]);
1325 bool saw_passthrough_token_before_file =
false;
1327 for (
int i = 1; i < argc; ++i) {
1328 const std::string token = argv[i];
1333 std::string attached_value;
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)) {
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)) {
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])) {
1358 input->runtime_args.push_back(
"+Help");
1359 input->runtime_args.push_back(topic);
1360 input->available =
true;
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])) {
1371 input->runtime_args.push_back(
"+Doc");
1372 input->runtime_args.push_back(topic);
1373 input->available =
true;
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)) {
1381 input->runtime_args.push_back(
"+Default");
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)) {
1389 input->runtime_args.push_back(
"+Run");
1393 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
1395 if (!parse_option_argument(&i, argc, argv, attached_value, token.c_str(), &
filename, error)) {
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]);
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]);
1415 input->runtime_args.push_back(
"+F");
1416 input->runtime_args.push_back(
filename);
1420 input->runtime_args.push_back(token);
1421 saw_passthrough_token_before_file =
true;
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";
1431 void populate_arg_pointers(
const std::vector<std::string>& values, std::vector<char*>* argv)
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());
1439 void print_lines(FILE* stream,
const char* label,
const std::vector<std::string>& lines)
1441 for (std::size_t i = 0; i < lines.size(); ++i) {
1442 std::fprintf(stream,
"%s%s\n", label, lines[i].c_str());
1446 ParserCompareMode parser_compare_mode()
1448 const char* raw = std::getenv(
"OPENCARP_PARAM_COMPARE");
1450 return ParserCompareMode::Strict;
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;
1458 if (normalized ==
"warn") {
1459 return ParserCompareMode::Warn;
1461 if (normalized ==
"0" || normalized ==
"off" || normalized ==
"false" || normalized ==
"no") {
1462 return ParserCompareMode::Off;
1465 return ParserCompareMode::Strict;
1468 ParserFallbackMode parser_fallback_mode()
1470 const char* raw = std::getenv(
"OPENCARP_PARAM_FALLBACK");
1472 return ParserFallbackMode::Off;
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;
1480 if (normalized ==
"legacy") {
1481 return ParserFallbackMode::Legacy;
1484 return ParserFallbackMode::Off;
1487 paramschema::ExecutionResult execute_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
1488 const bool allow_save)
1490 std::vector<char*> argv;
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()),
1501 bool apply_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
1502 const bool allow_save,
1503 paramschema::ExecutionResult* executed_out)
1505 const paramschema::ExecutionResult executed = execute_parser_runtime_args(
runtime_args, allow_save);
1506 if (executed_out != NULL) {
1507 *executed_out = executed;
1509 print_lines(stderr,
"parameter parser warning: ", executed.warnings);
1510 if (!executed.rendered_output.empty()) {
1511 std::fputs(executed.rendered_output.c_str(), stdout);
1513 if (!executed.errors.empty() || executed.status == paramschema::ExecutionStatus::Fatal) {
1514 print_lines(stderr,
"parameter parser error: ", executed.errors);
1518 if (executed.status == paramschema::ExecutionStatus::Help) {
1525 std::string parent_directory(
const std::string& path)
1527 const std::string::size_type slash = path.rfind(
'/');
1528 if (slash == std::string::npos) {
1534 return path.substr(0, slash);
1537 std::string join_path(
const std::string& left,
const std::string& right)
1539 if (left.empty() || left ==
".") {
1542 if (!left.empty() && left[left.size() - 1] ==
'/') {
1543 return left + right;
1545 return left +
"/" + right;
1548 bool find_executable_on_path(
const std::string& name, std::string* resolved_path)
1550 if (name.empty() || name.find(
'/') != std::string::npos) {
1554 const char* path_env = std::getenv(
"PATH");
1555 if (path_env == NULL || path_env[0] ==
'\0') {
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();
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;
1574 if (end == path_list.size()) {
1583 bool resolve_legacy_snapshot_helper(
const std::string& program_path, std::string* helper_path)
1585 std::vector<std::string> resolved_program_paths;
1586 if (!program_path.empty()) {
1587 resolved_program_paths.push_back(program_path);
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);
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);
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"),
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];
1614 return find_executable_on_path(
"mesher-param-parser-legacy-snapshot", helper_path);
1617 bool create_temp_output_path(
const char* suffix, std::string* path)
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);
1623 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/mesher-parser-compare-XXXXXX");
1626 const int fd = suffix != NULL && suffix[0] !=
'\0' ? mkstemps(temp_path,
static_cast<int>(std::strlen(suffix))) :
1636 bool capture_current_parser_snapshot(paramschema::SnapshotResult* snapshot)
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);
1647 void maybe_force_test_mismatch(paramschema::SnapshotResult* snapshot)
1649 const char* raw = std::getenv(
"OPENCARP_PARAM_TEST_FORCE_MISMATCH");
1654 const std::string normalized = to_lower_ascii(trim_copy(raw));
1655 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
1656 normalized ==
"no") {
1660 if (!snapshot->entries.empty()) {
1661 snapshot->entries[0].value +=
"__forced_parser_compare_mismatch__";
1665 paramschema::SnapshotEntry entry;
1666 entry.path =
"mesh";
1667 entry.value =
"__forced_parser_compare_mismatch__";
1668 snapshot->entries.push_back(entry);
1671 bool run_legacy_snapshot_helper(
const std::string& helper_path,
1673 const std::string& output_path)
1675 std::vector<std::string> child_values;
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(
"--");
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()));
1688 child_argv.push_back(NULL);
1690 const pid_t pid = fork();
1692 std::perror(
"fork");
1697 execv(helper_path.c_str(), child_argv.data());
1698 std::perror(
"execv");
1703 if (waitpid(pid, &status, 0) < 0) {
1704 std::perror(
"waitpid");
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",
1713 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper exited with status %d\n",
1714 WEXITSTATUS(status));
1722 bool legacy_fallback_requested(
const RuntimeCompatOptions& compat)
1724 return compat.fallback_mode == ParserFallbackMode::Legacy ||
1725 parser_fallback_mode() == ParserFallbackMode::Legacy;
1728 void print_legacy_fallback_workaround()
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");
1736 bool restore_legacy_snapshot_state(
const paramschema::SnapshotResult& legacy_snapshot)
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);
1748 bool run_parser_legacy_compare(
const std::vector<std::string>&
runtime_args,
1749 const LegacyCompareInput& legacy_input,
1750 const RuntimeCompatOptions& compat)
1752 const ParserCompareMode mode = parser_compare_mode();
1753 if (mode == ParserCompareMode::Off) {
1756 const bool fallback_to_legacy = legacy_fallback_requested(compat);
1759 std::fprintf(stderr,
"parameter compare error: unable to reconstruct mesher argv\n");
1760 if (fallback_to_legacy) {
1761 print_legacy_fallback_workaround();
1763 return mode != ParserCompareMode::Strict;
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();
1772 return mode != ParserCompareMode::Strict;
1774 maybe_force_test_mismatch(&parser_snapshot);
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();
1782 std::fprintf(stderr,
1783 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1785 return mode != ParserCompareMode::Strict;
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");
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();
1803 std::fprintf(stderr,
1804 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1806 return mode != ParserCompareMode::Strict;
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());
1817 if (!read_error.empty()) {
1818 std::fprintf(stderr,
"parameter compare error: %s\n", read_error.c_str());
1820 std::fprintf(stderr,
"parameter compare error: unable to capture the legacy parameter state\n");
1822 if (fallback_to_legacy) {
1823 print_legacy_fallback_workaround();
1825 std::fprintf(stderr,
1826 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1828 return mode != ParserCompareMode::Strict;
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();
1844 std::fprintf(stderr,
"parameter compare warning: continuing with the legacy parameter state\n");
1848 print_legacy_fallback_workaround();
1849 return mode != ParserCompareMode::Strict;
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());
1870 paramschema::ExecutionResult executed;
1871 if (!apply_parser_runtime_args(normalized_args,
true, &executed)) {
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) {
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";
1889 if (!run_parser_legacy_compare(normalized_args, legacy_input, compat)) {
1893 float tsize[3], x0[3];
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;
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];
1907 x0[i] = -0.5*param_globals::size[i]+param_globals::center[i];
1910 tsize[i] = param_globals::size[i]-2*param_globals::bath[i];
1912 x0[i] = -0.5*tsize[i]+param_globals::center[i];
1918 ctr.
x = param_globals::center[0];
1919 ctr.
y = param_globals::center[1];
1920 ctr.
z = param_globals::center[2];
1922 RegionDef* regdef = param_globals::regdef;
1924 for (
int r = 0; r < param_globals::numRegions; r++ ) {
1926 if( !param_globals::size[2]) p0.
z = 0;
1928 switch(regdef[r].type) {
1930 if( !param_globals::size[2] ) p1.
z = 0;
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 );
1939 regdef[r].rad *=
CM2UM;
1944 if( !param_globals::size[2] )
1948 regdef[r].rad *=
CM2UM;
1949 regdef[r].cyllen *=
CM2UM;
1951 regdef[r].cyllen, regdef[r].bath );
1954 region[r]->
tag(regdef[r].tag);
1956 region[param_globals::numRegions] = NULL;
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 );
1965 grid =
new Grid3D( param_globals::mesh, region );
1970 grid->
build_mesh(x0, tsize, param_globals::size, symbath, param_globals::resolution,
1971 param_globals::perturb, param_globals::anisoBath, param_globals::periodic);
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;
virtual bool inside(Point p)
BlockRegion(Point p0, Point p, int bth)
float z2xi(float z, bool nodeGrid)
float mx_z(bool nodeGrid)
void init(int *_bx, float *_res, Point p0)
float mn_z(bool nodeGrid)
virtual bool inside(Point p)
CylindricalRegion(Point origin, Point dir, float r, float l, int bth)
Element(int N, const char *t)
virtual void output_boundary(char *fn)
Grid1D(char *m, Region **r)
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
Grid2D(char *m, Region **r)
virtual void output_boundary(char *fn)
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)
virtual void output_boundary(char *fn)
Grid3D(char *m, Region **r)
virtual void output_boundary(char *)=0
virtual void build_mesh(float *, float *, float *, bool *, float *, float, bool, int)=0
void set_indx_bounds(bool *sym)
Grid(char *, Region **, int)
void add_element(Element &, region_t)
void unPrMFiberDefs(void)
bool chk_bath(int i, int j, int k)
Hexahedron(int A, int B, int C, int D, int E, int F, int G, int H)
Quadrilateral(int A, int B, int C, int D)
virtual bool inside(Point)=0
virtual bool inside(Point p)
SphericalRegion(Point ctr, float r, int bth)
Tetrahedron(int A, int B, int C, int D)
void chkNegVolume(Point *pts)
void set_axes(float alpha_, float beta_pr_, float gamma_)
Triangle(int A, int B, int C)
void setFiberDefs(char *f_prof, float fEndo, float fEpi, float imbr, char *s_prof, float sEndo, float sEpi)
int linear(float ang_endo, float ang_epi, float z_endo, float z_epi)
int main(int argc, char *argv[])
std::ostream & operator<<(std::ostream &out, Element &e)
Point p_assign_array(float *p)
constexpr T min(T a, T b)
constexpr T max(T a, T b)
V det3(const vec3< V > &a, const vec3< V > &b, const vec3< V > &c)
vec3< V > scal_X(const vec3< V > &a, S k)
V dot(const vec3< V > &p1, const vec3< V > &p2)
vec3< V > normalize(vec3< V > a)
V dist_2(const vec3< V > &p1, const vec3< V > &p2)
V mag2(const vec3< V > &vect)
std::vector< std::string > runtime_args
ParserFallbackMode fallback_mode
std::string unavailable_reason
void assign(S ix, S iy, S iz)