rnatm_main.c (12035B)
1 /* Copyright (C) 2022, 2023, 2025 Centre National de la Recherche Scientifique 2 * Copyright (C) 2022, 2023, 2025 Institut Pierre-Simon Laplace 3 * Copyright (C) 2022, 2023, 2025 Institut de Physique du Globe de Paris 4 * Copyright (C) 2022, 2023, 2025 |Méso|Star> (contact@meso-star.com) 5 * Copyright (C) 2022, 2023, 2025 Observatoire de Paris 6 * Copyright (C) 2022, 2023, 2025 Université de Reims Champagne-Ardenne 7 * Copyright (C) 2022, 2023, 2025 Université de Versaille Saint-Quentin 8 * Copyright (C) 2022, 2023, 2025 Université Paul Sabatier 9 * 10 * This program is free software: you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation, either version 3 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 22 23 #define _POSIX_C_SOURCE 200809L /* strdup, strtok_r */ 24 25 #include "rnatm.h" 26 27 #include <rsys/cstr.h> 28 #include <rsys/mem_allocator.h> 29 #include <rsys/rsys.h> 30 #include <rsys/stretchy_array.h> 31 32 #include <getopt.h> 33 #include <string.h> 34 35 struct args { 36 struct rnatm_create_args rnatm; 37 const char* vtk_filename; 38 int check; 39 int quit; 40 }; 41 #define ARGS_DEFAULT__ { RNATM_CREATE_ARGS_DEFAULT__, NULL, 0, 0 } 42 static const struct args ARGS_DEFAULT = ARGS_DEFAULT__; 43 44 /******************************************************************************* 45 * Helper functions 46 ******************************************************************************/ 47 static void 48 usage(FILE* stream) 49 { 50 ASSERT(stream); 51 fprintf(stream, 52 "usage: rnatm [-hNv] [-a aerosol_opt[:aerosol_opt ...]] [-c] [-d octrees]\n" 53 " [-i storage] [-n name] [-o storage] [-s nu0,nu1]\n" 54 " [-T optical_thickness] [-t thread_count] [-V definition]\n" 55 " -g gas_opt[:gas_opt ...]\n"); 56 } 57 58 static res_T 59 parse_gas_parameters(const char* str, void* ptr) 60 { 61 enum { MESH, CK, TEMP } iparam; 62 char buf[BUFSIZ]; 63 struct args* args = ptr; 64 char* key; 65 char* val; 66 char* tk_ctx; 67 res_T res = RES_OK; 68 ASSERT(args && str); 69 70 if(strlen(str) >= sizeof(buf) -1/*NULL char*/) { 71 fprintf(stderr, "Could not duplicate the gas parameter `%s'\n", str); 72 res = RES_MEM_ERR; 73 goto error; 74 } 75 strncpy(buf, str, sizeof(buf)); 76 77 key = strtok_r(buf, "=", &tk_ctx); 78 val = strtok_r(NULL, "", &tk_ctx); 79 80 if(!strcmp(key, "mesh")) iparam = MESH; 81 else if(!strcmp(key, "ck")) iparam = CK; 82 else if(!strcmp(key, "temp")) iparam = TEMP; 83 else { 84 fprintf(stderr, "Invalid gas parameter `%s'\n", key); 85 res = RES_BAD_ARG; 86 goto error; 87 } 88 89 if(!val) { 90 fprintf(stderr, "Invalid null value for gas parameter `%s'\n", key); 91 res = RES_BAD_ARG; 92 goto error; 93 } 94 95 switch(iparam) { 96 case MESH: 97 args->rnatm.gas.smsh_filename = strdup(val); 98 if(!args->rnatm.gas.smsh_filename) res = RES_MEM_ERR; 99 break; 100 case CK: 101 args->rnatm.gas.sck_filename = strdup(val); 102 if(!args->rnatm.gas.sck_filename) res = RES_MEM_ERR; 103 break; 104 case TEMP: 105 args->rnatm.gas.temperatures_filename = strdup(val); 106 if(!args->rnatm.gas.temperatures_filename) res = RES_MEM_ERR; 107 break; 108 default: FATAL("Unreachable code\n"); break; 109 } 110 if(res != RES_OK) { 111 fprintf(stderr, "Unable to parse the gas parameter `%s' -- %s\n", 112 str, res_to_cstr(res)); 113 goto error; 114 } 115 116 exit: 117 return res; 118 error: 119 goto exit; 120 } 121 122 static res_T 123 parse_aerosol_parameters(const char* str, void* ptr) 124 { 125 enum { MESH, NAME, RADPROP, PHASEFN, PHASEIDS } iparam; 126 struct rnatm_aerosol_args* aerosol = NULL; 127 char buf[BUFSIZ]; 128 struct args* args = ptr; 129 char* key; 130 char* val; 131 char* tk_ctx; 132 res_T res = RES_OK; 133 ASSERT(args && str); 134 135 if(strlen(str) >= sizeof(buf) -1/*NULL char*/) { 136 fprintf(stderr, "Could not duplicate the aerosol parameter `%s'\n", str); 137 res = RES_MEM_ERR; 138 goto error; 139 } 140 strncpy(buf, str, sizeof(buf)); 141 142 key = strtok_r(buf, "=", &tk_ctx); 143 val = strtok_r(NULL, "", &tk_ctx); 144 145 if(!strcmp(key, "mesh")) iparam = MESH; 146 else if(!strcmp(key, "name")) iparam = NAME; 147 else if(!strcmp(key, "radprop")) iparam = RADPROP; 148 else if(!strcmp(key, "phasefn")) iparam = PHASEFN; 149 else if(!strcmp(key, "phaseids")) iparam = PHASEIDS; 150 else { 151 fprintf(stderr, "Invalid aerosol parameter `%s'\n", key); 152 res = RES_BAD_ARG; 153 goto error; 154 } 155 156 if(!val) { 157 fprintf(stderr, "Invalid null value for aerosol parameter `%s'\n", key); 158 res = RES_BAD_ARG; 159 goto error; 160 } 161 162 ASSERT(args->rnatm.naerosols); 163 aerosol = args->rnatm.aerosols + (args->rnatm.naerosols - 1); 164 165 switch(iparam) { 166 case MESH: 167 aerosol->smsh_filename = strdup(val); 168 if(!aerosol->smsh_filename) res = RES_MEM_ERR; 169 break; 170 case NAME: 171 aerosol->name = strdup(val); 172 if(!aerosol->name) res = RES_MEM_ERR; 173 break; 174 case RADPROP: 175 aerosol->sars_filename = strdup(val); 176 if(!aerosol->sars_filename) res = RES_MEM_ERR; 177 break; 178 case PHASEFN: 179 aerosol->phase_fn_lst_filename = strdup(val); 180 if(!aerosol->phase_fn_lst_filename) res = RES_MEM_ERR; 181 break; 182 case PHASEIDS: 183 aerosol->phase_fn_ids_filename = strdup(val); 184 if(!aerosol->phase_fn_ids_filename) res = RES_MEM_ERR; 185 break; 186 default: FATAL("Unreachable code\n"); break; 187 } 188 if(res != RES_OK) { 189 fprintf(stderr, "Unable to parse the aerosol parameter `%s' -- %s\n", 190 str, res_to_cstr(res)); 191 goto error; 192 } 193 194 exit: 195 return res; 196 error: 197 goto exit; 198 } 199 200 static res_T 201 parse_spectral_range(struct args* args, char* str) 202 { 203 size_t len = 0; 204 res_T res = RES_OK; 205 ASSERT(args && str); 206 207 res = cstr_to_list_double(str, ',', args->rnatm.spectral_range, &len, 2); 208 if(res == RES_OK && len != 2) res = RES_BAD_ARG; 209 if(res != RES_OK) goto error; 210 211 if(args->rnatm.spectral_range[0] < 0 212 || args->rnatm.spectral_range[1] < 0 213 || args->rnatm.spectral_range[0] > args->rnatm.spectral_range[1]) 214 goto error; 215 216 exit: 217 return res; 218 error: 219 goto exit; 220 } 221 222 static void 223 args_release(struct args* args) 224 { 225 size_t i; 226 ASSERT(args); 227 if(args->rnatm.gas.smsh_filename) free(args->rnatm.gas.smsh_filename); 228 if(args->rnatm.gas.sck_filename) free(args->rnatm.gas.sck_filename); 229 if(args->rnatm.gas.temperatures_filename) 230 free(args->rnatm.gas.temperatures_filename); 231 if(args->rnatm.octrees_storage) CHK(fclose(args->rnatm.octrees_storage) == 0); 232 233 FOR_EACH(i, 0, args->rnatm.naerosols) { 234 struct rnatm_aerosol_args* aerosol = args->rnatm.aerosols + i; 235 if(aerosol->name) free(aerosol->name); 236 if(aerosol->smsh_filename) free(aerosol->smsh_filename); 237 if(aerosol->sars_filename) free(aerosol->sars_filename); 238 if(aerosol->phase_fn_ids_filename) free(aerosol->phase_fn_ids_filename); 239 if(aerosol->phase_fn_lst_filename) free(aerosol->phase_fn_lst_filename); 240 } 241 sa_release(args->rnatm.aerosols); 242 *args = ARGS_DEFAULT; 243 } 244 245 static res_T 246 args_init(struct args* args, int argc, char** argv) 247 { 248 const char* storage_filename = NULL; 249 size_t i = 0; 250 res_T res = RES_OK; 251 int opt; 252 ASSERT(args && argc && argv); 253 254 *args = ARGS_DEFAULT; 255 256 while((opt = getopt(argc, argv, "a:cd:g:hi:Nn:o:s:T:t:V:v")) != -1) { 257 switch(opt) { 258 case 'a': 259 (void)sa_add(args->rnatm.aerosols, 1); 260 args->rnatm.aerosols[args->rnatm.naerosols] = RNATM_AEROSOL_ARGS_NULL; 261 args->rnatm.naerosols += 1; 262 res = cstr_parse_list(optarg, ':', parse_aerosol_parameters, args); 263 break; 264 case 'c': args->check = 1; break; 265 case 'd': args->vtk_filename = optarg; break; 266 case 'g': 267 res = cstr_parse_list(optarg, ':', parse_gas_parameters, args); 268 break; 269 case 'h': 270 usage(stdout); 271 args_release(args); 272 args->quit = 1; 273 goto exit; 274 case 'N': args->rnatm.precompute_normals = 1; break; 275 case 'n': args->rnatm.name = optarg; break; 276 case 'o': 277 args->rnatm.load_octrees_from_storage = 0; 278 storage_filename = optarg; 279 break; 280 case 'i': 281 args->rnatm.load_octrees_from_storage = 1; 282 storage_filename = optarg; 283 break; 284 case 's': 285 res = parse_spectral_range(args, optarg); 286 break; 287 case 'T': 288 res = cstr_to_double(optarg, &args->rnatm.optical_thickness); 289 if(res != RES_OK && args->rnatm.optical_thickness<=0) res = RES_BAD_ARG; 290 break; 291 case 't': 292 res = cstr_to_uint(optarg, &args->rnatm.nthreads); 293 if(res == RES_OK && !args->rnatm.nthreads) res = RES_BAD_ARG; 294 break; 295 case 'V': 296 res = cstr_to_uint(optarg, &args->rnatm.grid_definition_hint); 297 if(res == RES_OK && !args->rnatm.grid_definition_hint) res = RES_BAD_ARG; 298 break; 299 case 'v': args->rnatm.verbose = 1; break; 300 default: res = RES_BAD_ARG; break; 301 } 302 if(res != RES_OK) { 303 if(optarg) { 304 fprintf(stderr, "%s: invalid option args `%s' -- `%c'\n", 305 argv[0], optarg, opt); 306 } 307 goto error; 308 } 309 } 310 311 if(storage_filename) { 312 const char* mode = args->rnatm.load_octrees_from_storage ? "r" : "w+"; 313 args->rnatm.octrees_storage = fopen(storage_filename, mode); 314 if(!args->rnatm.octrees_storage) { 315 fprintf(stderr, "Unable to open octree storage file %s\n", 316 storage_filename); 317 res = RES_IO_ERR; 318 goto error; 319 } 320 } 321 322 /* Check the required options */ 323 if(!args->rnatm.gas.smsh_filename 324 || !args->rnatm.gas.sck_filename 325 || !args->rnatm.gas.temperatures_filename) { 326 fprintf(stderr, "Incomplete gas definition -- option `-g'\n"); 327 res = RES_BAD_ARG; 328 goto error; 329 } 330 331 FOR_EACH(i, 0, args->rnatm.naerosols) { 332 struct rnatm_aerosol_args* aerosol = args->rnatm.aerosols + i; 333 334 if(!aerosol->smsh_filename 335 || !aerosol->sars_filename 336 || !aerosol->phase_fn_ids_filename 337 || !aerosol->phase_fn_lst_filename) { 338 fprintf(stderr, "Incomplete %lu^th aerosol definition -- option `-a'\n", 339 (unsigned long)i); 340 res = RES_BAD_ARG; 341 goto error; 342 } 343 } 344 345 exit: 346 return res; 347 error: 348 usage(stderr); 349 args_release(args); 350 goto exit; 351 } 352 353 static res_T 354 write_vtk_octrees(struct rnatm* atm, const char* filename) 355 { 356 size_t octrees_range[2]; 357 FILE* fp = NULL; 358 res_T res = RES_OK; 359 ASSERT(atm && filename); 360 361 if(!strcmp(filename, "-")) { 362 fp = stdout; 363 } else { 364 fp = fopen(filename, "w"); 365 if(!fp) { 366 fprintf(stderr, "Could not open `%s' -- %s\n", filename, strerror(errno)); 367 res = RES_IO_ERR; 368 goto error; 369 } 370 } 371 372 octrees_range[0] = 0; 373 octrees_range[1] = rnatm_get_spectral_items_count(atm) - 1; 374 375 res = rnatm_write_vtk_octrees(atm, octrees_range, fp); 376 if(res != RES_OK) goto error; 377 378 exit: 379 if(fp != stdout) CHK(fclose(fp) == 0); 380 return res; 381 error: 382 goto exit; 383 } 384 385 /******************************************************************************* 386 * Main function 387 ******************************************************************************/ 388 int 389 main(int argc, char** argv) 390 { 391 struct args args = ARGS_DEFAULT; 392 struct rnatm* atm = NULL; 393 res_T res = RES_OK; 394 int err = 0; 395 396 res = args_init(&args, argc, argv); 397 if(res != RES_OK) goto error; 398 399 res = rnatm_create(&args.rnatm, &atm); 400 if(res != RES_OK) goto error; 401 402 if(args.check) { 403 res = rnatm_validate(atm); 404 if(res != RES_OK) goto error; 405 } 406 407 if(args.vtk_filename) { 408 res = write_vtk_octrees(atm, args.vtk_filename); 409 if(res != RES_OK) goto error; 410 } 411 412 exit: 413 args_release(&args); 414 if(atm) RNATM(ref_put(atm)); 415 if(mem_allocated_size() != 0) { 416 fprintf(stderr, "Memory leaks: %lu bytes\n", 417 (unsigned long)mem_allocated_size()); 418 err = -1; 419 } 420 return err; 421 error: 422 err = -1; 423 goto exit; 424 }