First version of a AMG for the Blackoil equations.

The approach is inspired by Geiger's system-amg but we use dune-istl
aggregation AMG for it. On the fine level all unknowns attached to a cell
form a matrix block and are treated fully coupled. To form the first
coarse level system we use only the pressure component to guide the aggregation
and neglect all other unknowns on the fine level. All other level are formed
in the usual way by scalar aggregation.

Currently,it has to be requested for flow_ebos manually by passing
"linear_solver_use_amg=true amg_blackoil_system=true" to it.
This commit is contained in:
Markus Blatt 2017-09-07 09:15:39 +02:00
parent a2419e285f
commit f5d81513da
6 changed files with 1124 additions and 22 deletions

View File

@ -137,6 +137,7 @@ list (APPEND MAIN_SOURCE_FILES
list (APPEND TEST_SOURCE_FILES
tests/test_autodiffhelpers.cpp
tests/test_autodiffmatrix.cpp
tests/test_blackoil_amg.cpp
tests/test_block.cpp
tests/test_boprops_ad.cpp
tests/test_rateconverter.cpp
@ -256,6 +257,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/autodiff/AutoDiffHelpers.hpp
opm/autodiff/AutoDiffMatrix.hpp
opm/autodiff/AutoDiff.hpp
opm/autodiff/BlackoilAmg.hpp
opm/autodiff/BlackoilDetails.hpp
opm/autodiff/BlackoilLegacyDetails.hpp
opm/autodiff/BlackoilModel.hpp

View File

@ -0,0 +1,727 @@
/*
Copyright 2017 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_AMG_HEADER_INCLUDED
#define OPM_AMG_HEADER_INCLUDED
#include <opm/autodiff/ParallelOverlappingILU0.hpp>
#include <opm/autodiff/CPRPreconditioner.hpp>
#include <dune/istl/paamg/twolevelmethod.hh>
#include <dune/istl/paamg/aggregates.hh>
#include <dune/istl/bvector.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/preconditioners.hh>
#include <dune/istl/schwarz.hh>
#include <dune/istl/scalarproducts.hh>
#include <dune/common/fvector.hh>
#include <dune/common/fmatrix.hh>
namespace Dune
{
namespace Amg
{
template<class M, class Norm>
class UnSymmetricCriterion;
}
}
namespace Dune
{
template <class Scalar, int n, int m>
class MatrixBlock;
}
namespace Opm
{
namespace Detail
{
template<typename NonScalarType>
struct ScalarType
{
};
template<typename FieldType, int SIZE>
struct ScalarType<Dune::FieldVector<FieldType, SIZE> >
{
typedef Dune::FieldVector<FieldType, 1> value;
};
template<typename FieldType, int ROWS, int COLS>
struct ScalarType<Dune::FieldMatrix<FieldType, ROWS, COLS> >
{
typedef Dune::FieldMatrix<FieldType, 1, 1> value;
};
template<typename FieldType, int ROWS, int COLS>
struct ScalarType<Dune::MatrixBlock<FieldType, ROWS, COLS> >
{
typedef Dune::MatrixBlock<FieldType, 1, 1> value;
};
template<typename BlockType, typename Allocator>
struct ScalarType<Dune::BCRSMatrix<BlockType, Allocator> >
{
using ScalarBlock = typename ScalarType<BlockType>::value;
using ScalarAllocator = typename Allocator::template rebind<ScalarBlock>::other;
typedef Dune::BCRSMatrix<ScalarBlock,ScalarAllocator> value;
};
template<typename BlockType, typename Allocator>
struct ScalarType<Dune::BlockVector<BlockType, Allocator> >
{
using ScalarBlock = typename ScalarType<BlockType>::value;
using ScalarAllocator = typename Allocator::template rebind<ScalarBlock>::other;
typedef Dune::BlockVector<ScalarBlock,ScalarAllocator> value;
};
template<typename X>
struct ScalarType<Dune::SeqScalarProduct<X> >
{
typedef Dune::SeqScalarProduct<typename ScalarType<X>::value> value;
};
#define ComposeScalarTypeForSeqPrecond(PREC) \
template<typename M, typename X, typename Y, int l> \
struct ScalarType<PREC<M,X,Y,l> > \
{ \
typedef PREC<typename ScalarType<M>::value, \
typename ScalarType<X>::value, \
typename ScalarType<Y>::value, \
l> value; \
};
ComposeScalarTypeForSeqPrecond(Dune::SeqJac);
ComposeScalarTypeForSeqPrecond(Dune::SeqSOR);
ComposeScalarTypeForSeqPrecond(Dune::SeqSSOR);
ComposeScalarTypeForSeqPrecond(Dune::SeqGS);
ComposeScalarTypeForSeqPrecond(Dune::SeqILU0);
ComposeScalarTypeForSeqPrecond(Dune::SeqILUn);
#undef ComposeScalarTypeForSeqPrecond
template<typename X, typename Y>
struct ScalarType<Dune::Richardson<X,Y> >
{
typedef Dune::Richardson<typename ScalarType<X>::value,
typename ScalarType<Y>::value> value;
};
template<class M, class X, class Y, class C>
struct ScalarType<Dune::OverlappingSchwarzOperator<M,X,Y,C> >
{
typedef Dune::OverlappingSchwarzOperator<typename ScalarType<M>::value,
typename ScalarType<X>::value,
typename ScalarType<Y>::value,
C> value;
};
template<class M, class X, class Y>
struct ScalarType<Dune::MatrixAdapter<M,X,Y> >
{
typedef Dune::MatrixAdapter<typename ScalarType<M>::value,
typename ScalarType<X>::value,
typename ScalarType<Y>::value> value;
};
template<class X, class C>
struct ScalarType<Dune::OverlappingSchwarzScalarProduct<X,C> >
{
typedef Dune::OverlappingSchwarzScalarProduct<typename ScalarType<X>::value,
C> value;
};
template<class X, class C>
struct ScalarType<Dune::NonoverlappingSchwarzScalarProduct<X,C> >
{
typedef Dune::NonoverlappingSchwarzScalarProduct<typename ScalarType<X>::value,
C> value;
};
template<class X, class Y, class C, class T>
struct ScalarType<Dune::BlockPreconditioner<X,Y,C,T> >
{
typedef Dune::BlockPreconditioner<typename ScalarType<X>::value,
typename ScalarType<Y>::value,
C,
typename ScalarType<T>::value> value;
};
template<class M, class X, class Y, class C>
struct ScalarType<ParallelOverlappingILU0<M,X,Y,C> >
{
typedef ParallelOverlappingILU0<typename ScalarType<M>::value,
typename ScalarType<X>::value,
typename ScalarType<Y>::value,
C> value;
};
template<class B, class N>
struct ScalarType<Dune::Amg::CoarsenCriterion<Dune::Amg::SymmetricCriterion<Dune::BCRSMatrix<B>,N> > >
{
using value = Dune::Amg::CoarsenCriterion<Dune::Amg::SymmetricCriterion<Dune::BCRSMatrix<typename ScalarType<B>::value>, Dune::Amg::FirstDiagonal> >;
};
template<class B, class N>
struct ScalarType<Dune::Amg::CoarsenCriterion<Dune::Amg::UnSymmetricCriterion<Dune::BCRSMatrix<B>,N> > >
{
using value = Dune::Amg::CoarsenCriterion<Dune::Amg::UnSymmetricCriterion<Dune::BCRSMatrix<typename ScalarType<B>::value>, Dune::Amg::FirstDiagonal> >;
};
template<class C, std::size_t COMPONENT_INDEX>
struct OneComponentCriterionType
{};
template<class B, class N, std::size_t COMPONENT_INDEX>
struct OneComponentCriterionType<Dune::Amg::CoarsenCriterion<Dune::Amg::SymmetricCriterion<Dune::BCRSMatrix<B>,N> >,COMPONENT_INDEX>
{
using value = Dune::Amg::CoarsenCriterion<Dune::Amg::SymmetricCriterion<Dune::BCRSMatrix<B>, Dune::Amg::Diagonal<COMPONENT_INDEX> > >;
};
template<class B, class N, std::size_t COMPONENT_INDEX>
struct OneComponentCriterionType<Dune::Amg::CoarsenCriterion<Dune::Amg::UnSymmetricCriterion<Dune::BCRSMatrix<B>,N> >,COMPONENT_INDEX>
{
using value = Dune::Amg::CoarsenCriterion<Dune::Amg::UnSymmetricCriterion<Dune::BCRSMatrix<B>, Dune::Amg::Diagonal<COMPONENT_INDEX> > >;
};
template<class Operator, class Criterion, class Communication, std::size_t COMPONENT_INDEX>
class OneComponentAggregationLevelTransferPolicy;
/**
* @brief A policy class for solving the coarse level system using one step of AMG.
* @tparam O The type of the linear operator used.
* @tparam S The type of the smoother used in AMG.
* @tparam C The type of the crition used for the aggregation within AMG.
* @tparam C1 The type of the information about the communication. Either
* Dune::OwnerOverlapCopyCommunication or Dune::SequentialInformation.
*/
template<class O, class S, class C, class P>
class OneStepAMGCoarseSolverPolicy
{
public:
typedef P LevelTransferPolicy;
/** @brief The type of the linear operator used. */
typedef O Operator;
/** @brief The type of the range and domain of the operator. */
typedef typename O::range_type X;
/** @brief The type of the crition used for the aggregation within AMG.*/
typedef C Criterion;
/** @brief The type of the communication used for AMG.*/
typedef typename P::ParallelInformation Communication;
/** @brief The type of the smoother used in AMG. */
typedef S Smoother;
/** @brief The type of the arguments used for constructing the smoother. */
typedef typename Dune::Amg::SmootherTraits<S>::Arguments SmootherArgs;
/** @brief The type of the AMG construct on the coarse level.*/
typedef Dune::Amg::AMG<Operator,X,Smoother,Communication> AMGType;
/**
* @brief Constructs the coarse solver policy.
* @param args The arguments used for constructing the smoother.
* @param c The crition used for the aggregation within AMG.
*/
OneStepAMGCoarseSolverPolicy(const SmootherArgs& args, const Criterion& c)
: smootherArgs_(args), criterion_(c)
{}
/** @brief Copy constructor. */
OneStepAMGCoarseSolverPolicy(const OneStepAMGCoarseSolverPolicy& other)
: coarseOperator_(other.coarseOperator_), smootherArgs_(other.smootherArgs_),
criterion_(other.criterion_)
{}
private:
/**
* @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 AMGInverseOperator : public Dune::InverseOperator<X,X>
{
AMGInverseOperator(const typename AMGType::Operator& op,
const Criterion& crit,
const typename AMGType::SmootherArgs& args,
const Communication& comm)
: amg_(op, crit,args, comm), op_(op), comm_(comm), first_(true)
{}
void apply(X& x, X& b, double reduction, Dune::InverseOperatorResult& res)
{
DUNE_UNUSED_PARAMETER(reduction);
DUNE_UNUSED_PARAMETER(res);
/*
if(first_)
{
amg_.pre(x,b);
first_=false;
x_=x;
}
amg_.apply(x,b);
*/
using Chooser = Dune::ScalarProductChooser<X,Communication,AMGType::category>;
auto sp = Chooser::construct(comm_);
Dune::BiCGSTABSolver<X> solver(const_cast<typename AMGType::Operator&>(op_), *sp, amg_, 1e-2, 25, 0);
solver.apply(x,b,res);
delete sp;
}
void apply(X& x, X& b, Dune::InverseOperatorResult& res)
{
return apply(x,b,1e-8,res);
}
~AMGInverseOperator()
{
/*
if(!first_)
amg_.post(x_);
*/
}
AMGInverseOperator(const AMGInverseOperator& other)
: x_(other.x_), amg_(other.amg_), first_(other.first_)
{
}
private:
X x_;
AMGType amg_;
const typename AMGType::Operator& op_;
const Communication& comm_;
bool first_;
};
public:
/** @brief The type of solver constructed for the coarse level. */
typedef AMGInverseOperator CoarseLevelSolver;
/**
* @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>
CoarseLevelSolver* createCoarseLevelSolver(LTP& transferPolicy)
{
coarseOperator_=transferPolicy.getCoarseLevelOperator();
const LevelTransferPolicy& transfer =
reinterpret_cast<const LevelTransferPolicy&>(transferPolicy);
AMGInverseOperator* inv = new AMGInverseOperator(*coarseOperator_,
criterion_,
smootherArgs_,
transfer.getCoarseLevelCommunication());
return inv; //std::shared_ptr<InverseOperator<X,X> >(inv);
}
private:
/** @brief The coarse level operator. */
std::shared_ptr<Operator> coarseOperator_;
/** @brief The arguments used to construct the smoother. */
SmootherArgs smootherArgs_;
/** @brief The coarsening criterion. */
Criterion criterion_;
};
template<class Smoother, class Operator, class Communication>
Smoother* constructSmoother(const Operator& op,
const typename Dune::Amg::SmootherTraits<Smoother>::Arguments& smargs,
const Communication& comm)
{
typename Dune::Amg::ConstructionTraits<Smoother>::Arguments args;
args.setMatrix(op.getmat());
args.setComm(comm);
args.setArgs(smargs);
return Dune::Amg::ConstructionTraits<Smoother>::construct(args);
}
template<class G, class C, class S>
const Dune::Amg::OverlapVertex<typename G::VertexDescriptor>*
buildOverlapVertices(const G& graph, const C& pinfo,
Dune::Amg::AggregatesMap<typename G::VertexDescriptor>& aggregates,
const S& overlap,
std::size_t& overlapCount)
{
// count the overlap vertices.
auto end = graph.end();
overlapCount = 0;
const auto& lookup=pinfo.globalLookup();
for(auto vertex=graph.begin(); vertex != end; ++vertex) {
const auto* pair = lookup.pair(*vertex);
if(pair!=0 && overlap.contains(pair->local().attribute()))
++overlapCount;
}
// Allocate space
using Vertex = typename G::VertexDescriptor;
using OverlapVertex = Dune::Amg::OverlapVertex<Vertex>;
auto* overlapVertices = new OverlapVertex[overlapCount=0 ? 1 : overlapCount];
if(overlapCount==0)
return overlapVertices;
// Initialize them
overlapCount=0;
for(auto vertex=graph.begin(); vertex != end; ++vertex) {
const auto* pair = lookup.pair(*vertex);
if(pair!=0 && overlap.contains(pair->local().attribute())) {
overlapVertices[overlapCount].aggregate = &aggregates[pair->local()];
overlapVertices[overlapCount].vertex = pair->local();
++overlapCount;
}
}
std::sort(overlapVertices, overlapVertices+overlapCount,
[](const OverlapVertex& v1, const OverlapVertex& v2)
{
return *v1.aggregate < *v2.aggregate;
});
// due to the sorting the isolated aggregates (to be skipped) are at the end.
return overlapVertices;
}
template<class M, class G, class V, class C, class S>
void buildCoarseSparseMatrix(M& coarseMatrix, G& fineGraph,
const V& visitedMap,
const C& pinfo,
Dune::Amg::AggregatesMap<typename G::VertexDescriptor>& aggregates,
const S& overlap)
{
using OverlapVertex = Dune::Amg ::OverlapVertex<typename G::VertexDescriptor>;
std::size_t count;
const OverlapVertex* overlapVertices = buildOverlapVertices(fineGraph,
pinfo,
aggregates,
overlap,
count);
// Reset the visited flags of all vertices.
// As the isolated nodes will be skipped we simply mark them as visited
#ifndef NDEBUG
const auto UNAGGREGATED = Dune::Amg::AggregatesMap<typename G::VertexDescriptor>::UNAGGREGATED;
#endif
const auto ISOLATED = Dune::Amg::AggregatesMap<typename G::VertexDescriptor>::ISOLATED;
auto vend = fineGraph.end();
for(auto vertex = fineGraph.begin(); vertex != vend; ++vertex) {
assert(aggregates[*vertex] != UNAGGREGATED);
put(visitedMap, *vertex, aggregates[*vertex]==ISOLATED);
}
Dune::Amg::SparsityBuilder<M> sparsityBuilder(coarseMatrix);
Dune::Amg::ConnectivityConstructor<G,C>::examine(fineGraph, visitedMap, pinfo,
aggregates, overlap,
overlapVertices,
overlapVertices+count,
sparsityBuilder);
delete[] overlapVertices;
}
template<class M, class G, class V, class S>
void buildCoarseSparseMatrix(M& coarseMatrix, G& fineGraph, const V& visitedMap,
const Dune::Amg::SequentialInformation& pinfo,
Dune::Amg::AggregatesMap<typename G::VertexDescriptor>& aggregates,
const S&)
{
// Reset the visited flags of all vertices.
// As the isolated nodes will be skipped we simply mark them as visited
#ifndef NDEBUG
const auto UNAGGREGATED = Dune::Amg::AggregatesMap<typename G::VertexDescriptor>::UNAGGREGATED;
#endif
const auto ISOLATED = Dune::Amg::AggregatesMap<typename G::VertexDescriptor>::ISOLATED;
using Vertex = typename G::VertexIterator;
Vertex vend = fineGraph.end();
for(Vertex vertex = fineGraph.begin(); vertex != vend; ++vertex) {
assert(aggregates[*vertex] != UNAGGREGATED);
put(visitedMap, *vertex, aggregates[*vertex]==ISOLATED);
}
Dune::Amg::SparsityBuilder<M> sparsityBuilder(coarseMatrix);
Dune::Amg::ConnectivityConstructor<G,Dune::Amg::SequentialInformation>
::examine(fineGraph, visitedMap, pinfo, aggregates, sparsityBuilder);
}
} // end namespace Detail
/**
* @brief A LevelTransferPolicy that uses aggregation to construct the coarse level system.
* @tparam Operator The type of the fine level operator.
* @tparam Criterion The criterion that describes the aggregation procedure.
* @tparam Communication The class that describes the communication pattern.
*/
template<class Operator, class Criterion, class Communication, std::size_t COMPONENT_INDEX>
class OneComponentAggregationLevelTransferPolicy
: public Dune::Amg::LevelTransferPolicy<Operator, typename Detail::ScalarType<Operator>::value>
{
typedef Dune::Amg::AggregatesMap<typename Operator::matrix_type::size_type> AggregatesMap;
public:
using CoarseOperator = typename Detail::ScalarType<Operator>::value;
typedef Dune::Amg::LevelTransferPolicy<Operator,CoarseOperator> FatherType;
typedef Communication ParallelInformation;
public:
OneComponentAggregationLevelTransferPolicy(const Criterion& crit, const Communication& comm)
: criterion_(crit), communication_(&const_cast<Communication&>(comm))
{}
void createCoarseLevelSystem(const Operator& fineOperator)
{
prolongDamp_ = 1;
typedef Dune::Amg::PropertiesGraphCreator<Operator> GraphCreator;
typedef typename GraphCreator::PropertiesGraph PropertiesGraph;
typedef typename GraphCreator::GraphTuple GraphTuple;
typedef typename PropertiesGraph::VertexDescriptor Vertex;
std::vector<bool> excluded(fineOperator.getmat().N(), false);
using OverlapFlags = Dune::NegateSet<typename ParallelInformation::OwnerSet>;
GraphTuple graphs = GraphCreator::create(fineOperator, excluded,
*communication_, OverlapFlags());
aggregatesMap_.reset(new AggregatesMap(std::get<1>(graphs)->maxVertex()+1));
int noAggregates, isoAggregates, oneAggregates, skippedAggregates;
using std::get;
std::tie(noAggregates, isoAggregates, oneAggregates, skippedAggregates) =
aggregatesMap_->buildAggregates(fineOperator.getmat(), *(get<1>(graphs)),
criterion_, true);
std::cout<<"no aggregates="<<noAggregates<<" iso="<<isoAggregates<<" one="<<oneAggregates<<" skipped="<<skippedAggregates<<std::endl;
using CommunicationArgs = typename Dune::Amg::ConstructionTraits<Communication>::Arguments;
CommunicationArgs commArgs(communication_->communicator(), communication_->getSolverCategory());
coarseLevelCommunication_.reset(Dune::Amg::ConstructionTraits<Communication>::construct(commArgs));
using Iterator = typename std::vector<bool>::iterator;
auto visitedMap = get(Dune::Amg::VertexVisitedTag(), *(get<1>(graphs)));
communication_->buildGlobalLookup(fineOperator.getmat().N());
std::size_t aggregates =
Dune::Amg::IndicesCoarsener<ParallelInformation,OverlapFlags>
::coarsen(*communication_, *get<1>(graphs), visitedMap,
*aggregatesMap_, *coarseLevelCommunication_,
noAggregates);
GraphCreator::free(graphs);
coarseLevelCommunication_->buildGlobalLookup(aggregates);
Dune::Amg::AggregatesPublisher<Vertex,OverlapFlags,ParallelInformation>
::publish(*aggregatesMap_,
*communication_,
coarseLevelCommunication_->globalLookup());
std::vector<bool>& visited=excluded;
std::fill(visited.begin(), visited.end(), false);
Dune::IteratorPropertyMap<Iterator, Dune::IdentityMap>
visitedMap2(visited.begin(), Dune::IdentityMap());
using CoarseMatrix = typename CoarseOperator::matrix_type;
coarseLevelMatrix_.reset(new CoarseMatrix(aggregates, aggregates,
CoarseMatrix::row_wise));
Detail::buildCoarseSparseMatrix(*coarseLevelMatrix_, *get<0>(graphs), visitedMap2,
*communication_,
*aggregatesMap_,
OverlapFlags());
delete get<0>(graphs);
communication_->freeGlobalLookup();
if( static_cast<int>(this->coarseLevelMatrix_->N())
< criterion_.coarsenTarget())
{
coarseLevelCommunication_->freeGlobalLookup();
}
calculateCoarseEntries(fineOperator.getmat());
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));
}
template<class M>
void calculateCoarseEntries(const M& fineMatrix)
{
*coarseLevelMatrix_ = 0;
for(auto row = fineMatrix.begin(), rowEnd = fineMatrix.end();
row != rowEnd; ++row)
{
const auto& i = (*aggregatesMap_)[row.index()];
if(i != AggregatesMap::ISOLATED)
{
for(auto entry = row->begin(), entryEnd = row->end();
entry != entryEnd; ++entry)
{
const auto& j = (*aggregatesMap_)[entry.index()];
if ( j != AggregatesMap::ISOLATED )
{
(*coarseLevelMatrix_)[i][j] += (*entry)[COMPONENT_INDEX][COMPONENT_INDEX];
}
}
}
}
}
void moveToCoarseLevel(const typename FatherType::FineRangeType& fine)
{
// Set coarse vector to zero
this->rhs_=0;
auto end = fine.end(), begin=fine.begin();
for(auto block=begin; block != end; ++block)
{
const auto& vertex = (*aggregatesMap_)[block-begin];
if(vertex != AggregatesMap::ISOLATED)
{
this->rhs_[vertex] += (*block)[COMPONENT_INDEX];
}
}
this->lhs_=0;
}
void moveToFineLevel(typename FatherType::FineDomainType& fine)
{
//for(auto& entry: this->lhs_)
// entry*=prolongDamp_;
this->lhs_ *= prolongDamp_;
auto end=fine.end(), begin=fine.begin();
for(auto block=begin; block != end; ++block)
{
const auto& vertex = (*aggregatesMap_)[block-begin];
if(vertex != AggregatesMap::ISOLATED)
(*block)[COMPONENT_INDEX] += this->lhs_[vertex];
}
communication_->copyOwnerToAll(fine,fine);
}
OneComponentAggregationLevelTransferPolicy* clone() const
{
return new OneComponentAggregationLevelTransferPolicy(*this);
}
const Communication& getCoarseLevelCommunication() const
{
return *coarseLevelCommunication_;
}
private:
typename Operator::matrix_type::field_type prolongDamp_;
std::shared_ptr<AggregatesMap> aggregatesMap_;
Criterion criterion_;
Communication* communication_;
std::shared_ptr<Communication> coarseLevelCommunication_;
std::shared_ptr<typename CoarseOperator::matrix_type> coarseLevelMatrix_;
};
template<typename O, typename S, typename C,
typename P, std::size_t COMPONENT_INDEX>
class BlackoilAmg
: public Dune::Preconditioner<typename O::domain_type, typename O::range_type>
{
public:
using Operator = O;
using Criterion = C;
using Communication = P;
using Smoother = S;
protected:
using SmootherArgs = typename Dune::Amg::SmootherTraits<Smoother>::Arguments;
using CoarseOperator = typename Detail::ScalarType<Operator>::value;
using CoarseSmoother = typename Detail::ScalarType<Smoother>::value;
using FineCriterion =
typename Detail::OneComponentCriterionType<Criterion,COMPONENT_INDEX>::value;
using CoarseCriterion = typename Detail::ScalarType<Criterion>::value;
using LevelTransferPolicy =
OneComponentAggregationLevelTransferPolicy<Operator,
FineCriterion,
Communication,
COMPONENT_INDEX>;
using CoarseSolverPolicy =
Detail::OneStepAMGCoarseSolverPolicy<CoarseOperator,
CoarseSmoother,
CoarseCriterion,
LevelTransferPolicy>;
using TwoLevelMethod =
Dune::Amg::TwoLevelMethod<Operator,
CoarseSolverPolicy,
Smoother>;
public:
// define the category
enum {
//! \brief The category the precondtioner is part of.
category = Operator::category
};
BlackoilAmg(const Operator& fineOperator, const Criterion& criterion,
const SmootherArgs& smargs, const Communication& comm)
: smoother_(Detail::constructSmoother<Smoother>(fineOperator,smargs,comm)),
levelTransferPolicy_(criterion, comm),
coarseSolverPolicy_(smargs, criterion),
twoLevelMethod_(fineOperator, smoother_,
levelTransferPolicy_,
coarseSolverPolicy_, 0, 1)
{}
void pre(typename TwoLevelMethod::FineDomainType& x,
typename TwoLevelMethod::FineRangeType& b)
{
twoLevelMethod_.pre(x,b);
}
void post(typename TwoLevelMethod::FineDomainType& x)
{
twoLevelMethod_.post(x);
}
void apply(typename TwoLevelMethod::FineDomainType& v,
const typename TwoLevelMethod::FineRangeType& d)
{
twoLevelMethod_.apply(v, d);
}
private:
std::shared_ptr<Smoother> smoother_;
LevelTransferPolicy levelTransferPolicy_;
CoarseSolverPolicy coarseSolverPolicy_;
TwoLevelMethod twoLevelMethod_;
};
namespace ISTLUtility
{
///
/// \brief A traits class for selecting the types of the preconditioner.
///
/// \tparam M The type of the matrix.
/// \tparam X The type of the domain of the linear problem.
/// \tparam Y The type of the range of the linear problem.
/// \tparam P The type of the parallel information.
/// \tparam C The type of the coarsening criterion to use.
/// \tparam index The pressure index.
////
template<class M, class X, class Y, class P, class C, std::size_t index>
struct BlackoilAmgSelector
{
using Criterion = C;
using Selector = CPRSelector<M,X,Y,P>;
using ParallelInformation = typename Selector::ParallelInformation;
using Operator = typename Selector::Operator;
using Smoother = typename Selector::EllipticPreconditioner;
using AMG = BlackoilAmg<Operator,Smoother,Criterion,ParallelInformation,index>;
};
} // end namespace ISTLUtility
} // end namespace Opm
#endif

