22from abc
import abstractmethod
32 compute_number_of_face_projection_quantities,
40 An abstract class for any RKDG solver of any order
42 This is the base class of all Runge-Kutta DG solvers. It defines what kind of action
43 sets do exist, i.e., what in principle can be done while we run through the grid. It
44 also provides some kind of very basic infrastructure, i.e. what the name of a solver
45 is, what data is to be held per face or cell, or where in the multiscale mesh
46 we actually have to hold data.
48 The RungeKuttaDG class cannot/should not be instantiated. Its children actually
49 decide which steps are required or invoked in which order.
51 They do so by setting the guards: Each data structure has a guard which controls
52 if it is to be stored. Each action set has a guard that says if it should be
53 called in this particular mesh traversal.
54 If you want to redefine/reset these guards,
55 you have to redefine create_data_structures() and reset the guards after
56 you have called the superclass' operation. I recommend that you refrain from
57 defining completely new guards. Use the predefined guards instead and
58 refine them by adding more and and or clauses.
60 If you want to study the mathematics routines used by all the Python-generated
61 code, read the Discontinous Galerkin page in dg.h.
66 Each cell hosts a DG polynomial of order _dg_order. Technically, we store this polynomial
67 in a regular array (blockstructured data) with (_dg_order+1)^d entries. On top of this
68 current time step, we need the shots into the future. for RK(_rk_order), we need _rk_order
69 of these snapshots. They are called estimates.
71 On the boundary, I store both the left and right solution projections. Per RK step, we
72 project the linear combination onto the face. This is in line with the action set
73 ComputeFinalLinearCombination which simply copies over the old time step
74 into the linear combination field in the very first Runge-Kutta step.
76 Our DG implementation is not tied to one particular Riemann solver, even though most
77 users will likely use Rusanov. Therefore, it is not clear which data is to be projected
78 onto the faces. For plain Rusanov with only a flux function, the solutions are sufficient.
79 Other Riemann solvers or Rusanov's ncp terms however require the derivatives. So I give
80 the code the opportunity to work with different data cardinalities.
82 More details are provided in create_data_structures().
85 ## Control flow between this class and subclasses
87 There are three key routines: the constructor, create_data_structures() and
88 create_action_sets(). The constructor sets/memorises some solver variables (such as the
89 name) and then invokes the other two routines.
91 create_data_structures() establishes all the data structures tied to the
92 grid entities. If you want to alter the configuration of data tied to grid
93 entities, you should redefine create_data_structures(). However, any
94 subclass still should call Runge Kutta's create_data_structures() before that.
95 This will ensure that the baseline configuration of all data is in place.
96 After that, you can modify the properties.
98 create_action_sets() establishes the action sets, i.e., activities that are to
99 be triggered whenever you run a time step, you plot, you initialise the grid.
100 Same here: If you want to alter the configuration of the code, call
101 create_action_sets() of the superclass first, before you do fancy stuff. This
102 way, all the important action sets are in place.
105 ## General control flow and realisation of solver steps
107 A proper numerical time step is mapped onto a cascade of time step sweeps. Per sweep,
108 we realise the following steps (so step in a computational sense), though their exact
109 orchestration depends on the realisation chosen, i.e. some might merge some
110 subcalculations into one grid sweep while others distribute a calculation over multiple
113 - We combine the data of the current time step with all the shots into the future. These
114 shots are held in one large array. This is not super efficient, as we could build up
115 these arrays one by one, but I just use one large scratchpad even for the first line
116 in the Butcher tableau. See create_data_structures() for details. So all the estimates
117 and the current solution are combined according to the Butcher tableau.
119 It is up to the particular subclass to configure the guard of the linear combination
120 such that it is only computed when it is actually needed.
122 - Next, we project this linear combination onto the faces. At the same time, we compute
123 a new estimate for the volume integral. The time step size for this new estimate
124 again depends on the time step size from the Butcher tableau. The two action sets
125 both are volumetric, i.e. run per cell, but we can run them in parallel. The face
126 projection has to complete immediately (we need the data on the faces), while the
127 volumetric computation, in principle, doesn't have to finish prior to the next grid
128 sweep, as long as the linear combination remains persistent. It is only a temporary
129 thing, so will be destroyed immediately once we leave a cell.
132 ## Mandatory step of subclasses
134 There are different nuances of subclasses/algorithmic realisations, i.e., few solvers inherit
135 directly from RungeKuttaDG. However, even those subclasses still require you do provide some
136 additional information.
138 These are the fields you have to set:
140 _compute_time_step_size A simple string which defines a double timeStepSize. This snippet will
141 be used by both the Riemann solver and the volumetric integration. You don't have to care
142 about the Runge-Kutta scaling of time step sizes (or shifts), as this is all done by the
143 source code, but you have to feed an instruction into the solver how to determine a time step
146 self._compute_new_time_step_size = "const double newTimeStepSize = timeStepSize;"
149 ## Semantics of FaceLabel
151 - The update time stamp is the one we update in each and every
154 - The new time stamp is the valid one, i.e., the one that
155 corresponds to the face's EstimateProjection after a time step.
157 - The old time stamp is the one that corresponds to the (old)
160 - We set the updated flag if and only if we have finished the
161 last Runge-Kutta step.
168 face_projections: FaceProjections,
173 plot_grid_properties,
174 pde_terms_without_state: bool,
175 baseline_action_set_descend_invocation_order=0,
178 Constructor of the Runge-Kutta Discontinuous Galerkin solver
183 A unique name for the solver. This one will be used for all generated
184 classes. Also the C++ object instance later on will incorporate this
188 Order of the Discontinuous Galerkin approximation.
191 Runge-Kutta order, i.e., time stepping order.
193 polynomials: Polynomials
194 Picks which kind of polynomial to use.
196 face_projections: Integer
197 How many projections do we need per face. If you are only
198 interested in the left and right solution along a face, pass in 1. If you
199 want to have the fluxes or derivatives as well, pass in 2. The latter version
200 is the type of data structure you need for Rusanov, e.g.
202 There are a couple of pre-defined values for this guy.
205 Number of unknowns per Finite Volume voxel.
207 auxiliary_variables: int
208 Number of auxiliary variables per Finite Volume voxel. Eventually, both
209 unknowns and auxiliary_variables are merged into one big vector if we
210 work with AoS. But the solver has to be able to distinguish them, as
211 only the unknowns are subject to a hyperbolic formulation.
214 This size refers to the individual discretisation cell.
217 This size refers to the individual discretisation cell.
219 plot_grid_properties: Boolean
220 Clarifies whether a dump of the data should be enriched with grid info
221 (such as enclave status flags), too.
226 All the constructor parameters are stored in object attributes. There's a few
227 more which are of interest for subclasses:
229 _solver_template_file_class_name: String
230 This can be used by subclasses to pick which template is to be used to create
231 the abstract solver and the actual solver blueprint.
234 _number_of_derivatives_projected_onto_face: Int
236 _solver_states: [String]
237 Returns a list of strings of the individual solver states. The code will
238 automatically supplement this list with the following states:
243 PlottingAfterGridInitialisation
245 So you may implicitly assume that these guys do exist always.
247 assert rk_order >= 1,
"Runge-Kutta order has to be greater or equal to one"
258 "#error Please set the solver property _Riemann_compute_kernel_call in the Python class"
261 "#error Please set the solver property _Riemann_compute_kernel_call_stateless in the Python class"
266 self.
_kernel_namespace =
"#error Please set the solver property _kernel_namespace in the Python class"
269 self._unknowns and self._auxiliary_variables respectively hold the number of unknowns and
270 auxiliary variables in the equation to be computed. Unknowns are variables that change over
271 time whereas auxiliary variables can be space-dependent but don't vary over time.
272 These can be specified either as simple ints or by a dictionary
273 (e.g.) unknowns = {'a': 1, 'b': 1, 'c': 3}
274 in which the user specifies the multiplicity of the variable (the velocity has one component
275 per dimension for example.)
276 If they are specified by a dictionary then the code generates a "VariableShortcuts" file which
277 allows the user to specify a variable by name and automatically maps this to the right position
278 in an array for better legibility. Otherwise they must manually remember the position of each
281 use_var_shortcut is used to know whether or not the user passed their variables via a dict
282 variable_names and variable_pos are used internally to remember the names and respective
283 positions of variables if set by a dictionary.
289 if type(unknowns)
is dict:
292 for var
in list(unknowns.values()):
295 elif type(unknowns)
is int:
299 "Not a valid type for parameter unknowns, needs to be int or dictionary."
302 if type(auxiliary_variables)
is dict:
305 for var
in list(auxiliary_variables.values()):
308 elif type(auxiliary_variables)
is int:
312 "Not a valid type for parameter auxiliary_variables, needs to be int or dictionary."
323 if min_cell_h > max_cell_h:
327 +
") is bigger than max_cell_h ("
385 + self.__class__.__module__
387Stateless PDE terms: """
398Auxiliary variables: """
407Initial conditions: """
410Boundary conditions: """
413Refinement criterion: """
439 coarsest_tree_level = 0
440 while domain_size * 3 ** (-coarsest_tree_level) > self.
_max_cell_h:
441 coarsest_tree_level += 1
442 return coarsest_tree_level
446 finest_tree_level = 0
447 while domain_size * 3 ** (-finest_tree_level) > self.
_min_cell_h:
448 finest_tree_level += 1
449 return finest_tree_level
468Real type of this solver: """
472We assume that you use a domain size of (0,"""
474 +
""")^d. Peano 4 will cut this domain equidistantly
475and recursively into three parts along each coordinate axis. This yields a spacetree.
477The spacetree will at least have """
480The spacetree will at most have """
484The spacetree will thus span at least """
486 +
""" cells per coordinate axis.
487The spacetree will thus span at most """
489 +
""" cells per coordinate axis.
493 +
""") for the time stepping.
502 Add further includes to this property, if your action sets require some additional
503 routines from other header files.
511 Add further includes to this property, if your solver requires some additional
512 routines from other header files.
519 Add further includes to this property, if your action sets require some additional
520 routines from other header files.
527 Add further includes to this property, if your solver requires some additional
528 routines from other header files.
536 Recall in subclasses if you wanna change the number of unknowns
537 or auxiliary variables. See class description's subsection on
540 :: Call order and ownership
542 This operation can be called multiple times. However, only the very
543 last call matters. All previous calls are wiped out.
545 If you have a hierarchy of solvers, every create_data_structure()
546 should first(!) call its parent version. This way, you always ensure
547 that all data are in place before you continue to alter the more
548 specialised versions. So it is (logically) a top-down (general to
549 specialised) run through all create_data_structure() variants
550 within the inheritance tree.
552 :: Solver fields built up
554 _time_step: Patch ( (_dg_order+1)x(_dg_order+1)x(_dg_order+1) )
555 Actual polynomial data of the DG patch in the current time step.
557 _rhs_estimates: Patch ( (_dg_order+1)x(_dg_order+1)x(_dg_order+1)xno of RK steps )
558 These are the Runge-Kutta extrapolations. It is important that the
559 rk_order is mixed into the first argument, as the triple is
560 automatically truncated to a tuple if the code is translated with 2d.
561 Please note that the term rhs refers to the right-hand side of an ODE
563 @f$ \partial Q = F(Q) @f$
565 and thus does not include auxiliary variables.
567 _linear_combination_of_estimates: Patch ( (_dg_order+1)x(_dg_order+1)x(_dg_order+1) )
568 Non-persistent helper data structure.
570 _current_time_step_projection: Patch ( 2xface_projectionsx(_dg_order+1)x(_dg_order+1) )
571 This is the projection of the polynomial data of the current guess onto
572 the faces. It is important that the first dimension is only a 2. This
573 way, we can (logically) handle the projected data again as an overlap
574 of two patches with the width 1.
576 _estimate_projection: Patch ( Nxface_projectionsx(_dg_order+1)x(_dg_order+1) )
577 The N is usually 2. Usually this estimate projection holds the data
578 from the latest Runge-Kutta step. This implies that it never ever holds
579 the projection of the solution unless after the very first Runge-Kutta
580 step. After that, it is always a linear combination of estimates.
582 To avoid this plain behaviour, I make the last and final linear
583 combination project the solution again onto the faces, i.e. after
584 the very last step of the Runge-Kutta scheme, there will be a valid
585 representation of the solution _estimate_projection. Consult the
586 documentation of exahype2.solvers.rkdg.actionsets.ComputeFinalLinearCombination
587 for some further information. Also consult the semantics of the FaceLabel
590 _old_solution_projection: Same as _estimate_projection
591 This is a backup of the final solution stored in _estimate_projection.
594 _face_label: FaceLabel
595 See class description. General information such as "is boundary".
597 _cell_label: CellLabel
598 See class description. General information such as "is enclave".
600 Per default, I do always store the projections onto faces, and I
601 always keep the actual time step data and the cell projections. It is
602 up to the subclasses to alter this storage behaviour.
604 By default, I do not exchange any face data in-between two grid sweeps.
605 You will have to overwrite the behaviour in subclasses. Please note that
606 a face is communicated between two tree/domain partitions if and only if
607 it is stored persistently and the exchange flag is set, too.
611 self.
_basis.dofs_per_axis,
612 self.
_basis.dofs_per_axis,
613 self.
_basis.dofs_per_axis,
621 self.
_basis.dofs_per_axis,
622 self.
_basis.dofs_per_axis,
629 self.
_basis.dofs_per_axis,
630 self.
_basis.dofs_per_axis,
631 self.
_basis.dofs_per_axis,
640 self.
_basis.dofs_per_axis,
641 self.
_basis.dofs_per_axis,
650 self.
_basis.dofs_per_axis,
651 self.
_basis.dofs_per_axis,
657 (2, self.
_basis.dofs_per_axis, self.
_basis.dofs_per_axis),
750 assert False,
"Storage variant {} not supported".format(
754 self.
_estimate_projection.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
761#include "peano4/utils/Loop.h"
762#include "repositories/SolverRepository.h"
765 peano4.toolbox.blockstructured.get_face_merge_implementation(
770 self.
_old_solution_projection.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
776#include "peano4/utils/Loop.h"
777#include "repositories/SolverRepository.h"
780 self.
_current_time_step.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
786#include "peano4/utils/Loop.h"
787#include "repositories/SolverRepository.h"
790 peano4.toolbox.blockstructured.get_cell_merge_implementation(
795 self.
_rhs_estimates.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
801#include "peano4/utils/Loop.h"
802#include "repositories/SolverRepository.h"
811#include "peano4/utils/Loop.h"
812#include "repositories/SolverRepository.h"
820 self.
_Riemann_solution.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
826#include "repositories/SolverRepository.h"
833#include "../repositories/SolverRepository.h"
836#include "../repositories/SolverRepository.h"
839#include "../repositories/SolverRepository.h"
842#include "../repositories/SolverRepository.h"
856 Overwrite in subclasses if you wanna create different
859 :: Call order and ownership
861 This operation can be called multiple times over the construction of
862 a solver. However, only the very last call matters. All previous calls
865 If you have a hierarchy of solvers, every create_data_structure()
866 should first(!) call its parent version. This way, you always ensure
867 that all data are in place before you continue to alter the more
868 specialised versions. So it is (logically) a top-down (general to
869 specialised) run through all create_data_structure() variants
870 within the inheritance tree.
889 +
" and (repositories::"
891 +
".isLastGridSweepOfTimeStep() or repositories::"
893 +
".getSolverState()=="
895 +
"::SolverState::GridInitialisation)",
896 build_up_new_refinement_instructions=
True,
897 implement_previous_refinement_instructions=
True,
898 called_by_grid_construction=
False,
904 build_up_new_refinement_instructions=
False,
905 implement_previous_refinement_instructions=
True,
906 called_by_grid_construction=
False,
913 build_up_new_refinement_instructions=
True,
914 implement_previous_refinement_instructions=
True,
915 called_by_grid_construction=
True,
1013 "not marker.willBeRefined() "
1014 +
"and repositories::"
1016 +
".getSolverState()!="
1018 +
"::SolverState::GridConstruction"
1024 "not marker.willBeRefined() "
1025 +
"and repositories::"
1027 +
".getSolverState()!="
1029 +
"::SolverState::GridConstruction"
1035 Extend the guard via ands only. Never use an or, as subclasses might
1036 extend it as well, and they will append further ends.
1039 "not marker.willBeRefined() "
1040 +
"and repositories::"
1042 +
".getSolverState()!="
1044 +
"::SolverState::GridConstruction"
1050 Extend the guard via ands only. Never use an or, as subclasses might
1051 extend it as well, and they will append further ends.
1054 "not marker.hasBeenRefined() "
1055 +
"and repositories::"
1057 +
".getSolverState()!="
1059 +
"::SolverState::GridConstruction "
1060 +
"and repositories::"
1062 +
".getSolverState()!="
1064 +
"::SolverState::GridInitialisation"
1070 Extend the guard via ands only. Never use an or, as subclasses might
1071 extend it as well, and they will append further ends.
1074 "not marker.willBeRefined() "
1075 +
"and repositories::"
1077 +
".getSolverState()!="
1079 +
"::SolverState::GridConstruction"
1085 Extend the guard via ands only. Never use an or, as subclasses might
1086 extend it as well, and they will append further ends.
1089 "not marker.hasBeenRefined() "
1090 +
"and repositories::"
1092 +
".getSolverState()!="
1094 +
"::SolverState::GridConstruction "
1095 +
"and repositories::"
1097 +
".getSolverState()!="
1099 +
"::SolverState::GridInitialisation"
1104 return self.
_name +
"Q"
1108 return "instanceOf" + self.
_name
1113 Add all required data to the Peano4 project's datamodel
1114 so it is properly built up
1117 print(
"Polynomial basis")
1120 print(
"Face projections")
1135 Tell Peano what data to move around
1137 Inform Peano4 step which data are to be moved around via the
1138 use_cell and use_face commands. This operation is generic from
1139 ExaHyPE's point of view, i.e. I use it for all grid sweep types.
1153#include "tarch/la/Vector.h"
1155#include "peano4/utils/Globals.h"
1156#include "peano4/utils/Loop.h"
1158#include "repositories/SolverRepository.h"
1163 Add the action sets to the grid initialisation
1165 The AMR stuff has to be the very first thing. Actually, the AMR routines'
1166 interpolation doesn't play any role here. But the restriction indeed is
1167 very important, as we have to get the face data for BCs et al. The action
1168 set order is inverted while we ascend within the tree again. Therefore, we
1169 add the AMR action set first which means it will be called last when we go
1170 from fine to coarse levels within the tree.
1174 The order of the action sets is preserved throughout the steps down within
1175 the tree hierarchy. It is inverted throughout the backrolling.
1177 This is what we want to achieve:
1179 - Restrict the data to the coarser level if we are on a hanging face.
1192 The boundary information is set only once. It is therefore important that
1193 we ues the face label and initialise it properly.
1198 if evaluate_refinement_criterion:
1206 Use this one to set a description within the output patch file that tells
1207 the vis solver what the semantics of the entries are. Typicallly, I use
1208 a comma-separated list here.
1215 Dump snapshot of solution
1217 Consult the discussion in add_actions_to_init_grid() around the order
1218 of the individual action sets.
1220 It is important that we have the coupling/dynamic AMR part in here, as
1221 there might be pending AMR refinement requests that now are realised.
1222 For the same reason, we need the update of the face label and the update
1223 of the cell label in here: The AMR might just propagate over into the
1224 plotting, i.e. we might create new grid entities throughout the plot.
1225 These entities (faces and cells) have to be initialised properly.
1226 Otherwise, their un-initialised data will propagate through to the next
1229 To make the restriction work, we have to project the solutions onto the
1242 for z
in self.
_basis.quadrature_points:
1243 for y
in self.
_basis.quadrature_points:
1244 for x
in self.
_basis.quadrature_points:
1245 mapping.append((x, y, z))
1248 filename=output_path +
"solution-" + self.
_name,
1253 guard=
"repositories::plotFilter.plotPatch(marker) and "
1255 additional_includes=
"""
1256#include "exahype2/PlotFilter.h"
1257#include "../repositories/SolverRepository.h"
1259 precision=
"PlotterPrecision",
1260 time_stamp_evaluation=
"repositories::getMinTimeStamp()",
1261 plot_cell_data=
False,
1265 step.add_action_set(plot_patches_action_set)
1269 filename=output_path +
"grid-" + self.
_name,
1271 guard=
"repositories::plotFilter.plotPatch(marker) and "
1273 additional_includes=
"""
1274#include "exahype2/PlotFilter.h"
1275#include "../repositories/SolverRepository.h"
1277 plot_cell_data=
False,
1279 plot_grid_properties_action_set.descend_invocation_order = (
1282 step.add_action_set(plot_grid_properties_action_set)
1289 The tricky part here is that we have to get the order right.
1291 - Update of the labels: This can be done as very first step, as it
1292 might feed into follow-up steps.
1293 - Linear combination: This has to be the first step of a Runge-Kutta
1294 scheme. Without this first step, there's absolutely nothing we can
1296 - Couple resolutions and AMR: Once we have the linear combination,
1297 we can couple different resolutions.
1298 - Project linear combination onto faces: This should be done as soon
1299 as possible but after we've determined the linear combination. After
1300 all, we need the solution representation on the faces asap.
1301 - Solve volume integral: can run in parallel to the projection onto
1303 - Handle boundary: doesn't really matter when we insert it, as it plugs
1304 into the first face load, while all the other stuff so far are volumetric
1306 It is important that we do the inter-grid transfer operators before we
1307 apply the boundary conditions.
1308 - Solve Riemann problem: the constraint here is that it has to come after
1309 the boundary handling.
1310 - Add volume and face solution: last
1312 If we use enclave tasking, we have to be careful when we insert the merger.
1313 See SeparateSweepsWithEnclaveTasking.add_actions_to_perform_time_step() for
1314 a discussion of the details.
1324 step.add_action_set(
1332 step.add_action_set(
1346 The ExaHyPE project will call this operation when it sets
1347 up the overall environment.
1349 This routine is typically not invoked by a user.
1351 output: peano4.output.Output
1353 templatefile_prefix = os.path.dirname(os.path.realpath(__file__)) +
"/"
1355 if self._solver_template_file_class_name
is None:
1356 templatefile_prefix += self.__class__.__name__
1358 templatefile_prefix += self._solver_template_file_class_name
1363 abstractHeaderDictionary = {}
1364 implementationDictionary = {}
1365 self._init_dictionary_with_default_parameters(abstractHeaderDictionary)
1366 self._init_dictionary_with_default_parameters(implementationDictionary)
1367 self.add_entries_to_text_replacement_dictionary(abstractHeaderDictionary)
1368 self.add_entries_to_text_replacement_dictionary(implementationDictionary)
1370 generated_abstract_header_file = (
1372 templatefile_prefix +
"Abstract.template.h",
1373 templatefile_prefix +
"Abstract.template.cpp",
1374 "Abstract" + self._name,
1377 abstractHeaderDictionary,
1382 generated_solver_files = (
1384 templatefile_prefix +
".template.h",
1385 templatefile_prefix +
".template.cpp",
1389 implementationDictionary,
1395 output.add(generated_abstract_header_file)
1396 output.add(generated_solver_files)
1397 output.makefile.add_h_file(subdirectory +
"Abstract" + self._name +
".h", generated=
True)
1398 output.makefile.add_h_file(subdirectory + self._name +
".h", generated=
True)
1399 output.makefile.add_cpp_file(subdirectory +
"Abstract" + self._name +
".cpp", generated=
True)
1400 output.makefile.add_cpp_file(subdirectory + self._name +
".cpp", generated=
True)
1402 if self._use_var_shortcut:
1404 os.path.dirname(os.path.realpath(__file__))
1406 +
"../VariableShortcuts.template.h",
1407 "VariableShortcuts",
1410 implementationDictionary,
1414 output.add(generated_shortcut_file)
1415 output.makefile.add_h_file(subdirectory +
"VariableShortcuts.h", generated=
True)
1428 This one is called by all algorithmic steps before I invoke
1429 add_entries_to_text_replacement_dictionary().
1431 See the remarks on set_postprocess_updated_cell_kernel to understand why
1432 we have to apply the (partially befilled) dictionary to create a new entry
1433 for this very dictionary.
1435 d[
"DG_ORDER"] = self.
_basis.order
1440 d[
"SOLVER_NAME"] = self.
_name
1442 d[
"NUMBER_OF_UNKNOWNS"] = self.
_unknowns
1445 d[
"NUMBER_OF_DOFS_PER_CELL_2D"] = (self.
_basis.dofs_per_axis) * (
1446 self.
_basis.dofs_per_axis
1448 d[
"NUMBER_OF_DOFS_PER_CELL_3D"] = (
1449 (self.
_basis.dofs_per_axis)
1450 * (self.
_basis.dofs_per_axis)
1451 * (self.
_basis.dofs_per_axis)
1454 d[
"NUMBER_OF_DOFS_PER_FACE_2D"] = self.
_basis.dofs_per_axis
1455 d[
"NUMBER_OF_DOFS_PER_FACE_3D"] = (self.
_basis.dofs_per_axis) * (
1456 self.
_basis.dofs_per_axis
1459 d[
"ASSERTION_WITH_1_ARGUMENTS"] =
"nonCriticalAssertion1"
1460 d[
"ASSERTION_WITH_2_ARGUMENTS"] =
"nonCriticalAssertion2"
1461 d[
"ASSERTION_WITH_3_ARGUMENTS"] =
"nonCriticalAssertion3"
1462 d[
"ASSERTION_WITH_4_ARGUMENTS"] =
"nonCriticalAssertion4"
1463 d[
"ASSERTION_WITH_5_ARGUMENTS"] =
"nonCriticalAssertion5"
1464 d[
"ASSERTION_WITH_6_ARGUMENTS"] =
"nonCriticalAssertion6"
1467 raise Exception(
"min/max h are inconsistent")
1477 d[
"PREPROCESS_RECONSTRUCTED_CELL"] = jinja2.Template(
1480 d[
"POSTPROCESS_UPDATED_CELL_AFTER_RUNGE_KUTTA_STEP"] = jinja2.Template(
1482 undefined=jinja2.DebugUndefined,
1484 d[
"POSTPROCESS_UPDATED_CELL_AFTER_FINAL_LINEAR_COMBINATION"] = jinja2.Template(
1486 undefined=jinja2.DebugUndefined,
1489 "BOUNDARY_CONDITIONS_IMPLEMENTATION"
1492 "REFINEMENT_CRITERION_IMPLEMENTATION"
1495 d[
"ABSTRACT_SOLVER_USER_DECLARATIONS"] = jinja2.Template(
1498 d[
"ABSTRACT_SOLVER_USER_DEFINITIONS"] = jinja2.Template(
1501 d[
"SOLVER_USER_DECLARATIONS"] = jinja2.Template(
1504 d[
"SOLVER_USER_DEFINITIONS"] = jinja2.Template(
1507 d[
"START_TIME_STEP_IMPLEMENTATION"] = jinja2.Template(
1510 d[
"FINISH_TIME_STEP_IMPLEMENTATION"] = jinja2.Template(
1513 d[
"CONSTRUCTOR_IMPLEMENTATION"] = jinja2.Template(
1516 d[
"COMPUTE_TIME_STEP_SIZE"] = jinja2.Template(
1519 d[
"COMPUTE_NEW_TIME_STEP_SIZE"] = jinja2.Template(
1537 self.
_basis.init_dictionary_with_default_parameters(d,
False)
1540 d[
"VOLUMETRIC_COMPUTE_KERNEL_CALL"] = jinja2.Template(
1543 d[
"VOLUMETRIC_COMPUTE_KERNEL_CALL_STATELESS"] = jinja2.Template(
1546 d[
"RIEMANN_COMPUTE_KERNEL_CALL"] = jinja2.Template(
1549 d[
"RIEMANN_COMPUTE_KERNEL_CALL_STATELESS"] = jinja2.Template(
1552 d[
"ADD_SOLVER_CONTRIBUTIONS_CALL"] = jinja2.Template(
1555 d[
"MULTIPLY_WITH_INVERTED_MASS_MATRIX_CALL"] = jinja2.Template(
1557 undefined=jinja2.DebugUndefined,
1578 @auxiliary_variables.setter
1594 boundary_conditions=None,
1595 refinement_criterion=None,
1596 initial_conditions=None,
1599 additional_action_set_includes="",
1600 additional_user_includes="",
1603 If you pass in User_Defined, then the generator will create C++ stubs
1604 that you have to befill manually. If you pass in None_Implementation, it
1605 will create nop, i.e., no implementation or defaults. Any other string
1606 is copied 1:1 into the implementation. If you pass in None, then the
1607 set value so far won't be overwritten.
1609 if boundary_conditions
is not None:
1611 if refinement_criterion
is not None:
1613 if initial_conditions
is not None:
1616 if refinement_criterion == exahype2.solvers.PDETerms.None_Implementation:
1617 assert False,
"refinement criterion cannot be none"
1618 if initial_conditions == exahype2.solvers.PDETerms.None_Implementation:
1619 assert False,
"initial conditions cannot be none"
1621 if flux
is not None:
1625 if eigenvalues
is not None:
1627 if source_term
is not None:
1629 if point_source
is not None:
1649 @postprocess_updated_cell_after_Runge_Kutta_step.setter
1652 Define a postprocessing routine over the data
1654 This routine allows you to update the patch data immediately after the
1655 patch update. The routine provides the whole patch, and therefore you
1656 can write stuff similar to
1658 my_solver.postprocess_updated_cell = " ""
1660 constexpr int itmax = ({{DG_ORDER}} + 1) * ({{DG_ORDER}} + 1) * ({{DG_ORDER}} + 1);
1662 for (int i = 0; i < itmax; i++) {
1663 applications::exahype2::ccz4::enforceCCZ4constraints(dQdt + index);
1664 index += {{NUMBER_OF_UNKNOWNS}};
1669 ## Difference to Finite Volume solvers
1671 Different to the Finite Volume solvers, you don't have the auxiliary
1672 variables in this routine. You also don't have the new solution, but the
1673 time derivative instead.
1676 ## Available constants
1678 This list is not complete. You might want to consult the generated code to
1679 spot more variables that are on offer. Whenever I use the brackets below,
1680 these are symbolic constants which will be befilled with the proper constants
1681 when the postprocessing code snippet is inserted into the generated code.
1682 The other variables are directly available, i.e., no text replacement is done
1685 - {{DG_ORDER}} Spatial discretisation order.
1690 ## Runge-Kutta steps
1692 This postprocessing is called after each and every Runge-Kutta step.
1699 @postprocess_updated_cell_after_final_linear_combination.setter
1702 Define a postprocessing routine over the data
1704 This routine allows you to update the patch data immediately after the
1705 patch update. The routine provides the whole patch, and therefore you
1706 can write stuff similar to
1708 my_solver.postprocess_updated_cell = " ""
1710 constexpr int itmax = ({{DG_ORDER}} +1 ) * ({{DG_ORDER}} + 1) * ({{DG_ORDER}} + 1);
1712 for (int i = 0; i < itmax; i++) {
1713 applications::exahype2::ccz4::enforceCCZ4constraints(dQdt + index);
1714 index += {{NUMBER_OF_UNKNOWNS}};
1719 ## Difference to Finite Volume solvers
1721 Different to the Finite Volume solvers, you don't have the auxiliary
1722 variables in this routine. You also don't have the new solution, but the
1723 time derivative instead.
1726 ## Available constants
1728 This list is not complete. You might want to consult the generated code to
1729 spot more variables that are on offer. Whenever I use the brackets below,
1730 these are symbolic constants which will be befilled with the proper constants
1731 when the postprocessing code snippet is inserted into the generated code.
1732 The other variables are directly available, i.e., no text replacement is done
1735 - {{DG_ORDER}} Spatial discretisation order.
1738 ## Runge-Kutta steps
1740 This postprocessing is called after each and every Runge-Kutta step.
1749 cell_data_storage: Storage,
1750 face_data_storage: Storage,
1753 By default, we hold all data on the call stacks. You can explicitly switch
1754 to storage on the heap via smart pointers.
1756 @see create_data_structures()
1758 assert isinstance(cell_data_storage, Storage)
1759 assert isinstance(face_data_storage, Storage)
Update the cell label within a sweep.
An abstract class for any RKDG solver of any order.
get_min_number_of_spacetree_levels(self, domain_size)
_load_face_data_default_guard(self)
Extend the guard via ands only.
create_readme_descriptor(self, domain_offset, domain_size)
_action_set_project_linear_combination_onto_faces
_postprocess_updated_cell_after_Runge_Kutta_step
_action_set_update_cell_label
add_entries_to_text_replacement_dictionary(self, d)
_action_set_solve_Riemann_problem
get_coarsest_number_of_cells(self, domain_size)
_action_set_solve_volume_integral
get_name_of_global_instance(self)
_store_cell_data_default_guard(self)
Extend the guard via ands only.
_load_cell_data_default_guard(self)
Extend the guard via ands only.
_provide_cell_data_to_compute_kernels_default_guard(self)
_abstract_solver_user_definitions
add_to_Peano4_datamodel(self, datamodel, verbose)
Add all required data to the Peano4 project's datamodel so it is properly built up.
number_of_Runge_Kutta_steps(self)
add_actions_to_init_grid(self, step)
Add the action sets to the grid initialisation.
_solver_user_declarations
_add_solver_contributions_call
add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory="")
The ExaHyPE project will call this operation when it sets up the overall environment.
_action_set_compute_final_linear_combination_and_project_solution_onto_faces
_provide_face_data_to_compute_kernels_default_guard(self)
_Riemann_compute_kernel_call
_get_default_includes(self)
_action_set_initial_conditions_for_grid_construction
postprocess_updated_cell_after_final_linear_combination(self)
_store_face_data_default_guard(self)
Extend the guard via ands only.
_start_time_step_implementation
_constructor_implementation
_volumetric_compute_kernel_call
postprocess_updated_cell_after_Runge_Kutta_step(self)
add_actions_to_plot_solution(self, step, output_path)
Dump snapshot of solution.
_boundary_conditions_implementation
add_actions_to_create_grid(self, step, evaluate_refinement_criterion)
create_data_structures(self)
Recall in subclasses if you wanna change the number of unknowns or auxiliary variables.
_source_term_implementation
_action_set_postprocess_solution
_Riemann_compute_kernel_call_stateless
_finish_time_step_implementation
_volumetric_compute_kernel_call_stateless
add_solver_constants(self, datastring)
create_action_sets(self)
Overwrite in subclasses if you wanna create different action sets.
get_finest_number_of_cells(self, domain_size)
_action_set_update_face_label
_action_set_handle_boundary
_action_set_preprocess_solution
set_implementation(self, flux=None, ncp=None, eigenvalues=None, boundary_conditions=None, refinement_criterion=None, initial_conditions=None, source_term=None, point_source=None, additional_action_set_includes="", additional_user_includes="")
If you pass in User_Defined, then the generator will create C++ stubs that you have to befill manuall...
user_action_set_includes(self)
Add further includes to this property, if your action sets require some additional routines from othe...
_eigenvalues_implementation
user_solver_includes(self)
Add further includes to this property, if your solver requires some additional routines from other he...
switch_storage_scheme(self, Storage cell_data_storage, Storage face_data_storage)
By default, we hold all data on the call stacks.
_query_fine_grid_cell_in_action_set_if_it_holds_solution(self)
_init_dictionary_with_default_parameters(self, d)
This one is called by all algorithmic steps before I invoke add_entries_to_text_replacement_dictionar...
auxiliary_variables(self)
_abstract_solver_user_declarations
_multiply_with_inverted_mass_matrix_call
_action_set_AMR_commit_without_further_analysis
_baseline_action_set_descend_invocation_order
_point_sources_implementation
add_user_action_set_includes(self, value)
Add further includes to this property, if your action sets require some additional routines from othe...
_refinement_criterion_implementation
_solver_template_file_class_name
__init__(self, name, rk_order, polynomial_basis, FaceProjections face_projections, unknowns, auxiliary_variables, min_cell_h, max_cell_h, plot_grid_properties, bool pde_terms_without_state, baseline_action_set_descend_invocation_order=0)
Constructor of the Runge-Kutta Discontinuous Galerkin solver.
_user_action_set_includes
_unknown_identifier(self)
_compute_new_time_step_size
_action_set_linear_combination_of_estimates
_action_set_add_volume_and_face_solution
_action_set_initial_conditions
set_solver_constants(self, datastring)
_postprocess_updated_cell_after_final_linear_combination
get_max_number_of_spacetree_levels(self, domain_size)
add_actions_to_perform_time_step(self, step)
The tricky part here is that we have to get the order right.
add_use_data_statements_to_Peano4_solver_step(self, step)
Tell Peano what data to move around.
set_plot_description(self, description)
Use this one to set a description within the output patch file that tells the vis solver what the sem...
_initial_conditions_implementation
add_user_solver_includes(self, value)
Add further includes to this property, if your solver requires some additional routines from other he...
_action_set_couple_resolution_transitions_and_handle_dynamic_mesh_refinement
_action_set_AMR_throughout_grid_construction
_linear_combination_of_estimates
The action set to realise AMR.
The linear combination of the Runge Kutta trials has to be projected onto the faces,...
Action set determining the final solution using a linear combination of previous Runge-Kutta guesses.
Default DG interpolation/restriction action set.
The linear combination of the Runge Kutta trials has to be projected onto the faces,...
Computes a linear combination of all of the estimates according to the Butcher Tableau.
PostprocessSolution differs from other action sets, as I only create it once.
PreprocessSolution differs from other action sets, as I only create it once.
The linear combination of the Runge-Kutta trials has to be projected onto the faces,...
Solve the actual Riemann problem and add stuff back to the solution.
Realise patch via smart pointers.
Realise patch via smart pointers.
Very simple converter which maps the patch 1:1 onto a double array.
Action set (reactions to events)