star-line

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

sln_build.c (14616B)


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