Merge pull request #1852 from atgeirr/flexible-linear-solver

Flexible linear solver
This commit is contained in:
Atgeirr Flø Rasmussen 2019-06-06 14:21:51 +02:00 committed by GitHub
commit 2c5ef367dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2241 additions and 16 deletions

20
.clang-format Normal file
View File

@ -0,0 +1,20 @@
{
BasedOnStyle: WebKit,
AlignAfterOpenBracket: AlwaysBreak,
AlignConsecutiveAssignments: false,
AlignConsecutiveDeclarations: false,
AlignAfterOpenBracket: Align,
AllowShortBlocksOnASingleLine: false,
AllowShortFunctionsOnASingleLine: None,
AlwaysBreakAfterReturnType: TopLevelDefinitions,
AlwaysBreakTemplateDeclarations: Yes,
BinPackArguments: false,
BinPackParameters: false,
BreakBeforeBraces: Linux,
BreakConstructorInitializers: BeforeComma,
ColumnLimit: 120,
Cpp11BracedListStyle: true,
FixNamespaceComments: true,
MaxEmptyLinesToKeep: 5,
NamespaceIndentation: Inner,
}

View File

@ -37,6 +37,7 @@ list (APPEND MAIN_SOURCE_FILES
opm/core/wells/well_controls.c
opm/core/wells/wells.c
opm/simulators/linalg/ExtractParallelGridInformationToISTL.cpp
opm/simulators/linalg/setupPropertyTree.cpp
opm/simulators/timestepping/TimeStepControl.cpp
opm/simulators/timestepping/AdaptiveSimulatorTimer.cpp
opm/simulators/timestepping/SimulatorTimer.cpp
@ -55,6 +56,8 @@ list (APPEND TEST_SOURCE_FILES
tests/test_ecl_output.cc
tests/test_blackoil_amg.cpp
tests/test_convergencereport.cpp
tests/test_flexiblesolver.cpp
tests/test_preconditionerfactory.cpp
tests/test_graphcoloring.cpp
tests/test_vfpproperties.cpp
tests/test_milu.cpp
@ -110,6 +113,10 @@ list (APPEND TEST_DATA_FILES
tests/relpermDiagnostics.DATA
tests/norne_pvt.data
tests/wells_no_perforation.data
tests/matr33.txt
tests/rhs3.txt
tests/options_flexiblesolver.json
tests/options_flexiblesolver_simple.json
)
@ -155,14 +162,25 @@ list (APPEND PUBLIC_HEADER_FILES
opm/simulators/linalg/twolevelmethodcpr.hh
opm/simulators/linalg/CPRPreconditioner.hpp
opm/simulators/linalg/ExtractParallelGridInformationToISTL.hpp
opm/simulators/linalg/FlexibleSolver.hpp
opm/simulators/linalg/FlowLinearSolverParameters.hpp
opm/simulators/linalg/GraphColoring.hpp
opm/simulators/linalg/ISTLSolverEbos.hpp
opm/simulators/linalg/ISTLSolverEbosCpr.hpp
opm/simulators/linalg/ISTLSolverEbosFlexible.hpp
opm/simulators/linalg/MatrixBlock.hpp
opm/simulators/linalg/MatrixMarketUtils.hpp
opm/simulators/linalg/OwningBlockPreconditioner.hpp
opm/simulators/linalg/OwningTwoLevelPreconditioner.hpp
opm/simulators/linalg/ParallelOverlappingILU0.hpp
opm/simulators/linalg/ParallelRestrictedAdditiveSchwarz.hpp
opm/simulators/linalg/ParallelIstlInformation.hpp
opm/simulators/linalg/PressureSolverPolicy.hpp
opm/simulators/linalg/PressureTransferPolicy.hpp
opm/simulators/linalg/PreconditionerFactory.hpp
opm/simulators/linalg/PreconditionerWithUpdate.hpp
opm/simulators/linalg/getQuasiImpesWeights.hpp
opm/simulators/linalg/setupPropertyTree.hpp
opm/simulators/timestepping/AdaptiveSimulatorTimer.hpp
opm/simulators/timestepping/AdaptiveTimeSteppingEbos.hpp
opm/simulators/timestepping/ConvergenceReport.hpp

View File

@ -1,5 +1,5 @@
/*
Copyright 2013, 2014, 2015 SINTEF ICT, Applied Mathematics.
Copyright 2013, 2014, 2015, 2019 SINTEF Digital, Mathematics and Cybernetics.
Copyright 2014 Dr. Blatt - HPC-Simulation-Software & Services
Copyright 2015, 2017 IRIS AS
@ -20,9 +20,12 @@
*/
#include "config.h"
#include "flow/flow_tag.hpp"
//#include <opm/linearsolvers/amgclsolverbackend.hh>
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6)
#include <opm/simulators/linalg/ISTLSolverEbosFlexible.hpp>
#else
#include <opm/simulators/linalg/ISTLSolverEbosCpr.hpp>
//#include <ewoms/linear/superlubackend.hh>
#endif
BEGIN_PROPERTIES
NEW_TYPE_TAG(EclFlowProblemSimple, INHERITS_FROM(EclFlowProblem));
@ -46,10 +49,10 @@ SET_PROP(EclFlowProblemSimple, FluidState)
typedef Opm::BlackOilFluidState<Evaluation, FluidSystem, enableTemperature, enableEnergy, compositionSwitchEnabled, Indices::numPhases > type;
};
SET_BOOL_PROP(EclFlowProblemSimple,MatrixAddWellContributions,true);
SET_INT_PROP(EclFlowProblemSimple,LinearSolverVerbosity,0);
SET_SCALAR_PROP(EclFlowProblemSimple, LinearSolverReduction, 1e-4);
SET_INT_PROP(EclFlowProblemSimple, LinearSolverMaxIter, 20);
SET_BOOL_PROP(EclFlowProblemSimple, MatrixAddWellContributions, true);
SET_INT_PROP(EclFlowProblemSimple, LinearSolverVerbosity,0);
SET_SCALAR_PROP(EclFlowProblemSimple, LinearSolverReduction, 1e-2);
SET_INT_PROP(EclFlowProblemSimple, LinearSolverMaxIter, 100);
SET_BOOL_PROP(EclFlowProblemSimple, UseAmg, true);//probably not used
SET_BOOL_PROP(EclFlowProblemSimple, UseCpr, true);
SET_INT_PROP(EclFlowProblemSimple, CprMaxEllIter, 1);
@ -79,7 +82,11 @@ namespace Ewoms {
//SET_TYPE_PROP(EclFlowProblemSimple, LinearSolverBackend, Ewoms::Linear::ParallelBiCGStabSolverBackend<TypeTag>);//not work
//SET_TYPE_PROP(EclFlowProblemSimple, LinearSolverBackend, Ewoms::Linear::SuperLUBackend<TypeTag>)//not work
//SET_TAG_PROP(EclFlowProblem, FluidState, Opm::BlackOilFluidState);
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6)
SET_TYPE_PROP(EclFlowProblemSimple, LinearSolverBackend, Opm::ISTLSolverEbosFlexible<TypeTag>);
#else
SET_TYPE_PROP(EclFlowProblemSimple, LinearSolverBackend, Opm::ISTLSolverEbosCpr<TypeTag>);
#endif
SET_BOOL_PROP(EclFlowProblemSimple, EnableStorageCache, true);
SET_BOOL_PROP(EclFlowProblemSimple, EnableIntensiveQuantityCache, true);

View File

@ -395,9 +395,9 @@ private:
}
}
void updateAmgPreconditioner(typename AMGType::Operator& op)
void updatePreconditioner()
{
amg_->updateSolver(crit_, op, comm_);
amg_->updateSolver(crit_, op_, comm_);
}
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6)

View File

