1369 lines
44 KiB
C++
1369 lines
44 KiB
C++
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||
//
|
||
// This file is part of the MFEM library. For more information and source code
|
||
// availability visit https://mfem.org.
|
||
//
|
||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||
// CONTRIBUTING.md for details.
|
||
|
||
// Implementation of hyperbolic conservation laws
|
||
|
||
#include "hyperbolic.hpp"
|
||
#include "nonlinearform.hpp"
|
||
#include "pnonlinearform.hpp"
|
||
|
||
namespace mfem
|
||
{
|
||
|
||
HyperbolicFormIntegrator::HyperbolicFormIntegrator(
|
||
const NumericalFlux &numFlux,
|
||
const int IntOrderOffset,
|
||
real_t sign)
|
||
: NonlinearFormIntegrator(),
|
||
numFlux(numFlux),
|
||
fluxFunction(numFlux.GetFluxFunction()),
|
||
IntOrderOffset(IntOrderOffset),
|
||
sign(sign),
|
||
num_equations(fluxFunction.num_equations)
|
||
{
|
||
#ifndef MFEM_THREAD_SAFE
|
||
state.SetSize(num_equations);
|
||
flux.SetSize(num_equations, fluxFunction.dim);
|
||
state1.SetSize(num_equations);
|
||
state2.SetSize(num_equations);
|
||
fluxN.SetSize(num_equations);
|
||
JDotN.SetSize(num_equations);
|
||
nor.SetSize(fluxFunction.dim);
|
||
#endif
|
||
ResetMaxCharSpeed();
|
||
}
|
||
|
||
void HyperbolicFormIntegrator::AssembleElementVector(const FiniteElement &el,
|
||
ElementTransformation &Tr,
|
||
const Vector &elfun,
|
||
Vector &elvect)
|
||
{
|
||
// current element's the number of degrees of freedom
|
||
// does not consider the number of equations
|
||
const int dof = el.GetDof();
|
||
|
||
#ifdef MFEM_THREAD_SAFE
|
||
// Local storage for element integration
|
||
|
||
// shape function value at an integration point
|
||
Vector shape(dof);
|
||
// derivative of shape function at an integration point
|
||
DenseMatrix dshape(dof, Tr.GetSpaceDim());
|
||
// state value at an integration point
|
||
Vector state(num_equations);
|
||
// flux value at an integration point
|
||
DenseMatrix flux(num_equations, el.GetDim());
|
||
#else
|
||
// resize shape and gradient shape storage
|
||
shape.SetSize(dof);
|
||
dshape.SetSize(dof, Tr.GetSpaceDim());
|
||
#endif
|
||
|
||
// setDegree-up output vector
|
||
elvect.SetSize(dof * num_equations);
|
||
elvect = 0.0;
|
||
|
||
// make state variable and output dual vector matrix form.
|
||
const DenseMatrix elfun_mat(elfun.GetData(), dof, num_equations);
|
||
DenseMatrix elvect_mat(elvect.GetData(), dof, num_equations);
|
||
|
||
// obtain integration rule. If integration is rule is given, then use it.
|
||
// Otherwise, get (2*p + IntOrderOffset) order integration rule
|
||
const IntegrationRule *ir = IntRule;
|
||
if (!ir)
|
||
{
|
||
const int order = el.GetOrder()*2 + IntOrderOffset;
|
||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||
}
|
||
|
||
// loop over integration points
|
||
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);
|
||
// compute current state value with given shape function values
|
||
elfun_mat.MultTranspose(shape, state);
|
||
// compute F(u,x) and point maximum characteristic speed
|
||
|
||
const real_t mcs = fluxFunction.ComputeFlux(state, Tr, flux);
|
||
// update maximum characteristic speed
|
||
max_char_speed = std::max(mcs, max_char_speed);
|
||
// integrate (F(u,x), grad v)
|
||
AddMult_a_ABt(ip.weight * Tr.Weight() * sign, dshape, flux, elvect_mat);
|
||
}
|
||
}
|
||
|
||
void HyperbolicFormIntegrator::AssembleElementGrad(
|
||
const FiniteElement &el, ElementTransformation &Tr, const Vector &elfun,
|
||
DenseMatrix &grad)
|
||
{
|
||
// current element's the number of degrees of freedom
|
||
// does not consider the number of equations
|
||
const int dof = el.GetDof();
|
||
|
||
#ifdef MFEM_THREAD_SAFE
|
||
// Local storage for element integration
|
||
|
||
// shape function value at an integration point
|
||
Vector shape(dof);
|
||
// derivative of shape function at an integration point
|
||
DenseMatrix dshape(dof, Tr.GetSpaceDim());
|
||
// state value at an integration point
|
||
Vector state(num_equations);
|
||
// Jacobian value at an integration point
|
||
DenseTensor J(num_equations, num_equations, fluxFunction.dim);
|
||
#else
|
||
// resize shape, gradient shape and Jacobian storage
|
||
shape.SetSize(dof);
|
||
dshape.SetSize(dof, Tr.GetSpaceDim());
|
||
J.SetSize(num_equations, num_equations, fluxFunction.dim);
|
||
#endif
|
||
|
||
// setup output gradient matrix
|
||
grad.SetSize(dof * num_equations);
|
||
grad = 0.0;
|
||
|
||
// make state variable and output dual vector matrix form.
|
||
const DenseMatrix elfun_mat(elfun.GetData(), dof, num_equations);
|
||
//DenseMatrix elvect_mat(elvect.GetData(), dof, num_equations);
|
||
|
||
// obtain integration rule. If integration is rule is given, then use it.
|
||
// Otherwise, get (2*p + IntOrderOffset) order integration rule
|
||
const IntegrationRule *ir = IntRule;
|
||
if (!ir)
|
||
{
|
||
const int order = el.GetOrder()*2 + IntOrderOffset;
|
||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||
}
|
||
|
||
// loop over integration points
|
||
for (int q = 0; q < ir->GetNPoints(); q++)
|
||
{
|
||
const IntegrationPoint &ip = ir->IntPoint(q);
|
||
Tr.SetIntPoint(&ip);
|
||
|
||
el.CalcShape(ip, shape);
|
||
el.CalcPhysDShape(Tr, dshape);
|
||
// compute current state value with given shape function values
|
||
elfun_mat.MultTranspose(shape, state);
|
||
|
||
// compute J(u,x)
|
||
fluxFunction.ComputeFluxJacobian(state, Tr, J);
|
||
|
||
// integrate (J(u,x), grad v)
|
||
const real_t w = ip.weight * Tr.Weight() * sign;
|
||
for (int di = 0; di < num_equations; di++)
|
||
for (int dj = 0; dj < num_equations; dj++)
|
||
for (int i = 0; i < dof; i++)
|
||
for (int j = 0; j < dof; j++)
|
||
for (int d = 0; d < fluxFunction.dim; d++)
|
||
{
|
||
grad(di*dof+i, dj*dof+j) += w * dshape(i,d) * shape(j) * J(di,dj,d);
|
||
}
|
||
}
|
||
}
|
||
|
||
void HyperbolicFormIntegrator::AssembleFaceVector(
|
||
const FiniteElement &el1, const FiniteElement &el2,
|
||
FaceElementTransformations &Tr, const Vector &elfun, Vector &elvect)
|
||
{
|
||
// current elements' the number of degrees of freedom
|
||
// does not consider the number of equations
|
||
const int dof1 = el1.GetDof();
|
||
const int dof2 = (Tr.Elem2No >= 0)?(el2.GetDof()):(0);
|
||
|
||
#ifdef MFEM_THREAD_SAFE
|
||
// Local storage for element integration
|
||
|
||
// shape function value at an integration point - first elem
|
||
Vector shape1(dof1);
|
||
// shape function value at an integration point - second elem
|
||
Vector shape2(dof2);
|
||
// normal vector (usually not a unit vector)
|
||
Vector nor(Tr.GetSpaceDim());
|
||
// state value at an integration point - first elem
|
||
Vector state1(num_equations);
|
||
// state value at an integration point - second elem
|
||
Vector state2(num_equations);
|
||
// hat(F)(u,x)
|
||
Vector fluxN(num_equations);
|
||
#else
|
||
shape1.SetSize(dof1);
|
||
shape2.SetSize(dof2);
|
||
#endif
|
||
|
||
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);
|
||
|
||
// Obtain integration rule. If integration is rule is given, then use it.
|
||
// Otherwise, get (2*p + IntOrderOffset) order integration rule
|
||
const IntegrationRule *ir = IntRule;
|
||
if (!ir)
|
||
{
|
||
const int max_el_order = dof2 ? std::max(el1.GetOrder(),
|
||
el2.GetOrder()) : el1.GetOrder();
|
||
const int order = 2*max_el_order + IntOrderOffset;
|
||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||
}
|
||
// loop over integration points
|
||
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);
|
||
|
||
// Interpolate elfun at the point
|
||
elfun1_mat.MultTranspose(shape1, state1);
|
||
|
||
if (dof2)
|
||
{
|
||
// Calculate basis functions on both elements at the face
|
||
el2.CalcShape(Tr.GetElement2IntPoint(), shape2);
|
||
// Interpolate elfun at the point
|
||
elfun2_mat.MultTranspose(shape2, state2);
|
||
}
|
||
|
||
// Get the normal vector and the flux on the face
|
||
if (nor.Size() == 1) // if 1D, use 1 or -1.
|
||
{
|
||
nor(0) = 2*Tr.GetElement1IntPoint().x - 1.;
|
||
}
|
||
else
|
||
{
|
||
CalcOrtho(Tr.Jacobian(), nor);
|
||
}
|
||
// Compute F(u+, x) and F(u-, x) with maximum characteristic speed
|
||
// Compute hat(F) using evaluated quantities
|
||
const real_t speed = (dof2) ? numFlux.Eval(state1, state2, nor, Tr, fluxN):
|
||
fluxFunction.ComputeFluxDotN(state1, nor, Tr, fluxN);
|
||
|
||
// Update the global max char speed
|
||
max_char_speed = std::max(speed, max_char_speed);
|
||
|
||
// pre-multiply integration weight to flux
|
||
AddMult_a_VWt(-ip.weight*sign, shape1, fluxN, elvect1_mat);
|
||
if (dof2)
|
||
{
|
||
AddMult_a_VWt(+ip.weight*sign, shape2, fluxN, elvect2_mat);
|
||
}
|
||
}
|
||
}
|
||
|
||
void HyperbolicFormIntegrator::AssembleFaceGrad(
|
||
const FiniteElement &el1, const FiniteElement &el2,
|
||
FaceElementTransformations &Tr, const Vector &elfun, DenseMatrix &elmat)
|
||
{
|
||
// current elements' the number of degrees of freedom
|
||
// does not consider the number of equations
|
||
const int dof1 = el1.GetDof();
|
||
const int dof2 = (Tr.Elem2No >= 0)?(el2.GetDof()):(0);
|
||
|
||
#ifdef MFEM_THREAD_SAFE
|
||
// Local storage for element integration
|
||
|
||
// shape function value at an integration point - first elem
|
||
Vector shape1(dof1);
|
||
// shape function value at an integration point - second elem
|
||
Vector shape2(dof2);
|
||
// normal vector (usually not a unit vector)
|
||
Vector nor(Tr.GetSpaceDim());
|
||
// state value at an integration point - first elem
|
||
Vector state1(num_equations);
|
||
// state value at an integration point - second elem
|
||
Vector state2(num_equations);
|
||
// hat(J)(u,x)
|
||
DenseMatrix JDotN(num_equations);
|
||
#else
|
||
shape1.SetSize(dof1);
|
||
shape2.SetSize(dof2);
|
||
#endif
|
||
|
||
elmat.SetSize((dof1 + dof2) * num_equations);
|
||
elmat = 0.0;
|
||
|
||
const DenseMatrix elfun1_mat(elfun.GetData(), dof1, num_equations);
|
||
const DenseMatrix elfun2_mat(elfun.GetData() + dof1 * num_equations, dof2,
|
||
num_equations);
|
||
|
||
// Obtain integration rule. If integration is rule is given, then use it.
|
||
// Otherwise, get (2*p + IntOrderOffset) order integration rule
|
||
const IntegrationRule *ir = IntRule;
|
||
if (!ir)
|
||
{
|
||
const int max_el_order = dof2 ? std::max(el1.GetOrder(),
|
||
el2.GetOrder()) : el1.GetOrder();
|
||
const int order = 2*max_el_order + IntOrderOffset;
|
||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||
}
|
||
// loop over integration points
|
||
for (int q = 0; q < ir->GetNPoints(); q++)
|
||
{
|
||
const IntegrationPoint &ip = ir->IntPoint(q);
|
||
|
||
Tr.SetAllIntPoints(&ip); // set face and element int. points
|
||
|
||
// Calculate basis functions of the first element at the face
|
||
el1.CalcShape(Tr.GetElement1IntPoint(), shape1);
|
||
|
||
// Interpolate elfun at the point
|
||
elfun1_mat.MultTranspose(shape1, state1);
|
||
|
||
if (dof2)
|
||
{
|
||
// Calculate basis function of the second element at the face
|
||
el2.CalcShape(Tr.GetElement2IntPoint(), shape2);
|
||
|
||
// Interpolate elfun at the point
|
||
elfun2_mat.MultTranspose(shape2, state2);
|
||
}
|
||
|
||
// Get the normal vector and the flux on the face
|
||
if (nor.Size() == 1) // if 1D, use 1 or -1.
|
||
{
|
||
nor(0) = 2*Tr.GetElement1IntPoint().x - 1.;
|
||
}
|
||
else
|
||
{
|
||
CalcOrtho(Tr.Jacobian(), nor);
|
||
}
|
||
|
||
// Trial side 1
|
||
|
||
// Compute hat(J) using evaluated quantities
|
||
if (dof2)
|
||
{
|
||
numFlux.Grad(1, state1, state2, nor, Tr, JDotN);
|
||
}
|
||
else
|
||
{
|
||
fluxFunction.ComputeFluxJacobianDotN(state1, nor, Tr, JDotN);
|
||
}
|
||
|
||
const int ioff = fluxFunction.num_equations * dof1;
|
||
|
||
for (int di = 0; di < fluxFunction.num_equations; di++)
|
||
for (int dj = 0; dj < fluxFunction.num_equations; dj++)
|
||
{
|
||
// pre-multiply integration weight to Jacobian
|
||
const real_t w = -ip.weight * sign * JDotN(di,dj);
|
||
for (int j = 0; j < dof1; j++)
|
||
{
|
||
// Test side 1
|
||
for (int i = 0; i < dof1; i++)
|
||
{
|
||
elmat(i+dof1*di, j+dof1*dj) += w * shape1(i) * shape1(j);
|
||
}
|
||
|
||
// Test side 2
|
||
for (int i = 0; i < dof2; i++)
|
||
{
|
||
elmat(ioff+i+dof2*di, j+dof1*dj) -= w * shape2(i) * shape1(j);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (dof2)
|
||
{
|
||
// Trial side 2
|
||
|
||
// Compute hat(J) using evaluated quantities
|
||
numFlux.Grad(2, state1, state2, nor, Tr, JDotN);
|
||
|
||
const int joff = ioff;
|
||
|
||
for (int di = 0; di < fluxFunction.num_equations; di++)
|
||
for (int dj = 0; dj < fluxFunction.num_equations; dj++)
|
||
{
|
||
// pre-multiply integration weight to Jacobian
|
||
const real_t w = +ip.weight * sign * JDotN(di,dj);
|
||
for (int j = 0; j < dof2; j++)
|
||
{
|
||
// Test side 1
|
||
for (int i = 0; i < dof1; i++)
|
||
{
|
||
elmat(i+dof1*di, joff+j+dof2*dj) += w * shape1(i) * shape2(j);
|
||
}
|
||
|
||
// Test side 2
|
||
for (int i = 0; i < dof2; i++)
|
||
{
|
||
elmat(ioff+i+dof2*di, joff+j+dof2*dj) -= w * shape2(i) * shape2(j);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
BdrHyperbolicDirichletIntegrator::BdrHyperbolicDirichletIntegrator(
|
||
const NumericalFlux &numFlux,
|
||
VectorCoefficient &bdrState,
|
||
const int IntOrderOffset,
|
||
real_t sign)
|
||
: NonlinearFormIntegrator(),
|
||
numFlux(numFlux),
|
||
fluxFunction(numFlux.GetFluxFunction()),
|
||
u_vcoeff(bdrState),
|
||
IntOrderOffset(IntOrderOffset),
|
||
sign(sign),
|
||
num_equations(fluxFunction.num_equations)
|
||
{
|
||
MFEM_VERIFY(fluxFunction.num_equations == bdrState.GetVDim(),
|
||
"Flux function does not match the vector dimension of the coefficient!");
|
||
#ifndef MFEM_THREAD_SAFE
|
||
state_in.SetSize(num_equations);
|
||
state_out.SetSize(num_equations);
|
||
fluxN.SetSize(num_equations);
|
||
JDotN.SetSize(num_equations);
|
||
nor.SetSize(fluxFunction.dim);
|
||
#endif
|
||
ResetMaxCharSpeed();
|
||
}
|
||
|
||
void BdrHyperbolicDirichletIntegrator::AssembleFaceVector(
|
||
const FiniteElement &el, const FiniteElement &,
|
||
FaceElementTransformations &Tr, const Vector &elfun, Vector &elvect)
|
||
{
|
||
MFEM_ASSERT(Tr.Elem2No < 0, "Not a boundary face!");
|
||
|
||
// current elements' the number of degrees of freedom
|
||
// does not consider the number of equations
|
||
const int dof = el.GetDof();
|
||
|
||
#ifdef MFEM_THREAD_SAFE
|
||
// Local storage for element integration
|
||
|
||
// shape function value at an integration point
|
||
Vector shape(dof);
|
||
// normal vector (usually not a unit vector)
|
||
Vector nor(Tr.GetSpaceDim());
|
||
// state value at an integration point - interior
|
||
Vector state_in(num_equations);
|
||
// state value at an integration point - boundary
|
||
Vector state_out(num_equations);
|
||
// hat(F)(u,x)
|
||
Vector fluxN(num_equations);
|
||
#else
|
||
shape.SetSize(dof);
|
||
#endif
|
||
|
||
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);
|
||
|
||
// Obtain integration rule. If integration is rule is given, then use it.
|
||
// Otherwise, get (2*p + IntOrderOffset) order integration rule
|
||
const IntegrationRule *ir = IntRule;
|
||
if (!ir)
|
||
{
|
||
const int order = 2*el.GetOrder() + IntOrderOffset;
|
||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||
}
|
||
// loop over integration points
|
||
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 at the face
|
||
el.CalcShape(Tr.GetElement1IntPoint(), shape);
|
||
|
||
// Interpolate elfun at the point
|
||
elfun_mat.MultTranspose(shape, state_in);
|
||
|
||
// Evaluate boundary state at the point
|
||
u_vcoeff.Eval(state_out, Tr, ip);
|
||
|
||
// Get the normal vector and the flux on the face
|
||
if (nor.Size() == 1) // if 1D, use 1 or -1.
|
||
{
|
||
nor(0) = 2*Tr.GetElement1IntPoint().x - 1.;
|
||
}
|
||
else
|
||
{
|
||
CalcOrtho(Tr.Jacobian(), nor);
|
||
}
|
||
// Compute F(u+, x) and F(u_b, x) with maximum characteristic speed
|
||
// Compute hat(F) using evaluated quantities
|
||
const real_t speed = numFlux.Eval(state_in, state_out, nor, Tr, fluxN);
|
||
|
||
// Update the global max char speed
|
||
max_char_speed = std::max(speed, max_char_speed);
|
||
|
||
// pre-multiply integration weight to flux
|
||
AddMult_a_VWt(-ip.weight*sign, shape, fluxN, elvect_mat);
|
||
}
|
||
}
|
||
|
||
void BdrHyperbolicDirichletIntegrator::AssembleFaceGrad(
|
||
const FiniteElement &el, const FiniteElement &,
|
||
FaceElementTransformations &Tr, const Vector &elfun, DenseMatrix &elmat)
|
||
{
|
||
// current elements' the number of degrees of freedom
|
||
// does not consider the number of equations
|
||
const int dof = el.GetDof();
|
||
|
||
#ifdef MFEM_THREAD_SAFE
|
||
// Local storage for element integration
|
||
|
||
// shape function value at an integration point
|
||
Vector shape(dof);
|
||
// normal vector (usually not a unit vector)
|
||
Vector nor(Tr.GetSpaceDim());
|
||
// state value at an integration point - interior
|
||
Vector state_in(num_equations);
|
||
// state value at an integration point - boundary
|
||
Vector state_out(num_equations);
|
||
// hat(J)(u,x)
|
||
DenseMatrix JDotN(num_equations);
|
||
#else
|
||
shape.SetSize(dof);
|
||
#endif
|
||
|
||
elmat.SetSize(dof * num_equations);
|
||
elmat = 0.0;
|
||
|
||
const DenseMatrix elfun_mat(elfun.GetData(), dof, num_equations);
|
||
|
||
// Obtain integration rule. If integration is rule is given, then use it.
|
||
// Otherwise, get (2*p + IntOrderOffset) order integration rule
|
||
const IntegrationRule *ir = IntRule;
|
||
if (!ir)
|
||
{
|
||
const int order = 2*el.GetOrder() + IntOrderOffset;
|
||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||
}
|
||
// loop over integration points
|
||
for (int q = 0; q < ir->GetNPoints(); q++)
|
||
{
|
||
const IntegrationPoint &ip = ir->IntPoint(q);
|
||
|
||
Tr.SetAllIntPoints(&ip); // set face and element int. points
|
||
|
||
// Calculate basis functions at the face
|
||
el.CalcShape(Tr.GetElement1IntPoint(), shape);
|
||
|
||
// Interpolate elfun at the point
|
||
elfun_mat.MultTranspose(shape, state_in);
|
||
|
||
// Evaluate boundary state at the point
|
||
u_vcoeff.Eval(state_out, Tr, ip);
|
||
|
||
// Get the normal vector and the flux on the face
|
||
if (nor.Size() == 1) // if 1D, use 1 or -1.
|
||
{
|
||
nor(0) = 2*Tr.GetElement1IntPoint().x - 1.;
|
||
}
|
||
else
|
||
{
|
||
CalcOrtho(Tr.Jacobian(), nor);
|
||
}
|
||
|
||
// Compute hat(J) using evaluated quantities
|
||
numFlux.Grad(1, state_in, state_out, nor, Tr, JDotN);
|
||
|
||
for (int di = 0; di < fluxFunction.num_equations; di++)
|
||
for (int dj = 0; dj < fluxFunction.num_equations; dj++)
|
||
{
|
||
// pre-multiply integration weight to Jacobian
|
||
const real_t w = -ip.weight * sign * JDotN(di,dj);
|
||
for (int j = 0; j < dof; j++)
|
||
for (int i = 0; i < dof; i++)
|
||
{
|
||
elmat(i+dof*di, j+dof*dj) += w * shape(i) * shape(j);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
BoundaryHyperbolicFlowIntegrator::BoundaryHyperbolicFlowIntegrator(
|
||
const FluxFunction &flux, VectorCoefficient &u, real_t alpha_, real_t beta_,
|
||
const int IntOrderOffset_)
|
||
: fluxFunction(flux), u_vcoeff(u), alpha(alpha_), beta(beta_),
|
||
IntOrderOffset(IntOrderOffset_)
|
||
{
|
||
MFEM_VERIFY(fluxFunction.num_equations == u_vcoeff.GetVDim(),
|
||
"Flux function does not match the vector dimension of the coefficient!");
|
||
#ifndef MFEM_THREAD_SAFE
|
||
state.SetSize(fluxFunction.num_equations);
|
||
nor.SetSize(fluxFunction.dim);
|
||
fluxN.SetSize(fluxFunction.num_equations);
|
||
#endif
|
||
ResetMaxCharSpeed();
|
||
}
|
||
|
||
void BoundaryHyperbolicFlowIntegrator::AssembleRHSElementVect(
|
||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||
{
|
||
mfem_error("BoundaryHyperbolicFlowIntegrator::AssembleRHSElementVect\n"
|
||
" is not implemented as boundary integrator!\n"
|
||
" Use LinearForm::AddBdrFaceIntegrator instead of\n"
|
||
" LinearForm::AddBoundaryIntegrator.");
|
||
}
|
||
|
||
void BoundaryHyperbolicFlowIntegrator::AssembleRHSElementVect(
|
||
const FiniteElement &el, FaceElementTransformations &Tr, Vector &elvect)
|
||
{
|
||
// current elements' the number of degrees of freedom
|
||
// does not consider the number of equations
|
||
const int dof = el.GetDof();
|
||
|
||
#ifdef MFEM_THREAD_SAFE
|
||
// Local storage for element integration
|
||
|
||
// shape function value at an integration point
|
||
Vector shape(dof);
|
||
// state value at an integration point
|
||
Vector state(fluxFunction.num_equations);
|
||
// normal vector (usually not a unit vector)
|
||
Vector nor(Tr.GetSpaceDim());
|
||
// hat(F)(u,x)
|
||
Vector fluxN(fluxFunction.num_equations);
|
||
#else
|
||
shape.SetSize(dof);
|
||
#endif
|
||
|
||
elvect.SetSize(dof * fluxFunction.num_equations);
|
||
elvect = 0.0;
|
||
|
||
DenseMatrix elvect_mat(elvect.GetData(), dof, fluxFunction.num_equations);
|
||
|
||
// Obtain integration rule. If integration is rule is given, then use it.
|
||
// Otherwise, get (2*p + IntOrderOffset) order integration rule
|
||
const IntegrationRule *ir = IntRule;
|
||
if (!ir)
|
||
{
|
||
const int order = 2*el.GetOrder() + IntOrderOffset;
|
||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||
}
|
||
// loop over integration points
|
||
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
|
||
el.CalcShape(Tr.GetElement1IntPoint(), shape);
|
||
|
||
// Evaluate the coefficient at the point
|
||
u_vcoeff.Eval(state, Tr, ip);
|
||
|
||
// Get the normal vector and the flux on the face
|
||
if (nor.Size() == 1) // if 1D, use 1 or -1.
|
||
{
|
||
nor(0) = 2*Tr.GetElement1IntPoint().x - 1.;
|
||
}
|
||
else
|
||
{
|
||
CalcOrtho(Tr.Jacobian(), nor);
|
||
}
|
||
// Compute F(u, x) with maximum characteristic speed
|
||
const real_t speed = fluxFunction.ComputeFluxDotN(state, nor, Tr, fluxN);
|
||
|
||
// Update the global max char speed
|
||
max_char_speed = std::max(speed, max_char_speed);
|
||
|
||
// pre-multiply integration weight to flux
|
||
const real_t a = 0.5 * alpha * ip.weight;
|
||
const real_t b = beta * ip.weight;
|
||
|
||
for (int n = 0; n < fluxFunction.num_equations; n++)
|
||
{
|
||
fluxN(n) = a * fluxN(n) - b * fabs(fluxN(n));
|
||
}
|
||
|
||
AddMultVWt(shape, fluxN, elvect_mat);
|
||
}
|
||
}
|
||
|
||
real_t FluxFunction::ComputeFluxDotN(const Vector &U,
|
||
const Vector &normal,
|
||
FaceElementTransformations &Tr,
|
||
Vector &FUdotN) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
DenseMatrix flux(num_equations, dim);
|
||
#else
|
||
flux.SetSize(num_equations, dim);
|
||
#endif
|
||
real_t val = ComputeFlux(U, Tr, flux);
|
||
flux.Mult(normal, FUdotN);
|
||
return val;
|
||
}
|
||
|
||
real_t FluxFunction::ComputeAvgFluxDotN(const Vector &U1, const Vector &U2,
|
||
const Vector &normal,
|
||
FaceElementTransformations &Tr,
|
||
Vector &fluxDotN) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
DenseMatrix flux(num_equations, dim);
|
||
#else
|
||
flux.SetSize(num_equations, dim);
|
||
#endif
|
||
real_t val = ComputeAvgFlux(U1, U2, Tr, flux);
|
||
flux.Mult(normal, fluxDotN);
|
||
return val;
|
||
}
|
||
|
||
void FluxFunction::ComputeFluxJacobianDotN(const Vector &U,
|
||
const Vector &normal,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &JDotN) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
DenseTensor J(num_equations, num_equations, dim);
|
||
#else
|
||
J.SetSize(num_equations, num_equations, dim);
|
||
#endif
|
||
ComputeFluxJacobian(U, Tr, J);
|
||
JDotN.Set(normal(0), J(0));
|
||
for (int d = 1; d < dim; d++)
|
||
{
|
||
JDotN.AddMatrix(normal(d), J(d), 0, 0);
|
||
}
|
||
}
|
||
|
||
RusanovFlux::RusanovFlux(const FluxFunction &fluxFunction)
|
||
: NumericalFlux(fluxFunction)
|
||
{
|
||
#ifndef MFEM_THREAD_SAFE
|
||
fluxN1.SetSize(fluxFunction.num_equations);
|
||
fluxN2.SetSize(fluxFunction.num_equations);
|
||
#endif
|
||
}
|
||
|
||
real_t RusanovFlux::Eval(const Vector &state1, const Vector &state2,
|
||
const Vector &nor, FaceElementTransformations &Tr,
|
||
Vector &flux) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector fluxN1(fluxFunction.num_equations), fluxN2(fluxFunction.num_equations);
|
||
#endif
|
||
const real_t speed1 = fluxFunction.ComputeFluxDotN(state1, nor, Tr, fluxN1);
|
||
const real_t speed2 = fluxFunction.ComputeFluxDotN(state2, nor, Tr, fluxN2);
|
||
// NOTE: nor in general is not a unit normal
|
||
const real_t maxE = std::max(speed1, speed2);
|
||
// here, nor.Norml2() is multiplied to match the scale with fluxN
|
||
const real_t scaledMaxE = maxE * nor.Norml2();
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
flux(i) = 0.5*(scaledMaxE*(state1(i) - state2(i)) + (fluxN1(i) + fluxN2(i)));
|
||
}
|
||
return maxE;
|
||
}
|
||
|
||
void RusanovFlux::Grad(int side, const Vector &state1, const Vector &state2,
|
||
const Vector &nor, FaceElementTransformations &Tr,
|
||
DenseMatrix &grad) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector fluxN1(fluxFunction.num_equations), fluxN2(fluxFunction.num_equations);
|
||
#endif
|
||
|
||
const real_t speed1 = fluxFunction.ComputeFluxDotN(state1, nor, Tr, fluxN1);
|
||
const real_t speed2 = fluxFunction.ComputeFluxDotN(state2, nor, Tr, fluxN2);
|
||
|
||
// NOTE: nor in general is not a unit normal
|
||
const real_t maxE = std::max(speed1, speed2);
|
||
// here, nor.Norml2() is multiplied to match the scale with fluxN
|
||
const real_t scaledMaxE = maxE * nor.Norml2();
|
||
|
||
if (side == 1)
|
||
{
|
||
fluxFunction.ComputeFluxJacobianDotN(state1, nor, Tr, grad);
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
grad(i,i) += 0.5 * scaledMaxE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fluxFunction.ComputeFluxJacobianDotN(state2, nor, Tr, grad);
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
grad(i,i) -= 0.5 * scaledMaxE;
|
||
}
|
||
}
|
||
}
|
||
|
||
real_t RusanovFlux::Average(const Vector &state1, const Vector &state2,
|
||
const Vector &nor, FaceElementTransformations &Tr,
|
||
Vector &flux) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector fluxN1(fluxFunction.num_equations), fluxN2(fluxFunction.num_equations);
|
||
#endif
|
||
const real_t speed1 = fluxFunction.ComputeFluxDotN(state1, nor, Tr, fluxN1);
|
||
const real_t speed2 = fluxFunction.ComputeAvgFluxDotN(state1, state2, nor, Tr,
|
||
fluxN2);
|
||
// NOTE: nor in general is not a unit normal
|
||
const real_t maxE = std::max(speed1, speed2);
|
||
// here, nor.Norml2() is multiplied to match the scale with fluxN
|
||
const real_t scaledMaxE = maxE * nor.Norml2() * 0.5;
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
flux(i) = 0.5*(scaledMaxE*(state1(i) - state2(i)) + (fluxN1(i) + fluxN2(i)));
|
||
}
|
||
return maxE;
|
||
}
|
||
|
||
void RusanovFlux::AverageGrad(int side, const Vector &state1,
|
||
const Vector &state2,
|
||
const Vector &nor, FaceElementTransformations &Tr,
|
||
DenseMatrix &grad) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector fluxN1(fluxFunction.num_equations), fluxN2(fluxFunction.num_equations);
|
||
#endif
|
||
|
||
#if defined(MFEM_USE_DOUBLE)
|
||
constexpr real_t tol = 1e-12;
|
||
#elif defined(MFEM_USE_SINGLE)
|
||
constexpr real_t tol = 4e-6;
|
||
#else
|
||
#error "Only single and double precision are supported!"
|
||
constexpr real_t tol = 1.;
|
||
#endif
|
||
|
||
auto equal_check = [=](real_t a, real_t b) -> bool { return std::abs(a - b) <= tol * std::abs(a + b); };
|
||
|
||
if (side == 1)
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
DenseMatrix JDotN(fluxFunction.num_equations);
|
||
#else
|
||
JDotN.SetSize(fluxFunction.num_equations);
|
||
#endif
|
||
const real_t speed1 = fluxFunction.ComputeFluxDotN(state1, nor, Tr, fluxN1);
|
||
const real_t speed2 = fluxFunction.ComputeAvgFluxDotN(state1, state2, nor, Tr,
|
||
fluxN2);
|
||
fluxFunction.ComputeFluxJacobianDotN(state1, nor, Tr, JDotN);
|
||
|
||
// NOTE: nor in general is not a unit normal
|
||
const real_t maxE = std::max(speed1, speed2);
|
||
// here, nor.Norml2() is multiplied to match the scale with fluxN
|
||
const real_t scaledMaxE = maxE * nor.Norml2() * 0.5;
|
||
|
||
grad = 0.;
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
// Only diagonal terms of J are considered
|
||
// lim_{u → u⁻} (F̄(u⁻,u)n - F(u⁻)n) / (u - u⁻) = ½λ
|
||
if (equal_check(state1(i), state2(i))) { continue; }
|
||
grad(i,i) = 0.5 * ((fluxN2(i) - fluxN1(i)) / (state2(i) - state1(i))
|
||
- JDotN(i,i) + scaledMaxE);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
const real_t speed1 = fluxFunction.ComputeAvgFluxDotN(state1, state2, nor, Tr,
|
||
fluxN1);
|
||
const real_t speed2 = fluxFunction.ComputeFluxDotN(state2, nor, Tr, fluxN2);
|
||
|
||
// NOTE: nor in general is not a unit normal
|
||
const real_t maxE = std::max(speed1, speed2);
|
||
// here, nor.Norml2() is multiplied to match the scale with fluxN
|
||
const real_t scaledMaxE = maxE * nor.Norml2() * 0.5;
|
||
|
||
grad = 0.;
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
// lim_{u → u⁻} (F(u)n - F̄(u⁻,u)n) / (u - u⁻) = ½λ
|
||
if (equal_check(state1(i), state2(i))) { continue; }
|
||
grad(i,i) = 0.5 * ((fluxN2(i) - fluxN1(i)) / (state2(i) - state1(i))
|
||
- scaledMaxE);
|
||
}
|
||
}
|
||
}
|
||
|
||
ComponentwiseUpwindFlux::ComponentwiseUpwindFlux(
|
||
const FluxFunction &fluxFunction)
|
||
: NumericalFlux(fluxFunction)
|
||
{
|
||
#ifndef MFEM_THREAD_SAFE
|
||
fluxN1.SetSize(fluxFunction.num_equations);
|
||
fluxN2.SetSize(fluxFunction.num_equations);
|
||
#endif
|
||
if (fluxFunction.dim > 1)
|
||
MFEM_WARNING("Upwinded flux is implemented only component-wise.")
|
||
}
|
||
|
||
real_t ComponentwiseUpwindFlux::Eval(const Vector &state1, const Vector &state2,
|
||
const Vector &nor, FaceElementTransformations &Tr,
|
||
Vector &flux) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector fluxN1(fluxFunction.num_equations), fluxN2(fluxFunction.num_equations);
|
||
#endif
|
||
const real_t speed1 = fluxFunction.ComputeFluxDotN(state1, nor, Tr, fluxN1);
|
||
const real_t speed2 = fluxFunction.ComputeFluxDotN(state2, nor, Tr, fluxN2);
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
if (state1(i) <= state2(i))
|
||
{
|
||
flux(i) = std::min(fluxN1(i), fluxN2(i));
|
||
}
|
||
else
|
||
{
|
||
flux(i) = std::max(fluxN1(i), fluxN2(i));
|
||
}
|
||
}
|
||
|
||
return std::max(speed1, speed2);
|
||
}
|
||
|
||
void ComponentwiseUpwindFlux::Grad(int side, const Vector &state1,
|
||
const Vector &state2,
|
||
const Vector &nor, FaceElementTransformations &Tr,
|
||
DenseMatrix &grad) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
DenseMatrix JDotN(fluxFunction.num_equations);
|
||
#else
|
||
JDotN.SetSize(fluxFunction.num_equations);
|
||
#endif
|
||
|
||
grad = 0.;
|
||
|
||
if (side == 1)
|
||
{
|
||
fluxFunction.ComputeFluxJacobianDotN(state1, nor, Tr, JDotN);
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
// Only diagonal terms of J are considered
|
||
grad(i,i) = std::max(JDotN(i,i), 0_r);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
fluxFunction.ComputeFluxJacobianDotN(state2, nor, Tr, JDotN);
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
// Only diagonal terms of J are considered
|
||
grad(i,i) = std::min(JDotN(i,i), 0_r);
|
||
}
|
||
}
|
||
}
|
||
|
||
real_t ComponentwiseUpwindFlux::Average(const Vector &state1,
|
||
const Vector &state2,
|
||
const Vector &nor, FaceElementTransformations &Tr,
|
||
Vector &flux) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector fluxN1(fluxFunction.num_equations), fluxN2(fluxFunction.num_equations);
|
||
#endif
|
||
const real_t speed1 = fluxFunction.ComputeFluxDotN(state1, nor, Tr, fluxN1);
|
||
const real_t speed2 = fluxFunction.ComputeAvgFluxDotN(state1, state2, nor, Tr,
|
||
fluxN2);
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
if (state1(i) <= state2(i))
|
||
{
|
||
flux(i) = std::min(fluxN1(i), fluxN2(i));
|
||
}
|
||
else
|
||
{
|
||
flux(i) = std::max(fluxN1(i), fluxN2(i));
|
||
}
|
||
}
|
||
|
||
return std::max(speed1, speed2);
|
||
}
|
||
|
||
void ComponentwiseUpwindFlux::AverageGrad(int side, const Vector &state1,
|
||
const Vector &state2,
|
||
const Vector &nor, FaceElementTransformations &Tr,
|
||
DenseMatrix &grad) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector fluxN1(fluxFunction.num_equations), fluxN2(fluxFunction.num_equations);
|
||
#endif
|
||
|
||
#if defined(MFEM_USE_DOUBLE)
|
||
constexpr real_t tol = 1e-12;
|
||
#elif defined(MFEM_USE_SINGLE)
|
||
constexpr real_t tol = 4e-6;
|
||
#else
|
||
#error "Only single and double precision are supported!"
|
||
constexpr real_t tol = 1.;
|
||
#endif
|
||
|
||
auto equal_check = [=](real_t a, real_t b) -> bool { return std::abs(a - b) <= tol * std::abs(a + b); };
|
||
|
||
if (side == 1)
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
DenseMatrix JDotN(fluxFunction.num_equations);
|
||
#else
|
||
JDotN.SetSize(fluxFunction.num_equations);
|
||
#endif
|
||
fluxFunction.ComputeFluxDotN(state1, nor, Tr, fluxN1);
|
||
fluxFunction.ComputeAvgFluxDotN(state1, state2, nor, Tr, fluxN2);
|
||
fluxFunction.ComputeFluxJacobianDotN(state1, nor, Tr, JDotN);
|
||
|
||
grad = 0.;
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
// Only diagonal terms of J are considered
|
||
// lim_{u → u⁻} (F̄(u⁻,u)n - F(u⁻)n) / (u - u⁻) = ½J(u⁻)n
|
||
const real_t gr12 = (!equal_check(state1(i), state2(i)))?
|
||
(fluxN2(i) - fluxN1(i)) / (state2(i) - state1(i))
|
||
:(0.5 * JDotN(i,i));
|
||
grad(i,i) = (gr12 >= 0.)?(JDotN(i,i)):(gr12);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
DenseMatrix JDotN;
|
||
#endif
|
||
fluxFunction.ComputeAvgFluxDotN(state1, state2, nor, Tr, fluxN1);
|
||
fluxFunction.ComputeFluxDotN(state2, nor, Tr, fluxN2);
|
||
|
||
// Jacobian is not needed except the limit case when u⁺=u⁻
|
||
bool J_needed = false;
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
if (equal_check(state1(i), state2(i)))
|
||
{
|
||
J_needed = true;
|
||
break;
|
||
}
|
||
|
||
if (J_needed)
|
||
{
|
||
JDotN.SetSize(fluxFunction.num_equations);
|
||
fluxFunction.ComputeFluxJacobianDotN(state1, nor, Tr, JDotN);
|
||
}
|
||
|
||
grad = 0.;
|
||
|
||
for (int i = 0; i < fluxFunction.num_equations; i++)
|
||
{
|
||
// Only diagonal terms of J are considered
|
||
// lim_{u → u⁻} (F(u)n - F̄(u⁻,u)n) / (u - u⁻) = ½J(u⁻)n
|
||
const real_t gr12 = (!equal_check(state1(i), state2(i)))?
|
||
(fluxN2(i) - fluxN1(i)) / (state2(i) - state1(i))
|
||
:(0.5 * JDotN(i,i));
|
||
grad(i,i) = std::min(gr12, 0_r);
|
||
}
|
||
}
|
||
}
|
||
|
||
real_t AdvectionFlux::ComputeFlux(const Vector &U,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &FU) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector bval(b.GetVDim());
|
||
#endif
|
||
b.Eval(bval, Tr, Tr.GetIntPoint());
|
||
MultVWt(U, bval, FU);
|
||
return bval.Norml2();
|
||
}
|
||
|
||
real_t AdvectionFlux::ComputeFluxDotN(const Vector &U,
|
||
const Vector &normal,
|
||
FaceElementTransformations &Tr,
|
||
Vector &FDotN) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector bval(b.GetVDim());
|
||
#endif
|
||
b.Eval(bval, Tr, Tr.GetIntPoint());
|
||
FDotN(0) = U(0) * (bval * normal);
|
||
return bval.Norml2();
|
||
}
|
||
|
||
real_t AdvectionFlux::ComputeAvgFlux(const Vector &U1, const Vector &U2,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &FU) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector bval(b.GetVDim());
|
||
#endif
|
||
b.Eval(bval, Tr, Tr.GetIntPoint());
|
||
Vector Uavg(1);
|
||
Uavg(0) = (U1(0) + U2(0)) * 0.5;
|
||
MultVWt(Uavg, bval, FU);
|
||
return bval.Norml2();
|
||
}
|
||
|
||
real_t AdvectionFlux::ComputeAvgFluxDotN(const Vector &U1, const Vector &U2,
|
||
const Vector &normal,
|
||
FaceElementTransformations &Tr,
|
||
Vector &FDotN) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector bval(b.GetVDim());
|
||
#endif
|
||
b.Eval(bval, Tr, Tr.GetIntPoint());
|
||
FDotN(0) = (U1(0) + U2(0)) * 0.5 * (bval * normal);
|
||
return bval.Norml2();
|
||
}
|
||
|
||
void AdvectionFlux::ComputeFluxJacobian(const Vector &state,
|
||
ElementTransformation &Tr,
|
||
DenseTensor &J) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector bval(b.GetVDim());
|
||
#endif
|
||
b.Eval(bval, Tr, Tr.GetIntPoint());
|
||
J = 0.;
|
||
for (int d = 0; d < dim; d++)
|
||
{
|
||
J(0,0,d) = bval(d);
|
||
}
|
||
}
|
||
|
||
void AdvectionFlux::ComputeFluxJacobianDotN(const Vector &state,
|
||
const Vector &normal,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &JDotN) const
|
||
{
|
||
#ifdef MFEM_THREAD_SAFE
|
||
Vector bval(b.GetVDim());
|
||
#endif
|
||
b.Eval(bval, Tr, Tr.GetIntPoint());
|
||
JDotN(0,0) = bval * normal;
|
||
}
|
||
|
||
real_t BurgersFlux::ComputeFlux(const Vector &U,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &FU) const
|
||
{
|
||
FU = U(0) * U(0) * 0.5;
|
||
return std::fabs(U(0));
|
||
}
|
||
|
||
real_t BurgersFlux::ComputeFluxDotN(const Vector &U,
|
||
const Vector &normal,
|
||
FaceElementTransformations &Tr,
|
||
Vector &FDotN) const
|
||
{
|
||
FDotN(0) = U(0) * U(0) * 0.5 * normal.Sum();
|
||
return std::fabs(U(0));
|
||
}
|
||
|
||
real_t BurgersFlux::ComputeAvgFlux(const Vector &U1,
|
||
const Vector &U2,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &FU) const
|
||
{
|
||
FU = (U1(0)*U1(0) + U1(0)*U2(0) + U2(0)*U2(0)) / 6.;
|
||
return std::max(std::fabs(U1(0)), std::fabs(U2(0)));
|
||
}
|
||
|
||
real_t BurgersFlux::ComputeAvgFluxDotN(const Vector &U1,
|
||
const Vector &U2,
|
||
const Vector &normal,
|
||
FaceElementTransformations &Tr,
|
||
Vector &FDotN) const
|
||
{
|
||
FDotN(0) = (U1(0)*U1(0) + U1(0)*U2(0) + U2(0)*U2(0)) / 6. * normal.Sum();
|
||
return std::max(std::fabs(U1(0)), std::fabs(U2(0)));
|
||
}
|
||
|
||
void BurgersFlux::ComputeFluxJacobian(const Vector &U,
|
||
ElementTransformation &Tr,
|
||
DenseTensor &J) const
|
||
{
|
||
J = 0.;
|
||
for (int d = 0; d < dim; d++)
|
||
{
|
||
J(0,0,d) = U(0);
|
||
}
|
||
}
|
||
|
||
void BurgersFlux::ComputeFluxJacobianDotN(const Vector &U,
|
||
const Vector &normal,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &JDotN) const
|
||
{
|
||
JDotN(0,0) = U(0) * normal.Sum();
|
||
}
|
||
|
||
real_t ShallowWaterFlux::ComputeFlux(const Vector &U,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &FU) const
|
||
{
|
||
const real_t height = U(0);
|
||
const Vector h_vel(U.GetData() + 1, dim);
|
||
|
||
const real_t energy = 0.5 * g * (height * height);
|
||
|
||
MFEM_ASSERT(height >= 0, "Negative Height");
|
||
|
||
for (int d = 0; d < dim; d++)
|
||
{
|
||
FU(0, d) = h_vel(d);
|
||
for (int i = 0; i < dim; i++)
|
||
{
|
||
FU(1 + i, d) = h_vel(i) * h_vel(d) / height;
|
||
}
|
||
FU(1 + d, d) += energy;
|
||
}
|
||
|
||
const real_t sound = std::sqrt(g * height);
|
||
const real_t vel = std::sqrt(h_vel * h_vel) / height;
|
||
|
||
return vel + sound;
|
||
}
|
||
|
||
|
||
real_t ShallowWaterFlux::ComputeFluxDotN(const Vector &U,
|
||
const Vector &normal,
|
||
FaceElementTransformations &Tr,
|
||
Vector &FUdotN) const
|
||
{
|
||
const real_t height = U(0);
|
||
const Vector h_vel(U.GetData() + 1, dim);
|
||
|
||
const real_t energy = 0.5 * g * (height * height);
|
||
|
||
MFEM_ASSERT(height >= 0, "Negative Height");
|
||
FUdotN(0) = h_vel * normal;
|
||
const real_t normal_vel = FUdotN(0) / height;
|
||
for (int i = 0; i < dim; i++)
|
||
{
|
||
FUdotN(1 + i) = normal_vel * h_vel(i) + energy * normal(i);
|
||
}
|
||
|
||
const real_t sound = std::sqrt(g * height);
|
||
const real_t vel = std::fabs(normal_vel) / std::sqrt(normal*normal);
|
||
|
||
return vel + sound;
|
||
}
|
||
|
||
|
||
real_t EulerFlux::ComputeFlux(const Vector &U,
|
||
ElementTransformation &Tr,
|
||
DenseMatrix &FU) const
|
||
{
|
||
// 1. Get states
|
||
const real_t density = U(0); // ρ
|
||
const Vector momentum(U.GetData() + 1, dim); // ρu
|
||
const real_t energy = U(1 + dim); // E, internal energy ρe
|
||
const real_t kinetic_energy = 0.5 * (momentum*momentum) / density;
|
||
// pressure, p = (γ-1)*(E - ½ρ|u|^2)
|
||
const real_t pressure = (specific_heat_ratio - 1.0) *
|
||
(energy - kinetic_energy);
|
||
|
||
// Check whether the solution is physical only in debug mode
|
||
MFEM_ASSERT(density >= 0, "Negative Density");
|
||
MFEM_ASSERT(pressure >= 0, "Negative Pressure");
|
||
MFEM_ASSERT(energy >= 0, "Negative Energy");
|
||
|
||
// 2. Compute Flux
|
||
for (int d = 0; d < dim; d++)
|
||
{
|
||
FU(0, d) = momentum(d); // ρu
|
||
for (int i = 0; i < dim; i++)
|
||
{
|
||
// ρuuᵀ
|
||
FU(1 + i, d) = momentum(i) * momentum(d) / density;
|
||
}
|
||
// (ρuuᵀ) + p
|
||
FU(1 + d, d) += pressure;
|
||
}
|
||
// enthalpy H = e + p/ρ = (E + p)/ρ
|
||
const real_t H = (energy + pressure) / density;
|
||
for (int d = 0; d < dim; d++)
|
||
{
|
||
// u(E+p) = ρu*(E + p)/ρ = ρu*H
|
||
FU(1 + dim, d) = momentum(d) * H;
|
||
}
|
||
|
||
// 3. Compute maximum characteristic speed
|
||
|
||
// sound speed, √(γ p / ρ)
|
||
const real_t sound = std::sqrt(specific_heat_ratio * pressure / density);
|
||
// fluid speed |u|
|
||
const real_t speed = std::sqrt(2.0 * kinetic_energy / density);
|
||
// max characteristic speed = fluid speed + sound speed
|
||
return speed + sound;
|
||
}
|
||
|
||
|
||
real_t EulerFlux::ComputeFluxDotN(const Vector &x,
|
||
const Vector &normal,
|
||
FaceElementTransformations &Tr,
|
||
Vector &FUdotN) const
|
||
{
|
||
// 1. Get states
|
||
const real_t density = x(0); // ρ
|
||
const Vector momentum(x.GetData() + 1, dim); // ρu
|
||
const real_t energy = x(1 + dim); // E, internal energy ρe
|
||
const real_t kinetic_energy = 0.5 * (momentum*momentum) / density;
|
||
// pressure, p = (γ-1)*(E - ½ρ|u|^2)
|
||
const real_t pressure = (specific_heat_ratio - 1.0) *
|
||
(energy - kinetic_energy);
|
||
|
||
// Check whether the solution is physical only in debug mode
|
||
MFEM_ASSERT(density >= 0, "Negative Density");
|
||
MFEM_ASSERT(pressure >= 0, "Negative Pressure");
|
||
MFEM_ASSERT(energy >= 0, "Negative Energy");
|
||
|
||
// 2. Compute normal flux
|
||
|
||
FUdotN(0) = momentum * normal; // ρu⋅n
|
||
// u⋅n
|
||
const real_t normal_velocity = FUdotN(0) / density;
|
||
for (int d = 0; d < dim; d++)
|
||
{
|
||
// (ρuuᵀ + pI)n = ρu*(u⋅n) + pn
|
||
FUdotN(1 + d) = normal_velocity * momentum(d) + pressure * normal(d);
|
||
}
|
||
// (u⋅n)(E + p)
|
||
FUdotN(1 + dim) = normal_velocity * (energy + pressure);
|
||
|
||
// 3. Compute maximum characteristic speed
|
||
|
||
// sound speed, √(γ p / ρ)
|
||
const real_t sound = std::sqrt(specific_heat_ratio * pressure / density);
|
||
// fluid speed |u|
|
||
const real_t speed = std::fabs(normal_velocity) / std::sqrt(normal*normal);
|
||
// max characteristic speed = fluid speed + sound speed
|
||
return speed + sound;
|
||
}
|
||
|
||
} // namespace mfem
|