Files
mfem/fem/nonlinearform.hpp
T

423 lines
17 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_NONLINEARFORM
#define MFEM_NONLINEARFORM
#include "../config/config.hpp"
#include "nonlininteg.hpp"
#include "nonlinearform_ext.hpp"
#include "bilinearform.hpp"
#include "gridfunc.hpp"
namespace mfem
{
class NonlinearForm : public Operator
{
protected:
/// The assembly level.
AssemblyLevel assembly;
/// Extension for supporting different AssemblyLevel%s
/** For nonlinear operators, the "matrix" assembly levels usually do not make
sense, so only PARTIAL and NONE (matrix-free) are supported. */
NonlinearFormExtension *ext; // owned
/// FE space on which the form lives.
FiniteElementSpace *fes; // not owned
/** @brief Indicates the NonlinearFormIntegrator%s stored in #dnfi, #bnfi,
#fnfi, and #bfnfi are not owned by this NonlinearForm. */
bool extern_bfs;
/// Set of Domain Integrators to be assembled (added).
Array<NonlinearFormIntegrator*> dnfi; // owned
Array<Array<int>*> dnfi_marker; // not owned
/// Set of Boundary Integrators to be assembled (added).
Array<NonlinearFormIntegrator*> bnfi; // owned
Array<Array<int>*> bnfi_marker; // not owned
/// Set of interior face Integrators to be assembled (added).
Array<NonlinearFormIntegrator*> fnfi; // owned
/// Set of boundary face Integrators to be assembled (added).
Array<NonlinearFormIntegrator*> bfnfi; // owned
Array<Array<int>*> bfnfi_marker; // not owned
mutable SparseMatrix *Grad, *cGrad; // owned
/// Gradient Operator when not assembled as a matrix.
mutable OperatorHandle hGrad; // has internal ownership flag
/// A list of all essential true dofs
Array<int> ess_tdof_list;
/// Counter for updates propagated from the FiniteElementSpace.
long sequence;
/// Auxiliary Vector%s
mutable Vector aux1, aux2;
/// Pointer to the prolongation matrix of fes, may be NULL.
const Operator *P; // not owned
/// The result of dynamic-casting P to SparseMatrix pointer.
const SparseMatrix *cP; // not owned
bool Serial() const { return (!P || cP); }
const Vector &Prolongate(const Vector &x) const;
public:
/// Construct a NonlinearForm on the given FiniteElementSpace, @a f.
/** As an Operator, the NonlinearForm has input and output size equal to the
number of true degrees of freedom, i.e. f->GetTrueVSize(). */
NonlinearForm(FiniteElementSpace *f)
: Operator(f->GetTrueVSize()), assembly(AssemblyLevel::LEGACY),
ext(NULL), fes(f), extern_bfs(0), Grad(NULL), cGrad(NULL),
sequence(f->GetSequence()), P(f->GetProlongationMatrix()),
cP(dynamic_cast<const SparseMatrix*>(P))
{ }
/// Set the desired assembly level. The default is AssemblyLevel::LEGACY.
/** For nonlinear operators, the "matrix" assembly levels usually do not make
sense, so only LEGACY, NONE (matrix-free) and PARTIAL are supported.
Currently, AssemblyLevel::LEGACY uses the standard nonlinear action
methods like AssembleElementVector of the NonlinearFormIntegrator class
which work only on CPU and do not utilize features such as fast
tensor-product basis evaluations. In this mode, the gradient operator is
constructed as a SparseMatrix (or, in parallel, format such as
HypreParMatrix).
When using AssemblyLevel::PARTIAL, the action is performed using methods
like AddMultPA of the NonlinearFormIntegrator class which typically
support both CPU and GPU backends and utilize features such as fast
tensor-product basis evaluations. In this mode, the gradient operator
also uses partial assembly with support for CPU and GPU backends.
When using AssemblyLevel::NONE, the action is performed using methods
like AddMultMF of the NonlinearFormIntegrator class which typically
support both CPU and GPU backends and utilize features such as fast
tensor-product basis evaluations. In this mode, the gradient operator
is currently not supported.
This method must be called before "assembly" with Setup(). */
void SetAssemblyLevel(AssemblyLevel assembly_level);
FiniteElementSpace *FESpace() { return fes; }
const FiniteElementSpace *FESpace() const { return fes; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(NonlinearFormIntegrator *nlfi)
{ dnfi.Append(nlfi); dnfi_marker.Append(NULL); }
/// Adds new Domain Integrator, restricted to specific attributes.
void AddDomainIntegrator(NonlinearFormIntegrator *nlfi,
Array<int> &elem_marker)
{ dnfi.Append(nlfi); dnfi_marker.Append(&elem_marker); }
/// Access all integrators added with AddDomainIntegrator().
Array<NonlinearFormIntegrator*> *GetDNFI() { return &dnfi; }
const Array<NonlinearFormIntegrator*> *GetDNFI() const { return &dnfi; }
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(NonlinearFormIntegrator *nlfi)
{ bnfi.Append(nlfi); bnfi_marker.Append(NULL); }
/// Adds new Boundary Integrator, restricted to specific attributes.
void AddBoundaryIntegrator(NonlinearFormIntegrator *nlfi,
Array<int> &elem_marker)
{ bnfi.Append(nlfi); bnfi_marker.Append(&elem_marker); }
/// Access all integrators added with AddBoundaryIntegrator().
Array<NonlinearFormIntegrator*> *GetBNFI() { return &bnfi; }
const Array<NonlinearFormIntegrator*> *GetBNFI() const { return &bnfi; }
/// Adds new Interior Face Integrator.
void AddInteriorFaceIntegrator(NonlinearFormIntegrator *nlfi)
{ fnfi.Append(nlfi); }
/** @brief Access all interior face integrators added with
AddInteriorFaceIntegrator(). */
const Array<NonlinearFormIntegrator*> &GetInteriorFaceIntegrators() const
{ return fnfi; }
/// Adds new Boundary Face Integrator.
void AddBdrFaceIntegrator(NonlinearFormIntegrator *nlfi)
{ bfnfi.Append(nlfi); bfnfi_marker.Append(NULL); }
/** @brief Adds new Boundary Face Integrator, restricted to specific boundary
attributes. */
void AddBdrFaceIntegrator(NonlinearFormIntegrator *nfi,
Array<int> &bdr_marker)
{ bfnfi.Append(nfi); bfnfi_marker.Append(&bdr_marker); }
/** @brief Access all boundary face integrators added with
AddBdrFaceIntegrator(). */
const Array<NonlinearFormIntegrator*> &GetBdrFaceIntegrators() const
{ return bfnfi; }
/// Specify essential boundary conditions.
/** This method calls FiniteElementSpace::GetEssentialTrueDofs() and stores
the result internally for use by other methods. If the @a rhs pointer is
not NULL, its essential true dofs will be set to zero. This makes it
"compatible" with the output vectors from the Mult() method which also
have zero entries at the essential true dofs. */
void SetEssentialBC(const Array<int> &bdr_attr_is_ess, Vector *rhs = NULL);
/// Specify essential boundary conditions.
/** Use either SetEssentialBC() or SetEssentialTrueDofs() if possible. */
void SetEssentialVDofs(const Array<int> &ess_vdofs_list);
/// Specify essential boundary conditions.
void SetEssentialTrueDofs(const Array<int> &ess_tdof_list_)
{ ess_tdof_list_.Copy(this->ess_tdof_list); }
/// Return a (read-only) list of all essential true dofs.
const Array<int> &GetEssentialTrueDofs() const { return ess_tdof_list; }
/// Compute the energy corresponding to the state @a x.
/** In general, @a x may have non-homogeneous essential boundary values.
The state @a x must be a "GridFunction size" vector, i.e. its size must
be fes->GetVSize(). */
real_t GetGridFunctionEnergy(const Vector &x) const;
/// Compute the energy corresponding to the state @a x.
/** In general, @a x may have non-homogeneous essential boundary values.
The state @a x must be a true-dof vector. */
virtual real_t GetEnergy(const Vector &x) const
{ return GetGridFunctionEnergy(Prolongate(x)); }
/// Evaluate the action of the NonlinearForm.
/** The input essential dofs in @a x will, generally, be non-zero. However,
the output essential dofs in @a y will always be set to zero.
Both the input and the output vectors, @a x and @a y, must be true-dof
vectors, i.e. their size must be fes->GetTrueVSize(). */
void Mult(const Vector &x, Vector &y) const override;
/** @brief Compute the gradient Operator of the NonlinearForm corresponding
to the state @a x. */
/** Any previously specified essential boundary conditions will be
automatically imposed on the gradient operator.
The returned object is valid until the next call to this method or the
destruction of this object.
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 { 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
set again. */
virtual void Update();
/** @brief Setup the NonlinearForm: based on the current AssemblyLevel and
the current mesh, optionally, precompute and store data that will be
reused in subsequent call to Mult(). */
/** Typically, this method has to be called before Mult() when using
AssemblyLevel::PARTIAL, after calling Update(), or after modifying the
mesh coordinates. */
virtual void Setup();
/// Get the finite element space prolongation matrix
const Operator *GetProlongation() const override { return P; }
/// Get the finite element space restriction matrix
const Operator *GetRestriction() const override
{ return fes->GetRestrictionMatrix(); }
/// Indicate that integrators are not owned by the NonlinearForm
void UseExternalIntegrators() { extern_bfs = 1; }
/** @brief Destroy the NonlinearForm including the owned
NonlinearFormIntegrator%s and gradient Operator. */
virtual ~NonlinearForm();
};
/** @brief A class representing a general block nonlinear operator defined on
the Cartesian product of multiple FiniteElementSpace%s. */
class BlockNonlinearForm : public Operator
{
protected:
/// FE spaces on which the form lives.
Array<FiniteElementSpace*> fes;
/// Set of Domain Integrators to be assembled (added).
Array<BlockNonlinearFormIntegrator*> dnfi;
Array<Array<int>*> dnfi_marker;
/// Set of Boundary Integrators to be assembled (added).
Array<BlockNonlinearFormIntegrator*> bnfi;
Array<Array<int>*> bnfi_marker;
/// Set of interior face Integrators to be assembled (added).
Array<BlockNonlinearFormIntegrator*> fnfi;
/// Set of Boundary Face Integrators to be assembled (added).
Array<BlockNonlinearFormIntegrator*> bfnfi;
Array<Array<int>*> bfnfi_marker;
/** Auxiliary block-vectors for wrapping input and output vectors or holding
GridFunction-like block-vector data (e.g. in parallel). */
mutable BlockVector xs, ys;
mutable Array2D<SparseMatrix*> Grads, cGrads;
mutable BlockOperator *BlockGrad;
// A list of the offsets
Array<int> block_offsets;
Array<int> block_trueOffsets;
// Array of Arrays of tdofs for each space in 'fes'
Array<Array<int> *> ess_tdofs;
/// Array of pointers to the prolongation matrix of fes, may be NULL
Array<const Operator *> P;
/// Array of results of dynamic-casting P to SparseMatrix pointer
Array<const SparseMatrix *> cP;
/// Indicator if the Operator is part of a parallel run
bool is_serial = true;
/// Indicator if the Operator needs prolongation on assembly
bool needs_prolongation = false;
mutable BlockVector aux1, aux2;
const BlockVector &Prolongate(const BlockVector &bx) const;
/// Specialized version of GetEnergy() for BlockVectors
real_t GetEnergyBlocked(const BlockVector &bx) const;
/// Specialized version of Mult() for BlockVector%s
/// Block L-Vector to Block L-Vector
void MultBlocked(const BlockVector &bx, BlockVector &by) const;
/// Specialized version of GetGradient() for BlockVector
void ComputeGradientBlocked(const BlockVector &bx, bool finalize = true) const;
public:
/// Construct an empty BlockNonlinearForm. Initialize with SetSpaces().
BlockNonlinearForm();
/// Construct a BlockNonlinearForm on the given set of FiniteElementSpace%s.
BlockNonlinearForm(Array<FiniteElementSpace *> &f);
/// Return the @a k-th FE space of the BlockNonlinearForm.
FiniteElementSpace *FESpace(int k) { return fes[k]; }
/// Return the @a k-th FE space of the BlockNonlinearForm (const version).
const FiniteElementSpace *FESpace(int k) const { return fes[k]; }
/// (Re)initialize the BlockNonlinearForm.
/** After a call to SetSpaces(), the essential b.c. must be set again. */
void SetSpaces(Array<FiniteElementSpace *> &f);
/// Return the regular dof offsets.
const Array<int> &GetBlockOffsets() const { return block_offsets; }
/// Return the true-dof offsets.
const Array<int> &GetBlockTrueOffsets() const { return block_trueOffsets; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(BlockNonlinearFormIntegrator *nlfi)
{ dnfi.Append(nlfi); dnfi_marker.Append(NULL); }
/// Adds new Domain Integrator, restricted to specific attributes.
void AddDomainIntegrator(BlockNonlinearFormIntegrator *nlfi,
Array<int> &elem_marker)
{ dnfi.Append(nlfi); dnfi_marker.Append(&elem_marker); }
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(BlockNonlinearFormIntegrator *nlfi)
{ bnfi.Append(nlfi); bnfi_marker.Append(NULL); }
/// Adds new Boundary Integrator, restricted to specific attributes.
void AddBoundaryIntegrator(BlockNonlinearFormIntegrator *nlfi,
Array<int> &elem_marker)
{ bnfi.Append(nlfi); bnfi_marker.Append(&elem_marker); }
/// Adds new Interior Face Integrator.
void AddInteriorFaceIntegrator(BlockNonlinearFormIntegrator *nlfi)
{ fnfi.Append(nlfi); }
/// Adds new Boundary Face Integrator.
void AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nlfi)
{ bfnfi.Append(nlfi); bfnfi_marker.Append(NULL); }
/** @brief Adds new Boundary Face Integrator, restricted to specific boundary
attributes. */
void AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nlfi,
Array<int> &bdr_marker)
{ bfnfi.Append(nlfi); bfnfi_marker.Append(&bdr_marker); }
/** @brief Set essential boundary conditions to each finite element space
using boundary attribute markers.
This method calls `FiniteElementSpace::GetEssentialTrueDofs()` for each
space and stores ess_tdof_lists internally.
If `rhs` vectors are non-null, the entries corresponding to these
essential DoFs are set to zero. This ensures compatibility with the
output of the `Mult()` method, which also zeroes out these entries.
@param[in] bdr_attr_is_ess A list of boundary attribute markers for each
space.
@param[in,out] rhs An array of optional right-hand side vectors.
If a vector at `rhs[i]` is non-null, its essential DoFs will be set
to zero. */
virtual void SetEssentialBC(const Array<Array<int>*> &bdr_attr_is_ess,
Array<Vector*> &rhs);
/** @brief Set essential boundary conditions to each finite element space
using essential true dof lists.
This method stores a copy of the provided essential true dof lists.
If `rhs` vectors are non-null, the entries corresponding to these
essential DoFs are set to zero. This ensures compatibility with the
output of the `Mult()` method, which also zeroes out these entries.
@param[in] ess_tdof_list A list of essential true dofs for each space.
@param[in,out] rhs An array of optional right-hand side vectors.
If a vector at `rhs[i]` is non-null, its essential DoFs will be set
to zero. */
virtual void SetEssentialTrueDofs(const Array<Array<int>*> &ess_tdof_list,
Array<Vector*> &rhs);
virtual real_t GetEnergy(const Vector &x) const;
/// Method is only called in serial, the parallel version calls MultBlocked
/// directly.
void Mult(const Vector &x, Vector &y) const override;
/// Method is only called in serial, the parallel version calls
/// GetGradientBlocked directly.
Operator &GetGradient(const Vector &x) const override;
/// Destructor.
virtual ~BlockNonlinearForm();
};
}
#endif