Files
mfem/examples/darcyhybridization.cpp
T

2521 lines
71 KiB
C++

// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "darcyhybridization.hpp"
#define MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
#define MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
namespace mfem
{
DarcyHybridization::DarcyHybridization(FiniteElementSpace *fes_u_,
FiniteElementSpace *fes_p_,
FiniteElementSpace *fes_c_,
bool bsymmetrize)
: Hybridization(fes_u_, fes_c_), Operator(c_fes->GetVSize()),
fes_p(fes_p_), bsym(bsymmetrize)
{
c_bfi_p = NULL;
c_nlfi_p = NULL;
m_nlfi_u = NULL;
m_nlfi_p = NULL;
m_nlfi = NULL;
own_m_nlfi_u = false;
own_m_nlfi_p = false;
bfin = false;
bnl = false;
SetLocalNLSolver(LSsolveType::LBFGS);
SetLocalNLPreconditioner(LPrecType::GMRES);
Ae_data = NULL;
Bf_data = NULL;
Be_data = NULL;
Df_data = NULL;
Df_lin_data = NULL;
Df_ipiv = NULL;
D_empty = true;
Ct_data = NULL;
E_data = NULL;
G_data = NULL;
H_data = NULL;
}
DarcyHybridization::~DarcyHybridization()
{
delete c_bfi_p;
delete c_nlfi_p;
if (own_m_nlfi_u) { delete m_nlfi_u; }
if (own_m_nlfi_p) { delete m_nlfi_p; }
if (own_m_nlfi) { delete m_nlfi; }
if (!extern_bdr_constr_pot_integs)
{
for (int k=0; k < boundary_constraint_pot_integs.Size(); k++)
{ delete boundary_constraint_pot_integs[k]; }
for (int k=0; k < boundary_constraint_pot_nonlin_integs.Size(); k++)
{ delete boundary_constraint_pot_nonlin_integs[k]; }
}
delete[] Ae_data;
delete[] Bf_data;
delete[] Be_data;
delete[] Df_data;
delete[] Df_lin_data;
delete[] Df_ipiv;
delete[] Ct_data;
delete[] E_data;
delete[] G_data;
delete[] H_data;
}
void DarcyHybridization::SetConstraintIntegrators(
BilinearFormIntegrator *c_flux_integ, BilinearFormIntegrator *c_pot_integ)
{
MFEM_VERIFY(!m_nlfi_p, "Linear constraint cannot work with a non-linear mass");
delete c_bfi;
c_bfi = c_flux_integ;
delete c_bfi_p;
c_bfi_p = c_pot_integ;
delete c_nlfi_p;
c_nlfi_p = NULL;
bnl = false;
}
void DarcyHybridization::SetConstraintIntegrators(
BilinearFormIntegrator *c_flux_integ, NonlinearFormIntegrator *c_pot_integ)
{
delete c_bfi;
c_bfi = c_flux_integ;
delete c_bfi_p;
c_bfi_p = NULL;
delete c_nlfi_p;
c_nlfi_p = c_pot_integ;
bnl = true;
}
void DarcyHybridization::SetFluxMassNonlinearIntegrator(
NonlinearFormIntegrator *flux_integ, bool own)
{
if (own_m_nlfi_u) { delete m_nlfi_u; }
own_m_nlfi_u = own;
m_nlfi_u = flux_integ;
bnl = true;
}
void DarcyHybridization::SetPotMassNonlinearIntegrator(NonlinearFormIntegrator
*pot_integ, bool own)
{
MFEM_VERIFY(!c_bfi_p, "Non-linear mass cannot work with a linear constraint");
if (own_m_nlfi_p) { delete m_nlfi_p; }
own_m_nlfi_p = own;
m_nlfi_p = pot_integ;
bnl = true;
}
void DarcyHybridization::SetBlockNonlinearIntegrator(
BlockNonlinearFormIntegrator *block_integ, bool own)
{
if (own_m_nlfi) { delete m_nlfi; }
own_m_nlfi = own;
m_nlfi = block_integ;
bnl = true;
}
void DarcyHybridization::Init(const Array<int> &ess_flux_tdof_list)
{
const int NE = fes->GetNE();
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
if (Ct_data) { return; }
// count the number of dofs in the discontinuous version of fes:
Array<int> vdofs;
int num_hat_dofs = 0;
hat_offsets.SetSize(NE+1);
hat_offsets[0] = 0;
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, vdofs);
num_hat_dofs += vdofs.Size();
hat_offsets[i+1] = num_hat_dofs;
}
// Define the "free" (0) and "essential" (1) hat_dofs.
// The "essential" hat_dofs are those that depend only on essential cdofs;
// all other hat_dofs are "free".
hat_dofs_marker.SetSize(num_hat_dofs);
Array<int> free_tdof_marker;
#ifdef MFEM_USE_MPI
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace*>(fes);
free_tdof_marker.SetSize(pfes ? pfes->TrueVSize() :
fes->GetConformingVSize());
#else
free_tdof_marker.SetSize(fes->GetConformingVSize());
#endif
free_tdof_marker = 1;
for (int i = 0; i < ess_flux_tdof_list.Size(); i++)
{
free_tdof_marker[ess_flux_tdof_list[i]] = 0;
}
Array<int> free_vdofs_marker;
#ifdef MFEM_USE_MPI
if (!pfes)
{
const SparseMatrix *cP = fes->GetConformingProlongation();
if (!cP)
{
free_vdofs_marker.MakeRef(free_tdof_marker);
}
else
{
free_vdofs_marker.SetSize(fes->GetVSize());
cP->BooleanMult(free_tdof_marker, free_vdofs_marker);
}
}
else
{
HypreParMatrix *P = pfes->Dof_TrueDof_Matrix();
free_vdofs_marker.SetSize(fes->GetVSize());
P->BooleanMult(1, free_tdof_marker, 0, free_vdofs_marker);
}
#else
const SparseMatrix *cP = fes->GetConformingProlongation();
if (!cP)
{
free_vdofs_marker.MakeRef(free_tdof_marker);
}
else
{
free_vdofs_marker.SetSize(fes->GetVSize());
cP->BooleanMult(free_tdof_marker, free_vdofs_marker);
}
#endif
for (int i = 0; i < NE; i++)
{
fes->GetElementVDofs(i, vdofs);
FiniteElementSpace::AdjustVDofs(vdofs);
for (int j = 0; j < vdofs.Size(); j++)
{
hat_dofs_marker[hat_offsets[i]+j] = ! free_vdofs_marker[vdofs[j]];
}
}
#ifndef MFEM_DEBUG
// In DEBUG mode this array is used below.
free_tdof_marker.DeleteAll();
#endif
free_vdofs_marker.DeleteAll();
// Split the "free" (0) hat_dofs into "internal" (0) or "boundary" (-1).
// The "internal" hat_dofs are those "free" hat_dofs for which the
// corresponding column in C is zero; otherwise the free hat_dof is
// "boundary".
/*for (int i = 0; i < num_hat_dofs; i++)
{
// skip "essential" hat_dofs and empty rows in Ct
if (hat_dofs_marker[i] == 1) { continue; }
//CT row????????
//hat_dofs_marker[i] = -1; // mark this hat_dof as "boundary"
}*/
// Define Af_offsets and Af_f_offsets
Af_offsets.SetSize(NE+1);
Af_offsets[0] = 0;
Af_f_offsets.SetSize(NE+1);
Af_f_offsets[0] = 0;
for (int i = 0; i < NE; i++)
{
int f_size = 0; // count the "free" hat_dofs in element i
for (int j = hat_offsets[i]; j < hat_offsets[i+1]; j++)
{
if (hat_dofs_marker[j] != 1) { f_size++; }
}
Af_offsets[i+1] = Af_offsets[i] + f_size*f_size;
Af_f_offsets[i+1] = Af_f_offsets[i] + f_size;
}
Af_data = new real_t[Af_offsets[NE]];
Af_ipiv = new int[Af_f_offsets[NE]];
// Assemble the constraint matrix C
ConstructC();
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
if (Ct) { return; }
Hybridization::Init(ess_flux_tdof_list);
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
// Define Bf_offsets, Df_offsets and Df_f_offsets
Bf_offsets.SetSize(NE+1);
Bf_offsets[0] = 0;
Df_offsets.SetSize(NE+1);
Df_offsets[0] = 0;
Df_f_offsets.SetSize(NE+1);
Df_f_offsets[0] = 0;
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
Ae_offsets.SetSize(NE+1);
Ae_offsets[0] = 0;
Be_offsets.SetSize(NE+1);
Be_offsets[0] = 0;
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
for (int i = 0; i < NE; i++)
{
int f_size = Af_f_offsets[i+1] - Af_f_offsets[i];
int d_size = fes_p->GetFE(i)->GetDof();
Bf_offsets[i+1] = Bf_offsets[i] + f_size*d_size;
Df_offsets[i+1] = Df_offsets[i] + d_size*d_size;
Df_f_offsets[i+1] = Df_f_offsets[i] + d_size;
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
int a_size = hat_offsets[i+1] - hat_offsets[i];
int e_size = a_size - f_size;
Ae_offsets[i+1] = Ae_offsets[i] + e_size*a_size;
Be_offsets[i+1] = Be_offsets[i] + e_size*d_size;
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
}
Bf_data = new real_t[Bf_offsets[NE]]();//init by zeros
if (!m_nlfi_p)
{
Df_data = new real_t[Df_offsets[NE]]();//init by zeros
Df_ipiv = new int[Df_f_offsets[NE]];
}
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
Ae_data = new real_t[Ae_offsets[NE]];
Be_data = new real_t[Be_offsets[NE]]();//init by zeros
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
if (c_bfi_p)
{
AllocEG();
if (bnl)
{
AllocH();
}
}
}
void DarcyHybridization::AssembleFluxMassMatrix(int el, const DenseMatrix &A)
{
const int o = hat_offsets[el];
const int s = hat_offsets[el+1] - o;
real_t *Af_el_data = Af_data + Af_offsets[el];
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
real_t *Ae_el_data = Ae_data + Ae_offsets[el];
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
for (int j = 0; j < s; j++)
{
if (hat_dofs_marker[o + j] == 1)
{
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
for (int i = 0; i < s; i++)
{
*(Ae_el_data++) = A(i, j);
}
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
continue;
}
for (int i = 0; i < s; i++)
{
if (hat_dofs_marker[o + i] == 1) { continue; }
*(Af_el_data++) = A(i, j);
}
}
MFEM_ASSERT(Af_el_data == Af_data + Af_offsets[el+1], "Internal error");
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
MFEM_ASSERT(Ae_el_data == Ae_data + Ae_offsets[el+1], "Internal error");
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
}
void DarcyHybridization::AssemblePotMassMatrix(int el, const DenseMatrix &D)
{
const int s = Df_f_offsets[el+1] - Df_f_offsets[el];
DenseMatrix D_i(Df_data + Df_offsets[el], s, s);
MFEM_ASSERT(D.Size() == s, "Incompatible sizes");
D_i += D;
D_empty = false;
}
void DarcyHybridization::AssembleDivMatrix(int el, const DenseMatrix &B)
{
const int o = hat_offsets[el];
const int w = hat_offsets[el+1] - o;
const int h = Df_f_offsets[el+1] - Df_f_offsets[el];
real_t *Bf_el_data = Bf_data + Bf_offsets[el];
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
real_t *Be_el_data = Be_data + Be_offsets[el];
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
for (int j = 0; j < w; j++)
{
if (hat_dofs_marker[o + j] == 1)
{
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
for (int i = 0; i < h; i++)
{
*(Be_el_data++) += B(i, j);
}
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
continue;
}
for (int i = 0; i < h; i++)
{
*(Bf_el_data++) += B(i, j);
}
}
MFEM_ASSERT(Bf_el_data == Bf_data + Bf_offsets[el+1], "Internal error");
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
MFEM_ASSERT(Be_el_data == Be_data + Be_offsets[el+1], "Internal error");
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
}
void DarcyHybridization::ComputeAndAssemblePotFaceMatrix(
int face, DenseMatrix &elmat1, DenseMatrix &elmat2, Array<int> &vdofs1,
Array<int> &vdofs2)
{
Mesh *mesh = fes_p->GetMesh();
const FiniteElement *tr_fe, *fe1, *fe2;
DenseMatrix elmat, h_elmat;
int ndof1, ndof2;
Array<int> c_dofs;
tr_fe = c_fes->GetFaceElement(face);
c_fes->GetFaceDofs(face, c_dofs);
const int c_dof = c_dofs.Size();
FaceElementTransformations *ftr = mesh->GetFaceElementTransformations(face);
fes_p->GetElementVDofs(ftr->Elem1No, vdofs1);
fe1 = fes_p->GetFE(ftr->Elem1No);
ndof1 = fe1->GetDof();
if (ftr->Elem2No >= 0)
{
fes_p->GetElementVDofs(ftr->Elem2No, vdofs2);
fe2 = fes_p->GetFE(ftr->Elem2No);
ndof2 = fe2->GetDof();
}
else
{
vdofs2.SetSize(0);
fe2 = fe1;
ndof2 = 0;
}
c_bfi_p->AssembleHDGFaceMatrix(*tr_fe, *fe1, *fe2, *ftr, elmat);
MFEM_ASSERT(elmat.Width() == ndof1+ndof2+c_dof &&
elmat.Height() == ndof1+ndof2+c_dof,
"Size mismatch");
// assemble D element matrices
elmat1.CopyMN(elmat, ndof1, ndof1, 0, 0);
AssemblePotMassMatrix(ftr->Elem1No, elmat1);
if (ndof2)
{
elmat2.CopyMN(elmat, ndof2, ndof2, ndof1, ndof1);
AssemblePotMassMatrix(ftr->Elem2No, elmat2);
}
// assemble E constraint
DenseMatrix E_f_1(E_data + E_offsets[face], ndof1, c_dof);
E_f_1.CopyMN(elmat, ndof1, c_dof, 0, ndof1+ndof2);
if (ndof2)
{
DenseMatrix E_f_2(E_data + E_offsets[face] + c_dof*ndof1, ndof2, c_dof);
E_f_2.CopyMN(elmat, ndof2, c_dof, ndof1, ndof1+ndof2);
}
// assemble G constraint
DenseMatrix G_f(G_data + G_offsets[face], c_dof, ndof1+ndof2);
G_f.CopyMN(elmat, c_dof, ndof1+ndof2, ndof1+ndof2, 0);
// assemble H matrix
if (bnl)
{
DenseMatrix H_f(H_data + H_offsets[face], c_dof, c_dof);
H_f.CopyMN(elmat, c_dof, c_dof, ndof1+ndof2, ndof1+ndof2);
}
else
{
if (!H) { H = new SparseMatrix(c_fes->GetVSize()); }
h_elmat.CopyMN(elmat, c_dof, c_dof, ndof1+ndof2, ndof1+ndof2);
H->AddSubMatrix(c_dofs, c_dofs, h_elmat);
}
}
void DarcyHybridization::ComputeAndAssemblePotBdrFaceMatrix(
int bface, DenseMatrix &elmat1, Array<int> &vdofs)
{
Mesh *mesh = fes_p->GetMesh();
const FiniteElement *tr_fe, *fe;
DenseMatrix elmat, elmat_aux, h_elmat;
Array<int> c_dofs;
const int face = mesh->GetBdrElementFaceIndex(bface);
tr_fe = c_fes->GetFaceElement(face);
c_fes->GetFaceDofs(face, c_dofs);
const int c_dof = c_dofs.Size();
FaceElementTransformations *ftr = mesh->GetFaceElementTransformations(face);
fes_p->GetElementVDofs(ftr->Elem1No, vdofs);
fe = fes_p->GetFE(ftr->Elem1No);
const int ndof = fe->GetDof();
MFEM_ASSERT(boundary_constraint_pot_integs.Size() > 0,
"No boundary constraint integrators");
const int bdr_attr = mesh->GetBdrAttribute(bface);
for (int i = 0; i < boundary_constraint_pot_integs.Size(); i++)
{
if (boundary_constraint_pot_integs_marker[i]
&& (*boundary_constraint_pot_integs_marker[i])[bdr_attr-1] == 0) { continue; }
boundary_constraint_pot_integs[i]->AssembleHDGFaceMatrix(*tr_fe, *fe, *fe, *ftr,
elmat_aux);
if (elmat.Size() > 0)
{ elmat += elmat_aux; }
else
{ elmat = elmat_aux; }
}
if (elmat.Size() == 0) { return; }
MFEM_ASSERT(elmat.Width() == ndof+c_dof &&
elmat.Height() == ndof+c_dof,
"Size mismatch");
// assemble D element matrices
elmat1.CopyMN(elmat, ndof, ndof, 0, 0);
AssemblePotMassMatrix(ftr->Elem1No, elmat1);
// assemble E constraint
DenseMatrix E_f_1(E_data + E_offsets[face], ndof, c_dof);
E_f_1.CopyMN(elmat, ndof, c_dof, 0, ndof);
// assemble G constraint
DenseMatrix G_f(G_data + G_offsets[face], c_dof, ndof);
G_f.CopyMN(elmat, c_dof, ndof, ndof, 0);
// assemble H matrix
if (bnl)
{
DenseMatrix H_f(H_data + H_offsets[face], c_dof, c_dof);
H_f.CopyMN(elmat, c_dof, c_dof, ndof, ndof);
}
else
{
if (!H) { H = new SparseMatrix(c_fes->GetVSize()); }
h_elmat.CopyMN(elmat, c_dof, c_dof, ndof, ndof);
H->AddSubMatrix(c_dofs, c_dofs, h_elmat);
}
}
void DarcyHybridization::GetFDofs(int el, Array<int> &fdofs) const
{
const int o = hat_offsets[el];
const int s = hat_offsets[el+1] - o;
Array<int> vdofs;
fes->GetElementVDofs(el, vdofs);
MFEM_ASSERT(vdofs.Size() == s, "Incompatible DOF sizes");
fdofs.DeleteAll();
fdofs.Reserve(s);
for (int i = 0; i < s; i++)
{
if (hat_dofs_marker[i + o] != 1)
{
fdofs.Append(vdofs[i]);
}
}
}
void DarcyHybridization::GetEDofs(int el, Array<int> &edofs) const
{
const int o = hat_offsets[el];
const int s = hat_offsets[el+1] - o;
Array<int> vdofs;
fes->GetElementVDofs(el, vdofs);
MFEM_ASSERT(vdofs.Size() == s, "Incompatible DOF sizes");
edofs.DeleteAll();
edofs.Reserve(s);
for (int i = 0; i < s; i++)
{
if (hat_dofs_marker[i + o] == 1)
{
edofs.Append(vdofs[i]);
}
}
}
void DarcyHybridization::AssembleCtFaceMatrix(int face, int el1, int el2,
const DenseMatrix &elmat)
{
const int hat_size_1 = hat_offsets[el1+1] - hat_offsets[el1];
const int f_size_1 = Af_f_offsets[el1+1] - Af_f_offsets[el1];
const int c_size = c_fes->GetFaceElement(face)->GetDof() * c_fes->GetVDim();
//el1
DenseMatrix Ct_face_1(Ct_data + Ct_offsets[face], f_size_1, c_size);
AssembleCtSubMatrix(el1, elmat, Ct_face_1);
//el2
if (el2 >= 0)
{
//const int hat_size_2 = hat_offsets[el2+1] - hat_offsets[el2];
const int f_size_2 = Af_f_offsets[el2+1] - Af_f_offsets[el2];
DenseMatrix Ct_face_2(Ct_data + Ct_offsets[face] + f_size_1*c_size,
f_size_2, c_size);
AssembleCtSubMatrix(el2, elmat, Ct_face_2, hat_size_1);
}
}
void DarcyHybridization::AssembleCtSubMatrix(int el, const DenseMatrix &elmat,
DenseMatrix &Ct, int ioff)
{
const int hat_offset = hat_offsets[el];
const int hat_size = hat_offsets[el+1] - hat_offset;
int row = 0;
for (int i = 0; i < hat_size; i++)
{
if (hat_dofs_marker[hat_offset + i] == 1) { continue; }
bool bzero = true;
for (int j = 0; j < Ct.Width(); j++)
{
const real_t val = elmat(i + ioff, j);
if (val == 0.) { continue; }
Ct(row, j) = val;
bzero = false;
}
if (!bzero)
{
//mark the hat dof as "boundary" if the row is non-zero
hat_dofs_marker[hat_offset + i] = -1;
}
row++;
}
MFEM_ASSERT(row == Af_f_offsets[el+1] - Af_f_offsets[el], "Internal error.");
}
void DarcyHybridization::ConstructC()
{
#ifndef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
Hybridization::ConstructC();
return;
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
FaceElementTransformations *FTr;
Mesh *mesh = fes->GetMesh();
int num_faces = mesh->GetNumFaces();
#if defined(MFEM_USE_DOUBLE)
constexpr real_t mtol = 1e-12;
#elif defined(MFEM_USE_SINGLE)
constexpr real_t mtol = 4e-6;
#else
#error "Only single and double precision are supported!"
constexpr real_t mtol = 1.;
#endif
// Define Ct_offsets and allocate Ct_data
Ct_offsets.SetSize(num_faces+1);
Ct_offsets[0] = 0;
for (int f = 0; f < num_faces; f++)
{
FTr = mesh->GetFaceElementTransformations(f, 0);
int f_size = Af_f_offsets[FTr->Elem1No+1] - Af_f_offsets[FTr->Elem1No];
if (FTr->Elem2No >= 0)
{
f_size += Af_f_offsets[FTr->Elem2No+1] - Af_f_offsets[FTr->Elem2No];
}
const int c_size = c_fes->GetFaceElement(f)->GetDof() * c_fes->GetVDim();
Ct_offsets[f+1] = Ct_offsets[f] + c_size * f_size;
}
Ct_data = new real_t[Ct_offsets[num_faces]]();//init by zeros
// Assemble the constraint element matrices
if (c_bfi)
{
DenseMatrix elmat;
for (int f = 0; f < num_faces; f++)
{
FTr = mesh->GetInteriorFaceTransformations(f);
if (!FTr) { continue; }
const FiniteElement *fe1 = fes->GetFE(FTr->Elem1No);
const FiniteElement *fe2 = fes->GetFE(FTr->Elem2No);
c_bfi->AssembleFaceMatrix(*c_fes->GetFaceElement(f),
*fe1, *fe2, *FTr, elmat);
// zero-out small elements in elmat
elmat.Threshold(mtol * elmat.MaxMaxNorm());
// assemble the matrix
AssembleCtFaceMatrix(f, FTr->Elem1No, FTr->Elem2No, elmat);
}
if (boundary_constraint_integs.Size())
{
const FiniteElement *fe1, *fe2;
const FiniteElement *face_el;
// 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 < boundary_constraint_integs.Size(); k++)
{
if (boundary_constraint_integs_marker[k] == NULL)
{
bdr_attr_marker = 1;
break;
}
Array<int> &bdr_marker = *boundary_constraint_integs_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; }
FTr = mesh->GetBdrFaceTransformations(i);
if (!FTr) { continue; }
int iface = mesh->GetBdrElementFaceIndex(i);
face_el = c_fes->GetFaceElement(iface);
fe1 = fes -> GetFE (FTr -> 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 < boundary_constraint_integs.Size(); k++)
{
if (boundary_constraint_integs_marker[k] &&
(*boundary_constraint_integs_marker[k])[bdr_attr-1] == 0) { continue; }
boundary_constraint_integs[k]->AssembleFaceMatrix(*face_el, *fe1, *fe2, *FTr,
elmat);
// zero-out small elements in elmat
elmat.Threshold(mtol * elmat.MaxMaxNorm());
// assemble the matrix
AssembleCtFaceMatrix(iface, FTr->Elem1No, FTr->Elem2No, elmat);
}
}
}
}
else
{
// Check if c_fes is really needed here.
MFEM_ABORT("TODO: algebraic definition of C");
}
}
void DarcyHybridization::AllocD() const
{
const int NE = fes_p->GetNE();
Df_data = new real_t[Df_offsets[NE]]();//init by zeros
Df_ipiv = new int[Df_f_offsets[NE]];
}
void DarcyHybridization::AllocEG() const
{
FaceElementTransformations *FTr;
Mesh *mesh = fes->GetMesh();
int num_faces = mesh->GetNumFaces();
// Define E_offsets and allocate E_data and G_data
E_offsets.SetSize(num_faces+1);
E_offsets[0] = 0;
for (int f = 0; f < num_faces; f++)
{
FTr = mesh->GetFaceElementTransformations(f, 0);
int d_size = Df_f_offsets[FTr->Elem1No+1] - Df_f_offsets[FTr->Elem1No];
if (FTr->Elem2No >= 0)
{
d_size += Df_f_offsets[FTr->Elem2No+1] - Df_f_offsets[FTr->Elem2No];
}
const int c_size = c_fes->GetFaceElement(f)->GetDof() * c_fes->GetVDim();
E_offsets[f+1] = E_offsets[f] + c_size * d_size;
}
E_data = new real_t[E_offsets[num_faces]]();//init by zeros
G_data = new real_t[G_offsets[num_faces]]();//init by zeros
}
void DarcyHybridization::AllocH() const
{
Mesh *mesh = fes->GetMesh();
int num_faces = mesh->GetNumFaces();
// Define E_offsets and allocate E_data and G_data
H_offsets.SetSize(num_faces+1);
H_offsets[0] = 0;
for (int f = 0; f < num_faces; f++)
{
const int c_size = c_fes->GetFaceElement(f)->GetDof() * c_fes->GetVDim();
H_offsets[f+1] = H_offsets[f] + c_size * c_size;
}
H_data = new real_t[H_offsets[num_faces]]();//init by zeros
}
void DarcyHybridization::InvertA()
{
const int NE = fes->GetNE();
for (int el = 0; el < NE; el++)
{
int a_dofs_size = Af_f_offsets[el+1] - Af_f_offsets[el];
// Decompose A
LUFactors LU_A(Af_data + Af_offsets[el], Af_ipiv + Af_f_offsets[el]);
LU_A.Factor(a_dofs_size);
}
}
void DarcyHybridization::InvertD()
{
const int NE = fes->GetNE();
for (int el = 0; el < NE; el++)
{
int d_dofs_size = Df_f_offsets[el+1] - Df_f_offsets[el];
// Decompose D
#ifdef MFEM_DEBUG
DenseMatrix D(Df_data + Df_offsets[el], d_dofs_size, d_dofs_size);
const double norm = D.MaxMaxNorm();
if (norm == 0.)
{
MFEM_ABORT("Inverting an empty matrix!");
}
if (D.Rank(norm * 1e-12) < d_dofs_size)
{
MFEM_ABORT("Inverting a singular matrix!");
}
#endif
LUFactors LU_D(Df_data + Df_offsets[el], Df_ipiv + Df_f_offsets[el]);
LU_D.Factor(d_dofs_size);
}
}
void DarcyHybridization::ComputeH()
{
MFEM_ASSERT(!bnl, "Cannot assemble H matrix in the non-linear regime");
const int skip_zeros = 1;
const int NE = fes->GetNE();
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
const int dim = fes->GetMesh()->Dimension();
DenseMatrix AiBt, AiCt, BAiCt, CAiBt, H_l;
DenseMatrix Ct_1_el_1, Ct_1_el_2, Ct_2_el_1, Ct_2_el_2;
DenseMatrix E_el_1, E_el_2, Gt_el_1, Gt_el_2;
Array<int> c_dofs_1, c_dofs_2;
Array<int> faces, oris;
if (!H) { H = new SparseMatrix(c_fes->GetVSize()); }
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
MFEM_ASSERT(!c_bfi_p,
"Potential constraint is not supported in non-block assembly!");
DenseMatrix AiBt, BAi, Hb_l;
Array<int> a_dofs;
SparseMatrix *Hb = new SparseMatrix(Ct->Height());
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
for (int el = 0; el < NE; el++)
{
int a_dofs_size = Af_f_offsets[el+1] - Af_f_offsets[el];
int d_dofs_size = Df_f_offsets[el+1] - Df_f_offsets[el];
// Decompose A
LUFactors LU_A(Af_data + Af_offsets[el], Af_ipiv + Af_f_offsets[el]);
LU_A.Factor(a_dofs_size);
// Construct Schur complement
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
DenseMatrix D(Df_data + Df_offsets[el], d_dofs_size, d_dofs_size);
AiBt.SetSize(a_dofs_size, d_dofs_size);
AiBt.Transpose(B);
if (!bsym) { AiBt.Neg(); }
LU_A.Solve(AiBt.Height(), AiBt.Width(), AiBt.GetData());
mfem::AddMult(B, AiBt, D);
// Decompose Schur complement
LUFactors LU_S(D.GetData(), Df_ipiv + Df_f_offsets[el]);
LU_S.Factor(d_dofs_size);
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
switch (dim)
{
case 1:
fes->GetMesh()->GetElementVertices(el, faces);
break;
case 2:
fes->GetMesh()->GetElementEdges(el, faces, oris);
break;
case 3:
fes->GetMesh()->GetElementFaces(el, faces, oris);
break;
}
// Mult C^T
for (int f1 = 0; f1 < faces.Size(); f1++)
{
FaceElementTransformations *FTr = GetCtFaceMatrix(faces[f1], Ct_1_el_1,
Ct_1_el_2);
if (!FTr) { continue; }
DenseMatrix &Ct_1 = (FTr->Elem1No == el)?(Ct_1_el_1):(Ct_1_el_2);
//A^-1 C^T
AiCt.SetSize(Ct_1.Height(), Ct_1.Width());
AiCt = Ct_1;
LU_A.Solve(Ct_1.Height(), Ct_1.Width(), AiCt.GetData());
//S^-1 (B A^-1 C^T - E)
BAiCt.SetSize(B.Height(), Ct_1.Width());
mfem::Mult(B, AiCt, BAiCt);
if (c_bfi_p)
{
if (GetEFaceMatrix(faces[f1], E_el_1, E_el_2))
{
DenseMatrix &E = (FTr->Elem1No == el)?(E_el_1):(E_el_2);
BAiCt -= E;
}
}
LU_S.Solve(BAiCt.Height(), BAiCt.Width(), BAiCt.GetData());
for (int f2 = 0; f2 < faces.Size(); f2++)
{
FaceElementTransformations *FTr = GetCtFaceMatrix(faces[f2], Ct_2_el_1,
Ct_2_el_2);
if (!FTr) { continue; }
DenseMatrix &Ct_2 = (FTr->Elem1No == el)?(Ct_2_el_1):(Ct_2_el_2);
//- C A^-1 C^T
H_l.SetSize(Ct_2.Width(), Ct_1.Width());
mfem::MultAtB(Ct_2, AiCt, H_l);
H_l.Neg();
//(C A^-1 B^T + G) S^-1 (B A^-1 C^T - E)
CAiBt.SetSize(Ct_2.Width(), B.Height());
mfem::MultAtB(Ct_2, AiBt, CAiBt);
if (c_bfi_p)
{
if (GetGFaceMatrix(faces[f2], Gt_el_1, Gt_el_2))
{
DenseMatrix &G = (FTr->Elem1No == el)?(Gt_el_1):(Gt_el_2);
CAiBt += G;
}
}
mfem::AddMult(CAiBt, BAiCt, H_l);
c_fes->GetFaceVDofs(faces[f1], c_dofs_1);
if (f1 == f2)
{
H->AddSubMatrix(c_dofs_1, c_dofs_1, H_l, skip_zeros);
}
else
{
c_fes->GetFaceVDofs(faces[f2], c_dofs_2);
H->AddSubMatrix(c_dofs_2, c_dofs_1, H_l, skip_zeros);
}
}
}
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
Hb_l.SetSize(B.Width());
//-A^-1
LU_A.GetInverseMatrix(B.Width(), Hb_l.GetData());
Hb_l.Neg();
//B A^-1
BAi.SetSize(B.Height(), B.Width());
mfem::Mult(B, Hb_l, BAi);
BAi.Neg();
//S^-1 B A^-1
LU_S.Solve(BAi.Height(), BAi.Width(), BAi.GetData());
//A^-1 B^T S^-1 B A^-1
mfem::AddMult(AiBt, BAi, Hb_l);
a_dofs.SetSize(a_dofs_size);
for (int i = 0; i < a_dofs_size; i++)
{
a_dofs[i] = hat_offsets[el] + i;
}
Hb->AddSubMatrix(a_dofs, a_dofs, Hb_l, skip_zeros);
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
}
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
H->Finalize();
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
Hb->Finalize();
if (H)
{
SparseMatrix *rap = RAP(*Ct, *Hb, *Ct);
*H += *rap;
delete rap;
}
else
{
H = RAP(*Ct, *Hb, *Ct);
}
delete Hb;
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
}
FaceElementTransformations *DarcyHybridization::GetCtFaceMatrix(
int f, DenseMatrix &Ct_1, DenseMatrix &Ct_2) const
{
FaceElementTransformations *FTr =
fes->GetMesh()->GetFaceElementTransformations(f, 0);
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
const int c_size = c_fes->GetFaceElement(f)->GetDof() * c_fes->GetVDim();
const int f_size_1 = Af_f_offsets[FTr->Elem1No+1] - Af_f_offsets[FTr->Elem1No];
Ct_1.Reset(Ct_data + Ct_offsets[f], f_size_1, c_size);
if (FTr->Elem2No >= 0)
{
const int f_size_2 = Af_f_offsets[FTr->Elem2No+1] - Af_f_offsets[FTr->Elem2No];
Ct_2.Reset(Ct_data + Ct_offsets[f] + f_size_1*c_size,
f_size_2, c_size);
}
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
Array<int> c_dofs;
c_fes->GetFaceVDofs(f, c_dofs);
GetCtSubMatrix(FTr->Elem1No, c_dofs, Ct_1);
if (FTr->Elem2No >= 0)
{
GetCtSubMatrix(FTr->Elem2No, c_dofs, Ct_2);
}
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK_ASSEMBLY
return FTr;
}
FaceElementTransformations *DarcyHybridization::GetEFaceMatrix(
int f, DenseMatrix &E_1, DenseMatrix &E_2) const
{
FaceElementTransformations *FTr =
fes->GetMesh()->GetFaceElementTransformations(f, 3);
const int c_size = c_fes->GetFaceElement(f)->GetDof() * c_fes->GetVDim();
const int d_size_1 = Df_f_offsets[FTr->Elem1No+1] - Df_f_offsets[FTr->Elem1No];
E_1.Reset(E_data + E_offsets[f], d_size_1, c_size);
if (FTr->Elem2No >= 0)
{
const int d_size_2 = Df_f_offsets[FTr->Elem2No+1] - Df_f_offsets[FTr->Elem2No];
E_2.Reset(E_data + E_offsets[f] + d_size_1*c_size, d_size_2, c_size);
}
return FTr;
}
FaceElementTransformations *DarcyHybridization::GetGFaceMatrix(
int f, DenseMatrix &G_1, DenseMatrix &G_2) const
{
FaceElementTransformations *FTr =
fes->GetMesh()->GetFaceElementTransformations(f, 0);
const int c_size = c_fes->GetFaceElement(f)->GetDof() * c_fes->GetVDim();
const int d_size_1 = Df_f_offsets[FTr->Elem1No+1] - Df_f_offsets[FTr->Elem1No];
G_1.Reset(G_data + G_offsets[f], c_size, d_size_1);
if (FTr->Elem2No >= 0)
{
const int d_size_2 = Df_f_offsets[FTr->Elem2No+1] - Df_f_offsets[FTr->Elem2No];
G_2.Reset(G_data + G_offsets[f] + d_size_1*c_size, c_size, d_size_2);
}
return FTr;
}
void DarcyHybridization::GetHFaceMatrix(int f, DenseMatrix &H) const
{
const int c_size = c_fes->GetFaceElement(f)->GetDof() * c_fes->GetVDim();
H.Reset(H_data + H_offsets[f], c_size, c_size);
}
void DarcyHybridization::GetCtSubMatrix(int el, const Array<int> &c_dofs,
DenseMatrix &Ct_l) const
{
const int hat_offset = hat_offsets[el ];
const int hat_size = hat_offsets[el+1] - hat_offset;
const int f_size = Af_f_offsets[el+1] - Af_f_offsets[el];
Array<int> vdofs;
fes->GetElementVDofs(el, vdofs);
Ct_l.SetSize(f_size, c_dofs.Size());
Ct_l = 0.;
int i = 0;
for (int row = hat_offset; row < hat_offset + hat_size; row++)
{
if (hat_dofs_marker[row] == 1) { continue; }
const int ncols = Ct->RowSize(row);
const int *cols = Ct->GetRowColumns(row);
const real_t *vals = Ct->GetRowEntries(row);
for (int j = 0; j < c_dofs.Size(); j++)
{
const int cdof = (c_dofs[j]>=0)?(c_dofs[j]):(-1-c_dofs[j]);
for (int col = 0; col < ncols; col++)
if (cols[col] == cdof)
{
real_t val = vals[col];
Ct_l(i,j) = (c_dofs[j] >= 0)?(+val):(-val);
break;
}
}
i++;
}
}
void DarcyHybridization::Mult(const Vector &x, Vector &y) const
{
MFEM_VERIFY(bfin, "DarcyHybridization must be finalized");
if (H)
{
H->Mult(x, y);
return;
}
MultNL(MultNlMode::Mult, darcy_rhs, x, y);
}
Operator &DarcyHybridization::GetGradient(const Vector &x) const
{
MFEM_VERIFY(bfin, "DarcyHybridization must be finalized");
if (H) { return *H; }
const int NE = fes->GetNE();
if (!Df_data) { AllocD(); }// D is resetted in ConstructGrad()
if (!E_data || !G_data) { AllocEG(); }// E and G are rewritten
if (!H_data) { AllocH(); }
else if (c_nlfi_p)
{
// H is resetted here for additive double side integration
memset(H_data, 0, H_offsets[NE] * sizeof(real_t));
}
Vector y;//dummy
MultNL(MultNlMode::Grad, darcy_rhs, x, y);
pGrad.Reset(new Gradient(*this));
return *pGrad;
}
void DarcyHybridization::MultNL(MultNlMode mode, const BlockVector &b,
const Vector &x, Vector &y) const
{
const int NE = fes->GetNE();
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
const int dim = fes->GetMesh()->Dimension();
DenseMatrix Ct_1, Ct_2, E_1, E_2, G_1, G_2, H;
BlockVector x_l;
Array<int> c_dofs;
Array<int> c_offsets;
Array<int> faces, oris;
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
MFEM_ASSERT(!c_nlfi_p,
"Potential constraint is not supported in non-block assembly!");
Vector hat_bu(hat_offsets.Last());
Vector hat_u;
if (mode == MultNlMode::Mult)
{
hat_u.SetSize(hat_offsets.Last());
hat_u = 0.;//essential vdofs?!
}
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
Vector bu_l, bp_l, u_l, p_l, y_l;
Array<int> u_vdofs, p_dofs;
const Vector &bu = b.GetBlock(0);
const Vector &bp = b.GetBlock(1);
BlockVector yb;
if (mode == MultNlMode::Sol)
{
yb.Update(y, darcy_offsets);
}
else
{
y = 0.0;
}
if (f_2_b.Size() == 0)
{
f_2_b = fes->GetMesh()->GetFaceToBdrElMap();
}
#ifndef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
//C^T sol_r
Ct->Mult(x, hat_bu);
#endif //!MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
for (int el = 0; el < NE; el++)
{
//Load RHS
if (mode != MultNlMode::GradMult)
{
GetFDofs(el, u_vdofs);
bu.GetSubVector(u_vdofs, bu_l);
fes_p->GetElementDofs(el, p_dofs);
bp.GetSubVector(p_dofs, bp_l);
if (bsym)
{
//In the case of the symmetrized system, the sign is oppposite!
bp_l.Neg();
}
}
else
{
bu_l.SetSize(Af_f_offsets[el+1] - Af_f_offsets[el]);
bu_l = 0.;
bp_l.SetSize(Df_f_offsets[el+1] - Df_f_offsets[el]);
bp_l = 0.;
}
switch (dim)
{
case 1:
fes->GetMesh()->GetElementVertices(el, faces);
break;
case 2:
fes->GetMesh()->GetElementEdges(el, faces, oris);
break;
case 3:
fes->GetMesh()->GetElementFaces(el, faces, oris);
break;
}
c_offsets.SetSize(faces.Size()+1);
c_offsets[0] = 0;
for (int f = 0; f < faces.Size(); f++)
{
const int c_size = c_fes->GetFaceElement(faces[f])->GetDof() * c_fes->GetVDim();
c_offsets[f+1] = c_offsets[f] + c_size;
}
x_l.Update(c_offsets);
for (int f = 0; f < faces.Size(); f++)
{
c_fes->GetFaceVDofs(faces[f], c_dofs);
x.GetSubVector(c_dofs, x_l.GetBlock(f));
}
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
// bu - C^T x
for (int f = 0; f < faces.Size(); f++)
{
FaceElementTransformations *FTr = GetCtFaceMatrix(faces[f], Ct_1, Ct_2);
if (!FTr) { continue; }
const Vector &x_f = x_l.GetBlock(f);
DenseMatrix &Ct = (FTr->Elem1No == el)?(Ct_1):(Ct_2);
Ct.AddMult_a(-1., x_f, bu_l);
//bp - E x
if (c_bfi_p || mode == MultNlMode::GradMult)
{
if (GetEFaceMatrix(faces[f], E_1, E_2))
{
DenseMatrix &E = (FTr->Elem1No == el)?(E_1):(E_2);
E.AddMult_a(-1., x_f, bp_l);
}
}
}
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
// bu - C^T sol
for (int dof = hat_offsets[el], i = 0; dof < hat_offsets[el+1]; dof++)
{
if (hat_dofs_marker[dof] == 1) { continue; }
bu_l[i++] -= hat_bu[dof];
}
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
if (mode != MultNlMode::GradMult)
{
//local u
if (darcy_u.Size() > 0)
{
//load the initial guess from the non-reduced solution vector
darcy_u.GetSubVector(u_vdofs, u_l);
}
else
{
u_l.SetSize(u_vdofs.Size());
u_l = 0.;//initial guess?
}
//local p
if (darcy_p.Size() > 0)
{
//load the initial guess from the non-reduced solution vector
darcy_p.GetSubVector(p_dofs, p_l);
}
else
{
p_l.SetSize(p_dofs.Size());
p_l = 0.;//initial guess?
}
//(A^-1 - A^-1 B^T S^-1 B A^-1) (bu - C^T sol)
MultInvNL(el, bu_l, bp_l, x_l, u_l, p_l);
if (mode == MultNlMode::Sol)
{
yb.GetBlock(0).SetSubVector(u_vdofs, u_l);
yb.GetBlock(1).SetSubVector(p_dofs, p_l);
continue;
}
else if (mode == MultNlMode::Grad)
{
ConstructGrad(el, faces, x_l, u_l, p_l);
continue;
}
}
else
{
//(A^-1 - A^-1 B^T S^-1 B A^-1) (bu - C^T sol)
MultInv(el, bu_l, bp_l, u_l, p_l);
}
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
// C u_l
for (int f = 0; f < faces.Size(); f++)
{
FaceElementTransformations *FTr = GetCtFaceMatrix(faces[f], Ct_1, Ct_2);
if (!FTr) { continue; }
const Vector &x_f = x_l.GetBlock(f);
DenseMatrix &Ct = (FTr->Elem1No == el)?(Ct_1):(Ct_2);
y_l.SetSize(x_f.Size());
Ct.MultTranspose(u_l, y_l);
//G p_l + H x_l
if (c_bfi_p || mode == MultNlMode::GradMult)
{
//linear
if (GetGFaceMatrix(faces[f], G_1, G_2))
{
DenseMatrix &G = (FTr->Elem1No == el)?(G_1):(G_2);
G.AddMult(p_l, y_l);
}
//integrate the face contrbution only on one (first) side
if (FTr->Elem1No == el)
{
GetHFaceMatrix(faces[f], H);
H.AddMult(x_f, y_l);
}
}
else
{
//nonlinear
Vector GpHx_l;
int type = NonlinearFormIntegrator::HDGFaceType::CONSTR
| NonlinearFormIntegrator::HDGFaceType::FACE;
if (FTr->Elem2No >= 0)
{
//interior
if (c_nlfi_p)
{
if (FTr->Elem1No != el) { type |= 1; }
c_nlfi_p->AssembleHDGFaceVector(type,
*c_fes->GetFaceElement(faces[f]),
*fes_p->GetFE(el),
*fes->GetMesh()->GetInteriorFaceTransformations(faces[f]),
x_f, p_l, GpHx_l);
y_l += GpHx_l;
}
}
else
{
//boundary
const int bdr_attr = fes->GetMesh()->GetBdrAttribute(f_2_b[faces[f]]);
for (int i = 0; i < boundary_constraint_pot_nonlin_integs.Size(); i++)
{
if (boundary_constraint_pot_nonlin_integs_marker[i]
&& (*boundary_constraint_pot_nonlin_integs_marker[i])[bdr_attr-1] == 0) { continue; }
boundary_constraint_pot_nonlin_integs[i]->AssembleHDGFaceVector(type,
*c_fes->GetFaceElement(faces[f]),
*fes_p->GetFE(el),
*fes->GetMesh()->GetFaceElementTransformations(faces[f]),
x_f, p_l, GpHx_l);
y_l += GpHx_l;
}
}
}
c_fes->GetFaceVDofs(faces[f], c_dofs);
y.AddElementVector(c_dofs, y_l);
}
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
// hat_u += u_l
for (int dof = hat_offsets[el], i = 0; dof < hat_offsets[el+1]; dof++)
{
if (hat_dofs_marker[dof] == 1) { continue; }
hat_u[dof] += u_l[i++];
}
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
}
#ifndef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
if (mode == 0)
{
//C u
Ct->MultTranspose(hat_u, y);
}
#endif //!MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
}
void DarcyHybridization::Finalize()
{
if (!bfin)
{
if (bnl)
{
if (!m_nlfi_u && !m_nlfi)
{
InvertA();
}
if (!m_nlfi_p && !c_nlfi_p && !D_empty && !m_nlfi)
{
InvertD();
}
else if (!D_empty)
{
std::swap(Df_data, Df_lin_data);
if (!Df_data)
{
const int NE = fes->GetMesh()->GetNE();
Df_data = new real_t[Df_offsets[NE]]();
}
}
}
else
{
ComputeH();
}
bfin = true;
}
}
void DarcyHybridization::EliminateVDofsInRHS(const Array<int> &vdofs_flux,
const BlockVector &x, BlockVector &b)
{
if (bnl)
{
//save the rhs for initial guess in the iterative local solve
darcy_u = x.GetBlock(0);
darcy_p = x.GetBlock(1);
}
const int NE = fes->GetNE();
Vector u_e, bu_e, bp_e;
Array<int> u_vdofs, p_dofs, edofs;
const Vector &xu = x.GetBlock(0);
Vector &bu = b.GetBlock(0);
Vector &bp = b.GetBlock(1);
for (int el = 0; el < NE; el++)
{
GetEDofs(el, edofs);
xu.GetSubVector(edofs, u_e);
u_e.Neg();
//bu -= A_e u_e
const int a_size = hat_offsets[el+1] - hat_offsets[el];
DenseMatrix Ae(Ae_data + Ae_offsets[el], a_size, edofs.Size());
bu_e.SetSize(a_size);
Ae.Mult(u_e, bu_e);
fes->GetElementVDofs(el, u_vdofs);
bu.AddElementVector(u_vdofs, bu_e);
//bp -= B_e u_e
const int d_size = Df_f_offsets[el+1] - Df_f_offsets[el];
DenseMatrix Be(Be_data + Be_offsets[el], d_size, edofs.Size());
bp_e.SetSize(d_size);
Be.Mult(u_e, bp_e);
if (bsym)
{
//In the case of the symmetrized system, the sign is oppposite!
bp_e.Neg();
}
fes_p->GetElementDofs(el, p_dofs);
bp.AddElementVector(p_dofs, bp_e);
}
for (int vdof : vdofs_flux)
{
bu(vdof) = xu(vdof);//<--can be arbitrary as it is ignored
}
}
void DarcyHybridization::MultInvNL(int el, const Vector &bu_l,
const Vector &bp_l, const BlockVector &x_l,
Vector &u_l, Vector &p_l) const
{
const int a_dofs_size = Af_f_offsets[el+1] - Af_f_offsets[el];
const int d_dofs_size = Df_f_offsets[el+1] - Df_f_offsets[el];
MFEM_ASSERT(bu_l.Size() == a_dofs_size &&
bp_l.Size() == d_dofs_size, "Incompatible size");
//prepare vector of local traces
Array<int> faces, oris;
const int dim = fes->GetMesh()->Dimension();
switch (dim)
{
case 1:
fes->GetMesh()->GetElementVertices(el, faces);
break;
case 2:
fes->GetMesh()->GetElementEdges(el, faces, oris);
break;
case 3:
fes->GetMesh()->GetElementFaces(el, faces, oris);
break;
}
//construct the local operator
LocalNLOperator *lop;
enum class LocalOpType { FluxNL, PotNL, FullNL } ltype;
if (!m_nlfi_p && !c_nlfi_p && !D_empty && !m_nlfi)
{
lop = new LocalFluxNLOperator(*this, el, bp_l, x_l, faces);
ltype = LocalOpType::FluxNL;
}
else if (!m_nlfi_u && !m_nlfi)
{
lop = new LocalPotNLOperator(*this, el, bu_l, x_l, faces);
ltype = LocalOpType::PotNL;
}
else
{
lop = new LocalNLOperator(*this, el, x_l, faces);
ltype = LocalOpType::FullNL;
}
//solve the local system
IterativeSolver *lsolver;
bool use_prec;
switch (lsolve.type)
{
case LSsolveType::LBFGS:
lsolver = new LBFGSSolver();
use_prec = false;
break;
case LSsolveType::LBB:
lsolver = new LBBSolver();
use_prec = false;
break;
case LSsolveType::Newton:
lsolver = new NewtonSolver();
use_prec = true;
break;
default:
MFEM_ABORT("Unknown local solver");
}
IterativeSolver *prec = NULL;
if (use_prec)
{
switch (lsolve.prec.type)
{
case LPrecType::GMRES:
prec = new GMRESSolver();
break;
default:
MFEM_ABORT("Unknown local preconditioner");
}
prec->SetMaxIter(lsolve.prec.iters);
prec->SetRelTol((lsolve.prec.rtol >= 0)?
(lsolve.prec.rtol):(lsolve.rtol));
prec->SetAbsTol((lsolve.prec.atol >= 0)?
(lsolve.prec.atol):(lsolve.atol));
}
lsolver->SetOperator(*lop);
if (prec) { lsolver->SetPreconditioner(*prec); }
lsolver->SetMaxIter(lsolve.iters);
lsolver->SetRelTol(lsolve.rtol);
lsolver->SetAbsTol(lsolve.atol);
lsolver->SetPrintLevel(lsolve.print_lvl);
switch (ltype)
{
case LocalOpType::FluxNL:
{
//solve the flux
lsolver->Mult(bu_l, u_l);
//solve the potential
static_cast<LocalFluxNLOperator*>(lop)->SolveP(u_l, p_l);
}
break;
case LocalOpType::PotNL:
{
//solve the potential
lsolver->Mult(bp_l, p_l);
//solve the flux
static_cast<LocalPotNLOperator*>(lop)->SolveU(p_l, u_l);
}
break;
case LocalOpType::FullNL:
{
//rhs
BlockVector b(lop->GetOffsets());
b.GetBlock(0) = bu_l;
b.GetBlock(1) = bp_l;
//x
BlockVector x(lop->GetOffsets());
x.GetBlock(0) = u_l;
x.GetBlock(1) = p_l;
//solve the flux and potential
lsolver->Mult(b, x);
u_l = x.GetBlock(0);
p_l = x.GetBlock(1);
}
break;
}
if (lsolver->GetConverged())
{
if (lsolve.print_lvl >= 0)
std::cout << "el: " << el
<< " iters: " << lsolver->GetNumIterations()
<< " rel. norm: " << lsolver->GetFinalRelNorm()
<< std::endl;
}
else
{
std::cout << "el: " << el
<< " not convered in " << lsolver->GetNumIterations() << " iters"
<< " rel. norm: " << lsolver->GetFinalRelNorm()
<< std::endl;
}
delete lsolver;
delete prec;
delete lop;
}
void DarcyHybridization::MultInv(int el, const Vector &bu, const Vector &bp,
Vector &u, Vector &p) const
{
Vector AiBtSiBAibu, AiBtSibp;
const int a_dofs_size = Af_f_offsets[el+1] - Af_f_offsets[el];
const int d_dofs_size = Df_f_offsets[el+1] - Df_f_offsets[el];
MFEM_ASSERT(bu.Size() == a_dofs_size &&
bp.Size() == d_dofs_size, "Incompatible size");
// Load LU decomposition of A and Schur complement
LUFactors LU_A(Af_data + Af_offsets[el], Af_ipiv + Af_f_offsets[el]);
LUFactors LU_S(Df_data + Df_offsets[el], Df_ipiv + Df_f_offsets[el]);
// Load B
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
//u = A^-1 bu
u.SetSize(bu.Size());
u = bu;
LU_A.Solve(u.Size(), 1, u.GetData());
//p = -S^-1 (B A^-1 bu - bp)
p.SetSize(bp.Size());
B.Mult(u, p);
p -= bp;
LU_S.Solve(p.Size(), 1, p.GetData());
p.Neg();
//u += -A^-1 B^T S^-1 (B A^-1 bu - bp)
AiBtSiBAibu.SetSize(B.Width());
B.MultTranspose(p, AiBtSiBAibu);
LU_A.Solve(AiBtSiBAibu.Size(), 1, AiBtSiBAibu.GetData());
if (bsym) { u += AiBtSiBAibu; }
else { u -= AiBtSiBAibu; }
}
void DarcyHybridization::ConstructGrad(int el, const Array<int> &faces,
const BlockVector &x_l,
const Vector &u_l, const Vector &p_l) const
{
const FiniteElement *fe_u = fes->GetFE(el);
const FiniteElement *fe_p = fes_p->GetFE(el);
const int a_dofs_size = Af_f_offsets[el+1] - Af_f_offsets[el];
const int d_dofs_size = Df_f_offsets[el+1] - Df_f_offsets[el];
ElementTransformation *Tr = fes->GetElementTransformation(el);
DenseMatrix A(Af_data + Af_offsets[el], a_dofs_size, a_dofs_size);
DenseMatrix D(Df_data + Df_offsets[el], d_dofs_size, d_dofs_size);
LUFactors LU_A(A.GetData(), Af_ipiv + Af_f_offsets[el]);
if (m_nlfi)
{
Array<const FiniteElement*> fe_arr({fe_u, fe_p});
Array<const Vector*> x_arr({&u_l, &p_l});
Array2D<DenseMatrix*> grad_arr(2,2);
grad_arr(0,0) = &A;
grad_arr(1,0) = NULL;
grad_arr(0,1) = NULL;
grad_arr(1,1) = &D;
m_nlfi->AssembleElementGrad(fe_arr, *Tr, x_arr, grad_arr);
}
else
{
if (m_nlfi_u) { A = 0.; }
D = 0.;
}
if (m_nlfi_u)
{
DenseMatrix grad_A;
m_nlfi_u->AssembleElementGrad(*fe_u, *Tr, u_l, grad_A);
A += grad_A;
}
if (m_nlfi_p)
{
DenseMatrix grad_D;
m_nlfi_p->AssembleElementGrad(*fe_p, *Tr, p_l, grad_D);
D += grad_D;
}
else if (!D_empty)
{
DenseMatrix D_lin(Df_lin_data + Df_offsets[el], d_dofs_size, d_dofs_size);
D += D_lin;
}
if (c_nlfi_p)
{
//bp += E x
for (int f = 0; f < faces.Size(); f++)
{
FaceElementTransformations *FTr = fes->GetMesh()->GetFaceElementTransformations(
faces[f], 0);
const Vector &x_f = x_l.GetBlock(f);
if (FTr->Elem2No >= 0)
{
//interior
AssembleHDGGrad(el, faces[f], *c_nlfi_p, x_f, p_l);
}
else
{
//boundary
const int bdr_attr = fes->GetMesh()->GetBdrAttribute(f_2_b[faces[f]]);
for (int i = 0; i < boundary_constraint_pot_nonlin_integs.Size(); i++)
{
if (boundary_constraint_pot_nonlin_integs_marker[i]
&& (*boundary_constraint_pot_nonlin_integs_marker[i])[bdr_attr-1] == 0) { continue; }
AssembleHDGGrad(el, faces[f], *boundary_constraint_pot_nonlin_integs[i], x_f,
p_l);
}
}
}
}
if (m_nlfi_u || m_nlfi)
{
// Decompose A
LU_A.Factor(a_dofs_size);
}
// Construct Schur complement
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
DenseMatrix AiBt(a_dofs_size, d_dofs_size);
AiBt.Transpose(B);
if (!bsym) { AiBt.Neg(); }
LU_A.Solve(AiBt.Height(), AiBt.Width(), AiBt.GetData());
mfem::AddMult(B, AiBt, D);
// Decompose Schur complement
LUFactors LU_S(D.GetData(), Df_ipiv + Df_f_offsets[el]);
LU_S.Factor(d_dofs_size);
}
void DarcyHybridization::AssembleHDGGrad(int el, int f,
NonlinearFormIntegrator &nlfi, const Vector &x_f, const Vector &p_l) const
{
const FiniteElement *fe_c = c_fes->GetFaceElement(f);
const FiniteElement *fe_p = fes_p->GetFE(el);
const int d_dofs_size = Df_f_offsets[el+1] - Df_f_offsets[el];
const int c_dofs_size = x_f.Size();
FaceElementTransformations *FTr =
fes->GetMesh()->GetFaceElementTransformations(f);
int type = NonlinearFormIntegrator::HDGFaceType::ELEM
| NonlinearFormIntegrator::HDGFaceType::TRACE
| NonlinearFormIntegrator::HDGFaceType::CONSTR
| NonlinearFormIntegrator::HDGFaceType::FACE;
if (FTr->Elem1No != el) { type |= 1; }
DenseMatrix elmat;
nlfi.AssembleHDGFaceGrad(type, *fe_c, *fe_p, *FTr, x_f, p_l, elmat);
// assemble D element matrices
DenseMatrix D(Df_data + Df_offsets[el], d_dofs_size, d_dofs_size);
DenseMatrix elmat_D;
elmat_D.CopyMN(elmat, d_dofs_size, d_dofs_size, 0, 0);
D += elmat_D;
// assemble E constraint
const int E_off = (FTr->Elem1No == el)?(0):(c_dofs_size*d_dofs_size);
DenseMatrix E_f(E_data + E_offsets[f] + E_off, d_dofs_size, c_dofs_size);
E_f.CopyMN(elmat, d_dofs_size, c_dofs_size, 0, d_dofs_size);
// assemble G constraint
const int G_off = E_off;
DenseMatrix G_f(G_data + G_offsets[f] + G_off, c_dofs_size, d_dofs_size);
G_f.CopyMN(elmat, c_dofs_size, d_dofs_size, d_dofs_size, 0);
// assemble H matrix
DenseMatrix H_f(H_data + H_offsets[f], c_dofs_size, c_dofs_size);
DenseMatrix elmat_H;
elmat_H.CopyMN(elmat, c_dofs_size, c_dofs_size, d_dofs_size, d_dofs_size);
H_f += elmat_H;
}
void DarcyHybridization::ReduceRHS(const BlockVector &b, Vector &b_r) const
{
if (bnl)
{
//store RHS for Mult
if (!darcy_offsets.Size())
{
darcy_offsets.SetSize(3);
darcy_offsets[0] = 0;
darcy_offsets[1] = fes->GetVSize();
darcy_offsets[2] = fes_p->GetVSize();
darcy_offsets.PartialSum();
darcy_rhs.Update(darcy_offsets);
}
darcy_rhs = b;
if (b_r.Size() != Height())
{
b_r.SetSize(Height());
b_r = 0.;
}
return;
}
const int NE = fes->GetNE();
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
const int dim = fes->GetMesh()->Dimension();
DenseMatrix Ct_1, Ct_2, G_1, G_2;
Vector b_rl;
Array<int> c_dofs;
Array<int> faces, oris;
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
MFEM_ASSERT(!c_bfi_p,
"Potential constraint is not supported in non-block assembly!");
Vector hat_u(hat_offsets.Last());
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
Vector bu_l, bp_l, u_l, p_l;
Array<int> u_vdofs, p_dofs;
if (b_r.Size() != H->Height())
{
b_r.SetSize(H->Height());
b_r = 0.;
}
const Vector &bu = b.GetBlock(0);
const Vector &bp = b.GetBlock(1);
for (int el = 0; el < NE; el++)
{
// Load RHS
GetFDofs(el, u_vdofs);
bu.GetSubVector(u_vdofs, bu_l);
fes_p->GetElementDofs(el, p_dofs);
bp.GetSubVector(p_dofs, bp_l);
if (bsym)
{
//In the case of the symmetrized system, the sign is oppposite!
bp_l.Neg();
}
//-A^-1 bu - A^-1 B^T S^-1 B A^-1 bu
MultInv(el, bu_l, bp_l, u_l, p_l);
u_l.Neg();
p_l.Neg();
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
switch (dim)
{
case 1:
fes->GetMesh()->GetElementVertices(el, faces);
break;
case 2:
fes->GetMesh()->GetElementEdges(el, faces, oris);
break;
case 3:
fes->GetMesh()->GetElementFaces(el, faces, oris);
break;
}
// Mult C u + G p
for (int f = 0; f < faces.Size(); f++)
{
FaceElementTransformations *FTr = GetCtFaceMatrix(faces[f], Ct_1, Ct_2);
if (!FTr) { continue; }
DenseMatrix &Ct = (FTr->Elem1No == el)?(Ct_1):(Ct_2);
b_rl.SetSize(Ct.Width());
Ct.MultTranspose(u_l, b_rl);
if (c_bfi_p)
{
if (GetGFaceMatrix(faces[f], G_1, G_2))
{
DenseMatrix &G = (FTr->Elem1No == el)?(G_1):(G_2);
G.AddMult(p_l, b_rl);
}
}
c_fes->GetFaceVDofs(faces[f], c_dofs);
b_r.AddElementVector(c_dofs, b_rl);
}
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
int i = 0;
for (int dof = hat_offsets[el]; dof < hat_offsets[el+1]; dof++)
{
if (hat_dofs_marker[dof] == 1) { continue; }
hat_u[dof] = u_l[i++];
}
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
}
#ifndef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
Ct->MultTranspose(hat_u, b_r);
#endif //!MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
}
void DarcyHybridization::ComputeSolution(const BlockVector &b,
const Vector &sol_r, BlockVector &sol) const
{
if (bnl)
{
MultNL(MultNlMode::Sol, b, sol_r, sol);
return;
}
const int NE = fes->GetNE();
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
const int dim = fes->GetMesh()->Dimension();
DenseMatrix Ct_1, Ct_2, E_1, E_2;
Vector sol_rl;
Array<int> c_dofs;
Array<int> faces, oris;
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
MFEM_ASSERT(!c_bfi_p,
"Potential constraint is not supported in non-block assembly!");
Vector hat_bu(hat_offsets.Last());
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
Vector bu_l, bp_l, u_l, p_l;
Array<int> u_vdofs, p_dofs;
const Vector &bu = b.GetBlock(0);
const Vector &bp = b.GetBlock(1);
Vector &u = sol.GetBlock(0);
Vector &p = sol.GetBlock(1);
#ifndef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
Ct->Mult(sol_r, hat_bu);
#endif //!MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
for (int el = 0; el < NE; el++)
{
//Load RHS
GetFDofs(el, u_vdofs);
bu.GetSubVector(u_vdofs, bu_l);
fes_p->GetElementDofs(el, p_dofs);
bp.GetSubVector(p_dofs, bp_l);
if (bsym)
{
//In the case of the symmetrized system, the sign is oppposite!
bp_l.Neg();
}
#ifdef MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
switch (dim)
{
case 1:
fes->GetMesh()->GetElementVertices(el, faces);
break;
case 2:
fes->GetMesh()->GetElementEdges(el, faces, oris);
break;
case 3:
fes->GetMesh()->GetElementFaces(el, faces, oris);
break;
}
// bu - C^T sol
for (int f = 0; f < faces.Size(); f++)
{
FaceElementTransformations *FTr = GetCtFaceMatrix(faces[f], Ct_1, Ct_2);
if (!FTr) { continue; }
c_fes->GetFaceVDofs(faces[f], c_dofs);
sol_r.GetSubVector(c_dofs, sol_rl);
DenseMatrix &Ct = (FTr->Elem1No == el)?(Ct_1):(Ct_2);
Ct.AddMult_a(-1., sol_rl, bu_l);
//bp - E sol
if (c_bfi_p)
{
if (GetEFaceMatrix(faces[f], E_1, E_2))
{
DenseMatrix &E = (FTr->Elem1No == el)?(E_1):(E_2);
E.AddMult_a(-1., sol_rl, bp_l);
}
}
}
#else //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
// bu - C^T sol
int i = 0;
for (int dof = hat_offsets[el]; dof < hat_offsets[el+1]; dof++)
{
if (hat_dofs_marker[dof] == 1) { continue; }
bu_l[i++] -= hat_bu[dof];
}
#endif //MFEM_DARCY_HYBRIDIZATION_CT_BLOCK
//(A^-1 - A^-1 B^T S^-1 B A^-1) (bu - C^T sol)
MultInv(el, bu_l, bp_l, u_l, p_l);
u.SetSubVector(u_vdofs, u_l);
p.SetSubVector(p_dofs, p_l);
}
}
void DarcyHybridization::Reset()
{
Hybridization::Reset();
bfin = false;
const int NE = fes->GetMesh()->GetNE();
memset(Bf_data, 0, Bf_offsets[NE] * sizeof(real_t));
if (Df_data)
{
memset(Df_data, 0, Df_offsets[NE] * sizeof(real_t));
D_empty = true;
}
#ifdef MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
memset(Be_data, 0, Be_offsets[NE] * sizeof(real_t));
#endif //MFEM_DARCY_HYBRIDIZATION_ELIM_BCS
}
void DarcyHybridization::Gradient::Mult(const Vector &x, Vector &y) const
{
//note that rhs is not used, it is only a dummy
dh.MultNL(MultNlMode::GradMult, dh.darcy_rhs, x, y);
}
DarcyHybridization::LocalNLOperator::LocalNLOperator(
const DarcyHybridization &dh_, int el_, const BlockVector &trps_,
const Array<int> &faces_)
: dh(dh_), el(el_), trps(trps_), faces(faces_),
a_dofs_size(dh.Af_f_offsets[el+1] - dh.Af_f_offsets[el]),
d_dofs_size(dh.Df_f_offsets[el+1] - dh.Df_f_offsets[el]),
B(dh.Bf_data + dh.Bf_offsets[el], d_dofs_size, a_dofs_size),
Bt(B), offsets({0, a_dofs_size, a_dofs_size+d_dofs_size}), grad(offsets)
{
width = height = a_dofs_size + d_dofs_size;
fe_u = dh.fes->GetFE(el);
fe_p = dh.fes_p->GetFE(el);
// element transformation
Tr = new IsoparametricTransformation();
if (faces.Size() <= 0)
{
dh.fes_p->GetMesh()->GetElementTransformation(el, Tr);
}
// face transformations
FTrs.SetSize(faces.Size());
NbrTrs.SetSize(faces.Size());
for (int f = 0; f < faces.Size(); f++)
{
FaceElementTransformations *&FTr = FTrs[f];
FTr = new FaceElementTransformations();
dh.fes_p->GetMesh()->GetFaceElementTransformations(faces[f], *FTr, *Tr, *Tr, 0);
IsoparametricTransformation *Tr1, *Tr2;
if (FTr->Elem2No >= 0)
{
NbrTrs[f] = new IsoparametricTransformation();
if (FTr->Elem1No == el)
{
Tr1 = Tr;
Tr2 = NbrTrs[f];
}
else
{
Tr1 = NbrTrs[f];
Tr2 = Tr;
}
}
else
{
NbrTrs[f] = NULL;
Tr1 = Tr2 = Tr;
}
dh.fes_p->GetMesh()->GetFaceElementTransformations(faces[f], *FTr, *Tr1, *Tr2);
}
}
DarcyHybridization::LocalNLOperator::~LocalNLOperator()
{
delete Tr;
for (int f = 0; f < faces.Size(); f++)
{
delete FTrs[f];
delete NbrTrs[f];
}
}
void DarcyHybridization::LocalNLOperator::AddMultA(const Vector &u_l,
Vector &bu) const
{
//bu += A u_l
if (dh.m_nlfi_u)
{
dh.m_nlfi_u->AssembleElementVector(*fe_u, *Tr, u_l, Au);
bu += Au;
}
}
void DarcyHybridization::LocalNLOperator::AddMultDE(const Vector &p_l,
Vector &bp) const
{
//bp += D p_l
if (dh.m_nlfi_p)
{
dh.m_nlfi_p->AssembleElementVector(*fe_p, *Tr, p_l, Dp);
bp += Dp;
}
else if (!dh.D_empty)
{
DenseMatrix D(dh.Df_lin_data + dh.Df_offsets[el], d_dofs_size, d_dofs_size);
D.AddMult(p_l, bp);
}
if (dh.c_nlfi_p)
{
//bp += E x
for (int f = 0; f < faces.Size(); f++)
{
FaceElementTransformations *FTr = FTrs[f];
int type = NonlinearFormIntegrator::HDGFaceType::ELEM
| NonlinearFormIntegrator::HDGFaceType::TRACE;
const Vector &trp_f = trps.GetBlock(f);
if (FTr->Elem2No >= 0)
{
//interior
if (FTr->Elem1No != el) { type |= 1; }
dh.c_nlfi_p->AssembleHDGFaceVector(type, *dh.c_fes->GetFaceElement(faces[f]),
*fe_p, *FTr, trp_f, p_l, DpEx);
bp += DpEx;
}
else
{
//boundary
const int bdr_attr = dh.fes->GetMesh()->GetBdrAttribute(dh.f_2_b[faces[f]]);
for (int i = 0; i < dh.boundary_constraint_pot_nonlin_integs.Size(); i++)
{
if (dh.boundary_constraint_pot_nonlin_integs_marker[i]
&& (*dh.boundary_constraint_pot_nonlin_integs_marker[i])[bdr_attr-1] == 0) { continue; }
dh.boundary_constraint_pot_nonlin_integs[i]->AssembleHDGFaceVector(type,
*dh.c_fes->GetFaceElement(faces[f]),
*fe_p, *FTr, trp_f, p_l, DpEx);
bp += DpEx;
}
}
}
}
}
void DarcyHybridization::LocalNLOperator::AddGradA(const Vector &u_l,
DenseMatrix &grad) const
{
//grad += A
if (dh.m_nlfi_u)
{
DenseMatrix grad_A;
dh.m_nlfi_u->AssembleElementGrad(*fe_u, *Tr, u_l, grad_A);
grad += grad_A;
}
}
void DarcyHybridization::LocalNLOperator::AddGradDE(const Vector &p_l,
DenseMatrix &grad) const
{
//grad += D
if (dh.m_nlfi_p)
{
DenseMatrix grad_D;
dh.m_nlfi_p->AssembleElementGrad(*fe_p, *Tr, p_l, grad_D);
grad += grad_D;
}
else if (!dh.D_empty)
{
DenseMatrix D(dh.Df_lin_data + dh.Df_offsets[el], d_dofs_size, d_dofs_size);
grad += D;
}
if (dh.c_nlfi_p)
{
DenseMatrix grad_Df;
//grad += D_f
for (int f = 0; f < faces.Size(); f++)
{
FaceElementTransformations *FTr = FTrs[f];
int type = NonlinearFormIntegrator::HDGFaceType::ELEM;
const Vector &trp_f = trps.GetBlock(f);
if (FTr->Elem2No >= 0)
{
//interior
if (FTr->Elem1No != el) { type |= 1; }
dh.c_nlfi_p->AssembleHDGFaceGrad(type, *dh.c_fes->GetFaceElement(faces[f]),
*fe_p, *FTr, trp_f, p_l, grad_Df);
grad += grad_Df;
}
else
{
//boundary
const int bdr_attr = dh.fes->GetMesh()->GetBdrAttribute(dh.f_2_b[faces[f]]);
for (int i = 0; i < dh.boundary_constraint_pot_nonlin_integs.Size(); i++)
{
if (dh.boundary_constraint_pot_nonlin_integs_marker[i]
&& (*dh.boundary_constraint_pot_nonlin_integs_marker[i])[bdr_attr-1] == 0) { continue; }
dh.boundary_constraint_pot_nonlin_integs[i]->AssembleHDGFaceGrad(type,
*dh.c_fes->GetFaceElement(faces[f]),
*fe_p, *FTr, trp_f, p_l, grad_Df);
grad += grad_Df;
}
}
}
}
}
void DarcyHybridization::LocalNLOperator::Mult(const Vector &x, Vector &y) const
{
MFEM_ASSERT(x.Size() == Width() && y.Size() == Height(), "Incompatible size");
const BlockVector x_l(const_cast<Vector&>(x), offsets);
const Vector &u_l = x_l.GetBlock(0);
const Vector &p_l = x_l.GetBlock(1);
BlockVector b(y, offsets);
Vector &bu = b.GetBlock(0);
Vector &bp = b.GetBlock(1);
//bu = B^T p
B.MultTranspose(p_l, bu);
if (dh.bsym) { bu.Neg(); }
//bu += A u
AddMultA(u_l, bu);
//bp = B u
B.Mult(u_l, bp);
//bp += D p
AddMultDE(p_l, bp);
//bu += A u_l - B^T p_l
//bp += B u_l + D p_l
if (dh.m_nlfi)
{
Array<const FiniteElement*> fe_arr({fe_u, fe_p});
Array<const Vector*> x_arr({&u_l, &p_l});
Array<Vector*> y_arr({&Au, &Dp});
dh.m_nlfi->AssembleElementVector(fe_arr, *Tr, x_arr, y_arr);
bu += Au;
bp += Dp;
}
}
Operator &DarcyHybridization::LocalNLOperator::GetGradient(
const Vector &x) const
{
MFEM_ASSERT(x.Size() == Width(), "Incompatible size");
const BlockVector x_l(const_cast<Vector&>(x), offsets);
const Vector &u_l = x_l.GetBlock(0);
const Vector &p_l = x_l.GetBlock(1);
if (dh.m_nlfi)
{
Array<const FiniteElement*> fe_arr({fe_u, fe_p});
Array<const Vector*> x_arr({&u_l, &p_l});
Array2D<DenseMatrix*> grad_arr(2,2);
grad_arr(0,0) = &grad_A;
grad_arr(1,0) = NULL;
grad_arr(0,1) = NULL;
grad_arr(1,1) = &grad_D;
dh.m_nlfi->AssembleElementGrad(fe_arr, *Tr, x_arr, grad_arr);
}
else
{
grad_A.SetSize(a_dofs_size);
grad_A = 0.;
grad_D.SetSize(d_dofs_size);
grad_D = 0.;
}
//A
AddGradA(u_l, grad_A);
grad.SetDiagonalBlock(0, &grad_A);
//B
grad.SetBlock(1, 0, &const_cast<DenseMatrix&>(B));
//B^T
grad.SetBlock(0, 1, &const_cast<TransposeOperator&>(Bt), (dh.bsym)?(-1.):(+1.));
//D
AddGradDE(p_l, grad_D);
grad.SetDiagonalBlock(1, &grad_D);
return grad;
}
DarcyHybridization::LocalFluxNLOperator::LocalFluxNLOperator(
const DarcyHybridization &dh_, int el_, const Vector &bp_,
const BlockVector &trps_, const Array<int> &faces_)
: LocalNLOperator(dh_, el_, trps_, faces_), bp(bp_),
LU_D(dh.Df_data + dh.Df_offsets[el], dh.Df_ipiv + dh.Df_f_offsets[el])
{
MFEM_ASSERT(bp.Size() == d_dofs_size, "Incompatible size");
width = height = a_dofs_size;
}
void DarcyHybridization::LocalFluxNLOperator::SolveP(const Vector &u_l,
Vector &p_l) const
{
p_l = bp;
//bp - E x - B^T p
B.AddMult(u_l, p_l, -1.);
//p = D^-1 rp
LU_D.Solve(d_dofs_size, 1, p_l.GetData());
}
void DarcyHybridization::LocalFluxNLOperator::Mult(const Vector &u_l,
Vector &bu) const
{
MFEM_ASSERT(u_l.Size() == a_dofs_size &&
bu.Size() == a_dofs_size, "Incompatible size");
SolveP(u_l, p_l);
//bu = B^T p
B.MultTranspose(p_l, bu);
if (dh.bsym) { bu.Neg(); }
AddMultA(u_l, bu);
}
Operator &DarcyHybridization::LocalFluxNLOperator::GetGradient(
const Vector &u_l) const
{
MFEM_ASSERT(u_l.Size() == a_dofs_size, "Incompatible size");
SolveP(u_l, p_l);
//grad = B^T D^-1 B
DenseMatrix DiB = B;
LU_D.Solve(d_dofs_size, a_dofs_size, DiB.GetData());
grad_A.SetSize(a_dofs_size);
MultAtB(B, DiB, grad_A);
if (!dh.bsym) { grad_A.Neg(); }
//grad += A
AddGradA(u_l, grad_A);
return grad_A;
}
DarcyHybridization::LocalPotNLOperator::LocalPotNLOperator(
const DarcyHybridization &dh_, int el_, const Vector &bu_,
const BlockVector &trps_, const Array<int> &faces_)
: LocalNLOperator(dh_, el_, trps_, faces_), bu(bu_),
LU_A(dh.Af_data + dh.Af_offsets[el], dh.Af_ipiv + dh.Af_f_offsets[el])
{
MFEM_ASSERT(bu.Size() == a_dofs_size, "Incompatible size");
width = height = d_dofs_size;
}
void DarcyHybridization::LocalPotNLOperator::SolveU(const Vector &p_l,
Vector &u_l) const
{
u_l = bu;
//bu - C^T x + B^T p
B.AddMultTranspose(p_l, u_l, (dh.bsym)?(+1.):(-1.));
//u = A^-1 ru
LU_A.Solve(a_dofs_size, 1, u_l.GetData());
}
void DarcyHybridization::LocalPotNLOperator::Mult(const Vector &p_l,
Vector &bp) const
{
MFEM_ASSERT(p_l.Size() == d_dofs_size &&
bp.Size() == d_dofs_size, "Incompatible size");
SolveU(p_l, u_l);
//bp = B u
B.Mult(u_l, bp);
AddMultDE(p_l, bp);
}
Operator &DarcyHybridization::LocalPotNLOperator::GetGradient(
const Vector &p_l) const
{
MFEM_ASSERT(p_l.Size() == d_dofs_size, "Incompatible size");
SolveU(p_l, u_l);
//grad = B A^-1 B^T
DenseMatrix BAi = B;
LU_A.RightSolve(a_dofs_size, d_dofs_size, BAi.GetData());
grad_D.SetSize(d_dofs_size);
MultABt(BAi, B, grad_D);
if (!dh.bsym) { grad_D.Neg(); }
//grad += D
AddGradDE(p_l, grad_D);
return grad_D;
}
}