4from abc
import abstractmethod
20 Abstract finite volume solver step sizes that works on patch-based AMR with a halo layer of one.
22 FV is the base class of all FV solvers. It defines what kind of action sets do
23 exist, i.e., what in principle can be done while we run through the grid. It also
24 provides some kind of very basic infrastructure, i.e., what the name of a solver
25 is, what data is to be held per face or cell, or where in the multiscale mesh
26 we actually have to hold data.
28 The FV class cannot/should not be instantiated. There are two direct children:
29 SingleSweep and EnclaveTasking. The FV base class defines what data are available.
30 The two subclasses define how we run through the mesh: once per time step or
31 twice per time step with some tasking. So FV defines what can be done, the
32 subclasses define how the steps are orchestrated. Again, they do not (yet) say
33 what is computed. That's then the responsibility of classes that inherit from
34 SingleSweep or EnclaveTasking, respectively.
36 A finite volume solver in ExaHyPE 2 (actually any solver) is first of all a
37 static collection of data logically tied to grid entities and some operations
38 over the mesh that are just there. All data and action sets however have guards,
39 i.e., boolean guards that define
41 - are data to be stored in-between traversals,
42 - are action sets really invoked in a particular traversal or can they be
45 In the baseline FV class, these guards are usually set to default values.
46 That is, all action sets per time step are invoked always (the guard is
47 true) and all data are always stored on the finest mesh. If you want to alter
48 this behaviour, i.e., store data not always or skip steps, you can overwrite
49 the corresponding attributes of the attributes, i.e., the guards of the
52 See the discussion on "Control flow between this class and subclasses" below.
56 I do equip both Q and NewQ with proper merge routines. However, all merge guards
57 set to "never" by default. If you need some data exchange, you have to activate
60 ## Control flow between this class and subclasses
62 There are three key routines: the constructor, create_data_structures() and
63 create_action_sets(). The constructor sets some global variables (such as the
64 name) and then invokes the other two routines.
66 create_data_structures() establishes all the data structures tied to the
67 grid entities. It also sets some properties of these data such as the patch
68 size, e.g. If you want to add additional data (such as additional quantities
69 per cell) or if you want to alter the configuration of data tied to grid
70 entities, you should redefine create_data_structures(). However, any
71 subclass still should call FV's create_data_structures() - or the create_data_structures()
72 of the SingleSweep or EnclaveTasking, respectively. This will ensure that the
73 baseline configuration of all data is in place. After that, you can modify
76 create_action_sets() establishes the action sets, i.e. activities that are to
77 be triggered whenever you run a time step, you plot, you initialise the grid.
79 Both create_data_structures() and create_action_sets() add attributes to the
80 FV class. See self._patch for example within create_action_sets(). These
81 attributes have guards such as self._action_set_initial_conditions.guard.
82 These guards are set to defaults in FV. It is basically the job of SingleSweep
83 or EnclaveTasking - they determine when which data are used - to reset these
84 guards from a default to something tailored to the particular data flow.
86 If you want to redefine when data is stored or operations are invoked, overwrite
87 create_data_structures(), and call the superclass, i.e. either SingleSweep or
88 EnclaveTasking. This is what happens:
90 - SingleSweep or EnclaveTasking pass on the call to FV.create_data_structures().
91 This call ensures that all the data structures are in place. Then it returns.
92 - SingleSweep's or EnclaveTasking's create_data_structures() then sets the
93 guard, i.e. they configure when data is stored or used.
94 - Finally, your own overwrite of create_data_structures() can augment the
95 data structures (which are now in place and called at the right time) with
96 information what it actually shall do.
98 ## Adaptive mesh refinement (AMR)
100 We use by default a linear interpolation and averaging. For the linear interpolation,
101 I do not compute the operators on demand. Instead, I use the optimised scheme which
102 computes the operators once and then reuses them as static operation.
104 If you wanna alter the inter-resolution transfer operators, please use
106 self._interpolation = "tensor_product< " + self._name + ">"
107 self.add_solver_constants( "" "static constexpr double NormalInterpolationMatrix1d[] = {
111 self.add_solver_constants( "" "static constexpr double TangentialInterpolationMatrix1d[] = {
128 The above snippet refers to a overlap of one and five unknowns. So the interpolation
129 along a 1d tangential direction is three 5x5 matrices. Along the normal, we have to
130 project onto one target element, so our projection matrix has one row. We have two
131 values (inside and outside) along this normal, so two columns. In the standard
132 enumeration scheme (refers to the face with normal 0), we have one real coarse
133 grid value in this case. The other one would be the restricted value. We don't
134 often use it (and therefore have a 0 here).
136 ## Data postprocessing or coupling to other codes
138 For data postprocessing, the introduction of fancy interior conditions or the coupling to
139 other codes, each Finite Volume solver has an attribute
141 _action_set_postprocess_solution
143 You can use this attribute to add postprocessing routines to the code.
155 plot_grid_properties,
156 pde_terms_without_state: bool,
158 baseline_action_set_descend_invocation_order=0,
164 A unique name for the solver. This one will be used for all generated
165 classes. Also the C++ object instance later on will incorporate this
169 Size of the patch in one dimension. All stuff here's dimension-generic.
172 That's the size of the halo layer which is half of the overlap with a
173 neighbour. A value of 1 means that a patch_size x patch_size patch in
174 2d is surrounded by one additional cell layer. The overlap has to be
175 bigger or equal to one. It has to be smaller or equal to patch_size.
178 Number of unknowns per Finite Volume voxel.
180 auxiliary_variables: int
181 Number of auxiliary variables per Finite Volume voxel. Eventually, both
182 unknowns and auxiliary_variables are merged into one big vector if we
183 work with AoS. But the solver has to be able to distinguish them, as
184 only the unknowns are subject to a hyperbolic formulation.
187 This size refers to the individual Finite Volume.
190 This size refers to the individual Finite Volume.
192 plot_grid_properties: Boolean
193 Clarifies whether a dump of the data should be enriched with grid info
194 (such as enclave status flags), too.
206 self._unknowns and self._auxiliary_variables respectively hold the number of unknowns and
207 auxiliary variables in the equation to be computed. Unknowns are variables that change over
208 time whereas auxiliary variables can be space-dependent but don't vary over time.
209 These can be specified either as simple ints or by a dictionary
210 (e.g.) unknowns = {'a': 1, 'b': 1, 'c': 3}
211 in which the user specifies the multiplicity of the variable (the velocity has one component
212 per dimension for example.)
213 If they are specified by a dictionary then the code generates a "VariableShortcuts" file which
214 allows the user to specify a variable by name and automatically maps this to the right position
215 in an array for better legibility. Otherwise they must manually remember the position of each
218 use_var_shortcut is used to know whether or not the user passed their variables via a dict
219 variable_names and variable_pos are used internally to remember the names and respective
220 positions of variables if set by a dictionary.
226 if type(unknowns)
is dict:
229 for var
in list(unknowns.values()):
232 elif type(unknowns)
is int:
236 "Not a valid type for parameter unknowns, needs to be int or dictionary."
239 if type(auxiliary_variables)
is dict:
242 for var
in list(auxiliary_variables.values()):
245 elif type(auxiliary_variables)
is int:
249 "Not a valid type for parameter auxiliary_variables, needs to be int or dictionary."
256 if min_volume_h > max_volume_h:
260 +
") is bigger than max_volume_h ("
266 peano4.toolbox.blockstructured.ReconstructedArrayMemoryLocation.CallStack
329 + self.__class__.__module__
331Stateless PDE terms: """
346Auxiliary variables: """
355Initial conditions: """
358Boundary conditions: """
361Refinement criterion: """
384 coarsest_tree_level = 0
386 domain_size * 3 ** (-coarsest_tree_level) / self.
_patch_size
389 coarsest_tree_level += 1
390 return coarsest_tree_level
394 finest_tree_level = 0
396 domain_size * 3 ** (-finest_tree_level) / self.
_patch_size
399 finest_tree_level += 1
400 return finest_tree_level
439Real type of this solver: """
443We assume that you use a domain size of (0,"""
445 +
""")^d. Peano 4 will cut this domain equidistantly
446and recursively into three parts along each coordinate axis. This yields a spacetree.
448The spacetree will at least have """
451The spacetree will at most have """
455The spacetree will thus span at least """
457 +
""" cells (or patches) per coordinate axis. This means a """
459 +
"""^d grid of patches.
460The spacetree will thus span at most """
462 +
""" cells (or patches) per coordinate axis. This means a """
464 +
"""^d grid of patches.
468 +
"""^d patches of Finite Volumes into the finest tree level.
470The coarsest possible mesh will consist of """
472 +
""" Finite Volumes per coordinate axis.
473The finest possible mesh will consist of """
475 +
""" Finite Volumes per coordinate axis.
477The coarsest mesh width of """
479 +
""" is thus just smaller than the maximum volume size """
482The finest mesh width of """
484 +
""" is thus just smaller than the minimum volume size """
494 Add further includes to this property, if your action sets require some additional
495 routines from other header files.
503 Add further includes to this property, if your solver requires some additional
504 routines from other header files.
511 Add further includes to this property, if your action sets require some additional
512 routines from other header files.
519 Add further includes to this property, if your solver requires some additional
520 routines from other header files.
529 Create data structures required by all Finite Volume solvers
531 Recall in subclasses if you wanna change the number of unknowns
532 or auxiliary variables. See class description's subsection on
535 :: Call order and ownership
537 This operation can be called multiple times. However, only the very
538 last call matters. All previous calls are wiped out.
540 If you have a hierarchy of solvers, every create_data_structures()
541 should first(!) call its parent version. This way, you always ensure
542 that all data are in place before you continue to alter the more
543 specialised versions. So it is (logically) a top-down (general to
544 specialised) run through all create_data_structures() variants
545 within the inheritance tree.
549 _patch: Patch (NxNxN)
550 Actual patch data. We use Finite Volumes, so this is
551 always the current snapshot, i.e. the valid data at one point.
553 _patch_overlap_old, _patch_overlap_new: Patch (2xNxN)
554 This is a copy/excerpt from the two adjacent finite volume
555 snapshots plus the old data as backup. If I want to implement
556 local timestepping, I don't have to backup the whole patch
557 (see _patch), but I need a backup of the face data to be able
558 to interpolate in time.
560 _patch_overlap_update: Patch (2xNxN)
561 This is the new update. After the time step, I roll this
562 information over into _patch_overlap_new, while I backup the
563 previous _patch_overlap_new into _patch_overlap_old. If I
564 worked with regular meshes only, I would not need this update
565 field and could work directly with _patch_overlap_new. However,
566 AMR requires me to accumulate data within new while I need
567 the new and old data temporarily. Therefore, I employ this
568 accumulation/roll-over data which usually is not stored
605 assert False,
"Storage variant {} not supported".format(
652 assert False,
"Storage variant {} not supported".format(
657 peano4.toolbox.blockstructured.get_face_merge_implementation(
662 peano4.toolbox.blockstructured.get_face_merge_implementation(
666 self.
_patch.generator.merge_method_definition = (
667 peano4.toolbox.blockstructured.get_cell_merge_implementation(self.
_patch)
671#include "peano4/utils/Loop.h"
672#include "repositories/SolverRepository.h"
675#include "peano4/utils/Loop.h"
676#include "repositories/SolverRepository.h"
679 self.
_patch.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
694 self.
_patch_overlap_old.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
700 self.
_patch_overlap_new.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
706 self.
_patch_overlap_update.generator.load_store_compute_flag =
"::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
712 self.
_patch.generator.includes +=
"""
713#include "../repositories/SolverRepository.h"
716#include "../repositories/SolverRepository.h"
719#include "../repositories/SolverRepository.h"
722#include "../repositories/SolverRepository.h"
732 Create all the action sets
734 Overwrite in subclasses if you wanna create different
735 action sets. Subclasses also might redefine the invocation order.
737 ## Call order and ownership
739 This operation can be called multiple times. However, only the very
740 last call matters. All previous calls are wiped out.
742 If you have a hierarchy of solvers, every create_data_structures()
743 should first(!) call its parent version. This way, you always ensure
744 that all data are in place before you continue to alter the more
745 specialised versions. So it is (logically) a top-down (general to
746 specialised) run through all create_data_structures() variants
747 within the inheritance tree.
749 ## Recreation vs backup (state discussion)
751 We faced some issues with action sets that should not be
752 overwritten. For example, the postprocessing should not be overwritten
753 as users might want to set it and then later on reset the number of
754 unknowns, e.g. In this case, you would loose your postprocessing if
755 create_action_sets() recreated them. So I decided to make an exception
756 here: the postprocessing step is never overwritten by the standard
759 There are further action sets which have a state, which users might
760 want to alter. The most prominent one is the AMR routines, where users
761 often alter the interpolation and restriction scheme. Here, things are
762 tricky: If we keep the default one, the action set would not pick up
763 if you changed the number of unknowns, e.g. However, if we recreated
764 the action set, we'd miss out on any changed interpolation/restriction
765 scheme. Therefore, I have to hold the interpolation and restriction
781 build_up_new_refinement_instructions=
True,
782 implement_previous_refinement_instructions=
True,
783 called_by_grid_construction=
True,
789 build_up_new_refinement_instructions=
False,
790 implement_previous_refinement_instructions=
True,
791 called_by_grid_construction=
True,
797 build_up_new_refinement_instructions=
True,
798 implement_previous_refinement_instructions=
True,
799 called_by_grid_construction=
False,
882 """! Default logic when to create cell data or not
884 ExaHyPE allows you to define when you want to load or store data, and
885 you can also define if you need some grid data for your computations.
886 You might have some temporary data per cell which is neither stored
887 nor loaded, but that has to be there while you compute. Or there might
888 be cells which don't need to store a particular piece of data. See
889 ::peano4::grid::LoadStoreComputeFlag for some details. This predicate
890 here is a default. Particular solvers will take it as a starting point
891 to make more detailed rules: an enclave solver for example might
892 decide to take the information where to store data, but to toggle the
893 storage on and off depending on the solver phase.
895 return "not marker.willBeRefined() and repositories::{}.getSolverState()!={}::SolverState::GridConstruction".format(
903 "not marker.willBeRefined() "
904 +
"and repositories::"
906 +
".getSolverState()!="
908 +
"::SolverState::GridConstruction"
914 Extend the guard via ands only. Never use an or, as subclasses might
915 extend it as well, and they will append further ends.
918 "not marker.willBeRefined() "
919 +
"and repositories::"
921 +
".getSolverState()!="
923 +
"::SolverState::GridConstruction"
929 Extend the guard via ands only. Never use an or, as subclasses might
930 extend it as well, and they will append further ends.
933 "not marker.hasBeenRefined() "
934 +
"and repositories::"
936 +
".getSolverState()!="
938 +
"::SolverState::GridConstruction "
939 +
"and repositories::"
941 +
".getSolverState()!="
943 +
"::SolverState::GridInitialisation"
949 Extend the guard via ands only. Never use an or, as subclasses might
950 extend it as well, and they will append further ends.
953 "not marker.willBeRefined() "
954 +
"and repositories::"
956 +
".getSolverState()!="
958 +
"::SolverState::GridConstruction"
964 Extend the guard via ands only. Never use an or, as subclasses might
965 extend it as well, and they will append further ends.
968 "not marker.hasBeenRefined() "
969 +
"and repositories::"
971 +
".getSolverState()!="
973 +
"::SolverState::GridConstruction "
974 +
"and repositories::"
976 +
".getSolverState()!="
978 +
"::SolverState::GridInitialisation"
983 return self.
_name +
"Q"
987 return "instanceOf" + self.
_name
992 Add all required data to the Peano project's datamodel
993 so it is properly built up.
999 print(
"Patch overlap data")
1002 print(
"Patch overlap data")
1006 datamodel.add_cell(self.
_patch)
1015 Tell Peano what data to move around
1017 Inform Peano4 step which data are to be moved around via the
1018 use_cell and use_face commands. This operation is generic from
1019 ExaHyPE's point of view, i.e. I use it for all grid sweep types.
1021 It is important that the cell label comes first, as some other
1022 data might make their load/store decisions depend on the label.
1024 step.use_cell(self.
_patch)
1034#include "tarch/la/Vector.h"
1036#include "peano4/utils/Globals.h"
1037#include "peano4/utils/Loop.h"
1039#include "repositories/SolverRepository.h"
1045 Add all the action sets to init grid
1047 The AMR stuff has to be the very first thing. Actually, the AMR routines'
1048 interpolation doesn't play any role here. But the restriction indeed is
1049 very important, as we have to get the face data for BCs et al. The action
1050 set order is inverted while we ascend within the tree again. Therefore, we
1051 add the AMR action set first which means it will be called last when we go
1052 from fine to coarse levels within the tree.
1056 The order of the action sets is preserved throughout the steps down within
1057 the tree hierarchy. It is inverted throughout the backrolling.
1059 This is what we want to achieve:
1061 - Project solution onto faces. This happens in touchCellLastTime(). See
1062 exahype2.solvers.fv.actionsets.ProjectPatchOntoFaces for comments.
1063 The project will end up in QUpdate.
1064 - Roll updates over on the faces from QUpdate into Q_new. This is done
1065 by RollOverUpdateFace, which requires in turn that the getUpdated()
1066 flag is set. As the roll-over plugs into touchFaceLastTime(), it will
1067 always be called after the projection, since the latter is a cell
1069 - Copy new face data Q_new into old face data Q_old, as this is the
1070 initial sweep, i.e. the old face data otherwise might hold rubbish.
1071 This is a backup operation realised through the action set
1072 BackupPatchOverlap. This one plugs into touchFaceLastTime() too.
1073 Therefore, it is important that its priority is smaller than the one
1074 of the roll-over, since we invert the call order for the touch-last
1076 - Restrict the data to the coarser level if we are on a hanging face.
1077 - The postprocessing is the very last thing. It does not matter if it
1078 plugs into touchCellFirstTime() or touchCellLastTime(). As we invert the
1079 order when we backtrace, the postprocessing will have completed before
1080 we invoke _action_set_project_patch_onto_faces, i.e. any update will
1081 be projected onto the faces.
1089 step.add_action_set(
1104 The boundary information is set only once. It is therefore important that
1105 we ues the face label and initialise it properly.
1110 if evaluate_refinement_criterion:
1123 @plot_description.setter
1126 Add a proper description to the plots
1128 Use this one to set a description within the output patch file that tells
1129 the solver what the semantics of the entries are. Typically, I use
1130 a comma-separated list here.
1137 Add action sets to plot solution step
1139 Consult the discussion in add_actions_to_init_grid() around the order
1140 of the individual action sets.
1142 It is important that we have the coupling/dynamic AMR part in here, as
1143 there might be pending AMR refinement requests that now are realised.
1144 For the same reason, we need the update of the face label and the update
1145 of the cell label in here: The AMR might just propagate over into the
1146 plotting, i.e. we might create new grid entities throughout the plot.
1147 These entities (faces and cells) have to be initialised properly.
1148 Otherwise, their un-initialised data will propagate through to the next
1151 To make the restriction work, we have to project the solutions onto the
1158 step.add_action_set(
1168 filename=output_path +
"solution-" + self.
_name,
1172 guard=
"repositories::plotFilter.plotPatch(marker) and "
1174 additional_includes=
"""
1175#include "exahype2/PlotFilter.h"
1176#include "../repositories/SolverRepository.h"
1178 precision=
"PlotterPrecision",
1179 time_stamp_evaluation=
"0.5*(repositories::getMinTimeStamp()+repositories::getMaxTimeStamp())",
1183 step.add_action_set(plot_patches_action_set)
1187 filename=output_path +
"grid-" + self.
_name,
1189 guard=
"repositories::plotFilter.plotPatch(marker) and "
1191 additional_includes=
"""
1192#include "exahype2/PlotFilter.h"
1193#include "../repositories/SolverRepository.h"
1196 plot_grid_properties_action_set.descend_invocation_order = (
1199 step.add_action_set(plot_grid_properties_action_set)
1205 Add action sets to time step
1207 See exahype2.solvers.fv.FV.add_actions_to_init_grid() for details on the
1208 ordering of the action sets.
1212 It is important that we do the inter-grid transfer operators before we
1213 apply the boundary conditions.
1220 step.add_action_set(
1239 The ExaHyPE project will call this operation when it sets
1240 up the overall environment.
1242 This routine is typically not invoked by a user.
1244 output: peano4.output.Output
1246 templatefile_prefix = os.path.dirname(os.path.realpath(__file__)) +
"/"
1248 if self._solver_template_file_class_name
is None:
1249 templatefile_prefix += self.__class__.__name__
1251 templatefile_prefix += self._solver_template_file_class_name
1256 abstractHeaderDictionary = {}
1257 implementationDictionary = {}
1258 self._init_dictionary_with_default_parameters(abstractHeaderDictionary)
1259 self._init_dictionary_with_default_parameters(implementationDictionary)
1261 header_file_abstr_template = templatefile_prefix +
"Abstract.template.h"
1262 cpp_file_abstr_template = templatefile_prefix +
"Abstract.template.cpp"
1263 abstractHeaderDictionary[
"HEADER_FILE_ABSTR_TEMPLATE" ] = os.path.basename(header_file_abstr_template)
1264 abstractHeaderDictionary[
"CPP_FILE_ABSTR_TEMPLATE" ] = os.path.basename(cpp_file_abstr_template)
1265 self.add_entries_to_text_replacement_dictionary(abstractHeaderDictionary)
1267 header_file_template = templatefile_prefix +
".template.h"
1268 cpp_file_template = templatefile_prefix +
".template.cpp"
1269 implementationDictionary[
"HEADER_FILE_TEMPLATE" ] = os.path.basename(header_file_template)
1270 implementationDictionary[
"CPP_FILE_TEMPLATE" ] = os.path.basename(cpp_file_template)
1271 self.add_entries_to_text_replacement_dictionary(implementationDictionary)
1273 generated_abstract_header_file = (
1275 header_file_abstr_template,
1276 cpp_file_abstr_template,
1277 "Abstract" + self._name,
1280 abstractHeaderDictionary,
1285 generated_solver_files = (
1287 header_file_template,
1292 implementationDictionary,
1298 output.add(generated_abstract_header_file)
1299 output.add(generated_solver_files)
1300 output.makefile.add_h_file(subdirectory +
"Abstract" + self._name +
".h", generated=
True)
1301 output.makefile.add_h_file(subdirectory + self._name +
".h", generated=
True)
1302 output.makefile.add_cpp_file(subdirectory +
"Abstract" + self._name +
".cpp", generated=
True)
1303 output.makefile.add_cpp_file(subdirectory + self._name +
".cpp", generated=
True)
1305 if self._use_var_shortcut:
1307 os.path.dirname(os.path.realpath(__file__))
1309 +
"../VariableShortcuts.template.h",
1310 "VariableShortcuts",
1313 implementationDictionary,
1317 output.add(generated_shortcut_file)
1318 output.makefile.add_h_file(subdirectory +
"VariableShortcuts.h", generated=
True)
1331 This one is called by all algorithmic steps before I invoke
1332 add_entries_to_text_replacement_dictionary().
1334 See the remarks on set_postprocess_updated_patch_kernel to understand why
1335 we have to apply the (partially befilled) dictionary to create a new entry
1336 for this very dictionary.
1338 d[
"NUMBER_OF_VOLUMES_PER_AXIS"] = self.
_patch.dim[0]
1342 d[
"SOLVER_NAME"] = self.
_name
1344 d[
"NUMBER_OF_UNKNOWNS"] = self.
_unknowns
1346 d[
"SOLVER_NUMBER"] = 22
1348 d[
"ASSERTION_WITH_1_ARGUMENTS"] =
"nonCriticalAssertion1"
1349 d[
"ASSERTION_WITH_2_ARGUMENTS"] =
"nonCriticalAssertion2"
1350 d[
"ASSERTION_WITH_3_ARGUMENTS"] =
"nonCriticalAssertion3"
1351 d[
"ASSERTION_WITH_4_ARGUMENTS"] =
"nonCriticalAssertion4"
1352 d[
"ASSERTION_WITH_5_ARGUMENTS"] =
"nonCriticalAssertion5"
1353 d[
"ASSERTION_WITH_6_ARGUMENTS"] =
"nonCriticalAssertion6"
1356 raise Exception(
"min/max h are inconsistent")
1366 d[
"COMPUTE_KERNEL_CALL"] = jinja2.Template(
1369 d[
"COMPUTE_KERNEL_CALL_STATELESS"] = jinja2.Template(
1373 d[
"ABSTRACT_SOLVER_USER_DECLARATIONS"] = jinja2.Template(
1376 d[
"ABSTRACT_SOLVER_USER_DEFINITIONS"] = jinja2.Template(
1379 d[
"SOLVER_USER_DECLARATIONS"] = jinja2.Template(
1382 d[
"SOLVER_USER_DEFINITIONS"] = jinja2.Template(
1385 d[
"START_TIME_STEP_IMPLEMENTATION"] = jinja2.Template(
1388 d[
"FINISH_TIME_STEP_IMPLEMENTATION"] = jinja2.Template(
1391 d[
"CONSTRUCTOR_IMPLEMENTATION"] = jinja2.Template(
1395 d[
"PREPROCESS_RECONSTRUCTED_PATCH"] = jinja2.Template(
1398 d[
"POSTPROCESS_UPDATED_PATCH"] = jinja2.Template(
1402 d[
"COMPUTE_TIME_STEP_SIZE"] = jinja2.Template(
1405 d[
"COMPUTE_NEW_TIME_STEP_SIZE"] = jinja2.Template(
1447 @auxiliary_variables.setter
1459 @preprocess_reconstructed_patch.setter
1462 Set a new processing kernel
1464 Before the actual update, the numerical solver takes the actual solution
1465 including all auxiliary variables and copies it into another array which
1466 is calls reconstructedPatch (in FD4, this field is called oldQWithHalo).
1467 This array is bigger than the actual patch data, as it also holds the
1468 halo. One the solution is copied over, the kernel writes in the halo
1469 data using data from the faces. This reconstructed object is now passed
1470 into the compute kernel, which derives new values for all evolution
1471 variables. That is, the compute kernels' output does not hold the auxiliary
1472 variables anymore, as they are not subject to the PDE. The new variables
1473 are eventually written back into the real data, where they replace the
1474 old quantities (the auxiliary variables in there remain untouched).
1475 After the compute kernel has terminated, the reconstructed data including
1476 the halo is thrown away.
1478 ## Auxiliary variables
1480 If you alter the auxiliary variables (material parameters) in the patch
1481 preparation, this is absolutely fine, but the change will not be committed
1482 into the real data. It is only there temporarily while the actual patch
1483 will continue to hold the original material parameters.
1485 You can alter the actual patch data Q in the preprocessing. If this
1486 actual data are evolution (PDE) parameters, these changes however will
1487 be overwritten by the compute kernel. If you alter auxiliary variables
1488 within the preprocessing within the patch (Q), then this is fine, but the
1489 changes will not be visible to the reconstructed patch immediately. They
1490 will be visible there in the next time step. So you might be well-advised
1491 to alter the auxiliary variables in both the patch data and the
1496 We recommend that you use the AoS enumerator exahype2::enumerator::AoSLexicographicEnumerator.
1497 This one has to be given 1 as number of cells/patches, it requires the size
1498 of the patch, its halo size, you have to pass it the unknowns and the correct
1499 number of auxiliary parameters. If you want the preprocessing also to alter
1500 the actual input, you need a second enumerator. In this second one, all
1501 parameters are the same besides the halo size, which you have to set to 0:
1502 The input patch has no halo. This halo is only added temporarily in the
1503 reconstruction step.
1520 @postprocess_updated_patch.setter
1523 Define a postprocessing routine over the data
1525 The postprocessing kernel often looks similar to the following code:
1529 dfor( volume, {{NUMBER_OF_VOLUMES_PER_AXIS}} ) {
1530 enforceCCZ4constraints( newQ+index );
1531 index += {{NUMBER_OF_UNKNOWNS}} + {{NUMBER_OF_AUXILIARY_VARIABLES}};
1535 but often, it is nicer ot user one of the pre-defined kernels in ExaHyPE
1538 ::exahype2::fv::mapInnerNeighbourVoxelAlongBoundayOntoAuxiliaryVariable(
1540 {{NUMBER_OF_VOLUMES_PER_AXIS}},
1541 {{NUMBER_OF_UNKNOWNS}},
1542 {{NUMBER_OF_AUXILIARY_VARIABLES}}
1546 As highlighted from the examples above, you can use normal ExaHyPE constant
1547 within your injected code. Furthermore, you have the following two variables
1550 - newQ This is a pointer to the whole data structure (one large
1551 array). The patch is not supplemented by a halo layer.
1552 - marker An instance of the CellMarker which identifies the cell.
1554 In ExaHyPE's finite volume solvers, there are two different ways how to inject
1555 postprocessing: You can either define your own postprocessing step by setting
1556 _action_set_postprocess_solution, or you can use this setter to inject some
1557 source code. The two options are not equivalent: The snippet injected here is
1558 executed directly after the kernel update. Consequently, it also runs on a GPU
1559 if GPUs are employed, or it runs directly after a task has been launched to
1562 The postprocessing action set is launched when the cell update is committed,
1563 i.e., after the cell task has terminated or a task result came back from the
1564 GPU. Consequently, the snippet injected here might not have access to global
1565 variables (if it executes on a GPU) while the postprocessing action set
1566 always has access to the global program state.
1572 C++ code that holds the postprocessing kernel
1588 "Halo (overlap) size has to be bigger than zero but was {}".format(
1602 @interpolation.setter
1623 cell_data_storage: Storage,
1624 face_data_storage: Storage,
1627 By default, we hold all data on the heap using smart pointers. You can explicitly switch
1628 to storage on the call stack or heap using raw pointers.
1630 @see create_data_structures()
1632 assert isinstance(cell_data_storage, Storage)
1633 assert isinstance(face_data_storage, Storage)
Update the cell label within a sweep.
Abstract finite volume solver step sizes that works on patch-based AMR with a halo layer of one.
_solver_template_file_class_name
_action_set_initial_conditions_for_grid_construction
_unknown_identifier(self)
_store_face_data_default_guard(self)
Extend the guard via ands only.
user_solver_includes(self)
Add further includes to this property, if your solver requires some additional routines from other he...
get_finest_volume_size(self, domain_size)
create_data_structures(self)
Create data structures required by all Finite Volume solvers.
_action_set_couple_resolution_transitions_and_handle_dynamic_mesh_refinement
_abstract_solver_user_declarations
_postprocess_updated_patch
_init_dictionary_with_default_parameters(self, d)
This one is called by all algorithmic steps before I invoke add_entries_to_text_replacement_dictionar...
add_actions_to_plot_solution(self, step, output_path)
Add action sets to plot solution step.
_boundary_conditions_implementation
add_user_solver_includes(self, value)
Add further includes to this property, if your solver requires some additional routines from other he...
_solver_user_declarations
_baseline_action_set_descend_invocation_order
create_action_sets(self)
Create all the action sets.
_provide_cell_data_to_compute_kernels_default_guard(self)
Default logic when to create cell data or not.
get_min_number_of_spacetree_levels(self, domain_size)
get_max_number_of_spacetree_levels(self, domain_size)
_action_set_preprocess_solution
get_coarsest_number_of_finite_volumes(self, domain_size)
get_coarsest_number_of_patches(self, domain_size)
_action_set_AMR_commit_without_further_analysis
_action_set_handle_boundary
_action_set_initial_conditions
create_readme_descriptor(self, domain_offset, domain_size)
add_actions_to_perform_time_step(self, step)
Add action sets to time step.
postprocess_updated_patch(self)
add_actions_to_create_grid(self, step, evaluate_refinement_criterion)
The boundary information is set only once.
_user_action_set_includes
_source_term_implementation
_store_cell_data_default_guard(self)
Extend the guard via ands only.
add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory="", abstract_overwrite=True)
The ExaHyPE project will call this operation when it sets up the overall environment.
_compute_new_time_step_size
add_to_Peano4_datamodel(self, datamodel, verbose)
Add all required data to the Peano project's datamodel so it is properly built up.
_action_set_project_patch_onto_faces
__init__(self, name, patch_size, overlap, unknowns, auxiliary_variables, min_volume_h, max_volume_h, plot_grid_properties, bool pde_terms_without_state, kernel_namespace, baseline_action_set_descend_invocation_order=0)
Create solver.
get_name_of_global_instance(self)
_load_cell_data_default_guard(self)
Extend the guard via ands only.
_initial_conditions_implementation
get_finest_number_of_patches(self, domain_size)
user_action_set_includes(self)
Add further includes to this property, if your action sets require some additional routines from othe...
_action_set_update_face_label
_start_time_step_implementation
_eigenvalues_implementation
_get_default_includes(self)
set_solver_constants(self, datastring)
add_solver_constants(self, datastring)
add_entries_to_text_replacement_dictionary(self, d)
_compute_kernel_call_stateless
get_finest_number_of_finite_volumes(self, domain_size)
add_actions_to_init_grid(self, step)
Add all the action sets to init grid.
_load_face_data_default_guard(self)
Extend the guard via ands only.
_action_set_postprocess_solution
_action_set_AMR_throughout_grid_construction
_constructor_implementation
_reconstructed_array_memory_location
_refinement_criterion_implementation
_finish_time_step_implementation
_abstract_solver_user_definitions
auxiliary_variables(self)
get_coarsest_volume_size(self, domain_size)
add_user_action_set_includes(self, value)
Add further includes to this property, if your action sets require some additional routines from othe...
_action_set_copy_new_faces_onto_old_faces
preprocess_reconstructed_patch(self)
add_use_data_statements_to_Peano4_solver_step(self, step)
Tell Peano what data to move around.
_preprocess_reconstructed_patch
_action_set_update_cell_label
_provide_face_data_to_compute_kernels_default_guard(self)
switch_storage_scheme(self, Storage cell_data_storage, Storage face_data_storage)
By default, we hold all data on the heap using smart pointers.
_action_set_roll_over_update_of_faces
The action set to realise AMR.
The ExaHyPE 2 Finite Volume handling of (dynamically) adaptive meshes.
The global periodic boundary conditions are set in the Constants.h.
PostprocessSolution differs from other action sets, as I only create it once.
PreprocessSolution differs from other action sets, as I only create it once.
Project patch data onto faces so we can reconstruct the halo layers later on.
This action set takes the updated data per face and writes it into new.
Realise patch via smart pointers.
Realise patch via smart pointers.
Very simple converter which maps the patch 1:1 onto a double array.