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 tarch::multicore::spawnTask(
115 newEnclaveTask,
116 predecessorEnclaveTaskNumber>=0 ? std::set<int>{predecessorEnclaveTaskNumber} : tarch::multicore::NoInDependencies,
117 newEnclaveTask->getTaskId()
118 );
119
120 if (predecessorEnclaveTaskNumber>=0) {
121 ::exahype2::EnclaveTask::releaseTaskNumber(predecessorEnclaveTaskNumber);
122 }
123
124 fineGridCell{{SEMAPHORE_LABEL}}.setSemaphoreNumber( newEnclaveTask->getTaskId() );
125 fineGridCell{{SOLVER_NAME}}CellLabel.setTimeStamp(timeStamp + timeStepSize);
126 }
127 """
128 )
129
130
131 def __init__(self, solver):
132 ReconstructPatchAndApplyFunctor.__init__(
133 self,
134 patch=solver._patch,
135 # todo hier muessen beide rein, denn ich muss ja interpolieren -> machen die anderen Codes dann
136 patch_overlap=solver._patch_overlap_new,
137 functor_implementation="<not yet set - will do this later>",
138 reconstructed_array_memory_location=peano4.toolbox.blockstructured.ReconstructedArrayMemoryLocation.ManagedSharedAcceleratorDeviceMemoryThroughTarchWithoutDelete,
139 # todo Dokumentieren, dass net willBeRefined(), weil wir ja das brauchen wenn wir runtergehen
140 guard="not marker.hasBeenRefined() and ("
141 + "repositories::"
142 + solver.get_name_of_global_instance()
143 + ".getSolverState()=="
144 + solver._name
145 + "::SolverState::Primary or "
146 + "repositories::"
147 + solver.get_name_of_global_instance()
148 + ".getSolverState()=="
149 + solver._name
150 + "::SolverState::PrimaryAfterGridInitialisation"
151 + ")",
152 add_assertions_to_halo_exchange=True,
153 )
154
156 """
157 if (
158 repositories::"""
159 + solver.get_name_of_global_instance()
160 + """.getSolverState()=="""
161 + solver._name
162 + """::SolverState::Primary or
163 repositories::"""
164 + solver.get_name_of_global_instance()
165 + """.getSolverState()=="""
166 + solver._name
167 + """::SolverState::PrimaryAfterGridInitialisation
168 ) {{
169 fineGridCell"""
170 + solver._name
171 + """CellLabel.setHasUpdated(false);
172 }}
173"""
175 )
176
177 self._solver = solver
178
179
180 def get_includes(self):
181 return (
182 ReconstructPatchAndApplyFunctor.get_includes(self)
183 + """
184#include "tarch/multicore/Task.h"
185#include "repositories/SolverRepository.h"
186#include "tasks/"""
187 + self._solver._name
188 + """EnclaveTask.h"
189"""
190 + self._solver._get_default_includes()
191 + self._solver.user_action_set_includes
192 )
193
194
196 return __name__.replace(".py", "").replace(".", "_")
197
198
200 """
201 First ask the solver to add its symbols, and then re-construct the
202 functor which should not contain any symbols after that anymore.
203 Next, we call the superclass routine which supplements all those
204 instructions that any reconstruction wants to have.
205 """
206
207 self._solver._init_dictionary_with_default_parameters(d)
208 self._solver.add_entries_to_text_replacement_dictionary(d)
209
211
212 super(UpdateCell, self)._add_action_set_entries_to_dictionary(d)
213
214
216 Template = """
217 if (
218 not marker.hasBeenRefined()
219 and
220 {{GUARD}}
221 and
222 repositories::{{SOLVER_INSTANCE}}.getSolverState() == {{SOLVER_NAME}}::SolverState::Secondary
223 ) {
224 const int taskNumber = fineGridCell{{LABEL_NAME}}.getSemaphoreNumber();
225 if (marker.hasBeenEnclaveCell() and taskNumber >= 0) {
226 double maxEigenvalue;
227 ::exahype2::EnclaveBookkeeping::getInstance().waitForTaskToTerminateAndCopyResultOver( taskNumber, fineGridCell{{UNKNOWN_IDENTIFIER}}.value, maxEigenvalue );
228
229 ::exahype2::fv::validatePatch(
230 fineGridCell{{UNKNOWN_IDENTIFIER}}.value,
231 {{NUMBER_OF_UNKNOWNS}},
232 {{NUMBER_OF_AUXILIARY_VARIABLES}},
233 {{NUMBER_OF_VOLUMES_PER_AXIS}},
234 0,
235 std::string(__FILE__) + ": " + std::to_string(__LINE__) + "; marker=" + marker.toString()
236 );
237
238 {{COMPUTE_NEW_TIME_STEP_SIZE}}
239
240 fineGridCell{{LABEL_NAME}}.setSemaphoreNumber( ::exahype2::EnclaveBookkeeping::NoEnclaveTaskNumber );
241 fineGridCell{{SOLVER_NAME}}CellLabel.setHasUpdated(true);
242 fineGridCell{{SOLVER_NAME}}CellLabel.setTimeStepSize(newTimeStepSize);
243 }
244
245 if (fineGridCell{{SOLVER_NAME}}CellLabel.getHasUpdated()) {
246 double* newQ = fineGridCell{{UNKNOWN_IDENTIFIER}}.value;
247
248 {{POSTPROCESS_UPDATED_PATCH}}
249
250 repositories::{{SOLVER_INSTANCE}}.update(
251 fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStepSize(),
252 fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp(),
253 marker.h()(0)
254 );
255 } else {
256 repositories::{{SOLVER_INSTANCE}}.update(
257 0.0,
258 fineGridCell{{SOLVER_NAME}}CellLabel.getTimeStamp(),
259 marker.h()(0)
260 );
261 }
262 }
263"""
264
265
266 def __init__(self, solver):
267 super(MergeEnclaveTaskOutcome, self).__init__(solver)
268 self.label_name = exahype2.grid.UpdateCellLabel.get_attribute_name(solver._name)
269 self.guard = "true"
270 self.descend_invocation_orderdescend_invocation_order = solver._baseline_action_set_descend_invocation_order
271
272
273 def get_body_of_operation(self, operation_name):
274 result = ""
275 if (
276 operation_name
277 == peano4.solversteps.ActionSet.OPERATION_TOUCH_CELL_FIRST_TIME
278 ):
279 d = {}
280 self._solver._init_dictionary_with_default_parameters(d)
281 self._solver.add_entries_to_text_replacement_dictionary(d)
282 d["LABEL_NAME"] = self.label_name
283 d["GUARD"] = self.guard
284 result = jinja2.Template(self.Template).render(**d)
285 pass
286 return result
287
288
290 return __name__.replace(".py", "").replace(".", "_")
291
292
294 """!
295 Enclave tasking variant of the Finite Volume scheme
296
297 The concept of (enclave) tasking within ExaHyPE solvers is described in
298 detail in the @ref page_exahype_solvers_enclave_solvers "generic enclave discussion of ExaHyPE".
299 This class is a prototype realisation of this concept which other solvers
300 then specialise for particular numerical schemes.
301
302 The class basically replaces the standard "update a cell" action set with an
303 action set that might or might not spawn a task. In return, it adds a further
304 action set which merges the arising task outcomes into the actual mesh
305 structure. By default, we use peano4::datamanagement::CellMarker::willBeEnclaveCell()
306 and peano4::datamanagement::CellMarker::hasBeenEnclaveCell() to guide the
307 decision whether to spawn a task or not. You can overwrite this decision
308 by redefining the corresponding entry in the dictionary befilled by
309 add_entries_to_text_replacement_dictionary().
310
311
312 ## Task priorities
313
314 Use the attributes self.enclave_task_priority to change the priority of the
315 task. This value can either be a string that C++ can evaluate into a
316 priority or a plain numerical value. I set it to
317
318 self.enclave_task_priority = "tarch::multicore::Task::DefaultPriority-1"
319
320 by default.
321 """
322
324 self,
325 name,
326 patch_size,
327 overlap,
328 unknowns,
329 auxiliary_variables,
330 min_volume_h,
331 max_volume_h,
332 plot_grid_properties,
333 pde_terms_without_state: bool,
334 kernel_namespace,
335 ):
336 """
337 Not so nice. I have to store this field as I later rely on get_name_of_global_instance()
338 which uses this field.
339 """
340 self._name_name = name
341
342 self._fused_compute_kernel_call_stateless_cpu = "#error Not yet defined"
343 self._fused_compute_kernel_call_stateless_gpu = "#error Not yet defined"
344
345 super(EnclaveTasking, self).__init__(
346 name,
347 patch_size,
348 overlap,
349 unknowns,
350 auxiliary_variables,
351 min_volume_h,
352 max_volume_h,
353 plot_grid_properties,
354 pde_terms_without_state,
355 kernel_namespace,
356 )
358
359 additional_includes = """
360#include "exahype2/EnclaveBookkeeping.h"
361#include "exahype2/EnclaveTask.h"
362"""
363
364 self.add_user_action_set_includes(additional_includes)
365 self.enclave_task_priority = "tarch::multicore::Task::DefaultPriority-1"
366
367
368 def _create_guards(self):
369 """!
370 All the internal logic depends on guards, i.e., boolean predicates. We
371 want to be able to alter them in subclasses, but we need a certain
372 baseline. It is defined in this routine.
373 """
375 "("
376 + "repositories::"
378 + ".getSolverState()=="
379 + self._name_name
380 + "::SolverState::GridInitialisation"
381 + ")"
382 )
383
385 "("
386 + "repositories::"
388 + ".getSolverState()=="
389 + self._name_name
390 + "::SolverState::PrimaryAfterGridInitialisation or "
391 + "repositories::"
393 + ".getSolverState()=="
394 + self._name_name
395 + "::SolverState::PlottingInitialCondition"
396 + ")"
397 )
398
400 "("
401 + "repositories::"
403 + ".getSolverState()=="
404 + self._name_name
405 + "::SolverState::Primary or "
406 + "repositories::"
408 + ".getSolverState()=="
409 + self._name_name
410 + "::SolverState::PrimaryAfterGridInitialisation"
411 + ")"
412 )
413
415 repositories::{}.getSolverState()=={}::SolverState::Primary
416 or repositories::{}.getSolverState()=={}::SolverState::PrimaryAfterGridInitialisation
417 or repositories::{}.getSolverState()=={}::SolverState::Plotting
418 or repositories::{}.getSolverState()=={}::SolverState::PlottingInitialCondition
419 or repositories::{}.getSolverState()=={}::SolverState::Suspended
420 )""".format(
426 )
427
429 repositories::{}.getSolverState()=={}::SolverState::GridInitialisation
430 or repositories::{}.getSolverState()=={}::SolverState::Primary
431 or repositories::{}.getSolverState()=={}::SolverState::PrimaryAfterGridInitialisation
432 )""".format(
436 )
437
439 repositories::{}.getSolverState()=={}::SolverState::GridInitialisation
440 or repositories::{}.getSolverState()=={}::SolverState::Primary
441 or repositories::{}.getSolverState()=={}::SolverState::PrimaryAfterGridInitialisation
442 or repositories::{}.getSolverState()=={}::SolverState::GridConstruction
443 )""".format(
448 )
449
451 "("
452 + "repositories::"
454 + ".getSolverState()=="
455 + self._name_name
456 + "::SolverState::Secondary"
457 + ")"
458 )
459
461 "("
462 + "repositories::"
464 + ".getSolverState()=="
465 + self._name_name
466 + "::SolverState::Secondary or "
467 + "repositories::"
469 + ".getSolverState()=="
470 + self._name_name
471 + "::SolverState::GridConstruction"
472 + ")"
473 )
474
476 "("
477 + "repositories::"
479 + ".getSolverState()=="
480 + self._name_name
481 + "::SolverState::Secondary or "
482 + "repositories::"
484 + ".getSolverState()=="
485 + self._name_name
486 + "::SolverState::GridInitialisation"
487 + ")"
488 )
489
491 repositories::{}.getSolverState()=={}::SolverState::Secondary
492 or repositories::{}.getSolverState()=={}::SolverState::GridInitialisation
493 or repositories::{}.getSolverState()=={}::SolverState::PlottingInitialCondition
494 or repositories::{}.getSolverState()=={}::SolverState::Plotting
495 or repositories::{}.getSolverState()=={}::SolverState::Suspended
496 )""".format(
502 )
503
504
506 """
507 This routine does not really add new data, but it heavily tailors when data are
508 stored, exchanged, ... Each generator has some guard attributes, i.e., some guards,
509 which control when data is stored, sent, received. The routine takes these guards
510 and rewires them to the local guards of this object. If you alter these guards
511 further, you have to alter them before you invoke this class' create_data_structures().
512 """
513 super(EnclaveTasking, self).create_data_structures()
514
515 self._create_guards()
516
517 self._patch_overlap_new.generator.send_condition = (
519 )
520 self._patch_overlap_new.generator.receive_and_merge_condition = (
522 )
523
524 self._patch_overlap_old.generator.send_condition = (
526 )
527 self._patch_overlap_old.generator.receive_and_merge_condition = (
529 )
530
531
533 """!
534 Make storage and loading more restrictive such that enclave data are not held in-between primary and secondary sweep
535
536 If you work with global time stepping, you know that each enclave cell will
537 be updated per grid traversal duo. Consequently, every enclave cell's data
538 doesn't have to be stored in-between two grid traversals - we know that it
539 is currently outsourced to a task.
540
541 Things are different if we use local time stepping, as there will always be
542 cells that are currently processed, and then there are cells which are not
543 updated and which we consequently should keep.
544
545 If you want to have this optimisation, you have to call this routine
546 explicitly in create_data_structures(). By default, we always store the
547 patches all the time.
548
549 If we work with smart pointers, it is a bad idea to call this routine,
550 as the enclave framework does not(!) use smart pointers. So we rely on
551 the fact that someone holds the raw pointers alive. If we don't store
552 data here, we run risk that the smart pointer becomes zero and the
553 underlying memory is freed while the enclave task still works against it.
554
555 As I don't know what storage scheme we employ, I decided to disable
556 this routine. Notably as I don't think storing makes much of a
557 difference if data are held on the heap anyway.
558 """
559 return
560 #self._patch.generator.load_store_compute_flag = "::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
561 # self._provide_cell_data_to_compute_kernels_default_guard(),
562 # self._load_cell_data_default_guard()
563 # + " and ("
564 # + self._primary_sweep_or_plot_guard
565 # + " or marker.hasBeenSkeletonCell())",
566 # self._store_cell_data_default_guard()
567 # + " and ("
568 # + self._secondary_sweep_or_grid_initialisation_or_plot_guard
569 # + " or marker.willBeSkeletonCell())",
570 #)
571
572
574 """
575 Adaptive mesh handing
576
577 Adaptive meshes require us to clear the patch overlaps and to restrict/interpolate.
578 Obviously, there's no need to do this for a refined faces. So we can eliminate these
579 cases a priori. Furthermore, we clear faces only in the primary sweep. We know that
580 either the primary sweep (for skeleton) or the secondary sweep (for enclaves) will
581 write in proper data into anything that's cleared, and we know that restriction only
582 has to happen after the primary sweep, as all cells next to an adaptivity boundary
583 are skeleton cells.
584
585 As pointed out, both interpolation and restriction are to be active for the first
586 sweep only. We interpolate into hanging faces, and we have to restrict immediately
587 again as they are non-persistent. The projection onto the (hanging) faces is also
588 happening directly in the primary sweep, as the cells adjacent to the hanging
589 face are skeleton cells.
590
591 AMR and adjust cell have to be there always, i.e., also throughout
592 the grid construction. But the criterion is something that we only
593 evaluate in the secondary sweep. That's when we have an updated/changed time step.
594 If we identify coarsening and refinement instructions in the secondary sweep, the
595 next primary one will actually see them and trigger the update. That is, the
596 subsequent secondary switch will actually implement the grid changes, and we can
597 evaluate the criteria again.
598
599 For dynamic AMR, this implies that we have to ensure that all changed grid parts
600 are labelled as skeleton cells. This way, we can implement the AMR properly, we
601 ensure that all the enclaves run in parallel, and we know that all data is held
602 persistently on the stacks.
603 """
604 super(EnclaveTasking, self).create_action_sets()
605
608
609 self._action_set_initial_conditions.guard = (
611 )
614 )
618 )
619 # We do not set the guard of the secondary sweep
620 # self._action_set_postprocess_solution.guard = self._secondary_sweep_guard
621
622 self._action_set_handle_boundary.guard = (
624 + " and "
626 )
629 + " and ("
630 + "(repositories::"
632 + ".getSolverState()=="
633 + self._name_name
634 + "::SolverState::Primary and marker.willBeSkeletonCell() ) "
635 + "or (repositories::"
637 + ".getSolverState()=="
638 + self._name_name
639 + "::SolverState::PrimaryAfterGridInitialisation and marker.willBeSkeletonCell() ) "
640 + "or (repositories::"
642 + ".getSolverState()=="
643 + self._name_name
644 + "::SolverState::Secondary and marker.willBeEnclaveCell() ) "
645 + "or (repositories::"
647 + ".getSolverState()=="
648 + self._name_name
649 + "::SolverState::GridInitialisation )"
650 + ")"
651 )
654 + " and "
656 )
659 + " and "
661 )
662
663
665 self,
666 boundary_conditions,
667 refinement_criterion,
668 initial_conditions,
669 memory_location,
670 use_split_loop,
671 additional_action_set_includes,
672 additional_user_includes,
673 ):
674 """
675 If you pass in User_Defined, then the generator will create C++ stubs
676 that you have to befill manually. If you pass in None_Implementation, it
677 will create nop, i.e., no implementation or defaults. Any other string
678 is copied 1:1 into the implementation. If you pass in None, then the
679 set value so far won't be overwritten.
680 """
681 if boundary_conditions is not None:
683 if refinement_criterion is not None:
685 if initial_conditions is not None:
687 if memory_location is not None:
689 if use_split_loop:
690 self._use_split_loop = use_split_loop
691
692 if refinement_criterion == exahype2.solvers.PDETerms.None_Implementation:
693 assert False, "Refinement criterion cannot be none"
694 if initial_conditions == exahype2.solvers.PDETerms.None_Implementation:
695 assert False, "Initial conditions cannot be none"
696
697 if (
698 memory_location
699 != peano4.toolbox.blockstructured.ReconstructedArrayMemoryLocation.HeapThroughTarchWithoutDelete
700 and memory_location != None
701 ):
702 raise Exception(
703 "Only valid memory mode for enclave tasking is heap without a delete, as enclave tasks delete memory themselves through the tarch. Selected mode="
704 + str(solver._reconstructed_array_memory_location)
705 )
706
708
709
711 """
712 d: Dictionary of string to string
713 in/out argument
714 """
715 d["NUMBER_OF_DOUBLE_VALUES_IN_PATCH_2D"] = (
716 d["NUMBER_OF_VOLUMES_PER_AXIS"]
717 * d["NUMBER_OF_VOLUMES_PER_AXIS"]
718 * (d["NUMBER_OF_UNKNOWNS"] + d["NUMBER_OF_AUXILIARY_VARIABLES"])
719 )
720 d["NUMBER_OF_DOUBLE_VALUES_IN_PATCH_3D"] = (
721 d["NUMBER_OF_VOLUMES_PER_AXIS"]
722 * d["NUMBER_OF_VOLUMES_PER_AXIS"]
723 * d["NUMBER_OF_VOLUMES_PER_AXIS"]
724 * (d["NUMBER_OF_UNKNOWNS"] + d["NUMBER_OF_AUXILIARY_VARIABLES"])
725 )
726
727 d["NUMBER_OF_DOUBLE_VALUES_IN_PATCH_PLUS_HALO_2D"] = (
728 (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
729 * (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
730 * (d["NUMBER_OF_UNKNOWNS"] + d["NUMBER_OF_AUXILIARY_VARIABLES"])
731 )
732 d["NUMBER_OF_DOUBLE_VALUES_IN_PATCH_PLUS_HALO_3D"] = (
733 (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
734 * (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
735 * (d["NUMBER_OF_VOLUMES_PER_AXIS"] + 2)
736 * (d["NUMBER_OF_UNKNOWNS"] + d["NUMBER_OF_AUXILIARY_VARIABLES"])
737 )
738
739 d["FUSED_COMPUTE_KERNEL_CALL_STATELESS_CPU"] = jinja2.Template(
740 self._fused_compute_kernel_call_stateless_cpu, undefined=jinja2.DebugUndefined
741 ).render(**d)
742 d["FUSED_COMPUTE_KERNEL_CALL_STATELESS_GPU"] = jinja2.Template(
743 self._fused_compute_kernel_call_stateless_gpu, undefined=jinja2.DebugUndefined
744 ).render(**d)
745
746 d["SEMAPHORE_LABEL"] = exahype2.grid.UpdateCellLabel.get_attribute_name(
747 self._name_name
748 )
749 d["ENCLAVE_TASK_PRIORITY"] = self.enclave_task_priority
750 d["MAKE_COPY_OF_ENCLAVE_TASK_DATA"] = self.make_copy_of_enclave_task_data
751
752
753 def add_actions_to_create_grid(self, step, evaluate_refinement_criterion):
754 super(EnclaveTasking, self).add_actions_to_create_grid(
755 step, evaluate_refinement_criterion
756 )
757 step.add_action_set(exahype2.grid.UpdateCellLabel(self._name_name))
758
759
761 super(EnclaveTasking, self).add_actions_to_init_grid(step)
762 step.add_action_set(exahype2.grid.UpdateCellLabel(self._name_name))
763
764
766 """!
767 Add enclave aspect to time stepping
768
769 There's a bunch of different things to do to extend my standard solver
770 into an enclave solver. In this operation, we add the runtime logic,
771 i.e., what happens at which point.
772
773 We need additional action sets that are
774 triggered throughout the traversal in every second time step. I call this
775 one task_based_implementation_primary_iteration or secondary,
776 respectively. One wraps the implementation of _HandleCellTemplate into a
777 task, the other communicates with the task bookkeeping only. Both rely on
778 additional labels within the cell. We therefore end up with three new
779 action sets: reconstruct_patch_and_apply_FV_kernel, exahype2.grid.UpdateCellLabel
780 and roll_over_enclave_task_results.
781 """
782 super(EnclaveTasking, self).add_actions_to_perform_time_step(step)
783 step.add_action_set(self._action_set_merge_enclave_task_outcome)
784
785
786 def add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory=""):
787 super(EnclaveTasking, self).add_implementation_files_to_project(
788 namespace, output, dimensions
789 )
790
791 templatefile_prefix = os.path.join(
792 os.path.dirname(os.path.realpath(__file__)),
793 "EnclaveTasking.EnclaveTask.template",
794 )
795
796 if(subdirectory):
797 subdirectory += "/"
798
799 implementationDictionary = {}
800 self._init_dictionary_with_default_parameters(implementationDictionary)
802
803 # Some includes might logically belong into the action sets, but now they are
804 # 'outsourced' into the enclave task. So we manually add it here.
805 implementationDictionary["SOLVER_INCLUDES"] += self.user_solver_includes
806 implementationDictionary["SOLVER_INCLUDES"] += self.user_action_set_includes
807
808 task_name = self._enclave_task_name()
809 generated_solver_files = (
811 "{}.h".format(templatefile_prefix),
812 "{}.cpp".format(templatefile_prefix),
813 task_name,
814 namespace + ["tasks"],
815 subdirectory + "tasks",
816 implementationDictionary,
817 True,
818 )
819 )
820
821 output.add(generated_solver_files)
822 output.makefile.add_h_file(subdirectory + "tasks/" + task_name + ".h", generated=True)
823 output.makefile.add_cpp_file(subdirectory + "tasks/" + task_name + ".cpp", generated=True)
824
825
827 return "{}EnclaveTask".format(self._name_name)
828
829
831 self,
832 cell_data_storage: Storage,
833 face_data_storage: Storage,
834 ):
835 if cell_data_storage == Storage.SmartPointers:
837 else:
839
840 super(EnclaveTasking, self).switch_storage_scheme(
841 cell_data_storage,
842 face_data_storage
843 )
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...
__init__(self, solver)
solver: ADERDG Reference to creating class
get_includes(self)
Return include statements that you need.
__init__(self, solver)
patch: peano4.datamodel.Patch Patch which is to be used
_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
_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
_action_set_couple_resolution_transitions_and_handle_dynamic_mesh_refinement
Definition FV.py:283
_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:1329
create_action_sets(self)
Create all the action sets.
Definition FV.py:730
_action_set_AMR_commit_without_further_analysis
Definition FV.py:278
_store_cell_data_default_guard(self)
Extend the guard via ands only.
Definition FV.py:912
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
add_entries_to_text_replacement_dictionary(self, d)
Definition FV.py:1234
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
_action_set_roll_over_update_of_faces
Definition FV.py:281