1 #include "smith/numerics/block_preconditioner.hpp"
9 #include "axom/slic/core/SimpleLogger.hpp"
15 void applyOverrides(
int num_blocks, std::vector<std::unique_ptr<const mfem::Operator>>& block_op_overrides,
16 std::vector<BlockOverride> overrides)
18 for (
auto& ov : overrides) {
19 const int i = ov.first;
22 if (i < 0 || i >= num_blocks) {
23 throw std::out_of_range(
"Override block index out of range");
26 throw std::invalid_argument(
"Override operator must be non-null");
28 if (block_op_overrides[
static_cast<size_t>(i)]) {
29 throw std::invalid_argument(
"Duplicate override for same block index");
32 block_op_overrides[
static_cast<size_t>(i)] = std::move(op);
39 std::vector<std::unique_ptr<mfem::Solver>> solvers,
40 std::vector<BlockOverride> overrides)
41 : block_offsets_(offsets),
42 num_blocks_(offsets.Size() - 1),
43 block_jacobian_(nullptr),
44 solver_diag_(block_offsets_),
45 mfem_solvers_(std::move(solvers)),
46 block_op_overrides_(static_cast<size_t>(num_blocks_))
48 SLIC_ERROR_IF(mfem_solvers_.size() !=
static_cast<size_t>(num_blocks_),
49 "Number of solvers must match number of blocks");
51 applyOverrides(num_blocks_, block_op_overrides_, std::move(overrides));
58 height = jacobian.Height();
59 width = jacobian.Width();
61 block_jacobian_ =
dynamic_cast<const mfem::BlockOperator*
>(&jacobian);
64 for (
int i = 0; i < num_blocks_; i++) {
66 const mfem::Operator* op =
nullptr;
67 const size_t si =
static_cast<size_t>(i);
69 if (block_op_overrides_[si]) {
70 op = block_op_overrides_[si].get();
72 op = &block_jacobian_->GetBlock(i, i);
75 mfem_solvers_[si]->SetOperator(*op);
78 solver_diag_.SetBlock(i, i, mfem_solvers_[si].
get());
82 BlockDiagonalPreconditioner::~BlockDiagonalPreconditioner() {}
85 std::vector<std::unique_ptr<mfem::Solver>> solvers,
87 std::vector<BlockOverride> overrides)
88 : block_offsets_(offsets),
89 num_blocks_(offsets.Size() - 1),
90 block_jacobian_(nullptr),
91 mfem_solvers_(std::move(solvers)),
93 block_op_overrides_(static_cast<size_t>(num_blocks_))
95 SLIC_ERROR_IF(mfem_solvers_.size() !=
static_cast<size_t>(num_blocks_),
96 "Number of solvers must match number of blocks");
98 applyOverrides(num_blocks_, block_op_overrides_, std::move(overrides));
101 void BlockTriangularPreconditioner::LowerSweep(
const mfem::Vector& in, mfem::Vector& out)
const
103 mfem::BlockVector b(
const_cast<mfem::Vector&
>(in), block_offsets_);
104 mfem::BlockVector x(out, block_offsets_);
107 for (
int i = 0; i < num_blocks_; i++) {
108 mfem::Vector& bi = b.GetBlock(i);
109 mfem::Vector& xi = x.GetBlock(i);
112 mfem::Vector rhs_i(bi.Size());
116 for (
int j = 0; j < i; j++) {
117 if (block_jacobian_->IsZeroBlock(i, j)) {
120 const mfem::Operator& A_ij = block_jacobian_->GetBlock(i, j);
122 mfem::Vector tmp(rhs_i.Size());
123 const mfem::Vector& xj = x.GetBlock(j);
126 rhs_i.Add(-1.0, tmp);
130 mfem_solvers_[
static_cast<size_t>(i)]->
Mult(rhs_i, xi);
134 void BlockTriangularPreconditioner::UpperSweep(
const mfem::Vector& in, mfem::Vector& out)
const
136 mfem::BlockVector b(
const_cast<mfem::Vector&
>(in), block_offsets_);
137 mfem::BlockVector x(out, block_offsets_);
140 for (
int i = num_blocks_ - 1; i >= 0; i--) {
141 mfem::Vector& bi = b.GetBlock(i);
142 mfem::Vector& xi = x.GetBlock(i);
145 mfem::Vector rhs_i(bi.Size());
149 for (
int j = i + 1; j < num_blocks_; j++) {
150 if (block_jacobian_->IsZeroBlock(i, j)) {
153 const mfem::Operator& A_ij = block_jacobian_->GetBlock(i, j);
155 mfem::Vector tmp(rhs_i.Size());
156 const mfem::Vector& xj = x.GetBlock(j);
159 rhs_i.Add(-1.0, tmp);
163 mfem_solvers_[
static_cast<size_t>(i)]->
Mult(rhs_i, xi);
183 mfem::Vector tmp(out.Size());
188 mfem::BlockVector tmp_block(tmp, block_offsets_);
190 for (
int i = 0; i < num_blocks_; i++) {
191 mfem::Vector& tmp_i = tmp_block.GetBlock(i);
192 mfem::Vector tmp_i_scaled(tmp_i.Size());
194 const mfem::Operator& A_ii = block_jacobian_->GetBlock(i, i);
195 A_ii.Mult(tmp_i, tmp_i_scaled);
197 tmp_i = tmp_i_scaled;
202 UpperSweep(tmp, out);
210 height = jacobian.Height();
211 width = jacobian.Width();
213 block_jacobian_ =
dynamic_cast<const mfem::BlockOperator*
>(&jacobian);
216 for (
int i = 0; i < num_blocks_; i++) {
218 const mfem::Operator* op =
nullptr;
219 const size_t si =
static_cast<size_t>(i);
221 if (block_op_overrides_[si]) {
222 op = block_op_overrides_[si].get();
224 op = &block_jacobian_->GetBlock(i, i);
227 mfem_solvers_[si]->SetOperator(*op);
231 BlockTriangularPreconditioner::~BlockTriangularPreconditioner() {}
234 std::vector<std::unique_ptr<mfem::Solver>> solvers,
236 std::vector<BlockOverride> overrides)
237 : block_offsets_(offsets),
238 block_jacobian_(nullptr),
239 solver_diag_(block_offsets_),
240 mfem_solvers_(std::move(solvers)),
242 approxType_(approxType),
243 block_op_overrides_(static_cast<size_t>(2))
245 SLIC_ERROR_IF(block_offsets_.Size() - 1 != 2,
"This precondition is specifically for 2X2 block systems");
247 applyOverrides(2, block_op_overrides_, std::move(overrides));
250 throw std::invalid_argument(
251 "SchurApproxType::Custom requires an override operator for block index 1 (custom Schur operator)");
255 void BlockSchurPreconditioner::LowerBlock(
const mfem::Vector& in, mfem::Vector& out)
const
258 mfem::BlockVector b(
const_cast<mfem::Vector&
>(in), block_offsets_);
259 mfem::BlockVector x(out, block_offsets_);
261 mfem::Vector& b1 = b.GetBlock(0);
262 mfem::Vector& b2 = b.GetBlock(1);
263 mfem::Vector& x1 = x.GetBlock(0);
264 mfem::Vector& x2 = x.GetBlock(1);
267 mfem_solvers_[0]->Mult(b1, x1);
278 void BlockSchurPreconditioner::UpperBlock(
const mfem::Vector& in, mfem::Vector& out)
const
281 mfem::BlockVector b(
const_cast<mfem::Vector&
>(in), block_offsets_);
282 mfem::BlockVector x(out, block_offsets_);
284 mfem::Vector& b1 = b.GetBlock(0);
285 mfem::Vector& b2 = b.GetBlock(1);
286 mfem::Vector& x1 = x.GetBlock(0);
287 mfem::Vector& x2 = x.GetBlock(1);
290 mfem::Vector rhs1(b1.Size());
291 A_12_->Mult(b2, rhs1);
294 mfem_solvers_[0]->Mult(rhs1, x1);
309 solver_diag_.Mult(in, out);
315 mfem::Vector tmp(out.Size());
317 solver_diag_.Mult(tmp, out);
323 mfem::Vector tmp(out.Size());
324 solver_diag_.Mult(in, tmp);
325 UpperBlock(tmp, out);
331 mfem::Vector tmp(out.Size());
332 mfem::Vector tmp2(out.Size());
334 solver_diag_.Mult(tmp, tmp2);
335 UpperBlock(tmp2, out);
354 mfem::HypreParMatrix* BlockSchurPreconditioner::BuildSchurDiagApprox_(
const mfem::HypreParMatrix& A11,
355 const mfem::HypreParMatrix& A12,
356 const mfem::HypreParMatrix& A21,
357 const mfem::HypreParMatrix& A22)
const
360 auto* Md =
new mfem::HypreParVector(A11.GetComm(), A11.GetGlobalNumRows(), A11.GetRowStarts());
364 auto* A12_scaled =
new mfem::HypreParMatrix(A12);
365 A12_scaled->InvScaleRows(*Md);
371 mfem::HypreParMatrix* A21DinvA12 = mfem::ParMult(&A21, A12_scaled);
373 A12_scaled =
nullptr;
376 mfem::HypreParMatrix* S = mfem::Add(1.0, A22, -1.0, *A21DinvA12);
378 A21DinvA12 =
nullptr;
386 const mfem::HypreParMatrix* H = &K;
387 hypre_ParCSRMatrix* Hhypre =
static_cast<hypre_ParCSRMatrix*
>(*H);
389 hypre_ParCSRMatrixNormFro(Hhypre, &Hfronorm);
395 S_approx_view_ =
nullptr;
396 height = jacobian.Height();
397 width = jacobian.Width();
398 block_jacobian_ =
dynamic_cast<const mfem::BlockOperator*
>(&jacobian);
399 MFEM_VERIFY(block_jacobian_,
"Jacobian must be a BlockOperator");
401 auto* A11 =
dynamic_cast<const mfem::HypreParMatrix*
>(&block_jacobian_->GetBlock(0, 0));
402 auto* A12 =
dynamic_cast<const mfem::HypreParMatrix*
>(&block_jacobian_->GetBlock(0, 1));
403 auto* A21 =
dynamic_cast<const mfem::HypreParMatrix*
>(&block_jacobian_->GetBlock(1, 0));
404 auto* A22 =
dynamic_cast<const mfem::HypreParMatrix*
>(&block_jacobian_->GetBlock(1, 1));
406 MFEM_VERIFY(A11 && A12 && A21 && A22,
407 "All blocks must be HypreParMatrix for assembled Schur complement preconditioner.");
416 const mfem::Operator* op =
nullptr;
417 if (block_op_overrides_[0]) {
418 op = block_op_overrides_[0].get();
422 mfem_solvers_[0]->SetOperator(*op);
425 S_approx_owned_.reset(BuildSchurDiagApprox_(*A11, *A12, *A21, *A22));
426 S_approx_view_ = S_approx_owned_.get();
428 S_approx_owned_.reset(
new mfem::HypreParMatrix(*A22));
429 S_approx_view_ = S_approx_owned_.get();
431 S_approx_owned_.reset();
432 S_approx_view_ = block_op_overrides_[1].get();
435 MFEM_VERIFY(S_approx_view_,
"Schur complement approximation operator must be set");
438 mfem_solvers_[1]->SetOperator(*S_approx_view_);
441 solver_diag_.SetBlock(0, 0, mfem_solvers_[0].
get());
442 solver_diag_.SetBlock(1, 1, mfem_solvers_[1].
get());
445 BlockSchurPreconditioner::~BlockSchurPreconditioner() {}
BlockDiagonalPreconditioner(mfem::Array< int > &offsets, std::vector< std::unique_ptr< mfem::Solver >> solvers, std::vector< BlockOverride > overrides={})
Construct a new N by N block diagonal preconditioner.
virtual void Mult(const mfem::Vector &in, mfem::Vector &out) const
The action of the precondition on the block vector (b_1, ..., b_n)
virtual void SetOperator(const mfem::Operator &jacobian)
Set the preconditioner to use the supplied linearized block Jacobian.
virtual void Mult(const mfem::Vector &in, mfem::Vector &out) const
The action of the precondition on the block vector (b_1, b_2)
BlockSchurPreconditioner(mfem::Array< int > &offsets, std::vector< std::unique_ptr< mfem::Solver >> solvers, BlockSchurType type=BlockSchurType::Diagonal, SchurApproxType approxType=SchurApproxType::DiagInv, std::vector< BlockOverride > overrides={})
Construct a new 2x2 block Schur complement preconditioner.
virtual void SetOperator(const mfem::Operator &jacobian)
Set the preconditioner to use the supplied linearized block Jacobian.
BlockTriangularPreconditioner(mfem::Array< int > &offsets, std::vector< std::unique_ptr< mfem::Solver >> solvers, BlockTriangularType type=BlockTriangularType::Lower, std::vector< BlockOverride > overrides={})
Construct a new nxn block triangular preconditioner.
virtual void Mult(const mfem::Vector &in, mfem::Vector &out) const
The action of the precondition on the block vector (b_1, ..., b_n)
virtual void SetOperator(const mfem::Operator &jacobian)
Set the preconditioner to use the supplied linearized block Jacobian.
Accelerator functionality.
constexpr T & get(variant< T0, T1 > &v)
Returns the variant member of specified type.
SchurApproxType
Selects how the (1,1) Schur operator is approximated.
double matrixNorm(std::unique_ptr< mfem::HypreParMatrix > &K)
Utility to compute the matrix norm.
constexpr SMITH_HOST_DEVICE auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.
BlockTriangularType
Selects the block triangular sweep used by BlockTriangularPreconditioner.
BlockSchurType
Selects the block Schur preconditioner variant.