Newer
Older
Generator scripts are the primary way *pystencils-sfg* is meant to be used.
A generator script is a single Python script, say `kernels.py`, which contains *pystencils-sfg*
code at the top level such that, when executed, it emits source code to a pair of files `kernels.h`
and `kernels.cpp`. This guide describes how to write such a generator script, its structure, and how
it can be used to generate code.
This page gives a general overview over the code generation process, but introduces only the
convenient high-level interface provided by the [SourceFileGenerator][pystencilssfg.SourceFileGenerator]
and [SfgComposer][pystencilssfg.SfgComposer] classes.
For a more in-depth look into building source files, and about using *pystencils-sfg* outside
of a generator script, please take a look at the [In-Depth Guide](building.md).
## Anatomy
The code generation process in a generator script is controlled by the
[SourceFileGenerator][pystencilssfg.SourceFileGenerator] context manager.
It configures the code generator by combining configuration options from the
environment (e.g. a CMake build system) with options specified in the script,
and infers the names of the output files from the script's name.
It then prepares and returns a code generation [context][pystencilssfg.SfgContext].
This context may then be passed to a [composer][pystencilssfg.SfgComposer],
which provides a convenient interface for constructing the source files.
To start, place the following code in a Python script, e.g. `kernels.py`:
```Python
from pystencilssfg import SourceFileGenerator, SfgConfiguration, SfgComposer
sfg_config = SfgConfiguration()
with SourceFileGenerator(sfg_config) as ctx:
sfg = SfgComposer(ctx)
```
The source file is constructed within the context manager's managed region.
During execution of the script, when the region ends, a header/source file pair
`kernels.h` and `kernels.cpp` will be written to the file system next to your script.
Execute the script as-is and inspect the generated files, which will of course
still be empty.
A few notes on configuration:
- The [SourceFileGenerator][pystencilssfg.SourceFileGenerator] parses the script's command line arguments
for configuration options (refer to [CLI and Build System Integration](cli.md)).
If you intend to use command-line parameters in your
generation script, use [`sfg.context.argv`][pystencilssfg.SfgContext.argv] instead of `sys.argv`.
There, all arguments meant for the code generator are already removed.
- The code generator's configuration is consolidated from a global project configuration which may
be provided by the build system; a number of command line arguments; and the
[SfgConfiguration][pystencilssfg.SfgConfiguration] provided in the script.
The project configuration may safely be overridden by the latter two; however, conflicts
between command-line arguments and the configuration defined in the script will cause
an exception to be thrown.
## Using the Composer
The object `sfg` constructed in above snippet is an instance of [SfgComposer][pystencilssfg.SfgComposer].
The composer is the central part of the user front-end of *pystencils-sfg*.
It provides an interface for constructing source files that attempts to closely mimic
C++ syntactic structures within Python.
Here is an overview of its various functions:
### Includes and Definitions
With [`SfgComposer.include`][pystencilssfg.SfgComposer.include], the code generator can be instructed
to include header files.
```Python
with SourceFileGenerator(sfg_config) as ctx:
sfg = SfgComposer(ctx)
# ...
sfg.include("<vector>")
sfg.incldue("custom_header.h")
```
### Adding Kernels
`pystencils`-generated kernels are managed in
[kernel namespaces][pystencilssfg.source_components.SfgKernelNamespace].
The default kernel namespace is called `kernels` and is available via
[`SfgComposer.kernels`][pystencilssfg.SfgComposer.kernels].
Adding an existing `pystencils` AST, or creating one from a list of assignments, is possible
through [`add`][pystencilssfg.source_components.SfgKernelNamespace.add]
and [`create`][pystencilssfg.source_components.SfgKernelNamespace.create].
The latter is a wrapper around
[`pystencils.create_kernel`](
https://pycodegen.pages.i10git.cs.fau.de/pystencils/sphinx/kernel_compile_and_call.html#pystencils.create_kernel
).
Both functions return a [kernel handle][pystencilssfg.source_components.SfgKernelHandle]
through which the kernel can be accessed, e.g. for calling it in a function.
If required, use [`SfgComposer.kernel_namespace`][pystencilssfg.SfgComposer.kernel_namespace]
to access other kernel namespaces than the default one.
```Python
with SourceFileGenerator(sfg_config) as ctx:
sfg = SfgComposer(ctx)
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# ...
ast = ps.create_kernel(assignments, config)
khandle = sfg.kernels.add(ast, "kernel_a")
# is equivalent to
khandle = sfg.kernels.create(assignments, "kernel_a", config)
# You may use a different namespace
nspace = sfg.kernel_namespace("group_of_kernels")
nspace.create(assignments, "kernel_a", config)
```
### Building Functions
[Functions][pystencilssfg.source_components.SfgFunction] form the link between your `pystencils` kernels
and your C++ framework. A function in *pystencils-sfg* translates to a simple C++ function, and should
fulfill just the following tasks:
- Extract kernel parameters (pointers, sizes, strides, numerical coefficients)
from C++ objects (like fields, vectors, other data containers)
- Call one or more kernels in sequence or in conditional branches
It is the philosophy of this project that anything more complicated than this should happen in handwritten
code; these generated functions are merely meant to close the remaining gap.
The composer provides an interface for constructing functions that tries to mimic the look of the generated C++
code.
Use [`SfgComposer.function`][pystencilssfg.SfgComposer.function] to create a function,
and [`SfgComposer.call`][pystencilssfg.SfgComposer.call] to call a kernel by its handle:
```Python
with SourceFileGenerator(sfg_config) as ctx:
sfg = SfgComposer(ctx)
# ...
sfg.function("MyFunction")(
sfg.call(khandle)
)
```
Note the special syntax: To mimic the look of a C++ function, the composer uses a sequence of two calls
to construct the function.
The function body may further be populated with the following things:
#### Parameter Mappings
Extract kernel parameters from C++ objects:
- [`map_param`][pystencilssfg.SfgComposer.map_param]: Add a single line of code to define one parameter
depending on one other.
- [`map_field`][pystencilssfg.SfgComposer.map_field] maps a pystencils
[`Field`](https://pycodegen.pages.i10git.cs.fau.de/pystencils/sphinx/field.html)
to a field data structure providing the necessary pointers, sizes and stride information.
The field data structure must be provided as an instance of a subclass of
[`SrcField`][pystencilssfg.source_concepts.SrcField].
Currently, *pystencils-sfg* provides mappings to
[`std::vector`](https://en.cppreference.com/w/cpp/container/vector)
(via [`std_vector_ref`][pystencilssfg.source_concepts.cpp.std_vector_ref])
and
[`std::mdspan`](https://en.cppreference.com/w/cpp/container/mdspan)
(via [`mdspan_ref`][pystencilssfg.source_concepts.cpp.mdspan_ref])
from the C++ standard library.
- [`map_vector`][pystencilssfg.SfgComposer.map_vector] maps a sequence of scalar numerical values
(given as `pystencils.TypedSymbol`s) to a vector data type. Currently, only `std::vector` is provided.
#### Conditional Branches
A conditonal branch may be added with [`SfgComposer.branch`][pystencilssfg.SfgComposer.branch]
using a special syntax:
```Python
with SourceFileGenerator(sfg_config) as ctx:
sfg = SfgComposer(ctx)