Files
mfem/examples/hyperbolic_conservation_laws.hpp
T

668 lines
24 KiB
C++

// MFEM Example 18 - Serial/Parallel Shared Code
#include "mfem.hpp"
using namespace std;
using namespace mfem;
// Problem definition
// extern int problem;
// Maximum characteristic speed (updated by integrators)
// Abstract Numerical flux hat{F}(u-, u+).
// Eval: state (u-, u+), flux (Fu-, Fu+), speed, face normal |-> flux
class NumericalFlux {
public:
NumericalFlux(){};
virtual void Eval(const Vector &state1, const Vector &state2,
const Vector &flux1, const Vector &flux2, const double maxE,
const Vector &nor, Vector &flux) {
mfem_error("Not Implemented");
}
};
// Element term: (F(u), grad v)
class HyperbolicElementFormIntegrator : public NonlinearFormIntegrator {
private:
const int num_equations;
double *max_char_speed;
const int IntOrderOffset;
Vector shape;
Vector state;
DenseMatrix flux;
DenseMatrix dshape;
protected:
virtual double ComputeFlux(const Vector &state, ElementTransformation &Tr,
DenseMatrix &flux) = 0;
public:
HyperbolicElementFormIntegrator(const int dim, const int num_equations_,
const int IntOrderOffset_ = 3)
: NonlinearFormIntegrator(),
num_equations(num_equations_),
IntOrderOffset(IntOrderOffset_),
state(num_equations_),
flux(num_equations_, dim){};
HyperbolicElementFormIntegrator(const int dim, const int num_equations_,
const IntegrationRule *ir)
: NonlinearFormIntegrator(ir),
num_equations(num_equations_),
IntOrderOffset(0),
state(num_equations_),
flux(num_equations_, dim){};
const IntegrationRule &GetRule(const FiniteElement &el) {
int order;
order = 2 * el.GetOrder() + IntOrderOffset;
return IntRules.Get(el.GetGeomType(), order);
}
void setMaxCharSpeed(double &max_char_speed_) {
max_char_speed = &max_char_speed_;
}
virtual void AssembleElementVector(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun, Vector &elvect);
virtual ~HyperbolicElementFormIntegrator() {}
};
// Interior face term: <hat{F}.n,[w]>
// where hat{F}.n is determined by NumericalFlux rsolver.
class HyperbolicFaceFormIntegrator : public NonlinearFormIntegrator {
private:
const int num_equations;
double *max_char_speed;
const int IntOrderOffset;
NumericalFlux *rsolver;
Vector shape1;
Vector shape2;
Vector state1;
Vector state2;
Vector flux1;
Vector flux2;
Vector nor;
Vector fluxN;
protected:
virtual double ComputeFluxDotN(const Vector &state, const Vector &nor,
ElementTransformation &Tr, Vector &flux) = 0;
public:
HyperbolicFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
const int num_equations_,
const int IntOrderOffset_ = 3)
: NonlinearFormIntegrator(),
num_equations(num_equations_),
IntOrderOffset(IntOrderOffset_),
rsolver(rsolver_),
state1(num_equations_),
state2(num_equations_),
flux1(num_equations_),
flux2(num_equations_),
nor(dim),
fluxN(num_equations_){};
HyperbolicFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
const int num_equations_,
const IntegrationRule *ir)
: NonlinearFormIntegrator(ir),
num_equations(num_equations_),
max_char_speed(),
IntOrderOffset(0),
rsolver(rsolver_),
state1(num_equations_),
state2(num_equations_),
flux1(num_equations_),
flux2(num_equations_),
nor(dim),
fluxN(num_equations_){};
const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe) {
int order;
order = trial_fe.GetOrder() + test_fe.GetOrder() + IntOrderOffset;
return IntRules.Get(trial_fe.GetGeomType(), order);
}
void setMaxCharSpeed(double &max_char_speed_) {
max_char_speed = &max_char_speed_;
}
virtual void AssembleFaceVector(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Tr,
const Vector &elfun, Vector &elvect);
virtual ~HyperbolicFaceFormIntegrator() {}
};
// Base Hyperbolic conservation law class.
// This contains all methods needed except the flux function.
class DGHyperbolicConservationLaws : public TimeDependentOperator {
private:
const int dim;
const int num_equations;
// Vector finite element space containing conserved variables
FiniteElementSpace *vfes;
// Element integration form. Should contain ComputeFlux
HyperbolicElementFormIntegrator &elementFormIntegrator;
// Face integration form. Should contain ComputeFluxDotN and Riemann Solver
HyperbolicFaceFormIntegrator &faceFormIntegrator;
// Base Nonlinear Form
NonlinearForm *nonlinearForm;
// element-wise inverse mass matrix
std::vector<DenseMatrix> Me_inv;
// global maximum characteristic speed. Updated by form integrators
mutable double max_char_speed;
mutable Vector z;
// Compute element-wise inverse mass matrix
void ComputeInvMass();
public:
// Constructor
DGHyperbolicConservationLaws(
FiniteElementSpace *vfes_, NonlinearForm *nonlinForm_,
HyperbolicElementFormIntegrator &elementFormIntegrator_,
HyperbolicFaceFormIntegrator &faceFormIntegrator_,
const int num_equations_);
// Apply M\(DIV F(U) + JUMP HAT{F}(U))
virtual void Mult(const Vector &x, Vector &y) const;
// Update operators when mesh and finite element spaces are updated
void Update();
inline double getMaxCharSpeed() { return max_char_speed; }
virtual ~DGHyperbolicConservationLaws() {}
};
//////////////////////////////////////////////////////////////////
/// HYPERBOLIC CONSERVATION LAWS IMPLEMENTATION ///
//////////////////////////////////////////////////////////////////
// Implementation of class DGHyperbolicConservationLaws
DGHyperbolicConservationLaws::DGHyperbolicConservationLaws(
FiniteElementSpace *vfes_, NonlinearForm *nonlinearForm_,
HyperbolicElementFormIntegrator &elementFormIntegrator_,
HyperbolicFaceFormIntegrator &faceFormIntegrator_, const int num_equations_)
: TimeDependentOperator(vfes_->GetNDofs() * num_equations_),
dim(vfes_->GetFE(0)->GetDim()),
num_equations(num_equations_),
vfes(vfes_),
elementFormIntegrator(elementFormIntegrator_),
faceFormIntegrator(faceFormIntegrator_),
nonlinearForm(nonlinearForm_),
Me_inv(0),
z(vfes_->GetNDofs() * num_equations_) {
// Standard local assembly and inversion for energy mass matrices.
ComputeInvMass();
elementFormIntegrator.setMaxCharSpeed(max_char_speed);
faceFormIntegrator.setMaxCharSpeed(max_char_speed);
nonlinearForm->AddDomainIntegrator(&elementFormIntegrator);
nonlinearForm->AddInteriorFaceIntegrator(&faceFormIntegrator);
height = z.Size();
width = z.Size();
}
void DGHyperbolicConservationLaws::ComputeInvMass() {
DenseMatrix Me;
MassIntegrator mi;
Me_inv.resize(vfes->GetNE());
for (int i = 0; i < vfes->GetNE(); i++) {
Me.SetSize(vfes->GetFE(i)->GetDof());
mi.AssembleElementMatrix(*vfes->GetFE(i), *vfes->GetElementTransformation(i),
Me);
DenseMatrixInverse inv(&Me);
inv.Factor();
inv.GetInverseMatrix(Me_inv[i]);
}
}
void DGHyperbolicConservationLaws::Update() {
nonlinearForm->Update();
ComputeInvMass();
width = nonlinearForm->Width();
height = nonlinearForm->Height();
z.SetSize(height);
}
void DGHyperbolicConservationLaws::Mult(const Vector &x, Vector &y) const {
// 0. Reset wavespeed computation before operator application.
max_char_speed = 0.;
// 1. Create the vector z with the face terms (F(u), grad v) - <F.n(u), [w]>.
nonlinearForm->Mult(x, z);
// 3. Multiply element-wise by the inverse mass matrices.
Vector zval;
Array<int> vdofs;
// const int dof = vfes->GetFE(0)->GetDof();
DenseMatrix zmat, ymat;
for (int i = 0; i < vfes->GetNE(); i++) {
// Return the vdofs ordered byNODES
vfes->GetElementVDofs(i, vdofs);
z.GetSubVector(vdofs, zval);
zmat.UseExternalData(zval.GetData(), vfes->GetFE(i)->GetDof(),
num_equations);
ymat.SetSize(Me_inv[i].Height(), num_equations);
mfem::Mult(Me_inv[i], zmat, ymat);
y.SetSubVector(vdofs, ymat.GetData());
}
}
//////////////////////////////////////////////////////////////////
/// ELEMENT INTEGRATOR ///
//////////////////////////////////////////////////////////////////
void HyperbolicElementFormIntegrator::AssembleElementVector(
const FiniteElement &el, ElementTransformation &Tr, const Vector &elfun,
Vector &elvect) {
const int dof = el.GetDof();
shape.SetSize(dof);
dshape.SetSize(dof, el.GetDim());
elvect.SetSize(dof * num_equations);
elvect = 0.0;
const DenseMatrix elfun_mat(elfun.GetData(), dof, num_equations);
DenseMatrix elvect_mat(elvect.GetData(), dof, num_equations);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el);
for (int i = 0; i < ir->GetNPoints(); i++) {
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetIntPoint(&ip);
el.CalcShape(ip, shape);
el.CalcPhysDShape(Tr, dshape);
elfun_mat.MultTranspose(shape, state);
const double mcs = ComputeFlux(state, Tr, flux);
*max_char_speed = mcs > *max_char_speed ? mcs : *max_char_speed;
AddMult_a_ABt(ip.weight * Tr.Weight(), dshape, flux, elvect_mat);
}
}
//////////////////////////////////////////////////////////////////
/// FACE INTEGRATOR ///
//////////////////////////////////////////////////////////////////
void HyperbolicFaceFormIntegrator::AssembleFaceVector(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Tr, const Vector &elfun, Vector &elvect) {
// Compute the term <F.n(u),[w]> on the interior faces.
const int dof1 = el1.GetDof();
const int dof2 = el2.GetDof();
shape1.SetSize(dof1);
shape2.SetSize(dof2);
elvect.SetSize((dof1 + dof2) * num_equations);
elvect = 0.0;
const DenseMatrix elfun1_mat(elfun.GetData(), dof1, num_equations);
const DenseMatrix elfun2_mat(elfun.GetData() + dof1 * num_equations, dof2,
num_equations);
DenseMatrix elvect1_mat(elvect.GetData(), dof1, num_equations);
DenseMatrix elvect2_mat(elvect.GetData() + dof1 * num_equations, dof2,
num_equations);
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el1, el2);
for (int i = 0; i < ir->GetNPoints(); i++) {
const IntegrationPoint &ip = ir->IntPoint(i);
Tr.SetAllIntPoints(&ip); // set face and element int. points
// Calculate basis functions on both elements at the face
el1.CalcShape(Tr.GetElement1IntPoint(), shape1);
el2.CalcShape(Tr.GetElement2IntPoint(), shape2);
// Interpolate elfun at the point
elfun1_mat.MultTranspose(shape1, state1);
elfun2_mat.MultTranspose(shape2, state2);
// Get the normal vector and the flux on the face
if (nor.Size() == 1)
nor(0) = (Tr.GetElement1IntPoint().x - 0.5) * 2.0;
else
CalcOrtho(Tr.Jacobian(), nor);
const double mcs = max(
ComputeFluxDotN(state1, nor, Tr.GetElement1Transformation(), flux1),
ComputeFluxDotN(state2, nor, Tr.GetElement2Transformation(), flux2));
rsolver->Eval(state1, state2, flux1, flux2, mcs, nor, fluxN);
// Update max char speed
*max_char_speed = mcs > *max_char_speed ? mcs : *max_char_speed;
fluxN *= ip.weight;
for (int k = 0; k < num_equations; k++) {
for (int s = 0; s < dof1; s++) {
elvect1_mat(s, k) -= fluxN(k) * shape1(s);
}
for (int s = 0; s < dof2; s++) {
elvect2_mat(s, k) += fluxN(k) * shape2(s);
}
}
}
}
//////////////////////////////////////////////////////////////////
/// NUMERICAL FLUXES ///
//////////////////////////////////////////////////////////////////
// Rusanov Flux
class RusanovFlux : public NumericalFlux {
public:
void Eval(const Vector &state1, const Vector &state2, const Vector &flux1,
const Vector &flux2, const double maxE, const Vector &nor,
Vector &flux) {
// NOTE: nor in general is not a unit normal
flux = 0.0;
flux += state1;
flux -= state2;
flux *= maxE * sqrt(nor * nor);
flux += flux1;
flux += flux2;
flux *= 0.5;
}
};
// Upwind Flux, Not Yet Implemented
class UpwindFlux : public NumericalFlux {
public:
void Eval(const Vector &state1, const Vector &state2, const Vector &flux1,
const Vector &flux2, const double maxE, const Vector &nor,
Vector &flux) {
// NOTE: nor in general is not a unit normal
mfem_error("Not Implemented");
}
};
//////////////////////////////////////////////////////////////////
/// EULER SYSTEM ///
//////////////////////////////////////////////////////////////////
class EulerElementFormIntegrator : public HyperbolicElementFormIntegrator {
private:
const double specific_heat_ratio;
const double gas_constant;
double ComputeFlux(const Vector &state, ElementTransformation &Tr,
DenseMatrix &flux) {
const int dim = state.Size() - 2;
const double den = state(0);
const Vector den_vel(state.GetData() + 1, dim);
const double den_energy = state(1 + dim);
const double pres = (specific_heat_ratio - 1.0) *
(den_energy - 0.5 * (den_vel * den_vel) / den);
MFEM_ASSERT(den >= 0, "Negative Density");
MFEM_ASSERT(pres >= 0, "Negative Pressure");
MFEM_ASSERT(den_energy >= 0, "Negative Energy");
for (int d = 0; d < dim; d++) {
flux(0, d) = den_vel(d);
for (int i = 0; i < dim; i++) {
flux(1 + i, d) = den_vel(i) * den_vel(d) / den;
}
flux(1 + d, d) += pres;
}
const double H = (den_energy + pres) / den;
for (int d = 0; d < dim; d++) {
flux(1 + dim, d) = den_vel(d) * H;
}
const double sound = sqrt(specific_heat_ratio * pres / den);
const double vel = sqrt(den_vel * den_vel) / den;
return vel + sound;
}
public:
EulerElementFormIntegrator(const int dim, const double specific_heat_ratio_,
const double gas_constant_,
const int IntOrderOffset_ = 3)
: HyperbolicElementFormIntegrator(dim, dim + 2, IntOrderOffset_),
specific_heat_ratio(specific_heat_ratio_),
gas_constant(gas_constant_) {}
EulerElementFormIntegrator(const int dim, const double specific_heat_ratio_,
const double gas_constant_,
const IntegrationRule *ir)
: HyperbolicElementFormIntegrator(dim, dim + 2, ir),
specific_heat_ratio(specific_heat_ratio_),
gas_constant(gas_constant_) {}
};
// Euler System face integration. Overload ComputeFluxDotN
class EulerFaceFormIntegrator : public HyperbolicFaceFormIntegrator {
private:
const double specific_heat_ratio;
const double gas_constant;
double ComputeFluxDotN(const Vector &state, const Vector &nor,
ElementTransformation &Tr, Vector &fluxN) {
// NOTE: nor in general is not a unit normal
const int dim = nor.Size();
const double den = state(0);
const Vector den_vel(state.GetData() + 1, dim);
const double den_energy = state(1 + dim);
const double pres = (specific_heat_ratio - 1.0) *
(den_energy - 0.5 * (den_vel * den_vel) / den);
MFEM_ASSERT(den >= 0, "Negative Density");
MFEM_ASSERT(pres >= 0, "Negative Pressure");
MFEM_ASSERT(den_energy >= 0, "Negative Energy");
double den_velN = 0;
for (int d = 0; d < dim; d++) {
den_velN += den_vel(d) * nor(d);
}
fluxN(0) = den_velN;
for (int d = 0; d < dim; d++) {
fluxN(1 + d) = den_velN * den_vel(d) / den + pres * nor(d);
}
const double H = (den_energy + pres) / den;
fluxN(1 + dim) = den_velN * H;
const double sound = sqrt(specific_heat_ratio * pres / den);
const double vel = sqrt(den_vel * den_vel) / den;
return vel + sound;
}
public:
EulerFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
const double specific_heat_ratio_,
const double gas_constant_,
const int IntOrderOffset_ = 3)
: HyperbolicFaceFormIntegrator(rsolver_, dim, dim + 2, IntOrderOffset_),
specific_heat_ratio(specific_heat_ratio_),
gas_constant(gas_constant_){};
EulerFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
const double specific_heat_ratio_,
const double gas_constant_, const IntegrationRule *ir)
: HyperbolicFaceFormIntegrator(rsolver_, dim, dim + 2, ir),
specific_heat_ratio(specific_heat_ratio_),
gas_constant(gas_constant_){};
};
//////////////////////////////////////////////////////////////////
/// BURGERS EQUATION ///
//////////////////////////////////////////////////////////////////
// Burgers equation main class. Overload ComputeFlux
class BurgersElementFormIntegrator : public HyperbolicElementFormIntegrator {
private:
double ComputeFlux(const Vector &state, ElementTransformation &Tr,
DenseMatrix &flux) {
flux = state * state * 0.5;
return abs(state(0));
}
public:
BurgersElementFormIntegrator(const int dim, const int IntOrderOffset_ = 3)
: HyperbolicElementFormIntegrator(dim, 1, IntOrderOffset_){};
BurgersElementFormIntegrator(const int dim, const IntegrationRule *ir)
: HyperbolicElementFormIntegrator(dim, 1, ir){};
};
// Burgers equation face integration. Overload ComputeFluxDotN
class BurgersFaceFormIntegrator : public HyperbolicFaceFormIntegrator {
private:
double ComputeFluxDotN(const Vector &state, const Vector &nor,
ElementTransformation &Tr, Vector &fluxN) {
fluxN = nor.Sum() * (state * state) * 0.5;
return abs(state(0));
}
public:
BurgersFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
const int IntOrderOffset_ = 3)
: HyperbolicFaceFormIntegrator(rsolver_, dim, 1, IntOrderOffset_){};
BurgersFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
const IntegrationRule *ir)
: HyperbolicFaceFormIntegrator(rsolver_, dim, 1, ir){};
};
//////////////////////////////////////////////////////////////////
/// BURGERS EQUATION ///
//////////////////////////////////////////////////////////////////
// Advection equation main class. Overload ComputeFlux
class AdvectionElementFormIntegrator : public HyperbolicElementFormIntegrator {
private:
VectorCoefficient &b;
Vector bval;
double ComputeFlux(const Vector &state, ElementTransformation &Tr,
DenseMatrix &flux) {
b.Eval(bval, Tr, Tr.GetIntPoint());
const int dim = bval.Size();
const int num_equations = state.Size();
for (int j = 0; j < dim; j++) {
for (int i = 0; i < num_equations; i++) {
flux(i, j) = bval(j) * state(i);
}
}
return bval.Norml2();
}
public:
AdvectionElementFormIntegrator(const int dim, VectorCoefficient &b_,
const int IntOrderOffset_ = 3)
: HyperbolicElementFormIntegrator(dim, 1, IntOrderOffset_),
b(b_),
bval(dim){};
AdvectionElementFormIntegrator(const int dim, VectorCoefficient &b_,
const IntegrationRule *ir)
: HyperbolicElementFormIntegrator(dim, 1, ir), b(b_), bval(dim){};
};
// Advection equation face integration. Overload ComputeFluxDotN
class AdvectionFaceFormIntegrator : public HyperbolicFaceFormIntegrator {
private:
VectorCoefficient &b;
Vector bval;
double ComputeFluxDotN(const Vector &state, const Vector &nor,
ElementTransformation &Tr, Vector &fluxN) {
b.Eval(bval, Tr, Tr.GetIntPoint());
const double bN = bval * nor;
const int num_equations = state.Size();
for (int i = 0; i < num_equations; i++) {
fluxN(i) = bN * state(i);
}
return bval.Norml2();
}
public:
AdvectionFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
VectorCoefficient &b_,
const int IntOrderOffset_ = 3)
: HyperbolicFaceFormIntegrator(rsolver_, dim, 1, IntOrderOffset_),
b(b_),
bval(dim){};
AdvectionFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
VectorCoefficient &b_, const IntegrationRule *ir)
: HyperbolicFaceFormIntegrator(rsolver_, dim, 1, ir), b(b_), bval(dim){};
};
//////////////////////////////////////////////////////////////////
/// SHALLOW WATER ///
//////////////////////////////////////////////////////////////////
// ShallowWater equation element integration. Overload ComputeFlux
class ShallowWaterElementFormIntegrator
: public HyperbolicElementFormIntegrator {
private:
const double g;
double ComputeFlux(const Vector &state, ElementTransformation &Tr,
DenseMatrix &flux) {
const int dim = state.Size() - 1;
const double height = state(0);
const Vector h_vel(state.GetData() + 1, dim);
const double energy = 0.5 * g * (height * height);
MFEM_ASSERT(height >= 0, "Negative Height");
for (int d = 0; d < dim; d++) {
flux(0, d) = h_vel(d);
for (int i = 0; i < dim; i++) {
flux(1 + i, d) = h_vel(i) * h_vel(d) / height;
}
flux(1 + d, d) += energy;
}
const double sound = sqrt(g * height);
const double vel = sqrt(h_vel * h_vel) / height;
return vel + sound;
}
public:
ShallowWaterElementFormIntegrator(const int dim, const double g_,
const int IntOrderOffset_ = 3)
: HyperbolicElementFormIntegrator(dim, dim + 1, IntOrderOffset_), g(g_){};
ShallowWaterElementFormIntegrator(const int dim, const double g_,
const IntegrationRule *ir)
: HyperbolicElementFormIntegrator(dim, dim + 1, ir), g(g_){};
};
// ShallowWater equation face integration. Overload ComputeFluxDotN
class ShallowWaterFaceFormIntegrator : public HyperbolicFaceFormIntegrator {
private:
const double g;
double ComputeFluxDotN(const Vector &state, const Vector &nor,
ElementTransformation &Tr, Vector &fluxN) {
const int dim = nor.Size();
const double height = state(0);
const Vector h_vel(state.GetData() + 1, dim);
const double energy = 0.5 * g * (height * height);
MFEM_ASSERT(height >= 0, "Negative Height");
fluxN(0) = h_vel * nor;
const double normal_vel = fluxN(0) / height;
for (int i = 0; i < dim; i++) {
fluxN(1 + i) = normal_vel * h_vel(i) + energy * nor(i);
}
const double sound = sqrt(g * height);
const double vel = sqrt(h_vel * h_vel) / height;
return vel + sound;
}
public:
ShallowWaterFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
const double g_, const int IntOrderOffset_ = 3)
: HyperbolicFaceFormIntegrator(rsolver_, dim, dim + 1, IntOrderOffset_),
g(g_){};
ShallowWaterFaceFormIntegrator(NumericalFlux *rsolver_, const int dim,
const double g_, const IntegrationRule *ir)
: HyperbolicFaceFormIntegrator(rsolver_, dim, dim + 1, ir), g(g_){};
};