opm-simulators/tests/test_preconditionerfactory.cpp
Bård Skaflestad 48e1af8bbe Address Static Code Analysis Warnings
In particular, don't print uninitialized memory (Reorder.cpp:left)
and don't capture objects ('prm') that aren't actually used.  While
here, refactor the initialization of the MT19937 random number
generator.  Constructing this object is too expensive to do for each
try, especially when we can just run the generator in place.
2021-07-13 11:37:57 +02:00

376 lines
11 KiB
C++

/*
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 <boost/version.hpp>
#include <dune/common/version.hh>
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 6) && \
BOOST_VERSION / 100 % 1000 > 48
#include <opm/simulators/linalg/PreconditionerFactory.hpp>
#include <opm/simulators/linalg/PropertyTree.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 <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 Opm::PropertyTree& 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 = Opm::PreconditionerFactory<Operator>;
bool transpose = false;
if(prm.get<std::string>("preconditioner.type") == "cprt"){
transpose = true;
}
auto wc = [&matrix, transpose]()
{
return Opm::Amg::getQuasiImpesWeights<Matrix, Vector>(matrix, 1, transpose);
};
auto prec = PrecFactory::create(op, prm.get_child("preconditioner"), wc, 1);
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;
}
void test1(const Opm::PropertyTree& 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 Opm::PropertyTree& 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)
{
// Read parameters.
Opm::PropertyTree prm("options_flexiblesolver.json");
// 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 = Opm::PreconditionerFactory<O<bz>>;
BOOST_AUTO_TEST_CASE(TestAddingPreconditioner)
{
// Read parameters.
Opm::PropertyTree prm("options_flexiblesolver_simple.json");
// Test with 1x1 block solvers.
{
const int bz = 1;
BOOST_CHECK_THROW(testPrec<bz>(prm, "matr33.txt", "rhs3.txt"), std::invalid_argument);
}
// Test with 3x3 block solvers.
{
const int bz = 3;
BOOST_CHECK_THROW(testPrec<bz>(prm, "matr33.txt", "rhs3.txt"), std::invalid_argument);
}
// Add preconditioner to factory for block size 1.
PF<1>::addCreator("nothing", [](const O<1>&, const Opm::PropertyTree&, const std::function<V<1>()>&,
std::size_t) {
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::invalid_argument);
}
// Add preconditioner to factory for block size 3.
PF<3>::addCreator("nothing", [](const O<3>&, const Opm::PropertyTree&, const std::function<V<3>()>&,
std::size_t) {
return Dune::wrapPreconditioner<NothingPreconditioner<V<3>>>();
});
// Test with 1x1 block solvers.
test1(prm);
// Test with 3x3 block solvers.
test3(prm);
}
template<class Mat, class Vec>
class RepeatingOperator : public Dune::AssembledLinearOperator<Mat, Vec, Vec>
{
public:
using matrix_type = Mat;
using domain_type = Vec;
using range_type = Vec;
using field_type = typename Vec::field_type;
Dune::SolverCategory::Category category() const override
{
return Dune::SolverCategory::sequential;
}
RepeatingOperator(const Mat& matrix, const int repeats)
: matrix_(matrix)
, repeats_(repeats)
{
}
// y = A*x;
virtual void apply(const Vec& x, Vec& y) const override
{
y = 0;
applyscaleadd(1.0, x, y);
}
// y += \alpha * A * x
virtual void applyscaleadd(field_type alpha, const Vec& x, Vec& y) const override
{
Vec temp1 = x;
Vec temp2 = x; // For size.
temp2 = 0.0;
for (int rr = 0; rr < repeats_; ++rr) {
// mv below means: temp2 = matrix_ * temp1;
matrix_.mv(temp1, temp2);
temp1 = temp2;
}
temp2 *= alpha;
y += temp2;
}
virtual const matrix_type& getmat() const override
{
return matrix_;
}
protected:
const Mat& matrix_;
const int repeats_;
};
template <int bz>
Dune::BlockVector<Dune::FieldVector<double, bz>>
testPrecRepeating(const Opm::PropertyTree& 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 = RepeatingOperator<Matrix, Vector>;
Operator op(matrix, 2);
using PrecFactory = Opm::PreconditionerFactory<Operator>;
// Add no-oppreconditioner to factory for block size 1.
PrecFactory::addCreator("nothing", [](const Operator&, const Opm::PropertyTree&, const std::function<Vector()>&,
std::size_t) {
return Dune::wrapPreconditioner<NothingPreconditioner<Vector>>();
});
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;
}
void test1rep(const Opm::PropertyTree& prm)
{
const int bz = 1;
auto sol = testPrecRepeating<bz>(prm, "matr33rep.txt", "rhs3rep.txt");
Dune::BlockVector<Dune::FieldVector<double, bz>> expected {0.285714285714286,
0.285714285714286,
0.285714285714286,
-0.214285714285714,
-0.214285714285714,
-0.214285714285714,
-0.214285714285714,
-0.214285714285714,
-0.214285714285714};
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 test3rep(const Opm::PropertyTree& prm)
{
const int bz = 3;
auto sol = testPrecRepeating<bz>(prm, "matr33rep.txt", "rhs3rep.txt");
Dune::BlockVector<Dune::FieldVector<double, bz>> expected {
{0.285714285714286, 0.285714285714286, 0.285714285714286},
{-0.214285714285714, -0.214285714285714, -0.214285714285714},
{-0.214285714285714, -0.214285714285714, -0.214285714285714}
};
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(TestWithRepeatingOperator)
{
// Read parameters.
Opm::PropertyTree prm("options_flexiblesolver_simple.json");
// Test with 1x1 block solvers.
test1rep(prm);
// Test with 3x3 block solvers.
test3rep(prm);
}
#else
// Do nothing if we do not have at least Dune 2.6.
BOOST_AUTO_TEST_CASE(DummyTest)
{
BOOST_REQUIRE(true);
}
#endif