test_shtr_lines.c (15626B)
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 20 21 #include "shtr.h" 22 23 #include <rsys/clock_time.h> 24 #include <rsys/mem_allocator.h> 25 #include <rsys/math.h> 26 27 #include <math.h> 28 #include <string.h> 29 30 static void 31 print_lines 32 (FILE* fp, 33 const struct shtr_line* lines, 34 const size_t nlines) 35 { 36 size_t i; 37 38 CHK(fp && (!nlines || lines)); 39 FOR_EACH(i, 0, nlines) { 40 fprintf(fp, 41 "%2d%1d%12.6f%10.3e 0.000E-00.%04d%5.3f%10.4f%4.2f%8.6f" 42 /* Dummy remaining data */ 43 " 0 0 0" /* Global upper quanta */ 44 " 0 0 0" /* Global upper quanta */ 45 " 5 5 0 " /* Local upper quanta */ 46 " 5 5 1 " /* Local lower quanta */ 47 "562220" /* Error indices */ 48 "5041 7833348" /* References */ 49 " " /* Line mixing flag */ 50 " 66.0" /* g' */ 51 " 66.0" /* g'' */ 52 "\n", 53 lines[i].molecule_id, 54 lines[i].isotope_id_local == 9 ? 0 : lines[i].isotope_id_local+1, 55 lines[i].wavenumber, 56 lines[i].intensity, 57 (int)(lines[i].gamma_air*10000+0.5/*round*/), 58 lines[i].gamma_self, 59 lines[i].lower_state_energy, 60 lines[i].n_air, 61 lines[i].delta_air); 62 } 63 } 64 65 static void 66 test_line_eq(void) 67 { 68 const struct shtr_line l0 = { 69 0.000134, 2.672E-38, 0.0533, 0.410, 608.4727, 0.79, 0.000060, 1, 4 70 }; 71 struct shtr_line l1 = l0; 72 73 CHK(shtr_line_eq(&l0, &l1)); 74 75 l1.wavenumber = nextafter(l1.wavenumber, INF); 76 CHK(!shtr_line_eq(&l0, &l1)); 77 l1.wavenumber = l0.wavenumber; 78 l1.intensity = nextafter(l1.intensity, INF); 79 CHK(!shtr_line_eq(&l0, &l1)); 80 l1.intensity = l0.intensity; 81 l1.gamma_air = nextafter(l1.gamma_air, INF); 82 CHK(!shtr_line_eq(&l0, &l1)); 83 l1.gamma_air = l0.gamma_air; 84 l1.gamma_self = nextafter(l1.gamma_self, INF); 85 CHK(!shtr_line_eq(&l0, &l1)); 86 l1.gamma_self = l0.gamma_self; 87 l1.lower_state_energy = nextafter(l1.lower_state_energy, INF); 88 CHK(!shtr_line_eq(&l0, &l1)); 89 l1.lower_state_energy = l0.lower_state_energy; 90 l1.n_air = nextafter(l1.n_air, INF); 91 CHK(!shtr_line_eq(&l0, &l1)); 92 l1.n_air = l0.n_air; 93 l1.delta_air = nextafter(l1.delta_air, INF); 94 CHK(!shtr_line_eq(&l0, &l1)); 95 l1.delta_air = l0.delta_air; 96 l1.molecule_id += 1; 97 CHK(!shtr_line_eq(&l0, &l1)); 98 l1.molecule_id = l0.molecule_id; 99 l1.isotope_id_local += 1; 100 CHK(!shtr_line_eq(&l0, &l1)); 101 l1.isotope_id_local = l0.isotope_id_local; 102 103 CHK(shtr_line_eq(&l0, &l1)); 104 } 105 106 static void 107 line_eq_eps 108 (const struct shtr_line* ln0, 109 const struct shtr_line* ln1, 110 struct shtr_line_list_info* info) 111 { 112 #define EQ_EPS(Name) CHK(eq_eps(ln0->Name, ln1->Name, info->Name.err)) 113 EQ_EPS(wavenumber); 114 EQ_EPS(intensity); 115 EQ_EPS(gamma_air); 116 EQ_EPS(gamma_self); 117 EQ_EPS(lower_state_energy); 118 EQ_EPS(n_air); 119 EQ_EPS(delta_air); 120 #undef EQ_EPS 121 CHK(ln0->molecule_id == ln1->molecule_id); 122 CHK(ln0->isotope_id_local == ln1->isotope_id_local); 123 } 124 125 static void 126 test_load(struct shtr* shtr) 127 { 128 const struct shtr_line l[] = { 129 {0.000134, 2.672E-38, 0.0533, 0.410, 608.4727, 0.79, 0.000060, 1, 4}, 130 {0.000379, 1.055E-39, 0.0418, 0.329,1747.9686, 0.79, 0.000110, 1, 5}, 131 {0.000448, 5.560E-38, 0.0490, 0.364,1093.0269, 0.79, 0.000060, 1, 4}, 132 {0.000686, 1.633E-36, 0.0578, 0.394, 701.1162, 0.79, 0.000180, 1, 4}, 133 {0.000726, 6.613E-33, 0.0695, 0.428, 402.3295, 0.79, 0.000240, 1, 3} 134 }; 135 const size_t nlines = sizeof(l) / sizeof(struct shtr_line); 136 137 struct shtr_line_list* list = NULL; 138 struct shtr_line_list_info info = SHTR_LINE_LIST_INFO_NULL; 139 struct shtr_line_list_info info_ref = SHTR_LINE_LIST_INFO_NULL; 140 struct shtr_line line = SHTR_LINE_NULL; 141 struct shtr_line_list_load_args args = SHTR_LINE_LIST_LOAD_ARGS_NULL__; 142 const char* filename = "test_lines.txt"; 143 FILE* fp = NULL; 144 size_t i, n; 145 146 CHK(fp = fopen(filename, "w+")); 147 print_lines(fp, l, nlines); 148 rewind(fp); 149 150 args.file = fp; 151 CHK(shtr_line_list_load(NULL, &args, &list) == RES_BAD_ARG); 152 CHK(shtr_line_list_load(shtr, NULL, &list) == RES_BAD_ARG); 153 CHK(shtr_line_list_load(shtr, &args, NULL) == RES_BAD_ARG); 154 CHK(shtr_line_list_load(shtr, &args, &list) == RES_OK); 155 156 CHK(shtr_line_list_get_info(NULL, &info) == RES_BAD_ARG); 157 CHK(shtr_line_list_get_info(list, NULL) == RES_BAD_ARG); 158 CHK(shtr_line_list_get_info(list, &info) == RES_OK); 159 160 /* Setup the reference information, i.e., from the original list of lines */ 161 FOR_EACH(i, 0, nlines) { 162 #define UPDATE_INFO(Name) { \ 163 info_ref.Name.range[0] = MMIN(l[i].Name, info_ref.Name.range[0]); \ 164 info_ref.Name.range[1] = MMAX(l[i].Name, info_ref.Name.range[1]); \ 165 } (void)0 166 UPDATE_INFO(wavenumber); 167 UPDATE_INFO(intensity); 168 UPDATE_INFO(gamma_air); 169 UPDATE_INFO(gamma_self); 170 UPDATE_INFO(lower_state_energy); 171 UPDATE_INFO(n_air); 172 UPDATE_INFO(delta_air); 173 #undef UPDATE_INFO 174 } 175 176 #define CHK_INFO(Name) { \ 177 CHK(eq_eps(info.Name.range[0], info_ref.Name.range[0], info.Name.err)); \ 178 CHK(eq_eps(info.Name.range[1], info_ref.Name.range[1], info.Name.err)); \ 179 } (void)0 180 CHK_INFO(wavenumber); 181 CHK_INFO(intensity); 182 CHK_INFO(gamma_air); 183 CHK_INFO(gamma_self); 184 CHK_INFO(lower_state_energy); 185 CHK_INFO(n_air); 186 CHK_INFO(delta_air); 187 #undef CHK_INFO 188 189 CHK(shtr_line_list_get_size(NULL, &n) == RES_BAD_ARG); 190 CHK(shtr_line_list_get_size(list, NULL) == RES_BAD_ARG); 191 CHK(shtr_line_list_get_size(list, &n) == RES_OK); 192 CHK(n == nlines); 193 194 CHK(shtr_line_list_at(NULL, 0, &line) == RES_BAD_ARG); 195 CHK(shtr_line_list_at(list, nlines, &line) == RES_BAD_ARG); 196 CHK(shtr_line_list_at(list, 0, NULL) == RES_BAD_ARG); 197 FOR_EACH(i, 0, n) { 198 CHK(shtr_line_list_at(list, i, &line) == RES_OK); 199 line_eq_eps(&line, l+i, &info); 200 } 201 202 CHK(shtr_line_list_ref_get(NULL) == RES_BAD_ARG); 203 CHK(shtr_line_list_ref_get(list) == RES_OK); 204 CHK(shtr_line_list_ref_put(NULL) == RES_BAD_ARG); 205 CHK(shtr_line_list_ref_put(list) == RES_OK); 206 CHK(shtr_line_list_ref_put(list) == RES_OK); 207 208 CHK(fclose(fp) == 0); 209 210 args.file = NULL; 211 args.filename = filename; 212 CHK(shtr_line_list_load(NULL, &args, &list) == RES_BAD_ARG); 213 CHK(shtr_line_list_load(shtr, NULL, &list) == RES_BAD_ARG); 214 CHK(shtr_line_list_load(shtr, &args, NULL) == RES_BAD_ARG); 215 CHK(shtr_line_list_load(shtr, &args, &list) == RES_OK); 216 217 CHK(shtr_line_list_get_info(list, &info) == RES_OK); 218 219 CHK(shtr_line_list_get_size(list, &n) == RES_OK); 220 CHK(n == nlines); 221 222 FOR_EACH(i, 0, n) { 223 CHK(shtr_line_list_at(list, i, &line) == RES_OK); 224 line_eq_eps(&line, l+i, &info); 225 } 226 227 CHK(shtr_line_list_ref_put(list) == RES_OK); 228 } 229 230 static void 231 test_line 232 (struct shtr* shtr, const struct shtr_line* ln, const res_T res) 233 { 234 struct shtr_line_list* list = NULL; 235 struct shtr_line_list_load_args args = SHTR_LINE_LIST_LOAD_ARGS_NULL__; 236 237 CHK(args.file = tmpfile()); 238 print_lines(args.file, ln, 1); 239 rewind(args.file); 240 CHK(shtr_line_list_load(shtr, &args, &list) == res); 241 CHK(fclose(args.file) == 0); 242 243 if(res == RES_OK) CHK(shtr_line_list_ref_put(list) == RES_OK); 244 } 245 246 static void 247 test_load_failures(struct shtr* shtr) 248 { 249 const struct shtr_line ln_ref = { 250 0.000134, 2.672E-38, 0.0533, 0.410, 608.4727, 0.79, 0.000060, 1, 4, 251 }; 252 struct shtr_line ln; 253 struct shtr_line_list* list = NULL; 254 struct shtr_line_list_load_args args = SHTR_LINE_LIST_LOAD_ARGS_NULL__; 255 256 /* Check that the reference line is valid */ 257 test_line(shtr, &ln_ref, RES_OK); 258 259 /* Invalid wavenumber */ 260 ln = ln_ref; ln.wavenumber = 0; 261 test_line(shtr, &ln, RES_BAD_ARG); 262 263 /* Invalid intensity */ 264 ln = ln_ref; ln.intensity = 0; 265 test_line(shtr, &ln, RES_BAD_ARG); 266 267 /* Invalid gamma air */ 268 ln = ln_ref; ln.gamma_air = -1; 269 test_line(shtr, &ln, RES_BAD_ARG); 270 271 /* Invalid gamma self */ 272 ln = ln_ref; ln.gamma_self = -1; 273 test_line(shtr, &ln, RES_BAD_ARG); 274 275 /* Unavailable lower state energy */ 276 ln = ln_ref; ln.lower_state_energy = -1; 277 test_line(shtr, &ln, RES_OK); 278 279 /* Invalid lower state energy */ 280 ln = ln_ref; ln.lower_state_energy = -2; 281 test_line(shtr, &ln, RES_BAD_ARG); 282 283 /* Invalid molecule id */ 284 ln = ln_ref; ln.molecule_id = -1; 285 test_line(shtr, &ln, RES_BAD_ARG); 286 287 /* Bad file formatting */ 288 ln = ln_ref; ln.molecule_id = 100; 289 test_line(shtr, &ln, RES_BAD_ARG); 290 ln = ln_ref; ln.isotope_id_local = 10; 291 test_line(shtr, &ln, RES_BAD_ARG); 292 293 /* Lines are not correctly sorted */ 294 CHK(args.file = tmpfile()); 295 ln = ln_ref; 296 print_lines(args.file, &ln, 1); 297 ln.wavenumber -= 1e-4; 298 print_lines(args.file, &ln, 1); 299 rewind(args.file); 300 CHK(shtr_line_list_load(shtr, &args, &list) == RES_BAD_ARG); 301 CHK(fclose(args.file) == 0); 302 } 303 304 static void 305 check_line_list_equality 306 (struct shtr_line_list* list1, 307 struct shtr_line_list* list2) 308 { 309 size_t n1, n2; 310 size_t iline, nlines; 311 CHK(list1 && list2); 312 313 CHK(shtr_line_list_get_size(list1, &n1) == RES_OK); 314 CHK(shtr_line_list_get_size(list2, &n2) == RES_OK); 315 CHK(n1 == n2); 316 nlines = n1; 317 318 FOR_EACH(iline, 0, nlines) { 319 struct shtr_line lines1 = SHTR_LINE_NULL; 320 struct shtr_line lines2 = SHTR_LINE_NULL; 321 322 CHK(shtr_line_list_at(list1, iline, &lines1) == RES_OK); 323 CHK(shtr_line_list_at(list2, iline, &lines2) == RES_OK); 324 325 CHK(shtr_line_eq(&lines1, &lines2)); 326 } 327 } 328 329 static void 330 test_serialization(struct shtr* shtr) 331 { 332 const struct shtr_line l[] = { 333 {0.000134, 2.672E-38, 0.0533, 0.410, 608.4727, 0.79, 0.000060, 1, 4}, 334 {0.000379, 1.055E-39, 0.0418, 0.329,1747.9686, 0.79, 0.000110, 1, 5}, 335 {0.000448, 5.560E-38, 0.0490, 0.364,1093.0269, 0.79, 0.000060, 1, 4}, 336 {0.000686, 1.633E-36, 0.0578, 0.394, 701.1162, 0.79, 0.000180, 1, 4}, 337 {0.000726, 6.613E-33, 0.0695, 0.428, 402.3295, 0.79, 0.000240, 1, 3} 338 }; 339 const size_t nlines = sizeof(l) / sizeof(struct shtr_line); 340 341 struct shtr_line_list_load_args load_args = SHTR_LINE_LIST_LOAD_ARGS_NULL__; 342 struct shtr_line_list_read_args read_args = SHTR_LINE_LIST_READ_ARGS_NULL__; 343 struct shtr_line_list_write_args write_args = SHTR_LINE_LIST_WRITE_ARGS_NULL__; 344 struct shtr_line_list* list1 = NULL; 345 struct shtr_line_list* list2 = NULL; 346 347 const char* filename = "line_list.shtr"; 348 FILE* fp = NULL; 349 350 CHK(fp = tmpfile()); 351 print_lines(fp, l, nlines); 352 rewind(fp); 353 354 load_args.file = fp; 355 CHK(shtr_line_list_load(shtr, &load_args, &list1) == RES_OK); 356 CHK(fclose(fp) == 0); 357 358 CHK(fp = fopen(filename, "w+")); 359 write_args.file = fp; 360 CHK(shtr_line_list_write(NULL, &write_args) == RES_BAD_ARG); 361 CHK(shtr_line_list_write(list1, NULL) == RES_BAD_ARG); 362 CHK(shtr_line_list_write(list1, &write_args) == RES_OK); 363 rewind(fp); 364 365 read_args.file = fp; 366 CHK(shtr_line_list_read(NULL, &read_args, &list2) == RES_BAD_ARG); 367 CHK(shtr_line_list_read(shtr, NULL, &list2) == RES_BAD_ARG); 368 CHK(shtr_line_list_read(shtr, &read_args, NULL) == RES_BAD_ARG); 369 CHK(shtr_line_list_read(shtr, &read_args, &list2) == RES_OK); 370 CHK(fclose(fp) == 0); 371 372 check_line_list_equality(list1, list2); 373 CHK(shtr_line_list_ref_put(list2) == RES_OK); 374 375 write_args.file = NULL; 376 CHK(shtr_line_list_write(list1, &write_args) == RES_BAD_ARG); 377 write_args.filename = filename; 378 CHK(shtr_line_list_write(list1, &write_args) == RES_OK); 379 380 read_args.file = NULL; 381 CHK(shtr_line_list_read(shtr, &read_args, &list2) == RES_BAD_ARG); 382 read_args.filename = "nop"; 383 CHK(shtr_line_list_read(shtr, &read_args, &list2) == RES_IO_ERR); 384 read_args.filename = filename; 385 CHK(shtr_line_list_read(shtr, &read_args, &list2) == RES_OK); 386 387 check_line_list_equality(list1, list2); 388 CHK(shtr_line_list_ref_put(list2) == RES_OK); 389 CHK(shtr_line_list_ref_put(list1) == RES_OK); 390 } 391 392 static void 393 test_deserialization_of_a_subset(struct shtr* shtr) 394 { 395 const struct shtr_line l[] = { 396 {0.000134, 2.672E-38, 0.0533, 0.410, 608.4727, 0.79, 0.000060, 1, 4}, 397 {0.000379, 1.055E-39, 0.0418, 0.329,1747.9686, 0.79, 0.000110, 1, 5}, 398 {0.000448, 5.560E-38, 0.0490, 0.364,1093.0269, 0.79, 0.000060, 1, 4}, 399 {0.000686, 1.633E-36, 0.0578, 0.394, 701.1162, 0.79, 0.000180, 1, 4}, 400 {0.000726, 6.613E-33, 0.0695, 0.428, 402.3295, 0.79, 0.000240, 1, 3} 401 }; 402 const size_t nlines = sizeof(l) / sizeof(struct shtr_line); 403 404 struct shtr_line_list_load_args load_args = SHTR_LINE_LIST_LOAD_ARGS_NULL__; 405 struct shtr_line_list_read_args read_args = SHTR_LINE_LIST_READ_ARGS_NULL__; 406 struct shtr_line_list_write_args write_args = SHTR_LINE_LIST_WRITE_ARGS_NULL__; 407 struct shtr_line_list* list1 = NULL; 408 struct shtr_line_list* list2 = NULL; 409 410 FILE* fp = NULL; 411 size_t i = 0; 412 size_t n = 0; 413 414 CHK(fp = tmpfile()); 415 print_lines(fp, l, nlines); 416 rewind(fp); 417 418 load_args.file = fp; 419 CHK(shtr_line_list_load(shtr, &load_args, &list1) == RES_OK); 420 CHK(fclose(fp) == 0); 421 422 CHK(fp = tmpfile()); 423 write_args.file = fp; 424 CHK(shtr_line_list_write(list1, &write_args) == RES_OK); 425 426 rewind(fp); 427 read_args.file = fp; 428 read_args.range[0] = nlines; 429 CHK(shtr_line_list_read(shtr, &read_args, &list2) == RES_OK); 430 431 CHK(shtr_line_list_get_size(list2, &n) == RES_OK); 432 CHK(n == 0); 433 CHK(shtr_line_list_ref_put(list2) == RES_OK); 434 435 rewind(fp); 436 read_args.range[0] = 0; 437 read_args.range[1] = nlines-1; /*inclusive bound*/ 438 CHK(shtr_line_list_read(shtr, &read_args, &list2) == RES_OK); 439 check_line_list_equality(list1, list2); 440 CHK(shtr_line_list_ref_put(list2) == RES_OK); 441 442 rewind(fp); 443 read_args.range[0] = 0; 444 read_args.range[1] = 2; 445 CHK(shtr_line_list_read(shtr, &read_args, &list2) == RES_OK); 446 CHK(shtr_line_list_get_size(list2, &n) == RES_OK); 447 CHK(n == read_args.range[1] - read_args.range[0] + 1/*inclusive bounds*/); 448 FOR_EACH(i, 0, n) { 449 struct shtr_line line1, line2; 450 CHK(shtr_line_list_at(list1, i, &line1) == RES_OK); 451 CHK(shtr_line_list_at(list2, i, &line2) == RES_OK); 452 CHK(shtr_line_eq(&line1, &line2)); 453 } 454 CHK(shtr_line_list_ref_put(list2) == RES_OK); 455 456 rewind(fp); 457 read_args.range[0] = nlines-3; 458 read_args.range[1] = SIZE_MAX; /* Upper bound will be fit */ 459 CHK(shtr_line_list_read(shtr, &read_args, &list2) == RES_OK); 460 CHK(shtr_line_list_get_size(list2, &n) == RES_OK); 461 CHK(n == 3); 462 FOR_EACH(i, 0, n) { 463 struct shtr_line line1, line2; 464 CHK(shtr_line_list_at(list1, i+read_args.range[0], &line1) == RES_OK); 465 CHK(shtr_line_list_at(list2, i, &line2) == RES_OK); 466 CHK(shtr_line_eq(&line1, &line2)); 467 } 468 CHK(shtr_line_list_ref_put(list2) == RES_OK); 469 470 CHK(fclose(fp) == 0); 471 CHK(shtr_line_list_ref_put(list1) == RES_OK); 472 } 473 474 int 475 main(int argc, char** argv) 476 { 477 struct shtr_create_args args = SHTR_CREATE_ARGS_DEFAULT; 478 struct shtr* shtr = NULL; 479 (void)argc, (void)argv; 480 481 args.verbose = 1; 482 CHK(shtr_create(&args, &shtr) == RES_OK); 483 484 test_line_eq(); 485 test_load(shtr); 486 test_load_failures(shtr); 487 test_serialization(shtr); 488 test_deserialization_of_a_subset(shtr); 489 490 CHK(shtr_ref_put(shtr) == RES_OK); 491 CHK(mem_allocated_size() == 0); 492 return 0; 493 }