OGS
ComputeSparsityPattern.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#include <numeric>
7#include <range/v3/algorithm/sort.hpp>
8#include <range/v3/algorithm/unique.hpp>
9#include <range/v3/range/conversion.hpp>
10#include <range/v3/view/transform.hpp>
11
14
15#ifdef USE_PETSC
17
19 NumLib::LocalToGlobalIndexMap const& dof_table, MeshLib::Mesh const& mesh)
20{
21 assert(dynamic_cast<MeshLib::NodePartitionedMesh const*>(&mesh));
22
23 MeshLib::NodeAdjacencyTable const node_adjacency_table(mesh);
24
25 // getGlobalIndices() returns raw PETSc global indices:
26 // idx >= 0 — owned by this rank, actual PETSc global row/col index
27 // idx < 0 — ghost node on this rank (owned by another rank);
28 // the actual global index is stored negated
29 auto const global_idcs =
31 ranges::views::transform([&](auto&& l)
32 { return dof_table.getGlobalIndices(l); }) |
33 ranges::to<std::vector>();
34
35 auto const n_local =
36 static_cast<GlobalIndexType>(dof_table.dofSizeWithoutGhosts());
37
38 // Each rank owns a contiguous range of global indices; global_start is the
39 // first index owned here, obtained by an exclusive prefix-sum of the local
40 // sizes. It maps a global row index to the corresponding local row index.
41 //
42 // MPIU_INT is not an MPI standard datatype but a PETSc macro (defined in
43 // petscsys.h) that expands to the MPI datatype matching the width of
44 // PetscInt, i.e. MPI_INT or MPI_INT64_T depending on the PETSc
45 // configuration. It is used rather than the BaseLib::MPI wrappers because
46 // BaseLib::MPI::mpiType() has no mapping for a 64-bit PetscInt and would
47 // silently pick the wrong datatype.
48 GlobalIndexType global_start = 0;
49 MPI_Exscan(&n_local, &global_start, 1, MPIU_INT, MPI_SUM, PETSC_COMM_WORLD);
50 // Total global DOF count needed to decode the special ghost encoding where
51 // global index 0 is stored as -num_global_dof.
52 GlobalIndexType num_global_dof = 0;
53 MPI_Allreduce(&n_local, &num_global_dof, 1, MPIU_INT, MPI_SUM,
54 PETSC_COMM_WORLD);
55
56 // Collect all (row, col) pairs into per-local-row lists of global column
57 // indices; they are sorted and deduplicated below.
58 //
59 // A row holds only a few dozen columns, so a flat vector plus one
60 // sort/unique per row is used instead of std::set: appending is a
61 // contiguous write without a per-element node allocation, and the sort
62 // is on data that already sits in cache. The result is the same -- sorted
63 // and unique -- as std::set gives, which is what the CSR pattern needs.
64 std::vector<std::vector<GlobalIndexType>> col_lists(n_local);
65
66 // Decode a raw PETSc global index (possibly negative ghost encoding) to the
67 // actual non-negative global index.
68 auto const decode_col = [&](GlobalIndexType const raw) -> GlobalIndexType
69 {
70 if (raw >= 0)
71 {
72 return raw;
73 }
74 // Ghost encoding: actual index k is stored as -k, except k==0 which is
75 // stored as -num_global_dof.
76 return (-raw == num_global_dof) ? 0 : -raw;
77 };
78
79 // Collect a (row, col) pair into the per-row column set.
80 // row >= 0 (non-negative) — owned by this rank
81 // row < 0 — ghost row, not owned here → skip
82 auto const collect =
83 [&](GlobalIndexType const row, GlobalIndexType const col)
84 {
85 if (row < 0)
86 {
87 return;
88 }
89 // A non-negative raw index is owned by this rank, and an owned index
90 // lies in this rank's contiguous range [global_start, global_start +
91 // n_local), so the local row is always in range here.
92 GlobalIndexType const local_row = row - global_start;
93 assert(local_row >= 0 && local_row < n_local);
94 col_lists[local_row].push_back(decode_col(col));
95 };
96
97 // Standard mesh-adjacency contribution.
98 // Note: getAdjacentNodes() already includes node n itself (the adjacency
99 // table is built from all element nodes, including self), so there is no
100 // need for a separate self-node loop.
101 for (std::size_t n = 0; n < mesh.getNumberOfNodes(); ++n)
102 {
103 auto const& adj_nodes = node_adjacency_table.getAdjacentNodes(n);
104 for (auto const row_idx : global_idcs[n])
105 {
106 for (auto const adj : adj_nodes)
107 {
108 for (auto const col_idx : global_idcs[adj])
109 {
110 collect(row_idx, col_idx);
111 }
112 }
113 }
114 }
115
116 // Sort and deduplicate each row, so that col_idx ends up sorted within each
117 // row as PETScSparsityPattern documents.
118 std::size_t number_of_nonzeros = 0;
119 for (auto& cols : col_lists)
120 {
121 ranges::sort(cols);
122 cols.erase(ranges::unique(cols), cols.end());
123 number_of_nonzeros += cols.size();
124 }
125
126 // Build the CSR sparsity pattern from the collected column lists.
127 GlobalSparsityPattern sparsity_pattern;
128 sparsity_pattern.row_ptr.resize(n_local + 1);
129 sparsity_pattern.row_ptr[0] = 0;
130 sparsity_pattern.col_idx.reserve(number_of_nonzeros);
131
132 for (GlobalIndexType local_row = 0; local_row < n_local; ++local_row)
133 {
134 auto const& cols = col_lists[local_row];
135 sparsity_pattern.col_idx.insert(sparsity_pattern.col_idx.end(),
136 cols.begin(), cols.end());
137 sparsity_pattern.row_ptr[local_row + 1] =
138 sparsity_pattern.row_ptr[local_row] +
139 static_cast<GlobalIndexType>(cols.size());
140 }
141
142 return sparsity_pattern;
143}
144#else
145GlobalSparsityPattern computeSparsityPatternNonPETSc(
146 NumLib::LocalToGlobalIndexMap const& dof_table, MeshLib::Mesh const& mesh)
147{
148 MeshLib::NodeAdjacencyTable const node_adjacency_table(mesh);
149
150 // A mapping mesh node id -> global indices
151 // It acts as a cache for dof table queries.
152 auto const global_idcs =
154 ranges::views::transform([&](auto&& l)
155 { return dof_table.getGlobalIndices(l); }) |
156 ranges::to<std::vector>();
157
158 GlobalSparsityPattern sparsity_pattern;
159 sparsity_pattern.number_non_zeros_per_row.assign(
160 dof_table.dofSizeWithGhosts(), 0);
161
162 // Map adjacent mesh nodes to "adjacent global indices".
163 for (std::size_t n = 0; n < mesh.getNumberOfNodes(); ++n)
164 {
165 auto const& an = node_adjacency_table.getAdjacentNodes(n);
166 auto const n_self_dof = global_idcs[n].size();
167 auto const n_connected_dof = std::accumulate(
168 cbegin(an), cend(an), 0, [&](auto const result, auto const i)
169 { return result + global_idcs[i].size(); });
170 auto const n_dof = n_self_dof + n_connected_dof;
171 for (auto global_index : global_idcs[n])
172 {
173 sparsity_pattern.number_non_zeros_per_row[global_index] = n_dof;
174 }
175 }
176
177 return sparsity_pattern;
178}
179#endif
180
181namespace NumLib
182{
184 LocalToGlobalIndexMap const& dof_table, MeshLib::Mesh const& mesh)
185{
186#ifdef USE_PETSC
187 return computeSparsityPatternPETSc(dof_table, mesh);
188#else
189 return computeSparsityPatternNonPETSc(dof_table, mesh);
190#endif
191}
192
193} // namespace NumLib
GlobalSparsityPattern computeSparsityPatternPETSc(NumLib::LocalToGlobalIndexMap const &dof_table, MeshLib::Mesh const &mesh)
MathLib::PETScSparsityPattern GlobalSparsityPattern
GlobalMatrix::IndexType GlobalIndexType
std::size_t getNumberOfNodes() const
Get the number of nodes.
Definition Mesh.h:92
std::vector< std::size_t > const & getAdjacentNodes(std::size_t const node_id) const
std::vector< GlobalIndexType > getGlobalIndices(const MeshLib::Location &l) const
Forwards the respective method from MeshComponentMap.
auto meshLocations(Mesh const &mesh, MeshItemType const item_type)
Definition Mesh.h:234
GlobalSparsityPattern computeSparsityPattern(LocalToGlobalIndexMap const &dof_table, MeshLib::Mesh const &mesh)
Computes a sparsity pattern for the given inputs.
std::vector< PetscInt > col_idx
Global column indices, sorted within each local row.
std::vector< PetscInt > row_ptr
CSR row pointers (length n_local_rows + 1).