OGS
PETScMatrix.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
4#include "PETScMatrix.h"
5
6#include "BaseLib/Error.h"
7#include "BaseLib/MPI.h"
8
9namespace MathLib
10{
11PETScMatrix::PETScMatrix(const PetscInt nrows,
12 const PETScSparsityPattern& sparsity_pattern)
13 : nrows_(PETSC_DECIDE),
14 ncols_(PETSC_DECIDE),
15 n_loc_rows_(nrows),
16 n_loc_cols_(nrows)
17{
18 create(sparsity_pattern);
19}
20
21PETScMatrix::PETScMatrix(const PetscInt nrows, const PetscInt ncols,
22 const PETScSparsityPattern& sparsity_pattern)
23 : nrows_(PETSC_DECIDE),
24 ncols_(PETSC_DECIDE),
25 n_loc_rows_(nrows),
26 n_loc_cols_(ncols)
27{
28 create(sparsity_pattern);
29}
30
32 : nrows_(A.nrows_),
33 ncols_(A.ncols_),
39{
40 // MatDuplicate reproduces the nonzero structure exactly, so the copy
41 // shares the source's structure.
42 PetscCallAbort(PETSC_COMM_WORLD, MatDuplicate(A.A_, MAT_COPY_VALUES, &A_));
43}
44
46{
47 nrows_ = A.nrows_;
48 ncols_ = A.ncols_;
53
54 if (A_ != nullptr)
55 {
56 auto const pattern =
59 PetscCallAbort(PETSC_COMM_WORLD, MatCopy(A.A_, A_, pattern));
60 }
61 else
62 {
64 destroy();
65 PetscCallAbort(PETSC_COMM_WORLD,
66 MatDuplicate(A.A_, MAT_COPY_VALUES, &A_));
67 }
68
69 return *this;
70}
71
72void PETScMatrix::setRowsColumnsZero(std::vector<PetscInt> const& row_pos)
73{
74 // Each rank (compute core) processes only the rows that belong to the rank
75 // itself.
76 const PetscScalar one = 1.0;
77 const PetscInt nrows = static_cast<PetscInt>(row_pos.size());
78
79 // Each process will only zero its own rows.
80 // This avoids all reductions in the zero row routines
81 // and thus improves performance for very large process counts.
82 // See PETSc doc about MAT_NO_OFF_PROC_ZERO_ROWS.
83 PetscCallAbort(PETSC_COMM_WORLD,
84 MatSetOption(A_, MAT_NO_OFF_PROC_ZERO_ROWS, PETSC_TRUE));
85
86 // Keep the non-zero pattern for the assignment operator.
87 PetscCallAbort(PETSC_COMM_WORLD,
88 MatSetOption(A_, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE));
89
90 if (nrows > 0)
91 {
92 PetscCallAbort(PETSC_COMM_WORLD,
93 MatZeroRows(A_, nrows, &row_pos[0], one, PETSC_NULLPTR,
94 PETSC_NULLPTR));
95 }
96 else
97 {
98 PetscCallAbort(PETSC_COMM_WORLD,
99 MatZeroRows(A_, 0, PETSC_NULLPTR, one, PETSC_NULLPTR,
100 PETSC_NULLPTR));
101 }
102}
103
104void PETScMatrix::viewer(const std::string& file_name,
105 const PetscViewerFormat vw_format)
106{
107 PetscViewer viewer;
108 PetscViewerASCIIOpen(PETSC_COMM_WORLD, file_name.c_str(), &viewer);
109 PetscViewerPushFormat(viewer, vw_format);
110
112
113 PetscObjectSetName((PetscObject)A_, "Stiffness_matrix");
114 MatView(A_, viewer);
115
116// This preprocessor is only for debugging, e.g. dump the matrix and exit the
117// program.
118// #define EXIT_TEST
119#ifdef EXIT_TEST
120 MatDestroy(A_);
121 PetscFinalize();
122 exit(0);
123#endif
124}
125
126void PETScMatrix::create(const PETScSparsityPattern& sparsity_pattern)
127{
128 PetscCallAbort(PETSC_COMM_WORLD, MatCreate(PETSC_COMM_WORLD, &A_));
129 PetscCallAbort(PETSC_COMM_WORLD,
130 MatSetSizes(A_, n_loc_rows_, n_loc_cols_, nrows_, ncols_));
131
132 PetscCallAbort(PETSC_COMM_WORLD, MatSetType(A_, MATAIJ));
133 PetscCallAbort(PETSC_COMM_WORLD, MatSetFromOptions(A_));
134
135 // Use MATPREALLOCATOR for type-agnostic exact sparsity preallocation.
136 // This works regardless of the matrix type chosen via -mat_type.
137 preallocateFromSparsityPattern(sparsity_pattern);
138
139 PetscCallAbort(PETSC_COMM_WORLD,
140 MatGetOwnershipRange(A_, &start_rank_, &end_rank_));
141 PetscCallAbort(PETSC_COMM_WORLD, MatGetSize(A_, &nrows_, &ncols_));
142 PetscCallAbort(PETSC_COMM_WORLD,
143 MatGetLocalSize(A_, &n_loc_rows_, &n_loc_cols_));
144}
145
147 const PETScSparsityPattern& sparsity_pattern)
148{
149 Mat preallocator;
150 PetscCallAbort(PETSC_COMM_WORLD,
151 MatCreate(PETSC_COMM_WORLD, &preallocator));
152 PetscCallAbort(
153 PETSC_COMM_WORLD,
154 MatSetSizes(preallocator, n_loc_rows_, n_loc_cols_, nrows_, ncols_));
155 PetscCallAbort(PETSC_COMM_WORLD, MatSetType(preallocator, MATPREALLOCATOR));
156 PetscCallAbort(PETSC_COMM_WORLD, MatSetUp(preallocator));
157
158 PetscInt row_start;
159 PetscInt row_end;
160 PetscCallAbort(PETSC_COMM_WORLD,
161 MatGetOwnershipRange(preallocator, &row_start, &row_end));
162
163 PetscInt const n_local = row_end - row_start;
164
165 // The pattern is indexed by local row below, so PETSc's row distribution
166 // must agree with the one the pattern was built for. A mismatch would
167 // otherwise silently read the wrong rows, or read out of bounds.
168 //
169 // The condition is rank-local, so it is reduced before aborting: a rank
170 // aborting on its own would leave the other ranks hanging in the collective
171 // assembly below instead of failing with it.
172 if (!BaseLib::MPI::allOf(sparsity_pattern.numberOfRows() == n_local))
173 {
174 OGS_FATAL(
175 "PETScMatrix: the sparsity pattern's local row count and PETSc's "
176 "row distribution disagree on at least one rank. On this rank the "
177 "pattern describes {} local rows, but PETSc distributed {} rows.",
178 sparsity_pattern.numberOfRows(), n_local);
179 }
180
181 // Record the nonzero structure of this rank's rows in the MATPREALLOCATOR
182 // helper, one row at a time. MATPREALLOCATOR stores positions only, so the
183 // values array is ignored and nullptr is passed. All rows here are in
184 // [row_start, row_end), so the off-process stash path (which would read
185 // the values) is never taken.
186 for (PetscInt local_row = 0; local_row < n_local; ++local_row)
187 {
188 PetscInt const global_row = row_start + local_row;
189 PetscInt const ncols = sparsity_pattern.nnzInRow(local_row);
190 PetscInt const* const cols = sparsity_pattern.col_idx.data() +
191 sparsity_pattern.row_ptr[local_row];
192 PetscCallAbort(PETSC_COMM_WORLD,
193 MatSetValues(preallocator, 1, &global_row, ncols, cols,
194 nullptr, INSERT_VALUES));
195 }
196
197 PetscCallAbort(PETSC_COMM_WORLD,
198 MatAssemblyBegin(preallocator, MAT_FINAL_ASSEMBLY));
199 PetscCallAbort(PETSC_COMM_WORLD,
200 MatAssemblyEnd(preallocator, MAT_FINAL_ASSEMBLY));
201
202 // Insert explicit zeros at every preallocated position by
203 // MatPreallocatorPreallocate below (fill = PETSC_TRUE) so that
204 // MAT_FINAL_ASSEMBLY in subsequent partial assemblies (e.g. soil-only) does
205 // not discard entries that were allocated but not yet written to.
206 constexpr PetscBool fill_zeros = PETSC_TRUE;
207 PetscCallAbort(PETSC_COMM_WORLD,
208 MatPreallocatorPreallocate(preallocator, fill_zeros, A_));
209 // The preallocator is a transient helper, destroyed once the target matrix
210 // has its structure. Keeping it would let further matrices built from the
211 // same sparsity pattern skip the row-by-row MatSetValues loop above, but
212 // the cache would have to live where the pattern lives (the matrix
213 // specifications / matrix provider), not in a single matrix. It is not done
214 // here: matrices are constructed a handful of times per process, and the
215 // loop costs one pass over the pattern, negligible next to assembly.
216 PetscCallAbort(PETSC_COMM_WORLD, MatDestroy(&preallocator));
217
218 sparsity_col_idx_ = sparsity_pattern.col_idx;
219}
220
221bool finalizeMatrixAssembly(PETScMatrix& mat, const MatAssemblyType asm_type)
222{
223 mat.finalizeAssembly(asm_type);
224 return true;
225}
226
227void PETScMatrix::addToDiagonal(const PetscScalar value)
228{
229 // MatShift computes A_ = A_ + value * I, i.e. adds value to every diagonal
230 // entry (creating missing ones). Correct in both serial and parallel.
231 // Temporarily allow new nonzero entries so a diagonal entry missing from
232 // the preallocated sparsity pattern doesn't abort the run.
233 PetscCallAbort(
234 PETSC_COMM_WORLD,
235 MatSetOption(A_, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE));
236 PetscCallAbort(PETSC_COMM_WORLD, MatShift(A_, value));
237 PetscCallAbort(
238 PETSC_COMM_WORLD,
239 MatSetOption(A_, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE));
240}
241
242// Matrices preallocated from equal column indices have, by construction,
243// identical nonzero structures: preallocation fills every position with an
244// explicit zero and setRowsColumnsZero() keeps the pattern. PETSc can then copy
245// the values array directly instead of merging row by row. An empty (unknown)
246// structure falls back to the safe, slower path.
247void setPreallocationNonzeroOption(PETScMatrix& matrix, bool const has_col_idx)
248{
249 if (has_col_idx)
250 {
251 PetscCallAbort(
252 PETSC_COMM_WORLD,
253 MatSetOption(matrix.getRawMatrix(), MAT_NEW_NONZERO_ALLOCATION_ERR,
254 PETSC_TRUE));
255 }
256 else
257 {
258 PetscCallAbort(PETSC_COMM_WORLD,
259 MatSetOption(matrix.getRawMatrix(),
260 MAT_NEW_NONZERO_LOCATIONS, PETSC_TRUE));
261 }
262}
263
264MatStructure nonzeroPatternStructure(std::vector<PetscInt> const& col_a,
265 std::vector<PetscInt> const& col_b)
266{
267 return (!col_a.empty() && col_a == col_b) ? SAME_NONZERO_PATTERN
268 : DIFFERENT_NONZERO_PATTERN;
269}
270
271} // namespace MathLib
#define OGS_FATAL(...)
Definition Error.h:10
Wrapper class for PETSc matrix routines for matrix.
Definition PETScMatrix.h:23
PetscInt n_loc_cols_
Number of the local columns.
PETScMatrix & operator=(PETScMatrix const &A)
Mat & getRawMatrix()
Get matrix reference.
Definition PETScMatrix.h:85
PetscInt end_rank_
Ending index in a rank.
void finalizeAssembly(const MatAssemblyType asm_type=MAT_FINAL_ASSEMBLY)
Perform MPI collection of assembled entries in buffer.
Definition PETScMatrix.h:60
PetscInt n_loc_rows_
Number of the local rows.
Mat A_
PETSc matrix.
void preallocateFromSparsityPattern(const PETScSparsityPattern &sparsity_pattern)
Preallocate the matrix storage from an exact sparsity pattern.
PetscInt nrows_
Number of the global rows.
void setRowsColumnsZero(std::vector< PetscInt > const &row_pos)
Set the specified rows to zero except diagonal entries, i.e. , where This function must be called...
PetscInt ncols_
Number of the global columns.
void viewer(const std::string &file_name, const PetscViewerFormat vw_format=PETSC_VIEWER_ASCII_MATLAB)
void create(const PETScSparsityPattern &sparsity_pattern)
Create the matrix, configure memory allocation and set the related member data.
void addToDiagonal(const PetscScalar value)
Add a constant value to all diagonal entries of the matrix.
PetscInt start_rank_
Starting index in a rank.
std::vector< PetscInt > sparsity_col_idx_
Column indices from preallocation (empty when fallback path was used).
static bool allOf(bool const val, Mpi const &mpi=Mpi{OGS_COMM_WORLD})
Definition MPI.h:190
void setPreallocationNonzeroOption(PETScMatrix &matrix, bool const has_col_idx)
MatStructure nonzeroPatternStructure(std::vector< PetscInt > const &col_a, std::vector< PetscInt > const &col_b)
bool finalizeMatrixAssembly(MAT_T &)
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).
PetscInt nnzInRow(PetscInt const row) const
Number of nonzeros in the given local row.
PetscInt numberOfRows() const
Number of local rows the pattern describes.