View File

@ -271,6 +271,29 @@ createEllipticPreconditionerPointer(const M& Ae, double relax,
#endif // #if HAVE_MPI
template < class C, class Op, class P, class AMG >
inline void
createAMGPreconditionerPointer(Op& opA, const double relax, const P& comm, std::unique_ptr< AMG >& amgPtr )
{
// TODO: revise choice of parameters
int coarsenTarget=1200;
using Criterion = C;
Criterion criterion(15, coarsenTarget);
criterion.setDebugLevel( 0 ); // no debug information, 1 for printing hierarchy information
criterion.setDefaultValuesIsotropic(2);
criterion.setNoPostSmoothSteps( 1 );
criterion.setNoPreSmoothSteps( 1 );
// for DUNE 2.2 we also need to pass the smoother args
typedef typename AMG::Smoother Smoother;
typedef typename Dune::Amg::SmootherTraits<Smoother>::Arguments SmootherArgs;
SmootherArgs smootherArgs;
smootherArgs.iterations = 1;
smootherArgs.relaxationFactor = relax;
amgPtr.reset( new AMG(opA, criterion, smootherArgs, comm ) );
}
/// \brief Creates the elliptic preconditioner (ILU0)
/// \param opA The operator representing the matrix of the system.
/// \param relax The relaxation parameter for ILU0.
@ -292,22 +315,7 @@ createAMGPreconditionerPointer( Op& opA, const double relax, const P& comm, std:
// The coarsening criterion used in the AMG
typedef Dune::Amg::CoarsenCriterion<CritBase> Criterion;
// TODO: revise choice of parameters
int coarsenTarget=1200;
Criterion criterion(15,coarsenTarget);
criterion.setDebugLevel( 0 ); // no debug information, 1 for printing hierarchy information
criterion.setDefaultValuesIsotropic(2);
criterion.setNoPostSmoothSteps( 1 );
criterion.setNoPreSmoothSteps( 1 );
// for DUNE 2.2 we also need to pass the smoother args
typedef typename AMG::Smoother Smoother;
typedef typename Dune::Amg::SmootherTraits<Smoother>::Arguments SmootherArgs;
SmootherArgs smootherArgs;
smootherArgs.iterations = 1;
smootherArgs.relaxationFactor = relax;
amgPtr.reset( new AMG(opA, criterion, smootherArgs, comm ) );
createAMGPreconditionerPointer<Criterion>(opA, relax, comm, amgPtr);
}
} // end namespace ISTLUtility

