stardis

Perform coupled heat transfer calculations
git clone git://git.meso-star.fr/stardis.git
Log | Files | Refs | README | LICENSE

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