39 #include "mesher_schema.hpp"
40 #include "runtime.hpp"
41 #include "snapshot_file_io.hpp"
48 #define BOX_CENTERS_GRID false
49 #define NODE_GRID true
54 a.
x = p[0]; a.
y=p[1]; a.
z=p[2];
69 void set_axes(
float alpha_,
float beta_pr_,
float gamma_);
70 void set_bath_axes(
bool);
90 Point p(cos(alpha), sin(alpha), sin(gamma));
93 Point sp(0.0, sin(
double(beta_pr)), cos(
double(beta_pr)));
102 s.x = s.y = s.z = 0.0;
104 f.x = aniso_bath?1.0:0.0;
113 int tag() {
return tag_; }
114 void tag(
int t) { tag_ = t; }
124 virtual bool inside(
Point p);
132 Region(ctr, bth), radius2_(r*r) {}
141 return p.
x >= p0_.x && p.
x <= p1_.x && p.
y >= p0_.y && p.
y <= p1_.y &&
142 p.
z >= p0_.z && p.
z <= p1_.z;
149 Region(origin, bth), radius2_(r*r)
150 {axis_=
normalize(dir);len_=(l==0.)?1.e36:l;}
151 virtual bool inside(
Point p );
162 float d =
dot(R, axis_);
164 return d>=0 && d<=len_ &&
mag2(R)-d*d <= radius2_;
170 Element(
int N,
const char *t):n_(N),p_(new int[N]),type_(t){}
184 Point result = ctr[p_[0]];
185 for(
int i=1; i<n_; i++ ) result = result + ctr[p_[i]];
186 return scal_X( result, 1./(
float)n_);
193 p_[0]=A; p_[1]=B; p_[2]=C; p_[3]=D; }
194 void chkNegVolume(
Point *pts);
199 Point P01 = pts[p_[1]] - pts[p_[0]];
200 Point P02 = pts[p_[2]] - pts[p_[0]];
201 Point P03 = pts[p_[3]] - pts[p_[0]];
202 double vol =
det3(P01,P02,P03);
204 std::swap(p_[0],p_[1]);
210 p_[0]=A; p_[1]=B; p_[2]=C; p_[3]=D; p_[4]=E; p_[5]=F; p_[6]=G; p_[7]=H; }
217 p_[0] = A; p_[1] = B; p_[2] = C; p_[3] = D;
225 p_[0] = A; p_[1] = B; p_[2] = C;
237 out << e.
type_ <<
" ";
238 for(
int i=0; i<e.
num()-1; i++ )
239 out << e.
p_[i] <<
" ";
240 out << e.
p_[e.
num()-1];
245 out << p.
x <<
" " << p.
y <<
" " << p.
z;
253 int read(
char *fname);
254 float lookup(
float xi);
255 int linear(
float ang_endo,
float ang_epi,
float z_endo,
float z_epi );
270 N = (z_epi-z_endo)/dz;
272 xi = (
float *)malloc(
sizeof(
float)*(N+1));
273 ang = (
float *)malloc(
sizeof(
float)*(N+1));
275 if(xi==NULL || ang==NULL) {
276 std::cerr <<
"Memory allocation failed." << std::endl;
283 float K = (ang_epi-ang_endo)/(z_epi-z_endo);
284 for(
int i=0;i<=N;i++) {
285 float z = z_endo+i*dz;
286 xi[i] = (z-z_endo)/(z_epi-z_endo)-0.5;
287 ang[i] = ang_endo + K*(z-z_endo);
296 if( !N )
return ang[0];
299 while(xi[i]<xi_ && i<N) i++;
304 return ang[i-1]+(ang[i]-ang[i-1])/(xi[i]-xi[i-1])*(xi_-xi[i-1]);
310 FILE *profile = fopen(fname,
"rt");
312 fprintf( stderr,
"Can't open transmural profile data file %s.\n", fname);
316 int err = fscanf(profile,
"%d",&N);
317 xi = (
float *)malloc(
sizeof(
float)*N);
318 ang = (
float *)malloc(
sizeof(
float)*N);
319 for(
int i=0; i<N; i++) {
320 xi[i] = (float)i/(
float)(N-1)-0.5;
321 err = fscanf(profile,
"%f",ang+i);
330 void update(
Point p );
346 if(p.
x<x_mn) x_mn = p.
x;
347 if(p.
y<y_mn) y_mn = p.
y;
348 if(p.
z<z_mn) z_mn = p.
z;
349 if(p.
x>x_mx) x_mx = p.
x;
350 if(p.
y>y_mx) y_mx = p.
y;
351 if(p.
z>z_mx) z_mx = p.
z;
358 void init(
int *_bx,
float *_res,
Point p0);
360 float z2xi(
float z,
bool nodeGrid);
361 float mn_z(
bool nodeGrid){
return nodeGrid?nodes.z_mn:bctrs.z_mn; };
362 float mx_z(
bool nodeGrid){
return nodeGrid?nodes.z_mx:bctrs.z_mx; };
375 float zd = nodeGrid?nodes.zd:bctrs.zd;
376 float z_mn = nodeGrid?nodes.z_mn:bctrs.z_mn;
381 return (z-z_mn)/zd-0.5;
389 for(
int i=0;i<3;i++) {
393 bx_inds[i][1] = bx[i]-1;
401 pc0.
x = p0.
x + res[0]/2;
402 pc0.
y = p0.
y + res[1]/2;
403 pc0.
z = p0.
z + res[2]/2;
408 p1.
x = p0.
x+bx[0]*res[0];
409 p1.
y = p0.
y+bx[1]*res[1];
410 p1.
z = p0.
z+bx[2]*res[2];
415 pc1.
x = p1.
x - res[0]/2;
416 pc1.
y = p1.
y - res[1]/2;
417 pc1.
z = p1.
z - res[2]/2;
427 nodes.xd = nodes.x_mx - nodes.x_mn;
428 nodes.yd = nodes.y_mx - nodes.y_mn;
429 nodes.zd = nodes.z_mx - nodes.z_mn;
432 bctrs.xd = bctrs.x_mx - bctrs.x_mn;
433 bctrs.yd = bctrs.y_mx - bctrs.y_mn;
434 bctrs.zd = bctrs.z_mx - bctrs.z_mn;
439 void setFiberDefs(
char *f_prof,
float fEndo,
float fEpi,
float imbr,
440 char *s_prof,
float sEndo,
float sEpi);
441 bool withSheets(
void);
461 char *s_prof,
float sEndo,
float sEpi)
463 f_Prof = strdup(f_prof);
467 s_Prof = strdup(s_prof);
475 if((f_imbr!=0.0) || (s_Endo!=0.0) || (s_Epi!=0.0) || (strcmp(s_Prof,
"")))
487 void unPrMFiberDefs(
void);
488 virtual void build_mesh(
float*,
float*,
float*,
bool *,
float*,
float,
bool,
int)=0;
491 void set_indx_bounds(
bool *sym);
492 bool chk_bath(
int i,
int j,
int k );
499 std::ofstream pt_os,
elem_os, lon_os, elemc_os, vec_os;
510 virtual void build_mesh(
float*,
float*,
float*,
bool *,
float*,
float,
bool,
int);
514 void add_tri(
int,
int,
int );
519 virtual void build_mesh(
float*,
float*,
float*,
bool *,
float*,
float,
bool,
int);
523 void add_tet(
int,
int,
int,
int );
528 virtual void build_mesh(
float*,
float*,
float*,
bool *,
float*,
float,
bool,
int);
532 void add_line(
int,
int,
int,
int );
538 return pt_os.good() && lon_os.good() && elem_os.good();
544 std::string fname(msh);
547 lon_os.open(fname.c_str());
551 pt_os.open(fname.c_str());
563 vec_os.open(fname.c_str());
576 for(
int i=0;i<3;i++) {
598 bool bth[] = {
false,
false,
false };
599 int idx[] = { i, j, k };
601 for(
int cnt=0; cnt<
dim; cnt++ )
613 param_globals::fibers.rotEndo,
614 param_globals::fibers.rotEpi,
615 param_globals::fibers.imbrication,
616 param_globals::fibers.tm_sheet_profile,
617 param_globals::fibers.sheetEndo,
618 param_globals::fibers.sheetEpi);
634 while(
region[++r] != NULL ) {
635 if(
region[r]->inside( c ) ) {
636 if(
region[r]->isbath() ) {
642 if(param_globals::first_reg)
658 elem_os <<
" " << regid << std::endl;
683 float pert,
bool aniso_bath,
int periodic_bc)
691 for(
int i=0; i<3; i++ ) {
692 bnx[i] = (int)(x[i]/res[i]);
693 tnx[i] = (int)(tissue[i]/res[i]);
698 pt0.
x = -tnx[0]*res[0]/2 + x0[0];
702 pb0.
x = sym[0]?pt0.
x-(bnx[0]-tnx[0])*res[0]/2:pt0.
x+x0[0];
718 std::cout <<
"Number of points: " <<
npt << std::endl;
722 for(
int i=0; i<=bnx[0]; i++ ) {
726 pt[
npt].
x += ((double)random()/(double)RAND_MAX)*pert*res[0];
732 int num_in_layer = (bnx[0]+1);
733 elem_os << bnx[0] << std::endl;
734 std::cout <<
"Number of linear elements: " << bnx[0] << std::endl;
736 for(
int i=0; i<bnx[0]; i++ ) {
767 float pert,
bool aniso_bath,
int periodic_bc)
775 for(
int i=0; i<3; i++ ) {
776 bnx[i] = (int)(x[i]/res[i]);
777 tnx[i] = (int)(tissue[i]/res[i]);
782 pt0.
x = -tnx[0]*res[0]/2 + x0[0];
783 pt0.
y = -tnx[1]*res[1]/2 + x0[1];
786 pb0.
x = sym[0]?pt0.
x-(bnx[0]-tnx[0])*res[0]/2:pt0.
x+ x0[0];
787 pb0.
y = sym[0]?pt0.
y-(bnx[0]-tnx[0])*res[0]/2:pt0.
y+ x0[1];
798 npt = (bnx[0]+1)*(bnx[1]+1);
799 std::cout <<
"Number of points: " <<
npt << std::endl;
803 for(
int j=0; j<=bnx[1]; j++ )
804 for(
int i=0; i<=bnx[0]; i++ ) {
805 pt[
npt].
assign<
double>(x0[0]+i*res[0], x0[1]+j*res[1], 0);
807 if( j && j!=bnx[1] && i && i!=bnx[0] ) {
808 pt[
npt].
x += ((double)random()/(double)RAND_MAX)*pert*res[0];
809 pt[
npt].
y += ((double)random()/(double)RAND_MAX)*pert*res[1];
817 if( (periodic_bc&1) == 1 )
818 nper_cnnx += bnx[1]+1;
819 if( (periodic_bc&2) == 2 )
820 nper_cnnx += bnx[0]+1;
822 int num_in_layer = (bnx[0]+1)*(bnx[1]+1);
824 if(param_globals::tri2D) {
825 elem_os << 2*bnx[0]*bnx[1]+nper_cnnx << std::endl;
826 elemc_os << 2*bnx[0]*bnx[1]+nper_cnnx << std::endl;
827 std::cout <<
"Number of triangles: " << 2*bnx[0]*bnx[1] << std::endl;
829 elem_os << bnx[0]*bnx[1]+nper_cnnx << std::endl;
830 elemc_os << bnx[0]*bnx[1]+nper_cnnx << std::endl;
831 std::cout <<
"Number of quadrilaterals: " << bnx[0]*bnx[1] << std::endl;
834 std::cout <<
"Number of periodic connections: " << nper_cnnx << std::endl;
842 std::cout <<
"Using orthotropic fiber setup." << std::endl;
844 std::cout <<
"Using transversely isotropic fiber setup." << std::endl;
846 for(
int j=0; j<bnx[1]; j++ ) {
848 for(
int i=0; i<bnx[0]; i++ ) {
849 p1 = j*(bnx[0]+1) + i;
851 p3 = (j+1)*(bnx[0]+1) + i;
858 if( param_globals::tri2D ) {
860 Triangle t1(p1, p2, p3), t2(p2, p4, p3);
864 Triangle t1(p1, p2, p4), t2(p1, p4, p3);
876 if( (periodic_bc&1) == 1 ) {
877 for(
int i=0; i<=bnx[1]; i++ ) {
878 Line l( i*(bnx[0]+1), (i+1)*(bnx[0]+1)-1 );
879 elem_os << l <<
" " << param_globals::periodic_tag<<std::endl;
880 lon_os <<
"1 0 0" << std::endl;
883 if( (periodic_bc&2) == 2 ) {
884 for(
int i=0; i<=bnx[0]; i++ ) {
885 Line l( i, (bnx[0]+1)*(bnx[1])+i );
886 elem_os << l <<
" " << param_globals::periodic_tag+1 << std::endl;
887 lon_os <<
"0 1 0" << std::endl;
909 float pert,
bool aniso_bath,
int periodic_bc)
912 int p1, p2, p3, p4, p5, p6, p7,p8;
917 for(
int i=0; i<3; i++ ) {
918 bnx[i] = (int)(x[i]/res[i]);
919 tnx[i] = (int)(tissue[i]/res[i]);
924 pt0 = {x0[0],x0[1],x0[2]};
926 pb0.
x = sym[0]?pt0.
x-(bnx[0]-tnx[0])*res[0]/2:pt0.
x + x0[0];
927 pb0.
y = sym[1]?pt0.
y-(bnx[1]-tnx[1])*res[1]/2:pt0.
y + x0[1];
928 pb0.
z = sym[2]?pt0.
z-(bnx[2]-tnx[2])*res[2]/2:pt0.
z + x0[2];
940 std::cout <<
"Reading transmural fiber rotation profile from " <<
f_def.
f_name() << std::endl;
948 std::cout <<
"Reading transmural sheet profile from " <<
f_def.
s_name() << std::endl;
956 npt = (bnx[0]+1)*(bnx[1]+1)*(bnx[2]+1);
957 std::cout <<
"Number of points: " <<
npt << std::endl;
961 for(
int k=0; k<=bnx[2]; k++ )
962 for(
int j=0; j<=bnx[1]; j++ )
963 for(
int i=0; i<=bnx[0]; i++ ) {
964 pt[
npt] = {x0[0]+i*res[0], x0[1]+j*res[1], x0[2]+k*res[2]};
965 if( k && k!=bnx[2] && j && j!=bnx[1] && i && i!=bnx[0] ) {
966 pt[
npt].
x += ((double)random()/(double)RAND_MAX)*pert*res[0];
967 pt[
npt].
y += ((double)random()/(double)RAND_MAX)*pert*res[1];
968 pt[
npt].
z += ((double)random()/(double)RAND_MAX)*pert*res[2];
974 int num_in_layer = (bnx[0]+1)*(bnx[1]+1);
975 if(!param_globals::Elem3D) {
976 elem_os << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
977 elemc_os << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
978 std::cout <<
"Number of Tetrahedra: " << 5*bnx[0]*bnx[1]*bnx[2] << std::endl;
981 elem_os << bnx[0]*bnx[1]*bnx[2] << std::endl;
982 elemc_os << bnx[0]*bnx[1]*bnx[2] << std::endl;
983 std::cout <<
"Number of Hexahedra: " << bnx[0]*bnx[1]*bnx[2] << std::endl;
989 std::cout <<
"Using orthotropic fiber setup." << std::endl;
991 std::cout <<
"Using transversely isotropic fiber setup." << std::endl;
994 for(
int k=0; k<bnx[2]; k++ ) {
996 for(
int j=0; j<bnx[1]; j++ ) {
999 if(j && !(bnx[0]%2) )
1002 for(
int i=0; i<bnx[0]; i++ ) {
1003 p1 = k*num_in_layer + j*(bnx[0]+1) + i;
1005 p3 = k*num_in_layer + (j+1)*(bnx[0]+1) + i;
1007 p5 = (k+1)*num_in_layer + j*(bnx[0]+1) + i;
1009 p7 = (k+1)*num_in_layer + (j+1)*(bnx[0]+1) + i;
1016 if(!param_globals::Elem3D) {
1049 Hexahedron h1(p5, p7, p8, p6, p1, p2, p4, p3);
1064 enum class ParserCompareMode {
1070 enum class ParserFallbackMode {
1075 struct RuntimeCompatOptions {
1079 struct LegacyCompareInput {
1085 std::string trim_copy(
const std::string& value)
1087 std::string::size_type first = 0;
1088 while (first < value.size() && std::isspace(
static_cast<unsigned char>(value[first]))) {
1092 std::string::size_type last = value.size();
1093 while (last > first && std::isspace(
static_cast<unsigned char>(value[last - 1]))) {
1097 return value.substr(first, last - first);
1100 std::string to_lower_ascii(std::string value)
1102 for (std::string::size_type i = 0; i < value.size(); ++i) {
1103 value[i] =
static_cast<char>(std::tolower(
static_cast<unsigned char>(value[i])));
1108 bool parse_option_argument(
int* index,
1111 const std::string& attached_value,
1112 const char* option_name,
1116 if (!attached_value.empty()) {
1117 *value = attached_value;
1120 if (*index + 1 >= argc) {
1121 *error =
"Missing argument after " + std::string(option_name);
1124 *value = argv[++(*index)];
1128 bool filename_has_suffix(
const std::string&
filename,
const char* suffix)
1130 const std::string normalized_filename = to_lower_ascii(trim_copy(
filename));
1131 const std::string normalized_suffix = to_lower_ascii(std::string(suffix == NULL ?
"" : suffix));
1132 return normalized_filename.size() >= normalized_suffix.size() &&
1133 normalized_filename.compare(normalized_filename.size() - normalized_suffix.size(),
1134 normalized_suffix.size(),
1135 normalized_suffix) == 0;
1138 bool match_long_option(
const std::string& token,
const char* option, std::string* attached_value)
1140 *attached_value = std::string();
1141 if (token == option) {
1145 const std::string prefix = std::string(option) +
"=";
1146 if (token.size() > prefix.size() && token.compare(0, prefix.size(), prefix) == 0) {
1147 *attached_value = token.substr(prefix.size());
1154 bool is_help_topic_candidate(
const char* token)
1156 return token != NULL && token[0] !=
'\0' && token[0] !=
'-' && token[0] !=
'+';
1159 bool is_long_option_argument_error(
const std::string& token,
1161 const std::string& attached_value,
1164 if (attached_value.empty()) {
1168 *error =
"Unexpected argument for " + std::string(option) +
" in '" + token +
"'";
1172 bool normalize_runtime_args(
int argc,
1174 std::vector<std::string>* normalized,
1175 RuntimeCompatOptions* compat,
1178 normalized->clear();
1179 compat->fallback_mode = ParserFallbackMode::Off;
1181 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
1182 *error =
"Missing program name";
1186 normalized->push_back(argv[0]);
1188 for (
int i = 1; i < argc; ++i) {
1189 const std::string token = argv[i];
1194 std::string attached_value;
1196 if (token ==
"+Help" || match_long_option(token,
"--help", &attached_value)) {
1197 std::string topic =
"PrM";
1198 if (!attached_value.empty()) {
1199 topic = attached_value;
1200 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
1203 normalized->push_back(
"+Help");
1204 normalized->push_back(topic);
1208 if (token ==
"+Doc" || match_long_option(token,
"--doc", &attached_value)) {
1209 std::string topic =
"ALL";
1210 if (!attached_value.empty()) {
1211 topic = attached_value;
1212 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
1215 normalized->push_back(
"+Doc");
1216 normalized->push_back(topic);
1220 if (token ==
"+Default" || match_long_option(token,
"--default", &attached_value)) {
1221 if (token !=
"+Default" && is_long_option_argument_error(token,
"--default", attached_value, error)) {
1224 normalized->push_back(
"+Default");
1228 if (token ==
"+Run" || match_long_option(token,
"--run", &attached_value)) {
1229 if (token !=
"+Run" && is_long_option_argument_error(token,
"--run", attached_value, error)) {
1232 normalized->push_back(
"+Run");
1236 if (token ==
"+I" || match_long_option(token,
"--interactive", &attached_value)) {
1237 if (!attached_value.empty()) {
1238 *error =
"Unexpected argument for --interactive in '" + token +
"'";
1240 *error =
"Unsupported option " + token +
" (interactive mode is not available)";
1245 if (match_long_option(token,
"--param-fallback", &attached_value)) {
1246 std::string mode = attached_value;
1248 if (i + 1 >= argc) {
1249 *error =
"Missing argument after --param-fallback";
1255 if (to_lower_ascii(trim_copy(mode)) !=
"legacy") {
1256 *error =
"Unsupported value '" + mode +
"' for --param-fallback (expected legacy)";
1260 compat->fallback_mode = ParserFallbackMode::Legacy;
1264 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
1265 std::string
filename = attached_value;
1267 if (i + 1 >= argc) {
1268 *error =
"Missing filename after " + token;
1273 normalized->push_back(
"+F");
1278 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
1279 std::string
filename = attached_value;
1281 if (i + 1 >= argc) {
1282 *error =
"Missing argument after " + token;
1287 normalized->push_back(
"+Save");
1292 normalized->push_back(token);
1298 bool build_legacy_compare_input(
int argc,
char** argv, LegacyCompareInput* input, std::string* error)
1300 input->available =
false;
1301 input->runtime_args.clear();
1302 input->unavailable_reason.clear();
1304 if (argc <= 0 || argv == NULL || argv[0] == NULL) {
1305 *error =
"Missing program name";
1309 input->runtime_args.push_back(argv[0]);
1310 bool saw_passthrough_token_before_file =
false;
1312 for (
int i = 1; i < argc; ++i) {
1313 const std::string token = argv[i];
1318 std::string attached_value;
1320 if (match_long_option(token,
"--param-fallback", &attached_value)) {
1321 std::string ignored;
1322 if (!parse_option_argument(&i, argc, argv, attached_value,
"--param-fallback", &ignored, error)) {
1328 if (token ==
"+Save" || match_long_option(token,
"--save", &attached_value)) {
1329 std::string ignored;
1330 if (!parse_option_argument(&i, argc, argv, attached_value, token.c_str(), &ignored, error)) {
1336 if (token ==
"+Help" || match_long_option(token,
"--help", &attached_value)) {
1337 std::string topic =
"PrM";
1338 if (!attached_value.empty()) {
1339 topic = attached_value;
1340 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
1343 input->runtime_args.push_back(
"+Help");
1344 input->runtime_args.push_back(topic);
1345 input->available =
true;
1349 if (token ==
"+Doc" || match_long_option(token,
"--doc", &attached_value)) {
1350 std::string topic =
"ALL";
1351 if (!attached_value.empty()) {
1352 topic = attached_value;
1353 }
else if (i + 1 < argc && is_help_topic_candidate(argv[i + 1])) {
1356 input->runtime_args.push_back(
"+Doc");
1357 input->runtime_args.push_back(topic);
1358 input->available =
true;
1362 if (token ==
"+Default" || match_long_option(token,
"--default", &attached_value)) {
1363 if (token !=
"+Default" && is_long_option_argument_error(token,
"--default", attached_value, error)) {
1366 input->runtime_args.push_back(
"+Default");
1370 if (token ==
"+Run" || match_long_option(token,
"--run", &attached_value)) {
1371 if (token !=
"+Run" && is_long_option_argument_error(token,
"--run", attached_value, error)) {
1374 input->runtime_args.push_back(
"+Run");
1378 if (token ==
"+F" || match_long_option(token,
"--file", &attached_value)) {
1380 if (!parse_option_argument(&i, argc, argv, attached_value, token.c_str(), &
filename, error)) {
1384 if (saw_passthrough_token_before_file) {
1385 input->unavailable_reason =
1386 "legacy compare is unavailable when direct parameter arguments precede a .par input file";
1387 input->runtime_args.clear();
1388 input->runtime_args.push_back(argv[0]);
1392 if (!filename_has_suffix(
filename,
".par")) {
1393 input->unavailable_reason =
1394 "legacy compare is unavailable for non-.par input file '" +
filename +
"'";
1395 input->runtime_args.clear();
1396 input->runtime_args.push_back(argv[0]);
1400 input->runtime_args.push_back(
"+F");
1401 input->runtime_args.push_back(
filename);
1405 input->runtime_args.push_back(token);
1406 saw_passthrough_token_before_file =
true;
1409 input->available = input->runtime_args.size() > 1;
1410 if (!input->available && input->unavailable_reason.empty()) {
1411 input->unavailable_reason =
"unable to reconstruct a legacy-compatible parameter input";
1416 void populate_arg_pointers(
const std::vector<std::string>& values, std::vector<char*>* argv)
1418 argv->assign(values.size(), NULL);
1419 for (std::size_t i = 0; i < values.size(); ++i) {
1420 (*argv)[i] =
const_cast<char*
>(values[i].c_str());
1424 void print_lines(FILE* stream,
const char* label,
const std::vector<std::string>& lines)
1426 for (std::size_t i = 0; i < lines.size(); ++i) {
1427 std::fprintf(stream,
"%s%s\n", label, lines[i].c_str());
1431 ParserCompareMode parser_compare_mode()
1433 const char* raw = std::getenv(
"OPENCARP_PARAM_COMPARE");
1435 return ParserCompareMode::Strict;
1438 const std::string normalized = to_lower_ascii(trim_copy(raw));
1439 if (normalized.empty() || normalized ==
"1" || normalized ==
"on" || normalized ==
"true" ||
1440 normalized ==
"yes" || normalized ==
"strict" || normalized ==
"fail" || normalized ==
"error") {
1441 return ParserCompareMode::Strict;
1443 if (normalized ==
"warn") {
1444 return ParserCompareMode::Warn;
1446 if (normalized ==
"0" || normalized ==
"off" || normalized ==
"false" || normalized ==
"no") {
1447 return ParserCompareMode::Off;
1450 return ParserCompareMode::Strict;
1453 ParserFallbackMode parser_fallback_mode()
1455 const char* raw = std::getenv(
"OPENCARP_PARAM_FALLBACK");
1457 return ParserFallbackMode::Off;
1460 const std::string normalized = to_lower_ascii(trim_copy(raw));
1461 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
1462 normalized ==
"no") {
1463 return ParserFallbackMode::Off;
1465 if (normalized ==
"legacy") {
1466 return ParserFallbackMode::Legacy;
1469 return ParserFallbackMode::Off;
1472 paramschema::ExecutionResult execute_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
1473 const bool allow_save)
1475 std::vector<char*> argv;
1478 paramschema::ExecutionOptions options;
1479 options.allow_save = allow_save;
1480 return paramschema::execute_legacy_cli(paramschema::mesher_schema(),
1481 static_cast<int>(argv.size()),
1486 bool apply_parser_runtime_args(
const std::vector<std::string>&
runtime_args,
1487 const bool allow_save,
1488 paramschema::ExecutionResult* executed_out)
1490 const paramschema::ExecutionResult executed = execute_parser_runtime_args(
runtime_args, allow_save);
1491 if (executed_out != NULL) {
1492 *executed_out = executed;
1494 print_lines(stderr,
"parameter parser warning: ", executed.warnings);
1495 if (!executed.rendered_output.empty()) {
1496 std::fputs(executed.rendered_output.c_str(), stdout);
1498 if (!executed.errors.empty() || executed.status == paramschema::ExecutionStatus::Fatal) {
1499 print_lines(stderr,
"parameter parser error: ", executed.errors);
1503 if (executed.status == paramschema::ExecutionStatus::Help) {
1510 std::string parent_directory(
const std::string& path)
1512 const std::string::size_type slash = path.rfind(
'/');
1513 if (slash == std::string::npos) {
1519 return path.substr(0, slash);
1522 std::string join_path(
const std::string& left,
const std::string& right)
1524 if (left.empty() || left ==
".") {
1527 if (!left.empty() && left[left.size() - 1] ==
'/') {
1528 return left + right;
1530 return left +
"/" + right;
1533 bool find_executable_on_path(
const std::string& name, std::string* resolved_path)
1535 if (name.empty() || name.find(
'/') != std::string::npos) {
1539 const char* path_env = std::getenv(
"PATH");
1540 if (path_env == NULL || path_env[0] ==
'\0') {
1544 const std::string path_list = path_env;
1545 std::string::size_type start = 0;
1546 while (start <= path_list.size()) {
1547 std::string::size_type end = path_list.find(
':', start);
1548 if (end == std::string::npos) {
1549 end = path_list.size();
1552 const std::string directory = path_list.substr(start, end - start);
1553 const std::string candidate = join_path(directory.empty() ?
"." : directory, name);
1554 if (access(candidate.c_str(), X_OK) == 0) {
1555 *resolved_path = candidate;
1559 if (end == path_list.size()) {
1568 bool resolve_legacy_snapshot_helper(
const std::string& program_path, std::string* helper_path)
1570 std::vector<std::string> resolved_program_paths;
1571 if (!program_path.empty()) {
1572 resolved_program_paths.push_back(program_path);
1575 if (program_path.find(
'/') == std::string::npos) {
1576 std::string resolved_program_path;
1577 if (find_executable_on_path(program_path, &resolved_program_path)) {
1578 resolved_program_paths.push_back(resolved_program_path);
1582 for (std::size_t i = 0; i < resolved_program_paths.size(); ++i) {
1583 const std::string program_dir = parent_directory(resolved_program_paths[i]);
1584 const std::string parent_dir = parent_directory(program_dir);
1586 const std::vector<std::string> candidates = {
1587 join_path(program_dir,
"mesher-param-parser-legacy-snapshot"),
1588 join_path(join_path(parent_dir,
"tools/mesher"),
"mesher-param-parser-legacy-snapshot"),
1591 for (std::size_t j = 0; j < candidates.size(); ++j) {
1592 if (access(candidates[j].c_str(), X_OK) == 0) {
1593 *helper_path = candidates[j];
1599 return find_executable_on_path(
"mesher-param-parser-legacy-snapshot", helper_path);
1602 bool create_temp_output_path(
const char* suffix, std::string* path)
1604 char temp_path[128];
1605 if (suffix != NULL && suffix[0] !=
'\0') {
1606 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/mesher-parser-compare-XXXXXX%s", suffix);
1608 std::snprintf(temp_path,
sizeof temp_path,
"/tmp/mesher-parser-compare-XXXXXX");
1611 const int fd = suffix != NULL && suffix[0] !=
'\0' ? mkstemps(temp_path,
static_cast<int>(std::strlen(suffix))) :
1621 bool capture_current_parser_snapshot(paramschema::SnapshotResult* snapshot)
1623 *snapshot = paramschema::snapshot_schema_state(paramschema::mesher_schema());
1624 print_lines(stderr,
"parameter compare warning: ", snapshot->warnings);
1625 if (!snapshot->errors.empty()) {
1626 print_lines(stderr,
"parameter compare error: ", snapshot->errors);
1632 void maybe_force_test_mismatch(paramschema::SnapshotResult* snapshot)
1634 const char* raw = std::getenv(
"OPENCARP_PARAM_TEST_FORCE_MISMATCH");
1639 const std::string normalized = to_lower_ascii(trim_copy(raw));
1640 if (normalized.empty() || normalized ==
"0" || normalized ==
"off" || normalized ==
"false" ||
1641 normalized ==
"no") {
1645 if (!snapshot->entries.empty()) {
1646 snapshot->entries[0].value +=
"__forced_parser_compare_mismatch__";
1650 paramschema::SnapshotEntry entry;
1651 entry.path =
"mesh";
1652 entry.value =
"__forced_parser_compare_mismatch__";
1653 snapshot->entries.push_back(entry);
1656 bool run_legacy_snapshot_helper(
const std::string& helper_path,
1658 const std::string& output_path)
1660 std::vector<std::string> child_values;
1662 child_values.push_back(helper_path);
1663 child_values.push_back(
"--snapshot-out");
1664 child_values.push_back(output_path);
1665 child_values.push_back(
"--");
1668 std::vector<char*> child_argv;
1669 child_argv.reserve(child_values.size() + 1);
1670 for (std::size_t i = 0; i < child_values.size(); ++i) {
1671 child_argv.push_back(
const_cast<char*
>(child_values[i].c_str()));
1673 child_argv.push_back(NULL);
1675 const pid_t pid = fork();
1677 std::perror(
"fork");
1682 execv(helper_path.c_str(), child_argv.data());
1683 std::perror(
"execv");
1688 if (waitpid(pid, &status, 0) < 0) {
1689 std::perror(
"waitpid");
1693 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
1694 if (WIFSIGNALED(status)) {
1695 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper terminated with signal %d\n",
1698 std::fprintf(stderr,
"parameter compare error: legacy snapshot helper exited with status %d\n",
1699 WEXITSTATUS(status));
1707 bool legacy_fallback_requested(
const RuntimeCompatOptions& compat)
1709 return compat.fallback_mode == ParserFallbackMode::Legacy ||
1710 parser_fallback_mode() == ParserFallbackMode::Legacy;
1713 void print_legacy_fallback_workaround()
1715 std::fprintf(stderr,
1716 "parameter compare error: rerun with OPENCARP_PARAM_FALLBACK=legacy to continue with the legacy parameter state\n");
1717 std::fprintf(stderr,
1718 "parameter compare error: or add --param-fallback=legacy to the mesher command line\n");
1721 bool restore_legacy_snapshot_state(
const paramschema::SnapshotResult& legacy_snapshot)
1723 const paramschema::SnapshotRestoreResult restored =
1724 paramschema::restore_snapshot_state(paramschema::mesher_schema(), legacy_snapshot);
1725 print_lines(stderr,
"parameter compare warning: ", restored.warnings);
1726 if (!restored.errors.empty()) {
1727 print_lines(stderr,
"parameter compare error: ", restored.errors);
1733 bool run_parser_legacy_compare(
const std::vector<std::string>&
runtime_args,
1734 const LegacyCompareInput& legacy_input,
1735 const RuntimeCompatOptions& compat)
1737 const ParserCompareMode mode = parser_compare_mode();
1738 if (mode == ParserCompareMode::Off) {
1741 const bool fallback_to_legacy = legacy_fallback_requested(compat);
1744 std::fprintf(stderr,
"parameter compare error: unable to reconstruct mesher argv\n");
1745 if (fallback_to_legacy) {
1746 print_legacy_fallback_workaround();
1748 return mode != ParserCompareMode::Strict;
1751 paramschema::SnapshotResult parser_snapshot;
1752 if (!capture_current_parser_snapshot(&parser_snapshot)) {
1753 std::fprintf(stderr,
"parameter compare error: unable to snapshot the parser runtime state\n");
1754 if (fallback_to_legacy) {
1755 print_legacy_fallback_workaround();
1757 return mode != ParserCompareMode::Strict;
1759 maybe_force_test_mismatch(&parser_snapshot);
1761 std::string helper_path;
1762 if (!resolve_legacy_snapshot_helper(
runtime_args[0], &helper_path)) {
1763 std::fprintf(stderr,
"parameter compare error: unable to locate mesher-param-parser-legacy-snapshot\n");
1764 if (fallback_to_legacy) {
1765 print_legacy_fallback_workaround();
1767 std::fprintf(stderr,
1768 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1770 return mode != ParserCompareMode::Strict;
1773 if (!legacy_input.available) {
1774 std::fprintf(stderr,
"parameter compare warning: %s\n", legacy_input.unavailable_reason.c_str());
1775 if (fallback_to_legacy) {
1776 std::fprintf(stderr,
1777 "parameter compare warning: legacy fallback is unavailable because no legacy baseline exists for this input\n");
1782 std::string snapshot_path;
1783 if (!create_temp_output_path(
"", &snapshot_path)) {
1784 std::fprintf(stderr,
"parameter compare error: unable to create temporary snapshot file\n");
1785 if (fallback_to_legacy) {
1786 print_legacy_fallback_workaround();
1788 std::fprintf(stderr,
1789 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1791 return mode != ParserCompareMode::Strict;
1794 const bool helper_ok = run_legacy_snapshot_helper(helper_path, legacy_input.runtime_args, snapshot_path);
1795 paramschema::SnapshotResult legacy_snapshot;
1796 std::string read_error;
1797 const bool loaded = helper_ok &&
1798 paramschema::snapshotio::read_snapshot_file(snapshot_path, &legacy_snapshot, &read_error);
1799 unlink(snapshot_path.c_str());
1802 if (!read_error.empty()) {
1803 std::fprintf(stderr,
"parameter compare error: %s\n", read_error.c_str());
1805 std::fprintf(stderr,
"parameter compare error: unable to capture the legacy parameter state\n");
1807 if (fallback_to_legacy) {
1808 print_legacy_fallback_workaround();
1810 std::fprintf(stderr,
1811 "parameter compare error: rerun with OPENCARP_PARAM_COMPARE=0 to bypass this temporary compatibility gate\n");
1813 return mode != ParserCompareMode::Strict;
1816 const paramschema::SnapshotComparisonResult comparison =
1817 paramschema::compare_snapshot_results(paramschema::mesher_schema(), parser_snapshot, legacy_snapshot);
1818 if (!comparison.errors.empty() || !comparison.mismatches.empty()) {
1819 print_lines(stderr,
"", paramschema::format_snapshot_comparison_report(comparison,
"parser compare"));
1820 std::fprintf(stderr,
1821 "parameter compare error: the parser runtime and legacy param() produced different parameter states\n");
1822 std::fprintf(stderr,
1823 "parameter compare error: please open an issue and include the triggering command line and parameter files\n");
1824 if (fallback_to_legacy) {
1825 if (!restore_legacy_snapshot_state(legacy_snapshot)) {
1826 print_legacy_fallback_workaround();
1829 std::fprintf(stderr,
"parameter compare warning: continuing with the legacy parameter state\n");
1833 print_legacy_fallback_workaround();
1834 return mode != ParserCompareMode::Strict;
1845 LegacyCompareInput legacy_input;
1846 std::vector<std::string> normalized_args;
1847 RuntimeCompatOptions compat;
1848 std::string normalize_error;
1849 if (!build_legacy_compare_input(argc, argv, &legacy_input, &normalize_error) ||
1850 !normalize_runtime_args(argc, argv, &normalized_args, &compat, &normalize_error)) {
1851 std::fprintf(stderr,
"\n*** %s\n\n", normalize_error.c_str());
1855 paramschema::ExecutionResult executed;
1856 if (!apply_parser_runtime_args(normalized_args,
true, &executed)) {
1860 if (legacy_input.available) {
1861 for (std::size_t i = 0; i < executed.validation.assignments.size(); ++i) {
1862 if (!executed.validation.assignments[i].synthesized) {
1865 legacy_input.available =
false;
1866 legacy_input.runtime_args.clear();
1867 legacy_input.runtime_args.push_back(normalized_args[0]);
1868 legacy_input.unavailable_reason =
1869 "legacy compare is unavailable because the parser inferred optional controller counts from the original input";
1874 if (!run_parser_legacy_compare(normalized_args, legacy_input, compat)) {
1878 float tsize[3], x0[3];
1882 for(
int i=0; i<3; i++ ) {
1883 param_globals::size[i] *=
CM2UM;
1884 param_globals::bath[i] *=
CM2UM;
1885 param_globals::center[i] *=
CM2UM;
1888 if(param_globals::size[i]==0.) param_globals::bath[i] = 0.0;
1889 if(param_globals::bath[i]>=0) {
1890 tsize[i] = param_globals::size[i]+param_globals::bath[i];
1892 x0[i] = -0.5*param_globals::size[i]+param_globals::center[i];
1895 tsize[i] = param_globals::size[i]-2*param_globals::bath[i];
1897 x0[i] = -0.5*tsize[i]+param_globals::center[i];
1903 ctr.
x = param_globals::center[0];
1904 ctr.
y = param_globals::center[1];
1905 ctr.
z = param_globals::center[2];
1907 RegionDef* regdef = param_globals::regdef;
1909 for (
int r = 0; r < param_globals::numRegions; r++ ) {
1911 if( !param_globals::size[2]) p0.
z = 0;
1913 switch(regdef[r].type) {
1915 if( !param_globals::size[2] ) p1.
z = 0;
1918 if( p0.
x>p1.x ) std::swap( p0.
x, p1.x );
1919 if( p0.
y>p1.y ) std::swap( p0.
y, p1.y );
1920 if( p0.
z>p1.z ) std::swap( p0.
z, p1.z );
1921 region[r] =
new BlockRegion( p0, p1, regdef[r].bath );
1924 regdef[r].rad *=
CM2UM;
1929 if( !param_globals::size[2] )
1933 regdef[r].rad *=
CM2UM;
1934 regdef[r].cyllen *=
CM2UM;
1936 regdef[r].cyllen, regdef[r].bath );
1939 region[r]->
tag(regdef[r].tag);
1941 region[param_globals::numRegions] = NULL;
1945 if( !param_globals::size[1] && !param_globals::size[2] )
1946 grid =
new Grid1D( param_globals::mesh, region );
1947 else if( !param_globals::size[2] )
1948 grid =
new Grid2D( param_globals::mesh, region );
1950 grid =
new Grid3D( param_globals::mesh, region );
1955 grid->
build_mesh(x0, tsize, param_globals::size, symbath, param_globals::resolution,
1956 param_globals::perturb, param_globals::anisoBath, param_globals::periodic);
1960 std::cerr <<
"ERROR:" << std::endl;
1961 std::cerr <<
"Could not open mesh files " << param_globals::mesh <<
".* for writing!" << std::endl;
1962 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)