9from .Helper
import write_file
11from .CompileMode
import CompileMode
15 """Represents the created Makefile of a Peano 4 project"""
17 default_overwrite =
True
24 result_template = jinja2.Template(
29CXXFLAGS = {{CXXFLAGS}}
35CONFIGUREPATH = {{CONFIGUREPATH}}
36EXECUTABLENAME = {{EXECUTABLENAME}}
37FORTRAN_MODULES = {{FORTRAN_MODULES}}
47 version_string = subprocess.run(
48 [self.
d[
"CXX"],
"--version"], stdout=subprocess.PIPE
51 version_string = subprocess.run(
52 [
"echo",
"Unknown"], shell=
True, stdout=subprocess.PIPE
54 self.
d[
"VERSION"] = version_string.stdout.decode(
"utf-8")
59 return jinja2.Template(
63### MPI parallelisation
65Peano's distributed memory parallelisation is based upon plain space-filling
66curves which are used first to split the cells among the ranks, and, after
67that, to split the cells into chunks and deploy them onto cores (if shared
68memory is used). There is no particularly novel contribution/paper on this
69aspect of the code. The paper that is (algorithmically) most interesting is
70an old EuroPar publication:
72 @InProceedings{Bungartz:2006:Parallelisation,
73 author={Bungartz, Hans-Joachim and Mehl, Miriam and Weinzierl, Tobias},
74 editor={Nagel, Wolfgang E. and Walter, Wolfgang V. and Lehner, Wolfgang},
75 title={A Parallel Adaptive Cartesian PDE Solver Using Space--Filling Curves},
76 booktitle={Euro-Par 2006 Parallel Processing},
78 publisher={Springer Berlin Heidelberg},
79 address={Berlin, Heidelberg},
81 abstract={In this paper, we present a parallel multigrid PDE solver working on adaptive hierarchical cartesian grids. The presentation is restricted to the linear elliptic operator of second order, but extensions are possible and have already been realised as prototypes. Within the solver the handling of the vertices and the degrees of freedom associated to them is implemented solely using stacks and iterates of a Peano space--filling curve. Thus, due to the structuredness of the grid, two administrative bits per vertex are sufficient to store both geometry and grid refinement information. The implementation and parallel extension, using a space--filling curve to obtain a load balanced domain decomposition, will be formalised. In view of the fact that we are using a multigrid solver of linear complexity {\\$}{\\backslash}mathcal{\\{}O{\\}}(n){\\$}, it has to be ensured that communication cost and, hence, the parallel algorithm's overall complexity do not exceed this linear behaviour.},
82 isbn={978-3-540-37784-9}
88### OpenMP parallelisation
90Peano 4 run uses a wrapper around OpenMP to obtain a high task efficiency.
91The wrapper can be read as user-level threading implemented on top of OpenMP's
92tasking mechanism. It is described in
94 @article{Schulz:2021:Tasking,
95 title = {Task inefficiency patterns for a wave equation solver},
98 author = {Holger Schulz and Gonzalo Brito Gadeschi and Oleksandr Rudyy and Tobias Weinzierl},
105Peano relies on a mixture of classic domain decomposition and task-based
106parallelism. The domain decomposition provides the baseline performance,
107and the tasking adds the big flexibility and scalability gain on top. The
108key publication discussing the overall idea and algorithmic ingredients is
111 @article{Charrier:2020:Enclave,
112 author = {Charrier, Dominic Etienne and Hazelwood, Benjamin and Weinzierl, Tobias},
113 title = {Enclave Tasking for DG Methods on Dynamically Adaptive Meshes},
114 journal = {SIAM Journal on Scientific Computing},
119 doi = {10.1137/19M1276194},
120 URL = {https://doi.org/10.1137/19M1276194},
121 eprint = {https://doi.org/10.1137/19M1276194}
130 return self.
d[
"EXECUTABLENAME"]
136 self.
d[
"CXXFLAGS"] =
""
138 self.
d[
"FCFLAGS"] =
""
139 self.
d[
"LDFLAGS"] =
""
142 self.
d[
"CONFIGUREPATH"] =
"."
143 self.
d[
"EXECUTABLENAME"] =
""
144 self.
d[
"CMAKE_BUILD_DIR"] =
""
145 self.
d[
"FORTRAN_MODULES"] = []
146 self.
d[
"APP_SUBDIRECTORIES"] = []
151 self.
d[
"EXECUTABLENAME"] = fname
162 self.
d[
"DIM"] = str(dimension)
166 Returns the directory where the ./configure script is located
168 return self.
d[
"CONFIGUREPATH"]
172 Returns the directory where the ./configure script is located
178 Add the header search path to both the C++ and the Fortran
181 self.
d[
"CXXFLAGS"] +=
" -I" + path
182 self.
d[
"FCFLAGS"] +=
" -I" + path
186 If you want to link against a library from Peano, feel free to use
187 get_Peano4_source_directory() and hand in a concatenation of this
188 path plus a subpath. Otherwise, specify the absolute path where to
189 search for. By default, Peano's src directory is in the search
192 A popular invocation including one of Peano's toolboxes is
194 project.output.makefile.add_library("ToolboxFiniteElements2d_trace", project.output.makefile.get_source_path() + "/toolbox/finiteelements")
196 if library_path !=
"":
197 self.
d[
"LIBS"] =
"-L" + library_path +
" " + self.
d[
"LIBS"]
198 self.
d[
"LIBS"] += library_name +
" "
202 mode should be of type CompileMode. Pass in
204 peano4.output.CompileMode.Debug
206 for example. Debug is the default.
208 if mode == CompileMode.Debug:
209 self.
d[
"MODE"] =
"DEBUG"
210 elif mode == CompileMode.Asserts:
211 self.
d[
"MODE"] =
"ASSERTS"
212 elif mode == CompileMode.Stats:
213 self.
d[
"MODE"] =
"STATS"
214 elif mode == CompileMode.Trace:
215 self.
d[
"MODE"] =
"TRACE"
216 elif mode == CompileMode.Release:
217 self.
d[
"MODE"] =
"RELEASE"
222 self.
d[
"CXX"] = value
225 self.
d[
"CXXFLAGS"] = value
228 if value
in self.
d[
"CXXFLAGS"]
and not force:
232 +
" is already in list of flags. Ignored as force attribute is not set"
235 self.
d[
"CXXFLAGS"] +=
" " + value
241 self.
d[
"FCFLAGS"] = value
244 if value
in self.
d[
"FCFLAGS"]
and not force:
248 +
" is already in list of flags. Ignored as force attribute is not set"
251 self.
d[
"FCFLAGS"] +=
" " + value
254 self.
d[
"LDFLAGS"] +=
" " + value
257 self.
d[
"LDFLAGS"] = value +
" "
261 directory should point to the directory which holds the ./configure script.
262 It furthermore has to be invoked after configure has passed successfully.
263 This script does not accept relative paths. We then search for the subdirectory
264 src and parse the Makefile there.
266 if 'PEANO_SRC_ROOT_DIR' in os.environ:
267 directory = os.environ[
'PEANO_SRC_ROOT_DIR']
269 self.
d[
"CONFIGUREPATH"] = directory
271 if 'PEANO_CMAKE_BUILD_DIR' in os.environ:
272 self.
d[
"CMAKE_BUILD_DIR"] = os.environ[
'PEANO_CMAKE_BUILD_DIR']
275 input_file = directory +
"/config.log"
276 input = open(input_file,
"r")
278 print(
"parse configure outcome " + input_file +
" to extract configure settings")
288 print(
"found the configure call info " + line)
290 for define
in MakefileDefined:
291 if re.search(
"#define " + define, line):
294 input_file = directory +
"/src/Makefile"
295 input = open(input_file,
"r")
296 print(
"parse configure outcome " + input_file +
" to extract compile settings")
297 MakefileConstants = [
298 "CXXFLAGS_PEANO_2D_RELEASE",
299 "CXXFLAGS_PEANO_2D_STATS",
300 "CXXFLAGS_PEANO_2D_ASSERTS",
301 "CXXFLAGS_PEANO_2D_TRACE",
302 "CXXFLAGS_PEANO_2D_DEBUG",
303 "CXXFLAGS_PEANO_3D_RELEASE",
304 "CXXFLAGS_PEANO_3D_STATS",
305 "CXXFLAGS_PEANO_3D_ASSERTS",
306 "CXXFLAGS_PEANO_3D_TRACE",
307 "CXXFLAGS_PEANO_3D_DEBUG",
308 "LDFLAGS_PEANO_RELEASE",
309 "LDFLAGS_PEANO_STATS",
310 "LDFLAGS_PEANO_ASSERTS",
311 "LDFLAGS_PEANO_TRACE",
312 "LDFLAGS_PEANO_DEBUG",
313 "LDADD_PEANO_2D_RELEASE",
314 "LDADD_PEANO_2D_STATS",
315 "LDADD_PEANO_2D_ASSERTS",
316 "LDADD_PEANO_2D_TRACE",
317 "LDADD_PEANO_2D_DEBUG",
318 "LDADD_PEANO_3D_RELEASE",
319 "LDADD_PEANO_3D_STATS",
320 "LDADD_PEANO_3D_ASSERTS",
321 "LDADD_PEANO_3D_TRACE",
322 "LDADD_PEANO_3D_DEBUG",
331 for constant
in MakefileConstants:
332 if re.search(constant +
" *=", line)
and line.startswith(constant):
334 flags = line.split(
"=", 1)[1].strip()
336 self.
d[constant] = flags
338 print(
"Error in " + line +
" for token " + constant)
341 self.
d[
"CMAKE_BUILD_DIR"] = next((path
for path
in (os.path.join(directory, subdir)
for subdir
in os.listdir(directory))
if os.path.isfile(os.path.join(path,
"PeanoTargets.cmake"))),
"")
344 if self.
d[
"CMAKE_BUILD_DIR"] !=
"":
345 cmake_cache_file = self.
d[
"CMAKE_BUILD_DIR"] +
"/CMakeCache.txt"
346 if os.path.isfile(cmake_cache_file):
347 with open(cmake_cache_file,
'r')
as input:
349 key_value = line.split(
"=", 1)
350 if len(key_value) == 2:
351 key, value = key_value
352 key = key.split(
":")[0].strip()
353 if key ==
"CMAKE_CXX_COMPILER":
354 self.
d[
"CXX"] = value.strip()
355 if key ==
"CMAKE_Fortran_COMPILER":
356 self.
d[
"FC"] = value.strip()
359 Error: if you call parse_configure_script_outcome(), please hand over directory where
360 ./configure had been called. You passed """ + directory
365 Add a new header filename.
366 This is actually not needed for compilation,
367 but is useful to have for IDEs where the header files can be displayed
371 Filename of a C/C++ header file. They usually should have the .h/.hpp extension.
373 Use this flag for generated files which can be tracked
374 by the cleaning routines 'distclean' or 'maintainer-clean' of the Makefile.
379 and self.
hfiles.count(filename) == 0
383 if self.
hfiles.count(filename) == 0:
384 self.
hfiles.append(filename)
391 if filename
in self.
hfiles:
392 self.
hfiles.remove(filename)
396 Add a new cpp filename. This is basically a set implementation, i.e., you can
397 add files multiple times, but they are not inserted multiple times. This
398 is important, as the steps add the cpp files. Multiple steps can hold the
399 same action, so this action would be created multiple times.
401 All the standard Peano 4 routines rely on this function to add their
402 generated files to the build environment. Nothing stops you however to
403 add more files yourself.
406 Filename of a C++ file. They usually should have the .cpp/.cxx extension.
408 Use this flag for generated files which can be tracked
409 by the cleaning routines 'distclean' or 'maintainer-clean' of the Makefile.
416 and self.
cppfiles.count(filename) == 0
421 if self.
cppfiles.count(filename) == 0:
437 Add a new Fortran file.
439 All the standard Peano 4 routines rely on this function to add their
440 generated files to the build environment. Nothing stops you however to
441 add more files yourself. Don't add a file multiple times. This might
444 Fortran is really picky about the translation order. So you have to add
445 the stuff in the right order. Otherwise, Fortran might complain. This is
448 If your file defines a module, please do not use this routine, but use
449 add_Fortran_module() instead.
452 Filename of a Fortran file. They usually should have the .f90 extension.
454 Use this flag for generated files which can be tracked
455 by the cleaning routines 'distclean' or 'maintainer-clean' of the Makefile.
480 Filename of a Fortran source code file which hosts a module. It should
481 have the extension .f90 or similar.
484 Enforce that Fortran module is added even though it might already be in
487 if not module_file.endswith(
".f90"):
489 "Warning: Fortran module file does not have extension .f90 ("
491 +
") and translation thus might fail"
493 if module_file
in self.
d[
"FORTRAN_MODULES"]
and not force:
495 """Fortran module file
499is already in module file list. Did not add it once more. You can overwrite
500this default behaviour via the force attribute in add_Fortran_module(). If
501you create multiple Peano 4 makefiles in a row (as you change parameters, e.g.)
502then this message can typically be ignored.
505 elif module_file
in self.
d[
"FORTRAN_MODULES"]
and force:
507 "Fortran module file "
509 +
" is already in module file list but force flag is set. Add it"
511 self.
d[
"FORTRAN_MODULES"].append(module_file)
513 self.
d[
"FORTRAN_MODULES"].append(module_file)
516 for i
in module_files:
519 def generate(self, overwrite, directory, subdirectories=[]):
521 Generates build files and other project-related files based on templates.
523 :param overwrite: Specifies whether existing files should be overwritten.
524 :type overwrite: bool
525 :param directory: The directory where the generated files will be placed.
528 self.
d[
"H_HEADERS"] = self.
hfiles
535 for subdirectory
in subdirectories:
536 self.
d[
"APP_SUBDIRECTORIES"].append(subdirectory)
539 def generate_output_file(input_file_path, output_file_path):
541 print(
"write " + output_file_path)
542 template_loader = jinja2.FileSystemLoader(searchpath=os.path.split(input_file_path)[0])
543 templateEnv = jinja2.Environment(loader=template_loader)
544 template = templateEnv.get_template(os.path.split(input_file_path)[1])
545 with open(output_file_path,
"w")
as output:
546 output.write(template.render(self.
d))
548 file_dir = os.path.dirname(os.path.realpath(__file__))
550 generate_output_file(os.path.join(file_dir,
"Gitignore.template"), os.path.join(directory,
".gitignore"))
552 if self.
d[
"CMAKE_BUILD_DIR"] !=
"":
553 generate_output_file(os.path.join(file_dir,
"CMakeLists.txt.template"), os.path.join(directory,
"CMakeLists.txt"))
555 generate_output_file(os.path.join(file_dir,
"Makefile.cmake.template"), os.path.join(directory,
"Makefile"))
557 generate_output_file(os.path.join(file_dir,
"Makefile.template"), os.path.join(directory,
"Makefile"))
Represents the created Makefile of a Peano 4 project.
remove_Fortran_file(self, filename)
add_Fortran_flag(self, value, force=False)
set_Fortran_compiler(self, value)
add_CXX_flag(self, value, force=False)
add_cpp_file(self, filename, generated=False)
Add a new cpp filename.
add_header_search_path(self, path)
Add the header search path to both the C++ and the Fortran call command.
add_Fortran_module(self, module_file, force=False)
Add a Fortran module.
set_CXX_flags(self, value)
remove_cpp_file(self, filename)
get_source_path(self)
Returns the directory where the .
set_dimension(self, dimension)
set_CXX_compiler(self, value)
set_linker_flags(self, value)
add_Fortran_modules(self, module_files)
set_Fortran_flags(self, value)
generate(self, overwrite, directory, subdirectories=[])
Generates build files and other project-related files based on templates.
add_h_file(self, filename, generated=False)
Add a new header filename.
readme_package_descriptor(self)
add_library(self, library_name, library_path="")
If you want to link against a library from Peano, feel free to use get_Peano4_source_directory() and ...
set_mode(self, mode)
mode should be of type CompileMode.
get_configure_path(self)
Returns the directory where the .
add_Fortran_file(self, filename, generated=False)
Add a new Fortran file.
parse_configure_script_outcome(self, directory)
directory should point to the directory which holds the .
add_linker_flag(self, value)
set_executable_name(self, fname)
remove_h_file(self, filename)