Peano
ComputeFirstDerivatives.py
Go to the documentation of this file.
1 #
2 # New action set for second order CCZ4 formulation
3 #
4 import peano4
5 import jinja2
6 
7 from exahype2.solvers.ButcherTableau import ButcherTableau
8 from exahype2.solvers.rkfd.OneSweepPerRungeKuttaStep import ReconstructLinearCombination
9 
10 class ComputeFirstDerivativesFD4RK(peano4.toolbox.blockstructured.ReconstructPatch):
11  """!
12 
13  Explicit reconstruction of first derivatives for FD4 discretisation
14 
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.
19 
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
23 
24  ~~~~~~~~~~~~~~~~~~~~~~~~~~
25  if (repositories::isLastGridSweepOfTimeStep()
26  and
27  repositories::StepRepository::toStepEnum( peano4::parallel::Node::getInstance().getCurrentProgramStep() ) != repositories::StepRepository::Steps::AdditionalMeshTraversal
28  ) {
29  peano4::parallel::Node::getInstance().setNextProgramStep(
30  repositories::StepRepository::toProgramStep( repositories::StepRepository::Steps::AdditionalMeshTraversal )
31  );
32  continueToSolve = true;
33  }
34  ~~~~~~~~~~~~~~~~~~~~~~~~~~
35 
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:
41 
42  1. A solver might not hold the rhs estimates persistently in-between two
43  time steps.
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.
48 
49  Consequently, we need special treatment for the very last Runge-Kutta step:
50 
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.
56 
57  The actual derviative calculation is "outsourced" to the routine
58 
59  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60  ::exahype2::CellData patchData(
61  oldQWithHalo,
62  marker.x(), marker.h(),
63  0.0, // timeStamp => not used here
64  0.0, // timeStepSize => not used here
65  newQ
66  );
67  ::exahype2::fd::fd4::reconstruct_first_derivatives(
68  patchData,
69  {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, // int numberOfGridCellsPerPatchPerAxis,
70  {{HALO_SIZE}},
71  {{NUMBER_OF_UNKNOWNS}},
72  {{NUMBER_OF_AUXILIARY_VARIABLES}}
73  );
74  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75 
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.
81 
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
92  action sets.
93 
94  Further to the projection, we also have to roll over details. Again,
95  exahype2.solvers.rkfd.actionsets.ProjectPatchOnto has more documentation
96  on this.
97 
98  """
99  def __init__(self,
100  solver,
101  is_enclave_solver
102  ):
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,
108  )
109  self._solver_solver = solver
110  self._butcher_tableau_butcher_tableau = ButcherTableau(self._solver_solver._rk_order)
111  self._use_enclave_solver_use_enclave_solver = is_enclave_solver
112  pass
113 
114 
115  ComputeDerivativesOverCell = jinja2.Template( """
116  double timeStamp = fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp();
117 
118  // Set the variable
119  // double timeStepSize
120  {{COMPUTE_TIME_STEP_SIZE}}
121 
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 );
126 
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) ];
134  {% endif %}
135  {% endfor %}
136  }
137  }
138 
139  newQ = fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value + enumeratorWithoutAuxiliaryVariables({{PREDICATE_NO}},0,0);
140  }
141  {% endfor %}
142 
143  assertion2( tarch::la::greaterEquals( timeStamp, 0.0 ), timeStamp, timeStepSize );
144  assertion2( tarch::la::greaterEquals( timeStepSize, 0.0 ), timeStamp, timeStepSize );
145 
146  ::exahype2::fd::validatePatch(
147  oldQWithHalo,
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
154  """ + """
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;
159  }
160 
161  ::exahype2::CellData patchData(
162  oldQWithHalo,
163  marker.x(), marker.h(),
164  0.0, // timeStamp => not used here
165  0.0, // timeStepSize => not used here
166  newQ
167  );
168  ::exahype2::fd::fd4::reconstruct_first_derivatives(
169  patchData,
170  {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, // int numberOfGridCellsPerPatchPerAxis,
171  {{HALO_SIZE}},
172  {{NUMBER_OF_UNKNOWNS}},
173  {{NUMBER_OF_AUXILIARY_VARIABLES}}
174  );
175 
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;
181  }
182  }
183  }
184  """)
185 
186 
187 # """+str(solver._patch.dim[0])+","+str(int(solver._patch_overlap_new.dim[0]/2))+","+str(solver._patch.no_of_unknowns)+"""
188 
189 
191  return False
192 
193 
194  def get_includes(self):
195  return super( ComputeFirstDerivativesFD4RK, self ).get_includes() + """
196  #include "exahype2/CellData.h"
197  #include "exahype2/fd/fd4/FD4.h"
198  #include "tarch/NonCriticalAssertions.h"
199  """
200 
202  """!
203 
204  This is our plug-in point to alter the underlying dictionary
205 
206  """
207  super(ComputeFirstDerivativesFD4RK, self)._add_action_set_entries_to_dictionary(d)
208 
209  self._solver_solver._init_dictionary_with_default_parameters(d)
210  self._solver_solver.add_entries_to_text_replacement_dictionary(d)
211 
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()
214 
215  if self._use_enclave_solver_use_enclave_solver:
216  d["PREDICATES"] = self._solver_solver._primary_sweeps_of_Runge_Kutta_step_on_cell
217  else:
218  d["PREDICATES"] = [
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(),
222  self._solver_solver._name,
223  step,
224  )
225  for step in range(0, self._solver_solver.number_of_Runge_Kutta_steps())
226  ]
227 
228  def get_body_of_operation(self, operation_name):
229  result = super(ComputeFirstDerivativesFD4RK, self).get_body_of_operation(self, operation_name)
230  if operation_name == peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME:
231  d = {}
232  self._add_action_set_entries_to_dictionary_add_action_set_entries_to_dictionary(d)
233  result += self.ComputeDerivativesOverCellComputeDerivativesOverCell.format(**d)
234  return result
235 
236 
238  return __name__.replace(".py", "").replace(".", "_")