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 }