/* Copyright 2016 IRIS AS This file is part of the Open Porous Media project (OPM). OPM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OPM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OPM. If not, see . */ #ifndef OPM_ISTLSOLVERCPR_EBOS_HEADER_INCLUDED #define OPM_ISTLSOLVERCPR_EBOS_HEADER_INCLUDED #include #include #include #include namespace Opm { //===================================================================== // Implementation for ISTL-matrix based operator //===================================================================== /// This class solves the fully implicit black-oil system by /// solving the reduced system (after eliminating well variables) /// as a block-structured matrix (one block for all cell variables) for a fixed /// number of cell variables np . /// \tparam MatrixBlockType The type of the matrix block used. /// \tparam VectorBlockType The type of the vector block used. /// \tparam pressureIndex The index of the pressure component in the vector /// vector block. It is used to guide the AMG coarsening. /// Default is zero. template class ISTLSolverEbosCpr : public ISTLSolverEbos { protected: // Types and indices from superclass. using SuperClass = ISTLSolverEbos; using Matrix = typename SuperClass::Matrix; using Vector = typename SuperClass::Vector; using WellModel = typename SuperClass::WellModel; using Simulator = typename SuperClass::Simulator; using SparseMatrixAdapter = typename SuperClass::SparseMatrixAdapter; enum { pressureEqnIndex = SuperClass::pressureEqnIndex }; enum { pressureVarIndex = SuperClass::pressureVarIndex }; // New properties in this subclass. using Preconditioner = Dune::Preconditioner; using MatrixAdapter = Dune::MatrixAdapter; using CouplingMetric = Opm::Amg::Element; using CritBase = Dune::Amg::SymmetricCriterion; using Criterion = Dune::Amg::CoarsenCriterion; using CprSmootherFine = Opm::ParallelOverlappingILU0; using CprSmootherCoarse = CprSmootherFine; using BlackoilAmgType = BlackoilAmgCpr; using OperatorSerial = WellModelMatrixAdapter< Matrix, Vector, Vector, WellModel, false>; #if HAVE_MPI using POrComm = Dune::OwnerOverlapCopyCommunication; using ParallelMatrixAdapter = Dune::OverlappingSchwarzOperator; using ParallelCprSmootherFine = Opm::ParallelOverlappingILU0; using ParallelCprSmootherCoarse = ParallelCprSmootherFine; using ParallelBlackoilAmgType = BlackoilAmgCpr; using OperatorParallel = WellModelMatrixAdapter< Matrix, Vector, Vector, WellModel, true>; using ParallelScalarProduct = Dune::OverlappingSchwarzScalarProduct; #else using POrComm = Dune::Amg::SequentialInformation; using ParallelBlackoilAmgType = BlackoilAmgType; using ParallelScalarProduct = Dune::SeqScalarProduct; using ParallelMatrixAdapter = MatrixAdapter; using OperatorParallel = OperatorSerial; #endif public: static void registerParameters() { FlowLinearSolverParameters::registerParameters(); } /// Construct a system solver. /// \param[in] parallelInformation In the case of a parallel run /// with dune-istl the information about the parallelization. explicit ISTLSolverEbosCpr(const Simulator& simulator) : SuperClass(simulator), oldMat() { extractParallelGridInformationToISTL(this->simulator_.vanguard().grid(), this->parallelInformation_); detail::findOverlapAndInterior(this->simulator_.vanguard().grid(), this->overlapRows_, this->interiorRows_); } void prepare(const SparseMatrixAdapter& M, Vector& b) { if (oldMat != nullptr) std::cout << "old was "<simulator_.model().newtonMethod().numIterations(); if (newton_iteration < 1 or not(this->parameters_.cpr_reuse_setup_)) { SuperClass::matrix_.reset(new Matrix(M.istlMatrix())); } else { *SuperClass::matrix_ = M.istlMatrix(); } SuperClass::rhs_ = &b; SuperClass::scaleSystem(); const WellModel& wellModel = this->simulator_.problem().wellModel(); #if HAVE_MPI if( this->isParallel() ) { //remove ghost rows in local matrix without doing a copy. this->makeOverlapRowsInvalid(*(this->matrix_)); if (newton_iteration < 1 or not(this->parameters_.cpr_reuse_setup_)) { //Not sure what actual_mat_for_prec is, so put ebosJacIgnoreOverlap as both variables //to be certain that correct matrix is used for preconditioning. if( ! comm_ ) { opAParallel_.reset(new OperatorParallel(*(this->matrix_), *(this->matrix_), wellModel, this->parallelInformation_ )); comm_ = opAParallel_->comm(); assert(comm_->indexSet().size()==0); const size_t size = opAParallel_->getmat().N(); const ParallelISTLInformation& info = boost::any_cast( this->parallelInformation_); // As we use a dune-istl with block size np the number of components // per parallel is only one. info.copyValuesTo(comm_->indexSet(), comm_->remoteIndices(), size, 1); } else { opAParallel_.reset(new OperatorParallel(*(this->matrix_), *(this->matrix_), wellModel, comm_ )); } } constexpr Dune::SolverCategory::Category category=Dune::SolverCategory::overlapping; auto sp = Dune::createScalarProduct(*comm_, category); sp_ = std::move(sp); using AMGOperator = Dune::OverlappingSchwarzOperator; // If clause is always execute as as Linearoperator is WellModelMatrixAdapter< Matrix, Vector, Vector, WellModel, false|true>; if( ! std::is_same< OperatorParallel, AMGOperator > :: value && ( newton_iteration < 1 or not(this->parameters_.cpr_reuse_setup_) ) ) { // create new operator in case linear operator and matrix operator differ opA_.reset( new AMGOperator( opAParallel_->getmat(), *comm_ )); } prepareSolver(*opAParallel_, *comm_); } else #endif { if (newton_iteration < 1 or not(this->parameters_.cpr_reuse_setup_)) { opASerial_.reset(new OperatorSerial(*(this->matrix_), *(this->matrix_), wellModel)); } using POrCommType = Dune::Amg::SequentialInformation; POrCommType parallelInformation_arg; typedef OperatorSerial LinearOperator; constexpr Dune::SolverCategory::Category category=Dune::SolverCategory::sequential; auto sp = Dune::createScalarProduct(parallelInformation_arg, category); sp_ = std::move(sp); // If clause is always execute as as Linearoperator is WellModelMatrixAdapter< Matrix, Vector, Vector, WellModel, false|true>; if( ! std::is_same< LinearOperator, MatrixAdapter > :: value && ( newton_iteration < 1 or not(this->parameters_.cpr_reuse_setup_) ) ) { // create new operator in case linear operator and matrix operator differ opA_.reset( new MatrixAdapter( opASerial_->getmat()));//, parallelInformation_arg ) ); } prepareSolver(*opASerial_, parallelInformation_arg); } } template void prepareSolver(Operator& wellOpA, Comm& comm) { Vector& istlb = *(this->rhs_); comm.copyOwnerToAll(istlb, istlb); const double relax = this->parameters_.ilu_relaxation_; const MILU_VARIANT ilu_milu = this->parameters_.ilu_milu_; // TODO: revise choice of parameters // int coarsenTarget = 4000; int coarsenTarget = 1200; Criterion criterion(15, coarsenTarget); criterion.setDebugLevel( this->parameters_.cpr_solver_verbose_ ); // no debug information, 1 for printing hierarchy information criterion.setDefaultValuesIsotropic(2); criterion.setNoPostSmoothSteps( 1 ); criterion.setNoPreSmoothSteps( 1 ); //new guesses by hmbn //criterion.setAlpha(0.01); // criterion for connection strong 1/3 is default //criterion.setMaxLevel(2); // //criterion.setGamma(1); // //1 V cycle 2 WW // Since DUNE 2.2 we also need to pass the smoother args instead of steps directly using AmgType = typename std::conditional::value, BlackoilAmgType, ParallelBlackoilAmgType>::type; using SpType = typename std::conditional::value, Dune::SeqScalarProduct, ParallelScalarProduct >::type; using OperatorType = typename std::conditional::value, MatrixAdapter, ParallelMatrixAdapter>::type; typedef typename AmgType::Smoother Smoother; typedef typename Dune::Amg::SmootherTraits::Arguments SmootherArgs; SmootherArgs smootherArgs; smootherArgs.iterations = 1; smootherArgs.relaxationFactor = relax; const Opm::CPRParameter& params(this->parameters_); // strange conversion ISTLUtility::setILUParameters(smootherArgs, ilu_milu); auto& opARef = reinterpret_cast(*opA_); int newton_iteration = this->simulator_.model().newtonMethod().numIterations(); bool update_preconditioner = false; if (this->parameters_.cpr_reuse_setup_ < 1) { update_preconditioner = true; } if (this->parameters_.cpr_reuse_setup_ < 2) { if (newton_iteration < 1) { update_preconditioner = true; } } if (this->parameters_.cpr_reuse_setup_ < 3) { if (this->iterations() > 10) { update_preconditioner = true; } } if ( update_preconditioner or (amg_== 0) ) { amg_.reset( new AmgType( params, this->weights_, opARef, criterion, smootherArgs, comm ) ); } else { if (this->parameters_.cpr_solver_verbose_) { std::cout << " Only update amg solver " << std::endl; } reinterpret_cast(amg_.get())->updatePreconditioner(opARef, smootherArgs, comm); } // Solve. //SuperClass::solve(linearOperator, x, istlb, *sp, *amg, result); //references seems to do something els than refering int verbosity_linsolve = 0; if (comm.communicator().rank() == 0) { verbosity_linsolve = this->parameters_.linear_solver_verbosity_; } linsolve_.reset(new Dune::BiCGSTABSolver(wellOpA, reinterpret_cast(*sp_), reinterpret_cast(*amg_), this->parameters_.linear_solver_reduction_, this->parameters_.linear_solver_maxiter_, verbosity_linsolve)); } bool solve(Vector& x) { // Solve system. Dune::InverseOperatorResult result; Vector& istlb = *(this->rhs_); linsolve_->apply(x, istlb, result); SuperClass::checkConvergence(result); if (this->parameters_.scale_linear_system_) { this->scaleSolution(x); } return this->converged_; } protected: ///! \brief The dune-istl operator (either serial or parallel std::unique_ptr< Dune::LinearOperator > opA_; ///! \brief Serial well matrix adapter std::unique_ptr< OperatorSerial > opASerial_; ///! \brief Parallel well matrix adapter std::unique_ptr< OperatorParallel > opAParallel_; ///! \brief The preconditoner to use (either serial or parallel CPR with AMG) std::unique_ptr< Preconditioner > amg_; using SPPointer = std::shared_ptr< Dune::ScalarProduct >; SPPointer sp_; std::shared_ptr< Dune::BiCGSTABSolver > linsolve_; const void* oldMat; std::shared_ptr comm_; }; // end ISTLSolver } // namespace Opm #endif