test_sln_tree.c (13113B)
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.mesh_decimation_err == tree_args.mesh_decimation_err); 176 CHK(desc.mesh_type == tree_args.mesh_type); 177 CHK(desc.line_profile == tree_args.line_profile); 178 179 CHK(node = sln_tree_get_root(tree)); 180 while(!sln_node_is_leaf(node)) { 181 node = sln_node_get_child(node, 0); 182 } 183 CHK(sln_node_get_lines_count(node) <= desc.max_nlines_per_leaf); 184 CHK(sln_node_get_line(NULL, node, 0, &line) == RES_BAD_ARG); 185 CHK(sln_node_get_line(tree, NULL, 0, &line) == RES_BAD_ARG); 186 CHK(sln_node_get_line(tree, node, desc.max_nlines_per_leaf+1, &line) 187 == RES_BAD_ARG); 188 CHK(sln_node_get_line(tree, node, 0, NULL) == RES_BAD_ARG); 189 CHK(sln_node_get_line(tree, node, 0, &line) == RES_OK); 190 CHK(sln_node_get_line(tree, sln_tree_get_root(tree), 0, &line) == RES_OK); 191 192 CHK(sln_node_get_mesh(NULL, node, &mesh) == RES_BAD_ARG); 193 CHK(sln_node_get_mesh(tree, NULL, &mesh) == RES_BAD_ARG); 194 CHK(sln_node_get_mesh(tree, node, NULL) == RES_BAD_ARG); 195 CHK(sln_node_get_mesh(tree, node, &mesh) == RES_OK); 196 197 CHK(node = sln_tree_get_root(tree)); 198 CHK(sln_node_get_mesh(tree, node, &mesh) == RES_OK); 199 200 dump_line(stdout, &mesh); 201 check_tree(tree, line_list); 202 203 CHK(sln_tree_ref_get(NULL) == RES_BAD_ARG); 204 CHK(sln_tree_ref_get(tree) == RES_OK); 205 CHK(sln_tree_ref_put(NULL) == RES_BAD_ARG); 206 CHK(sln_tree_ref_put(tree) == RES_OK); 207 CHK(sln_tree_ref_put(tree) == RES_OK); 208 209 tree_args.mesh_decimation_err = -1; 210 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG); 211 212 tree_args.mesh_decimation_err = 1e-1f; 213 tree_args.mesh_type = SLN_MESH_TYPES_COUNT__; 214 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG); 215 216 tree_args.mesh_type = SLN_MESH_FIT; 217 tree_args.line_profile = SLN_LINE_PROFILES_COUNT__; 218 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_BAD_ARG); 219 220 tree_args.line_profile = SLN_LINE_PROFILE_VOIGT; 221 CHK(sln_tree_create(sln, &tree_args, &tree) == RES_OK); 222 CHK(sln_tree_ref_put(tree) == RES_OK); 223 } 224 225 static void 226 check_mesh_equality(const struct sln_mesh* mesh1, const struct sln_mesh* mesh2) 227 { 228 size_t i; 229 CHK(mesh1 && mesh2); 230 CHK(mesh1->nvertices == mesh2->nvertices); 231 232 FOR_EACH(i, 0, mesh1->nvertices) { 233 CHK(mesh1->vertices[i].wavenumber == mesh2->vertices[i].wavenumber); 234 CHK(mesh1->vertices[i].ka == mesh2->vertices[i].ka); 235 } 236 } 237 238 static void 239 check_node_equality 240 (const struct sln_tree* tree1, 241 const struct sln_tree* tree2, 242 const struct sln_node* node1, 243 const struct sln_node* node2) 244 { 245 struct sln_mesh mesh1 = SLN_MESH_NULL; 246 struct sln_mesh mesh2 = SLN_MESH_NULL; 247 size_t iline, nlines; 248 249 CHK(node1 && node2); 250 CHK(sln_node_is_leaf(node1) == sln_node_is_leaf(node2)); 251 CHK(sln_node_get_lines_count(node1) == sln_node_get_lines_count(node2)); 252 nlines = sln_node_get_lines_count(node1); 253 254 FOR_EACH(iline, 0, nlines) { 255 struct shtr_line line1 = SHTR_LINE_NULL; 256 struct shtr_line line2 = SHTR_LINE_NULL; 257 CHK(sln_node_get_line(tree1, node1, iline, &line1) == RES_OK); 258 CHK(sln_node_get_line(tree2, node2, iline, &line2) == RES_OK); 259 CHK(shtr_line_eq(&line1, &line2)); 260 } 261 262 CHK(sln_node_get_mesh(tree1, node1, &mesh1) == RES_OK); 263 CHK(sln_node_get_mesh(tree2, node2, &mesh2) == RES_OK); 264 check_mesh_equality(&mesh1, &mesh2); 265 } 266 267 static void 268 check_tree_equality 269 (const struct sln_tree* tree1, 270 const struct sln_tree* tree2) 271 { 272 const struct sln_node* stack[128] = {NULL}; 273 int istack = 0; 274 275 struct sln_tree_desc desc1 = SLN_TREE_DESC_NULL; 276 struct sln_tree_desc desc2 = SLN_TREE_DESC_NULL; 277 const struct sln_node* node1 = NULL; 278 const struct sln_node* node2 = NULL; 279 280 CHK(sln_tree_get_desc(tree1, &desc1) == RES_OK); 281 CHK(sln_tree_get_desc(tree2, &desc2) == RES_OK); 282 CHK(desc1.max_nlines_per_leaf == desc2.max_nlines_per_leaf); 283 CHK(desc1.mesh_decimation_err == desc2.mesh_decimation_err); 284 CHK(desc1.line_profile == desc2.line_profile); 285 286 stack[istack++] = NULL; 287 stack[istack++] = NULL; 288 289 node1 = sln_tree_get_root(tree1); 290 node2 = sln_tree_get_root(tree2); 291 292 while(node1 || node2) { 293 CHK((!node1 && !node2) || (node1 && node2)); 294 check_node_equality(tree1, tree2, node1, node2); 295 296 if(sln_node_is_leaf(node1)) { 297 node2 = stack[--istack]; 298 node1 = stack[--istack]; 299 } else { 300 stack[istack++] = sln_node_get_child(node1, 1); 301 stack[istack++] = sln_node_get_child(node2, 1); 302 node1 = sln_node_get_child(node1, 0); 303 node2 = sln_node_get_child(node2, 0); 304 } 305 } 306 } 307 308 static void 309 test_tree_serialization 310 (struct sln_device* sln, 311 const struct sln_tree_create_args* tree_args, 312 struct shtr* shtr) 313 { 314 struct sln_tree* tree1 = NULL; 315 struct sln_tree* tree2 = NULL; 316 FILE* fp = NULL; 317 318 CHK(sln_tree_create(sln, tree_args, &tree1) == RES_OK); 319 320 CHK(fp = tmpfile()); 321 CHK(sln_tree_write(NULL, fp) == RES_BAD_ARG); 322 CHK(sln_tree_write(tree1, NULL) == RES_BAD_ARG); 323 CHK(sln_tree_write(tree1, fp) == RES_OK); 324 rewind(fp); 325 326 CHK(sln_tree_create_from_stream(NULL, shtr, fp, &tree2) == RES_BAD_ARG); 327 CHK(sln_tree_create_from_stream(sln, NULL, fp, &tree2) == RES_BAD_ARG); 328 CHK(sln_tree_create_from_stream(sln, shtr, NULL, &tree2) == RES_BAD_ARG); 329 CHK(sln_tree_create_from_stream(sln, shtr, fp, NULL) == RES_BAD_ARG); 330 CHK(sln_tree_create_from_stream(sln, shtr, fp, &tree2) == RES_OK); 331 fclose(fp); 332 333 check_tree_equality(tree1, tree2); 334 335 CHK(sln_tree_ref_put(tree1) == RES_OK); 336 CHK(sln_tree_ref_put(tree2) == RES_OK); 337 } 338 339 /******************************************************************************* 340 * Test function 341 ******************************************************************************/ 342 int 343 main(int argc, char** argv) 344 { 345 struct sln_device_create_args dev_args = SLN_DEVICE_CREATE_ARGS_DEFAULT; 346 struct sln_tree_create_args tree_args = SLN_TREE_CREATE_ARGS_DEFAULT; 347 struct sln_device* sln = NULL; 348 349 struct shtr_create_args shtr_args = SHTR_CREATE_ARGS_DEFAULT; 350 struct shtr* shtr = NULL; 351 struct shtr_line_list_load_args line_load_args = SHTR_LINE_LIST_LOAD_ARGS_NULL; 352 struct shtr_line_list* line_list = NULL; 353 struct shtr_isotope_metadata* metadata = NULL; 354 355 FILE* fp_lines = NULL; 356 FILE* fp_mdata = NULL; 357 (void)argc, (void)argv; 358 359 /* Generate the file of the isotope metadata */ 360 CHK(fp_mdata = tmpfile()); 361 fprintf(fp_mdata, "Molecule # Iso Abundance Q(296K) gj Molar Mass(g)\n"); 362 write_shtr_molecule(fp_mdata, &g_H2O); 363 write_shtr_molecule(fp_mdata, &g_CO2); 364 write_shtr_molecule(fp_mdata, &g_O3); 365 rewind(fp_mdata); 366 367 /* Generate the file of lines */ 368 CHK(fp_lines = tmpfile()); 369 write_shtr_lines(fp_lines, g_lines, g_nlines); 370 rewind(fp_lines); 371 372 /* Load the isotope metadata and the lines */ 373 shtr_args.verbose = 1; 374 CHK(shtr_create(&shtr_args, &shtr) == RES_OK); 375 CHK(shtr_isotope_metadata_load_stream(shtr, fp_mdata, NULL, &metadata) == RES_OK); 376 377 line_load_args.filename = "stream"; 378 line_load_args.file = fp_lines; 379 CHK(shtr_line_list_load(shtr, &line_load_args, &line_list) == RES_OK); 380 381 CHK(fclose(fp_lines) == 0); 382 CHK(fclose(fp_mdata) == 0); 383 384 dev_args.verbose = 1; 385 CHK(sln_device_create(&dev_args, &sln) == RES_OK); 386 387 /* Create the mixture */ 388 tree_args.metadata = metadata; 389 tree_args.lines = line_list; 390 tree_args.molecules[SHTR_H2O].concentration = 1.0/3.0; 391 tree_args.molecules[SHTR_H2O].cutoff = 25; 392 tree_args.molecules[SHTR_CO2].concentration = 1.0/3.0; 393 tree_args.molecules[SHTR_CO2].cutoff = 50; 394 tree_args.molecules[SHTR_O3 ].concentration = 1.0/3.0; 395 tree_args.molecules[SHTR_O3 ].cutoff = 25; 396 tree_args.pressure = 1; 397 tree_args.temperature = 296; 398 399 test_tree(sln, &tree_args, line_list); 400 test_tree_serialization(sln, &tree_args, shtr); 401 402 CHK(sln_device_ref_put(sln) == RES_OK); 403 CHK(shtr_ref_put(shtr) == RES_OK); 404 CHK(shtr_line_list_ref_put(line_list) == RES_OK); 405 CHK(shtr_isotope_metadata_ref_put(metadata) == RES_OK); 406 CHK(mem_allocated_size() == 0); 407 return 0; 408 }