@ -0,0 +1,175 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_FLEXIBLE_SOLVER_HEADER_INCLUDED
#define OPM_FLEXIBLE_SOLVER_HEADER_INCLUDED
#include <opm/simulators/linalg/PreconditionerFactory.hpp>
#include <dune/common/fmatrix.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/solvers.hh>
#include <dune/istl/umfpack.hh>
#include <boost/property_tree/ptree.hpp>
namespace Dune
{
template <class X, class Y>
class SolverWithUpdate : public Dune::InverseOperator<X, Y>
{
public:
virtual void updatePreconditioner() = 0;
};
template <class MatrixTypeT, class VectorTypeT>
class FlexibleSolver : public Dune::SolverWithUpdate<VectorTypeT, VectorTypeT>
{
public:
using MatrixType = MatrixTypeT;
using VectorType = VectorTypeT;
/// Create a sequential solver.
FlexibleSolver(const boost::property_tree::ptree& prm, const MatrixType& matrix)
{
init(prm, matrix, Dune::Amg::SequentialInformation());
}
/// Create a parallel solver (if Comm is e.g. OwnerOverlapCommunication).
template <class Comm>
FlexibleSolver(const boost::property_tree::ptree& prm, const MatrixType& matrix, const Comm& comm)
{
init(prm, matrix, comm);
}
virtual void apply(VectorType& x, VectorType& rhs, Dune::InverseOperatorResult& res) override
{
linsolver_->apply(x, rhs, res);
}
virtual void apply(VectorType& x, VectorType& rhs, double reduction, Dune::InverseOperatorResult& res) override
{
linsolver_->apply(x, rhs, reduction, res);
}
virtual void updatePreconditioner() override
{
preconditioner_->update();
}
virtual Dune::SolverCategory::Category category() const override
{
return linearoperator_->category();
}
private:
using AbstractOperatorType = Dune::AssembledLinearOperator<MatrixType, VectorType, VectorType>;
using AbstractPrecondType = Dune::PreconditionerWithUpdate<VectorType, VectorType>;
using AbstractScalarProductType = Dune::ScalarProduct<VectorType>;
using AbstractSolverType = Dune::InverseOperator<VectorType, VectorType>;
// Machinery for making sequential or parallel operators/preconditioners/scalar products.
template <class Comm>
void initOpPrecSp(const MatrixType& matrix, const boost::property_tree::ptree& prm, const Comm& comm)
{
// Parallel case.
using ParOperatorType = Dune::OverlappingSchwarzOperator<MatrixType, VectorType, VectorType, Comm>;
auto linop = std::make_shared<ParOperatorType>(matrix, comm);
linearoperator_ = linop;
preconditioner_
= Dune::PreconditionerFactory<ParOperatorType, Comm>::create(*linop, prm.get_child("preconditioner"), comm);
scalarproduct_ = Dune::createScalarProduct<VectorType, Comm>(comm, linearoperator_->category());
}
void initOpPrecSp(const MatrixType& matrix, const boost::property_tree::ptree& prm, const Dune::Amg::SequentialInformation&)
{
// Sequential case.
using SeqOperatorType = Dune::MatrixAdapter<MatrixType, VectorType, VectorType>;
auto linop = std::make_shared<SeqOperatorType>(matrix);
linearoperator_ = linop;
preconditioner_ = Dune::PreconditionerFactory<SeqOperatorType>::create(*linop, prm.get_child("preconditioner"));
scalarproduct_ = std::make_shared<Dune::SeqScalarProduct<VectorType>>();
}
void initSolver(const boost::property_tree::ptree& prm)
{
const double tol = prm.get<double>("tol");
const int maxiter = prm.get<int>("maxiter");
const int verbosity = prm.get<int>("verbosity");
const std::string solver_type = prm.get<std::string>("solver");
if (solver_type == "bicgstab") {
linsolver_.reset(new Dune::BiCGSTABSolver<VectorType>(*linearoperator_,
*scalarproduct_,
*preconditioner_,
tol, // desired residual reduction factor
maxiter, // maximum number of iterations
verbosity));
} else if (solver_type == "loopsolver") {
linsolver_.reset(new Dune::LoopSolver<VectorType>(*linearoperator_,
*scalarproduct_,
*preconditioner_,
tol, // desired residual reduction factor
maxiter, // maximum number of iterations
verbosity));
} else if (solver_type == "gmres") {
int restart = prm.get<int>("restart");
linsolver_.reset(new Dune::RestartedGMResSolver<VectorType>(*linearoperator_,
*scalarproduct_,
*preconditioner_,
tol,
restart, // desired residual reduction factor
maxiter, // maximum number of iterations
verbosity));
#if HAVE_SUITESPARSE_UMFPACK
} else if (solver_type == "umfpack") {
bool dummy = false;
linsolver_.reset(new Dune::UMFPack<MatrixType>(linearoperator_->getmat(), verbosity, dummy));
#endif
} else {
std::string msg("Solver not known ");
msg += solver_type;
throw std::runtime_error(msg);
}
}
// Main initialization routine.
// Call with Comm == Dune::Amg::SequentialInformation to get a serial solver.
template <class Comm>
void init(const boost::property_tree::ptree& prm, const MatrixType& matrix, const Comm& comm)
{
initOpPrecSp(matrix, prm, comm);
initSolver(prm);
}
std::shared_ptr<AbstractOperatorType> linearoperator_;
std::shared_ptr<AbstractPrecondType> preconditioner_;
std::shared_ptr<AbstractScalarProductType> scalarproduct_;
std::shared_ptr<AbstractSolverType> linsolver_;
};
} // namespace Dune
#endif // OPM_FLEXIBLE_SOLVER_HEADER_INCLUDED

View File

@ -66,6 +66,7 @@ NEW_PROP_TAG(CprUseDrs);
NEW_PROP_TAG(CprMaxEllIter);
NEW_PROP_TAG(CprEllSolvetype);
NEW_PROP_TAG(CprReuseSetup);
NEW_PROP_TAG(LinearSolverConfigurationJsonFile);
SET_SCALAR_PROP(FlowIstlSolverParams, LinearSolverReduction, 1e-2);
SET_SCALAR_PROP(FlowIstlSolverParams, IluRelaxation, 0.9);
@ -90,6 +91,7 @@ SET_BOOL_PROP(FlowIstlSolverParams, CprUseDrs, false);
SET_INT_PROP(FlowIstlSolverParams, CprMaxEllIter, 20);
SET_INT_PROP(FlowIstlSolverParams, CprEllSolvetype, 0);
SET_INT_PROP(FlowIstlSolverParams, CprReuseSetup, 0);
SET_STRING_PROP(FlowIstlSolverParams, LinearSolverConfigurationJsonFile, "none");
@ -160,6 +162,7 @@ namespace Opm
bool use_cpr_;
std::string system_strategy_;
bool scale_linear_system_;
std::string linear_solver_configuration_json_file_;
template <class TypeTag>
void init()
@ -186,6 +189,7 @@ namespace Opm
cpr_max_ell_iter_ = EWOMS_GET_PARAM(TypeTag, int, CprMaxEllIter);
cpr_ell_solvetype_ = EWOMS_GET_PARAM(TypeTag, int, CprEllSolvetype);
cpr_reuse_setup_ = EWOMS_GET_PARAM(TypeTag, int, CprReuseSetup);
linear_solver_configuration_json_file_ = EWOMS_GET_PARAM(TypeTag, std::string, LinearSolverConfigurationJsonFile);
}
template <class TypeTag>
@ -212,6 +216,7 @@ namespace Opm
EWOMS_REGISTER_PARAM(TypeTag, int, CprMaxEllIter, "MaxIterations of the elliptic pressure part of the cpr solver");
EWOMS_REGISTER_PARAM(TypeTag, int, CprEllSolvetype, "Solver type of elliptic pressure solve (0: bicgstab, 1: cg, 2: only amg preconditioner)");
EWOMS_REGISTER_PARAM(TypeTag, int, CprReuseSetup, "Reuse Amg Setup");
EWOMS_REGISTER_PARAM(TypeTag, std::string, LinearSolverConfigurationJsonFile, "Filename of JSON configuration for flexible linear solver system.");
}
FlowLinearSolverParameters() { reset(); }

View File

@ -0,0 +1,214 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_ISTLSOLVEREBOSFLEXIBLE_HEADER_INCLUDED
#define OPM_ISTLSOLVEREBOSFLEXIBLE_HEADER_INCLUDED
#include <ewoms/linear/matrixblock.hh>
#include <opm/autodiff/BlackoilDetails.hpp>
#include <opm/simulators/linalg/FlexibleSolver.hpp>
#include <opm/simulators/linalg/setupPropertyTree.hpp>
#include <memory>
#include <utility>
BEGIN_PROPERTIES
NEW_TYPE_TAG(FlowIstlSolverFlexible, INHERITS_FROM(FlowIstlSolverParams));
NEW_PROP_TAG(LinearSolverConfiguration);
NEW_PROP_TAG(GlobalEqVector);
NEW_PROP_TAG(SparseMatrixAdapter);
NEW_PROP_TAG(Simulator);
END_PROPERTIES
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.
///
/// The solvers and preconditioners used are run-time configurable.
template <class TypeTag>
class ISTLSolverEbosFlexible
{
using SparseMatrixAdapter = typename GET_PROP_TYPE(TypeTag, SparseMatrixAdapter);
using VectorType = typename GET_PROP_TYPE(TypeTag, GlobalEqVector);
using Simulator = typename GET_PROP_TYPE(TypeTag, Simulator);
using Scalar = typename GET_PROP_TYPE(TypeTag, Scalar);
using MatrixType = typename SparseMatrixAdapter::IstlMatrix;
#if HAVE_MPI
using Communication = Dune::OwnerOverlapCopyCommunication<int, int>;
#endif
using SolverType = Dune::FlexibleSolver<MatrixType, VectorType>;
public:
static void registerParameters()
{
FlowLinearSolverParameters::registerParameters<TypeTag>();
}
explicit ISTLSolverEbosFlexible(const Simulator& simulator)
: simulator_(simulator)
, comm_(nullptr)
{
parameters_.template init<TypeTag>();
prm_ = setupPropertyTree(parameters_);
extractParallelGridInformationToISTL(simulator_.vanguard().grid(), parallelInformation_);
detail::findOverlapRowsAndColumns(simulator_.vanguard().grid(), overlapRowAndColumns_);
#if HAVE_MPI
if (parallelInformation_.type() == typeid(ParallelISTLInformation)) {
// Parallel case.
const ParallelISTLInformation* parinfo = boost::any_cast<ParallelISTLInformation>(&parallelInformation_);
assert(parinfo);
comm_.reset(new Communication(parinfo->communicator()));
}
#endif
}
void eraseMatrix()
{
}
void prepare(SparseMatrixAdapter& mat, VectorType& b)
{
#if HAVE_MPI
static bool firstcall = true;
if (firstcall && parallelInformation_.type() == typeid(ParallelISTLInformation)) {
// Parallel case.
const ParallelISTLInformation* parinfo = boost::any_cast<ParallelISTLInformation>(&parallelInformation_);
assert(parinfo);
const size_t size = mat.istlMatrix().N();
parinfo->copyValuesTo(comm_->indexSet(), comm_->remoteIndices(), size, 1);
firstcall = false;
}
makeOverlapRowsInvalid(mat.istlMatrix());
#endif
// Decide if we should recreate the solver or just do
// a minimal preconditioner update.
const int newton_iteration = this->simulator_.model().newtonMethod().numIterations();
bool recreate_solver = false;
if (this->parameters_.cpr_reuse_setup_ == 0) {
recreate_solver = true;
} else if (this->parameters_.cpr_reuse_setup_ == 1) {
if (newton_iteration == 0) {
recreate_solver = true;
}
} else if (this->parameters_.cpr_reuse_setup_ == 2) {
if (this->iterations() > 10) {
recreate_solver = true;
}
} else {
assert(this->parameters_.cpr_reuse_setup_ == 3);
assert(recreate_solver == false);
}
if (recreate_solver || !solver_) {
if (isParallel()) {
solver_.reset(new SolverType(prm_, mat.istlMatrix(), *comm_));
} else {
solver_.reset(new SolverType(prm_, mat.istlMatrix()));
}
rhs_ = b;
} else {
solver_->updatePreconditioner();
rhs_ = b;
}
}
bool solve(VectorType& x)
{
solver_->apply(x, rhs_, res_);
return res_.converged;
}
bool isParallel() const
{
#if HAVE_MPI
return parallelInformation_.type() == typeid(ParallelISTLInformation);
#else
return false;
#endif
}
int iterations() const
{
return res_.iterations;
}
void setResidual(VectorType& /* b */)
{
// rhs_ = &b; // Must be handled in prepare() instead.
}
void setMatrix(const SparseMatrixAdapter& /* M */)
{
// matrix_ = &M.istlMatrix(); // Must be handled in prepare() instead.
}
protected:
/// Zero out off-diagonal blocks on rows corresponding to overlap cells
/// Diagonal blocks on ovelap rows are set to diag(1e100).
void makeOverlapRowsInvalid(MatrixType& matrix) const
{
// Value to set on diagonal
const int numEq = MatrixType::block_type::rows;
typename MatrixType::block_type diag_block(0.0);
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
matrix[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
matrix[lcell][ncell] = 0.0;
}
}
}
const Simulator& simulator_;
std::unique_ptr<SolverType> solver_;
FlowLinearSolverParameters parameters_;
boost::property_tree::ptree prm_;
VectorType rhs_;
Dune::InverseOperatorResult res_;
boost::any parallelInformation_;
std::unique_ptr<Communication> comm_;
std::vector<std::pair<int, std::vector<int>>> overlapRowAndColumns_;
}; // end ISTLSolverEbosFlexible
} // namespace Opm
#endif // OPM_ISTLSOLVEREBOSFLEXIBLE_HEADER_INCLUDED

