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 }