stardis-args.c (33349B)
1 /* Copyright (C) 2018-2025 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200809L /* strdup */ 17 #include "stardis-args.h" 18 #include "stardis-parsing.h" 19 #include "stardis-app.h" 20 #include "stardis-default.h" 21 #include "stardis-version.h" 22 23 #include <sdis_version.h> 24 25 #include <rsys/cstr.h> 26 #include <rsys/double2.h> 27 #include <rsys/double3.h> 28 #include <rsys/logger.h> 29 #include <rsys/text_reader.h> 30 31 #include <getopt.h> 32 #include <stdlib.h> 33 #include <stdio.h> 34 #include <string.h> 35 36 #ifdef COMPILER_GCC 37 #include <strings.h> /* strcasecmp */ 38 #else 39 #define strcasecmp(s1, s2) _stricmp((s1), (s2)) 40 #endif 41 42 #ifdef STARDIS_ENABLE_MPI 43 #include <mpi.h> 44 #endif 45 46 /******************************************************************************* 47 * Local Functions 48 ******************************************************************************/ 49 50 static char** 51 split_line 52 (char* a_str, 53 const char a_delim) 54 { 55 char** result = 0; 56 size_t chunks_count; 57 char* tmp = a_str; 58 char delim[2]; 59 char* tok_ctx = NULL; 60 61 ASSERT(a_str); 62 63 delim[0] = a_delim; 64 delim[1] = 0; 65 66 /* if a_str starts with initial useless delimiters remove them */ 67 while(*a_str == a_delim) a_str++; 68 69 /* if a_str ends with final useless delimiters remove them */ 70 tmp = a_str + strlen(a_str) - 1; 71 while(*tmp == a_delim && tmp >= a_str) { *tmp = '\0'; tmp--; } 72 73 if(tmp >= a_str) chunks_count = 1; 74 else return NULL; 75 76 tmp = a_str; 77 while(*tmp) { 78 int delim_found = 0; 79 while(*tmp == a_delim) { delim_found = 1; tmp++; } 80 if(delim_found) chunks_count++; 81 tmp++; 82 } 83 84 /* Add space for terminating null string so caller 85 knows where the list of returned strings ends. */ 86 result = malloc(sizeof(char*) * (1 + chunks_count)); 87 if(result) { 88 size_t idx = 0; 89 char* token = strtok_r(a_str, delim, &tok_ctx); 90 91 while(token) { 92 ASSERT(idx <= chunks_count); 93 #ifdef COMPILER_CL 94 *(result + idx++) = _strdup(token); 95 #else 96 *(result + idx++) = strdup(token); 97 #endif 98 token = strtok_r(NULL, delim, &tok_ctx); 99 } 100 ASSERT(idx == chunks_count); 101 *(result + idx) = 0; 102 } 103 return result; 104 } 105 106 static char 107 mode_option(const int m) 108 { 109 int found = 0; 110 char res = '?'; 111 if(m & MODE_DUMP_C_CHUNKS) { found++; res = 'c'; } 112 if(m & MODE_DUMP_MODEL) { found++; res = 'd'; } 113 if(m & MODE_DUMP_PATHS) { found++; res = 'D'; } 114 if(m & MODE_EXTENDED_RESULTS) { found++; res = 'e'; } 115 if(m & MODE_COMPUTE_FLUX_THROUGH_SURF) { found++; res = 'F'; } 116 if(m & MODE_COMPUTE_PROBE_FLUX_DNSTY_ON_SURF) { found++; res = 'f'; } 117 if(m & MODE_GREEN_ASCII) { found++; res = 'g'; } 118 if(m & MODE_GREEN_BIN) { found++; res = 'G'; } 119 if(m & MODE_DUMP_HELP) { found++; res = 'h'; } 120 if(m & MODE_COMPUTE_LIST_PROBE_TEMP_ON_SURF) { found++; res = 'L'; } 121 if(m & MODE_COMPUTE_LIST_PROBE_FLUX_DNSTY_ON_SURF) { found++; res = 'l'; } 122 if(m & MODE_COMPUTE_TEMP_MEAN_IN_MEDIUM) { found++; res = 'm'; } 123 if(m & MODE_COMPUTE_PROBE_TEMP_ON_VOL) { found++; res = 'p'; } 124 if(m & MODE_COMPUTE_PROBE_TEMP_ON_SURF) { found++; res = 'P'; } 125 if(m & MODE_COMPUTE_IMAGE_IR) { found++; res = 'R'; } 126 if(m & MODE_COMPUTE_TEMP_MEAN_ON_SURF) { found++; res = 's'; } 127 if(m & MODE_COMPUTE_TEMP_MAP_ON_SURF) { found++; res = 'S'; } 128 if(m & MODE_VERBOSITY) { found++; res = 'V'; } 129 if(m & MODE_DUMP_VERSION) { found++; res = 'v'; } 130 ASSERT(found == 1);(void)found; 131 return res; 132 } 133 134 static void 135 print_multiple_modes 136 (char* buf, 137 const size_t sz, 138 const int modes, 139 const int dont) /* Modes in dont are not printed */ 140 { 141 int b = 0, fst = 1; 142 int m = UNDEF_MODE; 143 size_t left = sz; 144 ASSERT(buf); 145 do { 146 m = BIT(b++); 147 if(m & dont) continue; 148 if(m & modes) { 149 size_t n = 150 (size_t)snprintf(buf, left, (fst ? "-%c" : ", -%c"), mode_option(m)); 151 if(n >= left) FATAL("Buffer is too small."); 152 left -= n; 153 buf += n; 154 fst = 0; 155 } 156 } while(m < modes); 157 } 158 159 static res_T 160 parse_diff_algo 161 (const char* str, 162 struct logger* logger, 163 enum sdis_diffusion_algorithm* out_diff_algo) 164 { 165 enum sdis_diffusion_algorithm diff_algo = SDIS_DIFFUSION_NONE; 166 res_T res = RES_OK; 167 ASSERT(str && out_diff_algo); 168 169 if(!strcmp(str, "dsphere")) { 170 diff_algo = SDIS_DIFFUSION_DELTA_SPHERE; 171 } else if(!strcmp(str, "wos")) { 172 diff_algo = SDIS_DIFFUSION_WOS; 173 } else { 174 logger_print(logger, LOG_ERROR, 175 "Invalid diffusion algorithm `%s'\n", str); 176 res = RES_BAD_ARG; 177 goto error; 178 } 179 180 exit: 181 *out_diff_algo = diff_algo; 182 return res; 183 error: 184 goto exit; 185 } 186 187 static res_T 188 parse_position_and_time(const char* str, double pos[3], double time[2]) 189 { 190 char buf[128]; 191 double pos_and_time[5]; 192 size_t len; 193 res_T res = RES_OK; 194 ASSERT(str && pos && time); 195 196 197 if(strlen(str) >= sizeof(buf)-1/*NULL char*/) { 198 fprintf(stderr, 199 "Could not duplicate the string defining a position and, optionally, " 200 "a time range `%s'\n", str); 201 res = RES_MEM_ERR; 202 goto error; 203 } 204 strncpy(buf, str, sizeof(buf)); 205 206 res = cstr_to_list_double(str, ',', pos_and_time, &len, 5); 207 if(res != RES_OK 208 || len < 3 /* Invalid position */ 209 || len > 5 /* Too many fields */) { 210 fprintf(stderr, 211 "Error parsing position and optional time range `%s'\n", str); 212 res = RES_BAD_ARG; 213 goto error; 214 } 215 216 pos[0] = pos_and_time[0]; 217 pos[1] = pos_and_time[1]; 218 pos[2] = pos_and_time[2]; 219 220 switch(len) { 221 /* No time was parsed => Steady state */ 222 case 3: 223 time[0] = INF; 224 time[1] = INF; 225 break; 226 /* A single time was parsed => the time range is degenerated */ 227 case 4: 228 time[0] = pos_and_time[3]; 229 time[1] = pos_and_time[3]; 230 break; 231 /* A time range was parsed and must be not degenerated */ 232 case 5: 233 time[0] = pos_and_time[3]; 234 time[1] = pos_and_time[4]; 235 if(time[0] > time[1]) { 236 fprintf(stderr, "Invalid time range [%g, %g}\n", time[0], time[1]); 237 res = RES_BAD_ARG; 238 goto error; 239 } 240 break; 241 default: FATAL("Unreachable code\n"); break; 242 } 243 244 exit: 245 return res; 246 error: 247 goto exit; 248 } 249 250 static res_T 251 parse_side_indicator(const char* str, char side_name[STARDIS_MAX_NAME_LENGTH]) 252 { 253 res_T res = RES_OK; 254 ASSERT(str && side_name); 255 256 if(strlen(str) >= STARDIS_MAX_NAME_LENGTH) { 257 fprintf(stderr, 258 "Side indicator could not exceed %d characters `%s'\n", 259 STARDIS_MAX_NAME_LENGTH-1, str); 260 res = RES_MEM_ERR; 261 goto error; 262 } 263 strncpy(side_name, str, STARDIS_MAX_NAME_LENGTH); 264 265 exit: 266 return res; 267 error: 268 goto exit; 269 } 270 271 static res_T 272 parse_probe_boundary 273 (const char* str, 274 int with_sides, 275 struct stardis_probe_boundary* probe) 276 { 277 char buf[128]; 278 char* pos_and_time = NULL; 279 char* side = NULL; 280 char* ctx = NULL; 281 res_T res = RES_OK; 282 ASSERT(str && probe); 283 284 if(strlen(str) >= sizeof(buf)-1/*NULL char*/) { 285 fprintf(stderr, 286 "Could not duplicate string defining probe at boundary `%s'\n", str); 287 res = RES_MEM_ERR; 288 goto error; 289 } 290 strncpy(buf, str, sizeof(buf)); 291 292 pos_and_time = strtok_r(buf, ":", &ctx); 293 if(with_sides) side = strtok_r(NULL, "", &ctx); 294 295 res = parse_position_and_time(pos_and_time, probe->position, probe->time); 296 if(res != RES_OK) goto error; 297 298 if(with_sides && side) { 299 res = parse_side_indicator(side, probe->side); 300 if(res != RES_OK) goto error; 301 } 302 303 exit: 304 return res; 305 error: 306 goto exit; 307 } 308 309 static res_T 310 allocate_probe_boundary 311 (struct args* args, 312 struct stardis_probe_boundary** out_probe) 313 { 314 size_t i = 0; 315 res_T res = RES_OK; 316 ASSERT(args && out_probe); 317 318 i = darray_probe_boundary_size_get(&args->probe_boundary_list); 319 res = darray_probe_boundary_resize(&args->probe_boundary_list, i+1); 320 if(res != RES_OK) { 321 logger_print(args->logger, LOG_ERROR, 322 "Error allocating the probe on the boundary -- %s.\n", 323 res_to_cstr(res)); 324 goto error; 325 } 326 327 *out_probe = darray_probe_boundary_data_get(&args->probe_boundary_list) + i; 328 329 exit: 330 return res; 331 error: 332 darray_probe_boundary_resize(&args->probe_boundary_list, i); /* Deallocate */ 333 goto exit; 334 } 335 336 static res_T 337 parse_probe_boundary_list 338 (const char* filename, 339 struct logger* logger, 340 struct mem_allocator* allocator, 341 int with_sides, 342 struct darray_probe_boundary* list) 343 { 344 struct txtrdr* txtrdr = NULL; 345 res_T res = RES_OK; 346 ASSERT(filename && list); 347 348 res = txtrdr_file(allocator, filename, '#', &txtrdr); 349 if(res != RES_OK) goto error; 350 351 for(;;) { 352 struct stardis_probe_boundary probe = STARDIS_PROBE_BOUNDARY_NULL; 353 const char* line = NULL; 354 355 res = txtrdr_read_line(txtrdr); 356 if(res != RES_OK) { 357 logger_print(logger, LOG_ERROR, 358 "%s: could not read the line `%lu' -- %s.\n", 359 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 360 res_to_cstr(res)); 361 goto error; 362 } 363 364 if(!(line = txtrdr_get_cline(txtrdr))) goto exit; /* No line to parse */ 365 366 res = parse_probe_boundary(line, with_sides, &probe); 367 if(res != RES_OK) goto error; 368 369 res = darray_probe_boundary_push_back(list, &probe); 370 if(res != RES_OK) { 371 logger_print(logger, LOG_ERROR, 372 "%s:%lu: error registering the probe on the boundary -- %s.\n", 373 txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), 374 res_to_cstr(res)); 375 goto error; 376 } 377 } 378 379 if(!darray_probe_boundary_size_get(list)) { 380 logger_print(logger, LOG_ERROR, 381 "The file `%s' does not list any probes on the boundary.\n", 382 filename); 383 res = RES_BAD_ARG; 384 goto error; 385 } 386 387 exit: 388 if(txtrdr) txtrdr_ref_put(txtrdr); 389 return res; 390 error: 391 darray_probe_boundary_clear(list); 392 goto exit; 393 } 394 395 /******************************************************************************* 396 * Public Functions 397 ******************************************************************************/ 398 399 void 400 print_version 401 (FILE* stream) 402 { 403 ASSERT(stream); 404 fprintf(stream, 405 "Stardis version %i.%i.%i built on stardis solver version %i.%i.%i; MPI is " 406 #ifdef STARDIS_ENABLE_MPI 407 "enabled.\n", 408 #else 409 "disabled.\n", 410 #endif 411 STARDIS_APP_VERSION_MAJOR, STARDIS_APP_VERSION_MINOR, STARDIS_APP_VERSION_PATCH, 412 SDIS_VERSION_MAJOR, SDIS_VERSION_MINOR, SDIS_VERSION_PATCH); 413 } 414 415 res_T 416 init_args 417 (struct logger* logger, 418 struct mem_allocator* allocator, 419 struct args** out_args) 420 { 421 res_T res = RES_OK; 422 struct args* args = NULL; 423 ASSERT(logger && allocator && out_args); 424 425 args = calloc(1, sizeof(struct args)); 426 if(!args) { 427 res = RES_MEM_ERR; 428 goto error; 429 } 430 431 args->logger = logger; 432 args->allocator = allocator; 433 darray_str_init(allocator, &args->model_files); 434 /* Set non-zero default values */ 435 args->samples = STARDIS_DEFAULT_SAMPLES_COUNT; 436 args->nthreads = SDIS_NTHREADS_DEFAULT; 437 args->picard_order = STARDIS_DEFAULT_PICARD_ORDER; 438 args->diff_algo = SDIS_DIFFUSION_DELTA_SPHERE; 439 d2(args->pos_and_time+3, 440 STARDIS_DEFAULT_COMPUTE_TIME, STARDIS_DEFAULT_COMPUTE_TIME); 441 args->verbose = STARDIS_DEFAULT_VERBOSE_LEVEL; 442 darray_probe_boundary_init(allocator, &args->probe_boundary_list); 443 444 end: 445 *out_args = args; 446 return res; 447 error: 448 if(args) release_args(args); 449 args = NULL; 450 goto end; 451 } 452 453 void 454 release_args(struct args* args) 455 { 456 ASSERT(args); 457 darray_str_release(&args->model_files); 458 darray_probe_boundary_release(&args->probe_boundary_list); 459 free(args); 460 } 461 462 void 463 usage(FILE* stream) 464 { 465 #define PRINT(Msg) fprintf(stream, Msg) 466 PRINT("stardis [-eghiv] [-a diff_algo] [-D path_type,files_name_prefix]\n"); 467 PRINT(" [-d file_base_name] [-F surface[,time[,time]]]\n"); 468 PRINT(" [-f x,y,z[,time[,time]]] [-G green_bin[,green_ascii]]\n"); 469 PRINT(" [-I initial_time] [-L interface_probes] [-l interface_probes]\n"); 470 PRINT(" [-m medium_name[,time[,time]]] [-n samples_count]\n"); 471 PRINT(" [-o picard_order] [-P x,y,z[,time[,time]][:side_indicator]]\n"); 472 PRINT(" [-p x,y,z[,time[,time]]] [-R rendering_opt[:rendering_opt ...]]\n"); 473 PRINT(" [-S surface[,time[,time]]] [-s surface[,time[,time]]]\n"); 474 PRINT(" [-t threads_count] [-V verbosity_level] [-X output_rng]\n"); 475 PRINT(" [-x input_rng] -M system\n"); 476 #undef PRINT 477 } 478 479 #define FREE_AARRAY(ARRAY) \ 480 if(ARRAY) {\ 481 int i__ = 0; \ 482 for(i__=0; *((ARRAY)+i__);i__++){\ 483 free((ARRAY)[i__]);\ 484 }\ 485 free(ARRAY);\ 486 (ARRAY) = NULL;\ 487 } 488 489 /* Workaround for a gcc warning when GET_OPTIONAL_TIME_RANGE used with Rank=0 */ 490 static FINLINE int is_less(size_t a, size_t b) { return a < b; } 491 492 /* Get a time range from a coma-separated list of doubles 493 * The first Rank values are mandatory, followed by an optional time range 494 * that can be a single time */ 495 #define GET_OPTIONAL_TIME_RANGE(Src, Rank, Dst, Logger, OptionString, Option, FullSrc) \ 496 res = cstr_to_list_double((Src), ',', (Dst), &len, (Rank)+2); \ 497 if(res != RES_OK \ 498 || is_less(len, (Rank)) \ 499 || (len == (Rank)+1 && (Dst)[(Rank)] < 0) \ 500 || (len == (Rank)+2 && ((Dst)[0] < 0 || (Dst)[(Rank)] > (Dst)[(Rank)+1])) \ 501 || len > (Rank)+2) \ 502 { \ 503 if(res == RES_OK) res = RES_BAD_ARG; \ 504 logger_print((Logger), LOG_ERROR, \ 505 "Invalid argument for option "OptionString": %s\n", \ 506 (Option), (FullSrc)); \ 507 goto error; \ 508 } else { \ 509 if(len == (Rank)+1) (Dst)[(Rank)+1] = (Dst)[(Rank)];\ 510 } 511 512 /* Get a string followed by an optional time range */ 513 #define GET_STR_AND_OPTIONAL_TIME_RANGE(Str, Time) \ 514 ptr = strchr(optarg, ','); /* First ',' */ \ 515 if(ptr) { /* Time range provided */ \ 516 GET_OPTIONAL_TIME_RANGE(ptr+1, 0, (Time), args->logger, "-%c", opt, optarg); \ 517 *ptr = '\0'; \ 518 } \ 519 (Str) = optarg; 520 521 /* Get a position followed by an optional time range */ 522 #define GET_POS_AND_OPTIONAL_TIME_RANGE(Src, Dst, FullSrc) \ 523 GET_OPTIONAL_TIME_RANGE((Src), 3, (Dst), args->logger, "-%c", opt, (FullSrc)); 524 525 res_T 526 parse_args 527 (const int argc, 528 char** argv, 529 struct args* args, 530 struct mem_allocator* allocator) 531 { 532 int opt = 0, n_used = 0, o_used = 0; 533 size_t len = 0; 534 const char option_list[] = "a:c:d:D:ef:F:gG:hiI:L:l:m:M:n:o:p:P:R:s:S:t:vV:x:X:"; 535 char buf[128]; 536 struct str keep; 537 char** line = NULL; 538 res_T res = RES_OK; 539 540 ASSERT(argv && args); 541 542 str_init(allocator, &keep); 543 opterr = 0; /* No default error messages */ 544 while((opt = getopt(argc, argv, option_list)) != -1) { 545 switch (opt) { 546 547 case '?': /* Unreconised option */ 548 { 549 char* ptr = strchr(option_list, optopt); 550 res = RES_BAD_ARG; 551 if(ptr && ptr[1] == ':') { 552 logger_print(args->logger, LOG_ERROR, 553 "Missing argument for option -%c\n", 554 optopt); 555 } else { 556 logger_print(args->logger, LOG_ERROR, "Invalid option -%c\n", optopt); 557 } 558 goto error; 559 } 560 561 case 'a': 562 res = parse_diff_algo(optarg, args->logger, &args->diff_algo); 563 if(res != RES_OK) goto error; 564 break; 565 566 case 'c': 567 if(args->mode & USE_STDOUT_MODES) { 568 res = RES_BAD_ARG; 569 print_multiple_modes(buf, sizeof(buf), USE_STDOUT_MODES, MODE_DUMP_C_CHUNKS); 570 logger_print(args->logger, LOG_ERROR, 571 "Option -%c cannot be used in conjunction with other dump options (%s).\n", 572 (char)opt, buf); 573 goto error; 574 } 575 args->chunks_prefix = optarg; 576 args->mode |= MODE_DUMP_C_CHUNKS; 577 break; 578 579 case 'd': 580 args->dump_model_filename = optarg; 581 args->mode |= MODE_DUMP_MODEL; 582 break; 583 584 case 'D': { 585 char* ptr = strrchr(optarg, ','); 586 if(!ptr || ptr != strchr(optarg, ',')) 587 res = RES_BAD_ARG; /* Single ',' expected */ 588 else { 589 args->paths_filename = ptr + 1; 590 *ptr = '\0'; 591 } 592 if(res == RES_OK) { 593 if(0 == strcasecmp(optarg, "all")) { 594 args->dump_paths = DUMP_ALL; 595 } 596 else if(0 == strcasecmp(optarg, "error")) { 597 args->dump_paths = DUMP_ERROR; 598 } 599 else if(0 == strcasecmp(optarg, "success")) { 600 args->dump_paths = DUMP_SUCCESS; 601 } 602 } 603 if(res != RES_OK) { 604 res = RES_BAD_ARG; 605 logger_print(args->logger, LOG_ERROR, 606 "Invalid argument for option -%c: %s\n", 607 opt, optarg); 608 goto error; 609 } 610 args->mode |= MODE_DUMP_PATHS; 611 break; 612 } 613 614 case 'e': 615 args->mode |= MODE_EXTENDED_RESULTS; 616 break; 617 618 case 'f': 619 { 620 struct stardis_probe_boundary* probe = NULL; 621 622 if(args->mode & EXCLUSIVE_MODES) { 623 logger_print(args->logger, LOG_ERROR, 624 "Options -%c and -%c are exclusive.\n", 625 (char)opt, mode_option(args->mode)); 626 res = RES_BAD_ARG; 627 goto error; 628 } 629 args->mode |= MODE_COMPUTE_PROBE_FLUX_DNSTY_ON_SURF; 630 res = allocate_probe_boundary(args, &probe); 631 if(res != RES_OK) goto error; 632 res = parse_probe_boundary(optarg, 0, probe); 633 if(res != RES_OK) goto error; 634 break; 635 } 636 637 /*case 'F': see 's' */ 638 639 case 'g': 640 if(args->mode & MODE_GREEN_BIN) { 641 res = RES_BAD_ARG; 642 logger_print(args->logger, LOG_ERROR, 643 "Options -%c and -%c are exclusive.\n", 644 (char)opt, mode_option(MODE_GREEN_BIN)); 645 goto error; 646 } 647 args->mode |= MODE_GREEN_ASCII; 648 break; 649 650 case 'G': { 651 char* ptr = strrchr(optarg, ','); 652 if(ptr && ptr != strchr(optarg, ',')) 653 res = RES_BAD_ARG; /* Expecting 1 or 0 ',' */ 654 if(args->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)) { 655 res = RES_BAD_ARG; 656 if(args->mode & MODE_GREEN_BIN) 657 logger_print(args->logger, LOG_ERROR, 658 "Option -%c cannot be used twice.\n", 659 (char)opt); 660 else 661 logger_print(args->logger, LOG_ERROR, 662 "Options -%c and -%c are exclusive.\n", 663 (char)opt, mode_option(MODE_GREEN_ASCII)); 664 goto error; 665 } 666 args->mode |= MODE_GREEN_BIN; 667 if(ptr) { 668 args->end_paths_filename = ptr + 1; 669 *ptr = '\0'; 670 } 671 args->bin_green_filename = optarg; 672 break; 673 } 674 675 case 'h': 676 if(args->mode & USE_STDOUT_MODES) { 677 res = RES_BAD_ARG; 678 print_multiple_modes(buf, sizeof(buf), USE_STDOUT_MODES, MODE_DUMP_HELP); 679 logger_print(args->logger, LOG_ERROR, 680 "Option -%c cannot be used in conjunction with other dump options (%s).\n", 681 (char)opt, buf); 682 goto error; 683 } 684 args->mode |= MODE_DUMP_HELP; 685 break; 686 687 case 'I': 688 res = cstr_to_double(optarg, &args->initial_time); 689 if(res != RES_OK) goto error; 690 break; 691 692 case 'i': 693 args->disable_intrad = 1; 694 break; 695 696 case 'L': 697 if(args->mode & EXCLUSIVE_MODES) { 698 logger_print(args->logger, LOG_ERROR, 699 "Options -%c and -%c are exclusive.\n", 700 (char)opt, mode_option(args->mode)); 701 goto error; 702 } 703 args->mode |= MODE_COMPUTE_LIST_PROBE_TEMP_ON_SURF; 704 res = parse_probe_boundary_list 705 (optarg, args->logger, args->allocator, 1, &args->probe_boundary_list); 706 if(res != RES_OK) goto error; 707 break; 708 709 case 'l': 710 if(args->mode & EXCLUSIVE_MODES) { 711 logger_print(args->logger, LOG_ERROR, 712 "Options -%c and -%c are exclusive.\n", 713 (char)opt, mode_option(args->mode)); 714 goto error; 715 } 716 args->mode |= MODE_COMPUTE_LIST_PROBE_FLUX_DNSTY_ON_SURF; 717 res = parse_probe_boundary_list 718 (optarg, args->logger, args->allocator, 0, &args->probe_boundary_list); 719 if(res != RES_OK) goto error; 720 break; 721 722 case 'm': { 723 char* ptr; 724 if(args->mode & EXCLUSIVE_MODES) { 725 res = RES_BAD_ARG; 726 logger_print(args->logger, LOG_ERROR, 727 "Options -%c and -%c are exclusive.\n", 728 (char)opt, mode_option(args->mode)); 729 goto error; 730 } 731 args->mode |= MODE_COMPUTE_TEMP_MEAN_IN_MEDIUM; 732 GET_STR_AND_OPTIONAL_TIME_RANGE(args->medium_name, args->pos_and_time + 3); 733 break; 734 } 735 736 case 'M': { 737 struct str name; 738 str_init(args->allocator, &name); 739 ERR(str_set(&name, optarg)); 740 ERR(darray_str_push_back(&args->model_files, &name)); 741 str_release(&name); 742 break; 743 } 744 case 'n': { 745 long n; 746 res = cstr_to_long(optarg, &n); 747 if(res != RES_OK 748 || n <= 0) 749 { 750 if(res == RES_OK) res = RES_BAD_ARG; 751 logger_print(args->logger, LOG_ERROR, 752 "Invalid argument for option -%c: %s\n", 753 opt, optarg); 754 goto error; 755 } 756 args->samples = (unsigned long)n; 757 n_used = 1; 758 break; 759 } 760 761 case 'o': { 762 int order; 763 res = cstr_to_int(optarg, &order); 764 if(res != RES_OK 765 || order <= 0) 766 { 767 if(res == RES_OK) res = RES_BAD_ARG; 768 logger_print(args->logger, LOG_ERROR, 769 "Invalid argument for option -%c: %s\n", 770 opt, optarg); 771 goto error; 772 } 773 args->picard_order = (unsigned)order; 774 o_used = 1; 775 break; 776 } 777 778 case 'p': 779 if(args->mode & EXCLUSIVE_MODES) { 780 res = RES_BAD_ARG; 781 logger_print(args->logger, LOG_ERROR, 782 "Options -%c and -%c are exclusive.\n", 783 (char)opt, mode_option(args->mode)); 784 goto error; 785 } 786 args->mode |= MODE_COMPUTE_PROBE_TEMP_ON_VOL; 787 GET_POS_AND_OPTIONAL_TIME_RANGE(optarg, args->pos_and_time, optarg); 788 break; 789 790 case 'P': { 791 struct stardis_probe_boundary* probe = NULL; 792 793 if(args->mode & EXCLUSIVE_MODES) { 794 logger_print(args->logger, LOG_ERROR, 795 "Options -%c and -%c are exclusive.\n", 796 (char)opt, mode_option(args->mode)); 797 res = RES_BAD_ARG; 798 goto error; 799 } 800 args->mode |= MODE_COMPUTE_PROBE_TEMP_ON_SURF; 801 res = allocate_probe_boundary(args, &probe); 802 if(res != RES_OK) goto error; 803 res = parse_probe_boundary(optarg, 1, probe); 804 if(res != RES_OK) goto error; 805 break; 806 } 807 808 case 'R': 809 if(args->mode & EXCLUSIVE_MODES) { 810 res = RES_BAD_ARG; 811 logger_print(args->logger, LOG_ERROR, 812 "Options -%c and -%c are exclusive.\n", 813 (char)opt, mode_option(args->mode)); 814 goto error; 815 } 816 args->mode |= MODE_COMPUTE_IMAGE_IR; 817 args->camera = optarg; 818 break; 819 820 case 's': 821 case 'S': 822 case 'F': { 823 char *ptr; 824 if(args->mode & EXCLUSIVE_MODES) { 825 res = RES_BAD_ARG; 826 logger_print(args->logger, LOG_ERROR, 827 "Options -%c and -%c are exclusive.\n", 828 (char)opt, mode_option(args->mode)); 829 goto error; 830 } 831 switch (opt) { 832 case 's': 833 args->mode |= MODE_COMPUTE_TEMP_MEAN_ON_SURF; 834 break; 835 case 'S': 836 args->mode |= MODE_COMPUTE_TEMP_MAP_ON_SURF; 837 break; 838 case 'F': 839 args->mode |= MODE_COMPUTE_FLUX_THROUGH_SURF; 840 break; 841 } 842 GET_STR_AND_OPTIONAL_TIME_RANGE(args->solve_filename, args->pos_and_time + 3); 843 break; 844 } 845 846 case 't': { 847 int nt; 848 res = cstr_to_int(optarg, &nt); 849 if(res != RES_OK 850 || nt <= 0) 851 { 852 if(res == RES_OK) res = RES_BAD_ARG; 853 logger_print(args->logger, LOG_ERROR, 854 "Invalid argument for option -%c: %s\n", 855 opt, optarg); 856 goto error; 857 } 858 args->nthreads = (unsigned)nt; 859 break; 860 } 861 862 case 'v': 863 if(args->mode & USE_STDOUT_MODES) { 864 res = RES_BAD_ARG; 865 print_multiple_modes(buf, sizeof(buf), USE_STDOUT_MODES, MODE_DUMP_VERSION); 866 logger_print(args->logger, LOG_ERROR, 867 "Option -%c cannot be used in conjunction with other dump options (%s).\n", 868 (char)opt, buf); 869 goto error; 870 } 871 args->mode |= MODE_DUMP_VERSION; 872 break; 873 874 case 'V': 875 res = cstr_to_int(optarg, &args->verbose); 876 if(res != RES_OK 877 || args->verbose < 0 878 || args->verbose > 3) 879 { 880 if(res == RES_OK) res = RES_BAD_ARG; 881 logger_print(args->logger, LOG_ERROR, 882 "Invalid argument for option -%c: %s\n", 883 opt, optarg); 884 goto error; 885 } 886 break; 887 888 case 'x': 889 if(!(args->mode & RANDOM_RW_MODES)) { 890 res = RES_BAD_ARG; 891 print_multiple_modes(buf, sizeof(buf), RANDOM_RW_MODES, 0); 892 logger_print(args->logger, LOG_ERROR, 893 "Option -%c can only be used in conjunction with one of the following options: %s.\n", 894 (char)opt, buf); 895 goto error; 896 } 897 args->rndgen_state_in_filename = optarg; 898 break; 899 900 case 'X': 901 if(!(args->mode & RANDOM_RW_MODES)) { 902 res = RES_BAD_ARG; 903 print_multiple_modes(buf, sizeof(buf), RANDOM_RW_MODES, 0); 904 logger_print(args->logger, LOG_ERROR, 905 "Option -%c can only be used in conjunction with one of the following options: %s.\n", 906 (char)opt, buf); 907 goto error; 908 } 909 args->rndgen_state_out_filename = optarg; 910 break; 911 } 912 } 913 914 if(argc > optind) { 915 int i; 916 for(i = optind; i < argc; i++) { 917 logger_print(args->logger, LOG_ERROR, "Unexpected argument: %s.\n", argv[i]); 918 } 919 res = RES_BAD_ARG; 920 goto error; 921 } 922 923 if(!darray_str_size_get(&args->model_files) 924 && !(args->mode & SHORT_EXIT_MODES)) { 925 logger_print(args->logger, LOG_ERROR, 926 "Missing mandatory argument: -M <model_file_name>\n"); 927 res = RES_BAD_ARG; 928 goto error; 929 } 930 931 if(args->mode == UNDEF_MODE) { 932 print_multiple_modes(buf, sizeof(buf), EXCLUSIVE_MODES | USE_STDOUT_MODES, 0); 933 logger_print(args->logger, LOG_ERROR, 934 "Nothing to do.\n"); 935 logger_print(args->logger, LOG_ERROR, 936 "One of the following options should be used: %s\n", buf); 937 res = RES_BAD_ARG; 938 goto error; 939 } 940 941 if(args->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII) 942 && !(args->mode & GREEN_COMPATIBLE_MODES)) 943 { 944 print_multiple_modes(buf, sizeof(buf), GREEN_COMPATIBLE_MODES, 0); 945 logger_print(args->logger, LOG_ERROR, 946 "Option -%c can only be used in conjunction with: %s\n", 947 mode_option(args->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)), buf); 948 res = RES_BAD_ARG; 949 goto error; 950 } 951 952 if(args->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII) 953 && o_used && args->picard_order > 1) 954 { 955 logger_print(args->logger, LOG_ERROR, 956 "Option -%c cannot be used if Picard order is not 1 (here order is %u)\n", 957 mode_option(args->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)), 958 args->picard_order); 959 res = RES_BAD_ARG; 960 goto error; 961 } 962 963 if(args->mode & MODE_COMPUTE_IMAGE_IR && n_used) { 964 logger_print(args->logger, LOG_ERROR, 965 "The -n option has no effect in rendering mode;" 966 " use rendering's SPP suboption instead.\n"); 967 res = RES_BAD_ARG; 968 goto error; 969 } 970 971 if(args->mode & MODE_DUMP_PATHS) { 972 if(!(args->mode & CAN_DUMP_PATHS)) { 973 res = RES_BAD_ARG; 974 print_multiple_modes(buf, sizeof(buf), CAN_DUMP_PATHS, 0); 975 logger_print(args->logger, LOG_ERROR, 976 "Option -%c can only be used in conjunction with some options" 977 " that sample heat paths (%s).\n", 978 mode_option(MODE_DUMP_PATHS), buf); 979 goto error; 980 } 981 if(args->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)) { 982 res = RES_BAD_ARG; 983 logger_print(args->logger, LOG_ERROR, 984 "Option -%c cannot be used in conjunction with -%c nor -%c.\n", 985 mode_option(MODE_DUMP_PATHS), mode_option(MODE_GREEN_ASCII) 986 , mode_option(MODE_GREEN_BIN)); 987 goto error; 988 } 989 } 990 991 if(args->mode & MODE_EXTENDED_RESULTS) { 992 if(!(args->mode & EXT_COMPATIBLE_MODES)) { 993 res = RES_BAD_ARG; 994 print_multiple_modes(buf, sizeof(buf), EXT_COMPATIBLE_MODES, 0); 995 logger_print(args->logger, LOG_ERROR, 996 "Option -%c can only be used in conjunction with an option" 997 " that computes a single Monte-Carlo (%s).\n", 998 mode_option(MODE_EXTENDED_RESULTS), buf); 999 goto error; 1000 } 1001 if(args->mode & (MODE_GREEN_BIN | MODE_GREEN_ASCII)) { 1002 res = RES_BAD_ARG; 1003 logger_print(args->logger, LOG_ERROR, 1004 "Option -%c cannot be used in conjunction with -%c nor -%c.\n", 1005 mode_option(MODE_EXTENDED_RESULTS), mode_option(MODE_GREEN_ASCII) 1006 , mode_option(MODE_GREEN_BIN)); 1007 goto error; 1008 } 1009 } 1010 1011 if(args->rndgen_state_in_filename && !(args->mode & COMPUTE_MODES)) { 1012 res = RES_BAD_ARG; 1013 print_multiple_modes(buf, sizeof(buf), COMPUTE_MODES, 0); 1014 logger_print(args->logger, LOG_ERROR, 1015 "Option -x can only be used in conjunction with an option" 1016 " that launch a MC computation (%s).\n", 1017 buf); 1018 goto error; 1019 } 1020 1021 if(args->rndgen_state_out_filename && !(args->mode & COMPUTE_MODES)) { 1022 res = RES_BAD_ARG; 1023 print_multiple_modes(buf, sizeof(buf), COMPUTE_MODES, 0); 1024 logger_print(args->logger, LOG_ERROR, 1025 "Option -X can only be used in conjunction with an option" 1026 " that launch a MC computation (%s).\n", 1027 buf); 1028 goto error; 1029 } 1030 1031 end: 1032 FREE_AARRAY(line); 1033 str_release(&keep); 1034 return res; 1035 error: 1036 logger_print(args->logger, LOG_ERROR, "Use option -h to print help.\n"); 1037 goto end; 1038 } 1039 1040 res_T 1041 parse_camera 1042 (struct logger* logger, 1043 char* cam_param, 1044 struct stardis* stardis) 1045 { 1046 char** line = NULL; 1047 char** opt = NULL; 1048 struct camera* cam; 1049 struct str keep; 1050 int i = 0; 1051 res_T res = RES_OK; 1052 1053 ASSERT(cam_param && stardis); 1054 cam = &stardis->camera; 1055 line = split_line(cam_param, ':'); 1056 if(!line) { 1057 res = RES_MEM_ERR; 1058 goto error; 1059 } 1060 1061 str_init(stardis->allocator, &keep); 1062 for(i = 0; *(line + i); i++) { 1063 size_t len = 0; 1064 ERR(str_set(&keep, line[i])); 1065 opt = split_line(line[i], '='); 1066 if(!opt[0] || !opt[1] || opt[2]) { /* We expect 2 parts */ 1067 if(res == RES_OK) res = RES_BAD_ARG; 1068 logger_print((logger), LOG_ERROR, 1069 "Invalid option syntax: %s\n", str_cget(&keep)); 1070 goto error; 1071 } 1072 if(strcasecmp(opt[0], "T") == 0) { 1073 GET_OPTIONAL_TIME_RANGE(opt[1], 0, cam->time_range, logger, "%s", opt[0], 1074 str_cget(&keep)); 1075 } 1076 else if(strcasecmp(opt[0], "FILE") == 0) { 1077 ERR(str_set(&cam->file_name, opt[1])); 1078 } 1079 else if(strcasecmp(opt[0], "FMT") == 0) { 1080 if(strcasecmp(opt[1], "VTK") == 0) 1081 cam->fmt = STARDIS_RENDERING_OUTPUT_FILE_FMT_VTK; 1082 else if(strcasecmp(opt[1], "HT") == 0) 1083 cam->fmt = STARDIS_RENDERING_OUTPUT_FILE_FMT_HT; 1084 else { 1085 logger_print(logger, LOG_ERROR, 1086 "Unexpected value for rendering option %s: %s.\n", 1087 opt[0], opt[1]); 1088 res = RES_BAD_ARG; 1089 goto error; 1090 } 1091 } 1092 else if(strcasecmp(opt[0], "FOV") == 0) { 1093 res = cstr_to_double(opt[1], &cam->fov); 1094 if(res != RES_OK 1095 || cam->fov <= 0) 1096 { 1097 if(res == RES_OK) res = RES_BAD_ARG; 1098 logger_print((logger), LOG_ERROR, 1099 "Invalid %s option: %s\n", opt[0], opt[1]); 1100 goto error; 1101 } 1102 } 1103 else if(strcasecmp(opt[0], "UP") == 0) { 1104 ERR(cstr_to_list_double(opt[1], ',', cam->up, &len, 3)); 1105 } 1106 else if(strcasecmp(opt[0], "TGT") == 0) { 1107 ERR(cstr_to_list_double(opt[1], ',', cam->tgt, &len, 3)); 1108 cam->auto_look_at = 0; 1109 } 1110 else if(strcasecmp(opt[0], "POS") == 0) { 1111 ERR(cstr_to_list_double(opt[1], ',', cam->pos, &len, 3)); 1112 cam->auto_look_at = 0; 1113 } 1114 else if(strcasecmp(opt[0], "IMG") == 0) { 1115 unsigned img_sz[2]; 1116 res = cstr_to_list_uint(opt[1], 'x', img_sz, &len, 2); 1117 if(res != RES_OK 1118 /* mimic cstr_to_list_int() possible behaviour; but it doesnt exist */ 1119 || img_sz[0] == 0 || img_sz[0] > INT_MAX 1120 || img_sz[1] == 0 || img_sz[1] > INT_MAX) 1121 { 1122 if(res == RES_OK) res = RES_BAD_ARG; 1123 logger_print((logger), LOG_ERROR, 1124 "Invalid %s option: %s\n", opt[0], opt[1]); 1125 goto error; 1126 } 1127 cam->img_width = img_sz[0]; 1128 cam->img_height = img_sz[1]; 1129 } 1130 else if(strcasecmp(opt[0], "SPP") == 0) { 1131 int ssp; 1132 res = cstr_to_int(opt[1], &ssp); 1133 if(res != RES_OK 1134 || ssp <= 0) 1135 { 1136 if(res == RES_OK) res = RES_BAD_ARG; 1137 logger_print((logger), LOG_ERROR, 1138 "Invalid %s option: %s\n", opt[0], opt[1]); 1139 goto error; 1140 } 1141 cam->spp = (unsigned)ssp; 1142 } else { 1143 logger_print(logger, LOG_ERROR, 1144 "Unexpected option for rendering mode: %s.\n", 1145 opt[0]); 1146 res = RES_BAD_ARG; 1147 goto error; 1148 } 1149 FREE_AARRAY(opt); 1150 } 1151 1152 end: 1153 FREE_AARRAY(line); 1154 FREE_AARRAY(opt); 1155 #undef FREE_AARRAY 1156 1157 str_release(&keep); 1158 return res; 1159 error: 1160 logger_print(logger, LOG_ERROR, "Error parsing camera options.\n"); 1161 logger_print(logger, LOG_ERROR, "Use the -h option to get help.\n"); 1162 goto end; 1163 } 1164 1165 #undef GET_STR_AND_OPTIONAL_TIME_RANGE 1166 #undef GET_POS_AND_OPTIONAL_TIME_RANGE 1167 #undef GET_OPTIONAL_TIME_RANGE