Skip to content
Snippets Groups Projects
Commit 734b370a authored by Frederik Hennig's avatar Frederik Hennig
Browse files

small mdspan refactor. Added install instructions and usage primer.

parent fcd75114
No related branches found
No related tags found
No related merge requests found
Pipeline #57462 failed
...@@ -3,5 +3,61 @@ ...@@ -3,5 +3,61 @@
A bridge over the semantic gap between code emitted by [pystencils](https://pypi.org/project/pystencils/) A bridge over the semantic gap between code emitted by [pystencils](https://pypi.org/project/pystencils/)
and your C/C++/Cuda/HIP framework. and your C/C++/Cuda/HIP framework.
The initial version of this software is still under development. ## Installation
### From Git
Clone the [repository](https://i10git.cs.fau.de/da15siwa/pystencils-sfg) and install the package into your current Python environment
(usage of virtual environments is strongly encouraged!):
```shell
$ git clone https://i10git.cs.fau.de/da15siwa/pystencils-sfg.git
$ cd pystencils-sfg
$ pip install .
```
### From PyPI
Not yet available.
## Primer
With *pystencils-sfg*, including your *pystencils*-generated kernels with handwritten code becomes straightforward
and intuitive. To illustrate, generating a Jacobi smoother for the two-dimensional Poisson equation,
using the awesome C++23 `std::mdspan`, takes just a few lines of code:
```python
import sympy as sp
from pystencils import fields, kernel
from pystencilssfg import SourceFileGenerator
from pystencilssfg.source_concepts.cpp import mdspan_ref
with SourceFileGenerator() as sfg:
u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx")
h = sp.Symbol("h")
@kernel
def poisson_jacobi():
u_dst[0,0] @= (h**2 * f[0, 0] * u_src[1, 0] + u_src[-1, 0] + u_src[0, 1] + u_src[0, -1]) / 4
poisson_kernel = sfg.kernels.create(poisson_jacobi)
sfg.function("jacobi_smooth")(
sfg.map_field(u_src, mdspan_ref(u_src)),
sfg.map_field(u_dst, mdspan_ref(u_dst)),
sfg.map_field(f, mdspan_ref(f)),
sfg.call(poisson_kernel)
)
```
Take this code, store it into a file `poisson_smoother.py`, and enter the magic words into a terminal:
```shell
$ python poisson_smoother.py
```
This command will execute the code generator through the `SourceFileGenerator` context manager.
The code generator takes the name of your Python script, replaces `.py` with `.cpp` and `.h`, and writes
`poisson_smoother.cpp` and `poisson_smoother.h` into the current directory, ready to be `#include`d.
site_name: pystencils Source File Generator Documentation site_name: pystencils Source File Generator Documentation
theme: theme:
name: material name: material
features: navigation.tabs features:
- navigation.tabs
- content.code.copy
palette: palette:
scheme: slate scheme: slate
primary: deep purple primary: deep purple
...@@ -27,6 +29,10 @@ plugins: ...@@ -27,6 +29,10 @@ plugins:
show_signature_annotations: True show_signature_annotations: True
signature_crossrefs: True signature_crossrefs: True
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences
nav: nav:
- Home: index.md - Home: index.md
......
from .std_mdspan import std_mdspan from .std_mdspan import StdMdspan, mdspan_ref
from .std_vector import std_vector, std_vector_ref from .std_vector import std_vector, std_vector_ref
__all__ = [ __all__ = [
"std_mdspan", "std_vector", "std_vector_ref" "StdMdspan", "std_vector", "std_vector_ref",
"mdspan_ref"
] ]
from typing import Union from typing import Union
import numpy as np
from pystencils import Field
from pystencils.typing import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol from pystencils.typing import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol
from ...tree import SfgStatements from ...tree import SfgStatements
...@@ -9,7 +12,7 @@ from ...types import PsType, cpp_typename, SrcType ...@@ -9,7 +12,7 @@ from ...types import PsType, cpp_typename, SrcType
from ...exceptions import SfgException from ...exceptions import SfgException
class std_mdspan(SrcField): class StdMdspan(SrcField):
dynamic_extent = "std::dynamic_extent" dynamic_extent = "std::dynamic_extent"
def __init__(self, identifer: str, def __init__(self, identifer: str,
...@@ -77,3 +80,25 @@ class std_mdspan(SrcField): ...@@ -77,3 +80,25 @@ class std_mdspan(SrcField):
f"assert( {self._identifier}.stride({coordinate}) == {stride} );", f"assert( {self._identifier}.stride({coordinate}) == {stride} );",
(), (self, ) (), (self, )
) )
def mdspan_ref(field: Field, extents_type: type = np.uint32):
"""Creates a `std::mdspan &` for a given pystencils field."""
from pystencils.field import layout_string_to_tuple
if field.layout != layout_string_to_tuple("soa", field.spatial_dimensions):
raise NotImplementedError("mdspan mapping is currently only available for structure-of-arrays fields")
extents = []
for s in field.spatial_shape:
extents += StdMdspan.dynamic_extent if isinstance(s, FieldShapeSymbol) else s
if field.index_shape != (1,):
for s in field.index_shape:
extents += StdMdspan.dynamic_extent if isinstance(s, FieldShapeSymbol) else s
return StdMdspan(field.name, field.dtype,
(StdMdspan.dynamic_extent, StdMdspan.dynamic_extent),
extents_type=extents_type,
reference=True)
...@@ -10,8 +10,6 @@ from pystencilssfg import SourceFileGenerator ...@@ -10,8 +10,6 @@ from pystencilssfg import SourceFileGenerator
with SourceFileGenerator() as sfg: with SourceFileGenerator() as sfg:
src, dst = fields("src, dst(1) : double[2D]") src, dst = fields("src, dst(1) : double[2D]")
h = sp.Symbol('h')
@kernel @kernel
def poisson_jacobi(): def poisson_jacobi():
dst[0, 0] @= (src[1, 0] + src[-1, 0] + src[0, 1] + src[0, -1]) / 4 dst[0, 0] @= (src[1, 0] + src[-1, 0] + src[0, 1] + src[0, -1]) / 4
......
...@@ -20,7 +20,7 @@ clean: ...@@ -20,7 +20,7 @@ clean:
$(GEN_SRC)/kernels.cpp $(GEN_SRC)/kernels.h &: kernels.py $(GEN_SRC)/kernels.cpp $(GEN_SRC)/kernels.h &: kernels.py
@$(dir_guard) @$(dir_guard)
$(PYTHON) $< -d $(@D) $(PYTHON) $< --sfg-output-dir $(@D)
$(OBJ)/kernels.o: $(GEN_SRC)/kernels.cpp $(GEN_SRC)/kernels.h $(OBJ)/kernels.o: $(GEN_SRC)/kernels.cpp $(GEN_SRC)/kernels.h
@$(dir_guard) @$(dir_guard)
......
import sympy as sp import sympy as sp
import numpy as np
from pystencils.session import * from pystencils import fields, kernel
from pystencilssfg import SourceFileGenerator from pystencilssfg import SourceFileGenerator
from pystencilssfg.source_concepts.cpp import std_mdspan from pystencilssfg.source_concepts.cpp import mdspan_ref
def field_t(field: ps.Field):
return std_mdspan(field.name,
field.dtype,
(std_mdspan.dynamic_extent, std_mdspan.dynamic_extent),
extents_type=np.uint32,
reference=True)
with SourceFileGenerator() as sfg: with SourceFileGenerator() as sfg:
src, dst = ps.fields("src, dst(1) : double[2D]") u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx")
h = sp.Symbol("h")
h = sp.Symbol('h')
@ps.kernel @kernel
def poisson_jacobi(): def poisson_jacobi():
dst[0,0] @= (src[1, 0] + src[-1, 0] + src[0, 1] + src[0, -1]) / 4 u_dst[0,0] @= (h**2 * f[0, 0] * u_src[1, 0] + u_src[-1, 0] + u_src[0, 1] + u_src[0, -1]) / 4
poisson_kernel = sfg.kernels.create(poisson_jacobi) poisson_kernel = sfg.kernels.create(poisson_jacobi)
sfg.function("jacobi_smooth")( sfg.function("jacobi_smooth")(
sfg.map_field(src, field_t(src)), sfg.map_field(u_src, mdspan_ref(u_src)),
sfg.map_field(dst, field_t(dst)), sfg.map_field(u_dst, mdspan_ref(u_dst)),
sfg.map_field(f, mdspan_ref(f)),
sfg.call(poisson_kernel) sfg.call(poisson_kernel)
) )
...@@ -25,34 +25,38 @@ int main(int argc, char ** argv){ ...@@ -25,34 +25,38 @@ int main(int argc, char ** argv){
std::vector< double > data_dst(N*N); std::vector< double > data_dst(N*N);
field_t dst(data_dst.data(), N, N); field_t dst(data_dst.data(), N, N);
std::vector< double > data_f(N*N);
field_t f(data_f.data(), N, N);
for(uint32_t i = 0; i < N; ++i){ for(uint32_t i = 0; i < N; ++i){
for(uint32_t j = 0; j < N; ++j){ for(uint32_t j = 0; j < N; ++j){
if(i == 0 || j == 0 || i == N-1 || j == N-1){ if(i == 0 || j == 0 || i == N-1 || j == N-1){
src[i, j] = boundary(double(i) * h, double(j) * h); src[i, j] = boundary(double(i) * h, double(j) * h);
dst[i, j] = boundary(double(i) * h, double(j) * h); dst[i, j] = boundary(double(i) * h, double(j) * h);
f[i, j] = 0.0;
} }
} }
} }
for(uint32_t i = 0; i < n_iters; ++i){ for(uint32_t i = 0; i < n_iters; ++i){
poisson::jacobi_smooth(dst, src); jacobi_smooth(f, h, dst, src);
std::swap(src, dst); std::swap(src, dst);
} }
std::ofstream f("data.out", std::ios::trunc | std::ios::out); std::ofstream file("data.out", std::ios::trunc | std::ios::out);
if(!f.is_open()){ if(!file.is_open()){
std::cerr << "Could not open output file.\n"; std::cerr << "Could not open output file.\n";
} else { } else {
for(uint32_t i = 0; i < N; ++i){ for(uint32_t i = 0; i < N; ++i){
for(uint32_t j = 0; j < N; ++j){ for(uint32_t j = 0; j < N; ++j){
f << src[i, j] << " "; file << src[i, j] << " ";
} }
f << '\n'; file << '\n';
} }
} }
f.close(); file.close();
return 0; return 0;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment