sln_build.c (10326B)
1 /* Copyright (C) 2022, 2026 |Méso|Star> (contact@meso-star.com) 2 * Copyright (C) 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 200809L /* strtok_r */ 20 21 #include "sln.h" 22 23 #include <rsys/cstr.h> 24 #include <rsys/mem_allocator.h> 25 #include <rsys/rsys.h> 26 27 #include <stdio.h> 28 #include <string.h> 29 #include <unistd.h> /* getopt */ 30 31 enum line_list_format { 32 LINE_LIST_HITRAN, 33 LINE_LIST_SHTR, 34 LINE_LIST_FORMAT_COUNT__ 35 }; 36 37 struct args { 38 /* Spectroscopic parameters */ 39 const char* lines; 40 const char* molparams; 41 enum sln_line_profile line_profile; 42 43 const char* output; 44 45 /* Thermodynamic properties */ 46 const char* mixture; 47 double pressure; /* [atm] */ 48 double temperature; /* [K] */ 49 50 /* Polyline */ 51 double mesh_decimation_err; 52 unsigned nvertices_hint; 53 enum sln_mesh_type mesh_type; 54 55 /* Miscellaneous */ 56 int quit; 57 int verbose; 58 enum line_list_format line_format; 59 }; 60 #define ARGS_DEFAULT__ { \ 61 /* Spectroscopic parameters */ \ 62 NULL, /* line list */ \ 63 NULL, /* Isotopologue metadata */ \ 64 SLN_LINE_PROFILE_VOIGT, /* Line profile */ \ 65 \ 66 NULL, /* Output */ \ 67 \ 68 /* Thermodynamic properties */ \ 69 NULL, \ 70 -1, /* Pressure [atm] */ \ 71 -1, /* Temperature [K] */ \ 72 \ 73 /* Polyline */ \ 74 0.01, \ 75 16, \ 76 SLN_MESH_UPPER, \ 77 \ 78 /* Miscellaneous */ \ 79 0, /* Quit */ \ 80 0, /* Verbose */ \ 81 LINE_LIST_HITRAN /* lines_format */ \ 82 } 83 static const struct args ARGS_DEFAULT = ARGS_DEFAULT__; 84 85 struct cmd { 86 struct sln_device* sln; 87 88 struct shtr* shtr; 89 struct shtr_line_list* lines; 90 struct shtr_isotope_metadata* molparams; 91 92 FILE* output; 93 }; 94 static const struct cmd CMD_NULL = {0}; 95 96 /******************************************************************************* 97 * Helper functions 98 ******************************************************************************/ 99 static void 100 usage(FILE* stream) 101 { 102 fprintf(stream, 103 "usage: sln-build [-hsv] [-e polyline_opt[:polyline_opt ...]] [-l line_profile]\n" 104 " [-o accel_struct] -P pressure -T temperature -m molparams\n" 105 " -x mixture [lines]\n"); 106 } 107 108 static res_T 109 parse_line_profile(const char* str, enum sln_line_profile* profile) 110 { 111 res_T res = RES_OK; 112 ASSERT(str && profile); 113 114 if(!strcmp(str, "voigt")) { 115 *profile = SLN_LINE_PROFILE_VOIGT; 116 117 } else { 118 fprintf(stderr, "invalid line profile '%s'\n", str); 119 res = RES_BAD_ARG; 120 goto error; 121 } 122 123 exit: 124 return res; 125 error: 126 goto exit; 127 } 128 129 static res_T 130 parse_mesh_type(const char* str, enum sln_mesh_type* type) 131 { 132 res_T res = RES_OK; 133 ASSERT(str && type); 134 135 if(!strcmp(str, "fit")) { 136 *type = SLN_MESH_FIT; 137 } else if(!strcmp(str, "upper")) { 138 *type = SLN_MESH_UPPER; 139 } else { 140 fprintf(stderr, "invalid mesh type `%s'\n", str); 141 res = RES_BAD_ARG; 142 goto error; 143 } 144 145 exit: 146 return res; 147 error: 148 goto exit; 149 } 150 151 static res_T 152 parse_polyline_opt(const char* str, void* ptr) 153 { 154 enum { ERR, MESH, VCOUNT } opt; 155 char buf[BUFSIZ]; 156 157 struct args* args = ptr; 158 159 char* key = NULL; 160 char* val = NULL; 161 char* tk_ctx = NULL; 162 res_T res = RES_OK; 163 164 ASSERT(str && ptr); 165 166 if(strlen(str) >= sizeof(buf) -1/*NULL char*/) { 167 fprintf(stderr, "could not duplicate polyline option `%s'\n", str); 168 res = RES_MEM_ERR; 169 goto error; 170 } 171 172 strncpy(buf, str, sizeof(buf)); 173 174 key = strtok_r(buf, "=", &tk_ctx); 175 val = strtok_r(NULL, "", &tk_ctx); 176 177 if(!strcmp(key, "err")) opt = ERR; 178 else if(!strcmp(key, "mesh")) opt = MESH; 179 else if(!strcmp(key, "vcount")) opt = MESH; 180 else { 181 fprintf(stderr, "invalid polyline option `%s'\n", key); 182 res = RES_BAD_ARG; 183 goto error; 184 } 185 186 switch(opt) { 187 case ERR: 188 res = cstr_to_double(val, &args->mesh_decimation_err); 189 if(res == RES_OK && args->mesh_decimation_err < 0) res = RES_BAD_ARG; 190 break; 191 case MESH: 192 res = parse_mesh_type(val, &args->mesh_type); 193 break; 194 case VCOUNT: 195 res = cstr_to_uint(val, &args->nvertices_hint); 196 break; 197 default: FATAL("Unreachable code\n"); break; 198 } 199 200 if(res != RES_OK) { 201 fprintf(stderr, 202 "error while parsing the polyline option `%s' -- %s\n", 203 str, res_to_cstr(res)); 204 goto error; 205 } 206 207 exit: 208 return res; 209 error: 210 goto exit; 211 } 212 213 static res_T 214 args_init(struct args* args, int argc, char** argv) 215 { 216 int opt = 0; 217 res_T res = RES_OK; 218 219 ASSERT(args); 220 221 *args = ARGS_DEFAULT; 222 223 while((opt = getopt(argc, argv, "e:hl:o:P:sT:m:vx:")) != -1) { 224 switch(opt) { 225 case 'e': 226 res = cstr_parse_list(optarg, ':', parse_polyline_opt, args); 227 break; 228 case 'h': 229 usage(stdout); 230 args->quit = 1; 231 goto exit; 232 case 'l': 233 res = parse_line_profile(optarg, &args->line_profile); 234 break; 235 case 'o': args->output = optarg; break; 236 case 'P': 237 res = cstr_to_double(optarg, &args->pressure); 238 if(res == RES_OK && args->pressure < 0) res = RES_BAD_ARG; 239 break; 240 case 's': args->line_format = LINE_LIST_SHTR; break; 241 case 'T': 242 res = cstr_to_double(optarg, &args->temperature); 243 if(res == RES_OK && args->temperature < 0) res = RES_BAD_ARG; 244 break; 245 case 'm': args->molparams = optarg; break; 246 case 'v': args->verbose += (args->verbose < 3); break; 247 case 'x': args->mixture = optarg; break; 248 default: res = RES_BAD_ARG; break; 249 } 250 251 if(res != RES_OK) { 252 if(optarg) { 253 fprintf(stderr, "%s: invalid option argument '%s' -- '%c'\n", 254 argv[0], optarg, opt); 255 } 256 goto error; 257 } 258 } 259 260 if(optind < argc) args->lines = argv[optind]; 261 262 #define MANDATORY(Cond, Name, Opt) { \ 263 if(!(Cond)) { \ 264 fprintf(stderr, "%s: %s missing -- option '-%c'\n", argv[0], (Name), (Opt)); \ 265 res = RES_BAD_ARG; \ 266 goto error; \ 267 } \ 268 } (void)0 269 MANDATORY(args->pressure >= 0, "pressure", 'P'); 270 MANDATORY(args->temperature >= 0, "temperature", 'T'); 271 MANDATORY(args->molparams, "molparams", 'm'); 272 MANDATORY(args->mixture, "mixture", 'x'); 273 #undef MANDATORY 274 275 exit: 276 return res; 277 error: 278 usage(stderr); 279 goto exit; 280 } 281 282 static res_T 283 load_lines_hitran(struct cmd* cmd, const struct args* args) 284 { 285 struct shtr_line_list_load_args load_args = SHTR_LINE_LIST_LOAD_ARGS_NULL; 286 ASSERT(cmd && args); 287 288 if(args->lines != NULL) { 289 load_args.filename = args->lines; 290 } else { 291 load_args.filename = "stdin"; 292 load_args.file = stdin; 293 } 294 295 return shtr_line_list_load(cmd->shtr, &load_args, &cmd->lines); 296 } 297 298 static res_T 299 load_lines_shtr(struct cmd* cmd, const struct args* args) 300 { 301 FILE* fp = NULL; 302 res_T res = RES_OK; 303 304 ASSERT(cmd && args); 305 306 if(args->lines == NULL) { 307 fp = stdin; 308 } else { 309 fp = fopen(args->lines, "r"); 310 if(!fp) { 311 fprintf(stderr, "error opening file `%s' -- %s\n", 312 args->lines, strerror(errno)); 313 res = RES_IO_ERR; 314 goto error; 315 } 316 } 317 318 res = shtr_line_list_create_from_stream(cmd->shtr, fp, &cmd->lines); 319 if(res != RES_OK) goto error; 320 321 exit: 322 if(cmd->lines) SHTR(line_list_ref_put(cmd->lines)); 323 if(fp && fp != stdin) CHK(fclose(fp) == 0); 324 return res; 325 error: 326 goto exit; 327 } 328 329 static res_T 330 load_lines(struct cmd* cmd, const struct args* args) 331 { 332 res_T res = RES_OK; 333 switch(args->line_format) { 334 case LINE_LIST_HITRAN: res = load_lines_hitran(cmd, args); break; 335 case LINE_LIST_SHTR: res = load_lines_shtr(cmd, args); break; 336 default: FATAL("Unreachable code\n"); break; 337 } 338 return res; 339 } 340 341 static res_T 342 setup_output(struct cmd* cmd, const struct args* args) 343 { 344 res_T res = RES_OK; 345 ASSERT(cmd && args); 346 347 if(!args->output) { 348 cmd->output = stdout; 349 350 } else { 351 cmd->output = fopen(args->output, "w"); 352 if(!cmd->output) { 353 fprintf(stderr, "error opening file `%s' -- %s\n", 354 args->output, strerror(errno)); 355 res = RES_IO_ERR; 356 goto error; 357 } 358 } 359 360 exit: 361 return res; 362 error: 363 if(cmd->output && cmd->output != stdout) CHK(fclose(cmd->output) == 0); 364 goto exit; 365 } 366 367 static void 368 cmd_release(struct cmd* cmd) 369 { 370 ASSERT(cmd); 371 if(cmd->sln) SLN(device_ref_put(cmd->sln)); 372 if(cmd->shtr) SHTR(ref_put(cmd->shtr)); 373 if(cmd->lines) SHTR(line_list_ref_put(cmd->lines)); 374 if(cmd->molparams) SHTR(isotope_metadata_ref_put(cmd->molparams)); 375 } 376 377 static res_T 378 cmd_init(struct cmd* cmd, const struct args* args) 379 { 380 struct sln_device_create_args sln_args = SLN_DEVICE_CREATE_ARGS_DEFAULT; 381 struct shtr_create_args shtr_args = SHTR_CREATE_ARGS_DEFAULT; 382 res_T res = RES_OK; 383 384 ASSERT(cmd && args); 385 386 *cmd = CMD_NULL; 387 388 shtr_args.verbose = args->verbose; 389 res = shtr_create(&shtr_args, &cmd->shtr); 390 if(res != RES_OK) goto error; 391 392 sln_args.verbose = args->verbose; 393 res = sln_device_create(&sln_args, &cmd->sln); 394 if(res != RES_OK) goto error; 395 396 res = load_lines(cmd, args); 397 if(res != RES_OK) goto error; 398 399 res = shtr_isotope_metadata_load(cmd->shtr, args->molparams, &cmd->molparams); 400 if(res != RES_OK) goto error; 401 402 res = setup_output(cmd, args); 403 if(res != RES_OK) goto error; 404 405 exit: 406 return res; 407 error: 408 cmd_release(cmd); 409 goto exit; 410 } 411 412 /******************************************************************************* 413 * The program 414 ******************************************************************************/ 415 int 416 main(int argc, char** argv) 417 { 418 struct args args = ARGS_DEFAULT; 419 struct cmd cmd = CMD_NULL; 420 int err = 0; 421 res_T res = RES_OK; 422 423 if((res = args_init(&args, argc, argv)) != RES_OK) goto error; 424 if(args.quit) goto exit; 425 426 if((res = cmd_init(&cmd, &args)) != RES_OK) goto error; 427 428 exit: 429 cmd_release(&cmd); 430 CHK(mem_allocated_size() == 0); 431 return err; 432 error: 433 err = 1; 434 goto exit; 435 }