@@ -375,24 +375,34 @@ mesh::build_local_dual_graph(
375
375
" Number of cell types must match number of cell arrays." );
376
376
};
377
377
378
- constexpr std::int32_t padding_value = -1 ;
378
+ int tdim = mesh::cell_dim (celltypes.front ());
379
+
380
+ // 1) Create indexing offset for each cell type and determine max number
381
+ // of vertices per facet -> size computations for later on used data
382
+ // structures
379
383
380
- // Create indexing offset for each cell type and determine max number
381
- // of vertices per facet
384
+ // TODO: cell_offsets can be removed?
382
385
std::vector<std::int32_t > cell_offsets = {0 };
386
+ cell_offsets.reserve (cells.size () + 1 );
387
+
383
388
int max_vertices_per_facet = 0 ;
384
389
int facet_count = 0 ;
385
- int tdim = mesh::cell_dim (celltypes.front ());
386
390
for (std::size_t j = 0 ; j < cells.size (); ++j)
387
391
{
388
- assert (tdim == mesh::cell_dim (celltypes[j]));
389
- int num_cell_vertices = mesh::cell_num_entities (celltypes[j], 0 );
390
- std::int32_t num_cells = cells[j].size () / num_cell_vertices;
392
+ const auto & cell_type = celltypes[j];
393
+ const auto & _cells = cells[j];
394
+
395
+ assert (tdim == mesh::cell_dim (cell_type));
396
+
397
+ int num_cell_vertices = mesh::cell_num_entities (cell_type, 0 );
398
+ int num_cell_facets = mesh::cell_num_entities (cell_type, tdim - 1 );
399
+
400
+ std::int32_t num_cells = _cells.size () / num_cell_vertices;
391
401
cell_offsets.push_back (cell_offsets.back () + num_cells);
392
- facet_count += mesh::cell_num_entities (celltypes[j], tdim - 1 ) * num_cells;
402
+ facet_count += num_cell_facets * num_cells;
393
403
394
404
graph::AdjacencyList<std::int32_t > cell_facets
395
- = mesh::get_entity_vertices (celltypes[j] , tdim - 1 );
405
+ = mesh::get_entity_vertices (cell_type , tdim - 1 );
396
406
397
407
// Determine maximum number of vertices for facet
398
408
for (std::int32_t i = 0 ; i < cell_facets.num_nodes (); ++i)
@@ -402,28 +412,39 @@ mesh::build_local_dual_graph(
402
412
}
403
413
}
404
414
415
+ // 2) Build a list of (all) facets, defined by sorted vertices, with the
416
+ // connected cell index after the vertices. For v_ij the j-th vertex of the
417
+ // i-th facet. The last index is the cell index (non unique).
418
+ // facets = [v_11, v_12, v_13, -1, ..., -1, 0,
419
+ // v_21, v_22, v_23, -1, ..., -1, 0,
420
+ // ⋮ ⋮ ⋮ ⋮ ⋱ ⋮ ⋮
421
+ // v_n1, v_n2, -1, -1, ..., -1, n]
422
+
405
423
const int shape1 = max_vertices_per_facet + 1 ;
406
424
std::vector<std::int64_t > facets;
407
425
facets.reserve (facet_count * shape1);
426
+ constexpr std::int32_t padding_value = -1 ;
408
427
409
428
for (std::size_t j = 0 ; j < cells.size (); ++j)
410
429
{
411
- // Build a list of facets, defined by sorted vertices, with the connected
412
- // cell index after the vertices
413
- int num_cell_vertices = mesh::cell_num_entities (celltypes[j], 0 );
414
- std::int32_t num_cells = cells[j].size () / num_cell_vertices;
430
+ const CellType& cell_type = celltypes[j];
431
+ std::span _cells = cells[j];
432
+
433
+ int num_cell_vertices = mesh::cell_num_entities (cell_type, 0 );
434
+ std::int32_t num_cells = _cells.size () / num_cell_vertices;
415
435
graph::AdjacencyList<int > cell_facets
416
- = mesh::get_entity_vertices (celltypes[j] , tdim - 1 );
436
+ = mesh::get_entity_vertices (cell_type , tdim - 1 );
417
437
418
438
for (std::int32_t c = 0 ; c < num_cells; ++c)
419
439
{
420
440
// Loop over cell facets
421
- auto v = cells[j] .subspan (num_cell_vertices * c, num_cell_vertices);
441
+ auto v = _cells .subspan (num_cell_vertices * c, num_cell_vertices);
422
442
for (int f = 0 ; f < cell_facets.num_nodes (); ++f)
423
443
{
424
444
auto facet_vertices = cell_facets.links (f);
425
445
std::ranges::transform (facet_vertices, std::back_inserter (facets),
426
446
[v](auto idx) { return v[idx]; });
447
+ // TODO: radix_sort?
427
448
std::sort (std::prev (facets.end (), facet_vertices.size ()), facets.end ());
428
449
facets.insert (facets.end (),
429
450
max_vertices_per_facet - facet_vertices.size (),
@@ -433,9 +454,10 @@ mesh::build_local_dual_graph(
433
454
}
434
455
}
435
456
436
- // Sort facets by vertex key
457
+ // 3) Sort facets by vertex key
437
458
std::vector<std::size_t > perm (facets.size () / shape1, 0 );
438
459
std::iota (perm.begin (), perm.end (), 0 );
460
+ // TODO: radix_sort?
439
461
std::sort (perm.begin (), perm.end (),
440
462
[&facets, shape1](auto f0, auto f1)
441
463
{
@@ -445,7 +467,7 @@ mesh::build_local_dual_graph(
445
467
it1, std::next (it1, shape1));
446
468
});
447
469
448
- // Iterate over sorted list of facets. Facets shared by more than one
470
+ // 4) Iterate over sorted list of facets. Facets shared by more than one
449
471
// cell lead to a graph edge to be added. Facets that are not shared
450
472
// are stored as these might be shared by a cell on another process.
451
473
std::vector<std::int64_t > unmatched_facets;
@@ -455,41 +477,58 @@ mesh::build_local_dual_graph(
455
477
auto it = perm.begin ();
456
478
while (it != perm.end ())
457
479
{
458
- std::span f0 (facets.data () + (*it) * shape1, shape1);
480
+ std::size_t facet_index = *it;
481
+ std::span facet (facets.data () + facet_index * shape1, shape1);
459
482
460
- // Find iterator to next facet different from f0
461
- auto it1 = std::find_if_not (
483
+ // Find iterator to next facet different from f0 -> all facets in [it,
484
+ // it_next_facet) describe the same facet
485
+ auto it_next_facet = std::find_if_not (
462
486
it, perm.end (),
463
- [f0 , &facets, shape1](auto idx) -> bool
487
+ [facet , &facets, shape1](auto idx) -> bool
464
488
{
465
489
auto f1_it = std::next (facets.begin (), idx * shape1);
466
- return std::equal (f0 .begin (), std::prev (f0 .end ()), f1_it);
490
+ return std::equal (facet .begin (), std::prev (facet .end ()), f1_it);
467
491
});
468
492
469
- // Add dual graph edges (one direction only, other direction is
470
- // added later)
471
- std::int32_t cell0 = f0.back ();
472
- for (auto itx = std::next (it); itx != it1; ++itx)
473
- {
474
- std::span f1 (facets.data () + *itx * shape1, shape1);
475
- std::int32_t cell1 = f1.back ();
476
- edges.push_back ({cell0, cell1});
477
- }
493
+ std::int32_t cell0 = facet.back ();
478
494
479
- // Store unmatched facets and the attached cell
480
- if (std::distance (it, it1) == 1 )
495
+ auto cell_count = std::distance (it, it_next_facet);
496
+ assert (cell_count >= 1 );
497
+ // TODO: this constant as user control?
498
+ if (cell_count == 1 )
481
499
{
482
- unmatched_facets.insert (unmatched_facets.end (), f0.begin (),
483
- std::prev (f0.end ()));
500
+ // Store unmatched facets and the attached cell
501
+ unmatched_facets.insert (unmatched_facets.end (), facet.begin (),
502
+ std::prev (facet.end ()));
484
503
local_cells.push_back (cell0);
485
504
}
505
+ else
506
+ {
507
+ // Add dual graph edges (one direction only, other direction is
508
+ // added later). In the range [it, it_next_facet), all combinations are
509
+ // added.
510
+ for (auto facet_a_it = it; facet_a_it != it_next_facet; facet_a_it++)
511
+ {
512
+ std::span facet_a (facets.data () + *facet_a_it * shape1, shape1);
513
+ std::int32_t cell_a = facet_a.back ();
514
+ for (auto facet_b_it = std::next (facet_a_it);
515
+ facet_b_it != it_next_facet; facet_b_it++)
516
+ {
517
+ std::span facet_b (facets.data () + *facet_b_it * shape1, shape1);
518
+ std::int32_t cell_b = facet_b.back ();
519
+ edges.push_back ({cell_a, cell_b});
520
+ }
521
+ }
522
+ }
486
523
487
524
// Update iterator
488
- it = it1 ;
525
+ it = it_next_facet ;
489
526
}
490
527
}
491
528
492
- // -- Build adjacency list data
529
+ // 5) Build adjacency list data. Prepare data structure and assemble into.
530
+ // Important: we have only computed one direction of the dual edges, we add
531
+ // both forward and backward to the final data structure.
493
532
494
533
std::vector<std::int32_t > sizes (cell_offsets.back (), 0 );
495
534
for (auto e : edges)
0 commit comments