shtr_main.c (9193B)
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 print_info; 43 int human_readable; 44 int quit; 45 }; 46 static const struct args ARGS_DEFAULT = {0}; 47 48 struct cmd { 49 struct mem_allocator allocator; 50 int allocator_is_init; 51 struct args args; 52 struct shtr* shtr; 53 }; 54 static const struct cmd CMD_NULL = {0}; 55 56 /******************************************************************************* 57 * Helper functions 58 ******************************************************************************/ 59 static INLINE void 60 usage(FILE* stream) 61 { 62 fprintf(stream, 63 "usage: shtr [-aHhisv] [-l lines] [-m molparam] [-o output]\n"); 64 } 65 66 static res_T 67 args_init(struct args* args, int argc, char** argv) 68 { 69 int opt = 0; 70 res_T res = RES_OK; 71 72 ASSERT(args); 73 74 *args = ARGS_DEFAULT; 75 76 while((opt = getopt(argc, argv, "aHhil:m:o:sv")) != -1) { 77 switch(opt) { 78 case 'a': args->bench_line_access = 1; break; 79 case 'H': args->human_readable = 1; break; 80 case 'h': usage(stdout); args->quit = 1; break; 81 case 'i': args->print_info = 1; break; 82 case 'l': args->lines = optarg; break; 83 case 'm': args->molparam = optarg; break; 84 case 'o': args->output = optarg; break; 85 case 's': args->internal_format = 1; break; 86 case 'v': args->verbose += (args->verbose < 3); 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 if(!args->molparam && !args->lines) { 99 fprintf(stderr, "neither lines nor isotopologues are defined\n"); 100 res = RES_BAD_ARG; 101 goto error; 102 } 103 104 exit: 105 return res; 106 error: 107 usage(stderr); 108 goto exit; 109 } 110 111 static void 112 cmd_release(struct cmd* cmd) 113 { 114 if(cmd->shtr) SHTR(ref_put(cmd->shtr)); 115 if(cmd->allocator_is_init) mem_shutdown_regular_allocator(&cmd->allocator); 116 } 117 118 static res_T 119 cmd_init(struct cmd* cmd, const struct args* args) 120 { 121 struct shtr_create_args create_args = SHTR_CREATE_ARGS_DEFAULT; 122 res_T res = RES_OK; 123 124 ASSERT(cmd && args); 125 126 cmd->args = *args; 127 128 mem_init_regular_allocator(&cmd->allocator); 129 cmd->allocator_is_init = 1; 130 131 create_args.allocator = &cmd->allocator; 132 create_args.verbose = args->verbose; 133 if((res = shtr_create(&create_args, &cmd->shtr)) != RES_OK) goto error; 134 135 exit: 136 return res; 137 error: 138 cmd_release(cmd); 139 goto exit; 140 } 141 142 static res_T 143 load_molparam(const struct cmd* cmd, struct shtr_isotope_metadata** molparam) 144 { 145 ASSERT(cmd && molparam && cmd->args.molparam); 146 if(!strcmp(cmd->args.molparam, STDIN_NAME)) { 147 return shtr_isotope_metadata_load_stream(cmd->shtr, stdin, "stdin", molparam); 148 } else { 149 return shtr_isotope_metadata_load(cmd->shtr, cmd->args.molparam, molparam); 150 } 151 } 152 153 static res_T 154 load_lines_binary(const struct cmd* cmd, struct shtr_line_list** lines) 155 { 156 FILE* fp = NULL; 157 res_T res = RES_OK; 158 159 ASSERT(cmd && lines && cmd->args.lines); 160 161 if(!strcmp(cmd->args.lines, STDIN_NAME)) { 162 fp = stdin; 163 } else { 164 fp = fopen(cmd->args.lines, "r"); 165 if(!fp) { 166 fprintf(stderr, "%s: error opening file -- %s\n", 167 cmd->args.lines, strerror(errno)); 168 res = RES_IO_ERR; 169 goto error; 170 } 171 } 172 173 res = shtr_line_list_create_from_stream(cmd->shtr, fp, lines); 174 if(res != RES_OK) goto error; 175 176 exit: 177 if(fp && fp != stdin) CHK(fclose(fp) == 0); 178 return res; 179 error: 180 goto exit; 181 } 182 183 static res_T 184 load_lines_hitran(const struct cmd* cmd, struct shtr_line_list** lines) 185 { 186 struct shtr_line_list_load_args args = SHTR_LINE_LIST_LOAD_ARGS_NULL__; 187 res_T res = RES_OK; 188 189 ASSERT(cmd && lines && cmd->args.lines); 190 191 if(strcmp(cmd->args.lines, STDIN_NAME)) { 192 args.filename = cmd->args.lines; 193 } else { 194 args.file = stdin; 195 args.filename = "stdin"; 196 } 197 198 res = shtr_line_list_load(cmd->shtr, &args, lines); 199 if(res != RES_OK) goto error; 200 201 exit: 202 return res; 203 error: 204 goto exit; 205 } 206 207 static res_T 208 load_lines(const struct cmd* cmd, struct shtr_line_list** lines) 209 { 210 ASSERT(cmd && lines && cmd->args.lines); 211 if(cmd->args.internal_format) { 212 return load_lines_binary(cmd, lines); 213 } else { 214 return load_lines_hitran(cmd, lines); 215 } 216 } 217 218 static void 219 lines_info(const struct shtr_line_list* list) 220 { 221 struct shtr_line_list_info info = SHTR_LINE_LIST_INFO_NULL; 222 ASSERT(list); 223 224 SHTR(line_list_get_info(list, &info)); 225 226 #define PRINT(Name) \ 227 printf("%-18s in [%12.4e,%12.4e]; error: %e\n", \ 228 STR(Name), SPLIT2(info.Name.range), info.Name.err) 229 PRINT(wavenumber); 230 PRINT(intensity); 231 PRINT(gamma_air); 232 PRINT(gamma_self); 233 PRINT(lower_state_energy); 234 PRINT(n_air); 235 PRINT(delta_air); 236 #undef PRINT 237 } 238 239 static void 240 print_memsz(const struct cmd* cmd) 241 { 242 char buf[128] = {0}; 243 size_t sz = 0; 244 ASSERT(cmd); 245 246 sz = MEM_ALLOCATED_SIZE(&cmd->allocator); 247 if(!cmd->args.human_readable) { 248 printf("memory usage: %lu byt%s\n", (unsigned long)sz, sz > 0 ? "es" : "e"); 249 } else { 250 size_to_cstr(sz, SIZE_ALL, NULL, buf, sizeof(buf)); 251 printf("memory usage: %s\n", buf); 252 } 253 } 254 255 static res_T 256 write_lines(const struct cmd* cmd, const struct shtr_line_list* list) 257 { 258 FILE* fp = NULL; 259 res_T res = RES_OK; 260 ASSERT(cmd && list && cmd->args.output); 261 262 fp = fopen(cmd->args.output, "w"); 263 if(!fp) { 264 fprintf(stderr, "%s: error opening file -- %s\n", 265 cmd->args.output, strerror(errno)); 266 res = RES_IO_ERR; 267 goto error; 268 } 269 270 res = shtr_line_list_write(list, fp); 271 if(res != RES_OK) goto error; 272 273 exit: 274 if(fp) CHK(fclose(fp) == 0); 275 return res; 276 error: 277 goto exit; 278 } 279 280 static void 281 bench_line_access(struct shtr_line_list* lines) 282 { 283 struct shtr_line line; 284 struct time t0, t1; 285 const size_t read_count = 10000; 286 double lines_per_second = 0; 287 int64_t usec = 0; 288 size_t i, n; 289 290 ASSERT(lines); 291 292 SHTR(line_list_get_size(lines, &n)); 293 294 /* Linear access */ 295 time_current(&t0); 296 FOR_EACH(i, 0, MMIN(n, read_count)) { 297 SHTR(line_list_at(lines, i, &line)); 298 } 299 usec = time_val(time_sub(&t0, time_current(&t1), &t0), TIME_USEC); 300 lines_per_second = 1.e9 * (double)n / (double)usec; 301 printf("linear access: %e lps\n", lines_per_second); 302 303 /* Random access */ 304 time_current(&t0); 305 FOR_EACH(i, 0, read_count) { 306 const double r = (double)rand() / (double)(RAND_MAX-1); 307 const size_t iline = (size_t)(r * (double)n); 308 SHTR(line_list_at(lines, iline, &line)); 309 } 310 usec = time_val(time_sub(&t0, time_current(&t1), &t0), TIME_USEC); 311 lines_per_second = 1.e9 * (double)read_count / (double)usec; 312 printf("random access: %e lps\n", lines_per_second); 313 } 314 315 static res_T 316 cmd_run(const struct cmd* cmd) 317 { 318 struct shtr_isotope_metadata* molparam = NULL; 319 struct shtr_line_list* lines = NULL; 320 res_T res = RES_OK; 321 ASSERT(cmd); 322 323 if(cmd->args.molparam) { 324 if((res = load_molparam(cmd, &molparam)) != RES_OK) goto error; 325 } 326 if(cmd->args.lines) { 327 if((res = load_lines(cmd, &lines)) != RES_OK) goto error; 328 if(cmd->args.print_info) lines_info(lines); 329 if(cmd->args.bench_line_access) bench_line_access(lines); 330 if(cmd->args.output) { 331 res = write_lines(cmd, lines); 332 if(res != RES_OK) goto error; 333 } 334 } 335 if(cmd->args.print_info) print_memsz(cmd); 336 337 exit: 338 if(molparam) SHTR(isotope_metadata_ref_put(molparam)); 339 if(lines) SHTR(line_list_ref_put(lines)); 340 return res; 341 error: 342 goto exit; 343 } 344 345 /******************************************************************************* 346 * The program 347 ******************************************************************************/ 348 int 349 main(int argc, char** argv) 350 { 351 struct args args = ARGS_DEFAULT; 352 struct cmd cmd = CMD_NULL; 353 int err = 0; 354 res_T res = RES_OK; 355 356 if((res = args_init(&args, argc, argv)) != RES_OK) goto error; 357 if(args.quit) goto exit; 358 359 if((res = cmd_init(&cmd, &args)) != RES_OK) goto error; 360 if((res = cmd_run(&cmd)) != RES_OK) goto error; 361 362 exit: 363 cmd_release(&cmd); 364 CHK(mem_allocated_size() == 0); 365 return err; 366 error: 367 err = 1; 368 goto exit; 369 }