158 using Matrix =
typename SparseMatrixAdapter::IstlMatrix;
161 using AbstractSolverType = Dune::InverseOperator<Vector, Vector>;
162 using AbstractOperatorType = Dune::AssembledLinearOperator<Matrix, Vector, Vector>;
166 using ElementChunksType = ElementChunks<GridView, Dune::Partitions::All>;
171 constexpr static bool isIncompatibleWithCprw = enablePolymerMolarWeight;
174 using CommunicationType = Dune::OwnerOverlapCopyCommunication<int,int>;
176 using CommunicationType = Dune::Communication<int>;
180 using AssembledLinearOperatorType = Dune::AssembledLinearOperator< Matrix, Vector, Vector >;
182 static void registerParameters()
184 FlowLinearSolverParameters::registerParameters();
196 bool forceSerial =
false)
197 : simulator_(simulator),
200 parameters_{parameters},
201 forceSerial_(forceSerial)
209 : simulator_(simulator),
214 parameters_.resize(1);
215 parameters_[0].init(simulator_.vanguard().eclState().getSimulationConfig().useCPR());
221 OPM_TIMEBLOCK(IstlSolver);
223 if (isIncompatibleWithCprw) {
225 if (parameters_[0].linsolver_ ==
"cprw" || parameters_[0].linsolver_ ==
"hybrid") {
226 OPM_THROW(std::runtime_error,
227 "The polymer injectivity model is incompatible with the CPRW linear solver.\n"
228 "Choose a different option, for example --linear-solver=ilu0");
232 if (parameters_[0].linsolver_ ==
"hybrid") {
240 FlowLinearSolverParameters para;
242 para.linsolver_ =
"cprw";
243 parameters_.push_back(para);
245 Parameters::IsSet<Parameters::LinearSolverMaxIter>(),
246 Parameters::IsSet<Parameters::LinearSolverReduction>()));
249 FlowLinearSolverParameters para;
251 para.linsolver_ =
"ilu0";
252 parameters_.push_back(para);
254 Parameters::IsSet<Parameters::LinearSolverMaxIter>(),
255 Parameters::IsSet<Parameters::LinearSolverReduction>()));
259 assert(parameters_.size() == 1);
260 assert(prm_.empty());
263 if (parameters_[0].is_nldd_local_solver_) {
265 Parameters::IsSet<Parameters::NlddLocalLinearSolverMaxIter>(),
266 Parameters::IsSet<Parameters::NlddLocalLinearSolverReduction>()));
270 Parameters::IsSet<Parameters::LinearSolverMaxIter>(),
271 Parameters::IsSet<Parameters::LinearSolverReduction>()));
274 flexibleSolver_.resize(prm_.size());
276 const bool on_io_rank = (simulator_.gridView().
comm().rank() == 0);
278 comm_.reset(
new CommunicationType( simulator_.vanguard().grid().comm() ) );
280 extractParallelGridInformationToISTL(simulator_.vanguard().grid(), parallelInformation_);
285 ElementMapper elemMapper(simulator_.vanguard().gridView(), Dune::mcmgElementLayout());
286 detail::findOverlapAndInterior(simulator_.vanguard().grid(), elemMapper, overlapRows_, interiorRows_);
287 useWellConn_ = Parameters::Get<Parameters::MatrixAddWellContributions>();
288 const bool ownersFirst = Parameters::Get<Parameters::OwnerCellsFirst>();
290 const std::string msg =
"The linear solver no longer supports --owner-cells-first=false.";
294 OPM_THROW_NOLOG(std::runtime_error, msg);
297 const int interiorCellNum_ = detail::numMatrixRowsToUseInSolver(simulator_.vanguard().grid(),
true);
298 for (
auto& f : flexibleSolver_) {
299 f.interiorCellNum_ = interiorCellNum_;
304 const std::size_t size = simulator_.vanguard().grid().leafGridView().size(0);
305 detail::copyParValues(parallelInformation_, size, *comm_);
310 detail::printLinearSolverParameters(parameters_, activeSolverNum_, prm_, simulator_.gridView().comm());
312 element_chunks_ = std::make_unique<ElementChunksType>(simulator_.vanguard().gridView(), Dune::Partitions::all,
ThreadManager::maxThreads());
322 if (num >
static_cast<int>(prm_.size()) - 1) {
323 OPM_THROW(std::logic_error,
"Solver number " + std::to_string(num) +
" not available.");
325 activeSolverNum_ = num;
326 if (simulator_.gridView().comm().rank() == 0) {
327 OpmLog::debug(
"Active solver = " + std::to_string(activeSolverNum_)
328 +
" (" + parameters_[activeSolverNum_].linsolver_ +
")");
334 return flexibleSolver_.size();
337 void initPrepare(
const Matrix& M, Vector& b)
339 const bool firstcall = (matrix_ ==
nullptr);
346 matrix_ =
const_cast<Matrix*
>(&M);
352 if ( &M != matrix_ ) {
353 OPM_THROW(std::logic_error,
354 "Matrix objects are expected to be reused when reassembling!");
361 std::string type = prm_[activeSolverNum_].template get<std::string>(
"preconditioner.type",
"paroverilu0");
362 std::transform(type.begin(), type.end(), type.begin(), ::tolower);
363 if (isParallel() && type !=
"paroverilu0") {
364 detail::makeOverlapRowsInvalid(getMatrix(), overlapRows_);
368 void prepare(
const SparseMatrixAdapter& M, Vector& b)
override
373 void prepare(
const Matrix& M, Vector& b)
override
375 OPM_TIMEBLOCK(istlSolverPrepare);
379 prepareFlexibleSolver();
380 } 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.");
403 void resetSolveCount() {
409 OPM_TIMEBLOCK(istlSolverSolve);
412 const int verbosity = prm_[activeSolverNum_].get(
"verbosity", 0);
413 const bool write_matrix = verbosity > 10;
415 Helper::writeSystem(simulator_,
422 Dune::InverseOperatorResult result;
424 OPM_TIMEBLOCK(flexibleSolverApply);
425 assert(flexibleSolver_[activeSolverNum_].solver_);
426 flexibleSolver_[activeSolverNum_].solver_->apply(x, *rhs_, result);
429 iterations_ = result.iterations;
432 return checkConvergence(result);
448 const CommunicationType*
comm()
const override {
return comm_.get(); }
450 void setDomainIndex(
const int index)
452 domainIndex_ = index;
455 bool isNlddLocalSolver()
const
457 return parameters_[activeSolverNum_].is_nldd_local_solver_;
462 using Comm = Dune::OwnerOverlapCopyCommunication<int, int>;
465 bool checkConvergence(
const Dune::InverseOperatorResult& result)
const
470 bool isParallel()
const {
472 return !forceSerial_ && comm_->communicator().size() > 1;
478 void prepareFlexibleSolver()
480 OPM_TIMEBLOCK(flexibleSolverPrepare);
483 if (isNlddLocalSolver()) {
484 auto wellOp = std::make_unique<DomainWellModelAsLinearOperator<WellModel, Vector, Vector>>(simulator_.problem().wellModel());
485 wellOp->setDomainIndex(domainIndex_);
486 flexibleSolver_[activeSolverNum_].wellOperator_ = std::move(wellOp);
489 auto wellOp = std::make_unique<WellModelOperator>(simulator_.problem().wellModel());
490 flexibleSolver_[activeSolverNum_].wellOperator_ = std::move(wellOp);
493 std::function<Vector()> weightCalculator = this->getWeightsCalculator(prm_[activeSolverNum_], getMatrix(), pressureIndex);
494 OPM_TIMEBLOCK(flexibleSolverCreate);
495 flexibleSolver_[activeSolverNum_].create(getMatrix(),
497 prm_[activeSolverNum_],
505 OPM_TIMEBLOCK(flexibleSolverUpdate);
506 flexibleSolver_[activeSolverNum_].pre_->update();
517 if (flexibleSolver_.empty()) {
520 if (!flexibleSolver_[activeSolverNum_].solver_) {
524 if (flexibleSolver_[activeSolverNum_].pre_->hasPerfectUpdate()) {
530 if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 0) {
534 if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 1) {
536 const int newton_iteration = this->simulator_.model().newtonMethod().numIterations();
537 return newton_iteration == 0;
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::transform(preconditionerType.begin(), preconditionerType.end(), 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_;