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 (10639B)


      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 check_line_data;
     41   int internal_format;
     42   int verbose; /* Verbosity level */
     43   int print_info;
     44   int human_readable;
     45   int quit;
     46 };
     47 static const struct args ARGS_DEFAULT = {0};
     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 [-acHhisv] [-l lines] [-m molparam] [-o output]\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, "acHhil:m:o:sv")) != -1) {
     78     switch(opt) {
     79       case 'a': args->bench_line_access = 1; break;
     80       case 'c': args->check_line_data = 1; break;
     81       case 'H': args->human_readable = 1; break;
     82       case 'h': usage(stdout); args->quit = 1; break;
     83       case 'i': args->print_info = 1; break;
     84       case 'l': args->lines = optarg; break;
     85       case 'm': args->molparam = optarg; break;
     86       case 'o': args->output = optarg; break;
     87       case 's': args->internal_format = 1; break;
     88       case 'v': args->verbose += (args->verbose < 3); break;
     89       default: res = RES_BAD_ARG; break;
     90     }
     91     if(res != RES_OK) {
     92       if(optarg) {
     93         fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n",
     94           argv[0], optarg, opt);
     95       }
     96       goto error;
     97     }
     98   }
     99 
    100   if(!args->molparam && !args->lines) {
    101     fprintf(stderr, "neither lines nor isotopologues are defined\n");
    102     res = RES_BAD_ARG;
    103     goto error;
    104   }
    105 
    106 exit:
    107   return res;
    108 error:
    109   usage(stderr);
    110   goto exit;
    111 }
    112 
    113 static void
    114 cmd_release(struct cmd* cmd)
    115 {
    116   if(cmd->shtr) SHTR(ref_put(cmd->shtr));
    117   if(cmd->allocator_is_init) mem_shutdown_regular_allocator(&cmd->allocator);
    118 }
    119 
    120 static res_T
    121 cmd_init(struct cmd* cmd, const struct args* args)
    122 {
    123   struct shtr_create_args create_args = SHTR_CREATE_ARGS_DEFAULT;
    124   res_T res = RES_OK;
    125 
    126   ASSERT(cmd && args);
    127 
    128   cmd->args = *args;
    129 
    130   mem_init_regular_allocator(&cmd->allocator);
    131   cmd->allocator_is_init = 1;
    132 
    133   create_args.allocator = &cmd->allocator;
    134   create_args.verbose = args->verbose;
    135   if((res = shtr_create(&create_args, &cmd->shtr)) != RES_OK) goto error;
    136 
    137 exit:
    138   return res;
    139 error:
    140   cmd_release(cmd);
    141   goto exit;
    142 }
    143 
    144 static res_T
    145 load_molparam(const struct cmd* cmd, struct shtr_isotope_metadata** molparam)
    146 {
    147   ASSERT(cmd && molparam && cmd->args.molparam);
    148   if(!strcmp(cmd->args.molparam, STDIN_NAME)) {
    149     return shtr_isotope_metadata_load_stream(cmd->shtr, stdin, "stdin", molparam);
    150   } else {
    151     return shtr_isotope_metadata_load(cmd->shtr, cmd->args.molparam, molparam);
    152   }
    153 }
    154 
    155 static res_T
    156 load_lines_binary(const struct cmd* cmd, struct shtr_line_list** lines)
    157 {
    158   struct shtr_line_list_read_args args = SHTR_LINE_LIST_READ_ARGS_NULL;
    159   FILE* fp = NULL;
    160   res_T res = RES_OK;
    161 
    162   ASSERT(cmd && lines && cmd->args.lines);
    163 
    164   if(strcmp(cmd->args.lines, STDIN_NAME)) {
    165     args.filename = cmd->args.lines;
    166   } else {
    167     args.file = stdin;
    168     args.filename = "stdin";
    169   }
    170 
    171   res = shtr_line_list_read(cmd->shtr, &args, lines);
    172   if(res != RES_OK) goto error;
    173 
    174 exit:
    175   if(fp && fp != stdin) CHK(fclose(fp) == 0);
    176   return res;
    177 error:
    178   goto exit;
    179 }
    180 
    181 static res_T
    182 load_lines_hitran(const struct cmd* cmd, struct shtr_line_list** lines)
    183 {
    184   struct shtr_line_list_load_args args = SHTR_LINE_LIST_LOAD_ARGS_NULL__;
    185   res_T res = RES_OK;
    186 
    187   ASSERT(cmd && lines && cmd->args.lines);
    188 
    189   if(strcmp(cmd->args.lines, STDIN_NAME)) {
    190     args.filename = cmd->args.lines;
    191   } else {
    192     args.file = stdin;
    193     args.filename = "stdin";
    194   }
    195 
    196   res = shtr_line_list_load(cmd->shtr, &args, lines);
    197   if(res != RES_OK) goto error;
    198 
    199 exit:
    200   return res;
    201 error:
    202   goto exit;
    203 }
    204 
    205 static res_T
    206 load_lines(const struct cmd* cmd, struct shtr_line_list** lines)
    207 {
    208   ASSERT(cmd && lines && cmd->args.lines);
    209   if(cmd->args.internal_format) {
    210     return load_lines_binary(cmd, lines);
    211   } else {
    212     return load_lines_hitran(cmd, lines);
    213   }
    214 }
    215 
    216 static void
    217 lines_info(const struct shtr_line_list* list)
    218 {
    219   struct shtr_line_list_info info = SHTR_LINE_LIST_INFO_NULL;
    220   ASSERT(list);
    221 
    222   SHTR(line_list_get_info(list, &info));
    223 
    224   #define PRINT(Name) \
    225     printf("%-18s in [%12.4e,%12.4e]; error: %e\n", \
    226       STR(Name), 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 
    237 static void
    238 print_memsz(const struct cmd* cmd)
    239 {
    240   char buf[128] = {0};
    241   size_t sz = 0;
    242   ASSERT(cmd);
    243 
    244   sz = MEM_ALLOCATED_SIZE(&cmd->allocator);
    245   if(!cmd->args.human_readable) {
    246     printf("memory usage: %lu byt%s\n", (unsigned long)sz, sz > 0 ? "es" : "e");
    247   } else {
    248     size_to_cstr(sz, SIZE_ALL, NULL, buf, sizeof(buf));
    249     printf("memory usage: %s\n", buf);
    250   }
    251 }
    252 
    253 static res_T
    254 write_lines(const struct cmd* cmd, const struct shtr_line_list* list)
    255 {
    256   struct shtr_line_list_write_args args = SHTR_LINE_LIST_WRITE_ARGS_NULL;
    257   res_T res = RES_OK;
    258   ASSERT(cmd && list && cmd->args.output);
    259 
    260   args.filename = cmd->args.output;
    261   res = shtr_line_list_write(list, &args);
    262   if(res != RES_OK) goto error;
    263 
    264 exit:
    265   return res;
    266 error:
    267   goto exit;
    268 }
    269 
    270 static void
    271 bench_line_access(struct shtr_line_list* lines)
    272 {
    273   struct shtr_line line;
    274   struct time t0, t1;
    275   const size_t read_count = 10000;
    276   double lines_per_second = 0;
    277   int64_t usec = 0;
    278   size_t i, n;
    279 
    280   ASSERT(lines);
    281 
    282   SHTR(line_list_get_size(lines, &n));
    283 
    284   /* Linear access */
    285   time_current(&t0);
    286   FOR_EACH(i, 0, MMIN(n, read_count)) {
    287     SHTR(line_list_at(lines, i, &line));
    288   }
    289   usec = time_val(time_sub(&t0, time_current(&t1), &t0), TIME_USEC);
    290   lines_per_second = 1.e9 * (double)n / (double)usec;
    291   printf("linear access: %e lps\n", lines_per_second);
    292 
    293   /* Random access */
    294   time_current(&t0);
    295   FOR_EACH(i, 0, read_count) {
    296     const double r = (double)rand() / (double)(RAND_MAX-1);
    297     const size_t iline = (size_t)(r * (double)n);
    298     SHTR(line_list_at(lines, iline, &line));
    299   }
    300   usec = time_val(time_sub(&t0, time_current(&t1), &t0), TIME_USEC);
    301   lines_per_second = 1.e9 * (double)read_count / (double)usec;
    302   printf("random access: %e lps\n", lines_per_second);
    303 }
    304 
    305 static res_T
    306 check_line(const struct shtr_line* ln)
    307 {
    308   /* Check NaN */
    309   if(ln->wavenumber != ln->wavenumber
    310   || ln->intensity != ln->intensity
    311   || ln->gamma_air != ln->gamma_air
    312   || ln->gamma_self != ln->gamma_self
    313   || ln->lower_state_energy != ln->lower_state_energy
    314   || ln->n_air != ln->n_air
    315   || ln->delta_air != ln->delta_air) {
    316     return RES_BAD_ARG;
    317   }
    318 
    319   if(ln->wavenumber < 0
    320   || ln->intensity < 0
    321   || ln->gamma_air < 0
    322   || ln->gamma_self < 0
    323   || ln->lower_state_energy < 0
    324   || ln->molecule_id < 0
    325   || ln->molecule_id > 99
    326   || ln->isotope_id_local < 0
    327   || ln->isotope_id_local > 9) {
    328     return RES_BAD_ARG;
    329   }
    330 
    331   return RES_OK;
    332 }
    333 
    334 static res_T
    335 check_line_list(struct shtr_line_list* list)
    336 {
    337   size_t nerr = 0;
    338   size_t i, n;
    339   res_T res = RES_OK;
    340 
    341   SHTR(line_list_get_size(list, &n));
    342 
    343   FOR_EACH(i, 0, n) {
    344     struct shtr_line line = SHTR_LINE_NULL;
    345 
    346     SHTR(line_list_at(list, i, &line));
    347     if((res = check_line(&line)) != RES_OK) {
    348       ++nerr;
    349       fprintf(stderr,
    350         "invalid line %lu: "
    351         "nu=%-12.4e "
    352         "S=%-12.4e "
    353         "Yair=%-12.4e "
    354         "Yself=%-12.4e "
    355         "E=%-12.4e "
    356         "n=%-12.4e "
    357         "d=%-12.4e\n",
    358         (unsigned long)i,
    359         line.wavenumber,
    360         line.intensity,
    361         line.gamma_air,
    362         line.gamma_self,
    363         line.lower_state_energy,
    364         line.n_air,
    365         line.delta_air);
    366     }
    367   }
    368 
    369   return nerr > 0 ? RES_BAD_ARG : RES_OK;
    370 }
    371 
    372 static res_T
    373 cmd_run(const struct cmd* cmd)
    374 {
    375   struct shtr_isotope_metadata* molparam = NULL;
    376   struct shtr_line_list* lines = NULL;
    377   res_T res = RES_OK;
    378   ASSERT(cmd);
    379 
    380   if(cmd->args.molparam) {
    381     if((res = load_molparam(cmd, &molparam)) != RES_OK)  goto error;
    382   }
    383   if(cmd->args.lines) {
    384     if((res = load_lines(cmd, &lines)) != RES_OK) goto error;
    385     if(cmd->args.print_info) lines_info(lines);
    386     if(cmd->args.check_line_data) {
    387       res = check_line_list(lines);
    388       if(res != RES_OK) goto error;
    389     }
    390     if(cmd->args.bench_line_access) bench_line_access(lines);
    391     if(cmd->args.output) {
    392       res = write_lines(cmd, lines);
    393       if(res != RES_OK) goto error;
    394     }
    395   }
    396   if(cmd->args.print_info) print_memsz(cmd);
    397 
    398 exit:
    399   if(molparam) SHTR(isotope_metadata_ref_put(molparam));
    400   if(lines) SHTR(line_list_ref_put(lines));
    401   return res;
    402 error:
    403   goto exit;
    404 }
    405 
    406 /*******************************************************************************
    407  * The program
    408  ******************************************************************************/
    409 int
    410 main(int argc, char** argv)
    411 {
    412   struct args args = ARGS_DEFAULT;
    413   struct cmd cmd = CMD_NULL;
    414   int err = 0;
    415   res_T res = RES_OK;
    416 
    417   if((res = args_init(&args, argc, argv)) != RES_OK) goto error;
    418   if(args.quit) goto exit;
    419 
    420   if((res = cmd_init(&cmd, &args)) != RES_OK) goto error;
    421   if((res = cmd_run(&cmd)) != RES_OK) goto error;
    422 
    423 exit:
    424   cmd_release(&cmd);
    425   CHK(mem_allocated_size() == 0);
    426   return err;
    427 error:
    428   err = 1;
    429   goto exit;
    430 }