added: support for dune-istl linear solvers

This commit is contained in:
Arne Morten Kvarving
2016-05-12 16:19:35 +02:00
committed by Knut Morten Okstad
parent 0364126769
commit efa6c99665
19 changed files with 2055 additions and 4 deletions

View File

@@ -110,6 +110,12 @@ if(NOT PETSC_FOUND)
${PROJECT_SOURCE_DIR}/src/LinAlg/PETScSolParams.C)
endif()
if(NOT ISTL_FOUND)
list(REMOVE_ITEM IFEM_SRCS
${PROJECT_SOURCE_DIR}/src/LinAlg/ISTLMatrix.C
${PROJECT_SOURCE_DIR}/src/LinAlg/ISTLSolParams.C)
endif()
if(LRSPLINE_FOUND OR LRSpline_FOUND)
file(GLOB LR_SRCS ${PROJECT_SOURCE_DIR}/src/ASM/LR/*.C)
list(APPEND IFEM_SRCS ${LR_SRCS})

View File

@@ -211,6 +211,16 @@ IF(IFEM_USE_OPENMP)
ENDIF(OPENMP_FOUND)
ENDIF(IFEM_USE_OPENMP)
# ISTL
if(IFEM_USE_ISTL)
find_package(ISTL)
if(ISTL_FOUND)
list(APPEND IFEM_DEPLIBS ${ISTL_LIBRARIES})
list(APPEND IFEM_DEPINCLUDES ${ISTL_INCLUDE_DIRS})
list(APPEND IFEM_DEFINITIONS -DHAS_ISTL=1 ${ISTL_DEFINITIONS})
endif()
endif()
# Portability issues
include(CheckFunctionExists)
set(CMAKE_REQUIRED_DEFINITIONS)

View File

@@ -0,0 +1,38 @@
find_package(PkgConfig)
set(OLD_PKG $ENV{PKG_CONFIG_PATH})
set(ENV{PKG_CONFIG_PATH} $ENV{PETSC_DIR}/$ENV{PETSC_ARCH}/lib/pkgconfig)
pkg_check_modules(ISTL dune-istl)
set(ENV{PKG_CONFIG_PATH} ${OLD_PKG})
list(APPEND ISTL_DEFINITIONS -DHAVE_NULLPTR=1 -DISTL_VERSION="${ISTL_VERSION}")
string(REPLACE "." ";" VERSION_LIST "${ISTL_VERSION}")
if(VERSION_LIST)
list(GET VERSION_LIST 0 DUNE_ISTL_VERSION_MAJOR)
list(GET VERSION_LIST 1 DUNE_ISTL_VERSION_MINOR)
list(GET VERSION_LIST 2 DUNE_ISTL_VERSION_PATCH)
list(APPEND ISTL_DEFINITIONS -DDUNE_ISTL_VERSION_MAJOR=${DUNE_ISTL_VERSION_MAJOR}
-DDUNE_ISTL_VERSION_MINOR=${DUNE_ISTL_VERSION_MINOR}
-DDUNE_ISTL_VERSION_REVISION=${DUNE_ISTL_VERSION_PATCH})
endif()
if(SUPERLU_FOUND)
list(APPEND ISTL_DEFINITIONS -DHAVE_SUPERLU=1 -DSUPERLU_POST_2005_VERSION=1 -DSUPERLU_NTYPE=1 -DSUPERLU_MIN_VERSION_4_3=1)
endif()
if(IFEM_USE_UMFPACK)
find_package(SuiteSparse COMPONENTS umfpack)
endif()
if(SuiteSparse_UMFPACK_FOUND)
list(APPEND ISTL_DEFINITIONS -DHAVE_UMFPACK=1)
list(APPEND ISTL_INCLUDE_DIRS ${UMFPACK_INCLUDE_DIR})
list(APPEND ISTL_LIBRARIES ${UMFPACK_LIBRARY})
endif()
list(APPEND ISTL_INCLUDE_DIRS ${ISTL_INCLUDEDIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ISTL DEFAULT_MSG
ISTL_INCLUDE_DIRS ISTL_LIBRARIES)

View File

@@ -0,0 +1,294 @@
# - Find Tim Davis' SuiteSparse collection of sparse matrix libraries
#
# Synopsis:
# find_package (SuiteSparse COMPONENTS <list-of-components>)
#
# Components are:
# amd Approximate Minimum Degree ordering
# camd Constrained Approximate Minimum Degree ordering
# colamd COLumn Approximate Minimum Degree ordering
# ccolamd Constrained COLumn Approximate Minimum Degree ordering
# cholmod Supernodal sparse Cholesky factorization and update
# umfpack Unsymmetric MultiFrontal sparse LU factorization
#
# The following variables will be set:
#
# SuiteSparse_FOUND True if all dependencies are satisfied
# SuiteSparse_Xxx_FOUND True if module Xxx is found
# HAVE_SUITESPARSE_Xxx_H Binary value indicating presence of header
# SuiteSparse_INCLUDE_DIRS Paths containing the SuiteSparse header files
# SuiteSparse_LIBRARIES Name of the libraries which must be linked
# SuiteSparse_DEFINITIONS Defines that must be passed to the compiler
# SuiteSparse_LINKER_FLAGS Options that must be passed when linking
#
# The following options can be set to configure the module:
#
# SUITESPARSE_USE_STATIC Link with a static library, even if a
# dynamic library is also present. Note that
# setting this to OFF does not ensure that a
# shared library will be used.
#
# See <http://www.cise.ufl.edu/research/sparse/SuiteSparse>.
# Copyright (C) 2012 Uni Research AS
# This file is licensed under the GNU General Public License v3.0
function (try_compile_umfpack varname)
include (CMakePushCheckState)
include (CheckCSourceCompiles)
cmake_push_check_state ()
set (CMAKE_REQUIRED_INCLUDES ${UMFPACK_INCLUDE_DIRS})
set (CMAKE_REQUIRED_LIBRARIES ${UMFPACK_LIBRARY} ${ARGN} ${SuiteSparse_EXTRA_LIBS})
check_c_source_compiles (
"#include <umfpack.h>
int main (void) {
void *Symbolic, *Numeric;
double Info[UMFPACK_INFO], Control[UMFPACK_CONTROL];
umfpack_dl_defaults(Control);
umfpack_dl_symbolic(0, 0, 0, 0, 0,
&Symbolic, Control, Info);
umfpack_dl_numeric (0, 0, 0,
Symbolic, &Numeric, Control, Info);
umfpack_dl_free_symbolic(&Symbolic);
umfpack_dl_solve(UMFPACK_A, 0, 0, 0, 0, 0,
Numeric, Control, Info);
umfpack_dl_free_numeric(&Numeric);
umfpack_timer ();
return 0;
}" ${varname})
cmake_pop_check_state ()
set (${varname} "${${varname}}" PARENT_SCOPE)
endfunction (try_compile_umfpack varname)
# variables to pass on to other packages
if (FIND_QUIETLY)
set (SuiteSparse_QUIET "QUIET")
else (FIND_QUIETLY)
set (SuiteSparse_QUIET "")
endif (FIND_QUIETLY)
# we need to link to BLAS and LAPACK
if (NOT BLAS_FOUND)
find_package (BLAS ${SuiteSparse_QUIET} REQUIRED)
endif (NOT BLAS_FOUND)
if (NOT LAPACK_FOUND)
find_package (LAPACK ${SuiteSparse_QUIET} REQUIRED)
endif (NOT LAPACK_FOUND)
# we also need the math part of the runtime library
find_library (MATH_LIBRARY NAMES "m")
set (SuiteSparse_EXTRA_LIBS ${LAPACK_LIBRARIES} ${BLAS_LIBRARIES} ${MATH_LIBRARY})
# if we don't get any further clues about where to look, then start
# roaming around the system
set (_no_default_path "")
# search system directories by default
set (SuiteSparse_SEARCH_PATH)
# pick up paths from the environment if specified there; these replace the
# pre-defined paths so that we don't accidentially pick up old stuff
if (NOT $ENV{SuiteSparse_DIR} STREQUAL "")
set (SuiteSparse_SEARCH_PATH "$ENV{SuiteSparse_DIR}")
endif (NOT $ENV{SuiteSparse_DIR} STREQUAL "")
if (SuiteSparse_DIR)
set (SuiteSparse_SEARCH_PATH "${SuiteSparse_DIR}")
endif (SuiteSparse_DIR)
# CMake uses _DIR suffix as default for config-mode files; it is unlikely
# that we are building SuiteSparse ourselves; use _ROOT suffix to specify
# location to pre-canned binaries
if (NOT $ENV{SuiteSparse_ROOT} STREQUAL "")
set (SuiteSparse_SEARCH_PATH "$ENV{SuiteSparse_ROOT}")
endif (NOT $ENV{SuiteSparse_ROOT} STREQUAL "")
if (SuiteSparse_ROOT)
set (SuiteSparse_SEARCH_PATH "${SuiteSparse_ROOT}")
endif (SuiteSparse_ROOT)
# most commonly, we use the uppercase version of this variable
if (SUITESPARSE_ROOT)
set (SuiteSparse_SEARCH_PATH "${SUITESPARSE_ROOT}")
endif (SUITESPARSE_ROOT)
# if we have specified a search path, then confine ourselves to that
if (SuiteSparse_SEARCH_PATH)
set (_no_default_path "NO_DEFAULT_PATH")
endif (SuiteSparse_SEARCH_PATH)
# transitive closure of dependencies; after this SuiteSparse_MODULES is the
# full list of modules that must be found to satisfy the user's link demands
set (SuiteSparse_MODULES ${SuiteSparse_FIND_COMPONENTS})
list (FIND SuiteSparse_MODULES "umfpack" UMFPACK_DESIRED)
if (NOT UMFPACK_DESIRED EQUAL -1)
list (APPEND SuiteSparse_MODULES amd cholmod)
endif (NOT UMFPACK_DESIRED EQUAL -1)
list (FIND SuiteSparse_MODULES "cholmod" CHOLMOD_DESIRED)
if (NOT CHOLMOD_DESIRED EQUAL -1)
list (APPEND SuiteSparse_MODULES amd camd colamd)
endif (NOT CHOLMOD_DESIRED EQUAL -1)
if (SuiteSparse_MODULES)
list (REMOVE_DUPLICATES SuiteSparse_MODULES)
endif (SuiteSparse_MODULES)
# if someone else already have found all the packages for us, then don't do anything
set (SuiteSparse_EVERYTHING_FOUND TRUE)
foreach (module IN LISTS SuiteSparse_MODULES)
string (TOUPPER ${module} MODULE)
if (NOT SuiteSparse_${MODULE}_FOUND)
set (SuiteSparse_EVERYTHING_FOUND FALSE)
break ()
endif (NOT SuiteSparse_${MODULE}_FOUND)
endforeach (module)
if (SuiteSparse_EVERYTHING_FOUND)
return ()
endif (SuiteSparse_EVERYTHING_FOUND)
# only search in architecture-relevant directory
if (CMAKE_SIZEOF_VOID_P)
math (EXPR _BITS "8 * ${CMAKE_SIZEOF_VOID_P}")
endif (CMAKE_SIZEOF_VOID_P)
# if we are told to link SuiteSparse statically, add these parts
# to the name so we always match only that particular type of lib
option (SUITESPARSE_USE_STATIC "Link SuiteSparse statically" OFF)
mark_as_advanced (SUITESPARSE_USE_STATIC)
if (SUITESPARSE_USE_STATIC)
set (_pref_ "${CMAKE_STATIC_LIBRARY_PREFIX}")
set (_suff_ "${CMAKE_STATIC_LIBRARY_SUFFIX}")
else (SUITESPARSE_USE_STATIC)
set (_pref_ "")
set (_suff_ "")
endif (SUITESPARSE_USE_STATIC)
# if SuiteSparse >= 4.0 we must also link with libsuitesparseconfig
# assume that this is the case if we find the library; otherwise just
# ignore it (older versions don't have a file named like this)
find_library (config_LIBRARY
NAMES "${_pref_}suitesparseconfig${_suff_}"
PATHS ${SuiteSparse_SEARCH_PATH}
PATH_SUFFIXES ".libs" "lib" "lib${_BITS}" "lib/${CMAKE_LIBRARY_ARCHITECTURE}" "lib/ufsparse"
${_no_default_path}
)
if (config_LIBRARY)
list (APPEND SuiteSparse_EXTRA_LIBS ${config_LIBRARY})
# POSIX.1-2001 REALTIME portion require us to link this library too for
# clock_gettime() which is used by suitesparseconfig
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
list (APPEND SuiteSparse_EXTRA_LIBS "-lrt")
endif ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
endif (config_LIBRARY)
# search filesystem for each of the module individually
foreach (module IN LISTS SuiteSparse_MODULES)
string (TOUPPER ${module} MODULE)
# search for files which implements this module
find_path (${MODULE}_INCLUDE_DIR
NAMES ${module}.h
PATHS ${SuiteSparse_SEARCH_PATH}
PATH_SUFFIXES "include" "include/suitesparse" "include/ufsparse" "${MODULE}/Include"
${_no_default_path}
)
find_library (${MODULE}_LIBRARY
NAMES "${_pref_}${module}${_suff_}"
PATHS ${SuiteSparse_SEARCH_PATH}
PATH_SUFFIXES "lib/.libs" "lib" "lib${_BITS}" "lib/${CMAKE_LIBRARY_ARCHITECTURE}" "lib/ufsparse" "${MODULE}/Lib"
${_no_default_path}
)
# start out by including the module itself; other dependencies will be added later
set (${MODULE}_INCLUDE_DIRS ${${MODULE}_INCLUDE_DIR})
set (${MODULE}_LIBRARIES ${${MODULE}_LIBRARY})
endforeach (module)
# insert any inter-modular dependencies here
if (CHOLMOD_LIBRARY)
list (APPEND CHOLMOD_LIBRARIES ${AMD_LIBRARIES} ${COLAMD_LIBRARIES})
# optional libraries; don't insert any -NOT_FOUND paths
if (CAMD_LIBRARY)
list (APPEND CHOLMOD_LIBRARIES ${CAMD_LIBRARIES})
endif (CAMD_LIBRARY)
if (CCOLAMD_LIBRARY)
list (APPEND CHOLMOD_LIBRARIES ${CCOLAMD_LIBRARIES})
endif (CCOLAMD_LIBRARY)
list (REVERSE CHOLMOD_LIBRARIES)
# always remove the *first* library from the list
list (REMOVE_DUPLICATES CHOLMOD_LIBRARIES)
list (REVERSE CHOLMOD_LIBRARIES)
endif (CHOLMOD_LIBRARY)
if (UMFPACK_LIBRARY)
set (UMFPACK_EXTRA_LIBS)
# test if umfpack is usable with only amd and not cholmod
try_compile_umfpack (HAVE_UMFPACK_WITHOUT_CHOLMOD ${AMD_LIBRARIES})
if (HAVE_UMFPACK_WITHOUT_CHOLMOD)
list (APPEND UMFPACK_EXTRA_LIBS ${AMD_LIBRARIES})
else (HAVE_UMFPACK_WITHOUT_CHOLMOD)
if (CHOLMOD_LIBRARIES)
try_compile_umfpack (HAVE_UMFPACK_WITH_CHOLMOD ${CHOLMOD_LIBRARIES})
if (HAVE_UMFPACK_WITH_CHOLMOD)
list (APPEND UMFPACK_EXTRA_LIBS ${CHOLMOD_LIBRARIES})
else (HAVE_UMFPACK_WITH_CHOLMOD)
set (UMFPACK_EXTRA_LIBS "-NOTFOUND")
endif (HAVE_UMFPACK_WITH_CHOLMOD)
else (CHOLMOD_LIBRARIES)
# if we don't have cholmod, then we certainly cannot have umfpack with cholmod
set (UMFPACK_EXTRA_LIBS "-NOTFOUND")
endif (CHOLMOD_LIBRARIES)
endif (HAVE_UMFPACK_WITHOUT_CHOLMOD)
list (APPEND UMFPACK_LIBRARIES ${UMFPACK_EXTRA_LIBS})
list (REVERSE UMFPACK_LIBRARIES)
list (REMOVE_DUPLICATES UMFPACK_LIBRARIES)
list (REVERSE UMFPACK_LIBRARIES)
endif (UMFPACK_LIBRARY)
# don't reset these sets; if two packages request SuiteSparse with
# different modules, we want the sets to be merged
#set (SuiteSparse_LIBRARIES "")
#set (SuiteSparse_INCLUDE_DIRS "")
# determine which modules were found based on whether all dependencies
# were satisfied; create a list of ALL modules (specified) that was found
# (to be included in one swoop in CMakeLists.txt)
set (SuiteSparse_FOUND TRUE)
foreach (module IN LISTS SuiteSparse_FIND_COMPONENTS)
string (TOUPPER ${module} MODULE)
set (SuiteSparse_${MODULE}_FOUND TRUE)
foreach (file IN LISTS ${MODULE}_INCLUDE_DIRS ${MODULE}_LIBRARIES)
if (NOT EXISTS ${file})
set (SuiteSparse_${MODULE}_FOUND FALSE)
endif (NOT EXISTS ${file})
endforeach (file)
if (NOT SuiteSparse_${MODULE}_FOUND)
set (SuiteSparse_FOUND FALSE)
# use empty string instead of zero, so it can be tested with #ifdef
# as well as #if in the source code
set (HAVE_SUITESPARSE_${MODULE}_H "" CACHE INT "Is ${module} header present?")
else (NOT SuiteSparse_${MODULE}_FOUND)
set (HAVE_SUITESPARSE_${MODULE}_H 1 CACHE INT "Is ${module} header present?")
list (APPEND SuiteSparse_LIBRARIES "${${MODULE}_LIBRARIES}")
list (APPEND SuiteSparse_LINKER_FLAGS "${${MODULE}_LINKER_FLAGS}")
list (APPEND SuiteSparse_INCLUDE_DIRS "${${MODULE}_INCLUDE_DIRS}")
endif (NOT SuiteSparse_${MODULE}_FOUND)
mark_as_advanced (HAVE_SUITESPARSE_${MODULE}_H)
mark_as_advanced (${MODULE}_INCLUDE_DIR)
mark_as_advanced (${MODULE}_LIBRARY)
endforeach (module)
if (SuiteSparse_INCLUDE_DIRS)
list (REMOVE_DUPLICATES SuiteSparse_INCLUDE_DIRS)
endif (SuiteSparse_INCLUDE_DIRS)
if (SuiteSparse_LIBRARIES)
list (REVERSE SuiteSparse_LIBRARIES)
list (REMOVE_DUPLICATES SuiteSparse_LIBRARIES)
list (REVERSE SuiteSparse_LIBRARIES)
endif (SuiteSparse_LIBRARIES)
# print a message to indicate status of this package
include (FindPackageHandleStandardArgs)
find_package_handle_standard_args (SuiteSparse
DEFAULT_MSG
SuiteSparse_LIBRARIES
SuiteSparse_INCLUDE_DIRS
)
# add these after checking to not pollute the message output (checking for
# BLAS and LAPACK is REQUIRED so if they are not found, we'll have failed
# already; suitesparseconfig is "optional" anyway)
list (APPEND SuiteSparse_LIBRARIES ${SuiteSparse_EXTRA_LIBS})

View File

@@ -4,11 +4,13 @@ OPTION(IFEM_USE_SUPERLU "Compile with SuperLU support?" ON)
OPTION(IFEM_USE_SUPERLU_MT "Compile with SuperLU_MT support?" OFF)
OPTION(IFEM_USE_PETSC "Compile with PETSc support?" ON)
OPTION(IFEM_USE_MPI "Compile with MPI support?" OFF)
OPTION(IFEM_USE_ISTL "Compile with dune-istl support?" ON)
OPTION(IFEM_USE_SLEPC "Compile with SLEPc support?" OFF)
OPTION(IFEM_USE_SPR "Compile with SPR support?" OFF)
OPTION(IFEM_USE_SAMG "Compile with SAMG support?" OFF)
OPTION(IFEM_USE_HDF5 "Compile with HDF5 support?" ON)
OPTION(IFEM_USE_VTFWRITER "Compile with VTFWriter support?" ON)
OPTION(IFEM_USE_UMFPACK "Compile with UMFPACK support?" ON)
OPTION(IFEM_AS_SUBMODULE "Compile IFEM as a submodule of apps?" OFF)
OPTION(IFEM_WHOLE_PROG_OPTIM "Compile IFEM with link-time optimizations?" OFF)
OPTION(IFEM_TEST_MEMCHECK "Run tests through valgrind?" OFF)

View File

@@ -53,6 +53,12 @@ macro(IFEM_add_unittests IFEM_PATH)
file(GLOB TEST_SOURCES ${IFEM_PATH}/src/Utility/Test/*.C;${IFEM_PATH}/src/ASM/Test/*.C;${IFEM_PATH}/src/LinAlg/Test/*.C;${IFEM_PATH}/src/SIM/Test/*.C)
if(NOT PETSC_FOUND)
list(REMOVE_ITEM TEST_SOURCES ${IFEM_PATH}/src/LinAlg/Test/TestPETScMatrix.C)
list(REMOVE_ITEM TEST_SOURCES ${IFEM_PATH}/src/LinAlg/Test/TestISTLPETScMatrix.C)
endif()
if(NOT ISTL_FOUND)
list(REMOVE_ITEM TEST_SOURCES ${IFEM_PATH}/src/LinAlg/Test/TestISTLMatrix.C)
list(REMOVE_ITEM TEST_SOURCES ${IFEM_PATH}/src/LinAlg/Test/TestISTLPETScMatrix.C)
endif()
IFEM_add_test_app("${TEST_SOURCES}"
@@ -66,6 +72,9 @@ macro(IFEM_add_unittests IFEM_PATH)
if(PETSC_FOUND)
list(APPEND TEST_SRCS_MPI ${IFEM_PATH}/src/LinAlg/Test/MPI/TestPETScMatrix.C)
endif()
if(ISTL_FOUND)
list(APPEND TEST_SRCS_MPI ${IFEM_PATH}/src/LinAlg/Test/MPI/TestISTLMatrix.C)
endif()
add_executable(IFEM-MPI-test EXCLUDE_FROM_ALL
${IFEM_PATH}/src/IFEM-test.C ${TEST_SRCS_MPI})
target_link_libraries(IFEM-MPI-test ${IFEM_LIBRARIES} ${IFEM_DEPLIBS} gtest)

View File

@@ -52,6 +52,8 @@ bool AlgEqSystem::init (SystemMatrix::Type mtype, const LinSolParams* spar,
SystemVector::Type vtype = SystemVector::STD;
if (mtype == SystemMatrix::PETSC)
vtype = SystemVector::PETSC;
if (mtype == SystemMatrix::ISTL)
vtype = SystemVector::ISTL;
for (i = 0; i < b.size(); i++)
if (!b[i])

View File

@@ -114,6 +114,13 @@ int IFEM::Init (int arg_c, char** arg_v, const char* title)
std::cout <<"disabled";
#endif
std::cout <<"\n ISTL support: ";
#if HAS_ISTL
std::cout <<"enabled (v"<< ISTL_VERSION <<")";
#else
std::cout <<"disabled";
#endif
std::cout <<"\n VTF support: ";
#if HAS_VTFAPI == 2
std::cout <<"enabled (v2)";

253
src/LinAlg/ISTLMatrix.C Normal file
View File

@@ -0,0 +1,253 @@
// $Id$
//==============================================================================
//!
//! \file ISTLMatrix.C
//!
//! \date Mar 2 2016
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Representation of the system matrix in ISTL format.
//!
//==============================================================================
#include "ISTLMatrix.h"
#include "LinSolParams.h"
#include "LinAlgInit.h"
#include "ProcessAdm.h"
#include "SIMenums.h"
#include "ASMstruct.h"
#include "Profiler.h"
#include "SAMpatch.h"
#include "DomainDecomposition.h"
ISTLVector::ISTLVector(const ProcessAdm& padm) : adm(padm)
{
LinAlgInit::increfs();
}
ISTLVector::ISTLVector(const ProcessAdm& padm, size_t n) : adm(padm)
{
x.resize(n);
LinAlgInit::increfs();
}
ISTLVector::ISTLVector(const ProcessAdm& padm, const Real* values, size_t n) : adm(padm)
{
x.resize(n);
this->restore(values);
LinAlgInit::increfs();
}
ISTLVector::ISTLVector(const ISTLVector& vec) : adm(vec.adm)
{
x = vec.x;
LinAlgInit::increfs();
}
ISTLVector::~ISTLVector()
{
LinAlgInit::decrefs();
}
void ISTLVector::init(Real value)
{
StdVector::init(value);
x = value;
}
size_t ISTLVector::dim() const
{
return x.size();
}
void ISTLVector::redim(size_t n)
{
x.resize(n);
StdVector::redim(n);
}
bool ISTLVector::beginAssembly()
{
for (size_t i = 0; i < size(); ++i)
x[i] = (*this)[i];
return true;
}
bool ISTLVector::endAssembly()
{
return true;
}
Real ISTLVector::L1norm() const
{
return x.one_norm();
}
Real ISTLVector::L2norm() const
{
return x.two_norm();
}
Real ISTLVector::Linfnorm() const
{
return x.infinity_norm();
}
ISTLMatrix::ISTLMatrix (const ProcessAdm& padm, const LinSolParams& spar,
LinAlg::LinearSystemType ltype) :
SparseMatrix(SUPERLU, 1), adm(padm),
solParams(spar, adm), linsysType(ltype)
{
LinAlgInit::increfs();
setParams = true;
nLinSolves = 0;
}
ISTLMatrix::ISTLMatrix (const ISTLMatrix& B) :
adm(B.adm), solParams(B.solParams.get(), B.adm)
{
A = B.A;
LinAlgInit::increfs();
setParams = true;
}
ISTLMatrix::~ISTLMatrix ()
{
LinAlgInit::decrefs();
}
void ISTLMatrix::initAssembly (const SAM& sam, bool b)
{
SparseMatrix::initAssembly(sam, b);
SparseMatrix::preAssemble(sam, b);
std::vector<std::set<int>> dofc;
sam.getDofCouplings(dofc);
// Set correct number of rows and columns for matrix.
size_t sum = 0;
for (const auto& it : dofc)
sum += it.size();
A.setSize(rows(), cols(), sum);
A.setBuildMode(ISTL::Mat::random);
for (size_t i = 0; i < dofc.size(); ++i)
A.setrowsize(i,dofc[i].size());
A.endrowsizes();
for (size_t i = 0; i < dofc.size(); ++i)
for (const auto& it : dofc[i])
A.addindex(i, it-1);
A.endindices();
A = 0;
}
bool ISTLMatrix::beginAssembly()
{
for (size_t j = 0; j < cols(); ++j)
for (int i = IA[j]; i < IA[j+1]; ++i)
A[JA[i]][j] = SparseMatrix::A[i];
return true;
}
bool ISTLMatrix::endAssembly()
{
return true;
}
void ISTLMatrix::init ()
{
SparseMatrix::init();
// Set all matrix elements to zero
A = 0;
}
bool ISTLMatrix::solve (SystemVector& B, bool newLHS, Real*)
{
if (!pre)
std::tie(solver, pre, op) = solParams.setupPC(A);
ISTLVector* Bptr = dynamic_cast<ISTLVector*>(&B);
if (!Bptr || !solver || !pre)
return false;
try {
Dune::InverseOperatorResult r;
ISTL::Vec b(Bptr->getVector());
Bptr->getVector() = 0;
solver->apply(Bptr->getVector(), b, r);
} catch (Dune::ISTLError& e) {
std::cerr << "ISTL exception " << e << std::endl;
return false;
}
for (size_t i = 0; i < rows(); ++i)
(*Bptr)(i+1) = Bptr->getVector()[i];
return true;
}
bool ISTLMatrix::solve (const SystemVector& b, SystemVector& x, bool newLHS)
{
if (!pre)
std::tie(solver, pre, op) = solParams.setupPC(A);
const ISTLVector* Bptr = dynamic_cast<const ISTLVector*>(&b);
if (!Bptr || ! solver || !pre)
return false;
ISTLVector* Xptr = dynamic_cast<ISTLVector*>(&x);
if (!Xptr)
return false;
try {
Dune::InverseOperatorResult r;
solver->apply(Xptr->getVector(),
const_cast<ISTL::Vec&>(Bptr->getVector()), r);
} catch (Dune::ISTLError& e) {
std::cerr << "ISTL exception " << e << std::endl;
return false;
}
for (size_t i = 0; i < rows(); ++i)
(*Xptr)(i+1) = Xptr->getVector()[i];
return true;
}
Real ISTLMatrix::Linfnorm () const
{
return A.infinity_norm();
}

163
src/LinAlg/ISTLMatrix.h Normal file
View File

@@ -0,0 +1,163 @@
// $Id$
//==============================================================================
//!
//! \file ISTLMatrix.h
//!
//! \date Mar 2 2016
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Representation of the system matrix in ISTL format with interface
//! to ISTL routines for solving linear equation systems.
//!
//==============================================================================
#ifndef _ISTL_MATRIX_H
#define _ISTL_MATRIX_H
#include "SystemMatrix.h"
#include "SparseMatrix.h"
#include "ISTLSolParams.h"
#include "LinAlgenums.h"
#include <memory>
class LinSolParams;
class SAMpatch;
/*!
\brief Class for representing the system vector in ISTL format.
*/
class ISTLVector : public StdVector
{
public:
//! \brief Constructor creating an empty vector.
ISTLVector(const ProcessAdm& padm);
//! \brief Constructor creating a vector of length \a n.
ISTLVector(const ProcessAdm& padm, size_t n);
//! \brief Constructor creating a vector from an array.
ISTLVector(const ProcessAdm& padm, const Real* values, size_t n);
//! \brief Copy constructor.
ISTLVector(const ISTLVector& vec);
//! \brief Destructor.
virtual ~ISTLVector();
#endif
//! \brief Returns the vector type.
virtual Type getType() const { return ISTL; }
//! \brief Initializes the vector to a given scalar value.
virtual void init(Real value = Real(0));
//! \brief Returns the dimension of the system vector.
virtual size_t dim() const;
//! \brief Sets the dimension of the system vector.
virtual void redim(size_t n);
//! \brief Begins communication step needed in parallel vector assembly.
//! \details Must be called together with endAssembly after vector assembly
//! is completed on each processor and before the linear system is solved.
virtual bool beginAssembly();
//! \brief Ends communication step needed in parallel vector assembly.
//! \details Must be called together with beginAssembly after vector assembly
//! is completed on each processor and before the linear system is solved.
virtual bool endAssembly();
//! \brief L1-norm of vector.
virtual Real L1norm() const;
//! \brief L2-norm of vector.
virtual Real L2norm() const;
//! \brief Linfinity-norm of vector.
virtual Real Linfnorm() const;
//! \brief Return associated process administrator
const ProcessAdm& getAdm() const { return adm; }
typedef Dune::BlockVector<Dune::FieldVector<double,1>> Vec;
Vec& getVector() { return x; }
const Vec& getVector() const { return x; }
protected:
ISTL::Vec x; //!< ISTL vector
const ProcessAdm& adm; //!< Process administrator
};
/*!
\brief Class for representing the system matrix in ISTL format.
*/
class ISTLMatrix : public SparseMatrix
{
public:
//! \brief Constructor.
ISTLMatrix(const ProcessAdm& padm, const LinSolParams& spar,
LinAlg::LinearSystemType ltype);
//! \brief Copy constructor.
ISTLMatrix(const ISTLMatrix& A);
//! \brief The destructor frees the dynamically allocated arrays.
virtual ~ISTLMatrix();
//! \brief Returns the matrix type.
virtual Type getType() const { return ISTL; }
//! \brief Returns the dimension of the system matrix.
virtual size_t dim(int = 1) const { return 0; }
//! \brief Creates a copy of the system matrix and returns a pointer to it.
virtual SystemMatrix* copy() const { return new ISTLMatrix(*this); }
//! \brief Initializes the element assembly process.
//! \details Must be called once before the element assembly loop.
//! The PETSc data structures are initialized and the all symbolic operations
//! that are needed before the actual assembly can start are performed.
//! \param[in] sam Auxiliary data describing the FE model topology, etc.
virtual void initAssembly(const SAM& sam, bool);
//! \brief Initializes the matrix to zero assuming it is properly dimensioned.
virtual void init();
//! \brief Begins communication step needed in parallel matrix assembly.
//! \details Must be called together with endAssembly after matrix assembly
//! is completed on each processor and before the linear system is solved.
virtual bool beginAssembly();
//! \brief Ends communication step needed in parallel matrix assembly.
//! \details Must be called together with beginAssembly after matrix assembly
//! is completed on each processor and before the linear system is solved.
virtual bool endAssembly();
//! \brief Solves the linear system of equations for a given right-hand-side.
//! \param B Right-hand-side vector on input, solution vector on output
//! \param[in] newLHS \e true if the left-hand-side matrix has been updated
virtual bool solve(SystemVector& B, bool newLHS, Real*);
//! \brief Solves the linear system of equations for a given right-hand-side.
//! \param[in] B Right-hand-side vector
//! \param[out] x Solution vector
//! \param[in] newLHS \e true if the left-hand-side matrix has been updated
virtual bool solve(const SystemVector& B, SystemVector& x, bool newLHS);
//! \brief Returns the L-infinity norm of the matrix.
virtual Real Linfnorm() const;
//! \brief Returns the ISTL matrix (for assignment).
virtual ISTL::Mat& getMatrix() { return A; }
//! \brief Returns the ISTL matrix (for read access).
virtual const ISTL::Mat& getMatrix() const { return A; }
protected:
ISTL::Mat A; //!< The actual ISTL matrix
std::unique_ptr<ISTL::Operator> op; //!< The matrix adapter
std::unique_ptr<ISTL::InverseOperator> solver; //!< Solver to use
std::unique_ptr<ISTL::Preconditioner> pre; //!< Preconditioner to use
const ProcessAdm& adm; //!< Process administrator
ISTLSolParams solParams; //!< Linear solver parameters
bool setParams; //!< If linear solver parameters are set
int nLinSolves; //!< Number of linear solves
LinAlg::LinearSystemType linsysType; //!< Linear system type
};

510
src/LinAlg/ISTLSolParams.C Normal file
View File

@@ -0,0 +1,510 @@
// $Id$
//==============================================================================
//!
//! \file ISTLSolParams.C
//!
//! \date Mar 10 2016
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Linear solver parameters for ISTL.
//!
//==============================================================================
#include "ISTLSolParams.h"
#include "ASMstruct.h"
#include "LinSolParams.h"
#include "ProcessAdm.h"
#include "SAMpatch.h"
#include <dune/istl/overlappingschwarz.hh>
#include <dune/istl/paamg/amg.hh>
#include <dune/istl/matrixmatrix.hh>
#include <dune/common/version.hh>
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3)
#include <dune/istl/paamg/fastamg.hh>
#include <dune/istl/paamg/twolevelmethod.hh>
#endif
namespace ISTL {
BlockPreconditioner::BlockPreconditioner(const ISTL::Mat& A,
const DomainDecomposition& dd_,
const std::string& schurType) :
dd(dd_)
{
blocks.resize(dd.getNoBlocks()*dd.getNoBlocks()+1);
size_t k = 0;
for (size_t i = 0; i < dd.getNoBlocks(); ++i)
for (size_t j = 0; j < dd.getNoBlocks(); ++j)
extractBlock(blocks[k++], A, dd.getBlockEqs(i), dd.getBlockEqs(j), dd.getG2LEQ(j));
// Scale blocks[1] with inverse diagonal of blocks[0]
blocks.back() = blocks[1];
for (auto row = blocks.back().begin(); row != blocks.back().end(); ++row)
for (auto it = row->begin(); it != row->end(); ++it)
*it /= blocks[0][row.index()][row.index()];
// blocks[1] = D - C*diag(A)^-1*B
ISTL::Mat SP;
Dune::matMultMat(SP, blocks[2], blocks.back());
blocks[2] = blocks[1];
subtractMatrices(blocks[1], blocks[3], SP);
// clear memory for unused blocks
blocks.resize(schurType == "diag" ? 2 : 3);
blockPre.resize(dd.getNoBlocks());
blockOp.resize(dd.getNoBlocks());
}
void BlockPreconditioner::pre(ISTL::Vec& x, ISTL::Vec& b)
{
ISTL::Vec tempx, tempb;
for (size_t block = 0; block < dd.getNoBlocks(); ++block) {
ISTL::Vec tempx, tempb;
tempx.resize(dd.getBlockEqs(block).size());
tempb.resize(dd.getBlockEqs(block).size());
size_t i = 0;
for (auto& it : dd.getBlockEqs(block)) {
tempx[i] = x[it-1];
tempb[i++] = b[it-1];
}
blockPre[block]->pre(tempx, tempb);
i = 0;
for (auto& it : dd.getBlockEqs(block)) {
x[it-1] = tempx[i];
b[it-1] = tempb[i++];
}
}
}
void BlockPreconditioner::apply(ISTL::Vec& v, const ISTL::Vec& d)
{
// backwards due to upper schur complement
ISTL::Vec tempx, tempb;
for (int block = dd.getNoBlocks()-1; block >= 0; --block) {
tempb.resize(dd.getBlockEqs(block).size());
size_t i = 0;
for (auto& it : dd.getBlockEqs(block))
tempb[i++] = d[it-1];
if (block == 0 && blocks.size() > 2)
blocks.back().mmv(tempx, tempb);
tempx.resize(dd.getBlockEqs(block).size());
tempx = 0;
blockPre[block]->apply(tempx, tempb);
i = 0;
for (auto& it : dd.getBlockEqs(block))
v[it-1] = tempx[i++];
}
}
void BlockPreconditioner::post(ISTL::Vec& x)
{
ISTL::Vec tempx, tempb;
for (size_t block = 0; block < dd.getNoBlocks(); ++block) {
ISTL::Vec tempx;
tempx.resize(dd.getBlockEqs(block).size());
size_t i = 0;
for (auto& it : dd.getBlockEqs(block))
tempx[i++] = x[it-1];
blockPre[block]->post(tempx);
i = 0;
for (auto& it : dd.getBlockEqs(block))
x[it-1] = tempx[i++];
}
}
void BlockPreconditioner::extractBlock(ISTL::Mat& B, const ISTL::Mat& A,
const std::set<int>& eqs_row1,
const std::set<int>& eqs_col1,
const std::map<int,int>& eqs_col_g2l)
{
std::vector<int> eqs_row(eqs_row1.begin(), eqs_row1.end());
std::vector<int> eqs_col(eqs_col1.begin(), eqs_col1.end());
size_t sum=0;
std::vector<std::set<int>> adj;
adj.resize(eqs_row.size());
size_t i = 0;
for (auto& it3 : eqs_row) {
auto it = A.begin()+it3-1;
for (auto it2 = it->begin(); it2 != it->end(); ++it2) {
auto itc = eqs_col_g2l.find(it2.index()+1);
if (itc != eqs_col_g2l.end()) {
adj[i].insert(itc->second-1);
++sum;
}
}
++i;
}
B.setSize(eqs_row.size(), eqs_col.size(), sum);
B.setBuildMode(ISTL::Mat::random);
for (size_t i = 0; i < adj.size(); i++)
B.setrowsize(i,adj[i].size());
B.endrowsizes();
for (size_t i = 0; i < adj.size(); i++)
for (auto& it : adj[i])
B.addindex(i, it);
B.endindices();
B = 0;
for (auto it = B.begin(); it != B.end(); ++it)
for (auto it2 = it->begin(); it2 != it->end(); ++it2)
*it2 = A[eqs_row[it.index()]-1][eqs_col[it2.index()]-1];
}
void BlockPreconditioner::subtractMatrices(ISTL::Mat& A, const ISTL::Mat& B,
const ISTL::Mat& C)
{
size_t sum=0;
std::vector<std::set<int>> adj;
adj.resize(B.N());
for (auto row = B.begin(); row != B.end(); ++row) {
for (auto it = row->begin(); it != row->end(); ++it)
adj[row.index()].insert(it.index());
}
for (auto row = C.begin(); row != C.end(); ++row) {
for (auto it = row->begin(); it != row->end(); ++it)
adj[row.index()].insert(it.index());
sum += adj[row.index()].size();
}
A.setSize(B.N(), B.M(), sum);
A.setBuildMode(ISTL::Mat::random);
for (size_t i = 0; i < adj.size(); i++)
A.setrowsize(i,adj[i].size());
A.endrowsizes();
for (size_t i = 0; i < adj.size(); i++)
for (auto& it : adj[i])
A.addindex(i, it);
A.endindices();
A = 0;
for (auto row = B.begin(); row != B.end(); ++row)
for (auto it = row->begin(); it != row->end(); ++it)
A[row.index()][it.index()] = *it;
for (auto row = C.begin(); row != C.end(); ++row)
for (auto it = row->begin(); it != row->end(); ++it)
A[row.index()][it.index()] -= *it;
}
} // namespace ISTL
/*! \brief Helper template for setting up a solver with the appropriate
preconditioner type.
\details We cannot instance using dynamic polymorphism, the solver need
access to the real type for the preconditioner. We can however
call the solver in the interface class scope afterwards.
*/
template<class Prec>
static ISTL::InverseOperator* setupWithPreType(const LinSolParams& solParams,
ISTL::Operator& op,
Prec& pre)
{
std::string type = solParams.getStringValue("type");
double rtol = solParams.getDoubleValue("rtol");
int maxits = solParams.getIntValue("maxits");
int verbosity = solParams.getIntValue("verbosity");
int restart = solParams.getIntValue("gmres_restart_iterations");
if (type == "bcgs")
return new Dune::BiCGSTABSolver<ISTL::Vec>(op, pre, rtol, maxits, verbosity);
else if (type == "cg")
return new Dune::CGSolver<ISTL::Vec>(op, pre, rtol, maxits, verbosity);
else if (type == "minres")
return new Dune::MINRESSolver<ISTL::Vec>(op, pre, rtol, maxits, verbosity);
else if (type == "gmres")
return new Dune::RestartedGMResSolver<ISTL::Vec>(op, pre, rtol, restart,
maxits, verbosity);
return nullptr;
}
/*! \brief Helper template for setting up an AMG preconditioner with a given smoother */
template<class Smoother>
static ISTL::Preconditioner* setupAMG(const LinSolParams& params,
size_t block, ISTL::Operator& op,
std::unique_ptr<ISTL::InverseOperator>* solver)
{
// The coupling metric used in the AMG
typedef Dune::Amg::FirstDiagonal CouplingMetric;
// The coupling criterion used in the AMG
typedef Dune::Amg::SymmetricCriterion<ISTL::Mat, CouplingMetric> CritBase;
// The coarsening criterion used in the AMG
typedef Dune::Amg::CoarsenCriterion<CritBase> Criterion;
Criterion crit;
typedef typename Dune::Amg::AMG<ISTL::Operator, ISTL::Vec, Smoother> AMG;
typename AMG::SmootherArgs args;
args.relaxationFactor = 1.0;
args.iterations = std::max(1, params.getBlock(block).getIntValue("multigrid_no_smooth"));
if (params.getBlock(block).hasValue("multigrid_max_coarse_size"))
crit.setCoarsenTarget(params.getBlock(block).getIntValue("multigrid_max_coarse_size"));
if (params.getBlock(block).hasValue("multigrid_no_smooth")) {
int val = params.getBlock(block).getIntValue("multigrid_no_smooth");
crit.setNoPreSmoothSteps(val);
crit.setNoPostSmoothSteps(val);
}
if (params.getBlock(block).hasValue("multigrid_max_coarse_size"))
crit.setCoarsenTarget(params.getBlock(block).getIntValue("multigrid_max_coarse_size"));
auto result = new AMG(op, crit, args);
if (solver)
solver->reset(setupWithPreType(params, op, *result));
return result;
}
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3)
/*! \brief Helper template for setting up a AMG preconditioner
*! with fine smoother differing from the other levels */
template<class Smoother, class FineSmoother>
static ISTL::Preconditioner* setupAMG2_full(const LinSolParams& params, size_t block,
ISTL::Operator& op,
std::unique_ptr<ISTL::InverseOperator>* solver,
FineSmoother* fsmooth)
{
typedef Dune::Amg::FirstDiagonal CouplingMetric;
typedef Dune::Amg::SymmetricCriterion<ISTL::Mat,CouplingMetric> CritBase;
typedef Dune::Amg::CoarsenCriterion<CritBase> Criterion;
typedef Dune::Amg::AggregationLevelTransferPolicy<ISTL::Operator,Criterion> TransferPolicy;
typedef Dune::Amg::OneStepAMGCoarseSolverPolicy<ISTL::Operator,Smoother,Criterion> CoarsePolicy;
typedef typename Dune::Amg::SmootherTraits<Smoother>::Arguments SmootherArgs;
typedef typename Dune::Amg::TwoLevelMethod<ISTL::Operator, CoarsePolicy, FineSmoother> AMG2;
SmootherArgs args;
args.relaxationFactor = 1.0;
Criterion crit;
if (params.getBlock(block).hasValue("multigrid_max_coarse_size"))
crit.setCoarsenTarget(params.getBlock(block).getIntValue("multigrid_max_coarse_size"));
if (params.getBlock(block).hasValue("multigrid_no_smooth")) {
int val = params.getBlock(block).getIntValue("multigrid_no_smooth");
crit.setNoPreSmoothSteps(val);
crit.setNoPostSmoothSteps(val);
}
if (params.getBlock(block).hasValue("multigrid_max_coarse_size"))
crit.setCoarsenTarget(params.getBlock(block).getIntValue("multigrid_max_coarse_size"));
CoarsePolicy coarsePolicy(args, crit);
TransferPolicy policy(crit);
Dune::shared_ptr<FineSmoother> fsp(fsmooth);
auto result = new AMG2(op, fsp, policy, coarsePolicy);
if (solver)
solver->reset(setupWithPreType(params, op, *result));
return result;
}
template<class FineSmoother>
static ISTL::Preconditioner* setupAMG2_smoother(const LinSolParams& params, size_t block,
ISTL::Operator& op,
std::unique_ptr<ISTL::InverseOperator>* solver,
FineSmoother* fsmooth)
{
std::string smoother = params.getBlock(block).getStringValue("multigrid_smoother");
if (smoother == "ilu")
return setupAMG2_full<ISTL::ILU0>(params, block, op, solver, fsmooth);
else if (smoother == "sor")
return setupAMG2_full<ISTL::SOR>(params, block, op, solver, fsmooth);
else if (smoother == "ssor")
return setupAMG2_full<ISTL::SSOR>(params, block, op, solver, fsmooth);
else if (smoother == "jacobi")
return setupAMG2_full<ISTL::GJ>(params, block, op, solver, fsmooth);
else {
std::cerr << "** ISTLSolParams ** Invalid smoother " << smoother << "." << std::endl;
return nullptr;
}
}
static ISTL::Preconditioner* setupAMG2(const LinSolParams& params, size_t block,
ISTL::Operator& op, std::unique_ptr<ISTL::InverseOperator>* solver)
{
std::string smoother = params.getBlock(block).getStringValue("multigrid_finesmoother");
if (params.getBlock(block).getStringValue("multigrid_finesmoother") == "ilu") {
auto fsmooth = new ISTL::ILU0(op.getmat(), 1.0);
return setupAMG2_smoother(params, block, op, solver, fsmooth);
} else if (params.getBlock(block).getStringValue("multigrid_finesmoother") == "sor") {
auto fsmooth = new ISTL::SOR(op.getmat(), 1, 1.0);
return setupAMG2_smoother(params, block, op, solver, fsmooth);
} else if (params.getBlock(block).getStringValue("multigrid_finesmoother") == "ssor") {
auto fsmooth = new ISTL::SSOR(op.getmat(), 1, 1.0);
return setupAMG2_smoother(params, block, op, solver, fsmooth);
} else if (params.getBlock(block).getStringValue("multigrid_finesmoother") == "jacobi") {
auto fsmooth = new ISTL::GJ(op.getmat(), 1, 1.0);
return setupAMG2_smoother(params, block, op, solver, fsmooth);
} else {
std::cerr << "** ISTLSolParams ** Invalid fine smoother " << smoother << "." << std::endl;
return nullptr;
}
}
#endif
/*! \brief Conditionally setup a KSP */
template<class Type>
static ISTL::Preconditioner*
setupSolver(Type* pre,
ISTL::Operator& op,
const LinSolParams& solParams,
std::unique_ptr<ISTL::InverseOperator>* solver)
{
if (solver)
solver->reset(setupWithPreType(solParams, op, *pre));
return pre;
}
ISTL::Preconditioner* ISTLSolParams::setupPCInternal(ISTL::Mat& A,
ISTL::Operator& op,
size_t block,
std::unique_ptr<ISTL::InverseOperator>* solver)
{
std::string prec = solParams.getBlock(block).getStringValue("pc");
if (prec == "ilu") {
int fill_level = solParams.getBlock(block).getIntValue("ilu_fill_level");
if (fill_level == 0)
return setupSolver(new ISTL::ILU0(A, 1.0), op, solParams, solver);
else
return setupSolver(new ISTL::ILUn(A, fill_level, 1.0),
op, solParams, solver);
} else if (prec == "lu")
return setupSolver(new ISTL::LU(new ISTL::LUType(A)),
op, solParams, solver);
else if (prec == "sor")
return setupSolver(new ISTL::SOR(A, 1, 1.0), op, solParams, solver);
else if (prec == "ssor")
return setupSolver(new ISTL::SSOR(A, 1, 1.0), op, solParams, solver);
else if (prec == "jacobi")
return setupSolver(new ISTL::GJ(A, 1, 1.0), op, solParams, solver);
else if (prec == "gs")
return setupSolver(new ISTL::GS(A, 1, 1.0), op, solParams, solver);
else if (prec == "asm" || prec == "asmlu") {
size_t nx = solParams.getBlock(block).getIntValue("asm_nx");
nx = std::max(1ul, nx);
size_t ny = solParams.getBlock(block).getIntValue("asm_ny");
ny = std::max(1ul, ny);
size_t nz = solParams.getBlock(block).getIntValue("asm_nz");
nz = std::max(1ul, nz);
int overlap = solParams.getBlock(block).getIntValue("asm_overlap");
auto locSubdDofs = adm.dd.getSubdomains(nx, ny, nz, overlap, block);
if (prec == "asmlu") {
ISTL::ASMLU::subdomain_vector ddofs(locSubdDofs.size());
for (size_t i = 0; i < locSubdDofs.size(); ++i)
for (const auto& it : locSubdDofs[i])
ddofs[i].insert(it-1);
return setupSolver(new ISTL::ASMLU(A, ddofs), op, solParams, solver);
} else {
ISTL::ASM::subdomain_vector ddofs(locSubdDofs.size());
for (size_t i = 0; i < locSubdDofs.size(); ++i) {
for (size_t i = 0; i < locSubdDofs.size(); ++i)
for (const auto& it : locSubdDofs[i])
ddofs[i].insert(it-1);
}
return setupSolver(new ISTL::ASM(A, ddofs), op, solParams, solver);
}
} else if (prec == "amg" || prec == "gamg") {
if (solParams.getBlock(block).getStringValue("multigrid_smoother") !=
solParams.getBlock(block).getStringValue("multigrid_finesmoother")) {
#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3)
return setupAMG2(solParams, block, op, solver);
#else
std::cerr << "** ISTLSolParams ** Separate fine smoother not implemented." << std::endl;
return nullptr;
#endif
} else {
std::string smoother = solParams.getBlock(block).getStringValue("multigrid_smoother");
if (smoother.empty()) {
std::cerr << "** ISTLSolParams ** No smoother defined for AMG, defaulting to ILU" << std::endl;
smoother = "ilu";
}
if (smoother == "ilu")
return setupAMG<ISTL::ILU0>(solParams, block, op, solver);
else if (smoother == "sor")
return setupAMG<ISTL::SOR>(solParams, block, op, solver);
else if (smoother == "ssor")
return setupAMG<ISTL::SSOR>(solParams, block, op, solver);
else if (smoother == "jacobi")
return setupAMG<ISTL::GJ>(solParams, block, op, solver);
}
}
return nullptr;
}
std::tuple<std::unique_ptr<ISTL::InverseOperator>,
std::unique_ptr<ISTL::Preconditioner>,
std::unique_ptr<ISTL::Operator>> ISTLSolParams::setupPC(ISTL::Mat& A)
{
std::unique_ptr<ISTL::InverseOperator> solver;
std::unique_ptr<ISTL::Preconditioner> pre;
std::unique_ptr<ISTL::Operator> op;
if (solParams.getNoBlocks() > 2) {
std::cerr << "*** ISTLSolParams ** More than two blocks are not implemented." << std::endl;
return std::make_tuple(nullptr, nullptr, nullptr);
}
if (solParams.getNoBlocks() > 1) {
op.reset(new ISTL::Operator(A));
ISTL::BlockPreconditioner* bpre = new ISTL::BlockPreconditioner(A, adm.dd, solParams.getStringValue("schur"));
pre.reset(bpre);
for (size_t i = 0; i < adm.dd.getNoBlocks(); ++i)
bpre->getBlockPre(i).reset(setupPCInternal(bpre->getBlock(i),
bpre->getBlockOp(i), i, nullptr));
solver.reset(setupWithPreType(solParams, *op, *bpre));
} else {
#ifdef HAVE_MPI
if (adm.isParallel()) {
Dune::IndexInfoFromGrid<int, int> index;
for (size_t i = 0; i < adm.dd.getMLGEQ().size(); ++i)
index.addLocalIndex(std::make_tuple(adm.dd.getGlobalEq(i+1), i, 0));
Dune::OwnerOverlapCopyCommunication<int,int> comm(index, *adm.getCommunicator());
//ISTL::ParMatrixAdapter* nop = new ISTL::ParMatrixAdapter(A, comm);
//op.reset(nop);
//pre.reset(setupPCInternal(A, *nop, 0, &solver));
} else
#endif
{
ISTL::Operator* nop = new ISTL::Operator(A);
op.reset(nop);
pre.reset(setupPCInternal(A, *nop, 0, &solver));
}
}
return std::make_tuple(std::move(solver), std::move(pre), std::move(op));
}

