Program Listing for File augmented_lagrangian_preconditioner.h¶
↰ Return to documentation for file (include/augmented_lagrangian_preconditioner.h)
#include <deal.II/base/config.h>
#include <deal.II/base/exceptions.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/lac/block_vector.h>
#include <deal.II/lac/linear_operator.h>
#include <deal.II/lac/linear_operator_tools.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/vector.h>
#ifdef DEAL_II_WITH_TRILINOS
# include <deal.II/lac/trilinos_precondition.h>
# include <deal.II/lac/trilinos_sparse_matrix.h>
# include <EpetraExt_MatrixMatrix.h>
# include <Epetra_Comm.h>
# include <Epetra_CrsMatrix.h>
# include <Epetra_Map.h>
# include <Epetra_RowMatrixTransposer.h>
#endif
#ifndef augmented_lagrangian_prec_h
# define augmented_lagrangian_prec_h
using namespace dealii;
namespace UtilitiesAL
{
template <typename VectorType,
typename BlockVectorType = TrilinosWrappers::MPI::BlockVector>
class BlockPreconditionerAugmentedLagrangian
{
public:
BlockPreconditionerAugmentedLagrangian(
const LinearOperator<VectorType> Aug_inv_,
const LinearOperator<VectorType> C_,
const LinearOperator<VectorType> Ct_,
const LinearOperator<VectorType> invW_,
const double gamma_ = 1e1)
{
static_assert(
std::is_same<VectorType, TrilinosWrappers::MPI::Vector>::value == true);
Aug_inv = Aug_inv_;
C = C_;
Ct = Ct_;
invW = invW_;
gamma = gamma_;
}
void
vmult(BlockVectorType &v, const BlockVectorType &u) const
{
v.block(0) = 0.;
v.block(1) = 0.;
v.block(1) = -gamma * invW * u.block(1);
v.block(0) = Aug_inv * (u.block(0) - Ct * v.block(1));
}
private:
LinearOperator<VectorType> K;
LinearOperator<VectorType> Aug_inv;
LinearOperator<VectorType> C;
LinearOperator<VectorType> invW;
LinearOperator<VectorType> Ct;
double gamma;
};
template <typename MatrixType = SparseMatrix<double>,
typename VectorType = Vector<typename MatrixType::value_type>,
typename PreconditionerType = TrilinosWrappers::PreconditionAMG>
void
create_augmented_block(const MatrixType &A,
const MatrixType &Ct,
const VectorType &scaling_vector,
const double gamma,
MatrixType &augmented_matrix)
{
# ifdef DEAL_II_WITH_TRILINOS
if constexpr (std::is_same_v<TrilinosWrappers::SparseMatrix, MatrixType>)
{
Assert((std::is_same_v<TrilinosWrappers::MPI::Vector, VectorType>),
ExcMessage("You must use Trilinos vectors, as you are using "
"Trilinos matrices."));
Epetra_CrsMatrix A_trilinos = A.trilinos_matrix();
Epetra_CrsMatrix Ct_trilinos = Ct.trilinos_matrix();
auto multi_vector = scaling_vector.trilinos_vector();
Assert((A_trilinos.NumGlobalRows() !=
Ct_trilinos.DomainMap().NumGlobalElements()),
ExcMessage("Number of columns in C must match dimension of A"));
// Ensure the MultiVector has only one column.
Assert((multi_vector.NumVectors() == 1),
ExcMessage("The MultiVector must have exactly one column."));
// Create diagonal matrix from first vector of v
// Explicitly cast the map to Epetra_Map
const Epetra_Map &map =
static_cast<const Epetra_Map &>(multi_vector.Map());
// Create a diagonal matrix with 1 nonzero entry per row
Epetra_CrsMatrix diag_matrix(Copy, map, 1);
for (int i = 0; i < multi_vector.Map().NumMyElements(); ++i)
{
int global_row = multi_vector.Map().GID(i);
double val = multi_vector[0][i]; // Access first vector
diag_matrix.InsertGlobalValues(global_row, 1, &val, &global_row);
}
diag_matrix.FillComplete();
Epetra_CrsMatrix *W =
new Epetra_CrsMatrix(Copy, Ct_trilinos.RowMap(), 0);
EpetraExt::MatrixMatrix::Multiply(
Ct_trilinos, false, diag_matrix, false, *W);
// Compute Ct^T * W, which is equivalent to (C^T * diag(V)) * C
Epetra_CrsMatrix *CtT_W = new Epetra_CrsMatrix(Copy, W->RangeMap(), 0);
EpetraExt::MatrixMatrix::Multiply(
*W, false /* transpose */, Ct_trilinos, true, *CtT_W);
// Add A to the result, scaling with gamma
Epetra_CrsMatrix *result =
new Epetra_CrsMatrix(Copy,
A_trilinos.RowMap(),
A_trilinos.MaxNumEntries());
EpetraExt::MatrixMatrix::Add(
A_trilinos, false, 1.0, *CtT_W, false, gamma, result);
result->FillComplete();
// Initialize the final Trilinos matrix
augmented_matrix.reinit(*result, true /*copy_values*/);
// Delete unnecessary objects.
delete W;
delete CtT_W;
delete result;
}
else
{
// PETSc not supported so far.
AssertThrow(false, ExcNotImplemented("Matrix type not supported!"));
}
# else
AssertThrow(
false,
ExcMessage(
"This function requires deal.II to be configured with Trilinos."));
(void)velocity_dh;
(void)C;
(void)Ct;
(void)scaling_vector;
(void)velocity_constraints;
(void)gamma;
(void)augmented_matrix;
# endif
}
template <int spacedim, typename VectorType>
void
set_null_space(Teuchos::ParameterList ¶meter_list,
std::unique_ptr<Epetra_MultiVector> &ptr_distributed_modes,
const Epetra_RowMatrix &matrix,
const std::vector<VectorType> &modes)
{
static_assert((spacedim == 2 || spacedim == 3),
"This function only works for 2D and 3D.");
# ifdef DEAL_II_WITH_TRILINOS
using size_type = TrilinosWrappers::PreconditionAMG::size_type;
const Epetra_Map &domain_map = matrix.OperatorDomainMap();
constexpr size_type modes_dimension = spacedim == 3 ? 6 : 3;
ptr_distributed_modes.reset(
new Epetra_MultiVector(domain_map, modes_dimension));
Assert(ptr_distributed_modes, ExcNotInitialized());
Epetra_MultiVector &distributed_modes = *ptr_distributed_modes;
const size_type global_size =
static_cast<size_type>(domain_map.NumGlobalElements());
const size_type my_size =
static_cast<size_type>(domain_map.NumMyElements());
Assert(global_size == static_cast<size_type>(
TrilinosWrappers::global_length(distributed_modes)),
ExcDimensionMismatch(
global_size, TrilinosWrappers::global_length(distributed_modes)));
// Reshape null space as a contiguous vector of doubles so that
// Trilinos can read from it.
for (size_type d = 0; d < modes_dimension; ++d)
{
const size_type mode_size = static_cast<size_type>(modes[d].size());
Assert(mode_size == my_size || mode_size == global_size,
ExcMessage("Rigid body mode vector has the wrong size. "
"Expected either locally-owned size (" +
std::to_string(my_size) + ") or global size (" +
std::to_string(global_size) + "), but got " +
std::to_string(mode_size) + "."));
for (size_type row = 0; row < my_size; ++row)
{
if (mode_size == my_size)
{
distributed_modes[d][row] = static_cast<double>(modes[d][row]);
}
else
{
const TrilinosWrappers::types::int_type gid =
domain_map.GID(static_cast<int>(row));
Assert(gid >= 0 && static_cast<size_type>(gid) < global_size,
ExcMessage("Invalid global index " +
std::to_string(gid) +
" for mode vector of size " +
std::to_string(global_size) + "."));
distributed_modes[d][row] =
static_cast<double>(modes[d][static_cast<size_type>(gid)]);
}
}
}
parameter_list.set("null space: type", "pre-computed");
parameter_list.set("null space: dimension", distributed_modes.NumVectors());
parameter_list.set("null space: vectors", distributed_modes.Values());
# else
AssertThrow(
false,
ExcMessage(
"This function requires deal.II to be configured with Trilinos."));
# endif
}
} // namespace UtilitiesAL
#endif