View File

@ -21,6 +21,7 @@
#define OPM_ISTLSOLVER_HEADER_INCLUDED
#include <opm/autodiff/AdditionalObjectDeleter.hpp>
#include <opm/autodiff/BlackoilAmg.hpp>
#include <opm/autodiff/CPRPreconditioner.hpp>
#include <opm/autodiff/NewtonIterationBlackoilInterleaved.hpp>
#include <opm/autodiff/NewtonIterationUtilities.hpp>
@ -407,10 +408,8 @@ namespace Opm
if( parameters_.linear_solver_use_amg_ )
{
typedef ISTLUtility::CPRSelector< Matrix, Vector, Vector, POrComm> CPRSelectorType;
typedef typename CPRSelectorType::AMG AMG;
typedef typename CPRSelectorType::Operator MatrixOperator;
std::unique_ptr< AMG > amg;
std::unique_ptr< MatrixOperator > opA;
if( ! std::is_same< LinearOperator, MatrixOperator > :: value )
@ -420,12 +419,34 @@ namespace Opm
}
const double relax = 1.0;
if ( ! parameters_.amg_blackoil_system_ )
{
typedef typename CPRSelectorType::AMG AMG;
std::unique_ptr< AMG > amg;
// Construct preconditioner.
constructAMGPrecond( linearOperator, parallelInformation_arg, amg, opA, relax );
// Construct preconditioner.
constructAMGPrecond( linearOperator, parallelInformation_arg, amg, opA, relax );
// Solve.
solve(linearOperator, x, istlb, *sp, *amg, result);
// Solve.
solve(linearOperator, x, istlb, *sp, *amg, result);
}
else
{
using Matrix = typename MatrixOperator::matrix_type;
using CouplingMetric = Dune::Amg::Diagonal<pressureIndex>;
using CritBase = Dune::Amg::SymmetricCriterion<Matrix, CouplingMetric>;
using Criterion = Dune::Amg::CoarsenCriterion<CritBase>;
using AMG = typename ISTLUtility
::BlackoilAmgSelector< Matrix, Vector, Vector,POrComm, Criterion, pressureIndex >::AMG;
std::unique_ptr< AMG > amg;
// Construct preconditioner.
Criterion crit(15, 2000);
constructAMGPrecond<Criterion>( linearOperator, parallelInformation_arg, amg, opA, relax );
// Solve.
solve(linearOperator, x, istlb, *sp, *amg, result);
}
}
else
#endif
@ -495,6 +516,20 @@ namespace Opm
ISTLUtility::template createAMGPreconditionerPointer<pressureIndex>( opA, relax, comm, amg );
}
template <class C, class LinearOperator, class MatrixOperator, class POrComm, class AMG >
void
constructAMGPrecond(LinearOperator& /* linearOperator */, const POrComm& comm, std::unique_ptr< AMG >& amg, std::unique_ptr< MatrixOperator >& opA, const double relax ) const
{
ISTLUtility::template createAMGPreconditionerPointer<C>( *opA, relax, comm, amg );
}
template <class C, class MatrixOperator, class POrComm, class AMG >
void
constructAMGPrecond(MatrixOperator& opA, const POrComm& comm, std::unique_ptr< AMG >& amg, std::unique_ptr< MatrixOperator >&, const double relax ) const
{
ISTLUtility::template createAMGPreconditionerPointer<C>( opA, relax, comm, amg );
}
/// \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