View File

@ -0,0 +1,101 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
Copyright 2019 Dr. Blatt - HPC-Simulation-Software & Services.
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_MATRIXMARKETUTILS_HEADER_INCLUDED
#define OPM_MATRIXMARKETUTILS_HEADER_INCLUDED
#include <dune/istl/matrixmarket.hh>
#include <cstddef>
namespace Dune
{
namespace MatrixMarketImpl
{
template <typename T, typename A, int brows, int bcols, typename D>
void makeSparseEntries(Dune::BCRSMatrix<Dune::FieldMatrix<T, brows, bcols>, A>& matrix,
std::vector<int>& i,
std::vector<int>& j,
std::vector<double>& val,
const D&)
{
// addapted from readSparseEntries
typedef Dune::BCRSMatrix<Dune::FieldMatrix<T, brows, bcols>, A> Matrix;
std::vector<std::set<IndexData<D>>> rows(matrix.N() * brows);
for (std::size_t kk = 0; kk < i.size(); ++kk) {
std::size_t row;
IndexData<D> data;
row = i[kk];
assert(row / bcols < matrix.N());
data.number = val[kk];
data.index = j[kk];
assert(data.index / bcols < matrix.M());
rows[row].insert(data);
}
// Setup the matrix sparsity pattern
int nnz = 0;
for (typename Matrix::CreateIterator iter = matrix.createbegin(); iter != matrix.createend(); ++iter) {
for (std::size_t brow = iter.index() * brows, browend = iter.index() * brows + brows; brow < browend;
++brow) {
typedef typename std::set<IndexData<D>>::const_iterator Siter;
for (Siter siter = rows[brow].begin(), send = rows[brow].end(); siter != send; ++siter, ++nnz)
iter.insert(siter->index / bcols);
}
}
// Set the matrix values
matrix = 0;
MatrixValuesSetter<D, brows, bcols> Setter;
Setter(rows, matrix);
}
} // end namespace MatrixMarketImpl
template <typename T, typename A, int brows, int bcols>
void
makeMatrixMarket(Dune::BCRSMatrix<Dune::FieldMatrix<T, brows, bcols>, A>& matrix,
std::vector<int> i,
std::vector<int> j,
std::vector<T> val,
size_t rows,
size_t cols)
{
// addapted from readMatrixMarket
using namespace MatrixMarketImpl;
// std::size_t rows, cols, entries;
// std::size_t nnz, blockrows, blockcols;
// std::tie(blockrows, blockcols, nnz) = calculateNNZ<brows, bcols>(rows, cols, entries, header);
std::size_t blockrows = rows / brows;
std::size_t blockcols = cols / bcols;
matrix.setSize(blockrows, blockcols);
matrix.setBuildMode(Dune::BCRSMatrix<Dune::FieldMatrix<T, brows, bcols>, A>::row_wise);
makeSparseEntries(matrix, i, j, val, NumericWrapper<T>());
}
} // end namespace Dune
#endif // OPM_MATRIXMARKETUTILS_HEADER_INCLUDED

View File

@ -0,0 +1,86 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_OWNINGBLOCKPRECONDITIONER_HEADER_INCLUDED
#define OPM_OWNINGBLOCKPRECONDITIONER_HEADER_INCLUDED
#include <opm/simulators/linalg/PreconditionerWithUpdate.hpp>
#include <dune/istl/schwarz.hh>
namespace Dune
{
template <class OriginalPreconditioner, class Comm>
class OwningBlockPreconditioner : public PreconditionerWithUpdate<typename OriginalPreconditioner::domain_type,
typename OriginalPreconditioner::range_type>
{
public:
template <class... Args>
OwningBlockPreconditioner(const Comm& comm, Args&&... args)
: orig_precond_(std::forward<Args>(args)...)
, block_precond_(orig_precond_, comm)
{
}
using X = typename OriginalPreconditioner::domain_type;
using Y = typename OriginalPreconditioner::range_type;
virtual void pre(X& x, Y& b) override
{
block_precond_.pre(x, b);
}
virtual void apply(X& v, const Y& d) override
{
block_precond_.apply(v, d);
}
virtual void post(X& x) override
{
block_precond_.post(x);
}
virtual SolverCategory::Category category() const override
{
return block_precond_.category();
}
// The update() function does nothing for a wrapped preconditioner.
virtual void update() override
{
orig_precond_.update();
}
private:
OriginalPreconditioner orig_precond_;
BlockPreconditioner<X, Y, Comm, OriginalPreconditioner> block_precond_;
};
template <class OriginalPreconditioner, class Comm, class... Args>
std::shared_ptr<OwningBlockPreconditioner<OriginalPreconditioner, Comm>>
wrapBlockPreconditioner(const Comm& comm, Args&&... args)
{
return std::make_shared<OwningBlockPreconditioner<OriginalPreconditioner, Comm>>(comm, std::forward<Args>(args)...);
}
} // namespace Dune
#endif // OPM_OWNINGBLOCKPRECONDITIONER_HEADER_INCLUDED

View File

