Files
mfem/examples/darcyreduction.cpp
T

761 lines
20 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 "darcyreduction.hpp"
namespace mfem
{
DarcyReduction::DarcyReduction(FiniteElementSpace *fes_u_,
FiniteElementSpace *fes_p_, bool bsym_)
: fes_u(fes_u_), fes_p(fes_p_), bsym(bsym_)
{
m_nlfi_u = NULL;
m_nlfi_p = NULL;
own_m_nlfi_u = false;
own_m_nlfi_p = false;
Af_data = NULL;
Bf_data = NULL;
Df_data = NULL;
S = NULL;
}
DarcyReduction::~DarcyReduction()
{
if (own_m_nlfi_u) { delete m_nlfi_u; }
if (own_m_nlfi_p) { delete m_nlfi_p; }
delete[] Af_data;
delete[] Bf_data;
delete[] Df_data;
delete S;
}
void DarcyReduction::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;
}
void DarcyReduction::SetPotMassNonlinearIntegrator(NonlinearFormIntegrator
*pot_integ, bool own)
{
if (own_m_nlfi_p) { delete m_nlfi_p; }
own_m_nlfi_p = own;
m_nlfi_p = pot_integ;
}
void DarcyReduction::InitA()
{
const int NE = fes_u->GetNE();
// 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 = fes_u->GetFE(i)->GetDof() * fes_u->GetVDim();
Af_offsets[i+1] = Af_offsets[i] + f_size*f_size;
Af_f_offsets[i+1] = Af_f_offsets[i] + f_size;
}
if (!m_nlfi_u)
{
Af_data = new real_t[Af_offsets[NE]];
}
}
void DarcyReduction::InitBD()
{
const int NE = fes_u->GetNE();
// 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;
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;
}
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
}
}
void DarcyReduction::Init(const Array<int> &)
{
InitA();
InitBD();
}
void DarcyReduction::AssembleFluxMassMatrix(int el, const DenseMatrix &A)
{
const int s = Af_f_offsets[el+1] - Af_f_offsets[el];
DenseMatrix A_i(Af_data + Af_offsets[el], s, s);
MFEM_ASSERT(A.Size() == s, "Incompatible sizes");
A_i = A;
}
void DarcyReduction::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;
}
void DarcyReduction::AssembleDivMatrix(int el, const DenseMatrix &B)
{
const int w = Af_f_offsets[el+1] - Af_f_offsets[el];
const int h = Df_f_offsets[el+1] - Df_f_offsets[el];
DenseMatrix B_i(Bf_data + Bf_offsets[el], h, w);
MFEM_ASSERT(B.Width() == w && B.Height() == h, "Incompatible sizes");
B_i += B;
}
void DarcyReduction::Mult(const Vector &x, Vector &y) const
{
S->Mult(x, y);
}
void DarcyReduction::Finalize()
{
if (!S) { ComputeS(); }
}
void DarcyReduction::Reset()
{
delete S;
S = NULL;
const int NE = fes_u->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));
}
}
DarcyFluxReduction::DarcyFluxReduction(FiniteElementSpace *fes_u,
FiniteElementSpace *fes_p, bool bsym)
: DarcyReduction(fes_u, fes_p, bsym)
{
width = height = fes_p->GetVSize();
Af_ipiv = NULL;
}
DarcyFluxReduction::~DarcyFluxReduction()
{
delete[] Af_ipiv;
}
void DarcyFluxReduction::Init(const Array<int> &ess_flux_tdof_list)
{
MFEM_ASSERT(ess_flux_tdof_list.Size() == 0,
"Essential VDOFs are not supported");
DarcyReduction::Init(ess_flux_tdof_list);
const int NE = fes_u->GetNE();
Af_ipiv = new int[Af_f_offsets[NE]];
}
void DarcyFluxReduction::ComputeS()
{
MFEM_ASSERT(!m_nlfi_u && !m_nlfi_p,
"Cannot assemble S matrix in the non-linear regime");
const int skip_zeros = 1;
const int NE = fes_u->GetNE();
if (!S) { S = new SparseMatrix(fes_p->GetVSize()); }
DenseMatrix AiBt;
Array<int> p_dofs;
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];
DenseMatrix D(Df_data + Df_offsets[el], d_dofs_size, d_dofs_size);
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
// Decompose A
LUFactors LU_A(Af_data + Af_offsets[el], Af_ipiv + Af_f_offsets[el]);
LU_A.Factor(a_dofs_size);
// Schur complement
AiBt.Transpose(B);
if (!bsym) { AiBt.Neg(); }
LU_A.Solve(AiBt.Height(), AiBt.Width(), AiBt.GetData());
mfem::AddMult(B, AiBt, D);
fes_p->GetElementDofs(el, p_dofs);
S->AddSubMatrix(p_dofs, p_dofs, D, skip_zeros);
}
S->Finalize();
}
void DarcyFluxReduction::ReduceRHS(const BlockVector &b, Vector &b_r) const
{
const int NE = fes_u->GetNE();
Vector bu_l, bp_l;
Array<int> u_vdofs, p_dofs;
const Vector &bu = b.GetBlock(0);
const Vector &bp = b.GetBlock(1);
if (b_r.Size() != S->Height())
{
b_r.SetSize(S->Height());
}
for (int el = 0; el < NE; el++)
{
// Load RHS
fes_u->GetElementVDofs(el, u_vdofs);
bu.GetSubVector(u_vdofs, bu_l);
fes_p->GetElementDofs(el, p_dofs);
bp.GetSubVector(p_dofs, bp_l);
// -B A^-1 bu
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];
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
LUFactors LU_A(Af_data + Af_offsets[el], Af_ipiv + Af_f_offsets[el]);
LU_A.Solve(a_dofs_size, 1, bu_l.GetData());
B.AddMult(bu_l, bp_l, (bsym)?(+1.):(-1.));
b_r.SetSubVector(p_dofs, bp_l);
}
}
void DarcyFluxReduction::ComputeSolution(const BlockVector &b,
const Vector &sol_r,
BlockVector &sol) const
{
const int NE = fes_u->GetNE();
Vector bu_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);
p = sol_r;
for (int el = 0; el < NE; el++)
{
//Load RHS
fes_u->GetElementVDofs(el, u_vdofs);
bu.GetSubVector(u_vdofs, bu_l);
fes_p->GetElementDofs(el, p_dofs);
p.GetSubVector(p_dofs, p_l);
// A^-1 (R - B^T p)
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];
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
LUFactors LU_A(Af_data + Af_offsets[el], Af_ipiv + Af_f_offsets[el]);
B.AddMultTranspose(p_l, bu_l, (bsym)?(-1.):(+1.));
LU_A.Solve(a_dofs_size, 1, bu_l.GetData());
u.SetSubVector(u_vdofs, bu_l);
}
}
DarcyPotentialReduction::DarcyPotentialReduction(FiniteElementSpace *fes_u,
FiniteElementSpace *fes_p, bool bsym)
: DarcyReduction(fes_u, fes_p, bsym)
{
width = height = fes_u->GetVSize();
Ae_data = NULL;
Be_data = NULL;
Df_ipiv = NULL;
}
DarcyPotentialReduction::~DarcyPotentialReduction()
{
delete[] Ae_data;
delete[] Be_data;
delete[] Df_ipiv;
}
void DarcyPotentialReduction::Init(const Array<int> &ess_flux_tdof_list)
{
const int NE = fes_p->GetNE();
// 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_u->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_u);
free_tdof_marker.SetSize(pfes ? pfes->TrueVSize() :
fes_u->GetConformingVSize());
#else
free_tdof_marker.SetSize(fes_u->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_u->GetConformingProlongation();
if (!cP)
{
free_vdofs_marker.MakeRef(free_tdof_marker);
}
else
{
free_vdofs_marker.SetSize(fes_u->GetVSize());
cP->BooleanMult(free_tdof_marker, free_vdofs_marker);
}
}
else
{
HypreParMatrix *P = pfes->Dof_TrueDof_Matrix();
free_vdofs_marker.SetSize(fes_u->GetVSize());
P->BooleanMult(1, free_tdof_marker, 0, free_vdofs_marker);
}
#else
const SparseMatrix *cP = fes_u->GetConformingProlongation();
if (!cP)
{
free_vdofs_marker.MakeRef(free_tdof_marker);
}
else
{
free_vdofs_marker.SetSize(fes_u->GetVSize());
cP->BooleanMult(free_tdof_marker, free_vdofs_marker);
}
#endif
for (int i = 0; i < NE; i++)
{
fes_u->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;
#ifdef MFEM_DARCY_REDUCTION_ELIM_BCS
Ae_offsets.SetSize(NE+1);
Ae_offsets[0] = 0;
Be_offsets.SetSize(NE+1);
Be_offsets[0] = 0;
#endif //MFEM_DARCY_REDUCTION_ELIM_BCS
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;
#ifdef MFEM_DARCY_REDUCTION_ELIM_BCS
int a_size = hat_offsets[i+1] - hat_offsets[i];
int e_size = a_size - f_size;
int d_size = fes_p->GetFE(i)->GetDof();
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_REDUCTION_ELIM_BCS
}
if (!m_nlfi_u)
{
Af_data = new real_t[Af_offsets[NE]];
#ifdef MFEM_DARCY_REDUCTION_ELIM_BCS
Ae_data = new real_t[Ae_offsets[NE]];
#endif //MFEM_DARCY_REDUCTION_ELIM_BCS
}
InitBD();
#ifdef MFEM_DARCY_REDUCTION_ELIM_BCS
Be_data = new real_t[Be_offsets[NE]]();//init by zeros
#endif //MFEM_DARCY_REDUCTION_ELIM_BCS
Df_ipiv = new int[Df_f_offsets[NE]];
}
void DarcyPotentialReduction::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_u->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 DarcyPotentialReduction::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_u->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 DarcyPotentialReduction::ComputeS()
{
MFEM_ASSERT(!m_nlfi_u && !m_nlfi_p,
"Cannot assemble S matrix in the non-linear regime");
const int skip_zeros = 1;
const int NE = fes_u->GetNE();
if (!S) { S = new SparseMatrix(fes_u->GetVSize()); }
DenseMatrix DiB;
Array<int> a_dofs;
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];
DenseMatrix A(Af_data + Af_offsets[el], a_dofs_size, a_dofs_size);
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
// Decompose D
LUFactors LU_D(Df_data + Df_offsets[el], Df_ipiv + Df_f_offsets[el]);
LU_D.Factor(d_dofs_size);
// Schur complement
DiB = B;
if (!bsym) { DiB.Neg(); }
LU_D.Solve(DiB.Height(), DiB.Width(), DiB.GetData());
mfem::AddMultAtB(B, DiB, A);
GetFDofs(el, a_dofs);
S->AddSubMatrix(a_dofs, a_dofs, A, skip_zeros);
// Complete the diagonal
GetEDofs(el, a_dofs);
FiniteElementSpace::AdjustVDofs(a_dofs);
for (int i = 0; i < a_dofs.Size(); i++)
{
S->Set(a_dofs[i], a_dofs[i], 1.);
}
}
S->Finalize();
}
void DarcyPotentialReduction::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_REDUCTION_ELIM_BCS
real_t *Ae_el_data = Ae_data + Ae_offsets[el];
#endif //MFEM_DARCY_REDUCTION_ELIM_BCS
for (int j = 0; j < s; j++)
{
if (hat_dofs_marker[o + j] == 1)
{
#ifdef MFEM_DARCY_REDUCTION_ELIM_BCS
for (int i = 0; i < s; i++)
{
*(Ae_el_data++) = A(i, j);
}
#endif //MFEM_DARCY_REDUCTION_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_REDUCTION_ELIM_BCS
MFEM_ASSERT(Ae_el_data == Ae_data + Ae_offsets[el+1], "Internal error");
#endif //MFEM_DARCY_REDUCTION_ELIM_BCS
}
void DarcyPotentialReduction::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_REDUCTION_ELIM_BCS
real_t *Be_el_data = Be_data + Be_offsets[el];
#endif //MFEM_DARCY_REDUCTION_ELIM_BCS
for (int j = 0; j < w; j++)
{
if (hat_dofs_marker[o + j] == 1)
{
#ifdef MFEM_DARCY_REDUCTION_ELIM_BCS
for (int i = 0; i < h; i++)
{
*(Be_el_data++) += B(i, j);
}
#endif //MFEM_DARCY_REDUCTION_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_REDUCTION_ELIM_BCS
MFEM_ASSERT(Be_el_data == Be_data + Be_offsets[el+1], "Internal error");
#endif //MFEM_DARCY_REDUCTION_ELIM_BCS
}
void DarcyPotentialReduction::EliminateVDofsInRHS(const Array<int> &vdofs_flux,
const BlockVector &x, BlockVector &b)
{
const int NE = fes_u->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_u->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 DarcyPotentialReduction::ReduceRHS(const BlockVector &b, Vector &b_r) const
{
const int NE = fes_u->GetNE();
Vector bu_l, bp_l;
Array<int> u_vdofs, p_dofs;
const Vector &bu = b.GetBlock(0);
const Vector &bp = b.GetBlock(1);
b_r = bu;
for (int el = 0; el < NE; el++)
{
// Load RHS
GetFDofs(el, u_vdofs);
bu_l.SetSize(u_vdofs.Size());
fes_p->GetElementDofs(el, p_dofs);
bp.GetSubVector(p_dofs, bp_l);
// -B^T D^-1 bp
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];
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
LUFactors LU_D(Df_data + Df_offsets[el], Df_ipiv + Df_f_offsets[el]);
LU_D.Solve(d_dofs_size, 1, bp_l.GetData());
bp_l.Neg();
B.MultTranspose(bp_l, bu_l);
b_r.AddElementVector(u_vdofs, bu_l);
}
}
void DarcyPotentialReduction::ComputeSolution(const BlockVector &b,
const Vector &sol_r,
BlockVector &sol) const
{
const int NE = fes_u->GetNE();
Vector bp_l, u_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);
u = sol_r;
for (int el = 0; el < NE; el++)
{
//Load RHS
GetFDofs(el, u_vdofs);
u.GetSubVector(u_vdofs, u_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();
}
// D^-1 (F - B u)
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];
DenseMatrix B(Bf_data + Bf_offsets[el], d_dofs_size, a_dofs_size);
LUFactors LU_D(Df_data + Df_offsets[el], Df_ipiv + Df_f_offsets[el]);
B.AddMult(u_l, bp_l, -1.);
LU_D.Solve(d_dofs_size, 1, bp_l.GetData());
p.SetSubVector(p_dofs, bp_l);
}
}
void DarcyPotentialReduction::Reset()
{
DarcyReduction::Reset();
#ifdef MFEM_DARCY_REDUCTION_ELIM_BCS
const int NE = fes_p->GetNE();
memset(Be_data, 0, Be_offsets[NE] * sizeof(real_t));
#endif //MFEM_DARCY_REDUCTION_ELIM_BCS
}
}