14 Explicit reconstruction of derivatives for Finite Volumes
20 super( ComputeFirstDerivativesFD4, self ).
__init__(patch=solver._patch,
21 patch_overlap=solver._patch_overlap_new,
22 functor_implementation=
"""
23::exahype2::CellData patchData(
25 marker.x(), marker.h(),
30::exahype2::fd::fd4::reconstruct_first_derivatives(patchData,"""+str(solver._patch.dim[0])+
","+str(
int(solver._patch_overlap_new.dim[0]/2))+
","+str(solver._patch.no_of_unknowns)+
""",0);
32 reconstructed_array_memory_location=solver._reconstructed_array_memory_location,
33 guard=
"not marker.willBeRefined() and not marker.hasBeenRefined()",
34 add_assertions_to_halo_exchange=
False,
44 return super( ComputeFirstDerivativesFV, self ).
get_includes() +
"""
45 #include "exahype2/CellData.h"
46 #include "tarch/NonCriticalAssertions.h"
51 return __name__.replace(
".py",
"").replace(
".",
"_")
58 Explicit reconstruction of first derivatives for FD4 discretisation
60 FD4 is implemented as Runge-Kutta scheme. So we first have to recompute
61 the current solution guess according to the Butcher tableau. Then we feed
62 this reconstruction into the derivative computation. In theory, this is all
63 simple, but in practice things require more work.
65 We assume that developers follow @ref page_exahype_coupling "ExaHyPE's generic recommendation how to add additional traversals".
66 Yet, this is only half of the story. If they follow the vanilla blueprint
67 and plug into a step after the last time step
69 ~~~~~~~~~~~~~~~~~~~~~~~~~~
70 if (repositories::isLastGridSweepOfTimeStep()
72 repositories::StepRepository::toStepEnum( peano4::parallel::Node::getInstance().getCurrentProgramStep() ) != repositories::StepRepository::Steps::AdditionalMeshTraversal
74 peano4::parallel::Node::getInstance().setNextProgramStep(
75 repositories::StepRepository::toProgramStep( repositories::StepRepository::Steps::AdditionalMeshTraversal )
77 continueToSolve = true;
79 ~~~~~~~~~~~~~~~~~~~~~~~~~~
81 then the reconstruction is only done after the last and final Runge-Kutta
82 step. Alternatively, users might plug into the solver after each
83 Runge-Kutta step. In principle, we might say "so what", as all the
84 right-hand sides after the final step are there, so we can reconstruct
85 the final solution. However, there are two pitfalls:
87 1. A solver might not hold the rhs estimates persistently in-between two
89 2. The solver's state always is toggled in startTimeStep() on the solver
90 instance's class. Therefore, an additional grid sweep after the last
91 Runge-Kutta sweep cannot be distinguished from an additional sweep
92 after the whole time step. They are the same.
94 Consequently, we need special treatment for the very last Runge-Kutta step:
96 - If we have an additional grid sweep after an intermediate RK step, we use
97 the right-hand side estimate of @f$ \partial _t Q = F(Q) @f$ to store the
98 outcome. Furthermore, we scale the derivative with 1/dt, as the linear
99 combination of RK will later on multiple this estimate with dt again.
100 - If we are in the very last grid sweep, we work with the current estimate.
102 The actual derviative calculation is "outsourced" to the routine
104 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105 ::exahype2::CellData patchData(
107 marker.x(), marker.h(),
108 0.0, // timeStamp => not used here
109 0.0, // timeStepSize => not used here
112 ::exahype2::fd::fd4::reconstruct_first_derivatives(
114 {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, // int numberOfGridCellsPerPatchPerAxis,
116 {{NUMBER_OF_UNKNOWNS}},
117 {{NUMBER_OF_AUXILIARY_VARIABLES}}
119 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
121 Wrapping up oldQWithHalo and newQ in a CellData object is a slight
122 overkill (we could have done with only passing the two pointers, as we
123 don't use the time step size or similar anyway), but we follow the
124 general ExaHyPE pattern here - and in theory could rely on someone else
125 later to deploy this to GPUs, e.g.
127 Right after the reconstruction, we have to project the outcome back again
128 onto the faces. Here, we again have to take into account that we work
129 with a Runge-Kutta scheme. The cell hosts N blocks of cell data for the
130 N shots of Runge-Kutta. The reconstruction writes into the nth block
131 according to the current step. Consequently, the projection also has to
132 work with the nth block. exahype2.solvers.rkfd.actionsets.ProjectPatchOntoFaces
133 provides all the logic for this, so we assume that users add this one to
134 their script. As the present action set plugs into touchCellFirstTime()
135 and the projection onto the faces plugs into touchCellLastTime(), the
136 order is always correct no matter how you prioritise between the different
139 Further to the projection, we also have to roll over details. Again,
140 exahype2.solvers.rkfd.actionsets.ProjectPatchOnto has more documentation
148 super( ComputeFirstDerivativesFD4RK, self ).
__init__(patch=solver._patch,
149 patch_overlap=solver._patch_overlap_new,
150 functor_implementation=
"""
151#error Will be inserted later down in this Python file
153 reconstructed_array_memory_location=solver._reconstructed_array_memory_location,
154 guard=
"not marker.willBeRefined() and not marker.hasBeenRefined()",
155 add_assertions_to_halo_exchange=
False,
163 ComputeDerivativesOverCell = jinja2.Template(
"""
164 double timeStamp = fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp();
167 // double timeStepSize
168 {{COMPUTE_TIME_STEP_SIZE}}
170 {% for PREDICATE_NO in range(0,PREDICATES|length) %}
171 if ({{PREDICATES[PREDICATE_NO]}}) {
172 ::exahype2::enumerator::AoSLexicographicEnumerator enumeratorWithAuxiliaryVariablesOnReconstructedPatch( 1, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, {{HALO_SIZE}}, {{NUMBER_OF_UNKNOWNS}}, {{NUMBER_OF_AUXILIARY_VARIABLES}});
173 ::exahype2::enumerator::AoSLexicographicEnumerator enumeratorWithoutAuxiliaryVariables( {{RK_STEPS}}, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, 0, {{NUMBER_OF_UNKNOWNS}}, 0 );
175 dfor( dof, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}} ) {
176 for (int unknown=0; unknown<{{NUMBER_OF_UNKNOWNS}}; unknown++) {
177 {% for WEIGHT_NO in range(0,BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO]|length) %}
178 {% if BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO][WEIGHT_NO]!=0 %}
179 oldQWithHalo[ enumeratorWithAuxiliaryVariablesOnReconstructedPatch(0,dof,unknown) ] +=
180 {{BUTCHER_TABLEAU_RELATIVE_TIME_STEP_SIZES[PREDICATE_NO]}} * timeStepSize * {{BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO][WEIGHT_NO]}} *
181 fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value[ enumeratorWithoutAuxiliaryVariables({{PREDICATE_NO-1}},dof,unknown) ];
187 newQ = fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value + enumeratorWithoutAuxiliaryVariables({{PREDICATE_NO}},0,0);
191 assertion2( tarch::la::greaterEquals( timeStamp, 0.0 ), timeStamp, timeStepSize );
192 assertion2( tarch::la::greaterEquals( timeStepSize, 0.0 ), timeStamp, timeStepSize );
194 ::exahype2::fd::validatePatch(
196 {{NUMBER_OF_UNKNOWNS}},
197 {{NUMBER_OF_AUXILIARY_VARIABLES}},
198 {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}},
199 {{HALO_SIZE}}, // halo
200 std::string(__FILE__) + "(" + std::to_string(__LINE__) + "): " + marker.toString()
201 ); // previous time step has to be valid
203 // take into account that linear combination has already been computed,
204 // and rhs even might not be held persistently
205 if ( repositories::isLastGridSweepOfTimeStep() ) {
206 newQ = fineGridCell{{UNKNOWN_IDENTIFIER}}.value;
209 ::exahype2::CellData patchData(
211 marker.x(), marker.h(),
212 0.0, // timeStamp => not used here
213 0.0, // timeStepSize => not used here
216 ::exahype2::fd::fd4::reconstruct_first_derivatives(
218 {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, // int numberOfGridCellsPerPatchPerAxis,
220 {{NUMBER_OF_UNKNOWNS}},
221 {{NUMBER_OF_AUXILIARY_VARIABLES}}
224 if ( not repositories::isLastGridSweepOfTimeStep() ) {
225 ::exahype2::enumerator::AoSLexicographicEnumerator enumerator( 1, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, 0, {{NUMBER_OF_UNKNOWNS}}, 0 );
226 dfor( dof, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}} ) {
227 for (int unknown=0; unknown<{{NUMBER_OF_UNKNOWNS}}; unknown++) {
228 newQ[ enumerator(0,dof,unknown) ] *= 1.0 / timeStepSize;
243 return super( ComputeFirstDerivativesFD4RK, self ).
get_includes() +
"""
244 #include "exahype2/CellData.h"
245 #include "exahype2/fd/fd4/FD4.h"
246 #include "tarch/NonCriticalAssertions.h"
252 This is our plug-in point to alter the underlying dictionary
257 self.
_solver._init_dictionary_with_default_parameters(d)
258 self.
_solver.add_entries_to_text_replacement_dictionary(d)
261 d[
"BUTCHER_TABLEAU_RELATIVE_TIME_STEP_SIZES"] = self.
_butcher_tableau.time_step_sizes()
264 d[
"PREDICATES"] = self.
_solver._primary_sweeps_of_Runge_Kutta_step_on_cell
267 "{} and repositories::{}.getSolverState()=={}::SolverState::RungeKuttaSubStep{}".format(
268 self.
_solver._store_cell_data_default_guard(),
269 self.
_solver.get_name_of_global_instance(),
273 for step
in range(0, self.
_solver.number_of_Runge_Kutta_steps())
284 return __name__.replace(
".py",
"").replace(
".",
"_")
Explicit reconstruction of first derivatives for FD4 discretisation.
get_includes(self)
Return include statements that you need.
__init__(self, solver, is_enclave_solver)
patch: peano4.datamodel.Patch Patch which is to be used
_add_action_set_entries_to_dictionary(self, d)
This is our plug-in point to alter the underlying dictionary.
get_action_set_name(self)
Return unique action set name.
ComputeDerivativesOverCell
user_should_modify_template(self)
Is the user allowed to modify the output.
Explicit reconstruction of derivatives for Finite Volumes.
get_includes(self)
Return include statements that you need.
__init__(self, solver)
patch: peano4.datamodel.Patch Patch which is to be used
get_action_set_name(self)
Return unique action set name.
user_should_modify_template(self)
Is the user allowed to modify the output.