template<size_t Dim, typename EvolutionSystem, typename DgStepChoosers, bool LocalTimeStepping, bool UseNodegroupDgElements>
struct evolution::dg::Actions::ComputeTimeDerivative< Dim, EvolutionSystem, DgStepChoosers, LocalTimeStepping, UseNodegroupDgElements >
Computes the time derivative for a DG time step.
Computes the volume fluxes, the divergence of the fluxes and all additional interior contributions to the time derivatives (both nonconservative products and source terms). The internal mortar data is also computed.
The general first-order hyperbolic evolution equation solved for conservative systems is:
\begin{align*} \frac{\partial u_\alpha}{\partial \hat{t}} + \partial_{i} \left(F^i_\alpha - v^i_g u_\alpha\right) = S_\alpha-u_\alpha\partial_i v^i_g, \end{align*}
where \(F^i_{\alpha}\) are the fluxes when the mesh isn't moving, \(v^i_g\) is the velocity of the mesh, \(u_{\alpha}\) are the evolved variables, \(S_{\alpha}\) are the source terms, \(\hat{t}\) is the time in the logical frame, \(t\) is the time in the inertial frame, hatted indices correspond to logical frame quantites, and unhatted indices to inertial frame quantities (e.g. \(\partial_i\) is the derivative with respect to the inertial coordinates). For evolution equations that do not have any fluxes and only nonconservative products we evolve:
\begin{align*} \frac{\partial u_\alpha}{\partial \hat{t}} +\left(B^i_{\alpha\beta}-v^i_g \delta_{\alpha\beta} \right)\partial_{i}u_\beta = S_\alpha. \end{align*}
Finally, for equations with both conservative terms and nonconservative products we use:
\begin{align*} \frac{\partial u_\alpha}{\partial \hat{t}} + \partial_{i} \left(F^i_\alpha - v^i_g u_\alpha\right) +B^i_{\alpha\beta}\partial_{i}u_\beta = S_\alpha-u_\alpha\partial_i v^i_g, \end{align*}
where \(B^i_{\alpha\beta}\) is the matrix for the nonconservative products.
Volume Terms
The mesh velocity is added to the flux automatically if the mesh is moving. That is,
\begin{align*} F^i_{\alpha}\to F^i_{\alpha}-v^i_{g} u_{\alpha} \end{align*}
The source terms are also altered automatically by adding:
\begin{align*} -u_\alpha \partial_i v^i_g, \end{align*}
For systems with equations that only contain nonconservative products, the following mesh velocity is automatically added to the time derivative:
\begin{align*} v^i_g \partial_i u_\alpha, \end{align*}
- Note
- The term is always added in the
Frame::Inertial frame, and the plus sign arises because we add it to the time derivative.
- Warning
- The mesh velocity terms are added to the time derivatives before invoking the boundary conditions. This means that the time derivatives passed to the boundary conditions are with respect to \(\hat{t}\), not \(t\). This is especially important in the TimeDerivative/Bjorhus boundary conditions.
Here are examples of the TimeDerivative struct used to compute the volume time derivative. This struct is what the type alias System::compute_volume_time_derivative points to. The time derivatives are as gsl::not_null first, then the temporary tags as gsl::not_null, followed by the argument_tags. These type aliases are given by
using temporary_tags = tmpl::list<Var3Squared>;
using common_argument_tags = tmpl::list<Var1, Var2<Dim>, Var3>;
using argument_tags =
tmpl::conditional_t<HasPrimitiveVars,
tmpl::push_back<common_argument_tags, PrimVar1>,
common_argument_tags>;
for the examples. For a conservative system without primitives the apply function would look like
static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(
// Time derivatives returned by reference. All the tags in the
// variables_tag in the system struct.
const gsl::not_null<Scalar<DataVector>*> dt_var1,
const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> dt_var2,
// Fluxes returned by reference. Listed in the system struct as
// flux_variables.
const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> flux_var1,
const gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>
flux_var2,
// Temporaries returned by reference. Listed in temporary_tags above.
const gsl::not_null<Scalar<DataVector>*> square_var3,
// Arguments listed in argument_tags above
const Scalar<DataVector>& var1,
const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,
const Scalar<DataVector>& var3) {
get(*square_var3) = square(get(var3));
// Set source terms
get(*dt_var1) = get(*square_var3);
for (size_t d = 0; d < Dim; ++d) {
dt_var2->get(d) = get(var3) * d;
}
// Set fluxes
for (size_t i = 0; i < Dim; ++i) {
flux_var1->get(i) = square(get(var1)) * var2.get(i);
for (size_t j = 0; j < Dim; ++j) {
flux_var2->get(i, j) = var2.get(i) * var2.get(j) * get(var1);
if (i == j) {
flux_var2->get(i, j) += cube(get(var1));
}
}
}
return {true};
}
For a nonconservative system it would be
static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(
// Time derivatives returned by reference. All the tags in the
// variables_tag in the system struct.
const gsl::not_null<Scalar<DataVector>*> dt_var1,
const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> dt_var2,
// Temporaries returned by reference. Listed in temporary_tags above.
const gsl::not_null<Scalar<DataVector>*> square_var3,
// Partial derivative arguments. Listed in the system struct as
// gradient_variables.
const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,
const tnsr::iJ<DataVector, Dim, Frame::Inertial>& d_var2,
// Arguments listed in argument_tags above
const Scalar<DataVector>& var1,
const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,
const Scalar<DataVector>& var3) {
get(*square_var3) = square(get(var3));
// Set source terms and nonconservative products
get(*dt_var1) = get(*square_var3);
for (size_t d = 0; d < Dim; ++d) {
get(*dt_var1) -= var2.get(d) * d_var1.get(d);
dt_var2->get(d) = get(var3) * d;
for (size_t i = 0; i < Dim; ++i) {
dt_var2->get(d) -= get(var1) * var2.get(i) * d_var2.get(i, d);
}
}
return {true};
}
And finally, for a mixed conservative-nonconservative system with primitive variables
static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(
const gsl::not_null<Scalar<DataVector>*> dt_var1,
const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> dt_var2,
const gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>
flux_var2,
const gsl::not_null<Scalar<DataVector>*> square_var3,
const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,
const Scalar<DataVector>& var1,
const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,
const Scalar<DataVector>& var3, const Scalar<DataVector>& prim_var1) {
apply(dt_var1, dt_var2, flux_var2, square_var3, d_var1, var1, var2, var3);
get(*dt_var1) += get(prim_var1);
return {true};
}
In addition to each variable being passed individually, if the time derivative struct inherits from evolution::PassVariables, then the time derivatives, fluxes, and temporaries are passed as gsl::not_null<Variables<...>>. This is useful for systems where additional quantities are sometimes evolved, and just generally nice for keeping the number of arguments reasonable. Below are the above examples but with Variables being passed.
static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(
// Time derivatives returned by reference. All the tags in the
// variables_tag in the system struct.
const gsl::not_null<Variables<tmpl::list<dt_var1, dt_var2>>*> dt_vars,
// Fluxes returned by reference. Listed in the system struct as
// flux_variables.
const gsl::not_null<Variables<tmpl::list<flux_var1, flux_var2>>*>
flux_vars,
// Temporaries returned by reference. Listed in temporary_tags above.
const gsl::not_null<Variables<tmpl::list<Var3Squared>>*> temporaries,
// Arguments listed in argument_tags above
const Scalar<DataVector>& var1,
const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,
const Scalar<DataVector>& var3) {
// just forward to other implementation to reduce code duplication
base::apply(get<dt_var1>(dt_vars), get<dt_var2>(dt_vars),
get<flux_var1>(flux_vars), get<flux_var2>(flux_vars),
get<Var3Squared>(temporaries), var1, var2, var3);
return {true};
}
static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(
// Time derivatives returned by reference. All the tags in the
// variables_tag in the system struct.
const gsl::not_null<Variables<tmpl::list<dt_var1, dt_var2>>*> dt_vars,
// Temporaries returned by reference. Listed in temporary_tags above.
const gsl::not_null<Variables<tmpl::list<Var3Squared>>*> temporaries,
// Partial derivative arguments. Listed in the system struct as
// gradient_variables.
const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,
const tnsr::iJ<DataVector, Dim, Frame::Inertial>& d_var2,
// Arguments listed in argument_tags above
const Scalar<DataVector>& var1,
const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,
const Scalar<DataVector>& var3) {
// just forward to other implementation to reduce code duplication
base::apply(get<dt_var1>(dt_vars), get<dt_var2>(dt_vars),
get<Var3Squared>(temporaries), d_var1, d_var2, var1, var2,
var3);
return {true};
}
static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(
const gsl::not_null<Variables<tmpl::list<dt_var1, dt_var2>>*> dt_vars,
const gsl::not_null<Variables<tmpl::list<flux_var2>>*> flux_vars,
const gsl::not_null<Variables<tmpl::list<Var3Squared>>*> temporaries,
const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,
const Scalar<DataVector>& var1,
const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,
const Scalar<DataVector>& var3, const Scalar<DataVector>& prim_var1) {
// just forward to other implementation to reduce code duplication
base::apply(get<dt_var1>(dt_vars), get<dt_var2>(dt_vars),
get<flux_var2>(flux_vars), get<Var3Squared>(temporaries),
d_var1, var1, var2, var3, prim_var1);
return {true};
}
Uses:
- System:
variables_tag
flux_variables
gradient_variables
compute_volume_time_derivative_terms
- DataBox:
- Items in
system::compute_volume_time_derivative_terms::argument_tags
domain::Tags::MeshVelocity<Metavariables::volume_dim>
Metavariables::system::variables_tag
Metavariables::system::flux_variables
Metavariables::system::gradient_variables
domain::Tags::DivMeshVelocity
DirectionsTag,
- Required interface items for
Metavariables::system::normal_dot_fluxes
DataBox changes:
- Adds: nothing
- Removes: nothing
- Modifies:
Internal Boundary Terms
Internal boundary terms must be derived from evolution::BoundaryCorrection. Each concrete boundary correction must specify:
- type alias
dg_package_field_tags. These are what will be returned by gsl::not_null from the dg_package_data member function.
- type alias
dg_package_data_temporary_tags. These are temporary tags that are projected to the face and then passed to the dg_package_data function.
- type alias
dg_package_data_primitive_tags. These are the primitive variables (if any) that are projected to the face and then passed to dg_package_data.
- type alias
dg_package_data_volume_tags. These are tags that are not projected to the interface and are retrieved directly from the DataBox. The equation of state for hydrodynamics systems is an example of what would be a "volume tag".
A static constexpr bool need_normal_vector must be specified. If true then the normal vector is computed from the normal covector. This is currently not implemented.
The dg_package_data function takes as arguments gsl::not_null of the dg_package_field_tags, then the projected evolved variables, the projected fluxes, the projected temporaries, the projected primitives, the unit normal covector, mesh velocity, normal dotted into the mesh velocity, the volume_tags, and finally the dg::Formulation. The dg_package_data function must compute all ingredients for the boundary correction, including mesh-velocity-corrected characteristic speeds. However, the projected fluxes passed in are \(F^i - u v^i_g\) (the mesh velocity term is already included). The dg_package_data function must also return a double that is the maximum absolute characteristic speed over the entire face. This will be used for checking that the time step doesn't violate the CFL condition.
Here is an example of the type aliases and bool:
using dg_package_field_tags = tmpl::push_back<
tmpl::append<db::wrap_tags_in<::Tags::NormalDotFlux, variables_tags>,
variables_tags>,
MaxAbsCharSpeed>;
using dg_package_data_temporary_tags = tmpl::list<Var3Squared>;
using dg_package_data_primitive_tags =
tmpl::conditional_t<HasPrims, tmpl::list<PrimVar1>, tmpl::list<>>;
using dg_package_data_volume_tags =
tmpl::conditional_t<HasPrims, tmpl::list<Tags::TimeStepId>, tmpl::list<>>;
using dg_boundary_terms_volume_tags =
tmpl::conditional_t<HasPrims, tmpl::list<Tags::TimeStepId>, tmpl::list<>>;
The normal vector requirement is:
static constexpr bool need_normal_vector = false;
For a conservative system with primitive variables and using the TimeStepId as a volume tag the dg_package_data function looks like:
double dg_package_data(
const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,
const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>
out_normal_dot_flux_var2,
const gsl::not_null<Scalar<DataVector>*> out_var1,
const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,
const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,
const Scalar<DataVector>& var1,
const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,
const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,
const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,
const Scalar<DataVector>& var3_squared,
const Scalar<DataVector>& prim_var1,
const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,
const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&
mesh_velocity,
const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,
const TimeStepId& time_step_id) const {
dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,
out_var1, out_var2, max_abs_char_speed, var1, var2,
flux_var1, flux_var2, var3_squared, normal_covector,
mesh_velocity, normal_dot_mesh_velocity);
get(*out_var1) += get(prim_var1) + time_step_id.step_time().value();
return max(get(*max_abs_char_speed));
}
For a mixed conservative-nonconservative system with primitive variables and using the TimeStepId as a volume tag the dg_package_data function looks like:
double dg_package_data(
const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,
const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>
out_normal_dot_flux_var2,
const gsl::not_null<Scalar<DataVector>*> out_var1,
const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,
const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,
const Scalar<DataVector>& var1,
const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,
const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,
const Scalar<DataVector>& var3_squared,
const Scalar<DataVector>& prim_var1,
const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,
const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&
mesh_velocity,
const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,
const TimeStepId& time_step_id) const {
dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,
out_var1, out_var2, max_abs_char_speed, var1, var2,
flux_var2, var3_squared, normal_covector, mesh_velocity,
normal_dot_mesh_velocity);
get(*out_var1) += get(prim_var1) + time_step_id.step_time().value();
return max(get(*max_abs_char_speed));
}
Uses:
- System:
boundary_correction
variables_tag
flux_variables
gradients_tags
compute_volume_time_derivative
has_primitive_and_conservative_vars
primitive_variables_tag if system has primitive variables
- DataBox:
domain::Tags::Element<Dim>
domain::Tags::Mesh<Dim>
evolution::dg::Tags::MortarMesh<Dim>
evolution::dg::Tags::MortarData<Dim>
Tags::TimeStepId
Tag which is either a SimpleTag for quantities on an interface, base tag to a compute item which acts...
Definition: Tags.hpp:344
The computational grid of the Element in the DataBox.
Definition: Tags.hpp:62
The set of directions to neighboring Elements.
Definition: Tags.hpp:247
The unnormalized face normal one form.
Definition: FaceNormal.hpp:129
Metavariables::system::variables_tag
Metavariables::system::flux_variables
Metavariables::system::primitive_tags if exists
- boundary correction
dg_package_data_volume_tags
DataBox changes:
- Adds: nothing
- Removes: nothing
- Modifies: