Files
mfem/fem/bilinearform.cpp
T

1801 lines
49 KiB
C++

// Copyright (c) 2010-2020, 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.
// Implementation of class BilinearForm
#include "fem.hpp"
#include "../general/device.hpp"
#include <cmath>
namespace mfem
{
void BilinearForm::AllocMat()
{
if (static_cond) { return; }
if (precompute_sparsity == 0 || fes->GetVDim() > 1)
{
mat = new SparseMatrix(height);
return;
}
const Table &elem_dof = fes->GetElementToDofTable();
Table dof_dof;
if (fbfi.Size() > 0)
{
// the sparsity pattern is defined from the map: face->element->dof
Table face_dof, dof_face;
{
Table *face_elem = fes->GetMesh()->GetFaceToElementTable();
mfem::Mult(*face_elem, elem_dof, face_dof);
delete face_elem;
}
Transpose(face_dof, dof_face, height);
mfem::Mult(dof_face, face_dof, dof_dof);
}
else
{
// the sparsity pattern is defined from the map: element->dof
Table dof_elem;
Transpose(elem_dof, dof_elem, height);
mfem::Mult(dof_elem, elem_dof, dof_dof);
}
dof_dof.SortRows();
int *I = dof_dof.GetI();
int *J = dof_dof.GetJ();
double *data = Memory<double>(I[height]);
mat = new SparseMatrix(I, J, data, height, height, true, true, true);
*mat = 0.0;
dof_dof.LoseData();
}
BilinearForm::BilinearForm(FiniteElementSpace * f)
: Matrix (f->GetVSize())
{
fes = f;
sequence = f->GetSequence();
mat = mat_e = NULL;
extern_bfs = 0;
element_matrices = NULL;
static_cond = NULL;
hybridization = NULL;
precompute_sparsity = 0;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::FULL;
batch = 1;
ext = NULL;
}
BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
: Matrix (f->GetVSize())
{
fes = f;
sequence = f->GetSequence();
mat_e = NULL;
extern_bfs = 1;
element_matrices = NULL;
static_cond = NULL;
hybridization = NULL;
precompute_sparsity = ps;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::FULL;
batch = 1;
ext = NULL;
// Copy the pointers to the integrators
dbfi = bf->dbfi;
bbfi = bf->bbfi;
bbfi_marker = bf->bbfi_marker;
fbfi = bf->fbfi;
bfbfi = bf->bfbfi;
bfbfi_marker = bf->bfbfi_marker;
AllocMat();
}
void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
{
if (ext)
{
MFEM_ABORT("the assembly level has already been set!");
}
assembly = assembly_level;
switch (assembly)
{
case AssemblyLevel::FULL:
// ext = new FABilinearFormExtension(this);
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
ext = new EABilinearFormExtension(this);
break;
case AssemblyLevel::PARTIAL:
ext = new PABilinearFormExtension(this);
break;
case AssemblyLevel::NONE:
mfem_error("Matrix-free action not supported yet... stay tuned!");
// ext = new MFBilinearFormExtension(this);
break;
default:
mfem_error("Unknown assembly level");
}
}
void BilinearForm::EnableStaticCondensation()
{
delete static_cond;
if (assembly != AssemblyLevel::FULL)
{
static_cond = NULL;
MFEM_WARNING("Static condensation not supported for this assembly level");
return;
}
static_cond = new StaticCondensation(fes);
if (static_cond->ReducesTrueVSize())
{
bool symmetric = false; // TODO
bool block_diagonal = false; // TODO
static_cond->Init(symmetric, block_diagonal);
}
else
{
delete static_cond;
static_cond = NULL;
}
}
void BilinearForm::EnableHybridization(FiniteElementSpace *constr_space,
BilinearFormIntegrator *constr_integ,
const Array<int> &ess_tdof_list)
{
delete hybridization;
if (assembly != AssemblyLevel::FULL)
{
delete constr_integ;
hybridization = NULL;
MFEM_WARNING("Hybridization not supported for this assembly level");
return;
}
hybridization = new Hybridization(fes, constr_space);
hybridization->SetConstraintIntegrator(constr_integ);
hybridization->Init(ess_tdof_list);
}
void BilinearForm::UseSparsity(int *I, int *J, bool isSorted)
{
if (static_cond) { return; }
if (mat)
{
if (mat->Finalized() && mat->GetI() == I && mat->GetJ() == J)
{
return; // mat is already using the given sparsity
}
delete mat;
}
height = width = fes->GetVSize();
mat = new SparseMatrix(I, J, NULL, height, width, false, true, isSorted);
}
void BilinearForm::UseSparsity(SparseMatrix &A)
{
MFEM_ASSERT(A.Height() == fes->GetVSize() && A.Width() == fes->GetVSize(),
"invalid matrix A dimensions: "
<< A.Height() << " x " << A.Width());
MFEM_ASSERT(A.Finalized(), "matrix A must be Finalized");
UseSparsity(A.GetI(), A.GetJ(), A.ColumnsAreSorted());
}
double& BilinearForm::Elem (int i, int j)
{
return mat -> Elem(i,j);
}
const double& BilinearForm::Elem (int i, int j) const
{
return mat -> Elem(i,j);
}
MatrixInverse * BilinearForm::Inverse() const
{
return mat -> Inverse();
}
void BilinearForm::Finalize (int skip_zeros)
{
if (assembly == AssemblyLevel::FULL)
{
if (!static_cond) { mat->Finalize(skip_zeros); }
if (mat_e) { mat_e->Finalize(skip_zeros); }
if (static_cond) { static_cond->Finalize(); }
if (hybridization) { hybridization->Finalize(); }
}
}
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
{
dbfi.Append(bfi);
}
void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
{
bbfi.Append (bfi);
bbfi_marker.Append(NULL); // NULL marker means apply everywhere
}
void BilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker)
{
bbfi.Append (bfi);
bbfi_marker.Append(&bdr_marker);
}
void BilinearForm::AddInteriorFaceIntegrator (BilinearFormIntegrator * bfi)
{
fbfi.Append (bfi);
}
void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi)
{
bfbfi.Append(bfi);
bfbfi_marker.Append(NULL); // NULL marker means apply everywhere
}
void BilinearForm::AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker)
{
bfbfi.Append(bfi);
bfbfi_marker.Append(&bdr_marker);
}
void BilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
{
if (element_matrices)
{
elmat.SetSize(element_matrices->SizeI(), element_matrices->SizeJ());
elmat = element_matrices->GetData(i);
return;
}
if (dbfi.Size())
{
const FiniteElement &fe = *fes->GetFE(i);
ElementTransformation *eltrans = fes->GetElementTransformation(i);
dbfi[0]->AssembleElementMatrix(fe, *eltrans, elmat);
for (int k = 1; k < dbfi.Size(); k++)
{
dbfi[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
elmat += elemmat;
}
}
else
{
fes->GetElementVDofs(i, vdofs);
elmat.SetSize(vdofs.Size());
elmat = 0.0;
}
}
void BilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat)
{
if (bbfi.Size())
{
const FiniteElement &be = *fes->GetBE(i);
ElementTransformation *eltrans = fes->GetBdrElementTransformation(i);
bbfi[0]->AssembleElementMatrix(be, *eltrans, elmat);
for (int k = 1; k < bbfi.Size(); k++)
{
bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat);
elmat += elemmat;
}
}
else
{
fes->GetBdrElementVDofs(i, vdofs);
elmat.SetSize(vdofs.Size());
elmat = 0.0;
}
}
void BilinearForm::AssembleElementMatrix(
int i, const DenseMatrix &elmat, int skip_zeros)
{
AssembleElementMatrix(i, elmat, vdofs, skip_zeros);
}
void BilinearForm::AssembleElementMatrix(
int i, const DenseMatrix &elmat, Array<int> &vdofs, int skip_zeros)
{
fes->GetElementVDofs(i, vdofs);
if (static_cond)
{
static_cond->AssembleMatrix(i, elmat);
}
else
{
if (mat == NULL)
{
AllocMat();
}
mat->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
if (hybridization)
{
hybridization->AssembleMatrix(i, elmat);
}
}
}
void BilinearForm::AssembleBdrElementMatrix(
int i, const DenseMatrix &elmat, int skip_zeros)
{
AssembleBdrElementMatrix(i, elmat, vdofs, skip_zeros);
}
void BilinearForm::AssembleBdrElementMatrix(
int i, const DenseMatrix &elmat, Array<int> &vdofs, int skip_zeros)
{
fes->GetBdrElementVDofs(i, vdofs);
if (static_cond)
{
static_cond->AssembleBdrMatrix(i, elmat);
}
else
{
if (mat == NULL)
{
AllocMat();
}
mat->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
if (hybridization)
{
hybridization->AssembleBdrMatrix(i, elmat);
}
}
}
void BilinearForm::Assemble(int skip_zeros)
{
if (ext)
{
ext->Assemble();
return;
}
ElementTransformation *eltrans;
Mesh *mesh = fes -> GetMesh();
DenseMatrix elmat, *elmat_p;
if (mat == NULL)
{
AllocMat();
}
#ifdef MFEM_USE_LEGACY_OPENMP
int free_element_matrices = 0;
if (!element_matrices)
{
ComputeElementMatrices();
free_element_matrices = 1;
}
#endif
if (dbfi.Size())
{
for (int i = 0; i < fes -> GetNE(); i++)
{
fes->GetElementVDofs(i, vdofs);
if (element_matrices)
{
elmat_p = &(*element_matrices)(i);
}
else
{
const FiniteElement &fe = *fes->GetFE(i);
eltrans = fes->GetElementTransformation(i);
dbfi[0]->AssembleElementMatrix(fe, *eltrans, elmat);
for (int k = 1; k < dbfi.Size(); k++)
{
dbfi[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
elmat += elemmat;
}
elmat_p = &elmat;
}
if (static_cond)
{
static_cond->AssembleMatrix(i, *elmat_p);
}
else
{
mat->AddSubMatrix(vdofs, vdofs, *elmat_p, skip_zeros);
if (hybridization)
{
hybridization->AssembleMatrix(i, *elmat_p);
}
}
}
}
if (bbfi.Size())
{
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bbfi.Size(); k++)
{
if (bbfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bbfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
const FiniteElement &be = *fes->GetBE(i);
fes -> GetBdrElementVDofs (i, vdofs);
eltrans = fes -> GetBdrElementTransformation (i);
int k = 0;
for (; k < bbfi.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
bbfi[k]->AssembleElementMatrix(be, *eltrans, elmat);
k++;
break;
}
for (; k < bbfi.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
bbfi[k]->AssembleElementMatrix(be, *eltrans, elemmat);
elmat += elemmat;
}
if (!static_cond)
{
mat->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
if (hybridization)
{
hybridization->AssembleBdrMatrix(i, elmat);
}
}
else
{
static_cond->AssembleBdrMatrix(i, elmat);
}
}
}
if (fbfi.Size())
{
FaceElementTransformations *tr;
Array<int> vdofs2;
int nfaces = mesh->GetNumFaces();
for (int i = 0; i < nfaces; i++)
{
tr = mesh -> GetInteriorFaceTransformations (i);
if (tr != NULL)
{
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
fes -> GetElementVDofs (tr -> Elem2No, vdofs2);
vdofs.Append (vdofs2);
for (int k = 0; k < fbfi.Size(); k++)
{
fbfi[k] -> AssembleFaceMatrix (*fes -> GetFE (tr -> Elem1No),
*fes -> GetFE (tr -> Elem2No),
*tr, elemmat);
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
}
}
}
}
if (bfbfi.Size())
{
FaceElementTransformations *tr;
const FiniteElement *fe1, *fe2;
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bfbfi.Size(); k++)
{
if (bfbfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bfbfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary face integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
tr = mesh -> GetBdrFaceTransformations (i);
if (tr != NULL)
{
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
fe1 = fes -> GetFE (tr -> Elem1No);
// The fe2 object is really a dummy and not used on the boundaries,
// but we can't dereference a NULL pointer, and we don't want to
// actually make a fake element.
fe2 = fe1;
for (int k = 0; k < bfbfi.Size(); k++)
{
if (bfbfi_marker[k] &&
(*bfbfi_marker[k])[bdr_attr-1] == 0) { continue; }
bfbfi[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr, elemmat);
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
}
}
}
}
#ifdef MFEM_USE_LEGACY_OPENMP
if (free_element_matrices)
{
FreeElementMatrices();
}
#endif
}
void BilinearForm::ConformingAssemble()
{
// Do not remove zero entries to preserve the symmetric structure of the
// matrix which in turn will give rise to symmetric structure in the new
// matrix. This ensures that subsequent calls to EliminateRowCol will work
// correctly.
Finalize(0);
MFEM_ASSERT(mat, "the BilinearForm is not assembled");
const SparseMatrix *P = fes->GetConformingProlongation();
if (!P) { return; } // conforming mesh
SparseMatrix *R = Transpose(*P);
SparseMatrix *RA = mfem::Mult(*R, *mat);
delete mat;
if (mat_e)
{
SparseMatrix *RAe = mfem::Mult(*R, *mat_e);
delete mat_e;
mat_e = RAe;
}
delete R;
mat = mfem::Mult(*RA, *P);
delete RA;
if (mat_e)
{
SparseMatrix *RAeP = mfem::Mult(*mat_e, *P);
delete mat_e;
mat_e = RAeP;
}
height = mat->Height();
width = mat->Width();
}
void BilinearForm::AssembleDiagonal(Vector &diag) const
{
if (ext)
{
MFEM_ASSERT(diag.Size() == fes->GetTrueVSize(),
"Vector for holding diagonal has wrong size!");
const Operator *P = fes->GetProlongationMatrix();
if (!IsIdentityProlongation(P))
{
Vector local_diag(P->Height());
ext->AssembleDiagonal(local_diag);
P->MultTranspose(local_diag, diag);
}
else
{
ext->AssembleDiagonal(diag);
}
}
else
{
MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a "
"matrix and use SparseMatrix::GetDiag?");
}
}
void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
Vector &b, OperatorHandle &A, Vector &X,
Vector &B, int copy_interior)
{
if (ext)
{
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
return;
}
const SparseMatrix *P = fes->GetConformingProlongation();
FormSystemMatrix(ess_tdof_list, A);
// Transform the system and perform the elimination in B, based on the
// essential BC values from x. Restrict the BC part of x in X, and set the
// non-BC part to zero. Since there is no good initial guess for the Lagrange
// multipliers, set X = 0.0 for hybridization.
if (static_cond)
{
// Schur complement reduction to the exposed dofs
static_cond->ReduceSystem(x, b, X, B, copy_interior);
}
else if (!P) // conforming space
{
if (hybridization)
{
// Reduction to the Lagrange multipliers system
EliminateVDofsInRHS(ess_tdof_list, x, b);
hybridization->ReduceRHS(b, B);
X.SetSize(B.Size());
X = 0.0;
}
else
{
// A, X and B point to the same data as mat, x and b
EliminateVDofsInRHS(ess_tdof_list, x, b);
X.NewMemoryAndSize(x.GetMemory(), x.Size(), false);
B.NewMemoryAndSize(b.GetMemory(), b.Size(), false);
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
}
else // non-conforming space
{
if (hybridization)
{
// Reduction to the Lagrange multipliers system
const SparseMatrix *R = fes->GetConformingRestriction();
Vector conf_b(P->Width()), conf_x(P->Width());
P->MultTranspose(b, conf_b);
R->Mult(x, conf_x);
EliminateVDofsInRHS(ess_tdof_list, conf_x, conf_b);
R->MultTranspose(conf_b, b); // store eliminated rhs in b
hybridization->ReduceRHS(conf_b, B);
X.SetSize(B.Size());
X = 0.0;
}
else
{
// Variational restriction with P
const SparseMatrix *R = fes->GetConformingRestriction();
B.SetSize(P->Width());
P->MultTranspose(b, B);
X.SetSize(R->Height());
R->Mult(x, X);
EliminateVDofsInRHS(ess_tdof_list, X, B);
if (!copy_interior) { X.SetSubVectorComplement(ess_tdof_list, 0.0); }
}
}
}
void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
OperatorHandle &A)
{
if (ext)
{
ext->FormSystemMatrix(ess_tdof_list, A);
return;
}
// Finish the matrix assembly and perform BC elimination, storing the
// eliminated part of the matrix.
if (static_cond)
{
if (!static_cond->HasEliminatedBC())
{
static_cond->SetEssentialTrueDofs(ess_tdof_list);
static_cond->Finalize(); // finalize Schur complement (to true dofs)
static_cond->EliminateReducedTrueDofs(diag_policy);
static_cond->Finalize(); // finalize eliminated part
}
A.Reset(&static_cond->GetMatrix(), false);
}
else
{
if (!mat_e)
{
const SparseMatrix *P = fes->GetConformingProlongation();
if (P) { ConformingAssemble(); }
EliminateVDofs(ess_tdof_list, diag_policy);
const int remove_zeros = 0;
Finalize(remove_zeros);
}
if (hybridization)
{
A.Reset(&hybridization->GetMatrix(), false);
}
else
{
A.Reset(mat, false);
}
}
}
void BilinearForm::RecoverFEMSolution(const Vector &X,
const Vector &b, Vector &x)
{
if (ext)
{
ext->RecoverFEMSolution(X, b, x);
return;
}
const SparseMatrix *P = fes->GetConformingProlongation();
if (!P) // conforming space
{
if (static_cond)
{
// Private dofs back solve
static_cond->ComputeSolution(b, X, x);
}
else if (hybridization)
{
// Primal unknowns recovery
hybridization->ComputeSolution(b, X, x);
}
else
{
// X and x point to the same data
// If the validity flags of X's Memory were changed (e.g. if it was
// moved to device memory) then we need to tell x about that.
x.SyncMemory(X);
}
}
else // non-conforming space
{
if (static_cond)
{
// Private dofs back solve
static_cond->ComputeSolution(b, X, x);
}
else if (hybridization)
{
// Primal unknowns recovery
Vector conf_b(P->Width()), conf_x(P->Width());
P->MultTranspose(b, conf_b);
const SparseMatrix *R = fes->GetConformingRestriction();
R->Mult(x, conf_x); // get essential b.c. from x
hybridization->ComputeSolution(conf_b, X, conf_x);
x.SetSize(P->Height());
P->Mult(conf_x, x);
}
else
{
// Apply conforming prolongation
x.SetSize(P->Height());
P->Mult(X, x);
}
}
}
void BilinearForm::ComputeElementMatrices()
{
if (element_matrices || dbfi.Size() == 0 || fes->GetNE() == 0)
{
return;
}
int num_elements = fes->GetNE();
int num_dofs_per_el = fes->GetFE(0)->GetDof() * fes->GetVDim();
element_matrices = new DenseTensor(num_dofs_per_el, num_dofs_per_el,
num_elements);
DenseMatrix tmp;
IsoparametricTransformation eltrans;
#ifdef MFEM_USE_LEGACY_OPENMP
#pragma omp parallel for private(tmp,eltrans)
#endif
for (int i = 0; i < num_elements; i++)
{
DenseMatrix elmat(element_matrices->GetData(i),
num_dofs_per_el, num_dofs_per_el);
const FiniteElement &fe = *fes->GetFE(i);
#ifdef MFEM_DEBUG
if (num_dofs_per_el != fe.GetDof()*fes->GetVDim())
mfem_error("BilinearForm::ComputeElementMatrices:"
" all elements must have same number of dofs");
#endif
fes->GetElementTransformation(i, &eltrans);
dbfi[0]->AssembleElementMatrix(fe, eltrans, elmat);
for (int k = 1; k < dbfi.Size(); k++)
{
// note: some integrators may not be thread-safe
dbfi[k]->AssembleElementMatrix(fe, eltrans, tmp);
elmat += tmp;
}
elmat.ClearExternalData();
}
}
void BilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
{
Array<int> ess_dofs, conf_ess_dofs;
fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
if (fes->GetVSize() == height)
{
EliminateEssentialBCFromDofs(ess_dofs, sol, rhs, dpolicy);
}
else
{
fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
EliminateEssentialBCFromDofs(conf_ess_dofs, sol, rhs, dpolicy);
}
}
void BilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
DiagonalPolicy dpolicy)
{
Array<int> ess_dofs, conf_ess_dofs;
fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
if (fes->GetVSize() == height)
{
EliminateEssentialBCFromDofs(ess_dofs, dpolicy);
}
else
{
fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
EliminateEssentialBCFromDofs(conf_ess_dofs, dpolicy);
}
}
void BilinearForm::EliminateEssentialBCDiag (const Array<int> &bdr_attr_is_ess,
double value)
{
Array<int> ess_dofs, conf_ess_dofs;
fes->GetEssentialVDofs(bdr_attr_is_ess, ess_dofs);
if (fes->GetVSize() == height)
{
EliminateEssentialBCFromDofsDiag(ess_dofs, value);
}
else
{
fes->GetRestrictionMatrix()->BooleanMult(ess_dofs, conf_ess_dofs);
EliminateEssentialBCFromDofsDiag(conf_ess_dofs, value);
}
}
void BilinearForm::EliminateVDofs(const Array<int> &vdofs,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
{
for (int i = 0; i < vdofs.Size(); i++)
{
int vdof = vdofs[i];
if ( vdof >= 0 )
{
mat -> EliminateRowCol (vdof, sol(vdof), rhs, dpolicy);
}
else
{
mat -> EliminateRowCol (-1-vdof, sol(-1-vdof), rhs, dpolicy);
}
}
}
void BilinearForm::EliminateVDofs(const Array<int> &vdofs,
DiagonalPolicy dpolicy)
{
if (mat_e == NULL)
{
mat_e = new SparseMatrix(height);
}
for (int i = 0; i < vdofs.Size(); i++)
{
int vdof = vdofs[i];
if ( vdof >= 0 )
{
mat -> EliminateRowCol (vdof, *mat_e, dpolicy);
}
else
{
mat -> EliminateRowCol (-1-vdof, *mat_e, dpolicy);
}
}
}
void BilinearForm::EliminateEssentialBCFromDofs(
const Array<int> &ess_dofs, const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
{
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
MFEM_ASSERT(sol.Size() == height, "incorrect sol Vector size");
MFEM_ASSERT(rhs.Size() == height, "incorrect rhs Vector size");
for (int i = 0; i < ess_dofs.Size(); i++)
if (ess_dofs[i] < 0)
{
mat -> EliminateRowCol (i, sol(i), rhs, dpolicy);
}
}
void BilinearForm::EliminateEssentialBCFromDofs (const Array<int> &ess_dofs,
DiagonalPolicy dpolicy)
{
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
for (int i = 0; i < ess_dofs.Size(); i++)
if (ess_dofs[i] < 0)
{
mat -> EliminateRowCol (i, dpolicy);
}
}
void BilinearForm::EliminateEssentialBCFromDofsDiag (const Array<int> &ess_dofs,
double value)
{
MFEM_ASSERT(ess_dofs.Size() == height, "incorrect dof Array size");
for (int i = 0; i < ess_dofs.Size(); i++)
if (ess_dofs[i] < 0)
{
mat -> EliminateRowColDiag (i, value);
}
}
void BilinearForm::EliminateVDofsInRHS(
const Array<int> &vdofs, const Vector &x, Vector &b)
{
mat_e->AddMult(x, b, -1.);
mat->PartMult(vdofs, x, b);
}
void BilinearForm::Mult(const Vector &x, Vector &y) const
{
if (ext)
{
ext->Mult(x, y);
}
else
{
mat->Mult(x, y);
}
}
void BilinearForm::Update(FiniteElementSpace *nfes)
{
bool full_update;
if (nfes && nfes != fes)
{
full_update = true;
fes = nfes;
}
else
{
// Check for different size (e.g. assembled form on non-conforming space)
// or different sequence number.
full_update = (fes->GetVSize() != Height() ||
sequence < fes->GetSequence());
}
delete mat_e;
mat_e = NULL;
FreeElementMatrices();
delete static_cond;
static_cond = NULL;
if (full_update)
{
delete mat;
mat = NULL;
delete hybridization;
hybridization = NULL;
sequence = fes->GetSequence();
}
else
{
if (mat) { *mat = 0.0; }
if (hybridization) { hybridization->Reset(); }
}
height = width = fes->GetVSize();
if (ext) { ext->Update(); }
}
void BilinearForm::SetDiagonalPolicy(DiagonalPolicy policy)
{
diag_policy = policy;
}
BilinearForm::~BilinearForm()
{
delete mat_e;
delete mat;
delete element_matrices;
delete static_cond;
delete hybridization;
if (!extern_bfs)
{
int k;
for (k=0; k < dbfi.Size(); k++) { delete dbfi[k]; }
for (k=0; k < bbfi.Size(); k++) { delete bbfi[k]; }
for (k=0; k < fbfi.Size(); k++) { delete fbfi[k]; }
for (k=0; k < bfbfi.Size(); k++) { delete bfbfi[k]; }
}
delete ext;
}
MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
FiniteElementSpace *te_fes)
: Matrix(te_fes->GetVSize(), tr_fes->GetVSize())
{
trial_fes = tr_fes;
test_fes = te_fes;
mat = NULL;
mat_e = NULL;
extern_bfs = 0;
assembly = AssemblyLevel::FULL;
ext = NULL;
}
MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
FiniteElementSpace *te_fes,
MixedBilinearForm * mbf)
: Matrix(te_fes->GetVSize(), tr_fes->GetVSize())
{
trial_fes = tr_fes;
test_fes = te_fes;
mat = NULL;
mat_e = NULL;
extern_bfs = 1;
ext = NULL;
// Copy the pointers to the integrators
dbfi = mbf->dbfi;
bbfi = mbf->bbfi;
tfbfi = mbf->tfbfi;
btfbfi = mbf->btfbfi;
bbfi_marker = mbf->bbfi_marker;
btfbfi_marker = mbf->btfbfi_marker;
assembly = AssemblyLevel::FULL;
ext = NULL;
}
void MixedBilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
{
if (ext)
{
MFEM_ABORT("the assembly level has already been set!");
}
assembly = assembly_level;
switch (assembly)
{
case AssemblyLevel::FULL:
// ext = new FAMixedBilinearFormExtension(this);
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
mfem_error("Element assembly not supported yet... stay tuned!");
// ext = new EAMixedBilinearFormExtension(this);
break;
case AssemblyLevel::PARTIAL:
ext = new PAMixedBilinearFormExtension(this);
break;
case AssemblyLevel::NONE:
mfem_error("Matrix-free action not supported yet... stay tuned!");
// ext = new MFMixedBilinearFormExtension(this);
break;
default:
mfem_error("Unknown assembly level");
}
}
double & MixedBilinearForm::Elem (int i, int j)
{
return (*mat)(i, j);
}
const double & MixedBilinearForm::Elem (int i, int j) const
{
return (*mat)(i, j);
}
void MixedBilinearForm::Mult(const Vector & x, Vector & y) const
{
y = 0.0;
AddMult(x, y);
}
void MixedBilinearForm::AddMult(const Vector & x, Vector & y,
const double a) const
{
if (ext)
{
ext->AddMult(x, y, a);
}
else
{
mat->AddMult(x, y, a);
}
}
void MixedBilinearForm::MultTranspose(const Vector & x, Vector & y) const
{
y = 0.0;
AddMultTranspose(x, y);
}
void MixedBilinearForm::AddMultTranspose(const Vector & x, Vector & y,
const double a) const
{
if (ext)
{
ext->AddMultTranspose(x, y, a);
}
else
{
mat->AddMultTranspose(x, y, a);
}
}
MatrixInverse * MixedBilinearForm::Inverse() const
{
if (assembly != AssemblyLevel::FULL)
{
MFEM_WARNING("MixedBilinearForm::Inverse not possible with this assembly level!");
return NULL;
}
else
{
return mat -> Inverse ();
}
}
void MixedBilinearForm::Finalize (int skip_zeros)
{
if (assembly == AssemblyLevel::FULL)
{
mat -> Finalize (skip_zeros);
}
}
void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
{
MFEM_VERIFY(trial_fes->GetOrdering() == Ordering::byNODES &&
test_fes->GetOrdering() == Ordering::byNODES,
"MixedBilinearForm::GetBlocks: both trial and test spaces "
"must use Ordering::byNODES!");
blocks.SetSize(test_fes->GetVDim(), trial_fes->GetVDim());
mat->GetBlocks(blocks);
}
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
{
dbfi.Append (bfi);
}
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
{
bbfi.Append (bfi);
bbfi_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker)
{
bbfi.Append (bfi);
bbfi_marker.Append(&bdr_marker);
}
void MixedBilinearForm::AddTraceFaceIntegrator (BilinearFormIntegrator * bfi)
{
tfbfi.Append (bfi);
}
void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi)
{
btfbfi.Append(bfi);
btfbfi_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi,
Array<int> &bdr_marker)
{
btfbfi.Append(bfi);
btfbfi_marker.Append(&bdr_marker);
}
void MixedBilinearForm::Assemble (int skip_zeros)
{
if (ext)
{
ext->Assemble();
return;
}
Array<int> tr_vdofs, te_vdofs;
ElementTransformation *eltrans;
DenseMatrix elemmat;
Mesh *mesh = test_fes -> GetMesh();
if (mat == NULL)
{
mat = new SparseMatrix(height, width);
}
if (dbfi.Size())
{
for (int i = 0; i < test_fes -> GetNE(); i++)
{
trial_fes -> GetElementVDofs (i, tr_vdofs);
test_fes -> GetElementVDofs (i, te_vdofs);
eltrans = test_fes -> GetElementTransformation (i);
for (int k = 0; k < dbfi.Size(); k++)
{
dbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
*test_fes -> GetFE(i),
*eltrans, elemmat);
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (bbfi.Size())
{
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < bbfi.Size(); k++)
{
if (bbfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *bbfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < test_fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
trial_fes -> GetBdrElementVDofs (i, tr_vdofs);
test_fes -> GetBdrElementVDofs (i, te_vdofs);
eltrans = test_fes -> GetBdrElementTransformation (i);
for (int k = 0; k < bbfi.Size(); k++)
{
if (bbfi_marker[k] &&
(*bbfi_marker[k])[bdr_attr-1] == 0) { continue; }
bbfi[k] -> AssembleElementMatrix2 (*trial_fes -> GetBE(i),
*test_fes -> GetBE(i),
*eltrans, elemmat);
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (tfbfi.Size())
{
FaceElementTransformations *ftr;
Array<int> te_vdofs2;
const FiniteElement *trial_face_fe, *test_fe1, *test_fe2;
int nfaces = mesh->GetNumFaces();
for (int i = 0; i < nfaces; i++)
{
ftr = mesh->GetFaceElementTransformations(i);
trial_fes->GetFaceVDofs(i, tr_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs);
trial_face_fe = trial_fes->GetFaceElement(i);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
if (ftr->Elem2No >= 0)
{
test_fes->GetElementVDofs(ftr->Elem2No, te_vdofs2);
te_vdofs.Append(te_vdofs2);
test_fe2 = test_fes->GetFE(ftr->Elem2No);
}
else
{
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
// want to actually make a fake element.
test_fe2 = test_fe1;
}
for (int k = 0; k < tfbfi.Size(); k++)
{
tfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
*ftr, elemmat);
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
if (btfbfi.Size())
{
FaceElementTransformations *ftr;
Array<int> te_vdofs2;
const FiniteElement *trial_face_fe, *test_fe1, *test_fe2;
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
bdr_attr_marker = 0;
for (int k = 0; k < btfbfi.Size(); k++)
{
if (btfbfi_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *btfbfi_marker[k];
MFEM_ASSERT(bdr_marker.Size() == bdr_attr_marker.Size(),
"invalid boundary marker for boundary trace face integrator #"
<< k << ", counting from zero");
for (int i = 0; i < bdr_attr_marker.Size(); i++)
{
bdr_attr_marker[i] |= bdr_marker[i];
}
}
for (int i = 0; i < trial_fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
ftr = mesh->GetBdrFaceTransformations(i);
if (ftr)
{
trial_fes->GetFaceVDofs(i, tr_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs);
trial_face_fe = trial_fes->GetFaceElement(i);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
// The test_fe2 object is really a dummy and not used on the
// boundaries, but we can't dereference a NULL pointer, and we don't
// want to actually make a fake element.
test_fe2 = test_fe1;
for (int k = 0; k < btfbfi.Size(); k++)
{
if (btfbfi_marker[k] &&
(*btfbfi_marker[k])[bdr_attr-1] == 0) { continue; }
btfbfi[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1, *test_fe2,
*ftr, elemmat);
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
}
}
}
}
}
void MixedBilinearForm::AssembleDiagonal_ADAt(const Vector &D,
Vector &diag) const
{
if (ext)
{
MFEM_ASSERT(diag.Size() == test_fes->GetTrueVSize(),
"Vector for holding diagonal has wrong size!");
MFEM_ASSERT(D.Size() == trial_fes->GetTrueVSize(),
"Vector for holding diagonal has wrong size!");
const Operator *P_trial = trial_fes->GetProlongationMatrix();
const Operator *P_test = test_fes->GetProlongationMatrix();
if (!IsIdentityProlongation(P_trial))
{
Vector local_D(P_trial->Height());
P_trial->Mult(D, local_D);
if (!IsIdentityProlongation(P_test))
{
Vector local_diag(P_test->Height());
ext->AssembleDiagonal_ADAt(local_D, local_diag);
P_test->MultTranspose(local_diag, diag);
}
else
{
ext->AssembleDiagonal_ADAt(local_D, diag);
}
}
else
{
if (!IsIdentityProlongation(P_test))
{
Vector local_diag(P_test->Height());
ext->AssembleDiagonal_ADAt(D, local_diag);
P_test->MultTranspose(local_diag, diag);
}
else
{
ext->AssembleDiagonal_ADAt(D, diag);
}
}
}
else
{
MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a "
"matrix and use SparseMatrix functions?");
}
}
void MixedBilinearForm::ConformingAssemble()
{
if (assembly != AssemblyLevel::FULL)
{
MFEM_WARNING("Conforming assemble not supported for this assembly level!");
return;
}
Finalize();
const SparseMatrix *P2 = test_fes->GetConformingProlongation();
if (P2)
{
SparseMatrix *R = Transpose(*P2);
SparseMatrix *RA = mfem::Mult(*R, *mat);
delete R;
delete mat;
mat = RA;
}
const SparseMatrix *P1 = trial_fes->GetConformingProlongation();
if (P1)
{
SparseMatrix *RAP = mfem::Mult(*mat, *P1);
delete mat;
mat = RAP;
}
height = mat->Height();
width = mat->Width();
}
void MixedBilinearForm::ComputeElementMatrix(int i, DenseMatrix &elmat)
{
if (dbfi.Size())
{
const FiniteElement &trial_fe = *trial_fes->GetFE(i);
const FiniteElement &test_fe = *test_fes->GetFE(i);
ElementTransformation *eltrans = test_fes->GetElementTransformation(i);
dbfi[0]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elmat);
for (int k = 1; k < dbfi.Size(); k++)
{
dbfi[k]->AssembleElementMatrix2(trial_fe, test_fe, *eltrans, elemmat);
elmat += elemmat;
}
}
else
{
trial_fes->GetElementVDofs(i, trial_vdofs);
test_fes->GetElementVDofs(i, test_vdofs);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
elmat = 0.0;
}
}
void MixedBilinearForm::ComputeBdrElementMatrix(int i, DenseMatrix &elmat)
{
if (bbfi.Size())
{
const FiniteElement &trial_be = *trial_fes->GetBE(i);
const FiniteElement &test_be = *test_fes->GetBE(i);
ElementTransformation *eltrans = test_fes->GetBdrElementTransformation(i);
bbfi[0]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elmat);
for (int k = 1; k < bbfi.Size(); k++)
{
bbfi[k]->AssembleElementMatrix2(trial_be, test_be, *eltrans, elemmat);
elmat += elemmat;
}
}
else
{
trial_fes->GetBdrElementVDofs(i, trial_vdofs);
test_fes->GetBdrElementVDofs(i, test_vdofs);
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
elmat = 0.0;
}
}
void MixedBilinearForm::AssembleElementMatrix(
int i, const DenseMatrix &elmat, int skip_zeros)
{
AssembleElementMatrix(i, elmat, trial_vdofs, test_vdofs, skip_zeros);
}
void MixedBilinearForm::AssembleElementMatrix(
int i, const DenseMatrix &elmat, Array<int> &trial_vdofs,
Array<int> &test_vdofs, int skip_zeros)
{
trial_fes->GetElementVDofs(i, trial_vdofs);
test_fes->GetElementVDofs(i, test_vdofs);
if (mat == NULL)
{
mat = new SparseMatrix(height, width);
}
mat->AddSubMatrix(test_vdofs, trial_vdofs, elmat, skip_zeros);
}
void MixedBilinearForm::AssembleBdrElementMatrix(
int i, const DenseMatrix &elmat, int skip_zeros)
{
AssembleBdrElementMatrix(i, elmat, trial_vdofs, test_vdofs, skip_zeros);
}
void MixedBilinearForm::AssembleBdrElementMatrix(
int i, const DenseMatrix &elmat, Array<int> &trial_vdofs,
Array<int> &test_vdofs, int skip_zeros)
{
trial_fes->GetBdrElementVDofs(i, trial_vdofs);
test_fes->GetBdrElementVDofs(i, test_vdofs);
if (mat == NULL)
{
mat = new SparseMatrix(height, width);
}
mat->AddSubMatrix(test_vdofs, trial_vdofs, elmat, skip_zeros);
}
void MixedBilinearForm::EliminateTrialDofs (
const Array<int> &bdr_attr_is_ess, const Vector &sol, Vector &rhs )
{
int i, j, k;
Array<int> tr_vdofs, cols_marker (trial_fes -> GetVSize());
cols_marker = 0;
for (i = 0; i < trial_fes -> GetNBE(); i++)
if (bdr_attr_is_ess[trial_fes -> GetBdrAttribute (i)-1])
{
trial_fes -> GetBdrElementVDofs (i, tr_vdofs);
for (j = 0; j < tr_vdofs.Size(); j++)
{
if ( (k = tr_vdofs[j]) < 0 )
{
k = -1-k;
}
cols_marker[k] = 1;
}
}
mat -> EliminateCols (cols_marker, &sol, &rhs);
}
void MixedBilinearForm::EliminateEssentialBCFromTrialDofs (
const Array<int> &marked_vdofs, const Vector &sol, Vector &rhs)
{
mat -> EliminateCols (marked_vdofs, &sol, &rhs);
}
void MixedBilinearForm::EliminateTestDofs (const Array<int> &bdr_attr_is_ess)
{
int i, j, k;
Array<int> te_vdofs;
for (i = 0; i < test_fes -> GetNBE(); i++)
if (bdr_attr_is_ess[test_fes -> GetBdrAttribute (i)-1])
{
test_fes -> GetBdrElementVDofs (i, te_vdofs);
for (j = 0; j < te_vdofs.Size(); j++)
{
if ( (k = te_vdofs[j]) < 0 )
{
k = -1-k;
}
mat -> EliminateRow (k);
}
}
}
void MixedBilinearForm::FormRectangularSystemMatrix(const Array<int>
&trial_tdof_list,
const Array<int> &test_tdof_list,
OperatorHandle &A)
{
if (ext)
{
ext->FormRectangularSystemOperator(trial_tdof_list, test_tdof_list, A);
return;
}
const SparseMatrix *test_P = test_fes->GetConformingProlongation();
const SparseMatrix *trial_P = trial_fes->GetConformingProlongation();
mat->Finalize();
if (test_P) // TODO: Must actually check for trial_P too
{
SparseMatrix *m = RAP(*test_P, *mat, *trial_P);
delete mat;
mat = m;
}
Array<int> ess_trial_tdof_marker, ess_test_tdof_marker;
FiniteElementSpace::ListToMarker(trial_tdof_list, trial_fes->GetTrueVSize(),
ess_trial_tdof_marker);
FiniteElementSpace::ListToMarker(test_tdof_list, test_fes->GetTrueVSize(),
ess_test_tdof_marker);
mat_e = new SparseMatrix(mat->Height(), mat->Width());
mat->EliminateCols(ess_trial_tdof_marker, *mat_e);
for (int i=0; i<test_tdof_list.Size(); ++i)
{
mat->EliminateRow(test_tdof_list[i]);
}
mat_e->Finalize();
A.Reset(mat, false);
}
void MixedBilinearForm::FormRectangularLinearSystem(const Array<int>
&trial_tdof_list,
const Array<int> &test_tdof_list,
Vector &x, Vector &b,
OperatorHandle &A,
Vector &X, Vector &B)
{
if (ext)
{
ext->FormRectangularLinearSystem(trial_tdof_list, test_tdof_list, x, b, A, X,
B);
return;
}
const Operator *Pi = this->GetProlongation();
const Operator *Po = this->GetOutputProlongation();
const Operator *Ri = this->GetRestriction();
InitTVectors(Po, Ri, Pi, x, b, X, B);
if (!mat_e)
{
FormRectangularSystemMatrix(trial_tdof_list, test_tdof_list,
A); // Set A = mat_e
}
// Eliminate essential BCs with B -= Ab xb
mat_e->AddMult(X, B, -1.0);
B.SetSubVector(test_tdof_list, 0.0);
}
void MixedBilinearForm::Update()
{
delete mat;
mat = NULL;
delete mat_e;
mat_e = NULL;
height = test_fes->GetVSize();
width = trial_fes->GetVSize();
if (ext) { ext->Update(); }
}
MixedBilinearForm::~MixedBilinearForm()
{
if (mat) { delete mat; }
if (mat_e) { delete mat_e; }
if (!extern_bfs)
{
int i;
for (i = 0; i < dbfi.Size(); i++) { delete dbfi[i]; }
for (i = 0; i < bbfi.Size(); i++) { delete bbfi[i]; }
for (i = 0; i < tfbfi.Size(); i++) { delete tfbfi[i]; }
for (i = 0; i < btfbfi.Size(); i++) { delete btfbfi[i]; }
}
delete ext;
}
void DiscreteLinearOperator::Assemble(int skip_zeros)
{
Array<int> dom_vdofs, ran_vdofs;
ElementTransformation *T;
const FiniteElement *dom_fe, *ran_fe;
DenseMatrix totelmat, elmat;
if (mat == NULL)
{
mat = new SparseMatrix(height, width);
}
if (dbfi.Size() > 0)
{
for (int i = 0; i < test_fes->GetNE(); i++)
{
trial_fes->GetElementVDofs(i, dom_vdofs);
test_fes->GetElementVDofs(i, ran_vdofs);
T = test_fes->GetElementTransformation(i);
dom_fe = trial_fes->GetFE(i);
ran_fe = test_fes->GetFE(i);
dbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
for (int j = 1; j < dbfi.Size(); j++)
{
dbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
totelmat += elmat;
}
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
}
}
if (tfbfi.Size())
{
const int nfaces = test_fes->GetMesh()->GetNumFaces();
for (int i = 0; i < nfaces; i++)
{
trial_fes->GetFaceVDofs(i, dom_vdofs);
test_fes->GetFaceVDofs(i, ran_vdofs);
T = test_fes->GetMesh()->GetFaceTransformation(i);
dom_fe = trial_fes->GetFaceElement(i);
ran_fe = test_fes->GetFaceElement(i);
tfbfi[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, totelmat);
for (int j = 1; j < tfbfi.Size(); j++)
{
tfbfi[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T, elmat);
totelmat += elmat;
}
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
}
}
}
}