258
src/LinAlg/ISTLSolParams.h Normal file
View File

@@ -0,0 +1,258 @@
// $Id$
//==============================================================================
//!
//! \file ISTLSolParams.h
//!
//! \date Mar 10 2016
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Linear solver parameters for ISTL matrices.
//! \details Includes linear solver method, preconditioner
//! and convergence criteria.
//!
//==============================================================================
#ifndef _ISTL_SOLPARAMS_H
#define _ISTL_SOLPARAMS_H
#include "ISTLSupport.h"
#include <dune/istl/operators.hh>
#include <dune/istl/solvercategory.hh>
#include <iostream>
#include <set>
#include <string>
#include <vector>
class DomainDecomposition;
class LinSolParams;
class ProcessAdm;
/*! This implements a Schur-decomposition based preconditioner for the
* block system
* [A B]
* [C D]
*
* The preconditioner is
* [Apre B]
* [ P]
* Here Apre is some preconditioner for A and P some preconditioner for
* S = D - C*diag(A)^-1*B. The B block may be dropped.
!*/
namespace ISTL {
class BlockPreconditioner : public Preconditioner {
public:
// define the category
enum {
//! \brief The category the preconditioner is part of.
category=Dune::SolverCategory::sequential
};
//! \brief Constructor
//! \param[in] A The system matrix
//! \param[in] dd Domain decomposition
BlockPreconditioner(const ISTL::Mat& A, const DomainDecomposition& dd_,
const std::string& schurType);
//! \brief Destructor
virtual ~BlockPreconditioner()
{}
//! \brief Preprocess preconditioner
virtual void pre(ISTL::Vec& x, ISTL::Vec& b);
//! \brief Applies the preconditioner
//! \param[out] v The resulting vector
//! \param[in] d The vector to apply the preconditioner to
virtual void apply(ISTL::Vec& v, const ISTL::Vec& d);
//! \brief Post-process function
virtual void post(ISTL::Vec& x);
//! \brief Obtain reference to a block preconditioner
std::unique_ptr<ISTL::Preconditioner>& getBlockPre(size_t block)
{
return blockPre[block];
}
//! \brief Obtain reference to a block matrix
ISTL::Mat& getBlock(size_t block)
{
return blocks[block];
}
//! \brief Obtain matrix adaptor for a block matrix
ISTL::Operator& getBlockOp(size_t block)
{
if (!blockOp[block])
blockOp[block].reset(new ISTL::Operator(blocks[block]));
return *blockOp[block];
}
protected:
//! \brief Build block from block equations.
static void extractBlock(ISTL::Mat& B, const ISTL::Mat& A,
const std::set<int>& eqs_row,
const std::set<int>& eqs_col,
const std::map<int,int>& eqs_col_g2l);
//! \brief Find A = B - C
static void subtractMatrices(ISTL::Mat& A,
const ISTL::Mat& B,
const ISTL::Mat& C);
std::vector<std::unique_ptr<ISTL::Preconditioner>> blockPre; //!< The preconditioners
std::vector<std::unique_ptr<ISTL::Operator>> blockOp; //!< The matrix adaptors for the blocks
std::vector<ISTL::Mat> blocks; //!< Matrix blocks
const DomainDecomposition& dd; //!< Domain decomposition
};
#ifdef HAVE_MPI
/**
* \brief An overlapping schwarz operator.
*
* This operator represents a parallel matrix product using
* sequential data structures together with a parallel index set
* describing an overlapping domain decomposition and the communication.
* \tparam M The type of the sequential matrix to use,
* e.g. BCRSMatrix or another matrix type fulfilling the
* matrix interface of ISTL.
* \tparam X The type of the sequential vector to use for the left hand side,
* e.g. BlockVector or another type fulfilling the ISTL
* vector interface.
* \tparam Y The type of the sequential vector to use for the right hand side,
* e..g. BlockVector or another type fulfilling the ISTL
* vector interface.
* \tparam C The type of the communication object.
* This must either be OwnerOverlapCopyCommunication or a type
* implementing the same interface.
*
* This is a modified version which does summation of the vector after evaluation.
*/
template<class M, class X, class Y, class C>
class OverlappingSchwarzOperator : public Dune::AssembledLinearOperator<M,X,Y>
{
public:
//! \brief The type of the matrix we operate on.
//!
//! E.g. BCRSMatrix or another matrix type fulfilling the
//! matrix interface of ISTL
typedef M matrix_type;
//! \brief The type of the domain.
//!
//! E.g. BlockVector or another type fulfilling the ISTL
//! vector interface.
typedef X domain_type;
//! \brief The type of the range.
//!
//! E.g. BlockVector or another type fulfilling the ISTL
//! vector interface.
typedef Y range_type;
//! \brief The field type of the range
typedef typename X::field_type field_type;
//! \brief The type of the communication object.
//!
//! This must either be OwnerOverlapCopyCommunication or a type
//! implementing the same interface.
typedef C communication_type;
enum {
//! \brief The solver category.
category=Dune::SolverCategory::overlapping
};
/**
* @brief constructor: just store a reference to a matrix.
*
* @param A The assembled matrix.
* @param com The communication object for syncing overlap and copy
* data points. (E.~g. OwnerOverlapCopyCommunication )
*/
OverlappingSchwarzOperator (const matrix_type& A, const communication_type& com)
: _A_(A), communication(com)
{}
//! apply operator to x: \f$ y = A(x) \f$
virtual void apply (const X& x, Y& y) const
{
// Y y2(y.size());
_A_.umv(x,y);
communication.addOwnerOverlapToAll(y,y);
}
//! apply operator to x, scale and add: \f$ y = y + \alpha A(x) \f$
virtual void applyscaleadd (field_type alpha, const X& x, Y& y) const
{
Y y2(y.size());
_A_.usmv(alpha, x, y2);
y = 0;
communication.addOwnerOverlapToAll(y2,y);
}
//! get the sequential assembled linear operator.
virtual const matrix_type& getmat () const
{
return _A_;
}
private:
const matrix_type& _A_;
const communication_type& communication;
};
typedef OverlappingSchwarzOperator<ISTL::Mat,ISTL::Vec,ISTL::Vec,Dune::OwnerOverlapCopyCommunication<int,int>> ParMatrixAdapter;
#endif
}
/*!
\brief Class for ISTL solver parameters.
\details It contains information about solver method, preconditioner
and convergence criteria.
*/
class ISTLSolParams
{
public:
//! \brief Constructor.
//! \param spar Linear solver parameters to use
//! \param padm Process administrator to use
ISTLSolParams(const LinSolParams& spar, const ProcessAdm& padm) :
solParams(spar), adm(padm)
{}
//! \brief Setup solver and preconditioner.
//! \param A Matrix to use
//! \return tuple with (solver, preconditioner, op)
std::tuple<std::unique_ptr<ISTL::InverseOperator>,
std::unique_ptr<ISTL::Preconditioner>,
std::unique_ptr<ISTL::Operator>>
setupPC(ISTL::Mat& A);
//! \brief Obtain linear solver parameters.
const LinSolParams& get() const { return solParams; }
protected:
//! \brief Internal helper function for setting up a preconditioner.
//! \param A Matrix to construct preconditioner for
//! \param op Matrix adaptor to use (used with AMG)
//! \param block Block to read settings from
//! \param solver Solver object to instance. nullptr to do nothing.
ISTL::Preconditioner* setupPCInternal(ISTL::Mat& A,
ISTL::Operator& op,
size_t block,
std::unique_ptr<ISTL::InverseOperator>* solver);
const LinSolParams& solParams; //!< Reference to linear solver parameters.
const ProcessAdm& adm; //!< Reference to process administrator.
};
#endif

