star-line

Structure for accelerating line importance sampling
git clone git://git.meso-star.fr/star-line.git
Log | Files | Refs | README | LICENSE

sln_build.c (10326B)


      1 /* Copyright (C) 2022, 2026 |Méso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2026 Université de Lorraine
      3  * Copyright (C) 2022 Centre National de la Recherche Scientifique
      4  * Copyright (C) 2022 Université Paul Sabatier
      5  *
      6  * This program is free software: you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation, either version 3 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     14  * GNU General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU General Public License
     17  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     18 
     19 #define _POSIX_C_SOURCE 200809L /* strtok_r */
     20 
     21 #include "sln.h"
     22 
     23 #include <rsys/cstr.h>
     24 #include <rsys/mem_allocator.h>
     25 #include <rsys/rsys.h>
     26 
     27 #include <stdio.h>
     28 #include <string.h>
     29 #include <unistd.h> /* getopt */
     30 
     31 enum line_list_format {
     32   LINE_LIST_HITRAN,
     33   LINE_LIST_SHTR,
     34   LINE_LIST_FORMAT_COUNT__
     35 };
     36 
     37 struct args {
     38   /* Spectroscopic parameters */
     39   const char* lines;
     40   const char* molparams;
     41   enum sln_line_profile line_profile;
     42 
     43   const char* output;
     44 
     45   /* Thermodynamic properties */
     46   const char* mixture;
     47   double pressure; /* [atm] */
     48   double temperature; /* [K] */
     49 
     50   /* Polyline */
     51   double mesh_decimation_err;
     52   unsigned nvertices_hint;
     53   enum sln_mesh_type mesh_type;
     54 
     55   /* Miscellaneous */
     56   int quit;
     57   int verbose;
     58   enum line_list_format line_format;
     59 };
     60 #define ARGS_DEFAULT__ { \
     61   /* Spectroscopic parameters */ \
     62   NULL, /* line list */ \
     63   NULL, /* Isotopologue metadata */ \
     64   SLN_LINE_PROFILE_VOIGT, /* Line profile */ \
     65   \
     66   NULL, /* Output */ \
     67   \
     68   /* Thermodynamic properties */ \
     69   NULL, \
     70   -1, /* Pressure [atm] */ \
     71   -1, /* Temperature [K] */ \
     72   \
     73   /* Polyline */ \
     74   0.01, \
     75   16, \
     76   SLN_MESH_UPPER, \
     77   \
     78   /* Miscellaneous */ \
     79   0, /* Quit */ \
     80   0, /* Verbose */ \
     81   LINE_LIST_HITRAN /* lines_format */ \
     82 }
     83 static const struct args ARGS_DEFAULT = ARGS_DEFAULT__;
     84 
     85 struct cmd {
     86   struct sln_device* sln;
     87 
     88   struct shtr* shtr;
     89   struct shtr_line_list* lines;
     90   struct shtr_isotope_metadata* molparams;
     91 
     92   FILE* output;
     93 };
     94 static const struct cmd CMD_NULL = {0};
     95 
     96 /*******************************************************************************
     97  * Helper functions
     98  ******************************************************************************/
     99 static void
    100 usage(FILE* stream)
    101 {
    102   fprintf(stream,
    103 "usage: sln-build [-hsv] [-e polyline_opt[:polyline_opt ...]] [-l line_profile]\n"
    104 "                 [-o accel_struct] -P pressure -T temperature -m molparams\n"
    105 "                 -x mixture [lines]\n");
    106 }
    107 
    108 static res_T
    109 parse_line_profile(const char* str, enum sln_line_profile* profile)
    110 {
    111   res_T res = RES_OK;
    112   ASSERT(str && profile);
    113 
    114   if(!strcmp(str, "voigt")) {
    115     *profile = SLN_LINE_PROFILE_VOIGT;
    116 
    117   } else {
    118     fprintf(stderr, "invalid line profile '%s'\n", str);
    119     res = RES_BAD_ARG;
    120     goto error;
    121   }
    122 
    123 exit:
    124   return res;
    125 error:
    126   goto exit;
    127 }
    128 
    129 static res_T
    130 parse_mesh_type(const char* str, enum sln_mesh_type* type)
    131 {
    132   res_T res = RES_OK;
    133   ASSERT(str && type);
    134 
    135   if(!strcmp(str, "fit")) {
    136     *type = SLN_MESH_FIT;
    137   } else if(!strcmp(str, "upper")) {
    138     *type = SLN_MESH_UPPER;
    139   } else {
    140     fprintf(stderr, "invalid mesh type `%s'\n", str);
    141     res = RES_BAD_ARG;
    142     goto error;
    143   }
    144 
    145 exit:
    146   return res;
    147 error:
    148   goto exit;
    149 }
    150 
    151 static res_T
    152 parse_polyline_opt(const char* str, void* ptr)
    153 {
    154   enum { ERR, MESH, VCOUNT } opt;
    155   char buf[BUFSIZ];
    156 
    157   struct args* args = ptr;
    158 
    159   char* key = NULL;
    160   char* val = NULL;
    161   char* tk_ctx = NULL;
    162   res_T res = RES_OK;
    163 
    164   ASSERT(str && ptr);
    165 
    166   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    167     fprintf(stderr, "could not duplicate polyline option `%s'\n", str);
    168     res = RES_MEM_ERR;
    169     goto error;
    170   }
    171 
    172   strncpy(buf, str, sizeof(buf));
    173 
    174   key = strtok_r(buf, "=", &tk_ctx);
    175   val = strtok_r(NULL, "", &tk_ctx);
    176 
    177        if(!strcmp(key, "err")) opt = ERR;
    178   else if(!strcmp(key, "mesh")) opt = MESH;
    179   else if(!strcmp(key, "vcount")) opt = MESH;
    180   else {
    181     fprintf(stderr, "invalid polyline option `%s'\n", key);
    182     res = RES_BAD_ARG;
    183     goto error;
    184   }
    185 
    186   switch(opt) {
    187     case ERR:
    188       res = cstr_to_double(val, &args->mesh_decimation_err);
    189       if(res == RES_OK && args->mesh_decimation_err < 0) res = RES_BAD_ARG;
    190       break;
    191     case MESH:
    192       res = parse_mesh_type(val, &args->mesh_type);
    193       break;
    194     case VCOUNT:
    195       res = cstr_to_uint(val, &args->nvertices_hint);
    196       break;
    197     default: FATAL("Unreachable code\n"); break;
    198   }
    199 
    200   if(res != RES_OK) {
    201     fprintf(stderr,
    202       "error while parsing the polyline option `%s' -- %s\n",
    203       str, res_to_cstr(res));
    204     goto error;
    205   }
    206 
    207 exit:
    208   return res;
    209 error:
    210   goto exit;
    211 }
    212 
    213 static res_T
    214 args_init(struct args* args, int argc, char** argv)
    215 {
    216   int opt = 0;
    217   res_T res = RES_OK;
    218 
    219   ASSERT(args);
    220 
    221   *args = ARGS_DEFAULT;
    222 
    223   while((opt = getopt(argc, argv, "e:hl:o:P:sT:m:vx:")) != -1) {
    224     switch(opt) {
    225       case 'e':
    226         res = cstr_parse_list(optarg, ':', parse_polyline_opt, args);
    227         break;
    228       case 'h':
    229         usage(stdout);
    230         args->quit = 1;
    231         goto exit;
    232       case 'l':
    233         res = parse_line_profile(optarg, &args->line_profile);
    234         break;
    235       case 'o': args->output = optarg; break;
    236       case 'P':
    237         res = cstr_to_double(optarg, &args->pressure);
    238         if(res == RES_OK && args->pressure < 0) res = RES_BAD_ARG;
    239         break;
    240       case 's': args->line_format = LINE_LIST_SHTR; break;
    241       case 'T':
    242         res = cstr_to_double(optarg, &args->temperature);
    243         if(res == RES_OK && args->temperature < 0) res = RES_BAD_ARG;
    244         break;
    245       case 'm': args->molparams = optarg; break;
    246       case 'v': args->verbose += (args->verbose < 3); break;
    247       case 'x': args->mixture = optarg; break;
    248       default: res = RES_BAD_ARG; break;
    249     }
    250 
    251     if(res != RES_OK) {
    252       if(optarg) {
    253         fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n",
    254           argv[0], optarg, opt);
    255       }
    256       goto error;
    257     }
    258   }
    259 
    260   if(optind < argc) args->lines = argv[optind];
    261 
    262   #define MANDATORY(Cond, Name, Opt) { \
    263     if(!(Cond)) { \
    264       fprintf(stderr, "%s: %s missing -- option '-%c'\n", argv[0], (Name), (Opt)); \
    265       res = RES_BAD_ARG; \
    266       goto error; \
    267     } \
    268   } (void)0
    269   MANDATORY(args->pressure >= 0, "pressure", 'P');
    270   MANDATORY(args->temperature >= 0, "temperature", 'T');
    271   MANDATORY(args->molparams, "molparams", 'm');
    272   MANDATORY(args->mixture, "mixture", 'x');
    273   #undef MANDATORY
    274 
    275 exit:
    276   return res;
    277 error:
    278   usage(stderr);
    279   goto exit;
    280 }
    281 
    282 static res_T
    283 load_lines_hitran(struct cmd* cmd, const struct args* args)
    284 {
    285   struct shtr_line_list_load_args load_args = SHTR_LINE_LIST_LOAD_ARGS_NULL;
    286   ASSERT(cmd && args);
    287 
    288   if(args->lines != NULL) {
    289     load_args.filename = args->lines;
    290   } else {
    291     load_args.filename = "stdin";
    292     load_args.file = stdin;
    293   }
    294 
    295   return shtr_line_list_load(cmd->shtr, &load_args, &cmd->lines);
    296 }
    297 
    298 static res_T
    299 load_lines_shtr(struct cmd* cmd, const struct args* args)
    300 {
    301   FILE* fp = NULL;
    302   res_T res = RES_OK;
    303 
    304   ASSERT(cmd && args);
    305 
    306   if(args->lines == NULL) {
    307     fp = stdin;
    308   } else {
    309     fp = fopen(args->lines, "r");
    310     if(!fp) {
    311       fprintf(stderr, "error opening file `%s' -- %s\n",
    312         args->lines, strerror(errno));
    313       res = RES_IO_ERR;
    314       goto error;
    315     }
    316   }
    317 
    318   res = shtr_line_list_create_from_stream(cmd->shtr, fp, &cmd->lines);
    319   if(res != RES_OK) goto error;
    320 
    321 exit:
    322   if(cmd->lines) SHTR(line_list_ref_put(cmd->lines));
    323   if(fp && fp != stdin) CHK(fclose(fp) == 0);
    324   return res;
    325 error:
    326   goto exit;
    327 }
    328 
    329 static res_T
    330 load_lines(struct cmd* cmd, const struct args* args)
    331 {
    332   res_T res = RES_OK;
    333   switch(args->line_format) {
    334     case LINE_LIST_HITRAN: res = load_lines_hitran(cmd, args); break;
    335     case LINE_LIST_SHTR: res = load_lines_shtr(cmd, args); break;
    336     default: FATAL("Unreachable code\n"); break;
    337   }
    338   return res;
    339 }
    340 
    341 static res_T
    342 setup_output(struct cmd* cmd, const struct args* args)
    343 {
    344   res_T res = RES_OK;
    345   ASSERT(cmd && args);
    346 
    347   if(!args->output) {
    348     cmd->output = stdout;
    349 
    350   } else {
    351     cmd->output = fopen(args->output, "w");
    352     if(!cmd->output) {
    353       fprintf(stderr, "error opening file `%s' -- %s\n",
    354         args->output, strerror(errno));
    355       res = RES_IO_ERR;
    356       goto error;
    357     }
    358   }
    359 
    360 exit:
    361   return res;
    362 error:
    363   if(cmd->output && cmd->output != stdout) CHK(fclose(cmd->output) == 0);
    364   goto exit;
    365 }
    366 
    367 static void
    368 cmd_release(struct cmd* cmd)
    369 {
    370   ASSERT(cmd);
    371   if(cmd->sln) SLN(device_ref_put(cmd->sln));
    372   if(cmd->shtr) SHTR(ref_put(cmd->shtr));
    373   if(cmd->lines) SHTR(line_list_ref_put(cmd->lines));
    374   if(cmd->molparams) SHTR(isotope_metadata_ref_put(cmd->molparams));
    375 }
    376 
    377 static res_T
    378 cmd_init(struct cmd* cmd, const struct args* args)
    379 {
    380   struct sln_device_create_args sln_args = SLN_DEVICE_CREATE_ARGS_DEFAULT;
    381   struct shtr_create_args shtr_args = SHTR_CREATE_ARGS_DEFAULT;
    382   res_T res = RES_OK;
    383 
    384   ASSERT(cmd && args);
    385 
    386   *cmd = CMD_NULL;
    387 
    388   shtr_args.verbose = args->verbose;
    389   res = shtr_create(&shtr_args, &cmd->shtr);
    390   if(res != RES_OK) goto error;
    391 
    392   sln_args.verbose = args->verbose;
    393   res = sln_device_create(&sln_args, &cmd->sln);
    394   if(res != RES_OK) goto error;
    395 
    396   res = load_lines(cmd, args);
    397   if(res != RES_OK) goto error;
    398 
    399   res = shtr_isotope_metadata_load(cmd->shtr, args->molparams, &cmd->molparams);
    400   if(res != RES_OK) goto error;
    401 
    402   res = setup_output(cmd, args);
    403   if(res != RES_OK) goto error;
    404 
    405 exit:
    406   return res;
    407 error:
    408   cmd_release(cmd);
    409   goto exit;
    410 }
    411 
    412 /*******************************************************************************
    413  * The program
    414  ******************************************************************************/
    415 int
    416 main(int argc, char** argv)
    417 {
    418   struct args args = ARGS_DEFAULT;
    419   struct cmd cmd = CMD_NULL;
    420   int err = 0;
    421   res_T res = RES_OK;
    422 
    423   if((res = args_init(&args, argc, argv)) != RES_OK) goto error;
    424   if(args.quit) goto exit;
    425 
    426   if((res = cmd_init(&cmd, &args)) != RES_OK) goto error;
    427 
    428 exit:
    429   cmd_release(&cmd);
    430   CHK(mem_allocated_size() == 0);
    431   return err;
    432 error:
    433   err = 1;
    434   goto exit;
    435 }