rnatm

Load and structure data describing an atmosphere
git clone git://git.meso-star.fr/rnatm.git
Log | Files | Refs | README | LICENSE

rnatm_main.c (12035B)


      1 /* Copyright (C) 2022, 2023, 2025 Centre National de la Recherche Scientifique
      2  * Copyright (C) 2022, 2023, 2025 Institut Pierre-Simon Laplace
      3  * Copyright (C) 2022, 2023, 2025 Institut de Physique du Globe de Paris
      4  * Copyright (C) 2022, 2023, 2025 |Méso|Star> (contact@meso-star.com)
      5  * Copyright (C) 2022, 2023, 2025 Observatoire de Paris
      6  * Copyright (C) 2022, 2023, 2025 Université de Reims Champagne-Ardenne
      7  * Copyright (C) 2022, 2023, 2025 Université de Versaille Saint-Quentin
      8  * Copyright (C) 2022, 2023, 2025 Université Paul Sabatier
      9  *
     10  * This program is free software: you can redistribute it and/or modify
     11  * it under the terms of the GNU General Public License as published by
     12  * the Free Software Foundation, either version 3 of the License, or
     13  * (at your option) any later version.
     14  *
     15  * This program is distributed in the hope that it will be useful,
     16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     18  * GNU General Public License for more details.
     19  *
     20  * You should have received a copy of the GNU General Public License
     21  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     22 
     23 #define _POSIX_C_SOURCE 200809L /* strdup, strtok_r */
     24 
     25 #include "rnatm.h"
     26 
     27 #include <rsys/cstr.h>
     28 #include <rsys/mem_allocator.h>
     29 #include <rsys/rsys.h>
     30 #include <rsys/stretchy_array.h>
     31 
     32 #include <getopt.h>
     33 #include <string.h>
     34 
     35 struct args {
     36   struct rnatm_create_args rnatm;
     37   const char* vtk_filename;
     38   int check;
     39   int quit;
     40 };
     41 #define ARGS_DEFAULT__ { RNATM_CREATE_ARGS_DEFAULT__, NULL, 0, 0 }
     42 static const struct args ARGS_DEFAULT = ARGS_DEFAULT__;
     43 
     44 /*******************************************************************************
     45  * Helper functions
     46  ******************************************************************************/
     47 static void
     48 usage(FILE* stream)
     49 {
     50   ASSERT(stream);
     51   fprintf(stream,
     52 "usage: rnatm [-hNv] [-a aerosol_opt[:aerosol_opt ...]] [-c] [-d octrees]\n"
     53 "              [-i storage] [-n name] [-o storage] [-s nu0,nu1]\n"
     54 "              [-T optical_thickness] [-t thread_count] [-V definition]\n"
     55 "              -g gas_opt[:gas_opt ...]\n");
     56 }
     57 
     58 static res_T
     59 parse_gas_parameters(const char* str, void* ptr)
     60 {
     61   enum { MESH, CK, TEMP } iparam;
     62   char buf[BUFSIZ];
     63   struct args* args = ptr;
     64   char* key;
     65   char* val;
     66   char* tk_ctx;
     67   res_T res = RES_OK;
     68   ASSERT(args && str);
     69 
     70   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
     71     fprintf(stderr, "Could not duplicate the gas parameter `%s'\n", str);
     72     res = RES_MEM_ERR;
     73     goto error;
     74   }
     75   strncpy(buf, str, sizeof(buf));
     76 
     77   key = strtok_r(buf, "=", &tk_ctx);
     78   val = strtok_r(NULL, "", &tk_ctx);
     79 
     80        if(!strcmp(key, "mesh")) iparam = MESH;
     81   else if(!strcmp(key, "ck")) iparam = CK;
     82   else if(!strcmp(key, "temp")) iparam = TEMP;
     83   else {
     84     fprintf(stderr, "Invalid gas parameter `%s'\n", key);
     85     res = RES_BAD_ARG;
     86     goto error;
     87   }
     88 
     89   if(!val) {
     90     fprintf(stderr, "Invalid null value for gas parameter `%s'\n", key);
     91     res = RES_BAD_ARG;
     92     goto error;
     93   }
     94 
     95   switch(iparam) {
     96     case MESH:
     97       args->rnatm.gas.smsh_filename = strdup(val);
     98       if(!args->rnatm.gas.smsh_filename) res = RES_MEM_ERR;
     99       break;
    100     case CK:
    101       args->rnatm.gas.sck_filename = strdup(val);
    102       if(!args->rnatm.gas.sck_filename) res = RES_MEM_ERR;
    103       break;
    104     case TEMP:
    105       args->rnatm.gas.temperatures_filename = strdup(val);
    106       if(!args->rnatm.gas.temperatures_filename) res = RES_MEM_ERR;
    107       break;
    108     default: FATAL("Unreachable code\n"); break;
    109   }
    110   if(res != RES_OK) {
    111     fprintf(stderr, "Unable to parse the gas parameter `%s' -- %s\n",
    112       str, res_to_cstr(res));
    113     goto error;
    114   }
    115 
    116 exit:
    117   return res;
    118 error:
    119   goto exit;
    120 }
    121 
    122 static res_T
    123 parse_aerosol_parameters(const char* str, void* ptr)
    124 {
    125   enum { MESH, NAME, RADPROP, PHASEFN, PHASEIDS } iparam;
    126   struct rnatm_aerosol_args* aerosol = NULL;
    127   char buf[BUFSIZ];
    128   struct args* args = ptr;
    129   char* key;
    130   char* val;
    131   char* tk_ctx;
    132   res_T res = RES_OK;
    133   ASSERT(args && str);
    134 
    135   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    136     fprintf(stderr, "Could not duplicate the aerosol parameter `%s'\n", str);
    137     res = RES_MEM_ERR;
    138     goto error;
    139   }
    140   strncpy(buf, str, sizeof(buf));
    141 
    142   key = strtok_r(buf, "=", &tk_ctx);
    143   val = strtok_r(NULL, "", &tk_ctx);
    144 
    145        if(!strcmp(key, "mesh")) iparam = MESH;
    146   else if(!strcmp(key, "name")) iparam = NAME;
    147   else if(!strcmp(key, "radprop")) iparam = RADPROP;
    148   else if(!strcmp(key, "phasefn")) iparam = PHASEFN;
    149   else if(!strcmp(key, "phaseids")) iparam = PHASEIDS;
    150   else {
    151     fprintf(stderr, "Invalid aerosol parameter `%s'\n", key);
    152     res = RES_BAD_ARG;
    153     goto error;
    154   }
    155 
    156   if(!val) {
    157     fprintf(stderr, "Invalid null value for aerosol parameter `%s'\n", key);
    158     res = RES_BAD_ARG;
    159     goto error;
    160   }
    161 
    162   ASSERT(args->rnatm.naerosols);
    163   aerosol = args->rnatm.aerosols + (args->rnatm.naerosols - 1);
    164 
    165   switch(iparam) {
    166     case MESH:
    167       aerosol->smsh_filename = strdup(val);
    168       if(!aerosol->smsh_filename) res = RES_MEM_ERR;
    169       break;
    170     case NAME:
    171       aerosol->name = strdup(val);
    172       if(!aerosol->name) res = RES_MEM_ERR;
    173       break;
    174     case RADPROP:
    175       aerosol->sars_filename = strdup(val);
    176       if(!aerosol->sars_filename) res = RES_MEM_ERR;
    177       break;
    178     case PHASEFN:
    179       aerosol->phase_fn_lst_filename = strdup(val);
    180       if(!aerosol->phase_fn_lst_filename) res = RES_MEM_ERR;
    181       break;
    182     case PHASEIDS:
    183       aerosol->phase_fn_ids_filename = strdup(val);
    184       if(!aerosol->phase_fn_ids_filename) res = RES_MEM_ERR;
    185       break;
    186     default: FATAL("Unreachable code\n"); break;
    187   }
    188   if(res != RES_OK) {
    189     fprintf(stderr, "Unable to parse the aerosol parameter `%s' -- %s\n",
    190       str, res_to_cstr(res));
    191     goto error;
    192   }
    193 
    194 exit:
    195   return res;
    196 error:
    197   goto exit;
    198 }
    199 
    200 static res_T
    201 parse_spectral_range(struct args* args, char* str)
    202 {
    203   size_t len = 0;
    204   res_T res = RES_OK;
    205   ASSERT(args && str);
    206 
    207   res = cstr_to_list_double(str, ',', args->rnatm.spectral_range, &len, 2);
    208   if(res == RES_OK && len != 2) res = RES_BAD_ARG;
    209   if(res != RES_OK) goto error;
    210 
    211   if(args->rnatm.spectral_range[0] < 0
    212   || args->rnatm.spectral_range[1] < 0
    213   || args->rnatm.spectral_range[0] > args->rnatm.spectral_range[1])
    214     goto error;
    215 
    216 exit:
    217   return res;
    218 error:
    219   goto exit;
    220 }
    221 
    222 static void
    223 args_release(struct args* args)
    224 {
    225   size_t i;
    226   ASSERT(args);
    227   if(args->rnatm.gas.smsh_filename) free(args->rnatm.gas.smsh_filename);
    228   if(args->rnatm.gas.sck_filename) free(args->rnatm.gas.sck_filename);
    229   if(args->rnatm.gas.temperatures_filename)
    230     free(args->rnatm.gas.temperatures_filename);
    231   if(args->rnatm.octrees_storage) CHK(fclose(args->rnatm.octrees_storage) == 0);
    232 
    233   FOR_EACH(i, 0, args->rnatm.naerosols) {
    234     struct rnatm_aerosol_args* aerosol = args->rnatm.aerosols + i;
    235     if(aerosol->name) free(aerosol->name);
    236     if(aerosol->smsh_filename) free(aerosol->smsh_filename);
    237     if(aerosol->sars_filename) free(aerosol->sars_filename);
    238     if(aerosol->phase_fn_ids_filename) free(aerosol->phase_fn_ids_filename);
    239     if(aerosol->phase_fn_lst_filename) free(aerosol->phase_fn_lst_filename);
    240   }
    241   sa_release(args->rnatm.aerosols);
    242   *args = ARGS_DEFAULT;
    243 }
    244 
    245 static res_T
    246 args_init(struct args* args, int argc, char** argv)
    247 {
    248   const char* storage_filename = NULL;
    249   size_t i = 0;
    250   res_T res = RES_OK;
    251   int opt;
    252   ASSERT(args && argc && argv);
    253 
    254   *args = ARGS_DEFAULT;
    255 
    256   while((opt = getopt(argc, argv, "a:cd:g:hi:Nn:o:s:T:t:V:v")) != -1) {
    257     switch(opt) {
    258       case 'a':
    259         (void)sa_add(args->rnatm.aerosols, 1);
    260         args->rnatm.aerosols[args->rnatm.naerosols] = RNATM_AEROSOL_ARGS_NULL;
    261         args->rnatm.naerosols += 1;
    262         res = cstr_parse_list(optarg, ':', parse_aerosol_parameters, args);
    263         break;
    264       case 'c': args->check = 1; break;
    265       case 'd': args->vtk_filename = optarg; break;
    266       case 'g':
    267         res = cstr_parse_list(optarg, ':', parse_gas_parameters, args);
    268         break;
    269       case 'h':
    270         usage(stdout);
    271         args_release(args);
    272         args->quit = 1;
    273         goto exit;
    274       case 'N': args->rnatm.precompute_normals = 1; break;
    275       case 'n': args->rnatm.name = optarg; break;
    276       case 'o':
    277         args->rnatm.load_octrees_from_storage = 0;
    278         storage_filename = optarg;
    279         break;
    280       case 'i':
    281         args->rnatm.load_octrees_from_storage = 1;
    282         storage_filename = optarg;
    283         break;
    284       case 's':
    285         res = parse_spectral_range(args, optarg);
    286         break;
    287       case 'T':
    288         res = cstr_to_double(optarg, &args->rnatm.optical_thickness);
    289         if(res != RES_OK && args->rnatm.optical_thickness<=0) res = RES_BAD_ARG;
    290         break;
    291       case 't':
    292         res = cstr_to_uint(optarg, &args->rnatm.nthreads);
    293         if(res == RES_OK && !args->rnatm.nthreads) res = RES_BAD_ARG;
    294         break;
    295       case 'V':
    296         res = cstr_to_uint(optarg, &args->rnatm.grid_definition_hint);
    297         if(res == RES_OK && !args->rnatm.grid_definition_hint) res = RES_BAD_ARG;
    298         break;
    299       case 'v': args->rnatm.verbose = 1; break;
    300       default: res = RES_BAD_ARG; break;
    301     }
    302     if(res != RES_OK) {
    303       if(optarg) {
    304         fprintf(stderr, "%s: invalid option args `%s' -- `%c'\n",
    305           argv[0], optarg, opt);
    306       }
    307       goto error;
    308     }
    309   }
    310 
    311   if(storage_filename) {
    312     const char* mode = args->rnatm.load_octrees_from_storage ? "r" : "w+";
    313     args->rnatm.octrees_storage = fopen(storage_filename, mode);
    314     if(!args->rnatm.octrees_storage) {
    315       fprintf(stderr, "Unable to open octree storage file %s\n",
    316         storage_filename);
    317       res = RES_IO_ERR;
    318       goto error;
    319     }
    320   }
    321 
    322   /* Check the required options */
    323   if(!args->rnatm.gas.smsh_filename
    324   || !args->rnatm.gas.sck_filename
    325   || !args->rnatm.gas.temperatures_filename) {
    326     fprintf(stderr, "Incomplete gas definition -- option `-g'\n");
    327     res = RES_BAD_ARG;
    328     goto error;
    329   }
    330 
    331   FOR_EACH(i, 0, args->rnatm.naerosols) {
    332     struct rnatm_aerosol_args* aerosol = args->rnatm.aerosols + i;
    333 
    334     if(!aerosol->smsh_filename
    335     || !aerosol->sars_filename
    336     || !aerosol->phase_fn_ids_filename
    337     || !aerosol->phase_fn_lst_filename) {
    338       fprintf(stderr, "Incomplete %lu^th aerosol definition -- option `-a'\n",
    339         (unsigned long)i);
    340       res = RES_BAD_ARG;
    341       goto error;
    342     }
    343   }
    344 
    345 exit:
    346   return res;
    347 error:
    348   usage(stderr);
    349   args_release(args);
    350   goto exit;
    351 }
    352 
    353 static res_T
    354 write_vtk_octrees(struct rnatm* atm, const char* filename)
    355 {
    356   size_t octrees_range[2];
    357   FILE* fp = NULL;
    358   res_T res = RES_OK;
    359   ASSERT(atm && filename);
    360 
    361   if(!strcmp(filename, "-")) {
    362     fp = stdout;
    363   } else {
    364     fp = fopen(filename, "w");
    365     if(!fp) {
    366       fprintf(stderr, "Could not open `%s' -- %s\n", filename, strerror(errno));
    367       res = RES_IO_ERR;
    368       goto error;
    369     }
    370   }
    371 
    372   octrees_range[0] = 0;
    373   octrees_range[1] = rnatm_get_spectral_items_count(atm) - 1;
    374 
    375   res = rnatm_write_vtk_octrees(atm, octrees_range, fp);
    376   if(res != RES_OK) goto error;
    377 
    378 exit:
    379   if(fp != stdout) CHK(fclose(fp) == 0);
    380   return res;
    381 error:
    382   goto exit;
    383 }
    384 
    385 /*******************************************************************************
    386  * Main function
    387  ******************************************************************************/
    388 int
    389 main(int argc, char** argv)
    390 {
    391   struct args  args = ARGS_DEFAULT;
    392   struct rnatm* atm = NULL;
    393   res_T res = RES_OK;
    394   int err = 0;
    395 
    396   res = args_init(&args, argc, argv);
    397   if(res != RES_OK) goto error;
    398 
    399   res = rnatm_create(&args.rnatm, &atm);
    400   if(res != RES_OK) goto error;
    401 
    402   if(args.check) {
    403     res = rnatm_validate(atm);
    404     if(res != RES_OK) goto error;
    405   }
    406 
    407   if(args.vtk_filename) {
    408     res = write_vtk_octrees(atm, args.vtk_filename);
    409     if(res != RES_OK) goto error;
    410   }
    411 
    412 exit:
    413   args_release(&args);
    414   if(atm) RNATM(ref_put(atm));
    415   if(mem_allocated_size() != 0) {
    416     fprintf(stderr, "Memory leaks: %lu bytes\n",
    417       (unsigned long)mem_allocated_size());
    418     err = -1;
    419   }
    420   return err;
    421 error:
    422   err = -1;
    423   goto exit;
    424 }