Peano
Loading...
Searching...
No Matches
SeparateSweepsWithEnclaveTasking.py
Go to the documentation of this file.
1# This file is part of the ExaHyPE2 project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3from .SeparateSweeps import SeparateSweeps
4from exahype2.solvers.PDETerms import PDETerms
5from exahype2.solvers.rkfd.actionsets.AbstractRKFDActionSet import AbstractRKFDActionSet
6from exahype2.solvers.LagrangeBasis import render_tensor_1
7from exahype2.solvers.LagrangeBasis import render_tensor_2
8
9import peano4
10import exahype2
11import jinja2
12
13
14import os
15
17 ReconstructPatchAndApplyFunctor,
18)
19
20from exahype2.solvers.ButcherTableau import ButcherTableau
21from exahype2.solvers.Storage import Storage
22
23
25 """!
26
27 Update one cell that is compute Runge-Kutta step on it
28
29 This routine is significantly simpler than its counterpart in SeparateSweeps,
30 as we basically check if a cell is an enclave cell or not. If it is one, we
31 spawn a task. If not, we call the static routine from the task class which
32 updates the patch. All the computations thus are removed.
33
34 """
35
36 SolveRiemannProblemsOverPatch = jinja2.Template(
37 """
38 double timeStamp = fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp();
39
40 // Set the variable
41 // double timeStepSize
42 {{COMPUTE_TIME_STEP_SIZE}}
43
44 if ({{PREDICATES[0]}}) {
45 if (marker.willBeEnclaveCell() and {{DEVICE_RESIDENT_RK}}) {
46 // The device memory for each cell is maintained across Runge-Kutta substeps to reduce the number of allocations and deallocations.
47 // The deallocation takes place in ComputeFinalLinearCombination, which is the last step of a Runge-Kutta time step
48 ::exahype2::enumerator::AoSLexicographicEnumerator enumeratorWithAuxiliaryVariablesOnReconstructedPatch( 1, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, {{HALO_SIZE}}, {{NUMBER_OF_UNKNOWNS}}, {{NUMBER_OF_AUXILIARY_VARIABLES}});
49 int device = 0;
50 fineGridCell{{SOLVER_NAME}}CellLabel.setDeviceNumber(device);
51 fineGridCell{{SOLVER_NAME}}CellLabel.setDeviceMemoryAddress(tarch::accelerator::GPUMemoryManager::getInstance().allocate<double>(enumeratorWithAuxiliaryVariablesOnReconstructedPatch.size(), device));
52 fineGridCell{{SOLVER_NAME}}CellLabel.setDeviceMemoryStatus(celldata::{{SOLVER_NAME}}CellLabel::DeviceMemoryStatus::OutOfDate);
53 fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.reallocateOnDevice(device);
54
55 omp_target_memcpy(fineGridCell{{SOLVER_NAME}}CellLabel.getDeviceMemoryAddress(), oldQWithHalo, enumeratorWithAuxiliaryVariablesOnReconstructedPatch.size() * sizeof(double), 0, 0, device, omp_get_initial_device());
56 }
57 }
58 #endif
59
60 {% for PREDICATE_NO in range(0,PREDICATES|length) %}
61 if ({{PREDICATES[PREDICATE_NO]}}) {
62 ::exahype2::enumerator::AoSLexicographicEnumerator enumeratorWithAuxiliaryVariablesOnReconstructedPatch( 1, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, {{HALO_SIZE}}, {{NUMBER_OF_UNKNOWNS}}, {{NUMBER_OF_AUXILIARY_VARIABLES}});
63 ::exahype2::enumerator::AoSLexicographicEnumerator enumeratorWithoutAuxiliaryVariables( {{RK_STEPS}}, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, 0, {{NUMBER_OF_UNKNOWNS}}, 0 );
64
65 if (marker.willBeEnclaveCell() and {{DEVICE_RESIDENT_RK}}) {
66 ::exahype2::fd::fd4::omp::computeLinearCombinationStateless<{{SOLVER_NAME}}, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, {{HALO_SIZE}}, {{NUMBER_OF_UNKNOWNS}}, {{NUMBER_OF_AUXILIARY_VARIABLES}}, {{RK_ORDER}}, {{PREDICATE_NO}}>(fineGridCell{{SOLVER_NAME}}CellLabel.getDeviceNumber(), fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value, fineGridCell{{SOLVER_NAME}}CellLabel.getDeviceMemoryAddress(), timeStepSize);
67 } else{
68 dfor( dof, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}} ) {
69 for (int unknown=0; unknown<{{NUMBER_OF_UNKNOWNS}}; unknown++) {
70 {% for WEIGHT_NO in range(0,BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO]|length) %}
71 {% if BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO][WEIGHT_NO]!=0 %}
72 oldQWithHalo[ enumeratorWithAuxiliaryVariablesOnReconstructedPatch(0,dof,unknown) ] +=
73 timeStepSize * {{BUTCHER_TABLEAU_WEIGHTS[PREDICATE_NO][WEIGHT_NO]}} *
74 fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value[ enumeratorWithoutAuxiliaryVariables({{WEIGHT_NO}},dof,unknown) ];
75 {% endif %}
76 {% endfor %}
77 }
78 }
79 }
80
81 newQ = fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value + enumeratorWithoutAuxiliaryVariables({{PREDICATE_NO}},0,0);
82 }
83 {% endfor %}
84
85 {{PREPROCESS_RECONSTRUCTED_PATCH}}
86
87 assertion2( tarch::la::greaterEquals( timeStamp, 0.0 ), timeStamp, timeStepSize );
88 assertion2( tarch::la::greaterEquals( timeStepSize, 0.0 ), timeStamp, timeStepSize );
89
90 ::exahype2::fd::validatePatch(
91 oldQWithHalo,
92 {{NUMBER_OF_UNKNOWNS}},
93 {{NUMBER_OF_AUXILIARY_VARIABLES}},
94 {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}},
95 {{HALO_SIZE}}, // halo
96 std::string(__FILE__) + "(" + std::to_string(__LINE__) + "): " + marker.toString()
97 ); // previous time step has to be valid
98
99 double subTimeStamp=timeStamp;
100 {% for PREDICATE_NO in range(0,PREDICATES|length-1) %}
101 if ({{PREDICATES[PREDICATE_NO]}}) {
102 subTimeStamp += {{BUTCHER_TABLEAU_RELATIVE_TIME_STEP_SIZES[PREDICATE_NO]}}*timeStepSize;
103 }
104 {% endfor %}
105
106 if ( marker.willBeSkeletonCell() ) {
107 tasks::{{SOLVER_NAME}}EnclaveTask::applyKernelToCell(
108 marker,
109 subTimeStamp,
110 timeStepSize,
111 oldQWithHalo,
112 newQ
113 );
114
115 fineGridCell{{SEMAPHORE_LABEL}}.setSemaphoreNumber( ::exahype2::EnclaveBookkeeping::SkeletonTask );
116 }
117 else {
118 assertion( marker.willBeEnclaveCell() );
119 assertion( not marker.willBeRefined() );
120 ::exahype2::enumerator::AoSLexicographicEnumerator enumeratorWithAuxiliaryVariablesOnReconstructedPatch( 1, {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}}, {{HALO_SIZE}}, {{NUMBER_OF_UNKNOWNS}}, {{NUMBER_OF_AUXILIARY_VARIABLES}});
121
122 tasks::{{SOLVER_NAME}}EnclaveTask* newEnclaveTask;
123 if ({{DEVICE_RESIDENT_RK}}){
124 // The reconstructed patch is copied into the allocated device memory for the cell
125 const int device = fineGridCell{{SOLVER_NAME}}CellLabel.getDeviceNumber();
126 fineGridCell{{SOLVER_NAME}}CellLabel.setDeviceMemoryStatus(celldata::{{SOLVER_NAME}}CellLabel::DeviceMemoryStatus::UpToDate);
127
128 newEnclaveTask = new tasks::{{SOLVER_NAME}}EnclaveTask(
129 marker,
130 subTimeStamp,
131 timeStepSize,
132 fineGridCell{{SOLVER_NAME}}CellLabel.getDeviceMemoryAddress(),
133 {% if MAKE_COPY_OF_ENCLAVE_TASK_DATA %}
134 nullptr
135 {% else %}
136 newQ
137 {% endif %}
138 , device
139 );
140 } else {
141 newEnclaveTask = new tasks::{{SOLVER_NAME}}EnclaveTask(
142 marker,
143 subTimeStamp,
144 timeStepSize,
145 oldQWithHalo,
146 {% if MAKE_COPY_OF_ENCLAVE_TASK_DATA %}
147 nullptr
148 {% else %}
149 newQ
150 {% endif %}
151 );
152 }
153
154 int predecessorEnclaveTaskNumber = fineGridCell{{SEMAPHORE_LABEL}}.getSemaphoreNumber();
155
156 fineGridCell{{SEMAPHORE_LABEL}}.setSemaphoreNumber( newEnclaveTask->getTaskId() );
157
158 tarch::multicore::spawnTask(
159 newEnclaveTask,
160 predecessorEnclaveTaskNumber>=0 ? std::set<int>{predecessorEnclaveTaskNumber} : tarch::multicore::NoInDependencies,
161 newEnclaveTask->getTaskId()
162 );
163
164 if (predecessorEnclaveTaskNumber>=0) {
165 ::exahype2::EnclaveTask::releaseTaskNumber(predecessorEnclaveTaskNumber);
166 }
167
168 // Time stamp is not updated, as this will be done by final linear combination
169 // fineGridCell{{SOLVER_NAME}}CellLabel.setTimeStamp(timeStamp + timeStepSize);
170
171 }
172 """
173 )
174
175 def __init__(self, solver):
176 """ """
177 one_huge_boolean_guard_expression = "false"
178 for expr in solver._primary_sweeps_of_Runge_Kutta_step_on_cell:
179 one_huge_boolean_guard_expression += " or (" + expr + ")"
180
181 super(UpdateCell, self).__init__(
182 patch=solver._patch,
183 patch_overlap=solver._patch_overlap_new,
184 functor_implementation="""
185#error please switch to your Riemann solver of choice
186""",
187 reconstructed_array_memory_location=peano4.toolbox.blockstructured.ReconstructedArrayMemoryLocation.ManagedSharedAcceleratorDeviceMemoryThroughTarchWithoutDelete,
188 guard=one_huge_boolean_guard_expression,
189 add_assertions_to_halo_exchange=False,
190 )
191 self._solver = solver
192
194 """
195 fineGridCell"""
196 + solver._name
197 + """CellLabel.setHasUpdated(false);
198"""
200 )
201
203 self._device_resident_rk = solver._device_resident_rk
204
206 """!
207
208 This is our plug-in point to alter the underlying dictionary
209
210 """
211 super(UpdateCell, self)._add_action_set_entries_to_dictionary(d)
212
213 self._solver._init_dictionary_with_default_parameters(d)
214 self._solver.add_entries_to_text_replacement_dictionary(d)
215
216 d["PREDICATES"] = self._solver._primary_sweeps_of_Runge_Kutta_step_on_cell
217 d["BUTCHER_TABLEAU_WEIGHTS"] = self._butcher_tableau.weight_matrix()
218 d[
219 "BUTCHER_TABLEAU_RELATIVE_TIME_STEP_SIZES"
220 ] = self._butcher_tableau.time_step_sizes()
221 d["DEVICE_RESIDENT_RK"] = str(self._device_resident_rk).lower()
222
223 # Has to come after we've set the predicates, as we use these
224 # fields in here already
225 d["CELL_FUNCTOR_IMPLEMENTATION"] = self.SolveRiemannProblemsOverPatch.render(
226 **d
227 )
228
229 def get_includes(self):
230 return (
231 """
232#include "tarch/NonCriticalAssertions.h"
233#include "exahype2/enumerator/enumerator.h"
234#include "exahype2/fd/PatchUtils.h"
235#include "exahype2/EnclaveBookkeeping.h"
236#include "tarch/multicore/Task.h"
237"""
238 + self._solver._get_default_includes()
239 + self._solver.user_action_set_includes
240 + """
241#include "tasks/{}.h"
242""".format(
243 self._solver._enclave_task_name()
244 )
245 )
246
248 return __name__.replace(".py", "").replace(".", "_") + "_UpdateCell"
249
250
252 Template = """
253 {% for PREDICATE_NO in range(0,PREDICATES|length) %}
254 if (
255 not marker.hasBeenRefined()
256 and
257 marker.hasBeenEnclaveCell()
258 and
259 {{PREDICATES[PREDICATE_NO]}}
260 ) {
261 const int taskNumber = fineGridCell{{LABEL_NAME}}.getSemaphoreNumber();
262 if ( taskNumber>=0 ) {
263 double maxEigenvalue; // not used here
264 if ({{DEVICE_RESIDENT_RK}}){
265 ::exahype2::EnclaveBookkeeping::getInstance().waitForTaskToTerminateAndWriteResultDirectly( taskNumber, marker.x(), marker.h() );
266 } else {
267 #if Dimensions==2
268 constexpr int NumberOfDoFsPerCell = {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}} * {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}};
269 double* QOut = fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value + {{PREDICATE_NO}} * NumberOfDoFsPerCell * {{NUMBER_OF_UNKNOWNS}};
270 #elif Dimensions==3
271 constexpr int NumberOfDoFsPerCell = {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}} * {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}} * {{NUMBER_OF_GRID_CELLS_PER_PATCH_PER_AXIS}};
272 double* QOut = fineGridCell{{UNKNOWN_IDENTIFIER}}RhsEstimates.value + {{PREDICATE_NO}} * NumberOfDoFsPerCell * {{NUMBER_OF_UNKNOWNS}};
273 #endif
274 ::exahype2::EnclaveBookkeeping::getInstance().waitForTaskToTerminateAndCopyResultOver( taskNumber, QOut, maxEigenvalue, marker.x(), marker.h() );
275 }
276 fineGridCell{{LABEL_NAME}}.setSemaphoreNumber( ::exahype2::EnclaveBookkeeping::NoEnclaveTaskNumber );
277 }
278 }
279 {% endfor %}
280"""
281
282 def __init__(self, solver):
283 super(MergeEnclaveTaskOutcome, self).__init__(solver)
285 self.label_name = exahype2.grid.UpdateCellLabel.get_attribute_name(solver._name)
286 self._device_resident_rk = solver._device_resident_rk
287
288 def get_body_of_operation(self, operation_name):
289 result = ""
290 if (
291 operation_name
292 == peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME
293 ):
294 d = {}
295 self._solver._init_dictionary_with_default_parameters(d)
296 self._solver.add_entries_to_text_replacement_dictionary(d)
297 d["LABEL_NAME"] = self.label_name
298 d["PREDICATES"] = self._solver._secondary_sweeps_of_Runge_Kutta_step_on_cell
299 d["DEVICE_RESIDENT_RK"] = str(self._device_resident_rk).lower()
300 result = jinja2.Template(self.Template).render(**d)
301 pass
302 return result
303
305 return (
306 __name__.replace(".py", "").replace(".", "_") + "_MergeEnclaveTaskOutcome"
307 )
308
309 def get_includes(self):
310 return (
311 super(MergeEnclaveTaskOutcome, self).get_includes()
312 + """
313#include "exahype2/EnclaveBookkeeping.h"
314"""
315 )
316
317
319 """!
320
321 Enclave variant of the solver where we still run through mesh once per Runge-Kutta sweep
322
323 The concept of (enclave) tasking within ExaHyPE solvers is described in
324 detail in the @ref page_exahype_solvers_enclave_solvers "generic enclave discussion of ExaHyPE".
325 This class is a prototype realisation of this concept which other solvers
326 then specialise for particular numerical schemes.
327
328 The class basically replaces the standard "update a cell" action set with an
329 action set that might or might not spawn a task. In return, it adds a further
330 action set which merges the arising task outcomes into the actual mesh
331 structure. By default, we use peano4::datamanagement::CellMarker::willBeEnclaveCell()
332 and peano4::datamanagement::CellMarker::hasBeenEnclaveCell() to guide the
333 decision whether to spawn a task or not. You can overwrite this decision
334 by redefining the corresponding entry in the dictionary befilled by
335 add_entries_to_text_replacement_dictionary().
336
337 ## Task priorities
338
339 Use the attributes self.enclave_task_priority to change the priority of the
340 task. This value can either be a string that C++ can evaluate into a
341 priority or a plain numerical value. I set it to
342
343 self.enclave_task_priority = "tarch::multicore::Task::DefaultPriority-1"
344
345 by default.
346
347
348 """
349
351 self,
352 name,
353 patch_size,
354 overlap,
355 rk_order,
356 unknowns,
357 auxiliary_variables,
358 min_meshcell_h,
359 max_meshcell_h,
360 plot_grid_properties,
361 kernel_namespace,
362 pde_terms_without_state,
363 device_resident_rk = False
364 ):
365 """ """
366 self._name_name_name = name
368 self._device_resident_rk = device_resident_rk
369
371 "{} and repositories::{}.getSolverState()=={}::SolverState::RungeKuttaPrimarySubStep{}".format(
374 self._name_name_name,
375 step,
376 )
377 for step in range(0, self.number_of_Runge_Kutta_steps())
378 ] + [
379 "{} and repositories::{}.getSolverState()=={}::SolverState::RungeKuttaPrimarySubStep0AfterGridInitialisation".format(
382 self._name_name_name,
383 )
384 ]
386 "{} and repositories::{}.getSolverState()=={}::SolverState::RungeKuttaSecondarySubStep{}".format(
389 self._name_name_name,
390 step,
391 )
392 for step in range(0, self.number_of_Runge_Kutta_steps())
393 ]
394 self._last_secondary_sweep_of_Runge_Kutta_step_on_cell = "repositories::{}.getSolverState()=={}::SolverState::RungeKuttaSecondarySubStep{}".format(
396 self._name_name_name,
398 )
399
401 "{} and repositories::{}.getSolverState()=={}::SolverState::RungeKuttaPrimarySubStep{}".format(
404 self._name_name_name,
405 step,
406 )
407 for step in range(0, self.number_of_Runge_Kutta_steps())
408 ] + [
409 "{} and repositories::{}.getSolverState()=={}::SolverState::RungeKuttaPrimarySubStep0AfterGridInitialisation".format(
412 self._name_name_name,
413 )
414 ]
416 "{} and repositories::{}.getSolverState()=={}::SolverState::RungeKuttaSecondarySubStep{}".format(
419 self._name_name_name,
420 step,
421 )
422 for step in range(0, self.number_of_Runge_Kutta_steps())
423 ]
424
426 "("
427 + "repositories::"
429 + ".getSolverState()=="
430 + self._name_name_name
431 + "::SolverState::RungeKuttaPrimarySubStep0AfterGridInitialisation "
432 )
433 for step in range(0, self.number_of_Runge_Kutta_steps()):
434 self._primary_sweep_guard += " or repositories::{}.getSolverState()=={}::SolverState::RungeKuttaPrimarySubStep{}".format(
436 )
437 self._primary_sweep_guard += ")"
438
440 repositories::{}.getSolverState()=={}::SolverState::RungeKuttaPrimarySubStep0AfterGridInitialisation
441 or repositories::{}.getSolverState()=={}::SolverState::PlottingAfterGridInitialisation
442 or repositories::{}.getSolverState()=={}::SolverState::Plotting
443 or repositories::{}.getSolverState()=={}::SolverState::Suspended
444 )""".format(
449 )
450
451
452 for step in range(0, self.number_of_Runge_Kutta_steps()):
453 self._primary_sweep_or_plot_guard += " or repositories::{}.getSolverState()=={}::SolverState::RungeKuttaPrimarySubStep{}".format(
455 )
457
458 self._secondary_sweep_guard = "( false"
459 for step in range(0, self.number_of_Runge_Kutta_steps()):
460 self._secondary_sweep_guard += " or repositories::{}.getSolverState()=={}::SolverState::RungeKuttaSecondarySubStep{}".format(
462 )
463 self._secondary_sweep_guard += ")"
464
466 repositories::{}.getSolverState()=={}::SolverState::GridInitialisation""".format(
468 )
469 for step in range(0, self.number_of_Runge_Kutta_steps()):
470 self._secondary_sweep_or_initialisation_guard += " or repositories::{}.getSolverState()=={}::SolverState::RungeKuttaSecondarySubStep{}".format(
472 )
474
475 super(SeparateSweepsWithEnclaveTasking, self).__init__(
476 name,
477 patch_size,
478 overlap,
479 rk_order,
480 unknowns,
481 auxiliary_variables,
482 min_meshcell_h,
483 max_meshcell_h,
484 plot_grid_properties,
485 kernel_namespace,
486 )
487
489
491 "#error Not yet defined. Set in your Python solver class."
492 )
494 "#error Not yet defined. Set in your Python solver class."
495 )
496 self._pde_terms_without_state = pde_terms_without_state
497
498 self._fused_volumetric_kernel_call_cpu = "#error Not yet defined. Set self._fused_volumetric_kernel_call_cpu in your Python solver class."
499 self._fused_volumetric_kernel_call_gpu = "#error Not yet defined. Set self._fused_volumetric_kernel_call_gpu in your Python solver class."
500
501 self.enclave_task_priority = "tarch::multicore::Task::DefaultPriority-1"
502
505
506 self.add_solver_constants("static constexpr double RKWeights[] = {};\n\n".format(
507 render_tensor_1(tensor=ButcherTableau(self._rk_order_rk_order_rk_order).final_estimate_weights())
508 ))
509 tensor = render_tensor_2(tensor=ButcherTableau(self._rk_order_rk_order_rk_order).weight_matrix(), use_multidimensional_arrays=True)
510 # Get around Jinja2 errors when "{{" and "}}" appear in the output
511 tensor = tensor.replace("{{", "{ {")
512 tensor = tensor.replace("}}", "} }")
513 self.add_solver_constants("static constexpr double RKWeightMatrix[{}][{}] = {};\n\n".format(
516 tensor
517 ))
518
520 """
521
522 Call the superclass' create_data_structures() to ensure that all the data
523 structures are in place, i.e. each cell can host a patch, that each face hosts
524 patch overlaps, and so forth. These quantities are all set to defaults. See
525 FV.create_data_structures().
526
527 After that, take the patch overlap (that's the data stored within the faces)
528 and ensure that these are sent and received via MPI whenever they are also
529 stored persistently. The default in FV is that no domain boundary data exchange
530 is active. Finally, ensure that the old data is only exchanged between the
531 initialisation sweep and the first first grid run-through.
532
533 """
534 super(SeparateSweepsWithEnclaveTasking, self).create_data_structures()
535
536 initialisation_sweep_guard = (
537 "("
538 + "repositories::"
540 + ".getSolverState()=="
541 + self._name_name_name
542 + "::SolverState::GridInitialisation"
543 + ")"
544 )
545 first_iteration_after_initialisation_guard = (
546 "("
547 + "repositories::"
549 + ".getSolverState()=="
550 + self._name_name_name
551 + "::SolverState::RungeKuttaPrimarySubStep0AfterGridInitialisation or "
552 + "repositories::"
554 + ".getSolverState()=="
555 + self._name_name_name
556 + "::SolverState::PlottingAfterGridInitialisation"
557 + ")"
558 )
559
560 self._patch_overlap_old.generator.send_condition = initialisation_sweep_guard
561 self._patch_overlap_old.generator.receive_and_merge_condition = (
562 first_iteration_after_initialisation_guard
563 )
564
565 secondary_sweep_or_initialisation_or_plotting_guard = """(
566 repositories::{}.getSolverState()=={}::SolverState::GridInitialisation or
567 repositories::{}.getSolverState()=={}::SolverState::PlottingAfterGridInitialisation or
568 repositories::{}.getSolverState()=={}::SolverState::Plotting or
569 repositories::{}.getSolverState()=={}::SolverState::Suspended or
570 repositories::{}.isLastGridSweepOfTimeStep()
571 )""".format(
577 )
578
579 primary_sweep_or_plotting = """(
580 repositories::{}.getSolverState()=={}::SolverState::PlottingAfterGridInitialisation or
581 repositories::{}.getSolverState()=={}::SolverState::Plotting or
582 repositories::{}.getSolverState()=={}::SolverState::Suspended or
583 repositories::{}.isFirstGridSweepOfTimeStep()
584 )""".format(
589 )
590
591 self._patch_overlap_new.generator.send_condition = (
592 secondary_sweep_or_initialisation_or_plotting_guard
593 )
594 self._patch_overlap_new.generator.receive_and_merge_condition = (
595 primary_sweep_or_plotting
596 )
597
598 first_sweep_of_time_step_or_plotting_guard = """(
599 repositories::{}.isFirstGridSweepOfTimeStep() or
600 repositories::{}.getSolverState()=={}::SolverState::PlottingAfterGridInitialisation or
601 repositories::{}.getSolverState()=={}::SolverState::Plotting or
602 repositories::{}.getSolverState()=={}::SolverState::Suspended
603 )""".format(
608 )
609
610 last_sweep_of_time_step_or_plotting_or_initialisation = """(
611 repositories::{}.getSolverState()=={}::SolverState::GridInitialisation or
612 repositories::{}.getSolverState()=={}::SolverState::PlottingAfterGridInitialisation or
613 repositories::{}.getSolverState()=={}::SolverState::Plotting or
614 repositories::{}.getSolverState()=={}::SolverState::Suspended or
615 repositories::{}.isLastGridSweepOfTimeStep()
616 )""".format(
622 )
623
624 self._patch_estimates.generator.load_store_compute_flag = "::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
627 + """
628 and not ("""
629 + first_sweep_of_time_step_or_plotting_guard
630 + ")",
632 + """
633 and not ("""
634 + last_sweep_of_time_step_or_plotting_or_initialisation
635 + ")",
636 )
637
639 """
640
641 Call superclass routine and then reconfigure the update cell call.
642 Only the UpdateCell action set is specific to a single sweep.
643
644 This operation is implicity called via the superconstructor.
645
646 ## Guard construction
647
648 We note that the guard sets all contain the storage predicate already,
649 i.e. they combine the logic state analysis with an evaluation of
650 _load_cell_data_default_guard() and _store_cell_data_default_guard().
651 The singular strings like _primary_sweep_guard do not have this check
652 built in. We have to add it here.
653
654 """
655 super(SeparateSweepsWithEnclaveTasking, self).create_action_sets()
656
659
661 self._action_set_merge_enclave_task_outcome.descend_invocation_order = (
663 )
664 #
665 # have a single guard (technically)
666 #
669 + " and ("
671 + ")"
672 )
673 self._action_set_handle_boundary.guard = (
675 + " and ("
677 + ")"
678 )
681 + " and ("
683 + ")"
684 )
685
686 #
687 # the following mappings have guards, i.e. a whole set of guards
688 #
690 "{} and repositories::{}.getSolverState()=={}::SolverState::GridInitialisation".format(
693 self._name_name_name,
694 )
695 ]
696
697 #
698 # this one is fine, as it only is used in the initialisation
699 #
700 # self._action_set_copy_new_faces_onto_old_faces.guard = self._secondary_sweeps_of_Runge_Kutta_step_on_face
701
702 # last_sweep_of_time_step_or_plotting_or_initialisation
705 )
706
707 def add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory=""):
708 """
709
710 Add the enclave task for the GPU
711
712 See superclass for further information.
713
714 """
715 super(
716 SeparateSweepsWithEnclaveTasking, self
717 ).add_implementation_files_to_project(namespace, output, dimensions, subdirectory)
718 templatefile_prefix = os.path.join(
719 os.path.dirname(os.path.realpath(__file__)),
720 "SeparateSweeps.EnclaveTask.template",
721 )
722
723 if(subdirectory):
724 subdirectory += "/"
725
726 implementationDictionary = {}
727 self._init_dictionary_with_default_parameters(implementationDictionary)
729
730 # Some includes might logically belong into the action sets, but now they are
731 # 'outsourced' into the enclave task. So we manually add it here.
732 implementationDictionary["SOLVER_INCLUDES"] += self.user_solver_includes
733 implementationDictionary["SOLVER_INCLUDES"] += self.user_action_set_includesuser_action_set_includesuser_action_set_includes
734
735 task_name = self._enclave_task_name()
736 generated_solver_files = (
738 "{}.h".format(templatefile_prefix),
739 "{}.cpp".format(templatefile_prefix),
740 task_name,
741 namespace + ["tasks"],
742 subdirectory + "tasks",
743 implementationDictionary,
744 True,
745 )
746 )
747
748 output.add(generated_solver_files)
749 output.makefile.add_cpp_file(subdirectory + "tasks/" + task_name + ".cpp", generated=True)
750
751
753 """!
754
755 Add enclave aspect
756
757 Add enclave aspect to time stepping. If you study the superclass'
758 routine add_actions_to_perform_time_step() and consider that this action
759 set is invoked in the secondary grid sweep, then it becomes clear that
760 this merger has to come first, i.e. we first add the action set and then
761 we call the superclass' add_action_set().
762
763 We need the result of the volumetric operation before we sum up this
764 volumetric solution and the Riemann solution.
765
766 """
767 super(SeparateSweepsWithEnclaveTasking, self).add_actions_to_perform_time_step(
768 step
769 )
770 step.add_action_set(self._action_set_merge_enclave_task_outcome)
771
773 super(
774 SeparateSweepsWithEnclaveTasking, self
776
777 d["FUSED_COMPUTE_KERNEL_CALL_CPU"] = jinja2.Template(
778 self._fused_compute_kernel_call_cpu, undefined=jinja2.DebugUndefined
779 ).render(**d)
780 d["FUSED_COMPUTE_KERNEL_CALL_GPU"] = jinja2.Template(
781 self._fused_compute_kernel_call_gpu, undefined=jinja2.DebugUndefined
782 ).render(**d)
783
784 d["SEMAPHORE_LABEL"] = exahype2.grid.UpdateCellLabel.get_attribute_name(
785 self._name_name_name
786 )
787 d["STATELESS_PDE_TERMS"] = self._pde_terms_without_state
788 d["ENCLAVE_TASK_PRIORITY"] = self.enclave_task_priority
789 d["MAKE_COPY_OF_ENCLAVE_TASK_DATA"] = self.make_copy_of_enclave_task_data
790
791
792 @property
794 return (
795 """
796#include "exahype2/CellData.h"
797"""
798 + super(SeparateSweeps, self).user_action_set_includes
799 )
800
802 return "{}EnclaveTask".format(self._name_name_name)
803
805 self,
806 cell_data_storage: Storage,
807 face_data_storage: Storage,
808 ):
809 if cell_data_storage == Storage.SmartPointers:
811 else:
813
814 super(SeparateSweepsWithEnclaveTasking, self).switch_storage_scheme(
815 cell_data_storage,
816 face_data_storage
817 )
user_action_set_includes(self)
Add further includes to this property, if your action sets require some additional routines from othe...
number_of_Runge_Kutta_steps(self)
Return number of steps required to realise the Runge-Kutta scheme.
user_solver_includes(self)
Add further includes to this property, if your solver requires some additional routines from other he...
_init_dictionary_with_default_parameters(self, d)
This one is called by all algorithmic steps before I invoke add_entries_to_text_replacement_dictionar...
create_data_structures(self)
Recall in subclasses if you wanna change the number of unknowns or auxiliary variables.
get_body_of_operation(self, operation_name)
Return actual C++ code snippets to be inserted into C++ code.
get_action_set_name(self)
You should replicate this function in each subclass, so you get meaningful action set names (otherwis...
Enclave variant of the solver where we still run through mesh once per Runge-Kutta sweep.
switch_storage_scheme(self, Storage cell_data_storage, Storage face_data_storage)
By default, we hold all data on the call stacks.
create_data_structures(self)
Call the superclass' create_data_structures() to ensure that all the data structures are in place,...
add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory="")
Add the enclave task for the GPU.
__init__(self, name, patch_size, overlap, rk_order, unknowns, auxiliary_variables, min_meshcell_h, max_meshcell_h, plot_grid_properties, kernel_namespace, pde_terms_without_state, device_resident_rk=False)
Instantiate a generic FV scheme with an overlap of 1.
create_action_sets(self)
Call superclass routine and then reconfigure the update cell call.
user_action_set_includes(self)
Add further includes to this property, if your action sets require some additional routines from othe...
__init__(self, 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.
Probably the simplest solver you could think off.
add_entries_to_text_replacement_dictionary(self, d)
d: Dictionary of string to string in/out argument
user_action_set_includes(self)
Add further includes to this property, if your action sets require some additional routines from othe...
create_data_structures(self)
Call the superclass' create_data_structures() to ensure that all the data structures are in place,...
create_action_sets(self)
Call superclass routine and then reconfigure the update cell call.