76
src/LinAlg/ISTLSupport.h Normal file
View File

@@ -0,0 +1,76 @@
// $Id$
//==============================================================================
//!
//! \file ISTLSupport.h
//!
//! \date May 12 2016
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief IFEM ISTL support
//!
//==============================================================================
#ifndef _ISTL_SUPPORT_H_
#define _ISTL_SUPPORT_H_
#include <vector>
#ifdef HAS_ISTL
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/overlappingschwarz.hh>
#include <dune/istl/preconditioners.hh>
#include <dune/istl/solvers.hh>
#include <dune/istl/schwarz.hh>
#include <dune/istl/owneroverlapcopy.hh>
#ifdef HAVE_SUPERLU
#include <dune/istl/superlu.hh>
#endif
#ifdef HAVE_UMFPACK
#include <dune/istl/umfpack.hh>
#endif
namespace ISTL
{
typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1>> Mat; //!< A sparse system matrix
typedef Dune::BlockVector<Dune::FieldVector<double,1>> Vec; //!< A vector
typedef Dune::MatrixAdapter<Mat,Vec,Vec> Operator; //!< A serial matrix operator
typedef Dune::InverseOperator<Vec, Vec> InverseOperator; //!< Linear system inversion abstraction
typedef Dune::Preconditioner<Vec,Vec> Preconditioner; //!< Preconditioner abstraction
/*! \brief Wrapper template to avoid memory leaks */
template<template<class M> class Pre>
class IOp2Pre : public Dune::InverseOperator2Preconditioner<Pre<ISTL::Mat>, Dune::SolverCategory::sequential> {
typedef Dune::InverseOperator2Preconditioner<Pre<ISTL::Mat>, Dune::SolverCategory::sequential> SolverType;
public:
IOp2Pre(Pre<ISTL::Mat>* iop) : SolverType(*iop)
{
m_op.reset(iop);
}
protected:
std::unique_ptr<Pre<ISTL::Mat>> m_op;
};
#if defined(HAVE_UMFPACK)
typedef Dune::UMFPack<ISTL::Mat> LUType;
typedef IOp2Pre<Dune::UMFPack> LU;
#elif defined(HAVE_SUPERLU)
typedef Dune::SuperLU<ISTL::Mat> LUType;
typedef IOp2Pre<Dune::SuperLU> LU;
#endif
// Preconditioner types
typedef Dune::SeqILU0<Mat, Vec, Vec> ILU0; //!< Sequential ILU(0)
typedef Dune::SeqILUn<Mat, Vec, Vec> ILUn; //!< Sequential ILU(n)
typedef Dune::SeqSOR<Mat, Vec, Vec> SOR; //!< Sequential SOR
typedef Dune::SeqSSOR<Mat, Vec,Vec> SSOR; //!< Sequential SSOR
typedef Dune::SeqJac<Mat, Vec, Vec> GJ; //!< Sequential Gauss-Jacobi
typedef Dune::SeqGS<Mat, Vec, Vec> GS; //!< Sequential Gauss-Seidel
typedef Dune::SeqOverlappingSchwarz<Mat, Vec,
Dune::AdditiveSchwarzMode,
LUType> ASMLU; //!< Sequential additive Schwarz w/ LU subdomain solvers
typedef Dune::SeqOverlappingSchwarz<Mat, Vec> ASM; //!< Sequential additive Schwarz w/ ILU subdomain solvers
}
#endif
#endif

