Files
mfem/miniapps/dpg/util/weakform.hpp
T

300 lines
10 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.
#ifndef MFEM_DPGWEAKFORM
#define MFEM_DPGWEAKFORM
#include "mfem.hpp"
#include "blockstaticcond.hpp"
namespace mfem
{
/** @brief Class representing the DPG weak formulation.
Given the variational formulation
a(u,v) = b(v), (or A u = b, where <Au,v> = a(u,v))
this class forms the DPG linear system
A^T G^-1 A u = A^T G^-1 b
This system results from the minimum residual formulation
u = argmin_w ||G^-1(b - Aw)||.
Here G is a symmetric positive definite matrix resulting from the discretization of
the Riesz operator on the test space. Since the test space is broken
(discontinuous), G is defined and inverted element-wise and the assembly
of the global system is performed in the same manner as the standard FEM method.
Note that DPGWeakForm can handle multiple Finite Element spaces.*/
class DPGWeakForm
{
protected:
BlockStaticCondensation *static_cond; ///< Owned.
bool initialized = false;
Mesh * mesh = nullptr;
int height, width;
int nblocks;
Array<int> dof_offsets;
Array<int> tdof_offsets;
/// Block matrix $ M $ to be associated with the Block bilinear form. Owned.
BlockMatrix *mat = nullptr;
/// Block vector $ y $ to be associated with the Block linear form
BlockVector * y = nullptr;
/** @brief Block Matrix $ M_e $ used to store the eliminations
from the b.c. Owned.
$ M + M_e = M_{original} $ */
BlockMatrix *mat_e = nullptr;
/// Trial FE spaces
Array<FiniteElementSpace * > trial_fes;
/// Flags to determine if a FiniteElementSpace is Trace
Array<int> IsTraceFes;
/// Test FE Collections (Broken)
Array<FiniteElementCollection *> test_fecols;
Array<int> test_fecols_vdims;
/// Set of Trial Integrators to be applied for matrix B.
Array2D<Array<BilinearFormIntegrator * > * > trial_integs;
/// Set of Test Space (broken) Integrators to be applied for matrix G
Array2D<Array<BilinearFormIntegrator * > * > test_integs;
/// Set of Linear Form Integrators to be applied.
Array<Array<LinearFormIntegrator * > * > lfis;
/// Block Prolongation
BlockMatrix * P = nullptr;
/// Block Restriction
BlockMatrix * R = nullptr;
mfem::Operator::DiagonalPolicy diag_policy;
void Init();
void ReleaseInitMemory();
/// Allocate appropriate BlockMatrix and assign it to mat
void AllocMat();
void ConformingAssemble();
void ComputeOffsets();
virtual void BuildProlongation();
bool store_matrices = false;
/** Store the matrix L^-1 B and Vector L^-1 l
where G = L L^t */
Array<DenseMatrix * > Bmat;
Array<Vector * > fvec;
Vector residuals;
public:
/// Default constructor. User must call SetSpaces to setup the FE spaces
DPGWeakForm()
{
height = 0;
width = 0;
}
/// Creates bilinear form associated with FE spaces @a fes_.
DPGWeakForm(Array<FiniteElementSpace* > & fes_,
Array<FiniteElementCollection *> & fecol_)
{
SetSpaces(fes_,fecol_);
}
void SetTestFECollVdim(int test_fec, int vdim)
{
test_fecols_vdims[test_fec] = vdim;
}
void SetSpaces(Array<FiniteElementSpace* > & fes_,
Array<FiniteElementCollection *> & fecol_)
{
trial_fes = fes_;
test_fecols = fecol_;
test_fecols_vdims.SetSize(test_fecols.Size());
test_fecols_vdims = 1;
nblocks = trial_fes.Size();
mesh = trial_fes[0]->GetMesh();
IsTraceFes.SetSize(nblocks);
for (int i = 0; i < nblocks; i++)
{
IsTraceFes[i] =
(dynamic_cast<const H1_Trace_FECollection*>(trial_fes[i]->FEColl()) ||
dynamic_cast<const ND_Trace_FECollection*>(trial_fes[i]->FEColl()) ||
dynamic_cast<const RT_Trace_FECollection*>(trial_fes[i]->FEColl()));
}
Init();
}
/// Get the size of the bilinear form of the DPGWeakForm
int Size() const { return height; }
/// Pre-allocate the internal BlockMatrix before assembly.
void AllocateMatrix() { if (mat == nullptr) { AllocMat(); } }
/// Finalizes the matrix initialization.
void Finalize(int skip_zeros = 1);
/// Returns a reference to the BlockMatrix: $ M $
BlockMatrix &BlockMat()
{
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
return *mat;
}
/// Returns a reference to the sparse matrix of eliminated b.c.: $ M_e $
BlockMatrix &BlockMatElim()
{
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
return *mat_e;
}
/// Adds new Trial Integrator. Assumes ownership of @a bfi.
void AddTrialIntegrator(BilinearFormIntegrator *bfi, int n, int m);
/// Adds new Test Integrator. Assumes ownership of @a bfi.
void AddTestIntegrator(BilinearFormIntegrator *bfi, int n, int m);
/// Adds new Domain LF Integrator. Assumes ownership of @a bfi.
void AddDomainLFIntegrator(LinearFormIntegrator *lfi, int n);
/// Assembles the form i.e. sums over all integrators.
void Assemble(int skip_zeros = 1);
/** @brief Form the linear system A X = B, corresponding to this DPG weak
form */
/** This method applies any necessary transformations to the linear system
such as: eliminating boundary conditions; applying conforming constraints
for non-conforming AMR; static condensation;
The GridFunction-size vector @a x must contain the essential b.c. The
DPGWeakForm must be assembled.
The vector @a X is initialized with a suitable initial guess: the essential
entries of @a X are set to the corresponding b.c. and all other entries
are set to zero (@a copy_interior == 0) or copied from @a x
(@a copy_interior != 0).
After solving the linear system, the finite element solution @a x can be
recovered by calling RecoverFEMSolution() (with the same vectors @a X,
and @a x).
NOTE: If there are no transformations, @a X simply reuses the data of
@a x. */
virtual void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
OperatorHandle &A, Vector &X,
Vector &B, int copy_interior = 0);
/** @brief Form the linear system A X = B, corresponding to this DPG weak form
Version of the method FormLinearSystem() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). */
template <typename OpType>
void FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
OpType &A, Vector &X, Vector &B,
int copy_interior = 0)
{
OperatorHandle Ah;
FormLinearSystem(ess_tdof_list, x, Ah, X, B, copy_interior);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
/// Form the linear system matrix @a A, see FormLinearSystem() for details.
virtual void FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A);
/// Form the linear system matrix A, see FormLinearSystem() for details.
/** Version of the method FormSystemMatrix() where the system matrix is
returned in the variable @a A, of type OpType, holding a *reference* to
the system matrix (created with the method OpType::MakeRef()). */
template <typename OpType>
void FormSystemMatrix(const Array<int> &ess_tdof_list, OpType &A)
{
OperatorHandle Ah;
FormSystemMatrix(ess_tdof_list, Ah);
OpType *A_ptr = Ah.Is<OpType>();
MFEM_VERIFY(A_ptr, "invalid OpType used");
A.MakeRef(*A_ptr);
}
/// Eliminate the given @a vdofs, storing the eliminated part internally in $ M_e $.
/** This method works in conjunction with EliminateVDofsInRHS() and allows
elimination of boundary conditions in multiple right-hand sides. In this
method, @a vdofs is a list of DOFs. */
void EliminateVDofs(const Array<int> &vdofs,
Operator::DiagonalPolicy dpolicy = Operator::DIAG_ONE);
/** @brief Use the stored eliminated part of the matrix (see
EliminateVDofs(const Array<int> &, DiagonalPolicy)) to modify the r.h.s.
@a b; @a vdofs is a list of DOFs. */
void EliminateVDofsInRHS(const Array<int> &vdofs, const Vector &x, Vector &b);
/// Recover the solution of a linear system formed with FormLinearSystem().
/** Call this method after solving a linear system constructed using the
FormLinearSystem() method to recover the solution as a GridFunction-size
vector in @a x. Use the same arguments as in the FormLinearSystem() call.
*/
virtual void RecoverFEMSolution(const Vector &X,Vector &x);
/// Sets diagonal policy used upon construction of the linear system.
/** Policies include:
- DIAG_ZERO (Set the diagonal values to zero)
- DIAG_ONE (Set the diagonal values to one)
- DIAG_KEEP (Keep the diagonal values)
*/
void SetDiagonalPolicy(Operator::DiagonalPolicy policy)
{
diag_policy = policy;
}
/// Update the DPGWeakForm after mesh modifications (AMR)
virtual void Update();
/// Store internal element matrices used for computation of residual after solve
void StoreMatrices(bool store_matrices_ = true)
{
store_matrices = store_matrices_;
if (Bmat.Size() == 0)
{
Bmat.SetSize(mesh->GetNE());
fvec.SetSize(mesh->GetNE());
for (int i =0; i<mesh->GetNE(); i++)
{
Bmat[i] = nullptr;
fvec[i] = nullptr;
}
}
}
void EnableStaticCondensation();
/// Compute DPG residual based error estimator
Vector & ComputeResidual(const BlockVector & x);
virtual ~DPGWeakForm();
};
} // namespace mfem
#endif