577 lines
20 KiB
C++
577 lines
20 KiB
C++
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
|
|
#include "div_free_solver.hpp"
|
|
|
|
using namespace std;
|
|
|
|
namespace mfem::blocksolvers
|
|
{
|
|
|
|
static HypreParMatrix* TwoStepsRAP(const HypreParMatrix *Rt,
|
|
const HypreParMatrix *A,
|
|
const HypreParMatrix *P)
|
|
{
|
|
OperatorPtr R(Rt->Transpose());
|
|
OperatorPtr RA(ParMult(R.As<HypreParMatrix>(), A));
|
|
return ParMult(RA.As<HypreParMatrix>(), P, true);
|
|
}
|
|
|
|
void GetRowColumnsRef(const SparseMatrix& A, int row, Array<int>& cols)
|
|
{
|
|
cols.MakeRef(const_cast<int*>(A.GetRowColumns(row)), A.RowSize(row));
|
|
}
|
|
|
|
SparseMatrix ElemToDof(const ParFiniteElementSpace& fes)
|
|
{
|
|
int* I = new int[fes.GetNE()+1];
|
|
copy_n(fes.GetElementToDofTable().GetI(), fes.GetNE()+1, I);
|
|
Array<int> J(new int[I[fes.GetNE()]], I[fes.GetNE()]);
|
|
copy_n(fes.GetElementToDofTable().GetJ(), J.Size(), J.begin());
|
|
fes.AdjustVDofs(J);
|
|
real_t* D = new real_t[J.Size()];
|
|
fill_n(D, J.Size(), 1.0);
|
|
return SparseMatrix(I, J, D, fes.GetNE(), fes.GetVSize());
|
|
}
|
|
|
|
DFSSpaces::DFSSpaces(int order, int num_refine, ParMesh *mesh,
|
|
const Array<int>& ess_attr, const DFSParameters& param)
|
|
: hdiv_fec_(order, mesh->Dimension()), l2_fec_(order, mesh->Dimension()),
|
|
l2_0_fec_(0, mesh->Dimension()), ess_bdr_attr_(ess_attr), level_(0)
|
|
{
|
|
if (mesh->GetNE() > 0)
|
|
{
|
|
if (mesh->GetElement(0)->GetType() == Element::TETRAHEDRON && order)
|
|
{
|
|
MFEM_ABORT("DFSDataCollector: High order spaces on tetrahedra are not supported");
|
|
}
|
|
}
|
|
|
|
data_.param = param;
|
|
|
|
if (mesh->Dimension() == 3)
|
|
{
|
|
hcurl_fec_ = std::make_unique<ND_FECollection>(order+1, mesh->Dimension());
|
|
}
|
|
else
|
|
{
|
|
hcurl_fec_ = std::make_unique<H1_FECollection>(order+1, mesh->Dimension());
|
|
}
|
|
|
|
all_bdr_attr_.SetSize(ess_attr.Size(), 1);
|
|
hdiv_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &hdiv_fec_);
|
|
l2_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &l2_fec_);
|
|
coarse_hdiv_fes_ = std::make_unique<ParFiniteElementSpace>(*hdiv_fes_);
|
|
coarse_l2_fes_ = std::make_unique<ParFiniteElementSpace>(*l2_fes_);
|
|
l2_0_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &l2_0_fec_);
|
|
l2_0_fes_->SetUpdateOperatorType(Operator::MFEM_SPARSEMAT);
|
|
el_l2dof_.reserve(num_refine+1);
|
|
el_l2dof_.push_back(ElemToDof(*coarse_l2_fes_));
|
|
|
|
data_.agg_hdivdof.resize(num_refine);
|
|
data_.agg_l2dof.resize(num_refine);
|
|
data_.P_hdiv.resize(num_refine);
|
|
data_.P_l2.resize(num_refine);
|
|
|
|
data_.Q_l2.resize(num_refine);
|
|
hdiv_fes_->GetEssentialTrueDofs(ess_attr, data_.coarsest_ess_hdivdofs);
|
|
data_.C.resize(num_refine+1);
|
|
data_.Ae.resize(num_refine+1);
|
|
|
|
hcurl_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, hcurl_fec_.get());
|
|
coarse_hcurl_fes_ = std::make_unique<ParFiniteElementSpace>(*hcurl_fes_);
|
|
data_.P_hcurl.resize(num_refine);
|
|
}
|
|
|
|
SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
|
|
const SparseMatrix& agg_elem,
|
|
const SparseMatrix& elem_dof,
|
|
const HypreParMatrix& dof_truedof,
|
|
Array<HYPRE_BigInt>& agg_starts)
|
|
{
|
|
OperatorPtr agg_dof(Mult(agg_elem, elem_dof));
|
|
SparseMatrix& agg_dof_ref = *agg_dof.As<SparseMatrix>();
|
|
OperatorPtr agg_tdof(dof_truedof.LeftDiagMult(agg_dof_ref, agg_starts));
|
|
OperatorPtr agg_tdof_T(agg_tdof.As<HypreParMatrix>()->Transpose());
|
|
SparseMatrix tdof_agg, is_shared;
|
|
HYPRE_BigInt* trash;
|
|
agg_tdof_T.As<HypreParMatrix>()->GetDiag(tdof_agg);
|
|
agg_tdof_T.As<HypreParMatrix>()->GetOffd(is_shared, trash);
|
|
|
|
int *I = new int[tdof_agg.NumRows()+1]();
|
|
int *J = new int[tdof_agg.NumNonZeroElems()];
|
|
|
|
Array<int> is_bdr;
|
|
FiniteElementSpace::ListToMarker(bdr_truedofs, tdof_agg.NumRows(), is_bdr);
|
|
|
|
int counter = 0;
|
|
for (int i = 0; i < tdof_agg.NumRows(); ++i)
|
|
{
|
|
bool agg_bdr = is_bdr[i] || is_shared.RowSize(i) || tdof_agg.RowSize(i)>1;
|
|
if (agg_bdr) { I[i+1] = I[i]; continue; }
|
|
I[i+1] = I[i] + 1;
|
|
J[counter++] = tdof_agg.GetRowColumns(i)[0];
|
|
}
|
|
|
|
auto *D = new real_t[I[tdof_agg.NumRows()]];
|
|
std::fill_n(D, I[tdof_agg.NumRows()], 1.0);
|
|
|
|
SparseMatrix intdof_agg(I, J, D, tdof_agg.NumRows(), tdof_agg.NumCols());
|
|
return Transpose(intdof_agg);
|
|
}
|
|
|
|
void DFSSpaces::MakeDofRelationTables(int level)
|
|
{
|
|
Array<HYPRE_BigInt> agg_starts(Array<HYPRE_BigInt>(l2_0_fes_->GetDofOffsets(),
|
|
2));
|
|
auto& elem_agg = (const SparseMatrix&)*l2_0_fes_->GetUpdateOperator();
|
|
OperatorPtr agg_elem(Transpose(elem_agg));
|
|
SparseMatrix& agg_el = *agg_elem.As<SparseMatrix>();
|
|
|
|
el_l2dof_.push_back(ElemToDof(*l2_fes_));
|
|
data_.agg_l2dof[level].Reset(Mult(agg_el, el_l2dof_[level+1]));
|
|
|
|
Array<int> bdr_tdofs;
|
|
hdiv_fes_->GetEssentialTrueDofs(all_bdr_attr_, bdr_tdofs);
|
|
auto tmp = AggToInteriorDof(bdr_tdofs, agg_el, ElemToDof(*hdiv_fes_),
|
|
*hdiv_fes_->Dof_TrueDof_Matrix(), agg_starts);
|
|
data_.agg_hdivdof[level].Reset(tmp);
|
|
}
|
|
|
|
void DFSSpaces::CollectDFSData()
|
|
{
|
|
auto GetP = [&](std::unique_ptr<OperatorPtr> &P,
|
|
std::unique_ptr<ParFiniteElementSpace> &cfes,
|
|
ParFiniteElementSpace& fes, const bool remove_zero)
|
|
{
|
|
fes.Update();
|
|
auto T = new OperatorHandle(Operator::Hypre_ParCSR);
|
|
fes.GetTrueTransferOperator(*cfes, *T);
|
|
P.reset(T);
|
|
if (remove_zero) { P->As<HypreParMatrix>()->DropSmallEntries(1e-16); }
|
|
(level_ < (int)data_.P_l2.size()-1) ? cfes->Update() : cfes.reset();
|
|
};
|
|
|
|
GetP(data_.P_hdiv[level_], coarse_hdiv_fes_, *hdiv_fes_, true);
|
|
GetP(data_.P_l2[level_], coarse_l2_fes_, *l2_fes_, false);
|
|
|
|
MakeDofRelationTables(level_);
|
|
|
|
GetP(data_.P_hcurl[level_], coarse_hcurl_fes_, *hcurl_fes_, true);
|
|
|
|
ParDiscreteLinearOperator curl(hcurl_fes_.get(), hdiv_fes_.get());
|
|
curl.AddDomainInterpolator(new CurlInterpolator);
|
|
curl.Assemble();
|
|
curl.Finalize();
|
|
data_.C[level_+1].Reset(curl.ParallelAssemble());
|
|
mfem::Array<int> ess_hcurl_tdof;
|
|
hcurl_fes_->GetEssentialTrueDofs(ess_bdr_attr_, ess_hcurl_tdof);
|
|
data_.Ae[level_+1].reset(
|
|
data_.C[level_+1].As<HypreParMatrix>()
|
|
->EliminateCols(ess_hcurl_tdof));
|
|
|
|
++level_;
|
|
|
|
if (level_ == (int)data_.P_l2.size()) { DataFinalize(); }
|
|
}
|
|
|
|
void DFSSpaces::DataFinalize()
|
|
{
|
|
ParBilinearForm mass(l2_fes_.get());
|
|
mass.AddDomainIntegrator(new MassIntegrator());
|
|
mass.Assemble();
|
|
mass.Finalize();
|
|
OperatorPtr W(mass.LoseMat());
|
|
|
|
SparseMatrix P_l2;
|
|
for (int l = (int)data_.P_l2.size()-1; l >= 0; --l)
|
|
{
|
|
data_.P_l2[l]->As<HypreParMatrix>()->GetDiag(P_l2);
|
|
OperatorPtr PT_l2(Transpose(P_l2));
|
|
auto PTW = Mult(*PT_l2.As<SparseMatrix>(), *W.As<SparseMatrix>());
|
|
auto cW = Mult(*PTW, P_l2);
|
|
auto cW_inv = new SymDirectSubBlockSolver(*cW, el_l2dof_[l]);
|
|
data_.Q_l2[l].Reset(new ProductOperator(cW_inv, PTW, true, true));
|
|
W.Reset(cW);
|
|
}
|
|
|
|
l2_0_fes_.reset();
|
|
}
|
|
|
|
BBTSolver::BBTSolver(const HypreParMatrix& B, IterSolveParameters param)
|
|
: Solver(B.NumRows()), BBT_solver_(B.GetComm())
|
|
{
|
|
OperatorPtr BT(B.Transpose());
|
|
BBT_.Reset(ParMult(&B, BT.As<HypreParMatrix>()));
|
|
BBT_.As<HypreParMatrix>()->CopyColStarts();
|
|
|
|
BBT_prec_.Reset(new HypreBoomerAMG(*BBT_.As<HypreParMatrix>()));
|
|
BBT_prec_.As<HypreBoomerAMG>()->SetPrintLevel(0);
|
|
|
|
SetOptions(BBT_solver_, param);
|
|
BBT_solver_.SetOperator(*BBT_);
|
|
BBT_solver_.SetPreconditioner(*BBT_prec_.As<HypreBoomerAMG>());
|
|
}
|
|
|
|
LocalSolver::LocalSolver(const DenseMatrix& M, const DenseMatrix& B)
|
|
: Solver(M.NumRows()+B.NumRows()), local_system_(height), offset_(M.NumRows())
|
|
{
|
|
local_system_.CopyMN(M, 0, 0);
|
|
local_system_.CopyMN(B, offset_, 0);
|
|
local_system_.CopyMNt(B, 0, offset_);
|
|
|
|
local_system_.SetRow(offset_, 0.0);
|
|
local_system_.SetCol(offset_, 0.0);
|
|
local_system_(offset_, offset_) = -1.0;
|
|
local_solver_.SetOperator(local_system_);
|
|
}
|
|
|
|
void LocalSolver::Mult(const Vector &x, Vector &y) const
|
|
{
|
|
const real_t x0 = x[offset_];
|
|
const_cast<Vector&>(x)[offset_] = 0.0;
|
|
|
|
y.SetSize(local_system_.NumRows());
|
|
local_solver_.Mult(x, y);
|
|
|
|
const_cast<Vector&>(x)[offset_] = x0;
|
|
}
|
|
|
|
SaddleSchwarzSmoother::SaddleSchwarzSmoother(const HypreParMatrix& M,
|
|
const HypreParMatrix& B,
|
|
const SparseMatrix& agg_hdivdof,
|
|
const SparseMatrix& agg_l2dof,
|
|
const HypreParMatrix& P_l2,
|
|
const ProductOperator& Q_l2)
|
|
: Solver(M.NumRows() + B.NumRows()), agg_hdivdof_(agg_hdivdof),
|
|
agg_l2dof_(agg_l2dof), solvers_loc_(agg_l2dof.NumRows())
|
|
{
|
|
coarse_l2_projector_.Reset(new ProductOperator(&P_l2, &Q_l2, false, false));
|
|
|
|
offsets_loc_.SetSize(3, 0);
|
|
offsets_.SetSize(3, 0);
|
|
offsets_[1] = M.NumRows();
|
|
offsets_[2] = M.NumRows() + B.NumRows();
|
|
|
|
SparseMatrix M_diag, B_diag;
|
|
M.GetDiag(M_diag);
|
|
B.GetDiag(B_diag);
|
|
|
|
DenseMatrix B_loc, M_loc;
|
|
|
|
for (int agg = 0; agg < (int)solvers_loc_.size(); agg++)
|
|
{
|
|
GetRowColumnsRef(agg_hdivdof_, agg, hdivdofs_loc_);
|
|
GetRowColumnsRef(agg_l2dof_, agg, l2dofs_loc_);
|
|
M_loc.SetSize(hdivdofs_loc_.Size(), hdivdofs_loc_.Size());
|
|
B_loc.SetSize(l2dofs_loc_.Size(), hdivdofs_loc_.Size());
|
|
M_diag.GetSubMatrix(hdivdofs_loc_, hdivdofs_loc_, M_loc);
|
|
B_diag.GetSubMatrix(l2dofs_loc_, hdivdofs_loc_, B_loc);
|
|
solvers_loc_[agg].Reset(new LocalSolver(M_loc, B_loc));
|
|
}
|
|
}
|
|
|
|
void SaddleSchwarzSmoother::Mult(const Vector & x, Vector & y) const
|
|
{
|
|
y.SetSize(offsets_[2]);
|
|
y = 0.0;
|
|
|
|
BlockVector blk_y(y.GetData(), offsets_);
|
|
BlockVector Pi_x(offsets_); // aggregate-wise average free projection of x
|
|
static_cast<Vector&>(Pi_x) = x;
|
|
|
|
// Right hand side: F_l = F - W_l P_l2[l] (W_{l+1})^{-1} P_l2[l]^T F
|
|
// This ensures the existence of solutions to the local problems
|
|
Vector coarse_l2_projection(Pi_x.BlockSize(1));
|
|
coarse_l2_projector_->MultTranspose(Pi_x.GetBlock(1), coarse_l2_projection);
|
|
|
|
Pi_x.GetBlock(1) -= coarse_l2_projection;
|
|
|
|
for (int agg = 0; agg < (int)solvers_loc_.size(); agg++)
|
|
{
|
|
GetRowColumnsRef(agg_hdivdof_, agg, hdivdofs_loc_);
|
|
GetRowColumnsRef(agg_l2dof_, agg, l2dofs_loc_);
|
|
|
|
offsets_loc_[1] = hdivdofs_loc_.Size();
|
|
offsets_loc_[2] = offsets_loc_[1]+l2dofs_loc_.Size();
|
|
|
|
BlockVector rhs_loc(offsets_loc_), sol_loc(offsets_loc_);
|
|
Pi_x.GetBlock(0).GetSubVector(hdivdofs_loc_, rhs_loc.GetBlock(0));
|
|
Pi_x.GetBlock(1).GetSubVector(l2dofs_loc_, rhs_loc.GetBlock(1));
|
|
|
|
solvers_loc_[agg]->Mult(rhs_loc, sol_loc);
|
|
|
|
blk_y.GetBlock(0).AddElementVector(hdivdofs_loc_, sol_loc.GetBlock(0));
|
|
blk_y.GetBlock(1).AddElementVector(l2dofs_loc_, sol_loc.GetBlock(1));
|
|
}
|
|
|
|
coarse_l2_projector_->Mult(blk_y.GetBlock(1), coarse_l2_projection);
|
|
blk_y.GetBlock(1) -= coarse_l2_projection;
|
|
}
|
|
|
|
DivFreeSolver::DivFreeSolver(const HypreParMatrix &M,
|
|
const HypreParMatrix &B,
|
|
const DFSData& data)
|
|
: DarcySolver(M.NumRows(), B.NumRows()), data_(data), param_(data.param),
|
|
BT_(B.Transpose()),
|
|
BBT_solver_(B, param_.BBT_solve_param),
|
|
ops_offsets_(data.P_l2.size()+1),
|
|
ops_(ops_offsets_.size()),
|
|
blk_Ps_(ops_.size()-1),
|
|
smoothers_(ops_.size())
|
|
{
|
|
ops_offsets_.back().MakeRef(DarcySolver::offsets_);
|
|
ops_.back() = std::make_unique<BlockOperator>(ops_offsets_.back());
|
|
ops_.back()->SetBlock(0, 0, const_cast<HypreParMatrix*>(&M));
|
|
ops_.back()->SetBlock(1, 0, const_cast<HypreParMatrix*>(&B));
|
|
ops_.back()->SetBlock(0, 1, BT_.Ptr());
|
|
|
|
for (int l = data.P_l2.size(); l >= 0; --l)
|
|
{
|
|
auto &M_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(0, 0));
|
|
auto &B_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(1, 0));
|
|
|
|
if (l == 0)
|
|
{
|
|
SparseMatrix M_f_diag, B_f_diag;
|
|
M_f.GetDiag(M_f_diag);
|
|
B_f.GetDiag(B_f_diag);
|
|
for (int dof : data.coarsest_ess_hdivdofs)
|
|
{
|
|
M_f_diag.EliminateRowCol(dof);
|
|
B_f_diag.EliminateCol(dof);
|
|
}
|
|
|
|
const IterSolveParameters& param = param_.coarse_solve_param;
|
|
auto coarse_solver = new BDPMinresSolver(M_f, B_f, param);
|
|
if (ops_.size() > 1)
|
|
{
|
|
coarse_solver->SetEssZeroDofs(data.coarsest_ess_hdivdofs);
|
|
}
|
|
smoothers_[l].reset(coarse_solver);
|
|
continue;
|
|
}
|
|
|
|
auto P_hdiv_l = data.P_hdiv[l-1]->As<HypreParMatrix>();
|
|
auto P_l2_l = data.P_l2[l-1]->As<HypreParMatrix>();
|
|
SparseMatrix& agg_hdivdof_l = *data.agg_hdivdof[l-1].As<SparseMatrix>();
|
|
SparseMatrix& agg_l2dof_l = *data.agg_l2dof[l-1].As<SparseMatrix>();
|
|
ProductOperator& Q_l2_l = *data.Q_l2[l-1].As<ProductOperator>();
|
|
auto* C_l = data.C[l].As<HypreParMatrix>();
|
|
|
|
auto S0 = new SaddleSchwarzSmoother(M_f, B_f, agg_hdivdof_l,
|
|
agg_l2dof_l, *P_l2_l, Q_l2_l);
|
|
if (param_.coupled_solve)
|
|
{
|
|
auto S1 = new BlockDiagonalPreconditioner(ops_offsets_[l]);
|
|
S1->SetDiagonalBlock(0, new AuxSpaceSmoother(M_f, C_l));
|
|
S1->owns_blocks = 1;
|
|
smoothers_[l] =
|
|
std::make_unique<ProductSolver>(ops_[l].get(), S0, S1, false, true, true);
|
|
}
|
|
else
|
|
{
|
|
smoothers_[l].reset(S0);
|
|
}
|
|
|
|
HypreParMatrix* M_c = TwoStepsRAP(P_hdiv_l, &M_f, P_hdiv_l);
|
|
HypreParMatrix* B_c = TwoStepsRAP(P_l2_l, &B_f, P_hdiv_l);
|
|
|
|
ops_offsets_[l-1].SetSize(3, 0);
|
|
ops_offsets_[l-1][1] = M_c->NumRows();
|
|
ops_offsets_[l-1][2] = M_c->NumRows() + B_c->NumRows();
|
|
|
|
blk_Ps_[l-1] =
|
|
std::make_unique<BlockOperator>(ops_offsets_[l], ops_offsets_[l-1]);
|
|
blk_Ps_[l-1]->SetBlock(0, 0, P_hdiv_l);
|
|
blk_Ps_[l-1]->SetBlock(1, 1, P_l2_l);
|
|
|
|
ops_[l-1] =
|
|
std::make_unique<BlockOperator>(ops_offsets_[l-1]);
|
|
ops_[l-1]->SetBlock(0, 0, M_c);
|
|
ops_[l-1]->SetBlock(1, 0, B_c);
|
|
ops_[l-1]->SetBlock(0, 1, B_c->Transpose());
|
|
ops_[l-1]->owns_blocks = 1;
|
|
}
|
|
|
|
if (data_.P_l2.size() == 0) { return; }
|
|
|
|
Array<bool> own_ops(ops_.size());
|
|
Array<bool> own_smoothers(smoothers_.size());
|
|
Array<bool> own_blk_Ps(blk_Ps_.size());
|
|
own_ops = false, own_smoothers = false, own_blk_Ps = false;
|
|
|
|
Array<Solver*> smoothers(smoothers_.size());
|
|
|
|
if (param_.coupled_solve)
|
|
{
|
|
solver_.Reset(new GMRESSolver(B.GetComm()));
|
|
solver_.As<GMRESSolver>()->SetOperator(*(ops_.back()));
|
|
Array<BlockOperator*> ops(ops_.size()), blk_Ps(blk_Ps_.size());
|
|
for (size_t i = 0; i < ops_.size(); ++i) { ops[i] = ops_[i].get(); }
|
|
for (size_t i = 0; i < blk_Ps_.size(); ++i) { blk_Ps[i] = blk_Ps_[i].get(); }
|
|
for (size_t i = 0; i < smoothers_.size(); ++i) { smoothers[i] = smoothers_[i].get(); }
|
|
prec_.Reset(new Multigrid(ops, smoothers, blk_Ps,
|
|
own_ops, own_smoothers, own_blk_Ps));
|
|
}
|
|
else
|
|
{
|
|
Array<HypreParMatrix*> ops(data_.P_hcurl.size()+1);
|
|
Array<HypreParMatrix*> Ps(data_.P_hcurl.size());
|
|
auto C_finest = data.C.back().As<HypreParMatrix>();
|
|
ops.Last() = TwoStepsRAP(C_finest, &M, C_finest);
|
|
ops.Last()->EliminateZeroRows();
|
|
ops.Last()->DropSmallEntries(1e-14);
|
|
solver_.Reset(new CGSolver(B.GetComm()));
|
|
solver_.As<CGSolver>()->SetOperator(*ops.Last());
|
|
smoothers.Last() = new HypreSmoother(*ops.Last());
|
|
static_cast<HypreSmoother*>(smoothers.Last())->SetOperatorSymmetry(true);
|
|
for (int l = Ps.Size()-1; l >= 0; --l)
|
|
{
|
|
Ps[l] = data_.P_hcurl[l]->As<HypreParMatrix>();
|
|
ops[l] = TwoStepsRAP(Ps[l], ops[l+1], Ps[l]);
|
|
ops[l]->DropSmallEntries(1e-14);
|
|
smoothers[l] = new HypreSmoother(*ops[l]);
|
|
static_cast<HypreSmoother*>(smoothers[l])->SetOperatorSymmetry(true);
|
|
}
|
|
own_ops = true, own_smoothers = true;
|
|
prec_.Reset(new Multigrid(ops, smoothers, Ps,
|
|
own_ops, own_smoothers, own_blk_Ps));
|
|
}
|
|
|
|
solver_.As<IterativeSolver>()->SetPreconditioner(*prec_.As<Solver>());
|
|
SetOptions(*solver_.As<IterativeSolver>(), param_);
|
|
}
|
|
|
|
void DivFreeSolver::SolveParticular(const Vector& rhs, Vector& sol) const
|
|
{
|
|
std::vector<Vector> rhss(smoothers_.size()), sols(smoothers_.size());
|
|
rhss.back().SetDataAndSize(const_cast<real_t*>(rhs.HostRead()), rhs.Size());
|
|
sols.back().SetDataAndSize(sol.HostWrite(), sol.Size());
|
|
|
|
for (int l = blk_Ps_.size()-1; l >= 0; --l)
|
|
{
|
|
rhss[l].SetSize(blk_Ps_[l]->NumCols());
|
|
sols[l].SetSize(blk_Ps_[l]->NumCols());
|
|
|
|
sols[l] = 0.0;
|
|
rhss[l] = 0.0;
|
|
|
|
blk_Ps_[l]->MultTranspose(rhss[l+1], rhss[l]);
|
|
}
|
|
|
|
for (size_t l = 0; l < smoothers_.size(); ++l)
|
|
{
|
|
smoothers_[l]->Mult(rhss[l], sols[l]);
|
|
}
|
|
|
|
for (size_t l = 0; l < blk_Ps_.size(); ++l)
|
|
{
|
|
Vector P_sol(blk_Ps_[l]->NumRows());
|
|
blk_Ps_[l]->Mult(sols[l], P_sol);
|
|
sols[l+1] += P_sol;
|
|
}
|
|
}
|
|
|
|
void DivFreeSolver::SolveDivFree(const Vector &rhs, Vector& sol) const
|
|
{
|
|
Vector rhs_divfree(data_.C.back()->NumCols());
|
|
data_.C.back()->MultTranspose(rhs, rhs_divfree);
|
|
|
|
Vector potential_divfree(rhs_divfree.Size());
|
|
potential_divfree = 0.0;
|
|
solver_->Mult(rhs_divfree, potential_divfree);
|
|
|
|
data_.C.back()->Mult(potential_divfree, sol);
|
|
}
|
|
|
|
void DivFreeSolver::SolvePotential(const Vector& rhs, Vector& sol) const
|
|
{
|
|
Vector rhs_p(BT_->NumCols());
|
|
BT_->MultTranspose(rhs, rhs_p);
|
|
BBT_solver_.Mult(rhs_p, sol);
|
|
}
|
|
|
|
void DivFreeSolver::Mult(const Vector & x, Vector & y) const
|
|
{
|
|
MFEM_VERIFY(x.Size() == offsets_[2], "MLDivFreeSolver: x size is invalid");
|
|
MFEM_VERIFY(y.Size() == offsets_[2], "MLDivFreeSolver: y size is invalid");
|
|
|
|
if (ops_.size() == 1) { smoothers_[0]->Mult(x, y); return; }
|
|
|
|
BlockVector blk_y(y, offsets_);
|
|
|
|
BlockVector resid(offsets_);
|
|
ops_.back()->Mult(y, resid);
|
|
add(1.0, x, -1.0, resid, resid);
|
|
|
|
BlockVector correction(offsets_);
|
|
correction = 0.0;
|
|
|
|
if (param_.coupled_solve)
|
|
{
|
|
solver_->Mult(resid, correction);
|
|
y += correction;
|
|
}
|
|
else
|
|
{
|
|
StopWatch ch;
|
|
ch.Start();
|
|
|
|
SolveParticular(resid, correction);
|
|
blk_y += correction;
|
|
|
|
if (param_.verbose)
|
|
{
|
|
cout << "Particular solution found in " << ch.RealTime() << "s.\n";
|
|
}
|
|
|
|
ch.Clear();
|
|
ch.Start();
|
|
|
|
ops_.back()->Mult(y, resid);
|
|
add(1.0, x, -1.0, resid, resid);
|
|
|
|
SolveDivFree(resid.GetBlock(0), correction.GetBlock(0));
|
|
blk_y.GetBlock(0) += correction.GetBlock(0);
|
|
|
|
if (param_.verbose)
|
|
{
|
|
cout << "Divergence free solution found in " << ch.RealTime() << "s.\n";
|
|
}
|
|
|
|
ch.Clear();
|
|
ch.Start();
|
|
|
|
auto& M = dynamic_cast<const HypreParMatrix&>(ops_.back()->GetBlock(0, 0));
|
|
M.Mult(-1.0, correction.GetBlock(0), 1.0, resid.GetBlock(0));
|
|
SolvePotential(resid.GetBlock(0), correction.GetBlock(1));
|
|
blk_y.GetBlock(1) += correction.GetBlock(1);
|
|
|
|
if (param_.verbose)
|
|
{
|
|
cout << "Scalar potential found in " << ch.RealTime() << "s.\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
int DivFreeSolver::GetNumIterations() const
|
|
{
|
|
if (ops_.size() == 1)
|
|
{
|
|
return static_cast<BDPMinresSolver*>
|
|
(smoothers_.at(0).get())->GetNumIterations();
|
|
}
|
|
return solver_.As<IterativeSolver>()->GetNumIterations();
|
|
}
|
|
|
|
} // namespace mfem::blocksolvers
|