@ -0,0 +1,192 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_OWNINGTWOLEVELPRECONDITIONER_HEADER_INCLUDED
#define OPM_OWNINGTWOLEVELPRECONDITIONER_HEADER_INCLUDED
#include <opm/simulators/linalg/PreconditionerWithUpdate.hpp>
#include <opm/simulators/linalg/PressureSolverPolicy.hpp>
#include <opm/simulators/linalg/PressureTransferPolicy.hpp>
#include <opm/simulators/linalg/getQuasiImpesWeights.hpp>
#include <opm/simulators/linalg/twolevelmethodcpr.hh>
#include <dune/common/fmatrix.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/paamg/amg.hh>
#include <boost/property_tree/ptree.hpp>
#include <fstream>
#include <type_traits>
namespace Dune
{
// Circular dependency between PreconditionerFactory [which can make an OwningTwoLevelPreconditioner]
// and OwningTwoLevelPreconditioner [which uses PreconditionerFactory to choose the fine-level smoother]
// must be broken, accomplished by forward-declaration here.
template <class Operator, class Comm = Dune::Amg::SequentialInformation>
class PreconditionerFactory;
// Must forward-declare FlexibleSolver as we want to use it as solver for the pressure system.
template <class MatrixTypeT, class VectorTypeT>
class FlexibleSolver;
/// A version of the two-level preconditioner that is:
/// - Self-contained, because it owns its policy components.
/// - Flexible, because it uses the runtime-flexible solver
/// and preconditioner factory.
template <class OperatorType,
class VectorType,
bool transpose = false,
class Communication = Dune::Amg::SequentialInformation>
class OwningTwoLevelPreconditioner : public Dune::PreconditionerWithUpdate<VectorType, VectorType>
{
public:
using pt = boost::property_tree::ptree;
using MatrixType = typename OperatorType::matrix_type;
using PrecFactory = PreconditionerFactory<OperatorType, Communication>;
OwningTwoLevelPreconditioner(const OperatorType& linearoperator, const pt& prm)
: linear_operator_(linearoperator)
, finesmoother_(PrecFactory::create(linearoperator, prm.get_child("finesmoother")))
, comm_(nullptr)
, weights_(Opm::Amg::getQuasiImpesWeights<MatrixType, VectorType>(
linearoperator.getmat(), prm.get<int>("pressure_var_index"), transpose))
, levelTransferPolicy_(*comm_, weights_, prm.get<int>("pressure_var_index"))
, coarseSolverPolicy_(prm.get_child("coarsesolver"))
, twolevel_method_(linearoperator,
finesmoother_,
levelTransferPolicy_,
coarseSolverPolicy_,
transpose ? 1 : 0,
transpose ? 0 : 1)
, prm_(prm)
{
if (prm.get<int>("verbosity") > 10) {
std::ofstream outfile(prm.get<std::string>("weights_filename"));
if (!outfile) {
throw std::runtime_error("Could not write weights");
}
Dune::writeMatrixMarket(weights_, outfile);
}
}
OwningTwoLevelPreconditioner(const OperatorType& linearoperator, const pt& prm, const Communication& comm)
: linear_operator_(linearoperator)
, finesmoother_(PrecFactory::create(linearoperator, prm.get_child("finesmoother"), comm))
, comm_(&comm)
, weights_(Opm::Amg::getQuasiImpesWeights<MatrixType, VectorType>(
linearoperator.getmat(), prm.get<int>("pressure_var_index"), transpose))
, levelTransferPolicy_(*comm_, weights_, prm.get<int>("pressure_var_index"))
, coarseSolverPolicy_(prm.get_child("coarsesolver"))
, twolevel_method_(linearoperator,
finesmoother_,
levelTransferPolicy_,
coarseSolverPolicy_,
transpose ? 1 : 0,
transpose ? 0 : 1)
, prm_(prm)
{
if (prm.get<int>("verbosity") > 10) {
std::ofstream outfile(prm.get<std::string>("weights_filename"));
if (!outfile) {
throw std::runtime_error("Could not write weights");
}
Dune::writeMatrixMarket(weights_, outfile);
}
}
virtual void pre(VectorType& x, VectorType& b) override
{
twolevel_method_.pre(x, b);
}
virtual void apply(VectorType& v, const VectorType& d) override
{
twolevel_method_.apply(v, d);
}
virtual void post(VectorType& x) override
{
twolevel_method_.post(x);
}
virtual void update() override
{
Opm::Amg::getQuasiImpesWeights<MatrixType, VectorType>(
linear_operator_.getmat(), prm_.get<int>("pressure_var_index"), transpose, weights_);
updateImpl(comm_);
}
virtual Dune::SolverCategory::Category category() const override
{
return linear_operator_.category();
}
private:
using PressureMatrixType = Dune::BCRSMatrix<Dune::FieldMatrix<double, 1, 1>>;
using PressureVectorType = Dune::BlockVector<Dune::FieldVector<double, 1>>;
using SeqCoarseOperatorType = Dune::MatrixAdapter<PressureMatrixType, PressureVectorType, PressureVectorType>;
using ParCoarseOperatorType
= Dune::OverlappingSchwarzOperator<PressureMatrixType, PressureVectorType, PressureVectorType, Communication>;
using CoarseOperatorType = std::conditional_t<std::is_same<Communication, Dune::Amg::SequentialInformation>::value,
SeqCoarseOperatorType,
ParCoarseOperatorType>;
using LevelTransferPolicy = Opm::PressureTransferPolicy<OperatorType, CoarseOperatorType, Communication, transpose>;
using CoarseSolverPolicy = Dune::Amg::PressureSolverPolicy<CoarseOperatorType,
FlexibleSolver<PressureMatrixType, PressureVectorType>,
LevelTransferPolicy>;
using TwoLevelMethod
= Dune::Amg::TwoLevelMethodCpr<OperatorType, CoarseSolverPolicy, Dune::Preconditioner<VectorType, VectorType>>;
// Handling parallel vs serial instantiation of preconditioner factory.
template <class Comm>
void updateImpl(const Comm*)
{
// Parallel case.
finesmoother_ = PrecFactory::create(linear_operator_, prm_.get_child("finesmoother"), *comm_);
twolevel_method_.updatePreconditioner(finesmoother_, coarseSolverPolicy_);
}
void updateImpl(const Dune::Amg::SequentialInformation*)
{
// Serial case.
finesmoother_ = PrecFactory::create(linear_operator_, prm_.get_child("finesmoother"));
twolevel_method_.updatePreconditioner(finesmoother_, coarseSolverPolicy_);
}
const OperatorType& linear_operator_;
std::shared_ptr<Dune::Preconditioner<VectorType, VectorType>> finesmoother_;
const Communication* comm_;
VectorType weights_;
LevelTransferPolicy levelTransferPolicy_;
CoarseSolverPolicy coarseSolverPolicy_;
TwoLevelMethod twolevel_method_;
boost::property_tree::ptree prm_;
};
} // namespace Dune
#endif // OPM_OWNINGTWOLEVELPRECONDITIONER_HEADER_INCLUDED

View File

