7 from exahype2.solvers.ButcherTableau
import ButcherTableau
8 from exahype2.solvers.rkfd.OneSweepPerRungeKuttaStep
import ReconstructLinearCombination
13 Explicit reconstruction of first derivatives for FD4 discretisation
15 FD4 is implemented as Runge-Kutta scheme. So we first have to recompute
16 the current solution guess according to the Butcher tableau. Then we feed
17 this reconstruction into the derivative computation. In theory, this is all
18 simple, but in practice things require more work.
20 We assume that developers follow @ref page_exahype_coupling "ExaHyPE's generic recommendation how to add additional traversals".
21 Yet, this is only half of the story. If they follow the vanilla blueprint
22 and plug into a step after the last time step
24 ~~~~~~~~~~~~~~~~~~~~~~~~~~
25 if (repositories::isLastGridSweepOfTimeStep()
27 repositories::StepRepository::toStepEnum( peano4::parallel::Node::getInstance().getCurrentProgramStep() ) != repositories::StepRepository::Steps::AdditionalMeshTraversal
29 peano4::parallel::Node::getInstance().setNextProgramStep(
30 repositories::StepRepository::toProgramStep( repositories::StepRepository::Steps::AdditionalMeshTraversal )
32 continueToSolve = true;
34 ~~~~~~~~~~~~~~~~~~~~~~~~~~
36 then the reconstruction is only done after the last and final Runge-Kutta
37 step. Alternatively, users might plug into the solver after each
38 Runge-Kutta step. In principle, we might say "so what", as all the
39 right-hand sides after the final step are there, so we can reconstruct
40 the final solution. However, there are two pitfalls:
42 1. A solver might not hold the rhs estimates persistently in-between two
44 2. The solver's state always is toggled in startTimeStep() on the solver
45 instance's class. Therefore, an additional grid sweep after the last
46 Runge-Kutta sweep cannot be distinguished from an additional sweep
47 after the whole time step. They are the same.
49 Consequently, we need special treatment for the very last Runge-Kutta step:
51 - If we have an additional grid sweep after an intermediate RK step, we use
52 the right-hand side estimate of @f$ \partial _t Q = F(Q) @f$ to store the
53 outcome. Furthermore, we scale the derivative with 1/dt, as the linear
54 combination of RK will later on multiple this estimate with dt again.
55 - If we are in the very last grid sweep, we work with the current estimate.
57 The actual derviative calculation is "outsourced" to the routine
59 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60 ::exahype2::CellData patchData(
62 marker.x(), marker.h(),
63 0.0, // timeStamp => not used here
64 0.0, // timeStepSize => not used here
67 ::exahype2::fd::fd4::reconstruct_first_derivatives(
69 {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, // int numberOfGridCellsPerPatchPerAxis,
71 {{NUMBER_OF_UNKNOWNS}},
72 {{NUMBER_OF_AUXILIARY_VARIABLES}}
74 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
76 Wrapping up oldQWithHalo and newQ in a CellData object is a slight
77 overkill (we could have done with only passing the two pointers, as we
78 don't use the time step size or similar anyway), but we follow the
79 general ExaHyPE pattern here - and in theory could rely on someone else
80 later to deploy this to GPUs, e.g.
82 Right after the reconstruction, we have to project the outcome back again
83 onto the faces. Here, we again have to take into account that we work
84 with a Runge-Kutta scheme. The cell hosts N blocks of cell data for the
85 N shots of Runge-Kutta. The reconstruction writes into the nth block
86 according to the current step. Consequently, the projection also has to
87 work with the nth block. exahype2.solvers.rkfd.actionsets.ProjectPatchOntoFaces
88 provides all the logic for this, so we assume that users add this one to
89 their script. As the present action set plugs into touchCellFirstTime()
90 and the projection onto the faces plugs into touchCellLastTime(), the
91 order is always correct no matter how you prioritise between the different
94 Further to the projection, we also have to roll over details. Again,
95 exahype2.solvers.rkfd.actionsets.ProjectPatchOnto has more documentation
103 super( ComputeFirstDerivativesFD4RK, self ).
__init__(patch=solver._patch,
104 patch_overlap=solver._patch_overlap_new,
105 patch_reconstructed=solver._patch_reconstructed,
106 guard=
"not marker.willBeRefined() and not marker.hasBeenRefined()",
107 add_assertions_to_halo_exchange=
False,
115 ComputeDerivativesOverCell = jinja2.Template(
"""
116 double timeStamp = fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp();
119 // double timeStepSize
120 {{COMPUTE_TIME_STEP_SIZE}}
122 {% for PREDICATE_NO in range(0,PREDICATES|length) %}
123 if ({{PREDICATES[PREDICATE_NO]}}) {
124 ::exahype2::enumerator::AoSLexicographicEnumerator enumeratorWithAuxiliaryVariablesOnReconstructedPatch( 1, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, {{HALO_SIZE}}, {{NUMBER_OF_UNKNOWNS}}, {{NUMBER_OF_AUXILIARY_VARIABLES}});
125 ::exahype2::enumerator::AoSLexicographicEnumerator enumeratorWithoutAuxiliaryVariables( {{RK_STEPS}}, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, 0, {{NUMBER_OF_UNKNOWNS}}, 0 );
127 dfor( dof, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}} ) {
128 for (int unknown=0; unknown<{{NUMBER_OF_UNKNOWNS}}; unknown++) {
129 {% for WEIGHT_NO in range(0,BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO]|length) %}
130 {% if BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO][WEIGHT_NO]!=0 %}
131 oldQWithHalo[ enumeratorWithAuxiliaryVariablesOnReconstructedPatch(0,dof,unknown) ] +=
132 {{BUTCHER_TABLEAU_RELATIVE_TIME_STEP_SIZES[PREDICATE_NO]}} * timeStepSize * {{BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO][WEIGHT_NO]}} *
133 fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value[ enumeratorWithoutAuxiliaryVariables({{PREDICATE_NO-1}},dof,unknown) ];
139 newQ = fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value + enumeratorWithoutAuxiliaryVariables({{PREDICATE_NO}},0,0);
143 assertion2( tarch::la::greaterEquals( timeStamp, 0.0 ), timeStamp, timeStepSize );
144 assertion2( tarch::la::greaterEquals( timeStepSize, 0.0 ), timeStamp, timeStepSize );
146 ::exahype2::fd::validatePatch(
148 {{NUMBER_OF_UNKNOWNS}},
149 {{NUMBER_OF_AUXILIARY_VARIABLES}},
150 {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}},
151 {{HALO_SIZE}}, // halo
152 std::string(__FILE__) + "(" + std::to_string(__LINE__) + "): " + marker.toString()
153 ); // previous time step has to be valid
155 // take into account that linear combination has already been computed,
156 // and rhs even might not be held persistently
157 if ( repositories::isLastGridSweepOfTimeStep() ) {
158 newQ = fineGridCell{{UNKNOWN_IDENTIFIER}}.value;
161 ::exahype2::CellData patchData(
163 marker.x(), marker.h(),
164 0.0, // timeStamp => not used here
165 0.0, // timeStepSize => not used here
168 ::exahype2::fd::fd4::reconstruct_first_derivatives(
170 {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, // int numberOfGridCellsPerPatchPerAxis,
172 {{NUMBER_OF_UNKNOWNS}},
173 {{NUMBER_OF_AUXILIARY_VARIABLES}}
176 if ( not repositories::isLastGridSweepOfTimeStep() ) {
177 ::exahype2::enumerator::AoSLexicographicEnumerator enumerator( 1, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, 0, {{NUMBER_OF_UNKNOWNS}}, 0 );
178 dfor( dof, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}} ) {
179 for (int unknown=0; unknown<{{NUMBER_OF_UNKNOWNS}}; unknown++) {
180 newQ[ enumerator(0,dof,unknown) ] *= 1.0 / timeStepSize;
195 return super( ComputeFirstDerivativesFD4RK, self ).
get_includes() +
"""
196 #include "exahype2/CellData.h"
197 #include "exahype2/fd/fd4/FD4.h"
198 #include "tarch/NonCriticalAssertions.h"
204 This is our plug-in point to alter the underlying dictionary
209 self.
_solver_solver._init_dictionary_with_default_parameters(d)
210 self.
_solver_solver.add_entries_to_text_replacement_dictionary(d)
212 d[
"BUTCHER_TABLEAU_WEIGHTS"] = self.
_butcher_tableau_butcher_tableau.weight_matrix()
213 d[
"BUTCHER_TABLEAU_RELATIVE_TIME_STEP_SIZES"] = self.
_butcher_tableau_butcher_tableau.time_step_sizes()
216 d[
"PREDICATES"] = self.
_solver_solver._primary_sweeps_of_Runge_Kutta_step_on_cell
219 "{} and repositories::{}.getSolverState()=={}::SolverState::RungeKuttaSubStep{}".format(
220 self.
_solver_solver._store_cell_data_default_guard(),
221 self.
_solver_solver.get_name_of_global_instance(),
225 for step
in range(0, self.
_solver_solver.number_of_Runge_Kutta_steps())
230 if operation_name == peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME:
238 return __name__.replace(
".py",
"").replace(
".",
"_")
def user_should_modify_template(self)
def _add_action_set_entries_to_dictionary(self, d)
def get_body_of_operation(self, operation_name)
ComputeDerivativesOverCell
def get_action_set_name(self)
def __init__(self, solver, is_enclave_solver)