ISTLSolverSystem.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_ISTLSOLVERSYSTEM_HEADER_INCLUDED
20#define OPM_ISTLSOLVERSYSTEM_HEADER_INCLUDED
21
26
29
30#include <opm/common/ErrorMacros.hpp>
31
32#include <dune/common/fmatrix.hh>
33#include <dune/common/fvector.hh>
34
35#include <algorithm>
36#include <cassert>
37#include <cmath>
38#include <cstddef>
39#include <stdexcept>
40#include <string>
41#include <vector>
42
43namespace Opm
44{
45
46template <class TypeTag>
47class ISTLSolverSystem : public ISTLSolver<TypeTag>
48{
49protected:
53 using Matrix = typename SparseMatrixAdapter::IstlMatrix;
56
57 // Compile-time validation: SystemPreconditionerFactory and related types
58 // are hardcoded for standard 3-phase blackoil (3 reservoir equations, 4 well equations).
59 // See SystemTypes.hpp for details.
60 static_assert(Indices::numEq == 3,
61 "ISTLSolverSystem (with system_cpr preconditioner) only supports "
62 "3-equation blackoil models. This model has different equation count.");
63
64 constexpr static std::size_t pressureIndex
65 = Indices::pressureSwitchIdx;
66
67 enum { enablePolymerMolarWeight = getPropValue<TypeTag, Properties::EnablePolymerMW>() };
69
70#if HAVE_MPI
71 using CommunicationType = Dune::OwnerOverlapCopyCommunication<int, int>;
72#else
73 using CommunicationType = Dune::Communication<int>;
74#endif
76
77 static constexpr auto _0 = Dune::Indices::_0;
78 static constexpr auto _1 = Dune::Indices::_1;
79
80public:
81 ISTLSolverSystem(const Simulator& simulator,
82 const FlowLinearSolverParameters& parameters,
83 bool forceSerial = false)
84 : Parent(simulator, parameters, forceSerial)
85 {
86 }
87
88 explicit ISTLSolverSystem(const Simulator& simulator)
89 : Parent(simulator)
90 {
91 }
92
93 void prepare(const SparseMatrixAdapter& M, Vector& b) override
94 {
95 OPM_TIMEBLOCK(istlSolverPrepare);
96 this->initPrepare(M.istlMatrix(), b);
97 prepareSystemSolver();
98 }
99
100 void prepare(const Matrix& M, Vector& b) override
101 {
102 OPM_TIMEBLOCK(istlSolverPrepare);
103 this->initPrepare(M, b);
104 prepareSystemSolver();
105 }
106
107 bool solve(Vector& x) override
108 {
109 OPM_TIMEBLOCK(istlSolverSolve);
110 ++this->solveCount_;
111
112 // Same fine-system dump as ISTLSolver::solve(), which this overrides,
113 // so the reservoir matrix and rhs can be diffed against the classic path.
114 if (this->prm_[this->activeSolverNum_].get("verbosity", 0) > 10) {
116 this->getMatrix(),
118 this->comm_.get());
119 }
120
121 const std::size_t numRes = Parent::matrix_->N();
122 const std::size_t numWell = cachedWellStructure_.totalWellBlocks;
123
124 sysX_[_0].resize(numRes);
125 sysX_[_0] = 0.0;
126 sysX_[_1].resize(numWell);
127 sysX_[_1] = 0.0;
128
129 sysRhs_[_0].resize(numRes);
130 sysRhs_[_0] = *Parent::rhs_;
131 sysRhs_[_1].resize(numWell);
132 sysRhs_[_1] = 0.0;
133
135 sysSolver_->apply(sysX_, sysRhs_, result);
136 this->iterations_ = result.iterations;
137
138 x = sysX_[_0];
139
140 return this->checkConvergence(result);
141 }
142
143private:
144 bool sysInitialized_ = false;
145 WellMatrixStructure cachedWellStructure_;
146
147 // Aggregation of merged well block rows into wells, and the well weights
148 // used by the CPRW pressure stage. Both are produced here, in the outer
149 // layer, from data already extracted from the well model; the
150 // preconditioner consumes them as plain numbers.
151 WellDofLayout wellLayout_;
152 std::string wellWeightType_ = "quasiimpes";
153
154 // Current per-well B/C/D blocks for the explicit 2x2 system matrix.
155 std::vector<WRMatrix<Scalar>> wellBMatrices_;
156 std::vector<RWMatrix<Scalar>> wellCMatrices_;
157 std::vector<WWMatrix<Scalar>> wellDMatrices_;
158 Opm::SparseTable<int> wellCells_;
159
160 // Owned storage for merged well matrices; SystemMatrix points into these.
161 WRMatrix<Scalar> mergedB_;
162 RWMatrix<Scalar> mergedC_;
163 WWMatrix<Scalar> mergedD_;
164
165 SystemMatrix<Scalar> sysMatrix_;
167 SystemVector<Scalar> sysRhs_;
168
169 // Serial solver components
170 std::unique_ptr<SystemSeqOp<Scalar>> sysOp_;
171 std::unique_ptr<Dune::FlexibleSolver<SystemSeqOp<Scalar>>> sysFlexSolverSeq_;
172
173 // Parallel solver components
174#if HAVE_MPI
175 using WellComm = Dune::JacComm;
176 std::unique_ptr<WellComm> wellComm_;
177 std::unique_ptr<SystemComm> systemComm_;
178 std::unique_ptr<SystemParOp<Scalar>> sysOpPar_;
179 std::unique_ptr<Dune::FlexibleSolver<SystemParOp<Scalar>>> sysFlexSolverPar_;
180#endif
181
182 using SysSolverType = Dune::InverseOperator<SystemVector<Scalar>, SystemVector<Scalar>>;
185#if HAVE_MPI
187#endif
188 SysSolverType* sysSolver_ = nullptr;
189 SysPrecondType* sysPrecond_ = nullptr;
190
191 void prepareSystemSolver()
192 {
193 OPM_TIMEBLOCK(flexibleSolverPrepare);
194
195 // Read before buildWellDofLayout(), which consults
196 // wellLayout_.identityOnPressureControl to decide whether to
197 // populate wellLayout_.pressureControlled: reading it afterwards
198 // would leave that flag one call stale.
199 const auto& prm = this->prm_[this->activeSolverNum_];
200 wellWeightType_ = prm.get("preconditioner.well_weight_type", std::string{"cellavg"});
201 if (wellWeightType_ != "unit" && wellWeightType_ != "cellavg"
202 && wellWeightType_ != "cellblockavg" && wellWeightType_ != "quasiimpes") {
203 OPM_THROW(std::invalid_argument,
204 "Unknown preconditioner.well_weight_type '" + wellWeightType_
205 + "'. Valid values are 'unit', 'cellavg', "
206 "'cellblockavg' and 'quasiimpes'.");
207 }
208 // Give a pressure-controlled well a trivial coarse equation, as the
209 // classic CPRW does. Off keeps the contracted equation for every well.
210 wellLayout_.identityOnPressureControl
211 = prm.get("preconditioner.well_identity_on_pressure_control", false);
212
213 wellBMatrices_.clear();
214 wellCMatrices_.clear();
215 wellDMatrices_.clear();
216 wellCells_.clear();
217
218 this->simulator_.problem().wellModel().addBCDMatrix(
219 wellBMatrices_, wellCMatrices_, wellDMatrices_, wellCells_);
220
221 buildWellDofLayout();
222
224 Parent::matrix_->N(), wellBMatrices_, wellCMatrices_, wellDMatrices_, wellCells_);
225
226 const bool localStructureChanged = !sysInitialized_
227 || !merger.hasSameStructure(cachedWellStructure_);
228
229 // All ranks must agree on whether to take the structure-change path,
230 // because the distributed solver create and update paths use different
231 // MPI-collective sequences.
232#if HAVE_MPI
233 const bool globalStructureChanged = this->comm_->communicator().max(
234 static_cast<int>(localStructureChanged)) > 0;
235#else
236 const bool globalStructureChanged = localStructureChanged;
237#endif
238 const bool needStructureRefresh = !sysInitialized_ || globalStructureChanged;
239
240 if (needStructureRefresh) {
241 OPM_TIMEBLOCK(flexibleSolverCreate);
242 merger.buildMatrices(mergedB_, mergedC_, mergedD_);
243 sysMatrix_.A = Parent::matrix_;
244 sysMatrix_.B = &mergedB_;
245 sysMatrix_.C = &mergedC_;
246 sysMatrix_.D = &mergedD_;
247 sysMatrix_.wellLayout = &wellLayout_;
248 cachedWellStructure_ = merger.buildStructure();
249
250 refreshSystemSolverForChangedWellStructure(prm);
251 sysInitialized_ = true;
252 } else {
253 OPM_TIMEBLOCK(flexibleSolverUpdate);
254 // Pattern unchanged: write fresh values into the existing merged
255 // matrices without any (de)allocation.
256 merger.updateValues(mergedB_, mergedC_, mergedD_);
257
258 // Refresh A pointer in case the reservoir matrix was reallocated.
259 sysMatrix_.A = Parent::matrix_;
260 sysMatrix_.B = &mergedB_;
261 sysMatrix_.C = &mergedC_;
262 sysMatrix_.D = &mergedD_;
263 sysMatrix_.wellLayout = &wellLayout_;
264 sysPrecond_->update();
265 }
266 }
267
268 // Which merged well block rows belong to which well. The merged D matrix
269 // is the per-well D blocks concatenated, so this is a plain prefix sum
270 // over their dimensions: one block for a standard well, one per segment
271 // for a multisegment well.
272 void buildWellDofLayout()
273 {
274 auto& offsets = wellLayout_.wellBlockOffsets;
275 offsets.clear();
276 offsets.reserve(wellDMatrices_.size() + 1);
277 offsets.push_back(0);
278 std::size_t total = 0;
279 for (const auto& d : wellDMatrices_) {
280 total += d.N();
281 offsets.push_back(total);
282 }
283
284 // Which wells are on pressure control. Asking this is the outer
285 // layer's job; below here it is just a flag per well. The
286 // pressureControlled array is in the same order as the locally present
287 // wells in the BlackoilWellModel, which addBCDMatrix also follows.
288 wellLayout_.pressureControlled.clear();
289 if (wellLayout_.identityOnPressureControl) {
290 const auto& wellModel = this->simulator_.problem().wellModel();
291 const auto& wellState = wellModel.wellState();
292 wellLayout_.pressureControlled.reserve(wellDMatrices_.size());
293 for (const auto& well : wellModel) {
294 wellLayout_.pressureControlled.push_back(
295 well->isPressureControlled(wellState) ? 1 : 0);
296 }
297 if (wellLayout_.pressureControlled.size() != wellDMatrices_.size()) {
298 OPM_THROW(std::logic_error,
299 "System CPRW: the well container and the extracted well "
300 "matrices disagree on the number of wells.");
301 }
302 }
303 }
304
305 // Weights used to contract each well's equations down to the single scalar
306 // the CPRW pressure system carries for that well. Computed here rather
307 // than inside the preconditioner so that the linear-solver core never sees
308 // anything well-specific, and so that this can later be replaced by a
309 // value obtained from the well model without touching the core.
310 WellVector<Scalar> computeWellWeights(const ResVector<Scalar>& resWeights) const
311 {
312 const std::size_t numBlocks = mergedD_.N();
313 // See the assumptions about well variable and equation indices in SystemTypes.hpp.
314 const int q = wellLayout_.pressureVariableIndex;
315
316 WellVector<Scalar> weights(numBlocks);
317
318 if (wellWeightType_ == "cellavg") {
319 // Classic CPRW (use_well_weights = false): for every block b of
320 // well j, lambda_b = mean of the cell weights w_c over all cells c perforated by j.
321 for (std::size_t j = 0; j < wellLayout_.numWells(); ++j) {
322 const auto lambda = averagePerforatedCellWeights(
323 resWeights, wellLayout_.firstBlock(j), wellLayout_.endBlock(j));
324 for (std::size_t wb = wellLayout_.firstBlock(j); wb < wellLayout_.endBlock(j); ++wb) {
325 weights[wb] = lambda;
326 }
327 }
328 return weights;
329 }
330
331 if (wellWeightType_ == "cellblockavg") {
332 // lambda_b = mean of w_c over the cells c connected to block b only.
333 for (std::size_t wb = 0; wb < numBlocks; ++wb) {
334 weights[wb] = averagePerforatedCellWeights(resWeights, wb, wb + 1);
335 }
336 return weights;
337 }
338
339 for (std::size_t wb = 0; wb < numBlocks; ++wb) {
340 auto& lambda = weights[wb];
341 lambda = 0.0;
342
343 if (wellWeightType_ == "unit") {
344 // Pick the control or pressure equation of the well as-is.
345 lambda[q] = 1.0;
346 continue;
347 }
348
349 // Quasi-IMPES well weights: lambda = D_ii^-T e_q, scaled to unit
350 // max norm. This is the analogue of the use_well_weights=true
351 // branch of StandardWellEquations::extractCPRPressureMatrix, and
352 // it needs no knowledge of the well's control mode.
353 Dune::FieldVector<Scalar, numWellDofs> rhs(0.0);
354 rhs[q] = 1.0;
355 bool ok = false;
356 if (mergedD_.exists(wb, wb)) {
357 try {
358 const auto dt = mergedD_[wb][wb].transposed();
359 dt.solve(lambda, rhs);
360 Scalar absMax = 0.0;
361 for (int i = 0; i < numWellDofs; ++i) {
362 absMax = std::max(absMax, std::abs(lambda[i]));
363 }
364 if (absMax > 0.0 && std::isfinite(absMax)) {
365 lambda /= absMax;
366 ok = true;
367 }
368 } catch (const Dune::FMatrixError&) {
369 ok = false;
370 }
371 }
372 if (!ok) {
373 // Singular or degenerate well block: fall back to the plain
374 // pressure row rather than poisoning the coarse system.
375 lambda = 0.0;
376 lambda[q] = 1.0;
377 }
378 }
379 return weights;
380 }
381
382 // Mean reservoir weight over the cells connected to well blocks [first, last),
383 // applied to the well conservation equations; zero on the control equation.
384 Dune::FieldVector<Scalar, numWellDofs>
385 averagePerforatedCellWeights(const ResVector<Scalar>& resWeights,
386 const std::size_t first,
387 const std::size_t last) const
388 {
389 Dune::FieldVector<Scalar, numWellDofs> lambda(0.0);
390 int numPerfs = 0;
391 for (std::size_t b = first; b < last; ++b) {
392 for (auto col = mergedB_[b].begin(), end = mergedB_[b].end(); col != end; ++col) {
393 const auto& cw = resWeights[col.index()];
394 for (int conservation_eq_index = 0; conservation_eq_index < numResDofs;
395 ++conservation_eq_index) {
396 lambda[conservation_eq_index] += cw[conservation_eq_index];
397 }
398 ++numPerfs;
399 }
400 }
401 for (int conservation_eq_index = 0; conservation_eq_index < numResDofs;
402 ++conservation_eq_index) {
403 // No perforations on this rank: regularise rather than leave an empty row.
404 lambda[conservation_eq_index] = numPerfs > 0
405 ? lambda[conservation_eq_index] / numPerfs
406 : Scalar{1.0};
407 }
408 lambda[wellLayout_.pressureVariableIndex] = 0.0;
409 return lambda;
410 }
411
412 void refreshSystemSolverForChangedWellStructure(const Opm::PropertyTree& prm)
413 {
414 if (!sysInitialized_ || !sysPrecond_) {
415 createSystemSolver(prm);
416 return;
417 }
418
419#if HAVE_MPI
420 if (this->comm_->communicator().size() > 1) {
421 if (auto* precond = dynamic_cast<ParSysPrecondType*>(sysPrecond_)) {
422 precond->updateForChangedWellStructure();
423 } else
424 { // Rebuild the parallel solver if the parallel preconditioner cannot be updated in-place.
425 createSystemSolver(prm);
426 }
427 return;
428 }
429#endif
430
431 if (auto* precond = dynamic_cast<SeqSysPrecondType*>(sysPrecond_)) {
432 precond->updateForChangedWellStructure();
433 } else
434 { // Rebuild the solver if the sequential preconditioner cannot be updated in-place
435 createSystemSolver(prm);
436 }
437 }
438
439 void createSystemSolver(const Opm::PropertyTree& prm)
440 {
441 // Derive weights from the reservoir sub-block config (which uses CPR internally)
442 auto resSolverPrm = prm.get_child("preconditioner.reservoir_solver");
443 std::function<ResVector<Scalar>()> resWeightCalc
444 = this->getWeightsCalculator(resSolverPrm, pressureIndex);
445
446 // The well part of the weights is filled here too: the CPRW pressure
447 // stage restricts the well rows with it, and re-reads it on every
448 // update, so it has to track the current merged D.
449 std::function<SystemVector<Scalar>()> sysWeightCalc;
450 if (resWeightCalc) {
451 sysWeightCalc = [this, resWeightCalc]() {
452 SystemVector<Scalar> w;
453 w[_0] = resWeightCalc();
454 w[_1] = this->computeWellWeights(w[_0]);
455 return w;
456 };
457 }
458
459#if HAVE_MPI
460 const bool is_parallel = this->comm_->communicator().size() > 1;
461 if (is_parallel) {
462 wellComm_ = std::make_unique<WellComm>();
463 systemComm_ = std::make_unique<SystemComm>(*(this->comm_), *wellComm_);
464
465 sysOpPar_ = std::make_unique<SystemParOp<Scalar>>(sysMatrix_, *systemComm_);
466
467 sysFlexSolverPar_ = std::make_unique<Dune::FlexibleSolver<SystemParOp<Scalar>>>(
468 *sysOpPar_, *systemComm_, prm, sysWeightCalc, pressureIndex);
469
470 sysSolver_ = sysFlexSolverPar_.get();
471 sysPrecond_ = &sysFlexSolverPar_->preconditioner();
472 }
473 else
474#endif
475 {
476 sysOp_ = std::make_unique<SystemSeqOp<Scalar>>(sysMatrix_);
477
478 sysFlexSolverSeq_ = std::make_unique<Dune::FlexibleSolver<SystemSeqOp<Scalar>>>(
479 *sysOp_, prm, sysWeightCalc, pressureIndex);
480
481 sysSolver_ = sysFlexSolverSeq_.get();
482 sysPrecond_ = &sysFlexSolverSeq_->preconditioner();
483 }
484 }
485};
486
487} // namespace Opm
488
489#endif // OPM_ISTLSOLVERSYSTEM_HEADER_INCLUDED
Definition: MultiComm.hpp:75
Interface class adding the update() method to the preconditioner interface.
Definition: PreconditionerWithUpdate.hpp:34
Definition: ISTLSolver.hpp:152
std::shared_ptr< CommunicationType > comm_
Definition: ISTLSolver.hpp:739
std::function< Vector()> getWeightsCalculator(const PropertyTree &prm, std::size_t pressIndex) const
Definition: ISTLSolver.hpp:634
typename SparseMatrixAdapter::IstlMatrix Matrix
Definition: ISTLSolver.hpp:161
int solveCount_
Definition: ISTLSolver.hpp:715
Matrix & getMatrix()
Definition: ISTLSolver.hpp:703
GetPropType< TypeTag, Properties::SparseMatrixAdapter > SparseMatrixAdapter
Definition: ISTLSolver.hpp:156
Dune::OwnerOverlapCopyCommunication< int, int > CommunicationType
Definition: ISTLSolver.hpp:181
Matrix * matrix_
Definition: ISTLSolver.hpp:719
bool checkConvergence(const Dune::InverseOperatorResult &result) const
Definition: ISTLSolver.hpp:507
GetPropType< TypeTag, Properties::GlobalEqVector > Vector
Definition: ISTLSolver.hpp:157
void initPrepare(const Matrix &M, Vector &b)
Definition: ISTLSolver.hpp:365
int iterations_
Definition: ISTLSolver.hpp:714
GetPropType< TypeTag, Properties::Simulator > Simulator
Definition: ISTLSolver.hpp:160
Vector * rhs_
Definition: ISTLSolver.hpp:720
int activeSolverNum_
Definition: ISTLSolver.hpp:722
GetPropType< TypeTag, Properties::Indices > Indices
Definition: ISTLSolver.hpp:158
const Simulator & simulator_
Definition: ISTLSolver.hpp:713
std::vector< PropertyTree > prm_
Definition: ISTLSolver.hpp:737
Definition: ISTLSolverSystem.hpp:48
void prepare(const SparseMatrixAdapter &M, Vector &b) override
Definition: ISTLSolverSystem.hpp:93
@ enablePolymerMolarWeight
Definition: ISTLSolverSystem.hpp:67
static constexpr auto _1
Definition: ISTLSolverSystem.hpp:78
GetPropType< TypeTag, Properties::Scalar > Scalar
Definition: ISTLSolverSystem.hpp:50
static constexpr std::size_t pressureIndex
Definition: ISTLSolverSystem.hpp:65
ISTLSolverSystem(const Simulator &simulator, const FlowLinearSolverParameters &parameters, bool forceSerial=false)
Definition: ISTLSolverSystem.hpp:81
static constexpr bool isIncompatibleWithCprw
Definition: ISTLSolverSystem.hpp:68
static constexpr auto _0
Definition: ISTLSolverSystem.hpp:77
ISTLSolverSystem(const Simulator &simulator)
Definition: ISTLSolverSystem.hpp:88
void prepare(const Matrix &M, Vector &b) override
Definition: ISTLSolverSystem.hpp:100
bool solve(Vector &x) override
Definition: ISTLSolverSystem.hpp:107
Hierarchical collection of key/value pairs.
Definition: PropertyTree.hpp:39
PropertyTree get_child(const std::string &key) const
Definition: SystemTypes.hpp:166
const WellDofLayout * wellLayout
Definition: SystemTypes.hpp:182
const WRMatrix< Scalar > * B
Definition: SystemTypes.hpp:177
const RWMatrix< Scalar > * C
Definition: SystemTypes.hpp:176
const RRMatrix< Scalar > * A
Definition: SystemTypes.hpp:175
const WWMatrix< Scalar > * D
Definition: SystemTypes.hpp:178
Definition: SystemPreconditioner.hpp:62
Definition: WellMatrixMerger.hpp:124
void writeSystem(const SimulatorType &simulator, const MatrixType &matrix, const VectorType &rhs, const std::string &sysName, const Communicator *comm)
Definition: WriteSystemMatrixHelper.hpp:197
Definition: blackoilbioeffectsmodules.hh:45
Dune::MultiTypeBlockVector< ResVector< Scalar >, WellVector< Scalar > > SystemVector
Definition: SystemTypes.hpp:65
Dune::BCRSMatrix< Dune::FieldMatrix< Scalar, numWellDofs, numWellDofs > > WWMatrix
Definition: SystemTypes.hpp:58
Dune::InverseOperatorResult InverseOperatorResult
Definition: GpuBridge.hpp:32
Dune::BCRSMatrix< Dune::FieldMatrix< Scalar, numResDofs, numWellDofs > > RWMatrix
Definition: SystemTypes.hpp:54
Dune::OwnerOverlapCopyCommunication< int, int > ParResComm
Definition: SystemPreconditioner.hpp:45
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(....
Definition: propertysystem.hh:233
Dune::BCRSMatrix< Dune::FieldMatrix< Scalar, numWellDofs, numResDofs > > WRMatrix
Definition: SystemTypes.hpp:56
constexpr int numWellDofs
Definition: SystemTypes.hpp:49
constexpr int numResDofs
Definition: SystemTypes.hpp:48
This class carries all parameters for the NewtonIterationBlackoilInterleaved class.
Definition: FlowLinearSolverParameters.hpp:98
Definition: SystemTypes.hpp:82
std::vector< char > pressureControlled
Definition: SystemTypes.hpp:106
std::vector< std::size_t > wellBlockOffsets
Definition: SystemTypes.hpp:84
std::size_t firstBlock(const std::size_t j) const
Definition: SystemTypes.hpp:123
int pressureVariableIndex
Definition: SystemTypes.hpp:96
bool identityOnPressureControl
Definition: SystemTypes.hpp:107
std::size_t endBlock(const std::size_t j) const
Definition: SystemTypes.hpp:128
std::size_t numWells() const
Definition: SystemTypes.hpp:116
Definition: WellMatrixMerger.hpp:63
std::size_t totalWellBlocks
Definition: WellMatrixMerger.hpp:65