@ -0,0 +1,362 @@
/*
Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_PRECONDITIONERFACTORY_HEADER
#define OPM_PRECONDITIONERFACTORY_HEADER
#include <opm/simulators/linalg/OwningBlockPreconditioner.hpp>
#include <opm/simulators/linalg/OwningTwoLevelPreconditioner.hpp>
#include <opm/simulators/linalg/ParallelOverlappingILU0.hpp>
#include <opm/simulators/linalg/PreconditionerWithUpdate.hpp>
#include <opm/simulators/linalg/amgcpr.hh>
#include <dune/istl/paamg/amg.hh>
#include <dune/istl/paamg/fastamg.hh>
#include <dune/istl/preconditioners.hh>
#include <boost/property_tree/ptree.hpp>
#include <map>
#include <memory>
namespace Dune
{
/// This is an object factory for creating preconditioners. The
/// user need only interact with the factory through the static
/// methods addStandardPreconditioners() and create(). In addition
/// a user can call the addCreator() static method to add further
/// preconditioners.
template <class Operator, class Comm>
class PreconditionerFactory
{
public:
/// Linear algebra types.
using Matrix = typename Operator::matrix_type;
using Vector = typename Operator::domain_type; // Assuming symmetry: that domain and range types are the same.
/// The type of pointer returned by create().
using PrecPtr = std::shared_ptr<Dune::PreconditionerWithUpdate<Vector, Vector>>;
/// The type of creator functions passed to addCreator().
using Creator = std::function<PrecPtr(const Operator&, const boost::property_tree::ptree&)>;
using ParCreator = std::function<PrecPtr(const Operator&, const boost::property_tree::ptree&, const Comm&)>;
/// Create a new serial preconditioner and return a pointer to it.
/// \param op operator to be preconditioned.
/// \param prm parameters for the preconditioner, in particular its type.
/// \return (smart) pointer to the created preconditioner.
static PrecPtr create(const Operator& op, const boost::property_tree::ptree& prm)
{
return instance().doCreate(op, prm);
}
/// Create a new parallel preconditioner and return a pointer to it.
/// \param op operator to be preconditioned.
/// \param prm parameters for the preconditioner, in particular its type.
/// \param comm communication object (typically OwnerOverlapCopyCommunication).
/// \return (smart) pointer to the created preconditioner.
static PrecPtr create(const Operator& op, const boost::property_tree::ptree& prm, const Comm& comm)
{
return instance().doCreate(op, prm, comm);
}
/// Add a creator for a serial preconditioner to the PreconditionerFactory.
/// After the call, the user may obtain a preconditioner by
/// calling create() with the given type string as a parameter
/// contained in the property_tree.
/// \param type the type string we want the PreconditionerFactory to
/// associate with the preconditioner.
/// \param creator a function or lambda creating a preconditioner.
static void addCreator(const std::string& type, Creator creator)
{
instance().doAddCreator(type, creator);
}
/// Add a creator for a parallel preconditioner to the PreconditionerFactory.
/// After the call, the user may obtain a preconditioner by
/// calling create() with the given type string as a parameter
/// contained in the property_tree.
/// \param type the type string we want the PreconditionerFactory to
/// associate with the preconditioner.
/// \param creator a function or lambda creating a preconditioner.
static void addCreator(const std::string& type, ParCreator creator)
{
instance().doAddCreator(type, creator);
}
private:
using CriterionBase
= Dune::Amg::AggregationCriterion<Dune::Amg::SymmetricMatrixDependency<Matrix, Dune::Amg::FirstDiagonal>>;
using Criterion = Dune::Amg::CoarsenCriterion<CriterionBase>;
// Helpers for creation of AMG preconditioner.
static Criterion amgCriterion(const boost::property_tree::ptree& prm)
{
Criterion criterion(15, prm.get<int>("coarsenTarget"));
criterion.setDefaultValuesIsotropic(2);
criterion.setAlpha(prm.get<double>("alpha"));
criterion.setBeta(prm.get<double>("beta"));
criterion.setMaxLevel(prm.get<int>("maxlevel"));
criterion.setSkipIsolated(false);
criterion.setDebugLevel(prm.get<int>("verbosity"));
return criterion;
}
template <typename Smoother>
static auto amgSmootherArgs(const boost::property_tree::ptree& prm)
{
using SmootherArgs = typename Dune::Amg::SmootherTraits<Smoother>::Arguments;
SmootherArgs smootherArgs;
smootherArgs.iterations = prm.get<int>("iterations");
// smootherArgs.overlap=SmootherArgs::vertex;
// smootherArgs.overlap=SmootherArgs::none;
// smootherArgs.overlap=SmootherArgs::aggregate;
smootherArgs.relaxationFactor = prm.get<double>("relaxation");
return smootherArgs;
}
template <class Smoother>
static PrecPtr makeAmgPreconditioner(const Operator& op, const boost::property_tree::ptree& prm)
{
auto crit = amgCriterion(prm);
auto sargs = amgSmootherArgs<Smoother>(prm);
return std::make_shared<Dune::Amg::AMGCPR<Operator, Vector, Smoother>>(op, crit, sargs);
}
// Add a useful default set of preconditioners to the factory.
// This is the default template, used for parallel preconditioners.
// (Serial specialization below).
template <class CommArg>
void addStandardPreconditioners(const CommArg*)
{
using namespace Dune;
using O = Operator;
using M = Matrix;
using V = Vector;
using P = boost::property_tree::ptree;
using C = Comm;
doAddCreator("ILU0", [](const O& op, const P& prm, const C& comm) {
const double w = prm.get<double>("relaxation");
return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqILU0<M, V, V>>>(comm, op.getmat(), w);
});
doAddCreator("ParOverILU0", [](const O& op, const P& prm, const C& comm) {
const double w = prm.get<double>("relaxation");
// Already a parallel preconditioner. Need to pass comm, but no need to wrap it in a BlockPreconditioner.
return wrapPreconditioner<Opm::ParallelOverlappingILU0<M, V, V, C>>(
op.getmat(), comm, 0, w, Opm::MILU_VARIANT::ILU);
});
doAddCreator("ILUn", [](const O& op, const P& prm, const C& comm) {
const int n = prm.get<int>("ilulevel");
const double w = prm.get<double>("relaxation");
return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqILUn<M, V, V>>>(comm, op.getmat(), n, w);
});
doAddCreator("Jac", [](const O& op, const P& prm, const C& comm) {
const int n = prm.get<int>("repeats");
const double w = prm.get<double>("relaxation");
return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqJac<M, V, V>>>(comm, op.getmat(), n, w);
});
doAddCreator("GS", [](const O& op, const P& prm, const C& comm) {
const int n = prm.get<int>("repeats");
const double w = prm.get<double>("relaxation");
return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqGS<M, V, V>>>(comm, op.getmat(), n, w);
});
doAddCreator("SOR", [](const O& op, const P& prm, const C& comm) {
const int n = prm.get<int>("repeats");
const double w = prm.get<double>("relaxation");
return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqSOR<M, V, V>>>(comm, op.getmat(), n, w);
});
doAddCreator("SSOR", [](const O& op, const P& prm, const C& comm) {
const int n = prm.get<int>("repeats");
const double w = prm.get<double>("relaxation");
return wrapBlockPreconditioner<DummyUpdatePreconditioner<SeqSSOR<M, V, V>>>(comm, op.getmat(), n, w);
});
doAddCreator("amg", [](const O& op, const P& prm, const C& comm) {
const std::string smoother = prm.get<std::string>("smoother");
if (smoother == "ILU0") {
using Smoother = Opm::ParallelOverlappingILU0<M, V, V, C>;
auto crit = amgCriterion(prm);
auto sargs = amgSmootherArgs<Smoother>(prm);
return std::make_shared<Dune::Amg::AMGCPR<O, V, Smoother, C>>(op, crit, sargs, comm);
} else {
std::string msg("No such smoother: ");
msg += smoother;
throw std::runtime_error(msg);
}
});
doAddCreator("cpr", [](const O& op, const P& prm, const C& comm) {
return std::make_shared<OwningTwoLevelPreconditioner<O, V, false, Comm>>(op, prm, comm);
});
doAddCreator("cprt", [](const O& op, const P& prm, const C& comm) {
return std::make_shared<OwningTwoLevelPreconditioner<O, V, true, Comm>>(op, prm, comm);
});
}
// Add a useful default set of preconditioners to the factory.
// This is the specialization for the serial case.
void addStandardPreconditioners(const Dune::Amg::SequentialInformation*)
{
using namespace Dune;
using O = Operator;
using M = Matrix;
using V = Vector;
using P = boost::property_tree::ptree;
doAddCreator("ILU0", [](const O& op, const P& prm) {
const double w = prm.get<double>("relaxation");
return wrapPreconditioner<SeqILU0<M, V, V>>(op.getmat(), w);
});
doAddCreator("ParOverILU0", [](const O& op, const P& prm) {
const double w = prm.get<double>("relaxation");
return wrapPreconditioner<Opm::ParallelOverlappingILU0<M, V, V>>(op.getmat(), 0, w, Opm::MILU_VARIANT::ILU);
});
doAddCreator("ILUn", [](const O& op, const P& prm) {
const int n = prm.get<int>("ilulevel");
const double w = prm.get<double>("relaxation");
return wrapPreconditioner<SeqILUn<M, V, V>>(op.getmat(), n, w);
});
doAddCreator("Jac", [](const O& op, const P& prm) {
const int n = prm.get<int>("repeats");
const double w = prm.get<double>("relaxation");
return wrapPreconditioner<SeqJac<M, V, V>>(op.getmat(), n, w);
});
doAddCreator("GS", [](const O& op, const P& prm) {
const int n = prm.get<int>("repeats");
const double w = prm.get<double>("relaxation");
return wrapPreconditioner<SeqGS<M, V, V>>(op.getmat(), n, w);
});
doAddCreator("SOR", [](const O& op, const P& prm) {
const int n = prm.get<int>("repeats");
const double w = prm.get<double>("relaxation");
return wrapPreconditioner<SeqSOR<M, V, V>>(op.getmat(), n, w);
});
doAddCreator("SSOR", [](const O& op, const P& prm) {
const int n = prm.get<int>("repeats");
const double w = prm.get<double>("relaxation");
return wrapPreconditioner<SeqSSOR<M, V, V>>(op.getmat(), n, w);
});
doAddCreator("amg", [](const O& op, const P& prm) {
const std::string smoother = prm.get<std::string>("smoother");
if (smoother == "ILU0") {
using Smoother = SeqILU0<M, V, V>;
return makeAmgPreconditioner<Smoother>(op, prm);
} else if (smoother == "Jac") {
using Smoother = SeqJac<M, V, V>;
return makeAmgPreconditioner<Smoother>(op, prm);
} else if (smoother == "SOR") {
using Smoother = SeqSOR<M, V, V>;
return makeAmgPreconditioner<Smoother>(op, prm);
} else if (smoother == "SSOR") {
using Smoother = SeqSSOR<M, V, V>;
return makeAmgPreconditioner<Smoother>(op, prm);
} else if (smoother == "ILUn") {
using Smoother = SeqILUn<M, V, V>;
return makeAmgPreconditioner<Smoother>(op, prm);
} else {
std::string msg("No such smoother: ");
msg += smoother;
throw std::runtime_error(msg);
}
});
doAddCreator("famg", [](const O& op, const P& prm) {
auto crit = amgCriterion(prm);
Dune::Amg::Parameters parms;
parms.setNoPreSmoothSteps(1);
parms.setNoPostSmoothSteps(1);
return wrapPreconditioner<Dune::Amg::FastAMG<O, V>>(op, crit, parms);
});
doAddCreator("cpr", [](const O& op, const P& prm) {
return std::make_shared<OwningTwoLevelPreconditioner<O, V, false>>(op, prm);
});
doAddCreator("cprt", [](const O& op, const P& prm) {
return std::make_shared<OwningTwoLevelPreconditioner<O, V, true>>(op, prm);
});
}
// The method that implements the singleton pattern,
// using the Meyers singleton technique.
static PreconditionerFactory& instance()
{
static PreconditionerFactory singleton;
return singleton;
}
// Private constructor, to keep users from creating a PreconditionerFactory.
PreconditionerFactory()
{
Comm* dummy = nullptr;
addStandardPreconditioners(dummy);
}
// Actually creates the product object.
PrecPtr doCreate(const Operator& op, const boost::property_tree::ptree& prm)
{
const std::string& type = prm.get<std::string>("type");
auto it = creators_.find(type);
if (it == creators_.end()) {
std::ostringstream msg;
msg << "Preconditioner type " << type << " is not registered in the factory. Available types are: ";
for (const auto& prec : creators_) {
msg << prec.first << ' ';
}
msg << std::endl;
throw std::runtime_error(msg.str());
}
return it->second(op, prm);
}
PrecPtr doCreate(const Operator& op, const boost::property_tree::ptree& prm, const Comm& comm)
{
const std::string& type = prm.get<std::string>("type");
auto it = parallel_creators_.find(type);
if (it == parallel_creators_.end()) {
std::ostringstream msg;
msg << "Parallel preconditioner type " << type
<< " is not registered in the factory. Available types are: ";
for (const auto& prec : parallel_creators_) {
msg << prec.first << ' ';
}
msg << std::endl;
throw std::runtime_error(msg.str());
}
return it->second(op, prm, comm);
}
// Actually adds the creator.
void doAddCreator(const std::string& type, Creator c)
{
creators_[type] = c;
}
// Actually adds the creator.
void doAddCreator(const std::string& type, ParCreator c)
{
parallel_creators_[type] = c;
}
// This map contains the whole factory, i.e. all the Creators.
std::map<std::string, Creator> creators_;
std::map<std::string, ParCreator> parallel_creators_;
};
} // namespace Dune
#endif // OPM_PRECONDITIONERFACTORY_HEADER

View File

@ -0,0 +1,91 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_PRECONDITIONERWITHUPDATE_HEADER_INCLUDED
#define OPM_PRECONDITIONERWITHUPDATE_HEADER_INCLUDED
#include <dune/istl/preconditioner.hh>
#include <memory>
namespace Dune
{
/// Interface class adding the update() method to the preconditioner interface.
template <class X, class Y>
class PreconditionerWithUpdate : public Preconditioner<X, Y>
{
public:
virtual void update() = 0;
};
template <class OriginalPreconditioner>
class DummyUpdatePreconditioner : public PreconditionerWithUpdate<typename OriginalPreconditioner::domain_type,
typename OriginalPreconditioner::range_type>
{
public:
template <class... Args>
DummyUpdatePreconditioner(Args&&... args)
: orig_precond_(std::forward<Args>(args)...)
{
}
using X = typename OriginalPreconditioner::domain_type;
using Y = typename OriginalPreconditioner::range_type;
virtual void pre(X& x, Y& b) override
{
orig_precond_.pre(x, b);
}
virtual void apply(X& v, const Y& d) override
{
orig_precond_.apply(v, d);
}
virtual void post(X& x) override
{
orig_precond_.post(x);
}
virtual SolverCategory::Category category() const override
{
return orig_precond_.category();
}
// The update() function does nothing for a wrapped preconditioner.
virtual void update() override
{
}
private:
OriginalPreconditioner orig_precond_;
};
template <class OriginalPreconditioner, class... Args>
std::shared_ptr<DummyUpdatePreconditioner<OriginalPreconditioner>>
wrapPreconditioner(Args&&... args)
{
return std::make_shared<DummyUpdatePreconditioner<OriginalPreconditioner>>(std::forward<Args>(args)...);
}
} // namespace Dune
#endif // OPM_PRECONDITIONERWITHUPDATE_HEADER_INCLUDED

View File

@ -0,0 +1,128 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_PRESSURE_SOLVER_POLICY_HEADER_INCLUDED
#define OPM_PRESSURE_SOLVER_POLICY_HEADER_INCLUDED
#include <opm/simulators/linalg/PressureTransferPolicy.hpp>
#include <boost/property_tree/ptree.hpp>
#include <dune/istl/solver.hh>
namespace Dune
{
namespace Amg
{
namespace pt = boost::property_tree;
template <class OperatorType, class Solver, class LevelTransferPolicy>
class PressureSolverPolicy
{
public:
/** @brief The type of the linear operator used. */
using Operator = OperatorType;
/**
* @brief Constructs the coarse solver policy.
* @param prm Parameter tree specifying the solver details.
*/
explicit PressureSolverPolicy(const pt::ptree prm)
: prm_(prm)
{
}
private:
using X = typename Operator::range_type;
/**
* @brief A wrapper that makes an inverse operator out of AMG.
*
* The operator will use one step of AMG to approximately solve
* the coarse level system.
*/
struct PressureInverseOperator : public Dune::InverseOperator<X, X> {
template <class Comm>
PressureInverseOperator(Operator& op, const boost::property_tree::ptree& prm, const Comm& comm)
: linsolver_()
{
if (op.category() == Dune::SolverCategory::overlapping) {
linsolver_.reset(new Solver(prm, op.getmat(), comm));
} else {
linsolver_.reset(new Solver(prm, op.getmat()));
}
}
Dune::SolverCategory::Category category() const override
{
return Dune::SolverCategory::sequential;
}
void apply(X& x, X& b, double reduction, Dune::InverseOperatorResult& res) override
{
linsolver_->apply(x, b, reduction, res);
}
void apply(X& x, X& b, Dune::InverseOperatorResult& res) override
{
linsolver_->apply(x, b, res);
}
void updatePreconditioner()
{
linsolver_->updatePreconditioner();
}
private:
std::shared_ptr<Solver> linsolver_;
};
public:
/** @brief The type of solver constructed for the coarse level. */
using CoarseLevelSolver = PressureInverseOperator;
/**
* @brief Constructs a coarse level solver.
*
* @param transferPolicy The policy describing the transfer between levels.
* @return A pointer to the constructed coarse level solver.
*/
template <class LTP>
void setCoarseOperator(LTP& transferPolicy)
{
coarseOperator_ = transferPolicy.getCoarseLevelOperator();
}
template <class LTP>
CoarseLevelSolver* createCoarseLevelSolver(LTP& transferPolicy)
{
coarseOperator_ = transferPolicy.getCoarseLevelOperator();
auto& tp = dynamic_cast<LevelTransferPolicy&>(transferPolicy); // TODO: make this unnecessary.
PressureInverseOperator* inv
= new PressureInverseOperator(*coarseOperator_, prm_, tp.getCoarseLevelCommunication());
return inv;
}
private:
/** @brief The coarse level operator. */
std::shared_ptr<Operator> coarseOperator_;
pt::ptree prm_;
};
} // namespace Amg
} // namespace Dune
#endif // OPM_PRESSURE_SOLVER_POLICY_HEADER_INCLUDED

