Peano
Loading...
Searching...
No Matches
EnclaveTasking.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 .FV import FV
4from exahype2.solvers.PDETerms import PDETerms
5
6import peano4
7import exahype2
8
9import jinja2
10import os
11
13 ReconstructPatchAndApplyFunctor,
14)
15from exahype2.solvers.fv.actionsets.AbstractFVActionSet import AbstractFVActionSet
16
17from exahype2.solvers.Storage import Storage
18
19
20
22 """!
23 Update cell in primary sweep
24
25 This action set is used in the primary sweeps only. In the secondary sweep,
26 its counterpart, the action set MergeEnclaveTaskOutcome, is active and works in all data
27 computed by tasks which have been spawned here.
28
29 We extend the superclass ReconstructPatchAndApplyFunctor and hence have
30 access to the reconstructed data including a halo layer of one. Furthermore,
31 the superclass provides us with a guard which we should use, as this guard
32 ensures that we reconstruct the patch plus halo if and only if certain
33 conditions are met. By default, we compute only on unrefined octants.
34
35 Our condition whether to spawn a task or to compute the new time step
36 data immediately depends on peano4::datamanagement::CellMarker::willBeSkeletonCell().
37 If this predicate holds, we compute stuff straightaway. Otherwise, we
38 span a new enclave task. While this check does the job in almost all
39 cases, there are special situations where you might want to label more
40 cells as skeleton cells.
41
42
43 ## Modify templates
44
45 You can alter the template. Typical codes augment _Template_TouchCellFirstTime_Preamble
46 for example. However, there are two things to consider:
47
48 - _Template_TouchCellFirstTime_Preamble is a member of the class and
49 initialised in the constructor.
50 - The constructor is used to create an object in the create_action_sets()
51 of the using class.
52
53 If you want to alter the preamble, you thus should specialise
54 create_action_sets() and invoke the supertype's create_action_sets(). After
55 that, alter self._action_set_update_cell._Template_TouchCellFirstTime_Preamble.
56 We recommend to add an entry and not to replace the preamble, as the
57 preamble already consists meaningful code.
58 """
59
60 TemplateUpdateCell = jinja2.Template(
61 """
62 double timeStamp = fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp();
63
64 // Set the following two parameters
65 // double timeStepSize
66 {{COMPUTE_TIME_STEP_SIZE}}
67
68 {{PREPROCESS_RECONSTRUCTED_PATCH}}
69
70 assertion2(tarch::la::greaterEquals( timeStepSize, 0.0 ), timeStepSize, timeStamp);
71 assertion2(tarch::la::greaterEquals( timeStamp, 0.0 ), timeStepSize, timeStamp);
72
73 ::exahype2::fv::validatePatch(
74 oldQWithHalo,
75 {{NUMBER_OF_UNKNOWNS}},
76 {{NUMBER_OF_AUXILIARY_VARIABLES}},
77 {{NUMBER_OF_VOLUMES_PER_AXIS}},
78 1, // Halo size
79 std::string(__FILE__) + "(" + std::to_string(__LINE__) + "): " + marker.toString()
80 ); // Previous time step has to be valid
81
82 if (marker.willBeSkeletonCell()) {
83 const double maxEigenvalue = tasks::{{SOLVER_NAME}}EnclaveTask::applyKernelToCell(
84 marker,
85 timeStamp,
86 timeStepSize,
87 oldQWithHalo,
88 newQ
89 );
90
91 {{COMPUTE_NEW_TIME_STEP_SIZE}}
92
93 fineGridCell{{SEMAPHORE_LABEL}}.setSemaphoreNumber(::exahype2::EnclaveBookkeeping::SkeletonTask);
94 fineGridCell{{SOLVER_NAME}}CellLabel.setHasUpdated(true);
95 fineGridCell{{SOLVER_NAME}}CellLabel.setTimeStamp(timeStamp + timeStepSize);
96 fineGridCell{{SOLVER_NAME}}CellLabel.setTimeStepSize(newTimeStepSize);
97 } else { // is an enclave cell
98 assertion(marker.willBeEnclaveCell());
99 assertion(not marker.willBeRefined());
100 auto newEnclaveTask = new tasks::{{SOLVER_NAME}}EnclaveTask(
101 marker,
102 timeStamp,
103 timeStepSize,
104 oldQWithHalo,
105 {% if MAKE_COPY_OF_ENCLAVE_TASK_DATA %}
106 nullptr
107 {% else %}
108 newQ
109 {% endif %}
110 );
111
112 int predecessorEnclaveTaskNumber = fineGridCell{{SEMAPHORE_LABEL}}.getSemaphoreNumber();
113
114 fineGridCell{{SEMAPHORE_LABEL}}.setSemaphoreNumber( newEnclaveTask->getTaskId() );
115
116 tarch::multicore::spawnTask(
117 newEnclaveTask,
118 predecessorEnclaveTaskNumber>=0 ? std::set<int>{predecessorEnclaveTaskNumber} : tarch::multicore::NoInDependencies,
119 newEnclaveTask->getTaskId()
120 );
121
122 if (predecessorEnclaveTaskNumber>=0) {
123 ::exahype2::EnclaveTask::releaseTaskNumber(predecessorEnclaveTaskNumber);
124 }
125
126 fineGridCell{{SOLVER_NAME}}CellLabel.setTimeStamp(timeStamp + timeStepSize);
127 }
128 """
129 )
130
131
132 def __init__(self, solver):
133 ReconstructPatchAndApplyFunctor.__init__(
134 self,
135 patch=solver._patch,
136 # todo hier muessen beide rein, denn ich muss ja interpolieren -> machen die anderen Codes dann
137 patch_overlap=solver._patch_overlap_new,
138 functor_implementation="<not yet set - will do this later>",
139 reconstructed_array_memory_location=peano4.toolbox.blockstructured.ReconstructedArrayMemoryLocation.ManagedSharedAcceleratorDeviceMemoryThroughTarchWithoutDelete,
140 # todo Dokumentieren, dass net willBeRefined(), weil wir ja das brauchen wenn wir runtergehen
141 guard="not marker.hasBeenRefined() and ("
142 + "repositories::"
143 + solver.get_name_of_global_instance()
144 + ".getSolverState()=="
145 + solver._name
146 + "::SolverState::Primary or "
147 + "repositories::"
148 + solver.get_name_of_global_instance()
149 + ".getSolverState()=="
150 + solver._name
151 + "::SolverState::PrimaryAfterGridInitialisation"
152 + ")",
153 add_assertions_to_halo_exchange=True,
154 )
155
157 """
158 if (
159 repositories::"""
160 + solver.get_name_of_global_instance()
161 + """.getSolverState()=="""
162 + solver._name
163 + """::SolverState::Primary or
164 repositories::"""
165 + solver.get_name_of_global_instance()
166 + """.getSolverState()=="""
167 + solver._name
168 + """::SolverState::PrimaryAfterGridInitialisation
169 ) {{
170 fineGridCell"""
171 + solver._name
172 + """CellLabel.setHasUpdated(false);
173 }}
174"""
176 )
177
178 self._solver = solver
179
180
181 def get_includes(self):
182 return (
183 ReconstructPatchAndApplyFunctor.get_includes(self)
184 + """
185#include "tarch/multicore/Task.h"
186#include "repositories/SolverRepository.h"
187#include "tasks/"""
188 + self._solver._name
189 + """EnclaveTask.h"
190"""
191 + self._solver._get_default_includes()
192 + self._solver.user_action_set_includes
193 )
194
195
197 return __name__.replace(".py", "").replace(".", "_") + "_ReconstructPatchAndApplyFunctor"
198
199
201 """
202 First ask the solver to add its symbols, and then re-construct the
203 functor which should not contain any symbols after that anymore.
204 Next, we call the superclass routine which supplements all those
205 instructions that any reconstruction wants to have.
206 """
207
208 self._solver._init_dictionary_with_default_parameters(d)
209 self._solver.add_entries_to_text_replacement_dictionary(d)
210
212
213 super(UpdateCell, self)._add_action_set_entries_to_dictionary(d)
214
215
217 Template = """
218 if (
219 not marker.hasBeenRefined()
220 and
221 {{GUARD}}
222 and
223 repositories::{{SOLVER_INSTANCE}}.getSolverState() == {{SOLVER_NAME}}::SolverState::Secondary
224 ) {
225 const int taskNumber = fineGridCell{{LABEL_NAME}}.getSemaphoreNumber();
226 if (marker.hasBeenEnclaveCell() and taskNumber >= 0) {
227 double maxEigenvalue;
228 ::exahype2::EnclaveBookkeeping::getInstance().waitForTaskToTerminateAndCopyResultOver(
229 taskNumber,
230 fineGridCell{{UNKNOWN_IDENTIFIER}}.value,
231 maxEigenvalue,
232 marker.x(),
233 marker.h()
234 );
235
236 ::exahype2::fv::validatePatch(
237 fineGridCell{{UNKNOWN_IDENTIFIER}}.value,
238 {{NUMBER_OF_UNKNOWNS}},
239 {{NUMBER_OF_AUXILIARY_VARIABLES}},
240 {{NUMBER_OF_VOLUMES_PER_AXIS}},
241 0,
242 std::string(__FILE__) + ": " + std::to_string(__LINE__) + "; marker=" + marker.toString()
243 );
244
245 {{COMPUTE_NEW_TIME_STEP_SIZE}}
246
247 fineGridCell{{LABEL_NAME}}.setSemaphoreNumber( ::exahype2::EnclaveBookkeeping::NoEnclaveTaskNumber );
248 fineGridCell{{SOLVER_NAME}}CellLabel.setHasUpdated(true);
249 fineGridCell{{SOLVER_NAME}}CellLabel.setTimeStepSize(newTimeStepSize);
250 }
251
252 if (fineGridCell{{SOLVER_NAME}}CellLabel.getHasUpdated()) {
253 double* newQ = fineGridCell{{UNKNOWN_IDENTIFIER}}.value;
254
255 {{POSTPROCESS_UPDATED_PATCH}}
256
257 repositories::{{SOLVER_INSTANCE}}.update(
258 fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStepSize(),
259 fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp(),
260 marker.h()(0)
261 );
262 } else {
263 repositories::{{SOLVER_INSTANCE}}.update(
264 0.0,
265 fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp(),
266 marker.h()(0)
267 );
268 }
269 }
270"""
271
272
273 def __init__(self, solver):
274 super(MergeEnclaveTaskOutcome, self).__init__(solver)
275 self.label_name = exahype2.grid.UpdateCellLabel.get_attribute_name(solver._name)
276 self.guard = "true"
277 self.descend_invocation_order = solver._baseline_action_set_descend_invocation_order
278
279
280 def get_body_of_operation(self, operation_name):
281 result = ""
282 if (
283 operation_name
284 == peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME
285 ):
286 d = {}
287 self._solver._init_dictionary_with_default_parameters(d)
288 self._solver.add_entries_to_text_replacement_dictionary(d)
289 d["LABEL_NAME"] = self.label_name
290 d["GUARD"] = self.guard
291 result = jinja2.Template(self.Template).render(**d)
292 pass
293 return result
294
295
297 return __name__.replace(".py", "").replace(".", "_") + "_MergeEnclaveTaskOutcome"
298
299
301 """!
302 Enclave tasking variant of the Finite Volume scheme
303
304 The concept of (enclave) tasking within ExaHyPE solvers is described in
305 detail in the @ref page_exahype_solvers_enclave_solvers "generic enclave discussion of ExaHyPE".
306 This class is a prototype realisation of this concept which other solvers
307 then specialise for particular numerical schemes.
308
309 The class basically replaces the standard "update a cell" action set with an
310 action set that might or might not spawn a task. In return, it adds a further
311 action set which merges the arising task outcomes into the actual mesh
312 structure. By default, we use peano4::datamanagement::CellMarker::willBeEnclaveCell()
313 and peano4::datamanagement::CellMarker::hasBeenEnclaveCell() to guide the
314 decision whether to spawn a task or not. You can overwrite this decision
315 by redefining the corresponding entry in the dictionary befilled by
316 add_entries_to_text_replacement_dictionary().
317
318
319 ## Task priorities
320
321 Use the attributes self.enclave_task_priority to change the priority of the
322 task. This value can either be a string that C++ can evaluate into a
323 priority or a plain numerical value. I set it to
324
325 self.enclave_task_priority = "tarch::multicore::Task::DefaultPriority-1"
326
327 by default.
328 """
329
331 self,
332 name,
333 patch_size,
334 overlap,
335 unknowns,
336 auxiliary_variables,
337 min_volume_h,
338 max_volume_h,
339 plot_grid_properties,
340 pde_terms_without_state: bool,
341 kernel_namespace,
342 ):
343 """
344 Not so nice. I have to store this field as I later rely on get_name_of_global_instance()
345 which uses this field.
346 """
347 self._name = name
348
349 self._fused_compute_kernel_call_stateless_cpu = "#error Not yet defined"
350 self._fused_compute_kernel_call_stateless_gpu = "#error Not yet defined"
351
352 super(EnclaveTasking, self).__init__(
353 name,
354 patch_size,
355 overlap,
356 unknowns,
357 auxiliary_variables,
358 min_volume_h,
359 max_volume_h,
360 plot_grid_properties,
361 pde_terms_without_state,
362 kernel_namespace,
363 )
364 self._solver_template_file_class_name = "EnclaveTasking"
365
366 additional_includes = """
367#include "exahype2/EnclaveBookkeeping.h"
368#include "exahype2/EnclaveTask.h"
369"""
370
371 self.add_user_action_set_includes(additional_includes)
372 self.enclave_task_priority = "tarch::multicore::Task::DefaultPriority-1"
373
374
375 def _create_guards(self):
376 """!
377 All the internal logic depends on guards, i.e., boolean predicates. We
378 want to be able to alter them in subclasses, but we need a certain
379 baseline. It is defined in this routine.
380 """
382 "("
383 + "repositories::"
385 + ".getSolverState()=="
386 + self._name
387 + "::SolverState::GridInitialisation"
388 + ")"
389 )
390
392 "("
393 + "repositories::"
395 + ".getSolverState()=="
396 + self._name
397 + "::SolverState::PrimaryAfterGridInitialisation or "
398 + "repositories::"
400 + ".getSolverState()=="
401 + self._name
402 + "::SolverState::PlottingInitialCondition"
403 + ")"
404 )
405
407 "("
408 + "repositories::"
410 + ".getSolverState()=="
411 + self._name
412 + "::SolverState::Primary or "
413 + "repositories::"
415 + ".getSolverState()=="
416 + self._name
417 + "::SolverState::PrimaryAfterGridInitialisation"
418 + ")"
419 )
420
422 repositories::{}.getSolverState()=={}::SolverState::Primary
423 or repositories::{}.getSolverState()=={}::SolverState::PrimaryAfterGridInitialisation
424 or repositories::{}.getSolverState()=={}::SolverState::Plotting
425 or repositories::{}.getSolverState()=={}::SolverState::PlottingInitialCondition
426 or repositories::{}.getSolverState()=={}::SolverState::Suspended
427 )""".format(
433 )
434
436 repositories::{}.getSolverState()=={}::SolverState::GridInitialisation
437 or repositories::{}.getSolverState()=={}::SolverState::Primary
438 or repositories::{}.getSolverState()=={}::SolverState::PrimaryAfterGridInitialisation
439 )""".format(
443 )
444
446 repositories::{}.getSolverState()=={}::SolverState::GridInitialisation
447 or repositories::{}.getSolverState()=={}::SolverState::Primary
448 or repositories::{}.getSolverState()=={}::SolverState::PrimaryAfterGridInitialisation
449 or repositories::{}.getSolverState()=={}::SolverState::GridConstruction
450 )""".format(
455 )
456
458 "("
459 + "repositories::"
461 + ".getSolverState()=="
462 + self._name
463 + "::SolverState::Secondary"
464 + ")"
465 )
466
468 "("
469 + "repositories::"
471 + ".getSolverState()=="
472 + self._name
473 + "::SolverState::Secondary or "
474 + "repositories::"
476 + ".getSolverState()=="
477 + self._name
478 + "::SolverState::GridConstruction"
479 + ")"
480 )
481
483 "("
484 + "repositories::"
486 + ".getSolverState()=="
487 + self._name
488 + "::SolverState::Secondary or "
489 + "repositories::"
491 + ".getSolverState()=="
492 + self._name
493 + "::SolverState::GridInitialisation"
494 + ")"
495 )
496
498 repositories::{}.getSolverState()=={}::SolverState::Secondary
499 or repositories::{}.getSolverState()=={}::SolverState::GridInitialisation
500 or repositories::{}.getSolverState()=={}::SolverState::PlottingInitialCondition
501 or repositories::{}.getSolverState()=={}::SolverState::Plotting
502 or repositories::{}.getSolverState()=={}::SolverState::Suspended
503 )""".format(
509 )
510
511
513 """
514 This routine does not really add new data, but it heavily tailors when data are
515 stored, exchanged, ... Each generator has some guard attributes, i.e., some guards,
516 which control when data is stored, sent, received. The routine takes these guards
517 and rewires them to the local guards of this object. If you alter these guards
518 further, you have to alter them before you invoke this class' create_data_structures().
519 """
520 super(EnclaveTasking, self).create_data_structures()
521
522 self._create_guards()
523
524 self._patch_overlap_new.generator.send_condition = (
526 )
527 self._patch_overlap_new.generator.receive_and_merge_condition = (
529 )
530
531 self._patch_overlap_old.generator.send_condition = (
533 )
534 self._patch_overlap_old.generator.receive_and_merge_condition = (
536 )
537
538
540 """!
541 Make storage and loading more restrictive such that enclave data are not held in-between primary and secondary sweep
542
543 If you work with global time stepping, you know that each enclave cell will
544 be updated per grid traversal duo. Consequently, every enclave cell's data
545 doesn't have to be stored in-between two grid traversals - we know that it
546 is currently outsourced to a task.
547
548 Things are different if we use local time stepping, as there will always be
549 cells that are currently processed, and then there are cells which are not
550 updated and which we consequently should keep.
551
552 If you want to have this optimisation, you have to call this routine
553 explicitly in create_data_structures(). By default, we always store the
554 patches all the time.
555
556 If we work with smart pointers, it is a bad idea to call this routine,
557 as the enclave framework does not(!) use smart pointers. So we rely on
558 the fact that someone holds the raw pointers alive. If we don't store
559 data here, we run risk that the smart pointer becomes zero and the
560 underlying memory is freed while the enclave task still works against it.
561
562 As I don't know what storage scheme we employ, I decided to disable
563 this routine. Notably as I don't think storing makes much of a
564 difference if data are held on the heap anyway.
565 """
566 return
567 #self._patch.generator.load_store_compute_flag = "::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
568 # self._provide_cell_data_to_compute_kernels_default_guard(),
569 # self._load_cell_data_default_guard()
570 # + " and ("
571 # + self._primary_sweep_or_plot_guard
572 # + " or marker.hasBeenSkeletonCell())",
573 # self._store_cell_data_default_guard()
574 # + " and ("
575 # + self._secondary_sweep_or_grid_initialisation_or_plot_guard
576 # + " or marker.willBeSkeletonCell())",
577 #)
578
579
581 """
582 Adaptive mesh handing
583
584 Adaptive meshes require us to clear the patch overlaps and to restrict/interpolate.
585 Obviously, there's no need to do this for a refined faces. So we can eliminate these
586 cases a priori. Furthermore, we clear faces only in the primary sweep. We know that
587 either the primary sweep (for skeleton) or the secondary sweep (for enclaves) will
588 write in proper data into anything that's cleared, and we know that restriction only
589 has to happen after the primary sweep, as all cells next to an adaptivity boundary
590 are skeleton cells.
591
592 As pointed out, both interpolation and restriction are to be active for the first
593 sweep only. We interpolate into hanging faces, and we have to restrict immediately
594 again as they are non-persistent. The projection onto the (hanging) faces is also
595 happening directly in the primary sweep, as the cells adjacent to the hanging
596 face are skeleton cells.
597
598 AMR and adjust cell have to be there always, i.e., also throughout
599 the grid construction. But the criterion is something that we only
600 evaluate in the secondary sweep. That's when we have an updated/changed time step.
601 If we identify coarsening and refinement instructions in the secondary sweep, the
602 next primary one will actually see them and trigger the update. That is, the
603 subsequent secondary switch will actually implement the grid changes, and we can
604 evaluate the criteria again.
605
606 For dynamic AMR, this implies that we have to ensure that all changed grid parts
607 are labelled as skeleton cells. This way, we can implement the AMR properly, we
608 ensure that all the enclaves run in parallel, and we know that all data is held
609 persistently on the stacks.
610 """
611 super(EnclaveTasking, self).create_action_sets()
612
615
616 self._action_set_initial_conditions.guard = (
618 )
621 )
625 )
626 # We do not set the guard of the secondary sweep
627 # self._action_set_postprocess_solution.guard = self._secondary_sweep_guard
628
629 self._action_set_handle_boundary.guard = (
631 + " and "
633 )
636 + " and ("
637 + "(repositories::"
639 + ".getSolverState()=="
640 + self._name
641 + "::SolverState::Primary and marker.willBeSkeletonCell() ) "
642 + "or (repositories::"
644 + ".getSolverState()=="
645 + self._name
646 + "::SolverState::PrimaryAfterGridInitialisation and marker.willBeSkeletonCell() ) "
647 + "or (repositories::"
649 + ".getSolverState()=="
650 + self._name
651 + "::SolverState::Secondary and marker.willBeEnclaveCell() ) "
652 + "or (repositories::"
654 + ".getSolverState()=="
655 + self._name
656 + "::SolverState::GridInitialisation )"
657 + ")"
658 )
661 + " and "
663 )
666 + " and "
668 )
669
670
672 self,
673 boundary_conditions,
674 refinement_criterion,
675 initial_conditions,
676 memory_location,
677 use_split_loop,
678 additional_action_set_includes,
679 additional_user_includes,
680 ):
681 """
682 If you pass in User_Defined, then the generator will create C++ stubs
683 that you have to befill manually. If you pass in None_Implementation, it
684 will create nop, i.e., no implementation or defaults. Any other string
685 is copied 1:1 into the implementation. If you pass in None, then the
686 set value so far won't be overwritten.
687 """
688 if boundary_conditions is not None:
689 self._boundary_conditions_implementation = boundary_conditions
690 if refinement_criterion is not None:
691 self._refinement_criterion_implementation = refinement_criterion
692 if initial_conditions is not None:
693 self._initial_conditions_implementation = initial_conditions
694 if memory_location is not None:
696 if use_split_loop:
697 self._use_split_loop = use_split_loop
698
699 if refinement_criterion == exahype2.solvers.PDETerms.None_Implementation:
700 assert False, "Refinement criterion cannot be none"
701 if initial_conditions == exahype2.solvers.PDETerms.None_Implementation:
702 assert False, "Initial conditions cannot be none"
703
704 if (
705 memory_location
706 != peano4.toolbox.blockstructured.ReconstructedArrayMemoryLocation.HeapThroughTarchWithoutDelete
707 and memory_location != None
708 ):
709 raise Exception(
710 "Only valid memory mode for enclave tasking is heap without a delete, as enclave tasks delete memory themselves through the tarch. Selected mode="
711 + str(solver._reconstructed_array_memory_location)
712 )
713
714 self.create_action_sets()
715
716
718 """
719 d: Dictionary of string to string
720 in/out argument
721 """
722 d["NUMBER_OF_DOUBLE_VALUES_IN_PATCH_2D"] = (
723 d["NUMBER_OF_VOLUMES_PER_AXIS"]
724 * d["NUMBER_OF_VOLUMES_PER_AXIS"]
725 * (d["NUMBER_OF_UNKNOWNS"] + d["NUMBER_OF_AUXILIARY_VARIABLES"])
726 )
727 d["NUMBER_OF_DOUBLE_VALUES_IN_PATCH_3D"] = (
728 d["NUMBER_OF_VOLUMES_PER_AXIS"]
729 * d["NUMBER_OF_VOLUMES_PER_AXIS"]
730 * d["NUMBER_OF_VOLUMES_PER_AXIS"]
731 * (d["NUMBER_OF_UNKNOWNS"] + d["NUMBER_OF_AUXILIARY_VARIABLES"])
732 )
733
734 d["NUMBER_OF_DOUBLE_VALUES_IN_PATCH_PLUS_HALO_2D"] = (
735 (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
736 * (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
737 * (d["NUMBER_OF_UNKNOWNS"] + d["NUMBER_OF_AUXILIARY_VARIABLES"])
738 )
739 d["NUMBER_OF_DOUBLE_VALUES_IN_PATCH_PLUS_HALO_3D"] = (
740 (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
741 * (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
742 * (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
743 * (d["NUMBER_OF_UNKNOWNS"] + d["NUMBER_OF_AUXILIARY_VARIABLES"])
744 )
745
746 d["FUSED_COMPUTE_KERNEL_CALL_STATELESS_CPU"] = jinja2.Template(
747 self._fused_compute_kernel_call_stateless_cpu, undefined=jinja2.DebugUndefined
748 ).render(**d)
749 d["FUSED_COMPUTE_KERNEL_CALL_STATELESS_GPU"] = jinja2.Template(
750 self._fused_compute_kernel_call_stateless_gpu, undefined=jinja2.DebugUndefined
751 ).render(**d)
752
753 d["SEMAPHORE_LABEL"] = exahype2.grid.UpdateCellLabel.get_attribute_name(
754 self._name
755 )
756 d["ENCLAVE_TASK_PRIORITY"] = self.enclave_task_priority
757 d["MAKE_COPY_OF_ENCLAVE_TASK_DATA"] = self.make_copy_of_enclave_task_data
758
759
760 def add_actions_to_create_grid(self, step, evaluate_refinement_criterion):
761 super(EnclaveTasking, self).add_actions_to_create_grid(
762 step, evaluate_refinement_criterion
763 )
764 step.add_action_set(exahype2.grid.UpdateCellLabel(self._name))
765
766
768 super(EnclaveTasking, self).add_actions_to_init_grid(step)
769 step.add_action_set(exahype2.grid.UpdateCellLabel(self._name))
770
771
773 """!
774 Add enclave aspect to time stepping
775
776 There's a bunch of different things to do to extend my standard solver
777 into an enclave solver. In this operation, we add the runtime logic,
778 i.e., what happens at which point.
779
780 We need additional action sets that are
781 triggered throughout the traversal in every second time step. I call this
782 one task_based_implementation_primary_iteration or secondary,
783 respectively. One wraps the implementation of _HandleCellTemplate into a
784 task, the other communicates with the task bookkeeping only. Both rely on
785 additional labels within the cell. We therefore end up with three new
786 action sets: reconstruct_patch_and_apply_FV_kernel, exahype2.grid.UpdateCellLabel
787 and roll_over_enclave_task_results.
788 """
789 super(EnclaveTasking, self).add_actions_to_perform_time_step(step)
790 step.add_action_set(self._action_set_merge_enclave_task_outcome)
791
792
793 def add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory=""):
794 super(EnclaveTasking, self).add_implementation_files_to_project(
795 namespace, output, dimensions
796 )
797
798 templatefile_prefix = os.path.join(
799 os.path.dirname(os.path.realpath(__file__)),
800 "EnclaveTasking.EnclaveTask.template",
801 )
802
803 if(subdirectory):
804 subdirectory += "/"
805
806 implementationDictionary = {}
807 self._init_dictionary_with_default_parameters(implementationDictionary)
808 self.add_entries_to_text_replacement_dictionary(implementationDictionary)
809
810 # Some includes might logically belong into the action sets, but now they are
811 # 'outsourced' into the enclave task. So we manually add it here.
812 implementationDictionary["SOLVER_INCLUDES"] += self.user_solver_includes
813 implementationDictionary["SOLVER_INCLUDES"] += self.user_action_set_includes
814
815 task_name = self._enclave_task_name()
816 generated_solver_files = (
818 "{}.h".format(templatefile_prefix),
819 "{}.cpp".format(templatefile_prefix),
820 task_name,
821 namespace + ["tasks"],
822 subdirectory + "tasks",
823 implementationDictionary,
824 True,
825 )
826 )
827
828 output.add(generated_solver_files)
829 output.makefile.add_h_file(subdirectory + "tasks/" + task_name + ".h", generated=True)
830 output.makefile.add_cpp_file(subdirectory + "tasks/" + task_name + ".cpp", generated=True)
831
832
834 return "{}EnclaveTask".format(self._name)
835
836
838 self,
839 cell_data_storage: Storage,
840 face_data_storage: Storage,
841 ):
842 if cell_data_storage == Storage.SmartPointers:
844 else:
846
847 super(EnclaveTasking, self).switch_storage_scheme(
848 cell_data_storage,
849 face_data_storage
850 )
Update the cell label within a sweep.
Definition CellLabel.py:9
Enclave tasking variant of the Finite Volume scheme.
switch_storage_scheme(self, Storage cell_data_storage, Storage face_data_storage)
By default, we hold all data on the heap using smart pointers.
add_entries_to_text_replacement_dictionary(self, d)
d: Dictionary of string to string in/out argument
__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)
Not so nice.
_optimise_patch_storage_for_global_time_stepping(self)
Make storage and loading more restrictive such that enclave data are not held in-between primary and ...
set_implementation(self, boundary_conditions, refinement_criterion, initial_conditions, memory_location, use_split_loop, 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...
_create_guards(self)
All the internal logic depends on guards, i.e., boolean predicates.
create_data_structures(self)
This routine does not really add new data, but it heavily tailors when data are stored,...
add_actions_to_init_grid(self, step)
Add all the action sets to init grid.
add_actions_to_perform_time_step(self, step)
Add enclave aspect to time stepping.
add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory="")
The ExaHyPE project will call this operation when it sets up the overall environment.
add_actions_to_create_grid(self, step, evaluate_refinement_criterion)
The boundary information is set only once.
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...
get_includes(self)
Return include statements that you need.
_add_action_set_entries_to_dictionary(self, d)
First ask the solver to add its symbols, and then re-construct the functor which should not contain a...
get_action_set_name(self)
Return unique action set name.
Abstract finite volume solver step sizes that works on patch-based AMR with a halo layer of one.
Definition FV.py:18
tuple _action_set_initial_conditions_for_grid_construction
Definition FV.py:275
_store_face_data_default_guard(self)
Extend the guard via ands only.
Definition FV.py:947
user_solver_includes(self)
Add further includes to this property, if your solver requires some additional routines from other he...
Definition FV.py:501
_init_dictionary_with_default_parameters(self, d)
This one is called by all algorithmic steps before I invoke add_entries_to_text_replacement_dictionar...
Definition FV.py:1400
tuple _action_set_AMR_commit_without_further_analysis
Definition FV.py:278
tuple _action_set_handle_boundary
Definition FV.py:279
tuple _action_set_initial_conditions
Definition FV.py:274
_store_cell_data_default_guard(self)
Extend the guard via ands only.
Definition FV.py:912
tuple _action_set_project_patch_onto_faces
Definition FV.py:280
get_name_of_global_instance(self)
Definition FV.py:986
user_action_set_includes(self)
Add further includes to this property, if your action sets require some additional routines from othe...
Definition FV.py:492
tuple _action_set_couple_resolution_transitions_and_handle_dynamic_mesh_refinement
Definition FV.py:283
add_user_action_set_includes(self, value)
Add further includes to this property, if your action sets require some additional routines from othe...
Definition FV.py:509
tuple _action_set_roll_over_update_of_faces
Definition FV.py:281