SystemPreconditioner.hpp
Go to the documentation of this file.
1/*
2 Copyright Equinor ASA 2026
3
4 This file is part of the Open Porous Media project (OPM).
5
6 OPM is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 OPM is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with OPM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef OPM_SYSTEMPRECONDITIONER_HEADER_INCLUDED
20#define OPM_SYSTEMPRECONDITIONER_HEADER_INCLUDED
21
28
29#include <opm/common/ErrorMacros.hpp>
30
31#include <dune/istl/operators.hh>
32#include <dune/istl/paamg/pinfo.hh>
33
34#include <functional>
35#include <memory>
36#include <stdexcept>
37
38namespace Opm {
39
40// Reservoir operator/comm types used as template arguments.
41template<typename Scalar>
42using SeqResOperator = Dune::MatrixAdapter<RRMatrix<Scalar>, ResVector<Scalar>, ResVector<Scalar>>;
43
44#if HAVE_MPI
45using ParResComm = Dune::OwnerOverlapCopyCommunication<int, int>;
46template<typename Scalar>
47using ParResOperator = Dune::OverlappingSchwarzOperator<RRMatrix<Scalar>, ResVector<Scalar>, ResVector<Scalar>, ParResComm>;
48#endif
49
50// Preconditioner for the coupled reservoir-well system.
51//
52// Templated on scalar type, reservoir operator and communication types to
53// unify sequential and parallel implementations. The 3-stage algorithm:
54// 1. Reservoir CPR solve
55// 2. Well solve + reservoir smoothing
56// 3. Final well solve
57//
58// For parallel runs, copyOwnerToAll synchronises overlap DOFs before
59// each reservoir sub-solve.
60template <class Scalar, class ResOp, class ResComm = Dune::Amg::SequentialInformation>
61class SystemPreconditioner : public Dune::PreconditionerWithUpdate<SystemVector<Scalar>, SystemVector<Scalar>>
62{
63public:
64 static constexpr bool isParallel = !std::is_same_v<ResComm, Dune::Amg::SequentialInformation>;
65
67 using WellOperator = Dune::MatrixAdapter<WWMatrix<Scalar>, WellVector<Scalar>, WellVector<Scalar>>;
69
70 static constexpr auto _0 = Dune::Indices::_0;
71 static constexpr auto _1 = Dune::Indices::_1;
72
73 // Sequential constructor (enabled only for non-parallel specializations).
75 const std::function<SystemVector<Scalar>()>& weightsCalculator,
76 int pressureIndex,
77 const Opm::PropertyTree& prm)
78 requires (!isParallel)
79 : S_(S)
80 , pressureIndex_(pressureIndex)
81 {
82 initSubSolvers(prm, weightsCalculator);
83 initWorkVectors();
84 }
85
86 // Parallel constructor (enabled only for parallel specializations).
88 const std::function<SystemVector<Scalar>()>& weightsCalculator,
89 int pressureIndex,
90 const Opm::PropertyTree& prm,
91 const ResComm& resComm)
92 requires (isParallel)
93 : S_(S)
94 , resComm_(&resComm)
95 , pressureIndex_(pressureIndex)
96 {
97 initSubSolvers(prm, weightsCalculator);
98 initWorkVectors();
99 }
100
102 {
103 }
104
106 {
107 }
108
109 Dune::SolverCategory::Category category() const override
110 {
111 if constexpr (isParallel)
112 return Dune::SolverCategory::overlapping;
113 else
114 return Dune::SolverCategory::sequential;
115 }
116
117 void update() override
118 {
119 if (cprwStage_) {
120 weights_ = weightsCalculator_();
121 cprwStage_->update(weights_);
122 } else {
123 resSolver_->preconditioner().update();
124 }
125 resSmoother_->preconditioner().update();
126 wellSolver_->preconditioner().update();
127 }
128
130 {
131 if (cprwStage_) {
132 // The coarse system carries one unknown per well, so a changed
133 // well structure changes its dimension and pattern.
134 weights_ = weightsCalculator_();
135 cprwStage_->buildStructure(weights_);
136 } else {
137 resSolver_->preconditioner().update();
138 }
139 resSmoother_->preconditioner().update();
140 initWellSolver();
141 resizeWellWorkVectors();
142 }
143
144 bool hasPerfectUpdate() const override
145 {
146 return true;
147 }
148
149// System matrix block structure:
150//
151// [ A C ] [ x_res ] [ resRes ]
152// S = [ B D ] [ x_well ] = [ wRes ]
153//
154// A = reservoir-reservoir (top-left)
155// C = reservoir-well coupling (top-right)
156// B = well-reservoir coupling (bottom-left)
157// D = well-well (bottom-right)
159 {
160 // Extract blocks using the agreed convention
161 const auto& A = S_[_0][_0];
162 const auto& C = S_[_0][_1];
163 const auto& B = S_[_1][_0];
164 const auto& D = S_[_1][_1];
165
166 resRes_ = d[_0];
167 wRes_ = d[_1];
168 resSol_ = 0.0;
169 wSol_ = 0.0;
170
171 // Stage 1: pressure solve. Either reservoir-only CPR, or -- with
172 // add_wells -- CPRW on the full system, in which case the stage also
173 // produces a correction for the well unknowns.
174 if (cprwStage_) {
175 dresSol_ = 0.0;
176 dwSol_ = 0.0;
177 tmp_resRes_ = resRes_;
178 syncResVector(tmp_resRes_);
179 cprwStage_->apply(tmp_resRes_, wRes_, weights_, dresSol_, dwSol_);
180 resSol_ += dresSol_;
181 // resRes_ -= A * dresSol_
182 A.mmv(dresSol_, resRes_);
183 // wRes_ -= B * dresSol_
184 B.mmv(dresSol_, wRes_);
185 if (cprwStage_->prolongatesWellPressure()) {
186 wSol_ += dwSol_;
187 // resRes_ -= C * dwSol_ ; wRes_ -= D * dwSol_
188 C.mmv(dwSol_, resRes_);
189 D.mmv(dwSol_, wRes_);
190 }
191 } else {
193 dresSol_ = 0.0;
194 tmp_resRes_ = resRes_;
195 syncResVector(tmp_resRes_);
196 resSolver_->apply(dresSol_, tmp_resRes_, res_result);
197 resSol_ += dresSol_;
198 // resRes_ -= A * dresSol_
199 A.mmv(dresSol_, resRes_);
200 // wRes_ -= B * dresSol_
201 B.mmv(dresSol_, wRes_);
202 }
203
204 // Stage 2: Well solve + reservoir system smoothing
205 {
206 Dune::InverseOperatorResult well_result;
207 dwSol_ = 0.0;
208 tmp_wRes_ = wRes_;
209 wellSolver_->apply(dwSol_, tmp_wRes_, well_result);
210 wSol_ += dwSol_;
211 // resRes_ -= C * dwSol_
212 C.mmv(dwSol_, resRes_);
213 // resRes_ -= D * dwSol_
214 D.mmv(dwSol_, wRes_);
215
217 dresSol_ = 0.0;
218 tmp_resRes_ = resRes_;
219 syncResVector(tmp_resRes_);
220 resSmoother_->apply(dresSol_, tmp_resRes_, res_result);
221 resSol_ += dresSol_;
222 // wRes_ -= B * dresSol_
223 B.mmv(dresSol_, wRes_);
224 }
225
226 // Stage 3: Final well solve
227 {
228 Dune::InverseOperatorResult well_result;
229 dwSol_ = 0.0;
230 tmp_wRes_ = wRes_;
231 wellSolver_->apply(dwSol_, tmp_wRes_, well_result);
232 wSol_ += dwSol_;
233 }
234
235 syncResVector(resSol_);
236 v[_0] = resSol_;
237 v[_1] = wSol_;
238 }
239
240private:
241 const SystemMatrix<Scalar>& S_;
242 const ResComm* resComm_ = nullptr;
243 int pressureIndex_ = 0;
244 static constexpr int dummyWellPressureIndex = std::numeric_limits<int>::min();
245 Opm::PropertyTree wellprm_;
246
247 std::unique_ptr<ResOp> rop_;
248 std::unique_ptr<WellOperator> wop_;
249 std::unique_ptr<ResFlexibleSolverType> resSolver_;
250 std::unique_ptr<ResFlexibleSolverType> resSmoother_;
251 std::unique_ptr<WellFlexibleSolverType> wellSolver_;
252
253 // Non-null when the pressure stage includes the well unknowns (CPRW).
254 // Then resSolver_ is not built and stage 1 goes through cprwStage_.
256 std::unique_ptr<CprwStage> cprwStage_;
257 std::function<SystemVector<Scalar>()> weightsCalculator_;
258 SystemVector<Scalar> weights_;
259
260 WellVector<Scalar> wSol_;
261 ResVector<Scalar> resSol_;
262 ResVector<Scalar> dresSol_;
263 WellVector<Scalar> dwSol_;
264 ResVector<Scalar> tmp_resRes_;
265 WellVector<Scalar> tmp_wRes_;
266 ResVector<Scalar> resRes_;
267 WellVector<Scalar> wRes_;
268
269 void syncResVector(ResVector<Scalar>& v)
270 {
271 if constexpr (isParallel) {
272 resComm_->copyOwnerToAll(v, v);
273 }
274 }
275
276 void initWellSolver()
277 {
278 wop_ = std::make_unique<WellOperator>(S_[_1][_1]);
279 std::function<WellVector<Scalar>()> weightsCalculatorWell;
280 wellSolver_ = std::make_unique<WellFlexibleSolverType>(
281 *wop_, wellprm_, weightsCalculatorWell, dummyWellPressureIndex);
282 }
283
284 void initSubSolvers(const Opm::PropertyTree& prm,
285 const std::function<SystemVector<Scalar>()>& weightsCalculator)
286 {
287 auto resprm = prm.get_child("reservoir_solver");
288 auto resprmsmoother = prm.get_child("reservoir_smoother");
289 wellprm_ = prm.get_child("well_solver");
290
291 // add_wells is the same switch the classic CPR/CPRW pair uses: it
292 // promotes the pressure stage from reservoir-only CPR to CPRW over the
293 // full (reservoir, well) system.
294 const bool addWells = resprm.get("preconditioner.add_wells", false);
295
296 // The weights arrive from the outer layer for the whole system; the
297 // reservoir-only sub-solvers want just their own part of them.
298 std::function<ResVector<Scalar>()> resWeightCalc;
299 if (weightsCalculator) {
300 resWeightCalc = [weightsCalculator]() {
301 return weightsCalculator()[_0];
302 };
303 }
304
305 if constexpr (isParallel) {
306 rop_ = std::make_unique<ResOp>(S_[_0][_0], *resComm_);
307 resSmoother_ = std::make_unique<ResFlexibleSolverType>(
308 *rop_, *resComm_, resprmsmoother, resWeightCalc, pressureIndex_);
309 } else {
310 rop_ = std::make_unique<ResOp>(S_[_0][_0]);
311 resSmoother_ = std::make_unique<ResFlexibleSolverType>(
312 *rop_, resprmsmoother, resWeightCalc, pressureIndex_);
313 }
314
315 if (addWells) {
316 if (!weightsCalculator) {
317 OPM_THROW(std::invalid_argument,
318 "The CPRW pressure stage (add_wells) needs a weights calculator, but "
319 "none was configured. Set reservoir_solver.preconditioner.weight_type.");
320 }
321 weightsCalculator_ = weightsCalculator;
322 weights_ = weightsCalculator_();
323
324 auto coarseprm = resprm.get_child_optional("preconditioner.coarsesolver")
325 ? resprm.get_child("preconditioner.coarsesolver")
326 : PropertyTree();
327 const auto wellTransfer = wellTransferFromString(
328 // Same default as setupPropertyTree ships, so a JSON that omits
329 // the key gets the same preconditioner as the built-in setup.
330 prm.get("well_transfer", std::string{"classic"}));
331 const auto diagonal = wellCoarseDiagonalFromString(
332 prm.get("well_coarse_diagonal", std::string{"contract_d"}));
333 cprwStage_ = std::make_unique<CprwStage>(S_, coarseprm, pressureIndex_,
334 wellTransfer, resComm_, diagonal,
335 prm.get("verbosity", 0));
336 cprwStage_->buildStructure(weights_);
337 } else {
338 if constexpr (isParallel) {
339 resSolver_ = std::make_unique<ResFlexibleSolverType>(
340 *rop_, *resComm_, resprm, resWeightCalc, pressureIndex_);
341 } else {
342 resSolver_ = std::make_unique<ResFlexibleSolverType>(
343 *rop_, resprm, resWeightCalc, pressureIndex_);
344 }
345 }
346
347 initWellSolver();
348 }
349
350 void initWorkVectors()
351 {
352 resizeReservoirWorkVectors();
353 resizeWellWorkVectors();
354 }
355
356 void resizeReservoirWorkVectors()
357 {
358 const auto numRes = S_[_0][_0].N();
359 resSol_.resize(numRes);
360 dresSol_.resize(numRes);
361 tmp_resRes_.resize(numRes);
362 resRes_.resize(numRes);
363 }
364
365 void resizeWellWorkVectors()
366 {
367 const auto numWell = S_[_1][_1].N();
368 wSol_.resize(numWell);
369 dwSol_.resize(numWell);
370 tmp_wRes_.resize(numWell);
371 wRes_.resize(numWell);
372 }
373};
374
375} // namespace Opm
376
377#endif // OPM_SYSTEMPRECONDITIONER_HEADER_INCLUDED
Definition: FlexibleSolver.hpp:45
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
PropertyTree get_child(const std::string &key) const
Definition: SystemCprwPressureStage.hpp:131
Definition: SystemTypes.hpp:166
static constexpr size_type N()
Definition: SystemTypes.hpp:171
Definition: SystemPreconditioner.hpp:62
bool hasPerfectUpdate() const override
Definition: SystemPreconditioner.hpp:144
void post(SystemVector< Scalar > &) override
Definition: SystemPreconditioner.hpp:105
void pre(SystemVector< Scalar > &, SystemVector< Scalar > &) override
Definition: SystemPreconditioner.hpp:101
Dune::SolverCategory::Category category() const override
Definition: SystemPreconditioner.hpp:109
void updateForChangedWellStructure()
Definition: SystemPreconditioner.hpp:129
static constexpr auto _0
Definition: SystemPreconditioner.hpp:70
static constexpr auto _1
Definition: SystemPreconditioner.hpp:71
SystemPreconditioner(const SystemMatrix< Scalar > &S, const std::function< SystemVector< Scalar >()> &weightsCalculator, int pressureIndex, const Opm::PropertyTree &prm, const ResComm &resComm)
Definition: SystemPreconditioner.hpp:87
Dune::MatrixAdapter< WWMatrix< Scalar >, WellVector< Scalar >, WellVector< Scalar > > WellOperator
Definition: SystemPreconditioner.hpp:67
static constexpr bool isParallel
Definition: SystemPreconditioner.hpp:64
SystemPreconditioner(const SystemMatrix< Scalar > &S, const std::function< SystemVector< Scalar >()> &weightsCalculator, int pressureIndex, const Opm::PropertyTree &prm)
Definition: SystemPreconditioner.hpp:74
void update() override
Definition: SystemPreconditioner.hpp:117
void apply(SystemVector< Scalar > &v, const SystemVector< Scalar > &d) override
Definition: SystemPreconditioner.hpp:158
Definition: blackoilbioeffectsmodules.hh:45
Dune::MultiTypeBlockVector< ResVector< Scalar >, WellVector< Scalar > > SystemVector
Definition: SystemTypes.hpp:65
Dune::InverseOperatorResult InverseOperatorResult
Definition: GpuBridge.hpp:32
WellCoarseDiagonal wellCoarseDiagonalFromString(const std::string &name)
Definition: SystemCprwPressureStage.hpp:102
Dune::OverlappingSchwarzOperator< RRMatrix< Scalar >, ResVector< Scalar >, ResVector< Scalar >, ParResComm > ParResOperator
Definition: SystemPreconditioner.hpp:47
Dune::OwnerOverlapCopyCommunication< int, int > ParResComm
Definition: SystemPreconditioner.hpp:45
Dune::MatrixAdapter< RRMatrix< Scalar >, ResVector< Scalar >, ResVector< Scalar > > SeqResOperator
Definition: SystemPreconditioner.hpp:42
Dune::BlockVector< Dune::FieldVector< Scalar, numResDofs > > ResVector
Definition: SystemTypes.hpp:61
WellTransfer wellTransferFromString(const std::string &name)
Definition: SystemCprwPressureStage.hpp:112
Dune::BlockVector< Dune::FieldVector< Scalar, numWellDofs > > WellVector
Definition: SystemTypes.hpp:63