View File

@ -0,0 +1,158 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
Copyright 2019 Dr. Blatt - HPC-Simulation-Software & Services.
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_PRESSURE_TRANSFER_POLICY_HEADER_INCLUDED
#define OPM_PRESSURE_TRANSFER_POLICY_HEADER_INCLUDED
#include <opm/simulators/linalg/twolevelmethodcpr.hh>
namespace Opm
{
template <class FineOperator, class CoarseOperator, class Communication, bool transpose = false>
class PressureTransferPolicy : public Dune::Amg::LevelTransferPolicyCpr<FineOperator, CoarseOperator>
{
public:
typedef Dune::Amg::LevelTransferPolicyCpr<FineOperator, CoarseOperator> ParentType;
typedef Communication ParallelInformation;
typedef typename FineOperator::domain_type FineVectorType;
public:
PressureTransferPolicy(const Communication& comm, const FineVectorType& weights, int pressure_var_index)
: communication_(&const_cast<Communication&>(comm))
, weights_(weights)
, pressure_var_index_(pressure_var_index)
{
}
virtual void createCoarseLevelSystem(const FineOperator& fineOperator) override
{
using CoarseMatrix = typename CoarseOperator::matrix_type;
const auto& fineLevelMatrix = fineOperator.getmat();
coarseLevelMatrix_.reset(new CoarseMatrix(fineLevelMatrix.N(), fineLevelMatrix.M(), CoarseMatrix::row_wise));
auto createIter = coarseLevelMatrix_->createbegin();
for (const auto& row : fineLevelMatrix) {
for (auto col = row.begin(), cend = row.end(); col != cend; ++col) {
createIter.insert(col.index());
}
++createIter;
}
calculateCoarseEntries(fineOperator);
coarseLevelCommunication_.reset(communication_, [](Communication*) {});
this->lhs_.resize(this->coarseLevelMatrix_->M());
this->rhs_.resize(this->coarseLevelMatrix_->N());
using OperatorArgs = typename Dune::Amg::ConstructionTraits<CoarseOperator>::Arguments;
OperatorArgs oargs(*coarseLevelMatrix_, *coarseLevelCommunication_);
this->operator_.reset(Dune::Amg::ConstructionTraits<CoarseOperator>::construct(oargs));
}
virtual void calculateCoarseEntries(const FineOperator& fineOperator) override
{
const auto& fineMatrix = fineOperator.getmat();
*coarseLevelMatrix_ = 0;
auto rowCoarse = coarseLevelMatrix_->begin();
for (auto row = fineMatrix.begin(), rowEnd = fineMatrix.end(); row != rowEnd; ++row, ++rowCoarse) {
assert(row.index() == rowCoarse.index());
auto entryCoarse = rowCoarse->begin();
for (auto entry = row->begin(), entryEnd = row->end(); entry != entryEnd; ++entry, ++entryCoarse) {
assert(entry.index() == entryCoarse.index());
double matrix_el = 0;
if (transpose) {
auto bw = weights_[entry.index()];
for (size_t i = 0; i < bw.size(); ++i) {
matrix_el += (*entry)[pressure_var_index_][i] * bw[i];
}
} else {
auto bw = weights_[row.index()];
for (size_t i = 0; i < bw.size(); ++i) {
matrix_el += (*entry)[i][pressure_var_index_] * bw[i];
}
}
(*entryCoarse) = matrix_el;
}
}
assert(rowCoarse == coarseLevelMatrix_->end());
}
virtual void moveToCoarseLevel(const typename ParentType::FineRangeType& fine) override
{
// Set coarse vector to zero
this->rhs_ = 0;
auto end = fine.end(), begin = fine.begin();
for (auto block = begin; block != end; ++block) {
auto bw = weights_[block.index()];
double rhs_el = 0.0;
if (transpose) {
rhs_el = (*block)[pressure_var_index_];
} else {
for (size_t i = 0; i < block->size(); ++i) {
rhs_el += (*block)[i] * bw[i];
}
}
this->rhs_[block - begin] = rhs_el;
}
this->lhs_ = 0;
}
virtual void moveToFineLevel(typename ParentType::FineDomainType& fine) override
{
auto end = fine.end(), begin = fine.begin();
for (auto block = begin; block != end; ++block) {
if (transpose) {
auto bw = weights_[block.index()];
for (size_t i = 0; i < block->size(); ++i) {
(*block)[i] = this->lhs_[block - begin] * bw[i];
}
} else {
(*block)[pressure_var_index_] = this->lhs_[block - begin];
}
}
}
virtual PressureTransferPolicy* clone() const override
{
return new PressureTransferPolicy(*this);
}
const Communication& getCoarseLevelCommunication() const
{
return *coarseLevelCommunication_;
}
private:
Communication* communication_;
const FineVectorType& weights_;
const int pressure_var_index_;
std::shared_ptr<Communication> coarseLevelCommunication_;
std::shared_ptr<typename CoarseOperator::matrix_type> coarseLevelMatrix_;
};
} // namespace Opm
#endif // OPM_PRESSURE_TRANSFER_POLICY_HEADER_INCLUDED

View File

