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