288 lines
8.3 KiB
C++
288 lines
8.3 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 "pcomplexweakform.hpp"
|
|
|
|
#ifdef MFEM_USE_MPI
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
void ParComplexDPGWeakForm::FillEssTdofLists(const Array<int> &
|
|
ess_tdof_list)
|
|
{
|
|
int j;
|
|
for (int i = 0; i < ess_tdof_list.Size(); i++)
|
|
{
|
|
int tdof = ess_tdof_list[i];
|
|
for (j = 0; j < nblocks; j++)
|
|
{
|
|
if (tdof_offsets[j+1] > tdof) { break; }
|
|
}
|
|
ess_tdofs[j]->Append(tdof-tdof_offsets[j]);
|
|
}
|
|
}
|
|
|
|
void ParComplexDPGWeakForm::Assemble(int skip_zeros)
|
|
{
|
|
ComplexDPGWeakForm::Assemble(skip_zeros);
|
|
}
|
|
|
|
void ParComplexDPGWeakForm::ParallelAssemble(BlockMatrix *m_r,
|
|
BlockMatrix *m_i)
|
|
{
|
|
if (!P) { BuildProlongation(); }
|
|
|
|
p_mat_r = new BlockOperator(tdof_offsets);
|
|
p_mat_i = new BlockOperator(tdof_offsets);
|
|
p_mat_e_r = new BlockOperator(tdof_offsets);
|
|
p_mat_e_i = new BlockOperator(tdof_offsets);
|
|
p_mat_r->owns_blocks = 1;
|
|
p_mat_i->owns_blocks = 1;
|
|
p_mat_e_r->owns_blocks = 1;
|
|
p_mat_e_i->owns_blocks = 1;
|
|
HypreParMatrix * A_r = nullptr;
|
|
HypreParMatrix * A_i = nullptr;
|
|
HypreParMatrix * PtAP_r = nullptr;
|
|
HypreParMatrix * PtAP_i = nullptr;
|
|
for (int i = 0; i < nblocks; i++)
|
|
{
|
|
HypreParMatrix * Pi = (HypreParMatrix*)(&P->GetBlock(i,i));
|
|
for (int j = 0; j<nblocks; j++)
|
|
{
|
|
if (m_r->IsZeroBlock(i,j)) { continue; }
|
|
if (i == j)
|
|
{
|
|
// Make block diagonal square hypre matrix
|
|
A_r = new HypreParMatrix(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
|
|
trial_pfes[i]->GetDofOffsets(), &m_r->GetBlock(i,i));
|
|
PtAP_r = RAP(A_r,Pi);
|
|
delete A_r;
|
|
p_mat_e_r->SetBlock(i, i, PtAP_r->EliminateRowsCols(*ess_tdofs[i]));
|
|
|
|
A_i = new HypreParMatrix(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
|
|
trial_pfes[i]->GetDofOffsets(), &m_i->GetBlock(i,i));
|
|
|
|
PtAP_i = RAP(A_i,Pi);
|
|
delete A_i;
|
|
p_mat_e_i->SetBlock(i, i, PtAP_i->EliminateCols(*ess_tdofs[i]));
|
|
PtAP_i->EliminateRows(*ess_tdofs[i]);
|
|
}
|
|
else
|
|
{
|
|
HypreParMatrix * Pj = (HypreParMatrix*)(&P->GetBlock(j,j));
|
|
A_r = new HypreParMatrix(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
|
|
trial_pfes[j]->GlobalVSize(), trial_pfes[i]->GetDofOffsets(),
|
|
trial_pfes[j]->GetDofOffsets(), &m_r->GetBlock(i,j));
|
|
PtAP_r = RAP(Pi,A_r,Pj);
|
|
delete A_r;
|
|
p_mat_e_r->SetBlock(i, j, PtAP_r->EliminateCols(*ess_tdofs[j]));
|
|
PtAP_r->EliminateRows(*ess_tdofs[i]);
|
|
|
|
A_i = new HypreParMatrix(trial_pfes[i]->GetComm(), trial_pfes[i]->GlobalVSize(),
|
|
trial_pfes[j]->GlobalVSize(), trial_pfes[i]->GetDofOffsets(),
|
|
trial_pfes[j]->GetDofOffsets(), &m_i->GetBlock(i,j));
|
|
PtAP_i = RAP(Pi,A_i,Pj);
|
|
delete A_i;
|
|
p_mat_e_i->SetBlock(i, j, PtAP_i->EliminateCols(*ess_tdofs[j]));
|
|
PtAP_i->EliminateRows(*ess_tdofs[i]);
|
|
}
|
|
p_mat_r->SetBlock(i, j, PtAP_r);
|
|
p_mat_i->SetBlock(i, j, PtAP_i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ParComplexDPGWeakForm::BuildProlongation()
|
|
{
|
|
P = new BlockOperator(dof_offsets, tdof_offsets);
|
|
R = new BlockMatrix(tdof_offsets, dof_offsets);
|
|
P->owns_blocks = 0;
|
|
R->owns_blocks = 0;
|
|
|
|
for (int i = 0; i < nblocks; i++)
|
|
{
|
|
HypreParMatrix * P_ = trial_pfes[i]->Dof_TrueDof_Matrix();
|
|
P->SetBlock(i,i,P_);
|
|
const SparseMatrix * R_ = trial_pfes[i]->GetRestrictionMatrix();
|
|
R->SetBlock(i, i, const_cast<SparseMatrix*>(R_));
|
|
}
|
|
}
|
|
|
|
void ParComplexDPGWeakForm::FormLinearSystem(const Array<int>
|
|
&ess_tdof_list,
|
|
Vector &x,
|
|
OperatorHandle &A,
|
|
Vector &X, Vector &B,
|
|
int copy_interior)
|
|
{
|
|
FormSystemMatrix(ess_tdof_list, A);
|
|
if (static_cond)
|
|
{
|
|
static_cond->ReduceSystem(x, X, B, copy_interior);
|
|
}
|
|
else
|
|
{
|
|
int n = P->Width();
|
|
B.SetSize(2*n);
|
|
Vector B_r(B, 0, n);
|
|
Vector B_i(B, n, n);
|
|
P->MultTranspose(*y_r, B_r);
|
|
P->MultTranspose(*y_i, B_i);
|
|
|
|
int m = R->Height();
|
|
X.SetSize(2*m);
|
|
|
|
Vector X_r(X, 0, m);
|
|
Vector X_i(X, m, m);
|
|
|
|
Vector x_r(x, 0, x.Size()/2);
|
|
Vector x_i(x, x.Size()/2, x.Size()/2);
|
|
|
|
R->Mult(x_r, X_r);
|
|
R->Mult(x_i, X_i);
|
|
|
|
// eliminate tdof is RHS
|
|
// B_r -= Ae_r*X_r + Ae_i X_i
|
|
// B_i -= Ae_i*X_r + Ae_r X_i
|
|
Vector tmp(B_r.Size());
|
|
p_mat_e_r->Mult(X_r, tmp); B_r-=tmp;
|
|
p_mat_e_i->Mult(X_i, tmp); B_r+=tmp;
|
|
|
|
p_mat_e_i->Mult(X_r, tmp); B_i-=tmp;
|
|
p_mat_e_r->Mult(X_i, tmp); B_i-=tmp;
|
|
|
|
for (int j = 0; j < nblocks; j++)
|
|
{
|
|
if (!ess_tdofs[j]->Size()) { continue; }
|
|
for (int i = 0; i < ess_tdofs[j]->Size(); i++)
|
|
{
|
|
int tdof = (*ess_tdofs[j])[i];
|
|
int gdof = tdof + tdof_offsets[j];
|
|
B_r(gdof) = X_r(gdof); // diagonal policy is always one in parallel
|
|
B_i(gdof) = X_i(gdof); // diagonal policy is always one in parallel
|
|
}
|
|
}
|
|
if (!copy_interior)
|
|
{
|
|
X_r.SetSubVectorComplement(ess_tdof_list, 0.0);
|
|
X_i.SetSubVectorComplement(ess_tdof_list, 0.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParComplexDPGWeakForm::FormSystemMatrix(const Array<int>
|
|
&ess_tdof_list,
|
|
OperatorHandle &A)
|
|
{
|
|
if (static_cond)
|
|
{
|
|
if (!static_cond->HasEliminatedBC())
|
|
{
|
|
static_cond->SetEssentialTrueDofs(ess_tdof_list);
|
|
static_cond->FormSystemMatrix(Operator::DiagonalPolicy::DIAG_ONE);
|
|
}
|
|
A.Reset(&static_cond->GetSchurComplexOperator(), false);
|
|
}
|
|
else
|
|
{
|
|
FillEssTdofLists(ess_tdof_list);
|
|
if (mat_r)
|
|
{
|
|
const int remove_zeros = 0;
|
|
Finalize(remove_zeros);
|
|
ParallelAssemble(mat_r, mat_i);
|
|
delete mat_r;
|
|
delete mat_i;
|
|
mat_r = nullptr;
|
|
mat_i = nullptr;
|
|
delete mat_e_r;
|
|
delete mat_e_i;
|
|
mat_e_r = nullptr;
|
|
mat_e_i = nullptr;
|
|
}
|
|
p_mat = new ComplexOperator(p_mat_r, p_mat_i, false, false);
|
|
A.Reset(p_mat, false);
|
|
}
|
|
}
|
|
|
|
void ParComplexDPGWeakForm::RecoverFEMSolution(const Vector &X,
|
|
Vector &x)
|
|
{
|
|
if (static_cond)
|
|
{
|
|
static_cond->ComputeSolution(X, x);
|
|
}
|
|
else
|
|
{
|
|
int n = P->Height();
|
|
int m = P->Width();
|
|
x.SetSize(2*n);
|
|
|
|
Vector x_r(x,0,n);
|
|
Vector x_i(x,n,n);
|
|
Vector X_r(const_cast<Vector&>(X), 0, m);
|
|
Vector X_i(const_cast<Vector&>(X), m, m);
|
|
|
|
P->Mult(X_r, x_r);
|
|
P->Mult(X_i, x_i);
|
|
}
|
|
}
|
|
|
|
void ParComplexDPGWeakForm::Update()
|
|
{
|
|
ComplexDPGWeakForm::Update();
|
|
delete p_mat_e_r;
|
|
delete p_mat_e_i;
|
|
p_mat_e_r = nullptr;
|
|
p_mat_e_i = nullptr;
|
|
delete p_mat_r;
|
|
delete p_mat_i;
|
|
p_mat_r = nullptr;
|
|
p_mat_i = nullptr;
|
|
delete p_mat;
|
|
p_mat = nullptr;
|
|
for (int i = 0; i < nblocks; i++)
|
|
{
|
|
delete ess_tdofs[i];
|
|
ess_tdofs[i] = new Array<int>();
|
|
}
|
|
delete P;
|
|
P = nullptr;
|
|
delete R;
|
|
R = nullptr;
|
|
}
|
|
|
|
ParComplexDPGWeakForm::~ParComplexDPGWeakForm()
|
|
{
|
|
delete p_mat_e_r;
|
|
delete p_mat_e_i;
|
|
p_mat_e_r = nullptr;
|
|
p_mat_e_i = nullptr;
|
|
delete p_mat_r;
|
|
delete p_mat_i;
|
|
p_mat_r = nullptr;
|
|
p_mat_i = nullptr;
|
|
delete p_mat;
|
|
p_mat = nullptr;
|
|
for (int i = 0; i < nblocks; i++)
|
|
{
|
|
delete ess_tdofs[i];
|
|
}
|
|
delete P;
|
|
delete R;
|
|
}
|
|
|
|
} // namespace mfem
|
|
|
|
#endif
|