2018-06-21 05:14:17 -05:00
/*
Copyright 2016 IRIS AS
2019-03-15 05:27:38 -05:00
Copyright 2019 Equinor ASA
2018-06-21 05:14:17 -05:00
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 < http : //www.gnu.org/licenses/>.
*/
# ifndef OPM_ISTLSOLVER_EBOS_HEADER_INCLUDED
# define OPM_ISTLSOLVER_EBOS_HEADER_INCLUDED
2019-05-07 06:06:02 -05:00
# include <opm/simulators/linalg/MatrixBlock.hpp>
# include <opm/simulators/linalg/BlackoilAmg.hpp>
# include <opm/simulators/linalg/CPRPreconditioner.hpp>
# include <opm/simulators/linalg/ParallelRestrictedAdditiveSchwarz.hpp>
# include <opm/simulators/linalg/ParallelOverlappingILU0.hpp>
# include <opm/simulators/linalg/ExtractParallelGridInformationToISTL.hpp>
2019-06-19 08:04:14 -05:00
# include <opm/simulators/linalg/findOverlapRowsAndColumns.hpp>
2018-06-21 05:14:17 -05:00
# include <opm/common/Exceptions.hpp>
2019-05-07 06:06:02 -05:00
# include <opm/simulators/linalg/ParallelIstlInformation.hpp>
2018-06-21 05:14:17 -05:00
# include <opm/common/utility/platform_dependent/disable_warnings.h>
2019-03-15 05:27:38 -05:00
# include <opm/material/fluidsystems/BlackOilDefaultIndexTraits.hpp>
2018-06-21 05:14:17 -05:00
2019-09-16 03:58:20 -05:00
# include <opm/models/utils/parametersystem.hh>
# include <opm/models/utils/propertysystem.hh>
2018-06-21 05:14:17 -05:00
# include <dune/istl/scalarproducts.hh>
# include <dune/istl/operators.hh>
# include <dune/istl/preconditioners.hh>
# include <dune/istl/solvers.hh>
# include <dune/istl/owneroverlapcopy.hh>
# include <dune/istl/paamg/amg.hh>
# include <opm/common/utility/platform_dependent/reenable_warnings.h>
2019-12-04 09:59:58 -06:00
# include <opm/simulators/linalg/bda/BdaBridge.hpp>
2018-06-21 05:14:17 -05:00
BEGIN_PROPERTIES
2018-06-21 05:14:17 -05:00
NEW_TYPE_TAG ( FlowIstlSolver , INHERITS_FROM ( FlowIstlSolverParams ) ) ;
2018-06-21 05:14:17 -05:00
NEW_PROP_TAG ( Scalar ) ;
NEW_PROP_TAG ( GlobalEqVector ) ;
2018-11-12 05:40:11 -06:00
NEW_PROP_TAG ( SparseMatrixAdapter ) ;
2018-06-21 05:14:17 -05:00
NEW_PROP_TAG ( Indices ) ;
2018-11-12 04:03:54 -06:00
NEW_PROP_TAG ( Simulator ) ;
NEW_PROP_TAG ( EclWellModel ) ;
2018-06-21 05:14:17 -05:00
2019-05-31 03:55:13 -05:00
//! Set the type of a global jacobian matrix for linear solvers that are based on
//! dune-istl.
SET_PROP ( FlowIstlSolver , SparseMatrixAdapter )
{
private :
typedef typename GET_PROP_TYPE ( TypeTag , Scalar ) Scalar ;
enum { numEq = GET_PROP_VALUE ( TypeTag , NumEq ) } ;
2019-09-05 10:04:39 -05:00
typedef Opm : : MatrixBlock < Scalar , numEq , numEq > Block ;
2019-05-31 03:55:13 -05:00
public :
2019-09-05 10:04:39 -05:00
typedef typename Opm : : Linear : : IstlSparseMatrixAdapter < Block > type ;
2019-05-31 03:55:13 -05:00
} ;
2018-06-21 05:14:17 -05:00
END_PROPERTIES
namespace Opm
{
2019-03-19 09:06:55 -05:00
template < class DenseMatrix >
DenseMatrix transposeDenseMatrix ( const DenseMatrix & M )
{
DenseMatrix tmp ;
for ( int i = 0 ; i < M . rows ; + + i )
for ( int j = 0 ; j < M . cols ; + + j )
tmp [ j ] [ i ] = M [ i ] [ j ] ;
return tmp ;
}
2018-11-12 04:03:54 -06:00
//=====================================================================
// Implementation for ISTL-matrix based operator
//=====================================================================
2019-04-09 07:56:00 -05:00
2018-11-12 04:03:54 -06:00
/*!
\ brief Adapter to turn a matrix into a linear operator .
Adapts a matrix to the assembled linear operator interface
*/
template < class M , class X , class Y , class WellModel , bool overlapping >
class WellModelMatrixAdapter : public Dune : : AssembledLinearOperator < M , X , Y >
{
typedef Dune : : AssembledLinearOperator < M , X , Y > BaseType ;
public :
typedef M matrix_type ;
typedef X domain_type ;
typedef Y range_type ;
typedef typename X : : field_type field_type ;
# if HAVE_MPI
typedef Dune : : OwnerOverlapCopyCommunication < int , int > communication_type ;
# else
typedef Dune : : CollectiveCommunication < int > communication_type ;
# endif
# if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6)
Dune : : SolverCategory : : Category category ( ) const override
{
return overlapping ?
Dune : : SolverCategory : : overlapping : Dune : : SolverCategory : : sequential ;
}
# else
enum {
//! \brief The solver category.
category = overlapping ?
Dune : : SolverCategory : : overlapping :
Dune : : SolverCategory : : sequential
} ;
# endif
//! constructor: just store a reference to a matrix
WellModelMatrixAdapter ( const M & A ,
const M & A_for_precond ,
const WellModel & wellMod ,
2019-06-28 07:20:51 -05:00
const boost : : any & parallelInformation OPM_UNUSED_NOMPI = boost : : any ( ) )
2018-11-12 04:03:54 -06:00
: A_ ( A ) , A_for_precond_ ( A_for_precond ) , wellMod_ ( wellMod ) , comm_ ( )
{
# if HAVE_MPI
if ( parallelInformation . type ( ) = = typeid ( ParallelISTLInformation ) )
{
const ParallelISTLInformation & info =
boost : : any_cast < const ParallelISTLInformation & > ( parallelInformation ) ;
comm_ . reset ( new communication_type ( info . communicator ( ) ) ) ;
}
# endif
}
2019-04-09 14:46:05 -05:00
WellModelMatrixAdapter ( const M & A ,
const M & A_for_precond ,
const WellModel & wellMod ,
std : : shared_ptr < communication_type > comm )
: A_ ( A ) , A_for_precond_ ( A_for_precond ) , wellMod_ ( wellMod ) , comm_ ( comm )
{
}
2018-11-12 04:03:54 -06:00
2019-04-05 04:14:13 -05:00
virtual void apply ( const X & x , Y & y ) const override
2018-11-12 04:03:54 -06:00
{
A_ . mv ( x , y ) ;
// add well model modification to y
wellMod_ . apply ( x , y ) ;
# if HAVE_MPI
if ( comm_ )
comm_ - > project ( y ) ;
# endif
}
// y += \alpha * A * x
2019-04-05 04:14:13 -05:00
virtual void applyscaleadd ( field_type alpha , const X & x , Y & y ) const override
2018-11-12 04:03:54 -06:00
{
A_ . usmv ( alpha , x , y ) ;
// add scaled well model modification to y
wellMod_ . applyScaleAdd ( alpha , x , y ) ;
# if HAVE_MPI
if ( comm_ )
comm_ - > project ( y ) ;
# endif
}
2019-04-05 04:14:13 -05:00
virtual const matrix_type & getmat ( ) const override { return A_for_precond_ ; }
2018-11-12 04:03:54 -06:00
2019-04-09 14:46:05 -05:00
std : : shared_ptr < communication_type > comm ( )
2018-11-12 04:03:54 -06:00
{
2019-04-09 14:46:05 -05:00
return comm_ ;
2018-11-12 04:03:54 -06:00
}
protected :
const matrix_type & A_ ;
const matrix_type & A_for_precond_ ;
2019-05-07 09:59:00 -05:00
const WellModel & wellMod_ ;
2019-04-09 14:46:05 -05:00
std : : shared_ptr < communication_type > comm_ ;
2018-11-12 04:03:54 -06:00
} ;
2018-06-21 05:14:17 -05:00
/// 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 .
template < class TypeTag >
2018-11-15 04:07:47 -06:00
class ISTLSolverEbos
2018-06-21 05:14:17 -05:00
{
2019-04-08 06:44:48 -05:00
protected :
2019-03-14 05:22:07 -05:00
typedef typename GET_PROP_TYPE ( TypeTag , GridView ) GridView ;
2018-06-21 05:14:17 -05:00
typedef typename GET_PROP_TYPE ( TypeTag , Scalar ) Scalar ;
2018-11-12 05:40:11 -06:00
typedef typename GET_PROP_TYPE ( TypeTag , SparseMatrixAdapter ) SparseMatrixAdapter ;
2018-06-21 05:14:17 -05:00
typedef typename GET_PROP_TYPE ( TypeTag , GlobalEqVector ) Vector ;
typedef typename GET_PROP_TYPE ( TypeTag , Indices ) Indices ;
2018-11-12 04:03:54 -06:00
typedef typename GET_PROP_TYPE ( TypeTag , EclWellModel ) WellModel ;
typedef typename GET_PROP_TYPE ( TypeTag , Simulator ) Simulator ;
2018-11-12 05:40:11 -06:00
typedef typename SparseMatrixAdapter : : IstlMatrix Matrix ;
2019-03-14 05:22:07 -05:00
typedef typename SparseMatrixAdapter : : MatrixBlock MatrixBlockType ;
typedef typename Vector : : block_type BlockVector ;
typedef typename GET_PROP_TYPE ( TypeTag , Evaluation ) Evaluation ;
typedef typename GET_PROP_TYPE ( TypeTag , ThreadManager ) ThreadManager ;
typedef typename GridView : : template Codim < 0 > : : Entity Element ;
typedef typename GET_PROP_TYPE ( TypeTag , ElementContext ) ElementContext ;
2019-03-15 05:27:38 -05:00
// Due to miscibility oil <-> gas the water eqn is the one we can replace with a pressure equation.
2019-04-01 05:55:06 -05:00
static const bool waterEnabled = Indices : : waterEnabled ;
2019-04-03 02:09:57 -05:00
static const int pindex = ( waterEnabled ) ? BlackOilDefaultIndexTraits : : waterCompIdx : BlackOilDefaultIndexTraits : : oilCompIdx ;
2019-04-01 05:55:06 -05:00
enum { pressureEqnIndex = pindex } ;
2019-03-15 05:27:38 -05:00
enum { pressureVarIndex = Indices : : pressureSwitchIdx } ;
2018-11-12 04:03:54 -06:00
static const int numEq = Indices : : numEq ;
2019-03-14 05:22:07 -05:00
2019-12-03 07:10:21 -06:00
# if HAVE_CUDA
BdaBridge * bdaBridge ;
# endif
2018-06-21 05:14:17 -05:00
public :
typedef Dune : : AssembledLinearOperator < Matrix , Vector , Vector > AssembledLinearOperatorType ;
2018-06-21 05:14:17 -05:00
static void registerParameters ( )
2018-06-21 05:14:17 -05:00
{
2018-11-15 04:07:47 -06:00
FlowLinearSolverParameters : : registerParameters < TypeTag > ( ) ;
2018-06-21 05:14:17 -05:00
}
/// Construct a system solver.
/// \param[in] parallelInformation In the case of a parallel run
/// with dune-istl the information about the parallelization.
2018-11-12 04:03:54 -06:00
ISTLSolverEbos ( const Simulator & simulator )
: simulator_ ( simulator ) ,
iterations_ ( 0 ) ,
converged_ ( false )
2018-06-21 05:14:17 -05:00
{
2018-06-21 05:14:17 -05:00
parameters_ . template init < TypeTag > ( ) ;
2019-12-03 07:10:21 -06:00
# if HAVE_CUDA
const bool use_gpu = EWOMS_GET_PARAM ( TypeTag , bool , UseGpu ) ;
const int maxit = EWOMS_GET_PARAM ( TypeTag , int , LinearSolverMaxIter ) ;
const double tolerance = EWOMS_GET_PARAM ( TypeTag , double , LinearSolverReduction ) ;
const bool matrix_add_well_contributions = EWOMS_GET_PARAM ( TypeTag , bool , MatrixAddWellContributions ) ;
2019-12-05 02:44:55 -06:00
const int linear_solver_verbosity = parameters_ . linear_solver_verbosity_ ;
2019-12-03 07:10:21 -06:00
if ( use_gpu & & ! matrix_add_well_contributions ) {
2019-12-04 09:59:58 -06:00
std : : cerr < < " Error cannot use GPU solver if command line parameter --matrix-add-well-contributions is false, because the GPU solver performs a standard bicgstab " < < std : : endl ;
2019-12-03 07:10:21 -06:00
exit ( 1 ) ;
}
2019-12-05 02:44:55 -06:00
bdaBridge = new BdaBridge ( use_gpu , linear_solver_verbosity , maxit , tolerance ) ;
2019-12-03 07:10:21 -06:00
# else
const bool use_gpu = EWOMS_GET_PARAM ( TypeTag , bool , UseGpu ) ;
if ( use_gpu ) {
std : : cerr < < " Error cannot use GPU solver since CUDA was not found during compilation " < < std : : endl ;
exit ( 1 ) ;
}
# endif
2018-11-12 04:03:54 -06:00
extractParallelGridInformationToISTL ( simulator_ . vanguard ( ) . grid ( ) , parallelInformation_ ) ;
detail : : findOverlapRowsAndColumns ( simulator_ . vanguard ( ) . grid ( ) , overlapRowAndColumns_ ) ;
}
2019-12-03 07:10:21 -06:00
~ ISTLSolverEbos ( ) {
# if HAVE_CUDA
delete bdaBridge ;
# endif
}
2018-11-12 04:03:54 -06:00
// nothing to clean here
void eraseMatrix ( ) {
matrix_for_preconditioner_ . reset ( ) ;
}
2019-03-14 05:22:07 -05:00
void prepare ( const SparseMatrixAdapter & M , Vector & b )
{
matrix_ . reset ( new Matrix ( M . istlMatrix ( ) ) ) ;
rhs_ = & b ;
this - > scaleSystem ( ) ;
}
void scaleSystem ( )
{
const bool matrix_cont_added = EWOMS_GET_PARAM ( TypeTag , bool , MatrixAddWellContributions ) ;
2019-05-07 09:59:00 -05:00
2019-03-14 05:22:07 -05:00
if ( matrix_cont_added ) {
bool form_cpr = true ;
if ( parameters_ . system_strategy_ = = " quasiimpes " ) {
weights_ = getQuasiImpesWeights ( ) ;
} else if ( parameters_ . system_strategy_ = = " trueimpes " ) {
weights_ = getStorageWeights ( ) ;
} else if ( parameters_ . system_strategy_ = = " simple " ) {
BlockVector bvec ( 1.0 ) ;
weights_ = getSimpleWeights ( bvec ) ;
} else if ( parameters_ . system_strategy_ = = " original " ) {
BlockVector bvec ( 0.0 ) ;
2019-03-15 05:27:38 -05:00
bvec [ pressureEqnIndex ] = 1 ;
2019-03-14 05:22:07 -05:00
weights_ = getSimpleWeights ( bvec ) ;
} else {
2019-03-20 04:24:44 -05:00
if ( parameters_ . system_strategy_ ! = " none " ) {
OpmLog : : warning ( " unknown_system_strategy " , " Unknown linear solver system strategy: ' " + parameters_ . system_strategy_ + " ', applying 'none' strategy. " ) ;
}
2019-03-14 05:22:07 -05:00
form_cpr = false ;
}
if ( parameters_ . scale_linear_system_ ) {
// also scale weights
this - > scaleEquationsAndVariables ( weights_ ) ;
}
2019-03-20 04:24:44 -05:00
if ( form_cpr & & ! ( parameters_ . cpr_use_drs_ ) ) {
2019-03-14 05:22:07 -05:00
scaleMatrixAndRhs ( weights_ ) ;
}
if ( weights_ . size ( ) = = 0 ) {
// if weights are not set cpr_use_drs_=false;
parameters_ . cpr_use_drs_ = false ;
}
} else {
2019-03-14 03:32:32 -05:00
if ( parameters_ . use_cpr_ & & parameters_ . cpr_use_drs_ ) {
OpmLog : : warning ( " DRS_DISABLE " , " Disabling DRS as matrix does not contain well contributions " ) ;
}
parameters_ . cpr_use_drs_ = false ;
2019-03-14 05:22:07 -05:00
if ( parameters_ . scale_linear_system_ ) {
// also scale weights
this - > scaleEquationsAndVariables ( weights_ ) ;
}
}
}
2019-01-18 02:05:23 -06:00
2019-03-14 05:22:07 -05:00
void setResidual ( Vector & /* b */ ) {
// rhs_ = &b; // Must be handled in prepare() instead.
2018-11-12 04:03:54 -06:00
}
2019-01-30 08:13:38 -06:00
void getResidual ( Vector & b ) const {
b = * rhs_ ;
}
2019-03-14 05:22:07 -05:00
void setMatrix ( const SparseMatrixAdapter & /* M */ ) {
// matrix_ = &M.istlMatrix(); // Must be handled in prepare() instead.
2019-01-18 02:05:23 -06:00
}
2018-11-12 04:03:54 -06:00
bool solve ( Vector & x ) {
// Solve system.
const WellModel & wellModel = simulator_ . problem ( ) . wellModel ( ) ;
if ( isParallel ( ) )
{
typedef WellModelMatrixAdapter < Matrix , Vector , Vector , WellModel , true > Operator ;
auto ebosJacIgnoreOverlap = Matrix ( * matrix_ ) ;
//remove ghost rows in local matrix
makeOverlapRowsInvalid ( ebosJacIgnoreOverlap ) ;
//Not sure what actual_mat_for_prec is, so put ebosJacIgnoreOverlap as both variables
//to be certain that correct matrix is used for preconditioning.
Operator opA ( ebosJacIgnoreOverlap , ebosJacIgnoreOverlap , wellModel ,
2019-03-20 04:24:44 -05:00
parallelInformation_ ) ;
2018-11-12 04:03:54 -06:00
assert ( opA . comm ( ) ) ;
solve ( opA , x , * rhs_ , * ( opA . comm ( ) ) ) ;
}
else
2019-03-14 05:22:07 -05:00
{
2019-03-14 03:04:51 -05:00
typedef WellModelMatrixAdapter < Matrix , Vector , Vector , WellModel , false > Operator ;
Operator opA ( * matrix_ , * matrix_ , wellModel ) ;
2018-11-12 04:03:54 -06:00
solve ( opA , x , * rhs_ ) ;
2019-03-14 05:22:07 -05:00
}
2019-03-14 03:04:51 -05:00
if ( parameters_ . scale_linear_system_ ) {
2019-03-14 05:22:07 -05:00
scaleSolution ( x ) ;
2019-03-14 03:04:51 -05:00
}
2018-11-12 04:03:54 -06:00
return converged_ ;
2018-06-21 05:14:17 -05:00
}
2018-07-03 07:00:26 -05:00
2018-06-21 05:14:17 -05:00
/// Solve the system of linear equations Ax = b, with A being the
/// combined derivative matrix of the residual and b
/// being the residual itself.
/// \param[in] residual residual object containing A and b.
/// \return the solution x
/// \copydoc NewtonIterationBlackoilInterface::iterations
int iterations ( ) const { return iterations_ ; }
/// \copydoc NewtonIterationBlackoilInterface::parallelInformation
const boost : : any & parallelInformation ( ) const { return parallelInformation_ ; }
2018-11-12 04:03:54 -06:00
protected :
2018-06-21 05:14:17 -05:00
/// \brief construct the CPR preconditioner and the solver.
/// \tparam P The type of the parallel information.
/// \param parallelInformation the information about the parallelization.
# if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6)
template < Dune : : SolverCategory : : Category category = Dune : : SolverCategory : : sequential ,
class LinearOperator , class POrComm >
# else
template < int category = Dune : : SolverCategory : : sequential , class LinearOperator , class POrComm >
# endif
void constructPreconditionerAndSolve ( LinearOperator & linearOperator ,
Vector & x , Vector & istlb ,
const POrComm & parallelInformation_arg ,
Dune : : InverseOperatorResult & result ) const
{
// Construct scalar product.
# if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6)
auto sp = Dune : : createScalarProduct < Vector , POrComm > ( parallelInformation_arg , category ) ;
# else
typedef Dune : : ScalarProductChooser < Vector , POrComm , category > ScalarProductChooser ;
typedef std : : unique_ptr < typename ScalarProductChooser : : ScalarProduct > SPPointer ;
SPPointer sp ( ScalarProductChooser : : construct ( parallelInformation_arg ) ) ;
# endif
// Communicate if parallel.
parallelInformation_arg . copyOwnerToAll ( istlb , istlb ) ;
# if FLOW_SUPPORT_AMG // activate AMG if either flow_ebos is used or UMFPack is not available
2018-08-02 05:23:02 -05:00
if ( parameters_ . linear_solver_use_amg_ | | parameters_ . use_cpr_ )
2018-06-21 05:14:17 -05:00
{
typedef ISTLUtility : : CPRSelector < Matrix , Vector , Vector , POrComm > CPRSelectorType ;
typedef typename CPRSelectorType : : Operator MatrixOperator ;
std : : unique_ptr < MatrixOperator > opA ;
if ( ! std : : is_same < LinearOperator , MatrixOperator > : : value )
{
// create new operator in case linear operator and matrix operator differ
opA . reset ( CPRSelectorType : : makeOperator ( linearOperator . getmat ( ) , parallelInformation_arg ) ) ;
}
const double relax = parameters_ . ilu_relaxation_ ;
2018-08-02 05:23:02 -05:00
const MILU_VARIANT ilu_milu = parameters_ . ilu_milu_ ;
2018-06-21 05:14:17 -05:00
if ( parameters_ . use_cpr_ )
{
2019-07-08 05:14:40 -05:00
using MatrixType = typename MatrixOperator : : matrix_type ;
2019-03-15 05:27:38 -05:00
using CouplingMetric = Opm : : Amg : : Element < pressureEqnIndex , pressureVarIndex > ;
2019-07-08 05:14:40 -05:00
using CritBase = Dune : : Amg : : SymmetricCriterion < MatrixType , CouplingMetric > ;
2018-06-21 05:14:17 -05:00
using Criterion = Dune : : Amg : : CoarsenCriterion < CritBase > ;
using AMG = typename ISTLUtility
2019-07-08 05:14:40 -05:00
: : BlackoilAmgSelector < MatrixType , Vector , Vector , POrComm , Criterion , pressureEqnIndex , pressureVarIndex > : : AMG ;
2018-06-21 05:14:17 -05:00
std : : unique_ptr < AMG > amg ;
// Construct preconditioner.
Criterion crit ( 15 , 2000 ) ;
2018-08-02 05:23:02 -05:00
constructAMGPrecond < Criterion > ( linearOperator , parallelInformation_arg , amg , opA , relax , ilu_milu ) ;
2018-06-21 05:14:17 -05:00
// Solve.
solve ( linearOperator , x , istlb , * sp , * amg , result ) ;
}
else
{
typedef typename CPRSelectorType : : AMG AMG ;
std : : unique_ptr < AMG > amg ;
// Construct preconditioner.
2018-08-02 05:23:02 -05:00
constructAMGPrecond ( linearOperator , parallelInformation_arg , amg , opA , relax , ilu_milu ) ;
2018-06-21 05:14:17 -05:00
// Solve.
solve ( linearOperator , x , istlb , * sp , * amg , result ) ;
}
}
else
# endif
{
2019-12-03 07:10:21 -06:00
// tries to solve linear system
// solve_system() does nothing if Dune is selected
# if HAVE_CUDA
bdaBridge - > solve_system ( matrix_ . get ( ) , istlb , result ) ;
if ( result . converged ) {
// get result vector x from non-Dune backend, iff solve was successful
bdaBridge - > get_result ( x ) ;
} else {
// CPU fallback, or default case for Dune
2019-12-05 10:08:32 -06:00
OpmLog : : warning ( " cusparseSolver did not converge, now trying Dune to solve current linear system... " ) ;
2019-12-03 07:10:21 -06:00
auto precond = constructPrecond ( linearOperator , parallelInformation_arg ) ;
solve ( linearOperator , x , istlb , * sp , * precond , result ) ;
} // end Dune call
# else
2019-12-04 09:59:58 -06:00
// Construct preconditioner.
2019-12-03 07:10:21 -06:00
auto precond = constructPrecond ( linearOperator , parallelInformation_arg ) ;
2018-06-21 05:14:17 -05:00
// Solve.
solve ( linearOperator , x , istlb , * sp , * precond , result ) ;
2019-12-04 09:59:58 -06:00
# endif
2018-06-21 05:14:17 -05:00
}
}
2019-03-14 03:04:51 -05:00
// 3x3 matrix block inversion was unstable at least 2.3 until and including
// 2.5.0. There may still be some issue with the 4x4 matrix block inversion
// we therefore still use the block inversion in OPM
2018-06-21 05:14:17 -05:00
typedef ParallelOverlappingILU0 < Dune : : BCRSMatrix < Dune : : MatrixBlock < typename Matrix : : field_type ,
2019-03-14 03:04:51 -05:00
Matrix : : block_type : : rows ,
Matrix : : block_type : : cols > > ,
Vector , Vector > SeqPreconditioner ;
2018-06-21 05:14:17 -05:00
template < class Operator >
std : : unique_ptr < SeqPreconditioner > constructPrecond ( Operator & opA , const Dune : : Amg : : SequentialInformation & ) const
{
2018-08-02 05:23:02 -05:00
const double relax = parameters_ . ilu_relaxation_ ;
const int ilu_fillin = parameters_ . ilu_fillin_level_ ;
const MILU_VARIANT ilu_milu = parameters_ . ilu_milu_ ;
const bool ilu_redblack = parameters_ . ilu_redblack_ ;
const bool ilu_reorder_spheres = parameters_ . ilu_reorder_sphere_ ;
std : : unique_ptr < SeqPreconditioner > precond ( new SeqPreconditioner ( opA . getmat ( ) , ilu_fillin , relax , ilu_milu , ilu_redblack , ilu_reorder_spheres ) ) ;
2018-06-21 05:14:17 -05:00
return precond ;
}
# if HAVE_MPI
typedef Dune : : OwnerOverlapCopyCommunication < int , int > Comm ;
# if DUNE_VERSION_NEWER_REV(DUNE_ISTL, 2 , 5, 1)
// 3x3 matrix block inversion was unstable from at least 2.3 until and
// including 2.5.0
typedef ParallelOverlappingILU0 < Matrix , Vector , Vector , Comm > ParPreconditioner ;
# else
typedef ParallelOverlappingILU0 < Dune : : BCRSMatrix < Dune : : MatrixBlock < typename Matrix : : field_type ,
Matrix : : block_type : : rows ,
Matrix : : block_type : : cols > > ,
Vector , Vector , Comm > ParPreconditioner ;
# endif
template < class Operator >
std : : unique_ptr < ParPreconditioner >
constructPrecond ( Operator & opA , const Comm & comm ) const
{
typedef std : : unique_ptr < ParPreconditioner > Pointer ;
2018-08-02 05:23:02 -05:00
const double relax = parameters_ . ilu_relaxation_ ;
const MILU_VARIANT ilu_milu = parameters_ . ilu_milu_ ;
const bool ilu_redblack = parameters_ . ilu_redblack_ ;
const bool ilu_reorder_spheres = parameters_ . ilu_reorder_sphere_ ;
return Pointer ( new ParPreconditioner ( opA . getmat ( ) , comm , relax , ilu_milu , ilu_redblack , ilu_reorder_spheres ) ) ;
2018-06-21 05:14:17 -05:00
}
# endif
template < class LinearOperator , class MatrixOperator , class POrComm , class AMG >
void
2018-08-02 05:23:02 -05:00
constructAMGPrecond ( LinearOperator & /* linearOperator */ , const POrComm & comm , std : : unique_ptr < AMG > & amg , std : : unique_ptr < MatrixOperator > & opA , const double relax , const MILU_VARIANT milu ) const
2018-06-21 05:14:17 -05:00
{
2019-03-15 05:27:38 -05:00
ISTLUtility : : template createAMGPreconditionerPointer < pressureEqnIndex , pressureVarIndex > ( * opA , relax , milu , comm , amg ) ;
2018-06-21 05:14:17 -05:00
}
template < class C , class LinearOperator , class MatrixOperator , class POrComm , class AMG >
void
2018-08-02 05:23:02 -05:00
constructAMGPrecond ( LinearOperator & /* linearOperator */ , const POrComm & comm , std : : unique_ptr < AMG > & amg , std : : unique_ptr < MatrixOperator > & opA , const double relax ,
2019-04-24 10:18:44 -05:00
const MILU_VARIANT /* milu */ ) const
2018-06-21 05:14:17 -05:00
{
2018-08-02 05:23:02 -05:00
ISTLUtility : : template createAMGPreconditionerPointer < C > ( * opA , relax ,
2019-03-12 07:55:11 -05:00
comm , amg , parameters_ , weights_ ) ;
2018-06-21 05:14:17 -05:00
}
/// \brief Solve the system using the given preconditioner and scalar product.
template < class Operator , class ScalarProd , class Precond >
void solve ( Operator & opA , Vector & x , Vector & istlb , ScalarProd & sp , Precond & precond , Dune : : InverseOperatorResult & result ) const
{
// TODO: Revise when linear solvers interface opm-core is done
// Construct linear solver.
// GMRes solver
2019-03-21 06:31:12 -05:00
int verbosity = 0 ;
if ( simulator_ . gridView ( ) . comm ( ) . rank ( ) = = 0 )
verbosity = parameters_ . linear_solver_verbosity_ ;
2018-06-21 05:14:17 -05:00
if ( parameters_ . newton_use_gmres_ ) {
Dune : : RestartedGMResSolver < Vector > linsolve ( opA , sp , precond ,
parameters_ . linear_solver_reduction_ ,
parameters_ . linear_solver_restart_ ,
parameters_ . linear_solver_maxiter_ ,
verbosity ) ;
// Solve system.
linsolve . apply ( x , istlb , result ) ;
}
else { // BiCGstab solver
Dune : : BiCGSTABSolver < Vector > linsolve ( opA , sp , precond ,
parameters_ . linear_solver_reduction_ ,
parameters_ . linear_solver_maxiter_ ,
verbosity ) ;
// Solve system.
linsolve . apply ( x , istlb , result ) ;
}
}
/// Solve the linear system Ax = b, with A being the
/// combined derivative matrix of the residual and b
/// being the residual itself.
/// \param[in] A matrix A
/// \param[inout] x solution to be computed x
/// \param[in] b right hand side b
void solve ( Matrix & A , Vector & x , Vector & b ) const
{
// Parallel version is deactivated until we figure out how to do it properly.
# if HAVE_MPI
if ( parallelInformation_ . type ( ) = = typeid ( ParallelISTLInformation ) )
{
const ParallelISTLInformation & info =
boost : : any_cast < const ParallelISTLInformation & > ( parallelInformation_ ) ;
Comm istlComm ( info . communicator ( ) ) ;
// Construct operator, scalar product and vectors needed.
typedef Dune : : OverlappingSchwarzOperator < Matrix , Vector , Vector , Comm > Operator ;
Operator opA ( A , istlComm ) ;
solve ( opA , x , b , istlComm ) ;
}
else
# endif
{
// Construct operator, scalar product and vectors needed.
Dune : : MatrixAdapter < Matrix , Vector , Vector > opA ( A ) ;
solve ( opA , x , b ) ;
}
}
/// Solve the linear system Ax = b, with A being the
/// combined derivative matrix of the residual and b
/// being the residual itself.
/// \param[in] A matrix A
/// \param[inout] x solution to be computed x
/// \param[in] b right hand side b
template < class Operator , class Comm >
2019-06-28 07:20:51 -05:00
void solve ( Operator & opA OPM_UNUSED_NOMPI ,
Vector & x OPM_UNUSED_NOMPI ,
Vector & b OPM_UNUSED_NOMPI ,
Comm & comm OPM_UNUSED_NOMPI ) const
2018-06-21 05:14:17 -05:00
{
Dune : : InverseOperatorResult result ;
// Parallel version is deactivated until we figure out how to do it properly.
# if HAVE_MPI
if ( parallelInformation_ . type ( ) = = typeid ( ParallelISTLInformation ) )
{
const size_t size = opA . getmat ( ) . N ( ) ;
const ParallelISTLInformation & info =
boost : : any_cast < const ParallelISTLInformation & > ( 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 ) ;
// Construct operator, scalar product and vectors needed.
constructPreconditionerAndSolve < Dune : : SolverCategory : : overlapping > ( opA , x , b , comm , result ) ;
}
else
# endif
{
OPM_THROW ( std : : logic_error , " this method if for parallel solve only " ) ;
}
checkConvergence ( result ) ;
}
/// Solve the linear system Ax = b, with A being the
/// combined derivative matrix of the residual and b
/// being the residual itself.
/// \param[in] A matrix A
/// \param[inout] x solution to be computed x
/// \param[in] b right hand side b
template < class Operator >
void solve ( Operator & opA , Vector & x , Vector & b ) const
{
Dune : : InverseOperatorResult result ;
// Construct operator, scalar product and vectors needed.
Dune : : Amg : : SequentialInformation info ;
constructPreconditionerAndSolve ( opA , x , b , info , result ) ;
checkConvergence ( result ) ;
}
void checkConvergence ( const Dune : : InverseOperatorResult & result ) const
{
// store number of iterations
iterations_ = result . iterations ;
2018-11-12 04:03:54 -06:00
converged_ = result . converged ;
2018-06-21 05:14:17 -05:00
// Check for failure of linear solver.
if ( ! parameters_ . ignoreConvergenceFailure_ & & ! result . converged ) {
const std : : string msg ( " Convergence failure for linear solver. " ) ;
2019-03-01 03:36:29 -06:00
OPM_THROW_NOLOG ( NumericalIssue , msg ) ;
2018-06-21 05:14:17 -05:00
}
}
protected :
2018-11-12 04:03:54 -06:00
bool isParallel ( ) const {
# if HAVE_MPI
return parallelInformation_ . type ( ) = = typeid ( ParallelISTLInformation ) ;
# else
return false ;
# endif
}
/// Zero out off-diagonal blocks on rows corresponding to overlap cells
/// Diagonal blocks on ovelap rows are set to diag(1e100).
void makeOverlapRowsInvalid ( Matrix & ebosJacIgnoreOverlap ) const
{
//value to set on diagonal
2019-07-08 05:14:40 -05:00
Dune : : FieldMatrix < Scalar , numEq , numEq > diag_block ( 0.0 ) ;
2018-11-12 04:03:54 -06:00
for ( int eq = 0 ; eq < numEq ; + + eq )
diag_block [ eq ] [ eq ] = 1.0e100 ;
//loop over precalculated overlap rows and columns
for ( auto row = overlapRowAndColumns_ . begin ( ) ; row ! = overlapRowAndColumns_ . end ( ) ; row + + )
{
int lcell = row - > first ;
//diagonal block set to large value diagonal
ebosJacIgnoreOverlap [ lcell ] [ lcell ] = diag_block ;
//loop over off diagonal blocks in overlap row
for ( auto col = row - > second . begin ( ) ; col ! = row - > second . end ( ) ; + + col )
{
int ncell = * col ;
//zero out block
ebosJacIgnoreOverlap [ lcell ] [ ncell ] = 0.0 ;
}
}
}
2019-03-14 05:22:07 -05:00
// Weights to make approximate pressure equations.
2019-03-20 04:24:44 -05:00
// Calculated from the storage terms (only) of the
// conservation equations, ignoring all other terms.
2019-03-14 05:22:07 -05:00
Vector getStorageWeights ( ) const
{
2019-05-07 09:59:00 -05:00
Vector weights ( rhs_ - > size ( ) ) ;
2019-03-14 05:22:07 -05:00
BlockVector rhs ( 0.0 ) ;
2019-03-15 05:27:38 -05:00
rhs [ pressureVarIndex ] = 1.0 ;
2019-03-14 05:22:07 -05:00
int index = 0 ;
ElementContext elemCtx ( simulator_ ) ;
const auto & vanguard = simulator_ . vanguard ( ) ;
auto elemIt = vanguard . gridView ( ) . template begin < /*codim=*/ 0 > ( ) ;
const auto & elemEndIt = vanguard . gridView ( ) . template end < /*codim=*/ 0 > ( ) ;
for ( ; elemIt ! = elemEndIt ; + + elemIt ) {
const Element & elem = * elemIt ;
elemCtx . updatePrimaryStencil ( elem ) ;
elemCtx . updatePrimaryIntensiveQuantities ( /*timeIdx=*/ 0 ) ;
Dune : : FieldVector < Evaluation , numEq > storage ;
unsigned threadId = ThreadManager : : threadId ( ) ;
simulator_ . model ( ) . localLinearizer ( threadId ) . localResidual ( ) . computeStorage ( storage , elemCtx , /*spaceIdx=*/ 0 , /*timeIdx=*/ 0 ) ;
Scalar extrusionFactor = elemCtx . intensiveQuantities ( 0 , /*timeIdx=*/ 0 ) . extrusionFactor ( ) ;
Scalar scvVolume = elemCtx . stencil ( /*timeIdx=*/ 0 ) . subControlVolume ( 0 ) . volume ( ) * extrusionFactor ;
Scalar storage_scale = scvVolume / elemCtx . simulator ( ) . timeStepSize ( ) ;
MatrixBlockType block ;
double pressure_scale = 50e5 ;
for ( int ii = 0 ; ii < numEq ; + + ii ) {
for ( int jj = 0 ; jj < numEq ; + + jj ) {
block [ ii ] [ jj ] = storage [ ii ] . derivative ( jj ) / storage_scale ;
2019-03-15 05:27:38 -05:00
if ( jj = = pressureVarIndex ) {
2019-03-14 05:22:07 -05:00
block [ ii ] [ jj ] * = pressure_scale ;
}
}
}
BlockVector bweights ;
2019-03-19 09:06:55 -05:00
MatrixBlockType block_transpose = Opm : : transposeDenseMatrix ( block ) ;
2019-03-14 05:22:07 -05:00
block_transpose . solve ( bweights , rhs ) ;
bweights / = 1000.0 ; // given normal densities this scales weights to about 1.
weights [ index ] = bweights ;
+ + index ;
}
return weights ;
}
2019-03-12 07:55:11 -05:00
2019-03-20 04:24:44 -05:00
// Interaction between the CPR weights (the function argument 'weights')
// and the variable and equation weights from
// simulator_.model().primaryVarWeight() and
// simulator_.model().eqWeight() is nontrivial and does not work
// at the moment. Possibly refactoring of ewoms weight treatment
// is needed. In the meantime this function shows what needs to be
// done to integrate the weights properly.
2019-03-14 05:22:07 -05:00
void scaleEquationsAndVariables ( Vector & weights )
{
// loop over primary variables
const auto endi = matrix_ - > end ( ) ;
for ( auto i = matrix_ - > begin ( ) ; i ! = endi ; + + i ) {
const auto endj = ( * i ) . end ( ) ;
BlockVector & brhs = ( * rhs_ ) [ i . index ( ) ] ;
for ( auto j = ( * i ) . begin ( ) ; j ! = endj ; + + j ) {
MatrixBlockType & block = * j ;
for ( std : : size_t ii = 0 ; ii < block . rows ; ii + + ) {
for ( std : : size_t jj = 0 ; jj < block . cols ; jj + + ) {
double var_scale = simulator_ . model ( ) . primaryVarWeight ( i . index ( ) , jj ) ;
block [ ii ] [ jj ] / = var_scale ;
block [ ii ] [ jj ] * = simulator_ . model ( ) . eqWeight ( i . index ( ) , ii ) ;
}
}
}
for ( std : : size_t ii = 0 ; ii < brhs . size ( ) ; ii + + ) {
brhs [ ii ] * = simulator_ . model ( ) . eqWeight ( i . index ( ) , ii ) ;
}
2019-03-20 04:24:44 -05:00
if ( weights . size ( ) = = matrix_ - > N ( ) ) {
2019-03-14 05:22:07 -05:00
BlockVector & bw = weights [ i . index ( ) ] ;
for ( std : : size_t ii = 0 ; ii < brhs . size ( ) ; ii + + ) {
bw [ ii ] / = simulator_ . model ( ) . eqWeight ( i . index ( ) , ii ) ;
}
double abs_max =
* std : : max_element ( bw . begin ( ) , bw . end ( ) , [ ] ( double a , double b ) { return std : : abs ( a ) < std : : abs ( b ) ; } ) ;
bw / = abs_max ;
}
}
}
void scaleSolution ( Vector & x )
{
for ( std : : size_t i = 0 ; i < x . size ( ) ; + + i ) {
auto & bx = x [ i ] ;
for ( std : : size_t jj = 0 ; jj < bx . size ( ) ; jj + + ) {
double var_scale = simulator_ . model ( ) . primaryVarWeight ( i , jj ) ;
bx [ jj ] / = var_scale ;
}
}
}
Vector getQuasiImpesWeights ( )
{
Matrix & A = * matrix_ ;
Vector weights ( rhs_ - > size ( ) ) ;
BlockVector rhs ( 0.0 ) ;
2019-03-15 05:27:38 -05:00
rhs [ pressureVarIndex ] = 1 ;
2019-03-14 05:22:07 -05:00
const auto endi = A . end ( ) ;
for ( auto i = A . begin ( ) ; i ! = endi ; + + i ) {
const auto endj = ( * i ) . end ( ) ;
MatrixBlockType diag_block ( 0.0 ) ;
for ( auto j = ( * i ) . begin ( ) ; j ! = endj ; + + j ) {
if ( i . index ( ) = = j . index ( ) ) {
diag_block = ( * j ) ;
break ;
}
}
BlockVector bweights ;
2019-03-19 09:06:55 -05:00
auto diag_block_transpose = Opm : : transposeDenseMatrix ( diag_block ) ;
2019-03-14 05:22:07 -05:00
diag_block_transpose . solve ( bweights , rhs ) ;
double abs_max =
* std : : max_element ( bweights . begin ( ) , bweights . end ( ) , [ ] ( double a , double b ) { return std : : abs ( a ) < std : : abs ( b ) ; } ) ;
bweights / = std : : abs ( abs_max ) ;
weights [ i . index ( ) ] = bweights ;
}
return weights ;
}
Vector getSimpleWeights ( const BlockVector & rhs )
{
Vector weights ( rhs_ - > size ( ) , 0 ) ;
for ( auto & bw : weights ) {
bw = rhs ;
}
return weights ;
}
void scaleMatrixAndRhs ( const Vector & weights )
{
using Block = typename Matrix : : block_type ;
const auto endi = matrix_ - > end ( ) ;
for ( auto i = matrix_ - > begin ( ) ; i ! = endi ; + + i ) {
const BlockVector & bweights = weights [ i . index ( ) ] ;
BlockVector & brhs = ( * rhs_ ) [ i . index ( ) ] ;
const auto endj = ( * i ) . end ( ) ;
for ( auto j = ( * i ) . begin ( ) ; j ! = endj ; + + j ) {
// assume it is something on all rows
Block & block = ( * j ) ;
2019-03-15 05:27:38 -05:00
BlockVector neweq ( 0.0 ) ;
for ( std : : size_t ii = 0 ; ii < block . rows ; ii + + ) {
for ( std : : size_t jj = 0 ; jj < block . cols ; jj + + ) {
neweq [ jj ] + = bweights [ ii ] * block [ ii ] [ jj ] ;
2019-03-14 05:22:07 -05:00
}
}
2019-03-15 05:27:38 -05:00
block [ pressureEqnIndex ] = neweq ;
2019-03-14 05:22:07 -05:00
}
2019-03-18 09:34:49 -05:00
Scalar newrhs ( 0.0 ) ;
2019-03-14 05:22:07 -05:00
for ( std : : size_t ii = 0 ; ii < brhs . size ( ) ; ii + + ) {
2019-03-15 05:27:38 -05:00
newrhs + = bweights [ ii ] * brhs [ ii ] ;
2019-03-14 05:22:07 -05:00
}
2019-03-18 09:34:49 -05:00
brhs [ pressureEqnIndex ] = newrhs ;
2019-03-14 05:22:07 -05:00
}
}
static void multBlocksInMatrix ( Matrix & ebosJac , const MatrixBlockType & trans , const bool left = true )
{
const int n = ebosJac . N ( ) ;
for ( int row_index = 0 ; row_index < n ; + + row_index ) {
auto & row = ebosJac [ row_index ] ;
auto * dataptr = row . getptr ( ) ;
for ( int elem = 0 ; elem < row . N ( ) ; + + elem ) {
auto & block = dataptr [ elem ] ;
if ( left ) {
block = block . leftmultiply ( trans ) ;
} else {
block = block . rightmultiply ( trans ) ;
}
}
}
}
2019-05-07 09:59:00 -05:00
2019-03-14 05:22:07 -05:00
static void multBlocksVector ( Vector & ebosResid_cp , const MatrixBlockType & leftTrans )
{
for ( auto & bvec : ebosResid_cp ) {
auto bvec_new = bvec ;
leftTrans . mv ( bvec , bvec_new ) ;
bvec = bvec_new ;
}
}
2019-03-12 07:55:11 -05:00
2019-03-14 05:22:07 -05:00
static void scaleCPRSystem ( Matrix & M_cp , Vector & b_cp , const MatrixBlockType & leftTrans )
{
multBlocksInMatrix ( M_cp , leftTrans , true ) ;
multBlocksVector ( b_cp , leftTrans ) ;
}
2019-03-12 07:55:11 -05:00
2018-11-12 04:03:54 -06:00
const Simulator & simulator_ ;
2018-06-21 05:14:17 -05:00
mutable int iterations_ ;
2018-11-12 04:03:54 -06:00
mutable bool converged_ ;
2018-06-21 05:14:17 -05:00
boost : : any parallelInformation_ ;
2019-03-12 07:55:11 -05:00
std : : unique_ptr < Matrix > matrix_ ;
2018-11-12 04:03:54 -06:00
Vector * rhs_ ;
std : : unique_ptr < Matrix > matrix_for_preconditioner_ ;
2018-06-21 05:14:17 -05:00
2018-11-12 04:03:54 -06:00
std : : vector < std : : pair < int , std : : vector < int > > > overlapRowAndColumns_ ;
2018-11-15 04:07:47 -06:00
FlowLinearSolverParameters parameters_ ;
2019-03-12 07:55:11 -05:00
Vector weights_ ;
2019-03-14 03:04:51 -05:00
bool scale_variables_ ;
2018-06-21 05:14:17 -05:00
} ; // end ISTLSolver
} // namespace Opm
# endif