94def create_runner(peano_project, identifier, steps):
97 Create the header/implementation file pair that represents one of a
98 project's algorithmic phases represented by a sequence of steps.
99 It yields one class of the classname identifier which is held in
100 the project's runners subdirectory/namespace. This class holds
101 exactly one method run() which toggles through the steps one by one.
103 Even though the runner seems to accept only a sequence of steps
104 (which are members of @ref peano4.api.solversteps), the runner
105 belongs into the task graph repository, as it assumes that each
106 step has a field has_to_repeat. This field is not defined within
107 @ref peano4.api.solversteps.Step. It is something graph compilers
108 add to the data model. Consult the compiler description in
109 @ref peano4.api.taskgraph.compiler.
111 peano_project: peano4.api.Project
112 The Peano 4 project to add the generated files to.
115 Same identifier used for the phase's Observers, e.g.
116 "SetupSimulation", "TimeStep", "Plot".
118 steps: [peano4.api.solversteps.Step]
119 The sequence of Observers realising this algorithmic phase, in the
120 order compiled from the phase's task graph (@see
121 peano4.api.taskgraph.compiler.Sequential.translate). Its first
122 element's name is the repositories::StepRepository::Steps enumerator
123 the generated runner hands to peano4::RunMultipleSteps's constructor
124 to kick the phase off. run() instantiates each step's Observer in
125 turn and traverses the spacetree set with it, switching the log
126 filter's program phase beforehand, bracketing the traversal itself
127 with that Observer's own static prepareTraversal()/
128 unprepareTraversal() (@see peano4.api.output.Observer - the spacetree
129 set never calls these itself, so the runner is exactly the "up to
130 the user" call site its own doc comment asks for), and timing the
131 traversal via the inherited _watch/_runtimeMeasurement. "Undef" (and
132 no traversals at all) if the phase has no steps, e.g. no solver
133 contributed to it, so the generated code still compiles.
135 Before every step but the first, run() also calls
136 peano4::parallel::Node::getInstance().setNextProgramStep(...) with
137 that step's own program step - whoever calls run() in the first
138 place already had to set Node's program step to this phase's first
139 step beforehand (@see getFirstSubstep(), used exactly that way in
140 exahype2.api.generators.DefaultSimulationMain), so run() only
141 asserts on it there rather than setting it again; every subsequent
142 step within the same phase has nothing else to set it, so run()
143 must do so itself. This is what makes
144 peano4::parallel::Node::getInstance().getCurrentProgramStep()
145 reliable for every step, not just a phase's first - in particular,
146 it is what the guard expressions
147 peano4.api.taskgraph.compiler.DeriveLoadStoreComputeFlag builds
148 actually depend on at runtime.
151 step_names = [step.name
for step
in steps]
152 observer_names = [step.get_observer_class_name()
for step
in steps]
153 first_step = step_names[0]
if step_names
else "Undef"
154 repeat_conditions = [step.has_to_repeat
for step
in steps]
157 headerfile_template = TEMPLATE_RUNNER_HEADER_FILE,
158 cppfile_template = TEMPLATE_RUNNER_CPP_FILE,
159 classname = identifier,
160 namespace = peano_project.namespace + [
"runners"],
161 subdirectory = peano_project.subdirectory +
"runners",
163 "PROJECT_NAMESPACE": peano_project.namespace,
164 "SUBNAMESPACE": peano_project._subdirectory +
"::" if peano_project._subdirectory
else "",
165 "SUBDIRECTORY": peano_project.subdirectory,
167 "OBSERVERS": observer_names,
168 "FIRST_STEP": first_step,
169 "REPEAT_CONDITIONS": repeat_conditions,
171 default_overwrite =
True,
172 apply_iteratively =
False,
174 peano_project.output.add(generated_files)
175 peano_project.output.makefile.add_cpp_file(
176 peano_project.subdirectory +
"runners/" + identifier +
".cpp", generated=
True