HyprePreconditioner.hpp
Go to the documentation of this file.
1/*
2 Copyright 2024 SINTEF AS
3 Copyright 2024-2025 Equinor ASA
4
5 This file is part of the Open Porous Media project (OPM).
6
7 OPM is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 OPM is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with OPM. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#ifndef OPM_HYPRE_PRECONDITIONER_HEADER_INCLUDED
22#define OPM_HYPRE_PRECONDITIONER_HEADER_INCLUDED
23
24#include <opm/common/ErrorMacros.hpp>
25#include <opm/common/TimingMacros.hpp>
29
30#if HYPRE_USING_CUDA
32#elif HYPRE_USING_HIP
33#include <opm/simulators/linalg/gpuistl_hip/detail/gpu_type_detection.hpp>
34#endif
35
36#include <dune/common/fmatrix.hh>
37#include <dune/istl/bcrsmatrix.hh>
38
39#include <HYPRE.h>
40#include <HYPRE_krylov.h>
41#include <HYPRE_parcsr_ls.h>
42#include <_hypre_utilities.h>
43
44#include <numeric>
45#include <vector>
46
47namespace Opm::linalg
48{
49
68template <class M, class X, class Y, class Comm>
70{
71public:
73 using matrix_type = M;
75 using matrix_field_type = typename M::field_type;
77 using domain_type = X;
79 using range_type = Y;
81 using vector_field_type = typename X::field_type;
82
92 HyprePreconditioner(const M& A, const Opm::PropertyTree prm, const Comm& comm)
93 : A_(A)
94 , comm_(comm)
95 {
96 OPM_TIMEBLOCK(prec_construct);
97 int size;
98 int rank;
99 MPI_Comm mpi_comm;
100 if constexpr (std::is_same_v<Comm, Dune::Amg::SequentialInformation>) {
101 mpi_comm = MPI_COMM_SELF;
102 } else {
103 mpi_comm = comm.communicator();
104 }
105 MPI_Comm_size(mpi_comm, &size);
106 MPI_Comm_rank(mpi_comm, &rank);
107 if (size > 1) {
108 assert(size == comm.communicator().size());
109 assert(rank == comm.communicator().rank());
110 }
111 // Set use_gpu_backend_ to user value if specified, otherwise match input type
112#if HYPRE_USING_CUDA || HYPRE_USING_HIP
113 use_gpu_backend_ = prm.get<bool>("use_gpu", gpuistl::is_gpu_type<M>::value);
114#endif
115
116 // Initialize Hypre library with backend configuration
117 HypreInterface::initialize(use_gpu_backend_);
118
119 // Create solver
121 HypreInterface::setSolverParameters(solver_, prm, use_gpu_backend_);
122
123 // Setup parallel info and mappings
124 par_info_ = HypreInterface::setupHypreParallelInfo(comm_, A_);
125
126 // Setup sparsity pattern
127 sparsity_pattern_ = HypreInterface::setupSparsityPattern(A_, par_info_, par_info_.owner_first);
128
129 // Setup host arrays
131 sparsity_pattern_.ncols,
133 par_info_.owner_first);
134
135 // Create indices for vector operations - simple sequential indices for owned DOFs
136 host_arrays_.indices.resize(par_info_.N_owned);
137 std::iota(host_arrays_.indices.begin(), host_arrays_.indices.end(), par_info_.dof_offset);
138
139 // Setup continuous vector values buffer - only needed for non-owner-first
140 if (!par_info_.owner_first) {
141 host_arrays_.continuous_vector_values.resize(par_info_.N_owned);
142 }
143
144 // Allocate device arrays if using GPU backend
145 if (use_gpu_backend_) {
146#if HYPRE_USING_CUDA || HYPRE_USING_HIP
147 device_arrays_.ncols_device = hypre_CTAlloc(HYPRE_Int, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
148 device_arrays_.rows_device = hypre_CTAlloc(HYPRE_BigInt, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
149 device_arrays_.cols_device = hypre_CTAlloc(HYPRE_BigInt, sparsity_pattern_.nnz, HYPRE_MEMORY_DEVICE);
150 device_arrays_.row_indexes_device = hypre_CTAlloc(HYPRE_Int, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
151 device_arrays_.indices_device = hypre_CTAlloc(HYPRE_BigInt, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
152 device_arrays_.vector_buffer_device = hypre_CTAlloc(HYPRE_Real, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
154 // For CPU input and GPU backend we need to allocate space for transfering the matrix values
155 // Note that the buffer must be allocated with the number of nonzeroes in the matrix, not the
156 // sparsity_pattern.nnz because we need to copy the entire matrix values from the host to the device.
157 device_arrays_.matrix_buffer_device = hypre_CTAlloc(HYPRE_Real, A_.nonzeroes(), HYPRE_MEMORY_DEVICE);
158 }
159 // Copy data to device
160 hypre_TMemcpy(device_arrays_.ncols_device, sparsity_pattern_.ncols.data(), HYPRE_Int, par_info_.N_owned, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
161 hypre_TMemcpy(device_arrays_.rows_device, sparsity_pattern_.rows.data(), HYPRE_BigInt, par_info_.N_owned, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
162 hypre_TMemcpy(device_arrays_.cols_device, sparsity_pattern_.cols.data(), HYPRE_BigInt, sparsity_pattern_.nnz, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
163 hypre_TMemcpy(device_arrays_.row_indexes_device, host_arrays_.row_indexes.data(), HYPRE_Int, par_info_.N_owned, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
164 hypre_TMemcpy(device_arrays_.indices_device, host_arrays_.indices.data(), HYPRE_BigInt, par_info_.N_owned, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
165#endif
166 }
167
168 // Create Hypre matrix and vectors
169 A_hypre_ = HypreInterface::createMatrix(par_info_.N_owned, par_info_.dof_offset, comm_);
170 x_hypre_ = HypreInterface::createVector(par_info_.N_owned, par_info_.dof_offset, comm_);
171 b_hypre_ = HypreInterface::createVector(par_info_.N_owned, par_info_.dof_offset, comm_);
172
173 // Perform initial update
174 update();
175 }
176
183 {
184 // Clean up device arrays if allocated
185 if (use_gpu_backend_) {
186#if HYPRE_USING_CUDA || HYPRE_USING_HIP
187 if (device_arrays_.ncols_device) {
188 hypre_TFree(device_arrays_.ncols_device, HYPRE_MEMORY_DEVICE);
189 }
190 if (device_arrays_.rows_device) {
191 hypre_TFree(device_arrays_.rows_device, HYPRE_MEMORY_DEVICE);
192 }
193 if (device_arrays_.cols_device) {
194 hypre_TFree(device_arrays_.cols_device, HYPRE_MEMORY_DEVICE);
195 }
196 if (device_arrays_.row_indexes_device) {
197 hypre_TFree(device_arrays_.row_indexes_device, HYPRE_MEMORY_DEVICE);
198 }
199 if (device_arrays_.indices_device) {
200 hypre_TFree(device_arrays_.indices_device, HYPRE_MEMORY_DEVICE);
201 }
202 if (device_arrays_.vector_buffer_device) {
203 hypre_TFree(device_arrays_.vector_buffer_device, HYPRE_MEMORY_DEVICE);
204 }
205 if (device_arrays_.matrix_buffer_device) {
206 hypre_TFree(device_arrays_.matrix_buffer_device, HYPRE_MEMORY_DEVICE);
207 }
208#endif
209 }
210
215 }
216
222 void update() override
223 {
224 OPM_TIMEBLOCK(prec_update);
225
226 // Update matrix values using pre-allocated helper arrays
227 HypreInterface::updateMatrixValues(A_, A_hypre_, sparsity_pattern_,
228 host_arrays_, device_arrays_, use_gpu_backend_);
229
230 // Get the underlying ParCSR matrix for setup
231 HYPRE_ParCSRMatrix parcsr_A;
232 HYPRE_SAFE_CALL(HYPRE_IJMatrixGetObject(A_hypre_, reinterpret_cast<void**>(&parcsr_A)));
233
234 // Get the underlying ParVector objects
235 HYPRE_ParVector par_x, par_b;
236 HYPRE_SAFE_CALL(HYPRE_IJVectorGetObject(x_hypre_, reinterpret_cast<void**>(&par_x)));
237 HYPRE_SAFE_CALL(HYPRE_IJVectorGetObject(b_hypre_, reinterpret_cast<void**>(&par_b)));
238
239 // Setup the solver
240 HYPRE_SAFE_CALL(HYPRE_BoomerAMGSetup(solver_, parcsr_A, par_b, par_x));
241 }
242
251 void pre(X& v, Y& /*d*/) override
252 {
253 comm_.copyOwnerToAll(v, v); // From dune: make dirichlet values consistent ??
254 }
255
266 void apply(X& v, const Y& d) override
267 {
268 OPM_TIMEBLOCK(prec_apply);
269
270 // Transfer vectors to Hypre
271 HypreInterface::transferVectorToHypre(v, x_hypre_, host_arrays_,
272 device_arrays_, par_info_, use_gpu_backend_);
273 HypreInterface::transferVectorToHypre(d, b_hypre_, host_arrays_,
274 device_arrays_, par_info_, use_gpu_backend_);
275
276 // Get the underlying ParCSR matrix and ParVector objects
277 HYPRE_ParCSRMatrix parcsr_A;
278 HYPRE_ParVector par_x, par_b;
279 HYPRE_SAFE_CALL(HYPRE_IJMatrixGetObject(A_hypre_, reinterpret_cast<void**>(&parcsr_A)));
280 HYPRE_SAFE_CALL(HYPRE_IJVectorGetObject(x_hypre_, reinterpret_cast<void**>(&par_x)));
281 HYPRE_SAFE_CALL(HYPRE_IJVectorGetObject(b_hypre_, reinterpret_cast<void**>(&par_b)));
282
283 // Apply the preconditioner (one AMG V-cycle)
284 HYPRE_SAFE_CALL(HYPRE_BoomerAMGSolve(solver_, parcsr_A, par_b, par_x));
285
286 // Transfer result back
287 HypreInterface::transferVectorFromHypre(x_hypre_, v, host_arrays_,
288 device_arrays_, par_info_, use_gpu_backend_);
289 // NB do we need to sync values to get correct values since a preconditioner
290 // consistent result (a operator apply should give unique).
291 comm_.copyOwnerToAll(v, v);
292 }
293
301 void post(X& /*v*/) override
302 {
303 }
304
310 Dune::SolverCategory::Category category() const override
311 {
312 return std::is_same_v<Comm, Dune::Amg::SequentialInformation> ? Dune::SolverCategory::sequential
313 : Dune::SolverCategory::overlapping;
314 }
315
321 bool hasPerfectUpdate() const override
322 {
323 // The Hypre preconditioner can depend on the values of the matrix so it does not have perfect update.
324 // However, copying the matrix to Hypre requires to setup the solver again, so this is handled internally.
325 // So for ISTLSolver, we can return true.
326 return true;
327 }
328
329private:
330 // Reference to the input matrix
331 const M& A_;
332 const Comm& comm_;
333
334 // Parallel information and sparsity pattern from HypreInterface
336 HypreInterface::SparsityPattern sparsity_pattern_;
339
340 // Hypre handles
341 HYPRE_Solver solver_ = nullptr;
342 HYPRE_IJMatrix A_hypre_ = nullptr;
343 HYPRE_IJVector x_hypre_ = nullptr;
344 HYPRE_IJVector b_hypre_ = nullptr;
345
346 // Backend configuration
347 bool use_gpu_backend_ = false;
348};
349
350} // namespace Hypre
351
352#endif
Dune::OwnerOverlapCopyCommunication< int, int > Comm
Definition: FlexibleSolver_impl.hpp:325
#define HYPRE_SAFE_CALL(expr)
Short form macro for Hypre function calls (for backward compatibility)
Definition: HypreErrorHandling.hpp:102
Interface class adding the update() method to the preconditioner interface.
Definition: PreconditionerWithUpdate.hpp:34
Hierarchical collection of key/value pairs.
Definition: PropertyTree.hpp:39
T get(const std::string &key) const
Wrapper for Hypre's BoomerAMG preconditioner.
Definition: HyprePreconditioner.hpp:70
void apply(X &v, const Y &d) override
Applies the preconditioner to a vector.
Definition: HyprePreconditioner.hpp:266
HyprePreconditioner(const M &A, const Opm::PropertyTree prm, const Comm &comm)
Constructor for the HyprePreconditioner class.
Definition: HyprePreconditioner.hpp:92
void post(X &) override
Post-processing step after applying the preconditioner.
Definition: HyprePreconditioner.hpp:301
Y range_type
The range type of the preconditioner.
Definition: HyprePreconditioner.hpp:79
bool hasPerfectUpdate() const override
Checks if the preconditioner has a perfect update.
Definition: HyprePreconditioner.hpp:321
void update() override
Updates the preconditioner with the current matrix values.
Definition: HyprePreconditioner.hpp:222
typename X::field_type vector_field_type
The field type of the vectors.
Definition: HyprePreconditioner.hpp:81
Dune::SolverCategory::Category category() const override
Returns the solver category.
Definition: HyprePreconditioner.hpp:310
~HyprePreconditioner()
Destructor for HyprePreconditioner.
Definition: HyprePreconditioner.hpp:182
X domain_type
The domain type of the preconditioner.
Definition: HyprePreconditioner.hpp:77
M matrix_type
The matrix type the preconditioner is for.
Definition: HyprePreconditioner.hpp:73
typename M::field_type matrix_field_type
The field type of the matrix.
Definition: HyprePreconditioner.hpp:75
void pre(X &v, Y &) override
Pre-processing step before applying the preconditioner.
Definition: HyprePreconditioner.hpp:251
void initialize(bool use_gpu_backend)
Initialize the Hypre library and set memory/execution policy.
Definition: hypreinterface/HypreSetup.hpp:84
HYPRE_IJVector createVector(HYPRE_Int N, HYPRE_Int dof_offset, const CommType &comm)
Create Hypre vector.
Definition: hypreinterface/HypreSetup.hpp:192
void destroySolver(HYPRE_Solver solver)
Destroy Hypre solver.
Definition: hypreinterface/HypreSetup.hpp:214
SparsityPattern setupSparsityPattern(const MatrixType &matrix, const ParallelInfo &par_info, bool owner_first)
Setup sparsity pattern from matrix (automatically detects CPU/GPU type)
Definition: hypreinterface/HypreSetup.hpp:462
ParallelInfo setupHypreParallelInfo(const CommType &comm, const MatrixType &matrix)
Setup parallel information for Hypre (automatically detects serial/parallel)
Definition: hypreinterface/HypreSetup.hpp:258
void destroyMatrix(HYPRE_IJMatrix matrix)
Destroy Hypre matrix.
Definition: hypreinterface/HypreSetup.hpp:228
HYPRE_IJMatrix createMatrix(HYPRE_Int N, HYPRE_Int dof_offset, const CommType &comm)
Create Hypre matrix.
Definition: hypreinterface/HypreSetup.hpp:165
void updateMatrixValues(const MatrixType &matrix, HYPRE_IJMatrix hypre_matrix, const SparsityPattern &sparsity_pattern, const HostDataArrays &host_arrays, const DeviceDataArrays &device_arrays, bool use_gpu_backend)
Update matrix values in Hypre.
Definition: HypreInterface.hpp:206
void transferVectorFromHypre(HYPRE_IJVector hypre_vec, VectorType &vec, HostDataArrays &host_arrays, const DeviceDataArrays &device_arrays, const ParallelInfo &par_info, bool use_gpu_backend)
Transfer vector from Hypre to any vector type (CPU or GPU)
Definition: HypreInterface.hpp:186
void setSolverParameters(HYPRE_Solver solver, const PropertyTree &prm, bool use_gpu_backend)
Set solver parameters from property tree.
Definition: hypreinterface/HypreSetup.hpp:128
std::vector< HYPRE_Int > computeRowIndexes(const MatrixType &matrix, const std::vector< HYPRE_Int > &ncols, const std::vector< int > &local_dune_to_local_hypre, bool owner_first)
Compute row indexes for HYPRE_IJMatrixSetValues2.
Definition: hypreinterface/HypreSetup.hpp:557
void transferVectorToHypre(const VectorType &vec, HYPRE_IJVector hypre_vec, HostDataArrays &host_arrays, const DeviceDataArrays &device_arrays, const ParallelInfo &par_info, bool use_gpu_backend)
Transfer vector to Hypre from any vector type (CPU or GPU)
Definition: HypreInterface.hpp:166
void destroyVector(HYPRE_IJVector vector)
Destroy Hypre vector.
Definition: hypreinterface/HypreSetup.hpp:242
HYPRE_Solver createAMGSolver()
Create Hypre solver (BoomerAMG)
Definition: hypreinterface/HypreSetup.hpp:112
Definition: hypreinterface/HypreCpuTransfers.hpp:36
Type trait to detect if a type is a GPU type.
Definition: gpu_type_detection.hpp:40
GPU device memory arrays for HYPRE operations with GPU backend.
Definition: HypreDataStructures.hpp:137
HYPRE_Real * vector_buffer_device
Device buffer for vector operations Used when input type and backend are different,...
Definition: HypreDataStructures.hpp:149
HYPRE_BigInt * rows_device
Definition: HypreDataStructures.hpp:140
HYPRE_Int * ncols_device
Mirrors host data arrays.
Definition: HypreDataStructures.hpp:139
HYPRE_BigInt * indices_device
Definition: HypreDataStructures.hpp:143
HYPRE_Real * matrix_buffer_device
Device buffer for matrix values, only needed for CPU input + GPU backend.
Definition: HypreDataStructures.hpp:155
HYPRE_BigInt * cols_device
Definition: HypreDataStructures.hpp:141
HYPRE_Int * row_indexes_device
Definition: HypreDataStructures.hpp:142
Host arrays for HYPRE matrix and vector data transfers.
Definition: HypreDataStructures.hpp:106
std::vector< HYPRE_Int > row_indexes
Pre-computed row start indexes for HYPRE_IJMatrixSetValues2.
Definition: HypreDataStructures.hpp:113
std::vector< HYPRE_Real > continuous_vector_values
Temporary buffer for vector values in non-owner-first ordering.
Definition: HypreDataStructures.hpp:128
std::vector< HYPRE_BigInt > indices
Global DOF indices for owned degrees of freedom.
Definition: HypreDataStructures.hpp:120
Parallel domain decomposition information for HYPRE-Dune interface.
Definition: HypreDataStructures.hpp:37
HYPRE_Int dof_offset
Global index offset for this process's owned DOFs.
Definition: HypreDataStructures.hpp:69
std::vector< int > local_dune_to_local_hypre
Mapping from local Dune indices to local HYPRE indices.
Definition: HypreDataStructures.hpp:44
HYPRE_Int N_owned
Number of DOFs owned by this MPI process.
Definition: HypreDataStructures.hpp:62
bool owner_first
Whether owned DOFs appear first in local Dune ordering.
Definition: HypreDataStructures.hpp:77
Compressed Sparse Row (CSR) sparsity pattern for HYPRE matrix assembly.
Definition: HypreDataStructures.hpp:86
HYPRE_Int nnz
Number of non-zero entries in matrix.
Definition: HypreDataStructures.hpp:97
std::vector< HYPRE_BigInt > cols
Global column indices in CSR format (size: nnz)
Definition: HypreDataStructures.hpp:94
std::vector< HYPRE_Int > ncols
Non-zero entries per owned row (size: N_owned)
Definition: HypreDataStructures.hpp:88
std::vector< HYPRE_BigInt > rows
Global row indices for owned rows (size: N_owned)
Definition: HypreDataStructures.hpp:91