opm-simulators/tests/gpuistl/test_solver_adapter.cpp

118 lines
3.9 KiB
C++

/*
Copyright 2022-2023 SINTEF AS
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#define BOOST_TEST_MODULE TestSolverAdapter
#include <boost/test/unit_test.hpp>
#include <dune/istl/solvers.hh>
#include <opm/simulators/linalg/PreconditionerFactory.hpp>
#include <opm/simulators/linalg/PropertyTree.hpp>
#include <opm/simulators/linalg/gpuistl/SolverAdapter.hpp>
static const constexpr int dim = 3;
using Matrix = Dune::BCRSMatrix<Dune::FieldMatrix<double, dim, dim>>;
using Vector = Dune::BlockVector<Dune::FieldVector<double, dim>>;
using Moperator = Dune::MatrixAdapter<Matrix, Vector, Vector>;
using PrecondFactory = Opm::PreconditionerFactory<Moperator, Dune::Amg::SequentialInformation>;
using SolverAdapter = Opm::gpuistl::SolverAdapter<Moperator, Dune::BiCGSTABSolver, Vector>;
namespace
{
auto
createSolverAdapterWithMatrix(const size_t N = 10)
{
const int nonZeroes = N * 3 - 2;
// We need to hold the matrix in memory somehow, but we don't want to deference
// a pointer all the time (quality of life...):
auto matrixPtr = std::make_shared<Matrix>(N, N, nonZeroes, Matrix::row_wise);
auto& matrix = *matrixPtr;
for (auto row = matrix.createbegin(); row != matrix.createend(); ++row) {
// Add nonzeros for left neighbour, diagonal and right neighbour
if (row.index() > 0) {
row.insert(row.index() - 1);
}
row.insert(row.index());
if (row.index() < matrix.N() - 1) {
row.insert(row.index() + 1);
}
}
// This might not be the most elegant way of filling in a Dune sparse matrix, but it works.
for (size_t i = 0; i < N; ++i) {
for (int k = 0; k < dim; ++k) {
matrix[i][i][k][k] = -2;
}
if (i < N - 1) {
for (int k = 0; k < dim; ++k) {
matrix[i][i + 1][k][k] = 1;
}
}
if (i > 0) {
for (int k = 0; k < dim; ++k) {
matrix[i][i - 1][k][k] = 1;
}
}
}
auto op = std::make_shared<Moperator>(matrix);
auto sp = std::make_shared<Dune::ScalarProduct<Vector>>();
auto prm = Opm::PropertyTree();
prm.put<double>("relaxation", 1.0);
prm.put<std::string>("type", "CUILU0");
auto prec = PrecondFactory::create(*op, prm);
auto solverAdapter = std::make_shared<SolverAdapter>(*op, *sp, prec, 1.0, 10, 0, Dune::Amg::SequentialInformation());
return std::make_tuple(matrixPtr, solverAdapter, op, sp);
}
} // namespace
BOOST_AUTO_TEST_CASE(TestCreation)
{
BOOST_CHECK_NO_THROW(createSolverAdapterWithMatrix(););
}
BOOST_AUTO_TEST_CASE(TestSolve)
{
const size_t N = 10;
auto [matrix, solverAdapter, op, sp] = createSolverAdapterWithMatrix(N);
Vector xActual(N), xInitial(N), b(N);
for (size_t i = 0; i < N; ++i) {
for (size_t j = 0; j < dim; ++j) {
xActual[i][j] = 1.0;
xInitial[i][j] = 0.1 * i;
}
}
matrix->mv(xActual, b);
Dune::InverseOperatorResult res;
solverAdapter->apply(xInitial, b, res);
for (size_t i = 0; i < N; ++i) {
for (size_t j = 0; j < dim; ++j) {
// This should actually be up to rounding exact since ILU is just the inverse
// for this matrix.
BOOST_CHECK_CLOSE(xActual[i][j], xInitial[i][j], 1e-13);
}
}
}