View File

@ -46,6 +46,7 @@ namespace Opm
bool require_full_sparsity_pattern_;
bool ignoreConvergenceFailure_;
bool linear_solver_use_amg_;
bool amg_blackoil_system_;
NewtonIterationBlackoilInterleavedParameters() { reset(); }
// read values from parameter class
@ -63,6 +64,7 @@ namespace Opm
require_full_sparsity_pattern_ = param.getDefault("require_full_sparsity_pattern", require_full_sparsity_pattern_);
ignoreConvergenceFailure_ = param.getDefault("linear_solver_ignoreconvergencefailure", ignoreConvergenceFailure_);
linear_solver_use_amg_ = param.getDefault("linear_solver_use_amg", linear_solver_use_amg_ );
amg_blackoil_system_ = param.getDefault("amg_blackoil_system", amg_blackoil_system_);
ilu_relaxation_ = param.getDefault("ilu_relaxation", ilu_relaxation_ );
ilu_fillin_level_ = param.getDefault("ilu_fillin_level", ilu_fillin_level_ );
}
@ -70,6 +72,7 @@ namespace Opm
// set default values
void reset()
{
amg_blackoil_system_ = false;
newton_use_gmres_ = false;
linear_solver_reduction_ = 1e-2;
linear_solver_maxiter_ = 150;

327
tests/test_blackoil_amg.cpp Normal file
View File

@ -0,0 +1,327 @@
/*
Copyright 2017 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/>.
*/
#include <config.h>
#if HAVE_DYNAMIC_BOOST_TEST && HAVE_MPI
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE BlackoilAmgTest
#define BOOST_TEST_NO_MAIN
#include <boost/test/unit_test.hpp>
#include <opm/autodiff/BlackoilAmg.hpp>
#include <dune/common/parallel/mpihelper.hh>
#include <dune/common/fmatrix.hh>
#include <dune/common/unused.hh>
#include <dune/common/parallel/indexset.hh>
#include <dune/common/parallel/plocalindex.hh>
#include <dune/common/parallel/collectivecommunication.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/owneroverlapcopy.hh>
class MPIError {
public:
/** @brief Constructor. */
MPIError(std::string s, int e) : errorstring(s), errorcode(e){}
/** @brief The error string. */
std::string errorstring;
/** @brief The mpi error code. */
int errorcode;
};
void MPI_err_handler(MPI_Comm *, int *err_code, ...){
char *err_string=new char[MPI_MAX_ERROR_STRING];
int err_length;
MPI_Error_string(*err_code, err_string, &err_length);
std::string s(err_string, err_length);
std::cerr << "An MPI Error ocurred:"<<std::endl<<s<<std::endl;
delete[] err_string;
throw MPIError(s, *err_code);
}
typedef Dune::OwnerOverlapCopyAttributeSet GridAttributes;
typedef GridAttributes::AttributeSet GridFlag;
typedef Dune::ParallelLocalIndex<GridFlag> LocalIndex;
template<class M, class G, class L, int n>
void setupPattern(int N, M& mat, Dune::ParallelIndexSet<G,L,n>& indices, int overlapStart, int overlapEnd,
int start, int end);
template<class M>
void fillValues(int N, M& mat, int overlapStart, int overlapEnd, int start, int end);
template<class M, class G, class L, class C, int n>
M setupAnisotropic2d(int N, Dune::ParallelIndexSet<G,L,n>& indices,
const Dune::CollectiveCommunication<C>& p, int *nout, typename M::block_type::value_type eps=1.0);
template<class M, class G, class L, int s>
void setupPattern(int N, M& mat, Dune::ParallelIndexSet<G,L,s>& indices, int overlapStart, int overlapEnd,
int start, int end)
{
int n = overlapEnd - overlapStart;
typename M::CreateIterator iter = mat.createbegin();
indices.beginResize();
for(int j=0; j < N; j++)
for(int i=overlapStart; i < overlapEnd; i++, ++iter) {
int global = j*N+i;
GridFlag flag = GridAttributes::owner;
bool isPublic = false;
if((i<start && i > 0) || (i>= end && i < N-1))
flag=GridAttributes::copy;
if(i<start+1 || i>= end-1) {
isPublic = true;
indices.add(global, LocalIndex(iter.index(), flag, isPublic));
}
iter.insert(iter.index());
// i direction
if(i > overlapStart )
// We have a left neighbour
iter.insert(iter.index()-1);
if(i < overlapEnd-1)
// We have a rigt neighbour
iter.insert(iter.index()+1);
// j direction
// Overlap is a dirichlet border, discard neighbours
if(flag != GridAttributes::copy) {
if(j>0)
// lower neighbour
iter.insert(iter.index()-n);
if(j<N-1)
// upper neighbour
iter.insert(iter.index()+n);
}
}
indices.endResize();
}
template<class M, class T>
void fillValues(int N, M& mat, int overlapStart, int overlapEnd, int start, int end, T eps)
{
DUNE_UNUSED_PARAMETER(N);
typedef typename M::block_type Block;
Block dval(0), bone(0), bmone(0), beps(0);
for(typename Block::RowIterator b = dval.begin(); b != dval.end(); ++b)
b->operator[](b.index())=2.0+2.0*eps;
for(typename Block::RowIterator b=bone.begin(); b != bone.end(); ++b)
b->operator[](b.index())=1.0;
for(typename Block::RowIterator b=bmone.begin(); b != bmone.end(); ++b)
b->operator[](b.index())=-1.0;
for(typename Block::RowIterator b=beps.begin(); b != beps.end(); ++b)
b->operator[](b.index())=-eps;
int n = overlapEnd-overlapStart;
typedef typename M::ColIterator ColIterator;
typedef typename M::RowIterator RowIterator;
for (RowIterator i = mat.begin(); i != mat.end(); ++i) {
// calculate coordinate
int y = i.index() / n;
int x = overlapStart + i.index() - y * n;
ColIterator endi = (*i).end();
if(x<start || x >= end) { // || x==0 || x==N-1 || y==0 || y==N-1){
// overlap node is dirichlet border
ColIterator j = (*i).begin();
for(; j.index() < i.index(); ++j)
*j=0;
*j = bone;
for(++j; j != endi; ++j)
*j=0;
}else{
for(ColIterator j = (*i).begin(); j != endi; ++j)
if(j.index() == i.index())
*j=dval;
else if(j.index()+1==i.index() || j.index()-1==i.index())
*j=beps;
else
*j=bmone;
}
}
}
template<class V, class G, class L, int s>
void setBoundary(V& lhs, V& rhs, const G& n, Dune::ParallelIndexSet<G,L,s>& indices)
{
typedef typename Dune::ParallelIndexSet<G,L,s>::const_iterator Iter;
for(Iter i=indices.begin(); i != indices.end(); ++i) {
G x = i->global()/n;
G y = i->global()%n;
if(x==0 || y ==0 || x==n-1 || y==n-1) {
//double h = 1.0 / ((double) (n-1));
lhs[i->local()]=rhs[i->local()]=0; //((double)x)*((double)y)*h*h;
}
}
}
template<class V, class G>
void setBoundary(V& lhs, V& rhs, const G& N)
{
typedef typename V::block_type Block;
typedef typename Block::value_type T;
for(int j=0; j < N; ++j)
for(int i=0; i < N; i++)
if(i==0 || j ==0 || i==N-1 || j==N-1) {
T h = 1.0 / ((double) (N-1));
T x, y;
if(i==N-1)
x=1;
else
x = ((T) i)*h;
if(j==N-1)
y = 1;
else
y = ((T) j)*h;
lhs[j*N+i]=rhs[j*N+i]=0; //x*y;
}
}
template<class M, class G, class L, class C, int s>
M setupAnisotropic2d(int N, Dune::ParallelIndexSet<G,L,s>& indices, const Dune::CollectiveCommunication<C>& p, int *nout, typename M::block_type::value_type eps)
{
int procs=p.size(), rank=p.rank();
typedef M BCRSMat;
// calculate size of local matrix in the distributed direction
int start, end, overlapStart, overlapEnd;
int n = N/procs; // number of unknowns per process
int bigger = N%procs; // number of process with n+1 unknows
// Compute owner region
if(rank<bigger) {
start = rank*(n+1);
end = start+(n+1);
}else{
start = bigger*(n+1) + (rank-bigger) * n;
end = start+n;
}
// Compute overlap region
if(start>0)
overlapStart = start - 1;
else
overlapStart = start;
if(end<N)
overlapEnd = end + 1;
else
overlapEnd = end;
int noKnown = overlapEnd-overlapStart;
*nout = noKnown;
BCRSMat mat(noKnown*N, noKnown*N, noKnown*N*5, BCRSMat::row_wise);
setupPattern(N, mat, indices, overlapStart, overlapEnd, start, end);
fillValues(N, mat, overlapStart, overlapEnd, start, end, eps);
// Dune::printmatrix(std::cout,mat,"aniso 2d","row",9,1);
return mat;
}
//BOOST_AUTO_TEST_CASE(runBlackoilAmgLaplace)
void runBlackoilAmgLaplace()
{
const int BS=2, N=100;
typedef Dune::FieldMatrix<double,BS,BS> MatrixBlock;
typedef Dune::BCRSMatrix<MatrixBlock> BCRSMat;
typedef Dune::FieldVector<double,BS> VectorBlock;
typedef Dune::BlockVector<VectorBlock> Vector;
typedef int GlobalId;
typedef Dune::OwnerOverlapCopyCommunication<GlobalId> Communication;
typedef Dune::OverlappingSchwarzOperator<BCRSMat,Vector,Vector,Communication> Operator;
int argc;
char** argv;
const auto& ccomm = Dune::MPIHelper::instance(argc, argv).getCollectiveCommunication();
Communication comm(ccomm);
int n=0;
BCRSMat mat = setupAnisotropic2d<BCRSMat>(N, comm.indexSet(), comm.communicator(), &n, 1);
comm.remoteIndices().template rebuild<false>();
Vector b(mat.N()), x(mat.M());
b=0;
x=100;
setBoundary(x, b, N, comm.indexSet());
Operator fop(mat, comm);
typedef Dune::Amg::CoarsenCriterion<Dune::Amg::SymmetricCriterion<BCRSMat,Dune::Amg::FirstDiagonal> >
Criterion;
typedef Dune::SeqSSOR<BCRSMat,Vector,Vector> Smoother;
//typedef Dune::SeqJac<BCRSMat,Vector,Vector> Smoother;
//typedef Dune::SeqILU0<BCRSMat,Vector,Vector> Smoother;
//typedef Dune::SeqILUn<BCRSMat,Vector,Vector> Smoother;
typedef Dune::BlockPreconditioner<Vector,Vector,Communication,Smoother> ParSmoother;
typedef typename Dune::Amg::SmootherTraits<ParSmoother>::Arguments SmootherArgs;
Dune::OverlappingSchwarzScalarProduct<Vector,Communication> sp(comm);
Dune::InverseOperatorResult r;
SmootherArgs smootherArgs;
Criterion criterion;
smootherArgs.iterations = 1;
Opm::BlackoilAmg<Operator,ParSmoother,Criterion,Communication,0> amg(fop, criterion,
smootherArgs,
comm);
Dune::CGSolver<Vector> amgCG(fop, sp, amg, 10e-8, 300, (ccomm.rank()==0) ? 2 : 0);
amgCG.apply(x,b,r);
}
bool init_unit_test_func()
{
return true;
}
int main(int argc, char** argv)
{
const auto& helper = Dune::MPIHelper::instance(argc, argv);
boost::unit_test::unit_test_main(&init_unit_test_func,
argc, argv);
}
#else
int main () { return 0; }
#endif // #if HAVE_DYNAMIC_BOOST_TEST