StandardPreconditioners_serial.hpp
Go to the documentation of this file.
1/*
2 Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
3 Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_STANDARDPRECONDITIONERS_SERIAL_HPP
22#define OPM_STANDARDPRECONDITIONERS_SERIAL_HPP
23
24#if HAVE_CUDA
25#if USE_HIP
26#include <opm/simulators/linalg/gpuistl_hip/PreconditionerCPUMatrixToGPUMatrix.hpp>
27#else
29#endif
30#endif
31
32#include <functional>
33#include <memory>
34#include <type_traits>
35
36#if HAVE_AVX2_EXTENSION
38#endif
39
40namespace Opm {
41
42template <class X, class Y>
44{
45 public:
47 void update() override {};
48 bool hasPerfectUpdate() const override {return true;}
49 void pre ([[maybe_unused]] X& x, [[maybe_unused]] Y& y) override {};
50 void post ([[maybe_unused]] X& x) override {};
51 void apply ([[maybe_unused]] X& x, [[maybe_unused]] const Y& y) override {};
52 Dune::SolverCategory::Category category() const override { return Dune::SolverCategory::sequential; };
53};
54
55
56template <class Operator>
57struct StandardPreconditioners<Operator, Dune::Amg::SequentialInformation, typename std::enable_if_t<!Opm::is_gpu_operator_v<Operator>>>
58{
59 static void add()
60 {
61 using namespace Dune;
62 using O = Operator;
63 using C = Dune::Amg::SequentialInformation;
65 using M = typename F::Matrix;
66 using V = typename F::Vector;
67 using P = PropertyTree;
68 F::addCreator("ilu0", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
69 const double w = prm.get<double>("relaxation", 1.0);
70 return std::make_shared<ParallelOverlappingILU0<M, V, V, C>>(
71 op.getmat(), 0, w, MILU_VARIANT::ILU);
72 });
73 F::addCreator("duneilu", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
74 const double w = prm.get<double>("relaxation", 1.0);
75 const int n = prm.get<int>("ilulevel", 0);
76 const bool resort = prm.get<bool>("resort", false);
77 return getRebuildOnUpdateWrapper<Dune::SeqILU<M, V, V>>(std::cref(op.getmat()), n, w, resort);
78 });
79 F::addCreator("paroverilu0", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
80 const double w = prm.get<double>("relaxation", 1.0);
81 const int n = prm.get<int>("ilulevel", 0);
82 return std::make_shared<ParallelOverlappingILU0<M, V, V, C>>(
83 op.getmat(), n, w, MILU_VARIANT::ILU);
84 });
85 F::addCreator("ilun", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
86 const int n = prm.get<int>("ilulevel", 0);
87 const double w = prm.get<double>("relaxation", 1.0);
88 return std::make_shared<ParallelOverlappingILU0<M, V, V, C>>(
89 op.getmat(), n, w, MILU_VARIANT::ILU);
90 });
91 F::addCreator("dilu", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
92 DUNE_UNUSED_PARAMETER(prm);
93 return std::make_shared<MultithreadDILU<M, V, V>>(op.getmat());
94 });
95#if HAVE_AVX2_EXTENSION
96 F::addCreator("mixed-ilu0", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
97 DUNE_UNUSED_PARAMETER(prm);
98 if constexpr (std::is_same_v<typename V::field_type, float>) {
99 OPM_THROW(std::logic_error, "mixed-ilu0 is not available for floats");
100 return nullptr;
101 } else {
102 return std::make_shared<MixedPreconditioner<M,V,V>>(op.getmat());
103 }
104 });
105 F::addCreator("mixed-dilu", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
106 DUNE_UNUSED_PARAMETER(prm);
107 if constexpr (std::is_same_v<typename V::field_type, float>) {
108 OPM_THROW(std::logic_error, "mixed-dilu is not available for floats");
109 return nullptr;
110 } else {
111 return std::make_shared<MixedPreconditioner<M,V,V>>(op.getmat(),true);
112 }
113 });
114#endif
115 F::addCreator("legacy-mixed-ilu0", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
116 DUNE_UNUSED_PARAMETER(prm);
117 DUNE_UNUSED_PARAMETER(op);
118 return std::make_shared<TrivialPreconditioner<V,V>>();
119 });
120 F::addCreator("legacy-mixed-dilu", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
121 DUNE_UNUSED_PARAMETER(prm);
122 DUNE_UNUSED_PARAMETER(op);
123 return std::make_shared<TrivialPreconditioner<V,V>>();
124 });
125 F::addCreator("jac", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
126 const int n = prm.get<int>("repeats", 1);
127 const double w = prm.get<double>("relaxation", 1.0);
128 return getDummyUpdateWrapper<SeqJac<M, V, V>>(op.getmat(), n, w);
129 });
130 F::addCreator("gs", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
131 const int n = prm.get<int>("repeats", 1);
132 const double w = prm.get<double>("relaxation", 1.0);
133 return getDummyUpdateWrapper<SeqGS<M, V, V>>(op.getmat(), n, w);
134 });
135 F::addCreator("sor", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
136 const int n = prm.get<int>("repeats", 1);
137 const double w = prm.get<double>("relaxation", 1.0);
138 return getDummyUpdateWrapper<SeqSOR<M, V, V>>(op.getmat(), n, w);
139 });
140 F::addCreator("ssor", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
141 const int n = prm.get<int>("repeats", 1);
142 const double w = prm.get<double>("relaxation", 1.0);
143 return getDummyUpdateWrapper<SeqSSOR<M, V, V>>(op.getmat(), n, w);
144 });
145
146 // Only add AMG preconditioners to the factory if the operator
147 // is an actual matrix operator.
148 if constexpr (std::is_same_v<O, Dune::MatrixAdapter<M, V, V>>) {
149 F::addCreator("amg", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
150 std::string smoother = prm.get<std::string>("smoother", "paroverilu0");
151 // Make the smoother type lowercase for internal canonical representation
152 std::ranges::transform(smoother, smoother.begin(), ::tolower);
153 if (smoother == "ilu0" || smoother == "paroverilu0") {
154 using Smoother = SeqILU<M, V, V>;
155 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm);
156 } else if (smoother == "jac") {
157 using Smoother = SeqJac<M, V, V>;
158 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm);
159 } else if (smoother == "gs") {
160 using Smoother = SeqGS<M, V, V>;
161 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm);
162 } else if (smoother == "dilu") {
163 using Smoother = MultithreadDILU<M, V, V>;
164 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm);
165 } else if (smoother == "sor") {
166 using Smoother = SeqSOR<M, V, V>;
167 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm);
168 } else if (smoother == "ssor") {
169 using Smoother = SeqSSOR<M, V, V>;
170 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm);
171 } else if (smoother == "ilun") {
172 using Smoother = SeqILU<M, V, V>;
173 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm);
174 } else {
175 OPM_THROW(std::invalid_argument, "Properties: No smoother with name " + smoother + ".");
176 }
177 });
178 F::addCreator("kamg", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
179 std::string smoother = prm.get<std::string>("smoother", "paroverilu0");
180 // Make the smoother type lowercase for internal canonical representation
181 std::ranges::transform(smoother, smoother.begin(), ::tolower);
182 if (smoother == "ilu0" || smoother == "paroverilu0") {
183 using Smoother = SeqILU<M, V, V>;
184 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm, true);
185 } else if (smoother == "jac") {
186 using Smoother = SeqJac<M, V, V>;
187 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm, true);
188 } else if (smoother == "sor") {
189 using Smoother = SeqSOR<M, V, V>;
190 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm, true);
191 } else if (smoother == "gs") {
192 using Smoother = SeqGS<M, V, V>;
193 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm, true);
194 } else if (smoother == "ssor") {
195 using Smoother = SeqSSOR<M, V, V>;
196 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm, true);
197 } else if (smoother == "ilun") {
198 using Smoother = SeqILU<M, V, V>;
199 return AMGHelper<O, C, M, V>::template makeAmgPreconditioner<Smoother>(op, prm, true);
200 } else {
201 OPM_THROW(std::invalid_argument, "Properties: No smoother with name " + smoother + ".");
202 }
203 });
204 F::addCreator("famg", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
205 if constexpr (std::is_same_v<typename V::field_type, float>) {
206 OPM_THROW(std::logic_error, "famg requires UMFPack which is not available for floats");
207 return nullptr;
208 } else {
209 auto crit = AMGHelper<O, C, M, V>::criterion(prm);
210 Dune::Amg::Parameters parms;
211 parms.setNoPreSmoothSteps(1);
212 parms.setNoPostSmoothSteps(1);
213 return getRebuildOnUpdateWrapper<Dune::Amg::FastAMG<O, V>>(op, crit, parms);
214 }
215 });
216
217#if HAVE_AMGX
218 // Only add AMGX for scalar matrices
219 if constexpr (M::block_type::rows == 1 && M::block_type::cols == 1) {
220 F::addCreator("amgx", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
221 auto prm_copy = prm;
222 prm_copy.put("setup_frequency", Opm::Parameters::Get<Opm::Parameters::CprReuseInterval>());
223 return std::make_shared<Amgx::AmgxPreconditioner<M, V, V>>(op.getmat(), prm_copy);
224 });
225 }
226#endif
227
228#if HAVE_HYPRE
229 // Only add Hypre for scalar matrices
230 if constexpr (M::block_type::rows == 1 && M::block_type::cols == 1 &&
231 std::is_same_v<HYPRE_Real, typename V::field_type>) {
232 F::addCreator("hypre", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
233 return std::make_shared<linalg::HyprePreconditioner<M, V, V, Dune::Amg::SequentialInformation>>(op.getmat(), prm, Dune::Amg::SequentialInformation());
234 });
235 }
236#endif
237 }
238
239 // Add CPRW only for the WellModelMatrixAdapter, as the method requires that the operator
240 // has the addWellPressureEquations() method (and a few more) it can not be combined with
241 // a well-less operator such as Dune::MatrixAdapter. For OPM Flow this corresponds to
242 // requiring --matrix-add-well-contributions=false (which is the default).
243 if constexpr (std::is_same_v<O, WellModelMatrixAdapter<M, V, V>>) {
244 F::addCreator(
245 "cprw",
246 [](const O& op, const P& prm, const std::function<V()>& weightsCalculator, std::size_t pressureIndex) {
247 if (pressureIndex == std::numeric_limits<std::size_t>::max()) {
248 OPM_THROW(std::logic_error, "Pressure index out of bounds. It needs to specified for CPR");
249 }
250 using Scalar = typename V::field_type;
251 using LevelTransferPolicy
253 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy>>(
254 op, prm, weightsCalculator, pressureIndex);
255 });
256 }
257
258 F::addCreator(
259 "cpr",
260 [](const O& op, const P& prm, const std::function<V()>& weightsCalculator, std::size_t pressureIndex) {
261 if (pressureIndex == std::numeric_limits<std::size_t>::max()) {
262 OPM_THROW(std::logic_error, "Pressure index out of bounds. It needs to specified for CPR");
263 }
264 using Scalar = typename V::field_type;
266 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy>>(
267 op, prm, weightsCalculator, pressureIndex);
268 });
269 F::addCreator(
270 "cprt",
271 [](const O& op, const P& prm, const std::function<V()>& weightsCalculator, std::size_t pressureIndex) {
272 if (pressureIndex == std::numeric_limits<std::size_t>::max()) {
273 OPM_THROW(std::logic_error, "Pressure index out of bounds. It needs to specified for CPR");
274 }
275 using Scalar = typename V::field_type;
277 return std::make_shared<OwningTwoLevelPreconditioner<O, V, LevelTransferPolicy>>(
278 op, prm, weightsCalculator, pressureIndex);
279 });
280
281#if HAVE_CUDA
282 // Here we create the *wrapped* GPU preconditioners
283 // meaning they will act as CPU preconditioners on the outside,
284 // but copy data back and forth to the GPU as needed.
285
286 // TODO: Make this use the GPU preconditioner factory once that is up and running.
287 F::addCreator("gpuilu0", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
288 const double w = prm.get<double>("relaxation", 1.0);
289 using field_type = typename V::field_type;
290 using GpuILU0 = typename gpuistl::
291 GpuSeqILU0<M, gpuistl::GpuVector<field_type>, gpuistl::GpuVector<field_type>>;
292 return std::make_shared<gpuistl::PreconditionerAdapter<V, V, GpuILU0>>(
293 std::make_shared<GpuILU0>(op.getmat(), w));
294 });
295
296 F::addCreator("gpuilu0float", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
297 const double w = prm.get<double>("relaxation", 1.0);
298 using block_type = typename V::block_type;
299 using VTo = Dune::BlockVector<Dune::FieldVector<float, block_type::dimension>>;
300 using matrix_type_to =
301 typename Dune::BCRSMatrix<Dune::FieldMatrix<float, block_type::dimension, block_type::dimension>>;
302 using GpuILU0 = typename gpuistl::
303 GpuSeqILU0<matrix_type_to, gpuistl::GpuVector<float>, gpuistl::GpuVector<float>>;
306 auto converted = std::make_shared<Converter>(op.getmat());
307 auto adapted = std::make_shared<Adapter>(std::make_shared<GpuILU0>(converted->getConvertedMatrix(), w));
308 converted->setUnderlyingPreconditioner(adapted);
309 return converted;
310 });
311
312 F::addCreator("gpujac", [](const O& op, const P& prm, const std::function<V()>&, std::size_t) {
313 const double w = prm.get<double>("relaxation", 1.0);
314 using field_type = typename V::field_type;
315 using GPUJac =
318
321 return std::make_shared<gpuistl::PreconditionerAdapter<V, V, MatrixOwner>>(
322 std::make_shared<MatrixOwner>(op.getmat(), w));
323 });
324
325 F::addCreator("opmgpuilu0", [](const O& op, [[maybe_unused]] const P& prm, const std::function<V()>&, std::size_t) {
326 const bool split_matrix = prm.get<bool>("split_matrix", true);
327 const bool tune_gpu_kernels = prm.get<bool>("tune_gpu_kernels", true);
328 const int mixed_precision_scheme = prm.get<int>("mixed_precision_scheme", 0);
329
330 using field_type = typename V::field_type;
334 return std::make_shared<gpuistl::PreconditionerAdapter<V, V, MatrixOwner>>(
335 // Note: op.getmat() is passed twice, because the ILU0 needs both the CPU and GPU matrix.
336 // The first argument will be converted to a GPU matrix, and the second one is used as a CPU matrix.
337 std::make_shared<MatrixOwner>(op.getmat(), op.getmat(), split_matrix, tune_gpu_kernels, mixed_precision_scheme));
338 });
339
340
341 F::addCreator("gpudilu", [](const O& op, [[maybe_unused]] const P& prm, const std::function<V()>&, std::size_t) {
342 const bool split_matrix = prm.get<bool>("split_matrix", true);
343 const bool tune_gpu_kernels = prm.get<bool>("tune_gpu_kernels", true);
344 const int mixed_precision_scheme = prm.get<int>("mixed_precision_scheme", 0);
345 const bool reorder = prm.get<bool>("reorder", true);
346 using field_type = typename V::field_type;
350 return std::make_shared<gpuistl::PreconditionerAdapter<V, V, MatrixOwner>>(
351 // Note: op.getmat() is passed twice, because the DILU needs both the CPU and GPU matrix.
352 // The first argument will be converted to a GPU matrix, and the second one is used as a CPU matrix.
353 std::make_shared<MatrixOwner>(op.getmat(), op.getmat(), split_matrix, tune_gpu_kernels, mixed_precision_scheme, reorder));
354 });
355
356 F::addCreator("gpudilufloat", [](const O& op, [[maybe_unused]] const P& prm, const std::function<V()>&, std::size_t) {
357 const bool split_matrix = prm.get<bool>("split_matrix", true);
358 const bool tune_gpu_kernels = prm.get<bool>("tune_gpu_kernels", true);
359 const int mixed_precision_scheme = prm.get<int>("mixed_precision_scheme", 0);
360 const bool reorder = prm.get<bool>("reorder", true);
361
362 using block_type = typename V::block_type;
363 using VTo = Dune::BlockVector<Dune::FieldVector<float, block_type::dimension>>;
364 using matrix_type_to = typename Dune::BCRSMatrix<Dune::FieldMatrix<float, block_type::dimension, block_type::dimension>>;
367 gpuistl::GpuVector<float>, GpuDILU, matrix_type_to>;
370
371
372 auto converted = std::make_shared<Converter>(op.getmat());
373 // Note: converted->getConvertedMatrix() is passed twice, because the DILU needs both the CPU and GPU matrix.
374 // The first argument will be converted to a GPU matrix, and the second one is used as a CPU matrix.
375 auto adapted = std::make_shared<Adapter>(std::make_shared<MatrixOwner>(
376 converted->getConvertedMatrix(), converted->getConvertedMatrix(),
377 split_matrix, tune_gpu_kernels, mixed_precision_scheme, reorder));
378 converted->setUnderlyingPreconditioner(adapted);
379 return converted;
380 });
381#endif // HAVE_CUDA
382 }
383};
384
385
386} // namespace Opm
387
388
389#endif // OPM_STANDARDPRECONDITIONERS_SERIAL_HPP
The OpenMP thread parallelized DILU preconditioner.
Definition: DILU.hpp:53
Interface class adding the update() method to the preconditioner interface.
Definition: PreconditionerWithUpdate.hpp:34
Definition: PreconditionerFactory.hpp:64
Definition: PressureBhpTransferPolicy.hpp:99
Definition: PressureTransferPolicy.hpp:55
Hierarchical collection of key/value pairs.
Definition: PropertyTree.hpp:39
Definition: StandardPreconditioners_serial.hpp:44
void update() override
Definition: StandardPreconditioners_serial.hpp:47
void apply(X &x, const Y &y) override
Definition: StandardPreconditioners_serial.hpp:51
Dune::SolverCategory::Category category() const override
Definition: StandardPreconditioners_serial.hpp:52
void post(X &x) override
Definition: StandardPreconditioners_serial.hpp:50
void pre(X &x, Y &y) override
Definition: StandardPreconditioners_serial.hpp:49
bool hasPerfectUpdate() const override
Definition: StandardPreconditioners_serial.hpp:48
TrivialPreconditioner()
Definition: StandardPreconditioners_serial.hpp:46
DILU preconditioner on the GPU.
Definition: GpuDILU.hpp:53
Jacobi preconditioner on the GPU.
Definition: GpuJac.hpp:47
ILU0 preconditioner on the GPU.
Definition: OpmGpuILU0.hpp:51
Makes a CUDA preconditioner available to a CPU simulator.
Definition: PreconditionerAdapter.hpp:43
Convert a CPU matrix to a GPU matrix and use a CUDA preconditioner on the GPU.
Definition: PreconditionerCPUMatrixToGPUMatrix.hpp:42
Converts the field type (eg. double to float) to benchmark single precision preconditioners.
Definition: PreconditionerConvertFieldTypeAdapter.hpp:86
Definition: fvbaseprimaryvariables.hh:161
Definition: blackoilbioeffectsmodules.hh:45
@ ILU
Do not perform modified ILU.
Definition: PreconditionerFactory.hpp:43
static Criterion criterion(const PropertyTree &prm)
Definition: StandardPreconditioners_mpi.hpp:94
Definition: StandardPreconditioners_mpi.hpp:139