Peano
Loading...
Searching...
No Matches
FV.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
3import os
4from abc import abstractmethod
5
6import jinja2
7
8import peano4.datamodel
13
15
16from exahype2.solvers.Storage import Storage
17
18class FV(object):
19 """!
20 Abstract finite volume solver step sizes that works on patch-based AMR with a halo layer of one.
21
22 FV is the base class of all FV solvers. It defines what kind of action sets do
23 exist, i.e., what in principle can be done while we run through the grid. It also
24 provides some kind of very basic infrastructure, i.e., what the name of a solver
25 is, what data is to be held per face or cell, or where in the multiscale mesh
26 we actually have to hold data.
27
28 The FV class cannot/should not be instantiated. There are two direct children:
29 SingleSweep and EnclaveTasking. The FV base class defines what data are available.
30 The two subclasses define how we run through the mesh: once per time step or
31 twice per time step with some tasking. So FV defines what can be done, the
32 subclasses define how the steps are orchestrated. Again, they do not (yet) say
33 what is computed. That's then the responsibility of classes that inherit from
34 SingleSweep or EnclaveTasking, respectively.
35
36 A finite volume solver in ExaHyPE 2 (actually any solver) is first of all a
37 static collection of data logically tied to grid entities and some operations
38 over the mesh that are just there. All data and action sets however have guards,
39 i.e., boolean guards that define
40
41 - are data to be stored in-between traversals,
42 - are action sets really invoked in a particular traversal or can they be
43 skipped.
44
45 In the baseline FV class, these guards are usually set to default values.
46 That is, all action sets per time step are invoked always (the guard is
47 true) and all data are always stored on the finest mesh. If you want to alter
48 this behaviour, i.e., store data not always or skip steps, you can overwrite
49 the corresponding attributes of the attributes, i.e., the guards of the
50 associated data.
51
52 See the discussion on "Control flow between this class and subclasses" below.
53
54 ## Parallelisation
55
56 I do equip both Q and NewQ with proper merge routines. However, all merge guards
57 set to "never" by default. If you need some data exchange, you have to activate
58 them manually.
59
60 ## Control flow between this class and subclasses
61
62 There are three key routines: the constructor, create_data_structures() and
63 create_action_sets(). The constructor sets some global variables (such as the
64 name) and then invokes the other two routines.
65
66 create_data_structures() establishes all the data structures tied to the
67 grid entities. It also sets some properties of these data such as the patch
68 size, e.g. If you want to add additional data (such as additional quantities
69 per cell) or if you want to alter the configuration of data tied to grid
70 entities, you should redefine create_data_structures(). However, any
71 subclass still should call FV's create_data_structures() - or the create_data_structures()
72 of the SingleSweep or EnclaveTasking, respectively. This will ensure that the
73 baseline configuration of all data is in place. After that, you can modify
74 the properties.
75
76 create_action_sets() establishes the action sets, i.e. activities that are to
77 be triggered whenever you run a time step, you plot, you initialise the grid.
78
79 Both create_data_structures() and create_action_sets() add attributes to the
80 FV class. See self._patch for example within create_action_sets(). These
81 attributes have guards such as self._action_set_initial_conditions.guard.
82 These guards are set to defaults in FV. It is basically the job of SingleSweep
83 or EnclaveTasking - they determine when which data are used - to reset these
84 guards from a default to something tailored to the particular data flow.
85
86 If you want to redefine when data is stored or operations are invoked, overwrite
87 create_data_structures(), and call the superclass, i.e. either SingleSweep or
88 EnclaveTasking. This is what happens:
89
90 - SingleSweep or EnclaveTasking pass on the call to FV.create_data_structures().
91 This call ensures that all the data structures are in place. Then it returns.
92 - SingleSweep's or EnclaveTasking's create_data_structures() then sets the
93 guard, i.e. they configure when data is stored or used.
94 - Finally, your own overwrite of create_data_structures() can augment the
95 data structures (which are now in place and called at the right time) with
96 information what it actually shall do.
97
98 ## Adaptive mesh refinement (AMR)
99
100 We use by default a linear interpolation and averaging. For the linear interpolation,
101 I do not compute the operators on demand. Instead, I use the optimised scheme which
102 computes the operators once and then reuses them as static operation.
103
104 If you wanna alter the inter-resolution transfer operators, please use
105
106 self._interpolation = "tensor_product< " + self._name + ">"
107 self.add_solver_constants( "" "static constexpr double NormalInterpolationMatrix1d[] = {
108 1.0, 0.0
109 };
110 "" " )
111 self.add_solver_constants( "" "static constexpr double TangentialInterpolationMatrix1d[] = {
112 1.0,0.0,0.0,0.0,
113 1.0,0.0,0.0,0.0,
114 1.0,0.0,0.0,0.0,
115 0.0,1.0,0.0,0.0,
116
117 0.0,1.0,0.0,0.0,
118 0.0,1.0,0.0,0.0,
119 0.0,0.0,1.0,0.0,
120 0.0,0.0,1.0,0.0,
121
122 0.0,0.0,1.0,0.0,
123 0.0,0.0,0.0,1.0,
124 0.0,0.0,0.0,1.0,
125 0.0,0.0,0.0,1.0
126 };"" " )
127
128 The above snippet refers to a overlap of one and five unknowns. So the interpolation
129 along a 1d tangential direction is three 5x5 matrices. Along the normal, we have to
130 project onto one target element, so our projection matrix has one row. We have two
131 values (inside and outside) along this normal, so two columns. In the standard
132 enumeration scheme (refers to the face with normal 0), we have one real coarse
133 grid value in this case. The other one would be the restricted value. We don't
134 often use it (and therefore have a 0 here).
135
136 ## Data postprocessing or coupling to other codes
137
138 For data postprocessing, the introduction of fancy interior conditions or the coupling to
139 other codes, each Finite Volume solver has an attribute
140
141 _action_set_postprocess_solution
142
143 You can use this attribute to add postprocessing routines to the code.
144 """
145
147 self,
148 name,
149 patch_size,
150 overlap,
151 unknowns,
152 auxiliary_variables,
153 min_volume_h,
154 max_volume_h,
155 plot_grid_properties,
156 pde_terms_without_state: bool,
157 kernel_namespace,
158 baseline_action_set_descend_invocation_order=0,
159 ):
160 """!
161 Create solver
162
163 name: string
164 A unique name for the solver. This one will be used for all generated
165 classes. Also the C++ object instance later on will incorporate this
166 name.
167
168 patch_size: int
169 Size of the patch in one dimension. All stuff here's dimension-generic.
170
171 overlap: int
172 That's the size of the halo layer which is half of the overlap with a
173 neighbour. A value of 1 means that a patch_size x patch_size patch in
174 2d is surrounded by one additional cell layer. The overlap has to be
175 bigger or equal to one. It has to be smaller or equal to patch_size.
176
177 unknowns: int
178 Number of unknowns per Finite Volume voxel.
179
180 auxiliary_variables: int
181 Number of auxiliary variables per Finite Volume voxel. Eventually, both
182 unknowns and auxiliary_variables are merged into one big vector if we
183 work with AoS. But the solver has to be able to distinguish them, as
184 only the unknowns are subject to a hyperbolic formulation.
185
186 min_volume_h: double
187 This size refers to the individual Finite Volume.
188
189 max_volume_h: double
190 This size refers to the individual Finite Volume.
191
192 plot_grid_properties: Boolean
193 Clarifies whether a dump of the data should be enriched with grid info
194 (such as enclave status flags), too.
195 """
196 self._name = name
197
198 self._min_volume_h = min_volume_h
199 self._max_volume_h = max_volume_h
200 self._plot_grid_properties = plot_grid_properties
201
202 self._patch_size = patch_size
203 self._overlap = overlap
204
205 """
206 self._unknowns and self._auxiliary_variables respectively hold the number of unknowns and
207 auxiliary variables in the equation to be computed. Unknowns are variables that change over
208 time whereas auxiliary variables can be space-dependent but don't vary over time.
209 These can be specified either as simple ints or by a dictionary
210 (e.g.) unknowns = {'a': 1, 'b': 1, 'c': 3}
211 in which the user specifies the multiplicity of the variable (the velocity has one component
212 per dimension for example.)
213 If they are specified by a dictionary then the code generates a "VariableShortcuts" file which
214 allows the user to specify a variable by name and automatically maps this to the right position
215 in an array for better legibility. Otherwise they must manually remember the position of each
216 variable manually.
217
218 use_var_shortcut is used to know whether or not the user passed their variables via a dict
219 variable_names and variable_pos are used internally to remember the names and respective
220 positions of variables if set by a dictionary.
221 """
222 self._use_var_shortcut = False
224 self._variable_pos = [0]
225
226 if type(unknowns) is dict:
227 self._unknowns = sum(unknowns.values())
228 self._variable_names += list(unknowns.keys())
229 for var in list(unknowns.values()):
230 self._variable_pos.append(var + self._variable_pos[-1])
231 self._use_var_shortcut = True
232 elif type(unknowns) is int:
233 self._unknowns = unknowns
234 else:
235 raise Exception(
236 "Not a valid type for parameter unknowns, needs to be int or dictionary."
237 )
238
239 if type(auxiliary_variables) is dict:
240 self._auxiliary_variables = sum(auxiliary_variables.values())
241 self._variable_names += list(auxiliary_variables.keys())
242 for var in list(auxiliary_variables.values()):
243 self._variable_pos.append(var + self._variable_pos[-1])
244 self._use_var_shortcut = True
245 elif type(auxiliary_variables) is int:
246 self._auxiliary_variables = auxiliary_variables
247 else:
248 raise Exception(
249 "Not a valid type for parameter auxiliary_variables, needs to be int or dictionary."
250 )
251
255
256 if min_volume_h > max_volume_h:
257 raise Exception(
258 "min_volume_h ("
259 + str(min_volume_h)
260 + ") is bigger than max_volume_h ("
261 + str(max_volume_h)
262 + ")"
263 )
264
266 peano4.toolbox.blockstructured.ReconstructedArrayMemoryLocation.CallStack
267 )
268
270
273
276 self._action_set_AMR = None
284 None
285 )
288
289 self._compute_time_step_size = "#error Not yet defined"
290 self._compute_new_time_step_size = "#error Not yet defined"
293
294 self._compute_kernel_call = "#error Not yet defined"
295 self._compute_kernel_call_stateless = "#error Not yet defined"
296 self._pde_terms_without_state = pde_terms_without_state
297
303
307
311
312 self._interpolation = "piecewise_constant"
313 self._restriction = "averaging"
314
315 self._kernel_namespace = kernel_namespace
316
317 self._baseline_action_set_descend_invocation_order = baseline_action_set_descend_invocation_order
318
319 self.switch_storage_scheme(Storage.SmartPointers, Storage.SmartPointers)
320
321
322 def __str__(self):
323 result = (
324 """
325Name: """
326 + self._name
327 + """
328Type: """
329 + self.__class__.__module__
330 + """
331Stateless PDE terms: """
332 + str(self._pde_terms_without_state)
333 + """
334Storage schemes: """
335 + str(self._cell_data_storage) + ", " + str(self._face_data_storage)
336 + """
337Patch size: """
338 + str(self._patch_size)
339 + """
340Overlap: """
341 + str(self._overlap)
342 + """
343Unknowns: """
344 + str(self._unknowns)
345 + """
346Auxiliary variables: """
347 + str(self._auxiliary_variables)
348 + """
349h_volume_min: """
350 + str(self._min_volume_h)
351 + """
352h_volume_max: """
353 + str(self._max_volume_h)
354 + """
355Initial conditions: """
357 + """
358Boundary conditions: """
360 + """
361Refinement criterion: """
363 + """
364Eigenvalues: """
366 + """
367Flux: """
369 + """
370NCP: """
372 + """
373Source term: """
375 + """
376"""
377 )
378 return result
379
380 __repr__ = __str__
381
382
384 coarsest_tree_level = 0
385 while (
386 domain_size * 3 ** (-coarsest_tree_level) / self._patch_size
387 > self._max_volume_h
388 ):
389 coarsest_tree_level += 1
390 return coarsest_tree_level
391
392
394 finest_tree_level = 0
395 while (
396 domain_size * 3 ** (-finest_tree_level) / self._patch_size
397 > self._min_volume_h
398 ):
399 finest_tree_level += 1
400 return finest_tree_level
401
402 # self._overlap = overlap
403 # self._unknowns = unknowns
404 # self._auxiliary_variables = auxiliary_variables
405
406 def get_coarsest_number_of_patches(self, domain_size):
407 return 3 ** self.get_min_number_of_spacetree_levels(domain_size)
408
409
410 def get_finest_number_of_patches(self, domain_size):
411 return 3 ** self.get_max_number_of_spacetree_levels(domain_size)
412
413
415 return self.get_coarsest_number_of_patches(domain_size) * self._patch_size
416
417
419 return self.get_finest_number_of_patches(domain_size) * self._patch_size
420
421
422 def get_coarsest_volume_size(self, domain_size):
423 return domain_size / self.get_coarsest_number_of_finite_volumes(domain_size)
424
425
426 def get_finest_volume_size(self, domain_size):
427 return domain_size / self.get_finest_number_of_finite_volumes(domain_size)
428
429
430 def create_readme_descriptor(self, domain_offset, domain_size):
431 return (
432 """
433### ExaHyPE 2 solver
434
435"""
436 + str(self)
437 + """
438
439Real type of this solver: """
440 + str(type(self))
441 + """
442
443We assume that you use a domain size of (0,"""
444 + str(domain_size)
445 + """)^d. Peano 4 will cut this domain equidistantly
446and recursively into three parts along each coordinate axis. This yields a spacetree.
447
448The spacetree will at least have """
449 + str(self.get_min_number_of_spacetree_levels(domain_size))
450 + """ levels.
451The spacetree will at most have """
452 + str(self.get_max_number_of_spacetree_levels(domain_size))
453 + """ levels.
454
455The spacetree will thus span at least """
456 + str(self.get_coarsest_number_of_patches(domain_size))
457 + """ cells (or patches) per coordinate axis. This means a """
458 + str(self.get_coarsest_number_of_patches(domain_size))
459 + """^d grid of patches.
460The spacetree will thus span at most """
461 + str(self.get_finest_number_of_patches(domain_size))
462 + """ cells (or patches) per coordinate axis. This means a """
463 + str(self.get_finest_number_of_patches(domain_size))
464 + """^d grid of patches.
465
466ExaHyPE 2 embeds """
467 + str(self._patch_size)
468 + """^d patches of Finite Volumes into the finest tree level.
469
470The coarsest possible mesh will consist of """
471 + str(self.get_coarsest_number_of_finite_volumes(domain_size))
472 + """ Finite Volumes per coordinate axis.
473The finest possible mesh will consist of """
474 + str(self.get_finest_number_of_finite_volumes(domain_size))
475 + """ Finite Volumes per coordinate axis.
476
477The coarsest mesh width of """
478 + str(self.get_coarsest_volume_size(domain_size))
479 + """ is thus just smaller than the maximum volume size """
480 + str(self._max_volume_h)
481 + """.
482The finest mesh width of """
483 + str(self.get_finest_volume_size(domain_size))
484 + """ is thus just smaller than the minimum volume size """
485 + str(self._min_volume_h)
486 + """.
487"""
488 )
489
490
491 @property
493 """
494 Add further includes to this property, if your action sets require some additional
495 routines from other header files.
496 """
497 return self._user_action_set_includes
498
499
500 @property
502 """
503 Add further includes to this property, if your solver requires some additional
504 routines from other header files.
505 """
506 return self._user_solver_includes
507
508
510 """
511 Add further includes to this property, if your action sets require some additional
512 routines from other header files.
513 """
514 self._user_action_set_includes += value
515
516
517 def add_user_solver_includes(self, value):
518 """
519 Add further includes to this property, if your solver requires some additional
520 routines from other header files.
521 """
522 self._user_solver_includes += value
523
524
525 @abstractmethod
527 """!
528
529 Create data structures required by all Finite Volume solvers
530
531 Recall in subclasses if you wanna change the number of unknowns
532 or auxiliary variables. See class description's subsection on
533 data flow.
534
535 :: Call order and ownership
536
537 This operation can be called multiple times. However, only the very
538 last call matters. All previous calls are wiped out.
539
540 If you have a hierarchy of solvers, every create_data_structures()
541 should first(!) call its parent version. This way, you always ensure
542 that all data are in place before you continue to alter the more
543 specialised versions. So it is (logically) a top-down (general to
544 specialised) run through all create_data_structures() variants
545 within the inheritance tree.
546
547 :: Arguments
548
549 _patch: Patch (NxNxN)
550 Actual patch data. We use Finite Volumes, so this is
551 always the current snapshot, i.e. the valid data at one point.
552
553 _patch_overlap_old, _patch_overlap_new: Patch (2xNxN)
554 This is a copy/excerpt from the two adjacent finite volume
555 snapshots plus the old data as backup. If I want to implement
556 local timestepping, I don't have to backup the whole patch
557 (see _patch), but I need a backup of the face data to be able
558 to interpolate in time.
559
560 _patch_overlap_update: Patch (2xNxN)
561 This is the new update. After the time step, I roll this
562 information over into _patch_overlap_new, while I backup the
563 previous _patch_overlap_new into _patch_overlap_old. If I
564 worked with regular meshes only, I would not need this update
565 field and could work directly with _patch_overlap_new. However,
566 AMR requires me to accumulate data within new while I need
567 the new and old data temporarily. Therefore, I employ this
568 accumulation/roll-over data which usually is not stored
569 persistently.
570 """
572 (self._patch_size, self._patch_size, self._patch_size),
574 self._unknown_identifier(),
575 )
577 (2 * self._overlap, self._patch_size, self._patch_size),
579 self._unknown_identifier() + "Old",
580 )
582 (2 * self._overlap, self._patch_size, self._patch_size),
584 self._unknown_identifier() + "New",
585 )
587 (2 * self._overlap, self._patch_size, self._patch_size),
589 self._unknown_identifier() + "Update",
590 )
591
592 if self._cell_data_storage == Storage.CallStack:
594 self._patch, "double"
595 )
596 elif self._cell_data_storage == Storage.Heap:
598 self._patch, "double"
599 )
600 elif self._cell_data_storage == Storage.SmartPointers:
602 self._patch, "double"
603 )
604 else:
605 assert False, "Storage variant {} not supported".format(
607 )
608
609 if self._face_data_storage == Storage.CallStack:
611 self._patch_overlap_old, "double"
612 )
614 self._patch_overlap_new, "double"
615 )
617 self._patch_overlap_update, "double"
618 )
619 elif self._face_data_storage == Storage.Heap:
620 self._patch_overlap_old.generator = (
622 self._patch_overlap_old, "double"
623 )
624 )
625 self._patch_overlap_new.generator = (
627 self._patch_overlap_new, "double"
628 )
629 )
630 self._patch_overlap_update.generator = (
632 self._patch_overlap_update, "double"
633 )
634 )
635 elif self._face_data_storage == Storage.SmartPointers:
636 self._patch_overlap_old.generator = (
638 self._patch_overlap_old, "double"
639 )
640 )
641 self._patch_overlap_new.generator = (
643 self._patch_overlap_new, "double"
644 )
645 )
646 self._patch_overlap_update.generator = (
648 self._patch_overlap_update, "double"
649 )
650 )
651 else:
652 assert False, "Storage variant {} not supported".format(
654 )
655
656 self._patch_overlap_old.generator.merge_method_definition = (
657 peano4.toolbox.blockstructured.get_face_merge_implementation(
659 )
660 )
661 self._patch_overlap_new.generator.merge_method_definition = (
662 peano4.toolbox.blockstructured.get_face_merge_implementation(
664 )
665 )
666 self._patch.generator.merge_method_definition = (
667 peano4.toolbox.blockstructured.get_cell_merge_implementation(self._patch)
668 )
669
670 self._patch_overlap_old.generator.includes += """
671#include "peano4/utils/Loop.h"
672#include "repositories/SolverRepository.h"
673"""
674 self._patch_overlap_new.generator.includes += """
675#include "peano4/utils/Loop.h"
676#include "repositories/SolverRepository.h"
677"""
678
679 self._patch.generator.load_store_compute_flag = "::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
683 )
684
685 self._patch_overlap_old.generator.send_condition = "false"
686 self._patch_overlap_old.generator.receive_and_merge_condition = "false"
687
688 self._patch_overlap_new.generator.send_condition = "false"
689 self._patch_overlap_new.generator.receive_and_merge_condition = "false"
690
691 self._patch_overlap_update.generator.send_condition = "false"
692 self._patch_overlap_update.generator.receive_and_merge_condition = "false"
693
694 self._patch_overlap_old.generator.load_store_compute_flag = "::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
698 )
699
700 self._patch_overlap_new.generator.load_store_compute_flag = "::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
704 )
705
706 self._patch_overlap_update.generator.load_store_compute_flag = "::peano4::grid::constructLoadStoreComputeFlag({},{},{})".format(
708 "false",
709 "false"
710 )
711
712 self._patch.generator.includes += """
713#include "../repositories/SolverRepository.h"
714"""
715 self._patch_overlap_old.generator.includes += """
716#include "../repositories/SolverRepository.h"
717"""
718 self._patch_overlap_new.generator.includes += """
719#include "../repositories/SolverRepository.h"
720"""
721 self._patch_overlap_update.generator.includes += """
722#include "../repositories/SolverRepository.h"
723"""
724
725 self._cell_label = exahype2.grid.create_cell_label(self._name)
726 self._face_label = exahype2.grid.create_face_label(self._name)
727
728
729 @abstractmethod
731 """!
732 Create all the action sets
733
734 Overwrite in subclasses if you wanna create different
735 action sets. Subclasses also might redefine the invocation order.
736
737 ## Call order and ownership
738
739 This operation can be called multiple times. However, only the very
740 last call matters. All previous calls are wiped out.
741
742 If you have a hierarchy of solvers, every create_data_structures()
743 should first(!) call its parent version. This way, you always ensure
744 that all data are in place before you continue to alter the more
745 specialised versions. So it is (logically) a top-down (general to
746 specialised) run through all create_data_structures() variants
747 within the inheritance tree.
748
749 ## Recreation vs backup (state discussion)
750
751 We faced some issues with action sets that should not be
752 overwritten. For example, the postprocessing should not be overwritten
753 as users might want to set it and then later on reset the number of
754 unknowns, e.g. In this case, you would loose your postprocessing if
755 create_action_sets() recreated them. So I decided to make an exception
756 here: the postprocessing step is never overwritten by the standard
757 classes.
758
759 There are further action sets which have a state, which users might
760 want to alter. The most prominent one is the AMR routines, where users
761 often alter the interpolation and restriction scheme. Here, things are
762 tricky: If we keep the default one, the action set would not pick up
763 if you changed the number of unknowns, e.g. However, if we recreated
764 the action set, we'd miss out on any changed interpolation/restriction
765 scheme. Therefore, I have to hold the interpolation and restriction
766 scheme separate.
767 """
770 self, self._store_cell_data_default_guard(), "true"
771 )
772 )
775 self, self._store_cell_data_default_guard(), "false"
776 )
777 )
779 solver=self,
781 build_up_new_refinement_instructions=True,
782 implement_previous_refinement_instructions=True,
783 called_by_grid_construction=True,
784 )
787 solver=self,
789 build_up_new_refinement_instructions=False,
790 implement_previous_refinement_instructions=True,
791 called_by_grid_construction=True,
792 )
793 )
795 solver=self,
797 build_up_new_refinement_instructions=True,
798 implement_previous_refinement_instructions=True,
799 called_by_grid_construction=False,
800 )
804 )
805 )
809 )
810 )
814 )
815 )
820 False,
823 )
824 )
826 solver=self,
827 interpolation=self._interpolation,
828 restriction=self._restriction,
829 )
830
831 # This one is different: it is the hook-in point for other solvers, so we create it
832 # only once if it does not exist.
833 if self._action_set_postprocess_solution == None:
836 )
837 if self._action_set_preprocess_solution == None:
840 )
841
844
846
847 self._action_set_initial_conditions.descend_invocation_order = (
849 )
850 self._action_set_initial_conditions_for_grid_construction.descend_invocation_order = (
852 )
853 self._action_set_AMR.descend_invocation_order = self._baseline_action_set_descend_invocation_order + 2
855 self._action_set_AMR_commit_without_further_analysis.descend_invocation_order = (
857 )
858 self._action_set_handle_boundary.descend_invocation_order = (
860 )
861 self._action_set_project_patch_onto_faces.descend_invocation_order = (
863 ) # touch cell last time, i.e. leave cell
864 self._action_set_roll_over_update_of_faces.descend_invocation_order = (
866 )
867 self._action_set_copy_new_faces_onto_old_faces.descend_invocation_order = (
869 )
872 )
873 self._action_set_postprocess_solution.descend_invocation_order = (
875 ) # touch cell last time, i.e. leave cell
876 self._action_set_preprocess_solution.descend_invocation_order = (
878 )
879
880
882 """! Default logic when to create cell data or not
883
884 ExaHyPE allows you to define when you want to load or store data, and
885 you can also define if you need some grid data for your computations.
886 You might have some temporary data per cell which is neither stored
887 nor loaded, but that has to be there while you compute. Or there might
888 be cells which don't need to store a particular piece of data. See
889 ::peano4::grid::LoadStoreComputeFlag for some details. This predicate
890 here is a default. Particular solvers will take it as a starting point
891 to make more detailed rules: an enclave solver for example might
892 decide to take the information where to store data, but to toggle the
893 storage on and off depending on the solver phase.
894 """
895 return "not marker.willBeRefined() and repositories::{}.getSolverState()!={}::SolverState::GridConstruction".format(
897 self._name
898 )
899
900
902 return (
903 "not marker.willBeRefined() "
904 + "and repositories::"
906 + ".getSolverState()!="
907 + self._name
908 + "::SolverState::GridConstruction"
909 )
910
911
913 """!
914 Extend the guard via ands only. Never use an or, as subclasses might
915 extend it as well, and they will append further ends.
916 """
917 return (
918 "not marker.willBeRefined() "
919 + "and repositories::"
921 + ".getSolverState()!="
922 + self._name
923 + "::SolverState::GridConstruction"
924 )
925
926
928 """!
929 Extend the guard via ands only. Never use an or, as subclasses might
930 extend it as well, and they will append further ends.
931 """
932 return (
933 "not marker.hasBeenRefined() "
934 + "and repositories::"
936 + ".getSolverState()!="
937 + self._name
938 + "::SolverState::GridConstruction "
939 + "and repositories::"
941 + ".getSolverState()!="
942 + self._name
943 + "::SolverState::GridInitialisation"
944 )
945
946
948 """!
949 Extend the guard via ands only. Never use an or, as subclasses might
950 extend it as well, and they will append further ends.
951 """
952 return (
953 "not marker.willBeRefined() "
954 + "and repositories::"
956 + ".getSolverState()!="
957 + self._name
958 + "::SolverState::GridConstruction"
959 )
960
961
963 """!
964 Extend the guard via ands only. Never use an or, as subclasses might
965 extend it as well, and they will append further ends.
966 """
967 return (
968 "not marker.hasBeenRefined() "
969 + "and repositories::"
971 + ".getSolverState()!="
972 + self._name
973 + "::SolverState::GridConstruction "
974 + "and repositories::"
976 + ".getSolverState()!="
977 + self._name
978 + "::SolverState::GridInitialisation"
979 )
980
981
983 return self._name + "Q"
984
985
987 return "instanceOf" + self._name
988
989
990 def add_to_Peano4_datamodel(self, datamodel, verbose):
991 """
992 Add all required data to the Peano project's datamodel
993 so it is properly built up.
994 """
995 if verbose:
996 print("Patch data")
997 print("----------")
998 print(str(self._patch))
999 print("Patch overlap data")
1000 print("----------")
1001 print(str(self._patch_overlap_old))
1002 print("Patch overlap data")
1003 print("----------")
1004 print(str(self._patch_overlap_new))
1005 datamodel.add_cell(self._cell_label)
1006 datamodel.add_cell(self._patch)
1007 datamodel.add_face(self._patch_overlap_old)
1008 datamodel.add_face(self._patch_overlap_new)
1009 datamodel.add_face(self._patch_overlap_update)
1010 datamodel.add_face(self._face_label)
1011
1012
1014 """
1015 Tell Peano what data to move around
1016
1017 Inform Peano4 step which data are to be moved around via the
1018 use_cell and use_face commands. This operation is generic from
1019 ExaHyPE's point of view, i.e. I use it for all grid sweep types.
1020
1021 It is important that the cell label comes first, as some other
1022 data might make their load/store decisions depend on the label.
1023 """
1024 step.use_cell(self._patch)
1025 step.use_cell(self._cell_label)
1026 step.use_face(self._patch_overlap_old)
1027 step.use_face(self._patch_overlap_new)
1028 step.use_face(self._patch_overlap_update)
1029 step.use_face(self._face_label)
1030
1031
1033 return """
1034#include "tarch/la/Vector.h"
1035
1036#include "peano4/utils/Globals.h"
1037#include "peano4/utils/Loop.h"
1038
1039#include "repositories/SolverRepository.h"
1040"""
1041
1042
1043 def add_actions_to_init_grid(self, step, restart_from_checkpoint=False):
1044 """!
1045 Add all the action sets to init grid
1046
1047 The AMR stuff has to be the very first thing. Actually, the AMR routines'
1048 interpolation doesn't play any role here. But the restriction indeed is
1049 very important, as we have to get the face data for BCs et al. The action
1050 set order is inverted while we ascend within the tree again. Therefore, we
1051 add the AMR action set first which means it will be called last when we go
1052 from fine to coarse levels within the tree.
1053
1054 ## Ordering
1055
1056 The order of the action sets is preserved throughout the steps down within
1057 the tree hierarchy. It is inverted throughout the backrolling.
1058
1059 This is what we want to achieve:
1060
1061 - Project solution onto faces. This happens in touchCellLastTime(). See
1062 exahype2.solvers.fv.actionsets.ProjectPatchOntoFaces for comments.
1063 The project will end up in QUpdate.
1064 - Roll updates over on the faces from QUpdate into Q_new. This is done
1065 by RollOverUpdateFace, which requires in turn that the getUpdated()
1066 flag is set. As the roll-over plugs into touchFaceLastTime(), it will
1067 always be called after the projection, since the latter is a cell
1068 operation.
1069 - Copy new face data Q_new into old face data Q_old, as this is the
1070 initial sweep, i.e. the old face data otherwise might hold rubbish.
1071 This is a backup operation realised through the action set
1072 BackupPatchOverlap. This one plugs into touchFaceLastTime() too.
1073 Therefore, it is important that its priority is smaller than the one
1074 of the roll-over, since we invert the call order for the touch-last
1075 events.
1076 - Restrict the data to the coarser level if we are on a hanging face.
1077 - The postprocessing is the very last thing. It does not matter if it
1078 plugs into touchCellFirstTime() or touchCellLastTime(). As we invert the
1079 order when we backtrace, the postprocessing will have completed before
1080 we invoke _action_set_project_patch_onto_faces, i.e. any update will
1081 be projected onto the faces.
1082 """
1083 assert (
1084 self._action_set_copy_new_faces_onto_old_faces.descend_invocation_order
1085 < self._action_set_roll_over_update_of_faces.descend_invocation_order
1086 )
1087
1088 if restart_from_checkpoint:
1091 self, self._store_cell_data_default_guard(), "true", restart_from_checkpoint=True
1092 )
1093 )
1094
1095 step.add_action_set(self._action_set_preprocess_solution)
1096 step.add_action_set(
1098 )
1099 step.add_action_set(self._action_set_copy_new_faces_onto_old_faces)
1100 step.add_action_set(self._action_set_roll_over_update_of_faces)
1101 step.add_action_set(self._action_set_initial_conditions)
1102 step.add_action_set(self._action_set_project_patch_onto_faces)
1103 step.add_action_set(self._action_set_update_face_label)
1104 step.add_action_set(self._action_set_update_cell_label)
1105 step.add_action_set(self._action_set_AMR)
1106 step.add_action_set(self._action_set_postprocess_solution)
1107
1108
1109 def add_actions_to_create_grid(self, step, evaluate_refinement_criterion):
1110 """
1111 The boundary information is set only once. It is therefore important that
1112 we ues the face label and initialise it properly.
1113 """
1115 step.add_action_set(self._action_set_update_face_label)
1116 step.add_action_set(self._action_set_update_cell_label)
1117 if evaluate_refinement_criterion:
1118 step.add_action_set(self._action_set_AMR_throughout_grid_construction)
1119 else:
1120 step.add_action_set(self._action_set_AMR_commit_without_further_analysis)
1121
1122
1123 @property
1125 self,
1126 ):
1127 return self._plot_description
1128
1129
1130 @plot_description.setter
1131 def plot_description(self, description):
1132 """!
1133 Add a proper description to the plots
1134
1135 Use this one to set a description within the output patch file that tells
1136 the solver what the semantics of the entries are. Typically, I use
1137 a comma-separated list here.
1138 """
1139 self._plot_description = description
1140
1141
1142 def add_actions_to_plot_solution(self, step, output_path, restart_from_checkpoint=False):
1143 """!
1144 Add action sets to plot solution step
1145
1146 Consult the discussion in add_actions_to_init_grid() around the order
1147 of the individual action sets.
1148
1149 It is important that we have the coupling/dynamic AMR part in here, as
1150 there might be pending AMR refinement requests that now are realised.
1151 For the same reason, we need the update of the face label and the update
1152 of the cell label in here: The AMR might just propagate over into the
1153 plotting, i.e. we might create new grid entities throughout the plot.
1154 These entities (faces and cells) have to be initialised properly.
1155 Otherwise, their un-initialised data will propagate through to the next
1156 time step.
1157
1158 To make the restriction work, we have to project the solutions onto the
1159 faces.
1160 """
1161 d = {}
1164
1165 step.add_action_set(
1167 )
1168 step.add_action_set(self._action_set_roll_over_update_of_faces)
1169 step.add_action_set(self._action_set_update_face_label)
1170 step.add_action_set(self._action_set_update_cell_label)
1171 step.add_action_set(self._action_set_project_patch_onto_faces)
1172 # step.add_action_set( self._action_set_AMR_commit_without_further_analysis )
1173
1175 filename=output_path + "solution-" + self._name,
1176 patch=self._patch,
1177 dataset_name=self._unknown_identifier(),
1178 description=self._plot_description,
1179 guard="repositories::plotFilter.plotPatch(marker) and "
1181 additional_includes="""
1182#include "exahype2/PlotFilter.h"
1183#include "../repositories/SolverRepository.h"
1184""",
1185 precision="PlotterPrecision",
1186 time_stamp_evaluation="0.5*(repositories::getMinTimeStamp()+repositories::getMaxTimeStamp())",
1187 select_dofs=self.select_dofs_to_print,
1188 restart_preprocess=restart_from_checkpoint
1189 )
1190 plot_patches_action_set.descend_invocation_order = self._baseline_action_set_descend_invocation_order
1191 step.add_action_set(plot_patches_action_set)
1192
1193 if self._plot_grid_properties:
1194 plot_grid_properties_action_set = peano4.toolbox.PlotGridInPeanoBlockFormat(
1195 filename=output_path + "grid-" + self._name,
1196 cell_unknown=None,
1197 guard="repositories::plotFilter.plotPatch(marker) and "
1199 additional_includes="""
1200#include "exahype2/PlotFilter.h"
1201#include "../repositories/SolverRepository.h"
1202""",
1203 )
1204 plot_grid_properties_action_set.descend_invocation_order = (
1206 )
1207 step.add_action_set(plot_grid_properties_action_set)
1208 pass
1209
1210 def add_actions_to_checkpoint_solution(self, step, output_path, restart_from_checkpoint=False):
1211 """!
1212 Add action sets to plot solution step
1213
1214 Consult the discussion in add_actions_to_init_grid() around the order
1215 of the individual action sets.
1216
1217 It is important that we have the coupling/dynamic AMR part in here, as
1218 there might be pending AMR refinement requests that now are realised.
1219 For the same reason, we need the update of the face label and the update
1220 of the cell label in here: The AMR might just propagate over into the
1221 plotting, i.e. we might create new grid entities throughout the plot.
1222 These entities (faces and cells) have to be initialised properly.
1223 Otherwise, their un-initialised data will propagate through to the next
1224 time step.
1225
1226 To make the restriction work, we have to project the solutions onto the
1227 faces.
1228 """
1229 d = {}
1232
1233 step.add_action_set(
1235 )
1236 step.add_action_set(self._action_set_roll_over_update_of_faces)
1237 step.add_action_set(self._action_set_update_face_label)
1238 step.add_action_set(self._action_set_update_cell_label)
1239 step.add_action_set(self._action_set_project_patch_onto_faces)
1240 # step.add_action_set( self._action_set_AMR_commit_without_further_analysis )
1241
1243 filename=output_path + "checkpoint-" + self._name,
1244 patch=self._patch,
1245 dataset_name=self._unknown_identifier(),
1246 description=self._plot_description,
1247 guard=self._load_cell_data_default_guard(),
1248 additional_includes="""
1249#include "../repositories/SolverRepository.h"
1250""",
1251 precision="PlotterPrecision",
1252 time_stamp_evaluation="0.5*(repositories::getMinTimeStamp()+repositories::getMaxTimeStamp())",
1253 select_dofs=None,
1254 restart_preprocess=restart_from_checkpoint
1255 )
1256 plot_patches_action_set.descend_invocation_order = self._baseline_action_set_descend_invocation_order
1257 step.add_action_set(plot_patches_action_set)
1258
1259 if self._plot_grid_properties:
1260 plot_grid_properties_action_set = peano4.toolbox.PlotGridInPeanoBlockFormat(
1261 filename=output_path + "grid-" + self._name,
1262 cell_unknown=None,
1263 guard=self._load_cell_data_default_guard(),
1264 additional_includes="""
1265#include "../repositories/SolverRepository.h"
1266""",
1267 )
1268 plot_grid_properties_action_set.descend_invocation_order = (
1270 )
1271 step.add_action_set(plot_grid_properties_action_set)
1272 pass
1273
1275 """!
1276 Add action sets to time step
1277
1278 See exahype2.solvers.fv.FV.add_actions_to_init_grid() for details on the
1279 ordering of the action sets.
1280
1281 ## AMR
1282
1283 It is important that we do the inter-grid transfer operators before we
1284 apply the boundary conditions.
1285 """
1286 d = {}
1289
1290 step.add_action_set(self._action_set_preprocess_solution)
1291 step.add_action_set(
1293 )
1294 step.add_action_set(self._action_set_roll_over_update_of_faces)
1295 step.add_action_set(self._action_set_update_face_label)
1296 step.add_action_set(self._action_set_update_cell_label)
1297 step.add_action_set(self._action_set_handle_boundary)
1298 step.add_action_set(self._action_set_update_cell)
1299 step.add_action_set(self._action_set_project_patch_onto_faces)
1300 step.add_action_set(self._action_set_AMR)
1301 step.add_action_set(self._action_set_postprocess_solution)
1302
1303
1304 @abstractmethod
1308 def add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory="", abstract_overwrite=True):
1309 """
1310 The ExaHyPE project will call this operation when it sets
1311 up the overall environment.
1312
1313 This routine is typically not invoked by a user.
1314
1315 output: peano4.output.Output
1316 """
1317 templatefile_prefix = os.path.dirname(os.path.realpath(__file__)) + "/"
1318
1319 if self._solver_template_file_class_name is None:
1320 templatefile_prefix += self.__class__.__name__
1321 else:
1322 templatefile_prefix += self._solver_template_file_class_name
1323
1324 if(subdirectory):
1325 subdirectory += "/"
1326
1327 abstractHeaderDictionary = {}
1328 implementationDictionary = {}
1329 self._init_dictionary_with_default_parameters(abstractHeaderDictionary)
1330 self._init_dictionary_with_default_parameters(implementationDictionary)
1331
1332 header_file_abstr_template = templatefile_prefix + "Abstract.template.h"
1333 cpp_file_abstr_template = templatefile_prefix + "Abstract.template.cpp"
1334 abstractHeaderDictionary[ "HEADER_FILE_ABSTR_TEMPLATE" ] = os.path.basename(header_file_abstr_template)
1335 abstractHeaderDictionary[ "CPP_FILE_ABSTR_TEMPLATE" ] = os.path.basename(cpp_file_abstr_template)
1336 self.add_entries_to_text_replacement_dictionary(abstractHeaderDictionary)
1337
1338 header_file_template = templatefile_prefix + ".template.h"
1339 cpp_file_template = templatefile_prefix + ".template.cpp"
1340 implementationDictionary[ "HEADER_FILE_TEMPLATE" ] = os.path.basename(header_file_template)
1341 implementationDictionary[ "CPP_FILE_TEMPLATE" ] = os.path.basename(cpp_file_template)
1342 self.add_entries_to_text_replacement_dictionary(implementationDictionary)
1343
1344 generated_abstract_header_file = (
1346 header_file_abstr_template,
1347 cpp_file_abstr_template,
1348 "Abstract" + self._name,
1349 namespace,
1350 subdirectory + ".",
1351 abstractHeaderDictionary,
1352 abstract_overwrite,
1353 True,
1354 )
1355 )
1356 generated_solver_files = (
1358 header_file_template,
1359 cpp_file_template,
1360 self._name,
1361 namespace,
1362 subdirectory + ".",
1363 implementationDictionary,
1364 False,
1365 True,
1366 )
1367 )
1368
1369 output.add(generated_abstract_header_file)
1370 output.add(generated_solver_files)
1371 output.makefile.add_h_file(subdirectory + "Abstract" + self._name + ".h", generated=True)
1372 output.makefile.add_h_file(subdirectory + self._name + ".h", generated=True)
1373 output.makefile.add_cpp_file(subdirectory + "Abstract" + self._name + ".cpp", generated=True)
1374 output.makefile.add_cpp_file(subdirectory + self._name + ".cpp", generated=True)
1375
1376 if self._use_var_shortcut:
1377 generated_shortcut_file = peano4.output.Jinja2TemplatedHeaderFile(
1378 os.path.dirname(os.path.realpath(__file__))
1379 + "/"
1380 + "../VariableShortcuts.template.h",
1381 "VariableShortcuts",
1382 namespace,
1383 subdirectory + ".",
1384 implementationDictionary,
1385 True,
1386 True,
1387 )
1388 output.add(generated_shortcut_file)
1389 output.makefile.add_h_file(subdirectory + "VariableShortcuts.h", generated=True)
1390
1391
1392 def set_solver_constants(self, datastring):
1393 self._solver_constants = datastring
1394
1395
1396 def add_solver_constants(self, datastring):
1397 self._solver_constants += datastring + "\n"
1398
1399
1401 """
1402 This one is called by all algorithmic steps before I invoke
1403 add_entries_to_text_replacement_dictionary().
1404
1405 See the remarks on set_postprocess_updated_patch_kernel to understand why
1406 we have to apply the (partially befilled) dictionary to create a new entry
1407 for this very dictionary.
1408 """
1409 d["NUMBER_OF_VOLUMES_PER_AXIS"] = self._patch.dim[0]
1410 d["OVERLAP"] = self._overlap
1411 d["HALO_SIZE"] = int(self._patch_overlap_old.dim[0] / 2)
1412 d["SOLVER_INSTANCE"] = self.get_name_of_global_instance()
1413 d["SOLVER_NAME"] = self._name
1414 d["UNKNOWN_IDENTIFIER"] = self._unknown_identifier()
1415 d["NUMBER_OF_UNKNOWNS"] = self._unknowns
1416 d["NUMBER_OF_AUXILIARY_VARIABLES"] = self._auxiliary_variables
1417 d["SOLVER_NUMBER"] = 22
1418
1419 d["ASSERTION_WITH_1_ARGUMENTS"] = "nonCriticalAssertion1"
1420 d["ASSERTION_WITH_2_ARGUMENTS"] = "nonCriticalAssertion2"
1421 d["ASSERTION_WITH_3_ARGUMENTS"] = "nonCriticalAssertion3"
1422 d["ASSERTION_WITH_4_ARGUMENTS"] = "nonCriticalAssertion4"
1423 d["ASSERTION_WITH_5_ARGUMENTS"] = "nonCriticalAssertion5"
1424 d["ASSERTION_WITH_6_ARGUMENTS"] = "nonCriticalAssertion6"
1425
1426 if self._min_volume_h > self._max_volume_h:
1427 raise Exception("min/max h are inconsistent")
1428
1429 d["MAX_VOLUME_H"] = self._max_volume_h
1430 d["MIN_VOLUME_H"] = self._min_volume_h
1431 d["SOLVER_CONSTANTS"] = self._solver_constants
1432 d["SOLVER_INCLUDES"] = self.user_solver_includes
1433 d["BOUNDARY_CONDITIONS_IMPLEMENTATION"] = self._boundary_conditions_implementation
1434 d["REFINEMENT_CRITERION_IMPLEMENTATION"] = self._refinement_criterion_implementation
1435 d["INITIAL_CONDITIONS_IMPLEMENTATION"] = self._initial_conditions_implementation
1436
1437 d["COMPUTE_KERNEL_CALL"] = jinja2.Template(
1438 self._compute_kernel_call, undefined=jinja2.DebugUndefined
1439 ).render(**d)
1440 d["COMPUTE_KERNEL_CALL_STATELESS"] = jinja2.Template(
1441 self._compute_kernel_call_stateless, undefined=jinja2.DebugUndefined
1442 ).render(**d)
1443
1444 d["ABSTRACT_SOLVER_USER_DECLARATIONS"] = jinja2.Template(
1445 self._abstract_solver_user_declarations, undefined=jinja2.DebugUndefined
1446 ).render(**d)
1447 d["ABSTRACT_SOLVER_USER_DEFINITIONS"] = jinja2.Template(
1448 self._abstract_solver_user_definitions, undefined=jinja2.DebugUndefined
1449 ).render(**d)
1450 d["SOLVER_USER_DECLARATIONS"] = jinja2.Template(
1451 self._solver_user_declarations, undefined=jinja2.DebugUndefined
1452 ).render(**d)
1453 d["SOLVER_USER_DEFINITIONS"] = jinja2.Template(
1454 self._solver_user_definitions, undefined=jinja2.DebugUndefined
1455 ).render(**d)
1456 d["START_TIME_STEP_IMPLEMENTATION"] = jinja2.Template(
1457 self._start_time_step_implementation, undefined=jinja2.DebugUndefined
1458 ).render(**d)
1459 d["FINISH_TIME_STEP_IMPLEMENTATION"] = jinja2.Template(
1460 self._finish_time_step_implementation, undefined=jinja2.DebugUndefined
1461 ).render(**d)
1462 d["CONSTRUCTOR_IMPLEMENTATION"] = jinja2.Template(
1463 self._constructor_implementation, undefined=jinja2.DebugUndefined
1464 ).render(**d)
1465
1466 d["PREPROCESS_RECONSTRUCTED_PATCH"] = jinja2.Template(
1467 self._preprocess_reconstructed_patch, undefined=jinja2.DebugUndefined
1468 ).render(**d)
1469 d["POSTPROCESS_UPDATED_PATCH"] = jinja2.Template(
1470 self._postprocess_updated_patch, undefined=jinja2.DebugUndefined
1471 ).render(**d)
1472
1473 d["COMPUTE_TIME_STEP_SIZE"] = jinja2.Template(
1474 self._compute_time_step_size, undefined=jinja2.DebugUndefined
1475 ).render(**d)
1476 d["COMPUTE_NEW_TIME_STEP_SIZE"] = jinja2.Template(
1477 self._compute_new_time_step_size, undefined=jinja2.DebugUndefined
1478 ).render(**d)
1479 d["COMPUTE_MAX_EIGENVALUE"] = self._compute_eigenvalue
1480
1481 d["STATELESS_PDE_TERMS"] = self._pde_terms_without_state
1482 d["KERNEL_NAMESPACE"] = self._kernel_namespace
1483
1484 d["USE_VARIABLE_SHORTCUT"] = self._use_var_shortcut
1485 d["VARIABLE_NAMES"] = self._variable_names
1486 d["VARIABLE_POSITIONS"] = self._variable_pos
1487
1488
1489 @property
1490 def unknowns(self):
1491 return self._unknowns
1492
1493
1494 @property
1495 def patch_size(self):
1496 return self._patch_size
1497
1498
1499 @property
1501 return self._auxiliary_variables
1502
1503
1504 @patch_size.setter
1505 def patch_size(self, value):
1506 self._patch_size = value
1508 self.create_action_sets()
1509
1510
1511 @unknowns.setter
1512 def unknowns(self, value):
1513 self._unknowns = value
1515 self.create_action_sets()
1516
1517
1518 @auxiliary_variables.setter
1519 def auxiliary_variables(self, value):
1520 self._auxiliary_variables = value
1522 self.create_action_sets()
1523
1524
1525 @property
1530 @preprocess_reconstructed_patch.setter
1532 """!
1533 Set a new processing kernel
1534
1535 Before the actual update, the numerical solver takes the actual solution
1536 including all auxiliary variables and copies it into another array which
1537 is calls reconstructedPatch (in FD4, this field is called oldQWithHalo).
1538 This array is bigger than the actual patch data, as it also holds the
1539 halo. One the solution is copied over, the kernel writes in the halo
1540 data using data from the faces. This reconstructed object is now passed
1541 into the compute kernel, which derives new values for all evolution
1542 variables. That is, the compute kernels' output does not hold the auxiliary
1543 variables anymore, as they are not subject to the PDE. The new variables
1544 are eventually written back into the real data, where they replace the
1545 old quantities (the auxiliary variables in there remain untouched).
1546 After the compute kernel has terminated, the reconstructed data including
1547 the halo is thrown away.
1548
1549 ## Auxiliary variables
1550
1551 If you alter the auxiliary variables (material parameters) in the patch
1552 preparation, this is absolutely fine, but the change will not be committed
1553 into the real data. It is only there temporarily while the actual patch
1554 will continue to hold the original material parameters.
1555
1556 You can alter the actual patch data Q in the preprocessing. If this
1557 actual data are evolution (PDE) parameters, these changes however will
1558 be overwritten by the compute kernel. If you alter auxiliary variables
1559 within the preprocessing within the patch (Q), then this is fine, but the
1560 changes will not be visible to the reconstructed patch immediately. They
1561 will be visible there in the next time step. So you might be well-advised
1562 to alter the auxiliary variables in both the patch data and the
1563 reconstructed data.
1564
1565 ## Access
1566
1567 We recommend that you use the AoS enumerator exahype2::enumerator::AoSLexicographicEnumerator.
1568 This one has to be given 1 as number of cells/patches, it requires the size
1569 of the patch, its halo size, you have to pass it the unknowns and the correct
1570 number of auxiliary parameters. If you want the preprocessing also to alter
1571 the actual input, you need a second enumerator. In this second one, all
1572 parameters are the same besides the halo size, which you have to set to 0:
1573 The input patch has no halo. This halo is only added temporarily in the
1574 reconstruction step.
1575 """
1578 self.create_action_sets()
1579
1580
1581 @property
1582 def name(self):
1583 return self._name
1584
1585
1586 @property
1588 return self._postprocess_updated_patch
1589
1590
1591 @postprocess_updated_patch.setter
1592 def postprocess_updated_patch(self, kernel):
1593 """
1594 Define a postprocessing routine over the data
1595
1596 The postprocessing kernel often looks similar to the following code:
1597
1598 {
1599 int index = 0;
1600 dfor( volume, {{NUMBER_OF_VOLUMES_PER_AXIS}} ) {
1601 enforceCCZ4constraints( newQ+index );
1602 index += {{NUMBER_OF_UNKNOWNS}} + {{NUMBER_OF_AUXILIARY_VARIABLES}};
1603 }
1604 }
1605
1606 but often, it is nicer ot user one of the pre-defined kernels in ExaHyPE
1607 for postprocessing:
1608
1609 ::exahype2::fv::mapInnerNeighbourVoxelAlongBoundayOntoAuxiliaryVariable(
1610 newQ,
1611 {{NUMBER_OF_VOLUMES_PER_AXIS}},
1612 {{NUMBER_OF_UNKNOWNS}},
1613 {{NUMBER_OF_AUXILIARY_VARIABLES}}
1614 );
1615
1616
1617 As highlighted from the examples above, you can use normal ExaHyPE constant
1618 within your injected code. Furthermore, you have the following two variables
1619 available:
1620
1621 - newQ This is a pointer to the whole data structure (one large
1622 array). The patch is not supplemented by a halo layer.
1623 - marker An instance of the CellMarker which identifies the cell.
1624
1625 In ExaHyPE's finite volume solvers, there are two different ways how to inject
1626 postprocessing: You can either define your own postprocessing step by setting
1627 _action_set_postprocess_solution, or you can use this setter to inject some
1628 source code. The two options are not equivalent: The snippet injected here is
1629 executed directly after the kernel update. Consequently, it also runs on a GPU
1630 if GPUs are employed, or it runs directly after a task has been launched to
1631 update a cell.
1632
1633 The postprocessing action set is launched when the cell update is committed,
1634 i.e., after the cell task has terminated or a task result came back from the
1635 GPU. Consequently, the snippet injected here might not have access to global
1636 variables (if it executes on a GPU) while the postprocessing action set
1637 always has access to the global program state.
1638
1639
1640 ## Attributes
1641
1642 kernel: String
1643 C++ code that holds the postprocessing kernel
1644 """
1645 self._postprocess_updated_patch = kernel
1647 self.create_action_sets()
1648
1649
1650 @property
1651 def overlap(self):
1652 return self._overlap
1653
1654
1655 @overlap.setter
1656 def overlap(self, value):
1657 if value < 1:
1658 raise Exception(
1659 "Halo (overlap) size has to be bigger than zero but was {}".format(
1660 value
1661 )
1662 )
1663 self._overlap = value
1665 self.create_action_sets()
1666
1667
1668 @property
1669 def interpolation(self):
1670 return self._interpolation
1671
1672
1673 @interpolation.setter
1674 def interpolation(self, value):
1675 self._interpolation = value
1677 self.create_action_sets()
1678
1679
1680 @property
1681 def restriction(self):
1682 return self._restriction
1683
1684
1685 @restriction.setter
1686 def restriction(self, value):
1687 self._restriction = value
1689 self.create_action_sets()
1690
1691
1693 self,
1694 cell_data_storage: Storage,
1695 face_data_storage: Storage,
1696 ):
1697 """
1698 By default, we hold all data on the heap using smart pointers. You can explicitly switch
1699 to storage on the call stack or heap using raw pointers.
1700
1701 @see create_data_structures()
1702 """
1703 assert isinstance(cell_data_storage, Storage)
1704 assert isinstance(face_data_storage, Storage)
1705
1706 self._cell_data_storage = cell_data_storage
1707 self._face_data_storage = face_data_storage
1708
1710 self.create_action_sets()
Update the cell label within a sweep.
Definition CellLabel.py:9
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
_unknown_identifier(self)
Definition FV.py:982
_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
get_finest_volume_size(self, domain_size)
Definition FV.py:426
create_data_structures(self)
Create data structures required by all Finite Volume solvers.
Definition FV.py:526
_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:1400
add_user_solver_includes(self, value)
Add further includes to this property, if your solver requires some additional routines from other he...
Definition FV.py:517
_baseline_action_set_descend_invocation_order
Definition FV.py:317
create_action_sets(self)
Create all the action sets.
Definition FV.py:730
_provide_cell_data_to_compute_kernels_default_guard(self)
Default logic when to create cell data or not.
Definition FV.py:881
get_min_number_of_spacetree_levels(self, domain_size)
Definition FV.py:383
get_max_number_of_spacetree_levels(self, domain_size)
Definition FV.py:393
get_coarsest_number_of_finite_volumes(self, domain_size)
Definition FV.py:414
get_coarsest_number_of_patches(self, domain_size)
Definition FV.py:406
_action_set_AMR_commit_without_further_analysis
Definition FV.py:278
create_readme_descriptor(self, domain_offset, domain_size)
Definition FV.py:430
add_actions_to_perform_time_step(self, step)
Add action sets to time step.
Definition FV.py:1274
postprocess_updated_patch(self)
Definition FV.py:1587
add_actions_to_create_grid(self, step, evaluate_refinement_criterion)
The boundary information is set only once.
Definition FV.py:1109
_store_cell_data_default_guard(self)
Extend the guard via ands only.
Definition FV.py:912
add_implementation_files_to_project(self, namespace, output, dimensions, subdirectory="", abstract_overwrite=True)
The ExaHyPE project will call this operation when it sets up the overall environment.
Definition FV.py:1308
add_to_Peano4_datamodel(self, datamodel, verbose)
Add all required data to the Peano project's datamodel so it is properly built up.
Definition FV.py:990
add_actions_to_init_grid(self, step, restart_from_checkpoint=False)
Add all the action sets to init grid.
Definition FV.py:1043
__init__(self, name, patch_size, overlap, unknowns, auxiliary_variables, min_volume_h, max_volume_h, plot_grid_properties, bool pde_terms_without_state, kernel_namespace, baseline_action_set_descend_invocation_order=0)
Create solver.
Definition FV.py:159
get_name_of_global_instance(self)
Definition FV.py:986
_load_cell_data_default_guard(self)
Extend the guard via ands only.
Definition FV.py:927
get_finest_number_of_patches(self, domain_size)
Definition FV.py:410
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
_get_default_includes(self)
Definition FV.py:1032
set_solver_constants(self, datastring)
Definition FV.py:1392
add_solver_constants(self, datastring)
Definition FV.py:1396
add_entries_to_text_replacement_dictionary(self, d)
Definition FV.py:1305
get_finest_number_of_finite_volumes(self, domain_size)
Definition FV.py:418
_load_face_data_default_guard(self)
Extend the guard via ands only.
Definition FV.py:962
_action_set_AMR_throughout_grid_construction
Definition FV.py:277
get_coarsest_volume_size(self, domain_size)
Definition FV.py:422
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_copy_new_faces_onto_old_faces
Definition FV.py:282
preprocess_reconstructed_patch(self)
Definition FV.py:1526
add_use_data_statements_to_Peano4_solver_step(self, step)
Tell Peano what data to move around.
Definition FV.py:1013
add_actions_to_checkpoint_solution(self, step, output_path, restart_from_checkpoint=False)
Add action sets to plot solution step.
Definition FV.py:1210
_provide_face_data_to_compute_kernels_default_guard(self)
Definition FV.py:901
switch_storage_scheme(self, Storage cell_data_storage, Storage face_data_storage)
By default, we hold all data on the heap using smart pointers.
Definition FV.py:1696
add_actions_to_plot_solution(self, step, output_path, restart_from_checkpoint=False)
Add action sets to plot solution step.
Definition FV.py:1142
_action_set_roll_over_update_of_faces
Definition FV.py:281
The ExaHyPE 2 Finite Volume handling of (dynamically) adaptive meshes.
Definition DynamicAMR.py:11
The global periodic boundary conditions are set in the Constants.h.
PostprocessSolution differs from other action sets, as I only create it once.
PreprocessSolution differs from other action sets, as I only create it once.
Project patch data onto faces so we can reconstruct the halo layers later on.
This action set takes the updated data per face and writes it into new.
Very simple converter which maps the patch 1:1 onto a double array.
This class plugs into the first access to a face and copies the data over.