star-hitran

Load line-by-line data from the HITRAN database
git clone git://git.meso-star.fr/star-hitran.git
Log | Files | Refs | README | LICENSE

shtr_main.c (8857B)


      1 /* Copyright (C) 2022, 2025, 2026 |Méso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2025, 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 200112L /* getopt support */
     20 
     21 #include "shtr.h"
     22 
     23 #include <rsys/clock_time.h>
     24 #include <rsys/cstr.h>
     25 #include <rsys/mem_allocator.h>
     26 #include <rsys/rsys.h>
     27 
     28 #include <stdio.h>
     29 #include <string.h>
     30 #include <unistd.h> /* getopt */
     31 
     32 #define STDIN_NAME "-"
     33 
     34 struct args {
     35   char* molparam;
     36   char* lines;
     37   char* output;
     38 
     39   int bench_line_access;
     40   int internal_format;
     41   int verbose; /* Verbosity level */
     42   int compression; /* Compression level */
     43   int human_readable;
     44 };
     45 static const struct args ARGS_DEFAULT = {
     46   NULL, NULL, NULL, 0, 0, 0, SHTR_DEFAULT_COMPRESSION, 0
     47 };
     48 
     49 struct cmd {
     50   struct mem_allocator allocator;
     51   int allocator_is_init;
     52   struct args args;
     53   struct shtr* shtr;
     54 };
     55 static const struct cmd CMD_NULL = {0};
     56 
     57 /*******************************************************************************
     58  * Helper functions
     59  ******************************************************************************/
     60 static INLINE void
     61 usage(FILE* stream)
     62 {
     63   fprintf(stream,
     64 "usage: shtr [-ahsv] [-l lines] [-m molparam] [-o output] [-z compression_level]\n");
     65 }
     66 
     67 static res_T
     68 args_init(struct args* args, int argc, char** argv)
     69 {
     70   int opt = 0;
     71   res_T res = RES_OK;
     72 
     73   ASSERT(args);
     74 
     75   *args = ARGS_DEFAULT;
     76 
     77   while((opt = getopt(argc, argv, "ahl:m:o:svz:")) != -1) {
     78     switch(opt) {
     79       case 'a': args->bench_line_access = 1; break;
     80       case 'h': args->human_readable = 1; break;
     81       case 'l': args->lines = optarg; break;
     82       case 'm': args->molparam = optarg; break;
     83       case 'o': args->output = optarg; break;
     84       case 's': args->internal_format = 1; break;
     85       case 'v': args->verbose += (args->verbose < 3); break;
     86       case 'z': res = cstr_to_int(optarg, &args->compression); break;
     87       default: res = RES_BAD_ARG; break;
     88     }
     89     if(res != RES_OK) {
     90       if(optarg) {
     91         fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n",
     92           argv[0], optarg, opt);
     93       }
     94       goto error;
     95     }
     96   }
     97 
     98 exit:
     99   return res;
    100 error:
    101   usage(stderr);
    102   goto exit;
    103 }
    104 
    105 static void
    106 cmd_release(struct cmd* cmd)
    107 {
    108   if(cmd->shtr) SHTR(ref_put(cmd->shtr));
    109   if(cmd->allocator_is_init) mem_shutdown_regular_allocator(&cmd->allocator);
    110 }
    111 
    112 static res_T
    113 cmd_init(struct cmd* cmd, const struct args* args)
    114 {
    115   struct shtr_create_args create_args = SHTR_CREATE_ARGS_DEFAULT;
    116   res_T res = RES_OK;
    117 
    118   ASSERT(cmd && args);
    119 
    120   cmd->args = *args;
    121 
    122   mem_init_regular_allocator(&cmd->allocator);
    123   cmd->allocator_is_init = 1;
    124 
    125   create_args.allocator = &cmd->allocator;
    126   create_args.verbose = args->verbose;
    127   if((res = shtr_create(&create_args, &cmd->shtr)) != RES_OK) goto error;
    128 
    129 exit:
    130   return res;
    131 error:
    132   cmd_release(cmd);
    133   goto exit;
    134 }
    135 
    136 static res_T
    137 load_molparam(const struct cmd* cmd, struct shtr_isotope_metadata** molparam)
    138 {
    139   ASSERT(cmd && molparam && cmd->args.molparam);
    140   if(!strcmp(cmd->args.molparam, STDIN_NAME)) {
    141     return shtr_isotope_metadata_load_stream(cmd->shtr, stdin, "stdin", molparam);
    142   } else {
    143     return shtr_isotope_metadata_load(cmd->shtr, cmd->args.molparam, molparam);
    144   }
    145 }
    146 
    147 static res_T
    148 load_lines_binary(const struct cmd* cmd, struct shtr_line_list** lines)
    149 {
    150   FILE* fp = NULL;
    151   res_T res = RES_OK;
    152 
    153   ASSERT(cmd && lines && cmd->args.lines);
    154 
    155   if(!strcmp(cmd->args.lines, STDIN_NAME)) {
    156     fp = stdin;
    157   } else {
    158     fp = fopen(cmd->args.lines, "r");
    159     if(!fp) {
    160       fprintf(stderr, "%s: error opening file -- %s\n",
    161         cmd->args.lines, strerror(errno));
    162       res = RES_IO_ERR;
    163       goto error;
    164     }
    165   }
    166 
    167   res = shtr_line_list_create_from_stream(cmd->shtr, fp, lines);
    168   if(res != RES_OK) goto error;
    169 
    170 exit:
    171   if(fp && fp != stdin) CHK(fclose(fp) == 0);
    172   return res;
    173 error:
    174   goto exit;
    175 }
    176 
    177 static res_T
    178 load_lines_hitran(const struct cmd* cmd, struct shtr_line_list** lines)
    179 {
    180   struct shtr_line_list_load_args args = SHTR_LINE_LIST_LOAD_ARGS_NULL__;
    181   res_T res = RES_OK;
    182 
    183   ASSERT(cmd && lines && cmd->args.lines);
    184 
    185   if(strcmp(cmd->args.lines, STDIN_NAME)) {
    186     args.filename = cmd->args.lines;
    187   } else {
    188     args.file = stdin;
    189     args.filename = "stdin";
    190   }
    191   args.compression_level = cmd->args.compression;
    192 
    193   res = shtr_line_list_load(cmd->shtr, &args, lines);
    194   if(res != RES_OK) goto error;
    195 
    196 exit:
    197   return res;
    198 error:
    199   goto exit;
    200 }
    201 
    202 static res_T
    203 load_lines(const struct cmd* cmd, struct shtr_line_list** lines)
    204 {
    205   ASSERT(cmd && lines && cmd->args.lines);
    206   if(cmd->args.internal_format) {
    207     return load_lines_binary(cmd, lines);
    208   } else {
    209     return load_lines_hitran(cmd, lines);
    210   }
    211 }
    212 
    213 static res_T
    214 process_lines(const struct cmd* cmd, const struct shtr_line_list* list)
    215 {
    216   FILE* fp = NULL;
    217   struct shtr_line_list_info info = SHTR_LINE_LIST_INFO_NULL;
    218   res_T res = RES_OK;
    219 
    220   ASSERT(cmd && list);
    221 
    222   SHTR(line_list_get_info(list, &info));
    223 
    224   #define PRINT(Name) \
    225     printf(STR(Name)" in [%g, %g]; absolute error: %g\n", \
    226       SPLIT2(info.Name.range), info.Name.err)
    227   PRINT(wavenumber);
    228   PRINT(intensity);
    229   PRINT(gamma_air);
    230   PRINT(gamma_self);
    231   PRINT(lower_state_energy);
    232   PRINT(n_air);
    233   PRINT(delta_air);
    234   #undef PRINT
    235 
    236   if(!cmd->args.output) goto exit;
    237 
    238   fp = fopen(cmd->args.output, "w");
    239   if(!fp) {
    240     fprintf(stderr, "%s: error opening file -- %s\n",
    241       cmd->args.output, strerror(errno));
    242     res = RES_IO_ERR;
    243     goto error;
    244   }
    245 
    246   res = shtr_line_list_write(list, fp);
    247   if(res != RES_OK) goto error;
    248 
    249 exit:
    250   if(fp) CHK(fclose(fp) == 0);
    251   return res;
    252 error:
    253   goto exit;
    254 }
    255 
    256 static void
    257 bench_line_access(struct shtr_line_list* lines)
    258 {
    259   struct shtr_line line;
    260   struct time t0, t1;
    261   const size_t read_count = 10000;
    262   double lines_per_second = 0;
    263   int64_t usec = 0;
    264   size_t i, n;
    265 
    266   ASSERT(lines);
    267 
    268   SHTR(line_list_get_size(lines, &n));
    269 
    270   /* Linear access */
    271   time_current(&t0);
    272   FOR_EACH(i, 0, MMIN(n, read_count)) {
    273     SHTR(line_list_at(lines, i, &line));
    274   }
    275   usec = time_val(time_sub(&t0, time_current(&t1), &t0), TIME_USEC);
    276   lines_per_second = 1.e9 * (double)n / (double)usec;
    277   printf("linear access: %g lines per second\n", lines_per_second);
    278 
    279   /* Random access */
    280   time_current(&t0);
    281   FOR_EACH(i, 0, read_count) {
    282     const double r = (double)rand() / (double)(RAND_MAX-1);
    283     const size_t iline = (size_t)(r * (double)n);
    284     SHTR(line_list_at(lines, iline, &line));
    285   }
    286   usec = time_val(time_sub(&t0, time_current(&t1), &t0), TIME_USEC);
    287   lines_per_second = 1.e9 * (double)read_count / (double)usec;
    288   printf("random access: %g lines per second\n", lines_per_second);
    289 }
    290 
    291 static res_T
    292 cmd_run(const struct cmd* cmd)
    293 {
    294   char buf[128] = {0};
    295   struct shtr_isotope_metadata* molparam = NULL;
    296   struct shtr_line_list* lines = NULL;
    297   size_t sz = 0;
    298   res_T res = RES_OK;
    299   ASSERT(cmd);
    300 
    301   if(cmd->args.molparam) {
    302     if((res = load_molparam(cmd, &molparam)) != RES_OK)  goto error;
    303   }
    304   if(cmd->args.lines) {
    305     if((res = load_lines(cmd, &lines)) != RES_OK) goto error;
    306     if((res = process_lines(cmd, lines)) != RES_OK) goto error;
    307     if(cmd->args.bench_line_access) bench_line_access(lines);
    308   }
    309 
    310   sz = MEM_ALLOCATED_SIZE(&cmd->allocator);
    311 
    312   if(!cmd->args.human_readable) {
    313     printf("memory usage: %lu\n", (unsigned long)sz);
    314   } else {
    315     size_to_cstr(sz, SIZE_ALL, NULL, buf, sizeof(buf));
    316     printf("memory usage: %s\n", buf);
    317   }
    318 
    319 exit:
    320   if(molparam) SHTR(isotope_metadata_ref_put(molparam));
    321   if(lines) SHTR(line_list_ref_put(lines));
    322   return res;
    323 error:
    324   goto exit;
    325 }
    326 
    327 /*******************************************************************************
    328  * The program
    329  ******************************************************************************/
    330 int
    331 main(int argc, char** argv)
    332 {
    333   struct args args = ARGS_DEFAULT;
    334   struct cmd cmd = CMD_NULL;
    335   int err = 0;
    336   res_T res = RES_OK;
    337 
    338   if((res = args_init(&args, argc, argv)) != RES_OK) goto error;
    339 
    340   if((res = cmd_init(&cmd, &args)) != RES_OK) goto error;
    341   if((res = cmd_run(&cmd)) != RES_OK) goto error;
    342 
    343 exit:
    344   cmd_release(&cmd);
    345   CHK(mem_allocated_size() == 0);
    346   return err;
    347 error:
    348   err = 1;
    349   goto exit;
    350 }