Compare commits

...
Author SHA1 Message Date
Mark L. Stowell 13846b5b98 Merge branch 'master' into hcurl-bdr-dev 2025-10-21 14:42:42 -07:00
Tzanio Kolev cd4e583f9f Merge pull request #4659 from mfem/najlkin/parform-impro
Improvements of Par(Mixed)BilinearForm and Par(Block)NonlinearForm
2025-10-18 10:48:44 -07:00
Tzanio Kolev ee94776558 Merge pull request #5066 from mfem/najlkin/fix-nc-blknlform
[BUG] Non-conforming meshes in BlockNonlinearForm
2025-10-18 10:48:25 -07:00
Tzanio Kolev f9be12c539 Merge pull request #5060 from mfem/hypre-cmake
Remove hypre+umpire check in CMake
2025-10-15 16:13:01 -07:00
Tzanio Kolev af478afd00 Merge branch 'master' into najlkin/fix-nc-blknlform 2025-10-15 16:11:05 -07:00
Jan Nikl e13d1a1d53 Fixed support of non-conforming meshes in BlockNonlinearForm. 2025-10-10 13:32:53 -07:00
Victor A. P. Magri 09e73b5cee Remove check 2025-10-09 17:11:13 -04:00
Victor A. P. Magri 464b441409 Remove check 2025-10-09 17:10:09 -04:00
Victor A. P. Magri 3f0a5ad501 Add check for HYPRE_USING_UMPIRE 2025-10-09 12:49:42 -04:00
Jan Nikl b7a0b2cf9a Renamed ParallelAssembleInternal(Matrix) and added more documentation. 2025-10-07 12:28:31 -07:00
Tzanio Kolev bce6e2ca76 Merge branch 'master' into najlkin/parform-impro 2025-10-05 13:25:11 -07:00
Tzanio Kolev 40d1550fd6 Merge branch 'master' into najlkin/parform-impro 2025-09-17 03:31:41 -07:00
Jan Nikl 193f8a6801 Partially reverted const modifiers in HypreParMatrix::Copy(Bool)CSR(). 2025-09-16 16:19:03 -07:00
Jan Nikl 110720dd04 Fixed documentation of Par(Mixed)BilinearForm::KeepNbrBlock(). 2025-09-16 15:39:35 -07:00
Jan Nikl 3fd335c77b Fixed usage of EliminateVDofsInRHS(). 2025-09-16 15:36:04 -07:00
Jan Nikl 058fdaae3f Implemented gradient of ParBlockNonlinearForm with shared face contributions. 2025-06-11 16:23:42 -07:00
Jan Nikl 9a92e4875b Implemented Mult of ParBlockNonlinearForm with shared face contributions. 2025-06-11 16:22:16 -07:00
Jan Nikl 9dab032bd0 Merge branch 'master' into najlkin/parform-impro 2025-04-24 15:40:44 -07:00
Jan Nikl cdfe8102ae Added gradient of ParNonlinearForm with face integrators. 2025-03-18 16:44:51 -07:00
Jan Nikl 5b82bf0328 Revert "WIP: Added support for trace face integrators in ParMixedBilinearForm."
This reverts commit 323ee572b6.
2025-03-06 06:00:07 -08:00
Tzanio Kolev 41b65d6333 Merge branch 'master' into najlkin/parform-impro 2025-02-04 14:55:19 -08:00
Jan Nikl 2534d2207d Fixed name of ParallelEliminateTrialEssentialBC(). 2025-01-09 10:49:10 -08:00
Jan Nikl 260b817b3c Merge branch 'master' into najlkin/parform-impro 2025-01-09 10:01:52 -08:00
Jan Nikl 613d5dd826 Fixed constness in some HyperParMatrix constructors. 2025-01-08 17:56:42 -08:00
Jan Nikl 323ee572b6 WIP: Added support for trace face integrators in ParMixedBilinearForm. 2025-01-08 17:56:05 -08:00
Jan Nikl 56ff5ac5bb Added support for interior face integrators to ParMixedBilinearForm. 2025-01-08 17:55:42 -08:00
Jan Nikl e1a06bd6c8 Added methods to Par(Mixed)BilinearForm for elimination of essential BCs. 2025-01-08 17:55:08 -08:00
Jan Nikl 9acae54669 Extended ParMixedBilinearForm methods for parallel assembly. 2025-01-08 17:54:28 -08:00
Jan Nikl 7d92e22a45 Added ParallelAssembleInternal() method to ParBilinearForm. 2025-01-08 17:53:21 -08:00
Stowell, Mark L 352fe1b703 Bugfix: fec is normally NULL 2020-05-21 13:47:43 -07:00
Stowell, Mark L 50dd45fb23 Adding specialization for HCurl basis in ProjectBdrCoefficientTangent 2020-05-21 13:07:27 -07:00
12 changed files with 887 additions and 117 deletions
-10
View File
@@ -197,16 +197,6 @@ if (HYPRE_FOUND AND HYPRE_USING_HIP)
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
endif()
# Hypre+Umpire check
if (HYPRE_FOUND AND (HYPRE_USING_CUDA OR HYPRE_USING_HIP) AND NOT MFEM_USE_UMPIRE)
message(WARNING
"===============================================================
Detected GPU-enabled HYPRE build without Umpire support.
This is not recommended for performance reasons!
Consider rebuilding HYPRE with Umpire support.
===============================================================")
endif()
find_package_handle_standard_args(HYPRE
REQUIRED_VARS HYPRE_LIBRARIES HYPRE_INCLUDE_DIRS HYPRE_VERSION
)
+20
View File
@@ -2750,6 +2750,26 @@ void GridFunction::ProjectBdrCoefficientNormal(
void GridFunction::ProjectBdrCoefficientTangent(
VectorCoefficient &vcoeff, const Array<int> &bdr_attr)
{
if (fes->FEColl()->GetContType() == FiniteElementCollection::TANGENTIAL)
{
const FiniteElement *fe;
ElementTransformation *T;
Array<int> vdofs;
Vector vals;
for (int i = 0; i < fes->GetNBE(); i++)
{
if (bdr_attr[fes->GetBdrAttribute(i) - 1] == 0) { continue; }
fe = fes->GetBE(i);
T = fes->GetBdrElementTransformation(i);
fes->GetBdrElementVDofs(i, vdofs);
vals.SetSize(vdofs.Size());
fe->Project(vcoeff, *T, vals);
SetSubVector(vdofs, vals);
}
return;
}
Array<int> values_counter;
AccumulateAndCountBdrTangentValues(vcoeff, bdr_attr, values_counter);
ComputeMeans(ARITHMETIC, values_counter);
+35 -5
View File
@@ -436,7 +436,7 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
// In parallel, the result is in 'py' which is an alias for 'aux2'.
}
Operator &NonlinearForm::GetGradient(const Vector &x) const
Operator &NonlinearForm::GetGradient(const Vector &x, bool finalize) const
{
if (ext)
{
@@ -644,6 +644,8 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
}
}
if (!finalize) { return *Grad; }
if (!Grad->Finalized())
{
Grad->Finalize(skip_zeros);
@@ -1203,7 +1205,14 @@ const BlockVector &BlockNonlinearForm::Prolongate(const BlockVector &bx) const
aux1.Update(block_offsets);
for (int s = 0; s < fes.Size(); s++)
{
P[s]->Mult(bx.GetBlock(s), aux1.GetBlock(s));
if (P[s])
{
P[s]->Mult(bx.GetBlock(s), aux1.GetBlock(s));
}
else
{
aux1.GetBlock(s) = bx.GetBlock(s);
}
}
return aux1;
}
@@ -1232,11 +1241,16 @@ void BlockNonlinearForm::Mult(const Vector &x, Vector &y) const
{
cP[s]->MultTranspose(pby.GetBlock(s), by.GetBlock(s));
}
else if (needs_prolongation)
{
by.GetBlock(s) = pby.GetBlock(s);
}
by.GetBlock(s).SetSubVector(*ess_tdofs[s], 0.0);
}
}
void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx,
bool finalize) const
{
const int skip_zeros = 0;
Array<Array<int> *> vdofs(fes.Size());
@@ -1490,7 +1504,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
}
}
if (!Grads(0,0)->Finalized())
if (finalize && !Grads(0,0)->Finalized())
{
for (int i=0; i<fes.Size(); ++i)
{
@@ -1529,7 +1543,23 @@ Operator &BlockNonlinearForm::GetGradient(const Vector &x) const
for (int s2 = 0; s2 < fes.Size(); ++s2)
{
delete cGrads(s1, s2);
cGrads(s1, s2) = RAP(*cP[s1], *Grads(s1, s2), *cP[s2]);
if (cP[s1] && cP[s2])
{
cGrads(s1, s2) = RAP(*cP[s1], *Grads(s1, s2), *cP[s2]);
}
else if (cP[s1])
{
cGrads(s1, s2) = TransposeMult(*cP[s1], *Grads(s1, s2));
}
else if (cP[s2])
{
cGrads(s1, s2) = mfem::Mult(*Grads(s1, s2), *cP[s2]);
}
else
{
cGrads(s1, s2) = NULL;
continue;
}
mGrads(s1, s2) = cGrads(s1, s2);
}
}
+7 -2
View File
@@ -217,7 +217,12 @@ public:
In general, @a x may have non-homogeneous essential boundary values.
The state @a x must be a true-dof vector. */
Operator &GetGradient(const Vector &x) const override;
Operator &GetGradient(const Vector &x) const override { return GetGradient(x, true); }
/** @brief Compute the gradient Operator of the NonlinearForm corresponding
to the state @a x with optional finalization and elimintaion. */
/** @see GetGradient(const Vector &) */
Operator &GetGradient(const Vector &x, bool finalize) const;
/// Update the NonlinearForm to propagate updates of the associated FE space.
/** After calling this method, the essential boundary conditions need to be
@@ -308,7 +313,7 @@ protected:
void MultBlocked(const BlockVector &bx, BlockVector &by) const;
/// Specialized version of GetGradient() for BlockVector
void ComputeGradientBlocked(const BlockVector &bx) const;
void ComputeGradientBlocked(const BlockVector &bx, bool finalize = true) const;
public:
/// Construct an empty BlockNonlinearForm. Initialize with SetSpaces().
+251 -39
View File
@@ -151,6 +151,15 @@ void ParBilinearForm::ParallelRAP(SparseMatrix &loc_A, OperatorHandle &A,
}
}
HypreParMatrix *ParBilinearForm::ParallelAssembleInternalMatrix()
{
if (p_mat.Ptr() == NULL)
{
ParallelAssemble(p_mat, mat);
}
return p_mat.As<HypreParMatrix>();
}
void ParBilinearForm::ParallelAssemble(OperatorHandle &A, SparseMatrix *A_local)
{
A.Clear();
@@ -333,6 +342,15 @@ void ParBilinearForm
A.EliminateRowsCols(dof_list, X, B);
}
void ParBilinearForm::ParallelEliminateEssentialBC(
const Array<int> &bdr_attr_is_ess, const HypreParVector &X, HypreParVector &B)
{
Array<int> dof_list;
pfes->GetEssentialTrueDofs(bdr_attr_is_ess, dof_list);
p_mat.As<HypreParMatrix>()->EliminateRowsCols(dof_list, X, B);
}
HypreParMatrix *ParBilinearForm::
ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
HypreParMatrix &A) const
@@ -344,6 +362,26 @@ ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
return A.EliminateRowsCols(dof_list);
}
void ParBilinearForm::ParallelEliminateEssentialBC(const Array<int>
&bdr_attr_is_ess)
{
Array<int> tdofs_list;
pfes->GetEssentialTrueDofs(bdr_attr_is_ess, tdofs_list);
ParallelEliminateTDofs(tdofs_list);
}
void ParBilinearForm::ParallelEliminateTDofs(const Array<int> &tdofs_list)
{
p_mat_e.EliminateRowsCols(p_mat, tdofs_list);
}
void ParBilinearForm::ParallelEliminateTDofsInRHS(
const Array<int> &tdofs_list, const Vector &x, Vector &b)
{
p_mat.EliminateBC(p_mat_e, tdofs_list, x, b);
}
void ParBilinearForm::TrueAddMult(const Vector &x, Vector &y, const real_t a)
const
{
@@ -485,7 +523,7 @@ void ParBilinearForm::FormLinearSystem(
HypreParVector true_X(pfes), true_B(pfes);
P.MultTranspose(b, true_B);
R.Mult(x, true_X);
p_mat.EliminateBC(p_mat_e, ess_tdof_list, true_X, true_B);
ParallelEliminateTDofsInRHS(ess_tdof_list, true_X, true_B);
R.MultTranspose(true_B, b);
hybridization->ReduceRHS(true_B, B);
X.SetSize(B.Size());
@@ -498,17 +536,11 @@ void ParBilinearForm::FormLinearSystem(
B.SetSize(X.Size());
P.MultTranspose(b, B);
R.Mult(x, X);
p_mat.EliminateBC(p_mat_e, ess_tdof_list, X, B);
ParallelEliminateTDofsInRHS(ess_tdof_list, X, B);
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
}
void ParBilinearForm::EliminateVDofsInRHS(
const Array<int> &vdofs, const Vector &x, Vector &b)
{
p_mat.EliminateBC(p_mat_e, vdofs, x, b);
}
void ParBilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A)
{
@@ -553,7 +585,7 @@ void ParBilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
mat = NULL;
delete mat_e;
mat_e = NULL;
p_mat_e.EliminateRowsCols(p_mat, ess_tdof_list);
ParallelEliminateTDofs(ess_tdof_list);
}
if (hybridization)
{
@@ -615,36 +647,180 @@ void ParBilinearForm::Update(FiniteElementSpace *nfes)
p_mat_e.Clear();
}
HypreParMatrix *ParMixedBilinearForm::ParallelAssemble()
void ParMixedBilinearForm::pAllocMat()
{
// construct the block-diagonal matrix A
HypreParMatrix *A =
new HypreParMatrix(trial_pfes->GetComm(),
test_pfes->GlobalVSize(),
trial_pfes->GlobalVSize(),
test_pfes->GetDofOffsets(),
trial_pfes->GetDofOffsets(),
mat);
const int trial_nbr_size = trial_pfes->GetFaceNbrVSize();
const int test_nbr_size = test_pfes->GetFaceNbrVSize();
HypreParMatrix *rap = RAP(test_pfes->Dof_TrueDof_Matrix(), A,
trial_pfes->Dof_TrueDof_Matrix());
delete A;
return rap;
if (keep_nbr_block)
{
mat = new SparseMatrix(height + test_nbr_size, width + trial_nbr_size);
}
else
{
mat = new SparseMatrix(height, width + trial_nbr_size);
}
}
void ParMixedBilinearForm::ParallelAssemble(OperatorHandle &A)
void ParMixedBilinearForm::AssembleSharedFaces(int skip_zeros)
{
// construct the rectangular block-diagonal matrix dA
OperatorHandle dA(A.Type());
dA.MakeRectangularBlockDiag(trial_pfes->GetComm(),
test_pfes->GlobalVSize(),
trial_pfes->GlobalVSize(),
test_pfes->GetDofOffsets(),
trial_pfes->GetDofOffsets(),
mat);
ParMesh *pmesh = trial_pfes->GetParMesh();
FaceElementTransformations *T;
Array<int> tr_vdofs1, tr_vdofs2, tr_vdofs_all;
Array<int> te_vdofs1, te_vdofs2, te_vdofs_all;
DenseMatrix elemmat;
int nfaces = pmesh->GetNSharedFaces();
for (int i = 0; i < nfaces; i++)
{
T = pmesh->GetSharedFaceTransformations(i);
int Elem2NbrNo = T->Elem2No - pmesh->GetNE();
trial_pfes->GetElementVDofs(T->Elem1No, tr_vdofs1);
test_pfes->GetElementVDofs(T->Elem1No, te_vdofs1);
trial_pfes->GetFaceNbrElementVDofs(Elem2NbrNo, tr_vdofs2);
test_pfes->GetFaceNbrElementVDofs(Elem2NbrNo, te_vdofs2);
tr_vdofs1.Copy(tr_vdofs_all);
for (int j = 0; j < tr_vdofs2.Size(); j++)
{
if (tr_vdofs2[j] >= 0)
{
tr_vdofs2[j] += width;
}
else
{
tr_vdofs2[j] -= width;
}
}
tr_vdofs_all.Append(tr_vdofs2);
if (keep_nbr_block)
{
te_vdofs1.Copy(te_vdofs_all);
for (int j = 0; j < te_vdofs2.Size(); j++)
{
if (te_vdofs2[j] >= 0)
{
te_vdofs2[j] += height;
}
else
{
te_vdofs2[j] -= height;
}
}
te_vdofs_all.Append(te_vdofs2);
}
for (int k = 0; k < interior_face_integs.Size(); k++)
{
interior_face_integs[k]->
AssembleFaceMatrix(*trial_pfes->GetFE(T->Elem1No),
*test_pfes->GetFE(T->Elem1No),
*trial_pfes->GetFaceNbrFE(Elem2NbrNo),
*test_pfes->GetFaceNbrFE(Elem2NbrNo),
*T, elemmat);
if (keep_nbr_block)
{
mat->AddSubMatrix(te_vdofs_all, tr_vdofs_all, elemmat, skip_zeros);
}
else
{
mat->AddSubMatrix(te_vdofs1, tr_vdofs_all, elemmat, skip_zeros);
}
}
}
}
void ParMixedBilinearForm::Assemble(int skip_zeros)
{
if (interior_face_integs.Size())
{
trial_pfes->ExchangeFaceNbrData();
test_pfes->ExchangeFaceNbrData();
if (!ext && mat == NULL)
{
pAllocMat();
}
}
MixedBilinearForm::Assemble(skip_zeros);
if (!ext && interior_face_integs.Size() > 0)
{
AssembleSharedFaces(skip_zeros);
}
}
HypreParMatrix *ParMixedBilinearForm::ParallelAssembleInternalMatrix()
{
if (p_mat.Ptr() == NULL)
{
ParallelAssemble(p_mat, mat);
}
return p_mat.As<HypreParMatrix>();
}
HypreParMatrix *ParMixedBilinearForm::ParallelAssemble(SparseMatrix *m)
{
OperatorHandle Mh(Operator::Hypre_ParCSR);
ParallelAssemble(Mh, m);
Mh.SetOperatorOwner(false);
return Mh.As<HypreParMatrix>();
}
void ParMixedBilinearForm::ParallelAssemble(OperatorHandle &A,
SparseMatrix *A_local)
{
A.Clear();
if (A_local == NULL) { return; }
MFEM_VERIFY(A_local->Finalized(), "the local matrix must be finalized");
OperatorHandle dA(A.Type()), hdA;
if (interior_face_integs.Size() == 0)
{
// construct the rectangular block-diagonal matrix dA
dA.MakeRectangularBlockDiag(trial_pfes->GetComm(),
test_pfes->GlobalVSize(),
trial_pfes->GlobalVSize(),
test_pfes->GetDofOffsets(),
trial_pfes->GetDofOffsets(),
A_local);
}
else
{
// handle the case when 'a' contains off-diagonal
const int lvrows = test_pfes->GetVSize();
const int lvcols = trial_pfes->GetVSize();
const HYPRE_BigInt *face_nbr_glob_lcol = trial_pfes->GetFaceNbrGlobalDofMap();
const HYPRE_BigInt lcol_offset = trial_pfes->GetMyDofOffset();
Array<HYPRE_BigInt> glob_J(A_local->NumNonZeroElems());
const int *J = A_local->GetJ();
for (int i = 0; i < glob_J.Size(); i++)
{
if (J[i] < lvcols)
{
glob_J[i] = J[i] + lcol_offset;
}
else
{
glob_J[i] = face_nbr_glob_lcol[J[i] - lvcols];
}
}
// TODO - construct dA directly in the A format
hdA.Reset(
new HypreParMatrix(trial_pfes->GetComm(), lvrows, test_pfes->GlobalVSize(),
trial_pfes->GlobalVSize(), A_local->GetI(), glob_J,
A_local->GetData(), test_pfes->GetDofOffsets(),
trial_pfes->GetDofOffsets()));
// - hdA owns the new HypreParMatrix
// - the above constructor copies all input arrays
glob_J.DeleteAll();
dA.ConvertFrom(hdA);
}
OperatorHandle P_test(A.Type()), P_trial(A.Type());
@@ -670,6 +846,44 @@ void ParMixedBilinearForm::TrueAddMult(const Vector &x, Vector &y,
test_pfes->Dof_TrueDof_Matrix()->MultTranspose(a, Yaux, 1.0, y);
}
void ParMixedBilinearForm::ParallelEliminateTrialEssentialBC(
const Array<int> &bdr_attr_is_ess)
{
Array<int> trial_tdof_list;
trial_pfes->GetEssentialTrueDofs(bdr_attr_is_ess, trial_tdof_list);
ParallelEliminateTrialTDofs(trial_tdof_list);
}
void ParMixedBilinearForm::ParallelEliminateTrialTDofs(
const Array<int> &trial_tdof_list)
{
HypreParMatrix *temp = p_mat.As<HypreParMatrix>()->EliminateCols(
trial_tdof_list);
p_mat_e.Reset(temp, true);
}
void ParMixedBilinearForm::ParallelEliminateTrialTDofsInRHS(
const Array<int> &trial_tdof_list, const Vector &x, Vector &b)
{
p_mat_e.As<HypreParMatrix>()->Mult(-1.0, x, 1.0, b);
}
void ParMixedBilinearForm::ParallelEliminateTestEssentialBC(
const Array<int> &bdr_attr_is_ess)
{
Array<int> test_tdof_list;
test_pfes->GetEssentialTrueDofs(bdr_attr_is_ess, test_tdof_list);
ParallelEliminateTestTDofs(test_tdof_list);
}
void ParMixedBilinearForm::ParallelEliminateTestTDofs(
const Array<int> &test_tdof_list)
{
p_mat.As<HypreParMatrix>()->EliminateRows(test_tdof_list);
}
void ParMixedBilinearForm::FormRectangularSystemMatrix(
const Array<int>
&trial_tdof_list,
@@ -690,10 +904,8 @@ void ParMixedBilinearForm::FormRectangularSystemMatrix(
mat = NULL;
delete mat_e;
mat_e = NULL;
HypreParMatrix *temp =
p_mat.As<HypreParMatrix>()->EliminateCols(trial_tdof_list);
p_mat.As<HypreParMatrix>()->EliminateRows(test_tdof_list);
p_mat_e.Reset(temp, true);
ParallelEliminateTrialTDofs(trial_tdof_list);
ParallelEliminateTestTDofs(test_tdof_list);
}
A = p_mat;
@@ -723,7 +935,7 @@ void ParMixedBilinearForm::FormRectangularLinearSystem(
test_P->MultTranspose(b, B);
trial_R->Mult(x, X);
p_mat_e.As<HypreParMatrix>()->Mult(-1.0, X, 1.0, B);
ParallelEliminateTrialTDofsInRHS(trial_tdof_list, X, B);
B.SetSubVector(test_tdof_list, 0.0);
}
+128 -5
View File
@@ -73,7 +73,7 @@ public:
/** When set to true and the ParBilinearForm has interior face integrators,
the local SparseMatrix will include the rows (in addition to the columns)
corresponding to face-neighbor dofs. The default behavior is to disregard
those rows. Must be called before the first Assemble call. */
those rows. Must be called before the first Assemble() call. */
void KeepNbrBlock(bool knb = true) { keep_nbr_block = knb; }
/** @brief Set the operator type id for the parallel matrix/operator when
@@ -101,6 +101,14 @@ public:
diagonal for this case. */
void AssembleDiagonal(Vector &diag) const override;
/// Returns the matrix assembled on the true dofs, i.e. P^t A P.
/** The returned matrix is the internal one, owned by the form. It is not
reassembled if it has been already constructed. If FormSystemMatrix()
has been called before, it is the system matrix with eliminated
essential DOFs, otherwise the parallel matrix is assembled here without
the elimination process. */
HypreParMatrix *ParallelAssembleInternalMatrix();
/// Returns the matrix assembled on the true dofs, i.e. P^t A P.
/** The returned matrix has to be deleted by the caller. */
HypreParMatrix *ParallelAssemble() { return ParallelAssemble(mat); }
@@ -146,6 +154,13 @@ public:
const HypreParVector &X,
HypreParVector &B) const;
/// Eliminate essential boundary DOFs from the parallel system matrix.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. */
void ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
const HypreParVector &X,
HypreParVector &B);
/// Eliminate essential boundary DOFs from a parallel assembled matrix @a A.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. The eliminated part is stored in a
@@ -157,6 +172,12 @@ public:
HypreParMatrix *ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
HypreParMatrix &A) const;
/// Eliminate essential boundary DOFs from the parallel system matrix.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. This method relies on
ParallelEliminateTDofs(const Array<int> &), see it for details. */
void ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess);
/// Eliminate essential true DOFs from a parallel assembled matrix @a A.
/** Given a list of essential true dofs and the parallel assembled matrix
@a A, eliminate the true dofs from the matrix, storing the eliminated
@@ -169,6 +190,28 @@ public:
HypreParMatrix &A) const
{ return A.EliminateRowsCols(tdofs_list); }
/// Eliminate essential true DOFs from the parallel system matrix.
/** Given a list of essential true dofs, eliminate the true dofs from
the parallel assembled system matrix, storing the eliminated part
internally. This method works in conjunction with
ParallelEliminateTDofsInRHS() and allows elimination of boundary
conditions in multiple right-hand sides. */
void ParallelEliminateTDofs(const Array<int> &tdofs_list);
/** @brief Use the stored eliminated part of the parallel system matrix for
elimination of boundary conditions in the r.h.s. */
/** Given a list of essential true dofs, eliminate the true dofs from the
right-hand side @a b using the solution vector @a x and the previously
stored eliminated part of the parallel assembled system matrix produced
by ParallelEliminateTDofs(const Array<int> &). */
void ParallelEliminateTDofsInRHS(const Array<int> &tdofs, const Vector &x,
Vector &b);
/// @deprecated Use ParallelEliminateTDofsInRHS() instead.
MFEM_DEPRECATED void EliminateVDofsInRHS(const Array<int> &vdofs,
const Vector &x, Vector &b)
{ ParallelEliminateTDofsInRHS(vdofs, x, b); }
/** @brief Compute @a y += @a a (P^t A P) @a x, where @a x and @a y are
vectors on the true dofs. */
void TrueAddMult(const Vector &x, Vector &y, const real_t a = 1.0) const;
@@ -238,8 +281,6 @@ public:
void Update(FiniteElementSpace *nfes = NULL) override;
void EliminateVDofsInRHS(const Array<int> &vdofs, const Vector &x, Vector &b);
virtual ~ParBilinearForm() { }
};
@@ -257,6 +298,13 @@ protected:
/// Matrix and eliminated matrix
OperatorHandle p_mat, p_mat_e;
bool keep_nbr_block;
// Allocate mat - called when (mat == NULL && fbfi.Size() > 0)
void pAllocMat();
void AssembleSharedFaces(int skip_zeros = 1);
private:
/// Copy construction is not supported; body is undefined.
ParMixedBilinearForm(const ParMixedBilinearForm &);
@@ -276,6 +324,7 @@ public:
{
trial_pfes = trial_fes;
test_pfes = test_fes;
keep_nbr_block = false;
}
/** @brief Create a ParMixedBilinearForm on the given FiniteElementSpace%s
@@ -295,15 +344,89 @@ public:
{
trial_pfes = trial_fes;
test_pfes = test_fes;
keep_nbr_block = false;
}
/** When set to true and the ParMixedBilinearForm has interior face
integrators, the local SparseMatrix will include the rows (in addition
to the columns) corresponding to face-neighbor dofs. The default
behavior is to disregard those rows. Must be called before the first
Assemble() call. */
void KeepNbrBlock(bool knb = true) { keep_nbr_block = knb; }
/// Assemble the local matrix
void Assemble(int skip_zeros = 1);
/// Returns the matrix assembled on the true dofs, i.e. P_test^t A P_trial.
HypreParMatrix *ParallelAssemble();
/** The returned matrix is the internal one, owned by the form. It is not
reassembled if it has been already constructed. If
FormRectangularSystemMatrix() has been called before, it is the system
matrix with eliminated essential DOFs, otherwise the parallel matrix is
assembled here without the elimination process. */
HypreParMatrix *ParallelAssembleInternalMatrix();
/// Returns the matrix assembled on the true dofs, i.e. P_test^t A P_trial.
/** The returned matrix has to be deleted by the caller. */
HypreParMatrix *ParallelAssemble() { return ParallelAssemble(mat); }
/** @brief Returns the eliminated matrix assembled on the true dofs, i.e.
P_test^t A_local P_trial. */
/** The returned matrix has to be deleted by the caller. */
HypreParMatrix *ParallelAssembleElim() { return ParallelAssemble(mat_e); }
/** @brief Return the matrix @a m assembled on the true dofs, i.e. P_test^t
A_local P_trial. */
/** The returned matrix has to be deleted by the caller. */
HypreParMatrix *ParallelAssemble(SparseMatrix *m);
/** @brief Returns the matrix assembled on the true dofs, i.e.
@a A = P_test^t A_local P_trial, in the format (type id) specified by
@a A. */
void ParallelAssemble(OperatorHandle &A);
void ParallelAssemble(OperatorHandle &A) { ParallelAssemble(A, mat); }
/** Returns the eliminated matrix assembled on the true dofs, i.e.
@a A_elim = P^t A_elim_local P in the format (type id) specified by @a A.
*/
void ParallelAssembleElim(OperatorHandle &A_elim)
{ ParallelAssemble(A_elim, mat_e); }
/** Returns the matrix @a A_local assembled on the true dofs, i.e.
@a A = P_test^t A_local P_trial in the format (type id) specified by
@a A. */
void ParallelAssemble(OperatorHandle &A, SparseMatrix *A_local);
/// Eliminate essential boundary trial DOFs from the parallel system matrix.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. This method relies on
ParallelEliminateTrialTDofs(const Array<int> &), see it for details. */
void ParallelEliminateTrialEssentialBC(const Array<int> &bdr_attr_is_ess);
/// Eliminate essential trial true DOFs from the parallel system matrix.
/** Given a list of essential trial true dofs, eliminate the trial true dofs
from the parallel assembled system matrix, storing the eliminated part
internally. This method works in conjunction with
ParallelEliminateTrialTDofsInRHS() and allows elimination of boundary
conditions in multiple right-hand sides. */
void ParallelEliminateTrialTDofs(const Array<int> &trial_tdof_list);
/** @brief Use the stored eliminated part of the parallel system matrix for
elimination of boundary conditions in the r.h.s. */
/** Given a list of essential trial true dofs, eliminate the trial true dofs
from the right-hand side @a B using the solution vector @a X and the
previously stored eliminated part of the parallel assembled system
matrix produced by ParallelEliminateTrialTDofs(const Array<int> &). */
void ParallelEliminateTrialTDofsInRHS(const Array<int> &trial_tdof_list,
const Vector &X, Vector &B);
/// Eliminate essential boundary test DOFs from the parallel system matrix.
/** The array @a bdr_attr_is_ess marks boundary attributes that constitute
the essential part of the boundary. */
void ParallelEliminateTestEssentialBC(const Array<int> &bdr_attr_is_ess);
/// Eliminate essential test true DOFs from the parallel system matrix.
/** Given a list of essential test true dofs, eliminate the test true dofs
from the parallel assembled system matrix. */
void ParallelEliminateTestTDofs(const Array<int> &test_tdof_list);
using MixedBilinearForm::FormRectangularSystemMatrix;
using MixedBilinearForm::FormRectangularLinearSystem;
+20
View File
@@ -711,6 +711,26 @@ void ParGridFunction::ProjectBdrCoefficient(
void ParGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
const Array<int> &bdr_attr)
{
if (pfes->FEColl()->GetContType() == FiniteElementCollection::TANGENTIAL)
{
const FiniteElement *fe;
ElementTransformation *T;
Array<int> vdofs;
Vector vals;
for (int i = 0; i < pfes->GetNBE(); i++)
{
if (bdr_attr[pfes->GetBdrAttribute(i) - 1] == 0) { continue; }
fe = pfes->GetBE(i);
T = pfes->GetBdrElementTransformation(i);
pfes->GetBdrElementVDofs(i, vdofs);
vals.SetSize(vdofs.Size());
fe->Project(vcoeff, *T, vals);
SetSubVector(vdofs, vals);
}
return;
}
Array<int> values_counter;
AccumulateAndCountBdrTangentValues(vcoeff, bdr_attr, values_counter);
+405 -41
View File
@@ -105,6 +105,59 @@ const SparseMatrix &ParNonlinearForm::GetLocalGradient(const Vector &x) const
return *Grad;
}
void ParNonlinearForm::GradientSharedFaces(const Vector &x,
int skip_zeros) const
{
ParFiniteElementSpace *pfes = ParFESpace();
ParMesh *pmesh = pfes->GetParMesh();
FaceElementTransformations *T;
Array<int> vdofs1, vdofs2, vdofs_all;
DenseMatrix elemmat;
Vector el_x, nbr_x, face_x;
const Vector &px = Prolongate(x);
ParGridFunction pgf(pfes, const_cast<Vector&>(px), 0);
pgf.ExchangeFaceNbrData();
int nfaces = pmesh->GetNSharedFaces();
for (int i = 0; i < nfaces; i++)
{
T = pmesh->GetSharedFaceTransformations(i);
int Elem2NbrNo = T->Elem2No - pmesh->GetNE();
pfes->GetElementVDofs(T->Elem1No, vdofs1);
pfes->GetFaceNbrElementVDofs(Elem2NbrNo, vdofs2);
face_x.SetSize(vdofs1.Size() + vdofs2.Size());
el_x.MakeRef(face_x, 0, vdofs1.Size());
pgf.GetSubVector(vdofs1, el_x);
nbr_x.MakeRef(face_x, vdofs1.Size(), vdofs2.Size());
pgf.FaceNbrData().GetSubVector(vdofs2, nbr_x);
vdofs1.Copy(vdofs_all);
for (int j = 0; j < vdofs2.Size(); j++)
{
if (vdofs2[j] >= 0)
{
vdofs2[j] += height;
}
else
{
vdofs2[j] -= height;
}
}
vdofs_all.Append(vdofs2);
for (int k = 0; k < fnfi.Size(); k++)
{
fnfi[k]->AssembleFaceGrad(*pfes->GetFE(T->Elem1No),
*pfes->GetFaceNbrFE(Elem2NbrNo),
*T, face_x, elemmat);
Grad->AddSubMatrix(vdofs1, vdofs_all, elemmat, skip_zeros);
}
}
}
Operator &ParNonlinearForm::GetGradient(const Vector &x) const
{
if (NonlinearForm::ext) { return NonlinearForm::GetGradient(x); }
@@ -112,19 +165,61 @@ Operator &ParNonlinearForm::GetGradient(const Vector &x) const
ParFiniteElementSpace *pfes = ParFESpace();
pGrad.Clear();
OperatorHandle dA(pGrad.Type()), Ph(pGrad.Type()), hdA;
NonlinearForm::GetGradient(x); // (re)assemble Grad, no b.c.
OperatorHandle dA(pGrad.Type()), Ph(pGrad.Type());
if (fnfi.Size() == 0)
if (fnfi.Size())
{
dA.MakeSquareBlockDiag(pfes->GetComm(), pfes->GlobalVSize(),
pfes->GetDofOffsets(), Grad);
const int skip_zeros = 0;
pfes->ExchangeFaceNbrData();
if (Grad == NULL)
{
int nbr_size = pfes->GetFaceNbrVSize();
Grad = new SparseMatrix(pfes->GetVSize(), pfes->GetVSize() + nbr_size);
}
NonlinearForm::GetGradient(x, false); // (re)assemble Grad, no b.c.
GradientSharedFaces(x, skip_zeros);
Grad->Finalize(skip_zeros);
// handle the case when 'a' contains off-diagonal
int lvsize = pfes->GetVSize();
const HYPRE_BigInt *face_nbr_glob_ldof = pfes->GetFaceNbrGlobalDofMap();
HYPRE_BigInt ldof_offset = pfes->GetMyDofOffset();
Array<HYPRE_BigInt> glob_J(Grad->NumNonZeroElems());
int *J = Grad->GetJ();
for (int i = 0; i < glob_J.Size(); i++)
{
if (J[i] < lvsize)
{
glob_J[i] = J[i] + ldof_offset;
}
else
{
glob_J[i] = face_nbr_glob_ldof[J[i] - lvsize];
}
}
// TODO - construct dA directly in the A format
hdA.Reset(
new HypreParMatrix(pfes->GetComm(), lvsize, pfes->GlobalVSize(),
pfes->GlobalVSize(), Grad->GetI(), glob_J,
Grad->GetData(), pfes->GetDofOffsets(),
pfes->GetDofOffsets()));
// - hdA owns the new HypreParMatrix
// - the above constructor copies all input arrays
glob_J.DeleteAll();
dA.ConvertFrom(hdA);
}
else
{
MFEM_ABORT("TODO: assemble contributions from shared face terms");
NonlinearForm::GetGradient(x); // (re)assemble Grad, no b.c.
dA.MakeSquareBlockDiag(pfes->GetComm(), pfes->GlobalVSize(),
pfes->GetDofOffsets(), Grad);
}
// RAP the local gradient dA.
@@ -271,7 +366,70 @@ void ParBlockNonlinearForm::Mult(const Vector &x, Vector &y) const
if (fnfi.Size() > 0)
{
MFEM_ABORT("TODO: assemble contributions from shared face terms");
// Terms over shared interior faces in parallel.
ParMesh *pmesh = ParFESpace(0)->GetParMesh();
FaceElementTransformations *tr;
Array<Array<int> *>vdofs(fes.Size());
Array<Array<int> *>vdofs2(fes.Size());
Array<Vector *> el_x(fes.Size());
Array<const Vector *> el_x_const(fes.Size());
Array<Vector *> el_y(fes.Size());
Array<const FiniteElement *> fe(fes.Size());
Array<const FiniteElement *> fe2(fes.Size());
Array<ParGridFunction *> pgfs(fes.Size());
for (int s=0; s<fes.Size(); ++s)
{
el_x_const[s] = el_x[s] = new Vector();
el_y[s] = new Vector();
vdofs[s] = new Array<int>;
vdofs2[s] = new Array<int>;
pgfs[s] = new ParGridFunction(const_cast<ParFiniteElementSpace*>(ParFESpace(s)),
xs.GetBlock(s));
pgfs[s]->ExchangeFaceNbrData();
}
const int n_shared_faces = pmesh->GetNSharedFaces();
for (int i = 0; i < n_shared_faces; i++)
{
tr = pmesh->GetSharedFaceTransformations(i, true);
int Elem2NbrNo = tr->Elem2No - pmesh->GetNE();
for (int s=0; s<fes.Size(); ++s)
{
const ParFiniteElementSpace *pfes = ParFESpace(s);
fe[s] = pfes->GetFE(tr->Elem1No);
fe2[s] = pfes->GetFaceNbrFE(Elem2NbrNo);
pfes->GetElementVDofs(tr->Elem1No, *(vdofs[s]));
pfes->GetFaceNbrElementVDofs(Elem2NbrNo, *(vdofs2[s]));
el_x[s]->SetSize(vdofs[s]->Size() + vdofs2[s]->Size());
xs.GetBlock(s).GetSubVector(*(vdofs[s]), el_x[s]->GetData());
pgfs[s]->FaceNbrData().GetSubVector(*(vdofs2[s]),
el_x[s]->GetData() + vdofs[s]->Size());
}
for (int k = 0; k < fnfi.Size(); ++k)
{
fnfi[k]->AssembleFaceVector(fe, fe2, *tr, el_x_const, el_y);
for (int s=0; s<fes.Size(); ++s)
{
if (el_y[s]->Size() == 0) { continue; }
ys.GetBlock(s).AddElementVector(*(vdofs[s]), *el_y[s]);
}
}
}
for (int s=0; s<fes.Size(); ++s)
{
delete pgfs[s];
delete vdofs2[s];
delete vdofs[s];
delete el_y[s];
delete el_x[s];
}
}
for (int s=0; s<fes.Size(); ++s)
@@ -328,6 +486,106 @@ void ParBlockNonlinearForm::SetGradientType(Operator::Type tid)
}
}
void ParBlockNonlinearForm::GradientSharedFaces(const BlockVector &xs,
int skip_zeros) const
{
// Terms over shared interior faces in parallel.
ParMesh *pmesh = ParFESpace(0)->GetParMesh();
FaceElementTransformations *tr;
Array<Array<int> *>vdofs(fes.Size());
Array<Array<int> *>vdofs2(fes.Size());
Array<Array<int> *>vdofs_all(fes.Size());
Array<Vector *> el_x(fes.Size());
Array<const Vector *> el_x_const(fes.Size());
Array2D<DenseMatrix *> elmats(fes.Size(), fes.Size());
Array<const FiniteElement *> fe(fes.Size());
Array<const FiniteElement *> fe2(fes.Size());
Array<ParGridFunction *> pgfs(fes.Size());
for (int s1=0; s1<fes.Size(); ++s1)
{
el_x_const[s1] = el_x[s1] = new Vector();
vdofs[s1] = new Array<int>;
vdofs2[s1] = new Array<int>;
vdofs_all[s1] = new Array<int>;
pgfs[s1] = new ParGridFunction(
const_cast<ParFiniteElementSpace*>(ParFESpace(s1)),
const_cast<Vector&>(xs.GetBlock(s1)));
pgfs[s1]->ExchangeFaceNbrData();
for (int s2=0; s2<fes.Size(); ++s2)
{
elmats(s1,s2) = new DenseMatrix();
}
}
const int n_shared_faces = pmesh->GetNSharedFaces();
for (int i = 0; i < n_shared_faces; i++)
{
tr = pmesh->GetSharedFaceTransformations(i, true);
int Elem2NbrNo = tr->Elem2No - pmesh->GetNE();
for (int s=0; s<fes.Size(); ++s)
{
const ParFiniteElementSpace *pfes = ParFESpace(s);
fe[s] = pfes->GetFE(tr->Elem1No);
fe2[s] = pfes->GetFaceNbrFE(Elem2NbrNo);
pfes->GetElementVDofs(tr->Elem1No, *(vdofs[s]));
pfes->GetFaceNbrElementVDofs(Elem2NbrNo, *(vdofs2[s]));
el_x[s]->SetSize(vdofs[s]->Size() + vdofs2[s]->Size());
xs.GetBlock(s).GetSubVector(*(vdofs[s]), el_x[s]->GetData());
pgfs[s]->FaceNbrData().GetSubVector(*(vdofs2[s]),
el_x[s]->GetData() + vdofs[s]->Size());
vdofs[s]->Copy(*vdofs_all[s]);
const int lvsize = pfes->GetVSize();
for (int j = 0; j < vdofs2[s]->Size(); j++)
{
if ((*vdofs2[s])[j] >= 0)
{
(*vdofs2[s])[j] += lvsize;
}
else
{
(*vdofs2[s])[j] -= lvsize;
}
}
vdofs_all[s]->Append(*(vdofs2[s]));
}
for (int k = 0; k < fnfi.Size(); ++k)
{
fnfi[k]->AssembleFaceGrad(fe, fe2, *tr, el_x_const, elmats);
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
if (elmats(s1,s2)->Height() == 0) { continue; }
Grads(s1,s2)->AddSubMatrix(*vdofs[s1], *vdofs_all[s2],
*elmats(s1,s2), skip_zeros);
}
}
}
}
for (int s1=0; s1<fes.Size(); ++s1)
{
delete pgfs[s1];
delete vdofs_all[s1];
delete vdofs2[s1];
delete vdofs[s1];
delete el_x[s1];
for (int s2=0; s2<fes.Size(); ++s2)
{
delete elmats(s1,s2);
}
}
}
BlockOperator & ParBlockNonlinearForm::GetGradient(const Vector &x) const
{
if (pBlockGrad == NULL)
@@ -347,49 +605,155 @@ BlockOperator & ParBlockNonlinearForm::GetGradient(const Vector &x) const
}
}
GetLocalGradient(x); // gradients are stored in 'Grads'
// xs_true is not modified, so const_cast is okay
xs_true.Update(const_cast<Vector &>(x), block_trueOffsets);
xs.Update(block_offsets);
for (int s=0; s<fes.Size(); ++s)
{
fes[s]->GetProlongationMatrix()->Mult(
xs_true.GetBlock(s), xs.GetBlock(s));
}
if (fnfi.Size() > 0)
{
MFEM_ABORT("TODO: assemble contributions from shared face terms");
}
const int skip_zeros = 0;
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
for (int s=0; s<fes.Size(); ++s)
{
OperatorHandle dA(phBlockGrad(s1,s2)->Type()),
Ph(phBlockGrad(s1,s2)->Type()),
Rh(phBlockGrad(s1,s2)->Type());
const_cast<ParFiniteElementSpace*>(pfes[s])->ExchangeFaceNbrData();
}
if (s1 == s2)
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
dA.MakeSquareBlockDiag(pfes[s1]->GetComm(), pfes[s1]->GlobalVSize(),
pfes[s1]->GetDofOffsets(), Grads(s1,s1));
Ph.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
phBlockGrad(s1,s1)->MakePtAP(dA, Ph);
OperatorHandle Ae;
Ae.EliminateRowsCols(*phBlockGrad(s1,s1), *ess_tdofs[s1]);
if (Grads(s1,s2) == NULL)
{
int nbr_size = pfes[s2]->GetFaceNbrVSize();
Grads(s1,s2) = new SparseMatrix(pfes[s1]->GetVSize(),
pfes[s2]->GetVSize() + nbr_size);
}
}
else
}
// (re)assemble Grad without b.c. into 'Grads'
BlockNonlinearForm::ComputeGradientBlocked(xs, false);
GradientSharedFaces(xs, skip_zeros);
// finalize the gradients
for (int s1=0; s1<fes.Size(); ++s1)
for (int s2=0; s2<fes.Size(); ++s2)
{
dA.MakeRectangularBlockDiag(pfes[s1]->GetComm(),
pfes[s1]->GlobalVSize(),
pfes[s2]->GlobalVSize(),
pfes[s1]->GetDofOffsets(),
pfes[s2]->GetDofOffsets(),
Grads(s1,s2));
Rh.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
Ph.ConvertFrom(pfes[s2]->Dof_TrueDof_Matrix());
phBlockGrad(s1,s2)->MakeRAP(Rh, dA, Ph);
phBlockGrad(s1,s2)->EliminateRows(*ess_tdofs[s1]);
phBlockGrad(s1,s2)->EliminateCols(*ess_tdofs[s2]);
Grads(s1,s2)->Finalize(skip_zeros);
}
pBlockGrad->SetBlock(s1, s2, phBlockGrad(s1,s2)->Ptr());
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
OperatorHandle hdA;
OperatorHandle dA(phBlockGrad(s1,s2)->Type()),
Ph(phBlockGrad(s1,s2)->Type()),
Rh(phBlockGrad(s1,s2)->Type());
// handle the case when 'a' contains off-diagonal
int lvsize = pfes[s2]->GetVSize();
const HYPRE_BigInt *face_nbr_glob_ldof =
const_cast<ParFiniteElementSpace*>(pfes[s2])->GetFaceNbrGlobalDofMap();
HYPRE_BigInt ldof_offset = pfes[s2]->GetMyDofOffset();
Array<HYPRE_BigInt> glob_J(Grads(s1,s2)->NumNonZeroElems());
int *J = Grads(s1,s2)->GetJ();
for (int i = 0; i < glob_J.Size(); i++)
{
if (J[i] < lvsize)
{
glob_J[i] = J[i] + ldof_offset;
}
else
{
glob_J[i] = face_nbr_glob_ldof[J[i] - lvsize];
}
}
// TODO - construct dA directly in the A format
hdA.Reset(
new HypreParMatrix(pfes[s2]->GetComm(), pfes[s1]->GetVSize(),
pfes[s1]->GlobalVSize(), pfes[s2]->GlobalVSize(),
Grads(s1,s2)->GetI(), glob_J, Grads(s1,s2)->GetData(),
pfes[s1]->GetDofOffsets(), pfes[s2]->GetDofOffsets()));
// - hdA owns the new HypreParMatrix
// - the above constructor copies all input arrays
glob_J.DeleteAll();
dA.ConvertFrom(hdA);
if (s1 == s2)
{
Ph.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
phBlockGrad(s1,s1)->MakePtAP(dA, Ph);
OperatorHandle Ae;
Ae.EliminateRowsCols(*phBlockGrad(s1,s1), *ess_tdofs[s1]);
}
else
{
Rh.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
Ph.ConvertFrom(pfes[s2]->Dof_TrueDof_Matrix());
phBlockGrad(s1,s2)->MakeRAP(Rh, dA, Ph);
phBlockGrad(s1,s2)->EliminateRows(*ess_tdofs[s1]);
phBlockGrad(s1,s2)->EliminateCols(*ess_tdofs[s2]);
}
pBlockGrad->SetBlock(s1, s2, phBlockGrad(s1,s2)->Ptr());
}
}
}
else
{
// (re)assemble Grad without b.c. into 'Grads'
BlockNonlinearForm::ComputeGradientBlocked(xs);
for (int s1=0; s1<fes.Size(); ++s1)
{
for (int s2=0; s2<fes.Size(); ++s2)
{
OperatorHandle dA(phBlockGrad(s1,s2)->Type()),
Ph(phBlockGrad(s1,s2)->Type()),
Rh(phBlockGrad(s1,s2)->Type());
if (s1 == s2)
{
dA.MakeSquareBlockDiag(pfes[s1]->GetComm(), pfes[s1]->GlobalVSize(),
pfes[s1]->GetDofOffsets(), Grads(s1,s1));
Ph.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
phBlockGrad(s1,s1)->MakePtAP(dA, Ph);
OperatorHandle Ae;
Ae.EliminateRowsCols(*phBlockGrad(s1,s1), *ess_tdofs[s1]);
}
else
{
dA.MakeRectangularBlockDiag(pfes[s1]->GetComm(),
pfes[s1]->GlobalVSize(),
pfes[s2]->GlobalVSize(),
pfes[s1]->GetDofOffsets(),
pfes[s2]->GetDofOffsets(),
Grads(s1,s2));
Rh.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
Ph.ConvertFrom(pfes[s2]->Dof_TrueDof_Matrix());
phBlockGrad(s1,s2)->MakeRAP(Rh, dA, Ph);
phBlockGrad(s1,s2)->EliminateRows(*ess_tdofs[s1]);
phBlockGrad(s1,s2)->EliminateCols(*ess_tdofs[s2]);
}
pBlockGrad->SetBlock(s1, s2, phBlockGrad(s1,s2)->Ptr());
}
}
}
+4
View File
@@ -29,6 +29,8 @@ protected:
mutable ParGridFunction X, Y;
mutable OperatorHandle pGrad;
void GradientSharedFaces(const Vector &x, int skip_zeros = 1) const;
public:
ParNonlinearForm(ParFiniteElementSpace *pf);
@@ -81,6 +83,8 @@ protected:
mutable Array2D<OperatorHandle *> phBlockGrad;
mutable BlockOperator *pBlockGrad;
void GradientSharedFaces(const BlockVector &xs, int skip_zeros) const;
public:
/// Computes the energy of the system
real_t GetEnergy(const Vector &x) const override;
+11 -9
View File
@@ -561,7 +561,8 @@ void CopyMemory(Memory<T> &src, Memory<T> &dst, MemoryClass dst_mc,
this function. In particular, @a dst should be empty or deleted before
calling this function. */
template <typename SrcT, typename DstT>
void CopyConvertMemory(Memory<SrcT> &src, MemoryClass dst_mc, Memory<DstT> &dst)
void CopyConvertMemory(const Memory<SrcT> &src, MemoryClass dst_mc,
Memory<DstT> &dst)
{
auto capacity = src.Capacity();
dst.New(capacity, GetMemoryType(dst_mc));
@@ -842,8 +843,8 @@ static int GetPartitioningArraySize(MPI_Comm comm)
///
/// Both @a row and @a col are partitioning arrays, whose length is returned by
/// GetPartitioningArraySize(), see @ref hypre_partitioning_descr.
static bool RowAndColStartsAreEqual(MPI_Comm comm, HYPRE_BigInt *rows,
HYPRE_BigInt *cols)
static bool RowAndColStartsAreEqual(MPI_Comm comm, const HYPRE_BigInt *rows,
const HYPRE_BigInt *cols)
{
const int part_size = GetPartitioningArraySize(comm);
bool are_equal = true;
@@ -1131,7 +1132,7 @@ HypreParMatrix::HypreParMatrix(
HypreParMatrix::HypreParMatrix(MPI_Comm comm,
HYPRE_BigInt *row_starts,
HYPRE_BigInt *col_starts,
SparseMatrix *sm_a)
const SparseMatrix *sm_a)
{
MFEM_ASSERT(sm_a != NULL, "invalid input");
MFEM_VERIFY(!HYPRE_AssumedPartitionCheck(),
@@ -1145,7 +1146,7 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm,
hypre_CSRMatrixSetDataOwner(csr_a,0);
MemoryIJData mem_a;
CopyCSR(sm_a, mem_a, csr_a, false);
CopyCSR(const_cast<SparseMatrix*>(sm_a), mem_a, csr_a, false);
hypre_CSRMatrixSetRownnz(csr_a);
// NOTE: this call creates a matrix on host even when device support is
@@ -1307,10 +1308,11 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm, int id, int np,
HypreParMatrix::HypreParMatrix(MPI_Comm comm, int nrows,
HYPRE_BigInt glob_nrows,
HYPRE_BigInt glob_ncols,
int *I, HYPRE_BigInt *J,
real_t *data,
HYPRE_BigInt *rows,
HYPRE_BigInt *cols)
const int *I,
const HYPRE_BigInt *J,
const real_t *data,
const HYPRE_BigInt *rows,
const HYPRE_BigInt *cols)
{
Init();
+4 -4
View File
@@ -565,7 +565,7 @@ public:
partitioning arrays @a row_starts and @a col_starts. */
HypreParMatrix(MPI_Comm comm, HYPRE_BigInt *row_starts,
HYPRE_BigInt *col_starts,
SparseMatrix *a); // constructor with 4 arguments, v2
const SparseMatrix *a); // constructor with 4 arguments, v2
/// Creates boolean block-diagonal rectangular parallel matrix.
/** The new HypreParMatrix does not take ownership of any of the input
@@ -594,9 +594,9 @@ public:
arrays (so they can be deleted). See @ref hypre_partitioning_descr "here"
for a description of the partitioning arrays @a rows and @a cols. */
HypreParMatrix(MPI_Comm comm, int nrows, HYPRE_BigInt glob_nrows,
HYPRE_BigInt glob_ncols, int *I, HYPRE_BigInt *J,
real_t *data, HYPRE_BigInt *rows,
HYPRE_BigInt *cols); // constructor with 9 arguments
HYPRE_BigInt glob_ncols, const int *I, const HYPRE_BigInt *J,
const real_t *data, const HYPRE_BigInt *rows,
const HYPRE_BigInt *cols); // constructor with 9 arguments
/** @brief Copy constructor for a ParCSR matrix which creates a deep copy of
structure and data from @a P. */
+2 -2
View File
@@ -461,7 +461,7 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
Kmat.Mult(u, z);
z.Neg(); // z = -z
K->EliminateVDofsInRHS(ess_tdof_list, u, z);
K->ParallelEliminateTDofsInRHS(ess_tdof_list, u, z);
M_solver.Mult(z, du_dt);
du_dt.Print();
@@ -483,7 +483,7 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
Kmat.Mult(u, z);
z.Neg();
K->EliminateVDofsInRHS(ess_tdof_list, u, z);
K->ParallelEliminateTDofsInRHS(ess_tdof_list, u, z);
T_solver.Mult(z, du_dt);
du_dt.SetSubVector(ess_tdof_list, 0.0);