Peano
Loading...
Searching...
No Matches
Project.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 peano4
5
6import dastgen2
7
8import os
9import sys
10
11from swift2.particle.Particle import Particle
13 DynamicMeshRefinementAnalysis,
14)
15from swift2.output.SWIFTMain import SWIFTMain
16from swift2.actionsets.Cleanup import Cleanup
17
18import swift2.api.graphcompiler
19
20
21from abc import abstractmethod
22
23
24class Project(object):
25 """!
26
27 Swift2 project
28
29 Represents an abstract SWIFT 2 project. An SWIFT 2 project is a Peano 4
30 project with a particular set of actions (algorithmic phases)
31 that you can choose from and with particular solver types. It
32 realises a builder mechanism, i.e. you build up your SWIFT 2
33 project and then you finally tell the project "give me the Peano 4
34 project". From hereon, you can use this Peano 4 project to actually
35 set up the Peano 4 application.
36
37 Please do not use this class directly. Use one of its subclasses.
38
39 @see generate_Peano4_project()
40
41 """
42
43 def __init__(self, namespace, project_name, directory=".", executable="swift2"):
44 self._project = peano4.Project(namespace, project_name, directory)
45
46 self._domain_offset = [0.0, 0.0, 0.0]
47 self._domain_size = [1.0, 1.0, 1.0]
48 self._dimensions = 2
56 self._build_mode = peano4.output.CompileMode.Asserts
57 self._executable_name = executable
58 self._periodic_BC = [False, False, False]
59 self._output_path = "./"
60
63
66 "InitialConditions", False
67 )
70
72
74 swift2.api.graphcompiler.particle_steps_onto_separate_mesh_traversals_multiscale_sort_scattered_memory
75 )
77 swift2.api.graphcompiler.initialisation_steps_onto_separate_mesh_traversals_multiscale_sort_scattered_memory
78 )
79
80 def set_load_balancing(self, load_balancer_name, load_balancer_arguments):
81 """
82 load_balancer_name: string
83 Should be full-qualified name of the load balancer.
84 By default, I recommend to pass "toolbox::loadbalancing::strategies::SpreadOutHierarchically",
85 but you might be well-advices to study different classes within the namespace toolbox::loadbalancing.
86
87 load_balancer_arguments: string
88 If your load balancing requires parameters, add them
89 here. It is a string that will be copied into the C++ class instantiation.
90 Please add the brackets yourself, i.e. "(3,4,5)" is fine, but "3,4,5" is not.
91 The only exception is the empty parameter list. Here, you can/should simply
92 add the empty string.
93 """
94 self._load_balancer_name = load_balancer_name
95 self._load_balancer_arguments = load_balancer_arguments
96
97 """
98 The standard extensions that I use for both Peano and ExaHyPE.
99 """
100 LibraryDebug = "_debug"
101 LibraryRelease = ""
102 LibraryTrace = "_trace"
103 LibraryAsserts = "_asserts"
104 LibraryStats = "_stats"
105
106 def set_Peano4_installation(self, src_path, mode=peano4.output.CompileMode.Release):
107 """
108 src_path: string
109 Path (relative or absolute) to the src directory of Peano. This path
110 should hold both the headers (in subdirectories) and all the static
111 libraries.
112
113 mode: peano4.output.CompileMode
114 """
115
116 swift2dir = os.path.join(src_path, "src", "swift2")
117 peano4dir = os.path.join(src_path, "src", "peano4")
118 tarchdir = os.path.join(src_path, "src", "tarch")
119
120 for d in (swift2dir, peano4dir, tarchdir):
121 if not os.path.exists(d):
122 dir_stripped = d[len(src_path) :]
123 raise FileNotFoundError(
124 f"Didn't find directory {dir_stripped} in passed peano root dir={src_path}"
125 )
126
127 self._Peano_src_directory = src_path
128 self._build_mode = mode
129
131 self._global_max_h = 0.0
132 for current_species_set in self._particle_species_set:
133 self._global_max_h = max(
134 self._global_max_h, current_species_set.particle_model.max_h
135 )
136
138 """
139
140 We export SWIFT's constants. Besides the constants from SWIFT,
141 I also export some parameters from Peano onto the SWIFT constants
142 file. Therefore, it is important that you parse the configure output
143 before we export the constants.
144
145 """
146 self._project.constants.clear()
147 offset_string = "{" + str(self._domain_offset[0])
148 size_string = "{" + str(self._domain_size[0])
149 for i in range(1, self._dimensions):
150 offset_string += ","
151 size_string += ","
152 offset_string += str(self._domain_offset[i])
153 size_string += str(self._domain_size[i])
154 offset_string += "}"
155 size_string += "}"
156 self._project.constants.add_include("""#include <bitset>""")
157 self._project.constants.add_include("""#include "tarch/la/Vector.h" """)
158 self._project.constants.add_include("""#include "peano4/utils/Globals.h" """)
159 self._project.constants.export_const_with_type(
160 "DomainOffset", offset_string, "tarch::la::Vector<Dimensions,double>"
161 )
162 self._project.constants.export_const_with_type(
163 "DomainSize", size_string, "tarch::la::Vector<Dimensions,double>"
164 )
165 self._project.constants.export("MinTerminalTime", str(self._min_terminal_time))
167 self._project.constants.export(
168 "MaxTerminalTime", str(self._max_terminal_time)
169 )
170 else:
171 self._project.constants.export(
172 "MaxTerminalTime", "std::numeric_limits<double>::max()"
173 )
174 self._project.constants.export(
175 "FirstPlotTimeStamp", str(self._first_plot_time_stamp)
176 )
177 self._project.constants.export(
178 "TimeInBetweenPlots", str(self._time_in_between_plots)
179 )
180 self._project.constants.export("PlotterPrecision", str(self._plotter_precision))
181 self._project.constants.export_boolean_sequence("PeriodicBC", self._periodic_BC)
182 self._project.constants.export("GlobalMaxH", str(self._global_max_h))
183
184 build_string = "python3 "
185 for i in sys.argv:
186 build_string += " "
187 build_string += i
188 self._project.constants.export_string("BuildInformation", build_string)
189 self._project.constants.export_string(
190 "ConfigureInformation", self._project.output.makefile.configure_call
191 )
192
193 readme_text = """
194
195### SWIFT 2
196
197This code uses the second generation of the SWIFT code controlled through
198its Python API. Under the hood it uses Peano 4. We do not yet have a release
199paper for this second generation of SWIFT yet, and thus appreciate any
200citation of the original SWIFT paper
201
202 @article{Schaller:2020:SWIFT,
203 title = {t.b.d.}
204 }
205
206"""
207
208 if self._dimensions == 2:
209 readme_text += (
210 """
211
212We assume that you use a domain of ("""
213 + str(self._domain_offset[0])
214 + ""","""
215 + str(self._domain_size[0] - self._domain_offset[0])
216 + """)x("""
217 + str(self._domain_offset[1])
218 + ""","""
219 + str(self._domain_size[1] - self._domain_offset[1])
220 + """).
221 """
222 )
223 else:
224 readme_text += (
225 """
226
227We assume that you use a domain of ("""
228 + str(self._domain_offset[0])
229 + ""","""
230 + str(self._domain_size[0] - self._domain_offset[0])
231 + """)x("""
232 + str(self._domain_offset[1])
233 + ""","""
234 + str(self._domain_size[1] - self._domain_offset[1])
235 + """)x("""
236 + str(self._domain_offset[2])
237 + ""","""
238 + str(self._domain_size[2] - self._domain_offset[2])
239 + """).
240"""
241 )
242
243 readme_text += (
244 """
245
246Peano 4 will cut this domain equidistantly and recursively into three parts along each coordinate axis. This yields a spacetree.
247
248The coarsest mesh chosen has a mesh width of h_max="""
249 + str(self._global_max_h)
250 + """.
251As Peano realises three-partitioning, the actual maximum mesh size will be h_max="""
252 + str(self.__real_max_mesh_size()[0])
253 + """.
254This corresponds to at least """
255 + str(self.__real_max_mesh_size()[1])
256 + """ spacetree levels.
257The coarsest regular mesh hence will host """
258 + str((3**self._dimensions) ** (self.__real_max_mesh_size()[1]))
259 + """ octants.
260
261Once this resultion is reached, the mesh will stop the initial load balancing, insert the
262particles and then kick off the simulation. The mesh might be refined and erased subsequently,
263but it will never become coarser than these constraints.
264
265Each particle provides its own additional meshing constraints:
266
267Species | max_h | coarsest level | min_h | finest level
268--------|-------|----------------|-------|-------------"""
269 )
270
271 for i in self._particle_species:
272 readme_text += """
273{} | {} | {} | {} | {}
274""".format(
275 i.name,
276 self.real_mesh_size(i.max_h)[0],
277 self.real_mesh_size(i.max_h)[1],
278 self.real_mesh_size(i.min_h)[0],
279 self.real_mesh_size(i.min_h)[1],
280 )
281
282 readme_text += """
283
284The meshing quantities abive are species-specific. Once you have multiple
285particle species, the actual grid results from a combination of their
286properties, and some species particles might reside on different resolution
287levels.
288
289"""
290 self._project.output.readme.add_package_description(readme_text)
291
292 def real_mesh_size(self, target_h):
293 """!
294
295 Translate a mesh size into its real Peano mesh size
296
297 Peano uses three-partitioning. That is, it will neve be able to match
298 given refinement instructions exactly. All it can do is to approximate
299 them. See @ref
300
301 Return a tuple of mesh size and mesh levels
302
303 """
304 h = self._domain_size[0]
305 level = 0
306 while h > target_h:
307 h = h / 3.0
308 level += 1
309 return (h, level)
310
312 return self.real_mesh_size(self._global_max_h)
313
315 self._project.output.makefile.set_dimension(self._dimensions)
316 self._project.output.makefile.set_executable_name(self._executable_name)
317
319 self,
320 dimensions,
321 offset,
322 domain_size,
323 min_end_time,
324 max_end_time,
325 first_plot_time_stamp,
326 time_in_between_plots,
327 periodic_BC=[False, False, False],
328 plotter_precision=5,
329 ):
330 """
331
332 offset and size should be lists with dimensions double entries.
333
334 first_plot_time_stamp: Float
335 Is irrelevant if time_in_between_plots equals zero
336
337 time_in_between_plots: Float
338 Set to zero if you don't want to have any plots
339
340 max_end_time: Float
341 If you set it zero (or actually any value msmaller than min_end_time), then
342 the code will run until the cell that lags behind the most hits the min time.
343 If you specify a valid max time however, you can stop the sim as soon as the
344 most advanced cell exceeds this threshold.
345
346 """
347 self._domain_offset = offset
348 self._domain_size = domain_size
349 self._dimensions = dimensions
350 self._max_terminal_time = max_end_time
351 self._min_terminal_time = min_end_time
352 self._first_plot_time_stamp = first_plot_time_stamp
353 self._time_in_between_plots = time_in_between_plots
354 self._periodic_BC = []
355 self._plotter_precision = plotter_precision
356 for d in range(0, dimensions):
357 self._periodic_BC.append(periodic_BC[d])
358 if plotter_precision <= 0:
359 raise Exception("Plotter precision has to be bigger than 0")
360
361 def set_output_path(self, path):
362 self._output_path = path
363 if not self._output_path.endswith("/"):
364 self._output_path += "/"
365
367 solverRepositoryDictionary = {
368 "PARTICLE_SPECIES": [x.name for x in self._particle_species],
369 "LOAD_BALANCER": self._load_balancer_name,
370 "LOAD_BALANCER_ARGUMENTS": self._load_balancer_arguments,
371 }
372
373 templatefile_prefix = os.path.dirname(__file__)
374 generated_solver_files = (
376 templatefile_prefix + "/output/GlobalState.template.h",
377 templatefile_prefix + "/output/GlobalState.template.cpp",
378 "GlobalState",
379 self._project.namespace + ["repositories"],
380 "repositories",
381 solverRepositoryDictionary,
382 True,
383 )
384 )
385
386 self._project.output.add(generated_solver_files)
387 self._project.output.makefile.add_cpp_file(
388 "repositories/GlobalState.cpp", generated=True
389 )
390
391 def add_particle_species(self, particle: Particle):
392 """
393
394 Add a new particle species (type) to the project. You get the container
395 back in which the particles are handled on the rank. Use this one where
396 another action set requires a particle set.
397
398 """
399 self._particle_species.append(particle)
400 self._particle_species_set.append(
402 )
403 return self._particle_species_set[-1]
404
405 def generate_Peano4_project(self, verbose=False):
406 """!
407
408 Build the Peano4 project
409
410 The main job of this routine is to add all the action sets et al that you
411 require to run this ExaHyPE2 application.
412
413 This routine generates a Peano project, i.e. the domain-specific ExaHyPE
414 view is translated into a Peano model. Once you have called this routine,
415 any changes to the ExaHyPE 2 configuration do not propagate into the Peano
416 setup anymore. If you alter the ExaHyPE setup, you have to call
417 generate_Peano4_project() again to get a new snapshot/version of the
418 Peano setup.
419
420
421 ## Initial grid
422
423 The initial grid will be a regular one, spanned through
424
425 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
426 action_set_create_regular_grid = peano4.toolbox.CreateRegularGrid(...)
427 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
428
429 According to the documentation of peano4.toolbox.CreateRegularGrid,
430 the action set will produce a mesh that is just finer than the mesh
431 width passed, so we meet the max mesh width, but do not create a
432 mesh that is significantly finer.
433
434 As we insert particles in SPH, we have therefore to make this initial
435 resolution three times coarser than what we allow, as we only insert
436 particles into the finest mesh.
437
438
439
440 ## Task graph compiler
441
442 The core contribution of the generation is the task graph compiler which
443 really is a mapping of algorithm steps per particle species onto grid
444 sweeps. The actual mapping is outsourced into the function represented
445 by self.task_graph_compiler. This way, users can switch the translator's
446 behaviour. This function returns a sequence of mesh traversals. On top
447 of that, we have the default traversals to create a grid, to plot it,
448 to initialise the setup, and to clean up after we're done.
449
450 Once we have our three default steps plus a sequence of algorithmic steps
451 per time step, we run through the following steps:
452
453 - Create a particle set around each species and add it to the project as
454 global variable. This is the global container administering these guys.
455 - Tell each algorithmic step to use this particle set. An exception could be
456 the grid creation. At this point, we don't have particles yet. We add
457 the data structure nevertheless. It ensures that we have all the data
458 in place, and it we also then can be sure that everything is properly
459 initialised. The actual particle set will be empty at this point of the
460 simulation.
461 - Ensure that plotting and initialisation use the update particle-grid
462 association properly. The plot has to update it, as previous steps
463 might have started a resort yet might not have finished it.
464 - Add all the algorithmic steps, including the default ones, to
465 the project.
466
467
468 ## Particle initialisation and proper sorting
469
470 The particle initialisation might end up with an invalid association of
471 particles to vertices. The graph compiler might make the first step of a
472 time step sequence sort if and only if the last one has altered the
473 particles' position. Consequently, we might end up with an initialisation
474 which yields an inconsistent data association. We therefore make it sort
475 the particles, but we need another grid sweep to finalise this sort in
476 case we have to do some global rearrangements. This is our rationale why
477 we realise the initialisation in two steps.
478
479 """
482
483 #
484 # Make plotting resort and filter the particles. For this, they need
485 # the species set. The initialisation steps and the actual time steps
486 # are added the resorting action sets by the graph compiler, i.e. we
487 # should and we may not add them here manually. It is only the plotting
488 # (and the grid construction) which are not automatically added the
489 # sorting steps. However, the grid construction does not hold any
490 # particles yet, so we don't have to deal with this algorithm phase.
491 #
492 for current_species_set in self._particle_species_set:
493 self.algorithm_step_plot.add_action_set(
495 current_species_set
496 )
497 )
498
499 self.algorithm_step_plot.add_action_set(
501 )
502
503 #
504 # Create cleanup action sets
505 #
506
507 for current_species_set in self._particle_species_set:
508 self.algorithm_step_cleanup.add_action_set(Cleanup(current_species_set))
509
510 #
511 # Create mesh traversals aka solver steps
512 #
513 initialisation_steps = self.initialisation_steps_task_graph_compiler(
514 species_sets=self._particle_species_set, verbose=verbose
515 )
516 solver_steps = self.algorithm_steps_task_graph_compiler(
517 species_sets=self._particle_species_set, verbose=verbose
518 )
519
520 #
521 # Make each solver step (incl predefined ones) use the particles and particle sets
522 #
523 for current_species_set in self._particle_species_set:
524 self._project.datamodel.add_global_object(
525 current_species_set.particle_model
526 )
527 self._project.datamodel.add_vertex(current_species_set)
528
529 self.algorithm_step_create_grid.use_vertex(current_species_set)
530 self.algorithm_step_initial_conditions.use_vertex(current_species_set)
531 self.algorithm_step_plot.use_vertex(current_species_set)
532 self.algorithm_step_cleanup.use_vertex(current_species_set)
533
534 #
535 # Add the cell statistics and the task markers everywhere
536 #
537 for current_species_set in self._particle_species_set:
538 cell_marker_for_statistics = (
539 DynamicMeshRefinementAnalysis.create_cell_marker(
540 current_species_set.name
541 )
542 )
544 current_species_set.name
545 )
547 task_name=current_species_set.name,
548 full_qualified_enumerator_type="::swift2::TaskEnumerator",
549 enumerator_include=""" #include "swift2/TaskEnumerator.h" """,
550 )
551
552 # Enable cell statistics for this species
553 self._project.datamodel.add_cell(cell_marker_for_statistics)
554 self._project.datamodel.add_cell(cell_marker_for_tasks)
555 self._project.datamodel.add_vertex(vertex_marker_for_tasks)
556
557 # Add cell statistics to these action steps
558 self.algorithm_step_create_grid.use_cell(cell_marker_for_statistics)
559 self.algorithm_step_initial_conditions.use_cell(cell_marker_for_statistics)
560 self.algorithm_step_plot.use_cell(cell_marker_for_statistics)
561 self.algorithm_step_cleanup.use_cell(cell_marker_for_statistics)
562
563 self.algorithm_step_create_grid.use_cell(cell_marker_for_tasks)
564 self.algorithm_step_initial_conditions.use_cell(cell_marker_for_tasks)
565 self.algorithm_step_plot.use_cell(cell_marker_for_tasks)
566 self.algorithm_step_cleanup.use_cell(cell_marker_for_tasks)
567
568 self.algorithm_step_create_grid.use_vertex(vertex_marker_for_tasks)
569 self.algorithm_step_initial_conditions.use_vertex(vertex_marker_for_tasks)
570 self.algorithm_step_plot.use_vertex(vertex_marker_for_tasks)
571 self.algorithm_step_cleanup.use_vertex(vertex_marker_for_tasks)
572
573 for solverstep in solver_steps:
574 solverstep.use_vertex(current_species_set)
575 solverstep.use_vertex(vertex_marker_for_tasks)
576 solverstep.use_cell(cell_marker_for_statistics)
577 solverstep.use_cell(cell_marker_for_tasks)
578
579 for solverstep in initialisation_steps:
580 solverstep.use_vertex(current_species_set)
581 solverstep.use_vertex(vertex_marker_for_tasks)
582 solverstep.use_cell(cell_marker_for_statistics)
583 solverstep.use_cell(cell_marker_for_tasks)
584
585 action_set_plot_grid = peano4.toolbox.PlotGridInPeanoBlockFormat(
586 filename="grid",
587 time_stamp_evaluation="repositories::getMinTimeStamp()",
588 additional_includes="""
589#include "repositories/GlobalState.h"
590""",
591 )
592 action_set_plot_grid.descend_invocation_order = (
593 self.algorithm_step_plot.highest_descend_invocation_order() + 1
594 )
595 action_set_plot_grid.parallel = True
596 self.algorithm_step_plot.add_action_set(action_set_plot_grid)
597
598 action_set_create_regular_grid = peano4.toolbox.CreateRegularGrid(
599 self._global_max_h
600 )
601 action_set_create_regular_grid.descend_invocation_order = (
602 self.algorithm_step_create_grid.lowest_descend_invocation_order() - 1
603 )
604 action_set_create_regular_grid.parallel = True
605 self.algorithm_step_create_grid.add_action_set(action_set_create_regular_grid)
606
607 self._project.solversteps.add_step(self.algorithm_step_create_grid)
608 self._project.solversteps.add_step(self.algorithm_step_initial_conditions)
609 self._project.solversteps.add_step(self.algorithm_step_plot)
610 self._project.solversteps.add_step(self.algorithm_step_cleanup)
611
612 for solverstep in solver_steps:
613 for action_set in self.additional_action_sets_per_solver_step:
614 solverstep.add_action_set(action_set)
615 self._project.solversteps.add_step(solverstep)
616
617 for initialisation_step in initialisation_steps:
618 self._project.solversteps.add_step(initialisation_step)
619
620 self._project.main = SWIFTMain(
621 self._project, initialisation_steps, solver_steps
622 )
623
625 self._project.output.makefile.parse_configure_script_outcome(
627 )
628 self.__export_constants()
629
630 self._project.output.makefile.set_mode(self._build_mode)
631
632 return self._project
633
634 @abstractmethod
636 assert False, "should be implemented by subclass"
637 pass
Represents a Peano 4 project.
Definition Project.py:16
Update the parallel state of particles and keep stats of them.
Swift2 project.
Definition Project.py:24
generate_Peano4_project(self, verbose=False)
Build the Peano4 project.
Definition Project.py:405
__generate_global_state_files(self)
Definition Project.py:366
__init__(self, namespace, project_name, directory=".", executable="swift2")
Definition Project.py:43
real_mesh_size(self, target_h)
Translate a mesh size into its real Peano mesh size.
Definition Project.py:292
__export_constants(self)
We export SWIFT's constants.
Definition Project.py:137
initialisation_steps_task_graph_compiler
Definition Project.py:76
set_load_balancing(self, load_balancer_name, load_balancer_arguments)
load_balancer_name: string Should be full-qualified name of the load balancer.
Definition Project.py:80
set_output_path(self, path)
Definition Project.py:361
add_particle_species(self, Particle particle)
Add a new particle species (type) to the project.
Definition Project.py:391
set_Peano4_installation(self, src_path, mode=peano4.output.CompileMode.Release)
src_path: string Path (relative or absolute) to the src directory of Peano.
Definition Project.py:106
set_global_simulation_parameters(self, dimensions, offset, domain_size, min_end_time, max_end_time, first_plot_time_stamp, time_in_between_plots, periodic_BC=[False, False, False], plotter_precision=5)
offset and size should be lists with dimensions double entries.
Definition Project.py:329
create_vertex_marker(task_name, full_qualified_enumerator_type="tarch::Enumerator", enumerator_include=""" #include "tarch/Enumerator.h" """)
Create vertex marker.
create_cell_marker(task_name)
Create cell marker for tasking.