htrdr

Solving radiative transfer in heterogeneous media
git clone git://git.meso-star.fr/htrdr.git
Log | Files | Refs | README | LICENSE

htrdr_planets_args.c (27055B)


      1 /* Copyright (C) 2018-2019, 2022-2025 Centre National de la Recherche Scientifique
      2  * Copyright (C) 2020-2022 Institut Mines Télécom Albi-Carmaux
      3  * Copyright (C) 2022-2025 Institut Pierre-Simon Laplace
      4  * Copyright (C) 2022-2025 Institut de Physique du Globe de Paris
      5  * Copyright (C) 2018-2025 |Méso|Star> (contact@meso-star.com)
      6  * Copyright (C) 2022-2025 Observatoire de Paris
      7  * Copyright (C) 2022-2025 Université de Reims Champagne-Ardenne
      8  * Copyright (C) 2022-2025 Université de Versaille Saint-Quentin
      9  * Copyright (C) 2018-2019, 2022-2025 Université Paul Sabatier
     10  *
     11  * This program is free software: you can redistribute it and/or modify
     12  * it under the terms of the GNU General Public License as published by
     13  * the Free Software Foundation, either version 3 of the License, or
     14  * (at your option) any later version.
     15  *
     16  * This program is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     19  * GNU General Public License for more details.
     20  *
     21  * You should have received a copy of the GNU General Public License
     22  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     23 
     24 #define _POSIX_C_SOURCE 200112L /* strtok_r support */
     25 
     26 #include "planets/htrdr_planets_args.h"
     27 
     28 #include <rsys/cstr.h>
     29 #include <rsys/stretchy_array.h>
     30 #include <rsys/mem_allocator.h>
     31 
     32 #include <getopt.h>
     33 #include <string.h>
     34 
     35 /*******************************************************************************
     36  * Helper functions
     37  ******************************************************************************/
     38 static INLINE res_T
     39 check_gas_args(const struct rnatm_gas_args* args)
     40 {
     41   if(!args) return RES_BAD_ARG;
     42 
     43   /* Filenames cannot be NULL */
     44   if(!args->smsh_filename
     45   || !args->sck_filename
     46   || !args->temperatures_filename)
     47     return RES_BAD_ARG;
     48 
     49   return RES_OK;
     50 }
     51 
     52 static INLINE res_T
     53 check_aerosol_args(const struct rnatm_aerosol_args* args)
     54 {
     55   if(!args) return RES_BAD_ARG;
     56 
     57   /* Filenames cannot be NULL */
     58   if(!args->smsh_filename
     59   || !args->sars_filename
     60   || !args->phase_fn_ids_filename
     61   || !args->phase_fn_lst_filename)
     62     return RES_BAD_ARG;
     63 
     64   return RES_OK;
     65 }
     66 
     67 static INLINE res_T
     68 check_ground_args(const struct htrdr_planets_ground_args* args)
     69 {
     70   if(!args) return RES_BAD_ARG;
     71 
     72   /* Filenames cannot be NULL */
     73   if(!args->smsh_filename
     74   || !args->props_filename
     75   || !args->mtllst_filename)
     76     return RES_BAD_ARG;
     77 
     78   return RES_OK;
     79 }
     80 
     81 static INLINE res_T
     82 check_spectral_args(const struct htrdr_planets_spectral_args* args)
     83 {
     84   if(!args) return RES_BAD_ARG;
     85 
     86   /* Invalid type */
     87   switch(args->type) {
     88     case HTRDR_SPECTRAL_LW:
     89     case HTRDR_SPECTRAL_SW:
     90     case HTRDR_SPECTRAL_SW_CIE_XYZ:
     91       /* Nothing to be done */
     92       break;
     93     default:
     94       return RES_BAD_ARG;
     95   }
     96 
     97   /* Invalid spectral range */
     98   if(args->wlen_range[0] < 0
     99   || args->wlen_range[1] < 0
    100   || args->wlen_range[0] > args->wlen_range[1])
    101     return RES_BAD_ARG;
    102 
    103   return RES_OK;
    104 }
    105 
    106 static INLINE res_T
    107 check_volrad_budget_args(const struct htrdr_planets_volrad_budget_args* args)
    108 {
    109   if(!args) return RES_BAD_ARG;
    110 
    111   /* Filename could not be NULL */
    112   if(!args->smsh_filename) return RES_BAD_ARG;
    113 
    114   /* Samples per tetrahedron could not be zero */
    115   if(!args->spt) return RES_BAD_ARG;
    116 
    117   return RES_OK;
    118 }
    119 
    120 static INLINE res_T
    121 check_accel_struct_build_args
    122   (const struct htrdr_planets_accel_struct_build_args* args)
    123 {
    124   if(!args) return RES_BAD_ARG;
    125 
    126   /* Definition and number of threads cannot be null */
    127   if(!args->definition_hint || !args->nthreads) return RES_BAD_ARG;
    128 
    129   /* Invalid threshold */
    130   if(args->optical_thickness < 0) return RES_BAD_ARG;
    131 
    132   return RES_OK;
    133 }
    134 
    135 static void
    136 usage(void)
    137 {
    138   printf("usage: htrdr-planets [-dfhNv] [-a aerosol_opt[:aerosol_opt ...]]\n");
    139   printf("                     [-b accel_struct_build_opt[:accel_struct_build_opt ...]]\n");
    140   printf("                     [-C persp_camera_opt[:persp_camera_opt ...]]\n");
    141   printf("                     [-G ground_opt[:ground_opt ...]]\n");
    142   printf("                     [-i image_opt[:image_opt ...]] [-o output]\n");
    143   printf("                     [-P ortho_camera_opt[:ortho_camera_opt ...]]\n");
    144   printf("                     [-r volrad_budget_opt[:volrad_budget_opt ...]]\n");
    145   printf("                     [-S source_opt[:source_opt ...]]\n");
    146   printf("                     [-s spectral_opt[:spectral_opt ...]] [-t threads_count]\n");
    147   printf("                     -g gas_opt[:gas_opt ...]\n");
    148 }
    149 
    150 static INLINE char*
    151 str_dup(const char* str)
    152 {
    153   size_t len = 0;
    154   char* dup = NULL;
    155   ASSERT(str);
    156   len = strlen(str) + 1/*NULL char*/;
    157   dup = mem_alloc(len);
    158   if(!dup) {
    159     return NULL;
    160   } else {
    161     return memcpy(dup, str, len);
    162   }
    163 }
    164 
    165 static res_T
    166 parse_aerosol_parameters(const char* str, void* ptr)
    167 {
    168   enum { MESH, NAME, RADPROP, PHASEFN, PHASEIDS } iparam;
    169   struct rnatm_aerosol_args* aerosol = NULL;
    170   char buf[BUFSIZ];
    171   struct htrdr_planets_args* args = ptr;
    172   char* key;
    173   char* val;
    174   char* tk_ctx;
    175   res_T res = RES_OK;
    176   ASSERT(args && str);
    177 
    178   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    179     fprintf(stderr, "Could not duplicate the aerosol parameter `%s'\n", str);
    180     res = RES_MEM_ERR;
    181     goto error;
    182   }
    183   strncpy(buf, str, sizeof(buf));
    184 
    185   key = strtok_r(buf, "=", &tk_ctx);
    186   val = strtok_r(NULL, "", &tk_ctx);
    187 
    188        if(!strcmp(key, "mesh")) iparam = MESH;
    189   else if(!strcmp(key, "name")) iparam = NAME;
    190   else if(!strcmp(key, "radprop")) iparam = RADPROP;
    191   else if(!strcmp(key, "phasefn")) iparam = PHASEFN;
    192   else if(!strcmp(key, "phaseids")) iparam = PHASEIDS;
    193   else {
    194     fprintf(stderr, "Invalid aerosol parameter `%s'\n", key);
    195     res = RES_BAD_ARG;
    196     goto error;
    197   }
    198 
    199   if(!val) {
    200     fprintf(stderr, "Invalid null value for aerosol parameter `%s'\n", key);
    201     res = RES_BAD_ARG;
    202     goto error;
    203   }
    204 
    205   ASSERT(args->naerosols);
    206   aerosol = args->aerosols + (args->naerosols - 1);
    207 
    208   #define SET_STR(Dst) {                                                       \
    209     if(Dst) mem_rm(Dst);                                                       \
    210     if(!((Dst) = str_dup(val))) res = RES_MEM_ERR;                             \
    211   } (void)0
    212   switch(iparam) {
    213     case MESH: SET_STR(aerosol->smsh_filename); break;
    214     case NAME: SET_STR(aerosol->name); break;
    215     case RADPROP: SET_STR(aerosol->sars_filename); break;
    216     case PHASEFN: SET_STR(aerosol->phase_fn_lst_filename); break;
    217     case PHASEIDS: SET_STR(aerosol->phase_fn_ids_filename); break;
    218     default: FATAL("Unreachable code\n"); break;
    219   }
    220   #undef SET_STR
    221   if(res != RES_OK) {
    222     fprintf(stderr, "Unable to parse the aerosol parameter `%s' -- %s\n",
    223       str, res_to_cstr(res));
    224     goto error;
    225   }
    226 
    227 exit:
    228   return res;
    229 error:
    230   goto exit;
    231 }
    232 
    233 static res_T
    234 parse_ground_parameters(const char* str, void* ptr)
    235 {
    236   enum { BRDF, MESH, NAME, PROP } iparam;
    237   char buf[BUFSIZ];
    238   struct htrdr_planets_args* args = ptr;
    239   char* key;
    240   char* val;
    241   char* tk_ctx;
    242   res_T res = RES_OK;
    243   ASSERT(args && str);
    244 
    245   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    246     fprintf(stderr, "Could not duplicate the ground parameter `%s'\n", str);
    247     res = RES_MEM_ERR;
    248     goto error;
    249   }
    250   strncpy(buf, str, sizeof(buf));
    251 
    252   key = strtok_r(buf, "=", &tk_ctx);
    253   val = strtok_r(NULL, "", &tk_ctx);
    254 
    255        if(!strcmp(key, "brdf")) iparam = BRDF;
    256   else if(!strcmp(key, "mesh")) iparam = MESH;
    257   else if(!strcmp(key, "name")) iparam = NAME;
    258   else if(!strcmp(key, "prop")) iparam = PROP;
    259   else {
    260     fprintf(stderr, "Invalid ground parameter `%s'\n", key);
    261     res = RES_BAD_ARG;
    262     goto error;
    263   }
    264 
    265   if(!val) {
    266     fprintf(stderr, "Invalid null value for ground parameter `%s'\n", key);
    267     res = RES_BAD_ARG;
    268     goto error;
    269   }
    270 
    271   #define SET_STR(Dst) {                                                       \
    272     if(Dst) mem_rm(Dst);                                                       \
    273     if(!((Dst) = str_dup(val))) res = RES_MEM_ERR;                             \
    274   } (void)0
    275   switch(iparam) {
    276     case BRDF: SET_STR(args->ground.mtllst_filename); break;
    277     case MESH: SET_STR(args->ground.smsh_filename); break;
    278     case NAME: SET_STR(args->ground.name); break;
    279     case PROP: SET_STR(args->ground.props_filename); break;
    280     default: FATAL("Unreachable code\n"); break;
    281   }
    282   #undef SET_STR
    283   if(res != RES_OK) {
    284     fprintf(stderr, "Unable to parse the ground parameter `%s' -- %s\n",
    285       str, res_to_cstr(res));
    286     goto error;
    287   }
    288 
    289 exit:
    290   return res;
    291 error:
    292   goto exit;
    293 }
    294 
    295 static res_T
    296 parse_gas_parameters(const char* str, void* ptr)
    297 {
    298   enum { MESH, CK, TEMP } iparam;
    299   char buf[BUFSIZ];
    300   struct htrdr_planets_args* args = ptr;
    301   char* key;
    302   char* val;
    303   char* tk_ctx;
    304   res_T res = RES_OK;
    305   ASSERT(args && str);
    306 
    307   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    308     fprintf(stderr, "Could not duplicate the gas parameter `%s'\n", str);
    309     res = RES_MEM_ERR;
    310     goto error;
    311   }
    312   strncpy(buf, str, sizeof(buf));
    313 
    314   key = strtok_r(buf, "=", &tk_ctx);
    315   val = strtok_r(NULL, "", &tk_ctx);
    316 
    317        if(!strcmp(key, "mesh")) iparam = MESH;
    318   else if(!strcmp(key, "ck")) iparam = CK;
    319   else if(!strcmp(key, "temp")) iparam = TEMP;
    320   else {
    321     fprintf(stderr, "Invalid gas parameter `%s'\n", key);
    322     res = RES_BAD_ARG;
    323     goto error;
    324   }
    325 
    326   if(!val) {
    327     fprintf(stderr, "Invalid null value for gas parameter `%s'\n", key);
    328     res = RES_BAD_ARG;
    329     goto error;
    330   }
    331 
    332   #define SET_STR(Dst) {                                                       \
    333     if(Dst) mem_rm(Dst);                                                       \
    334     if(!((Dst) = str_dup(val))) res = RES_MEM_ERR;                             \
    335   } (void)0
    336   switch(iparam) {
    337     case MESH: SET_STR(args->gas.smsh_filename); break;
    338     case CK: SET_STR(args->gas.sck_filename); break;
    339     case TEMP: SET_STR(args->gas.temperatures_filename); break;
    340     default: FATAL("Unreachable code\n"); break;
    341   }
    342   #undef SET_STR
    343   if(res != RES_OK) {
    344     fprintf(stderr, "Unable to parse the gas parameter `%s' -- %s\n",
    345       str, res_to_cstr(res));
    346     goto error;
    347   }
    348 
    349 exit:
    350   return res;
    351 error:
    352   goto exit;
    353 }
    354 
    355 static res_T
    356 parse_source_parameters(const char* str, void* ptr)
    357 {
    358   enum {LAT, LON, DST, RADIUS, TEMP, RAD} iparam;
    359   char buf[BUFSIZ];
    360   struct htrdr_planets_args* args = ptr;
    361   struct htrdr_planets_source_args* src = NULL;
    362   char* key;
    363   char* val;
    364   char* tk_ctx;
    365   res_T res = RES_OK;
    366   ASSERT(str && ptr);
    367 
    368   src = &args->source;
    369 
    370   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    371     fprintf(stderr, "Could not duplicate the source parameter `%s'\n", str);
    372     res = RES_MEM_ERR;
    373     goto error;
    374   }
    375   strncpy(buf, str, sizeof(buf));
    376 
    377   key = strtok_r(buf, "=", &tk_ctx);
    378   val = strtok_r(NULL, "", &tk_ctx);
    379 
    380        if(!strcmp(key, "lat")) iparam = LAT;
    381   else if(!strcmp(key, "lon")) iparam = LON;
    382   else if(!strcmp(key, "dst")) iparam = DST;
    383   else if(!strcmp(key, "rad")) iparam = RAD;
    384   else if(!strcmp(key, "radius")) iparam = RADIUS;
    385   else if(!strcmp(key, "temp")) iparam = TEMP;
    386   else {
    387     fprintf(stderr, "Invalid source parameter `%s'\n", key);
    388     res = RES_BAD_ARG;
    389     goto error;
    390   }
    391 
    392   if(!val) {
    393     fprintf(stderr, "Invalid null value for the source parameter`%s'\n", key);
    394     res = RES_BAD_ARG;
    395     goto error;
    396   }
    397 
    398   switch(iparam) {
    399     case LAT:
    400       res = cstr_to_double(val, &src->latitude);
    401       if(res == RES_OK && (src->latitude < -90 || src->latitude > 90)) {
    402         res = RES_BAD_ARG;
    403       }
    404       break;
    405     case LON:
    406       res = cstr_to_double(val, &src->longitude);
    407       if(res == RES_OK && (src->longitude < -180 || src->longitude > 180)) {
    408         res = RES_BAD_ARG;
    409       }
    410       break;
    411     case DST:
    412       res = cstr_to_double(val, &src->distance);
    413       if(res == RES_OK && src->distance < 0) res = RES_BAD_ARG;
    414       break;
    415     case RAD:
    416       /* Use a per wavelength radiance rather than a constant temperature */
    417       src->temperature = -1;
    418       if(src->rnrl_filename) mem_rm(src->rnrl_filename);
    419       src->rnrl_filename = str_dup(val);
    420       if(!src->rnrl_filename) res = RES_MEM_ERR;
    421       break;
    422     case RADIUS:
    423       res = cstr_to_double(val, &src->radius);
    424       if(res == RES_OK && src->radius < 0) res = RES_BAD_ARG;
    425       break;
    426     case TEMP:
    427       /* Use a constant temperature rather than a per wavelength radiance */
    428       if(src->rnrl_filename) {
    429         mem_rm(src->rnrl_filename);
    430         src->rnrl_filename = NULL;
    431       }
    432       res = cstr_to_double(val, &src->temperature);
    433       if(res == RES_OK && src->temperature < 0) res = RES_BAD_ARG;
    434       break;
    435     default: FATAL("Unreachable code\n"); break;
    436   }
    437   if(res != RES_OK) {
    438     fprintf(stderr, "Unable to parse the source parameter `%s' -- %s\n",
    439       str, res_to_cstr(res));
    440     goto error;
    441   }
    442 
    443 exit:
    444   return res;
    445 error:
    446   goto exit;
    447 }
    448 
    449 static INLINE res_T
    450 parse_spectral_range(const char* str, double wlen_range[2])
    451 {
    452   double range[2];
    453   size_t len;
    454   res_T res = RES_OK;
    455   ASSERT(wlen_range && str);
    456 
    457   res = cstr_to_list_double(str, ',', range, &len, 2);
    458   if(res == RES_OK && len != 2) res = RES_BAD_ARG;
    459   if(res == RES_OK && range[0] > range[1]) res = RES_BAD_ARG;
    460   if(res == RES_OK && (range[0] < 0 || range[1] < 0)) res = RES_BAD_ARG;
    461   if(res != RES_OK) goto error;
    462 
    463   wlen_range[0] = range[0];
    464   wlen_range[1] = range[1];
    465 
    466 exit:
    467   return res;
    468 error:
    469   goto exit;
    470 }
    471 
    472 static res_T
    473 parse_spectral_parameters(const char* str, void* ptr)
    474 {
    475   enum {CIE_XYZ, LW, SW} iparam;
    476   char buf[BUFSIZ];
    477   struct htrdr_planets_args* args = ptr;
    478   struct htrdr_planets_spectral_args* spectral = NULL;
    479   char* key;
    480   char* val;
    481   char* tk_ctx;
    482   res_T res = RES_OK;
    483   ASSERT(str && ptr);
    484 
    485   spectral = &args->spectral_domain;
    486 
    487   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    488     fprintf(stderr, "Could not duplicate the spectral parameter `%s'\n", str);
    489     res = RES_MEM_ERR;
    490     goto error;
    491   }
    492   strncpy(buf, str, sizeof(buf));
    493 
    494   key = strtok_r(buf, "=", &tk_ctx);
    495   val = strtok_r(NULL, "", &tk_ctx);
    496 
    497        if(!strcmp(key, "cie_xyz")) iparam = CIE_XYZ;
    498   else if(!strcmp(key, "lw")) iparam = LW;
    499   else if(!strcmp(key, "sw")) iparam = SW;
    500   else {
    501     fprintf(stderr, "Invalid spectral parameter `%s'\n", key);
    502     res = RES_BAD_ARG;
    503     goto error;
    504   }
    505 
    506   if((iparam == LW || iparam == SW) && !val) {
    507     fprintf(stderr,
    508       "Invalid null value for the spectral parameter `%s'\n", key);
    509     res = RES_BAD_ARG;
    510     goto error;
    511   }
    512 
    513   switch(iparam) {
    514     case CIE_XYZ:
    515       spectral->type = HTRDR_SPECTRAL_SW_CIE_XYZ;
    516       spectral->wlen_range[0] = HTRDR_RAN_WLEN_CIE_XYZ_RANGE_DEFAULT[0];
    517       spectral->wlen_range[1] = HTRDR_RAN_WLEN_CIE_XYZ_RANGE_DEFAULT[1];
    518       break;
    519     case LW:
    520       spectral->type = HTRDR_SPECTRAL_LW;
    521       res = parse_spectral_range(val, spectral->wlen_range);
    522       break;
    523     case SW:
    524       spectral->type = HTRDR_SPECTRAL_SW;
    525       res = parse_spectral_range(val, spectral->wlen_range);
    526       break;
    527     default: FATAL("Unreachable code\n"); break;
    528   }
    529   if(res != RES_OK) {
    530     fprintf(stderr, "Unable to parse the spectral parameter `%s' -- %s\n",
    531       str, res_to_cstr(res));
    532     goto error;
    533   }
    534 
    535 exit:
    536   return res;
    537 error:
    538   goto exit;
    539 }
    540 
    541 static res_T
    542 parse_volrad_budget_parameters(const char* str, void* ptr)
    543 {
    544   enum { MESH, SPT } iparam;
    545   char buf[BUFSIZ];
    546   struct htrdr_planets_args* args = ptr;
    547   char* key;
    548   char* val;
    549   char* tk_ctx;
    550   res_T res = RES_OK;
    551   ASSERT(str && ptr);
    552 
    553   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    554     fprintf(stderr,
    555       "Could not duplicate the parameters "
    556       "of the volumic radiative budget calculation `%s'\n",
    557       str);
    558     res = RES_MEM_ERR;
    559     goto error;
    560   }
    561   strncpy(buf, str, sizeof(buf));
    562 
    563   key = strtok_r(buf, "=", &tk_ctx);
    564   val = strtok_r(NULL, "", &tk_ctx);
    565 
    566        if(!strcmp(key, "mesh")) iparam = MESH;
    567   else if(!strcmp(key, "spt")) iparam = SPT;
    568   else {
    569     fprintf(stderr, "Invalid volumic radiative budget parameter `%s'\n", key);
    570     res = RES_BAD_ARG;
    571     goto error;
    572   }
    573 
    574   if(!val) {
    575     fprintf(stderr,
    576       "Invalid null value for the volumic radiative budget parameter `%s'.\n",
    577       key);
    578     res = RES_BAD_ARG;
    579     goto error;
    580   }
    581 
    582   switch(iparam) {
    583     case MESH:
    584       if(args->volrad_budget.smsh_filename) {
    585         mem_rm(args->volrad_budget.smsh_filename);
    586       }
    587       if(!(args->volrad_budget.smsh_filename = str_dup(val))) {
    588         res = RES_MEM_ERR;
    589       }
    590       break;
    591     case SPT: /* Sample Per Tetrahedron */
    592       res = cstr_to_uint(val, &args->volrad_budget.spt);
    593       break;
    594     default: FATAL("Unreachable code\n"); break;
    595   }
    596   if(res != RES_OK) {
    597     fprintf(stderr,
    598       "Unable to parse the volumic radiative budget parameter `%s' -- %s\n",
    599       str, res_to_cstr(res));
    600     goto error;
    601   }
    602 
    603 exit:
    604   return res;
    605 error:
    606   goto exit;
    607 }
    608 
    609 static res_T
    610 parse_accel_struct_build_parameters(const char* str, void* ptr)
    611 {
    612   enum { DEF, NTHREADS, PROC, STORAGE, OPTIC_THICKNESS} iparam;
    613   char buf[BUFSIZ];
    614   struct htrdr_planets_args* args = ptr;
    615   char* key;
    616   char* val;
    617   char* tk_ctx;
    618   res_T res = RES_OK;
    619 
    620   ASSERT(str && ptr);
    621 
    622   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    623     fprintf(stderr,
    624       "Could not duplicate the parameters of the acceleration structures `%s'",
    625       str);
    626     res = RES_MEM_ERR;
    627     goto error;
    628   }
    629   strncpy(buf, str, sizeof(buf));
    630 
    631   key = strtok_r(buf, "=", &tk_ctx);
    632   val = strtok_r(NULL, "", &tk_ctx);
    633 
    634        if(!strcmp(key, "def")) iparam = DEF;
    635   else if(!strcmp(key, "nthreads")) iparam = NTHREADS;
    636   else if(!strcmp(key, "proc")) iparam = PROC;
    637   else if(!strcmp(key, "storage")) iparam = STORAGE;
    638   else if(!strcmp(key, "tau")) iparam = OPTIC_THICKNESS;
    639   else {
    640     fprintf(stderr, "Invalid acceleration structure parameter `%s'\n", key);
    641     res = RES_BAD_ARG;
    642     goto error;
    643   }
    644 
    645   switch(iparam) {
    646     case DEF:
    647       res = cstr_to_uint(val, &args->accel_struct.definition_hint);
    648       if(res == RES_OK && args->accel_struct.definition_hint == 0)
    649         res = RES_BAD_ARG;
    650       break;
    651     case NTHREADS:
    652       res = cstr_to_uint(val, &args->accel_struct.nthreads);
    653       if(res == RES_OK && args->accel_struct.nthreads == 0) res = RES_BAD_ARG;
    654       break;
    655     case PROC:
    656       if(!strcmp(val, "all")) {
    657         args->accel_struct.master_only = 0;
    658       } else if(!strcmp(val, "master")) {
    659         args->accel_struct.master_only = 1;
    660       } else {
    661         res = RES_BAD_ARG;
    662       }
    663       break;
    664     case STORAGE:
    665       if(args->accel_struct.storage) mem_rm(args->accel_struct.storage);
    666       if(!(args->accel_struct.storage = str_dup(val))) res = RES_MEM_ERR;
    667       break;
    668     case OPTIC_THICKNESS:
    669       res = cstr_to_double(val, &args->accel_struct.optical_thickness);
    670       if(res == RES_OK && args->accel_struct.optical_thickness < 0)
    671         res = RES_BAD_ARG;
    672       break;
    673     default: FATAL("Unreachable code\n"); break;
    674   }
    675   if(res != RES_OK) {
    676     fprintf(stderr,
    677       "Unable to parse the acceleration structure parameter `%s' -- %s\n",
    678       str, res_to_cstr(res));
    679     goto error;
    680   }
    681 
    682 exit:
    683   return res;
    684 error:
    685   goto exit;
    686 }
    687 
    688 /*******************************************************************************
    689  * Local functions
    690  ******************************************************************************/
    691 res_T
    692 htrdr_planets_args_init(struct htrdr_planets_args* args, int argc, char** argv)
    693 {
    694   int opt;
    695   res_T res = RES_OK;
    696   ASSERT(args && argc && argv);
    697 
    698   *args = HTRDR_PLANETS_ARGS_DEFAULT;
    699 
    700   while((opt = getopt(argc, argv, "a:b:C:dfG:g:hi:No:P:r:S:s:t:v")) != -1) {
    701     switch(opt) {
    702       case 'a':
    703         (void)sa_add(args->aerosols, 1);
    704         args->aerosols[args->naerosols] = RNATM_AEROSOL_ARGS_NULL;
    705         args->naerosols += 1;
    706         res = cstr_parse_list(optarg, ':', parse_aerosol_parameters, args);
    707         if(res == RES_OK) {
    708           res = check_aerosol_args(args->aerosols+args->naerosols-1);
    709         }
    710         break;
    711       case 'b':
    712         res = cstr_parse_list(optarg, ':', parse_accel_struct_build_parameters, args);
    713         break;
    714       case 'C':
    715         args->output_type = HTRDR_PLANETS_ARGS_OUTPUT_IMAGE;
    716         args->cam_type = HTRDR_ARGS_CAMERA_PERSPECTIVE;
    717         res = htrdr_args_camera_perspective_parse(&args->cam_persp, optarg);
    718         break;
    719       case 'd':
    720         args->output_type = HTRDR_PLANETS_ARGS_OUTPUT_OCTREES;
    721         break;
    722       case 'f':
    723         args->force_output_overwrite = 1;
    724         break;
    725       case 'G':
    726         res = cstr_parse_list(optarg, ':', parse_ground_parameters, args);
    727         if(res == RES_OK) {
    728           res = check_ground_args(&args->ground);
    729         }
    730         break;
    731       case 'g':
    732         res = cstr_parse_list(optarg, ':', parse_gas_parameters, args);
    733         if(res == RES_OK) {
    734           res = check_gas_args(&args->gas);
    735         }
    736         break;
    737       case 'h':
    738         usage();
    739         htrdr_planets_args_release(args);
    740         args->quit = 1;
    741         goto exit;
    742       case 'i':
    743         res = htrdr_args_image_parse(&args->image, optarg);
    744         break;
    745       case 'N': args->precompute_normals = 1; break;
    746       case 'o': args->output = optarg; break;
    747       case 'P':
    748         args->output_type = HTRDR_PLANETS_ARGS_OUTPUT_IMAGE;
    749         args->cam_type = HTRDR_ARGS_CAMERA_ORTHOGRAPHIC;
    750         res = htrdr_args_camera_orthographic_parse(&args->cam_ortho, optarg);
    751         break;
    752       case 'r':
    753         res = cstr_parse_list(optarg, ':', parse_volrad_budget_parameters, args);
    754         args->output_type = HTRDR_PLANETS_ARGS_OUTPUT_VOLUMIC_RADIATIVE_BUDGET;
    755         break;
    756       case 'S':
    757         res = cstr_parse_list(optarg, ':', parse_source_parameters, args);
    758         break;
    759       case 's':
    760         res = cstr_parse_list(optarg, ':', parse_spectral_parameters, args);
    761         break;
    762       case 't':
    763         res = cstr_to_uint(optarg, &args->nthreads);
    764         if(res == RES_OK && !args->nthreads) res = RES_BAD_ARG;
    765         break;
    766       case 'v': args->verbose = 1; break;
    767       default: res = RES_BAD_ARG; break;
    768     }
    769     if(res != RES_OK) {
    770       if(optarg) {
    771         fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n",
    772           argv[0], optarg, opt);
    773       }
    774       goto error;
    775     }
    776   }
    777 
    778   res = check_gas_args(&args->gas);
    779   if(res != RES_OK) {
    780     fprintf(stderr, "missing gas definition -- option '-g'\n");
    781     goto error;
    782   }
    783 
    784   if(args->output_type != HTRDR_PLANETS_ARGS_OUTPUT_OCTREES) {
    785     res = check_ground_args(&args->ground);
    786     if(res != RES_OK) {
    787       fprintf(stderr, "missing ground definition -- option '-G'\n");
    788       goto error;
    789     }
    790 
    791     /* Check the source */
    792     if(args->spectral_domain.type == HTRDR_SPECTRAL_SW
    793     || args->spectral_domain.type == HTRDR_SPECTRAL_SW_CIE_XYZ) {
    794       res = htrdr_planets_source_args_check(&args->source);
    795       if(res != RES_OK) {
    796         fprintf(stderr, "missing source definition -- option '-S'\n");
    797         goto error;
    798       }
    799     }
    800   }
    801 
    802   if(args->output_type == HTRDR_PLANETS_ARGS_OUTPUT_VOLUMIC_RADIATIVE_BUDGET
    803   && args->spectral_domain.type != HTRDR_SPECTRAL_LW
    804   && args->spectral_domain.type != HTRDR_SPECTRAL_SW) {
    805     fprintf(stderr,
    806       "volumic radiative budget can be evaluated in "
    807       "longwave or shortwave only -- option '-s'\n");
    808     res = RES_BAD_ARG;
    809     goto error;
    810   }
    811 
    812 exit:
    813   return res;
    814 error:
    815   usage();
    816   htrdr_planets_args_release(args);
    817   goto exit;
    818 }
    819 
    820 void
    821 htrdr_planets_args_release(struct htrdr_planets_args* args)
    822 {
    823   size_t i;
    824   ASSERT(args);
    825 
    826   if(args->gas.smsh_filename) mem_rm(args->gas.smsh_filename);
    827   if(args->gas.sck_filename) mem_rm(args->gas.sck_filename);
    828   if(args->gas.temperatures_filename) mem_rm(args->gas.temperatures_filename);
    829   if(args->ground.smsh_filename) mem_rm(args->ground.smsh_filename);
    830   if(args->ground.props_filename) mem_rm(args->ground.props_filename);
    831   if(args->ground.mtllst_filename) mem_rm(args->ground.mtllst_filename);
    832   if(args->ground.name) mem_rm(args->ground.name);
    833   if(args->source.rnrl_filename) mem_rm(args->source.rnrl_filename);
    834   if(args->volrad_budget.smsh_filename) mem_rm(args->volrad_budget.smsh_filename);
    835   if(args->accel_struct.storage) mem_rm(args->accel_struct.storage);
    836 
    837   FOR_EACH(i, 0, args->naerosols) {
    838     struct rnatm_aerosol_args* aerosol = args->aerosols + i;
    839     if(aerosol->name) mem_rm(aerosol->name);
    840     if(aerosol->smsh_filename) mem_rm(aerosol->smsh_filename);
    841     if(aerosol->sars_filename) mem_rm(aerosol->sars_filename);
    842     if(aerosol->phase_fn_ids_filename) mem_rm(aerosol->phase_fn_ids_filename);
    843     if(aerosol->phase_fn_lst_filename) mem_rm(aerosol->phase_fn_lst_filename);
    844   }
    845   sa_release(args->aerosols);
    846 
    847   *args = HTRDR_PLANETS_ARGS_DEFAULT;
    848 }
    849 
    850 res_T
    851 htrdr_planets_args_check(const struct htrdr_planets_args* args)
    852 {
    853   size_t i;
    854   res_T res = RES_OK;
    855 
    856   if(!args) return RES_BAD_ARG;
    857 
    858   /* Check the gas */
    859   res = check_gas_args(&args->gas);
    860   if(res != RES_OK) return res;
    861 
    862   /* Check the aerosols */
    863   FOR_EACH(i, 0, args->naerosols) {
    864     res = check_aerosol_args(args->aerosols+i);
    865     if(res != RES_OK) return res;
    866   }
    867 
    868   /* Check the octree parameters */
    869   res = check_accel_struct_build_args(&args->accel_struct);
    870   if(res != RES_OK) return res;
    871 
    872   /* Check the spectral domain */
    873   res = check_spectral_args(&args->spectral_domain);
    874   if(res != RES_OK) return res;
    875 
    876   if(args->output_type != HTRDR_PLANETS_ARGS_OUTPUT_OCTREES) {
    877     /* Check the ground */
    878     res = check_ground_args(&args->ground);
    879     if(res != RES_OK) return res;
    880 
    881     /* Check the source */
    882     if(args->spectral_domain.type == HTRDR_SPECTRAL_SW
    883     || args->spectral_domain.type == HTRDR_SPECTRAL_SW_CIE_XYZ) {
    884       res = htrdr_planets_source_args_check(&args->source);
    885       if(res != RES_OK) return res;
    886     }
    887   }
    888 
    889   if(args->output_type == HTRDR_PLANETS_ARGS_OUTPUT_IMAGE) {
    890     res = htrdr_args_camera_perspective_check(&args->cam_persp);
    891     if(res != RES_OK) return res;
    892 
    893     res = htrdr_args_image_check(&args->image);
    894     if(res != RES_OK) return res;
    895   }
    896 
    897   if(args->output_type == HTRDR_PLANETS_ARGS_OUTPUT_VOLUMIC_RADIATIVE_BUDGET) {
    898     res = check_volrad_budget_args(&args->volrad_budget);
    899     if(res != RES_OK) return res;
    900 
    901     /* The volumic radiative budget can be evaluated
    902      * in longwave or shortwave only */
    903     if(args->spectral_domain.type != HTRDR_SPECTRAL_LW
    904     && args->spectral_domain.type != HTRDR_SPECTRAL_SW) {
    905       return RES_BAD_ARG;
    906     }
    907   }
    908 
    909   /* Check miscalleneous parameters */
    910   if(args->nthreads == 0
    911   || (unsigned)args->output_type >= HTRDR_PLANETS_ARGS_OUTPUT_TYPES_COUNT__)
    912     return RES_BAD_ARG;
    913 
    914   return RES_OK;
    915 }
    916 
    917 res_T
    918 htrdr_planets_source_args_check(const struct htrdr_planets_source_args* args)
    919 {
    920   if(!args) return RES_BAD_ARG;
    921 
    922   /* Invalid position */
    923   if(args->latitude <-90
    924   || args->latitude > 90
    925   || args->longitude <-180
    926   || args->longitude > 180
    927   || args->distance < 0)
    928     return RES_BAD_ARG;
    929 
    930   /* Invalid radius */
    931   if(args->radius < 0)
    932     return RES_BAD_ARG;
    933 
    934   /* Invalid radiance */
    935   if((args->temperature < 0 && !args->rnrl_filename) /* Both are invalids */
    936   || (args->temperature >=0 &&  args->rnrl_filename)) /* Both are valids */
    937     return RES_BAD_ARG;
    938 
    939   return RES_OK;
    940 }