opm-simulators
AmgxPreconditioner.hpp
1 /*
2  Copyright 2024 SINTEF AS
3  Copyright 2024 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_AMGX_PRECONDITIONER_HEADER_INCLUDED
22 #define OPM_AMGX_PRECONDITIONER_HEADER_INCLUDED
23 
24 #include <opm/common/ErrorMacros.hpp>
25 #include <opm/common/TimingMacros.hpp>
26 #include <opm/simulators/linalg/PreconditionerWithUpdate.hpp>
27 #include <opm/simulators/linalg/PropertyTree.hpp>
28 #include <opm/simulators/linalg/gpuistl/AmgxInterface.hpp>
29 
30 #include <dune/common/fmatrix.hh>
31 #include <dune/istl/bcrsmatrix.hh>
32 
33 #include <amgx_c.h>
34 
35 namespace Amgx
36 {
37 
38 using AmgxInterface = Opm::gpuistl::AmgxInterface;
39 
45 struct AmgxConfig {
46  int determinism_flag = 0;
47  int print_grid_stats = 0;
48  int print_solve_stats = 0;
49  std::string solver = "AMG";
50  std::string algorithm = "CLASSICAL";
51  std::string interpolator = "D2";
52  std::string selector = "PMIS";
53  std::string smoother = "BLOCK_JACOBI";
54  int presweeps = 3;
55  int postsweeps = 3;
56  double strength_threshold = 0.5;
57  int max_iters = 1;
58 
59  explicit AmgxConfig(const Opm::PropertyTree& prm)
60  {
61  determinism_flag = prm.get<int>("determinism_flag", determinism_flag);
62  print_grid_stats = prm.get<int>("print_grid_stats", print_grid_stats);
63  print_solve_stats = prm.get<int>("print_solve_stats", print_solve_stats);
64  solver = prm.get<std::string>("solver", solver);
65  algorithm = prm.get<std::string>("algorithm", algorithm);
66  interpolator = prm.get<std::string>("interpolator", interpolator);
67  selector = prm.get<std::string>("selector", selector);
68  smoother = prm.get<std::string>("smoother", smoother);
69  presweeps = prm.get<int>("presweeps", presweeps);
70  postsweeps = prm.get<int>("postsweeps", postsweeps);
71  strength_threshold = prm.get<double>("strength_threshold", strength_threshold);
72  max_iters = prm.get<int>("max_iters", max_iters);
73  }
74 
75  std::string toString() const {
76  return "config_version=2, "
77  "determinism_flag=" + std::to_string(determinism_flag) + ", "
78  "print_grid_stats=" + std::to_string(print_grid_stats) + ", "
79  "print_solve_stats=" + std::to_string(print_solve_stats) + ", "
80  "solver=" + solver + ", "
81  "algorithm=" + algorithm + ", "
82  "interpolator=" + interpolator + ", "
83  "selector=" + selector + ", "
84  "smoother=" + smoother + ", "
85  "presweeps=" + std::to_string(presweeps) + ", "
86  "postsweeps=" + std::to_string(postsweeps) + ", "
87  "strength_threshold=" + std::to_string(strength_threshold) + ", "
88  "max_iters=" + std::to_string(max_iters);
89  }
90 };
91 
105 template <class M, class X, class Y>
107 {
108 public:
110  using matrix_type = M;
112  using matrix_field_type = typename M::field_type;
114  using domain_type = X;
116  using range_type = Y;
118  using vector_field_type = typename X::field_type;
119 
120  static constexpr int block_size = 1;
121 
130  AmgxPreconditioner(const M& A, const Opm::PropertyTree prm)
131  : A_(A)
132  , setup_frequency_(prm.get<int>("setup_frequency", 30))
133  , update_counter_(0)
134  {
135  OPM_TIMEBLOCK(prec_construct);
136 
137  // Create configuration
138  AmgxConfig config(prm);
139  cfg_ = AmgxInterface::createConfig(config.toString());
140  rsrc_ = AmgxInterface::createResources(cfg_);
141 
142  // Determine appropriate AMGX mode based on matrix and vector types
143  amgx_mode_ = AmgxInterface::determineAmgxMode<matrix_field_type, vector_field_type>();
144 
145  // Create solver, matrix, and vector handles, given the mode
146  solver_ = AmgxInterface::createSolver(rsrc_, amgx_mode_, cfg_);
147  A_amgx_ = AmgxInterface::createMatrix(rsrc_, amgx_mode_);
148  x_amgx_ = AmgxInterface::createVector(rsrc_, amgx_mode_);
149  b_amgx_ = AmgxInterface::createVector(rsrc_, amgx_mode_);
150 
151  // Initialize matrix structure and values
152  AmgxInterface::initializeMatrix(A_, A_amgx_);
153 
154  // Initialize vectors with proper dimensions
155  const int N = A_.N();
156  AmgxInterface::initializeVector(N, block_size, x_amgx_);
157  AmgxInterface::initializeVector(N, block_size, b_amgx_);
158 
159  // Perform initial update
160  update();
161  }
162 
169  {
173  AmgxInterface::destroyMatrix(A_amgx_, A_);
176  }
177 
186  void pre(X& /*v*/, Y& /*d*/) override
187  {
188  }
189 
200  void apply(X& v, const Y& d) override
201  {
202  OPM_TIMEBLOCK(prec_apply);
203 
204  // Transfer vectors to AMGX
207 
208  // Apply preconditioner
209  AMGX_SAFE_CALL(AMGX_solver_solve(solver_, b_amgx_, x_amgx_));
210 
211  // Transfer result back
213  }
214 
222  void post(X& /*v*/) override
223  {
224  }
225 
231  void update() override
232  {
233  OPM_TIMEBLOCK(prec_update);
234 
236 
237  // Setup or resetup the solver based on update counter
238  if (update_counter_ == 0) {
239  AMGX_SAFE_CALL(AMGX_solver_setup(solver_, A_amgx_));
240  } else {
241  AMGX_SAFE_CALL(AMGX_solver_resetup(solver_, A_amgx_));
242  }
243 
244  ++update_counter_;
245  if (update_counter_ >= setup_frequency_) {
246  update_counter_ = 0;
247  }
248  }
249 
255  Dune::SolverCategory::Category category() const override
256  {
257  return Dune::SolverCategory::sequential;
258  }
259 
265  bool hasPerfectUpdate() const override
266  {
267  // The AMG hierarchy of the Amgx preconditioner can depend on the values of the matrix, so it must be recreated
268  // when the matrix values change, at a given frequency. Since this is handled internally, we return true.
269  return true;
270  }
271 
272 private:
273  // Reference to the input matrix
274  const M& A_;
275 
276  // AMGX handles
277  AMGX_config_handle cfg_ = nullptr;
278  AMGX_resources_handle rsrc_ = nullptr;
279  AMGX_solver_handle solver_ = nullptr;
280  AMGX_matrix_handle A_amgx_ = nullptr;
281  AMGX_vector_handle x_amgx_ = nullptr;
282  AMGX_vector_handle b_amgx_ = nullptr;
283  AMGX_Mode amgx_mode_;
284 
285  // Frequency of updating the AMG hierarchy
286  int setup_frequency_;
287  // Counter for setup updates
288  int update_counter_;
289 };
290 
291 } // namespace Amgx
292 
293 #endif // OPM_AMGX_PRECONDITIONER_HEADER_INCLUDED
static void updateMatrixValues(const MatrixType &matrix, AMGX_matrix_handle amgx_matrix)
Update matrix values in AMGX.
Definition: AmgxInterface.hpp:590
static AMGX_config_handle createConfig(const std::string &config_string)
Create an AMGX config handle from a configuration string.
Definition: AmgxInterface.hpp:153
M matrix_type
The matrix type the preconditioner is for.
Definition: AmgxPreconditioner.hpp:110
typename X::field_type vector_field_type
The field type of the vectors.
Definition: AmgxPreconditioner.hpp:118
Configuration structure for AMGX parameters.
Definition: AmgxPreconditioner.hpp:45
AmgxPreconditioner(const M &A, const Opm::PropertyTree prm)
Constructor for AmgxPreconditioner.
Definition: AmgxPreconditioner.hpp:130
T get(const std::string &key) const
Retrieve property value given hierarchical property key.
Definition: PropertyTree.cpp:59
void apply(X &v, const Y &d) override
Applies the preconditioner to a vector.
Definition: AmgxPreconditioner.hpp:200
static AMGX_matrix_handle createMatrix(AMGX_resources_handle resources, AMGX_Mode mode)
Create an AMGX matrix.
Definition: AmgxInterface.hpp:198
~AmgxPreconditioner()
Destructor for AmgxPreconditioner.
Definition: AmgxPreconditioner.hpp:168
Unified interface for AMGX operations with both CPU and GPU data structures.
Definition: AmgxInterface.hpp:121
Y range_type
The range type of the preconditioner.
Definition: AmgxPreconditioner.hpp:116
static void destroyResources(AMGX_resources_handle resources)
Destroy an AMGX resources handle.
Definition: AmgxInterface.hpp:244
static void transferVectorToAmgx(const VectorType &vec, AMGX_vector_handle amgx_vec)
Transfer vector to AMGX from any vector type (CPU or GPU)
Definition: AmgxInterface.hpp:370
bool hasPerfectUpdate() const override
Checks if the preconditioner has a perfect update.
Definition: AmgxPreconditioner.hpp:265
Definition: AmgxPreconditioner.hpp:35
static void initializeVector(int N, int block_size, AMGX_vector_handle amgx_vector)
Initialize an AMGX vector with zeros.
Definition: AmgxInterface.hpp:574
static AMGX_vector_handle createVector(AMGX_resources_handle resources, AMGX_Mode mode)
Create an AMGX vector.
Definition: AmgxInterface.hpp:213
Interface class adding the update() method to the preconditioner interface.
Definition: PreconditionerWithUpdate.hpp:33
static AMGX_resources_handle createResources(AMGX_config_handle config)
Create AMGX resources from a config.
Definition: AmgxInterface.hpp:167
void post(X &) override
Post-processing step after applying the preconditioner.
Definition: AmgxPreconditioner.hpp:222
Dune::SolverCategory::Category category() const override
Returns the solver category.
Definition: AmgxPreconditioner.hpp:255
static void destroyConfig(AMGX_config_handle config)
Destroy an AMGX config handle.
Definition: AmgxInterface.hpp:226
X domain_type
The domain type of the preconditioner.
Definition: AmgxPreconditioner.hpp:114
static void destroyMatrix(AMGX_matrix_handle amgx_matrix, const MatrixType &matrix)
Destroy an AMGX matrix handle.
Definition: AmgxInterface.hpp:272
typename M::field_type matrix_field_type
The field type of the matrix.
Definition: AmgxPreconditioner.hpp:112
Wrapper for AMGX&#39;s AMG preconditioner.
Definition: AmgxPreconditioner.hpp:106
void pre(X &, Y &) override
Pre-processing step before applying the preconditioner.
Definition: AmgxPreconditioner.hpp:186
static void destroyVector(AMGX_vector_handle vector)
Destroy an AMGX vector handle.
Definition: AmgxInterface.hpp:292
static AMGX_solver_handle createSolver(AMGX_resources_handle resources, AMGX_Mode mode, AMGX_config_handle config)
Create an AMGX solver.
Definition: AmgxInterface.hpp:183
void update() override
Updates the preconditioner with the current matrix values.
Definition: AmgxPreconditioner.hpp:231
Hierarchical collection of key/value pairs.
Definition: PropertyTree.hpp:38
static void initializeMatrix(const MatrixType &matrix, AMGX_matrix_handle amgx_matrix)
Initialize an AMGX matrix from any matrix type (CPU or GPU)
Definition: AmgxInterface.hpp:515
static void destroySolver(AMGX_solver_handle solver)
Destroy an AMGX solver handle.
Definition: AmgxInterface.hpp:257
static void transferVectorFromAmgx(AMGX_vector_handle amgx_vec, VectorType &vec)
Transfer vector from AMGX to any vector type (CPU or GPU)
Definition: AmgxInterface.hpp:396