149 GetPropType<TypeTag, Properties::GlobalEqVector>>
159 using Matrix =
typename SparseMatrixAdapter::IstlMatrix;
162 using AbstractSolverType = Dune::InverseOperator<Vector, Vector>;
163 using AbstractOperatorType = Dune::AssembledLinearOperator<Matrix, Vector, Vector>;
167 using ElementChunksType = ElementChunks<GridView, Dune::Partitions::All>;
172 constexpr static bool isIncompatibleWithCprw = enablePolymerMolarWeight;
175 using CommunicationType = Dune::OwnerOverlapCopyCommunication<int,int>;
177 using CommunicationType = Dune::Communication<int>;
181 using AssembledLinearOperatorType = Dune::AssembledLinearOperator< Matrix, Vector, Vector >;
183 static void registerParameters()
185 FlowLinearSolverParameters::registerParameters();
197 bool forceSerial =
false)
198 : simulator_(simulator),
201 parameters_{parameters},
202 forceSerial_(forceSerial)
210 : simulator_(simulator),
215 parameters_.resize(1);
216 parameters_[0].init(simulator_.vanguard().eclState().getSimulationConfig().useCPR());
222 OPM_TIMEBLOCK(IstlSolver);
224 if (isIncompatibleWithCprw) {
226 if (parameters_[0].linsolver_ ==
"cprw" || parameters_[0].linsolver_ ==
"hybrid") {
227 OPM_THROW(std::runtime_error,
228 "The polymer injectivity model is incompatible with the CPRW linear solver.\n"
229 "Choose a different option, for example --linear-solver=ilu0");
233 if (parameters_[0].linsolver_ ==
"hybrid") {
241 FlowLinearSolverParameters para;
243 para.linsolver_ =
"cprw";
244 parameters_.push_back(para);
246 Parameters::IsSet<Parameters::LinearSolverMaxIter>(),
247 Parameters::IsSet<Parameters::LinearSolverReduction>()));
250 FlowLinearSolverParameters para;
252 para.linsolver_ =
"ilu0";
253 parameters_.push_back(para);
255 Parameters::IsSet<Parameters::LinearSolverMaxIter>(),
256 Parameters::IsSet<Parameters::LinearSolverReduction>()));
260 assert(parameters_.size() == 1);
261 assert(prm_.empty());
264 if (parameters_[0].is_nldd_local_solver_) {
266 Parameters::IsSet<Parameters::NlddLocalLinearSolverMaxIter>(),
267 Parameters::IsSet<Parameters::NlddLocalLinearSolverReduction>()));
271 Parameters::IsSet<Parameters::LinearSolverMaxIter>(),
272 Parameters::IsSet<Parameters::LinearSolverReduction>()));
275 flexibleSolver_.resize(prm_.size());
277 const bool on_io_rank = (simulator_.gridView().
comm().rank() == 0);
279 comm_.reset(
new CommunicationType( simulator_.vanguard().grid().comm() ) );
286 ElementMapper elemMapper(simulator_.vanguard().gridView(), Dune::mcmgElementLayout());
287 detail::findOverlapAndInterior(simulator_.vanguard().grid(), elemMapper, overlapRows_, interiorRows_);
288 useWellConn_ = Parameters::Get<Parameters::MatrixAddWellContributions>();
289 const bool ownersFirst = Parameters::Get<Parameters::OwnerCellsFirst>();
291 const std::string msg =
"The linear solver no longer supports --owner-cells-first=false.";
295 OPM_THROW_NOLOG(std::runtime_error, msg);
298 const int interiorCellNum_ = detail::numMatrixRowsToUseInSolver(simulator_.vanguard().grid(),
true);
299 for (
auto& f : flexibleSolver_) {
300 f.interiorCellNum_ = interiorCellNum_;
305 const std::size_t size = simulator_.vanguard().grid().leafGridView().size(0);
306 detail::copyParValues(parallelInformation_, size, *comm_);
311 detail::printLinearSolverParameters(parameters_, activeSolverNum_, prm_, simulator_.gridView().comm());
313 element_chunks_ = std::make_unique<ElementChunksType>(simulator_.vanguard().gridView(), Dune::Partitions::all,
ThreadManager::maxThreads());
323 if (num >
static_cast<int>(prm_.size()) - 1) {
324 OPM_THROW(std::logic_error,
"Solver number " + std::to_string(num) +
" not available.");
326 activeSolverNum_ = num;
327 if (simulator_.gridView().comm().rank() == 0) {
328 OpmLog::debug(
"Active solver = " + std::to_string(activeSolverNum_)
329 +
" (" + parameters_[activeSolverNum_].linsolver_ +
")");
335 return flexibleSolver_.size();
338 void initPrepare(
const Matrix& M, Vector& b)
340 const bool firstcall = (matrix_ ==
nullptr);
347 matrix_ =
const_cast<Matrix*
>(&M);
353 if ( &M != matrix_ ) {
354 OPM_THROW(std::logic_error,
355 "Matrix objects are expected to be reused when reassembling!");
362 std::string type = prm_[activeSolverNum_].template get<std::string>(
"preconditioner.type",
"paroverilu0");
363 std::ranges::transform(type, type.begin(), ::tolower);
364 if (isParallel() && type !=
"paroverilu0") {
365 detail::makeOverlapRowsInvalid(getMatrix(), overlapRows_);
369 void prepare(
const SparseMatrixAdapter& M, Vector& b)
override
371 prepare(M.istlMatrix(), b);
374 void prepare(
const Matrix& M, Vector& b)
override
376 OPM_TIMEBLOCK(istlSolverPrepare);
380 prepareFlexibleSolver();
381 } OPM_CATCH_AND_RETHROW_AS_CRITICAL_ERROR(
"This is likely due to a faulty linear solver JSON specification. Check for errors related to missing nodes.");
385 void setResidual(Vector& )
override
390 void getResidual(Vector& b)
const override
395 void setMatrix(
const SparseMatrixAdapter& )
override
404 void resetSolveCount() {
408 bool solve(Vector& x)
override
410 OPM_TIMEBLOCK(istlSolverSolve);
413 const int verbosity = prm_[activeSolverNum_].get(
"verbosity", 0);
414 const bool write_matrix = verbosity > 10;
416 Helper::writeSystem(simulator_,
423 Dune::InverseOperatorResult result;
425 OPM_TIMEBLOCK(flexibleSolverApply);
426 assert(flexibleSolver_[activeSolverNum_].solver_);
427 flexibleSolver_[activeSolverNum_].solver_->apply(x, *rhs_, result);
430 iterations_ = result.iterations;
433 return checkConvergence(result);
449 const CommunicationType*
comm()
const override {
return comm_.get(); }
451 void setDomainIndex(
const int index)
453 domainIndex_ = index;
456 bool isNlddLocalSolver()
const
458 return parameters_[activeSolverNum_].is_nldd_local_solver_;
463 using Comm = Dune::OwnerOverlapCopyCommunication<int, int>;
466 bool checkConvergence(
const Dune::InverseOperatorResult& result)
const
471 bool isParallel()
const {
473 return !forceSerial_ && comm_->communicator().size() > 1;
479 void prepareFlexibleSolver()
481 OPM_TIMEBLOCK(flexibleSolverPrepare);
484 if (isNlddLocalSolver()) {
485 auto wellOp = std::make_unique<DomainWellModelAsLinearOperator<WellModel, Vector, Vector>>(simulator_.problem().wellModel());
486 wellOp->setDomainIndex(domainIndex_);
487 flexibleSolver_[activeSolverNum_].wellOperator_ = std::move(wellOp);
490 auto wellOp = std::make_unique<WellModelOperator>(simulator_.problem().wellModel());
491 flexibleSolver_[activeSolverNum_].wellOperator_ = std::move(wellOp);
494 std::function<Vector()> weightCalculator = this->getWeightsCalculator(prm_[activeSolverNum_], getMatrix(), pressureIndex);
495 OPM_TIMEBLOCK(flexibleSolverCreate);
496 flexibleSolver_[activeSolverNum_].create(getMatrix(),
498 prm_[activeSolverNum_],
506 OPM_TIMEBLOCK(flexibleSolverUpdate);
507 flexibleSolver_[activeSolverNum_].pre_->update();
518 if (flexibleSolver_.empty()) {
521 if (!flexibleSolver_[activeSolverNum_].solver_) {
525 if (flexibleSolver_[activeSolverNum_].pre_->hasPerfectUpdate()) {
531 if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 0) {
535 if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 1) {
537 return this->simulator_.problem().iterationContext().isFirstGlobalIteration();
539 if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 2) {
543 if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 3) {
547 if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 4) {
549 const int step = this->parameters_[activeSolverNum_].cpr_reuse_interval_;
550 const bool create = ((solveCount_ % step) == 0);
554 const bool on_io_rank = (simulator_.gridView().
comm().rank() == 0);
555 std::string msg =
"Invalid value: " + std::to_string(this->parameters_[activeSolverNum_].cpr_reuse_setup_)
556 +
" for --cpr-reuse-setup parameter, run with --help to see allowed values.";
560 throw std::runtime_error(msg);
569 std::function<Vector()> getWeightsCalculator(
const PropertyTree& prm,
570 const Matrix& matrix,
571 std::size_t pressIndex)
const
573 std::function<Vector()> weightsCalculator;
575 using namespace std::string_literals;
577 auto preconditionerType = prm.
get(
"preconditioner.type"s,
"cpr"s);
579 std::ranges::transform(preconditionerType, preconditionerType.begin(), ::tolower);
580 if (preconditionerType ==
"cpr" || preconditionerType ==
"cprt"
581 || preconditionerType ==
"cprw" || preconditionerType ==
"cprwt") {
582 const bool transpose = preconditionerType ==
"cprt" || preconditionerType ==
"cprwt";
583 const bool enableThreadParallel = this->parameters_[0].cpr_weights_thread_parallel_;
584 const auto weightsType = prm.
get(
"preconditioner.weight_type"s,
"quasiimpes"s);
585 if (weightsType ==
"quasiimpes") {
589 weightsCalculator = [matrix, transpose, pressIndex, enableThreadParallel]() {
590 return Amg::getQuasiImpesWeights<Matrix, Vector>(matrix,
593 enableThreadParallel);
595 }
else if ( weightsType ==
"trueimpes" ) {
597 [
this, pressIndex, enableThreadParallel]
599 Vector weights(rhs_->size());
600 ElementContext elemCtx(simulator_);
601 Amg::getTrueImpesWeights(pressIndex,
610 }
else if (weightsType ==
"trueimpesanalytic" ) {
612 [
this, pressIndex, enableThreadParallel]
614 Vector weights(rhs_->size());
615 ElementContext elemCtx(simulator_);
616 Amg::getTrueImpesWeightsAnalytic(pressIndex,
626 OPM_THROW(std::invalid_argument,
627 "Weights type " + weightsType +
628 "not implemented for cpr."
629 " Please use quasiimpes, trueimpes or trueimpesanalytic.");
632 return weightsCalculator;
641 const Matrix& getMatrix()
const
646 const Simulator& simulator_;
647 mutable int iterations_;
648 mutable int solveCount_;
649 std::any parallelInformation_;
655 int activeSolverNum_ = 0;
656 std::vector<detail::FlexibleSolverInfo<Matrix,Vector,CommunicationType>> flexibleSolver_;
657 std::vector<int> overlapRows_;
658 std::vector<int> interiorRows_;
660 int domainIndex_ = -1;
664 std::vector<FlowLinearSolverParameters> parameters_;
665 bool forceSerial_ =
false;
666 std::vector<PropertyTree> prm_;
668 std::shared_ptr< CommunicationType > comm_;
669 std::unique_ptr<ElementChunksType> element_chunks_;