@ -6,7 +6,8 @@
// NOTE: This file is a modified version of dune/istl/paamg/amg.hh from
// dune-istl release 2.6.0. Modifications have been kept as minimal as possible.
#include <memory>
#include <opm/simulators/linalg/PreconditionerWithUpdate.hpp>
#include <dune/common/exceptions.hh>
#include <dune/istl/paamg/smoother.hh>
#include <dune/istl/paamg/transfer.hh>
@ -19,6 +20,8 @@
#include <dune/common/typetraits.hh>
#include <dune/common/exceptions.hh>
#include <memory>
namespace Dune
{
namespace Amg
@ -47,9 +50,9 @@ namespace Dune
static type* create(const M& mat, bool verbose, bool reusevector )
{
create(mat, verbose, reusevector, std::integral_constant<bool, isDirectSolver>());
return create(mat, verbose, reusevector, std::integral_constant<bool, isDirectSolver>());
}
static type* create(const M& mat, bool verbose, bool reusevector, std::integral_constant<bool, false> )
static type* create(const M& /* mat */, bool /* verbose */, bool /* reusevector */, std::integral_constant<bool, false> )
{
DUNE_THROW(NotImplemented,"DirectSolver not selected");
return nullptr;
@ -130,7 +133,7 @@ namespace Dune
*/
template<class M, class X, class S, class PI=SequentialInformation,
class A=std::allocator<X> >
class AMGCPR : public Preconditioner<X,X>
class AMGCPR : public PreconditionerWithUpdate<X,X>
{
template<class M1, class X1, class S1, class P1, class K1, class A1>
friend class KAMG;
@ -291,7 +294,12 @@ namespace Dune
* @brief Update the coarse solver and the hierarchies.
*/
template<class C>
void updateSolver(C& criterion, Operator& /* matrix */, const PI& pinfo);
void updateSolver(C& criterion, const Operator& /* matrix */, const PI& pinfo);
/**
* @brief Update the coarse solver and the hierarchies.
*/
virtual void update();
/**
* @brief Check whether the coarse solver used is a direct solver.
@ -525,7 +533,13 @@ namespace Dune
template<class M, class X, class S, class PI, class A>
template<class C>
void AMGCPR<M,X,S,PI,A>::updateSolver(C& /* criterion */, Operator& /* matrix */, const PI& /* pinfo */)
void AMGCPR<M,X,S,PI,A>::updateSolver(C& /* criterion */, const Operator& /* matrix */, const PI& /* pinfo */)
{
update();
}
template<class M, class X, class S, class PI, class A>
void AMGCPR<M,X,S,PI,A>::update()
{
Timer watch;
smoothers_.reset(new Hierarchy<Smoother,A>);

View File

@ -0,0 +1,90 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_GET_QUASI_IMPES_WEIGHTS_HEADER_INCLUDED
#define OPM_GET_QUASI_IMPES_WEIGHTS_HEADER_INCLUDED
#include <algorithm>
#include <cmath>
namespace Opm
{
namespace Details
{
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;
}
} // namespace Details
namespace Amg
{
template <class Matrix, class Vector>
void getQuasiImpesWeights(const Matrix& matrix, const int pressureVarIndex, const bool transpose, Vector& weights)
{
using VectorBlockType = typename Vector::block_type;
using MatrixBlockType = typename Matrix::block_type;
const Matrix& A = matrix;
VectorBlockType rhs(0.0);
rhs[pressureVarIndex] = 1.0;
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;
}
}
VectorBlockType bweights;
if (transpose) {
diag_block.solve(bweights, rhs);
} else {
auto diag_block_transpose = Opm::Details::transposeDenseMatrix(diag_block);
diag_block_transpose.solve(bweights, rhs);
}
double abs_max = *std::max_element(
bweights.begin(), bweights.end(), [](double a, double b) { return std::fabs(a) < std::fabs(b); });
bweights /= std::fabs(abs_max);
weights[i.index()] = bweights;
}
// return weights;
}
template <class Matrix, class Vector>
Vector getQuasiImpesWeights(const Matrix& matrix, const int pressureVarIndex, const bool transpose)
{
Vector weights(matrix.N());
getQuasiImpesWeights(matrix, pressureVarIndex, transpose, weights);
return weights;
}
} // namespace Amg
} // namespace Opm
#endif // OPM_GET_QUASI_IMPES_WEIGHTS_HEADER_INCLUDED

View File

@ -0,0 +1,51 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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/>.
*/
#include <config.h>
#include <opm/simulators/linalg/setupPropertyTree.hpp>
#include <boost/property_tree/json_parser.hpp>
namespace Opm
{
/// Set up a property tree intended for FlexibleSolver by either reading
/// the tree from a JSON file or creating a tree giving the default solver
/// and preconditioner. If the latter, the parameters --linear-solver-reduction,
/// --linear-solver-maxiter and --linear-solver-verbosity are used, but if reading
/// from file the data in the JSON file will override any other options.
boost::property_tree::ptree
setupPropertyTree(const FlowLinearSolverParameters& p)
{
boost::property_tree::ptree prm;
if (p.linear_solver_configuration_json_file_ != "none") {
boost::property_tree::read_json(p.linear_solver_configuration_json_file_, prm);
} else {
prm.put("tol", p.linear_solver_reduction_);
prm.put("maxiter", p.linear_solver_maxiter_);
prm.put("verbosity", p.linear_solver_verbosity_);
prm.put("solver", "bicgstab");
prm.put("preconditioner.type", "ParOverILU0");
prm.put("preconditioner.relaxation", 1.0);
}
return prm;
}
} // namespace Opm

View File

@ -0,0 +1,34 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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_SETUPPROPERTYTREE_HEADER_INCLUDED
#define OPM_SETUPPROPERTYTREE_HEADER_INCLUDED
#include <opm/simulators/linalg/FlowLinearSolverParameters.hpp>
#include <boost/property_tree/ptree.hpp>
namespace Opm
{
boost::property_tree::ptree setupPropertyTree(const FlowLinearSolverParameters& p);
} // namespace Opm
#endif // OPM_SETUPPROPERTYTREE_HEADER_INCLUDED

View File

