solstice

Compute collected power and efficiencies of a solar plant
git clone git://git.meso-star.com/solstice.git
Log | Files | Refs | README | LICENSE

solstice_args.c (21454B)


      1 /* Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2016-2018 CNRS
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     16 
     17 #define _XOPEN_SOURCE /* strptime support */
     18 #define _POSIX_C_SOURCE 2
     19 
     20 #include "solstice_args.h"
     21 #include "solstice_version.h"
     22 
     23 #include <rsys/cstr.h>
     24 #include <rsys/double2.h>
     25 #include <rsys/double3.h>
     26 #include <rsys/stretchy_array.h>
     27 
     28 #include <unistd.h>
     29 
     30 #include <string.h>
     31 #include <time.h>
     32 
     33 /*******************************************************************************
     34  * Helper functions
     35  ******************************************************************************/
     36 static const char* ALGO_NAMES[] = { "meeus", "psa" };
     37 
     38 static const char*
     39 algo_to_str(enum solstice_args_sun_direction_algorithm algo) {
     40   ASSERT((unsigned)algo < (sizeof(ALGO_NAMES)/sizeof(*ALGO_NAMES)));
     41  return ALGO_NAMES[algo];
     42 }
     43 
     44 static void
     45 print_help(const char* program)
     46 {
     47   printf(
     48 "Usage: %s [OPTIONS] [FILE]\n"
     49 "Integrate the solar flux in a complex solar facility described in FILE. If not\n"
     50 "define, the solar facility is read from standard input. Refer to solstice(1)\n"
     51 "man page for more informations.\n\n",
     52     program);
     53   printf(
     54 "  -A <algo>        set the algorithm to compute sun directions. Default is %s.\n",
     55     algo_to_str(SOLSTICE_ARGS_DEFAULT.sun_algorithm));
     56   printf(
     57 "  -D <dir>         request a computation for a sun direction.\n");
     58   printf(
     59 "  -f               overwrite the output files if they already exist, i.e. the\n"
     60 "                   OUTPUT file and the output RNG state.\n");
     61   printf(
     62 "  -G <rng>         save and restore the state of the random number generator.\n");
     63   printf(
     64 "  -g <dump>        switch in dump geometry mode and configure it.\n");
     65   printf(
     66 "  -h               display this help and exit.\n");
     67   printf(
     68 "  -I <DNI>         set the DNI value.\n");
     69   printf(
     70 "  -n SAMPLES       number of Monte Carlo samples. Default is %lu.\n",
     71     SOLSTICE_ARGS_DEFAULT.nexperiments);
     72   printf(
     73 "  -L <location>    set the location of the plant on earth surface.\n");
     74   printf(
     75 "  -o OUTPUT        write results to OUTPUT. If not defined, write results to\n"
     76 "                   standard output.\n");
     77   printf(
     78 "  -p <dump-paths>  switch in dump radiative paths mode and configure it.\n");
     79   printf(
     80 "  -q               do not print the helper message when no FILE is submitted.\n");
     81   printf(
     82 "  -R RECEIVERS     define the file from which the list of receivers are read.\n");
     83   printf(
     84 "  -r <rendering>   switch in rendering mode and configure it.\n");
     85   printf(
     86 "  -T <time>        request a computation at a given time at the last specified\n"
     87 "                   location.\n");
     88   printf(
     89 "  -t THREADS       hint on the number of threads to use. By default use as\n"
     90 "                   many threads as CPU cores.\n");
     91   printf(
     92 "  -v               make the program more verbose.\n");
     93   printf(
     94 "  --version        display version information and exit.\n");
     95   printf("\n");
     96   printf(
     97 "Copyright (C) 2018-2026 |Méso|Star> (contact@meso-star.com).\n"
     98 "Copyright (C) 2016-2018 CNRS.\n"
     99 "Solstice is a free software released under the GNU GPL license, version 3 or\n"
    100 "later. You are free to change or redistribute it under certain conditions\n"
    101 "<http://gnu.org/licenses/gpl.html>.\n");
    102 }
    103 
    104 static res_T
    105 parse_fov(const char* str, double* out_fov)
    106 {
    107   double fov;
    108   res_T res = RES_OK;
    109   ASSERT(str && out_fov);
    110 
    111   res = cstr_to_double(str, &fov);
    112   if(res != RES_OK) {
    113     fprintf(stderr, "Invalid field of view `%s'.\n", str);
    114     return RES_BAD_ARG;
    115   }
    116 
    117   if(fov < 30 || fov > 120) {
    118     fprintf(stderr, "The field of view %g is not in [30, 120].\n", fov);
    119     return RES_BAD_ARG;
    120   }
    121   *out_fov = fov;
    122   return RES_OK;
    123 }
    124 
    125 static res_T
    126 parse_multiple_options
    127   (const char* str,
    128    struct solstice_args* args,
    129    res_T (*parse_option)(const char* str, struct solstice_args* args))
    130 {
    131   char buf[512];
    132   char* tk;
    133   char* ctx;
    134   res_T res = RES_OK;
    135   ASSERT(args && str);
    136 
    137   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    138     fprintf(stderr, "Could not duplicate the option string `%s'.\n", str);
    139     res = RES_MEM_ERR;
    140     goto error;
    141   }
    142   strncpy(buf, str, sizeof(buf));
    143 
    144   tk = strtok_r(buf, ":", &ctx);
    145   do {
    146     res = parse_option(tk, args);
    147     if(res != RES_OK) goto error;
    148     tk = strtok_r(NULL, ":", &ctx);
    149   } while(tk);
    150 
    151 exit:
    152   return res;
    153 error:
    154   goto exit;
    155 }
    156 
    157 static res_T
    158 parse_sun_spherical(const char* str, struct solstice_args* args)
    159 {
    160   size_t len;
    161   struct solstice_args_sun_dir sun_dir;
    162   double tmp[2];
    163   res_T res = RES_OK;
    164   ASSERT(str && args);
    165 
    166   res = cstr_to_list_double(str, ',', tmp, &len, 2);
    167   if(res != RES_OK || len != 2) {
    168     if(res == RES_OK) res = RES_BAD_ARG;
    169     fprintf(stderr, "Invalid sun direction `%s'.\n", str);
    170     goto error;
    171   }
    172 
    173   if(tmp[0] < 0 || tmp[0] >= 360) {
    174     fprintf(stderr,
    175         "Invalid azimuth angle `%g'. Azimuth must be in [0, 360[ degrees.\n",
    176         tmp[0]);
    177     res = RES_BAD_ARG;
    178     goto error;
    179   }
    180   if(tmp[1] < 0 || tmp[1] > 90) {
    181     fprintf(stderr,
    182         "Invalid elevation angle `%g'. Elevation must be in [0, 90] degrees.\n",
    183         tmp[1]);
    184     res = RES_BAD_ARG;
    185     goto error;
    186   }
    187 
    188   sun_dir.type = SOLSTICE_ARGS_SPHERICAL;
    189   sun_dir.spherical.azimuth = tmp[0];
    190   sun_dir.spherical.elevation = tmp[1];
    191   sa_push(args->sun_dirs, sun_dir);
    192   args->nsun_dirs++;
    193 
    194 exit:
    195   return res;
    196 error:
    197   if(args->sun_dirs) {
    198     sa_release(args->sun_dirs);
    199     args->sun_dirs = NULL;
    200     args->nsun_dirs = 0;
    201   }
    202   goto exit;
    203 }
    204 
    205 static res_T
    206 parse_sun_algorithm(const char* str, struct solstice_args* args)
    207 {
    208   res_T res = RES_OK;
    209   ASSERT(str && args);
    210 
    211   if(0 == strcmp(str, "meeus")) {
    212     args->sun_algorithm = SOLSTICE_ARGS_SUN_DIRECTION_ALGORITHM_meeus;
    213   }
    214   else if(0 == strcmp(str, "psa")) {
    215     args->sun_algorithm = SOLSTICE_ARGS_SUN_DIRECTION_ALGORITHM_psa;
    216   }
    217   else {
    218     res = RES_BAD_ARG;
    219     fprintf(stderr, "Invalid sun algorithm `%s'.\n", str);
    220     goto error;
    221   }
    222 
    223 exit:
    224   return res;
    225 error:
    226   goto exit;
    227 }
    228 
    229 static res_T
    230 parse_location
    231   (const char* str,
    232    double current_loc[2])
    233 {
    234   size_t len;
    235   double tmp[2];
    236   res_T res = RES_OK;
    237   ASSERT(str && current_loc);
    238 
    239   res = cstr_to_list_double(str, ',', tmp, &len, 2);
    240   if(res != RES_OK || len != 2) {
    241     if(res == RES_OK) res = RES_BAD_ARG;
    242     fprintf(stderr, "Invalid location: `%s'.\n", str);
    243     goto error;
    244   }
    245 
    246   if(tmp[0] < -90 || tmp[0] > 90) {
    247     fprintf(stderr,
    248         "Invalid latitude `%g'. Must be in [-90, 90] degrees.\n",
    249         tmp[0]);
    250     res = RES_BAD_ARG;
    251     goto error;
    252   }
    253   if(tmp[1] < -180 || tmp[1] > 180) {
    254     fprintf(stderr,
    255         "Invalid longitude `%g'. Must be in [-180, 180] degrees.\n",
    256         tmp[1]);
    257     res = RES_BAD_ARG;
    258     goto error;
    259   }
    260 
    261   d2_set(current_loc, tmp);
    262 exit:
    263   return res;
    264 error:
    265   goto exit;
    266 }
    267 
    268 static res_T
    269 parse_time
    270   (const char* str,
    271    const double current_loc[2],
    272    struct solstice_args* args)
    273 {
    274   res_T res = RES_OK;
    275   struct solstice_args_sun_dir sun_dir;
    276   char* p;
    277   ASSERT(str && current_loc && args);
    278 
    279   p = strptime(str, "%Y-%m-%dT%H:%M:%S", &sun_dir.location_time.time);
    280   if(p == NULL || *p != '\0') {
    281     res = RES_BAD_ARG;
    282     fprintf(stderr, "Invalid time `%s'.\n", str);
    283     goto error;
    284   }
    285 
    286   sun_dir.type = SOLSTICE_ARGS_LOCATION_TIME;
    287   sun_dir.location_time.latitude = current_loc[0];
    288   sun_dir.location_time.longitude = current_loc[1];
    289   sa_push(args->sun_dirs, sun_dir);
    290   args->nsun_dirs++;
    291 
    292 exit:
    293   return res;
    294 error:
    295   if(args->sun_dirs) {
    296     sa_release(args->sun_dirs);
    297     args->sun_dirs = NULL;
    298     args->nsun_dirs = 0;
    299   }
    300   goto exit;
    301 }
    302 
    303 static res_T
    304 parse_image_definition
    305   (const char* str,
    306    unsigned long* width,
    307    unsigned long* height)
    308 {
    309   char buf[64];
    310   char* tk;
    311   char* ctx;
    312   res_T res = RES_OK;
    313   ASSERT(str && width && height);
    314 
    315   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    316     fprintf(stderr,
    317       "Could not duplicate the image definition string `%s'.\n", str);
    318     return RES_MEM_ERR;
    319   }
    320   strncpy(buf, str, sizeof(buf));
    321 
    322   tk = strtok_r(buf, "x", &ctx);
    323   res = cstr_to_ulong(tk, width);
    324   if(res == RES_OK && !*width) res = RES_BAD_ARG;
    325   if(res != RES_OK) {
    326     fprintf(stderr, "Invalid image width `%s'\n", tk);
    327     return res;
    328   }
    329 
    330   tk = strtok_r(NULL, "", &ctx);
    331   res = cstr_to_ulong(tk, height);
    332   if(res == RES_OK && !*height) res = RES_BAD_ARG;
    333   if(res != RES_OK) {
    334     fprintf(stderr, "Invalid image height `%s'\n", tk);
    335     return res;
    336   }
    337 
    338   return res;
    339 }
    340 
    341 static res_T
    342 parse_render_mode(const char* str, enum solstice_args_render_mode* mode)
    343 {
    344   res_T res = RES_OK;
    345   ASSERT(str && mode);
    346 
    347   if(!strcmp(str, "draft")) {
    348     *mode = SOLSTICE_ARGS_RENDER_DRAFT;
    349   } else if(!strcmp(str, "pt")) {
    350     *mode = SOLSTICE_ARGS_RENDER_PATH_TRACING;
    351   } else {
    352     fprintf(stderr, "Invalid render mode `%s'.\n", str);
    353     res = RES_BAD_ARG;
    354     goto error;
    355   }
    356 exit:
    357   return res;
    358 error:
    359   goto exit;
    360 }
    361 
    362 static res_T
    363 parse_rendering_option(const char* str, struct solstice_args* args)
    364 {
    365   char buf[128];
    366   char* key;
    367   char* val;
    368   char* ctx;
    369   size_t len;
    370   res_T res = RES_OK;
    371   ASSERT(str && args);
    372 
    373   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    374     fprintf(stderr,
    375       "Could not duplicate the rendering option string `%s'\n", str);
    376     res = RES_MEM_ERR;
    377     goto error;
    378   }
    379   strncpy(buf, str, sizeof(buf));
    380 
    381   key = strtok_r(buf, "=", &ctx);
    382   val = strtok_r(NULL, "", &ctx);
    383 
    384   if(!val) {
    385     fprintf(stderr, "Missing a value to the rendering option `%s'.\n", key);
    386     res = RES_BAD_ARG;
    387     goto error;
    388   }
    389 
    390   if(!strcmp(key, "fov")) { /* Camera horizontal field of view in degrees */
    391     res = parse_fov(val, &args->camera.fov_x);
    392     if(res != RES_OK) goto error;
    393   } else if(!strcmp(key, "img")) { /* Image definition */
    394     res = parse_image_definition(val, &args->img.width, &args->img.height);
    395     if(res != RES_OK) goto error;
    396   } else if(!strcmp(key, "pos")) { /* Camera position */
    397     res = cstr_to_list_double(val, ',', args->camera.pos, &len, 3);
    398     if(res == RES_OK && len != 3) res = RES_BAD_ARG;
    399     if(res != RES_OK ) {
    400       fprintf(stderr, "Invalid camera position `%s'.\n", val);
    401       goto error;
    402     }
    403     args->camera.auto_look_at = 0; /* Disable auto look at */
    404   } else if(!strcmp(key, "rmode")) { /* Render mode */
    405     res = parse_render_mode(val, &args->render_mode);
    406     if(res != RES_OK) goto error;
    407   } else if(!strcmp(key, "spp")) { /*# Samples per pixel */
    408     res = cstr_to_uint(val, &args->img.spp);
    409     if(res == RES_OK && !args->img.spp) res = RES_BAD_ARG;
    410     if(res != RES_OK) {
    411       fprintf(stderr, "Invalid number of samples per pixel `%s'.\n", val);
    412       goto error;
    413     }
    414   } else if(!strcmp(key, "tgt")) { /* Camera target */
    415     res = cstr_to_list_double(val, ',', args->camera.tgt, &len, 3);
    416     if(res == RES_OK && len != 3) res = RES_BAD_ARG;
    417     if(res != RES_OK) {
    418       fprintf(stderr, "Invalid camera target `%s'.\n", val);
    419       goto error;
    420     }
    421     args->camera.auto_look_at = 0; /* Disable auto look at */
    422   } else if(!strcmp(key, "up")) { /* Camera up vector */
    423     res = cstr_to_list_double(val, ',', args->camera.up, &len, 3);
    424     if(res == RES_OK && len != 3) res = RES_BAD_ARG;
    425     if(res != RES_OK) {
    426       fprintf(stderr, "Invalid camera up vector `%s'.\n", val);
    427       goto error;
    428     }
    429   } else {
    430     fprintf(stderr, "Invalid rendering option `%s'.\n", key);
    431     res = RES_BAD_ARG;
    432     goto error;
    433   }
    434 
    435 exit:
    436   return res;
    437 error:
    438   goto exit;
    439 }
    440 
    441 static res_T
    442 parse_dump_format(const char* str, enum solstice_args_dump_format* fmt)
    443 {
    444   res_T res = RES_OK;
    445   ASSERT(str && fmt);
    446 
    447   if(!strcmp(str, "obj")) {
    448     *fmt = SOLSTICE_ARGS_DUMP_OBJ;
    449   } else {
    450     fprintf(stderr, "Invalid dump format `%s'.\n", str);
    451     res = RES_BAD_ARG;
    452     goto error;
    453   }
    454 
    455 exit:
    456   return res;
    457 error:
    458   goto exit;
    459 }
    460 
    461 static res_T
    462 parse_dump_split_mode(const char* str, enum solstice_args_dump_split_mode* mode)
    463 {
    464   res_T res = RES_OK;
    465   ASSERT(str && mode);
    466 
    467   if(!strcmp(str, "geometry")) {
    468     *mode = SOLSTICE_ARGS_DUMP_SPLIT_GEOMETRY;
    469   } else if(!strcmp(str, "none")) {
    470     *mode = SOLSTICE_ARGS_DUMP_SPLIT_NONE;
    471   } else if(!strcmp(str, "object")) {
    472     *mode = SOLSTICE_ARGS_DUMP_SPLIT_OBJECT;
    473   } else {
    474     fprintf(stderr, "Invalid dump split mode `%s'.\n", str);
    475     res = RES_BAD_ARG;
    476     goto error;
    477   }
    478 exit:
    479   return res;
    480 error:
    481   goto exit;
    482 }
    483 
    484 static res_T
    485 parse_dump_option(const char* str, struct solstice_args* args)
    486 {
    487   char buf[128];
    488   char* key;
    489   char* val;
    490   char* ctx;
    491   res_T res = RES_OK;
    492   ASSERT(str && args);
    493 
    494   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    495     fprintf(stderr,
    496       "Could not duplicate the dump geometry option string `%s'\n", str);
    497     res = RES_MEM_ERR;
    498     goto error;
    499   }
    500   strncpy(buf, str, sizeof(buf));
    501 
    502   key = strtok_r(buf, "=", &ctx);
    503   val = strtok_r(NULL, "", &ctx);
    504 
    505   if(!val) {
    506     fprintf(stderr, "Missing a value to the dump option `%s'.\n", key);
    507     res = RES_BAD_ARG;
    508     goto error;
    509   }
    510 
    511   if(!strcmp(key, "format")) {
    512     res = parse_dump_format(val, &args->dump_format);
    513   } else if(!strcmp(key, "split")) {
    514     res = parse_dump_split_mode(val, &args->dump_split_mode);
    515   } else {
    516     fprintf(stderr, "Invalid dump option `%s'.\n", val);
    517     res = RES_BAD_ARG;
    518     goto error;
    519   }
    520   if(res != RES_OK) goto error;
    521 
    522 exit:
    523   return res;
    524 error:
    525   goto exit;
    526 }
    527 
    528 static res_T
    529 parse_dump_paths_option(const char* str, struct solstice_args* args)
    530 {
    531   char buf[128];
    532   char* key;
    533   char* val;
    534   char* ctx;
    535   res_T res = RES_OK;
    536   ASSERT(str && args);
    537 
    538   if(!strcmp(str, "default")) {
    539     args->infinite_ray_length = SOLSTICE_ARGS_DEFAULT.infinite_ray_length;
    540     args->sun_ray_length = SOLSTICE_ARGS_DEFAULT.sun_ray_length;
    541     goto exit;
    542   }
    543 
    544   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    545     fprintf(stderr,
    546 "Could not duplicate the dump radiative paths option string `%s'.\n", str);
    547     res = RES_MEM_ERR;
    548     goto error;
    549   }
    550 
    551   strncpy(buf, str, sizeof(buf));
    552 
    553   key = strtok_r(buf, "=", &ctx);
    554   val = strtok_r(NULL, "", &ctx);
    555 
    556   if(!val) {
    557     fprintf(stderr,
    558       "Missing a value to the dump radiative paths option `%s'.\n", key);
    559     res = RES_BAD_ARG;
    560     goto error;
    561   }
    562 
    563   if(!strcmp(key, "irlen")) {
    564     res = cstr_to_double(val, &args->infinite_ray_length);
    565     if(res != RES_OK) {
    566       fprintf(stderr, "Invalid infinite ray length `%s'.\n", val);
    567       goto error;
    568     }
    569   } else if(!strcmp(key, "srlen")) {
    570     res = cstr_to_double(val, &args->sun_ray_length);
    571     if(res != RES_OK) {
    572       fprintf(stderr, "Invalid sun ray length `%s'.\n", val);
    573       goto error;
    574     }
    575   } else {
    576     fprintf(stderr, "Invalid dump radiative paths option `%s'.\n", val);
    577     res = RES_BAD_ARG;
    578     goto error;
    579   }
    580   if(res != RES_OK) goto error;
    581 
    582 exit:
    583   return res;
    584 error:
    585   goto exit;
    586 }
    587 
    588 static res_T
    589 parse_rng_option(const char* str, struct solstice_args* args)
    590 {
    591   char buf[128];
    592   char* key;
    593   char* val;
    594   char* ctx;
    595   size_t len;
    596   res_T res = RES_OK;
    597   ASSERT(str && args);
    598 
    599   if(strlen(str) >= sizeof(buf)-1/*NULL char*/) {
    600     fprintf(stderr,
    601       "Could not duplicate the RNG option string `%s'\n", str);
    602     res = RES_MEM_ERR;
    603     goto error;
    604   }
    605   strncpy(buf, str, sizeof(buf));
    606 
    607   key = strtok_r(buf, "=", &ctx);
    608   val = strtok_r(NULL, "", &ctx);
    609 
    610   if(!val) {
    611     fprintf(stderr, "Missing a value to the RNG option `%s'.\n", key);
    612     res = RES_BAD_ARG;
    613     goto error;
    614   }
    615 
    616   if(!strcmp(key, "istate")) { /* Input state */
    617     len = strlen(val);
    618     args->rng_state_input_filename = mem_calloc(len+1, sizeof(char));
    619     if(!args->rng_state_input_filename) { res = RES_MEM_ERR; goto error; }
    620     strcpy(args->rng_state_input_filename, val);
    621   } else if(!strcmp(key, "ostate")) { /* Output state */
    622     len = strlen(val);
    623     args->rng_state_output_filename = mem_calloc(len+1, sizeof(char));
    624     if(!args->rng_state_output_filename) { res = RES_MEM_ERR; goto error; }
    625     strcpy(args->rng_state_output_filename, val);
    626   } else {
    627     fprintf(stderr, "Invalid RNG option `%s'.\n", key);
    628     res = RES_BAD_ARG;
    629     goto error;
    630   }
    631 
    632 exit:
    633   return res;
    634 error:
    635   goto exit;
    636 }
    637 
    638 /*******************************************************************************
    639  * Local function
    640  ******************************************************************************/
    641 res_T
    642 solstice_args_init(struct solstice_args* args, const int argc, char** argv)
    643 {
    644   int opt;
    645   int i, L_defined = 0;
    646   double current_loc[2];
    647   res_T res = RES_OK;
    648   ASSERT(args && argc && argv);
    649 
    650   *args = SOLSTICE_ARGS_DEFAULT;
    651 
    652   FOR_EACH(i, 1, argc) {
    653     if(!strcmp(argv[i], "--version")) {
    654       printf("Solstice %d.%d.%d\n",
    655         SOLSTICE_VERSION_MAJOR,
    656         SOLSTICE_VERSION_MINOR,
    657         SOLSTICE_VERSION_PATCH);
    658       args->quit = 1;
    659       goto exit;
    660     }
    661   }
    662 
    663   optind = 0;
    664   while((opt = getopt(argc, argv, "A:D:fG:g:hI:n:L:o:p:qR:r:T:t:v")) != -1) {
    665     switch(opt) {
    666       case 'A': /* 1 sun direction */
    667         res = parse_sun_algorithm(optarg, args);
    668         break;
    669       case 'D': /* 1 sun direction */
    670         res = parse_sun_spherical(optarg, args);
    671         break;
    672       case 'f': args->force_overwriting = 1; break;
    673       case 'G': /* Setup the random number generator */
    674         res = parse_multiple_options(optarg, args, parse_rng_option);
    675         break;
    676       case 'g': /* Switch in dump geometry mode and configure it */
    677         res = parse_multiple_options(optarg, args, parse_dump_option);
    678         if(res == RES_OK && args->dump_format == SOLSTICE_ARGS_DUMP_NONE) {
    679           fprintf(stderr, "%s: missing a dump format -- `%s'.\n",
    680             argv[0], optarg);
    681           res = RES_BAD_ARG;
    682           goto error;
    683         }
    684         break;
    685       case 'h': /* Print short help and exit */
    686         print_help(argv[0]);
    687         solstice_args_release(args);
    688         args->quit = 1;
    689         goto exit;
    690       case 'I': /* DNI */
    691         res = cstr_to_double(optarg, &args->dni);
    692         if(res == RES_OK && args->dni <= 0) {
    693           fprintf(stderr, "%s: invalid DNI value -- `%s'.\n",
    694             argv[0], optarg);
    695           res = RES_BAD_ARG;
    696           goto error;
    697         }
    698         break;
    699       case 'L': /* Plant location */
    700         L_defined = 1;
    701         res = parse_location(optarg, current_loc);
    702         break;
    703       case 'n': /* Define the number of MC samples */
    704         res = cstr_to_ulong(optarg, &args->nexperiments);
    705         if(res == RES_OK && !args->nexperiments) res = RES_BAD_ARG;
    706         break;
    707       case 'o': args->output_filename = optarg; break;
    708       case 'p': /* Switch in dump radiative paths mode and configure it */
    709         args->dump_paths = 1;
    710         res = parse_multiple_options(optarg, args, parse_dump_paths_option);
    711         break;
    712       case 'q': args->quiet = 1; break;
    713       case 'R': args->receivers_filename = optarg; break;
    714       case 'r':  /* Switch in rendering mode and configure it */
    715         args->rendering = 1;
    716         res = parse_multiple_options(optarg, args, parse_rendering_option);
    717         break;
    718       case 'T': /* 1 time */
    719         if(!L_defined) {
    720           fprintf(stderr,
    721               "%s: cannot use the -%c option with no location defined '%s'\n",
    722               argv[0], opt, optarg);
    723           res = RES_BAD_ARG;
    724           goto error;
    725           break;
    726         }
    727         res = parse_time(optarg, current_loc, args);
    728         break;
    729       case 't': /* Submit an hint on the number of threads to use */
    730         res = cstr_to_uint(optarg, &args->nthreads);
    731         if(res == RES_OK && !args->nthreads) res = RES_BAD_ARG;
    732         break;
    733       case 'v': args->verbose = 1; break;
    734       default: res = RES_BAD_ARG; break;
    735     }
    736     if(res != RES_OK) {
    737       if(optarg) {
    738         fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n",
    739             argv[0], optarg, opt);
    740       }
    741       goto error;
    742     }
    743   }
    744 
    745   if(!args->rendering
    746       && args->dump_format == SOLSTICE_ARGS_DUMP_NONE
    747       && args->nsun_dirs == 0)
    748   {
    749     fprintf(stderr, "No computation request.\n");
    750     res = RES_BAD_ARG;
    751     goto error;
    752   }
    753 
    754   if(args->dump_format != SOLSTICE_ARGS_DUMP_NONE && args->rendering) {
    755     fprintf(stderr, "The '-g' and '-r' options are exclusives.\n");
    756     res = RES_BAD_ARG;
    757     goto error;
    758   }
    759 
    760   if(args->dump_format != SOLSTICE_ARGS_DUMP_NONE && args->dump_paths) {
    761     fprintf(stderr, "The '-g' and '-p' options are exclusives.\n");
    762     res = RES_BAD_ARG;
    763     goto error;
    764   }
    765 
    766   if(args->dump_paths && args->rendering) {
    767     fprintf(stderr, "The '-p' and '-r' options are exclusives.\n");
    768     res = RES_BAD_ARG;
    769     goto error;
    770   }
    771 
    772   if(optind < argc) {
    773     args->input_filename = argv[optind];
    774   }
    775 
    776 exit:
    777   optind = 1;
    778   return res;
    779 error:
    780   solstice_args_release(args);
    781   goto exit;
    782 }
    783 
    784 void
    785 solstice_args_release(struct solstice_args* args)
    786 {
    787   ASSERT(args);
    788   sa_release(args->sun_dirs);
    789   if(args->rng_state_input_filename) mem_rm(args->rng_state_input_filename);
    790   if(args->rng_state_output_filename) mem_rm(args->rng_state_output_filename);
    791   *args = SOLSTICE_ARGS_NULL;
    792 }
    793