sln_tree_build.c (53029B)
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 file is part of Star-Line. 7 * 8 * This program is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 20 21 #include "sln.h" 22 #include "sln_device_c.h" 23 #include "sln_polyline.h" 24 #include "sln_tree_c.h" 25 26 #include <star/shtr.h> 27 28 #include <rsys/cstr.h> 29 #include <rsys/math.h> 30 31 #include <omp.h> 32 33 /* Structure used to track the progress of a construction phase */ 34 struct progress { 35 /* Absolute progression */ 36 size_t total; /* Total number of items to process */ 37 size_t processed; /* Number of items already processed */ 38 39 /* Relative progression */ 40 int pcent; /* Currently displayed percentage */ 41 int pcent_step; /* Increment between displayed percentages */ 42 43 const char* msg; /* Message to add before the displayed percentage */ 44 }; 45 #define PROGRESS_DEFAULT__ {0,0,0,10,NULL} 46 static const struct progress PROGRESS_DEFAULT = PROGRESS_DEFAULT__; 47 48 /* Structure containing temporary variables used to create polyline on a leaf 49 * These variables are not declared locally within the function responsible for 50 * constructing this polyline in order to avoid having to allocate and 51 * deallocate them with each call to the function. Instead, they are passed as 52 * function inputs so as to share, as much as possible, the memory space 53 * allocated during previous calls to the function, thereby reducing the cost of 54 * allocation and deallocation. */ 55 struct build_leaf_polyline_scratch { 56 struct darray_vertex vertices; /* Polyline vertices */ 57 struct darray_node nodes; /* Dummy leaf nodes */ 58 59 /* Temp vertex buffer used when merging polylines into a single one */ 60 struct darray_vertex vertices_tmp; 61 }; 62 63 static res_T 64 merge_child_polylines 65 (struct sln_tree* tree, 66 const size_t inode, /* Node at which child polylines are merged */ 67 const unsigned nchildren, /* #children to merge */ 68 struct darray_node* nodes, /* List of nodes */ 69 struct darray_vertex* vertices); /* List of polyline vertices */ 70 71 static res_T 72 collapse_child_polylines 73 (struct sln_tree* tree, 74 const size_t inode, /* Node at which child polylines are merged */ 75 const unsigned nchildren, /* #children to collapse */ 76 struct darray_node* nodes, /* List of nodes */ 77 struct darray_vertex* scratch, /* Temporary buffer of vertices */ 78 struct darray_vertex* vertices); /* List of polyline vertices */ 79 80 /* A multiplier to be applied to the calculated ka values in order to mitigate 81 * numerical issues related to ka interpolation in the case of a tree with an 82 * upper bound. This interpolation must ensure that the interpolated ka value is 83 * well above the actual ka value, which may not be the case due to numerical 84 * inaccuracies. Hence this constant, which defines a percentage of ka to be 85 * added to the calculated ka values to ensure that any interpolated value is 86 * well above the actual ka value. */ 87 #define KA_ADJUSTEMENT 1.01 88 89 /* Maximum queue size used for the breadth-first tree traversal. For a perfectly 90 * balanced tree, this corresponds to the maximum number of leaves a tree can 91 * have. Note that this value does not need to be too high, since the 92 * breadth-first traversal is only used to partition the first few levels of the 93 * tree until a sufficient number of subtrees have been identified so that they 94 * can then be constructed in parallel using a depth-first algorithm */ 95 #define QUEUE_SIZE_MAX 256 96 97 /******************************************************************************* 98 * Helper functions 99 ******************************************************************************/ 100 static FINLINE uint32_t 101 ui64_to_ui32(const uint64_t ui64) 102 { 103 if(ui64 > UINT32_MAX) 104 VFATAL("%s: overflow %lu.\n", ARG2(FUNC_NAME, ((unsigned long)ui64))); 105 return (uint32_t)ui64; 106 } 107 108 static FINLINE unsigned 109 size_t_to_unsigned(const size_t sz) 110 { 111 if(sz > UINT_MAX) 112 VFATAL("%s: overflow %lu.\n", ARG2(FUNC_NAME, ((unsigned long)sz))); 113 return (unsigned)sz; 114 } 115 116 static void 117 progress_print(struct sln_device* dev, const struct progress* progress) 118 { 119 ASSERT(dev && progress); 120 if(progress->msg) { 121 INFO(dev, "%s%3d%%\n", progress->msg, progress->pcent); 122 } else { 123 INFO(dev, "%3d%%\n", progress->pcent); 124 } 125 } 126 127 static void 128 progress_update 129 (struct sln_device* dev, 130 struct progress* progress, 131 const size_t count) 132 { 133 int pcent = 0; 134 ASSERT(dev && progress); 135 136 #pragma omp critical 137 { 138 progress->processed += count; 139 ASSERT(progress->processed <= progress->total); 140 141 pcent = (int) 142 ( (double)progress->processed*100.0 143 / (double)progress->total 144 + 0.5 /* round */); 145 146 if(pcent/progress->pcent_step > progress->pcent/progress->pcent_step) { 147 progress->pcent = pcent; 148 progress_print(dev, progress); 149 } 150 } 151 } 152 153 static INLINE void 154 build_leaf_polyline_scratch_init 155 (struct mem_allocator* allocator, 156 struct build_leaf_polyline_scratch* scratch) 157 { 158 ASSERT(scratch); 159 darray_vertex_init(allocator, &scratch->vertices); 160 darray_vertex_init(allocator, &scratch->vertices_tmp); 161 darray_node_init(allocator, &scratch->nodes); 162 } 163 164 static INLINE void 165 build_leaf_polyline_scratch_release 166 (struct build_leaf_polyline_scratch* scratch) 167 { 168 ASSERT(scratch); 169 darray_vertex_release(&scratch->vertices); 170 darray_vertex_release(&scratch->vertices_tmp); 171 darray_node_release(&scratch->nodes); 172 } 173 174 static INLINE res_T 175 build_leaf_polyline_from_1line 176 (struct sln_tree* tree, 177 struct sln_node* leaf, 178 struct darray_vertex* vertices) 179 { 180 size_t vertices_range[2]; 181 res_T res = RES_OK; 182 183 ASSERT(tree && leaf && vertices); 184 185 /* Assume that there is only one line per leaf */ 186 ASSERT(leaf->range[0] == leaf->range[1]); 187 188 /* Line meshing */ 189 res = line_mesh 190 (/* in */ tree, leaf->range[0], tree->args.nvertices_hint, 191 /* out */ vertices, vertices_range); 192 if(res != RES_OK) goto error; 193 194 /* Decimate the line mesh */ 195 res = polyline_decimate(tree->sln, darray_vertex_data_get(vertices), 196 vertices_range, (float)tree->args.mesh_decimation_err, tree->args.mesh_type); 197 if(res != RES_OK) goto error; 198 199 /* Shrink the size of the vertices */ 200 darray_vertex_resize(vertices, vertices_range[1] + 1); 201 202 /* Setup the leaf polyline */ 203 leaf->ivertex = vertices_range[0]; 204 leaf->nvertices = ui64_to_ui32(vertices_range[1] - vertices_range[0] + 1); 205 206 exit: 207 return res; 208 error: 209 goto exit; 210 } 211 212 /* Build the polyline of a leaf node containing more than one line. 213 * 214 * Each line is drawn as if it were the only one on the leaf. This allows the 215 * line meshing routine to be used, and consequently the algorithm that relies 216 * on the properties of the lines (symmetry, monotonicity on one half). Next, 217 * this set of polylines is merged/collapsed into a single polyline, which is 218 * the leaf polyline. 219 * 220 * To reuse the merging/collapsing designed for internal nodes, the function 221 * treats a leaf as if it were an internal node. A list of temporary nodes is 222 * thus created, containing not only the leaf node but also its “children” 223 * i.e., its lines. 224 * 225 * Once created, the leaf’s polyline is finally copied into the tree’s vertex 226 * buffer */ 227 static INLINE res_T 228 build_leaf_polyline_from_Nlines 229 (struct sln_tree* tree, 230 struct sln_node* leaf, 231 struct build_leaf_polyline_scratch* scratch, 232 struct darray_vertex* vertices) 233 { 234 const struct sln_vertex* src = NULL; 235 struct sln_vertex* dst = NULL; 236 size_t memsz = 0; 237 238 size_t nnodes = 0; 239 unsigned nlines = 0; /* Number of lines in the leaf */ 240 unsigned i = 0; 241 res_T res = RES_OK; 242 243 ASSERT(tree && leaf && scratch && vertices); 244 245 /* Compute the number of lines of the leaf */ 246 nlines = size_t_to_unsigned(leaf->range[1] - leaf->range[0] + 1/*inclusive*/); 247 ASSERT(nlines > 1); /* Assume there is more than one line per leaf */ 248 249 nnodes = nlines + 1/*leaf*/; 250 251 /* Helper macro */ 252 #define NODE(Id) (darray_node_data_get(&scratch->nodes) + (Id)) 253 254 /* Allocate the temporary list of nodes, one node per leaf to which on 255 * additionnal node is added for the leaf */ 256 res = darray_node_resize(&scratch->nodes, nnodes); 257 if(res != RES_OK) goto error; 258 memset(NODE(0), 0, sizeof(struct sln_node)*nnodes); 259 260 /* Clean up the temporary list of vertices */ 261 darray_vertex_clear(&scratch->vertices); 262 263 /* The leaf node will be the first one. Its "child" representing the node of 264 * each lines, will be store after it in the node list */ 265 NODE(0)->offset = 1; 266 NODE(0)->range[0] = leaf->range[0]; 267 NODE(0)->range[1] = leaf->range[1]; 268 269 FOR_EACH(i, 0, nlines) { 270 size_t vertices_range[2] = {0, 0}; /* Range in the vertex buffer */ 271 const size_t ichild = NODE(0)->offset + i; /* Index of the child */ 272 const size_t iline = leaf->range[0] + i; 273 274 /* Mesh the line in the temporary vertex buffer */ 275 res = line_mesh 276 (/* in */ tree, iline, tree->args.nvertices_hint, 277 /* out */ &scratch->vertices, vertices_range); 278 if(res != RES_OK) goto error; 279 280 /* Decimate the line mesh */ 281 res = polyline_decimate(tree->sln, 282 darray_vertex_data_get(&scratch->vertices), vertices_range, 283 (float)tree->args.mesh_decimation_err, tree->args.mesh_type); 284 if(res != RES_OK) goto error; 285 286 NODE(ichild)->ivertex = vertices_range[0]; 287 NODE(ichild)->nvertices = ui64_to_ui32(vertices_range[1] - vertices_range[0] + 1); 288 NODE(ichild)->range[0] = iline; 289 NODE(ichild)->range[1] = iline; 290 } 291 292 /* Merge/collapse the polylines of each lines associated to nodes. 293 * These nodes are the "children" of the leaf */ 294 if(tree->args.collapse_polylines) { 295 res = collapse_child_polylines(tree, 0, nlines, 296 &scratch->nodes, &scratch->vertices_tmp, &scratch->vertices); 297 } else { 298 res = merge_child_polylines(tree, 0, nlines, 299 &scratch->nodes, &scratch->vertices); 300 } 301 if(res != RES_OK) goto error; 302 303 /* Setup the leaf */ 304 leaf->ivertex = darray_vertex_size_get(vertices); 305 leaf->nvertices = NODE(0/*leaf*/)->nvertices; 306 307 /* Copy the leaf vertices from temporary buffer to the vertex buffer of the 308 * tree */ 309 res = darray_vertex_resize(vertices, leaf->ivertex + leaf->nvertices); 310 if(res != RES_OK) goto error; 311 src = darray_vertex_cdata_get(&scratch->vertices) + NODE(0/*leaf*/)->ivertex; 312 dst = darray_vertex_data_get(vertices) + leaf->ivertex; 313 memsz = leaf->nvertices * sizeof(*src); 314 memcpy(dst, src, memsz); 315 316 #undef NODE 317 318 exit: 319 return res; 320 error: 321 goto exit; 322 } 323 324 static INLINE res_T 325 build_leaf_polyline 326 (struct sln_tree* tree, 327 struct sln_node* leaf, 328 struct build_leaf_polyline_scratch* scratch, 329 struct darray_vertex* vertices) 330 { 331 size_t nlines = 0; /* Number of lines in the leaf */ 332 ASSERT(tree && leaf); 333 334 nlines = leaf->range[1] - leaf->range[0] + 1; 335 336 if(nlines == 1) { 337 return build_leaf_polyline_from_1line(tree, leaf, vertices); 338 } else { 339 return build_leaf_polyline_from_Nlines(tree, leaf, scratch, vertices); 340 } 341 } 342 343 static INLINE double 344 eval_ka 345 (const struct sln_node* node, 346 const struct sln_vertex* vertices, 347 const double wavenumber) 348 { 349 struct sln_mesh mesh = SLN_MESH_NULL; 350 double ka = 0; 351 ASSERT(node && vertices); 352 353 /* Whether the mesh to be constructed corresponds to the spectrum or its upper 354 * limit, use the node mesh to calculate the value of ka at a given wave 355 * number. Calculating the value from the node lines would take far too long*/ 356 mesh.vertices = vertices + node->ivertex; 357 mesh.nvertices = node->nvertices; 358 ka = sln_mesh_eval(&mesh, wavenumber); 359 return ka; 360 } 361 362 /* Merge all child polylines in a single step. The polylines are merged before 363 * being decimated */ 364 res_T 365 merge_child_polylines 366 (struct sln_tree* tree, 367 const size_t inode, 368 const unsigned nchildren, 369 struct darray_node* nodes, 370 struct darray_vertex* vertex_list) 371 { 372 /* Helper constant */ 373 static const size_t NO_MORE_VERTEX = SIZE_MAX; 374 375 /* Polyline vertices */ 376 #define NCHILDREN_MAX \ 377 ( SLN_TREE_ARITY_MAX > SLN_LEAF_NLINES_MAX \ 378 ? SLN_TREE_ARITY_MAX : SLN_LEAF_NLINES_MAX) 379 size_t children_ivtx[NCHILDREN_MAX] = {0}; 380 381 size_t vertices_range[2] = {0,0}; 382 struct sln_vertex* vertices = NULL; 383 size_t ivtx = 0; 384 size_t nvertices = 0; 385 386 /* Miscellaneous */ 387 unsigned i = 0; 388 res_T res = RES_OK; 389 390 /* Pre-conditions */ 391 ASSERT(tree && inode < darray_node_size_get(nodes)); 392 ASSERT(nchildren >= 2 && nchildren <= NCHILDREN_MAX); 393 394 #define NODE(Id) (darray_node_data_get(nodes) + (Id)) 395 396 /* Compute the number of vertices to be merged, 397 * i.e., the sum of vertices of the children */ 398 nvertices = 0; 399 FOR_EACH(i, 0, nchildren) { 400 const size_t ichild = inode + NODE(inode)->offset + i; 401 nvertices += NODE(ichild)->nvertices; 402 } 403 404 /* Define the vertices range of the merged polyline */ 405 vertices_range[0] = darray_vertex_size_get(vertex_list); 406 vertices_range[1] = vertices_range[0] + nvertices - 1/*inclusive bound*/; 407 408 /* Allocate the memory space to store the new polyline */ 409 res = darray_vertex_resize(vertex_list, vertices_range[1]+1); 410 if(res != RES_OK) { 411 ERROR(tree->sln, "Error in merging polylines -- %s.\n", res_to_cstr(res)); 412 goto error; 413 } 414 vertices = darray_vertex_data_get(vertex_list); 415 416 /* Initialize the vertex index list. For each child, the initial value 417 * corresponds to the index of its first vertex. This index will be 418 * incremented as vertices are merged into the parent polyline. */ 419 FOR_EACH(i, 0, nchildren) { 420 const size_t ichild = inode + NODE(inode)->offset + i; 421 children_ivtx[i] = NODE(ichild)->ivertex; 422 } 423 424 FOR_EACH(ivtx, vertices_range[0], vertices_range[1]+1/*inclusive bound*/) { 425 double ka = 0; 426 double nu = INF; 427 428 /* The number of vertices corresponding to the current wave number for which 429 * the parent ka is calculated. It is at least equal to one, since this nu 430 * is defined by the child vertices, but may be greater if multiple children 431 * share the same vertex, i.e., a ka value calculated for the same nu */ 432 unsigned nvertices_merged = 0; 433 434 /* Find the minimum wave number among the vertices of the child vertices 435 * that are candidates for merging */ 436 FOR_EACH(i, 0, nchildren) { 437 const size_t child_ivtx = children_ivtx[i]; 438 if(child_ivtx != NO_MORE_VERTEX) { 439 nu = MMIN(nu, vertices[child_ivtx].wavenumber); 440 } 441 } 442 ASSERT(nu != INF); /* At least one vertex must have been found */ 443 444 /* Compute the value of ka at the wave number determined above */ 445 FOR_EACH(i, 0, nchildren) { 446 const size_t child_ivtx = children_ivtx[i]; 447 const struct sln_node* child = NODE(inode + NODE(inode)->offset + i); 448 449 if(child_ivtx == NO_MORE_VERTEX 450 || nu != vertices[child_ivtx].wavenumber) { 451 /* The wave number does not correspond to a vertex in the current 452 * child's mesh. Therefore, its contribution to the parent node's ka is 453 * computed */ 454 ka += eval_ka(child, vertices, nu); 455 456 } else { 457 /* The wave number is the one for which the child node stores a ka 458 * value. Add it to the parent node's ka value and designate the child's 459 * next vertex as a candidate for merging into the parent. The exception 460 * is when all vertices of the child have already been merged. In this 461 * case, report that the child no longer has any candidate vertices */ 462 ka += vertices[child_ivtx].ka; 463 ++children_ivtx[i]; 464 if(children_ivtx[i] >= child->ivertex + child->nvertices) { 465 children_ivtx[i] = NO_MORE_VERTEX; 466 } 467 ++nvertices_merged; /* Record that a vertex has been merged */ 468 } 469 } 470 471 /* Setup the parent vertex */ 472 vertices[ivtx].wavenumber = (float)nu; 473 vertices[ivtx].ka = (float)ka; 474 475 /* If multiple child vertices have been merged, then a single wave number 476 * corresponds to a vertex with multiple children. The number of parent 477 * vertices is therefore no longer the sum of the number of its children's 478 * vertices, since some vertices are duplicated. Hence the following 479 * adjustment, which removes the duplicate vertices. */ 480 vertices_range[1] -= (nvertices_merged-1); 481 } 482 483 /* Decimate the resulting polyline */ 484 res = polyline_decimate(tree->sln, darray_vertex_data_get(vertex_list), 485 vertices_range, (float)tree->args.mesh_decimation_err, tree->args.mesh_type); 486 if(res != RES_OK) goto error; 487 488 /* Setup the node polyline */ 489 NODE(inode)->ivertex = vertices_range[0]; 490 NODE(inode)->nvertices = ui64_to_ui32(vertices_range[1] - vertices_range[0] + 1); 491 492 /* It is necessary to ensure that the recorded vertices define a polyline 493 * along which any value (calculated by linear interpolation) is well above 494 * the sum of the corresponding values of the polylines to be merged. However, 495 * although this is guaranteed by definition for the vertices of the polyline, 496 * numerical uncertainty may nevertheless introduce errors that violate this 497 * criterion. Hence the following adjustment, which slightly increases the ka 498 * of the mesh so as to guarantee this constraint between the mesh of a node 499 * and that of its children */ 500 if(tree->args.mesh_type == SLN_MESH_UPPER) { 501 FOR_EACH(ivtx, vertices_range[0], vertices_range[1]+1/*inclusive bound*/) { 502 const double ka = vertices[ivtx].ka; 503 vertices[ivtx].ka = (float)(ka*KA_ADJUSTEMENT); 504 } 505 } 506 507 /* Shrink the size of the vertices */ 508 darray_vertex_resize(vertex_list, vertices_range[1] + 1); 509 510 #undef NODE 511 #undef NCHILDREN_MAX 512 513 exit: 514 return res; 515 error: 516 goto exit; 517 } 518 519 /* Merge child polylines by combining them in pairs (the resulting polyline is 520 * then simplified), and repeat this process until only a single polyline 521 * remains. This polyline becomes the node's polyline */ 522 static res_T 523 collapse_child_polylines 524 (struct sln_tree* tree, 525 const size_t inode, 526 const unsigned nchildren, 527 struct darray_node* nodes, 528 struct darray_vertex* scratch, 529 struct darray_vertex* vertex_list) 530 { 531 /* Polylines to be collapsed, i.e., the ids of their first and last vertices */ 532 #define NCHILDREN_MAX \ 533 ( SLN_TREE_ARITY_MAX > SLN_LEAF_NLINES_MAX \ 534 ? SLN_TREE_ARITY_MAX : SLN_LEAF_NLINES_MAX) 535 size_t poly_parts[NCHILDREN_MAX][2] = {0}; 536 size_t nparts; 537 538 /* Indices of the first and last vertices of the resulting polyline. 539 * These indices are absolute to the tree's vertex list */ 540 size_t vertices_range[2] = {0,0}; 541 542 /* Redux double buffering */ 543 struct sln_vertex* buf[2] = {NULL, NULL}; 544 int r, w; /* Index of the buffer in read/write */ 545 546 /* Miscellaneous */ 547 struct sln_vertex* vertices = NULL; /* Pointer to the tree's vertex buffer */ 548 size_t range_merge[2] = {0,0}; /* vertex range of a merged polyline */ 549 size_t nvertices = 0; /* #vertices of the resulting polyline */ 550 size_t ivtx = 0; 551 unsigned i = 0; 552 int ncollapses = 0; /* Number of collapse steps */ 553 res_T res = RES_OK; 554 555 /* Pre-conditions */ 556 ASSERT(tree && inode < darray_node_size_get(nodes)); 557 ASSERT(nchildren >= 2 && nchildren <= NCHILDREN_MAX); 558 559 #define NODE(Id) (darray_node_data_get(nodes) + (Id)) 560 561 /* Compute the number of vertices to be merged, 562 * i.e., the sum of vertices of the children */ 563 nvertices = 0; 564 FOR_EACH(i, 0, nchildren) { 565 const size_t ichild = inode + NODE(inode)->offset + i; 566 nvertices += NODE(ichild)->nvertices; 567 } 568 569 /* Define the vertices range of the merged polyline */ 570 vertices_range[0] = darray_vertex_size_get(vertex_list); 571 vertices_range[1] = vertices_range[0] + nvertices - 1/*inclusive bound*/; 572 573 /* Allocate the memory space required to store the new polyline and the 574 * temporary buffer. This is the memory space in which the collapse 575 * procedure's double buffering will take place. Each buffer will be used 576 * alternately as a read/write buffer when reducing the polylines of the 577 * child nodes */ 578 if((res = darray_vertex_resize(vertex_list, vertices_range[1]+1)) != RES_OK 579 || (res = darray_vertex_resize(scratch, nvertices)) != RES_OK) { 580 ERROR(tree->sln, "Error in merging polylines -- %s.\n", res_to_cstr(res)); 581 goto error; 582 } 583 584 /* Recover the memory space of the tree's vertices */ 585 vertices = darray_vertex_data_get(vertex_list); 586 587 /* Recover the memory space to be used in the Redux process. 588 * 589 * In the vertex buffer, this refers to the newly allocated memory space used 590 * during the collapse. The beginning of the buffer contains the vertices of 591 * the registered nodes and must therefore not be modified. At the end of the 592 * collapse, this space will contain the vertices of the polylines resulting 593 * from the collapse of the child nodes' polylines. 594 * 595 * The scratch buffer is used as-is, since its sole purpose is to temporarily 596 * store the vertices of the polylines to be merged. */ 597 buf[0] = vertices + vertices_range[0]; 598 buf[1] = darray_vertex_data_get(scratch); 599 600 /* Initially, the number of partitions to be reduced is equal to the number of 601 * children */ 602 nparts = nchildren; 603 604 /* Calculate the number of reduction steps, i.e., the logarithm of the power 605 * of 2 that is greater than or equal to the number of polylines to be 606 * reduced, to which one is added to obtain the number of steps, not the index 607 * of the last step */ 608 ncollapses = log2i((int)round_up_pow2(nparts)) + 1; 609 610 /* Set the index of the write buffer so that, once the collapse process is 611 * complete, the last write buffer is the one whose memory space is allocated 612 * in the tree's vertex buffer. This way, no additional copies will be needed 613 * to store the result of the reduction */ 614 w = ncollapses % 2 ? 0 : 1; 615 616 /* Initialize the polylines to be reduced, i.e., copy the child vertices into 617 * a collapse buffer and record their index ranges */ 618 ivtx = 0; 619 for(i = 0; i < nchildren; ++i) { 620 const size_t ichild = inode + NODE(inode)->offset + i; 621 const struct sln_node* child = NODE(ichild); 622 const size_t memsz = child->nvertices * sizeof(struct sln_vertex); 623 const struct sln_vertex* src = vertices + child->ivertex; 624 struct sln_vertex* dst = buf[w] + ivtx; 625 626 memcpy(dst, src, memsz); 627 poly_parts[i][0] = ivtx; 628 poly_parts[i][1] = ivtx + child->nvertices - 1/*inclusive bound*/; 629 630 ivtx += child->nvertices; 631 } 632 633 r = w; /* Index of the buffer from which to read the data */ 634 w =!r; /* Index of the buffer into which to write the data */ 635 636 637 /* As long as the number of segments is not equal to one, there are still 638 * polylines to be merged in pairs */ 639 while(nparts > 1) { 640 size_t ipart = 0; 641 642 for(ipart=0; ipart < nparts-1; ipart+=2) { 643 struct sln_mesh mesh0 = SLN_MESH_NULL; 644 struct sln_mesh mesh1 = SLN_MESH_NULL; 645 646 /* Retrieve the two polylines to be merged */ 647 const size_t* part0 = poly_parts[ipart+0]; 648 const size_t* part1 = poly_parts[ipart+1]; 649 650 /* Calculate the partition index of the merged polyline, that is, the index 651 * that will contain the information for the polyline resulting from the 652 * merge: the first and last indices of its in the vertex array currently 653 * being written */ 654 const size_t ipart_merge = ipart/2; 655 656 /* Compute the number of vertices of the merged polyline */ 657 const size_t nvtx = 658 ((part0[1] - part0[0]) + 1/*inclusive bound*/) 659 + ((part1[1] - part1[0]) + 1/*inclusive bound*/); 660 661 /* For each child, initialized its vertex index to its first vertex. This 662 * index will be incremented as vertices are merged into the merged 663 * polyline */ 664 size_t ivtx0 = part0[0]; 665 size_t ivtx1 = part1[0]; 666 667 /* Set the vertex index range for the merged polyline in the write buffer */ 668 range_merge[0] = part0[0]; 669 range_merge[1] = range_merge[0] + nvtx-1/*inclusive bound*/; 670 671 /* Setup the mesh of the two polylines to be merged */ 672 mesh0.vertices = buf[r] + part0[0]; mesh0.nvertices = part0[1]-part0[0]+1; 673 mesh1.vertices = buf[r] + part1[0]; mesh1.nvertices = part1[1]-part1[0]+1; 674 675 /* Merge the polylines */ 676 FOR_EACH(ivtx, range_merge[0], range_merge[1]+1/*inclusive bound*/) { 677 const double nu0 = ivtx0 <= part0[1] ? buf[r][ivtx0].wavenumber : INF; 678 const double nu1 = ivtx1 <= part1[1] ? buf[r][ivtx1].wavenumber : INF; 679 double ka = 0; 680 double nu = 0; 681 682 /* Find the minimum wave number among the vertices of the child vertices 683 * that are candidates for merging */ 684 if(nu0 < nu1) { /* The vertex comes from the child0 */ 685 nu = buf[r][ivtx0].wavenumber; 686 ka = buf[r][ivtx0].ka + sln_mesh_eval(&mesh1, nu); 687 ++ivtx0; 688 689 } else if(nu0 > nu1) { /* The vertex comes from the child1 */ 690 nu = buf[r][ivtx1].wavenumber; 691 ka = buf[r][ivtx1].ka + sln_mesh_eval(&mesh0, nu); 692 ++ivtx1; 693 694 } else { /* The vertex is shared by node0 and node1 */ 695 nu = buf[r][ivtx0].wavenumber; 696 ka = buf[r][ivtx0].ka + buf[r][ivtx1].ka; 697 --range_merge[1]; /* Remove duplicate */ 698 ++ivtx0; 699 ++ivtx1; 700 } 701 buf[w][ivtx].wavenumber = (float)nu; 702 buf[w][ivtx].ka = (float)ka; 703 } 704 705 /* Decimate the resulting polyline */ 706 res = polyline_decimate(tree->sln, buf[w], range_merge, 707 (float)tree->args.mesh_decimation_err, tree->args.mesh_type); 708 if(res != RES_OK) goto error; 709 710 /* Setup the partition of the merge polyline */ 711 poly_parts[ipart_merge][0] = range_merge[0]; 712 poly_parts[ipart_merge][1] = range_merge[1]; 713 } 714 715 /* If there is a polyline that has not been merged, copy its vertices to the 716 * write buffer so that it can be processed in the next reduction step */ 717 if(nparts % 2) { 718 const size_t* remain_part = poly_parts[nparts-1]; 719 const size_t nvtx = remain_part[1] - remain_part[0] + 1/*inclusive*/; 720 const size_t memsz = nvtx * sizeof(struct sln_vertex); 721 const struct sln_vertex* src = buf[r] + remain_part[0]; 722 struct sln_vertex* dst = buf[w] + remain_part[0]; 723 724 memcpy(dst, src, memsz); 725 poly_parts[nparts/2][0] = remain_part[0]; 726 poly_parts[nparts/2][1] = remain_part[1]; 727 } 728 729 #ifndef NDEBUG 730 FOR_EACH(i, 1, (nparts+1/*ceil*/)/2) { 731 ASSERT(poly_parts[i][0] > poly_parts[i-1][1]); 732 } 733 #endif 734 735 /* Update the number of partitions to be reduced */ 736 nparts = (nparts + 1/*ceil*/)/2; 737 738 /* Swap read/write buffers */ 739 r = !r; 740 w = !w; 741 } 742 743 nvertices = (range_merge[1] - range_merge[0]) + 1/*inclusive bound*/; 744 vertices_range[1] = vertices_range[0] + nvertices - 1/*inclusive bound*/; 745 746 /* Assumed that double buffering was configured to ensure that the resulting 747 * polyline is stored in the tree vertex buffer */ 748 ASSERT(buf[r] != darray_vertex_cdata_get(scratch)); 749 750 /* Setup the node */ 751 NODE(inode)->ivertex = vertices_range[0]; 752 NODE(inode)->nvertices = ui64_to_ui32(nvertices); 753 754 /* It is necessary to ensure that the recorded vertices define a polyline 755 * along which any value (calculated by linear interpolation) is well above 756 * the sum of the corresponding values of the polylines to be merged. However, 757 * although this is guaranteed by definition for the vertices of the polyline, 758 * numerical uncertainty may nevertheless introduce errors that violate this 759 * criterion. Hence the following adjustment, which slightly increases the ka 760 * of the mesh so as to guarantee this constraint between the mesh of a node 761 * and that of its children */ 762 if(tree->args.mesh_type == SLN_MESH_UPPER) { 763 FOR_EACH(ivtx, vertices_range[0], vertices_range[1]+1/*inclusive bound*/) { 764 const double ka = vertices[ivtx].ka; 765 vertices[ivtx].ka = (float)(ka*KA_ADJUSTEMENT); 766 } 767 } 768 769 /* Shrink the size of the vertices */ 770 darray_vertex_resize(vertex_list, vertices_range[1] + 1); 771 772 #undef NCHILDREN 773 #undef NODE 774 775 exit: 776 return res; 777 error: 778 goto exit; 779 } 780 static res_T 781 build_polylines 782 (struct sln_tree* tree, 783 const size_t root_index, 784 const size_t nodes_count, /* Total number of nodes in the tree (for debug) */ 785 struct darray_vertex* vertices, 786 struct progress* progress) 787 { 788 /* Stack */ 789 #define STACK_SIZE (SLN_TREE_DEPTH_MAX*SLN_TREE_ARITY_MAX) 790 size_t stack[STACK_SIZE]; 791 size_t istack = 0; 792 793 /* Progress */ 794 size_t nnodes_processed = 0; 795 796 /* Miscellaneous */ 797 struct build_leaf_polyline_scratch scratch; 798 size_t inode = 0; 799 res_T res = RES_OK; 800 801 ASSERT(tree && nodes_count != 0); 802 ASSERT(root_index < darray_node_size_get(&tree->nodes)); 803 (void)nodes_count; /* Avoid "Unused variable" warning */ 804 805 build_leaf_polyline_scratch_init(tree->sln->allocator, &scratch); 806 807 #define NODE(Id) (darray_node_data_get(&tree->nodes) + (Id)) 808 #define IS_LEAF(Id) (NODE(Id)->offset == 0) 809 810 /* Push back SIZE_MAX which, once pop up, will mark the end of recursion */ 811 stack[istack++] = SIZE_MAX; 812 813 inode = root_index; /* Root node */ 814 while(inode != SIZE_MAX) { 815 const size_t istack_saved = istack; 816 817 if(IS_LEAF(inode)) { 818 res = build_leaf_polyline(tree, NODE(inode), &scratch, vertices); 819 if(res != RES_OK) goto error; 820 821 inode = stack[--istack]; /* Pop the next node */ 822 823 } else { 824 const size_t ichild0 = inode + NODE(inode)->offset + 0; 825 const struct sln_node* node = darray_node_cdata_get(&tree->nodes)+inode; 826 const unsigned nchildren = node_child_count(node, tree->args.arity); 827 int child_polylines_are_missing = 1; 828 size_t i = 0; 829 830 FOR_EACH(i, 0, nchildren) { 831 child_polylines_are_missing = NODE(ichild0 + i)->nvertices == 0; 832 if(child_polylines_are_missing) break; 833 } 834 835 /* Child nodes have their polyline created */ 836 if(!child_polylines_are_missing) { 837 #ifndef NDEBUG 838 /* Check that all children have their polylines created */ 839 FOR_EACH(i, 1, nchildren) { 840 const size_t ichild = ichild0 + i; 841 ASSERT(NODE(ichild)->nvertices != 0); 842 } 843 #endif 844 if(tree->args.collapse_polylines) { 845 res = collapse_child_polylines(tree, inode, nchildren, &tree->nodes, 846 &scratch.vertices_tmp, vertices); 847 } else { 848 res = merge_child_polylines 849 (tree, inode, nchildren, &tree->nodes, vertices); 850 } 851 if(res != RES_OK) goto error; 852 853 inode = stack[--istack]; /* Pop the next node */ 854 855 /* Child nodes have NOT their polyline created */ 856 } else { 857 ASSERT(istack + (nchildren - 1/*ichild0*/ + 1/*inode*/) <= STACK_SIZE); 858 stack[istack++] = inode; /* Push the current node */ 859 860 /* Push the child nodes, except for those whose polyline has already 861 * been constructed, as is the case when the child node was the root 862 * of a separately constructed subtree */ 863 FOR_EACH_REVERSE(i, nchildren, 0) { 864 const size_t ichild = ichild0 + i-1; 865 if(NODE(ichild)->nvertices == 0) stack[istack++] = ichild; 866 } 867 868 /* Ensure that at least one child was pushed */ 869 ASSERT(stack[istack-1] != inode); 870 871 inode = stack[--istack]; /* Recursively build the polyline of the 1st child */ 872 } 873 } 874 875 /* Handle progression bar */ 876 if(istack < istack_saved) { 877 size_t nnodes = istack_saved - istack; 878 879 nnodes_processed += nnodes; 880 progress_update(tree->sln, progress, nnodes); 881 } 882 } 883 ASSERT(nnodes_processed == nodes_count); 884 885 #undef NODE 886 #undef IS_LEAF 887 #undef LOG_MSG 888 #undef STACK_SIZE 889 890 exit: 891 build_leaf_polyline_scratch_release(&scratch); 892 return res; 893 error: 894 goto exit; 895 } 896 897 static res_T 898 partition_lines_depth_first 899 (struct sln_tree* tree, 900 const size_t root_index, 901 struct darray_node* nodes, 902 struct progress* progress) 903 { 904 /* Stack */ 905 #define STACK_SIZE (SLN_TREE_DEPTH_MAX*(SLN_TREE_ARITY_MAX-1)) 906 size_t stack[STACK_SIZE]; 907 size_t istack = 0; 908 909 /* Progress */ 910 size_t nlines_total = 0; 911 size_t nlines_processed = 0; 912 913 /* Miscellaneous */ 914 size_t inode = 0; 915 res_T res = RES_OK; 916 917 /* Pre-condition */ 918 ASSERT(tree && nodes && progress); 919 ASSERT(root_index < darray_node_size_get(nodes)); 920 (void)nlines_total; /* Avoid "Unused variable" warning */ 921 922 #define NODE(Id) (darray_node_data_get(nodes) + (Id)) 923 #define CREATE_NODE { \ 924 res = darray_node_push_back(nodes, &SLN_NODE_NULL); \ 925 if(res != RES_OK) goto error; \ 926 } (void)0 927 928 nlines_total = NODE(root_index)->range[1] - NODE(root_index)->range[0] + 1; 929 nlines_processed = 0; 930 931 /* Push back SIZE_MAX which, once pop up, will mark the end of recursion */ 932 stack[istack++] = SIZE_MAX; 933 934 inode = root_index; /* Root node */ 935 while(inode != SIZE_MAX) { 936 /* #lines into the node */ 937 size_t nlines_node = NODE(inode)->range[1] - NODE(inode)->range[0] + 1; 938 939 /* Make a leaf */ 940 if(nlines_node <= tree->args.leaf_nlines) { 941 942 NODE(inode)->offset = 0; 943 inode = stack[--istack]; /* Pop the next node */ 944 945 nlines_processed += nlines_node; /* For debug */ 946 947 progress_update(tree->sln, progress, nlines_node); 948 949 /* Split the node */ 950 } else { 951 size_t node_range[2] = {0,0}; 952 size_t nlines_child_min = 0; /* Min #lines per child */ 953 size_t nlines_remain = 0; 954 size_t nchildren = 0; 955 size_t iline = 0; 956 size_t i = 0; 957 958 /* Calculate the index of the first child */ 959 size_t ichildren = darray_node_size_get(nodes); 960 961 /* Compute how the number of children that node has */ 962 nchildren = node_child_count(NODE(inode), tree->args.arity); 963 964 ASSERT(nchildren <= tree->args.arity); 965 ASSERT(ichildren > inode); 966 967 node_range[0] = NODE(inode)->range[0]; 968 node_range[1] = NODE(inode)->range[1]; 969 970 /* Define the offset from the current node to its children */ 971 NODE(inode)->offset = ui64_to_ui32((uint64_t)(ichildren - inode)); 972 973 nlines_child_min = nlines_node / nchildren; 974 nlines_remain = nlines_node % nchildren; 975 976 iline = node_range[0]; 977 FOR_EACH(i, 0, nchildren) { 978 /* Compute the number of lines per child. Start by assigning the minimum 979 * number of lines to each child, then distribute the remaining lines 980 * among the first children */ 981 size_t nlines_child = nlines_child_min + (i < nlines_remain); 982 983 CREATE_NODE; 984 985 /* Set the range of lines line for the newly created child. Note that 986 * the boundaries of the range are inclusive, which is why 1 is 987 * subtracted to the upper bound */ 988 NODE(ichildren+i)->range[0] = iline; 989 NODE(ichildren+i)->range[1] = iline + nlines_child - 1/*inclusive bound*/; 990 iline += nlines_child; 991 992 /* Check that the child's lines are a subset of the parent's lines */ 993 ASSERT(NODE(ichildren+i)->range[0] >= node_range[0]); 994 ASSERT(NODE(ichildren+i)->range[1] <= node_range[1]); 995 } 996 997 inode = ichildren; /* Make the first child the current node */ 998 999 /* Push the other children */ 1000 ASSERT(istack + (nchildren-1/*1st child*/) <= STACK_SIZE); 1001 FOR_EACH_REVERSE(i, nchildren-1, 0) stack[istack++] = ichildren + i; 1002 } 1003 } 1004 ASSERT(nlines_processed == nlines_total); 1005 1006 #undef NODE 1007 #undef CREATE_NODE 1008 #undef STACK_SIZE 1009 1010 exit: 1011 return res; 1012 error: 1013 goto exit; 1014 } 1015 1016 static res_T 1017 partition_lines_breadth_first 1018 (struct sln_tree* tree, 1019 const size_t root_index, 1020 const unsigned nleaves_max_hint) /* Advice on the maxium of leafs to create */ 1021 { 1022 /* Static memory space of the queue */ 1023 struct item { 1024 struct list_node link; 1025 size_t inode; 1026 } items[QUEUE_SIZE_MAX]; 1027 1028 /* Linked lists for managing queue data */ 1029 struct list_node free_items; 1030 struct list_node queue; 1031 size_t nnodes = 0; /* Number of enqueud nodes */ 1032 1033 /* Miscellaneous */ 1034 size_t nlines_total = 0; 1035 size_t nleaves = 0; 1036 size_t i = 0; 1037 res_T res = RES_OK; 1038 1039 /* Pre-conditions */ 1040 ASSERT(tree); 1041 ASSERT(root_index < darray_node_size_get(&tree->nodes)); 1042 1043 /* Macros to help in managing nodes */ 1044 #define NODE(Id) (darray_node_data_get(&tree->nodes) + (Id)) 1045 #define CREATE_NODE { \ 1046 res = darray_node_push_back(&tree->nodes, &SLN_NODE_NULL); \ 1047 if(res != RES_OK) goto error; \ 1048 } (void)0 1049 1050 /* Helper macro for the queue data structure */ 1051 #define ENQUEUE(INode) { \ 1052 struct item* item__ = NULL; \ 1053 ASSERT(!is_list_empty(&free_items)); \ 1054 item__ = CONTAINER_OF(list_head(&free_items), struct item, link); \ 1055 item__->inode = INode; \ 1056 list_move_tail(&item__->link, &queue); \ 1057 ++nnodes; \ 1058 } 1059 #define DEQUEUE \ 1060 (list_move_tail(list_head(&queue), &free_items), \ 1061 --nnodes, \ 1062 CONTAINER_OF(list_tail(&free_items), struct item, link)->inode) 1063 1064 /* Setup the linked lists */ 1065 list_init(&free_items); 1066 list_init(&queue); 1067 FOR_EACH(i, 0, sizeof(items)/sizeof(items[0])) { 1068 items[i].inode = SIZE_MAX; 1069 list_init(&items[i].link); 1070 list_add_tail(&free_items, &items[i].link); 1071 } 1072 1073 SHTR(line_list_get_size(tree->args.lines, &nlines_total)); 1074 1075 /* Setup the root node */ 1076 NODE(root_index)->range[0] = 0; 1077 NODE(root_index)->range[1] = nlines_total - 1; 1078 1079 /* Register the root node in the queue */ 1080 ENQUEUE(root_index); 1081 1082 /* Breadth-first partitioning */ 1083 while(!is_list_empty(&queue)) { 1084 size_t node_range[2] = {0,0}; 1085 size_t nlines_node = 0; /* #lines in the node */ 1086 size_t nlines_child_min = 0; /* Min #lines per child */ 1087 size_t nlines_remain = 0; /* Lines to be distributed among the children */ 1088 size_t nchildren = 0; /* #children of the node */ 1089 size_t ichildren = 0; /* Index of the node's first child */ 1090 size_t iline = 0; 1091 size_t inode = 0; 1092 1093 1094 /* Check whether the number of registered leaves and currently processed 1095 * nodes is greater than or equal to the recommended maximum number of 1096 * leaves. If so, stop breadth-first partitioning and treat all registered 1097 * nodes as leaves. The policy is therefore to accept more leaves than 1098 * expected */ 1099 if(nnodes + nleaves >= nleaves_max_hint) { 1100 nleaves += nnodes; 1101 break; 1102 } 1103 1104 inode = DEQUEUE; /* Get the current node */ 1105 1106 /* Retrieve the range of lines contained within the node */ 1107 node_range[0] = NODE(inode)->range[0]; 1108 node_range[1] = NODE(inode)->range[1]; 1109 nlines_node = node_range[1] - node_range[0] + 1/*inclusive bound*/; 1110 ASSERT(nlines_node >= tree->args.leaf_nlines); 1111 1112 if(nlines_node <= tree->args.leaf_nlines) { 1113 /* Make a leaf */ 1114 ++nleaves; 1115 continue; 1116 } 1117 1118 /* Compute the number of children that node has */ 1119 nchildren = node_child_count(NODE(inode), tree->args.arity); 1120 ASSERT(nchildren <= tree->args.arity); 1121 1122 /* Check whether, after adding the child nodes to the queue, the total 1123 * number of nodes in the queue, plus the number of leaves already created, 1124 * does not exceed the max size of the queue. If so, the breadth-first 1125 * partitioning is complete and the nodes currently in the queue are all 1126 * leaves */ 1127 if(nnodes + nchildren + nleaves > QUEUE_SIZE_MAX) { 1128 nleaves += nnodes + 1/*Do not forget the the current node*/; 1129 break; 1130 } 1131 1132 /* Calculate the index of the first child */ 1133 ichildren = darray_node_size_get(&tree->nodes); 1134 ASSERT(ichildren > inode); 1135 1136 /* Define the offset from the current node to its children */ 1137 NODE(inode)->offset = ui64_to_ui32((uint64_t)(ichildren - inode)); 1138 1139 /* Calculate the minimum number of lines per child and the number of 1140 * remaining lines to be distributed equally among them */ 1141 nlines_child_min = nlines_node / nchildren; 1142 nlines_remain = nlines_node % nchildren; 1143 1144 iline = node_range[0]; 1145 FOR_EACH(i, 0, nchildren) { 1146 /* Compute the number of lines per child. Start by assigning the minimum 1147 * number of lines to each child, then distribute the remaining lines 1148 * among the first children */ 1149 size_t nlines_child = nlines_child_min + (i < nlines_remain); 1150 1151 CREATE_NODE; 1152 1153 /* Set the range of lines line for the newly created child. Note that 1154 * the boundaries of the range are inclusive, which is why 1 is 1155 * subtracted to the upper bound */ 1156 NODE(ichildren+i)->range[0] = iline; 1157 NODE(ichildren+i)->range[1] = iline + nlines_child - 1/*inclusive bound*/; 1158 iline += nlines_child; 1159 1160 /* Check that the child's lines are a subset of the parent's lines */ 1161 ASSERT(NODE(ichildren+i)->range[0] >= node_range[0]); 1162 ASSERT(NODE(ichildren+i)->range[1] <= node_range[1]); 1163 1164 ENQUEUE(ichildren+i); /* Register the child node */ 1165 } 1166 } 1167 1168 #undef NODE 1169 #undef CREATE_NODE 1170 #undef ENQUEUE 1171 #undef DEQUEUE 1172 1173 exit: 1174 return res; 1175 error: 1176 goto exit; 1177 } 1178 1179 static res_T 1180 build_subtrees 1181 (struct sln_tree* tree, 1182 const size_t subtrees[], 1183 unsigned nsubtrees, 1184 struct progress* meshing) 1185 { 1186 /* Subtree temporary data */ 1187 struct scratch { 1188 struct darray_node nodes; 1189 struct darray_vertex vertices; 1190 size_t nnodes; 1191 }* scratches; 1192 1193 /* Miscellaneous */ 1194 struct progress partitioning = PROGRESS_DEFAULT; 1195 struct mem_allocator* allocator = NULL; 1196 size_t nlines = 0; 1197 unsigned nthreads = 0; 1198 int i = 0; 1199 ATOMIC res = RES_OK; 1200 1201 /* Pre-conditions */ 1202 ASSERT(tree && subtrees && nsubtrees >= 1); 1203 ASSERT(nsubtrees < INT_MAX); 1204 1205 #define NODE(Id) (darray_node_data_get(&tree->nodes) + (Id)) 1206 #define IS_LEAF(Buf, Id) (darray_node_cdata_get(Buf)[Id].offset == 0) 1207 1208 allocator = tree->sln->allocator; 1209 1210 /* Allocate the per subtree scratch */ 1211 scratches = MEM_CALLOC(allocator, nsubtrees, sizeof(*scratches)); 1212 if(!scratches) { res = RES_MEM_ERR; goto error; } 1213 FOR_EACH(i, 0, (int)nsubtrees) { 1214 darray_node_init(allocator, &scratches[i].nodes); 1215 darray_vertex_init(allocator, &scratches[i].vertices); 1216 } 1217 1218 /* Setup the progress bar and print that although nothing has been done yet, 1219 * the calculation is nevertheless in progress */ 1220 SHTR(line_list_get_size(tree->args.lines, &nlines)); 1221 partitioning.total = nlines; 1222 partitioning.msg = "Partitioning: "; 1223 progress_print(tree->sln, &partitioning); 1224 1225 /* Set the number of threads to use. The advice on the number of threads must 1226 * not exceed the number of threads available on the machine (see the 1227 * sln_tree_create function); the caller can only specify a lower number of 1228 * threads. urthermore, the number of subtrees is calculated so that each subtree 1229 * corresponds to at least one thread, ensuring that there are no more 1230 * subtrees than available threads. 1231 * 1232 * The actual number of threads to be used for constructing the subtrees in 1233 * parallel should therefore always be set to the number of subtrees. However, 1234 * to provide greater flexibility in the distribution of threads or the number 1235 * of subtrees, the number of threads is calculated below as the minimum 1236 * between the number of available threads and the number of subtrees */ 1237 nthreads = MMIN(tree->args.nthreads_hint, nsubtrees); 1238 omp_set_num_threads((int)nthreads); 1239 1240 #pragma omp parallel for 1241 for(i=0; i < (int)nsubtrees; ++i) { 1242 struct sln_node root = SLN_NODE_NULL; 1243 struct scratch* scratch = scratches + i; 1244 size_t nnodes_s = 0; 1245 size_t nnodes_t = 0; 1246 res_T res2 = RES_OK; 1247 1248 /* Skip the remaining subtrees in case of an error */ 1249 if(ATOMIC_GET(&res) != RES_OK) continue; 1250 1251 /* Copy the subtree root in the temporary node buffer */ 1252 #pragma omp critical 1253 { 1254 root = *NODE(subtrees[i]); 1255 } 1256 res2 = darray_node_push_back(&scratch->nodes, &root); 1257 if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; } 1258 1259 /* Partition the line */ 1260 res2 = partition_lines_depth_first 1261 (tree, 0/*root*/, &scratch->nodes, &partitioning); 1262 if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; } 1263 1264 #pragma omp critical 1265 { 1266 struct sln_node* dst = NULL; 1267 const struct sln_node* src = NULL; 1268 1269 /* Increase the node buffer to store the subtree nodes */ 1270 nnodes_s = darray_node_size_get(&scratch->nodes); 1271 nnodes_t = darray_node_size_get(&tree->nodes); 1272 res2 = darray_node_resize(&tree->nodes, nnodes_t + nnodes_s - 1/*root*/); 1273 if(res2 == RES_OK) { 1274 1275 if(!IS_LEAF(&scratch->nodes, 0)) { 1276 /* Set the offset to the first child of the root node of the subtree */ 1277 NODE(subtrees[i])->offset = ui64_to_ui32(nnodes_t - subtrees[i]); 1278 } 1279 1280 /* Copy the subtree nodes in the main node buffer */ 1281 src = darray_node_cdata_get(&scratch->nodes) + 1/*discard root*/; 1282 dst = darray_node_data_get(&tree->nodes) + nnodes_t; 1283 memcpy(dst, src, (nnodes_s-1/*discard root*/)*sizeof(*src)); 1284 } 1285 } 1286 if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; } 1287 1288 /* Free the temporary buffer but retain the number of nodes in the subtree. 1289 * This number is used later when constructing polylines (see below) */ 1290 darray_node_purge(&scratch->nodes); 1291 scratch->nnodes = nnodes_s; 1292 } 1293 1294 /* Setup the progress bar and print that although nothing has been done yet, 1295 * the calculation is nevertheless in progress */ 1296 *meshing = PROGRESS_DEFAULT; 1297 meshing->total = darray_node_size_get(&tree->nodes); 1298 meshing->msg = "Meshing: "; 1299 progress_print(tree->sln, meshing); 1300 1301 #pragma omp parallel for 1302 for(i=0; i < (int)nsubtrees; ++i) { 1303 struct scratch* scratch = scratches + i; 1304 size_t inode = subtrees[i]; 1305 size_t nverts_s = 0; 1306 size_t nverts_t = 0; 1307 size_t j = 0; 1308 res_T res2 = RES_OK; 1309 1310 /* Skip the remaining subtrees in case of an error */ 1311 if(ATOMIC_GET(&res) != RES_OK) continue; 1312 1313 res2 = build_polylines 1314 (tree, inode, scratch->nnodes, &scratch->vertices, meshing); 1315 if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; } 1316 1317 #pragma omp critical 1318 { 1319 struct sln_vertex* dst = NULL; 1320 const struct sln_vertex* src = NULL; 1321 1322 /* Increase the vertex buffer to store the subtree polylines */ 1323 nverts_s = darray_vertex_size_get(&scratch->vertices); 1324 nverts_t = darray_vertex_size_get(&tree->vertices); 1325 res2 = darray_vertex_resize(&tree->vertices, nverts_t + nverts_s); 1326 if(res2 == RES_OK) { 1327 /* Copy the subtree nodes in the main vertex buffer */ 1328 src = darray_vertex_cdata_get(&scratch->vertices); 1329 dst = darray_vertex_data_get(&tree->vertices) + nverts_t; 1330 memcpy(dst, src, nverts_s*sizeof(*src)); 1331 } 1332 } 1333 if(res2 != RES_OK) { ATOMIC_SET(&res, res2); continue; } 1334 1335 /* Update the index of the first vertex of the polyline for the nodes in the 1336 * subtree based on their new locations, i.e., the main vertex buffer */ 1337 NODE(subtrees[i])->ivertex += nverts_t; 1338 FOR_EACH(j, 0, scratch->nnodes-1/*The root has been just treated*/) { 1339 inode = subtrees[i] + NODE(subtrees[i])->offset + j; 1340 NODE(inode)->ivertex += nverts_t; 1341 } 1342 1343 /* Free the temporary vertex buffer */ 1344 darray_node_purge(&scratch->nodes); 1345 } 1346 1347 #undef NODE 1348 #undef IS_LEAF 1349 1350 exit: 1351 /* Clean up temporary data */ 1352 FOR_EACH(i, 0, (int)nsubtrees) { 1353 darray_node_release(&scratches[i].nodes); 1354 darray_vertex_release(&scratches[i].vertices); 1355 } 1356 MEM_RM(allocator, scratches); 1357 return (res_T)res; 1358 error: 1359 goto exit; 1360 } 1361 1362 static res_T 1363 tree_build_sequential(struct sln_tree* tree) 1364 { 1365 /* Progress statuses */ 1366 struct progress partitioning = PROGRESS_DEFAULT; 1367 struct progress meshing = PROGRESS_DEFAULT; 1368 1369 struct sln_node node = SLN_NODE_NULL; 1370 size_t nlines = 0; 1371 size_t nnodes = 0; 1372 size_t iroot = 0; 1373 res_T res = RES_OK; 1374 1375 SHTR(line_list_get_size(tree->args.lines, &nlines)); 1376 1377 iroot = darray_node_size_get(&tree->nodes); 1378 ASSERT(iroot == 0); /* assume that the tree contains no data */ 1379 1380 /* Setup the root node */ 1381 node.range[0] = 0; 1382 node.range[1] = nlines - 1/* inclusive bound */; 1383 res = darray_node_push_back(&tree->nodes, &node); 1384 if(res != RES_OK) goto error; 1385 1386 /* Partition lines */ 1387 partitioning.total = nlines; 1388 partitioning.msg = "Partitioning: "; 1389 progress_print(tree->sln, &partitioning); 1390 res = partition_lines_depth_first(tree, iroot, &tree->nodes, &partitioning); 1391 if(res != RES_OK) goto error; 1392 1393 /* Create polylines */ 1394 nnodes = darray_node_size_get(&tree->nodes); 1395 meshing.total = nnodes; 1396 meshing.msg = "Meshing: "; 1397 progress_print(tree->sln, &meshing); 1398 res = build_polylines(tree, iroot, nnodes, &tree->vertices, &meshing); 1399 if(res != RES_OK) goto error; 1400 1401 exit: 1402 return res; 1403 error: 1404 goto exit; 1405 } 1406 1407 static res_T 1408 tree_build_parallel(struct sln_tree* tree) 1409 { 1410 /* Stack data structure */ 1411 #define STACK_SIZE (SLN_TREE_DEPTH_MAX*(SLN_TREE_ARITY_MAX-1)) 1412 size_t stack[STACK_SIZE]; 1413 size_t istack = 0; 1414 1415 /* Subtrees */ 1416 size_t subtrees[QUEUE_SIZE_MAX]; /* List of sub-tree root indices */ 1417 unsigned nsubtrees = 0; 1418 1419 /* Progress message of the meshing step */ 1420 struct progress meshing = PROGRESS_DEFAULT; 1421 1422 /* Miscellaneous */ 1423 struct sln_node root = SLN_NODE_NULL; 1424 size_t nlines = 0; 1425 size_t nnodes_upper_tree = 0; /* #nodes from the root to the subtrees */ 1426 size_t iroot = 0; 1427 size_t inode = 0; 1428 size_t i = 0; 1429 res_T res = RES_OK; 1430 1431 ASSERT(tree); 1432 1433 iroot = darray_node_size_get(&tree->nodes); 1434 ASSERT(iroot == 0); /* assume that the tree contains no data */ 1435 1436 SHTR(line_list_get_size(tree->args.lines, &nlines)); 1437 1438 /* Setup the root node */ 1439 root.range[0] = 0; 1440 root.range[1] = nlines; 1441 res = darray_node_push_back(&tree->nodes, &root); 1442 if(res != RES_OK) goto error; 1443 1444 /* Partition the lines in bread-first fixing the maximum number of leafs to 1445 * the available number of threads */ 1446 res = partition_lines_breadth_first(tree, iroot, tree->args.nthreads_hint); 1447 if(res != RES_OK) goto error; 1448 1449 /* Push back SIZE_MAX which, once pop up, will mark the end of recursion */ 1450 stack[istack++] = SIZE_MAX; 1451 1452 /* Retrieve the leaf indices generated by bread-first partitioning. These 1453 * correspond to the roots of the subtree to be built */ 1454 inode = iroot; /* Root node */ 1455 while(inode != SIZE_MAX) { 1456 const struct sln_node* node = darray_node_cdata_get(&tree->nodes) + inode; 1457 ASSERT(inode < darray_node_size_get(&tree->nodes)); 1458 1459 if(sln_node_is_leaf(node)) { 1460 ASSERT(nsubtrees < QUEUE_SIZE_MAX); 1461 subtrees[nsubtrees++] = inode; 1462 inode = stack[--istack]; /* Pop the next node */ 1463 1464 } else { 1465 const size_t ichild0 = inode + node->offset; 1466 const unsigned nchildren = node_child_count(node, tree->args.arity); 1467 1468 /* Push the children except the 1st one */ 1469 ASSERT(istack + (nchildren-1/*ichild0*/) <= STACK_SIZE); 1470 FOR_EACH_REVERSE(i, nchildren-1, 0) stack[istack++] = ichild0 + i; 1471 1472 /* Recursively traverse the 1st child */ 1473 inode = ichild0; 1474 } 1475 } 1476 1477 /* Retrieves the number of registered nodes. This is the number of nodes from 1478 * the root to the subtrees, excluding the roots of those subtrees */ 1479 nnodes_upper_tree = darray_node_size_get(&tree->nodes); 1480 nnodes_upper_tree -= nsubtrees; /* Exclude the root node of the subtrees */ 1481 1482 res = build_subtrees(tree, subtrees, nsubtrees, &meshing); 1483 if(res != RES_OK) goto error; 1484 1485 /* Build the polylines of the upper level if necessary */ 1486 if(nnodes_upper_tree != 0) { 1487 res = build_polylines 1488 (tree, 0/*root*/, nnodes_upper_tree, &tree->vertices, &meshing); 1489 if(res != RES_OK) goto error; 1490 } 1491 1492 #undef STACK_SIZE 1493 1494 exit: 1495 return res; 1496 error: 1497 goto exit; 1498 } 1499 1500 /******************************************************************************* 1501 * Local functions 1502 ******************************************************************************/ 1503 res_T 1504 tree_build(struct sln_tree* tree) 1505 { 1506 res_T res = RES_OK; 1507 ASSERT(tree); 1508 1509 res = tree->args.nthreads_hint == 1 1510 ? tree_build_sequential(tree) 1511 : tree_build_parallel(tree); 1512 if(res != RES_OK) goto error; 1513 1514 exit: 1515 return res; 1516 error: 1517 darray_node_purge(&tree->nodes); 1518 darray_vertex_purge(&tree->vertices); 1519 goto exit; 1520 }