Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class TekoSmoother<double, int, GlobalOrdinal, Node> : public SmootherPrototype<
RCP<const ParameterList> GetValidParameterList() const {
RCP<ParameterList> validParamList = rcp(new ParameterList());

validParamList->set<RCP<const FactoryBase> >("A", null, "Generating factory of the matrix A");
validParamList->set<RCP<const FactoryBase>>("A", null, "Generating factory of the matrix A");
validParamList->set<std::string>("Inverse Type", "", "Name of parameter list within 'Teko parameters' containing the Teko smoother parameters.");

return validParamList;
Expand All @@ -200,7 +200,7 @@ class TekoSmoother<double, int, GlobalOrdinal, Node> : public SmootherPrototype<
this->GetOStream(Warnings0) << "MueLu::TekoSmoother::Setup(): Setup() has already been called";

// extract blocked operator A from current level
A_ = Factory::Get<RCP<Matrix> >(currentLevel, "A"); // A needed for extracting map extractors
A_ = Factory::Get<RCP<Matrix>>(currentLevel, "A"); // A needed for extracting map extractors
bA_ = Teuchos::rcp_dynamic_cast<BlockedCrsMatrix>(A_);
TEUCHOS_TEST_FOR_EXCEPTION(bA_.is_null(), Exceptions::BadCast,
"MueLu::TekoSmoother::Build: input matrix A is not of type BlockedCrsMatrix.");
Expand All @@ -209,7 +209,7 @@ class TekoSmoother<double, int, GlobalOrdinal, Node> : public SmootherPrototype<
TEUCHOS_TEST_FOR_EXCEPTION(bThyOp_.is_null(), Exceptions::BadCast,
"MueLu::TekoSmoother::Build: Could not extract thyra operator from BlockedCrsMatrix.");

Teuchos::RCP<const Thyra::LinearOpBase<Scalar> > thyOp = Teuchos::rcp_dynamic_cast<const Thyra::LinearOpBase<Scalar> >(bThyOp_);
Teuchos::RCP<const Thyra::LinearOpBase<Scalar>> thyOp = Teuchos::rcp_dynamic_cast<const Thyra::LinearOpBase<Scalar>>(bThyOp_);
TEUCHOS_TEST_FOR_EXCEPTION(thyOp.is_null(), Exceptions::BadCast,
"MueLu::TekoSmoother::Build: Downcast of Thyra::BlockedLinearOpBase to Teko::LinearOp failed.");

Expand Down Expand Up @@ -237,51 +237,64 @@ class TekoSmoother<double, int, GlobalOrdinal, Node> : public SmootherPrototype<

@param X initial guess
@param B right-hand side
@param InitialGuessIsZero This option has no effect.
@param InitialGuessIsZero whether the initial guess is zero
*/
void Apply(MultiVector &X, const MultiVector &B, bool /* InitialGuessIsZero */ = false) const {
TEUCHOS_TEST_FOR_EXCEPTION(this->IsSetup() == false, Exceptions::RuntimeError,
void Apply(MultiVector &X, const MultiVector &B, bool InitialGuessIsZero = false) const {
TEUCHOS_TEST_FOR_EXCEPTION(!this->IsSetup(), Exceptions::RuntimeError,
"MueLu::TekoSmoother::Apply(): Setup() has not been called");

Teuchos::RCP<const Teuchos::Comm<int> > comm = X.getMap()->getComm();

Teuchos::RCP<const MapExtractor> rgMapExtractor = bA_->getRangeMapExtractor();
auto comm = X.getMap()->getComm();
auto rgMapExtractor = bA_->getRangeMapExtractor();
TEUCHOS_TEST_FOR_EXCEPT(Teuchos::is_null(rgMapExtractor));

// copy initial solution vector X to Ptr<Thyra::MultiVectorBase> YY

// create a Thyra RHS vector
Teuchos::RCP<Thyra::MultiVectorBase<Scalar> > thyB = Thyra::createMembers(Teuchos::rcp_dynamic_cast<const Thyra::VectorSpaceBase<Scalar> >(bThyOp_->productRange()), Teuchos::as<int>(B.getNumVectors()));
Teuchos::RCP<Thyra::ProductMultiVectorBase<Scalar> > thyProdB =
Teuchos::rcp_dynamic_cast<Thyra::ProductMultiVectorBase<Scalar> >(thyB);
TEUCHOS_TEST_FOR_EXCEPTION(thyProdB.is_null(), Exceptions::BadCast,
"MueLu::TekoSmoother::Apply: Failed to cast range space to product range space.");

// copy RHS vector B to Thyra::MultiVectorBase thyProdB
Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::updateThyra(Teuchos::rcpFromRef(B), rgMapExtractor, thyProdB);

// create a Thyra SOL vector
Teuchos::RCP<Thyra::MultiVectorBase<Scalar> > thyX = Thyra::createMembers(Teuchos::rcp_dynamic_cast<const Thyra::VectorSpaceBase<Scalar> >(bThyOp_->productDomain()), Teuchos::as<int>(X.getNumVectors()));
Teuchos::RCP<Thyra::ProductMultiVectorBase<Scalar> > thyProdX =
Teuchos::rcp_dynamic_cast<Thyra::ProductMultiVectorBase<Scalar> >(thyX);
TEUCHOS_TEST_FOR_EXCEPTION(thyProdX.is_null(), Exceptions::BadCast,
"MueLu::TekoSmoother::Apply: Failed to cast domain space to product domain space.");

// copy RHS vector X to Thyra::MultiVectorBase thyProdX
Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::updateThyra(Teuchos::rcpFromRef(X), rgMapExtractor, thyProdX);

inverseOp_->apply(
Thyra::NOTRANS,
*thyB, // const MultiVectorBase<Scalar> &X,
thyX.ptr(), // const Ptr<MultiVectorBase<Scalar> > &Y,
1.0,
0.0);

// copy back content of Ptr<Thyra::MultiVectorBase> thyX into X
Teuchos::RCP<Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node> > XX =
Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toXpetra(thyX, comm);

X.update(Teuchos::ScalarTraits<Scalar>::one(), *XX, Teuchos::ScalarTraits<Scalar>::zero());
using STS = Teuchos::ScalarTraits<Scalar>;

auto createProductMultiVector = [](const Teuchos::RCP<const Thyra::VectorSpaceBase<Scalar>> &space,
int numVecs,
const char *castErrorMsg) {
auto mv = Thyra::createMembers(space, numVecs);
auto prodMv = Teuchos::rcp_dynamic_cast<Thyra::ProductMultiVectorBase<Scalar>>(mv);
TEUCHOS_TEST_FOR_EXCEPTION(prodMv.is_null(), Exceptions::BadCast, castErrorMsg);
return std::make_pair(mv, prodMv);
};

auto solveWithThyra = [&](const Teuchos::RCP<const Xpetra::MultiVector<Scalar, LocalOrdinal, GlobalOrdinal, Node>> &rhs,
bool initializeFromX,
const typename STS::magnitudeType alphaX) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The parameter alphaX should be of type Scalar to be consistent with the Xpetra::MultiVector::update signature and the values passed to the lambda (STS::zero() and STS::one()).

Suggested change
const typename STS::magnitudeType alphaX) {
const Scalar alphaX) {

auto [thyB, thyProdB] = createProductMultiVector(
Teuchos::rcp_dynamic_cast<const Thyra::VectorSpaceBase<Scalar>>(bThyOp_->productRange()),
Teuchos::as<int>(rhs->getNumVectors()),
"MueLu::TekoSmoother::Apply: Failed to cast range space to product range space.");

Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::updateThyra(rhs, rgMapExtractor, thyProdB);

auto [thyX, thyProdX] = createProductMultiVector(
Teuchos::rcp_dynamic_cast<const Thyra::VectorSpaceBase<Scalar>>(bThyOp_->productDomain()),
Teuchos::as<int>(X.getNumVectors()),
"MueLu::TekoSmoother::Apply: Failed to cast domain space to product domain space.");

if (initializeFromX) {
Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::updateThyra(Teuchos::rcpFromRef(X), rgMapExtractor, thyProdX);
}

inverseOp_->apply(
Thyra::NOTRANS,
*thyB,
thyX.ptr(),
STS::one(),
STS::zero());

auto XX = Xpetra::ThyraUtils<Scalar, LocalOrdinal, GlobalOrdinal, Node>::toXpetra(thyX, comm);
X.update(STS::one(), *XX, alphaX);
};

if (InitialGuessIsZero) {
solveWithThyra(Teuchos::rcpFromRef(B), /*initializeFromX=*/true, STS::zero());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When InitialGuessIsZero is true, initializing the Thyra vector from X is unnecessary. Since inverseOp_->apply is called with beta=0.0 (line 285), the initial content of the output vector is ignored. Setting initializeFromX to false avoids a redundant copy of X (which might contain garbage or NaNs).

Suggested change
solveWithThyra(Teuchos::rcpFromRef(B), /*initializeFromX=*/true, STS::zero());
solveWithThyra(Teuchos::rcpFromRef(B), /*initializeFromX=*/false, STS::zero());

return;
}

auto residual = Utilities::Residual(*A_, X, B);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The Utilities class is likely not defined in this scope without a full namespace qualification or a local alias. Given that other utility classes in this file (like Xpetra::ThyraUtils) are fully qualified, you should use the fully qualified name for MueLu::Utilities to ensure successful compilation.

Suggested change
auto residual = Utilities::Residual(*A_, X, B);
auto residual = MueLu::Utilities<Scalar, LocalOrdinal, GlobalOrdinal, Node>::Residual(*A_, X, B);

solveWithThyra(residual, /*initializeFromX=*/false, STS::one());
}
//@}

Expand Down Expand Up @@ -325,7 +338,7 @@ class TekoSmoother<double, int, GlobalOrdinal, Node> : public SmootherPrototype<
//! block operator
RCP<Matrix> A_; // < ! internal blocked operator "A" generated by AFact_
RCP<BlockedCrsMatrix> bA_;
RCP<const Thyra::BlockedLinearOpBase<Scalar> > bThyOp_;
RCP<const Thyra::BlockedLinearOpBase<Scalar>> bThyOp_;

//! Teko parameters
RCP<ParameterList> tekoParams_; // < ! parameter list containing Teko parameters. These parameters are not administrated by the factory and not validated.
Expand Down
177 changes: 176 additions & 1 deletion packages/muelu/test/unit_tests/Smoothers/BlockedSmoother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
#include <Xpetra_ReorderedBlockedCrsMatrix.hpp>
#include <Xpetra_ReorderedBlockedMultiVector.hpp>

#ifdef HAVE_MUELU_TEKO
#include <MueLu_TekoSmoother.hpp>
#endif

namespace MueLuTests {

// this namespace already has: #include "MueLu_UseShortNames.hpp"
Expand Down Expand Up @@ -6774,6 +6778,176 @@ TEUCHOS_UNIT_TEST_TEMPLATE_4_DECL(BlockedSmoother, SplitReorder, Scalar, LocalOr
} // end UseTpetra
}

#ifdef HAVE_MUELU_TEKO
TEUCHOS_UNIT_TEST_TEMPLATE_4_DECL(BlockedSmoother, Teko_Setup_Apply, Scalar, LocalOrdinal, GlobalOrdinal, Node) {
#include <MueLu_UseShortNames.hpp>
MUELU_TESTING_SET_OSTREAM;
MUELU_TESTING_LIMIT_SCOPE(Scalar, GlobalOrdinal, Node);

using Teuchos::ParameterList;

MUELU_TEST_ONLY_FOR(Xpetra::UseTpetra) {
if (!std::is_same<Scalar, Teko::ST>::value || !std::is_same<LocalOrdinal, Teko::LO>::value) {
out << "Skipping Teko test: TekoSmoother is only available for Scalar=Teko::ST, LocalOrdinal=Teko::LO." << std::endl;
return;
}

if (!std::is_same<Node, Teko::NT>::value) {
out << "Skipping Teko test: TekoSmoother is only available for Node=Teko::NT." << std::endl;
return;
}

RCP<const Teuchos::Comm<int> > comm = Parameters::getDefaultComm();

// Teko wants a blocked Thyra-capable operator
int noBlocks = 2;
Teuchos::RCP<const BlockedCrsMatrix> bop = CreateBlockDiagonalExampleMatrixThyra<Scalar, LocalOrdinal, GlobalOrdinal, Node, TpetraMap>(noBlocks, *comm);
Teuchos::RCP<const Matrix> Aconst = Teuchos::rcp_dynamic_cast<const Matrix>(bop);
Teuchos::RCP<Matrix> A = Teuchos::rcp_const_cast<Matrix>(Aconst);

Level level;
TestHelpers::TestFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::createSingleLevelHierarchy(level);
level.Set("A", A);

//////////////////////////////////////////////////////////////////////
// Teko smoother
RCP<TekoSmoother> smootherPrototype =
rcp(new TekoSmoother());

smootherPrototype->SetParameter("Inverse Type", Teuchos::ParameterEntry(std::string("TekoDiag")));

RCP<ParameterList> tekoParams = rcp(new ParameterList());
{
ParameterList& tekoDiag = tekoParams->sublist("TekoDiag");
tekoDiag.set("Type", "Block Jacobi");
}

smootherPrototype->SetTekoParameters(tekoParams);

RCP<SmootherFactory> smootherFact = rcp(new SmootherFactory(smootherPrototype));

FactoryManager M;
M.SetFactory("Smoother", smootherFact);

MueLu::SetFactoryManager SFM(Teuchos::rcpFromRef(level), Teuchos::rcpFromRef(M));

level.Request("Smoother", smootherFact.get());
level.Request("PreSmoother", smootherFact.get());
level.Request("PostSmoother", smootherFact.get());

smootherFact->Build(level);

level.print(std::cout, Teuchos::VERB_EXTREME);

RCP<SmootherBase> tekoSmoother = level.Get<RCP<SmootherBase> >("PreSmoother", smootherFact.get());

RCP<MultiVector> X = MultiVectorFactory::Build(A->getDomainMap(), 1);
RCP<MultiVector> RHS = MultiVectorFactory::Build(A->getRangeMap(), 1);

// Random exact solution
X->setSeed(846930886);
X->randomize();

typedef typename Teuchos::ScalarTraits<Scalar>::magnitudeType magnitude_type;

// Normalize X
Array<magnitude_type> norms(1);
X->norm2(norms);
X->scale(1 / norms[0]);

// Build RHS = A * X_exact
A->apply(*X, *RHS, Teuchos::NO_TRANS, (SC)1.0, (SC)0.0);

// Reset X to 0
X->putScalar((SC)0.0);

RHS->norm2(norms);
out << "||RHS|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(10) << norms[0] << std::endl;

magnitude_type tol = 100. * Teuchos::ScalarTraits<Scalar>::eps();

//
// Case 1: zero initial guess
//
out << "solve with zero initial guess" << std::endl;
Teuchos::Array<magnitude_type> initialNorms(1);
X->norm2(initialNorms);
out << " ||X_initial|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(10) << initialNorms[0] << std::endl;

tekoSmoother->Apply(*X, *RHS, true);

Teuchos::Array<magnitude_type> finalNorms(1);
X->norm2(finalNorms);
Teuchos::Array<magnitude_type> residualNorm1 = Utilities::ResidualNorm(*A, *X, *RHS);
out << " ||Residual_final|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(20) << residualNorm1[0] << std::endl;
out << " ||X_final|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(10) << finalNorms[0] << std::endl;

TEUCHOS_TEST_COMPARE(residualNorm1[0], <, tol, out, success);
TEUCHOS_TEST_COMPARE(finalNorms[0] - Teuchos::ScalarTraits<Scalar>::magnitude(Teuchos::ScalarTraits<Scalar>::one()), <, tol, out, success);

//
// Case 2: InitialGuessIsZero=true with nonzero/garbage X
//
out << "solve with zero initial guess, and unreliable nonzeroed vector X" << std::endl;
X->randomize();
X->norm2(initialNorms);
out << " ||X_initial|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(10) << initialNorms[0] << std::endl;

tekoSmoother->Apply(*X, *RHS, true);

X->norm2(finalNorms);
Teuchos::Array<magnitude_type> residualNorm2 = Utilities::ResidualNorm(*A, *X, *RHS);
out << " ||Residual_final|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(20) << residualNorm2[0] << std::endl;
out << " ||X_final|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(10) << finalNorms[0] << std::endl;

TEUCHOS_TEST_COMPARE(residualNorm2[0], <, tol, out, success);
TEUCHOS_TEST_COMPARE(finalNorms[0] - Teuchos::ScalarTraits<Scalar>::magnitude(Teuchos::ScalarTraits<Scalar>::one()), <, tol, out, success);

//
// Case 3: random initial guess, InitialGuessIsZero=false
//
out << "solve with random initial guess" << std::endl;
X->randomize();
X->norm2(initialNorms);
out << " ||X_initial|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(10) << initialNorms[0] << std::endl;

tekoSmoother->Apply(*X, *RHS, false);

X->norm2(finalNorms);
Teuchos::Array<magnitude_type> residualNorm3 = Utilities::ResidualNorm(*A, *X, *RHS);
out << " ||Residual_final|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(20) << residualNorm3[0] << std::endl;
out << " ||X_final|| = " << std::setiosflags(std::ios::fixed)
<< std::setprecision(10) << finalNorms[0] << std::endl;

TEUCHOS_TEST_COMPARE(residualNorm3[0], <, tol, out, success);
TEUCHOS_TEST_COMPARE(finalNorms[0] - Teuchos::ScalarTraits<Scalar>::magnitude(Teuchos::ScalarTraits<Scalar>::one()), <, tol, out, success);

if (comm->getSize() == 1) {
TEST_EQUALITY(residualNorm1[0] == residualNorm2[0], true);
TEST_EQUALITY(residualNorm1[0] != residualNorm3[0], true);
} else {
out << "Pass/Fail is only checked in serial." << std::endl;
}
} // end UseTpetra
}
#endif // HAVE_MUELU_TEKO

#ifdef HAVE_MUELU_TEKO
#define MUELU_ETI_GROUP_TEKO(SC, LO, GO, NO) \
TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(BlockedSmoother, Teko_Setup_Apply, SC, LO, GO, NO)
#else
#define MUELU_ETI_GROUP_TEKO(SC, LO, GO, NO)
#endif

#define MUELU_ETI_GROUP(SC, LO, GO, NO) \
TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(BlockedSmoother, Jacobi_Setup_Apply, SC, LO, GO, NO) \
TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(BlockedSmoother, BGS_Setup_Apply, SC, LO, GO, NO) \
Expand Down Expand Up @@ -6812,7 +6986,8 @@ TEUCHOS_UNIT_TEST_TEMPLATE_4_DECL(BlockedSmoother, SplitReorder, Scalar, LocalOr
TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(BlockedSmoother, NestedI2I01II_Thyra_Indef_Setup_Apply, SC, LO, GO, NO) \
TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(BlockedSmoother, NestedII01I2I_Thyra_Indef_Setup_Apply2, SC, LO, GO, NO) \
TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(BlockedSmoother, NestedI0I21II_Thyra_Indef_Setup_Apply3, SC, LO, GO, NO) \
TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(BlockedSmoother, SplitReorder, SC, LO, GO, NO)
TEUCHOS_UNIT_TEST_TEMPLATE_4_INSTANT(BlockedSmoother, SplitReorder, SC, LO, GO, NO) \
MUELU_ETI_GROUP_TEKO(SC, LO, GO, NO)

#include <MueLu_ETI_4arg.hpp>

Expand Down
Loading