Skip to content

Commit 8590134

Browse files
schnellerhasejorgensdchrisrichardson
authored
Local dual graph for branching meshes (#3730)
* Move Boundary vertex function to input of create_mesh Moves the reorder_fn to an argument to the new constructor create_boundary_vertices_fn * Doc and simplify argument names * Example running up to mesh partitioning * Add failing (local) dualgraph construction * Start documenting and simplifiying build local dual graph * Unused args * Deactivate test * Fixed? * Fix iterator combinations * Test for local dual graph for branching mesh * Tidy up * Format * Revert vertices * Docs * doxgen * More doxygen * Fix typo Co-authored-by: Chris Richardson <[email protected]> --------- Co-authored-by: Jørgen Schartum Dokken <[email protected]> Co-authored-by: Chris Richardson <[email protected]>
1 parent bde06f5 commit 8590134

File tree

5 files changed

+323
-178
lines changed

5 files changed

+323
-178
lines changed

cpp/dolfinx/mesh/graphbuild.cpp

Lines changed: 76 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -375,24 +375,34 @@ mesh::build_local_dual_graph(
375375
"Number of cell types must match number of cell arrays.");
376376
};
377377

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
379383

380-
// Create indexing offset for each cell type and determine max number
381-
// of vertices per facet
384+
// TODO: cell_offsets can be removed?
382385
std::vector<std::int32_t> cell_offsets = {0};
386+
cell_offsets.reserve(cells.size() + 1);
387+
383388
int max_vertices_per_facet = 0;
384389
int facet_count = 0;
385-
int tdim = mesh::cell_dim(celltypes.front());
386390
for (std::size_t j = 0; j < cells.size(); ++j)
387391
{
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;
391401
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;
393403

394404
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);
396406

397407
// Determine maximum number of vertices for facet
398408
for (std::int32_t i = 0; i < cell_facets.num_nodes(); ++i)
@@ -402,28 +412,39 @@ mesh::build_local_dual_graph(
402412
}
403413
}
404414

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+
405423
const int shape1 = max_vertices_per_facet + 1;
406424
std::vector<std::int64_t> facets;
407425
facets.reserve(facet_count * shape1);
426+
constexpr std::int32_t padding_value = -1;
408427

409428
for (std::size_t j = 0; j < cells.size(); ++j)
410429
{
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;
415435
graph::AdjacencyList<int> cell_facets
416-
= mesh::get_entity_vertices(celltypes[j], tdim - 1);
436+
= mesh::get_entity_vertices(cell_type, tdim - 1);
417437

418438
for (std::int32_t c = 0; c < num_cells; ++c)
419439
{
420440
// 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);
422442
for (int f = 0; f < cell_facets.num_nodes(); ++f)
423443
{
424444
auto facet_vertices = cell_facets.links(f);
425445
std::ranges::transform(facet_vertices, std::back_inserter(facets),
426446
[v](auto idx) { return v[idx]; });
447+
// TODO: radix_sort?
427448
std::sort(std::prev(facets.end(), facet_vertices.size()), facets.end());
428449
facets.insert(facets.end(),
429450
max_vertices_per_facet - facet_vertices.size(),
@@ -433,9 +454,10 @@ mesh::build_local_dual_graph(
433454
}
434455
}
435456

436-
// Sort facets by vertex key
457+
// 3) Sort facets by vertex key
437458
std::vector<std::size_t> perm(facets.size() / shape1, 0);
438459
std::iota(perm.begin(), perm.end(), 0);
460+
// TODO: radix_sort?
439461
std::sort(perm.begin(), perm.end(),
440462
[&facets, shape1](auto f0, auto f1)
441463
{
@@ -445,7 +467,7 @@ mesh::build_local_dual_graph(
445467
it1, std::next(it1, shape1));
446468
});
447469

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
449471
// cell lead to a graph edge to be added. Facets that are not shared
450472
// are stored as these might be shared by a cell on another process.
451473
std::vector<std::int64_t> unmatched_facets;
@@ -455,41 +477,58 @@ mesh::build_local_dual_graph(
455477
auto it = perm.begin();
456478
while (it != perm.end())
457479
{
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);
459482

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(
462486
it, perm.end(),
463-
[f0, &facets, shape1](auto idx) -> bool
487+
[facet, &facets, shape1](auto idx) -> bool
464488
{
465489
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);
467491
});
468492

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();
478494

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)
481499
{
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()));
484503
local_cells.push_back(cell0);
485504
}
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+
}
486523

487524
// Update iterator
488-
it = it1;
525+
it = it_next_facet;
489526
}
490527
}
491528

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.
493532

494533
std::vector<std::int32_t> sizes(cell_offsets.back(), 0);
495534
for (auto e : edges)

cpp/dolfinx/mesh/graphbuild.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,14 @@ build_local_dual_graph(std::span<const CellType> celltypes,
6363
/// @param[in] cells Collections of cells, defined by the cell vertices
6464
/// from which to build the dual graph, as flattened arrays for each
6565
/// cell type in `celltypes`.
66-
/// @note `cells` and `celltypes` must have the same size.
6766
/// @return The dual graph
67+
///
68+
/// @note `cells` and `celltypes` must have the same size.
69+
///
70+
/// @note The assumption in `build_local_dual_graph` on how unmatched facets are
71+
/// identified will not allow for T-joints (or any other higher branching)
72+
/// across process boundaries to be picked up by the dual graph. If the joints
73+
/// do not live on the process boundary this is not a problem.
6874
graph::AdjacencyList<std::int64_t>
6975
build_dual_graph(MPI_Comm comm, std::span<const CellType> celltypes,
7076
const std::vector<std::span<const std::int64_t>>& cells);

0 commit comments

Comments
 (0)