@ -16,6 +16,7 @@
#include<dune/istl/solver.hh>
#include<dune/common/unused.hh>
#include<dune/common/version.hh>
/**
* @addtogroup ISTL_PAAMG
@ -454,13 +455,19 @@ public:
void updatePreconditioner(FineOperatorType& /* op */,
std::shared_ptr<SmootherType> smoother,
CoarseLevelSolverPolicy& coarsePolicy)
{
updatePreconditioner(smoother, coarsePolicy);
}
void updatePreconditioner(std::shared_ptr<SmootherType> smoother,
CoarseLevelSolverPolicy& coarsePolicy)
{
//assume new matrix is not reallocated the new precondition should anyway be made
smoother_ = smoother;
if (coarseSolver_) {
policy_->calculateCoarseEntries(*operator_);
coarsePolicy.setCoarseOperator(*policy_);
coarseSolver_->updateAmgPreconditioner(*(policy_->getCoarseLevelOperator()));
coarseSolver_->updatePreconditioner();
} else {
// we should probably not be heere
policy_->createCoarseLevelSystem(*operator_);

66
tests/matr33.txt Normal file
View File

@ -0,0 +1,66 @@
%%MatrixMarket matrix coordinate real general
% ISTL_STRUCT blocked 3 3
9 9 63
1 1 1.63148e-07
1 2 0.126774
1 3 -6.09774e-05
2 1 4.64465e-10
2 2 -0.0987407
2 3 -5.86038e-05
3 1 5.66966e-08
3 2 -12.0531
3 3 0.0462478
1 4 -8.84841e-11
1 5 -5.64705e-05
1 6 0
2 4 -3.93176e-10
2 5 0
2 6 0
3 4 -4.79944e-08
3 5 0
3 6 0
4 1 -8.89921e-11
4 2 -0.00786145
4 3 0
5 1 -3.53139e-10
5 2 0.0283977
5 3 -3.79289e-05
6 1 -4.31072e-08
6 2 3.46646
6 3 -0.00944335
4 4 1.20862e-10
4 5 0.0829656
4 6 0
5 4 7.40377e-10
5 5 -0.0929483
5 6 -0.0967963
6 4 4.64141e-07
6 5 -9.10276
6 6 16.3772
4 7 -2.168e-11
4 8 -1.38362e-05
4 9 0
5 7 -7.8331e-10
5 8 0
5 9 0
6 7 -1.20687e-07
6 8 0
6 9 0
7 4 -2.17548e-11
7 5 -0.00183118
7 6 0
8 4 -8.00212e-10
8 5 0.0276779
8 6 0.0320899
9 4 -1.56667e-07
9 5 2.7106
9 6 -7.32919
7 7 4.43266e-09
7 8 0.0744616
7 9 0
8 7 1.59375e-07
8 8 -0.0899451
8 9 -0.0940009
9 7 2.86852e-05
9 8 -5.63337
9 9 13.3423

View File

@ -0,0 +1,33 @@
{
"tol": "0.5",
"maxiter": "20",
"preconditioner": {
"type": "cpr",
"finesmoother": {
"type": "ILU0",
"relaxation": "1.0"
},
"coarsesolver": {
"tol": "0.5",
"maxiter": "20",
"preconditioner": {
"type": "amg",
"maxlevel": "5",
"coarsenTarget": "1000",
"smoother": "ILU0",
"alpha": "0.2",
"beta": "0.0001",
"verbosity": "0",
"iterations": "1",
"relaxation": "1"
},
"verbosity": "0",
"solver": "bicgstab"
},
"verbosity": "11",
"weights_filename" : "weight_cpr.txt",
"pressure_var_index" : "1"
},
"verbosity": "10",
"solver": "bicgstab"
}

View File

@ -0,0 +1,9 @@
{
"tol": "1e-12",
"maxiter": "200",
"verbosity": "0",
"solver": "bicgstab",
"preconditioner": {
"type": "nothing"
}
}

12
tests/rhs3.txt Normal file
View File

@ -0,0 +1,12 @@
%%MatrixMarket matrix array real general
% ISTL_STRUCT blocked 3 1
9 1
-4.48332e-07
3.53746e-07
4.31812e-05
1.48051e-07
-5.76325e-07
-0.000243496
-3.81652e-08
1.21761e-06
-9.97615e-05

View File

@ -0,0 +1,123 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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/>.
*/
#include <config.h>
#define BOOST_TEST_MODULE OPM_test_FlexibleSolver
#include <boost/test/unit_test.hpp>
#include <dune/common/version.hh>
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6)
#include <opm/simulators/linalg/FlexibleSolver.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <fstream>
#include <iostream>
template <int bz>
Dune::BlockVector<Dune::FieldVector<double, bz>>
testSolver(const boost::property_tree::ptree& prm, const std::string& matrix_filename, const std::string& rhs_filename)
{
using Matrix = Dune::BCRSMatrix<Dune::FieldMatrix<double, bz, bz>>;
using Vector = Dune::BlockVector<Dune::FieldVector<double, bz>>;
Matrix matrix;
{
std::ifstream mfile(matrix_filename);
if (!mfile) {
throw std::runtime_error("Could not read matrix file");
}
readMatrixMarket(matrix, mfile);
}
Vector rhs;
{
std::ifstream rhsfile(rhs_filename);
if (!rhsfile) {
throw std::runtime_error("Could not read rhs file");
}
readMatrixMarket(rhs, rhsfile);
}
Dune::FlexibleSolver<Matrix, Vector> solver(prm, matrix);
Vector x(rhs.size());
Dune::InverseOperatorResult res;
solver.apply(x, rhs, res);
return x;
}
BOOST_AUTO_TEST_CASE(TestFlexibleSolver)
{
namespace pt = boost::property_tree;
pt::ptree prm;
// Read parameters.
{
std::ifstream file("options_flexiblesolver.json");
pt::read_json(file, prm);
// pt::write_json(std::cout, prm);
}
// Test with 1x1 block solvers.
{
const int bz = 1;
auto sol = testSolver<bz>(prm, "matr33.txt", "rhs3.txt");
Dune::BlockVector<Dune::FieldVector<double, bz>> expected {-1.62493,
-1.76435e-06,
1.86991e-10,
-458.542,
2.28308e-06,
-2.45341e-07,
-1.48005,
-5.02264e-07,
-1.049e-05};
BOOST_REQUIRE_EQUAL(sol.size(), expected.size());
for (size_t i = 0; i < sol.size(); ++i) {
for (int row = 0; row < bz; ++row) {
BOOST_CHECK_CLOSE(sol[i][row], expected[i][row], 1e-3);
}
}
}
// Test with 3x3 block solvers.
{
const int bz = 3;
auto sol = testSolver<bz>(prm, "matr33.txt", "rhs3.txt");
Dune::BlockVector<Dune::FieldVector<double, bz>> expected {{-1.62493, -1.76435e-06, 1.86991e-10},
{-458.542, 2.28308e-06, -2.45341e-07},
{-1.48005, -5.02264e-07, -1.049e-05}};
BOOST_REQUIRE_EQUAL(sol.size(), expected.size());
for (size_t i = 0; i < sol.size(); ++i) {
for (int row = 0; row < bz; ++row) {
BOOST_CHECK_CLOSE(sol[i][row], expected[i][row], 1e-3);
}
}
}
}
#else
// Do nothing if we do not have at least Dune 2.6.
BOOST_AUTO_TEST_CASE(DummyTest)
{
BOOST_REQUIRE(true);
}
#endif

View File

@ -0,0 +1,229 @@
/*
Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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/>.
*/
#include <config.h>
#define BOOST_TEST_MODULE OPM_test_PreconditionerFactory
#include <boost/test/unit_test.hpp>
#include <dune/common/version.hh>
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6)
#include <opm/simulators/linalg/PreconditionerFactory.hpp>
#include <opm/simulators/linalg/FlexibleSolver.hpp>
#include <dune/common/fvector.hh>
#include <dune/istl/bvector.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/matrixmarket.hh>
#include <dune/istl/solvers.hh>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <fstream>
#include <iostream>
template <class X>
class NothingPreconditioner : public Dune::Preconditioner<X, X>
{
public:
virtual void pre(X&, X&) override
{
}
virtual void apply(X& v, const X& d) override
{
v = d;
}
virtual void post(X&) override
{
}
virtual Dune::SolverCategory::Category category() const override
{
return Dune::SolverCategory::sequential;
}
};
template <int bz>
Dune::BlockVector<Dune::FieldVector<double, bz>>
testPrec(const boost::property_tree::ptree& prm, const std::string& matrix_filename, const std::string& rhs_filename)
{
using Matrix = Dune::BCRSMatrix<Dune::FieldMatrix<double, bz, bz>>;
using Vector = Dune::BlockVector<Dune::FieldVector<double, bz>>;
Matrix matrix;
{
std::ifstream mfile(matrix_filename);
if (!mfile) {
throw std::runtime_error("Could not read matrix file");
}
readMatrixMarket(matrix, mfile);
}
Vector rhs;
{
std::ifstream rhsfile(rhs_filename);
if (!rhsfile) {
throw std::runtime_error("Could not read rhs file");
}
readMatrixMarket(rhs, rhsfile);
}
using Operator = Dune::MatrixAdapter<Matrix, Vector, Vector>;
Operator op(matrix);
using PrecFactory = Dune::PreconditionerFactory<Operator>;
auto prec = PrecFactory::create(op, prm.get_child("preconditioner"));
Dune::BiCGSTABSolver<Vector> solver(op, *prec, prm.get<double>("tol"), prm.get<int>("maxiter"), prm.get<int>("verbosity"));
Vector x(rhs.size());
Dune::InverseOperatorResult res;
solver.apply(x, rhs, res);
return x;
}
namespace pt = boost::property_tree;
void test1(const pt::ptree& prm)
{
const int bz = 1;
auto sol = testPrec<bz>(prm, "matr33.txt", "rhs3.txt");
Dune::BlockVector<Dune::FieldVector<double, bz>> expected {-1.62493,
-1.76435e-06,
1.86991e-10,
-458.542,
2.28308e-06,
-2.45341e-07,
-1.48005,
-5.02264e-07,
-1.049e-05};
BOOST_REQUIRE_EQUAL(sol.size(), expected.size());
for (size_t i = 0; i < sol.size(); ++i) {
for (int row = 0; row < bz; ++row) {
BOOST_CHECK_CLOSE(sol[i][row], expected[i][row], 1e-3);
}
}
}
void test3(const pt::ptree& prm)
{
const int bz = 3;
auto sol = testPrec<bz>(prm, "matr33.txt", "rhs3.txt");
Dune::BlockVector<Dune::FieldVector<double, bz>> expected {{-1.62493, -1.76435e-06, 1.86991e-10},
{-458.542, 2.28308e-06, -2.45341e-07},
{-1.48005, -5.02264e-07, -1.049e-05}};
BOOST_REQUIRE_EQUAL(sol.size(), expected.size());
for (size_t i = 0; i < sol.size(); ++i) {
for (int row = 0; row < bz; ++row) {
BOOST_CHECK_CLOSE(sol[i][row], expected[i][row], 1e-3);
}
}
}
BOOST_AUTO_TEST_CASE(TestDefaultPreconditionerFactory)
{
pt::ptree prm;
// Read parameters.
{
std::ifstream file("options_flexiblesolver.json");
pt::read_json(file, prm);
}
// Test with 1x1 block solvers.
test1(prm);
// Test with 3x3 block solvers.
test3(prm);
}
template <int bz>
using M = Dune::BCRSMatrix<Dune::FieldMatrix<double, bz, bz>>;
template <int bz>
using V = Dune::BlockVector<Dune::FieldVector<double, bz>>;
template <int bz>
using O = Dune::MatrixAdapter<M<bz>, V<bz>, V<bz>>;
template <int bz>
using PF = Dune::PreconditionerFactory<O<bz>>;
BOOST_AUTO_TEST_CASE(TestAddingPreconditioner)
{
namespace pt = boost::property_tree;
pt::ptree prm;
// Read parameters.
{
std::ifstream file("options_flexiblesolver_simple.json"); // Requests "nothing" for preconditioner type.
pt::read_json(file, prm);
}
// Test with 1x1 block solvers.
{
const int bz = 1;
BOOST_CHECK_THROW(testPrec<bz>(prm, "matr33.txt", "rhs3.txt"), std::runtime_error);
}
// Test with 3x3 block solvers.
{
const int bz = 3;
BOOST_CHECK_THROW(testPrec<bz>(prm, "matr33.txt", "rhs3.txt"), std::runtime_error);
}
// Add preconditioner to factory for block size 1.
PF<1>::addCreator("nothing", [](const O<1>&, const pt::ptree&) {
return Dune::wrapPreconditioner<NothingPreconditioner<V<1>>>();
});
// Test with 1x1 block solvers.
test1(prm);
// Test with 3x3 block solvers.
{
const int bz = 3;
BOOST_CHECK_THROW(testPrec<bz>(prm, "matr33.txt", "rhs3.txt"), std::runtime_error);
}
// Add preconditioner to factory for block size 3.
PF<3>::addCreator("nothing", [](const O<3>&, const pt::ptree&) {
return Dune::wrapPreconditioner<NothingPreconditioner<V<3>>>();
});
// Test with 1x1 block solvers.
test1(prm);
// Test with 3x3 block solvers.
test3(prm);
}
#else
// Do nothing if we do not have at least Dune 2.6.
BOOST_AUTO_TEST_CASE(DummyTest)
{
BOOST_REQUIRE(true);
}
#endif