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>
30
31#include <dune/common/fmatrix.hh>
32#include <dune/istl/bcrsmatrix.hh>
33
34#include <HYPRE.h>
35#include <HYPRE_krylov.h>
36#include <HYPRE_parcsr_ls.h>
37#include <_hypre_utilities.h>
38
39#include <numeric>
40#include <vector>
41
42namespace Hypre
43{
44
46
65template <class M, class X, class Y, class Comm>
67{
68public:
70 using matrix_type = M;
72 using matrix_field_type = typename M::field_type;
74 using domain_type = X;
76 using range_type = Y;
78 using vector_field_type = typename X::field_type;
79
89 // template <typename Prm = std::enable_if_t<std::is_same_v<Comm, Dune::Amg::SequentialInformation>, ::Opm::PropertyTree>>
90 // HyprePreconditioner (const M& A, const Prm& prm)
91 // : HyprePreconditioner(A, prm, Dune::Amg::SequentialInformation())
92 // {
93 // //NB if this is used comm_ can never be used
94 // }
95
96 HyprePreconditioner (const M& A, const Opm::PropertyTree prm, const Comm& comm)
97 : A_(A),comm_(comm)
98 {
99 OPM_TIMEBLOCK(prec_construct);
100 int size;
101 int rank;
102 MPI_Comm mpi_comm;
103 if constexpr (std::is_same_v<Comm, Dune::Amg::SequentialInformation>) {
104 mpi_comm = MPI_COMM_SELF;
105 } else {
106 mpi_comm = comm.communicator();
107 }
108 MPI_Comm_size(mpi_comm, &size);
109 MPI_Comm_rank(mpi_comm, &rank);
110 if (size > 1) {
111 assert(size == comm.communicator().size());
112 assert(rank == comm.communicator().rank());
113 }
114
115 // Initialize Hypre library with backend configuration
116 HypreInterface::initialize(use_gpu_backend_);
117
118 // Create solver
120 HypreInterface::setSolverParameters(solver_, prm, use_gpu_backend_);
121
122 // Setup parallel info and mappings
123 par_info_ = HypreInterface::setupHypreParallelInfo(comm_, A_);
124
125 // Setup sparsity pattern
126 sparsity_pattern_ = HypreInterface::setupSparsityPattern(A_, par_info_, par_info_.owner_first);
127
128 // Setup host arrays
130 A_, sparsity_pattern_.ncols, par_info_.local_dune_to_local_hypre, par_info_.owner_first);
131
132 // Create indices for vector operations - simple sequential indices for owned DOFs
133 host_arrays_.indices.resize(par_info_.N_owned);
134 std::iota(host_arrays_.indices.begin(), host_arrays_.indices.end(), par_info_.dof_offset);
135
136 // Setup continuous vector values buffer - only needed for non-owner-first
137 if (!par_info_.owner_first) {
138 host_arrays_.continuous_vector_values.resize(par_info_.N_owned);
139 }
140
141 // Allocate device arrays if using GPU backend
142 if (use_gpu_backend_) {
143#if HYPRE_USING_CUDA || HYPRE_USING_HIP
144 device_arrays_.ncols_device = hypre_CTAlloc(HYPRE_Int, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
145 device_arrays_.rows_device = hypre_CTAlloc(HYPRE_BigInt, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
146 device_arrays_.cols_device = hypre_CTAlloc(HYPRE_BigInt, sparsity_pattern_.nnz, HYPRE_MEMORY_DEVICE);
147 device_arrays_.row_indexes_device = hypre_CTAlloc(HYPRE_Int, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
148 device_arrays_.indices_device = hypre_CTAlloc(HYPRE_BigInt, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
149 device_arrays_.vector_buffer_device = hypre_CTAlloc(HYPRE_Real, par_info_.N_owned, HYPRE_MEMORY_DEVICE);
151 // For CPU input and GPU backend we need to allocate space for transfering the matrix values
152 // Note that the buffer must be allocated with the number of nonzeroes in the matrix, not the
153 // sparsity_pattern.nnz because we need to copy the entire matrix values from the host to the device.
154 device_arrays_.matrix_buffer_device = hypre_CTAlloc(HYPRE_Real, A_.nonzeroes(), HYPRE_MEMORY_DEVICE);
155 }
156 // Copy data to device
157 hypre_TMemcpy(device_arrays_.ncols_device, sparsity_pattern_.ncols.data(), HYPRE_Int, par_info_.N_owned, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
158 hypre_TMemcpy(device_arrays_.rows_device, sparsity_pattern_.rows.data(), HYPRE_BigInt, par_info_.N_owned, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
159 hypre_TMemcpy(device_arrays_.cols_device, sparsity_pattern_.cols.data(), HYPRE_BigInt, sparsity_pattern_.nnz, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
160 hypre_TMemcpy(device_arrays_.row_indexes_device, host_arrays_.row_indexes.data(), HYPRE_Int, par_info_.N_owned, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
161 hypre_TMemcpy(device_arrays_.indices_device, host_arrays_.indices.data(), HYPRE_BigInt, par_info_.N_owned, HYPRE_MEMORY_DEVICE, HYPRE_MEMORY_HOST);
162#endif
163 }
164
165 // Create Hypre matrix and vectors
166 A_hypre_ = HypreInterface::createMatrix(par_info_.N_owned, par_info_.dof_offset, comm_);
167 x_hypre_ = HypreInterface::createVector(par_info_.N_owned, par_info_.dof_offset, comm_);
168 b_hypre_ = HypreInterface::createVector(par_info_.N_owned, par_info_.dof_offset, comm_);
169
170 // Perform initial update
171 update();
172 }
173
180 {
181 // Clean up device arrays if allocated
182 if (use_gpu_backend_) {
183#if HYPRE_USING_CUDA || HYPRE_USING_HIP
184 if (device_arrays_.ncols_device) {
185 hypre_TFree(device_arrays_.ncols_device, HYPRE_MEMORY_DEVICE);
186 }
187 if (device_arrays_.rows_device) {
188 hypre_TFree(device_arrays_.rows_device, HYPRE_MEMORY_DEVICE);
189 }
190 if (device_arrays_.cols_device) {
191 hypre_TFree(device_arrays_.cols_device, HYPRE_MEMORY_DEVICE);
192 }
193 if (device_arrays_.row_indexes_device) {
194 hypre_TFree(device_arrays_.row_indexes_device, HYPRE_MEMORY_DEVICE);
195 }
196 if (device_arrays_.indices_device) {
197 hypre_TFree(device_arrays_.indices_device, HYPRE_MEMORY_DEVICE);
198 }
199 if (device_arrays_.vector_buffer_device) {
200 hypre_TFree(device_arrays_.vector_buffer_device, HYPRE_MEMORY_DEVICE);
201 }
202 if (device_arrays_.matrix_buffer_device) {
203 hypre_TFree(device_arrays_.matrix_buffer_device, HYPRE_MEMORY_DEVICE);
204 }
205#endif
206 }
207
212 }
213
219 void update() override
220 {
221 OPM_TIMEBLOCK(prec_update);
222
223 // Update matrix values using pre-allocated helper arrays
225 A_, A_hypre_, sparsity_pattern_, host_arrays_, device_arrays_, use_gpu_backend_);
226
227 // Get the underlying ParCSR matrix for setup
228 HYPRE_ParCSRMatrix parcsr_A;
229 HYPRE_SAFE_CALL(HYPRE_IJMatrixGetObject(A_hypre_, reinterpret_cast<void**>(&parcsr_A)));
230
231 // Get the underlying ParVector objects
232 HYPRE_ParVector par_x, par_b;
233 HYPRE_SAFE_CALL(HYPRE_IJVectorGetObject(x_hypre_, reinterpret_cast<void**>(&par_x)));
234 HYPRE_SAFE_CALL(HYPRE_IJVectorGetObject(b_hypre_, reinterpret_cast<void**>(&par_b)));
235
236 // Setup the solver
237 HYPRE_SAFE_CALL(HYPRE_BoomerAMGSetup(solver_, parcsr_A, par_b, par_x));
238 }
239
248 void pre(X& v, Y& /*d*/) override
249 {
250 comm_.copyOwnerToAll(v, v); // From dune: make dirichlet values consistent ??
251 }
252
263 void apply(X& v, const Y& d) override
264 {
265 OPM_TIMEBLOCK(prec_apply);
266
267 // Transfer vectors to Hypre
268 HypreInterface::transferVectorToHypre(v, x_hypre_, host_arrays_, device_arrays_, par_info_, use_gpu_backend_);
269 HypreInterface::transferVectorToHypre(d, b_hypre_, host_arrays_, device_arrays_, par_info_, use_gpu_backend_);
270
271 // Get the underlying ParCSR matrix and ParVector objects
272 HYPRE_ParCSRMatrix parcsr_A;
273 HYPRE_ParVector par_x, par_b;
274 HYPRE_SAFE_CALL(HYPRE_IJMatrixGetObject(A_hypre_, reinterpret_cast<void**>(&parcsr_A)));
275 HYPRE_SAFE_CALL(HYPRE_IJVectorGetObject(x_hypre_, reinterpret_cast<void**>(&par_x)));
276 HYPRE_SAFE_CALL(HYPRE_IJVectorGetObject(b_hypre_, reinterpret_cast<void**>(&par_b)));
277
278 // Apply the preconditioner (one AMG V-cycle)
279 HYPRE_SAFE_CALL(HYPRE_BoomerAMGSolve(solver_, parcsr_A, par_b, par_x));
280
281 // Transfer result back
282 HypreInterface::transferVectorFromHypre(x_hypre_, v, host_arrays_, device_arrays_, par_info_, use_gpu_backend_);
283 // NB do we need to sync values to get correct values since a preconditioner
284 // consistent result (a operator apply should give unique).
285 comm_.copyOwnerToAll(v, v);
286 }
287
295 void post(X& /*v*/) override
296 {
297 }
298
304 Dune::SolverCategory::Category category() const override
305 {
306 return std::is_same_v<Comm, Dune::Amg::SequentialInformation> ? Dune::SolverCategory::sequential
307 : Dune::SolverCategory::overlapping;
308 }
309
315 bool hasPerfectUpdate() const override
316 {
317 // The Hypre preconditioner can depend on the values of the matrix so it does not have perfect update.
318 // However, copying the matrix to Hypre requires to setup the solver again, so this is handled internally.
319 // So for ISTLSolver, we can return true.
320 return true;
321 }
322
323private:
324 // Reference to the input matrix
325 const M& A_;
326 const Comm& comm_;
327
328 // Parallel information and sparsity pattern from HypreInterface
330 HypreInterface::SparsityPattern sparsity_pattern_;
331 HypreInterface::HostArrays host_arrays_;
332 HypreInterface::DeviceArrays device_arrays_;
333
334 // Hypre handles
335 HYPRE_Solver solver_ = nullptr;
336 HYPRE_IJMatrix A_hypre_ = nullptr;
337 HYPRE_IJVector x_hypre_ = nullptr;
338 HYPRE_IJVector b_hypre_ = nullptr;
339
340 // Backend configuration
341 bool use_gpu_backend_ = false;
342};
343
344} // namespace Hypre
345
346#endif
Dune::OwnerOverlapCopyCommunication< int, int > Comm
Definition: FlexibleSolver_impl.hpp:304
#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:32
Wrapper for Hypre's BoomerAMG preconditioner.
Definition: HyprePreconditioner.hpp:67
M matrix_type
The matrix type the preconditioner is for.
Definition: HyprePreconditioner.hpp:70
void apply(X &v, const Y &d) override
Applies the preconditioner to a vector.
Definition: HyprePreconditioner.hpp:263
typename M::field_type matrix_field_type
The field type of the matrix.
Definition: HyprePreconditioner.hpp:72
X domain_type
The domain type of the preconditioner.
Definition: HyprePreconditioner.hpp:74
void update() override
Updates the preconditioner with the current matrix values.
Definition: HyprePreconditioner.hpp:219
~HyprePreconditioner()
Destructor for HyprePreconditioner.
Definition: HyprePreconditioner.hpp:179
Dune::SolverCategory::Category category() const override
Returns the solver category.
Definition: HyprePreconditioner.hpp:304
typename X::field_type vector_field_type
The field type of the vectors.
Definition: HyprePreconditioner.hpp:78
void pre(X &v, Y &) override
Pre-processing step before applying the preconditioner.
Definition: HyprePreconditioner.hpp:248
bool hasPerfectUpdate() const override
Checks if the preconditioner has a perfect update.
Definition: HyprePreconditioner.hpp:315
HyprePreconditioner(const M &A, const Opm::PropertyTree prm, const Comm &comm)
Constructor for the HyprePreconditioner class.
Definition: HyprePreconditioner.hpp:96
Y range_type
The range type of the preconditioner.
Definition: HyprePreconditioner.hpp:76
void post(X &) override
Post-processing step after applying the preconditioner.
Definition: HyprePreconditioner.hpp:295
Hierarchical collection of key/value pairs.
Definition: PropertyTree.hpp:39
Definition: HyprePreconditioner.hpp:43
Unified interface for Hypre operations with both CPU and GPU data structures.
Definition: HypreInterface.hpp:61
void destroySolver(HYPRE_Solver solver)
Destroy Hypre solver.
Definition: HypreSetup.hpp:219
void transferVectorToHypre(const VectorType &vec, HYPRE_IJVector hypre_vec, HostArrays &host_arrays, const DeviceArrays &device_arrays, const ParallelInfo &par_info, bool use_gpu_backend)
Transfer vector to Hypre from any vector type (CPU or GPU)
Definition: HypreInterface.hpp:164
ParallelInfo setupHypreParallelInfo(const CommType &comm, const MatrixType &matrix)
Setup parallel information for Hypre (automatically detects serial/parallel)
Definition: HypreSetup.hpp:263
void destroyMatrix(HYPRE_IJMatrix matrix)
Destroy Hypre matrix.
Definition: HypreSetup.hpp:233
HYPRE_Solver createAMGSolver()
Create Hypre solver (BoomerAMG)
Definition: HypreSetup.hpp:117
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: HypreSetup.hpp:634
HYPRE_IJMatrix createMatrix(HYPRE_Int N, HYPRE_Int dof_offset, const CommType &comm)
Create Hypre matrix.
Definition: HypreSetup.hpp:170
void updateMatrixValues(const MatrixType &matrix, HYPRE_IJMatrix hypre_matrix, const SparsityPattern &sparsity_pattern, const HostArrays &host_arrays, const DeviceArrays &device_arrays, bool use_gpu_backend)
Update matrix values in Hypre.
Definition: HypreInterface.hpp:200
SparsityPattern setupSparsityPattern(const MatrixType &matrix, const ParallelInfo &par_info, bool owner_first)
Setup sparsity pattern from matrix (automatically detects CPU/GPU type)
Definition: HypreSetup.hpp:465
void initialize(bool use_gpu_backend)
Initialize the Hypre library and set memory/execution policy.
Definition: HypreSetup.hpp:89
HYPRE_IJVector createVector(HYPRE_Int N, HYPRE_Int dof_offset, const CommType &comm)
Create Hypre vector.
Definition: HypreSetup.hpp:197
void destroyVector(HYPRE_IJVector vector)
Destroy Hypre vector.
Definition: HypreSetup.hpp:247
void setSolverParameters(HYPRE_Solver solver, const PropertyTree &prm, bool use_gpu_backend)
Set solver parameters from property tree.
Definition: HypreSetup.hpp:133
void transferVectorFromHypre(HYPRE_IJVector hypre_vec, VectorType &vec, HostArrays &host_arrays, const DeviceArrays &device_arrays, const ParallelInfo &par_info, bool use_gpu_backend)
Transfer vector from Hypre to any vector type (CPU or GPU)
Definition: HypreInterface.hpp:182
GPU device memory arrays for HYPRE operations with GPU backend.
Definition: HypreDataStructures.hpp:137
HYPRE_BigInt * rows_device
Definition: HypreDataStructures.hpp:140
HYPRE_Real * vector_buffer_device
Device buffer for vector operations Used when input type and backend are different,...
Definition: HypreDataStructures.hpp:149
HYPRE_Int * ncols_device
Mirrors host data arrays.
Definition: HypreDataStructures.hpp:139
HYPRE_Int * row_indexes_device
Definition: HypreDataStructures.hpp:142
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
Host arrays for HYPRE matrix and vector data transfers.
Definition: HypreDataStructures.hpp:106
std::vector< HYPRE_BigInt > indices
Global DOF indices for owned degrees of freedom.
Definition: HypreDataStructures.hpp:120
std::vector< HYPRE_Real > continuous_vector_values
Temporary buffer for vector values in non-owner-first ordering.
Definition: HypreDataStructures.hpp:128
std::vector< HYPRE_Int > row_indexes
Pre-computed row start indexes for HYPRE_IJMatrixSetValues2.
Definition: HypreDataStructures.hpp:113
Parallel domain decomposition information for HYPRE-Dune interface.
Definition: HypreDataStructures.hpp:37
bool owner_first
Whether owned DOFs appear first in local Dune ordering.
Definition: HypreDataStructures.hpp:77
std::vector< int > local_dune_to_local_hypre
Mapping from local Dune indices to local HYPRE indices.
Definition: HypreDataStructures.hpp:44
HYPRE_Int dof_offset
Global index offset for this process's owned DOFs.
Definition: HypreDataStructures.hpp:69
HYPRE_Int N_owned
Number of DOFs owned by this MPI process.
Definition: HypreDataStructures.hpp:62
Compressed Sparse Row (CSR) sparsity pattern for HYPRE matrix assembly.
Definition: HypreDataStructures.hpp:86
std::vector< HYPRE_BigInt > rows
Global row indices for owned rows (size: N_owned)
Definition: HypreDataStructures.hpp:91
std::vector< HYPRE_Int > ncols
Non-zero entries per owned row (size: N_owned)
Definition: HypreDataStructures.hpp:88
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
Type trait to detect if a type is a GPU type.
Definition: gpu_type_detection.hpp:40