#include "Stratimikos_DefaultLinearSolverBuilder.hpp"
#include "Thyra_LinearOpWithSolveFactoryHelpers.hpp"
#include "Thyra_PreconditionerFactoryHelpers.hpp"
#include "Thyra_DefaultInverseLinearOp.hpp"
#include "Thyra_DefaultMultipliedLinearOp.hpp"
#include "Thyra_EpetraLinearOp.hpp"
#include "Thyra_get_Epetra_Operator.hpp"
#include "Thyra_TestingTools.hpp"
#include "Epetra_CrsMatrix.h"
#include "Teuchos_GlobalMPISession.hpp"
#include "Teuchos_VerboseObject.hpp"
#include "Teuchos_XMLParameterListHelpers.hpp"
#include "Teuchos_CommandLineProcessor.hpp"
#include "Teuchos_StandardCatchMacros.hpp"
#include "Teuchos_TimeMonitor.hpp"
#ifdef HAVE_MPI
# include "Epetra_MpiComm.h"
#else
# include "Epetra_SerialComm.h"
#endif
namespace {
readEpetraCrsMatrixFromMatrixMarket(
)
{
std::runtime_error,
"Error, could not read matrix file \""<<fileName<<"\"!"
);
}
readEpetraCrsMatrixFromMatrixMarketAsLinearOp(
const std::string &label
)
{
return Thyra::epetraLinearOp(
readEpetraCrsMatrixFromMatrixMarket(fileName,comm),label
);
}
}
int main(int argc, char* argv[])
{
using Teuchos::describe;
using Teuchos::rcp_dynamic_cast;
using Teuchos::rcp_const_cast;
using Teuchos::sublist;
typedef ParameterList::PrintOptions PLPrintOptions;
using Thyra::initializePreconditionedOp;
using Thyra::initializeOp;
using Thyra::unspecifiedPrec;
using Thyra::solve;
typedef RCP<const Thyra::LinearOpBase<double> > LinearOpPtr;
typedef RCP<Thyra::VectorBase<double> > VectorPtr;
bool success = true;
bool verbose = true;
try {
const int numVerbLevels = 6;
verbLevelValues[] =
{
};
const char*
verbLevelNames[] =
{ "default", "none", "low", "medium", "high", "extreme" };
std::string paramsFile = "";
std::string extraParamsFile = "";
std::string baseDir = ".";
bool useP1Prec = true;
bool invertP1 = false;
bool showParams = false;
double solveTol = 1e-8;
CommandLineProcessor clp(false);
clp.setOption( "verb-level", &verbLevel,
numVerbLevels, verbLevelValues, verbLevelNames,
"Verbosity level used for all objects."
);
clp.setOption( "params-file", ¶msFile,
"File containing parameters in XML format.",
true
);
clp.setOption( "extra-params-file", &extraParamsFile,
"File containing extra parameters in XML format."
);
clp.setOption( "base-dir", &baseDir,
"Base directory for all of the files."
);
clp.setOption( "use-P1-prec", "use-algebraic-prec", &useP1Prec,
"Use the physics-based preconditioner for P2 based on P1 or just an algebraic preconditioner"
);
clp.setOption( "invert-P1", "prec-P1-only", &invertP1,
"In the physics-based preconditioner, use P1's preconditioner or fully invert P1."
);
clp.setOption( "solve-tol", &solveTol,
"Tolerance for the solution to determine success or failure!"
);
clp.setOption( "show-params", "no-show-params", &showParams,
"Show the parameter list or not."
);
clp.setDocString(
"This example program shows an attempt to create a physics-based\n"
"preconditioner using the building blocks of Stratimikos and Thyra\n"
"implicit linear operators. The idea is to use the linear operator\n"
"for first-order Lagrange finite elements as the preconditioner for the\n"
"operator using second-order Lagrange finite elements. The details of the\n"
"PDE being represented, the mesh being used, or the boundary conditions are\n"
"beyond the scope of this example.\n"
"\n"
"The matrices used in this example are:\n"
"\n"
" P2: The discretized PDE matrix using second-order Lagrange finite elements.\n"
" P1: The discretized PDE matrix using first-order Lagrange finite elements.\n"
" M22: The mass matrix for the second-order Lagrange finite-element basis functions.\n"
" M11: The mass matrix for the first-order Lagrange finite-element basis functions.\n"
" M21: A rectangular matrix that uses mixed first- and second-order basis functions\n"
" to map from P2 space to P1 space.\n"
" M12: A rectangular matrix that uses mixed first- and second-order basis functions\n"
" to map from P1 space to P2 space.\n"
"\n"
"The above matrices are read from Matrix Market *.mtx files in the directory given by\n"
"the --base-dir command-line option.\n"
"\n"
"The preconditioner operator created in this example program is:\n"
"\n"
" precP2Op = (inv(M22) * M12) * prec(P1) * (inv(M11) * M21)\n"
"\n"
"where prec(P1) is either the algebraic preconditioner for P1 (--prec-P1-only)\n"
"or is a full solve for P1 (--invert-P1).\n"
"\n"
"We use Stratimikos to specify the linear solvers and/or algebraic\n"
"preconditioners and we use the Thyra implicit operators to build the\n"
"implicitly multiplied linear operators associated with the preconditioner.\n"
"\n"
"Warning! This physics-based preconditioner is singular and can not\n"
"be used to solve the linear system given a random right-hand side. However,\n"
"singular or not, this example shows how one can use Thyra/Stratimikos to quickly\n"
"try out these types of preconditioning ideas (even if they do not work).\n"
);
CommandLineProcessor::EParseCommandLineReturn parse_return = clp.parse(argc,argv);
if( parse_return != CommandLineProcessor::PARSE_SUCCESSFUL ) return parse_return;
*out << "\nA) Reading in Epetra_CrsMatrix objects for P1, P2, M11, M12, M21, and M22 ...\n";
#ifdef HAVE_MPI
#else
#endif
LinearOpPtr P1=readEpetraCrsMatrixFromMatrixMarketAsLinearOp(
baseDir+"/P1.mtx",comm,"P1");
*out << "\nP1 = " << describe(*P1,verbLevel) << "\n";
LinearOpPtr P2= readEpetraCrsMatrixFromMatrixMarketAsLinearOp(
baseDir+"/P2.mtx",comm,"P2");
*out << "\nP2 = " << describe(*P2,verbLevel) << "\n";
LinearOpPtr M11=readEpetraCrsMatrixFromMatrixMarketAsLinearOp(
baseDir+"/M11.mtx",comm,"M11");
*out << "\nM11 = " << describe(*M11,verbLevel) << "\n";
LinearOpPtr M22=readEpetraCrsMatrixFromMatrixMarketAsLinearOp(
baseDir+"/M22.mtx",comm,"M22");
*out << "\nM22 = " << describe(*M22,verbLevel) << "\n";
LinearOpPtr M12=readEpetraCrsMatrixFromMatrixMarketAsLinearOp(
baseDir+"/M12.mtx",comm,"M12");
*out << "\nM12 = " << describe(*M12,verbLevel) << "\n";
LinearOpPtr M21=readEpetraCrsMatrixFromMatrixMarketAsLinearOp(
baseDir+"/M21.mtx",comm,"M21");
*out << "\nM21 = " << describe(*M21,verbLevel) << "\n";
*out << "\nB) Get the preconditioner and/or linear solver strategies to invert M11, M22, P1, and P2 ...\n";
RCP<ParameterList> paramList =
Teuchos::getParametersFromXmlFile( baseDir+"/"+paramsFile );
if (extraParamsFile.length()) {
Teuchos::updateParametersFromXmlFile( baseDir+"/"+extraParamsFile, paramList.ptr() );
}
if (showParams) {
*out << "\nRead in parameter list:\n\n";
paramList->print(*out,PLPrintOptions().indent(2).showTypes(true));
}
sublist(paramList,"M11 Solver",true) );
sublist(paramList,"M22 Solver",true) );
sublist(paramList,"P1 Solver",true) );
sublist(paramList,"P2 Solver",true) );
RCP<const Thyra::LinearOpWithSolveFactoryBase<double> > M11_linsolve_strategy
= createLinearSolveStrategy(M11_linsolve_strategy_builder);
RCP<Thyra::LinearOpWithSolveFactoryBase<double> > M22_linsolve_strategy
= createLinearSolveStrategy(M22_linsolve_strategy_builder);
RCP<const Thyra::LinearOpWithSolveFactoryBase<double> > P1_linsolve_strategy;
RCP<const Thyra::PreconditionerFactoryBase<double> > P1_prec_strategy;
if(invertP1)
P1_linsolve_strategy
= createLinearSolveStrategy(P1_linsolve_strategy_builder);
else
P1_prec_strategy
= createPreconditioningStrategy(P1_linsolve_strategy_builder);
RCP<const Thyra::LinearOpWithSolveFactoryBase<double> > P2_linsolve_strategy
= createLinearSolveStrategy(P2_linsolve_strategy_builder);
*out << "\nC) Create the physics-based preconditioner! ...\n";
*out << "\nCreating inv(M11) ...\n";
LinearOpPtr invM11 =
inverse(*M11_linsolve_strategy, M11);
*out << "\ninvM11 = " << describe(*invM11,verbLevel) << "\n";
*out << "\nCreating inv(M22) ...\n";
LinearOpPtr invM22 =
inverse(*M22_linsolve_strategy, M22);
*out << "\ninvM22 = " << describe(*invM22,verbLevel) << "\n";
*out << "\nCreating prec(P1) ...\n";
LinearOpPtr invP1;
if(invertP1) {
*out << "\nCreating prec(P1) as a full solver ...\n";
invP1 =
inverse(*P1_linsolve_strategy, P1);
}
else {
*out << "\nCreating prec(P1) as just an algebraic preconditioner ...\n";
RCP<Thyra::PreconditionerBase<double> >
precP1 = prec(*P1_prec_strategy,P1);
*out << "\nprecP1 = " << describe(*precP1,verbLevel) << "\n";
invP1 = precP1->getUnspecifiedPrecOp();
}
invP1)->setObjectLabel("invP1");
*out << "\ninvP1 = " << describe(*invP1,verbLevel) << "\n";
LinearOpPtr P2ToP1 = multiply( invM11, M21 );
*out << "\nP2ToP1 = " << describe(*P2ToP1,verbLevel) << "\n";
LinearOpPtr P1ToP2 = multiply( invM22, M12 );
*out << "\nP1ToP2 = " << describe(*P1ToP2,verbLevel) << "\n";
LinearOpPtr precP2Op = multiply( P1ToP2, invP1, P2ToP1 );
*out << "\nprecP2Op = " << describe(*precP2Op,verbLevel) << "\n";
*out << "\nD) Setup the solver for P2 ...\n";
RCP<Thyra::LinearOpWithSolveBase<double> >
P2_lows = P2_linsolve_strategy->createOp();
if(useP1Prec) {
*out << "\nCreating the solver P2 using the specialized precP2Op\n";
initializePreconditionedOp<double>( *P2_linsolve_strategy, P2,
unspecifiedPrec(precP2Op), P2_lows.ptr());
}
else {
*out << "\nCreating the solver P2 using algebraic preconditioner\n";
initializeOp(*P2_linsolve_strategy, P2, P2_lows.ptr());
}
*out << "\nP2_lows = " << describe(*P2_lows, verbLevel) << "\n";
*out << "\nE) Solve P2 for a random RHS ...\n";
VectorPtr x = createMember(P2->domain());
VectorPtr b = createMember(P2->range());
Thyra::randomize(-1.0, +1.0, b.ptr());
Thyra::assign(x.ptr(), 0.0);
*out << "\nSolve status:\n" << solveStatus;
*out << "\nSolution ||x|| = " << Thyra::norm(*x) << "\n";
if(showParams) {
*out << "\nParameter list after use:\n\n";
paramList->print(*out, PLPrintOptions().indent(2).showTypes(true));
}
*out << "\nF) Checking the error in the solution of r=b-P2*x ...\n";
VectorPtr P2x = Thyra::createMember(b->space());
Thyra::apply( *P2, Thyra::NOTRANS, *x, P2x.ptr() );
VectorPtr r = Thyra::createMember(b->space());
Thyra::V_VmV<double>(r.ptr(), *b, *P2x);
double
P2x_nrm = Thyra::norm(*P2x),
r_nrm = Thyra::norm(*r),
b_nrm = Thyra::norm(*b),
r_nrm_over_b_nrm = r_nrm / b_nrm;
bool result = r_nrm_over_b_nrm <= solveTol;
if(!result) success = false;
*out
<< "\n||P2*x|| = " << P2x_nrm << "\n";
*out
<< "\n||P2*x-b||/||b|| = " << r_nrm << "/" << b_nrm
<< " = " << r_nrm_over_b_nrm << " <= " << solveTol
}
if (verbose) {
if(success) *out << "\nCongratulations! All of the tests checked out!\n";
else *out << "\nOh no! At least one of the tests failed!\n";
}
return ( success ? EXIT_SUCCESS : EXIT_FAILURE );
}