test_sln_tree.c (13961B)
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 #include "test_sln_lines.h" 20 #include "sln.h" 21 22 #include <star/shtr.h> 23 24 #include <rsys/algorithm.h> 25 #include <rsys/math.h> 26 #include <rsys/mem_allocator.h> 27 28 /******************************************************************************* 29 * Helper function 30 ******************************************************************************/ 31 /* Return the index of the line in the line_list or SIZE_MAX if the line does 32 * not exist */ 33 static INLINE size_t 34 find_line 35 (struct shtr_line_list* line_list, 36 const struct shtr_line* line) 37 { 38 struct shtr_line ln = SHTR_LINE_NULL; 39 size_t lo, hi, mid; 40 size_t iline; 41 size_t nlines; 42 43 CHK(shtr_line_list_get_size(line_list, &nlines) == RES_OK); 44 45 /* Dichotomic search */ 46 lo = 0; hi = nlines -1; 47 while(lo < hi) { 48 mid = (lo+hi)/2; 49 50 CHK(shtr_line_list_at(line_list, mid, &ln) == RES_OK); 51 if(line->wavenumber > ln.wavenumber) { 52 lo = mid + 1; 53 } else { 54 hi = mid; 55 } 56 } 57 iline = lo; 58 59 CHK(shtr_line_list_at(line_list, iline, &ln) == RES_OK); 60 if(ln.wavenumber != line->wavenumber) return SIZE_MAX; 61 62 63 /* Find a line with the same wavenumber as the one searched for and whose 64 * other member variables are also equal to those of the line searched for */ 65 while(ln.wavenumber == line->wavenumber 66 && !shtr_line_eq(&ln, line) 67 && iline < nlines) { 68 iline += 1; 69 CHK(shtr_line_list_at(line_list, iline, &ln) == RES_OK); 70 } 71 72 return shtr_line_eq(&ln, line) ? iline : SIZE_MAX; 73 } 74 75 /* This test assumes that all the lines contained into the list are 76 * partitionned in the tree */ 77 static void 78 check_tree 79 (const struct sln_tree* tree, 80 struct shtr_line_list* line_list) 81 { 82 #define STACK_SIZE 64 83 const struct sln_node* stack[STACK_SIZE] = {NULL}; 84 struct sln_tree_desc desc = SLN_TREE_DESC_NULL; 85 size_t istack = 0; 86 const struct sln_node* node = NULL; 87 88 char* found_lines = NULL; 89 size_t found_nlines = 0; 90 size_t line_list_sz; 91 92 CHK(shtr_line_list_get_size(line_list, &line_list_sz) == RES_OK); 93 CHK(sln_tree_get_desc(tree, &desc) == RES_OK); 94 95 CHK(found_lines = mem_calloc(line_list_sz, sizeof(char))); 96 97 node = sln_tree_get_root(tree); 98 while(node) { 99 if(!sln_node_is_leaf(node)) { 100 CHK(sln_node_get_lines_count(node) > desc.max_nlines_per_leaf); 101 102 ASSERT(istack < STACK_SIZE); 103 stack[istack++] = sln_node_get_child(node, 1); /* Push the child 1 */ 104 node = sln_node_get_child(node, 0); /* Handle the child 0 */ 105 106 } else { 107 size_t iline, nlines; 108 109 nlines = sln_node_get_lines_count(node); 110 CHK(nlines <= desc.max_nlines_per_leaf); 111 FOR_EACH(iline, 0, nlines) { 112 struct shtr_line line = SHTR_LINE_NULL; 113 size_t found_iline = 0; 114 CHK(sln_node_get_line(tree, node, iline, &line) == RES_OK); 115 found_iline = find_line(line_list, &line); 116 CHK(found_iline != SIZE_MAX); /* Line is found */ 117 118 if(!found_lines[found_iline]) { 119 found_nlines += 1; 120 found_lines[found_iline] = 1; 121 } 122 } 123 124 node = istack ? stack[--istack] : NULL; /* Pop the next node */ 125 } 126 } 127 128 /* Check that all lines are found */ 129 CHK(found_nlines == line_list_sz); 130 131 mem_rm(found_lines); 132 #undef STACK_SIZE 133 } 134 135 static void 136 dump_line(FILE* stream, const struct sln_mesh* mesh) 137 { 138 size_t i; 139 CHK(mesh); 140 FOR_EACH(i, 0, mesh->nvertices) { 141 fprintf(stream, "%g %g\n", 142 mesh->vertices[i].wavenumber, 143 mesh->vertices[i].ka); 144 } 145 } 146 147 static void 148 test_tree 149 (struct sln_device* sln, 150 const struct sln_tree_create_args* tree_args_in, 151 struct shtr_line_list* line_list) 152 { 153 struct sln_tree_create_args tree_args = SLN_TREE_CREATE_ARGS_DEFAULT; 154 struct sln_tree_desc desc = SLN_TREE_DESC_NULL; 155 struct sln_mesh mesh = SLN_MESH_NULL; 156 struct sln_tree* tree = NULL; 157 const struct sln_node* node = NULL; 158 struct shtr_line line = SHTR_LINE_NULL; 159 size_t nlines = 0; 160 161 CHK(sln && tree_args_in && line_list); 162 tree_args = *tree_args_in; 163 164 CHK(sln_tree_create(NULL, &tree_args, &tree) == RES_BAD_ARG); 165 CHK(sln_tree_create(sln, NULL, &tree) == RES_BAD_ARG); 166 CHK(sln_tree_create(sln, &tree_args, NULL) == RES_BAD_ARG); 167 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_OK); 168 169 CHK(shtr_line_list_get_size(line_list, &nlines) == RES_OK); 170 171 CHK(sln_tree_get_desc(NULL, &desc) == RES_BAD_ARG); 172 CHK(sln_tree_get_desc(tree, NULL) == RES_BAD_ARG); 173 CHK(sln_tree_get_desc(tree, &desc) == RES_OK); 174 175 CHK(desc.max_nlines_per_leaf >= 1); 176 CHK(desc.mesh_decimation_err == tree_args.mesh_decimation_err); 177 CHK(desc.mesh_type == tree_args.mesh_type); 178 CHK(desc.line_profile == tree_args.line_profile); 179 CHK(desc.nvertices >= 1); 180 CHK(desc.nnodes >= 1); 181 182 CHK(node = sln_tree_get_root(tree)); 183 while(!sln_node_is_leaf(node)) { 184 node = sln_node_get_child(node, 0); 185 } 186 CHK(sln_node_get_lines_count(node) <= desc.max_nlines_per_leaf); 187 CHK(sln_node_get_line(NULL, node, 0, &line) == RES_BAD_ARG); 188 CHK(sln_node_get_line(tree, NULL, 0, &line) == RES_BAD_ARG); 189 CHK(sln_node_get_line(tree, node, desc.max_nlines_per_leaf+1, &line) 190 == RES_BAD_ARG); 191 CHK(sln_node_get_line(tree, node, 0, NULL) == RES_BAD_ARG); 192 CHK(sln_node_get_line(tree, node, 0, &line) == RES_OK); 193 CHK(sln_node_get_line(tree, sln_tree_get_root(tree), 0, &line) == RES_OK); 194 195 CHK(sln_node_get_mesh(NULL, node, &mesh) == RES_BAD_ARG); 196 CHK(sln_node_get_mesh(tree, NULL, &mesh) == RES_BAD_ARG); 197 CHK(sln_node_get_mesh(tree, node, NULL) == RES_BAD_ARG); 198 CHK(sln_node_get_mesh(tree, node, &mesh) == RES_OK); 199 200 CHK(node = sln_tree_get_root(tree)); 201 CHK(sln_node_get_mesh(tree, node, &mesh) == RES_OK); 202 203 dump_line(stdout, &mesh); 204 check_tree(tree, line_list); 205 206 CHK(sln_tree_ref_get(NULL) == RES_BAD_ARG); 207 CHK(sln_tree_ref_get(tree) == RES_OK); 208 CHK(sln_tree_ref_put(NULL) == RES_BAD_ARG); 209 CHK(sln_tree_ref_put(tree) == RES_OK); 210 CHK(sln_tree_ref_put(tree) == RES_OK); 211 212 tree_args.mesh_decimation_err = -1; 213 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG); 214 215 tree_args.mesh_decimation_err = 1e-1f; 216 tree_args.mesh_type = SLN_MESH_TYPES_COUNT__; 217 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG); 218 219 tree_args.mesh_type = SLN_MESH_FIT; 220 tree_args.line_profile = SLN_LINE_PROFILES_COUNT__; 221 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG); 222 223 tree_args.line_profile = SLN_LINE_PROFILE_VOIGT; 224 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_OK); 225 CHK(sln_tree_ref_put(tree) == RES_OK); 226 } 227 228 static void 229 check_mesh_equality(const struct sln_mesh* mesh1, const struct sln_mesh* mesh2) 230 { 231 size_t i; 232 CHK(mesh1 && mesh2); 233 CHK(mesh1->nvertices == mesh2->nvertices); 234 235 FOR_EACH(i, 0, mesh1->nvertices) { 236 CHK(mesh1->vertices[i].wavenumber == mesh2->vertices[i].wavenumber); 237 CHK(mesh1->vertices[i].ka == mesh2->vertices[i].ka); 238 } 239 } 240 241 static void 242 check_node_equality 243 (const struct sln_tree* tree1, 244 const struct sln_tree* tree2, 245 const struct sln_node* node1, 246 const struct sln_node* node2) 247 { 248 struct sln_mesh mesh1 = SLN_MESH_NULL; 249 struct sln_mesh mesh2 = SLN_MESH_NULL; 250 size_t iline, nlines; 251 252 CHK(node1 && node2); 253 CHK(sln_node_is_leaf(node1) == sln_node_is_leaf(node2)); 254 CHK(sln_node_get_lines_count(node1) == sln_node_get_lines_count(node2)); 255 nlines = sln_node_get_lines_count(node1); 256 257 FOR_EACH(iline, 0, nlines) { 258 struct shtr_line line1 = SHTR_LINE_NULL; 259 struct shtr_line line2 = SHTR_LINE_NULL; 260 CHK(sln_node_get_line(tree1, node1, iline, &line1) == RES_OK); 261 CHK(sln_node_get_line(tree2, node2, iline, &line2) == RES_OK); 262 CHK(shtr_line_eq(&line1, &line2)); 263 } 264 265 CHK(sln_node_get_mesh(tree1, node1, &mesh1) == RES_OK); 266 CHK(sln_node_get_mesh(tree2, node2, &mesh2) == RES_OK); 267 check_mesh_equality(&mesh1, &mesh2); 268 } 269 270 static void 271 check_tree_equality 272 (const struct sln_tree* tree1, 273 const struct sln_tree* tree2) 274 { 275 const struct sln_node* stack[128] = {NULL}; 276 int istack = 0; 277 278 struct sln_tree_desc desc1 = SLN_TREE_DESC_NULL; 279 struct sln_tree_desc desc2 = SLN_TREE_DESC_NULL; 280 const struct sln_node* node1 = NULL; 281 const struct sln_node* node2 = NULL; 282 283 CHK(sln_tree_get_desc(tree1, &desc1) == RES_OK); 284 CHK(sln_tree_get_desc(tree2, &desc2) == RES_OK); 285 CHK(desc1.max_nlines_per_leaf == desc2.max_nlines_per_leaf); 286 CHK(desc1.mesh_decimation_err == desc2.mesh_decimation_err); 287 CHK(desc1.line_profile == desc2.line_profile); 288 289 stack[istack++] = NULL; 290 stack[istack++] = NULL; 291 292 node1 = sln_tree_get_root(tree1); 293 node2 = sln_tree_get_root(tree2); 294 295 while(node1 || node2) { 296 CHK((!node1 && !node2) || (node1 && node2)); 297 check_node_equality(tree1, tree2, node1, node2); 298 299 if(sln_node_is_leaf(node1)) { 300 node2 = stack[--istack]; 301 node1 = stack[--istack]; 302 } else { 303 stack[istack++] = sln_node_get_child(node1, 1); 304 stack[istack++] = sln_node_get_child(node2, 1); 305 node1 = sln_node_get_child(node1, 0); 306 node2 = sln_node_get_child(node2, 0); 307 } 308 } 309 } 310 311 static void 312 test_tree_serialization 313 (struct sln_device* sln, 314 const struct sln_tree_create_args* tree_args, 315 struct shtr* shtr) 316 { 317 struct sln_tree_write_args wargs = SLN_TREE_WRITE_ARGS_NULL; 318 struct sln_tree_read_args rargs = SLN_TREE_READ_ARGS_NULL; 319 struct sln_tree* tree1 = NULL; 320 struct sln_tree* tree2 = NULL; 321 322 const char* filename = "tree.sln"; 323 FILE* fp = NULL; 324 325 CHK(sln_tree_create(sln, tree_args, &tree1) == RES_OK); 326 327 CHK(fp = fopen(filename, "w+")); 328 329 wargs.file = fp; 330 CHK(sln_tree_write(NULL, &wargs) == RES_BAD_ARG); 331 CHK(sln_tree_write(tree1, NULL) == RES_BAD_ARG); 332 wargs.file = NULL; 333 CHK(sln_tree_write(tree1, &wargs) == RES_BAD_ARG); 334 wargs.file = fp; 335 CHK(sln_tree_write(tree1, &wargs) == RES_OK); 336 rewind(fp); 337 338 rargs.shtr = shtr; 339 rargs.file = fp; 340 CHK(sln_tree_read(NULL, &rargs, &tree2) == RES_BAD_ARG); 341 CHK(sln_tree_read(sln, NULL, &tree2) == RES_BAD_ARG); 342 rargs.shtr = NULL; 343 CHK(sln_tree_read(sln, &rargs, &tree2) == RES_BAD_ARG); 344 rargs.shtr = shtr; 345 rargs.file = NULL; 346 CHK(sln_tree_read(sln, &rargs, &tree2) == RES_BAD_ARG); 347 rargs.file = fp; 348 CHK(sln_tree_read(sln, &rargs, NULL) == RES_BAD_ARG); 349 CHK(sln_tree_read(sln, &rargs, &tree2) == RES_OK); 350 fclose(fp); 351 352 check_tree_equality(tree1, tree2); 353 CHK(sln_tree_ref_put(tree2) == RES_OK); 354 355 wargs.file = NULL; 356 wargs.filename = filename; 357 CHK(sln_tree_write(tree1, &wargs) == RES_OK); 358 359 rargs.file = NULL; 360 rargs.filename = "nop"; 361 CHK(sln_tree_read(sln, &rargs, &tree2) == RES_IO_ERR); 362 rargs.filename = filename; 363 CHK(sln_tree_read(sln, &rargs, &tree2) == RES_OK); 364 365 check_tree_equality(tree1, tree2); 366 367 CHK(sln_tree_ref_put(tree2) == RES_OK); 368 CHK(sln_tree_ref_put(tree1) == RES_OK); 369 } 370 371 /******************************************************************************* 372 * Test function 373 ******************************************************************************/ 374 int 375 main(int argc, char** argv) 376 { 377 struct sln_device_create_args dev_args = SLN_DEVICE_CREATE_ARGS_DEFAULT; 378 struct sln_tree_create_args tree_args = SLN_TREE_CREATE_ARGS_DEFAULT; 379 struct sln_device* sln = NULL; 380 381 struct shtr_create_args shtr_args = SHTR_CREATE_ARGS_DEFAULT; 382 struct shtr* shtr = NULL; 383 struct shtr_line_list_load_args line_load_args = SHTR_LINE_LIST_LOAD_ARGS_NULL; 384 struct shtr_line_list* line_list = NULL; 385 struct shtr_isotope_metadata* metadata = NULL; 386 387 FILE* fp_lines = NULL; 388 FILE* fp_mdata = NULL; 389 (void)argc, (void)argv; 390 391 /* Generate the file of the isotope metadata */ 392 CHK(fp_mdata = tmpfile()); 393 fprintf(fp_mdata, "Molecule # Iso Abundance Q(296K) gj Molar Mass(g)\n"); 394 write_shtr_molecule(fp_mdata, &g_H2O); 395 write_shtr_molecule(fp_mdata, &g_CO2); 396 write_shtr_molecule(fp_mdata, &g_O3); 397 rewind(fp_mdata); 398 399 /* Generate the file of lines */ 400 CHK(fp_lines = tmpfile()); 401 write_shtr_lines(fp_lines, g_lines, g_nlines); 402 rewind(fp_lines); 403 404 /* Load the isotope metadata and the lines */ 405 shtr_args.verbose = 1; 406 CHK(shtr_create(&shtr_args, &shtr) == RES_OK); 407 CHK(shtr_isotope_metadata_load_stream(shtr, fp_mdata, NULL, &metadata) == RES_OK); 408 409 line_load_args.filename = "stream"; 410 line_load_args.file = fp_lines; 411 CHK(shtr_line_list_load(shtr, &line_load_args, &line_list) == RES_OK); 412 413 CHK(fclose(fp_lines) == 0); 414 CHK(fclose(fp_mdata) == 0); 415 416 dev_args.verbose = 1; 417 CHK(sln_device_create(&dev_args, &sln) == RES_OK); 418 419 /* Create the mixture */ 420 tree_args.metadata = metadata; 421 tree_args.lines = line_list; 422 tree_args.molecules[SHTR_H2O].concentration = 1.0/3.0; 423 tree_args.molecules[SHTR_H2O].cutoff = 25; 424 tree_args.molecules[SHTR_CO2].concentration = 1.0/3.0; 425 tree_args.molecules[SHTR_CO2].cutoff = 50; 426 tree_args.molecules[SHTR_O3 ].concentration = 1.0/3.0; 427 tree_args.molecules[SHTR_O3 ].cutoff = 25; 428 tree_args.pressure = 1; 429 tree_args.temperature = 296; 430 431 test_tree(sln, &tree_args, line_list); 432 test_tree_serialization(sln, &tree_args, shtr); 433 434 CHK(sln_device_ref_put(sln) == RES_OK); 435 CHK(shtr_ref_put(shtr) == RES_OK); 436 CHK(shtr_line_list_ref_put(line_list) == RES_OK); 437 CHK(shtr_isotope_metadata_ref_put(metadata) == RES_OK); 438 CHK(mem_allocated_size() == 0); 439 return 0; 440 }