View File

@@ -13,12 +13,15 @@
#include "SystemMatrix.h"
#include "DenseMatrix.h"
#ifdef HAS_ISTL
#include "ISTLMatrix.h"
#endif
#include "SPRMatrix.h"
#include "SparseMatrix.h"
#ifdef HAS_PETSC
#include "LinSolParams.h"
#include "PETScMatrix.h"
#endif
#include "LinSolParams.h"
SystemVector* SystemVector::create (const ProcessAdm& adm, Type vectorType)
@@ -26,6 +29,9 @@ SystemVector* SystemVector::create (const ProcessAdm& adm, Type vectorType)
switch (vectorType)
{
case STD : return new StdVector();
#ifdef HAS_ISTL
case ISTL : return new ISTLVector(adm);
#endif
#ifdef HAS_PETSC
case PETSC : return new PETScVector(adm);
#endif
@@ -73,6 +79,10 @@ SystemMatrix* SystemMatrix::create (const ProcessAdm& padm, Type matrixType,
if (matrixType == PETSC)
return new PETScMatrix(padm,spar,ltype);
#endif
#ifdef HAS_ISTL
if (matrixType == ISTL)
return new ISTLMatrix(padm,spar,ltype);
#endif
return SystemMatrix::create(padm,matrixType,ltype);
}
@@ -88,7 +98,16 @@ SystemMatrix* SystemMatrix::create (const ProcessAdm& padm, Type matrixType,
<< std::endl;
exit(1);
}
#else
#endif
#ifndef HAS_ISTL
if (matrixType == ISTL) {
std::cerr <<"SystemMatrix::create: ISTL not compiled in, bailing out..."
<< std::endl;
exit(1);
}
#endif
#if defined(HAS_PETSC) || defined(HAS_ISTL)
// Use default settings when no parameters are provided by user
static LinSolParams defaultPar;
#endif
@@ -99,6 +118,9 @@ SystemMatrix* SystemMatrix::create (const ProcessAdm& padm, Type matrixType,
case SPR : return new SPRMatrix();
case SPARSE: return new SparseMatrix(SparseMatrix::SUPERLU,num_thread_SLU);
case SAMG : return new SparseMatrix(SparseMatrix::S_A_M_G);
#ifdef HAS_ISTL
case ISTL : return new ISTLMatrix(padm,defaultPar,ltype);
#endif
#ifdef HAS_PETSC
case PETSC : return new PETScMatrix(padm,defaultPar,ltype);
#endif

View File

@@ -30,7 +30,7 @@ class SystemVector
{
public:
//! \brief The available system vector formats.
enum Type { STD = 0, PETSC = 1 };
enum Type { STD = 0, PETSC = 1, ISTL = 2 };
//! \brief Static method creating a vector of the given type.
static SystemVector* create(const ProcessAdm& padm, Type vectorType = STD);
@@ -203,7 +203,7 @@ class SystemMatrix
public:
//! \brief The available system matrix formats.
enum Type { DENSE = 0, SPR = 1, SPARSE = 2, SAMG = 3,
PETSC = 4 };
PETSC = 4, ISTL = 5 };
//! \brief Static method creating a matrix of the given type.
static SystemMatrix* create(const ProcessAdm& padm, Type matrixType,

View File

@@ -0,0 +1,110 @@
//==============================================================================
//!
//! \file TestISTLMatrix.C
//!
//! \date Apr 27 2016
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Unit tests for parallel ISTL matrices
//!
//==============================================================================
#include "AlgEqSystem.h"
#include "ISTLMatrix.h"
#include "ISTLSolParams.h"
#include "SIM2D.h"
#include "ASMs2D.h"
#include "IntegrandBase.h"
#include "SAM.h"
#include "IFEM.h"
#include "gtest/gtest.h"
#include <fstream>
typedef std::vector<int> IntVec;
static IntVec readIntVector(const std::string& file)
{
std::vector<int> result;
std::ifstream f(file);
size_t size;
f >> size;
result.resize(size);
for (size_t j=0;j<size;++j)
f >> result[j];
return result;
}
class DummyIntegrandForDummies : public IntegrandBase {
public:
DummyIntegrandForDummies(unsigned short int n = 0) : IntegrandBase(n) {}
};
class InspectMatrixSIM : public SIM2D {
public:
InspectMatrixSIM(unsigned char n1 = 2, bool check = false) :
SIM2D(n1, check) { myProblem = new DummyIntegrandForDummies; }
InspectMatrixSIM(const std::vector<unsigned char>& n, bool check = false) :
SIM2D(n, check) { myProblem = new DummyIntegrandForDummies; }
SystemMatrix* getMatrix() { return myEqSys->getMatrix(0); }
SystemVector* getVector() { return myEqSys->getVector(0); }
};
TEST(TestISTLMatrix, AssembleMPI)
{
InspectMatrixSIM sim(1);
sim.read("src/LinAlg/Test/refdata/petsc_test.xinp");
sim.opt.solver = SystemMatrix::ISTL;
sim.preprocess();
sim.initSystem(SystemMatrix::ISTL);
Matrix stencil(4,4);
stencil(1,1) = stencil(2,2) = stencil(3,3) = stencil(4,4) = 1.0;
for (int iel = 1; iel <= sim.getSAM()->getNoElms(); ++iel)
sim.getMatrix()->assemble(stencil, *sim.getSAM(), iel);
sim.getMatrix()->beginAssembly();
sim.getMatrix()->endAssembly();
// now inspect the matrix
const ProcessAdm& adm = sim.getProcessAdm();
ISTL::Mat& mat = static_cast<ISTLMatrix*>(sim.getMatrix())->getMatrix();
ISTL::Vec b(mat.N()), b2(mat.N());
try {
Dune::OwnerOverlapCopyCommunication<int,int> comm(*adm.getCommunicator());
comm.indexSet().beginResize();
typedef Dune::ParallelLocalIndex<Dune::OwnerOverlapCopyAttributeSet::AttributeSet> LI;
for (size_t i = 0; i < adm.dd.getMLGEQ().size(); ++i) {
int gid = adm.dd.getGlobalEq(i+1);
comm.indexSet().add(gid-1, LI(i, gid >= adm.dd.getMinEq() ?
Dune::OwnerOverlapCopyAttributeSet::owner :
Dune::OwnerOverlapCopyAttributeSet::overlap));
}
comm.indexSet().endResize();
comm.remoteIndices().setIncludeSelf(true);
comm.remoteIndices().template rebuild<false>();
ISTL::ParMatrixAdapter op(mat, comm);
b = 1.0;
op.apply(b, b2);
} catch (Dune::ISTLError e) {
std::cerr << e << std::endl;
ASSERT_TRUE(false);
}
IntVec v = readIntVector("src/LinAlg/Test/refdata/petsc_matrix_diagonal.ref");
for (size_t i = 1; i <= adm.dd.getMLGEQ().size(); ++i)
ASSERT_FLOAT_EQ(v[adm.dd.getGlobalEq(i)-1], b2[i-1]);
}

View File

@@ -0,0 +1,205 @@
//==============================================================================
//!
//! \file TestISTLMatrix.C
//!
//! \date Apr 27 2016
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Unit tests for ISTL matrices
//!
//==============================================================================
#include "AlgEqSystem.h"
#include "ISTLMatrix.h"
#include "SIM2D.h"
#include "ASMs2D.h"
#include "IntegrandBase.h"
#include "SAM.h"
#include <dune/istl/io.hh>
#include "gtest/gtest.h"
#include <fstream>
typedef std::vector<int> IntVec;
static IntVec readIntVector(const std::string& file)
{
std::vector<int> result;
std::ifstream f(file);
size_t size;
f >> size;
result.resize(size);
for (size_t j=0;j<size;++j)
f >> result[j];
return result;
}
class DummyIntegrandForDummies : public IntegrandBase {
public:
DummyIntegrandForDummies(unsigned short int n = 0) : IntegrandBase(n) {}
};
class InspectMatrixSIM : public SIM2D {
public:
InspectMatrixSIM(unsigned char n1 = 2, bool check = false) :
SIM2D(n1, check) { myProblem = new DummyIntegrandForDummies; }
InspectMatrixSIM(const std::vector<unsigned char>& n, bool check = false) :
SIM2D(n, check) { myProblem = new DummyIntegrandForDummies; }
SystemMatrix* getMatrix() { return myEqSys->getMatrix(0); }
};
class InspectBlockPreconditioner : public ISTL::BlockPreconditioner {
public:
InspectBlockPreconditioner(const ISTL::Mat& A,
const DomainDecomposition& dd) :
ISTL::BlockPreconditioner(A, dd, "upper") {}
void extractBlock(ISTL::Mat& B, const ISTL::Mat& A,
const std::set<int>& eqs_row,
const std::set<int>& eqs_col,
const std::map<int,int>& eqs_col_g2l)
{
ISTL::BlockPreconditioner::extractBlock(B, A, eqs_row, eqs_col, eqs_col_g2l);
}
};
TEST(TestISTLMatrix, Assemble)
{
InspectMatrixSIM sim(1);
sim.read("src/LinAlg/Test/refdata/petsc_test.xinp");
sim.opt.solver = SystemMatrix::ISTL;
sim.preprocess();
sim.initSystem(SystemMatrix::ISTL);
Matrix stencil(4,4);
stencil(1,1) = stencil(2,2) = stencil(3,3) = stencil(4,4) = 1.0;
for (int iel = 1; iel <= sim.getSAM()->getNoElms(); ++iel)
sim.getMatrix()->assemble(stencil, *sim.getSAM(), iel);
sim.getMatrix()->beginAssembly();
sim.getMatrix()->endAssembly();
// now inspect the matrix
ISTL::Mat& mat = static_cast<ISTLMatrix*>(sim.getMatrix())->getMatrix();
IntVec v = readIntVector("src/LinAlg/Test/refdata/petsc_matrix_diagonal.ref");
ASSERT_EQ(mat.N(), v.size());
for (size_t i = 0; i < v.size(); ++i)
ASSERT_FLOAT_EQ(double(v[i]), mat[i][i]);
}
TEST(TestISTLMatrix, AssembleBasisBlocks)
{
InspectMatrixSIM sim({1,1});
sim.read("src/LinAlg/Test/refdata/petsc_test_blocks_basis.xinp");
sim.opt.solver = SystemMatrix::ISTL;
sim.preprocess();
sim.initSystem(SystemMatrix::ISTL);
Matrix stencil(13,13);
for (size_t i = 1; i<= 9; ++i)
stencil(i,i) = 1.0;
for (size_t i = 10; i<= 13; ++i)
stencil(i,i) = 2.0;
for (int iel = 1; iel <= sim.getSAM()->getNoElms(); ++iel)
sim.getMatrix()->assemble(stencil, *sim.getSAM(), iel);
sim.getMatrix()->beginAssembly();
sim.getMatrix()->endAssembly();
const ProcessAdm& adm = sim.getProcessAdm();
ISTL::Mat& A = static_cast<ISTLMatrix*>(sim.getMatrix())->getMatrix();
InspectBlockPreconditioner block(A, adm.dd);
// now inspect the matrix blocks
std::vector<ISTL::Mat> mat(5);
block.extractBlock(mat[0], A, adm.dd.getBlockEqs(0), adm.dd.getBlockEqs(0), adm.dd.getG2LEQ(0));
block.extractBlock(mat[1], A, adm.dd.getBlockEqs(0), adm.dd.getBlockEqs(1), adm.dd.getG2LEQ(1));
block.extractBlock(mat[2], A, adm.dd.getBlockEqs(1), adm.dd.getBlockEqs(0), adm.dd.getG2LEQ(0));
block.extractBlock(mat[3], A, adm.dd.getBlockEqs(1), adm.dd.getBlockEqs(1), adm.dd.getG2LEQ(1));
mat[4] = block.getBlock(1); // S = D
for (size_t b = 0; b < mat.size(); ++b) {
if (b == 0 || b >= 3) { // diagonal blocks
std::stringstream str;
str << "src/LinAlg/Test/refdata/petsc_matrix_diagonal_basis_block" << std::min(b/2 + 1, 2ul) << ".ref";
IntVec v = readIntVector(str.str());
ASSERT_EQ(v.size(), mat[b].N());
for (size_t i = 0; i < v.size(); ++i)
ASSERT_FLOAT_EQ(double(v[i]), mat[b][i][i]);
}
// check that no values outside the diagonal are != 0
for (auto r = mat[b].begin(); r != mat[b].end(); ++r) {
for (auto it = r->begin(); it != r->end(); ++it)
if (r.index() != it.index())
ASSERT_FLOAT_EQ(*it, 0.0);
}
}
}
TEST(TestISTLMatrix, AssembleComponentBlocks)
{
InspectMatrixSIM sim(2);
sim.read("src/LinAlg/Test/refdata/petsc_test_blocks_components.xinp");
sim.opt.solver = SystemMatrix::ISTL;
sim.preprocess();
sim.initSystem(SystemMatrix::ISTL);
Matrix stencil(4*2,4*2);
for (size_t i = 1; i<= 4; ++i) {
stencil(2*i-1,2*i-1) = 1.0;
stencil(2*i,2*i) = 2.0;
}
for (int iel = 1; iel <= sim.getSAM()->getNoElms(); ++iel)
sim.getMatrix()->assemble(stencil, *sim.getSAM(), iel);
sim.getMatrix()->beginAssembly();
sim.getMatrix()->endAssembly();
const ProcessAdm& adm = sim.getProcessAdm();
ISTL::Mat& A = static_cast<ISTLMatrix*>(sim.getMatrix())->getMatrix();
InspectBlockPreconditioner block(A, adm.dd);
// now inspect the matrix blocks
std::vector<ISTL::Mat> mat(5);
block.extractBlock(mat[0], A, adm.dd.getBlockEqs(0), adm.dd.getBlockEqs(0), adm.dd.getG2LEQ(0));
block.extractBlock(mat[1], A, adm.dd.getBlockEqs(0), adm.dd.getBlockEqs(1), adm.dd.getG2LEQ(1));
block.extractBlock(mat[2], A, adm.dd.getBlockEqs(1), adm.dd.getBlockEqs(0), adm.dd.getG2LEQ(0));
block.extractBlock(mat[3], A, adm.dd.getBlockEqs(1), adm.dd.getBlockEqs(1), adm.dd.getG2LEQ(1));
mat[4] = block.getBlock(1); // S = D
for (size_t b = 0; b < mat.size(); ++b) {
if (b == 0 || b >= 3) { // diagonal blocks
std::stringstream str;
str << "src/LinAlg/Test/refdata/petsc_matrix_diagonal_components_block" << std::min(b/2 + 1, 2ul) << ".ref";
IntVec v = readIntVector(str.str());
ASSERT_EQ(v.size(), mat[b].N());
for (size_t i = 0; i < v.size(); ++i)
ASSERT_FLOAT_EQ(double(v[i]), mat[b][i][i]);
}
// check that no values outside the diagonal are != 0
for (auto r = mat[b].begin(); r != mat[b].end(); ++r) {
for (auto it = r->begin(); it != r->end(); ++it)
if (r.index() != it.index())
ASSERT_FLOAT_EQ(*it, 0.0);
}
}
}

View File

@@ -0,0 +1,82 @@
//==============================================================================
//!
//! \file TestISTLPETScMatrix.C
//!
//! \date Apr 27 2016
//!
//! \author Arne Morten Kvarving / SINTEF
//!
//! \brief Unit tests comparing ISTL and PETSc matrices
//!
//==============================================================================
#include "AlgEqSystem.h"
#include "ISTLMatrix.h"
#include "PETScMatrix.h"
#include "SIM2D.h"
#include "ASMs2D.h"
#include "IntegrandBase.h"
#include "SAM.h"
#include <dune/istl/io.hh>
#include "gtest/gtest.h"
#include <fstream>
class DummyIntegrandForDummies : public IntegrandBase {
public:
DummyIntegrandForDummies(unsigned short int n = 0) : IntegrandBase(n) {}
};
class InspectMatrixSIM : public SIM2D {
public:
InspectMatrixSIM() :
SIM2D({1,1}, false) { myProblem = new DummyIntegrandForDummies; }
SystemMatrix* getMatrix() { return myEqSys->getMatrix(0); }
};
TEST(TestISTLPETScMatrix, SchurComplement)
{
Matrix stencil(13,13);
for (size_t i = 1; i<= 13; ++i)
for (size_t j = 1; j <= 13; ++j)
stencil(i,j) = 1.0;
std::array<InspectMatrixSIM,2> sim;
for (size_t i = 0; i < 2; ++i) {
sim[i].read("src/LinAlg/Test/refdata/petsc_test_blocks_basis.xinp");
sim[i].opt.solver = i == 0 ? SystemMatrix::PETSC : SystemMatrix::ISTL;
sim[i].preprocess();
sim[i].initSystem(i == 0 ? SystemMatrix::PETSC : SystemMatrix::ISTL);
for (int iel = 1; iel <= sim[i].getSAM()->getNoElms(); ++iel)
sim[i].getMatrix()->assemble(stencil, *sim[i].getSAM(), iel);
sim[i].getMatrix()->beginAssembly();
sim[i].getMatrix()->endAssembly();
}
const ProcessAdm& adm = sim[1].getProcessAdm();
ISTL::Mat& A = static_cast<ISTLMatrix*>(sim[1].getMatrix())->getMatrix();
ISTL::BlockPreconditioner block(A, adm.dd, "upper");
ISTL::Mat& S = block.getBlock(1);
PETScSolParams params(LinSolParams(), adm);
params.setupSchurComplement(static_cast<PETScMatrix*>(sim[0].getMatrix())->getBlockMatrices());
// check that matrices are the same
for (size_t r = 0; r < S.N(); ++r) {
const PetscInt* cols;
PetscInt ncols;
const PetscScalar* vals;
MatGetRow(params.getSchurComplement(), r, &ncols, &cols, &vals);
for (PetscInt i = 0; i < ncols; ++i)
ASSERT_FLOAT_EQ(vals[i], S[r][cols[i]]);
MatRestoreRow(params.getSchurComplement(), r, &ncols, &cols, &vals);
}
}

View File

@@ -68,6 +68,8 @@ void SIMoptions::setLinearSolver (const std::string& eqsolver)
solver = SystemMatrix::SAMG;
else if (eqsolver == "petsc")
solver = SystemMatrix::PETSC;
else if (eqsolver == "istl")
solver = SystemMatrix::ISTL;
}
@@ -223,6 +225,8 @@ bool SIMoptions::parseOldOptions (int argc, char** argv, int& i)
solver = SystemMatrix::SAMG;
else if (!strcmp(argv[i],"-petsc"))
solver = SystemMatrix::PETSC;
else if (!strcmp(argv[i],"-istl"))
solver = SystemMatrix::ISTL;
else if (!strncmp(argv[i],"-lag",4))
discretization = ASM::Lagrange;
else if (!strncmp(argv[i],"-spec",5))